Skip to content

Commit 2027c5a

Browse files
committed
abstol/reltol for Chebyshev
1 parent ca295a0 commit 2027c5a

File tree

2 files changed

+58
-34
lines changed

2 files changed

+58
-34
lines changed

src/chebyshev.jl

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,20 @@ mutable struct ChebyshevIterable{precT, matT, solT, vecT, realT <: Real}
1717
λ_diff::realT
1818

1919
resnorm::realT
20-
reltol::realT
20+
tol::realT
2121
maxiter::Int
2222
mv_products::Int
2323
end
2424

25-
converged(c::ChebyshevIterable) = c.resnorm c.reltol
26-
start(::ChebyshevIterable) = 0
27-
done(c::ChebyshevIterable, iteration::Int) = iteration c.maxiter || converged(c)
25+
@inline converged(it::ChebyshevIterable) = it.resnorm it.tol
26+
@inline start(::ChebyshevIterable) = 0
27+
@inline done(it::ChebyshevIterable, iteration::Int) = iteration it.maxiter || converged(it)
2828

2929
function iterate(cheb::ChebyshevIterable, iteration::Int=start(cheb))
30-
if done(cheb, iteration) return nothing end
30+
# Check for termination first
31+
if done(cheb, iteration)
32+
return nothing
33+
end
3134

3235
T = eltype(cheb.x)
3336

@@ -54,7 +57,12 @@ function iterate(cheb::ChebyshevIterable, iteration::Int=start(cheb))
5457
end
5558

5659
function chebyshev_iterable!(x, A, b, λmin::Real, λmax::Real;
57-
tol = sqrt(eps(real(eltype(b)))), maxiter = size(A, 2), Pl = Identity(), initially_zero = false)
60+
abstol::Real = zero(real(eltype(b))),
61+
reltol::Real = sqrt(eps(real(eltype(b)))),
62+
tol = nothing, # TODO: Deprecations introduced in v0.8
63+
maxiter = size(A, 2),
64+
Pl = Identity(),
65+
initially_zero = false)
5866

