Skip to content

Commit db1cd35

Browse files
RalphASandreasnoack
authored andcommitted
patches to matrix log (#33245)
* patches to matrix log Avoid integer overflow if `s > 63`. Correct logic for `s == 0`. Only use fancy divided difference formulae if eigenvalues are close - avoids dangerous roundoff error if they are in opposite sectors. * add tests
1 parent fa16d78 commit db1cd35

File tree

2 files changed

+26
-8
lines changed

2 files changed

+26
-8
lines changed

stdlib/LinearAlgebra/src/triangular.jl

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2183,13 +2183,14 @@ function log(A0::UpperTriangular{T}) where T<:BlasFloat
21832183
end
21842184

21852185
# Compute accurate superdiagonal of T
2186-
blockpower!(A, A0, 1 / 2^s)
2186+
blockpower!(A, A0, 0.5^s)
21872187

21882188
# Compute accurate diagonal of T
21892189
for i = 1:n
21902190
a = A0[i,i]
21912191
if s == 0
2192-
r = a - 1
2192+
A[i,i] = a - 1
2193+
continue
21932194
end
21942195
s0 = s
21952196
if angle(a) >= pi / 2
@@ -2226,7 +2227,7 @@ function log(A0::UpperTriangular{T}) where T<:BlasFloat
22262227
end
22272228

22282229
# Scale back
2229-
lmul!(2^s, Y)
2230+
lmul!(2.0^s, Y)
22302231

22312232
# Compute accurate diagonal and superdiagonal of log(T)
22322233
for k = 1:n-1
@@ -2242,8 +2243,12 @@ function log(A0::UpperTriangular{T}) where T<:BlasFloat
22422243
Y[k,k+1] = A0[k,k+1] * (logAkp1 - logAk) / (Akp1 - Ak)
22432244
else
22442245
z = (Akp1 - Ak)/(Akp1 + Ak)
2245-
w = atanh(z) + im * pi * (unw(logAkp1-logAk) + unw(log1p(z)-log1p(-z)))
2246-
Y[k,k+1] = 2 * A0[k,k+1] * w / (Akp1 - Ak)
2246+
if abs(z) > 1
2247+
Y[k,k+1] = A0[k,k+1] * (logAkp1 - logAk) / (Akp1 - Ak)
2248+
else
2249+
w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z)))
2250+
Y[k,k+1] = 2 * A0[k,k+1] * w / (Akp1 - Ak)
2251+
end
22472252
end
22482253
end
22492254

@@ -2396,9 +2401,13 @@ function blockpower!(A::UpperTriangular, A0::UpperTriangular, p)
23962401
logAk = log(Ak)
23972402
logAkp1 = log(Akp1)
23982403
z = (Akp1 - Ak)/(Akp1 + Ak)
2399-
w = atanh(z) + im * pi * (unw(logAkp1-logAk) + unw(log1p(z)-log1p(-z)))
2400-
dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak);
2401-
A[k,k+1] = A0[k,k+1] * dd
2404+
if abs(z) > 1
2405+
A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
2406+
else
2407+
w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z)))
2408+
dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak);
2409+
A[k,k+1] = A0[k,k+1] * dd
2410+
end
24022411
end
24032412
end
24042413
end

stdlib/LinearAlgebra/test/dense.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,4 +909,13 @@ end
909909
end
910910
end
911911

912+
@testset "Matrix log PR #33245" begin
913+
# edge case for divided difference
914+
A1 = triu(ones(3,3),1) + diagm([1.0, -2eps()-1im, -eps()+0.75im])
915+
@test exp(log(A1)) A1
916+
# case where no sqrt is needed (s=0)
917+
A2 = [1.01 0.01 0.01; 0 1.01 0.01; 0 0 1.01]
918+
@test exp(log(A2)) A2
919+
end
920+
912921
end # module TestDense

0 commit comments

Comments
 (0)