Skip to content

Commit cd818e0

Browse files
committed
Add extension for ArnoldiMethod and tests for cluster using it
1 parent 55225a1 commit cd818e0

File tree

5 files changed

+102
-13
lines changed

5 files changed

+102
-13
lines changed

Project.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,27 @@ version = "0.2.2-DEV"
55
[deps]
66
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
77

8+
[weakdeps]
9+
ArnoldiMethod = "ec485272-7323-5ecc-a04f-4719b315124d"
10+
11+
[extensions]
12+
ArnoldiExt = "ArnoldiMethod"
13+
814
[compat]
915
julia = "1"
1016
Aqua = "0.8"
17+
ArnoldiMethod = "0.4"
1118
LinearAlgebra = "1"
1219
Quadmath = "0.5"
1320
Random = "1"
1421
Test = "1"
1522

1623
[extras]
1724
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
25+
ArnoldiMethod = "ec485272-7323-5ecc-a04f-4719b315124d"
1826
Quadmath = "be4d8f0f-7fa4-5f49-b795-2f01399ab2dd"
1927
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
2028
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2129

2230
[targets]
23-
test = ["Test","Random","Quadmath","Aqua"]
31+
test = ["Test","Random","Quadmath","ArnoldiMethod","Aqua"]

ext/ArnoldiExt.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module ArnoldiExt
2+
3+
using LinearAlgebra
4+
using IterativeRefinement
5+
const IR = IterativeRefinement
6+
using ArnoldiMethod
7+
8+
"""
9+
rfeigen(A, S::partialschur, idxλ, DT, maxiter=5) -> vals, vecs, status
10+
11+
Uses a partial Schur decomposition from ArnoldiMethod for cluster refinement.
12+
"""
13+
function IR.rfeigen(A::AbstractMatrix{T}, ps::ArnoldiMethod.PartialSchur{TQ,TR}, idxλ,
14+
DT = widen(real(T)), maxiter=5; kwargs...
15+
) where {T, TQ, TR <: AbstractMatrix{TRe}} where {TRe <: Complex}
16+
S = LinearAlgebra.Schur(Matrix(ps.R), Matrix(ps.Q), ps.eigenvalues)
17+
IterativeRefinement.rfeigen(A, S, idxλ, DT, maxiter; kwargs...)
18+
end
19+
20+
function IR.rfeigen(A::AbstractMatrix{T}, ps::ArnoldiMethod.PartialSchur{TQ,TR}, idxλ,
21+
DT = widen(real(T)), maxiter=5; kwargs...
22+
) where {T, TQ, TR <: AbstractMatrix{TRe}} where {TRe <: Real}
23+
Sr = LinearAlgebra.Schur(Matrix(ps.R), Matrix(ps.Q), ps.eigenvalues)
24+
S = LinearAlgebra.Schur{complex(TRe)}(Sr)
25+
IterativeRefinement.rfeigen(A, S, idxλ, DT, maxiter; kwargs...)
26+
end
27+
28+
end

