Skip to content

Commit 67e620a

Browse files
authored
Backport gh-2209 (#2220)
This PR backports of #2209 from development branch to `maintenance/0.16.x`.
1 parent 641eacf commit 67e620a

File tree

6 files changed

+110
-62
lines changed

6 files changed

+110
-62
lines changed

CHANGELOG.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7-
## [0.16.1] - 12/05/2024
7+
## [0.16.1] - 12/06/2024
88

99
This is a bug-fix release.
1010

@@ -18,8 +18,9 @@ This is a bug-fix release.
1818
### Fixed
1919

2020
* Resolved an issue with Compute Follows Data inconsistency in `dpnp.extract` function [#2172](https://github.com/IntelPython/dpnp/pull/2172)
21-
* Resolves an import error when using `dpnp` in virtual environment on Linux [#2199](https://github.com/IntelPython/dpnp/pull/2199)
21+
* Resolved an import error when using `dpnp` in virtual environment on Linux [#2199](https://github.com/IntelPython/dpnp/pull/2199)
2222
* Fixed incorrect result produced by `dpnp.fft.fft` function when input array has negative strides [#2202](https://github.com/IntelPython/dpnp/pull/2202)
23+
* Fixed an issue with `numpy.ndarray` input processing in the `dpnp.from_dlpack` function and updated the documentation [#2209](https://github.com/IntelPython/dpnp/pull/2209)
2324
* Resolved a compilation error when building with DPC++ 2025.1 compiler [#2211](https://github.com/IntelPython/dpnp/pull/2211)
2425

2526

doc/conf.py

+1
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ def _can_document_member(member, *args, **kwargs):
217217
"python": ("https://docs.python.org/3/", None),
218218
"numpy": ("https://docs.scipy.org/doc/numpy/", None),
219219
"scipy": ("https://docs.scipy.org/doc/scipy/reference/", None),
220+
"dpctl": ("https://intelpython.github.io/dpctl/latest/", None),
220221
}
221222

222223
# If true, `todo` and `todoList` produce output, else they produce nothing.

dpnp/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949
[os.getenv("PATH", ""), mypath, dpctlpath]
5050
)
5151

52+
# Borrowed from DPCTL
53+
from dpctl.tensor import DLDeviceType
54+
5255
from dpnp.dpnp_array import dpnp_array as ndarray
5356
from dpnp.dpnp_flatiter import flatiter as flatiter
5457
from dpnp.dpnp_iface_types import *

dpnp/dpnp_iface.py

-55
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@
6363
"check_supported_arrays_type",
6464
"convert_single_elem_array_to_scalar",
6565
"default_float_type",
66-
"from_dlpack",
6766
"get_dpnp_descriptor",
6867
"get_include",
6968
"get_normalized_queue_device",
@@ -464,60 +463,6 @@ def default_float_type(device=None, sycl_queue=None):
464463
return map_dtype_to_device(float64, _sycl_queue.sycl_device)
465464

466465

467-
def from_dlpack(obj, /, *, device=None, copy=None):
468-
"""
469-
Create a dpnp array from a Python object implementing the ``__dlpack__``
470-
protocol.
471-
472-
See https://dmlc.github.io/dlpack/latest/ for more details.
473-
474-
Parameters
475-
----------
476-
obj : object
477-
A Python object representing an array that implements the ``__dlpack__``
478-
and ``__dlpack_device__`` methods.
479-
device : {:class:`dpctl.SyclDevice`, :class:`dpctl.SyclQueue`,
480-
:class:`dpctl.tensor.Device`, tuple, None}, optional
481-
Array API concept of a device where the output array is to be placed.
482-
``device`` can be ``None``, an oneAPI filter selector string,
483-
an instance of :class:`dpctl.SyclDevice` corresponding to
484-
a non-partitioned SYCL device, an instance of :class:`dpctl.SyclQueue`,
485-
a :class:`dpctl.tensor.Device` object returned by
486-
:attr:`dpctl.tensor.usm_ndarray.device`, or a 2-tuple matching
487-
the format of the output of the ``__dlpack_device__`` method,
488-
an integer enumerator representing the device type followed by
489-
an integer representing the index of the device.
490-
Default: ``None``.
491-
copy {bool, None}, optional
492-
Boolean indicating whether or not to copy the input.
493-
494-
* If `copy``is ``True``, the input will always be copied.
495-
* If ``False``, a ``BufferError`` will be raised if a copy is deemed
496-
necessary.
497-
* If ``None``, a copy will be made only if deemed necessary, otherwise,
498-
the existing memory buffer will be reused.
499-
500-
Default: ``None``.
501-
502-
Returns
503-
-------
504-
out : dpnp_array
505-
Returns a new dpnp array containing the data from another array
506-
(obj) with the ``__dlpack__`` method on the same device as object.
507-
508-
Raises
509-
------
510-
TypeError:
511-
if `obj` does not implement ``__dlpack__`` method
512-
ValueError:
513-
if the input array resides on an unsupported device
514-
515-
"""
516-
517-
usm_res = dpt.from_dlpack(obj, device=device, copy=copy)
518-
return dpnp_array._create_from_usm_ndarray(usm_res)
519-
520-
521466
def get_dpnp_descriptor(
522467
ext_obj,
523468
copy_when_strides=True,

dpnp/dpnp_iface_arraycreation.py

+88
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
"fromfunction",
7676
"fromiter",
7777
"fromstring",
78+
"from_dlpack",
7879
"full",
7980
"full_like",
8081
"geomspace",
@@ -1956,6 +1957,93 @@ def fromstring(
19561957
)
19571958

19581959

1960+
def from_dlpack(x, /, *, device=None, copy=None):
1961+
"""
1962+
Constructs :class:`dpnp.ndarray` or :class:`numpy.ndarray` instance from
1963+
a Python object `x` that implements ``__dlpack__`` protocol.
1964+
1965+
For full documentation refer to :obj:`numpy.from_dlpack`.
1966+
1967+
Parameters
1968+
----------
1969+
x : object
1970+
A Python object representing an array that implements the ``__dlpack__``
1971+
and ``__dlpack_device__`` methods.
1972+
device : {None, string, tuple, device}, optional
1973+
Device where the output array is to be placed. `device` keyword values
1974+
can be:
1975+
1976+
* ``None`` : The data remains on the same device.
1977+
* oneAPI filter selector string : SYCL device selected by filter
1978+
selector string.
1979+
* :class:`dpctl.SyclDevice` : Explicit SYCL device that must correspond
1980+
to a non-partitioned SYCL device.
1981+
* :class:`dpctl.SyclQueue` : Implies SYCL device targeted by the SYCL
1982+
queue.
1983+
* :class:`dpctl.tensor.Device` : Implies SYCL device
1984+
``device.sycl_queue``. The `device` object is obtained via
1985+
:attr:`dpctl.tensor.usm_ndarray.device`.
1986+
* ``(device_type, device_id)`` : 2-tuple matching the format of the
1987+
output of the ``__dlpack_device__`` method: an integer enumerator
1988+
representing the device type followed by an integer representing
1989+
the index of the device. The only supported :class:`dpnp.DLDeviceType`
1990+
device types are ``"kDLCPU"`` and ``"kDLOneAPI"``.
1991+
1992+
Default: ``None``.
1993+
copy : {bool, None}, optional
1994+
Boolean indicating whether or not to copy the input.
1995+
1996+
* If `copy` is ``True``, the input will always be copied.
1997+
* If ``False``, a ``BufferError`` will be raised if a copy is deemed
1998+
necessary.
1999+
* If ``None``, a copy will be made only if deemed necessary, otherwise,
2000+
the existing memory buffer will be reused.
2001+
2002+
Default: ``None``.
2003+
2004+
Returns
2005+
-------
2006+
out : {dpnp.ndarray, numpy.ndarray}
2007+
An array containing the data in `x`. When `copy` is ``None`` or
2008+
``False``, this may be a view into the original memory.
2009+
The type of the returned object depends on where the data backing up
2010+
input object `x` resides. If it resides in a USM allocation on a SYCL
2011+
device, the type :class:`dpnp.ndarray` is returned, otherwise if it
2012+
resides on ``"kDLCPU"`` device the type is :class:`numpy.ndarray`, and
2013+
otherwise an exception is raised.
2014+
2015+
Raises
2016+
------
2017+
TypeError
2018+
if `obj` does not implement ``__dlpack__`` method
2019+
ValueError
2020+
if data of the input object resides on an unsupported device
2021+
2022+
Notes
2023+
-----
2024+
If the return type is :class:`dpnp.ndarray`, the associated SYCL queue is
2025+
derived from the `device` keyword. When `device` keyword value has type
2026+
:class:`dpctl.SyclQueue`, the explicit queue instance is used, when `device`
2027+
keyword value has type :class:`dpctl.tensor.Device`, the
2028+
``device.sycl_queue`` is used. In all other cases, the cached SYCL queue
2029+
corresponding to the implied SYCL device is used.
2030+
2031+
Examples
2032+
--------
2033+
>>> import dpnp as np
2034+
>>> import numpy
2035+
>>> x = numpy.arange(10)
2036+
>>> # create a view of the numpy array "x" in dpnp:
2037+
>>> y = np.from_dlpack(x)
2038+
2039+
"""
2040+
2041+
result = dpt.from_dlpack(x, device=device, copy=copy)
2042+
if isinstance(result, dpt.usm_ndarray):
2043+
return dpnp_array._create_from_usm_ndarray(result)
2044+
return result
2045+
2046+
19592047
def full(
19602048
shape,
19612049
fill_value,

tests/test_dlpack.py

+15-5
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
get_all_dtypes,
99
)
1010

11-
device_oneAPI = 14 # DLDeviceType.kDLOneAPI
12-
1311

1412
class TestDLPack:
1513
@pytest.mark.parametrize("stream", [None, 1])
@@ -56,11 +54,11 @@ def test_non_contiguous(self, xp):
5654

5755
def test_device(self):
5856
x = dpnp.arange(5)
59-
assert x.__dlpack_device__()[0] == device_oneAPI
57+
assert x.__dlpack_device__()[0] == dpnp.DLDeviceType.kDLOneAPI
6058
y = dpnp.from_dlpack(x)
61-
assert y.__dlpack_device__()[0] == device_oneAPI
59+
assert y.__dlpack_device__()[0] == dpnp.DLDeviceType.kDLOneAPI
6260
z = y[::2]
63-
assert z.__dlpack_device__()[0] == device_oneAPI
61+
assert z.__dlpack_device__()[0] == dpnp.DLDeviceType.kDLOneAPI
6462

6563
def test_ndim0(self):
6664
x = dpnp.array(1.0)
@@ -72,3 +70,15 @@ def test_device(self):
7270
y = dpnp.from_dlpack(x, device=x.__dlpack_device__())
7371
assert x.device == y.device
7472
assert x.get_array()._pointer == y.get_array()._pointer
73+
74+
def test_numpy_input(self):
75+
x = numpy.arange(10)
76+
77+
y = dpnp.from_dlpack(x)
78+
assert isinstance(y, numpy.ndarray)
79+
assert y.ctypes.data == x.ctypes.data
80+
assert y.dtype == x.dtype
81+
82+
z = dpnp.from_dlpack(x, device=(dpnp.DLDeviceType.kDLCPU, 0))
83+
assert isinstance(z, numpy.ndarray)
84+
assert z.dtype == y.dtype

0 commit comments

Comments
 (0)