Skip to content

Add implementation of dpnp.ndarray.data and dpnp.ndarray.data.ptr attributes #2521

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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Added `--target-cuda[=ARCH]` option to replace the deprecated `--target=cuda`, allowing users to build for CUDA devices with optional architecture selection using [CodePlay oneAPI plug-in](https://developer.codeplay.com/products/oneapi/nvidia/home/) [#2478](https://github.com/IntelPython/dpnp/pull/2478)
* Added several new `pre-commit` rules, including protection against direct commits to master/maintenance branches [#2500](https://github.com/IntelPython/dpnp/pull/2500)
* Added implementation of `dpnp.ndarray.view` method [#2520](https://github.com/IntelPython/dpnp/pull/2520)
* Added implementation of `dpnp.ndarray.data` and `dpnp.ndarray.data.ptr` attributes [#2521](https://github.com/IntelPython/dpnp/pull/2521)

### Changed

Expand Down
18 changes: 15 additions & 3 deletions dpnp/dpnp_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from dpctl.tensor._numpy_helper import AxisError

import dpnp
import dpnp.memory as dpm


def _get_unwrapped_index_key(key):
Expand Down Expand Up @@ -76,9 +77,12 @@ def __init__(
order = "C"

if buffer is not None:
buffer = dpnp.get_usm_ndarray(buffer)
# expecting to have buffer as dpnp.ndarray and usm_ndarray,
# or as USM memory allocation
if isinstance(buffer, dpnp_array):
buffer = buffer.get_array()

if dtype is None:
if dtype is None and hasattr(buffer, "dtype"):
dtype = buffer.dtype
else:
buffer = usm_type
Expand Down Expand Up @@ -1015,7 +1019,15 @@ def cumsum(self, axis=None, dtype=None, out=None):

return dpnp.cumsum(self, axis=axis, dtype=dtype, out=out)

# 'data',
@property
def data(self):
"""
Python object pointing to the start of USM memory allocation with the
array's data.

"""

return dpm.create_data(self._array_obj)

def diagonal(self, offset=0, axis1=0, axis2=1):
"""
Expand Down
1 change: 1 addition & 0 deletions dpnp/dpnp_iface.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
from dpnp.dpnp_array import dpnp_array
from dpnp.fft import *
from dpnp.linalg import *
from dpnp.memory import *
from dpnp.random import *

__all__ = [
Expand Down
39 changes: 39 additions & 0 deletions dpnp/memory/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# *****************************************************************************
# Copyright (c) 2025, Intel Corporation
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# - Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# - Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
# *****************************************************************************

from ._memory import (
MemoryUSMDevice,
MemoryUSMHost,
MemoryUSMShared,
create_data,
)

__all__ = [
"MemoryUSMDevice",
"MemoryUSMHost",
"MemoryUSMShared",
"create_data",
]
105 changes: 105 additions & 0 deletions dpnp/memory/_memory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
# *****************************************************************************
# Copyright (c) 2025, Intel Corporation
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# - Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# - Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
# *****************************************************************************

import dpctl.tensor as dpt
from dpctl.memory import MemoryUSMDevice as DPCTLMemoryUSMDevice
from dpctl.memory import MemoryUSMHost as DPCTLMemoryUSMHost
from dpctl.memory import MemoryUSMShared as DPCTLMemoryUSMShared


def _add_ptr_property(cls):
_storage_attr = "_ptr"

@property
def ptr(self):
"""
Returns USM pointer to the start of array (element with zero
multi-index) encoded as integer.

"""

return getattr(self, _storage_attr, None)

@ptr.setter
def ptr(self, value):
setattr(self, _storage_attr, value)

cls.ptr = ptr
return cls


@_add_ptr_property
class MemoryUSMDevice(DPCTLMemoryUSMDevice):
pass


@_add_ptr_property
class MemoryUSMHost(DPCTLMemoryUSMHost):
pass


@_add_ptr_property
class MemoryUSMShared(DPCTLMemoryUSMShared):
pass


def create_data(x):
"""
Create an instance of :class:`.MemoryUSMDevice`, :class:`.MemoryUSMHost`,
or :class:`.MemoryUSMShared` class depending on the type of USM allocation.

Parameters
----------
x : usm_ndarray
Input array of :class:`dpctl.tensor.usm_ndarray` type.

Returns
-------
out : {MemoryUSMDevice, MemoryUSMHost, MemoryUSMShared}
A data object with a reference on USM memory.

"""

dispatch = {
DPCTLMemoryUSMDevice: MemoryUSMDevice,
DPCTLMemoryUSMHost: MemoryUSMHost,
DPCTLMemoryUSMShared: MemoryUSMShared,
}

if not isinstance(x, dpt.usm_ndarray):
raise TypeError(
f"An array must be any of supported type, but got {type(x)}"
)
usm_data = x.usm_data

cls = dispatch.get(type(usm_data), None)
if cls:
data = cls(usm_data)
# `ptr`` is expecting to point at the start of the array's data,
# while `usm_data._pointer` is a pointer at the start of memory buffer
data.ptr = x._pointer
return data
raise TypeError(f"Expected USM memory, but got {type(usm_data)}")
2 changes: 1 addition & 1 deletion dpnp/tests/test_dlpack.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def test_device(self):
x = dpnp.arange(5)
y = dpnp.from_dlpack(x, device=x.__dlpack_device__())
assert x.device == y.device
assert x.get_array()._pointer == y.get_array()._pointer
assert x.data.ptr == y.data.ptr

def test_numpy_input(self):
x = numpy.arange(10)
Expand Down
23 changes: 12 additions & 11 deletions dpnp/tests/third_party/cupy/core_tests/test_dlpack.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,15 @@ def __dlpack_device__(self):
@pytest.mark.skip("toDlpack() and fromDlpack() are not supported")
class TestDLPackConversion:

@pytest.mark.filterwarnings("ignore::DeprecationWarning")
@testing.for_all_dtypes(no_bool=False)
def test_conversion(self, dtype):
def test_conversion(self, dtype, recwarn):
orig_array = _gen_array(dtype)
tensor = orig_array.toDlpack()
out_array = cupy.fromDlpack(tensor)
testing.assert_array_equal(orig_array, out_array)
assert orig_array.get_array()._pointer == out_array.get_array()._pointer
testing.assert_array_equal(orig_array.data.ptr, out_array.data.ptr)
for w in recwarn:
assert issubclass(w.category, cupy.VisibleDeprecationWarning)


class TestNewDLPackConversion:
Expand Down Expand Up @@ -82,7 +83,7 @@ def test_conversion(self, dtype):
orig_array = _gen_array(dtype)
out_array = cupy.from_dlpack(orig_array)
testing.assert_array_equal(orig_array, out_array)
assert orig_array.get_array()._pointer == out_array.get_array()._pointer
testing.assert_array_equal(orig_array.data.ptr, out_array.data.ptr)

@pytest.mark.skip("no limitations in from_dlpack()")
def test_from_dlpack_and_conv_errors(self):
Expand Down Expand Up @@ -121,7 +122,7 @@ def test_conversion_max_version(self, kwargs, versioned):
)

testing.assert_array_equal(orig_array, out_array)
assert orig_array.get_array()._pointer == out_array.get_array()._pointer
testing.assert_array_equal(orig_array.data.ptr, out_array.data.ptr)

def test_conversion_device(self):
orig_array = _gen_array("float32")
Expand All @@ -135,7 +136,7 @@ def test_conversion_device(self):
)

testing.assert_array_equal(orig_array, out_array)
assert orig_array.get_array()._pointer == out_array.get_array()._pointer
testing.assert_array_equal(orig_array.data.ptr, out_array.data.ptr)

def test_conversion_bad_device(self):
arr = _gen_array("float32")
Expand Down Expand Up @@ -212,9 +213,8 @@ def test_stream(self):
out_array = dlp.from_dlpack_capsule(dltensor)
out_array = cupy.from_dlpack(out_array, device=dst_s)
testing.assert_array_equal(orig_array, out_array)
assert (
orig_array.get_array()._pointer
== out_array.get_array()._pointer
testing.assert_array_equal(
orig_array.data.ptr, out_array.data.ptr
)


Expand Down Expand Up @@ -267,12 +267,13 @@ def test_deleter2(self, pool, max_version):
# assert pool.n_free_blocks() == 1

@pytest.mark.skip("toDlpack() and fromDlpack() are not supported")
@pytest.mark.filterwarnings("ignore::DeprecationWarning")
def test_multiple_consumption_error(self):
def test_multiple_consumption_error(self, recwarn):
# Prevent segfault, see #3611
array = cupy.empty(10)
tensor = array.toDlpack()
array2 = cupy.fromDlpack(tensor)
with pytest.raises(ValueError) as e:
array3 = cupy.fromDlpack(tensor)
assert "consumed multiple times" in str(e.value)
for w in recwarn:
assert issubclass(w.category, cupy.VisibleDeprecationWarning)
10 changes: 6 additions & 4 deletions dpnp/tests/third_party/cupy/core_tests/test_ndarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ def test_shape_not_integer(self):

def test_shape_int_with_strides(self):
dummy = cupy.ndarray(3)
a = cupy.ndarray(3, strides=(0,), buffer=dummy)
a = cupy.ndarray(3, strides=(0,), buffer=dummy.data)
assert a.shape == (3,)
assert a.strides == (0,)

def test_memptr(self):
a = cupy.arange(6).astype(numpy.float32).reshape((2, 3))
memptr = a
memptr = a.data

b = cupy.ndarray((2, 3), numpy.float32, memptr)
testing.assert_array_equal(a, b)
Expand All @@ -62,7 +62,7 @@ def test_memptr(self):
)
def test_memptr_with_strides(self):
buf = cupy.ndarray(20, numpy.uint8)
memptr = buf
memptr = buf.data

# self-overlapping strides
a = cupy.ndarray((2, 3), numpy.float32, memptr, strides=(2, 1))
Expand All @@ -82,7 +82,9 @@ def test_strides_without_memptr(self):

def test_strides_is_given_and_order_is_ignored(self):
buf = cupy.ndarray(20, numpy.uint8)
a = cupy.ndarray((2, 3), numpy.float32, buf, strides=(2, 1), order="C")
a = cupy.ndarray(
(2, 3), numpy.float32, buf.data, strides=(2, 1), order="C"
)
assert a.strides == (2, 1)

@testing.with_requires("numpy>=1.19")
Expand Down
Loading
Loading