Skip to content

Commit d1f64fd

Browse files
committed
refactor: delegate eigenvalue transforms to the DiagonalAlgorithm kernels
In the `MatrixFunctionViaEig(h)` implementations of `squareroot`, `logarithm` and `power`, apply the scalar function to the eigenvalue matrix by calling the function's own `DiagonalAlgorithm` kernel (which already contains the domain checks and tolerance clamping), mirroring how `exponential!` recurses into `exponential!((τ, D), D)`. The eig kernels only retain the negative-real-axis scan of the complex eigenvalues for real input, which has no `Diagonal` counterpart.
1 parent 5ab2c78 commit d1f64fd

3 files changed

Lines changed: 27 additions & 50 deletions

File tree

src/implementations/logarithm.jl

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,31 +48,23 @@ end
4848
function logarithm!(A::AbstractMatrix, logA, alg::MatrixFunctionViaEigh)
4949
check_input(logarithm!, A, logA, alg)
5050
D, V = eigh_full!(A, alg.eigh_alg)
51-
λ = diagview(D)
52-
atol = something(alg.domain_atol, default_domain_atol(λ))
53-
_check_nonzero_eigenvalues(λ, atol)
54-
_clamp_domain_eigenvalues!(λ, atol)
55-
λ .= log.(λ)
56-
VD = V * D
51+
VD = V * logarithm!(D, D, DiagonalAlgorithm(; domain_atol = alg.domain_atol))
5752
mul!(logA, VD, V')
5853
return project_hermitian!(logA)
5954
end
6055

6156
function logarithm!(A::AbstractMatrix, logA, alg::MatrixFunctionViaEig)
6257
check_input(logarithm!, A, logA, alg)
6358
D, V = eig_full!(A, alg.eig_alg)
64-
λ = diagview(D)
65-
atol = something(alg.domain_atol, default_domain_atol(λ))
66-
_check_nonzero_eigenvalues(λ, atol)
59+
diag_alg = DiagonalAlgorithm(; domain_atol = alg.domain_atol)
6760
if eltype(A) <: Real
68-
_clamp_domain_eigenvalues!(λ, atol)
69-
λ .= log.(λ)
70-
VD = V * D
71-
logAc = rdiv!(VD, LinearAlgebra.lu!(V))
61+
atol = something(alg.domain_atol, default_domain_atol(diagview(D)))
62+
_clamp_domain_eigenvalues!(diagview(D), atol)
63+
VlogD = V * logarithm!(D, D, diag_alg)
64+
logAc = rdiv!(VlogD, LinearAlgebra.lu!(V))
7265
return logA .= real.(logAc)
7366
else
74-
λ .= log.(λ)
75-
logA .= V .* transpose(λ)
67+
logA .= V .* transpose(diagview(logarithm!(D, D, diag_alg)))
7668
return rdiv!(logA, LinearAlgebra.lu!(V))
7769
end
7870
end

src/implementations/power.jl

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -54,43 +54,32 @@ end
5454
function power!(A::AbstractMatrix, p::Real, powA, alg::MatrixFunctionViaEigh)
5555
check_input(power!, A, p, powA, alg)
5656
D, V = eigh_full!(A, alg.eigh_alg)
57-
λ = diagview(D)
57+
diag_alg = DiagonalAlgorithm(; domain_atol = alg.domain_atol)
5858
if isinteger(p)
59-
p < 0 && any(iszero, λ) && throw(LinearAlgebra.SingularException(0))
60-
λ .= λ .^ p
61-
VD = V * D
59+
VD = V * power!(D, p, D, diag_alg)
6260
mul!(powA, VD, V')
6361
return project_hermitian!(powA)
6462
else
65-
atol = something(alg.domain_atol, default_domain_atol(λ))
66-
p < 0 && _check_nonzero_eigenvalues(λ, atol)
67-
_clamp_domain_eigenvalues!(λ, atol)
6863
# `A^p = (V * D^(p/2)) * (V * D^(p/2))'` is hermitian by construction
69-
λ .= λ .^ (p / 2)
70-
Vs = rmul!(V, D)
64+
Vs = rmul!(V, power!(D, p / 2, D, diag_alg))
7165
return _mul_herm!(powA, Vs)
7266
end
7367
end
7468

7569
function power!(A::AbstractMatrix, p::Real, powA, alg::MatrixFunctionViaEig)
7670
check_input(power!, A, p, powA, alg)
7771
D, V = eig_full!(A, alg.eig_alg)
78-
λ = diagview(D)
79-
if isinteger(p)
80-
p < 0 && any(iszero, λ) && throw(LinearAlgebra.SingularException(0))
81-
else
82-
atol = something(alg.domain_atol, default_domain_atol(λ))
83-
p < 0 && _check_nonzero_eigenvalues(λ, atol)
84-
eltype(A) <: Real && _clamp_domain_eigenvalues!(λ, atol)
85-
end
72+
diag_alg = DiagonalAlgorithm(; domain_atol = alg.domain_atol)
8673
if eltype(A) <: Real
87-
λ .= λ .^ p
88-
VD = V * D
89-
powAc = rdiv!(VD, LinearAlgebra.lu!(V))
74+
if !isinteger(p)
75+
atol = something(alg.domain_atol, default_domain_atol(diagview(D)))
76+
_clamp_domain_eigenvalues!(diagview(D), atol)
77+
end
78+
VpD = V * power!(D, p, D, diag_alg)
79+
powAc = rdiv!(VpD, LinearAlgebra.lu!(V))
9080
return powA .= real.(powAc)
9181
else
92-
λ .= λ .^ p
93-
powA .= V .* transpose(λ)
82+
powA .= V .* transpose(diagview(power!(D, p, D, diag_alg)))
9483
return rdiv!(powA, LinearAlgebra.lu!(V))
9584
end
9685
end

src/implementations/squareroot.jl

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,29 +48,25 @@ end
4848
function squareroot!(A::AbstractMatrix, sqrtA, alg::MatrixFunctionViaEigh)
4949
check_input(squareroot!, A, sqrtA, alg)
5050
D, V = eigh_full!(A, alg.eigh_alg)
51-
λ = diagview(D)
52-
atol = something(alg.domain_atol, default_domain_atol(λ))
53-
_clamp_domain_eigenvalues!(λ, atol)
51+
diag_alg = DiagonalAlgorithm(; domain_atol = alg.domain_atol)
5452
# `sqrt(A) = (V * D^(1/4)) * (V * D^(1/4))'` is hermitian by construction
55-
λ .= sqrt.(sqrt.(λ))
56-
Vs = rmul!(V, D)
53+
sqrtD = squareroot!(D, D, diag_alg)
54+
Vs = rmul!(V, squareroot!(sqrtD, sqrtD, diag_alg))
5755
return _mul_herm!(sqrtA, Vs)
5856
end
5957

6058
function squareroot!(A::AbstractMatrix, sqrtA, alg::MatrixFunctionViaEig)
6159
check_input(squareroot!, A, sqrtA, alg)
6260
D, V = eig_full!(A, alg.eig_alg)
63-
λ = diagview(D)
61+
diag_alg = DiagonalAlgorithm(; domain_atol = alg.domain_atol)
6462
if eltype(A) <: Real
65-
atol = something(alg.domain_atol, default_domain_atol(λ))
66-
_clamp_domain_eigenvalues!(λ, atol)
67-
λ .= sqrt.(λ)
68-
VD = V * D
69-
sqrtAc = rdiv!(VD, LinearAlgebra.lu!(V))
63+
atol = something(alg.domain_atol, default_domain_atol(diagview(D)))
64+
_clamp_domain_eigenvalues!(diagview(D), atol)
65+
VsqrtD = V * squareroot!(D, D, diag_alg)
66+
sqrtAc = rdiv!(VsqrtD, LinearAlgebra.lu!(V))
7067
return sqrtA .= real.(sqrtAc)
7168
else
72-
λ .= sqrt.(λ)
73-
sqrtA .= V .* transpose(λ)
69+
sqrtA .= V .* transpose(diagview(squareroot!(D, D, diag_alg)))
7470
return rdiv!(sqrtA, LinearAlgebra.lu!(V))
7571
end
7672
end

0 commit comments

Comments
 (0)