From 34895577d692495a3ca358aa9b1cfb6c55e84d66 Mon Sep 17 00:00:00 2001 From: Jae-Mo Lihm Date: Wed, 5 Apr 2023 01:21:00 +0900 Subject: [PATCH 01/15] Add LAPACK cheevd and zheevd wrapper --- stdlib/LinearAlgebra/src/lapack.jl | 51 +++++++++++++++++++++++++++-- stdlib/LinearAlgebra/test/lapack.jl | 7 ++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl index c498e9b51bc19..39b7b5a19a2a1 100644 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ b/stdlib/LinearAlgebra/src/lapack.jl @@ -5376,7 +5376,7 @@ for (syev, syevr, sygvd, elty, relty) in end lda = max(1,stride(A,2)) m = Ref{BlasInt}() - w = similar(A, $relty, n) + W = similar(A, $relty, n) if jobz == 'N' ldz = 1 Z = similar(A, $elty, ldz, 0) @@ -5404,7 +5404,7 @@ for (syev, syevr, sygvd, elty, relty) in jobz, range, uplo, n, A, lda, vl, vu, il, iu, abstol, m, - w, Z, ldz, isuppz, + W, Z, ldz, isuppz, work, lwork, rwork, lrwork, iwork, liwork, info, 1, 1, 1) @@ -5418,11 +5418,56 @@ for (syev, syevr, sygvd, elty, relty) in resize!(iwork, liwork) end end - w[1:m[]], Z[:,1:(jobz == 'V' ? m[] : 0)] + W[1:m[]], Z[:,1:(jobz == 'V' ? m[] : 0)] end syevr!(jobz::AbstractChar, A::AbstractMatrix{$elty}) = syevr!(jobz, 'A', 'U', A, 0.0, 0.0, 0, 0, -1.0) + # SUBROUTINE ZHEEVD( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, RWORK, + # $ LRWORK, IWORK, LIWORK, INFO ) + # * .. Scalar Arguments .. + # CHARACTER JOBZ, UPLO + # INTEGER INFO, LDA, LIWORK, LRWORK, LWORK, N + # * .. + # * .. Array Arguments .. + # INTEGER IWORK( * ) + # DOUBLE PRECISION RWORK( * ) + # COMPLEX*16 A( LDA, * ), WORK( * ) + function syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) + chkstride1(A) + chkuplofinite(A, uplo) + n = checksquare(A) + lda = max(1, stride(A,2)) + m = Ref{BlasInt}() + W = similar(A, $relty, n) + work = Vector{$elty}(undef, 1) + lwork = BlasInt(-1) + rwork = Vector{$relty}(undef, 1) + lrwork = BlasInt(-1) + iwork = Vector{BlasInt}(undef, 1) + liwork = BlasInt(-1) + info = Ref{BlasInt}() + for i = 1:2 # first call returns lwork as work[1], lrwork as rwork[1] and liwork as iwork[1] + ccall((@blasfunc($syevd), liblapack), Cvoid, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, Ptr{$relty}, Ref{BlasInt}, + Ptr{BlasInt}, Ref{BlasInt}, Ptr{BlasInt}, Clong, Clong), + jobz, uplo, n, A, stride(A,2), + W, work, lwork, rwork, lrwork, + iwork, liwork, info, 1, 1) + chklapackerror(info[]) + if i == 1 + lwork = BlasInt(real(work[1])) + resize!(work, lwork) + lrwork = BlasInt(rwork[1]) + resize!(rwork, lrwork) + liwork = iwork[1] + resize!(iwork, liwork) + end + end + jobz == 'V' ? (W, A) : W + end + # SUBROUTINE ZHEGVD( ITYPE, JOBZ, UPLO, N, A, LDA, B, LDB, W, WORK, # $ LWORK, RWORK, LRWORK, IWORK, LIWORK, INFO ) # * .. Scalar Arguments .. diff --git a/stdlib/LinearAlgebra/test/lapack.jl b/stdlib/LinearAlgebra/test/lapack.jl index 1e9e2a2e31e65..a164de0e31815 100644 --- a/stdlib/LinearAlgebra/test/lapack.jl +++ b/stdlib/LinearAlgebra/test/lapack.jl @@ -27,6 +27,13 @@ using LinearAlgebra: BlasInt @test LAPACK.syevr!('N', 'V', 'U', copy(Asym), 0.0, 1.0, 4, 5, -1.0)[1] ≈ vals[vals .< 1.0] @test LAPACK.syevr!('N', 'I', 'U', copy(Asym), 0.0, 1.0, 4, 5, -1.0)[1] ≈ vals[4:5] @test vals ≈ LAPACK.syev!('N', 'U', copy(Asym)) + @test vals ≈ LAPACK.syevd!('N', 'U', copy(Asym)) + vals_test, Z_test = LAPACK.syev!('V', 'U', copy(Asym)) + @test vals_test ≈ vals + @test Z_test*(Diagonal(vals)*Z_test') ≈ Asym + vals_test, Z_test = LAPACK.syevd!('V', 'U', copy(Asym)) + @test vals_test ≈ vals + @test Z_test*(Diagonal(vals)*Z_test') ≈ Asym @test_throws DimensionMismatch LAPACK.sygvd!(1, 'V', 'U', copy(Asym), zeros(elty, 6, 6)) end end From b630b74c7f425ce85b2663ba10a4018801fb0316 Mon Sep 17 00:00:00 2001 From: Jae-Mo Lihm Date: Wed, 5 Apr 2023 22:01:17 +0900 Subject: [PATCH 02/15] Fix typo --- stdlib/LinearAlgebra/src/lapack.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl index 39b7b5a19a2a1..5f3ef7805a1c8 100644 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ b/stdlib/LinearAlgebra/src/lapack.jl @@ -5313,9 +5313,9 @@ for (syev, syevr, sygvd, elty) in end end # Hermitian eigensolvers -for (syev, syevr, sygvd, elty, relty) in - ((:zheev_,:zheevr_,:zhegvd_,:ComplexF64,:Float64), - (:cheev_,:cheevr_,:chegvd_,:ComplexF32,:Float32)) +for (syev, syevr, syevd, sygvd, elty, relty) in + ((:zheev_,:zheevr_,:zheevd,:zhegvd_,:ComplexF64,:Float64), + (:cheev_,:cheevr_,:cheevd,:chegvd_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE ZHEEV( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, RWORK, INFO ) # * .. Scalar Arguments .. From f309c0677c69c7333372ec80570edb6d4393c6ce Mon Sep 17 00:00:00 2001 From: Jae-Mo Lihm Date: Wed, 5 Apr 2023 22:03:35 +0900 Subject: [PATCH 03/15] Update docs --- stdlib/LinearAlgebra/docs/src/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index 305fa6fa2562d..00ce21ed6fcae 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -838,6 +838,7 @@ LinearAlgebra.LAPACK.hetri! LinearAlgebra.LAPACK.hetrs! LinearAlgebra.LAPACK.syev! LinearAlgebra.LAPACK.syevr! +LinearAlgebra.LAPACK.syevd! LinearAlgebra.LAPACK.sygvd! LinearAlgebra.LAPACK.bdsqr! LinearAlgebra.LAPACK.bdsdc! From 9cbb576dcd371b6688364568883051b81200ecd7 Mon Sep 17 00:00:00 2001 From: Jae-Mo Lihm Date: Wed, 5 Apr 2023 22:36:05 +0900 Subject: [PATCH 04/15] FIx typo --- stdlib/LinearAlgebra/src/lapack.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl index 5f3ef7805a1c8..f383ddedf5a71 100644 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ b/stdlib/LinearAlgebra/src/lapack.jl @@ -5314,8 +5314,8 @@ for (syev, syevr, sygvd, elty) in end # Hermitian eigensolvers for (syev, syevr, syevd, sygvd, elty, relty) in - ((:zheev_,:zheevr_,:zheevd,:zhegvd_,:ComplexF64,:Float64), - (:cheev_,:cheevr_,:cheevd,:chegvd_,:ComplexF32,:Float32)) + ((:zheev_,:zheevr_,:zheevd_,:zhegvd_,:ComplexF64,:Float64), + (:cheev_,:cheevr_,:cheevd_,:chegvd_,:ComplexF32,:Float32)) @eval begin # SUBROUTINE ZHEEV( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, RWORK, INFO ) # * .. Scalar Arguments .. From 5dc6dc5e59290c15938029baacb2b17c911489b2 Mon Sep 17 00:00:00 2001 From: Jae-Mo Lihm Date: Fri, 7 Apr 2023 00:52:37 +0900 Subject: [PATCH 05/15] Add docs --- stdlib/LinearAlgebra/src/lapack.jl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl index f383ddedf5a71..1705046fc65a1 100644 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ b/stdlib/LinearAlgebra/src/lapack.jl @@ -5549,6 +5549,20 @@ The eigenvalues are returned in `W` and the eigenvectors in `Z`. syevr!(jobz::AbstractChar, range::AbstractChar, uplo::AbstractChar, A::AbstractMatrix, vl::AbstractFloat, vu::AbstractFloat, il::Integer, iu::Integer, abstol::AbstractFloat) +""" + syevd!(jobz, uplo, A) + +Finds the eigenvalues (`jobz = N`) or eigenvalues and eigenvectors +(`jobz = V`) of a symmetric matrix `A`. If `uplo = U`, the upper triangle +of `A` is used. If `uplo = L`, the lower triangle of `A` is used. + +Use the divide-and-conquer method, instead of the QR iteration used by +`syev!` or multiple relatively robust representations used by `syevr!`. +See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for +a comparison of the accuracy and performatce of different methods. +""" +syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix) + """ sygvd!(itype, jobz, uplo, A, B) -> (w, A, B) From ddbd2c2165fec8f9f985adbb4b4bf586a60b02be Mon Sep 17 00:00:00 2001 From: Jae-Mo Lihm Date: Fri, 14 Apr 2023 09:37:10 +0900 Subject: [PATCH 06/15] Add interface to real symmetric eigensolver syevd --- stdlib/LinearAlgebra/src/lapack.jl | 52 ++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl index 1705046fc65a1..c5f820a68a6fc 100644 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ b/stdlib/LinearAlgebra/src/lapack.jl @@ -5170,9 +5170,9 @@ solution `X`. hetrs!(uplo::AbstractChar, A::AbstractMatrix, ipiv::AbstractVector{BlasInt}, B::AbstractVecOrMat) # Symmetric (real) eigensolvers -for (syev, syevr, sygvd, elty) in - ((:dsyev_,:dsyevr_,:dsygvd_,:Float64), - (:ssyev_,:ssyevr_,:ssygvd_,:Float32)) +for (syev, syevr, syevd, sygvd, elty) in + ((:dsyev_,:dsyevr_,:dsyevd_,:dsygvd_,:Float64), + (:ssyev_,:ssyevr_,:ssyevd_,:ssygvd_,:Float32)) @eval begin # SUBROUTINE DSYEV( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, INFO ) # * .. Scalar Arguments .. @@ -5225,7 +5225,7 @@ for (syev, syevr, sygvd, elty) in end lda = stride(A,2) m = Ref{BlasInt}() - w = similar(A, $elty, n) + W = similar(A, $elty, n) ldz = n if jobz == 'N' Z = similar(A, $elty, ldz, 0) @@ -5249,7 +5249,7 @@ for (syev, syevr, sygvd, elty) in jobz, range, uplo, n, A, max(1,lda), vl, vu, il, iu, abstol, m, - w, Z, max(1,ldz), isuppz, + W, Z, max(1,ldz), isuppz, work, lwork, iwork, liwork, info, 1, 1, 1) chklapackerror(info[]) @@ -5260,11 +5260,51 @@ for (syev, syevr, sygvd, elty) in resize!(iwork, liwork) end end - w[1:m[]], Z[:,1:(jobz == 'V' ? m[] : 0)] + W[1:m[]], Z[:,1:(jobz == 'V' ? m[] : 0)] end syevr!(jobz::AbstractChar, A::AbstractMatrix{$elty}) = syevr!(jobz, 'A', 'U', A, 0.0, 0.0, 0, 0, -1.0) + # SUBROUTINE DSYEVD( JOBZ, UPLO, N, A, LDA, W, WORK, LWORK, + # $ IWORK, LIWORK, INFO ) + # * .. Scalar Arguments .. + # CHARACTER JOBZ, UPLO + # INTEGER INFO, LDA, LIWORK, LWORK, N + # * .. + # * .. Array Arguments .. + # INTEGER IWORK( * ) + # DOUBLE PRECISION A( LDA, * ), W( * ), WORK( * ) + function syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix{$elty}) + chkstride1(A) + n = checksquare(A) + chkuplofinite(A, uplo) + lda = stride(A,2) + m = Ref{BlasInt}() + W = similar(A, $elty, n) + work = Vector{$elty}(undef, 1) + lwork = BlasInt(-1) + iwork = Vector{BlasInt}(undef, 1) + liwork = BlasInt(-1) + info = Ref{BlasInt}() + for i = 1:2 # first call returns lwork as work[1] and liwork as iwork[1] + ccall((@blasfunc($syevd), libblastrampoline), Cvoid, + (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, + Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, Ptr{BlasInt}, Ref{BlasInt}, + Ptr{BlasInt}, Clong, Clong), + jobz, uplo, n, A, max(1,lda), + W, work, lwork, iwork, liwork, + info, 1, 1) + chklapackerror(info[]) + if i == 1 + lwork = BlasInt(real(work[1])) + resize!(work, lwork) + liwork = iwork[1] + resize!(iwork, liwork) + end + end + jobz == 'V' ? (W, A) : W + end + # Generalized eigenproblem # SUBROUTINE DSYGVD( ITYPE, JOBZ, UPLO, N, A, LDA, B, LDB, W, WORK, # $ LWORK, IWORK, LIWORK, INFO ) From f195e4270575110a24ae966c5faa4d738e7b3173 Mon Sep 17 00:00:00 2001 From: Jae-Mo Lihm Date: Fri, 14 Apr 2023 15:10:59 +0900 Subject: [PATCH 07/15] Add interface to use symmetric eigen with lapack_method --- stdlib/LinearAlgebra/src/lapack.jl | 5 -- stdlib/LinearAlgebra/src/symmetriceigen.jl | 68 ++++++++++++++++++++++ stdlib/LinearAlgebra/test/symmetric.jl | 14 +++++ 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl index c5f820a68a6fc..b11e392ba678a 100644 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ b/stdlib/LinearAlgebra/src/lapack.jl @@ -5595,11 +5595,6 @@ syevr!(jobz::AbstractChar, range::AbstractChar, uplo::AbstractChar, A::AbstractM Finds the eigenvalues (`jobz = N`) or eigenvalues and eigenvectors (`jobz = V`) of a symmetric matrix `A`. If `uplo = U`, the upper triangle of `A` is used. If `uplo = L`, the lower triangle of `A` is used. - -Use the divide-and-conquer method, instead of the QR iteration used by -`syev!` or multiple relatively robust representations used by `syevr!`. -See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for -a comparison of the accuracy and performatce of different methods. """ syevd!(jobz::AbstractChar, uplo::AbstractChar, A::AbstractMatrix) diff --git a/stdlib/LinearAlgebra/src/symmetriceigen.jl b/stdlib/LinearAlgebra/src/symmetriceigen.jl index 17371b74bb343..7a6fd6edb5f02 100644 --- a/stdlib/LinearAlgebra/src/symmetriceigen.jl +++ b/stdlib/LinearAlgebra/src/symmetriceigen.jl @@ -13,6 +13,42 @@ function eigen(A::RealHermSymComplexHerm; sortby::Union{Function,Nothing}=nothin eigen!(eigencopy_oftype(A, S), sortby=sortby) end +function eigen!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, lapack_method::Symbol; sortby::Union{Function,Nothing}=nothing) + if lapack_method == :syevr + Eigen(sorteig!(LAPACK.syevr!('V', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)..., sortby)...) + elseif lapack_method == :syev + Eigen(sorteig!(LAPACK.syev!('V', A.uplo, A.data)..., sortby)...) + elseif lapack_method == :syevd + Eigen(sorteig!(LAPACK.syevd!('V', A.uplo, A.data)..., sortby)...) + else + throw(ArgumentError("Wrong lapack_method $lapack_method. Must be :syevr or :syev or :syevd.")) + end +end + +""" + eigen(A::Union{SymTridiagonal, Hermitian, Symmetric}, lapack_method::Symbol) -> Eigen + +Compute the eigenvalue decomposition of `A`, returning an [`Eigen`](@ref) factorization object `F` +which contains the eigenvalues in `F.values` and the eigenvectors in the columns of the +matrix `F.vectors`. (The `k`th eigenvector can be obtained from the slice `F.vectors[:, k]`.) + +Iterating the decomposition produces the components `F.values` and `F.vectors`. + +`lapack_method` specifies which LAPACK method to use for eigenvalue decomposition. +* `lapack_method = :syev`: Use QR iteration +* `lapack_method = :syevr`: Use multiple relatively robust representations +* `lapack_method = :syevd`: Use divide-and-conquer method +See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for +a comparison of the accuracy and performance of different methods. + +The following functions are available for `Eigen` objects: [`inv`](@ref), [`det`](@ref), and [`isposdef`](@ref). +""" +function eigen(A::RealHermSymComplexHerm, lapack_method::Symbol; sortby::Union{Function,Nothing}=nothing) + S = eigtype(eltype(A)) + eigen!(eigencopy_oftype(A, S), lapack_method; sortby) +end + + eigen!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, irange::UnitRange) = Eigen(LAPACK.syevr!('V', 'I', A.uplo, A.data, 0.0, 0.0, irange.start, irange.stop, -1.0)...) @@ -74,6 +110,38 @@ function eigvals(A::RealHermSymComplexHerm; sortby::Union{Function,Nothing}=noth eigvals!(eigencopy_oftype(A, S), sortby=sortby) end +function eigvals!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, lapack_method::Symbol; sortby::Union{Function,Nothing}=nothing) + if lapack_method == :syevr + vals = LAPACK.syevr!('N', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)[1] + elseif lapack_method == :syev + vals = LAPACK.syev!('N', A.uplo, A.data) + elseif lapack_method == :syevd + vals = LAPACK.syevd!('N', A.uplo, A.data) + else + throw(ArgumentError("Wrong lapack_method $lapack_method. Must be :syevr or :syev or :syevd.")) + end + !isnothing(sortby) && sort!(vals, by=sortby) + return vals +end + +""" + eigvals(A::Union{SymTridiagonal, Hermitian, Symmetric}, lapack_method::Symbol) -> values + +Return the eigenvalues of `A`. + +`lapack_method` specifies which LAPACK method to use for eigenvalue decomposition. +* `lapack_method = :syev`: Use QR iteration +* `lapack_method = :syevr`: Use multiple relatively robust representations +* `lapack_method = :syevd`: Use divide-and-conquer method +See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for +a comparison of the accuracy and performance of different methods. +""" +function eigvals(A::RealHermSymComplexHerm, lapack_method::Symbol; sortby::Union{Function,Nothing}=nothing) + S = eigtype(eltype(A)) + eigvals!(eigencopy_oftype(A, S), lapack_method; sortby) +end + + """ eigvals!(A::Union{SymTridiagonal, Hermitian, Symmetric}, irange::UnitRange) -> values diff --git a/stdlib/LinearAlgebra/test/symmetric.jl b/stdlib/LinearAlgebra/test/symmetric.jl index 04621c4b49e86..3979e58dc0579 100644 --- a/stdlib/LinearAlgebra/test/symmetric.jl +++ b/stdlib/LinearAlgebra/test/symmetric.jl @@ -571,6 +571,20 @@ end end end +@testset "eigendecomposition with lapack_method" begin + for T in (Float64, ComplexF64) + n = 4 + A = T <: Real ? Symmetric(randn(T, n,n)) : Hermitian(randn(T, n,n)) + d, v = eigen(A) + for lapack_method in (:syev, :syevr, :syevd) + @test eigvals(A, lapack_method) ≈ d + d2, v2 = eigen(A, lapack_method) + @test d2 ≈ d + @test A * v2 ≈ v2 * Diagonal(d2) + end + end +end + const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") isdefined(Main, :ImmutableArrays) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "ImmutableArrays.jl")) using .Main.ImmutableArrays From 0be8e13789e20fdca118e4ba1aade758b635b73a Mon Sep 17 00:00:00 2001 From: Jae-Mo Lihm Date: Fri, 14 Apr 2023 19:15:42 +0900 Subject: [PATCH 08/15] Add default to lapack_method, remove one method --- stdlib/LinearAlgebra/src/symmetriceigen.jl | 58 ++++++++-------------- 1 file changed, 21 insertions(+), 37 deletions(-) diff --git a/stdlib/LinearAlgebra/src/symmetriceigen.jl b/stdlib/LinearAlgebra/src/symmetriceigen.jl index 7a6fd6edb5f02..e104f996becfc 100644 --- a/stdlib/LinearAlgebra/src/symmetriceigen.jl +++ b/stdlib/LinearAlgebra/src/symmetriceigen.jl @@ -5,20 +5,12 @@ eigencopy_oftype(A::Hermitian, S) = Hermitian(copy_similar(A, S), sym_uplo(A.upl eigencopy_oftype(A::Symmetric, S) = Symmetric(copy_similar(A, S), sym_uplo(A.uplo)) # Eigensolvers for symmetric and Hermitian matrices -eigen!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}; sortby::Union{Function,Nothing}=nothing) = - Eigen(sorteig!(LAPACK.syevr!('V', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)..., sortby)...) - -function eigen(A::RealHermSymComplexHerm; sortby::Union{Function,Nothing}=nothing) - S = eigtype(eltype(A)) - eigen!(eigencopy_oftype(A, S), sortby=sortby) -end - -function eigen!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, lapack_method::Symbol; sortby::Union{Function,Nothing}=nothing) - if lapack_method == :syevr +function eigen!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, lapack_method::Symbol=:syevr; sortby::Union{Function,Nothing}=nothing) + if lapack_method === :syevr Eigen(sorteig!(LAPACK.syevr!('V', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)..., sortby)...) - elseif lapack_method == :syev + elseif lapack_method === :syev Eigen(sorteig!(LAPACK.syev!('V', A.uplo, A.data)..., sortby)...) - elseif lapack_method == :syevd + elseif lapack_method === :syevd Eigen(sorteig!(LAPACK.syevd!('V', A.uplo, A.data)..., sortby)...) else throw(ArgumentError("Wrong lapack_method $lapack_method. Must be :syevr or :syev or :syevd.")) @@ -26,7 +18,7 @@ function eigen!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, lapack_me end """ - eigen(A::Union{SymTridiagonal, Hermitian, Symmetric}, lapack_method::Symbol) -> Eigen + eigen(A::Union{Hermitian, Symmetric}, lapack_method::Symbol=:syevr) -> Eigen Compute the eigenvalue decomposition of `A`, returning an [`Eigen`](@ref) factorization object `F` which contains the eigenvalues in `F.values` and the eigenvectors in the columns of the @@ -34,16 +26,17 @@ matrix `F.vectors`. (The `k`th eigenvector can be obtained from the slice `F.vec Iterating the decomposition produces the components `F.values` and `F.vectors`. -`lapack_method` specifies which LAPACK method to use for eigenvalue decomposition. -* `lapack_method = :syev`: Use QR iteration -* `lapack_method = :syevr`: Use multiple relatively robust representations -* `lapack_method = :syevd`: Use divide-and-conquer method +`lapack_method` specifies which LAPACK method to use for eigenvalue decomposition: +- `lapack_method = :syevr` (default): Use multiple relatively robust representations +- `lapack_method = :syev`: Use QR iteration +- `lapack_method = :syevd`: Use divide-and-conquer method + See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for a comparison of the accuracy and performance of different methods. The following functions are available for `Eigen` objects: [`inv`](@ref), [`det`](@ref), and [`isposdef`](@ref). """ -function eigen(A::RealHermSymComplexHerm, lapack_method::Symbol; sortby::Union{Function,Nothing}=nothing) +function eigen(A::RealHermSymComplexHerm, lapack_method::Symbol=:syevr; sortby::Union{Function,Nothing}=nothing) S = eigtype(eltype(A)) eigen!(eigencopy_oftype(A, S), lapack_method; sortby) end @@ -99,23 +92,13 @@ function eigen(A::RealHermSymComplexHerm, vl::Real, vh::Real) eigen!(eigencopy_oftype(A, S), vl, vh) end -function eigvals!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}; sortby::Union{Function,Nothing}=nothing) - vals = LAPACK.syevr!('N', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)[1] - !isnothing(sortby) && sort!(vals, by=sortby) - return vals -end - -function eigvals(A::RealHermSymComplexHerm; sortby::Union{Function,Nothing}=nothing) - S = eigtype(eltype(A)) - eigvals!(eigencopy_oftype(A, S), sortby=sortby) -end -function eigvals!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, lapack_method::Symbol; sortby::Union{Function,Nothing}=nothing) - if lapack_method == :syevr +function eigvals!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, lapack_method::Symbol=:syevr; sortby::Union{Function,Nothing}=nothing) + if lapack_method === :syevr vals = LAPACK.syevr!('N', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)[1] - elseif lapack_method == :syev + elseif lapack_method === :syev vals = LAPACK.syev!('N', A.uplo, A.data) - elseif lapack_method == :syevd + elseif lapack_method === :syevd vals = LAPACK.syevd!('N', A.uplo, A.data) else throw(ArgumentError("Wrong lapack_method $lapack_method. Must be :syevr or :syev or :syevd.")) @@ -125,18 +108,19 @@ function eigvals!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, lapack_ end """ - eigvals(A::Union{SymTridiagonal, Hermitian, Symmetric}, lapack_method::Symbol) -> values + eigvals(A::Union{Hermitian, Symmetric}, lapack_method::Symbol=:syevr) -> values Return the eigenvalues of `A`. `lapack_method` specifies which LAPACK method to use for eigenvalue decomposition. -* `lapack_method = :syev`: Use QR iteration -* `lapack_method = :syevr`: Use multiple relatively robust representations -* `lapack_method = :syevd`: Use divide-and-conquer method +- `lapack_method = :syevr` (default): Use multiple relatively robust representations +- `lapack_method = :syev`: Use QR iteration +- `lapack_method = :syevd`: Use divide-and-conquer method + See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for a comparison of the accuracy and performance of different methods. """ -function eigvals(A::RealHermSymComplexHerm, lapack_method::Symbol; sortby::Union{Function,Nothing}=nothing) +function eigvals(A::RealHermSymComplexHerm, lapack_method::Symbol=:syevr; sortby::Union{Function,Nothing}=nothing) S = eigtype(eltype(A)) eigvals!(eigencopy_oftype(A, S), lapack_method; sortby) end From c66882f1053eefef2ef2ad29042a9024064ba7d7 Mon Sep 17 00:00:00 2001 From: Jae-Mo Lihm Date: Fri, 14 Apr 2023 19:17:16 +0900 Subject: [PATCH 09/15] Add doc --- stdlib/LinearAlgebra/src/symmetriceigen.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stdlib/LinearAlgebra/src/symmetriceigen.jl b/stdlib/LinearAlgebra/src/symmetriceigen.jl index e104f996becfc..bb71d68f5ad73 100644 --- a/stdlib/LinearAlgebra/src/symmetriceigen.jl +++ b/stdlib/LinearAlgebra/src/symmetriceigen.jl @@ -34,6 +34,8 @@ Iterating the decomposition produces the components `F.values` and `F.vectors`. See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for a comparison of the accuracy and performance of different methods. +The default `lapack_method` used may change in the future. + The following functions are available for `Eigen` objects: [`inv`](@ref), [`det`](@ref), and [`isposdef`](@ref). """ function eigen(A::RealHermSymComplexHerm, lapack_method::Symbol=:syevr; sortby::Union{Function,Nothing}=nothing) @@ -119,6 +121,8 @@ Return the eigenvalues of `A`. See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for a comparison of the accuracy and performance of different methods. + +The default `lapack_method` used may change in the future. """ function eigvals(A::RealHermSymComplexHerm, lapack_method::Symbol=:syevr; sortby::Union{Function,Nothing}=nothing) S = eigtype(eltype(A)) From e36b52fe9acba65ba28002ba06468d37e98f9a98 Mon Sep 17 00:00:00 2001 From: Jae-Mo Lihm Date: Thu, 4 May 2023 14:43:54 +0900 Subject: [PATCH 10/15] Use alg instead of lapack_method, add type assertion in eigvals --- stdlib/LinearAlgebra/src/LinearAlgebra.jl | 1 + stdlib/LinearAlgebra/src/symmetriceigen.jl | 71 ++++++++++++---------- stdlib/LinearAlgebra/test/symmetric.jl | 13 ++-- 3 files changed, 46 insertions(+), 39 deletions(-) diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index a29c259dae607..d27300a7d8910 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -175,6 +175,7 @@ end abstract type Algorithm end struct DivideAndConquer <: Algorithm end struct QRIteration <: Algorithm end +struct RobustRepresentations <: Algorithm end abstract type PivotingStrategy end struct NoPivot <: PivotingStrategy end diff --git a/stdlib/LinearAlgebra/src/symmetriceigen.jl b/stdlib/LinearAlgebra/src/symmetriceigen.jl index bb71d68f5ad73..e61daf7a74546 100644 --- a/stdlib/LinearAlgebra/src/symmetriceigen.jl +++ b/stdlib/LinearAlgebra/src/symmetriceigen.jl @@ -4,21 +4,23 @@ eigencopy_oftype(A::Hermitian, S) = Hermitian(copy_similar(A, S), sym_uplo(A.uplo)) eigencopy_oftype(A::Symmetric, S) = Symmetric(copy_similar(A, S), sym_uplo(A.uplo)) +default_eigen_alg(A) = DivideAndConquer() + # Eigensolvers for symmetric and Hermitian matrices -function eigen!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, lapack_method::Symbol=:syevr; sortby::Union{Function,Nothing}=nothing) - if lapack_method === :syevr - Eigen(sorteig!(LAPACK.syevr!('V', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)..., sortby)...) - elseif lapack_method === :syev - Eigen(sorteig!(LAPACK.syev!('V', A.uplo, A.data)..., sortby)...) - elseif lapack_method === :syevd - Eigen(sorteig!(LAPACK.syevd!('V', A.uplo, A.data)..., sortby)...) +function eigen!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, alg::Algorithm = default_eigen_alg(A); sortby::Union{Function,Nothing}=nothing) + if alg === DivideAndConquer() + Eigen(sorteig!(LAPACK.syevd!('V', A.uplo, A.data)..., sortby)...) + elseif alg === QRIteration() + Eigen(sorteig!(LAPACK.syev!('V', A.uplo, A.data)..., sortby)...) + elseif alg === RobustRepresentations() + Eigen(sorteig!(LAPACK.syevr!('V', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)..., sortby)...) else - throw(ArgumentError("Wrong lapack_method $lapack_method. Must be :syevr or :syev or :syevd.")) + throw(ArgumentError("Unsupported value for `alg` keyword.")) end end """ - eigen(A::Union{Hermitian, Symmetric}, lapack_method::Symbol=:syevr) -> Eigen + eigen(A::Union{Hermitian, Symmetric}, alg::Algorithm = default_eigen_alg(A)) -> Eigen Compute the eigenvalue decomposition of `A`, returning an [`Eigen`](@ref) factorization object `F` which contains the eigenvalues in `F.values` and the eigenvectors in the columns of the @@ -26,21 +28,24 @@ matrix `F.vectors`. (The `k`th eigenvector can be obtained from the slice `F.vec Iterating the decomposition produces the components `F.values` and `F.vectors`. -`lapack_method` specifies which LAPACK method to use for eigenvalue decomposition: -- `lapack_method = :syevr` (default): Use multiple relatively robust representations -- `lapack_method = :syev`: Use QR iteration -- `lapack_method = :syevd`: Use divide-and-conquer method +`alg` specifies which algorithm and LAPACK method to use for eigenvalue decomposition: +- `alg = DivideAndConquer()` (default): Calls `LAPACK.syevd`. +- `alg = QRIteration()`: Calls `LAPACK.syev`. +- `alg = RobustRepresentations()`: Multiple relatively robust representations method, Calls `LAPACK.syevr!`. See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for -a comparison of the accuracy and performance of different methods. +a comparison of the accuracy and performance of different algorithms. + +The default `alg` used may change in the future. -The default `lapack_method` used may change in the future. +!!! compat "Julia 1.10" + The `alg` keyword argument requires Julia 1.10 or later. The following functions are available for `Eigen` objects: [`inv`](@ref), [`det`](@ref), and [`isposdef`](@ref). """ -function eigen(A::RealHermSymComplexHerm, lapack_method::Symbol=:syevr; sortby::Union{Function,Nothing}=nothing) +function eigen(A::RealHermSymComplexHerm, alg::Algorithm = default_eigen_alg(A); sortby::Union{Function,Nothing}=nothing) S = eigtype(eltype(A)) - eigen!(eigencopy_oftype(A, S), lapack_method; sortby) + eigen!(eigencopy_oftype(A, S), alg; sortby) end @@ -95,38 +100,38 @@ function eigen(A::RealHermSymComplexHerm, vl::Real, vh::Real) end -function eigvals!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, lapack_method::Symbol=:syevr; sortby::Union{Function,Nothing}=nothing) - if lapack_method === :syevr - vals = LAPACK.syevr!('N', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)[1] - elseif lapack_method === :syev - vals = LAPACK.syev!('N', A.uplo, A.data) - elseif lapack_method === :syevd - vals = LAPACK.syevd!('N', A.uplo, A.data) +function eigvals!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, alg::Algorithm = default_eigen_alg(A); sortby::Union{Function,Nothing}=nothing) + vals::Vector{real(eltype(A))} = if alg === DivideAndConquer() + LAPACK.syevd!('N', A.uplo, A.data) + elseif alg === QRIteration() + LAPACK.syev!('N', A.uplo, A.data) + elseif alg === RobustRepresentations() + LAPACK.syevr!('N', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)[1] else - throw(ArgumentError("Wrong lapack_method $lapack_method. Must be :syevr or :syev or :syevd.")) + throw(ArgumentError("Unsupported value for `alg` keyword.")) end !isnothing(sortby) && sort!(vals, by=sortby) return vals end """ - eigvals(A::Union{Hermitian, Symmetric}, lapack_method::Symbol=:syevr) -> values + eigvals(A::Union{Hermitian, Symmetric}, alg::Algorithm = default_eigen_alg(A))) -> values Return the eigenvalues of `A`. -`lapack_method` specifies which LAPACK method to use for eigenvalue decomposition. -- `lapack_method = :syevr` (default): Use multiple relatively robust representations -- `lapack_method = :syev`: Use QR iteration -- `lapack_method = :syevd`: Use divide-and-conquer method +`alg` specifies which algorithm and LAPACK method to use for eigenvalue decomposition: +- `alg = DivideAndConquer()` (default): Calls `LAPACK.syevd`. +- `alg = QRIteration()`: Calls `LAPACK.syev`. +- `alg = RobustRepresentations()`: Multiple relatively robust representations method, Calls `LAPACK.syevr!`. See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for a comparison of the accuracy and performance of different methods. -The default `lapack_method` used may change in the future. +The default `alg` used may change in the future. """ -function eigvals(A::RealHermSymComplexHerm, lapack_method::Symbol=:syevr; sortby::Union{Function,Nothing}=nothing) +function eigvals(A::RealHermSymComplexHerm, alg::Algorithm = default_eigen_alg(A); sortby::Union{Function,Nothing}=nothing) S = eigtype(eltype(A)) - eigvals!(eigencopy_oftype(A, S), lapack_method; sortby) + eigvals!(eigencopy_oftype(A, S), alg; sortby) end diff --git a/stdlib/LinearAlgebra/test/symmetric.jl b/stdlib/LinearAlgebra/test/symmetric.jl index 3979e58dc0579..278e0533007ab 100644 --- a/stdlib/LinearAlgebra/test/symmetric.jl +++ b/stdlib/LinearAlgebra/test/symmetric.jl @@ -571,14 +571,15 @@ end end end -@testset "eigendecomposition with lapack_method" begin - for T in (Float64, ComplexF64) +@testset "eigendecomposition with alg" begin + using LinearAlgebra: DivideAndConquer, QRIteration, RobustRepresentations + for T in (Float64, ComplexF64, Float32, ComplexF32) n = 4 - A = T <: Real ? Symmetric(randn(T, n,n)) : Hermitian(randn(T, n,n)) + A = T <: Real ? Symmetric(randn(T, n, n)) : Hermitian(randn(T, n, n)) d, v = eigen(A) - for lapack_method in (:syev, :syevr, :syevd) - @test eigvals(A, lapack_method) ≈ d - d2, v2 = eigen(A, lapack_method) + for alg in (DivideAndConquer(), QRIteration(), RobustRepresentations()) + @test (@inferred eigvals(A, alg)) ≈ d + d2, v2 = @inferred eigen(A, alg) @test d2 ≈ d @test A * v2 ≈ v2 * Diagonal(d2) end From a69c9c69357adca423d519342d9cc92cc36b2e2c Mon Sep 17 00:00:00 2001 From: Jae-Mo Lihm Date: Thu, 4 May 2023 14:53:55 +0900 Subject: [PATCH 11/15] Misc change in testset name --- stdlib/LinearAlgebra/test/symmetric.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/LinearAlgebra/test/symmetric.jl b/stdlib/LinearAlgebra/test/symmetric.jl index 278e0533007ab..3e9af28bc07f7 100644 --- a/stdlib/LinearAlgebra/test/symmetric.jl +++ b/stdlib/LinearAlgebra/test/symmetric.jl @@ -571,7 +571,7 @@ end end end -@testset "eigendecomposition with alg" begin +@testset "eigendecomposition Algorithms" begin using LinearAlgebra: DivideAndConquer, QRIteration, RobustRepresentations for T in (Float64, ComplexF64, Float32, ComplexF32) n = 4 From ebc5b967a209f0b0648f752bf96e0c266ed7a8f8 Mon Sep 17 00:00:00 2001 From: Jae-Mo Lihm Date: Thu, 4 May 2023 15:11:01 +0900 Subject: [PATCH 12/15] Fix typo in docstring --- stdlib/LinearAlgebra/src/symmetriceigen.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/LinearAlgebra/src/symmetriceigen.jl b/stdlib/LinearAlgebra/src/symmetriceigen.jl index e61daf7a74546..13fe8dcc15755 100644 --- a/stdlib/LinearAlgebra/src/symmetriceigen.jl +++ b/stdlib/LinearAlgebra/src/symmetriceigen.jl @@ -29,8 +29,8 @@ matrix `F.vectors`. (The `k`th eigenvector can be obtained from the slice `F.vec Iterating the decomposition produces the components `F.values` and `F.vectors`. `alg` specifies which algorithm and LAPACK method to use for eigenvalue decomposition: -- `alg = DivideAndConquer()` (default): Calls `LAPACK.syevd`. -- `alg = QRIteration()`: Calls `LAPACK.syev`. +- `alg = DivideAndConquer()` (default): Calls `LAPACK.syevd!`. +- `alg = QRIteration()`: Calls `LAPACK.syev!`. - `alg = RobustRepresentations()`: Multiple relatively robust representations method, Calls `LAPACK.syevr!`. See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for @@ -120,8 +120,8 @@ end Return the eigenvalues of `A`. `alg` specifies which algorithm and LAPACK method to use for eigenvalue decomposition: -- `alg = DivideAndConquer()` (default): Calls `LAPACK.syevd`. -- `alg = QRIteration()`: Calls `LAPACK.syev`. +- `alg = DivideAndConquer()` (default): Calls `LAPACK.syevd!`. +- `alg = QRIteration()`: Calls `LAPACK.syev!`. - `alg = RobustRepresentations()`: Multiple relatively robust representations method, Calls `LAPACK.syevr!`. See James W. Demmel et al, SIAM J. Sci. Comput. 30, 3, 1508 (2008) for From 623c06d2a3fbb8181bc4545c4b696076b7e93210 Mon Sep 17 00:00:00 2001 From: Jae-Mo Lihm Date: Fri, 12 May 2023 13:54:20 +0900 Subject: [PATCH 13/15] Update svd docstring --- stdlib/LinearAlgebra/src/svd.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/svd.jl b/stdlib/LinearAlgebra/src/svd.jl index c1b886f616f02..6ce2291527d94 100644 --- a/stdlib/LinearAlgebra/src/svd.jl +++ b/stdlib/LinearAlgebra/src/svd.jl @@ -149,8 +149,9 @@ and `V` is ``N \\times N``, while in the thin factorization `U` is ``M \\times K`` and `V` is ``N \\times K``, where ``K = \\min(M,N)`` is the number of singular values. -If `alg = DivideAndConquer()` a divide-and-conquer algorithm is used to calculate the SVD. -Another (typically slower but more accurate) option is `alg = QRIteration()`. +`alg` specifies which algorithm and LAPACK method to use for SVD: +- `alg = DivideAndConquer()` (default): Calls `LAPACK.gesdd!`. +- `alg = QRIteration()`: Calls `LAPACK.gesvd!` (typically slower but more accurate) . !!! compat "Julia 1.3" The `alg` keyword argument requires Julia 1.3 or later. From 015a4ae60b43baf3b122c2bf82c4c5b04fa84fbe Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Sat, 6 Jan 2024 19:22:01 +0100 Subject: [PATCH 14/15] Update stdlib/LinearAlgebra/src/symmetriceigen.jl --- stdlib/LinearAlgebra/src/symmetriceigen.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/LinearAlgebra/src/symmetriceigen.jl b/stdlib/LinearAlgebra/src/symmetriceigen.jl index 30d72a69ef737..b4eccec80521e 100644 --- a/stdlib/LinearAlgebra/src/symmetriceigen.jl +++ b/stdlib/LinearAlgebra/src/symmetriceigen.jl @@ -38,8 +38,8 @@ a comparison of the accuracy and performance of different algorithms. The default `alg` used may change in the future. -!!! compat "Julia 1.10" - The `alg` keyword argument requires Julia 1.10 or later. +!!! compat "Julia 1.11" + The `alg` keyword argument requires Julia 1.11 or later. The following functions are available for `Eigen` objects: [`inv`](@ref), [`det`](@ref), and [`isposdef`](@ref). """ From e2d3b7cf7f2d7705861324db10650e8faea601ec Mon Sep 17 00:00:00 2001 From: Daniel Karrasch Date: Wed, 22 May 2024 12:40:19 +0200 Subject: [PATCH 15/15] add NEWS entry, adjust version compat --- NEWS.md | 3 +++ stdlib/LinearAlgebra/src/symmetriceigen.jl | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 6a3ad9246d1a1..b5c304714d878 100644 --- a/NEWS.md +++ b/NEWS.md @@ -88,6 +88,9 @@ Standard library changes #### LinearAlgebra * `rank` can now take a `QRPivoted` matrix to allow rank estimation via QR factorization ([#54283]). +* Added keyword argument `alg` to `eigen`, `eigen!`, `eigvals` and `eigvals!` for self-adjoint + matrix types (i.e., the type union `RealHermSymComplexHerm`) that allows one to switch + between different eigendecomposition algorithms ([#49355]). #### Logging diff --git a/stdlib/LinearAlgebra/src/symmetriceigen.jl b/stdlib/LinearAlgebra/src/symmetriceigen.jl index 17ffc9b3b67a0..666b9a9bc81df 100644 --- a/stdlib/LinearAlgebra/src/symmetriceigen.jl +++ b/stdlib/LinearAlgebra/src/symmetriceigen.jl @@ -46,8 +46,8 @@ a comparison of the accuracy and performance of different algorithms. The default `alg` used may change in the future. -!!! compat "Julia 1.11" - The `alg` keyword argument requires Julia 1.11 or later. +!!! compat "Julia 1.12" + The `alg` keyword argument requires Julia 1.12 or later. The following functions are available for `Eigen` objects: [`inv`](@ref), [`det`](@ref), and [`isposdef`](@ref). """