Skip to content

Commit

Permalink
Move dpnp.from_dlpack to array creation routine (IntelPython#2209)
Browse files Browse the repository at this point in the history
The PR proposes to move `dpnp.from_dlpack` function to
`dpnp/dpnp_iface_arraycreation.py` and to update docstring description,
because there were formatting issues within rendered documentation of
`dpnp.from_dlpack`.

Also sphinx configuration was tuned to add mapping towards dpctl
documentation pages.
  • Loading branch information
antonwolfy authored Dec 5, 2024
1 parent 3f737be commit e53fa72
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 60 deletions.
1 change: 1 addition & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ def _can_document_member(member, *args, **kwargs):
"python": ("https://docs.python.org/3/", None),
"numpy": ("https://docs.scipy.org/doc/numpy/", None),
"scipy": ("https://docs.scipy.org/doc/scipy/reference/", None),
"dpctl": ("https://intelpython.github.io/dpctl/latest/", None),
}

# If true, `todo` and `todoList` produce output, else they produce nothing.
Expand Down
3 changes: 3 additions & 0 deletions dpnp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
[os.getenv("PATH", ""), mypath, dpctlpath]
)

# Borrowed from DPCTL
from dpctl.tensor import DLDeviceType

from dpnp.dpnp_array import dpnp_array as ndarray
from dpnp.dpnp_flatiter import flatiter as flatiter
from dpnp.dpnp_iface_types import *
Expand Down
55 changes: 0 additions & 55 deletions dpnp/dpnp_iface.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@
"check_limitations",
"check_supported_arrays_type",
"default_float_type",
"from_dlpack",
"get_dpnp_descriptor",
"get_include",
"get_normalized_queue_device",
Expand Down Expand Up @@ -443,60 +442,6 @@ def default_float_type(device=None, sycl_queue=None):
return map_dtype_to_device(float64, _sycl_queue.sycl_device)


def from_dlpack(obj, /, *, device=None, copy=None):
"""
Create a dpnp array from a Python object implementing the ``__dlpack__``
protocol.
See https://dmlc.github.io/dlpack/latest/ for more details.
Parameters
----------
obj : object
A Python object representing an array that implements the ``__dlpack__``
and ``__dlpack_device__`` methods.
device : {:class:`dpctl.SyclDevice`, :class:`dpctl.SyclQueue`,
:class:`dpctl.tensor.Device`, tuple, None}, optional
Array API concept of a device where the output array is to be placed.
``device`` can be ``None``, an oneAPI filter selector string,
an instance of :class:`dpctl.SyclDevice` corresponding to
a non-partitioned SYCL device, an instance of :class:`dpctl.SyclQueue`,
a :class:`dpctl.tensor.Device` object returned by
:attr:`dpctl.tensor.usm_ndarray.device`, or a 2-tuple matching
the format of the output of the ``__dlpack_device__`` method,
an integer enumerator representing the device type followed by
an integer representing the index of the device.
Default: ``None``.
copy {bool, None}, optional
Boolean indicating whether or not to copy the input.
* If `copy``is ``True``, the input will always be copied.
* If ``False``, a ``BufferError`` will be raised if a copy is deemed
necessary.
* If ``None``, a copy will be made only if deemed necessary, otherwise,
the existing memory buffer will be reused.
Default: ``None``.
Returns
-------
out : dpnp_array
Returns a new dpnp array containing the data from another array
(obj) with the ``__dlpack__`` method on the same device as object.
Raises
------
TypeError:
if `obj` does not implement ``__dlpack__`` method
ValueError:
if the input array resides on an unsupported device
"""

usm_res = dpt.from_dlpack(obj, device=device, copy=copy)
return dpnp_array._create_from_usm_ndarray(usm_res)


