Getting started
Installing
In Julia open the package manager in the REPL via ] and run:
(v1.0) pkg> add ArnoldiMethodThen use the package.
using ArnoldiMethodConstruct a partial Schur decomposition
ArnoldiMethod.jl exports the partialschur function which can be used to obtain a partial Schur decomposition of any matrix A.
ArnoldiMethod.partialschur — Function.partialschur(A; nev, which, tol, mindim, maxdim, restarts) → PartialSchur, HistoryFind nev approximate eigenpairs of A with eigenvalues near a specified target.
The matrix A can be any linear map that implements mul!(y, A, x), eltype and size.
The method will run iteratively until the eigenpairs are approximated to the prescribed tolerance or until restarts restarts have passed.
Arguments
The most important keyword arguments:
| Keyword | Type | Default | Description |
|---|---|---|---|
nev | Int | min(6, size(A, 1)) | Number of eigenvalues |
which | Target | LM() | One of LM(), LR(), SR(), LI(), SI(), see below. |
tol | Real | √eps | Tolerance for convergence: ‖Ax - xλ‖₂ < tol * ‖λ‖ |
The target which can be any of subtypes(ArnoldiMethod.Target):
| Target | Description |
|---|---|
LM() | Largest magnitude: abs(λ) is largest |
LR() | Largest real part: real(λ) is largest |
SR() | Smallest real part: real(λ) is smallest |
LI() | Largest imaginary part: imag(λ) is largest |
SI() | Smallest imaginary part: imag(λ) is smallest |
The targets LI() and SI() only make sense in complex arithmetic. In real arithmetic λ is an eigenvalue iff conj(λ) is an eigenvalue and this conjugate pair converges simultaneously.
Return values
The function returns a tuple
decomp, history = partialschur(A, ...)where decomp is a PartialSchur struct which forms a partial Schur decomposition of A to a prescribed tolerance:
> norm(A * decomp.Q - decomp.Q * decomp.R)history is a History struct that holds some basic information about convergence of the method:
> history.converged
true
> @show history
Converged after 359 matrix-vector productsAdvanced usage
Further there are advanced keyword arguments for tuning the algorithm:
| Keyword | Type | Default | Description |
|---|---|---|---|
mindim | Int | min(max(10, nev), size(A,1)) | Minimum Krylov dimension (≥ nev) |
maxdim | Int | min(max(20, 2nev), size(A,1)) | Maximum Krylov dimension (≥ min) |
restarts | Int | 200 | Maximum number of restarts |
When the algorithm does not converge, one can increase restarts. When the algorithm converges too slowly, one can play with mindim and maxdim. It is suggested to keep mindim equal to or slightly larger than nev, and maxdim is usually about two times mindim.
From a Schur decomposition to an eigendecomposition
The eigenvalues and eigenvectors are obtained from the Schur form with the partialeigen function that is exported by ArnoldiMethod.jl:
ArnoldiMethod.partialeigen — Function.partialeigen(P::PartialSchur) → (Vector{<:Union{Real,Complex}}, Matrix{<:Union{Real,Complex}})Transforms a partial Schur decomposition into an eigendecomposition.
For real-symmetric and Hermitian matrices the Schur vectors coincide with the eigenvectors, and hence it is not necessary to call this function in that case.
The method still relies on LAPACK to compute the eigenvectors of the (quasi) upper triangular matrix R from the partial Schur decomposition.
This method is currently type unstable for real matrices, since we have not yet decided how to deal with complex conjugate pairs of eigenvalues. E.g. if almost all eigenvalues are real, but there are just a few conjugate pairs, should all eigenvectors be complex-valued?
Example
Here we compute the first ten eigenvalues and eigenvectors of a tridiagonal sparse matrix.
julia> using ArnoldiMethod, LinearAlgebra, SparseArrays
julia> A = spdiagm(
-1 => fill(-1.0, 99),
0 => fill(2.0, 100),
1 => fill(-1.0, 99)
);
julia> decomp, history = partialschur(A, nev=10, tol=1e-6, which=SR());
julia> decomp
PartialSchur decomposition (Float64) of dimension 10
eigenvalues:
10-element Array{Complex{Float64},1}:
0.0009674354160236865 + 0.0im
0.003868805732811139 + 0.0im
0.008701304061962657 + 0.0im
0.01546025527344699 + 0.0im
0.024139120518486677 + 0.0im
0.0347295035554728 + 0.0im
0.04722115887278571 + 0.0im
0.06160200160067088 + 0.0im
0.0778581192025522 + 0.0im
0.09597378493453936 + 0.0im
julia> history
Converged: 10 of 10 eigenvalues in 174 matrix-vector products
julia> norm(A * decomp.Q - decomp.Q * decomp.R)
6.39386920955869e-8
julia> λs, X = partialeigen(decomp);
julia> norm(A * X - X * Diagonal(λs))
6.393869211477937e-8The PartialSchur and History structs
For completeness, the return values of the partialschur function:
ArnoldiMethod.PartialSchur — Type.PartialSchur(Q, R, eigenvalues)Holds an orthonormal basis Q and a (quasi) upper triangular matrix R.
For convenience the eigenvalues that appear on the diagonal of R are also listed as eigenvalues, which is in particular useful in the case of real matrices with complex eigenvalues. Note that the eigenvalues are always a complex, even when the matrix R is real.
ArnoldiMethod.History — Type.History(mvproducts, nconverged, converged, nev)History shows whether the method has converged (when nconverged ≥ nev) and how many matrix-vector products were necessary to do so.