diff --git a/numba_dpex/core/typing/dpnpdecl.py b/numba_dpex/core/typing/dpnpdecl.py index 355be22179..6856f8ea1f 100644 --- a/numba_dpex/core/typing/dpnpdecl.py +++ b/numba_dpex/core/typing/dpnpdecl.py @@ -179,12 +179,16 @@ def install_operations(cls): "ldexp", "spacing", "isnat", + "cbrt", ] ) -# A list of ufuncs that are in fact aliases of other ufuncs. They need to insert -# the resolve method, but not register the ufunc itself -_aliases = set(["bitwise_not", "mod", "abs"]) +# TODO: A list of ufuncs that are in fact aliases of other ufuncs. They need +# to insert the resolve method, but not register the ufunc itself. +# In a meantime let's just register them as user functions: +# TODO: for some reason it affects "mod", but does not affect "bitwise_not" and +# "abs". May be mod is not an alias? +_aliases = {"bitwise_not", "abs"} all_ufuncs = sum( [ diff --git a/numba_dpex/tests/_helper.py b/numba_dpex/tests/_helper.py index e5382eac69..3eca02ae06 100644 --- a/numba_dpex/tests/_helper.py +++ b/numba_dpex/tests/_helper.py @@ -5,6 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 import contextlib +import inspect import shutil from functools import cache @@ -12,8 +13,7 @@ import dpnp import pytest -from numba_dpex import dpjit, numba_sem_version -from numba_dpex.core import config +from numba_dpex import config, dpjit, numba_sem_version @cache @@ -179,6 +179,14 @@ def get_complex_dtypes(device=None): return dtypes +def get_int_dtypes(device=None): + """ + Build a list of integer types supported by DPNP based on device capabilities. + """ + + return [dpnp.int32, dpnp.int64] + + def get_float_dtypes(no_float16=True, device=None): """ Build a list of floating types supported by DPNP based on device capabilities. @@ -227,7 +235,7 @@ def get_all_dtypes( # add integer types if not no_int: - dtypes.extend([dpnp.int32, dpnp.int64]) + dtypes.extend(get_int_dtypes(device=dev)) # add floating types if not no_float: @@ -276,3 +284,20 @@ def skip_if_dtype_not_supported(dt, q_or_dev): pytest.skip( f"{dev.name} does not support half precision floating point type" ) + + +def num_required_arguments(func): + """Returns number of required arguments of the functions. Does not work + with kwargs arguments.""" + if func == dpnp.true_divide: + func = dpnp.divide + + sig = inspect.signature(func) + params = sig.parameters + required_args = [ + p + for p in params + if params[p].default == inspect._empty and p != "kwargs" + ] + + return len(required_args) diff --git a/numba_dpex/tests/dpjit_tests/parfors/test_builtin_ops.py b/numba_dpex/tests/dpjit_tests/parfors/test_builtin_ops.py index 8777d0e241..7ad73476bb 100644 --- a/numba_dpex/tests/dpjit_tests/parfors/test_builtin_ops.py +++ b/numba_dpex/tests/dpjit_tests/parfors/test_builtin_ops.py @@ -2,68 +2,349 @@ # # SPDX-License-Identifier: Apache-2.0 +from itertools import product + import dpctl import dpnp import numpy import pytest from numba_dpex import dpjit -from numba_dpex.tests._helper import get_all_dtypes +from numba_dpex.tests._helper import ( + get_float_dtypes, + get_int_dtypes, + has_opencl_gpu, + num_required_arguments, +) def parfor_add(a, b): + "Same as a + b." return a + b -def parfor_sub(a, b): - return a - b +def parfor_and_(a, b): + "Same as a & b." + return a & b -def parfor_mult(a, b): - return a * b +def parfor_floordiv(a, b): + "Same as a // b." + return a // b -def parfor_divide(a, b): - return a / b +def parfor_inv(a): + "Same as ~a." + return ~a + + +def parfor_lshift(a, b): + "Same as a << b." + return a << b -def parfor_modulus(a, b): +def parfor_mod(a, b): + "Same as a % b." return a % b -def parfor_exponent(a, b): +def parfor_mul(a, b): + "Same as a * b." + return a * b + + +def parfor_matmul(a, b): + "Same as a @ b." + return a @ b + + +def parfor_neg(a): + "Same as -a." + return -a + + +def parfor_or_(a, b): + "Same as a | b." + return a | b + + +def parfor_pos(a): + "Same as +a." + return +a + + +def parfor_pow(a, b): + "Same as a ** b." return a**b +def parfor_rshift(a, b): + "Same as a >> b." + return a >> b + + +def parfor_sub(a, b): + "Same as a - b." + return a - b + + +def parfor_truediv(a, b): + "Same as a / b." + return a / b + + +def parfor_xor(a, b): + "Same as a ^ b." + return a ^ b + + +# Inplace operations +def parfor_iadd(a, b): + "Same as a += b." + a += b + return a + + +def parfor_iand(a, b): + "Same as a &= b." + a &= b + return a + + +def parfor_ifloordiv(a, b): + "Same as a //= b." + a //= b + return a + + +def parfor_ilshift(a, b): + "Same as a <<= b." + a <<= b + return a + + +def parfor_imod(a, b): + "Same as a %= b." + a %= b + return a + + +def parfor_imul(a, b): + "Same as a *= b." + a *= b + return a + + +def parfor_imatmul(a, b): + "Same as a @= b." + a @= b + return a + + +def parfor_ior(a, b): + "Same as a |= b." + a |= b + return a + + +def parfor_ipow(a, b): + "Same as a **= b." + a **= b + return a + + +def parfor_irshift(a, b): + "Same as a >>= b." + a >>= b + return a + + +def parfor_isub(a, b): + "Same as a -= b." + a -= b + return a + + +def parfor_itruediv(a, b): + "Same as a /= b." + a /= b + return a + + +def parfor_ixor(a, b): + "Same as a ^= b." + a ^= b + return a + + +# Comparison operations + + +def parfor_lt(a, b): + "Same as a < b." + return a < b + + +def parfor_le(a, b): + "Same as a <= b." + return a <= b + + +def parfor_eq(a, b): + "Same as a == b." + return a == b + + +def parfor_ne(a, b): + "Same as a != b." + return a != b + + +def parfor_ge(a, b): + "Same as a >= b." + return a >= b + + +def parfor_gt(a, b): + "Same as a > b." + return a > b + + +# logical + + +def parfor_not_(a): + "Same as not a." + return not a + + shapes = [100, (25, 4)] -dtypes = get_all_dtypes( - no_bool=True, no_float16=True, no_none=True, no_complex=True -) +int_dtypes = set(get_int_dtypes()) +float_dtypes = set(get_float_dtypes()) usm_types = ["device", "host", "shared"] -funcs = [ + +funcs = { parfor_add, + parfor_and_, + parfor_floordiv, + parfor_inv, + parfor_lshift, + parfor_mod, + parfor_mul, + parfor_matmul, + parfor_neg, + parfor_or_, + parfor_pos, + parfor_pow, + parfor_rshift, parfor_sub, - parfor_mult, - parfor_divide, - parfor_modulus, - parfor_exponent, -] + parfor_truediv, + parfor_xor, +} -# TODO: fails for integer because it is being cast to float64 internally? -if dpnp.float64 not in dtypes: - funcs.remove(parfor_divide) - funcs.remove(parfor_exponent) +inplace_funcs = { + parfor_iand, + parfor_iadd, + parfor_iand, + parfor_ifloordiv, + parfor_ilshift, + parfor_imod, + parfor_imul, + parfor_imatmul, + parfor_ior, + parfor_ipow, + parfor_irshift, + parfor_isub, + parfor_itruediv, + parfor_ixor, +} +comparison_funcs = { + parfor_lt, + parfor_le, + parfor_eq, + parfor_ne, + parfor_ge, + parfor_gt, +} -def parfor_floor(a, b): - return a // b +all_funcs = funcs | inplace_funcs | comparison_funcs + +not_float_funcs = { + parfor_inv, + parfor_and_, + parfor_floordiv, + parfor_lshift, + parfor_mod, + parfor_or_, + parfor_rshift, + parfor_xor, + parfor_iand, + parfor_ifloordiv, + parfor_ilshift, + parfor_imod, + parfor_ior, + parfor_irshift, + parfor_ixor, +} + +not_int_funcs = { + parfor_itruediv, +} + +not_bool_funcs = { + parfor_neg, + parfor_pos, + parfor_sub, + parfor_lshift, + parfor_rshift, + parfor_mod, + parfor_pow, + parfor_floordiv, + parfor_isub, + parfor_ilshift, + parfor_irshift, + parfor_imod, + parfor_ipow, + parfor_ifloordiv, +} | not_int_funcs + +unsupported_funcs = { + parfor_matmul, + parfor_imatmul, + parfor_not_, # not supported by numpy +} + + +if has_opencl_gpu(): + unsupported_funcs |= {parfor_ipow} + + +func_dtypes = ( + set(product(all_funcs - not_float_funcs, float_dtypes)) + | set(product(all_funcs - not_int_funcs, int_dtypes)) + | set(product((all_funcs | {parfor_not_}) - not_bool_funcs, {dpnp.bool})) +) @pytest.mark.parametrize("shape", shapes) -@pytest.mark.parametrize("dtype", dtypes) @pytest.mark.parametrize("usm_type", usm_types) -@pytest.mark.parametrize("func", funcs) +@pytest.mark.parametrize( + "func, dtype", + sorted(list(func_dtypes), key=lambda a: (str(a[0]), str(a[1]))), +) def test_built_in_operators(shape, dtype, usm_type, func): + if func in unsupported_funcs: + pytest.xfail(reason="not supported") + + if dpnp.float64 not in float_dtypes and func in [ + parfor_truediv, + parfor_pow, + ]: + pytest.xfail( + f"{func} does not work on without double precision support" + ) + queue = dpctl.SyclQueue() a = dpnp.zeros( shape=shape, dtype=dtype, usm_type=usm_type, sycl_queue=queue @@ -71,7 +352,10 @@ def test_built_in_operators(shape, dtype, usm_type, func): b = dpnp.ones(shape=shape, dtype=dtype, usm_type=usm_type, sycl_queue=queue) try: op = dpjit(func) - c = op(a, b) + if num_required_arguments(func) == 1: + c = op(a) + else: + c = op(a, b) del op except Exception: pytest.fail("Failed to compile.") @@ -81,13 +365,16 @@ def test_built_in_operators(shape, dtype, usm_type, func): else: assert c.shape == shape - if func != parfor_divide: + if func not in {parfor_truediv} | comparison_funcs: assert c.dtype == dtype assert c.usm_type == usm_type assert c.sycl_device.filter_string == queue.sycl_device.filter_string - expected = dpnp.asnumpy(func(a, b)) + if num_required_arguments(func) == 1: + expected = dpnp.asnumpy(func(a)) + else: + expected = dpnp.asnumpy(func(a, b)) nc = dpnp.asnumpy(c) numpy.allclose(nc, expected) diff --git a/numba_dpex/tests/dpjit_tests/parfors/test_dpnp_bitwise_ops.py b/numba_dpex/tests/dpjit_tests/parfors/test_dpnp_bitwise_ops.py deleted file mode 100644 index ab10c83e2f..0000000000 --- a/numba_dpex/tests/dpjit_tests/parfors/test_dpnp_bitwise_ops.py +++ /dev/null @@ -1,92 +0,0 @@ -# SPDX-FileCopyrightText: 2020 - 2024 Intel Corporation -# -# SPDX-License-Identifier: Apache-2.0 - -import dpctl.tensor as dpt -import dpnp -import numpy as np -import pytest - -from numba_dpex import dpjit - -list_of_binary_ops = [ - "bitwise_and", - "bitwise_or", - "bitwise_xor", - "left_shift", - "right_shift", -] - - -@pytest.fixture(params=list_of_binary_ops) -def binary_op(request): - return request.param - - -list_of_unary_ops = [ - "bitwise_not", - "invert", -] - - -@pytest.fixture(params=list_of_unary_ops) -def unary_op(request): - return request.param - - -list_of_dtypes = [ - dpnp.int32, - dpnp.int64, -] - - -@pytest.fixture(params=list_of_dtypes) -def input_arrays(request): - # The size of input and out arrays to be used - N = 2048 - a = dpnp.array(dpnp.random.random(N), request.param) - b = dpnp.array(dpnp.random.random(N), request.param) - return a, b - - -def test_binary_ops(binary_op, input_arrays): - a, b = input_arrays - binop = getattr(dpnp, binary_op) - actual = dpnp.empty(shape=a.shape, dtype=a.dtype) - expected = dpnp.empty(shape=a.shape, dtype=a.dtype) - - @dpjit - def f(a, b): - return binop(a, b) - - actual = f(a, b) - - expected = binop(a, b) - np.testing.assert_allclose( - dpt.asnumpy(actual._array_obj), - dpt.asnumpy(expected._array_obj), - rtol=1e-5, - atol=0, - ) - - -def test_unary_ops(unary_op, input_arrays): - a = input_arrays[0] - uop = getattr(dpnp, unary_op) - actual = np.empty(shape=a.shape, dtype=a.dtype) - expected = np.empty(shape=a.shape, dtype=a.dtype) - - @dpjit - def f(a): - return uop(a) - - actual = f(a) - - expected = uop(a) - - np.testing.assert_allclose( - dpt.asnumpy(actual._array_obj), - dpt.asnumpy(expected._array_obj), - rtol=1e-5, - atol=0, - ) diff --git a/numba_dpex/tests/dpjit_tests/parfors/test_dpnp_logic_ops.py b/numba_dpex/tests/dpjit_tests/parfors/test_dpnp_logic_ops.py deleted file mode 100644 index 9263cc83b4..0000000000 --- a/numba_dpex/tests/dpjit_tests/parfors/test_dpnp_logic_ops.py +++ /dev/null @@ -1,99 +0,0 @@ -# SPDX-FileCopyrightText: 2020 - 2024 Intel Corporation -# -# SPDX-License-Identifier: Apache-2.0 - -import dpctl -import dpctl.tensor as dpt -import dpnp -import numpy as np -import pytest - -from numba_dpex import dpjit -from numba_dpex.tests._helper import get_all_dtypes - -""" Following cases, dpnp raises NotImplementedError""" - -list_of_binary_ops = [ - "greater", - "greater_equal", - "less", - "less_equal", - "not_equal", - "equal", - "logical_and", - "logical_or", - "logical_xor", -] - - -@pytest.fixture(params=list_of_binary_ops) -def binary_op(request): - return request.param - - -list_of_unary_ops = [ - "isinf", - "isfinite", - "isnan", -] - - -@pytest.fixture(params=list_of_unary_ops) -def unary_op(request): - return request.param - - -list_of_dtypes = get_all_dtypes( - no_bool=True, no_float16=True, no_none=True, no_complex=True -) - - -@pytest.fixture(params=list_of_dtypes) -def input_arrays(request): - # The size of input and out arrays to be used - N = 2048 - a = dpnp.array(dpnp.random.random(N), request.param) - b = dpnp.array(dpnp.random.random(N), request.param) - return a, b - - -def test_binary_ops(binary_op, input_arrays): - a, b = input_arrays - binop = getattr(dpnp, binary_op) - actual = dpnp.empty(shape=a.shape, dtype=a.dtype) - expected = dpnp.empty(shape=a.shape, dtype=a.dtype) - - @dpjit - def f(a, b): - return binop(a, b) - - actual = f(a, b) - - expected = binop(a, b) - np.testing.assert_allclose( - dpt.asnumpy(actual._array_obj), - dpt.asnumpy(expected._array_obj), - rtol=1e-5, - atol=0, - ) - - -def test_unary_ops(unary_op, input_arrays): - a = input_arrays[0] - uop = getattr(dpnp, unary_op) - actual = dpnp.empty(shape=a.shape, dtype=a.dtype) - expected = dpnp.empty(shape=a.shape, dtype=a.dtype) - - @dpjit - def f(a): - return uop(a) - - actual = f(a) - - expected = uop(a) - np.testing.assert_allclose( - dpt.asnumpy(actual._array_obj), - dpt.asnumpy(expected._array_obj), - rtol=1e-5, - atol=0, - ) diff --git a/numba_dpex/tests/dpjit_tests/parfors/test_dpnp_transcedental_functions.py b/numba_dpex/tests/dpjit_tests/parfors/test_dpnp_transcedental_functions.py deleted file mode 100644 index 71b6e02d30..0000000000 --- a/numba_dpex/tests/dpjit_tests/parfors/test_dpnp_transcedental_functions.py +++ /dev/null @@ -1,126 +0,0 @@ -# SPDX-FileCopyrightText: 2020 - 2024 Intel Corporation -# -# SPDX-License-Identifier: Apache-2.0 - -import dpctl -import dpctl.tensor as dpt -import dpnp -import numpy as np -import pytest - -from numba_dpex import dpjit -from numba_dpex.tests._helper import get_all_dtypes, is_gen12 - -"""dpnp raise error on : mod, abs and remainder(float32)""" -list_of_binary_ops = [ - "add", - "subtract", - "multiply", - "divide", - "power", - # "remainder", - # "mod", - "hypot", - "maximum", - "minimum", - "fmax", - "fmin", -] - - -@pytest.fixture(params=list_of_binary_ops) -def binary_op(request): - return request.param - - -list_of_unary_ops = [ - "negative", - # "abs", - "absolute", - "fabs", - "sign", - "conj", - "exp", - "exp2", - "log", - "log2", - "log10", - "expm1", - "log1p", - "sqrt", - "square", - "reciprocal", - "conjugate", - "floor", - "ceil", - "trunc", - "erf", -] - - -@pytest.fixture(params=list_of_unary_ops) -def unary_op(request): - return request.param - - -list_of_dtypes = get_all_dtypes( - no_bool=True, no_int=True, no_float16=True, no_none=True, no_complex=True -) - - -@pytest.fixture(params=list_of_dtypes) -def input_arrays(request): - # The size of input and out arrays to be used - N = 2048 - a = dpnp.array(dpnp.random.random(N), request.param) - b = dpnp.array(dpnp.random.random(N), request.param) - return a, b - - -def test_binary_ops(binary_op, input_arrays): - a, b = input_arrays - binop = getattr(dpnp, binary_op) - actual = dpnp.empty(shape=a.shape, dtype=a.dtype) - expected = dpnp.empty(shape=a.shape, dtype=a.dtype) - - @dpjit - def f(a, b): - return binop(a, b) - - actual = f(a, b) - - expected = binop(a, b) - - np.testing.assert_allclose( - dpt.asnumpy(actual._array_obj), - dpt.asnumpy(expected._array_obj), - rtol=1e-5, - atol=0, - ) - - -def test_unary_ops(unary_op, input_arrays): - skip_ops = ["abs", "sign", "log", "log2", "log10", "expm1"] - device = dpctl.SyclDevice() - if unary_op in skip_ops and is_gen12(device.filter_string): - pytest.skip() - - a = input_arrays[0] - uop = getattr(dpnp, unary_op) - actual = dpnp.empty(shape=a.shape, dtype=a.dtype) - expected = dpnp.empty(shape=a.shape, dtype=a.dtype) - - @dpjit - def f(a): - return uop(a) - - actual = f(a) - - expected = uop(a) - - np.testing.assert_allclose( - dpt.asnumpy(actual._array_obj), - dpt.asnumpy(expected._array_obj), - rtol=1e-5, - atol=0, - ) diff --git a/numba_dpex/tests/dpjit_tests/parfors/test_dpnp_trigonometric_functions.py b/numba_dpex/tests/dpjit_tests/parfors/test_dpnp_trigonometric_functions.py deleted file mode 100644 index f5fcc1aa8d..0000000000 --- a/numba_dpex/tests/dpjit_tests/parfors/test_dpnp_trigonometric_functions.py +++ /dev/null @@ -1,91 +0,0 @@ -# SPDX-FileCopyrightText: 2020 - 2024 Intel Corporation -# -# SPDX-License-Identifier: Apache-2.0 - -import dpctl -import dpctl.tensor as dpt -import dpnp -import numpy as np -import pytest - -from numba_dpex import dpjit -from numba_dpex.tests._helper import get_all_dtypes, is_gen12 - -list_of_trig_ops = [ - "sin", - "cos", - "tan", - "arcsin", - "arccos", - "arctan", - "arctan2", - "sinh", - "cosh", - "tanh", - "arcsinh", - "arccosh", - "arctanh", - "deg2rad", - "rad2deg", - "degrees", - "radians", -] - -list_of_dtypes = get_all_dtypes( - no_bool=True, no_int=True, no_float16=True, no_none=True, no_complex=True -) - -# TODO: fails for float32 because it uses cast to float64 internally? -if dpnp.float64 not in list_of_dtypes: - list_of_trig_ops.remove("arctan2") - - -@pytest.fixture(params=list_of_trig_ops) -def trig_op(request): - return request.param - - -@pytest.fixture(params=list_of_dtypes) -def input_arrays(request): - # The size of input and out arrays to be used - N = 2048 - - a = dpnp.array(dpnp.random.random(N), request.param) - b = dpnp.array(dpnp.random.random(N), request.param) - return a, b - - -def test_trigonometric_fn(trig_op, input_arrays): - filter_str = dpctl.SyclDevice().filter_string - # FIXME: Why does archcosh fail on Gen12 discrete graphics card? - if trig_op == "arccosh" and is_gen12(filter_str): - pytest.skip() - - a, b = input_arrays - trig_fn = getattr(dpnp, trig_op) - actual = dpnp.empty(shape=a.shape, dtype=a.dtype) - expected = dpnp.empty(shape=a.shape, dtype=a.dtype) - - if trig_op == "arctan2": - - @dpjit - def f(a, b): - return trig_fn(a, b) - - actual = f(a, b) - expected = trig_fn(a, b) - else: - - @dpjit - def f(a): - return trig_fn(a) - - actual = f(a) - expected = trig_fn(a) - - np.testing.assert_allclose( - dpt.asnumpy(actual._array_obj), - dpt.asnumpy(expected._array_obj), - rtol=1e-5, - atol=0, - ) diff --git a/numba_dpex/tests/dpjit_tests/parfors/test_dpnp_ufuncs.py b/numba_dpex/tests/dpjit_tests/parfors/test_dpnp_ufuncs.py new file mode 100644 index 0000000000..eb0c3856cc --- /dev/null +++ b/numba_dpex/tests/dpjit_tests/parfors/test_dpnp_ufuncs.py @@ -0,0 +1,180 @@ +# SPDX-FileCopyrightText: 2020 - 2024 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +from itertools import product + +import dpctl +import dpctl.tensor as dpt +import dpnp +import numpy as np +import pytest + +from numba_dpex import dpjit +from numba_dpex.core.typing.dpnpdecl import ( + _bit_twiddling_functions, + _comparison_functions, + _floating_functions, + _logic_functions, + _math_operations, + _trigonometric_functions, + _unsupported, +) +from numba_dpex.tests._helper import ( + get_float_dtypes, + get_int_dtypes, + has_opencl_gpu, + is_gen12, + num_required_arguments, +) + +int_ops = { + "gcd", + "lcm", + "mod", + "divmod", +} | set(_bit_twiddling_functions) + + +float_ops = { + "fmod", + "reciprocal", +} + +all_ufuncs = set( + _math_operations + + _trigonometric_functions + + _bit_twiddling_functions + + _comparison_functions + + _floating_functions + + _logic_functions +) - { + "frexp" # there is no dpnp.frexp +} + +unary_ops = { + op for op in all_ufuncs if num_required_arguments(getattr(dpnp, op)) == 1 +} | {"frexp"} + +binary_ops = { + op for op in all_ufuncs if num_required_arguments(getattr(dpnp, op)) == 2 +} + +if has_opencl_gpu(): + float_ops |= { + "copysign", + "divide", + "hypot", + "power", + "true_divide", + "arccos", + "arccosh", + "arcsin", + "arcsinh", + "arctan", + "arctanh", + "ceil", + "cos", + "cosh", + "deg2rad", + "degrees", + "erf", + "exp", + "exp2", + "expm1", + "fabs", + "floor", + "log", + "log10", + "log1p", + "log2", + "rad2deg", + "radians", + "sin", + "sinh", + "sqrt", + "tan", + "tanh", + "trunc", + } + +int_dtypes = get_int_dtypes() +float_dtypes = get_float_dtypes(no_float16=True) + +unary_op_dtypes = set(product(unary_ops - int_ops, float_dtypes)) | set( + product(unary_ops - float_ops, int_dtypes) +) +binary_op_dtypes = set(product(binary_ops - int_ops, float_dtypes)) | set( + product(binary_ops - float_ops, int_dtypes) +) + +N = 1024 + + +@pytest.mark.parametrize( + "binary_op, dtype", + sorted(list(binary_op_dtypes), key=lambda a: (a[0], str(a[1]))), +) +def test_binary_ops(binary_op, dtype): + # TODO: fails for float32 because it uses cast to float64 internally? + if binary_op == "arctan2" and dpnp.float64 not in float_dtypes: + pytest.xfail("arctan2 requires float64 support") + + a = dpnp.array(dpnp.random.random(N), dtype) + b = dpnp.array(dpnp.random.random(N), dtype) + + binop = getattr(dpnp, binary_op) + actual = dpnp.empty(shape=a.shape, dtype=a.dtype) + expected = dpnp.empty(shape=a.shape, dtype=a.dtype) + + if binary_op in _unsupported: + pytest.xfail(reason="not supported") + + @dpjit + def f(a, b): + return binop(a, b) + + actual = f(a, b) + + expected = binop(a, b) + + np.testing.assert_allclose( + dpt.asnumpy(actual._array_obj), + dpt.asnumpy(expected._array_obj), + rtol=1e-5, + atol=0, + ) + + +@pytest.mark.parametrize( + "unary_op, dtype", + sorted(list(unary_op_dtypes), key=lambda a: (a[0], str(a[1]))), +) +def test_unary_ops(unary_op, dtype): + if unary_op in _unsupported: + pytest.xfail(reason="not supported") + + xfail_ops = ["sign", "log", "log2", "log10", "expm1", "arccosh"] + if unary_op in xfail_ops and is_gen12(dpctl.SyclDevice().filter_string): + pytest.xfail(f"{unary_op} does not work on gen12") + + a = dpnp.array(dpnp.random.random(N), dtype) + + uop = getattr(dpnp, unary_op) + actual = dpnp.empty(shape=a.shape, dtype=a.dtype) + expected = dpnp.empty(shape=a.shape, dtype=a.dtype) + + @dpjit + def f(a): + return uop(a) + + actual = f(a) + + expected = uop(a) + + np.testing.assert_allclose( + dpt.asnumpy(actual._array_obj), + dpt.asnumpy(expected._array_obj), + rtol=1e-5, + atol=0, + )