Skip to content
Draft
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
26 changes: 20 additions & 6 deletions dpnp/tests/third_party/cupy/core_tests/test_cub_reduction.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,22 @@ def _test_can_use(self, i_shape, o_shape, r_axis, o_axis, order, expected):
assert result is expected


@pytest.mark.parametrize("shape", [(2,), (2, 3), (2, 3, 4), (2, 3, 4, 5)])
@pytest.mark.parametrize("order", ["C", "F"])
_MIN_SIZE = cupy._core._cub_reduction._CUB_REDUCE_SIZE_THRESHOLD


@pytest.mark.parametrize(
"shape",
[
(_MIN_SIZE,),
(_MIN_SIZE, _MIN_SIZE + 1),
(_MIN_SIZE, 3, _MIN_SIZE + 1),
(_MIN_SIZE, 3, 4, _MIN_SIZE + 1),
],
)
@pytest.mark.parametrize(
"order",
["C", "F"],
)
class TestSimpleCubReductionKernelContiguity(CubReductionTestBase):

@testing.for_contiguous_axes()
Expand Down Expand Up @@ -139,15 +153,15 @@ def test_can_use_cub_oversize_input4(self):
b = cupy.empty((), dtype=cupy.int8)
assert self.can_use([a], [b], (1,), (0,)) is None

# thread_unsafe marker requires pytest-run-parallel, not used by dpnp
# @pytest.mark.thread_unsafe(
# reason="AssertFunctionIsCalled and accelerate mutation.")
@pytest.mark.thread_unsafe(
reason="AssertFunctionIsCalled and accelerate mutation."
)
def test_can_use_accelerator_set_unset(self):
# ensure we use CUB block reduction and not CUB device reduction
old_routine_accelerators = _accelerator.get_routine_accelerators()
_accelerator.set_routine_accelerators([])

