Repository: cupy/cupy

Issue:
implementing array_equal() for CuPy
I was trying to implement array_equal() for Cupy and was thinking along these lines

```py
def array_equal(a1, a2):
    try:
        a1, a2 = cupy.asarray(a1), cupy.asarray(a2)
    except Exception:
        return False
    if a1.shape != a2.shape:
        return False
    return bool(cupy.asarray(a1 == a2).all())
```

Should I move forward with this ? I would be grateful if someone could guide me.


Discussion:
Hello, thanks for being interested in contributing to CuPy!.

1st is that `asarray` checks are not needed, since cupy functions accepts only cupy arrays. https://docs-cupy.chainer.org/en/stable/reference/difference.html#universal-functions-only-work-with-cupy-array-or-scalar

2nd is that this function currently synchronizes the device when you do `return bool(...)`
just doing a `return (a1==a2).all()` should be fine.

The function can be added to the `cupy/logic/comparison.py` file.

For sending a PR you will need to add a docstring to the function and also add tests for the external behavior.
are you interested in opening a PR? @portfoliocreator 
Otherwise, I would be fine doing it!
@rushabh-v Yes , sure go ahead.

Resolving patch:
diff --git a/cupy/__init__.py b/cupy/__init__.py
--- a/cupy/__init__.py
+++ b/cupy/__init__.py
@@ -487,6 +487,7 @@ def base_repr(number, base=2, padding=0):  # NOQA (needed to avoid redefinition
 # Logic functions
 # -----------------------------------------------------------------------------
 from cupy.logic.comparison import allclose  # NOQA
+from cupy.logic.comparison import array_equal  # NOQA
 from cupy.logic.comparison import isclose  # NOQA
 
 from cupy.logic.content import isfinite  # NOQA
diff --git a/cupy/logic/comparison.py b/cupy/logic/comparison.py
--- a/cupy/logic/comparison.py
+++ b/cupy/logic/comparison.py
@@ -38,6 +38,25 @@
 )
 
 
+def array_equal(a1, a2):
+    """Returns ``True`` if two arrays are element-wise exactly equal.
+
+    Args:
+        a1 (cupy.ndarray): Input array to compare.
+        a2 (cupy.ndarray): Input array to compare.
+
+    Returns:
+        cupy.ndarray: A boolean 0-dim array.
+            If its value is ``True``, two arrays are element-wise equal.
+
+    .. seealso:: :func:`numpy.array_equal`
+
+    """
+    if a1.shape != a2.shape:
+        return cupy.array(False)
+    return (a1 == a2).all()
+
+
 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.
 


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
@@ -74,6 +74,48 @@ def test_binary_array_pyscalar(self, xp, dtype):
         return [op(a, b) for op in self.operators]
 
 
+class TestArrayEqual(unittest.TestCase):
+
+    @testing.for_all_dtypes()
+    @testing.numpy_cupy_equal()
+    def test_array_equal_not_equal(self, xp, dtype):
+        a = xp.array([1, 2, 3, 4], dtype=dtype)
+        b = xp.array([1, 2, 4, 5], dtype=dtype)
+        return xp.array_equal(a, b)
+
+    @testing.for_all_dtypes()
+    @testing.numpy_cupy_equal()
+    def test_array_equal_is_equal(self, xp, dtype):
+        a = xp.array([1, 2, 3, 4], dtype=dtype)
+        b = xp.array([1, 2, 3, 4], dtype=dtype)
+        return xp.array_equal(a, b)
+
+    @testing.for_all_dtypes()
+    @testing.numpy_cupy_equal()
+    def test_array_equal_diff_length(self, xp, dtype):
+        a = xp.array([1, 2, 3, 4], dtype=dtype)
+        b = xp.array([1, 2, 3], dtype=dtype)
+        return xp.array_equal(a, b)
+
+    @testing.numpy_cupy_equal()
+    def test_array_equal_diff_dtypes_not_equal(self, xp):
+        a = xp.array([0.9e-5, 1.1e-5, 100.5, 10.5])
+        b = xp.array([0, 0, 1000, 1000])
+        return xp.array_equal(a, b)
+
+    @testing.numpy_cupy_equal()
+    def test_array_equal_diff_dtypes_is_equal(self, xp):
+        a = xp.array([0.0, 1.0, 100.0, 10.0])
+        b = xp.array([0, 1, 100, 10])
+        return xp.array_equal(a, b)
+
+    @testing.numpy_cupy_equal()
+    def test_array_equal_broadcast_not_allowed(self, xp):
+        a = xp.array([1, 1, 1, 1])
+        b = xp.array([1])
+        return xp.array_equal(a, b)
+
+
 class TestAllclose(unittest.TestCase):
 
     @testing.for_all_dtypes()