Skip to content

Add absolute tolerance when comparing FITS - #4729

Merged
pllim merged 10 commits into
astropy:masterfrom
elehcim:master
Feb 17, 2017
Merged

pllim merged 10 commits into
astropy:masterfrom
elehcim:master

Conversation

@elehcim

@elehcim elehcim commented Mar 24, 2016

Copy link
Copy Markdown
Contributor

This changes a bit the interface: tolerance -> reltol
Added abstol keyword argument.

This is particularly useful when numbers to compare are very little. I'm exploiting the underlying numpy function numpy.allclose which can be used also with absolute tolerance.

@pllim pllim added io.fits Affects-dev PRs and issues that do not impact an existing Astropy release labels Mar 24, 2016
@pllim

pllim commented Mar 24, 2016

Copy link
Copy Markdown
Member

Need a change log. Also...

👍 on more controls on the accuracy. However, this changes the API. I think usually we issue a deprecation warning first for a while, and then really change it. So might need that and a way for backward compatibility here, at least temporarily. I think this needs some further discussions between core maintainers.

@pllim

pllim commented Mar 24, 2016

Copy link
Copy Markdown
Member

inserts magical @embray incantation

@embray

embray commented Mar 25, 2016

Copy link
Copy Markdown
Member

This should keep tolerance as an alias for the existing functionality, but I otherwise agree that where needed it's good to have separate absolute and relative tolerances passed through.

@embray embray added Affects-release and removed Affects-dev PRs and issues that do not impact an existing Astropy release labels Mar 25, 2016
@embray

embray commented Mar 25, 2016

Copy link
Copy Markdown
Member

"Affects-release" means "changes functionality in existing release"

Comment thread astropy/io/fits/diff.py Outdated

abs(x1 - x2) > abstol + abs(x1)*reltol

are considered to be different

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Silly nitpick but missing a period at the end here.

@elehcim

elehcim commented Mar 25, 2016

Copy link
Copy Markdown
Contributor Author

Thanks for the comments.
I'd propose something like the following:

def __init__(self, a, b, ignore_keywords=[], ignore_comments=[],
             ignore_fields=[], numdiffs=10, reltol=0.0, abstol=0.0,
             ignore_blanks=True, ignore_blank_cards=True, tolerance=None):

    [...]
    self.reltol = reltol
    self.abstol = abstol
    if tolerance is not None:
        warn("Named argument `tolerance` is deprecated and will be removed in future releases.
              Use `reltol` instead.")
        if reltol == 0.0:
            self.reltol = tolerance

Is it ok to use reltol in case FITSDiff(reltol=1e-2, tolerance=1e-3)?
Whats your standard way to warn the user of a deprecated syntax?
I'm pushing some more code, you can comment on that.

@elehcim

elehcim commented Mar 25, 2016

Copy link
Copy Markdown
Contributor Author

Ops, I forgot to update the other __init__ methods 😅. Before I proceed, please comment on the idea.

@pllim

pllim commented Mar 25, 2016

Copy link
Copy Markdown
Member

@embray, sorry. 😅

I thought "affect release" means we will need to backport or something. I still can't find a documentation on how to properly use the "affect" tags. At one point I had to dig through a bunch of issues for that ASCII flowchart you made buried in the comments. But that's about it.

Maybe for now, I'll just not touch those tags anymore...

@embray

embray commented Mar 29, 2016

Copy link
Copy Markdown
Member

On one hand I usually would push against having more than one way to do it, and I'd be okay with deprecating tolerance. On the other hand, I know there are enough tests at STScI using this that I don't want to be cropping up from beyond the grave 👻 to cause hair-pulling and headaches over that.

As a compromise I think we could deprecate the tolerance keyword argument to FITSDiff (since I think the API is used less frequently, but we might want to check on that--@pllim?) but keep the existing options in the command-line arguments (without a deprecation warning).

Comment thread astropy/io/fits/diff.py Outdated

if tolerance is not None:
warnings.warn("Named argument `tolerance` is deprecated "
"and will be removed in future releases. Use `reltol` instead.")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should be formatted a bit difference. Grep the source for AstropyDeprecationWarning for some other examples.

@mcara

mcara commented Mar 29, 2016

Copy link
Copy Markdown
Contributor

I like the approach that is taking shape including deprecating tolerance parameter. Except, for a little bit more clarity, I would suggest the following change to the logic [the confusion may arise from the fact that in the current implementation tolerance is sometimes used and sometimes ignored]:

    if tolerance is not None:
        warnings.warn("Named argument `tolerance` is deprecated "
                      "and will be removed in future releases. Use `reltol` instead. "
                      "Ignoring `reltol` value...")

       self.reltol = tolerance # when tolerance is provided *always* ignore `reltol`
                               # during the transition/deprecation period

@pllim

pllim commented Apr 18, 2016

Copy link
Copy Markdown
Member

I know there are enough tests at STScI using this that...

@embray , no one from SSB raised any objection thus far. So it's on them if they run into problem in the future.

👍 to this change.

@astrofrog

Copy link
Copy Markdown
Member

Numpy uses the convention atol and rtol instead of abstol and reltol - does anyone have any thoughts on whether to follow this?

@mcara

mcara commented May 10, 2016

Copy link
Copy Markdown
Contributor

@astrofrog I am for following numpy

@astrofrog

astrofrog commented May 12, 2016 •

Copy link
Copy Markdown
Member

@elehcim - could you change abstol to atol and reltol to rtol? (if you can make this change by the end of tomorrow, we can include this in Astropy 1.2)

@elehcim

elehcim commented May 17, 2016

Copy link
Copy Markdown
Contributor Author

@astrofrog I've changed tolerance names and implemented some comments noted on the discussion above.

@embray

embray commented May 17, 2016

Copy link
Copy Markdown
Member

I actually liked @elehcim's original names over Numpy's--they're much clearer, and it's obvious enough how it maps onto the underlying Numpy function. But I'm not going to bikeshed any more than that. Decide amongst yourselves.

@pllim

pllim commented Dec 2, 2016

Copy link
Copy Markdown
Member

This needs a rebase. And if the coverage decrease still shows up after, we need to figure out why.

@elehcim

elehcim commented Dec 3, 2016

Copy link
Copy Markdown
Contributor Author

Rebased. Waiting for continuous integration checks.

Comment thread astropy/io/fits/diff.py Outdated
'reported: {}').format(self.numdiffs))
self._writeln(u(' Data comparison level: {}').format(self.tolerance))
self._writeln(u(' Relative tolerance: {},'
' Absolute tolerance: {}}').format(self.rtol, self.atol))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

