From beb8b422e62456b22dad517873e4fa2d692defb9 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Wed, 3 Jul 2024 19:38:07 +0200 Subject: [PATCH 1/5] Remove limitations from dpnp.extract implementation --- doc/reference/sorting.rst | 2 +- dpnp/dpnp_iface_indexing.py | 92 ++++++++++++++----- tests/skipped_tests.tbl | 5 - tests/skipped_tests_gpu.tbl | 5 - .../cupy/indexing_tests/test_indexing.py | 47 ++++++++-- 5 files changed, 108 insertions(+), 43 deletions(-) diff --git a/doc/reference/sorting.rst b/doc/reference/sorting.rst index d0a966c6731..ead79b1098a 100644 --- a/doc/reference/sorting.rst +++ b/doc/reference/sorting.rst @@ -31,10 +31,10 @@ Searching dpnp.nanargmax dpnp.argmin dpnp.nanargmin + dpnp.argwhere dpnp.nonzero dpnp.flatnonzero dpnp.where - dpnp.argwhere dpnp.searchsorted dpnp.extract diff --git a/dpnp/dpnp_iface_indexing.py b/dpnp/dpnp_iface_indexing.py index 0a1c8529c42..7e773b3dbfe 100644 --- a/dpnp/dpnp_iface_indexing.py +++ b/dpnp/dpnp_iface_indexing.py @@ -490,42 +490,86 @@ def diagonal(a, offset=0, axis1=0, axis2=1): ) -def extract(condition, x): +def extract(condition, a): """ Return the elements of an array that satisfy some condition. + This is equivalent to + ``dpnp.compress(dpnp.ravel(condition), dpnp.ravel(a))``. If `condition` + is boolean :obj:`dpnp.dpnp.extract` is equivalent to ``a[condition]``. + + Note that :obj:`dpnp.place` does the exact opposite of :obj:`dpnp.extract`. + For full documentation refer to :obj:`numpy.extract`. + Parameters + ---------- + condition : {array_like, scalar} + An array whose non-zero or ``True`` entries indicate the element of `a` + to extract. + a : {dpnp_array, usm_ndarray} + Input array of the same size as `condition`. + Returns ------- out : dpnp.ndarray - Rank 1 array of values from `x` where `condition` is True. + Rank 1 array of values from `a` where `condition` is ``True``. + + See Also + -------- + :obj:`dpnp.take` : Take elements from an array along an axis. + :obj:`dpnp.put` : Replaces specified elements of an array with given values. + :obj:`dpnp.copyto` : Copies values from one array to another, broadcasting + as necessary. + :obj:`dpnp.compress` : eturn selected slices of an array along given axis. + :obj:`dpnp.place` : Change elements of an array based on conditional and + input values. + + Examples + -------- + >>> import dpnp as np + >>> a = np.arange(12).reshape((3, 4)) + >>> a + array([[ 0, 1, 2, 3], + [ 4, 5, 6, 7], + [ 8, 9, 10, 11]]) + >>> condition = np.mod(a, 3) == 0 + >>> condition + array([[ True, False, False, True], + [False, False, True, False], + [False, True, False, False]]) + >>> np.extract(condition, a) + array([0, 3, 6, 9]) + + If `condition` is boolean: + + >>> a[condition] + array([0, 3, 6, 9]) - Limitations - ----------- - Parameters `condition` and `x` are supported either as - :class:`dpnp.ndarray` or :class:`dpctl.tensor.usm_ndarray`. - Parameter `x` must be the same shape as `condition`. - Otherwise the function will be executed sequentially on CPU. """ - if dpnp.is_supported_array_type(condition) and dpnp.is_supported_array_type( - x - ): - if condition.shape != x.shape: - pass - else: - dpt_condition = ( - condition.get_array() - if isinstance(condition, dpnp_array) - else condition - ) - dpt_array = x.get_array() if isinstance(x, dpnp_array) else x - return dpnp_array._create_from_usm_ndarray( - dpt.extract(dpt_condition, dpt_array) - ) + usm_a = dpnp.get_usm_ndarray(a) + if not dpnp.is_supported_array_type(condition): + usm_cond = dpt.asarray( + condition, usm_type=a.usm_type, sycl_queue=a.sycl_queue + ) + else: + usm_cond = dpnp.get_usm_ndarray(condition) + + if usm_cond.size != usm_a.size: + usm_a = dpt.reshape(usm_a, -1) + usm_cond = dpt.reshape(usm_cond, -1) + + usm_res = dpt.take(usm_a, dpt.nonzero(usm_cond)[0]) + else: + if usm_cond.shape != usm_a.shape: + usm_a = dpt.reshape(usm_a, -1) + usm_cond = dpt.reshape(usm_cond, -1) + + usm_res = dpt.extract(usm_cond, usm_a) - return call_origin(numpy.extract, condition, x) + dpnp.synchronize_array_data(usm_res) + return dpnp_array._create_from_usm_ndarray(usm_res) def fill_diagonal(a, val, wrap=False): diff --git a/tests/skipped_tests.tbl b/tests/skipped_tests.tbl index 37285be810f..199566295a3 100644 --- a/tests/skipped_tests.tbl +++ b/tests/skipped_tests.tbl @@ -124,11 +124,6 @@ tests/third_party/cupy/indexing_tests/test_generate.py::TestUnravelIndex::test_i tests/third_party/cupy/indexing_tests/test_generate.py::TestUnravelIndex::test_invalid_index tests/third_party/cupy/indexing_tests/test_generate.py::TestUnravelIndex::test_invalid_order -tests/third_party/cupy/indexing_tests/test_indexing.py::TestIndexing::test_compress -tests/third_party/cupy/indexing_tests/test_indexing.py::TestIndexing::test_compress_empty_1dim -tests/third_party/cupy/indexing_tests/test_indexing.py::TestIndexing::test_compress_empty_1dim_no_axis -tests/third_party/cupy/indexing_tests/test_indexing.py::TestIndexing::test_compress_no_axis -tests/third_party/cupy/indexing_tests/test_indexing.py::TestIndexing::test_compress_no_bool tests/third_party/cupy/indexing_tests/test_indexing.py::TestSelect::test_select tests/third_party/cupy/indexing_tests/test_indexing.py::TestSelect::test_select_1D_choicelist tests/third_party/cupy/indexing_tests/test_indexing.py::TestSelect::test_select_choicelist_condlist_broadcast diff --git a/tests/skipped_tests_gpu.tbl b/tests/skipped_tests_gpu.tbl index 55fd91b0def..26b52190539 100644 --- a/tests/skipped_tests_gpu.tbl +++ b/tests/skipped_tests_gpu.tbl @@ -174,11 +174,6 @@ tests/third_party/cupy/indexing_tests/test_generate.py::TestUnravelIndex::test_i tests/third_party/cupy/indexing_tests/test_generate.py::TestUnravelIndex::test_invalid_index tests/third_party/cupy/indexing_tests/test_generate.py::TestUnravelIndex::test_invalid_order -tests/third_party/cupy/indexing_tests/test_indexing.py::TestIndexing::test_compress -tests/third_party/cupy/indexing_tests/test_indexing.py::TestIndexing::test_compress_empty_1dim -tests/third_party/cupy/indexing_tests/test_indexing.py::TestIndexing::test_compress_empty_1dim_no_axis -tests/third_party/cupy/indexing_tests/test_indexing.py::TestIndexing::test_compress_no_axis -tests/third_party/cupy/indexing_tests/test_indexing.py::TestIndexing::test_compress_no_bool tests/third_party/cupy/indexing_tests/test_indexing.py::TestSelect::test_select tests/third_party/cupy/indexing_tests/test_indexing.py::TestSelect::test_select_1D_choicelist tests/third_party/cupy/indexing_tests/test_indexing.py::TestSelect::test_select_choicelist_condlist_broadcast diff --git a/tests/third_party/cupy/indexing_tests/test_indexing.py b/tests/third_party/cupy/indexing_tests/test_indexing.py index 7d05eedd2c3..6696bc47087 100644 --- a/tests/third_party/cupy/indexing_tests/test_indexing.py +++ b/tests/third_party/cupy/indexing_tests/test_indexing.py @@ -4,6 +4,7 @@ import pytest import dpnp as cupy +from tests.helper import has_support_aspect64 from tests.third_party.cupy import testing @@ -35,8 +36,10 @@ def test_take_no_axis(self, xp): return a.take(b) # see cupy#3017 + # mark slow as NumPy could go OOM on the Windows CI + @testing.slow @testing.for_int_dtypes(no_bool=True) - @testing.numpy_cupy_array_equal() + @testing.numpy_cupy_array_equal(type_check=has_support_aspect64()) def test_take_index_range_overflow(self, xp, dtype): # Skip for too large dimensions if numpy.dtype(dtype) in (numpy.int64, numpy.uint64): @@ -46,7 +49,7 @@ def test_take_index_range_overflow(self, xp, dtype): if dtype in (numpy.int32, numpy.uint32): pytest.skip() iinfo = numpy.iinfo(dtype) - a = xp.broadcast_to(xp.ones(1, dtype=dtype), (iinfo.max + 1,)) + a = xp.broadcast_to(xp.ones(1), (iinfo.max + 1,)) b = xp.array([0], dtype=dtype) return a.take(b) @@ -62,18 +65,21 @@ def test_take_along_axis_none_axis(self, xp): b = testing.shaped_random((30,), xp, dtype="int64", scale=24) return xp.take_along_axis(a, b, axis=None) + @pytest.mark.skip("compress() is not implemented yet") @testing.numpy_cupy_array_equal() def test_compress(self, xp): a = testing.shaped_arange((3, 4, 5), xp) b = xp.array([True, False, True]) return xp.compress(b, a, axis=1) + @pytest.mark.skip("compress() is not implemented yet") @testing.numpy_cupy_array_equal() def test_compress_no_axis(self, xp): a = testing.shaped_arange((3, 4, 5), xp) b = xp.array([True, False, True]) return xp.compress(b, a) + @pytest.mark.skip("compress() is not implemented yet") @testing.for_int_dtypes() @testing.numpy_cupy_array_equal() def test_compress_no_bool(self, xp, dtype): @@ -81,18 +87,34 @@ def test_compress_no_bool(self, xp, dtype): b = testing.shaped_arange((3,), xp, dtype) return xp.compress(b, a, axis=1) + @pytest.mark.skip("compress() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_compress_overrun_false(self, xp): + a = testing.shaped_arange((3,), xp) + b = xp.array([True, False, True, False, False, False]) + return xp.compress(b, a) + + @pytest.mark.skip("compress() is not implemented yet") @testing.numpy_cupy_array_equal() def test_compress_empty_1dim(self, xp): a = testing.shaped_arange((3, 4, 5), xp) b = xp.array([]) return xp.compress(b, a, axis=1) + @pytest.mark.skip("compress() is not implemented yet") @testing.numpy_cupy_array_equal() def test_compress_empty_1dim_no_axis(self, xp): a = testing.shaped_arange((3, 4, 5), xp) b = xp.array([]) return xp.compress(b, a) + @pytest.mark.skip("compress() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_compress_0dim(self, xp): + a = xp.array(3) + b = xp.array([True]) + return xp.compress(b, a) + @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() def test_diagonal(self, xp, dtype): @@ -162,28 +184,24 @@ def test_extract_no_bool(self, xp, dtype): b = xp.array([[1, 0, 1], [0, 1, 0], [1, 0, 1]], dtype=dtype) return xp.extract(b, a) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.numpy_cupy_array_equal() def test_extract_shape_mismatch(self, xp): a = testing.shaped_arange((2, 3), xp) b = xp.array([[True, False], [True, False], [True, False]]) return xp.extract(b, a) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.numpy_cupy_array_equal() def test_extract_size_mismatch(self, xp): a = testing.shaped_arange((3, 3), xp) b = xp.array([[True, False, True], [False, True, False]]) return xp.extract(b, a) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.numpy_cupy_array_equal() def test_extract_size_mismatch2(self, xp): a = testing.shaped_arange((3, 3), xp) b = xp.array([[True, False, True, False], [False, True, False, True]]) return xp.extract(b, a) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.numpy_cupy_array_equal() def test_extract_empty_1dim(self, xp): a = testing.shaped_arange((3, 3), xp) @@ -191,7 +209,6 @@ def test_extract_empty_1dim(self, xp): return xp.extract(b, a) -@pytest.mark.usefixtures("allow_fall_back_on_numpy") class TestChoose(unittest.TestCase): @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() @@ -200,13 +217,15 @@ def test_choose(self, xp, dtype): c = testing.shaped_arange((3, 4), xp, dtype) return a.choose(c) + @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() def test_choose_broadcast(self, xp, dtype): a = xp.array([[1, 0, 1], [0, 1, 0], [1, 0, 1]]) - c = xp.array([-10, 10], dtype=dtype) + c = xp.array([-10, 10]).astype(dtype) return a.choose(c) + @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() def test_choose_broadcast2(self, xp, dtype): @@ -214,6 +233,7 @@ def test_choose_broadcast2(self, xp, dtype): c = testing.shaped_arange((3, 5, 2), xp, dtype) return a.choose(c) + @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() def test_choose_wrap(self, xp, dtype): @@ -221,6 +241,7 @@ def test_choose_wrap(self, xp, dtype): c = testing.shaped_arange((3, 4), xp, dtype) return a.choose(c, mode="wrap") + @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() def test_choose_clip(self, xp, dtype): @@ -228,6 +249,7 @@ def test_choose_clip(self, xp, dtype): c = testing.shaped_arange((3, 4), xp, dtype) return a.choose(c, mode="clip") + @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.with_requires("numpy>=1.19") def test_unknown_clip(self): for xp in (numpy, cupy): @@ -236,12 +258,14 @@ def test_unknown_clip(self): with pytest.raises(ValueError): a.choose(c, mode="unknown") + @pytest.mark.usefixtures("allow_fall_back_on_numpy") def test_raise(self): a = cupy.array([2]) c = cupy.array([[0, 1]]) with self.assertRaises(ValueError): a.choose(c) + @pytest.mark.usefixtures("allow_fall_back_on_numpy") @testing.for_all_dtypes() def test_choose_broadcast_fail(self, dtype): for xp in (numpy, cupy): @@ -370,3 +394,10 @@ def test_select_default_scalar(self, dtype): choicelist = [a, b] with pytest.raises(TypeError): cupy.select(condlist, choicelist, [dtype(2)]) + + @pytest.mark.skip("as_strided() is not implemented yet") + @testing.numpy_cupy_array_equal() + def test_indexing_overflows(self, xp): + a = xp.arange(2, dtype=xp.int32) + a = xp.lib.stride_tricks.as_strided(a, shape=(2, 2**32), strides=(4, 0)) + return a[xp.array([1]), xp.array([1])] From 9564438c92d72cc4a927791529aa79f464ee4f16 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Wed, 3 Jul 2024 21:21:24 +0200 Subject: [PATCH 2/5] Add more tests --- tests/test_indexing.py | 230 ++++++++++++++++++++++++++------------- tests/test_sycl_queue.py | 1 + tests/test_usm_type.py | 2 + 3 files changed, 157 insertions(+), 76 deletions(-) diff --git a/tests/test_indexing.py b/tests/test_indexing.py index f001f994dbd..6051424841a 100644 --- a/tests/test_indexing.py +++ b/tests/test_indexing.py @@ -7,6 +7,7 @@ assert_array_equal, assert_equal, assert_raises, + assert_raises_regex, ) import dpnp @@ -29,6 +30,159 @@ def wrapped(a, axis, **kwargs): return wrapped +class TestDiagonal: + @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True)) + @pytest.mark.parametrize("offset", [-3, -1, 0, 1, 3]) + @pytest.mark.parametrize( + "shape", + [(2, 2), (3, 3), (2, 5), (3, 2, 2), (2, 2, 2, 2), (2, 2, 2, 3)], + ids=[ + "(2,2)", + "(3,3)", + "(2,5)", + "(3,2,2)", + "(2,2,2,2)", + "(2,2,2,3)", + ], + ) + def test_diagonal_offset(self, shape, dtype, offset): + a = numpy.arange(numpy.prod(shape), dtype=dtype).reshape(shape) + a_dp = dpnp.array(a) + expected = numpy.diagonal(a, offset) + result = dpnp.diagonal(a_dp, offset) + assert_array_equal(expected, result) + + @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True)) + @pytest.mark.parametrize( + "shape, axis_pairs", + [ + ((3, 4), [(0, 1), (1, 0)]), + ((3, 4, 5), [(0, 1), (1, 2), (0, 2)]), + ((4, 3, 5, 2), [(0, 1), (1, 2), (2, 3), (0, 3)]), + ], + ) + def test_diagonal_axes(self, shape, axis_pairs, dtype): + a = numpy.arange(numpy.prod(shape), dtype=dtype).reshape(shape) + a_dp = dpnp.array(a) + for axis1, axis2 in axis_pairs: + expected = numpy.diagonal(a, axis1=axis1, axis2=axis2) + result = dpnp.diagonal(a_dp, axis1=axis1, axis2=axis2) + assert_array_equal(expected, result) + + def test_diagonal_errors(self): + a = dpnp.arange(12).reshape(3, 4) + + # unsupported type + a_np = dpnp.asnumpy(a) + assert_raises(TypeError, dpnp.diagonal, a_np) + + # a.ndim < 2 + a_ndim_1 = a.flatten() + assert_raises(ValueError, dpnp.diagonal, a_ndim_1) + + # unsupported type `offset` + assert_raises(TypeError, dpnp.diagonal, a, offset=1.0) + assert_raises(TypeError, dpnp.diagonal, a, offset=[0]) + + # axes are out of bounds + assert_raises(numpy.AxisError, a.diagonal, axis1=0, axis2=5) + assert_raises(numpy.AxisError, a.diagonal, axis1=5, axis2=0) + assert_raises(numpy.AxisError, a.diagonal, axis1=5, axis2=5) + + # same axes + assert_raises(ValueError, a.diagonal, axis1=1, axis2=1) + assert_raises(ValueError, a.diagonal, axis1=1, axis2=-1) + + +class TestExtins: + @pytest.mark.parametrize("a_dt", get_all_dtypes(no_none=True)) + @pytest.mark.parametrize("cond_dt", get_all_dtypes(no_none=True)) + def test_extract_diff_dtypes(self, a_dt, cond_dt): + a = numpy.array([-2, -1, 0, 1, 2, 3], dtype=a_dt) + cond = numpy.array([1, -1, 2, 0, -2, 3], dtype=cond_dt) + ia, icond = dpnp.array(a), dpnp.array(cond) + + result = dpnp.extract(icond, ia) + expected = numpy.extract(cond, a) + assert_array_equal(result, expected) + + @pytest.mark.parametrize("dt", get_all_dtypes(no_none=True)) + def test_extract(self, dt): + a = numpy.array([1, 3, 2, 1, 2, 3, 3], dtype=dt) + ia = dpnp.array(a) + + result = dpnp.extract(ia > 1, ia) + expected = numpy.extract(a > 1, a) + assert_array_equal(result, expected) + + @pytest.mark.usefixtures("allow_fall_back_on_numpy") + @pytest.mark.parametrize("dt", get_all_dtypes(no_none=True)) + def test_place(self, dt): + a = numpy.array([1, 4, 3, 2, 5, 8, 7], dtype=dt) + ia = dpnp.array(a) + + dpnp.place(ia, [0, 1, 0, 1, 0, 1, 0], [2, 4, 6]) + numpy.place(a, [0, 1, 0, 1, 0, 1, 0], [2, 4, 6]) + assert_array_equal(ia, a) + + @pytest.mark.usefixtures("allow_fall_back_on_numpy") + def test_place_broadcast_vals(self): + a = numpy.array([1, 4, 3, 2, 5, 8, 7]) + ia = dpnp.array(a) + + dpnp.place(ia, [1, 0, 1, 0, 1, 0, 1], [8, 9]) + numpy.place(a, [1, 0, 1, 0, 1, 0, 1], [8, 9]) + assert_array_equal(ia, a) + + @pytest.mark.usefixtures("allow_fall_back_on_numpy") + def test_place_empty_vals(self): + a = numpy.array([1, 4, 3, 2, 5, 8, 7]) + mask = numpy.zeros(7) + ia, imask = dpnp.array(a), dpnp.array(mask) + vals = [] + + dpnp.place(ia, imask, vals) + numpy.place(a, mask, vals) + assert_array_equal(ia, a) + + @pytest.mark.usefixtures("allow_fall_back_on_numpy") + @pytest.mark.parametrize("xp", [numpy, dpnp]) + def test_place_insert_from_empty_vals(self, xp): + a = xp.array([1, 4, 3, 2, 5, 8, 7]) + assert_raises_regex( + ValueError, + "Cannot insert from an empty array", + lambda: xp.place(a, [0, 0, 0, 0, 0, 1, 0], []), + ) + + @pytest.mark.usefixtures("allow_fall_back_on_numpy") + @pytest.mark.parametrize("xp", [numpy, dpnp]) + def test_place_wrong_array_type(self, xp): + assert_raises(TypeError, xp.place, [1, 2, 3], [True, False], [0, 1]) + + @pytest.mark.usefixtures("allow_fall_back_on_numpy") + @pytest.mark.parametrize("dt", get_all_dtypes(no_none=True)) + def test_both(self, dt): + a = numpy.random.rand(10).astype(dt) + mask = a > 0.5 + ia, imask = dpnp.array(a), dpnp.array(mask) + + result = dpnp.extract(imask, ia) + expected = numpy.extract(mask, a) + assert_array_equal(result, expected) + + ic = dpnp.extract(imask, ia) + c = numpy.extract(mask, a) + assert_array_equal(ic, c) + + dpnp.place(ia, imask, 0) + dpnp.place(ia, imask, ic) + + numpy.place(a, mask, 0) + numpy.place(a, mask, c) + assert_array_equal(ia, a) + + class TestIndexing: def test_ellipsis_index(self): a = dpnp.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) @@ -477,82 +631,6 @@ def test_choose(): assert_array_equal(expected, result) -class TestDiagonal: - @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True)) - @pytest.mark.parametrize("offset", [-3, -1, 0, 1, 3]) - @pytest.mark.parametrize( - "shape", - [(2, 2), (3, 3), (2, 5), (3, 2, 2), (2, 2, 2, 2), (2, 2, 2, 3)], - ids=[ - "(2,2)", - "(3,3)", - "(2,5)", - "(3,2,2)", - "(2,2,2,2)", - "(2,2,2,3)", - ], - ) - def test_diagonal_offset(self, shape, dtype, offset): - a = numpy.arange(numpy.prod(shape), dtype=dtype).reshape(shape) - a_dp = dpnp.array(a) - expected = numpy.diagonal(a, offset) - result = dpnp.diagonal(a_dp, offset) - assert_array_equal(expected, result) - - @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True)) - @pytest.mark.parametrize( - "shape, axis_pairs", - [ - ((3, 4), [(0, 1), (1, 0)]), - ((3, 4, 5), [(0, 1), (1, 2), (0, 2)]), - ((4, 3, 5, 2), [(0, 1), (1, 2), (2, 3), (0, 3)]), - ], - ) - def test_diagonal_axes(self, shape, axis_pairs, dtype): - a = numpy.arange(numpy.prod(shape), dtype=dtype).reshape(shape) - a_dp = dpnp.array(a) - for axis1, axis2 in axis_pairs: - expected = numpy.diagonal(a, axis1=axis1, axis2=axis2) - result = dpnp.diagonal(a_dp, axis1=axis1, axis2=axis2) - assert_array_equal(expected, result) - - def test_diagonal_errors(self): - a = dpnp.arange(12).reshape(3, 4) - - # unsupported type - a_np = dpnp.asnumpy(a) - assert_raises(TypeError, dpnp.diagonal, a_np) - - # a.ndim < 2 - a_ndim_1 = a.flatten() - assert_raises(ValueError, dpnp.diagonal, a_ndim_1) - - # unsupported type `offset` - assert_raises(TypeError, dpnp.diagonal, a, offset=1.0) - assert_raises(TypeError, dpnp.diagonal, a, offset=[0]) - - # axes are out of bounds - assert_raises(numpy.AxisError, a.diagonal, axis1=0, axis2=5) - assert_raises(numpy.AxisError, a.diagonal, axis1=5, axis2=0) - assert_raises(numpy.AxisError, a.diagonal, axis1=5, axis2=5) - - # same axes - assert_raises(ValueError, a.diagonal, axis1=1, axis2=1) - assert_raises(ValueError, a.diagonal, axis1=1, axis2=-1) - - -@pytest.mark.parametrize("arr_dtype", get_all_dtypes()) -@pytest.mark.parametrize("cond_dtype", get_all_dtypes()) -def test_extract_1d(arr_dtype, cond_dtype): - a = numpy.array([-2, -1, 0, 1, 2, 3], dtype=arr_dtype) - ia = dpnp.array(a) - cond = numpy.array([1, -1, 2, 0, -2, 3], dtype=cond_dtype) - icond = dpnp.array(cond) - expected = numpy.extract(cond, a) - result = dpnp.extract(icond, ia) - assert_array_equal(expected, result) - - @pytest.mark.parametrize("val", [-1, 0, 1], ids=["-1", "0", "1"]) @pytest.mark.parametrize( "array", diff --git a/tests/test_sycl_queue.py b/tests/test_sycl_queue.py index f7c70320dbf..1ea5592ecc4 100644 --- a/tests/test_sycl_queue.py +++ b/tests/test_sycl_queue.py @@ -647,6 +647,7 @@ def test_reduce_hypot(device): pytest.param("dot", [3.0, 4.0, 5.0], [1.0, 2.0, 3.0]), pytest.param("dot", [3, 4, 5], [1, 2, 3]), pytest.param("dot", [3 + 2j, 4 + 1j, 5], [1, 2 + 3j, 3]), + pytest.param("extract", [False, True, True, False], [0, 1, 2, 3]), pytest.param( "floor_divide", [1.0, 2.0, 3.0, 4.0], [2.5, 2.5, 2.5, 2.5] ), diff --git a/tests/test_usm_type.py b/tests/test_usm_type.py index 8d43bccd75a..d38acc4a657 100644 --- a/tests/test_usm_type.py +++ b/tests/test_usm_type.py @@ -637,6 +637,8 @@ def test_1in_1out(func, data, usm_type): pytest.param("dot", [3.0, 4.0, 5.0], [1.0, 2.0, 3.0]), pytest.param("dot", [3, 4, 5], [1, 2, 3]), pytest.param("dot", [3 + 2j, 4 + 1j, 5], [1, 2 + 3j, 3]), + # TODO: uncomment once resolved in gh-1723 by dpctl + # pytest.param("extract", [False, True, True, False], [0, 1, 2, 3]), pytest.param("fmax", [[0.0, 1.0, 2.0]], [[3.0, 4.0, 5.0]]), pytest.param("fmin", [[0.0, 1.0, 2.0]], [[3.0, 4.0, 5.0]]), pytest.param("fmod", [5, 3], [2, 2.0]), From 7541430201aa4102348a08fe455d7d72e879ca49 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Wed, 3 Jul 2024 21:29:11 +0200 Subject: [PATCH 3/5] Tune rtol and atol for a histogram test, since might fail on Windows --- tests/test_histogram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_histogram.py b/tests/test_histogram.py index da58a4ac2f8..0e6e33fd99c 100644 --- a/tests/test_histogram.py +++ b/tests/test_histogram.py @@ -182,7 +182,7 @@ def test_density(self, dtype): result_hist, result_edges = dpnp.histogram(iv, density=True) if numpy.issubdtype(dtype, numpy.inexact): - tol = numpy.finfo(dtype).resolution + tol = 4 * numpy.finfo(dtype).resolution assert_allclose(result_hist, expected_hist, rtol=tol, atol=tol) assert_allclose(result_edges, expected_edges, rtol=tol, atol=tol) else: From bfa2da289783739d922764be890a0b0a4e9f7784 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Wed, 3 Jul 2024 22:06:06 +0200 Subject: [PATCH 4/5] Fix a typo in description --- dpnp/dpnp_iface_indexing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpnp/dpnp_iface_indexing.py b/dpnp/dpnp_iface_indexing.py index 7e773b3dbfe..20a046c82c1 100644 --- a/dpnp/dpnp_iface_indexing.py +++ b/dpnp/dpnp_iface_indexing.py @@ -496,7 +496,7 @@ def extract(condition, a): This is equivalent to ``dpnp.compress(dpnp.ravel(condition), dpnp.ravel(a))``. If `condition` - is boolean :obj:`dpnp.dpnp.extract` is equivalent to ``a[condition]``. + is boolean :obj:`dpnp.extract` is equivalent to ``a[condition]``. Note that :obj:`dpnp.place` does the exact opposite of :obj:`dpnp.extract`. From 014a09c42bf4b9fb6fbe07ce6adf5f61437172ef Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 4 Jul 2024 12:26:10 +0200 Subject: [PATCH 5/5] Add test to cover condition as list --- tests/test_indexing.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_indexing.py b/tests/test_indexing.py index 6051424841a..8b54bc482ce 100644 --- a/tests/test_indexing.py +++ b/tests/test_indexing.py @@ -115,6 +115,16 @@ def test_extract(self, dt): expected = numpy.extract(a > 1, a) assert_array_equal(result, expected) + @pytest.mark.parametrize("a_dt", get_all_dtypes(no_none=True)) + def test_extract_list_cond(self, a_dt): + a = numpy.array([-2, -1, 0, 1, 2, 3], dtype=a_dt) + cond = [1, -1, 2, 0, -2, 3] + ia = dpnp.array(a) + + result = dpnp.extract(cond, ia) + expected = numpy.extract(cond, a) + assert_array_equal(result, expected) + @pytest.mark.usefixtures("allow_fall_back_on_numpy") @pytest.mark.parametrize("dt", get_all_dtypes(no_none=True)) def test_place(self, dt):