diff --git a/dpnp/dpnp_iface_manipulation.py b/dpnp/dpnp_iface_manipulation.py index 056ac790720..bf3c66d7fda 100644 --- a/dpnp/dpnp_iface_manipulation.py +++ b/dpnp/dpnp_iface_manipulation.py @@ -1248,12 +1248,16 @@ def repeat(a, repeats, axis=None): ---------- x : {dpnp.ndarray, usm_ndarray} Input array. - repeat : int or array of int + 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. - axis : int, optional + 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). + axis : {None, int}, optional The axis along which to repeat values. By default, use the flattened input array, and return a flat output array. + Default: ``None``. Returns ------- @@ -1263,8 +1267,8 @@ def repeat(a, repeats, axis=None): See Also -------- - :obj:`dpnp.tile` : Construct an array by repeating A the number of times - given by reps. + :obj:`dpnp.tile` : Tile an array. + :obj:`dpnp.unique` : Find the unique elements of an array. Examples -------- @@ -1286,14 +1290,15 @@ def repeat(a, repeats, axis=None): """ - rep = repeats - if isinstance(repeats, dpnp_array): - rep = dpnp.get_usm_ndarray(repeats) + dpnp.check_supported_arrays_type(a) + if not isinstance(repeats, (int, tuple, list, range)): + repeats = dpnp.get_usm_ndarray(repeats) + if axis is None and a.ndim > 1: - usm_arr = dpnp.get_usm_ndarray(a.flatten()) - else: - usm_arr = dpnp.get_usm_ndarray(a) - usm_arr = dpt.repeat(usm_arr, rep, axis=axis) + a = dpnp.ravel(a) + + usm_arr = dpnp.get_usm_ndarray(a) + usm_arr = dpt.repeat(usm_arr, repeats, axis=axis) return dpnp_array._create_from_usm_ndarray(usm_arr) diff --git a/tests/test_arraymanipulation.py b/tests/test_arraymanipulation.py index 12f14bf4109..a6bbfd0e987 100644 --- a/tests/test_arraymanipulation.py +++ b/tests/test_arraymanipulation.py @@ -1016,114 +1016,3 @@ def test_can_cast(): assert dpnp.can_cast(X, "float32") == numpy.can_cast(X_np, "float32") assert dpnp.can_cast(X, dpnp.int32) == numpy.can_cast(X_np, numpy.int32) assert dpnp.can_cast(X, dpnp.int64) == numpy.can_cast(X_np, numpy.int64) - - -def test_repeat_scalar_sequence_agreement(): - x = dpnp.arange(5, dtype="i4") - expected_res = dpnp.empty(10, dtype="i4") - expected_res[1::2], expected_res[::2] = x, x - - # scalar case - reps = 2 - res = dpnp.repeat(x, reps) - assert dpnp.all(res == expected_res) - - # tuple - reps = (2, 2, 2, 2, 2) - res = dpnp.repeat(x, reps) - assert dpnp.all(res == expected_res) - - -def test_repeat_as_broadcasting(): - reps = 5 - x = dpnp.arange(reps, dtype="i4") - x1 = x[:, dpnp.newaxis] - expected_res = dpnp.broadcast_to(x1, (reps, reps)) - - res = dpnp.repeat(x1, reps, axis=1) - assert dpnp.all(res == expected_res) - - x2 = x[dpnp.newaxis, :] - expected_res = dpnp.broadcast_to(x2, (reps, reps)) - - res = dpnp.repeat(x2, reps, axis=0) - assert dpnp.all(res == expected_res) - - -def test_repeat_axes(): - reps = 2 - x = dpnp.reshape(dpnp.arange(5 * 10, dtype="i4"), (5, 10)) - expected_res = dpnp.empty((x.shape[0] * 2, x.shape[1]), dtype=x.dtype) - expected_res[::2, :], expected_res[1::2] = x, x - res = dpnp.repeat(x, reps, axis=0) - assert dpnp.all(res == expected_res) - - expected_res = dpnp.empty((x.shape[0], x.shape[1] * 2), dtype=x.dtype) - expected_res[:, ::2], expected_res[:, 1::2] = x, x - res = dpnp.repeat(x, reps, axis=1) - assert dpnp.all(res == expected_res) - - -def test_repeat_size_0_outputs(): - x = dpnp.ones((3, 0, 5), dtype="i4") - reps = 10 - res = dpnp.repeat(x, reps, axis=0) - assert res.size == 0 - assert res.shape == (30, 0, 5) - - res = dpnp.repeat(x, reps, axis=1) - assert res.size == 0 - assert res.shape == (3, 0, 5) - - res = dpnp.repeat(x, (2, 2, 2), axis=0) - assert res.size == 0 - assert res.shape == (6, 0, 5) - - x = dpnp.ones((3, 2, 5)) - res = dpnp.repeat(x, 0, axis=1) - assert res.size == 0 - assert res.shape == (3, 0, 5) - - x = dpnp.ones((3, 2, 5)) - res = dpnp.repeat(x, (0, 0), axis=1) - assert res.size == 0 - assert res.shape == (3, 0, 5) - - -def test_repeat_strides(): - reps = 2 - x = dpnp.reshape(dpnp.arange(10 * 10, dtype="i4"), (10, 10)) - x1 = x[:, ::-2] - expected_res = dpnp.empty((10, 10), dtype="i4") - expected_res[:, ::2], expected_res[:, 1::2] = x1, x1 - res = dpnp.repeat(x1, reps, axis=1) - assert dpnp.all(res == expected_res) - res = dpnp.repeat(x1, (reps,) * x1.shape[1], axis=1) - assert dpnp.all(res == expected_res) - - x1 = x[::-2, :] - expected_res = dpnp.empty((10, 10), dtype="i4") - expected_res[::2, :], expected_res[1::2, :] = x1, x1 - res = dpnp.repeat(x1, reps, axis=0) - assert dpnp.all(res == expected_res) - res = dpnp.repeat(x1, (reps,) * x1.shape[0], axis=0) - assert dpnp.all(res == expected_res) - - -def test_repeat_casting(): - x = dpnp.arange(5, dtype="i4") - # i4 is cast to i8 - reps = dpnp.ones(5, dtype="i4") - res = dpnp.repeat(x, reps) - assert res.shape == x.shape - assert dpnp.all(res == x) - - -def test_repeat_strided_repeats(): - x = dpnp.arange(5, dtype="i4") - reps = dpnp.ones(10, dtype="i8") - reps[::2] = 0 - reps = reps[::-2] - res = dpnp.repeat(x, reps) - assert res.shape == x.shape - assert dpnp.all(res == x) diff --git a/tests/test_manipulation.py b/tests/test_manipulation.py index 9c0869024a5..0178ff9a28b 100644 --- a/tests/test_manipulation.py +++ b/tests/test_manipulation.py @@ -1,6 +1,7 @@ +import dpctl.tensor as dpt import numpy import pytest -from numpy.testing import assert_array_equal +from numpy.testing import assert_array_equal, assert_raises import dpnp @@ -58,20 +59,6 @@ def test_copyto_where_raises(where): dpnp.copyto(a, b, where=where) -@pytest.mark.usefixtures("allow_fall_back_on_numpy") -@pytest.mark.parametrize( - "arr", - [[], [1, 2, 3, 4], [[1, 2], [3, 4]], [[[1], [2]], [[3], [4]]]], - ids=["[]", "[1, 2, 3, 4]", "[[1, 2], [3, 4]]", "[[[1], [2]], [[3], [4]]]"], -) -def test_repeat(arr): - a = numpy.array(arr) - dpnp_a = dpnp.array(arr) - expected = numpy.repeat(a, 2) - result = dpnp.repeat(dpnp_a, 2) - assert_array_equal(expected, result) - - def test_result_type(): X = [dpnp.ones((2), dtype=dpnp.int64), dpnp.int32, "float32"] X_np = [numpy.ones((2), dtype=numpy.int64), numpy.int32, "float32"] @@ -114,6 +101,225 @@ def test_unique(array): assert_array_equal(expected, result) +class TestRepeat: + @pytest.mark.parametrize( + "data", + [[], [1, 2, 3, 4], [[1, 2], [3, 4]], [[[1], [2]], [[3], [4]]]], + ids=[ + "[]", + "[1, 2, 3, 4]", + "[[1, 2], [3, 4]]", + "[[[1], [2]], [[3], [4]]]", + ], + ) + @pytest.mark.parametrize("dtype", get_all_dtypes()) + def test_data(self, data, dtype): + a = numpy.array(data, dtype=dtype) + ia = dpnp.array(a) + + expected = numpy.repeat(a, 2) + result = dpnp.repeat(ia, 2) + assert_array_equal(expected, result) + + @pytest.mark.parametrize( + "repeats", [2, (2, 2, 2, 2, 2)], ids=["scalar", "tuple"] + ) + def test_scalar_sequence_agreement(self, repeats): + a = numpy.arange(5, dtype="i4") + ia = dpnp.array(a) + + expected = numpy.repeat(a, repeats) + result = dpnp.repeat(ia, repeats) + assert_array_equal(expected, result) + + @pytest.mark.parametrize("axis", [0, 1]) + def test_broadcasting(self, axis): + reps = 5 + a = numpy.arange(reps, dtype="i4") + if axis == 0: + sh = (reps, 1) + else: + sh = (1, reps) + a = a.reshape(sh) + ia = dpnp.array(a) + + expected = numpy.repeat(a, reps) + result = dpnp.repeat(ia, reps) + assert_array_equal(expected, result) + + @pytest.mark.parametrize("axis", [0, 1]) + def test_axes(self, axis): + reps = 2 + a = numpy.arange(5 * 10, dtype="i4").reshape((5, 10)) + ia = dpnp.array(a) + + expected = numpy.repeat(a, reps, axis=axis) + result = dpnp.repeat(ia, reps, axis=axis) + assert_array_equal(expected, result) + + def test_size_0_outputs(self): + reps = 10 + a = dpnp.ones((3, 0, 5), dtype="i4") + ia = dpnp.array(a) + + expected = numpy.repeat(a, reps, axis=0) + result = dpnp.repeat(ia, reps, axis=0) + assert_array_equal(expected, result) + + expected = numpy.repeat(a, reps, axis=1) + result = dpnp.repeat(ia, reps, axis=1) + assert_array_equal(expected, result) + + reps = (2, 2, 2) + expected = numpy.repeat(a, reps, axis=0) + result = dpnp.repeat(ia, reps, axis=0) + assert_array_equal(expected, result) + + a = numpy.ones((3, 2, 5)) + ia = dpnp.array(a) + + reps = 0 + expected = numpy.repeat(a, reps, axis=1) + result = dpnp.repeat(ia, reps, axis=1) + assert_array_equal(expected, result) + + reps = (0, 0) + expected = numpy.repeat(a, reps, axis=1) + result = dpnp.repeat(ia, reps, axis=1) + assert_array_equal(expected, result) + + def test_strides_0(self): + reps = 2 + a = numpy.arange(10 * 10, dtype="i4").reshape((10, 10)) + ia = dpnp.array(a) + + a = a[::-2, :] + ia = ia[::-2, :] + + expected = numpy.repeat(a, reps, axis=0) + result = dpnp.repeat(ia, reps, axis=0) + assert_array_equal(expected, result) + + expected = numpy.repeat(a, (reps,) * a.shape[0], axis=0) + result = dpnp.repeat(ia, (reps,) * ia.shape[0], axis=0) + assert_array_equal(expected, result) + + def test_strides_1(self): + reps = 2 + a = numpy.arange(10 * 10, dtype="i4").reshape((10, 10)) + ia = dpnp.array(a) + + a = a[:, ::-2] + ia = ia[:, ::-2] + + expected = numpy.repeat(a, reps, axis=1) + result = dpnp.repeat(ia, reps, axis=1) + assert_array_equal(expected, result) + + expected = numpy.repeat(a, (reps,) * a.shape[1], axis=1) + result = dpnp.repeat(ia, (reps,) * ia.shape[1], axis=1) + assert_array_equal(expected, result) + + def test_casting(self): + a = numpy.arange(5, dtype="i4") + ia = dpnp.array(a) + + # i4 is cast to i8 + reps = numpy.ones(5, dtype="i4") + ireps = dpnp.array(reps) + + expected = numpy.repeat(a, reps) + result = dpnp.repeat(ia, ireps) + assert_array_equal(expected, result) + + def test_strided_repeats(self): + a = numpy.arange(5, dtype="i4") + ia = dpnp.array(a) + + reps = numpy.ones(10, dtype="i8") + reps[::2] = 0 + ireps = dpnp.array(reps) + + reps = reps[::-2] + ireps = ireps[::-2] + + expected = numpy.repeat(a, reps) + result = dpnp.repeat(ia, ireps) + assert_array_equal(expected, result) + + def test_usm_ndarray_as_input_array(self): + reps = [1, 3, 2, 1, 1, 2] + a = numpy.array([[1, 2, 3, 4, 5, 6]]) + ia = dpt.asarray(a) + + expected = numpy.repeat(a, reps) + result = dpnp.repeat(ia, reps) + assert_array_equal(expected, result) + assert isinstance(result, dpnp.ndarray) + + def test_scalar_as_input_array(self): + assert_raises(TypeError, dpnp.repeat, 3, 2) + + def test_usm_ndarray_as_repeats(self): + a = numpy.array([1, 2, 3, 4, 5, 6]).reshape((2, 3)) + ia = dpnp.asarray(a) + + reps = numpy.array([1, 3, 2]) + ireps = dpt.asarray(reps) + + expected = a.repeat(reps, axis=1) + result = ia.repeat(ireps, axis=1) + assert_array_equal(expected, result) + assert isinstance(result, dpnp.ndarray) + + def test_unsupported_array_as_repeats(self): + assert_raises(TypeError, dpnp.arange(5, dtype="i4"), numpy.array(3)) + + @pytest.mark.parametrize( + "data, dtype", + [ + pytest.param([1, 2**7 - 1, -(2**7)], numpy.int8, id="int8"), + pytest.param([1, 2**15 - 1, -(2**15)], numpy.int16, id="int16"), + pytest.param([1, 2**31 - 1, -(2**31)], numpy.int32, id="int32"), + pytest.param([1, 2**63 - 1, -(2**63)], numpy.int64, id="int64"), + ], + ) + def test_maximum_signed_integers(self, data, dtype): + reps = 129 + a = numpy.array(data, dtype=dtype) + ia = dpnp.asarray(a) + + expected = a.repeat(reps) + result = ia.repeat(reps) + assert_array_equal(expected, result) + + @pytest.mark.parametrize( + "data, dtype", + [ + pytest.param( + [1, -(2**7), -(2**7) + 1, 2**7 - 1], numpy.int8, id="int8" + ), + pytest.param( + [1, -(2**15), -(2**15) + 1, 2**15 - 1], numpy.int16, id="int16" + ), + pytest.param( + [1, -(2**31), -(2**31) + 1, 2**31 - 1], numpy.int32, id="int32" + ), + pytest.param( + [1, -(2**63), -(2**63) + 1, 2**63 - 1], numpy.int64, id="int64" + ), + ], + ) + def test_minimum_signed_integers(self, data, dtype): + reps = 129 + a = numpy.array(data, dtype=dtype) + ia = dpnp.asarray(a) + + expected = a.repeat(reps) + result = ia.repeat(reps) + assert_array_equal(expected, result) + + class TestTranspose: @pytest.mark.parametrize("axes", [(0, 1), (1, 0), [0, 1]]) def test_2d_with_axes(self, axes): diff --git a/tests/third_party/cupy/manipulation_tests/test_tiling.py b/tests/third_party/cupy/manipulation_tests/test_tiling.py index eb29036d248..365a01f7e14 100644 --- a/tests/third_party/cupy/manipulation_tests/test_tiling.py +++ b/tests/third_party/cupy/manipulation_tests/test_tiling.py @@ -16,7 +16,6 @@ {"repeats": [1, 2, 3], "axis": 1}, {"repeats": [1, 2, 3], "axis": -2}, ) -@pytest.mark.usefixtures("allow_fall_back_on_numpy") class TestRepeat(unittest.TestCase): @testing.numpy_cupy_array_equal() def test_array_repeat(self, xp): @@ -42,7 +41,6 @@ def test_method(self): {"repeats": [2], "axis": None}, {"repeats": [2], "axis": 1}, ) -@pytest.mark.usefixtures("allow_fall_back_on_numpy") class TestRepeatListBroadcast(unittest.TestCase): """Test for `repeats` argument using single element list. @@ -62,7 +60,6 @@ def test_array_repeat(self, xp): {"repeats": [1, 2, 3, 4], "axis": None}, {"repeats": [1, 2, 3, 4], "axis": 0}, ) -@pytest.mark.usefixtures("allow_fall_back_on_numpy") class TestRepeat1D(unittest.TestCase): @testing.numpy_cupy_array_equal() def test_array_repeat(self, xp): @@ -91,7 +88,6 @@ def test_array_repeat(self, xp): {"repeats": 2, "axis": -4}, {"repeats": 2, "axis": 3}, ) -@pytest.mark.usefixtures("allow_fall_back_on_numpy") class TestRepeatFailure(unittest.TestCase): def test_repeat_failure(self): for xp in (numpy, cupy):