Repository: chainer/chainer

Issue:
Implement `any` and `all` in `cupy.ndarray`
To check boolean arrays, we need to make `any` and `all` methods.
related to #403 , #404 



Discussion:

Resolving patch:
diff --git a/cupy/__init__.py b/cupy/__init__.py
--- a/cupy/__init__.py
+++ b/cupy/__init__.py
@@ -646,8 +646,12 @@ def prod(self, axis=None, dtype=None, out=None, keepdims=None):
         return prod(self, axis=axis, dtype=dtype, out=out, keepdims=keepdims)
 
     # TODO(okuta): Implement cumprod
-    # TODO(okuta): Implement all
-    # TODO(okuta): Implement any
+
+    def all(self, axis=None, out=None):
+        return logic.truth.all(self, axis, out)
+
+    def any(self, axis=None, out=None):
+        return logic.truth.any(self, axis, out)
 
     # -------------------------------------------------------------------------
     # Arithmetic and comparison operations
@@ -681,7 +685,7 @@ def __nonzero__(self):
             return bool(self.get())
         else:
             msg = 'The truth value of an array with more than one element is ' \
-                  'ambiguous. Use a.get().any() or a.get().all()'
+                  'ambiguous. Use a.any() or a.all()'
             raise ValueError(msg)
 
     def __bool__(self):
@@ -914,7 +918,7 @@ def __getitem__(self, slices):
         else:
             slices = list(slices)
 
-        if any(isinstance(s, ndarray) for s in slices):
+        if six.moves.builtins.any(isinstance(s, ndarray) for s in slices):
             raise ValueError('Advanced indexing is not supported')
 
         # Expand ellipsis into empty slices
@@ -1301,6 +1305,9 @@ def _should_use_rop(self, a):
 equal = logic.comparison.equal
 not_equal = logic.comparison.not_equal
 
+all = logic.truth.all
+any = logic.truth.any
+
 # -----------------------------------------------------------------------------
 # Mathematical functions
 # -----------------------------------------------------------------------------
@@ -1441,7 +1448,7 @@ def softplus(x):
                return xp.maximum(0, x) + xp.log1p(xp.exp(-abs(x)))
 
     """
-    if any(isinstance(arg, ndarray) for arg in args):
+    if six.moves.builtins.any(isinstance(arg, ndarray) for arg in args):
         return _cupy
     else:
         return numpy
diff --git a/cupy/logic/truth.py b/cupy/logic/truth.py
--- a/cupy/logic/truth.py
+++ b/cupy/logic/truth.py
@@ -1,7 +1,25 @@
-# flake8: NOQA
-# "flake8: NOQA" to suppress warning "H104  File contains nothing but comments"
+from cupy import reduction
 
-# TODO(okuta): Implement all
 
+def all(a, axis=None, out=None, keepdims=False):
+    return _all(a, axis=axis, out=out, keepdims=keepdims)
 
-# TODO(okuta): Implement any
+
+def any(a, axis=None, out=None, keepdims=False):
+    return _any(a, axis=axis, out=out, keepdims=keepdims)
+
+
+_all = reduction.create_reduction_func(
+    'cupy_all',
+    ('?->?', 'B->?', 'h->?', 'H->?', 'i->?', 'I->?', 'l->?', 'L->?',
+     'q->?', 'Q->?', 'e->?', 'f->?', 'd->?'),
+    ('in0', 'a & b', 'out0 = a', 'bool'),
+    'true', '')
+
+
+_any = reduction.create_reduction_func(
+    'cupy_any',
+    ('?->?', 'B->?', 'h->?', 'H->?', 'i->?', 'I->?', 'l->?', 'L->?',
+     'q->?', 'Q->?', 'e->?', 'f->?', 'd->?'),
+    ('in0', 'a | b', 'out0 = a', 'bool'),
+    'false', '')


Test patch:
diff --git a/tests/cupy_tests/logic_tests/test_truth.py b/tests/cupy_tests/logic_tests/test_truth.py
--- a/tests/cupy_tests/logic_tests/test_truth.py
+++ b/tests/cupy_tests/logic_tests/test_truth.py
@@ -1,9 +1,85 @@
 import unittest
 
+import numpy
+import six
+
 from cupy import testing
 
 
+def _calc_out_shape(shape, axis, keepdims):
+    if axis is None:
+        axis = list(six.moves.range(len(shape)))
+    elif isinstance(axis, int):
+        axis = [axis]
+    else:
+        axis = list(axis)
+
+    shape = numpy.array(shape)
+
+    if keepdims:
+        shape[axis] = 1
+    else:
+        shape[axis] = -1
+        shape = filter(lambda x: x != -1, shape)
+    return tuple(shape)
+
+
+@testing.parameterize(
+    *testing.product(
+        {'f': ['all', 'any'],
+         'x': [numpy.arange(24).reshape(2, 3, 4) - 10,
+               numpy.zeros((2, 3, 4)),
+               numpy.ones((2, 3, 4))],
+         'axis': [None, (0, 1, 2), 0, 1, 2, (0, 1)],
+         'keepdims': [False, True]}))
+@testing.gpu
+class TestAllAny(unittest.TestCase):
+
+    _multiprocess_can_split_ = True
+
+    @testing.for_all_dtypes()
+    @testing.numpy_cupy_array_equal()
+    def test_without_out(self, xp, dtype):
+        x = xp.asarray(self.x).astype(dtype)
+        return getattr(xp, self.f)(x, self.axis, None, self.keepdims)
+
+    @testing.for_all_dtypes()
+    @testing.numpy_cupy_array_equal()
+    def test_with_out(self, xp, dtype):
+        x = xp.asarray(self.x).astype(dtype)
+        out_shape = _calc_out_shape(x.shape, self.axis, self.keepdims)
+        out = xp.empty(out_shape, dtype=x.dtype)
+        getattr(xp, self.f)(x, self.axis, out, self.keepdims)
+        return out
+
+
+@testing.parameterize(
+    *testing.product(
+        {'f': ['all', 'any'],
+         'x': [numpy.array([[[numpy.nan]]]),
+               numpy.array([[[numpy.nan, 0]]]),
+               numpy.array([[[numpy.nan, 1]]]),
+               numpy.array([[[numpy.nan, 0, 1]]])],
+         'axis': [None, (0, 1, 2), 0, 1, 2, (0, 1)],
+         'keepdims': [False, True]}))
 @testing.gpu
-class TestTruth(unittest.TestCase):
+class TestAllAnyWithNaN(unittest.TestCase):
 
     _multiprocess_can_split_ = True
+
+    @testing.for_dtypes(
+        (numpy.float64, numpy.float32, numpy.float16, numpy.bool_))
+    @testing.numpy_cupy_array_equal()
+    def test_without_out(self, xp, dtype):
+        x = xp.asarray(self.x).astype(dtype)
+        return getattr(xp, self.f)(x, self.axis, None, self.keepdims)
+
+    @testing.for_dtypes(
+        (numpy.float64, numpy.float32, numpy.float16, numpy.bool_))
+    @testing.numpy_cupy_array_equal()
+    def test_with_out(self, xp, dtype):
+        x = xp.asarray(self.x).astype(dtype)
+        out_shape = _calc_out_shape(x.shape, self.axis, self.keepdims)
+        out = xp.empty(out_shape, dtype=x.dtype)
+        getattr(xp, self.f)(x, self.axis, out, self.keepdims)
+        return out