Skip to content

Commit 2b60f5b

Browse files
committed
Merge pull request numpy#5590 from chebee7i/allclose_isclose
Make allclose use isclose.
2 parents f896f50 + e59c622 commit 2b60f5b

File tree

4 files changed

+46
-41
lines changed

4 files changed

+46
-41
lines changed

doc/release/1.10.0-notes.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,18 @@ returned values, especially for corner cases.
144144
The strings produced by ``float.hex`` look like ``0x1.921fb54442d18p+1``,
145145
so this is not the hex used to represent unsigned integer types.
146146

147+
*np.isclose* properly handles minimal values of integer dtypes
148+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
149+
In order to properly handle minimal values of integer types, *np.isclose* will
150+
now cast to the float dtype during comparisons. This aligns its behavior with
151+
what was provided by *np.allclose*.
152+
153+
*np.allclose* uses *np.isclose* internally.
154+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
155+
*np.allcose* now uses *np.isclose* internally and inherits the ability to
156+
compare NaNs as equal by setting ``equal_nan=True``. Subclasses, such as
157+
*np.ma.MaskedArray*, are also preserved now.
158+
147159

148160
Changes
149161
=======

numpy/core/numeric.py

Lines changed: 16 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,9 +1024,8 @@ def outer(a, b, out=None):
10241024
Second input vector. Input is flattened if
10251025
not already 1-dimensional.
10261026
out : (M, N) ndarray, optional
1027-
A location where the result is stored
1028-
10291027
.. versionadded:: 1.9.0
1028+
A location where the result is stored
10301029
10311030
Returns
10321031
-------
@@ -2206,42 +2205,7 @@ def identity(n, dtype=None):
22062205
from numpy import eye
22072206
return eye(n, dtype=dtype)
22082207

2209-
def _allclose_points(a, b, rtol=1.e-5, atol=1.e-8):
2210-
"""
2211-
This is the point-wise inner calculation of 'allclose', which is subtly
2212-
different from 'isclose'. Provided as a comparison routine for use in
2213-
testing.assert_allclose.
2214-
See those routines for further details.
2215-
2216-
"""
2217-
x = array(a, copy=False, ndmin=1)
2218-
y = array(b, copy=False, ndmin=1)
2219-
2220-
# make sure y is an inexact type to avoid abs(MIN_INT); will cause
2221-
# casting of x later.
2222-
dtype = multiarray.result_type(y, 1.)
2223-
y = array(y, dtype=dtype, copy=False)
2224-
2225-
xinf = isinf(x)
2226-
yinf = isinf(y)
2227-
if any(xinf) or any(yinf):
2228-
# Check that x and y have inf's only in the same positions
2229-
if not all(xinf == yinf):
2230-
return False
2231-
# Check that sign of inf's in x and y is the same
2232-
if not all(x[xinf] == y[xinf]):
2233-
return False
2234-
2235-
x = x[~xinf]
2236-
y = y[~xinf]
2237-
2238-
# ignore invalid fpe's
2239-
with errstate(invalid='ignore'):
2240-
r = less_equal(abs(x - y), atol + rtol * abs(y))
2241-
2242-
return r
2243-
2244-
def allclose(a, b, rtol=1.e-5, atol=1.e-8):
2208+
def allclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False):
22452209
"""
22462210
Returns True if two arrays are element-wise equal within a tolerance.
22472211
@@ -2262,6 +2226,10 @@ def allclose(a, b, rtol=1.e-5, atol=1.e-8):
22622226
The relative tolerance parameter (see Notes).
22632227
atol : float
22642228
The absolute tolerance parameter (see Notes).
2229+
equal_nan : bool
2230+
.. versionadded:: 1.10.0
2231+
Whether to compare NaN's as equal. If True, NaN's in `a` will be
2232+
considered equal to NaN's in `b` in the output array.
22652233
22662234
Returns
22672235
-------
@@ -2294,9 +2262,11 @@ def allclose(a, b, rtol=1.e-5, atol=1.e-8):
22942262
False
22952263
>>> np.allclose([1.0, np.nan], [1.0, np.nan])
22962264
False
2265+
>>> np.allclose([1.0, np.nan], [1.0, np.nan], equal_nan=True)
2266+
True
22972267
22982268
"""
2299-
return all(_allclose_points(a, b, rtol=rtol, atol=atol))
2269+
return all(isclose(a, b, rtol=rtol, atol=atol, equal_nan=equal_nan))
23002270

