-
Notifications
You must be signed in to change notification settings - Fork 259
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
Replace np.sctypes for numpy 2.0 compat #1250
Conversation
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## master #1250 +/- ##
==========================================
+ Coverage 92.22% 92.25% +0.02%
==========================================
Files 99 99
Lines 12454 12466 +12
Branches 2559 2564 +5
==========================================
+ Hits 11486 11500 +14
+ Misses 644 643 -1
+ Partials 324 323 -1
☔ View full report in Codecov by Sentry. |
Thank you! I'm a little bit dreading cleaning up the tests for numpy 2 (#1242), but happy to take preemptive fixes that don't break existing numpy. Would you care to add yourself to the Zenodo and author list? |
Actually, would you mind running |
I suspect this is just a workaround since the release notes say:
So really it seems like the code should be updated not to use |
I don't read this as a deprecation of |
I asked on: numpy/numpy#24376 Not sure how likely they are to see a question on a closed PR, so might ask again in a couple days if we don't get a response. |
Perfect, at least that will be a clear answer :) |
Looks like |
Well that was quick. I don't have time today, but I can have a look tomorrow to replace |
nibabel/casting.py
Outdated
# np.sctypes is deprecated in numpy 2.0 and np.core.sctypes should not be used instead. | ||
sctypes = { | ||
"int": [np.int8, np.int16, np.int32, np.int64], | ||
"uint": [np.uint8, np.uint16, np.uint32, np.uint64], | ||
"float": [np.float16, np.float32, np.float64, np.float128], | ||
"complex": [np.complex64, np.complex128, np.complex256], | ||
"others": [bool, object, bytes, str, np.void], | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not entirely convince this is enough to keep a working status on all platforms. It looks to me like np.sctypes
is defined per platform, and if this is correct, we might need to add some logic to define the mapping between strings and scalar types per platform as well.
nibabel/casting.py
Outdated
sctypes = { | ||
'int': [np.int8, np.int16, np.int32, np.int64], | ||
'uint': [np.uint8, np.uint16, np.uint32, np.uint64], | ||
'float': [np.float16, np.float32, np.float64, np.longdouble], | ||
'complex': [np.complex64, np.complex128, np.complex256], | ||
'others': [bool, object, bytes, str, np.void], | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm out of idea to correctly build this mapping dict on all platforms. I never worked with longlong
, longdouble
, clongdouble
and I am not familiar with the issues that are attached to them. A couple of ideas:
- Don't use the size aliases and let numpy figure that out (6c30a84). It should work, but compared to that commit the
list(set(...))
should be sorted by precision. One point eluded me however:np.byte
correctly points tonp.int8
,np.ubyte
tonp.uint8
, and so on.. except fornp.longlong
andnp.ulonglong
which points to themselves.np.ulonglong == np.uint64
is False and I guess that would cause issues. - create the mapping based on attributes, e.g.
hasattr(np, "float128")
.
Any good idea?
Hi, thanks for this, and I think this will work. I need to find a little time to think clearly about this problem. Just to let you know that I haven't forgotten about this, just a little slow to get back to it. |
Looks like this branch is getting out of date, trying to use it in MNE I get:
|
Green! (Other than some coverage misses that I think probably exist in @effigies feel free to review/merge if you're happy, only ended up at |
nibabel/tests/test_floating.py
Outdated
# TODO: Should this actually still overflow? Does it matter? | ||
if FP_OVERFLOW_WARN: | ||
ctx = pytest.raises(OverflowError) | ||
else: | ||
ctx = nullcontext() | ||
with ctx: | ||
as_int(val) | ||
with pytest.raises(OverflowError): | ||
with ctx: | ||
as_int(-val) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens with numpy 2.0? This seems like it should still fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not overflow. Agreed it's weird that it does not
nibabel/tests/test_volumeutils.py
Outdated
# This is the normal rule - no upcast from Python scalar | ||
# (on NumPy 2.0 it *will* upcast from a np.float64 scalar!) | ||
assert (f32_arr * 1.).dtype == np.float32 | ||
assert (f32_arr + 1.).dtype == np.float32 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The goal here seems to be contrasting to numpy casting rules, not numpy + Python scalar casting rules. I would just branch on numpy version about the expected dtype for using f64(...)
.
Co-authored-by: Chris Markiewicz <effigies@gmail.com>
Okay, I'm starting to understand what's going on here. Numpy previously had broken behavior around longdoubles and ints. If you install Python 3.6 and numpy 1.11, you get things like: In [1]: import numpy as np
In [2]: val = np.longdouble(2**1024) * 2
---------------------------------------------------------------------------
OverflowError Traceback (most recent call last)
<ipython-input-2-4bc60941a286> in <module>
----> 1 val = np.longdouble(2**1024) * 2
OverflowError: int too large to convert to float
In [6]: val = np.longdouble(2**1023)
In [7]: val
Out[7]: 8.9884656743115795386e+307
In [8]: val * 2
Out[8]: 1.7976931348623159077e+308
In [9]: val * 4
Out[9]: 3.5953862697246318155e+308
In [10]: int(val * 4)
---------------------------------------------------------------------------
OverflowError Traceback (most recent call last)
<ipython-input-10-af59c627c981> in <module>
----> 1 int(val * 4)
OverflowError: cannot convert float infinity to integer However, if you use Python 3.8 and numpy 1.20 (nibabel current minimum requirements): In [1]: import numpy as np
In [2]: nexp64 = 1024
In [3]: val = np.longdouble(2**1024) * 2
In [4]: val
Out[4]: 3.5953862697246318155e+308
In [5]: int(val)
Out[5]: 359538626972463181545861038157804946723595395788461314546860162315465351611001926265416954644815072042240227759742786715317579537628833244985694861278948248755535786849730970552604439202492188238906165904170011537676301364684925762947826221081654474326701021369172596479894491876959432609670712659248448274432 I think at this point, probably the correct thing is just to get rid of |
further info: numpy/numpy#17325 nipy/nibabel#1250
further info: numpy/numpy#17325 nipy/nibabel#1250
further info: numpy/numpy#17325 nipy/nibabel#1250
further info: numpy/numpy#17325 nipy/nibabel#1250
With nightly numpy release, the following attribute error is raised:
corresponding to this bullet point in the release notes:
This PR replaces:
np.sctypes
calls withnp.core.sctypes
arr.newbyteorder
witharr.view(arr.dtype.newbyteorder())
int
to explicit casts then multiplynp.can_cast
usage