Repository: cupy/cupy

Issue:
`cupy.allclose` does not support comparison of complex-number arrays
As title. The reason is that in this line of the ufunc helper 
https://github.com/cupy/cupy/blob/bb99716ffee178368ec71c875ace0553053cadc2/cupy/logic/comparison.py#L6
only `float16` (`e`), `float32` (`f`), and `float64` (`d`) arrays are included. Note that the NumPy counterpart does support comparing complex arrays using the same comparison logic, and I believe this can be easily patched by adding another ufunc helper for `complex64` and `complex128` arrays. PR to follow.


Discussion:

Resolving patch:
diff --git a/cupy/logic/comparison.py b/cupy/logic/comparison.py
--- a/cupy/logic/comparison.py
+++ b/cupy/logic/comparison.py
@@ -1,3 +1,6 @@
+import numpy
+
+import cupy
 from cupy import core
 
 
@@ -16,6 +19,24 @@
     '''
 )
 
+# Note that in cupy/core/include/cupy/complex.cuh, we already got isfinite and
+# isnan working for complex numbers, so just replace fabs above by abs (from
+# thrust) and we are ready to go
+_is_close_complex = core.create_ufunc(
+    'cupy_is_close_complex',
+    ('FFff?->?', 'DDdd?->?'),
+    '''
+    bool equal_nan = in4;
+    if (isfinite(in0) && isfinite(in1)) {
+      out0 = abs(in0 - in1) <= in3 + in2 * abs(in1);
+    } else if (equal_nan) {
+      out0 = (in0 == in1) || (isnan(in0) && isnan(in1));
+    } else {
+      out0 = (in0 == in1);
+    }
+    '''
+)
+
 
 def allclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False):
     """Returns True if two arrays are element-wise equal within a tolerance.
@@ -70,7 +91,13 @@ def isclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False):
     .. seealso:: :func:`numpy.isclose`
 
     """
-    return _is_close(a, b, rtol, atol, equal_nan)
+    a = cupy.asanyarray(a)
+    b = cupy.asanyarray(b)
+    if (a.dtype in [numpy.complex64, numpy.complex128]) or \
+       (b.dtype in [numpy.complex64, numpy.complex128]):
+        return _is_close_complex(a, b, rtol, atol, equal_nan)
+    else:
+        return _is_close(a, b, rtol, atol, equal_nan)
 
 
 # TODO(okuta): Implement array_equal


Test patch:
diff --git a/tests/cupy_tests/logic_tests/test_comparison.py b/tests/cupy_tests/logic_tests/test_comparison.py
--- a/tests/cupy_tests/logic_tests/test_comparison.py
+++ b/tests/cupy_tests/logic_tests/test_comparison.py
@@ -77,7 +77,7 @@ def test_binary_array_pyscalar(self, xp, dtype):
 class TestAllclose(unittest.TestCase):
 
     @testing.with_requires('numpy>=1.10')
-    @testing.for_all_dtypes(no_complex=True)
+    @testing.for_all_dtypes()
     @testing.numpy_cupy_equal()
     def test_allclose_finite(self, xp, dtype):
         a = xp.array([0.9e-5, 1.1e-5, 1000 + 1e-4, 1000 - 1e-4], dtype=dtype)
@@ -85,7 +85,7 @@ def test_allclose_finite(self, xp, dtype):
         return xp.allclose(a, b)
 
     @testing.with_requires('numpy>=1.10')
-    @testing.for_all_dtypes(no_complex=True)
+    @testing.for_all_dtypes()
     @testing.numpy_cupy_equal()
     def test_allclose_min_int(self, xp, dtype):
         a = xp.array([0], dtype=dtype)
@@ -113,7 +113,7 @@ def test_allclose_infinite_equal_nan(self, xp, dtype):
         b = xp.array([0, nan, inf, ninf], dtype=dtype)
         return xp.allclose(a, b, equal_nan=True)
 
-    @testing.for_all_dtypes(no_complex=True)
+    @testing.for_all_dtypes()
     @testing.numpy_cupy_equal()
     def test_allclose_array_scalar(self, xp, dtype):
         a = xp.array([0.9e-5, 1.1e-5], dtype=dtype)