Eigen  5.0.1
 
Loading...
Searching...
No Matches
GMRES.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2011 Gael Guennebaud <gael.guennebaud@inria.fr>
5// Copyright (C) 2012, 2014 Kolja Brix <brix@igpm.rwth-aaachen.de>
6//
7// This Source Code Form is subject to the terms of the Mozilla
8// Public License v. 2.0. If a copy of the MPL was not distributed
9// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10// SPDX-License-Identifier: MPL-2.0
11
12#ifndef EIGEN_GMRES_H
13#define EIGEN_GMRES_H
14
15// IWYU pragma: private
16#include "./InternalHeaderCheck.h"
17
18namespace Eigen {
19
20namespace internal {
21
60template <typename MatrixType, typename Rhs, typename Dest, typename Preconditioner>
61bool gmres(const MatrixType& mat, const Rhs& rhs, Dest& x, const Preconditioner& precond, Index& iters,
62 const Index& restart_, typename Dest::RealScalar& tol_error) {
63 using std::abs;
64 using std::sqrt;
65
66 using RealScalar = typename Dest::RealScalar;
67 using Scalar = typename Dest::Scalar;
68 using VectorType = Matrix<Scalar, Dynamic, 1>;
69 using FMatrixType = Matrix<Scalar, Dynamic, Dynamic, ColMajor>;
70
71 const RealScalar considerAsZero = (std::numeric_limits<RealScalar>::min)();
72
73 if (rhs.norm() <= considerAsZero) {
74 x.setZero();
75 tol_error = 0;
76 return true;
77 }
78
79 RealScalar tol = tol_error;
80 const Index maxIters = iters;
81 iters = 0;
82
83 const Index m = mat.rows();
84 // A GMRES cycle cannot use more Krylov vectors than the problem dimension, so cap the internal workspace even if
85 // the user requests a larger restart value.
86 const Index restart = numext::mini(numext::maxi(restart_, Index(1)), m);
87
88 // residual and preconditioned residual
89 VectorType p0 = rhs - mat * x;
90 VectorType r0 = precond.solve(p0);
91
92 const RealScalar r0Norm = r0.norm();
93 // w(k) tracks the residual norm of the left-preconditioned system. Normalize it by the matching right-hand
94 // side, not by the initial residual, so solveWithGuess() uses the same relative-residual criterion as solve().
95 const RealScalar rhsNorm = numext::maxi((x.squaredNorm() == 0) ? r0Norm : precond.solve(rhs).norm(), considerAsZero);
96 const RealScalar threshold = numext::maxi(tol * rhsNorm, considerAsZero);
97
98 // is initial guess already good enough?
99 if (r0Norm < threshold) {
100 tol_error = r0Norm / rhsNorm;
101 return true;
102 }
103
104 // storage for Hessenberg matrix and Householder data
105 FMatrixType H = FMatrixType::Zero(m, restart + 1);
106 VectorType w = VectorType::Zero(restart + 1);
107 VectorType tau = VectorType::Zero(restart + 1);
108
109 // storage for Jacobi rotations
110 std::vector<JacobiRotation<Scalar> > G(restart);
111
112 // storage for temporaries
113 VectorType t(m), v(m), workspace(m), x_new(m);
114
115 // generate first Householder vector
116 Ref<VectorType> H0_tail = H.col(0).tail(m - 1);
117 RealScalar beta;
118 r0.makeHouseholder(H0_tail, tau.coeffRef(0), beta);
119 w(0) = Scalar(beta);
120
121 for (Index k = 1; k <= restart; ++k) {
122 ++iters;
123
124 v = VectorType::Unit(m, k - 1);
125
126 // apply Householder reflections H_{1} ... H_{k-1} to v
127 // TODO: use a HouseholderSequence
128 for (Index i = k - 1; i >= 0; --i) {
129 v.tail(m - i).applyHouseholderOnTheLeft(H.col(i).tail(m - i - 1), tau.coeffRef(i), workspace.data());
130 }
131
132 // apply matrix M to v: v = mat * v;
133 t.noalias() = mat * v;
134 v = precond.solve(t);
135
136 // apply Householder reflections H_{k-1} ... H_{1} to v
137 // TODO: use a HouseholderSequence
138 for (Index i = 0; i < k; ++i) {
139 v.tail(m - i).applyHouseholderOnTheLeft(H.col(i).tail(m - i - 1), tau.coeffRef(i), workspace.data());
140 }
141
142 if (v.tail(m - k).norm() != 0.0 && k <= restart) {
143 // generate new Householder vector
144 Ref<VectorType> Hk_tail = H.col(k).tail(m - k - 1);
145 v.tail(m - k).makeHouseholder(Hk_tail, tau.coeffRef(k), beta);
146
147 // apply Householder reflection H_{k} to v
148 v.tail(m - k).applyHouseholderOnTheLeft(Hk_tail, tau.coeffRef(k), workspace.data());
149 }
150
151 // apply old Givens rotations to v
152 for (Index i = 0; i < k - 1; ++i) {
153 v.applyOnTheLeft(i, i + 1, G[i].adjoint());
154 }
155
156 if (k < m && v(k) != (Scalar)0) {
157 // determine next Givens rotation
158 G[k - 1].makeGivens(v(k - 1), v(k));
159
160 // apply Givens rotation to v and w
161 v.applyOnTheLeft(k - 1, k, G[k - 1].adjoint());
162 w.applyOnTheLeft(k - 1, k, G[k - 1].adjoint());
163 }
164
165 // insert coefficients into upper matrix triangle
166 H.col(k - 1).head(k) = v.head(k);
167
168 tol_error = abs(w(k)) / rhsNorm;
169 bool stop = (k == m || tol_error < tol || iters == maxIters);
170
171 if (stop || k == restart) {
172 // solve upper triangular system
173 Ref<VectorType> y = w.head(k);
174 H.topLeftCorner(k, k).template triangularView<Upper>().solveInPlace(y);
175
176 // use Horner-like scheme to calculate solution vector
177 x_new.setZero();
178 for (Index i = k - 1; i >= 0; --i) {
179 x_new(i) += y(i);
180 // apply Householder reflection H_{i} to x_new
181 x_new.tail(m - i).applyHouseholderOnTheLeft(H.col(i).tail(m - i - 1), tau.coeffRef(i), workspace.data());
182 }
183
184 x += x_new;
185
186 if (stop) {
187 return true;
188 } else {
189 k = 0;
190
191 // reset data for restart
192 p0.noalias() = rhs - mat * x;
193 r0 = precond.solve(p0);
194
195 // clear Hessenberg matrix and Householder data
196 H.setZero();
197 w.setZero();
198 tau.setZero();
199
200 // generate first Householder vector
201 r0.makeHouseholder(H0_tail, tau.coeffRef(0), beta);
202 w(0) = Scalar(beta);
203 }
204 }
205 }
206
207 return false;
208}
209
210} // namespace internal
211
212template <typename MatrixType_, typename Preconditioner_ = DiagonalPreconditioner<typename MatrixType_::Scalar> >
213class GMRES;
214
215namespace internal {
216
217template <typename MatrixType_, typename Preconditioner_>
218struct traits<GMRES<MatrixType_, Preconditioner_> > {
219 using MatrixType = MatrixType_;
220 using Preconditioner = Preconditioner_;
221};
222
223} // namespace internal
224
263template <typename MatrixType_, typename Preconditioner_>
264class GMRES : public IterativeSolverBase<GMRES<MatrixType_, Preconditioner_> > {
265 protected:
266 using Base = IterativeSolverBase<GMRES>;
267 using Base::m_error;
268 using Base::m_info;
269 using Base::m_isInitialized;
270 using Base::m_iterations;
271 using Base::matrix;
272
273 private:
274 Index m_restart = 30;
275
276 public:
277 using Base::_solve_impl;
278 using MatrixType = MatrixType_;
279 using Scalar = typename MatrixType::Scalar;
280 using RealScalar = typename MatrixType::RealScalar;
281 using Preconditioner = Preconditioner_;
282
283 public:
285 GMRES() : Base(), m_restart(30) {}
286
297 template <typename MatrixDerived>
298 explicit GMRES(const EigenBase<MatrixDerived>& A) : Base(A.derived()), m_restart(30) {}
299
302 Index get_restart() const { return m_restart; }
303
307 void set_restart(const Index restart) { m_restart = restart; }
308
310 template <typename Rhs, typename Dest>
311 void _solve_vector_with_guess_impl(const Rhs& b, Dest& x) const {
312 m_iterations = Base::maxIterations();
313 m_error = Base::m_tolerance;
314 bool ret = internal::gmres(matrix(), b, x, Base::m_preconditioner, m_iterations, m_restart, m_error);
315 m_info = (!ret) ? NumericalIssue : m_error <= Base::m_tolerance ? Success : NoConvergence;
316 }
317
318 protected:
319};
320
321} // end namespace Eigen
322
323#endif // EIGEN_GMRES_H
A GMRES solver for sparse square problems.
Definition GMRES.h:264
GMRES(const EigenBase< MatrixDerived > &A)
Definition GMRES.h:298
Index get_restart() const
Definition GMRES.h:302
GMRES()
Definition GMRES.h:285
void set_restart(const Index restart)
Definition GMRES.h:307
Index maxIterations() const
Definition IterativeSolverBase.h:245
@ NumericalIssue
Definition Constants.h:459
@ Success
Definition Constants.h:457
@ NoConvergence
Definition Constants.h:461
Definition EigenBase.h:34