Eigen  5.0.1
 
Loading...
Searching...
No Matches
LSMR.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// This Source Code Form is subject to the terms of the Mozilla
5// Public License v. 2.0. If a copy of the MPL was not distributed
6// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
7// SPDX-FileCopyrightText: The Eigen Authors
8// SPDX-License-Identifier: MPL-2.0
9
10#ifndef EIGEN_LSMR_H
11#define EIGEN_LSMR_H
12
13// LSMR is an iterative algorithm for least-squares problems min ||A x - b||,
14// based on the Golub-Kahan bidiagonalization process. It is analytically
15// equivalent to applying MINRES to the normal equation A^T A x = A^T b, so the
16// quantity ||A^T r_k|| decreases monotonically (where r_k = b - A x_k). In
17// practice ||r_k|| also decreases monotonically, which makes LSMR safer than
18// LSQR to stop early. With a damping parameter lambda > 0 it instead minimizes
19// || (A; lambda I) x - (b; 0) ||, i.e. it solves the regularized (Tikhonov)
20// least-squares problem.
21//
22// This implementation follows the published algorithm:
23//
24// D. C.-L. Fong and M. A. Saunders, "LSMR: An Iterative Algorithm for Sparse
25// Least-Squares Problems", SIAM J. Sci. Comput. 33(5):2950-2971, 2011.
26// https://web.stanford.edu/group/SOL/software/lsmr/
27//
28// The scalar recurrences, the cheap estimates of ||r||, ||A^T r||, ||A|| and
29// cond(A), and the stopping rules reproduce the reference Fortran 90
30// implementation by the same authors (distributed under the BSD / Common Public
31// License at the URL above). No source code from that implementation is
32// reproduced here; only the mathematical algorithm is implemented, in Eigen's
33// own idiom.
34
35// IWYU pragma: private
36#include "./InternalHeaderCheck.h"
37
38namespace Eigen {
39
40namespace internal {
41
76template <typename MatrixType, typename Rhs, typename Dest, typename Preconditioner>
77EIGEN_DONT_INLINE Index lsmr(const MatrixType& mat, const Rhs& rhs, Dest& x, const Preconditioner& precond,
78 Index& iters, typename Dest::RealScalar& tol_error, const typename Dest::RealScalar& atol,
79 const typename Dest::RealScalar& btol, const typename Dest::RealScalar& lambda,
80 const typename Dest::RealScalar& conlim) {
81 using numext::abs;
82 using numext::sqrt;
83 using RealScalar = typename Dest::RealScalar;
84 using Scalar = typename Dest::Scalar;
85 using VectorType = Matrix<Scalar, Dynamic, 1>;
86
87 const RealScalar zero(0);
88 const RealScalar one(1);
89
90 const Index n = mat.cols();
91 const Index maxIters = iters;
92
93 // n-vectors needed before the early-return below. u is the only m-vector.
94 VectorType v(n), Atu(n);
95
96 // Set up the first vectors u and v for the bidiagonalization. These satisfy
97 // beta*u = b - A*x0 and alpha*v = M^{-1} A^T u.
98 VectorType u = rhs - mat * x; // working residual r0 of the initial guess
99 RealScalar alpha = zero;
100 RealScalar beta = u.stableNorm();
101 if (beta > zero) {
102 u /= beta;
103 Atu.noalias() = mat.adjoint() * u;
104 v = precond.solve(Atu);
105 alpha = v.stableNorm();
106 if (alpha > zero) v /= alpha;
107 }
108
109 iters = 0;
110 // If b - A*x0 = 0 or A^T(b - A*x0) = 0 then the current x already solves the
111 // (least-squares) problem: no correction is needed.
112 if (alpha * beta == zero) {
113 tol_error = zero;
114 return 0; // istop = 0
115 }
116
117 // dx accumulates the correction (in z-space when a preconditioner is used).
118 // The remaining n-vectors are only needed once we start iterating.
119 VectorType dx = VectorType::Zero(n);
120 VectorType h = v;
121 VectorType hbar = VectorType::Zero(n);
122 VectorType t(n);
123
124 // Quantities driving the two plane rotations.
125 RealScalar alphabar = alpha;
126 RealScalar zetabar = alpha * beta;
127 RealScalar rho = one;
128 RealScalar rhobar = one;
129 RealScalar cbar = one;
130 RealScalar sbar = zero;
131
132 // Quantities for the running estimate of ||r||.
133 RealScalar betadd = beta;
134 RealScalar betad = zero;
135 RealScalar rhodold = one;
136 RealScalar tautildeold = zero;
137 RealScalar thetatilde = zero;
138 RealScalar zeta = zero;
139 RealScalar d = zero;
140
141 // Quantities for the running estimates of ||A|| and cond(A).
142 RealScalar normA2 = alpha * alpha;
143 RealScalar maxrbar = zero;
144 RealScalar minrbar = NumTraits<RealScalar>::highest();
145
146 const RealScalar normb = beta;
147 const RealScalar ctol = conlim > zero ? one / conlim : zero;
148 RealScalar test2 = zero; // recomputed each iteration; also read after the loop for tol_error
149
150 Index istop = 0;
151 Index itn = 0;
152 while (istop == 0) {
153 ++itn;
154
155 // Perform the next step of the bidiagonalization to obtain the next
156 // beta, u, alpha, v. These satisfy
157 // beta*u = A*(M^{-1} v) - alpha*u,
158 // alpha*v = M^{-1} A^T u - beta*v.
159 t = precond.solve(v);
160 u *= -alpha;
161 u.noalias() += mat * t;
162 beta = u.stableNorm();
163 if (beta > zero) {
164 u /= beta;
165 Atu.noalias() = mat.adjoint() * u;
166 t = precond.solve(Atu);
167 v = t - beta * v;
168 alpha = v.stableNorm();
169 if (alpha > zero) v /= alpha;
170 }
171
172 // Construct rotation Qhat_{k} that folds in the damping.
173 const RealScalar alphahat = numext::hypot(alphabar, lambda);
174 const RealScalar chat = alphabar / alphahat;
175 const RealScalar shat = lambda / alphahat;
176
177 // Use a plane rotation Q_{k} to turn B_{k} into R_{k}.
178 const RealScalar rhoold = rho;
179 rho = numext::hypot(alphahat, beta);
180 const RealScalar c = alphahat / rho;
181 const RealScalar s = beta / rho;
182 const RealScalar thetanew = s * alpha;
183 alphabar = c * alpha;
184
185 // Use a plane rotation Qbar_{k} to turn R_{k}^T into Rbar_{k}.
186 const RealScalar rhobarold = rhobar;
187 const RealScalar zetaold = zeta;
188 const RealScalar thetabar = sbar * rho;
189 const RealScalar rhotemp = cbar * rho;
190 rhobar = numext::hypot(cbar * rho, thetanew);
191 cbar = cbar * rho / rhobar;
192 sbar = thetanew / rhobar;
193 zeta = cbar * zetabar;
194 zetabar = -sbar * zetabar;
195
196 // Update h, hbar and the (correction) solution dx.
197 hbar = h - (thetabar * rho / (rhoold * rhobarold)) * hbar;
198 dx += (zeta / (rho * rhobar)) * hbar;
199 h = v - (thetanew / rho) * h;
200
201 // Estimate ||r||. Apply rotation Qhat_{k}, then Q_{k}, then Qtilde_{k-1}.
202 const RealScalar betaacute = chat * betadd;
203 const RealScalar betacheck = -shat * betadd;
204 const RealScalar betahat = c * betaacute;
205 betadd = -s * betaacute;
206
207 const RealScalar thetatildeold = thetatilde;
208 const RealScalar rhotildeold = numext::hypot(rhodold, thetabar);
209 const RealScalar ctildeold = rhodold / rhotildeold;
210 const RealScalar stildeold = thetabar / rhotildeold;
211 thetatilde = stildeold * rhobar;
212 rhodold = ctildeold * rhobar;
213 betad = -stildeold * betad + ctildeold * betahat;
214
215 tautildeold = (zetaold - thetatildeold * tautildeold) / rhotildeold;
216 const RealScalar taud = (zeta - thetatilde * tautildeold) / rhodold;
217 d += betacheck * betacheck;
218 const RealScalar normr = sqrt(d + numext::abs2(betad - taud) + numext::abs2(betadd));
219
220 // Estimate ||A||.
221 normA2 += beta * beta;
222 const RealScalar normA = sqrt(normA2);
223 normA2 += alpha * alpha;
224
225 // Estimate cond(A).
226 maxrbar = numext::maxi(maxrbar, rhobarold);
227 if (itn > 1) minrbar = numext::mini(minrbar, rhobarold);
228 const RealScalar condA = numext::maxi(maxrbar, rhotemp) / numext::mini(minrbar, rhotemp);
229
230 // Compute the norms needed for the stopping rules.
231 const RealScalar normAr = abs(zetabar);
232 const RealScalar normx = dx.stableNorm();
233
234 const RealScalar test1 = normr / normb;
235 test2 = (normA * normr > zero) ? normAr / (normA * normr) : zero;
236 const RealScalar test3 = one / condA;
237 const RealScalar t1 = test1 / (one + normA * normx / normb);
238 const RealScalar rtol = btol + atol * normA * normx / normb;
239
240 // The "1 + test <= 1" guards trigger near machine precision and make the
241 // method behave as if atol = btol = eps and conlim = 1/eps even when the
242 // user passed 0 for any of them. The user tolerances are tested first so a
243 // genuine convergence (istop 1/2/3) takes priority over the machine limits.
244 // (istop 4 uses t1 rather than test1, matching the reference algorithm.)
245 if (test1 <= rtol)
246 istop = 1;
247 else if (test2 <= atol)
248 istop = 2;
249 else if (test3 <= ctol)
250 istop = 3;
251 else if (one + t1 <= one)
252 istop = 4;
253 else if (one + test2 <= one)
254 istop = 5;
255 else if (one + test3 <= one)
256 istop = 6;
257 else if (itn >= maxIters)
258 istop = 7;
259 }
260
261 // Recover the solution: x <- x0 + M^{-1} dx.
262 t = precond.solve(dx);
263 x += t;
264
265 iters = itn;
266 tol_error = test2;
267 return istop;
268}
269
270} // namespace internal
271
272template <typename MatrixType_, typename Preconditioner_ = IdentityPreconditioner>
273class LSMR;
274
275namespace internal {
276
277template <typename MatrixType_, typename Preconditioner_>
278struct traits<LSMR<MatrixType_, Preconditioner_> > {
279 using MatrixType = MatrixType_;
280 using Preconditioner = Preconditioner_;
281};
282
283} // namespace internal
284
353template <typename MatrixType_, typename Preconditioner_>
354class LSMR : public IterativeSolverBase<LSMR<MatrixType_, Preconditioner_> > {
355 protected:
356 using Base = IterativeSolverBase<LSMR>;
357 using Base::m_error;
358 using Base::m_info;
359 using Base::m_isInitialized;
360 using Base::m_iterations;
361 using Base::matrix;
362
363 public:
364 using MatrixType = MatrixType_;
365 using Scalar = typename MatrixType::Scalar;
366 using RealScalar = typename MatrixType::RealScalar;
367 using Preconditioner = Preconditioner_;
368
370 LSMR() : Base() {}
371
382 template <typename MatrixDerived>
383 explicit LSMR(const EigenBase<MatrixDerived>& A) : Base(A.derived()) {}
384
394 LSMR& setDamping(const RealScalar& lambda) {
395 m_lambda = lambda;
396 return *this;
397 }
398
400 RealScalar damping() const { return m_lambda; }
401
406 LSMR& setConditionLimit(const RealScalar& conlim) {
407 m_conditionLimit = conlim;
408 return *this;
409 }
410
412 RealScalar conditionLimit() const { return m_conditionLimit; }
413
418 LSMR& setToleranceA(const RealScalar& atol) {
419 m_atol = atol;
420 return *this;
421 }
422
425 RealScalar toleranceA() const { return m_atol >= RealScalar(0) ? m_atol : Base::m_tolerance; }
426
431 LSMR& setToleranceB(const RealScalar& btol) {
432 m_btol = btol;
433 return *this;
434 }
435
438 RealScalar toleranceB() const { return m_btol >= RealScalar(0) ? m_btol : Base::m_tolerance; }
439
441 template <typename Rhs, typename Dest>
442 void _solve_vector_with_guess_impl(const Rhs& b, Dest& x) const {
443 m_iterations = Base::maxIterations();
444
445 Index istop = internal::lsmr(matrix(), b, x, Base::m_preconditioner, m_iterations, m_error, toleranceA(),
446 toleranceB(), m_lambda, m_conditionLimit);
447 // istop in {0,1,2,4,5}: the (least-squares) solution was found, possibly
448 // only to within machine precision (4,5). istop in {3,6,7}: stopped on the
449 // condition-number limit or the iteration limit without meeting the
450 // requested tolerance.
451 m_info = (istop == 3 || istop == 6 || istop == 7) ? NoConvergence : Success;
452 }
453
454 protected:
455 RealScalar m_lambda = RealScalar(0);
456 RealScalar m_conditionLimit = RealScalar(0);
457 // Negative means "unset": toleranceA()/toleranceB() then fall back to tolerance().
458 RealScalar m_atol = RealScalar(-1);
459 RealScalar m_btol = RealScalar(-1);
460};
461
462} // end namespace Eigen
463
464#endif // EIGEN_LSMR_H
Index maxIterations() const
Definition IterativeSolverBase.h:245
An LSMR solver for sparse (or dense) least-squares problems.
Definition LSMR.h:354
LSMR(const EigenBase< MatrixDerived > &A)
Definition LSMR.h:383
LSMR()
Definition LSMR.h:370
LSMR & setConditionLimit(const RealScalar &conlim)
Definition LSMR.h:406
LSMR & setDamping(const RealScalar &lambda)
Definition LSMR.h:394
LSMR & setToleranceB(const RealScalar &btol)
Definition LSMR.h:431
RealScalar conditionLimit() const
Definition LSMR.h:412
RealScalar damping() const
Definition LSMR.h:400
RealScalar toleranceB() const
Definition LSMR.h:438
LSMR & setToleranceA(const RealScalar &atol)
Definition LSMR.h:418
RealScalar toleranceA() const
Definition LSMR.h:425
@ Success
Definition Constants.h:457
@ NoConvergence
Definition Constants.h:461
Definition EigenBase.h:34