src/eigen.jl

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ function rfeigen(A::AbstractMatrix{T},
3939
factor = lu,
4040
scale = true,
4141
verbose = false
42-
) where {T,Tλ,Tx}
42+
) where {T,Tλ <: Union{Real,Complex},Tx}
4343
Tr = promote_type(promote_type(Tx,DT),Tλ)
4444
res = _rfeigen(A, x, λ, Tr, factor, maxiter, tol, scale, verbose)
4545
return res
@@ -59,7 +59,7 @@ function rfeigen(A::AbstractMatrix{T},
5959
factor = lu,
6060
scale = true,
6161
verbose = false
62-
) where {T,Tλ}
62+
) where {T,Tλ <: Union{Real,Complex}}
6363
# CHECKME: is this condition adequate?
6464
if issymmetric(A) && (Tλ <: Real)
6565
Tx =
@@ -148,20 +148,25 @@ end
148148
"""
149149
rfeigen(A, S::Schur, idxλ, DT, maxiter=5) -> vals, vecs, status
150150
151-
Improves the precision of a cluster of eigenvalues of matrix `A`
152-
via multi-precision iterative refinement, using more-precise real type `DT`.
151+
Improves the precision of a cluster of eigenvalues of square matrix `A`
152+
via multi-precision iterative refinement, using more-precise real type `DT`,
153+
using a pre-computed Schur decomposition `S`.
153154
Returns improved estimates of eigenvalues and vectors generating
154155
the corresponding invariant subspace.
155156
156157
This method works on the set of eigenvalues in `S.values` indexed by `idxλ`.
157158
It is designed to handle (nearly) defective cases, but will fail if
158159
the matrix is extremely non-normal or the initial estimates are poor.
159-
Note that `S` must be a true Schur decomposition, not a "real Schur".
160+
161+
If `S` is a quasi-triangular "real Schur", it will be converted to
162+
a complex upper-triangular Schur decomposition. `S` may be a partial
163+
decomposition (i.e. fewer than `size(A,1)` vectors).
160164
"""
161165
function rfeigen(A::AbstractMatrix{T}, S::Schur{TS}, idxλ,
162166
DT = widen(real(T)), maxiter=5;
163167
tol = 1, verbose = false) where {T, TS <: Complex}
164168
n = size(A,1)
169+
ns = size(S.T,1)
165170
m = length(idxλ)
166171
λ = [S.values[idxλ[i]] for i in 1:m]
167172
Tw = promote_type(T,eltype(λ))
@@ -174,8 +179,8 @@ function rfeigen(A::AbstractMatrix{T}, S::Schur{TS}, idxλ,
174179
# M is an upper-triangular matrix of mixing coefficients
175180

176181
# Most of the work is in the space of Schur vectors
177-
Z = zeros(Tw, n, m)
178-
z = zeros(Tw, n)
182+
Z = zeros(Tw, ns, m)
183+
z = zeros(Tw, ns)
179184
idxz = Vector{Int}(undef, m)
180185

181186
k = idxλ[1]
@@ -195,7 +200,9 @@ function rfeigen(A::AbstractMatrix{T}, S::Schur{TS}, idxλ,
195200
for l=2:m
196201
kp = k
197202
k = idxλ[l]
198-
@assert k > kp
203+
if k <= kp
204+
throw(ArgumentError("idxλ must be an increasing sequence"))
205+
end
199206
x0 = (S.T[1:k-1,1:k-1] - λ[l]*I) \ S.T[1:k-1,k]
200207
X1 = (S.T[1:k-1,1:k-1] - λ[l]*I) \ Z[1:k-1,1:l-1]
201208
z[1:k-1] .= -x0
@@ -329,3 +336,9 @@ function rfeigen(A::AbstractMatrix{T}, S::Schur{TS}, idxλ,
329336
λnew = λbar .+
330337
λnew, Xnew, status
331338
end
339+
340+
function rfeigen(A::AbstractMatrix{T}, S::Schur{TS}, idxλ, DT = widen(real(T)), maxiter=5;
341+
kwargs...) where {T, TS <: Real}
342+
Sc = Schur{Complex{TS}}(S)
343+
rfeigen(A, Sc, idxλ, DT, maxiter; kwargs...)
344+
end

test/eigen.jl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ end
7474
tol = 20.0
7575
for n in [5,10,32]
7676
for k in [2,3]
77+
# testset seems to screw with scope, so revert to ancient practice
7778
@label retry
7879
A = mkmat_multiple(n,k,etarget,dmin,T)
7980
# we need a true Schur here
@@ -99,3 +100,41 @@ end
99100
end
100101
end
101102
end
103+
104+
using ArnoldiMethod: partialschur
105+
106+
@testset "eigenvalue cluster $T" for T in (Float32, ComplexF32)
107+
DT = widen(T)
108+
RT = real(T)
109+
etarget = T(2)
110+
dmin = RT(1e3) * eps(RT)
111+
maxit = 5
112+
tol = 20.0
113+
bcond = RT(100)
114+
for n in [200]
115+
for k in [2,3]
116+
etargets = etarget * (T(1) .+ dmin * collect(0:k-1))
117+
@label retry
118+
A = mkmat_cond(n,etargets,bcond,T; fac22=0.1)
119+
ps,_ = partialschur(A, nev=3*k)
120+
Ad = convert.(DT,A)
121+
ew = eigvals(Ad)
122+
idx = findall(abs.(ps.eigenvalues .- etarget) .< 0.2)
123+
# don't try if A is so nonnormal that initial estimates are bad
124+
if length(idx) != k
125+
@goto retry
126+
end
127+
e = eps(real(T))
128+
if verbose
129+
ewerrs = [minimum(abs.(ps.eigenvalues[j] .- ew)) for j in idx]
130+
println("initial errors ", ewerrs / (e * n))
131+
end
132+
newew, newvecs = rfeigen(A, ps, idx, DT, maxit)
133+
ewerrs = [minimum(abs.(newew[j] .- ew)) for j in 1:k]
134+
if verbose
135+
println("final errors ", ewerrs / (e * n))
136+
end
137+
@test maximum(ewerrs) / abs(etarget) < tol * e * n
138+
end
139+
end
140+
end

test/utils.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,19 +98,20 @@ construct a matrix with a cluster of eigenvalues with specified condition.
9898
to have worse condition than the cluster of interest, which may be
9999
undesirable.)
100100
"""
101-
function mkmat_cond(n, targets, cond, ::Type{T}; lbdiag=false, verbose=false) where T
101+
function mkmat_cond(n, targets, cond, ::Type{T};
102+
lbdiag=false, verbose=false, fac22=1) where T
102103
if (cond < 1)
103104
throw(ArgumentError("condition cannot be < 1"))
104105
end
105106
k = length(targets)
106107
verbose && println("Matrix{$T} N=$n $k-cluster w/ cond $cond")
107108
Tw = (T <: Real) ? Float64 : ComplexF64
109+
roffset = (T <: Real) ? 1/T(2) : (1+im)/T(2)
108110
A11 = diagm(0=>Tw.(targets)) + triu(rand(Tw,k,k),1)
109-
ews = rand(n-k)
110111
if lbdiag
111-
A22 = diagm(0=>rand(Tw,n-k))
112+
A22 = fac22 * diagm(0=>(rand(Tw,n-k) .- roffset))
112113
else
113-
A22 = triu(rand(Tw,n-k,n-k))
114+
A22 = fac22 * triu((rand(Tw,n-k,n-k) .- roffset))
114115
end
115116
R = rand(Tw,k,n-k)
116117
condr = sqrt(cond^2 - 1.0)

0 commit comments

Comments
 (0)