Skip to content

Commit d98bba3

Browse files
authored
Fix exceptional shift such that we can handle more matrices. (#69)
* Fix exceptional shift such that we can handle more matrices. Also reduce the default number of iterations. Add complicated test matrices. * Drop Julia 1.0 support and delete azurepipelines.yml file
1 parent a3140d3 commit d98bba3

File tree

5 files changed

+71
-99
lines changed

5 files changed

+71
-99
lines changed

.travis.yml

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ os:
44
- linux
55

66
julia:
7-
- 1.0
87
- 1
98
- nightly
109

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1515
test = ["Quaternions", "Test"]
1616

1717
[compat]
18-
julia = "1"
18+
julia = "1.3"

azure-pipelines.yml

-70
This file was deleted.

src/eigenGeneral.jl

+13-27
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ function _schur!(
8585
tol = eps(real(T)),
8686
debug = false,
8787
shiftmethod = :Francis,
88-
maxiter = 100*size(H, 1),
88+
maxiter = 30*size(H, 1),
8989
kwargs...) where T
9090

9191
n = size(H, 1)
@@ -99,6 +99,7 @@ function _schur!(
9999

100100
@inbounds while true
101101
i += 1
102+
debug && println("iteration: $i")
102103
if i > maxiter
103104
throw(ArgumentError("iteration limit $maxiter reached"))
104105
end
@@ -137,35 +138,20 @@ function _schur!(
137138
else
138139
Hmm = HH[iend, iend]
139140
Hm1m1 = HH[iend - 1, iend - 1]
140-
d = Hm1m1*Hmm - HH[iend, iend - 1]*HH[iend - 1, iend]
141-
t = Hm1m1 + Hmm
142-
t = iszero(t) ? eps(real(one(t))) : t # introduce a small pertubation for zero shifts
141+
if iszero(i % 10)
142+
# Use Eispack exceptional shift
143+
β = abs(HH[iend, iend - 1]) + abs(HH[iend - 1, iend - 2])
144+
d = (Hmm + β)^2 - Hmm*β/2
145+
t = 2*Hmm + 3*β/2
146+
else
147+
d = Hm1m1*Hmm - HH[iend, iend - 1]*HH[iend - 1, iend]
148+
t = Hm1m1 + Hmm
149+
end
143150
debug && @printf("block start is: %6d, block end is: %6d, d: %10.3e, t: %10.3e\n", istart, iend, d, t)
144151

145152
if shiftmethod == :Francis
146-
# Run a bulge chase
147-
if iszero(i % 10)
148-
# Vary the shift strategy to avoid dead locks
149-
# We use a Wilkinson-like shift as suggested in "Sandia technical report 96-0913J: How the QR algorithm fails to converge and how fix it".
150-
151-
debug && @printf("Wilkinson-like shift! Subdiagonal is: %10.3e, last subdiagonal is: %10.3e\n", HH[iend, iend - 1], HH[iend - 1, iend - 2])
152-
_d = t*t - 4d
153-
if _d isa Real && _d >= 0
154-
# real eigenvalues
155-
a = t/2
156-
b = sqrt(_d)/2
157-
s = a > Hmm ? a - b : a + b
158-
else
159-
# complex case
160-
s = t/2
161-
end
162-
singleShiftQR!(HH, τ, s, istart, iend)
163-
else
164-
# most of the time use Francis double shifts
165-
166-
debug && @printf("Francis double shift! Subdiagonal is: %10.3e, last subdiagonal is: %10.3e\n", HH[iend, iend - 1], HH[iend - 1, iend - 2])
167-
doubleShiftQR!(HH, τ, t, d, istart, iend)
168-
end
153+
debug && @printf("Francis double shift! Subdiagonal is: %10.3e, last subdiagonal is: %10.3e\n", HH[iend, iend - 1], HH[iend - 1, iend - 2])
154+
doubleShiftQR!(HH, τ, t, d, istart, iend)
169155
elseif shiftmethod == :Rayleigh
170156
debug && @printf("Single shift with Rayleigh shift! Subdiagonal is: %10.3e\n", HH[iend, iend - 1])
171157

test/eigengeneral.jl

+57
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,61 @@ end
9393
end
9494
end
9595

96+
Demmel(η) = [0 1 0 0
97+
1 0 η 0
98+
0 -η 0 1
99+
0 0 1 0]
100+
101+
@testset "Demmel matrix" for t in (1e-10, 1e-9, 1e-8)
102+
# See "Sandia technical report 96-0913J: How the QR algorithm fails to converge and how fix it"
103+
A = Demmel(t)
104+
vals = GenericLinearAlgebra._eigvals!(GenericLinearAlgebra._schur!(A, maxiter=35))
105+
@test abs.(vals) ones(4)
96106
end
107+
108+
function Hevil2(θ, κ, α, γ)
109+
# Eq (13) and (14)
110+
β = ω = 0.0
111+
ν = cos(θ)*cos(2γ) + cos+ β + ω)*sin(2γ)*κ/2
112+
σ = 1 + κ*sin(2γ)*cos+ β + ω - θ) + κ^2*sin(γ)^2
113+
μ = -sin(θ)*cos(2γ) - sin+ β + ω)*sin(2γ)*κ/2
114+
ρ = sqrt- ν^2)
115+
116+
return [ν (cos(2θ) - ν^2)/ρ μ/ρ*(cos(2θ) - ν^2 + ρ^2)/sqrt^2 - μ^2) (-2*μ*ν - sin(2*θ))/sqrt^2 - μ^2)
117+
ρ -ν - sin(2*θ)*μ/ρ^2 -μ/ρ^2**sin(2*θ) + 2*ν*ρ^2)/sqrt^2 - μ^2) -μ/ρ*(cos(2*θ) - ν^2 + ρ^2)/sqrt^2 - μ^2)
118+
0 sin(2*θ)*sqrt^2 - μ^2)/ρ^2 ν + sin(2*θ)*μ/ρ^2 (cos(2*θ) - ν^2)/ρ
119+
0 0 ρ -ν]
120+
end
121+
122+
@testset "Complicate matrix from Sandia technical report" begin
123+
H = Hevil2(0.111866322512629152, 1.08867072154101741, 0.338146383137297168, -0.313987810419091240)
124+
125+
@test Float64.(abs.(eigvals(big.(H)))) ones(4)
126+
end
127+
128+
@testset "Issue 67" for (A, λs) in (
129+
([1 -2 1 -1 -1 0; 0 1 0 1 0 1; 1 -1 2 0 -1 0; 0 1 0 2 1 1; 1 0 1 0 0 0; 0 -1 1 -1 -2 0] ,
130+
[0.0, 0.0, (3 - big(3)*im)/2, (3 + big(3)*im)/2, (3 - big(3)im)/2, (3 + big(3)im)/2]),
131+
([1 0 -1 0 0 0; 0 1 1 1 -1 0; 1 0 -1 0 0 0; 1 -1 0 -1 1 1; 0 0 1 0 0 0; -1 0 1 0 0 0] ,
132+
zeros(6)),
133+
([1 -2 -1 1 -1 0; 0 1 0 -1 0 1; -1 1 2 0 1 0; 0 -1 -2 2 -1 -1; 1 0 -1 0 0 0; 0 -1 -1 1 0 0],
134+
[(1 - big(3)*im)/2, (1 + big(3)*im)/2, (1 - big(3)im)/2, (1 + big(3)im)/2, 2, 2]),
135+
([2 0 -1 0 0 1; 0 2 0 -1 -1 0; -1 0 1 0 0 -1; 0 -1 0 1 1 0; 0 1 0 -1 0 0; -1 0 1 0 0 0] ,
136+
ones(6)),
137+
([1 0 1 0 -1 0; -2 1 2 -1 1 1; -1 0 -1 0 1 0; 2 1 2 -1 -2 1; 1 0 1 0 -1 0; 1 -1 -2 1 0 -1] ,
138+
[-1, -1, 0, 0, 0, 0]))
139+
140+
@testset "shouldn't need excessive iterations (30*n) in the Float64 case" begin
141+
GenericLinearAlgebra._schur!(float(A))
142+
end
143+
144+
# For BigFloats, many iterations are required for convergence
145+
# Improving this is probably a hard problem
146+
vals = eigvals(big.(A), maxiter=1500)
147+
148+
# It's hard to compare complex conjugate pairs so we compare the real and imaginary parts separately
149+
@test sort(real(vals)) sort(real(λs)) atol=1e-25
150+
@test sort(imag(vals)) sort(imag(λs)) atol=1e-25
151+
end
152+
153+
end

0 commit comments

Comments
 (0)