Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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 @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* 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 a new backend routine `syrk` from oneMKL to perform symmetric rank-k update which is used for a specialized matrix multiplication where the result is a symmetric matrix [2509](https://github.com/IntelPython/dpnp/pull/2509)
* 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