diff --git a/CHANGELOG.md b/CHANGELOG.md index 070e3428a04..adaf9ebe61d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ This release is compatible with NumPy 2.5. * Replaced `.pxi` includes in `dpnp.tensor` with modular `.pxd`/`.pyx` Cython imports [#2913](https://github.com/IntelPython/dpnp/pull/2913) * Reimplemented `dpnp.eye` and `dpnp.tensor.eye` with a branchless kernel [#2937](https://github.com/IntelPython/dpnp/pull/2937) * Cleaned up Python bindings for indexing functions, renaming `usm_ndarray_take` and `usm_ndarray_put` to `py_take` and `py_put` and refactoring validation [#2935](https://github.com/IntelPython/dpnp/pull/2935) +* Updated `dpnp.linalg.eig` and `dpnp.linalg.eigvals` documentation to reflect NumPy's always-complex eigenvalue output for general matrices [#2953](https://github.com/IntelPython/dpnp/pull/2953) ### Deprecated diff --git a/dpnp/linalg/dpnp_iface_linalg.py b/dpnp/linalg/dpnp_iface_linalg.py index 9923971577a..ca46a91b270 100644 --- a/dpnp/linalg/dpnp_iface_linalg.py +++ b/dpnp/linalg/dpnp_iface_linalg.py @@ -472,10 +472,10 @@ def eig(a): eigenvalues : (..., M) dpnp.ndarray The eigenvalues, each repeated according to its multiplicity. - The eigenvalues are not necessarily ordered. The resulting array will - be of complex type, unless the imaginary part is zero in which case it - will be cast to a real type. When `a` is real the resulting eigenvalues - will be real (zero imaginary part) or occur in conjugate pairs. + The eigenvalues are not necessarily ordered. The resulting array is + always of complex type, even when `a` is real-valued. In that case the + eigenvalues either have a zero imaginary part or occur in conjugate + pairs. eigenvectors : (..., M, M) dpnp.ndarray The normalized (unit "length") eigenvectors, such that the column ``eigenvectors[:,i]`` is the eigenvector corresponding to the @@ -503,8 +503,8 @@ def eig(a): (Almost) trivial example with real eigenvalues and eigenvectors. >>> w, v = LA.eig(np.diag((1, 2, 3))) - >>> w, v - (array([1., 2., 3.]), + >>> w, v.real + (array([1.+0.j, 2.+0.j, 3.+0.j]), array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])) @@ -533,8 +533,8 @@ def eig(a): >>> a = np.array([[1 + 1e-9, 0], [0, 1 - 1e-9]]) >>> # Theor. eigenvalues are 1 +/- 1e-9 >>> w, v = LA.eig(a) - >>> w, v - (array([1., 1.]), + >>> w, v.real + (array([1.+0.j, 1.+0.j]), array([[1., 0.], [0., 1.]])) @@ -681,11 +681,11 @@ def eigvals(a): >>> D = np.diag((-1, 1)) >>> LA.eigvals(D) - array([-1., 1.]) + array([-1.+0.j, 1.+0.j]) >>> A = np.dot(Q, D) >>> A = np.dot(A, Q.T) >>> LA.eigvals(A) - array([-1., 1.]) # random + array([-1.+0.j, 1.+0.j]) # random """ diff --git a/dpnp/tests/test_linalg.py b/dpnp/tests/test_linalg.py index be8987ab066..723b1dcedc4 100644 --- a/dpnp/tests/test_linalg.py +++ b/dpnp/tests/test_linalg.py @@ -464,6 +464,8 @@ def test_det_errors(self): class TestEigenvalue: + ALL_DTYPES_NO_BOOL = get_all_dtypes(no_none=True, no_bool=True) + # Eigenvalue decomposition of a matrix or a batch of matrices # by checking if the eigen equation A*v=w*v holds for given eigenvalues(w) # and eigenvectors(v). @@ -487,7 +489,7 @@ def assert_eigen_decomposition(self, a, w, v, rtol=1e-5, atol=1e-5): [(2, 2), (2, 3, 3), (2, 2, 3, 3)], ids=["(2, 2)", "(2, 3, 3)", "(2, 2, 3, 3)"], ) - @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True)) + @pytest.mark.parametrize("dtype", ALL_DTYPES_NO_BOOL) @pytest.mark.parametrize("order", ["C", "F"]) def test_eigenvalues(self, func, shape, dtype, order): # Set a `hermitian` flag for generate_random_numpy_array() to @@ -524,7 +526,7 @@ def test_eigenvalues(self, func, shape, dtype, order): [(0, 0), (2, 0, 0), (0, 3, 3)], ids=["(0, 0)", "(2, 0, 0)", "(0, 3, 3)"], ) - @pytest.mark.parametrize("dtype", get_all_dtypes(no_bool=True)) + @pytest.mark.parametrize("dtype", ALL_DTYPES_NO_BOOL) def test_eigenvalue_empty(self, func, shape, dtype): a_np = numpy.empty(shape, dtype=dtype) a_dp = dpnp.array(a_np)