fix incorrect folder name for julia-0.6.x
Former-commit-id: ef2c7401e0876f22d2f7762d182cfbcd5a7d9c70
This commit is contained in:
@@ -0,0 +1,422 @@
|
||||
# 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
|
||||
@@ -0,0 +1,288 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
module ARPACK
|
||||
|
||||
import ..LinAlg: BlasInt, ARPACKException
|
||||
|
||||
## aupd and eupd wrappers
|
||||
|
||||
function aupd_wrapper(T, matvecA!::Function, matvecB::Function, solveSI::Function, n::Integer,
|
||||
sym::Bool, cmplx::Bool, bmat::String,
|
||||
nev::Integer, ncv::Integer, which::String,
|
||||
tol::Real, maxiter::Integer, mode::Integer, v0::Vector)
|
||||
lworkl = cmplx ? ncv * (3*ncv + 5) : (sym ? ncv * (ncv + 8) : ncv * (3*ncv + 6) )
|
||||
TR = cmplx ? T.types[1] : T
|
||||
TOL = Vector{TR}(1)
|
||||
TOL[1] = tol
|
||||
|
||||
v = Matrix{T}(n, ncv)
|
||||
workd = Vector{T}(3*n)
|
||||
workl = Vector{T}(lworkl)
|
||||
rwork = cmplx ? Vector{TR}(ncv) : Vector{TR}(0)
|
||||
|
||||
if isempty(v0)
|
||||
resid = Vector{T}(n)
|
||||
info = zeros(BlasInt, 1)
|
||||
else
|
||||
resid = deepcopy(v0)
|
||||
info = ones(BlasInt, 1)
|
||||
end
|
||||
iparam = zeros(BlasInt, 11)
|
||||
ipntr = zeros(BlasInt, (sym && !cmplx) ? 11 : 14)
|
||||
ido = zeros(BlasInt, 1)
|
||||
|
||||
iparam[1] = BlasInt(1) # ishifts
|
||||
iparam[3] = BlasInt(maxiter) # maxiter
|
||||
iparam[7] = BlasInt(mode) # mode
|
||||
|
||||
zernm1 = 0:(n-1)
|
||||
|
||||
while true
|
||||
if cmplx
|
||||
naupd(ido, bmat, n, which, nev, TOL, resid, ncv, v, n,
|
||||
iparam, ipntr, workd, workl, lworkl, rwork, info)
|
||||
elseif sym
|
||||
saupd(ido, bmat, n, which, nev, TOL, resid, ncv, v, n,
|
||||
iparam, ipntr, workd, workl, lworkl, info)
|
||||
else
|
||||
naupd(ido, bmat, n, which, nev, TOL, resid, ncv, v, n,
|
||||
iparam, ipntr, workd, workl, lworkl, info)
|
||||
end
|
||||
if info[1] != 0
|
||||
throw(ARPACKException(info[1]))
|
||||
end
|
||||
|
||||
x = view(workd, ipntr[1]+zernm1)
|
||||
y = view(workd, ipntr[2]+zernm1)
|
||||
if mode == 1 # corresponds to dsdrv1, dndrv1 or zndrv1
|
||||
if ido[1] == 1
|
||||
matvecA!(y, x)
|
||||
elseif ido[1] == 99
|
||||
break
|
||||
else
|
||||
throw(ARPACKException("unexpected behavior"))
|
||||
end
|
||||
elseif mode == 3 && bmat == "I" # corresponds to dsdrv2, dndrv2 or zndrv2
|
||||
if ido[1] == -1 || ido[1] == 1
|
||||
y[:] = solveSI(x)
|
||||
elseif ido[1] == 99
|
||||
break
|
||||
else
|
||||
throw(ARPACKException("unexpected behavior"))
|
||||
end
|
||||
elseif mode == 2 # corresponds to dsdrv3, dndrv3 or zndrv3
|
||||
if ido[1] == -1 || ido[1] == 1
|
||||
matvecA!(y, x)
|
||||
if sym
|
||||
x[:] = y # overwrite as per Remark 5 in dsaupd.f
|
||||
end
|
||||
y[:] = solveSI(y)
|
||||
elseif ido[1] == 2
|
||||
y[:] = matvecB(x)
|
||||
elseif ido[1] == 99
|
||||
break
|
||||
else
|
||||
throw(ARPACKException("unexpected behavior"))
|
||||
end
|
||||
elseif mode == 3 && bmat == "G" # corresponds to dsdrv4, dndrv4 or zndrv4
|
||||
if ido[1] == -1
|
||||
y[:] = solveSI(matvecB(x))
|
||||
elseif ido[1] == 1
|
||||
y[:] = solveSI(view(workd,ipntr[3]+zernm1))
|
||||
elseif ido[1] == 2
|
||||
y[:] = matvecB(x)
|
||||
elseif ido[1] == 99
|
||||
break
|
||||
else
|
||||
throw(ARPACKException("unexpected behavior"))
|
||||
end
|
||||
else
|
||||
throw(ArgumentError("ARPACK mode ($mode) not yet supported"))
|
||||
end
|
||||
end
|
||||
|
||||
return (resid, v, n, iparam, ipntr, workd, workl, lworkl, rwork, TOL)
|
||||
end
|
||||
|
||||
function eupd_wrapper(T, n::Integer, sym::Bool, cmplx::Bool, bmat::String,
|
||||
nev::Integer, which::String, ritzvec::Bool,
|
||||
TOL::Array, resid, ncv::Integer, v, ldv, sigma, iparam, ipntr,
|
||||
workd, workl, lworkl, rwork)
|
||||
howmny = "A"
|
||||
select = Vector{BlasInt}(ncv)
|
||||
info = zeros(BlasInt, 1)
|
||||
|
||||
dmap = x->abs.(x)
|
||||
if iparam[7] == 3 # shift-and-invert
|
||||
dmap = x->abs.(1 ./ (x .- sigma))
|
||||
elseif which == "LR" || which == "LA" || which == "BE"
|
||||
dmap = real
|
||||
elseif which == "SR" || which == "SA"
|
||||
dmap = x->-real(x)
|
||||
elseif which == "LI"
|
||||
dmap = imag
|
||||
elseif which == "SI"
|
||||
dmap = x->-imag(x)
|
||||
end
|
||||
|
||||
if cmplx
|
||||
d = Vector{T}(nev+1)
|
||||
sigmar = ones(T, 1)*sigma
|
||||
workev = Vector{T}(2ncv)
|
||||
neupd(ritzvec, howmny, select, d, v, ldv, sigmar, workev,
|
||||
bmat, n, which, nev, TOL, resid, ncv, v, ldv,
|
||||
iparam, ipntr, workd, workl, lworkl, rwork, info)
|
||||
if info[1] != 0
|
||||
throw(ARPACKException(info[1]))
|
||||
end
|
||||
|
||||
p = sortperm(dmap(d[1:nev]), rev=true)
|
||||
return ritzvec ? (d[p], v[1:n, p],iparam[5],iparam[3],iparam[9],resid) : (d[p],iparam[5],iparam[3],iparam[9],resid)
|
||||
elseif sym
|
||||
d = Vector{T}(nev)
|
||||
sigmar = ones(T, 1)*sigma
|
||||
seupd(ritzvec, howmny, select, d, v, ldv, sigmar,
|
||||
bmat, n, which, nev, TOL, resid, ncv, v, ldv,
|
||||
iparam, ipntr, workd, workl, lworkl, info)
|
||||
if info[1] != 0
|
||||
throw(ARPACKException(info[1]))
|
||||
end
|
||||
|
||||
p = sortperm(dmap(d), rev=true)
|
||||
return ritzvec ? (d[p], v[1:n, p],iparam[5],iparam[3],iparam[9],resid) : (d,iparam[5],iparam[3],iparam[9],resid)
|
||||
else
|
||||
dr = Vector{T}(nev+1)
|
||||
di = Vector{T}(nev+1)
|
||||
fill!(dr,NaN)
|
||||
fill!(di,NaN)
|
||||
sigmar = ones(T, 1)*real(sigma)
|
||||
sigmai = ones(T, 1)*imag(sigma)
|
||||
workev = Vector{T}(3*ncv)
|
||||
neupd(ritzvec, howmny, select, dr, di, v, ldv, sigmar, sigmai,
|
||||
workev, bmat, n, which, nev, TOL, resid, ncv, v, ldv,
|
||||
iparam, ipntr, workd, workl, lworkl, info)
|
||||
if info[1] != 0
|
||||
throw(ARPACKException(info[1]))
|
||||
end
|
||||
evec = complex.(Matrix{T}(n, nev+1), Matrix{T}(n, nev+1))
|
||||
|
||||
j = 1
|
||||
while j <= nev
|
||||
if di[j] == 0
|
||||
evec[:,j] = v[:,j]
|
||||
else # For complex conjugate pairs
|
||||
evec[:,j] = v[:,j] + im*v[:,j+1]
|
||||
evec[:,j+1] = v[:,j] - im*v[:,j+1]
|
||||
j += 1
|
||||
end
|
||||
j += 1
|
||||
end
|
||||
if j == nev+1 && !isnan(di[j])
|
||||
if di[j] == 0
|
||||
evec[:,j] = v[:,j]
|
||||
j += 1
|
||||
else
|
||||
throw(ARPACKException("unexpected behavior"))
|
||||
end
|
||||
end
|
||||
|
||||
d = complex.(dr, di)
|
||||
|
||||
if j == nev+1
|
||||
p = sortperm(dmap(d[1:nev]), rev=true)
|
||||
else
|
||||
p = sortperm(dmap(d), rev=true)
|
||||
p = p[1:nev]
|
||||
end
|
||||
|
||||
return ritzvec ? (d[p], evec[1:n, p],iparam[5],iparam[3],iparam[9],resid) : (d[p],iparam[5],iparam[3],iparam[9],resid)
|
||||
end
|
||||
end
|
||||
|
||||
for (T, saupd_name, seupd_name, naupd_name, neupd_name) in
|
||||
((:Float64, :dsaupd_, :dseupd_, :dnaupd_, :dneupd_),
|
||||
(:Float32, :ssaupd_, :sseupd_, :snaupd_, :sneupd_))
|
||||
@eval begin
|
||||
function naupd(ido, bmat, n, evtype, nev, TOL::Array{$T}, resid::Array{$T}, ncv, v::Array{$T}, ldv,
|
||||
iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, info)
|
||||
ccall(($(string(naupd_name)), :libarpack), Void,
|
||||
(Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt},
|
||||
Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, Ptr{BlasInt},
|
||||
Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong),
|
||||
ido, bmat, &n, evtype, &nev, TOL, resid, &ncv, v, &ldv,
|
||||
iparam, ipntr, workd, workl, &lworkl, info, sizeof(bmat), sizeof(evtype))
|
||||
end
|
||||
|
||||
function neupd(rvec, howmny, select, dr, di, z, ldz, sigmar, sigmai,
|
||||
workev::Array{$T}, bmat, n, evtype, nev, TOL::Array{$T}, resid::Array{$T}, ncv, v, ldv,
|
||||
iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, info)
|
||||
ccall(($(string(neupd_name)), :libarpack), Void,
|
||||
(Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{$T},
|
||||
Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{$T}, Ptr{UInt8}, Ptr{BlasInt},
|
||||
Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T},
|
||||
Ptr{BlasInt}, Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T},
|
||||
Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong),
|
||||
&rvec, howmny, select, dr, di, z, &ldz, sigmar, sigmai,
|
||||
workev, bmat, &n, evtype, &nev, TOL, resid, &ncv, v, &ldv,
|
||||
iparam, ipntr, workd, workl, &lworkl, info,
|
||||
sizeof(howmny), sizeof(bmat), sizeof(evtype))
|
||||
end
|
||||
|
||||
function saupd(ido, bmat, n, which, nev, TOL::Array{$T}, resid::Array{$T}, ncv, v::Array{$T}, ldv,
|
||||
iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, info)
|
||||
ccall(($(string(saupd_name)), :libarpack), Void,
|
||||
(Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt},
|
||||
Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, Ptr{BlasInt},
|
||||
Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong),
|
||||
ido, bmat, &n, which, &nev, TOL, resid, &ncv, v, &ldv,
|
||||
iparam, ipntr, workd, workl, &lworkl, info, sizeof(bmat), sizeof(which))
|
||||
end
|
||||
|
||||
function seupd(rvec, howmny, select, d, z, ldz, sigma,
|
||||
bmat, n, evtype, nev, TOL::Array{$T}, resid::Array{$T}, ncv, v::Array{$T}, ldv,
|
||||
iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl, info)
|
||||
ccall(($(string(seupd_name)), :libarpack), Void,
|
||||
(Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T},
|
||||
Ptr{UInt8}, Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt},
|
||||
Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, Ptr{BlasInt}, Ptr{BlasInt},
|
||||
Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{BlasInt}, Clong, Clong, Clong),
|
||||
&rvec, howmny, select, d, z, &ldz, sigma,
|
||||
bmat, &n, evtype, &nev, TOL, resid, &ncv, v, &ldv,
|
||||
iparam, ipntr, workd, workl, &lworkl, info, sizeof(howmny), sizeof(bmat), sizeof(evtype))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for (T, TR, naupd_name, neupd_name) in
|
||||
((:Complex128, :Float64, :znaupd_, :zneupd_),
|
||||
(:Complex64, :Float32, :cnaupd_, :cneupd_))
|
||||
@eval begin
|
||||
function naupd(ido, bmat, n, evtype, nev, TOL::Array{$TR}, resid::Array{$T}, ncv, v::Array{$T}, ldv,
|
||||
iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl,
|
||||
rwork::Array{$TR}, info)
|
||||
ccall(($(string(naupd_name)), :libarpack), Void,
|
||||
(Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt},
|
||||
Ptr{$TR}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, Ptr{BlasInt},
|
||||
Ptr{BlasInt}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt},
|
||||
Ptr{$TR}, Ptr{BlasInt}),
|
||||
ido, bmat, &n, evtype, &nev, TOL, resid, &ncv, v, &ldv,
|
||||
iparam, ipntr, workd, workl, &lworkl, rwork, info)
|
||||
end
|
||||
|
||||
function neupd(rvec, howmny, select, d, z, ldz, sigma, workev::Array{$T},
|
||||
bmat, n, evtype, nev, TOL::Array{$TR}, resid::Array{$T}, ncv, v::Array{$T}, ldv,
|
||||
iparam, ipntr, workd::Array{$T}, workl::Array{$T}, lworkl,
|
||||
rwork::Array{$TR}, info)
|
||||
ccall(($(string(neupd_name)), :libarpack), Void,
|
||||
(Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt},
|
||||
Ptr{$T}, Ptr{$T}, Ptr{UInt8}, Ptr{BlasInt}, Ptr{UInt8}, Ptr{BlasInt},
|
||||
Ptr{$TR}, Ptr{$T}, Ptr{BlasInt}, Ptr{$T}, Ptr{BlasInt}, Ptr{BlasInt},
|
||||
Ptr{BlasInt}, Ptr{$T}, Ptr{$T}, Ptr{BlasInt}, Ptr{$TR}, Ptr{BlasInt}),
|
||||
&rvec, howmny, select, d, z, &ldz, sigma, workev,
|
||||
bmat, &n, evtype, &nev, TOL, resid, &ncv, v, &ldv,
|
||||
iparam, ipntr, workd, workl, &lworkl, rwork, info)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end # module ARPACK
|
||||
@@ -0,0 +1,641 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# Bidiagonal matrices
|
||||
mutable struct Bidiagonal{T} <: AbstractMatrix{T}
|
||||
dv::Vector{T} # diagonal
|
||||
ev::Vector{T} # sub/super diagonal
|
||||
isupper::Bool # is upper bidiagonal (true) or lower (false)
|
||||
function Bidiagonal{T}(dv::Vector{T}, ev::Vector{T}, isupper::Bool) where T
|
||||
if length(ev) != length(dv)-1
|
||||
throw(DimensionMismatch("length of diagonal vector is $(length(dv)), length of off-diagonal vector is $(length(ev))"))
|
||||
end
|
||||
new(dv, ev, isupper)
|
||||
end
|
||||
end
|
||||
"""
|
||||
Bidiagonal(dv, ev, isupper::Bool)
|
||||
|
||||
Constructs an upper (`isupper=true`) or lower (`isupper=false`) bidiagonal matrix using the
|
||||
given diagonal (`dv`) and off-diagonal (`ev`) vectors. The result is of type `Bidiagonal`
|
||||
and provides efficient specialized linear solvers, but may be converted into a regular
|
||||
matrix with [`convert(Array, _)`](@ref) (or `Array(_)` for short). `ev`'s length
|
||||
must be one less than the length of `dv`.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> dv = [1; 2; 3; 4]
|
||||
4-element Array{Int64,1}:
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
|
||||
julia> ev = [7; 8; 9]
|
||||
3-element Array{Int64,1}:
|
||||
7
|
||||
8
|
||||
9
|
||||
|
||||
julia> Bu = Bidiagonal(dv, ev, true) # ev is on the first superdiagonal
|
||||
4×4 Bidiagonal{Int64}:
|
||||
1 7 ⋅ ⋅
|
||||
⋅ 2 8 ⋅
|
||||
⋅ ⋅ 3 9
|
||||
⋅ ⋅ ⋅ 4
|
||||
|
||||
julia> Bl = Bidiagonal(dv, ev, false) # ev is on the first subdiagonal
|
||||
4×4 Bidiagonal{Int64}:
|
||||
1 ⋅ ⋅ ⋅
|
||||
7 2 ⋅ ⋅
|
||||
⋅ 8 3 ⋅
|
||||
⋅ ⋅ 9 4
|
||||
```
|
||||
"""
|
||||
Bidiagonal(dv::AbstractVector{T}, ev::AbstractVector{T}, isupper::Bool) where {T} = Bidiagonal{T}(collect(dv), collect(ev), isupper)
|
||||
Bidiagonal(dv::AbstractVector, ev::AbstractVector) = throw(ArgumentError("did you want an upper or lower Bidiagonal? Try again with an additional true (upper) or false (lower) argument."))
|
||||
|
||||
"""
|
||||
Bidiagonal(dv, ev, uplo::Char)
|
||||
|
||||
Constructs an upper (`uplo='U'`) or lower (`uplo='L'`) bidiagonal matrix using the
|
||||
given diagonal (`dv`) and off-diagonal (`ev`) vectors. The result is of type `Bidiagonal`
|
||||
and provides efficient specialized linear solvers, but may be converted into a regular
|
||||
matrix with [`convert(Array, _)`](@ref) (or `Array(_)` for short). `ev`'s
|
||||
length must be one less than the length of `dv`.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> dv = [1; 2; 3; 4]
|
||||
4-element Array{Int64,1}:
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
|
||||
julia> ev = [7; 8; 9]
|
||||
3-element Array{Int64,1}:
|
||||
7
|
||||
8
|
||||
9
|
||||
|
||||
julia> Bu = Bidiagonal(dv, ev, 'U') #e is on the first superdiagonal
|
||||
4×4 Bidiagonal{Int64}:
|
||||
1 7 ⋅ ⋅
|
||||
⋅ 2 8 ⋅
|
||||
⋅ ⋅ 3 9
|
||||
⋅ ⋅ ⋅ 4
|
||||
|
||||
julia> Bl = Bidiagonal(dv, ev, 'L') #e is on the first subdiagonal
|
||||
4×4 Bidiagonal{Int64}:
|
||||
1 ⋅ ⋅ ⋅
|
||||
7 2 ⋅ ⋅
|
||||
⋅ 8 3 ⋅
|
||||
⋅ ⋅ 9 4
|
||||
```
|
||||
"""
|
||||
#Convert from BLAS uplo flag to boolean internal
|
||||
function Bidiagonal(dv::AbstractVector, ev::AbstractVector, uplo::Char)
|
||||
if uplo === 'U'
|
||||
isupper = true
|
||||
elseif uplo === 'L'
|
||||
isupper = false
|
||||
else
|
||||
throw(ArgumentError("Bidiagonal uplo argument must be upper 'U' or lower 'L', got $(repr(uplo))"))
|
||||
end
|
||||
Bidiagonal(collect(dv), collect(ev), isupper)
|
||||
end
|
||||
function Bidiagonal(dv::AbstractVector{Td}, ev::AbstractVector{Te}, isupper::Bool) where {Td,Te}
|
||||
T = promote_type(Td,Te)
|
||||
Bidiagonal(convert(Vector{T}, dv), convert(Vector{T}, ev), isupper)
|
||||
end
|
||||
|
||||
"""
|
||||
Bidiagonal(A, isupper::Bool)
|
||||
|
||||
Construct a `Bidiagonal` matrix from the main diagonal of `A` and
|
||||
its first super- (if `isupper=true`) or sub-diagonal (if `isupper=false`).
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1 1 1 1; 2 2 2 2; 3 3 3 3; 4 4 4 4]
|
||||
4×4 Array{Int64,2}:
|
||||
1 1 1 1
|
||||
2 2 2 2
|
||||
3 3 3 3
|
||||
4 4 4 4
|
||||
|
||||
julia> Bidiagonal(A, true) #contains the main diagonal and first superdiagonal of A
|
||||
4×4 Bidiagonal{Int64}:
|
||||
1 1 ⋅ ⋅
|
||||
⋅ 2 2 ⋅
|
||||
⋅ ⋅ 3 3
|
||||
⋅ ⋅ ⋅ 4
|
||||
|
||||
julia> Bidiagonal(A, false) #contains the main diagonal and first subdiagonal of A
|
||||
4×4 Bidiagonal{Int64}:
|
||||
1 ⋅ ⋅ ⋅
|
||||
2 2 ⋅ ⋅
|
||||
⋅ 3 3 ⋅
|
||||
⋅ ⋅ 4 4
|
||||
```
|
||||
"""
|
||||
Bidiagonal(A::AbstractMatrix, isupper::Bool)=Bidiagonal(diag(A), diag(A, isupper?1:-1), isupper)
|
||||
|
||||
function getindex(A::Bidiagonal{T}, i::Integer, j::Integer) where T
|
||||
if !((1 <= i <= size(A,2)) && (1 <= j <= size(A,2)))
|
||||
throw(BoundsError(A,(i,j)))
|
||||
end
|
||||
if i == j
|
||||
return A.dv[i]
|
||||
elseif (istriu(A) && (i == j - 1)) || (istril(A) && (i == j + 1))
|
||||
return A.ev[min(i,j)]
|
||||
else
|
||||
return zero(T)
|
||||
end
|
||||
end
|
||||
|
||||
function setindex!(A::Bidiagonal, x, i::Integer, j::Integer)
|
||||
@boundscheck checkbounds(A, i, j)
|
||||
if i == j
|
||||
@inbounds A.dv[i] = x
|
||||
elseif istriu(A) && (i == j - 1)
|
||||
@inbounds A.ev[i] = x
|
||||
elseif istril(A) && (i == j + 1)
|
||||
@inbounds A.ev[j] = x
|
||||
elseif !iszero(x)
|
||||
throw(ArgumentError(string("cannot set entry ($i, $j) off the ",
|
||||
"$(istriu(A) ? "upper" : "lower") bidiagonal band to a nonzero value ($x)")))
|
||||
end
|
||||
return x
|
||||
end
|
||||
|
||||
## structured matrix methods ##
|
||||
function Base.replace_in_print_matrix(A::Bidiagonal,i::Integer,j::Integer,s::AbstractString)
|
||||
if A.isupper
|
||||
i==j || i==j-1 ? s : Base.replace_with_centered_mark(s)
|
||||
else
|
||||
i==j || i==j+1 ? s : Base.replace_with_centered_mark(s)
|
||||
end
|
||||
end
|
||||
|
||||
#Converting from Bidiagonal to dense Matrix
|
||||
function convert(::Type{Matrix{T}}, A::Bidiagonal) where T
|
||||
n = size(A, 1)
|
||||
B = zeros(T, n, n)
|
||||
for i = 1:n - 1
|
||||
B[i,i] = A.dv[i]
|
||||
if A.isupper
|
||||
B[i, i + 1] = A.ev[i]
|
||||
else
|
||||
B[i + 1, i] = A.ev[i]
|
||||
end
|
||||
end
|
||||
B[n,n] = A.dv[n]
|
||||
return B
|
||||
end
|
||||
convert(::Type{Matrix}, A::Bidiagonal{T}) where {T} = convert(Matrix{T}, A)
|
||||
convert(::Type{Array}, A::Bidiagonal) = convert(Matrix, A)
|
||||
full(A::Bidiagonal) = convert(Array, A)
|
||||
promote_rule(::Type{Matrix{T}}, ::Type{Bidiagonal{S}}) where {T,S} = Matrix{promote_type(T,S)}
|
||||
|
||||
#Converting from Bidiagonal to Tridiagonal
|
||||
Tridiagonal(M::Bidiagonal{T}) where {T} = convert(Tridiagonal{T}, M)
|
||||
function convert(::Type{Tridiagonal{T}}, A::Bidiagonal) where T
|
||||
z = zeros(T, size(A)[1]-1)
|
||||
A.isupper ? Tridiagonal(z, convert(Vector{T},A.dv), convert(Vector{T},A.ev)) : Tridiagonal(convert(Vector{T},A.ev), convert(Vector{T},A.dv), z)
|
||||
end
|
||||
promote_rule(::Type{Tridiagonal{T}}, ::Type{Bidiagonal{S}}) where {T,S} = Tridiagonal{promote_type(T,S)}
|
||||
|
||||
# No-op for trivial conversion Bidiagonal{T} -> Bidiagonal{T}
|
||||
convert(::Type{Bidiagonal{T}}, A::Bidiagonal{T}) where {T} = A
|
||||
# Convert Bidiagonal to Bidiagonal{T} by constructing a new instance with converted elements
|
||||
convert(::Type{Bidiagonal{T}}, A::Bidiagonal) where {T} = Bidiagonal(convert(Vector{T}, A.dv), convert(Vector{T}, A.ev), A.isupper)
|
||||
# When asked to convert Bidiagonal to AbstractMatrix{T}, preserve structure by converting to Bidiagonal{T} <: AbstractMatrix{T}
|
||||
convert(::Type{AbstractMatrix{T}}, A::Bidiagonal) where {T} = convert(Bidiagonal{T}, A)
|
||||
|
||||
broadcast(::typeof(big), B::Bidiagonal) = Bidiagonal(big.(B.dv), big.(B.ev), B.isupper)
|
||||
|
||||
similar(B::Bidiagonal, ::Type{T}) where {T} = Bidiagonal{T}(similar(B.dv, T), similar(B.ev, T), B.isupper)
|
||||
|
||||
###################
|
||||
# LAPACK routines #
|
||||
###################
|
||||
|
||||
#Singular values
|
||||
svdvals!(M::Bidiagonal{<:BlasReal}) = LAPACK.bdsdc!(M.isupper ? 'U' : 'L', 'N', M.dv, M.ev)[1]
|
||||
function svdfact!(M::Bidiagonal{<:BlasReal}; thin::Bool=true)
|
||||
d, e, U, Vt, Q, iQ = LAPACK.bdsdc!(M.isupper ? 'U' : 'L', 'I', M.dv, M.ev)
|
||||
SVD(U, d, Vt)
|
||||
end
|
||||
svdfact(M::Bidiagonal; thin::Bool=true) = svdfact!(copy(M),thin=thin)
|
||||
|
||||
####################
|
||||
# Generic routines #
|
||||
####################
|
||||
|
||||
function show(io::IO, M::Bidiagonal)
|
||||
# TODO: make this readable and one-line
|
||||
println(io, summary(M), ":")
|
||||
print(io, " diag:")
|
||||
print_matrix(io, (M.dv)')
|
||||
print(io, M.isupper?"\n super:":"\n sub:")
|
||||
print_matrix(io, (M.ev)')
|
||||
end
|
||||
|
||||
size(M::Bidiagonal) = (length(M.dv), length(M.dv))
|
||||
function size(M::Bidiagonal, d::Integer)
|
||||
if d < 1
|
||||
throw(ArgumentError("dimension must be ≥ 1, got $d"))
|
||||
elseif d <= 2
|
||||
return length(M.dv)
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
#Elementary operations
|
||||
broadcast(::typeof(abs), M::Bidiagonal) = Bidiagonal(abs.(M.dv), abs.(M.ev), abs.(M.isupper))
|
||||
broadcast(::typeof(round), M::Bidiagonal) = Bidiagonal(round.(M.dv), round.(M.ev), M.isupper)
|
||||
broadcast(::typeof(trunc), M::Bidiagonal) = Bidiagonal(trunc.(M.dv), trunc.(M.ev), M.isupper)
|
||||
broadcast(::typeof(floor), M::Bidiagonal) = Bidiagonal(floor.(M.dv), floor.(M.ev), M.isupper)
|
||||
broadcast(::typeof(ceil), M::Bidiagonal) = Bidiagonal(ceil.(M.dv), ceil.(M.ev), M.isupper)
|
||||
for func in (:conj, :copy, :real, :imag)
|
||||
@eval ($func)(M::Bidiagonal) = Bidiagonal(($func)(M.dv), ($func)(M.ev), M.isupper)
|
||||
end
|
||||
broadcast(::typeof(round), ::Type{T}, M::Bidiagonal) where {T<:Integer} = Bidiagonal(round.(T, M.dv), round.(T, M.ev), M.isupper)
|
||||
broadcast(::typeof(trunc), ::Type{T}, M::Bidiagonal) where {T<:Integer} = Bidiagonal(trunc.(T, M.dv), trunc.(T, M.ev), M.isupper)
|
||||
broadcast(::typeof(floor), ::Type{T}, M::Bidiagonal) where {T<:Integer} = Bidiagonal(floor.(T, M.dv), floor.(T, M.ev), M.isupper)
|
||||
broadcast(::typeof(ceil), ::Type{T}, M::Bidiagonal) where {T<:Integer} = Bidiagonal(ceil.(T, M.dv), ceil.(T, M.ev), M.isupper)
|
||||
|
||||
transpose(M::Bidiagonal) = Bidiagonal(M.dv, M.ev, !M.isupper)
|
||||
ctranspose(M::Bidiagonal) = Bidiagonal(conj(M.dv), conj(M.ev), !M.isupper)
|
||||
|
||||
istriu(M::Bidiagonal) = M.isupper || iszero(M.ev)
|
||||
istril(M::Bidiagonal) = !M.isupper || iszero(M.ev)
|
||||
|
||||
function tril!(M::Bidiagonal, k::Integer=0)
|
||||
n = length(M.dv)
|
||||
if abs(k) > n
|
||||
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($n,$n)"))
|
||||
elseif M.isupper && k < 0
|
||||
fill!(M.dv,0)
|
||||
fill!(M.ev,0)
|
||||
elseif k < -1
|
||||
fill!(M.dv,0)
|
||||
fill!(M.ev,0)
|
||||
elseif M.isupper && k == 0
|
||||
fill!(M.ev,0)
|
||||
elseif !M.isupper && k == -1
|
||||
fill!(M.dv,0)
|
||||
end
|
||||
return M
|
||||
end
|
||||
|
||||
function triu!(M::Bidiagonal, k::Integer=0)
|
||||
n = length(M.dv)
|
||||
if abs(k) > n
|
||||
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($n,$n)"))
|
||||
elseif !M.isupper && k > 0
|
||||
fill!(M.dv,0)
|
||||
fill!(M.ev,0)
|
||||
elseif k > 1
|
||||
fill!(M.dv,0)
|
||||
fill!(M.ev,0)
|
||||
elseif !M.isupper && k == 0
|
||||
fill!(M.ev,0)
|
||||
elseif M.isupper && k == 1
|
||||
fill!(M.dv,0)
|
||||
end
|
||||
return M
|
||||
end
|
||||
|
||||
function diag(M::Bidiagonal{T}, n::Integer=0) where T
|
||||
if n == 0
|
||||
return M.dv
|
||||
elseif n == 1
|
||||
return M.isupper ? M.ev : zeros(T, size(M,1)-1)
|
||||
elseif n == -1
|
||||
return M.isupper ? zeros(T, size(M,1)-1) : M.ev
|
||||
elseif -size(M,1) < n < size(M,1)
|
||||
return zeros(T, size(M,1)-abs(n))
|
||||
else
|
||||
throw(ArgumentError("matrix size is $(size(M)), n is $n"))
|
||||
end
|
||||
end
|
||||
|
||||
function +(A::Bidiagonal, B::Bidiagonal)
|
||||
if A.isupper == B.isupper
|
||||
Bidiagonal(A.dv+B.dv, A.ev+B.ev, A.isupper)
|
||||
else
|
||||
Tridiagonal((A.isupper ? (B.ev,A.dv+B.dv,A.ev) : (A.ev,A.dv+B.dv,B.ev))...)
|
||||
end
|
||||
end
|
||||
|
||||
function -(A::Bidiagonal, B::Bidiagonal)
|
||||
if A.isupper == B.isupper
|
||||
Bidiagonal(A.dv-B.dv, A.ev-B.ev, A.isupper)
|
||||
else
|
||||
Tridiagonal((A.isupper ? (-B.ev,A.dv-B.dv,A.ev) : (A.ev,A.dv-B.dv,-B.ev))...)
|
||||
end
|
||||
end
|
||||
|
||||
-(A::Bidiagonal)=Bidiagonal(-A.dv,-A.ev,A.isupper)
|
||||
*(A::Bidiagonal, B::Number) = Bidiagonal(A.dv*B, A.ev*B, A.isupper)
|
||||
*(B::Number, A::Bidiagonal) = A*B
|
||||
/(A::Bidiagonal, B::Number) = Bidiagonal(A.dv/B, A.ev/B, A.isupper)
|
||||
==(A::Bidiagonal, B::Bidiagonal) = (A.dv==B.dv) && (A.ev==B.ev) && (A.isupper==B.isupper)
|
||||
|
||||
const BiTriSym = Union{Bidiagonal,Tridiagonal,SymTridiagonal}
|
||||
const BiTri = Union{Bidiagonal,Tridiagonal}
|
||||
A_mul_B!(C::AbstractMatrix, A::SymTridiagonal, B::BiTriSym) = A_mul_B_td!(C, A, B)
|
||||
A_mul_B!(C::AbstractMatrix, A::BiTri, B::BiTriSym) = A_mul_B_td!(C, A, B)
|
||||
A_mul_B!(C::AbstractMatrix, A::BiTriSym, B::BiTriSym) = A_mul_B_td!(C, A, B)
|
||||
A_mul_B!(C::AbstractMatrix, A::AbstractTriangular, B::BiTriSym) = A_mul_B_td!(C, A, B)
|
||||
A_mul_B!(C::AbstractMatrix, A::AbstractMatrix, B::BiTriSym) = A_mul_B_td!(C, A, B)
|
||||
A_mul_B!(C::AbstractVector, A::BiTri, B::AbstractVector) = A_mul_B_td!(C, A, B)
|
||||
A_mul_B!(C::AbstractMatrix, A::BiTri, B::AbstractVecOrMat) = A_mul_B_td!(C, A, B)
|
||||
A_mul_B!(C::AbstractVecOrMat, A::BiTri, B::AbstractVecOrMat) = A_mul_B_td!(C, A, B)
|
||||
|
||||
\(::Diagonal, ::RowVector) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector"))
|
||||
\(::Bidiagonal, ::RowVector) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector"))
|
||||
\(::Bidiagonal{<:Number}, ::RowVector{<:Number}) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector"))
|
||||
|
||||
At_ldiv_B(::Bidiagonal, ::RowVector) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector"))
|
||||
At_ldiv_B(::Bidiagonal{<:Number}, ::RowVector{<:Number}) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector"))
|
||||
|
||||
Ac_ldiv_B(::Bidiagonal, ::RowVector) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector"))
|
||||
Ac_ldiv_B(::Bidiagonal{<:Number}, ::RowVector{<:Number}) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector"))
|
||||
|
||||
function check_A_mul_B!_sizes(C, A, B)
|
||||
nA, mA = size(A)
|
||||
nB, mB = size(B)
|
||||
nC, mC = size(C)
|
||||
if nA != nC
|
||||
throw(DimensionMismatch("sizes size(A)=$(size(A)) and size(C) = $(size(C)) must match at first entry."))
|
||||
elseif mA != nB
|
||||
throw(DimensionMismatch("second entry of size(A)=$(size(A)) and first entry of size(B) = $(size(B)) must match."))
|
||||
elseif mB != mC
|
||||
throw(DimensionMismatch("sizes size(B)=$(size(B)) and size(C) = $(size(C)) must match at first second entry."))
|
||||
end
|
||||
end
|
||||
|
||||
function A_mul_B_td!(C::AbstractMatrix, A::BiTriSym, B::BiTriSym)
|
||||
check_A_mul_B!_sizes(C, A, B)
|
||||
n = size(A,1)
|
||||
n <= 3 && return A_mul_B!(C, Array(A), Array(B))
|
||||
fill!(C, zero(eltype(C)))
|
||||
Al = diag(A, -1)
|
||||
Ad = diag(A, 0)
|
||||
Au = diag(A, 1)
|
||||
Bl = diag(B, -1)
|
||||
Bd = diag(B, 0)
|
||||
Bu = diag(B, 1)
|
||||
@inbounds begin
|
||||
# first row of C
|
||||
C[1,1] = A[1,1]*B[1,1] + A[1, 2]*B[2, 1]
|
||||
C[1,2] = A[1,1]*B[1,2] + A[1,2]*B[2,2]
|
||||
C[1,3] = A[1,2]*B[2,3]
|
||||
# second row of C
|
||||
C[2,1] = A[2,1]*B[1,1] + A[2,2]*B[2,1]
|
||||
C[2,2] = A[2,1]*B[1,2] + A[2,2]*B[2,2] + A[2,3]*B[3,2]
|
||||
C[2,3] = A[2,2]*B[2,3] + A[2,3]*B[3,3]
|
||||
C[2,4] = A[2,3]*B[3,4]
|
||||
for j in 3:n-2
|
||||
Ajj₋1 = Al[j-1]
|
||||
Ajj = Ad[j]
|
||||
Ajj₊1 = Au[j]
|
||||
Bj₋1j₋2 = Bl[j-2]
|
||||
Bj₋1j₋1 = Bd[j-1]
|
||||
Bj₋1j = Bu[j-1]
|
||||
Bjj₋1 = Bl[j-1]
|
||||
Bjj = Bd[j]
|
||||
Bjj₊1 = Bu[j]
|
||||
Bj₊1j = Bl[j]
|
||||
Bj₊1j₊1 = Bd[j+1]
|
||||
Bj₊1j₊2 = Bu[j+1]
|
||||
C[j,j-2] = Ajj₋1*Bj₋1j₋2
|
||||
C[j, j-1] = Ajj₋1*Bj₋1j₋1 + Ajj*Bjj₋1
|
||||
C[j, j ] = Ajj₋1*Bj₋1j + Ajj*Bjj + Ajj₊1*Bj₊1j
|
||||
C[j, j+1] = Ajj *Bjj₊1 + Ajj₊1*Bj₊1j₊1
|
||||
C[j, j+2] = Ajj₊1*Bj₊1j₊2
|
||||
end
|
||||
# row before last of C
|
||||
C[n-1,n-3] = A[n-1,n-2]*B[n-2,n-3]
|
||||
C[n-1,n-2] = A[n-1,n-1]*B[n-1,n-2] + A[n-1,n-2]*B[n-2,n-2]
|
||||
C[n-1,n-1] = A[n-1,n-2]*B[n-2,n-1] + A[n-1,n-1]*B[n-1,n-1] + A[n-1,n]*B[n,n-1]
|
||||
C[n-1,n ] = A[n-1,n-1]*B[n-1,n ] + A[n-1, n]*B[n ,n ]
|
||||
# last row of C
|
||||
C[n,n-2] = A[n,n-1]*B[n-1,n-2]
|
||||
C[n,n-1] = A[n,n-1]*B[n-1,n-1] + A[n,n]*B[n,n-1]
|
||||
C[n,n ] = A[n,n-1]*B[n-1,n ] + A[n,n]*B[n,n ]
|
||||
end # inbounds
|
||||
C
|
||||
end
|
||||
|
||||
function A_mul_B_td!(C::AbstractVecOrMat, A::BiTriSym, B::AbstractVecOrMat)
|
||||
nA = size(A,1)
|
||||
nB = size(B,2)
|
||||
if !(size(C,1) == size(B,1) == nA)
|
||||
throw(DimensionMismatch("A has first dimension $nA, B has $(size(B,1)), C has $(size(C,1)) but all must match"))
|
||||
end
|
||||
if size(C,2) != nB
|
||||
throw(DimensionMismatch("A has second dimension $nA, B has $(size(B,2)), C has $(size(C,2)) but all must match"))
|
||||
end
|
||||
nA <= 3 && return A_mul_B!(C, Array(A), Array(B))
|
||||
l = diag(A, -1)
|
||||
d = diag(A, 0)
|
||||
u = diag(A, 1)
|
||||
@inbounds begin
|
||||
for j = 1:nB
|
||||
b₀, b₊ = B[1, j], B[2, j]
|
||||
C[1, j] = d[1]*b₀ + u[1]*b₊
|
||||
for i = 2:nA - 1
|
||||
b₋, b₀, b₊ = b₀, b₊, B[i + 1, j]
|
||||
C[i, j] = l[i - 1]*b₋ + d[i]*b₀ + u[i]*b₊
|
||||
end
|
||||
C[nA, j] = l[nA - 1]*b₀ + d[nA]*b₊
|
||||
end
|
||||
end
|
||||
C
|
||||
end
|
||||
|
||||
function A_mul_B_td!(C::AbstractMatrix, A::AbstractMatrix, B::BiTriSym)
|
||||
check_A_mul_B!_sizes(C, A, B)
|
||||
n = size(A,1)
|
||||
n <= 3 && return A_mul_B!(C, Array(A), Array(B))
|
||||
m = size(B,2)
|
||||
Bl = diag(B, -1)
|
||||
Bd = diag(B, 0)
|
||||
Bu = diag(B, 1)
|
||||
@inbounds begin
|
||||
# first and last column of C
|
||||
B11 = Bd[1]
|
||||
B21 = Bl[1]
|
||||
Bmm = Bd[m]
|
||||
Bm₋1m = Bu[m-1]
|
||||
for i in 1:n
|
||||
C[i, 1] = A[i,1] * B11 + A[i, 2] * B21
|
||||
C[i, m] = A[i, m-1] * Bm₋1m + A[i, m] * Bmm
|
||||
end
|
||||
# middle columns of C
|
||||
for j = 2:m-1
|
||||
Bj₋1j = Bu[j-1]
|
||||
Bjj = Bd[j]
|
||||
Bj₊1j = Bl[j]
|
||||
for i = 1:n
|
||||
C[i, j] = A[i, j-1] * Bj₋1j + A[i, j]*Bjj + A[i, j+1] * Bj₊1j
|
||||
end
|
||||
end
|
||||
end # inbounds
|
||||
C
|
||||
end
|
||||
|
||||
const SpecialMatrix = Union{Bidiagonal,SymTridiagonal,Tridiagonal}
|
||||
# to avoid ambiguity warning, but shouldn't be necessary
|
||||
*(A::AbstractTriangular, B::SpecialMatrix) = Array(A) * Array(B)
|
||||
*(A::SpecialMatrix, B::SpecialMatrix) = Array(A) * Array(B)
|
||||
|
||||
#Generic multiplication
|
||||
for func in (:*, :Ac_mul_B, :A_mul_Bc, :/, :A_rdiv_Bc)
|
||||
@eval ($func)(A::Bidiagonal{T}, B::AbstractVector{T}) where {T} = ($func)(Array(A), B)
|
||||
end
|
||||
|
||||
#Linear solvers
|
||||
A_ldiv_B!(A::Union{Bidiagonal, AbstractTriangular}, b::AbstractVector) = naivesub!(A, b)
|
||||
At_ldiv_B!(A::Bidiagonal, b::AbstractVector) = A_ldiv_B!(transpose(A), b)
|
||||
Ac_ldiv_B!(A::Bidiagonal, b::AbstractVector) = A_ldiv_B!(ctranspose(A), b)
|
||||
function A_ldiv_B!(A::Union{Bidiagonal,AbstractTriangular}, B::AbstractMatrix)
|
||||
nA,mA = size(A)
|
||||
tmp = similar(B,size(B,1))
|
||||
n = size(B, 1)
|
||||
if nA != n
|
||||
throw(DimensionMismatch("size of A is ($nA,$mA), corresponding dimension of B is $n"))
|
||||
end
|
||||
for i = 1:size(B,2)
|
||||
copy!(tmp, 1, B, (i - 1)*n + 1, n)
|
||||
A_ldiv_B!(A, tmp)
|
||||
copy!(B, (i - 1)*n + 1, tmp, 1, n) # Modify this when array view are implemented.
|
||||
end
|
||||
B
|
||||
end
|
||||
for func in (:Ac_ldiv_B!, :At_ldiv_B!)
|
||||
@eval function ($func)(A::Union{Bidiagonal,AbstractTriangular}, B::AbstractMatrix)
|
||||
nA,mA = size(A)
|
||||
tmp = similar(B,size(B,1))
|
||||
n = size(B, 1)
|
||||
if mA != n
|
||||
throw(DimensionMismatch("size of A' is ($mA,$nA), corresponding dimension of B is $n"))
|
||||
end
|
||||
for i = 1:size(B,2)
|
||||
copy!(tmp, 1, B, (i - 1)*n + 1, n)
|
||||
($func)(A, tmp)
|
||||
copy!(B, (i - 1)*n + 1, tmp, 1, n) # Modify this when array view are implemented.
|
||||
end
|
||||
B
|
||||
end
|
||||
end
|
||||
#Generic solver using naive substitution
|
||||
function naivesub!(A::Bidiagonal{T}, b::AbstractVector, x::AbstractVector = b) where T
|
||||
N = size(A, 2)
|
||||
if N != length(b) || N != length(x)
|
||||
throw(DimensionMismatch("second dimension of A, $N, does not match one of the lengths of x, $(length(x)), or b, $(length(b))"))
|
||||
end
|
||||
if !A.isupper #do forward substitution
|
||||
for j = 1:N
|
||||
x[j] = b[j]
|
||||
j > 1 && (x[j] -= A.ev[j-1] * x[j-1])
|
||||
x[j] /= A.dv[j] == zero(T) ? throw(SingularException(j)) : A.dv[j]
|
||||
end
|
||||
else #do backward substitution
|
||||
for j = N:-1:1
|
||||
x[j] = b[j]
|
||||
j < N && (x[j] -= A.ev[j] * x[j+1])
|
||||
x[j] /= A.dv[j] == zero(T) ? throw(SingularException(j)) : A.dv[j]
|
||||
end
|
||||
end
|
||||
x
|
||||
end
|
||||
|
||||
### Generic promotion methods and fallbacks
|
||||
for (f,g) in ((:\, :A_ldiv_B!), (:At_ldiv_B, :At_ldiv_B!), (:Ac_ldiv_B, :Ac_ldiv_B!))
|
||||
@eval begin
|
||||
function ($f)(A::Bidiagonal{TA}, B::AbstractVecOrMat{TB}) where {TA<:Number,TB<:Number}
|
||||
TAB = typeof((zero(TA)*zero(TB) + zero(TA)*zero(TB))/one(TA))
|
||||
($g)(convert(AbstractArray{TAB}, A), copy_oftype(B, TAB))
|
||||
end
|
||||
($f)(A::Bidiagonal, B::AbstractVecOrMat) = ($g)(A, copy(B))
|
||||
end
|
||||
end
|
||||
|
||||
factorize(A::Bidiagonal) = A
|
||||
|
||||
# Eigensystems
|
||||
eigvals(M::Bidiagonal) = M.dv
|
||||
function eigvecs(M::Bidiagonal{T}) where T
|
||||
n = length(M.dv)
|
||||
Q = Matrix{T}(n,n)
|
||||
blks = [0; find(x -> x == 0, M.ev); n]
|
||||
v = zeros(T, n)
|
||||
if M.isupper
|
||||
for idx_block = 1:length(blks) - 1, i = blks[idx_block] + 1:blks[idx_block + 1] #index of eigenvector
|
||||
fill!(v, zero(T))
|
||||
v[blks[idx_block] + 1] = one(T)
|
||||
for j = blks[idx_block] + 1:i - 1 #Starting from j=i, eigenvector elements will be 0
|
||||
v[j+1] = (M.dv[i] - M.dv[j])/M.ev[j] * v[j]
|
||||
end
|
||||
c = norm(v)
|
||||
for j = 1:n
|
||||
Q[j, i] = v[j] / c
|
||||
end
|
||||
end
|
||||
else
|
||||
for idx_block = 1:length(blks) - 1, i = blks[idx_block + 1]:-1:blks[idx_block] + 1 #index of eigenvector
|
||||
fill!(v, zero(T))
|
||||
v[blks[idx_block+1]] = one(T)
|
||||
for j = (blks[idx_block+1] - 1):-1:max(1, (i - 1)) #Starting from j=i, eigenvector elements will be 0
|
||||
v[j] = (M.dv[i] - M.dv[j+1])/M.ev[j] * v[j+1]
|
||||
end
|
||||
c = norm(v)
|
||||
for j = 1:n
|
||||
Q[j, i] = v[j] / c
|
||||
end
|
||||
end
|
||||
end
|
||||
Q #Actually Triangular
|
||||
end
|
||||
eigfact(M::Bidiagonal) = Eigen(eigvals(M), eigvecs(M))
|
||||
|
||||
# fill! methods
|
||||
_valuefields(::Type{<:Diagonal}) = [:diag]
|
||||
_valuefields(::Type{<:Bidiagonal}) = [:dv, :ev]
|
||||
_valuefields(::Type{<:Tridiagonal}) = [:dl, :d, :du]
|
||||
_valuefields(::Type{<:SymTridiagonal}) = [:dv, :ev]
|
||||
_valuefields(::Type{<:AbstractTriangular}) = [:data]
|
||||
|
||||
const SpecialArrays = Union{Diagonal,Bidiagonal,Tridiagonal,SymTridiagonal,AbstractTriangular}
|
||||
|
||||
@generated function fillslots!(A::SpecialArrays, x)
|
||||
ex = :(xT = convert(eltype(A), x))
|
||||
for field in _valuefields(A)
|
||||
ex = :($ex; fill!(A.$field, xT))
|
||||
end
|
||||
:($ex;return A)
|
||||
end
|
||||
|
||||
# for historical reasons:
|
||||
fill!(a::AbstractTriangular, x) = fillslots!(a, x)
|
||||
fill!(D::Diagonal, x) = fillslots!(D, x)
|
||||
|
||||
_small_enough(A::Bidiagonal) = size(A, 1) <= 1
|
||||
_small_enough(A::Tridiagonal) = size(A, 1) <= 2
|
||||
_small_enough(A::SymTridiagonal) = size(A, 1) <= 2
|
||||
|
||||
function fill!(A::Union{Bidiagonal,Tridiagonal,SymTridiagonal}, x)
|
||||
xT = convert(eltype(A), x)
|
||||
(xT == zero(eltype(A)) || _small_enough(A)) && return fillslots!(A, xT)
|
||||
throw(ArgumentError("array A of type $(typeof(A)) and size $(size(A)) can
|
||||
not be filled with x=$x, since some of its entries are constrained."))
|
||||
end
|
||||
@@ -0,0 +1,298 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
function dot(x::BitVector, y::BitVector)
|
||||
# simplest way to mimic Array dot behavior
|
||||
length(x) == length(y) || throw(DimensionMismatch())
|
||||
s = 0
|
||||
xc = x.chunks
|
||||
yc = y.chunks
|
||||
@inbounds for i = 1:length(xc)
|
||||
s += count_ones(xc[i] & yc[i])
|
||||
end
|
||||
s
|
||||
end
|
||||
|
||||
## slower than the unpacked version, which is MUCH slower
|
||||
# than blas'd (this one saves storage though, keeping it commented
|
||||
# just in case)
|
||||
#function aTb(A::BitMatrix, B::BitMatrix)
|
||||
#(mA, nA) = size(A)
|
||||
#(mB, nB) = size(B)
|
||||
#C = falses(nA, nB)
|
||||
#if mA != mB; throw(DimensionMismatch()) end
|
||||
#if mA == 0; return C; end
|
||||
#col_ch = num_bit_chunks(mA)
|
||||
## TODO: avoid using aux chunks and copy (?)
|
||||
#aux_chunksA = zeros(UInt64, col_ch)
|
||||
#aux_chunksB = [zeros(UInt64, col_ch) for j=1:nB]
|
||||
#for j = 1:nB
|
||||
#Base.copy_chunks!(aux_chunksB[j], 1, B.chunks, (j-1)*mA+1, mA)
|
||||
#end
|
||||
#for i = 1:nA
|
||||
#Base.copy_chunks!(aux_chunksA, 1, A.chunks, (i-1)*mA+1, mA)
|
||||
#for j = 1:nB
|
||||
#for k = 1:col_ch
|
||||
## TODO: improve
|
||||
#C[i, j] += count_ones(aux_chunksA[k] & aux_chunksB[j][k])
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
#C
|
||||
#end
|
||||
|
||||
#aCb{T, S}(A::BitMatrix{T}, B::BitMatrix{S}) = aTb(A, B)
|
||||
|
||||
function triu(B::BitMatrix, k::Integer=0)
|
||||
m,n = size(B)
|
||||
A = falses(m,n)
|
||||
Ac = A.chunks
|
||||
Bc = B.chunks
|
||||
for i = max(k+1,1):n
|
||||
j = clamp((i - 1) * m + 1, 1, i * m)
|
||||
Base.copy_chunks!(Ac, j, Bc, j, min(i-k, m))
|
||||
end
|
||||
A
|
||||
end
|
||||
|
||||
function tril(B::BitMatrix, k::Integer=0)
|
||||
m,n = size(B)
|
||||
A = falses(m, n)
|
||||
Ac = A.chunks
|
||||
Bc = B.chunks
|
||||
for i = 1:min(n, m+k)
|
||||
j = clamp((i - 1) * m + i - k, 1, i * m)
|
||||
Base.copy_chunks!(Ac, j, Bc, j, max(m-i+k+1, 0))
|
||||
end
|
||||
A
|
||||
end
|
||||
|
||||
## diff and gradient
|
||||
|
||||
# TODO: this could be improved (is it worth it?)
|
||||
gradient(F::BitVector) = gradient(Array(F))
|
||||
gradient(F::BitVector, h::Real) = gradient(Array(F), h)
|
||||
gradient(F::Vector, h::BitVector) = gradient(F, Array(h))
|
||||
gradient(F::BitVector, h::Vector) = gradient(Array(F), h)
|
||||
gradient(F::BitVector, h::BitVector) = gradient(Array(F), Array(h))
|
||||
|
||||
## diag and related
|
||||
|
||||
function diag(B::BitMatrix)
|
||||
n = minimum(size(B))
|
||||
v = similar(B, n)
|
||||
for i = 1:n
|
||||
v[i] = B[i,i]
|
||||
end
|
||||
v
|
||||
end
|
||||
|
||||
function diagm(v::Union{BitVector,BitMatrix})
|
||||
isa(v, BitMatrix) && size(v,1)==1 || size(v,2)==1 || throw(DimensionMismatch())
|
||||
n = length(v)
|
||||
a = falses(n, n)
|
||||
for i=1:n
|
||||
a[i,i] = v[i]
|
||||
end
|
||||
a
|
||||
end
|
||||
|
||||
## norm and rank
|
||||
|
||||
svd(A::BitMatrix) = svd(float(A))
|
||||
qr(A::BitMatrix) = qr(float(A))
|
||||
|
||||
## kron
|
||||
|
||||
function kron(a::BitVector, b::BitVector)
|
||||
m = length(a)
|
||||
n = length(b)
|
||||
R = falses(n * m)
|
||||
Rc = R.chunks
|
||||
bc = b.chunks
|
||||
for j = 1:m
|
||||
a[j] && Base.copy_chunks!(Rc, (j-1)*n+1, bc, 1, n)
|
||||
end
|
||||
R
|
||||
end
|
||||
|
||||
function kron(a::BitMatrix, b::BitMatrix)
|
||||
mA,nA = size(a)
|
||||
mB,nB = size(b)
|
||||
R = falses(mA*mB, nA*nB)
|
||||
|
||||
for i = 1:mA
|
||||
ri = (1:mB)+(i-1)*mB
|
||||
for j = 1:nA
|
||||
if a[i,j]
|
||||
rj = (1:nB)+(j-1)*nB
|
||||
R[ri,rj] = b
|
||||
end
|
||||
end
|
||||
end
|
||||
R
|
||||
end
|
||||
|
||||
## Structure query functions
|
||||
|
||||
issymmetric(A::BitMatrix) = size(A, 1)==size(A, 2) && countnz(A - A.')==0
|
||||
ishermitian(A::BitMatrix) = issymmetric(A)
|
||||
|
||||
function nonzero_chunks(chunks::Vector{UInt64}, pos0::Int, pos1::Int)
|
||||
k0, l0 = Base.get_chunks_id(pos0)
|
||||
k1, l1 = Base.get_chunks_id(pos1)
|
||||
|
||||
delta_k = k1 - k0
|
||||
|
||||
z = UInt64(0)
|
||||
u = ~z
|
||||
if delta_k == 0
|
||||
msk_0 = (u << l0) & ~(u << l1 << 1)
|
||||
else
|
||||
msk_0 = (u << l0)
|
||||
msk_1 = ~(u << l1 << 1)
|
||||
end
|
||||
|
||||
@inbounds begin
|
||||
(chunks[k0] & msk_0) == z || return true
|
||||
delta_k == 0 && return false
|
||||
for i = k0 + 1 : k1 - 1
|
||||
chunks[i] == z || return true
|
||||
end
|
||||
(chunks[k1] & msk_1)==z || return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function istriu(A::BitMatrix)
|
||||
m, n = size(A)
|
||||
for j = 1:min(n,m-1)
|
||||
stride = (j-1) * m
|
||||
nonzero_chunks(A.chunks, stride+j+1, stride+m) && return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function istril(A::BitMatrix)
|
||||
m, n = size(A)
|
||||
(m == 0 || n == 0) && return true
|
||||
for j = 2:n
|
||||
stride = (j-1) * m
|
||||
nonzero_chunks(A.chunks, stride+1, stride+min(j-1,m)) && return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function findmax(a::BitArray)
|
||||
isempty(a) && throw(ArgumentError("BitArray must be non-empty"))
|
||||
m, mi = false, 1
|
||||
ti = 1
|
||||
ac = a.chunks
|
||||
for i = 1:length(ac)
|
||||
@inbounds k = trailing_zeros(ac[i])
|
||||
ti += k
|
||||
k == 64 || return (true, ti)
|
||||
end
|
||||
return m, mi
|
||||
end
|
||||
|
||||
function findmin(a::BitArray)
|
||||
isempty(a) && throw(ArgumentError("BitArray must be non-empty"))
|
||||
m, mi = true, 1
|
||||
ti = 1
|
||||
ac = a.chunks
|
||||
for i = 1:length(ac)-1
|
||||
@inbounds k = trailing_ones(ac[i])
|
||||
ti += k
|
||||
k == 64 || return (false, ti)
|
||||
end
|
||||
l = Base._mod64(length(a)-1) + 1
|
||||
@inbounds k = trailing_ones(ac[end] & Base._msk_end(l))
|
||||
ti += k
|
||||
k == l || return (false, ti)
|
||||
return m, mi
|
||||
end
|
||||
|
||||
# fast 8x8 bit transpose from Henry S. Warrens's "Hacker's Delight"
|
||||
# http://www.hackersdelight.org/hdcodetxt/transpose8.c.txt
|
||||
function transpose8x8(x::UInt64)
|
||||
y = x
|
||||
t = xor(y, y >>> 7) & 0x00aa00aa00aa00aa
|
||||
y = xor(y, t, t << 7)
|
||||
t = xor(y, y >>> 14) & 0x0000cccc0000cccc
|
||||
y = xor(y, t, t << 14)
|
||||
t = xor(y, y >>> 28) & 0x00000000f0f0f0f0
|
||||
return xor(y, t, t << 28)
|
||||
end
|
||||
|
||||
function form_8x8_chunk(Bc::Vector{UInt64}, i1::Int, i2::Int, m::Int, cgap::Int, cinc::Int, nc::Int, msk8::UInt64)
|
||||
x = UInt64(0)
|
||||
|
||||
k, l = Base.get_chunks_id(i1 + (i2 - 1) * m)
|
||||
r = 0
|
||||
for j = 1:8
|
||||
k > nc && break
|
||||
x |= ((Bc[k] >>> l) & msk8) << r
|
||||
if l + 8 >= 64 && nc > k
|
||||
r0 = 8 - Base._mod64(l + 8)
|
||||
x |= (Bc[k + 1] & (msk8 >>> r0)) << (r + r0)
|
||||
end
|
||||
k += cgap + (l + cinc >= 64 ? 1 : 0)
|
||||
l = Base._mod64(l + cinc)
|
||||
r += 8
|
||||
end
|
||||
return x
|
||||
end
|
||||
|
||||
# note: assumes B is filled with 0's
|
||||
function put_8x8_chunk(Bc::Vector{UInt64}, i1::Int, i2::Int, x::UInt64, m::Int, cgap::Int, cinc::Int, nc::Int, msk8::UInt64)
|
||||
k, l = Base.get_chunks_id(i1 + (i2 - 1) * m)
|
||||
r = 0
|
||||
for j = 1:8
|
||||
k > nc && break
|
||||
Bc[k] |= ((x >>> r) & msk8) << l
|
||||
if l + 8 >= 64 && nc > k
|
||||
r0 = 8 - Base._mod64(l + 8)
|
||||
Bc[k + 1] |= ((x >>> (r + r0)) & (msk8 >>> r0))
|
||||
end
|
||||
k += cgap + (l + cinc >= 64 ? 1 : 0)
|
||||
l = Base._mod64(l + cinc)
|
||||
r += 8
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
function transpose(B::BitMatrix)
|
||||
l1 = size(B, 1)
|
||||
l2 = size(B, 2)
|
||||
Bt = falses(l2, l1)
|
||||
|
||||
cgap1, cinc1 = Base._div64(l1), Base._mod64(l1)
|
||||
cgap2, cinc2 = Base._div64(l2), Base._mod64(l2)
|
||||
|
||||
Bc = B.chunks
|
||||
Btc = Bt.chunks
|
||||
|
||||
nc = length(Bc)
|
||||
|
||||
for i = 1:8:l1
|
||||
msk8_1 = UInt64(0xff)
|
||||
if (l1 < i + 7)
|
||||
msk8_1 >>>= i + 7 - l1
|
||||
end
|
||||
|
||||
for j = 1:8:l2
|
||||
x = form_8x8_chunk(Bc, i, j, l1, cgap1, cinc1, nc, msk8_1)
|
||||
x = transpose8x8(x)
|
||||
|
||||
msk8_2 = UInt64(0xff)
|
||||
if (l2 < j + 7)
|
||||
msk8_2 >>>= j + 7 - l2
|
||||
end
|
||||
|
||||
put_8x8_chunk(Btc, j, i, x, l2, cgap2, cinc2, nc, msk8_2)
|
||||
end
|
||||
end
|
||||
return Bt
|
||||
end
|
||||
|
||||
ctranspose(B::Union{BitMatrix,BitVector}) = transpose(B)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,199 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
## Create an extractor that extracts the modified original matrix, e.g.
|
||||
## LD for BunchKaufman, UL for CholeskyDense, LU for LUDense and
|
||||
## define size methods for Factorization types using it.
|
||||
|
||||
struct BunchKaufman{T,S<:AbstractMatrix} <: Factorization{T}
|
||||
LD::S
|
||||
ipiv::Vector{BlasInt}
|
||||
uplo::Char
|
||||
symmetric::Bool
|
||||
rook::Bool
|
||||
info::BlasInt
|
||||
end
|
||||
BunchKaufman{T}(A::AbstractMatrix{T}, ipiv::Vector{BlasInt}, uplo::Char, symmetric::Bool,
|
||||
rook::Bool, info::BlasInt) =
|
||||
BunchKaufman{T,typeof(A)}(A, ipiv, uplo, symmetric, rook, info)
|
||||
|
||||
"""
|
||||
bkfact!(A, uplo::Symbol=:U, symmetric::Bool=issymmetric(A), rook::Bool=false) -> BunchKaufman
|
||||
|
||||
`bkfact!` is the same as [`bkfact`](@ref), but saves space by overwriting the
|
||||
input `A`, instead of creating a copy.
|
||||
"""
|
||||
function bkfact!(A::StridedMatrix{<:BlasReal}, uplo::Symbol = :U,
|
||||
symmetric::Bool = issymmetric(A), rook::Bool = false)
|
||||
|
||||
if !symmetric
|
||||
throw(ArgumentError("Bunch-Kaufman decomposition is only valid for symmetric matrices"))
|
||||
end
|
||||
if rook
|
||||
LD, ipiv, info = LAPACK.sytrf_rook!(char_uplo(uplo), A)
|
||||
else
|
||||
LD, ipiv, info = LAPACK.sytrf!(char_uplo(uplo), A)
|
||||
end
|
||||
BunchKaufman(LD, ipiv, char_uplo(uplo), symmetric, rook, info)
|
||||
end
|
||||
function bkfact!(A::StridedMatrix{<:BlasComplex}, uplo::Symbol=:U,
|
||||
symmetric::Bool=issymmetric(A), rook::Bool=false)
|
||||
|
||||
if rook
|
||||
if symmetric
|
||||
LD, ipiv, info = LAPACK.sytrf_rook!(char_uplo(uplo), A)
|
||||
else
|
||||
LD, ipiv, info = LAPACK.hetrf_rook!(char_uplo(uplo), A)
|
||||
end
|
||||
else
|
||||
if symmetric
|
||||
LD, ipiv, info = LAPACK.sytrf!(char_uplo(uplo), A)
|
||||
else
|
||||
LD, ipiv, info = LAPACK.hetrf!(char_uplo(uplo), A)
|
||||
end
|
||||
end
|
||||
BunchKaufman(LD, ipiv, char_uplo(uplo), symmetric, rook, info)
|
||||
end
|
||||
|
||||
"""
|
||||
bkfact(A, uplo::Symbol=:U, symmetric::Bool=issymmetric(A), rook::Bool=false) -> BunchKaufman
|
||||
|
||||
Compute the Bunch-Kaufman [^Bunch1977] factorization of a symmetric or Hermitian
|
||||
matrix `A` and return a `BunchKaufman` object.
|
||||
`uplo` indicates which triangle of matrix `A` to reference.
|
||||
If `symmetric` is `true`, `A` is assumed to be symmetric. If `symmetric` is `false`,
|
||||
`A` is assumed to be Hermitian. If `rook` is `true`, rook pivoting is used. If
|
||||
`rook` is false, rook pivoting is not used.
|
||||
The following functions are available for
|
||||
`BunchKaufman` objects: [`size`](@ref), `\\`, [`inv`](@ref), [`issymmetric`](@ref), [`ishermitian`](@ref).
|
||||
|
||||
[^Bunch1977]: J R Bunch and L Kaufman, Some stable methods for calculating inertia and solving symmetric linear systems, Mathematics of Computation 31:137 (1977), 163-179. [url](http://www.ams.org/journals/mcom/1977-31-137/S0025-5718-1977-0428694-0/).
|
||||
|
||||
"""
|
||||
bkfact(A::StridedMatrix{<:BlasFloat}, uplo::Symbol=:U, symmetric::Bool=issymmetric(A),
|
||||
rook::Bool=false) =
|
||||
bkfact!(copy(A), uplo, symmetric, rook)
|
||||
bkfact(A::StridedMatrix{T}, uplo::Symbol=:U, symmetric::Bool=issymmetric(A),
|
||||
rook::Bool=false) where {T} =
|
||||
bkfact!(convert(Matrix{promote_type(Float32, typeof(sqrt(one(T))))}, A),
|
||||
uplo, symmetric, rook)
|
||||
|
||||
convert(::Type{BunchKaufman{T}}, B::BunchKaufman{T}) where {T} = B
|
||||
convert(::Type{BunchKaufman{T}}, B::BunchKaufman) where {T} =
|
||||
BunchKaufman(convert(Matrix{T}, B.LD), B.ipiv, B.uplo, B.symmetric, B.rook, B.info)
|
||||
convert(::Type{Factorization{T}}, B::BunchKaufman{T}) where {T} = B
|
||||
convert(::Type{Factorization{T}}, B::BunchKaufman) where {T} = convert(BunchKaufman{T}, B)
|
||||
|
||||
size(B::BunchKaufman) = size(B.LD)
|
||||
size(B::BunchKaufman, d::Integer) = size(B.LD, d)
|
||||
issymmetric(B::BunchKaufman) = B.symmetric
|
||||
ishermitian(B::BunchKaufman) = !B.symmetric
|
||||
|
||||
function inv(B::BunchKaufman{<:BlasReal})
|
||||
if B.info > 0
|
||||
throw(SingularException(B.info))
|
||||
end
|
||||
|
||||
if B.rook
|
||||
copytri!(LAPACK.sytri_rook!(B.uplo, copy(B.LD), B.ipiv), B.uplo, true)
|
||||
else
|
||||
copytri!(LAPACK.sytri!(B.uplo, copy(B.LD), B.ipiv), B.uplo, true)
|
||||
end
|
||||
end
|
||||
|
||||
function inv(B::BunchKaufman{<:BlasComplex})
|
||||
if B.info > 0
|
||||
throw(SingularException(B.info))
|
||||
end
|
||||
|
||||
if issymmetric(B)
|
||||
if B.rook
|
||||
copytri!(LAPACK.sytri_rook!(B.uplo, copy(B.LD), B.ipiv), B.uplo)
|
||||
else
|
||||
copytri!(LAPACK.sytri!(B.uplo, copy(B.LD), B.ipiv), B.uplo)
|
||||
end
|
||||
else
|
||||
if B.rook
|
||||
copytri!(LAPACK.hetri_rook!(B.uplo, copy(B.LD), B.ipiv), B.uplo, true)
|
||||
else
|
||||
copytri!(LAPACK.hetri!(B.uplo, copy(B.LD), B.ipiv), B.uplo, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function A_ldiv_B!(B::BunchKaufman{T}, R::StridedVecOrMat{T}) where T<:BlasReal
|
||||
if B.info > 0
|
||||
throw(SingularException(B.info))
|
||||
end
|
||||
|
||||
if B.rook
|
||||
LAPACK.sytrs_rook!(B.uplo, B.LD, B.ipiv, R)
|
||||
else
|
||||
LAPACK.sytrs!(B.uplo, B.LD, B.ipiv, R)
|
||||
end
|
||||
end
|
||||
function A_ldiv_B!(B::BunchKaufman{T}, R::StridedVecOrMat{T}) where T<:BlasComplex
|
||||
if B.info > 0
|
||||
throw(SingularException(B.info))
|
||||
end
|
||||
|
||||
if B.rook
|
||||
if issymmetric(B)
|
||||
LAPACK.sytrs_rook!(B.uplo, B.LD, B.ipiv, R)
|
||||
else
|
||||
LAPACK.hetrs_rook!(B.uplo, B.LD, B.ipiv, R)
|
||||
end
|
||||
else
|
||||
if issymmetric(B)
|
||||
LAPACK.sytrs!(B.uplo, B.LD, B.ipiv, R)
|
||||
else
|
||||
LAPACK.hetrs!(B.uplo, B.LD, B.ipiv, R)
|
||||
end
|
||||
end
|
||||
end
|
||||
# There is no fallback solver for Bunch-Kaufman so we'll have to promote to same element type
|
||||
function A_ldiv_B!(B::BunchKaufman{T}, R::StridedVecOrMat{S}) where {T,S}
|
||||
TS = promote_type(T,S)
|
||||
return A_ldiv_B!(convert(BunchKaufman{TS}, B), convert(AbstractArray{TS}, R))
|
||||
end
|
||||
|
||||
function logabsdet(F::BunchKaufman)
|
||||
M = F.LD
|
||||
p = F.ipiv
|
||||
n = size(F.LD, 1)
|
||||
|
||||
if F.info > 0
|
||||
return eltype(F)(-Inf), zero(eltype(F))
|
||||
end
|
||||
s = one(real(eltype(F)))
|
||||
i = 1
|
||||
abs_det = zero(real(eltype(F)))
|
||||
while i <= n
|
||||
if p[i] > 0
|
||||
elm = M[i,i]
|
||||
s *= sign(elm)
|
||||
abs_det += log(abs(elm))
|
||||
i += 1
|
||||
else
|
||||
# 2x2 pivot case. Make sure not to square before the subtraction by scaling
|
||||
# with the off-diagonal element. This is safe because the off diagonal is
|
||||
# always large for 2x2 pivots.
|
||||
if F.uplo == 'U'
|
||||
elm = M[i, i + 1]*(M[i,i]/M[i, i + 1]*M[i + 1, i + 1] -
|
||||
(issymmetric(F) ? M[i, i + 1] : conj(M[i, i + 1])))
|
||||
s *= sign(elm)
|
||||
abs_det += log(abs(elm))
|
||||
else
|
||||
elm = M[i + 1,i]*(M[i, i]/M[i + 1, i]*M[i + 1, i + 1] -
|
||||
(issymmetric(F) ? M[i + 1, i] : conj(M[i + 1, i])))
|
||||
s *= sign(elm)
|
||||
abs_det += log(abs(elm))
|
||||
end
|
||||
i += 2
|
||||
end
|
||||
end
|
||||
return abs_det, s
|
||||
end
|
||||
|
||||
## reconstruct the original matrix
|
||||
## TODO: understand the procedure described at
|
||||
## http://www.nag.com/numeric/FL/nagdoc_fl22/pdf/F07/f07mdf.pdf
|
||||
@@ -0,0 +1,669 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
##########################
|
||||
# Cholesky Factorization #
|
||||
##########################
|
||||
|
||||
# The dispatch structure in the chol!, chol, cholfact, and cholfact! methods is a bit
|
||||
# complicated and some explanation is therefore provided in the following
|
||||
#
|
||||
# In the methods below, LAPACK is called when possible, i.e. StridedMatrices with Float32,
|
||||
# Float64, Complex{Float32}, and Complex{Float64} element types. For other element or
|
||||
# matrix types, the unblocked Julia implementation in _chol! is used. For cholfact
|
||||
# and cholfact! pivoting is supported through a Val{Bool} argument. A type argument is
|
||||
# necessary for type stability since the output of cholfact and cholfact! is either
|
||||
# Cholesky or PivotedCholesky. The latter is only
|
||||
# supported for the four LAPACK element types. For other types, e.g. BigFloats Val{true} will
|
||||
# give an error. It is required that the input is Hermitian (including real symmetric) either
|
||||
# through the Hermitian and Symmetric views or exact symmetric or Hermitian elements which
|
||||
# is checked for and an error is thrown if the check fails. The dispatch
|
||||
# is further complicated by a limitation in the formulation of Unions. The relevant union
|
||||
# would be Union{Symmetric{T<:Real,S}, Hermitian} but, right now, it doesn't work in Julia
|
||||
# so we'll have to define methods for the two elements of the union separately.
|
||||
|
||||
# FixMe? The dispatch below seems overly complicated. One simplification could be to
|
||||
# merge the two Cholesky types into one. It would remove the need for Val completely but
|
||||
# the cost would be extra unnecessary/unused fields for the unpivoted Cholesky and runtime
|
||||
# checks of those fields before calls to LAPACK to check which version of the Cholesky
|
||||
# factorization the type represents.
|
||||
|
||||
struct Cholesky{T,S<:AbstractMatrix} <: Factorization{T}
|
||||
factors::S
|
||||
uplo::Char
|
||||
end
|
||||
Cholesky{T}(A::AbstractMatrix{T}, uplo::Symbol) = Cholesky{T,typeof(A)}(A, char_uplo(uplo))
|
||||
Cholesky{T}(A::AbstractMatrix{T}, uplo::Char) = Cholesky{T,typeof(A)}(A, uplo)
|
||||
|
||||
struct CholeskyPivoted{T,S<:AbstractMatrix} <: Factorization{T}
|
||||
factors::S
|
||||
uplo::Char
|
||||
piv::Vector{BlasInt}
|
||||
rank::BlasInt
|
||||
tol::Real
|
||||
info::BlasInt
|
||||
end
|
||||
function CholeskyPivoted{T}(A::AbstractMatrix{T}, uplo::Char, piv::Vector{BlasInt},
|
||||
rank::BlasInt, tol::Real, info::BlasInt)
|
||||
CholeskyPivoted{T,typeof(A)}(A, uplo, piv, rank, tol, info)
|
||||
end
|
||||
|
||||
|
||||
# _chol!. Internal methods for calling unpivoted Cholesky
|
||||
## BLAS/LAPACK element types
|
||||
function _chol!(A::StridedMatrix{<:BlasFloat}, ::Type{UpperTriangular})
|
||||
C, info = LAPACK.potrf!('U', A)
|
||||
return @assertposdef UpperTriangular(C) info
|
||||
end
|
||||
function _chol!(A::StridedMatrix{<:BlasFloat}, ::Type{LowerTriangular})
|
||||
C, info = LAPACK.potrf!('L', A)
|
||||
return @assertposdef LowerTriangular(C) info
|
||||
end
|
||||
|
||||
## Non BLAS/LAPACK element types (generic)
|
||||
function _chol!(A::AbstractMatrix, ::Type{UpperTriangular})
|
||||
n = checksquare(A)
|
||||
@inbounds begin
|
||||
for k = 1:n
|
||||
for i = 1:k - 1
|
||||
A[k,k] -= A[i,k]'A[i,k]
|
||||
end
|
||||
Akk = _chol!(A[k,k], UpperTriangular)
|
||||
A[k,k] = Akk
|
||||
AkkInv = inv(Akk')
|
||||
for j = k + 1:n
|
||||
for i = 1:k - 1
|
||||
A[k,j] -= A[i,k]'A[i,j]
|
||||
end
|
||||
A[k,j] = AkkInv*A[k,j]
|
||||
end
|
||||
end
|
||||
end
|
||||
return UpperTriangular(A)
|
||||
end
|
||||
function _chol!(A::AbstractMatrix, ::Type{LowerTriangular})
|
||||
n = checksquare(A)
|
||||
@inbounds begin
|
||||
for k = 1:n
|
||||
for i = 1:k - 1
|
||||
A[k,k] -= A[k,i]*A[k,i]'
|
||||
end
|
||||
Akk = _chol!(A[k,k], LowerTriangular)
|
||||
A[k,k] = Akk
|
||||
AkkInv = inv(Akk)
|
||||
for j = 1:k
|
||||
for i = k + 1:n
|
||||
if j == 1
|
||||
A[i,k] = A[i,k]*AkkInv'
|
||||
end
|
||||
if j < k
|
||||
A[i,k] -= A[i,j]*A[k,j]'*AkkInv'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return LowerTriangular(A)
|
||||
end
|
||||
|
||||
## Numbers
|
||||
function _chol!(x::Number, uplo)
|
||||
rx = real(x)
|
||||
if rx != abs(x)
|
||||
throw(ArgumentError("x must be positive semidefinite"))
|
||||
end
|
||||
rxr = sqrt(rx)
|
||||
convert(promote_type(typeof(x), typeof(rxr)), rxr)
|
||||
end
|
||||
|
||||
non_hermitian_error(f) = throw(ArgumentError("matrix is not symmetric/" *
|
||||
"Hermitian. This error can be avoided by calling $f(Hermitian(A)) " *
|
||||
"which will ignore either the upper or lower triangle of the matrix."))
|
||||
|
||||
# chol!. Destructive methods for computing Cholesky factor of real symmetric or Hermitian
|
||||
# matrix
|
||||
chol!(A::Hermitian) =
|
||||
_chol!(A.uplo == 'U' ? A.data : LinAlg.copytri!(A.data, 'L', true), UpperTriangular)
|
||||
chol!(A::Symmetric{<:Real,<:StridedMatrix}) =
|
||||
_chol!(A.uplo == 'U' ? A.data : LinAlg.copytri!(A.data, 'L', true), UpperTriangular)
|
||||
function chol!(A::StridedMatrix)
|
||||
ishermitian(A) || non_hermitian_error("chol!")
|
||||
return _chol!(A, UpperTriangular)
|
||||
end
|
||||
|
||||
|
||||
|
||||
# chol. Non-destructive methods for computing Cholesky factor of a real symmetric or
|
||||
# Hermitian matrix. Promotes elements to a type that is stable under square roots.
|
||||
function chol(A::Hermitian)
|
||||
T = promote_type(typeof(chol(one(eltype(A)))), Float32)
|
||||
AA = similar(A, T, size(A))
|
||||
if A.uplo == 'U'
|
||||
copy!(AA, A.data)
|
||||
else
|
||||
Base.ctranspose!(AA, A.data)
|
||||
end
|
||||
chol!(Hermitian(AA, :U))
|
||||
end
|
||||
function chol(A::Symmetric{T,<:AbstractMatrix}) where T<:Real
|
||||
TT = promote_type(typeof(chol(one(T))), Float32)
|
||||
AA = similar(A, TT, size(A))
|
||||
if A.uplo == 'U'
|
||||
copy!(AA, A.data)
|
||||
else
|
||||
Base.ctranspose!(AA, A.data)
|
||||
end
|
||||
chol!(Hermitian(AA, :U))
|
||||
end
|
||||
|
||||
## for StridedMatrices, check that matrix is symmetric/Hermitian
|
||||
"""
|
||||
chol(A) -> U
|
||||
|
||||
Compute the Cholesky factorization of a positive definite matrix `A`
|
||||
and return the [`UpperTriangular`](@ref) matrix `U` such that `A = U'U`.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1. 2.; 2. 50.]
|
||||
2×2 Array{Float64,2}:
|
||||
1.0 2.0
|
||||
2.0 50.0
|
||||
|
||||
julia> U = chol(A)
|
||||
2×2 UpperTriangular{Float64,Array{Float64,2}}:
|
||||
1.0 2.0
|
||||
⋅ 6.78233
|
||||
|
||||
julia> U'U
|
||||
2×2 Array{Float64,2}:
|
||||
1.0 2.0
|
||||
2.0 50.0
|
||||
```
|
||||
"""
|
||||
function chol(A::AbstractMatrix)
|
||||
ishermitian(A) || non_hermitian_error("chol")
|
||||
return chol(Hermitian(A))
|
||||
end
|
||||
|
||||
## Numbers
|
||||
"""
|
||||
chol(x::Number) -> y
|
||||
|
||||
Compute the square root of a non-negative number `x`.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> chol(16)
|
||||
4.0
|
||||
```
|
||||
"""
|
||||
chol(x::Number, args...) = _chol!(x, nothing)
|
||||
|
||||
|
||||
|
||||
# cholfact!. Destructive methods for computing Cholesky factorization of real symmetric
|
||||
# or Hermitian matrix
|
||||
## No pivoting
|
||||
function cholfact!(A::Hermitian, ::Type{Val{false}})
|
||||
if A.uplo == 'U'
|
||||
Cholesky(_chol!(A.data, UpperTriangular).data, 'U')
|
||||
else
|
||||
Cholesky(_chol!(A.data, LowerTriangular).data, 'L')
|
||||
end
|
||||
end
|
||||
function cholfact!(A::Symmetric{<:Real}, ::Type{Val{false}})
|
||||
if A.uplo == 'U'
|
||||
Cholesky(_chol!(A.data, UpperTriangular).data, 'U')
|
||||
else
|
||||
Cholesky(_chol!(A.data, LowerTriangular).data, 'L')
|
||||
end
|
||||
end
|
||||
|
||||
### for StridedMatrices, check that matrix is symmetric/Hermitian
|
||||
"""
|
||||
cholfact!(A, [uplo::Symbol,] Val{false}) -> Cholesky
|
||||
|
||||
The same as [`cholfact`](@ref), but saves space by overwriting the input `A`,
|
||||
instead of creating a copy. An [`InexactError`](@ref) exception is thrown if
|
||||
the factorization produces a number not representable by the element type of
|
||||
`A`, e.g. for integer types.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1 2; 2 50]
|
||||
2×2 Array{Int64,2}:
|
||||
1 2
|
||||
2 50
|
||||
|
||||
julia> cholfact!(A)
|
||||
ERROR: InexactError()
|
||||
```
|
||||
"""
|
||||
function cholfact!(A::StridedMatrix, uplo::Symbol, ::Type{Val{false}})
|
||||
ishermitian(A) || non_hermitian_error("cholfact!")
|
||||
return cholfact!(Hermitian(A, uplo), Val{false})
|
||||
end
|
||||
|
||||
### Default to no pivoting (and storing of upper factor) when not explicit
|
||||
cholfact!(A::Hermitian) = cholfact!(A, Val{false})
|
||||
cholfact!(A::Symmetric{<:Real}) = cholfact!(A, Val{false})
|
||||
#### for StridedMatrices, check that matrix is symmetric/Hermitian
|
||||
function cholfact!(A::StridedMatrix, uplo::Symbol = :U)
|
||||
ishermitian(A) || non_hermitian_error("cholfact!")
|
||||
return cholfact!(Hermitian(A, uplo))
|
||||
end
|
||||
|
||||
|
||||
## With pivoting
|
||||
### BLAS/LAPACK element types
|
||||
function cholfact!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix},
|
||||
::Type{Val{true}}; tol = 0.0)
|
||||
AA, piv, rank, info = LAPACK.pstrf!(A.uplo, A.data, tol)
|
||||
return CholeskyPivoted{eltype(AA),typeof(AA)}(AA, A.uplo, piv, rank, tol, info)
|
||||
end
|
||||
|
||||
### Non BLAS/LAPACK element types (generic). Since generic fallback for pivoted Cholesky
|
||||
### is not implemented yet we throw an error
|
||||
cholfact!(A::RealHermSymComplexHerm{<:Real}, ::Type{Val{true}};
|
||||
tol = 0.0) =
|
||||
throw(ArgumentError("generic pivoted Cholesky factorization is not implemented yet"))
|
||||
|
||||
### for StridedMatrices, check that matrix is symmetric/Hermitian
|
||||
"""
|
||||
cholfact!(A, [uplo::Symbol,] Val{true}; tol = 0.0) -> CholeskyPivoted
|
||||
|
||||
The same as [`cholfact`](@ref), but saves space by overwriting the input `A`,
|
||||
instead of creating a copy. An [`InexactError`](@ref) exception is thrown if the
|
||||
factorization produces a number not representable by the element type of `A`,
|
||||
e.g. for integer types.
|
||||
"""
|
||||
function cholfact!(A::StridedMatrix, uplo::Symbol, ::Type{Val{true}}; tol = 0.0)
|
||||
ishermitian(A) || non_hermitian_error("cholfact!")
|
||||
return cholfact!(Hermitian(A, uplo), Val{true}; tol = tol)
|
||||
end
|
||||
|
||||
# cholfact. Non-destructive methods for computing Cholesky factorization of real symmetric
|
||||
# or Hermitian matrix
|
||||
## No pivoting
|
||||
cholfact(A::Hermitian, ::Type{Val{false}}) =
|
||||
cholfact!(copy_oftype(A, promote_type(typeof(chol(one(eltype(A)))),Float32)), Val{false})
|
||||
cholfact(A::Symmetric{<:Real,<:StridedMatrix}, ::Type{Val{false}}) =
|
||||
cholfact!(copy_oftype(A, promote_type(typeof(chol(one(eltype(A)))),Float32)), Val{false})
|
||||
|
||||
### for StridedMatrices, check that matrix is symmetric/Hermitian
|
||||
"""
|
||||
cholfact(A, [uplo::Symbol,] Val{false}) -> Cholesky
|
||||
|
||||
Compute the Cholesky factorization of a dense symmetric positive definite matrix `A`
|
||||
and return a `Cholesky` factorization. The matrix `A` can either be a [`Symmetric`](@ref) or [`Hermitian`](@ref)
|
||||
`StridedMatrix` or a *perfectly* symmetric or Hermitian `StridedMatrix`. In the latter case,
|
||||
the optional argument `uplo` may be `:L` for using the lower part or `:U` for the upper part of `A`.
|
||||
The default is to use `:U`.
|
||||
The triangular Cholesky factor can be obtained from the factorization `F` with: `F[:L]` and `F[:U]`.
|
||||
The following functions are available for `Cholesky` objects: [`size`](@ref), [`\\`](@ref),
|
||||
[`inv`](@ref), and [`det`](@ref).
|
||||
A `PosDefException` exception is thrown in case the matrix is not positive definite.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [4. 12. -16.; 12. 37. -43.; -16. -43. 98.]
|
||||
3×3 Array{Float64,2}:
|
||||
4.0 12.0 -16.0
|
||||
12.0 37.0 -43.0
|
||||
-16.0 -43.0 98.0
|
||||
|
||||
julia> C = cholfact(A)
|
||||
Base.LinAlg.Cholesky{Float64,Array{Float64,2}} with factor:
|
||||
[2.0 6.0 -8.0; 0.0 1.0 5.0; 0.0 0.0 3.0]
|
||||
|
||||
julia> C[:U]
|
||||
3×3 UpperTriangular{Float64,Array{Float64,2}}:
|
||||
2.0 6.0 -8.0
|
||||
⋅ 1.0 5.0
|
||||
⋅ ⋅ 3.0
|
||||
|
||||
julia> C[:L]
|
||||
3×3 LowerTriangular{Float64,Array{Float64,2}}:
|
||||
2.0 ⋅ ⋅
|
||||
6.0 1.0 ⋅
|
||||
-8.0 5.0 3.0
|
||||
|
||||
julia> C[:L] * C[:U] == A
|
||||
true
|
||||
```
|
||||
"""
|
||||
function cholfact(A::StridedMatrix, uplo::Symbol, ::Type{Val{false}})
|
||||
ishermitian(A) || non_hermitian_error("cholfact")
|
||||
return cholfact(Hermitian(A, uplo), Val{false})
|
||||
end
|
||||
|
||||
### Default to no pivoting (and storing of upper factor) when not explicit
|
||||
cholfact(A::Hermitian) = cholfact(A, Val{false})
|
||||
cholfact(A::Symmetric{<:Real,<:StridedMatrix}) = cholfact(A, Val{false})
|
||||
#### for StridedMatrices, check that matrix is symmetric/Hermitian
|
||||
function cholfact(A::StridedMatrix, uplo::Symbol = :U)
|
||||
ishermitian(A) || non_hermitian_error("cholfact")
|
||||
return cholfact(Hermitian(A, uplo))
|
||||
end
|
||||
|
||||
|
||||
## With pivoting
|
||||
cholfact(A::Hermitian, ::Type{Val{true}}; tol = 0.0) =
|
||||
cholfact!(copy_oftype(A, promote_type(typeof(chol(one(eltype(A)))),Float32)),
|
||||
Val{true}; tol = tol)
|
||||
cholfact(A::RealHermSymComplexHerm{<:Real,<:StridedMatrix}, ::Type{Val{true}}; tol = 0.0) =
|
||||
cholfact!(copy_oftype(A, promote_type(typeof(chol(one(eltype(A)))),Float32)),
|
||||
Val{true}; tol = tol)
|
||||
|
||||
### for StridedMatrices, check that matrix is symmetric/Hermitian
|
||||
"""
|
||||
cholfact(A, [uplo::Symbol,] Val{true}; tol = 0.0) -> CholeskyPivoted
|
||||
|
||||
Compute the pivoted Cholesky factorization of a dense symmetric positive semi-definite matrix `A`
|
||||
and return a `CholeskyPivoted` factorization. The matrix `A` can either be a [`Symmetric`](@ref)
|
||||
or [`Hermitian`](@ref) `StridedMatrix` or a *perfectly* symmetric or Hermitian `StridedMatrix`.
|
||||
In the latter case, the optional argument `uplo` may be `:L` for using the lower part or `:U`
|
||||
for the upper part of `A`. The default is to use `:U`.
|
||||
The triangular Cholesky factor can be obtained from the factorization `F` with: `F[:L]` and `F[:U]`.
|
||||
The following functions are available for `PivotedCholesky` objects:
|
||||
[`size`](@ref), [`\\`](@ref), [`inv`](@ref), [`det`](@ref), and [`rank`](@ref).
|
||||
The argument `tol` determines the tolerance for determining the rank.
|
||||
For negative values, the tolerance is the machine precision.
|
||||
"""
|
||||
function cholfact(A::StridedMatrix, uplo::Symbol, ::Type{Val{true}}; tol = 0.0)
|
||||
ishermitian(A) || non_hermitian_error("cholfact")
|
||||
return cholfact(Hermitian(A, uplo), Val{true}; tol = tol)
|
||||
end
|
||||
|
||||
## Number
|
||||
function cholfact(x::Number, uplo::Symbol=:U)
|
||||
xf = fill(chol(x), 1, 1)
|
||||
Cholesky(xf, uplo)
|
||||
end
|
||||
|
||||
|
||||
function convert(::Type{Cholesky{T}}, C::Cholesky) where T
|
||||
Cnew = convert(AbstractMatrix{T}, C.factors)
|
||||
Cholesky{T, typeof(Cnew)}(Cnew, C.uplo)
|
||||
end
|
||||
convert(::Type{Factorization{T}}, C::Cholesky{T}) where {T} = C
|
||||
convert(::Type{Factorization{T}}, C::Cholesky) where {T} = convert(Cholesky{T}, C)
|
||||
convert(::Type{CholeskyPivoted{T}},C::CholeskyPivoted{T}) where {T} = C
|
||||
convert(::Type{CholeskyPivoted{T}},C::CholeskyPivoted) where {T} =
|
||||
CholeskyPivoted(AbstractMatrix{T}(C.factors),C.uplo,C.piv,C.rank,C.tol,C.info)
|
||||
convert(::Type{Factorization{T}}, C::CholeskyPivoted{T}) where {T} = C
|
||||
convert(::Type{Factorization{T}}, C::CholeskyPivoted) where {T} = convert(CholeskyPivoted{T}, C)
|
||||
|
||||
convert(::Type{AbstractMatrix}, C::Cholesky) = C.uplo == 'U' ? C[:U]'C[:U] : C[:L]*C[:L]'
|
||||
convert(::Type{AbstractArray}, C::Cholesky) = convert(AbstractMatrix, C)
|
||||
convert(::Type{Matrix}, C::Cholesky) = convert(Array, convert(AbstractArray, C))
|
||||
convert(::Type{Array}, C::Cholesky) = convert(Matrix, C)
|
||||
full(C::Cholesky) = convert(AbstractArray, C)
|
||||
|
||||
function convert(::Type{AbstractMatrix}, F::CholeskyPivoted)
|
||||
ip = invperm(F[:p])
|
||||
(F[:L] * F[:U])[ip,ip]
|
||||
end
|
||||
convert(::Type{AbstractArray}, F::CholeskyPivoted) = convert(AbstractMatrix, F)
|
||||
convert(::Type{Matrix}, F::CholeskyPivoted) = convert(Array, convert(AbstractArray, F))
|
||||
convert(::Type{Array}, F::CholeskyPivoted) = convert(Matrix, F)
|
||||
full(F::CholeskyPivoted) = convert(AbstractArray, F)
|
||||
|
||||
copy(C::Cholesky) = Cholesky(copy(C.factors), C.uplo)
|
||||
copy(C::CholeskyPivoted) = CholeskyPivoted(copy(C.factors), C.uplo, C.piv, C.rank, C.tol, C.info)
|
||||
|
||||
size(C::Union{Cholesky, CholeskyPivoted}) = size(C.factors)
|
||||
size(C::Union{Cholesky, CholeskyPivoted}, d::Integer) = size(C.factors, d)
|
||||
|
||||
function getindex(C::Cholesky, d::Symbol)
|
||||
d == :U && return UpperTriangular(Symbol(C.uplo) == d ? C.factors : C.factors')
|
||||
d == :L && return LowerTriangular(Symbol(C.uplo) == d ? C.factors : C.factors')
|
||||
d == :UL && return Symbol(C.uplo) == :U ? UpperTriangular(C.factors) : LowerTriangular(C.factors)
|
||||
throw(KeyError(d))
|
||||
end
|
||||
function getindex(C::CholeskyPivoted{T}, d::Symbol) where T<:BlasFloat
|
||||
d == :U && return UpperTriangular(Symbol(C.uplo) == d ? C.factors : C.factors')
|
||||
d == :L && return LowerTriangular(Symbol(C.uplo) == d ? C.factors : C.factors')
|
||||
d == :p && return C.piv
|
||||
if d == :P
|
||||
n = size(C, 1)
|
||||
P = zeros(T, n, n)
|
||||
for i = 1:n
|
||||
P[C.piv[i],i] = one(T)
|
||||
end
|
||||
return P
|
||||
end
|
||||
throw(KeyError(d))
|
||||
end
|
||||
|
||||
show(io::IO, C::Cholesky{<:Any,<:AbstractMatrix}) =
|
||||
(println(io, "$(typeof(C)) with factor:");show(io,C[:UL]))
|
||||
|
||||
A_ldiv_B!(C::Cholesky{T,<:AbstractMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} =
|
||||
LAPACK.potrs!(C.uplo, C.factors, B)
|
||||
|
||||
function A_ldiv_B!(C::Cholesky{<:Any,<:AbstractMatrix}, B::StridedVecOrMat)
|
||||
if C.uplo == 'L'
|
||||
return Ac_ldiv_B!(LowerTriangular(C.factors), A_ldiv_B!(LowerTriangular(C.factors), B))
|
||||
else
|
||||
return A_ldiv_B!(UpperTriangular(C.factors), Ac_ldiv_B!(UpperTriangular(C.factors), B))
|
||||
end
|
||||
end
|
||||
|
||||
function A_ldiv_B!(C::CholeskyPivoted{T}, B::StridedVector{T}) where T<:BlasFloat
|
||||
chkfullrank(C)
|
||||
ipermute!(LAPACK.potrs!(C.uplo, C.factors, permute!(B, C.piv)), C.piv)
|
||||
end
|
||||
function A_ldiv_B!(C::CholeskyPivoted{T}, B::StridedMatrix{T}) where T<:BlasFloat
|
||||
chkfullrank(C)
|
||||
n = size(C, 1)
|
||||
for i=1:size(B, 2)
|
||||
permute!(view(B, 1:n, i), C.piv)
|
||||
end
|
||||
LAPACK.potrs!(C.uplo, C.factors, B)
|
||||
for i=1:size(B, 2)
|
||||
ipermute!(view(B, 1:n, i), C.piv)
|
||||
end
|
||||
B
|
||||
end
|
||||
|
||||
function A_ldiv_B!(C::CholeskyPivoted, B::StridedVector)
|
||||
if C.uplo == 'L'
|
||||
Ac_ldiv_B!(LowerTriangular(C.factors),
|
||||
A_ldiv_B!(LowerTriangular(C.factors), B[C.piv]))[invperm(C.piv)]
|
||||
else
|
||||
A_ldiv_B!(UpperTriangular(C.factors),
|
||||
Ac_ldiv_B!(UpperTriangular(C.factors), B[C.piv]))[invperm(C.piv)]
|
||||
end
|
||||
end
|
||||
|
||||
function A_ldiv_B!(C::CholeskyPivoted, B::StridedMatrix)
|
||||
if C.uplo == 'L'
|
||||
Ac_ldiv_B!(LowerTriangular(C.factors),
|
||||
A_ldiv_B!(LowerTriangular(C.factors), B[C.piv,:]))[invperm(C.piv),:]
|
||||
else
|
||||
A_ldiv_B!(UpperTriangular(C.factors),
|
||||
Ac_ldiv_B!(UpperTriangular(C.factors), B[C.piv,:]))[invperm(C.piv),:]
|
||||
end
|
||||
end
|
||||
|
||||
function det(C::Cholesky)
|
||||
dd = one(real(eltype(C)))
|
||||
for i in 1:size(C.factors,1)
|
||||
dd *= real(C.factors[i,i])^2
|
||||
end
|
||||
dd
|
||||
end
|
||||
|
||||
function logdet(C::Cholesky)
|
||||
dd = zero(real(eltype(C)))
|
||||
for i in 1:size(C.factors,1)
|
||||
dd += log(real(C.factors[i,i]))
|
||||
end
|
||||
dd + dd # instead of 2.0dd which can change the type
|
||||
end
|
||||
|
||||
function det(C::CholeskyPivoted)
|
||||
if C.rank < size(C.factors, 1)
|
||||
return zero(real(eltype(C)))
|
||||
else
|
||||
dd = one(real(eltype(C)))
|
||||
for i in 1:size(C.factors,1)
|
||||
dd *= real(C.factors[i,i])^2
|
||||
end
|
||||
return dd
|
||||
end
|
||||
end
|
||||
|
||||
function logdet(C::CholeskyPivoted)
|
||||
if C.rank < size(C.factors, 1)
|
||||
return real(eltype(C))(-Inf)
|
||||
else
|
||||
dd = zero(real(eltype(C)))
|
||||
for i in 1:size(C.factors,1)
|
||||
dd += log(real(C.factors[i,i]))
|
||||
end
|
||||
return dd + dd # instead of 2.0dd which can change the type
|
||||
end
|
||||
end
|
||||
|
||||
inv!(C::Cholesky{<:BlasFloat,<:StridedMatrix}) =
|
||||
copytri!(LAPACK.potri!(C.uplo, C.factors), C.uplo, true)
|
||||
|
||||
inv(C::Cholesky{<:BlasFloat,<:StridedMatrix}) =
|
||||
inv!(copy(C))
|
||||
|
||||
function inv(C::CholeskyPivoted)
|
||||
chkfullrank(C)
|
||||
ipiv = invperm(C.piv)
|
||||
copytri!(LAPACK.potri!(C.uplo, copy(C.factors)), C.uplo, true)[ipiv, ipiv]
|
||||
end
|
||||
|
||||
function chkfullrank(C::CholeskyPivoted)
|
||||
if C.rank < size(C.factors, 1)
|
||||
throw(RankDeficientException(C.info))
|
||||
end
|
||||
end
|
||||
|
||||
rank(C::CholeskyPivoted) = C.rank
|
||||
|
||||
"""
|
||||
lowrankupdate!(C::Cholesky, v::StridedVector) -> CC::Cholesky
|
||||
|
||||
Update a Cholesky factorization `C` with the vector `v`. If `A = C[:U]'C[:U]` then
|
||||
`CC = cholfact(C[:U]'C[:U] + v*v')` but the computation of `CC` only uses `O(n^2)`
|
||||
operations. The input factorization `C` is updated in place such that on exit `C == CC`.
|
||||
The vector `v` is destroyed during the computation.
|
||||
"""
|
||||
function lowrankupdate!(C::Cholesky, v::StridedVector)
|
||||
A = C.factors
|
||||
n = length(v)
|
||||
if size(C, 1) != n
|
||||
throw(DimensionMismatch("updating vector must fit size of factorization"))
|
||||
end
|
||||
if C.uplo == 'U'
|
||||
conj!(v)
|
||||
end
|
||||
|
||||
for i = 1:n
|
||||
|
||||
# Compute Givens rotation
|
||||
c, s, r = givensAlgorithm(A[i,i], v[i])
|
||||
|
||||
# Store new diagonal element
|
||||
A[i,i] = r
|
||||
|
||||
# Update remaining elements in row/column
|
||||
if C.uplo == 'U'
|
||||
for j = i + 1:n
|
||||
Aij = A[i,j]
|
||||
vj = v[j]
|
||||
A[i,j] = c*Aij + s*vj
|
||||
v[j] = -s'*Aij + c*vj
|
||||
end
|
||||
else
|
||||
for j = i + 1:n
|
||||
Aji = A[j,i]
|
||||
vj = v[j]
|
||||
A[j,i] = c*Aji + s*vj
|
||||
v[j] = -s'*Aji + c*vj
|
||||
end
|
||||
end
|
||||
end
|
||||
return C
|
||||
end
|
||||
|
||||
"""
|
||||
lowrankdowndate!(C::Cholesky, v::StridedVector) -> CC::Cholesky
|
||||
|
||||
Downdate a Cholesky factorization `C` with the vector `v`. If `A = C[:U]'C[:U]` then
|
||||
`CC = cholfact(C[:U]'C[:U] - v*v')` but the computation of `CC` only uses `O(n^2)`
|
||||
operations. The input factorization `C` is updated in place such that on exit `C == CC`.
|
||||
The vector `v` is destroyed during the computation.
|
||||
"""
|
||||
function lowrankdowndate!(C::Cholesky, v::StridedVector)
|
||||
A = C.factors
|
||||
n = length(v)
|
||||
if size(C, 1) != n
|
||||
throw(DimensionMismatch("updating vector must fit size of factorization"))
|
||||
end
|
||||
if C.uplo == 'U'
|
||||
conj!(v)
|
||||
end
|
||||
|
||||
for i = 1:n
|
||||
|
||||
Aii = A[i,i]
|
||||
|
||||
# Compute Givens rotation
|
||||
s = conj(v[i]/Aii)
|
||||
s2 = abs2(s)
|
||||
if s2 > 1
|
||||
throw(LinAlg.PosDefException(i))
|
||||
end
|
||||
c = sqrt(1 - abs2(s))
|
||||
|
||||
# Store new diagonal element
|
||||
A[i,i] = c*Aii
|
||||
|
||||
# Update remaining elements in row/column
|
||||
if C.uplo == 'U'
|
||||
for j = i + 1:n
|
||||
vj = v[j]
|
||||
Aij = (A[i,j] - s*vj)/c
|
||||
A[i,j] = Aij
|
||||
v[j] = -s'*Aij + c*vj
|
||||
end
|
||||
else
|
||||
for j = i + 1:n
|
||||
vj = v[j]
|
||||
Aji = (A[j,i] - s*vj)/c
|
||||
A[j,i] = Aji
|
||||
v[j] = -s'*Aji + c*vj
|
||||
end
|
||||
end
|
||||
end
|
||||
return C
|
||||
end
|
||||
|
||||
"""
|
||||
lowrankupdate(C::Cholesky, v::StridedVector) -> CC::Cholesky
|
||||
|
||||
Update a Cholesky factorization `C` with the vector `v`. If `A = C[:U]'C[:U]`
|
||||
then `CC = cholfact(C[:U]'C[:U] + v*v')` but the computation of `CC` only uses
|
||||
`O(n^2)` operations.
|
||||
"""
|
||||
lowrankupdate(C::Cholesky, v::StridedVector) = lowrankupdate!(copy(C), copy(v))
|
||||
|
||||
"""
|
||||
lowrankdowndate(C::Cholesky, v::StridedVector) -> CC::Cholesky
|
||||
|
||||
Downdate a Cholesky factorization `C` with the vector `v`. If `A = C[:U]'C[:U]`
|
||||
then `CC = cholfact(C[:U]'C[:U] - v*v')` but the computation of `CC` only uses
|
||||
`O(n^2)` operations.
|
||||
"""
|
||||
lowrankdowndate(C::Cholesky, v::StridedVector) = lowrankdowndate!(copy(C), copy(v))
|
||||
@@ -0,0 +1,62 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
"""
|
||||
ConjArray(array)
|
||||
|
||||
A lazy-view wrapper of an `AbstractArray`, taking the elementwise complex conjugate. This
|
||||
type is usually constructed (and unwrapped) via the [`conj`](@ref) function (or related
|
||||
[`ctranspose`](@ref)), but currently this is the default behavior for `RowVector` only. For
|
||||
other arrays, the `ConjArray` constructor can be used directly.
|
||||
|
||||
# Examples
|
||||
|
||||
```jldoctest
|
||||
julia> [1+im, 1-im]'
|
||||
1×2 RowVector{Complex{Int64},ConjArray{Complex{Int64},1,Array{Complex{Int64},1}}}:
|
||||
1-1im 1+1im
|
||||
|
||||
julia> ConjArray([1+im 0; 0 1-im])
|
||||
2×2 ConjArray{Complex{Int64},2,Array{Complex{Int64},2}}:
|
||||
1-1im 0+0im
|
||||
0+0im 1+1im
|
||||
```
|
||||
"""
|
||||
struct ConjArray{T,N,A<:AbstractArray} <: AbstractArray{T,N}
|
||||
parent::A
|
||||
end
|
||||
|
||||
@inline ConjArray(a::AbstractArray{T,N}) where {T,N} = ConjArray{conj_type(T),N,typeof(a)}(a)
|
||||
|
||||
const ConjVector{T,V<:AbstractVector} = ConjArray{T,1,V}
|
||||
@inline ConjVector(v::AbstractVector{T}) where {T} = ConjArray{conj_type(T),1,typeof(v)}(v)
|
||||
|
||||
const ConjMatrix{T,M<:AbstractMatrix} = ConjArray{T,2,M}
|
||||
@inline ConjMatrix(m::AbstractMatrix{T}) where {T} = ConjArray{conj_type(T),2,typeof(m)}(m)
|
||||
|
||||
# This type can cause the element type to change under conjugation - e.g. an array of complex arrays.
|
||||
@inline conj_type(x) = conj_type(typeof(x))
|
||||
@inline conj_type(::Type{T}) where {T} = promote_op(conj, T)
|
||||
|
||||
@inline parent(c::ConjArray) = c.parent
|
||||
@inline parent_type(c::ConjArray) = parent_type(typeof(c))
|
||||
@inline parent_type(::Type{ConjArray{T,N,A}}) where {T,N,A} = A
|
||||
|
||||
@inline size(a::ConjArray) = size(a.parent)
|
||||
IndexStyle(::CA) where {CA<:ConjArray} = IndexStyle(parent_type(CA))
|
||||
IndexStyle(::Type{CA}) where {CA<:ConjArray} = IndexStyle(parent_type(CA))
|
||||
|
||||
@propagate_inbounds getindex(a::ConjArray{T,N}, i::Int) where {T,N} = conj(getindex(a.parent, i))
|
||||
@propagate_inbounds getindex(a::ConjArray{T,N}, i::Vararg{Int,N}) where {T,N} = conj(getindex(a.parent, i...))
|
||||
@propagate_inbounds setindex!(a::ConjArray{T,N}, v, i::Int) where {T,N} = setindex!(a.parent, conj(v), i)
|
||||
@propagate_inbounds setindex!(a::ConjArray{T,N}, v, i::Vararg{Int,N}) where {T,N} = setindex!(a.parent, conj(v), i...)
|
||||
|
||||
@inline similar(a::ConjArray, ::Type{T}, dims::Dims{N}) where {T,N} = similar(parent(a), T, dims)
|
||||
|
||||
# Currently, this is default behavior for RowVector only
|
||||
@inline conj(a::ConjArray) = parent(a)
|
||||
|
||||
# Helper functions, currently used by RowVector
|
||||
@inline _conj(a::AbstractArray) = ConjArray(a)
|
||||
@inline _conj(a::AbstractArray{T}) where {T<:Real} = a
|
||||
@inline _conj(a::ConjArray) = parent(a)
|
||||
@inline _conj(a::ConjArray{T}) where {T<:Real} = parent(a)
|
||||
@@ -0,0 +1,961 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# Linear algebra functions for dense matrices in column major format
|
||||
|
||||
## BLAS cutoff threshold constants
|
||||
|
||||
const SCAL_CUTOFF = 2048
|
||||
const DOT_CUTOFF = 128
|
||||
const ASUM_CUTOFF = 32
|
||||
const NRM2_CUTOFF = 32
|
||||
|
||||
function scale!(X::Array{T}, s::T) where T<:BlasFloat
|
||||
s == 0 && return fill!(X, zero(T))
|
||||
s == 1 && return X
|
||||
if length(X) < SCAL_CUTOFF
|
||||
generic_scale!(X, s)
|
||||
else
|
||||
BLAS.scal!(length(X), s, X, 1)
|
||||
end
|
||||
X
|
||||
end
|
||||
|
||||
scale!(s::T, X::Array{T}) where {T<:BlasFloat} = scale!(X, s)
|
||||
|
||||
scale!(X::Array{T}, s::Number) where {T<:BlasFloat} = scale!(X, convert(T, s))
|
||||
function scale!(X::Array{T}, s::Real) where T<:BlasComplex
|
||||
R = typeof(real(zero(T)))
|
||||
BLAS.scal!(2*length(X), convert(R,s), convert(Ptr{R},pointer(X)), 1)
|
||||
X
|
||||
end
|
||||
|
||||
# Test whether a matrix is positive-definite
|
||||
isposdef!(A::StridedMatrix{<:BlasFloat}, UL::Symbol) = LAPACK.potrf!(char_uplo(UL), A)[2] == 0
|
||||
|
||||
"""
|
||||
isposdef!(A) -> Bool
|
||||
|
||||
Test whether a matrix is positive definite, overwriting `A` in the process.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1. 2.; 2. 50.];
|
||||
|
||||
julia> isposdef!(A)
|
||||
true
|
||||
|
||||
julia> A
|
||||
2×2 Array{Float64,2}:
|
||||
1.0 2.0
|
||||
2.0 6.78233
|
||||
```
|
||||
"""
|
||||
isposdef!(A::StridedMatrix) = ishermitian(A) && isposdef!(A, :U)
|
||||
|
||||
function isposdef(A::AbstractMatrix{T}, UL::Symbol) where T
|
||||
S = typeof(sqrt(one(T)))
|
||||
isposdef!(S == T ? copy(A) : convert(AbstractMatrix{S}, A), UL)
|
||||
end
|
||||
"""
|
||||
isposdef(A) -> Bool
|
||||
|
||||
Test whether a matrix is positive definite.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1 2; 2 50]
|
||||
2×2 Array{Int64,2}:
|
||||
1 2
|
||||
2 50
|
||||
|
||||
julia> isposdef(A)
|
||||
true
|
||||
```
|
||||
"""
|
||||
function isposdef(A::AbstractMatrix{T}) where T
|
||||
S = typeof(sqrt(one(T)))
|
||||
isposdef!(S == T ? copy(A) : convert(AbstractMatrix{S}, A))
|
||||
end
|
||||
isposdef(x::Number) = imag(x)==0 && real(x) > 0
|
||||
|
||||
stride1(x::Array) = 1
|
||||
stride1(x::StridedVector) = stride(x, 1)::Int
|
||||
|
||||
function norm(x::StridedVector{T}, rx::Union{UnitRange{TI},Range{TI}}) where {T<:BlasFloat,TI<:Integer}
|
||||
if minimum(rx) < 1 || maximum(rx) > length(x)
|
||||
throw(BoundsError(x, rx))
|
||||
end
|
||||
BLAS.nrm2(length(rx), pointer(x)+(first(rx)-1)*sizeof(T), step(rx))
|
||||
end
|
||||
|
||||
vecnorm1(x::Union{Array{T},StridedVector{T}}) where {T<:BlasReal} =
|
||||
length(x) < ASUM_CUTOFF ? generic_vecnorm1(x) : BLAS.asum(x)
|
||||
|
||||
vecnorm2(x::Union{Array{T},StridedVector{T}}) where {T<:BlasFloat} =
|
||||
length(x) < NRM2_CUTOFF ? generic_vecnorm2(x) : BLAS.nrm2(x)
|
||||
|
||||
"""
|
||||
triu!(M, k::Integer)
|
||||
|
||||
Returns the upper triangle of `M` starting from the `k`th superdiagonal,
|
||||
overwriting `M` in the process.
|
||||
|
||||
# Example
|
||||
```jldoctest
|
||||
julia> M = [1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5]
|
||||
5×5 Array{Int64,2}:
|
||||
1 2 3 4 5
|
||||
1 2 3 4 5
|
||||
1 2 3 4 5
|
||||
1 2 3 4 5
|
||||
1 2 3 4 5
|
||||
|
||||
julia> triu!(M, 1)
|
||||
5×5 Array{Int64,2}:
|
||||
0 2 3 4 5
|
||||
0 0 3 4 5
|
||||
0 0 0 4 5
|
||||
0 0 0 0 5
|
||||
0 0 0 0 0
|
||||
```
|
||||
"""
|
||||
function triu!(M::AbstractMatrix, k::Integer)
|
||||
m, n = size(M)
|
||||
if (k > 0 && k > n) || (k < 0 && -k > m)
|
||||
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($m,$n)"))
|
||||
end
|
||||
idx = 1
|
||||
for j = 0:n-1
|
||||
ii = min(max(0, j+1-k), m)
|
||||
for i = (idx+ii):(idx+m-1)
|
||||
M[i] = zero(M[i])
|
||||
end
|
||||
idx += m
|
||||
end
|
||||
M
|
||||
end
|
||||
|
||||
triu(M::Matrix, k::Integer) = triu!(copy(M), k)
|
||||
|
||||
"""
|
||||
tril!(M, k::Integer)
|
||||
|
||||
Returns the lower triangle of `M` starting from the `k`th superdiagonal, overwriting `M` in
|
||||
the process.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> M = [1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5; 1 2 3 4 5]
|
||||
5×5 Array{Int64,2}:
|
||||
1 2 3 4 5
|
||||
1 2 3 4 5
|
||||
1 2 3 4 5
|
||||
1 2 3 4 5
|
||||
1 2 3 4 5
|
||||
|
||||
julia> tril!(M, 2)
|
||||
5×5 Array{Int64,2}:
|
||||
1 2 3 0 0
|
||||
1 2 3 4 0
|
||||
1 2 3 4 5
|
||||
1 2 3 4 5
|
||||
1 2 3 4 5
|
||||
```
|
||||
"""
|
||||
function tril!(M::AbstractMatrix, k::Integer)
|
||||
m, n = size(M)
|
||||
if (k > 0 && k > n) || (k < 0 && -k > m)
|
||||
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($m,$n)"))
|
||||
end
|
||||
idx = 1
|
||||
for j = 0:n-1
|
||||
ii = min(max(0, j-k), m)
|
||||
for i = idx:(idx+ii-1)
|
||||
M[i] = zero(M[i])
|
||||
end
|
||||
idx += m
|
||||
end
|
||||
M
|
||||
end
|
||||
tril(M::Matrix, k::Integer) = tril!(copy(M), k)
|
||||
|
||||
function gradient(F::AbstractVector, h::Vector)
|
||||
n = length(F)
|
||||
T = typeof(oneunit(eltype(F))/oneunit(eltype(h)))
|
||||
g = similar(F, T)
|
||||
if n == 1
|
||||
g[1] = zero(T)
|
||||
elseif n > 1
|
||||
g[1] = (F[2] - F[1]) / (h[2] - h[1])
|
||||
g[n] = (F[n] - F[n-1]) / (h[end] - h[end-1])
|
||||
if n > 2
|
||||
h = h[3:n] - h[1:n-2]
|
||||
g[2:n-1] = (F[3:n] - F[1:n-2]) ./ h
|
||||
end
|
||||
end
|
||||
g
|
||||
end
|
||||
|
||||
function diagind(m::Integer, n::Integer, k::Integer=0)
|
||||
if !(-m <= k <= n)
|
||||
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($m,$n)"))
|
||||
end
|
||||
k <= 0 ? range(1-k, m+1, min(m+k, n)) : range(k*m+1, m+1, min(m, n-k))
|
||||
end
|
||||
|
||||
"""
|
||||
diagind(M, k::Integer=0)
|
||||
|
||||
A `Range` giving the indices of the `k`th diagonal of the matrix `M`.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1 2 3; 4 5 6; 7 8 9]
|
||||
3×3 Array{Int64,2}:
|
||||
1 2 3
|
||||
4 5 6
|
||||
7 8 9
|
||||
|
||||
julia> diagind(A,-1)
|
||||
2:4:6
|
||||
```
|
||||
"""
|
||||
diagind(A::AbstractMatrix, k::Integer=0) = diagind(size(A,1), size(A,2), k)
|
||||
|
||||
"""
|
||||
diag(M, k::Integer=0)
|
||||
|
||||
The `k`th diagonal of a matrix, as a vector.
|
||||
Use [`diagm`](@ref) to construct a diagonal matrix.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1 2 3; 4 5 6; 7 8 9]
|
||||
3×3 Array{Int64,2}:
|
||||
1 2 3
|
||||
4 5 6
|
||||
7 8 9
|
||||
|
||||
julia> diag(A,1)
|
||||
2-element Array{Int64,1}:
|
||||
2
|
||||
6
|
||||
```
|
||||
"""
|
||||
diag(A::AbstractMatrix, k::Integer=0) = A[diagind(A,k)]
|
||||
|
||||
"""
|
||||
diagm(v, k::Integer=0)
|
||||
|
||||
Construct a matrix by placing `v` on the `k`th diagonal.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> diagm([1,2,3],1)
|
||||
4×4 Array{Int64,2}:
|
||||
0 1 0 0
|
||||
0 0 2 0
|
||||
0 0 0 3
|
||||
0 0 0 0
|
||||
```
|
||||
"""
|
||||
function diagm(v::AbstractVector{T}, k::Integer=0) where T
|
||||
n = length(v) + abs(k)
|
||||
A = zeros(T,n,n)
|
||||
A[diagind(A,k)] = v
|
||||
A
|
||||
end
|
||||
|
||||
diagm(x::Number) = (X = Matrix{typeof(x)}(1,1); X[1,1] = x; X)
|
||||
|
||||
function trace(A::Matrix{T}) where T
|
||||
n = checksquare(A)
|
||||
t = zero(T)
|
||||
for i=1:n
|
||||
t += A[i,i]
|
||||
end
|
||||
t
|
||||
end
|
||||
|
||||
"""
|
||||
kron(A, B)
|
||||
|
||||
Kronecker tensor product of two vectors or two matrices.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1 2; 3 4]
|
||||
2×2 Array{Int64,2}:
|
||||
1 2
|
||||
3 4
|
||||
|
||||
julia> B = [im 1; 1 -im]
|
||||
2×2 Array{Complex{Int64},2}:
|
||||
0+1im 1+0im
|
||||
1+0im 0-1im
|
||||
|
||||
julia> kron(A, B)
|
||||
4×4 Array{Complex{Int64},2}:
|
||||
0+1im 1+0im 0+2im 2+0im
|
||||
1+0im 0-1im 2+0im 0-2im
|
||||
0+3im 3+0im 0+4im 4+0im
|
||||
3+0im 0-3im 4+0im 0-4im
|
||||
```
|
||||
"""
|
||||
function kron(a::AbstractMatrix{T}, b::AbstractMatrix{S}) where {T,S}
|
||||
R = Matrix{promote_op(*,T,S)}(size(a,1)*size(b,1), size(a,2)*size(b,2))
|
||||
m = 1
|
||||
for j = 1:size(a,2), l = 1:size(b,2), i = 1:size(a,1)
|
||||
aij = a[i,j]
|
||||
for k = 1:size(b,1)
|
||||
R[m] = aij*b[k,l]
|
||||
m += 1
|
||||
end
|
||||
end
|
||||
R
|
||||
end
|
||||
|
||||
kron(a::Number, b::Union{Number, AbstractVecOrMat}) = a * b
|
||||
kron(a::AbstractVecOrMat, b::Number) = a * b
|
||||
kron(a::AbstractVector, b::AbstractVector) = vec(kron(reshape(a ,length(a), 1), reshape(b, length(b), 1)))
|
||||
kron(a::AbstractMatrix, b::AbstractVector) = kron(a, reshape(b, length(b), 1))
|
||||
kron(a::AbstractVector, b::AbstractMatrix) = kron(reshape(a, length(a), 1), b)
|
||||
|
||||
# Matrix power
|
||||
(^)(A::AbstractMatrix{T}, p::Integer) where {T} = p < 0 ? Base.power_by_squaring(inv(A), -p) : Base.power_by_squaring(A, p)
|
||||
function (^)(A::AbstractMatrix{T}, p::Real) where T
|
||||
# For integer powers, use repeated squaring
|
||||
if isinteger(p)
|
||||
TT = Base.promote_op(^, eltype(A), typeof(p))
|
||||
return (TT == eltype(A) ? A : copy!(similar(A, TT), A))^Integer(p)
|
||||
end
|
||||
|
||||
# If possible, use diagonalization
|
||||
if T <: Real && issymmetric(A)
|
||||
return (Symmetric(A)^p)
|
||||
end
|
||||
if ishermitian(A)
|
||||
return (Hermitian(A)^p)
|
||||
end
|
||||
|
||||
n = checksquare(A)
|
||||
|
||||
# Quicker return if A is diagonal
|
||||
if isdiag(A)
|
||||
retmat = copy(A)
|
||||
for i in 1:n
|
||||
retmat[i, i] = retmat[i, i] ^ p
|
||||
end
|
||||
return retmat
|
||||
end
|
||||
|
||||
# Otherwise, use Schur decomposition
|
||||
if istriu(A)
|
||||
# Integer part
|
||||
retmat = A ^ floor(p)
|
||||
# Real part
|
||||
if p - floor(p) == 0.5
|
||||
# special case: A^0.5 === sqrtm(A)
|
||||
retmat = retmat * sqrtm(A)
|
||||
else
|
||||
retmat = retmat * powm!(UpperTriangular(float.(A)), real(p - floor(p)))
|
||||
end
|
||||
else
|
||||
S,Q,d = schur(complex(A))
|
||||
# Integer part
|
||||
R = S ^ floor(p)
|
||||
# Real part
|
||||
if p - floor(p) == 0.5
|
||||
# special case: A^0.5 === sqrtm(A)
|
||||
R = R * sqrtm(S)
|
||||
else
|
||||
R = R * powm!(UpperTriangular(float.(S)), real(p - floor(p)))
|
||||
end
|
||||
retmat = Q * R * Q'
|
||||
end
|
||||
|
||||
# if A has nonpositive real eigenvalues, retmat is a nonprincipal matrix power.
|
||||
if isreal(retmat)
|
||||
return real(retmat)
|
||||
else
|
||||
return retmat
|
||||
end
|
||||
end
|
||||
(^)(A::AbstractMatrix, p::Number) = expm(p*logm(A))
|
||||
|
||||
# Matrix exponential
|
||||
|
||||
"""
|
||||
expm(A)
|
||||
|
||||
Compute the matrix exponential of `A`, defined by
|
||||
|
||||
```math
|
||||
e^A = \\sum_{n=0}^{\\infty} \\frac{A^n}{n!}.
|
||||
```
|
||||
|
||||
For symmetric or Hermitian `A`, an eigendecomposition ([`eigfact`](@ref)) is
|
||||
used, otherwise the scaling and squaring algorithm (see [^H05]) is chosen.
|
||||
|
||||
[^H05]: Nicholas J. Higham, "The squaring and scaling method for the matrix exponential revisited", SIAM Journal on Matrix Analysis and Applications, 26(4), 2005, 1179-1193. [doi:10.1137/090768539](http://dx.doi.org/10.1137/090768539)
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = eye(2, 2)
|
||||
2×2 Array{Float64,2}:
|
||||
1.0 0.0
|
||||
0.0 1.0
|
||||
|
||||
julia> expm(A)
|
||||
2×2 Array{Float64,2}:
|
||||
2.71828 0.0
|
||||
0.0 2.71828
|
||||
```
|
||||
"""
|
||||
expm(A::StridedMatrix{<:BlasFloat}) = expm!(copy(A))
|
||||
expm(A::StridedMatrix{<:Integer}) = expm!(float(A))
|
||||
expm(x::Number) = exp(x)
|
||||
|
||||
## Destructive matrix exponential using algorithm from Higham, 2008,
|
||||
## "Functions of Matrices: Theory and Computation", SIAM
|
||||
function expm!(A::StridedMatrix{T}) where T<:BlasFloat
|
||||
n = checksquare(A)
|
||||
if ishermitian(A)
|
||||
return full(expm(Hermitian(A)))
|
||||
end
|
||||
ilo, ihi, scale = LAPACK.gebal!('B', A) # modifies A
|
||||
nA = norm(A, 1)
|
||||
I = eye(T,n)
|
||||
## For sufficiently small nA, use lower order Padé-Approximations
|
||||
if (nA <= 2.1)
|
||||
if nA > 0.95
|
||||
C = T[17643225600.,8821612800.,2075673600.,302702400.,
|
||||
30270240., 2162160., 110880., 3960.,
|
||||
90., 1.]
|
||||
elseif nA > 0.25
|
||||
C = T[17297280.,8648640.,1995840.,277200.,
|
||||
25200., 1512., 56., 1.]
|
||||
elseif nA > 0.015
|
||||
C = T[30240.,15120.,3360.,
|
||||
420., 30., 1.]
|
||||
else
|
||||
C = T[120.,60.,12.,1.]
|
||||
end
|
||||
A2 = A * A
|
||||
P = copy(I)
|
||||
U = C[2] * P
|
||||
V = C[1] * P
|
||||
for k in 1:(div(size(C, 1), 2) - 1)
|
||||
k2 = 2 * k
|
||||
P *= A2
|
||||
U += C[k2 + 2] * P
|
||||
V += C[k2 + 1] * P
|
||||
end
|
||||
U = A * U
|
||||
X = V + U
|
||||
LAPACK.gesv!(V-U, X)
|
||||
else
|
||||
s = log2(nA/5.4) # power of 2 later reversed by squaring
|
||||
if s > 0
|
||||
si = ceil(Int,s)
|
||||
A /= convert(T,2^si)
|
||||
end
|
||||
CC = T[64764752532480000.,32382376266240000.,7771770303897600.,
|
||||
1187353796428800., 129060195264000., 10559470521600.,
|
||||
670442572800., 33522128640., 1323241920.,
|
||||
40840800., 960960., 16380.,
|
||||
182., 1.]
|
||||
A2 = A * A
|
||||
A4 = A2 * A2
|
||||
A6 = A2 * A4
|
||||
U = A * (A6 * (CC[14]*A6 + CC[12]*A4 + CC[10]*A2) +
|
||||
CC[8]*A6 + CC[6]*A4 + CC[4]*A2 + CC[2]*I)
|
||||
V = A6 * (CC[13]*A6 + CC[11]*A4 + CC[9]*A2) +
|
||||
CC[7]*A6 + CC[5]*A4 + CC[3]*A2 + CC[1]*I
|
||||
|
||||
X = V + U
|
||||
LAPACK.gesv!(V-U, X)
|
||||
|
||||
if s > 0 # squaring to reverse dividing by power of 2
|
||||
for t=1:si; X *= X end
|
||||
end
|
||||
end
|
||||
|
||||
# Undo the balancing
|
||||
for j = ilo:ihi
|
||||
scj = scale[j]
|
||||
for i = 1:n
|
||||
X[j,i] *= scj
|
||||
end
|
||||
for i = 1:n
|
||||
X[i,j] /= scj
|
||||
end
|
||||
end
|
||||
|
||||
if ilo > 1 # apply lower permutations in reverse order
|
||||
for j in (ilo-1):-1:1; rcswap!(j, Int(scale[j]), X) end
|
||||
end
|
||||
if ihi < n # apply upper permutations in forward order
|
||||
for j in (ihi+1):n; rcswap!(j, Int(scale[j]), X) end
|
||||
end
|
||||
X
|
||||
end
|
||||
|
||||
## Swap rows i and j and columns i and j in X
|
||||
function rcswap!(i::Integer, j::Integer, X::StridedMatrix{<:Number})
|
||||
for k = 1:size(X,1)
|
||||
X[k,i], X[k,j] = X[k,j], X[k,i]
|
||||
end
|
||||
for k = 1:size(X,2)
|
||||
X[i,k], X[j,k] = X[j,k], X[i,k]
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
logm(A{T}::StridedMatrix{T})
|
||||
|
||||
If `A` has no negative real eigenvalue, compute the principal matrix logarithm of `A`, i.e.
|
||||
the unique matrix ``X`` such that ``e^X = A`` and ``-\\pi < Im(\\lambda) < \\pi`` for all
|
||||
the eigenvalues ``\\lambda`` of ``X``. If `A` has nonpositive eigenvalues, a nonprincipal
|
||||
matrix function is returned whenever possible.
|
||||
|
||||
If `A` is symmetric or Hermitian, its eigendecomposition ([`eigfact`](@ref)) is
|
||||
used, if `A` is triangular an improved version of the inverse scaling and squaring method is
|
||||
employed (see [^AH12] and [^AHR13]). For general matrices, the complex Schur form
|
||||
([`schur`](@ref)) is computed and the triangular algorithm is used on the
|
||||
triangular factor.
|
||||
|
||||
[^AH12]: Awad H. Al-Mohy and Nicholas J. Higham, "Improved inverse scaling and squaring algorithms for the matrix logarithm", SIAM Journal on Scientific Computing, 34(4), 2012, C153-C169. [doi:10.1137/110852553](http://dx.doi.org/10.1137/110852553)
|
||||
|
||||
[^AHR13]: Awad H. Al-Mohy, Nicholas J. Higham and Samuel D. Relton, "Computing the Fréchet derivative of the matrix logarithm and estimating the condition number", SIAM Journal on Scientific Computing, 35(4), 2013, C394-C410. [doi:10.1137/120885991](http://dx.doi.org/10.1137/120885991)
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = 2.7182818 * eye(2)
|
||||
2×2 Array{Float64,2}:
|
||||
2.71828 0.0
|
||||
0.0 2.71828
|
||||
|
||||
julia> logm(A)
|
||||
2×2 Symmetric{Float64,Array{Float64,2}}:
|
||||
1.0 0.0
|
||||
0.0 1.0
|
||||
```
|
||||
"""
|
||||
function logm(A::StridedMatrix{T}) where T
|
||||
# If possible, use diagonalization
|
||||
if issymmetric(A) && T <: Real
|
||||
return logm(Symmetric(A))
|
||||
end
|
||||
if ishermitian(A)
|
||||
return logm(Hermitian(A))
|
||||
end
|
||||
|
||||
# Use Schur decomposition
|
||||
n = checksquare(A)
|
||||
if istriu(A)
|
||||
return full(logm(UpperTriangular(complex(A))))
|
||||
else
|
||||
if isreal(A)
|
||||
SchurF = schurfact(real(A))
|
||||
else
|
||||
SchurF = schurfact(A)
|
||||
end
|
||||
if !istriu(SchurF.T)
|
||||
SchurS = schurfact(complex(SchurF.T))
|
||||
logT = SchurS.Z * logm(UpperTriangular(SchurS.T)) * SchurS.Z'
|
||||
return SchurF.Z * logT * SchurF.Z'
|
||||
else
|
||||
R = logm(UpperTriangular(complex(SchurF.T)))
|
||||
return SchurF.Z * R * SchurF.Z'
|
||||
end
|
||||
end
|
||||
end
|
||||
function logm(a::Number)
|
||||
b = log(complex(a))
|
||||
return imag(b) == 0 ? real(b) : b
|
||||
end
|
||||
logm(a::Complex) = log(a)
|
||||
|
||||
"""
|
||||
sqrtm(A)
|
||||
|
||||
If `A` has no negative real eigenvalues, compute the principal matrix square root of `A`,
|
||||
that is the unique matrix ``X`` with eigenvalues having positive real part such that
|
||||
``X^2 = A``. Otherwise, a nonprincipal square root is returned.
|
||||
|
||||
If `A` is symmetric or Hermitian, its eigendecomposition ([`eigfact`](@ref)) is
|
||||
used to compute the square root. Otherwise, the square root is determined by means of the
|
||||
Björck-Hammarling method [^BH83], which computes the complex Schur form ([`schur`](@ref))
|
||||
and then the complex square root of the triangular factor.
|
||||
|
||||
[^BH83]:
|
||||
|
||||
Åke Björck and Sven Hammarling, "A Schur method for the square root of a matrix",
|
||||
Linear Algebra and its Applications, 52-53, 1983, 127-140.
|
||||
[doi:10.1016/0024-3795(83)80010-X](http://dx.doi.org/10.1016/0024-3795(83)80010-X)
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [4 0; 0 4]
|
||||
2×2 Array{Int64,2}:
|
||||
4 0
|
||||
0 4
|
||||
|
||||
julia> sqrtm(A)
|
||||
2×2 Array{Float64,2}:
|
||||
2.0 0.0
|
||||
0.0 2.0
|
||||
```
|
||||
"""
|
||||
function sqrtm(A::StridedMatrix{<:Real})
|
||||
if issymmetric(A)
|
||||
return full(sqrtm(Symmetric(A)))
|
||||
end
|
||||
n = checksquare(A)
|
||||
if istriu(A)
|
||||
return full(sqrtm(UpperTriangular(A)))
|
||||
else
|
||||
SchurF = schurfact(complex(A))
|
||||
R = full(sqrtm(UpperTriangular(SchurF[:T])))
|
||||
return SchurF[:vectors] * R * SchurF[:vectors]'
|
||||
end
|
||||
end
|
||||
function sqrtm(A::StridedMatrix{<:Complex})
|
||||
if ishermitian(A)
|
||||
return full(sqrtm(Hermitian(A)))
|
||||
end
|
||||
n = checksquare(A)
|
||||
if istriu(A)
|
||||
return full(sqrtm(UpperTriangular(A)))
|
||||
else
|
||||
SchurF = schurfact(A)
|
||||
R = full(sqrtm(UpperTriangular(SchurF[:T])))
|
||||
return SchurF[:vectors] * R * SchurF[:vectors]'
|
||||
end
|
||||
end
|
||||
sqrtm(a::Number) = (b = sqrt(complex(a)); imag(b) == 0 ? real(b) : b)
|
||||
sqrtm(a::Complex) = sqrt(a)
|
||||
|
||||
function inv(A::StridedMatrix{T}) where T
|
||||
checksquare(A)
|
||||
S = typeof((one(T)*zero(T) + one(T)*zero(T))/one(T))
|
||||
AA = convert(AbstractArray{S}, A)
|
||||
if istriu(AA)
|
||||
Ai = inv(UpperTriangular(AA))
|
||||
elseif istril(AA)
|
||||
Ai = inv(LowerTriangular(AA))
|
||||
else
|
||||
Ai = inv(lufact(AA))
|
||||
end
|
||||
return convert(typeof(parent(Ai)), Ai)
|
||||
end
|
||||
|
||||
"""
|
||||
factorize(A)
|
||||
|
||||
Compute a convenient factorization of `A`, based upon the type of the input matrix.
|
||||
`factorize` checks `A` to see if it is symmetric/triangular/etc. if `A` is passed
|
||||
as a generic matrix. `factorize` checks every element of `A` to verify/rule out
|
||||
each property. It will short-circuit as soon as it can rule out symmetry/triangular
|
||||
structure. The return value can be reused for efficient solving of multiple
|
||||
systems. For example: `A=factorize(A); x=A\\b; y=A\\C`.
|
||||
|
||||
| Properties of `A` | type of factorization |
|
||||
|:---------------------------|:-----------------------------------------------|
|
||||
| Positive-definite | Cholesky (see [`cholfact`](@ref)) |
|
||||
| Dense Symmetric/Hermitian | Bunch-Kaufman (see [`bkfact`](@ref)) |
|
||||
| Sparse Symmetric/Hermitian | LDLt (see [`ldltfact`](@ref)) |
|
||||
| Triangular | Triangular |
|
||||
| Diagonal | Diagonal |
|
||||
| Bidiagonal | Bidiagonal |
|
||||
| Tridiagonal | LU (see [`lufact`](@ref)) |
|
||||
| Symmetric real tridiagonal | LDLt (see [`ldltfact`](@ref)) |
|
||||
| General square | LU (see [`lufact`](@ref)) |
|
||||
| General non-square | QR (see [`qrfact`](@ref)) |
|
||||
|
||||
If `factorize` is called on a Hermitian positive-definite matrix, for instance, then `factorize`
|
||||
will return a Cholesky factorization.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = Array(Bidiagonal(ones(5, 5), true))
|
||||
5×5 Array{Float64,2}:
|
||||
1.0 1.0 0.0 0.0 0.0
|
||||
0.0 1.0 1.0 0.0 0.0
|
||||
0.0 0.0 1.0 1.0 0.0
|
||||
0.0 0.0 0.0 1.0 1.0
|
||||
0.0 0.0 0.0 0.0 1.0
|
||||
|
||||
julia> factorize(A) # factorize will check to see that A is already factorized
|
||||
5×5 Bidiagonal{Float64}:
|
||||
1.0 1.0 ⋅ ⋅ ⋅
|
||||
⋅ 1.0 1.0 ⋅ ⋅
|
||||
⋅ ⋅ 1.0 1.0 ⋅
|
||||
⋅ ⋅ ⋅ 1.0 1.0
|
||||
⋅ ⋅ ⋅ ⋅ 1.0
|
||||
```
|
||||
This returns a `5×5 Bidiagonal{Float64}`, which can now be passed to other linear algebra functions
|
||||
(e.g. eigensolvers) which will use specialized methods for `Bidiagonal` types.
|
||||
"""
|
||||
function factorize(A::StridedMatrix{T}) where T
|
||||
m, n = size(A)
|
||||
if m == n
|
||||
if m == 1 return A[1] end
|
||||
utri = true
|
||||
utri1 = true
|
||||
herm = true
|
||||
sym = true
|
||||
for j = 1:n-1, i = j+1:m
|
||||
if utri1
|
||||
if A[i,j] != 0
|
||||
utri1 = i == j + 1
|
||||
utri = false
|
||||
end
|
||||
end
|
||||
if sym
|
||||
sym &= A[i,j] == A[j,i]
|
||||
end
|
||||
if herm
|
||||
herm &= A[i,j] == conj(A[j,i])
|
||||
end
|
||||
if !(utri1|herm|sym) break end
|
||||
end
|
||||
ltri = true
|
||||
ltri1 = true
|
||||
for j = 3:n, i = 1:j-2
|
||||
ltri1 &= A[i,j] == 0
|
||||
if !ltri1 break end
|
||||
end
|
||||
if ltri1
|
||||
for i = 1:n-1
|
||||
if A[i,i+1] != 0
|
||||
ltri &= false
|
||||
break
|
||||
end
|
||||
end
|
||||
if ltri
|
||||
if utri
|
||||
return Diagonal(A)
|
||||
end
|
||||
if utri1
|
||||
return Bidiagonal(diag(A), diag(A, -1), false)
|
||||
end
|
||||
return LowerTriangular(A)
|
||||
end
|
||||
if utri
|
||||
return Bidiagonal(diag(A), diag(A, 1), true)
|
||||
end
|
||||
if utri1
|
||||
if (herm & (T <: Complex)) | sym
|
||||
try
|
||||
return ldltfact!(SymTridiagonal(diag(A), diag(A, -1)))
|
||||
end
|
||||
end
|
||||
return lufact(Tridiagonal(diag(A, -1), diag(A), diag(A, 1)))
|
||||
end
|
||||
end
|
||||
if utri
|
||||
return UpperTriangular(A)
|
||||
end
|
||||
if herm
|
||||
try
|
||||
return cholfact(A)
|
||||
end
|
||||
return factorize(Hermitian(A))
|
||||
end
|
||||
if sym
|
||||
return factorize(Symmetric(A))
|
||||
end
|
||||
return lufact(A)
|
||||
end
|
||||
qrfact(A, Val{true})
|
||||
end
|
||||
|
||||
## Moore-Penrose pseudoinverse
|
||||
|
||||
"""
|
||||
pinv(M[, tol::Real])
|
||||
|
||||
Computes the Moore-Penrose pseudoinverse.
|
||||
|
||||
For matrices `M` with floating point elements, it is convenient to compute
|
||||
the pseudoinverse by inverting only singular values above a given threshold,
|
||||
`tol`.
|
||||
|
||||
The optimal choice of `tol` varies both with the value of `M` and the intended application
|
||||
of the pseudoinverse. The default value of `tol` is
|
||||
`eps(real(float(one(eltype(M)))))*maximum(size(A))`, which is essentially machine epsilon
|
||||
for the real part of a matrix element multiplied by the larger matrix dimension. For
|
||||
inverting dense ill-conditioned matrices in a least-squares sense,
|
||||
`tol = sqrt(eps(real(float(one(eltype(M))))))` is recommended.
|
||||
|
||||
For more information, see [^issue8859], [^B96], [^S84], [^KY88].
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> M = [1.5 1.3; 1.2 1.9]
|
||||
2×2 Array{Float64,2}:
|
||||
1.5 1.3
|
||||
1.2 1.9
|
||||
|
||||
julia> N = pinv(M)
|
||||
2×2 Array{Float64,2}:
|
||||
1.47287 -1.00775
|
||||
-0.930233 1.16279
|
||||
|
||||
julia> M * N
|
||||
2×2 Array{Float64,2}:
|
||||
1.0 -2.22045e-16
|
||||
4.44089e-16 1.0
|
||||
```
|
||||
|
||||
[^issue8859]: Issue 8859, "Fix least squares", https://github.com/JuliaLang/julia/pull/8859
|
||||
|
||||
[^B96]: Åke Björck, "Numerical Methods for Least Squares Problems", SIAM Press, Philadelphia, 1996, "Other Titles in Applied Mathematics", Vol. 51. [doi:10.1137/1.9781611971484](http://epubs.siam.org/doi/book/10.1137/1.9781611971484)
|
||||
|
||||
[^S84]: G. W. Stewart, "Rank Degeneracy", SIAM Journal on Scientific and Statistical Computing, 5(2), 1984, 403-413. [doi:10.1137/0905030](http://epubs.siam.org/doi/abs/10.1137/0905030)
|
||||
|
||||
[^KY88]: Konstantinos Konstantinides and Kung Yao, "Statistical analysis of effective singular values in matrix rank determination", IEEE Transactions on Acoustics, Speech and Signal Processing, 36(5), 1988, 757-763. [doi:10.1109/29.1585](http://dx.doi.org/10.1109/29.1585)
|
||||
"""
|
||||
function pinv(A::StridedMatrix{T}, tol::Real) where T
|
||||
m, n = size(A)
|
||||
Tout = typeof(zero(T)/sqrt(one(T) + one(T)))
|
||||
if m == 0 || n == 0
|
||||
return Matrix{Tout}(n, m)
|
||||
end
|
||||
if istril(A)
|
||||
if istriu(A)
|
||||
maxabsA = maximum(abs.(diag(A)))
|
||||
B = zeros(Tout, n, m)
|
||||
for i = 1:min(m, n)
|
||||
if abs(A[i,i]) > tol*maxabsA
|
||||
Aii = inv(A[i,i])
|
||||
if isfinite(Aii)
|
||||
B[i,i] = Aii
|
||||
end
|
||||
end
|
||||
end
|
||||
return B
|
||||
end
|
||||
end
|
||||
SVD = svdfact(A, thin=true)
|
||||
Stype = eltype(SVD.S)
|
||||
Sinv = zeros(Stype, length(SVD.S))
|
||||
index = SVD.S .> tol*maximum(SVD.S)
|
||||
Sinv[index] = one(Stype) ./ SVD.S[index]
|
||||
Sinv[find(.!isfinite.(Sinv))] = zero(Stype)
|
||||
return SVD.Vt' * (Diagonal(Sinv) * SVD.U')
|
||||
end
|
||||
function pinv(A::StridedMatrix{T}) where T
|
||||
tol = eps(real(float(one(T))))*maximum(size(A))
|
||||
return pinv(A, tol)
|
||||
end
|
||||
pinv(a::StridedVector) = pinv(reshape(a, length(a), 1))
|
||||
function pinv(x::Number)
|
||||
xi = inv(x)
|
||||
return ifelse(isfinite(xi), xi, zero(xi))
|
||||
end
|
||||
|
||||
## Basis for null space
|
||||
|
||||
"""
|
||||
nullspace(M)
|
||||
|
||||
Basis for nullspace of `M`.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> M = [1 0 0; 0 1 0; 0 0 0]
|
||||
3×3 Array{Int64,2}:
|
||||
1 0 0
|
||||
0 1 0
|
||||
0 0 0
|
||||
|
||||
julia> nullspace(M)
|
||||
3×1 Array{Float64,2}:
|
||||
0.0
|
||||
0.0
|
||||
1.0
|
||||
```
|
||||
"""
|
||||
function nullspace(A::StridedMatrix{T}) where T
|
||||
m, n = size(A)
|
||||
(m == 0 || n == 0) && return eye(T, n)
|
||||
SVD = svdfact(A, thin = false)
|
||||
indstart = sum(SVD.S .> max(m,n)*maximum(SVD.S)*eps(eltype(SVD.S))) + 1
|
||||
return SVD.Vt[indstart:end,:]'
|
||||
end
|
||||
nullspace(a::StridedVector) = nullspace(reshape(a, length(a), 1))
|
||||
|
||||
"""
|
||||
cond(M, p::Real=2)
|
||||
|
||||
Condition number of the matrix `M`, computed using the operator `p`-norm. Valid values for
|
||||
`p` are `1`, `2` (default), or `Inf`.
|
||||
"""
|
||||
function cond(A::AbstractMatrix, p::Real=2)
|
||||
if p == 2
|
||||
v = svdvals(A)
|
||||
maxv = maximum(v)
|
||||
return maxv == 0.0 ? oftype(real(A[1,1]),Inf) : maxv / minimum(v)
|
||||
elseif p == 1 || p == Inf
|
||||
checksquare(A)
|
||||
return cond(lufact(A), p)
|
||||
end
|
||||
throw(ArgumentError("p-norm must be 1, 2 or Inf, got $p"))
|
||||
end
|
||||
|
||||
## Lyapunov and Sylvester equation
|
||||
|
||||
# AX + XB + C = 0
|
||||
|
||||
"""
|
||||
sylvester(A, B, C)
|
||||
|
||||
Computes the solution `X` to the Sylvester equation `AX + XB + C = 0`, where `A`, `B` and
|
||||
`C` have compatible dimensions and `A` and `-B` have no eigenvalues with equal real part.
|
||||
"""
|
||||
function sylvester(A::StridedMatrix{T},B::StridedMatrix{T},C::StridedMatrix{T}) where T<:BlasFloat
|
||||
RA, QA = schur(A)
|
||||
RB, QB = schur(B)
|
||||
|
||||
D = -Ac_mul_B(QA,C*QB)
|
||||
Y, scale = LAPACK.trsyl!('N','N', RA, RB, D)
|
||||
scale!(QA*A_mul_Bc(Y,QB), inv(scale))
|
||||
end
|
||||
sylvester(A::StridedMatrix{T}, B::StridedMatrix{T}, C::StridedMatrix{T}) where {T<:Integer} = sylvester(float(A), float(B), float(C))
|
||||
|
||||
sylvester(a::Union{Real,Complex}, b::Union{Real,Complex}, c::Union{Real,Complex}) = -c / (a + b)
|
||||
|
||||
# AX + XA' + C = 0
|
||||
|
||||
"""
|
||||
lyap(A, C)
|
||||
|
||||
Computes the solution `X` to the continuous Lyapunov equation `AX + XA' + C = 0`, where no
|
||||
eigenvalue of `A` has a zero real part and no two eigenvalues are negative complex
|
||||
conjugates of each other.
|
||||
"""
|
||||
function lyap(A::StridedMatrix{T}, C::StridedMatrix{T}) where {T<:BlasFloat}
|
||||
R, Q = schur(A)
|
||||
|
||||
D = -Ac_mul_B(Q,C*Q)
|
||||
Y, scale = LAPACK.trsyl!('N', T <: Complex ? 'C' : 'T', R, R, D)
|
||||
scale!(Q*A_mul_Bc(Y,Q), inv(scale))
|
||||
end
|
||||
lyap(A::StridedMatrix{T}, C::StridedMatrix{T}) where {T<:Integer} = lyap(float(A), float(C))
|
||||
lyap(a::T, c::T) where {T<:Number} = -c/(2a)
|
||||
@@ -0,0 +1,373 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
## Diagonal matrices
|
||||
|
||||
struct Diagonal{T} <: AbstractMatrix{T}
|
||||
diag::Vector{T}
|
||||
end
|
||||
"""
|
||||
Diagonal(A::AbstractMatrix)
|
||||
|
||||
Constructs a matrix from the diagonal of `A`.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1 2 3; 4 5 6; 7 8 9]
|
||||
3×3 Array{Int64,2}:
|
||||
1 2 3
|
||||
4 5 6
|
||||
7 8 9
|
||||
|
||||
julia> Diagonal(A)
|
||||
3×3 Diagonal{Int64}:
|
||||
1 ⋅ ⋅
|
||||
⋅ 5 ⋅
|
||||
⋅ ⋅ 9
|
||||
```
|
||||
"""
|
||||
Diagonal(A::AbstractMatrix) = Diagonal(diag(A))
|
||||
"""
|
||||
Diagonal(V::AbstractVector)
|
||||
|
||||
Constructs a matrix with `V` as its diagonal.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> V = [1; 2]
|
||||
2-element Array{Int64,1}:
|
||||
1
|
||||
2
|
||||
|
||||
julia> Diagonal(V)
|
||||
2×2 Diagonal{Int64}:
|
||||
1 ⋅
|
||||
⋅ 2
|
||||
```
|
||||
"""
|
||||
Diagonal(V::AbstractVector) = Diagonal(collect(V))
|
||||
|
||||
convert(::Type{Diagonal{T}}, D::Diagonal{T}) where {T} = D
|
||||
convert(::Type{Diagonal{T}}, D::Diagonal) where {T} = Diagonal{T}(convert(Vector{T}, D.diag))
|
||||
convert(::Type{AbstractMatrix{T}}, D::Diagonal) where {T} = convert(Diagonal{T}, D)
|
||||
convert(::Type{Matrix}, D::Diagonal) = diagm(D.diag)
|
||||
convert(::Type{Array}, D::Diagonal) = convert(Matrix, D)
|
||||
full(D::Diagonal) = convert(Array, D)
|
||||
|
||||
function similar(D::Diagonal, ::Type{T}) where T
|
||||
return Diagonal{T}(similar(D.diag, T))
|
||||
end
|
||||
|
||||
copy!(D1::Diagonal, D2::Diagonal) = (copy!(D1.diag, D2.diag); D1)
|
||||
|
||||
size(D::Diagonal) = (length(D.diag),length(D.diag))
|
||||
|
||||
function size(D::Diagonal,d::Integer)
|
||||
if d<1
|
||||
throw(ArgumentError("dimension must be ≥ 1, got $d"))
|
||||
end
|
||||
return d<=2 ? length(D.diag) : 1
|
||||
end
|
||||
|
||||
@inline function getindex(D::Diagonal, i::Int, j::Int)
|
||||
@boundscheck checkbounds(D, i, j)
|
||||
if i == j
|
||||
@inbounds r = D.diag[i]
|
||||
else
|
||||
r = diagzero(D, i, j)
|
||||
end
|
||||
r
|
||||
end
|
||||
diagzero(::Diagonal{T},i,j) where {T} = zero(T)
|
||||
diagzero(D::Diagonal{Matrix{T}},i,j) where {T} = zeros(T, size(D.diag[i], 1), size(D.diag[j], 2))
|
||||
|
||||
function setindex!(D::Diagonal, v, i::Int, j::Int)
|
||||
@boundscheck checkbounds(D, i, j)
|
||||
if i == j
|
||||
@inbounds D.diag[i] = v
|
||||
elseif !iszero(v)
|
||||
throw(ArgumentError("cannot set off-diagonal entry ($i, $j) to a nonzero value ($v)"))
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
|
||||
## structured matrix methods ##
|
||||
function Base.replace_in_print_matrix(A::Diagonal,i::Integer,j::Integer,s::AbstractString)
|
||||
i==j ? s : Base.replace_with_centered_mark(s)
|
||||
end
|
||||
|
||||
parent(D::Diagonal) = D.diag
|
||||
|
||||
ishermitian(D::Diagonal{<:Real}) = true
|
||||
ishermitian(D::Diagonal{<:Number}) = isreal(D.diag)
|
||||
ishermitian(D::Diagonal) = all(ishermitian, D.diag)
|
||||
issymmetric(D::Diagonal{<:Number}) = true
|
||||
issymmetric(D::Diagonal) = all(issymmetric, D.diag)
|
||||
isposdef(D::Diagonal) = all(x -> x > 0, D.diag)
|
||||
|
||||
factorize(D::Diagonal) = D
|
||||
|
||||
broadcast(::typeof(abs), D::Diagonal) = Diagonal(abs.(D.diag))
|
||||
real(D::Diagonal) = Diagonal(real(D.diag))
|
||||
imag(D::Diagonal) = Diagonal(imag(D.diag))
|
||||
|
||||
istriu(D::Diagonal) = true
|
||||
istril(D::Diagonal) = true
|
||||
function triu!(D::Diagonal,k::Integer=0)
|
||||
n = size(D,1)
|
||||
if abs(k) > n
|
||||
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($n,$n)"))
|
||||
elseif k > 0
|
||||
fill!(D.diag,0)
|
||||
end
|
||||
return D
|
||||
end
|
||||
|
||||
function tril!(D::Diagonal,k::Integer=0)
|
||||
n = size(D,1)
|
||||
if abs(k) > n
|
||||
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($n,$n)"))
|
||||
elseif k < 0
|
||||
fill!(D.diag,0)
|
||||
end
|
||||
return D
|
||||
end
|
||||
|
||||
(==)(Da::Diagonal, Db::Diagonal) = Da.diag == Db.diag
|
||||
(-)(A::Diagonal) = Diagonal(-A.diag)
|
||||
(+)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag + Db.diag)
|
||||
(-)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag - Db.diag)
|
||||
|
||||
(*)(x::Number, D::Diagonal) = Diagonal(x * D.diag)
|
||||
(*)(D::Diagonal, x::Number) = Diagonal(D.diag * x)
|
||||
(/)(D::Diagonal, x::Number) = Diagonal(D.diag / x)
|
||||
(*)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag .* Db.diag)
|
||||
(*)(D::Diagonal, V::AbstractVector) = D.diag .* V
|
||||
|
||||
(*)(A::AbstractTriangular, D::Diagonal) = A_mul_B!(copy(A), D)
|
||||
(*)(D::Diagonal, B::AbstractTriangular) = A_mul_B!(D, copy(B))
|
||||
|
||||
(*)(A::AbstractMatrix, D::Diagonal) =
|
||||
scale!(similar(A, promote_op(*, eltype(A), eltype(D.diag)), size(A)), A, D.diag)
|
||||
(*)(D::Diagonal, A::AbstractMatrix) =
|
||||
scale!(similar(A, promote_op(*, eltype(A), eltype(D.diag)), size(A)), D.diag, A)
|
||||
|
||||
A_mul_B!(A::Union{LowerTriangular,UpperTriangular}, D::Diagonal) =
|
||||
typeof(A)(A_mul_B!(A.data, D))
|
||||
function A_mul_B!(A::UnitLowerTriangular, D::Diagonal)
|
||||
A_mul_B!(A.data, D)
|
||||
for i = 1:size(A, 1)
|
||||
A.data[i,i] = D.diag[i]
|
||||
end
|
||||
LowerTriangular(A.data)
|
||||
end
|
||||
function A_mul_B!(A::UnitUpperTriangular, D::Diagonal)
|
||||
A_mul_B!(A.data, D)
|
||||
for i = 1:size(A, 1)
|
||||
A.data[i,i] = D.diag[i]
|
||||
end
|
||||
UpperTriangular(A.data)
|
||||
end
|
||||
function A_mul_B!(D::Diagonal, B::UnitLowerTriangular)
|
||||
A_mul_B!(D, B.data)
|
||||
for i = 1:size(B, 1)
|
||||
B.data[i,i] = D.diag[i]
|
||||
end
|
||||
LowerTriangular(B.data)
|
||||
end
|
||||
function A_mul_B!(D::Diagonal, B::UnitUpperTriangular)
|
||||
A_mul_B!(D, B.data)
|
||||
for i = 1:size(B, 1)
|
||||
B.data[i,i] = D.diag[i]
|
||||
end
|
||||
UpperTriangular(B.data)
|
||||
end
|
||||
|
||||
Ac_mul_B(A::AbstractTriangular, D::Diagonal) = A_mul_B!(ctranspose(A), D)
|
||||
function Ac_mul_B(A::AbstractMatrix, D::Diagonal)
|
||||
Ac = similar(A, promote_op(*, eltype(A), eltype(D.diag)), (size(A, 2), size(A, 1)))
|
||||
ctranspose!(Ac, A)
|
||||
A_mul_B!(Ac, D)
|
||||
end
|
||||
|
||||
At_mul_B(A::AbstractTriangular, D::Diagonal) = A_mul_B!(transpose(A), D)
|
||||
function At_mul_B(A::AbstractMatrix, D::Diagonal)
|
||||
At = similar(A, promote_op(*, eltype(A), eltype(D.diag)), (size(A, 2), size(A, 1)))
|
||||
transpose!(At, A)
|
||||
A_mul_B!(At, D)
|
||||
end
|
||||
|
||||
A_mul_Bc(D::Diagonal, B::AbstractTriangular) = A_mul_B!(D, ctranspose(B))
|
||||
A_mul_Bc(D::Diagonal, Q::Union{Base.LinAlg.QRCompactWYQ,Base.LinAlg.QRPackedQ}) = A_mul_Bc!(Array(D), Q)
|
||||
function A_mul_Bc(D::Diagonal, A::AbstractMatrix)
|
||||
Ac = similar(A, promote_op(*, eltype(A), eltype(D.diag)), (size(A, 2), size(A, 1)))
|
||||
ctranspose!(Ac, A)
|
||||
A_mul_B!(D, Ac)
|
||||
end
|
||||
|
||||
A_mul_Bt(D::Diagonal, B::AbstractTriangular) = A_mul_B!(D, transpose(B))
|
||||
function A_mul_Bt(D::Diagonal, A::AbstractMatrix)
|
||||
At = similar(A, promote_op(*, eltype(A), eltype(D.diag)), (size(A, 2), size(A, 1)))
|
||||
transpose!(At, A)
|
||||
A_mul_B!(D, At)
|
||||
end
|
||||
|
||||
A_mul_B!(A::Diagonal,B::Diagonal) = throw(MethodError(A_mul_B!, Tuple{Diagonal,Diagonal}))
|
||||
At_mul_B!(A::Diagonal,B::Diagonal) = throw(MethodError(At_mul_B!, Tuple{Diagonal,Diagonal}))
|
||||
Ac_mul_B!(A::Diagonal,B::Diagonal) = throw(MethodError(Ac_mul_B!, Tuple{Diagonal,Diagonal}))
|
||||
A_mul_B!(A::Base.LinAlg.QRPackedQ, D::Diagonal) = throw(MethodError(A_mul_B!, Tuple{Diagonal,Diagonal}))
|
||||
A_mul_B!(A::Diagonal,B::AbstractMatrix) = scale!(A.diag,B)
|
||||
At_mul_B!(A::Diagonal,B::AbstractMatrix) = scale!(A.diag,B)
|
||||
Ac_mul_B!(A::Diagonal,B::AbstractMatrix) = scale!(conj(A.diag),B)
|
||||
A_mul_B!(A::AbstractMatrix,B::Diagonal) = scale!(A,B.diag)
|
||||
A_mul_Bt!(A::AbstractMatrix,B::Diagonal) = scale!(A,B.diag)
|
||||
A_mul_Bc!(A::AbstractMatrix,B::Diagonal) = scale!(A,conj(B.diag))
|
||||
|
||||
# Get ambiguous method if try to unify AbstractVector/AbstractMatrix here using AbstractVecOrMat
|
||||
A_mul_B!(out::AbstractVector, A::Diagonal, in::AbstractVector) = out .= A.diag .* in
|
||||
Ac_mul_B!(out::AbstractVector, A::Diagonal, in::AbstractVector) = out .= ctranspose.(A.diag) .* in
|
||||
At_mul_B!(out::AbstractVector, A::Diagonal, in::AbstractVector) = out .= transpose.(A.diag) .* in
|
||||
|
||||
A_mul_B!(out::AbstractMatrix, A::Diagonal, in::AbstractMatrix) = out .= A.diag .* in
|
||||
Ac_mul_B!(out::AbstractMatrix, A::Diagonal, in::AbstractMatrix) = out .= ctranspose.(A.diag) .* in
|
||||
At_mul_B!(out::AbstractMatrix, A::Diagonal, in::AbstractMatrix) = out .= transpose.(A.diag) .* in
|
||||
|
||||
|
||||
(/)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag ./ Db.diag)
|
||||
function A_ldiv_B!(D::Diagonal{T}, v::AbstractVector{T}) where T
|
||||
if length(v) != length(D.diag)
|
||||
throw(DimensionMismatch("diagonal matrix is $(length(D.diag)) by $(length(D.diag)) but right hand side has $(length(v)) rows"))
|
||||
end
|
||||
for i=1:length(D.diag)
|
||||
d = D.diag[i]
|
||||
if d == zero(T)
|
||||
throw(SingularException(i))
|
||||
end
|
||||
v[i] *= inv(d)
|
||||
end
|
||||
v
|
||||
end
|
||||
function A_ldiv_B!(D::Diagonal{T}, V::AbstractMatrix{T}) where T
|
||||
if size(V,1) != length(D.diag)
|
||||
throw(DimensionMismatch("diagonal matrix is $(length(D.diag)) by $(length(D.diag)) but right hand side has $(size(V,1)) rows"))
|
||||
end
|
||||
for i=1:length(D.diag)
|
||||
d = D.diag[i]
|
||||
if d == zero(T)
|
||||
throw(SingularException(i))
|
||||
end
|
||||
d⁻¹ = inv(d)
|
||||
for j=1:size(V,2)
|
||||
@inbounds V[i,j] *= d⁻¹
|
||||
end
|
||||
end
|
||||
V
|
||||
end
|
||||
|
||||
# Methods to resolve ambiguities with `Diagonal`
|
||||
@inline *(rowvec::RowVector, D::Diagonal) = transpose(D * transpose(rowvec))
|
||||
@inline A_mul_Bt(D::Diagonal, rowvec::RowVector) = D*transpose(rowvec)
|
||||
@inline A_mul_Bc(D::Diagonal, rowvec::RowVector) = D*ctranspose(rowvec)
|
||||
|
||||
conj(D::Diagonal) = Diagonal(conj(D.diag))
|
||||
transpose(D::Diagonal{<:Number}) = D
|
||||
transpose(D::Diagonal) = Diagonal(transpose.(D.diag))
|
||||
ctranspose(D::Diagonal{<:Number}) = conj(D)
|
||||
ctranspose(D::Diagonal) = Diagonal(ctranspose.(D.diag))
|
||||
|
||||
diag(D::Diagonal) = D.diag
|
||||
trace(D::Diagonal) = sum(D.diag)
|
||||
det(D::Diagonal) = prod(D.diag)
|
||||
logdet(D::Diagonal{<:Real}) = sum(log, D.diag)
|
||||
function logdet(D::Diagonal{<:Complex}) # make sure branch cut is correct
|
||||
z = sum(log, D.diag)
|
||||
complex(real(z), rem2pi(imag(z), RoundNearest))
|
||||
end
|
||||
# identity matrices via eye(Diagonal{type},n)
|
||||
eye(::Type{Diagonal{T}}, n::Int) where {T} = Diagonal(ones(T,n))
|
||||
|
||||
# Matrix functions
|
||||
expm(D::Diagonal) = Diagonal(exp.(D.diag))
|
||||
expm(D::Diagonal{<:AbstractMatrix}) = Diagonal(expm.(D.diag))
|
||||
logm(D::Diagonal) = Diagonal(log.(D.diag))
|
||||
logm(D::Diagonal{<:AbstractMatrix}) = Diagonal(logm.(D.diag))
|
||||
sqrtm(D::Diagonal) = Diagonal(sqrt.(D.diag))
|
||||
sqrtm(D::Diagonal{<:AbstractMatrix}) = Diagonal(sqrtm.(D.diag))
|
||||
|
||||
#Linear solver
|
||||
function A_ldiv_B!(D::Diagonal, B::StridedVecOrMat)
|
||||
m, n = size(B, 1), size(B, 2)
|
||||
if m != length(D.diag)
|
||||
throw(DimensionMismatch("diagonal matrix is $(length(D.diag)) by $(length(D.diag)) but right hand side has $m rows"))
|
||||
end
|
||||
(m == 0 || n == 0) && return B
|
||||
for j = 1:n
|
||||
for i = 1:m
|
||||
di = D.diag[i]
|
||||
if di == 0
|
||||
throw(SingularException(i))
|
||||
end
|
||||
B[i,j] /= di
|
||||
end
|
||||
end
|
||||
return B
|
||||
end
|
||||
(\)(D::Diagonal, A::AbstractMatrix) = D.diag .\ A
|
||||
(\)(D::Diagonal, b::AbstractVector) = D.diag .\ b
|
||||
(\)(Da::Diagonal, Db::Diagonal) = Diagonal(Da.diag .\ Db.diag)
|
||||
|
||||
function inv(D::Diagonal{T}) where T
|
||||
Di = similar(D.diag, typeof(inv(zero(T))))
|
||||
for i = 1:length(D.diag)
|
||||
if D.diag[i] == zero(T)
|
||||
throw(SingularException(i))
|
||||
end
|
||||
Di[i] = inv(D.diag[i])
|
||||
end
|
||||
Diagonal(Di)
|
||||
end
|
||||
|
||||
function pinv(D::Diagonal{T}) where T
|
||||
Di = similar(D.diag, typeof(inv(zero(T))))
|
||||
for i = 1:length(D.diag)
|
||||
isfinite(inv(D.diag[i])) ? Di[i]=inv(D.diag[i]) : Di[i]=zero(T)
|
||||
end
|
||||
Diagonal(Di)
|
||||
end
|
||||
function pinv(D::Diagonal{T}, tol::Real) where T
|
||||
Di = similar(D.diag, typeof(inv(zero(T))))
|
||||
if( !isempty(D.diag) ) maxabsD = maximum(abs.(D.diag)) end
|
||||
for i = 1:length(D.diag)
|
||||
if( abs(D.diag[i]) > tol*maxabsD && isfinite(inv(D.diag[i])) )
|
||||
Di[i]=inv(D.diag[i])
|
||||
else
|
||||
Di[i]=zero(T)
|
||||
end
|
||||
end
|
||||
Diagonal(Di)
|
||||
end
|
||||
|
||||
#Eigensystem
|
||||
eigvals(D::Diagonal{<:Number}) = D.diag
|
||||
eigvals(D::Diagonal) = [eigvals(x) for x in D.diag] #For block matrices, etc.
|
||||
eigvecs(D::Diagonal) = eye(D)
|
||||
eigfact(D::Diagonal) = Eigen(eigvals(D), eigvecs(D))
|
||||
|
||||
#Singular system
|
||||
svdvals(D::Diagonal{<:Number}) = sort!(abs.(D.diag), rev = true)
|
||||
svdvals(D::Diagonal) = [svdvals(v) for v in D.diag]
|
||||
function svd(D::Diagonal{<:Number})
|
||||
S = abs.(D.diag)
|
||||
piv = sortperm(S, rev = true)
|
||||
U = Diagonal(D.diag ./ S)
|
||||
Up = hcat([U[:,i] for i = 1:length(D.diag)][piv]...)
|
||||
V = Diagonal(ones(D.diag))
|
||||
Vp = hcat([V[:,i] for i = 1:length(D.diag)][piv]...)
|
||||
return (Up, S[piv], Vp)
|
||||
end
|
||||
function svdfact(D::Diagonal)
|
||||
U, s, V = svd(D)
|
||||
SVD(U, s, V')
|
||||
end
|
||||
@@ -0,0 +1,446 @@
|
||||
# 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)
|
||||
@@ -0,0 +1,38 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
export LAPACKException,
|
||||
ARPACKException,
|
||||
SingularException,
|
||||
PosDefException,
|
||||
RankDeficientException
|
||||
|
||||
mutable struct LAPACKException <: Exception
|
||||
info::BlasInt
|
||||
end
|
||||
|
||||
mutable struct ARPACKException <: Exception
|
||||
info::String
|
||||
end
|
||||
|
||||
function ARPACKException(i::Integer)
|
||||
if i == -8
|
||||
return ARPACKException("error return from calculation of a real Schur form.")
|
||||
elseif i == -9
|
||||
return ARPACKException("error return from calculation of eigenvectors.")
|
||||
elseif i == -14
|
||||
return ARPACKException("did not find any eigenvalues to sufficient accuracy. Try with a different starting vector or more Lanczos vectors by increasing the value of ncv.")
|
||||
end
|
||||
return ARPACKException("unspecified ARPACK error: $i")
|
||||
end
|
||||
|
||||
mutable struct SingularException <: Exception
|
||||
info::BlasInt
|
||||
end
|
||||
|
||||
mutable struct PosDefException <: Exception
|
||||
info::BlasInt
|
||||
end
|
||||
|
||||
mutable struct RankDeficientException <: Exception
|
||||
info::BlasInt
|
||||
end
|
||||
@@ -0,0 +1,93 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
## Matrix factorizations and decompositions
|
||||
|
||||
abstract type Factorization{T} end
|
||||
|
||||
eltype(::Type{Factorization{T}}) where {T} = T
|
||||
transpose(F::Factorization) = error("transpose not implemented for $(typeof(F))")
|
||||
ctranspose(F::Factorization) = error("ctranspose not implemented for $(typeof(F))")
|
||||
|
||||
macro assertposdef(A, info)
|
||||
:($(esc(info)) == 0 ? $(esc(A)) : throw(PosDefException($(esc(info)))))
|
||||
end
|
||||
|
||||
macro assertnonsingular(A, info)
|
||||
:($(esc(info)) == 0 ? $(esc(A)) : throw(SingularException($(esc(info)))))
|
||||
end
|
||||
|
||||
function logdet(F::Factorization)
|
||||
d, s = logabsdet(F)
|
||||
return d + log(s)
|
||||
end
|
||||
|
||||
function det(F::Factorization)
|
||||
d, s = logabsdet(F)
|
||||
return exp(d)*s
|
||||
end
|
||||
|
||||
### General promotion rules
|
||||
convert(::Type{Factorization{T}}, F::Factorization{T}) where {T} = F
|
||||
inv(F::Factorization{T}) where {T} = A_ldiv_B!(F, eye(T, size(F,1)))
|
||||
|
||||
# With a real lhs and complex rhs with the same precision, we can reinterpret
|
||||
# the complex rhs as a real rhs with twice the number of columns
|
||||
function (\){T<:BlasReal}(F::Factorization{T}, B::VecOrMat{Complex{T}})
|
||||
c2r = reshape(transpose(reinterpret(T, B, (2, length(B)))), size(B, 1), 2*size(B, 2))
|
||||
x = A_ldiv_B!(F, c2r)
|
||||
return reinterpret(Complex{T}, transpose(reshape(x, div(length(x), 2), 2)), _ret_size(F, B))
|
||||
end
|
||||
|
||||
for (f1, f2) in ((:\, :A_ldiv_B!),
|
||||
(:Ac_ldiv_B, :Ac_ldiv_B!))
|
||||
@eval begin
|
||||
function $f1(F::Factorization, B::AbstractVecOrMat)
|
||||
TFB = typeof(oneunit(eltype(B)) / oneunit(eltype(F)))
|
||||
BB = similar(B, TFB, size(B))
|
||||
copy!(BB, B)
|
||||
$f2(F, BB)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# support the same 3-arg idiom as in our other in-place A_*_B functions:
|
||||
for f in (:A_ldiv_B!, :Ac_ldiv_B!, :At_ldiv_B!)
|
||||
@eval $f(Y::AbstractVecOrMat, A::Factorization, B::AbstractVecOrMat) =
|
||||
$f(A, copy!(Y, B))
|
||||
end
|
||||
|
||||
# fallback methods for transposed solves
|
||||
At_ldiv_B(F::Factorization{<:Real}, B::AbstractVecOrMat) = Ac_ldiv_B(F, B)
|
||||
At_ldiv_B(F::Factorization, B) = conj.(Ac_ldiv_B(F, conj.(B)))
|
||||
|
||||
"""
|
||||
A_ldiv_B!([Y,] A, B) -> Y
|
||||
|
||||
Compute `A \\ B` in-place and store the result in `Y`, returning the result.
|
||||
If only two arguments are passed, then `A_ldiv_B!(A, B)` overwrites `B` with
|
||||
the result.
|
||||
|
||||
The argument `A` should *not* be a matrix. Rather, instead of matrices it should be a
|
||||
factorization object (e.g. produced by [`factorize`](@ref) or [`cholfact`](@ref)).
|
||||
The reason for this is that factorization itself is both expensive and typically allocates memory
|
||||
(although it can also be done in-place via, e.g., [`lufact!`](@ref)),
|
||||
and performance-critical situations requiring `A_ldiv_B!` usually also require fine-grained
|
||||
control over the factorization of `A`.
|
||||
"""
|
||||
A_ldiv_B!
|
||||
|
||||
"""
|
||||
Ac_ldiv_B!([Y,] A, B) -> Y
|
||||
|
||||
Similar to [`A_ldiv_B!`](@ref), but return ``Aᴴ`` \\ ``B``,
|
||||
computing the result in-place in `Y` (or overwriting `B` if `Y` is not supplied).
|
||||
"""
|
||||
Ac_ldiv_B!
|
||||
|
||||
"""
|
||||
At_ldiv_B!([Y,] A, B) -> Y
|
||||
|
||||
Similar to [`A_ldiv_B!`](@ref), but return ``Aᵀ`` \\ ``B``,
|
||||
computing the result in-place in `Y` (or overwriting `B` if `Y` is not supplied).
|
||||
"""
|
||||
At_ldiv_B!
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,363 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
# givensAlgorithm functions are derived from LAPACK, see below
|
||||
|
||||
abstract type AbstractRotation{T} end
|
||||
|
||||
transpose(R::AbstractRotation) = error("transpose not implemented for $(typeof(R)). Consider using conjugate transpose (') instead of transpose (.').")
|
||||
|
||||
function *(R::AbstractRotation{T}, A::AbstractVecOrMat{S}) where {T,S}
|
||||
TS = typeof(zero(T)*zero(S) + zero(T)*zero(S))
|
||||
A_mul_B!(convert(AbstractRotation{TS}, R), TS == S ? copy(A) : convert(AbstractArray{TS}, A))
|
||||
end
|
||||
function A_mul_Bc(A::AbstractVecOrMat{T}, R::AbstractRotation{S}) where {T,S}
|
||||
TS = typeof(zero(T)*zero(S) + zero(T)*zero(S))
|
||||
A_mul_Bc!(TS == T ? copy(A) : convert(AbstractArray{TS}, A), convert(AbstractRotation{TS}, R))
|
||||
end
|
||||
"""
|
||||
LinAlg.Givens(i1,i2,c,s) -> G
|
||||
|
||||
A Givens rotation linear operator. The fields `c` and `s` represent the cosine and sine of
|
||||
the rotation angle, respectively. The `Givens` type supports left multiplication `G*A` and
|
||||
conjugated transpose right multiplication `A*G'`. The type doesn't have a `size` and can
|
||||
therefore be multiplied with matrices of arbitrary size as long as `i2<=size(A,2)` for
|
||||
`G*A` or `i2<=size(A,1)` for `A*G'`.
|
||||
|
||||
See also: [`givens`](@ref)
|
||||
"""
|
||||
struct Givens{T} <: AbstractRotation{T}
|
||||
i1::Int
|
||||
i2::Int
|
||||
c::T
|
||||
s::T
|
||||
end
|
||||
mutable struct Rotation{T} <: AbstractRotation{T}
|
||||
rotations::Vector{Givens{T}}
|
||||
end
|
||||
|
||||
convert(::Type{Givens{T}}, G::Givens{T}) where {T} = G
|
||||
convert(::Type{Givens{T}}, G::Givens) where {T} = Givens(G.i1, G.i2, convert(T, G.c), convert(T, G.s))
|
||||
convert(::Type{Rotation{T}}, R::Rotation{T}) where {T} = R
|
||||
convert(::Type{Rotation{T}}, R::Rotation) where {T} = Rotation{T}([convert(Givens{T}, g) for g in R.rotations])
|
||||
convert(::Type{AbstractRotation{T}}, G::Givens) where {T} = convert(Givens{T}, G)
|
||||
convert(::Type{AbstractRotation{T}}, R::Rotation) where {T} = convert(Rotation{T}, R)
|
||||
|
||||
ctranspose(G::Givens) = Givens(G.i1, G.i2, conj(G.c), -G.s)
|
||||
ctranspose(R::Rotation{T}) where {T} = Rotation{T}(reverse!([ctranspose(r) for r in R.rotations]))
|
||||
|
||||
realmin2(::Type{Float32}) = reinterpret(Float32, 0x26000000)
|
||||
realmin2(::Type{Float64}) = reinterpret(Float64, 0x21a0000000000000)
|
||||
realmin2(::Type{T}) where {T} = (twopar = 2one(T); twopar^trunc(Integer,log(realmin(T)/eps(T))/log(twopar)/twopar))
|
||||
|
||||
# derived from LAPACK's dlartg
|
||||
# Copyright:
|
||||
# Univ. of Tennessee
|
||||
# Univ. of California Berkeley
|
||||
# Univ. of Colorado Denver
|
||||
# NAG Ltd.
|
||||
function givensAlgorithm(f::T, g::T) where T<:AbstractFloat
|
||||
onepar = one(T)
|
||||
twopar = 2one(T)
|
||||
T0 = typeof(onepar) # dimensionless
|
||||
zeropar = T0(zero(T)) # must be dimensionless
|
||||
|
||||
# need both dimensionful and dimensionless versions of these:
|
||||
safmn2 = realmin2(T0)
|
||||
safmn2u = realmin2(T)
|
||||
safmx2 = one(T)/safmn2
|
||||
safmx2u = oneunit(T)/safmn2
|
||||
|
||||
if g == 0
|
||||
cs = onepar
|
||||
sn = zeropar
|
||||
r = f
|
||||
elseif f == 0
|
||||
cs = zeropar
|
||||
sn = onepar
|
||||
r = g
|
||||
else
|
||||
f1 = f
|
||||
g1 = g
|
||||
scalepar = max(abs(f1), abs(g1))
|
||||
if scalepar >= safmx2u
|
||||
count = 0
|
||||
while true
|
||||
count += 1
|
||||
f1 *= safmn2
|
||||
g1 *= safmn2
|
||||
scalepar = max(abs(f1), abs(g1))
|
||||
if scalepar < safmx2u break end
|
||||
end
|
||||
r = sqrt(f1*f1 + g1*g1)
|
||||
cs = f1/r
|
||||
sn = g1/r
|
||||
for i = 1:count
|
||||
r *= safmx2
|
||||
end
|
||||
elseif scalepar <= safmn2u
|
||||
count = 0
|
||||
while true
|
||||
count += 1
|
||||
f1 *= safmx2
|
||||
g1 *= safmx2
|
||||
scalepar = max(abs(f1), abs(g1))
|
||||
if scalepar > safmn2u break end
|
||||
end
|
||||
r = sqrt(f1*f1 + g1*g1)
|
||||
cs = f1/r
|
||||
sn = g1/r
|
||||
for i = 1:count
|
||||
r *= safmn2
|
||||
end
|
||||
else
|
||||
r = sqrt(f1*f1 + g1*g1)
|
||||
cs = f1/r
|
||||
sn = g1/r
|
||||
end
|
||||
if abs(f) > abs(g) && cs < 0
|
||||
cs = -cs
|
||||
sn = -sn
|
||||
r = -r
|
||||
end
|
||||
end
|
||||
return cs, sn, r
|
||||
end
|
||||
|
||||
# derived from LAPACK's zlartg
|
||||
# Copyright:
|
||||
# Univ. of Tennessee
|
||||
# Univ. of California Berkeley
|
||||
# Univ. of Colorado Denver
|
||||
# NAG Ltd.
|
||||
function givensAlgorithm(f::Complex{T}, g::Complex{T}) where T<:AbstractFloat
|
||||
twopar, onepar = 2one(T), one(T)
|
||||
T0 = typeof(onepar) # dimensionless
|
||||
zeropar = T0(zero(T)) # must be dimensionless
|
||||
czero = complex(zeropar)
|
||||
|
||||
abs1(ff) = max(abs(real(ff)), abs(imag(ff)))
|
||||
safmin = realmin(T0)
|
||||
safmn2 = realmin2(T0)
|
||||
safmn2u = realmin2(T)
|
||||
safmx2 = one(T)/safmn2
|
||||
safmx2u = oneunit(T)/safmn2
|
||||
scalepar = max(abs1(f), abs1(g))
|
||||
fs = f
|
||||
gs = g
|
||||
count = 0
|
||||
if scalepar >= safmx2u
|
||||
while true
|
||||
count += 1
|
||||
fs *= safmn2
|
||||
gs *= safmn2
|
||||
scalepar *= safmn2
|
||||
if scalepar < safmx2u break end
|
||||
end
|
||||
elseif scalepar <= safmn2u
|
||||
if g == 0
|
||||
cs = onepar
|
||||
sn = czero
|
||||
r = f
|
||||
return cs, sn, r
|
||||
end
|
||||
while true
|
||||
count -= 1
|
||||
fs *= safmx2
|
||||
gs *= safmx2
|
||||
scalepar *= safmx2
|
||||
if scalepar > safmn2u break end
|
||||
end
|
||||
end
|
||||
f2 = abs2(fs)
|
||||
g2 = abs2(gs)
|
||||
if f2 <= max(g2, oneunit(T))*safmin
|
||||
# This is a rare case: F is very small.
|
||||
if f == 0
|
||||
cs = zero(T)
|
||||
r = complex(hypot(real(g), imag(g)))
|
||||
# do complex/real division explicitly with two real divisions
|
||||
d = hypot(real(gs), imag(gs))
|
||||
sn = complex(real(gs)/d, -imag(gs)/d)
|
||||
return cs, sn, r
|
||||
end
|
||||
f2s = hypot(real(fs), imag(fs))
|
||||
# g2 and g2s are accurate
|
||||
# g2 is at least safmin, and g2s is at least safmn2
|
||||
g2s = sqrt(g2)
|
||||
# error in cs from underflow in f2s is at most
|
||||
# unfl / safmn2 .lt. sqrt(unfl*eps) .lt. eps
|
||||
# if max(g2,one)=g2, then f2 .lt. g2*safmin,
|
||||
# and so cs .lt. sqrt(safmin)
|
||||
# if max(g2,one)=one, then f2 .lt. safmin
|
||||
# and so cs .lt. sqrt(safmin)/safmn2 = sqrt(eps)
|
||||
# therefore, cs = f2s/g2s / sqrt( 1 + (f2s/g2s)**2 ) = f2s/g2s
|
||||
cs = f2s/g2s
|
||||
# make sure abs(ff) = 1
|
||||
# do complex/real division explicitly with 2 real divisions
|
||||
if abs1(f) > 1
|
||||
d = hypot(real(f), imag(f))
|
||||
ff = complex(real(f)/d, imag(f)/d)
|
||||
else
|
||||
dr = safmx2*real(f)
|
||||
di = safmx2*imag(f)
|
||||
d = hypot(dr, di)
|
||||
ff = complex(dr/d, di/d)
|
||||
end
|
||||
sn = ff*complex(real(gs)/g2s, -imag(gs)/g2s)
|
||||
r = cs*f + sn*g
|
||||
else
|
||||
# This is the most common case.
|
||||
# Neither F2 nor F2/G2 are less than SAFMIN
|
||||
# F2S cannot overflow, and it is accurate
|
||||
f2s = sqrt(onepar + g2/f2)
|
||||
# do the f2s(real)*fs(complex) multiply with two real multiplies
|
||||
r = complex(f2s*real(fs), f2s*imag(fs))
|
||||
cs = onepar/f2s
|
||||
d = f2 + g2
|
||||
# do complex/real division explicitly with two real divisions
|
||||
sn = complex(real(r)/d, imag(r)/d)
|
||||
sn *= conj(gs)
|
||||
if count != 0
|
||||
if count > 0
|
||||
for i = 1:count
|
||||
r *= safmx2
|
||||
end
|
||||
else
|
||||
for i = 1:-count
|
||||
r *= safmn2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return cs, sn, r
|
||||
end
|
||||
|
||||
"""
|
||||
|
||||
givens{T}(f::T, g::T, i1::Integer, i2::Integer) -> (G::Givens, r::T)
|
||||
|
||||
Computes the Givens rotation `G` and scalar `r` such that for any vector `x` where
|
||||
```
|
||||
x[i1] = f
|
||||
x[i2] = g
|
||||
```
|
||||
the result of the multiplication
|
||||
```
|
||||
y = G*x
|
||||
```
|
||||
has the property that
|
||||
```
|
||||
y[i1] = r
|
||||
y[i2] = 0
|
||||
```
|
||||
|
||||
See also: [`LinAlg.Givens`](@ref)
|
||||
"""
|
||||
function givens(f::T, g::T, i1::Integer, i2::Integer) where T
|
||||
if i1 == i2
|
||||
throw(ArgumentError("Indices must be distinct."))
|
||||
end
|
||||
c, s, r = givensAlgorithm(f, g)
|
||||
if i1 > i2
|
||||
s = -conj(s)
|
||||
i1,i2 = i2,i1
|
||||
end
|
||||
Givens(i1, i2, convert(T, c), convert(T, s)), r
|
||||
end
|
||||
"""
|
||||
givens(A::AbstractArray, i1::Integer, i2::Integer, j::Integer) -> (G::Givens, r)
|
||||
|
||||
Computes the Givens rotation `G` and scalar `r` such that the result of the multiplication
|
||||
```
|
||||
B = G*A
|
||||
```
|
||||
has the property that
|
||||
```
|
||||
B[i1,j] = r
|
||||
B[i2,j] = 0
|
||||
```
|
||||
|
||||
See also: [`LinAlg.Givens`](@ref)
|
||||
"""
|
||||
givens(A::AbstractMatrix, i1::Integer, i2::Integer, j::Integer) =
|
||||
givens(A[i1,j], A[i2,j],i1,i2)
|
||||
|
||||
|
||||
"""
|
||||
givens(x::AbstractVector, i1::Integer, i2::Integer) -> (G::Givens, r)
|
||||
|
||||
Computes the Givens rotation `G` and scalar `r` such that the result of the multiplication
|
||||
```
|
||||
B = G*x
|
||||
```
|
||||
has the property that
|
||||
```
|
||||
B[i1] = r
|
||||
B[i2] = 0
|
||||
```
|
||||
|
||||
See also: [`LinAlg.Givens`](@ref)
|
||||
"""
|
||||
givens(x::AbstractVector, i1::Integer, i2::Integer) =
|
||||
givens(x[i1], x[i2], i1, i2)
|
||||
|
||||
|
||||
function getindex(G::Givens, i::Integer, j::Integer)
|
||||
if i == j
|
||||
if i == G.i1 || i == G.i2
|
||||
G.c
|
||||
else
|
||||
oneunit(G.c)
|
||||
end
|
||||
elseif i == G.i1 && j == G.i2
|
||||
G.s
|
||||
elseif i == G.i2 && j == G.i1
|
||||
-conj(G.s)
|
||||
else
|
||||
zero(G.s)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
A_mul_B!(G1::Givens, G2::Givens) = error("Operation not supported. Consider *")
|
||||
|
||||
function A_mul_B!(G::Givens, A::AbstractVecOrMat)
|
||||
m, n = size(A, 1), size(A, 2)
|
||||
if G.i2 > m
|
||||
throw(DimensionMismatch("column indices for rotation are outside the matrix"))
|
||||
end
|
||||
@inbounds @simd for i = 1:n
|
||||
a1, a2 = A[G.i1,i], A[G.i2,i]
|
||||
A[G.i1,i] = G.c *a1 + G.s*a2
|
||||
A[G.i2,i] = -conj(G.s)*a1 + G.c*a2
|
||||
end
|
||||
return A
|
||||
end
|
||||
function A_mul_Bc!(A::AbstractMatrix, G::Givens)
|
||||
m, n = size(A, 1), size(A, 2)
|
||||
if G.i2 > n
|
||||
throw(DimensionMismatch("column indices for rotation are outside the matrix"))
|
||||
end
|
||||
@inbounds @simd for i = 1:m
|
||||
a1, a2 = A[i,G.i1], A[i,G.i2]
|
||||
A[i,G.i1] = a1*G.c + a2*conj(G.s)
|
||||
A[i,G.i2] = -a1*G.s + a2*G.c
|
||||
end
|
||||
return A
|
||||
end
|
||||
function A_mul_B!(G::Givens, R::Rotation)
|
||||
push!(R.rotations, G)
|
||||
return R
|
||||
end
|
||||
function A_mul_B!(R::Rotation, A::AbstractMatrix)
|
||||
@inbounds for i = 1:length(R.rotations)
|
||||
A_mul_B!(R.rotations[i], A)
|
||||
end
|
||||
return A
|
||||
end
|
||||
function A_mul_Bc!(A::AbstractMatrix, R::Rotation)
|
||||
@inbounds for i = 1:length(R.rotations)
|
||||
A_mul_Bc!(A, R.rotations[i])
|
||||
end
|
||||
return A
|
||||
end
|
||||
*(G1::Givens{T}, G2::Givens{T}) where {T} = Rotation(push!(push!(Givens{T}[], G2), G1))
|
||||
@@ -0,0 +1,115 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
struct Hessenberg{T,S<:AbstractMatrix} <: Factorization{T}
|
||||
factors::S
|
||||
τ::Vector{T}
|
||||
Hessenberg{T,S}(factors::AbstractMatrix{T}, τ::Vector{T}) where {T,S<:AbstractMatrix} =
|
||||
new(factors, τ)
|
||||
end
|
||||
Hessenberg(factors::AbstractMatrix{T}, τ::Vector{T}) where {T} = Hessenberg{T,typeof(factors)}(factors, τ)
|
||||
|
||||
Hessenberg(A::StridedMatrix) = Hessenberg(LAPACK.gehrd!(A)...)
|
||||
|
||||
|
||||
"""
|
||||
hessfact!(A) -> Hessenberg
|
||||
|
||||
`hessfact!` is the same as [`hessfact`](@ref), but saves space by overwriting
|
||||
the input `A`, instead of creating a copy.
|
||||
"""
|
||||
hessfact!(A::StridedMatrix{<:BlasFloat}) = Hessenberg(A)
|
||||
|
||||
hessfact(A::StridedMatrix{<:BlasFloat}) = hessfact!(copy(A))
|
||||
|
||||
"""
|
||||
hessfact(A) -> Hessenberg
|
||||
|
||||
Compute the Hessenberg decomposition of `A` and return a `Hessenberg` object. If `F` is the
|
||||
factorization object, the unitary matrix can be accessed with `F[:Q]` and the Hessenberg
|
||||
matrix with `F[:H]`. When `Q` is extracted, the resulting type is the `HessenbergQ` object,
|
||||
and may be converted to a regular matrix with [`convert(Array, _)`](@ref)
|
||||
(or `Array(_)` for short).
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [4. 9. 7.; 4. 4. 1.; 4. 3. 2.]
|
||||
3×3 Array{Float64,2}:
|
||||
4.0 9.0 7.0
|
||||
4.0 4.0 1.0
|
||||
4.0 3.0 2.0
|
||||
|
||||
julia> F = hessfact(A);
|
||||
|
||||
julia> F[:Q] * F[:H] * F[:Q]'
|
||||
3×3 Array{Float64,2}:
|
||||
4.0 9.0 7.0
|
||||
4.0 4.0 1.0
|
||||
4.0 3.0 2.0
|
||||
```
|
||||
"""
|
||||
function hessfact(A::StridedMatrix{T}) where T
|
||||
S = promote_type(Float32, typeof(zero(T)/norm(one(T))))
|
||||
return hessfact!(copy_oftype(A, S))
|
||||
end
|
||||
|
||||
struct HessenbergQ{T,S<:AbstractMatrix} <: AbstractMatrix{T}
|
||||
factors::S
|
||||
τ::Vector{T}
|
||||
HessenbergQ{T,S}(factors::AbstractMatrix{T}, τ::Vector{T}) where {T,S<:AbstractMatrix} = new(factors, τ)
|
||||
end
|
||||
HessenbergQ(factors::AbstractMatrix{T}, τ::Vector{T}) where {T} = HessenbergQ{T,typeof(factors)}(factors, τ)
|
||||
HessenbergQ(A::Hessenberg) = HessenbergQ(A.factors, A.τ)
|
||||
size(A::HessenbergQ, d) = size(A.factors, d)
|
||||
size(A::HessenbergQ) = size(A.factors)
|
||||
|
||||
function getindex(A::Hessenberg, d::Symbol)
|
||||
d == :Q && return HessenbergQ(A)
|
||||
d == :H && return triu(A.factors, -1)
|
||||
throw(KeyError(d))
|
||||
end
|
||||
|
||||
function getindex(A::HessenbergQ, i::Integer, j::Integer)
|
||||
x = zeros(eltype(A), size(A, 1))
|
||||
x[i] = 1
|
||||
y = zeros(eltype(A), size(A, 2))
|
||||
y[j] = 1
|
||||
return dot(x, A_mul_B!(A, y))
|
||||
end
|
||||
|
||||
## reconstruct the original matrix
|
||||
convert(::Type{Matrix}, A::HessenbergQ{<:BlasFloat}) = LAPACK.orghr!(1, size(A.factors, 1), copy(A.factors), A.τ)
|
||||
convert(::Type{Array}, A::HessenbergQ) = convert(Matrix, A)
|
||||
full(A::HessenbergQ) = convert(Array, A)
|
||||
convert(::Type{AbstractMatrix}, F::Hessenberg) = (fq = Array(F[:Q]); (fq * F[:H]) * fq')
|
||||
convert(::Type{AbstractArray}, F::Hessenberg) = convert(AbstractMatrix, F)
|
||||
convert(::Type{Matrix}, F::Hessenberg) = convert(Array, convert(AbstractArray, F))
|
||||
convert(::Type{Array}, F::Hessenberg) = convert(Matrix, F)
|
||||
full(F::Hessenberg) = convert(AbstractArray, F)
|
||||
|
||||
A_mul_B!(Q::HessenbergQ{T}, X::StridedVecOrMat{T}) where {T<:BlasFloat} =
|
||||
LAPACK.ormhr!('L', 'N', 1, size(Q.factors, 1), Q.factors, Q.τ, X)
|
||||
A_mul_B!(X::StridedMatrix{T}, Q::HessenbergQ{T}) where {T<:BlasFloat} =
|
||||
LAPACK.ormhr!('R', 'N', 1, size(Q.factors, 1), Q.factors, Q.τ, X)
|
||||
Ac_mul_B!(Q::HessenbergQ{T}, X::StridedVecOrMat{T}) where {T<:BlasFloat} =
|
||||
LAPACK.ormhr!('L', ifelse(T<:Real, 'T', 'C'), 1, size(Q.factors, 1), Q.factors, Q.τ, X)
|
||||
A_mul_Bc!(X::StridedMatrix{T}, Q::HessenbergQ{T}) where {T<:BlasFloat} =
|
||||
LAPACK.ormhr!('R', ifelse(T<:Real, 'T', 'C'), 1, size(Q.factors, 1), Q.factors, Q.τ, X)
|
||||
|
||||
|
||||
function (*)(Q::HessenbergQ{T}, X::StridedVecOrMat{S}) where {T,S}
|
||||
TT = typeof(zero(T)*zero(S) + zero(T)*zero(S))
|
||||
return A_mul_B!(Q, copy_oftype(X, TT))
|
||||
end
|
||||
function (*)(X::StridedVecOrMat{S}, Q::HessenbergQ{T}) where {T,S}
|
||||
TT = typeof(zero(T)*zero(S) + zero(T)*zero(S))
|
||||
return A_mul_B!(copy_oftype(X, TT), Q)
|
||||
end
|
||||
function Ac_mul_B(Q::HessenbergQ{T}, X::StridedVecOrMat{S}) where {T,S}
|
||||
TT = typeof(zero(T)*zero(S) + zero(T)*zero(S))
|
||||
return Ac_mul_B!(Q, copy_oftype(X, TT))
|
||||
end
|
||||
function A_mul_Bc(X::StridedVecOrMat{S}, Q::HessenbergQ{T}) where {T,S}
|
||||
TT = typeof(zero(T)*zero(S) + zero(T)*zero(S))
|
||||
return A_mul_Bc!(copy_oftype(X, TT), Q)
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,91 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
struct LDLt{T,S<:AbstractMatrix} <: Factorization{T}
|
||||
data::S
|
||||
end
|
||||
|
||||
size(S::LDLt) = size(S.data)
|
||||
size(S::LDLt, i::Integer) = size(S.data, i)
|
||||
|
||||
convert(::Type{LDLt{T,S}}, F::LDLt) where {T,S} = LDLt{T,S}(convert(S, F.data))
|
||||
# NOTE: the annotaion <:AbstractMatrix shouldn't be necessary, it is introduced
|
||||
# to avoid an ambiguity warning (see issue #6383)
|
||||
convert(::Type{LDLt{T}}, F::LDLt{S,U}) where {T,S,U<:AbstractMatrix} = convert(LDLt{T,U}, F)
|
||||
|
||||
convert(::Type{Factorization{T}}, F::LDLt{T}) where {T} = F
|
||||
convert(::Type{Factorization{T}}, F::LDLt{S,U}) where {T,S,U} = convert(LDLt{T,U}, F)
|
||||
|
||||
# SymTridiagonal
|
||||
"""
|
||||
ldltfact!(S::SymTridiagonal) -> LDLt
|
||||
|
||||
Same as [`ldltfact`](@ref), but saves space by overwriting the input `A`, instead of creating a copy.
|
||||
"""
|
||||
function ldltfact!(S::SymTridiagonal{T}) where T<:Real
|
||||
n = size(S,1)
|
||||
d = S.dv
|
||||
e = S.ev
|
||||
@inbounds @simd for i = 1:n-1
|
||||
e[i] /= d[i]
|
||||
d[i+1] -= abs2(e[i])*d[i]
|
||||
end
|
||||
return LDLt{T,SymTridiagonal{T}}(S)
|
||||
end
|
||||
|
||||
"""
|
||||
ldltfact(S::SymTridiagonal) -> LDLt
|
||||
|
||||
Compute an `LDLt` factorization of a real symmetric tridiagonal matrix such that `A = L*Diagonal(d)*L'`
|
||||
where `L` is a unit lower triangular matrix and `d` is a vector. The main use of an `LDLt`
|
||||
factorization `F = ldltfact(A)` is to solve the linear system of equations `Ax = b` with `F\\b`.
|
||||
"""
|
||||
function ldltfact(M::SymTridiagonal{T}) where T
|
||||
S = typeof(zero(T)/one(T))
|
||||
return S == T ? ldltfact!(copy(M)) : ldltfact!(convert(SymTridiagonal{S}, M))
|
||||
end
|
||||
|
||||
factorize(S::SymTridiagonal) = ldltfact(S)
|
||||
|
||||
function A_ldiv_B!(S::LDLt{T,SymTridiagonal{T}}, B::AbstractVecOrMat{T}) where T
|
||||
n, nrhs = size(B, 1), size(B, 2)
|
||||
if size(S,1) != n
|
||||
throw(DimensionMismatch("Matrix has dimensions $(size(S)) but right hand side has first dimension $n"))
|
||||
end
|
||||
d = S.data.dv
|
||||
l = S.data.ev
|
||||
@inbounds begin
|
||||
for i = 2:n
|
||||
li1 = l[i-1]
|
||||
@simd for j = 1:nrhs
|
||||
B[i,j] -= li1*B[i-1,j]
|
||||
end
|
||||
end
|
||||
dn = d[n]
|
||||
@simd for j = 1:nrhs
|
||||
B[n,j] /= dn
|
||||
end
|
||||
for i = n-1:-1:1
|
||||
di = d[i]
|
||||
li = l[i]
|
||||
@simd for j = 1:nrhs
|
||||
B[i,j] /= di
|
||||
B[i,j] -= li*B[i+1,j]
|
||||
end
|
||||
end
|
||||
end
|
||||
return B
|
||||
end
|
||||
|
||||
# Conversion methods
|
||||
function convert(::Type{SymTridiagonal}, F::LDLt)
|
||||
e = copy(F.data.ev)
|
||||
d = copy(F.data.dv)
|
||||
e .*= d[1:end-1]
|
||||
d[2:end] += e .* F.data.ev
|
||||
SymTridiagonal(d, e)
|
||||
end
|
||||
convert(::Type{AbstractMatrix}, F::LDLt) = convert(SymTridiagonal, F)
|
||||
convert(::Type{AbstractArray}, F::LDLt) = convert(AbstractMatrix, F)
|
||||
convert(::Type{Matrix}, F::LDLt) = convert(Array, convert(AbstractArray, F))
|
||||
convert(::Type{Array}, F::LDLt) = convert(Matrix, F)
|
||||
full(F::LDLt) = convert(AbstractArray, F)
|
||||
@@ -0,0 +1,294 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
module LinAlg
|
||||
|
||||
import Base: \, /, *, ^, +, -, ==
|
||||
import Base: A_mul_Bt, At_ldiv_Bt, A_rdiv_Bc, At_ldiv_B, Ac_mul_Bc, A_mul_Bc, Ac_mul_B,
|
||||
Ac_ldiv_B, Ac_ldiv_Bc, At_mul_Bt, A_rdiv_Bt, At_mul_B
|
||||
import Base: USE_BLAS64, abs, big, broadcast, ceil, conj, convert, copy, copy!,
|
||||
ctranspose, eltype, eye, findmax, findmin, fill!, floor, full, getindex,
|
||||
hcat, imag, indices, inv, isapprox, kron, length, IndexStyle, map,
|
||||
ndims, oneunit, parent, power_by_squaring, print_matrix, promote_rule, real, round,
|
||||
setindex!, show, similar, size, transpose, trunc, typed_hcat
|
||||
using Base: promote_op, _length, iszero, @pure, @propagate_inbounds, IndexLinear,
|
||||
reduce, hvcat_fill, typed_vcat, promote_typeof
|
||||
# We use `_length` because of non-1 indices; releases after julia 0.5
|
||||
# can go back to `length`. `_length(A)` is equivalent to `length(linearindices(A))`.
|
||||
|
||||
export
|
||||
# Modules
|
||||
LAPACK,
|
||||
BLAS,
|
||||
|
||||
# Types
|
||||
RowVector,
|
||||
ConjArray,
|
||||
ConjVector,
|
||||
ConjMatrix,
|
||||
SymTridiagonal,
|
||||
Tridiagonal,
|
||||
Bidiagonal,
|
||||
Factorization,
|
||||
BunchKaufman,
|
||||
Cholesky,
|
||||
CholeskyPivoted,
|
||||
Eigen,
|
||||
GeneralizedEigen,
|
||||
GeneralizedSVD,
|
||||
GeneralizedSchur,
|
||||
Hessenberg,
|
||||
LU,
|
||||
LDLt,
|
||||
QR,
|
||||
QRPivoted,
|
||||
LQ,
|
||||
Schur,
|
||||
SVD,
|
||||
Hermitian,
|
||||
Symmetric,
|
||||
LowerTriangular,
|
||||
UpperTriangular,
|
||||
Diagonal,
|
||||
UniformScaling,
|
||||
|
||||
# Functions
|
||||
axpy!,
|
||||
bkfact,
|
||||
bkfact!,
|
||||
chol,
|
||||
cholfact,
|
||||
cholfact!,
|
||||
cond,
|
||||
condskeel,
|
||||
copy!,
|
||||
copy_transpose!,
|
||||
cross,
|
||||
ctranspose,
|
||||
ctranspose!,
|
||||
det,
|
||||
diag,
|
||||
diagind,
|
||||
diagm,
|
||||
diff,
|
||||
dot,
|
||||
eig,
|
||||
eigfact,
|
||||
eigfact!,
|
||||
eigmax,
|
||||
eigmin,
|
||||
eigs,
|
||||
eigvals,
|
||||
eigvals!,
|
||||
eigvecs,
|
||||
expm,
|
||||
eye,
|
||||
factorize,
|
||||
givens,
|
||||
gradient,
|
||||
hessfact,
|
||||
hessfact!,
|
||||
isdiag,
|
||||
ishermitian,
|
||||
isposdef,
|
||||
isposdef!,
|
||||
issymmetric,
|
||||
istril,
|
||||
istriu,
|
||||
kron,
|
||||
ldltfact!,
|
||||
ldltfact,
|
||||
linreg,
|
||||
logabsdet,
|
||||
logdet,
|
||||
logm,
|
||||
lu,
|
||||
lufact,
|
||||
lufact!,
|
||||
lyap,
|
||||
norm,
|
||||
normalize,
|
||||
normalize!,
|
||||
nullspace,
|
||||
ordschur!,
|
||||
ordschur,
|
||||
peakflops,
|
||||
pinv,
|
||||
qr,
|
||||
qrfact!,
|
||||
qrfact,
|
||||
lq,
|
||||
lqfact!,
|
||||
lqfact,
|
||||
rank,
|
||||
scale!,
|
||||
schur,
|
||||
schurfact!,
|
||||
schurfact,
|
||||
sqrtm,
|
||||
svd,
|
||||
svdfact!,
|
||||
svdfact,
|
||||
svds,
|
||||
svdvals!,
|
||||
svdvals,
|
||||
sylvester,
|
||||
trace,
|
||||
transpose,
|
||||
transpose!,
|
||||
transpose_type,
|
||||
tril,
|
||||
triu,
|
||||
tril!,
|
||||
triu!,
|
||||
vecdot,
|
||||
vecnorm,
|
||||
|
||||
# Operators
|
||||
\,
|
||||
/,
|
||||
A_ldiv_B!,
|
||||
A_ldiv_Bc,
|
||||
A_ldiv_Bt,
|
||||
A_mul_B!,
|
||||
A_mul_Bc,
|
||||
A_mul_Bc!,
|
||||
A_mul_Bt,
|
||||
A_mul_Bt!,
|
||||
A_rdiv_Bc,
|
||||
A_rdiv_Bt,
|
||||
Ac_ldiv_B,
|
||||
Ac_ldiv_Bc,
|
||||
Ac_ldiv_B!,
|
||||
Ac_mul_B,
|
||||
Ac_mul_B!,
|
||||
Ac_mul_Bc,
|
||||
Ac_mul_Bc!,
|
||||
Ac_rdiv_B,
|
||||
Ac_rdiv_Bc,
|
||||
At_ldiv_B,
|
||||
At_ldiv_Bt,
|
||||
At_ldiv_B!,
|
||||
At_mul_B,
|
||||
At_mul_B!,
|
||||
At_mul_Bt,
|
||||
At_mul_Bt!,
|
||||
At_rdiv_B,
|
||||
At_rdiv_Bt,
|
||||
|
||||
# Constants
|
||||
I
|
||||
|
||||
const BlasFloat = Union{Float64,Float32,Complex128,Complex64}
|
||||
const BlasReal = Union{Float64,Float32}
|
||||
const BlasComplex = Union{Complex128,Complex64}
|
||||
|
||||
if USE_BLAS64
|
||||
const BlasInt = Int64
|
||||
else
|
||||
const BlasInt = Int32
|
||||
end
|
||||
|
||||
# Check that stride of matrix/vector is 1
|
||||
# Writing like this to avoid splatting penalty when called with multiple arguments,
|
||||
# see PR 16416
|
||||
@inline chkstride1(A...) = _chkstride1(true, A...)
|
||||
@noinline _chkstride1(ok::Bool) = ok || error("matrix does not have contiguous columns")
|
||||
@inline _chkstride1(ok::Bool, A, B...) = _chkstride1(ok & (stride(A, 1) == 1), B...)
|
||||
|
||||
"""
|
||||
LinAlg.checksquare(A)
|
||||
|
||||
Check that a matrix is square, then return its common dimension.
|
||||
For multiple arguments, return a vector.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = ones(4,4); B = zeros(5,5);
|
||||
|
||||
julia> LinAlg.checksquare(A, B)
|
||||
2-element Array{Int64,1}:
|
||||
4
|
||||
5
|
||||
```
|
||||
"""
|
||||
function checksquare(A)
|
||||
m,n = size(A)
|
||||
m == n || throw(DimensionMismatch("matrix is not square: dimensions are $(size(A))"))
|
||||
m
|
||||
end
|
||||
|
||||
function checksquare(A...)
|
||||
sizes = Int[]
|
||||
for a in A
|
||||
size(a,1)==size(a,2) || throw(DimensionMismatch("matrix is not square: dimensions are $(size(a))"))
|
||||
push!(sizes, size(a,1))
|
||||
end
|
||||
return sizes
|
||||
end
|
||||
|
||||
function char_uplo(uplo::Symbol)
|
||||
if uplo == :U
|
||||
'U'
|
||||
elseif uplo == :L
|
||||
'L'
|
||||
else
|
||||
throw(ArgumentError("uplo argument must be either :U (upper) or :L (lower)"))
|
||||
end
|
||||
end
|
||||
|
||||
copy_oftype(A::AbstractArray{T}, ::Type{T}) where {T} = copy(A)
|
||||
copy_oftype(A::AbstractArray{T,N}, ::Type{S}) where {T,N,S} = convert(AbstractArray{S,N}, A)
|
||||
|
||||
include("conjarray.jl")
|
||||
include("transpose.jl")
|
||||
include("rowvector.jl")
|
||||
|
||||
include("exceptions.jl")
|
||||
include("generic.jl")
|
||||
|
||||
include("blas.jl")
|
||||
import .BLAS: gemv! # consider renaming gemv! in matmul
|
||||
include("matmul.jl")
|
||||
include("lapack.jl")
|
||||
|
||||
include("dense.jl")
|
||||
include("tridiag.jl")
|
||||
include("triangular.jl")
|
||||
|
||||
include("factorization.jl")
|
||||
include("qr.jl")
|
||||
include("hessenberg.jl")
|
||||
include("lq.jl")
|
||||
include("eigen.jl")
|
||||
include("svd.jl")
|
||||
include("symmetric.jl")
|
||||
include("cholesky.jl")
|
||||
include("lu.jl")
|
||||
include("bunchkaufman.jl")
|
||||
include("diagonal.jl")
|
||||
include("bidiag.jl")
|
||||
include("uniformscaling.jl")
|
||||
include("givens.jl")
|
||||
include("special.jl")
|
||||
include("bitarray.jl")
|
||||
include("ldlt.jl")
|
||||
include("schur.jl")
|
||||
|
||||
|
||||
include("arpack.jl")
|
||||
include("arnoldi.jl")
|
||||
|
||||
function __init__()
|
||||
try
|
||||
BLAS.check()
|
||||
if BLAS.vendor() == :mkl
|
||||
ccall((:MKL_Set_Interface_Layer, Base.libblas_name), Void, (Cint,), USE_BLAS64 ? 1 : 0)
|
||||
end
|
||||
catch ex
|
||||
Base.showerror_nostdio(ex,
|
||||
"WARNING: Error during initialization of module LinAlg")
|
||||
end
|
||||
end
|
||||
|
||||
end # module LinAlg
|
||||
@@ -0,0 +1,231 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# LQ Factorizations
|
||||
|
||||
struct LQ{T,S<:AbstractMatrix} <: Factorization{T}
|
||||
factors::S
|
||||
τ::Vector{T}
|
||||
LQ{T,S}(factors::AbstractMatrix{T}, τ::Vector{T}) where {T,S<:AbstractMatrix} = new(factors, τ)
|
||||
end
|
||||
|
||||
struct LQPackedQ{T,S<:AbstractMatrix} <: AbstractMatrix{T}
|
||||
factors::Matrix{T}
|
||||
τ::Vector{T}
|
||||
LQPackedQ{T,S}(factors::AbstractMatrix{T}, τ::Vector{T}) where {T,S<:AbstractMatrix} = new(factors, τ)
|
||||
end
|
||||
|
||||
LQ(factors::AbstractMatrix{T}, τ::Vector{T}) where {T} = LQ{T,typeof(factors)}(factors, τ)
|
||||
LQPackedQ(factors::AbstractMatrix{T}, τ::Vector{T}) where {T} = LQPackedQ{T,typeof(factors)}(factors, τ)
|
||||
|
||||
"""
|
||||
lqfact!(A) -> LQ
|
||||
|
||||
Compute the LQ factorization of `A`, using the input
|
||||
matrix as a workspace. See also [`lq`](@ref).
|
||||
"""
|
||||
lqfact!(A::StridedMatrix{<:BlasFloat}) = LQ(LAPACK.gelqf!(A)...)
|
||||
"""
|
||||
lqfact(A) -> LQ
|
||||
|
||||
Compute the LQ factorization of `A`. See also [`lq`](@ref).
|
||||
"""
|
||||
lqfact(A::StridedMatrix{<:BlasFloat}) = lqfact!(copy(A))
|
||||
lqfact(x::Number) = lqfact(fill(x,1,1))
|
||||
|
||||
"""
|
||||
lq(A; [thin=true]) -> L, Q
|
||||
|
||||
Perform an LQ factorization of `A` such that `A = L*Q`. The
|
||||
default is to compute a thin factorization. The LQ factorization
|
||||
is the QR factorization of `A.'`. `L` is not extended with
|
||||
zeros if the full `Q` is requested.
|
||||
"""
|
||||
function lq(A::Union{Number, AbstractMatrix}; thin::Bool=true)
|
||||
F = lqfact(A)
|
||||
F[:L], full(F[:Q], thin=thin)
|
||||
end
|
||||
|
||||
copy(A::LQ) = LQ(copy(A.factors), copy(A.τ))
|
||||
|
||||
convert(::Type{LQ{T}},A::LQ) where {T} = LQ(convert(AbstractMatrix{T}, A.factors), convert(Vector{T}, A.τ))
|
||||
convert(::Type{Factorization{T}}, A::LQ{T}) where {T} = A
|
||||
convert(::Type{Factorization{T}}, A::LQ) where {T} = convert(LQ{T}, A)
|
||||
convert(::Type{AbstractMatrix}, A::LQ) = A[:L]*A[:Q]
|
||||
convert(::Type{AbstractArray}, A::LQ) = convert(AbstractMatrix, A)
|
||||
convert(::Type{Matrix}, A::LQ) = convert(Array, convert(AbstractArray, A))
|
||||
convert(::Type{Array}, A::LQ) = convert(Matrix, A)
|
||||
full(A::LQ) = convert(AbstractArray, A)
|
||||
|
||||
ctranspose(A::LQ{T}) where {T} = QR{T,typeof(A.factors)}(A.factors', A.τ)
|
||||
|
||||
function getindex(A::LQ, d::Symbol)
|
||||
m, n = size(A)
|
||||
if d == :L
|
||||
return tril!(A.factors[1:m, 1:min(m,n)])
|
||||
elseif d == :Q
|
||||
return LQPackedQ(A.factors,A.τ)
|
||||
else
|
||||
throw(KeyError(d))
|
||||
end
|
||||
end
|
||||
|
||||
getindex(A::LQPackedQ, i::Integer, j::Integer) =
|
||||
A_mul_B!(A, setindex!(zeros(eltype(A), size(A, 2)), 1, j))[i]
|
||||
|
||||
getq(A::LQ) = LQPackedQ(A.factors, A.τ)
|
||||
|
||||
function show(io::IO, C::LQ)
|
||||
println(io, "$(typeof(C)) with factors L and Q:")
|
||||
show(io, C[:L])
|
||||
println(io)
|
||||
show(io, C[:Q])
|
||||
end
|
||||
|
||||
convert(::Type{LQPackedQ{T}}, Q::LQPackedQ) where {T} = LQPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(Vector{T}, Q.τ))
|
||||
convert(::Type{AbstractMatrix{T}}, Q::LQPackedQ) where {T} = convert(LQPackedQ{T}, Q)
|
||||
convert(::Type{Matrix}, A::LQPackedQ) = LAPACK.orglq!(copy(A.factors),A.τ)
|
||||
convert(::Type{Array}, A::LQPackedQ) = convert(Matrix, A)
|
||||
function full{T}(A::LQPackedQ{T}; thin::Bool = true)
|
||||
#= We construct the full eye here, even though it seems inefficient, because
|
||||
every element in the output matrix is a function of all the elements of
|
||||
the input matrix. The eye is modified by the elementary reflectors held
|
||||
in A, so this is not just an indexing operation. Note that in general
|
||||
explicitly constructing Q, rather than using the ldiv or mult methods,
|
||||
may be a wasteful allocation. =#
|
||||
if thin
|
||||
convert(Array, A)
|
||||
else
|
||||
A_mul_B!(A, eye(T, size(A.factors,2), size(A.factors,1)))
|
||||
end
|
||||
end
|
||||
|
||||
size(A::LQ, dim::Integer) = size(A.factors, dim)
|
||||
size(A::LQ) = size(A.factors)
|
||||
function size(A::LQPackedQ, dim::Integer)
|
||||
if 0 < dim && dim <= 2
|
||||
return size(A.factors, dim)
|
||||
elseif 0 < dim && dim > 2
|
||||
return 1
|
||||
else
|
||||
throw(BoundsError())
|
||||
end
|
||||
end
|
||||
|
||||
size(A::LQPackedQ) = size(A.factors)
|
||||
|
||||
## Multiplication by LQ
|
||||
A_mul_B!(A::LQ{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = A[:L]*LAPACK.ormlq!('L','N',A.factors,A.τ,B)
|
||||
A_mul_B!(A::LQ{T}, B::QR{T}) where {T<:BlasFloat} = A[:L]*LAPACK.ormlq!('L','N',A.factors,A.τ,full(B))
|
||||
A_mul_B!(A::QR{T}, B::LQ{T}) where {T<:BlasFloat} = A_mul_B!(zeros(full(A)), full(A), full(B))
|
||||
function *(A::LQ{TA}, B::StridedVecOrMat{TB}) where {TA,TB}
|
||||
TAB = promote_type(TA, TB)
|
||||
A_mul_B!(convert(Factorization{TAB},A), copy_oftype(B, TAB))
|
||||
end
|
||||
function *(A::LQ{TA},B::QR{TB}) where {TA,TB}
|
||||
TAB = promote_type(TA, TB)
|
||||
A_mul_B!(convert(Factorization{TAB},A), convert(Factorization{TAB},B))
|
||||
end
|
||||
function *(A::QR{TA},B::LQ{TB}) where {TA,TB}
|
||||
TAB = promote_type(TA, TB)
|
||||
A_mul_B!(convert(Factorization{TAB},A), convert(Factorization{TAB},B))
|
||||
end
|
||||
|
||||
## Multiplication by Q
|
||||
### QB
|
||||
A_mul_B!(A::LQPackedQ{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.ormlq!('L','N',A.factors,A.τ,B)
|
||||
function (*)(A::LQPackedQ, B::StridedVecOrMat)
|
||||
TAB = promote_type(eltype(A), eltype(B))
|
||||
A_mul_B!(convert(AbstractMatrix{TAB}, A), copy_oftype(B, TAB))
|
||||
end
|
||||
|
||||
### QcB
|
||||
Ac_mul_B!(A::LQPackedQ{T}, B::StridedVecOrMat{T}) where {T<:BlasReal} = LAPACK.ormlq!('L','T',A.factors,A.τ,B)
|
||||
Ac_mul_B!(A::LQPackedQ{T}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = LAPACK.ormlq!('L','C',A.factors,A.τ,B)
|
||||
function Ac_mul_B(A::LQPackedQ, B::StridedVecOrMat)
|
||||
TAB = promote_type(eltype(A), eltype(B))
|
||||
if size(B,1) == size(A.factors,2)
|
||||
Ac_mul_B!(convert(AbstractMatrix{TAB}, A), copy_oftype(B, TAB))
|
||||
elseif size(B,1) == size(A.factors,1)
|
||||
Ac_mul_B!(convert(AbstractMatrix{TAB}, A), [B; zeros(TAB, size(A.factors, 2) - size(A.factors, 1), size(B, 2))])
|
||||
else
|
||||
throw(DimensionMismatch("first dimension of B, $(size(B,1)), must equal one of the dimensions of A, $(size(A))"))
|
||||
end
|
||||
end
|
||||
|
||||
### QBc/QcBc
|
||||
for (f1, f2) in ((:A_mul_Bc, :A_mul_B!),
|
||||
(:Ac_mul_Bc, :Ac_mul_B!))
|
||||
@eval begin
|
||||
function ($f1)(A::LQPackedQ, B::StridedVecOrMat)
|
||||
TAB = promote_type(eltype(A), eltype(B))
|
||||
BB = similar(B, TAB, (size(B, 2), size(B, 1)))
|
||||
ctranspose!(BB, B)
|
||||
return ($f2)(A, BB)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
### AQ
|
||||
A_mul_B!(A::StridedMatrix{T}, B::LQPackedQ{T}) where {T<:BlasFloat} = LAPACK.ormlq!('R', 'N', B.factors, B.τ, A)
|
||||
function *(A::StridedMatrix{TA}, B::LQPackedQ{TB}) where {TA,TB}
|
||||
TAB = promote_type(TA,TB)
|
||||
if size(B.factors,2) == size(A,2)
|
||||
A_mul_B!(copy_oftype(A, TAB),convert(AbstractMatrix{TAB},B))
|
||||
elseif size(B.factors,1) == size(A,2)
|
||||
A_mul_B!( [A zeros(TAB, size(A,1), size(B.factors,2)-size(B.factors,1))], convert(AbstractMatrix{TAB},B))
|
||||
else
|
||||
throw(DimensionMismatch("second dimension of A, $(size(A,2)), must equal one of the dimensions of B, $(size(B))"))
|
||||
end
|
||||
end
|
||||
|
||||
### AQc
|
||||
A_mul_Bc!(A::StridedMatrix{T}, B::LQPackedQ{T}) where {T<:BlasReal} = LAPACK.ormlq!('R','T',B.factors,B.τ,A)
|
||||
A_mul_Bc!(A::StridedMatrix{T}, B::LQPackedQ{T}) where {T<:BlasComplex} = LAPACK.ormlq!('R','C',B.factors,B.τ,A)
|
||||
function A_mul_Bc(A::StridedVecOrMat{TA}, B::LQPackedQ{TB}) where {TA<:Number,TB<:Number}
|
||||
TAB = promote_type(TA,TB)
|
||||
A_mul_Bc!(copy_oftype(A, TAB), convert(AbstractMatrix{TAB},(B)))
|
||||
end
|
||||
|
||||
### AcQ/AcQc
|
||||
for (f1, f2) in ((:Ac_mul_B, :A_mul_B!),
|
||||
(:Ac_mul_Bc, :A_mul_Bc!))
|
||||
@eval begin
|
||||
function ($f1)(A::StridedMatrix, B::LQPackedQ)
|
||||
TAB = promote_type(eltype(A), eltype(B))
|
||||
AA = similar(A, TAB, (size(A, 2), size(A, 1)))
|
||||
ctranspose!(AA, A)
|
||||
return ($f2)(AA, B)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function (\)(A::LQ{TA}, b::StridedVector{Tb}) where {TA,Tb}
|
||||
S = promote_type(TA,Tb)
|
||||
m = checksquare(A)
|
||||
m == length(b) || throw(DimensionMismatch("left hand side has $m rows, but right hand side has length $(length(b))"))
|
||||
AA = convert(Factorization{S}, A)
|
||||
x = A_ldiv_B!(AA, copy_oftype(b, S))
|
||||
return x
|
||||
end
|
||||
function (\)(A::LQ{TA},B::StridedMatrix{TB}) where {TA,TB}
|
||||
S = promote_type(TA,TB)
|
||||
m = checksquare(A)
|
||||
m == size(B,1) || throw(DimensionMismatch("left hand side has $m rows, but right hand side has $(size(B,1)) rows"))
|
||||
AA = convert(Factorization{S}, A)
|
||||
X = A_ldiv_B!(AA, copy_oftype(B, S))
|
||||
return X
|
||||
end
|
||||
# With a real lhs and complex rhs with the same precision, we can reinterpret
|
||||
# the complex rhs as a real rhs with twice the number of columns
|
||||
function (\)(F::LQ{T}, B::VecOrMat{Complex{T}}) where T<:BlasReal
|
||||
c2r = reshape(transpose(reinterpret(T, B, (2, length(B)))), size(B, 1), 2*size(B, 2))
|
||||
x = A_ldiv_B!(F, c2r)
|
||||
return reinterpret(Complex{T}, transpose(reshape(x, div(length(x), 2), 2)),
|
||||
isa(B, AbstractVector) ? (size(F,2),) : (size(F,2), size(B,2)))
|
||||
end
|
||||
|
||||
|
||||
function A_ldiv_B!(A::LQ{T}, B::StridedVecOrMat{T}) where T
|
||||
Ac_mul_B!(A[:Q], A_ldiv_B!(LowerTriangular(A[:L]),B))
|
||||
return B
|
||||
end
|
||||
@@ -0,0 +1,563 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
####################
|
||||
# LU Factorization #
|
||||
####################
|
||||
struct LU{T,S<:AbstractMatrix} <: Factorization{T}
|
||||
factors::S
|
||||
ipiv::Vector{BlasInt}
|
||||
info::BlasInt
|
||||
LU{T,S}(factors::AbstractMatrix{T}, ipiv::Vector{BlasInt}, info::BlasInt) where {T,S} = new(factors, ipiv, info)
|
||||
end
|
||||
LU(factors::AbstractMatrix{T}, ipiv::Vector{BlasInt}, info::BlasInt) where {T} = LU{T,typeof(factors)}(factors, ipiv, info)
|
||||
|
||||
# StridedMatrix
|
||||
function lufact!(A::StridedMatrix{T}, pivot::Union{Type{Val{false}}, Type{Val{true}}} = Val{true}) where T<:BlasFloat
|
||||
if pivot === Val{false}
|
||||
return generic_lufact!(A, pivot)
|
||||
end
|
||||
lpt = LAPACK.getrf!(A)
|
||||
return LU{T,typeof(A)}(lpt[1], lpt[2], lpt[3])
|
||||
end
|
||||
|
||||
"""
|
||||
lufact!(A, pivot=Val{true}) -> LU
|
||||
|
||||
`lufact!` is the same as [`lufact`](@ref), but saves space by overwriting the
|
||||
input `A`, instead of creating a copy. An [`InexactError`](@ref)
|
||||
exception is thrown if the factorization produces a number not representable by the
|
||||
element type of `A`, e.g. for integer types.
|
||||
"""
|
||||
lufact!(A::StridedMatrix, pivot::Union{Type{Val{false}}, Type{Val{true}}} = Val{true}) = generic_lufact!(A, pivot)
|
||||
function generic_lufact!(A::StridedMatrix{T}, ::Type{Val{Pivot}} = Val{true}) where {T,Pivot}
|
||||
m, n = size(A)
|
||||
minmn = min(m,n)
|
||||
info = 0
|
||||
ipiv = Vector{BlasInt}(minmn)
|
||||
@inbounds begin
|
||||
for k = 1:minmn
|
||||
# find index max
|
||||
kp = k
|
||||
if Pivot
|
||||
amax = real(zero(T))
|
||||
for i = k:m
|
||||
absi = abs(A[i,k])
|
||||
if absi > amax
|
||||
kp = i
|
||||
amax = absi
|
||||
end
|
||||
end
|
||||
end
|
||||
ipiv[k] = kp
|
||||
if A[kp,k] != 0
|
||||
if k != kp
|
||||
# Interchange
|
||||
for i = 1:n
|
||||
tmp = A[k,i]
|
||||
A[k,i] = A[kp,i]
|
||||
A[kp,i] = tmp
|
||||
end
|
||||
end
|
||||
# Scale first column
|
||||
Akkinv = inv(A[k,k])
|
||||
for i = k+1:m
|
||||
A[i,k] *= Akkinv
|
||||
end
|
||||
elseif info == 0
|
||||
info = k
|
||||
end
|
||||
# Update the rest
|
||||
for j = k+1:n
|
||||
for i = k+1:m
|
||||
A[i,j] -= A[i,k]*A[k,j]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
LU{T,typeof(A)}(A, ipiv, convert(BlasInt, info))
|
||||
end
|
||||
|
||||
# floating point types doesn't have to be promoted for LU, but should default to pivoting
|
||||
lufact(A::Union{AbstractMatrix{T}, AbstractMatrix{Complex{T}}},
|
||||
pivot::Union{Type{Val{false}}, Type{Val{true}}} = Val{true}) where {T<:AbstractFloat} =
|
||||
lufact!(copy(A), pivot)
|
||||
|
||||
# for all other types we must promote to a type which is stable under division
|
||||
"""
|
||||
lufact(A [,pivot=Val{true}]) -> F::LU
|
||||
|
||||
Compute the LU factorization of `A`.
|
||||
|
||||
In most cases, if `A` is a subtype `S` of `AbstractMatrix{T}` with an element
|
||||
type `T` supporting `+`, `-`, `*` and `/`, the return type is `LU{T,S{T}}`. If
|
||||
pivoting is chosen (default) the element type should also support `abs` and
|
||||
`<`.
|
||||
|
||||
The individual components of the factorization `F` can be accessed by indexing:
|
||||
|
||||
| Component | Description |
|
||||
|:----------|:------------------------------------|
|
||||
| `F[:L]` | `L` (lower triangular) part of `LU` |
|
||||
| `F[:U]` | `U` (upper triangular) part of `LU` |
|
||||
| `F[:p]` | (right) permutation `Vector` |
|
||||
| `F[:P]` | (right) permutation `Matrix` |
|
||||
|
||||
The relationship between `F` and `A` is
|
||||
|
||||
`F[:L]*F[:U] == A[F[:p], :]`
|
||||
|
||||
`F` further supports the following functions:
|
||||
|
||||
| Supported function | `LU` | `LU{T,Tridiagonal{T}}` |
|
||||
|:---------------------------------|:-----|:-----------------------|
|
||||
| [`/`](@ref) | ✓ | |
|
||||
| [`\\`](@ref) | ✓ | ✓ |
|
||||
| [`cond`](@ref) | ✓ | |
|
||||
| [`inv`](@ref) | ✓ | ✓ |
|
||||
| [`det`](@ref) | ✓ | ✓ |
|
||||
| [`logdet`](@ref) | ✓ | ✓ |
|
||||
| [`logabsdet`](@ref) | ✓ | ✓ |
|
||||
| [`size`](@ref) | ✓ | ✓ |
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [4 3; 6 3]
|
||||
2×2 Array{Int64,2}:
|
||||
4 3
|
||||
6 3
|
||||
|
||||
julia> F = lufact(A)
|
||||
Base.LinAlg.LU{Float64,Array{Float64,2}} with factors L and U:
|
||||
[1.0 0.0; 1.5 1.0]
|
||||
[4.0 3.0; 0.0 -1.5]
|
||||
|
||||
julia> F[:L] * F[:U] == A[F[:p], :]
|
||||
true
|
||||
```
|
||||
"""
|
||||
function lufact(A::AbstractMatrix{T}, pivot::Union{Type{Val{false}}, Type{Val{true}}}) where T
|
||||
S = typeof(zero(T)/one(T))
|
||||
AA = similar(A, S, size(A))
|
||||
copy!(AA, A)
|
||||
lufact!(AA, pivot)
|
||||
end
|
||||
# We can't assume an ordered field so we first try without pivoting
|
||||
function lufact(A::AbstractMatrix{T}) where T
|
||||
S = typeof(zero(T)/one(T))
|
||||
AA = similar(A, S, size(A))
|
||||
copy!(AA, A)
|
||||
F = lufact!(AA, Val{false})
|
||||
if F.info == 0
|
||||
return F
|
||||
else
|
||||
AA = similar(A, S, size(A))
|
||||
copy!(AA, A)
|
||||
return lufact!(AA, Val{true})
|
||||
end
|
||||
end
|
||||
|
||||
lufact(x::Number) = LU(fill(x, 1, 1), BlasInt[1], x == 0 ? one(BlasInt) : zero(BlasInt))
|
||||
lufact(F::LU) = F
|
||||
|
||||
lu(x::Number) = (one(x), x, 1)
|
||||
|
||||
"""
|
||||
lu(A, pivot=Val{true}) -> L, U, p
|
||||
|
||||
Compute the LU factorization of `A`, such that `A[p,:] = L*U`.
|
||||
By default, pivoting is used. This can be overridden by passing
|
||||
`Val{false}` for the second argument.
|
||||
|
||||
See also [`lufact`](@ref).
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [4. 3.; 6. 3.]
|
||||
2×2 Array{Float64,2}:
|
||||
4.0 3.0
|
||||
6.0 3.0
|
||||
|
||||
julia> L, U, p = lu(A)
|
||||
([1.0 0.0; 0.666667 1.0], [6.0 3.0; 0.0 1.0], [2, 1])
|
||||
|
||||
julia> A[p, :] == L * U
|
||||
true
|
||||
```
|
||||
"""
|
||||
function lu(A::AbstractMatrix, pivot::Union{Type{Val{false}}, Type{Val{true}}} = Val{true})
|
||||
F = lufact(A, pivot)
|
||||
F[:L], F[:U], F[:p]
|
||||
end
|
||||
|
||||
function convert(::Type{LU{T}}, F::LU) where T
|
||||
M = convert(AbstractMatrix{T}, F.factors)
|
||||
LU{T,typeof(M)}(M, F.ipiv, F.info)
|
||||
end
|
||||
convert(::Type{LU{T,S}}, F::LU) where {T,S} = LU{T,S}(convert(S, F.factors), F.ipiv, F.info)
|
||||
convert(::Type{Factorization{T}}, F::LU{T}) where {T} = F
|
||||
convert(::Type{Factorization{T}}, F::LU) where {T} = convert(LU{T}, F)
|
||||
|
||||
|
||||
size(A::LU) = size(A.factors)
|
||||
size(A::LU,n) = size(A.factors,n)
|
||||
|
||||
function ipiv2perm(v::AbstractVector{T}, maxi::Integer) where T
|
||||
p = T[1:maxi;]
|
||||
@inbounds for i in 1:length(v)
|
||||
p[i], p[v[i]] = p[v[i]], p[i]
|
||||
end
|
||||
return p
|
||||
end
|
||||
|
||||
function getindex(F::LU{T,<:StridedMatrix}, d::Symbol) where T
|
||||
m, n = size(F)
|
||||
if d == :L
|
||||
L = tril!(F.factors[1:m, 1:min(m,n)])
|
||||
for i = 1:min(m,n); L[i,i] = one(T); end
|
||||
return L
|
||||
elseif d == :U
|
||||
return triu!(F.factors[1:min(m,n), 1:n])
|
||||
elseif d == :p
|
||||
return ipiv2perm(F.ipiv, m)
|
||||
elseif d == :P
|
||||
return eye(T, m)[:,invperm(F[:p])]
|
||||
else
|
||||
throw(KeyError(d))
|
||||
end
|
||||
end
|
||||
|
||||
function show(io::IO, C::LU)
|
||||
println(io, "$(typeof(C)) with factors L and U:")
|
||||
show(io, C[:L])
|
||||
println(io)
|
||||
show(io, C[:U])
|
||||
end
|
||||
|
||||
A_ldiv_B!(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} =
|
||||
@assertnonsingular LAPACK.getrs!('N', A.factors, A.ipiv, B) A.info
|
||||
A_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, b::StridedVector) =
|
||||
A_ldiv_B!(UpperTriangular(A.factors),
|
||||
A_ldiv_B!(UnitLowerTriangular(A.factors), b[ipiv2perm(A.ipiv, length(b))]))
|
||||
A_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, B::StridedMatrix) =
|
||||
A_ldiv_B!(UpperTriangular(A.factors),
|
||||
A_ldiv_B!(UnitLowerTriangular(A.factors), B[ipiv2perm(A.ipiv, size(B, 1)),:]))
|
||||
|
||||
At_ldiv_B!(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} =
|
||||
@assertnonsingular LAPACK.getrs!('T', A.factors, A.ipiv, B) A.info
|
||||
At_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, b::StridedVector) =
|
||||
At_ldiv_B!(UnitLowerTriangular(A.factors),
|
||||
At_ldiv_B!(UpperTriangular(A.factors), b))[invperm(ipiv2perm(A.ipiv, length(b)))]
|
||||
At_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, B::StridedMatrix) =
|
||||
At_ldiv_B!(UnitLowerTriangular(A.factors),
|
||||
At_ldiv_B!(UpperTriangular(A.factors), B))[invperm(ipiv2perm(A.ipiv, size(B,1))),:]
|
||||
|
||||
Ac_ldiv_B!(F::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:Real} =
|
||||
At_ldiv_B!(F, B)
|
||||
Ac_ldiv_B!(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasComplex} =
|
||||
@assertnonsingular LAPACK.getrs!('C', A.factors, A.ipiv, B) A.info
|
||||
Ac_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, b::StridedVector) =
|
||||
Ac_ldiv_B!(UnitLowerTriangular(A.factors),
|
||||
Ac_ldiv_B!(UpperTriangular(A.factors), b))[invperm(ipiv2perm(A.ipiv, length(b)))]
|
||||
Ac_ldiv_B!(A::LU{<:Any,<:StridedMatrix}, B::StridedMatrix) =
|
||||
Ac_ldiv_B!(UnitLowerTriangular(A.factors),
|
||||
Ac_ldiv_B!(UpperTriangular(A.factors), B))[invperm(ipiv2perm(A.ipiv, size(B,1))),:]
|
||||
|
||||
At_ldiv_Bt(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} =
|
||||
@assertnonsingular LAPACK.getrs!('T', A.factors, A.ipiv, transpose(B)) A.info
|
||||
At_ldiv_Bt(A::LU, B::StridedVecOrMat) = At_ldiv_B(A, transpose(B))
|
||||
|
||||
Ac_ldiv_Bc(A::LU{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasComplex} =
|
||||
@assertnonsingular LAPACK.getrs!('C', A.factors, A.ipiv, ctranspose(B)) A.info
|
||||
Ac_ldiv_Bc(A::LU, B::StridedVecOrMat) = Ac_ldiv_B(A, ctranspose(B))
|
||||
|
||||
function det(A::LU{T}) where T
|
||||
n = checksquare(A)
|
||||
A.info > 0 && return zero(T)
|
||||
P = one(T)
|
||||
c = 0
|
||||
@inbounds for i = 1:n
|
||||
P *= A.factors[i,i]
|
||||
if A.ipiv[i] != i
|
||||
c += 1
|
||||
end
|
||||
end
|
||||
s = (isodd(c) ? -one(T) : one(T))
|
||||
return P * s
|
||||
end
|
||||
|
||||
function logabsdet(A::LU{T}) where T # return log(abs(det)) and sign(det)
|
||||
n = checksquare(A)
|
||||
A.info > 0 && return log(zero(real(T))), log(one(T))
|
||||
c = 0
|
||||
P = one(T)
|
||||
abs_det = zero(real(T))
|
||||
@inbounds for i = 1:n
|
||||
dg_ii = A.factors[i,i]
|
||||
P *= sign(dg_ii)
|
||||
if A.ipiv[i] != i
|
||||
c += 1
|
||||
end
|
||||
abs_det += log(abs(dg_ii))
|
||||
end
|
||||
s = ifelse(isodd(c), -one(real(T)), one(real(T))) * P
|
||||
abs_det, s
|
||||
end
|
||||
|
||||
inv!(A::LU{<:BlasFloat,<:StridedMatrix}) =
|
||||
@assertnonsingular LAPACK.getri!(A.factors, A.ipiv) A.info
|
||||
inv(A::LU{<:BlasFloat,<:StridedMatrix}) =
|
||||
inv!(LU(copy(A.factors), copy(A.ipiv), copy(A.info)))
|
||||
|
||||
cond(A::LU{<:BlasFloat,<:StridedMatrix}, p::Number) =
|
||||
inv(LAPACK.gecon!(p == 1 ? '1' : 'I', A.factors, norm((A[:L]*A[:U])[A[:p],:], p)))
|
||||
cond(A::LU, p::Number) = norm(A[:L]*A[:U],p)*norm(inv(A),p)
|
||||
|
||||
# Tridiagonal
|
||||
|
||||
# See dgttrf.f
|
||||
function lufact!(A::Tridiagonal{T}, pivot::Union{Type{Val{false}}, Type{Val{true}}} = Val{true}) where T
|
||||
n = size(A, 1)
|
||||
info = 0
|
||||
ipiv = Vector{BlasInt}(n)
|
||||
dl = A.dl
|
||||
d = A.d
|
||||
du = A.du
|
||||
du2 = A.du2
|
||||
|
||||
@inbounds begin
|
||||
for i = 1:n
|
||||
ipiv[i] = i
|
||||
end
|
||||
for i = 1:n-2
|
||||
# pivot or not?
|
||||
if pivot === Val{false} || abs(d[i]) >= abs(dl[i])
|
||||
# No interchange
|
||||
if d[i] != 0
|
||||
fact = dl[i]/d[i]
|
||||
dl[i] = fact
|
||||
d[i+1] -= fact*du[i]
|
||||
du2[i] = 0
|
||||
end
|
||||
else
|
||||
# Interchange
|
||||
fact = d[i]/dl[i]
|
||||
d[i] = dl[i]
|
||||
dl[i] = fact
|
||||
tmp = du[i]
|
||||
du[i] = d[i+1]
|
||||
d[i+1] = tmp - fact*d[i+1]
|
||||
du2[i] = du[i+1]
|
||||
du[i+1] = -fact*du[i+1]
|
||||
ipiv[i] = i+1
|
||||
end
|
||||
end
|
||||
if n > 1
|
||||
i = n-1
|
||||
if pivot === Val{false} || abs(d[i]) >= abs(dl[i])
|
||||
if d[i] != 0
|
||||
fact = dl[i]/d[i]
|
||||
dl[i] = fact
|
||||
d[i+1] -= fact*du[i]
|
||||
end
|
||||
else
|
||||
fact = d[i]/dl[i]
|
||||
d[i] = dl[i]
|
||||
dl[i] = fact
|
||||
tmp = du[i]
|
||||
du[i] = d[i+1]
|
||||
d[i+1] = tmp - fact*d[i+1]
|
||||
ipiv[i] = i+1
|
||||
end
|
||||
end
|
||||
# check for a zero on the diagonal of U
|
||||
for i = 1:n
|
||||
if d[i] == 0
|
||||
info = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
LU{T,Tridiagonal{T}}(A, ipiv, convert(BlasInt, info))
|
||||
end
|
||||
|
||||
factorize(A::Tridiagonal) = lufact(A)
|
||||
|
||||
function getindex(F::Base.LinAlg.LU{T,Tridiagonal{T}}, d::Symbol) where T
|
||||
m, n = size(F)
|
||||
if d == :L
|
||||
L = Array(Bidiagonal(ones(T, n), F.factors.dl, false))
|
||||
for i = 2:n
|
||||
tmp = L[F.ipiv[i], 1:i - 1]
|
||||
L[F.ipiv[i], 1:i - 1] = L[i, 1:i - 1]
|
||||
L[i, 1:i - 1] = tmp
|
||||
end
|
||||
return L
|
||||
elseif d == :U
|
||||
U = Array(Bidiagonal(F.factors.d, F.factors.du, true))
|
||||
for i = 1:n - 2
|
||||
U[i,i + 2] = F.factors.du2[i]
|
||||
end
|
||||
return U
|
||||
elseif d == :p
|
||||
return ipiv2perm(F.ipiv, m)
|
||||
elseif d == :P
|
||||
return eye(T, m)[:,invperm(F[:p])]
|
||||
end
|
||||
throw(KeyError(d))
|
||||
end
|
||||
|
||||
# See dgtts2.f
|
||||
function A_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T
|
||||
n = size(A,1)
|
||||
if n != size(B,1)
|
||||
throw(DimensionMismatch("matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows"))
|
||||
end
|
||||
nrhs = size(B,2)
|
||||
dl = A.factors.dl
|
||||
d = A.factors.d
|
||||
du = A.factors.du
|
||||
du2 = A.factors.du2
|
||||
ipiv = A.ipiv
|
||||
@inbounds begin
|
||||
for j = 1:nrhs
|
||||
for i = 1:n-1
|
||||
ip = ipiv[i]
|
||||
tmp = B[i+1-ip+i,j] - dl[i]*B[ip,j]
|
||||
B[i,j] = B[ip,j]
|
||||
B[i+1,j] = tmp
|
||||
end
|
||||
B[n,j] /= d[n]
|
||||
if n > 1
|
||||
B[n-1,j] = (B[n-1,j] - du[n-1]*B[n,j])/d[n-1]
|
||||
end
|
||||
for i = n-2:-1:1
|
||||
B[i,j] = (B[i,j] - du[i]*B[i+1,j] - du2[i]*B[i+2,j])/d[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
return B
|
||||
end
|
||||
|
||||
function At_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T
|
||||
n = size(A,1)
|
||||
if n != size(B,1)
|
||||
throw(DimensionMismatch("matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows"))
|
||||
end
|
||||
nrhs = size(B,2)
|
||||
dl = A.factors.dl
|
||||
d = A.factors.d
|
||||
du = A.factors.du
|
||||
du2 = A.factors.du2
|
||||
ipiv = A.ipiv
|
||||
@inbounds begin
|
||||
for j = 1:nrhs
|
||||
B[1,j] /= d[1]
|
||||
if n > 1
|
||||
B[2,j] = (B[2,j] - du[1]*B[1,j])/d[2]
|
||||
end
|
||||
for i = 3:n
|
||||
B[i,j] = (B[i,j] - du[i-1]*B[i-1,j] - du2[i-2]*B[i-2,j])/d[i]
|
||||
end
|
||||
for i = n-1:-1:1
|
||||
if ipiv[i] == i
|
||||
B[i,j] = B[i,j] - dl[i]*B[i+1,j]
|
||||
else
|
||||
tmp = B[i+1,j]
|
||||
B[i+1,j] = B[i,j] - dl[i]*tmp
|
||||
B[i,j] = tmp
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return B
|
||||
end
|
||||
|
||||
# Ac_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where {T<:Real} = At_ldiv_B!(A,B)
|
||||
function Ac_ldiv_B!(A::LU{T,Tridiagonal{T}}, B::AbstractVecOrMat) where T
|
||||
n = size(A,1)
|
||||
if n != size(B,1)
|
||||
throw(DimensionMismatch("matrix has dimensions ($n,$n) but right hand side has $(size(B,1)) rows"))
|
||||
end
|
||||
nrhs = size(B,2)
|
||||
dl = A.factors.dl
|
||||
d = A.factors.d
|
||||
du = A.factors.du
|
||||
du2 = A.factors.du2
|
||||
ipiv = A.ipiv
|
||||
@inbounds begin
|
||||
for j = 1:nrhs
|
||||
B[1,j] /= conj(d[1])
|
||||
if n > 1
|
||||
B[2,j] = (B[2,j] - conj(du[1])*B[1,j])/conj(d[2])
|
||||
end
|
||||
for i = 3:n
|
||||
B[i,j] = (B[i,j] - conj(du[i-1])*B[i-1,j] - conj(du2[i-2])*B[i-2,j])/conj(d[i])
|
||||
end
|
||||
for i = n-1:-1:1
|
||||
if ipiv[i] == i
|
||||
B[i,j] = B[i,j] - conj(dl[i])*B[i+1,j]
|
||||
else
|
||||
tmp = B[i+1,j]
|
||||
B[i+1,j] = B[i,j] - conj(dl[i])*tmp
|
||||
B[i,j] = tmp
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return B
|
||||
end
|
||||
|
||||
/(B::AbstractMatrix,A::LU) = At_ldiv_Bt(A,B).'
|
||||
|
||||
# Conversions
|
||||
convert(::Type{AbstractMatrix}, F::LU) = (F[:L] * F[:U])[invperm(F[:p]),:]
|
||||
convert(::Type{AbstractArray}, F::LU) = convert(AbstractMatrix, F)
|
||||
convert(::Type{Matrix}, F::LU) = convert(Array, convert(AbstractArray, F))
|
||||
convert(::Type{Array}, F::LU) = convert(Matrix, F)
|
||||
full(F::LU) = convert(AbstractArray, F)
|
||||
|
||||
function convert(::Type{Tridiagonal}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where T
|
||||
n = size(F, 1)
|
||||
|
||||
dl = copy(F.factors.dl)
|
||||
d = copy(F.factors.d)
|
||||
du = copy(F.factors.du)
|
||||
du2 = copy(F.factors.du2)
|
||||
|
||||
for i = n - 1:-1:1
|
||||
li = dl[i]
|
||||
dl[i] = li*d[i]
|
||||
d[i + 1] += li*du[i]
|
||||
if i < n - 1
|
||||
du[i + 1] += li*du2[i]
|
||||
end
|
||||
|
||||
if F.ipiv[i] != i
|
||||
tmp = dl[i]
|
||||
dl[i] = d[i]
|
||||
d[i] = tmp
|
||||
|
||||
tmp = d[i + 1]
|
||||
d[i + 1] = du[i]
|
||||
du[i] = tmp
|
||||
|
||||
if i < n - 1
|
||||
tmp = du[i + 1]
|
||||
du[i + 1] = du2[i]
|
||||
du2[i] = tmp
|
||||
end
|
||||
end
|
||||
end
|
||||
return Tridiagonal(dl, d, du)
|
||||
end
|
||||
convert(::Type{AbstractMatrix}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} =
|
||||
convert(Tridiagonal, F)
|
||||
convert(::Type{AbstractArray}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} =
|
||||
convert(AbstractMatrix, F)
|
||||
convert(::Type{Matrix}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} =
|
||||
convert(Array, convert(AbstractArray, F))
|
||||
convert(::Type{Array}, F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} =
|
||||
convert(Matrix, F)
|
||||
full(F::Base.LinAlg.LU{T,Tridiagonal{T}}) where {T} = convert(AbstractArray, F)
|
||||
@@ -0,0 +1,738 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# matmul.jl: Everything to do with dense matrix multiplication
|
||||
|
||||
matprod(x, y) = x*y + x*y
|
||||
|
||||
# multiply by diagonal matrix as vector
|
||||
function scale!(C::AbstractMatrix, A::AbstractMatrix, b::AbstractVector)
|
||||
m, n = size(A)
|
||||
if size(A) != size(C)
|
||||
throw(DimensionMismatch("size of A, $(size(A)), does not match size of C, $(size(C))"))
|
||||
end
|
||||
if n != length(b)
|
||||
throw(DimensionMismatch("second dimension of A, $n, does not match length of b, $(length(b))"))
|
||||
end
|
||||
@inbounds for j = 1:n
|
||||
bj = b[j]
|
||||
for i = 1:m
|
||||
C[i,j] = A[i,j]*bj
|
||||
end
|
||||
end
|
||||
C
|
||||
end
|
||||
|
||||
function scale!(C::AbstractMatrix, b::AbstractVector, A::AbstractMatrix)
|
||||
m, n = size(A)
|
||||
if size(A) != size(C)
|
||||
throw(DimensionMismatch("size of A, $(size(A)), does not match size of C, $(size(C))"))
|
||||
end
|
||||
if m != length(b)
|
||||
throw(DimensionMismatch("first dimension of A, $m, does not match length of b, $(length(b))"))
|
||||
end
|
||||
@inbounds for j = 1:n, i = 1:m
|
||||
C[i,j] = A[i,j]*b[i]
|
||||
end
|
||||
C
|
||||
end
|
||||
|
||||
# Dot products
|
||||
|
||||
vecdot(x::Union{DenseArray{T},StridedVector{T}}, y::Union{DenseArray{T},StridedVector{T}}) where {T<:BlasReal} = BLAS.dot(x, y)
|
||||
vecdot(x::Union{DenseArray{T},StridedVector{T}}, y::Union{DenseArray{T},StridedVector{T}}) where {T<:BlasComplex} = BLAS.dotc(x, y)
|
||||
|
||||
function dot(x::Vector{T}, rx::Union{UnitRange{TI},Range{TI}}, y::Vector{T}, ry::Union{UnitRange{TI},Range{TI}}) where {T<:BlasReal,TI<:Integer}
|
||||
if length(rx) != length(ry)
|
||||
throw(DimensionMismatch("length of rx, $(length(rx)), does not equal length of ry, $(length(ry))"))
|
||||
end
|
||||
if minimum(rx) < 1 || maximum(rx) > length(x)
|
||||
throw(BoundsError(x, rx))
|
||||
end
|
||||
if minimum(ry) < 1 || maximum(ry) > length(y)
|
||||
throw(BoundsError(y, ry))
|
||||
end
|
||||
BLAS.dot(length(rx), pointer(x)+(first(rx)-1)*sizeof(T), step(rx), pointer(y)+(first(ry)-1)*sizeof(T), step(ry))
|
||||
end
|
||||
|
||||
function dot(x::Vector{T}, rx::Union{UnitRange{TI},Range{TI}}, y::Vector{T}, ry::Union{UnitRange{TI},Range{TI}}) where {T<:BlasComplex,TI<:Integer}
|
||||
if length(rx) != length(ry)
|
||||
throw(DimensionMismatch("length of rx, $(length(rx)), does not equal length of ry, $(length(ry))"))
|
||||
end
|
||||
if minimum(rx) < 1 || maximum(rx) > length(x)
|
||||
throw(BoundsError(x, rx))
|
||||
end
|
||||
if minimum(ry) < 1 || maximum(ry) > length(y)
|
||||
throw(BoundsError(y, ry))
|
||||
end
|
||||
BLAS.dotc(length(rx), pointer(x)+(first(rx)-1)*sizeof(T), step(rx), pointer(y)+(first(ry)-1)*sizeof(T), step(ry))
|
||||
end
|
||||
|
||||
At_mul_B(x::StridedVector{T}, y::StridedVector{T}) where {T<:BlasComplex} = BLAS.dotu(x, y)
|
||||
|
||||
# Matrix-vector multiplication
|
||||
function (*)(A::StridedMatrix{T}, x::StridedVector{S}) where {T<:BlasFloat,S}
|
||||
TS = promote_op(matprod, T, S)
|
||||
A_mul_B!(similar(x, TS, size(A,1)), A, convert(AbstractVector{TS}, x))
|
||||
end
|
||||
function (*)(A::AbstractMatrix{T}, x::AbstractVector{S}) where {T,S}
|
||||
TS = promote_op(matprod, T, S)
|
||||
A_mul_B!(similar(x,TS,size(A,1)),A,x)
|
||||
end
|
||||
|
||||
# these will throw a DimensionMismatch unless B has 1 row (or 1 col for transposed case):
|
||||
A_mul_Bt(a::AbstractVector, B::AbstractMatrix) = A_mul_Bt(reshape(a,length(a),1),B)
|
||||
A_mul_Bt(A::AbstractMatrix, b::AbstractVector) = A_mul_Bt(A,reshape(b,length(b),1))
|
||||
A_mul_Bc(a::AbstractVector, B::AbstractMatrix) = A_mul_Bc(reshape(a,length(a),1),B)
|
||||
A_mul_Bc(A::AbstractMatrix, b::AbstractVector) = A_mul_Bc(A,reshape(b,length(b),1))
|
||||
(*)(a::AbstractVector, B::AbstractMatrix) = reshape(a,length(a),1)*B
|
||||
|
||||
A_mul_B!(y::StridedVector{T}, A::StridedVecOrMat{T}, x::StridedVector{T}) where {T<:BlasFloat} = gemv!(y, 'N', A, x)
|
||||
for elty in (Float32,Float64)
|
||||
@eval begin
|
||||
function A_mul_B!(y::StridedVector{Complex{$elty}}, A::StridedVecOrMat{Complex{$elty}}, x::StridedVector{$elty})
|
||||
Afl = reinterpret($elty,A,(2size(A,1),size(A,2)))
|
||||
yfl = reinterpret($elty,y)
|
||||
gemv!(yfl,'N',Afl,x)
|
||||
return y
|
||||
end
|
||||
end
|
||||
end
|
||||
A_mul_B!(y::AbstractVector, A::AbstractVecOrMat, x::AbstractVector) = generic_matvecmul!(y, 'N', A, x)
|
||||
|
||||
function At_mul_B(A::StridedMatrix{T}, x::StridedVector{S}) where {T<:BlasFloat,S}
|
||||
TS = promote_op(matprod, T, S)
|
||||
At_mul_B!(similar(x,TS,size(A,2)), A, convert(AbstractVector{TS}, x))
|
||||
end
|
||||
function At_mul_B(A::AbstractMatrix{T}, x::AbstractVector{S}) where {T,S}
|
||||
TS = promote_op(matprod, T, S)
|
||||
At_mul_B!(similar(x,TS,size(A,2)), A, x)
|
||||
end
|
||||
At_mul_B!(y::StridedVector{T}, A::StridedVecOrMat{T}, x::StridedVector{T}) where {T<:BlasFloat} = gemv!(y, 'T', A, x)
|
||||
At_mul_B!(y::AbstractVector, A::AbstractVecOrMat, x::AbstractVector) = generic_matvecmul!(y, 'T', A, x)
|
||||
|
||||
function Ac_mul_B(A::StridedMatrix{T}, x::StridedVector{S}) where {T<:BlasFloat,S}
|
||||
TS = promote_op(matprod, T, S)
|
||||
Ac_mul_B!(similar(x,TS,size(A,2)),A,convert(AbstractVector{TS},x))
|
||||
end
|
||||
function Ac_mul_B(A::AbstractMatrix{T}, x::AbstractVector{S}) where {T,S}
|
||||
TS = promote_op(matprod, T, S)
|
||||
Ac_mul_B!(similar(x,TS,size(A,2)), A, x)
|
||||
end
|
||||
|
||||
Ac_mul_B!(y::StridedVector{T}, A::StridedVecOrMat{T}, x::StridedVector{T}) where {T<:BlasReal} = At_mul_B!(y, A, x)
|
||||
Ac_mul_B!(y::StridedVector{T}, A::StridedVecOrMat{T}, x::StridedVector{T}) where {T<:BlasComplex} = gemv!(y, 'C', A, x)
|
||||
Ac_mul_B!(y::AbstractVector, A::AbstractVecOrMat, x::AbstractVector) = generic_matvecmul!(y, 'C', A, x)
|
||||
|
||||
# Matrix-matrix multiplication
|
||||
|
||||
"""
|
||||
```
|
||||
*(A::AbstractMatrix, B::AbstractMatrix)
|
||||
```
|
||||
|
||||
Matrix multiplication.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> [1 1; 0 1] * [1 0; 1 1]
|
||||
2×2 Array{Int64,2}:
|
||||
2 1
|
||||
1 1
|
||||
```
|
||||
"""
|
||||
function (*)(A::AbstractMatrix{T}, B::AbstractMatrix{S}) where {T,S}
|
||||
TS = promote_op(matprod, T, S)
|
||||
A_mul_B!(similar(B, TS, (size(A,1), size(B,2))), A, B)
|
||||
end
|
||||
A_mul_B!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = gemm_wrapper!(C, 'N', 'N', A, B)
|
||||
for elty in (Float32,Float64)
|
||||
@eval begin
|
||||
function A_mul_B!(C::StridedMatrix{Complex{$elty}}, A::StridedVecOrMat{Complex{$elty}}, B::StridedVecOrMat{$elty})
|
||||
Afl = reinterpret($elty, A, (2size(A,1), size(A,2)))
|
||||
Cfl = reinterpret($elty, C, (2size(C,1), size(C,2)))
|
||||
gemm_wrapper!(Cfl, 'N', 'N', Afl, B)
|
||||
return C
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
A_mul_B!(Y, A, B) -> Y
|
||||
|
||||
Calculates the matrix-matrix or matrix-vector product ``A⋅B`` and stores the result in `Y`,
|
||||
overwriting the existing value of `Y`. Note that `Y` must not be aliased with either `A` or
|
||||
`B`.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A=[1.0 2.0; 3.0 4.0]; B=[1.0 1.0; 1.0 1.0]; Y = similar(B); A_mul_B!(Y, A, B);
|
||||
|
||||
julia> Y
|
||||
2×2 Array{Float64,2}:
|
||||
3.0 3.0
|
||||
7.0 7.0
|
||||
```
|
||||
"""
|
||||
A_mul_B!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat) = generic_matmatmul!(C, 'N', 'N', A, B)
|
||||
|
||||
function At_mul_B(A::AbstractMatrix{T}, B::AbstractMatrix{S}) where {T,S}
|
||||
TS = promote_op(matprod, T, S)
|
||||
At_mul_B!(similar(B, TS, (size(A,2), size(B,2))), A, B)
|
||||
end
|
||||
At_mul_B!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = A===B ? syrk_wrapper!(C, 'T', A) : gemm_wrapper!(C, 'T', 'N', A, B)
|
||||
At_mul_B!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat) = generic_matmatmul!(C, 'T', 'N', A, B)
|
||||
|
||||
function A_mul_Bt(A::AbstractMatrix{T}, B::AbstractMatrix{S}) where {T,S}
|
||||
TS = promote_op(matprod, T, S)
|
||||
A_mul_Bt!(similar(B, TS, (size(A,1), size(B,1))), A, B)
|
||||
end
|
||||
A_mul_Bt!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = A===B ? syrk_wrapper!(C, 'N', A) : gemm_wrapper!(C, 'N', 'T', A, B)
|
||||
for elty in (Float32,Float64)
|
||||
@eval begin
|
||||
function A_mul_Bt!(C::StridedMatrix{Complex{$elty}}, A::StridedVecOrMat{Complex{$elty}}, B::StridedVecOrMat{$elty})
|
||||
Afl = reinterpret($elty, A, (2size(A,1), size(A,2)))
|
||||
Cfl = reinterpret($elty, C, (2size(C,1), size(C,2)))
|
||||
gemm_wrapper!(Cfl, 'N', 'T', Afl, B)
|
||||
return C
|
||||
end
|
||||
end
|
||||
end
|
||||
A_mul_Bt!(C::AbstractVecOrMat, A::AbstractVecOrMat, B::AbstractVecOrMat) = generic_matmatmul!(C, 'N', 'T', A, B)
|
||||
|
||||
function At_mul_Bt(A::AbstractMatrix{T}, B::AbstractVecOrMat{S}) where {T,S}
|
||||
TS = promote_op(matprod, T, S)
|
||||
At_mul_Bt!(similar(B, TS, (size(A,2), size(B,1))), A, B)
|
||||
end
|
||||
At_mul_Bt!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = gemm_wrapper!(C, 'T', 'T', A, B)
|
||||
At_mul_Bt!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat) = generic_matmatmul!(C, 'T', 'T', A, B)
|
||||
|
||||
Ac_mul_B(A::StridedMatrix{T}, B::StridedMatrix{T}) where {T<:BlasReal} = At_mul_B(A, B)
|
||||
Ac_mul_B!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) where {T<:BlasReal} = At_mul_B!(C, A, B)
|
||||
function Ac_mul_B(A::AbstractMatrix{T}, B::AbstractMatrix{S}) where {T,S}
|
||||
TS = promote_op(matprod, T, S)
|
||||
Ac_mul_B!(similar(B, TS, (size(A,2), size(B,2))), A, B)
|
||||
end
|
||||
Ac_mul_B!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = A===B ? herk_wrapper!(C,'C',A) : gemm_wrapper!(C,'C', 'N', A, B)
|
||||
Ac_mul_B!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat) = generic_matmatmul!(C, 'C', 'N', A, B)
|
||||
|
||||
A_mul_Bc(A::StridedMatrix{<:BlasFloat}, B::StridedMatrix{<:BlasReal}) = A_mul_Bt(A, B)
|
||||
A_mul_Bc!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{<:BlasReal}) where {T<:BlasFloat} = A_mul_Bt!(C, A, B)
|
||||
function A_mul_Bc(A::AbstractMatrix{T}, B::AbstractMatrix{S}) where {T,S}
|
||||
TS = promote_op(matprod, T, S)
|
||||
A_mul_Bc!(similar(B,TS,(size(A,1),size(B,1))),A,B)
|
||||
end
|
||||
A_mul_Bc!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = A===B ? herk_wrapper!(C, 'N', A) : gemm_wrapper!(C, 'N', 'C', A, B)
|
||||
A_mul_Bc!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat) = generic_matmatmul!(C, 'N', 'C', A, B)
|
||||
|
||||
Ac_mul_Bc(A::AbstractMatrix{T}, B::AbstractMatrix{S}) where {T,S} =
|
||||
Ac_mul_Bc!(similar(B, promote_op(matprod, T, S), (size(A,2), size(B,1))), A, B)
|
||||
Ac_mul_Bc!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = gemm_wrapper!(C, 'C', 'C', A, B)
|
||||
Ac_mul_Bc!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat) = generic_matmatmul!(C, 'C', 'C', A, B)
|
||||
Ac_mul_Bt!(C::AbstractMatrix, A::AbstractVecOrMat, B::AbstractVecOrMat) = generic_matmatmul!(C, 'C', 'T', A, B)
|
||||
# Supporting functions for matrix multiplication
|
||||
|
||||
function copytri!(A::AbstractMatrix, uplo::Char, conjugate::Bool=false)
|
||||
n = checksquare(A)
|
||||
if uplo == 'U'
|
||||
for i = 1:(n-1), j = (i+1):n
|
||||
A[j,i] = conjugate ? conj(A[i,j]) : A[i,j]
|
||||
end
|
||||
elseif uplo == 'L'
|
||||
for i = 1:(n-1), j = (i+1):n
|
||||
A[i,j] = conjugate ? conj(A[j,i]) : A[j,i]
|
||||
end
|
||||
else
|
||||
throw(ArgumentError("uplo argument must be 'U' (upper) or 'L' (lower), got $uplo"))
|
||||
end
|
||||
A
|
||||
end
|
||||
|
||||
function gemv!(y::StridedVector{T}, tA::Char, A::StridedVecOrMat{T}, x::StridedVector{T}) where T<:BlasFloat
|
||||
mA, nA = lapack_size(tA, A)
|
||||
if nA != length(x)
|
||||
throw(DimensionMismatch("second dimension of A, $nA, does not match length of x, $(length(x))"))
|
||||
end
|
||||
if mA != length(y)
|
||||
throw(DimensionMismatch("first dimension of A, $mA, does not match length of y, $(length(y))"))
|
||||
end
|
||||
if mA == 0
|
||||
return y
|
||||
end
|
||||
if nA == 0
|
||||
return fill!(y,0)
|
||||
end
|
||||
stride(A, 1) == 1 && stride(A, 2) >= size(A, 1) && return BLAS.gemv!(tA, one(T), A, x, zero(T), y)
|
||||
return generic_matvecmul!(y, tA, A, x)
|
||||
end
|
||||
|
||||
function syrk_wrapper!(C::StridedMatrix{T}, tA::Char, A::StridedVecOrMat{T}) where T<:BlasFloat
|
||||
nC = checksquare(C)
|
||||
if tA == 'T'
|
||||
(nA, mA) = size(A,1), size(A,2)
|
||||
tAt = 'N'
|
||||
else
|
||||
(mA, nA) = size(A,1), size(A,2)
|
||||
tAt = 'T'
|
||||
end
|
||||
if nC != mA
|
||||
throw(DimensionMismatch("output matrix has size: $(nC), but should have size $(mA)"))
|
||||
end
|
||||
if mA == 0 || nA == 0
|
||||
return fill!(C,0)
|
||||
end
|
||||
if mA == 2 && nA == 2
|
||||
return matmul2x2!(C,tA,tAt,A,A)
|
||||
end
|
||||
if mA == 3 && nA == 3
|
||||
return matmul3x3!(C,tA,tAt,A,A)
|
||||
end
|
||||
|
||||
if stride(A, 1) == stride(C, 1) == 1 && stride(A, 2) >= size(A, 1) && stride(C, 2) >= size(C, 1)
|
||||
return copytri!(BLAS.syrk!('U', tA, one(T), A, zero(T), C), 'U')
|
||||
end
|
||||
return generic_matmatmul!(C, tA, tAt, A, A)
|
||||
end
|
||||
|
||||
function herk_wrapper!(C::Union{StridedMatrix{T}, StridedMatrix{Complex{T}}}, tA::Char, A::Union{StridedVecOrMat{T}, StridedVecOrMat{Complex{T}}}) where T<:BlasReal
|
||||
nC = checksquare(C)
|
||||
if tA == 'C'
|
||||
(nA, mA) = size(A,1), size(A,2)
|
||||
tAt = 'N'
|
||||
else
|
||||
(mA, nA) = size(A,1), size(A,2)
|
||||
tAt = 'C'
|
||||
end
|
||||
if nC != mA
|
||||
throw(DimensionMismatch("output matrix has size: $(nC), but should have size $(mA)"))
|
||||
end
|
||||
if mA == 0 || nA == 0
|
||||
return fill!(C,0)
|
||||
end
|
||||
if mA == 2 && nA == 2
|
||||
return matmul2x2!(C,tA,tAt,A,A)
|
||||
end
|
||||
if mA == 3 && nA == 3
|
||||
return matmul3x3!(C,tA,tAt,A,A)
|
||||
end
|
||||
|
||||
# Result array does not need to be initialized as long as beta==0
|
||||
# C = Matrix{T}(mA, mA)
|
||||
|
||||
if stride(A, 1) == stride(C, 1) == 1 && stride(A, 2) >= size(A, 1) && stride(C, 2) >= size(C, 1)
|
||||
return copytri!(BLAS.herk!('U', tA, one(T), A, zero(T), C), 'U', true)
|
||||
end
|
||||
return generic_matmatmul!(C,tA, tAt, A, A)
|
||||
end
|
||||
|
||||
function gemm_wrapper(tA::Char, tB::Char,
|
||||
A::StridedVecOrMat{T},
|
||||
B::StridedVecOrMat{T}) where T<:BlasFloat
|
||||
mA, nA = lapack_size(tA, A)
|
||||
mB, nB = lapack_size(tB, B)
|
||||
C = similar(B, T, mA, nB)
|
||||
gemm_wrapper!(C, tA, tB, A, B)
|
||||
end
|
||||
|
||||
function gemm_wrapper!(C::StridedVecOrMat{T}, tA::Char, tB::Char,
|
||||
A::StridedVecOrMat{T},
|
||||
B::StridedVecOrMat{T}) where T<:BlasFloat
|
||||
mA, nA = lapack_size(tA, A)
|
||||
mB, nB = lapack_size(tB, B)
|
||||
|
||||
if nA != mB
|
||||
throw(DimensionMismatch("A has dimensions ($mA,$nA) but B has dimensions ($mB,$nB)"))
|
||||
end
|
||||
|
||||
if C === A || B === C
|
||||
throw(ArgumentError("output matrix must not be aliased with input matrix"))
|
||||
end
|
||||
|
||||
if mA == 0 || nA == 0 || nB == 0
|
||||
if size(C) != (mA, nB)
|
||||
throw(DimensionMismatch("C has dimensions $(size(C)), should have ($mA,$nB)"))
|
||||
end
|
||||
return fill!(C,0)
|
||||
end
|
||||
|
||||
if mA == 2 && nA == 2 && nB == 2
|
||||
return matmul2x2!(C,tA,tB,A,B)
|
||||
end
|
||||
if mA == 3 && nA == 3 && nB == 3
|
||||
return matmul3x3!(C,tA,tB,A,B)
|
||||
end
|
||||
|
||||
if stride(A, 1) == stride(B, 1) == stride(C, 1) == 1 && stride(A, 2) >= size(A, 1) && stride(B, 2) >= size(B, 1) && stride(C, 2) >= size(C, 1)
|
||||
return BLAS.gemm!(tA, tB, one(T), A, B, zero(T), C)
|
||||
end
|
||||
generic_matmatmul!(C, tA, tB, A, B)
|
||||
end
|
||||
|
||||
# blas.jl defines matmul for floats; other integer and mixed precision
|
||||
# cases are handled here
|
||||
|
||||
lapack_size(t::Char, M::AbstractVecOrMat) = (size(M, t=='N' ? 1:2), size(M, t=='N' ? 2:1))
|
||||
|
||||
function copy!(B::AbstractVecOrMat, ir_dest::UnitRange{Int}, jr_dest::UnitRange{Int}, tM::Char, M::AbstractVecOrMat, ir_src::UnitRange{Int}, jr_src::UnitRange{Int})
|
||||
if tM == 'N'
|
||||
copy!(B, ir_dest, jr_dest, M, ir_src, jr_src)
|
||||
else
|
||||
Base.copy_transpose!(B, ir_dest, jr_dest, M, jr_src, ir_src)
|
||||
tM == 'C' && conj!(B)
|
||||
end
|
||||
B
|
||||
end
|
||||
|
||||
function copy_transpose!(B::AbstractMatrix, ir_dest::UnitRange{Int}, jr_dest::UnitRange{Int}, tM::Char, M::AbstractVecOrMat, ir_src::UnitRange{Int}, jr_src::UnitRange{Int})
|
||||
if tM == 'N'
|
||||
Base.copy_transpose!(B, ir_dest, jr_dest, M, ir_src, jr_src)
|
||||
else
|
||||
copy!(B, ir_dest, jr_dest, M, jr_src, ir_src)
|
||||
tM == 'C' && conj!(B)
|
||||
end
|
||||
B
|
||||
end
|
||||
|
||||
# TODO: It will be faster for large matrices to convert to float,
|
||||
# call BLAS, and convert back to required type.
|
||||
|
||||
# NOTE: the generic version is also called as fallback for
|
||||
# strides != 1 cases
|
||||
|
||||
function generic_matvecmul!(C::AbstractVector{R}, tA, A::AbstractVecOrMat, B::AbstractVector) where R
|
||||
mB = length(B)
|
||||
mA, nA = lapack_size(tA, A)
|
||||
if mB != nA
|
||||
throw(DimensionMismatch("matrix A has dimensions ($mA,$nA), vector B has length $mB"))
|
||||
end
|
||||
if mA != length(C)
|
||||
throw(DimensionMismatch("result C has length $(length(C)), needs length $mA"))
|
||||
end
|
||||
|
||||
Astride = size(A, 1)
|
||||
|
||||
if tA == 'T' # fastest case
|
||||
for k = 1:mA
|
||||
aoffs = (k-1)*Astride
|
||||
if mB == 0
|
||||
s = zero(R)
|
||||
else
|
||||
s = zero(A[aoffs + 1]*B[1] + A[aoffs + 1]*B[1])
|
||||
end
|
||||
for i = 1:nA
|
||||
s += A[aoffs+i].'B[i]
|
||||
end
|
||||
C[k] = s
|
||||
end
|
||||
elseif tA == 'C'
|
||||
for k = 1:mA
|
||||
aoffs = (k-1)*Astride
|
||||
if mB == 0
|
||||
s = zero(R)
|
||||
else
|
||||
s = zero(A[aoffs + 1]*B[1] + A[aoffs + 1]*B[1])
|
||||
end
|
||||
for i = 1:nA
|
||||
s += A[aoffs + i]'B[i]
|
||||
end
|
||||
C[k] = s
|
||||
end
|
||||
else # tA == 'N'
|
||||
for i = 1:mA
|
||||
if mB == 0
|
||||
C[i] = zero(R)
|
||||
else
|
||||
C[i] = zero(A[i]*B[1] + A[i]*B[1])
|
||||
end
|
||||
end
|
||||
for k = 1:mB
|
||||
aoffs = (k-1)*Astride
|
||||
b = B[k]
|
||||
for i = 1:mA
|
||||
C[i] += A[aoffs + i] * b
|
||||
end
|
||||
end
|
||||
end
|
||||
C
|
||||
end
|
||||
|
||||
function generic_matmatmul(tA, tB, A::AbstractVecOrMat{T}, B::AbstractMatrix{S}) where {T,S}
|
||||
mA, nA = lapack_size(tA, A)
|
||||
mB, nB = lapack_size(tB, B)
|
||||
C = similar(B, promote_op(matprod, T, S), mA, nB)
|
||||
generic_matmatmul!(C, tA, tB, A, B)
|
||||
end
|
||||
|
||||
const tilebufsize = 10800 # Approximately 32k/3
|
||||
const Abuf = Vector{UInt8}(tilebufsize)
|
||||
const Bbuf = Vector{UInt8}(tilebufsize)
|
||||
const Cbuf = Vector{UInt8}(tilebufsize)
|
||||
|
||||
function generic_matmatmul!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix)
|
||||
mA, nA = lapack_size(tA, A)
|
||||
mB, nB = lapack_size(tB, B)
|
||||
mC, nC = size(C)
|
||||
|
||||
if mA == nA == mB == nB == mC == nC == 2
|
||||
return matmul2x2!(C, tA, tB, A, B)
|
||||
end
|
||||
if mA == nA == mB == nB == mC == nC == 3
|
||||
return matmul3x3!(C, tA, tB, A, B)
|
||||
end
|
||||
_generic_matmatmul!(C, tA, tB, A, B)
|
||||
end
|
||||
|
||||
generic_matmatmul!(C::AbstractVecOrMat, tA, tB, A::AbstractVecOrMat, B::AbstractVecOrMat) = _generic_matmatmul!(C, tA, tB, A, B)
|
||||
|
||||
function _generic_matmatmul!(C::AbstractVecOrMat{R}, tA, tB, A::AbstractVecOrMat{T}, B::AbstractVecOrMat{S}) where {T,S,R}
|
||||
mA, nA = lapack_size(tA, A)
|
||||
mB, nB = lapack_size(tB, B)
|
||||
if mB != nA
|
||||
throw(DimensionMismatch("matrix A has dimensions ($mA,$nA), matrix B has dimensions ($mB,$nB)"))
|
||||
end
|
||||
if size(C,1) != mA || size(C,2) != nB
|
||||
throw(DimensionMismatch("result C has dimensions $(size(C)), needs ($mA,$nB)"))
|
||||
end
|
||||
if isempty(A) || isempty(B)
|
||||
return fill!(C, zero(R))
|
||||
end
|
||||
|
||||
tile_size = 0
|
||||
if isbits(R) && isbits(T) && isbits(S) && (tA == 'N' || tB != 'N')
|
||||
tile_size = floor(Int, sqrt(tilebufsize / max(sizeof(R), sizeof(S), sizeof(T))))
|
||||
end
|
||||
@inbounds begin
|
||||
if tile_size > 0
|
||||
sz = (tile_size, tile_size)
|
||||
Atile = unsafe_wrap(Array, convert(Ptr{T}, pointer(Abuf)), sz)
|
||||
Btile = unsafe_wrap(Array, convert(Ptr{S}, pointer(Bbuf)), sz)
|
||||
|
||||
z1 = zero(A[1, 1]*B[1, 1] + A[1, 1]*B[1, 1])
|
||||
z = convert(promote_type(typeof(z1), R), z1)
|
||||
|
||||
if mA < tile_size && nA < tile_size && nB < tile_size
|
||||
Base.copy_transpose!(Atile, 1:nA, 1:mA, tA, A, 1:mA, 1:nA)
|
||||
copy!(Btile, 1:mB, 1:nB, tB, B, 1:mB, 1:nB)
|
||||
for j = 1:nB
|
||||
boff = (j-1)*tile_size
|
||||
for i = 1:mA
|
||||
aoff = (i-1)*tile_size
|
||||
s = z
|
||||
for k = 1:nA
|
||||
s += Atile[aoff+k] * Btile[boff+k]
|
||||
end
|
||||
C[i,j] = s
|
||||
end
|
||||
end
|
||||
else
|
||||
Ctile = unsafe_wrap(Array, convert(Ptr{R}, pointer(Cbuf)), sz)
|
||||
for jb = 1:tile_size:nB
|
||||
jlim = min(jb+tile_size-1,nB)
|
||||
jlen = jlim-jb+1
|
||||
for ib = 1:tile_size:mA
|
||||
ilim = min(ib+tile_size-1,mA)
|
||||
ilen = ilim-ib+1
|
||||
fill!(Ctile, z)
|
||||
for kb = 1:tile_size:nA
|
||||
klim = min(kb+tile_size-1,mB)
|
||||
klen = klim-kb+1
|
||||
Base.copy_transpose!(Atile, 1:klen, 1:ilen, tA, A, ib:ilim, kb:klim)
|
||||
copy!(Btile, 1:klen, 1:jlen, tB, B, kb:klim, jb:jlim)
|
||||
for j=1:jlen
|
||||
bcoff = (j-1)*tile_size
|
||||
for i = 1:ilen
|
||||
aoff = (i-1)*tile_size
|
||||
s = z
|
||||
for k = 1:klen
|
||||
s += Atile[aoff+k] * Btile[bcoff+k]
|
||||
end
|
||||
Ctile[bcoff+i] += s
|
||||
end
|
||||
end
|
||||
end
|
||||
copy!(C, ib:ilim, jb:jlim, Ctile, 1:ilen, 1:jlen)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
# Multiplication for non-plain-data uses the naive algorithm
|
||||
|
||||
if tA == 'N'
|
||||
if tB == 'N'
|
||||
for i = 1:mA, j = 1:nB
|
||||
z2 = zero(A[i, 1]*B[1, j] + A[i, 1]*B[1, j])
|
||||
Ctmp = convert(promote_type(R, typeof(z2)), z2)
|
||||
for k = 1:nA
|
||||
Ctmp += A[i, k]*B[k, j]
|
||||
end
|
||||
C[i,j] = Ctmp
|
||||
end
|
||||
elseif tB == 'T'
|
||||
for i = 1:mA, j = 1:nB
|
||||
z2 = zero(A[i, 1]*B[j, 1] + A[i, 1]*B[j, 1])
|
||||
Ctmp = convert(promote_type(R, typeof(z2)), z2)
|
||||
for k = 1:nA
|
||||
Ctmp += A[i, k]*B[j, k].'
|
||||
end
|
||||
C[i,j] = Ctmp
|
||||
end
|
||||
else
|
||||
for i = 1:mA, j = 1:nB
|
||||
z2 = zero(A[i, 1]*B[j, 1] + A[i, 1]*B[j, 1])
|
||||
Ctmp = convert(promote_type(R, typeof(z2)), z2)
|
||||
for k = 1:nA
|
||||
Ctmp += A[i, k]*B[j, k]'
|
||||
end
|
||||
C[i,j] = Ctmp
|
||||
end
|
||||
end
|
||||
elseif tA == 'T'
|
||||
if tB == 'N'
|
||||
for i = 1:mA, j = 1:nB
|
||||
z2 = zero(A[1, i]*B[1, j] + A[1, i]*B[1, j])
|
||||
Ctmp = convert(promote_type(R, typeof(z2)), z2)
|
||||
for k = 1:nA
|
||||
Ctmp += A[k, i].'B[k, j]
|
||||
end
|
||||
C[i,j] = Ctmp
|
||||
end
|
||||
elseif tB == 'T'
|
||||
for i = 1:mA, j = 1:nB
|
||||
z2 = zero(A[1, i]*B[j, 1] + A[1, i]*B[j, 1])
|
||||
Ctmp = convert(promote_type(R, typeof(z2)), z2)
|
||||
for k = 1:nA
|
||||
Ctmp += A[k, i].'B[j, k].'
|
||||
end
|
||||
C[i,j] = Ctmp
|
||||
end
|
||||
else
|
||||
for i = 1:mA, j = 1:nB
|
||||
z2 = zero(A[1, i]*B[j, 1] + A[1, i]*B[j, 1])
|
||||
Ctmp = convert(promote_type(R, typeof(z2)), z2)
|
||||
for k = 1:nA
|
||||
Ctmp += A[k, i].'B[j, k]'
|
||||
end
|
||||
C[i,j] = Ctmp
|
||||
end
|
||||
end
|
||||
else
|
||||
if tB == 'N'
|
||||
for i = 1:mA, j = 1:nB
|
||||
z2 = zero(A[1, i]*B[1, j] + A[1, i]*B[1, j])
|
||||
Ctmp = convert(promote_type(R, typeof(z2)), z2)
|
||||
for k = 1:nA
|
||||
Ctmp += A[k, i]'B[k, j]
|
||||
end
|
||||
C[i,j] = Ctmp
|
||||
end
|
||||
elseif tB == 'T'
|
||||
for i = 1:mA, j = 1:nB
|
||||
z2 = zero(A[1, i]*B[j, 1] + A[1, i]*B[j, 1])
|
||||
Ctmp = convert(promote_type(R, typeof(z2)), z2)
|
||||
for k = 1:nA
|
||||
Ctmp += A[k, i]'B[j, k].'
|
||||
end
|
||||
C[i,j] = Ctmp
|
||||
end
|
||||
else
|
||||
for i = 1:mA, j = 1:nB
|
||||
z2 = zero(A[1, i]*B[j, 1] + A[1, i]*B[j, 1])
|
||||
Ctmp = convert(promote_type(R, typeof(z2)), z2)
|
||||
for k = 1:nA
|
||||
Ctmp += A[k, i]'B[j, k]'
|
||||
end
|
||||
C[i,j] = Ctmp
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end # @inbounds
|
||||
C
|
||||
end
|
||||
|
||||
|
||||
# multiply 2x2 matrices
|
||||
function matmul2x2(tA, tB, A::AbstractMatrix{T}, B::AbstractMatrix{S}) where {T,S}
|
||||
matmul2x2!(similar(B, promote_op(matprod, T, S), 2, 2), tA, tB, A, B)
|
||||
end
|
||||
|
||||
function matmul2x2!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix)
|
||||
if !(size(A) == size(B) == size(C) == (2,2))
|
||||
throw(DimensionMismatch("A has size $(size(A)), B has size $(size(B)), C has size $(size(C))"))
|
||||
end
|
||||
@inbounds begin
|
||||
if tA == 'T'
|
||||
A11 = transpose(A[1,1]); A12 = transpose(A[2,1]); A21 = transpose(A[1,2]); A22 = transpose(A[2,2])
|
||||
elseif tA == 'C'
|
||||
A11 = ctranspose(A[1,1]); A12 = ctranspose(A[2,1]); A21 = ctranspose(A[1,2]); A22 = ctranspose(A[2,2])
|
||||
else
|
||||
A11 = A[1,1]; A12 = A[1,2]; A21 = A[2,1]; A22 = A[2,2]
|
||||
end
|
||||
if tB == 'T'
|
||||
B11 = transpose(B[1,1]); B12 = transpose(B[2,1]); B21 = transpose(B[1,2]); B22 = transpose(B[2,2])
|
||||
elseif tB == 'C'
|
||||
B11 = ctranspose(B[1,1]); B12 = ctranspose(B[2,1]); B21 = ctranspose(B[1,2]); B22 = ctranspose(B[2,2])
|
||||
else
|
||||
B11 = B[1,1]; B12 = B[1,2]; B21 = B[2,1]; B22 = B[2,2]
|
||||
end
|
||||
C[1,1] = A11*B11 + A12*B21
|
||||
C[1,2] = A11*B12 + A12*B22
|
||||
C[2,1] = A21*B11 + A22*B21
|
||||
C[2,2] = A21*B12 + A22*B22
|
||||
end # inbounds
|
||||
C
|
||||
end
|
||||
|
||||
# Multiply 3x3 matrices
|
||||
function matmul3x3(tA, tB, A::AbstractMatrix{T}, B::AbstractMatrix{S}) where {T,S}
|
||||
matmul3x3!(similar(B, promote_op(matprod, T, S), 3, 3), tA, tB, A, B)
|
||||
end
|
||||
|
||||
function matmul3x3!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix)
|
||||
if !(size(A) == size(B) == size(C) == (3,3))
|
||||
throw(DimensionMismatch("A has size $(size(A)), B has size $(size(B)), C has size $(size(C))"))
|
||||
end
|
||||
@inbounds begin
|
||||
if tA == 'T'
|
||||
A11 = transpose(A[1,1]); A12 = transpose(A[2,1]); A13 = transpose(A[3,1])
|
||||
A21 = transpose(A[1,2]); A22 = transpose(A[2,2]); A23 = transpose(A[3,2])
|
||||
A31 = transpose(A[1,3]); A32 = transpose(A[2,3]); A33 = transpose(A[3,3])
|
||||
elseif tA == 'C'
|
||||
A11 = ctranspose(A[1,1]); A12 = ctranspose(A[2,1]); A13 = ctranspose(A[3,1])
|
||||
A21 = ctranspose(A[1,2]); A22 = ctranspose(A[2,2]); A23 = ctranspose(A[3,2])
|
||||
A31 = ctranspose(A[1,3]); A32 = ctranspose(A[2,3]); A33 = ctranspose(A[3,3])
|
||||
else
|
||||
A11 = A[1,1]; A12 = A[1,2]; A13 = A[1,3]
|
||||
A21 = A[2,1]; A22 = A[2,2]; A23 = A[2,3]
|
||||
A31 = A[3,1]; A32 = A[3,2]; A33 = A[3,3]
|
||||
end
|
||||
|
||||
if tB == 'T'
|
||||
B11 = transpose(B[1,1]); B12 = transpose(B[2,1]); B13 = transpose(B[3,1])
|
||||
B21 = transpose(B[1,2]); B22 = transpose(B[2,2]); B23 = transpose(B[3,2])
|
||||
B31 = transpose(B[1,3]); B32 = transpose(B[2,3]); B33 = transpose(B[3,3])
|
||||
elseif tB == 'C'
|
||||
B11 = ctranspose(B[1,1]); B12 = ctranspose(B[2,1]); B13 = ctranspose(B[3,1])
|
||||
B21 = ctranspose(B[1,2]); B22 = ctranspose(B[2,2]); B23 = ctranspose(B[3,2])
|
||||
B31 = ctranspose(B[1,3]); B32 = ctranspose(B[2,3]); B33 = ctranspose(B[3,3])
|
||||
else
|
||||
B11 = B[1,1]; B12 = B[1,2]; B13 = B[1,3]
|
||||
B21 = B[2,1]; B22 = B[2,2]; B23 = B[2,3]
|
||||
B31 = B[3,1]; B32 = B[3,2]; B33 = B[3,3]
|
||||
end
|
||||
|
||||
C[1,1] = A11*B11 + A12*B21 + A13*B31
|
||||
C[1,2] = A11*B12 + A12*B22 + A13*B32
|
||||
C[1,3] = A11*B13 + A12*B23 + A13*B33
|
||||
|
||||
C[2,1] = A21*B11 + A22*B21 + A23*B31
|
||||
C[2,2] = A21*B12 + A22*B22 + A23*B32
|
||||
C[2,3] = A21*B13 + A22*B23 + A23*B33
|
||||
|
||||
C[3,1] = A31*B11 + A32*B21 + A33*B31
|
||||
C[3,2] = A31*B12 + A32*B22 + A33*B32
|
||||
C[3,3] = A31*B13 + A32*B23 + A33*B33
|
||||
end # inbounds
|
||||
C
|
||||
end
|
||||
@@ -0,0 +1,847 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# QR and Hessenberg Factorizations
|
||||
"""
|
||||
QR <: Factorization
|
||||
|
||||
A QR matrix factorization stored in a packed format, typically obtained from
|
||||
[`qrfact`](@ref). If ``A`` is an `m`×`n` matrix, then
|
||||
|
||||
```math
|
||||
A = Q R
|
||||
```
|
||||
|
||||
where ``Q`` is an orthogonal/unitary matrix and ``R`` is upper triangular.
|
||||
The matrix ``Q`` is stored as a sequence of Householder reflectors ``v_i``
|
||||
and coefficients ``\\tau_i`` where:
|
||||
|
||||
```math
|
||||
Q = \\prod_{i=1}^{\\min(m,n)} (I - \\tau_i v_i v_i^T).
|
||||
```
|
||||
|
||||
The object has two fields:
|
||||
|
||||
* `factors` is an `m`×`n` matrix.
|
||||
|
||||
- The upper triangular part contains the elements of ``R``, that is `R =
|
||||
triu(F.factors)` for a `QR` object `F`.
|
||||
|
||||
- The subdiagonal part contains the reflectors ``v_i`` stored in a packed format where
|
||||
``v_i`` is the ``i``th column of the matrix `V = eye(m,n) + tril(F.factors,-1)`.
|
||||
|
||||
* `τ` is a vector of length `min(m,n)` containing the coefficients ``\tau_i``.
|
||||
|
||||
"""
|
||||
struct QR{T,S<:AbstractMatrix} <: Factorization{T}
|
||||
factors::S
|
||||
τ::Vector{T}
|
||||
QR{T,S}(factors::AbstractMatrix{T}, τ::Vector{T}) where {T,S<:AbstractMatrix} = new(factors, τ)
|
||||
end
|
||||
QR(factors::AbstractMatrix{T}, τ::Vector{T}) where {T} = QR{T,typeof(factors)}(factors, τ)
|
||||
|
||||
# Note. For QRCompactWY factorization without pivoting, the WY representation based method introduced in LAPACK 3.4
|
||||
"""
|
||||
QRCompactWY <: Factorization
|
||||
|
||||
A QR matrix factorization stored in a compact blocked format, typically obtained from
|
||||
[`qrfact`](@ref). If ``A`` is an `m`×`n` matrix, then
|
||||
|
||||
```math
|
||||
A = Q R
|
||||
```
|
||||
|
||||
where ``Q`` is an orthogonal/unitary matrix and ``R`` is upper triangular. It is similar
|
||||
to the [`QR`](@ref) format except that the orthogonal/unitary matrix ``Q`` is stored in
|
||||
*Compact WY* format [^Schreiber1989], as a lower trapezoidal matrix ``V`` and an upper
|
||||
triangular matrix ``T`` where
|
||||
|
||||
```math
|
||||
Q = \\prod_{i=1}^{\\min(m,n)} (I - \\tau_i v_i v_i^T) = I - V T V^T
|
||||
```
|
||||
|
||||
such that ``v_i`` is the ``i``th column of ``V``, and ``\tau_i`` is the ``i``th diagonal
|
||||
element of ``T``.
|
||||
|
||||
The object has two fields:
|
||||
|
||||
* `factors`, as in the [`QR`](@ref) type, is an `m`×`n` matrix.
|
||||
|
||||
- The upper triangular part contains the elements of ``R``, that is `R =
|
||||
triu(F.factors)` for a `QR` object `F`.
|
||||
|
||||
- The subdiagonal part contains the reflectors ``v_i`` stored in a packed format such
|
||||
that `V = eye(m,n) + tril(F.factors,-1)`.
|
||||
|
||||
* `T` is a square matrix with `min(m,n)` columns, whose upper triangular part gives the
|
||||
matrix ``T`` above (the subdiagonal elements are ignored).
|
||||
|
||||
!!! note
|
||||
|
||||
This format should not to be confused with the older *WY* representation
|
||||
[^Bischof1987].
|
||||
|
||||
|
||||
[^Bischof1987]: C Bischof and C Van Loan, "The WY representation for products of Householder matrices", SIAM J Sci Stat Comput 8 (1987), s2-s13. [doi:10.1137/0908009](http://dx.doi.org/10.1137/0908009)
|
||||
|
||||
[^Schreiber1989]: R Schreiber and C Van Loan, "A storage-efficient WY representation for products of Householder transformations", SIAM J Sci Stat Comput 10 (1989), 53-57. [doi:10.1137/0910005](http://dx.doi.org/10.1137/0910005)
|
||||
"""
|
||||
struct QRCompactWY{S,M<:AbstractMatrix} <: Factorization{S}
|
||||
factors::M
|
||||
T::Matrix{S}
|
||||
QRCompactWY{S,M}(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S,M<:AbstractMatrix} = new(factors, T)
|
||||
end
|
||||
QRCompactWY(factors::AbstractMatrix{S}, T::AbstractMatrix{S}) where {S} = QRCompactWY{S,typeof(factors)}(factors, T)
|
||||
|
||||
"""
|
||||
QRPivoted <: Factorization
|
||||
|
||||
A QR matrix factorization with column pivoting in a packed format, typically obtained from
|
||||
[`qrfact`](@ref). If ``A`` is an `m`×`n` matrix, then
|
||||
|
||||
```math
|
||||
A P = Q R
|
||||
```
|
||||
|
||||
where ``P`` is a permutation matrix, ``Q`` is an orthogonal/unitary matrix and ``R`` is
|
||||
upper triangular. The matrix ``Q`` is stored as a sequence of Householder reflectors:
|
||||
|
||||
```math
|
||||
Q = \\prod_{i=1}^{\\min(m,n)} (I - \\tau_i v_i v_i^T).
|
||||
```
|
||||
|
||||
The object has three fields:
|
||||
|
||||
* `factors` is an `m`×`n` matrix.
|
||||
|
||||
- The upper triangular part contains the elements of ``R``, that is `R =
|
||||
triu(F.factors)` for a `QR` object `F`.
|
||||
|
||||
- The subdiagonal part contains the reflectors ``v_i`` stored in a packed format where
|
||||
``v_i`` is the ``i``th column of the matrix `V = eye(m,n) + tril(F.factors,-1)`.
|
||||
|
||||
* `τ` is a vector of length `min(m,n)` containing the coefficients ``\tau_i``.
|
||||
|
||||
* `jpvt` is an integer vector of length `n` corresponding to the permutation ``P``.
|
||||
"""
|
||||
struct QRPivoted{T,S<:AbstractMatrix} <: Factorization{T}
|
||||
factors::S
|
||||
τ::Vector{T}
|
||||
jpvt::Vector{BlasInt}
|
||||
QRPivoted{T,S}(factors::AbstractMatrix{T}, τ::Vector{T}, jpvt::Vector{BlasInt}) where {T,S<:AbstractMatrix} =
|
||||
new(factors, τ, jpvt)
|
||||
end
|
||||
QRPivoted(factors::AbstractMatrix{T}, τ::Vector{T}, jpvt::Vector{BlasInt}) where {T} =
|
||||
QRPivoted{T,typeof(factors)}(factors, τ, jpvt)
|
||||
|
||||
function qrfactUnblocked!(A::AbstractMatrix{T}) where {T}
|
||||
m, n = size(A)
|
||||
τ = zeros(T, min(m,n))
|
||||
for k = 1:min(m - 1 + !(T<:Real), n)
|
||||
x = view(A, k:m, k)
|
||||
τk = reflector!(x)
|
||||
τ[k] = τk
|
||||
reflectorApply!(x, τk, view(A, k:m, k + 1:n))
|
||||
end
|
||||
QR(A, τ)
|
||||
end
|
||||
|
||||
# Find index for columns with largest two norm
|
||||
function indmaxcolumn(A::StridedMatrix)
|
||||
mm = norm(view(A, :, 1))
|
||||
ii = 1
|
||||
for i = 2:size(A, 2)
|
||||
mi = norm(view(A, :, i))
|
||||
if abs(mi) > mm
|
||||
mm = mi
|
||||
ii = i
|
||||
end
|
||||
end
|
||||
return ii
|
||||
end
|
||||
|
||||
function qrfactPivotedUnblocked!(A::StridedMatrix)
|
||||
m, n = size(A)
|
||||
piv = collect(UnitRange{BlasInt}(1,n))
|
||||
τ = Vector{eltype(A)}(min(m,n))
|
||||
for j = 1:min(m,n)
|
||||
|
||||
# Find column with maximum norm in trailing submatrix
|
||||
jm = indmaxcolumn(view(A, j:m, j:n)) + j - 1
|
||||
|
||||
if jm != j
|
||||
# Flip elements in pivoting vector
|
||||
tmpp = piv[jm]
|
||||
piv[jm] = piv[j]
|
||||
piv[j] = tmpp
|
||||
|
||||
# Update matrix with
|
||||
for i = 1:m
|
||||
tmp = A[i,jm]
|
||||
A[i,jm] = A[i,j]
|
||||
A[i,j] = tmp
|
||||
end
|
||||
end
|
||||
|
||||
# Compute reflector of columns j
|
||||
x = view(A, j:m, j)
|
||||
τj = LinAlg.reflector!(x)
|
||||
τ[j] = τj
|
||||
|
||||
# Update trailing submatrix with reflector
|
||||
LinAlg.reflectorApply!(x, τj, view(A, j:m, j+1:n))
|
||||
end
|
||||
return LinAlg.QRPivoted{eltype(A), typeof(A)}(A, τ, piv)
|
||||
end
|
||||
|
||||
# LAPACK version
|
||||
qrfact!(A::StridedMatrix{<:BlasFloat}, ::Type{Val{false}}) = QRCompactWY(LAPACK.geqrt!(A, min(minimum(size(A)), 36))...)
|
||||
qrfact!(A::StridedMatrix{<:BlasFloat}, ::Type{Val{true}}) = QRPivoted(LAPACK.geqp3!(A)...)
|
||||
qrfact!(A::StridedMatrix{<:BlasFloat}) = qrfact!(A, Val{false})
|
||||
|
||||
# Generic fallbacks
|
||||
|
||||
"""
|
||||
qrfact!(A, pivot=Val{false})
|
||||
|
||||
`qrfact!` is the same as [`qrfact`](@ref) when `A` is a subtype of
|
||||
`StridedMatrix`, but saves space by overwriting the input `A`, instead of creating a copy.
|
||||
An [`InexactError`](@ref) exception is thrown if the factorization produces a number not
|
||||
representable by the element type of `A`, e.g. for integer types.
|
||||
"""
|
||||
qrfact!(A::StridedMatrix, ::Type{Val{false}}) = qrfactUnblocked!(A)
|
||||
qrfact!(A::StridedMatrix, ::Type{Val{true}}) = qrfactPivotedUnblocked!(A)
|
||||
qrfact!(A::StridedMatrix) = qrfact!(A, Val{false})
|
||||
|
||||
"""
|
||||
qrfact(A, pivot=Val{false}) -> F
|
||||
|
||||
Compute the QR factorization of the matrix `A`: an orthogonal (or unitary if `A` is
|
||||
complex-valued) matrix `Q`, and an upper triangular matrix `R` such that
|
||||
|
||||
```math
|
||||
A = Q R
|
||||
```
|
||||
|
||||
The returned object `F` stores the factorization in a packed format:
|
||||
|
||||
- if `pivot == Val{true}` then `F` is a [`QRPivoted`](@ref) object,
|
||||
|
||||
- otherwise if the element type of `A` is a BLAS type ([`Float32`](@ref), [`Float64`](@ref),
|
||||
`Complex64` or `Complex128`), then `F` is a [`QRCompactWY`](@ref) object,
|
||||
|
||||
- otherwise `F` is a [`QR`](@ref) object.
|
||||
|
||||
The individual components of the factorization `F` can be accessed by indexing with a symbol:
|
||||
|
||||
- `F[:Q]`: the orthogonal/unitary matrix `Q`
|
||||
- `F[:R]`: the upper triangular matrix `R`
|
||||
- `F[:p]`: the permutation vector of the pivot ([`QRPivoted`](@ref) only)
|
||||
- `F[:P]`: the permutation matrix of the pivot ([`QRPivoted`](@ref) only)
|
||||
|
||||
The following functions are available for the `QR` objects: [`inv`](@ref), [`size`](@ref),
|
||||
and [`\\`](@ref). When `A` is rectangular, `\\` will return a least squares
|
||||
solution and if the solution is not unique, the one with smallest norm is returned.
|
||||
|
||||
Multiplication with respect to either thin or full `Q` is allowed, i.e. both `F[:Q]*F[:R]`
|
||||
and `F[:Q]*A` are supported. A `Q` matrix can be converted into a regular matrix with
|
||||
[`full`](@ref) which has a named argument `thin`.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [3.0 -6.0; 4.0 -8.0; 0.0 1.0]
|
||||
3×2 Array{Float64,2}:
|
||||
3.0 -6.0
|
||||
4.0 -8.0
|
||||
0.0 1.0
|
||||
|
||||
julia> F = qrfact(A)
|
||||
Base.LinAlg.QRCompactWY{Float64,Array{Float64,2}} with factors Q and R:
|
||||
[-0.6 0.0 0.8; -0.8 0.0 -0.6; 0.0 -1.0 0.0]
|
||||
[-5.0 10.0; 0.0 -1.0]
|
||||
|
||||
julia> F[:Q] * F[:R] == A
|
||||
true
|
||||
```
|
||||
|
||||
!!! note
|
||||
`qrfact` returns multiple types because LAPACK uses several representations
|
||||
that minimize the memory storage requirements of products of Householder
|
||||
elementary reflectors, so that the `Q` and `R` matrices can be stored
|
||||
compactly rather as two separate dense matrices.
|
||||
"""
|
||||
function qrfact(A::AbstractMatrix{T}, arg) where T
|
||||
AA = similar(A, typeof(zero(T)/norm(one(T))), size(A))
|
||||
copy!(AA, A)
|
||||
return qrfact!(AA, arg)
|
||||
end
|
||||
function qrfact(A::AbstractMatrix{T}) where T
|
||||
AA = similar(A, typeof(zero(T)/norm(one(T))), size(A))
|
||||
copy!(AA, A)
|
||||
return qrfact!(AA)
|
||||
end
|
||||
qrfact(x::Number) = qrfact(fill(x,1,1))
|
||||
|
||||
"""
|
||||
qr(A, pivot=Val{false}; thin::Bool=true) -> Q, R, [p]
|
||||
|
||||
Compute the (pivoted) QR factorization of `A` such that either `A = Q*R` or `A[:,p] = Q*R`.
|
||||
Also see [`qrfact`](@ref).
|
||||
The default is to compute a thin factorization. Note that `R` is not
|
||||
extended with zeros when the full `Q` is requested.
|
||||
"""
|
||||
qr(A::Union{Number, AbstractMatrix}, pivot::Union{Type{Val{false}}, Type{Val{true}}}=Val{false}; thin::Bool=true) =
|
||||
_qr(A, pivot, thin=thin)
|
||||
function _qr(A::Union{Number, AbstractMatrix}, ::Type{Val{false}}; thin::Bool=true)
|
||||
F = qrfact(A, Val{false})
|
||||
full(getq(F), thin=thin), F[:R]::Matrix{eltype(F)}
|
||||
end
|
||||
function _qr(A::Union{Number, AbstractMatrix}, ::Type{Val{true}}; thin::Bool=true)
|
||||
F = qrfact(A, Val{true})
|
||||
full(getq(F), thin=thin), F[:R]::Matrix{eltype(F)}, F[:p]::Vector{BlasInt}
|
||||
end
|
||||
|
||||
"""
|
||||
qr(v::AbstractVector) -> w, r
|
||||
|
||||
Computes the polar decomposition of a vector.
|
||||
Returns `w`, a unit vector in the direction of `v`, and
|
||||
`r`, the norm of `v`.
|
||||
|
||||
See also [`normalize`](@ref), [`normalize!`](@ref),
|
||||
and [`LinAlg.qr!`](@ref).
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> v = [1; 2]
|
||||
2-element Array{Int64,1}:
|
||||
1
|
||||
2
|
||||
|
||||
julia> w, r = qr(v)
|
||||
([0.447214, 0.894427], 2.23606797749979)
|
||||
|
||||
julia> w*r == v
|
||||
true
|
||||
```
|
||||
"""
|
||||
function qr(v::AbstractVector)
|
||||
nrm = norm(v)
|
||||
if !isempty(v)
|
||||
vv = copy_oftype(v, typeof(v[1]/nrm))
|
||||
return __normalize!(vv, nrm), nrm
|
||||
else
|
||||
T = typeof(zero(eltype(v))/nrm)
|
||||
return T[], oneunit(T)
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
LinAlg.qr!(v::AbstractVector) -> w, r
|
||||
|
||||
Computes the polar decomposition of a vector. Instead of returning a new vector
|
||||
as `qr(v::AbstractVector)`, this function mutates the input vector `v` in place.
|
||||
Returns `w`, a unit vector in the direction of `v` (this is a mutation of `v`),
|
||||
and `r`, the norm of `v`.
|
||||
|
||||
See also [`normalize`](@ref), [`normalize!`](@ref),
|
||||
and [`qr`](@ref).
|
||||
"""
|
||||
function qr!(v::AbstractVector)
|
||||
nrm = norm(v)
|
||||
__normalize!(v, nrm), nrm
|
||||
end
|
||||
|
||||
# Conversions
|
||||
convert(::Type{QR{T}}, A::QR) where {T} = QR(convert(AbstractMatrix{T}, A.factors), convert(Vector{T}, A.τ))
|
||||
convert(::Type{Factorization{T}}, A::QR{T}) where {T} = A
|
||||
convert(::Type{Factorization{T}}, A::QR) where {T} = convert(QR{T}, A)
|
||||
convert(::Type{QRCompactWY{T}}, A::QRCompactWY) where {T} = QRCompactWY(convert(AbstractMatrix{T}, A.factors), convert(AbstractMatrix{T}, A.T))
|
||||
convert(::Type{Factorization{T}}, A::QRCompactWY{T}) where {T} = A
|
||||
convert(::Type{Factorization{T}}, A::QRCompactWY) where {T} = convert(QRCompactWY{T}, A)
|
||||
convert(::Type{AbstractMatrix}, F::Union{QR,QRCompactWY}) = F[:Q] * F[:R]
|
||||
convert(::Type{AbstractArray}, F::Union{QR,QRCompactWY}) = convert(AbstractMatrix, F)
|
||||
convert(::Type{Matrix}, F::Union{QR,QRCompactWY}) = convert(Array, convert(AbstractArray, F))
|
||||
convert(::Type{Array}, F::Union{QR,QRCompactWY}) = convert(Matrix, F)
|
||||
full(F::Union{QR,QRCompactWY}) = convert(AbstractArray, F)
|
||||
convert(::Type{QRPivoted{T}}, A::QRPivoted) where {T} = QRPivoted(convert(AbstractMatrix{T}, A.factors), convert(Vector{T}, A.τ), A.jpvt)
|
||||
convert(::Type{Factorization{T}}, A::QRPivoted{T}) where {T} = A
|
||||
convert(::Type{Factorization{T}}, A::QRPivoted) where {T} = convert(QRPivoted{T}, A)
|
||||
convert(::Type{AbstractMatrix}, F::QRPivoted) = (F[:Q] * F[:R])[:,invperm(F[:p])]
|
||||
convert(::Type{AbstractArray}, F::QRPivoted) = convert(AbstractMatrix, F)
|
||||
convert(::Type{Matrix}, F::QRPivoted) = convert(Array, convert(AbstractArray, F))
|
||||
convert(::Type{Array}, F::QRPivoted) = convert(Matrix, F)
|
||||
full(F::QRPivoted) = convert(AbstractArray, F)
|
||||
|
||||
function show(io::IO, F::Union{QR, QRCompactWY, QRPivoted})
|
||||
println(io, "$(typeof(F)) with factors Q and R:")
|
||||
show(io, F[:Q])
|
||||
println(io)
|
||||
show(io, F[:R])
|
||||
end
|
||||
|
||||
function getindex(A::QR, d::Symbol)
|
||||
m, n = size(A)
|
||||
if d == :R
|
||||
return triu!(A.factors[1:min(m,n), 1:n])
|
||||
elseif d == :Q
|
||||
return getq(A)
|
||||
else
|
||||
throw(KeyError(d))
|
||||
end
|
||||
end
|
||||
function getindex(A::QRCompactWY, d::Symbol)
|
||||
m, n = size(A)
|
||||
if d == :R
|
||||
return triu!(A.factors[1:min(m,n), 1:n])
|
||||
elseif d == :Q
|
||||
return getq(A)
|
||||
else
|
||||
throw(KeyError(d))
|
||||
end
|
||||
end
|
||||
function getindex(A::QRPivoted{T}, d::Symbol) where T
|
||||
m, n = size(A)
|
||||
if d == :R
|
||||
return triu!(A.factors[1:min(m,n), 1:n])
|
||||
elseif d == :Q
|
||||
return getq(A)
|
||||
elseif d == :p
|
||||
return A.jpvt
|
||||
elseif d == :P
|
||||
p = A[:p]
|
||||
n = length(p)
|
||||
P = zeros(T, n, n)
|
||||
for i in 1:n
|
||||
P[p[i],i] = one(T)
|
||||
end
|
||||
return P
|
||||
else
|
||||
throw(KeyError(d))
|
||||
end
|
||||
end
|
||||
|
||||
# Type-stable interface to get Q
|
||||
getq(A::QRCompactWY) = QRCompactWYQ(A.factors,A.T)
|
||||
getq(A::Union{QR, QRPivoted}) = QRPackedQ(A.factors,A.τ)
|
||||
|
||||
"""
|
||||
QRPackedQ <: AbstractMatrix
|
||||
|
||||
The orthogonal/unitary ``Q`` matrix of a QR factorization stored in [`QR`](@ref) or
|
||||
[`QRPivoted`](@ref) format.
|
||||
"""
|
||||
struct QRPackedQ{T,S<:AbstractMatrix} <: AbstractMatrix{T}
|
||||
factors::S
|
||||
τ::Vector{T}
|
||||
QRPackedQ{T,S}(factors::AbstractMatrix{T}, τ::Vector{T}) where {T,S<:AbstractMatrix} = new(factors, τ)
|
||||
end
|
||||
QRPackedQ(factors::AbstractMatrix{T}, τ::Vector{T}) where {T} = QRPackedQ{T,typeof(factors)}(factors, τ)
|
||||
|
||||
"""
|
||||
QRCompactWYQ <: AbstractMatrix
|
||||
|
||||
The orthogonal/unitary ``Q`` matrix of a QR factorization stored in [`QRCompactWY`](@ref)
|
||||
format.
|
||||
"""
|
||||
struct QRCompactWYQ{S, M<:AbstractMatrix} <: AbstractMatrix{S}
|
||||
factors::M
|
||||
T::Matrix{S}
|
||||
QRCompactWYQ{S,M}(factors::AbstractMatrix{S}, T::Matrix{S}) where {S,M<:AbstractMatrix} = new(factors, T)
|
||||
end
|
||||
QRCompactWYQ(factors::AbstractMatrix{S}, T::Matrix{S}) where {S} = QRCompactWYQ{S,typeof(factors)}(factors, T)
|
||||
|
||||
convert(::Type{QRPackedQ{T}}, Q::QRPackedQ) where {T} = QRPackedQ(convert(AbstractMatrix{T}, Q.factors), convert(Vector{T}, Q.τ))
|
||||
convert(::Type{AbstractMatrix{T}}, Q::QRPackedQ{T}) where {T} = Q
|
||||
convert(::Type{AbstractMatrix{T}}, Q::QRPackedQ) where {T} = convert(QRPackedQ{T}, Q)
|
||||
convert(::Type{QRCompactWYQ{S}}, Q::QRCompactWYQ) where {S} = QRCompactWYQ(convert(AbstractMatrix{S}, Q.factors), convert(AbstractMatrix{S}, Q.T))
|
||||
convert(::Type{AbstractMatrix{S}}, Q::QRCompactWYQ{S}) where {S} = Q
|
||||
convert(::Type{AbstractMatrix{S}}, Q::QRCompactWYQ) where {S} = convert(QRCompactWYQ{S}, Q)
|
||||
convert(::Type{Matrix}, A::Union{QRPackedQ{T},QRCompactWYQ{T}}) where {T} = A_mul_B!(A, eye(T, size(A.factors, 1), minimum(size(A.factors))))
|
||||
convert(::Type{Array}, A::Union{QRPackedQ,QRCompactWYQ}) = convert(Matrix, A)
|
||||
|
||||
"""
|
||||
full(A::Union{QRPackedQ,QRCompactWYQ}; thin::Bool=true) -> Matrix
|
||||
|
||||
Converts an orthogonal or unitary matrix stored as a `QRCompactWYQ` object, i.e. in the
|
||||
compact WY format [^Bischof1987], or in the `QRPackedQ` format, to a dense matrix.
|
||||
|
||||
Optionally takes a `thin` Boolean argument, which if `true` omits the columns that span the
|
||||
rows of `R` in the QR factorization that are zero. The resulting matrix is the `Q` in a thin
|
||||
QR factorization (sometimes called the reduced QR factorization). If `false`, returns a `Q`
|
||||
that spans all rows of `R` in its corresponding QR factorization.
|
||||
"""
|
||||
function full{T}(A::Union{QRPackedQ{T},QRCompactWYQ{T}}; thin::Bool = true)
|
||||
if thin
|
||||
convert(Array, A)
|
||||
else
|
||||
A_mul_B!(A, eye(T, size(A.factors, 1)))
|
||||
end
|
||||
end
|
||||
|
||||
size(A::Union{QR,QRCompactWY,QRPivoted}, dim::Integer) = size(A.factors, dim)
|
||||
size(A::Union{QR,QRCompactWY,QRPivoted}) = size(A.factors)
|
||||
size(A::Union{QRPackedQ,QRCompactWYQ}, dim::Integer) = 0 < dim ? (dim <= 2 ? size(A.factors, 1) : 1) : throw(BoundsError())
|
||||
size(A::Union{QRPackedQ,QRCompactWYQ}) = size(A, 1), size(A, 2)
|
||||
|
||||
|
||||
function getindex(A::Union{QRPackedQ,QRCompactWYQ}, i::Integer, j::Integer)
|
||||
x = zeros(eltype(A), size(A, 1))
|
||||
x[i] = 1
|
||||
y = zeros(eltype(A), size(A, 2))
|
||||
y[j] = 1
|
||||
return dot(x, A_mul_B!(A, y))
|
||||
end
|
||||
|
||||
## Multiplication by Q
|
||||
### QB
|
||||
A_mul_B!(A::QRCompactWYQ{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.gemqrt!('L','N',A.factors,A.T,B)
|
||||
A_mul_B!(A::QRPackedQ{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = LAPACK.ormqr!('L','N',A.factors,A.τ,B)
|
||||
function A_mul_B!(A::QRPackedQ, B::AbstractVecOrMat)
|
||||
mA, nA = size(A.factors)
|
||||
mB, nB = size(B,1), size(B,2)
|
||||
if mA != mB
|
||||
throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)"))
|
||||
end
|
||||
Afactors = A.factors
|
||||
@inbounds begin
|
||||
for k = min(mA,nA):-1:1
|
||||
for j = 1:nB
|
||||
vBj = B[k,j]
|
||||
for i = k+1:mB
|
||||
vBj += conj(Afactors[i,k])*B[i,j]
|
||||
end
|
||||
vBj = A.τ[k]*vBj
|
||||
B[k,j] -= vBj
|
||||
for i = k+1:mB
|
||||
B[i,j] -= Afactors[i,k]*vBj
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
B
|
||||
end
|
||||
|
||||
function (*)(A::Union{QRPackedQ,QRCompactWYQ}, b::StridedVector)
|
||||
TAb = promote_type(eltype(A), eltype(b))
|
||||
Anew = convert(AbstractMatrix{TAb}, A)
|
||||
if size(A.factors, 1) == length(b)
|
||||
bnew = copy_oftype(b, TAb)
|
||||
elseif size(A.factors, 2) == length(b)
|
||||
bnew = [b; zeros(TAb, size(A.factors, 1) - length(b))]
|
||||
else
|
||||
throw(DimensionMismatch("vector must have length either $(size(A.factors, 1)) or $(size(A.factors, 2))"))
|
||||
end
|
||||
A_mul_B!(Anew, bnew)
|
||||
end
|
||||
function (*)(A::Union{QRPackedQ,QRCompactWYQ}, B::StridedMatrix)
|
||||
TAB = promote_type(eltype(A), eltype(B))
|
||||
Anew = convert(AbstractMatrix{TAB}, A)
|
||||
if size(A.factors, 1) == size(B, 1)
|
||||
Bnew = copy_oftype(B, TAB)
|
||||
elseif size(A.factors, 2) == size(B, 1)
|
||||
Bnew = [B; zeros(TAB, size(A.factors, 1) - size(B,1), size(B, 2))]
|
||||
else
|
||||
throw(DimensionMismatch("first dimension of matrix must have size either $(size(A.factors, 1)) or $(size(A.factors, 2))"))
|
||||
end
|
||||
A_mul_B!(Anew, Bnew)
|
||||
end
|
||||
|
||||
### QcB
|
||||
Ac_mul_B!(A::QRCompactWYQ{T}, B::StridedVecOrMat{T}) where {T<:BlasReal} = LAPACK.gemqrt!('L','T',A.factors,A.T,B)
|
||||
Ac_mul_B!(A::QRCompactWYQ{T}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = LAPACK.gemqrt!('L','C',A.factors,A.T,B)
|
||||
Ac_mul_B!(A::QRPackedQ{T}, B::StridedVecOrMat{T}) where {T<:BlasReal} = LAPACK.ormqr!('L','T',A.factors,A.τ,B)
|
||||
Ac_mul_B!(A::QRPackedQ{T}, B::StridedVecOrMat{T}) where {T<:BlasComplex} = LAPACK.ormqr!('L','C',A.factors,A.τ,B)
|
||||
function Ac_mul_B!(A::QRPackedQ, B::AbstractVecOrMat)
|
||||
mA, nA = size(A.factors)
|
||||
mB, nB = size(B,1), size(B,2)
|
||||
if mA != mB
|
||||
throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but B has dimensions ($mB, $nB)"))
|
||||
end
|
||||
Afactors = A.factors
|
||||
@inbounds begin
|
||||
for k = 1:min(mA,nA)
|
||||
for j = 1:nB
|
||||
vBj = B[k,j]
|
||||
for i = k+1:mB
|
||||
vBj += conj(Afactors[i,k])*B[i,j]
|
||||
end
|
||||
vBj = conj(A.τ[k])*vBj
|
||||
B[k,j] -= vBj
|
||||
for i = k+1:mB
|
||||
B[i,j] -= Afactors[i,k]*vBj
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
B
|
||||
end
|
||||
function Ac_mul_B(Q::Union{QRPackedQ,QRCompactWYQ}, B::StridedVecOrMat)
|
||||
TQB = promote_type(eltype(Q), eltype(B))
|
||||
return Ac_mul_B!(convert(AbstractMatrix{TQB}, Q), copy_oftype(B, TQB))
|
||||
end
|
||||
|
||||
### QBc/QcBc
|
||||
for (f1, f2) in ((:A_mul_Bc, :A_mul_B!),
|
||||
(:Ac_mul_Bc, :Ac_mul_B!))
|
||||
@eval begin
|
||||
function ($f1)(Q::Union{QRPackedQ,QRCompactWYQ}, B::StridedVecOrMat)
|
||||
TQB = promote_type(eltype(Q), eltype(B))
|
||||
Bc = similar(B, TQB, (size(B, 2), size(B, 1)))
|
||||
ctranspose!(Bc, B)
|
||||
return ($f2)(convert(AbstractMatrix{TQB}, Q), Bc)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
### AQ
|
||||
A_mul_B!(A::StridedVecOrMat{T}, B::QRCompactWYQ{T}) where {T<:BlasFloat} = LAPACK.gemqrt!('R','N', B.factors, B.T, A)
|
||||
A_mul_B!(A::StridedVecOrMat{T}, B::QRPackedQ{T}) where {T<:BlasFloat} = LAPACK.ormqr!('R', 'N', B.factors, B.τ, A)
|
||||
function A_mul_B!(A::StridedMatrix,Q::QRPackedQ)
|
||||
mQ, nQ = size(Q.factors)
|
||||
mA, nA = size(A,1), size(A,2)
|
||||
if nA != mQ
|
||||
throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)"))
|
||||
end
|
||||
Qfactors = Q.factors
|
||||
@inbounds begin
|
||||
for k = 1:min(mQ,nQ)
|
||||
for i = 1:mA
|
||||
vAi = A[i,k]
|
||||
for j = k+1:mQ
|
||||
vAi += A[i,j]*Qfactors[j,k]
|
||||
end
|
||||
vAi = vAi*Q.τ[k]
|
||||
A[i,k] -= vAi
|
||||
for j = k+1:nA
|
||||
A[i,j] -= vAi*conj(Qfactors[j,k])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
A
|
||||
end
|
||||
|
||||
function (*)(A::StridedMatrix, Q::Union{QRPackedQ,QRCompactWYQ})
|
||||
TAQ = promote_type(eltype(A), eltype(Q))
|
||||
return A_mul_B!(copy_oftype(A, TAQ), convert(AbstractMatrix{TAQ}, Q))
|
||||
end
|
||||
|
||||
### AQc
|
||||
A_mul_Bc!(A::StridedVecOrMat{T}, B::QRCompactWYQ{T}) where {T<:BlasReal} = LAPACK.gemqrt!('R','T',B.factors,B.T,A)
|
||||
A_mul_Bc!(A::StridedVecOrMat{T}, B::QRCompactWYQ{T}) where {T<:BlasComplex} = LAPACK.gemqrt!('R','C',B.factors,B.T,A)
|
||||
A_mul_Bc!(A::StridedVecOrMat{T}, B::QRPackedQ{T}) where {T<:BlasReal} = LAPACK.ormqr!('R','T',B.factors,B.τ,A)
|
||||
A_mul_Bc!(A::StridedVecOrMat{T}, B::QRPackedQ{T}) where {T<:BlasComplex} = LAPACK.ormqr!('R','C',B.factors,B.τ,A)
|
||||
function A_mul_Bc!(A::AbstractMatrix,Q::QRPackedQ)
|
||||
mQ, nQ = size(Q.factors)
|
||||
mA, nA = size(A,1), size(A,2)
|
||||
if nA != mQ
|
||||
throw(DimensionMismatch("matrix A has dimensions ($mA,$nA) but matrix Q has dimensions ($mQ, $nQ)"))
|
||||
end
|
||||
Qfactors = Q.factors
|
||||
@inbounds begin
|
||||
for k = min(mQ,nQ):-1:1
|
||||
for i = 1:mA
|
||||
vAi = A[i,k]
|
||||
for j = k+1:mQ
|
||||
vAi += A[i,j]*Qfactors[j,k]
|
||||
end
|
||||
vAi = vAi*conj(Q.τ[k])
|
||||
A[i,k] -= vAi
|
||||
for j = k+1:nA
|
||||
A[i,j] -= vAi*conj(Qfactors[j,k])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
A
|
||||
end
|
||||
function A_mul_Bc(A::AbstractMatrix, B::Union{QRCompactWYQ,QRPackedQ})
|
||||
TAB = promote_type(eltype(A),eltype(B))
|
||||
BB = convert(AbstractMatrix{TAB}, B)
|
||||
if size(A,2) == size(B.factors, 1)
|
||||
AA = similar(A, TAB, size(A))
|
||||
copy!(AA, A)
|
||||
return A_mul_Bc!(AA, BB)
|
||||
elseif size(A,2) == size(B.factors,2)
|
||||
return A_mul_Bc!([A zeros(TAB, size(A, 1), size(B.factors, 1) - size(B.factors, 2))], BB)
|
||||
else
|
||||
throw(DimensionMismatch("matrix A has dimensions $(size(A)) but matrix B has dimensions $(size(B))"))
|
||||
end
|
||||
end
|
||||
@inline A_mul_Bc(rowvec::RowVector, B::Union{LinAlg.QRCompactWYQ,LinAlg.QRPackedQ}) = ctranspose(B*ctranspose(rowvec))
|
||||
|
||||
|
||||
### AcQ/AcQc
|
||||
for (f1, f2) in ((:Ac_mul_B, :A_mul_B!),
|
||||
(:Ac_mul_Bc, :A_mul_Bc!))
|
||||
@eval begin
|
||||
function ($f1)(A::StridedVecOrMat, Q::Union{QRPackedQ,QRCompactWYQ})
|
||||
TAQ = promote_type(eltype(A), eltype(Q))
|
||||
Ac = similar(A, TAQ, (size(A, 2), size(A, 1)))
|
||||
ctranspose!(Ac, A)
|
||||
return ($f2)(Ac, convert(AbstractMatrix{TAQ}, Q))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
A_ldiv_B!(A::QRCompactWY{T}, b::StridedVector{T}) where {T<:BlasFloat} = (A_ldiv_B!(UpperTriangular(A[:R]), view(Ac_mul_B!(A[:Q], b), 1:size(A, 2))); b)
|
||||
A_ldiv_B!(A::QRCompactWY{T}, B::StridedMatrix{T}) where {T<:BlasFloat} = (A_ldiv_B!(UpperTriangular(A[:R]), view(Ac_mul_B!(A[:Q], B), 1:size(A, 2), 1:size(B, 2))); B)
|
||||
|
||||
# Julia implementation similarly to xgelsy
|
||||
function A_ldiv_B!(A::QRPivoted{T}, B::StridedMatrix{T}, rcond::Real) where T<:BlasFloat
|
||||
mA, nA = size(A.factors)
|
||||
nr = min(mA,nA)
|
||||
nrhs = size(B, 2)
|
||||
if nr == 0
|
||||
return B, 0
|
||||
end
|
||||
ar = abs(A.factors[1])
|
||||
if ar == 0
|
||||
B[1:nA, :] = 0
|
||||
return B, 0
|
||||
end
|
||||
rnk = 1
|
||||
xmin = ones(T, 1)
|
||||
xmax = ones(T, 1)
|
||||
tmin = tmax = ar
|
||||
while rnk < nr
|
||||
tmin, smin, cmin = LAPACK.laic1!(2, xmin, tmin, view(A.factors, 1:rnk, rnk + 1), A.factors[rnk + 1, rnk + 1])
|
||||
tmax, smax, cmax = LAPACK.laic1!(1, xmax, tmax, view(A.factors, 1:rnk, rnk + 1), A.factors[rnk + 1, rnk + 1])
|
||||
tmax*rcond > tmin && break
|
||||
push!(xmin, cmin)
|
||||
push!(xmax, cmax)
|
||||
for i = 1:rnk
|
||||
xmin[i] *= smin
|
||||
xmax[i] *= smax
|
||||
end
|
||||
rnk += 1
|
||||
end
|
||||
C, τ = LAPACK.tzrzf!(A.factors[1:rnk,:])
|
||||
A_ldiv_B!(UpperTriangular(C[1:rnk,1:rnk]),view(Ac_mul_B!(getq(A),view(B, 1:mA, 1:nrhs)),1:rnk,1:nrhs))
|
||||
B[rnk+1:end,:] = zero(T)
|
||||
LAPACK.ormrz!('L', eltype(B)<:Complex ? 'C' : 'T', C, τ, view(B,1:nA,1:nrhs))
|
||||
B[1:nA,:] = view(B, 1:nA, :)[invperm(A[:p]::Vector{BlasInt}),:]
|
||||
return B, rnk
|
||||
end
|
||||
A_ldiv_B!(A::QRPivoted{T}, B::StridedVector{T}) where {T<:BlasFloat} = vec(A_ldiv_B!(A,reshape(B,length(B),1)))
|
||||
A_ldiv_B!(A::QRPivoted{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} = A_ldiv_B!(A, B, maximum(size(A))*eps(real(float(one(eltype(B))))))[1]
|
||||
function A_ldiv_B!(A::QR{T}, B::StridedMatrix{T}) where T
|
||||
m, n = size(A)
|
||||
minmn = min(m,n)
|
||||
mB, nB = size(B)
|
||||
Ac_mul_B!(A[:Q], view(B, 1:m, :))
|
||||
R = A[:R]
|
||||
@inbounds begin
|
||||
if n > m # minimum norm solution
|
||||
τ = zeros(T,m)
|
||||
for k = m:-1:1 # Trapezoid to triangular by elementary operation
|
||||
x = view(R, k, [k; m + 1:n])
|
||||
τk = reflector!(x)
|
||||
τ[k] = τk'
|
||||
for i = 1:k - 1
|
||||
vRi = R[i,k]
|
||||
for j = m + 1:n
|
||||
vRi += R[i,j]*x[j - m + 1]'
|
||||
end
|
||||
vRi *= τk
|
||||
R[i,k] -= vRi
|
||||
for j = m + 1:n
|
||||
R[i,j] -= vRi*x[j - m + 1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Base.A_ldiv_B!(UpperTriangular(view(R, :, 1:minmn)), view(B, 1:minmn, :))
|
||||
if n > m # Apply elementary transformation to solution
|
||||
B[m + 1:mB,1:nB] = zero(T)
|
||||
for j = 1:nB
|
||||
for k = 1:m
|
||||
vBj = B[k,j]
|
||||
for i = m + 1:n
|
||||
vBj += B[i,j]*R[k,i]'
|
||||
end
|
||||
vBj *= τ[k]
|
||||
B[k,j] -= vBj
|
||||
for i = m + 1:n
|
||||
B[i,j] -= R[k,i]*vBj
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return B
|
||||
end
|
||||
A_ldiv_B!(A::QR, B::StridedVector) = A_ldiv_B!(A, reshape(B, length(B), 1))[:]
|
||||
function A_ldiv_B!(A::QRPivoted, b::StridedVector)
|
||||
A_ldiv_B!(QR(A.factors,A.τ), b)
|
||||
b[1:size(A.factors, 2)] = view(b, 1:size(A.factors, 2))[invperm(A.jpvt)]
|
||||
b
|
||||
end
|
||||
function A_ldiv_B!(A::QRPivoted, B::StridedMatrix)
|
||||
A_ldiv_B!(QR(A.factors, A.τ), B)
|
||||
B[1:size(A.factors, 2),:] = view(B, 1:size(A.factors, 2), :)[invperm(A.jpvt),:]
|
||||
B
|
||||
end
|
||||
|
||||
# convenience methods
|
||||
## return only the solution of a least squares problem while avoiding promoting
|
||||
## vectors to matrices.
|
||||
_cut_B(x::AbstractVector, r::UnitRange) = length(x) > length(r) ? x[r] : x
|
||||
_cut_B(X::AbstractMatrix, r::UnitRange) = size(X, 1) > length(r) ? X[r,:] : X
|
||||
|
||||
## append right hand side with zeros if necessary
|
||||
_zeros(::Type{T}, b::AbstractVector, n::Integer) where {T} = zeros(T, max(length(b), n))
|
||||
_zeros(::Type{T}, B::AbstractMatrix, n::Integer) where {T} = zeros(T, max(size(B, 1), n), size(B, 2))
|
||||
|
||||
function (\)(A::Union{QR{TA},QRCompactWY{TA},QRPivoted{TA}}, B::AbstractVecOrMat{TB}) where {TA,TB}
|
||||
S = promote_type(TA,TB)
|
||||
m, n = size(A)
|
||||
m == size(B,1) || throw(DimensionMismatch("left hand side has $m rows, but right hand side has $(size(B,1)) rows"))
|
||||
|
||||
AA = convert(Factorization{S}, A)
|
||||
|
||||
X = _zeros(S, B, n)
|
||||
X[1:size(B, 1), :] = B
|
||||
|
||||
A_ldiv_B!(AA, X)
|
||||
|
||||
return _cut_B(X, 1:n)
|
||||
end
|
||||
|
||||
# With a real lhs and complex rhs with the same precision, we can reinterpret the complex
|
||||
# rhs as a real rhs with twice the number of columns.
|
||||
|
||||
# convenience methods to compute the return size correctly for vectors and matrices
|
||||
_ret_size(A::Factorization, b::AbstractVector) = (max(size(A, 2), length(b)),)
|
||||
_ret_size(A::Factorization, B::AbstractMatrix) = (max(size(A, 2), size(B, 1)), size(B, 2))
|
||||
|
||||
function (\)(A::Union{QR{T},QRCompactWY{T},QRPivoted{T}}, BIn::VecOrMat{Complex{T}}) where T<:BlasReal
|
||||
m, n = size(A)
|
||||
m == size(BIn, 1) || throw(DimensionMismatch("left hand side has $m rows, but right hand side has $(size(BIn,1)) rows"))
|
||||
|
||||
# |z1|z3| reinterpret |x1|x2|x3|x4| transpose |x1|y1| reshape |x1|y1|x3|y3|
|
||||
# |z2|z4| -> |y1|y2|y3|y4| -> |x2|y2| -> |x2|y2|x4|y4|
|
||||
# |x3|y3|
|
||||
# |x4|y4|
|
||||
B = reshape(transpose(reinterpret(T, BIn, (2, length(BIn)))), size(BIn, 1), 2*size(BIn, 2))
|
||||
|
||||
X = _zeros(T, B, n)
|
||||
X[1:size(B, 1), :] = B
|
||||
|
||||
A_ldiv_B!(A, X)
|
||||
|
||||
# |z1|z3| reinterpret |x1|x2|x3|x4| transpose |x1|y1| reshape |x1|y1|x3|y3|
|
||||
# |z2|z4| <- |y1|y2|y3|y4| <- |x2|y2| <- |x2|y2|x4|y4|
|
||||
# |x3|y3|
|
||||
# |x4|y4|
|
||||
XX = reinterpret(Complex{T}, transpose(reshape(X, div(length(X), 2), 2)), _ret_size(A, BIn))
|
||||
return _cut_B(XX, 1:n)
|
||||
end
|
||||
|
||||
##TODO: Add methods for rank(A::QRP{T}) and adjust the (\) method accordingly
|
||||
## Add rcond methods for Cholesky, LU, QR and QRP types
|
||||
## Lower priority: Add LQ, QL and RQ factorizations
|
||||
|
||||
# FIXME! Should add balancing option through xgebal
|
||||
@@ -0,0 +1,242 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
"""
|
||||
RowVector(vector)
|
||||
|
||||
A lazy-view wrapper of an `AbstractVector`, which turns a length-`n` vector into a `1×n`
|
||||
shaped row vector and represents the transpose of a vector (the elements are also transposed
|
||||
recursively). This type is usually constructed (and unwrapped) via the [`transpose`](@ref)
|
||||
function or `.'` operator (or related [`ctranspose`](@ref) or `'` operator).
|
||||
|
||||
By convention, a vector can be multiplied by a matrix on its left (`A * v`) whereas a row
|
||||
vector can be multiplied by a matrix on its right (such that `v.' * A = (A.' * v).'`). It
|
||||
differs from a `1×n`-sized matrix by the facts that its transpose returns a vector and the
|
||||
inner product `v1.' * v2` returns a scalar, but will otherwise behave similarly.
|
||||
"""
|
||||
struct RowVector{T,V<:AbstractVector} <: AbstractMatrix{T}
|
||||
vec::V
|
||||
function RowVector{T,V}(v::V) where V<:AbstractVector where T
|
||||
check_types(T,v)
|
||||
new(v)
|
||||
end
|
||||
end
|
||||
|
||||
@inline check_types(::Type{T1}, ::AbstractVector{T2}) where {T1,T2} = check_types(T1, T2)
|
||||
@pure check_types(::Type{T1}, ::Type{T2}) where {T1,T2} = T1 === transpose_type(T2) ? nothing :
|
||||
error("Element type mismatch. Tried to create a `RowVector{$T1}` from an `AbstractVector{$T2}`")
|
||||
|
||||
const ConjRowVector{T,CV<:ConjVector} = RowVector{T,CV}
|
||||
|
||||
# The element type may be transformed as transpose is recursive
|
||||
@inline transpose_type{T}(::Type{T}) = promote_op(transpose, T)
|
||||
|
||||
# Constructors that take a vector
|
||||
@inline RowVector(vec::AbstractVector{T}) where {T} = RowVector{transpose_type(T),typeof(vec)}(vec)
|
||||
@inline RowVector{T}(vec::AbstractVector{T}) where {T} = RowVector{T,typeof(vec)}(vec)
|
||||
|
||||
# Constructors that take a size and default to Array
|
||||
@inline RowVector{T}(n::Int) where {T} = RowVector{T}(Vector{transpose_type(T)}(n))
|
||||
@inline RowVector{T}(n1::Int, n2::Int) where {T} = n1 == 1 ?
|
||||
RowVector{T}(Vector{transpose_type(T)}(n2)) :
|
||||
error("RowVector expects 1×N size, got ($n1,$n2)")
|
||||
@inline RowVector{T}(n::Tuple{Int}) where {T} = RowVector{T}(Vector{transpose_type(T)}(n[1]))
|
||||
@inline RowVector{T}(n::Tuple{Int,Int}) where {T} = n[1] == 1 ?
|
||||
RowVector{T}(Vector{transpose_type(T)}(n[2])) :
|
||||
error("RowVector expects 1×N size, got $n")
|
||||
|
||||
# Conversion of underlying storage
|
||||
convert(::Type{RowVector{T,V}}, rowvec::RowVector) where {T,V<:AbstractVector} =
|
||||
RowVector{T,V}(convert(V,rowvec.vec))
|
||||
|
||||
# similar tries to maintain the RowVector wrapper and the parent type
|
||||
@inline similar(rowvec::RowVector) = RowVector(similar(parent(rowvec)))
|
||||
@inline similar(rowvec::RowVector, ::Type{T}) where {T} = RowVector(similar(parent(rowvec), transpose_type(T)))
|
||||
|
||||
# Resizing similar currently loses its RowVector property.
|
||||
@inline similar(rowvec::RowVector, ::Type{T}, dims::Dims{N}) where {T,N} = similar(parent(rowvec), T, dims)
|
||||
|
||||
# Basic methods
|
||||
"""
|
||||
transpose(v::AbstractVector)
|
||||
|
||||
The transposition operator (`.'`).
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> v = [1,2,3]
|
||||
3-element Array{Int64,1}:
|
||||
1
|
||||
2
|
||||
3
|
||||
|
||||
julia> transpose(v)
|
||||
1×3 RowVector{Int64,Array{Int64,1}}:
|
||||
1 2 3
|
||||
```
|
||||
"""
|
||||
@inline transpose(vec::AbstractVector) = RowVector(vec)
|
||||
@inline ctranspose(vec::AbstractVector) = RowVector(_conj(vec))
|
||||
|
||||
@inline transpose(rowvec::RowVector) = rowvec.vec
|
||||
@inline transpose(rowvec::ConjRowVector) = copy(rowvec.vec) # remove the ConjArray wrapper from any raw vector
|
||||
@inline ctranspose(rowvec::RowVector) = conj(rowvec.vec)
|
||||
@inline ctranspose(rowvec::RowVector{<:Real}) = rowvec.vec
|
||||
|
||||
parent(rowvec::RowVector) = rowvec.vec
|
||||
|
||||
"""
|
||||
conj(v::RowVector)
|
||||
|
||||
Returns a [`ConjArray`](@ref) lazy view of the input, where each element is conjugated.
|
||||
|
||||
### Example
|
||||
|
||||
```jldoctest
|
||||
julia> v = [1+im, 1-im].'
|
||||
1×2 RowVector{Complex{Int64},Array{Complex{Int64},1}}:
|
||||
1+1im 1-1im
|
||||
|
||||
julia> conj(v)
|
||||
1×2 RowVector{Complex{Int64},ConjArray{Complex{Int64},1,Array{Complex{Int64},1}}}:
|
||||
1-1im 1+1im
|
||||
```
|
||||
"""
|
||||
@inline conj(rowvec::RowVector) = RowVector(_conj(rowvec.vec))
|
||||
@inline conj(rowvec::RowVector{<:Real}) = rowvec
|
||||
|
||||
# AbstractArray interface
|
||||
@inline length(rowvec::RowVector) = length(rowvec.vec)
|
||||
@inline size(rowvec::RowVector) = (1, length(rowvec.vec))
|
||||
@inline size(rowvec::RowVector, d) = ifelse(d==2, length(rowvec.vec), 1)
|
||||
@inline indices(rowvec::RowVector) = (Base.OneTo(1), indices(rowvec.vec)[1])
|
||||
@inline indices(rowvec::RowVector, d) = ifelse(d == 2, indices(rowvec.vec)[1], Base.OneTo(1))
|
||||
IndexStyle(::RowVector) = IndexLinear()
|
||||
IndexStyle(::Type{<:RowVector}) = IndexLinear()
|
||||
|
||||
@propagate_inbounds getindex(rowvec::RowVector, i) = transpose(rowvec.vec[i])
|
||||
@propagate_inbounds setindex!(rowvec::RowVector, v, i) = (setindex!(rowvec.vec, transpose(v), i); rowvec)
|
||||
|
||||
# Cartesian indexing is distorted by getindex
|
||||
# Furthermore, Cartesian indexes don't have to match shape, apparently!
|
||||
@inline function getindex(rowvec::RowVector, i::CartesianIndex)
|
||||
@boundscheck if !(i.I[1] == 1 && i.I[2] ∈ indices(rowvec.vec)[1] && check_tail_indices(i.I...))
|
||||
throw(BoundsError(rowvec, i.I))
|
||||
end
|
||||
@inbounds return transpose(rowvec.vec[i.I[2]])
|
||||
end
|
||||
@inline function setindex!(rowvec::RowVector, v, i::CartesianIndex)
|
||||
@boundscheck if !(i.I[1] == 1 && i.I[2] ∈ indices(rowvec.vec)[1] && check_tail_indices(i.I...))
|
||||
throw(BoundsError(rowvec, i.I))
|
||||
end
|
||||
@inbounds rowvec.vec[i.I[2]] = transpose(v)
|
||||
end
|
||||
|
||||
@propagate_inbounds getindex(rowvec::RowVector, ::CartesianIndex{0}) = getindex(rowvec)
|
||||
@propagate_inbounds getindex(rowvec::RowVector, i::CartesianIndex{1}) = getindex(rowvec, i.I[1])
|
||||
|
||||
@propagate_inbounds setindex!(rowvec::RowVector, v, ::CartesianIndex{0}) = setindex!(rowvec, v)
|
||||
@propagate_inbounds setindex!(rowvec::RowVector, v, i::CartesianIndex{1}) = setindex!(rowvec, v, i.I[1])
|
||||
|
||||
@inline check_tail_indices(i1, i2) = true
|
||||
@inline check_tail_indices(i1, i2, i3, is...) = i3 == 1 ? check_tail_indices(i1, i2, is...) : false
|
||||
|
||||
# helper function for below
|
||||
@inline to_vec(rowvec::RowVector) = map(transpose, transpose(rowvec))
|
||||
@inline to_vec(x::Number) = x
|
||||
@inline to_vecs(rowvecs...) = (map(to_vec, rowvecs)...)
|
||||
|
||||
# map: Preserve the RowVector by un-wrapping and re-wrapping, but note that `f`
|
||||
# expects to operate within the transposed domain, so to_vec transposes the elements
|
||||
@inline map(f, rowvecs::RowVector...) = RowVector(map(transpose∘f, to_vecs(rowvecs...)...))
|
||||
|
||||
# broacast (other combinations default to higher-dimensional array)
|
||||
@inline broadcast(f, rowvecs::Union{Number,RowVector}...) =
|
||||
RowVector(broadcast(transpose∘f, to_vecs(rowvecs...)...))
|
||||
|
||||
# Horizontal concatenation #
|
||||
|
||||
@inline hcat(X::RowVector...) = transpose(vcat(map(transpose, X)...))
|
||||
@inline hcat(X::Union{RowVector,Number}...) = transpose(vcat(map(transpose, X)...))
|
||||
|
||||
@inline typed_hcat(::Type{T}, X::RowVector...) where {T} =
|
||||
transpose(typed_vcat(T, map(transpose, X)...))
|
||||
@inline typed_hcat(::Type{T}, X::Union{RowVector,Number}...) where {T} =
|
||||
transpose(typed_vcat(T, map(transpose, X)...))
|
||||
|
||||
# Multiplication #
|
||||
|
||||
# inner product -> dot product specializations
|
||||
@inline *(rowvec::RowVector{T}, vec::AbstractVector{T}) where {T<:Real} = dot(parent(rowvec), vec)
|
||||
@inline *(rowvec::ConjRowVector{T}, vec::AbstractVector{T}) where {T<:Real} = dot(rowvec', vec)
|
||||
@inline *(rowvec::ConjRowVector, vec::AbstractVector) = dot(rowvec', vec)
|
||||
|
||||
# Generic behavior
|
||||
@inline function *(rowvec::RowVector, vec::AbstractVector)
|
||||
if length(rowvec) != length(vec)
|
||||
throw(DimensionMismatch("A has dimensions $(size(rowvec)) but B has dimensions $(size(vec))"))
|
||||
end
|
||||
sum(@inbounds(return rowvec[i]*vec[i]) for i = 1:length(vec))
|
||||
end
|
||||
@inline *(rowvec::RowVector, mat::AbstractMatrix) = transpose(mat.' * transpose(rowvec))
|
||||
*(::RowVector, ::RowVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors"))
|
||||
@inline *(vec::AbstractVector, rowvec::RowVector) = vec .* rowvec
|
||||
*(vec::AbstractVector, rowvec::AbstractVector) = throw(DimensionMismatch("Cannot multiply two vectors"))
|
||||
|
||||
# Transposed forms
|
||||
A_mul_Bt(::RowVector, ::AbstractVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors"))
|
||||
@inline A_mul_Bt(rowvec::RowVector, mat::AbstractMatrix) = transpose(mat * transpose(rowvec))
|
||||
@inline A_mul_Bt(rowvec1::RowVector, rowvec2::RowVector) = rowvec1*transpose(rowvec2)
|
||||
A_mul_Bt(vec::AbstractVector, rowvec::RowVector) = throw(DimensionMismatch("Cannot multiply two vectors"))
|
||||
@inline A_mul_Bt(vec1::AbstractVector, vec2::AbstractVector) = vec1 * transpose(vec2)
|
||||
@inline A_mul_Bt(mat::AbstractMatrix, rowvec::RowVector) = mat * transpose(rowvec)
|
||||
|
||||
@inline At_mul_Bt(rowvec::RowVector, vec::AbstractVector) = transpose(rowvec) * transpose(vec)
|
||||
@inline At_mul_Bt(vec::AbstractVector, mat::AbstractMatrix) = transpose(mat * vec)
|
||||
At_mul_Bt(rowvec1::RowVector, rowvec2::RowVector) = throw(DimensionMismatch("Cannot multiply two vectors"))
|
||||
@inline At_mul_Bt(vec::AbstractVector, rowvec::RowVector) = transpose(vec)*transpose(rowvec)
|
||||
At_mul_Bt(vec::AbstractVector, rowvec::AbstractVector) = throw(DimensionMismatch(
|
||||
"Cannot multiply two transposed vectors"))
|
||||
@inline At_mul_Bt(mat::AbstractMatrix, rowvec::RowVector) = mat.' * transpose(rowvec)
|
||||
|
||||
At_mul_B(::RowVector, ::AbstractVector) = throw(DimensionMismatch("Cannot multiply two vectors"))
|
||||
@inline At_mul_B(vec::AbstractVector, mat::AbstractMatrix) = transpose(At_mul_B(mat,vec))
|
||||
@inline At_mul_B(rowvec1::RowVector, rowvec2::RowVector) = transpose(rowvec1) * rowvec2
|
||||
At_mul_B(vec::AbstractVector, rowvec::RowVector) = throw(DimensionMismatch(
|
||||
"Cannot multiply two transposed vectors"))
|
||||
@inline At_mul_B(vec1::AbstractVector{T}, vec2::AbstractVector{T}) where {T<:Real} =
|
||||
reduce(+, map(At_mul_B, vec1, vec2)) # Seems to be overloaded...
|
||||
@inline At_mul_B(vec1::AbstractVector, vec2::AbstractVector) = transpose(vec1) * vec2
|
||||
|
||||
# Conjugated forms
|
||||
A_mul_Bc(::RowVector, ::AbstractVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors"))
|
||||
@inline A_mul_Bc(rowvec::RowVector, mat::AbstractMatrix) = ctranspose(mat * ctranspose(rowvec))
|
||||
@inline A_mul_Bc(rowvec1::RowVector, rowvec2::RowVector) = rowvec1 * ctranspose(rowvec2)
|
||||
A_mul_Bc(vec::AbstractVector, rowvec::RowVector) = throw(DimensionMismatch("Cannot multiply two vectors"))
|
||||
@inline A_mul_Bc(vec1::AbstractVector, vec2::AbstractVector) = vec1 * ctranspose(vec2)
|
||||
@inline A_mul_Bc(mat::AbstractMatrix, rowvec::RowVector) = mat * ctranspose(rowvec)
|
||||
|
||||
@inline Ac_mul_Bc(rowvec::RowVector, vec::AbstractVector) = ctranspose(rowvec) * ctranspose(vec)
|
||||
@inline Ac_mul_Bc(vec::AbstractVector, mat::AbstractMatrix) = ctranspose(mat * vec)
|
||||
Ac_mul_Bc(rowvec1::RowVector, rowvec2::RowVector) = throw(DimensionMismatch("Cannot multiply two vectors"))
|
||||
@inline Ac_mul_Bc(vec::AbstractVector, rowvec::RowVector) = ctranspose(vec)*ctranspose(rowvec)
|
||||
Ac_mul_Bc(vec::AbstractVector, rowvec::AbstractVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors"))
|
||||
@inline Ac_mul_Bc(mat::AbstractMatrix, rowvec::RowVector) = mat' * ctranspose(rowvec)
|
||||
|
||||
Ac_mul_B(::RowVector, ::AbstractVector) = throw(DimensionMismatch("Cannot multiply two vectors"))
|
||||
@inline Ac_mul_B(vec::AbstractVector, mat::AbstractMatrix) = ctranspose(Ac_mul_B(mat,vec))
|
||||
@inline Ac_mul_B(rowvec1::RowVector, rowvec2::RowVector) = ctranspose(rowvec1) * rowvec2
|
||||
Ac_mul_B(vec::AbstractVector, rowvec::RowVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors"))
|
||||
@inline Ac_mul_B(vec1::AbstractVector, vec2::AbstractVector) = ctranspose(vec1)*vec2
|
||||
|
||||
# Left Division #
|
||||
|
||||
\(mat::AbstractMatrix, rowvec::RowVector) = throw(DimensionMismatch("Cannot left-divide transposed vector by matrix"))
|
||||
At_ldiv_B(mat::AbstractMatrix, rowvec::RowVector) = throw(DimensionMismatch("Cannot left-divide transposed vector by matrix"))
|
||||
Ac_ldiv_B(mat::AbstractMatrix, rowvec::RowVector) = throw(DimensionMismatch("Cannot left-divide transposed vector by matrix"))
|
||||
|
||||
# Right Division #
|
||||
|
||||
@inline /(rowvec::RowVector, mat::AbstractMatrix) = transpose(transpose(mat) \ transpose(rowvec))
|
||||
@inline A_rdiv_Bt(rowvec::RowVector, mat::AbstractMatrix) = transpose(mat \ transpose(rowvec))
|
||||
@inline A_rdiv_Bc(rowvec::RowVector, mat::AbstractMatrix) = ctranspose(mat \ ctranspose(rowvec))
|
||||
@@ -0,0 +1,289 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# Schur decomposition
|
||||
struct Schur{Ty,S<:AbstractMatrix} <: Factorization{Ty}
|
||||
T::S
|
||||
Z::S
|
||||
values::Vector
|
||||
Schur{Ty,S}(T::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}, values::Vector) where {Ty,S} = new(T, Z, values)
|
||||
end
|
||||
Schur(T::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}, values::Vector) where {Ty} = Schur{Ty, typeof(T)}(T, Z, values)
|
||||
|
||||
"""
|
||||
schurfact!(A::StridedMatrix) -> F::Schur
|
||||
|
||||
Same as [`schurfact`](@ref) but uses the input argument as workspace.
|
||||
"""
|
||||
schurfact!(A::StridedMatrix{<:BlasFloat}) = Schur(LinAlg.LAPACK.gees!('V', A)...)
|
||||
|
||||
"""
|
||||
schurfact(A::StridedMatrix) -> F::Schur
|
||||
|
||||
Computes the Schur factorization of the matrix `A`. The (quasi) triangular Schur factor can
|
||||
be obtained from the `Schur` object `F` with either `F[:Schur]` or `F[:T]` and the
|
||||
orthogonal/unitary Schur vectors can be obtained with `F[:vectors]` or `F[:Z]` such that
|
||||
`A = F[:vectors]*F[:Schur]*F[:vectors]'`. The eigenvalues of `A` can be obtained with `F[:values]`.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [-2. 1. 3.; 2. 1. -1.; -7. 2. 7.]
|
||||
3×3 Array{Float64,2}:
|
||||
-2.0 1.0 3.0
|
||||
2.0 1.0 -1.0
|
||||
-7.0 2.0 7.0
|
||||
|
||||
julia> F = schurfact(A)
|
||||
Base.LinAlg.Schur{Float64,Array{Float64,2}} with factors T and Z:
|
||||
[2.0 0.801792 6.63509; -8.55988e-11 2.0 8.08286; 0.0 0.0 1.99999]
|
||||
[0.577351 0.154299 -0.801784; 0.577346 -0.77152 0.267262; 0.577354 0.617211 0.534522]
|
||||
and values:
|
||||
Complex{Float64}[2.0+8.28447e-6im, 2.0-8.28447e-6im, 1.99999+0.0im]
|
||||
|
||||
julia> F[:vectors] * F[:Schur] * F[:vectors]'
|
||||
3×3 Array{Float64,2}:
|
||||
-2.0 1.0 3.0
|
||||
2.0 1.0 -1.0
|
||||
-7.0 2.0 7.0
|
||||
```
|
||||
"""
|
||||
schurfact(A::StridedMatrix{<:BlasFloat}) = schurfact!(copy(A))
|
||||
function schurfact{T}(A::StridedMatrix{T})
|
||||
S = promote_type(Float32, typeof(one(T)/norm(one(T))))
|
||||
return schurfact!(copy_oftype(A, S))
|
||||
end
|
||||
|
||||
function getindex(F::Schur, d::Symbol)
|
||||
if d == :T || d == :Schur
|
||||
return F.T
|
||||
elseif d == :Z || d == :vectors
|
||||
return F.Z
|
||||
elseif d == :values
|
||||
return F.values
|
||||
else
|
||||
throw(KeyError(d))
|
||||
end
|
||||
end
|
||||
|
||||
function show(io::IO, F::Schur)
|
||||
println(io, "$(typeof(F)) with factors T and Z:")
|
||||
show(io, F[:T])
|
||||
println(io)
|
||||
show(io, F[:Z])
|
||||
println(io)
|
||||
println(io, "and values:")
|
||||
show(io, F[:values])
|
||||
end
|
||||
|
||||
"""
|
||||
schur(A::StridedMatrix) -> T::Matrix, Z::Matrix, λ::Vector
|
||||
|
||||
Computes the Schur factorization of the matrix `A`. The methods return the (quasi)
|
||||
triangular Schur factor `T` and the orthogonal/unitary Schur vectors `Z` such that
|
||||
`A = Z*T*Z'`. The eigenvalues of `A` are returned in the vector `λ`.
|
||||
|
||||
See [`schurfact`](@ref).
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [-2. 1. 3.; 2. 1. -1.; -7. 2. 7.]
|
||||
3×3 Array{Float64,2}:
|
||||
-2.0 1.0 3.0
|
||||
2.0 1.0 -1.0
|
||||
-7.0 2.0 7.0
|
||||
|
||||
julia> T, Z, lambda = schur(A)
|
||||
([2.0 0.801792 6.63509; -8.55988e-11 2.0 8.08286; 0.0 0.0 1.99999], [0.577351 0.154299 -0.801784; 0.577346 -0.77152 0.267262; 0.577354 0.617211 0.534522], Complex{Float64}[2.0+8.28447e-6im, 2.0-8.28447e-6im, 1.99999+0.0im])
|
||||
|
||||
julia> Z * T * Z'
|
||||
3×3 Array{Float64,2}:
|
||||
-2.0 1.0 3.0
|
||||
2.0 1.0 -1.0
|
||||
-7.0 2.0 7.0
|
||||
```
|
||||
"""
|
||||
function schur(A::StridedMatrix)
|
||||
SchurF = schurfact(A)
|
||||
SchurF[:T], SchurF[:Z], SchurF[:values]
|
||||
end
|
||||
schur(A::Symmetric) = schur(full(A))
|
||||
schur(A::Hermitian) = schur(full(A))
|
||||
schur(A::UpperTriangular) = schur(full(A))
|
||||
schur(A::LowerTriangular) = schur(full(A))
|
||||
schur(A::Tridiagonal) = schur(full(A))
|
||||
|
||||
|
||||
"""
|
||||
ordschur!(F::Schur, select::Union{Vector{Bool},BitVector}) -> F::Schur
|
||||
|
||||
Same as [`ordschur`](@ref) but overwrites the factorization `F`.
|
||||
"""
|
||||
function ordschur!(schur::Schur, select::Union{Vector{Bool},BitVector})
|
||||
_, _, vals = ordschur!(schur.T, schur.Z, select)
|
||||
schur[:values][:] = vals
|
||||
return schur
|
||||
end
|
||||
|
||||
"""
|
||||
ordschur(F::Schur, select::Union{Vector{Bool},BitVector}) -> F::Schur
|
||||
|
||||
Reorders the Schur factorization `F` of a matrix `A = Z*T*Z'` according to the logical array
|
||||
`select` returning the reordered factorization `F` object. The selected eigenvalues appear
|
||||
in the leading diagonal of `F[:Schur]` and the corresponding leading columns of
|
||||
`F[:vectors]` form an orthogonal/unitary basis of the corresponding right invariant
|
||||
subspace. In the real case, a complex conjugate pair of eigenvalues must be either both
|
||||
included or both excluded via `select`.
|
||||
"""
|
||||
ordschur(schur::Schur, select::Union{Vector{Bool},BitVector}) =
|
||||
Schur(ordschur(schur.T, schur.Z, select)...)
|
||||
|
||||
"""
|
||||
ordschur!(T::StridedMatrix, Z::StridedMatrix, select::Union{Vector{Bool},BitVector}) -> T::StridedMatrix, Z::StridedMatrix, λ::Vector
|
||||
|
||||
Same as [`ordschur`](@ref) but overwrites the input arguments.
|
||||
"""
|
||||
ordschur!(T::StridedMatrix{Ty}, Z::StridedMatrix{Ty}, select::Union{Vector{Bool},BitVector}) where {Ty<:BlasFloat} =
|
||||
LinAlg.LAPACK.trsen!(convert(Vector{BlasInt}, select), T, Z)
|
||||
|
||||
"""
|
||||
ordschur(T::StridedMatrix, Z::StridedMatrix, select::Union{Vector{Bool},BitVector}) -> T::StridedMatrix, Z::StridedMatrix, λ::Vector
|
||||
|
||||
Reorders the Schur factorization of a real matrix `A = Z*T*Z'` according to the logical
|
||||
array `select` returning the reordered matrices `T` and `Z` as well as the vector of
|
||||
eigenvalues `λ`. The selected eigenvalues appear in the leading diagonal of `T` and the
|
||||
corresponding leading columns of `Z` form an orthogonal/unitary basis of the corresponding
|
||||
right invariant subspace. In the real case, a complex conjugate pair of eigenvalues must be
|
||||
either both included or both excluded via `select`.
|
||||
"""
|
||||
ordschur(T::StridedMatrix{Ty}, Z::StridedMatrix{Ty}, select::Union{Vector{Bool},BitVector}) where {Ty<:BlasFloat} =
|
||||
ordschur!(copy(T), copy(Z), select)
|
||||
|
||||
struct GeneralizedSchur{Ty,M<:AbstractMatrix} <: Factorization{Ty}
|
||||
S::M
|
||||
T::M
|
||||
alpha::Vector
|
||||
beta::Vector{Ty}
|
||||
Q::M
|
||||
Z::M
|
||||
function GeneralizedSchur{Ty,M}(S::AbstractMatrix{Ty}, T::AbstractMatrix{Ty}, alpha::Vector,
|
||||
beta::Vector{Ty}, Q::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}) where {Ty,M}
|
||||
new(S, T, alpha, beta, Q, Z)
|
||||
end
|
||||
end
|
||||
function GeneralizedSchur(S::AbstractMatrix{Ty}, T::AbstractMatrix{Ty}, alpha::Vector,
|
||||
beta::Vector{Ty}, Q::AbstractMatrix{Ty}, Z::AbstractMatrix{Ty}) where Ty
|
||||
GeneralizedSchur{Ty, typeof(S)}(S, T, alpha, beta, Q, Z)
|
||||
end
|
||||
|
||||
"""
|
||||
schurfact!(A::StridedMatrix, B::StridedMatrix) -> F::GeneralizedSchur
|
||||
|
||||
Same as [`schurfact`](@ref) but uses the input matrices `A` and `B` as workspace.
|
||||
"""
|
||||
schurfact!(A::StridedMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} =
|
||||
GeneralizedSchur(LinAlg.LAPACK.gges!('V', 'V', A, B)...)
|
||||
|
||||
"""
|
||||
schurfact(A::StridedMatrix, B::StridedMatrix) -> F::GeneralizedSchur
|
||||
|
||||
Computes the Generalized Schur (or QZ) factorization of the matrices `A` and `B`. The
|
||||
(quasi) triangular Schur factors can be obtained from the `Schur` object `F` with `F[:S]`
|
||||
and `F[:T]`, the left unitary/orthogonal Schur vectors can be obtained with `F[:left]` or
|
||||
`F[:Q]` and the right unitary/orthogonal Schur vectors can be obtained with `F[:right]` or
|
||||
`F[:Z]` such that `A=F[:left]*F[:S]*F[:right]'` and `B=F[:left]*F[:T]*F[:right]'`. The
|
||||
generalized eigenvalues of `A` and `B` can be obtained with `F[:alpha]./F[:beta]`.
|
||||
"""
|
||||
schurfact(A::StridedMatrix{T},B::StridedMatrix{T}) where {T<:BlasFloat} = schurfact!(copy(A),copy(B))
|
||||
function schurfact(A::StridedMatrix{TA}, B::StridedMatrix{TB}) where {TA,TB}
|
||||
S = promote_type(Float32, typeof(one(TA)/norm(one(TA))), TB)
|
||||
return schurfact!(copy_oftype(A, S), copy_oftype(B, S))
|
||||
end
|
||||
|
||||
"""
|
||||
ordschur!(F::GeneralizedSchur, select::Union{Vector{Bool},BitVector}) -> F::GeneralizedSchur
|
||||
|
||||
Same as `ordschur` but overwrites the factorization `F`.
|
||||
"""
|
||||
function ordschur!(gschur::GeneralizedSchur, select::Union{Vector{Bool},BitVector})
|
||||
_, _, α, β, _, _ = ordschur!(gschur.S, gschur.T, gschur.Q, gschur.Z, select)
|
||||
gschur[:alpha][:] = α
|
||||
gschur[:beta][:] = β
|
||||
return gschur
|
||||
end
|
||||
|
||||
"""
|
||||
ordschur(F::GeneralizedSchur, select::Union{Vector{Bool},BitVector}) -> F::GeneralizedSchur
|
||||
|
||||
Reorders the Generalized Schur factorization `F` of a matrix pair `(A, B) = (Q*S*Z', Q*T*Z')`
|
||||
according to the logical array `select` and returns a GeneralizedSchur object `F`. The
|
||||
selected eigenvalues appear in the leading diagonal of both `F[:S]` and `F[:T]`, and the
|
||||
left and right orthogonal/unitary Schur vectors are also reordered such that
|
||||
`(A, B) = F[:Q]*(F[:S], F[:T])*F[:Z]'` still holds and the generalized eigenvalues of `A`
|
||||
and `B` can still be obtained with `F[:alpha]./F[:beta]`.
|
||||
"""
|
||||
ordschur(gschur::GeneralizedSchur, select::Union{Vector{Bool},BitVector}) =
|
||||
GeneralizedSchur(ordschur(gschur.S, gschur.T, gschur.Q, gschur.Z, select)...)
|
||||
|
||||
"""
|
||||
ordschur!(S::StridedMatrix, T::StridedMatrix, Q::StridedMatrix, Z::StridedMatrix, select) -> S::StridedMatrix, T::StridedMatrix, Q::StridedMatrix, Z::StridedMatrix, α::Vector, β::Vector
|
||||
|
||||
Same as [`ordschur`](@ref) but overwrites the factorization the input arguments.
|
||||
"""
|
||||
ordschur!(S::StridedMatrix{Ty}, T::StridedMatrix{Ty}, Q::StridedMatrix{Ty},
|
||||
Z::StridedMatrix{Ty}, select::Union{Vector{Bool},BitVector}) where {Ty<:BlasFloat} =
|
||||
LinAlg.LAPACK.tgsen!(convert(Vector{BlasInt}, select), S, T, Q, Z)
|
||||
|
||||
"""
|
||||
ordschur(S::StridedMatrix, T::StridedMatrix, Q::StridedMatrix, Z::StridedMatrix, select) -> S::StridedMatrix, T::StridedMatrix, Q::StridedMatrix, Z::StridedMatrix, α::Vector, β::Vector
|
||||
|
||||
Reorders the Generalized Schur factorization of a matrix pair `(A, B) = (Q*S*Z', Q*T*Z')`
|
||||
according to the logical array `select` and returns the matrices `S`, `T`, `Q`, `Z` and
|
||||
vectors `α` and `β`. The selected eigenvalues appear in the leading diagonal of both `S`
|
||||
and `T`, and the left and right unitary/orthogonal Schur vectors are also reordered such
|
||||
that `(A, B) = Q*(S, T)*Z'` still holds and the generalized eigenvalues of `A` and `B` can
|
||||
still be obtained with `α./β`.
|
||||
"""
|
||||
ordschur(S::StridedMatrix{Ty}, T::StridedMatrix{Ty}, Q::StridedMatrix{Ty},
|
||||
Z::StridedMatrix{Ty}, select::Union{Vector{Bool},BitVector}) where {Ty<:BlasFloat} =
|
||||
ordschur!(copy(S), copy(T), copy(Q), copy(Z), select)
|
||||
|
||||
function getindex(F::GeneralizedSchur, d::Symbol)
|
||||
if d == :S
|
||||
return F.S
|
||||
elseif d == :T
|
||||
return F.T
|
||||
elseif d == :alpha
|
||||
return F.alpha
|
||||
elseif d == :beta
|
||||
return F.beta
|
||||
elseif d == :values
|
||||
return F.alpha./F.beta
|
||||
elseif d == :Q || d == :left
|
||||
return F.Q
|
||||
elseif d == :Z || d == :right
|
||||
return F.Z
|
||||
else
|
||||
throw(KeyError(d))
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
schur(A::StridedMatrix, B::StridedMatrix) -> S::StridedMatrix, T::StridedMatrix, Q::StridedMatrix, Z::StridedMatrix, α::Vector, β::Vector
|
||||
|
||||
See [`schurfact`](@ref).
|
||||
"""
|
||||
function schur(A::StridedMatrix, B::StridedMatrix)
|
||||
SchurF = schurfact(A, B)
|
||||
SchurF[:S], SchurF[:T], SchurF[:Q], SchurF[:Z], SchurF[:alpha], SchurF[:beta]
|
||||
end
|
||||
|
||||
# Conversion
|
||||
convert(::Type{AbstractMatrix}, F::Schur) = (F.Z * F.T) * F.Z'
|
||||
convert(::Type{AbstractArray}, F::Schur) = convert(AbstractMatrix, F)
|
||||
convert(::Type{Matrix}, F::Schur) = convert(Array, convert(AbstractArray, F))
|
||||
convert(::Type{Array}, F::Schur) = convert(Matrix, F)
|
||||
full(F::Schur) = convert(AbstractArray, F)
|
||||
|
||||
copy(F::Schur) = Schur(copy(F.T), copy(F.Z), copy(F.values))
|
||||
copy(F::GeneralizedSchur) = GeneralizedSchur(copy(F.S), copy(F.T), copy(F.alpha), copy(F.beta), copy(F.Q), copy(F.Z))
|
||||
@@ -0,0 +1,158 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# Methods operating on different special matrix types
|
||||
|
||||
# Interconversion between special matrix types
|
||||
convert(::Type{Bidiagonal}, A::Diagonal{T}) where {T} =
|
||||
Bidiagonal(A.diag, zeros(T, size(A.diag,1)-1), true)
|
||||
convert(::Type{SymTridiagonal}, A::Diagonal{T}) where {T} =
|
||||
SymTridiagonal(A.diag, zeros(T, size(A.diag,1)-1))
|
||||
convert(::Type{Tridiagonal}, A::Diagonal{T}) where {T} =
|
||||
Tridiagonal(zeros(T, size(A.diag,1)-1), A.diag, zeros(T, size(A.diag,1)-1))
|
||||
|
||||
function convert(::Type{Diagonal}, A::Union{Bidiagonal, SymTridiagonal})
|
||||
if !iszero(A.ev)
|
||||
throw(ArgumentError("matrix cannot be represented as Diagonal"))
|
||||
end
|
||||
Diagonal(A.dv)
|
||||
end
|
||||
|
||||
function convert(::Type{SymTridiagonal}, A::Bidiagonal)
|
||||
if !iszero(A.ev)
|
||||
throw(ArgumentError("matrix cannot be represented as SymTridiagonal"))
|
||||
end
|
||||
SymTridiagonal(A.dv, A.ev)
|
||||
end
|
||||
|
||||
convert(::Type{Tridiagonal}, A::Bidiagonal{T}) where {T} =
|
||||
Tridiagonal(A.isupper ? zeros(T, size(A.dv,1)-1) : A.ev, A.dv,
|
||||
A.isupper ? A.ev:zeros(T, size(A.dv,1)-1))
|
||||
|
||||
function convert(::Type{Bidiagonal}, A::SymTridiagonal)
|
||||
if !iszero(A.ev)
|
||||
throw(ArgumentError("matrix cannot be represented as Bidiagonal"))
|
||||
end
|
||||
Bidiagonal(A.dv, A.ev, true)
|
||||
end
|
||||
|
||||
function convert(::Type{Diagonal}, A::Tridiagonal)
|
||||
if !(iszero(A.dl) && iszero(A.du))
|
||||
throw(ArgumentError("matrix cannot be represented as Diagonal"))
|
||||
end
|
||||
Diagonal(A.d)
|
||||
end
|
||||
|
||||
function convert(::Type{Bidiagonal}, A::Tridiagonal)
|
||||
if iszero(A.dl)
|
||||
return Bidiagonal(A.d, A.du, true)
|
||||
elseif iszero(A.du)
|
||||
return Bidiagonal(A.d, A.dl, false)
|
||||
else
|
||||
throw(ArgumentError("matrix cannot be represented as Bidiagonal"))
|
||||
end
|
||||
end
|
||||
|
||||
function convert(::Type{SymTridiagonal}, A::Tridiagonal)
|
||||
if A.dl != A.du
|
||||
throw(ArgumentError("matrix cannot be represented as SymTridiagonal"))
|
||||
end
|
||||
SymTridiagonal(A.d, A.dl)
|
||||
end
|
||||
|
||||
function convert(::Type{Tridiagonal}, A::SymTridiagonal)
|
||||
Tridiagonal(copy(A.ev), A.dv, A.ev)
|
||||
end
|
||||
|
||||
function convert(::Type{Diagonal}, A::AbstractTriangular)
|
||||
if full(A) != diagm(diag(A))
|
||||
throw(ArgumentError("matrix cannot be represented as Diagonal"))
|
||||
end
|
||||
Diagonal(diag(A))
|
||||
end
|
||||
|
||||
function convert(::Type{Bidiagonal}, A::AbstractTriangular)
|
||||
fA = full(A)
|
||||
if fA == diagm(diag(A)) + diagm(diag(fA, 1), 1)
|
||||
return Bidiagonal(diag(A), diag(fA,1), true)
|
||||
elseif fA == diagm(diag(A)) + diagm(diag(fA, -1), -1)
|
||||
return Bidiagonal(diag(A), diag(fA,-1), false)
|
||||
else
|
||||
throw(ArgumentError("matrix cannot be represented as Bidiagonal"))
|
||||
end
|
||||
end
|
||||
|
||||
convert(::Type{SymTridiagonal}, A::AbstractTriangular) =
|
||||
convert(SymTridiagonal, convert(Tridiagonal, A))
|
||||
|
||||
function convert(::Type{Tridiagonal}, A::AbstractTriangular)
|
||||
fA = full(A)
|
||||
if fA == diagm(diag(A)) + diagm(diag(fA, 1), 1) + diagm(diag(fA, -1), -1)
|
||||
return Tridiagonal(diag(fA, -1), diag(A), diag(fA,1))
|
||||
else
|
||||
throw(ArgumentError("matrix cannot be represented as Tridiagonal"))
|
||||
end
|
||||
end
|
||||
|
||||
# Constructs two method definitions taking into account (assumed) commutativity
|
||||
# e.g. @commutative f{S,T}(x::S, y::T) = x+y is the same is defining
|
||||
# f{S,T}(x::S, y::T) = x+y
|
||||
# f{S,T}(y::T, x::S) = f(x, y)
|
||||
macro commutative(myexpr)
|
||||
@assert myexpr.head===:(=) || myexpr.head===:function # Make sure it is a function definition
|
||||
y = copy(myexpr.args[1].args[2:end])
|
||||
reverse!(y)
|
||||
reversed_call = Expr(:(=), Expr(:call,myexpr.args[1].args[1],y...), myexpr.args[1])
|
||||
esc(Expr(:block, myexpr, reversed_call))
|
||||
end
|
||||
|
||||
for op in (:+, :-)
|
||||
SpecialMatrices = [:Diagonal, :Bidiagonal, :Tridiagonal, :Matrix]
|
||||
for (idx, matrixtype1) in enumerate(SpecialMatrices) # matrixtype1 is the sparser matrix type
|
||||
for matrixtype2 in SpecialMatrices[idx+1:end] # matrixtype2 is the denser matrix type
|
||||
@eval begin # TODO quite a few of these conversions are NOT defined
|
||||
($op)(A::($matrixtype1), B::($matrixtype2)) = ($op)(convert(($matrixtype2), A), B)
|
||||
($op)(A::($matrixtype2), B::($matrixtype1)) = ($op)(A, convert(($matrixtype2), B))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for matrixtype1 in (:SymTridiagonal,) # matrixtype1 is the sparser matrix type
|
||||
for matrixtype2 in (:Tridiagonal, :Matrix) # matrixtype2 is the denser matrix type
|
||||
@eval begin
|
||||
($op)(A::($matrixtype1), B::($matrixtype2)) = ($op)(convert(($matrixtype2), A), B)
|
||||
($op)(A::($matrixtype2), B::($matrixtype1)) = ($op)(A, convert(($matrixtype2), B))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for matrixtype1 in (:Diagonal, :Bidiagonal) # matrixtype1 is the sparser matrix type
|
||||
for matrixtype2 in (:SymTridiagonal,) # matrixtype2 is the denser matrix type
|
||||
@eval begin
|
||||
($op)(A::($matrixtype1), B::($matrixtype2)) = ($op)(convert(($matrixtype2), A), B)
|
||||
($op)(A::($matrixtype2), B::($matrixtype1)) = ($op)(A, convert(($matrixtype2), B))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for matrixtype1 in (:Diagonal,)
|
||||
for (matrixtype2,matrixtype3) in ((:UpperTriangular,:UpperTriangular),
|
||||
(:UnitUpperTriangular,:UpperTriangular),
|
||||
(:LowerTriangular,:LowerTriangular),
|
||||
(:UnitLowerTriangular,:LowerTriangular))
|
||||
@eval begin
|
||||
($op)(A::($matrixtype1), B::($matrixtype2)) = ($op)(($matrixtype3)(A), B)
|
||||
($op)(A::($matrixtype2), B::($matrixtype1)) = ($op)(A, ($matrixtype3)(B))
|
||||
end
|
||||
end
|
||||
end
|
||||
for matrixtype in (:SymTridiagonal,:Tridiagonal,:Bidiagonal,:Matrix)
|
||||
@eval begin
|
||||
($op)(A::AbstractTriangular, B::($matrixtype)) = ($op)(full(A), B)
|
||||
($op)(A::($matrixtype), B::AbstractTriangular) = ($op)(A, full(B))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
A_mul_Bc!(A::AbstractTriangular, B::QRCompactWYQ) = A_mul_Bc!(full!(A),B)
|
||||
A_mul_Bc!(A::AbstractTriangular, B::QRPackedQ) = A_mul_Bc!(full!(A),B)
|
||||
A_mul_Bc(A::AbstractTriangular, B::Union{QRCompactWYQ,QRPackedQ}) = A_mul_Bc(full(A), B)
|
||||
@@ -0,0 +1,314 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# Singular Value Decomposition
|
||||
struct SVD{T,Tr,M<:AbstractArray} <: Factorization{T}
|
||||
U::M
|
||||
S::Vector{Tr}
|
||||
Vt::M
|
||||
SVD{T,Tr,M}(U::AbstractArray{T}, S::Vector{Tr}, Vt::AbstractArray{T}) where {T,Tr,M} =
|
||||
new(U, S, Vt)
|
||||
end
|
||||
SVD(U::AbstractArray{T}, S::Vector{Tr}, Vt::AbstractArray{T}) where {T,Tr} = SVD{T,Tr,typeof(U)}(U, S, Vt)
|
||||
|
||||
"""
|
||||
svdfact!(A, thin::Bool=true) -> SVD
|
||||
|
||||
`svdfact!` is the same as [`svdfact`](@ref), but saves space by
|
||||
overwriting the input `A`, instead of creating a copy.
|
||||
"""
|
||||
function svdfact!(A::StridedMatrix{T}; thin::Bool=true) where T<:BlasFloat
|
||||
m,n = size(A)
|
||||
if m == 0 || n == 0
|
||||
u,s,vt = (eye(T, m, thin ? n : m), real(zeros(T,0)), eye(T,n,n))
|
||||
else
|
||||
u,s,vt = LAPACK.gesdd!(thin ? 'S' : 'A', A)
|
||||
end
|
||||
SVD(u,s,vt)
|
||||
end
|
||||
|
||||
"""
|
||||
svdfact(A; thin::Bool=true) -> SVD
|
||||
|
||||
Compute the singular value decomposition (SVD) of `A` and return an `SVD` object.
|
||||
|
||||
`U`, `S`, `V` and `Vt` can be obtained from the factorization `F` with `F[:U]`,
|
||||
`F[:S]`, `F[:V]` and `F[:Vt]`, such that `A = U*diagm(S)*Vt`.
|
||||
The algorithm produces `Vt` and hence `Vt` is more efficient to extract than `V`.
|
||||
The singular values in `S` are sorted in descending order.
|
||||
|
||||
If `thin=true` (default), a thin SVD is returned. For a ``M \\times N`` matrix
|
||||
`A`, `U` is ``M \\times M`` for a full SVD (`thin=false`) and
|
||||
``M \\times \\min(M, N)`` for a thin SVD.
|
||||
|
||||
# Example
|
||||
```jldoctest
|
||||
julia> A = [1. 0. 0. 0. 2.; 0. 0. 3. 0. 0.; 0. 0. 0. 0. 0.; 0. 2. 0. 0. 0.]
|
||||
4×5 Array{Float64,2}:
|
||||
1.0 0.0 0.0 0.0 2.0
|
||||
0.0 0.0 3.0 0.0 0.0
|
||||
0.0 0.0 0.0 0.0 0.0
|
||||
0.0 2.0 0.0 0.0 0.0
|
||||
|
||||
julia> F = svdfact(A)
|
||||
Base.LinAlg.SVD{Float64,Float64,Array{Float64,2}}([0.0 1.0 0.0 0.0; 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 -1.0; 0.0 0.0 1.0 0.0], [3.0, 2.23607, 2.0, 0.0], [-0.0 0.0 … -0.0 0.0; 0.447214 0.0 … 0.0 0.894427; -0.0 1.0 … -0.0 0.0; 0.0 0.0 … 1.0 0.0])
|
||||
|
||||
julia> F[:U] * diagm(F[:S]) * F[:Vt]
|
||||
4×5 Array{Float64,2}:
|
||||
1.0 0.0 0.0 0.0 2.0
|
||||
0.0 0.0 3.0 0.0 0.0
|
||||
0.0 0.0 0.0 0.0 0.0
|
||||
0.0 2.0 0.0 0.0 0.0
|
||||
```
|
||||
"""
|
||||
function svdfact(A::StridedVecOrMat{T}; thin::Bool = true) where T
|
||||
S = promote_type(Float32, typeof(one(T)/norm(one(T))))
|
||||
svdfact!(copy_oftype(A, S), thin = thin)
|
||||
end
|
||||
svdfact(x::Number; thin::Bool=true) = SVD(x == 0 ? fill(one(x), 1, 1) : fill(x/abs(x), 1, 1), [abs(x)], fill(one(x), 1, 1))
|
||||
svdfact(x::Integer; thin::Bool=true) = svdfact(float(x), thin=thin)
|
||||
|
||||
"""
|
||||
svd(A; thin::Bool=true) -> U, S, V
|
||||
|
||||
Computes the SVD of `A`, returning `U`, vector `S`, and `V` such that
|
||||
`A == U*diagm(S)*V'`. The singular values in `S` are sorted in descending order.
|
||||
|
||||
If `thin=true` (default), a thin SVD is returned. For a ``M \\times N`` matrix
|
||||
`A`, `U` is ``M \\times M`` for a full SVD (`thin=false`) and
|
||||
``M \\times \\min(M, N)`` for a thin SVD.
|
||||
|
||||
`svd` is a wrapper around [`svdfact`](@ref), extracting all parts
|
||||
of the `SVD` factorization to a tuple. Direct use of `svdfact` is therefore more
|
||||
efficient.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1. 0. 0. 0. 2.; 0. 0. 3. 0. 0.; 0. 0. 0. 0. 0.; 0. 2. 0. 0. 0.]
|
||||
4×5 Array{Float64,2}:
|
||||
1.0 0.0 0.0 0.0 2.0
|
||||
0.0 0.0 3.0 0.0 0.0
|
||||
0.0 0.0 0.0 0.0 0.0
|
||||
0.0 2.0 0.0 0.0 0.0
|
||||
|
||||
julia> U, S, V = svd(A)
|
||||
([0.0 1.0 0.0 0.0; 1.0 0.0 0.0 0.0; 0.0 0.0 0.0 -1.0; 0.0 0.0 1.0 0.0], [3.0, 2.23607, 2.0, 0.0], [-0.0 0.447214 -0.0 0.0; 0.0 0.0 1.0 0.0; … ; -0.0 0.0 -0.0 1.0; 0.0 0.894427 0.0 0.0])
|
||||
|
||||
julia> U*diagm(S)*V'
|
||||
4×5 Array{Float64,2}:
|
||||
1.0 0.0 0.0 0.0 2.0
|
||||
0.0 0.0 3.0 0.0 0.0
|
||||
0.0 0.0 0.0 0.0 0.0
|
||||
0.0 2.0 0.0 0.0 0.0
|
||||
```
|
||||
"""
|
||||
function svd(A::Union{Number, AbstractArray}; thin::Bool=true)
|
||||
F = svdfact(A, thin=thin)
|
||||
F.U, F.S, F.Vt'
|
||||
end
|
||||
|
||||
function getindex(F::SVD, d::Symbol)
|
||||
if d == :U
|
||||
return F.U
|
||||
elseif d == :S
|
||||
return F.S
|
||||
elseif d == :Vt
|
||||
return F.Vt
|
||||
elseif d == :V
|
||||
return F.Vt'
|
||||
else
|
||||
throw(KeyError(d))
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
svdvals!(A)
|
||||
|
||||
Returns the singular values of `A`, saving space by overwriting the input.
|
||||
See also [`svdvals`](@ref).
|
||||
"""
|
||||
svdvals!(A::StridedMatrix{T}) where {T<:BlasFloat} = findfirst(size(A), 0) > 0 ? zeros(T, 0) : LAPACK.gesdd!('N', A)[2]
|
||||
svdvals(A::AbstractMatrix{<:BlasFloat}) = svdvals!(copy(A))
|
||||
|
||||
"""
|
||||
svdvals(A)
|
||||
|
||||
Returns the singular values of `A` in descending order.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1. 0. 0. 0. 2.; 0. 0. 3. 0. 0.; 0. 0. 0. 0. 0.; 0. 2. 0. 0. 0.]
|
||||
4×5 Array{Float64,2}:
|
||||
1.0 0.0 0.0 0.0 2.0
|
||||
0.0 0.0 3.0 0.0 0.0
|
||||
0.0 0.0 0.0 0.0 0.0
|
||||
0.0 2.0 0.0 0.0 0.0
|
||||
|
||||
julia> svdvals(A)
|
||||
4-element Array{Float64,1}:
|
||||
3.0
|
||||
2.23607
|
||||
2.0
|
||||
0.0
|
||||
```
|
||||
"""
|
||||
function svdvals(A::AbstractMatrix{T}) where T
|
||||
S = promote_type(Float32, typeof(one(T)/norm(one(T))))
|
||||
svdvals!(copy_oftype(A, S))
|
||||
end
|
||||
svdvals(x::Number) = abs(x)
|
||||
svdvals(S::SVD{<:Any,T}) where {T} = (S[:S])::Vector{T}
|
||||
|
||||
# SVD least squares
|
||||
function A_ldiv_B!{T}(A::SVD{T}, B::StridedVecOrMat)
|
||||
k = searchsortedlast(A.S, eps(real(T))*A.S[1], rev=true)
|
||||
view(A.Vt,1:k,:)' * (view(A.S,1:k) .\ (view(A.U,:,1:k)' * B))
|
||||
end
|
||||
|
||||
# Generalized svd
|
||||
struct GeneralizedSVD{T,S} <: Factorization{T}
|
||||
U::S
|
||||
V::S
|
||||
Q::S
|
||||
a::Vector
|
||||
b::Vector
|
||||
k::Int
|
||||
l::Int
|
||||
R::S
|
||||
function GeneralizedSVD{T,S}(U::AbstractMatrix{T}, V::AbstractMatrix{T}, Q::AbstractMatrix{T},
|
||||
a::Vector, b::Vector, k::Int, l::Int, R::AbstractMatrix{T}) where {T,S}
|
||||
new(U, V, Q, a, b, k, l, R)
|
||||
end
|
||||
end
|
||||
function GeneralizedSVD(U::AbstractMatrix{T}, V::AbstractMatrix{T}, Q::AbstractMatrix{T},
|
||||
a::Vector, b::Vector, k::Int, l::Int, R::AbstractMatrix{T}) where T
|
||||
GeneralizedSVD{T,typeof(U)}(U, V, Q, a, b, k, l, R)
|
||||
end
|
||||
|
||||
"""
|
||||
svdfact!(A, B) -> GeneralizedSVD
|
||||
|
||||
`svdfact!` is the same as [`svdfact`](@ref), but modifies the arguments
|
||||
`A` and `B` in-place, instead of making copies.
|
||||
"""
|
||||
function svdfact!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasFloat
|
||||
# xggsvd3 replaced xggsvd in LAPACK 3.6.0
|
||||
if LAPACK.laver() < (3, 6, 0)
|
||||
U, V, Q, a, b, k, l, R = LAPACK.ggsvd!('U', 'V', 'Q', A, B)
|
||||
else
|
||||
U, V, Q, a, b, k, l, R = LAPACK.ggsvd3!('U', 'V', 'Q', A, B)
|
||||
end
|
||||
GeneralizedSVD(U, V, Q, a, b, Int(k), Int(l), R)
|
||||
end
|
||||
svdfact(A::StridedMatrix{T}, B::StridedMatrix{T}) where {T<:BlasFloat} = svdfact!(copy(A),copy(B))
|
||||
|
||||
"""
|
||||
svdfact(A, B) -> GeneralizedSVD
|
||||
|
||||
Compute the generalized SVD of `A` and `B`, returning a `GeneralizedSVD` factorization
|
||||
object `F`, such that `A = F[:U]*F[:D1]*F[:R0]*F[:Q]'` and `B = F[:V]*F[:D2]*F[:R0]*F[:Q]'`.
|
||||
|
||||
For an M-by-N matrix `A` and P-by-N matrix `B`,
|
||||
|
||||
- `F[:U]` is a M-by-M orthogonal matrix,
|
||||
- `F[:V]` is a P-by-P orthogonal matrix,
|
||||
- `F[:Q]` is a N-by-N orthogonal matrix,
|
||||
- `F[:R0]` is a (K+L)-by-N matrix whose rightmost (K+L)-by-(K+L) block is
|
||||
nonsingular upper block triangular,
|
||||
- `F[:D1]` is a M-by-(K+L) diagonal matrix with 1s in the first K entries,
|
||||
- `F[:D2]` is a P-by-(K+L) matrix whose top right L-by-L block is diagonal,
|
||||
|
||||
`K+L` is the effective numerical rank of the matrix `[A; B]`.
|
||||
|
||||
The entries of `F[:D1]` and `F[:D2]` are related, as explained in the LAPACK
|
||||
documentation for the
|
||||
[generalized SVD](http://www.netlib.org/lapack/lug/node36.html) and the
|
||||
[xGGSVD3](http://www.netlib.org/lapack/explore-html/d6/db3/dggsvd3_8f.html)
|
||||
routine which is called underneath (in LAPACK 3.6.0 and newer).
|
||||
"""
|
||||
function svdfact(A::StridedMatrix{TA}, B::StridedMatrix{TB}) where {TA,TB}
|
||||
S = promote_type(Float32, typeof(one(TA)/norm(one(TA))),TB)
|
||||
return svdfact!(copy_oftype(A, S), copy_oftype(B, S))
|
||||
end
|
||||
|
||||
"""
|
||||
svd(A, B) -> U, V, Q, D1, D2, R0
|
||||
|
||||
Wrapper around [`svdfact`](@ref) extracting all parts of the
|
||||
factorization to a tuple. Direct use of
|
||||
`svdfact` is therefore generally more efficient. The function returns the generalized SVD of
|
||||
`A` and `B`, returning `U`, `V`, `Q`, `D1`, `D2`, and `R0` such that `A = U*D1*R0*Q'` and `B =
|
||||
V*D2*R0*Q'`.
|
||||
"""
|
||||
function svd(A::AbstractMatrix, B::AbstractMatrix)
|
||||
F = svdfact(A, B)
|
||||
F[:U], F[:V], F[:Q], F[:D1], F[:D2], F[:R0]
|
||||
end
|
||||
|
||||
function getindex(obj::GeneralizedSVD{T}, d::Symbol) where T
|
||||
if d == :U
|
||||
return obj.U
|
||||
elseif d == :V
|
||||
return obj.V
|
||||
elseif d == :Q
|
||||
return obj.Q
|
||||
elseif d == :alpha || d == :a
|
||||
return obj.a
|
||||
elseif d == :beta || d == :b
|
||||
return obj.b
|
||||
elseif d == :vals || d == :S
|
||||
return obj.a[1:obj.k + obj.l] ./ obj.b[1:obj.k + obj.l]
|
||||
elseif d == :D1
|
||||
m = size(obj.U, 1)
|
||||
if m - obj.k - obj.l >= 0
|
||||
return [eye(T, obj.k) zeros(T, obj.k, obj.l); zeros(T, obj.l, obj.k) diagm(obj.a[obj.k + 1:obj.k + obj.l]); zeros(T, m - obj.k - obj.l, obj.k + obj.l)]
|
||||
else
|
||||
return [eye(T, m, obj.k) [zeros(T, obj.k, m - obj.k); diagm(obj.a[obj.k + 1:m])] zeros(T, m, obj.k + obj.l - m)]
|
||||
end
|
||||
elseif d == :D2
|
||||
m = size(obj.U, 1)
|
||||
p = size(obj.V, 1)
|
||||
if m - obj.k - obj.l >= 0
|
||||
return [zeros(T, obj.l, obj.k) diagm(obj.b[obj.k + 1:obj.k + obj.l]); zeros(T, p - obj.l, obj.k + obj.l)]
|
||||
else
|
||||
return [zeros(T, p, obj.k) [diagm(obj.b[obj.k + 1:m]); zeros(T, obj.k + p - m, m - obj.k)] [zeros(T, m - obj.k, obj.k + obj.l - m); eye(T, obj.k + p - m, obj.k + obj.l - m)]]
|
||||
end
|
||||
elseif d == :R
|
||||
return obj.R
|
||||
elseif d == :R0
|
||||
n = size(obj.Q, 1)
|
||||
return [zeros(T, obj.k + obj.l, n - obj.k - obj.l) obj.R]
|
||||
else
|
||||
throw(KeyError(d))
|
||||
end
|
||||
end
|
||||
|
||||
function svdvals!(A::StridedMatrix{T}, B::StridedMatrix{T}) where T<:BlasFloat
|
||||
# xggsvd3 replaced xggsvd in LAPACK 3.6.0
|
||||
if LAPACK.laver() < (3, 6, 0)
|
||||
_, _, _, a, b, k, l, _ = LAPACK.ggsvd!('N', 'N', 'N', A, B)
|
||||
else
|
||||
_, _, _, a, b, k, l, _ = LAPACK.ggsvd3!('N', 'N', 'N', A, B)
|
||||
end
|
||||
a[1:k + l] ./ b[1:k + l]
|
||||
end
|
||||
svdvals(A::StridedMatrix{T},B::StridedMatrix{T}) where {T<:BlasFloat} = svdvals!(copy(A),copy(B))
|
||||
|
||||
"""
|
||||
svdvals(A, B)
|
||||
|
||||
Return the generalized singular values from the generalized singular value
|
||||
decomposition of `A` and `B`. See also [`svdfact`](@ref).
|
||||
"""
|
||||
function svdvals(A::StridedMatrix{TA}, B::StridedMatrix{TB}) where {TA,TB}
|
||||
S = promote_type(Float32, typeof(one(TA)/norm(one(TA))), TB)
|
||||
return svdvals!(copy_oftype(A, S), copy_oftype(B, S))
|
||||
end
|
||||
|
||||
# Conversion
|
||||
convert(::Type{AbstractMatrix}, F::SVD) = (F.U * Diagonal(F.S)) * F.Vt
|
||||
convert(::Type{AbstractArray}, F::SVD) = convert(AbstractMatrix, F)
|
||||
convert(::Type{Matrix}, F::SVD) = convert(Array, convert(AbstractArray, F))
|
||||
convert(::Type{Array}, F::SVD) = convert(Matrix, F)
|
||||
full(F::SVD) = convert(AbstractArray, F)
|
||||
@@ -0,0 +1,560 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
# Symmetric and Hermitian matrices
|
||||
struct Symmetric{T,S<:AbstractMatrix} <: AbstractMatrix{T}
|
||||
data::S
|
||||
uplo::Char
|
||||
end
|
||||
"""
|
||||
Symmetric(A, uplo=:U)
|
||||
|
||||
Construct a `Symmetric` view of the upper (if `uplo = :U`) or lower (if `uplo = :L`)
|
||||
triangle of the matrix `A`.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1 0 2 0 3; 0 4 0 5 0; 6 0 7 0 8; 0 9 0 1 0; 2 0 3 0 4]
|
||||
5×5 Array{Int64,2}:
|
||||
1 0 2 0 3
|
||||
0 4 0 5 0
|
||||
6 0 7 0 8
|
||||
0 9 0 1 0
|
||||
2 0 3 0 4
|
||||
|
||||
julia> Supper = Symmetric(A)
|
||||
5×5 Symmetric{Int64,Array{Int64,2}}:
|
||||
1 0 2 0 3
|
||||
0 4 0 5 0
|
||||
2 0 7 0 8
|
||||
0 5 0 1 0
|
||||
3 0 8 0 4
|
||||
|
||||
julia> Slower = Symmetric(A, :L)
|
||||
5×5 Symmetric{Int64,Array{Int64,2}}:
|
||||
1 0 6 0 2
|
||||
0 4 0 9 0
|
||||
6 0 7 0 3
|
||||
0 9 0 1 0
|
||||
2 0 3 0 4
|
||||
```
|
||||
|
||||
Note that `Supper` will not be equal to `Slower` unless `A` is itself symmetric (e.g. if `A == A.'`).
|
||||
"""
|
||||
Symmetric(A::AbstractMatrix, uplo::Symbol=:U) = (checksquare(A); Symmetric{eltype(A),typeof(A)}(A, char_uplo(uplo)))
|
||||
Symmetric(A::Symmetric) = A
|
||||
function Symmetric(A::Symmetric, uplo::Symbol)
|
||||
if A.uplo == char_uplo(uplo)
|
||||
return A
|
||||
else
|
||||
throw(ArgumentError("Cannot construct Symmetric; uplo doesn't match"))
|
||||
end
|
||||
end
|
||||
|
||||
struct Hermitian{T,S<:AbstractMatrix} <: AbstractMatrix{T}
|
||||
data::S
|
||||
uplo::Char
|
||||
end
|
||||
"""
|
||||
Hermitian(A, uplo=:U)
|
||||
|
||||
Construct a `Hermitian` view of the upper (if `uplo = :U`) or lower (if `uplo = :L`)
|
||||
triangle of the matrix `A`.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1 0 2+2im 0 3-3im; 0 4 0 5 0; 6-6im 0 7 0 8+8im; 0 9 0 1 0; 2+2im 0 3-3im 0 4];
|
||||
|
||||
julia> Hupper = Hermitian(A)
|
||||
5×5 Hermitian{Complex{Int64},Array{Complex{Int64},2}}:
|
||||
1+0im 0+0im 2+2im 0+0im 3-3im
|
||||
0+0im 4+0im 0+0im 5+0im 0+0im
|
||||
2-2im 0+0im 7+0im 0+0im 8+8im
|
||||
0+0im 5+0im 0+0im 1+0im 0+0im
|
||||
3+3im 0+0im 8-8im 0+0im 4+0im
|
||||
|
||||
julia> Hlower = Hermitian(A, :L)
|
||||
5×5 Hermitian{Complex{Int64},Array{Complex{Int64},2}}:
|
||||
1+0im 0+0im 6+6im 0+0im 2-2im
|
||||
0+0im 4+0im 0+0im 9+0im 0+0im
|
||||
6-6im 0+0im 7+0im 0+0im 3+3im
|
||||
0+0im 9+0im 0+0im 1+0im 0+0im
|
||||
2+2im 0+0im 3-3im 0+0im 4+0im
|
||||
```
|
||||
|
||||
Note that `Hupper` will not be equal to `Hlower` unless `A` is itself Hermitian (e.g. if `A == A'`).
|
||||
"""
|
||||
function Hermitian(A::AbstractMatrix, uplo::Symbol=:U)
|
||||
n = checksquare(A)
|
||||
for i=1:n
|
||||
isreal(A[i, i]) || throw(ArgumentError(
|
||||
"Cannot construct Hermitian from matrix with nonreal diagonals"))
|
||||
end
|
||||
Hermitian{eltype(A),typeof(A)}(A, char_uplo(uplo))
|
||||
end
|
||||
Hermitian(A::Hermitian) = A
|
||||
function Hermitian(A::Hermitian, uplo::Symbol)
|
||||
if A.uplo == char_uplo(uplo)
|
||||
return A
|
||||
else
|
||||
throw(ArgumentError("Cannot construct Hermitian; uplo doesn't match"))
|
||||
end
|
||||
end
|
||||
|
||||
const HermOrSym{T,S} = Union{Hermitian{T,S}, Symmetric{T,S}}
|
||||
const RealHermSymComplexHerm{T<:Real,S} = Union{Hermitian{T,S}, Symmetric{T,S}, Hermitian{Complex{T},S}}
|
||||
|
||||
size(A::HermOrSym, d) = size(A.data, d)
|
||||
size(A::HermOrSym) = size(A.data)
|
||||
@inline function getindex(A::Symmetric, i::Integer, j::Integer)
|
||||
@boundscheck checkbounds(A, i, j)
|
||||
@inbounds r = (A.uplo == 'U') == (i < j) ? A.data[i, j] : A.data[j, i]
|
||||
r
|
||||
end
|
||||
@inline function getindex(A::Hermitian, i::Integer, j::Integer)
|
||||
@boundscheck checkbounds(A, i, j)
|
||||
@inbounds r = (A.uplo == 'U') == (i < j) ? A.data[i, j] : conj(A.data[j, i])
|
||||
r
|
||||
end
|
||||
|
||||
function setindex!(A::Symmetric, v, i::Integer, j::Integer)
|
||||
i == j || throw(ArgumentError("Cannot set a non-diagonal index in a symmetric matrix"))
|
||||
setindex!(A.data, v, i, j)
|
||||
end
|
||||
|
||||
function setindex!(A::Hermitian, v, i::Integer, j::Integer)
|
||||
if i != j
|
||||
throw(ArgumentError("Cannot set a non-diagonal index in a Hermitian matrix"))
|
||||
elseif !isreal(v)
|
||||
throw(ArgumentError("Cannot set a diagonal entry in a Hermitian matrix to a nonreal value"))
|
||||
else
|
||||
setindex!(A.data, v, i, j)
|
||||
end
|
||||
end
|
||||
|
||||
similar(A::Symmetric, ::Type{T}) where {T} = Symmetric(similar(A.data, T))
|
||||
# Hermitian version can be simplified when check for imaginary part of
|
||||
# diagonal in Hermitian has been removed
|
||||
function similar(A::Hermitian, ::Type{T}) where T
|
||||
B = similar(A.data, T)
|
||||
for i = 1:size(A,1)
|
||||
B[i,i] = 0
|
||||
end
|
||||
return Hermitian(B)
|
||||
end
|
||||
|
||||
# Conversion
|
||||
convert(::Type{Matrix}, A::Symmetric) = copytri!(convert(Matrix, copy(A.data)), A.uplo)
|
||||
convert(::Type{Matrix}, A::Hermitian) = copytri!(convert(Matrix, copy(A.data)), A.uplo, true)
|
||||
convert(::Type{Array}, A::Union{Symmetric,Hermitian}) = convert(Matrix, A)
|
||||
full(A::Union{Symmetric,Hermitian}) = convert(Array, A)
|
||||
parent(A::HermOrSym) = A.data
|
||||
convert(::Type{Symmetric{T,S}},A::Symmetric{T,S}) where {T,S<:AbstractMatrix} = A
|
||||
convert(::Type{Symmetric{T,S}},A::Symmetric) where {T,S<:AbstractMatrix} = Symmetric{T,S}(convert(S,A.data),A.uplo)
|
||||
convert(::Type{AbstractMatrix{T}}, A::Symmetric) where {T} = Symmetric(convert(AbstractMatrix{T}, A.data), Symbol(A.uplo))
|
||||
convert(::Type{Hermitian{T,S}},A::Hermitian{T,S}) where {T,S<:AbstractMatrix} = A
|
||||
convert(::Type{Hermitian{T,S}},A::Hermitian) where {T,S<:AbstractMatrix} = Hermitian{T,S}(convert(S,A.data),A.uplo)
|
||||
convert(::Type{AbstractMatrix{T}}, A::Hermitian) where {T} = Hermitian(convert(AbstractMatrix{T}, A.data), Symbol(A.uplo))
|
||||
|
||||
copy(A::Symmetric{T,S}) where {T,S} = (B = copy(A.data); Symmetric{T,typeof(B)}(B,A.uplo))
|
||||
copy(A::Hermitian{T,S}) where {T,S} = (B = copy(A.data); Hermitian{T,typeof(B)}(B,A.uplo))
|
||||
|
||||
function copy!(dest::Symmetric, src::Symmetric)
|
||||
if src.uplo == dest.uplo
|
||||
copy!(dest.data, src.data)
|
||||
else
|
||||
transpose!(dest.data, src.data)
|
||||
end
|
||||
return dest
|
||||
end
|
||||
|
||||
function copy!(dest::Hermitian, src::Hermitian)
|
||||
if src.uplo == dest.uplo
|
||||
copy!(dest.data, src.data)
|
||||
else
|
||||
ctranspose!(dest.data, src.data)
|
||||
end
|
||||
return dest
|
||||
end
|
||||
|
||||
ishermitian(A::Hermitian) = true
|
||||
ishermitian(A::Symmetric{<:Real}) = true
|
||||
ishermitian(A::Symmetric{<:Complex}) = isreal(A.data)
|
||||
issymmetric(A::Hermitian{<:Real}) = true
|
||||
issymmetric(A::Hermitian{<:Complex}) = isreal(A.data)
|
||||
issymmetric(A::Symmetric) = true
|
||||
transpose(A::Symmetric) = A
|
||||
ctranspose(A::Symmetric{<:Real}) = A
|
||||
function ctranspose(A::Symmetric)
|
||||
AC = ctranspose(A.data)
|
||||
return Symmetric(AC, ifelse(A.uplo == 'U', :L, :U))
|
||||
end
|
||||
function transpose(A::Hermitian)
|
||||
AT = transpose(A.data)
|
||||
return Hermitian(AT, ifelse(A.uplo == 'U', :L, :U))
|
||||
end
|
||||
ctranspose(A::Hermitian) = A
|
||||
trace(A::Hermitian) = real(trace(A.data))
|
||||
|
||||
Base.conj(A::HermOrSym) = typeof(A)(conj(A.data), A.uplo)
|
||||
Base.conj!(A::HermOrSym) = typeof(A)(conj!(A.data), A.uplo)
|
||||
|
||||
# tril/triu
|
||||
function tril(A::Hermitian, k::Integer=0)
|
||||
if A.uplo == 'U' && k <= 0
|
||||
return tril!(A.data',k)
|
||||
elseif A.uplo == 'U' && k > 0
|
||||
return tril!(A.data',-1) + tril!(triu(A.data),k)
|
||||
elseif A.uplo == 'L' && k <= 0
|
||||
return tril(A.data,k)
|
||||
else
|
||||
return tril(A.data,-1) + tril!(triu!(A.data'),k)
|
||||
end
|
||||
end
|
||||
|
||||
function tril(A::Symmetric, k::Integer=0)
|
||||
if A.uplo == 'U' && k <= 0
|
||||
return tril!(A.data.',k)
|
||||
elseif A.uplo == 'U' && k > 0
|
||||
return tril!(A.data.',-1) + tril!(triu(A.data),k)
|
||||
elseif A.uplo == 'L' && k <= 0
|
||||
return tril(A.data,k)
|
||||
else
|
||||
return tril(A.data,-1) + tril!(triu!(A.data.'),k)
|
||||
end
|
||||
end
|
||||
|
||||
function triu(A::Hermitian, k::Integer=0)
|
||||
if A.uplo == 'U' && k >= 0
|
||||
return triu(A.data,k)
|
||||
elseif A.uplo == 'U' && k < 0
|
||||
return triu(A.data,1) + triu!(tril!(A.data'),k)
|
||||
elseif A.uplo == 'L' && k >= 0
|
||||
return triu!(A.data',k)
|
||||
else
|
||||
return triu!(A.data',1) + triu!(tril(A.data),k)
|
||||
end
|
||||
end
|
||||
|
||||
function triu(A::Symmetric, k::Integer=0)
|
||||
if A.uplo == 'U' && k >= 0
|
||||
return triu(A.data,k)
|
||||
elseif A.uplo == 'U' && k < 0
|
||||
return triu(A.data,1) + triu!(tril!(A.data.'),k)
|
||||
elseif A.uplo == 'L' && k >= 0
|
||||
return triu!(A.data.',k)
|
||||
else
|
||||
return triu!(A.data.',1) + triu!(tril(A.data),k)
|
||||
end
|
||||
end
|
||||
|
||||
(-)(A::Symmetric{Tv,S}) where {Tv,S<:AbstractMatrix} = Symmetric{Tv,S}(-A.data, A.uplo)
|
||||
|
||||
## Matvec
|
||||
A_mul_B!(y::StridedVector{T}, A::Symmetric{T,<:StridedMatrix}, x::StridedVector{T}) where {T<:BlasFloat} =
|
||||
BLAS.symv!(A.uplo, one(T), A.data, x, zero(T), y)
|
||||
A_mul_B!(y::StridedVector{T}, A::Hermitian{T,<:StridedMatrix}, x::StridedVector{T}) where {T<:BlasComplex} =
|
||||
BLAS.hemv!(A.uplo, one(T), A.data, x, zero(T), y)
|
||||
## Matmat
|
||||
A_mul_B!(C::StridedMatrix{T}, A::Symmetric{T,<:StridedMatrix}, B::StridedMatrix{T}) where {T<:BlasFloat} =
|
||||
BLAS.symm!('L', A.uplo, one(T), A.data, B, zero(T), C)
|
||||
A_mul_B!(C::StridedMatrix{T}, A::StridedMatrix{T}, B::Symmetric{T,<:StridedMatrix}) where {T<:BlasFloat} =
|
||||
BLAS.symm!('R', B.uplo, one(T), B.data, A, zero(T), C)
|
||||
A_mul_B!(C::StridedMatrix{T}, A::Hermitian{T,<:StridedMatrix}, B::StridedMatrix{T}) where {T<:BlasComplex} =
|
||||
BLAS.hemm!('L', A.uplo, one(T), A.data, B, zero(T), C)
|
||||
A_mul_B!(C::StridedMatrix{T}, A::StridedMatrix{T}, B::Hermitian{T,<:StridedMatrix}) where {T<:BlasComplex} =
|
||||
BLAS.hemm!('R', B.uplo, one(T), B.data, A, zero(T), C)
|
||||
|
||||
*(A::HermOrSym, B::HermOrSym) = full(A)*full(B)
|
||||
*(A::StridedMatrix, B::HermOrSym) = A*full(B)
|
||||
|
||||
for T in (:Symmetric, :Hermitian), op in (:+, :-, :*, :/)
|
||||
# Deal with an ambiguous case
|
||||
@eval ($op)(A::$T, x::Bool) = ($T)(($op)(A.data, x), Symbol(A.uplo))
|
||||
S = T == :Hermitian ? :Real : :Number
|
||||
@eval ($op)(A::$T, x::$S) = ($T)(($op)(A.data, x), Symbol(A.uplo))
|
||||
end
|
||||
|
||||
bkfact(A::HermOrSym) = bkfact(A.data, Symbol(A.uplo), issymmetric(A))
|
||||
factorize(A::HermOrSym) = bkfact(A)
|
||||
|
||||
det(A::RealHermSymComplexHerm) = real(det(bkfact(A)))
|
||||
det(A::Symmetric{<:Real}) = det(bkfact(A))
|
||||
det(A::Symmetric) = det(bkfact(A))
|
||||
|
||||
\(A::HermOrSym{<:Any,<:StridedMatrix}, B::StridedVecOrMat) = \(bkfact(A.data, Symbol(A.uplo), issymmetric(A)), B)
|
||||
|
||||
inv(A::Hermitian{T,S}) where {T<:BlasFloat,S<:StridedMatrix} = Hermitian{T,S}(inv(bkfact(A)), A.uplo)
|
||||
inv(A::Symmetric{T,S}) where {T<:BlasFloat,S<:StridedMatrix} = Symmetric{T,S}(inv(bkfact(A)), A.uplo)
|
||||
|
||||
isposdef!(A::HermOrSym{<:BlasFloat,<:StridedMatrix}) = ishermitian(A) && LAPACK.potrf!(A.uplo, A.data)[2] == 0
|
||||
|
||||
eigfact!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}) = Eigen(LAPACK.syevr!('V', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)...)
|
||||
|
||||
function eigfact(A::RealHermSymComplexHerm)
|
||||
T = eltype(A)
|
||||
S = promote_type(Float32, typeof(zero(T)/norm(one(T))))
|
||||
eigfact!(S != T ? convert(AbstractMatrix{S}, A) : copy(A))
|
||||
end
|
||||
|
||||
eigfact!(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)...)
|
||||
|
||||
"""
|
||||
eigfact(A::Union{SymTridiagonal, Hermitian, Symmetric}, irange::UnitRange) -> 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).
|
||||
|
||||
The `UnitRange` `irange` specifies indices of the sorted eigenvalues to search for.
|
||||
|
||||
!!! note
|
||||
If `irange` is not `1:n`, where `n` is the dimension of `A`, then the returned factorization
|
||||
will be a *truncated* factorization.
|
||||
"""
|
||||
function eigfact(A::RealHermSymComplexHerm, irange::UnitRange)
|
||||
T = eltype(A)
|
||||
S = promote_type(Float32, typeof(zero(T)/norm(one(T))))
|
||||
eigfact!(S != T ? convert(AbstractMatrix{S}, A) : copy(A), irange)
|
||||
end
|
||||
|
||||
eigfact!(A::RealHermSymComplexHerm{T,<:StridedMatrix}, vl::Real, vh::Real) where {T<:BlasReal} =
|
||||
Eigen(LAPACK.syevr!('V', 'V', A.uplo, A.data, convert(T, vl), convert(T, vh), 0, 0, -1.0)...)
|
||||
|
||||
"""
|
||||
eigfact(A::Union{SymTridiagonal, Hermitian, Symmetric}, vl::Real, vu::Real) -> 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).
|
||||
|
||||
`vl` is the lower bound of the window of eigenvalues to search for, and `vu` is the upper bound.
|
||||
|
||||
!!! note
|
||||
If [`vl`, `vu`] does not contain all eigenvalues of `A`, then the returned factorization
|
||||
will be a *truncated* factorization.
|
||||
"""
|
||||
function eigfact(A::RealHermSymComplexHerm, vl::Real, vh::Real)
|
||||
T = eltype(A)
|
||||
S = promote_type(Float32, typeof(zero(T)/norm(one(T))))
|
||||
eigfact!(S != T ? convert(AbstractMatrix{S}, A) : copy(A), vl, vh)
|
||||
end
|
||||
|
||||
eigvals!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}) =
|
||||
LAPACK.syevr!('N', 'A', A.uplo, A.data, 0.0, 0.0, 0, 0, -1.0)[1]
|
||||
|
||||
function eigvals(A::RealHermSymComplexHerm)
|
||||
T = eltype(A)
|
||||
S = promote_type(Float32, typeof(zero(T)/norm(one(T))))
|
||||
eigvals!(S != T ? convert(AbstractMatrix{S}, A) : copy(A))
|
||||
end
|
||||
|
||||
"""
|
||||
eigvals!(A::Union{SymTridiagonal, Hermitian, Symmetric}, irange::UnitRange) -> values
|
||||
|
||||
Same as [`eigvals`](@ref), but saves space by overwriting the input `A`, instead of creating a copy.
|
||||
`irange` is a range of eigenvalue *indices* to search for - for instance, the 2nd to 8th eigenvalues.
|
||||
"""
|
||||
eigvals!(A::RealHermSymComplexHerm{<:BlasReal,<:StridedMatrix}, irange::UnitRange) =
|
||||
LAPACK.syevr!('N', 'I', A.uplo, A.data, 0.0, 0.0, irange.start, irange.stop, -1.0)[1]
|
||||
|
||||
"""
|
||||
eigvals(A::Union{SymTridiagonal, Hermitian, Symmetric}, irange::UnitRange) -> values
|
||||
|
||||
Returns the eigenvalues of `A`. It is possible to calculate only a subset of the
|
||||
eigenvalues by specifying a `UnitRange` `irange` covering indices of the sorted eigenvalues,
|
||||
e.g. the 2nd to 8th eigenvalues.
|
||||
|
||||
```jldoctest
|
||||
julia> A = SymTridiagonal([1.; 2.; 1.], [2.; 3.])
|
||||
3×3 SymTridiagonal{Float64}:
|
||||
1.0 2.0 ⋅
|
||||
2.0 2.0 3.0
|
||||
⋅ 3.0 1.0
|
||||
|
||||
julia> eigvals(A, 2:2)
|
||||
1-element Array{Float64,1}:
|
||||
1.0
|
||||
|
||||
julia> eigvals(A)
|
||||
3-element Array{Float64,1}:
|
||||
-2.14005
|
||||
1.0
|
||||
5.14005
|
||||
```
|
||||
"""
|
||||
function eigvals(A::RealHermSymComplexHerm, irange::UnitRange)
|
||||
T = eltype(A)
|
||||
S = promote_type(Float32, typeof(zero(T)/norm(one(T))))
|
||||
eigvals!(S != T ? convert(AbstractMatrix{S}, A) : copy(A), irange)
|
||||
end
|
||||
|
||||
"""
|
||||
eigvals!(A::Union{SymTridiagonal, Hermitian, Symmetric}, vl::Real, vu::Real) -> values
|
||||
|
||||
Same as [`eigvals`](@ref), but saves space by overwriting the input `A`, instead of creating a copy.
|
||||
`vl` is the lower bound of the interval to search for eigenvalues, and `vu` is the upper bound.
|
||||
"""
|
||||
eigvals!(A::RealHermSymComplexHerm{T,<:StridedMatrix}, vl::Real, vh::Real) where {T<:BlasReal} =
|
||||
LAPACK.syevr!('N', 'V', A.uplo, A.data, convert(T, vl), convert(T, vh), 0, 0, -1.0)[1]
|
||||
|
||||
"""
|
||||
eigvals(A::Union{SymTridiagonal, Hermitian, Symmetric}, vl::Real, vu::Real) -> values
|
||||
|
||||
Returns the eigenvalues of `A`. It is possible to calculate only a subset of the eigenvalues
|
||||
by specifying a pair `vl` and `vu` for the lower and upper boundaries of the eigenvalues.
|
||||
|
||||
```jldoctest
|
||||
julia> A = SymTridiagonal([1.; 2.; 1.], [2.; 3.])
|
||||
3×3 SymTridiagonal{Float64}:
|
||||
1.0 2.0 ⋅
|
||||
2.0 2.0 3.0
|
||||
⋅ 3.0 1.0
|
||||
|
||||
julia> eigvals(A, -1, 2)
|
||||
1-element Array{Float64,1}:
|
||||
1.0
|
||||
|
||||
julia> eigvals(A)
|
||||
3-element Array{Float64,1}:
|
||||
-2.14005
|
||||
1.0
|
||||
5.14005
|
||||
```
|
||||
"""
|
||||
function eigvals(A::RealHermSymComplexHerm, vl::Real, vh::Real)
|
||||
T = eltype(A)
|
||||
S = promote_type(Float32, typeof(zero(T)/norm(one(T))))
|
||||
eigvals!(S != T ? convert(AbstractMatrix{S}, A) : copy(A), vl, vh)
|
||||
end
|
||||
|
||||
eigmax(A::RealHermSymComplexHerm{<:Real,<:StridedMatrix}) = eigvals(A, size(A, 1):size(A, 1))[1]
|
||||
eigmin(A::RealHermSymComplexHerm{<:Real,<:StridedMatrix}) = eigvals(A, 1:1)[1]
|
||||
|
||||
function eigfact!(A::HermOrSym{T,S}, B::HermOrSym{T,S}) where {T<:BlasReal,S<:StridedMatrix}
|
||||
vals, vecs, _ = LAPACK.sygvd!(1, 'V', A.uplo, A.data, B.uplo == A.uplo ? B.data : B.data')
|
||||
GeneralizedEigen(vals, vecs)
|
||||
end
|
||||
function eigfact!(A::Hermitian{T,S}, B::Hermitian{T,S}) where {T<:BlasComplex,S<:StridedMatrix}
|
||||
vals, vecs, _ = LAPACK.sygvd!(1, 'V', A.uplo, A.data, B.uplo == A.uplo ? B.data : B.data')
|
||||
GeneralizedEigen(vals, vecs)
|
||||
end
|
||||
|
||||
eigvals!(A::HermOrSym{T,S}, B::HermOrSym{T,S}) where {T<:BlasReal,S<:StridedMatrix} =
|
||||
LAPACK.sygvd!(1, 'N', A.uplo, A.data, B.uplo == A.uplo ? B.data : B.data')[1]
|
||||
eigvals!(A::Hermitian{T,S}, B::Hermitian{T,S}) where {T<:BlasComplex,S<:StridedMatrix} =
|
||||
LAPACK.sygvd!(1, 'N', A.uplo, A.data, B.uplo == A.uplo ? B.data : B.data')[1]
|
||||
|
||||
eigvecs(A::HermOrSym) = eigvecs(eigfact(A))
|
||||
|
||||
function svdvals!(A::RealHermSymComplexHerm)
|
||||
vals = eigvals!(A)
|
||||
for i = 1:length(vals)
|
||||
vals[i] = abs(vals[i])
|
||||
end
|
||||
return sort!(vals, rev = true)
|
||||
end
|
||||
|
||||
# Matrix functions
|
||||
function ^(A::Symmetric{T}, p::Integer) where T<:Real
|
||||
if p < 0
|
||||
return Symmetric(Base.power_by_squaring(inv(A), -p))
|
||||
else
|
||||
return Symmetric(Base.power_by_squaring(A, p))
|
||||
end
|
||||
end
|
||||
function ^(A::Symmetric{T}, p::Real) where T<:Real
|
||||
F = eigfact(A)
|
||||
if all(λ -> λ ≥ 0, F.values)
|
||||
retmat = (F.vectors * Diagonal((F.values).^p)) * F.vectors'
|
||||
else
|
||||
retmat = (F.vectors * Diagonal((complex(F.values)).^p)) * F.vectors'
|
||||
end
|
||||
return Symmetric(retmat)
|
||||
end
|
||||
function ^(A::Hermitian, p::Integer)
|
||||
n = checksquare(A)
|
||||
if p < 0
|
||||
retmat = Base.power_by_squaring(inv(A), -p)
|
||||
else
|
||||
retmat = Base.power_by_squaring(A, p)
|
||||
end
|
||||
for i = 1:n
|
||||
retmat[i,i] = real(retmat[i,i])
|
||||
end
|
||||
return Hermitian(retmat)
|
||||
end
|
||||
function ^(A::Hermitian{T}, p::Real) where T
|
||||
n = checksquare(A)
|
||||
F = eigfact(A)
|
||||
if all(λ -> λ ≥ 0, F.values)
|
||||
retmat = (F.vectors * Diagonal((F.values).^p)) * F.vectors'
|
||||
if T <: Real
|
||||
return Hermitian(retmat)
|
||||
else
|
||||
for i = 1:n
|
||||
retmat[i,i] = real(retmat[i,i])
|
||||
end
|
||||
return Hermitian(retmat)
|
||||
end
|
||||
else
|
||||
retmat = (F.vectors * Diagonal((complex(F.values).^p))) * F.vectors'
|
||||
return retmat
|
||||
end
|
||||
end
|
||||
|
||||
function expm(A::Symmetric)
|
||||
F = eigfact(A)
|
||||
return Symmetric((F.vectors * Diagonal(exp.(F.values))) * F.vectors')
|
||||
end
|
||||
function expm(A::Hermitian{T}) where T
|
||||
n = checksquare(A)
|
||||
F = eigfact(A)
|
||||
retmat = (F.vectors * Diagonal(exp.(F.values))) * F.vectors'
|
||||
if T <: Real
|
||||
return real(Hermitian(retmat))
|
||||
else
|
||||
for i = 1:n
|
||||
retmat[i,i] = real(retmat[i,i])
|
||||
end
|
||||
return Hermitian(retmat)
|
||||
end
|
||||
end
|
||||
|
||||
for (funm, func) in ([:logm,:log], [:sqrtm,:sqrt])
|
||||
@eval begin
|
||||
function ($funm)(A::Symmetric{T}) where T<:Real
|
||||
F = eigfact(A)
|
||||
if all(λ -> λ ≥ 0, F.values)
|
||||
retmat = (F.vectors * Diagonal(($func).(F.values))) * F.vectors'
|
||||
else
|
||||
retmat = (F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors'
|
||||
end
|
||||
return Symmetric(retmat)
|
||||
end
|
||||
|
||||
function ($funm)(A::Hermitian{T}) where T
|
||||
n = checksquare(A)
|
||||
F = eigfact(A)
|
||||
if all(λ -> λ ≥ 0, F.values)
|
||||
retmat = (F.vectors * Diagonal(($func).(F.values))) * F.vectors'
|
||||
if T <: Real
|
||||
return Hermitian(retmat)
|
||||
else
|
||||
for i = 1:n
|
||||
retmat[i,i] = real(retmat[i,i])
|
||||
end
|
||||
return Hermitian(retmat)
|
||||
end
|
||||
else
|
||||
retmat = (F.vectors * Diagonal(($func).(complex(F.values)))) * F.vectors'
|
||||
return retmat
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,154 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
ctranspose(a::AbstractArray) = error("ctranspose not defined for $(typeof(a)). Consider using `permutedims` for higher-dimensional arrays.")
|
||||
transpose(a::AbstractArray) = error("transpose not defined for $(typeof(a)). Consider using `permutedims` for higher-dimensional arrays.")
|
||||
|
||||
## Matrix transposition ##
|
||||
|
||||
"""
|
||||
transpose!(dest,src)
|
||||
|
||||
Transpose array `src` and store the result in the preallocated array `dest`, which should
|
||||
have a size corresponding to `(size(src,2),size(src,1))`. No in-place transposition is
|
||||
supported and unexpected results will happen if `src` and `dest` have overlapping memory
|
||||
regions.
|
||||
"""
|
||||
transpose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(transpose, B, A)
|
||||
|
||||
"""
|
||||
ctranspose!(dest,src)
|
||||
|
||||
Conjugate transpose array `src` and store the result in the preallocated array `dest`, which
|
||||
should have a size corresponding to `(size(src,2),size(src,1))`. No in-place transposition
|
||||
is supported and unexpected results will happen if `src` and `dest` have overlapping memory
|
||||
regions.
|
||||
"""
|
||||
ctranspose!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(ctranspose, B, A)
|
||||
function transpose!(B::AbstractVector, A::AbstractMatrix)
|
||||
indices(B,1) == indices(A,2) && indices(A,1) == 1:1 || throw(DimensionMismatch("transpose"))
|
||||
copy!(B, A)
|
||||
end
|
||||
function transpose!(B::AbstractMatrix, A::AbstractVector)
|
||||
indices(B,2) == indices(A,1) && indices(B,1) == 1:1 || throw(DimensionMismatch("transpose"))
|
||||
copy!(B, A)
|
||||
end
|
||||
function ctranspose!(B::AbstractVector, A::AbstractMatrix)
|
||||
indices(B,1) == indices(A,2) && indices(A,1) == 1:1 || throw(DimensionMismatch("transpose"))
|
||||
ccopy!(B, A)
|
||||
end
|
||||
function ctranspose!(B::AbstractMatrix, A::AbstractVector)
|
||||
indices(B,2) == indices(A,1) && indices(B,1) == 1:1 || throw(DimensionMismatch("transpose"))
|
||||
ccopy!(B, A)
|
||||
end
|
||||
|
||||
const transposebaselength=64
|
||||
function transpose_f!(f, B::AbstractMatrix, A::AbstractMatrix)
|
||||
inds = indices(A)
|
||||
indices(B,1) == inds[2] && indices(B,2) == inds[1] || throw(DimensionMismatch(string(f)))
|
||||
|
||||
m, n = length(inds[1]), length(inds[2])
|
||||
if m*n<=4*transposebaselength
|
||||
@inbounds begin
|
||||
for j = inds[2]
|
||||
for i = inds[1]
|
||||
B[j,i] = f(A[i,j])
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
transposeblock!(f,B,A,m,n,first(inds[1])-1,first(inds[2])-1)
|
||||
end
|
||||
return B
|
||||
end
|
||||
function transposeblock!(f, B::AbstractMatrix, A::AbstractMatrix, m::Int, n::Int, offseti::Int, offsetj::Int)
|
||||
if m*n<=transposebaselength
|
||||
@inbounds begin
|
||||
for j = offsetj+(1:n)
|
||||
for i = offseti+(1:m)
|
||||
B[j,i] = f(A[i,j])
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif m>n
|
||||
newm=m>>1
|
||||
transposeblock!(f,B,A,newm,n,offseti,offsetj)
|
||||
transposeblock!(f,B,A,m-newm,n,offseti+newm,offsetj)
|
||||
else
|
||||
newn=n>>1
|
||||
transposeblock!(f,B,A,m,newn,offseti,offsetj)
|
||||
transposeblock!(f,B,A,m,n-newn,offseti,offsetj+newn)
|
||||
end
|
||||
return B
|
||||
end
|
||||
|
||||
function ccopy!(B, A)
|
||||
RB, RA = eachindex(B), eachindex(A)
|
||||
if RB == RA
|
||||
for i = RB
|
||||
B[i] = ctranspose(A[i])
|
||||
end
|
||||
else
|
||||
for (i,j) = zip(RB, RA)
|
||||
B[i] = ctranspose(A[j])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
transpose(A::AbstractMatrix)
|
||||
|
||||
The transposition operator (`.'`).
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1 2 3; 4 5 6; 7 8 9]
|
||||
3×3 Array{Int64,2}:
|
||||
1 2 3
|
||||
4 5 6
|
||||
7 8 9
|
||||
|
||||
julia> transpose(A)
|
||||
3×3 Array{Int64,2}:
|
||||
1 4 7
|
||||
2 5 8
|
||||
3 6 9
|
||||
```
|
||||
"""
|
||||
function transpose(A::AbstractMatrix)
|
||||
ind1, ind2 = indices(A)
|
||||
B = similar(A, (ind2, ind1))
|
||||
transpose!(B, A)
|
||||
end
|
||||
function ctranspose(A::AbstractMatrix)
|
||||
ind1, ind2 = indices(A)
|
||||
B = similar(A, (ind2, ind1))
|
||||
ctranspose!(B, A)
|
||||
end
|
||||
|
||||
@inline ctranspose(A::AbstractVector{<:Real}) = transpose(A)
|
||||
@inline ctranspose(A::AbstractMatrix{<:Real}) = transpose(A)
|
||||
|
||||
function copy_transpose!(B::AbstractVecOrMat, ir_dest::Range{Int}, jr_dest::Range{Int},
|
||||
A::AbstractVecOrMat, ir_src::Range{Int}, jr_src::Range{Int})
|
||||
if length(ir_dest) != length(jr_src)
|
||||
throw(ArgumentError(string("source and destination must have same size (got ",
|
||||
length(jr_src)," and ",length(ir_dest),")")))
|
||||
end
|
||||
if length(jr_dest) != length(ir_src)
|
||||
throw(ArgumentError(string("source and destination must have same size (got ",
|
||||
length(ir_src)," and ",length(jr_dest),")")))
|
||||
end
|
||||
@boundscheck checkbounds(B, ir_dest, jr_dest)
|
||||
@boundscheck checkbounds(A, ir_src, jr_src)
|
||||
idest = first(ir_dest)
|
||||
for jsrc in jr_src
|
||||
jdest = first(jr_dest)
|
||||
for isrc in ir_src
|
||||
B[idest,jdest] = A[isrc,jsrc]
|
||||
jdest += step(jr_dest)
|
||||
end
|
||||
idest += step(ir_dest)
|
||||
end
|
||||
return B
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,651 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
#### Specialized matrix types ####
|
||||
|
||||
## (complex) symmetric tridiagonal matrices
|
||||
struct SymTridiagonal{T} <: AbstractMatrix{T}
|
||||
dv::Vector{T} # diagonal
|
||||
ev::Vector{T} # subdiagonal
|
||||
function SymTridiagonal{T}(dv::Vector{T}, ev::Vector{T}) where T
|
||||
if !(length(dv) - 1 <= length(ev) <= length(dv))
|
||||
throw(DimensionMismatch("subdiagonal has wrong length. Has length $(length(ev)), but should be either $(length(dv) - 1) or $(length(dv))."))
|
||||
end
|
||||
new(dv,ev)
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
SymTridiagonal(dv, ev)
|
||||
|
||||
Construct a symmetric tridiagonal matrix from the diagonal and first sub/super-diagonal,
|
||||
respectively. The result is of type `SymTridiagonal` and provides efficient specialized
|
||||
eigensolvers, but may be converted into a regular matrix with
|
||||
[`convert(Array, _)`](@ref) (or `Array(_)` for short).
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> dv = [1; 2; 3; 4]
|
||||
4-element Array{Int64,1}:
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
|
||||
julia> ev = [7; 8; 9]
|
||||
3-element Array{Int64,1}:
|
||||
7
|
||||
8
|
||||
9
|
||||
|
||||
julia> SymTridiagonal(dv, ev)
|
||||
4×4 SymTridiagonal{Int64}:
|
||||
1 7 ⋅ ⋅
|
||||
7 2 8 ⋅
|
||||
⋅ 8 3 9
|
||||
⋅ ⋅ 9 4
|
||||
```
|
||||
"""
|
||||
SymTridiagonal(dv::Vector{T}, ev::Vector{T}) where {T} = SymTridiagonal{T}(dv, ev)
|
||||
|
||||
function SymTridiagonal(dv::AbstractVector{Td}, ev::AbstractVector{Te}) where {Td,Te}
|
||||
T = promote_type(Td,Te)
|
||||
SymTridiagonal(convert(Vector{T}, dv), convert(Vector{T}, ev))
|
||||
end
|
||||
|
||||
function SymTridiagonal(A::AbstractMatrix)
|
||||
if diag(A,1) == diag(A,-1)
|
||||
SymTridiagonal(diag(A), diag(A,1))
|
||||
else
|
||||
throw(ArgumentError("matrix is not symmetric; cannot convert to SymTridiagonal"))
|
||||
end
|
||||
end
|
||||
|
||||
convert(::Type{SymTridiagonal{T}}, S::SymTridiagonal) where {T} =
|
||||
SymTridiagonal(convert(Vector{T}, S.dv), convert(Vector{T}, S.ev))
|
||||
convert(::Type{AbstractMatrix{T}}, S::SymTridiagonal) where {T} =
|
||||
SymTridiagonal(convert(Vector{T}, S.dv), convert(Vector{T}, S.ev))
|
||||
function convert(::Type{Matrix{T}}, M::SymTridiagonal{T}) where T
|
||||
n = size(M, 1)
|
||||
Mf = zeros(T, n, n)
|
||||
@inbounds begin
|
||||
@simd for i = 1:n-1
|
||||
Mf[i,i] = M.dv[i]
|
||||
Mf[i+1,i] = M.ev[i]
|
||||
Mf[i,i+1] = M.ev[i]
|
||||
end
|
||||
Mf[n,n] = M.dv[n]
|
||||
end
|
||||
return Mf
|
||||
end
|
||||
convert(::Type{Matrix}, M::SymTridiagonal{T}) where {T} = convert(Matrix{T}, M)
|
||||
convert(::Type{Array}, M::SymTridiagonal) = convert(Matrix, M)
|
||||
full(M::SymTridiagonal) = convert(Array, M)
|
||||
|
||||
size(A::SymTridiagonal) = (length(A.dv), length(A.dv))
|
||||
function size(A::SymTridiagonal, d::Integer)
|
||||
if d < 1
|
||||
throw(ArgumentError("dimension must be ≥ 1, got $d"))
|
||||
elseif d<=2
|
||||
return length(A.dv)
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
similar(S::SymTridiagonal, ::Type{T}) where {T} = SymTridiagonal{T}(similar(S.dv, T), similar(S.ev, T))
|
||||
|
||||
#Elementary operations
|
||||
broadcast(::typeof(abs), M::SymTridiagonal) = SymTridiagonal(abs.(M.dv), abs.(M.ev))
|
||||
broadcast(::typeof(round), M::SymTridiagonal) = SymTridiagonal(round.(M.dv), round.(M.ev))
|
||||
broadcast(::typeof(trunc), M::SymTridiagonal) = SymTridiagonal(trunc.(M.dv), trunc.(M.ev))
|
||||
broadcast(::typeof(floor), M::SymTridiagonal) = SymTridiagonal(floor.(M.dv), floor.(M.ev))
|
||||
broadcast(::typeof(ceil), M::SymTridiagonal) = SymTridiagonal(ceil.(M.dv), ceil.(M.ev))
|
||||
for func in (:conj, :copy, :real, :imag)
|
||||
@eval ($func)(M::SymTridiagonal) = SymTridiagonal(($func)(M.dv), ($func)(M.ev))
|
||||
end
|
||||
broadcast(::typeof(round), ::Type{T}, M::SymTridiagonal) where {T<:Integer} = SymTridiagonal(round.(T, M.dv), round.(T, M.ev))
|
||||
broadcast(::typeof(trunc), ::Type{T}, M::SymTridiagonal) where {T<:Integer} = SymTridiagonal(trunc.(T, M.dv), trunc.(T, M.ev))
|
||||
broadcast(::typeof(floor), ::Type{T}, M::SymTridiagonal) where {T<:Integer} = SymTridiagonal(floor.(T, M.dv), floor.(T, M.ev))
|
||||
broadcast(::typeof(ceil), ::Type{T}, M::SymTridiagonal) where {T<:Integer} = SymTridiagonal(ceil.(T, M.dv), ceil.(T, M.ev))
|
||||
|
||||
transpose(M::SymTridiagonal) = M #Identity operation
|
||||
ctranspose(M::SymTridiagonal) = conj(M)
|
||||
|
||||
function diag(M::SymTridiagonal{T}, n::Integer=0) where T
|
||||
absn = abs(n)
|
||||
if absn == 0
|
||||
return M.dv
|
||||
elseif absn==1
|
||||
return M.ev
|
||||
elseif absn<size(M,1)
|
||||
return zeros(T,size(M,1)-absn)
|
||||
else
|
||||
throw(ArgumentError("$n-th diagonal of a $(size(M)) matrix doesn't exist!"))
|
||||
end
|
||||
end
|
||||
|
||||
+(A::SymTridiagonal, B::SymTridiagonal) = SymTridiagonal(A.dv+B.dv, A.ev+B.ev)
|
||||
-(A::SymTridiagonal, B::SymTridiagonal) = SymTridiagonal(A.dv-B.dv, A.ev-B.ev)
|
||||
*(A::SymTridiagonal, B::Number) = SymTridiagonal(A.dv*B, A.ev*B)
|
||||
*(B::Number, A::SymTridiagonal) = A*B
|
||||
/(A::SymTridiagonal, B::Number) = SymTridiagonal(A.dv/B, A.ev/B)
|
||||
==(A::SymTridiagonal, B::SymTridiagonal) = (A.dv==B.dv) && (A.ev==B.ev)
|
||||
|
||||
function A_mul_B!(C::StridedVecOrMat, S::SymTridiagonal, B::StridedVecOrMat)
|
||||
m, n = size(B, 1), size(B, 2)
|
||||
if !(m == size(S, 1) == size(C, 1))
|
||||
throw(DimensionMismatch("A has first dimension $(size(S,1)), B has $(size(B,1)), C has $(size(C,1)) but all must match"))
|
||||
end
|
||||
if n != size(C, 2)
|
||||
throw(DimensionMismatch("second dimension of B, $n, doesn't match second dimension of C, $(size(C,2))"))
|
||||
end
|
||||
|
||||
if m == 0
|
||||
return C
|
||||
end
|
||||
|
||||
α = S.dv
|
||||
β = S.ev
|
||||
@inbounds begin
|
||||
for j = 1:n
|
||||
x₊ = B[1, j]
|
||||
x₀ = zero(x₊)
|
||||
# If m == 1 then β[1] is out of bounds
|
||||
β₀ = m > 1 ? zero(β[1]) : zero(eltype(β))
|
||||
for i = 1:m - 1
|
||||
x₋, x₀, x₊ = x₀, x₊, B[i + 1, j]
|
||||
β₋, β₀ = β₀, β[i]
|
||||
C[i, j] = β₋*x₋ + α[i]*x₀ + β₀*x₊
|
||||
end
|
||||
C[m, j] = β₀*x₀ + α[m]*x₊
|
||||
end
|
||||
end
|
||||
|
||||
return C
|
||||
end
|
||||
|
||||
(\)(T::SymTridiagonal, B::StridedVecOrMat) = ldltfact(T)\B
|
||||
|
||||
eigfact!(A::SymTridiagonal{<:BlasReal}) = Eigen(LAPACK.stegr!('V', A.dv, A.ev)...)
|
||||
function eigfact(A::SymTridiagonal{T}) where T
|
||||
S = promote_type(Float32, typeof(zero(T)/norm(one(T))))
|
||||
eigfact!(copy_oftype(A, S))
|
||||
end
|
||||
|
||||
eigfact!(A::SymTridiagonal{<:BlasReal}, irange::UnitRange) =
|
||||
Eigen(LAPACK.stegr!('V', 'I', A.dv, A.ev, 0.0, 0.0, irange.start, irange.stop)...)
|
||||
function eigfact(A::SymTridiagonal{T}, irange::UnitRange) where T
|
||||
S = promote_type(Float32, typeof(zero(T)/norm(one(T))))
|
||||
return eigfact!(copy_oftype(A, S), irange)
|
||||
end
|
||||
|
||||
eigfact!(A::SymTridiagonal{<:BlasReal}, vl::Real, vu::Real) =
|
||||
Eigen(LAPACK.stegr!('V', 'V', A.dv, A.ev, vl, vu, 0, 0)...)
|
||||
function eigfact(A::SymTridiagonal{T}, vl::Real, vu::Real) where T
|
||||
S = promote_type(Float32, typeof(zero(T)/norm(one(T))))
|
||||
return eigfact!(copy_oftype(A, S), vl, vu)
|
||||
end
|
||||
|
||||
eigvals!(A::SymTridiagonal{<:BlasReal}) = LAPACK.stev!('N', A.dv, A.ev)[1]
|
||||
function eigvals(A::SymTridiagonal{T}) where T
|
||||
S = promote_type(Float32, typeof(zero(T)/norm(one(T))))
|
||||
return eigvals!(copy_oftype(A, S))
|
||||
end
|
||||
|
||||
eigvals!(A::SymTridiagonal{<:BlasReal}, irange::UnitRange) =
|
||||
LAPACK.stegr!('N', 'I', A.dv, A.ev, 0.0, 0.0, irange.start, irange.stop)[1]
|
||||
function eigvals(A::SymTridiagonal{T}, irange::UnitRange) where T
|
||||
S = promote_type(Float32, typeof(zero(T)/norm(one(T))))
|
||||
return eigvals!(copy_oftype(A, S), irange)
|
||||
end
|
||||
|
||||
eigvals!(A::SymTridiagonal{<:BlasReal}, vl::Real, vu::Real) =
|
||||
LAPACK.stegr!('N', 'V', A.dv, A.ev, vl, vu, 0, 0)[1]
|
||||
function eigvals(A::SymTridiagonal{T}, vl::Real, vu::Real) where T
|
||||
S = promote_type(Float32, typeof(zero(T)/norm(one(T))))
|
||||
return eigvals!(copy_oftype(A, S), vl, vu)
|
||||
end
|
||||
|
||||
#Computes largest and smallest eigenvalue
|
||||
eigmax(A::SymTridiagonal) = eigvals(A, size(A, 1):size(A, 1))[1]
|
||||
eigmin(A::SymTridiagonal) = eigvals(A, 1:1)[1]
|
||||
|
||||
#Compute selected eigenvectors only corresponding to particular eigenvalues
|
||||
eigvecs(A::SymTridiagonal) = eigfact(A)[:vectors]
|
||||
|
||||
"""
|
||||
eigvecs(A::SymTridiagonal[, eigvals]) -> Matrix
|
||||
|
||||
Returns a matrix `M` whose columns are the eigenvectors of `A`. (The `k`th eigenvector can
|
||||
be obtained from the slice `M[:, k]`.)
|
||||
|
||||
If the optional vector of eigenvalues `eigvals` is specified, `eigvecs`
|
||||
returns the specific corresponding eigenvectors.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = SymTridiagonal([1.; 2.; 1.], [2.; 3.])
|
||||
3×3 SymTridiagonal{Float64}:
|
||||
1.0 2.0 ⋅
|
||||
2.0 2.0 3.0
|
||||
⋅ 3.0 1.0
|
||||
|
||||
julia> eigvals(A)
|
||||
3-element Array{Float64,1}:
|
||||
-2.14005
|
||||
1.0
|
||||
5.14005
|
||||
|
||||
julia> eigvecs(A)
|
||||
3×3 Array{Float64,2}:
|
||||
0.418304 -0.83205 0.364299
|
||||
-0.656749 -7.39009e-16 0.754109
|
||||
0.627457 0.5547 0.546448
|
||||
|
||||
julia> eigvecs(A, [1.])
|
||||
3×1 Array{Float64,2}:
|
||||
0.83205
|
||||
4.26351e-17
|
||||
-0.5547
|
||||
```
|
||||
"""
|
||||
eigvecs(A::SymTridiagonal{<:BlasFloat}, eigvals::Vector{<:Real}) = LAPACK.stein!(A.dv, A.ev, eigvals)
|
||||
|
||||
#tril and triu
|
||||
|
||||
istriu(M::SymTridiagonal) = iszero(M.ev)
|
||||
istril(M::SymTridiagonal) = iszero(M.ev)
|
||||
|
||||
function tril!(M::SymTridiagonal, k::Integer=0)
|
||||
n = length(M.dv)
|
||||
if abs(k) > n
|
||||
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($n,$n)"))
|
||||
elseif k < -1
|
||||
fill!(M.ev,0)
|
||||
fill!(M.dv,0)
|
||||
return Tridiagonal(M.ev,M.dv,copy(M.ev))
|
||||
elseif k == -1
|
||||
fill!(M.dv,0)
|
||||
return Tridiagonal(M.ev,M.dv,zeros(M.ev))
|
||||
elseif k == 0
|
||||
return Tridiagonal(M.ev,M.dv,zeros(M.ev))
|
||||
elseif k >= 1
|
||||
return Tridiagonal(M.ev,M.dv,copy(M.ev))
|
||||
end
|
||||
end
|
||||
|
||||
function triu!(M::SymTridiagonal, k::Integer=0)
|
||||
n = length(M.dv)
|
||||
if abs(k) > n
|
||||
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($n,$n)"))
|
||||
elseif k > 1
|
||||
fill!(M.ev,0)
|
||||
fill!(M.dv,0)
|
||||
return Tridiagonal(M.ev,M.dv,copy(M.ev))
|
||||
elseif k == 1
|
||||
fill!(M.dv,0)
|
||||
return Tridiagonal(zeros(M.ev),M.dv,M.ev)
|
||||
elseif k == 0
|
||||
return Tridiagonal(zeros(M.ev),M.dv,M.ev)
|
||||
elseif k <= -1
|
||||
return Tridiagonal(M.ev,M.dv,copy(M.ev))
|
||||
end
|
||||
end
|
||||
|
||||
###################
|
||||
# Generic methods #
|
||||
###################
|
||||
|
||||
#Needed for inv_usmani()
|
||||
mutable struct ZeroOffsetVector
|
||||
data::Vector
|
||||
end
|
||||
getindex(a::ZeroOffsetVector, i) = a.data[i+1]
|
||||
setindex!(a::ZeroOffsetVector, x, i) = a.data[i+1]=x
|
||||
|
||||
|
||||
## structured matrix methods ##
|
||||
function Base.replace_in_print_matrix(A::SymTridiagonal, i::Integer, j::Integer, s::AbstractString)
|
||||
i==j-1||i==j||i==j+1 ? s : Base.replace_with_centered_mark(s)
|
||||
end
|
||||
|
||||
#Implements the inverse using the recurrence relation between principal minors
|
||||
# a, b, c are assumed to be the subdiagonal, diagonal, and superdiagonal of
|
||||
# a tridiagonal matrix.
|
||||
#Reference:
|
||||
# R. Usmani, "Inversion of a tridiagonal Jacobi matrix",
|
||||
# Linear Algebra and its Applications 212-213 (1994), pp.413-414
|
||||
# doi:10.1016/0024-3795(94)90414-6
|
||||
function inv_usmani(a::Vector{T}, b::Vector{T}, c::Vector{T}) where T
|
||||
n = length(b)
|
||||
θ = ZeroOffsetVector(zeros(T, n+1)) #principal minors of A
|
||||
θ[0] = 1
|
||||
n>=1 && (θ[1] = b[1])
|
||||
for i=2:n
|
||||
θ[i] = b[i]*θ[i-1]-a[i-1]*c[i-1]*θ[i-2]
|
||||
end
|
||||
φ = zeros(T, n+1)
|
||||
φ[n+1] = 1
|
||||
n>=1 && (φ[n] = b[n])
|
||||
for i=n-1:-1:1
|
||||
φ[i] = b[i]*φ[i+1]-a[i]*c[i]*φ[i+2]
|
||||
end
|
||||
α = Matrix{T}(n, n)
|
||||
for i=1:n, j=1:n
|
||||
sign = (i+j)%2==0 ? (+) : (-)
|
||||
if i<j
|
||||
α[i,j]=(sign)(prod(c[i:j-1]))*θ[i-1]*φ[j+1]/θ[n]
|
||||
elseif i==j
|
||||
α[i,i]= θ[i-1]*φ[i+1]/θ[n]
|
||||
else #i>j
|
||||
α[i,j]=(sign)(prod(a[j:i-1]))*θ[j-1]*φ[i+1]/θ[n]
|
||||
end
|
||||
end
|
||||
α
|
||||
end
|
||||
|
||||
#Implements the determinant using principal minors
|
||||
#Inputs and reference are as above for inv_usmani()
|
||||
function det_usmani(a::Vector{T}, b::Vector{T}, c::Vector{T}) where T
|
||||
n = length(b)
|
||||
θa = one(T)
|
||||
if n == 0
|
||||
return θa
|
||||
end
|
||||
θb = b[1]
|
||||
for i=2:n
|
||||
θb, θa = b[i]*θb-a[i-1]*c[i-1]*θa, θb
|
||||
end
|
||||
return θb
|
||||
end
|
||||
|
||||
inv(A::SymTridiagonal) = inv_usmani(A.ev, A.dv, A.ev)
|
||||
det(A::SymTridiagonal) = det_usmani(A.ev, A.dv, A.ev)
|
||||
|
||||
function getindex(A::SymTridiagonal{T}, i::Integer, j::Integer) where T
|
||||
if !(1 <= i <= size(A,2) && 1 <= j <= size(A,2))
|
||||
throw(BoundsError(A, (i,j)))
|
||||
end
|
||||
if i == j
|
||||
return A.dv[i]
|
||||
elseif i == j + 1
|
||||
return A.ev[j]
|
||||
elseif i + 1 == j
|
||||
return A.ev[i]
|
||||
else
|
||||
return zero(T)
|
||||
end
|
||||
end
|
||||
|
||||
function setindex!(A::SymTridiagonal, x, i::Integer, j::Integer)
|
||||
@boundscheck checkbounds(A, i, j)
|
||||
if i == j
|
||||
@inbounds A.dv[i] = x
|
||||
else
|
||||
throw(ArgumentError("cannot set off-diagonal entry ($i, $j)"))
|
||||
end
|
||||
return x
|
||||
end
|
||||
|
||||
## Tridiagonal matrices ##
|
||||
struct Tridiagonal{T} <: AbstractMatrix{T}
|
||||
dl::Vector{T} # sub-diagonal
|
||||
d::Vector{T} # diagonal
|
||||
du::Vector{T} # sup-diagonal
|
||||
du2::Vector{T} # supsup-diagonal for pivoting
|
||||
end
|
||||
|
||||
"""
|
||||
Tridiagonal(dl, d, du)
|
||||
|
||||
Construct a tridiagonal matrix from the first subdiagonal, diagonal, and first superdiagonal,
|
||||
respectively. The result is of type `Tridiagonal` and provides efficient specialized linear
|
||||
solvers, but may be converted into a regular matrix with
|
||||
[`convert(Array, _)`](@ref) (or `Array(_)` for short).
|
||||
The lengths of `dl` and `du` must be one less than the length of `d`.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> dl = [1; 2; 3]
|
||||
3-element Array{Int64,1}:
|
||||
1
|
||||
2
|
||||
3
|
||||
|
||||
julia> du = [4; 5; 6]
|
||||
3-element Array{Int64,1}:
|
||||
4
|
||||
5
|
||||
6
|
||||
|
||||
julia> d = [7; 8; 9; 0]
|
||||
4-element Array{Int64,1}:
|
||||
7
|
||||
8
|
||||
9
|
||||
0
|
||||
|
||||
julia> Tridiagonal(dl, d, du)
|
||||
4×4 Tridiagonal{Int64}:
|
||||
7 4 ⋅ ⋅
|
||||
1 8 5 ⋅
|
||||
⋅ 2 9 6
|
||||
⋅ ⋅ 3 0
|
||||
```
|
||||
"""
|
||||
# Basic constructor takes in three dense vectors of same type
|
||||
function Tridiagonal(dl::Vector{T}, d::Vector{T}, du::Vector{T}) where T
|
||||
n = length(d)
|
||||
if (length(dl) != n-1 || length(du) != n-1)
|
||||
throw(ArgumentError("cannot make Tridiagonal from incompatible lengths of subdiagonal, diagonal and superdiagonal: ($(length(dl)), $(length(d)), $(length(du))"))
|
||||
end
|
||||
Tridiagonal(dl, d, du, zeros(T,n-2))
|
||||
end
|
||||
|
||||
# Construct from diagonals of any abstract vector, any eltype
|
||||
function Tridiagonal(dl::AbstractVector{Tl}, d::AbstractVector{Td}, du::AbstractVector{Tu}) where {Tl,Td,Tu}
|
||||
Tridiagonal(map(v->convert(Vector{promote_type(Tl,Td,Tu)}, v), (dl, d, du))...)
|
||||
end
|
||||
|
||||
# Provide a constructor Tridiagonal(A) similar to the triangulars, diagonal, symmetric
|
||||
"""
|
||||
Tridiagonal(A)
|
||||
|
||||
returns a `Tridiagonal` array based on (abstract) matrix `A`, using its first lower diagonal,
|
||||
main diagonal, and first upper diagonal.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> A = [1 2 3 4; 1 2 3 4; 1 2 3 4; 1 2 3 4]
|
||||
4×4 Array{Int64,2}:
|
||||
1 2 3 4
|
||||
1 2 3 4
|
||||
1 2 3 4
|
||||
1 2 3 4
|
||||
|
||||
julia> Tridiagonal(A)
|
||||
4×4 Tridiagonal{Int64}:
|
||||
1 2 ⋅ ⋅
|
||||
1 2 3 ⋅
|
||||
⋅ 2 3 4
|
||||
⋅ ⋅ 3 4
|
||||
```
|
||||
"""
|
||||
function Tridiagonal(A::AbstractMatrix)
|
||||
return Tridiagonal(diag(A,-1), diag(A), diag(A,+1))
|
||||
end
|
||||
|
||||
size(M::Tridiagonal) = (length(M.d), length(M.d))
|
||||
function size(M::Tridiagonal, d::Integer)
|
||||
if d < 1
|
||||
throw(ArgumentError("dimension d must be ≥ 1, got $d"))
|
||||
elseif d <= 2
|
||||
return length(M.d)
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
function convert(::Type{Matrix{T}}, M::Tridiagonal{T}) where T
|
||||
A = zeros(T, size(M))
|
||||
for i = 1:length(M.d)
|
||||
A[i,i] = M.d[i]
|
||||
end
|
||||
for i = 1:length(M.d)-1
|
||||
A[i+1,i] = M.dl[i]
|
||||
A[i,i+1] = M.du[i]
|
||||
end
|
||||
A
|
||||
end
|
||||
convert(::Type{Matrix}, M::Tridiagonal{T}) where {T} = convert(Matrix{T}, M)
|
||||
convert(::Type{Array}, M::Tridiagonal) = convert(Matrix, M)
|
||||
full(M::Tridiagonal) = convert(Array, M)
|
||||
function similar(M::Tridiagonal, ::Type{T}) where T
|
||||
Tridiagonal{T}(similar(M.dl, T), similar(M.d, T), similar(M.du, T), similar(M.du2, T))
|
||||
end
|
||||
|
||||
# Operations on Tridiagonal matrices
|
||||
copy!(dest::Tridiagonal, src::Tridiagonal) = Tridiagonal(copy!(dest.dl, src.dl), copy!(dest.d, src.d), copy!(dest.du, src.du), copy!(dest.du2, src.du2))
|
||||
|
||||
#Elementary operations
|
||||
broadcast(::typeof(abs), M::Tridiagonal) = Tridiagonal(abs.(M.dl), abs.(M.d), abs.(M.du), abs.(M.du2))
|
||||
broadcast(::typeof(round), M::Tridiagonal) = Tridiagonal(round.(M.dl), round.(M.d), round.(M.du), round.(M.du2))
|
||||
broadcast(::typeof(trunc), M::Tridiagonal) = Tridiagonal(trunc.(M.dl), trunc.(M.d), trunc.(M.du), trunc.(M.du2))
|
||||
broadcast(::typeof(floor), M::Tridiagonal) = Tridiagonal(floor.(M.dl), floor.(M.d), floor.(M.du), floor.(M.du2))
|
||||
broadcast(::typeof(ceil), M::Tridiagonal) = Tridiagonal(ceil.(M.dl), ceil.(M.d), ceil.(M.du), ceil.(M.du2))
|
||||
for func in (:conj, :copy, :real, :imag)
|
||||
@eval function ($func)(M::Tridiagonal)
|
||||
Tridiagonal(($func)(M.dl), ($func)(M.d), ($func)(M.du), ($func)(M.du2))
|
||||
end
|
||||
end
|
||||
broadcast(::typeof(round), ::Type{T}, M::Tridiagonal) where {T<:Integer} =
|
||||
Tridiagonal(round.(T, M.dl), round.(T, M.d), round.(T, M.du), round.(T, M.du2))
|
||||
broadcast(::typeof(trunc), ::Type{T}, M::Tridiagonal) where {T<:Integer} =
|
||||
Tridiagonal(trunc.(T, M.dl), trunc.(T, M.d), trunc.(T, M.du), trunc.(T, M.du2))
|
||||
broadcast(::typeof(floor), ::Type{T}, M::Tridiagonal) where {T<:Integer} =
|
||||
Tridiagonal(floor.(T, M.dl), floor.(T, M.d), floor.(T, M.du), floor.(T, M.du2))
|
||||
broadcast(::typeof(ceil), ::Type{T}, M::Tridiagonal) where {T<:Integer} =
|
||||
Tridiagonal(ceil.(T, M.dl), ceil.(T, M.d), ceil.(T, M.du), ceil.(T, M.du2))
|
||||
|
||||
transpose(M::Tridiagonal) = Tridiagonal(M.du, M.d, M.dl)
|
||||
ctranspose(M::Tridiagonal) = conj(transpose(M))
|
||||
|
||||
function diag(M::Tridiagonal{T}, n::Integer=0) where T
|
||||
if n == 0
|
||||
return M.d
|
||||
elseif n == -1
|
||||
return M.dl
|
||||
elseif n == 1
|
||||
return M.du
|
||||
elseif abs(n) < size(M,1)
|
||||
return zeros(T,size(M,1)-abs(n))
|
||||
else
|
||||
throw(ArgumentError("$n-th diagonal of a $(size(M)) matrix doesn't exist!"))
|
||||
end
|
||||
end
|
||||
|
||||
function getindex(A::Tridiagonal{T}, i::Integer, j::Integer) where T
|
||||
if !(1 <= i <= size(A,2) && 1 <= j <= size(A,2))
|
||||
throw(BoundsError(A, (i,j)))
|
||||
end
|
||||
if i == j
|
||||
return A.d[i]
|
||||
elseif i == j + 1
|
||||
return A.dl[j]
|
||||
elseif i + 1 == j
|
||||
return A.du[i]
|
||||
else
|
||||
return zero(T)
|
||||
end
|
||||
end
|
||||
|
||||
function setindex!(A::Tridiagonal, x, i::Integer, j::Integer)
|
||||
@boundscheck checkbounds(A, i, j)
|
||||
if i == j
|
||||
@inbounds A.d[i] = x
|
||||
elseif i - j == 1
|
||||
@inbounds A.dl[j] = x
|
||||
elseif j - i == 1
|
||||
@inbounds A.du[i] = x
|
||||
elseif !iszero(x)
|
||||
throw(ArgumentError(string("cannot set entry ($i, $j) off ",
|
||||
"the tridiagonal band to a nonzero value ($x)")))
|
||||
end
|
||||
return x
|
||||
end
|
||||
|
||||
## structured matrix methods ##
|
||||
function Base.replace_in_print_matrix(A::Tridiagonal,i::Integer,j::Integer,s::AbstractString)
|
||||
i==j-1||i==j||i==j+1 ? s : Base.replace_with_centered_mark(s)
|
||||
end
|
||||
|
||||
#tril and triu
|
||||
|
||||
istriu(M::Tridiagonal) = iszero(M.dl)
|
||||
istril(M::Tridiagonal) = iszero(M.du)
|
||||
|
||||
function tril!(M::Tridiagonal, k::Integer=0)
|
||||
n = length(M.d)
|
||||
if abs(k) > n
|
||||
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($n,$n)"))
|
||||
elseif k < -1
|
||||
fill!(M.dl,0)
|
||||
fill!(M.d,0)
|
||||
fill!(M.du,0)
|
||||
elseif k == -1
|
||||
fill!(M.d,0)
|
||||
fill!(M.du,0)
|
||||
elseif k == 0
|
||||
fill!(M.du,0)
|
||||
end
|
||||
return M
|
||||
end
|
||||
|
||||
function triu!(M::Tridiagonal, k::Integer=0)
|
||||
n = length(M.d)
|
||||
if abs(k) > n
|
||||
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($n,$n)"))
|
||||
elseif k > 1
|
||||
fill!(M.dl,0)
|
||||
fill!(M.d,0)
|
||||
fill!(M.du,0)
|
||||
elseif k == 1
|
||||
fill!(M.dl,0)
|
||||
fill!(M.d,0)
|
||||
elseif k == 0
|
||||
fill!(M.dl,0)
|
||||
end
|
||||
return M
|
||||
end
|
||||
|
||||
###################
|
||||
# Generic methods #
|
||||
###################
|
||||
|
||||
+(A::Tridiagonal, B::Tridiagonal) = Tridiagonal(A.dl+B.dl, A.d+B.d, A.du+B.du)
|
||||
-(A::Tridiagonal, B::Tridiagonal) = Tridiagonal(A.dl-B.dl, A.d-B.d, A.du-B.du)
|
||||
*(A::Tridiagonal, B::Number) = Tridiagonal(A.dl*B, A.d*B, A.du*B)
|
||||
*(B::Number, A::Tridiagonal) = A*B
|
||||
/(A::Tridiagonal, B::Number) = Tridiagonal(A.dl/B, A.d/B, A.du/B)
|
||||
|
||||
==(A::Tridiagonal, B::Tridiagonal) = (A.dl==B.dl) && (A.d==B.d) && (A.du==B.du)
|
||||
==(A::Tridiagonal, B::SymTridiagonal) = (A.dl==A.du==B.ev) && (A.d==B.dv)
|
||||
==(A::SymTridiagonal, B::Tridiagonal) = (B.dl==B.du==A.ev) && (B.d==A.dv)
|
||||
|
||||
inv(A::Tridiagonal) = inv_usmani(A.dl, A.d, A.du)
|
||||
det(A::Tridiagonal) = det_usmani(A.dl, A.d, A.du)
|
||||
|
||||
convert(::Type{Tridiagonal{T}},M::Tridiagonal) where {T} = Tridiagonal(convert(Vector{T}, M.dl), convert(Vector{T}, M.d), convert(Vector{T}, M.du), convert(Vector{T}, M.du2))
|
||||
convert(::Type{AbstractMatrix{T}},M::Tridiagonal) where {T} = convert(Tridiagonal{T}, M)
|
||||
convert(::Type{Tridiagonal{T}}, M::SymTridiagonal{T}) where {T} = Tridiagonal(M)
|
||||
function convert(::Type{SymTridiagonal{T}}, M::Tridiagonal) where T
|
||||
if M.dl == M.du
|
||||
return SymTridiagonal(convert(Vector{T},M.d), convert(Vector{T},M.dl))
|
||||
else
|
||||
throw(ArgumentError("Tridiagonal is not symmetric, cannot convert to SymTridiagonal"))
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,283 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
import Base: copy, ctranspose, getindex, show, transpose, one, zero, inv,
|
||||
hcat, vcat, hvcat
|
||||
import Base.LinAlg: SingularException
|
||||
|
||||
struct UniformScaling{T<:Number}
|
||||
λ::T
|
||||
end
|
||||
|
||||
"""
|
||||
I
|
||||
|
||||
An object of type `UniformScaling`, representing an identity matrix of any size.
|
||||
|
||||
# Example
|
||||
|
||||
```jldoctest
|
||||
julia> ones(5, 6) * I == ones(5, 6)
|
||||
true
|
||||
|
||||
julia> [1 2im 3; 1im 2 3] * I
|
||||
2×3 Array{Complex{Int64},2}:
|
||||
1+0im 0+2im 3+0im
|
||||
0+1im 2+0im 3+0im
|
||||
```
|
||||
"""
|
||||
const I = UniformScaling(1)
|
||||
|
||||
eltype(::Type{UniformScaling{T}}) where {T} = T
|
||||
ndims(J::UniformScaling) = 2
|
||||
getindex(J::UniformScaling, i::Integer,j::Integer) = ifelse(i==j,J.λ,zero(J.λ))
|
||||
|
||||
function show(io::IO, J::UniformScaling)
|
||||
s = "$(J.λ)"
|
||||
if ismatch(r"\w+\s*[\+\-]\s*\w+", s)
|
||||
s = "($s)"
|
||||
end
|
||||
print(io, "$(typeof(J))\n$s*I")
|
||||
end
|
||||
copy(J::UniformScaling) = UniformScaling(J.λ)
|
||||
|
||||
transpose(J::UniformScaling) = J
|
||||
ctranspose(J::UniformScaling) = UniformScaling(conj(J.λ))
|
||||
|
||||
one(::Type{UniformScaling{T}}) where {T} = UniformScaling(one(T))
|
||||
one(J::UniformScaling{T}) where {T} = one(UniformScaling{T})
|
||||
oneunit(::Type{UniformScaling{T}}) where {T} = UniformScaling(oneunit(T))
|
||||
oneunit(J::UniformScaling{T}) where {T} = oneunit(UniformScaling{T})
|
||||
zero(::Type{UniformScaling{T}}) where {T} = UniformScaling(zero(T))
|
||||
zero(J::UniformScaling{T}) where {T} = zero(UniformScaling{T})
|
||||
|
||||
istriu(::UniformScaling) = true
|
||||
istril(::UniformScaling) = true
|
||||
issymmetric(::UniformScaling) = true
|
||||
ishermitian(J::UniformScaling) = isreal(J.λ)
|
||||
|
||||
(+)(J1::UniformScaling, J2::UniformScaling) = UniformScaling(J1.λ+J2.λ)
|
||||
(+)(B::BitArray{2}, J::UniformScaling) = Array(B) + J
|
||||
(+)(J::UniformScaling, B::BitArray{2}) = J + Array(B)
|
||||
(+)(J::UniformScaling, A::AbstractMatrix) = A + J
|
||||
|
||||
(-)(J::UniformScaling) = UniformScaling(-J.λ)
|
||||
(-)(J1::UniformScaling, J2::UniformScaling) = UniformScaling(J1.λ-J2.λ)
|
||||
(-)(B::BitArray{2}, J::UniformScaling) = Array(B) - J
|
||||
(-)(J::UniformScaling, B::BitArray{2}) = J - Array(B)
|
||||
|
||||
for (t1, t2) in ((:UnitUpperTriangular, :UpperTriangular),
|
||||
(:UnitLowerTriangular, :LowerTriangular))
|
||||
for op in (:+,:-)
|
||||
@eval begin
|
||||
($op)(UL::$t2, J::UniformScaling) = ($t2)(($op)(UL.data, J))
|
||||
|
||||
function ($op)(UL::$t1, J::UniformScaling)
|
||||
ULnew = copy_oftype(UL.data, promote_type(eltype(UL), eltype(J)))
|
||||
for i = 1:size(ULnew, 1)
|
||||
ULnew[i,i] = ($op)(1, J.λ)
|
||||
end
|
||||
return ($t2)(ULnew)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function (-)(J::UniformScaling, UL::Union{UpperTriangular,UnitUpperTriangular})
|
||||
ULnew = similar(full(UL), promote_type(eltype(J), eltype(UL)))
|
||||
n = size(ULnew, 1)
|
||||
ULold = UL.data
|
||||
for j = 1:n
|
||||
for i = 1:j - 1
|
||||
ULnew[i,j] = -ULold[i,j]
|
||||
end
|
||||
if isa(UL, UnitUpperTriangular)
|
||||
ULnew[j,j] = J.λ - 1
|
||||
else
|
||||
ULnew[j,j] = J.λ - ULold[j,j]
|
||||
end
|
||||
end
|
||||
return UpperTriangular(ULnew)
|
||||
end
|
||||
function (-)(J::UniformScaling, UL::Union{LowerTriangular,UnitLowerTriangular})
|
||||
ULnew = similar(full(UL), promote_type(eltype(J), eltype(UL)))
|
||||
n = size(ULnew, 1)
|
||||
ULold = UL.data
|
||||
for j = 1:n
|
||||
if isa(UL, UnitLowerTriangular)
|
||||
ULnew[j,j] = J.λ - 1
|
||||
else
|
||||
ULnew[j,j] = J.λ - ULold[j,j]
|
||||
end
|
||||
for i = j + 1:n
|
||||
ULnew[i,j] = -ULold[i,j]
|
||||
end
|
||||
end
|
||||
return LowerTriangular(ULnew)
|
||||
end
|
||||
|
||||
function (+)(A::AbstractMatrix{TA}, J::UniformScaling{TJ}) where {TA,TJ}
|
||||
n = checksquare(A)
|
||||
B = similar(A, promote_type(TA,TJ))
|
||||
copy!(B,A)
|
||||
@inbounds for i = 1:n
|
||||
B[i,i] += J.λ
|
||||
end
|
||||
B
|
||||
end
|
||||
|
||||
function (-)(A::AbstractMatrix{TA}, J::UniformScaling{TJ}) where {TA,TJ<:Number}
|
||||
n = checksquare(A)
|
||||
B = similar(A, promote_type(TA,TJ))
|
||||
copy!(B, A)
|
||||
@inbounds for i = 1:n
|
||||
B[i,i] -= J.λ
|
||||
end
|
||||
B
|
||||
end
|
||||
function (-)(J::UniformScaling{TJ}, A::AbstractMatrix{TA}) where {TA,TJ<:Number}
|
||||
n = checksquare(A)
|
||||
B = convert(AbstractMatrix{promote_type(TJ,TA)}, -A)
|
||||
@inbounds for j = 1:n
|
||||
B[j,j] += J.λ
|
||||
end
|
||||
B
|
||||
end
|
||||
|
||||
inv(J::UniformScaling) = UniformScaling(inv(J.λ))
|
||||
|
||||
*(J1::UniformScaling, J2::UniformScaling) = UniformScaling(J1.λ*J2.λ)
|
||||
*(B::BitArray{2}, J::UniformScaling) = *(Array(B), J::UniformScaling)
|
||||
*(J::UniformScaling, B::BitArray{2}) = *(J::UniformScaling, Array(B))
|
||||
*(A::AbstractMatrix, J::UniformScaling) = A*J.λ
|
||||
*(J::UniformScaling, A::AbstractVecOrMat) = J.λ*A
|
||||
*(x::Number, J::UniformScaling) = UniformScaling(x*J.λ)
|
||||
*(J::UniformScaling, x::Number) = UniformScaling(J.λ*x)
|
||||
|
||||
/(J1::UniformScaling, J2::UniformScaling) = J2.λ == 0 ? throw(SingularException(1)) : UniformScaling(J1.λ/J2.λ)
|
||||
/(J::UniformScaling, A::AbstractMatrix) = scale!(J.λ, inv(A))
|
||||
/(A::AbstractMatrix, J::UniformScaling) = J.λ == 0 ? throw(SingularException(1)) : A/J.λ
|
||||
|
||||
/(J::UniformScaling, x::Number) = UniformScaling(J.λ/x)
|
||||
|
||||
\(J1::UniformScaling, J2::UniformScaling) = J1.λ == 0 ? throw(SingularException(1)) : UniformScaling(J1.λ\J2.λ)
|
||||
\(A::Union{Bidiagonal{T},AbstractTriangular{T}}, J::UniformScaling) where {T<:Number} = scale!(inv(A), J.λ)
|
||||
\(J::UniformScaling, A::AbstractVecOrMat) = J.λ == 0 ? throw(SingularException(1)) : J.λ\A
|
||||
\(A::AbstractMatrix, J::UniformScaling) = scale!(inv(A), J.λ)
|
||||
|
||||
\(x::Number, J::UniformScaling) = UniformScaling(x\J.λ)
|
||||
|
||||
broadcast(::typeof(*), x::Number,J::UniformScaling) = UniformScaling(x*J.λ)
|
||||
broadcast(::typeof(*), J::UniformScaling,x::Number) = UniformScaling(J.λ*x)
|
||||
|
||||
broadcast(::typeof(/), J::UniformScaling,x::Number) = UniformScaling(J.λ/x)
|
||||
|
||||
==(J1::UniformScaling,J2::UniformScaling) = (J1.λ == J2.λ)
|
||||
|
||||
function isapprox(J1::UniformScaling{T}, J2::UniformScaling{S};
|
||||
rtol::Real=Base.rtoldefault(T,S), atol::Real=0, nans::Bool=false) where {T<:Number,S<:Number}
|
||||
isapprox(J1.λ, J2.λ, rtol=rtol, atol=atol, nans=nans)
|
||||
end
|
||||
|
||||
function copy!(A::AbstractMatrix, J::UniformScaling)
|
||||
size(A,1)==size(A,2) || throw(DimensionMismatch("a UniformScaling can only be copied to a square matrix"))
|
||||
fill!(A, 0)
|
||||
λ = J.λ
|
||||
for i = 1:size(A,1)
|
||||
@inbounds A[i,i] = λ
|
||||
end
|
||||
return A
|
||||
end
|
||||
|
||||
function cond(J::UniformScaling{T}) where T
|
||||
onereal = inv(one(real(J.λ)))
|
||||
return J.λ ≠ zero(T) ? onereal : oftype(onereal, Inf)
|
||||
end
|
||||
|
||||
# promote_to_arrays(n,k, T, A...) promotes any UniformScaling matrices
|
||||
# in A to matrices of type T and sizes given by n[k:end]. n is an array
|
||||
# so that the same promotion code can be used for hvcat. We pass the type T
|
||||
# so that we can re-use this code for sparse-matrix hcat etcetera.
|
||||
promote_to_arrays_(n::Int, ::Type{Matrix}, J::UniformScaling{T}) where {T} = copy!(Matrix{T}(n,n), J)
|
||||
promote_to_arrays_(n::Int, ::Type, A::AbstractVecOrMat) = A
|
||||
promote_to_arrays(n,k, ::Type) = ()
|
||||
promote_to_arrays(n,k, ::Type{T}, A) where {T} = (promote_to_arrays_(n[k], T, A),)
|
||||
promote_to_arrays(n,k, ::Type{T}, A, B) where {T} =
|
||||
(promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B))
|
||||
promote_to_arrays(n,k, ::Type{T}, A, B, C) where {T} =
|
||||
(promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays_(n[k+2], T, C))
|
||||
promote_to_arrays(n,k, ::Type{T}, A, B, Cs...) where {T} =
|
||||
(promote_to_arrays_(n[k], T, A), promote_to_arrays_(n[k+1], T, B), promote_to_arrays(n,k+2, T, Cs...)...)
|
||||
promote_to_array_type(A::Tuple{Vararg{Union{AbstractVecOrMat,UniformScaling}}}) = Matrix
|
||||
|
||||
for (f,dim,name) in ((:hcat,1,"rows"), (:vcat,2,"cols"))
|
||||
@eval begin
|
||||
function $f(A::Union{AbstractVecOrMat,UniformScaling}...)
|
||||
n = 0
|
||||
for a in A
|
||||
if !isa(a, UniformScaling)
|
||||
na = size(a,$dim)
|
||||
n > 0 && n != na &&
|
||||
throw(DimensionMismatch(string("number of ", $name,
|
||||
" of each array must match (got ", n, " and ", na, ")")))
|
||||
n = na
|
||||
end
|
||||
end
|
||||
n == 0 && throw(ArgumentError($("$f of only UniformScaling objects cannot determine the matrix size")))
|
||||
return $f(promote_to_arrays(fill(n,length(A)),1, promote_to_array_type(A), A...)...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function hvcat(rows::Tuple{Vararg{Int}}, A::Union{AbstractVecOrMat,UniformScaling}...)
|
||||
nr = length(rows)
|
||||
sum(rows) == length(A) || throw(ArgumentError("mismatch between row sizes and number of arguments"))
|
||||
n = zeros(Int, length(A))
|
||||
needcols = false # whether we also need to infer some sizes from the column count
|
||||
j = 0
|
||||
for i = 1:nr # infer UniformScaling sizes from row counts, if possible:
|
||||
ni = 0 # number of rows in this block-row
|
||||
for k = 1:rows[i]
|
||||
if !isa(A[j+k], UniformScaling)
|
||||
na = size(A[j+k], 1)
|
||||
ni > 0 && ni != na &&
|
||||
throw(DimensionMismatch("mismatch in number of rows"))
|
||||
ni = na
|
||||
end
|
||||
end
|
||||
if ni > 0
|
||||
for k = 1:rows[i]
|
||||
n[j+k] = ni
|
||||
end
|
||||
else # row consisted only of UniformScaling objects
|
||||
needcols = true
|
||||
end
|
||||
j += rows[i]
|
||||
end
|
||||
if needcols # some sizes still unknown, try to infer from column count
|
||||
nc = j = 0
|
||||
for i = 1:nr
|
||||
nci = 0
|
||||
rows[i] > 0 && n[j+1] == 0 && continue # column count unknown in this row
|
||||
for k = 1:rows[i]
|
||||
nci += isa(A[j+k], UniformScaling) ? n[j+k] : size(A[j+k], 2)
|
||||
end
|
||||
nc > 0 && nc != nci && throw(DimensionMismatch("mismatch in number of columns"))
|
||||
nc = nci
|
||||
j += rows[i]
|
||||
end
|
||||
nc == 0 && throw(ArgumentError("sizes of UniformScalings could not be inferred"))
|
||||
j = 0
|
||||
for i = 1:nr
|
||||
if rows[i] > 0 && n[j+1] == 0 # this row consists entirely of UniformScalings
|
||||
nci = nc ÷ rows[i]
|
||||
nci * rows[i] != nc && throw(DimensionMismatch("indivisible UniformScaling sizes"))
|
||||
for k = 1:rows[i]
|
||||
n[j+k] = nci
|
||||
end
|
||||
end
|
||||
j += rows[i]
|
||||
end
|
||||
end
|
||||
return hvcat(rows, promote_to_arrays(n,1, promote_to_array_type(A), A...)...)
|
||||
end
|
||||
Reference in New Issue
Block a user