diff --git a/dpnp/tests/third_party/cupy/core_tests/test_ndarray_indexing.py b/dpnp/tests/third_party/cupy/core_tests/test_ndarray_indexing.py index 059345092b69..5814b53a6d6a 100644 --- a/dpnp/tests/third_party/cupy/core_tests/test_ndarray_indexing.py +++ b/dpnp/tests/third_party/cupy/core_tests/test_ndarray_indexing.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import unittest import warnings @@ -6,6 +8,10 @@ import dpnp as cupy from dpnp.tests.third_party.cupy import testing +from dpnp.tests.third_party.cupy.testing._protocol_helpers import ( + DummyObjectWithCudaArrayInterface, + DummyObjectWithCuPyGetNDArray, +) if numpy.lib.NumpyVersion(numpy.__version__) >= "2.0.0b1": from numpy.exceptions import ComplexWarning @@ -130,9 +136,30 @@ ) class TestArrayIndexingParameterized(unittest.TestCase): + _getitem_hip_skip_condition = [ + ((1, 0, 2), (2, 3, 4), None), + ((-1, 0, -2), (2, 3, 4), None), + ((1, 0, 2), (2, 3, 4), (2, 0, 1)), + ((-1, 0, -2), (2, 3, 4), (2, 0, 1)), + ((slice(None, None, None), None), (2,), None), + ((slice(-9, -10, -1),), (10,), None), + ((slice(-4, -5, -1),), (10,), None), + ((slice(-5, -6, -1),), (10,), None), + ] + + def _check_getitem_hip_skip_condition(self): + return ( + self.indexes, + self.shape, + self.transpose, + ) in self._getitem_hip_skip_condition + @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() def test_getitem(self, xp, dtype): + # if cupy.cuda.runtime.is_hip: + # if self._check_getitem_hip_skip_condition(): + # pytest.xfail("HIP may have a bug") a = testing.shaped_arange(self.shape, xp, dtype) if self.transpose: a = a.transpose(self.transpose) @@ -261,3 +288,18 @@ def test_remain0d(self, xp): a = xp.zeros((2, 3, 4), dtype=dtype) a[0, 1, 2] = testing.shaped_arange((), xp, dtype) return a + + +@pytest.mark.skip("CUDA array interface is not supported") +@pytest.mark.parametrize( + "cupy_like", + [ + DummyObjectWithCuPyGetNDArray, + DummyObjectWithCudaArrayInterface, + ], +) +def test_setitem_with_cupy_like(cupy_like): + # Test that normal assignment supports interfaces + a = cupy.zeros(10) + a[...] = cupy_like(cupy.arange(10)) + testing.assert_array_equal(a, cupy.arange(10)) diff --git a/dpnp/tests/third_party/cupy/core_tests/test_reduction.py b/dpnp/tests/third_party/cupy/core_tests/test_reduction.py index 0b97bc04ad06..660471a3e189 100644 --- a/dpnp/tests/third_party/cupy/core_tests/test_reduction.py +++ b/dpnp/tests/third_party/cupy/core_tests/test_reduction.py @@ -1,19 +1,22 @@ +from __future__ import annotations + import unittest import numpy import pytest -from dpctl.tensor._numpy_helper import AxisError import dpnp as cupy # import cupy._core._accelerator as _acc # from cupy import _core from dpnp.tests.third_party.cupy import testing +from dpnp.tests.third_party.cupy.testing._protocol_helpers import ( + DummyObjectWithCudaArrayInterface, + DummyObjectWithCuPyGetNDArray, +) + +# from cupy.exceptions import ComplexWarning, AxisError -if numpy.lib.NumpyVersion(numpy.__version__) >= "2.0.0b1": - from numpy.exceptions import ComplexWarning -else: - from numpy import ComplexWarning pytest.skip( "create/get_reduction_func() and ReductionKernel are not supported", @@ -258,3 +261,26 @@ def test_large_dims_keep_kernels(self): shape = (4, 3, 2, 4, 3, 2, 2) axis = (1, 4, 3, 6) self.check_int8_sum(shape, axis=axis, keepdims=True) + + +class TestArgumentTypes: + kernel = _core.create_reduction_func( + "my_sum", ("f->f",), ("in0", "a + b", "out0 = a", None), 0 + ) + + @pytest.mark.parametrize( + "cupy_like", + [ + DummyObjectWithCuPyGetNDArray, + DummyObjectWithCudaArrayInterface, + ], + ) + def test_cupy_like_protocols(self, cupy_like): + # Check that reduction kernels work on the cupy like protocols + x = cupy_like(cupy.arange(10, dtype=cupy.float32)) + res = self.kernel(x) + assert res == 45 + + def test_bad_argument(self): + with pytest.raises(TypeError, match="Argument 'a' has incorrect type"): + self.kernel(numpy.array([1, 2, 3])) 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 d3e42cc66c33..47505c6c00df 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 @@ -1,3 +1,6 @@ +from __future__ import annotations + +import os import tempfile import unittest @@ -8,6 +11,10 @@ import dpnp as cupy from dpnp.tests.helper import has_support_aspect64, is_cuda_device from dpnp.tests.third_party.cupy import testing +from dpnp.tests.third_party.cupy.testing._protocol_helpers import ( + DummyObjectWithCudaArrayInterface, + DummyObjectWithCuPyGetNDArray, +) class TestFromData(unittest.TestCase): @@ -37,6 +44,11 @@ def test_array_from_numpy(self, xp, dtype, order): a = testing.shaped_arange((2, 3, 4), numpy, dtype) return xp.array(a, order=order) + @pytest.mark.skip("no blocking keyword") + def test_array_from_numpy_blocking(self): + a = testing.shaped_arange((2, 3, 4), numpy, numpy.float32) + testing.assert_array_equal(cupy.array(a, blocking=True), a) + @testing.for_orders("CFAK") @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() @@ -242,7 +254,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 ): @@ -258,7 +270,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 ): @@ -274,7 +286,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 ): @@ -290,7 +302,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 ): @@ -401,6 +413,11 @@ def test_asarray(self, xp, dtype): a = testing.shaped_arange((2, 3, 4), xp, dtype) return xp.asarray(a) + @pytest.mark.skip("no blocking keyword") + def test_asarray_blocking(self): + a = testing.shaped_arange((2, 3, 4), numpy, numpy.float32) + testing.assert_array_equal(cupy.asarray(a, blocking=True), a) + @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() def test_asarray_is_not_copied(self, xp, dtype): @@ -454,6 +471,11 @@ def test_asanyarray_from_big_endian(self, xp, dtype): # happens to work before the change in #5828 return b + b + @pytest.mark.skip("no blocking keyword") + def test_asanyarray_blocking(self): + a = testing.shaped_arange((2, 3, 4), numpy, numpy.float32) + testing.assert_array_equal(cupy.asanyarray(a, blocking=True), a) + @testing.for_CF_orders() @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() @@ -727,67 +749,6 @@ def test_with_over_size_array(self): testing.assert_array_equal(a, b) -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 - if isinstance(a, cupy.ndarray): - self.a = a - else: - self.shape, self.strides, self.typestr, self.descr, self.data = a - self.ver = ver - self.include_strides = include_strides - self.mask = mask - self.stream = stream - - @property - def __cuda_array_interface__(self): - if self.a is not None: - desc = { - "shape": self.a.shape, - "typestr": self.a.dtype.str, - "descr": self.a.dtype.descr, - "data": (self.a.data.ptr, False), - "version": self.ver, - } - if self.a.flags.c_contiguous: - if self.include_strides is True: - desc["strides"] = self.a.strides - elif self.include_strides is None: - desc["strides"] = None - else: # self.include_strides is False - pass - else: # F contiguous or neither - desc["strides"] = self.a.strides - else: - desc = { - "shape": self.shape, - "typestr": self.typestr, - "descr": self.descr, - "data": (self.data, False), - "version": self.ver, - } - if self.include_strides is True: - desc["strides"] = self.strides - elif self.include_strides is None: - desc["strides"] = None - else: # self.include_strides is False - pass - if self.mask is not None: - desc["mask"] = self.mask - # The stream field is kept here for compliance. However, since the - # synchronization is done via calling a cpdef function, which cannot - # be mock-tested. - if self.stream is not None: - if self.stream is cuda.Stream.null: - desc["stream"] = cuda.runtime.streamLegacy - elif (not cuda.runtime.is_hip) and self.stream is cuda.Stream.ptds: - desc["stream"] = cuda.runtime.streamPerThread - else: - desc["stream"] = self.stream.ptr - return desc - - @testing.parameterize( *testing.product( { @@ -848,3 +809,12 @@ def test_invalid_type(self): a = numpy.array([1, 2, 3], dtype=object) with self.assertRaises(TypeError): cupy.array(a) + + +@pytest.mark.skip("CUDA array interface is not supported") +class TestArrayCuPyGetNDArray(unittest.TestCase): + def test_cupy_get_ndarray(self): + a = cupy.array([1, 2, 3]) + dummy = DummyObjectWithCuPyGetNDArray(a) + res = cupy.asarray(dummy) + assert a is res # OK if it was a view diff --git a/dpnp/tests/third_party/cupy/testing/_array.py b/dpnp/tests/third_party/cupy/testing/_array.py index f2f8d455dd8e..552dc19f456f 100644 --- a/dpnp/tests/third_party/cupy/testing/_array.py +++ b/dpnp/tests/third_party/cupy/testing/_array.py @@ -171,7 +171,11 @@ def assert_array_equal( ) if strides_check: - strides = tuple(el // desired.itemsize for el in desired.strides) + 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: msg = ["Strides are not equal:"] if err_msg: diff --git a/dpnp/tests/third_party/cupy/testing/_protocol_helpers.py b/dpnp/tests/third_party/cupy/testing/_protocol_helpers.py new file mode 100644 index 000000000000..8624f561c055 --- /dev/null +++ b/dpnp/tests/third_party/cupy/testing/_protocol_helpers.py @@ -0,0 +1,79 @@ +from __future__ import annotations + +import dpnp as cupy + +# from cupy import cuda + + +max_cuda_array_interface_version = 3 + + +class DummyObjectWithCudaArrayInterface: + # Private helper used for testing cuda array interface support. + def __init__(self, a, ver=3, include_strides=False, mask=None, stream=None): + assert ver in tuple(range(max_cuda_array_interface_version + 1)) + self.a = None + if isinstance(a, cupy.ndarray): + self.a = a + else: + self.shape, self.strides, self.typestr, self.descr, self.data = a + self.ver = ver + self.include_strides = include_strides + self.mask = mask + self.stream = stream + + @property + def __cuda_array_interface__(self): + if self.a is not None: + desc = { + "shape": self.a.shape, + "typestr": self.a.dtype.str, + "descr": self.a.dtype.descr, + "data": (self.a.data.ptr, False), + "version": self.ver, + } + if self.a.flags.c_contiguous: + if self.include_strides is True: + desc["strides"] = self.a.strides + elif self.include_strides is None: + desc["strides"] = None + else: # self.include_strides is False + pass + else: # F contiguous or neither + desc["strides"] = self.a.strides + else: + desc = { + "shape": self.shape, + "typestr": self.typestr, + "descr": self.descr, + "data": (self.data, False), + "version": self.ver, + } + if self.include_strides is True: + desc["strides"] = self.strides + elif self.include_strides is None: + desc["strides"] = None + else: # self.include_strides is False + pass + if self.mask is not None: + desc["mask"] = self.mask + # The stream field is kept here for compliance. However, since the + # synchronization is done via calling a cpdef function, which cannot + # be mock-tested. + if self.stream is not None: + if self.stream is cuda.Stream.null: + desc["stream"] = cuda.runtime.streamLegacy + elif (not cuda.runtime.is_hip) and self.stream is cuda.Stream.ptds: + desc["stream"] = cuda.runtime.streamPerThread + else: + desc["stream"] = self.stream.ptr + return desc + + +class DummyObjectWithCuPyGetNDArray: + # Private helper used for testing `__cupy_get_ndarray__` support. + def __init__(self, a): + self.a = a + + def __cupy_get_ndarray__(self): + return self.a