NuTo
Numerics Tool
Gmres.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <iostream>
4 
5 
6 #include <Eigen/Sparse>
7 #include <Eigen/Core>
8 #include <Eigen/QR>
9 
10 
11 namespace NuTo
12 {
13 
15 template <class T, class Preconditioner = Eigen::DiagonalPreconditioner<double>>
16 int Gmres(const T& A, const Eigen::VectorXd& rhs, Eigen::VectorXd& x, const int maxNumRestarts, const double tolerance,
17  const int krylovDimension)
18 {
19 
20  using MatrixType = Eigen::MatrixXd;
21  using VectorType = Eigen::VectorXd;
22 
23  const int n = A.rows();
24 
25  // Initialize upper Hessenberg matrix
26  MatrixType H = MatrixType::Zero(krylovDimension + 1, krylovDimension);
27 
28  // Initialize Krylov subspace
29  MatrixType V = MatrixType::Zero(n, krylovDimension + 1);
30 
31  // container for Givens rotation matrices
32  std::vector<Eigen::JacobiRotation<double>> Givens(krylovDimension + 1);
33 
34  VectorType e1 = VectorType::Zero(n);
35  e1[0] = 1.0;
36 
37  // Initialize preconditioner
38  Preconditioner precond(A);
39 
40  // preconditioned residual
41  VectorType r = precond.solve(rhs - A * x);
42  double rNorm = r.norm();
43  double rhsNorm = rhs.norm();
44 
45  if (rhsNorm < 1.e-5)
46  rhsNorm = 1.0;
47 
48  int numRestarts = 0;
49 
50 
51  while (rNorm > tolerance * rhsNorm and numRestarts < maxNumRestarts)
52  {
53 
54  // the first vector in the Krylov subspace is the normalized residual
55  V.col(0) = r / rNorm;
56 
57  // initialize the s vector used to estimate the residual
58  VectorType s = rNorm * e1;
59 
60  // construct orthonormal basis using Gram-Schmidt
61  for (int i = 0; i < krylovDimension; ++i)
62  {
63  VectorType w = precond.solve(A * V.col(i));
64 
65  for (int iRow = 0; iRow < i + 1; ++iRow)
66  {
67  H(iRow, i) = w.transpose() * V.col(iRow);
68  w = w - H(iRow, i) * V.col(iRow);
69  }
70 
71  H(i + 1, i) = w.norm();
72  V.col(i + 1) = w / H(i + 1, i);
73 
74  // Apply the Givens Rotations to ensure that H is an upper triangular matrix.
75  // First apply previous rotations to the current matrix
76  for (int iRow = 0; iRow < i; ++iRow)
77  {
78  H.col(i).applyOnTheLeft(iRow, iRow + 1, Givens[iRow].adjoint());
79  }
80 
81  // form the i-th rotation matrix
82  Givens[i].makeGivens(H(i, i), H(i + 1, i));
83 
84  // Apply the new Givens rotation on the
85  // new entry in the uppper Hessenberg matrix
86  H.col(i).applyOnTheLeft(i, i + 1, Givens[i].adjoint());
87 
88  // Finally apply the new Givens rotation on the s vector
89  s.applyOnTheLeft(i, i + 1, Givens[i].adjoint());
90 
91  rNorm = std::abs(s[i + 1]);
92 
93  if (rNorm < tolerance * rhsNorm)
94  {
95  VectorType y = s.head(i + 1);
96  H.topLeftCorner(i + 1, i + 1).triangularView<Eigen::Upper>().solveInPlace(y);
97  x = x + V.block(0, 0, n, i + 1) * y;
98  return numRestarts;
99  }
100  }
101 
102 
103  // we have exceeded the number of iterations. Update the approximation and start over
104  VectorType y = s.head(krylovDimension);
105  H.topLeftCorner(krylovDimension, krylovDimension).triangularView<Eigen::Upper>().solveInPlace(y);
106  x = x + V.block(0, 0, n, krylovDimension) * y;
107 
108  // compute preconditioned residual
109  r = precond.solve(rhs - A * x);
110  rNorm = r.norm();
111 
112  ++numRestarts;
113  }
114 
115  return numRestarts;
116 }
117 
118 } // namespace NuTo
constexpr double tolerance
Definition: NewtonRaphsonBenchmark.cpp:11
s
Definition: WaveFieldSynthesis.py:99
Definition: Exception.h:6
rhs
Definition: SparseDirectSolverMKLDSS.py:46
int Gmres(const T &A, const Eigen::VectorXd &rhs, Eigen::VectorXd &x, const int maxNumRestarts, const double tolerance, const int krylovDimension)
Generalized minimal residual method.
Definition: Gmres.h:16
const Eigen::MatrixBase< OtherDerived > abs() const
elementwise absolute value of the matrix
Definition: MatrixBaseAddons.h:17
int A
Definition: TimeIntegrationResultForce.py:7