From 54f5cd6c1d12799e5a8cc7e4f4b729aea9b90d05 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Thu, 25 Sep 2025 11:29:24 -0700 Subject: [PATCH 01/11] Implement dpnp.isin --- dpnp/__init__.py | 2 + dpnp/dpnp_iface_logic.py | 89 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/dpnp/__init__.py b/dpnp/__init__.py index 8a558fa00fe..a17524b7d1f 100644 --- a/dpnp/__init__.py +++ b/dpnp/__init__.py @@ -344,6 +344,7 @@ iscomplexobj, isfinite, isfortran, + isin, isinf, isnan, isneginf, @@ -835,6 +836,7 @@ "iscomplexobj", "isfinite", "isfortran", + "isin", "isinf", "isnan", "isneginf", diff --git a/dpnp/dpnp_iface_logic.py b/dpnp/dpnp_iface_logic.py index 8e0d19ac7d8..48c2ed513fa 100644 --- a/dpnp/dpnp_iface_logic.py +++ b/dpnp/dpnp_iface_logic.py @@ -1166,6 +1166,95 @@ def isfortran(a): return a.flags.fnc +def isin(element, test_elements, assume_unique=False, invert=False): + """ + Calculates ``element in test_elements``, broadcasting over `element` only. + Returns a boolean array of the same shape as `element` that is True + where an element of `element` is in `test_elements` and False otherwise. + + Parameters + ---------- + element : {array_like, dpnp.ndarray, usm_ndarray} + Input array. + test_elements : {array_like, dpnp.ndarray, usm_ndarray} + The values against which to test each value of `element`. + This argument is flattened if it is an array or array_like. + See notes for behavior with non-array-like parameters. + assume_unique : bool, optional + Ignored + invert : bool, optional + If True, the values in the returned array are inverted, as if + calculating `element not in test_elements`. Default is False. + ``dpnp.isin(a, b, invert=True)`` is equivalent to (but faster + than) ``dpnp.invert(dpnp.isin(a, b))``. + + + Returns + ------- + isin : dpnp.ndarray of bool dtype + Has the same shape as `element`. The values `element[isin]` + are in `test_elements`. + + + Examples + -------- + >>> import dpnp as np + >>> element = 2*np.arange(4).reshape((2, 2)) + >>> element + array([[0, 2], + [4, 6]]) + >>> test_elements = [1, 2, 4, 8] + >>> mask = np.isin(element, test_elements) + >>> mask + array([[False, True], + [ True, False]]) + >>> element[mask] + array([2, 4]) + + The indices of the matched values can be obtained with `nonzero`: + + >>> np.nonzero(mask) + (array([0, 1]), array([1, 0])) + + The test can also be inverted: + + >>> mask = np.isin(element, test_elements, invert=True) + >>> mask + array([[ True, False], + [False, True]]) + >>> element[mask] + array([0, 6]) + + """ + + dpnp.check_supported_arrays_type(element, test_elements, scalar_type=True) + if dpnp.isscalar(element): + usm_element = dpt.asarray( + element, + sycl_queue=test_elements.sycl_queue, + usm_type=test_elements.usm_type, + ) + usm_test = dpnp.get_usm_ndarray(test_elements) + elif dpnp.isscalar(test_elements): + usm_test = dpt.asarray( + test_elements, + sycl_queue=element.sycl_queue, + usm_type=element.usm_type, + ) + usm_element = dpnp.get_usm_ndarray(element) + else: + usm_element = dpnp.get_usm_ndarray(element) + usm_test = dpnp.get_usm_ndarray(test_elements) + return dpnp.get_result_array( + dpt.isin( + usm_element, + usm_test, + assume_unique=assume_unique, + invert=invert, + ) + ) + + _ISINF_DOCSTRING = """ Tests each element :math:`x_i` of the input array `x` to determine if equal to positive or negative infinity. From d9b4112031cf33f7207b38a3197a0cad9956e6f1 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Thu, 25 Sep 2025 13:48:38 -0700 Subject: [PATCH 02/11] unskip cupy isin tests --- dpnp/tests/third_party/cupy/logic_tests/test_truth.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dpnp/tests/third_party/cupy/logic_tests/test_truth.py b/dpnp/tests/third_party/cupy/logic_tests/test_truth.py index db9c61426f4..0ba35397210 100644 --- a/dpnp/tests/third_party/cupy/logic_tests/test_truth.py +++ b/dpnp/tests/third_party/cupy/logic_tests/test_truth.py @@ -89,7 +89,6 @@ def test_with_out(self, xp, dtype): return out -@pytest.mark.skip("isin() is not supported yet") @testing.parameterize( *testing.product( { From dc665fbfb46bef57371b434fb6f3d0b22559479a Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 19 Nov 2025 18:52:58 -0800 Subject: [PATCH 03/11] address review comments --- dpnp/dpnp_iface_logic.py | 45 +++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/dpnp/dpnp_iface_logic.py b/dpnp/dpnp_iface_logic.py index 48c2ed513fa..0110d9312ab 100644 --- a/dpnp/dpnp_iface_logic.py +++ b/dpnp/dpnp_iface_logic.py @@ -1169,24 +1169,27 @@ def isfortran(a): def isin(element, test_elements, assume_unique=False, invert=False): """ Calculates ``element in test_elements``, broadcasting over `element` only. - Returns a boolean array of the same shape as `element` that is True - where an element of `element` is in `test_elements` and False otherwise. + Returns a boolean array of the same shape as `element` that is ``True`` + where an element of `element` is in `test_elements` and ``False`` + otherwise. + + For full documentation refer to :obj:`numpy.isin`. Parameters ---------- - element : {array_like, dpnp.ndarray, usm_ndarray} + element : {dpnp.ndarray, usm_ndarray, scalar} Input array. - test_elements : {array_like, dpnp.ndarray, usm_ndarray} + test_elements : {dpnp.ndarray, usm_ndarray, scalar} The values against which to test each value of `element`. - This argument is flattened if it is an array or array_like. - See notes for behavior with non-array-like parameters. + This argument is flattened if it is an array. assume_unique : bool, optional Ignored invert : bool, optional - If True, the values in the returned array are inverted, as if - calculating `element not in test_elements`. Default is False. + If ``True``, the values in the returned array are inverted, as if + calculating `element not in test_elements`. ``dpnp.isin(a, b, invert=True)`` is equivalent to (but faster than) ``dpnp.invert(dpnp.isin(a, b))``. + Default: ``False``. Returns @@ -1228,28 +1231,18 @@ def isin(element, test_elements, assume_unique=False, invert=False): """ dpnp.check_supported_arrays_type(element, test_elements, scalar_type=True) - if dpnp.isscalar(element): - usm_element = dpt.asarray( - element, - sycl_queue=test_elements.sycl_queue, - usm_type=test_elements.usm_type, - ) - usm_test = dpnp.get_usm_ndarray(test_elements) - elif dpnp.isscalar(test_elements): - usm_test = dpt.asarray( - test_elements, - sycl_queue=element.sycl_queue, - usm_type=element.usm_type, - ) - usm_element = dpnp.get_usm_ndarray(element) - else: - usm_element = dpnp.get_usm_ndarray(element) - usm_test = dpnp.get_usm_ndarray(test_elements) + usm_element = dpnp.as_usm_ndarray( + element, usm_type=element.usm_type, sycl_queue=element.sycl_queue + ) + usm_test = dpnp.as_usm_ndarray( + test_elements, + usm_type=test_elements.usm_type, + sycl_queue=test_elements.sycl_queue, + ) return dpnp.get_result_array( dpt.isin( usm_element, usm_test, - assume_unique=assume_unique, invert=invert, ) ) From aca0ca54d863e434b1c28b4bc4c62c98b460d02b Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 19 Nov 2025 18:54:10 -0800 Subject: [PATCH 04/11] add to `assume_unique` documentation --- dpnp/dpnp_iface_logic.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dpnp/dpnp_iface_logic.py b/dpnp/dpnp_iface_logic.py index 0110d9312ab..27270751346 100644 --- a/dpnp/dpnp_iface_logic.py +++ b/dpnp/dpnp_iface_logic.py @@ -1183,7 +1183,9 @@ def isin(element, test_elements, assume_unique=False, invert=False): The values against which to test each value of `element`. This argument is flattened if it is an array. assume_unique : bool, optional - Ignored + Ignored, as no performance benefit is gained by assuming the + input arrays are unique. Included for compatibility with NumPy. + Default: ``False``. invert : bool, optional If ``True``, the values in the returned array are inverted, as if calculating `element not in test_elements`. From 5ba0a4cdeaf3ff9424726e672eb64db79d2733ec Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 19 Nov 2025 19:03:58 -0800 Subject: [PATCH 05/11] add isin tests --- dpnp/tests/test_logic.py | 97 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/dpnp/tests/test_logic.py b/dpnp/tests/test_logic.py index efcdf2b0be6..ab5c849ebfb 100644 --- a/dpnp/tests/test_logic.py +++ b/dpnp/tests/test_logic.py @@ -795,3 +795,100 @@ def test_array_equal_nan(a): result = dpnp.array_equal(dpnp.array(a), dpnp.array(b), equal_nan=True) expected = numpy.array_equal(a, b, equal_nan=True) assert_equal(result, expected) + + +@pytest.mark.parametrize( + "a", + [ + numpy.array([1, 2, 3, 4]), + numpy.array([[1, 2], [3, 4]]), + ], +) +@pytest.mark.parametrize( + "b", + [ + numpy.array([2, 4, 6]), + numpy.array([[1, 3], [5, 7]]), + ], +) +def test_isin_basic(a, b): + dp_a = dpnp.array(a) + dp_b = dpnp.array(b) + + expected = numpy.isin(a, b) + result = dpnp.isin(dp_a, dp_b) + assert_equal(result, expected) + + +@pytest.mark.parametrize("dtype", get_all_dtypes()) +def test_isin_dtype(dtype): + a = numpy.array([1, 2, 3, 4], dtype=dtype) + b = numpy.array([2, 4], dtype=dtype) + + dp_a = dpnp.array(a, dtype=dtype) + dp_b = dpnp.array(b, dtype=dtype) + + expected = numpy.isin(a, b) + result = dpnp.isin(dp_a, dp_b) + assert_equal(result, expected) + + +@pytest.mark.parametrize("sh_a, sh_b", [((3, 1), (1, 4)), ((2, 3, 1), (1, 1))]) +def test_isin_broadcast(sh_a, sh_b): + a = numpy.arange(numpy.prod(sh_a)).reshape(sh_a) + b = numpy.arange(numpy.prod(sh_b)).reshape(sh_b) + + dp_a = dpnp.array(a) + dp_b = dpnp.array(b) + + expected = numpy.isin(a, b) + result = dpnp.isin(dp_a, dp_b) + assert_equal(result, expected) + + +def test_isin_scalar_elements(): + a = numpy.array([1, 2, 3]) + b = 2 + + dp_a = dpnp.array(a) + dp_b = dpnp.array(b) + + expected = numpy.isin(a, b) + result = dpnp.isin(dp_a, dp_b) + assert_equal(result, expected) + + +def test_isin_scalar_test_elements(): + a = 2 + b = numpy.array([1, 2, 3]) + + dp_a = dpnp.array(a) + dp_b = dpnp.array(b) + + expected = numpy.isin(a, b) + result = dpnp.isin(dp_a, dp_b) + assert_equal(result, expected) + + +def test_isin_empty(): + a = numpy.array([], dtype=int) + b = numpy.array([1, 2, 3]) + + dp_a = dpnp.array(a) + dp_b = dpnp.array(b) + + expected = numpy.isin(a, b) + result = dpnp.isin(dp_a, dp_b) + assert_equal(result, expected) + + +def test_isin_errors(): + a = dpnp.arange(5) + b = dpnp.arange(3) + + # unsupported type for elements or test_elements + with pytest.raises(TypeError): + dpnp.isin(dict(), b) + + with pytest.raises(TypeError): + dpnp.isin(a, dict()) From b957ce2957a7ea18e97950142b177e05a1edf0e9 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Wed, 19 Nov 2025 19:16:44 -0800 Subject: [PATCH 06/11] disable pylint for isin unused `assume_unique` argument --- dpnp/dpnp_iface_logic.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dpnp/dpnp_iface_logic.py b/dpnp/dpnp_iface_logic.py index 27270751346..2f22742c8ef 100644 --- a/dpnp/dpnp_iface_logic.py +++ b/dpnp/dpnp_iface_logic.py @@ -1166,7 +1166,12 @@ def isfortran(a): return a.flags.fnc -def isin(element, test_elements, assume_unique=False, invert=False): +def isin( + element, + test_elements, + assume_unique=False, # pylint: disable=unused-argument + invert=False, +): """ Calculates ``element in test_elements``, broadcasting over `element` only. Returns a boolean array of the same shape as `element` that is ``True`` From ff7bb0e36419c057327d168f4eb216d21246c2d3 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Thu, 20 Nov 2025 23:16:24 -0800 Subject: [PATCH 07/11] address review comments --- dpnp/dpnp_iface_logic.py | 45 +++++++++--- dpnp/tests/test_logic.py | 149 +++++++++++++++++++-------------------- 2 files changed, 108 insertions(+), 86 deletions(-) diff --git a/dpnp/dpnp_iface_logic.py b/dpnp/dpnp_iface_logic.py index 2f22742c8ef..3570923a27f 100644 --- a/dpnp/dpnp_iface_logic.py +++ b/dpnp/dpnp_iface_logic.py @@ -1171,6 +1171,8 @@ def isin( test_elements, assume_unique=False, # pylint: disable=unused-argument invert=False, + *, + kind=None, # pylint: disable=unused-argument ): """ Calculates ``element in test_elements``, broadcasting over `element` only. @@ -1190,14 +1192,20 @@ def isin( assume_unique : bool, optional Ignored, as no performance benefit is gained by assuming the input arrays are unique. Included for compatibility with NumPy. + Default: ``False``. invert : bool, optional If ``True``, the values in the returned array are inverted, as if - calculating `element not in test_elements`. + calculating ``element not in test_elements``. ``dpnp.isin(a, b, invert=True)`` is equivalent to (but faster than) ``dpnp.invert(dpnp.isin(a, b))``. + Default: ``False``. + kind : {None, "sort"}, optional + Ignored, as the only algorithm implemented is ``"sort"``. Included for + compatibility with NumPy. + Default: ``None``. Returns ------- @@ -1205,7 +1213,6 @@ def isin( Has the same shape as `element`. The values `element[isin]` are in `test_elements`. - Examples -------- >>> import dpnp as np @@ -1238,14 +1245,32 @@ def isin( """ dpnp.check_supported_arrays_type(element, test_elements, scalar_type=True) - usm_element = dpnp.as_usm_ndarray( - element, usm_type=element.usm_type, sycl_queue=element.sycl_queue - ) - usm_test = dpnp.as_usm_ndarray( - test_elements, - usm_type=test_elements.usm_type, - sycl_queue=test_elements.sycl_queue, - ) + if dpnp.isscalar(element): + usm_element = dpnp.as_usm_ndarray( + element, + usm_type=test_elements.usm_type, + sycl_queue=test_elements.sycl_queue, + ) + usm_test = dpnp.get_usm_ndarray(test_elements) + elif dpnp.isscalar(test_elements): + usm_test = dpnp.as_usm_ndarray( + test_elements, + usm_type=element.usm_type, + sycl_queue=element.sycl_queue, + ) + usm_element = dpnp.get_usm_ndarray(element) + else: + if ( + dpu.get_execution_queue( + (element.sycl_queue, test_elements.sycl_queue) + ) + is None + ): + raise dpu.ExecutionPlacementError( + "Input arrays have incompatible allocation queues" + ) + usm_element = dpnp.get_usm_ndarray(element) + usm_test = dpnp.get_usm_ndarray(test_elements) return dpnp.get_result_array( dpt.isin( usm_element, diff --git a/dpnp/tests/test_logic.py b/dpnp/tests/test_logic.py index ab5c849ebfb..0fcb63f89c2 100644 --- a/dpnp/tests/test_logic.py +++ b/dpnp/tests/test_logic.py @@ -797,98 +797,95 @@ def test_array_equal_nan(a): assert_equal(result, expected) -@pytest.mark.parametrize( - "a", - [ - numpy.array([1, 2, 3, 4]), - numpy.array([[1, 2], [3, 4]]), - ], -) -@pytest.mark.parametrize( - "b", - [ - numpy.array([2, 4, 6]), - numpy.array([[1, 3], [5, 7]]), - ], -) -def test_isin_basic(a, b): - dp_a = dpnp.array(a) - dp_b = dpnp.array(b) - - expected = numpy.isin(a, b) - result = dpnp.isin(dp_a, dp_b) - assert_equal(result, expected) - - -@pytest.mark.parametrize("dtype", get_all_dtypes()) -def test_isin_dtype(dtype): - a = numpy.array([1, 2, 3, 4], dtype=dtype) - b = numpy.array([2, 4], dtype=dtype) - - dp_a = dpnp.array(a, dtype=dtype) - dp_b = dpnp.array(b, dtype=dtype) - - expected = numpy.isin(a, b) - result = dpnp.isin(dp_a, dp_b) - assert_equal(result, expected) - +class TestIsin: + @pytest.mark.parametrize( + "a", + [ + numpy.array([1, 2, 3, 4]), + numpy.array([[1, 2], [3, 4]]), + ], + ) + @pytest.mark.parametrize( + "b", + [ + numpy.array([2, 4, 6]), + numpy.array([[1, 3], [5, 7]]), + ], + ) + def test_isin_basic(self, a, b): + dp_a = dpnp.array(a) + dp_b = dpnp.array(b) -@pytest.mark.parametrize("sh_a, sh_b", [((3, 1), (1, 4)), ((2, 3, 1), (1, 1))]) -def test_isin_broadcast(sh_a, sh_b): - a = numpy.arange(numpy.prod(sh_a)).reshape(sh_a) - b = numpy.arange(numpy.prod(sh_b)).reshape(sh_b) + expected = numpy.isin(a, b) + result = dpnp.isin(dp_a, dp_b) + assert_equal(result, expected) - dp_a = dpnp.array(a) - dp_b = dpnp.array(b) + @pytest.mark.parametrize("dtype", get_all_dtypes(no_none=True)) + def test_isin_dtype(self, dtype): + a = numpy.array([1, 2, 3, 4], dtype=dtype) + b = numpy.array([2, 4], dtype=dtype) - expected = numpy.isin(a, b) - result = dpnp.isin(dp_a, dp_b) - assert_equal(result, expected) + dp_a = dpnp.array(a, dtype=dtype) + dp_b = dpnp.array(b, dtype=dtype) + expected = numpy.isin(a, b) + result = dpnp.isin(dp_a, dp_b) + assert_equal(result, expected) -def test_isin_scalar_elements(): - a = numpy.array([1, 2, 3]) - b = 2 + @pytest.mark.parametrize( + "sh_a, sh_b", [((3, 1), (1, 4)), ((2, 3, 1), (1, 1))] + ) + def test_isin_broadcast(self, sh_a, sh_b): + a = numpy.arange(numpy.prod(sh_a)).reshape(sh_a) + b = numpy.arange(numpy.prod(sh_b)).reshape(sh_b) - dp_a = dpnp.array(a) - dp_b = dpnp.array(b) + dp_a = dpnp.array(a) + dp_b = dpnp.array(b) - expected = numpy.isin(a, b) - result = dpnp.isin(dp_a, dp_b) - assert_equal(result, expected) + expected = numpy.isin(a, b) + result = dpnp.isin(dp_a, dp_b) + assert_equal(result, expected) + def test_isin_scalar_elements(self): + a = numpy.array([1, 2, 3]) + b = 2 -def test_isin_scalar_test_elements(): - a = 2 - b = numpy.array([1, 2, 3]) + dp_a = dpnp.array(a) + dp_b = dpnp.array(b) - dp_a = dpnp.array(a) - dp_b = dpnp.array(b) + expected = numpy.isin(a, b) + result = dpnp.isin(dp_a, dp_b) + assert_equal(result, expected) - expected = numpy.isin(a, b) - result = dpnp.isin(dp_a, dp_b) - assert_equal(result, expected) + def test_isin_scalar_test_elements(self): + a = 2 + b = numpy.array([1, 2, 3]) + dp_a = dpnp.array(a) + dp_b = dpnp.array(b) -def test_isin_empty(): - a = numpy.array([], dtype=int) - b = numpy.array([1, 2, 3]) + expected = numpy.isin(a, b) + result = dpnp.isin(dp_a, dp_b) + assert_equal(result, expected) - dp_a = dpnp.array(a) - dp_b = dpnp.array(b) + def test_isin_empty(self): + a = numpy.array([], dtype=int) + b = numpy.array([1, 2, 3]) - expected = numpy.isin(a, b) - result = dpnp.isin(dp_a, dp_b) - assert_equal(result, expected) + dp_a = dpnp.array(a) + dp_b = dpnp.array(b) + expected = numpy.isin(a, b) + result = dpnp.isin(dp_a, dp_b) + assert_equal(result, expected) -def test_isin_errors(): - a = dpnp.arange(5) - b = dpnp.arange(3) + def test_isin_errors(self): + a = dpnp.arange(5) + b = dpnp.arange(3) - # unsupported type for elements or test_elements - with pytest.raises(TypeError): - dpnp.isin(dict(), b) + # unsupported type for elements or test_elements + with pytest.raises(TypeError): + dpnp.isin(dict(), b) - with pytest.raises(TypeError): - dpnp.isin(a, dict()) + with pytest.raises(TypeError): + dpnp.isin(a, dict()) From e2f992c0f6dac93a857c9ca13c596b9119e87471 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Thu, 20 Nov 2025 23:19:11 -0800 Subject: [PATCH 08/11] add compute follows data tests for `isin` --- dpnp/tests/test_sycl_queue.py | 1 + dpnp/tests/test_usm_type.py | 1 + 2 files changed, 2 insertions(+) diff --git a/dpnp/tests/test_sycl_queue.py b/dpnp/tests/test_sycl_queue.py index c6d11dcd869..b153df8f444 100644 --- a/dpnp/tests/test_sycl_queue.py +++ b/dpnp/tests/test_sycl_queue.py @@ -536,6 +536,7 @@ def test_logic_op_1in(op, device): "greater", "greater_equal", "isclose", + "isin", "less", "less_equal", "logical_and", diff --git a/dpnp/tests/test_usm_type.py b/dpnp/tests/test_usm_type.py index c8598263c66..b595f7eb04b 100644 --- a/dpnp/tests/test_usm_type.py +++ b/dpnp/tests/test_usm_type.py @@ -355,6 +355,7 @@ def test_logic_op_1in(op, usm_type_x): "greater", "greater_equal", "isclose", + "isin", "less", "less_equal", "logical_and", From c6f97a23c78b342d813fa59ffb869162d2f3e315 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Tue, 16 Dec 2025 23:30:24 -0800 Subject: [PATCH 09/11] address review comments --- dpnp/dpnp_iface_logic.py | 3 ++- dpnp/tests/test_logic.py | 14 +++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/dpnp/dpnp_iface_logic.py b/dpnp/dpnp_iface_logic.py index 3570923a27f..a5c56054106 100644 --- a/dpnp/dpnp_iface_logic.py +++ b/dpnp/dpnp_iface_logic.py @@ -53,6 +53,7 @@ import dpnp.backend.extensions.ufunc._ufunc_impl as ufi from dpnp.dpnp_algo.dpnp_elementwise_common import DPNPBinaryFunc, DPNPUnaryFunc +from .dpnp_array import dpnp_array from .dpnp_utils import get_usm_allocations @@ -1271,7 +1272,7 @@ def isin( ) usm_element = dpnp.get_usm_ndarray(element) usm_test = dpnp.get_usm_ndarray(test_elements) - return dpnp.get_result_array( + return dpnp_array._create_from_usm_ndarray( dpt.isin( usm_element, usm_test, diff --git a/dpnp/tests/test_logic.py b/dpnp/tests/test_logic.py index 0fcb63f89c2..cae51e6777e 100644 --- a/dpnp/tests/test_logic.py +++ b/dpnp/tests/test_logic.py @@ -1,5 +1,7 @@ +import dpctl import numpy import pytest +from dpctl.utils import ExecutionPlacementError from numpy.testing import ( assert_allclose, assert_array_equal, @@ -880,12 +882,18 @@ def test_isin_empty(self): assert_equal(result, expected) def test_isin_errors(self): - a = dpnp.arange(5) - b = dpnp.arange(3) + q1 = dpctl.SyclQueue() + q2 = dpctl.SyclQueue() + + a = dpnp.arange(5, sycl_queue=q1) + b = dpnp.arange(3, sycl_queue=q2) # unsupported type for elements or test_elements with pytest.raises(TypeError): - dpnp.isin(dict(), b) + dpnp.isin(dict(), a) with pytest.raises(TypeError): dpnp.isin(a, dict()) + + with pytest.raises(ExecutionPlacementError): + dpnp.isin(a, b) From 2710fa96d924fe32d2a4485fdc4087a987235019 Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Thu, 18 Dec 2025 14:01:45 -0800 Subject: [PATCH 10/11] address further review comments --- dpnp/__init__.py | 2 +- dpnp/dpnp_iface_logic.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dpnp/__init__.py b/dpnp/__init__.py index a17524b7d1f..15c4ae18af7 100644 --- a/dpnp/__init__.py +++ b/dpnp/__init__.py @@ -344,7 +344,6 @@ iscomplexobj, isfinite, isfortran, - isin, isinf, isnan, isneginf, @@ -498,6 +497,7 @@ unique_inverse, unique_values, ) +from .dpnp_iface_logic import isin # ----------------------------------------------------------------------------- # Sorting, searching, and counting diff --git a/dpnp/dpnp_iface_logic.py b/dpnp/dpnp_iface_logic.py index a5c56054106..1834f25a048 100644 --- a/dpnp/dpnp_iface_logic.py +++ b/dpnp/dpnp_iface_logic.py @@ -1221,7 +1221,7 @@ def isin( >>> element array([[0, 2], [4, 6]]) - >>> test_elements = [1, 2, 4, 8] + >>> test_elements = np.array([1, 2, 4, 8]) >>> mask = np.isin(element, test_elements) >>> mask array([[False, True], From 719eb0a8cbf16908ff7ef7f3a7fd47dd59b9431c Mon Sep 17 00:00:00 2001 From: Nikita Grigorian Date: Thu, 18 Dec 2025 14:04:53 -0800 Subject: [PATCH 11/11] add isin implementation to the changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d166720479..5b9870c1ad1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Also, that release drops support for Python 3.9, making Python 3.10 the minimum * Added implementation of `dpnp.ndarray.tobytes` method [#2656](https://github.com/IntelPython/dpnp/pull/2656) * Added implementation of `dpnp.ndarray.__format__` method [#2662](https://github.com/IntelPython/dpnp/pull/2662) * Added implementation of `dpnp.ndarray.__bytes__` method [#2671](https://github.com/IntelPython/dpnp/pull/2671) +* Added implementation of `dpnp.isin` function [#2595](https://github.com/IntelPython/dpnp/pull/2595) ### Changed