Skip to content
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

ENH: Implement correct scalar and integer overflow errors for NEP 50 #21875

Merged
merged 13 commits into from Oct 18, 2022

Conversation

seberg
Copy link
Member

@seberg seberg commented Jun 29, 2022

This adds the main missing chunk of NEP 50 support. Unfortunately, I feel this is still fairly complex, although I am not sure it can be helped...

For the ufuncs, it may be possible to move the special handling to later. For the scalars, the special handling has a bit of additional complexity because of the "defer" check (if we did it always it would be easier logic flow).
Unfortunately, for the scalars it makes sense to keep the "faster" version for the safe cast around (for speed reasons).

The other question is whether we want to do this as a distinct method on the DType, although I don't think we have to settle that yet. It should be easy to change.

A small oddity is that the user has to resolve from one of the "int" dtypes (int32, int64, uint64, object). That seems OK to me, although it also seems like something that we may well know better later.


Marking as draft, I need to let this sink in a bit to see if I have a better idea. Bug, this should be ready for a test ride and may complete NEP 50 logic (with the exception of possibly allowing Python scalars for example in np.can_cast(123, np.int8))

@seberg
Copy link
Member Author

seberg commented Jul 5, 2022

OK, I still don't love the scalar code, but considering that the ufunc code is at least well localized and the scalar code could use more cleanup (as in conversion function introduction probably via C++), I will mark this as ready for review. I simply did not have a clear idea for how to improve it meaningfully.

@seberg seberg marked this pull request as ready for review July 5, 2022 18:46
if (!PyTypeNum_ISCOMPLEX(NPY_@TYPE@)) {
return PROMOTION_REQUIRED;
}
return CONVERT_PYSCALAR;
Copy link
Member

Choose a reason for hiding this comment

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

Is this line really skipped during testing or is codecoverage mistaken?

@mattip
Copy link
Member

mattip commented Jul 17, 2022

whether we want to do this as a distinct method on the DType

I would prefer not to, if we can avoid more methods

@seberg seberg force-pushed the weak-scalars-safe-ints branch 2 times, most recently from 056850c to f096789 Compare September 9, 2022 13:04
@seberg seberg added the triage review Issue/PR to be discussed at the next triage meeting label Sep 12, 2022
@seberg
Copy link
Member Author

seberg commented Sep 12, 2022

whether we want to do this as a distinct method on the DType

I would prefer not to, if we can avoid more methods

Hmm, trying to untangle the state of this. The reason for custom handling was that we can allow these to remain unmodified:

np.uint8(-1)
# And also:
np.array([-1], np.uint8)
arr_uint8[0] = -1

i.e. we still allow the forced cast in some cases, but not in the new paths.

This seems almost exclusively relevant for that use-case where you use -1 to mean UINT_MAX, but is a relevant use-case there.

However, we are already changing quite a lot, so maybe it is OK to just change that as well, with the one possible exception that we could hard-code np.uint(-1) to keep working (but not the other two examples).

The other question is only relevant if we do not want to change the above. That was where I was coming from. But I don't really see that it makes things clearer by not being a method (since methods are the typical path now, I think).

@seberg
Copy link
Member Author

seberg commented Sep 12, 2022

Lets discuss this on Wednesday a bit, maybe we can make a decision then (I think the code coverage/tests should be fine now, but will fix it up if not).

seberg and others added 12 commits October 12, 2022 10:41
This requires adding a path that uses the "normal" Python object
to dtype conversion (setitem) function, rather than always converting
to the default dtype.
The new weak promotion preserves the original type, this makes the
`//= 2**32` fail for certain inputs.  The alternative would be
typing that as `np.int64(2**32)`, but using Python integers seems
easier and cleaner.

The code was effectively OK before, since the inputs were guaranteed
signed (or Python integers) at that point and 2**32 would have been
considered like a NumPy `int64`.  (Which would be an alternative fix.)
This is the *actual* correct fix for the test adaptations.  The test
adaptations should only be necessary when running in weak-promotion mode,
but they are NOT doing that currently.
…values

This should be fixed in `choose` to not do the unnecessary cast,
see numpygh-22237.
Especially adding coverage also for the power operator which
does not share its code perfectly.
@seberg seberg removed the triage review Issue/PR to be discussed at the next triage meeting label Oct 12, 2022
@seberg
Copy link
Member Author

seberg commented Oct 12, 2022

OK, this is rebased to account for gh-22385. (I will have another scroll through myself later today just to make sure there are no small cleanups needed still).

@pytest.mark.parametrize("dtype", np.typecodes["AllInteger"])
def test_nep50_weak_integers(dtype):
# Avoids warning (different code path for scalars)
np._set_promotion_state("weak")
Copy link
Member

Choose a reason for hiding this comment

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

Should the tests reset global state at finish?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, there is a module wide auto-fixture that defaults to "weak_and_warn" and resets the state at the end.

@seberg
Copy link
Member Author

seberg commented Oct 18, 2022

Can we move forward with this, soon? I would like to make sure we have full optional NEP 50 logic by next release for testing.
I suppose this is the last very important thing, maybe np.can_cast(123, dtype=np.uint8) should also be "special" (but even if, I suspect it has much less impact in practice).

@mattip mattip merged commit aed648c into numpy:main Oct 18, 2022
@mattip
Copy link
Member

mattip commented Oct 18, 2022

Thanks @seberg

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.

None yet

2 participants