ARPACK-Armadillo
GenEigsSolver_Impl.h
1 // Copyright (C) 2015 Yixuan Qiu
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 // Arnoldi factorization starting from step-k
8 template < typename Scalar,
9  int SelectionRule,
10  typename OpType >
11 inline void GenEigsSolver<Scalar, SelectionRule, OpType>::factorize_from(int from_k, int to_m, const Vector &fk)
12 {
13  if(to_m <= from_k) return;
14 
15  fac_f = fk;
16 
17  Vector w(dim_n);
18  Scalar beta = 0.0;
19  // Keep the upperleft k x k submatrix of H and set other elements to 0
20  fac_H.tail_cols(ncv - from_k).zeros();
21  fac_H.submat(arma::span(from_k, ncv - 1), arma::span(0, from_k - 1)).zeros();
22  for(int i = from_k; i <= to_m - 1; i++)
23  {
24  beta = std::sqrt(arma::dot(fac_f, fac_f));
25  fac_V.col(i) = fac_f / beta; // The (i+1)-th column
26  fac_H(i, i - 1) = beta;
27 
28  // w = A * v, v = fac_V.col(i)
29  op->perform_op(fac_V.colptr(i), w.memptr());
30  nmatop++;
31 
32  // First i+1 columns of V
33  Matrix Vs(fac_V.memptr(), dim_n, i + 1, false);
34  // h = fac_H(0:i, i)
35  Vector h(fac_H.colptr(i), i + 1, false);
36  h = Vs.t() * w;
37 
38  fac_f = w - Vs * h;
39  // Correct f if it is not orthogonal to V
40  // Typically the largest absolute value occurs in
41  // the first element, i.e., <v1, f>, so we use this
42  // to test the orthogonality
43  Scalar v1f = arma::dot(fac_f, fac_V.col(0));
44  if(v1f > prec || v1f < -prec)
45  {
46  Vector Vf(i + 1);
47  Vf.tail(i) = fac_V.cols(1, i).t() * fac_f;
48  Vf[0] = v1f;
49  fac_f -= Vs * Vf;
50  }
51  }
52 }
53 
54 // Implicitly restarted Arnoldi factorization
55 template < typename Scalar,
56  int SelectionRule,
57  typename OpType >
59 {
60  if(k >= ncv)
61  return;
62 
63  DoubleShiftQR<Scalar> decomp_ds(ncv);
65  Matrix Q(ncv, ncv, arma::fill::eye);
66 
67  for(int i = k; i < ncv; i++)
68  {
69  if(is_complex(ritz_val[i], prec) && is_conj(ritz_val[i], ritz_val[i + 1], prec))
70  {
71  // H - mu * I = Q1 * R1
72  // H <- R1 * Q1 + mu * I = Q1' * H * Q1
73  // H - conj(mu) * I = Q2 * R2
74  // H <- R2 * Q2 + conj(mu) * I = Q2' * H * Q2
75  //
76  // (H - mu * I) * (H - conj(mu) * I) = Q1 * Q2 * R2 * R1 = Q * R
77  Scalar s = 2 * ritz_val[i].real();
78  Scalar t = std::norm(ritz_val[i]);
79 
80  decomp_ds.compute(fac_H, s, t);
81 
82  // Q -> Q * Qi
83  decomp_ds.apply_YQ(Q);
84  //decomp_ds.apply_YQ(fac_V, ncv);
85  // H -> Q'HQ
86  // Matrix Q(ncv, ncv, arma::fill::eye);
87  // decomp_ds.apply_YQ(Q);
88  // fac_H = Q.t() * fac_H * Q;
89  fac_H = decomp_ds.matrix_QtHQ();
90 
91  i++;
92  } else {
93  // QR decomposition of H - mu * I, mu is real
94  fac_H.diag() -= ritz_val[i].real();
95  decomp.compute(fac_H);
96 
97  // Q -> Q * Qi
98  decomp.apply_YQ(Q);
99  // H -> Q'HQ = RQ + mu * I
100  fac_H = decomp.matrix_RQ();
101  fac_H.diag() += ritz_val[i].real();
102  }
103  }
104  // V -> VQ
105  // Q has some elements being zero
106  // The first (ncv - k + i) elements of the i-th column of Q are non-zero
107  Matrix Vs(dim_n, k + 1);
108  int nnz;
109  for(int i = 0; i < k; i++)
110  {
111  nnz = ncv - k + i + 1;
112  Matrix V(fac_V.memptr(), dim_n, nnz, false);
113  Vector q(Q.colptr(i), nnz, false);
114  Vector v(Vs.colptr(i), dim_n, false);
115  v = V * q;
116  }
117  Vs.col(k) = fac_V * Q.col(k);
118  fac_V.head_cols(k + 1) = Vs;
119 
120  Vector fk = fac_f * Q(ncv - 1, k - 1) + fac_V.col(k) * fac_H(k, k - 1);
121  factorize_from(k, ncv, fk);
122  retrieve_ritzpair();
123 }
124 
125 // Calculate the number of converged Ritz values
126 template < typename Scalar,
127  int SelectionRule,
128  typename OpType >
130 {
131 /*
132  // thresh = tol * max(prec, abs(theta)), theta for ritz value
133  Vector rv = arma::abs(ritz_val.head(nev));
134  Vector thresh = tol * arma::clamp(rv, prec, std::max(prec, rv.max()));
135  Vector resid = arma::abs(ritz_vec.tail_rows(1).t()) * arma::norm(fac_f);
136  // Converged "wanted" ritz values
137  ritz_conv = (resid < thresh);
138 */
139 
140  const Scalar f_norm = arma::norm(fac_f);
141  for(unsigned int i = 0; i < ritz_conv.n_elem; i++)
142  {
143  Scalar thresh = tol * std::max(prec, std::abs(ritz_val[i]));
144  Scalar resid = std::abs(ritz_vec(ncv - 1, i)) * f_norm;
145  ritz_conv[i] = (resid < thresh);
146  }
147 
148  return arma::sum(ritz_conv);
149 }
150 
151 // Return the adjusted nev for restarting
152 template < typename Scalar,
153  int SelectionRule,
154  typename OpType >
156 {
157  int nev_new = nev;
158 
159  // Increase nev by one if ritz_val[nev - 1] and
160  // ritz_val[nev] are conjugate pairs
161  if(is_complex(ritz_val[nev - 1], prec) &&
162  is_conj(ritz_val[nev - 1], ritz_val[nev], prec))
163  {
164  nev_new = nev + 1;
165  }
166  // Adjust nev_new again, according to dnaup2.f line 660~674 in ARPACK
167  nev_new = nev_new + std::min(nconv, (ncv - nev_new) / 2);
168  if(nev_new == 1 && ncv >= 6)
169  nev_new = ncv / 2;
170  else if(nev_new == 1 && ncv > 3)
171  nev_new = 2;
172 
173  if(nev_new > ncv - 2)
174  nev_new = ncv - 2;
175 
176  // Examine conjugate pairs again
177  if(is_complex(ritz_val[nev_new - 1], prec) &&
178  is_conj(ritz_val[nev_new - 1], ritz_val[nev_new], prec))
179  {
180  nev_new++;
181  }
182 
183  return nev_new;
184 }
185 
186 // Retrieve and sort ritz values and ritz vectors
187 template < typename Scalar,
188  int SelectionRule,
189  typename OpType >
191 {
192  /*ComplexVector evals(ncv);
193  ComplexMatrix evecs(ncv, ncv);
194  arma::eig_gen(evals, evecs, fac_H);*/
195  UpperHessenbergEigen<Scalar> decomp(fac_H);
196  ComplexVector evals = decomp.eigenvalues();
197  ComplexMatrix evecs = decomp.eigenvectors();
198 
199  SortEigenvalue<Complex, SelectionRule> sorting(evals.memptr(), evals.n_elem);
200  std::vector<int> ind = sorting.index();
201 
202  // Copy the ritz values and vectors to ritz_val and ritz_vec, respectively
203  for(int i = 0; i < ncv; i++)
204  {
205  ritz_val[i] = evals[ind[i]];
206  }
207  for(int i = 0; i < nev; i++)
208  {
209  ritz_vec.col(i) = evecs.col(ind[i]);
210  }
211 }
212 
213 
214 
215 // Sort the first nev Ritz pairs in decreasing magnitude order
216 // This is used to return the final results
217 template < typename Scalar,
218  int SelectionRule,
219  typename OpType >
221 {
222  SortEigenvalue<Complex, LARGEST_MAGN> sorting(ritz_val.memptr(), nev);
223  std::vector<int> ind = sorting.index();
224 
225  ComplexVector new_ritz_val(ncv);
226  ComplexMatrix new_ritz_vec(ncv, nev);
227  BoolVector new_ritz_conv(nev);
228 
229  for(int i = 0; i < nev; i++)
230  {
231  new_ritz_val[i] = ritz_val[ind[i]];
232  new_ritz_vec.col(i) = ritz_vec.col(ind[i]);
233  new_ritz_conv[i] = ritz_conv[ind[i]];
234  }
235 
236  ritz_val.swap(new_ritz_val);
237  ritz_vec.swap(new_ritz_vec);
238  ritz_conv.swap(new_ritz_conv);
239 }
240 
241 
242 
243 // Initialization and clean-up
244 template < typename Scalar,
245  int SelectionRule,
246  typename OpType >
248 {
249  // Reset all matrices/vectors to zero
250  fac_V.zeros(dim_n, ncv);
251  fac_H.zeros(ncv, ncv);
252  fac_f.zeros(dim_n);
253  ritz_val.zeros(ncv);
254  ritz_vec.zeros(ncv, nev);
255  ritz_conv.zeros(nev);
256 
257  nmatop = 0;
258  niter = 0;
259 
260  Vector r(init_resid, dim_n, false);
261  // The first column of fac_V
262  Vector v(fac_V.colptr(0), dim_n, false);
263  Scalar rnorm = arma::norm(r);
264  if(rnorm < prec)
265  throw std::invalid_argument("initial residual vector cannot be zero");
266  v = r / rnorm;
267 
268  Vector w(dim_n);
269  op->perform_op(v.memptr(), w.memptr());
270  nmatop++;
271 
272  fac_H(0, 0) = arma::dot(v, w);
273  fac_f = w - v * fac_H(0, 0);
274 }
275 
276 // Initialization with random initial coefficients
277 template < typename Scalar,
278  int SelectionRule,
279  typename OpType >
281 {
282  Vector init_resid(dim_n, arma::fill::randu);
283  init_resid -= 0.5;
284  init(init_resid.memptr());
285 }
286 
287 // Compute Ritz pairs and return the number of converged eigenvalues
288 template < typename Scalar,
289  int SelectionRule,
290  typename OpType >
292 {
293  // The m-step Arnoldi factorization
294  factorize_from(1, ncv, fac_f);
295  retrieve_ritzpair();
296  // Restarting
297  int i, nconv = 0, nev_adj;
298  for(i = 0; i < maxit; i++)
299  {
300  nconv = num_converged(tol);
301  if(nconv >= nev)
302  break;
303 
304  nev_adj = nev_adjusted(nconv);
305  restart(nev_adj);
306  }
307  // Sorting results
308  sort_ritzpair();
309 
310  niter += i + 1;
311 
312  return std::min(nev, nconv);
313 }
314 
315 // Return converged eigenvalues
316 template < typename Scalar,
317  int SelectionRule,
318  typename OpType >
319 inline typename GenEigsSolver<Scalar, SelectionRule, OpType>::ComplexVector GenEigsSolver<Scalar, SelectionRule, OpType>::eigenvalues()
320 {
321  int nconv = arma::sum(ritz_conv);
322  ComplexVector res(nconv);
323 
324  if(!nconv)
325  return res;
326 
327  int j = 0;
328  for(int i = 0; i < nev; i++)
329  {
330  if(ritz_conv[i])
331  {
332  res[j] = ritz_val[i];
333  j++;
334  }
335  }
336 
337  return res;
338 }
339 
340 // Return converged eigenvectors
341 template < typename Scalar,
342  int SelectionRule,
343  typename OpType >
344 inline typename GenEigsSolver<Scalar, SelectionRule, OpType>::ComplexMatrix GenEigsSolver<Scalar, SelectionRule, OpType>::eigenvectors(int nvec)
345 {
346  int nconv = arma::sum(ritz_conv);
347  nvec = std::min(nvec, nconv);
348  ComplexMatrix res(dim_n, nvec);
349 
350  if(!nvec)
351  return res;
352 
353  ComplexMatrix ritz_vec_conv(ncv, nvec);
354  int j = 0;
355  for(int i = 0; i < nev && j < nvec; i++)
356  {
357  if(ritz_conv[i])
358  {
359  ritz_vec_conv.col(j) = ritz_vec.col(i);
360  j++;
361  }
362  }
363 
364  res = fac_V * ritz_vec_conv;
365 
366  return res;
367 }
virtual void compute(const Matrix &mat)
virtual Matrix matrix_RQ()
void apply_YQ(Matrix &Y)
ComplexVector eigenvalues()
int compute(int maxit=1000, Scalar tol=1e-10)
ComplexMatrix eigenvectors()