diff options
author | Sebastian Berg <sebastian@sipsolutions.net> | 2013-07-25 16:10:54 +0200 |
---|---|---|
committer | Sebastian Berg <sebastian@sipsolutions.net> | 2014-05-04 18:14:11 +0200 |
commit | 9b8f6c72caea0c6f3fa08b304135239636e4f165 (patch) | |
tree | b3418650ed740be1bb369f5ab2979749d4d27919 | |
parent | 84831ca7b7926bf1c73e1702201e7591c55588a3 (diff) | |
download | numpy-9b8f6c72caea0c6f3fa08b304135239636e4f165.tar.gz |
DEP: Deprecate that comparisons ignore errors.
This means that for example broadcasting errors get raised.
The array_equiv function is changed to explicitely test
if broadcasting is possible. It may be nice to do this
test differently, but I am not sure if that is possible.
Create a FutureWarning for comparisons to None, which
should result in areal elementwise (object) comparisons.
Slightly adepted a wrong test.
Poly changes: Some changes in the polycode was necessary,
the one is probably a bug fix, the other needs to be
thought over, since len check is not perfect maybe, since
it is more liekly to raise raise an error.
Closes gh-3759 and gh-1608
-rw-r--r-- | numpy/core/numeric.py | 6 | ||||
-rw-r--r-- | numpy/core/src/multiarray/arrayobject.c | 26 | ||||
-rw-r--r-- | numpy/core/tests/test_deprecations.py | 54 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray.py | 5 | ||||
-rw-r--r-- | numpy/lib/polynomial.py | 18 | ||||
-rw-r--r-- | numpy/polynomial/polytemplate.py | 6 |
6 files changed, 105 insertions, 10 deletions
diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 8c569ea15..7962f9679 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -2388,10 +2388,12 @@ def array_equiv(a1, a2): except: return False try: - return bool(asarray(a1 == a2).all()) - except ValueError: + multiarray.broadcast(a1, a2) + except: return False + return bool(asarray(a1 == a2).all()) + _errdict = {"ignore":ERR_IGNORE, "warn":ERR_WARN, diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c index a09e0e3b9..cd0912510 100644 --- a/numpy/core/src/multiarray/arrayobject.c +++ b/numpy/core/src/multiarray/arrayobject.c @@ -1292,6 +1292,10 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op) break; case Py_EQ: if (other == Py_None) { + if (DEPRECATE_FUTUREWARNING("comparison to `None` will result in " + "an elementwise object comparison in the future.") < 0) { + return NULL; + } Py_INCREF(Py_False); return Py_False; } @@ -1347,13 +1351,26 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op) * indicate that */ if (result == NULL) { + /* + * Comparisons should raise errors when element-wise comparison + * is not possible. + */ PyErr_Clear(); + if (DEPRECATE("elementwise comparison failed; " + "this will raise the error in the future.") < 0) { + return NULL; + } + Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } break; case Py_NE: if (other == Py_None) { + if (DEPRECATE_FUTUREWARNING("comparison to `None` will result in " + "an elementwise object comparison in the future.") < 0) { + return NULL; + } Py_INCREF(Py_True); return Py_True; } @@ -1404,7 +1421,16 @@ array_richcompare(PyArrayObject *self, PyObject *other, int cmp_op) } if (result == NULL) { + /* + * Comparisons should raise errors when element-wise comparison + * is not possible. + */ PyErr_Clear(); + if (DEPRECATE("elementwise comparison failed; " + "this will raise the error in the future.") < 0) { + return NULL; + } + Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 6e559ab26..58d62aa89 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -86,7 +86,7 @@ class _DeprecationTestCase(object): elif not ignore_others: raise AssertionError("expected DeprecationWarning but %s given" % warning.category) - if num_found != num: + if num is not None and num_found != num: raise AssertionError("%i warnings found but %i expected" % (len(self.log), num)) @@ -375,5 +375,57 @@ class TestRankDeprecation(_DeprecationTestCase): assert_warns(np.VisibleDeprecationWarning, np.rank, a) +class TestComparisonDepreactions(_DeprecationTestCase): + """This tests the deprecation, for non-elementwise comparison logic. + This used to mean that when an error occured during element-wise comparison + (i.e. broadcasting) NotImplemented was returned, but also in the comparison + itself, False was given instead of the error. + + Also test FutureWarning for the None comparison. + """ + + message = "elementwise comparison failed; " \ + "this will raise the error in the future." + + def test_normal_types(self): + for op in (operator.eq, operator.ne): + # Broadcasting errors: + self.assert_deprecated(op, args=(np.zeros(3), [])) + a = np.zeros(3, dtype='i,i') + # (warning is issued a couple of times here) + self.assert_deprecated(op, args=(a, a[:-1]), num=None) + + # Element comparison error (numpy array can't be compared). + a = np.array([1, np.array([1,2,3])], dtype=object) + self.assert_deprecated(op, args=(a, a), num=None) + + + def test_string(self): + # For two string arrays, strings always raised the broadcasting error: + a = np.array(['a', 'b']) + b = np.array(['a', 'b', 'c']) + assert_raises(ValueError, lambda x, y: x == y, a, b) + + # The empty list is not cast to string, this is only to document + # that fact (it likely should be changed). This means that the + # following works (and returns False) due to dtype mismatch: + a == [] + + + def test_none_comparison(self): + # Test comparison of None, which should result in elementwise + # comparison in the future. [1, 2] == None should be [False, False]. + with warnings.catch_warnings(): + warnings.filterwarnings('always', '', FutureWarning) + a = np.array([1, 2]) + assert_warns(FutureWarning, operator.eq, np.arange(3), None) + assert_warns(FutureWarning, operator.ne, np.arange(3), None) + + with warnings.catch_warnings(): + warnings.filterwarnings('error', '', FutureWarning) + assert_raises(FutureWarning, operator.eq, np.arange(3), None) + assert_raises(FutureWarning, operator.ne, np.arange(3), None) + + if __name__ == "__main__": run_module_suite() diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 51e4fc650..194ba0e37 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -561,8 +561,9 @@ class TestStructured(TestCase): a = np.zeros((3, 5), dtype=[('a', ('i4', (2, 2)))]) a['a'] = np.arange(60).reshape(3, 5, 2, 2) - # Since the subarray is always in C-order, these aren't equal - assert_(np.any(a['a'].T != a.T['a'])) + # Since the subarray is always in C-order, a transpose + # does not swap the subarray: + assert_array_equal(a.T['a'], a['a'].transpose(1, 0, 2, 3)) # In Fortran order, the subarray gets appended # like in all other cases, not prepended as a special case diff --git a/numpy/lib/polynomial.py b/numpy/lib/polynomial.py index 0dcb85bb2..e85e957e0 100644 --- a/numpy/lib/polynomial.py +++ b/numpy/lib/polynomial.py @@ -1193,10 +1193,24 @@ class poly1d(object): __rtruediv__ = __rdiv__ def __eq__(self, other): - return NX.alltrue(self.coeffs == other.coeffs) + dim = min(self.coeffs.shape[0], other.coeffs.shape[0]) + if (self.coeffs[-dim:] != other.coeffs[-dim:]).any(): + return False + elif (self.coeffs[:-dim] != 0).any(): + return False + elif (other.coeffs[:-dim] != 0).any(): + return False + return True def __ne__(self, other): - return NX.any(self.coeffs != other.coeffs) + dim = min(self.coeffs.shape[0], other.coeffs.shape[0]) + if (self.coeffs[-dim:] != other.coeffs[-dim:]).any(): + return True + elif (self.coeffs[:-dim] != 0).any(): + return True + elif (other.coeffs[:-dim] != 0).any(): + return True + return False def __setattr__(self, key, val): raise ValueError("Attributes cannot be changed this way.") diff --git a/numpy/polynomial/polytemplate.py b/numpy/polynomial/polytemplate.py index 6c60ab3b2..b0006407b 100644 --- a/numpy/polynomial/polytemplate.py +++ b/numpy/polynomial/polytemplate.py @@ -737,7 +737,7 @@ class $name(pu.PolyBase) : then a minimal domain that covers the points `x` is chosen. If ``[]`` the default domain ``$domain`` is used. The default value is $domain in numpy 1.4.x and ``None`` in later versions. - The ``'[]`` value was added in numpy 1.5.0. + The ``[]`` value was added in numpy 1.5.0. rcond : float, optional Relative condition number of the fit. Singular values smaller than this relative to the largest singular value will be @@ -780,10 +780,10 @@ class $name(pu.PolyBase) : """ if domain is None: domain = pu.getdomain(x) - elif domain == []: + elif len(domain) == 0: domain = $domain - if window == []: + if len(window) == 0: window = $domain xnew = pu.mapdomain(x, domain, window) |