Skip to content

Backport gh-2209 #2220

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.16.1] - 12/05/2024
## [0.16.1] - 12/06/2024

This is a bug-fix release.

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

* Resolved an issue with Compute Follows Data inconsistency in `dpnp.extract` function [#2172](https://github.com/IntelPython/dpnp/pull/2172)
* Resolves an import error when using `dpnp` in virtual environment on Linux [#2199](https://github.com/IntelPython/dpnp/pull/2199)
* Resolved an import error when using `dpnp` in virtual environment on Linux [#2199](https://github.com/IntelPython/dpnp/pull/2199)
* Fixed incorrect result produced by `dpnp.fft.fft` function when input array has negative strides [#2202](https://github.com/IntelPython/dpnp/pull/2202)
* 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)
* Resolved a compilation error when building with DPC++ 2025.1 compiler [#2211](https://github.com/IntelPython/dpnp/pull/2211)


Expand Down
1 change: 1 addition & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,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 @@ -63,7 +63,6 @@
"check_supported_arrays_type",
"convert_single_elem_array_to_scalar",
"default_float_type",
"from_dlpack",
"get_dpnp_descriptor",
"get_include",
"get_normalized_queue_device",
Expand Down Expand Up @@ -464,60 +463,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 @@ -1956,6 +1957,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 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
Loading