From e753e72236c1e79bf887323d3cc688fcea95a907 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Wed, 1 Mar 2023 12:37:38 -0600 Subject: [PATCH 1/3] dpnp.power() doesn't work properly with a scalar --- .../include/dpnp_gen_2arg_3type_tbl.hpp | 8 +- dpnp/backend/kernels/dpnp_krnl_elemwise.cpp | 59 +++-------- dpnp/dpnp_array.py | 11 ++- dpnp/dpnp_iface_mathematical.py | 82 +++++++++------ tests/skipped_tests.tbl | 1 - tests/skipped_tests_gpu.tbl | 1 - tests/test_mathematical.py | 99 +++++++++++++++---- tests/test_usm_type.py | 15 +++ .../cupy/math_tests/test_arithmetic.py | 2 +- 9 files changed, 177 insertions(+), 101 deletions(-) diff --git a/dpnp/backend/include/dpnp_gen_2arg_3type_tbl.hpp b/dpnp/backend/include/dpnp_gen_2arg_3type_tbl.hpp index e345c6eefea..0964f18df81 100644 --- a/dpnp/backend/include/dpnp_gen_2arg_3type_tbl.hpp +++ b/dpnp/backend/include/dpnp_gen_2arg_3type_tbl.hpp @@ -176,11 +176,11 @@ MACRO_2ARG_3TYPES_OP(dpnp_multiply_c, MACRO_UNPACK_TYPES(float, double, std::complex, std::complex)) MACRO_2ARG_3TYPES_OP(dpnp_power_c, - sycl::pow((double)input1_elem, (double)input2_elem), - nullptr, - std::false_type, + static_cast<_DataType_output>(std::pow(input1_elem, input2_elem)), + sycl::pow(x1, x2), + MACRO_UNPACK_TYPES(float, double), oneapi::mkl::vm::pow, - MACRO_UNPACK_TYPES(float, double)) + MACRO_UNPACK_TYPES(float, double, std::complex, std::complex)) MACRO_2ARG_3TYPES_OP(dpnp_subtract_c, input1_elem - input2_elem, diff --git a/dpnp/backend/kernels/dpnp_krnl_elemwise.cpp b/dpnp/backend/kernels/dpnp_krnl_elemwise.cpp index 5133473d393..c8b32fa9809 100644 --- a/dpnp/backend/kernels/dpnp_krnl_elemwise.cpp +++ b/dpnp/backend/kernels/dpnp_krnl_elemwise.cpp @@ -1247,28 +1247,34 @@ static void func_map_elemwise_2arg_3type_core(func_map_t& fmap) func_type_map_t::find_type, func_type_map_t::find_type>}), ...); + ((fmap[DPNPFuncName::DPNP_FN_DIVIDE_EXT][FT1][FTs] = + {get_divide_res_type(), + (void*)dpnp_divide_c_ext()>, + func_type_map_t::find_type, + func_type_map_t::find_type>, + get_divide_res_type(), + (void*)dpnp_divide_c_ext()>, + func_type_map_t::find_type, + func_type_map_t::find_type>}), + ...); ((fmap[DPNPFuncName::DPNP_FN_MULTIPLY_EXT][FT1][FTs] = {populate_func_types(), (void*)dpnp_multiply_c_ext()>, func_type_map_t::find_type, func_type_map_t::find_type>}), ...); + ((fmap[DPNPFuncName::DPNP_FN_POWER_EXT][FT1][FTs] = + {populate_func_types(), + (void*)dpnp_power_c_ext()>, + func_type_map_t::find_type, + func_type_map_t::find_type>}), + ...); ((fmap[DPNPFuncName::DPNP_FN_SUBTRACT_EXT][FT1][FTs] = {populate_func_types(), (void*)dpnp_subtract_c_ext()>, func_type_map_t::find_type, func_type_map_t::find_type>}), ...); - ((fmap[DPNPFuncName::DPNP_FN_DIVIDE_EXT][FT1][FTs] = - {get_divide_res_type(), - (void*)dpnp_divide_c_ext()>, - func_type_map_t::find_type, - func_type_map_t::find_type>, - get_divide_res_type(), - (void*)dpnp_divide_c_ext()>, - func_type_map_t::find_type, - func_type_map_t::find_type>}), - ...); } template @@ -1855,39 +1861,6 @@ static void func_map_init_elemwise_2arg_3type(func_map_t& fmap) fmap[DPNPFuncName::DPNP_FN_POWER][eft_DBL][eft_DBL] = {eft_DBL, (void*)dpnp_power_c_default}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_INT][eft_INT] = {eft_INT, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_INT][eft_LNG] = {eft_LNG, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_INT][eft_FLT] = {eft_DBL, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_INT][eft_DBL] = {eft_DBL, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_LNG][eft_INT] = {eft_LNG, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_LNG][eft_LNG] = {eft_LNG, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_LNG][eft_FLT] = {eft_DBL, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_LNG][eft_DBL] = {eft_DBL, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_FLT][eft_INT] = {eft_DBL, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_FLT][eft_LNG] = {eft_DBL, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_FLT][eft_FLT] = {eft_FLT, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_FLT][eft_DBL] = {eft_DBL, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_DBL][eft_INT] = {eft_DBL, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_DBL][eft_LNG] = {eft_DBL, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_DBL][eft_FLT] = {eft_DBL, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_POWER_EXT][eft_DBL][eft_DBL] = {eft_DBL, - (void*)dpnp_power_c_ext}; - fmap[DPNPFuncName::DPNP_FN_SUBTRACT][eft_INT][eft_INT] = { eft_INT, (void*)dpnp_subtract_c_default}; fmap[DPNPFuncName::DPNP_FN_SUBTRACT][eft_INT][eft_LNG] = { diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index c002d0db233..84db14667b9 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -211,7 +211,11 @@ def __int__(self): # '__invert__', # '__ior__', - # '__ipow__', + + def __ipow__(self, other): + dpnp.power(self, other, out=self) + return self + # '__irshift__', # '__isub__', # '__iter__', @@ -279,7 +283,10 @@ def __rmul__(self, other): return dpnp.multiply(other, self) # '__ror__', - # '__rpow__', + + def __rpow__(self, other): + return dpnp.power(other, self) + # '__rrshift__', # '__rshift__', diff --git a/dpnp/dpnp_iface_mathematical.py b/dpnp/dpnp_iface_mathematical.py index feff53288cf..8e531fc0478 100644 --- a/dpnp/dpnp_iface_mathematical.py +++ b/dpnp/dpnp_iface_mathematical.py @@ -45,6 +45,7 @@ import dpnp import numpy +import dpctl.tensor as dpt __all__ = [ @@ -1325,18 +1326,35 @@ def negative(x1, **kwargs): return call_origin(numpy.negative, x1, **kwargs) -def power(x1, x2, dtype=None, out=None, where=True, **kwargs): +def power(x1, + x2, + /, + out=None, + *, + where=True, + dtype=None, + subok=True, + **kwargs): """ First array elements raised to powers from second array, element-wise. + An integer type (of either negative or positive value, but not zero) + raised to a negative integer power will return an array of zeroes. + For full documentation refer to :obj:`numpy.power`. + Returns + ------- + y : dpnp.ndarray + The bases in `x1` raised to the exponents in `x2`. + Limitations ----------- - Parameters ``x1`` and ``x2`` are supported as either :obj:`dpnp.ndarray` or scalar. - Parameters ``dtype``, ``out`` and ``where`` are supported with their default values. + Parameters `x1` and `x2` are supported as either :class:`dpnp.ndarray` or scalar, + but not both (at least either `x1` or `x2` should be as :class:`dpnp.ndarray`). + Parameters `where`, `dtype` and `subok` are supported with their default values. Keyword arguments ``kwargs`` are currently unsupported. - Otherwise the functions will be executed sequentially on CPU. + Otherwise the function will be executed sequentially on CPU. Input array data types are limited by supported DPNP :ref:`Data types`. See Also @@ -1348,40 +1366,44 @@ def power(x1, x2, dtype=None, out=None, where=True, **kwargs): Example ------- - >>> import dpnp as np - >>> a = np.array([1, 2, 3, 4, 5]) - >>> b = np.array([2, 2, 2, 2, 2]) - >>> result = np.power(a, b) + >>> import dpnp as dp + >>> a = dp.array([1, 2, 3, 4, 5]) + >>> b = dp.array([2, 2, 2, 2, 2]) + >>> result = dp.power(a, b) >>> [x for x in result] [1, 4, 9, 16, 25] """ - x1_is_scalar = dpnp.isscalar(x1) - x2_is_scalar = dpnp.isscalar(x2) - x1_desc = dpnp.get_dpnp_descriptor(x1, copy_when_strides=False, copy_when_nondefault_queue=False) - x2_desc = dpnp.get_dpnp_descriptor(x2, copy_when_strides=False, copy_when_nondefault_queue=False) + if 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) - if x1_desc and x2_desc and not kwargs: - if not x1_desc and not x1_is_scalar: - pass - elif not x2_desc and not x2_is_scalar: - pass - elif x1_is_scalar and x2_is_scalar: - pass - elif x1_desc and x1_desc.ndim == 0: - pass - elif x2_desc and x2_desc.ndim == 0: - pass - elif dtype is not None: - pass - elif not where: - pass + 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 out is not None: + if not isinstance(out, (dpnp.ndarray, dpt.usm_ndarray)): + raise TypeError("return array must be of supported array type") + out_desc = dpnp.get_dpnp_descriptor(out, copy_when_nondefault_queue=False) else: - out_desc = dpnp.get_dpnp_descriptor(out, copy_when_nondefault_queue=False) if out is not None else None - return dpnp_power(x1_desc, x2_desc, dtype, out_desc, where).get_pyobj() + out_desc = None + + if x1_desc and x2_desc: + return dpnp_power(x1_desc, x2_desc, dtype=dtype, out=out_desc, where=where).get_pyobj() - return call_origin(numpy.power, x1, x2, dtype=dtype, out=out, where=where, **kwargs) + return call_origin(numpy.power, x1, x2, out=out, where=where, dtype=dtype, subok=subok, **kwargs) def prod(x1, axis=None, dtype=None, out=None, keepdims=False, initial=None, where=True): diff --git a/tests/skipped_tests.tbl b/tests/skipped_tests.tbl index d598ea2ca9f..57a7d393e09 100644 --- a/tests/skipped_tests.tbl +++ b/tests/skipped_tests.tbl @@ -761,7 +761,6 @@ tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticModf::test_m tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticRaisesWithNumpyInput_param_10_{name='remainder', nargs=2}::test_raises_with_numpy_input tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticRaisesWithNumpyInput_param_11_{name='mod', nargs=2}::test_raises_with_numpy_input tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticRaisesWithNumpyInput_param_1_{name='angle', nargs=1}::test_raises_with_numpy_input -tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticRaisesWithNumpyInput_param_5_{name='power', nargs=2}::test_raises_with_numpy_input tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticRaisesWithNumpyInput_param_8_{name='floor_divide', nargs=2}::test_raises_with_numpy_input tests/third_party/cupy/math_tests/test_explog.py::TestExplog::test_logaddexp diff --git a/tests/skipped_tests_gpu.tbl b/tests/skipped_tests_gpu.tbl index 3dedcff4af0..aebd577ed1a 100644 --- a/tests/skipped_tests_gpu.tbl +++ b/tests/skipped_tests_gpu.tbl @@ -976,7 +976,6 @@ tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticBinary2_para tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticRaisesWithNumpyInput_param_10_{name='remainder', nargs=2}::test_raises_with_numpy_input tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticRaisesWithNumpyInput_param_11_{name='mod', nargs=2}::test_raises_with_numpy_input tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticRaisesWithNumpyInput_param_1_{name='angle', nargs=1}::test_raises_with_numpy_input -tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticRaisesWithNumpyInput_param_5_{name='power', nargs=2}::test_raises_with_numpy_input tests/third_party/cupy/math_tests/test_arithmetic.py::TestArithmeticRaisesWithNumpyInput_param_8_{name='floor_divide', nargs=2}::test_raises_with_numpy_input tests/third_party/cupy/math_tests/test_explog.py::TestExplog::test_logaddexp diff --git a/tests/test_mathematical.py b/tests/test_mathematical.py index 78f62890833..a150af54a60 100644 --- a/tests/test_mathematical.py +++ b/tests/test_mathematical.py @@ -8,6 +8,7 @@ assert_allclose, assert_array_almost_equal, assert_array_equal, + assert_equal, assert_raises ) @@ -66,7 +67,7 @@ def test_diff(array): @pytest.mark.parametrize("dtype1", get_all_dtypes()) @pytest.mark.parametrize("dtype2", get_all_dtypes()) @pytest.mark.parametrize("func", - ['add', 'multiply', 'subtract', 'divide']) + ['add', 'divide', 'multiply', 'power', 'subtract']) @pytest.mark.parametrize("data", [[[1, 2], [3, 4]]], ids=['[[1, 2], [3, 4]]']) @@ -84,7 +85,7 @@ def test_op_multiple_dtypes(dtype1, func, dtype2, data): else: result = getattr(dpnp, func)(dpnp_a, dpnp_b) expected = getattr(numpy, func)(np_a, np_b) - assert_array_equal(result, expected) + assert_allclose(result, expected) @pytest.mark.parametrize("rhs", [[[1, 2, 3], [4, 5, 6]], [2.0, 1.5, 1.0], 3, 0.3]) @@ -116,7 +117,7 @@ def _test_mathematical(self, name, dtype, lhs, rhs): else: result = getattr(dpnp, name)(a_dpnp, b_dpnp) expected = getattr(numpy, name)(a_np, b_np) - assert_allclose(result, expected, atol=1e-4) + assert_allclose(result, expected, rtol=1e-6) @pytest.mark.parametrize("dtype", get_all_dtypes()) def test_add(self, dtype, lhs, rhs): @@ -170,8 +171,7 @@ def test_multiply(self, dtype, lhs, rhs): def test_remainder(self, dtype, lhs, rhs): self._test_mathematical('remainder', dtype, lhs, rhs) - @pytest.mark.usefixtures("allow_fall_back_on_numpy") - @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True, no_complex=True)) + @pytest.mark.parametrize("dtype", get_all_dtypes()) def test_power(self, dtype, lhs, rhs): self._test_mathematical('power', dtype, lhs, rhs) @@ -186,7 +186,7 @@ def test_subtract(self, dtype, lhs, rhs): ids=['bool', 'int', 'float']) @pytest.mark.parametrize("data_type", get_all_dtypes()) @pytest.mark.parametrize("func", - ['add', 'multiply', 'subtract', 'divide']) + ['add', 'divide', 'multiply', 'power', 'subtract']) @pytest.mark.parametrize("val", [0, 1, 5], ids=['0', '1', '5']) @@ -206,6 +206,9 @@ def test_op_with_scalar(array, val, func, data_type, val_type): dpnp_a = dpnp.array(array, dtype=data_type) val_ = val_type(val) + if func == 'power' and val_ == 0 and numpy.issubdtype(data_type, numpy.complexfloating): + pytest.skip("(0j ** 0) is different: (NaN + NaNj) in dpnp and (1 + 0j) in numpy") + if func == 'subtract' and val_type == bool and data_type == dpnp.bool: with pytest.raises(TypeError): result = getattr(dpnp, func)(dpnp_a, val_) @@ -275,6 +278,23 @@ def test_divide_scalar(shape, dtype): assert_allclose(result, expected) +@pytest.mark.parametrize("shape", + [(), (3, 2)], + ids=['()', '(3, 2)']) +@pytest.mark.parametrize("dtype", get_all_dtypes()) +def test_power_scalar(shape, dtype): + np_a = numpy.ones(shape, dtype=dtype) + dpnp_a = dpnp.ones(shape, dtype=dtype) + + result = 4.2 ** dpnp_a ** -1.3 + expected = 4.2 ** np_a ** -1.3 + assert_allclose(result, expected, rtol=1e-6) + + result **= dpnp_a + expected **= np_a + assert_allclose(result, expected, rtol=1e-6) + + @pytest.mark.usefixtures("allow_fall_back_on_numpy") @pytest.mark.parametrize("array", [[1, 2, 3, 4, 5], [1, 2, numpy.nan, 4, 5], @@ -314,12 +334,11 @@ def test_negative(data, dtype): assert_array_equal(result, expected) -@pytest.mark.usefixtures("allow_fall_back_on_numpy") -@pytest.mark.parametrize("val_type", get_all_dtypes(no_bool=True, no_complex=True, no_none=True)) -@pytest.mark.parametrize("data_type", get_all_dtypes(no_bool=True, no_complex=True)) +@pytest.mark.parametrize("val_type", get_all_dtypes(no_none=True)) +@pytest.mark.parametrize("data_type", get_all_dtypes()) @pytest.mark.parametrize("val", - [0, 1, 5], - ids=['0', '1', '5']) + [1.5, 1, 5], + ids=['1.5', '1', '5']) @pytest.mark.parametrize("array", [[[0, 0], [0, 0]], [[1, 2], [1, 2]], @@ -335,9 +354,10 @@ def test_power(array, val, data_type, val_type): np_a = numpy.array(array, dtype=data_type) dpnp_a = dpnp.array(array, dtype=data_type) val_ = val_type(val) + result = dpnp.power(dpnp_a, val_) expected = numpy.power(np_a, val_) - assert_array_equal(expected, result) + assert_allclose(expected, result, rtol=1e-6) class TestEdiff1d: @@ -622,13 +642,10 @@ def test_power(self): assert_array_equal(expected, result) - @pytest.mark.parametrize("dtype", - [numpy.float32, numpy.int64, numpy.int32], - ids=['numpy.float32', 'numpy.int64', 'numpy.int32']) + @pytest.mark.parametrize("dtype", get_all_dtypes(no_complex=True, no_none=True)) def test_invalid_dtype(self, dtype): - - dp_array1 = dpnp.arange(10, dtype=dpnp.float64) - dp_array2 = dpnp.arange(5, 15, dtype=dpnp.float64) + dp_array1 = dpnp.arange(10, dtype=dpnp.complex64) + dp_array2 = dpnp.arange(5, 15, dtype=dpnp.complex64) dp_out = dpnp.empty(10, dtype=dtype) with pytest.raises(ValueError): @@ -638,10 +655,54 @@ def test_invalid_dtype(self, dtype): [(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.power(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.power, a, 2, out) + assert_raises(TypeError, numpy.power, a.asnumpy(), 2, out) + + @pytest.mark.usefixtures("suppress_invalid_numpy_warnings") + def test_complex_values(self): + np_arr = numpy.array([0j, 1+1j, 0+2j, 1+2j, numpy.inf, numpy.nan]) + dp_arr = dpnp.array(np_arr) + func = lambda x: x ** 2 + + assert_allclose(func(np_arr), func(dp_arr).asnumpy()) + + @pytest.mark.parametrize("val", [0, 1], ids=['0', '1']) + @pytest.mark.parametrize("dtype", [dpnp.int32, dpnp.int64]) + def test_integer_power_of_0_or_1(self, val, dtype): + np_arr = numpy.arange(10, dtype=dtype) + dp_arr = dpnp.array(np_arr) + func = lambda x: 1 ** x + + assert_equal(func(np_arr), func(dp_arr)) + + @pytest.mark.parametrize("dtype", [dpnp.int32, dpnp.int64]) + def test_integer_to_negative_power(self, dtype): + ones = dpnp.ones(10, dtype=dtype) + a = dpnp.arange(2, 10, dtype=dtype) + b = dpnp.full(10, -2, dtype=dtype) + + assert_array_equal(ones ** (-2), ones) + assert_equal(a ** (-3), 0) # positive integer to negative integer power + assert_equal(b ** (-4), 0) # negative integer to negative integer power + + def test_float_to_inf(self): + a = numpy.array([1, 1, 2, 2, -2, -2, numpy.inf, -numpy.inf], dtype=numpy.float32) + b = numpy.array([numpy.inf, -numpy.inf, numpy.inf, -numpy.inf, + numpy.inf, -numpy.inf, numpy.inf, -numpy.inf], dtype=numpy.float32) + numpy_res = a ** b + dpnp_res = dpnp.array(a) ** dpnp.array(b) + + assert_allclose(numpy_res, dpnp_res.asnumpy()) diff --git a/tests/test_usm_type.py b/tests/test_usm_type.py index 326d0313c93..5fec346a000 100644 --- a/tests/test_usm_type.py +++ b/tests/test_usm_type.py @@ -63,6 +63,21 @@ def test_coerced_usm_types_divide(usm_type_x, usm_type_y): assert z.usm_type == du.get_coerced_usm_type([usm_type_x, usm_type_y]) +@pytest.mark.parametrize("usm_type_x", list_of_usm_types, ids=list_of_usm_types) +@pytest.mark.parametrize("usm_type_y", list_of_usm_types, ids=list_of_usm_types) +def test_coerced_usm_types_power(usm_type_x, usm_type_y): + x = dp.arange(70, usm_type = usm_type_x).reshape((7, 5, 2)) + y = dp.arange(70, usm_type = usm_type_y).reshape((7, 5, 2)) + + z = 2 ** x ** y ** 1.5 + z **= x + z **= 1.7 + + assert x.usm_type == usm_type_x + assert y.usm_type == usm_type_y + assert z.usm_type == du.get_coerced_usm_type([usm_type_x, usm_type_y]) + + @pytest.mark.parametrize( "func, args", [ diff --git a/tests/third_party/cupy/math_tests/test_arithmetic.py b/tests/third_party/cupy/math_tests/test_arithmetic.py index 027722d8bef..39dc3e10f72 100644 --- a/tests/third_party/cupy/math_tests/test_arithmetic.py +++ b/tests/third_party/cupy/math_tests/test_arithmetic.py @@ -153,7 +153,7 @@ def check_binary(self, xp): is_int_float = lambda _x, _y: numpy.issubdtype(_x, numpy.integer) and numpy.issubdtype(_y, numpy.floating) is_same_type = lambda _x, _y, _type: numpy.issubdtype(_x, _type) and numpy.issubdtype(_y, _type) - if self.name in ('add', 'multiply', 'subtract'): + if self.name in ('add', 'multiply', 'power', 'subtract'): if is_array_arg1 and is_array_arg2: # If both inputs are arrays where one is of floating type and another - integer, # NumPy will return an output array of always "float64" type, From e6957364f4830eee2533604a01569cf064f62212 Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 2 Mar 2023 04:01:13 -0600 Subject: [PATCH 2/3] skip tests with 0 value of complex128 on CPU --- tests/helper.py | 17 +++++++++++++++++ tests/test_mathematical.py | 32 +++++++++++++++++++++++++------- tests/test_sycl_queue.py | 8 +++++--- 3 files changed, 47 insertions(+), 10 deletions(-) diff --git a/tests/helper.py b/tests/helper.py index 17c62cecd28..bce21dcc29d 100644 --- a/tests/helper.py +++ b/tests/helper.py @@ -1,3 +1,5 @@ +from sys import platform + import dpctl import dpnp @@ -37,3 +39,18 @@ def get_all_dtypes(no_bool=False, if not no_none: dtypes.append(None) return dtypes + + +def is_cpu_device(device=None): + """ + Return True if a test is running on CPU device, False otherwise. + """ + dev = dpctl.select_default_device() if device is None else device + return dev.has_aspect_cpu + + +def is_win_platform(): + """ + Return True if a test is runing on Windows OS, False otherwise. + """ + return platform.startswith('win') diff --git a/tests/test_mathematical.py b/tests/test_mathematical.py index a150af54a60..e58e129c03b 100644 --- a/tests/test_mathematical.py +++ b/tests/test_mathematical.py @@ -1,5 +1,9 @@ import pytest -from .helper import get_all_dtypes +from .helper import ( + get_all_dtypes, + is_cpu_device, + is_win_platform +) import dpnp @@ -206,8 +210,12 @@ def test_op_with_scalar(array, val, func, data_type, val_type): dpnp_a = dpnp.array(array, dtype=data_type) val_ = val_type(val) - if func == 'power' and val_ == 0 and numpy.issubdtype(data_type, numpy.complexfloating): - pytest.skip("(0j ** 0) is different: (NaN + NaNj) in dpnp and (1 + 0j) in numpy") + if func == 'power': + if val_ == 0 and numpy.issubdtype(data_type, numpy.complexfloating): + pytest.skip("(0j ** 0) is different: (NaN + NaNj) in dpnp and (1 + 0j) in numpy") + elif is_cpu_device() and data_type == dpnp.complex128: + # TODO: discuss the bahavior with OneMKL team + pytest.skip("(0j ** 5) is different: (NaN + NaNj) in dpnp and (0j) in numpy") if func == 'subtract' and val_type == bool and data_type == dpnp.bool: with pytest.raises(TypeError): @@ -219,11 +227,11 @@ def test_op_with_scalar(array, val, func, data_type, val_type): else: result = getattr(dpnp, func)(dpnp_a, val_) expected = getattr(numpy, func)(np_a, val_) - assert_allclose(result, expected) + assert_allclose(result, expected, rtol=1e-6) result = getattr(dpnp, func)(val_, dpnp_a) expected = getattr(numpy, func)(val_, np_a) - assert_allclose(result, expected) + assert_allclose(result, expected, rtol=1e-6) @pytest.mark.parametrize("shape", @@ -355,6 +363,10 @@ def test_power(array, val, data_type, val_type): dpnp_a = dpnp.array(array, dtype=data_type) val_ = val_type(val) + if is_cpu_device() and dpnp.complex128 in (data_type, val_type): + # TODO: discuss the behavior with OneMKL team + pytest.skip("(0j ** 5) is different: (NaN + NaNj) in dpnp and (0j) in numpy") + result = dpnp.power(dpnp_a, val_) expected = numpy.power(np_a, val_) assert_allclose(expected, result, rtol=1e-6) @@ -673,11 +685,17 @@ def test_invalid_out(self, out): @pytest.mark.usefixtures("suppress_invalid_numpy_warnings") def test_complex_values(self): - np_arr = numpy.array([0j, 1+1j, 0+2j, 1+2j, numpy.inf, numpy.nan]) + np_arr = numpy.array([0j, 1+1j, 0+2j, 1+2j, numpy.nan, numpy.inf]) dp_arr = dpnp.array(np_arr) func = lambda x: x ** 2 - assert_allclose(func(np_arr), func(dp_arr).asnumpy()) + # Linux: ((inf + 0j) ** 2) == (Inf + NaNj) in dpnp and == (NaN + NaNj) in numpy + # Win: ((inf + 0j) ** 2) == (Inf + 0j) in dpnp and == (Inf + NaNj) in numpy + if is_win_platform(): + assert_equal(func(dp_arr)[5], numpy.inf) + else: + assert_equal(func(dp_arr)[5], (numpy.inf + 0j) * 1) + assert_allclose(func(np_arr)[:5], func(dp_arr).asnumpy()[:5], rtol=1e-6) @pytest.mark.parametrize("val", [0, 1], ids=['0', '1']) @pytest.mark.parametrize("dtype", [dpnp.int32, dpnp.int64]) diff --git a/tests/test_sycl_queue.py b/tests/test_sycl_queue.py index 2197dbe5414..ea36a0992b2 100644 --- a/tests/test_sycl_queue.py +++ b/tests/test_sycl_queue.py @@ -1,6 +1,8 @@ import pytest -from .helper import get_all_dtypes -import sys +from .helper import ( + get_all_dtypes, + is_win_platform +) import dpnp import dpctl @@ -204,7 +206,7 @@ def test_array_creation_follow_device(func, args, kwargs, device): valid_devices, ids=[device.filter_string for device in valid_devices]) def test_array_creation_cross_device(func, args, kwargs, device_x, device_y): - if func is 'linspace' and sys.platform.startswith('win'): + if func is 'linspace' and is_win_platform(): pytest.skip("CPU driver experiences an instability on Windows.") x_orig = numpy.array([1, 2, 3, 4]) From 4abccd84239e102e33682957595b52e4fc7cc5ce Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Thu, 2 Mar 2023 13:38:29 -0600 Subject: [PATCH 3/3] State support of :class: in descriptions --- dpnp/dpnp_iface_logic.py | 38 ++++++++++++++++----------------- dpnp/dpnp_iface_mathematical.py | 20 ++++++++--------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/dpnp/dpnp_iface_logic.py b/dpnp/dpnp_iface_logic.py index 8d3ba904628..685bb03972e 100644 --- a/dpnp/dpnp_iface_logic.py +++ b/dpnp/dpnp_iface_logic.py @@ -253,8 +253,8 @@ def equal(x1, Limitations ----------- - Parameters `x1` and `x2` are supported as either :class:`dpnp.ndarray` or scalar, - but not both (at least either `x1` or `x2` should be as :class:`dpnp.ndarray`). + 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. Otherwise the function will be executed sequentially on CPU. Input array data types are limited by supported DPNP :ref:`Data types`, @@ -323,8 +323,8 @@ def greater(x1, Limitations ----------- - Parameters `x1` and `x2` are supported as either :class:`dpnp.ndarray` or scalar, - but not both (at least either `x1` or `x2` should be as :class:`dpnp.ndarray`). + 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. Otherwise the function will be executed sequentially on CPU. Input array data types are limited by supported DPNP :ref:`Data types`, @@ -393,8 +393,8 @@ def greater_equal(x1, Limitations ----------- - Parameters `x1` and `x2` are supported as either :class:`dpnp.ndarray` or scalar, - but not both (at least either `x1` or `x2` should be as :class:`dpnp.ndarray`). + 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. Otherwise the function will be executed sequentially on CPU. Input array data types are limited by supported DPNP :ref:`Data types`, @@ -641,8 +641,8 @@ def less(x1, Limitations ----------- - Parameters `x1` and `x2` are supported as either :class:`dpnp.ndarray` or scalar, - but not both (at least either `x1` or `x2` should be as :class:`dpnp.ndarray`). + 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. Otherwise the function will be executed sequentially on CPU. Input array data types are limited by supported DPNP :ref:`Data types`, @@ -711,8 +711,8 @@ def less_equal(x1, Limitations ----------- - Parameters `x1` and `x2` are supported as either :class:`dpnp.ndarray` or scalar, - but not both (at least either `x1` or `x2` should be as :class:`dpnp.ndarray`). + 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. Otherwise the function will be executed sequentially on CPU. Input array data types are limited by supported DPNP :ref:`Data types`, @@ -781,8 +781,8 @@ def logical_and(x1, Limitations ----------- - Parameters `x1` and `x2` are supported as either :class:`dpnp.ndarray` or scalar, - but not both (at least either `x1` or `x2` should be as :class:`dpnp.ndarray`). + 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. Otherwise the function will be executed sequentially on CPU. Input array data types are limited by supported DPNP :ref:`Data types`, @@ -850,7 +850,7 @@ def logical_not(x, Limitations ----------- - Parameters `x` is only supported as :class:`dpnp.ndarray`. + Parameters `x` is only supported as either :class:`dpnp.ndarray` or :class:`dpctl.tensor.usm_ndarray`. Parameters `out`, `where`, `dtype` and `subok` are supported with their default values. Otherwise the function will be executed sequentially on CPU. Input array data type is limited by supported DPNP :ref:`Data types`, @@ -907,8 +907,8 @@ def logical_or(x1, Limitations ----------- - Parameters `x1` and `x2` are supported as either :class:`dpnp.ndarray` or scalar, - but not both (at least either `x1` or `x2` should be as :class:`dpnp.ndarray`). + 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. Otherwise the function will be executed sequentially on CPU. Input array data types are limited by supported DPNP :ref:`Data types`, @@ -976,8 +976,8 @@ def logical_xor(x1, Limitations ----------- - Parameters `x1` and `x2` are supported as either :class:`dpnp.ndarray` or scalar, - but not both (at least either `x1` or `x2` should be as :class:`dpnp.ndarray`). + 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. Otherwise the function will be executed sequentially on CPU. Input array data types are limited by supported DPNP :ref:`Data types`, @@ -1045,8 +1045,8 @@ def not_equal(x1, Limitations ----------- - Parameters `x1` and `x2` are supported as either :class:`dpnp.ndarray` or scalar, - but not both (at least either `x1` or `x2` should be as :class:`dpnp.ndarray`). + 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. Otherwise the function will be executed sequentially on CPU. Input array data types are limited by supported DPNP :ref:`Data types`, diff --git a/dpnp/dpnp_iface_mathematical.py b/dpnp/dpnp_iface_mathematical.py index 8e531fc0478..45f3b50accc 100644 --- a/dpnp/dpnp_iface_mathematical.py +++ b/dpnp/dpnp_iface_mathematical.py @@ -176,8 +176,8 @@ def add(x1, Limitations ----------- - Parameters `x1` and `x2` are supported as either :class:`dpnp.ndarray` or scalar, - but not both (at least either `x1` or `x2` should be as :class:`dpnp.ndarray`). + 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. Keyword arguments ``kwargs`` are currently unsupported. Otherwise the function will be executed sequentially on CPU. @@ -566,8 +566,8 @@ def divide(x1, Limitations ----------- - Parameters `x1` and `x2` are supported as either :class:`dpnp.ndarray` or scalar, - but not both (at least either `x1` or `x2` should be as :class:`dpnp.ndarray`). + 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. Keyword arguments ``kwargs`` are currently unsupported. Otherwise the function will be executed sequentially on CPU. @@ -1118,8 +1118,8 @@ def multiply(x1, Limitations ----------- - Parameters `x1` and `x2` are supported as either :class:`dpnp.ndarray` or scalar, - but not both (at least either `x1` or `x2` should be as :class:`dpnp.ndarray`). + 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. Keyword arguments ``kwargs`` are currently unsupported. Otherwise the functions will be executed sequentially on CPU. @@ -1350,8 +1350,8 @@ def power(x1, Limitations ----------- - Parameters `x1` and `x2` are supported as either :class:`dpnp.ndarray` or scalar, - but not both (at least either `x1` or `x2` should be as :class:`dpnp.ndarray`). + 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 `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. @@ -1574,8 +1574,8 @@ def subtract(x1, Limitations ----------- - Parameters `x1` and `x2` are supported as either :class:`dpnp.ndarray` or scalar, - but not both (at least either `x1` or `x2` should be as :class:`dpnp.ndarray`). + 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. Keyword arguments ``kwargs`` are currently unsupported. Otherwise the function will be executed sequentially on CPU.