23012271
def isclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False):
23022272
"""
@@ -2366,6 +2336,13 @@ def within_tol(x, y, atol, rtol):
23662336

23672337
x = array(a, copy=False, subok=True, ndmin=1)
23682338
y = array(b, copy=False, subok=True, ndmin=1)
2339+
2340+
# Make sure y is an inexact type to avoid bad behavior on abs(MIN_INT).
2341+
# This will cause casting of x later. Also, make sure to allow subclasses
2342+
# (e.g., for numpy.ma).
2343+
dt = multiarray.result_type(y, 1.)
2344+
y = array(y, dtype=dt, copy=False, subok=True)
2345+
23692346
xfin = isfinite(x)
23702347
yfin = isfinite(y)
23712348
if all(xfin) and all(yfin):

numpy/core/tests/test_numeric.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,6 +1574,11 @@ def test_min_int(self):
15741574
assert_(allclose(a, a))
15751575

15761576

1577+
def test_equalnan(self):
1578+
x = np.array([1.0, np.nan])
1579+
assert_(allclose(x, x, equal_nan=True))
1580+
1581+
15771582
class TestIsclose(object):
15781583
rtol = 1e-5
15791584
atol = 1e-8
@@ -1664,17 +1669,25 @@ def test_equal_nan(self):
16641669
assert_array_equal(isclose(arr, arr, equal_nan=True), [True, True])
16651670

16661671
def test_masked_arrays(self):
1672+
# Make sure to test the output type when arguments are interchanged.
1673+
16671674
x = np.ma.masked_where([True, True, False], np.arange(3))
16681675
assert_(type(x) is type(isclose(2, x)))
1676+
assert_(type(x) is type(isclose(x, 2)))
16691677

16701678
x = np.ma.masked_where([True, True, False], [nan, inf, nan])
16711679
assert_(type(x) is type(isclose(inf, x)))
1680+
assert_(type(x) is type(isclose(x, inf)))
16721681

16731682
x = np.ma.masked_where([True, True, False], [nan, nan, nan])
16741683
y = isclose(nan, x, equal_nan=True)
16751684
assert_(type(x) is type(y))
16761685
# Ensure that the mask isn't modified...
16771686
assert_array_equal([True, True, False], y.mask)
1687+
y = isclose(x, nan, equal_nan=True)
1688+
assert_(type(x) is type(y))
1689+
# Ensure that the mask isn't modified...
1690+
assert_array_equal([True, True, False], y.mask)
16781691

16791692
x = np.ma.masked_where([True, True, False], [nan, nan, nan])
16801693
y = isclose(x, x, equal_nan=True)

numpy/testing/utils.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,7 +1244,7 @@ def _assert_valid_refcount(op):
12441244

12451245
assert_(sys.getrefcount(i) >= rc)
12461246

1247-
def assert_allclose(actual, desired, rtol=1e-7, atol=0,
1247+
def assert_allclose(actual, desired, rtol=1e-7, atol=0, equal_nan=False,
12481248
err_msg='', verbose=True):
12491249
"""
12501250
Raises an AssertionError if two objects are not equal up to desired
@@ -1266,6 +1266,8 @@ def assert_allclose(actual, desired, rtol=1e-7, atol=0,
12661266
Relative tolerance.
12671267
atol : float, optional
12681268
Absolute tolerance.
1269+
equal_nan : bool, optional.
1270+
If True, NaNs will compare equal.
12691271
err_msg : str, optional
12701272
The error message to be printed in case of failure.
12711273
verbose : bool, optional
@@ -1289,7 +1291,8 @@ def assert_allclose(actual, desired, rtol=1e-7, atol=0,
12891291
"""
12901292
import numpy as np
12911293
def compare(x, y):
1292-
return np.core.numeric._allclose_points(x, y, rtol=rtol, atol=atol)
1294+
return np.core.numeric.isclose(x, y, rtol=rtol, atol=atol,
1295+
equal_nan=equal_nan)
12931296

12941297
actual, desired = np.asanyarray(actual), np.asanyarray(desired)
12951298
header = 'Not equal to tolerance rtol=%g, atol=%g' % (rtol, atol)

0 commit comments

Comments
 (0)