https://github.com/JuliaLang/julia
Tip revision: 7711781875620866da6a4f7dc545b355e5b1f24c authored by Alex Arslan on 10 July 2018, 00:08:41 UTC
Set VERSION to 0.6.5-pre (#28013)
Set VERSION to 0.6.5-pre (#28013)
Tip revision: 7711781
arnoldi.jl
# 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