Skip to content

ENH: add np.positive ufunc #8967

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/release/1.13.0-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
=======

Expand Down
4 changes: 2 additions & 2 deletions doc/source/reference/c-api.array.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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**,
Expand Down
1 change: 1 addition & 0 deletions doc/source/reference/ufuncs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,7 @@ Math operations
true_divide
floor_divide
negative
positive
power
remainder
mod
Expand Down
8 changes: 8 additions & 0 deletions numpy/core/code_generators/generate_umath.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,14 @@ def english_upper(s):
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'),
Expand Down
21 changes: 21 additions & 0 deletions numpy/core/code_generators/ufunc_docstrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2537,6 +2537,27 @@ def add_newdoc(place, name, doc):

""")

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.
Expand Down
8 changes: 8 additions & 0 deletions numpy/core/src/umath/funcs.inc.src
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,14 @@ nc_neg@c@(@ctype@ *a, @ctype@ *r)
return;
}

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)
{
Expand Down
33 changes: 33 additions & 0 deletions numpy/core/src/umath/loops.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -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#
Expand Down Expand Up @@ -1184,6 +1190,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))
{
Expand Down Expand Up @@ -1885,6 +1900,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))
{
Expand Down Expand Up @@ -2188,6 +2212,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))
{
Expand Down
8 changes: 8 additions & 0 deletions numpy/core/src/umath/loops.h.src
Original file line number Diff line number Diff line change
Expand Up @@ -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#
*/
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -383,6 +388,9 @@ C@TYPE@_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNU
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));

Expand Down
6 changes: 6 additions & 0 deletions numpy/core/tests/test_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,12 @@ def test_datetime_unary(self):
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)
Expand Down
1 change: 1 addition & 0 deletions numpy/core/tests/test_half.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ def test_half_ufuncs(self):
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]))
Expand Down
20 changes: 20 additions & 0 deletions numpy/core/tests/test_umath.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -1349,6 +1350,25 @@ def test_lower_align(self):
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):

Expand Down
5 changes: 3 additions & 2 deletions numpy/lib/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -174,5 +174,6 @@ def __repr__(self):

# 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')
2 changes: 1 addition & 1 deletion numpy/lib/tests/test_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def test_unary_methods(self):
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)))
Expand Down