diff options
author | Sebastian Berg <sebastian@sipsolutions.net> | 2013-12-05 22:53:26 +0100 |
---|---|---|
committer | Sebastian Berg <sebastian@sipsolutions.net> | 2014-02-12 13:50:34 +0100 |
commit | 58e9e27c0c110f9be1558a53fb547dc1abc76fa4 (patch) | |
tree | 79f5937059690e0d9188a9bada96f03a1661402b | |
parent | f57c77b88a735d5f49a407881777ff2e9f3b1be2 (diff) | |
download | numpy-58e9e27c0c110f9be1558a53fb547dc1abc76fa4.tar.gz |
DEP: Deprecate boolean `-` operations
Boolean - is not well defined, especially the unary and
binary operator are not compatible. In general boolean
minus seems to have no real application and does not do
what might be expected.
All "allclose" type functions (numpy, tests, masked) have to
now check for boolean to avoid the deprecation warning. In
the future one could think about removing it again and just
allowing the upcast.
-rw-r--r-- | numpy/core/code_generators/generate_umath.py | 2 | ||||
-rw-r--r-- | numpy/core/numeric.py | 7 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_type_resolution.c | 46 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_type_resolution.h | 7 | ||||
-rw-r--r-- | numpy/core/tests/test_defchararray.py | 6 | ||||
-rw-r--r-- | numpy/core/tests/test_deprecations.py | 23 | ||||
-rw-r--r-- | numpy/core/tests/test_regression.py | 5 | ||||
-rw-r--r-- | numpy/ma/core.py | 17 | ||||
-rw-r--r-- | numpy/ma/extras.py | 4 | ||||
-rw-r--r-- | numpy/testing/utils.py | 7 |
10 files changed, 109 insertions, 15 deletions
diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py index e02cb8709..e3c9cf28b 100644 --- a/numpy/core/code_generators/generate_umath.py +++ b/numpy/core/code_generators/generate_umath.py @@ -369,7 +369,7 @@ defdict = { '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 39e5a4cd5..6b078ae31 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -2154,7 +2154,12 @@ 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))) + if not x.dtype.kind == 'b' and not y.dtype.kind == 'b': + diff = abs(x - y) + else: + diff = x ^ y + + r = all(less_equal(diff, 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 12d8e406b..ffdb15bbe 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 a1241827e..a1e28d75b 100644 --- a/numpy/core/src/umath/ufunc_type_resolution.h +++ b/numpy/core/src/umath/ufunc_type_resolution.h @@ -16,6 +16,13 @@ PyUFunc_SimpleUnaryOperationTypeResolver(PyUFuncObject *ufunc, 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, PyArrayObject **operands, diff --git a/numpy/core/tests/test_defchararray.py b/numpy/core/tests/test_defchararray.py index 09fcff0d0..c210f8bf6 100644 --- a/numpy/core/tests/test_defchararray.py +++ b/numpy/core/tests/test_defchararray.py @@ -128,9 +128,9 @@ class TestWhitespace(TestCase): 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 41f1cf744..f7fbb94e5 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -343,5 +343,28 @@ class TestMultipleEllipsisDeprecation(_DeprecationTestCase): 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_regression.py b/numpy/core/tests/test_regression.py index 9f2abc5c0..cb3791daf 100644 --- a/numpy/core/tests/test_regression.py +++ b/numpy/core/tests/test_regression.py @@ -447,7 +447,10 @@ class TestRegression(TestCase): 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 42787e3c7..c62e55c45 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -6923,16 +6923,25 @@ def allclose (a, b, masked_equal=True, rtol=1e-5, atol=1e-8): return False # No infs at all if not np.any(xinf): - d = filled(umath.less_equal(umath.absolute(x - y), - atol + rtol * umath.absolute(y)), + if not x.dtype.kind == 'b' and not y.dtype.kind == 'b': + diff = umath.absolute(x - y) + else: + diff = x ^ y + + d = filled(umath.less_equal(diff, 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)), + + if not x.dtype.kind == 'b' and not y.dtype.kind == 'b': + diff = umath.absolute(x - y) + else: + diff = x ^ y + + d = filled(umath.less_equal(diff, atol + rtol * umath.absolute(y)), masked_equal) return np.all(d) diff --git a/numpy/ma/extras.py b/numpy/ma/extras.py index 058bde710..c2d105584 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/testing/utils.py b/numpy/testing/utils.py index 2a99fe5cb..82aa1e39c 100644 --- a/numpy/testing/utils.py +++ b/numpy/testing/utils.py @@ -810,7 +810,12 @@ def assert_array_almost_equal(x, y, decimal=6, err_msg='', verbose=True): y = y[~yinfid] except (TypeError, NotImplementedError): pass - z = abs(x-y) + + if x.dtype.kind == 'b' and y.dtype.kind == 'b': + z = x ^ y + else: + z = abs(x-y) + if not issubdtype(z.dtype, number): z = z.astype(float_) # handle object arrays return around(z, decimal) <= 10.0**(-decimal) |