From 80dcf1366fc57602f4291422c961cc1a2c7c10d9 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 10 Jul 2025 15:43:52 +0200 Subject: [PATCH 1/7] Add dpnp.ndarray.data property --- dpnp/dpnp_array.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index f47383619dc..791b2a8178c 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -29,6 +29,7 @@ from dpctl.tensor._numpy_helper import AxisError import dpnp +import dpnp.memory as dpm def _get_unwrapped_index_key(key): @@ -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 @@ -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): """ From 9455fafeaec730a246a6d97ac33e4622dc4bc389 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 10 Jul 2025 15:45:39 +0200 Subject: [PATCH 2/7] Add dpnp.memory module to return s data object holding USM memory --- dpnp/dpnp_iface.py | 1 + dpnp/memory/__init__.py | 39 ++++++++++++++++ dpnp/memory/_memory.py | 99 +++++++++++++++++++++++++++++++++++++++++ setup.py | 1 + 4 files changed, 140 insertions(+) create mode 100644 dpnp/memory/__init__.py create mode 100644 dpnp/memory/_memory.py diff --git a/dpnp/dpnp_iface.py b/dpnp/dpnp_iface.py index 20cbf41b407..70d1606e0d9 100644 --- a/dpnp/dpnp_iface.py +++ b/dpnp/dpnp_iface.py @@ -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__ = [ diff --git a/dpnp/memory/__init__.py b/dpnp/memory/__init__.py new file mode 100644 index 00000000000..bef741fe3d1 --- /dev/null +++ b/dpnp/memory/__init__.py @@ -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", +] diff --git a/dpnp/memory/_memory.py b/dpnp/memory/_memory.py new file mode 100644 index 00000000000..41238584ec1 --- /dev/null +++ b/dpnp/memory/_memory.py @@ -0,0 +1,99 @@ +# -*- 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): + 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)}") diff --git a/setup.py b/setup.py index 63b8f006316..7285ce0cf3f 100644 --- a/setup.py +++ b/setup.py @@ -35,6 +35,7 @@ "dpnp.dpnp_utils", "dpnp.fft", "dpnp.linalg", + "dpnp.memory", "dpnp.random", ], package_data={ From 7918bcf7a4195b18650b718766ba4925f5b20dd9 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 10 Jul 2025 16:31:45 +0200 Subject: [PATCH 3/7] Update third party tests --- .../cupy/core_tests/test_dlpack.py | 23 +++++------ .../cupy/core_tests/test_ndarray.py | 10 +++-- .../cupy/creation_tests/test_from_data.py | 35 +++++++++-------- .../cupy/misc_tests/test_byte_bounds.py | 38 ++++++++----------- 4 files changed, 53 insertions(+), 53 deletions(-) diff --git a/dpnp/tests/third_party/cupy/core_tests/test_dlpack.py b/dpnp/tests/third_party/cupy/core_tests/test_dlpack.py index 15e4c9c72fb..65feab0a041 100644 --- a/dpnp/tests/third_party/cupy/core_tests/test_dlpack.py +++ b/dpnp/tests/third_party/cupy/core_tests/test_dlpack.py @@ -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: @@ -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): @@ -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") @@ -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") @@ -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 ) @@ -267,8 +267,7 @@ 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() @@ -276,3 +275,5 @@ def test_multiple_consumption_error(self): 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) diff --git a/dpnp/tests/third_party/cupy/core_tests/test_ndarray.py b/dpnp/tests/third_party/cupy/core_tests/test_ndarray.py index 955f86f5d3a..3c7c2c9ed77 100644 --- a/dpnp/tests/third_party/cupy/core_tests/test_ndarray.py +++ b/dpnp/tests/third_party/cupy/core_tests/test_ndarray.py @@ -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) @@ -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)) @@ -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") diff --git a/dpnp/tests/third_party/cupy/creation_tests/test_from_data.py b/dpnp/tests/third_party/cupy/creation_tests/test_from_data.py index ba82ca6c4b4..480612ba6aa 100644 --- a/dpnp/tests/third_party/cupy/creation_tests/test_from_data.py +++ b/dpnp/tests/third_party/cupy/creation_tests/test_from_data.py @@ -62,7 +62,7 @@ def test_array_from_numpy_broad_cast(self, xp, dtype, order): @testing.for_orders("CFAK", name="src_order") @testing.for_orders("CFAK", name="dst_order") @testing.for_all_dtypes() - @testing.numpy_cupy_array_equal() + @testing.numpy_cupy_array_equal(strides_check=True) def test_array_from_list_of_numpy(self, xp, dtype, src_order, dst_order): # compares numpy.array() with # cupy.array() @@ -75,7 +75,7 @@ def test_array_from_list_of_numpy(self, xp, dtype, src_order, dst_order): @testing.for_orders("CFAK", name="src_order") @testing.for_orders("CFAK", name="dst_order") @testing.for_all_dtypes() - @testing.numpy_cupy_array_equal() + @testing.numpy_cupy_array_equal(strides_check=True) def test_array_from_list_of_numpy_view( self, xp, dtype, src_order, dst_order ): @@ -93,7 +93,7 @@ def test_array_from_list_of_numpy_view( @testing.for_orders("CFAK") @testing.for_all_dtypes() - @testing.numpy_cupy_array_equal() + @testing.numpy_cupy_array_equal(strides_check=True) def test_array_from_list_of_numpy_scalar(self, xp, dtype, order): # compares numpy.array() with # cupy.array() @@ -103,7 +103,7 @@ def test_array_from_list_of_numpy_scalar(self, xp, dtype, order): @testing.for_orders("CFAK", name="src_order") @testing.for_orders("CFAK", name="dst_order") @testing.for_all_dtypes() - @testing.numpy_cupy_array_equal() + @testing.numpy_cupy_array_equal(strides_check=True) def test_array_from_nested_list_of_numpy( self, xp, dtype, src_order, dst_order ): @@ -118,7 +118,9 @@ def test_array_from_nested_list_of_numpy( @testing.for_orders("CFAK", name="src_order") @testing.for_orders("CFAK", name="dst_order") @testing.for_all_dtypes_combination(names=("dtype1", "dtype2")) - @testing.numpy_cupy_array_equal(type_check=has_support_aspect64()) + @testing.numpy_cupy_array_equal( + type_check=has_support_aspect64(), strides_check=True + ) def test_array_from_list_of_cupy( self, xp, dtype1, dtype2, src_order, dst_order ): @@ -133,7 +135,7 @@ def test_array_from_list_of_cupy( @testing.for_orders("CFAK", name="src_order") @testing.for_orders("CFAK", name="dst_order") @testing.for_all_dtypes() - @testing.numpy_cupy_array_equal() + @testing.numpy_cupy_array_equal(strides_check=True) def test_array_from_list_of_cupy_view( self, xp, dtype, src_order, dst_order ): @@ -152,7 +154,7 @@ def test_array_from_list_of_cupy_view( @testing.for_orders("CFAK", name="src_order") @testing.for_orders("CFAK", name="dst_order") @testing.for_all_dtypes() - @testing.numpy_cupy_array_equal() + @testing.numpy_cupy_array_equal(strides_check=True) def test_array_from_nested_list_of_cupy( self, xp, dtype, src_order, dst_order ): @@ -166,7 +168,7 @@ def test_array_from_nested_list_of_cupy( @testing.for_orders("CFAK") @testing.for_all_dtypes() - @testing.numpy_cupy_array_equal() + @testing.numpy_cupy_array_equal(strides_check=True) def test_array_from_list_of_cupy_scalar(self, xp, dtype, order): # compares numpy.array() with # cupy.array() @@ -240,7 +242,7 @@ def test_array_copy_with_dtype_being_none(self, xp, order): @testing.for_orders("CFAK", name="dst_order") @testing.for_all_dtypes(name="dtype1", no_complex=True) @testing.for_all_dtypes(name="dtype2") - @testing.numpy_cupy_array_equal() + @testing.numpy_cupy_array_equal(strides_check=True) def test_array_copy_list_of_numpy_with_dtype( self, xp, dtype1, dtype2, src_order, dst_order ): @@ -256,7 +258,7 @@ def test_array_copy_list_of_numpy_with_dtype( @testing.for_orders("CFAK", name="dst_order") @testing.for_all_dtypes(name="dtype1", no_complex=True) @testing.for_all_dtypes(name="dtype2") - @testing.numpy_cupy_array_equal() + @testing.numpy_cupy_array_equal(strides_check=True) def test_array_copy_list_of_numpy_with_dtype_char( self, xp, dtype1, dtype2, src_order, dst_order ): @@ -272,7 +274,7 @@ def test_array_copy_list_of_numpy_with_dtype_char( @testing.for_orders("CFAK", name="dst_order") @testing.for_all_dtypes(name="dtype1", no_complex=True) @testing.for_all_dtypes(name="dtype2") - @testing.numpy_cupy_array_equal() + @testing.numpy_cupy_array_equal(strides_check=True) def test_array_copy_list_of_cupy_with_dtype( self, xp, dtype1, dtype2, src_order, dst_order ): @@ -288,7 +290,7 @@ def test_array_copy_list_of_cupy_with_dtype( @testing.for_orders("CFAK", name="dst_order") @testing.for_all_dtypes(name="dtype1", no_complex=True) @testing.for_all_dtypes(name="dtype2") - @testing.numpy_cupy_array_equal() + @testing.numpy_cupy_array_equal(strides_check=True) def test_array_copy_list_of_cupy_with_dtype_char( self, xp, dtype1, dtype2, src_order, dst_order ): @@ -656,7 +658,7 @@ def test_with_strides(self, dtype): DummyObjectWithCudaArrayInterface(a, self.ver, self.strides) ) assert a.strides == b.strides - assert a.nbytes == b.nbytes + assert a.nbytes == b.data.mem.size @testing.for_all_dtypes() def test_with_zero_size_array(self, dtype): @@ -665,7 +667,8 @@ def test_with_zero_size_array(self, dtype): DummyObjectWithCudaArrayInterface(a, self.ver, self.strides) ) assert a.strides == b.strides - assert a.nbytes == b.nbytes + assert a.nbytes == b.data.mem.size + assert a.data.ptr == 0 assert a.size == 0 @testing.for_all_dtypes() @@ -724,7 +727,7 @@ def test_with_over_size_array(self): testing.assert_array_equal(a, b) -class DummyObjectWithCudaArrayInterface(object): +class DummyObjectWithCudaArrayInterface: def __init__(self, a, ver, include_strides=False, mask=None, stream=None): assert ver in tuple(range(max_cuda_array_interface_version + 1)) self.a = None @@ -834,7 +837,7 @@ def test_cupy_array(self, dtype): is_copied = not ( (actual is a) or (self.xp is cupy) - and (a.get_array()._pointer == actual.get_array()._pointer) + and (a.data.ptr == actual.data.ptr) ) assert should_copy == is_copied diff --git a/dpnp/tests/third_party/cupy/misc_tests/test_byte_bounds.py b/dpnp/tests/third_party/cupy/misc_tests/test_byte_bounds.py index a0cfb53f93b..9e1158c8e87 100644 --- a/dpnp/tests/third_party/cupy/misc_tests/test_byte_bounds.py +++ b/dpnp/tests/third_party/cupy/misc_tests/test_byte_bounds.py @@ -8,16 +8,16 @@ class TestByteBounds: def test_1d_contiguous(self, dtype): a = cupy.zeros(12, dtype=dtype) itemsize = a.itemsize - a_low = a.get_array()._pointer - a_high = a.get_array()._pointer + 12 * itemsize + a_low = a.data.ptr + a_high = a.data.ptr + 12 * itemsize assert cupy.byte_bounds(a) == (a_low, a_high) @testing.for_all_dtypes() def test_2d_contiguous(self, dtype): a = cupy.zeros((4, 7), dtype=dtype) itemsize = a.itemsize - a_low = a.get_array()._pointer - a_high = a.get_array()._pointer + 4 * 7 * itemsize + a_low = a.data.ptr + a_high = a.data.ptr + 4 * 7 * itemsize assert cupy.byte_bounds(a) == (a_low, a_high) @testing.for_all_dtypes() @@ -25,8 +25,8 @@ def test_1d_noncontiguous_pos_stride(self, dtype): a = cupy.zeros(12, dtype=dtype) itemsize = a.itemsize b = a[::2] - b_low = b.get_array()._pointer - b_high = b.get_array()._pointer + 11 * itemsize # a[10] + b_low = b.data.ptr + b_high = b.data.ptr + 11 * itemsize # a[10] assert cupy.byte_bounds(b) == (b_low, b_high) @testing.for_all_dtypes() @@ -34,8 +34,8 @@ def test_2d_noncontiguous_pos_stride(self, dtype): a = cupy.zeros((4, 7), dtype=dtype) b = a[::2, ::2] itemsize = b.itemsize - b_low = a.get_array()._pointer - b_high = b.get_array()._pointer + 3 * 7 * itemsize # a[2][6] + b_low = a.data.ptr + b_high = b.data.ptr + 3 * 7 * itemsize # a[2][6] assert cupy.byte_bounds(b) == (b_low, b_high) @testing.for_all_dtypes() @@ -43,8 +43,8 @@ def test_1d_contiguous_neg_stride(self, dtype): a = cupy.zeros(12, dtype=dtype) b = a[::-1] itemsize = b.itemsize - b_low = b.get_array()._pointer - 11 * itemsize - b_high = b.get_array()._pointer + 1 * itemsize + b_low = b.data.ptr - 11 * itemsize + b_high = b.data.ptr + 1 * itemsize assert cupy.byte_bounds(b) == (b_low, b_high) @testing.for_all_dtypes() @@ -52,12 +52,8 @@ def test_2d_noncontiguous_neg_stride(self, dtype): a = cupy.zeros((4, 7), dtype=dtype) b = a[::-2, ::-2] # strides = (-56, -8), shape = (2, 4) itemsize = b.itemsize - b_low = ( - b.get_array()._pointer - - 2 * 7 * itemsize * (2 - 1) - - 2 * itemsize * (4 - 1) - ) - b_high = b.get_array()._pointer + 1 * itemsize + b_low = b.data.ptr - 2 * 7 * itemsize * (2 - 1) - 2 * itemsize * (4 - 1) + b_high = b.data.ptr + 1 * itemsize assert cupy.byte_bounds(b) == (b_low, b_high) @testing.for_all_dtypes() @@ -65,8 +61,8 @@ def test_2d_noncontiguous_posneg_stride_1(self, dtype): a = cupy.zeros((4, 7), dtype=dtype) b = a[::1, ::-1] # strides = (28, -4), shape=(4, 7) itemsize = b.itemsize - b_low = b.get_array()._pointer - itemsize * (7 - 1) - b_high = b.get_array()._pointer + 1 * itemsize + 7 * itemsize * (4 - 1) + b_low = b.data.ptr - itemsize * (7 - 1) + b_high = b.data.ptr + 1 * itemsize + 7 * itemsize * (4 - 1) assert cupy.byte_bounds(b) == (b_low, b_high) @testing.for_all_dtypes() @@ -74,8 +70,6 @@ def test_2d_noncontiguous_posneg_stride_2(self, dtype): a = cupy.zeros((4, 7), dtype=dtype) b = a[::2, ::-2] # strides = (56, -8), shape=(2, 4) itemsize = b.itemsize - b_low = b.get_array()._pointer - 2 * itemsize * (4 - 1) - b_high = ( - b.get_array()._pointer + 1 * itemsize + 2 * 7 * itemsize * (2 - 1) - ) + b_low = b.data.ptr - 2 * itemsize * (4 - 1) + b_high = b.data.ptr + 1 * itemsize + 2 * 7 * itemsize * (2 - 1) assert cupy.byte_bounds(b) == (b_low, b_high) From 4eba18a02b6eca4d7fe671cf3c0b76f30e80726a Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 10 Jul 2025 17:22:16 +0200 Subject: [PATCH 4/7] Use ndarray.data.ptr in the tests --- dpnp/tests/test_dlpack.py | 2 +- dpnp/tests/third_party/cupy/linalg_tests/test_einsum.py | 4 +--- dpnp/tests/third_party/cupy/manipulation_tests/test_shape.py | 4 ++-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/dpnp/tests/test_dlpack.py b/dpnp/tests/test_dlpack.py index 9a8783b5dbd..32b72452bfa 100644 --- a/dpnp/tests/test_dlpack.py +++ b/dpnp/tests/test_dlpack.py @@ -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) diff --git a/dpnp/tests/third_party/cupy/linalg_tests/test_einsum.py b/dpnp/tests/third_party/cupy/linalg_tests/test_einsum.py index bb44b791d41..484b0fbd3e4 100644 --- a/dpnp/tests/third_party/cupy/linalg_tests/test_einsum.py +++ b/dpnp/tests/third_party/cupy/linalg_tests/test_einsum.py @@ -359,9 +359,7 @@ def test_einsum_unary_views(self, xp, dtype): a = testing.shaped_arange(self.shape_a, xp, dtype) b = xp.einsum(self.subscripts, a) if xp is cupy: - return ( - b.ndim == 0 or b.get_array()._pointer == a.get_array()._pointer - ) + return b.ndim == 0 or b.data.ptr == a.data.ptr return b.ndim == 0 or b.base is a @testing.for_all_dtypes_combination( diff --git a/dpnp/tests/third_party/cupy/manipulation_tests/test_shape.py b/dpnp/tests/third_party/cupy/manipulation_tests/test_shape.py index 49f22951e4b..bec0215d4b6 100644 --- a/dpnp/tests/third_party/cupy/manipulation_tests/test_shape.py +++ b/dpnp/tests/third_party/cupy/manipulation_tests/test_shape.py @@ -103,7 +103,7 @@ def test_reshape_zerosize(self, xp): a = xp.zeros((0,)) b = a.reshape((0,)) if xp is cupy: - assert a.get_array()._pointer == b.get_array()._pointer + assert a.data.ptr == b.data.ptr else: assert b.base is a return b @@ -114,7 +114,7 @@ def test_reshape_zerosize2(self, xp, order): a = xp.zeros((2, 0, 3)) b = a.reshape((5, 0, 4), order=order) if xp is cupy: - assert a.get_array()._pointer == b.get_array()._pointer + assert a.data.ptr == b.data.ptr else: assert b.base is a return b From 6f15dff3137418f558ada450cb7cb059e781468f Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 10 Jul 2025 17:24:20 +0200 Subject: [PATCH 5/7] Add PR to the changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8d4adca875..3c0dfb4c492 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 From f1a039c668701323c1a2bb507eedb96f8211543a Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 10 Jul 2025 21:49:31 +0200 Subject: [PATCH 6/7] Remove stride checks where not supported still --- .../third_party/cupy/creation_tests/test_from_data.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dpnp/tests/third_party/cupy/creation_tests/test_from_data.py b/dpnp/tests/third_party/cupy/creation_tests/test_from_data.py index 480612ba6aa..d3e42cc66c3 100644 --- a/dpnp/tests/third_party/cupy/creation_tests/test_from_data.py +++ b/dpnp/tests/third_party/cupy/creation_tests/test_from_data.py @@ -242,7 +242,7 @@ def test_array_copy_with_dtype_being_none(self, xp, order): @testing.for_orders("CFAK", name="dst_order") @testing.for_all_dtypes(name="dtype1", no_complex=True) @testing.for_all_dtypes(name="dtype2") - @testing.numpy_cupy_array_equal(strides_check=True) + @testing.numpy_cupy_array_equal() def test_array_copy_list_of_numpy_with_dtype( self, xp, dtype1, dtype2, src_order, dst_order ): @@ -258,7 +258,7 @@ def test_array_copy_list_of_numpy_with_dtype( @testing.for_orders("CFAK", name="dst_order") @testing.for_all_dtypes(name="dtype1", no_complex=True) @testing.for_all_dtypes(name="dtype2") - @testing.numpy_cupy_array_equal(strides_check=True) + @testing.numpy_cupy_array_equal() def test_array_copy_list_of_numpy_with_dtype_char( self, xp, dtype1, dtype2, src_order, dst_order ): @@ -274,7 +274,7 @@ def test_array_copy_list_of_numpy_with_dtype_char( @testing.for_orders("CFAK", name="dst_order") @testing.for_all_dtypes(name="dtype1", no_complex=True) @testing.for_all_dtypes(name="dtype2") - @testing.numpy_cupy_array_equal(strides_check=True) + @testing.numpy_cupy_array_equal() def test_array_copy_list_of_cupy_with_dtype( self, xp, dtype1, dtype2, src_order, dst_order ): @@ -290,7 +290,7 @@ def test_array_copy_list_of_cupy_with_dtype( @testing.for_orders("CFAK", name="dst_order") @testing.for_all_dtypes(name="dtype1", no_complex=True) @testing.for_all_dtypes(name="dtype2") - @testing.numpy_cupy_array_equal(strides_check=True) + @testing.numpy_cupy_array_equal() def test_array_copy_list_of_cupy_with_dtype_char( self, xp, dtype1, dtype2, src_order, dst_order ): From ace5d19a99232ad078a604b4fa6e2740ad0853a9 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Fri, 11 Jul 2025 10:52:05 +0200 Subject: [PATCH 7/7] Add docstring to ptr attribute --- dpnp/memory/_memory.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dpnp/memory/_memory.py b/dpnp/memory/_memory.py index 41238584ec1..97e793e9fae 100644 --- a/dpnp/memory/_memory.py +++ b/dpnp/memory/_memory.py @@ -35,6 +35,12 @@ def _add_ptr_property(cls): @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