diff options
author | Eric Wieser <wieser.eric@gmail.com> | 2017-05-01 10:28:01 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-01 10:28:01 +0100 |
commit | d46df62746995481439b650790589e60a8070172 (patch) | |
tree | 6e44adb9da6a80fa3e25e793239a200c5147717f | |
parent | aa4f6080217964bdeddc9918764470b821955b1b (diff) | |
parent | e799be5f6a1bb2e0a294a1b0f03a1dc5333f529b (diff) | |
download | numpy-d46df62746995481439b650790589e60a8070172.tar.gz |
Merge pull request #8967 from shoyer/positive
ENH: add np.positive ufunc
-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 | ||||
-rw-r--r-- | numpy/lib/mixins.py | 5 | ||||
-rw-r--r-- | numpy/lib/tests/test_mixins.py | 2 |
13 files changed, 117 insertions, 5 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 efe368775..f13c056c3 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): diff --git a/numpy/lib/mixins.py b/numpy/lib/mixins.py index b5231e372..bbeed1437 100644 --- a/numpy/lib/mixins.py +++ b/numpy/lib/mixins.py @@ -70,8 +70,8 @@ class NDArrayOperatorsMixin(object): implement. This class does not yet implement the special operators corresponding - to ``divmod``, unary ``+`` or ``matmul`` (``@``), because these operation - do not yet have corresponding NumPy ufuncs. + to ``divmod`` or ``matmul`` (``@``), because these operation do not yet + have corresponding NumPy ufuncs. It is useful for writing classes that do not inherit from `numpy.ndarray`, but that should support arithmetic and numpy universal functions like @@ -174,5 +174,6 @@ class NDArrayOperatorsMixin(object): # unary methods __neg__ = _unary_method(um.negative, 'neg') + __pos__ = _unary_method(um.positive, 'pos') __abs__ = _unary_method(um.absolute, 'abs') __invert__ = _unary_method(um.invert, 'invert') diff --git a/numpy/lib/tests/test_mixins.py b/numpy/lib/tests/test_mixins.py index 57c4a4cd8..287d4ed29 100644 --- a/numpy/lib/tests/test_mixins.py +++ b/numpy/lib/tests/test_mixins.py @@ -143,7 +143,7 @@ class TestNDArrayOperatorsMixin(TestCase): array = np.array([-1, 0, 1, 2]) array_like = ArrayLike(array) for op in [operator.neg, - # pos is not yet implemented + operator.pos, abs, operator.invert]: _assert_equal_type_and_value(op(array_like), ArrayLike(op(array))) |