From d4aea9fb7f0202f641ca9722cd1c5f3cc06ec5eb Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Mon, 8 Sep 2025 04:34:02 -0700 Subject: [PATCH 1/6] Allow size with each element castable to Py_ssize_t --- dpnp/dpnp_utils/dpnp_algo_utils.pyx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dpnp/dpnp_utils/dpnp_algo_utils.pyx b/dpnp/dpnp_utils/dpnp_algo_utils.pyx index 732b792af125..e1ee1cd519dc 100644 --- a/dpnp/dpnp_utils/dpnp_algo_utils.pyx +++ b/dpnp/dpnp_utils/dpnp_algo_utils.pyx @@ -391,7 +391,14 @@ cpdef inline tuple _object_to_tuple(object obj): return () if cpython.PySequence_Check(obj): - return tuple(obj) + nd = len(obj) + shape = [] + + for i in range(0, nd): + # Assumes each item is castable to Py_ssize_t, + # otherwise TypeError will be raised + shape.append( obj[i]) + return tuple(shape) if dpnp.isscalar(obj): return (obj, ) From 3ef0c3a585f3483999fbf5b6339986c5aa3a06f0 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Mon, 8 Sep 2025 04:45:31 -0700 Subject: [PATCH 2/6] Add a test to cover the change --- dpnp/tests/test_random_state.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dpnp/tests/test_random_state.py b/dpnp/tests/test_random_state.py index ecc76611377a..1e504f351d2b 100644 --- a/dpnp/tests/test_random_state.py +++ b/dpnp/tests/test_random_state.py @@ -1115,3 +1115,13 @@ def test_invalid_dtype(self, dtype): def test_invalid_usm_type(self, usm_type): # dtype must be float32 or float64 assert_raises(ValueError, RandomState().uniform, usm_type=usm_type) + + def test_size_castable_to_integer(self): + M = numpy.int64(31) + N = numpy.int64(31) + K = 63 # plain Python int + + sizes = [(M, K), (M, N), (K, N)] + for size in sizes: + result = RandomState().uniform(size=size) + assert result.shape == size From 97542d396772376efcd76e39530097a11f61589d Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Mon, 8 Sep 2025 04:47:52 -0700 Subject: [PATCH 3/6] Add PR to the changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d8b00b20e58..830fe55f653e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Fixed `dpnp.unique` with 1d input array and `axis=0`, `equal_nan=True` keywords passed where the produced result doesn't collapse the NaNs [#2530](https://github.com/IntelPython/dpnp/pull/2530) * Resolved issue when `dpnp.ndarray` constructor is called with `dpnp.ndarray.data` as `buffer` keyword [#2533](https://github.com/IntelPython/dpnp/pull/2533) * Fixed `dpnp.linalg.cond` to always return a real dtype [#2547](https://github.com/IntelPython/dpnp/pull/2547) +* Resolved the issue in `dpnp.random` functions to allow any value of `size` where each element is castable to `Py_ssize_t` type [#2578](https://github.com/IntelPython/dpnp/pull/2578) ### Security From f832c6240d35a562d33bb9c3f3e792a48644c8e7 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Mon, 8 Sep 2025 08:27:19 -0700 Subject: [PATCH 4/6] Add explicit checks to disallow size with any boolean item --- dpnp/dpnp_utils/dpnp_algo_utils.pyx | 11 ++++++++++- dpnp/tests/test_random_state.py | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/dpnp/dpnp_utils/dpnp_algo_utils.pyx b/dpnp/dpnp_utils/dpnp_algo_utils.pyx index e1ee1cd519dc..944b49bc8e3a 100644 --- a/dpnp/dpnp_utils/dpnp_algo_utils.pyx +++ b/dpnp/dpnp_utils/dpnp_algo_utils.pyx @@ -390,17 +390,26 @@ cpdef inline tuple _object_to_tuple(object obj): if obj is None: return () - if cpython.PySequence_Check(obj): + # dpnp.ndarray unconditionally succeeds in PySequence_Check as it implements __getitem__ + if cpython.PySequence_Check(obj) and not dpnp.is_supported_array_type(obj): + if isinstance(obj, numpy.ndarray): + obj = numpy.atleast_1d(obj) + nd = len(obj) shape = [] for i in range(0, nd): + if cpython.PyBool_Check(obj[i]): + raise TypeError("DPNP object_to_tuple(): no item in size can be bool") + # Assumes each item is castable to Py_ssize_t, # otherwise TypeError will be raised shape.append( obj[i]) return tuple(shape) if dpnp.isscalar(obj): + if cpython.PyBool_Check(obj): + raise TypeError("DPNP object_to_tuple(): 'obj' can't be bool") return (obj, ) raise ValueError("DPNP object_to_tuple(): 'obj' should be 'None', collections.abc.Sequence, or 'int'") diff --git a/dpnp/tests/test_random_state.py b/dpnp/tests/test_random_state.py index 1e504f351d2b..93f27bfb3da1 100644 --- a/dpnp/tests/test_random_state.py +++ b/dpnp/tests/test_random_state.py @@ -1125,3 +1125,17 @@ def test_size_castable_to_integer(self): for size in sizes: result = RandomState().uniform(size=size) assert result.shape == size + + @pytest.mark.parametrize("xp", [numpy, dpnp]) + @pytest.mark.parametrize("size", [True, [True], dpnp.bool(True), numpy.array(True), numpy.array([True])]) + def test_bool_size(self, xp, size): + rs = xp.random.RandomState() + assert_raises(TypeError, rs.uniform, size=size) + + @pytest.mark.parametrize("size", [numpy.array(1), numpy.array([2])]) + def test_numpy_ndarray_size(self, size): + result = RandomState().uniform(size=size) + assert result.shape == size + + def test_dpnp_ndarray_size(self): + assert_raises(ValueError, RandomState().uniform, size=dpnp.array(1)) From 91388b866f8b61cfd4eb3fb5c727ea73bc3d9cc6 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Mon, 8 Sep 2025 17:31:11 +0200 Subject: [PATCH 5/6] Applied pre-commit --- dpnp/tests/test_random_state.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dpnp/tests/test_random_state.py b/dpnp/tests/test_random_state.py index 93f27bfb3da1..b4d92b263b2b 100644 --- a/dpnp/tests/test_random_state.py +++ b/dpnp/tests/test_random_state.py @@ -1127,7 +1127,10 @@ def test_size_castable_to_integer(self): assert result.shape == size @pytest.mark.parametrize("xp", [numpy, dpnp]) - @pytest.mark.parametrize("size", [True, [True], dpnp.bool(True), numpy.array(True), numpy.array([True])]) + @pytest.mark.parametrize( + "size", + [True, [True], dpnp.bool(True), numpy.array(True), numpy.array([True])], + ) def test_bool_size(self, xp, size): rs = xp.random.RandomState() assert_raises(TypeError, rs.uniform, size=size) @@ -1138,4 +1141,4 @@ def test_numpy_ndarray_size(self, size): assert result.shape == size def test_dpnp_ndarray_size(self): - assert_raises(ValueError, RandomState().uniform, size=dpnp.array(1)) + assert_raises(ValueError, RandomState().uniform, size=dpnp.array(1)) From a5cd35b386d6ca1a2c9325773230cf97161fc73f Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Mon, 8 Sep 2025 09:47:50 -0700 Subject: [PATCH 6/6] test_bool_size requires numpy 2.3.2 at least --- dpnp/tests/test_random_state.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dpnp/tests/test_random_state.py b/dpnp/tests/test_random_state.py index b4d92b263b2b..ae63f901e1f4 100644 --- a/dpnp/tests/test_random_state.py +++ b/dpnp/tests/test_random_state.py @@ -21,6 +21,7 @@ is_cpu_device, is_gpu_device, ) +from .third_party.cupy import testing # aspects of default device: _def_device = dpctl.SyclQueue().sycl_device @@ -1126,6 +1127,7 @@ def test_size_castable_to_integer(self): result = RandomState().uniform(size=size) assert result.shape == size + @testing.with_requires("numpy>=2.3.2") @pytest.mark.parametrize("xp", [numpy, dpnp]) @pytest.mark.parametrize( "size",