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

2209 lines
72 KiB
Julia
Raw Blame History

This file contains ambiguous Unicode characters

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

# This file is a part of Julia. License is MIT: https://julialang.org/license
## Triangular
# could be renamed to Triangular when that name has been fully deprecated
abstract type AbstractTriangular{T,S<:AbstractMatrix} <: AbstractMatrix{T} end
# First loop through all methods that don't need special care for upper/lower and unit diagonal
for t in (:LowerTriangular, :UnitLowerTriangular, :UpperTriangular,
:UnitUpperTriangular)
@eval begin
struct $t{T,S<:AbstractMatrix} <: AbstractTriangular{T,S}
data::S
end
$t(A::$t) = A
function $t(A::AbstractMatrix)
Base.LinAlg.checksquare(A)
return $t{eltype(A), typeof(A)}(A)
end
size(A::$t, d) = size(A.data, d)
size(A::$t) = size(A.data)
convert(::Type{$t{T}}, A::$t{T}) where {T} = A
function convert(::Type{$t{T}}, A::$t) where T
Anew = convert(AbstractMatrix{T}, A.data)
$t(Anew)
end
convert(::Type{AbstractMatrix{T}}, A::$t{T}) where {T} = A
convert(::Type{AbstractMatrix{T}}, A::$t) where {T} = convert($t{T}, A)
convert(::Type{Matrix}, A::$t{T}) where {T} = convert(Matrix{T}, A)
function similar(A::$t, ::Type{T}) where T
B = similar(A.data, T)
return $t(B)
end
copy(A::$t) = $t(copy(A.data))
broadcast(::typeof(big), A::$t) = $t(big.(A.data))
real(A::$t{<:Real}) = A
real(A::$t{<:Complex}) = (B = real(A.data); $t(B))
broadcast(::typeof(abs), A::$t) = $t(abs.(A.data))
end
end
LowerTriangular(U::UpperTriangular) = throw(ArgumentError(
"cannot create a LowerTriangular matrix from an UpperTriangular input"))
UpperTriangular(U::LowerTriangular) = throw(ArgumentError(
"cannot create an UpperTriangular matrix from a LowerTriangular input"))
"""
LowerTriangular(A::AbstractMatrix)
Construct a `LowerTriangular` view of the 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]
3×3 Array{Float64,2}:
1.0 2.0 3.0
4.0 5.0 6.0
7.0 8.0 9.0
julia> LowerTriangular(A)
3×3 LowerTriangular{Float64,Array{Float64,2}}:
1.0 ⋅ ⋅
4.0 5.0 ⋅
7.0 8.0 9.0
```
"""
LowerTriangular
"""
UpperTriangular(A::AbstractMatrix)
Construct an `UpperTriangular` view of the 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]
3×3 Array{Float64,2}:
1.0 2.0 3.0
4.0 5.0 6.0
7.0 8.0 9.0
julia> UpperTriangular(A)
3×3 UpperTriangular{Float64,Array{Float64,2}}:
1.0 2.0 3.0
⋅ 5.0 6.0
⋅ ⋅ 9.0
```
"""
UpperTriangular
imag(A::UpperTriangular) = UpperTriangular(imag(A.data))
imag(A::LowerTriangular) = LowerTriangular(imag(A.data))
imag(A::UnitLowerTriangular) = LowerTriangular(tril!(imag(A.data),-1))
imag(A::UnitUpperTriangular) = UpperTriangular(triu!(imag(A.data),1))
convert(::Type{Array}, A::AbstractTriangular) = convert(Matrix, A)
full(A::AbstractTriangular) = convert(Array, A)
parent(A::AbstractTriangular) = A.data
# then handle all methods that requires specific handling of upper/lower and unit diagonal
function convert(::Type{Matrix{T}}, A::LowerTriangular) where T
B = Matrix{T}(size(A, 1), size(A, 1))
copy!(B, A.data)
tril!(B)
B
end
function convert(::Type{Matrix{T}}, A::UnitLowerTriangular) where T
B = Matrix{T}(size(A, 1), size(A, 1))
copy!(B, A.data)
tril!(B)
for i = 1:size(B,1)
B[i,i] = 1
end
B
end
function convert(::Type{Matrix{T}}, A::UpperTriangular) where T
B = Matrix{T}(size(A, 1), size(A, 1))
copy!(B, A.data)
triu!(B)
B
end
function convert(::Type{Matrix{T}}, A::UnitUpperTriangular) where T
B = Matrix{T}(size(A, 1), size(A, 1))
copy!(B, A.data)
triu!(B)
for i = 1:size(B,1)
B[i,i] = 1
end
B
end
function full!(A::LowerTriangular)
B = A.data
tril!(B)
B
end
function full!(A::UnitLowerTriangular)
B = A.data
tril!(B)
for i = 1:size(A,1)
B[i,i] = 1
end
B
end
function full!(A::UpperTriangular)
B = A.data
triu!(B)
B
end
function full!(A::UnitUpperTriangular)
B = A.data
triu!(B)
for i = 1:size(A,1)
B[i,i] = 1
end
B
end
getindex(A::UnitLowerTriangular{T}, i::Integer, j::Integer) where {T} =
i > j ? A.data[i,j] : ifelse(i == j, oneunit(T), zero(T))
getindex(A::LowerTriangular, i::Integer, j::Integer) =
i >= j ? A.data[i,j] : zero(A.data[j,i])
getindex(A::UnitUpperTriangular{T}, i::Integer, j::Integer) where {T} =
i < j ? A.data[i,j] : ifelse(i == j, oneunit(T), zero(T))
getindex(A::UpperTriangular, i::Integer, j::Integer) =
i <= j ? A.data[i,j] : zero(A.data[j,i])
function setindex!(A::UpperTriangular, x, i::Integer, j::Integer)
if i > j
x == 0 || throw(ArgumentError("cannot set index in the lower triangular part " *
"($i, $j) of an UpperTriangular matrix to a nonzero value ($x)"))
else
A.data[i,j] = x
end
return A
end
function setindex!(A::UnitUpperTriangular, x, i::Integer, j::Integer)
if i > j
x == 0 || throw(ArgumentError("cannot set index in the lower triangular part " *
"($i, $j) of a UnitUpperTriangular matrix to a nonzero value ($x)"))
elseif i == j
x == 1 || throw(ArgumentError("cannot set index on the diagonal ($i, $j) " *
"of a UnitUpperTriangular matrix to a non-unit value ($x)"))
else
A.data[i,j] = x
end
return A
end
function setindex!(A::LowerTriangular, x, i::Integer, j::Integer)
if i < j
x == 0 || throw(ArgumentError("cannot set index in the upper triangular part " *
"($i, $j) of a LowerTriangular matrix to a nonzero value ($x)"))
else
A.data[i,j] = x
end
return A
end
function setindex!(A::UnitLowerTriangular, x, i::Integer, j::Integer)
if i < j
x == 0 || throw(ArgumentError("cannot set index in the upper triangular part " *
"($i, $j) of a UnitLowerTriangular matrix to a nonzero value ($x)"))
elseif i == j
x == 1 || throw(ArgumentError("cannot set index on the diagonal ($i, $j) " *
"of a UnitLowerTriangular matrix to a non-unit value ($x)"))
else
A.data[i,j] = x
end
return A
end
## structured matrix methods ##
function Base.replace_in_print_matrix(A::UpperTriangular,i::Integer,j::Integer,s::AbstractString)
i<=j ? s : Base.replace_with_centered_mark(s)
end
function Base.replace_in_print_matrix(A::LowerTriangular,i::Integer,j::Integer,s::AbstractString)
i>=j ? s : Base.replace_with_centered_mark(s)
end
istril(A::LowerTriangular) = true
istril(A::UnitLowerTriangular) = true
istriu(A::UpperTriangular) = true
istriu(A::UnitUpperTriangular) = true
function tril!(A::UpperTriangular, k::Integer=0)
n = size(A,1)
if abs(k) > n
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($n,$n)"))
elseif k < 0
fill!(A.data,0)
return A
elseif k == 0
for j in 1:n, i in 1:j-1
A.data[i,j] = 0
end
return A
else
return UpperTriangular(tril!(A.data,k))
end
end
triu!(A::UpperTriangular, k::Integer=0) = UpperTriangular(triu!(A.data,k))
function tril!(A::UnitUpperTriangular{T}, k::Integer=0) where T
n = size(A,1)
if abs(k) > n
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($n,$n)"))
elseif k < 0
fill!(A.data, zero(T))
return UpperTriangular(A.data)
elseif k == 0
fill!(A.data, zero(T))
for i in diagind(A)
A.data[i] = oneunit(T)
end
return UpperTriangular(A.data)
else
for i in diagind(A)
A.data[i] = oneunit(T)
end
return UpperTriangular(tril!(A.data,k))
end
end
function triu!(A::UnitUpperTriangular, k::Integer=0)
for i in diagind(A)
A.data[i] = oneunit(eltype(A))
end
return triu!(UpperTriangular(A.data),k)
end
function triu!(A::LowerTriangular, k::Integer=0)
n = size(A,1)
if abs(k) > n
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($n,$n)"))
elseif k > 0
fill!(A.data,0)
return A
elseif k == 0
for j in 1:n, i in j+1:n
A.data[i,j] = 0
end
return A
else
return LowerTriangular(triu!(A.data,k))
end
end
tril!(A::LowerTriangular, k::Integer=0) = LowerTriangular(tril!(A.data,k))
function triu!(A::UnitLowerTriangular{T}, k::Integer=0) where T
n = size(A,1)
if abs(k) > n
throw(ArgumentError("requested diagonal, $k, out of bounds in matrix of size ($n,$n)"))
elseif k > 0
fill!(A.data, zero(T))
return LowerTriangular(A.data)
elseif k == 0
fill!(A.data, zero(T))
for i in diagind(A)
A.data[i] = oneunit(T)
end
return LowerTriangular(A.data)
else
for i in diagind(A)
A.data[i] = oneunit(T)
end
return LowerTriangular(triu!(A.data,k))
end
end
function tril!(A::UnitLowerTriangular, k::Integer=0)
for i in diagind(A)
A.data[i] = oneunit(eltype(A))
end
return tril!(LowerTriangular(A.data),k)
end
transpose(A::LowerTriangular) = UpperTriangular(transpose(A.data))
transpose(A::UnitLowerTriangular) = UnitUpperTriangular(transpose(A.data))
transpose(A::UpperTriangular) = LowerTriangular(transpose(A.data))
transpose(A::UnitUpperTriangular) = UnitLowerTriangular(transpose(A.data))
ctranspose(A::LowerTriangular) = UpperTriangular(ctranspose(A.data))
ctranspose(A::UnitLowerTriangular) = UnitUpperTriangular(ctranspose(A.data))
ctranspose(A::UpperTriangular) = LowerTriangular(ctranspose(A.data))
ctranspose(A::UnitUpperTriangular) = UnitLowerTriangular(ctranspose(A.data))
transpose!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L'))
transpose!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L'))
transpose!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U'))
transpose!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U'))
ctranspose!(A::LowerTriangular) = UpperTriangular(copytri!(A.data, 'L' , true))
ctranspose!(A::UnitLowerTriangular) = UnitUpperTriangular(copytri!(A.data, 'L' , true))
ctranspose!(A::UpperTriangular) = LowerTriangular(copytri!(A.data, 'U' , true))
ctranspose!(A::UnitUpperTriangular) = UnitLowerTriangular(copytri!(A.data, 'U' , true))
diag(A::LowerTriangular) = diag(A.data)
diag(A::UnitLowerTriangular) = ones(eltype(A), size(A,1))
diag(A::UpperTriangular) = diag(A.data)
diag(A::UnitUpperTriangular) = ones(eltype(A), size(A,1))
# Unary operations
-(A::LowerTriangular) = LowerTriangular(-A.data)
-(A::UpperTriangular) = UpperTriangular(-A.data)
function -(A::UnitLowerTriangular)
Anew = -A.data
for i = 1:size(A, 1)
Anew[i, i] = -1
end
LowerTriangular(Anew)
end
function -(A::UnitUpperTriangular)
Anew = -A.data
for i = 1:size(A, 1)
Anew[i, i] = -1
end
UpperTriangular(Anew)
end
# copy and scale
function copy!(A::T, B::T) where T<:Union{UpperTriangular,UnitUpperTriangular}
n = size(B,1)
for j = 1:n
for i = 1:(isa(B, UnitUpperTriangular)?j-1:j)
@inbounds A[i,j] = B[i,j]
end
end
return A
end
function copy!(A::T, B::T) where T<:Union{LowerTriangular,UnitLowerTriangular}
n = size(B,1)
for j = 1:n
for i = (isa(B, UnitLowerTriangular)?j+1:j):n
@inbounds A[i,j] = B[i,j]
end
end
return A
end
function scale!(A::UpperTriangular, B::Union{UpperTriangular,UnitUpperTriangular}, c::Number)
n = checksquare(B)
for j = 1:n
if isa(B, UnitUpperTriangular)
@inbounds A[j,j] = c
end
for i = 1:(isa(B, UnitUpperTriangular)?j-1:j)
@inbounds A[i,j] = c * B[i,j]
end
end
return A
end
function scale!(A::LowerTriangular, B::Union{LowerTriangular,UnitLowerTriangular}, c::Number)
n = checksquare(B)
for j = 1:n
if isa(B, UnitLowerTriangular)
@inbounds A[j,j] = c
end
for i = (isa(B, UnitLowerTriangular)?j+1:j):n
@inbounds A[i,j] = c * B[i,j]
end
end
return A
end
scale!(A::Union{UpperTriangular,LowerTriangular}, c::Number) = scale!(A,A,c)
scale!(c::Number, A::Union{UpperTriangular,LowerTriangular}) = scale!(A,c)
# Binary operations
+(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(A.data + B.data)
+(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(A.data + B.data)
+(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(A.data + triu(B.data, 1) + I)
+(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(A.data + tril(B.data, -1) + I)
+(A::UnitUpperTriangular, B::UpperTriangular) = UpperTriangular(triu(A.data, 1) + B.data + I)
+(A::UnitLowerTriangular, B::LowerTriangular) = LowerTriangular(tril(A.data, -1) + B.data + I)
+(A::UnitUpperTriangular, B::UnitUpperTriangular) = UpperTriangular(triu(A.data, 1) + triu(B.data, 1) + 2I)
+(A::UnitLowerTriangular, B::UnitLowerTriangular) = LowerTriangular(tril(A.data, -1) + tril(B.data, -1) + 2I)
+(A::AbstractTriangular, B::AbstractTriangular) = full(A) + full(B)
-(A::UpperTriangular, B::UpperTriangular) = UpperTriangular(A.data - B.data)
-(A::LowerTriangular, B::LowerTriangular) = LowerTriangular(A.data - B.data)
-(A::UpperTriangular, B::UnitUpperTriangular) = UpperTriangular(A.data - triu(B.data, 1) - I)
-(A::LowerTriangular, B::UnitLowerTriangular) = LowerTriangular(A.data - tril(B.data, -1) - I)
-(A::UnitUpperTriangular, B::UpperTriangular) = UpperTriangular(triu(A.data, 1) - B.data + I)
-(A::UnitLowerTriangular, B::LowerTriangular) = LowerTriangular(tril(A.data, -1) - B.data + I)
-(A::UnitUpperTriangular, B::UnitUpperTriangular) = UpperTriangular(triu(A.data, 1) - triu(B.data, 1))
-(A::UnitLowerTriangular, B::UnitLowerTriangular) = LowerTriangular(tril(A.data, -1) - tril(B.data, -1))
-(A::AbstractTriangular, B::AbstractTriangular) = full(A) - full(B)
######################
# BlasFloat routines #
######################
A_mul_B!(A::Tridiagonal, B::AbstractTriangular) = A*full!(B)
A_mul_B!(C::AbstractMatrix, A::AbstractTriangular, B::Tridiagonal) = A_mul_B!(C, full(A), B)
A_mul_B!(C::AbstractMatrix, A::Tridiagonal, B::AbstractTriangular) = A_mul_B!(C, A, full(B))
A_mul_B!(C::AbstractVector, A::AbstractTriangular, B::AbstractVector) = A_mul_B!(A, copy!(C, B))
A_mul_B!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, copy!(C, B))
A_mul_B!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, copy!(C, B))
A_mul_Bt!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, transpose!(C, B))
A_mul_Bc!(C::AbstractMatrix, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, ctranspose!(C, B))
A_mul_Bc!(C::AbstractVecOrMat, A::AbstractTriangular, B::AbstractVecOrMat) = A_mul_B!(A, ctranspose!(C, B))
for (t, uploc, isunitc) in ((:LowerTriangular, 'L', 'N'),
(:UnitLowerTriangular, 'L', 'U'),
(:UpperTriangular, 'U', 'N'),
(:UnitUpperTriangular, 'U', 'U'))
@eval begin
# Vector multiplication
A_mul_B!(A::$t{T,<:StridedMatrix}, b::StridedVector{T}) where {T<:BlasFloat} =
BLAS.trmv!($uploc, 'N', $isunitc, A.data, b)
At_mul_B!(A::$t{T,<:StridedMatrix}, b::StridedVector{T}) where {T<:BlasFloat} =
BLAS.trmv!($uploc, 'T', $isunitc, A.data, b)
Ac_mul_B!(A::$t{T,<:StridedMatrix}, b::StridedVector{T}) where {T<:BlasReal} =
BLAS.trmv!($uploc, 'T', $isunitc, A.data, b)
Ac_mul_B!(A::$t{T,<:StridedMatrix}, b::StridedVector{T}) where {T<:BlasComplex} =
BLAS.trmv!($uploc, 'C', $isunitc, A.data, b)
# Matrix multiplication
A_mul_B!(A::$t{T,<:StridedMatrix}, B::StridedMatrix{T}) where {T<:BlasFloat} =
BLAS.trmm!('L', $uploc, 'N', $isunitc, one(T), A.data, B)
A_mul_B!(A::StridedMatrix{T}, B::$t{T,<:StridedMatrix}) where {T<:BlasFloat} =
BLAS.trmm!('R', $uploc, 'N', $isunitc, one(T), B.data, A)
At_mul_B!(A::$t{T,<:StridedMatrix}, B::StridedMatrix{T}) where {T<:BlasFloat} =
BLAS.trmm!('L', $uploc, 'T', $isunitc, one(T), A.data, B)
Ac_mul_B!(A::$t{T,<:StridedMatrix}, B::StridedMatrix{T}) where {T<:BlasComplex} =
BLAS.trmm!('L', $uploc, 'C', $isunitc, one(T), A.data, B)
Ac_mul_B!(A::$t{T,<:StridedMatrix}, B::StridedMatrix{T}) where {T<:BlasReal} =
BLAS.trmm!('L', $uploc, 'T', $isunitc, one(T), A.data, B)
A_mul_Bt!(A::StridedMatrix{T}, B::$t{T,<:StridedMatrix}) where {T<:BlasFloat} =
BLAS.trmm!('R', $uploc, 'T', $isunitc, one(T), B.data, A)
A_mul_Bc!(A::StridedMatrix{T}, B::$t{T,<:StridedMatrix}) where {T<:BlasComplex} =
BLAS.trmm!('R', $uploc, 'C', $isunitc, one(T), B.data, A)
A_mul_Bc!(A::StridedMatrix{T}, B::$t{T,<:StridedMatrix}) where {T<:BlasReal} =
BLAS.trmm!('R', $uploc, 'T', $isunitc, one(T), B.data, A)
# Left division
A_ldiv_B!(A::$t{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} =
LAPACK.trtrs!($uploc, 'N', $isunitc, A.data, B)
At_ldiv_B!(A::$t{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasFloat} =
LAPACK.trtrs!($uploc, 'T', $isunitc, A.data, B)
Ac_ldiv_B!(A::$t{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasReal} =
LAPACK.trtrs!($uploc, 'T', $isunitc, A.data, B)
Ac_ldiv_B!(A::$t{T,<:StridedMatrix}, B::StridedVecOrMat{T}) where {T<:BlasComplex} =
LAPACK.trtrs!($uploc, 'C', $isunitc, A.data, B)
# Right division
A_rdiv_B!(A::StridedMatrix{T}, B::$t{T,<:StridedMatrix}) where {T<:BlasFloat} =
BLAS.trsm!('R', $uploc, 'N', $isunitc, one(T), B.data, A)
A_rdiv_Bt!(A::StridedMatrix{T}, B::$t{T,<:StridedMatrix}) where {T<:BlasFloat} =
BLAS.trsm!('R', $uploc, 'T', $isunitc, one(T), B.data, A)
A_rdiv_Bc!(A::StridedMatrix{T}, B::$t{T,<:StridedMatrix}) where {T<:BlasReal} =
BLAS.trsm!('R', $uploc, 'T', $isunitc, one(T), B.data, A)
A_rdiv_Bc!(A::StridedMatrix{T}, B::$t{T,<:StridedMatrix}) where {T<:BlasComplex} =
BLAS.trsm!('R', $uploc, 'C', $isunitc, one(T), B.data, A)
# Matrix inverse
inv!(A::$t{T,S}) where {T<:BlasFloat,S<:StridedMatrix} =
$t{T,S}(LAPACK.trtri!($uploc, $isunitc, A.data))
# Error bounds for triangular solve
errorbounds(A::$t{T,<:StridedMatrix}, X::StridedVecOrMat{T}, B::StridedVecOrMat{T}) where {T<:BlasFloat} =
LAPACK.trrfs!($uploc, 'N', $isunitc, A.data, B, X)
# Condition numbers
function cond(A::$t{<:BlasFloat}, p::Real=2)
checksquare(A)
if p == 1
return inv(LAPACK.trcon!('O', $uploc, $isunitc, A.data))
elseif p == Inf
return inv(LAPACK.trcon!('I', $uploc, $isunitc, A.data))
else # use fallback
return cond(full(A), p)
end
end
end
end
function inv(A::LowerTriangular{T}) where T
S = typeof((zero(T)*one(T) + zero(T))/one(T))
LowerTriangular(A_ldiv_B!(convert(AbstractArray{S}, A), eye(S, size(A, 1))))
end
function inv(A::UpperTriangular{T}) where T
S = typeof((zero(T)*one(T) + zero(T))/one(T))
UpperTriangular(A_ldiv_B!(convert(AbstractArray{S}, A), eye(S, size(A, 1))))
end
inv(A::UnitUpperTriangular{T}) where {T} = UnitUpperTriangular(A_ldiv_B!(A, eye(T, size(A, 1))))
inv(A::UnitLowerTriangular{T}) where {T} = UnitLowerTriangular(A_ldiv_B!(A, eye(T, size(A, 1))))
errorbounds(A::AbstractTriangular{T,<:StridedMatrix}, X::StridedVecOrMat{T}, B::StridedVecOrMat{T}) where {T<:Union{BigFloat,Complex{BigFloat}}} =
error("not implemented yet! Please submit a pull request.")
function errorbounds(A::AbstractTriangular{TA,<:StridedMatrix}, X::StridedVecOrMat{TX}, B::StridedVecOrMat{TB}) where {TA<:Number,TX<:Number,TB<:Number}
TAXB = promote_type(TA, TB, TX, Float32)
errorbounds(convert(AbstractMatrix{TAXB}, A), convert(AbstractArray{TAXB}, X), convert(AbstractArray{TAXB}, B))
end
# Eigensystems
## Notice that trecv works for quasi-triangular matrices and therefore the lower sub diagonal must be zeroed before calling the subroutine
function eigvecs(A::UpperTriangular{<:BlasFloat,<:StridedMatrix})
LAPACK.trevc!('R', 'A', BlasInt[], triu!(A.data))
end
function eigvecs(A::UnitUpperTriangular{<:BlasFloat,<:StridedMatrix})
for i = 1:size(A, 1)
A.data[i,i] = 1
end
LAPACK.trevc!('R', 'A', BlasInt[], triu!(A.data))
end
function eigvecs(A::LowerTriangular{<:BlasFloat,<:StridedMatrix})
LAPACK.trevc!('L', 'A', BlasInt[], tril!(A.data)')
end
function eigvecs(A::UnitLowerTriangular{<:BlasFloat,<:StridedMatrix})
for i = 1:size(A, 1)
A.data[i,i] = 1
end
LAPACK.trevc!('L', 'A', BlasInt[], tril!(A.data)')
end
####################
# Generic routines #
####################
for (t, unitt) in ((UpperTriangular, UnitUpperTriangular),
(LowerTriangular, UnitLowerTriangular))
@eval begin
(*)(A::$t, x::Number) = $t(A.data*x)
function (*)(A::$unitt, x::Number)
B = A.data*x
for i = 1:size(A, 1)
B[i,i] = x
end
$t(B)
end
(*)(x::Number, A::$t) = $t(x*A.data)
function (*)(x::Number, A::$unitt)
B = x*A.data
for i = 1:size(A, 1)
B[i,i] = x
end
$t(B)
end
(/)(A::$t, x::Number) = $t(A.data/x)
function (/)(A::$unitt, x::Number)
B = A.data/x
invx = inv(x)
for i = 1:size(A, 1)
B[i,i] = invx
end
$t(B)
end
(\)(x::Number, A::$t) = $t(x\A.data)
function (\)(x::Number, A::$unitt)
B = x\A.data
invx = inv(x)
for i = 1:size(A, 1)
B[i,i] = invx
end
$t(B)
end
end
end
## Generic triangular multiplication
function A_mul_B!(A::UpperTriangular, B::StridedVecOrMat)
m, n = size(B, 1), size(B, 2)
if m != size(A, 1)
throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m"))
end
for j = 1:n
for i = 1:m
Bij = A.data[i,i]*B[i,j]
for k = i + 1:m
Bij += A.data[i,k]*B[k,j]
end
B[i,j] = Bij
end
end
B
end
function A_mul_B!(A::UnitUpperTriangular, B::StridedVecOrMat)
m, n = size(B, 1), size(B, 2)
if m != size(A, 1)
throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m"))
end
for j = 1:n
for i = 1:m
Bij = B[i,j]
for k = i + 1:m
Bij += A.data[i,k]*B[k,j]
end
B[i,j] = Bij
end
end
B
end
function A_mul_B!(A::LowerTriangular, B::StridedVecOrMat)
m, n = size(B, 1), size(B, 2)
if m != size(A, 1)
throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m"))
end
for j = 1:n
for i = m:-1:1
Bij = A.data[i,i]*B[i,j]
for k = 1:i - 1
Bij += A.data[i,k]*B[k,j]
end
B[i,j] = Bij
end
end
B
end
function A_mul_B!(A::UnitLowerTriangular, B::StridedVecOrMat)
m, n = size(B, 1), size(B, 2)
if m != size(A, 1)
throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m"))
end
for j = 1:n
for i = m:-1:1
Bij = B[i,j]
for k = 1:i - 1
Bij += A.data[i,k]*B[k,j]
end
B[i,j] = Bij
end
end
B
end
function Ac_mul_B!(A::UpperTriangular, B::StridedVecOrMat)
m, n = size(B, 1), size(B, 2)
if m != size(A, 1)
throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m"))
end
for j = 1:n
for i = m:-1:1
Bij = A.data[i,i]'B[i,j]
for k = 1:i - 1
Bij += A.data[k,i]'B[k,j]
end
B[i,j] = Bij
end
end
B
end
function Ac_mul_B!(A::UnitUpperTriangular, B::StridedVecOrMat)
m, n = size(B, 1), size(B, 2)
if m != size(A, 1)
throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m"))
end
for j = 1:n
for i = m:-1:1
Bij = B[i,j]
for k = 1:i - 1
Bij += A.data[k,i]'B[k,j]
end
B[i,j] = Bij
end
end
B
end
function Ac_mul_B!(A::LowerTriangular, B::StridedVecOrMat)
m, n = size(B, 1), size(B, 2)
if m != size(A, 1)
throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m"))
end
for j = 1:n
for i = 1:m
Bij = A.data[i,i]'B[i,j]
for k = i + 1:m
Bij += A.data[k,i]'B[k,j]
end
B[i,j] = Bij
end
end
B
end
function Ac_mul_B!(A::UnitLowerTriangular, B::StridedVecOrMat)
m, n = size(B, 1), size(B, 2)
if m != size(A, 1)
throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m"))
end
for j = 1:n
for i = 1:m
Bij = B[i,j]
for k = i + 1:m
Bij += A.data[k,i]'B[k,j]
end
B[i,j] = Bij
end
end
B
end
function At_mul_B!(A::UpperTriangular, B::StridedVecOrMat)
m, n = size(B, 1), size(B, 2)
if m != size(A, 1)
throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m"))
end
for j = 1:n
for i = m:-1:1
Bij = A.data[i,i].'B[i,j]
for k = 1:i - 1
Bij += A.data[k,i].'B[k,j]
end
B[i,j] = Bij
end
end
B
end
function At_mul_B!(A::UnitUpperTriangular, B::StridedVecOrMat)
m, n = size(B, 1), size(B, 2)
if m != size(A, 1)
throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m"))
end
for j = 1:n
for i = m:-1:1
Bij = B[i,j]
for k = 1:i - 1
Bij += A.data[k,i].'B[k,j]
end
B[i,j] = Bij
end
end
B
end
function At_mul_B!(A::LowerTriangular, B::StridedVecOrMat)
m, n = size(B, 1), size(B, 2)
if m != size(A, 1)
throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m"))
end
for j = 1:n
for i = 1:m
Bij = A.data[i,i].'B[i,j]
for k = i + 1:m
Bij += A.data[k,i].'B[k,j]
end
B[i,j] = Bij
end
end
B
end
function At_mul_B!(A::UnitLowerTriangular, B::StridedVecOrMat)
m, n = size(B, 1), size(B, 2)
if m != size(A, 1)
throw(DimensionMismatch("right hand side B needs first dimension of size $(size(A,1)), has size $m"))
end
for j = 1:n
for i = 1:m
Bij = B[i,j]
for k = i + 1:m
Bij += A.data[k,i].'B[k,j]
end
B[i,j] = Bij
end
end
B
end
function A_mul_B!(A::StridedMatrix, B::UpperTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = n:-1:1
Aij = A[i,j]*B[j,j]
for k = 1:j - 1
Aij += A[i,k]*B.data[k,j]
end
A[i,j] = Aij
end
end
A
end
function A_mul_B!(A::StridedMatrix, B::UnitUpperTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = n:-1:1
Aij = A[i,j]
for k = 1:j - 1
Aij += A[i,k]*B.data[k,j]
end
A[i,j] = Aij
end
end
A
end
function A_mul_B!(A::StridedMatrix, B::LowerTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = 1:n
Aij = A[i,j]*B[j,j]
for k = j + 1:n
Aij += A[i,k]*B.data[k,j]
end
A[i,j] = Aij
end
end
A
end
function A_mul_B!(A::StridedMatrix, B::UnitLowerTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = 1:n
Aij = A[i,j]
for k = j + 1:n
Aij += A[i,k]*B.data[k,j]
end
A[i,j] = Aij
end
end
A
end
function A_mul_Bc!(A::StridedMatrix, B::UpperTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = 1:n
Aij = A[i,j]*B.data[j,j]'
for k = j + 1:n
Aij += A[i,k]*B.data[j,k]'
end
A[i,j] = Aij
end
end
A
end
function A_mul_Bc!(A::StridedMatrix, B::UnitUpperTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = 1:n
Aij = A[i,j]
for k = j + 1:n
Aij += A[i,k]*B.data[j,k]'
end
A[i,j] = Aij
end
end
A
end
function A_mul_Bc!(A::StridedMatrix, B::LowerTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = n:-1:1
Aij = A[i,j]*B.data[j,j]'
for k = 1:j - 1
Aij += A[i,k]*B.data[j,k]'
end
A[i,j] = Aij
end
end
A
end
function A_mul_Bc!(A::StridedMatrix, B::UnitLowerTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = n:-1:1
Aij = A[i,j]
for k = 1:j - 1
Aij += A[i,k]*B.data[j,k]'
end
A[i,j] = Aij
end
end
A
end
function A_mul_Bt!(A::StridedMatrix, B::UpperTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = 1:n
Aij = A[i,j]*B.data[j,j].'
for k = j + 1:n
Aij += A[i,k]*B.data[j,k].'
end
A[i,j] = Aij
end
end
A
end
function A_mul_Bt!(A::StridedMatrix, B::UnitUpperTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = 1:n
Aij = A[i,j]
for k = j + 1:n
Aij += A[i,k]*B.data[j,k].'
end
A[i,j] = Aij
end
end
A
end
function A_mul_Bt!(A::StridedMatrix, B::LowerTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = n:-1:1
Aij = A[i,j]*B.data[j,j].'
for k = 1:j - 1
Aij += A[i,k]*B.data[j,k].'
end
A[i,j] = Aij
end
end
A
end
function A_mul_Bt!(A::StridedMatrix, B::UnitLowerTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = n:-1:1
Aij = A[i,j]
for k = 1:j - 1
Aij += A[i,k]*B.data[j,k].'
end
A[i,j] = Aij
end
end
A
end
#Generic solver using naive substitution
# manually hoisting x[j] significantly improves performance as of Dec 2015
# manually eliding bounds checking significantly improves performance as of Dec 2015
# directly indexing A.data rather than A significantly improves performance as of Dec 2015
# replacing repeated references to A.data with [Adata = A.data and references to Adata]
# does not significantly impact performance as of Dec 2015
# replacing repeated references to A.data[j,j] with [Ajj = A.data[j,j] and references to Ajj]
# does not significantly impact performance as of Dec 2015
function naivesub!(A::UpperTriangular, b::AbstractVector, x::AbstractVector = b)
n = size(A, 2)
if !(n == length(b) == length(x))
throw(DimensionMismatch("second dimension of left hand side A, $n, length of output x, $(length(x)), and length of right hand side b, $(length(b)), must be equal"))
end
@inbounds for j in n:-1:1
A.data[j,j] == zero(A.data[j,j]) && throw(SingularException(j))
xj = x[j] = A.data[j,j] \ b[j]
for i in j-1:-1:1 # counterintuitively 1:j-1 performs slightly better
b[i] -= A.data[i,j] * xj
end
end
x
end
function naivesub!(A::UnitUpperTriangular, b::AbstractVector, x::AbstractVector = b)
n = size(A, 2)
if !(n == length(b) == length(x))
throw(DimensionMismatch("second dimension of left hand side A, $n, length of output x, $(length(x)), and length of right hand side b, $(length(b)), must be equal"))
end
@inbounds for j in n:-1:1
xj = x[j] = b[j]
for i in j-1:-1:1 # counterintuitively 1:j-1 performs slightly better
b[i] -= A.data[i,j] * xj
end
end
x
end
function naivesub!(A::LowerTriangular, b::AbstractVector, x::AbstractVector = b)
n = size(A, 2)
if !(n == length(b) == length(x))
throw(DimensionMismatch("second dimension of left hand side A, $n, length of output x, $(length(x)), and length of right hand side b, $(length(b)), must be equal"))
end
@inbounds for j in 1:n
A.data[j,j] == zero(A.data[j,j]) && throw(SingularException(j))
xj = x[j] = A.data[j,j] \ b[j]
for i in j+1:n
b[i] -= A.data[i,j] * xj
end
end
x
end
function naivesub!(A::UnitLowerTriangular, b::AbstractVector, x::AbstractVector = b)
n = size(A, 2)
if !(n == length(b) == length(x))
throw(DimensionMismatch("second dimension of left hand side A, $n, length of output x, $(length(x)), and length of right hand side b, $(length(b)), must be equal"))
end
@inbounds for j in 1:n
xj = x[j] = b[j]
for i in j+1:n
b[i] -= A.data[i,j] * xj
end
end
x
end
# in the following transpose and conjugate transpose naive substitution variants,
# accumulating in z rather than b[j] significantly improves performance as of Dec 2015
function At_ldiv_B!(A::LowerTriangular, b::AbstractVector, x::AbstractVector = b)
n = size(A, 1)
if !(n == length(b) == length(x))
throw(DimensionMismatch("first dimension of left hand side A, $n, length of output x, $(length(x)), and length of right hand side b, $(length(b)), must be equal"))
end
@inbounds for j in n:-1:1
z = b[j]
for i in n:-1:j+1
z -= A.data[i,j] * x[i]
end
A.data[j,j] == zero(A.data[j,j]) && throw(SingularException(j))
x[j] = A.data[j,j] \ z
end
x
end
function At_ldiv_B!(A::UnitLowerTriangular, b::AbstractVector, x::AbstractVector = b)
n = size(A, 1)
if !(n == length(b) == length(x))
throw(DimensionMismatch("first dimension of left hand side A, $n, length of output x, $(length(x)), and length of right hand side b, $(length(b)), must be equal"))
end
@inbounds for j in n:-1:1
z = b[j]
for i in n:-1:j+1
z -= A.data[i,j] * x[i]
end
x[j] = z
end
x
end
function At_ldiv_B!(A::UpperTriangular, b::AbstractVector, x::AbstractVector = b)
n = size(A, 1)
if !(n == length(b) == length(x))
throw(DimensionMismatch("first dimension of left hand side A, $n, length of output x, $(length(x)), and length of right hand side b, $(length(b)), must be equal"))
end
@inbounds for j in 1:n
z = b[j]
for i in 1:j-1
z -= A.data[i,j] * x[i]
end
A.data[j,j] == zero(A.data[j,j]) && throw(SingularException(j))
x[j] = A.data[j,j] \ z
end
x
end
function At_ldiv_B!(A::UnitUpperTriangular, b::AbstractVector, x::AbstractVector = b)
n = size(A, 1)
if !(n == length(b) == length(x))
throw(DimensionMismatch("first dimension of left hand side A, $n, length of output x, $(length(x)), and length of right hand side b, $(length(b)), must be equal"))
end
@inbounds for j in 1:n
z = b[j]
for i in 1:j-1
z -= A.data[i,j] * x[i]
end
x[j] = z
end
x
end
function Ac_ldiv_B!(A::LowerTriangular, b::AbstractVector, x::AbstractVector = b)
n = size(A, 1)
if !(n == length(b) == length(x))
throw(DimensionMismatch("first dimension of left hand side A, $n, length of output x, $(length(x)), and length of right hand side b, $(length(b)), must be equal"))
end
@inbounds for j in n:-1:1
z = b[j]
for i in n:-1:j+1
z -= A.data[i,j]' * x[i]
end
A.data[j,j] == zero(A.data[j,j]) && throw(SingularException(j))
x[j] = A.data[j,j]' \ z
end
x
end
function Ac_ldiv_B!(A::UnitLowerTriangular, b::AbstractVector, x::AbstractVector = b)
n = size(A, 1)
if !(n == length(b) == length(x))
throw(DimensionMismatch("first dimension of left hand side A, $n, length of output x, $(length(x)), and length of right hand side b, $(length(b)), must be equal"))
end
@inbounds for j in n:-1:1
z = b[j]
for i in n:-1:j+1
z -= A.data[i,j]' * x[i]
end
x[j] = z
end
x
end
function Ac_ldiv_B!(A::UpperTriangular, b::AbstractVector, x::AbstractVector = b)
n = size(A, 1)
if !(n == length(b) == length(x))
throw(DimensionMismatch("first dimension of left hand side A, $n, length of output x, $(length(x)), and length of right hand side b, $(length(b)), must be equal"))
end
@inbounds for j in 1:n
z = b[j]
for i in 1:j-1
z -= A.data[i,j]' * x[i]
end
A.data[j,j] == zero(A.data[j,j]) && throw(SingularException(j))
x[j] = A.data[j,j]' \ z
end
x
end
function Ac_ldiv_B!(A::UnitUpperTriangular, b::AbstractVector, x::AbstractVector = b)
n = size(A, 1)
if !(n == length(b) == length(x))
throw(DimensionMismatch("first dimension of left hand side A, $n, length of output x, $(length(x)), and length of right hand side b, $(length(b)), must be equal"))
end
@inbounds for j in 1:n
z = b[j]
for i in 1:j-1
z -= A.data[i,j]' * x[i]
end
x[j] = z
end
x
end
function A_rdiv_B!(A::StridedMatrix, B::UpperTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = 1:n
Aij = A[i,j]
for k = 1:j - 1
Aij -= A[i,k]*B.data[k,j]
end
A[i,j] = Aij/B[j,j]
end
end
A
end
function A_rdiv_B!(A::StridedMatrix, B::UnitUpperTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = 1:n
Aij = A[i,j]
for k = 1:j - 1
Aij -= A[i,k]*B.data[k,j]
end
A[i,j] = Aij
end
end
A
end
function A_rdiv_B!(A::StridedMatrix, B::LowerTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = n:-1:1
Aij = A[i,j]
for k = j + 1:n
Aij -= A[i,k]*B.data[k,j]
end
A[i,j] = Aij/B[j,j]
end
end
A
end
function A_rdiv_B!(A::StridedMatrix, B::UnitLowerTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = n:-1:1
Aij = A[i,j]
for k = j + 1:n
Aij -= A[i,k]*B.data[k,j]
end
A[i,j] = Aij
end
end
A
end
function A_rdiv_Bc!(A::StridedMatrix, B::UpperTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = n:-1:1
Aij = A[i,j]
for k = j + 1:n
Aij -= A[i,k]*B.data[j,k]'
end
A[i,j] = Aij/B.data[j,j]'
end
end
A
end
function A_rdiv_Bc!(A::StridedMatrix, B::UnitUpperTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = n:-1:1
Aij = A[i,j]
for k = j + 1:n
Aij -= A[i,k]*B.data[j,k]'
end
A[i,j] = Aij
end
end
A
end
function A_rdiv_Bc!(A::StridedMatrix, B::LowerTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = 1:n
Aij = A[i,j]
for k = 1:j - 1
Aij -= A[i,k]*B.data[j,k]'
end
A[i,j] = Aij/B.data[j,j]'
end
end
A
end
function A_rdiv_Bc!(A::StridedMatrix, B::UnitLowerTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = 1:n
Aij = A[i,j]
for k = 1:j - 1
Aij -= A[i,k]*B.data[j,k]'
end
A[i,j] = Aij
end
end
A
end
function A_rdiv_Bt!(A::StridedMatrix, B::UpperTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = n:-1:1
Aij = A[i,j]
for k = j + 1:n
Aij -= A[i,k]*B.data[j,k].'
end
A[i,j] = Aij/B.data[j,j].'
end
end
A
end
function A_rdiv_Bt!(A::StridedMatrix, B::UnitUpperTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = n:-1:1
Aij = A[i,j]
for k = j + 1:n
Aij -= A[i,k]*B.data[j,k].'
end
A[i,j] = Aij
end
end
A
end
function A_rdiv_Bt!(A::StridedMatrix, B::LowerTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = 1:n
Aij = A[i,j]
for k = 1:j - 1
Aij -= A[i,k]*B.data[j,k].'
end
A[i,j] = Aij/B.data[j,j].'
end
end
A
end
function A_rdiv_Bt!(A::StridedMatrix, B::UnitLowerTriangular)
m, n = size(A)
if size(B, 1) != n
throw(DimensionMismatch("right hand side B needs first dimension of size $n, has size $(size(B,1))"))
end
for i = 1:m
for j = 1:n
Aij = A[i,j]
for k = 1:j - 1
Aij -= A[i,k]*B.data[j,k].'
end
A[i,j] = Aij
end
end
A
end
for f in (:Ac_mul_B!, :At_mul_B!, :Ac_ldiv_B!, :At_ldiv_B!)
@eval begin
$f(A::Union{LowerTriangular,UnitLowerTriangular}, B::UpperTriangular) =
UpperTriangular($f(A, triu!(B.data)))
$f(A::Union{UpperTriangular,UnitUpperTriangular}, B::LowerTriangular) =
LowerTriangular($f(A, tril!(B.data)))
end
end
A_rdiv_B!(A::UpperTriangular, B::Union{UpperTriangular,UnitUpperTriangular}) =
UpperTriangular(A_rdiv_B!(triu!(A.data), B))
A_rdiv_B!(A::LowerTriangular, B::Union{LowerTriangular,UnitLowerTriangular}) =
LowerTriangular(A_rdiv_B!(tril!(A.data), B))
for f in (:A_mul_Bc!, :A_mul_Bt!, :A_rdiv_Bc!, :A_rdiv_Bt!)
@eval begin
$f(A::UpperTriangular, B::Union{LowerTriangular,UnitLowerTriangular}) =
UpperTriangular($f(triu!(A.data), B))
$f(A::LowerTriangular, B::Union{UpperTriangular,UnitUpperTriangular}) =
LowerTriangular($f(tril!(A.data), B))
end
end
# Promotion
## Promotion methods in matmul don't apply to triangular multiplication since
## it is inplace. Hence we have to make very similar definitions, but without
## allocation of a result array. For multiplication and unit diagonal division
## the element type doesn't have to be stable under division whereas that is
## necessary in the general triangular solve problem.
## Some Triangular-Triangular cases. We might want to write taylored methods
## for these cases, but I'm not sure it is worth it.
for t in (UpperTriangular, UnitUpperTriangular, LowerTriangular, UnitLowerTriangular)
@eval begin
(*)(A::Tridiagonal, B::$t) = A_mul_B!(full(A), B)
end
end
for (f1, f2) in ((:*, :A_mul_B!), (:\, :A_ldiv_B!))
@eval begin
function ($f1)(A::LowerTriangular, B::LowerTriangular)
TAB = typeof(($f1)(zero(eltype(A)), zero(eltype(B))) +
($f1)(zero(eltype(A)), zero(eltype(B))))
BB = similar(B, TAB, size(B))
copy!(BB, B)
return LowerTriangular($f2(convert(AbstractMatrix{TAB}, A), BB))
end
function $(f1)(A::UnitLowerTriangular, B::LowerTriangular)
TAB = typeof((*)(zero(eltype(A)), zero(eltype(B))) +
(*)(zero(eltype(A)), zero(eltype(B))))
BB = similar(B, TAB, size(B))
copy!(BB, B)
return LowerTriangular($f2(convert(AbstractMatrix{TAB}, A), BB))
end
function ($f1)(A::UpperTriangular, B::UpperTriangular)
TAB = typeof(($f1)(zero(eltype(A)), zero(eltype(B))) +
($f1)(zero(eltype(A)), zero(eltype(B))))
BB = similar(B, TAB, size(B))
copy!(BB, B)
return UpperTriangular($f2(convert(AbstractMatrix{TAB}, A), BB))
end
function ($f1)(A::UnitUpperTriangular, B::UpperTriangular)
TAB = typeof((*)(zero(eltype(A)), zero(eltype(B))) +
(*)(zero(eltype(A)), zero(eltype(B))))
BB = similar(B, TAB, size(B))
copy!(BB, B)
return UpperTriangular($f2(convert(AbstractMatrix{TAB}, A), BB))
end
end
end
for (f1, f2) in ((:Ac_mul_B, :Ac_mul_B!), (:At_mul_B, :At_mul_B!),
(:Ac_ldiv_B, Ac_ldiv_B!), (:At_ldiv_B, :At_ldiv_B!))
@eval begin
function ($f1)(A::UpperTriangular, B::LowerTriangular)
TAB = typeof(($f1)(zero(eltype(A)), zero(eltype(B))) +
($f1)(zero(eltype(A)), zero(eltype(B))))
BB = similar(B, TAB, size(B))
copy!(BB, B)
return LowerTriangular($f2(convert(AbstractMatrix{TAB}, A), BB))
end
function ($f1)(A::UnitUpperTriangular, B::LowerTriangular)
TAB = typeof((*)(zero(eltype(A)), zero(eltype(B))) +
(*)(zero(eltype(A)), zero(eltype(B))))
BB = similar(B, TAB, size(B))
copy!(BB, B)
return LowerTriangular($f2(convert(AbstractMatrix{TAB}, A), BB))
end
function ($f1)(A::LowerTriangular, B::UpperTriangular)
TAB = typeof(($f1)(zero(eltype(A)), zero(eltype(B))) +
($f1)(zero(eltype(A)), zero(eltype(B))))
BB = similar(B, TAB, size(B))
copy!(BB, B)
return UpperTriangular($f2(convert(AbstractMatrix{TAB}, A), BB))
end
function ($f1)(A::UnitLowerTriangular, B::UpperTriangular)
TAB = typeof((*)(zero(eltype(A)), zero(eltype(B))) +
(*)(zero(eltype(A)), zero(eltype(B))))
BB = similar(B, TAB, size(B))
copy!(BB, B)
return UpperTriangular($f2(convert(AbstractMatrix{TAB}, A), BB))
end
end
end
function (/)(A::LowerTriangular, B::LowerTriangular)
TAB = typeof((/)(zero(eltype(A)), zero(eltype(B))) +
(/)(zero(eltype(A)), zero(eltype(B))))
AA = similar(A, TAB, size(A))
copy!(AA, A)
return LowerTriangular(A_rdiv_B!(AA, convert(AbstractMatrix{TAB}, B)))
end
function (/)(A::LowerTriangular, B::UnitLowerTriangular)
TAB = typeof((*)(zero(eltype(A)), zero(eltype(B))) +
(*)(zero(eltype(A)), zero(eltype(B))))
AA = similar(A, TAB, size(A))
copy!(AA, A)
return LowerTriangular(A_rdiv_B!(AA, convert(AbstractMatrix{TAB}, B)))
end
function (/)(A::UpperTriangular, B::UpperTriangular)
TAB = typeof((/)(zero(eltype(A)), zero(eltype(B))) +
(/)(zero(eltype(A)), zero(eltype(B))))
AA = similar(A, TAB, size(A))
copy!(AA, A)
return UpperTriangular(A_rdiv_B!(AA, convert(AbstractMatrix{TAB}, B)))
end
function (/)(A::UpperTriangular, B::UnitUpperTriangular)
TAB = typeof((*)(zero(eltype(A)), zero(eltype(B))) +
(*)(zero(eltype(A)), zero(eltype(B))))
AA = similar(A, TAB, size(A))
copy!(AA, A)
return UpperTriangular(A_rdiv_B!(AA, convert(AbstractMatrix{TAB}, B)))
end
for (f1, f2) in ((:A_mul_Bc, :A_mul_Bc!), (:A_mul_Bt, :A_mul_Bt!),
(:A_rdiv_Bc, :A_rdiv_Bc!), (:A_rdiv_Bt, :A_rdiv_Bt!))
@eval begin
function $f1(A::LowerTriangular, B::UpperTriangular)
TAB = typeof(($f1)(zero(eltype(A)), zero(eltype(B))) +
($f1)(zero(eltype(A)), zero(eltype(B))))
AA = similar(A, TAB, size(A))
copy!(AA, A)
return LowerTriangular($f2(AA, convert(AbstractMatrix{TAB}, B)))
end
function $f1(A::LowerTriangular, B::UnitUpperTriangular)
TAB = typeof((*)(zero(eltype(A)), zero(eltype(B))) +
(*)(zero(eltype(A)), zero(eltype(B))))
AA = similar(A, TAB, size(A))
copy!(AA, A)
return LowerTriangular($f2(AA, convert(AbstractMatrix{TAB}, B)))
end
function $f1(A::UpperTriangular, B::LowerTriangular)
TAB = typeof(($f1)(zero(eltype(A)), zero(eltype(B))) +
($f1)(zero(eltype(A)), zero(eltype(B))))
AA = similar(A, TAB, size(A))
copy!(AA, A)
return UpperTriangular($f2(AA, convert(AbstractMatrix{TAB}, B)))
end
function $f1(A::UpperTriangular, B::UnitLowerTriangular)
TAB = typeof((*)(zero(eltype(A)), zero(eltype(B))) +
(*)(zero(eltype(A)), zero(eltype(B))))
AA = similar(A, TAB, size(A))
copy!(AA, A)
return UpperTriangular($f2(AA, convert(AbstractMatrix{TAB}, B)))
end
end
end
## The general promotion methods
for (f, g) in ((:*, :A_mul_B!), (:Ac_mul_B, :Ac_mul_B!), (:At_mul_B, :At_mul_B!))
@eval begin
function ($f)(A::AbstractTriangular, B::AbstractTriangular)
TAB = typeof(zero(eltype(A))*zero(eltype(B)) + zero(eltype(A))*zero(eltype(B)))
BB = similar(B, TAB, size(B))
copy!(BB, B)
($g)(convert(AbstractArray{TAB}, A), BB)
end
end
end
for (f, g) in ((:A_mul_Bc, :A_mul_Bc!), (:A_mul_Bt, :A_mul_Bt!))
@eval begin
function ($f)(A::AbstractTriangular, B::AbstractTriangular)
TAB = typeof(zero(eltype(A))*zero(eltype(B)) + zero(eltype(A))*zero(eltype(B)))
AA = similar(A, TAB, size(A))
copy!(AA, A)
($g)(AA, convert(AbstractArray{TAB}, B))
end
end
end
for mat in (:AbstractVector, :AbstractMatrix)
### Multiplication with triangle to the left and hence rhs cannot be transposed.
for (f, g) in ((:*, :A_mul_B!), (:Ac_mul_B, :Ac_mul_B!), (:At_mul_B, :At_mul_B!))
@eval begin
function ($f)(A::AbstractTriangular, B::$mat)
TAB = typeof(zero(eltype(A))*zero(eltype(B)) + zero(eltype(A))*zero(eltype(B)))
BB = similar(B, TAB, size(B))
copy!(BB, B)
($g)(convert(AbstractArray{TAB}, A), BB)
end
end
end
### Left division with triangle to the left hence rhs cannot be transposed. No quotients.
for (f, g) in ((:\, :A_ldiv_B!), (:Ac_ldiv_B, :Ac_ldiv_B!), (:At_ldiv_B, :At_ldiv_B!))
@eval begin
function ($f)(A::Union{UnitUpperTriangular,UnitLowerTriangular}, B::$mat)
TAB = typeof(zero(eltype(A))*zero(eltype(B)) + zero(eltype(A))*zero(eltype(B)))
BB = similar(B, TAB, size(B))
copy!(BB, B)
($g)(convert(AbstractArray{TAB}, A), BB)
end
end
end
### Left division with triangle to the left hence rhs cannot be transposed. Quotients.
for (f, g) in ((:\, :A_ldiv_B!), (:Ac_ldiv_B, :Ac_ldiv_B!), (:At_ldiv_B, :At_ldiv_B!))
@eval begin
function ($f)(A::Union{UpperTriangular,LowerTriangular}, B::$mat)
TAB = typeof((zero(eltype(A))*zero(eltype(B)) + zero(eltype(A))*zero(eltype(B)))/one(eltype(A)))
BB = similar(B, TAB, size(B))
copy!(BB, B)
($g)(convert(AbstractArray{TAB}, A), BB)
end
end
end
### Multiplication with triangle to the right and hence lhs cannot be transposed.
for (f, g) in ((:*, :A_mul_B!), (:A_mul_Bc, :A_mul_Bc!), (:A_mul_Bt, :A_mul_Bt!))
mat != :AbstractVector && @eval begin
function ($f)(A::$mat, B::AbstractTriangular)
TAB = typeof(zero(eltype(A))*zero(eltype(B)) + zero(eltype(A))*zero(eltype(B)))
AA = similar(A, TAB, size(A))
copy!(AA, A)
($g)(AA, convert(AbstractArray{TAB}, B))
end
end
end
### Right division with triangle to the right hence lhs cannot be transposed. No quotients.
for (f, g) in ((:/, :A_rdiv_B!), (:A_rdiv_Bc, :A_rdiv_Bc!), (:A_rdiv_Bt, :A_rdiv_Bt!))
@eval begin
function ($f)(A::$mat, B::Union{UnitUpperTriangular, UnitLowerTriangular})
TAB = typeof(zero(eltype(A))*zero(eltype(B)) + zero(eltype(A))*zero(eltype(B)))
AA = similar(A, TAB, size(A))
copy!(AA, A)
($g)(AA, convert(AbstractArray{TAB}, B))
end
end
end
### Right division with triangle to the right hence lhs cannot be transposed. Quotients.
for (f, g) in ((:/, :A_rdiv_B!), (:A_rdiv_Bc, :A_rdiv_Bc!), (:A_rdiv_Bt, :A_rdiv_Bt!))
@eval begin
function ($f)(A::$mat, B::Union{UpperTriangular,LowerTriangular})
TAB = typeof((zero(eltype(A))*zero(eltype(B)) + zero(eltype(A))*zero(eltype(B)))/one(eltype(A)))
AA = similar(A, TAB, size(A))
copy!(AA, A)
($g)(AA, convert(AbstractArray{TAB}, B))
end
end
end
end
# If these are not defined, they will fallback to the versions in matmul.jl
# and dispatch to generic_matmatmul! which is very costly to compile. The methods
# below might compute an unnecessary copy. Eliminating the copy requires adding
# all the promotion logic here once again. Since these methods are probably relatively
# rare, we chose not to bother for now.
Ac_mul_B(A::AbstractMatrix, B::AbstractTriangular) = (*)(ctranspose(A), B)
At_mul_B(A::AbstractMatrix, B::AbstractTriangular) = (*)(transpose(A), B)
A_mul_Bc(A::AbstractTriangular, B::AbstractMatrix) = (*)(A, ctranspose(B))
A_mul_Bt(A::AbstractTriangular, B::AbstractMatrix) = (*)(A, transpose(B))
Ac_mul_Bc(A::AbstractTriangular, B::AbstractTriangular) = Ac_mul_B(A, B')
Ac_mul_Bc(A::AbstractTriangular, B::AbstractMatrix) = Ac_mul_B(A, B')
Ac_mul_Bc(A::AbstractMatrix, B::AbstractTriangular) = A_mul_Bc(A', B)
At_mul_Bt(A::AbstractTriangular, B::AbstractTriangular) = At_mul_B(A, B.')
At_mul_Bt(A::AbstractTriangular, B::AbstractMatrix) = At_mul_B(A, B.')
At_mul_Bt(A::AbstractMatrix, B::AbstractTriangular) = A_mul_Bt(A.', B)
# Specializations for RowVector
@inline *(rowvec::RowVector, A::AbstractTriangular) = transpose(A * transpose(rowvec))
@inline A_mul_Bt(rowvec::RowVector, A::AbstractTriangular) = transpose(A * transpose(rowvec))
@inline A_mul_Bt(A::AbstractTriangular, rowvec::RowVector) = A * transpose(rowvec)
@inline At_mul_Bt(A::AbstractTriangular, rowvec::RowVector) = A.' * transpose(rowvec)
@inline A_mul_Bc(rowvec::RowVector, A::AbstractTriangular) = ctranspose(A * ctranspose(rowvec))
@inline A_mul_Bc(A::AbstractTriangular, rowvec::RowVector) = A * ctranspose(rowvec)
@inline Ac_mul_Bc(A::AbstractTriangular, rowvec::RowVector) = A' * ctranspose(rowvec)
@inline /(rowvec::RowVector, A::Union{UpperTriangular,LowerTriangular}) = transpose(transpose(A) \ transpose(rowvec))
@inline /(rowvec::RowVector, A::Union{UnitUpperTriangular,UnitLowerTriangular}) = transpose(transpose(A) \ transpose(rowvec))
@inline A_rdiv_Bt(rowvec::RowVector, A::Union{UpperTriangular,LowerTriangular}) = transpose(A \ transpose(rowvec))
@inline A_rdiv_Bt(rowvec::RowVector, A::Union{UnitUpperTriangular,UnitLowerTriangular}) = transpose(A \ transpose(rowvec))
@inline A_rdiv_Bc(rowvec::RowVector, A::Union{UpperTriangular,LowerTriangular}) = ctranspose(A \ ctranspose(rowvec))
@inline A_rdiv_Bc(rowvec::RowVector, A::Union{UnitUpperTriangular,UnitLowerTriangular}) = ctranspose(A \ ctranspose(rowvec))
\(::Union{UpperTriangular,LowerTriangular}, ::RowVector) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector"))
\(::Union{UnitUpperTriangular,UnitLowerTriangular}, ::RowVector) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector"))
At_ldiv_B(::Union{UpperTriangular,LowerTriangular}, ::RowVector) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector"))
At_ldiv_B(::Union{UnitUpperTriangular,UnitLowerTriangular}, ::RowVector) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector"))
Ac_ldiv_B(::Union{UpperTriangular,LowerTriangular}, ::RowVector) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector"))
Ac_ldiv_B(::Union{UnitUpperTriangular,UnitLowerTriangular}, ::RowVector) = throw(DimensionMismatch("Cannot left-divide matrix by transposed vector"))
# Complex matrix power for upper triangular factor, see:
# Higham and Lin, "A Schur-Padé algorithm for fractional powers of a Matrix",
# SIAM J. Matrix Anal. & Appl., 32 (3), (2011) 10561078.
# Higham and Lin, "An improved Schur-Padé algorithm for fractional powers of
# a matrix and their Fréchet derivatives", SIAM. J. Matrix Anal. & Appl.,
# 34(3), (2013) 13411360.
function powm!(A0::UpperTriangular{<:BlasFloat}, p::Real)
if abs(p) >= 1
ArgumentError("p must be a real number in (-1,1), got $p")
end
normA0 = norm(A0, 1)
scale!(A0, 1/normA0)
theta = [1.53e-5, 2.25e-3, 1.92e-2, 6.08e-2, 1.25e-1, 2.03e-1, 2.84e-1]
n = checksquare(A0)
A, m, s = invsquaring(A0, theta)
A = I - A
# Compute accurate diagonal of I - T
sqrt_diag!(A0, A, s)
for i = 1:n
A[i, i] = -A[i, i]
end
# Compute the Padé approximant
c = 0.5 * (p - m) / (2 * m - 1)
triu!(A)
S = c * A
Stmp = similar(S)
for j = m-1:-1:1
j4 = 4 * j
c = (-p - j) / (j4 + 2)
for i = 1:n
@inbounds S[i, i] = S[i, i] + 1
end
copy!(Stmp, S)
scale!(S, A, c)
A_ldiv_B!(Stmp, S.data)
c = (p - j) / (j4 - 2)
for i = 1:n
@inbounds S[i, i] = S[i, i] + 1
end
copy!(Stmp, S)
scale!(S, A, c)
A_ldiv_B!(Stmp, S.data)
end
for i = 1:n
S[i, i] = S[i, i] + 1
end
copy!(Stmp, S)
scale!(S, A, -p)
A_ldiv_B!(Stmp, S.data)
for i = 1:n
@inbounds S[i, i] = S[i, i] + 1
end
blockpower!(A0, S, p/(2^s))
for m = 1:s
A_mul_B!(Stmp.data, S, S)
copy!(S, Stmp)
blockpower!(A0, S, p/(2^(s-m)))
end
scale!(S, normA0^p)
return S
end
powm(A::LowerTriangular, p::Real) = powm(A.', p::Real).'
# Complex matrix logarithm for the upper triangular factor, see:
# Al-Mohy and Higham, "Improved inverse scaling and squaring algorithms for
# the matrix logarithm", SIAM J. Sci. Comput., 34(4), (2012), pp. C153C169.
# Al-Mohy, Higham and Relton, "Computing the Frechet derivative of the matrix
# logarithm and estimating the condition number", SIAM J. Sci. Comput.,
# 35(4), (2013), C394C410.
#
# Based on the code available at http://eprints.ma.man.ac.uk/1851/02/logm.zip,
# Copyright (c) 2011, Awad H. Al-Mohy and Nicholas J. Higham
# Julia version relicensed with permission from original authors
function logm(A0::UpperTriangular{T}) where T<:Union{Float64,Complex{Float64}}
maxsqrt = 100
theta = [1.586970738772063e-005,
2.313807884242979e-003,
1.938179313533253e-002,
6.209171588994762e-002,
1.276404810806775e-001,
2.060962623452836e-001,
2.879093714241194e-001]
tmax = size(theta, 1)
n = size(A0, 1)
A = copy(A0)
p = 0
m = 0
# Compute repeated roots
d = complex(diag(A))
dm1 = d .- 1
s = 0
while norm(dm1, Inf) > theta[tmax] && s < maxsqrt
d .= sqrt.(d)
dm1 .= d .- 1
s = s + 1
end
s0 = s
for k = 1:min(s, maxsqrt)
A = sqrtm(A)
end
AmI = A - I
d2 = sqrt(norm(AmI^2, 1))
d3 = cbrt(norm(AmI^3, 1))
alpha2 = max(d2, d3)
foundm = false
if alpha2 <= theta[2]
m = alpha2 <= theta[1] ? 1 : 2
foundm = true
end
while !foundm
more = false
if s > s0
d3 = cbrt(norm(AmI^3, 1))
end
d4 = norm(AmI^4, 1)^(1/4)
alpha3 = max(d3, d4)
if alpha3 <= theta[tmax]
for j = 3:tmax
if alpha3 <= theta[j]
break
end
end
if j <= 6
m = j
break
elseif alpha3 / 2 <= theta[5] && p < 2
more = true
p = p + 1
end
end
if !more
d5 = norm(AmI^5, 1)^(1/5)
alpha4 = max(d4, d5)
eta = min(alpha3, alpha4)
if eta <= theta[tmax]
j = 0
for j = 6:tmax
if eta <= theta[j]
m = j
break
end
end
break
end
end
if s == maxsqrt
m = tmax
break
end
A = sqrtm(A)
AmI = A - I
s = s + 1
end
# Compute accurate superdiagonal of T
p = 1 / 2^s
for k = 1:n-1
Ak = A0[k,k]
Akp1 = A0[k+1,k+1]
Akp = Ak^p
Akp1p = Akp1^p
A[k,k] = Akp
A[k+1,k+1] = Akp1p
if Ak == Akp1
A[k,k+1] = p * A0[k,k+1] * Ak^(p-1)
elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak)
A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
else
logAk = log(Ak)
logAkp1 = log(Akp1)
w = atanh((Akp1 - Ak)/(Akp1 + Ak)) + im*pi*ceil((imag(logAkp1-logAk)-pi)/(2*pi))
dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak)
A[k,k+1] = A0[k,k+1] * dd
end
end
# Compute accurate diagonal of T
for i = 1:n
a = A0[i,i]
if s == 0
r = a - 1
end
s0 = s
if angle(a) >= pi / 2
a = sqrt(a)
s0 = s - 1
end
z0 = a - 1
a = sqrt(a)
r = 1 + a
for j = 1:s0-1
a = sqrt(a)
r = r * (1 + a)
end
A[i,i] = z0 / r
end
# Get the Gauss-Legendre quadrature points and weights
R = zeros(Float64, m, m)
for i = 1:m - 1
R[i,i+1] = i / sqrt((2 * i)^2 - 1)
R[i+1,i] = R[i,i+1]
end
x,V = eig(R)
w = Vector{Float64}(m)
for i = 1:m
x[i] = (x[i] + 1) / 2
w[i] = V[1,i]^2
end
# Compute the Padé approximation
Y = zeros(T, n, n)
for k = 1:m
Y = Y + w[k] * (A / (x[k] * A + I))
end
# Scale back
scale!(2^s, Y)
# Compute accurate diagonal and superdiagonal of log(T)
for k = 1:n-1
Ak = A0[k,k]
Akp1 = A0[k+1,k+1]
logAk = log(Ak)
logAkp1 = log(Akp1)
Y[k,k] = logAk
Y[k+1,k+1] = logAkp1
if Ak == Akp1
Y[k,k+1] = A0[k,k+1] / Ak
elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak)
Y[k,k+1] = A0[k,k+1] * (logAkp1 - logAk) / (Akp1 - Ak)
else
w = atanh((Akp1 - Ak)/(Akp1 + Ak) + im*pi*(ceil((imag(logAkp1-logAk) - pi)/(2*pi))))
Y[k,k+1] = 2 * A0[k,k+1] * w / (Akp1 - Ak)
end
end
return UpperTriangular(Y)
end
logm(A::LowerTriangular) = logm(A.').'
# Auxiliary functions for logm and matrix power
# Compute accurate diagonal of A = A0^s - I
# Al-Mohy, "A more accurate Briggs method for the logarithm",
# Numer. Algorithms, 59, (2012), 393402.
function sqrt_diag!(A0::UpperTriangular, A::UpperTriangular, s)
n = checksquare(A0)
@inbounds for i = 1:n
a = complex(A0[i,i])
if s == 0
A[i,i] = a - 1
else
s0 = s
if imag(a) >= 0 && real(a) <= 0 && a != 0
a = sqrt(a)
s0 = s - 1
end
z0 = a - 1
a = sqrt(a)
r = 1 + a
for j = 1:s0-1
a = sqrt(a)
r = r * (1 + a)
end
A[i,i] = z0 / r
end
end
end
# Used only by powm at the moment
# Repeatedly compute the square roots of A so that in the end its
# eigenvalues are close enough to the positive real line
function invsquaring(A0::UpperTriangular, theta)
# assumes theta is in ascending order
maxsqrt = 100
tmax = size(theta, 1)
n = checksquare(A0)
A = complex(copy(A0))
p = 0
m = 0
# Compute repeated roots
d = complex(diag(A))
dm1 = d .- 1
s = 0
while norm(dm1, Inf) > theta[tmax] && s < maxsqrt
d .= sqrt.(d)
dm1 .= d .- 1
s = s + 1
end
s0 = s
for k = 1:min(s, maxsqrt)
A = sqrtm(A)
end
AmI = A - I
d2 = sqrt(norm(AmI^2, 1))
d3 = cbrt(norm(AmI^3, 1))
alpha2 = max(d2, d3)
foundm = false
if alpha2 <= theta[2]
m = alpha2 <= theta[1] ? 1 : 2
foundm = true
end
while !foundm
more = false
if s > s0
d3 = cbrt(norm(AmI^3, 1))
end
d4 = norm(AmI^4, 1)^(1/4)
alpha3 = max(d3, d4)
if alpha3 <= theta[tmax]
for j = 3:tmax
if alpha3 <= theta[j]
break
elseif alpha3 / 2 <= theta[5] && p < 2
more = true
p = p + 1
end
end
if j <= 6
m = j
foundm = true
break
elseif alpha3 / 2 <= theta[5] && p < 2
more = true
p = p + 1
end
end
if !more
d5 = norm(AmI^5, 1)^(1/5)
alpha4 = max(d4, d5)
eta = min(alpha3, alpha4)
if eta <= theta[tmax]
j = 0
for j = 6:tmax
if eta <= theta[j]
m = j
break
end
break
end
end
if s == maxsqrt
m = tmax
break
end
A = sqrtm(A)
AmI = A - I
s = s + 1
end
end
# Compute accurate superdiagonal of T
p = 1 / 2^s
A = complex(A)
blockpower!(A, A0, p)
return A,m,s
end
# Compute accurate diagonal and superdiagonal of A = A0^p
function blockpower!(A::UpperTriangular, A0::UpperTriangular, p)
n = checksquare(A0)
@inbounds for k = 1:n-1
Ak = complex(A0[k,k])
Akp1 = complex(A0[k+1,k+1])
Akp = Ak^p
Akp1p = Akp1^p
A[k,k] = Akp
A[k+1,k+1] = Akp1p
if Ak == Akp1
A[k,k+1] = p * A0[k,k+1] * Ak^(p-1)
elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak)
A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
else
logAk = log(Ak)
logAkp1 = log(Akp1)
w = atanh((Akp1 - Ak)/(Akp1 + Ak)) + im * pi * unw(logAkp1-logAk)
dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak);
A[k,k+1] = A0[k,k+1] * dd
end
end
end
# Unwinding number
unw(x::Real) = 0
unw(x::Number) = ceil((imag(x) - pi) / (2 * pi))
# End of auxiliary functions for logm and matrix power
function sqrtm(A::UpperTriangular)
realmatrix = false
if isreal(A)
realmatrix = true
for i = 1:checksquare(A)
x = real(A[i,i])
if x < zero(x)
realmatrix = false
break
end
end
end
sqrtm(A,Val{realmatrix})
end
function sqrtm(A::UpperTriangular{T},::Type{Val{realmatrix}}) where {T,realmatrix}
B = A.data
n = checksquare(B)
t = realmatrix ? typeof(sqrt(zero(T))) : typeof(sqrt(complex(zero(T))))
R = zeros(t, n, n)
tt = typeof(zero(t)*zero(t))
@inbounds for j = 1:n
R[j,j] = realmatrix ? sqrt(B[j,j]) : sqrt(complex(B[j,j]))
for i = j-1:-1:1
r::tt = B[i,j]
@simd for k = i+1:j-1
r -= R[i,k]*R[k,j]
end
iszero(r) || (R[i,j] = sylvester(R[i,i],R[j,j],-r))
end
end
return UpperTriangular(R)
end
function sqrtm(A::UnitUpperTriangular{T}) where T
B = A.data
n = checksquare(B)
t = typeof(sqrt(zero(T)))
R = eye(t, n, n)
tt = typeof(zero(t)*zero(t))
half = inv(R[1,1]+R[1,1]) # for general, algebraic cases. PR#20214
@inbounds for j = 1:n
for i = j-1:-1:1
r::tt = B[i,j]
@simd for k = i+1:j-1
r -= R[i,k]*R[k,j]
end
r==0 || (R[i,j] = half*r)
end
end
return UnitUpperTriangular(R)
end
sqrtm(A::LowerTriangular) = sqrtm(A.').'
sqrtm(A::UnitLowerTriangular) = sqrtm(A.').'
# Generic eigensystems
eigvals(A::AbstractTriangular) = diag(A)
function eigvecs(A::AbstractTriangular{T}) where T
TT = promote_type(T, Float32)
if TT <: BlasFloat
return eigvecs(convert(AbstractMatrix{TT}, A))
else
throw(ArgumentError("eigvecs type $(typeof(A)) not supported. Please submit a pull request."))
end
end
det(A::UnitUpperTriangular{T}) where {T} = one(T)
det(A::UnitLowerTriangular{T}) where {T} = one(T)
logdet(A::UnitUpperTriangular{T}) where {T} = zero(T)
logdet(A::UnitLowerTriangular{T}) where {T} = zero(T)
logabsdet(A::UnitUpperTriangular{T}) where {T} = zero(T), one(T)
logabsdet(A::UnitLowerTriangular{T}) where {T} = zero(T), one(T)
det(A::UpperTriangular) = prod(diag(A.data))
det(A::LowerTriangular) = prod(diag(A.data))
function logabsdet(A::Union{UpperTriangular{T},LowerTriangular{T}}) where T
sgn = one(T)
abs_det = zero(real(T))
@inbounds for i in 1:size(A,1)
diag_i = A.data[i,i]
sgn *= sign(diag_i)
abs_det += log(abs(diag_i))
end
return abs_det, sgn
end
eigfact(A::AbstractTriangular) = Eigen(eigvals(A), eigvecs(A))
# Generic singular systems
for func in (:svd, :svdfact, :svdfact!, :svdvals)
@eval begin
($func)(A::AbstractTriangular) = ($func)(full(A))
end
end
factorize(A::AbstractTriangular) = A