EigenSolver.h
Go to the documentation of this file.
1 // This file is part of Eigen, a lightweight C++ template library
2 // for linear algebra.
3 //
4 // Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
5 // Copyright (C) 2010,2012 Jitse Niesen <jitse@maths.leeds.ac.uk>
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 http://mozilla.org/MPL/2.0/.
10 
11 #ifndef EIGEN_EIGENSOLVER_H
12 #define EIGEN_EIGENSOLVER_H
13 
14 #include "./RealSchur.h"
15 
16 // IWYU pragma: private
17 #include "./InternalHeaderCheck.h"
18 
19 namespace Eigen {
20 
67 template <typename MatrixType_>
68 class EigenSolver {
69  public:
71  typedef MatrixType_ MatrixType;
72 
73  enum {
74  RowsAtCompileTime = MatrixType::RowsAtCompileTime,
75  ColsAtCompileTime = MatrixType::ColsAtCompileTime,
77  MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
78  MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
79  };
80 
82  typedef typename MatrixType::Scalar Scalar;
84  typedef Eigen::Index Index;
85 
92  typedef std::complex<RealScalar> ComplexScalar;
93 
100 
109 
118  : m_eivec(), m_eivalues(), m_isInitialized(false), m_eigenvectorsOk(false), m_realSchur(), m_matT(), m_tmp() {}
119 
127  : m_eivec(size, size),
128  m_eivalues(size),
129  m_isInitialized(false),
130  m_eigenvectorsOk(false),
131  m_realSchur(size),
132  m_matT(size, size),
133  m_tmp(size) {}
134 
150  template <typename InputType>
151  explicit EigenSolver(const EigenBase<InputType>& matrix, bool computeEigenvectors = true)
152  : m_eivec(matrix.rows(), matrix.cols()),
154  m_isInitialized(false),
155  m_eigenvectorsOk(false),
157  m_matT(matrix.rows(), matrix.cols()),
158  m_tmp(matrix.cols()) {
159  compute(matrix.derived(), computeEigenvectors);
160  }
161 
183 
203  eigen_assert(m_isInitialized && "EigenSolver is not initialized.");
204  eigen_assert(m_eigenvectorsOk && "The eigenvectors have not been computed together with the eigenvalues.");
205  return m_eivec;
206  }
207 
227 
246  const EigenvalueType& eigenvalues() const {
247  eigen_assert(m_isInitialized && "EigenSolver is not initialized.");
248  return m_eivalues;
249  }
250 
278  template <typename InputType>
279  EigenSolver& compute(const EigenBase<InputType>& matrix, bool computeEigenvectors = true);
280 
284  eigen_assert(m_isInitialized && "EigenSolver is not initialized.");
285  return m_info;
286  }
287 
290  m_realSchur.setMaxIterations(maxIters);
291  return *this;
292  }
293 
296 
297  private:
298  void doComputeEigenvectors();
299 
300  protected:
303  EIGEN_STATIC_ASSERT(!NumTraits<Scalar>::IsComplex, NUMERIC_TYPE_MUST_BE_REAL);
304  }
305 
313 
316 };
317 
318 template <typename MatrixType>
320  eigen_assert(m_isInitialized && "EigenSolver is not initialized.");
321  const RealScalar precision = RealScalar(2) * NumTraits<RealScalar>::epsilon();
322  const Index n = m_eivalues.rows();
323  MatrixType matD = MatrixType::Zero(n, n);
324  Index i = 0;
325  for (; i < n - 1; ++i) {
326  RealScalar real = numext::real(m_eivalues.coeff(i));
327  RealScalar imag = numext::imag(m_eivalues.coeff(i));
328  matD.coeffRef(i, i) = real;
329  if (!internal::isMuchSmallerThan(imag, real, precision)) {
330  matD.coeffRef(i, i + 1) = imag;
331  matD.coeffRef(i + 1, i) = -imag;
332  matD.coeffRef(i + 1, i + 1) = real;
333  ++i;
334  }
335  }
336  if (i == n - 1) {
337  matD.coeffRef(i, i) = numext::real(m_eivalues.coeff(i));
338  }
339 
340  return matD;
341 }
342 
343 template <typename MatrixType>
345  eigen_assert(m_isInitialized && "EigenSolver is not initialized.");
346  eigen_assert(m_eigenvectorsOk && "The eigenvectors have not been computed together with the eigenvalues.");
347  const RealScalar precision = RealScalar(2) * NumTraits<RealScalar>::epsilon();
348  Index n = m_eivec.cols();
349  EigenvectorsType matV(n, n);
350  for (Index j = 0; j < n; ++j) {
351  if (internal::isMuchSmallerThan(numext::imag(m_eivalues.coeff(j)), numext::real(m_eivalues.coeff(j)), precision) ||
352  j + 1 == n) {
353  // we have a real eigen value
354  matV.col(j) = m_eivec.col(j).template cast<ComplexScalar>();
355  matV.col(j).normalize();
356  } else {
357  // we have a pair of complex eigen values
358  for (Index i = 0; i < n; ++i) {
359  matV.coeffRef(i, j) = ComplexScalar(m_eivec.coeff(i, j), m_eivec.coeff(i, j + 1));
360  matV.coeffRef(i, j + 1) = ComplexScalar(m_eivec.coeff(i, j), -m_eivec.coeff(i, j + 1));
361  }
362  matV.col(j).normalize();
363  matV.col(j + 1).normalize();
364  ++j;
365  }
366  }
367  return matV;
368 }
369 
370 template <typename MatrixType>
371 template <typename InputType>
373  bool computeEigenvectors) {
374  check_template_parameters();
375 
376  using numext::isfinite;
377  using std::abs;
378  using std::sqrt;
379  eigen_assert(matrix.cols() == matrix.rows());
380 
381  // Reduce to real Schur form.
382  m_realSchur.compute(matrix.derived(), computeEigenvectors);
383 
384  m_info = m_realSchur.info();
385 
386  if (m_info == Success) {
387  m_matT = m_realSchur.matrixT();
388  if (computeEigenvectors) m_eivec = m_realSchur.matrixU();
389 
390  // Compute eigenvalues from matT
391  m_eivalues.resize(matrix.cols());
392  Index i = 0;
393  while (i < matrix.cols()) {
394  if (i == matrix.cols() - 1 || m_matT.coeff(i + 1, i) == Scalar(0)) {
395  m_eivalues.coeffRef(i) = m_matT.coeff(i, i);
396  if (!(isfinite)(m_eivalues.coeffRef(i))) {
397  m_isInitialized = true;
398  m_eigenvectorsOk = false;
399  m_info = NumericalIssue;
400  return *this;
401  }
402  ++i;
403  } else {
404  Scalar p = Scalar(0.5) * (m_matT.coeff(i, i) - m_matT.coeff(i + 1, i + 1));
405  Scalar z;
406  // Compute z = sqrt(abs(p * p + m_matT.coeff(i+1, i) * m_matT.coeff(i, i+1)));
407  // without overflow
408  {
409  Scalar t0 = m_matT.coeff(i + 1, i);
410  Scalar t1 = m_matT.coeff(i, i + 1);
411  Scalar maxval = numext::maxi<Scalar>(abs(p), numext::maxi<Scalar>(abs(t0), abs(t1)));
412  t0 /= maxval;
413  t1 /= maxval;
414  Scalar p0 = p / maxval;
415  z = maxval * sqrt(abs(p0 * p0 + t0 * t1));
416  }
417 
418  m_eivalues.coeffRef(i) = ComplexScalar(m_matT.coeff(i + 1, i + 1) + p, z);
419  m_eivalues.coeffRef(i + 1) = ComplexScalar(m_matT.coeff(i + 1, i + 1) + p, -z);
420  if (!((isfinite)(m_eivalues.coeffRef(i)) && (isfinite)(m_eivalues.coeffRef(i + 1)))) {
421  m_isInitialized = true;
422  m_eigenvectorsOk = false;
423  m_info = NumericalIssue;
424  return *this;
425  }
426  i += 2;
427  }
428  }
429 
430  // Compute eigenvectors.
431  if (computeEigenvectors) doComputeEigenvectors();
432  }
433 
434  m_isInitialized = true;
435  m_eigenvectorsOk = computeEigenvectors;
436 
437  return *this;
438 }
439 
440 template <typename MatrixType>
442  using std::abs;
443  const Index size = m_eivec.cols();
445 
446  // inefficient! this is already computed in RealSchur
447  Scalar norm(0);
448  for (Index j = 0; j < size; ++j) {
449  norm += m_matT.row(j).segment((std::max)(j - 1, Index(0)), size - (std::max)(j - 1, Index(0))).cwiseAbs().sum();
450  }
451 
452  // Backsubstitute to find vectors of upper triangular form
453  if (norm == Scalar(0)) {
454  return;
455  }
456 
457  for (Index n = size - 1; n >= 0; n--) {
458  Scalar p = m_eivalues.coeff(n).real();
459  Scalar q = m_eivalues.coeff(n).imag();
460 
461  // Scalar vector
462  if (q == Scalar(0)) {
463  Scalar lastr(0), lastw(0);
464  Index l = n;
465 
466  m_matT.coeffRef(n, n) = Scalar(1);
467  for (Index i = n - 1; i >= 0; i--) {
468  Scalar w = m_matT.coeff(i, i) - p;
469  Scalar r = m_matT.row(i).segment(l, n - l + 1).dot(m_matT.col(n).segment(l, n - l + 1));
470 
471  if (m_eivalues.coeff(i).imag() < Scalar(0)) {
472  lastw = w;
473  lastr = r;
474  } else {
475  l = i;
476  if (m_eivalues.coeff(i).imag() == Scalar(0)) {
477  if (w != Scalar(0))
478  m_matT.coeffRef(i, n) = -r / w;
479  else
480  m_matT.coeffRef(i, n) = -r / (eps * norm);
481  } else // Solve real equations
482  {
483  Scalar x = m_matT.coeff(i, i + 1);
484  Scalar y = m_matT.coeff(i + 1, i);
485  Scalar denom = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) +
486  m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag();
487  Scalar t = (x * lastr - lastw * r) / denom;
488  m_matT.coeffRef(i, n) = t;
489  if (abs(x) > abs(lastw))
490  m_matT.coeffRef(i + 1, n) = (-r - w * t) / x;
491  else
492  m_matT.coeffRef(i + 1, n) = (-lastr - y * t) / lastw;
493  }
494 
495  // Overflow control
496  Scalar t = abs(m_matT.coeff(i, n));
497  if ((eps * t) * t > Scalar(1)) m_matT.col(n).tail(size - i) /= t;
498  }
499  }
500  } else if (q < Scalar(0) && n > 0) // Complex vector
501  {
502  Scalar lastra(0), lastsa(0), lastw(0);
503  Index l = n - 1;
504 
505  // Last vector component imaginary so matrix is triangular
506  if (abs(m_matT.coeff(n, n - 1)) > abs(m_matT.coeff(n - 1, n))) {
507  m_matT.coeffRef(n - 1, n - 1) = q / m_matT.coeff(n, n - 1);
508  m_matT.coeffRef(n - 1, n) = -(m_matT.coeff(n, n) - p) / m_matT.coeff(n, n - 1);
509  } else {
510  ComplexScalar cc =
511  ComplexScalar(Scalar(0), -m_matT.coeff(n - 1, n)) / ComplexScalar(m_matT.coeff(n - 1, n - 1) - p, q);
512  m_matT.coeffRef(n - 1, n - 1) = numext::real(cc);
513  m_matT.coeffRef(n - 1, n) = numext::imag(cc);
514  }
515  m_matT.coeffRef(n, n - 1) = Scalar(0);
516  m_matT.coeffRef(n, n) = Scalar(1);
517  for (Index i = n - 2; i >= 0; i--) {
518  Scalar ra = m_matT.row(i).segment(l, n - l + 1).dot(m_matT.col(n - 1).segment(l, n - l + 1));
519  Scalar sa = m_matT.row(i).segment(l, n - l + 1).dot(m_matT.col(n).segment(l, n - l + 1));
520  Scalar w = m_matT.coeff(i, i) - p;
521 
522  if (m_eivalues.coeff(i).imag() < Scalar(0)) {
523  lastw = w;
524  lastra = ra;
525  lastsa = sa;
526  } else {
527  l = i;
528  if (m_eivalues.coeff(i).imag() == RealScalar(0)) {
529  ComplexScalar cc = ComplexScalar(-ra, -sa) / ComplexScalar(w, q);
530  m_matT.coeffRef(i, n - 1) = numext::real(cc);
531  m_matT.coeffRef(i, n) = numext::imag(cc);
532  } else {
533  // Solve complex equations
534  Scalar x = m_matT.coeff(i, i + 1);
535  Scalar y = m_matT.coeff(i + 1, i);
536  Scalar vr = (m_eivalues.coeff(i).real() - p) * (m_eivalues.coeff(i).real() - p) +
537  m_eivalues.coeff(i).imag() * m_eivalues.coeff(i).imag() - q * q;
538  Scalar vi = (m_eivalues.coeff(i).real() - p) * Scalar(2) * q;
539  if ((vr == Scalar(0)) && (vi == Scalar(0)))
540  vr = eps * norm * (abs(w) + abs(q) + abs(x) + abs(y) + abs(lastw));
541 
542  ComplexScalar cc = ComplexScalar(x * lastra - lastw * ra + q * sa, x * lastsa - lastw * sa - q * ra) /
543  ComplexScalar(vr, vi);
544  m_matT.coeffRef(i, n - 1) = numext::real(cc);
545  m_matT.coeffRef(i, n) = numext::imag(cc);
546  if (abs(x) > (abs(lastw) + abs(q))) {
547  m_matT.coeffRef(i + 1, n - 1) = (-ra - w * m_matT.coeff(i, n - 1) + q * m_matT.coeff(i, n)) / x;
548  m_matT.coeffRef(i + 1, n) = (-sa - w * m_matT.coeff(i, n) - q * m_matT.coeff(i, n - 1)) / x;
549  } else {
550  cc = ComplexScalar(-lastra - y * m_matT.coeff(i, n - 1), -lastsa - y * m_matT.coeff(i, n)) /
551  ComplexScalar(lastw, q);
552  m_matT.coeffRef(i + 1, n - 1) = numext::real(cc);
553  m_matT.coeffRef(i + 1, n) = numext::imag(cc);
554  }
555  }
556 
557  // Overflow control
558  Scalar t = numext::maxi<Scalar>(abs(m_matT.coeff(i, n - 1)), abs(m_matT.coeff(i, n)));
559  if ((eps * t) * t > Scalar(1)) m_matT.block(i, n - 1, size - i, 2) /= t;
560  }
561  }
562 
563  // We handled a pair of complex conjugate eigenvalues, so need to skip them both
564  n--;
565  } else {
566  eigen_assert(0 && "Internal bug in EigenSolver (INF or NaN has not been detected)"); // this should not happen
567  }
568  }
569 
570  // Back transformation to get eigenvectors of original matrix
571  for (Index j = size - 1; j >= 0; j--) {
572  m_tmp.noalias() = m_eivec.leftCols(j + 1) * m_matT.col(j).segment(0, j + 1);
573  m_eivec.col(j) = m_tmp;
574  }
575 }
576 
577 } // end namespace Eigen
578 
579 #endif // EIGEN_EIGENSOLVER_H
AnnoyingScalar abs(const AnnoyingScalar &x)
Definition: AnnoyingScalar.h:135
AnnoyingScalar imag(const AnnoyingScalar &)
Definition: AnnoyingScalar.h:132
AnnoyingScalar sqrt(const AnnoyingScalar &x)
Definition: AnnoyingScalar.h:134
int i
Definition: BiCGSTAB_step_by_step.cpp:9
const unsigned n
Definition: CG3DPackingUnitTest.cpp:11
#define eigen_assert(x)
Definition: Macros.h:910
Vector3f p0
Definition: MatrixBase_all.cpp:2
RowVector3d w
Definition: Matrix_resize_int.cpp:3
#define EIGEN_STATIC_ASSERT(X, MSG)
Definition: StaticAssert.h:26
#define EIGEN_STATIC_ASSERT_NON_INTEGER(TYPE)
Definition: StaticAssert.h:74
float * p
Definition: Tutorial_Map_using.cpp:9
int rows
Definition: Tutorial_commainit_02.cpp:1
int cols
Definition: Tutorial_commainit_02.cpp:1
Scalar Scalar int size
Definition: benchVecAdd.cpp:17
SCALAR Scalar
Definition: bench_gemm.cpp:45
NumTraits< Scalar >::Real RealScalar
Definition: bench_gemm.cpp:46
MatrixXf MatrixType
Definition: benchmark-blocking-sizes.cpp:52
Computes eigenvalues and eigenvectors of general matrices.
Definition: EigenSolver.h:68
MatrixType::Scalar Scalar
Scalar type for matrices of type MatrixType.
Definition: EigenSolver.h:82
EigenSolver(const EigenBase< InputType > &matrix, bool computeEigenvectors=true)
Constructor; computes eigendecomposition of given matrix.
Definition: EigenSolver.h:151
const EigenvalueType & eigenvalues() const
Returns the eigenvalues of given matrix.
Definition: EigenSolver.h:246
MatrixType m_eivec
Definition: EigenSolver.h:306
void doComputeEigenvectors()
Definition: EigenSolver.h:441
EigenSolver(Index size)
Default constructor with memory preallocation.
Definition: EigenSolver.h:126
Matrix< Scalar, ColsAtCompileTime, 1, Options &~RowMajor, MaxColsAtCompileTime, 1 > ColumnVectorType
Definition: EigenSolver.h:314
MatrixType pseudoEigenvalueMatrix() const
Returns the block-diagonal matrix in the pseudo-eigendecomposition.
Definition: EigenSolver.h:319
RealSchur< MatrixType > m_realSchur
Definition: EigenSolver.h:311
ComputationInfo info() const
Definition: EigenSolver.h:283
EigenSolver & setMaxIterations(Index maxIters)
Sets the maximum number of iterations allowed.
Definition: EigenSolver.h:289
EigenvectorsType eigenvectors() const
Returns the eigenvectors of given matrix.
Definition: EigenSolver.h:344
std::complex< RealScalar > ComplexScalar
Complex scalar type for MatrixType.
Definition: EigenSolver.h:92
bool m_eigenvectorsOk
Definition: EigenSolver.h:309
EigenSolver()
Default constructor.
Definition: EigenSolver.h:117
Matrix< ComplexScalar, ColsAtCompileTime, 1, Options &~RowMajor, MaxColsAtCompileTime, 1 > EigenvalueType
Type for vector of eigenvalues as returned by eigenvalues().
Definition: EigenSolver.h:99
NumTraits< Scalar >::Real RealScalar
Definition: EigenSolver.h:83
static void check_template_parameters()
Definition: EigenSolver.h:301
Eigen::Index Index
Definition: EigenSolver.h:84
bool m_isInitialized
Definition: EigenSolver.h:308
EigenSolver & compute(const EigenBase< InputType > &matrix, bool computeEigenvectors=true)
Computes eigendecomposition of given matrix.
MatrixType m_matT
Definition: EigenSolver.h:312
@ MaxRowsAtCompileTime
Definition: EigenSolver.h:77
@ MaxColsAtCompileTime
Definition: EigenSolver.h:78
@ ColsAtCompileTime
Definition: EigenSolver.h:75
@ Options
Definition: EigenSolver.h:76
@ RowsAtCompileTime
Definition: EigenSolver.h:74
const MatrixType & pseudoEigenvectors() const
Returns the pseudo-eigenvectors of given matrix.
Definition: EigenSolver.h:202
Index getMaxIterations()
Returns the maximum number of iterations.
Definition: EigenSolver.h:295
Matrix< ComplexScalar, RowsAtCompileTime, ColsAtCompileTime, Options, MaxRowsAtCompileTime, MaxColsAtCompileTime > EigenvectorsType
Type for matrix of eigenvectors as returned by eigenvectors().
Definition: EigenSolver.h:108
MatrixType_ MatrixType
Synonym for the template parameter MatrixType_.
Definition: EigenSolver.h:71
ComputationInfo m_info
Definition: EigenSolver.h:310
EigenvalueType m_eivalues
Definition: EigenSolver.h:307
ColumnVectorType m_tmp
Definition: EigenSolver.h:315
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE Scalar & coeffRef(Index rowId, Index colId)
Definition: PlainObjectBase.h:217
RealSchur & setMaxIterations(Index maxIters)
Sets the maximum number of iterations allowed.
Definition: RealSchur.h:204
Index getMaxIterations()
Returns the maximum number of iterations.
Definition: RealSchur.h:210
Eigen::Map< Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor >, 0, Eigen::OuterStride<> > matrix(T *data, int rows, int cols, int stride)
Definition: common.h:85
float real
Definition: datatypes.h:10
#define max(a, b)
Definition: datatypes.h:23
ComputationInfo
Definition: Constants.h:438
@ NumericalIssue
Definition: Constants.h:442
@ Success
Definition: Constants.h:440
Scalar * y
Definition: level1_cplx_impl.h:128
#define isfinite(X)
Definition: main.h:111
double eps
Definition: crbond_bessel.cc:24
EIGEN_DEVICE_FUNC bool isMuchSmallerThan(const Scalar &x, const OtherScalar &y, const typename NumTraits< Scalar >::Real &precision=NumTraits< Scalar >::dummy_precision())
Definition: MathFunctions.h:1916
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE bool() isfinite(const Eigen::bfloat16 &h)
Definition: BFloat16.h:752
EIGEN_DEVICE_FUNC const Scalar & q
Definition: SpecialFunctionsImpl.h:2019
Namespace containing all symbols from the Eigen library.
Definition: bench_norm.cpp:70
const AutoDiffScalar< DerType > & real(const AutoDiffScalar< DerType > &x)
Definition: AutoDiffScalar.h:486
DerType::Scalar imag(const AutoDiffScalar< DerType > &)
Definition: AutoDiffScalar.h:490
EIGEN_DEFAULT_DENSE_INDEX_TYPE Index
The Index type as used for the API.
Definition: Meta.h:83
r
Definition: UniformPSDSelfTest.py:20
double Zero
Definition: pseudosolid_node_update_elements.cc:35
double epsilon
Definition: osc_ring_sarah_asymptotics.h:43
list x
Definition: plotDoE.py:28
t
Definition: plotPSD.py:36
Definition: EigenBase.h:33
Holds information about the various numeric (i.e. scalar) types allowed by Eigen.
Definition: NumTraits.h:217
Definition: ForwardDeclarations.h:21
Definition: main.h:116
Definition: main.h:115
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2