summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Berg <sebastian@sipsolutions.net>2013-12-05 22:53:26 +0100
committerSebastian Berg <sebastian@sipsolutions.net>2014-02-12 13:50:34 +0100
commit58e9e27c0c110f9be1558a53fb547dc1abc76fa4 (patch)
tree79f5937059690e0d9188a9bada96f03a1661402b
parentf57c77b88a735d5f49a407881777ff2e9f3b1be2 (diff)
downloadnumpy-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.py2
-rw-r--r--numpy/core/numeric.py7
-rw-r--r--numpy/core/src/umath/ufunc_type_resolution.c46
-rw-r--r--numpy/core/src/umath/ufunc_type_resolution.h7
-rw-r--r--numpy/core/tests/test_defchararray.py6
-rw-r--r--numpy/core/tests/test_deprecations.py23
-rw-r--r--numpy/core/tests/test_regression.py5
-rw-r--r--numpy/ma/core.py17
-rw-r--r--numpy/ma/extras.py4
-rw-r--r--numpy/testing/utils.py7
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)