UpperHessenbergEigen.h
Go to the documentation of this file.
1 // The code was adapted from Eigen/src/Eigenvaleus/EigenSolver.h
2 //
3 // Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
4 // Copyright (C) 2010,2012 Jitse Niesen <jitse@maths.leeds.ac.uk>
5 // Copyright (C) 2016-2025 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 SPECTRA_UPPER_HESSENBERG_EIGEN_H
12 #define SPECTRA_UPPER_HESSENBERG_EIGEN_H
13 
14 #include <Eigen/Core>
15 #include <stdexcept>
16 
17 #include "UpperHessenbergSchur.h"
18 
19 namespace Spectra {
20 
21 template <typename Scalar = double>
23 {
24 private:
30 
31  using Complex = std::complex<Scalar>;
34 
35  Index m_n; // Size of the matrix
36  UpperHessenbergSchur<Scalar> m_schur; // Schur decomposition solver
37  Matrix m_matT; // Schur T matrix
38  Matrix m_eivec; // Storing eigenvectors
39  ComplexVector m_eivalues; // Eigenvalues
40  bool m_computed;
41 
43  {
44  using std::abs;
45 
46  const Index size = m_eivec.cols();
48 
49  // inefficient! this is already computed in RealSchur
50  Scalar norm(0);
51  for (Index j = 0; j < size; ++j)
52  {
53  norm += m_matT.row(j).segment((std::max)(j - 1, Index(0)), size - (std::max)(j - 1, Index(0))).cwiseAbs().sum();
54  }
55 
56  // Backsubstitute to find vectors of upper triangular form
57  if (norm == Scalar(0))
58  return;
59 
60  for (Index n = size - 1; n >= 0; n--)
61  {
62  Scalar p = m_eivalues.coeff(n).real();
63  Scalar q = m_eivalues.coeff(n).imag();
64 
65  // Scalar vector
66  if (q == Scalar(0))
67  {
68  Scalar lastr(0), lastw(0);
69  Index l = n;
70 
71  m_matT.coeffRef(n, n) = Scalar(1);
72  for (Index i = n - 1; i >= 0; i--)
73  {
74  Scalar w = m_matT.coeff(i, i) - p;
75  Scalar r = m_matT.row(i).segment(l, n - l + 1).dot(m_matT.col(n).segment(l, n - l + 1));
76 
77  if (m_eivalues.coeff(i).imag() < Scalar(0))
78  {
79  lastw = w;
80  lastr = r;
81  }
82  else
83  {
84  l = i;
85  if (m_eivalues.coeff(i).imag() == Scalar(0))
86  {
87  if (w != Scalar(0))
88  m_matT.coeffRef(i, n) = -r / w;
89  else
90  m_matT.coeffRef(i, n) = -r / (eps * norm);
91  }
92  else // Solve real equations
93  {
94  Scalar x = m_matT.coeff(i, i + 1);
95  Scalar y = m_matT.coeff(i + 1, i);
96  Scalar denom = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) + m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag();
97  Scalar t = (x * lastr - lastw * r) / denom;
98  m_matT.coeffRef(i, n) = t;
99  if (abs(x) > abs(lastw))
100  m_matT.coeffRef(i + 1, n) = (-r - w * t) / x;
101  else
102  m_matT.coeffRef(i + 1, n) = (-lastr - y * t) / lastw;
103  }
104 
105  // Overflow control
106  Scalar t = abs(m_matT.coeff(i, n));
107  if ((eps * t) * t > Scalar(1))
108  m_matT.col(n).tail(size - i) /= t;
109  }
110  }
111  }
112  else if (q < Scalar(0) && n > 0)
113  { // Complex vector
114  Scalar lastra(0), lastsa(0), lastw(0);
115  Index l = n - 1;
116 
117  // Last vector component imaginary so matrix is triangular
118  if (abs(m_matT.coeff(n, n - 1)) > abs(m_matT.coeff(n - 1, n)))
119  {
120  m_matT.coeffRef(n - 1, n - 1) = q / m_matT.coeff(n, n - 1);
121  m_matT.coeffRef(n - 1, n) = -(m_matT.coeff(n, n) - p) / m_matT.coeff(n, n - 1);
122  }
123  else
124  {
125  Complex cc = Complex(Scalar(0), -m_matT.coeff(n - 1, n)) / Complex(m_matT.coeff(n - 1, n - 1) - p, q);
126  m_matT.coeffRef(n - 1, n - 1) = Eigen::numext::real(cc);
127  m_matT.coeffRef(n - 1, n) = Eigen::numext::imag(cc);
128  }
129  m_matT.coeffRef(n, n - 1) = Scalar(0);
130  m_matT.coeffRef(n, n) = Scalar(1);
131  for (Index i = n - 2; i >= 0; i--)
132  {
133  Scalar ra = m_matT.row(i).segment(l, n - l + 1).dot(m_matT.col(n - 1).segment(l, n - l + 1));
134  Scalar sa = m_matT.row(i).segment(l, n - l + 1).dot(m_matT.col(n).segment(l, n - l + 1));
135  Scalar w = m_matT.coeff(i, i) - p;
136 
137  if (m_eivalues.coeff(i).imag() < Scalar(0))
138  {
139  lastw = w;
140  lastra = ra;
141  lastsa = sa;
142  }
143  else
144  {
145  l = i;
146  if (m_eivalues.coeff(i).imag() == Scalar(0))
147  {
148  Complex cc = Complex(-ra, -sa) / Complex(w, q);
149  m_matT.coeffRef(i, n - 1) = Eigen::numext::real(cc);
151  }
152  else
153  {
154  // Solve complex equations
155  Scalar x = m_matT.coeff(i, i + 1);
156  Scalar y = m_matT.coeff(i + 1, i);
157  Scalar vr = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) + m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag() - q * q;
158  Scalar vi = (m_eivalues.coeff(i).real() - p) * Scalar(2) * q;
159  if ((vr == Scalar(0)) && (vi == Scalar(0)))
160  vr = eps * norm * (abs(w) + abs(q) + abs(x) + abs(y) + abs(lastw));
161 
162  Complex cc = Complex(x * lastra - lastw * ra + q * sa, x * lastsa - lastw * sa - q * ra) / Complex(vr, vi);
163  m_matT.coeffRef(i, n - 1) = Eigen::numext::real(cc);
165  if (abs(x) > (abs(lastw) + abs(q)))
166  {
167  m_matT.coeffRef(i + 1, n - 1) = (-ra - w * m_matT.coeff(i, n - 1) + q * m_matT.coeff(i, n)) / x;
168  m_matT.coeffRef(i + 1, n) = (-sa - w * m_matT.coeff(i, n) - q * m_matT.coeff(i, n - 1)) / x;
169  }
170  else
171  {
172  cc = Complex(-lastra - y * m_matT.coeff(i, n - 1), -lastsa - y * m_matT.coeff(i, n)) / Complex(lastw, q);
173  m_matT.coeffRef(i + 1, n - 1) = Eigen::numext::real(cc);
174  m_matT.coeffRef(i + 1, n) = Eigen::numext::imag(cc);
175  }
176  }
177 
178  // Overflow control
179  Scalar t = (std::max)(abs(m_matT.coeff(i, n - 1)), abs(m_matT.coeff(i, n)));
180  if ((eps * t) * t > Scalar(1))
181  m_matT.block(i, n - 1, size - i, 2) /= t;
182  }
183  }
184 
185  // We handled a pair of complex conjugate eigenvalues, so need to skip them both
186  n--;
187  }
188  }
189 
190  // Back transformation to get eigenvectors of original matrix
191  Vector m_tmp(size);
192  for (Index j = size - 1; j >= 0; j--)
193  {
194  m_tmp.noalias() = m_eivec.leftCols(j + 1) * m_matT.col(j).segment(0, j + 1);
195  m_eivec.col(j) = m_tmp;
196  }
197  }
198 
199 public:
201  m_n(0), m_computed(false)
202  {}
203 
205  m_n(mat.rows()), m_computed(false)
206  {
207  compute(mat);
208  }
209 
211  {
212  using std::abs;
213  using std::sqrt;
214 
215  if (mat.rows() != mat.cols())
216  throw std::invalid_argument("UpperHessenbergEigen: matrix must be square");
217 
218  m_n = mat.rows();
219  // Scale matrix prior to the Schur decomposition
220  const Scalar scale = mat.cwiseAbs().maxCoeff();
221 
222  // Reduce to real Schur form
223  m_schur.compute(mat / scale);
224  m_schur.swap_T(m_matT);
225  m_schur.swap_U(m_eivec);
226 
227  // Compute eigenvalues from matT
229  Index i = 0;
230  while (i < m_n)
231  {
232  // Real eigenvalue
233  if (i == m_n - 1 || m_matT.coeff(i + 1, i) == Scalar(0))
234  {
236  ++i;
237  }
238  else // Complex eigenvalues
239  {
240  Scalar p = Scalar(0.5) * (m_matT.coeff(i, i) - m_matT.coeff(i + 1, i + 1));
241  Scalar z;
242  // Compute z = sqrt(abs(p * p + m_matT.coeff(i+1, i) * m_matT.coeff(i, i+1)));
243  // without overflow
244  {
245  Scalar t0 = m_matT.coeff(i + 1, i);
246  Scalar t1 = m_matT.coeff(i, i + 1);
247  Scalar maxval = (std::max)(abs(p), (std::max)(abs(t0), abs(t1)));
248  t0 /= maxval;
249  t1 /= maxval;
250  Scalar p0 = p / maxval;
251  z = maxval * sqrt(abs(p0 * p0 + t0 * t1));
252  }
253  m_eivalues.coeffRef(i) = Complex(m_matT.coeff(i + 1, i + 1) + p, z);
254  m_eivalues.coeffRef(i + 1) = Complex(m_matT.coeff(i + 1, i + 1) + p, -z);
255  i += 2;
256  }
257  }
258 
259  // Compute eigenvectors
261 
262  // Scale eigenvalues back
263  m_eivalues *= scale;
264 
265  m_computed = true;
266  }
267 
268  const ComplexVector& eigenvalues() const
269  {
270  if (!m_computed)
271  throw std::logic_error("UpperHessenbergEigen: need to call compute() first");
272 
273  return m_eivalues;
274  }
275 
277  {
278  using std::abs;
279 
280  if (!m_computed)
281  throw std::logic_error("UpperHessenbergEigen: need to call compute() first");
282 
283  Index n = m_eivec.cols();
284  ComplexMatrix matV(n, n);
285  for (Index j = 0; j < n; ++j)
286  {
287  // imaginary part of real eigenvalue is already set to exact zero
288  if (Eigen::numext::imag(m_eivalues.coeff(j)) == Scalar(0) || j + 1 == n)
289  {
290  // we have a real eigen value
291  matV.col(j) = m_eivec.col(j).template cast<Complex>();
292  matV.col(j).normalize();
293  }
294  else
295  {
296  // we have a pair of complex eigen values
297  for (Index i = 0; i < n; ++i)
298  {
299  matV.coeffRef(i, j) = Complex(m_eivec.coeff(i, j), m_eivec.coeff(i, j + 1));
300  matV.coeffRef(i, j + 1) = Complex(m_eivec.coeff(i, j), -m_eivec.coeff(i, j + 1));
301  }
302  matV.col(j).normalize();
303  matV.col(j + 1).normalize();
304  ++j;
305  }
306  }
307 
308  return matV;
309  }
310 };
311 
312 } // namespace Spectra
313 
314 #endif // SPECTRA_UPPER_HESSENBERG_EIGEN_H
w
RowVector3d w
Definition: Matrix_resize_int.cpp:3
Spectra::UpperHessenbergEigen::Complex
std::complex< Scalar > Complex
Definition: UpperHessenbergEigen.h:31
Spectra::UpperHessenbergEigen::doComputeEigenvectors
void doComputeEigenvectors()
Definition: UpperHessenbergEigen.h:42
Spectra::UpperHessenbergEigen::Index
Eigen::Index Index
Definition: UpperHessenbergEigen.h:25
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
real
float real
Definition: datatypes.h:10
Spectra::UpperHessenbergEigen::UpperHessenbergEigen
UpperHessenbergEigen()
Definition: UpperHessenbergEigen.h:200
mat
MatrixXf mat
Definition: Tutorial_AdvancedInitialization_CommaTemporary.cpp:1
Eigen::PlainObjectBase::resize
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE void resize(Index rows, Index cols)
Definition: PlainObjectBase.h:271
Spectra::UpperHessenbergEigen::eigenvalues
const ComplexVector & eigenvalues() const
Definition: UpperHessenbergEigen.h:268
Spectra::UpperHessenbergEigen::m_matT
Matrix m_matT
Definition: UpperHessenbergEigen.h:37
Spectra::UpperHessenbergEigen::m_schur
UpperHessenbergSchur< Scalar > m_schur
Definition: UpperHessenbergEigen.h:36
rows
int rows
Definition: Tutorial_commainit_02.cpp:1
size
Scalar Scalar int size
Definition: benchVecAdd.cpp:17
n
int n
Definition: BiCGSTAB_simple.cpp:1
epsilon
static double epsilon
Definition: testRot3.cpp:37
Spectra::UpperHessenbergEigen::eigenvectors
ComplexMatrix eigenvectors()
Definition: UpperHessenbergEigen.h:276
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
j
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2
Eigen::numext::q
EIGEN_DEVICE_FUNC const Scalar & q
Definition: SpecialFunctionsImpl.h:1984
l
static const Line3 l(Rot3(), 1, 1)
Spectra::UpperHessenbergEigen::m_computed
bool m_computed
Definition: UpperHessenbergEigen.h:40
Spectra::UpperHessenbergEigen
Definition: UpperHessenbergEigen.h:22
imag
const EIGEN_DEVICE_FUNC ImagReturnType imag() const
Definition: CommonCwiseUnaryOps.h:109
Eigen::Matrix::coeffRef
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE Scalar & coeffRef(Index rowId, Index colId)
Definition: PlainObjectBase.h:175
pybind_wrapper_test_script.z
z
Definition: pybind_wrapper_test_script.py:61
Spectra::UpperHessenbergEigen::UpperHessenbergEigen
UpperHessenbergEigen(ConstGenericMatrix &mat)
Definition: UpperHessenbergEigen.h:204
y
Scalar * y
Definition: level1_cplx_impl.h:124
Spectra::UpperHessenbergEigen::m_eivalues
ComplexVector m_eivalues
Definition: UpperHessenbergEigen.h:39
Spectra::UpperHessenbergEigen::m_n
Index m_n
Definition: UpperHessenbergEigen.h:35
Eigen::Ref
A matrix or vector expression mapping an existing expression.
Definition: Ref.h:281
Eigen::PlainObjectBase::cols
EIGEN_DEVICE_FUNC EIGEN_STRONG_INLINE EIGEN_CONSTEXPR Index cols() const EIGEN_NOEXCEPT
Definition: PlainObjectBase.h:145
p0
Vector3f p0
Definition: MatrixBase_all.cpp:2
p
float * p
Definition: Tutorial_Map_using.cpp:9
Spectra::UpperHessenbergEigen::m_eivec
Matrix m_eivec
Definition: UpperHessenbergEigen.h:38
Eigen::PlainObjectBase::coeff
EIGEN_DEVICE_FUNC const EIGEN_STRONG_INLINE Scalar & coeff(Index rowId, Index colId) const
Definition: PlainObjectBase.h:152
Spectra::UpperHessenbergEigen::compute
void compute(ConstGenericMatrix &mat)
Definition: UpperHessenbergEigen.h:210
Spectra
Definition: LOBPCGSolver.h:19
Spectra::UpperHessenbergSchur
Definition: UpperHessenbergSchur.h:24
Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic >
abs
#define abs(x)
Definition: datatypes.h:17
align_3::t
Point2 t(10, 10)
UpperHessenbergSchur.h
max
#define max(a, b)
Definition: datatypes.h:20
ceres::sqrt
Jet< T, N > sqrt(const Jet< T, N > &f)
Definition: jet.h:418
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 Sun Feb 16 2025 04:08:29