sym-ildl  1.2
Incomplete LDL' factorizations of indefinite symmetric and skew-symmetric matrices.
solver_minres.h
1 //-*- mode: c++ -*-
2 #ifndef _SOLVER_MINRES_H_
3 #define _SOLVER_MINRES_H_
4 
5 #include <string>
6 #include <algorithm>
7 #include <cmath>
8 
9 template<class el_type, class mat_type >
10 void solver<el_type, mat_type> :: minres(int max_iter, double stop_tol, double shift) {
11  //Zero out solution vector
12  int n = A.n_rows();
13  sol_vec.resize(n, 0);
14 
15  // ---------- set initial values and allocate memory for variables ---------//
16  el_type alpha[2], beta[2]; // the last two entries of the T matrix
17  el_type res[2]; // the last two residuals
18 
19  // the last two vectors of the lanczos iteration
20  vector< vector<el_type> > v(2, vector<el_type>(n));
21 
22  double norm_A = 0; // matrix norm estimate
23  double cond_A = 1; // condition number estimate
24  double c = -1, s = 0; // givens rotation elements
25 
26  // temporary variables to store the corner of the matrix we're factoring
27  double gamma_min = 1e99;
28  el_type delta1[2], delta2[2], ep[2], gamma1[2], gamma2[2];
29  delta1[1] = 0;
30 
31  // temporary vectors for lanczos calcluations
32  vector<el_type> pk(n), tk(n);
33 
34  // step size in the current search direction (xk = x_{k-1} + tau*dk)
35  double tau = 0;
36 
37  // the last 3 search directions
38  vector< vector<el_type> > d(2, vector<el_type>(n));
39 
40  // set up initial values for variables above
41  double eps = A.eps;
42  beta[0] = 0;
43  beta[1] = norm(rhs, 2.0);
44 
45  double norm_rhs = beta[1];
46 
47  // v[1] = rhs/beta[1]
48  for (int i = 0; i < n; i++) {
49  v[1][i] = rhs[i]/beta[1];
50  }
51 
52  res[0] = beta[1];
53  tau = beta[1];
54 
55  auto sign = [&](double x) { return (abs(x) < eps ? 0 : x/abs(x)); };
56 
57  // -------------- begin minres iterations --------------//
58  int k = 1; // iteration number
59  while (res[(k+1)%2]/norm_rhs > stop_tol && k <= max_iter) {
60  int cur = k%2, nxt = (k+1)%2;
61  // ---------- begin lanczos step ----------//
62  //pk = (M^(-1) A M^(-t) - shift*I) * v[cur], where M = L|D|^(1/2) where |D|^(1/2) = Q|V|^(1/2)
63  //we do this in steps. first, tk = L^(-t) * |D|^(-t/2) pk
64  D.sqrt_solve(v[cur], pk, true);
65  L.forwardsolve(pk, tk);
66 
67  //pk = A*tk
68  A.multiply(tk, pk);
69 
70  //pk = |D|^(-1/2) L^(-1) pk. after this step, pk = M^(-1) A M^(-t) v[cur]
71  L.backsolve(pk, tk);
72  D.sqrt_solve(tk, pk, false);
73 
74  //finally, pk = pk - shift*I * v[cur];
75  for (int i = 0; i < n; i++) {
76  pk[i] -= shift * v[cur][i];
77  }
78 
79  // alpha = v[cur]' * pk
80  alpha[cur] = dot_product(v[cur], pk);
81 
82  // pk = pk - alpha*v[cur]
83  vector_sum(1, pk, -alpha[cur], v[cur], pk);
84  // v[nxt] = pk - beta[cur]*v[nxt]
85  vector_sum(1, pk, -beta[cur], v[nxt], v[nxt]);
86  beta[nxt] = norm(v[nxt], 2.0);
87 
88  // scale v[nxt] if beta[nxt] is not zero
89  if (abs(beta[nxt]) > eps) {
90  for (int i = 0; i < n; i++) {
91  v[nxt][i] /= beta[nxt];
92  }
93  }
94  // ---------- end lanczos step ----------//
95 
96  // left orthogonlization on the middle two entries in the last column of Tk
97  delta2[cur] = c*delta1[cur] + s*alpha[cur];
98  gamma1[cur] = s*delta1[cur] - c*alpha[cur];
99 
100  // left orthogonalization to product first two entries of T_{k+1} and ep_{k+1}
101  ep[nxt] = s*beta[nxt];
102  delta1[nxt] = -c*beta[nxt];
103 
104  // ---------- begin givens rotation ----------//
105  double a = gamma1[cur], b = beta[nxt];
106  if (abs(b) < eps) {
107  s = 0;
108  gamma2[cur] = abs(a);
109  if (abs(a) < eps) {
110  c = 1;
111  } else {
112  c = sign(a);
113  }
114  } else if (abs(a) < eps) {
115  c = 0;
116  s = sign(b);
117  gamma2[cur] = abs(b);
118  } else if (abs(b) > abs(a)) {
119  double t = a/b;
120  s = sign(b)/sqrt(1+t*t);
121  c = s*t;
122  gamma2[cur] = b/s;
123  } else { //abs(a) >= abs(b)
124  double t = b/a;
125  c = sign(a)/sqrt(1+t*t);
126  s = c*t;
127  gamma2[cur] = a/c;
128  }
129  // ---------- end givens rotation ----------//
130 
131  // update residual norms and estimate for matrix norm
132  tau = c*res[nxt];
133  res[cur] = s*res[nxt];
134 
135  if (k == 1) norm_A = sqrt(alpha[cur]*alpha[cur] + beta[nxt]*beta[nxt]);
136  else {
137  double tnorm = sqrt(alpha[cur]*alpha[cur] + beta[nxt]*beta[nxt] + beta[cur]*beta[cur]);
138  norm_A = std::max(norm_A, tnorm);
139  }
140 
141  // ------ update solution and matrix condition number ------ //
142  if (abs(gamma2[cur]) > eps) {
143  // compute new search direction
144  // d[cur] = (v[cur] - delta2[cur]*d[nxt] - ep[cur]*d[cur])/gamma2[cur];
145  vector_sum(1, v[cur], -ep[cur], d[cur], d[cur]);
146  vector_sum(1, d[cur], -delta2[cur], d[nxt], d[cur]);
147 
148  for (int i = 0; i < n; i++) {
149  d[cur][i] /= gamma2[cur];
150  }
151 
152  //sol = sol + tau*d[cur]
153  vector_sum(1, sol_vec, tau, d[cur], sol_vec);
154  gamma_min = std::min(gamma_min, gamma2[cur]);
155  cond_A = norm_A/gamma_min;
156  }
157 
158  k++;
159 
160  // ------------- end update ------------- //
161  //cout << "current residual " << res[cur]/norm_rhs << endl;
162  }
163 
164  if (msg_lvl) printf("The estimated condition number of the matrix is %e.\n", cond_A);
165 
166  std::string iter_str = "iterations";
167  if (k-1 == 1) iter_str = "iteration";
168 
169  if (msg_lvl) printf("MINRES took %i %s and got down to relative residual %e.\n", k-1, iter_str.c_str(), res[(k+1)%2]/norm_rhs);
170  return;
171 }
172 
173 #endif // _SOLVER_MINRES_H_