1
+ import Base: iterate
2
+
3
+ export lalqmr, lalqmr!
4
+
5
+ mutable struct LALQMRIterable{T, xT, rT, lanczosT}
6
+ x:: xT
7
+
8
+ lanczos:: lanczosT
9
+ resnorm:: rT
10
+ tol:: rT
11
+ maxiter:: Int
12
+
13
+ t:: Vector{T}
14
+ R:: UpperTriangular{T, Matrix{T}}
15
+
16
+ G:: Vector{Givens{T}}
17
+
18
+ d:: Matrix{T}
19
+ end
20
+
21
+ function lalqmr_iterable! (
22
+ x, A, b;
23
+ abstol:: Real = zero (real (eltype (b))),
24
+ reltol:: Real = sqrt (eps (real (eltype (b)))),
25
+ maxiter:: Int = size (A, 2 ),
26
+ initially_zero:: Bool = false ,
27
+ kwargs...
28
+ )
29
+ T = eltype (x)
30
+ r = copy (b)
31
+ if ! initially_zero
32
+ r -= A* x
33
+ end
34
+ resnorm = norm (r)
35
+ v = r/ resnorm
36
+ w = copy (v)
37
+
38
+ lanczos = LookAheadLanczosDecomp (
39
+ A, v, w;
40
+ vw_normalized = true ,
41
+ kwargs...
42
+ )
43
+
44
+ R = UpperTriangular (Matrix {T} (undef, 0 , 0 ))
45
+ # Givens rotations
46
+ G = Vector {Givens{T}} ()
47
+
48
+ # Projection operators
49
+ D = similar (x, size (x, 1 ), 0 )
50
+
51
+ t = Vector {T} (undef, 1 )
52
+ t[1 ] = resnorm
53
+
54
+ tolerance = max (reltol * resnorm, abstol)
55
+
56
+ return LALQMRIterable (
57
+ x,
58
+ lanczos, resnorm, tolerance,
59
+ maxiter,
60
+ t, R,
61
+ G, D
62
+ )
63
+ end
64
+
65
+ converged (q:: LALQMRIterable ) = q. resnorm ≤ q. tol
66
+ start (:: LALQMRIterable ) = 1
67
+ done (q:: LALQMRIterable , iteration:: Int ) = iteration > q. maxiter || converged (q)
68
+
69
+ function iterate (q:: LALQMRIterable , n:: Int = start (q))
70
+ # Check for termination first
71
+ if done (q, n)
72
+ return nothing
73
+ end
74
+
75
+ iterate (q. lanczos, n)
76
+
77
+ # Eq. 6.2, update QR factorization of L
78
+ Llastcol = q. lanczos. L[:, end ]
79
+ for g in q. G
80
+ Llastcol = g* Llastcol
81
+ end
82
+ gend, r = givens (Llastcol, n, n+ 1 )
83
+ push! (q. G, gend)
84
+ Llastcol[end - 1 ] = r
85
+ Llastcol[end ] = 0
86
+ q. R = UpperTriangular ([[q. R; fill (0 , 1 , n- 1 )] Llastcol[1 : end - 1 ]])
87
+ @assert q. R[:, end ] ≈ Llastcol[1 : end - 1 ]
88
+
89
+
90
+ # Eq. 6.2, update t
91
+ push! (q. t, 0 )
92
+ q. t = gend * q. t
93
+
94
+ # Eq. 6.3, calculate projection
95
+ # Dn = [Vn Un^-1 Rn^-1]
96
+ # => Dn Rn Un = Vn
97
+ RU = q. R* q. lanczos. U
98
+ d = q. lanczos. V[:, end - 1 ]
99
+ for i in 1 : size (RU, 1 )- 1
100
+ axpy! (- RU[i, end ], q. d[:, i], d)
101
+ end
102
+ d = d / RU[end , end ]
103
+ q. d = [q. d d]
104
+
105
+ # iterate x_n = x_n-1 + d_n τ_n
106
+ axpy! (q. t[end - 1 ], d, q. x)
107
+
108
+ # Eq. 4.12, Freund 1990
109
+ q. resnorm = q. resnorm * abs (gend. s) * sqrt (n+ 1 )/ sqrt (n)
110
+
111
+ return q. resnorm, n + 1
112
+ end
113
+
114
+ """
115
+ lalqmr(A, b; kwargs...) -> x, [history]
116
+
117
+ Same as [`lalqmr!`](@ref), but allocates a solution vector `x` initialized with zeros.
118
+ """
119
+ lalqmr (A, b; kwargs... ) = lalqmr! (zerox (A, b), A, b; initially_zero = true , kwargs... )
120
+
121
+ """
122
+ lalqmr!(x, A, b; kwargs...) -> x, [history]
123
+
124
+ Solves the problem ``Ax = b`` with the Quasi-Minimal Residual (QMR) method with Look-ahead. See [`LookAheadLanczosDecomp`](@ref).
125
+
126
+ # Arguments
127
+ - `x`: Initial guess, will be updated in-place;
128
+ - `A`: linear operator;
129
+ - `b`: right-hand side.
130
+
131
+ ## Keywords
132
+
133
+ - `initally_zero::Bool`: If `true` assumes that `iszero(x)` so that one
134
+ matrix-vector product can be saved when computing the initial residual
135
+ vector;
136
+ - `maxiter::Int = size(A, 2)`: maximum number of iterations;
137
+ - `abstol::Real = zero(real(eltype(b)))`,
138
+ `reltol::Real = sqrt(eps(real(eltype(b))))`: absolute and relative
139
+ tolerance for the stopping condition
140
+ `|r_k| ≤ max(reltol * |r_0|, abstol)`, where `r_k = A * x_k - b`
141
+ - `log::Bool`: keep track of the residual norm in each iteration;
142
+ - `verbose::Bool`: print convergence information during the iteration.
143
+ - `max_block_size`: maximum block size during look-ahead process
144
+ - `max_memory`: maximum allowed memory for look-ahead process
145
+
146
+ # Return values
147
+
148
+ **if `log` is `false`**
149
+
150
+ - `x`: approximate solution.
151
+
152
+ **if `log` is `true`**
153
+
154
+ - `x`: approximate solution;
155
+
156
+ - `history`: convergence history.
157
+
158
+ [^Freund1990]:
159
+ Freund, W. R., & Nachtigal, N. M. (1990). QMR : for a Quasi-Minimal
160
+ Residual Linear Method Systems. (December).
161
+ """
162
+ function lalqmr! (
163
+ x, A, b;
164
+ abstol:: Real = zero (real (eltype (b))),
165
+ reltol:: Real = sqrt (eps (real (eltype (b)))),
166
+ maxiter:: Int = size (A, 2 ),
167
+ log:: Bool = false ,
168
+ initially_zero:: Bool = false ,
169
+ verbose:: Bool = false ,
170
+ kwargs...
171
+ )
172
+ history = ConvergenceHistory (partial = ! log)
173
+ history[:abstol ] = abstol
174
+ history[:reltol ] = reltol
175
+ log && reserve! (history, :resnorm , maxiter)
176
+
177
+ iterable = lalqmr_iterable! (
178
+ x, A, b;
179
+ abstol= abstol,
180
+ reltol= reltol,
181
+ maxiter= maxiter,
182
+ initially_zero= initially_zero,
183
+ log= log,
184
+ verbose= verbose
185
+ )
186
+
187
+ verbose && @printf (" === qmr ===\n %4s\t %7s\n " ," iter" ," resnorm" )
188
+
189
+ for (iteration, residual) = enumerate (iterable)
190
+ if log
191
+ nextiter! (history)
192
+ push! (history, :resnorm , residual)
193
+ end
194
+
195
+ verbose && @printf (" %3d\t %1.2e\n " , iteration, residual)
196
+ end
197
+
198
+ verbose && println ()
199
+ log && setconv (history, converged (iterable))
200
+ log && shrink! (history)
201
+
202
+ log ? (x, history) : x
203
+ end
0 commit comments