there is a superfluous } here causing all kinds of testing failures

Comment thread astropy/io/fits/diff.py Outdated
Ignore all cards that are blank, i.e. they only contain
whitespace (default: True).

tolerance : float, optional

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The deprecation of tolerance should now follow the same implementation similar to #5171, utilizing a new decorator added in #5214.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think that's possible because the parameter was replaced by 2 new parameters. I haven't considered such a case. 😅

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@MSeifert04 , I disagree. tolerance is replacing rtol, and atol is a new feature.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

But atol is added in between existing parameters so it will permanently change the function signature. Say you call the function with positional parameters only then the old ignore_blanks will now be interpreted as atol.

But you're right for the tolerance -> rtol change.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hmm. That is too bad.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe not so "nice" but we could append "atol" at the end of the function signature. Then this is completly backwards-compatible. And of course using deprecated_renamed_argument for the tolerance -> rtol change.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think deprecated parameters should be added to the docstring. Probably best to remove it completly, the homepage contains the documentation for older versions and with the directive in rtol about the renaming it should be fine, right?

@pllim

pllim commented Dec 5, 2016

Copy link
Copy Markdown
Member

Sphinx test failed on Travis CI; I don't see that failure on master. I cancelled the Mac build.

@elehcim elehcim mentioned this pull request Feb 12, 2017
3 tasks done
@bsipocz

bsipocz commented Feb 12, 2017

Copy link
Copy Markdown
Member

