Skip to content

Commit db0bc60

Browse files
stevengjKristofferC
authored andcommitted
bug fixes in matrix log (#32327)
* bug fixes in matrix log * 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 (cherry picked from commit 318affa)
1 parent f7279ba commit db0bc60

File tree

8 files changed

+1871
-29
lines changed

8 files changed

+1871
-29
lines changed

stdlib/LinearAlgebra/src/triangular.jl

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2138,32 +2138,14 @@ function log(A0::UpperTriangular{T}) where T<:BlasFloat
21382138
end
21392139

21402140
# Compute accurate superdiagonal of T
2141-
p = 1 / 2^s
2142-
for k = 1:n-1
2143-
Ak = A0[k,k]
2144-
Akp1 = A0[k+1,k+1]
2145-
Akp = Ak^p
2146-
Akp1p = Akp1^p
2147-
A[k,k] = Akp
2148-
A[k+1,k+1] = Akp1p
2149-
if Ak == Akp1
2150-
A[k,k+1] = p * A0[k,k+1] * Ak^(p-1)
2151-
elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak)
2152-
A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
2153-
else
2154-
logAk = log(Ak)
2155-
logAkp1 = log(Akp1)
2156-
w = atanh((Akp1 - Ak)/(Akp1 + Ak)) + im*pi*ceil((imag(logAkp1-logAk)-pi)/(2*pi))
2157-
dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak)
2158-
A[k,k+1] = A0[k,k+1] * dd
2159-
end
2160-
end
2141+
blockpower!(A, A0, 0.5^s)
21612142

21622143
# Compute accurate diagonal of T
21632144
for i = 1:n
21642145
a = A0[i,i]
21652146
if s == 0
2166-
r = a - 1
2147+
A[i,i] = a - 1
2148+
continue
21672149
end
21682150
s0 = s
21692151
if angle(a) >= pi / 2
@@ -2200,7 +2182,7 @@ function log(A0::UpperTriangular{T}) where T<:BlasFloat
22002182
end
22012183

22022184
# Scale back
2203-
lmul!(2^s, Y)
2185+
lmul!(2.0^s, Y)
22042186

22052187
# Compute accurate diagonal and superdiagonal of log(T)
22062188
for k = 1:n-1
@@ -2212,11 +2194,16 @@ function log(A0::UpperTriangular{T}) where T<:BlasFloat
22122194
Y[k+1,k+1] = logAkp1
22132195
if Ak == Akp1
22142196
Y[k,k+1] = A0[k,k+1] / Ak
2215-
elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak)
2197+
elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak)
22162198
Y[k,k+1] = A0[k,k+1] * (logAkp1 - logAk) / (Akp1 - Ak)
22172199
else
2218-
w = atanh((Akp1 - Ak)/(Akp1 + Ak) + im*pi*(ceil((imag(logAkp1-logAk) - pi)/(2*pi))))
2219-
Y[k,k+1] = 2 * A0[k,k+1] * w / (Akp1 - Ak)
2200+
z = (Akp1 - Ak)/(Akp1 + Ak)
2201+
if abs(z) > 1
2202+
Y[k,k+1] = A0[k,k+1] * (logAkp1 - logAk) / (Akp1 - Ak)
2203+
else
2204+
w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z)))
2205+
Y[k,k+1] = 2 * A0[k,k+1] * w / (Akp1 - Ak)
2206+
end
22202207
end
22212208
end
22222209

@@ -2363,14 +2350,19 @@ function blockpower!(A::UpperTriangular, A0::UpperTriangular, p)
23632350

23642351
if Ak == Akp1
23652352
A[k,k+1] = p * A0[k,k+1] * Ak^(p-1)
2366-
elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak)
2353+
elseif 2 * abs(Ak) < abs(Akp1) || 2 * abs(Akp1) < abs(Ak) || iszero(Akp1 + Ak)
23672354
A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
23682355
else
23692356
logAk = log(Ak)
23702357
logAkp1 = log(Akp1)
2371-
w = atanh((Akp1 - Ak)/(Akp1 + Ak)) + im * pi * unw(logAkp1-logAk)
2372-
dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak);
2373-
A[k,k+1] = A0[k,k+1] * dd
2358+
z = (Akp1 - Ak)/(Akp1 + Ak)
2359+
if abs(z) > 1
2360+
A[k,k+1] = A0[k,k+1] * (Akp1p - Akp) / (Akp1 - Ak)
2361+
else
2362+
w = atanh(z) + im * pi * (unw(logAkp1-logAk) - unw(log1p(z)-log1p(-z)))
2363+
dd = 2 * exp(p*(logAk+logAkp1)/2) * sinh(p*w) / (Akp1 - Ak);
2364+
A[k,k+1] = A0[k,k+1] * dd
2365+
end
23742366
end
23752367
end
23762368
end

stdlib/LinearAlgebra/test/dense.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,4 +877,22 @@ end
877877
@test_broken inv(transpose(B))*transpose(B) I
878878
end
879879

880+
@testset "Matrix log issue #32313" begin
881+
for A in ([30 20; -50 -30], [10.0im 0; 0 -10.0im], randn(6,6))
882+
@test exp(log(A)) A
883+
end
884+
end
885+
886+
@testset "Matrix log PR #33245" begin
887+
###########
888+
# Note: Test removed in backporting to 1.0 due to diagm(::Array{Complex{Float64},1}) not existing
889+
###########
890+
# edge case for divided difference
891+
#A1 = triu(ones(3,3),1) + diagm([1.0, -2eps()-1im, -eps()+0.75im])
892+
#@test exp(log(A1)) ≈ A1
893+
# case where no sqrt is needed (s=0)
894+
A2 = [1.01 0.01 0.01; 0 1.01 0.01; 0 0 1.01]
895+
@test exp(log(A2)) A2
896+
end
897+
880898
end # module TestDense
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name = "Statistics"
2+
uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
3+
4+
[deps]
5+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
6+
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
7+
8+
[extras]
9+
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
10+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
11+
12+
[targets]
13+
test = ["Random", "Test"]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Statistics
2+
3+
The Statistics module contains basic statistics functionality.
4+
5+
!!! note
6+
To use any of the examples described below, run `using Statistics` and then the code from the example.
7+
8+
```@docs
9+
Statistics.std
10+
Statistics.stdm
11+
Statistics.var
12+
Statistics.varm
13+
Statistics.cor
14+
Statistics.cov
15+
Statistics.mean!
16+
Statistics.mean
17+
Statistics.median!
18+
Statistics.median
19+
Statistics.middle
20+
Statistics.quantile!
21+
Statistics.quantile
22+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1

0 commit comments

Comments
 (0)