| // This file is part of Eigen, a lightweight C++ template library |
| // for linear algebra. |
| // |
| // This Source Code Form is subject to the terms of the Mozilla |
| // Public License v. 2.0. If a copy of the MPL was not distributed |
| // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| // SPDX-FileCopyrightText: The Eigen Authors |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| #ifndef EIGEN_LSMR_H |
| #define EIGEN_LSMR_H |
| |
| // LSMR is an iterative algorithm for least-squares problems min ||A x - b||, |
| // based on the Golub-Kahan bidiagonalization process. It is analytically |
| // equivalent to applying MINRES to the normal equation A^T A x = A^T b, so the |
| // quantity ||A^T r_k|| decreases monotonically (where r_k = b - A x_k). In |
| // practice ||r_k|| also decreases monotonically, which makes LSMR safer than |
| // LSQR to stop early. With a damping parameter lambda > 0 it instead minimizes |
| // || (A; lambda I) x - (b; 0) ||, i.e. it solves the regularized (Tikhonov) |
| // least-squares problem. |
| // |
| // This implementation follows the published algorithm: |
| // |
| // D. C.-L. Fong and M. A. Saunders, "LSMR: An Iterative Algorithm for Sparse |
| // Least-Squares Problems", SIAM J. Sci. Comput. 33(5):2950-2971, 2011. |
| // https://web.stanford.edu/group/SOL/software/lsmr/ |
| // |
| // The scalar recurrences, the cheap estimates of ||r||, ||A^T r||, ||A|| and |
| // cond(A), and the stopping rules reproduce the reference Fortran 90 |
| // implementation by the same authors (distributed under the BSD / Common Public |
| // License at the URL above). No source code from that implementation is |
| // reproduced here; only the mathematical algorithm is implemented, in Eigen's |
| // own idiom. |
| |
| // IWYU pragma: private |
| #include "./InternalHeaderCheck.h" |
| |
| namespace Eigen { |
| |
| namespace internal { |
| |
| /** \internal Low-level LSMR algorithm |
| * \param mat The matrix A. |
| * \param rhs The right hand side vector b. |
| * \param x On input the initial guess x0 (usually zero), on output the |
| * computed solution. |
| * \param precond A preconditioner. It is applied as a self-adjoint right |
| * preconditioner: LSMR is run on the better-conditioned system |
| * \f$ A M^{-1} z = b \f$ (where \c precond.solve() applies |
| * \f$ M^{-1} \f$) and the solution is recovered as |
| * \f$ x = M^{-1} z \f$. With IdentityPreconditioner this |
| * reduces exactly to the reference algorithm. |
| * \param iters On input the maximum number of iterations, on output the |
| * number of iterations performed. |
| * \param tol_error On output, an estimate of the relative residual of the |
| * normal equations \f$ ||A^T r|| / (||A||\,||r||) \f$. |
| * \param atol Stopping tolerance bounding the assumed relative error in |
| * the entries of \a A. |
| * \param btol Stopping tolerance bounding the assumed relative error in |
| * the entries of \a b. |
| * \param lambda The damping parameter \f$ \lambda \ge 0 \f$ (0 for an |
| * unregularized problem). |
| * \param conlim An upper limit on cond(A); iterations stop if the estimate |
| * exceeds it. Pass 0 to disable (equivalent to 1/eps). |
| * |
| * \returns the LSMR stopping code \c istop: |
| * - 0: x = 0 (i.e. the supplied guess) is the exact solution; no iterations. |
| * - 1: A x = b is compatible and ||r|| is small enough, given atol and btol. |
| * - 2: a least-squares solution is good enough, given atol. |
| * - 3: the estimate of cond(A) exceeded conlim. |
| * - 4: A x = b is compatible and ||r|| is as small as machine precision allows. |
| * - 5: a least-squares solution is as good as machine precision allows. |
| * - 6: cond(A) seems too large for this machine. |
| * - 7: the iteration limit was reached. |
| */ |
| template <typename MatrixType, typename Rhs, typename Dest, typename Preconditioner> |
| EIGEN_DONT_INLINE Index lsmr(const MatrixType& mat, const Rhs& rhs, Dest& x, const Preconditioner& precond, |
| Index& iters, typename Dest::RealScalar& tol_error, const typename Dest::RealScalar& atol, |
| const typename Dest::RealScalar& btol, const typename Dest::RealScalar& lambda, |
| const typename Dest::RealScalar& conlim) { |
| using numext::abs; |
| using numext::sqrt; |
| using RealScalar = typename Dest::RealScalar; |
| using Scalar = typename Dest::Scalar; |
| using VectorType = Matrix<Scalar, Dynamic, 1>; |
| |
| const RealScalar zero(0); |
| const RealScalar one(1); |
| |
| const Index n = mat.cols(); |
| const Index maxIters = iters; |
| |
| // n-vectors needed before the early-return below. u is the only m-vector. |
| VectorType v(n), Atu(n); |
| |
| // Set up the first vectors u and v for the bidiagonalization. These satisfy |
| // beta*u = b - A*x0 and alpha*v = M^{-1} A^T u. |
| VectorType u = rhs - mat * x; // working residual r0 of the initial guess |
| RealScalar alpha = zero; |
| RealScalar beta = u.stableNorm(); |
| if (beta > zero) { |
| u /= beta; |
| Atu.noalias() = mat.adjoint() * u; |
| v = precond.solve(Atu); |
| alpha = v.stableNorm(); |
| if (alpha > zero) v /= alpha; |
| } |
| |
| iters = 0; |
| // If b - A*x0 = 0 or A^T(b - A*x0) = 0 then the current x already solves the |
| // (least-squares) problem: no correction is needed. |
| if (alpha * beta == zero) { |
| tol_error = zero; |
| return 0; // istop = 0 |
| } |
| |
| // dx accumulates the correction (in z-space when a preconditioner is used). |
| // The remaining n-vectors are only needed once we start iterating. |
| VectorType dx = VectorType::Zero(n); |
| VectorType h = v; |
| VectorType hbar = VectorType::Zero(n); |
| VectorType t(n); |
| |
| // Quantities driving the two plane rotations. |
| RealScalar alphabar = alpha; |
| RealScalar zetabar = alpha * beta; |
| RealScalar rho = one; |
| RealScalar rhobar = one; |
| RealScalar cbar = one; |
| RealScalar sbar = zero; |
| |
| // Quantities for the running estimate of ||r||. |
| RealScalar betadd = beta; |
| RealScalar betad = zero; |
| RealScalar rhodold = one; |
| RealScalar tautildeold = zero; |
| RealScalar thetatilde = zero; |
| RealScalar zeta = zero; |
| RealScalar d = zero; |
| |
| // Quantities for the running estimates of ||A|| and cond(A). |
| RealScalar normA2 = alpha * alpha; |
| RealScalar maxrbar = zero; |
| RealScalar minrbar = NumTraits<RealScalar>::highest(); |
| |
| const RealScalar normb = beta; |
| const RealScalar ctol = conlim > zero ? one / conlim : zero; |
| RealScalar test2 = zero; // recomputed each iteration; also read after the loop for tol_error |
| |
| Index istop = 0; |
| Index itn = 0; |
| while (istop == 0) { |
| ++itn; |
| |
| // Perform the next step of the bidiagonalization to obtain the next |
| // beta, u, alpha, v. These satisfy |
| // beta*u = A*(M^{-1} v) - alpha*u, |
| // alpha*v = M^{-1} A^T u - beta*v. |
| t = precond.solve(v); |
| u *= -alpha; |
| u.noalias() += mat * t; |
| beta = u.stableNorm(); |
| if (beta > zero) { |
| u /= beta; |
| Atu.noalias() = mat.adjoint() * u; |
| t = precond.solve(Atu); |
| v = t - beta * v; |
| alpha = v.stableNorm(); |
| if (alpha > zero) v /= alpha; |
| } |
| |
| // Construct rotation Qhat_{k} that folds in the damping. |
| const RealScalar alphahat = numext::hypot(alphabar, lambda); |
| const RealScalar chat = alphabar / alphahat; |
| const RealScalar shat = lambda / alphahat; |
| |
| // Use a plane rotation Q_{k} to turn B_{k} into R_{k}. |
| const RealScalar rhoold = rho; |
| rho = numext::hypot(alphahat, beta); |
| const RealScalar c = alphahat / rho; |
| const RealScalar s = beta / rho; |
| const RealScalar thetanew = s * alpha; |
| alphabar = c * alpha; |
| |
| // Use a plane rotation Qbar_{k} to turn R_{k}^T into Rbar_{k}. |
| const RealScalar rhobarold = rhobar; |
| const RealScalar zetaold = zeta; |
| const RealScalar thetabar = sbar * rho; |
| const RealScalar rhotemp = cbar * rho; |
| rhobar = numext::hypot(cbar * rho, thetanew); |
| cbar = cbar * rho / rhobar; |
| sbar = thetanew / rhobar; |
| zeta = cbar * zetabar; |
| zetabar = -sbar * zetabar; |
| |
| // Update h, hbar and the (correction) solution dx. |
| hbar = h - (thetabar * rho / (rhoold * rhobarold)) * hbar; |
| dx += (zeta / (rho * rhobar)) * hbar; |
| h = v - (thetanew / rho) * h; |
| |
| // Estimate ||r||. Apply rotation Qhat_{k}, then Q_{k}, then Qtilde_{k-1}. |
| const RealScalar betaacute = chat * betadd; |
| const RealScalar betacheck = -shat * betadd; |
| const RealScalar betahat = c * betaacute; |
| betadd = -s * betaacute; |
| |
| const RealScalar thetatildeold = thetatilde; |
| const RealScalar rhotildeold = numext::hypot(rhodold, thetabar); |
| const RealScalar ctildeold = rhodold / rhotildeold; |
| const RealScalar stildeold = thetabar / rhotildeold; |
| thetatilde = stildeold * rhobar; |
| rhodold = ctildeold * rhobar; |
| betad = -stildeold * betad + ctildeold * betahat; |
| |
| tautildeold = (zetaold - thetatildeold * tautildeold) / rhotildeold; |
| const RealScalar taud = (zeta - thetatilde * tautildeold) / rhodold; |
| d += betacheck * betacheck; |
| const RealScalar normr = sqrt(d + numext::abs2(betad - taud) + numext::abs2(betadd)); |
| |
| // Estimate ||A||. |
| normA2 += beta * beta; |
| const RealScalar normA = sqrt(normA2); |
| normA2 += alpha * alpha; |
| |
| // Estimate cond(A). |
| maxrbar = numext::maxi(maxrbar, rhobarold); |
| if (itn > 1) minrbar = numext::mini(minrbar, rhobarold); |
| const RealScalar condA = numext::maxi(maxrbar, rhotemp) / numext::mini(minrbar, rhotemp); |
| |
| // Compute the norms needed for the stopping rules. |
| const RealScalar normAr = abs(zetabar); |
| const RealScalar normx = dx.stableNorm(); |
| |
| const RealScalar test1 = normr / normb; |
| test2 = (normA * normr > zero) ? normAr / (normA * normr) : zero; |
| const RealScalar test3 = one / condA; |
| const RealScalar t1 = test1 / (one + normA * normx / normb); |
| const RealScalar rtol = btol + atol * normA * normx / normb; |
| |
| // The "1 + test <= 1" guards trigger near machine precision and make the |
| // method behave as if atol = btol = eps and conlim = 1/eps even when the |
| // user passed 0 for any of them. The user tolerances are tested first so a |
| // genuine convergence (istop 1/2/3) takes priority over the machine limits. |
| // (istop 4 uses t1 rather than test1, matching the reference algorithm.) |
| if (test1 <= rtol) |
| istop = 1; |
| else if (test2 <= atol) |
| istop = 2; |
| else if (test3 <= ctol) |
| istop = 3; |
| else if (one + t1 <= one) |
| istop = 4; |
| else if (one + test2 <= one) |
| istop = 5; |
| else if (one + test3 <= one) |
| istop = 6; |
| else if (itn >= maxIters) |
| istop = 7; |
| } |
| |
| // Recover the solution: x <- x0 + M^{-1} dx. |
| t = precond.solve(dx); |
| x += t; |
| |
| iters = itn; |
| tol_error = test2; |
| return istop; |
| } |
| |
| } // namespace internal |
| |
| template <typename MatrixType_, typename Preconditioner_ = IdentityPreconditioner> |
| class LSMR; |
| |
| namespace internal { |
| |
| template <typename MatrixType_, typename Preconditioner_> |
| struct traits<LSMR<MatrixType_, Preconditioner_> > { |
| using MatrixType = MatrixType_; |
| using Preconditioner = Preconditioner_; |
| }; |
| |
| } // namespace internal |
| |
| /** \ingroup IterativeLinearSolvers_Module |
| * \brief An LSMR solver for sparse (or dense) least-squares problems |
| * |
| * This class solves for the least-squares solution of \c A \c x = \c b using the |
| * LSMR algorithm of Fong and Saunders. LSMR is based on the Golub-Kahan |
| * bidiagonalization and is analytically equivalent to MINRES applied to the |
| * normal equation \f$ A^T A x = A^T b \f$, so the residual of the normal |
| * equation \f$ ||A^T r|| \f$ decreases monotonically. In exact arithmetic LSMR |
| * returns the minimum-norm least-squares solution. The matrix \c A can be |
| * non-symmetric and rectangular; \c A and the vectors \c x and \c b can be |
| * either dense or sparse. LSMR only needs \c A through the products \f$ Av \f$ |
| * and \f$ A^T u \f$, so a matrix-free operator may be used as well. |
| * |
| * Unlike LeastSquaresConjugateGradient, which forms \f$ A^T A \f$ implicitly, |
| * LSMR works on \c A directly through the bidiagonalization and is therefore |
| * more robust on ill-conditioned problems. |
| * |
| * \tparam MatrixType_ the type of the matrix A, can be a dense or a sparse matrix. |
| * \tparam Preconditioner_ the type of the preconditioner. Default is IdentityPreconditioner. |
| * |
| * \implsparsesolverconcept |
| * |
| * The maximum number of iterations and the tolerance can be controlled via the |
| * setMaxIterations() and setTolerance() methods. The defaults are twice the |
| * number of columns of the matrix for the maximum number of iterations and |
| * NumTraits<Scalar>::epsilon() for the tolerance. setTolerance() sets both of |
| * the algorithm's stopping tolerances \c atol (relative error assumed in \c A) |
| * and \c btol (relative error assumed in \c b); they can also be set |
| * independently via setToleranceA() and setToleranceB(). |
| * |
| * Unlike most other iterative solvers, error() does not report the relative |
| * residual \f$ ||Ax-b||/||b|| \f$: it reports the estimate |
| * \f$ ||A^T r|| / (||A||\,||r||) \f$, with \f$ r = b - Ax \f$, of the relative |
| * residual of the normal equations, the quantity that the least-squares |
| * stopping rule bounds by \c atol. |
| * |
| * The setDamping() method enables Tikhonov regularization: with a damping |
| * \f$ \lambda > 0 \f$ the solver minimizes |
| * \f$ ||Ax-b||^2 + \lambda^2 ||x||^2 \f$, for which a unique solution always |
| * exists. The setConditionLimit() method can be used to stop the iterations as |
| * soon as the estimated condition number of \c A exceeds a given bound. |
| * |
| * This class can be used like the other iterative solvers. Here is a typical |
| * usage example: |
| \code |
| int m = 1000000, n = 10000; |
| VectorXd x(n), b(m); |
| SparseMatrix<double> A(m, n); |
| // fill A and b |
| LSMR<SparseMatrix<double> > lsmr; |
| lsmr.compute(A); |
| x = lsmr.solve(b); |
| std::cout << "#iterations: " << lsmr.iterations() << std::endl; |
| std::cout << "estimated error: " << lsmr.error() << std::endl; |
| // update b, and solve again |
| x = lsmr.solve(b); |
| \endcode |
| * |
| * By default the iterations start with x=0 as an initial guess of the solution. |
| * One can control the start using the solveWithGuess() method. |
| * |
| * If a non-default preconditioner is supplied it is applied as a self-adjoint |
| * right preconditioner (LSMR is run on \f$ A M^{-1} z = b \f$ and the solution |
| * recovered from \f$ M x = z \f$); it should therefore be self-adjoint, as |
| * DiagonalPreconditioner and IdentityPreconditioner are. |
| * |
| * \sa class LeastSquaresConjugateGradient, class ConjugateGradient, SparseLU, SparseQR |
| */ |
| template <typename MatrixType_, typename Preconditioner_> |
| class LSMR : public IterativeSolverBase<LSMR<MatrixType_, Preconditioner_> > { |
| protected: |
| using Base = IterativeSolverBase<LSMR>; |
| using Base::m_error; |
| using Base::m_info; |
| using Base::m_isInitialized; |
| using Base::m_iterations; |
| using Base::matrix; |
| |
| public: |
| using MatrixType = MatrixType_; |
| using Scalar = typename MatrixType::Scalar; |
| using RealScalar = typename MatrixType::RealScalar; |
| using Preconditioner = Preconditioner_; |
| |
| /** Default constructor. */ |
| LSMR() : Base() {} |
| |
| /** Initialize the solver with matrix \a A for further \c Ax=b solving. |
| * |
| * This constructor is a shortcut for the default constructor followed |
| * by a call to compute(). |
| * |
| * \warning this class stores a reference to the matrix A as well as some |
| * precomputed values that depend on it. Therefore, if \a A is changed |
| * this class becomes invalid. Call compute() to update it with the new |
| * matrix A, or modify a copy of A. |
| */ |
| template <typename MatrixDerived> |
| explicit LSMR(const EigenBase<MatrixDerived>& A) : Base(A.derived()) {} |
| |
| /** Sets the damping parameter \f$ \lambda \ge 0 \f$ for Tikhonov |
| * regularization. With \a lambda > 0 the solver minimizes |
| * \f$ ||Ax-b||^2 + \lambda^2 ||x||^2 \f$. The default is 0 (no damping). |
| * |
| * \note When a non-identity preconditioner \f$ M \f$ is used the damping |
| * applies in the preconditioned variable, i.e. the solver minimizes |
| * \f$ ||Ax-b||^2 + \lambda^2 ||Mx||^2 \f$. With the default |
| * IdentityPreconditioner this is exactly \f$ ||Ax-b||^2 + \lambda^2 ||x||^2 \f$. |
| */ |
| LSMR& setDamping(const RealScalar& lambda) { |
| m_lambda = lambda; |
| return *this; |
| } |
| |
| /** \returns the damping parameter. \sa setDamping() */ |
| RealScalar damping() const { return m_lambda; } |
| |
| /** Sets an upper limit on the estimated condition number of \a A. The |
| * iterations stop as soon as the estimate exceeds \a conlim. The default is |
| * 0, which disables the limit (equivalent to 1/epsilon). |
| */ |
| LSMR& setConditionLimit(const RealScalar& conlim) { |
| m_conditionLimit = conlim; |
| return *this; |
| } |
| |
| /** \returns the condition-number limit. \sa setConditionLimit() */ |
| RealScalar conditionLimit() const { return m_conditionLimit; } |
| |
| /** Sets the stopping tolerance \c atol, which bounds the relative error |
| * assumed in the entries of \a A. It drives the least-squares stopping rule |
| * \f$ ||A^T r|| \le atol\,||A||\,||r|| \f$. If left unset (the default) it |
| * falls back to tolerance(). \sa setToleranceB(), setTolerance() */ |
| LSMR& setToleranceA(const RealScalar& atol) { |
| m_atol = atol; |
| return *this; |
| } |
| |
| /** \returns \c atol, or tolerance() if setToleranceA() has not been called. |
| * \sa setToleranceA() */ |
| RealScalar toleranceA() const { return m_atol >= RealScalar(0) ? m_atol : Base::m_tolerance; } |
| |
| /** Sets the stopping tolerance \c btol, which bounds the relative error |
| * assumed in the entries of \a b. It enters the compatible-system stopping |
| * rule \f$ ||r|| \le btol\,||b|| + atol\,||A||\,||x|| \f$. If left unset (the |
| * default) it falls back to tolerance(). \sa setToleranceA(), setTolerance() */ |
| LSMR& setToleranceB(const RealScalar& btol) { |
| m_btol = btol; |
| return *this; |
| } |
| |
| /** \returns \c btol, or tolerance() if setToleranceB() has not been called. |
| * \sa setToleranceB() */ |
| RealScalar toleranceB() const { return m_btol >= RealScalar(0) ? m_btol : Base::m_tolerance; } |
| |
| /** \internal */ |
| template <typename Rhs, typename Dest> |
| void _solve_vector_with_guess_impl(const Rhs& b, Dest& x) const { |
| m_iterations = Base::maxIterations(); |
| |
| Index istop = internal::lsmr(matrix(), b, x, Base::m_preconditioner, m_iterations, m_error, toleranceA(), |
| toleranceB(), m_lambda, m_conditionLimit); |
| // istop in {0,1,2,4,5}: the (least-squares) solution was found, possibly |
| // only to within machine precision (4,5). istop in {3,6,7}: stopped on the |
| // condition-number limit or the iteration limit without meeting the |
| // requested tolerance. |
| m_info = (istop == 3 || istop == 6 || istop == 7) ? NoConvergence : Success; |
| } |
| |
| protected: |
| RealScalar m_lambda = RealScalar(0); |
| RealScalar m_conditionLimit = RealScalar(0); |
| // Negative means "unset": toleranceA()/toleranceB() then fall back to tolerance(). |
| RealScalar m_atol = RealScalar(-1); |
| RealScalar m_btol = RealScalar(-1); |
| }; |
| |
| } // end namespace Eigen |
| |
| #endif // EIGEN_LSMR_H |