Skip to content

Commit 24714f9

Browse files
authored
Fix 2x2 symmetric eigenvalue computation (#124)
1 parent eee5e7d commit 24714f9

File tree

2 files changed

+81
-29
lines changed

2 files changed

+81
-29
lines changed

src/eigenSelfAdjoint.jl

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -135,24 +135,26 @@ function eigvals2x2(d1::Number, d2::Number, e::Number)
135135
r2 = hypot(d1 - d2, 2 * e) / 2
136136
return r1 + r2, r1 - r2
137137
end
138-
function eigQL2x2!(d::StridedVector, e::StridedVector, j::Integer, vectors::Matrix)
138+
function eig2x2!(d::StridedVector, e::StridedVector, j::Integer, vectors::Matrix)
139139
dj = d[j]
140-
dj1 = d[j+1]
140+
dj1 = d[j + 1]
141141
ej = e[j]
142142
r1 = (dj + dj1) / 2
143143
r2 = hypot(dj - dj1, 2 * ej) / 2
144-
λ = r1 + r2
145-
d[j] = λ
146-
d[j+1] = r1 - r2
144+
λ⁺ = r1 + r2
145+
λ⁻ = r1 - r2
146+
d[j] = λ⁺
147+
d[j + 1] = λ⁻
147148
e[j] = 0
148-
c = ej / (dj - λ)
149-
if isfinite(c) # FixMe! is this the right fix for overflow?
150-
h = hypot(one(c), c)
151-
c /= h
152-
s = inv(h)
149+
if iszero(ej)
150+
c = one(λ⁺)
151+
s = zero(λ⁺)
152+
elseif abs(λ⁺ - dj) > abs(λ⁻ - dj)
153+
c = -ej / hypot(ej, λ⁺ - dj)
154+
s = sqrt(1 - c*c)
153155
else
154-
c = one(c)
155-
s = zero(c)
156+
s = abs(ej) / hypot(ej, λ⁻ - dj)
157+
c = copysign(sqrt(1 - s*s), -ej)
156158
end
157159

158160
_updateVectors!(c, s, j, vectors)
@@ -173,14 +175,16 @@ function eigvalsPWK!(S::SymTridiagonal{T}; tol = eps(T)) where {T<:Real}
173175
end
174176
while true
175177
# Check for zero off diagonal elements
176-
for be = blockstart+1:n
177-
if abs(e[be-1]) <= tol * sqrt(abs(d[be-1])) * sqrt(abs(d[be]))
178+
for be = (blockstart + 1):n
179+
if abs(e[be - 1]) <= tol * sqrt(abs(d[be - 1])) * sqrt(abs(d[be]))
178180
blockend = be - 1
179181
break
180182
end
181183
blockend = n
182184
end
183185

186+
@debug "submatrix" blockstart blockend
187+
184188
# Deflate?
185189
if blockstart == blockend
186190
@debug "Deflate? Yes!"
@@ -200,11 +204,12 @@ function eigvalsPWK!(S::SymTridiagonal{T}; tol = eps(T)) where {T<:Real}
200204
μ = d[blockstart] - sqrte /+ copysign(r, μ))
201205

202206
# QL bulk chase
207+
@debug "Values before PWK QL bulge chase" e[blockstart] e[blockend-1] μ
203208
singleShiftQLPWK!(S, μ, blockstart, blockend)
204209

205210
rotations = blockend - blockstart
206211
iter += rotations
207-
@debug "QL, blockstart, blockend, e[blockstart], e[blockend-1], μ, rotations" blockstart blockend e[blockstart] e[blockend-1] μ rotations
212+
@debug "Values after PWK QL bulge chase" e[blockstart] e[blockend-1] rotations
208213
end
209214
if blockstart >= n
210215
break
@@ -243,32 +248,37 @@ function eigQL!(
243248
@inbounds begin
244249
while true
245250
# Check for zero off diagonal elements
246-
for be = blockstart+1:n
247-
if abs(e[be-1]) <= tol * sqrt(abs(d[be-1])) * sqrt(abs(d[be]))
251+
for be = (blockstart + 1):n
252+
if abs(e[be - 1]) <= tol * sqrt(abs(d[be - 1])) * sqrt(abs(d[be]))
248253
blockend = be - 1
249254
break
250255
end
251256
blockend = n
252257
end
258+
259+
@debug "submatrix" blockstart blockend
260+
253261
# Deflate?
254262
if blockstart == blockend
255263
@debug "Deflate? Yes!"
256264
blockstart += 1
257265
elseif blockstart + 1 == blockend
258266
@debug "Deflate? Yes, but after solving 2x2 block"
259-
eigQL2x2!(d, e, blockstart, vectors)
260-
blockstart += 1
267+
eig2x2!(d, e, blockstart, vectors)
268+
blockstart += 2
261269
else
262270
@debug "Deflate? No!"
263271
# Calculate shift
264-
μ = (d[blockstart+1] - d[blockstart]) / (2 * e[blockstart])
272+
μ = (d[blockstart + 1] - d[blockstart]) / (2 * e[blockstart])
265273
r = hypot(μ, one(T))
266274
μ = d[blockstart] - (e[blockstart] /+ copysign(r, μ)))
267275

