8000 ENH: Make assert_almost_equal & assert_array_almost_equal consistent. by charris · Pull Request #7760 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

ENH: Make assert_almost_equal & assert_array_almost_equal consistent. #7760

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 20, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion doc/release/1.12.0-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ FutureWarning to changed behavior
* np.average will emit a warning if the argument is a subclass of ndarray,
as the subclass will be preserved starting in 1.13. (see Future Changes)

Greater consistancy in ``assert_almost_equal``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The precision check for scalars has been changed to match that for arrays. It
is now

abs(actual - desired) < 1.5 * 10**(-decimal)

Note that this is looser than previously documented, but agrees with the
previous implementation used in ``assert_array_almost_equal``. Due to the
change in implementation some very delicate tests may fail that did not
fail before.


C API
~~~~~
Expand Down Expand Up @@ -261,4 +273,5 @@ If a 'width' parameter is passed into ``binary_repr`` that is insufficient to
represent the number in base 2 (positive) or 2's complement (negative) form,
the function used to silently ignore the parameter and return a representation
using the minimal number of bits needed for the form in question. Such behavior
is now considered unsafe from a user perspective and will raise an error in the future.
is now considered unsafe from a user perspective and will raise an error in the
future.
36 changes: 35 additions & 1 deletion numpy/testing/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
assert_warns, assert_no_warnings, assert_allclose, assert_approx_equal,
assert_array_almost_equal_nulp, assert_array_max_ulp,
clear_and_catch_warnings, run_module_suite,
assert_string_equal, assert_, tempdir, temppath,
assert_string_equal, assert_, tempdir, temppath,
)
import unittest

Expand Down Expand Up @@ -246,6 +246,23 @@ class TestArrayAlmostEqual(_GenericTest, unittest.TestCase):
def setUp(self):
self._assert_func = assert_array_almost_equal

def test_closeness(self):
# Note 8000 that in the course of time we ended up with
# `abs(x - y) < 1.5 * 10**(-decimal)`
# instead of the previously documented
# `abs(x - y) < 0.5 * 10**(-decimal)`
# so this check serves to preserve the wrongness.

# test scalars
self._assert_func(1.499999, 0.0, decimal=0)
self.assertRaises(AssertionError,
lambda: self._assert_func(1.5, 0.0, decimal=0))

# test arrays
self._assert_func([1.499999], [0.0], decimal=0)
self.assertRaises(AssertionError,
lambda: self._assert_func([1.5], [0.0], decimal=0))

def test_simple(self):
x = np.array([1234.2222])
y = np.array([1234.2223])
Expand Down Expand Up @@ -288,6 +305,23 @@ class TestAlmostEqual(_GenericTest, unittest.TestCase):
def setUp(self):
self._assert_func = assert_almost_equal

def test_closeness(self):
# Note that in the course of time we ended up with
# `abs(x - y) < 1.5 * 10**(-decimal)`
# instead of the previously documented
# `abs(x - y) < 0.5 * 10**(-decimal)`
# so this check serves to preserve the wrongness.

# test scalars
self._assert_func(1.499999, 0.0, decimal=0)
self.assertRaises(AssertionError,
lambda: self._assert_func(1.5, 0.0, decimal=0))

# test arrays
self._assert_func([1.499999], [0.0], decimal=0)
self.assertRaises(AssertionError,
lambda: self._assert_func([1.5], [0.0], decimal=0))

def test_nan_item(self):
self._assert_func(np.nan, np.nan)
self.assertRaises(AssertionError,
Expand Down
33 changes: 19 additions & 14 deletions numpy/testing/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,11 +424,14 @@ def assert_almost_equal(actual,desired,decimal=7,err_msg='',verbose=True):
instead of this function for more consistent floating point
comparisons.

The test is equivalent to ``abs(desired-actual) < 0.5 * 10**(-decimal)``.
The test verifies that the elements of ``actual`` and ``desired`` satisfy.

Given two objects (numbers or ndarrays), check that all elements of these
objects are almost equal. An exception is raised at conflicting values.
For ndarrays this delegates to assert_array_almost_equal
``abs(desired-actual) < 1.5 * 10**(-decimal)``

That is a looser test than originally documented, but agrees with what the
actual implementation in `assert_array_almost_equal` did up to rounding
vagaries. An exception is raised at conflicting values. For ndarrays this
delegates to assert_array_almost_equal

Parameters
----------
Expand Down Expand Up @@ -529,7 +532,7 @@ def _build_err_msg():
return
except (NotImplementedError, TypeError):
pass
if round(abs(desired - actual), decimal) != 0:
if abs(desired - actual) >= 1.5 * 10.0**(-decimal):
raise AssertionError(_build_err_msg())


Expand Down Expand Up @@ -819,14 +822,16 @@ def assert_array_almost_equal(x, y, decimal=6, err_msg='', verbose=True):
instead of this function for more consistent floating point
comparisons.

The test verifies identical shapes and verifies values with
``abs(desired-actual) < 0.5 * 10**(-decimal)``.
The test verifies identical shapes and that the elements of ``actual`` and
``desired`` satisfy.

Given two array_like objects, check that the shape is equal and all
elements of these objects are almost equal. An exception is raised at
shape mismatch or conflicting values. In contrast to the standard usage
in numpy, NaNs are compared like numbers, no assertion is raised if
both objects have NaNs in the same positions.
``abs(desired-actual) < 1.5 * 10**(-decimal)``

That is a looser test than originally documented, but agrees with what the
actual implementation did up to rounding vagaries. An exception is raised
at shape mismatch or conflicting values. In contrast to the standard usage
in numpy, NaNs are compared like numbers, no assertion is raised if both
objects have NaNs in the same positions.

Parameters
----------
Expand Down Expand Up @@ -903,12 +908,12 @@ def compare(x, y):
# casting of x later.
dtype = result_type(y, 1.)
y = array(y, dtype=dtype, copy=False, subok=True)
z = abs(x-y)
z = abs(x - y)

if not issubdtype(z.dtype, number):
z = z.astype(float_) # handle object arrays

return around(z, decimal) <= 10.0**(-decimal)
return z < 1.5 * 10.0**(-decimal)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would making this <= still make sense and remove the possibly stronger tests? Not sure if that makes sense or not. Anyway, looks good to me.
I would like to nudge you towards adding a test ;p. Wondering if we can somehow make it easier to understand what "decimal" means in the documentation. Also "vagaries" seems a bit difficult to me as a non-native speaker, maybe "details"?

Copy link
Member Author
@charris charris Jun 19, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had that, but changed it on account of

nt.assert_almost_equal([1.5], [0], decimal=0)

failing in master due to rounding the difference 1.5 up. Note that

nt.assert_almost_equal([1.49999], [0], decimal=0)

does pass (just to assure everyone the 1.5 is the proper factor).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't make everything exactly the same, but I figure that any problems are in tests too close to the edge in any case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah need a test. If there had been one in the first place we would never have ended up with 1.5 * 10**(-decimal). That was due to the use of a <= instead of <, and I think that came about from working around some other perceived bug. I'd just change it if it didn't cause so many new failures in downstream code.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test added.


assert_array_compare(compare, x, y, err_msg=err_msg, verbose=verbose,
header=('Arrays are not almost equal to %d decimals' % decimal),
Expand Down
0