Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add cupy.setxor1d api #6582

Merged
merged 6 commits into from May 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions cupy/__init__.py
Expand Up @@ -506,6 +506,7 @@ def base_repr(number, base=2, padding=0): # NOQA (needed to avoid redefinition
from cupy._logic.truth import intersect1d # NOQA
from cupy._logic.truth import isin # NOQA
from cupy._logic.truth import setdiff1d # NOQA
from cupy._logic.truth import setxor1d # NOQA
from cupy._logic.truth import union1d # NOQA


Expand Down
45 changes: 45 additions & 0 deletions cupy/_logic/truth.py
Expand Up @@ -4,6 +4,14 @@
from cupy import _util


_setxorkernel = cupy._core.ElementwiseKernel(
'raw T X, int64 len',
'bool z',
'z = (i == 0 || X[i] != X[i-1]) && (i == len - 1 || X[i] != X[i+1])',
'setxorkernel'
)


def all(a, axis=None, out=None, keepdims=False):
"""Tests whether all array elements along a given axis evaluate to True.

Expand Down Expand Up @@ -255,6 +263,43 @@ def setdiff1d(ar1, ar2, assume_unique=False):
return ar1[in1d(ar1, ar2, assume_unique=True, invert=True)]


def setxor1d(ar1, ar2, assume_unique=False):
"""Find the set exclusive-or of two arrays.

Parameters
----------
ar1, ar2 : cupy.ndarray
Input arrays. They are flattend if they are not already 1-D.
assume_unique : bool
By default, False, i.e. input arrays are not unique.
If True, input arrays are assumed to be unique. This can
speed up the calculation.

Returns
-------
setxor1d : cupy.ndarray
Return the sorted, unique values that are in only one
(not both) of the input arrays.

See Also
--------
numpy.setxor1d

"""
if not assume_unique:
ar1 = cupy.unique(ar1)
ar2 = cupy.unique(ar2)

aux = cupy.concatenate((ar1, ar2), axis=None)
if aux.size == 0:
return aux

aux.sort()

return aux[_setxorkernel(aux, aux.size,
cupy.zeros(aux.size, dtype=cupy.bool_))]


def union1d(arr1, arr2):
"""Find the union of two arrays.

Expand Down
1 change: 1 addition & 0 deletions docs/source/reference/set.rst
Expand Up @@ -24,3 +24,4 @@ Boolean operations
intersect1d
isin
setdiff1d
setxor1d
58 changes: 58 additions & 0 deletions tests/cupy_tests/logic_tests/test_truth.py
Expand Up @@ -183,6 +183,64 @@ def test_setdiff1d_bool_val(self, xp):
return xp.setdiff1d(x, y)


class TestSetxor1d:

@testing.for_all_dtypes()
@testing.numpy_cupy_array_equal()
def test_setxor1d_same_arrays(self, xp, dtype):
x = xp.array([1, 2, 3, 4, 5], dtype=dtype)
y = xp.array([1, 2, 3, 4, 5], dtype=dtype)
return xp.setxor1d(x, y, assume_unique=True)

@testing.for_all_dtypes()
@testing.numpy_cupy_array_equal()
def test_setxor1d_diff_size_arr_inputs(self, xp, dtype):
x = xp.array([3, 4, 9, 1, 5, 4], dtype=dtype)
y = xp.array([8, 7, 3, 9, 0], dtype=dtype)
return xp.setxor1d(x, y)

@testing.for_all_dtypes()
@testing.numpy_cupy_array_equal()
def test_setxor1d_diff_elements(self, xp, dtype):
x = xp.array([3, 4, 9, 1, 5, 4], dtype=dtype)
y = xp.array([8, 7, 3, 9, 0], dtype=dtype)
return xp.setxor1d(x, y, assume_unique=True)

@testing.for_all_dtypes()
@testing.numpy_cupy_array_equal()
def test_setxor1d_with_2d(self, xp, dtype):
x = testing.shaped_random((2, 3), xp, dtype=dtype)
y = testing.shaped_random((3, 5), xp, dtype=dtype)
return xp.setxor1d(x, y)

@testing.for_all_dtypes()
@testing.numpy_cupy_array_equal()
def test_setxor1d_with_duplicate_elements(self, xp, dtype):
x = xp.array([1, 2, 3, 2, 2, 6], dtype=dtype)
y = xp.array([3, 4, 2, 1, 1, 9], dtype=dtype)
return xp.setxor1d(x, y)

@testing.for_all_dtypes()
@testing.numpy_cupy_array_equal()
def test_setxor1d_empty_arr(self, xp, dtype):
x = xp.array([], dtype=dtype)
y = xp.array([], dtype=dtype)
return xp.setxor1d(x, y)

@testing.for_all_dtypes()
@testing.numpy_cupy_array_equal()
def test_setxor1d_more_dim(self, xp, dtype):
x = testing.shaped_arange((2, 3, 4, 8), xp, dtype=dtype)
y = testing.shaped_arange((5, 4, 2), xp, dtype=dtype)
return xp.setxor1d(x, y)

@testing.numpy_cupy_array_equal()
def test_setxor1d_bool_val(self, xp):
x = xp.array([True, False, True])
y = xp.array([False])
return xp.setxor1d(x, y)


class TestIntersect1d:

@testing.for_all_dtypes(no_bool=True)
Expand Down