Repository: numba/numba

Issue:
np.positive is not implemented
Interestingly, the comment here says it doesn't exist!

https://github.com/numba/numba/blob/579db91677b7d82ef81599905c3afed55668cf17/numba/core/typing/npydecl.py#L146-L153

It was added in numpy 1.13, https://numpy.org/doc/1.18/reference/generated/numpy.positive.html
operator.pos fails on strided arrays
<!--

Thanks for opening an issue! To help the Numba team handle your information
efficiently, please first ensure that there is no other issue present that
already describes the issue you have
(search at https://github.com/numba/numba/issues?&q=is%3Aissue).

For more general "how do I do X?" type questions, please speak to us in real
time on https://gitter.im/numba/numba or post to the Numba mailing list
https://groups.google.com/a/continuum.io/forum/#!forum/numba-users.

-->

## Reporting a bug

<!--

Before submitting a bug report please ensure that you can check off these boxes:

-->

- [x] I am using the latest released version of Numba (most recent is visible in
 the change log (https://github.com/numba/numba/blob/master/CHANGE_LOG).
- [x] I have included below a minimal working reproducer (if you are unsure how
 to write one see http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports).

<!--

Please include details of the bug here, including, if applicable, what you
expected to happen!

-->
```python
@numba.njit
def pos(x):
    return +x
pos(np.arange(6)[::2])
```
gives
```
LoweringError: Failed in nopython mode pipeline (step: nopython mode backend)
Don't know how to allocate array with layout 'A'.

File "<ipython-input-12-72e97bb40740>", line 3:
def pos(x):
    return +x
    ^
```



Discussion:
Thanks for the report, the time stamp on that docstring is `2015-04-17 17:36:36 -0500` which predates NumPy 1.13 by a couple of years? Guess this could do with an update on a few fronts.

1. Remove that docstring
2. Add in the lowering for `np.positive`.
3. Test 2.
Can I work on this ?
@niteya-shah certainly! Thanks for helping out!


Resolving patch:
diff --git a/numba/core/typing/npydecl.py b/numba/core/typing/npydecl.py
--- a/numba/core/typing/npydecl.py
+++ b/numba/core/typing/npydecl.py
@@ -143,22 +143,6 @@ def generic(self, args, kws):
         return signature(*out)
 
 
-@infer_global(operator.pos)
-class UnaryPositiveArray(AbstractTemplate):
-    '''Typing template class for +(array) expressions.  This operator is
-    special because there is no Numpy ufunc associated with it; we
-    include typing for it here (numba.typing.npydecl) because this is
-    where the remaining array operators are defined.
-    '''
-    key = operator.pos
-
-    def generic(self, args, kws):
-        assert not kws
-        if len(args) == 1 and isinstance(args[0], types.ArrayCompatible):
-            arg_ty = args[0]
-            return arg_ty.copy()(arg_ty)
-
-
 class NumpyRulesArrayOperator(Numpy_rules_ufunc):
     _op_map = {
         operator.add: "add",
@@ -252,9 +236,7 @@ def generic(self, args, kws):
 
 class NumpyRulesUnaryArrayOperator(NumpyRulesArrayOperator):
     _op_map = {
-        # Positive is a special case since there is no Numpy ufunc
-        # corresponding to it (it's essentially an identity operator).
-        # See UnaryPositiveArray, above.
+        operator.pos: "positive",
         operator.neg: "negative",
         operator.invert: "invert",
     }
@@ -269,7 +251,7 @@ def generic(self, args, kws):
 
 _math_operations = [ "add", "subtract", "multiply",
                      "logaddexp", "logaddexp2", "true_divide",
-                     "floor_divide", "negative", "power",
+                     "floor_divide", "negative", "positive", "power",
                      "remainder", "fmod", "absolute",
                      "rint", "sign", "conjugate", "exp", "exp2",
                      "log", "log2", "log10", "expm1", "log1p",
diff --git a/numba/np/ufunc_db.py b/numba/np/ufunc_db.py
--- a/numba/np/ufunc_db.py
+++ b/numba/np/ufunc_db.py
@@ -75,6 +75,24 @@ def _fill_ufunc_db(ufunc_db):
         'D->D': numbers.complex_negate_impl,
     }
 
+    ufunc_db[np.positive] = {
+        '?->?': numbers.int_positive_impl,
+        'b->b': numbers.int_positive_impl,
+        'B->B': numbers.int_positive_impl,
+        'h->h': numbers.int_positive_impl,
+        'H->H': numbers.int_positive_impl,
+        'i->i': numbers.int_positive_impl,
+        'I->I': numbers.int_positive_impl,
+        'l->l': numbers.int_positive_impl,
+        'L->L': numbers.int_positive_impl,
+        'q->q': numbers.int_positive_impl,
+        'Q->Q': numbers.int_positive_impl,
+        'f->f': numbers.real_positive_impl,
+        'd->d': numbers.real_positive_impl,
+        'F->F': numbers.complex_positive_impl,
+        'D->D': numbers.complex_positive_impl,
+    }
+
     ufunc_db[np.absolute] = {
         '?->?': numbers.int_abs_impl,
         'b->b': numbers.int_abs_impl,
@@ -998,6 +1016,9 @@ def _fill_ufunc_db(ufunc_db):
     ufunc_db[np.negative].update({
         'm->m': npdatetime.timedelta_neg_impl,
     })
+    ufunc_db[np.positive].update({
+        'm->m': npdatetime.timedelta_pos_impl,
+    })
     ufunc_db[np.absolute].update({
         'm->m': npdatetime.timedelta_abs_impl,
     })


Test patch:
diff --git a/numba/tests/test_ufuncs.py b/numba/tests/test_ufuncs.py
--- a/numba/tests/test_ufuncs.py
+++ b/numba/tests/test_ufuncs.py
@@ -303,6 +303,9 @@ def test_negative_ufunc(self, flags=no_pyobj_flags):
                               skip_inputs=[types.Array(types.uint32, 1, 'C'), types.uint32],
                               flags=flags)
 
+    def test_positive_ufunc(self, flags=no_pyobj_flags):
+        self.basic_ufunc_test(np.positive, flags=flags)
+
     def test_power_ufunc(self, flags=no_pyobj_flags):
         self.basic_ufunc_test(np.power, flags=flags,
                                positive_only=True)