Skip to content
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 @@ -99,6 +99,7 @@ This release is compatible with NumPy 2.5.
* Fixed `dpnp.cumsum`, `dpnp.cumprod`, and their `nan`/`cumulative_*` variants (including `dpnp.tensor.cumulative_sum`/`cumulative_prod`) silently returning incorrect results when accumulating along an axis of an array with more than one row [#3063](https://github.com/IntelPython/dpnp/pull/3063)
* Fixed `dpnp.einsum` returning a result whose memory layout differs from NumPy for the default `order="K"`, and ignoring `out` and `order` for a contraction over a size-0 dimension [#3058](https://github.com/IntelPython/dpnp/pull/3058)
* Fixed operations on a boolean array whose bytes are not `0x00`/`0x01` [#3055](https://github.com/IntelPython/dpnp/pull/3055)
* Fixed the `dpnp.ndarray` constructor returning a view at the wrong address [#3068](https://github.com/IntelPython/dpnp/pull/3068)

### Security

Expand Down
21 changes: 19 additions & 2 deletions dpnp/dpnp_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,26 @@ def __init__(
# or as USM memory allocation
if isinstance(buffer, dpnp_array):
buffer = buffer.get_array()
offset += buffer._element_offset

if dtype is None and hasattr(buffer, "dtype"):
if isinstance(buffer, dpt.usm_ndarray):
if dtype is None:
dtype = buffer.dtype

# `buffer._element_offset` is expressed in units of the
# buffer's own dtype, while `offset` is interpreted in units
# of `dtype`, so the displacement has to be rescaled through
# bytes whenever the two itemsizes differ
byte_offset = buffer._element_offset * buffer.itemsize
new_itemsize = dpnp.dtype(dtype).itemsize
add_offset, rem = divmod(byte_offset, new_itemsize)
if rem != 0:
raise ValueError(
"The offset of the buffer's data in memory is not "
"a multiple of the requested dtype size and so the "
"requested view is not possible"
)
offset += add_offset
elif dtype is None and hasattr(buffer, "dtype"):
dtype = buffer.dtype
else:
buffer = usm_type
Expand Down
62 changes: 62 additions & 0 deletions dpnp/tests/test_ndarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,68 @@ def test_nonzero_offset_buffer_ctor(self):
assert_array_equal(ia.view(), expected)
assert_array_equal(ia.view(dpnp.uint32), expected.view(numpy.uint32))

@pytest.mark.parametrize(
"src_dt, new_dt",
[
(dpnp.complex64, dpnp.uint16),
(dpnp.complex128, dpnp.float64),
(dpnp.float64, dpnp.float32),
(dpnp.int64, dpnp.int8),
(dpnp.int32, dpnp.int16),
(dpnp.int16, dpnp.int64),
],
)
def test_nonzero_offset_buffer_ctor_dtype_mismatch(self, src_dt, new_dt):
# the element offset of the `buffer=` array is expressed in units of
# the buffer's own dtype and has to be rescaled when the requested
# dtype has a different itemsize
base = dpnp.arange(32, dtype=src_dt)
sl = base[8:]

byte_offset = 8 * dpnp.dtype(src_dt).itemsize
size = (base.nbytes - byte_offset) // dpnp.dtype(new_dt).itemsize

ia = dpnp.ndarray((size,), dtype=new_dt, buffer=sl)
assert ia.data.ptr == sl.data.ptr
assert_array_equal(ia, dpnp.asnumpy(sl).view(new_dt))

def test_nonzero_offset_buffer_ctor_usm_ndarray(self):
# the same rescaling applies when `buffer=` is a bare usm_ndarray
# rather than a dpnp.ndarray
base = dpnp.arange(16, dtype=dpnp.complex64)
sl = base[4:]
usm_sl = sl.get_array()

for dt in [dpnp.complex64, dpnp.uint16, dpnp.float32]:
size = usm_sl.nbytes // dpnp.dtype(dt).itemsize
ia = dpnp.ndarray((size,), dtype=dt, buffer=usm_sl)
assert ia.data.ptr == sl.data.ptr

# and the dtype still defaults to the buffer's one
ia = dpnp.ndarray((12,), buffer=usm_sl)
assert ia.dtype == base.dtype
assert ia.data.ptr == sl.data.ptr

def test_nonzero_offset_buffer_ctor_write_through(self):
# a write through the dtype-mismatched view must land in the parent
# allocation at the offset the buffer points at
base = dpnp.zeros(16, dtype=dpnp.complex64)
sl = base[8:]

ia = dpnp.ndarray((16,), dtype=dpnp.float32, buffer=sl)
ia[:] = 1

expected = numpy.zeros(16, dtype=numpy.complex64)
expected[8:] = 1 + 1j
assert_array_equal(base, expected)

def test_misaligned_offset_buffer_ctor_error(self):
base = dpnp.arange(16, dtype=dpnp.int16)
# the buffer starts at a byte offset of 6, which is not addressable
# with an itemsize of 8
with pytest.raises(ValueError, match="not a multiple"):
dpnp.ndarray((3,), dtype=dpnp.int64, buffer=base[3:])

def test_misaligned_offset_error(self):
ia = dpnp.arange(10, dtype=dpnp.int16)
# numpy supports such a view, but usm_ndarray cannot address memory
Expand Down