a = cupy.random.random((10, 10))
a = cupy.random.random((10, _cub_reduction._CUB_REDUCE_SIZE_THRESHOLD))
# this is the only function we can mock; the rest is cdef'd
func_name = "".join(
(
Expand Down
85 changes: 85 additions & 0 deletions dpnp/tests/third_party/cupy/core_tests/test_dlpack.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

import ctypes

import dpctl
import numpy
import pytest
Expand All @@ -8,6 +10,12 @@
import dpnp.tensor._dlpack as dlp
from dpnp.tests.third_party.cupy import testing

ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p
ctypes.pythonapi.PyCapsule_GetPointer.argtypes = [
ctypes.py_object,
ctypes.c_char_p,
]


# TODO: to roll back the changes once the issue with CUDA support is resolved for random
def _gen_array(dtype, alloc_q=None):
Expand All @@ -29,6 +37,32 @@ def _gen_array(dtype, alloc_q=None):
return cupy.asarray(array, sycl_queue=alloc_q).astype(dtype)


def _inspect_dlpack(capsule):
ptr = ctypes.pythonapi.PyCapsule_GetPointer(capsule, b"dltensor")
managed = ctypes.cast(ptr, ctypes.POINTER(DLManagedTensor)).contents
tensor = managed.dl_tensor

shape = [tensor.shape[i] for i in range(tensor.ndim)]
strides = None
if tensor.strides:
strides = [tensor.strides[i] for i in range(tensor.ndim)]

return {
"data": tensor.data,
"device_type": tensor.device.device_type,
"device_id": tensor.device.device_id,
"ndim": tensor.ndim,
"dtype": {
"code": tensor.dtype.code,
"bits": tensor.dtype.bits,
"lanes": tensor.dtype.lanes,
},
"shape": shape,
"strides": strides,
"byte_offset": tensor.byte_offset,
}


class DLDummy:
"""Dummy object to wrap a __dlpack__ capsule, so we can use from_dlpack."""

Expand All @@ -43,6 +77,41 @@ def __dlpack_device__(self):
return self.device


class DLDevice(ctypes.Structure):
_fields_ = [
("device_type", ctypes.c_int32),
("device_id", ctypes.c_int32),
]


class DLDataType(ctypes.Structure):
_fields_ = [
("code", ctypes.c_uint8),
("bits", ctypes.c_uint8),
("lanes", ctypes.c_uint16),
]


class DLTensor(ctypes.Structure):
_fields_ = [
("data", ctypes.c_void_p),
("device", DLDevice),
("ndim", ctypes.c_int32),
("dtype", DLDataType),
("shape", ctypes.POINTER(ctypes.c_int64)),
("strides", ctypes.POINTER(ctypes.c_int64)),
("byte_offset", ctypes.c_uint64),
]


class DLManagedTensor(ctypes.Structure):
_fields_ = [
("dl_tensor", DLTensor),
("manager_ctx", ctypes.c_void_p),
("deleter", ctypes.c_void_p),
]


@pytest.mark.skip("toDlpack() and fromDlpack() are not supported")
class TestDLPackConversion:

Expand Down Expand Up @@ -295,3 +364,19 @@ def test_multiple_consumption_error(self, recwarn):
assert "consumed multiple times" in str(e.value)
for w in recwarn:
assert issubclass(w.category, cupy.VisibleDeprecationWarning)


class TestDLTensorContent:
@pytest.fixture(scope="class")
@classmethod
def configure(cls):
arr = _gen_array("uint32")
arr_flip = cupy.transpose(cupy.flip(arr, axis=1), (1, 0))
info = _inspect_dlpack(arr_flip.__dlpack__())
yield arr_flip, info

def test_strides(self, configure):
arr, dlpack_interface = configure
array_strides = tuple(s // arr.itemsize for s in arr.strides)
dlpack_strides = tuple(dlpack_interface["strides"])
assert array_strides == dlpack_strides
110 changes: 101 additions & 9 deletions dpnp/tests/third_party/cupy/core_tests/test_ndarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,56 @@ class UserNdarray(cupy.ndarray):
b.custom_attr = 100


@testing.parameterize(
*testing.product(
{
"np_order": ["C", "F"],
"np_pinned": [False], # no pinned memory
"pinned_alloc_fails": [False], # no pinned memory
"cp_setup": [
("C", False, (48, 16, 4)),
("C", True, (16, 4)),
("F", False, (4, 8, 24)),
("F", True, (4, 8)),
],
}
)
)
class TestAsarray(unittest.TestCase):
# @pytest.mark.thread_unsafe(reason="mutates global pinned allocator.")
def test_asarray(self):
cp_order, view, strides = self.cp_setup
shape = (2, 3, 4)
if self.np_pinned:
count = numpy.prod(shape)
dtype = numpy.float32()
pinned_ptr = cupy.cuda.alloc_pinned_memory(count * dtype.itemsize)
a_cpu = numpy.frombuffer(
pinned_ptr, dtype=dtype, count=count
).reshape(shape)
else:
a_cpu = numpy.ndarray(
shape, dtype=numpy.float32, order=self.np_order
)
a_cpu[...] = numpy.arange(a_cpu.size).reshape(a_cpu.shape)
if view:
a_cpu = a_cpu[:, 1, :]
try:
if self.pinned_alloc_fails:
cupy.cuda.set_pinned_memory_allocator(lambda _: None)
a = cupy.asarray(a_cpu, order=cp_order)
finally:
# None means "no pool", not "the default pool"
# cupy.cuda.set_pinned_memory_allocator(
# cupy.get_default_pinned_memory_pool().malloc
# )
pass
assert a.flags.c_contiguous == (cp_order == "C")
assert a.flags.f_contiguous == (cp_order == "F")
assert a.strides == strides
testing.assert_array_equal(a_cpu, a)


@testing.parameterize(
*testing.product(
{
Expand Down Expand Up @@ -249,9 +299,11 @@ def test_copy_multi_device_non_contiguous_K(self):
@testing.multi_gpu(2)
# @pytest.mark.xfail(
# runtime.is_hip,
# reason='ROCm may work differently in async D2D copy with streams')
# reason="ROCm may work differently in async D2D copy with streams",
# )
# @pytest.mark.thread_unsafe(
# reason="order is unclear multithread. Also, hard crash in threaded!")
# reason="order is unclear multithread. Also, hard crash in threaded!"
# )
def test_copy_multi_device_with_stream(self):
# Kernel that takes long enough then finally writes values.
src = _test_copy_multi_device_with_stream_src
Expand All @@ -278,14 +330,16 @@ def test_copy_multi_device_with_stream(self):
)


@pytest.mark.filterwarnings(
# Shape setting is deprecated starting NumPy 2.5
"ignore::DeprecationWarning"
)
class TestNdarrayShape(unittest.TestCase):

@testing.with_requires("numpy>=2.5")
@testing.numpy_cupy_array_equal()
def test_shape_set(self, xp):
arr = xp.ndarray((2, 3))
with testing.assert_warns(DeprecationWarning):
arr.shape = (3, 2)
arr.shape = (3, 2)
return xp.array(arr.shape)

@pytest.mark.skip(
Expand All @@ -298,15 +352,12 @@ def test_shape_set_infer(self, xp):
arr.shape = (3, -1)
return xp.array(arr.shape)

@testing.with_requires("numpy>=2.5")
@testing.numpy_cupy_array_equal()
def test_shape_set_int(self, xp):
arr = xp.ndarray((2, 3))
with testing.assert_warns(DeprecationWarning):
arr.shape = 6
arr.shape = 6
return xp.array(arr.shape)

@pytest.mark.filterwarnings("ignore::DeprecationWarning")
def test_shape_need_copy(self):
# from cupy/cupy#5470
for xp in (numpy, cupy):
Expand Down Expand Up @@ -563,6 +614,47 @@ def test_shape_mismatch(self):
wrap_take(a, i, out=o)


@testing.parameterize(
{"shape": (3, 4, 5), "indices": (2, 3), "out_shape": (2, 3)},
{"shape": (), "indices": (), "out_shape": ()},
)
class TestNdarrayTakeTypeMismatch(unittest.TestCase):
# NOTE(seberg): Historically cupy was always fully restrictive
# while NumPy was just wrong: https://github.com/numpy/numpy/pull/30615
# As of CuPy 14.2, CuPy uses 2.5+ (future) behavior.

@testing.with_requires("numpy>=2.5")
@testing.numpy_cupy_array_equal()
# incorrectly given by numpy (presumably until Deprecation is finalized)
@pytest.mark.filterwarnings("ignore::numpy.exceptions.ComplexWarning")
def test_output_dtype_same_kind_ok(self, xp):
# After NumPy 2.5, NumPy gets the cast safety right, the following
# is OK under same-kind casting rules.
a = testing.shaped_arange(self.shape, xp, numpy.int64)
i = testing.shaped_arange(self.indices, xp, numpy.int32) % 3
results = []
for out_dtype in (numpy.complex64, numpy.int32):
o = testing.shaped_arange(self.out_shape, xp, out_dtype)
results.append(wrap_take(a, i, out=o))
return results

@pytest.mark.skip()
@pytest.mark.filterwarnings(
"error:Implicit casting of output dtype:DeprecationWarning"
)
def test_output_dtype_unsafe_rejected(self):
for xp in (numpy, cupy):
a = testing.shaped_arange(self.shape, xp, numpy.float32)
i = testing.shaped_arange(self.indices, xp, numpy.int32) % 3
o = testing.shaped_arange(self.out_shape, xp, numpy.int32)
# As of NumPy 2.5 this is a deprecation warning, but CuPy never
# allowed it (so no deprecation required)
if xp is numpy and not testing.numpy_satisfies(">=2.5"):
continue
with pytest.warns((TypeError, DeprecationWarning)):
wrap_take(a, i, out=o)


@testing.parameterize(
{"shape": (0,), "indices": (0,), "axis": None},
{"shape": (0,), "indices": (0, 1), "axis": None},
Expand Down
27 changes: 12 additions & 15 deletions dpnp/tests/third_party/cupy/core_tests/test_ndarray_reduction.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import pytest

import dpnp as cupy

# import cupy._core._accelerator as _acc
# from cupy._core import _cub_reduction
from dpnp.tests.third_party.cupy import testing


Expand Down Expand Up @@ -34,11 +37,10 @@ def exclude_cutensor(cls):
# pass
# _acc.set_reduction_accelerators(red_acc)

# yield
yield

# _acc.set_routine_accelerators(old_routine_accelerators)
# _acc.set_reduction_accelerators(old_reduction_accelerators)
pass

@testing.for_all_dtypes()
@testing.numpy_cupy_allclose(contiguous_check=False)
Expand Down Expand Up @@ -298,15 +300,7 @@ def _axes_for_shape(shape):
"shape,axis",
[
(shape, axis)
for shape in [
(),
(0,),
(0, 2),
(2, 0),
(0, 2, 3),
(2, 0, 3),
(2, 3, 0),
]
for shape in [(), (0,), (0, 2), (2, 0), (0, 2, 3), (2, 0, 3), (2, 3, 0)]
for axis in _axes_for_shape(shape)
],
)
Expand All @@ -331,13 +325,16 @@ def test_zero_size(self, xp, shape, axis, order, func):

# This class compares CUB results against NumPy's. ("fallback" is CuPy's
# original kernel, also tested here to reduce code duplication.)
# Non-empty shapes keep both the first and last axis >= 128 so the
# contiguous reduction stays on the CUB block-reduction path rather than
# the short-axis fallback.
@pytest.mark.parametrize(
"shape",
[
(10,),
(10, 20),
(10, 20, 30),
(10, 20, 30, 40),
(128,),
(128, 128),
(128, 2, 128),
(128, 2, 2, 128),
# skip (2, 3, 0) because it would not hit the CUB code path
(0,),
(2, 0),
Expand Down
Loading
Loading