268276
# QL bulk chase
277+
@debug "Values before bulge chase" e[blockstart] e[blockend - 1] d[blockstart] μ
269278
singleShiftQL!(S, μ, blockstart, blockend, vectors)
270-
@debug "QL, blockstart, blockend, e[blockstart], e[blockend-1] μ" blockstart blockend e[blockstart] e[blockend-1] μ
279+
@debug "Values after bulge chase" e[blockstart] e[blockend - 1] d[blockstart]
271280
end
281+
272282
if blockstart >= n
273283
break
274284
end
@@ -291,32 +301,35 @@ function eigQR!(
291301
@inbounds begin
292302
while true
293303
# Check for zero off diagonal elements
294-
for be = (blockstart+1):n
295-
if abs(e[be-1]) <= tol * sqrt(abs(d[be-1])) * sqrt(abs(d[be]))
304+
for be = (blockstart + 1):n
305+
if abs(e[be - 1]) <= tol * sqrt(abs(d[be - 1])) * sqrt(abs(d[be]))
296306
blockend = be - 1
297307
break
298308
end
299309
blockend = n
300310
end
311+
312+
@debug "submatrix" blockstart blockend
313+
301314
# Deflate?
302315
if blockstart == blockend
303316
@debug "Deflate? Yes!"
304317
blockstart += 1
305318
elseif blockstart + 1 == blockend
306319
@debug "Deflate? Yes, but after solving 2x2 block!"
307-
eigQL2x2!(d, e, blockstart, vectors)
308-
blockstart += 1
320+
eig2x2!(d, e, blockstart, vectors)
321+
blockstart += 2
309322
else
310323
@debug "Deflate? No!"
311324
# Calculate shift
312-
μ = (d[blockend-1] - d[blockend]) / (2 * e[blockend-1])
325+
μ = (d[blockend - 1] - d[blockend]) / (2 * e[blockend - 1])
313326
r = hypot(μ, one(T))
314-
μ = d[blockend] - (e[blockend-1] /+ copysign(r, μ)))
327+
μ = d[blockend] - (e[blockend - 1] /+ copysign(r, μ)))
315328

316329
# QR bulk chase
330+
@debug "Values before QR bulge chase" e[blockstart] e[blockend - 1] d[blockend] μ
317331
singleShiftQR!(S, μ, blockstart, blockend, vectors)
318-
319-
@debug "QR, blockstart, blockend, e[blockstart], e[blockend-1], d[blockend], μ" blockstart blockend e[blockstart] e[blockend-1] d[blockend] μ
332+
@debug "Values after QR bulge chase" e[blockstart] e[blockend - 1] d[blockend]
320333
end
321334
if blockstart >= n
322335
break

test/eigenselfadjoint.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,43 @@ Base.isreal(q::Quaternion) = q.v1 == q.v2 == q.v3 == 0
118118
T = T + T'
119119
@test issorted(eigvals(T))
120120
end
121+
122+
@testset "issue 123" begin
123+
M = Hermitian([
124+
big"-0.4080898675832881399369478084264191594976530854542904557798567397269356887436951";
125+
big"-0.1032324294981949906363774065395184125237581835226155628209100984396171211818558";
126+
big"-1.0795157507124452910839896877334667387210301781514938067860918240771876343947";
127+
big"0.9172086645212876240254394768180975107502376572771647296150618931226550446699544";;
128+
big"-0.1032324294981949906363774065395184125237581835226155628209100984396171211818558";
129+
big"-0.9819956883377066621250198846550622559246996804965712336465013506629992739010227";
130+
big"0.1882735697944729855991976669864503854920622386133987141371224931350749728226066";
131+
big"-0.1599663084136352437739757607131301560774255778371317602542426234968564801904052";;
132+
big"-1.0795157507124452910839896877334667387210301781514938067860918240771876343947";
133+
big"0.1882735697944729855991976669864503854920622386133987141371224931350749728226066";
134+
big"0.9688026817149176598146701814747478080649943014810992426739997593840858865727305";
135+
big"-1.672789745967021000172452940954243617442140494364475046869527486458478435262502";;
136+
big"0.9172086645212876240254394768180975107502376572771647296150618931226550446699544";
137+
big"-0.1599663084136352437739757607131301560774255778371317602542426234968564801904052";
138+
big"-1.672789745967021000172452940954243617442140494364475046869527486458478435262502";
139+
big"0.4212828742060771422472975116067336073573584644697624467523583310058490760719874"
140+
])
141+
F = eigen(M)
142+
@test M * F.vectors F.vectors * Diagonal(F.values)
143+
144+
@testset "2x2 cases, d: $d, e: $e" for (d, e) in (
145+
([1.0, 1.0], [0.0]),
146+
([0.0, 0.0], [1.0]),
147+
([1.0, 1.0], [1e-20]),
148+
([1e-20, 1e-30], [0.5]),
149+
([1e-20, 1e-30], [1e-10]),
150+
([1e-20, 1e-30], [1e-40]),
151+
([1e20, 1e30], [0.5]),
152+
([1e20, 1e30], [1e10]),
153+
([1e20, 1e30], [1e40])
154+
)
155+
V = Matrix{Float64}(I, 2, 2)
156+
c, s = GenericLinearAlgebra.eig2x2!(d, e, 1, V)
157+
@test hypot(c, s) 1
158+
end
159+
end
121160
end

0 commit comments

Comments
 (0)