Skip to content

Commit 32f22a3

Browse files
authored
Set subdiagonal elements to zero when splitting in the general (#64)
eigenvalue solver. Fixes #63
1 parent a577fc1 commit 32f22a3

File tree

2 files changed

+81
-25
lines changed

2 files changed

+81
-25
lines changed

src/eigenGeneral.jl

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,14 @@ function wilkinson(Hmm, t, d)
8080
end
8181

8282
# We currently absorb extra unsupported keywords in kwargs. These could e.g. be scale and permute. Do we want to check that these are false?
83-
function _schur!(H::HessenbergFactorization{T}; tol = eps(real(T)), debug = false, shiftmethod = :Francis, maxiter = 100*size(H, 1), kwargs...) where T
83+
function _schur!(
84+
H::HessenbergFactorization{T};
85+
tol = eps(real(T)),
86+
debug = false,
87+
shiftmethod = :Francis,
88+
maxiter = 100*size(H, 1),
89+
kwargs...) where T
90+
8491
n = size(H, 1)
8592
istart = 1
8693
iend = n
@@ -99,14 +106,25 @@ function _schur!(H::HessenbergFactorization{T}; tol = eps(real(T)), debug = fals
99106
# Determine if the matrix splits. Find lowest positioned subdiagonal "zero"
100107
for _istart in iend - 1:-1:1
101108
if abs(HH[_istart + 1, _istart]) <= tol*(abs(HH[_istart, _istart]) + abs(HH[_istart + 1, _istart + 1]))
102-
istart = _istart + 1
109+
# Check if subdiagonal element H[i+1,i] is "zero" such that we can split the matrix
110+
111+
# Set istart to the beginning of the second block
112+
istart = _istart + 1
113+
103114
debug && @printf("Split! Subdiagonal element is: %10.3e and istart now %6d\n", HH[istart, istart - 1], istart)
115+
116+
# Set the subdiagonal element to zero to signal that a split has taken place
117+
HH[istart, istart - 1] = 0
104118
break
105-
elseif _istart > 1 && abs(HH[_istart, _istart - 1]) <= tol*(abs(HH[_istart - 1, _istart - 1]) + abs(HH[_istart, _istart]))
106-
debug && @printf("Split! Next subdiagonal element is: %10.3e and istart now %6d\n", HH[_istart, _istart - 1], _istart)
107-
istart = _istart
108-
break
119+
# elseif _istart > 1 && abs(HH[_istart, _istart - 1]) <= tol*(abs(HH[_istart - 1, _istart - 1]) + abs(HH[_istart, _istart]))
120+
121+
# debug && @printf("Split! Next subdiagonal element is: %10.3e and istart now %6d\n", HH[_istart, _istart - 1], _istart)
122+
# HH[_istart, _istart - 1]=0
123+
# istart = _istart
124+
# break
109125
end
126+
127+
# If no splits have taken place then we start from the top
110128
istart = 1
111129
end
112130

@@ -251,54 +269,63 @@ _eigvals!(H::HessenbergFactorization; kwargs...) = _eigvals!(_schur!(H; kwargs..
251269

252270
# Overload methods from LinearAlgebra to make them work generically
253271
if VERSION > v"1.2.0-DEV.0"
254-
function LinearAlgebra.eigvals!(A::StridedMatrix;
255-
sortby::Union{Function,Nothing}=LinearAlgebra.eigsortby,
256-
kwargs...)
257-
if ishermitian(A)
258-
return LinearAlgebra.sorteig!(eigvals!(Hermitian(A)), sortby)
259-
end
260-
LinearAlgebra.sorteig!(_eigvals!(A; kwargs...), sortby)
272+
function LinearAlgebra.eigvals!(
273+
A::StridedMatrix;
274+
sortby::Union{Function,Nothing}=LinearAlgebra.eigsortby,
275+
kwargs...)
276+
277+
if ishermitian(A)
278+
return LinearAlgebra.sorteig!(eigvals!(Hermitian(A)), sortby)
279+
end
280+
LinearAlgebra.sorteig!(_eigvals!(A; kwargs...), sortby)
261281
end
262282

263-
LinearAlgebra.eigvals!(H::HessenbergMatrix;
264-
sortby::Union{Function,Nothing}=LinearAlgebra.eigsortby,
265-
kwargs...) = LinearAlgebra.sorteig!(_eigvals!(H; kwargs...), sortby)
283+
LinearAlgebra.eigvals!(
284+
H::HessenbergMatrix;
285+
sortby::Union{Function,Nothing}=LinearAlgebra.eigsortby,
286+
kwargs...) = LinearAlgebra.sorteig!(_eigvals!(H; kwargs...), sortby)
266287

267-
LinearAlgebra.eigvals!(H::HessenbergFactorization;
268-
sortby::Union{Function,Nothing}=LinearAlgebra.eigsortby,
269-
kwargs...) = LinearAlgebra.sorteig!(_eigvals!(H; kwargs...), sortby)
288+
LinearAlgebra.eigvals!(
289+
H::HessenbergFactorization;
290+
sortby::Union{Function,Nothing}=LinearAlgebra.eigsortby,
291+
kwargs...) = LinearAlgebra.sorteig!(_eigvals!(H; kwargs...), sortby)
270292
else
271-
272293
function LinearAlgebra.eigvals!(A::StridedMatrix; kwargs...)
273294
if ishermitian(A)
274295
return eigvals!(Hermitian(A))
296+
else
297+
return _eigvals!(A; kwargs...)
275298
end
276-
_eigvals!(A; kwargs...)
277299
end
278300

279301
LinearAlgebra.eigvals!(H::HessenbergMatrix; kwargs...) = _eigvals!(H; kwargs...)
280302

281303
LinearAlgebra.eigvals!(H::HessenbergFactorization; kwargs...) = _eigvals!(H; kwargs...)
282304
end
283305

284-
306+
# To compute the eigenvalue of the pseudo triangular Schur matrix we just return
307+
# the values of the 1x1 diagonal blocks and compute the eigenvalues of the 2x2
308+
# blocks on the fly. We can check for exact zeros in the subdiagonal because
309+
# the QR algorithm sets the elements to zero when a split happens
285310
function _eigvals!(S::Schur{T}; tol = eps(real(T))) where T
286311
HH = S.data
287312
n = size(HH, 1)
288313
vals = Vector{complex(T)}(undef, n)
289314
i = 1
290315
while i < n
291316
Hii = HH[i, i]
292-
Hi1i1 = HH[i + 1, i + 1]
293-
if abs(HH[i + 1, i]) <= tol*(abs(Hi1i1) + abs(Hii))
317+
if iszero(HH[i + 1, i])
318+
# 1x1 block
294319
vals[i] = Hii
295320
i += 1
296321
else
322+
# 2x2 block
323+
Hi1i1 = HH[i + 1, i + 1]
297324
d = Hii*Hi1i1 - HH[i, i + 1]*HH[i + 1, i]
298325
t = Hii + Hi1i1
299326
x = 0.5*t
300327
y = sqrt(complex(x*x - d))
301-
vals[i] = x + y
328+
vals[i ] = x + y
302329
vals[i + 1] = x - y
303330
i += 2
304331
end

test/eigengeneral.jl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,33 @@ end
6464
@test sum(eigvals(schur(A).Schur)) sum(eigvals(Float64.(schur(big.(A)).Schur)))
6565
end
6666

67+
@testset "Issue 63" begin
68+
A = [1 2 1 1 1 1
69+
0 1 0 1 0 1
70+
1 1 2 0 1 0
71+
0 1 0 2 1 1
72+
1 1 1 0 2 0
73+
0 1 1 1 0 2]
74+
75+
z1 = [complex(1, sqrt(big(3))), complex(1, -sqrt(big(3)))]
76+
z2 = [
77+
4*2^(big(2)/3)/complex(5, 3*sqrt(big(111)))^(big(1)/3),
78+
complex(5, 3*sqrt(big(111)))^(big(1)/3)/2^(big(2)/3)
79+
]
80+
81+
truevals = real.([
82+
(7 - transpose(z1)*z2),
83+
3,
84+
3,
85+
3,
86+
(7 - adjoint(z1)*z2),
87+
(7 + [2, 2]'*z2)])/3
88+
89+
if VERSION > v"1.2.0-DEV.0"
90+
@test eigvals(big.(A)) truevals
91+
else
92+
@test sort(eigvals(big.(A)), by=real) truevals
93+
end
94+
end
95+
6796
end

0 commit comments

Comments
 (0)