def get_dpnp_descriptor(
ext_obj,
copy_when_strides=True,
Expand Down
88 changes: 88 additions & 0 deletions dpnp/dpnp_iface_arraycreation.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"fromfunction",
"fromiter",
"fromstring",
"from_dlpack",
"full",
"full_like",
"geomspace",
Expand Down Expand Up @@ -2047,6 +2048,93 @@ def fromstring(
)


def from_dlpack(x, /, *, device=None, copy=None):
"""
Constructs :class:`dpnp.ndarray` or :class:`numpy.ndarray` instance from
a Python object `x` that implements ``__dlpack__`` protocol.
For full documentation refer to :obj:`numpy.from_dlpack`.
Parameters
----------
x : object
A Python object representing an array that implements the ``__dlpack__``
and ``__dlpack_device__`` methods.
device : {None, string, tuple, device}, optional
Device where the output array is to be placed. `device` keyword values
can be:
* ``None`` : The data remains on the same device.
* oneAPI filter selector string : SYCL device selected by filter
selector string.
* :class:`dpctl.SyclDevice` : Explicit SYCL device that must correspond
to a non-partitioned SYCL device.
* :class:`dpctl.SyclQueue` : Implies SYCL device targeted by the SYCL
queue.
* :class:`dpctl.tensor.Device` : Implies SYCL device
``device.sycl_queue``. The `device` object is obtained via
:attr:`dpctl.tensor.usm_ndarray.device`.
* ``(device_type, device_id)`` : 2-tuple matching the format of the
output of the ``__dlpack_device__`` method: an integer enumerator
representing the device type followed by an integer representing
the index of the device. The only supported :class:`dpnp.DLDeviceType`
device types are ``"kDLCPU"`` and ``"kDLOneAPI"``.
Default: ``None``.
copy : {bool, None}, optional
Boolean indicating whether or not to copy the input.
* If `copy` is ``True``, the input will always be copied.
* If ``False``, a ``BufferError`` will be raised if a copy is deemed
necessary.
* If ``None``, a copy will be made only if deemed necessary, otherwise,
the existing memory buffer will be reused.
Default: ``None``.
Returns
-------
out : {dpnp.ndarray, numpy.ndarray}
An array containing the data in `x`. When `copy` is ``None`` or
``False``, this may be a view into the original memory.
The type of the returned object depends on where the data backing up
input object `x` resides. If it resides in a USM allocation on a SYCL
device, the type :class:`dpnp.ndarray` is returned, otherwise if it
resides on ``"kDLCPU"`` device the type is :class:`numpy.ndarray`, and
otherwise an exception is raised.
Raises
------
TypeError
if `obj` does not implement ``__dlpack__`` method
ValueError
if data of the input object resides on an unsupported device
Notes
-----
If the return type is :class:`dpnp.ndarray`, the associated SYCL queue is
derived from the `device` keyword. When `device` keyword value has type
:class:`dpctl.SyclQueue`, the explicit queue instance is used, when `device`
keyword value has type :class:`dpctl.tensor.Device`, the
``device.sycl_queue`` is used. In all other cases, the cached SYCL queue
corresponding to the implied SYCL device is used.
Examples
--------
>>> import dpnp as np
>>> import numpy
>>> x = numpy.arange(10)
>>> # create a view of the numpy array "x" in dpnp:
>>> y = np.from_dlpack(x)
"""

result = dpt.from_dlpack(x, device=device, copy=copy)
if isinstance(result, dpt.usm_ndarray):
return dpnp_array._create_from_usm_ndarray(result)
return result


def full(
shape,
fill_value,
Expand Down
20 changes: 15 additions & 5 deletions dpnp/tests/test_dlpack.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
get_all_dtypes,
)

device_oneAPI = 14 # DLDeviceType.kDLOneAPI


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

def test_device(self):
x = dpnp.arange(5)
assert x.__dlpack_device__()[0] == device_oneAPI
assert x.__dlpack_device__()[0] == dpnp.DLDeviceType.kDLOneAPI
y = dpnp.from_dlpack(x)
assert y.__dlpack_device__()[0] == device_oneAPI
assert y.__dlpack_device__()[0] == dpnp.DLDeviceType.kDLOneAPI
z = y[::2]
assert z.__dlpack_device__()[0] == device_oneAPI
assert z.__dlpack_device__()[0] == dpnp.DLDeviceType.kDLOneAPI

def test_ndim0(self):
x = dpnp.array(1.0)
Expand All @@ -72,3 +70,15 @@ def test_device(self):
y = dpnp.from_dlpack(x, device=x.__dlpack_device__())
assert x.device == y.device
assert x.get_array()._pointer == y.get_array()._pointer

def test_numpy_input(self):
x = numpy.arange(10)

y = dpnp.from_dlpack(x)
assert isinstance(y, numpy.ndarray)
assert y.ctypes.data == x.ctypes.data
assert y.dtype == x.dtype

z = dpnp.from_dlpack(x, device=(dpnp.DLDeviceType.kDLCPU, 0))
assert isinstance(z, numpy.ndarray)
assert z.dtype == y.dtype

0 comments on commit e53fa72

Please sign in to comment.