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