Skip to content

Commit

Permalink
Merge pull request astropy#16142 from neutrinoceros/numpy2/array_copy…
Browse files Browse the repository at this point in the history
…_False

BUG: fix compatibility with numpy 2.0 copy semantics
  • Loading branch information
mhvk authored Mar 5, 2024
2 parents d3a39fa + 6068539 commit d32a06a
Show file tree
Hide file tree
Showing 22 changed files with 87 additions and 50 deletions.
16 changes: 5 additions & 11 deletions astropy/convolution/convolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,10 @@ def _copy_input_if_needed(
# is no way to specify the return type or order etc. In addition
# ``np.nan`` is a ``float`` and there is no conversion to an
# ``int`` type. Therefore, a pre-fill copy is needed for non
# ``float`` masked arrays. ``subok=True`` is needed to retain
# ``np.ma.maskedarray.filled()``. ``copy=False`` allows the fill
# to act as the copy if type and order are already correct.
output = np.array(
input, dtype=dtype, copy=False, order=order, subok=True
)
# ``float`` masked arrays. ``asanyarray`` is needed to retain
# ``np.ma.maskedarray.filled()``.
# A copy is made if and only if order isn't already correct.
output = np.asanyarray(input, dtype=dtype, order=order)
output = output.filled(fill_value)
else:
# Since we're making a copy, we might as well use `subok=False` to save,
Expand All @@ -114,11 +112,7 @@ def _copy_input_if_needed(
# mask != 0 yields a bool mask for all ints/floats/bool
output[mask != 0] = fill_value
else:
# The call below is synonymous with np.asanyarray(array, ftype=float, order='C')
# The advantage of `subok=True` is that it won't copy when array is an ndarray subclass.
# If it is and `subok=False` (default), then it will copy even if `copy=False`. This
# uses less memory when ndarray subclasses are passed in.
output = np.array(input, dtype=dtype, copy=False, order=order, subok=True)
output = np.asanyarray(input, dtype=dtype, order=order)
except (TypeError, ValueError) as e:
raise TypeError(
"input should be a Numpy array or something convertible into a float array",
Expand Down
2 changes: 1 addition & 1 deletion astropy/convolution/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def __rmul__(self, value):
"""
return kernel_arithmetics(self, value, "mul")

def __array__(self):
def __array__(self, dtype=None, copy=None):
"""
Array representation of the kernel.
"""
Expand Down
5 changes: 3 additions & 2 deletions astropy/coordinates/polarization.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import numpy as np

from astropy.utils import unbroadcast
from astropy.utils.compat import COPY_IF_NEEDED
from astropy.utils.data_info import MixinInfo
from astropy.utils.shapes import ShapedLikeNDArray

Expand Down Expand Up @@ -186,8 +187,8 @@ def value(self):
def dtype(self):
return self._data.dtype

def __array__(self, dtype=None):
return self._data.astype(dtype, copy=False)
def __array__(self, dtype=None, copy=COPY_IF_NEEDED):
return self._data.astype(dtype, copy=copy)

def _apply(self, method, *args, **kwargs):
cls = type(self)
Expand Down
3 changes: 2 additions & 1 deletion astropy/coordinates/tests/test_representation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2220,7 +2220,8 @@ def test_roundtrip(self, item):
)
def test_differential_norm_noncartesian(cls):
# The norm of a non-Cartesian differential without specifying `base` should error
rep = cls(0, 0, 0)
args = (0,) * len(cls.attr_classes)
rep = cls(*args)
with pytest.raises(ValueError, match=r"`base` must be provided .* " + cls.__name__):
rep.norm()

Expand Down
5 changes: 3 additions & 2 deletions astropy/modeling/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from astropy.units import MagUnit, Quantity
from astropy.utils import isiterable
from astropy.utils.compat import COPY_IF_NEEDED

from .utils import array_repr_oneline, get_inputs_and_params

Expand Down Expand Up @@ -732,12 +733,12 @@ def _create_value_wrapper(self, wrapper, model):

return wrapper

def __array__(self, dtype=None):
def __array__(self, dtype=None, copy=COPY_IF_NEEDED):
# Make np.asarray(self) work a little more straightforwardly
arr = np.asarray(self.value, dtype=dtype)

if self.unit is not None:
arr = Quantity(arr, self.unit, copy=False, subok=True)
arr = Quantity(arr, self.unit, copy=copy, subok=True)

return arr

Expand Down
13 changes: 7 additions & 6 deletions astropy/modeling/polynomial.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import numpy as np

from astropy.utils import check_broadcast, indent
from astropy.utils.compat import COPY_IF_NEEDED

from .core import FittableModel, Model
from .functional_models import Shift
Expand Down Expand Up @@ -533,7 +534,7 @@ def fit_deriv(self, x, *params):
result : ndarray
The Vandermonde matrix
"""
x = np.array(x, dtype=float, copy=False, ndmin=1)
x = np.array(x, dtype=float, copy=COPY_IF_NEEDED, ndmin=1)
v = np.empty((self.degree + 1,) + x.shape, dtype=x.dtype)
v[0] = 1
if self.degree > 0:
Expand Down Expand Up @@ -654,7 +655,7 @@ def fit_deriv(self, x, *params):
result : ndarray
The Vandermonde matrix
"""
x = np.array(x, dtype=float, copy=False, ndmin=1)
x = np.array(x, dtype=float, copy=COPY_IF_NEEDED, ndmin=1)
v = np.empty((self.degree + 1,) + x.shape, dtype=x.dtype)
v[0] = 1
if self.degree > 0:
Expand Down Expand Up @@ -833,7 +834,7 @@ def _hermderiv1d(self, x, deg):
"""
Derivative of 1D Hermite series.
"""
x = np.array(x, dtype=float, copy=False, ndmin=1)
x = np.array(x, dtype=float, copy=COPY_IF_NEEDED, ndmin=1)
d = np.empty((deg + 1, len(x)), dtype=x.dtype)
d[0] = x * 0 + 1
if deg > 0:
Expand Down Expand Up @@ -936,7 +937,7 @@ def fit_deriv(self, x, *params):
result : ndarray
The Vandermonde matrix
"""
x = np.array(x, dtype=float, copy=False, ndmin=1)
x = np.array(x, dtype=float, copy=COPY_IF_NEEDED, ndmin=1)
v = np.empty((self.degree + 1,) + x.shape, dtype=x.dtype)
v[0] = 1
if self.degree > 0:
Expand Down Expand Up @@ -1489,7 +1490,7 @@ def _chebderiv1d(self, x, deg):
"""
Derivative of 1D Chebyshev series.
"""
x = np.array(x, dtype=float, copy=False, ndmin=1)
x = np.array(x, dtype=float, copy=COPY_IF_NEEDED, ndmin=1)
d = np.empty((deg + 1, len(x)), dtype=x.dtype)
d[0] = x * 0 + 1
if deg > 0:
Expand Down Expand Up @@ -1643,7 +1644,7 @@ def fit_deriv(self, x, y, *params):

def _legendderiv1d(self, x, deg):
"""Derivative of 1D Legendre polynomial."""
x = np.array(x, dtype=float, copy=False, ndmin=1)
x = np.array(x, dtype=float, copy=COPY_IF_NEEDED, ndmin=1)
d = np.empty((deg + 1,) + x.shape, dtype=x.dtype)
d[0] = x * 0 + 1
if deg > 0:
Expand Down
16 changes: 11 additions & 5 deletions astropy/nddata/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from astropy import log
from astropy.units import Unit, UnitConversionError, UnitsError # noqa: F401
from astropy.utils.compat import COPY_IF_NEEDED

from .flag_collection import FlagCollection
from .mixins.ndarithmetic import NDArithmeticMixin
Expand Down Expand Up @@ -168,7 +169,7 @@ def mask(self):
def mask(self, value):
# Check that value is not either type of null mask.
if (value is not None) and (value is not np.ma.nomask):
mask = np.array(value, dtype=np.bool_, copy=False)
mask = np.asarray(value, dtype=np.bool_)
if mask.shape != self.data.shape:
raise ValueError(
f"dimensions of mask {mask.shape} and data {self.data.shape} do not match"
Expand Down Expand Up @@ -220,23 +221,28 @@ def flags(self, value):
else:
self._flags = value
else:
flags = np.array(value, copy=False)
flags = np.asarray(value)
if flags.shape != self.shape:
raise ValueError("dimensions of flags do not match data")
else:
self._flags = flags
else:
self._flags = value

def __array__(self):
def __array__(self, dtype=None, copy=COPY_IF_NEEDED):
"""
This allows code that requests a Numpy array to use an NDData
object as a Numpy array.
"""
if self.mask is not None:
return np.ma.masked_array(self.data, self.mask)
return np.ma.masked_array(
self.data,
self.mask,
dtype=dtype,
copy=copy,
)
else:
return np.array(self.data)
return np.array(self.data, dtype=dtype, copy=copy)

def __array_wrap__(self, array, context=None, return_scalar=False):
"""
Expand Down
2 changes: 1 addition & 1 deletion astropy/nddata/nddata.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ def __init__(
):
# Data doesn't look like a numpy array, try converting it to
# one.
data = np.array(data, subok=True, copy=False)
data = np.asanyarray(data)
# Another quick check to see if what we got looks like an array
# rather than an object (since numpy will convert a
# non-numerical/non-string inputs to an array of objects).
Expand Down
2 changes: 1 addition & 1 deletion astropy/nddata/nduncertainty.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def array(self):
@array.setter
def array(self, value):
if isinstance(value, (list, np.ndarray)):
value = np.array(value, subok=False, copy=False)
value = np.asarray(value)
self._array = value

@property
Expand Down
2 changes: 1 addition & 1 deletion astropy/nddata/tests/test_nddata.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def shape(self):
def __getitem__(self, key):
pass

def __array__(self):
def __array__(self, dtype=None, copy=None):
pass

@property
Expand Down
5 changes: 5 additions & 0 deletions astropy/table/column.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,11 @@ def __new__(
copy=False,
copy_indices=True,
):
if not NUMPY_LT_2_0 and copy is False:
# strict backward compatibility
# see https://github.com/astropy/astropy/pull/16142
copy = None

if data is None:
self_data = np.zeros((length,) + shape, dtype=dtype)
elif isinstance(data, BaseColumn) and hasattr(data, "_name"):
Expand Down
6 changes: 4 additions & 2 deletions astropy/table/row.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import numpy as np

from astropy.utils.compat import COPY_IF_NEEDED


class Row:
"""A class to represent one row of a Table object.
Expand Down Expand Up @@ -88,7 +90,7 @@ def __ne__(self, other):
)
return self.as_void() != other

def __array__(self, dtype=None):
def __array__(self, dtype=None, copy=COPY_IF_NEEDED):
"""Support converting Row to np.array via np.array(table).
Coercion to a different dtype via np.array(table, dtype) is not
Expand All @@ -99,7 +101,7 @@ def __array__(self, dtype=None):
if dtype is not None:
raise ValueError("Datatype coercion is not allowed")

return np.asarray(self.as_void())
return np.array(self.as_void(), copy=copy)

def __len__(self):
return len(self._table.columns)
Expand Down
8 changes: 4 additions & 4 deletions astropy/table/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
deprecated,
isiterable,
)
from astropy.utils.compat import NUMPY_LT_1_25
from astropy.utils.compat import COPY_IF_NEEDED, NUMPY_LT_1_25
from astropy.utils.console import color_print
from astropy.utils.data_info import BaseColumnInfo, DataInfo, MixinInfo
from astropy.utils.decorators import format_doc
Expand Down Expand Up @@ -1129,7 +1129,7 @@ def index_mode(self, mode):
"""
return _IndexModeContext(self, mode)

def __array__(self, dtype=None):
def __array__(self, dtype=None, copy=COPY_IF_NEEDED):
"""Support converting Table to np.array via np.array(table).
Coercion to a different dtype via np.array(table, dtype) is not
Expand All @@ -1139,7 +1139,7 @@ def __array__(self, dtype=None):
if np.dtype(dtype) != object:
raise ValueError("Datatype coercion is not allowed")

out = np.array(None, dtype=object)
out = np.array(None, dtype=object, copy=copy)
out[()] = self
return out

Expand Down Expand Up @@ -1459,7 +1459,7 @@ def _convert_col_for_table(self, col):
if isinstance(col, Column) and not isinstance(col, self.ColumnClass):
col_cls = self._get_col_cls_for_table(col)
if col_cls is not col.__class__:
col = col_cls(col, copy=False)
col = col_cls(col, copy=COPY_IF_NEEDED)

return col

Expand Down
2 changes: 1 addition & 1 deletion astropy/table/tests/test_column.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def test_convert_numpy_array(self, Column):

np_data = np.array(d)
assert np.all(np_data == d)
np_data = np.array(d, copy=False)
np_data = np.asarray(d)
assert np.all(np_data == d)
np_data = np.array(d, dtype="i4")
assert np.all(np_data == d)
Expand Down
2 changes: 1 addition & 1 deletion astropy/table/tests/test_row.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def test_convert_numpy_array(self, table_types):
assert np_data is not d.as_void()
assert d.colnames == list(np_data.dtype.names)

np_data = np.array(d, copy=False)
np_data = np.asarray(d)
if table_types.Table is not MaskedTable:
assert np.all(np_data == d.as_void())
assert np_data is not d.as_void()
Expand Down
2 changes: 1 addition & 1 deletion astropy/table/tests/test_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -1508,7 +1508,7 @@ def test_convert_numpy_array(self, table_types):
assert np_data is not d.as_array()
assert d.colnames == list(np_data.dtype.names)

np_data = np.array(d, copy=False)
np_data = np.asarray(d)
if table_types.Table is not MaskedTable:
assert np.all(np_data == d.as_array())
assert d.colnames == list(np_data.dtype.names)
Expand Down
12 changes: 11 additions & 1 deletion astropy/time/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from astropy.extern import _strptime
from astropy.units import UnitConversionError
from astropy.utils import ShapedLikeNDArray, lazyproperty
from astropy.utils.compat import PYTHON_LT_3_11
from astropy.utils.compat import NUMPY_LT_2_0, PYTHON_LT_3_11
from astropy.utils.data_info import MixinInfo, data_info_factory
from astropy.utils.exceptions import AstropyDeprecationWarning, AstropyWarning
from astropy.utils.masked import Masked
Expand Down Expand Up @@ -1971,6 +1971,11 @@ def __init__(
location=None,
copy=False,
):
if not NUMPY_LT_2_0 and copy is False:
# strict backward compatibility
# see https://github.com/astropy/astropy/pull/16142
copy = None

if location is not None:
from astropy.coordinates import EarthLocation

Expand Down Expand Up @@ -3365,6 +3370,11 @@ def _make_array(val, copy=False):
else:
dtype = None

if not NUMPY_LT_2_0 and copy is False:
# strict backward compatibility
# see https://github.com/astropy/astropy/pull/16142
copy = None

val = np.array(val, copy=copy, subok=True, dtype=dtype)

# Allow only float64, string or object arrays as input
Expand Down
2 changes: 1 addition & 1 deletion astropy/uncertainty/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,7 @@ def __setitem__(self, item, value):
# If value is not already a Distribution, first make it an array
# to help interpret possible structured dtype, and then turn it
# into a Distribution with n_samples=1 (which will broadcast).
value = np.array(value, dtype=self.dtype, copy=False, subok=True)
value = np.asanyarray(value, dtype=self.dtype)
value = Distribution(value[..., np.newaxis])

super().__setitem__(item, value)
Expand Down
Loading

0 comments on commit d32a06a

Please sign in to comment.