From c7fa828405a524a6d263d8ddf214e98d650a1229 Mon Sep 17 00:00:00 2001 From: Max Lindqvist Date: Wed, 7 Oct 2026 17:56:56 +0200 Subject: [PATCH 1/3] converts the numpy argument to the form CuPy accepts at launch time --- src/cunumpy/_cuda_kernel.py | 4 ++++ tests/unit/test_cuda_kernel.py | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/cunumpy/_cuda_kernel.py b/src/cunumpy/_cuda_kernel.py index fafd890..e072149 100644 --- a/src/cunumpy/_cuda_kernel.py +++ b/src/cunumpy/_cuda_kernel.py @@ -2421,6 +2421,10 @@ def __call__( if shared_mem < 0: raise ValueError(f"shared_mem must be non-negative, got {shared_mem}") values = self.prepare_args(*args) + # RawKernel accepts size-one NumPy arrays for structs passed by value, + # but not structured NumPy scalars (np.void). Keep their packed bytes + # and alignment intact, including array-view pointers and strides. + values = tuple(np.asarray(v) if isinstance(v, np.void) else v for v in values) if 0 in grid_shape: return diff --git a/tests/unit/test_cuda_kernel.py b/tests/unit/test_cuda_kernel.py index 0ef8c8c..b268f36 100644 --- a/tests/unit/test_cuda_kernel.py +++ b/tests/unit/test_cuda_kernel.py @@ -2306,6 +2306,7 @@ def __init__(self): def __call__(self, grid, block, args, shared_mem=0): self.launches.append((grid, block, shared_mem)) + self.args = args @pytest.fixture @@ -2333,6 +2334,28 @@ def recorded(monkeypatch): return kernel, raw +def test_launch_passes_structs_as_size_one_numpy_arrays(recorded, monkeypatch): + kernel, raw = recorded + packed = np.zeros((), dtype=[("data", np.uintp), ("shape", np.int64, (1,))]) + packed["data"] = 0x1000 + packed["shape"] = (7,) + scalar = np.float64(2.0) + device_array = FakeDeviceArray(np.float64, shape=(7,)) + monkeypatch.setattr( + kernel, "prepare_args", lambda *args: (packed[()], scalar, device_array) + ) + + kernel(n_threads=1) + + struct_arg, scalar_arg, pointer_arg = raw.args + assert isinstance(struct_arg, np.ndarray) + assert struct_arg.size == 1 + assert struct_arg.dtype == packed.dtype + assert struct_arg.tobytes() == packed.tobytes() + assert scalar_arg is scalar + assert pointer_arg is device_array + + def test_n_threads_from_first_array(recorded): kernel, raw = recorded kernel.n_threads_from = "first_array" From 2e6380337a90680d97f4a30a2323536155b23d9c Mon Sep 17 00:00:00 2001 From: Max Lindqvist Date: Wed, 7 Oct 2026 17:58:13 +0200 Subject: [PATCH 2/3] formatting --- tests/unit/test_kernel_testing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_kernel_testing.py b/tests/unit/test_kernel_testing.py index ed01e9c..212613d 100644 --- a/tests/unit/test_kernel_testing.py +++ b/tests/unit/test_kernel_testing.py @@ -17,12 +17,12 @@ import cunumpy.kernel_testing from cunumpy.arguments import CudaArguments from cunumpy.cuda import parse_cuda_signature +from cunumpy.kernel_testing import backend # noqa: F401 - the fixture is used by name from cunumpy.kernel_testing import ( BACKENDS, _collect_arrays, _compare_results, assert_kernels_agree, - backend, # noqa: F401 - the fixture is used by name device_function_kernel, requires_cupy, ) From 50f19544a7d5c86a7d4b437fa4540461d2008a8a Mon Sep 17 00:00:00 2001 From: Max Lindqvist Date: Wed, 7 Oct 2026 18:10:59 +0200 Subject: [PATCH 3/3] ruff check --fix --- tests/unit/test_kernel_testing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/test_kernel_testing.py b/tests/unit/test_kernel_testing.py index 212613d..ed01e9c 100644 --- a/tests/unit/test_kernel_testing.py +++ b/tests/unit/test_kernel_testing.py @@ -17,12 +17,12 @@ import cunumpy.kernel_testing from cunumpy.arguments import CudaArguments from cunumpy.cuda import parse_cuda_signature -from cunumpy.kernel_testing import backend # noqa: F401 - the fixture is used by name from cunumpy.kernel_testing import ( BACKENDS, _collect_arrays, _compare_results, assert_kernels_agree, + backend, # noqa: F401 - the fixture is used by name device_function_kernel, requires_cupy, )