diff options
author | Stephan Hoyer <shoyer@google.com> | 2017-04-21 00:05:44 -0700 |
---|---|---|
committer | Stephan Hoyer <shoyer@google.com> | 2017-04-30 20:37:48 -0700 |
commit | 8db9662a3352ab029a5bbc2d81e5363d7a388e2e (patch) | |
tree | 829b6d03dc3927538336913158fd7b15ee12682f | |
parent | 590facafcbd454fc02f96aa52c1c3412d6cccd14 (diff) | |
download | numpy-8db9662a3352ab029a5bbc2d81e5363d7a388e2e.tar.gz |
ENH: add np.positive ufunc and use it for ndarray.__pos__
xref GH8932
-rw-r--r-- | doc/release/1.13.0-notes.rst | 5 | ||||
-rw-r--r-- | doc/source/reference/c-api.array.rst | 4 | ||||
-rw-r--r-- | doc/source/reference/ufuncs.rst | 1 | ||||
-rw-r--r-- | numpy/core/code_generators/generate_umath.py | 8 | ||||
-rw-r--r-- | numpy/core/code_generators/ufunc_docstrings.py | 21 | ||||
-rw-r--r-- | numpy/core/src/umath/funcs.inc.src | 8 | ||||
-rw-r--r-- | numpy/core/src/umath/loops.c.src | 33 | ||||
-rw-r--r-- | numpy/core/src/umath/loops.h.src | 8 | ||||
-rw-r--r-- | numpy/core/tests/test_datetime.py | 6 | ||||
-rw-r--r-- | numpy/core/tests/test_half.py | 1 | ||||
-rw-r--r-- | numpy/core/tests/test_umath.py | 20 |
11 files changed, 113 insertions, 2 deletions
diff --git a/doc/release/1.13.0-notes.rst b/doc/release/1.13.0-notes.rst index df6ce7c4b..f594c1825 100644 --- a/doc/release/1.13.0-notes.rst +++ b/doc/release/1.13.0-notes.rst @@ -327,6 +327,11 @@ regarding "workspace" sizes, and in some places may use faster algorithms. This now works on empty arrays, returning 0, and can reduce over multiple axes. Previously, a ``ValueError`` was thrown in these cases. +New ``positive`` ufunc +---------------------- +This ufunc corresponds to unary `+`, but unlike `+` on an ndarray it will raise +an error if array values do not support numeric operations. + Changes ======= diff --git a/doc/source/reference/c-api.array.rst b/doc/source/reference/c-api.array.rst index b50f86e46..35df42daa 100644 --- a/doc/source/reference/c-api.array.rst +++ b/doc/source/reference/c-api.array.rst @@ -3129,8 +3129,8 @@ Internal Flexibility **add**, **subtract**, **multiply**, **divide**, **remainder**, **power**, **square**, **reciprocal**, - **ones_like**, **sqrt**, **negative**, **absolute**, - **invert**, **left_shift**, **right_shift**, + **ones_like**, **sqrt**, **negative**, **positive**, + **absolute**, **invert**, **left_shift**, **right_shift**, **bitwise_and**, **bitwise_xor**, **bitwise_or**, **less**, **less_equal**, **equal**, **not_equal**, **greater**, **greater_equal**, **floor_divide**, diff --git a/doc/source/reference/ufuncs.rst b/doc/source/reference/ufuncs.rst index bcd3d5f0a..94663a141 100644 --- a/doc/source/reference/ufuncs.rst +++ b/doc/source/reference/ufuncs.rst @@ -505,6 +505,7 @@ Math operations true_divide floor_divide negative + positive power remainder mod diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py index 4a8566d9d..dfba04c18 100644 --- a/numpy/core/code_generators/generate_umath.py +++ b/numpy/core/code_generators/generate_umath.py @@ -398,6 +398,14 @@ defdict = { TD(cmplx, f='neg'), TD(O, f='PyNumber_Negative'), ), +'positive': + Ufunc(1, 1, None, + docstrings.get('numpy.core.umath.positive'), + 'PyUFunc_SimpleUnaryOperationTypeResolver', + TD(ints+flts+timedeltaonly), + TD(cmplx, f='pos'), + TD(O, f='PyNumber_Positive'), + ), 'sign': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.sign'), diff --git a/numpy/core/code_generators/ufunc_docstrings.py b/numpy/core/code_generators/ufunc_docstrings.py index ad4a10d3b..ed9e05b15 100644 --- a/numpy/core/code_generators/ufunc_docstrings.py +++ b/numpy/core/code_generators/ufunc_docstrings.py @@ -2537,6 +2537,27 @@ add_newdoc('numpy.core.umath', 'negative', """) +add_newdoc('numpy.core.umath', 'positive', + """ + Numerical positive, element-wise. + + Parameters + ---------- + x : array_like or scalar + Input array. + + Returns + ------- + y : ndarray or scalar + Returned array or scalar: `y = +x`. + + Notes + ----- + Equivalent to `x.copy()`, but only defined for types that support + arithmetic. + + """) + add_newdoc('numpy.core.umath', 'not_equal', """ Return (x1 != x2) element-wise. diff --git a/numpy/core/src/umath/funcs.inc.src b/numpy/core/src/umath/funcs.inc.src index 9887120f5..5613c30ee 100644 --- a/numpy/core/src/umath/funcs.inc.src +++ b/numpy/core/src/umath/funcs.inc.src @@ -188,6 +188,14 @@ nc_neg@c@(@ctype@ *a, @ctype@ *r) } static void +nc_pos@c@(@ctype@ *a, @ctype@ *r) +{ + r->real = +a->real; + r->imag = +a->imag; + return; +} + +static void nc_sqrt@c@(@ctype@ *x, @ctype@ *r) { *r = npy_csqrt@c@(*x); diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src index 24364afbd..47faaf180 100644 --- a/numpy/core/src/umath/loops.c.src +++ b/numpy/core/src/umath/loops.c.src @@ -833,6 +833,12 @@ NPY_NO_EXPORT void } } +NPY_NO_EXPORT void +@TYPE@_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +{ + UNARY_LOOP_FAST(@type@, @type@, *out = +in); +} + /**begin repeat1 * #isa = , _avx2# * #ISA = , AVX2# @@ -1185,6 +1191,15 @@ TIMEDELTA_negative(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY } NPY_NO_EXPORT void +TIMEDELTA_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +{ + UNARY_LOOP { + const npy_timedelta in1 = *(npy_timedelta *)ip1; + *((npy_timedelta *)op1) = +in1; + } +} + +NPY_NO_EXPORT void TIMEDELTA_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { UNARY_LOOP { @@ -1886,6 +1901,15 @@ NPY_NO_EXPORT void } NPY_NO_EXPORT void +@TYPE@_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +{ + UNARY_LOOP { + const @type@ in1 = *(@type@ *)ip1; + *((@type@ *)op1) = +in1; + } +} + +NPY_NO_EXPORT void @TYPE@_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { /* Sign of nan is nan */ @@ -2189,6 +2213,15 @@ HALF_negative(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUS } NPY_NO_EXPORT void +HALF_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) +{ + UNARY_LOOP { + const npy_half in1 = *(npy_half *)ip1; + *((npy_half *)op1) = +in1; + } +} + +NPY_NO_EXPORT void HALF_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { /* Sign of nan is nan */ diff --git a/numpy/core/src/umath/loops.h.src b/numpy/core/src/umath/loops.h.src index e5c0dc855..d20b776a0 100644 --- a/numpy/core/src/umath/loops.h.src +++ b/numpy/core/src/umath/loops.h.src @@ -61,6 +61,9 @@ BOOL__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UN NPY_NO_EXPORT void @S@@TYPE@__ones_like(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data)); +NPY_NO_EXPORT void +@S@@TYPE@_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); + /**begin repeat2 * #isa = , _avx2# */ @@ -235,6 +238,8 @@ NPY_NO_EXPORT void NPY_NO_EXPORT void @TYPE@_negative(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); +NPY_NO_EXPORT void +@TYPE@_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void @TYPE@_sign(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); @@ -384,6 +389,9 @@ NPY_NO_EXPORT void TIMEDELTA_negative(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void +TIMEDELTA_positive(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); + +NPY_NO_EXPORT void TIMEDELTA_absolute(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)); NPY_NO_EXPORT void diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py index b5d096d47..12ebd5ae9 100644 --- a/numpy/core/tests/test_datetime.py +++ b/numpy/core/tests/test_datetime.py @@ -794,6 +794,12 @@ class TestDateTime(TestCase): assert_equal(np.negative(tdb), tda) assert_equal(np.negative(tdb).dtype, tda.dtype) + # positive ufunc + assert_equal(np.positive(tda), tda) + assert_equal(np.positive(tda).dtype, tda.dtype) + assert_equal(np.positive(tdb), tdb) + assert_equal(np.positive(tdb).dtype, tdb.dtype) + # absolute ufunc assert_equal(np.absolute(tdb), tda) assert_equal(np.absolute(tdb).dtype, tda.dtype) diff --git a/numpy/core/tests/test_half.py b/numpy/core/tests/test_half.py index 56b574ae8..75b74f407 100644 --- a/numpy/core/tests/test_half.py +++ b/numpy/core/tests/test_half.py @@ -323,6 +323,7 @@ class TestHalf(TestCase): assert_equal(np.conjugate(b), b) assert_equal(np.absolute(b), [2, 5, 1, 4, 3]) assert_equal(np.negative(b), [2, -5, -1, -4, -3]) + assert_equal(np.positive(b), b) assert_equal(np.sign(b), [-1, 1, 1, 1, 1]) assert_equal(np.modf(b), ([0, 0, 0, 0, 0], b)) assert_equal(np.frexp(b), ([-0.5, 0.625, 0.5, 0.5, 0.75], [2, 3, 1, 3, 2])) diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index 41108ab5f..62882eaa9 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -1057,6 +1057,7 @@ class TestBool(TestCase): def test_exceptions(self): a = np.ones(1, dtype=np.bool_) assert_raises(TypeError, np.negative, a) + assert_raises(TypeError, np.positive, a) assert_raises(TypeError, np.subtract, a, a) def test_truth_table_logical(self): @@ -1349,6 +1350,25 @@ class TestAbsoluteNegative(TestCase): np.abs(np.ones_like(d), out=d) +class TestPositive(TestCase): + def test_valid(self): + valid_dtypes = [int, float, complex, object] + for dtype in valid_dtypes: + x = np.arange(5, dtype=dtype) + result = np.positive(x) + assert_equal(x, result, err_msg=str(dtype)) + + def test_invalid(self): + with assert_raises(TypeError): + np.positive(True) + with assert_raises(TypeError): + np.positive(np.datetime64('2000-01-01')) + with assert_raises(TypeError): + np.positive(np.array(['foo'], dtype=str)) + with assert_raises(TypeError): + np.positive(np.array(['bar'], dtype=object)) + + class TestSpecialMethods(TestCase): def test_wrap(self): |