8000 BUG: Fix warning problems of the mod operator by arubiales · Pull Request #19316 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

BUG: Fix warning problems of the mod operator #19316

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 7 commits into from
Jul 14, 2021

Conversation

arubiales
Copy link
Contributor

That's the initial solution, there are 13 test failing because it does not trigger the warning that we are resolving. So If this solution is correct I think we should change the test too.

@BvB93 BvB93 changed the title Warning problems of mod operator #18170 BUG: Fix warning problems of the mod operator Jun 23, 2021
@BvB93 BvB93 linked an issue Jun 23, 2021 that may be closed by this pull request
@seberg
Copy link
Member
seberg commented Jun 23, 2021

Could you fix up the tests (and maybe even add new ones to describe what you changed?). That also clarifies exactly what changed for the reviewers.

@arubiales
Copy link
Contributor Author

@seberg did it! Check the changes when you can please.

@seberg seberg self-requested a review June 29, 2021 15:46
@@ -716,9 +710,6 @@ npy_divmod@c@(@type@ a, @type@ b, @type@ *modulus)
@type@ div, mod, floordiv;

/* force set invalid flag, doesnt raise by default on gcc < 8 */
Copy link
Contributor

Choose a reason for hiding this comment

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

This line should probably go, too.

Copy link
Member

Choose a reason for hiding this comment

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

This is my "problem" so to speak. I am confused what this is about and what we tried to do in gh-16161.

My guess is that this godbolt snippet explains the comment if you switch to GCC 8, the correct answer is given (warning flag is set).

Now... If a is NaN and b < 0, the if (mod) will evaluate True if mod is NaN. Especially, if both a and b are NaN, then we definitely take that branch and both b < 0 and mod < 0 evaluate to False with setting the warning (since they are both NaN). And that was wrong on GCC 7, which did not set the warning (but led to the better result).

