diff --git a/CHANGELOG.md b/CHANGELOG.md index 34a0610bd185..2cd6b3d69289 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ Also, that release drops support for Python 3.9, making Python 3.10 the minimum * `dpnp.asfarray` is deprecated. Use `dpnp.asarray` with an appropriate dtype instead [#2650](https://github.com/IntelPython/dpnp/pull/2650) * Passing the output array ``out`` positionally to `dpnp.minimum` and `dpnp.maximum` is deprecated. Pass the output with the keyword form, e.g. ``dpnp.minimum(a, b, out=c)`` [#2659](https://github.com/IntelPython/dpnp/pull/2659) +* `dpnp.ndarray.T` property is deprecated for not two-dimensional array to be compatible with the Python array API standard. To achieve a similar behavior when ``a.ndim != 2``, either ``a.transpose()``, or ``a.mT`` (swaps the last two axes only), or ``dpnp.permute_dims(a, range(a.ndim)[::-1])`` can be used [#2681](https://github.com/IntelPython/dpnp/pull/2681) ### Removed diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index 656f099a0c4a..3148e7757f6c 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -35,6 +35,8 @@ # pylint: disable=invalid-name # pylint: disable=protected-access +import warnings + import dpctl.tensor as dpt import dpctl.tensor._type_utils as dtu from dpctl.tensor._numpy_helper import AxisError @@ -1925,7 +1927,8 @@ def T(self): """ View of the transposed array. - Same as ``self.transpose()``. + Same as ``self.transpose()`` except that it requires + the array to be 2-dimensional. See Also -------- @@ -1942,14 +1945,17 @@ def T(self): array([[1, 3], [2, 4]]) - >>> a = np.array([1, 2, 3, 4]) - >>> a - array([1, 2, 3, 4]) - >>> a.T - array([1, 2, 3, 4]) - """ + if self.ndim != 2: + warnings.warn( + "`.T` is deprecated for non-2D dpnp.ndarray " + "and will raise an error in a future release. " + "Either `self.transpose()` or `self.mT` (which swaps " + "the last two axes only) should be used instead.", + DeprecationWarning, + stacklevel=2, + ) return self.transpose() def take(self, indices, axis=None, *, out=None, mode="wrap"): diff --git a/dpnp/linalg/dpnp_utils_linalg.py b/dpnp/linalg/dpnp_utils_linalg.py index ff388675c183..f2e5d99f6e05 100644 --- a/dpnp/linalg/dpnp_utils_linalg.py +++ b/dpnp/linalg/dpnp_utils_linalg.py @@ -2210,14 +2210,14 @@ def dpnp_lstsq(a, b, rcond=None): # Solve the least-squares solution # x = vh.T.conj() @ diag(s1) @ u.T.conj() @ b - z = (dpnp.dot(b.T, u.conj()) * s1).T - x = dpnp.dot(vh.T.conj(), z) + z = (dpnp.dot(b.transpose(), u.conj()) * s1).transpose() + x = dpnp.dot(vh.transpose().conj(), z) # Calculate squared Euclidean 2-norm for each column in b - a*x if m <= n or rank != n: resids = dpnp.empty_like(s, shape=(0,)) else: e = b - a.dot(x) - resids = dpnp.atleast_1d(_nrm2_last_axis(e.T)) + resids = dpnp.atleast_1d(_nrm2_last_axis(e.transpose())) return x, resids, rank, s diff --git a/dpnp/tests/test_arraymanipulation.py b/dpnp/tests/test_arraymanipulation.py index 0ea0498b5be8..ba83ee94d8b0 100644 --- a/dpnp/tests/test_arraymanipulation.py +++ b/dpnp/tests/test_arraymanipulation.py @@ -503,8 +503,12 @@ def test_concatenate_3d(self, dtype): dp_res = dpnp.concatenate((dp_a0, dp_a1, dp_a2), axis=axis) assert_array_equal(dp_res, np_res) - np_res = numpy.concatenate((np_a0.T, np_a1.T, np_a2.T), axis=0) - dp_res = dpnp.concatenate((dp_a0.T, dp_a1.T, dp_a2.T), axis=0) + np_res = numpy.concatenate( + (np_a0.transpose(), np_a1.transpose(), np_a2.transpose()), axis=0 + ) + dp_res = dpnp.concatenate( + (dp_a0.transpose(), dp_a1.transpose(), dp_a2.transpose()), axis=0 + ) assert_array_equal(dp_res, np_res) @pytest.mark.parametrize( @@ -1164,3 +1168,13 @@ def test_can_cast(): assert dpnp.can_cast(X, "float32") == numpy.can_cast(X_np, "float32") assert dpnp.can_cast(X, dpnp.int32) == numpy.can_cast(X_np, numpy.int32) assert dpnp.can_cast(X, dpnp.int64) == numpy.can_cast(X_np, numpy.int64) + + +def test_depr_T_non_2d(): + x = dpnp.arange(8) + with pytest.warns(DeprecationWarning, match="deprecated"): + _ = x.T + + x_3d = x.reshape(2, 2, 2) + with pytest.warns(DeprecationWarning, match="deprecated"): + _ = x_3d.T diff --git a/dpnp/tests/test_linalg.py b/dpnp/tests/test_linalg.py index c5f6d3629515..31d99d71ce49 100644 --- a/dpnp/tests/test_linalg.py +++ b/dpnp/tests/test_linalg.py @@ -879,17 +879,25 @@ def check_einsum_sums(self, dtype, do_opt=False): b = numpy.arange(n, dtype=dtype) a_dp = dpnp.array(a) b_dp = dpnp.array(b) - expected = numpy.einsum("ji,j", a.T, b.T, optimize=do_opt) - result = dpnp.einsum("ji,j", a_dp.T, b_dp.T, optimize=do_opt) + expected = numpy.einsum( + "ji,j", a.transpose(), b.transpose(), optimize=do_opt + ) + result = dpnp.einsum( + "ji,j", a_dp.transpose(), b_dp.transpose(), optimize=do_opt + ) assert_dtype_allclose(result, expected) result = dpnp.einsum( - a_dp.T, [1, 0], b_dp.T, [1], optimize=do_opt + a_dp.transpose(), + [1, 0], + b_dp.transpose(), + [1], + optimize=do_opt, ) assert_dtype_allclose(result, expected) c = dpnp.arange(4, dtype=a_dp.dtype) - args = ["ji,j", a_dp.T, b_dp.T] + args = ["ji,j", a_dp.transpose(), b_dp.transpose()] result = dpnp.einsum( *args, out=c, dtype="f4", casting="unsafe", optimize=do_opt ) @@ -897,7 +905,7 @@ def check_einsum_sums(self, dtype, do_opt=False): assert_dtype_allclose(result, expected) c[...] = 0 - args = [a_dp.T, [1, 0], b_dp.T, [1]] + args = [a_dp.transpose(), [1, 0], b_dp.transpose(), [1]] result = dpnp.einsum( *args, out=c, dtype="f4", casting="unsafe", optimize=do_opt ) diff --git a/dpnp/tests/test_strides.py b/dpnp/tests/test_strides.py index 102a20d60c04..ac43374f10a2 100644 --- a/dpnp/tests/test_strides.py +++ b/dpnp/tests/test_strides.py @@ -316,8 +316,8 @@ def test_2args_in_out(func, dtype): b = numpy.full(sh, fill_value=0.7, dtype=dtype) ia, ib = dpnp.array(a), dpnp.array(b) - a, b = a[::2], b[::2].T - ia, ib = ia[::2], ib[::2].T + a, b = a[::2], b[::2].transpose() + ia, ib = ia[::2], ib[::2].transpose() expected = getattr(numpy, func)(a, b, out=out) result = getattr(dpnp, func)(ia, ib, out=iout) diff --git a/dpnp/tests/test_sum.py b/dpnp/tests/test_sum.py index eaee7efae8e1..e646adc15ae3 100644 --- a/dpnp/tests/test_sum.py +++ b/dpnp/tests/test_sum.py @@ -41,8 +41,8 @@ def test_sum(shape, dtype_in, dtype_out, transpose, keepdims, order): a = dpnp.asarray(a_np) if transpose: - a_np = a_np.T - a = a.T + a_np = a_np.transpose() + a = a.transpose() axes_range = list(numpy.arange(len(shape))) axes = [None] diff --git a/dpnp/tests/third_party/cupy/core_tests/test_ndarray_copy_and_view.py b/dpnp/tests/third_party/cupy/core_tests/test_ndarray_copy_and_view.py index 828794ec2d77..61980b6eda9b 100644 --- a/dpnp/tests/third_party/cupy/core_tests/test_ndarray_copy_and_view.py +++ b/dpnp/tests/third_party/cupy/core_tests/test_ndarray_copy_and_view.py @@ -66,7 +66,7 @@ def test_view_non_contiguous_raise(self, dtype): def test_view_f_contiguous(self, dtype): for xp in (numpy, cupy): a = testing.shaped_arange((2, 2, 2), xp, dtype=numpy.float32) - a = a.T + a = a.transpose() with pytest.raises(ValueError): a.view(dtype=dtype) diff --git a/dpnp/tests/third_party/cupy/core_tests/test_ndarray_indexing.py b/dpnp/tests/third_party/cupy/core_tests/test_ndarray_indexing.py index 5814b53a6d6a..9181f3f2f703 100644 --- a/dpnp/tests/third_party/cupy/core_tests/test_ndarray_indexing.py +++ b/dpnp/tests/third_party/cupy/core_tests/test_ndarray_indexing.py @@ -252,12 +252,12 @@ def test_setitem_partial_copy(self, xp, dtype): @testing.numpy_cupy_array_equal() def test_T(self, xp): a = testing.shaped_arange((2, 3, 4), xp) - return a.T + return a.transpose() @testing.numpy_cupy_array_equal() def test_T_vector(self, xp): a = testing.shaped_arange((4,), xp) - return a.T + return a.transpose() class TestSetItemCompatBroadcast: diff --git a/dpnp/tests/third_party/cupy/linalg_tests/test_product.py b/dpnp/tests/third_party/cupy/linalg_tests/test_product.py index e5d21441626e..adf0dfd63470 100644 --- a/dpnp/tests/third_party/cupy/linalg_tests/test_product.py +++ b/dpnp/tests/third_party/cupy/linalg_tests/test_product.py @@ -46,11 +46,11 @@ class TestDot(unittest.TestCase): def test_dot(self, xp, dtype_a, dtype_b): shape_a, shape_b = self.shape if self.trans_a: - a = testing.shaped_arange(shape_a[::-1], xp, dtype_a).T + a = testing.shaped_arange(shape_a[::-1], xp, dtype_a).transpose() else: a = testing.shaped_arange(shape_a, xp, dtype_a) if self.trans_b: - b = testing.shaped_arange(shape_b[::-1], xp, dtype_b).T + b = testing.shaped_arange(shape_b[::-1], xp, dtype_b).transpose() else: b = testing.shaped_arange(shape_b, xp, dtype_b) return xp.dot(a, b) @@ -62,11 +62,11 @@ def test_dot(self, xp, dtype_a, dtype_b): def test_dot_with_out(self, xp, dtype_a, dtype_b, dtype_c): shape_a, shape_b = self.shape if self.trans_a: - a = testing.shaped_arange(shape_a[::-1], xp, dtype_a).T + a = testing.shaped_arange(shape_a[::-1], xp, dtype_a).transpose() else: a = testing.shaped_arange(shape_a, xp, dtype_a) if self.trans_b: - b = testing.shaped_arange(shape_b[::-1], xp, dtype_b).T + b = testing.shaped_arange(shape_b[::-1], xp, dtype_b).transpose() else: b = testing.shaped_arange(shape_b, xp, dtype_b) if a.ndim == 0 or b.ndim == 0: @@ -206,11 +206,11 @@ class TestDotFor0Dim(unittest.TestCase): def test_dot(self, xp, dtype_a, dtype_b): shape_a, shape_b = self.shape if self.trans_a: - a = testing.shaped_arange(shape_a[::-1], xp, dtype_a).T + a = testing.shaped_arange(shape_a[::-1], xp, dtype_a).transpose() else: a = testing.shaped_arange(shape_a, xp, dtype_a) if self.trans_b: - b = testing.shaped_arange(shape_b[::-1], xp, dtype_b).T + b = testing.shaped_arange(shape_b[::-1], xp, dtype_b).transpose() else: b = testing.shaped_arange(shape_b, xp, dtype_b) return xp.dot(a, b) diff --git a/dpnp/tests/third_party/cupy/manipulation_tests/test_basic.py b/dpnp/tests/third_party/cupy/manipulation_tests/test_basic.py index 4aac9493d0b1..5b0b486d5c81 100644 --- a/dpnp/tests/third_party/cupy/manipulation_tests/test_basic.py +++ b/dpnp/tests/third_party/cupy/manipulation_tests/test_basic.py @@ -26,7 +26,7 @@ def test_copyto(self, xp, dtype): @testing.for_all_dtypes() @testing.numpy_cupy_array_equal() def test_copyto_different_contiguity(self, xp, dtype): - a = testing.shaped_arange((2, 3, 2), xp, dtype).T + a = testing.shaped_arange((2, 3, 2), xp, dtype).transpose() b = xp.empty((2, 3, 2), dtype=dtype) xp.copyto(b, a) return b diff --git a/dpnp/tests/third_party/cupy/manipulation_tests/test_join.py b/dpnp/tests/third_party/cupy/manipulation_tests/test_join.py index f6e97e571391..0695de034e0c 100644 --- a/dpnp/tests/third_party/cupy/manipulation_tests/test_join.py +++ b/dpnp/tests/third_party/cupy/manipulation_tests/test_join.py @@ -113,7 +113,7 @@ def test_concatenate_large_different_devices(self): @testing.numpy_cupy_array_equal() def test_concatenate_f_contiguous(self, xp, dtype): a = testing.shaped_arange((2, 3, 4), xp, dtype) - b = testing.shaped_arange((2, 3, 2), xp, dtype).T + b = testing.shaped_arange((2, 3, 2), xp, dtype).transpose() c = testing.shaped_arange((2, 3, 3), xp, dtype) return xp.concatenate((a, b, c), axis=-1) @@ -121,9 +121,9 @@ def test_concatenate_f_contiguous(self, xp, dtype): @testing.numpy_cupy_array_equal() def test_concatenate_large_f_contiguous(self, xp, dtype): a = testing.shaped_arange((2, 3, 4), xp, dtype) - b = testing.shaped_arange((2, 3, 2), xp, dtype).T + b = testing.shaped_arange((2, 3, 2), xp, dtype).transpose() c = testing.shaped_arange((2, 3, 3), xp, dtype) - d = testing.shaped_arange((2, 3, 2), xp, dtype).T + d = testing.shaped_arange((2, 3, 2), xp, dtype).transpose() e = testing.shaped_arange((2, 3, 2), xp, dtype) return xp.concatenate((a, b, c, d, e) * 2, axis=-1)