diff options
-rw-r--r-- | numpy/core/src/umath/loops.c.src | 25 | ||||
-rw-r--r-- | numpy/core/tests/test_deprecations.py | 56 | ||||
-rw-r--r-- | numpy/core/tests/test_umath.py | 11 | ||||
-rw-r--r-- | numpy/lib/polynomial.py | 18 | ||||
-rw-r--r-- | numpy/polynomial/_polybase.py | 5 | ||||
-rw-r--r-- | numpy/polynomial/polytemplate.py | 4 |
6 files changed, 80 insertions, 39 deletions
diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src index c3abd02f8..d0374fc0a 100644 --- a/numpy/core/src/umath/loops.c.src +++ b/numpy/core/src/umath/loops.c.src @@ -2562,22 +2562,28 @@ NPY_NO_EXPORT void /**begin repeat * #kind = equal, not_equal, greater, greater_equal, less, less_equal# * #OP = EQ, NE, GT, GE, LT, LE# - * #identity = NPY_TRUE, NPY_FALSE, NPY_FALSE, NPY_TRUE, NPY_FALSE, NPY_TRUE# + * #identity = NPY_TRUE, NPY_FALSE, -1*4# */ NPY_NO_EXPORT void OBJECT_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func)) { BINARY_LOOP { + int ret; + PyObject *ret_obj; PyObject *in1 = *(PyObject **)ip1; PyObject *in2 = *(PyObject **)ip2; + in1 = in1 ? in1 : Py_None; in2 = in2 ? in2 : Py_None; + /* - * Do not use RichCompareBool because it includes an identity check. - * But this is wrong for elementwise behaviour, since i.e. it says - * that NaN can be equal to NaN, and an array is equal to itself. + * Do not use RichCompareBool because it includes an identity check + * (for == and !=). + * This is wrong for elementwise behaviour, since it means + * that NaN can be equal to NaN and an array is equal to itself. */ - PyObject *ret_obj = PyObject_RichCompare(in1, in2, Py_@OP@); + ret_obj = PyObject_RichCompare(in1, in2, Py_@OP@); if (ret_obj == NULL) { +#if @identity@ != -1 if (in1 == in2) { PyErr_Clear(); if (DEPRECATE("numpy @kind@ will not check object identity " @@ -2588,10 +2594,12 @@ OBJECT_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUS *((npy_bool *)op1) = @identity@; continue; } +#endif return; } - int ret = PyObject_IsTrue(ret_obj); + ret = PyObject_IsTrue(ret_obj); if (ret == -1) { +#if @identity@ != -1 if (in1 == in2) { PyErr_Clear(); if (DEPRECATE("numpy @kind@ will not check object identity " @@ -2603,19 +2611,22 @@ OBJECT_@kind@(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUS *((npy_bool *)op1) = @identity@; continue; } +#endif return; } +#if @identity@ != -1 if ((in1 == in2) && ((npy_bool)ret != @identity@)) { if (DEPRECATE_FUTUREWARNING( "numpy @kind@ will not check object identity " "in the future. The comparison did not return the " - "same result as suggested by the identity " + "same result as suggested by the identity (`is`)) " "and will change.") < 0) { return; } *((npy_bool *)op1) = @identity@; continue; } +#endif *((npy_bool *)op1) = (npy_bool)ret; } } diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 58d62aa89..a2b30c3c9 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -397,7 +397,8 @@ class TestComparisonDepreactions(_DeprecationTestCase): # 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) + b = np.array([1, np.array([1,2,3])], dtype=object) + self.assert_deprecated(op, args=(a, b), num=None) def test_string(self): @@ -417,7 +418,6 @@ class TestComparisonDepreactions(_DeprecationTestCase): # 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) @@ -427,5 +427,57 @@ class TestComparisonDepreactions(_DeprecationTestCase): assert_raises(FutureWarning, operator.ne, np.arange(3), None) +class TestIdentityComparisonDepreactions(_DeprecationTestCase): + """This tests the equal and not_equal object ufuncs identity check + deprecation. This was due to the usage of PyObject_RichCompareBool. + + This tests that for example for `a = np.array([np.nan], dtype=object)` + `a == a` it is warned that False and not `np.nan is np.nan` is returned. + + Should be kept in sync with TestComparisonDepreactions and new tests + added when the deprecation is over. Requires only removing of @identity@ + (and blocks) from the ufunc loops.c.src of the OBJECT comparisons. + """ + + message = "numpy .* will not check object identity in the future." + + def test_identity_equality_mismatch(self): + a = np.array([np.nan], dtype=object) + + with warnings.catch_warnings(): + warnings.filterwarnings('always', '', FutureWarning) + assert_warns(FutureWarning, np.equal, a, a) + assert_warns(FutureWarning, np.not_equal, a, a) + + with warnings.catch_warnings(): + warnings.filterwarnings('error', '', FutureWarning) + assert_raises(FutureWarning, np.equal, a, a) + assert_raises(FutureWarning, np.not_equal, a, a) + # And the other do not warn: + np.less(a, a) + np.greater(a, a) + np.less_equal(a, a) + np.greater_equal(a, a) + + + def test_comparison_error(self): + class FunkyType(object): + def __eq__(self, other): + raise TypeError("I won't compare") + def __ne__(self, other): + raise TypeError("I won't compare") + + a = np.array([FunkyType()]) + self.assert_deprecated(np.equal, args=(a, a)) + self.assert_deprecated(np.not_equal, args=(a, a)) + + + def test_bool_error(self): + # The comparison result cannot be interpreted as a bool + a = np.array([np.array([1, 2, 3]), None], dtype=object) + self.assert_deprecated(np.equal, args=(a, a)) + self.assert_deprecated(np.not_equal, args=(a, a)) + + if __name__ == "__main__": run_module_suite() diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index b7b2f6308..3646fd2a9 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -1534,16 +1534,5 @@ def test_complex_nan_comparisons(): assert_equal(x == y, False, err_msg="%r == %r" % (x, y)) -def test_object_array_comparison(): - obj_array = np.arange(3) - a = np.array([obj_array, 1]) - # Should raise an error because (obj_array == obj_array) is not a bool. - # At this time, a == a would return False because of the error. - assert_raises(ValueError, np.equal, a, a) - - # Check the same for NaN, when it is the *same* NaN. - a = np.array([np.nan]) - assert_equal(a == a, [False]) - if __name__ == "__main__": run_module_suite() diff --git a/numpy/lib/polynomial.py b/numpy/lib/polynomial.py index e85e957e0..10ae32a60 100644 --- a/numpy/lib/polynomial.py +++ b/numpy/lib/polynomial.py @@ -1193,24 +1193,12 @@ class poly1d(object): __rtruediv__ = __rdiv__ def __eq__(self, other): - dim = min(self.coeffs.shape[0], other.coeffs.shape[0]) - if (self.coeffs[-dim:] != other.coeffs[-dim:]).any(): + if self.coeffs.shape != other.coeffs.shape: return False - elif (self.coeffs[:-dim] != 0).any(): - return False - elif (other.coeffs[:-dim] != 0).any(): - return False - return True + return (self.coeffs == other.coeffs).all() def __ne__(self, other): - 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 + return not self.__eq__(other) def __setattr__(self, key, val): raise ValueError("Attributes cannot be changed this way.") diff --git a/numpy/polynomial/_polybase.py b/numpy/polynomial/_polybase.py index 83119579c..23608c74a 100644 --- a/numpy/polynomial/_polybase.py +++ b/numpy/polynomial/_polybase.py @@ -438,6 +438,7 @@ class ABCPolyBase(object): res = (isinstance(other, self.__class__) and np.all(self.domain == other.domain) and np.all(self.window == other.window) and + (self.coef.shape == other.coef.shape) and np.all(self.coef == other.coef)) return res @@ -792,7 +793,7 @@ class ABCPolyBase(object): """ if domain is None: domain = pu.getdomain(x) - elif isinstance(domain, list) and len(domain) == 0: + elif type(domain) is list and len(domain) == 0: domain = cls.domain if window is None: @@ -836,7 +837,7 @@ class ABCPolyBase(object): [roots] = pu.as_series([roots], trim=False) if domain is None: domain = pu.getdomain(roots) - elif isinstance(domain, list) and len(domain) == 0: + elif type(domain) is list and len(domain) == 0: domain = cls.domain if window is None: diff --git a/numpy/polynomial/polytemplate.py b/numpy/polynomial/polytemplate.py index b0006407b..e68dd18ef 100644 --- a/numpy/polynomial/polytemplate.py +++ b/numpy/polynomial/polytemplate.py @@ -780,10 +780,10 @@ class $name(pu.PolyBase) : """ if domain is None: domain = pu.getdomain(x) - elif len(domain) == 0: + elif type(domain) is list and len(domain) == 0: domain = $domain - if len(window) == 0: + if type(window) is list and len(window) == 0: window = $domain xnew = pu.mapdomain(x, domain, window) |