Repository: cupy/cupy

Issue:
Missing `cupy.positive()`
See https://numpy.org/doc/stable/reference/generated/numpy.positive.html for `numpy.positive()`. This is basically a no-op + copying the input (subject to allowed dtypes etc). This is needed by the Array API standard.


Discussion:

Resolving patch:
diff --git a/cupy/__init__.py b/cupy/__init__.py
--- a/cupy/__init__.py
+++ b/cupy/__init__.py
@@ -614,6 +614,7 @@ def isscalar(element):
 from cupy._math.arithmetic import modf  # NOQA
 from cupy._math.arithmetic import multiply  # NOQA
 from cupy._math.arithmetic import negative  # NOQA
+from cupy._math.arithmetic import positive  # NOQA
 from cupy._math.arithmetic import power  # NOQA
 from cupy._math.arithmetic import reciprocal  # NOQA
 from cupy._math.arithmetic import remainder  # NOQA
diff --git a/cupy/_core/__init__.py b/cupy/_core/__init__.py
--- a/cupy/_core/__init__.py
+++ b/cupy/_core/__init__.py
@@ -50,6 +50,7 @@
 from cupy._core._routines_math import floor_divide  # NOQA
 from cupy._core._routines_math import multiply  # NOQA
 from cupy._core._routines_math import negative  # NOQA
+from cupy._core._routines_math import positive  # NOQA
 from cupy._core._routines_math import power  # NOQA
 from cupy._core._routines_math import remainder  # NOQA
 from cupy._core._routines_math import sqrt  # NOQA
diff --git a/cupy/_math/arithmetic.py b/cupy/_math/arithmetic.py
--- a/cupy/_math/arithmetic.py
+++ b/cupy/_math/arithmetic.py
@@ -21,6 +21,9 @@
     ''')
 
 
+positive = _core.positive
+
+
 negative = _core.negative
 
 


Test patch:
diff --git a/tests/cupy_tests/core_tests/test_ndarray_unary_op.py b/tests/cupy_tests/core_tests/test_ndarray_unary_op.py
--- a/tests/cupy_tests/core_tests/test_ndarray_unary_op.py
+++ b/tests/cupy_tests/core_tests/test_ndarray_unary_op.py
@@ -8,7 +8,6 @@
 from cupy import testing
 
 
-@testing.gpu
 class TestArrayBoolOp(unittest.TestCase):
 
     @testing.for_all_dtypes()
@@ -40,7 +39,6 @@ def test_bool_two_elements(self, dtype):
             bool(cupy.array([1, 2], dtype=dtype))
 
 
-@testing.gpu
 class TestArrayUnaryOp(unittest.TestCase):
 
     @testing.for_all_dtypes(no_bool=True)
@@ -61,8 +59,18 @@ def test_neg_array(self, xp, dtype):
         a = testing.shaped_arange((2, 3), xp, dtype)
         return operator.neg(a)
 
-    def test_pos_array(self):
-        self.check_array_op(operator.pos)
+    @testing.for_all_dtypes(no_bool=True)
+    @testing.numpy_cupy_allclose()
+    def test_pos_array(self, xp, dtype):
+        a = testing.shaped_arange((2, 3), xp, dtype)
+        assert a is not +a
+        return +a
+
+    def test_pos_boolarray(self):
+        for xp in (numpy, cupy):
+            a = xp.array(True, dtype=xp.bool_)
+            with pytest.deprecated_call():
+                assert a is not +a
 
     @testing.with_requires('numpy<1.16')
     def test_pos_array_full(self):
@@ -99,7 +107,6 @@ def test_abs_zerodim_full(self):
         self.check_zerodim_op_full(operator.abs)
 
 
-@testing.gpu
 class TestArrayIntUnaryOp(unittest.TestCase):
 
     @testing.for_int_dtypes()
@@ -125,7 +132,6 @@ def test_invert_zerodim(self):
     'xp': [numpy, cupy],
     'shape': [(3, 2), (), (3, 0, 2)]
 }))
-@testing.gpu
 class TestBoolNeg(unittest.TestCase):
 
     def test_bool_neg(self):