Skip to content
Merged
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ This release is compatible with NumPy 2.5.
* Fixed comparison functions (`dpnp.equal`, `dpnp.not_equal`, `dpnp.less`, `dpnp.less_equal`, `dpnp.greater`, `dpnp.greater_equal`) and `dpnp.divide` raising `OverflowError` when comparing an integer array against a Python integer scalar outside the array dtype's range [#3017](https://github.com/IntelPython/dpnp/pull/3017)
* Fixed a crash in boolean-mask advanced indexing (`dpnp.ndarray` get/set item) when the selection is empty (e.g. a scalar `False` index that injects a length-0 axis) [#3019](https://github.com/IntelPython/dpnp/pull/3019)
* Released the GIL before the remaining blocking OneMKL BLAS and LAPACK calls to prevent host tasks contention, completing the work started in [#2850](https://github.com/IntelPython/dpnp/pull/2850) [#3027](https://github.com/IntelPython/dpnp/pull/3027)

* Fixed `dpnp.repeat` raising an unclear `TypeError` for a nested sequence of `repeats` [#3024](https://github.com/IntelPython/dpnp/pull/3024)

### Security

Expand Down
5 changes: 3 additions & 2 deletions dpnp/dpnp_iface_manipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2800,14 +2800,15 @@ def repeat(a, repeats, axis=None):

Parameters
----------
x : {dpnp.ndarray, usm_ndarray}
a : {dpnp.ndarray, usm_ndarray}
Input array.
repeats : {int, tuple, list, range, dpnp.ndarray, usm_ndarray}
The number of repetitions for each element. `repeats` is broadcasted to
fit the shape of the given axis.
If `repeats` is an array, it must have an integer data type.
Otherwise, `repeats` must be a Python integer or sequence of Python
integers (i.e., a tuple, list, or range).
integers (i.e., a tuple, list, or range). A sequence must be 0- or
1-dimensional.
axis : {None, int}, optional
The axis along which to repeat values. By default, use the flattened
input array, and return a flat output array.
Expand Down
19 changes: 13 additions & 6 deletions dpnp/tensor/_manipulation_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,8 @@ def repeat(x, repeats, /, *, axis=None):

If `repeats` is an array, it must have an integer data type.
Otherwise, `repeats` must be a Python integer or sequence of
Python integers (i.e., a tuple, list, or range).
Python integers (i.e., a tuple, list, or range). A sequence must
be 0- or 1-dimensional.

axis (Optional[int]):
The axis along which to repeat values. If `axis` is `None`, the
Expand Down Expand Up @@ -663,14 +664,20 @@ def repeat(x, repeats, /, *, axis=None):
usm_type = x.usm_type
exec_q = x.sycl_queue

len_reps = len(repeats)
if len_reps == 1:
repeats = repeats[0]
# inspect the sequence on the host to preserve the scalar fast path
repeats = np.asarray(repeats)
if repeats.ndim > 1:
raise ValueError(
"`repeats` sequence must be 0- or 1-dimensional, got "
f"{repeats.ndim} dimensions"
)
if repeats.size == 1:
scalar = True
repeats = int(repeats[0])
if repeats < 0:
raise ValueError("`repeats` elements must be positive")
scalar = True
else:
if len_reps != axis_size:
if repeats.size != axis_size:
raise ValueError(
"`repeats` sequence must have the same length as the "
"repeated axis"
Expand Down
8 changes: 8 additions & 0 deletions dpnp/tests/tensor/test_usm_ndarray_manipulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1464,6 +1464,14 @@ def test_repeat_arg_validation():
with pytest.raises(ValueError):
dpt.repeat(x, dpt.ones((1, 1), dtype="i8"))

# repeats nested sequence must be 0d or 1d
with pytest.raises(ValueError, match="0- or 1-dimensional"):
dpt.repeat(x, [[4]])
with pytest.raises(ValueError, match="0- or 1-dimensional"):
Comment thread
antonwolfy marked this conversation as resolved.
dpt.repeat(x, [[1, 2, 3, 4, 5]])
with pytest.raises(ValueError, match="0- or 1-dimensional"):
dpt.repeat(x, [[1], [2], [3], [4], [5]])

# repeats must be castable to i8
with pytest.raises(TypeError):
dpt.repeat(x, dpt.asarray(2.0, dtype="f4"))
Expand Down
82 changes: 39 additions & 43 deletions dpnp/tests/third_party/cupy/manipulation_tests/test_tiling.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import unittest

import numpy
Expand Down Expand Up @@ -29,11 +31,7 @@ def test_array_repeat(self, xp):
{"repeats": [2], "axis": None},
{"repeats": [2], "axis": 1},
)
class TestRepeatListBroadcast(unittest.TestCase):
"""Test for `repeats` argument using single element list.

This feature is only supported in NumPy 1.10 or later.
"""
class TestRepeatListBroadcast:

@testing.numpy_cupy_array_equal()
def test_array_repeat(self, xp):
Expand All @@ -48,7 +46,7 @@ def test_array_repeat(self, xp):
{"repeats": [1, 2, 3, 4], "axis": None},
{"repeats": [1, 2, 3, 4], "axis": 0},
)
class TestRepeat1D(unittest.TestCase):
class TestRepeat1D:

@testing.numpy_cupy_array_equal()
def test_array_repeat(self, xp):
Expand All @@ -60,8 +58,7 @@ def test_array_repeat(self, xp):
{"repeats": [2], "axis": None},
{"repeats": [2], "axis": 0},
)
class TestRepeat1DListBroadcast(unittest.TestCase):
"""See comment in TestRepeatListBroadcast class."""
class TestRepeat1DListBroadcast:

@testing.numpy_cupy_array_equal()
def test_array_repeat(self, xp):
Expand All @@ -77,7 +74,7 @@ def test_array_repeat(self, xp):
{"repeats": 2, "axis": -4},
{"repeats": 2, "axis": 3},
)
class TestRepeatFailure(unittest.TestCase):
class TestRepeatFailure:

