Skip to content

Commit 3e4d569

Browse files
authored
Revert "Cleanup and generalize functions of Hermitian matrices (#1340)" (#1357)
This reverts commit 611395d. Unfortunately, this PR introduced some method ambiguities that weren't caught by the CI, as the ambiguity test was broken. After the fix in #1349, this now causes test failures on master (https://buildkite.com/julialang/linearalgebra-dot-jl/builds/740#0196c86e-2d8c-42c2-b9ae-a2143427f43e). Please re-land the PR after fixing the ambiguities. CC: @araujoms
1 parent 0b46d5c commit 3e4d569

File tree

2 files changed

+110
-58
lines changed

2 files changed

+110
-58
lines changed

src/symmetric.jl

Lines changed: 110 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -224,15 +224,11 @@ const RealHermSymSymTri{T<:Real} = Union{RealHermSym{T}, SymTridiagonal{T}}
224224
const RealHermSymComplexHerm{T<:Real,S} = Union{Hermitian{T,S}, Symmetric{T,S}, Hermitian{Complex{T},S}}
225225
const RealHermSymComplexSym{T<:Real,S} = Union{Hermitian{T,S}, Symmetric{T,S}, Symmetric{Complex{T},S}}
226226
const RealHermSymSymTriComplexHerm{T<:Real} = Union{RealHermSymComplexSym{T}, SymTridiagonal{T}}
227-
const SelfAdjoint = Union{SymTridiagonal{<:Real}, Symmetric{<:Real}, Hermitian}
227+
const SelfAdjoint = Union{Symmetric{<:Real}, Hermitian{<:Number}}
228228

229229
wrappertype(::Union{Symmetric, SymTridiagonal}) = Symmetric
230230
wrappertype(::Hermitian) = Hermitian
231231

232-
nonhermitianwrappertype(::SymSymTri{<:Real}) = Symmetric
233-
nonhermitianwrappertype(::Hermitian{<:Real}) = Symmetric
234-
nonhermitianwrappertype(::Hermitian) = identity
235-
236232
size(A::HermOrSym) = size(A.data)
237233
axes(A::HermOrSym) = axes(A.data)
238234
@inline function Base.isassigned(A::HermOrSym, i::Int, j::Int)
@@ -838,74 +834,119 @@ end
838834
^(A::Symmetric{<:Complex}, p::Integer) = sympow(A, p)
839835
^(A::SymTridiagonal{<:Real}, p::Integer) = sympow(A, p)
840836
^(A::SymTridiagonal{<:Complex}, p::Integer) = sympow(A, p)
841-
^(A::Hermitian, p::Integer) = sympow(A, p)
842-
function sympow(A, p::Integer)
837+
function sympow(A::SymSymTri, p::Integer)
838+
if p < 0
839+
return Symmetric(Base.power_by_squaring(inv(A), -p))
840+
else
841+
return Symmetric(Base.power_by_squaring(A, p))
842+
end
843+
end
844+
for hermtype in (:Symmetric, :SymTridiagonal)
845+
@eval begin
846+
function ^(A::$hermtype{<:Real}, p::Real)
847+
isinteger(p) && return integerpow(A, p)
848+
F = eigen(A)
849+
if all-> λ 0, F.values)
850+
return Symmetric((F.vectors * Diagonal((F.values).^p)) * F.vectors')
851+
else
852+
return Symmetric((F.vectors * Diagonal(complex.(F.values).^p)) * F.vectors')
853+
end
854+
end
855+
function ^(A::$hermtype{<:Complex}, p::Real)
856+
isinteger(p) && return integerpow(A, p)
857+
return Symmetric(schurpow(A, p))
858+
end
859+
end
860+
end
861+
function ^(A::Hermitian, p::Integer)
843862
if p < 0
844863
retmat = Base.power_by_squaring(inv(A), -p)
845864
else
846865
retmat = Base.power_by_squaring(A, p)
847866
end
848-
return wrappertype(A)(retmat)
867+
return Hermitian(retmat)
849868
end
850-
function ^(A::SelfAdjoint, p::Real)
869+
function ^(A::Hermitian{T}, p::Real) where T
851870
isinteger(p) && return integerpow(A, p)
852871
F = eigen(A)
853872
if all-> λ 0, F.values)
854873
retmat = (F.vectors * Diagonal((F.values).^p)) * F.vectors'
855-
return wrappertype(A)(retmat)
874+
return Hermitian(retmat)
856875
else
857-
retmat = (F.vectors * Diagonal(complex.(F.values).^p)) * F.vectors'
858-
return nonhermitianwrappertype(A)(retmat)
876+
retmat = (F.vectors * Diagonal((complex.(F.values).^p))) * F.vectors'
877+
if T <: Real
878+
return Symmetric(retmat)
879+
else
880+
return retmat
881+
end
859882
end
860883
end
861-
function ^(A::SymSymTri{<:Complex}, p::Real)
862-
isinteger(p) && return integerpow(A, p)
863-
return Symmetric(schurpow(A, p))
864-
end
865884

866885
for func in (:exp, :cos, :sin, :tan, :cosh, :sinh, :tanh, :atan, :asinh, :atanh, :cbrt)
867886
@eval begin
868-
function ($func)(A::SelfAdjoint)
887+
function ($func)(A::RealHermSymSymTri)
888+
F = eigen(A)
889+
return wrappertype(A)((F.vectors * Diagonal(($func).(F.values))) * F.vectors')
890+
end
891+
function ($func)(A::Hermitian{<:Complex})
869892
F = eigen(A)
870893
retmat = (F.vectors * Diagonal(($func).(F.values))) * F.vectors'
871-
return wrappertype(A)(retmat)
894+
return Hermitian(retmat)
872895
end
873896
end
874897
end
875898

876-
function cis(A::SelfAdjoint)
899+
function cis(A::RealHermSymSymTri)
877900
F = eigen(A)
878-
retmat = F.vectors .* cis.(F.values') * F.vectors'
879-
return nonhermitianwrappertype(A)(retmat)
901+
return Symmetric(F.vectors .* cis.(F.values') * F.vectors')
880902
end
903+
function cis(A::Hermitian{<:Complex})
904+
F = eigen(A)
905+
return F.vectors .* cis.(F.values') * F.vectors'
906+
end
907+
881908

882909
for func in (:acos, :asin)
883910
@eval begin
884-
function ($func)(A::SelfAdjoint)
911+
function ($func)(A::RealHermSymSymTri)
912+
F = eigen(A)
913+
if all-> -1 λ 1, F.values)
914+
return wrappertype(A)((F.vectors * Diagonal(($func).(F.values))) * F.vectors')
915+
else
916+
return Symmetric((F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors')
917+
end
918+
end
919+
function ($func)(A::Hermitian{<:Complex})
885920
F = eigen(A)
886921
if all-> -1 λ 1, F.values)
887922
retmat = (F.vectors * Diagonal(($func).(F.values))) * F.vectors'
888-
return wrappertype(A)(retmat)
923+
return Hermitian(retmat)
889924
else
890-
retmat = (F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors'
891-
return nonhermitianwrappertype(A)(retmat)
925+
return (F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors'
892926
end
893927
end
894928
end
895929
end
896930

897-
function acosh(A::SelfAdjoint)
931+
function acosh(A::RealHermSymSymTri)
932+
F = eigen(A)
933+
if all-> λ 1, F.values)
934+
return wrappertype(A)((F.vectors * Diagonal(acosh.(F.values))) * F.vectors')
935+
else
936+
return Symmetric((F.vectors * Diagonal(acosh.(complex.(F.values)))) * F.vectors')
937+
end
938+
end
939+
function acosh(A::Hermitian{<:Complex})
898940
F = eigen(A)
899941
if all-> λ 1, F.values)
900942
retmat = (F.vectors * Diagonal(acosh.(F.values))) * F.vectors'
901-
return wrappertype(A)(retmat)
943+
return Hermitian(retmat)
902944
else
903-
retmat = (F.vectors * Diagonal(acosh.(complex.(F.values)))) * F.vectors'
904-
return nonhermitianwrappertype(A)(retmat)
945+
return (F.vectors * Diagonal(acosh.(complex.(F.values)))) * F.vectors'
905946
end
906947
end
907948

908-
function sincos(A::SelfAdjoint)
949+
function sincos(A::RealHermSymSymTri)
909950
n = checksquare(A)
910951
F = eigen(A)
911952
T = float(eltype(F.values))
@@ -915,28 +956,49 @@ function sincos(A::SelfAdjoint)
915956
end
916957
return wrappertype(A)((F.vectors * S) * F.vectors'), wrappertype(A)((F.vectors * C) * F.vectors')
917958
end
918-
919-
function log(A::SelfAdjoint)
959+
function sincos(A::Hermitian{<:Complex})
960+
n = checksquare(A)
920961
F = eigen(A)
921-
if all-> λ 0, F.values)
922-
retmat = (F.vectors * Diagonal(log.(F.values))) * F.vectors'
923-
return wrappertype(A)(retmat)
924-
else
925-
retmat = (F.vectors * Diagonal(log.(complex.(F.values)))) * F.vectors'
926-
return nonhermitianwrappertype(A)(retmat)
962+
T = float(eltype(F.values))
963+
S, C = Diagonal(similar(A, T, (n,))), Diagonal(similar(A, T, (n,)))
964+
for i in eachindex(S.diag, C.diag, F.values)
965+
S.diag[i], C.diag[i] = sincos(F.values[i])
966+
end
967+
retmatS, retmatC = (F.vectors * S) * F.vectors', (F.vectors * C) * F.vectors'
968+
for i in diagind(retmatS, IndexStyle(retmatS))
969+
retmatS[i] = real(retmatS[i])
970+
retmatC[i] = real(retmatC[i])
927971
end
972+
return Hermitian(retmatS), Hermitian(retmatC)
928973
end
929974

930-
# sqrt has rtol kwarg to handle matrices that are semidefinite up to roundoff errors
931-
function sqrt(A::SelfAdjoint; rtol = eps(real(float(eltype(A)))) * size(A, 1))
932-
F = eigen(A)
933-
λ₀ = -maximum(abs, F.values) * rtol # treat λ ≥ λ₀ as "zero" eigenvalues up to roundoff
934-
if all-> λ λ₀, F.values)
935-
retmat = (F.vectors * Diagonal(sqrt.(max.(0, F.values)))) * F.vectors'
936-
return wrappertype(A)(retmat)
937-
else
938-
retmat = (F.vectors * Diagonal(sqrt.(complex.(F.values)))) * F.vectors'
939-
return nonhermitianwrappertype(A)(retmat)
975+
976+
for func in (:log, :sqrt)
977+
# sqrt has rtol arg to handle matrices that are semidefinite up to roundoff errors
978+
rtolarg = func === :sqrt ? Any[Expr(:kw, :(rtol::Real), :(eps(real(float(one(T))))*size(A,1)))] : Any[]
979+
rtolval = func === :sqrt ? :(-maximum(abs, F.values) * rtol) : 0
980+
@eval begin
981+
function ($func)(A::RealHermSymSymTri{T}; $(rtolarg...)) where {T<:Real}
982+
F = eigen(A)
983+
λ₀ = $rtolval # treat λ ≥ λ₀ as "zero" eigenvalues up to roundoff
984+
if all-> λ λ₀, F.values)
985+
return wrappertype(A)((F.vectors * Diagonal(($func).(max.(0, F.values)))) * F.vectors')
986+
else
987+
return Symmetric((F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors')
988+
end
989+
end
990+
function ($func)(A::Hermitian{T}; $(rtolarg...)) where {T<:Complex}
991+
n = checksquare(A)
992+
F = eigen(A)
993+
λ₀ = $rtolval # treat λ ≥ λ₀ as "zero" eigenvalues up to roundoff
994+
if all-> λ λ₀, F.values)
995+
retmat = (F.vectors * Diagonal(($func).(max.(0, F.values)))) * F.vectors'
996+
return Hermitian(retmat)
997+
else
998+
retmat = (F.vectors * Diagonal(($func).(complex.(F.values)))) * F.vectors'
999+
return retmat
1000+
end
1001+
end
9401002
end
9411003
end
9421004

test/symmetric.jl

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,14 +1199,4 @@ end
11991199
end
12001200
end
12011201

1202-
@testset "asin/acos/acosh for matrix outside the real domain" begin
1203-
M = [0 2;2 0] #eigenvalues are ±2
1204-
for T (Float32, Float64, ComplexF32, ComplexF64)
1205-
M2 = Hermitian(T.(M))
1206-
@test sin(asin(M2)) M2
1207-
@test cos(acos(M2)) M2
1208-
@test cosh(acosh(M2)) M2
1209-
end
1210-
end
1211-
12121202
end # module TestSymmetric

0 commit comments

Comments
 (0)