IDRS.h
Go to the documentation of this file.
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  bool trueres = false;
70 
72 
73  DenseMatrixType P;
74  {
75  HouseholderQR<DenseMatrixType> qr(DenseMatrixType::Random(N, S));
76  P = (qr.householderQ() * DenseMatrixType::Identity(N, S));
77  }
78 
79  const RealScalar normb = b.stableNorm();
80 
81  if (internal::isApprox(normb, RealScalar(0))) {
82  // Solution is the zero vector
83  x.setZero();
84  iter = 0;
85  relres = 0;
86  return true;
87  }
88  // from http://homepage.tudelft.nl/1w5b5/IDRS/manual.pdf
89  // A peak in the residual is considered dangerously high if‖ri‖/‖b‖> C(tol/epsilon).
90  // With epsilon the relative machine precision. The factor tol/epsilon corresponds
91  // to the size of a finite precision number that is so large that the absolute
92  // round-off error in this number, when propagated through the process, makes it
93  // impossible to achieve the required accuracy. The factor C accounts for the
94  // accumulation of round-off errors. This parameter has been set to 10^{-3}.
95  // mp is epsilon/C 10^3 * eps is very conservative, so normally no residual
96  // replacements will take place. It only happens if things go very wrong. Too many
97  // restarts may ruin the convergence.
98  const RealScalar mp = RealScalar(1e3) * NumTraits<Scalar>::epsilon();
99 
100  // Compute initial residual
101  const RealScalar tolb = tol * normb; // Relative tolerance
102  VectorType r = b - A * x;
103 
104  VectorType x_s, r_s;
105 
106  if (smoothing) {
107  x_s = x;
108  r_s = r;
109  }
110 
111  RealScalar normr = r.stableNorm();
112 
113  if (normr <= tolb) {
114  // Initial guess is a good enough solution
115  iter = 0;
116  relres = normr / normb;
117  return true;
118  }
119 
120  DenseMatrixType G = DenseMatrixType::Zero(N, S);
121  DenseMatrixType U = DenseMatrixType::Zero(N, S);
122  DenseMatrixType M = DenseMatrixType::Identity(S, S);
123  VectorType t(N), v(N);
124  Scalar om = 1.;
125 
126  // Main iteration loop, guild G-spaces:
127  iter = 0;
128 
129  while (normr > tolb && iter < maxit) {
130  // New right hand size for small system:
131  VectorType f = (r.adjoint() * P).adjoint();
132 
133  for (Index k = 0; k < S; ++k) {
134  // Solve small system and make v orthogonal to P:
135  // c = M(k:s,k:s)\f(k:s);
136  lu_solver.compute(M.block(k, k, S - k, S - k));
137  VectorType c = lu_solver.solve(f.segment(k, S - k));
138  // v = r - G(:,k:s)*c;
139  v = r - G.rightCols(S - k) * c;
140  // Preconditioning
141  v = precond.solve(v);
142 
143  // Compute new U(:,k) and G(:,k), G(:,k) is in space G_j
144  U.col(k) = U.rightCols(S - k) * c + om * v;
145  G.col(k) = A * U.col(k);
146 
147  // Bi-Orthogonalise the new basis vectors:
148  for (Index i = 0; i < k - 1; ++i) {
149  // alpha = ( P(:,i)'*G(:,k) )/M(i,i);
150  Scalar alpha = P.col(i).dot(G.col(k)) / M(i, i);
151  G.col(k) = G.col(k) - alpha * G.col(i);
152  U.col(k) = U.col(k) - alpha * U.col(i);
153  }
154 
155  // New column of M = P'*G (first k-1 entries are zero)
156  // M(k:s,k) = (G(:,k)'*P(:,k:s))';
157  M.block(k, k, S - k, 1) = (G.col(k).adjoint() * P.rightCols(S - k)).adjoint();
158 
159  if (internal::isApprox(M(k, k), Scalar(0))) {
160  return false;
161  }
162 
163  // Make r orthogonal to q_i, i = 0..k-1
164  Scalar beta = f(k) / M(k, k);
165  r = r - beta * G.col(k);
166  x = x + beta * U.col(k);
167  normr = r.stableNorm();
168 
169  if (replacement && normr > tolb / mp) {
170  trueres = true;
171  }
172 
173  // Smoothing:
174  if (smoothing) {
175  t = r_s - r;
176  // gamma is a Scalar, but the conversion is not allowed
177  Scalar gamma = t.dot(r_s) / t.stableNorm();
178  r_s = r_s - gamma * t;
179  x_s = x_s - gamma * (x_s - x);
180  normr = r_s.stableNorm();
181  }
182 
183  if (normr < tolb || iter == maxit) {
184  break;
185  }
186 
187  // New f = P'*r (first k components are zero)
188  if (k < S - 1) {
189  f.segment(k + 1, S - (k + 1)) = f.segment(k + 1, S - (k + 1)) - beta * M.block(k + 1, k, S - (k + 1), 1);
190  }
191  } // end for
192 
193  if (normr < tolb || iter == maxit) {
194  break;
195  }
196 
197  // Now we have sufficient vectors in G_j to compute residual in G_j+1
198  // Note: r is already perpendicular to P so v = r
199  // Preconditioning
200  v = r;
201  v = precond.solve(v);
202 
203  // Matrix-vector multiplication:
204  t = A * v;
205 
206  // Computation of a new omega
207  om = internal::omega(t, r, angle);
208 
209  if (om == RealScalar(0.0)) {
210  return false;
211  }
212 
213  r = r - om * t;
214  x = x + om * v;
215  normr = r.stableNorm();
216 
217  if (replacement && normr > tolb / mp) {
218  trueres = true;
219  }
220 
221  // Residual replacement?
222  if (trueres && normr < normb) {
223  r = b - A * x;
224  trueres = false;
225  }
226 
227  // Smoothing:
228  if (smoothing) {
229  t = r_s - r;
230  Scalar gamma = t.dot(r_s) / t.stableNorm();
231  r_s = r_s - gamma * t;
232  x_s = x_s - gamma * (x_s - x);
233  normr = r_s.stableNorm();
234  }
235 
236  iter++;
237 
238  } // end while
239 
240  if (smoothing) {
241  x = x_s;
242  }
243  relres = normr / normb;
244  return true;
245 }
246 
247 } // namespace internal
248 
249 template <typename MatrixType_, typename Preconditioner_ = DiagonalPreconditioner<typename MatrixType_::Scalar> >
250 class IDRS;
251 
252 namespace internal {
253 
254 template <typename MatrixType_, typename Preconditioner_>
255 struct traits<Eigen::IDRS<MatrixType_, Preconditioner_> > {
256  typedef MatrixType_ MatrixType;
257  typedef Preconditioner_ Preconditioner;
258 };
259 
260 } // namespace internal
261 
303 template <typename MatrixType_, typename Preconditioner_>
304 class IDRS : public IterativeSolverBase<IDRS<MatrixType_, Preconditioner_> > {
305  public:
306  typedef MatrixType_ MatrixType;
307  typedef typename MatrixType::Scalar Scalar;
308  typedef typename MatrixType::RealScalar RealScalar;
309  typedef Preconditioner_ Preconditioner;
310 
311  private:
313  using Base::m_error;
314  using Base::m_info;
315  using Base::m_isInitialized;
316  using Base::m_iterations;
317  using Base::matrix;
322 
323  public:
325  IDRS() : m_S(4), m_smoothing(false), m_angle(RealScalar(0.7)), m_residual(false) {}
326 
337  template <typename MatrixDerived>
338  explicit IDRS(const EigenBase<MatrixDerived>& A)
339  : Base(A.derived()), m_S(4), m_smoothing(false), m_angle(RealScalar(0.7)), m_residual(false) {}
340 
346  template <typename Rhs, typename Dest>
347  void _solve_vector_with_guess_impl(const Rhs& b, Dest& x) const {
350 
352  m_residual);
353 
355  }
356 
358  void setS(Index S) {
359  if (S < 1) {
360  S = 4;
361  }
362 
363  m_S = S;
364  }
365 
372  void setSmoothing(bool smoothing) { m_smoothing = smoothing; }
373 
384  void setAngle(RealScalar angle) { m_angle = angle; }
385 
389  void setResidualUpdate(bool update) { m_residual = update; }
390 };
391 
392 } // namespace Eigen
393 
394 #endif /* EIGEN_IDRS_H */
Array< int, Dynamic, 1 > v
SparseMatrix< double > A(n, n)
int i
Array33i c
Matrix4Xd M
Projective3d P(Matrix4d::Random())
HouseholderQR< MatrixXf > qr(A)
JacobiRotation< float > G
Matrix< float, 1, Dynamic > MatrixType
FullPivLU & compute(const EigenBase< InputType > &matrix)
const Solve< FullPivLU, Rhs > solve(const MatrixBase< Rhs > &b) const
The Induced Dimension Reduction method (IDR(s)) is a short-recurrences Krylov method for sparse squar...
Definition: IDRS.h:304
void setResidualUpdate(bool update)
Definition: IDRS.h:389
IDRS()
Definition: IDRS.h:325
RealScalar m_error
IDRS(const EigenBase< MatrixDerived > &A)
Definition: IDRS.h:338
const ActualMatrixType & matrix() const
MatrixType::Scalar Scalar
Definition: IDRS.h:307
ComputationInfo m_info
Preconditioner_ Preconditioner
Definition: IDRS.h:309
IterativeSolverBase< IDRS > Base
Definition: IDRS.h:312
Index m_iterations
bool m_residual
Definition: IDRS.h:321
void _solve_vector_with_guess_impl(const Rhs &b, Dest &x) const
Definition: IDRS.h:347
MatrixType_ MatrixType
Definition: IDRS.h:306
void setSmoothing(bool smoothing)
Definition: IDRS.h:372
MatrixType::RealScalar RealScalar
Definition: IDRS.h:308
void setS(Index S)
Definition: IDRS.h:358
bool m_smoothing
Definition: IDRS.h:319
void setAngle(RealScalar angle)
Definition: IDRS.h:384
Index m_S
Definition: IDRS.h:318
RealScalar m_angle
Definition: IDRS.h:320
Preconditioner m_preconditioner
const ActualMatrixType & matrix() const
internal::traits< Derived >::Scalar Scalar
NumericalIssue
bool idrs(const MatrixType &A, const Rhs &b, Dest &x, const Preconditioner &precond, Index &iter, typename Dest::RealScalar &relres, Index S, bool smoothing, typename Dest::RealScalar angle, bool replacement)
Definition: IDRS.h:57
bool isApprox(const Scalar &x, const Scalar &y, const typename NumTraits< Scalar >::Real &precision=NumTraits< Scalar >::dummy_precision())
Vector::Scalar omega(const Vector &t, const Vector &s, RealScalar angle)
Definition: IDRS.h:35
EIGEN_ALWAYS_INLINE std::enable_if_t< NumTraits< T >::IsSigned||NumTraits< T >::IsComplex, typename NumTraits< T >::Real > abs(const T &x)
: TensorContractionSycl.h, provides various tensor contraction kernel for SYCL backend
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)