12#ifndef EIGEN_REAL_SCHUR_H
13#define EIGEN_REAL_SCHUR_H
15#include "./HessenbergDecomposition.h"
18#include "./InternalHeaderCheck.h"
22template <
typename MatrixType_>
61template <
typename MatrixType_>
64 using MatrixType = MatrixType_;
66 RowsAtCompileTime = MatrixType::RowsAtCompileTime,
67 ColsAtCompileTime = MatrixType::ColsAtCompileTime,
68 Options = internal::plain_object_options<MatrixType>::value,
69 MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
70 MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
72 using Scalar =
typename MatrixType::Scalar;
73 using ComplexScalar = internal::make_complex_t<Scalar>;
95 explicit RealSchur(
Index size = RowsAtCompileTime == Dynamic ? 1 : RowsAtCompileTime)
98 m_workspaceVector(size),
99 m_isInitialized(false),
100 m_matUisUptodate(false),
102 if (size > 1) m_hCoeffs.resize(size - 1);
115 template <
typename InputType>
117 : m_matT(matrix.rows(), matrix.cols()),
118 m_matU(matrix.rows(), matrix.cols()),
119 m_workspaceVector(matrix.rows()),
120 m_isInitialized(false),
121 m_matUisUptodate(false),
143 template <typename InputType, bool IsRef = internal::is_ref<MatrixType>::value, std::enable_if_t<IsRef, int> = 0>
145 : m_matT(matrix.derived()),
146 m_matU(matrix.rows(), matrix.cols()),
147 m_workspaceVector(matrix.rows()),
148 m_isInitialized(false),
149 m_matUisUptodate(false),
166 eigen_assert(m_isInitialized &&
"RealSchur is not initialized.");
167 eigen_assert(m_matUisUptodate &&
"The matrix U has not been computed during the RealSchur decomposition.");
182 eigen_assert(m_isInitialized &&
"RealSchur is not initialized.");
210 template <
typename InputType>
235 template <
typename HessMatrixType,
typename OrthMatrixType>
242 eigen_assert(m_isInitialized &&
"RealSchur is not initialized.");
252 m_maxIters = maxIters;
270 using CoeffVectorType =
271 Matrix<Scalar, RowsAtCompileTime == Dynamic ? Dynamic : RowsAtCompileTime - 1, 1, Options &
~RowMajor,
272 MaxRowsAtCompileTime == Dynamic ? Dynamic : MaxRowsAtCompileTime - 1, 1>;
276 ColumnVectorType m_workspaceVector;
277 CoeffVectorType m_hCoeffs;
279 bool m_isInitialized;
280 bool m_matUisUptodate;
283 template <
typename TMatrix>
284 EIGEN_DONT_INLINE
RealSchur& computeFromHessenbergInPlace(TMatrix& matT,
bool computeU);
289 bool usePaddedWorkspace(
Index size)
const;
290 template <
typename TMatrix>
291 RealSchur& computeInPlace(TMatrix& matT,
bool computeU);
292 template <
typename TMatrix>
293 Scalar computeNormOfT(TMatrix& matT);
294 template <
typename TMatrix>
295 Index findSmallSubdiagEntry(TMatrix& matT,
Index iu,
const Scalar& considerAsZero);
296 template <
typename TMatrix>
297 void splitOffTwoRows(TMatrix& matT,
Index iu,
bool computeU,
const Scalar& exshift);
298 template <
typename TMatrix>
299 void computeShift(TMatrix& matT,
Index iu,
Index iter, Scalar& exshift, Vector3s& shiftInfo);
300 template <
typename TMatrix>
301 void initFrancisQRStep(TMatrix& matT,
Index il,
Index iu,
const Vector3s& shiftInfo,
Index& im,
302 Vector3s& firstHouseholderVector);
303 template <
typename TMatrix>
304 void performFrancisQRStep(TMatrix& matT,
Index il,
Index im,
Index iu,
bool computeU,
305 const Vector3s& firstHouseholderVector, Scalar* workspace);
308template <
typename MatrixType>
309template <
typename InputType>
311 eigen_assert(matrix.
cols() == matrix.
rows());
312 const Index size = matrix.
rows();
313 if (usePaddedWorkspace(size)) {
314 WorkspaceMatrix storage(size + (MatrixType::IsRowMajor ? 0 : 1), size + (MatrixType::IsRowMajor ? 1 : 0));
315 auto matT = storage.topLeftCorner(size, size);
317 computeInPlace(matT, computeU);
319 if (!internal::is_same_dense(m_matT, matrix.
derived())) m_matT = matrix.
derived();
320 computeInPlace(m_matT, computeU);
325template <
typename MatrixType>
326bool RealSchur<MatrixType>::usePaddedWorkspace(Index size)
const {
329 const Index stride = internal::is_ref<MatrixType>::value ? m_matT.outerStride() : size;
330 bool usePadding = size >= 128 && (stride *
sizeof(Scalar)) % 1024 == 0;
331#ifdef EIGEN_NO_MALLOC
333#elif defined(EIGEN_RUNTIME_NO_MALLOC)
334 usePadding = usePadding && internal::is_malloc_allowed() && internal::is_free_allowed();
340template <
typename MatrixType>
341template <
typename TMatrix>
343 const Scalar considerAsZero = (std::numeric_limits<Scalar>::min)();
344 const Index n = matT.rows();
345 eigen_assert(matT.cols() == n);
347 const Scalar maxCoeff = n == 0 ? Scalar(0) : matT.cwiseAbs().template maxCoeff<
PropagateNaN>();
348 if (!(numext::isfinite)(maxCoeff)) {
349 if (!internal::is_same_dense(m_matT, matT)) m_matT = matT;
351 m_isInitialized =
true;
352 m_matUisUptodate =
false;
355 if (maxCoeff < considerAsZero) {
359 if (computeU) m_matU.setIdentity(n, n);
361 m_isInitialized =
true;
362 m_matUisUptodate = computeU;
365 const auto factors = internal::safe_scaling<Scalar>::scale_in_place(matT, maxCoeff);
368 internal::hessenberg_decomposition_inplace(matT, m_hCoeffs, m_workspaceVector, m_matU, computeU);
371 computeFromHessenbergInPlace(matT, computeU);
374 if (internal::is_same_dense(m_matT, matT))
375 internal::safe_scaling<Scalar>::unscale_in_place(matT, maxCoeff, factors);
377 internal::safe_scaling<Scalar>::unscale_to(m_matT, matT, maxCoeff, factors);
381template <
typename MatrixType>
382template <
typename HessMatrixType,
typename OrthMatrixType>
384 const OrthMatrixType& matrixQ,
bool computeU) {
385 const Index size = matrixH.rows();
386 m_workspaceVector.resize(size);
387 if (usePaddedWorkspace(size)) {
388 WorkspaceMatrix storage(size + (MatrixType::IsRowMajor ? 0 : 1), size + (MatrixType::IsRowMajor ? 1 : 0));
389 auto matT = storage.topLeftCorner(size, size);
391 if (computeU && !internal::is_same_dense(m_matU, matrixQ)) m_matU = matrixQ;
392 computeFromHessenbergInPlace(matT, computeU);
395 if (!internal::is_same_dense(m_matT, matrixH)) m_matT = matrixH;
396 if (computeU && !internal::is_same_dense(m_matU, matrixQ)) m_matU = matrixQ;
397 computeFromHessenbergInPlace(m_matT, computeU);
402template <
typename MatrixType>
403template <
typename TMatrix>
404RealSchur<MatrixType>& RealSchur<MatrixType>::computeFromHessenbergInPlace(TMatrix& matT,
bool computeU) {
405 Index maxIters = m_maxIters;
406 if (maxIters == -1) maxIters = m_maxIterationsPerRow * matT.rows();
407 Scalar* workspace = &m_workspaceVector.coeffRef(0);
413 Index iu = matT.cols() - 1;
417 Scalar norm = computeNormOfT(matT);
420 Scalar considerAsZero =
421 numext::maxi<Scalar>(norm * numext::abs2(NumTraits<Scalar>::epsilon()), (std::numeric_limits<Scalar>::min)());
423 if (!numext::is_exactly_zero(norm)) {
425 Index il = findSmallSubdiagEntry(matT, iu, considerAsZero);
430 matT.coeffRef(iu, iu) = matT.coeff(iu, iu) + exshift;
431 if (iu > 0) matT.coeffRef(iu, iu - 1) = Scalar(0);
434 }
else if (il == iu - 1)
436 splitOffTwoRows(matT, iu, computeU, exshift);
443 Vector3s firstHouseholderVector = Vector3s::Zero(), shiftInfo;
444 computeShift(matT, iu, iter, exshift, shiftInfo);
446 totalIter = totalIter + 1;
447 if (totalIter > maxIters)
break;
449 initFrancisQRStep(matT, il, iu, shiftInfo, im, firstHouseholderVector);
450 performFrancisQRStep(matT, il, im, iu, computeU, firstHouseholderVector, workspace);
454 if (totalIter <= maxIters)
459 m_isInitialized =
true;
460 m_matUisUptodate = computeU;
465template <
typename MatrixType>
466template <
typename TMatrix>
467inline typename MatrixType::Scalar RealSchur<MatrixType>::computeNormOfT(TMatrix& matT) {
468 return internal::hessenberg_abs_sum<Upper>(matT);
472template <
typename MatrixType>
473template <
typename TMatrix>
474inline Index RealSchur<MatrixType>::findSmallSubdiagEntry(TMatrix& matT, Index iu,
const Scalar& considerAsZero) {
478 Scalar s = abs(matT.coeff(res - 1, res - 1)) + abs(matT.coeff(res, res));
480 s = numext::maxi<Scalar>(s * NumTraits<Scalar>::epsilon(), considerAsZero);
482 if (abs(matT.coeff(res, res - 1)) <= s)
break;
489template <
typename MatrixType>
490template <
typename TMatrix>
491inline void RealSchur<MatrixType>::splitOffTwoRows(TMatrix& matT, Index iu,
bool computeU,
const Scalar& exshift) {
494 const Index size = matT.cols();
498 Scalar p = Scalar(0.5) * (matT.coeff(iu - 1, iu - 1) - matT.coeff(iu, iu));
499 Scalar q = p * p + matT.coeff(iu, iu - 1) * matT.coeff(iu - 1, iu);
500 matT.coeffRef(iu, iu) += exshift;
501 matT.coeffRef(iu - 1, iu - 1) += exshift;
505 Scalar z = sqrt(abs(q));
508 rot.
makeGivens(p + z, matT.coeff(iu, iu - 1));
510 rot.makeGivens(p - z, matT.coeff(iu, iu - 1));
512 matT.rightCols(size - iu + 1).applyOnTheLeft(iu - 1, iu, rot.adjoint());
513 matT.topRows(iu + 1).applyOnTheRight(iu - 1, iu, rot);
514 matT.coeffRef(iu, iu - 1) = Scalar(0);
515 if (computeU) m_matU.applyOnTheRight(iu - 1, iu, rot);
518 if (iu > 1) matT.coeffRef(iu - 1, iu - 2) = Scalar(0);
522template <
typename MatrixType>
523template <
typename TMatrix>
524inline void RealSchur<MatrixType>::computeShift(TMatrix& matT, Index iu, Index iter, Scalar& exshift,
525 Vector3s& shiftInfo) {
528 shiftInfo.coeffRef(0) = matT.coeff(iu, iu);
529 shiftInfo.coeffRef(1) = matT.coeff(iu - 1, iu - 1);
530 shiftInfo.coeffRef(2) = matT.coeff(iu, iu - 1) * matT.coeff(iu - 1, iu);
533 if (iter > 0 && iter % 16 == 0) {
535 if (iter % 32 != 0) {
536 exshift += shiftInfo.coeff(0);
537 matT.diagonal().head(iu + 1).array() -= shiftInfo.coeff(0);
538 Scalar s = abs(matT.coeff(iu, iu - 1)) + abs(matT.coeff(iu - 1, iu - 2));
539 shiftInfo.coeffRef(0) = Scalar(0.75) * s;
540 shiftInfo.coeffRef(1) = Scalar(0.75) * s;
541 shiftInfo.coeffRef(2) = Scalar(-0.4375) * s * s;
544 Scalar s = (shiftInfo.coeff(1) - shiftInfo.coeff(0)) / Scalar(2.0);
545 s = s * s + shiftInfo.coeff(2);
548 if (shiftInfo.coeff(1) < shiftInfo.coeff(0)) s = -s;
549 s = s + (shiftInfo.coeff(1) - shiftInfo.coeff(0)) / Scalar(2.0);
550 s = shiftInfo.coeff(0) - shiftInfo.coeff(2) / s;
552 matT.diagonal().head(iu + 1).array() -= s;
553 shiftInfo.setConstant(Scalar(0.964));
560template <
typename MatrixType>
561template <
typename TMatrix>
562inline void RealSchur<MatrixType>::initFrancisQRStep(TMatrix& matT, Index il, Index iu,
const Vector3s& shiftInfo,
563 Index& im, Vector3s& firstHouseholderVector) {
565 Vector3s& v = firstHouseholderVector;
567 for (im = iu - 2; im >= il; --im) {
568 const Scalar Tmm = matT.coeff(im, im);
569 const Scalar r = shiftInfo.coeff(0) - Tmm;
570 const Scalar s = shiftInfo.coeff(1) - Tmm;
571 v.coeffRef(0) = (r * s - shiftInfo.coeff(2)) / matT.coeff(im + 1, im) + matT.coeff(im, im + 1);
572 v.coeffRef(1) = matT.coeff(im + 1, im + 1) - Tmm - r - s;
573 v.coeffRef(2) = matT.coeff(im + 2, im + 1);
577 const Scalar lhs = matT.coeff(im, im - 1) * (abs(v.coeff(1)) + abs(v.coeff(2)));
578 const Scalar rhs = v.coeff(0) * (abs(matT.coeff(im - 1, im - 1)) + abs(Tmm) + abs(matT.coeff(im + 1, im + 1)));
579 if (abs(lhs) < NumTraits<Scalar>::epsilon() * rhs)
break;
584template <
typename MatrixType>
585template <
typename TMatrix>
586inline void RealSchur<MatrixType>::performFrancisQRStep(TMatrix& matT, Index il, Index im, Index iu,
bool computeU,
587 const Vector3s& firstHouseholderVector, Scalar* workspace) {
588 eigen_assert(im >= il);
589 eigen_assert(im <= iu - 2);
591 const Index size = matT.cols();
597 constexpr Index WindowSize = 32;
598 constexpr bool deferLeft = !TMatrix::IsRowMajor && int(TMatrix::InnerStrideAtCompileTime) == 1;
599 Index windowK[WindowSize];
600 Scalar windowTau[WindowSize];
603 for (Index k0 = im; k0 <= iu - 2; k0 += WindowSize) {
604 const Index k1 = (std::min)(k0 + WindowSize, iu - 1);
605 const Index nearEnd = deferLeft ? (std::min)(size, k1 + 2) : size;
606 Index numDeferred = 0;
607 for (Index k = k0; k < k1; ++k) {
608 bool firstIteration = (k == im);
612 v = firstHouseholderVector;
614 v = matT.template block<3, 1>(k, k - 1);
618 v.makeHouseholder(ess, tau, beta);
620 if (!numext::is_exactly_zero(beta))
622 if (firstIteration && k > il)
623 matT.coeffRef(k, k - 1) = -matT.coeff(k, k - 1);
624 else if (!firstIteration)
625 matT.coeffRef(k, k - 1) = beta;
628 matT.block(k, k, 3, nearEnd - k).applyHouseholderOnTheLeft(ess, tau, workspace);
629 matT.block(0, k, (std::min)(iu, k + 3) + 1, 3).applyHouseholderOnTheRight(ess, tau, workspace);
630 if (computeU) m_matU.block(0, k, size, 3).applyHouseholderOnTheRight(ess, tau, workspace);
633 if (nearEnd < size && !numext::is_exactly_zero(tau)) {
634 windowK[numDeferred] = k;
635 windowTau[numDeferred] = tau;
636 windowEss[numDeferred] = ess;
643 constexpr Index ColumnGroup = 16;
644 for (Index j0 = nearEnd; j0 < size && numDeferred > 0; j0 += ColumnGroup) {
645 const Index j1 = (std::min)(j0 + ColumnGroup, size);
646 for (Index r = 0; r < numDeferred; ++r) {
647 const Index k = windowK[r];
648 const Scalar e0 = windowEss[r].coeff(0), e1 = windowEss[r].coeff(1), tau = windowTau[r];
649 for (Index j = j0; j < j1; ++j) {
651 Scalar* x = &matT.coeffRef(k, j);
652 const Scalar tmp = tau * ((e0 * x[1] + e1 * x[2]) + x[0]);
664 v.makeHouseholder(ess, tau, beta);
666 if (!numext::is_exactly_zero(beta))
668 matT.coeffRef(iu - 1, iu - 2) = beta;
669 matT.block(iu - 1, iu - 1, 2, size - iu + 1).applyHouseholderOnTheLeft(ess, tau, workspace);
670 matT.block(0, iu - 1, iu + 1, 2).applyHouseholderOnTheRight(ess, tau, workspace);
671 if (computeU) m_matU.block(0, iu - 1, size, 2).applyHouseholderOnTheRight(ess, tau, workspace);
675 for (Index i = im + 2; i <= iu; ++i) {
676 matT.coeffRef(i, i - 2) = Scalar(0);
677 if (i > im + 2) matT.coeffRef(i, i - 3) = Scalar(0);
Computes eigenvalues and eigenvectors of general matrices.
Definition EigenSolver.h:69
Rotation given by a cosine-sine pair.
Definition Jacobi.h:39
void makeGivens(const Scalar &p, const Scalar &q, Scalar *r=0)
Definition Jacobi.h:165
The matrix class, also used for vectors and row-vectors.
Definition Matrix.h:188
Performs a real Schur decomposition of a square matrix.
Definition RealSchur.h:62
Matrix< Scalar, RowsAtCompileTime, ColsAtCompileTime, Options, MaxRowsAtCompileTime, MaxColsAtCompileTime > MatrixUType
Type of the matrix returned by matrixU(): a plain matrix with the shape and storage options of Matrix...
Definition RealSchur.h:81
ComputationInfo info() const
Reports whether previous computation was successful.
Definition RealSchur.h:241
Eigen::Index Index
Definition RealSchur.h:74
RealSchur(Index size=RowsAtCompileTime==Dynamic ? 1 :RowsAtCompileTime)
Default constructor.
Definition RealSchur.h:95
RealSchur(EigenBase< InputType > &matrix, bool computeU=true)
Constructor for inplace decomposition .
Definition RealSchur.h:144
RealSchur(const EigenBase< InputType > &matrix, bool computeU=true)
Constructor; computes real Schur decomposition of given matrix.
Definition RealSchur.h:116
static const int m_maxIterationsPerRow
Maximum number of iterations per row.
Definition RealSchur.h:264
RealSchur & compute(const EigenBase< InputType > &matrix, bool computeU=true)
Computes Schur decomposition of given matrix.
Index getMaxIterations() const
Returns the maximum number of iterations.
Definition RealSchur.h:257
RealSchur & setMaxIterations(Index maxIters)
Sets the maximum number of iterations allowed.
Definition RealSchur.h:251
RealSchur & computeFromHessenberg(const HessMatrixType &matrixH, const OrthMatrixType &matrixQ, bool computeU)
Computes Schur decomposition of a Hessenberg matrix H = Z T Z^T.
const MatrixUType & matrixU() const
Returns the orthogonal matrix in the Schur decomposition.
Definition RealSchur.h:165
const MatrixType & matrixT() const
Returns the quasi-triangular matrix in the Schur decomposition.
Definition RealSchur.h:181
ComputationInfo
Definition Constants.h:455
@ PropagateNaN
Definition Constants.h:343
@ Success
Definition Constants.h:457
@ NoConvergence
Definition Constants.h:461
@ RowMajor
Definition Constants.h:321
Definition EigenBase.h:34
constexpr Index cols() const noexcept
Definition EigenBase.h:62
constexpr Derived & derived()
Definition EigenBase.h:50
constexpr Index rows() const noexcept
Definition EigenBase.h:60