Spectra  1.0.1
Header-only C++ Library for Large Scale Eigenvalue Problems
GenEigsComplexShiftSolver.h
1 // Copyright (C) 2016-2022 Yixuan Qiu <yixuan.qiu@cos.name>
2 //
3 // This Source Code Form is subject to the terms of the Mozilla
4 // Public License v. 2.0. If a copy of the MPL was not distributed
5 // with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 
7 #ifndef SPECTRA_GEN_EIGS_COMPLEX_SHIFT_SOLVER_H
8 #define SPECTRA_GEN_EIGS_COMPLEX_SHIFT_SOLVER_H
9 
10 #include <Eigen/Core>
11 
12 #include "GenEigsBase.h"
13 #include "Util/SelectionRule.h"
14 #include "MatOp/DenseGenComplexShiftSolve.h"
15 
16 namespace Spectra {
17 
32 template <typename OpType = DenseGenComplexShiftSolve<double>>
33 class GenEigsComplexShiftSolver : public GenEigsBase<OpType, IdentityBOp>
34 {
35 private:
36  using Scalar = typename OpType::Scalar;
37  using Index = Eigen::Index;
38  using Complex = std::complex<Scalar>;
39  using Vector = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;
40  using ComplexArray = Eigen::Array<Complex, Eigen::Dynamic, 1>;
41 
43  using Base::m_op;
44  using Base::m_n;
45  using Base::m_nev;
46  using Base::m_fac;
47  using Base::m_ritz_val;
48  using Base::m_ritz_vec;
49 
50  const Scalar m_sigmar;
51  const Scalar m_sigmai;
52 
53  // First transform back the Ritz values, and then sort
54  void sort_ritzpair(SortRule sort_rule) override
55  {
56  using std::abs;
57  using std::sqrt;
58  using std::norm;
59 
60  // The eigenvalues we get from the iteration is
61  // nu = 0.5 * (1 / (lambda - sigma) + 1 / (lambda - conj(sigma)))
62  // So the eigenvalues of the original problem is
63  // 1 \pm sqrt(1 - 4 * nu^2 * sigmai^2)
64  // lambda = sigmar + -----------------------------------
65  // 2 * nu
66  // We need to pick the correct root
67  // Let (lambdaj, vj) be the j-th eigen pair, then A * vj = lambdaj * vj
68  // and inv(A - r * I) * vj = 1 / (lambdaj - r) * vj
69  // where r is any shift value.
70  // We can use this identity to determine lambdaj
71  //
72  // op(v) computes Re(inv(A - r * I) * v) for any real v
73  // If r is real, then op(v) is also real. Let a = Re(vj), b = Im(vj),
74  // then op(vj) = op(a) + op(b) * i
75  // By comparing op(vj) and [1 / (lambdaj - r) * vj], we can determine
76  // which one is the correct root
77 
78  // Select a random shift value
79  SimpleRandom<Scalar> rng(0);
80  const Scalar shiftr = rng.random() * m_sigmar + rng.random();
81  const Complex shift = Complex(shiftr, Scalar(0));
82  m_op.set_shift(shiftr, Scalar(0));
83 
84  // Calculate inv(A - r * I) * vj
85  Vector v_real(m_n), v_imag(m_n), OPv_real(m_n), OPv_imag(m_n);
86  constexpr Scalar eps = TypeTraits<Scalar>::epsilon();
87  for (Index i = 0; i < m_nev; i++)
88  {
89  v_real.noalias() = m_fac.matrix_V() * m_ritz_vec.col(i).real();
90  v_imag.noalias() = m_fac.matrix_V() * m_ritz_vec.col(i).imag();
91  m_op.perform_op(v_real.data(), OPv_real.data());
92  m_op.perform_op(v_imag.data(), OPv_imag.data());
93 
94  // Two roots computed from the quadratic equation
95  const Complex nu = m_ritz_val[i];
96  const Complex root_part1 = m_sigmar + Scalar(0.5) / nu;
97  const Complex root_part2 = Scalar(0.5) * sqrt(Scalar(1) - Scalar(4) * m_sigmai * m_sigmai * (nu * nu)) / nu;
98  const Complex root1 = root_part1 + root_part2;
99  const Complex root2 = root_part1 - root_part2;
100 
101  // Test roots
102  Scalar err1 = Scalar(0), err2 = Scalar(0);
103  for (int k = 0; k < m_n; k++)
104  {
105  const Complex rhs1 = Complex(v_real[k], v_imag[k]) / (root1 - shift);
106  const Complex rhs2 = Complex(v_real[k], v_imag[k]) / (root2 - shift);
107  const Complex OPv = Complex(OPv_real[k], OPv_imag[k]);
108  err1 += norm(OPv - rhs1);
109  err2 += norm(OPv - rhs2);
110  }
111 
112  const Complex lambdaj = (err1 < err2) ? root1 : root2;
113  m_ritz_val[i] = lambdaj;
114 
115  if (abs(Eigen::numext::imag(lambdaj)) > eps)
116  {
117  m_ritz_val[i + 1] = Eigen::numext::conj(lambdaj);
118  i++;
119  }
120  else
121  {
122  m_ritz_val[i] = Complex(Eigen::numext::real(lambdaj), Scalar(0));
123  }
124  }
125 
126  Base::sort_ritzpair(sort_rule);
127  }
128 
129 public:
149  GenEigsComplexShiftSolver(OpType& op, Index nev, Index ncv, const Scalar& sigmar, const Scalar& sigmai) :
150  Base(op, IdentityBOp(), nev, ncv),
151  m_sigmar(sigmar), m_sigmai(sigmai)
152  {
153  op.set_shift(m_sigmar, m_sigmai);
154  }
155 };
156 
157 } // namespace Spectra
158 
159 #endif // SPECTRA_GEN_EIGS_COMPLEX_SHIFT_SOLVER_H
GenEigsComplexShiftSolver(OpType &op, Index nev, Index ncv, const Scalar &sigmar, const Scalar &sigmai)