Skip to content

Commit b86e653

Browse files
authored
Implement dpnp.mgrid and dpnp.ogrid function (#1633)
* Implement dpnp.mgrid and dpnp.ogrid function * address comments * address comments * Skip dtype check for Iris Xe * Update test_ranges.py * Added description of all parameters
1 parent c590dff commit b86e653

File tree

7 files changed

+240
-21
lines changed

7 files changed

+240
-21
lines changed

dpnp/dpnp_algo/dpnp_arraycreation.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import math
12
import operator
23

4+
import dpctl.utils as dpu
35
import numpy
46

57
import dpnp
@@ -10,6 +12,7 @@
1012
"dpnp_geomspace",
1113
"dpnp_linspace",
1214
"dpnp_logspace",
15+
"dpnp_nd_grid",
1316
]
1417

1518

@@ -256,3 +259,134 @@ def dpnp_logspace(
256259
if dtype is None:
257260
return dpnp.power(base, res)
258261
return dpnp.power(base, res).astype(dtype, copy=False)
262+
263+
264+
class dpnp_nd_grid:
265+
"""
266+
Construct a multi-dimensional "meshgrid".
267+
268+
``grid = dpnp_nd_grid()`` creates an instance which will return a mesh-grid
269+
when indexed. The dimension and number of the output arrays are equal
270+
to the number of indexing dimensions. If the step length is not a
271+
complex number, then the stop is not inclusive.
272+
273+
However, if the step length is a complex number (e.g. 5j), then the
274+
integer part of its magnitude is interpreted as specifying the
275+
number of points to create between the start and stop values, where
276+
the stop value is inclusive.
277+
278+
If instantiated with an argument of ``sparse=True``, the mesh-grid is
279+
open (or not fleshed out) so that only one-dimension of each returned
280+
argument is greater than 1.
281+
282+
Parameters
283+
----------
284+
sparse : bool, optional
285+
Whether the grid is sparse or not. Default is False.
286+
287+
"""
288+
289+
def __init__(
290+
self, sparse=False, device=None, usm_type="device", sycl_queue=None
291+
):
292+
dpu.validate_usm_type(usm_type, allow_none=False)
293+
self.sparse = sparse
294+
self.usm_type = usm_type
295+
self.sycl_queue_normalized = dpnp.get_normalized_queue_device(
296+
sycl_queue=sycl_queue, device=device
297+
)
298+
299+
def __getitem__(self, key):
300+
if isinstance(key, slice):
301+
step = key.step
302+
stop = key.stop
303+
start = key.start
304+
if start is None:
305+
start = 0
306+
if isinstance(step, complex):
307+
step = abs(step)
308+
length = int(step)
309+
if step != 1:
310+
step = (stop - start) / float(step - 1)
311+
stop = stop + step
312+
return (
313+
dpnp.arange(
314+
0,
315+
length,
316+
1,
317+
dtype=dpnp.default_float_type(),
318+
usm_type=self.usm_type,
319+
sycl_queue=self.sycl_queue_normalized,
320+
)
321+
* step
322+
+ start
323+
)
324+
else:
325+
return dpnp.arange(
326+
start,
327+
stop,
328+
step,
329+
usm_type=self.usm_type,
330+
sycl_queue=self.sycl_queue_normalized,
331+
)
332+
333+
size = []
334+
dtype = int
335+
for k in range(len(key)):
336+
step = key[k].step
337+
start = key[k].start
338+
stop = key[k].stop
339+
if start is None:
340+
start = 0
341+
if step is None:
342+
step = 1
343+
if isinstance(step, complex):
344+
size.append(int(abs(step)))
345+
dtype = dpnp.default_float_type()
346+
else:
347+
size.append(
348+
int(math.ceil((key[k].stop - start) / (step * 1.0)))
349+
)
350+
if (
351+
isinstance(step, float)
352+
or isinstance(start, float)
353+
or isinstance(stop, float)
354+
):
355+
dtype = dpnp.default_float_type()
356+
if self.sparse:
357+
nn = [
358+
dpnp.arange(
359+
_x,
360+
dtype=_t,
361+
usm_type=self.usm_type,
362+
sycl_queue=self.sycl_queue_normalized,
363+
)
364+
for _x, _t in zip(size, (dtype,) * len(size))
365+
]
366+
else:
367+
nn = dpnp.indices(
368+
size,
369+
dtype,
370+
usm_type=self.usm_type,
371+
sycl_queue=self.sycl_queue_normalized,
372+
)
373+
for k in range(len(size)):
374+
step = key[k].step
375+
start = key[k].start
376+
stop = key[k].stop
377+
if start is None:
378+
start = 0
379+
if step is None:
380+
step = 1
381+
if isinstance(step, complex):
382+
step = int(abs(step))
383+
if step != 1:
384+
step = (stop - start) / float(step - 1)
385+
nn[k] = nn[k] * step + start
386+
if self.sparse:
387+
slobj = [dpnp.newaxis] * len(size)
388+
for k in range(len(size)):
389+
slobj[k] = slice(None, None)
390+
nn[k] = nn[k][tuple(slobj)]
391+
slobj[k] = dpnp.newaxis
392+
return nn

dpnp/dpnp_iface_arraycreation.py

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
dpnp_geomspace,
5353
dpnp_linspace,
5454
dpnp_logspace,
55+
dpnp_nd_grid,
5556
)
5657

