Eigen  5.0.1
 
Loading...
Searching...
No Matches
IDRS.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2020 Chris Schoutrop <c.e.m.schoutrop@tue.nl>
5// Copyright (C) 2020 Jens Wehner <j.wehner@esciencecenter.nl>
6// Copyright (C) 2020 Jan van Dijk <j.v.dijk@tue.nl>
7//
8// This Source Code Form is subject to the terms of the Mozilla
9// Public License v. 2.0. If a copy of the MPL was not distributed
10// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
11// SPDX-License-Identifier: MPL-2.0
12
13#ifndef EIGEN_IDRS_H
14#define EIGEN_IDRS_H
15
16// IWYU pragma: private
17#include "./InternalHeaderCheck.h"
18
19namespace Eigen {
20
21namespace internal {
36template <typename Vector, typename RealScalar>
37typename Vector::Scalar omega(const Vector& t, const Vector& s, RealScalar angle) {
38 using numext::abs;
39 using Scalar = typename Vector::Scalar;
40 const RealScalar ns = s.stableNorm();
41 const RealScalar nt = t.stableNorm();
42 const Scalar ts = t.dot(s);
43 const RealScalar rho = abs(ts / (nt * ns));
44
45 if (rho < angle) {
46 if (ts == Scalar(0)) {
47 return Scalar(0);
48 }
49 // Original relation for om is given by
50 // om = om * angle / rho;
51 // To alleviate potential (near) division by zero this can be rewritten as
52 // om = angle * (ns / nt) * (ts / abs(ts)) = angle * (ns / nt) * sgn(ts)
53 return angle * (ns / nt) * (ts / abs(ts));
54 }
55 return ts / (nt * nt);
56}
57
58template <typename MatrixType, typename Rhs, typename Dest, typename Preconditioner>
59bool idrs(const MatrixType& A, const Rhs& b, Dest& x, const Preconditioner& precond, Index& iter,
60 typename Dest::RealScalar& relres, Index S, bool smoothing, typename Dest::RealScalar angle,
61 bool replacement) {
62 using RealScalar = typename Dest::RealScalar;
63 using Scalar = typename Dest::Scalar;
64 using VectorType = Matrix<Scalar, Dynamic, 1>;
65 using DenseMatrixType = Matrix<Scalar, Dynamic, Dynamic, ColMajor>;
66 const Index N = b.size();
67 S = numext::mini(S, x.rows());
68 const RealScalar tol = relres;
69 const Index maxit = iter;
70
71 bool trueres = false;
72
73 FullPivLU<DenseMatrixType> lu_solver;
74
75 DenseMatrixType P = internal::random_orthonormal_basis<DenseMatrixType>(N, S);
76
77 const RealScalar normb = b.stableNorm();
78
79 if (normb == RealScalar(0)) {
80 // Solution is the zero vector
81 x.setZero();
82 iter = 0;
83 relres = 0;
84 return true;
85 }
86 // from http://homepage.tudelft.nl/1w5b5/IDRS/manual.pdf
87 // A peak in the residual is considered dangerously high if‖ri‖/‖b‖> C(tol/epsilon).
88 // With epsilon the relative machine precision. The factor tol/epsilon corresponds
89 // to the size of a finite precision number that is so large that the absolute
90 // round-off error in this number, when propagated through the process, makes it
91 // impossible to achieve the required accuracy. The factor C accounts for the
92 // accumulation of round-off errors. This parameter has been set to 10^{-3}.
93 // mp is epsilon/C 10^3 * eps is very conservative, so normally no residual
94 // replacements will take place. It only happens if things go very wrong. Too many
95 // restarts may ruin the convergence.
96 const RealScalar mp = RealScalar(1e3) * NumTraits<Scalar>::epsilon();
97
98 // Compute initial residual
99 const RealScalar tolb = tol * normb; // Relative tolerance
100 VectorType r = b - A * x;
101
102 VectorType x_s, r_s;
103
104 if (smoothing) {
105 x_s = x;
106 r_s = r;
107 }
108
109 RealScalar normr = r.stableNorm();
110
111 if (normr <= tolb) {
112 // Initial guess is a good enough solution
113 iter = 0;
114 relres = normr / normb;
115 return true;
116 }
117
118 DenseMatrixType G = DenseMatrixType::Zero(N, S);
119 DenseMatrixType U = DenseMatrixType::Zero(N, S);
120 DenseMatrixType M = DenseMatrixType::Identity(S, S);
121 VectorType t(N), v(N);
122 Scalar om = 1.;
123
124 // Main iteration loop, build G-spaces:
125 iter = 0;
126
127 while (normr > tolb && iter < maxit) {
128 // New right hand size for small system:
129 VectorType f = (r.adjoint() * P).adjoint();
130
131 for (Index k = 0; k < S; ++k) {
132 // Solve small system and make v orthogonal to P:
133 // c = M(k:s,k:s)\f(k:s);
134 lu_solver.compute(M.block(k, k, S - k, S - k));
135 VectorType c = lu_solver.solve(f.segment(k, S - k));
136 // v = r - G(:,k:s)*c;
137 v.noalias() = r - G.rightCols(S - k) * c;
138 // Preconditioning
139 v = precond.solve(v);
140
141 // Compute new U(:,k) and G(:,k), G(:,k) is in space G_j
142 U.col(k) = U.rightCols(S - k) * c + om * v;
143 G.col(k).noalias() = A * U.col(k);
144
145 // Bi-Orthogonalise the new basis vectors:
146 for (Index i = 0; i < k - 1; ++i) {
147 // alpha = ( P(:,i)'*G(:,k) )/M(i,i);
148 Scalar alpha = P.col(i).dot(G.col(k)) / M(i, i);
149 G.col(k) = G.col(k) - alpha * G.col(i);
150 U.col(k) = U.col(k) - alpha * U.col(i);
151 }
152
153 // New column of M = P'*G (first k-1 entries are zero)
154 // M(k:s,k) = (G(:,k)'*P(:,k:s))';
155 M.block(k, k, S - k, 1) = (G.col(k).adjoint() * P.rightCols(S - k)).adjoint();
156
157 if (M(k, k) == Scalar(0)) {
158 return false;
159 }
160
161 // Make r orthogonal to q_i, i = 0..k-1
162 Scalar beta = f(k) / M(k, k);
163 r = r - beta * G.col(k);
164 x = x + beta * U.col(k);
165 normr = r.stableNorm();
166
167 if (replacement && normr > tolb / mp) {
168 trueres = true;
169 }
170
171 // Smoothing:
172 if (smoothing) {
173 t = r_s - r;
174 // gamma is a Scalar, but the conversion is not allowed
175 Scalar gamma = t.dot(r_s) / t.stableNorm();
176 r_s = r_s - gamma * t;
177 x_s = x_s - gamma * (x_s - x);
178 normr = r_s.stableNorm();
179 }
180
181 if (normr < tolb || iter == maxit) {
182 break;
183 }
184
185 // New f = P'*r (first k components are zero)
186 if (k < S - 1) {
187 f.segment(k + 1, S - (k + 1)) = f.segment(k + 1, S - (k + 1)) - beta * M.block(k + 1, k, S - (k + 1), 1);
188 }
189 } // end for
190
191 if (normr < tolb || iter == maxit) {
192 break;
193 }
194
195 // Now we have sufficient vectors in G_j to compute residual in G_j+1
196 // Note: r is already perpendicular to P so v = r
197 // Preconditioning
198 v = r;
199 v = precond.solve(v);
200
201 // Matrix-vector multiplication:
202 t.noalias() = A * v;
203
204 // Computation of a new omega
205 om = internal::omega(t, r, angle);
206
207 if (om == RealScalar(0.0)) {
208 return false;
209 }
210
211 r = r - om * t;
212 x = x + om * v;
213 normr = r.stableNorm();
214
215 if (replacement && normr > tolb / mp) {
216 trueres = true;
217 }
218
219 // Residual replacement?
220 if (trueres && normr < normb) {
221 r.noalias() = b - A * x;
222 trueres = false;
223 }
224
225 // Smoothing:
226 if (smoothing) {
227 t = r_s - r;
228 Scalar gamma = t.dot(r_s) / t.stableNorm();
229 r_s = r_s - gamma * t;
230 x_s = x_s - gamma * (x_s - x);
231 normr = r_s.stableNorm();
232 }
233
234 iter++;
235
236 } // end while
237
238 if (smoothing) {
239 x = x_s;
240 }
241 relres = normr / normb;
242 return true;
243}
244
245} // namespace internal
246
247template <typename MatrixType_, typename Preconditioner_ = DiagonalPreconditioner<typename MatrixType_::Scalar> >
248class IDRS;
249
250namespace internal {
251
252template <typename MatrixType_, typename Preconditioner_>
253struct traits<Eigen::IDRS<MatrixType_, Preconditioner_> > {
254 using MatrixType = MatrixType_;
255 using Preconditioner = Preconditioner_;
256};
257
258} // namespace internal
259
301template <typename MatrixType_, typename Preconditioner_>
302class IDRS : public IterativeSolverBase<IDRS<MatrixType_, Preconditioner_> > {
303 public:
304 using MatrixType = MatrixType_;
305 using Scalar = typename MatrixType::Scalar;
306 using RealScalar = typename MatrixType::RealScalar;
307 using Preconditioner = Preconditioner_;
308
309 private:
310 using Base = IterativeSolverBase<IDRS>;
311 using Base::m_error;
312 using Base::m_info;
313 using Base::m_isInitialized;
314 using Base::m_iterations;
315 using Base::matrix;
316 Index m_S = 4;
317 bool m_smoothing = false;
318 RealScalar m_angle = RealScalar(0.7);
319 bool m_residual = false;
320
321 public:
323 IDRS() = default;
324
335 template <typename MatrixDerived>
336 explicit IDRS(const EigenBase<MatrixDerived>& A) : Base(A.derived()) {}
337
343 template <typename Rhs, typename Dest>
344 void _solve_vector_with_guess_impl(const Rhs& b, Dest& x) const {
345 m_iterations = Base::maxIterations();
346 m_error = Base::m_tolerance;
347
348 bool ret = internal::idrs(matrix(), b, x, Base::m_preconditioner, m_iterations, m_error, m_S, m_smoothing, m_angle,
349 m_residual);
350
351 m_info = (!ret) ? NumericalIssue : m_error <= Base::m_tolerance ? Success : NoConvergence;
352 }
353
355 void setS(Index S) {
356 if (S < 1) {
357 S = 4;
358 }
359
360 m_S = S;
361 }
362
369 void setSmoothing(bool smoothing) { m_smoothing = smoothing; }
370
381 void setAngle(RealScalar angle) { m_angle = angle; }
382
386 void setResidualUpdate(bool update) { m_residual = update; }
387};
388
389} // namespace Eigen
390
391#endif /* EIGEN_IDRS_H */
The Induced Dimension Reduction method (IDR(s)) is a short-recurrences Krylov method for sparse squar...
Definition IDRS.h:302
void setResidualUpdate(bool update)
Definition IDRS.h:386
IDRS(const EigenBase< MatrixDerived > &A)
Definition IDRS.h:336
IDRS()=default
void _solve_vector_with_guess_impl(const Rhs &b, Dest &x) const
Definition IDRS.h:344
void setSmoothing(bool smoothing)
Definition IDRS.h:369
void setS(Index S)
Definition IDRS.h:355
void setAngle(RealScalar angle)
Definition IDRS.h:381
Index maxIterations() const
Definition IterativeSolverBase.h:245
@ NumericalIssue
Definition Constants.h:459
@ Success
Definition Constants.h:457
@ NoConvergence
Definition Constants.h:461
Matrix< Type, Size, 1 > Vector
Size×1 vector of type Type.
Definition Matrix.h:532
Definition EigenBase.h:34