5967
λ_avg = (λmax + λmin) / 2
6068
λ_diff = (λmax - λmin) / 2
@@ -65,23 +73,30 @@ function chebyshev_iterable!(x, A, b, λmin::Real, λmax::Real;
6573
u = zero(x)
6674
c = similar(x)
6775

76+
# TODO: Deprecations introduced in v0.8
77+
if tol !== nothing
78+
Base.depwarn("The keyword argument `tol` is deprecated, use `reltol` instead.", :chebyshev_iterable!)
79+
reltol = tol
80+
end
81+
6882
# One MV product less
6983
if initially_zero
70-
resnorm = norm(r)
71-
reltol = tol * resnorm
7284
mv_products = 0
7385
else
86+
mv_products = 1
7487
mul!(c, A, x)
7588
r .-= c
76-
resnorm = norm(r)
77-
reltol = tol * norm(b)
78-
mv_products = 1
7989
end
90+
resnorm = norm(r)
91+
# TODO: According to the docs, the code below should use the initial residual
92+
# instead of the norm of the RHS `b` to set the relative tolerance.
93+
# tolerance = max(reltol * resnorm, abstol)
94+
tolerance = max(reltol * norm(b), abstol)
8095

8196
ChebyshevIterable(Pl, A, x, r, u, c,
8297
zero(real(T)),
8398
λ_avg, λ_diff,
84-
resnorm, reltol, maxiter, mv_products
99+
resnorm, tolerance, maxiter, mv_products
85100
)
86101
end
87102

@@ -112,7 +127,11 @@ Solve Ax = b for symmetric, definite matrices A using Chebyshev iteration.
112127
- `initially_zero::Bool = false`: if `true` assumes that `iszero(x)` so that one
113128
matrix-vector product can be saved when computing the initial
114129
residual vector;
115-
- `tol`: tolerance for stopping condition `|r_k| / |r_0| ≤ tol`.
130+
- `abstol::Real = zero(real(eltype(b)))`,
131+
`reltol::Real = sqrt(eps(real(eltype(b))))`: absolute and relative
132+
tolerance for the stopping condition
133+
`|r_k| / |r_0| ≤ max(reltol * resnorm, abstol)`, where `r_k = A * x_k - b`
134+
is the residual in the `k`th iteration;
116135
- `maxiter::Int = size(A, 2)`: maximum number of inner iterations of GMRES;
117136
- `Pl = Identity()`: left preconditioner;
118137
- `log::Bool = false`: keep track of the residual norm in each iteration;
@@ -130,20 +149,28 @@ Solve Ax = b for symmetric, definite matrices A using Chebyshev iteration.
130149
- `history`: convergence history.
131150
"""
132151
function chebyshev!(x, A, b, λmin::Real, λmax::Real;
133-
Pl = Identity(),
134-
tol::Real=sqrt(eps(real(eltype(b)))),
135-
maxiter::Int=size(A, 2),
136-
log::Bool=false,
137-
verbose::Bool=false,
138-
initially_zero::Bool=false
139-
)
152+
abstol::Real = zero(real(eltype(b))),
153+
reltol::Real = sqrt(eps(real(eltype(b)))),
154+
tol = nothing, # TODO: Deprecations introduced in v0.8
155+
Pl = Identity(),
156+
maxiter::Int=size(A, 2),
157+
log::Bool=false,
158+
verbose::Bool=false,
159+
initially_zero::Bool=false)
140160
history = ConvergenceHistory(partial=!log)
141161
history[:tol] = tol
142162
reserve!(history, :resnorm, maxiter)
143163

144164
verbose && @printf("=== chebyshev ===\n%4s\t%7s\n","iter","resnorm")
145165

146-
iterable = chebyshev_iterable!(x, A, b, λmin, λmax; tol=tol, maxiter=maxiter, Pl=Pl, initially_zero=initially_zero)
166+
# TODO: Deprecations introduced in v0.8
167+
if tol !== nothing
168+
Base.depwarn("The keyword argument `tol` is deprecated, use `reltol` instead.", :cg!)
169+
reltol = tol
170+
end
171+
172+
iterable = chebyshev_iterable!(x, A, b, λmin, λmax; abstol=abstol, reltol=reltol,
173+
maxiter=maxiter, Pl=Pl, initially_zero=initially_zero)
147174
history.mvps = iterable.mv_products
148175
for (iteration, resnorm) = enumerate(iterable)
149176
nextiter!(history)

test/chebyshev.jl

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,37 +24,34 @@ Random.seed!(1234321)
2424
@testset "Matrix{$T}" for T in (Float32, Float64, ComplexF32, ComplexF64)
2525
A = randSPD(T, n)
2626
b = rand(T, n)
27-
tol = (eps(real(T)))
27+
reltol = (eps(real(T)))
2828

29-
# Without a preconditioner
30-
begin
29+
@testset "Without preconditioner" begin
3130
λ_min, λ_max = approx_eigenvalue_bounds(A)
3231
x0 = rand(n)
33-
x, history = chebyshev(A, b, λ_min, λ_max, tol=tol, maxiter=10n, log=true)
32+
x, history = chebyshev(A, b, λ_min, λ_max, reltol=reltol, maxiter=10n, log=true)
3433
@test isa(history, ConvergenceHistory)
3534
@test history.isconverged
36-
@test norm(A * x - b) / norm(b) tol
35+
@test norm(A * x - b) / norm(b) reltol
3736
end
3837

39-
# With an initial guess
40-
begin
38+
@testset "With an initial guess" begin
4139
λ_min, λ_max = approx_eigenvalue_bounds(A)
4240
x0 = rand(T, n)
43-
x, history = chebyshev!(x0, A, b, λ_min, λ_max, tol=tol, maxiter=10n, log=true)
41+
x, history = chebyshev!(x0, A, b, λ_min, λ_max, reltol=reltol, maxiter=10n, log=true)
4442
@test isa(history, ConvergenceHistory)
4543
@test history.isconverged
4644
@test x == x0
47-
@test norm(A * x - b) / norm(b) tol
45+
@test norm(A * x - b) / norm(b) reltol
4846
end
4947

50-
# With a preconditioner
51-
begin
48+
@testset "With a preconditioner" begin
5249
B = randSPD(T, n)
5350
B_fact = cholesky!(B, Val(false))
5451
λ_min, λ_max = approx_eigenvalue_bounds(B_fact \ A)
55-
x, history = chebyshev(A, b, λ_min, λ_max, Pl = B_fact, tol=tol, maxiter=10n, log=true)
52+
x, history = chebyshev(A, b, λ_min, λ_max, Pl = B_fact, reltol=reltol, maxiter=10n, log=true)
5653
@test history.isconverged
57-
@test norm(A * x - b) / norm(b) tol
54+
@test norm(A * x - b) / norm(b) reltol
5855
end
5956
end
6057
end

0 commit comments

Comments
 (0)