5758
__all__ = [
@@ -1452,6 +1453,24 @@ class MGridClass:
14521453
14531454
For full documentation refer to :obj:`numpy.mgrid`.
14541455
1456+
Parameters
1457+
----------
1458+
device : {None, string, SyclDevice, SyclQueue}, optional
1459+
An array API concept of device where the output array is created.
1460+
The `device` can be ``None`` (the default), an OneAPI filter selector string,
1461+
an instance of :class:`dpctl.SyclDevice` corresponding to a non-partitioned SYCL device,
1462+
an instance of :class:`dpctl.SyclQueue`, or a `Device` object returned by
1463+
:obj:`dpnp.dpnp_array.dpnp_array.device` property.
1464+
usm_type : {"device", "shared", "host"}, optional
1465+
The type of SYCL USM allocation for the output array.
1466+
sycl_queue : {None, SyclQueue}, optional
1467+
A SYCL queue to use for output array allocation and copying.
1468+
1469+
Returns
1470+
-------
1471+
out : one dpnp.ndarray or tuple of dpnp.ndarray
1472+
Returns one array of grid indices, grid.shape = (len(dimensions),) + tuple(dimensions).
1473+
14551474
Examples
14561475
--------
14571476
>>> import dpnp as np
@@ -1466,13 +1485,31 @@ class MGridClass:
14661485
[0, 1, 2, 3, 4],
14671486
[0, 1, 2, 3, 4],
14681487
[0, 1, 2, 3, 4]]])
1469-
>>> np.mgrid[-1:1:5j]
1488+
1489+
>>> x = np.mgrid[-1:1:5j]
1490+
>>> x
14701491
array([-1. , -0.5, 0. , 0.5, 1. ])
1492+
>>> x.usm_type
1493+
'device'
1494+
1495+
>>> y = np.mgrid(usm_type="host")[-1:1:5j]
1496+
>>> y
1497+
array([-1. , -0.5, 0. , 0.5, 1. ])
1498+
>>> x.usm_type
1499+
'host'
14711500
14721501
"""
14731502

14741503
def __getitem__(self, key):
1475-
return dpnp.array(numpy.mgrid[key])
1504+
return dpnp_nd_grid(sparse=False)[key]
1505+
1506+
def __call__(self, device=None, usm_type="device", sycl_queue=None):
1507+
return dpnp_nd_grid(
1508+
sparse=False,
1509+
device=device,
1510+
usm_type=usm_type,
1511+
sycl_queue=sycl_queue,
1512+
)
14761513

14771514

14781515
mgrid = MGridClass()
@@ -1484,23 +1521,56 @@ class OGridClass:
14841521
14851522
For full documentation refer to :obj:`numpy.ogrid`.
14861523
1524+
Parameters
1525+
----------
1526+
device : {None, string, SyclDevice, SyclQueue}, optional
1527+
An array API concept of device where the output array is created.
1528+
The `device` can be ``None`` (the default), an OneAPI filter selector string,
1529+
an instance of :class:`dpctl.SyclDevice` corresponding to a non-partitioned SYCL device,
1530+
an instance of :class:`dpctl.SyclQueue`, or a `Device` object returned by
1531+
:obj:`dpnp.dpnp_array.dpnp_array.device` property.
1532+
usm_type : {"device", "shared", "host"}, optional
1533+
The type of SYCL USM allocation for the output array.
1534+
sycl_queue : {None, SyclQueue}, optional
1535+
A SYCL queue to use for output array allocation and copying.
1536+
1537+
Returns
1538+
-------
1539+
out : one dpnp.ndarray or tuple of dpnp.ndarray
1540+
Returns a tuple of arrays, with grid[i].shape = (1, ..., 1, dimensions[i], 1, ..., 1)
1541+
with dimensions[i] in the ith place.
1542+
14871543
Examples
14881544
--------
14891545
>>> import dpnp as np
1490-
>>> from numpy import ogrid
1491-
>>> ogrid[-1:1:5j]
1492-
array([-1. , -0.5, 0. , 0.5, 1. ])
1493-
>>> ogrid[0:5,0:5]
1546+
>>> np.ogrid[0:5, 0:5]
14941547
[array([[0],
14951548
[1],
14961549
[2],
14971550
[3],
14981551
[4]]), array([[0, 1, 2, 3, 4]])]
14991552
1553+
>>> x = np.ogrid[-1:1:5j]
1554+
>>> x
1555+
array([-1. , -0.5, 0. , 0.5, 1. ])
1556+
>>> x.usm_type
1557+
'device'
1558+
1559+
>>> y = np.ogrid(usm_type="host")[-1:1:5j]
1560+
>>> y
1561+
array([-1. , -0.5, 0. , 0.5, 1. ])
1562+
>>> x.usm_type
1563+
'host'
1564+
15001565
"""
15011566