@elehcim - I'm afraid you've added the changelog to the wrong section. This should go in to 2.0 and not 1.3.1 (as the latter is a bugfix release and that by definition doesn't contain enhancements or new deprecations).

@bsipocz bsipocz added this to the v2.0.0 milestone Feb 12, 2017
@elehcim

elehcim commented Feb 13, 2017

Copy link
Copy Markdown
Contributor Author

Ok, rebased and changed target version, thank you @bsipocz.
Reviewers, please have a look at it. @pllim, @MSeifert04

@pllim pllim left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the updates. My review is attached.

Comment thread astropy/io/fits/diff.py Outdated

.. math::

\\left| x_1 - x_2 \\right| > \\text{atol} + \\text{rtol} \\cdot \\left| x_1 \\right|

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think just following the formula from numpy.allclose is less confusing, since the keyword names are the same anyway.

Comment thread astropy/io/fits/diff.py Outdated
are considered to be different.

atol : float, optional
The allowed absolute difference. See also rtol parameter.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nitpick: Double backticks around rtol.

Comment thread astropy/io/fits/diff.py Outdated
whitespace (default: True).

tolerance : float, optional
Alias of rtol. Deprecated, provided for backward compatibility.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nitpick: Double backticks around rtol.

Comment thread astropy/io/fits/diff.py Outdated
self.rtol = rtol
self.atol = atol

if tolerance is not None:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe add a in-code comment that this can be removed for v3.1 and after? Is that numbering right, @mhvk? Sorry, I am still learning the proper deprecated code removal policy.

-------

fitsdiff -k filename,filtnam1 -n 5 -d 1.e-6 test1.fits test2
fitsdiff -k filename,filtnam1 -n 5 -r 1.e-6 test1.fits test2

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

See #5799.

opts.ignore_comments = []
opts.ignore_fields = []
opts.tolerance = 0.0
opts.rtol = 0.0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No, wait. Now I am confused. In the parser above, there is a dest="rtol" for -d option. But then, it is no handling for when both -d and -r are defined. And then below, somehow there is a tolerance=opts.tolerance option. I think all these need some cleaning up.

diff = HeaderDiff(ha, hb, rtol=1e-5, atol=1e-5)
assert diff.identical
diff = HeaderDiff(ha, hb, atol=1.1e-5)
assert diff.identical

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, I would like to see such a test as well, where both rtol and tolerance are defined, but the latter overrides the former.

Comment thread astropy/io/fits/tests/test_diff.py Outdated
assert diff.identical
assert diff.diff_total == 0

def test_not_identical_within_rtol_and_atol(self):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The function name implies that you are testing for "not identical" but then the actual test does the opposite. This is very confusing. Same comment for the test that follows below.

@elehcim

elehcim commented Feb 15, 2017 •

Copy link
Copy Markdown
Contributor Author

Hello @pllim, I think I managed to implement all your comments, thank you very much.
I cannot see the link between the travis failures and this PR... do you have any idea?

@bsipocz

bsipocz commented Feb 15, 2017

Copy link
Copy Markdown
Member

I've restarted the failing builds.

@pllim

pllim commented Feb 15, 2017

Copy link
Copy Markdown
Member

Two of them still failed with URLError. I restarted them again.

@elehcim

elehcim commented Feb 15, 2017

Copy link
Copy Markdown
Contributor Author

🎉 travis did his job

@pllim pllim left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks like you added a test to the fitsdiff command too, nice! Just a minor comment from me.

@MSeifert04 , do you want to look at this one last time?

Comment thread astropy/io/fits/scripts/fitsdiff.py Outdated
opts, args = handle_options(argv)

if opts.tolerance is not None and opts.rtol is not None:
sys.stderr.write(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the codes above, tolerance overwrites rtol with a warning, but here it is outright error. Why the difference in behavior?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, maybe it's better to issue an AstropyDeprecationWarning and let tolerance overwrite rtol

from . import FitsTestCase
from ..hdu import PrimaryHDU
from ..scripts import fitsdiff
from ....tests.helper import pytest

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Note to @astrofrog , you'll need to change this too for #5694 if this gets merged first.

@MSeifert04 MSeifert04 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, I added a few additional comments and I'm not exactly sure the script is working as intended. Not because it seems wrong but just because I'm not familiar with scripts 😄

Comment thread astropy/io/fits/diff.py
\\left| a - b \\right| > \\text{atol} + \\text{rtol} \\cdot \\left| b \\right|

are considered to be different.
The underlying function used for comparison is `numpy.allclose`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you add a .. versionchanged:: 2.0-directive here explaining the change of parameter name tolerance->rtol?

Comment thread astropy/io/fits/diff.py Outdated
Ignore all cards that are blank, i.e. they only contain
whitespace (default: True).

tolerance : float, optional

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think deprecated parameters should be added to the docstring. Probably best to remove it completly, the homepage contains the documentation for older versions and with the directive in rtol about the renaming it should be fine, right?

Comment thread astropy/io/fits/diff.py Outdated
self.atol = atol

if tolerance is not None: # This should be removed in the next astropy version
warnings.warn('"tolerance" was '

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is just a PEP-8-nitpick but that seems like odd-indentation to me. Feel free to keep it as is but I would prefer:

        warnings.warn(
            '"tolerance" was deprecated in version 2.0 and will be removed in '
            'a future version. Use argument "rtol" instead.',
            AstropyDeprecationWarning)

because the hanging indentation is the same for all strings and parameters here. Same for the other occurances of this warning.

Comment thread astropy/io/fits/diff.py Outdated
if np.isnan(a) and np.isnan(b):
return False
return not np.allclose(a, b, tolerance, 0.0)
return not np.allclose(a, b, rtol, atol)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you pass them in by name here as well? rtol=rtol, atol=atol? That's what is done oion the other places as well.

… `fitsdiff`

* Add versionchanged directive
* Removed deprecated argument from docstring
* Fix indentation
@elehcim

elehcim commented Feb 16, 2017

Copy link
Copy Markdown
Contributor Author

Thank you @MSeifert04 for your review.
What are your doubts regarding the script?

@MSeifert04

Copy link
Copy Markdown
Contributor

What are your doubts regarding the script?

No specific doubts, it's just that these scripts are not covered by the test suite and I firmly believe that "untested code is broken code". 😄 (That doesn't mean that every case has to be tested but if there is no test at all then it may break down at any point).

@MSeifert04 MSeifert04 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

👍

@pllim

pllim commented Feb 17, 2017

Copy link
Copy Markdown
Member

So, we got two approvals, but CircleCI failed. I am not familiar with CircleCI (the log wouldn't load for me), can someone please confirm whether failure is related or not?

untested code is broken code

@MSeifert04 , that sound a little extreme as it implies 100% coverage to be a requirement. 😅

@astrofrog

Copy link
Copy Markdown
Member

I've restarted CircleCI.

Untested code is broken code - anytime I increase coverage I find bugs! It's just a question of whether they are critical bugs :)

@mcara

mcara commented Feb 17, 2017

Copy link
Copy Markdown
Contributor

anytime I increase coverage I find bugs!

That's hilarious!

@pllim

pllim commented Feb 17, 2017

Copy link
Copy Markdown
Member

The PR started back in March 2016. 😅 Time to merge! Thank you for your patience, @elehcim !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants