nmpc_cgmres
Gmres.h
Go to the documentation of this file.
1 /* Author: Masaki Murooka */
2 
3 #pragma once
4 
5 #include <functional>
6 #include <iostream>
7 #include <memory>
8 #include <vector>
9 
10 #include <Eigen/Core>
11 #include <Eigen/Dense>
12 
13 namespace nmpc_cgmres
14 {
21 class Gmres
22 {
23 public:
25  using AmulFunc = std::function<Eigen::VectorXd(const Eigen::Ref<const Eigen::VectorXd> &)>;
26 
27 public:
28  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
29 
31  Gmres() {}
32 
42  inline void solve(const Eigen::Ref<const Eigen::MatrixXd> & A,
43  const Eigen::Ref<const Eigen::VectorXd> & b,
44  Eigen::Ref<Eigen::VectorXd> x,
45  int k_max = 100,
46  double eps = 1e-10)
47  {
48  assert(A.rows() == b.size());
49  AmulFunc Amul_func = [&](const Eigen::Ref<const Eigen::VectorXd> & vec) { return A * vec; };
50  solve(Amul_func, b, x, k_max, eps);
51  }
52 
67  inline void solve(const AmulFunc & Amul_func,
68  const Eigen::Ref<const Eigen::VectorXd> & b,
69  Eigen::Ref<Eigen::VectorXd> x,
70  int k_max = 100,
71  double eps = 1e-10)
72  {
73  k_max = std::min(k_max, static_cast<int>(x.size()));
74 
75  err_list_.clear();
76  basis_.clear();
77 
78  // 1.
79  Eigen::VectorXd r = b - Amul_func(x);
80  basis_.push_back(r.normalized());
81  double rho = r.norm();
82  int k = 0;
83  g_.setZero(k_max + 1);
84  g_(0) = rho;
85 
86  double b_norm = b.norm();
87  H_.setZero(k_max + 1, k_max);
88  err_list_.push_back(rho);
89 
90  // 2.
91  Eigen::VectorXd y_k;
92  std::vector<double> c_list;
93  std::vector<double> s_list;
94  while(rho > eps * b_norm && k < k_max)
95  {
96  // (a).
97  // note that k is 1 in the first iteration
98  k++;
99 
100  // (b).
101  // new_basis corresponds to $v_{k+1}$ in the paper
102  Eigen::VectorXd Avk = Amul_func(basis_.back());
103  Eigen::VectorXd new_basis = Avk;
104  for(int j = 0; j < k; j++)
105  {
106  // i.
107  H_(j, k - 1) = new_basis.dot(basis_[j]);
108  // ii.
109  new_basis = new_basis - H_(j, k - 1) * basis_[j];
110  }
111 
112  // (c).
113  double new_basis_norm = new_basis.norm();
114  H_(k, k - 1) = new_basis_norm;
115 
116  // (d).
117  if(apply_reorth_)
118  {
119  double Avk_norm = Avk.norm();
120  if(Avk_norm + 1e-3 * new_basis_norm == Avk_norm)
121  {
122  // std::cout << "apply reorthogonalization. (loop: " << k << ")" << std::endl;
123  for(int j = 0; j < k; j++)
124  {
125  double h_tmp = new_basis.dot(basis_[j]);
126  H_(j, k - 1) += h_tmp;
127  new_basis -= h_tmp * basis_[j];
128  }
129  }
130  }
131 
132  // (e).
133  basis_.push_back(new_basis.normalized());
134 
135  if(make_triangular_)
136  {
137  // (f).
138  // i.
139  for(int i = 0; i < k - 1; i++)
140  {
141  double h0 = H_(i, k - 1);
142  double h1 = H_(i + 1, k - 1);
143  double c = c_list[i];
144  double s = s_list[i];
145  H_(i, k - 1) = c * h0 - s * h1;
146  H_(i + 1, k - 1) = s * h0 + c * h1;
147  }
148 
149  // ii.
150  double nu = std::sqrt(std::pow(H_(k - 1, k - 1), 2) + std::pow(H_(k, k - 1), 2));
151 
152  // iii.
153  double c_k = H_(k - 1, k - 1) / nu;
154  double s_k = -H_(k, k - 1) / nu;
155  c_list.push_back(c_k);
156  s_list.push_back(s_k);
157  H_(k - 1, k - 1) = c_k * H_(k - 1, k - 1) - s_k * H_(k, k - 1);
158  H_(k, k - 1) = 0;
159 
160  // iv.
161  double g0 = g_(k - 1);
162  double g1 = g_(k);
163  g_(k - 1) = c_k * g0 - s_k * g1;
164  g_(k) = s_k * g0 + c_k * g1;
165 
166  // (g).
167  rho = std::abs(g_(k));
168  }
169  else
170  {
171  // (f).
172  y_k = H_.topLeftCorner(k + 1, k).householderQr().solve(g_.head(k + 1));
173 
174  // (g).
175  rho = (g_.head(k + 1) - H_.topLeftCorner(k + 1, k) * y_k).norm();
176  }
177 
178  err_list_.push_back(rho);
179  }
180 
181  if(make_triangular_)
182  {
183  // 3.
184  y_k = H_.topLeftCorner(k, k).triangularView<Eigen::Upper>().solve(g_.head(k));
185  }
186 
187  // 4.
188  for(int i = 0; i < k; i++)
189  {
190  x += y_k(i) * basis_[i];
191  }
192  }
193 
194 public:
195  bool make_triangular_ = true;
196  bool apply_reorth_ = true;
197 
198  Eigen::MatrixXd H_;
199  Eigen::VectorXd g_;
200 
201  std::vector<double> err_list_;
202 
203  std::vector<Eigen::VectorXd> basis_;
204 };
205 } // namespace nmpc_cgmres
nmpc_cgmres
Definition: CgmresProblem.h:11
nmpc_cgmres::Gmres::solve
void solve(const Eigen::Ref< const Eigen::MatrixXd > &A, const Eigen::Ref< const Eigen::VectorXd > &b, Eigen::Ref< Eigen::VectorXd > x, int k_max=100, double eps=1e-10)
Solve.
Definition: Gmres.h:42
nmpc_cgmres::Gmres::AmulFunc
std::function< Eigen::VectorXd(const Eigen::Ref< const Eigen::VectorXd > &)> AmulFunc
Type of function that returns x multiplied by A.
Definition: Gmres.h:25
nmpc_cgmres::Gmres::basis_
std::vector< Eigen::VectorXd > basis_
Definition: Gmres.h:203
nmpc_cgmres::Gmres::g_
Eigen::VectorXd g_
Definition: Gmres.h:199
nmpc_cgmres::Gmres::make_triangular_
bool make_triangular_
Definition: Gmres.h:195
nmpc_cgmres::Gmres
GMRES method to solve a linear equation.
Definition: Gmres.h:21
nmpc_cgmres::Gmres::solve
void solve(const AmulFunc &Amul_func, const Eigen::Ref< const Eigen::VectorXd > &b, Eigen::Ref< Eigen::VectorXd > x, int k_max=100, double eps=1e-10)
Solve.
Definition: Gmres.h:67
nmpc_cgmres::Gmres::err_list_
std::vector< double > err_list_
Definition: Gmres.h:201
nmpc_cgmres::Gmres::H_
Eigen::MatrixXd H_
Definition: Gmres.h:198
nmpc_cgmres::Gmres::apply_reorth_
bool apply_reorth_
Definition: Gmres.h:196
nmpc_cgmres::Gmres::Gmres
EIGEN_MAKE_ALIGNED_OPERATOR_NEW Gmres()
Constructor.
Definition: Gmres.h:31