Skip to content

Commit d2ebcc8

Browse files
committed
TRICG & TRIMR: support warm-start with ldiv=true
This addresses some potential inconsistencies in `tricg` and `trimr` in the case `ldiv=true`: - docs: fixes the statement about the relationship between `E`/`F` and `M`/`N` - docs: describes the residual-norm in terms of `E`/`F` rather than `M`/`N` (only the former are unambiguous, given the problem statement) - adds support for warm-starting when `ldiv=true` for arbitrary `M`/`N`
1 parent 4e3d7a4 commit d2ebcc8

File tree

2 files changed

+43
-17
lines changed

2 files changed

+43
-17
lines changed

src/tricg.jl

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,24 @@ Given a matrix `A` of dimension m × n, TriCG solves the Hermitian linear system
3333
[ τE A ] [ x ] = [ b ]
3434
[ Aᴴ νF ] [ y ] [ c ],
3535
36-
of size (n+m) × (n+m) where `τ` and `ν` are real numbers, `E` = `M⁻¹` ≻ 0 and `F` = `N⁻¹` ≻ 0.
37-
TriCG could breakdown if `τ = 0` or `ν = 0`.
38-
It's recommended to use TriMR in these cases.
36+
of size (n+m) × (n+m) where `τ` and `ν` are real numbers, `E` ≻ 0 and `F` ≻ 0.
37+
`E` and `F` are related to the preconditioners `M` and `N` as `E = M⁻¹` and `F =
38+
N⁻¹` when `ldiv = false` (the default), and `E = M` and `F = N` when `ldiv =
39+
true`.
3940
4041
By default, TriCG solves Hermitian and quasi-definite linear systems with `τ = 1` and `ν = -1`.
42+
TriCG could breakdown if `τ = 0` or `ν = 0`. It's recommended to use TriMR in these cases.
4143
4244
TriCG is based on the preconditioned orthogonal tridiagonalization process
4345
and its relation with the preconditioned block-Lanczos process.
4446
4547
The matrix
4648
47-
[ M 0 ]
48-
[ 0 N ]
49+
[ E⁻¹ 0 ]
50+
[ 0 F⁻¹ ]
4951
5052
indicates the weighted norm in which residuals are measured, here denoted `‖·‖`.
51-
It's the Euclidean norm when `M` and `N` are identity operators.
53+
When `M` and `N` are identity operators and `τ` and `ν` are ±1, it's the Euclidean norm.
5254
5355
TriCG stops when `itmax` iterations are reached or when `‖rₖ‖ ≤ atol + ‖r₀‖ * rtol`.
5456
`atol` is an absolute tolerance and `rtol` is a relative tolerance.
@@ -70,7 +72,9 @@ For an in-place variant that reuses memory across solves, see [`tricg!`](@ref).
7072
* `x0`: a vector of length `m` that represents an initial guess of the solution `x`;
7173
* `y0`: a vector of length `n` that represents an initial guess of the solution `y`.
7274
73-
Warm-starting is supported only when `M` and `N` are either `I` or the corresponding coefficient (`τ` or `ν`) is zero.
75+
Warm-starting is supported only when:
76+
* ldiv = true; or
77+
* `M` and `N` are either `I` or the corresponding coefficient (`τ` or `ν`) is zero.
7478
7579
#### Keyword arguments
7680
@@ -178,8 +182,10 @@ kwargs_tricg = (:M, :N, :ldiv, :spd, :snd, :flip, :τ, :ν, :atol, :rtol, :itmax
178182
snd &&= -one(T) ; ν = -one(T))
179183

180184
warm_start = workspace.warm_start
181-
warm_start && 0) && !MisI && error("Warm-start with preconditioners is not supported.")
182-
warm_start && 0) && !NisI && error("Warm-start with preconditioners is not supported.")
185+
if !ldiv
186+
warm_start && 0) && !MisI && error("Warm-start with preconditioners is not supported.")
187+
warm_start && 0) && !NisI && error("Warm-start with preconditioners is not supported.")
188+
end
183189

