ARPACK-Eigen
GenEigsSolver.h
1 #ifndef GEN_EIGS_SOLVER_H
2 #define GEN_EIGS_SOLVER_H
3 
4 #include <Eigen/Core>
5 #include <Eigen/QR>
6 #include <Eigen/Eigenvalues>
7 
8 #include <vector>
9 #include <algorithm>
10 #include <cmath>
11 #include <complex>
12 #include <utility>
13 #include <stdexcept>
14 
15 #include "SelectionRule.h"
16 #include "UpperHessenbergQR.h"
17 #include "MatOp/DenseGenMatProd.h"
18 #include "MatOp/DenseGenRealShiftSolve.h"
19 #include "MatOp/DenseGenComplexShiftSolve.h"
20 
21 
77 template < typename Scalar = double,
78  int SelectionRule = LARGEST_MAGN,
79  typename OpType = DenseGenMatProd<double> >
81 {
82 private:
83  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> Matrix;
84  typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1> Vector;
85  typedef Eigen::Array<Scalar, Eigen::Dynamic, 1> Array;
86  typedef Eigen::Array<bool, Eigen::Dynamic, 1> BoolArray;
87  typedef Eigen::Map<const Matrix> MapMat;
88  typedef Eigen::Map<const Vector> MapVec;
89 
90  typedef std::complex<Scalar> Complex;
91  typedef Eigen::Matrix<Complex, Eigen::Dynamic, Eigen::Dynamic> ComplexMatrix;
92  typedef Eigen::Matrix<Complex, Eigen::Dynamic, 1> ComplexVector;
93 
94  typedef Eigen::EigenSolver<Matrix> EigenSolver;
95  typedef Eigen::HouseholderQR<Matrix> QRdecomp;
96  typedef Eigen::HouseholderSequence<Matrix, Vector> QRQ;
97 
98  typedef std::pair<Complex, int> SortPair;
99 
100 protected:
101  OpType *op; // object to conduct matrix operation,
102  // e.g. matrix-vector product
103  const int dim_n; // dimension of matrix A
104  const int nev; // number of eigenvalues requested
105 
106 private:
107  const int ncv; // number of ritz values
108  int nmatop; // number of matrix operations called
109  int niter; // number of restarting iterations
110 
111 protected:
112  Matrix fac_V; // V matrix in the Arnoldi factorization
113  Matrix fac_H; // H matrix in the Arnoldi factorization
114  Vector fac_f; // residual in the Arnoldi factorization
115 
116  ComplexVector ritz_val; // ritz values
117  ComplexMatrix ritz_vec; // ritz vectors
118 
119 private:
120  BoolArray ritz_conv; // indicator of the convergence of ritz values
121 
122  const Scalar prec; // precision parameter used to test convergence
123  // prec = epsilon^(2/3)
124  // epsilon is the machine precision,
125  // e.g. ~= 1e-16 for the "double" type
126 
127  // Arnoldi factorization starting from step-k
128  void factorize_from(int from_k, int to_m, const Vector &fk)
129  {
130  if(to_m <= from_k) return;
131 
132  fac_f = fk;
133 
134  Vector v(dim_n), w(dim_n);
135  Scalar beta = 0.0;
136  // Keep the upperleft k x k submatrix of H and set other elements to 0
137  fac_H.rightCols(ncv - from_k).setZero();
138  fac_H.block(from_k, 0, ncv - from_k, from_k).setZero();
139  for(int i = from_k; i <= to_m - 1; i++)
140  {
141  beta = fac_f.norm();
142  v.noalias() = fac_f / beta;
143  fac_V.col(i) = v; // The (i+1)-th column
144  fac_H.block(i, 0, 1, i).setZero();
145  fac_H(i, i - 1) = beta;
146 
147  op->perform_op(v.data(), w.data());
148  nmatop++;
149 
150  Vector h = fac_V.leftCols(i + 1).transpose() * w;
151  fac_H.block(0, i, i + 1, 1) = h;
152 
153  fac_f = w - fac_V.leftCols(i + 1) * h;
154  // Correct f if it is not orthogonal to V
155  // Typically the largest absolute value occurs in
156  // the first element, i.e., <v1, f>, so we use this
157  // to test the orthogonality
158  Scalar v1f = fac_f.dot(fac_V.col(0));
159  if(v1f > prec || v1f < -prec)
160  {
161  Vector Vf(i + 1);
162  Vf.tail(i) = fac_V.block(0, 1, dim_n, i).transpose() * fac_f;
163  Vf[0] = v1f;
164  fac_f -= fac_V.leftCols(i + 1) * Vf;
165  }
166  }
167  }
168 
169  static bool is_complex(Complex v, Scalar eps)
170  {
171  return std::abs(v.imag()) > eps;
172  }
173 
174  static bool is_conj(Complex v1, Complex v2, Scalar eps)
175  {
176  return std::abs(v1 - std::conj(v2)) < eps;
177  }
178 
179  // Implicitly restarted Arnoldi factorization
180  void restart(int k)
181  {
182  if(k >= ncv)
183  return;
184 
185  QRdecomp decomp_gen;
186  UpperHessenbergQR<Scalar> decomp_hb;
187  Vector em(ncv);
188  em.setZero();
189  em[ncv - 1] = 1;
190 
191  for(int i = k; i < ncv; i++)
192  {
193  if(is_complex(ritz_val[i], prec) && is_conj(ritz_val[i], ritz_val[i + 1], prec))
194  {
195  // H - mu * I = Q1 * R1
196  // H <- R1 * Q1 + mu * I = Q1' * H * Q1
197  // H - conj(mu) * I = Q2 * R2
198  // H <- R2 * Q2 + conj(mu) * I = Q2' * H * Q2
199  //
200  // (H - mu * I) * (H - conj(mu) * I) = Q1 * Q2 * R2 * R1 = Q * R
201  Scalar re = ritz_val[i].real();
202  Scalar s = std::norm(ritz_val[i]);
203  Matrix HH = fac_H;
204  HH.diagonal().array() -= 2 * re;
205  HH = fac_H * HH;
206  HH.diagonal().array() += s;
207 
208  // NOTE: HH is no longer upper Hessenberg
209  decomp_gen.compute(HH);
210  QRQ Q = decomp_gen.householderQ();
211 
212  // V -> VQ
213  fac_V.applyOnTheRight(Q);
214  // H -> Q'HQ
215  fac_H.applyOnTheRight(Q);
216  fac_H.applyOnTheLeft(Q.adjoint());
217  // em -> Q'em
218  em.applyOnTheLeft(Q.adjoint());
219 
220  i++;
221  } else {
222  // QR decomposition of H - mu * I, mu is real
223  fac_H.diagonal().array() -= ritz_val[i].real();
224  decomp_hb.compute(fac_H);
225 
226  // V -> VQ
227  decomp_hb.apply_YQ(fac_V);
228  // H -> Q'HQ = RQ + mu * I
229  fac_H = decomp_hb.matrix_RQ();
230  fac_H.diagonal().array() += ritz_val[i].real();
231  // em -> Q'em
232  decomp_hb.apply_QtY(em);
233  }
234  }
235 
236  Vector fk = fac_f * em[k - 1] + fac_V.col(k) * fac_H(k, k - 1);
237  factorize_from(k, ncv, fk);
238  retrieve_ritzpair();
239  }
240 
241  // Calculate the number of converged Ritz values
242  int num_converged(Scalar tol)
243  {
244  // thresh = tol * max(prec, abs(theta)), theta for ritz value
245  Array thresh = tol * ritz_val.head(nev).array().abs().max(prec);
246  Array resid = ritz_vec.template bottomRows<1>().transpose().array().abs() * fac_f.norm();
247  // Converged "wanted" ritz values
248  ritz_conv = (resid < thresh);
249 
250  return ritz_conv.cast<int>().sum();
251  }
252 
253  // Return the adjusted nev for restarting
254  int nev_adjusted(int nconv)
255  {
256  int nev_new = nev;
257 
258  // Increase nev by one if ritz_val[nev - 1] and
259  // ritz_val[nev] are conjugate pairs
260  if(is_complex(ritz_val[nev - 1], prec) &&
261  is_conj(ritz_val[nev - 1], ritz_val[nev], prec))
262  {
263  nev_new = nev + 1;
264  }
265  // Adjust nev_new again, according to dnaup2.f line 660~674 in ARPACK
266  nev_new = nev_new + std::min(nconv, (ncv - nev_new) / 2);
267  if(nev_new == 1 && ncv >= 6)
268  nev_new = ncv / 2;
269  else if(nev_new == 1 && ncv > 3)
270  nev_new = 2;
271 
272  if(nev_new > ncv - 2)
273  nev_new = ncv - 2;
274 
275  // Examine conjugate pairs again
276  if(is_complex(ritz_val[nev_new - 1], prec) &&
277  is_conj(ritz_val[nev_new - 1], ritz_val[nev_new], prec))
278  {
279  nev_new++;
280  }
281 
282  return nev_new;
283  }
284 
285  // Retrieve and sort ritz values and ritz vectors
286  void retrieve_ritzpair()
287  {
288  EigenSolver eig(fac_H);
289  ComplexVector evals = eig.eigenvalues();
290  ComplexMatrix evecs = eig.eigenvectors();
291 
292  std::vector<SortPair> pairs(ncv);
293  EigenvalueComparator<Complex, SelectionRule> comp;
294  for(int i = 0; i < ncv; i++)
295  {
296  pairs[i].first = evals[i];
297  pairs[i].second = i;
298  }
299  std::sort(pairs.begin(), pairs.end(), comp);
300 
301  // Copy the ritz values and vectors to ritz_val and ritz_vec, respectively
302  for(int i = 0; i < ncv; i++)
303  {
304  ritz_val[i] = pairs[i].first;
305  }
306  for(int i = 0; i < nev; i++)
307  {
308  ritz_vec.col(i) = evecs.col(pairs[i].second);
309  }
310  }
311 
312 protected:
313  // Sort the first nev Ritz pairs in decreasing magnitude order
314  // This is used to return the final results
315  virtual void sort_ritzpair()
316  {
317  std::vector<SortPair> pairs(nev);
318  EigenvalueComparator<Complex, LARGEST_MAGN> comp;
319  for(int i = 0; i < nev; i++)
320  {
321  pairs[i].first = ritz_val[i];
322  pairs[i].second = i;
323  }
324  std::sort(pairs.begin(), pairs.end(), comp);
325 
326  ComplexMatrix new_ritz_vec(ncv, nev);
327  BoolArray new_ritz_conv(nev);
328 
329  for(int i = 0; i < nev; i++)
330  {
331  ritz_val[i] = pairs[i].first;
332  new_ritz_vec.col(i) = ritz_vec.col(pairs[i].second);
333  new_ritz_conv[i] = ritz_conv[pairs[i].second];
334  }
335 
336  ritz_vec.swap(new_ritz_vec);
337  ritz_conv.swap(new_ritz_conv);
338  }
339 
340 public:
358  GenEigsSolver(OpType *op_, int nev_, int ncv_) :
359  op(op_),
360  dim_n(op->rows()),
361  nev(nev_),
362  ncv(ncv_ > dim_n ? dim_n : ncv_),
363  nmatop(0),
364  niter(0),
365  prec(std::pow(std::numeric_limits<Scalar>::epsilon(), Scalar(2.0 / 3)))
366  {
367  if(nev_ < 1 || nev_ > dim_n - 2)
368  throw std::invalid_argument("nev must satisfy 1 <= nev <= n - 2, n is the size of matrix");
369 
370  if(ncv_ < nev_ + 2 || ncv_ > dim_n)
371  throw std::invalid_argument("ncv must satisfy nev + 2 <= ncv <= n, n is the size of matrix");
372  }
373 
383  void init(const Scalar *init_resid)
384  {
385  // Reset all matrices/vectors to zero
386  fac_V.resize(dim_n, ncv);
387  fac_H.resize(ncv, ncv);
388  fac_f.resize(dim_n);
389  ritz_val.resize(ncv);
390  ritz_vec.resize(ncv, nev);
391  ritz_conv.resize(nev);
392 
393  fac_V.setZero();
394  fac_H.setZero();
395  fac_f.setZero();
396  ritz_val.setZero();
397  ritz_vec.setZero();
398  ritz_conv.setZero();
399 
400  Vector v(dim_n);
401  std::copy(init_resid, init_resid + dim_n, v.data());
402  Scalar vnorm = v.norm();
403  if(vnorm < prec)
404  throw std::invalid_argument("initial residual vector cannot be zero");
405  v /= vnorm;
406 
407  Vector w(dim_n);
408  op->perform_op(v.data(), w.data());
409  nmatop++;
410 
411  fac_H(0, 0) = v.dot(w);
412  fac_f = w - v * fac_H(0, 0);
413  fac_V.col(0) = v;
414  }
415 
423  void init()
424  {
425  Vector init_resid = Vector::Random(dim_n);
426  init_resid.array() -= 0.5;
427  init(init_resid.data());
428  }
429 
438  int compute(int maxit = 1000, Scalar tol = 1e-10)
439  {
440  // The m-step Arnoldi factorization
441  factorize_from(1, ncv, fac_f);
442  retrieve_ritzpair();
443  // Restarting
444  int i, nconv, nev_adj;
445  for(i = 0; i < maxit; i++)
446  {
447  nconv = num_converged(tol);
448  if(nconv >= nev)
449  break;
450 
451  nev_adj = nev_adjusted(nconv);
452  restart(nev_adj);
453  }
454  // Sorting results
455  sort_ritzpair();
456 
457  niter += i + 1;
458 
459  return std::min(nev, nconv);
460  }
461 
465  int num_iterations() { return niter; }
466 
470  int num_operations() { return nmatop; }
471 
479  ComplexVector eigenvalues()
480  {
481  int nconv = ritz_conv.cast<int>().sum();
482  ComplexVector res(nconv);
483 
484  if(!nconv)
485  return res;
486 
487  int j = 0;
488  for(int i = 0; i < nev; i++)
489  {
490  if(ritz_conv[i])
491  {
492  res[j] = ritz_val[i];
493  j++;
494  }
495  }
496 
497  return res;
498  }
499 
507  ComplexMatrix eigenvectors()
508  {
509  int nconv = ritz_conv.cast<int>().sum();
510  ComplexMatrix res(dim_n, nconv);
511 
512  if(!nconv)
513  return res;
514 
515  ComplexMatrix ritz_vec_conv(ncv, nconv);
516  int j = 0;
517  for(int i = 0; i < nev; i++)
518  {
519  if(ritz_conv[i])
520  {
521  ritz_vec_conv.col(j) = ritz_vec.col(i);
522  j++;
523  }
524  }
525 
526  res.noalias() = fac_V * ritz_vec_conv;
527 
528  return res;
529  }
530 };
531 
532 
533 
534 
535 
555 template <typename Scalar = double,
556  int SelectionRule = LARGEST_MAGN,
557  typename OpType = DenseGenRealShiftSolve<double> >
558 class GenEigsRealShiftSolver: public GenEigsSolver<Scalar, SelectionRule, OpType>
559 {
560 private:
561  typedef std::complex<Scalar> Complex;
562  typedef Eigen::Array<Complex, Eigen::Dynamic, 1> ComplexArray;
563 
564  Scalar sigma;
565 
566  // First transform back the ritz values, and then sort
567  void sort_ritzpair()
568  {
569  // The eigenvalus we get from the iteration is nu = 1 / (lambda - sigma)
570  // So the eigenvalues of the original problem is lambda = 1 / nu + sigma
571  ComplexArray ritz_val_org = Scalar(1.0) / this->ritz_val.head(this->nev).array() + sigma;
572  this->ritz_val.head(this->nev) = ritz_val_org;
574  }
575 public:
594  GenEigsRealShiftSolver(OpType *op_, int nev_, int ncv_, Scalar sigma_) :
595  GenEigsSolver<Scalar, SelectionRule, OpType>(op_, nev_, ncv_),
596  sigma(sigma_)
597  {
598  this->op->set_shift(sigma);
599  }
600 };
601 
602 
603 
604 
605 
625 template <typename Scalar = double,
626  int SelectionRule = LARGEST_MAGN,
627  typename OpType = DenseGenComplexShiftSolve<double> >
628 class GenEigsComplexShiftSolver: public GenEigsSolver<Scalar, SelectionRule, OpType>
629 {
630 private:
631  typedef Eigen::Array<Scalar, Eigen::Dynamic, 1> Array;
632  typedef std::complex<Scalar> Complex;
633  typedef Eigen::Array<Complex, Eigen::Dynamic, 1> ComplexArray;
634 
635  Scalar sigmar;
636  Scalar sigmai;
637 
638  // First transform back the ritz values, and then sort
639  void sort_ritzpair()
640  {
641  // The eigenvalus we get from the iteration is
642  // nu = 0.5 * (1 / (lambda - sigma)) + 1 / (lambda - conj(sigma)))
643  // So the eigenvalues of the original problem is
644  // 1 \pm sqrt(1 - 4 * nu^2 * sigmai^2)
645  // lambda = sigmar + -----------------------------------
646  // 2 * nu
647  // We need to rule out the wrong one
648  // Let v1 be the first eigenvector, then A * v1 = lambda1 * v1
649  // and inv(A - r * I) * v1 = 1 / (lambda1 - r) * v1
650  // where r is any real value.
651  // We can use this identity to test which lambda to choose
652  ComplexArray nu = this->ritz_val.head(this->nev).array();
653  ComplexArray tmp1 = Scalar(0.5) / nu + sigmar;
654  ComplexArray tmp2 = (Scalar(1) / nu / nu - 4 * sigmai * sigmai).sqrt() * Scalar(0.5);
655 
656  ComplexArray root1 = tmp1 + tmp2;
657  ComplexArray root2 = tmp1 - tmp2;
658 
659  ComplexArray v = this->fac_V * this->ritz_vec.col(0);
660  Array v_real = v.real();
661  Array v_imag = v.imag();
662  Array lhs_real(this->dim_n), lhs_imag(this->dim_n);
663 
664  this->op->set_shift(sigmar, 0);
665  this->op->perform_op(v_real.data(), lhs_real.data());
666  this->op->perform_op(v_imag.data(), lhs_imag.data());
667 
668  ComplexArray rhs1 = v / (root1[0] - Complex(sigmar, 0));
669  ComplexArray rhs2 = v / (root2[0] - Complex(sigmar, 0));
670 
671  Scalar err1 = (rhs1.real() - lhs_real).abs().sum() + (rhs1.imag() - lhs_imag).abs().sum();
672  Scalar err2 = (rhs2.real() - lhs_real).abs().sum() + (rhs2.imag() - lhs_imag).abs().sum();
673 
674  if(err1 < err2)
675  {
676  this->ritz_val.head(this->nev) = root1;
677  } else {
678  this->ritz_val.head(this->nev) = root2;
679  }
681  }
682 public:
702  GenEigsComplexShiftSolver(OpType *op_, int nev_, int ncv_, Scalar sigmar_, Scalar sigmai_) :
703  GenEigsSolver<Scalar, SelectionRule, OpType>(op_, nev_, ncv_),
704  sigmar(sigmar_), sigmai(sigmai_)
705  {
706  this->op->set_shift(sigmar, sigmai);
707  }
708 };
709 
710 #endif // GEN_EIGS_SOLVER_H
virtual Matrix matrix_RQ()
virtual void compute(ConstGenericMatrix &mat)
GenEigsRealShiftSolver(OpType *op_, int nev_, int ncv_, Scalar sigma_)
void apply_YQ(GenericMatrix Y)
int num_iterations()
int num_operations()
void init(const Scalar *init_resid)
GenEigsSolver(OpType *op_, int nev_, int ncv_)
ComplexVector eigenvalues()
int compute(int maxit=1000, Scalar tol=1e-10)
ComplexMatrix eigenvectors()
GenEigsComplexShiftSolver(OpType *op_, int nev_, int ncv_, Scalar sigmar_, Scalar sigmai_)
void apply_QtY(Vector &Y)