diff --git a/CHANGELOG.md b/CHANGELOG.md index 80800807524f..833a3fb5e2df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -121,6 +121,7 @@ This release is compatible with NumPy 2.5. * Fixed `dpnp.median` and `dpnp.nanmedian` raising a `ValueError` for a tuple `axis` when a kept dimension has size 0 [#3081](https://github.com/IntelPython/dpnp/pull/3081) * Fixed a `RuntimeError` raised by `dpnp.tensor` kernels launched over more than `INT_MAX` work-items when built with DPC++ compiler 2026.2 or newer, by also passing `-fno-sycl-id-queries-fit-in-int` to the linker [#3089](https://github.com/IntelPython/dpnp/pull/3089) * Fixed `dpnp.__all__` missing `ndarray` and `__array_namespace_info__` and listing `mean`, `median` and `min` twice [#3100](https://github.com/IntelPython/dpnp/pull/3100) +* Fixed `dpnp.repeat` and `dpnp.tensor.repeat` silently returning wrong results for `uint64` repeats above the `int64` maximum [#3115](https://github.com/IntelPython/dpnp/pull/3115) ### Security diff --git a/dpnp/tensor/_manipulation_functions.py b/dpnp/tensor/_manipulation_functions.py index 166a6cb5a526..7e3ffa05c57a 100644 --- a/dpnp/tensor/_manipulation_functions.py +++ b/dpnp/tensor/_manipulation_functions.py @@ -638,8 +638,12 @@ def repeat(x, repeats, /, *, axis=None): if not dpt.can_cast(repeats.dtype, dpt.int64, casting="same_kind"): raise TypeError( f"'repeats' data type {repeats.dtype} cannot be cast to " - "'int64' according to the casting rule ''safe.''" + "'int64' according to the casting rule ''same_kind.''" ) + err_msg = "`repeats` elements must be positive" + if repeats.dtype == dpt.uint64: + repeats = dpt.astype(repeats, dpt.int64) + err_msg = "`repeats` elements must not exceed the int64 maximum" if repeats.size == 1: scalar = True # bring the single element to the host @@ -650,7 +654,7 @@ def repeat(x, repeats, /, *, axis=None): # since non-0D arrays can not be converted to scalars repeats = int(repeats[0]) if repeats < 0: - raise ValueError("`repeats` elements must be positive") + raise ValueError(err_msg) else: if repeats.size != axis_size: raise ValueError( @@ -658,7 +662,7 @@ def repeat(x, repeats, /, *, axis=None): "the repeated axis" ) if not dpt.all(repeats >= 0): - raise ValueError("'repeats' elements must be positive") + raise ValueError(err_msg) elif isinstance(repeats, (tuple, list, range)): usm_type = x.usm_type diff --git a/dpnp/tests/tensor/test_usm_ndarray_manipulation.py b/dpnp/tests/tensor/test_usm_ndarray_manipulation.py index cd5962615fb9..6d82816b5453 100644 --- a/dpnp/tests/tensor/test_usm_ndarray_manipulation.py +++ b/dpnp/tests/tensor/test_usm_ndarray_manipulation.py @@ -1509,6 +1509,14 @@ def test_repeat_arg_validation(): with pytest.raises(ValueError): dpt.repeat(x, reps) + # u8 repeats above i8 max + reps = dpt.asarray(2**64 - 1, dtype="u8") + with pytest.raises(ValueError, match="int64 maximum"): + dpt.repeat(x, reps) + reps = dpt.asarray([1, 1, 2**64 - 1, 1, 1], dtype="u8") + with pytest.raises(ValueError, match="int64 maximum"): + dpt.repeat(x, reps) + # repeats must broadcastable to axis size reps = dpt.arange(10, dtype="i8") with pytest.raises(ValueError):