Skip to content

Commit 61679f4

Browse files
committed
abstol/reltol for QMR
1 parent 9331f7a commit 61679f4

File tree

2 files changed

+79
-29
lines changed

2 files changed

+79
-29
lines changed

src/qmr.jl

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ start(::LanczosDecomp) = 1
6161
done(l::LanczosDecomp, iteration::Int) = false
6262
function iterate(l::LanczosDecomp, iteration::Int=start(l))
6363
# Following Algorithm 7.1 of Saad
64-
if done(l, iteration) return nothing end
64+
if done(l, iteration)
65+
return nothing
66+
end
6567

6668
mul!(l.v_next, l.A, l.v_curr)
6769

@@ -79,7 +81,9 @@ function iterate(l::LanczosDecomp, iteration::Int=start(l))
7981

8082
vw = dot(l.v_next, l.w_next)
8183
l.δ = sqrt(abs(vw))
82-
if iszero(l.δ) return nothing end
84+
if iszero(l.δ)
85+
return nothing
86+
end
8387

8488
l.β_prev = l.β_curr
8589
l.β_curr = vw / l.δ
@@ -98,11 +102,11 @@ mutable struct QMRIterable{T, xT, rT, lanczosT}
98102

99103
lanczos::lanczosT
100104
resnorm::rT
101-
reltol::rT
105+
tol::rT
102106
maxiter::Int
103107

104108
g::Vector{T} # right-hand size following Hessenberg multiplication
105-
H::Vector{T} # Hessenberg Matrix, computed on CPU
109+
H::Vector{T} # Hessenberg Matrix, computed on CPU
106110

107111
c_prev::T
108112
c_curr::T
@@ -114,13 +118,20 @@ mutable struct QMRIterable{T, xT, rT, lanczosT}
114118
end
115119

116120
function qmr_iterable!(x, A, b;
117-
tol = sqrt(eps(real(eltype(b)))),
118-
maxiter::Int = size(A, 2),
119-
initially_zero::Bool = false,
120-
lookahead::Bool = false
121-
)
121+
abstol::Real = zero(real(eltype(b))),
122+
reltol::Real = sqrt(eps(real(eltype(b)))),
123+
tol = nothing, # TODO: Deprecations introduced in v0.8
124+
maxiter::Int = size(A, 2),
125+
initially_zero::Bool = false,
126+
lookahead::Bool = false)
122127
T = eltype(x)
123128

129+
# TODO: Deprecations introduced in v0.8
130+
if tol !== nothing
131+
Base.depwarn("The keyword argument `tol` is deprecated, use `reltol` instead.", :qmr_iterable!)
132+
reltol = tol
133+
end
134+
124135
lanczos = LanczosDecomp(x, A, b, initially_zero = initially_zero)
125136

