2209 lines
72 KiB
Julia
2209 lines
72 KiB
Julia
# 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) 1056–1078.
|
||
# 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) 1341–1360.
|
||
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. C153–C169.
|
||
# 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), C394–C410.
|
||
#
|
||
# 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), 393–402.
|
||
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
|