Skip to content

Commit 87ce4f8

Browse files
committed
abstol/reltol for BiCGStab(l)
1 parent ce1d466 commit 87ce4f8

File tree

5 files changed

+83
-34
lines changed

5 files changed

+83
-34
lines changed

src/bicgstabl.jl

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ mutable struct BiCGStabIterable{precT, matT, solT, vecT <: AbstractVector, small
1313

1414
max_mv_products::Int
1515
mv_products::Int
16-
reltol::realT
16+
tol::realT
1717
residual::realT
1818

1919
Pl::precT
@@ -25,11 +25,12 @@ mutable struct BiCGStabIterable{precT, matT, solT, vecT <: AbstractVector, small
2525
end
2626

2727
function bicgstabl_iterator!(x, A, b, l::Int = 2;
28-
Pl = Identity(),
29-
max_mv_products = size(A, 2),
30-
initial_zero = false,
31-
tol = sqrt(eps(real(eltype(b))))
32-
)
28+
Pl = Identity(),
29+
max_mv_products = size(A, 2),
30+
abstol::Real = zero(real(eltype(b))),
31+
reltol::Real = sqrt(eps(real(eltype(b)))),
32+
tol = nothing, # TODO: Deprecations introduced in v0.8
33+
initial_zero = false)
3334
T = eltype(x)
3435
n = size(A, 1)
3536
mv_products = 0
@@ -41,6 +42,12 @@ function bicgstabl_iterator!(x, A, b, l::Int = 2;
4142

4243
residual = view(rs, :, 1)
4344

45+
# TODO: Deprecations introduced in v0.8
46+
if tol !== nothing
47+
Base.depwarn("The keyword argument `tol` is deprecated, use `reltol` instead.", :bicgstabl_iterator!)
48+
reltol = tol
49+
end
50+
4451
# Compute the initial residual rs[:, 1] = b - A * x
4552
# Avoid computing A * 0.
4653
if initial_zero
@@ -62,17 +69,17 @@ function bicgstabl_iterator!(x, A, b, l::Int = 2;
6269
# For the least-squares problem
6370
M = zeros(T, l + 1, l + 1)
6471

65-
# Stopping condition based on relative tolerance.
66-
reltol = nrm * tol
72+
# Stopping condition based on absolute and relative tolerance.
73+
tolerance = max(reltol * nrm, abstol)
6774

6875
BiCGStabIterable(A, l, x, r_shadow, rs, us,
69-
max_mv_products, mv_products, reltol, nrm,
76+
max_mv_products, mv_products, tolerance, nrm,
7077
Pl,
7178
γ, ω, σ, M
7279
)
7380
end
7481

75-
@inline converged(it::BiCGStabIterable) = it.residual it.reltol
82+
@inline converged(it::BiCGStabIterable) = it.residual it.tol
7683
@inline start(::BiCGStabIterable) = 0
7784
@inline done(it::BiCGStabIterable, iteration::Int) = it.mv_products it.max_mv_products || converged(it)
7885

@@ -157,10 +164,16 @@ bicgstabl(A, b, l = 2; kwargs...) = bicgstabl!(zerox(A, b), A, b, l; initial_zer
157164
- `max_mv_products::Int = size(A, 2)`: maximum number of matrix vector products.
158165
For BiCGStab(l) this is a less dubious term than "number of iterations";
159166
- `Pl = Identity()`: left preconditioner of the method;
160-
- `tol::Real = sqrt(eps(real(eltype(b))))`: tolerance for stopping condition `|r_k| / |r_0| ≤ tol`.
161-
Note that (1) the true residual norm is never computed during the iterations,
162-
only an approximation; and (2) if a preconditioner is given, the stopping condition is based on the
163-
*preconditioned residual*.
167+
- `abstol::Real = zero(real(eltype(b)))`,
168+
`reltol::Real = sqrt(eps(real(eltype(b))))`: absolute and relative
169+
tolerance for the stopping condition
170+
`|r_k| / |r_0| ≤ max(reltol * resnorm, abstol)`, where `r_k = A * x_k - b`
171+
is the residual in the `k`th iteration;
172+
!!! note
173+
1. The true residual norm is never computed during the iterations,
174+
only an approximation;
175+
2. If a left preconditioner is given, the stopping condition is based on the
176+
*preconditioned residual*.
164177
165178
# Return values
166179
@@ -174,21 +187,31 @@ For BiCGStab(l) this is a less dubious term than "number of iterations";
174187
- `history`: convergence history.
175188
"""
176189
function bicgstabl!(x, A, b, l = 2;
177-
tol = sqrt(eps(real(eltype(b)))),
178-
max_mv_products::Int = size(A, 2),
179-
log::Bool = false,
180-
verbose::Bool = false,
181-
Pl = Identity(),
182-
kwargs...
183-
)
190+
abstol::Real = zero(real(eltype(b))),
191+
reltol::Real = sqrt(eps(real(eltype(b)))),
192+
tol = nothing, # TODO: Deprecations introduced in v0.8
193+
max_mv_products::Int = size(A, 2),
194+
log::Bool = false,
195+
verbose::Bool = false,
196+
Pl = Identity(),
197+
kwargs...)
184198
history = ConvergenceHistory(partial = !log)
185-
history[:tol] = tol
199+
history[:abstol] = abstol
200+
history[:reltol] = reltol
186201

187202
# This doesn't yet make sense: the number of iters is smaller.
188203
log && reserve!(history, :resnorm, max_mv_products)
189204

190-
# Actually perform CG
191-
iterable = bicgstabl_iterator!(x, A, b, l; Pl = Pl, tol = tol, max_mv_products = max_mv_products, kwargs...)
205+
# TODO: Deprecations introduced in v0.8
206+
if tol !== nothing
207+
Base.depwarn("The keyword argument `tol` is deprecated, use `reltol` instead.", :bicgstabl!)
208+
reltol = tol
209+
end
210+
211+
# Actually perform iterative solve
212+
iterable = bicgstabl_iterator!(x, A, b, l; Pl = Pl,
213+
abstol = abstol, reltol = reltol,
214+
max_mv_products = max_mv_products, kwargs...)
192215

193216
if log
194217
history.mvps = iterable.mv_products

src/chebyshev.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ function chebyshev!(x, A, b, λmin::Real, λmax::Real;
163163

164164
# TODO: Deprecations introduced in v0.8
165165
if tol !== nothing
166-
Base.depwarn("The keyword argument `tol` is deprecated, use `reltol` instead.", :cg!)
166+
Base.depwarn("The keyword argument `tol` is deprecated, use `reltol` instead.", :chebyshev!)
167167
reltol = tol
168168
end
169169

src/gmres.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ function gmres!(x, A, b;
200200

201201
# TODO: Deprecations introduced in v0.8
202202
if tol !== nothing
203-
Base.depwarn("The keyword argument `tol` is deprecated, use `reltol` instead.", :cg!)
203+
Base.depwarn("The keyword argument `tol` is deprecated, use `reltol` instead.", :gmres!)
204204
reltol = tol
205205
end
206206

src/idrs.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ function idrs!(x, A, b;
5959

6060
# TODO: Deprecations introduced in v0.8
6161
if tol !== nothing
62-
Base.depwarn("The keyword argument `tol` is deprecated, use `reltol` instead.", :cg!)
62+
Base.depwarn("The keyword argument `tol` is deprecated, use `reltol` instead.", :idrs!)
6363
reltol = tol
6464
end
6565

test/bicgstabl.jl

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,20 @@ n = 20
1313
x = ones(T, n)
1414
b = A * x
1515

16-
tol = eps(real(T))
16+
reltol = eps(real(T))
1717

1818
@testset "BiCGStab($l)" for l = (2, 4)
1919
# Solve without preconditioner
20-
x1, his1 = bicgstabl(A, b, l, max_mv_products = 100, log = true, tol = tol)
20+
x1, his1 = bicgstabl(A, b, l, max_mv_products = 100, log = true, reltol = reltol)
2121
@test isa(his1, ConvergenceHistory)
22-
@test norm(A * x1 - b) / norm(b) tol
22+
@test norm(A * x1 - b) / norm(b) reltol
2323

2424
# With an initial guess
2525
x_guess = rand(T, n)
26-
x2, his2 = bicgstabl!(x_guess, A, b, l, max_mv_products = 100, log = true, tol = tol)
26+
x2, his2 = bicgstabl!(x_guess, A, b, l, max_mv_products = 100, log = true, reltol = reltol)
2727
@test isa(his2, ConvergenceHistory)
2828
@test x2 == x_guess
29-
@test norm(A * x2 - b) / norm(b) tol
29+
@test norm(A * x2 - b) / norm(b) reltol
3030

3131
# The following tests fails CI on Windows and Ubuntu due to a
3232
# `SingularException(4)`
@@ -35,8 +35,34 @@ n = 20
3535
end
3636
# Do an exact LU decomp of a nearby matrix
3737
F = lu(A + rand(T, n, n))
38-
x3, his3 = bicgstabl(A, b, Pl = F, l, max_mv_products = 100, log = true, tol = tol)
39-
@test norm(A * x3 - b) / norm(b) tol
38+
x3, his3 = bicgstabl(A, b, Pl = F, l, max_mv_products = 100, log = true, reltol = reltol)
39+
@test norm(A * x3 - b) / norm(b) reltol
40+
end
41+
end
42+
43+
@testset "Termination criterion" begin
44+
for T in (Float32, Float64, ComplexF32, ComplexF64)
45+
A = T[ 2 -1 0
46+
-1 2 -1
47+
0 -1 2]
48+
n = size(A, 2)
49+
b = ones(T, n)
50+
x0 = A \ b
51+
perturbation = T[(-1)^i for i in 1:n]
52+
53+
# If the initial residual is small and a small relative tolerance is used,
54+
# many iterations are necessary
55+
x = x0 + sqrt(eps(real(T))) * perturbation
56+
initial_residual = norm(A * x - b)
57+
x, ch = bicgstabl!(x, A, b, log=true)
58+
@test 1 niters(ch) n÷2 # BiCGStab(2) makes more than one RHS evaluations per iteration
59+
60+
# If the initial residual is small and a large absolute tolerance is used,
61+
# no iterations are necessary
62+
x = x0 + 10*sqrt(eps(real(T))) * perturbation
63+
initial_residual = norm(A * x - b)
64+
x, ch = bicgstabl!(x, A, b, abstol=2*initial_residual, reltol=zero(real(T)), log=true)
65+
@test niters(ch) == 0
4066
end
4167
end
4268
end

0 commit comments

Comments
 (0)