TridiagEigen.h
Go to the documentation of this file.
1 // The code was adapted from Eigen/src/Eigenvaleus/SelfAdjointEigenSolver.h
2 //
3 // Copyright (C) 2008-2010 Gael Guennebaud <gael.guennebaud@inria.fr>
4 // Copyright (C) 2010 Jitse Niesen <jitse@maths.leeds.ac.uk>
5 // Copyright (C) 2016-2019 Yixuan Qiu <yixuan.qiu@cos.name>
6 //
7 // This Source Code Form is subject to the terms of the Mozilla
8 // Public License v. 2.0. If a copy of the MPL was not distributed
9 // with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
10 
11 #ifndef TRIDIAG_EIGEN_H
12 #define TRIDIAG_EIGEN_H
13 
14 #include <Eigen/Core>
15 #include <Eigen/Jacobi>
16 #include <stdexcept>
17 
18 #include "../Util/TypeTraits.h"
19 
20 namespace Spectra {
21 
22 template <typename Scalar = double>
24 {
25 private:
27  // For convenience in adapting the tridiagonal_qr_step() function
28  typedef Scalar RealScalar;
29 
32 
35 
37  Vector m_main_diag; // Main diagonal elements of the matrix
38  Vector m_sub_diag; // Sub-diagonal elements of the matrix
39  Matrix m_evecs; // To store eigenvectors
40 
41  bool m_computed;
42  const Scalar m_near_0; // a very small value, ~= 1e-307 for the "double" type
43 
44  // Adapted from Eigen/src/Eigenvaleus/SelfAdjointEigenSolver.h
46  RealScalar* subdiag, Index start,
47  Index end, Scalar* matrixQ,
48  Index n)
49  {
50  using std::abs;
51 
52  RealScalar td = (diag[end - 1] - diag[end]) * RealScalar(0.5);
53  RealScalar e = subdiag[end - 1];
54  // Note that thanks to scaling, e^2 or td^2 cannot overflow, however they can still
55  // underflow thus leading to inf/NaN values when using the following commented code:
56  // RealScalar e2 = numext::abs2(subdiag[end-1]);
57  // RealScalar mu = diag[end] - e2 / (td + (td>0 ? 1 : -1) * sqrt(td*td + e2));
58  // This explain the following, somewhat more complicated, version:
59  RealScalar mu = diag[end];
60  if (td == Scalar(0))
61  mu -= abs(e);
62  else
63  {
64  RealScalar e2 = Eigen::numext::abs2(subdiag[end - 1]);
65  RealScalar h = Eigen::numext::hypot(td, e);
66  if (e2 == RealScalar(0))
67  mu -= (e / (td + (td > RealScalar(0) ? RealScalar(1) : RealScalar(-1)))) * (e / h);
68  else
69  mu -= e2 / (td + (td > RealScalar(0) ? h : -h));
70  }
71 
72  RealScalar x = diag[start] - mu;
73  RealScalar z = subdiag[start];
74  Eigen::Map<Matrix> q(matrixQ, n, n);
75  for (Index k = start; k < end; ++k)
76  {
78  rot.makeGivens(x, z);
79 
80  const RealScalar s = rot.s();
81  const RealScalar c = rot.c();
82 
83  // do T = G' T G
84  RealScalar sdk = s * diag[k] + c * subdiag[k];
85  RealScalar dkp1 = s * subdiag[k] + c * diag[k + 1];
86 
87  diag[k] = c * (c * diag[k] - s * subdiag[k]) - s * (c * subdiag[k] - s * diag[k + 1]);
88  diag[k + 1] = s * sdk + c * dkp1;
89  subdiag[k] = c * sdk - s * dkp1;
90 
91  if (k > start)
92  subdiag[k - 1] = c * subdiag[k - 1] - s * z;
93 
94  x = subdiag[k];
95 
96  if (k < end - 1)
97  {
98  z = -s * subdiag[k + 1];
99  subdiag[k + 1] = c * subdiag[k + 1];
100  }
101 
102  // apply the givens rotation to the unit matrix Q = Q * G
103  if (matrixQ)
104  q.applyOnTheRight(k, k + 1, rot);
105  }
106  }
107 
108 public:
110  m_n(0), m_computed(false),
111  m_near_0(TypeTraits<Scalar>::min() * Scalar(10))
112  {}
113 
115  m_n(mat.rows()), m_computed(false),
116  m_near_0(TypeTraits<Scalar>::min() * Scalar(10))
117  {
118  compute(mat);
119  }
120 
122  {
123  using std::abs;
124 
125  m_n = mat.rows();
126  if (m_n != mat.cols())
127  throw std::invalid_argument("TridiagEigen: matrix must be square");
128 
130  m_sub_diag.resize(m_n - 1);
131  m_evecs.resize(m_n, m_n);
132  m_evecs.setIdentity();
133 
134  // Scale matrix to improve stability
135  const Scalar scale = std::max(mat.diagonal().cwiseAbs().maxCoeff(),
136  mat.diagonal(-1).cwiseAbs().maxCoeff());
137  // If scale=0, mat is a zero matrix, so we can early stop
138  if (scale < m_near_0)
139  {
140  // m_main_diag contains eigenvalues
142  // m_evecs has been set identity
143  // m_evecs.setIdentity();
144  m_computed = true;
145  return;
146  }
147  m_main_diag.noalias() = mat.diagonal() / scale;
148  m_sub_diag.noalias() = mat.diagonal(-1) / scale;
149 
151  Scalar* subdiag = m_sub_diag.data();
152 
153  Index end = m_n - 1;
154  Index start = 0;
155  Index iter = 0; // total number of iterations
156  int info = 0; // 0 for success, 1 for failure
157 
158  const Scalar considerAsZero = TypeTraits<Scalar>::min();
160 
161  while (end > 0)
162  {
163  for (Index i = start; i < end; i++)
164  if (abs(subdiag[i]) <= considerAsZero ||
165  abs(subdiag[i]) <= (abs(diag[i]) + abs(diag[i + 1])) * precision)
166  subdiag[i] = 0;
167 
168  // find the largest unreduced block
169  while (end > 0 && subdiag[end - 1] == Scalar(0))
170  end--;
171 
172  if (end <= 0)
173  break;
174 
175  // if we spent too many iterations, we give up
176  iter++;
177  if (iter > 30 * m_n)
178  {
179  info = 1;
180  break;
181  }
182 
183  start = end - 1;
184  while (start > 0 && subdiag[start - 1] != Scalar(0))
185  start--;
186 
187  tridiagonal_qr_step(diag, subdiag, start, end, m_evecs.data(), m_n);
188  }
189 
190  if (info > 0)
191  throw std::runtime_error("TridiagEigen: eigen decomposition failed");
192 
193  // Scale eigenvalues back
194  m_main_diag *= scale;
195 
196  m_computed = true;
197  }
198 
199  const Vector& eigenvalues() const
200  {
201  if (!m_computed)
202  throw std::logic_error("TridiagEigen: need to call compute() first");
203 
204  // After calling compute(), main_diag will contain the eigenvalues.
205  return m_main_diag;
206  }
207 
208  const Matrix& eigenvectors() const
209  {
210  if (!m_computed)
211  throw std::logic_error("TridiagEigen: need to call compute() first");
212 
213  return m_evecs;
214  }
215 };
216 
217 } // namespace Spectra
218 
219 #endif // TRIDIAG_EIGEN_H
Spectra::TridiagEigen::GenericMatrix
Eigen::Ref< Matrix > GenericMatrix
Definition: TridiagEigen.h:33
Spectra::TridiagEigen::ConstGenericMatrix
const typedef Eigen::Ref< const Matrix > ConstGenericMatrix
Definition: TridiagEigen.h:34
s
RealScalar s
Definition: level1_cplx_impl.h:126
e
Array< double, 1, 3 > e(1./3., 0.5, 2.)
mu
double mu
Definition: testBoundingConstraint.cpp:37
Spectra::TridiagEigen::compute
void compute(ConstGenericMatrix &mat)
Definition: TridiagEigen.h:121
c
Scalar Scalar * c
Definition: benchVecAdd.cpp:17
gtsam::diag
Matrix diag(const std::vector< Matrix > &Hs)
Definition: Matrix.cpp:206
x
set noclip points set clip one set noclip two set bar set border lt lw set xdata set ydata set zdata set x2data set y2data set boxwidth set dummy x
Definition: gnuplot_common_settings.hh:12
Spectra::TridiagEigen::TridiagEigen
TridiagEigen()
Definition: TridiagEigen.h:109
Spectra::TridiagEigen::m_computed
bool m_computed
Definition: TridiagEigen.h:41
mat
MatrixXf mat
Definition: Tutorial_AdvancedInitialization_CommaTemporary.cpp:1
h
const double h
Definition: testSimpleHelicopter.cpp:19
Eigen::PlainObjectBase::resize
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void resize(Index rows, Index cols)
Definition: PlainObjectBase.h:271
Eigen::JacobiRotation
Rotation given by a cosine-sine pair.
Definition: ForwardDeclarations.h:283
rot
int EIGEN_BLAS_FUNC() rot(int *n, RealScalar *px, int *incx, RealScalar *py, int *incy, RealScalar *pc, RealScalar *ps)
Definition: level1_real_impl.h:79
rows
int rows
Definition: Tutorial_commainit_02.cpp:1
Spectra::TridiagEigen::eigenvalues
const Vector & eigenvalues() const
Definition: TridiagEigen.h:199
Spectra::TridiagEigen::m_sub_diag
Vector m_sub_diag
Definition: TridiagEigen.h:38
n
int n
Definition: BiCGSTAB_simple.cpp:1
epsilon
static double epsilon
Definition: testRot3.cpp:37
Spectra::TridiagEigen::Index
Eigen::Index Index
Definition: TridiagEigen.h:26
scale
set noclip points set clip one set noclip two set bar set border lt lw set xdata set ydata set zdata set x2data set y2data set boxwidth set dummy y set format x g set format y g set format x2 g set format y2 g set format z g set angles radians set nogrid set key title set key left top Right noreverse box linetype linewidth samplen spacing width set nolabel set noarrow set nologscale set logscale x set set pointsize set encoding default set nopolar set noparametric set set set set surface set nocontour set clabel set mapping cartesian set nohidden3d set cntrparam order set cntrparam linear set cntrparam levels auto set cntrparam points set size set set xzeroaxis lt lw set x2zeroaxis lt lw set yzeroaxis lt lw set y2zeroaxis lt lw set tics in set ticslevel set tics scale
Definition: gnuplot_common_settings.hh:54
Eigen::numext::q
EIGEN_DEVICE_FUNC const Scalar & q
Definition: SpecialFunctionsImpl.h:1984
Spectra::TridiagEigen::m_near_0
const Scalar m_near_0
Definition: TridiagEigen.h:42
info
else if n * info
Definition: 3rdparty/Eigen/lapack/cholesky.cpp:18
Spectra::TridiagEigen::m_evecs
Matrix m_evecs
Definition: TridiagEigen.h:39
Spectra::TridiagEigen::m_n
Index m_n
Definition: TridiagEigen.h:36
pybind_wrapper_test_script.z
z
Definition: pybind_wrapper_test_script.py:61
Spectra::TridiagEigen::RealScalar
Scalar RealScalar
Definition: TridiagEigen.h:28
Eigen::Map
A matrix or vector expression mapping an existing array of data.
Definition: Map.h:94
Spectra::TridiagEigen::Matrix
Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > Matrix
Definition: TridiagEigen.h:30
Spectra::TridiagEigen::Vector
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > Vector
Definition: TridiagEigen.h:31
Spectra::TridiagEigen::eigenvectors
const Matrix & eigenvectors() const
Definition: TridiagEigen.h:208
Spectra::TridiagEigen
Definition: TridiagEigen.h:23
Eigen::numext::abs2
EIGEN_DEVICE_FUNC bool abs2(bool x)
Definition: Eigen/src/Core/MathFunctions.h:1294
Eigen::Ref
A matrix or vector expression mapping an existing expression.
Definition: Ref.h:281
precision
cout precision(2)
iter
iterator iter(handle obj)
Definition: pytypes.h:2475
Spectra
Definition: LOBPCGSolver.h:19
Eigen::PlainObjectBase::data
EIGEN_DEVICE_FUNC const EIGEN_STRONG_INLINE Scalar * data() const
Definition: PlainObjectBase.h:247
min
#define min(a, b)
Definition: datatypes.h:19
Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic >
Spectra::TridiagEigen::m_main_diag
Vector m_main_diag
Definition: TridiagEigen.h:37
abs
#define abs(x)
Definition: datatypes.h:17
Eigen::PlainObjectBase::setZero
EIGEN_DEVICE_FUNC Derived & setZero(Index size)
Definition: CwiseNullaryOp.h:562
Spectra::TridiagEigen::TridiagEigen
TridiagEigen(ConstGenericMatrix &mat)
Definition: TridiagEigen.h:114
Eigen::placeholders::end
static const EIGEN_DEPRECATED end_t end
Definition: IndexedViewHelper.h:181
max
#define max(a, b)
Definition: datatypes.h:20
Spectra::TridiagEigen::tridiagonal_qr_step
static void tridiagonal_qr_step(RealScalar *diag, RealScalar *subdiag, Index start, Index end, Scalar *matrixQ, Index n)
Definition: TridiagEigen.h:45
i
int i
Definition: BiCGSTAB_step_by_step.cpp:9
Scalar
SCALAR Scalar
Definition: bench_gemm.cpp:46
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 Sat Nov 16 2024 04:09:33