summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/code_generators/generate_umath.py7
-rw-r--r--numpy/core/code_generators/ufunc_docstrings.py50
-rw-r--r--numpy/core/src/multiarray/number.c39
-rw-r--r--numpy/core/src/multiarray/number.h1
-rw-r--r--numpy/core/src/umath/loops.c.src66
-rw-r--r--numpy/core/src/umath/loops.h.src6
-rw-r--r--numpy/core/tests/test_half.py1
-rw-r--r--numpy/core/tests/test_multiarray.py63
-rw-r--r--numpy/core/tests/test_scalarmath.py102
-rw-r--r--numpy/core/tests/test_umath.py85
-rw-r--r--numpy/lib/arraysetops.py1
-rw-r--r--numpy/lib/mixins.py8
-rw-r--r--numpy/lib/tests/test_mixins.py92
13 files changed, 332 insertions, 189 deletions
diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py
index 45476f931..2241618f7 100644
--- a/numpy/core/code_generators/generate_umath.py
+++ b/numpy/core/code_generators/generate_umath.py
@@ -786,6 +786,13 @@ defdict = {
TD(intflt),
TD(O, f='PyNumber_Remainder'),
),
+'divmod':
+ Ufunc(2, 2, None,
+ docstrings.get('numpy.core.umath.divmod'),
+ None,
+ TD(intflt),
+ TD(O, f='PyNumber_Divmod'),
+ ),
'hypot':
Ufunc(2, 1, Zero,
docstrings.get('numpy.core.umath.hypot'),
diff --git a/numpy/core/code_generators/ufunc_docstrings.py b/numpy/core/code_generators/ufunc_docstrings.py
index c783d4595..11f956b81 100644
--- a/numpy/core/code_generators/ufunc_docstrings.py
+++ b/numpy/core/code_generators/ufunc_docstrings.py
@@ -1290,6 +1290,7 @@ add_newdoc('numpy.core.umath', 'floor_divide',
See Also
--------
remainder : Remainder complementary to floor_divide.
+ divmod : Simultaneous floor division and remainder.
divide : Standard division.
floor : Round a number to the nearest integer toward minus infinity.
ceil : Round a number to the nearest integer toward infinity.
@@ -2509,6 +2510,11 @@ add_newdoc('numpy.core.umath', 'modf',
-----
For integer input the return values are floats.
+ See Also
+ --------
+ divmod : ``divmod(x, 1)`` is equivalent to ``modf`` with the return values
+ switched, except it always has a positive remainder.
+
Examples
--------
>>> np.modf([0, 3.5])
@@ -2576,6 +2582,8 @@ add_newdoc('numpy.core.umath', 'positive',
"""
Numerical positive, element-wise.
+ .. versionadded:: 1.13.0
+
Parameters
----------
x : array_like or scalar
@@ -2878,6 +2886,7 @@ add_newdoc('numpy.core.umath', 'remainder',
See Also
--------
floor_divide : Equivalent of Python ``//`` operator.
+ divmod : Simultaneous floor division and remainder.
fmod : Equivalent of the Matlab(TM) ``rem`` function.
divide, floor
@@ -2895,6 +2904,47 @@ add_newdoc('numpy.core.umath', 'remainder',
""")
+add_newdoc('numpy.core.umath', 'divmod',
+ """
+ Return element-wise quotient and remainder simultaneously.
+
+ .. versionadded:: 1.13.0
+
+ ``np.divmod(x, y)`` is equivalent to ``(x // y, x % y)``, but faster
+ because it avoids redundant work. It is used to implement the Python
+ built-in function ``divmod`` on NumPy arrays.
+
+ Parameters
+ ----------
+ x1 : array_like
+ Dividend array.
+ x2 : array_like
+ Divisor array.
+ out : tuple of ndarray, optional
+ Arrays into which the output is placed. Their types are preserved and
+ must be of the right shape to hold the output.
+
+ Returns
+ -------
+ out1 : ndarray
+ Element-wise quotient resulting from floor division.
+ out2 : ndarray
+ Element-wise remainder from floor division.
+
+ See Also
+ --------
+ floor_divide : Equivalent to Python's ``//`` operator.
+ remainder : Equivalent to Python's ``%`` operator.
+ modf : Equivalent to ``divmod(x, 1)`` for positive ``x`` with the return
+ values switched.
+
+ Examples
+ --------
+ >>> np.divmod(np.arange(5), 3)
+ (array([0, 0, 0, 1, 1]), array([0, 1, 2, 0, 1]))
+
+ """)
+
add_newdoc('numpy.core.umath', 'right_shift',
"""
Shift the bits of an integer to the right.
diff --git a/numpy/core/src/multiarray/number.c b/numpy/core/src/multiarray/number.c
index d6598cdb6..b8239c972 100644
--- a/numpy/core/src/multiarray/number.c
+++ b/numpy/core/src/multiarray/number.c
@@ -83,6 +83,7 @@ PyArray_SetNumericOps(PyObject *dict)
SET(multiply);
SET(divide);
SET(remainder);
+ SET(divmod);
SET(power);
SET(square);
SET(reciprocal);
@@ -135,6 +136,7 @@ PyArray_GetNumericOps(void)
GET(multiply);
GET(divide);
GET(remainder);
+ GET(divmod);
GET(power);
GET(square);
GET(reciprocal);
@@ -344,6 +346,12 @@ array_remainder(PyArrayObject *m1, PyObject *m2)
return PyArray_GenericBinaryFunction(m1, m2, n_ops.remainder);
}
+static PyObject *
+array_divmod(PyArrayObject *m1, PyObject *m2)
+{
+ BINOP_GIVE_UP_IF_NEEDED(m1, m2, nb_divmod, array_divmod);
+ return PyArray_GenericBinaryFunction(m1, m2, n_ops.divmod);
+}
#if PY_VERSION_HEX >= 0x03050000
/* Need this to be version dependent on account of the slot check */
@@ -796,37 +804,6 @@ _array_nonzero(PyArrayObject *mp)
}
-
-static PyObject *
-array_divmod(PyArrayObject *op1, PyObject *op2)
-{
- PyObject *divp, *modp, *result;
-
- BINOP_GIVE_UP_IF_NEEDED(op1, op2, nb_divmod, array_divmod);
-
- divp = array_floor_divide(op1, op2);
- if (divp == NULL) {
- return NULL;
- }
- else if(divp == Py_NotImplemented) {
- return divp;
- }
- modp = array_remainder(op1, op2);
- if (modp == NULL) {
- Py_DECREF(divp);
- return NULL;
- }
- else if(modp == Py_NotImplemented) {
- Py_DECREF(divp);
- return modp;
- }
- result = Py_BuildValue("OO", divp, modp);
- Py_DECREF(divp);
- Py_DECREF(modp);
- return result;
-}
-
-
NPY_NO_EXPORT PyObject *
array_int(PyArrayObject *v)
{
diff --git a/numpy/core/src/multiarray/number.h b/numpy/core/src/multiarray/number.h
index 86f681c10..113fc2475 100644
--- a/numpy/core/src/multiarray/number.h
+++ b/numpy/core/src/multiarray/number.h
@@ -7,6 +7,7 @@ typedef struct {
PyObject *multiply;
PyObject *divide;
PyObject *remainder;
+ PyObject *divmod;
PyObject *power;
PyObject *square;
PyObject *reciprocal;
diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src
index e88b87b5c..40ebc119a 100644
--- a/numpy/core/src/umath/loops.c.src
+++ b/numpy/core/src/umath/loops.c.src
@@ -1114,6 +1114,34 @@ NPY_NO_EXPORT void
}
}
+NPY_NO_EXPORT void
+@TYPE@_divmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP_TWO_OUT {
+ const @type@ in1 = *(@type@ *)ip1;
+ const @type@ in2 = *(@type@ *)ip2;
+ /* see FIXME note for divide above */
+ if (in2 == 0 || (in1 == NPY_MIN_@TYPE@ && in2 == -1)) {
+ npy_set_floatstatus_divbyzero();
+ *((@type@ *)op1) = 0;
+ *((@type@ *)op2) = 0;
+ }
+ else {
+ /* handle mixed case the way Python does */
+ const @type@ quo = in1 / in2;
+ const @type@ rem = in1 % in2;
+ if ((in1 > 0) == (in2 > 0) || rem == 0) {
+ *((@type@ *)op1) = quo;
+ *((@type@ *)op2) = rem;
+ }
+ else {
+ *((@type@ *)op1) = quo - 1;
+ *((@type@ *)op2) = rem + in2;
+ }
+ }
+ }
+}
+
/**end repeat**/
/**begin repeat
@@ -1168,6 +1196,24 @@ NPY_NO_EXPORT void
}
}
+NPY_NO_EXPORT void
+@TYPE@_divmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP_TWO_OUT {
+ const @type@ in1 = *(@type@ *)ip1;
+ const @type@ in2 = *(@type@ *)ip2;
+ if (in2 == 0) {
+ npy_set_floatstatus_divbyzero();
+ *((@type@ *)op1) = 0;
+ *((@type@ *)op2) = 0;
+ }
+ else {
+ *((@type@ *)op1)= in1/in2;
+ *((@type@ *)op2) = in1 % in2;
+ }
+ }
+}
+
/**end repeat**/
/*
@@ -1841,6 +1887,16 @@ NPY_NO_EXPORT void
}
NPY_NO_EXPORT void
+@TYPE@_divmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP_TWO_OUT {
+ const @type@ in1 = *(@type@ *)ip1;
+ const @type@ in2 = *(@type@ *)ip2;
+ *((@type@ *)op1) = npy_divmod@c@(in1, in2, (@type@ *)op2);
+ }
+}
+
+NPY_NO_EXPORT void
@TYPE@_square(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data))
{
char * margs[] = {args[0], args[0], args[1]};
@@ -2169,6 +2225,16 @@ HALF_remainder(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNU
}
NPY_NO_EXPORT void
+HALF_divmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP_TWO_OUT {
+ const npy_half in1 = *(npy_half *)ip1;
+ const npy_half in2 = *(npy_half *)ip2;
+ *((npy_half *)op1) = npy_half_divmod(in1, in2, (npy_half *)op2);
+ }
+}
+
+NPY_NO_EXPORT void
HALF_square(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data))
{
UNARY_LOOP {
diff --git a/numpy/core/src/umath/loops.h.src b/numpy/core/src/umath/loops.h.src
index c1b451c5b..4243c6522 100644
--- a/numpy/core/src/umath/loops.h.src
+++ b/numpy/core/src/umath/loops.h.src
@@ -140,6 +140,9 @@ NPY_NO_EXPORT void
NPY_NO_EXPORT void
@S@@TYPE@_remainder(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+NPY_NO_EXPORT void
+@S@@TYPE@_divmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+
/**end repeat1**/
/**end repeat**/
@@ -220,6 +223,9 @@ NPY_NO_EXPORT void
@TYPE@_remainder(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
+@TYPE@_divmod(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func));
+
+NPY_NO_EXPORT void
@TYPE@_square(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(data));
NPY_NO_EXPORT void
diff --git a/numpy/core/tests/test_half.py b/numpy/core/tests/test_half.py
index 75b74f407..7a4d36333 100644
--- a/numpy/core/tests/test_half.py
+++ b/numpy/core/tests/test_half.py
@@ -317,6 +317,7 @@ class TestHalf(TestCase):
assert_equal(np.floor_divide(a, b), [0, 0, 2, 1, 0])
assert_equal(np.remainder(a, b), [0, 1, 0, 0, 2])
+ assert_equal(np.divmod(a, b), ([0, 0, 2, 1, 0], [0, 1, 0, 0, 2]))
assert_equal(np.square(b), [4, 25, 1, 16, 9])
assert_equal(np.reciprocal(b), [-0.5, 0.199951171875, 1, 0.25, 0.333251953125])
assert_equal(np.ones_like(b), [1, 1, 1, 1, 1])
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index 8ab8c0ca7..97d801731 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -2881,7 +2881,7 @@ class TestBinop(object):
'truediv': (np.true_divide, True, float),
'floordiv': (np.floor_divide, True, float),
'mod': (np.remainder, True, float),
- 'divmod': (None, False, float),
+ 'divmod': (np.divmod, False, float),
'pow': (np.power, True, int),
'lshift': (np.left_shift, True, int),
'rshift': (np.right_shift, True, int),
@@ -2944,13 +2944,15 @@ class TestBinop(object):
def check(obj, binop_override_expected, ufunc_override_expected,
inplace_override_expected, check_scalar=True):
for op, (ufunc, has_inplace, dtype) in ops.items():
+ err_msg = ('op: %s, ufunc: %s, has_inplace: %s, dtype: %s'
+ % (op, ufunc, has_inplace, dtype))
check_objs = [np.arange(3, 5, dtype=dtype)]
if check_scalar:
check_objs.append(check_objs[0][0])
for arr in check_objs:
arr_method = getattr(arr, "__{0}__".format(op))
- def norm(result):
+ def first_out_arg(result):
if op == "divmod":
assert_(isinstance(result, tuple))
return result[0]
@@ -2959,77 +2961,78 @@ class TestBinop(object):
# arr __op__ obj
if binop_override_expected:
- assert_equal(arr_method(obj), NotImplemented)
+ assert_equal(arr_method(obj), NotImplemented, err_msg)
elif ufunc_override_expected:
- assert_equal(norm(arr_method(obj))[0],
- "__array_ufunc__")
+ assert_equal(arr_method(obj)[0], "__array_ufunc__",
+ err_msg)
else:
if (isinstance(obj, np.ndarray) and
(type(obj).__array_ufunc__ is
np.ndarray.__array_ufunc__)):
# __array__ gets ignored
- res = norm(arr_method(obj))
- assert_(res.__class__ is obj.__class__)
+ res = first_out_arg(arr_method(obj))
+ assert_(res.__class__ is obj.__class__, err_msg)
else:
assert_raises((TypeError, Coerced),
- arr_method, obj)
+ arr_method, obj, err_msg=err_msg)
# obj __op__ arr
arr_rmethod = getattr(arr, "__r{0}__".format(op))
if ufunc_override_expected:
- res = norm(arr_rmethod(obj))
- assert_equal(res[0], "__array_ufunc__")
- if ufunc is not None:
- assert_equal(res[1], ufunc)
+ res = arr_rmethod(obj)
+ assert_equal(res[0], "__array_ufunc__",
+ err_msg=err_msg)
+ assert_equal(res[1], ufunc, err_msg=err_msg)
else:
if (isinstance(obj, np.ndarray) and
(type(obj).__array_ufunc__ is
np.ndarray.__array_ufunc__)):
# __array__ gets ignored
- res = norm(arr_rmethod(obj))
- assert_(res.__class__ is obj.__class__)
+ res = first_out_arg(arr_rmethod(obj))
+ assert_(res.__class__ is obj.__class__, err_msg)
else:
# __array_ufunc__ = "asdf" creates a TypeError
assert_raises((TypeError, Coerced),
- arr_rmethod, obj)
+ arr_rmethod, obj, err_msg=err_msg)
# arr __iop__ obj
# array scalars don't have in-place operators
if has_inplace and isinstance(arr, np.ndarray):
arr_imethod = getattr(arr, "__i{0}__".format(op))
if inplace_override_expected:
- assert_equal(arr_method(obj), NotImplemented)
+ assert_equal(arr_method(obj), NotImplemented,
+ err_msg=err_msg)
elif ufunc_override_expected:
res = arr_imethod(obj)
- assert_equal(res[0], "__array_ufunc__")
- if ufunc is not None:
- assert_equal(res[1], ufunc)
- assert_(type(res[-1]["out"]) is tuple)
- assert_(res[-1]["out"][0] is arr)
+ assert_equal(res[0], "__array_ufunc__", err_msg)
+ assert_equal(res[1], ufunc, err_msg)
+ assert_(type(res[-1]["out"]) is tuple, err_msg)
+ assert_(res[-1]["out"][0] is arr, err_msg)
else:
if (isinstance(obj, np.ndarray) and
(type(obj).__array_ufunc__ is
np.ndarray.__array_ufunc__)):
# __array__ gets ignored
- assert_(arr_imethod(obj) is arr)
+ assert_(arr_imethod(obj) is arr, err_msg)
else:
assert_raises((TypeError, Coerced),
- arr_imethod, obj)
+ arr_imethod, obj,
+ err_msg=err_msg)
op_fn = getattr(operator, op, None)
if op_fn is None:
op_fn = getattr(operator, op + "_", None)
if op_fn is None:
op_fn = getattr(builtins, op)
- assert_equal(op_fn(obj, arr), "forward")
+ assert_equal(op_fn(obj, arr), "forward", err_msg)
if not isinstance(obj, np.ndarray):
if binop_override_expected:
- assert_equal(op_fn(arr, obj), "reverse")
+ assert_equal(op_fn(arr, obj), "reverse", err_msg)
elif ufunc_override_expected:
- assert_equal(norm(op_fn(arr, obj))[0],
- "__array_ufunc__")
- if ufunc_override_expected and ufunc is not None:
- assert_equal(norm(ufunc(obj, arr))[0],
- "__array_ufunc__")
+ assert_equal(op_fn(arr, obj)[0], "__array_ufunc__",
+ err_msg)
+ if ufunc_override_expected:
+ assert_equal(ufunc(obj, arr)[0], "__array_ufunc__",
+ err_msg)
# No array priority, no array_ufunc -> nothing called
check(make_obj(object), False, False, False)
diff --git a/numpy/core/tests/test_scalarmath.py b/numpy/core/tests/test_scalarmath.py
index 1cafde5a0..c76db98f8 100644
--- a/numpy/core/tests/test_scalarmath.py
+++ b/numpy/core/tests/test_scalarmath.py
@@ -189,30 +189,34 @@ class TestPower(TestCase):
assert_raises(TypeError, operator.pow, np.array(t(a)), b, c)
-class TestModulus(TestCase):
+def floordiv_and_mod(x, y):
+ return (x // y, x % y)
+
+
+def _signs(dt):
+ if dt in np.typecodes['UnsignedInteger']:
+ return (+1,)
+ else:
+ return (+1, -1)
- floordiv = operator.floordiv
- mod = operator.mod
+
+class TestModulus(TestCase):
def test_modulus_basic(self):
dt = np.typecodes['AllInteger'] + np.typecodes['Float']
- for dt1, dt2 in itertools.product(dt, dt):
- for sg1, sg2 in itertools.product((+1, -1), (+1, -1)):
- if sg1 == -1 and dt1 in np.typecodes['UnsignedInteger']:
- continue
- if sg2 == -1 and dt2 in np.typecodes['UnsignedInteger']:
- continue
- fmt = 'dt1: %s, dt2: %s, sg1: %s, sg2: %s'
- msg = fmt % (dt1, dt2, sg1, sg2)
- a = np.array(sg1*71, dtype=dt1)[()]
- b = np.array(sg2*19, dtype=dt2)[()]
- div = self.floordiv(a, b)
- rem = self.mod(a, b)
- assert_equal(div*b + rem, a, err_msg=msg)
- if sg2 == -1:
- assert_(b < rem <= 0, msg)
- else:
- assert_(b > rem >= 0, msg)
+ for op in [floordiv_and_mod, divmod]:
+ for dt1, dt2 in itertools.product(dt, dt):
+ for sg1, sg2 in itertools.product(_signs(dt1), _signs(dt2)):
+ fmt = 'op: %s, dt1: %s, dt2: %s, sg1: %s, sg2: %s'
+ msg = fmt % (op.__name__, dt1, dt2, sg1, sg2)
+ a = np.array(sg1*71, dtype=dt1)[()]
+ b = np.array(sg2*19, dtype=dt2)[()]
+ div, rem = op(a, b)
+ assert_equal(div*b + rem, a, err_msg=msg)
+ if sg2 == -1:
+ assert_(b < rem <= 0, msg)
+ else:
+ assert_(b > rem >= 0, msg)
def test_float_modulus_exact(self):
# test that float results are exact for small integers. This also
@@ -231,42 +235,42 @@ class TestModulus(TestCase):
tgtdiv = np.where((tgtdiv == 0.0) & ((b < 0) ^ (a < 0)), -0.0, tgtdiv)
tgtrem = np.where((tgtrem == 0.0) & (b < 0), -0.0, tgtrem)
- for dt in np.typecodes['Float']:
- msg = 'dtype: %s' % (dt,)
- fa = a.astype(dt)
- fb = b.astype(dt)
- # use list comprehension so a_ and b_ are scalars
- div = [self.floordiv(a_, b_) for a_, b_ in zip(fa, fb)]
- rem = [self.mod(a_, b_) for a_, b_ in zip(fa, fb)]
- assert_equal(div, tgtdiv, err_msg=msg)
- assert_equal(rem, tgtrem, err_msg=msg)
+ for op in [floordiv_and_mod, divmod]:
+ for dt in np.typecodes['Float']:
+ msg = 'op: %s, dtype: %s' % (op.__name__, dt)
+ fa = a.astype(dt)
+ fb = b.astype(dt)
+ # use list comprehension so a_ and b_ are scalars
+ div, rem = zip(*[op(a_, b_) for a_, b_ in zip(fa, fb)])
+ assert_equal(div, tgtdiv, err_msg=msg)
+ assert_equal(rem, tgtrem, err_msg=msg)
def test_float_modulus_roundoff(self):
# gh-6127
dt = np.typecodes['Float']
- for dt1, dt2 in itertools.product(dt, dt):
- for sg1, sg2 in itertools.product((+1, -1), (+1, -1)):
- fmt = 'dt1: %s, dt2: %s, sg1: %s, sg2: %s'
- msg = fmt % (dt1, dt2, sg1, sg2)
- a = np.array(sg1*78*6e-8, dtype=dt1)[()]
- b = np.array(sg2*6e-8, dtype=dt2)[()]
- div = self.floordiv(a, b)
- rem = self.mod(a, b)
- # Equal assertion should hold when fmod is used
- assert_equal(div*b + rem, a, err_msg=msg)
- if sg2 == -1:
- assert_(b < rem <= 0, msg)
- else:
- assert_(b > rem >= 0, msg)
+ for op in [floordiv_and_mod, divmod]:
+ for dt1, dt2 in itertools.product(dt, dt):
+ for sg1, sg2 in itertools.product((+1, -1), (+1, -1)):
+ fmt = 'op: %s, dt1: %s, dt2: %s, sg1: %s, sg2: %s'
+ msg = fmt % (op.__name__, dt1, dt2, sg1, sg2)
+ a = np.array(sg1*78*6e-8, dtype=dt1)[()]
+ b = np.array(sg2*6e-8, dtype=dt2)[()]
+ div, rem = op(a, b)
+ # Equal assertion should hold when fmod is used
+ assert_equal(div*b + rem, a, err_msg=msg)
+ if sg2 == -1:
+ assert_(b < rem <= 0, msg)
+ else:
+ assert_(b > rem >= 0, msg)
def test_float_modulus_corner_cases(self):
# Check remainder magnitude.
for dt in np.typecodes['Float']:
b = np.array(1.0, dtype=dt)
a = np.nextafter(np.array(0.0, dtype=dt), -b)
- rem = self.mod(a, b)
+ rem = operator.mod(a, b)
assert_(rem <= b, 'dt: %s' % dt)
- rem = self.mod(-a, -b)
+ rem = operator.mod(-a, -b)
assert_(rem >= -b, 'dt: %s' % dt)
# Check nans, inf
@@ -277,14 +281,14 @@ class TestModulus(TestCase):
fzer = np.array(0.0, dtype=dt)
finf = np.array(np.inf, dtype=dt)
fnan = np.array(np.nan, dtype=dt)
- rem = self.mod(fone, fzer)
+ rem = operator.mod(fone, fzer)
assert_(np.isnan(rem), 'dt: %s' % dt)
# MSVC 2008 returns NaN here, so disable the check.
- #rem = self.mod(fone, finf)
+ #rem = operator.mod(fone, finf)
#assert_(rem == fone, 'dt: %s' % dt)
- rem = self.mod(fone, fnan)
+ rem = operator.mod(fone, fnan)
assert_(np.isnan(rem), 'dt: %s' % dt)
- rem = self.mod(finf, fone)
+ rem = operator.mod(finf, fone)
assert_(np.isnan(rem), 'dt: %s' % dt)
diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py
index f13c056c3..51bf7c942 100644
--- a/numpy/core/tests/test_umath.py
+++ b/numpy/core/tests/test_umath.py
@@ -264,27 +264,34 @@ class TestDivision(TestCase):
assert_equal(y, [1.e+110, 0], err_msg=msg)
+def floor_divide_and_remainder(x, y):
+ return (np.floor_divide(x, y), np.remainder(x, y))
+
+
+def _signs(dt):
+ if dt in np.typecodes['UnsignedInteger']:
+ return (+1,)
+ else:
+ return (+1, -1)
+
+
class TestRemainder(TestCase):
def test_remainder_basic(self):
dt = np.typecodes['AllInteger'] + np.typecodes['Float']
- for dt1, dt2 in itertools.product(dt, dt):
- for sg1, sg2 in itertools.product((+1, -1), (+1, -1)):
- if sg1 == -1 and dt1 in np.typecodes['UnsignedInteger']:
- continue
- if sg2 == -1 and dt2 in np.typecodes['UnsignedInteger']:
- continue
- fmt = 'dt1: %s, dt2: %s, sg1: %s, sg2: %s'
- msg = fmt % (dt1, dt2, sg1, sg2)
- a = np.array(sg1*71, dtype=dt1)
- b = np.array(sg2*19, dtype=dt2)
- div = np.floor_divide(a, b)
- rem = np.remainder(a, b)
- assert_equal(div*b + rem, a, err_msg=msg)
- if sg2 == -1:
- assert_(b < rem <= 0, msg)
- else:
- assert_(b > rem >= 0, msg)
+ for op in [floor_divide_and_remainder, np.divmod]:
+ for dt1, dt2 in itertools.product(dt, dt):
+ for sg1, sg2 in itertools.product(_signs(dt1), _signs(dt2)):
+ fmt = 'op: %s, dt1: %s, dt2: %s, sg1: %s, sg2: %s'
+ msg = fmt % (op.__name__, dt1, dt2, sg1, sg2)
+ a = np.array(sg1*71, dtype=dt1)
+ b = np.array(sg2*19, dtype=dt2)
+ div, rem = op(a, b)
+ assert_equal(div*b + rem, a, err_msg=msg)
+ if sg2 == -1:
+ assert_(b < rem <= 0, msg)
+ else:
+ assert_(b > rem >= 0, msg)
def test_float_remainder_exact(self):
# test that float results are exact for small integers. This also
@@ -303,32 +310,32 @@ class TestRemainder(TestCase):
tgtdiv = np.where((tgtdiv == 0.0) & ((b < 0) ^ (a < 0)), -0.0, tgtdiv)
tgtrem = np.where((tgtrem == 0.0) & (b < 0), -0.0, tgtrem)
- for dt in np.typecodes['Float']:
- msg = 'dtype: %s' % (dt,)
- fa = a.astype(dt)
- fb = b.astype(dt)
- div = np.floor_divide(fa, fb)
- rem = np.remainder(fa, fb)
- assert_equal(div, tgtdiv, err_msg=msg)
- assert_equal(rem, tgtrem, err_msg=msg)
+ for op in [floor_divide_and_remainder, np.divmod]:
+ for dt in np.typecodes['Float']:
+ msg = 'op: %s, dtype: %s' % (op.__name__, dt)
+ fa = a.astype(dt)
+ fb = b.astype(dt)
+ div, rem = op(fa, fb)
+ assert_equal(div, tgtdiv, err_msg=msg)
+ assert_equal(rem, tgtrem, err_msg=msg)
def test_float_remainder_roundoff(self):
# gh-6127
dt = np.typecodes['Float']
- for dt1, dt2 in itertools.product(dt, dt):
- for sg1, sg2 in itertools.product((+1, -1), (+1, -1)):
- fmt = 'dt1: %s, dt2: %s, sg1: %s, sg2: %s'
- msg = fmt % (dt1, dt2, sg1, sg2)
- a = np.array(sg1*78*6e-8, dtype=dt1)
- b = np.array(sg2*6e-8, dtype=dt2)
- div = np.floor_divide(a, b)
- rem = np.remainder(a, b)
- # Equal assertion should hold when fmod is used
- assert_equal(div*b + rem, a, err_msg=msg)
- if sg2 == -1:
- assert_(b < rem <= 0, msg)
- else:
- assert_(b > rem >= 0, msg)
+ for op in [floor_divide_and_remainder, np.divmod]:
+ for dt1, dt2 in itertools.product(dt, dt):
+ for sg1, sg2 in itertools.product((+1, -1), (+1, -1)):
+ fmt = 'op: %s, dt1: %s, dt2: %s, sg1: %s, sg2: %s'
+ msg = fmt % (op.__name__, dt1, dt2, sg1, sg2)
+ a = np.array(sg1*78*6e-8, dtype=dt1)
+ b = np.array(sg2*6e-8, dtype=dt2)
+ div, rem = op(a, b)
+ # Equal assertion should hold when fmod is used
+ assert_equal(div*b + rem, a, err_msg=msg)
+ if sg2 == -1:
+ assert_(b < rem <= 0, msg)
+ else:
+ assert_(b > rem >= 0, msg)
def test_float_remainder_corner_cases(self):
# Check remainder magnitude.
diff --git a/numpy/lib/arraysetops.py b/numpy/lib/arraysetops.py
index 9a1448991..d29e555b8 100644
--- a/numpy/lib/arraysetops.py
+++ b/numpy/lib/arraysetops.py
@@ -522,6 +522,7 @@ def isin(element, test_elements, assume_unique=False, invert=False):
in1d : Flattened version of this function.
numpy.lib.arraysetops : Module with a number of other functions for
performing set operations on arrays.
+
Notes
-----
diff --git a/numpy/lib/mixins.py b/numpy/lib/mixins.py
index bbeed1437..fbdc2edfb 100644
--- a/numpy/lib/mixins.py
+++ b/numpy/lib/mixins.py
@@ -70,8 +70,7 @@ class NDArrayOperatorsMixin(object):
implement.
This class does not yet implement the special operators corresponding
- to ``divmod`` or ``matmul`` (``@``), because these operation do not yet
- have corresponding NumPy ufuncs.
+ to ``matmul`` (``@``), because ``np.matmul`` is not yet a NumPy ufunc.
It is useful for writing classes that do not inherit from `numpy.ndarray`,
but that should support arithmetic and numpy universal functions like
@@ -161,7 +160,10 @@ class NDArrayOperatorsMixin(object):
um.true_divide, 'truediv')
__floordiv__, __rfloordiv__, __ifloordiv__ = _numeric_methods(
um.floor_divide, 'floordiv')
- __mod__, __rmod__, __imod__ = _numeric_methods(um.mod, 'mod')
+ __mod__, __rmod__, __imod__ = _numeric_methods(um.remainder, 'mod')
+ __divmod__ = _binary_method(um.divmod, 'divmod')
+ __rdivmod__ = _reflected_binary_method(um.divmod, 'divmod')
+ # __idivmod__ does not exist
# TODO: handle the optional third argument for __pow__?
__pow__, __rpow__, __ipow__ = _numeric_methods(um.power, 'pow')
__lshift__, __rlshift__, __ilshift__ = _numeric_methods(
diff --git a/numpy/lib/tests/test_mixins.py b/numpy/lib/tests/test_mixins.py
index 287d4ed29..db38bdfd6 100644
--- a/numpy/lib/tests/test_mixins.py
+++ b/numpy/lib/tests/test_mixins.py
@@ -56,11 +56,47 @@ class ArrayLike(np.lib.mixins.NDArrayOperatorsMixin):
return '%s(%r)' % (type(self).__name__, self.value)
+def wrap_array_like(result):
+ if type(result) is tuple:
+ return tuple(ArrayLike(r) for r in result)
+ else:
+ return ArrayLike(result)
+
+
def _assert_equal_type_and_value(result, expected, err_msg=None):
assert_equal(type(result), type(expected), err_msg=err_msg)
- assert_equal(result.value, expected.value, err_msg=err_msg)
- assert_equal(getattr(result.value, 'dtype', None),
- getattr(expected.value, 'dtype', None), err_msg=err_msg)
+ if isinstance(result, tuple):
+ assert_equal(len(result), len(expected), err_msg=err_msg)
+ for result_item, expected_item in zip(result, expected):
+ _assert_equal_type_and_value(result_item, expected_item, err_msg)
+ else:
+ assert_equal(result.value, expected.value, err_msg=err_msg)
+ assert_equal(getattr(result.value, 'dtype', None),
+ getattr(expected.value, 'dtype', None), err_msg=err_msg)
+
+
+_ALL_BINARY_OPERATORS = [
+ operator.lt,
+ operator.le,
+ operator.eq,
+ operator.ne,
+ operator.gt,
+ operator.ge,
+ operator.add,
+ operator.sub,
+ operator.mul,
+ operator.truediv,
+ operator.floordiv,
+ # TODO: test div on Python 2, only
+ operator.mod,
+ divmod,
+ pow,
+ operator.lshift,
+ operator.rshift,
+ operator.and_,
+ operator.xor,
+ operator.or_,
+]
class TestNDArrayOperatorsMixin(TestCase):
@@ -148,52 +184,34 @@ class TestNDArrayOperatorsMixin(TestCase):
operator.invert]:
_assert_equal_type_and_value(op(array_like), ArrayLike(op(array)))
- def test_binary_methods(self):
+ def test_forward_binary_methods(self):
array = np.array([-1, 0, 1, 2])
array_like = ArrayLike(array)
- operators = [
- operator.lt,
- operator.le,
- operator.eq,
- operator.ne,
- operator.gt,
- operator.ge,
- operator.add,
- operator.sub,
- operator.mul,
- operator.truediv,
- operator.floordiv,
- # TODO: test div on Python 2, only
- operator.mod,
- # divmod is not yet implemented
- pow,
- operator.lshift,
- operator.rshift,
- operator.and_,
- operator.xor,
- operator.or_,
- ]
- for op in operators:
- expected = ArrayLike(op(array, 1))
+ for op in _ALL_BINARY_OPERATORS:
+ expected = wrap_array_like(op(array, 1))
actual = op(array_like, 1)
err_msg = 'failed for operator {}'.format(op)
_assert_equal_type_and_value(expected, actual, err_msg=err_msg)
+ def test_reflected_binary_methods(self):
+ for op in _ALL_BINARY_OPERATORS:
+ expected = wrap_array_like(op(2, 1))
+ actual = op(2, ArrayLike(1))
+ err_msg = 'failed for operator {}'.format(op)
+ _assert_equal_type_and_value(expected, actual, err_msg=err_msg)
+
def test_ufunc_at(self):
array = ArrayLike(np.array([1, 2, 3, 4]))
assert_(np.negative.at(array, np.array([0, 1])) is None)
_assert_equal_type_and_value(array, ArrayLike([-1, -2, 3, 4]))
def test_ufunc_two_outputs(self):
- def check(result):
- assert_(type(result) is tuple)
- assert_equal(len(result), 2)
- mantissa, exponent = np.frexp(2 ** -3)
- _assert_equal_type_and_value(result[0], ArrayLike(mantissa))
- _assert_equal_type_and_value(result[1], ArrayLike(exponent))
-
- check(np.frexp(ArrayLike(2 ** -3)))
- check(np.frexp(ArrayLike(np.array(2 ** -3))))
+ mantissa, exponent = np.frexp(2 ** -3)
+ expected = (ArrayLike(mantissa), ArrayLike(exponent))
+ _assert_equal_type_and_value(
+ np.frexp(ArrayLike(2 ** -3)), expected)
+ _assert_equal_type_and_value(
+ np.frexp(ArrayLike(np.array(2 ** -3))), expected)
if __name__ == "__main__":