From 1a1c17ca4868e77675de2a595c684bfef6ef54a8 Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad Date: Wed, 30 Oct 2024 17:46:47 -0700 Subject: [PATCH 1/2] update dpnp.ndarray.item --- dpnp/dpnp_array.py | 45 ++++++++++++++----- tests/test_ndarray.py | 36 ++++++++++++--- .../core_tests/test_ndarray_conversion.py | 1 - 3 files changed, 63 insertions(+), 19 deletions(-) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index 1dde60771dc9..49c74162795a 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -989,7 +989,7 @@ def flatten(self, order="C"): Returns ------- - out: dpnp.ndarray + out : dpnp.ndarray A copy of the input array, flattened to one dimension. See Also @@ -1052,40 +1052,61 @@ def imag(self, value): else: raise TypeError("array does not have imaginary part to set") - def item(self, id=None): + def item(self, *args): """ Copy an element of an array to a standard Python scalar and return it. For full documentation refer to :obj:`numpy.ndarray.item`. + Parameters + ---------- + *args : {none, int, tuple of ints} + - none: in this case, the method only works for arrays with + one element (``a.size == 1``), which element is copied into a + standard Python scalar object and returned. + - int: this argument is interpreted as a flat index into the array, + specifying which element to copy and return. + - tuple of ints: functions as does a single int argument, except + that the argument is interpreted as an nd-index into the array. + + Returns + ------- + out : Standard Python scalar object + A copy of the specified element of the array as a suitable Python scalar. + Examples -------- + >>> import dpnp as np >>> np.random.seed(123) >>> x = np.random.randint(9, size=(3, 3)) >>> x - array([[2, 2, 6], - [1, 3, 6], - [1, 0, 1]]) + array([[0, 0, 7], + [6, 6, 6], + [0, 7, 1]]) >>> x.item(3) - 1 + 6 >>> x.item(7) - 0 + 7 >>> x.item((0, 1)) - 2 + 0 >>> x.item((2, 2)) 1 + >>> x = np.array(5) + >>> x.item() + 5 + """ - if id is None: + if args is None: if self.size != 1: raise ValueError( - "DPNP ndarray::item(): can only convert an array of size 1 to a Python scalar" + "can only convert an array of size 1 to a Python scalar" ) else: - id = 0 + args = 0 - return self.flat[id] + return self.asnumpy().item(*args) # 'itemset', diff --git a/tests/test_ndarray.py b/tests/test_ndarray.py index 44e514f0f74b..eaa3f8c5d211 100644 --- a/tests/test_ndarray.py +++ b/tests/test_ndarray.py @@ -55,7 +55,7 @@ def test_flatten(arr, arr_dtype): [(), 0, (0,), (2), (5, 2), (5, 0, 2), (5, 3, 2)], ids=["()", "0", "(0,)", "(2)", "(5, 2)", "(5, 0, 2)", "(5, 3, 2)"], ) -@pytest.mark.parametrize("order", ["C", "F"], ids=["C", "F"]) +@pytest.mark.parametrize("order", ["C", "F"]) def test_flags(shape, order): usm_array = dpt.usm_ndarray(shape, order=order) numpy_array = numpy.ndarray(shape, order=order) @@ -71,7 +71,7 @@ def test_flags(shape, order): ids=["complex64", "float32", "int64", "int32", "bool"], ) @pytest.mark.parametrize("strides", [(1, 4), (4, 1)], ids=["(1, 4)", "(4, 1)"]) -@pytest.mark.parametrize("order", ["C", "F"], ids=["C", "F"]) +@pytest.mark.parametrize("order", ["C", "F"]) def test_flags_strides(dtype, order, strides): itemsize = numpy.dtype(dtype).itemsize numpy_strides = tuple([el * itemsize for el in strides]) @@ -104,6 +104,32 @@ def test_flags_writable(): assert not a.imag.flags.writable +class TestItem: + @pytest.mark.parametrize("args", [2, 7, (1, 2), (2, 0)]) + def test_basic(self, args): + a = numpy.arange(12).reshape(3, 4) + ia = dpnp.array(a) + + expected = a.item(args) + result = ia.item(args) + assert isinstance(result, int) + assert expected == result + + def test_0D(self): + a = numpy.array(5) + ia = dpnp.array(a) + + expected = a.item() + result = ia.item() + assert isinstance(result, int) + assert expected == result + + def test_error(self): + ia = dpnp.arange(12).reshape(3, 4) + with pytest.raises(ValueError): + ia.item() + + def test_print_dpnp_int(): result = repr(dpnp.array([1, 0, 2, -3, -1, 2, 21, -9], dtype="i4")) expected = "array([ 1, 0, 2, -3, -1, 2, 21, -9], dtype=int32)" @@ -165,9 +191,7 @@ def test_print_dpnp_boolean(): assert result == expected -@pytest.mark.parametrize( - "character", [dpnp.nan, dpnp.inf], ids=["dpnp.nan", "dpnp.inf"] -) +@pytest.mark.parametrize("character", [dpnp.nan, dpnp.inf]) def test_print_dpnp_special_character(character): result = repr(dpnp.array([1.0, 0.0, character, 3.0])) expected = f"array([ 1., 0., {character}, 3.])" @@ -264,7 +288,7 @@ def test_array_as_index(shape, index_dtype): @pytest.mark.parametrize( "shape", [(3, 5), (2, 5, 2), (2, 3, 3, 6)], - ids=["(3,5)", "(2,5,2)", "(2,3,3,6)"], + ids=["(3, 5)", "(2, 5, 2)", "(2, 3, 3, 6)"], ) def test_matrix_transpose(shape): a = numpy.arange(numpy.prod(shape)).reshape(shape) diff --git a/tests/third_party/cupy/core_tests/test_ndarray_conversion.py b/tests/third_party/cupy/core_tests/test_ndarray_conversion.py index 1e9db4627654..6c5fdba573b2 100644 --- a/tests/third_party/cupy/core_tests/test_ndarray_conversion.py +++ b/tests/third_party/cupy/core_tests/test_ndarray_conversion.py @@ -12,7 +12,6 @@ {"shape": (1,)}, {"shape": (1, 1, 1)}, ) -@pytest.mark.usefixtures("allow_fall_back_on_numpy") class TestNdarrayItem(unittest.TestCase): @testing.for_all_dtypes() @testing.numpy_cupy_equal() From ed76eaec94d8dd147e7e0169b698f5c0ad3737e3 Mon Sep 17 00:00:00 2001 From: Vahid Tavanashad Date: Thu, 31 Oct 2024 09:43:06 -0700 Subject: [PATCH 2/2] address comments --- dpnp/dpnp_array.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index 49c74162795a..0aefcf625d66 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -1098,14 +1098,8 @@ def item(self, *args): """ - if args is None: - if self.size != 1: - raise ValueError( - "can only convert an array of size 1 to a Python scalar" - ) - else: - args = 0 - + # TODO: implement a more efficient way to avoid copying to host + # for large arrays using `asnumpy()` return self.asnumpy().item(*args) # 'itemset',