Skip to content

Commit e353d7d

Browse files
authored
Update dpnp.place implementation to get rid of limitations for input arguments (#1912)
* Remove limitations from dpnp.take implementation * Add more test to cover specail cases and increase code coverage * Applied pre-commit hook * Corrected test_over_index * Update docsctrings with resolving typos * Use dpnp.reshape() to change shape and create dpnp array from usm_ndarray result * Remove limitations from dpnp.place implementation * Update relating tests * Roll back changed in dpnp.vander * Remove data sync at the end of function * Remove data sync from dpnp.get_result_array() * Fix typo in docstring * Corrected a link to dpnp.copyto() in description
1 parent ce26cf0 commit e353d7d

File tree

5 files changed

+158
-167
lines changed

5 files changed

+158
-167
lines changed

dpnp/dpnp_iface.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"array_equal",
5858
"asnumpy",
5959
"astype",
60+
"as_usm_ndarray",
6061
"check_limitations",
6162
"check_supported_arrays_type",
6263
"convert_single_elem_array_to_scalar",
@@ -247,6 +248,55 @@ def astype(x1, dtype, order="K", casting="unsafe", copy=True, device=None):
247248
return dpnp_array._create_from_usm_ndarray(array_obj)
248249

249250

251+
def as_usm_ndarray(a, dtype=None, device=None, usm_type=None, sycl_queue=None):
252+
"""
253+
Return :class:`dpctl.tensor.usm_ndarray` from input object `a`.
254+
255+
Parameters
256+
----------
257+
a : {array_like, scalar}
258+
Input array or scalar.
259+
dtype : {None, dtype}, optional
260+
The desired dtype for the result array if new array is creating. If not
261+
given, a default dtype will be used that can represent the values (by
262+
considering Promotion Type Rule and device capabilities when necessary).
263+
Default: ``None``.
264+
device : {None, string, SyclDevice, SyclQueue}, optional
265+
An array API concept of device where the result array is created if
266+
required.
267+
The `device` can be ``None`` (the default), an OneAPI filter selector
268+
string, an instance of :class:`dpctl.SyclDevice` corresponding to
269+
a non-partitioned SYCL device, an instance of :class:`dpctl.SyclQueue`,
270+
or a `Device` object returned by
271+
:obj:`dpnp.dpnp_array.dpnp_array.device` property.
272+
Default: ``None``.
273+
usm_type : {None, "device", "shared", "host"}, optional
274+
The type of SYCL USM allocation for the result array if new array
275+
is created.
276+
Default: ``None``.
277+
sycl_queue : {None, SyclQueue}, optional
278+
A SYCL queue to use for result array allocation if required.
279+
Default: ``None``.
280+
281+
Returns
282+
-------
283+
out : usm_ndarray
284+
A dpctl USM ndarray from input array or scalar `a`.
285+
If `a` is instance of :class:`dpnp.ndarray`
286+
or :class:`dpctl.tensor.usm_ndarray`, no array allocation will be done
287+
and `dtype`, `device`, `usm_type`, `sycl_queue` keywords
288+
will be ignored.
289+
290+
"""
291+
292+
if is_supported_array_type(a):
293+
return get_usm_ndarray(a)
294+
295+
return dpt.asarray(
296+
a, dtype=dtype, device=device, usm_type=usm_type, sycl_queue=sycl_queue
297+
)
298+
299+
250300
def check_limitations(
251301
order=None, subok=False, like=None, initial=None, where=True
252302
):

dpnp/dpnp_iface_histograms.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,9 @@ def _get_bin_edges(a, bins, range, usm_type):
164164
"a and bins must be allocated on the same SYCL queue"
165165
)
166166

167-
bin_edges = dpnp.get_usm_ndarray(bins)
168-
else:
169-
bin_edges = dpt.asarray(
170-
bins, sycl_queue=sycl_queue, usm_type=usm_type
171-
)
167+
bin_edges = dpnp.as_usm_ndarray(
168+
bins, usm_type=usm_type, sycl_queue=sycl_queue
169+
)
172170