126137
resnorm = lanczos.resnorm
@@ -135,23 +146,26 @@ function qmr_iterable!(x, A, b;
135146
p_prev = zero(x)
136147
p_curr = zero(x)
137148

138-
reltol = tol * lanczos.resnorm
149+
tolerance = max(reltol * lanczos.resnorm, abstol)
139150

140151
QMRIterable(x,
141-
lanczos, resnorm, reltol,
152+
lanczos, resnorm, tolerance,
142153
maxiter,
143154
g, H,
144155
c_prev, c_curr, s_prev, s_curr,
145156
p_prev, p_curr
146157
)
147158
end
148159

149-
converged(q::QMRIterable) = q.resnorm q.reltol
160+
converged(q::QMRIterable) = q.resnorm q.tol
150161
start(::QMRIterable) = 1
151162
done(q::QMRIterable, iteration::Int) = iteration > q.maxiter || converged(q)
152163

153164
function iterate(q::QMRIterable, iteration::Int=start(q))
154-
if done(q, iteration) return nothing end
165+
# Check for termination first
166+
if done(q, iteration)
167+
return nothing
168+
end
155169

156170
iterate(q.lanczos, iteration)
157171

@@ -230,7 +244,10 @@ Solves the problem ``Ax = b`` with the Quasi-Minimal Residual (QMR) method.
230244
matrix-vector product can be saved when computing the initial residual
231245
vector;
232246
- `maxiter::Int = size(A, 2)`: maximum number of iterations;
233-
- `tol`: relative tolerance;
247+
- `abstol::Real = zero(real(eltype(b)))`,
248+
`reltol::Real = sqrt(eps(real(eltype(b))))`: absolute and relative
249+
tolerance for the stopping condition
250+
`|r_k| / |r_0| ≤ max(reltol * resnorm, abstol)`, where `r_k = A * x_k - b`
234251
- `log::Bool`: keep track of the residual norm in each iteration;
235252
- `verbose::Bool`: print convergence information during the iteration.
236253
@@ -253,18 +270,27 @@ Solves the problem ``Ax = b`` with the Quasi-Minimal Residual (QMR) method.
253270
Residual Linear Method Systems. (December).
254271
"""
255272
function qmr!(x, A, b;
256-
tol = sqrt(eps(real(eltype(b)))),
257-
maxiter::Int = size(A, 2),
258-
lookahead::Bool = false,
259-
log::Bool = false,
260-
initially_zero::Bool = false,
261-
verbose::Bool = false
262-
)
273+
abstol::Real = zero(real(eltype(b))),
274+
reltol::Real = sqrt(eps(real(eltype(b)))),
275+
tol = nothing, # TODO: Deprecations introduced in v0.8
276+
maxiter::Int = size(A, 2),
277+
lookahead::Bool = false,
278+
log::Bool = false,
279+
initially_zero::Bool = false,
280+
verbose::Bool = false)
263281
history = ConvergenceHistory(partial = !log)
264-
history[:tol] = tol
282+
history[:abstol] = abstol
283+
history[:reltol] = reltol
265284
log && reserve!(history, :resnorm, maxiter)
266285

267-
iterable = qmr_iterable!(x, A, b; tol = tol, maxiter = maxiter, initially_zero = initially_zero)
286+
# TODO: Deprecations introduced in v0.8
287+
if tol !== nothing
288+
Base.depwarn("The keyword argument `tol` is deprecated, use `reltol` instead.", :qmr!)
289+
reltol = tol
290+
end
291+
292+
iterable = qmr_iterable!(x, A, b; abstol = abstol, reltol = reltol,
293+
maxiter = maxiter, initially_zero = initially_zero)
268294

269295
verbose && @printf("=== qmr ===\n%4s\t%7s\n","iter","resnorm")
270296

test/qmr.jl

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,22 @@ Random.seed!(1234567)
1313
@testset "Matrix{$T}" for T in (Float32, Float64, ComplexF32, ComplexF64)
1414
A = rand(T, n, n) + n * I
1515
b = rand(T, n)
16-
tol = eps(real(T))*10
16+
reltol = eps(real(T))*10
1717

1818
x, history = qmr(A, b, log=true)
1919
@test isa(history, ConvergenceHistory)
2020
@test history.isconverged
21-
@test norm(A * x - b) / norm(b) tol
22-
23-
21+
@test norm(A * x - b) / norm(b) reltol
2422
end
2523

2624
@testset "SparseMatrixCSC{$T, $Ti}" for T in (Float64, ComplexF64), Ti in (Int64, Int32)
2725
A = sprand(T, n, n, 0.5) + n * I
2826
b = rand(T, n)
29-
tol = eps(real(T))
27+
reltol = eps(real(T))
3028

3129
x, history = qmr(A, b, log=true)
3230
@test history.isconverged
33-
@test norm(A * x - b) / norm(b) tol
31+
@test norm(A * x - b) / norm(b) reltol
3432
end
3533

3634
@testset "Maximum number of iterations" begin
@@ -39,4 +37,30 @@ end
3937
@test length(history[:resnorm]) == 2
4038
end
4139

40+
@testset "Termination criterion" begin
41+
for T in (Float32, Float64, ComplexF32, ComplexF64)
42+
A = T[ 2 -1 0
43+
-1 2 -1
44+
0 -1 2]
45+
n = size(A, 2)
46+
b = ones(T, n)
47+
x0 = A \ b
48+
perturbation = T[(-1)^i for i in 1:n]
49+
50+
# If the initial residual is small and a small relative tolerance is used,
51+
# many iterations are necessary
52+
x = x0 + sqrt(eps(real(T))) * perturbation
53+
initial_residual = norm(A * x - b)
54+
x, ch = qmr!(x, A, b, log=true)
55+
@test 2 niters(ch) n
56+
57+
# If the initial residual is small and a large absolute tolerance is used,
58+
# no iterations are necessary
59+
x = x0 + 10*sqrt(eps(real(T))) * perturbation
60+
initial_residual = norm(A * x - b)
61+
x, ch = qmr!(x, A, b, abstol=2*initial_residual, reltol=zero(real(T)), log=true)
62+
@test niters(ch) == 0
63+
end
64+
end
65+
4266
end

0 commit comments

Comments
 (0)