# This file is a part of Julia. License is MIT: https://julialang.org/license using .ARPACK ## eigs """ eigs(A; nev=6, ncv=max(20,2*nev+1), which=:LM, tol=0.0, maxiter=300, sigma=nothing, ritzvec=true, v0=zeros((0,))) -> (d,[v,],nconv,niter,nmult,resid) Computes eigenvalues `d` of `A` using implicitly restarted Lanczos or Arnoldi iterations for real symmetric or general nonsymmetric matrices respectively. The following keyword arguments are supported: * `nev`: Number of eigenvalues * `ncv`: Number of Krylov vectors used in the computation; should satisfy `nev+1 <= ncv <= n` for real symmetric problems and `nev+2 <= ncv <= n` for other problems, where `n` is the size of the input matrix `A`. The default is `ncv = max(20,2*nev+1)`. Note that these restrictions limit the input matrix `A` to be of dimension at least 2. * `which`: type of eigenvalues to compute. See the note below. | `which` | type of eigenvalues | |:--------|:--------------------------------------------------------------------------------------------------------------------------| | `:LM` | eigenvalues of largest magnitude (default) | | `:SM` | eigenvalues of smallest magnitude | | `:LR` | eigenvalues of largest real part | | `:SR` | eigenvalues of smallest real part | | `:LI` | eigenvalues of largest imaginary part (nonsymmetric or complex `A` only) | | `:SI` | eigenvalues of smallest imaginary part (nonsymmetric or complex `A` only) | | `:BE` | compute half of the eigenvalues from each end of the spectrum, biased in favor of the high end. (real symmetric `A` only) | * `tol`: parameter defining the relative tolerance for convergence of Ritz values (eigenvalue estimates). A Ritz value ``θ`` is considered converged when its associated residual is less than or equal to the product of `tol` and ``max(ɛ^{2/3}, |θ|)``, where `ɛ = eps(real(eltype(A)))/2` is LAPACK's machine epsilon. The residual associated with ``θ`` and its corresponding Ritz vector ``v`` is defined as the norm ``||Av - vθ||``. The specified value of `tol` should be positive; otherwise, it is ignored and ``ɛ`` is used instead. Default: ``ɛ``. * `maxiter`: Maximum number of iterations (default = 300) * `sigma`: Specifies the level shift used in inverse iteration. If `nothing` (default), defaults to ordinary (forward) iterations. Otherwise, find eigenvalues close to `sigma` using shift and invert iterations. * `ritzvec`: Returns the Ritz vectors `v` (eigenvectors) if `true` * `v0`: starting vector from which to start the iterations `eigs` returns the `nev` requested eigenvalues in `d`, the corresponding Ritz vectors `v` (only if `ritzvec=true`), the number of converged eigenvalues `nconv`, the number of iterations `niter` and the number of matrix vector multiplications `nmult`, as well as the final residual vector `resid`. # Example ```jldoctest julia> A = spdiagm(1:4); julia> λ, ϕ = eigs(A, nev = 2); julia> λ 2-element Array{Float64,1}: 4.0 3.0 ``` !!! note The `sigma` and `which` keywords interact: the description of eigenvalues searched for by `which` do *not* necessarily refer to the eigenvalues of `A`, but rather the linear operator constructed by the specification of the iteration mode implied by `sigma`. | `sigma` | iteration mode | `which` refers to eigenvalues of | |:----------------|:---------------------------------|:---------------------------------| | `nothing` | ordinary (forward) | ``A`` | | real or complex | inverse with level shift `sigma` | ``(A - \\sigma I )^{-1}`` | !!! note Although `tol` has a default value, the best choice depends strongly on the matrix `A`. We recommend that users _always_ specify a value for `tol` which suits their specific needs. For details of how the errors in the computed eigenvalues are estimated, see: * B. N. Parlett, "The Symmetric Eigenvalue Problem", SIAM: Philadelphia, 2/e (1998), Ch. 13.2, "Accessing Accuracy in Lanczos Problems", pp. 290-292 ff. * R. B. Lehoucq and D. C. Sorensen, "Deflation Techniques for an Implicitly Restarted Arnoldi Iteration", SIAM Journal on Matrix Analysis and Applications (1996), 17(4), 789–821. doi:10.1137/S0895479895281484 """ eigs(A; kwargs...) = eigs(A, I; kwargs...) eigs(A::AbstractMatrix{<:BlasFloat}, ::UniformScaling; kwargs...) = _eigs(A, I; kwargs...) eigs(A::AbstractMatrix{T}, B::AbstractMatrix{T}; kwargs...) where {T<:BlasFloat} = _eigs(A, B; kwargs...) eigs(A::AbstractMatrix{BigFloat}, B::AbstractMatrix...; kwargs...) = throw(MethodError(eigs, Any[A,B,kwargs...])) eigs(A::AbstractMatrix{BigFloat}, B::UniformScaling; kwargs...) = throw(MethodError(eigs, Any[A,B,kwargs...])) function eigs(A::AbstractMatrix{T}, ::UniformScaling; kwargs...) where T Tnew = typeof(zero(T)/sqrt(one(T))) eigs(convert(AbstractMatrix{Tnew}, A), I; kwargs...) end function eigs(A::AbstractMatrix, B::AbstractMatrix; kwargs...) T = promote_type(eltype(A), eltype(B)) Tnew = typeof(zero(T)/sqrt(one(T))) eigs(convert(AbstractMatrix{Tnew}, A), convert(AbstractMatrix{Tnew}, B); kwargs...) end """ eigs(A, B; nev=6, ncv=max(20,2*nev+1), which=:LM, tol=0.0, maxiter=300, sigma=nothing, ritzvec=true, v0=zeros((0,))) -> (d,[v,],nconv,niter,nmult,resid) Computes generalized eigenvalues `d` of `A` and `B` using implicitly restarted Lanczos or Arnoldi iterations for real symmetric or general nonsymmetric matrices respectively. The following keyword arguments are supported: * `nev`: Number of eigenvalues * `ncv`: Number of Krylov vectors used in the computation; should satisfy `nev+1 <= ncv <= n` for real symmetric problems and `nev+2 <= ncv <= n` for other problems, where `n` is the size of the input matrices `A` and `B`. The default is `ncv = max(20,2*nev+1)`. Note that these restrictions limit the input matrix `A` to be of dimension at least 2. * `which`: type of eigenvalues to compute. See the note below. | `which` | type of eigenvalues | |:--------|:--------------------------------------------------------------------------------------------------------------------------| | `:LM` | eigenvalues of largest magnitude (default) | | `:SM` | eigenvalues of smallest magnitude | | `:LR` | eigenvalues of largest real part | | `:SR` | eigenvalues of smallest real part | | `:LI` | eigenvalues of largest imaginary part (nonsymmetric or complex `A` only) | | `:SI` | eigenvalues of smallest imaginary part (nonsymmetric or complex `A` only) | | `:BE` | compute half of the eigenvalues from each end of the spectrum, biased in favor of the high end. (real symmetric `A` only) | * `tol`: relative tolerance used in the convergence criterion for eigenvalues, similar to `tol` in the [`eigs(A)`](@ref) method for the ordinary eigenvalue problem, but effectively for the eigenvalues of ``B^{-1} A`` instead of ``A``. See the documentation for the ordinary eigenvalue problem in [`eigs(A)`](@ref) and the accompanying note about `tol`. * `maxiter`: Maximum number of iterations (default = 300) * `sigma`: Specifies the level shift used in inverse iteration. If `nothing` (default), defaults to ordinary (forward) iterations. Otherwise, find eigenvalues close to `sigma` using shift and invert iterations. * `ritzvec`: Returns the Ritz vectors `v` (eigenvectors) if `true` * `v0`: starting vector from which to start the iterations `eigs` returns the `nev` requested eigenvalues in `d`, the corresponding Ritz vectors `v` (only if `ritzvec=true`), the number of converged eigenvalues `nconv`, the number of iterations `niter` and the number of matrix vector multiplications `nmult`, as well as the final residual vector `resid`. # Example ```jldoctest julia> A = speye(4, 4); B = spdiagm(1:4); julia> λ, ϕ = eigs(A, B, nev = 2); julia> λ 2-element Array{Float64,1}: 1.0 0.5 ``` !!! note The `sigma` and `which` keywords interact: the description of eigenvalues searched for by `which` do *not* necessarily refer to the eigenvalue problem ``Av = Bv\\lambda``, but rather the linear operator constructed by the specification of the iteration mode implied by `sigma`. | `sigma` | iteration mode | `which` refers to the problem | |:----------------|:---------------------------------|:-----------------------------------| | `nothing` | ordinary (forward) | ``Av = Bv\\lambda`` | | real or complex | inverse with level shift `sigma` | ``(A - \\sigma B )^{-1}B = v\\nu`` | """ eigs(A, B; kwargs...) = _eigs(A, B; kwargs...) function _eigs(A, B; nev::Integer=6, ncv::Integer=max(20,2*nev+1), which=:LM, tol=0.0, maxiter::Integer=300, sigma=nothing, v0::Vector=zeros(eltype(A),(0,)), ritzvec::Bool=true) n = checksquare(A) T = eltype(A) iscmplx = T <: Complex isgeneral = B !== I sym = issymmetric(A) && issymmetric(B) && !iscmplx nevmax=sym ? n-1 : n-2 if nevmax <= 0 throw(ArgumentError("input matrix A is too small. Use eigfact instead.")) end if nev > nevmax warn("Adjusting nev from $nev to $nevmax") nev = nevmax end if nev <= 0 throw(ArgumentError("requested number of eigenvalues (nev) must be ≥ 1, got $nev")) end ncvmin = nev + (sym ? 1 : 2) if ncv < ncvmin warn("Adjusting ncv from $ncv to $ncvmin") ncv = ncvmin end ncv = BlasInt(min(ncv, n)) bmat = isgeneral ? "G" : "I" isshift = sigma !== nothing if isa(which,AbstractString) warn("Use symbols instead of strings for specifying which eigenvalues to compute") which=Symbol(which) end if (which != :LM && which != :SM && which != :LR && which != :SR && which != :LI && which != :SI && which != :BE) throw(ArgumentError("which must be :LM, :SM, :LR, :SR, :LI, :SI, or :BE, got $(repr(which))")) end if which == :BE && !sym throw(ArgumentError("which=:BE only possible for real symmetric problem")) end isshift && which == :SM && warn("use of :SM in shift-and-invert mode is not recommended, use :LM to find eigenvalues closest to sigma") if which==:SM && !isshift # transform into shift-and-invert method with sigma = 0 isshift=true sigma=zero(T) which=:LM end if sigma !== nothing && !iscmplx && isa(sigma,Complex) throw(ArgumentError("complex shifts for real problems are not yet supported")) end sigma = isshift ? convert(T,sigma) : zero(T) if !isempty(v0) if length(v0) != n throw(DimensionMismatch()) end if eltype(v0) != T throw(ArgumentError("starting vector must have element type $T, got $(eltype(v0))")) end end whichstr = "LM" if which == :BE whichstr = "BE" end if which == :LR whichstr = (!sym ? "LR" : "LA") end if which == :SR whichstr = (!sym ? "SR" : "SA") end if which == :LI if !sym whichstr = "LI" else throw(ArgumentError("largest imaginary is meaningless for symmetric eigenvalue problems")) end end if which == :SI if !sym whichstr = "SI" else throw(ArgumentError("smallest imaginary is meaningless for symmetric eigenvalue problems")) end end # Refer to ex-*.doc files in ARPACK/DOCUMENTS for calling sequence matvecA!(y, x) = A_mul_B!(y, A, x) if !isgeneral # Standard problem matvecB = x -> x if !isshift # Regular mode mode = 1 solveSI = x->x else # Shift-invert mode mode = 3 F = factorize(A - UniformScaling(sigma)) solveSI = x -> F \ x end else # Generalized eigenproblem matvecB = x -> B * x if !isshift # Regular inverse mode mode = 2 F = factorize(B) solveSI = x -> F \ x else # Shift-invert mode mode = 3 F = factorize(A - sigma*B) solveSI = x -> F \ x end end # Compute the Ritz values and Ritz vectors (resid, v, ldv, iparam, ipntr, workd, workl, lworkl, rwork, TOL) = ARPACK.aupd_wrapper(T, matvecA!, matvecB, solveSI, n, sym, iscmplx, bmat, nev, ncv, whichstr, tol, maxiter, mode, v0) # Postprocessing to get eigenvalues and eigenvectors output = ARPACK.eupd_wrapper(T, n, sym, iscmplx, bmat, nev, whichstr, ritzvec, TOL, resid, ncv, v, ldv, sigma, iparam, ipntr, workd, workl, lworkl, rwork) # Issue 10495, 10701: Check that all eigenvalues are converged nev = length(output[1]) nconv = output[ritzvec ? 3 : 2] nev ≤ nconv || warn("not all wanted Ritz pairs converged. Requested: $nev, converged: $nconv") return output end ## svds ### Restrict operator to BlasFloat because ARPACK only supports that. Loosen restriction ### when we switch to our own implementation mutable struct SVDOperator{T<:BlasFloat,S} <: AbstractArray{T, 2} X::S m::Int n::Int SVDOperator{T,S}(X::AbstractMatrix) where {T<:BlasFloat,S} = new(X, size(X, 1), size(X, 2)) end function SVDOperator(A::AbstractMatrix{T}) where T Tnew = typeof(zero(T)/sqrt(one(T))) Anew = convert(AbstractMatrix{Tnew}, A) SVDOperator{Tnew,typeof(Anew)}(Anew) end function A_mul_B!(u::StridedVector{T}, s::SVDOperator{T}, v::StridedVector{T}) where T a, b = s.m, length(v) A_mul_B!(view(u,1:a), s.X, view(v,a+1:b)) # left singular vector Ac_mul_B!(view(u,a+1:b), s.X, view(v,1:a)) # right singular vector u end size(s::SVDOperator) = s.m + s.n, s.m + s.n issymmetric(s::SVDOperator) = true svds(A::AbstractMatrix{<:BlasFloat}; kwargs...) = _svds(A; kwargs...) svds(A::AbstractMatrix{BigFloat}; kwargs...) = throw(MethodError(svds, Any[A, kwargs...])) function svds(A::AbstractMatrix{T}; kwargs...) where T Tnew = typeof(zero(T)/sqrt(one(T))) svds(convert(AbstractMatrix{Tnew}, A); kwargs...) end """ svds(A; nsv=6, ritzvec=true, tol=0.0, maxiter=1000, ncv=2*nsv, u0=zeros((0,)), v0=zeros((0,))) -> (SVD([left_sv,] s, [right_sv,]), nconv, niter, nmult, resid) Computes the largest singular values `s` of `A` using implicitly restarted Lanczos iterations derived from [`eigs`](@ref). **Inputs** * `A`: Linear operator whose singular values are desired. `A` may be represented as a subtype of `AbstractArray`, e.g., a sparse matrix, or any other type supporting the four methods `size(A)`, `eltype(A)`, `A * vector`, and `A' * vector`. * `nsv`: Number of singular values. Default: 6. * `ritzvec`: If `true`, return the left and right singular vectors `left_sv` and `right_sv`. If `false`, omit the singular vectors. Default: `true`. * `tol`: tolerance, see [`eigs`](@ref). * `maxiter`: Maximum number of iterations, see [`eigs`](@ref). Default: 1000. * `ncv`: Maximum size of the Krylov subspace, see [`eigs`](@ref) (there called `nev`). Default: `2*nsv`. * `u0`: Initial guess for the first left Krylov vector. It may have length `m` (the first dimension of `A`), or 0. * `v0`: Initial guess for the first right Krylov vector. It may have length `n` (the second dimension of `A`), or 0. **Outputs** * `svd`: An `SVD` object containing the left singular vectors, the requested values, and the right singular vectors. If `ritzvec = false`, the left and right singular vectors will be empty. * `nconv`: Number of converged singular values. * `niter`: Number of iterations. * `nmult`: Number of matrix--vector products used. * `resid`: Final residual vector. # Example ```jldoctest julia> A = spdiagm(1:4); julia> s = svds(A, nsv = 2)[1]; julia> s[:S] 2-element Array{Float64,1}: 4.0 3.0 ``` !!! note "Implementation" `svds(A)` is formally equivalent to calling [`eigs`](@ref) to perform implicitly restarted Lanczos tridiagonalization on the Hermitian matrix ``\\begin{pmatrix} 0 & A^\\prime \\\\ A & 0 \\end{pmatrix}``, whose eigenvalues are plus and minus the singular values of ``A``. """ svds(A; kwargs...) = _svds(A; kwargs...) function _svds(X; nsv::Int = 6, ritzvec::Bool = true, tol::Float64 = 0.0, maxiter::Int = 1000, ncv::Int = 2*nsv, u0::Vector=zeros(eltype(X),(0,)), v0::Vector=zeros(eltype(X),(0,))) if nsv < 1 throw(ArgumentError("number of singular values (nsv) must be ≥ 1, got $nsv")) end if nsv > minimum(size(X)) throw(ArgumentError("number of singular values (nsv) must be ≤ $(minimum(size(X))), got $nsv")) end m,n = size(X) otype = eltype(X) padv0 = zeros(eltype(X),(0,)) if length(v0) ∉ [0,n] throw(DimensionMismatch("length of v0, the guess for the starting right Krylov vector, must be 0, or $n, got $(length(v0))")) end if length(u0) ∉ [0,m] throw(DimensionMismatch("length of u0, the guess for the starting left Krylov vector, must be 0, or $m, got $(length(u0))")) end if length(v0) == n && length(u0) == m padv0 = [u0; v0] elseif length(v0) == n && length(u0) == 0 padv0 = [zeros(otype,m); v0] elseif length(v0) == 0 && length(u0) == m padv0 = [u0; zeros(otype,n) ] end ex = eigs(SVDOperator(X), I; ritzvec = ritzvec, nev = ncv, tol = tol, maxiter = maxiter, v0=padv0) ind = [1:2:ncv;] sval = abs.(ex[1][ind]) if ritzvec # calculating singular vectors left_sv = sqrt(2) * ex[2][ 1:size(X,1), ind ] .* sign.(ex[1][ind]') right_sv = sqrt(2) * ex[2][ size(X,1)+1:end, ind ] return (SVD(left_sv, sval, right_sv'), ex[3], ex[4], ex[5], ex[6]) else #The sort is necessary to work around #10329 return (SVD(zeros(eltype(sval), n, 0), sort!(sval, by=real, rev=true), zeros(eltype(sval), 0, m)), ex[2], ex[3], ex[4], ex[5]) end end