173171
if dpnp.any(bin_edges[:-1] > bin_edges[1:]):
174172
raise ValueError(

dpnp/dpnp_iface_indexing.py

Lines changed: 67 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -551,12 +551,12 @@ def extract(condition, a):
551551
"""
552552

553553
usm_a = dpnp.get_usm_ndarray(a)
554-
if not dpnp.is_supported_array_type(condition):
555-
usm_cond = dpt.asarray(
556-
condition, usm_type=a.usm_type, sycl_queue=a.sycl_queue
557-
)
558-
else:
559-
usm_cond = dpnp.get_usm_ndarray(condition)
554+
usm_cond = dpnp.as_usm_ndarray(
555+
condition,
556+
dtype=dpnp.bool,
557+
usm_type=usm_a.usm_type,
558+
sycl_queue=usm_a.sycl_queue,
559+
)
560560

561561
if usm_cond.size != usm_a.size:
562562
usm_a = dpt.reshape(usm_a, -1)
@@ -1011,30 +1011,74 @@ def nonzero(a):
10111011
)
10121012

10131013

1014-
def place(x, mask, vals, /):
1014+
def place(a, mask, vals):
10151015
"""
10161016
Change elements of an array based on conditional and input values.
10171017
1018+
Similar to ``dpnp.copyto(a, vals, where=mask)``, the difference is that
1019+
:obj:`dpnp.place` uses the first N elements of `vals`, where N is
1020+
the number of ``True`` values in `mask`, while :obj:`dpnp.copyto` uses
1021+
the elements where `mask` is ``True``.
1022+
1023+
Note that :obj:`dpnp.extract` does the exact opposite of :obj:`dpnp.place`.
1024+
10181025
For full documentation refer to :obj:`numpy.place`.
10191026
1020-
Limitations
1021-
-----------
1022-
Parameters `x`, `mask` and `vals` are supported either as
1023-
:class:`dpnp.ndarray` or :class:`dpctl.tensor.usm_ndarray`.
1024-
Otherwise the function will be executed sequentially on CPU.
1027+
Parameters
1028+
----------
1029+
a : {dpnp.ndarray, usm_ndarray}
1030+
Array to put data into.
1031+
mask : {array_like, scalar}
1032+
Boolean mask array. Must have the same size as `a`.
1033+
vals : {array_like, scalar}
1034+
Values to put into `a`. Only the first N elements are used, where N is
1035+
the number of ``True`` values in `mask`. If `vals` is smaller than N,
1036+
it will be repeated, and if elements of `a` are to be masked, this
1037+
sequence must be non-empty.
1038+
1039+
See Also
1040+
--------
1041+
:obj:`dpnp.copyto` : Copies values from one array to another.
1042+
:obj:`dpnp.put` : Replaces specified elements of an array with given values.
1043+
:obj:`dpnp.take` : Take elements from an array along an axis.
1044+
:obj:`dpnp.extract` : Return the elements of an array that satisfy some
1045+
condition.
1046+
1047+
Examples
1048+
--------
1049+
>>> import dpnp as np
1050+
>>> a = np.arange(6).reshape(2, 3)
1051+
>>> np.place(a, a > 2, [44, 55])
1052+
>>> a
1053+
array([[ 0, 1, 2],
1054+
[44, 55, 44]])
1055+
10251056
"""
10261057

1027-
if (
1028-
dpnp.is_supported_array_type(x)
1029-
and dpnp.is_supported_array_type(mask)
1030-
and dpnp.is_supported_array_type(vals)
1031-
):
1032-
dpt_array = x.get_array() if isinstance(x, dpnp_array) else x
1033-
dpt_mask = mask.get_array() if isinstance(mask, dpnp_array) else mask
1034-
dpt_vals = vals.get_array() if isinstance(vals, dpnp_array) else vals
1035-
return dpt.place(dpt_array, dpt_mask, dpt_vals)
1036-
1037-
return call_origin(numpy.place, x, mask, vals, dpnp_inplace=True)
1058+
usm_a = dpnp.get_usm_ndarray(a)
1059+
usm_mask = dpnp.as_usm_ndarray(
1060+
mask,
1061+
dtype=dpnp.bool,
1062+
usm_type=usm_a.usm_type,
1063+
sycl_queue=usm_a.sycl_queue,
1064+
)
1065+
usm_vals = dpnp.as_usm_ndarray(
1066+
vals,
1067+
dtype=usm_a.dtype,
1068+
usm_type=usm_a.usm_type,
1069+
sycl_queue=usm_a.sycl_queue,
1070+
)
1071+
1072+
if usm_vals.ndim != 1:
1073+
# dpt.place supports only 1-D array of values
1074+
usm_vals = dpt.reshape(usm_vals, -1)
1075+
1076+
if usm_vals.dtype != usm_a.dtype:
1077+
# dpt.place casts values to a.dtype with "unsafe" rule,
1078+
# while numpy.place does that with "safe" casting rule
1079+
usm_vals = dpt.astype(usm_vals, usm_a.dtype, casting="safe", copy=False)
1080+
1081+
dpt.place(usm_a, usm_mask, usm_vals)
10381082

10391083

10401084
def put(a, ind, v, /, *, axis=None, mode="wrap"):

0 commit comments

Comments
 (0)