Skip to content

Commit 5839328

Browse files
committed
abstol/reltol for MINRES
1 parent 87ce4f8 commit 5839328

File tree

2 files changed

+79
-30
lines changed

2 files changed

+79
-30
lines changed

src/minres.jl

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,17 @@ mutable struct MINRESIterable{matT, solT, vecT <: DenseVector, smallVecT <: Dens
3232
# Bookkeeping
3333
mv_products::Int
3434
maxiter::Int
35-
tolerance::realT
35+
tol::realT
3636
resnorm::realT
3737
end
3838

3939
function minres_iterable!(x, A, b;
40-
initially_zero::Bool = false,
41-
skew_hermitian::Bool = false,
42-
tol = sqrt(eps(real(eltype(b)))),
43-
maxiter = size(A, 2)
44-
)
40+
initially_zero::Bool = false,
41+
skew_hermitian::Bool = false,
42+
abstol::Real = zero(real(eltype(b))),
43+
reltol::Real = sqrt(eps(real(eltype(b)))),
44+
tol = nothing, # TODO: Deprecations introduced in v0.8
45+
maxiter = size(A, 2))
4546
T = eltype(x)
4647
HessenbergT = skew_hermitian ? T : real(T)
4748

@@ -53,6 +54,12 @@ function minres_iterable!(x, A, b;
5354
w_curr = similar(x)
5455
w_next = similar(x)
5556

57+
# TODO: Deprecations introduced in v0.8
58+
if tol !== nothing
59+
Base.depwarn("The keyword argument `tol` is deprecated, use `reltol` instead.", :minres_iterable!)
60+
reltol = tol
61+
end
62+
5663
mv_products = 0
5764

5865
# For nonzero x's, we must do an MV for the initial residual vec
@@ -64,7 +71,7 @@ function minres_iterable!(x, A, b;
6471
end
6572

6673
resnorm = norm(v_curr)
67-
reltol = resnorm * tol
74+
tolerance = max(reltol * resnorm, abstol)
6875

6976
# Last active column of the Hessenberg matrix
7077
# and last two entries of the right-hand side
@@ -84,18 +91,21 @@ function minres_iterable!(x, A, b;
8491
w_prev, w_curr, w_next,
8592
H, rhs,
8693
c_prev, s_prev, c_curr, s_curr,
87-
mv_products, maxiter, reltol, resnorm
94+
mv_products, maxiter, tolerance, resnorm
8895
)
8996
end
9097

91-
converged(m::MINRESIterable) = m.resnorm m.tolerance
98+
converged(m::MINRESIterable) = m.resnorm m.tol
9299

93100
start(::MINRESIterable) = 1
94101

95102
done(m::MINRESIterable, iteration::Int) = iteration > m.maxiter || converged(m)
96103

97104
function iterate(m::MINRESIterable, iteration::Int=start(m))
98-
if done(m, iteration) return nothing end
105+
# Check for termination first
106+
if done(m, iteration)
107+
return nothing
108+
end
99109

100110
# v_next = A * v_curr - H[2] * v_prev
101111
mul!(m.v_next, m.A, m.v_curr)
@@ -172,7 +182,13 @@ Solve Ax = b for (skew-)Hermitian matrices A using MINRES.
172182
matrix-vector product can be saved when computing the initial
173183
residual vector;
174184
- `skew_hermitian::Bool = false`: if `true` assumes that `A` is skew-symmetric or skew-Hermitian;
175-
- `tol`: tolerance for stopping condition `|r_k| / |r_0| ≤ tol`. Note that the residual is computed only approximately;
185+
- `abstol::Real = zero(real(eltype(b)))`,
186+
`reltol::Real = sqrt(eps(real(eltype(b))))`: absolute and relative
187+
tolerance for the stopping condition
188+
`|r_k| / |r_0| ≤ max(reltol * resnorm, abstol)`, where `r_k = A * x_k - b`
189+
is the residual in the `k`th iteration
190+
!!! note
191+
The residual is computed only approximately.
176192
- `maxiter::Int = size(A, 2)`: maximum number of iterations;
177193
- `Pl`: left preconditioner;
178194
- `Pr`: right preconditioner;
@@ -191,20 +207,27 @@ Solve Ax = b for (skew-)Hermitian matrices A using MINRES.
191207
- `history`: convergence history.
192208
"""
193209
function minres!(x, A, b;
194-
skew_hermitian::Bool = false,
195-
verbose::Bool = false,
196-
log::Bool = false,
197-
tol = sqrt(eps(real(eltype(b)))),
198-
maxiter::Int = size(A, 2),
199-
initially_zero::Bool = false
200-
)
210+
skew_hermitian::Bool = false,
211+
verbose::Bool = false,
212+
log::Bool = false,
213+
abstol::Real = zero(real(eltype(b))),
214+
reltol::Real = sqrt(eps(real(eltype(b)))),
215+
tol = nothing, # TODO: Deprecations introduced in v0.8
216+
maxiter::Int = size(A, 2),
217+
initially_zero::Bool = false)
201218
history = ConvergenceHistory(partial = !log)
202219
history[:tol] = tol
203220
log && reserve!(history, :resnorm, maxiter)
204221

222+
# TODO: Deprecations introduced in v0.8
223+
if tol !== nothing
224+
Base.depwarn("The keyword argument `tol` is deprecated, use `reltol` instead.", :minres!)
225+
reltol = tol
226+
end
227+
205228
iterable = minres_iterable!(x, A, b;
206229
skew_hermitian = skew_hermitian,
207-
tol = tol,
230+
abstol = abstol, reltol = reltol,
208231
maxiter = maxiter,
209232
initially_zero = initially_zero
210233
)

test/minres.jl

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,25 +29,25 @@ n = 15
2929

3030
@testset "Hermitian Matrix{$T}" for T in (Float32, Float64, ComplexF32, ComplexF64)
3131
A, x, b = hermitian_problem(T, n)
32-
tol = sqrt(eps(real(T)))
32+
reltol = sqrt(eps(real(T)))
3333
x0 = rand(T, n)
3434

35-
x1, hist1 = minres(A, b, maxiter = 10n, tol = tol, log = true)
36-
x2, hist2 = minres!(x0, A, b, maxiter = 10n, tol = tol, log = true)
35+
x1, hist1 = minres(A, b, maxiter = 10n, reltol = reltol, log = true)
36+
x2, hist2 = minres!(x0, A, b, maxiter = 10n, reltol = reltol, log = true)
3737

3838
@test isa(hist1, ConvergenceHistory)
39-
@test norm(b - A * x1) / norm(b) tol
39+
@test norm(b - A * x1) / norm(b) reltol
4040
@test hist1.isconverged
41-
@test norm(b - A * x2) / norm(b) tol
41+
@test norm(b - A * x2) / norm(b) reltol
4242
@test x2 == x0
4343
end
4444

4545
@testset "Skew-Hermitian Matrix{$T}" for T in (Float32, Float64, ComplexF32, ComplexF64)
4646
A, x, b = skew_hermitian_problem(T, n)
47-
tol = sqrt(eps(real(T)))
48-
x_approx, hist = minres(A, b, skew_hermitian = true, maxiter = 10n, tol = tol, log = true)
47+
reltol = sqrt(eps(real(T)))
48+
x_approx, hist = minres(A, b, skew_hermitian = true, maxiter = 10n, reltol = reltol, log = true)
4949

50-
@test norm(b - A * x_approx) / norm(b) tol
50+
@test norm(b - A * x_approx) / norm(b) reltol
5151
@test hist.isconverged
5252
end
5353

@@ -59,12 +59,38 @@ end
5959

6060
x = ones(T, n)
6161
b = A * x
62-
tol = sqrt(eps(real(T)))
62+
reltol = sqrt(eps(real(T)))
6363

64-
x_approx, hist = minres(A, b, maxiter = 10n, tol = tol, log = true)
64+
x_approx, hist = minres(A, b, maxiter = 10n, reltol = reltol, log = true)
6565

66-
@test norm(b - A * x_approx) / norm(b) tol
66+
@test norm(b - A * x_approx) / norm(b) reltol
6767
@test hist.isconverged
6868
end
6969

70+
@testset "Termination criterion" begin
71+
for T in (Float32, Float64, ComplexF32, ComplexF64)
72+
A = T[ 2 -1 0
73+
-1 2 -1
74+
0 -1 2]
75+
n = size(A, 2)
76+
b = ones(T, n)
77+
x0 = A \ b
78+
perturbation = T[(-1)^i for i in 1:n]
79+
80+
# If the initial residual is small and a small relative tolerance is used,
81+
# many iterations are necessary
82+
x = x0 + sqrt(eps(real(T))) * perturbation
83+
initial_residual = norm(A * x - b)
84+
x, ch = minres!(x, A, b, log=true)
85+
@test 2 niters(ch) n
86+
87+
# If the initial residual is small and a large absolute tolerance is used,
88+
# no iterations are necessary
89+
x = x0 + 10*sqrt(eps(real(T))) * perturbation
90+
initial_residual = norm(A * x - b)
91+
x, ch = minres!(x, A, b, abstol=2*initial_residual, reltol=zero(real(T)), log=true)
92+
@test niters(ch) == 0
93+
end
94+
end
95+
7096
end

0 commit comments

Comments
 (0)