|  | // This file is part of Eigen, a lightweight C++ template library | 
|  | // for linear algebra. | 
|  | // | 
|  | // Copyright (C) 2020 Chris Schoutrop <c.e.m.schoutrop@tue.nl> | 
|  | // Copyright (C) 2020 Jens Wehner <j.wehner@esciencecenter.nl> | 
|  | // Copyright (C) 2020 Jan van Dijk <j.v.dijk@tue.nl> | 
|  | // Copyright (C) 2020 Adithya Vijaykumar | 
|  | // | 
|  | // 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/. | 
|  |  | 
|  | /* | 
|  |  | 
|  | This implementation of BiCGStab(L) is based on the papers | 
|  | General algorithm: | 
|  | 1. G.L.G. Sleijpen, D.R. Fokkema. (1993). BiCGstab(l) for linear equations | 
|  | involving unsymmetric matrices with complex spectrum. Electronic Transactions | 
|  | on Numerical Analysis. Polynomial step update: | 
|  | 2. G.L.G. Sleijpen, M.B. Van Gijzen. (2010) Exploiting BiCGstab(l) | 
|  | strategies to induce dimension reduction SIAM Journal on Scientific Computing. | 
|  | 3. Fokkema, Diederik R. Enhanced implementation of BiCGstab (l) for | 
|  | solving linear systems of equations. Universiteit Utrecht. Mathematisch | 
|  | Instituut, 1996 | 
|  | 4. Sleijpen, G. L., & van der Vorst, H. A. (1996). Reliable updated | 
|  | residuals in hybrid Bi-CG methods. Computing, 56(2), 141-163. | 
|  | */ | 
|  |  | 
|  | #ifndef EIGEN_BICGSTABL_H | 
|  | #define EIGEN_BICGSTABL_H | 
|  |  | 
|  | namespace Eigen { | 
|  |  | 
|  | namespace internal { | 
|  | /**     \internal Low-level bi conjugate gradient stabilized algorithm with L | 
|  | additional residual minimization steps \param mat The matrix A \param rhs The | 
|  | right hand side vector b \param x On input and initial solution, on output | 
|  | the computed solution. \param precond A preconditioner being able to | 
|  | efficiently solve for an approximation of Ax=b (regardless of b) \param iters | 
|  | On input the max number of iteration, on output the number of performed | 
|  | iterations. \param tol_error On input the tolerance error, on output an | 
|  | estimation of the relative error. \param L On input Number of additional | 
|  | GMRES steps to take. If L is too large (~20) instabilities occur. \return | 
|  | false in the case of numerical issue, for example a break down of BiCGSTABL. | 
|  | */ | 
|  | template <typename MatrixType, typename Rhs, typename Dest, typename Preconditioner> | 
|  | bool bicgstabl(const MatrixType &mat, const Rhs &rhs, Dest &x, const Preconditioner &precond, Index &iters, | 
|  | typename Dest::RealScalar &tol_error, Index L) { | 
|  | using numext::abs; | 
|  | using numext::sqrt; | 
|  | typedef typename Dest::RealScalar RealScalar; | 
|  | typedef typename Dest::Scalar Scalar; | 
|  | const Index N = rhs.size(); | 
|  | L = L < x.rows() ? L : x.rows(); | 
|  |  | 
|  | Index k = 0; | 
|  |  | 
|  | const RealScalar tol = tol_error; | 
|  | const Index maxIters = iters; | 
|  |  | 
|  | typedef Matrix<Scalar, Dynamic, 1> VectorType; | 
|  | typedef Matrix<Scalar, Dynamic, Dynamic, ColMajor> DenseMatrixType; | 
|  |  | 
|  | DenseMatrixType rHat(N, L + 1); | 
|  | DenseMatrixType uHat(N, L + 1); | 
|  |  | 
|  | // We start with an initial guess x_0 and let us set r_0 as (residual | 
|  | // calculated from x_0) | 
|  | VectorType x0 = x; | 
|  | rHat.col(0) = rhs - mat * x0;  // r_0 | 
|  |  | 
|  | x.setZero();  // This will contain the updates to the solution. | 
|  | // rShadow is arbritary, but must never be orthogonal to any residual. | 
|  | VectorType rShadow = VectorType::Random(N); | 
|  |  | 
|  | VectorType x_prime = x; | 
|  |  | 
|  | // Redundant: x is already set to 0 | 
|  | // x.setZero(); | 
|  | VectorType b_prime = rHat.col(0); | 
|  |  | 
|  | // Other vectors and scalars initialization | 
|  | Scalar rho0 = 1.0; | 
|  | Scalar alpha = 0.0; | 
|  | Scalar omega = 1.0; | 
|  |  | 
|  | uHat.col(0).setZero(); | 
|  |  | 
|  | bool bicg_convergence = false; | 
|  |  | 
|  | const RealScalar normb = rhs.stableNorm(); | 
|  | if (internal::isApprox(normb, RealScalar(0))) { | 
|  | x.setZero(); | 
|  | iters = 0; | 
|  | return true; | 
|  | } | 
|  | RealScalar normr = rHat.col(0).stableNorm(); | 
|  | RealScalar Mx = normr; | 
|  | RealScalar Mr = normr; | 
|  |  | 
|  | // Keep track of the solution with the lowest residual | 
|  | RealScalar normr_min = normr; | 
|  | VectorType x_min = x_prime + x; | 
|  |  | 
|  | // Criterion for when to apply the group-wise update, conform ref 3. | 
|  | const RealScalar delta = 0.01; | 
|  |  | 
|  | bool compute_res = false; | 
|  | bool update_app = false; | 
|  |  | 
|  | while (normr > tol * normb && k < maxIters) { | 
|  | rho0 *= -omega; | 
|  |  | 
|  | for (Index j = 0; j < L; ++j) { | 
|  | const Scalar rho1 = rShadow.dot(rHat.col(j)); | 
|  |  | 
|  | if (!(numext::isfinite)(rho1) || rho0 == RealScalar(0.0)) { | 
|  | // We cannot continue computing, return the best solution found. | 
|  | x += x_prime; | 
|  |  | 
|  | // Check if x is better than the best stored solution thus far. | 
|  | normr = (rhs - mat * (precond.solve(x) + x0)).stableNorm(); | 
|  |  | 
|  | if (normr > normr_min || !(numext::isfinite)(normr)) { | 
|  | // x_min is a better solution than x, return x_min | 
|  | x = x_min; | 
|  | normr = normr_min; | 
|  | } | 
|  | tol_error = normr / normb; | 
|  | iters = k; | 
|  | // x contains the updates to x0, add those back to obtain the solution | 
|  | x = precond.solve(x); | 
|  | x += x0; | 
|  | return (normr < tol * normb); | 
|  | } | 
|  |  | 
|  | const Scalar beta = alpha * (rho1 / rho0); | 
|  | rho0 = rho1; | 
|  | // Update search directions | 
|  | uHat.leftCols(j + 1) = rHat.leftCols(j + 1) - beta * uHat.leftCols(j + 1); | 
|  | uHat.col(j + 1) = mat * precond.solve(uHat.col(j)); | 
|  | const Scalar sigma = rShadow.dot(uHat.col(j + 1)); | 
|  | alpha = rho1 / sigma; | 
|  | // Update residuals | 
|  | rHat.leftCols(j + 1) -= alpha * uHat.middleCols(1, j + 1); | 
|  | rHat.col(j + 1) = mat * precond.solve(rHat.col(j)); | 
|  | // Complete BiCG iteration by updating x | 
|  | x += alpha * uHat.col(0); | 
|  | normr = rHat.col(0).stableNorm(); | 
|  | // Check for early exit | 
|  | if (normr < tol * normb) { | 
|  | /* | 
|  | Convergence was achieved during BiCG step. | 
|  | Without this check BiCGStab(L) fails for trivial matrices, such as | 
|  | when the preconditioner already is the inverse, or the input matrix is | 
|  | identity. | 
|  | */ | 
|  | bicg_convergence = true; | 
|  | break; | 
|  | } else if (normr < normr_min) { | 
|  | // We found an x with lower residual, keep this one. | 
|  | x_min = x + x_prime; | 
|  | normr_min = normr; | 
|  | } | 
|  | } | 
|  | if (!bicg_convergence) { | 
|  | /* | 
|  | The polynomial/minimize residual step. | 
|  |  | 
|  | QR Householder method for argmin is more stable than (modified) | 
|  | Gram-Schmidt, in the sense that there is less loss of orthogonality. It | 
|  | is more accurate than solving the normal equations, since the normal | 
|  | equations scale with condition number squared. | 
|  | */ | 
|  | const VectorType gamma = rHat.rightCols(L).householderQr().solve(rHat.col(0)); | 
|  | x += rHat.leftCols(L) * gamma; | 
|  | rHat.col(0) -= rHat.rightCols(L) * gamma; | 
|  | uHat.col(0) -= uHat.rightCols(L) * gamma; | 
|  | normr = rHat.col(0).stableNorm(); | 
|  | omega = gamma(L - 1); | 
|  | } | 
|  | if (normr < normr_min) { | 
|  | // We found an x with lower residual, keep this one. | 
|  | x_min = x + x_prime; | 
|  | normr_min = normr; | 
|  | } | 
|  |  | 
|  | k++; | 
|  |  | 
|  | /* | 
|  | Reliable update part | 
|  |  | 
|  | The recursively computed residual can deviate from the actual residual | 
|  | after several iterations. However, computing the residual from the | 
|  | definition costs extra MVs and should not be done at each iteration. The | 
|  | reliable update strategy computes the true residual from the definition: | 
|  | r=b-A*x at strategic intervals. Furthermore a "group wise update" strategy | 
|  | is used to combine updates, which improves accuracy. | 
|  | */ | 
|  |  | 
|  | // Maximum norm of residuals since last update of x. | 
|  | Mx = numext::maxi(Mx, normr); | 
|  | // Maximum norm of residuals since last computation of the true residual. | 
|  | Mr = numext::maxi(Mr, normr); | 
|  |  | 
|  | if (normr < delta * normb && normb <= Mx) { | 
|  | update_app = true; | 
|  | } | 
|  |  | 
|  | if (update_app || (normr < delta * Mr && normb <= Mr)) { | 
|  | compute_res = true; | 
|  | } | 
|  |  | 
|  | if (bicg_convergence) { | 
|  | update_app = true; | 
|  | compute_res = true; | 
|  | bicg_convergence = false; | 
|  | } | 
|  |  | 
|  | if (compute_res) { | 
|  | // Explicitly compute residual from the definition | 
|  |  | 
|  | // This is equivalent to the shifted version of rhs - mat * | 
|  | // (precond.solve(x)+x0) | 
|  | rHat.col(0) = b_prime - mat * precond.solve(x); | 
|  | normr = rHat.col(0).stableNorm(); | 
|  | Mr = normr; | 
|  |  | 
|  | if (update_app) { | 
|  | // After the group wise update, the original problem is translated to a | 
|  | // shifted one. | 
|  | x_prime += x; | 
|  | x.setZero(); | 
|  | b_prime = rHat.col(0); | 
|  | Mx = normr; | 
|  | } | 
|  | } | 
|  | if (normr < normr_min) { | 
|  | // We found an x with lower residual, keep this one. | 
|  | x_min = x + x_prime; | 
|  | normr_min = normr; | 
|  | } | 
|  |  | 
|  | compute_res = false; | 
|  | update_app = false; | 
|  | } | 
|  |  | 
|  | // Convert internal variable to the true solution vector x | 
|  | x += x_prime; | 
|  |  | 
|  | normr = (rhs - mat * (precond.solve(x) + x0)).stableNorm(); | 
|  | if (normr > normr_min || !(numext::isfinite)(normr)) { | 
|  | // x_min is a better solution than x, return x_min | 
|  | x = x_min; | 
|  | normr = normr_min; | 
|  | } | 
|  | tol_error = normr / normb; | 
|  | iters = k; | 
|  |  | 
|  | // x contains the updates to x0, add those back to obtain the solution | 
|  | x = precond.solve(x); | 
|  | x += x0; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  |  | 
|  | template <typename MatrixType_, typename Preconditioner_ = DiagonalPreconditioner<typename MatrixType_::Scalar>> | 
|  | class BiCGSTABL; | 
|  |  | 
|  | namespace internal { | 
|  |  | 
|  | template <typename MatrixType_, typename Preconditioner_> | 
|  | struct traits<Eigen::BiCGSTABL<MatrixType_, Preconditioner_>> { | 
|  | typedef MatrixType_ MatrixType; | 
|  | typedef Preconditioner_ Preconditioner; | 
|  | }; | 
|  |  | 
|  | }  // namespace internal | 
|  |  | 
|  | template <typename MatrixType_, typename Preconditioner_> | 
|  | class BiCGSTABL : public IterativeSolverBase<BiCGSTABL<MatrixType_, Preconditioner_>> { | 
|  | typedef IterativeSolverBase<BiCGSTABL> Base; | 
|  | using Base::m_error; | 
|  | using Base::m_info; | 
|  | using Base::m_isInitialized; | 
|  | using Base::m_iterations; | 
|  | using Base::matrix; | 
|  | Index m_L; | 
|  |  | 
|  | public: | 
|  | typedef MatrixType_ MatrixType; | 
|  | typedef typename MatrixType::Scalar Scalar; | 
|  | typedef typename MatrixType::RealScalar RealScalar; | 
|  | typedef Preconditioner_ Preconditioner; | 
|  |  | 
|  | /** Default constructor. */ | 
|  | BiCGSTABL() : m_L(2) {} | 
|  |  | 
|  | /** | 
|  | 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 BiCGSTABL(const EigenBase<MatrixDerived> &A) : Base(A.derived()), m_L(2) {} | 
|  |  | 
|  | /** \internal */ | 
|  | /** Loops over the number of columns of b and does the following: | 
|  | 1. sets the tolerence and maxIterations | 
|  | 2. Calls the function that has the core solver routine | 
|  | */ | 
|  | template <typename Rhs, typename Dest> | 
|  | void _solve_vector_with_guess_impl(const Rhs &b, Dest &x) const { | 
|  | m_iterations = Base::maxIterations(); | 
|  |  | 
|  | m_error = Base::m_tolerance; | 
|  |  | 
|  | bool ret = internal::bicgstabl(matrix(), b, x, Base::m_preconditioner, m_iterations, m_error, m_L); | 
|  | m_info = (!ret) ? NumericalIssue : m_error <= Base::m_tolerance ? Success : NoConvergence; | 
|  | } | 
|  |  | 
|  | /** Sets the parameter L, indicating how many minimize residual steps are | 
|  | * used. Default: 2 */ | 
|  | void setL(Index L) { | 
|  | eigen_assert(L >= 1 && "L needs to be positive"); | 
|  | m_L = L; | 
|  | } | 
|  | }; | 
|  |  | 
|  | }  // namespace Eigen | 
|  |  | 
|  | #endif /* EIGEN_BICGSTABL_H */ |