diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index f2ccf56ef76b..70ba6f44580c 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -194,7 +194,10 @@ def __gt__(self, other): return dpnp.greater(self, other) # '__hash__', - # '__iadd__', + + def __iadd__(self, other): + dpnp.add(self, other, out=self) + return self def __iand__(self, other): dpnp.bitwise_and(self, other, out=self) diff --git a/dpnp/dpnp_iface_mathematical.py b/dpnp/dpnp_iface_mathematical.py index a001b055a280..4c19801346b4 100644 --- a/dpnp/dpnp_iface_mathematical.py +++ b/dpnp/dpnp_iface_mathematical.py @@ -235,7 +235,7 @@ def add(x1, ----------- Parameters `x1` and `x2` are supported as either scalar, :class:`dpnp.ndarray` or :class:`dpctl.tensor.usm_ndarray`, but both `x1` and `x2` can not be scalars at the same time. - Parameters `out`, `where`, `dtype` and `subok` are supported with their default values. + Parameters `where`, `dtype` and `subok` are supported with their default values. Keyword arguments ``kwargs`` are currently unsupported. Otherwise the function will be executed sequentially on CPU. Input array data types are limited by supported DPNP :ref:`Data types`. @@ -251,29 +251,7 @@ def add(x1, """ - if out is not None: - pass - elif where is not True: - pass - elif dtype is not None: - pass - elif subok is not True: - pass - elif dpnp.isscalar(x1) and dpnp.isscalar(x2): - # at least either x1 or x2 has to be an array - pass - else: - # get USM type and queue to copy scalar from the host memory into a USM allocation - usm_type, queue = get_usm_allocations([x1, x2]) if dpnp.isscalar(x1) or dpnp.isscalar(x2) else (None, None) - - x1_desc = dpnp.get_dpnp_descriptor(x1, copy_when_strides=False, copy_when_nondefault_queue=False, - alloc_usm_type=usm_type, alloc_queue=queue) - x2_desc = dpnp.get_dpnp_descriptor(x2, copy_when_strides=False, copy_when_nondefault_queue=False, - alloc_usm_type=usm_type, alloc_queue=queue) - if x1_desc and x2_desc: - return dpnp_add(x1_desc, x2_desc, dtype=dtype, out=out, where=where).get_pyobj() - - return call_origin(numpy.add, x1, x2, out=out, where=where, dtype=dtype, subok=subok, **kwargs) + return _check_nd_call(numpy.add, dpnp_add, x1, x2, out=out, where=where, dtype=dtype, subok=subok, **kwargs) def around(x1, decimals=0, out=None): diff --git a/tests/test_mathematical.py b/tests/test_mathematical.py index 4b3b5d07f941..47ee33cb64d0 100644 --- a/tests/test_mathematical.py +++ b/tests/test_mathematical.py @@ -1,6 +1,7 @@ import pytest from .helper import ( get_all_dtypes, + get_float_complex_dtypes, is_cpu_device, is_win_platform ) @@ -634,24 +635,108 @@ def test_invalid_shape(self, shape): dpnp.trunc(dp_array, out=dp_out) +class TestAdd: + @pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True)) + def test_add(self, dtype): + array1_data = numpy.arange(10) + array2_data = numpy.arange(5, 15) + out = numpy.empty(10, dtype=dtype) + + # DPNP + dp_array1 = dpnp.array(array1_data, dtype=dtype) + dp_array2 = dpnp.array(array2_data, dtype=dtype) + dp_out = dpnp.array(out, dtype=dtype) + result = dpnp.add(dp_array1, dp_array2, out=dp_out) + + # original + np_array1 = numpy.array(array1_data, dtype=dtype) + np_array2 = numpy.array(array2_data, dtype=dtype) + expected = numpy.add(np_array1, np_array2, out=out) + + assert_allclose(expected, result) + assert_allclose(out, dp_out) + + @pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True)) + def test_out_dtypes(self, dtype): + size = 2 if dtype == dpnp.bool else 10 + + np_array1 = numpy.arange(size, 2 * size, dtype=dtype) + np_array2 = numpy.arange(size, dtype=dtype) + np_out = numpy.empty(size, dtype=numpy.complex64) + expected = numpy.add(np_array1, np_array2, out=np_out) + + dp_array1 = dpnp.arange(size, 2 * size, dtype=dtype) + dp_array2 = dpnp.arange(size, dtype=dtype) + dp_out = dpnp.empty(size, dtype=dpnp.complex64) + result = dpnp.add(dp_array1, dp_array2, out=dp_out) + + assert_array_equal(expected, result) + + @pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True)) + def test_out_overlap(self, dtype): + size = 1 if dtype == dpnp.bool else 15 + + np_a = numpy.arange(2 * size, dtype=dtype) + expected = numpy.add(np_a[size::], np_a[::2], out=np_a[:size:]) + + dp_a = dpnp.arange(2 * size, dtype=dtype) + result = dpnp.add(dp_a[size::], dp_a[::2], out=dp_a[:size:]) + + assert_allclose(expected, result) + assert_allclose(dp_a, np_a) + + @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True, no_none=True)) + def test_inplace_strided_out(self, dtype): + size = 21 + + np_a = numpy.arange(size, dtype=dtype) + np_a[::3] += 4 + + dp_a = dpnp.arange(size, dtype=dtype) + dp_a[::3] += 4 + + assert_allclose(dp_a, np_a) + + @pytest.mark.parametrize("shape", + [(0,), (15, ), (2, 2)], + ids=['(0,)', '(15, )', '(2,2)']) + def test_invalid_shape(self, shape): + dp_array1 = dpnp.arange(10, dtype=dpnp.float64) + dp_array2 = dpnp.arange(5, 15, dtype=dpnp.float64) + dp_out = dpnp.empty(shape, dtype=dpnp.float64) + + with pytest.raises(ValueError): + dpnp.add(dp_array1, dp_array2, out=dp_out) + + @pytest.mark.parametrize("out", + [4, (), [], (3, 7), [2, 4]], + ids=['4', '()', '[]', '(3, 7)', '[2, 4]']) + def test_invalid_out(self, out): + a = dpnp.arange(10) + + assert_raises(TypeError, dpnp.add, a, 2, out) + assert_raises(TypeError, numpy.add, a.asnumpy(), 2, out) + + class TestPower: - def test_power(self): + @pytest.mark.parametrize("dtype", get_float_complex_dtypes()) + def test_power(self, dtype): array1_data = numpy.arange(10) array2_data = numpy.arange(5, 15) - out = numpy.empty(10, dtype=numpy.float64) + out = numpy.empty(10, dtype=dtype) # DPNP - dp_array1 = dpnp.array(array1_data, dtype=dpnp.float64) - dp_array2 = dpnp.array(array2_data, dtype=dpnp.float64) - dp_out = dpnp.array(out, dtype=dpnp.float64) + dp_array1 = dpnp.array(array1_data, dtype=dtype) + dp_array2 = dpnp.array(array2_data, dtype=dtype) + dp_out = dpnp.array(out, dtype=dtype) result = dpnp.power(dp_array1, dp_array2, out=dp_out) # original - np_array1 = numpy.array(array1_data, dtype=numpy.float64) - np_array2 = numpy.array(array2_data, dtype=numpy.float64) + np_array1 = numpy.array(array1_data, dtype=dtype) + np_array2 = numpy.array(array2_data, dtype=dtype) expected = numpy.power(np_array1, np_array2, out=out) - assert_array_equal(expected, result) + assert_allclose(expected, result) @pytest.mark.parametrize("dtype", get_all_dtypes(no_complex=True, no_none=True)) def test_out_dtypes(self, dtype): @@ -662,7 +747,7 @@ def test_out_dtypes(self, dtype): np_out = numpy.empty(size, dtype=numpy.complex64) expected = numpy.power(np_array1, np_array2, out=np_out) - dp_array1 = dpnp.arange(size, 2*size, dtype=dtype) + dp_array1 = dpnp.arange(size, 2 * size, dtype=dtype) dp_array2 = dpnp.arange(size, dtype=dtype) dp_out = dpnp.empty(size, dtype=dpnp.complex64) result = dpnp.power(dp_array1, dp_array2, out=dp_out) diff --git a/tests/test_strides.py b/tests/test_strides.py index 10bd575bf6a9..8295b03412a3 100644 --- a/tests/test_strides.py +++ b/tests/test_strides.py @@ -217,7 +217,7 @@ def test_strides_true_devide(dtype, shape): @pytest.mark.parametrize("func_name", - ["power"]) + ["add", "power"]) @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True, no_complex=True)) def test_strided_out_2args(func_name, dtype): np_out = numpy.ones((5, 3, 2))[::3] @@ -236,7 +236,7 @@ def test_strided_out_2args(func_name, dtype): @pytest.mark.parametrize("func_name", - ["power"]) + ["add", "power"]) @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True, no_complex=True)) def test_strided_in_out_2args(func_name, dtype): sh = (3, 4, 2) @@ -258,7 +258,7 @@ def test_strided_in_out_2args(func_name, dtype): @pytest.mark.parametrize("func_name", - ["power"]) + ["add", "power"]) @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True, no_complex=True)) def test_strided_in_out_2args_diff_out_dtype(func_name, dtype): sh = (3, 3, 2) @@ -280,7 +280,7 @@ def test_strided_in_out_2args_diff_out_dtype(func_name, dtype): @pytest.mark.parametrize("func_name", - ["power"]) + ["add", "power"]) @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True, no_complex=True, no_none=True)) def test_strided_in_2args_overlap(func_name, dtype): size = 5 @@ -296,7 +296,7 @@ def test_strided_in_2args_overlap(func_name, dtype): @pytest.mark.parametrize("func_name", - ["power"]) + ["add", "power"]) @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True, no_complex=True, no_none=True)) def test_strided_in_out_2args_overlap(func_name, dtype): sh = (4, 3, 2) diff --git a/tests/test_usm_type.py b/tests/test_usm_type.py index 817bdee66a57..2d5a3f4f1a4d 100644 --- a/tests/test_usm_type.py +++ b/tests/test_usm_type.py @@ -18,6 +18,8 @@ def test_coerced_usm_types_sum(usm_type_x, usm_type_y): y = dp.arange(1000, usm_type = usm_type_y) z = 1.3 + x + y + 2 + z += x + z += 7.4 assert x.usm_type == usm_type_x assert y.usm_type == usm_type_y