From d0259b00124c4272b9acc2ae21540c21d2e7a331 Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad Date: Tue, 18 Feb 2025 16:18:09 -0600 Subject: [PATCH 1/3] improve covergae dpnp_iface_manipulation.py --- dpnp/dpnp_iface_manipulation.py | 7 +- dpnp/tests/test_manipulation.py | 145 +++++++++++++++++++++----------- 2 files changed, 100 insertions(+), 52 deletions(-) diff --git a/dpnp/dpnp_iface_manipulation.py b/dpnp/dpnp_iface_manipulation.py index 8234bb7916ab..62226aa2d5d2 100644 --- a/dpnp/dpnp_iface_manipulation.py +++ b/dpnp/dpnp_iface_manipulation.py @@ -1494,11 +1494,10 @@ def copyto(dst, src, casting="same_kind", where=True): f"but got {type(dst)}" ) if not dpnp.is_supported_array_type(src): - no_dtype_attr = not hasattr(src, "dtype") + python_sc = dpnp.isscalar(src) and not isinstance(src, numpy.generic) src = dpnp.array(src, sycl_queue=dst.sycl_queue) - if no_dtype_attr: - # This case (scalar, list, etc) needs special handling to - # behave similar to NumPy + if python_sc: + # Python scalar needs special handling to behave similar to NumPy if dpnp.issubdtype(src, dpnp.integer) and dpnp.issubdtype( dst, dpnp.unsignedinteger ): diff --git a/dpnp/tests/test_manipulation.py b/dpnp/tests/test_manipulation.py index 4dcfe02862f0..745aebd03782 100644 --- a/dpnp/tests/test_manipulation.py +++ b/dpnp/tests/test_manipulation.py @@ -21,23 +21,10 @@ get_float_dtypes, get_integer_dtypes, has_support_aspect64, + numpy_version, ) from .third_party.cupy import testing -testdata = [] -testdata += [ - ([True, False, True], dtype) - for dtype in get_all_dtypes(no_none=True, no_complex=True) -] -testdata += [ - ([1, -1, 0], dtype) - for dtype in get_all_dtypes( - no_none=True, no_bool=True, no_complex=True, no_unsigned=True - ) -] -testdata += [([0.1, 0.0, -0.1], dtype) for dtype in get_float_dtypes()] -testdata += [([1j, -1j, 1 - 2j], dtype) for dtype in get_complex_dtypes()] - def _compare_results(result, expected): """Compare lists of arrays.""" @@ -48,40 +35,6 @@ def _compare_results(result, expected): assert_array_equal(x, y) -@pytest.mark.parametrize("in_obj, out_dtype", testdata) -def test_copyto_dtype(in_obj, out_dtype): - ndarr = numpy.array(in_obj) - expected = numpy.empty(ndarr.size, dtype=out_dtype) - numpy.copyto(expected, ndarr) - - dparr = dpnp.array(in_obj) - result = dpnp.empty(dparr.size, dtype=out_dtype) - dpnp.copyto(result, dparr) - - assert_array_equal(result, expected) - - -@pytest.mark.parametrize("dst", [7, numpy.ones(10), (2, 7), [5], range(3)]) -def test_copyto_dst_raises(dst): - a = dpnp.array(4) - with pytest.raises( - TypeError, - match="Destination array must be any of supported type, but got", - ): - dpnp.copyto(dst, a) - - -@pytest.mark.parametrize("where", [numpy.ones(10), (2, 7), [5], range(3)]) -def test_copyto_where_raises(where): - a = dpnp.empty((2, 3)) - b = dpnp.arange(6).reshape((2, 3)) - - with pytest.raises( - TypeError, match="`where` array must be any of supported type, but got" - ): - dpnp.copyto(a, b, where=where) - - 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"] @@ -364,6 +317,102 @@ def test_broadcast_shapes(self, shape): assert_equal(result, expected) +class TestCopyTo: + testdata = [] + testdata += [ + ([True, False, True], dtype) + for dtype in get_all_dtypes(no_none=True, no_complex=True) + ] + testdata += [ + ([1, -1, 0], dtype) + for dtype in get_all_dtypes( + no_none=True, no_bool=True, no_complex=True, no_unsigned=True + ) + ] + testdata += [([0.1, 0.0, -0.1], dtype) for dtype in get_float_dtypes()] + testdata += [([1j, -1j, 1 - 2j], dtype) for dtype in get_complex_dtypes()] + + @pytest.mark.parametrize("data, dt_out", testdata) + def test_dtype(self, data, dt_out): + a = numpy.array(data) + ia = dpnp.array(a) + + expected = numpy.empty(a.size, dtype=dt_out) + result = dpnp.empty(ia.size, dtype=dt_out) + numpy.copyto(expected, a) + dpnp.copyto(result, ia) + + assert_array_equal(result, expected) + + @pytest.mark.parametrize("data, dt_out", testdata) + def test_dtype_input_list(self, data, dt_out): + expected = numpy.empty(3, dtype=dt_out) + result = dpnp.empty(3, dtype=dt_out) + assert isinstance(data, list) + numpy.copyto(expected, data) + dpnp.copyto(result, data) + + assert_array_equal(result, expected) + + @pytest.mark.parametrize("xp", [dpnp, numpy]) + @pytest.mark.parametrize( + "data", [(1, 2, -3), [1, 2, -3]], ids=["tuple", "list"] + ) + @pytest.mark.parametrize( + "dst_dt", [dpnp.uint8, dpnp.uint16, dpnp.uint32, dpnp.uint64] + ) + def test_casting_error(self, xp, data, dst_dt): + # cannot cast to unsigned integer + dst = xp.empty(3, dtype=dst_dt) + assert_raises(TypeError, xp.copyto, dst, data) + + @pytest.mark.parametrize( + "dt_out", [dpnp.uint8, dpnp.uint16, dpnp.uint32, dpnp.uint64] + ) + def test_positive_python_scalar(self, dt_out): + # src is python scalar and positive + expected = numpy.empty(1, dtype=dt_out) + result = dpnp.array(expected) + numpy.copyto(expected, 5) + dpnp.copyto(result, 5) + + assert_array_equal(result, expected) + + @testing.with_requires("numpy>=2.0") + @pytest.mark.parametrize("xp", [dpnp, numpy]) + @pytest.mark.parametrize( + "dst_dt", [dpnp.uint8, dpnp.uint16, dpnp.uint32, dpnp.uint64] + ) + def test_numpy_scalar(self, xp, dst_dt): + dst = xp.empty(1, dtype=dst_dt) + # cannot cast from signed int to unsigned int, src is numpy scalar + assert_raises(TypeError, xp.copyto, dst, numpy.int32(5)) + assert_raises(TypeError, xp.copyto, dst, numpy.int32(-5)) + + # Python integer -5 out of bounds, src is python scalar and negative + assert_raises(OverflowError, xp.copyto, dst, -5) + + @pytest.mark.parametrize("dst", [7, numpy.ones(10), (2, 7), [5], range(3)]) + def test_dst_raises(self, dst): + a = dpnp.array(4) + with pytest.raises( + TypeError, + match="Destination array must be any of supported type, but got", + ): + dpnp.copyto(dst, a) + + @pytest.mark.parametrize("where", [numpy.ones(10), (2, 7), [5], range(3)]) + def test_where_raises(self, where): + a = dpnp.empty((2, 3)) + b = dpnp.arange(6).reshape((2, 3)) + + with pytest.raises( + TypeError, + match="`where` array must be any of supported type, but got", + ): + dpnp.copyto(a, b, where=where) + + class TestDelete: @pytest.mark.parametrize( "obj", [slice(0, 4, 2), 3, [2, 3]], ids=["slice", "int", "list"] From 01244e4a82d096c7fd49b43b9da694e75ccad5a5 Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad Date: Tue, 18 Feb 2025 18:15:18 -0600 Subject: [PATCH 2/3] remove unused function --- dpnp/tests/test_manipulation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dpnp/tests/test_manipulation.py b/dpnp/tests/test_manipulation.py index 745aebd03782..c3d9d0899bff 100644 --- a/dpnp/tests/test_manipulation.py +++ b/dpnp/tests/test_manipulation.py @@ -21,7 +21,6 @@ get_float_dtypes, get_integer_dtypes, has_support_aspect64, - numpy_version, ) from .third_party.cupy import testing From 90818003555bc25d399ce45408effac864b6eb26 Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad Date: Wed, 19 Feb 2025 10:06:23 -0600 Subject: [PATCH 3/3] change version of required numpy for a test --- dpnp/tests/test_manipulation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpnp/tests/test_manipulation.py b/dpnp/tests/test_manipulation.py index c3d9d0899bff..f453ba9954b2 100644 --- a/dpnp/tests/test_manipulation.py +++ b/dpnp/tests/test_manipulation.py @@ -377,7 +377,7 @@ def test_positive_python_scalar(self, dt_out): assert_array_equal(result, expected) - @testing.with_requires("numpy>=2.0") + @testing.with_requires("numpy>=2.1") @pytest.mark.parametrize("xp", [dpnp, numpy]) @pytest.mark.parametrize( "dst_dt", [dpnp.uint8, dpnp.uint16, dpnp.uint32, dpnp.uint64]