184190
# Compute the adjoint of A
185191
Aᴴ = A'
@@ -212,15 +218,27 @@ kwargs_tricg = (:M, :N, :ldiv, :spd, :snd, :flip, :τ, :ν, :atol, :rtol, :itmax
212218
kfill!(M⁻¹vₖ₋₁, zero(FC)) # v₀ = 0
213219
kfill!(N⁻¹uₖ₋₁, zero(FC)) # u₀ = 0
214220

215-
# [ τI A ] [ xₖ ] = [ b - τΔx - AΔy ] = [ b₀ ]
216-
# [ Aᴴ νI ] [ yₖ ] [ c - AᴴΔx - νΔy ] [ c₀ ]
217221
if warm_start
218-
kmul!(b₀, A, Δy)
219-
0) && kaxpy!(m, τ, Δx, b₀)
220-
kaxpby!(m, one(FC), b, -one(FC), b₀)
221-
kmul!(c₀, Aᴴ, Δx)
222-
0) && kaxpy!(n, ν, Δy, c₀)
223-
kaxpby!(n, one(FC), c, -one(FC), c₀)
222+
if ldiv
223+
# [ τM A ] [ xₖ ] = [ b - τMΔx - AΔy ] = [ b₀ ]
224+
# [ Aᴴ νN ] [ yₖ ] [ c - AᴴΔx - νNΔy ] [ c₀ ]
225+
kmul!(b₀, A, Δy)
226+
0) && mul!(b₀, M, Δx, τ, one(FC)) # b₀ ← τMΔx + AΔy
227+
kaxpby!(m, one(FC), b, -one(FC), b₀)
228+
kmul!(c₀, Aᴴ, Δx)
229+
0) && mul!(c₀, N, Δy, ν, one(FC)) # c₀ ← νNΔy + AᴴΔx
230+
kaxpby!(n, one(FC), c, -one(FC), c₀)
231+
else
232+
# Only supported for M = I or τ = 0, and N = I or ν = 0
233+
# [ τI A ] [ xₖ ] = [ b - τΔx - AΔy ] = [ b₀ ]
234+
# [ Aᴴ νI ] [ yₖ ] [ c - AᴴΔx - νΔy ] [ c₀ ]
235+
kmul!(b₀, A, Δy)
236+
0) && kaxpy!(m, τ, Δx, b₀)
237+
kaxpby!(m, one(FC), b, -one(FC), b₀)
238+
kmul!(c₀, Aᴴ, Δx)
239+
0) && kaxpy!(n, ν, Δy, c₀)
240+
kaxpby!(n, one(FC), c, -one(FC), c₀)
241+
end
224242
end
225243

226244
# β₁Ev₁ = b ↔ β₁v₁ = Mb

test/test_warm_start.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ function test_warm_start(FC)
9494
@test stats.niter <= 1
9595
@test_throws "x0 should have size $(length(x30))" tricg(A3x2, b3, c2, [1.0], y20)
9696
@test_throws "y0 should have size $(length(y20))" tricg(A3x2, b3, c2, x30, [1.0])
97+
98+
N = Diagonal([0.8, 1.2])
99+
x30, y20, _ = tricg(A3x2, b3, c2; N, ldiv=true)
100+
x3, y2, stats = tricg(A3x2, b3, c2, x30, y20; N, ldiv=true)
101+
@test x3 x30 atol=1e-12
102+
@test y2 y20 atol=1e-12
103+
@test stats.niter <= 1
104+
97105
workspace = TricgWorkspace(A, b)
98106
krylov_solve!(workspace, A, b, b, x0, y0)
99107
r = [b - workspace.x - A * workspace.y; b - A' * workspace.x + workspace.y]

0 commit comments

Comments
 (0)