diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a2dc01b8987..e14c1e4a4429 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ This release is compatible with NumPy 2.5. ### Deprecated +* Deprecated setting the shape via the `shape` attribute of `dpnp.ndarray` and `dpnp.tensor.usm_ndarray`. Use `dpnp.reshape`/`dpnp.tensor.reshape` (or the `reshape` method) instead [#2967](https://github.com/IntelPython/dpnp/pull/2967) + ### Removed * Removed support for arrays of 2-dimensional vectors in `dpnp.cross`, which now requires (arrays of) 3-dimensional vectors and raises `ValueError` otherwise [#2950](https://github.com/IntelPython/dpnp/pull/2950) diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index c5a85971f473..e9eebb4b62e3 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -1835,10 +1835,11 @@ def shape(self): For full documentation refer to :obj:`numpy.ndarray.shape`. - Note - ---- + Warnings + -------- + Setting ``a.shape`` is deprecated and may be removed in the future. Using :obj:`dpnp.ndarray.reshape` or :obj:`dpnp.reshape` is the - preferred approach to set new shape of an array. + preferred approach. See Also -------- @@ -1856,15 +1857,6 @@ def shape(self): >>> y.shape (2, 3, 4) - >>> y.shape = (3, 8) - >>> y - array([[ 0., 0., 0., 0., 0., 0., 0., 0.], - [ 0., 0., 0., 0., 0., 0., 0., 0.], - [ 0., 0., 0., 0., 0., 0., 0., 0.]]) - >>> y.shape = (3, 6) - ... - TypeError: Can not reshape array of size 24 into (3, 6) - """ return self._array_obj.shape @@ -1890,8 +1882,15 @@ def shape(self, newshape): New shape. Only non-negative values are supported. The new shape may not lead to the change in the number of elements in the array. + Warnings + -------- + Setting ``a.shape`` is deprecated and may be removed in the future. + Using :obj:`dpnp.ndarray.reshape` or :obj:`dpnp.reshape` is the + preferred approach. + """ + # the underlying usm_ndarray shape setter raises a deprecation warning self._array_obj.shape = newshape @property diff --git a/dpnp/dpnp_iface_arraycreation.py b/dpnp/dpnp_iface_arraycreation.py index 560ad16e1d59..7ed1eab03e93 100644 --- a/dpnp/dpnp_iface_arraycreation.py +++ b/dpnp/dpnp_iface_arraycreation.py @@ -3038,9 +3038,9 @@ def meshgrid(*xi, copy=True, sparse=False, indexing="xy"): """ Return a tuple of coordinate matrices from coordinate vectors. - Make N-D coordinate arrays for vectorized evaluations of - N-D scalar/vector fields over N-D grids, given - one-dimensional coordinate arrays ``x1, x2,..., xn``. + Make N-D coordinate arrays for vectorized evaluations of N-D scalar/vector + fields over N-D grids, given one-dimensional coordinate arrays + ``x1, x2,..., xn``. For full documentation refer to :obj:`numpy.meshgrid`. @@ -3054,10 +3054,12 @@ def meshgrid(*xi, copy=True, sparse=False, indexing="xy"): If ``True`` the shape of the returned coordinate array for dimension `i` is reduced from ``(N1, ..., Ni, ... Nn)`` to ``(1, ..., 1, Ni, 1, ..., 1)``. + Default: ``False``. copy : bool, optional If ``False``, a view into the original arrays are returned in order to conserve memory. + Default: ``True``. Returns @@ -3143,7 +3145,7 @@ def meshgrid(*xi, copy=True, sparse=False, indexing="xy"): class MGridClass: - """ + r""" Construct a dense multi-dimensional "meshgrid". For full documentation refer to :obj:`numpy.mgrid`. @@ -3175,6 +3177,13 @@ class MGridClass: A single array, containing a set of arrays all of the same dimensions, stacked along the first axis. + See Also + -------- + :obj:`dpnp.ogrid` : Work like :obj:`dpnp.mgrid` but returns open + (not fleshed out) mesh grids. + :obj:`dpnp.meshgrid`: Return coordinate matrices from coordinate vectors. + :obj:`dpnp.r_` : Array concatenator. + Examples -------- >>> import dpnp as np @@ -3247,21 +3256,30 @@ class OGridClass: Default: ``None``. usm_type : {None, "device", "shared", "host"}, optional The type of SYCL USM allocation for the output array. + Default: ``"device"``. sycl_queue : {None, SyclQueue}, optional A SYCL queue to use for output array allocation and copying. The `sycl_queue` can be passed as ``None`` (the default), which means to get the SYCL queue from `device` keyword if present or to use a default queue. + Default: ``None``. Returns ------- - out : dpnp.ndarray or tuple of dpnp.ndarray + out : one dpnp.ndarray or tuple of dpnp.ndarray If the input is a single slice, returns an array. If the input is multiple slices, returns a tuple of arrays, with only one dimension not equal to 1. + See Also + -------- + :obj:`dpnp.mgrid` : Work like :obj:`dpnp.ogrid` but returns dense + (or fleshed out) mesh grids. + :obj:`dpnp.meshgrid`: Return coordinate matrices from coordinate vectors. + :obj:`dpnp.r_` : Array concatenator. + Examples -------- >>> import dpnp as np diff --git a/dpnp/dpnp_iface_indexing.py b/dpnp/dpnp_iface_indexing.py index 2bf1594e0f58..a9e928f022dc 100644 --- a/dpnp/dpnp_iface_indexing.py +++ b/dpnp/dpnp_iface_indexing.py @@ -1071,12 +1071,12 @@ def indices( ------- out : one dpnp.ndarray or tuple of dpnp.ndarray If sparse is ``False``: - Returns one array of grid indices, - ``grid.shape = (len(dimensions),) + tuple(dimensions)``. + Returns one array of grid indices with + ``grid.shape == (len(dimensions),) + tuple(dimensions)``. If sparse is ``True``: Returns a tuple of arrays, - with grid[i].shape = (1, ..., 1, dimensions[i], 1, ..., 1) + with grid[i].shape == (1, ..., 1, dimensions[i], 1, ..., 1) with dimensions[i] in the i-th place. See Also @@ -1109,6 +1109,7 @@ def indices( Note that it would be more straightforward in the above example to extract the required elements directly with ``x[:2, :3]``. + If sparse is set to ``True``, the grid will be returned in a sparse representation. diff --git a/dpnp/dpnp_iface_linearalgebra.py b/dpnp/dpnp_iface_linearalgebra.py index acb123473482..a0bc197512e0 100644 --- a/dpnp/dpnp_iface_linearalgebra.py +++ b/dpnp/dpnp_iface_linearalgebra.py @@ -613,19 +613,20 @@ def inner(a, b): matches that of the array between `a` and `b`, whichever is an array. If `a` and `b` are both 1-D arrays then a 0-d array is returned; otherwise an array with a shape as - ``out.shape = (*a.shape[:-1], *b.shape[:-1])`` is returned. + ``out.shape == (*a.shape[:-1], *b.shape[:-1])`` is returned. See Also -------- :obj:`dpnp.einsum` : Einstein summation convention. :obj:`dpnp.dot` : Generalized matrix product, - using second last dimension of `b`. + using second last dimension of `b`. :obj:`dpnp.tensordot` : Sum products over arbitrary axes. + :obj:`dpnp.vecdot` : Vector dot product of two arrays. Examples -------- - # Ordinary inner product for vectors + Ordinary inner product for vectors: >>> import dpnp as np >>> a = np.array([1, 2, 3]) @@ -633,7 +634,7 @@ def inner(a, b): >>> np.inner(a, b) array(2) - # Some multidimensional examples + Some multidimensional examples: >>> a = np.arange(24).reshape((2, 3, 4)) >>> b = np.arange(4) @@ -652,7 +653,7 @@ def inner(a, b): >>> c array([[[1, 3, 5]]]) - An example where `b` is a scalar + An example where `b` is a scalar: >>> np.inner(np.eye(2), 7) array([[7., 0.], diff --git a/dpnp/dpnp_iface_mathematical.py b/dpnp/dpnp_iface_mathematical.py index f68fa68f3a63..9c2ad7032ab8 100644 --- a/dpnp/dpnp_iface_mathematical.py +++ b/dpnp/dpnp_iface_mathematical.py @@ -1818,8 +1818,8 @@ def ediff1d(ary, to_end=None, to_begin=None): :obj:`dpnp.floor` : Return the floor of the input, element-wise. :obj:`dpnp.ceil` : Return the ceiling of the input, element-wise. -Warning -------- +Warnings +-------- This function is deprecated. It is recommended to use :func:`dpnp.trunc` instead, as it provides the same functionality of truncating decimal values to their integer parts. @@ -3249,8 +3249,8 @@ def interp(x, xp, fp, left=None, right=None, period=None): Default: ``"K"``. -Warning -------- +Warnings +-------- Passing more than 2 positional arguments is deprecated. If you meant to use the third argument as an output, use the `out` keyword argument instead. @@ -3356,8 +3356,8 @@ def interp(x, xp, fp, left=None, right=None, period=None): An array containing the element-wise minima. The data type of the returned array is determined by the Type Promotion Rules. -Warning -------- +Warnings +-------- Passing more than 2 positional arguments is deprecated. If you meant to use the third argument as an output, use the `out` keyword argument instead. diff --git a/dpnp/dpnp_utils/dpnp_utils_linearalgebra.py b/dpnp/dpnp_utils/dpnp_utils_linearalgebra.py index c3070b8b25a6..37826256be2c 100644 --- a/dpnp/dpnp_utils/dpnp_utils_linearalgebra.py +++ b/dpnp/dpnp_utils/dpnp_utils_linearalgebra.py @@ -202,13 +202,13 @@ def _define_dim_flags(x, axis): """ Define useful flags for the calculations in dpnp_multiplication and dpnp_vecdot. x_is_1D: `x` is 1D array or inherently 1D (all dimensions are equal to one - except for dimension at `axis`), for instance, if x.shape = (1, 1, 1, 2), - and axis=-1, then x_is_1D = True. + except for dimension at `axis`), for instance, if ``x.shape == (1, 1, 1, 2)``, + and ``axis == -1``, then ``x_is_1D = True``. x_is_2D: `x` is 2D array or inherently 2D (all dimensions are equal to one - except for the last two of them), for instance, if x.shape = (1, 1, 3, 2), - then x_is_2D = True. + except for the last two of them), for instance, if ``x.shape == (1, 1, 3, 2)``, + then ``x_is_2D = True``. x_base_is_1D: `x` is 1D considering only its last two dimensions, for instance, - if x.shape = (3, 4, 1, 2), then x_base_is_1D = True. + if ``x.shape == (3, 4, 1, 2)``, then ``x_base_is_1D = True``. """ diff --git a/dpnp/linalg/dpnp_iface_linalg.py b/dpnp/linalg/dpnp_iface_linalg.py index ca46a91b2706..3e6f4bcab9bd 100644 --- a/dpnp/linalg/dpnp_iface_linalg.py +++ b/dpnp/linalg/dpnp_iface_linalg.py @@ -2083,17 +2083,21 @@ def tensorinv(a, ind=2): Examples -------- >>> import dpnp as np - >>> a = np.eye(4*6) - >>> a.shape = (4, 6, 8, 3) + >>> a = np.eye(4*6).reshape((4, 6, 8, 3)) >>> ainv = np.linalg.tensorinv(a, ind=2) >>> ainv.shape (8, 3, 4, 6) + >>> b = np.random.normal(size=(4, 6)) + >>> np.allclose(np.tensordot(ainv, b), np.linalg.tensorsolve(a, b)) + array(True) - >>> a = np.eye(4*6) - >>> a.shape = (24, 8, 3) + >>> a = np.eye(4*6).reshape((24, 8, 3)) >>> ainv = np.linalg.tensorinv(a, ind=1) >>> ainv.shape (8, 3, 24) + >>> b = np.random.normal(size=24) + >>> np.allclose(np.tensordot(ainv, b, axes=1), np.linalg.tensorsolve(a, b)) + array(True) """ @@ -2150,14 +2154,13 @@ def tensorsolve(a, b, axes=None): Examples -------- >>> import dpnp as np - >>> a = np.eye(2*3*4) - >>> a.shape = (2*3, 4, 2, 3, 4) + >>> a = np.eye(2*3*4).reshape((2*3, 4, 2, 3, 4)) >>> b = np.random.randn(2*3, 4) >>> x = np.linalg.tensorsolve(a, b) >>> x.shape (2, 3, 4) >>> np.allclose(np.tensordot(a, x, axes=3), b) - array([ True]) + array(True) """ diff --git a/dpnp/tensor/_indexing_functions.py b/dpnp/tensor/_indexing_functions.py index 675e770e411d..48849ff7bbd1 100644 --- a/dpnp/tensor/_indexing_functions.py +++ b/dpnp/tensor/_indexing_functions.py @@ -54,7 +54,7 @@ def _get_indexing_mode(name): def _range(sh_i, i, nd, q, usm_t, dt): ind = dpt.arange(sh_i, dtype=dt, usm_type=usm_t, sycl_queue=q) - ind.shape = tuple(sh_i if i == j else 1 for j in range(nd)) + ind = dpt.reshape(ind, tuple(sh_i if i == j else 1 for j in range(nd))) return ind diff --git a/dpnp/tensor/_usmarray.pyx b/dpnp/tensor/_usmarray.pyx index c050703eb773..bb03c5831927 100644 --- a/dpnp/tensor/_usmarray.pyx +++ b/dpnp/tensor/_usmarray.pyx @@ -30,6 +30,8 @@ # cython: language_level=3 # cython: linetrace=True +import warnings + import dpctl import dpctl.memory as dpmem import numpy as np @@ -691,10 +693,10 @@ cdef class usm_ndarray: Elements of the shape tuple give the lengths of the respective array dimensions. - Setting shape is allowed only when reshaping to the requested - dimensions can be returned as view, otherwise :exc:`AttributeError` - is raised. Use :func:`dpctl.tensor.reshape` to reshape the array - in all cases. + .. warning:: + Setting ``a.shape`` has been deprecated and may be removed in + the future. Use :func:`dpnp.tensor.reshape` to reshape the array + instead. :Example: @@ -703,7 +705,7 @@ cdef class usm_ndarray: from dpnp import tensor x = tensor.arange(899) - x.shape = (29, 31) + x = tensor.reshape(x, (29, 31)) """ if self.nd_ > 0: return _make_int_tuple(self.nd_, self.shape_) @@ -724,10 +726,23 @@ cdef class usm_ndarray: number of elements in the array. Whether the array can be reshape in-place depends on its - strides. Use :func:`dpctl.tensor.reshape` function which + strides. Use :func:`dpnp.tensor.reshape` function which always succeeds to reshape the array by performing a copy if necessary. - """ + + .. deprecated:: + Setting ``a.shape`` has been deprecated and may be removed in + the future. Use :func:`dpnp.tensor.reshape` to reshape the array + instead. + """ + warnings.warn( + "Setting the shape on an array has been deprecated. As an " + "alternative, you can create a new array with the desired shape " + "using the reshape function.", + DeprecationWarning, + stacklevel=2, + ) + cdef int new_nd = -1 cdef Py_ssize_t nelems = -1 cdef int err = 0 diff --git a/dpnp/tests/tensor/test_usm_ndarray_ctor.py b/dpnp/tests/tensor/test_usm_ndarray_ctor.py index c98acabca929..8494ca9181d1 100644 --- a/dpnp/tests/tensor/test_usm_ndarray_ctor.py +++ b/dpnp/tests/tensor/test_usm_ndarray_ctor.py @@ -169,7 +169,7 @@ def test_usm_ndarray_writable_flag_views(): a = dpt.arange(10, dtype="f4") a.flags["W"] = False - a.shape = (5, 2) + a = dpt.reshape(a, (5, 2)) assert not a.flags.writable assert not a.T.flags.writable assert not a.mT.flags.writable @@ -803,6 +803,7 @@ def test_setitem_wingaps(): assert np.array_equal(dpt.asnumpy(dpt_dst), np_src) +@pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_shape_setter(): def cc_strides(sh): return np.empty(sh, dtype="u1").strides diff --git a/dpnp/tests/test_arraycreation.py b/dpnp/tests/test_arraycreation.py index d6e726726362..f98abf138d7b 100644 --- a/dpnp/tests/test_arraycreation.py +++ b/dpnp/tests/test_arraycreation.py @@ -999,12 +999,17 @@ def test_meshgrid(arrays, dtype, indexing): assert isinstance(result, tuple) +@testing.with_requires("numpy>=2.5") @pytest.mark.parametrize("shape", [(24,), (4, 6), (2, 3, 4), (2, 3, 2, 2)]) def test_set_shape(shape): + deprecation_msg = "Setting the shape on .* has been deprecated" na = numpy.arange(24) - na.shape = shape + with pytest.warns(DeprecationWarning, match=deprecation_msg): + na.shape = shape + da = dpnp.arange(24) - da.shape = shape + with pytest.warns(DeprecationWarning, match=deprecation_msg): + da.shape = shape assert_array_equal(na, da) diff --git a/dpnp/tests/test_linalg.py b/dpnp/tests/test_linalg.py index 302a722bd1b1..1132d0b5f302 100644 --- a/dpnp/tests/test_linalg.py +++ b/dpnp/tests/test_linalg.py @@ -3740,7 +3740,7 @@ def test_solve_broadcast(self, a_shape, b_shape, dtype): @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True)) def test_solve_nrhs_greater_n(self, dtype): # Test checking the case when nrhs > n for - # for a.shape = (n x n) and b.shape = (n x nrhs). + # for a.shape == (n x n) and b.shape == (n x nrhs). a_np = numpy.array([[1, 2], [3, 5]], dtype=dtype) b_np = numpy.array([[1, 1, 1], [2, 2, 2]], dtype=dtype) diff --git a/dpnp/tests/test_ndarray.py b/dpnp/tests/test_ndarray.py index 5a848c9660fc..9ffb285cdb2b 100644 --- a/dpnp/tests/test_ndarray.py +++ b/dpnp/tests/test_ndarray.py @@ -1,3 +1,5 @@ +import warnings + import numpy import pytest from numpy.testing import ( @@ -56,11 +58,16 @@ def test_attributes(self): assert_equal(self.two.shape, (4, 5)) assert_equal(self.three.shape, (2, 5, 6)) - self.three.shape = (10, 3, 2) + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + self.three.shape = (10, 3, 2) assert_equal(self.three.shape, (10, 3, 2)) - self.three.shape = (2, 5, 6) + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=DeprecationWarning) + self.three.shape = (2, 5, 6) assert_equal(self.one.strides, (self.one.itemsize,)) + num = self.two.itemsize assert_equal(self.two.strides, (5 * num, num)) num = self.three.itemsize @@ -377,7 +384,7 @@ def test_flags_writable(): a = dpnp.arange(10, dtype="f4") a.flags["W"] = False - a.shape = (5, 2) + a = a.reshape((5, 2)) assert not a.flags.writable assert not a.T.flags.writable assert not a.real.flags.writable diff --git a/dpnp/tests/third_party/cupy/core_tests/test_ndarray.py b/dpnp/tests/third_party/cupy/core_tests/test_ndarray.py index cbb8a5c818d8..e17d09e2eddc 100644 --- a/dpnp/tests/third_party/cupy/core_tests/test_ndarray.py +++ b/dpnp/tests/third_party/cupy/core_tests/test_ndarray.py @@ -280,10 +280,12 @@ def test_copy_multi_device_with_stream(self): class TestNdarrayShape(unittest.TestCase): + @testing.with_requires("numpy>=2.5") @testing.numpy_cupy_array_equal() def test_shape_set(self, xp): arr = xp.ndarray((2, 3)) - arr.shape = (3, 2) + with testing.assert_warns(DeprecationWarning): + arr.shape = (3, 2) return xp.array(arr.shape) @pytest.mark.skip( @@ -296,12 +298,15 @@ def test_shape_set_infer(self, xp): arr.shape = (3, -1) return xp.array(arr.shape) + @testing.with_requires("numpy>=2.5") @testing.numpy_cupy_array_equal() def test_shape_set_int(self, xp): arr = xp.ndarray((2, 3)) - arr.shape = 6 + with testing.assert_warns(DeprecationWarning): + arr.shape = 6 return xp.array(arr.shape) + @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_shape_need_copy(self): # from cupy/cupy#5470 for xp in (numpy, cupy):