- Notifications
You must be signed in to change notification settings - Fork75
Add support for NumPy 2.0#232
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -265,6 +265,8 @@ | ||
""" | ||
class QuantitiesDeprecationWarning(DeprecationWarning): | ||
pass | ||
from ._version import __version__ | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -9,6 +9,8 @@ | ||
from .registry import unit_registry | ||
from .decorators import memoize | ||
_np_version = tuple(map(int, np.__version__.split('.'))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. This is now breaking the tests in MNE:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Are you all testing against a development version of NumPy? I don't think@bjodah or I thought about that in these PRs for parsing the numpy version. I guess a version parse instead would be better if you want to defend against dev version testing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. MNE is testing against stable and the development version of | ||
def assert_isinstance(obj, types): | ||
try: | ||
assert isinstance(obj, types) | ||
@@ -329,10 +331,11 @@ def _d_copy(q1, out=None): | ||
def _d_clip(a1, a2, a3, q): | ||
return q.dimensionality | ||
if _np_version < (2, 0, 0): | ||
p_dict[np.core.umath.clip] = _d_clip | ||
else: | ||
p_dict[np.clip] = _d_clip | ||
def _d_sqrt(q1, out=None): | ||
return q1._dimensionality**0.5 | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -3,10 +3,11 @@ | ||
import copy | ||
from functools import wraps | ||
import warnings | ||
import numpy as np | ||
from . import markup, QuantitiesDeprecationWarning | ||
from .dimensionality import Dimensionality, p_dict | ||
from .registry import unit_registry | ||
from .decorators import with_doc | ||
@@ -114,15 +115,19 @@ class Quantity(np.ndarray): | ||
# TODO: what is an appropriate value? | ||
__array_priority__ = 21 | ||
def __new__(cls, data, units='', dtype=None, copy=None): | ||
if copy is not None: | ||
warnings.warn(("The 'copy' argument in Quantity is deprecated and will be removed in the future. " | ||
"The argument has no effect since quantities-0.16.0 (to aid numpy-2.0 support)."), | ||
QuantitiesDeprecationWarning, stacklevel=2) | ||
if isinstance(data, Quantity): | ||
if units: | ||
data = data.rescale(units) | ||
if isinstance(data, unit_registry['UnitQuantity']): | ||
return 1*data | ||
return np.asanyarray(data, dtype=dtype).view(cls) | ||
ret = np.asarray(data, dtype=dtype).view(cls) | ||
ContributorAuthor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. After these changes we are no longer using the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. That's a good idea. | ||
ret._dimensionality.update(validate_dimensionality(units)) | ||
return ret | ||
@@ -210,15 +215,17 @@ def rescale(self, units=None, dtype=None): | ||
dtype = self.dtype | ||
if self.dimensionality == to_dims: | ||
return self.astype(dtype) | ||
to_u = Quantity(1.0, to_dims, dtype=dtype) | ||
from_u = Quantity(1.0, self.dimensionality, dtype=dtype) | ||
try: | ||
cf = get_conversion_factor(from_u, to_u) | ||
except AssertionError: | ||
raise ValueError( | ||
'Unable to convert between units of "%s" and "%s"' | ||
%(from_u._dimensionality, to_u._dimensionality) | ||
) | ||
if np.dtype(dtype).kind in 'fc': | ||
cf = np.array(cf, dtype=dtype) | ||
new_magnitude = cf*self.magnitude | ||
dtype = np.result_type(dtype, new_magnitude) | ||
return Quantity(new_magnitude, to_u, dtype=dtype) | ||
@@ -272,7 +279,7 @@ def __array_prepare__(self, obj, context=None): | ||
uf, objs, huh = context | ||
if uf.__name__.startswith('is'): | ||
return obj | ||
try: | ||
res._dimensionality = p_dict[uf](*objs) | ||
except KeyError: | ||
@@ -283,11 +290,12 @@ def __array_prepare__(self, obj, context=None): | ||
) | ||
return res | ||
def __array_wrap__(self, obj, context=None, return_scalar=False): | ||
if not isinstance(obj, Quantity): | ||
# backwards compatibility with numpy-1.3 | ||
return self.__array_prepare__(obj, context) | ||
else: | ||
return super().__array_wrap__(obj, context, return_scalar) | ||
Comment on lines +296 to +298 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. I'm confused by this. The previous array wrap would only do something if the object was not a Quantity if the object was already a quantity it would just return the object without any changes. Here you force thearray_wrap even in the case of the object being a Quantity. It seems like for the case of some failing tests the objects are losing their dimensionality and this seems like it could be a place where a Quantity should be returned, no? I'm not super familiar with this deep level of array wrapping so just thought maybe a discussion of this could help point us to the actual test failure issue. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. @bjodah just wanted to bump my message. Does this seem like it could be the problem with the tests failing? | ||
@with_doc(np.ndarray.__add__) | ||
@scale_other_units | ||
@@ -476,7 +484,7 @@ def sum(self, axis=None, dtype=None, out=None): | ||
ret = self.magnitude.sum(axis, dtype, None if out is None else out.magnitude) | ||
dim = self.dimensionality | ||
if out is None: | ||
return Quantity(ret, dim) | ||
if not isinstance(out, Quantity): | ||
raise TypeError("out parameter must be a Quantity") | ||
out._dimensionality = dim | ||
@@ -487,8 +495,7 @@ def nansum(self, axis=None, dtype=None, out=None): | ||
import numpy as np | ||
return Quantity( | ||
np.nansum(self.magnitude, axis, dtype, out), | ||
self.dimensionality | ||
) | ||
@with_doc(np.ndarray.fill) | ||
@@ -523,7 +530,7 @@ def argsort(self, axis=-1, kind='quick', order=None): | ||
@with_doc(np.ndarray.searchsorted) | ||
def searchsorted(self,values, side='left'): | ||
if not isinstance (values, Quantity): | ||
values = Quantity(values) | ||
if values._dimensionality != self._dimensionality: | ||
raise ValueError("values does not have the same units as self") | ||
@@ -539,7 +546,7 @@ def max(self, axis=None, out=None): | ||
ret = self.magnitude.max(axis, None if out is None else out.magnitude) | ||
dim = self.dimensionality | ||
if out is None: | ||
return Quantity(ret, dim) | ||
if not isinstance(out, Quantity): | ||
raise TypeError("out parameter must be a Quantity") | ||
out._dimensionality = dim | ||
@@ -553,16 +560,15 @@ def argmax(self, axis=None, out=None): | ||
def nanmax(self, axis=None, out=None): | ||
return Quantity( | ||
np.nanmax(self.magnitude), | ||
self.dimensionality | ||
) | ||
@with_doc(np.ndarray.min) | ||
def min(self, axis=None, out=None): | ||
ret = self.magnitude.min(axis, None if out is None else out.magnitude) | ||
dim = self.dimensionality | ||
if out is None: | ||
return Quantity(ret, dim) | ||
if not isinstance(out, Quantity): | ||
raise TypeError("out parameter must be a Quantity") | ||
out._dimensionality = dim | ||
@@ -572,8 +578,7 @@ def min(self, axis=None, out=None): | ||
def nanmin(self, axis=None, out=None): | ||
return Quantity( | ||
np.nanmin(self.magnitude), | ||
self.dimensionality | ||
) | ||
@with_doc(np.ndarray.argmin) | ||
@@ -590,10 +595,10 @@ def nanargmax(self,axis=None, out=None): | ||
@with_doc(np.ndarray.ptp) | ||
def ptp(self, axis=None, out=None): | ||
ret =np.ptp(self.magnitude,axis, None if out is None else out.magnitude) | ||
dim = self.dimensionality | ||
if out is None: | ||
return Quantity(ret, dim) | ||
if not isinstance(out, Quantity): | ||
raise TypeError("out parameter must be a Quantity") | ||
out._dimensionality = dim | ||
@@ -620,7 +625,7 @@ def clip(self, min=None, max=None, out=None): | ||
) | ||
dim = self.dimensionality | ||
if out is None: | ||
return Quantity(clipped, dim) | ||
if not isinstance(out, Quantity): | ||
raise TypeError("out parameter must be a Quantity") | ||
out._dimensionality = dim | ||
@@ -631,7 +636,7 @@ def round(self, decimals=0, out=None): | ||
ret = self.magnitude.round(decimals, None if out is None else out.magnitude) | ||
dim = self.dimensionality | ||
if out is None: | ||
return Quantity(ret, dim) | ||
if not isinstance(out, Quantity): | ||
raise TypeError("out parameter must be a Quantity") | ||
out._dimensionality = dim | ||
@@ -642,7 +647,7 @@ def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None): | ||
ret = self.magnitude.trace(offset, axis1, axis2, dtype, None if out is None else out.magnitude) | ||
dim = self.dimensionality | ||
if out is None: | ||
return Quantity(ret, dim) | ||
if not isinstance(out, Quantity): | ||
raise TypeError("out parameter must be a Quantity") | ||
out._dimensionality = dim | ||
@@ -652,16 +657,15 @@ def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None): | ||
def squeeze(self, axis=None): | ||
return Quantity( | ||
self.magnitude.squeeze(axis), | ||
self.dimensionality | ||
) | ||
@with_doc(np.ndarray.mean) | ||
def mean(self, axis=None, dtype=None, out=None): | ||
ret = self.magnitude.mean(axis, dtype, None if out is None else out.magnitude) | ||
dim = self.dimensionality | ||
if out is None: | ||
return Quantity(ret, dim) | ||
if not isinstance(out, Quantity): | ||
raise TypeError("out parameter must be a Quantity") | ||
out._dimensionality = dim | ||
@@ -672,15 +676,14 @@ def nanmean(self, axis=None, dtype=None, out=None): | ||
import numpy as np | ||
return Quantity( | ||
np.nanmean(self.magnitude, axis, dtype, out), | ||
self.dimensionality) | ||
@with_doc(np.ndarray.var) | ||
def var(self, axis=None, dtype=None, out=None, ddof=0): | ||
ret = self.magnitude.var(axis, dtype, out, ddof) | ||
dim = self._dimensionality**2 | ||
if out is None: | ||
return Quantity(ret, dim) | ||
if not isinstance(out, Quantity): | ||
raise TypeError("out parameter must be a Quantity") | ||
out._dimensionality = dim | ||
@@ -691,7 +694,7 @@ def std(self, axis=None, dtype=None, out=None, ddof=0): | ||
ret = self.magnitude.std(axis, dtype, out, ddof) | ||
dim = self.dimensionality | ||
if out is None: | ||
return Quantity(ret, dim) | ||
if not isinstance(out, Quantity): | ||
raise TypeError("out parameter must be a Quantity") | ||
out._dimensionality = dim | ||
@@ -701,8 +704,7 @@ def std(self, axis=None, dtype=None, out=None, ddof=0): | ||
def nanstd(self, axis=None, dtype=None, out=None, ddof=0): | ||
return Quantity( | ||
np.nanstd(self.magnitude, axis, dtype, out, ddof), | ||
self._dimensionality | ||
) | ||
@with_doc(np.ndarray.prod) | ||
@@ -715,7 +717,7 @@ def prod(self, axis=None, dtype=None, out=None): | ||
ret = self.magnitude.prod(axis, dtype, None if out is None else out.magnitude) | ||
dim = self._dimensionality**power | ||
if out is None: | ||
return Quantity(ret, dim) | ||
if not isinstance(out, Quantity): | ||
raise TypeError("out parameter must be a Quantity") | ||
out._dimensionality = dim | ||
@@ -726,7 +728,7 @@ def cumsum(self, axis=None, dtype=None, out=None): | ||
ret = self.magnitude.cumsum(axis, dtype, None if out is None else out.magnitude) | ||
dim = self.dimensionality | ||
if out is None: | ||
return Quantity(ret, dim) | ||
if not isinstance(out, Quantity): | ||
raise TypeError("out parameter must be a Quantity") | ||
out._dimensionality = dim | ||
@@ -743,7 +745,7 @@ def cumprod(self, axis=None, dtype=None, out=None): | ||
ret = self.magnitude.cumprod(axis, dtype, out) | ||
dim = self.dimensionality | ||
if out is None: | ||
return Quantity(ret, dim) | ||
if isinstance(out, Quantity): | ||
out._dimensionality = dim | ||
return out | ||
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.