diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py index e02cb87093f2..e3c9cf28bee0 100644 --- a/numpy/core/code_generators/generate_umath.py +++ b/numpy/core/code_generators/generate_umath.py @@ -369,7 +369,7 @@ def english_upper(s): 'negative': Ufunc(1, 1, None, docstrings.get('numpy.core.umath.negative'), - 'PyUFunc_SimpleUnaryOperationTypeResolver', + 'PyUFunc_NegativeTypeResolver', TD(bints+flts+timedeltaonly), TD(cmplx, f='neg'), TD(O, f='PyNumber_Negative'), diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 39e5a4cd5258..3b8e52e71d39 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -2139,6 +2139,11 @@ def allclose(a, b, rtol=1.e-5, atol=1.e-8): x = array(a, copy=False, ndmin=1) y = array(b, copy=False, ndmin=1) + # make sure y is an inexact type to avoid abs(MIN_INT); will cause + # casting of x later. + dtype = multiarray.result_type(y, 1.) + y = array(y, dtype=dtype, copy=False) + xinf = isinf(x) yinf = isinf(y) if any(xinf) or any(yinf): @@ -2154,7 +2159,7 @@ def allclose(a, b, rtol=1.e-5, atol=1.e-8): # ignore invalid fpe's with errstate(invalid='ignore'): - r = all(less_equal(abs(x-y), atol + rtol * abs(y))) + r = all(less_equal(abs(x - y), atol + rtol * abs(y))) return r diff --git a/numpy/core/src/umath/ufunc_type_resolution.c b/numpy/core/src/umath/ufunc_type_resolution.c index 12d8e406b0b6..ffdb15bbe8da 100644 --- a/numpy/core/src/umath/ufunc_type_resolution.c +++ b/numpy/core/src/umath/ufunc_type_resolution.c @@ -367,6 +367,34 @@ PyUFunc_SimpleUnaryOperationTypeResolver(PyUFuncObject *ufunc, return 0; } + +NPY_NO_EXPORT int +PyUFunc_NegativeTypeResolver(PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArrayObject **operands, + PyObject *type_tup, + PyArray_Descr **out_dtypes) +{ + int ret; + ret = PyUFunc_SimpleUnaryOperationTypeResolver(ufunc, casting, operands, + type_tup, out_dtypes); + if (ret < 0) { + return ret; + } + + /* The type resolver would have upcast already */ + if (out_dtypes[0]->type_num == NPY_BOOL) { + if (DEPRECATE("numpy boolean negative (the unary `-` operator) is " + "deprecated, use the bitwise_xor (the `^` operator) " + "or the logical_xor function instead.") < 0) { + return -1; + } + } + + return ret; +} + + /* * The ones_like function shouldn't really be a ufunc, but while it * still is, this provides type resolution that always forces UNSAFE @@ -762,8 +790,22 @@ PyUFunc_SubtractionTypeResolver(PyUFuncObject *ufunc, /* Use the default when datetime and timedelta are not involved */ if (!PyTypeNum_ISDATETIME(type_num1) && !PyTypeNum_ISDATETIME(type_num2)) { - return PyUFunc_SimpleBinaryOperationTypeResolver(ufunc, casting, - operands, type_tup, out_dtypes); + int ret; + ret = PyUFunc_SimpleBinaryOperationTypeResolver(ufunc, casting, + operands, type_tup, out_dtypes); + if (ret < 0) { + return ret; + } + + /* The type resolver would have upcast already */ + if (out_dtypes[0]->type_num == NPY_BOOL) { + if (DEPRECATE("numpy boolean subtract (the binary `-` operator) is " + "deprecated, use the bitwise_xor (the `^` operator) " + "or the logical_xor function instead.") < 0) { + return -1; + } + } + return ret; } if (type_num1 == NPY_TIMEDELTA) { diff --git a/numpy/core/src/umath/ufunc_type_resolution.h b/numpy/core/src/umath/ufunc_type_resolution.h index a1241827e100..a1e28d75b9d0 100644 --- a/numpy/core/src/umath/ufunc_type_resolution.h +++ b/numpy/core/src/umath/ufunc_type_resolution.h @@ -15,6 +15,13 @@ PyUFunc_SimpleUnaryOperationTypeResolver(PyUFuncObject *ufunc, PyObject *type_tup, PyArray_Descr **out_dtypes); +NPY_NO_EXPORT int +PyUFunc_NegativeTypeResolver(PyUFuncObject *ufunc, + NPY_CASTING casting, + PyArrayObject **operands, + PyObject *type_tup, + PyArray_Descr **out_dtypes); + NPY_NO_EXPORT int PyUFunc_OnesLikeTypeResolver(PyUFuncObject *ufunc, NPY_CASTING casting, diff --git a/numpy/core/tests/test_defchararray.py b/numpy/core/tests/test_defchararray.py index 09fcff0d0666..c210f8bf6bc6 100644 --- a/numpy/core/tests/test_defchararray.py +++ b/numpy/core/tests/test_defchararray.py @@ -128,9 +128,9 @@ def test1(self): assert_(all(self.A == self.B)) assert_(all(self.A >= self.B)) assert_(all(self.A <= self.B)) - assert_(all(negative(self.A > self.B))) - assert_(all(negative(self.A < self.B))) - assert_(all(negative(self.A != self.B))) + assert_(not any(self.A > self.B)) + assert_(not any(self.A < self.B)) + assert_(not any(self.A != self.B)) class TestChar(TestCase): def setUp(self): diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 41f1cf744e6c..f7fbb94e5b23 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -343,5 +343,28 @@ def test_basic(self): assert_raises(IndexError, a.__getitem__, ((Ellipsis, ) * 3,)) +class TestBooleanSubtractDeprecations(_DeprecationTestCase): + """Test deprecation of boolean `-`. While + and * are well + defined, - is not and even a corrected form seems to have + no real uses. + + The deprecation process was started in NumPy 1.9. + """ + message = r"numpy boolean .* \(the .* `-` operator\) is deprecated, " \ + "use the bitwise" + + def test_operator_deprecation(self): + array = np.array([True]) + generic = np.bool_(True) + + # Minus operator/subtract ufunc: + self.assert_deprecated(operator.sub, args=(array, array)) + self.assert_deprecated(operator.sub, args=(generic, generic)) + + # Unary minus/negative ufunc: + self.assert_deprecated(operator.neg, args=(array,)) + self.assert_deprecated(operator.neg, args=(generic,)) + + if __name__ == "__main__": run_module_suite() diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index ac341468c528..12a39a522b10 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -1420,6 +1420,13 @@ def test_no_parameter_modification(self): assert_array_equal(y, array([0, inf])) + def test_min_int(self): + # Could make problems because of abs(min_int) == min_int + min_int = np.iinfo(np.int_).min + a = np.array([min_int], dtype=np.int_) + assert_(allclose(a, a)) + + class TestIsclose(object): rtol = 1e-5 atol = 1e-8 diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py index 9f2abc5c0699..cb3791daf89f 100644 --- a/numpy/core/tests/test_regression.py +++ b/numpy/core/tests/test_regression.py @@ -447,7 +447,10 @@ def test_method_args(self, level=rlevel): res1 = getattr(arr, func_meth)() res2 = getattr(np, func)(arr2) if res1 is None: - assert_(abs(arr-res2).max() < 1e-8, func) + res1 = arr + + if res1.dtype.kind in 'uib': + assert_((res1 == res2).all(), func) else: assert_(abs(res1-res2).max() < 1e-8, func) diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 42787e3c70ac..16df1ea761e4 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -6916,6 +6916,13 @@ def allclose (a, b, masked_equal=True, rtol=1e-5, atol=1e-8): """ x = masked_array(a, copy=False) y = masked_array(b, copy=False) + + # make sure y is an inexact type to avoid abs(MIN_INT); will cause + # casting of x later. + dtype = np.result_type(y, 1.) + if y.dtype != dtype: + y = masked_array(y, dtype=dtype, copy=False) + m = mask_or(getmask(x), getmask(y)) xinf = np.isinf(masked_array(x, copy=False, mask=m)).filled(False) # If we have some infs, they should fall at the same place. @@ -6927,13 +6934,16 @@ def allclose (a, b, masked_equal=True, rtol=1e-5, atol=1e-8): atol + rtol * umath.absolute(y)), masked_equal) return np.all(d) + if not np.all(filled(x[xinf] == y[xinf], masked_equal)): return False x = x[~xinf] y = y[~xinf] + d = filled(umath.less_equal(umath.absolute(x - y), atol + rtol * umath.absolute(y)), masked_equal) + return np.all(d) #.............................................................................. diff --git a/numpy/ma/extras.py b/numpy/ma/extras.py index 058bde710390..c2d1055845bb 100644 --- a/numpy/ma/extras.py +++ b/numpy/ma/extras.py @@ -540,7 +540,7 @@ def average(a, axis=None, weights=None, returned=False): else: if weights is None: n = add.reduce(a, axis) - d = umath.add.reduce((-mask), axis=axis, dtype=float) + d = umath.add.reduce((~mask), axis=axis, dtype=float) else: w = filled(weights, 0.0) wsh = w.shape @@ -1735,7 +1735,7 @@ def _ezclump(mask): #def clump_masked(a): if mask.ndim > 1: mask = mask.ravel() - idx = (mask[1:] - mask[:-1]).nonzero() + idx = (mask[1:] ^ mask[:-1]).nonzero() idx = idx[0] + 1 slices = [slice(left, right) for (left, right) in zip(itertools.chain([0], idx), diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index 8d8e1c9475dd..19f13a8c4bbe 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -1995,6 +1995,10 @@ def test_allclose(self): a[0] = 0 self.assertTrue(allclose(a, 0, masked_equal=True)) + # Test that the function works for MIN_INT integer typed arrays + a = masked_array([np.iinfo(np.int_).min], dtype=np.int_) + self.assertTrue(allclose(a, a)) + def test_allany(self): # Checks the any/all methods/functions. x = np.array([[0.13, 0.26, 0.90], diff --git a/numpy/testing/tests/test_utils.py b/numpy/testing/tests/test_utils.py index 94fc4d655815..5956a42943ac 100644 --- a/numpy/testing/tests/test_utils.py +++ b/numpy/testing/tests/test_utils.py @@ -53,6 +53,9 @@ def test_objarray(self): a = np.array([1, 1], dtype=np.object) self._test_equal(a, 1) + def test_array_likes(self): + self._test_equal([1, 2, 3], (1, 2, 3)) + class TestArrayEqual(_GenericTest, unittest.TestCase): def setUp(self): self._assert_func = assert_array_equal @@ -373,6 +376,11 @@ def test_simple(self): assert_allclose(6, 10, rtol=0.5) self.assertRaises(AssertionError, assert_allclose, 10, 6, rtol=0.5) + def test_min_int(self): + a = np.array([np.iinfo(np.int_).min], dtype=np.int_) + # Should not raise: + assert_allclose(a, a) + class TestArrayAlmostEqualNulp(unittest.TestCase): @dec.knownfailureif(True, "Github issue #347") diff --git a/numpy/testing/utils.py b/numpy/testing/utils.py index 2a99fe5cb44f..97908c7e8117 100644 --- a/numpy/testing/utils.py +++ b/numpy/testing/utils.py @@ -793,7 +793,7 @@ def assert_array_almost_equal(x, y, decimal=6, err_msg='', verbose=True): y: array([ 1. , 2.33333, 5. ]) """ - from numpy.core import around, number, float_ + from numpy.core import around, number, float_, result_type, array from numpy.core.numerictypes import issubdtype from numpy.core.fromnumeric import any as npany def compare(x, y): @@ -810,13 +810,22 @@ def compare(x, y): y = y[~yinfid] except (TypeError, NotImplementedError): pass + + # make sure y is an inexact type to avoid abs(MIN_INT); will cause + # casting of x later. + dtype = result_type(y, 1.) + y = array(y, dtype=dtype, copy=False) z = abs(x-y) + if not issubdtype(z.dtype, number): z = z.astype(float_) # handle object arrays + return around(z, decimal) <= 10.0**(-decimal) + assert_array_compare(compare, x, y, err_msg=err_msg, verbose=verbose, header=('Arrays are not almost equal to %d decimals' % decimal)) + def assert_array_less(x, y, err_msg='', verbose=True): """ Raise an assertion if two array_like objects are not ordered by less than.