(Hopefully, that flow analysis is right... But something is off and my best guess is to fix most of these comparisons and replace them with isgreater and isless, as mentioned before and now also in gh-19398. In general, I don't think we really ever want float comparisons to set warning flags really, so de-facto all of those in the math code are probably just wrong...)

I honestly don't understand why:

with np.errstate(invalid='raise'):
    assert_no_warnings(FloatingPointError, np.floor_divide, 1, np.nan)

succeeds! If I just call np.floor_divide(1, np.nan), I get an "invalid value" warning, and with np.errstate(invalid="raise") the FloatingPointError.

Copy link
Member

Choose a reason for hiding this comment

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

@arubiales I am confused, did you maybe try these things out on a gcc version older than 8 (Which would be pretty old)?

Copy link
Contributor Author
@arubiales arubiales Jul 4, 2021

Choose a reason for hiding this comment

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

@nschloe Yes sorry I fogot to delete it.

@seberg No my GCC version is 8.4. I tryed the same, and with the changes I have no warning, but I'm with you, should raise the "invalid value" flag. After test with the following change:

NPY_INPLACE @type@
npy_floor_divide@c@(@type@ a, @type@ b) {
    @type@ div, mod;
    if (NPY_UNLIKELY(!b)) {
        div = a / b;
        if (!a || npy_isnan(a)) {
            npy_set_floatstatus_invalid();
        } else {
            npy_set_floatstatus_divbyzero();
        }
    } else {
        div = npy_divmod@c@(a, b, &mod);
        /* This is the change, I just set the invalid float status in the else */
        npy_set_floatstatus_invalid();
    }

I have the "invalid" flag Which mean I never enter in the if because NPY_UNLIKELY(!b) is always False

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, !b is false, but then it goes into divmod? I have to check more, maybe I was also missing a git clean...

Can you do me a favor and delete the assert_no_warnings completely and make it np.errstate(all="raise")? I still have to think about it more. I guess this is a good change though, and we should have more fixes as followup.

@nschloe
Copy link
Contributor
nschloe commented Jul 2, 2021

Perhaps and assert_no_warnings test for np.mod could be added.

@arubiales
Copy link
Contributor Author

@nschloe Do you want to add it in this PR? A new test function as for example divmod but for mod?

@seberg
Copy link
Member
seberg commented Jul 6, 2021

I have pushed a test commit to remove assert_no_warnings. Unfortunately, that should blow up the tests (if it does not, I am very suprised, since they fail locally).

I am thinking about how to fix things, but I am not sure how easy it will be to confine changes to a minimal set here.

@seberg
Copy link
Member
seberg commented Jul 6, 2021

Sorry, for taking over a bit here, but I had to look at it with much more concentration to make progress. It is a bit tricky to split the mod issue from the other issues :/.

@charris maybe you can have a look? I think this should be fine now, although a couple of new tests might be good (Although Anirudh had added good tests and the only problem is the behaviour with NaNs coming in).

The things to remember is:

  • We raise errors whenever a new inf or NaN is created. If a new inf is created, it will be a division-by-zero, and if a new NaN is created it will be an "invalid value".
  • if NaN takes the True branch (evaluates True); if !NaN: evaluates to false.
  • I assume divmod can create both a new NaN and a new Inf, so sets both flags. (EDIT: Does not set the error flags)
  • floor-divide and remainder only set one of those. When divmod would set both flags we cannot use it.
  • NaN > 0 sets an invalid value flag. We need to avoid that (see MAINT,ENH: Use non-error functions isless, isgreater, etc. #19398), using isgreater, etc. should not set the flags.

The last point is a big reason for the whole confusion here:

  • Old GCC versions (or some more?) did not set a warning flag for NaN comparisons (note that == and != do not set the warning)
  • We also use Python's divmod, but Python does not really care about floating point exceptions to begin with.

isgreater, etc. are C99 standard, so I assume we can use them.

@seberg seberg removed their request for review July 6, 2021 18:02
}
#endif
/**end repeat1**/

Copy link
Member

Choose a reason for hiding this comment

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

These fmod implementations were added in the old divmod-warnings PR. Removing them undoes that change.

@seberg seberg force-pushed the quit_runtimewarning_moduleop_ branch from 9d75d18 to a0060d7 Compare July 6, 2021 19:56
* but set the error flags correct (divmod may set too many).
* (Using !b here, since reverse logic set incorrect flags on clang)
*/
return npy_fmod@c@(a, b);
Copy link
Member

Choose a reason for hiding this comment

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

What I had missed before is that this also fails the following test for divmod:

with np.errstate(divide='ignore', invalid='raise'):
assert_raises(FloatingPointError, np.divmod, fone, fzero)

fmod(1, 0) seems to simply not set the correct floating point flags on clang (at least in the version used by CI). I suppose we could work around clang (or even mark the test as xfail?)

@seberg seberg force-pushed the quit_runtimewarning_moduleop_ branch 2 times, most recently from 06ae9ee to 5277577 Compare July 7, 2021 19:46
@arubiales
Copy link
Contributor Author
arubiales commented Jul 8, 2021

Sorry, for taking over a bit here

Don't worry @seberg , you have more knowledge that me about Numpy. I'm following all your changes, if there is something I can help, please tell me, I will be happy to help!

@seberg seberg force-pushed the quit_runtimewarning_moduleop_ branch 2 times, most recently from ebf46a2 to 428fae5 Compare July 8, 2021 19:43
@seberg
Copy link
Member
seberg commented Jul 8, 2021

Thanks. Right now I am thinking of calling it done. The tests will fail, and I will fix them up so that it skips the shaky ones at least on MacOS. (With a good chance of more skips needed for weird platforms.)

I have two options:

  1. My slight preference for now: Regress for %/divmod (np.floor_divide seems fine) to the behaviour in BUG: __floordiv__ gives NaN and invalid warning for 1//0 #14900 for MacOS. This is due to fmod not setting the correct warning flags on MacOS. This partially goes back to the behaviour of 1.19.x (and earlier). (MacOS used to not give any warnings for np.divmod(1., 0.)) But fixes a bunch of other things, and especially makes sure no spurious warnings are given. – If someone wants MacOS to be fixed, option 2 can be done later.
  2. Manually implement fmod (potentially selective for MacOS using the blocklist) that implements setting the floating point flags manually.

I dislike testing for correct flags a bit. It feels like this is bound to fail on exotic platforms. I am honestly wondering if rather than failing, these tests should just print out: 8000 "Careful, your system does not follow IEEE warning flags well for divmod"

arubiales and others added 7 commits July 12, 2021 15:32
The old tests seems not to have noticed all types of errors here.
It seems the `assert_no_warnings` context does not care about
exceptions.
It is very much possible that this will also fail on other systems
and should rather only run, e.g. on newer glibc versions.

In principle, we could also split this into "no warnings given"
and "warning given".  MacOS seems very sloppy with floating point
warnings, but hopefully it at least does not give spurious ones.
@seberg seberg force-pushed the quit_runtimewarning_moduleop_ branch from 428fae5 to e502de2 Compare July 12, 2021 20:42
@seberg seberg added the triage review Issue/PR to be discussed at the next triage meeting label Jul 14, 2021
@mattip
Copy link
Member
mattip commented Jul 14, 2021

We discussed this at the triage meeting and decided to merge this as is. In a follow on PR we should mention that macOS (probably)/clang (less likely) is not IEEE compliant.

@mattip mattip merged commit e02af0b into numpy:main Jul 14, 2021
@mattip
Copy link
Member
mattip commented Jul 14, 2021

Thanks @seberg

8CCD

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
00 - Bug triage review Issue/PR to be discussed at the next triage meeting
Projects
None yet
Development

Successfully merging this pull request may close these issues.

np.float64(np.nan) % 1 gives warning
4 participants
0