mollusk 0e4acfb8f2 fix incorrect folder name for julia-0.6.x
Former-commit-id: ef2c7401e0876f22d2f7762d182cfbcd5a7d9c70
2018-06-11 03:28:36 -07:00

447 lines
14 KiB
Julia
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# This file is a part of Julia. License is MIT: https://julialang.org/license
# Eigendecomposition
struct Eigen{T,V,S<:AbstractMatrix,U<:AbstractVector} <: Factorization{T}
values::U
vectors::S
Eigen{T,V,S,U}(values::AbstractVector{V}, vectors::AbstractMatrix{T}) where {T,V,S,U} =
new(values, vectors)
end
Eigen(values::AbstractVector{V}, vectors::AbstractMatrix{T}) where {T,V} =
Eigen{T,V,typeof(vectors),typeof(values)}(values, vectors)
# Generalized eigenvalue problem.
struct GeneralizedEigen{T,V,S<:AbstractMatrix,U<:AbstractVector} <: Factorization{T}
values::U
vectors::S
GeneralizedEigen{T,V,S,U}(values::AbstractVector{V}, vectors::AbstractMatrix{T}) where {T,V,S,U} =
new(values, vectors)
end
GeneralizedEigen(values::AbstractVector{V}, vectors::AbstractMatrix{T}) where {T,V} =
GeneralizedEigen{T,V,typeof(vectors),typeof(values)}(values, vectors)
function getindex(A::Union{Eigen,GeneralizedEigen}, d::Symbol)
d == :values && return A.values
d == :vectors && return A.vectors
throw(KeyError(d))
end
isposdef(A::Union{Eigen,GeneralizedEigen}) = isreal(A.values) && all(x -> x > 0, A.values)
"""
eigfact!(A, [B])
Same as [`eigfact`](@ref), but saves space by overwriting the input `A` (and
`B`), instead of creating a copy.
"""
function eigfact!(A::StridedMatrix{T}; permute::Bool=true, scale::Bool=true) where T<:BlasReal
n = size(A, 2)
n == 0 && return Eigen(zeros(T, 0), zeros(T, 0, 0))
issymmetric(A) && return eigfact!(Symmetric(A))
A, WR, WI, VL, VR, _ = LAPACK.geevx!(permute ? (scale ? 'B' : 'P') : (scale ? 'S' : 'N'), 'N', 'V', 'N', A)
iszero(WI) && return Eigen(WR, VR)
evec = zeros(Complex{T}, n, n)
j = 1
while j <= n
if WI[j] == 0
evec[:,j] = view(VR, :, j)
else
for i = 1:n
evec[i,j] = VR[i,j] + im*VR[i,j+1]
evec[i,j+1] = VR[i,j] - im*VR[i,j+1]
end
j += 1
end
j += 1
end
return Eigen(complex.(WR, WI), evec)
end
function eigfact!(A::StridedMatrix{T}; permute::Bool=true, scale::Bool=true) where T<:BlasComplex
n = size(A, 2)
n == 0 && return Eigen(zeros(T, 0), zeros(T, 0, 0))
ishermitian(A) && return eigfact!(Hermitian(A))
return Eigen(LAPACK.geevx!(permute ? (scale ? 'B' : 'P') : (scale ? 'S' : 'N'), 'N', 'V', 'N', A)[[2,4]]...)
end
"""
eigfact(A; permute::Bool=true, scale::Bool=true) -> Eigen
Computes the eigenvalue decomposition of `A`, returning an `Eigen` 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]`.)
The following functions are available for `Eigen` objects: [`inv`](@ref), [`det`](@ref), and [`isposdef`](@ref).
For general nonsymmetric matrices it is possible to specify how the matrix is balanced
before the eigenvector calculation. The option `permute=true` permutes the matrix to become
closer to upper triangular, and `scale=true` scales the matrix by its diagonal elements to
make rows and columns more equal in norm. The default is `true` for both options.
# Example
```jldoctest
julia> F = eigfact([1.0 0.0 0.0; 0.0 3.0 0.0; 0.0 0.0 18.0])
Base.LinAlg.Eigen{Float64,Float64,Array{Float64,2},Array{Float64,1}}([1.0, 3.0, 18.0], [1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0])
julia> F[:values]
3-element Array{Float64,1}:
1.0
3.0
18.0
julia> F[:vectors]
3×3 Array{Float64,2}:
1.0 0.0 0.0
0.0 1.0 0.0
0.0 0.0 1.0
```
"""
function eigfact(A::StridedMatrix{T}; permute::Bool=true, scale::Bool=true) where T
S = promote_type(Float32, typeof(one(T)/norm(one(T))))
eigfact!(copy_oftype(A, S), permute = permute, scale = scale)
end
eigfact(x::Number) = Eigen([x], fill(one(x), 1, 1))
function eig(A::Union{Number, StridedMatrix}; permute::Bool=true, scale::Bool=true)
F = eigfact(A, permute=permute, scale=scale)
F.values, F.vectors
end
"""
eig(A::Union{SymTridiagonal, Hermitian, Symmetric}, irange::UnitRange) -> D, V
eig(A::Union{SymTridiagonal, Hermitian, Symmetric}, vl::Real, vu::Real) -> D, V
eig(A, permute::Bool=true, scale::Bool=true) -> D, V
Computes eigenvalues (`D`) and eigenvectors (`V`) of `A`.
See [`eigfact`](@ref) for details on the
`irange`, `vl`, and `vu` arguments
(for [`SymTridiagonal`](@ref), `Hermitian`, and
`Symmetric` matrices)
and the `permute` and `scale` keyword arguments.
The eigenvectors are returned columnwise.
# Example
```jldoctest
julia> eig([1.0 0.0 0.0; 0.0 3.0 0.0; 0.0 0.0 18.0])
([1.0, 3.0, 18.0], [1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0])
```
`eig` is a wrapper around [`eigfact`](@ref), extracting all parts of the
factorization to a tuple; where possible, using [`eigfact`](@ref) is recommended.
"""
function eig(A::AbstractMatrix, args...)
F = eigfact(A, args...)
F.values, F.vectors
end
"""
eigvecs(A; permute::Bool=true, scale::Bool=true) -> Matrix
Returns a matrix `M` whose columns are the eigenvectors of `A`. (The `k`th eigenvector can
be obtained from the slice `M[:, k]`.) The `permute` and `scale` keywords are the same as
for [`eigfact`](@ref).
# Example
```jldoctest
julia> eigvecs([1.0 0.0 0.0; 0.0 3.0 0.0; 0.0 0.0 18.0])
3×3 Array{Float64,2}:
1.0 0.0 0.0
0.0 1.0 0.0
0.0 0.0 1.0
```
"""
eigvecs(A::Union{Number, AbstractMatrix}; permute::Bool=true, scale::Bool=true) =
eigvecs(eigfact(A, permute=permute, scale=scale))
eigvecs(F::Union{Eigen{T,V,S,U}, GeneralizedEigen{T,V,S,U}}) where {T,V,S,U} = F[:vectors]::S
eigvals(F::Union{Eigen{T,V,S,U}, GeneralizedEigen{T,V,S,U}}) where {T,V,S,U} = F[:values]::U
"""
eigvals!(A; permute::Bool=true, scale::Bool=true) -> values
Same as [`eigvals`](@ref), but saves space by overwriting the input `A`, instead of creating a copy.
The option `permute=true` permutes the matrix to become
closer to upper triangular, and `scale=true` scales the matrix by its diagonal elements to
make rows and columns more equal in norm.
"""
function eigvals!(A::StridedMatrix{<:BlasReal}; permute::Bool=true, scale::Bool=true)
issymmetric(A) && return eigvals!(Symmetric(A))
_, valsre, valsim, _ = LAPACK.geevx!(permute ? (scale ? 'B' : 'P') : (scale ? 'S' : 'N'), 'N', 'N', 'N', A)
return iszero(valsim) ? valsre : complex.(valsre, valsim)
end
function eigvals!(A::StridedMatrix{<:BlasComplex}; permute::Bool=true, scale::Bool=true)
ishermitian(A) && return eigvals(Hermitian(A))
return LAPACK.geevx!(permute ? (scale ? 'B' : 'P') : (scale ? 'S' : 'N'), 'N', 'N', 'N', A)[2]
end
"""
eigvals(A; permute::Bool=true, scale::Bool=true) -> values
Returns the eigenvalues of `A`.
For general non-symmetric matrices it is possible to specify how the matrix is balanced
before the eigenvalue calculation. The option `permute=true` permutes the matrix to
become closer to upper triangular, and `scale=true` scales the matrix by its diagonal
elements to make rows and columns more equal in norm. The default is `true` for both
options.
"""
function eigvals(A::StridedMatrix{T}; permute::Bool=true, scale::Bool=true) where T
S = promote_type(Float32, typeof(one(T)/norm(one(T))))
return eigvals!(copy_oftype(A, S), permute = permute, scale = scale)
end
function eigvals(x::T; kwargs...) where T<:Number
val = convert(promote_type(Float32, typeof(one(T)/norm(one(T)))), x)
return imag(val) == 0 ? [real(val)] : [val]
end
"""
eigmax(A; permute::Bool=true, scale::Bool=true)
Returns the largest eigenvalue of `A`.
The option `permute=true` permutes the matrix to become
closer to upper triangular, and `scale=true` scales the matrix by its diagonal elements to
make rows and columns more equal in norm.
Note that if the eigenvalues of `A` are complex,
this method will fail, since complex numbers cannot
be sorted.
# Example
```jldoctest
julia> A = [0 im; -im 0]
2×2 Array{Complex{Int64},2}:
0+0im 0+1im
0-1im 0+0im
julia> eigmax(A)
1.0
julia> A = [0 im; -1 0]
2×2 Array{Complex{Int64},2}:
0+0im 0+1im
-1+0im 0+0im
julia> eigmax(A)
ERROR: DomainError:
Stacktrace:
[1] #eigmax#46(::Bool, ::Bool, ::Function, ::Array{Complex{Int64},2}) at ./linalg/eigen.jl:238
[2] eigmax(::Array{Complex{Int64},2}) at ./linalg/eigen.jl:236
```
"""
function eigmax(A::Union{Number, StridedMatrix}; permute::Bool=true, scale::Bool=true)
v = eigvals(A, permute = permute, scale = scale)
if eltype(v)<:Complex
throw(DomainError())
end
maximum(v)
end
"""
eigmin(A; permute::Bool=true, scale::Bool=true)
Returns the smallest eigenvalue of `A`.
The option `permute=true` permutes the matrix to become
closer to upper triangular, and `scale=true` scales the matrix by its diagonal elements to
make rows and columns more equal in norm.
Note that if the eigenvalues of `A` are complex,
this method will fail, since complex numbers cannot
be sorted.
# Example
```jldoctest
julia> A = [0 im; -im 0]
2×2 Array{Complex{Int64},2}:
0+0im 0+1im
0-1im 0+0im
julia> eigmin(A)
-1.0
julia> A = [0 im; -1 0]
2×2 Array{Complex{Int64},2}:
0+0im 0+1im
-1+0im 0+0im
julia> eigmin(A)
ERROR: DomainError:
Stacktrace:
[1] #eigmin#47(::Bool, ::Bool, ::Function, ::Array{Complex{Int64},2}) at ./linalg/eigen.jl:280
[2] eigmin(::Array{Complex{Int64},2}) at ./linalg/eigen.jl:278
```
"""
function eigmin(A::Union{Number, StridedMatrix}; permute::Bool=true, scale::Bool=true)
v = eigvals(A, permute = permute, scale = scale)
if eltype(v)<:Complex
throw(DomainError())
end
minimum(v)
end
inv(A::Eigen) = A.vectors * inv(Diagonal(A.values)) / A.vectors
det(A::Eigen) = prod(A.values)
# Generalized eigenproblem
function eigfact!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasReal
issymmetric(A) && isposdef(B) && return eigfact!(Symmetric(A), Symmetric(B))
n = size(A, 1)
alphar, alphai, beta, _, vr = LAPACK.ggev!('N', 'V', A, B)
iszero(alphai) && return GeneralizedEigen(alphar ./ beta, vr)
vecs = zeros(Complex{T}, n, n)
j = 1
while j <= n
if alphai[j] == 0
vecs[:,j] = view(vr, :, j)
else
for i = 1:n
vecs[i,j ] = vr[i,j] + im*vr[i,j+1]
vecs[i,j+1] = vr[i,j] - im*vr[i,j+1]
end
j += 1
end
j += 1
end
return GeneralizedEigen(complex.(alphar, alphai)./beta, vecs)
end
function eigfact!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasComplex
ishermitian(A) && isposdef(B) && return eigfact!(Hermitian(A), Hermitian(B))
alpha, beta, _, vr = LAPACK.ggev!('N', 'V', A, B)
return GeneralizedEigen(alpha./beta, vr)
end
"""
eigfact(A, B) -> GeneralizedEigen
Computes the generalized eigenvalue decomposition of `A` and `B`, returning a
`GeneralizedEigen` factorization object `F` which contains the generalized eigenvalues in
`F[:values]` and the generalized eigenvectors in the columns of the matrix `F[:vectors]`.
(The `k`th generalized eigenvector can be obtained from the slice `F[:vectors][:, k]`.)
"""
function eigfact(A::AbstractMatrix{TA}, B::AbstractMatrix{TB}) where {TA,TB}
S = promote_type(Float32, typeof(one(TA)/norm(one(TA))),TB)
return eigfact!(copy_oftype(A, S), copy_oftype(B, S))
end
eigfact(A::Number, B::Number) = eigfact(fill(A,1,1), fill(B,1,1))
"""
eig(A, B) -> D, V
Computes generalized eigenvalues (`D`) and vectors (`V`) of `A` with respect to `B`.
`eig` is a wrapper around [`eigfact`](@ref), extracting all parts of the
factorization to a tuple; where possible, using [`eigfact`](@ref) is recommended.
# Example
```jldoctest
julia> A = [1 0; 0 -1]
2×2 Array{Int64,2}:
1 0
0 -1
julia> B = [0 1; 1 0]
2×2 Array{Int64,2}:
0 1
1 0
julia> eig(A, B)
(Complex{Float64}[0.0+1.0im, 0.0-1.0im], Complex{Float64}[0.0-1.0im 0.0+1.0im; -1.0-0.0im -1.0+0.0im])
```
"""
function eig(A::AbstractMatrix, B::AbstractMatrix)
F = eigfact(A,B)
F.values, F.vectors
end
function eig(A::Number, B::Number)
F = eigfact(A,B)
F.values, F.vectors
end
"""
eigvals!(A, B) -> values
Same as [`eigvals`](@ref), but saves space by overwriting the input `A` (and `B`), instead of creating copies.
"""
function eigvals!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasReal
issymmetric(A) && isposdef(B) && return eigvals!(Symmetric(A), Symmetric(B))
alphar, alphai, beta, vl, vr = LAPACK.ggev!('N', 'N', A, B)
return (iszero(alphai) ? alphar : complex.(alphar, alphai))./beta
end
function eigvals!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasComplex
ishermitian(A) && isposdef(B) && return eigvals!(Hermitian(A), Hermitian(B))
alpha, beta, vl, vr = LAPACK.ggev!('N', 'N', A, B)
alpha./beta
end
"""
eigvals(A, B) -> values
Computes the generalized eigenvalues of `A` and `B`.
# Example
```jldoctest
julia> A = [1 0; 0 -1]
2×2 Array{Int64,2}:
1 0
0 -1
julia> B = [0 1; 1 0]
2×2 Array{Int64,2}:
0 1
1 0
julia> eigvals(A,B)
2-element Array{Complex{Float64},1}:
0.0+1.0im
0.0-1.0im
```
"""
function eigvals(A::AbstractMatrix{TA}, B::AbstractMatrix{TB}) where {TA,TB}
S = promote_type(Float32, typeof(one(TA)/norm(one(TA))),TB)
return eigvals!(copy_oftype(A, S), copy_oftype(B, S))
end
"""
eigvecs(A, B) -> Matrix
Returns a matrix `M` whose columns are the generalized eigenvectors of `A` and `B`. (The `k`th eigenvector can
be obtained from the slice `M[:, k]`.)
# Example
```jldoctest
julia> A = [1 0; 0 -1]
2×2 Array{Int64,2}:
1 0
0 -1
julia> B = [0 1; 1 0]
2×2 Array{Int64,2}:
0 1
1 0
julia> eigvecs(A, B)
2×2 Array{Complex{Float64},2}:
0.0-1.0im 0.0+1.0im
-1.0-0.0im -1.0+0.0im
```
"""
eigvecs(A::AbstractMatrix, B::AbstractMatrix) = eigvecs(eigfact(A, B))
# Conversion methods
## Can we determine the source/result is Real? This is not stored in the type Eigen
convert(::Type{AbstractMatrix}, F::Eigen) = F.vectors * Diagonal(F.values) / F.vectors
convert(::Type{AbstractArray}, F::Eigen) = convert(AbstractMatrix, F)
convert(::Type{Matrix}, F::Eigen) = convert(Array, convert(AbstractArray, F))
convert(::Type{Array}, F::Eigen) = convert(Matrix, F)
full(F::Eigen) = convert(AbstractArray, F)