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