15021567
def __getitem__(self, key):
1503-
return dpnp.array(numpy.ogrid[key])
1568+
return dpnp_nd_grid(sparse=True)[key]
1569+
1570+
def __call__(self, device=None, usm_type="device", sycl_queue=None):
1571+
return dpnp_nd_grid(
1572+
sparse=True, device=device, usm_type=usm_type, sycl_queue=sycl_queue
1573+
)
15041574

15051575

15061576
ogrid = OGridClass()

tests/skipped_tests.tbl

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,6 @@ tests/third_party/cupy/creation_tests/test_ranges.py::TestMeshgrid_param_7_{copy
177177
tests/third_party/cupy/creation_tests/test_ranges.py::TestMeshgrid_param_7_{copy=True, indexing='ij', sparse=True}::test_meshgrid1
178178
tests/third_party/cupy/creation_tests/test_ranges.py::TestMeshgrid_param_7_{copy=True, indexing='ij', sparse=True}::test_meshgrid2
179179
tests/third_party/cupy/creation_tests/test_ranges.py::TestMeshgrid_param_7_{copy=True, indexing='ij', sparse=True}::test_meshgrid3
180-
tests/third_party/cupy/creation_tests/test_ranges.py::TestMgrid::test_mgrid3
181-
tests/third_party/cupy/creation_tests/test_ranges.py::TestOgrid::test_ogrid3
182-
tests/third_party/cupy/creation_tests/test_ranges.py::TestOgrid::test_ogrid4
183-
tests/third_party/cupy/creation_tests/test_ranges.py::TestOgrid::test_ogrid5
184180
tests/third_party/cupy/indexing_tests/test_generate.py::TestAxisConcatenator::test_AxisConcatenator_init1
185181
tests/third_party/cupy/indexing_tests/test_generate.py::TestAxisConcatenator::test_len
186182
tests/third_party/cupy/indexing_tests/test_generate.py::TestC_::test_c_1

tests/skipped_tests_gpu.tbl

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -255,11 +255,6 @@ tests/third_party/cupy/creation_tests/test_ranges.py::TestMeshgrid_param_7_{copy
255255
tests/third_party/cupy/creation_tests/test_ranges.py::TestMeshgrid_param_7_{copy=True, indexing='ij', sparse=True}::test_meshgrid1
256256
tests/third_party/cupy/creation_tests/test_ranges.py::TestMeshgrid_param_7_{copy=True, indexing='ij', sparse=True}::test_meshgrid2
257257
tests/third_party/cupy/creation_tests/test_ranges.py::TestMeshgrid_param_7_{copy=True, indexing='ij', sparse=True}::test_meshgrid3
258-
tests/third_party/cupy/creation_tests/test_ranges.py::TestMgrid::test_mgrid3
259-
tests/third_party/cupy/creation_tests/test_ranges.py::TestMgrid::test_mgrid5
260-
tests/third_party/cupy/creation_tests/test_ranges.py::TestOgrid::test_ogrid3
261-
tests/third_party/cupy/creation_tests/test_ranges.py::TestOgrid::test_ogrid4
262-
tests/third_party/cupy/creation_tests/test_ranges.py::TestOgrid::test_ogrid5
263258
tests/third_party/cupy/creation_tests/test_ranges.py::TestRanges::test_arange_negative_size
264259
tests/third_party/cupy/creation_tests/test_ranges.py::TestRanges::test_arange_no_dtype_int
265260

tests/test_sycl_queue.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,6 +1431,18 @@ def test_indices(device, sparse):
14311431
assert_sycl_queue_equal(dpnp_array.sycl_queue, sycl_queue)
14321432

14331433

1434+
@pytest.mark.parametrize(
1435+
"device",
1436+
valid_devices,
1437+
ids=[device.filter_string for device in valid_devices],
1438+
)
1439+
@pytest.mark.parametrize("func", ["mgrid", "ogrid"])
1440+
def test_grid(device, func):
1441+
sycl_queue = dpctl.SyclQueue(device)
1442+
x = getattr(dpnp, func)(sycl_queue=sycl_queue)[0:4]
1443+
assert_sycl_queue_equal(x.sycl_queue, sycl_queue)
1444+
1445+
14341446
@pytest.mark.parametrize(
14351447
"device",
14361448
valid_devices,

tests/test_usm_type.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,18 @@ def test_take(func, usm_type_x, usm_type_ind):
521521
assert z.usm_type == du.get_coerced_usm_type([usm_type_x, usm_type_ind])
522522

523523

524+
@pytest.mark.parametrize("usm_type", list_of_usm_types, ids=list_of_usm_types)
525+
def test_indices(usm_type):
526+
x = dp.indices((2,), usm_type=usm_type)
527+
assert x.usm_type == usm_type
528+
529+
530+
@pytest.mark.parametrize("usm_type", list_of_usm_types, ids=list_of_usm_types)
531+
@pytest.mark.parametrize("func", ["mgrid", "ogrid"])
532+
def test_grid(usm_type, func):
533+
assert getattr(dp, func)(usm_type=usm_type)[0:4].usm_type == usm_type
534+
535+
524536
@pytest.mark.parametrize("usm_type", list_of_usm_types, ids=list_of_usm_types)
525537
@pytest.mark.parametrize("sparse", [True, False], ids=["True", "False"])
526538
def test_indices(usm_type, sparse):

tests/third_party/cupy/creation_tests/test_ranges.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ def test_mgrid1(self, xp):
363363
def test_mgrid2(self, xp):
364364
return xp.mgrid[-10:10:10j]
365365

366-
@testing.numpy_cupy_array_equal()
366+
@testing.numpy_cupy_allclose(rtol=1e-4, type_check=has_support_aspect64())
367367
def test_mgrid3(self, xp):
368368
x = xp.zeros(10)[:, None]
369369
y = xp.ones(10)[:, None]
@@ -374,7 +374,7 @@ def test_mgrid4(self, xp):
374374
# check len(keys) > 1
375375
return xp.mgrid[-10:10:10j, -10:10:10j]
376376

377-
@testing.numpy_cupy_array_equal()
377+
@testing.numpy_cupy_allclose(rtol=1e-4, type_check=has_support_aspect64())
378378
def test_mgrid5(self, xp):
379379
# check len(keys) > 1
380380
x = xp.zeros(10)[:, None]
@@ -396,18 +396,18 @@ def test_ogrid1(self, xp):
396396
def test_ogrid2(self, xp):
397397
return xp.ogrid[-10:10:10j]
398398

399-
@testing.numpy_cupy_array_equal()
399+
@testing.numpy_cupy_allclose(rtol=1e-4, type_check=has_support_aspect64())
400400
def test_ogrid3(self, xp):
401401
x = xp.zeros(10)[:, None]
402402
y = xp.ones(10)[:, None]
403403
return xp.ogrid[x:y:10j]
404404

405-
@testing.numpy_cupy_array_equal()
405+
@testing.numpy_cupy_allclose(rtol=1e-4, type_check=has_support_aspect64())
406406
def test_ogrid4(self, xp):
407407
# check len(keys) > 1
408408
return xp.ogrid[-10:10:10j, -10:10:10j]
409409

410-
@testing.numpy_cupy_array_equal()
410+
@testing.numpy_cupy_allclose(rtol=1e-4, type_check=has_support_aspect64())
411411
def test_ogrid5(self, xp):
412412
# check len(keys) > 1
413413
x = xp.zeros(10)[:, None]

0 commit comments

Comments
 (0)