$darkmode
Eigen-unsupported  5.0.1-dev
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 
18 namespace Eigen {
19 
20 namespace internal {
35 template <typename Vector, typename RealScalar>
36 typename 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 
57 template <typename MatrixType, typename Rhs, typename Dest, typename Preconditioner>
58 bool 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 
250 template <typename MatrixType_, typename Preconditioner_ = DiagonalPreconditioner<typename MatrixType_::Scalar> >
251 class IDRS;
252 
253 namespace internal {
254 
255 template <typename MatrixType_, typename Preconditioner_>
256 struct traits<Eigen::IDRS<MatrixType_, Preconditioner_> > {
257  typedef MatrixType_ MatrixType;
258  typedef Preconditioner_ Preconditioner;
259 };
260 
261 } // namespace internal
262 
304 template <typename MatrixType_, typename Preconditioner_>
305 class 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>
339  explicit IDRS(const EigenBase<MatrixDerived>& A)
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 */
IDRS()
Definition: IDRS.h:326
IDRS(const EigenBase< MatrixDerived > &A)
Definition: IDRS.h:339
Namespace containing all symbols from the Eigen library.
The Induced Dimension Reduction method (IDR(s)) is a short-recurrences Krylov method for sparse squar...
Definition: IDRS.h:251
void setS(Index S)
Definition: IDRS.h:359
void _solve_vector_with_guess_impl(const Rhs &b, Dest &x) const
Definition: IDRS.h:348
Matrix< Type, Size, 1 > Vector
NumericalIssue
EIGEN_DEFAULT_DENSE_INDEX_TYPE Index
const Eigen::CwiseUnaryOp< Eigen::internal::scalar_abs_op< typename Derived::Scalar >, const Derived > abs(const Eigen::ArrayBase< Derived > &x)
void setResidualUpdate(bool update)
Definition: IDRS.h:390
void setSmoothing(bool smoothing)
Definition: IDRS.h:373
void setAngle(RealScalar angle)
Definition: IDRS.h:385