HermEigsBase.h
Go to the documentation of this file.
1 // Copyright (C) 2018-2025 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_HERM_EIGS_BASE_H
8 #define SPECTRA_HERM_EIGS_BASE_H
9 
10 #include <Eigen/Core>
11 #include <vector> // std::vector
12 #include <cmath> // std::abs, std::pow
13 #include <algorithm> // std::min
14 #include <stdexcept> // std::invalid_argument
15 #include <utility> // std::move
16 
17 #include "Util/Version.h"
18 #include "Util/TypeTraits.h"
19 #include "Util/SelectionRule.h"
20 #include "Util/CompInfo.h"
21 #include "Util/SimpleRandom.h"
24 #include "LinAlg/TridiagEigen.h"
25 #include "LinAlg/Lanczos.h"
26 
27 namespace Spectra {
28 
34 
43 template <typename OpType, typename BOpType>
45 {
46 private:
47  using Scalar = typename OpType::Scalar;
48  // The real part type of the matrix element, e.g.,
49  // Scalar = double => RealScalar = double
50  // Scalar = std::complex<double> => RealScalar = double
62 
65 
66 protected:
67  // clang-format off
68 
69  // In SymEigsSolver and SymEigsShiftSolver, the A operator is an lvalue provided by
70  // the user. In SymGEigsSolver, the A operator is an rvalue. To avoid copying objects,
71  // we use the following scheme:
72  // 1. If the op parameter in the constructor is an lvalue, make m_op a const reference to op
73  // 2. If op is an rvalue, move op to m_op_container, and then make m_op a const
74  // reference to m_op_container[0]
75  std::vector<OpType> m_op_container;
76  const OpType& m_op; // matrix operator for A
77  const Index m_n; // dimension of matrix A
78  const Index m_nev; // number of eigenvalues requested
79  const Index m_ncv; // dimension of Krylov subspace in the Lanczos method
80  Index m_nmatop; // number of matrix operations called
81  Index m_niter; // number of restarting iterations
82 
83  LanczosFac m_fac; // Lanczos factorization
84  RealVector m_ritz_val; // Ritz values
85 
86 private:
87  RealMatrix m_ritz_vec; // Ritz vectors
88  RealVector m_ritz_est; // last row of m_ritz_vec, also called the Ritz estimates
89  BoolArray m_ritz_conv; // indicator of the convergence of Ritz values
90  CompInfo m_info; // status of the computation
91  // clang-format on
92 
93  // Move rvalue object to the container
94  static std::vector<OpType> create_op_container(OpType&& rval)
95  {
96  std::vector<OpType> container;
97  container.emplace_back(std::move(rval));
98  return container;
99  }
100 
101  // Implicitly restarted Lanczos factorization
102  void restart(Index k, SortRule selection)
103  {
104  using std::abs;
105 
106  if (k >= m_ncv)
107  return;
108 
109  // QR decomposition on a real symmetric matrix
111  // Q is a real orthogonal matrix
112  RealMatrix Q = RealMatrix::Identity(m_ncv, m_ncv);
113 
114  // Apply large shifts first
115  const int nshift = m_ncv - k;
116  RealVector shifts = m_ritz_val.tail(nshift);
117  std::sort(shifts.data(), shifts.data() + nshift,
118  [](const RealScalar& v1, const RealScalar& v2) { return abs(v1) > abs(v2); });
119 
120  for (Index i = 0; i < nshift; i++)
121  {
122  // QR decomposition of H-mu*I, mu is the shift
123  // H is known to be a real symmetric matrix
124  decomp.compute(m_fac.matrix_H().real(), shifts[i]);
125 
126  // Q -> Q * Qi
127  decomp.apply_YQ(Q);
128  // H -> Q'HQ
129  // Since QR = H - mu * I, we have H = QR + mu * I
130  // and therefore Q'HQ = RQ + mu * I
131  m_fac.compress_H(decomp);
132  // Note that in our setting, mu is an eigenvalue of H,
133  // so after applying Q'HQ, H must have be of the following form
134  // H = [X 0 0]
135  // [0 mu 0]
136  // [0 0 D]
137  // Then we can force H[k, k-1] = H[k-1, k] = 0 and H[k, k] = mu,
138  // where k is the size of X
139  //
140  // Currently disabled due to numerical stability
141  //
142  // m_fac.deflate_H(m_ncv - i - 1, shifts[i]);
143  }
144 
145  m_fac.compress_V(Q);
147 
148  retrieve_ritzpair(selection);
149  }
150 
151  // Calculates the number of converged Ritz values
153  {
154  using std::pow;
155 
156  // The machine precision, ~= 1e-16 for the "double" type
158  // std::pow() is not constexpr, so we do not declare eps23 to be constexpr
159  // But most compilers should be able to compute eps23 at compile time
160  const RealScalar eps23 = pow(eps, RealScalar(2) / 3);
161 
162  // thresh = tol * max(eps23, abs(theta)), theta for Ritz value
163  RealArray thresh = tol * m_ritz_val.head(m_nev).array().abs().max(eps23);
164  RealArray resid = m_ritz_est.head(m_nev).array().abs() * m_fac.f_norm();
165  // Converged "wanted" Ritz values
166  m_ritz_conv = (resid < thresh);
167 
168  return m_ritz_conv.count();
169  }
170 
171  // Returns the adjusted nev for restarting
173  {
174  using std::abs;
175 
176  // A very small value, but 1.0 / near_0 does not overflow
177  // ~= 1e-307 for the "double" type
178  const RealScalar near_0 = TypeTraits<RealScalar>::min() * RealScalar(10);
179 
180  Index nev_new = m_nev;
181  for (Index i = m_nev; i < m_ncv; i++)
182  if (abs(m_ritz_est[i]) < near_0)
183  nev_new++;
184 
185  // Adjust nev_new, according to dsaup2.f line 677~684 in ARPACK
186  nev_new += (std::min)(nconv, (m_ncv - nev_new) / 2);
187  if (nev_new == 1 && m_ncv >= 6)
188  nev_new = m_ncv / 2;
189  else if (nev_new == 1 && m_ncv > 2)
190  nev_new = 2;
191 
192  if (nev_new > m_ncv - 1)
193  nev_new = m_ncv - 1;
194 
195  return nev_new;
196  }
197 
198  // Retrieves and sorts Ritz values and Ritz vectors
199  void retrieve_ritzpair(SortRule selection)
200  {
201  TridiagEigen<RealScalar> decomp(m_fac.matrix_H().real());
202  const RealVector& evals = decomp.eigenvalues();
203  const RealMatrix& evecs = decomp.eigenvectors();
204 
205  // Sort Ritz values and put the wanted ones at the beginning
206  std::vector<Index> ind = argsort(selection, evals, m_ncv);
207 
208  // Copy the Ritz values and vectors to m_ritz_val and m_ritz_vec, respectively
209  for (Index i = 0; i < m_ncv; i++)
210  {
211  m_ritz_val[i] = evals[ind[i]];
212  m_ritz_est[i] = evecs(m_ncv - 1, ind[i]);
213  }
214  for (Index i = 0; i < m_nev; i++)
215  {
216  m_ritz_vec.col(i).noalias() = evecs.col(ind[i]);
217  }
218  }
219 
220 protected:
221  // Sorts the first nev Ritz pairs in the specified order
222  // This is used to return the final results
223  virtual void sort_ritzpair(SortRule sort_rule)
224  {
225  if ((sort_rule != SortRule::LargestAlge) && (sort_rule != SortRule::LargestMagn) &&
226  (sort_rule != SortRule::SmallestAlge) && (sort_rule != SortRule::SmallestMagn))
227  throw std::invalid_argument("unsupported sorting rule");
228 
229  std::vector<Index> ind = argsort(sort_rule, m_ritz_val, m_nev);
230 
231  RealVector new_ritz_val(m_ncv);
232  RealMatrix new_ritz_vec(m_ncv, m_nev);
233  BoolArray new_ritz_conv(m_nev);
234 
235  for (Index i = 0; i < m_nev; i++)
236  {
237  new_ritz_val[i] = m_ritz_val[ind[i]];
238  new_ritz_vec.col(i).noalias() = m_ritz_vec.col(ind[i]);
239  new_ritz_conv[i] = m_ritz_conv[ind[i]];
240  }
241 
242  m_ritz_val.swap(new_ritz_val);
243  m_ritz_vec.swap(new_ritz_vec);
244  m_ritz_conv.swap(new_ritz_conv);
245  }
246 
247 public:
249 
250  // If op is an lvalue
251  HermEigsBase(OpType& op, const BOpType& Bop, Index nev, Index ncv) :
252  m_op(op),
253  m_n(op.rows()),
254  m_nev(nev),
255  m_ncv(ncv > m_n ? m_n : ncv),
256  m_nmatop(0),
257  m_niter(0),
258  m_fac(ArnoldiOpType(op, Bop), m_ncv),
260  {
261  if (nev < 1 || nev > m_n - 1)
262  throw std::invalid_argument("nev must satisfy 1 <= nev <= n - 1, n is the size of matrix");
263 
264  if (ncv <= nev || ncv > m_n)
265  throw std::invalid_argument("ncv must satisfy nev < ncv <= n, n is the size of matrix");
266  }
267 
268  // If op is an rvalue
269  HermEigsBase(OpType&& op, const BOpType& Bop, Index nev, Index ncv) :
271  m_op(m_op_container.front()),
272  m_n(m_op.rows()),
273  m_nev(nev),
274  m_ncv(ncv > m_n ? m_n : ncv),
275  m_nmatop(0),
276  m_niter(0),
277  m_fac(ArnoldiOpType(m_op, Bop), m_ncv),
278  m_info(CompInfo::NotComputed)
279  {
280  if (nev < 1 || nev > m_n - 1)
281  throw std::invalid_argument("nev must satisfy 1 <= nev <= n - 1, n is the size of matrix");
282 
283  if (ncv <= nev || ncv > m_n)
284  throw std::invalid_argument("ncv must satisfy nev < ncv <= n, n is the size of matrix");
285  }
286 
290  virtual ~HermEigsBase() {}
291 
293 
303  void init(const Scalar* init_resid)
304  {
305  // Reset all matrices/vectors to zero
310 
315 
316  m_nmatop = 0;
317  m_niter = 0;
318 
319  // Initialize the Lanczos factorization
320  MapConstVec v0(init_resid, m_n);
321  m_fac.init(v0, m_nmatop);
322  }
323 
331  void init()
332  {
333  SimpleRandom<Scalar> rng(0);
334  Vector init_resid = rng.random_vec(m_n);
335  init(init_resid.data());
336  }
337 
360  Index compute(SortRule selection = SortRule::LargestMagn, Index maxit = 1000,
361  RealScalar tol = 1e-10, SortRule sorting = SortRule::LargestAlge)
362  {
363  // The m-step Lanczos factorization
365  retrieve_ritzpair(selection);
366  // Restarting
367  Index i, nconv = 0, nev_adj;
368  for (i = 0; i < maxit; i++)
369  {
370  nconv = num_converged(tol);
371  if (nconv >= m_nev)
372  break;
373 
374  nev_adj = nev_adjusted(nconv);
375  restart(nev_adj, selection);
376  }
377  // Sorting results
378  sort_ritzpair(sorting);
379 
380  m_niter += i + 1;
382 
383  return (std::min)(m_nev, nconv);
384  }
385 
390  CompInfo info() const { return m_info; }
391 
395  Index num_iterations() const { return m_niter; }
396 
400  Index num_operations() const { return m_nmatop; }
401 
412  {
413  const Index nconv = m_ritz_conv.count();
414  RealVector res(nconv);
415 
416  if (!nconv)
417  return res;
418 
419  Index j = 0;
420  for (Index i = 0; i < m_nev; i++)
421  {
422  if (m_ritz_conv[i])
423  {
424  res[j] = m_ritz_val[i];
425  j++;
426  }
427  }
428 
429  return res;
430  }
431 
441  virtual Matrix eigenvectors(Index nvec) const
442  {
443  const Index nconv = m_ritz_conv.count();
444  nvec = (std::min)(nvec, nconv);
445  Matrix res(m_n, nvec);
446 
447  if (!nvec)
448  return res;
449 
450  RealMatrix ritz_vec_conv(m_ncv, nvec);
451  Index j = 0;
452  for (Index i = 0; i < m_nev && j < nvec; i++)
453  {
454  if (m_ritz_conv[i])
455  {
456  ritz_vec_conv.col(j).noalias() = m_ritz_vec.col(i);
457  j++;
458  }
459  }
460 
461  res.noalias() = m_fac.matrix_V() * ritz_vec_conv;
462 
463  return res;
464  }
465 
469  virtual Matrix eigenvectors() const
470  {
471  return eigenvectors(m_nev);
472  }
473 };
474 
475 } // namespace Spectra
476 
477 #endif // SPECTRA_HERM_EIGS_BASE_H
Spectra::Lanczos::factorize_from
void factorize_from(Index from_k, Index to_m, Index &op_counter) override
Definition: Lanczos.h:59
ind
std::vector< int > ind
Definition: Slicing_stdvector_cxx11.cpp:1
Spectra::HermEigsBase::m_ritz_val
RealVector m_ritz_val
Definition: HermEigsBase.h:84
Spectra::Arnoldi::f_norm
RealScalar f_norm() const
Definition: Arnoldi.h:128
rng
static std::mt19937 rng
Definition: timeFactorOverhead.cpp:31
Spectra::HermEigsBase::create_op_container
static std::vector< OpType > create_op_container(OpType &&rval)
Definition: HermEigsBase.h:94
Spectra::CompInfo::Successful
@ Successful
Computation was successful.
Spectra::HermEigsBase::init
void init(const Scalar *init_resid)
Definition: HermEigsBase.h:303
e
Array< double, 1, 3 > e(1./3., 0.5, 2.)
v0
static const double v0
Definition: testCal3DFisheye.cpp:31
Spectra::Arnoldi::init
void init(MapConstVec &v0, Index &op_counter)
Definition: Arnoldi.h:132
Spectra::Arnoldi::matrix_H
const Matrix & matrix_H() const
Definition: Arnoldi.h:126
Eigen::PlainObjectBase::swap
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void swap(DenseBase< OtherDerived > &other)
Definition: PlainObjectBase.h:953
Spectra::HermEigsBase::num_iterations
Index num_iterations() const
Definition: HermEigsBase.h:395
Spectra::HermEigsBase::m_ncv
const Index m_ncv
Definition: HermEigsBase.h:79
Spectra::Arnoldi::matrix_V
const Matrix & matrix_V() const
Definition: Arnoldi.h:125
Spectra::HermEigsBase::m_niter
Index m_niter
Definition: HermEigsBase.h:81
Spectra::HermEigsBase::m_nmatop
Index m_nmatop
Definition: HermEigsBase.h:80
Spectra::HermEigsBase::num_converged
Index num_converged(const RealScalar &tol)
Definition: HermEigsBase.h:152
Spectra::HermEigsBase< DenseSymShiftSolve< double >, IdentityBOp >::Index
Eigen::Index Index
Definition: HermEigsBase.h:52
Eigen::Array
General-purpose arrays with easy API for coefficient-wise operations.
Definition: Array.h:45
UpperHessenbergQR.h
Spectra::HermEigsBase::m_nev
const Index m_nev
Definition: HermEigsBase.h:78
res
cout<< "Here is the matrix m:"<< endl<< m<< endl;Matrix< ptrdiff_t, 3, 1 > res
Definition: PartialRedux_count.cpp:3
Spectra::HermEigsBase::m_fac
LanczosFac m_fac
Definition: HermEigsBase.h:83
Lanczos.h
Eigen::PlainObjectBase::resize
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void resize(Index rows, Index cols)
Definition: PlainObjectBase.h:271
Spectra::HermEigsBase::info
CompInfo info() const
Definition: HermEigsBase.h:390
rows
int rows
Definition: Tutorial_commainit_02.cpp:1
Spectra::SortRule::LargestAlge
@ LargestAlge
Spectra::HermEigsBase::sort_ritzpair
virtual void sort_ritzpair(SortRule sort_rule)
Definition: HermEigsBase.h:223
Spectra::TridiagEigen::eigenvalues
const Vector & eigenvalues() const
Definition: TridiagEigen.h:210
epsilon
static double epsilon
Definition: testRot3.cpp:37
TridiagEigen.h
Version.h
Spectra::HermEigsBase::num_operations
Index num_operations() const
Definition: HermEigsBase.h:400
Spectra::HermEigsBase::m_ritz_conv
BoolArray m_ritz_conv
Definition: HermEigsBase.h:89
Spectra::HermEigsBase::nev_adjusted
Index nev_adjusted(Index nconv)
Definition: HermEigsBase.h:172
Spectra::CompInfo::NotConverging
@ NotConverging
Spectra::HermEigsBase< DenseSymShiftSolve< double >, IdentityBOp >::RealScalar
typename Eigen::NumTraits< Scalar >::Real RealScalar
Definition: HermEigsBase.h:51
TypeTraits.h
j
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2
Spectra::HermEigsBase::m_info
CompInfo m_info
Definition: HermEigsBase.h:90
Spectra::CompInfo
CompInfo
Definition: CompInfo.h:17
Spectra::HermEigsBase::init
void init()
Definition: HermEigsBase.h:331
Spectra::Lanczos
Definition: Lanczos.h:27
Spectra::HermEigsBase::retrieve_ritzpair
void retrieve_ritzpair(SortRule selection)
Definition: HermEigsBase.h:199
CompInfo.h
Spectra::UpperHessenbergQR< double >::apply_YQ
void apply_YQ(GenericMatrix Y) const
Definition: UpperHessenbergQR.h:469
Eigen::Triplet< double >
Spectra::Arnoldi::compress_V
void compress_V(const Eigen::MatrixBase< Derived > &Q)
Definition: Arnoldi.h:305
ceres::pow
Jet< T, N > pow(const Jet< T, N > &f, double g)
Definition: jet.h:570
Spectra::ArnoldiOp
Definition: ArnoldiOp.h:33
Eigen::Map
A matrix or vector expression mapping an existing array of data.
Definition: Map.h:94
Spectra::HermEigsBase::eigenvectors
virtual Matrix eigenvectors(Index nvec) const
Definition: HermEigsBase.h:441
Spectra::HermEigsBase::restart
void restart(Index k, SortRule selection)
Definition: HermEigsBase.h:102
Spectra::HermEigsBase::m_ritz_est
RealVector m_ritz_est
Definition: HermEigsBase.h:88
Spectra::TridiagEigen::eigenvectors
const Matrix & eigenvectors() const
Definition: TridiagEigen.h:219
Eigen::Quaternion
The quaternion class used to represent 3D orientations and rotations.
Definition: ForwardDeclarations.h:293
move
detail::enable_if_t<!detail::move_never< T >::value, T > move(object &&obj)
Definition: cast.h:1250
Spectra::TridiagEigen
Definition: TridiagEigen.h:23
SelectionRule.h
Spectra::SortRule::SmallestMagn
@ SmallestMagn
Spectra::SortRule
SortRule
Definition: SelectionRule.h:33
std
Definition: BFloat16.h:88
Spectra::HermEigsBase< DenseSymShiftSolve< double >, IdentityBOp >::Scalar
typename DenseSymShiftSolve< double > ::Scalar Scalar
Definition: HermEigsBase.h:47
v2
Vector v2
Definition: testSerializationBase.cpp:39
Spectra
Definition: LOBPCGSolver.h:19
Spectra::SortRule::LargestMagn
@ LargestMagn
SimpleRandom.h
Spectra::TridiagQR::compute
void compute(ConstGenericMatrix &mat, const Scalar &shift=Scalar(0)) override
Definition: UpperHessenbergQR.h:601
ArnoldiOp.h
Spectra::HermEigsBase::m_ritz_vec
RealMatrix m_ritz_vec
Definition: HermEigsBase.h:87
Eigen::PlainObjectBase::data
EIGEN_DEVICE_FUNC const EIGEN_STRONG_INLINE Scalar * data() const
Definition: PlainObjectBase.h:247
Spectra::HermEigsBase::ArnoldiOpType
ArnoldiOp< Scalar, OpType, BOpType > ArnoldiOpType
Definition: HermEigsBase.h:63
min
#define min(a, b)
Definition: datatypes.h:19
gtsam::tol
const G double tol
Definition: Group.h:79
Spectra::HermEigsBase::m_n
const Index m_n
Definition: HermEigsBase.h:77
Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic >
abs
#define abs(x)
Definition: datatypes.h:17
Spectra::HermEigsBase::eigenvectors
virtual Matrix eigenvectors() const
Definition: HermEigsBase.h:469
Spectra::SortRule::SmallestAlge
@ SmallestAlge
Select eigenvalues with smallest algebraic value. Only for symmetric eigen solvers.
Eigen::PlainObjectBase::setZero
EIGEN_DEVICE_FUNC Derived & setZero(Index size)
Definition: CwiseNullaryOp.h:562
Spectra::CompInfo::NotComputed
@ NotComputed
Spectra::Lanczos::compress_H
void compress_H(const TridiagQR< RealScalar > &decomp)
Definition: Lanczos.h:195
Spectra::HermEigsBase
Definition: HermEigsBase.h:44
Spectra::TridiagQR
Definition: UpperHessenbergQR.h:545
Spectra::HermEigsBase::m_op
const OpType & m_op
Definition: HermEigsBase.h:76
Spectra::HermEigsBase::m_op_container
std::vector< OpType > m_op_container
Definition: HermEigsBase.h:75
i
int i
Definition: BiCGSTAB_step_by_step.cpp:9
Spectra::HermEigsBase::eigenvalues
RealVector eigenvalues() const
Definition: HermEigsBase.h:411
v1
Vector v1
Definition: testSerializationBase.cpp:38
Scalar
SCALAR Scalar
Definition: bench_gemm.cpp:46
Spectra::HermEigsBase::compute
Index compute(SortRule selection=SortRule::LargestMagn, Index maxit=1000, RealScalar tol=1e-10, SortRule sorting=SortRule::LargestAlge)
Definition: HermEigsBase.h:360
Eigen::Index
EIGEN_DEFAULT_DENSE_INDEX_TYPE Index
The Index type as used for the API.
Definition: Meta.h:74


gtsam
Author(s):
autogenerated on Fri Mar 28 2025 03:01:39