diff --git a/CHANGELOG.md b/CHANGELOG.md index 24a4b9296aa4..20fcba2ea255 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ Also, that release drops support for Python 3.9, making Python 3.10 the minimum * Changed the build scripts and documentation due to `python setup.py develop` deprecation notice [#2716](https://github.com/IntelPython/dpnp/pull/2716) * Clarified behavior on repeated `axes` in `dpnp.tensordot` and `dpnp.linalg.tensordot` functions [#2733](https://github.com/IntelPython/dpnp/pull/2733) * Improved documentation of `file` argument in `dpnp.fromfile` [#2745](https://github.com/IntelPython/dpnp/pull/2745) +* Aligned `strides` property of `dpnp.ndarray` with NumPy and CuPy implementations [#2747](https://github.com/IntelPython/dpnp/pull/2747) ### Deprecated diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index 6a2b2fd1977f..f37a3a2b3be3 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -105,6 +105,16 @@ def __init__( else: buffer = usm_type + if strides is not None: + # dpctl expects strides as elements displacement in memory, + # while dpnp (and numpy as well) relies on bytes displacement + if dtype is None: + dtype = dpnp.default_float_type( + device=device, sycl_queue=sycl_queue + ) + it_sz = dpnp.dtype(dtype).itemsize + strides = tuple(el // it_sz for el in strides) + sycl_queue_normalized = dpnp.get_normalized_queue_device( device=device, sycl_queue=sycl_queue ) @@ -1855,16 +1865,53 @@ def std( @property def strides(self): """ - Return memory displacement in array elements, upon unit - change of respective index. + Tuple of bytes to step in each dimension when traversing an array. - For example, for strides ``(s1, s2, s3)`` and multi-index - ``(i1, i2, i3)`` position of the respective element relative - to zero multi-index element is ``s1*s1 + s2*i2 + s3*i3``. + The byte offset of element ``(i[0], i[1], ..., i[n])`` in an array `a` + is:: - """ + offset = sum(dpnp.array(i) * a.strides) - return self._array_obj.strides + For full documentation refer to :obj:`numpy.ndarray.strides`. + + See Also + -------- + :obj:`dpnp.lib.stride_tricks.as_strided` : Return a view into the array + with given shape and strides. + + Examples + -------- + >>> import dpnp as np + >>> y = np.reshape(np.arange(2 * 3 * 4, dtype=np.int32), (2, 3, 4)) + >>> y + array([[[ 0, 1, 2, 3], + [ 4, 5, 6, 7], + [ 8, 9, 10, 11]], + [[12, 13, 14, 15], + [16, 17, 18, 19], + [20, 21, 22, 23]]], dtype=np.int32) + >>> y.strides + (48, 16, 4) + >>> y[1, 1, 1] + array(17, dtype=int32) + >>> offset = sum(i * s for i, s in zip((1, 1, 1), y.strides)) + >>> offset // y.itemsize + 17 + + >>> x = np.reshape(np.arange(5*6*7*8, dtype=np.int32), (5, 6, 7, 8)) + >>> x = x.transpose(2, 3, 1, 0) + >>> x.strides + (32, 4, 224, 1344) + >>> offset = sum(i * s for i, s in zip((3, 5, 2, 2), x.strides)) + >>> x[3, 5, 2, 2] + array(813, dtype=int32) + >>> offset // x.itemsize + 813 + + """ + + it_sz = self.itemsize + return tuple(el * it_sz for el in self._array_obj.strides) def sum( self, @@ -2335,23 +2382,20 @@ def view(self, /, dtype=None, *, type=None): # resize on last axis only axis = ndim - 1 - if old_sh[axis] != 1 and self.size != 0 and old_strides[axis] != 1: + if ( + old_sh[axis] != 1 + and self.size != 0 + and old_strides[axis] != old_itemsz + ): raise ValueError( "To change to a dtype of a different size, " "the last axis must be contiguous" ) # normalize strides whenever itemsize changes - if old_itemsz > new_itemsz: - new_strides = list( - el * (old_itemsz // new_itemsz) for el in old_strides - ) - else: - new_strides = list( - el // (new_itemsz // old_itemsz) for el in old_strides - ) - new_strides[axis] = 1 - new_strides = tuple(new_strides) + new_strides = tuple( + old_strides[i] if i != axis else new_itemsz for i in range(ndim) + ) new_dim = old_sh[axis] * old_itemsz if new_dim % new_itemsz != 0: @@ -2361,9 +2405,10 @@ def view(self, /, dtype=None, *, type=None): ) # normalize shape whenever itemsize changes - new_sh = list(old_sh) - new_sh[axis] = new_dim // new_itemsz - new_sh = tuple(new_sh) + new_sh = tuple( + old_sh[i] if i != axis else new_dim // new_itemsz + for i in range(ndim) + ) return dpnp_array( new_sh, diff --git a/dpnp/dpnp_iface_arraycreation.py b/dpnp/dpnp_iface_arraycreation.py index 539df84d3638..e7b902647186 100644 --- a/dpnp/dpnp_iface_arraycreation.py +++ b/dpnp/dpnp_iface_arraycreation.py @@ -105,7 +105,7 @@ def _get_empty_array( elif a.flags.c_contiguous: order = "C" else: - strides = _get_strides_for_order_k(a, _shape) + strides = _get_strides_for_order_k(a, _dtype, shape=_shape) order = "C" elif order not in "cfCF": raise ValueError( @@ -122,7 +122,7 @@ def _get_empty_array( ) -def _get_strides_for_order_k(x, shape=None): +def _get_strides_for_order_k(x, dtype, shape=None): """ Calculate strides when order='K' for empty_like, ones_like, zeros_like, and full_like where `shape` is ``None`` or len(shape) == x.ndim. @@ -130,7 +130,7 @@ def _get_strides_for_order_k(x, shape=None): """ stride_and_index = sorted([(abs(s), -i) for i, s in enumerate(x.strides)]) strides = [0] * x.ndim - stride = 1 + stride = dpnp.dtype(dtype).itemsize for _, i in stride_and_index: strides[-i] = stride stride *= shape[-i] if shape else x.shape[-i] diff --git a/dpnp/dpnp_iface_indexing.py b/dpnp/dpnp_iface_indexing.py index 6e7ab778299b..56a1665fadec 100644 --- a/dpnp/dpnp_iface_indexing.py +++ b/dpnp/dpnp_iface_indexing.py @@ -731,7 +731,7 @@ def diagonal(a, offset=0, axis1=0, axis2=1): elif 0 < offset < m: out_shape = a_shape[:-2] + (min(n, m - offset),) out_strides = a_straides[:-2] + (st_n + st_m,) - out_offset = st_m * offset + out_offset = st_m // a.itemsize * offset else: out_shape = a_shape[:-2] + (0,) out_strides = a_straides[:-2] + (1,) diff --git a/dpnp/dpnp_utils/dpnp_utils_linearalgebra.py b/dpnp/dpnp_utils/dpnp_utils_linearalgebra.py index 30be5d1ff5cb..191b8aa65d13 100644 --- a/dpnp/dpnp_utils/dpnp_utils_linearalgebra.py +++ b/dpnp/dpnp_utils/dpnp_utils_linearalgebra.py @@ -185,7 +185,7 @@ def _define_contig_flag(x): """ flag = False - x_strides = x.strides + x_strides = dpnp.get_usm_ndarray(x).strides x_shape = x.shape if x.ndim < 2: return True, True, True diff --git a/dpnp/fft/dpnp_utils_fft.py b/dpnp/fft/dpnp_utils_fft.py index 4e2b7aaaf842..709494e6255e 100644 --- a/dpnp/fft/dpnp_utils_fft.py +++ b/dpnp/fft/dpnp_utils_fft.py @@ -193,12 +193,13 @@ def _compute_result(dsc, a, out, forward, c2c, out_strides): ) result = a else: + out_usm = None if out is None else dpnp.get_usm_ndarray(out) if ( out is not None - and out.strides == tuple(out_strides) - and not ti._array_overlap(a_usm, dpnp.get_usm_ndarray(out)) + and out_usm.strides == tuple(out_strides) + and not ti._array_overlap(a_usm, out_usm) ): - res_usm = dpnp.get_usm_ndarray(out) + res_usm = out_usm result = out else: # Result array that is used in oneMKL must have the exact same @@ -223,6 +224,10 @@ def _compute_result(dsc, a, out, forward, c2c, out_strides): if a.dtype == dpnp.complex64 else dpnp.float64 ) + # cast to expected strides format + out_strides = tuple( + el * dpnp.dtype(out_dtype).itemsize for el in out_strides + ) result = dpnp_array( out_shape, dtype=out_dtype, @@ -419,7 +424,8 @@ def _fft(a, norm, out, forward, in_place, c2c, axes, batch_fft=True): if cufft_wa: # pragma: no cover a = dpnp.moveaxis(a, -1, -2) - a_strides = _standardize_strides_to_nonzero(a.strides, a.shape) + strides = dpnp.get_usm_ndarray(a).strides + a_strides = _standardize_strides_to_nonzero(strides, a.shape) dsc, out_strides = _commit_descriptor( a, forward, in_place, c2c, a_strides, index, batch_fft ) diff --git a/dpnp/linalg/dpnp_utils_linalg.py b/dpnp/linalg/dpnp_utils_linalg.py index 196cd2ae9da5..6881c7787e9f 100644 --- a/dpnp/linalg/dpnp_utils_linalg.py +++ b/dpnp/linalg/dpnp_utils_linalg.py @@ -215,7 +215,7 @@ def _batched_inv(a, res_type): _manager.add_event_pair(ht_ev, copy_ev) ipiv_stride = n - a_stride = a_h.strides[0] + a_stride = a_h.strides[0] // a_h.itemsize # Call the LAPACK extension function _getrf_batch # to perform LU decomposition of a batch of general matrices @@ -298,7 +298,7 @@ def _batched_lu_factor(a, res_type): dev_info_h = [0] * batch_size ipiv_stride = n - a_stride = a_h.strides[0] + a_stride = a_h.strides[0] // a_h.itemsize # Call the LAPACK extension function _getrf_batch # to perform LU decomposition of a batch of general matrices @@ -471,8 +471,8 @@ def _batched_qr(a, mode="reduced"): dtype=res_type, ) - a_stride = a_t.strides[0] - tau_stride = tau_h.strides[0] + a_stride = a_t.strides[0] // a_t.itemsize + tau_stride = tau_h.strides[0] // tau_h.itemsize # Call the LAPACK extension function _geqrf_batch to compute # the QR factorization of a general m x n matrix. @@ -535,8 +535,8 @@ def _batched_qr(a, mode="reduced"): ) _manager.add_event_pair(ht_ev, copy_ev) - q_stride = q.strides[0] - tau_stride = tau_h.strides[0] + q_stride = q.strides[0] // q.itemsize + tau_stride = tau_h.strides[0] // tau_h.itemsize # Get LAPACK function (_orgqr_batch for real or _ungqf_batch for complex # data types) for QR factorization @@ -1818,7 +1818,7 @@ def dpnp_cholesky_batch(a, upper_lower, res_type): ) _manager.add_event_pair(ht_ev, copy_ev) - a_stride = a_h.strides[0] + a_stride = a_h.strides[0] // a_h.itemsize # Call the LAPACK extension function _potrf_batch # to computes the Cholesky decomposition of a batch of diff --git a/dpnp/scipy/linalg/_utils.py b/dpnp/scipy/linalg/_utils.py index 282c645d1095..f00db6fdfb92 100644 --- a/dpnp/scipy/linalg/_utils.py +++ b/dpnp/scipy/linalg/_utils.py @@ -37,6 +37,7 @@ """ +# pylint: disable=duplicate-code # pylint: disable=no-name-in-module # pylint: disable=protected-access @@ -144,7 +145,7 @@ def _batched_lu_factor_scipy(a, res_type): # pylint: disable=too-many-locals dev_info_h = [0] * batch_size ipiv_stride = k - a_stride = a_h.strides[-1] + a_stride = a_h.strides[-1] // a_h.itemsize # Call the LAPACK extension function _getrf_batch # to perform LU decomposition of a batch of general matrices diff --git a/dpnp/tests/test_arraycreation.py b/dpnp/tests/test_arraycreation.py index eb20f9b3ffe5..d8a80ddbff78 100644 --- a/dpnp/tests/test_arraycreation.py +++ b/dpnp/tests/test_arraycreation.py @@ -861,12 +861,12 @@ def test_full_order(order1, order2): def test_full_strides(): a = numpy.full((3, 3), numpy.arange(3, dtype="i4")) ia = dpnp.full((3, 3), dpnp.arange(3, dtype="i4")) - assert ia.strides == tuple(el // a.itemsize for el in a.strides) + assert ia.strides == a.strides assert_array_equal(ia, a) a = numpy.full((3, 3), numpy.arange(6, dtype="i4")[::2]) ia = dpnp.full((3, 3), dpnp.arange(6, dtype="i4")[::2]) - assert ia.strides == tuple(el // a.itemsize for el in a.strides) + assert ia.strides == a.strides assert_array_equal(ia, a) diff --git a/dpnp/tests/test_ndarray.py b/dpnp/tests/test_ndarray.py index c58c26fdf977..4e4e42bbc85e 100644 --- a/dpnp/tests/test_ndarray.py +++ b/dpnp/tests/test_ndarray.py @@ -60,10 +60,10 @@ def test_attributes(self): assert_equal(self.three.shape, (10, 3, 2)) self.three.shape = (2, 5, 6) - assert_equal(self.one.strides, (self.one.itemsize / self.one.itemsize,)) - num = self.two.itemsize / self.two.itemsize + assert_equal(self.one.strides, (self.one.itemsize,)) + num = self.two.itemsize assert_equal(self.two.strides, (5 * num, num)) - num = self.three.itemsize / self.three.itemsize + num = self.three.itemsize assert_equal(self.three.strides, (30 * num, 6 * num, num)) assert_equal(self.one.ndim, 1) @@ -290,7 +290,7 @@ def test_flags_strides(dtype, order, strides): (4, 4), dtype=dtype, order=order, strides=strides ) a = numpy.ndarray((4, 4), dtype=dtype, order=order, strides=numpy_strides) - ia = dpnp.ndarray((4, 4), dtype=dtype, order=order, strides=strides) + ia = dpnp.ndarray((4, 4), dtype=dtype, order=order, strides=numpy_strides) assert usm_array.flags == ia.flags assert a.flags.c_contiguous == ia.flags.c_contiguous assert a.flags.f_contiguous == ia.flags.f_contiguous diff --git a/dpnp/tests/third_party/cupy/core_tests/test_elementwise.py b/dpnp/tests/third_party/cupy/core_tests/test_elementwise.py index b2d6e65cd37a..a024dd59d702 100644 --- a/dpnp/tests/third_party/cupy/core_tests/test_elementwise.py +++ b/dpnp/tests/third_party/cupy/core_tests/test_elementwise.py @@ -6,8 +6,6 @@ import dpnp as cupy from dpnp.tests.helper import ( has_support_aspect64, - is_win_platform, - numpy_version, ) from dpnp.tests.third_party.cupy import testing @@ -67,10 +65,10 @@ def test_copy_orders(self, order): a = cupy.empty((2, 3, 4)) b = cupy.copy(a, order) - a_cpu = numpy.empty((2, 3, 4)) + a_cpu = numpy.empty((2, 3, 4), dtype=a.dtype) b_cpu = numpy.copy(a_cpu, order) - assert b.strides == tuple(x / b_cpu.itemsize for x in b_cpu.strides) + assert b.strides == b_cpu.strides @pytest.mark.skip("`ElementwiseKernel` isn't supported") 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 d782eb9f41ec..95d753c90473 100644 --- a/dpnp/tests/third_party/cupy/core_tests/test_ndarray.py +++ b/dpnp/tests/third_party/cupy/core_tests/test_ndarray.py @@ -8,9 +8,19 @@ import pytest from dpctl.tensor._numpy_helper import AxisError +# from cupy_backends.cuda.api import driver +# from cupy_backends.cuda.api import runtime +# from cupy_backends.cuda import stream as stream_module import dpnp as cupy + +# from cupy import _util +# from cupy import _core +# from cupy import cuda +# from cupy import get_array_module from dpnp.tests.third_party.cupy import testing +# from cupy.exceptions import AxisError + def get_array_module(*args): for arg in args: @@ -67,8 +77,8 @@ def test_memptr_with_strides(self): memptr = buf.data # self-overlapping strides - a = cupy.ndarray((2, 3), numpy.float32, memptr, strides=(2, 1)) - assert a.strides == (2, 1) + a = cupy.ndarray((2, 3), numpy.float32, memptr, strides=(8, 4)) + assert a.strides == (8, 4) a[:] = 1 a[0, 2] = 4 @@ -85,23 +95,21 @@ 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.data, strides=(2, 1), order="C" + (2, 3), numpy.float32, buf.data, strides=(8, 4), order="C" ) - assert a.strides == (2, 1) + assert a.strides == (8, 4) @testing.with_requires("numpy>=1.19") def test_strides_is_given_but_order_is_invalid(self): for xp in (numpy, cupy): with pytest.raises(ValueError): - xp.ndarray((2, 3), numpy.float32, strides=(2, 1), order="!") + xp.ndarray((2, 3), numpy.float32, strides=(8, 4), order="!") def test_order(self): shape = (2, 3, 4) a = cupy.ndarray(shape, order="F") a_cpu = numpy.ndarray(shape, order="F", dtype=a.dtype) - assert all( - i * a.itemsize == j for i, j in zip(a.strides, a_cpu.strides) - ) + assert a.strides == a_cpu.strides assert a.flags.f_contiguous assert not a.flags.c_contiguous @@ -111,9 +119,7 @@ def test_order_none(self): a_cpu = numpy.ndarray(shape, order=None, dtype=a.dtype) assert a.flags.c_contiguous == a_cpu.flags.c_contiguous assert a.flags.f_contiguous == a_cpu.flags.f_contiguous - assert all( - i * a.itemsize == j for i, j in zip(a.strides, a_cpu.strides) - ) + assert a.strides == a_cpu.strides def test_slots(self): # Test for #7883. @@ -147,10 +153,7 @@ class TestNdarrayInitStrides(unittest.TestCase): @testing.numpy_cupy_equal() def test_strides(self, xp): arr = xp.ndarray(self.shape, dtype=self.dtype, order=self.order) - strides = arr.strides - if xp is cupy: - strides = tuple(i * arr.itemsize for i in strides) - return (strides, arr.flags.c_contiguous, arr.flags.f_contiguous) + return (arr.strides, arr.flags.c_contiguous, arr.flags.f_contiguous) class TestNdarrayInitRaise(unittest.TestCase): diff --git a/dpnp/tests/third_party/cupy/core_tests/test_ndarray_copy_and_view.py b/dpnp/tests/third_party/cupy/core_tests/test_ndarray_copy_and_view.py index 61980b6eda9b..7b503f1997a5 100644 --- a/dpnp/tests/third_party/cupy/core_tests/test_ndarray_copy_and_view.py +++ b/dpnp/tests/third_party/cupy/core_tests/test_ndarray_copy_and_view.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import numpy import pytest @@ -8,6 +10,7 @@ from numpy.exceptions import ComplexWarning else: from numpy import ComplexWarning +# from cupy import _util def astype_without_warning(x, dtype, *args, **kwargs): @@ -19,12 +22,6 @@ def astype_without_warning(x, dtype, *args, **kwargs): return x.astype(dtype, *args, **kwargs) -def get_strides(xp, a): - if xp is numpy: - return tuple(el // a.itemsize for el in a.strides) - return a.strides - - class TestView: @testing.numpy_cupy_array_equal() @@ -189,6 +186,9 @@ def test_view_larger_dtype_zero_sized(self, xp): class TestArrayCopy: + # @pytest.mark.skipif( + # not _util.ENABLE_SLICE_COPY, reason="Special copy disabled" + # ) @testing.for_orders("CF") @testing.for_dtypes( [numpy.int16, numpy.int64, numpy.float16, numpy.float64] @@ -200,13 +200,19 @@ def test_isinstance_numpy_copy(self, xp, dtype, order): b[:] = a return b + # @pytest.mark.skipif( + # not _util.ENABLE_SLICE_COPY, reason="Special copy disabled" + # ) @pytest.mark.skip("copy from host to device is allowed") def test_isinstance_numpy_copy_wrong_dtype(self): - a = numpy.arange(100, dtype=cupy.default_float_type()).reshape(10, 10) + a = numpy.arange(100, dtype=numpy.float64).reshape(10, 10) b = cupy.empty(a.shape, dtype=numpy.int32) with pytest.raises(ValueError): b[:] = a + # @pytest.mark.skipif( + # not _util.ENABLE_SLICE_COPY, reason="Special copy disabled" + # ) def test_isinstance_numpy_copy_wrong_shape(self): for xp in (numpy, cupy): a = numpy.arange(100, dtype=cupy.default_float_type()).reshape( @@ -216,12 +222,18 @@ def test_isinstance_numpy_copy_wrong_shape(self): with pytest.raises(ValueError): b[:] = a + # @pytest.mark.skipif( + # not _util.ENABLE_SLICE_COPY, reason="Special copy disabled" + # ) @testing.numpy_cupy_array_equal() def test_isinstance_numpy_copy_not_slice(self, xp): a = xp.arange(5, dtype=cupy.default_float_type()) a[a < 3] = 0 return a + # @pytest.mark.skipif( + # not _util.ENABLE_SLICE_COPY, reason="Special copy disabled" + # ) @pytest.mark.skip("copy from host to device is allowed") def test_copy_host_to_device_view(self): dev = cupy.empty((10, 10), dtype=numpy.float32)[2:5, 1:8] @@ -358,24 +370,24 @@ def test_astype_type_f_contiguous_no_copy(self, dtype, order): @testing.numpy_cupy_equal() def test_astype_strides(self, xp, src_dtype, dst_dtype): src = testing.shaped_arange((1, 2, 3), xp, dtype=src_dtype) - dst = astype_without_warning(src, dst_dtype, order="K") - return get_strides(xp, dst) + return astype_without_warning(src, dst_dtype, order="K").strides @testing.for_all_dtypes_combination(("src_dtype", "dst_dtype")) @testing.numpy_cupy_equal() def test_astype_strides_negative(self, xp, src_dtype, dst_dtype): src = testing.shaped_arange((2, 3), xp, dtype=src_dtype) src = src[::-1, :] - dst = astype_without_warning(src, dst_dtype, order="K") - return tuple(abs(x) for x in get_strides(xp, dst)) + return tuple( + abs(el) + for el in astype_without_warning(src, dst_dtype, order="K").strides + ) @testing.for_all_dtypes_combination(("src_dtype", "dst_dtype")) @testing.numpy_cupy_equal() def test_astype_strides_swapped(self, xp, src_dtype, dst_dtype): src = testing.shaped_arange((2, 3, 4), xp, dtype=src_dtype) src = xp.swapaxes(src, 1, 0) - dst = astype_without_warning(src, dst_dtype, order="K") - return get_strides(xp, dst) + return astype_without_warning(src, dst_dtype, order="K").strides @testing.for_all_dtypes_combination(("src_dtype", "dst_dtype")) @testing.numpy_cupy_equal() @@ -383,8 +395,7 @@ def test_astype_strides_broadcast(self, xp, src_dtype, dst_dtype): src1 = testing.shaped_arange((2, 3, 2), xp, dtype=src_dtype) src2 = testing.shaped_arange((2,), xp, dtype=src_dtype) src, _ = xp.broadcast_arrays(src1, src2) - dst = astype_without_warning(src, dst_dtype, order="K") - return get_strides(xp, dst) + return astype_without_warning(src, dst_dtype, order="K").strides @testing.numpy_cupy_array_equal() def test_astype_boolean_view(self, xp): @@ -413,6 +424,9 @@ def test_diagonal2(self, xp, dtype): {"src_order": "F"}, ) class TestNumPyArrayCopyView: + # @pytest.mark.skipif( + # not _util.ENABLE_SLICE_COPY, reason="Special copy disabled" + # ) @testing.for_orders("CF") @testing.for_dtypes( [numpy.int16, numpy.int64, numpy.float16, numpy.float64] diff --git a/dpnp/tests/third_party/cupy/creation_tests/test_basic.py b/dpnp/tests/third_party/cupy/creation_tests/test_basic.py index 8265671ab350..a9e382d22798 100644 --- a/dpnp/tests/third_party/cupy/creation_tests/test_basic.py +++ b/dpnp/tests/third_party/cupy/creation_tests/test_basic.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import warnings import numpy @@ -152,10 +154,7 @@ def test_empty_like_K_strides(self, dtype): bg.fill(0) # make sure NumPy and CuPy strides agree - scaled_numpy_strides = b.strides - scale = b.itemsize - numpy_strides = tuple(i / scale for i in scaled_numpy_strides) - assert numpy_strides == bg.strides + assert b.strides == bg.strides return @testing.with_requires("numpy>=1.19") @@ -171,7 +170,7 @@ def test_empty_like_subok(self): with pytest.raises(NotImplementedError): cupy.empty_like(a, subok=True) - @pytest.mark.skip("strides for zero sized array is different") + @pytest.mark.skip("strides for zero sized array are different") @testing.for_CF_orders() @testing.with_requires("numpy>=1.23") def test_empty_zero_sized_array_strides(self, order): @@ -221,8 +220,7 @@ def test_zeros_int(self, xp, dtype, order): def test_zeros_strides(self, order): a = numpy.zeros((2, 3), dtype=cupy.default_float_type(), order=order) b = cupy.zeros((2, 3), dtype=cupy.default_float_type(), order=order) - b_strides = tuple(x * b.itemsize for x in b.strides) - assert b_strides == a.strides + assert b.strides == a.strides @testing.for_orders("CFAK") @testing.for_all_dtypes() @@ -236,6 +234,15 @@ def test_zeros_like_subok(self): with pytest.raises(NotImplementedError): cupy.zeros_like(a, subok=True) + @pytest.mark.skip("only native byteorder is supported") + def test_reject_byteswap(self): + # Reject creation of arrays with bad byte-order at a low level + with pytest.raises(ValueError, match=".*byte-order"): + cupy.ndarray((2, 3, 4), dtype=">i") + + with pytest.raises(ValueError, match=".*byte-order"): + cupy.zeros((2, 3, 4), dtype=">i") + @testing.for_CF_orders() @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() @@ -254,6 +261,33 @@ def test_ones_like_subok(self): with pytest.raises(NotImplementedError): cupy.ones_like(a, subok=True) + @pytest.mark.parametrize( + "shape, strides", + [ + ((2, 3, 4), (8 * 3 * 4, 8 * 4, 8)), # contiguous + ((2, 3, 4), (8, 0, 8)), # smaller than contiguous needed + ((2, 0, 4), (8, 128, 1024)), # empty can be OK + ], + ) + def test_ndarray_strides(self, shape, strides): + a = cupy.ndarray( + shape, strides=strides, dtype=cupy.default_float_type() + ) + assert cupy.byte_bounds(a)[0] == a.data.ptr + assert cupy.byte_bounds(a)[1] - a.data.ptr <= a.data.size + + @pytest.mark.skip("due to dpctl-2239") + @pytest.mark.parametrize( + "shape, strides", + [ + ((2, 3, 4), (8, 128, 1024)), # too large + ((2, 3, 4), (-8, 8, 8)), # negative (needs offset) + ], + ) + def test_ndarray_strides_raises(self, shape, strides): + with pytest.raises(ValueError, match=r"ndarray\(\) with strides.*"): + cupy.ndarray(shape, strides=strides) + @testing.for_CF_orders() @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() @@ -457,10 +491,7 @@ def test_empty_like_K_strides_reshape(self, dtype): bg.fill(0) # make sure NumPy and CuPy strides agree - scaled_numpy_strides = b.strides - scale = b.itemsize - numpy_strides = tuple(i / scale for i in scaled_numpy_strides) - assert numpy_strides == bg.strides + assert b.strides == bg.strides return @testing.with_requires("numpy>=1.17.0") 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 47505c6c00df..a2496c855b02 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 @@ -131,7 +131,7 @@ def test_array_from_nested_list_of_numpy( @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(), strides_check=True + type_check=has_support_aspect64(), strides_check=has_support_aspect64() ) def test_array_from_list_of_cupy( self, xp, dtype1, dtype2, src_order, dst_order diff --git a/dpnp/tests/third_party/cupy/manipulation_tests/test_kind.py b/dpnp/tests/third_party/cupy/manipulation_tests/test_kind.py index b327b91a5616..e0bc9c7eb49e 100644 --- a/dpnp/tests/third_party/cupy/manipulation_tests/test_kind.py +++ b/dpnp/tests/third_party/cupy/manipulation_tests/test_kind.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import unittest import numpy @@ -35,8 +37,6 @@ def func(xp): ret = xp.asfortranarray(x) assert x.flags.c_contiguous assert ret.flags.f_contiguous - if xp is cupy: - return tuple(el * ret.itemsize for el in ret.strides) return ret.strides assert func(numpy) == func(cupy) @@ -48,8 +48,6 @@ def func(xp): ret = xp.asfortranarray(x) assert x.flags.c_contiguous assert ret.flags.f_contiguous - if xp is cupy: - return tuple(el * ret.itemsize for el in ret.strides) return ret.strides assert func(numpy) == func(cupy) @@ -61,8 +59,6 @@ def func(xp): ret = xp.asfortranarray(xp.asfortranarray(x)) assert x.flags.c_contiguous assert ret.flags.f_contiguous - if xp is cupy: - return tuple(el * ret.itemsize for el in ret.strides) return ret.strides assert func(numpy) == func(cupy) @@ -74,8 +70,6 @@ def func(xp): x = xp.transpose(x, (1, 0)) ret = xp.asfortranarray(x) assert ret.flags.f_contiguous - if xp is cupy: - return tuple(el * ret.itemsize for el in ret.strides) return ret.strides assert func(numpy) == func(cupy) @@ -87,8 +81,6 @@ def func(xp): ret = xp.asfortranarray(x) assert x.flags.c_contiguous assert ret.flags.f_contiguous - if xp is cupy: - return tuple(el * ret.itemsize for el in ret.strides) return ret.strides assert func(numpy) == func(cupy) @@ -106,7 +98,7 @@ def test_require_flag_check(self, dtype): @pytest.mark.skip("dpnp.require() does not support requirement ['O']") @testing.for_all_dtypes() def test_require_owndata(self, dtype): - x = cupy.zeros((2, 3, 4), dtype=dtype) + x = cupy.zeros((2, 3, 4), dtype) arr = x.view() arr = cupy.require(arr, dtype, ["O"]) assert arr.flags["OWNDATA"] 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 bec0215d4b64..0520fbbc0ff9 100644 --- a/dpnp/tests/third_party/cupy/manipulation_tests/test_shape.py +++ b/dpnp/tests/third_party/cupy/manipulation_tests/test_shape.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import numpy import pytest @@ -25,8 +27,6 @@ class TestReshape: def test_reshape_strides(self): def func(xp): a = testing.shaped_arange((1, 1, 1, 2, 2), xp) - if xp is cupy: - return tuple(el * a.itemsize for el in a.strides) return a.strides assert func(numpy) == func(cupy) @@ -259,5 +259,7 @@ def test_reshape_contiguity(self, order_init, order_reshape, shape_in_out): assert b_cupy.flags.f_contiguous == b_numpy.flags.f_contiguous assert b_cupy.flags.c_contiguous == b_numpy.flags.c_contiguous - # testing.assert_array_equal(b_cupy.strides, b_numpy.strides) + if shape_final != (1, 6, 1): + # strides mismatching is allowed due to multiple representation + testing.assert_array_equal(b_cupy.strides, b_numpy.strides) testing.assert_array_equal(b_cupy, b_numpy) diff --git a/dpnp/tests/third_party/cupy/testing/_array.py b/dpnp/tests/third_party/cupy/testing/_array.py index 552dc19f456f..8c88cacb2ff7 100644 --- a/dpnp/tests/third_party/cupy/testing/_array.py +++ b/dpnp/tests/third_party/cupy/testing/_array.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import warnings import numpy @@ -171,18 +173,13 @@ def assert_array_equal( ) if strides_check: - strides = desired.strides - if isinstance(actual, cupy.ndarray): - # need to agreed the strides with numpy.ndarray - strides = tuple(el // desired.itemsize for el in desired.strides) - - if actual.strides != strides: + if actual.strides != desired.strides: msg = ["Strides are not equal:"] if err_msg: msg = [msg[0] + " " + err_msg] if verbose: msg.append(" x: {}".format(actual.strides)) - msg.append(" y: {}".format(strides)) + msg.append(" y: {}".format(desired.strides)) raise AssertionError("\n".join(msg))