diff --git a/dpctl/tensor/_elementwise_common.py b/dpctl/tensor/_elementwise_common.py index 46837ab1df..6677670e73 100644 --- a/dpctl/tensor/_elementwise_common.py +++ b/dpctl/tensor/_elementwise_common.py @@ -47,9 +47,33 @@ def __init__(self, name, result_type_resolver_fn, unary_dp_impl_fn, docs): self.unary_fn_ = unary_dp_impl_fn self.__doc__ = docs - def __call__(self, x, order="K"): + def __call__(self, x, out=None, order="K"): if not isinstance(x, dpt.usm_ndarray): raise TypeError(f"Expected dpctl.tensor.usm_ndarray, got {type(x)}") + + if out is not None: + if not isinstance(out, dpt.usm_ndarray): + raise TypeError( + f"output array must be of usm_ndarray type, got {type(out)}" + ) + + if out.shape != x.shape: + raise TypeError( + "The shape of input and output arrays are inconsistent." + f"Expected output shape is {x.shape}, got {out.shape}" + ) + + if ti._array_overlap(x, out): + raise TypeError("Input and output arrays have memory overlap") + + if ( + dpctl.utils.get_execution_queue((x.sycl_queue, out.sycl_queue)) + is None + ): + raise TypeError( + "Input and output allocation queues are not compatible" + ) + if order not in ["C", "F", "K", "A"]: order = "K" buf_dt, res_dt = _find_buf_dtype( @@ -59,17 +83,24 @@ def __call__(self, x, order="K"): raise RuntimeError exec_q = x.sycl_queue if buf_dt is None: - if order == "K": - r = _empty_like_orderK(x, res_dt) + if out is None: + if order == "K": + out = _empty_like_orderK(x, res_dt) + else: + if order == "A": + order = "F" if x.flags.f_contiguous else "C" + out = dpt.empty_like(x, dtype=res_dt, order=order) else: - if order == "A": - order = "F" if x.flags.f_contiguous else "C" - r = dpt.empty_like(x, dtype=res_dt, order=order) + if res_dt != out.dtype: + raise TypeError( + f"Output array of type {res_dt} is needed," + f" got {out.dtype}" + ) - ht, _ = self.unary_fn_(x, r, sycl_queue=exec_q) + ht, _ = self.unary_fn_(x, out, sycl_queue=exec_q) ht.wait() - return r + return out if order == "K": buf = _empty_like_orderK(x, buf_dt) else: @@ -80,16 +111,22 @@ def __call__(self, x, order="K"): ht_copy_ev, copy_ev = ti._copy_usm_ndarray_into_usm_ndarray( src=x, dst=buf, sycl_queue=exec_q ) - if order == "K": - r = _empty_like_orderK(buf, res_dt) + if out is None: + if order == "K": + out = _empty_like_orderK(buf, res_dt) + else: + out = dpt.empty_like(buf, dtype=res_dt, order=order) else: - r = dpt.empty_like(buf, dtype=res_dt, order=order) + if buf_dt != out.dtype: + raise TypeError( + f"Output array of type {buf_dt} is needed, got {out.dtype}" + ) - ht, _ = self.unary_fn_(buf, r, sycl_queue=exec_q, depends=[copy_ev]) + ht, _ = self.unary_fn_(buf, out, sycl_queue=exec_q, depends=[copy_ev]) ht_copy_ev.wait() ht.wait() - return r + return out def _get_queue_usm_type(o): @@ -281,7 +318,7 @@ def __str__(self): def __repr__(self): return f"" - def __call__(self, o1, o2, order="K"): + def __call__(self, o1, o2, out=None, order="K"): if order not in ["K", "C", "F", "A"]: order = "K" q1, o1_usm_type = _get_queue_usm_type(o1) @@ -358,6 +395,31 @@ def __call__(self, o1, o2, order="K"): "supported types according to the casting rule ''safe''." ) + if out is not None: + if not isinstance(out, dpt.usm_ndarray): + raise TypeError( + f"output array must be of usm_ndarray type, got {type(out)}" + ) + + if out.shape != res_shape: + raise TypeError( + "The shape of input and output arrays are inconsistent." + f"Expected output shape is {o1_shape}, got {out.shape}" + ) + + if ti._array_overlap(o1, out) or ti._array_overlap(o2, out): + raise TypeError("Input and output arrays have memory overlap") + + if ( + dpctl.utils.get_execution_queue( + (o1.sycl_queue, o2.sycl_queue, out.sycl_queue) + ) + is None + ): + raise TypeError( + "Input and output allocation queues are not compatible" + ) + if isinstance(o1, dpt.usm_ndarray): src1 = o1 else: @@ -368,37 +430,45 @@ def __call__(self, o1, o2, order="K"): src2 = dpt.asarray(o2, dtype=o2_dtype, sycl_queue=exec_q) if buf1_dt is None and buf2_dt is None: - if order == "K": - r = _empty_like_pair_orderK( - src1, src2, res_dt, res_usm_type, exec_q - ) - else: - if order == "A": - order = ( - "F" - if all( - arr.flags.f_contiguous - for arr in ( - src1, - src2, + if out is None: + if order == "K": + out = _empty_like_pair_orderK( + src1, src2, res_dt, res_usm_type, exec_q + ) + else: + if order == "A": + order = ( + "F" + if all( + arr.flags.f_contiguous + for arr in ( + src1, + src2, + ) ) + else "C" ) - else "C" + out = dpt.empty( + res_shape, + dtype=res_dt, + usm_type=res_usm_type, + sycl_queue=exec_q, + order=order, ) - r = dpt.empty( - res_shape, - dtype=res_dt, - usm_type=res_usm_type, - sycl_queue=exec_q, - order=order, - ) + else: + if res_dt != out.dtype: + raise TypeError( + f"Output array of type {res_dt} is needed," + f"got {out.dtype}" + ) + src1 = dpt.broadcast_to(src1, res_shape) src2 = dpt.broadcast_to(src2, res_shape) ht_, _ = self.binary_fn_( - src1=src1, src2=src2, dst=r, sycl_queue=exec_q + src1=src1, src2=src2, dst=out, sycl_queue=exec_q ) ht_.wait() - return r + return out elif buf1_dt is None: if order == "K": buf2 = _empty_like_orderK(src2, buf2_dt) @@ -409,30 +479,38 @@ def __call__(self, o1, o2, order="K"): ht_copy_ev, copy_ev = ti._copy_usm_ndarray_into_usm_ndarray( src=src2, dst=buf2, sycl_queue=exec_q ) - if order == "K": - r = _empty_like_pair_orderK( - src1, buf2, res_dt, res_usm_type, exec_q - ) + if out is None: + if order == "K": + out = _empty_like_pair_orderK( + src1, buf2, res_dt, res_usm_type, exec_q + ) + else: + out = dpt.empty( + res_shape, + dtype=res_dt, + usm_type=res_usm_type, + sycl_queue=exec_q, + order=order, + ) else: - r = dpt.empty( - res_shape, - dtype=res_dt, - usm_type=res_usm_type, - sycl_queue=exec_q, - order=order, - ) + if res_dt != out.dtype: + raise TypeError( + f"Output array of type {res_dt} is needed," + f"got {out.dtype}" + ) + src1 = dpt.broadcast_to(src1, res_shape) buf2 = dpt.broadcast_to(buf2, res_shape) ht_, _ = self.binary_fn_( src1=src1, src2=buf2, - dst=r, + dst=out, sycl_queue=exec_q, depends=[copy_ev], ) ht_copy_ev.wait() ht_.wait() - return r + return out elif buf2_dt is None: if order == "K": buf1 = _empty_like_orderK(src1, buf1_dt) @@ -443,30 +521,38 @@ def __call__(self, o1, o2, order="K"): ht_copy_ev, copy_ev = ti._copy_usm_ndarray_into_usm_ndarray( src=src1, dst=buf1, sycl_queue=exec_q ) - if order == "K": - r = _empty_like_pair_orderK( - buf1, src2, res_dt, res_usm_type, exec_q - ) + if out is None: + if order == "K": + out = _empty_like_pair_orderK( + buf1, src2, res_dt, res_usm_type, exec_q + ) + else: + out = dpt.empty( + res_shape, + dtype=res_dt, + usm_type=res_usm_type, + sycl_queue=exec_q, + order=order, + ) else: - r = dpt.empty( - res_shape, - dtype=res_dt, - usm_type=res_usm_type, - sycl_queue=exec_q, - order=order, - ) + if res_dt != out.dtype: + raise TypeError( + f"Output array of type {res_dt} is needed," + f"got {out.dtype}" + ) + buf1 = dpt.broadcast_to(buf1, res_shape) src2 = dpt.broadcast_to(src2, res_shape) ht_, _ = self.binary_fn_( src1=buf1, src2=src2, - dst=r, + dst=out, sycl_queue=exec_q, depends=[copy_ev], ) ht_copy_ev.wait() ht_.wait() - return r + return out if order in ["K", "A"]: if src1.flags.f_contiguous and src2.flags.f_contiguous: @@ -489,26 +575,33 @@ def __call__(self, o1, o2, order="K"): ht_copy2_ev, copy2_ev = ti._copy_usm_ndarray_into_usm_ndarray( src=src2, dst=buf2, sycl_queue=exec_q ) - if order == "K": - r = _empty_like_pair_orderK( - buf1, buf2, res_dt, res_usm_type, exec_q - ) + if out is None: + if order == "K": + out = _empty_like_pair_orderK( + buf1, buf2, res_dt, res_usm_type, exec_q + ) + else: + out = dpt.empty( + res_shape, + dtype=res_dt, + usm_type=res_usm_type, + sycl_queue=exec_q, + order=order, + ) else: - r = dpt.empty( - res_shape, - dtype=res_dt, - usm_type=res_usm_type, - sycl_queue=exec_q, - order=order, - ) + if res_dt != out.dtype: + raise TypeError( + f"Output array of type {res_dt} is needed, got {out.dtype}" + ) + buf1 = dpt.broadcast_to(buf1, res_shape) buf2 = dpt.broadcast_to(buf2, res_shape) ht_, _ = self.binary_fn_( src1=buf1, src2=buf2, - dst=r, + dst=out, sycl_queue=exec_q, depends=[copy1_ev, copy2_ev], ) dpctl.SyclEvent.wait_for([ht_copy1_ev, ht_copy2_ev, ht_]) - return r + return out diff --git a/dpctl/tests/elementwise/test_abs.py b/dpctl/tests/elementwise/test_abs.py index 275be0d573..d4aefaf76d 100644 --- a/dpctl/tests/elementwise/test_abs.py +++ b/dpctl/tests/elementwise/test_abs.py @@ -22,9 +22,17 @@ def test_abs_out_type(dtype): np.dtype("c16"): np.dtype("f8"), } assert dpt.abs(X).dtype == type_map[arg_dt] + + r = dpt.empty_like(X, dtype=type_map[arg_dt]) + dpt.abs(X, out=r) + assert np.allclose(dpt.asnumpy(r), dpt.asnumpy(dpt.abs(X))) else: assert dpt.abs(X).dtype == arg_dt + r = dpt.empty_like(X, dtype=arg_dt) + dpt.abs(X, out=r) + assert np.allclose(dpt.asnumpy(r), dpt.asnumpy(dpt.abs(X))) + @pytest.mark.parametrize("usm_type", _usm_types) def test_abs_usm_type(usm_type): diff --git a/dpctl/tests/elementwise/test_add.py b/dpctl/tests/elementwise/test_add.py index 81176f4f45..2b4ab8e3cb 100644 --- a/dpctl/tests/elementwise/test_add.py +++ b/dpctl/tests/elementwise/test_add.py @@ -2,10 +2,12 @@ import numpy as np import pytest +from numpy.testing import assert_raises_regex import dpctl import dpctl.tensor as dpt from dpctl.tests.helper import get_queue_or_skip, skip_if_dtype_not_supported +from dpctl.utils import ExecutionPlacementError from .utils import _all_dtypes, _compare_dtypes, _usm_types @@ -31,6 +33,10 @@ def test_add_dtype_matrix(op1_dtype, op2_dtype): assert (dpt.asnumpy(r) == np.full(r.shape, 2, dtype=r.dtype)).all() assert r.sycl_queue == ar1.sycl_queue + r2 = dpt.empty_like(ar1, dtype=r.dtype) + dpt.add(ar1, ar2, out=r2) + assert (dpt.asnumpy(r2) == np.full(r2.shape, 2, dtype=r2.dtype)).all() + ar3 = dpt.ones(sz, dtype=op1_dtype) ar4 = dpt.ones(2 * sz, dtype=op2_dtype) @@ -43,6 +49,10 @@ def test_add_dtype_matrix(op1_dtype, op2_dtype): assert r.shape == ar3.shape assert (dpt.asnumpy(r) == np.full(r.shape, 2, dtype=r.dtype)).all() + r2 = dpt.empty_like(ar1, dtype=r.dtype) + dpt.add(ar3[::-1], ar4[::2], out=r2) + assert (dpt.asnumpy(r2) == np.full(r2.shape, 2, dtype=r2.dtype)).all() + @pytest.mark.parametrize("op1_usm_type", _usm_types) @pytest.mark.parametrize("op2_usm_type", _usm_types) @@ -64,37 +74,49 @@ def test_add_usm_type_matrix(op1_usm_type, op2_usm_type): def test_add_order(): get_queue_or_skip() - ar1 = dpt.ones((20, 20), dtype="i4", order="C") - ar2 = dpt.ones((20, 20), dtype="i4", order="C") - r1 = dpt.add(ar1, ar2, order="C") - assert r1.flags.c_contiguous - r2 = dpt.add(ar1, ar2, order="F") - assert r2.flags.f_contiguous - r3 = dpt.add(ar1, ar2, order="A") - assert r3.flags.c_contiguous - r4 = dpt.add(ar1, ar2, order="K") - assert r4.flags.c_contiguous - - ar1 = dpt.ones((20, 20), dtype="i4", order="F") - ar2 = dpt.ones((20, 20), dtype="i4", order="F") - r1 = dpt.add(ar1, ar2, order="C") - assert r1.flags.c_contiguous - r2 = dpt.add(ar1, ar2, order="F") - assert r2.flags.f_contiguous - r3 = dpt.add(ar1, ar2, order="A") - assert r3.flags.f_contiguous - r4 = dpt.add(ar1, ar2, order="K") - assert r4.flags.f_contiguous - - ar1 = dpt.ones((40, 40), dtype="i4", order="C")[:20, ::-2] - ar2 = dpt.ones((40, 40), dtype="i4", order="C")[:20, ::-2] - r4 = dpt.add(ar1, ar2, order="K") - assert r4.strides == (20, -1) - - ar1 = dpt.ones((40, 40), dtype="i4", order="C")[:20, ::-2].mT - ar2 = dpt.ones((40, 40), dtype="i4", order="C")[:20, ::-2].mT - r4 = dpt.add(ar1, ar2, order="K") - assert r4.strides == (-1, 20) + test_shape = ( + 20, + 20, + ) + test_shape2 = tuple(2 * dim for dim in test_shape) + n = test_shape[-1] + + for dt1, dt2 in zip(["i4", "i4", "f4"], ["i4", "f4", "i4"]): + ar1 = dpt.ones(test_shape, dtype=dt1, order="C") + ar2 = dpt.ones(test_shape, dtype=dt2, order="C") + r1 = dpt.add(ar1, ar2, order="C") + assert r1.flags.c_contiguous + r2 = dpt.add(ar1, ar2, order="F") + assert r2.flags.f_contiguous + r3 = dpt.add(ar1, ar2, order="A") + assert r3.flags.c_contiguous + r4 = dpt.add(ar1, ar2, order="K") + assert r4.flags.c_contiguous + + ar1 = dpt.ones(test_shape, dtype=dt1, order="F") + ar2 = dpt.ones(test_shape, dtype=dt2, order="F") + r1 = dpt.add(ar1, ar2, order="C") + assert r1.flags.c_contiguous + r2 = dpt.add(ar1, ar2, order="F") + assert r2.flags.f_contiguous + r3 = dpt.add(ar1, ar2, order="A") + assert r3.flags.f_contiguous + r4 = dpt.add(ar1, ar2, order="K") + assert r4.flags.f_contiguous + + ar1 = dpt.ones(test_shape2, dtype=dt1, order="C")[:20, ::-2] + ar2 = dpt.ones(test_shape2, dtype=dt2, order="C")[:20, ::-2] + r4 = dpt.add(ar1, ar2, order="K") + assert r4.strides == (n, -1) + r5 = dpt.add(ar1, ar2, order="C") + assert r5.strides == (n, 1) + + ar1 = dpt.ones(test_shape2, dtype=dt1, order="C")[:20, ::-2].mT + ar2 = dpt.ones(test_shape2, dtype=dt2, order="C")[:20, ::-2].mT + r4 = dpt.add(ar1, ar2, order="K") + assert r4.strides == (-1, n) + r5 = dpt.add(ar1, ar2, order="C") + assert r5.strides == (n, 1) def test_add_broadcasting(): @@ -104,12 +126,27 @@ def test_add_broadcasting(): v = dpt.arange(5, dtype="i4") r = dpt.add(m, v) - assert (dpt.asnumpy(r) == np.arange(1, 6, dtype="i4")[np.newaxis, :]).all() r2 = dpt.add(v, m) assert (dpt.asnumpy(r2) == np.arange(1, 6, dtype="i4")[np.newaxis, :]).all() + r3 = dpt.empty_like(m) + dpt.add(m, v, out=r3) + assert (dpt.asnumpy(r3) == np.arange(1, 6, dtype="i4")[np.newaxis, :]).all() + + r4 = dpt.empty_like(m) + dpt.add(v, m, out=r4) + assert (dpt.asnumpy(r4) == np.arange(1, 6, dtype="i4")[np.newaxis, :]).all() + + +def test_add_broadcasting_error(): + get_queue_or_skip() + m = dpt.ones((10, 10), dtype="i4") + v = dpt.ones((3,), dtype="i4") + with pytest.raises(ValueError): + dpt.add(m, v) + @pytest.mark.parametrize("arr_dt", _all_dtypes) def test_add_python_scalar(arr_dt): @@ -165,3 +202,89 @@ def __sycl_usm_array_interface__(self): c = Canary() with pytest.raises(ValueError): dpt.add(a, c) + + +def test_add_errors(): + get_queue_or_skip() + try: + gpu_queue = dpctl.SyclQueue("gpu") + except dpctl.SyclQueueCreationError: + pytest.skip("SyclQueue('gpu') failed, skipping") + try: + cpu_queue = dpctl.SyclQueue("cpu") + except dpctl.SyclQueueCreationError: + pytest.skip("SyclQueue('cpu') failed, skipping") + + ar1 = dpt.ones(2, dtype="float32", sycl_queue=gpu_queue) + ar2 = dpt.ones_like(ar1, sycl_queue=gpu_queue) + y = dpt.empty_like(ar1, sycl_queue=cpu_queue) + assert_raises_regex( + TypeError, + "Input and output allocation queues are not compatible", + dpt.add, + ar1, + ar2, + y, + ) + + ar1 = dpt.ones(2, dtype="float32") + ar2 = dpt.ones_like(ar1, dtype="int32") + y = dpt.empty(3) + assert_raises_regex( + TypeError, + "The shape of input and output arrays are inconsistent", + dpt.add, + ar1, + ar2, + y, + ) + + ar1 = dpt.ones(2, dtype="float32") + ar2 = dpt.ones_like(ar1, dtype="int32") + y = ar1 + assert_raises_regex( + TypeError, + "Input and output arrays have memory overlap", + dpt.add, + ar1, + ar2, + y, + ) + + ar1 = np.ones(2, dtype="float32") + ar2 = np.ones_like(ar1, dtype="int32") + assert_raises_regex( + ExecutionPlacementError, + "Execution placement can not be unambiguously inferred.*", + dpt.add, + ar1, + ar2, + ) + + ar1 = dpt.ones(2, dtype="float32") + ar2 = dpt.ones_like(ar1, dtype="int32") + y = np.empty_like(ar1) + assert_raises_regex( + TypeError, + "output array must be of usm_ndarray type", + dpt.add, + ar1, + ar2, + y, + ) + + +@pytest.mark.parametrize("dtype", _all_dtypes) +def test_add_dtype_error( + dtype, +): + q = get_queue_or_skip() + skip_if_dtype_not_supported(dtype, q) + + ar1 = dpt.ones(5, dtype=dtype) + ar2 = dpt.ones_like(ar1, dtype="f4") + + y = dpt.zeros_like(ar1, dtype="int8") + assert_raises_regex( + TypeError, "Output array of type.*is needed", dpt.add, ar1, ar2, y + ) diff --git a/dpctl/tests/elementwise/test_cos.py b/dpctl/tests/elementwise/test_cos.py index 22588aea44..395fa60435 100644 --- a/dpctl/tests/elementwise/test_cos.py +++ b/dpctl/tests/elementwise/test_cos.py @@ -2,7 +2,9 @@ import numpy as np import pytest +from numpy.testing import assert_raises_regex +import dpctl import dpctl.tensor as dpt from dpctl.tests.helper import get_queue_or_skip, skip_if_dtype_not_supported @@ -19,6 +21,13 @@ def test_cos_out_type(dtype): expected_dtype = _map_to_device_dtype(expected_dtype, q.sycl_device) assert dpt.cos(X).dtype == expected_dtype + X = dpt.asarray(0, dtype=dtype, sycl_queue=q) + expected_dtype = np.cos(np.array(0, dtype=dtype)).dtype + expected_dtype = _map_to_device_dtype(expected_dtype, q.sycl_device) + Y = dpt.empty_like(X, dtype=expected_dtype) + dpt.cos(X, out=Y) + np.testing.assert_allclose(dpt.asnumpy(dpt.cos(X)), dpt.asnumpy(Y)) + @pytest.mark.parametrize("dtype", ["f2", "f4", "f8", "c8", "c16"]) def test_cos_output(dtype): @@ -38,6 +47,13 @@ def test_cos_output(dtype): dpt.asnumpy(Y), np.repeat(np.cos(Xnp), n_rep), atol=tol, rtol=tol ) + Z = dpt.empty_like(X, dtype=dtype) + dpt.cos(X, out=Z) + + np.testing.assert_allclose( + dpt.asnumpy(Z), np.repeat(np.cos(Xnp), n_rep), atol=tol, rtol=tol + ) + @pytest.mark.parametrize("usm_type", ["device", "shared", "host"]) def test_cos_usm_type(usm_type): @@ -85,3 +101,59 @@ def test_cos_order(dtype): np.testing.assert_allclose( dpt.asnumpy(Y), expected_Y, atol=tol, rtol=tol ) + + +def test_cos_errors(): + get_queue_or_skip() + try: + gpu_queue = dpctl.SyclQueue("gpu") + except dpctl.SyclQueueCreationError: + pytest.skip("SyclQueue('gpu') failed, skipping") + try: + cpu_queue = dpctl.SyclQueue("cpu") + except dpctl.SyclQueueCreationError: + pytest.skip("SyclQueue('cpu') failed, skipping") + + x = dpt.zeros(2, sycl_queue=gpu_queue) + y = dpt.empty_like(x, sycl_queue=cpu_queue) + assert_raises_regex( + TypeError, + "Input and output allocation queues are not compatible", + dpt.cos, + x, + y, + ) + + x = dpt.zeros(2) + y = dpt.empty(3) + assert_raises_regex( + TypeError, + "The shape of input and output arrays are inconsistent", + dpt.cos, + x, + y, + ) + + x = dpt.zeros(2) + y = x + assert_raises_regex( + TypeError, "Input and output arrays have memory overlap", dpt.cos, x, y + ) + + x = dpt.zeros(2, dtype="float32") + y = np.empty_like(x) + assert_raises_regex( + TypeError, "output array must be of usm_ndarray type", dpt.cos, x, y + ) + + +@pytest.mark.parametrize("dtype", _all_dtypes) +def test_cos_error_dtype(dtype): + q = get_queue_or_skip() + skip_if_dtype_not_supported(dtype, q) + + x = dpt.zeros(5, dtype=dtype) + y = dpt.empty_like(x, dtype="int16") + assert_raises_regex( + TypeError, "Output array of type.*is needed", dpt.cos, x, y + ) diff --git a/dpctl/tests/elementwise/test_isfinite.py b/dpctl/tests/elementwise/test_isfinite.py index 5cc9699cf8..92e585d217 100644 --- a/dpctl/tests/elementwise/test_isfinite.py +++ b/dpctl/tests/elementwise/test_isfinite.py @@ -41,6 +41,10 @@ def test_isfinite_complex(dtype): Y = dpt.asarray(Ynp, sycl_queue=q) assert np.array_equal(dpt.asnumpy(dpt.isfinite(Y)), np.isfinite(Ynp)) + r = dpt.empty_like(Y, dtype="bool") + dpt.isfinite(Y, out=r) + assert np.array_equal(dpt.asnumpy(r)[()], np.isfinite(Ynp)) + @pytest.mark.parametrize("dtype", ["f2", "f4", "f8"]) def test_isfinite_floats(dtype): @@ -56,6 +60,10 @@ def test_isfinite_floats(dtype): Y = dpt.asarray(Ynp, sycl_queue=q) assert np.array_equal(dpt.asnumpy(dpt.isfinite(Y)), np.isfinite(Ynp)) + r = dpt.empty_like(Y, dtype="bool") + dpt.isfinite(Y, out=r) + assert np.array_equal(dpt.asnumpy(r)[()], np.isfinite(Ynp)) + @pytest.mark.parametrize("dtype", _all_dtypes) def test_isfinite_order(dtype): diff --git a/dpctl/tests/elementwise/test_isnan.py b/dpctl/tests/elementwise/test_isnan.py index 8e983cb2dc..7545251bf2 100644 --- a/dpctl/tests/elementwise/test_isnan.py +++ b/dpctl/tests/elementwise/test_isnan.py @@ -41,6 +41,10 @@ def test_isnan_complex(dtype): Y = dpt.asarray(Ynp, sycl_queue=q) assert np.array_equal(dpt.asnumpy(dpt.isnan(Y)), np.isnan(Ynp)) + r = dpt.empty_like(Y, dtype="bool") + dpt.isnan(Y, out=r) + assert np.array_equal(dpt.asnumpy(r)[()], np.isnan(Ynp)) + @pytest.mark.parametrize("dtype", ["f2", "f4", "f8"]) def test_isnan_floats(dtype): @@ -56,6 +60,10 @@ def test_isnan_floats(dtype): Y = dpt.asarray(Ynp, sycl_queue=q) assert np.array_equal(dpt.asnumpy(dpt.isnan(Y)), np.isnan(Ynp)) + r = dpt.empty_like(Y, dtype="bool") + dpt.isnan(Y, out=r) + assert np.array_equal(dpt.asnumpy(r)[()], np.isnan(Ynp)) + @pytest.mark.parametrize("dtype", _all_dtypes) def test_isnan_order(dtype):