fix incorrect folder name for julia-0.6.x

Former-commit-id: ef2c7401e0876f22d2f7762d182cfbcd5a7d9c70
This commit is contained in:
2018-06-11 03:28:36 -07:00
parent 5e0e436e4e
commit 0e4acfb8f2
722 changed files with 0 additions and 0 deletions
@@ -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), 789821. 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
+231
View File
@@ -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
+563
View File
@@ -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
+847
View File
@@ -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)
+314
View File
@@ -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