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 diff --git a/dpnp/dpnp_utils/dpnp_algo_utils.pyx b/dpnp/dpnp_utils/dpnp_algo_utils.pyx index 732b792af125..944b49bc8e3a 100644 --- a/dpnp/dpnp_utils/dpnp_algo_utils.pyx +++ b/dpnp/dpnp_utils/dpnp_algo_utils.pyx @@ -390,10 +390,26 @@ cpdef inline tuple _object_to_tuple(object obj): if obj is None: return () - if cpython.PySequence_Check(obj): - return tuple(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 ecc76611377a..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 @@ -1115,3 +1116,31 @@ 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 + + @testing.with_requires("numpy>=2.3.2") + @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))