def test_repeat_failure(self):
for xp in (numpy, cupy):
Expand Down Expand Up @@ -191,38 +188,6 @@ def test_reversed(self, xp):
return xp.repeat(x, xp.array([0, 1, 2, 1, 0]))


class TestRepeatNdarrayDtypeEdges:

@testing.numpy_cupy_array_equal()
def test_bool_perelement(self, xp):
return xp.repeat(xp.arange(3), xp.array([True, False, True]))

@testing.numpy_cupy_array_equal()
def test_bool_broadcast(self, xp):
return xp.repeat(
testing.shaped_arange((3, 4), xp), xp.array([True]), axis=0
)

@testing.numpy_cupy_array_equal()
def test_uint32_accepted(self, xp):
return xp.repeat(
xp.arange(4), xp.array([1, 2, 3, 4], dtype=numpy.uint32)
)


class TestRepeatNdarrayLarge:

@testing.numpy_cupy_array_equal()
def test_large_single(self, xp):
return xp.repeat(
testing.shaped_arange((3,), xp), xp.array([0, 100000, 0])
)

@testing.numpy_cupy_array_equal()
def test_large_broadcast(self, xp):
return xp.repeat(testing.shaped_arange((3,), xp), xp.array([50000]))


class TestRepeatScalarEquivalence:
"""All scalar-like repeats inputs produce identical results."""

Expand Down Expand Up @@ -293,9 +258,8 @@ def test_ndim_gt1_matches_numpy(self):
with pytest.raises(ValueError):
xp.repeat(xp.arange(6), xp.array([[1, 2, 3, 4, 5, 6]]))

@pytest.mark.skip("different message for nested lists")
def test_ndim_gt1_list_rejected(self):
with pytest.raises(ValueError, match=r"too deep"):
with pytest.raises(ValueError, match=r"0- or 1-dimensional"):
cupy.repeat(cupy.arange(6), [[1, 2, 3, 4, 5, 6]])

def test_bad_axis(self):
Expand All @@ -310,6 +274,38 @@ def test_method_interface(self):
testing.assert_array_equal(a.repeat(reps), cupy.repeat(a, reps))


class TestRepeatNdarrayDtypeEdges:

@testing.numpy_cupy_array_equal()
def test_bool_perelement(self, xp):
return xp.repeat(xp.arange(3), xp.array([True, False, True]))

@testing.numpy_cupy_array_equal()
def test_bool_broadcast(self, xp):
return xp.repeat(
testing.shaped_arange((3, 4), xp), xp.array([True]), axis=0
)

@testing.numpy_cupy_array_equal()
def test_uint32_accepted(self, xp):
return xp.repeat(
xp.arange(4), xp.array([1, 2, 3, 4], dtype=numpy.uint32)
)


class TestRepeatNdarrayLarge:

@testing.numpy_cupy_array_equal()
def test_large_single(self, xp):
return xp.repeat(
testing.shaped_arange((3,), xp), xp.array([0, 100000, 0])
)

@testing.numpy_cupy_array_equal()
def test_large_broadcast(self, xp):
return xp.repeat(testing.shaped_arange((3,), xp), xp.array([50000]))


@testing.parameterize(
{"reps": 0},
{"reps": 1},
Expand Down
Loading