Eigen  5.0.1
 
Loading...
Searching...
No Matches
TridiagonalEigenSolver.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2026 Rasmus Munk Larsen <rmlarsen@gmail.com>
5//
6// This Source Code Form is subject to the terms of the Mozilla
7// Public License v. 2.0. If a copy of the MPL was not distributed
8// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9// SPDX-License-Identifier: MPL-2.0
10
11#ifndef EIGEN_TRIDIAGONAL_EIGENSOLVER_H
12#define EIGEN_TRIDIAGONAL_EIGENSOLVER_H
13
14#include "./TridiagonalBisection.h"
15#include "./TridiagonalInverseIteration.h"
16
17// IWYU pragma: private
18#include "./InternalHeaderCheck.h"
19
20namespace Eigen {
21
75template <typename Scalar_>
77 public:
79 using Scalar = Scalar_;
80 using RealScalar = Scalar;
81 static_assert(NumTraits<Scalar>::IsComplex == 0 && NumTraits<Scalar>::IsInteger == 0,
82 "TridiagonalEigenSolver requires a real floating-point scalar type; for the eigenvalues of a complex "
83 "Hermitian tridiagonal matrix, pass subdiag.cwiseAbs() as the real sub-diagonal (see the class "
84 "documentation).");
85
90
93
95 explicit TridiagonalEigenSolver(Index size)
96 : m_eivalues(size), m_eivec(size, size), m_diag(size), m_subdiag(size > 1 ? size - 1 : 0) {}
97
102 template <typename DiagType, typename SubdiagType>
104 int options = ComputeEigenvectors, const EigenvalueRange& range = EigenvalueRange::all())
106 compute(diag, subdiag, options, range);
107 }
108
123 template <typename DiagType, typename SubdiagType>
125 int options = ComputeEigenvectors,
126 const EigenvalueRange& range = EigenvalueRange::all()) {
127 eigen_assert((options & ~EigVecMask) == 0 && (options & EigVecMask) != EigVecMask && "invalid option parameter");
128 computeEigenvalues(diag, subdiag, range);
129 if (m_info == Success && (options & ComputeEigenvectors) == ComputeEigenvectors) computeEigenvectors();
130 return *this;
131 }
132
147 template <typename DiagType, typename SubdiagType>
149 const EigenvalueRange& range = EigenvalueRange::all());
150
164 eigen_assert(m_isInitialized && "TridiagonalEigenSolver is not initialized.");
165 eigen_assert(m_info == Success && "computeEigenvectors() requires a preceding successful eigenvalue computation");
166 computeEigenvectorsImpl();
167 return *this;
168 }
169
212 template <typename DiagType, typename SubdiagType, typename EivalsType>
215
224 const VectorType& eigenvalues() const {
225 eigen_assert(m_isInitialized && "TridiagonalEigenSolver is not initialized.");
226 return m_eivalues;
227 }
228
237 const MatrixType& eigenvectors() const {
238 eigen_assert(m_isInitialized && "TridiagonalEigenSolver is not initialized.");
239 eigen_assert(m_eigenvectorsOk && "The eigenvectors have not been computed.");
240 return m_eivec;
241 }
242
248 eigen_assert(m_isInitialized && "TridiagonalEigenSolver is not initialized.");
249 return m_info;
250 }
251
252 protected:
253 // Sturm counts, bisection targets, and inverse-iteration thresholds are carried in the scalar type
254 // itself, so scalars narrower than float (half, bfloat16) compute in float internally: their
255 // epsilon cannot represent consecutive integer counts beyond ~1/eps (a 257-element bfloat16
256 // identity would report an eigenvalue of "5.19"), and intermediates such as the coincident-shift
257 // perturbation quantize away. Results are rounded back to Scalar on output.
258 using ComputeScalar = std::conditional_t<(NumTraits<Scalar>::digits() < NumTraits<float>::digits()), float, Scalar>;
259
260 void computeEigenvectorsImpl();
261
262 VectorType m_eivalues;
263 MatrixType m_eivec;
264 // The tridiagonal retained by the last compute step, so the staged computeEigenvectors() can
265 // re-form T - lambda*I without the caller having to keep diag/subdiag alive.
266 VectorType m_diag;
267 VectorType m_subdiag;
268 // Narrow scalars only: the unrounded float eigenvalues of the last computeEigenvalues(), kept so
269 // the staged eigenvector pass shifts by them rather than by the Scalar-rounded m_eivalues --
270 // shifts quantized to Scalar collapse neighbouring eigenvalues and yield near-parallel vectors.
271 // Empty whenever m_eivalues did not come from this solver's own bisection.
274 bool m_isInitialized = false;
275 bool m_eigenvectorsOk = false;
276};
277
278template <typename Scalar_>
279template <typename DiagType, typename SubdiagType>
280TridiagonalEigenSolver<Scalar_>& TridiagonalEigenSolver<Scalar_>::computeEigenvalues(
281 const MatrixBase<DiagType>& diag, const MatrixBase<SubdiagType>& subdiag, const EigenvalueRange& range) {
282 static_assert(internal::is_same<typename DiagType::Scalar, Scalar>::value &&
283 internal::is_same<typename SubdiagType::Scalar, Scalar>::value,
284 "diag and subdiag must have the solver's scalar type");
285 const Index n = diag.size();
286 EIGEN_UNUSED_VARIABLE(n);
287 eigen_assert(subdiag.size() == (n > 0 ? n - 1 : 0) && "sub-diagonal must have one fewer entry than the diagonal");
288
289 m_diag = diag;
290 m_subdiag = subdiag;
291 m_eigenvectorsOk = false;
292
293 // Reject non-finite input up front (cf. SelfAdjointEigenSolver::computeFromTridiagonal()): the
294 // Gershgorin bracketing below would otherwise iterate on NaN brackets and return garbage with
295 // info() == Success. allFinite() rather than a max-reduction, whose default PropagateFast
296 // semantics may not surface a NaN.
297 if (!(m_diag.allFinite() && m_subdiag.allFinite())) {
298 m_eivalues.resize(0);
299 m_eivaluesc.resize(0);
300 m_info = NoConvergence;
301 m_isInitialized = true;
302 return *this;
303 }
304
305 if (internal::is_same<Scalar, ComputeScalar>::value) {
306 internal::tridiagonal_bisection(m_diag, m_subdiag, range, RealScalar(0), m_eivalues);
307 m_eivaluesc.resize(0);
308 } else {
309 // Narrow scalar: bisect in float and round the eigenvalues back (see ComputeScalar). The
310 // unrounded values are retained for the staged eigenvector pass (see m_eivaluesc).
311 const Matrix<ComputeScalar, Dynamic, 1> cdiag = m_diag.template cast<ComputeScalar>();
312 const Matrix<ComputeScalar, Dynamic, 1> csubdiag = m_subdiag.template cast<ComputeScalar>();
313 internal::tridiagonal_bisection(cdiag, csubdiag, range, ComputeScalar(0), m_eivaluesc);
314 m_eivalues = m_eivaluesc.template cast<Scalar>();
315 }
316 m_info = Success;
317 m_isInitialized = true;
318 return *this;
319}
320
321template <typename Scalar_>
322template <typename DiagType, typename SubdiagType, typename EivalsType>
324 const MatrixBase<DiagType>& diag, const MatrixBase<SubdiagType>& subdiag,
325 const MatrixBase<EivalsType>& eigenvalues) {
326 static_assert(internal::is_same<typename DiagType::Scalar, Scalar>::value &&
327 internal::is_same<typename SubdiagType::Scalar, Scalar>::value &&
328 internal::is_same<typename EivalsType::Scalar, Scalar>::value,
329 "diag, subdiag and eigenvalues must have the solver's scalar type");
330 const Index n = diag.size();
331 EIGEN_UNUSED_VARIABLE(n);
332 eigen_assert(subdiag.size() == (n > 0 ? n - 1 : 0) && "sub-diagonal must have one fewer entry than the diagonal");
333 eigen_assert(eigenvalues.size() <= n && "cannot request more eigenvectors than the size of the matrix");
334
335 m_diag = diag;
336 m_subdiag = subdiag;
337 m_eivalues = eigenvalues;
338 m_eivaluesc.resize(0); // the caller's Scalar eigenvalues are authoritative here
339
340 // Reject non-finite input up front, mirroring computeEigenvalues(): inverse iteration on NaN/Inf
341 // data (or NaN shifts) would return NaN vectors with info() == Success.
342 if (!(m_diag.allFinite() && m_subdiag.allFinite() && m_eivalues.allFinite())) {
343 m_eivalues.resize(0);
344 m_eivec.resize(m_diag.size(), 0);
345 m_info = NoConvergence;
346 m_isInitialized = true;
347 m_eigenvectorsOk = false;
348 return *this;
349 }
350
351 computeEigenvectorsImpl();
352 return *this;
353}
354
355template <typename Scalar_>
356void TridiagonalEigenSolver<Scalar_>::computeEigenvectorsImpl() {
357 const Index n = m_diag.size();
358 const Index m = m_eivalues.size();
359 m_eivec.resize(n, m);
360 Index nonconv;
361 if (internal::is_same<Scalar, ComputeScalar>::value) {
362 nonconv = internal::tridiagonal_inverse_iteration(m_diag, m_subdiag, m_eivalues, m_eivec);
363 // Refine the eigenvectors of any genuinely degenerate cluster (Rayleigh-Ritz). The eigenvalues
364 // are left unchanged, and a non-degenerate spectrum is untouched.
365 internal::tridiagonal_rayleigh_ritz_refine(m_diag, m_subdiag, m_eivalues, m_eivec);
366 } else {
367 // Narrow scalar: iterate and refine in float, then round the vectors back (see ComputeScalar).
368 // Shift by the retained unrounded eigenvalues when they exist (the staged path): shifts rounded
369 // to Scalar collapse neighbouring eigenvalues and produce near-parallel vectors.
370 const Matrix<ComputeScalar, Dynamic, 1> cdiag = m_diag.template cast<ComputeScalar>();
371 const Matrix<ComputeScalar, Dynamic, 1> csubdiag = m_subdiag.template cast<ComputeScalar>();
373 (m_eivaluesc.size() == m) ? m_eivaluesc : m_eivalues.template cast<ComputeScalar>();
375 nonconv = internal::tridiagonal_inverse_iteration(cdiag, csubdiag, cw, cvec);
376 internal::tridiagonal_rayleigh_ritz_refine(cdiag, csubdiag, cw, cvec);
377 m_eivec = cvec.template cast<Scalar>();
378 }
379
380 // Like LAPACK xSTEIN, report NoConvergence if any eigenvector failed the inverse-iteration growth
381 // test (the columns are still returned, best effort, so eigenvectors() remains usable).
382 m_info = (nonconv == 0) ? Success : NoConvergence;
383 m_isInitialized = true;
384 m_eigenvectorsOk = true;
385}
386
387} // namespace Eigen
388
389#endif // EIGEN_TRIDIAGONAL_EIGENSOLVER_H
Base class for all dense matrices, vectors, and expressions.
Definition MatrixBase.h:53
The matrix class, also used for vectors and row-vectors.
Definition Matrix.h:188
Computes eigenvalues and eigenvectors of a real symmetric tridiagonal matrix.
Definition TridiagonalEigenSolver.h:76
Matrix< Scalar, Dynamic, 1 > VectorType
Type for the eigenvalues and the input diagonals: a dynamic-size column vector.
Definition TridiagonalEigenSolver.h:87
TridiagonalEigenSolver & computeEigenvectors(const MatrixBase< DiagType > &diag, const MatrixBase< SubdiagType > &subdiag, const MatrixBase< EivalsType > &eigenvalues)
Computes eigenvectors of a tridiagonal matrix by inverse iteration from known eigenvalues.
TridiagonalEigenSolver & computeEigenvectors()
Computes eigenvectors for the eigenvalues of the preceding compute step, by inverse iteration.
Definition TridiagonalEigenSolver.h:163
ComputationInfo info() const
Reports whether the computation was successful.
Definition TridiagonalEigenSolver.h:247
TridiagonalEigenSolver & compute(const MatrixBase< DiagType > &diag, const MatrixBase< SubdiagType > &subdiag, int options=ComputeEigenvectors, const EigenvalueRange &range=EigenvalueRange::all())
Computes the selected eigenvalues, and optionally eigenvectors, of a real symmetric tridiagonal matri...
Definition TridiagonalEigenSolver.h:124
const MatrixType & eigenvectors() const
Returns the computed eigenvectors, one column per eigenvalue.
Definition TridiagonalEigenSolver.h:237
TridiagonalEigenSolver(Index size)
Constructor pre-allocating room for the full spectrum of a matrix of dimension size.
Definition TridiagonalEigenSolver.h:95
TridiagonalEigenSolver()=default
Default constructor. Call compute() before querying any result.
Matrix< Scalar, Dynamic, Dynamic > MatrixType
Type for the eigenvector matrix: dynamic-size, one column per selected eigenvalue.
Definition TridiagonalEigenSolver.h:89
TridiagonalEigenSolver & computeEigenvalues(const MatrixBase< DiagType > &diag, const MatrixBase< SubdiagType > &subdiag, const EigenvalueRange &range=EigenvalueRange::all())
Computes the selected eigenvalues (only) of a real symmetric tridiagonal matrix.
TridiagonalEigenSolver(const MatrixBase< DiagType > &diag, const MatrixBase< SubdiagType > &subdiag, int options=ComputeEigenvectors, const EigenvalueRange &range=EigenvalueRange::all())
Constructor; computes the eigendecomposition of the given tridiagonal matrix.
Definition TridiagonalEigenSolver.h:103
Scalar_ Scalar
Scalar type of the matrix; must be real.
Definition TridiagonalEigenSolver.h:79
const VectorType & eigenvalues() const
Returns the computed eigenvalues, in non-decreasing order.
Definition TridiagonalEigenSolver.h:224
ComputationInfo
Definition Constants.h:455
@ InvalidInput
Definition Constants.h:464
@ Success
Definition Constants.h:457
@ NoConvergence
Definition Constants.h:461
@ ComputeEigenvectors
Definition Constants.h:406
Selects which eigenvalues to compute in a spectral-bisection solve.
Definition TridiagonalBisection.h:38
static EigenvalueRange all()
Definition TridiagonalBisection.h:50