diff --git a/doc/reference/linalg.rst b/doc/reference/linalg.rst index ddde45cb41de..0bb48a04cd9b 100644 --- a/doc/reference/linalg.rst +++ b/doc/reference/linalg.rst @@ -30,8 +30,10 @@ Decompositions :nosignatures: dpnp.linalg.cholesky + dpnp.linalg.outer dpnp.linalg.qr dpnp.linalg.svd + dpnp.linalg.svdvals Matrix eigenvalues ------------------ diff --git a/dpnp/linalg/dpnp_iface_linalg.py b/dpnp/linalg/dpnp_iface_linalg.py index 69cf31303140..712e8c5ba666 100644 --- a/dpnp/linalg/dpnp_iface_linalg.py +++ b/dpnp/linalg/dpnp_iface_linalg.py @@ -83,6 +83,7 @@ "qr", "solve", "svd", + "svdvals", "slogdet", "tensorinv", "tensorsolve", @@ -1315,6 +1316,62 @@ def svd(a, full_matrices=True, compute_uv=True, hermitian=False): return dpnp_svd(a, full_matrices, compute_uv, hermitian) +def svdvals(x, /): + """ + Returns the singular values of a matrix (or a stack of matrices) `x`. + + When `x` is a stack of matrices, the function will compute + the singular values for each matrix in the stack. + + Calling ``dpnp.linalg.svdvals(x)`` to get singular values is the same as + ``dpnp.linalg.svd(x, compute_uv=False, hermitian=False)``. + + For full documentation refer to :obj:`numpy.linalg.svdvals`. + + Parameters + ---------- + x : (..., M, N) {dpnp.ndarray, usm_ndarray} + Input array with ``x.ndim >= 2`` and whose last two dimensions + form matrices on which to perform singular value decomposition. + + Returns + ------- + out : (..., K) dpnp.ndarray + Vector(s) of singular values of length K, where K = min(M, N). + + + See Also + -------- + :obj:`dpnp.linalg.svd` : Compute the singular value decomposition. + + + Examples + -------- + >>> import dpnp as np + >>> a = np.array([[3, 0], [0, 4]]) + >>> np.linalg.svdvals(a) + array([4., 3.]) + + This is equivalent to calling: + + >>> np.linalg.svd(a, compute_uv=False, hermitian=False) + array([4., 3.]) + + Stack of matrices: + + >>> b = np.array([[[6, 0], [0, 8]], [[9, 0], [0, 12]]]) + >>> np.linalg.svdvals(b) + array([[ 8., 6.], + [12., 9.]]) + + """ + + dpnp.check_supported_arrays_type(x) + assert_stacked_2d(x) + + return dpnp_svd(x, full_matrices=True, compute_uv=False, hermitian=False) + + def slogdet(a): """ Compute the sign and (natural) logarithm of the determinant of an array. diff --git a/tests/test_linalg.py b/tests/test_linalg.py index 5951f103b170..d10f610e51c7 100644 --- a/tests/test_linalg.py +++ b/tests/test_linalg.py @@ -2993,6 +2993,50 @@ def test_svd_errors(self): assert_raises(inp.linalg.LinAlgError, inp.linalg.svd, a_dp_ndim_1) +# numpy.linalg.svdvals() is available since numpy >= 2.0 +@testing.with_requires("numpy>=2.0") +class TestSvdvals: + @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True)) + @pytest.mark.parametrize( + "shape", + [(3, 5), (4, 2), (2, 3, 3), (3, 5, 2)], + ids=["(3,5)", "(4,2)", "(2,3,3)", "(3,5,2)"], + ) + def test_svdvals(self, dtype, shape): + a = numpy.arange(numpy.prod(shape), dtype=dtype).reshape(shape) + dp_a = inp.array(a) + + expected = numpy.linalg.svdvals(a) + result = inp.linalg.svdvals(dp_a) + + assert_dtype_allclose(result, expected) + + @pytest.mark.parametrize( + "shape", + [(0, 0), (1, 0, 0), (0, 2, 2)], + ids=["(0,0)", "(1,0,0)", "(0,2,2)"], + ) + def test_svdvals_empty(self, shape): + a = generate_random_numpy_array(shape, inp.default_float_type()) + dp_a = inp.array(a) + + expected = numpy.linalg.svdvals(a) + result = inp.linalg.svdvals(dp_a) + + assert_dtype_allclose(result, expected) + + def test_svdvals_errors(self): + a_dp = inp.array([[1, 2], [3, 4]], dtype="float32") + + # unsupported type + a_np = inp.asnumpy(a_dp) + assert_raises(TypeError, inp.linalg.svdvals, a_np) + + # a.ndim < 2 + a_dp_ndim_1 = a_dp.flatten() + assert_raises(inp.linalg.LinAlgError, inp.linalg.svdvals, a_dp_ndim_1) + + class TestPinv: def get_tol(self, dtype): tol = 1e-06