BUG: apply the NEP 50 scalar rules in concatenate and choose, and make casting="no" match "equiv" - #32497
Conversation
| for casting in ["no", "equiv", "safe", "same_kind", "unsafe"]: | ||
| res = np.concatenate((arr, "z"), axis=None, casting=casting) | ||
| assert res[1] == "z" | ||
|
|
There was a problem hiding this comment.
this change hasn't made it into a release yet, so there's no backward compatibility concern
| [(1000, np.array([1], dtype=np.uint8)), | ||
| (-1, np.array([1], dtype=np.uint8)), | ||
| [(100, np.array([1], dtype=np.uint8)), | ||
| (-1, np.array([1], dtype=np.int8)), |
There was a problem hiding this comment.
This changed because 1000 now triggers an overflow error. Should it merely be a deprecation instead?
mhvk
left a comment
There was a problem hiding this comment.
Looks good! And especially good to update the docs -- so that is also where my attention went: a few in-line comments to help ensure we do not forget that for float we allow loss of precision with "safe".
| treats a Python ``int``, ``float`` or ``complex`` by its kind, since the | ||
| scalar has no precision of its own. Converting a Python ``int`` to any NumPy | ||
| integer dtype or a Python ``float`` to any NumPy floating point dtype counts | ||
| as "safe", even though the value may not fit. A value that does not fit |
There was a problem hiding this comment.
"may not fit or loose precision" (for float).
| scalar has no precision of its own. Converting a Python ``int`` to any NumPy | ||
| integer dtype or a Python ``float`` to any NumPy floating point dtype counts | ||
| as "safe", even though the value may not fit. A value that does not fit | ||
| raises ``OverflowError`` on conversion, as shown above. Lowering the kind, |
There was a problem hiding this comment.
"While lowering a python float to a lower-precision type is considered safe,, lowering it to an integer dtype requires casting="unsafe". ..."
| as "safe", even though the value may not fit. A value that does not fit | ||
| raises ``OverflowError`` on conversion, as shown above. Lowering the kind, | ||
| such as a Python ``float`` into an integer dtype, requires | ||
| ``casting="unsafe"``. `numpy.choose` applies the same rules with "safe" |
There was a problem hiding this comment.
Why is numpy.choose mentioned here (could perhaps be at the end?)
There was a problem hiding this comment.
Thanks. I just dropped it because this section isn't really about that and it's an obscure detail.
285de86 to
c677093
Compare
|
@seberg any chance I can get your opinion here? This is docs and two fixes for things that were missed in the NEP 50 transition. |
b2f2a63 to
59accf2
Compare
|
I went ahead and pushed another commit that removes use of |
seberg
left a comment
There was a problem hiding this comment.
Thanks this looks nice. The question is if we dare do the concatenate change as it could well break someone.
A deprecation is always nice I guess, but overall, I think I can live with it (until someone sounds the warning bells that they are hitting it of course!).
Really, I have never seen anyone rely on concatenate with axis=None and even if someone does it, the plausible issue is niche around:
concatenate((-1, arr, -1), dtype="uint8")
or so. Plausible, but strange.
But maybe I am getting a bit too relaxed about this these days. I just really think this'll break at most a number of people countable on one hand (of course if one of those is a large library).
| scalars with the result dtype, as ufuncs and `numpy.copyto` do. A Python | ||
| integer that does not fit the result dtype raises ``OverflowError`` instead of | ||
| wrapping. For example, ``np.concatenate((np.ones(2, "int8"), 300), axis=None)`` | ||
| previously returned ``44`` for the new element. |
There was a problem hiding this comment.
Yeah, I guess this change would be the one to worry about. But concatenating numbers with axis=None seems so awkward that...
(I have seen concatenate(([1], array)) more, although this actually probably is what those would want to use :))
There was a problem hiding this comment.
I just did another AI-assisted scan and couldn't find cases where the new concatenate behavior would cause breakage in popular downstream libraries.
However the append change does lead to breakage (numba encodes our current behavior, for one), so I'm going to go ahead and back that out from this PR and then do a second PR that introduces a deprecation warning cycle.
| ... | ||
| TypeError: Cannot cast scalar from dtype('float64') to dtype('int8') according to the rule 'same_kind' | ||
|
|
||
| Under ``casting="equiv"`` and ``casting="no"`` a Python scalar must convert |
There was a problem hiding this comment.
I might be tempted to not worry about this, but I guess for completeness sake it makes sense.
(I really don't see why a user would ever want to do this, except maybe being surprised by it actually being so strict.)
There was a problem hiding this comment.
Which is why I think it's safe to change. I'm only changing this because Marten noticed it looking at another PR and because I like fixing consistency holes like this.
| if arr.ndim != 1: | ||
| arr = arr.ravel() | ||
| values = ravel(values) | ||
| axis = arr.ndim - 1 |
59accf2 to
2a2aaa7
Compare
|
(Just saw the release note trimming, looks good too, and thanks, I like them short!) |
PR summary
This makes
np.concatenate(axis=None)andnp.choosecast the temporary array made from a Python scalar instead of converting the scalar with the resolved dtype.Before:
After:
This also makes
casting='no'an error in spots wherecasting='equiv'is an error. Closes #32491, see discussion there for details.While we're at it, also adds reference docs for the NumPy scalar casting rules.
I searched for open source codebases that this change would break and couldn't find any, but I could have easily missed things. Maybe the new errors should be deprecations instead?
The first commit duplicates #32496 because the tests in this PR crash without that fix. I'm making that PR separately from this one to backport the crash fix.
AI Disclosure
I iterated with an AI model on the code changes and new tests.