# This file is a part of Julia. License is MIT: https://julialang.org/license # Methods operating on different special matrix types # Interconversion between special matrix types convert(::Type{Bidiagonal}, A::Diagonal{T}) where {T} = Bidiagonal(A.diag, zeros(T, size(A.diag,1)-1), true) convert(::Type{SymTridiagonal}, A::Diagonal{T}) where {T} = SymTridiagonal(A.diag, zeros(T, size(A.diag,1)-1)) convert(::Type{Tridiagonal}, A::Diagonal{T}) where {T} = Tridiagonal(zeros(T, size(A.diag,1)-1), A.diag, zeros(T, size(A.diag,1)-1)) function convert(::Type{Diagonal}, A::Union{Bidiagonal, SymTridiagonal}) if !iszero(A.ev) throw(ArgumentError("matrix cannot be represented as Diagonal")) end Diagonal(A.dv) end function convert(::Type{SymTridiagonal}, A::Bidiagonal) if !iszero(A.ev) throw(ArgumentError("matrix cannot be represented as SymTridiagonal")) end SymTridiagonal(A.dv, A.ev) end convert(::Type{Tridiagonal}, A::Bidiagonal{T}) where {T} = Tridiagonal(A.isupper ? zeros(T, size(A.dv,1)-1) : A.ev, A.dv, A.isupper ? A.ev:zeros(T, size(A.dv,1)-1)) function convert(::Type{Bidiagonal}, A::SymTridiagonal) if !iszero(A.ev) throw(ArgumentError("matrix cannot be represented as Bidiagonal")) end Bidiagonal(A.dv, A.ev, true) end function convert(::Type{Diagonal}, A::Tridiagonal) if !(iszero(A.dl) && iszero(A.du)) throw(ArgumentError("matrix cannot be represented as Diagonal")) end Diagonal(A.d) end function convert(::Type{Bidiagonal}, A::Tridiagonal) if iszero(A.dl) return Bidiagonal(A.d, A.du, true) elseif iszero(A.du) return Bidiagonal(A.d, A.dl, false) else throw(ArgumentError("matrix cannot be represented as Bidiagonal")) end end function convert(::Type{SymTridiagonal}, A::Tridiagonal) if A.dl != A.du throw(ArgumentError("matrix cannot be represented as SymTridiagonal")) end SymTridiagonal(A.d, A.dl) end function convert(::Type{Tridiagonal}, A::SymTridiagonal) Tridiagonal(copy(A.ev), A.dv, A.ev) end function convert(::Type{Diagonal}, A::AbstractTriangular) if full(A) != diagm(diag(A)) throw(ArgumentError("matrix cannot be represented as Diagonal")) end Diagonal(diag(A)) end function convert(::Type{Bidiagonal}, A::AbstractTriangular) fA = full(A) if fA == diagm(diag(A)) + diagm(diag(fA, 1), 1) return Bidiagonal(diag(A), diag(fA,1), true) elseif fA == diagm(diag(A)) + diagm(diag(fA, -1), -1) return Bidiagonal(diag(A), diag(fA,-1), false) else throw(ArgumentError("matrix cannot be represented as Bidiagonal")) end end convert(::Type{SymTridiagonal}, A::AbstractTriangular) = convert(SymTridiagonal, convert(Tridiagonal, A)) function convert(::Type{Tridiagonal}, A::AbstractTriangular) fA = full(A) if fA == diagm(diag(A)) + diagm(diag(fA, 1), 1) + diagm(diag(fA, -1), -1) return Tridiagonal(diag(fA, -1), diag(A), diag(fA,1)) else throw(ArgumentError("matrix cannot be represented as Tridiagonal")) end end # Constructs two method definitions taking into account (assumed) commutativity # e.g. @commutative f{S,T}(x::S, y::T) = x+y is the same is defining # f{S,T}(x::S, y::T) = x+y # f{S,T}(y::T, x::S) = f(x, y) macro commutative(myexpr) @assert myexpr.head===:(=) || myexpr.head===:function # Make sure it is a function definition y = copy(myexpr.args[1].args[2:end]) reverse!(y) reversed_call = Expr(:(=), Expr(:call,myexpr.args[1].args[1],y...), myexpr.args[1]) esc(Expr(:block, myexpr, reversed_call)) end for op in (:+, :-) SpecialMatrices = [:Diagonal, :Bidiagonal, :Tridiagonal, :Matrix] for (idx, matrixtype1) in enumerate(SpecialMatrices) # matrixtype1 is the sparser matrix type for matrixtype2 in SpecialMatrices[idx+1:end] # matrixtype2 is the denser matrix type @eval begin # TODO quite a few of these conversions are NOT defined ($op)(A::($matrixtype1), B::($matrixtype2)) = ($op)(convert(($matrixtype2), A), B) ($op)(A::($matrixtype2), B::($matrixtype1)) = ($op)(A, convert(($matrixtype2), B)) end end end for matrixtype1 in (:SymTridiagonal,) # matrixtype1 is the sparser matrix type for matrixtype2 in (:Tridiagonal, :Matrix) # matrixtype2 is the denser matrix type @eval begin ($op)(A::($matrixtype1), B::($matrixtype2)) = ($op)(convert(($matrixtype2), A), B) ($op)(A::($matrixtype2), B::($matrixtype1)) = ($op)(A, convert(($matrixtype2), B)) end end end for matrixtype1 in (:Diagonal, :Bidiagonal) # matrixtype1 is the sparser matrix type for matrixtype2 in (:SymTridiagonal,) # matrixtype2 is the denser matrix type @eval begin ($op)(A::($matrixtype1), B::($matrixtype2)) = ($op)(convert(($matrixtype2), A), B) ($op)(A::($matrixtype2), B::($matrixtype1)) = ($op)(A, convert(($matrixtype2), B)) end end end for matrixtype1 in (:Diagonal,) for (matrixtype2,matrixtype3) in ((:UpperTriangular,:UpperTriangular), (:UnitUpperTriangular,:UpperTriangular), (:LowerTriangular,:LowerTriangular), (:UnitLowerTriangular,:LowerTriangular)) @eval begin ($op)(A::($matrixtype1), B::($matrixtype2)) = ($op)(($matrixtype3)(A), B) ($op)(A::($matrixtype2), B::($matrixtype1)) = ($op)(A, ($matrixtype3)(B)) end end end for matrixtype in (:SymTridiagonal,:Tridiagonal,:Bidiagonal,:Matrix) @eval begin ($op)(A::AbstractTriangular, B::($matrixtype)) = ($op)(full(A), B) ($op)(A::($matrixtype), B::AbstractTriangular) = ($op)(A, full(B)) end end end A_mul_Bc!(A::AbstractTriangular, B::QRCompactWYQ) = A_mul_Bc!(full!(A),B) A_mul_Bc!(A::AbstractTriangular, B::QRPackedQ) = A_mul_Bc!(full!(A),B) A_mul_Bc(A::AbstractTriangular, B::Union{QRCompactWYQ,QRPackedQ}) = A_mul_Bc(full(A), B)