Eigen  5.0.1
 
Loading...
Searching...
No Matches
SelfAdjointEigenSolver.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2008-2010 Gael Guennebaud <gael.guennebaud@inria.fr>
5// Copyright (C) 2010 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// SPDX-License-Identifier: MPL-2.0
11
12#ifndef EIGEN_SELFADJOINTEIGENSOLVER_H
13#define EIGEN_SELFADJOINTEIGENSOLVER_H
14
15#include "./Tridiagonalization.h"
16
17// IWYU pragma: private
18#include "./InternalHeaderCheck.h"
19
20namespace Eigen {
21
22template <typename MatrixType_>
24
25namespace internal {
26template <typename SolverType, int Size, bool IsComplex, bool IsDirect = !IsComplex && (Size == 2 || Size == 3)>
27struct direct_selfadjoint_eigenvalues;
28
29template <bool PerBlockScaling, typename MatrixType, typename DiagType, typename SubDiagType>
30EIGEN_DEVICE_FUNC ComputationInfo computeFromTridiagonal_impl(DiagType& diag, SubDiagType& subdiag,
31 const Index maxIterations, bool computeEigenvectors,
32 MatrixType& eivec);
33} // namespace internal
34
82template <typename MatrixType_>
84 public:
85 using MatrixType = MatrixType_;
86 enum {
87 Size = MatrixType::RowsAtCompileTime,
88 ColsAtCompileTime = MatrixType::ColsAtCompileTime,
89 Options = internal::plain_object_options<MatrixType>::value,
90 MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
91 };
92
94 using Scalar = typename MatrixType::Scalar;
95 using Index = Eigen::Index;
96
101
107 std::conditional_t<internal::is_ref<MatrixType>::value, MatrixType,
109
116 using RealScalar = typename NumTraits<Scalar>::Real;
117
118 friend struct internal::direct_selfadjoint_eigenvalues<SelfAdjointEigenSolver, Size, NumTraits<Scalar>::IsComplex>;
119
125 using VectorType = typename internal::plain_col_type<MatrixType, Scalar>::type;
126 using RealVectorType = typename internal::plain_col_type<MatrixType, RealScalar>::type;
127 using TridiagonalizationType = Tridiagonalization<MatrixType>;
128 using SubDiagonalType = typename TridiagonalizationType::SubDiagonalType;
129
140 EIGEN_DEVICE_FUNC SelfAdjointEigenSolver()
141 : m_eivec(),
142 m_workspace(),
143 m_eivalues(),
144 m_subdiag(),
145 m_hcoeffs(),
146 m_info(InvalidInput),
147 m_isInitialized(false),
148 m_eigenvectorsOk(false) {}
149
162 EIGEN_DEVICE_FUNC explicit SelfAdjointEigenSolver(Index size)
163 : m_eivec(size, size),
164 m_workspace(size),
165 m_eivalues(size),
166 m_subdiag(size > 1 ? size - 1 : 1),
167 m_hcoeffs(size > 1 ? size - 1 : 1),
168 m_isInitialized(false),
169 m_eigenvectorsOk(false) {}
170
186 template <typename InputType>
187 EIGEN_DEVICE_FUNC explicit SelfAdjointEigenSolver(const EigenBase<InputType>& matrix,
188 int options = ComputeEigenvectors)
189 : m_eivec(matrix.rows(), matrix.cols()),
190 m_workspace(matrix.cols()),
191 m_eivalues(matrix.cols()),
192 m_subdiag(matrix.rows() > 1 ? matrix.rows() - 1 : 1),
193 m_hcoeffs(matrix.cols() > 1 ? matrix.cols() - 1 : 1),
194 m_isInitialized(false),
195 m_eigenvectorsOk(false) {
196 compute(matrix.derived(), options);
197 }
198
210 template <typename InputType, bool IsRef = internal::is_ref<MatrixType>::value, std::enable_if_t<IsRef, int> = 0>
211 EIGEN_DEVICE_FUNC explicit SelfAdjointEigenSolver(EigenBase<InputType>& matrix, int options = ComputeEigenvectors)
212 : SelfAdjointEigenSolver(matrix, BindStorageTag()) {
213 m_eivec.template triangularView<StrictlyUpper>().setZero();
214 computeInPlace(options);
215 }
216
247 template <typename InputType>
248 EIGEN_DEVICE_FUNC SelfAdjointEigenSolver& compute(const EigenBase<InputType>& matrix,
249 int options = ComputeEigenvectors);
250
277 EIGEN_DEVICE_FUNC SelfAdjointEigenSolver& computeDirect(const MatrixType& matrix, int options = ComputeEigenvectors);
278
291 SelfAdjointEigenSolver& computeFromTridiagonal(const RealVectorType& diag, const SubDiagonalType& subdiag,
292 int options = ComputeEigenvectors);
293
317 EIGEN_DEVICE_FUNC const EigenvectorsType& eigenvectors() const {
318 eigen_assert(m_isInitialized && "SelfAdjointEigenSolver is not initialized.");
319 eigen_assert(m_eigenvectorsOk && "The eigenvectors have not been computed together with the eigenvalues.");
320 return m_eivec;
321 }
322
338 EIGEN_DEVICE_FUNC const RealVectorType& eigenvalues() const {
339 eigen_assert(m_isInitialized && "SelfAdjointEigenSolver is not initialized.");
340 return m_eivalues;
341 }
342
360 EIGEN_DEVICE_FUNC PlainMatrixType operatorSqrt() const {
361 eigen_assert(m_isInitialized && "SelfAdjointEigenSolver is not initialized.");
362 eigen_assert(m_eigenvectorsOk && "The eigenvectors have not been computed together with the eigenvalues.");
363 return m_eivec * m_eivalues.cwiseSqrt().asDiagonal() * m_eivec.adjoint();
364 }
365
376 EIGEN_DEVICE_FUNC PlainMatrixType operatorExp() const {
377 eigen_assert(m_isInitialized && "SelfAdjointEigenSolver is not initialized.");
378 eigen_assert(m_eigenvectorsOk && "The eigenvectors have not been computed together with the eigenvalues.");
379 return m_eivec * m_eivalues.array().exp().matrix().asDiagonal() * m_eivec.adjoint();
380 }
381
400 EIGEN_DEVICE_FUNC PlainMatrixType operatorInverseSqrt() const {
401 eigen_assert(m_isInitialized && "SelfAdjointEigenSolver is not initialized.");
402 eigen_assert(m_eigenvectorsOk && "The eigenvectors have not been computed together with the eigenvalues.");
403 return m_eivec * m_eivalues.cwiseInverse().cwiseSqrt().asDiagonal() * m_eivec.adjoint();
404 }
405
410 EIGEN_DEVICE_FUNC ComputationInfo info() const {
411 eigen_assert(m_isInitialized && "SelfAdjointEigenSolver is not initialized.");
412 return m_info;
413 }
414
420 static const int m_maxIterations = 30;
421
422 protected:
423 EIGEN_STATIC_ASSERT_NON_INTEGER(Scalar)
424
425
426 struct BindStorageTag {};
427
430 template <typename InputType>
431 EIGEN_DEVICE_FUNC SelfAdjointEigenSolver(EigenBase<InputType>& matrix, BindStorageTag)
432 : m_eivec(matrix.derived()),
433 m_workspace(matrix.cols()),
434 m_eivalues(matrix.cols()),
435 m_subdiag(matrix.rows() > 1 ? matrix.rows() - 1 : 1),
436 m_hcoeffs(matrix.cols() > 1 ? matrix.cols() - 1 : 1),
437 m_isInitialized(false),
438 m_eigenvectorsOk(false) {}
439
442 EIGEN_DEVICE_FUNC SelfAdjointEigenSolver& computeInPlace(int options);
443
444 EigenvectorsType m_eivec;
445 VectorType m_workspace;
446 RealVectorType m_eivalues;
447 typename TridiagonalizationType::SubDiagonalType m_subdiag;
448 typename TridiagonalizationType::CoeffVectorType m_hcoeffs;
449 ComputationInfo m_info;
450 bool m_isInitialized;
451 bool m_eigenvectorsOk;
452};
453
454namespace internal {
473template <typename RealScalar, typename Index, typename MatrixQType>
474EIGEN_DEVICE_FUNC static void tridiagonal_qr_step(RealScalar* diag, RealScalar* subdiag, Index start, Index end,
475 MatrixQType* matrixQ);
476} // namespace internal
477
478template <typename MatrixType>
479template <typename InputType>
481 const EigenBase<InputType>& a_matrix, int options) {
482 const InputType& matrix(a_matrix.derived());
483 eigen_assert(matrix.cols() == matrix.rows());
484 m_eivec = matrix.template triangularView<Lower>();
485 return computeInPlace(options);
486}
487
488template <typename MatrixType>
489EIGEN_DEVICE_FUNC SelfAdjointEigenSolver<MatrixType>& SelfAdjointEigenSolver<MatrixType>::computeInPlace(int options) {
490 eigen_assert(m_eivec.cols() == m_eivec.rows());
491 eigen_assert((options & ~(EigVecMask | GenEigMask)) == 0 && (options & EigVecMask) != EigVecMask &&
492 "invalid option parameter");
493 bool computeEigenvectors = (options & ComputeEigenvectors) == ComputeEigenvectors;
494 Index n = m_eivec.cols();
495 m_eivalues.resize(n, 1);
496
497 if (n <= 1) {
498 if (n == 1) m_eivalues.coeffRef(0, 0) = numext::real(m_eivec.coeff(0, 0));
499 if (computeEigenvectors) m_eivec.setOnes();
500 m_info = m_eivalues.allFinite() ? Success : NoConvergence;
501 m_isInitialized = true;
502 m_eigenvectorsOk = computeEigenvectors;
503 return *this;
504 }
505
506 // declare some aliases
507 RealVectorType& diag = m_eivalues;
508 EigenvectorsType& mat = m_eivec;
509
510 // Scale the matrix to O(1) to avoid overflow/underflow during tridiagonalization
511 // and subsequent QR iteration. Power-of-two factors avoid rounding representable scaled coefficients.
512 // Note: for block-diagonal matrices with widely separated scales, this
513 // can underflow small blocks. Users with such matrices should tridiagonalize separately
514 // and call computeFromTridiagonal(), which uses per-block scaling.
515 const RealScalar maxCoeff = internal::safe_scaling<RealScalar>::recover_flushed_max_coeff(
516 mat, mat.cwiseAbs().template maxCoeff<PropagateNaN>());
517 if (!(numext::isfinite)(maxCoeff)) {
518 // Input contains Inf or NaN.
519 m_info = NoConvergence;
520 m_isInitialized = true;
521 m_eigenvectorsOk = false;
522 return *this;
523 }
524 const auto factors = internal::safe_scaling<RealScalar>::with_scaled(mat, maxCoeff, [&](const auto& scaled) {
525 mat.template triangularView<Lower>() = scaled.template triangularView<Lower>();
526 });
527 m_subdiag.resize(n - 1);
528 m_hcoeffs.resize(n - 1);
529 internal::tridiagonalization_inplace(mat, diag, m_subdiag, m_hcoeffs, m_workspace, computeEigenvectors);
530
531 m_info = internal::computeFromTridiagonal_impl<false>(diag, m_subdiag, m_maxIterations, computeEigenvectors, m_eivec);
532
533 // Scale back the eigenvalues.
534 internal::safe_scaling<RealScalar>::unscale_in_place(m_eivalues, maxCoeff, factors);
535
536 m_isInitialized = true;
537 m_eigenvectorsOk = computeEigenvectors;
538 return *this;
539}
540
541template <typename MatrixType>
543 const RealVectorType& diag, const SubDiagonalType& subdiag, int options) {
544 bool computeEigenvectors = (options & ComputeEigenvectors) == ComputeEigenvectors;
545
546 m_eivalues = diag;
547 m_subdiag = subdiag;
548
549 // Check for Inf/NaN in the input.
550 {
551 RealScalar scale = RealScalar(0);
552 if (m_eivalues.size() > 0) scale = m_eivalues.cwiseAbs().maxCoeff();
553 if (m_subdiag.size() > 0) scale = numext::maxi(scale, m_subdiag.cwiseAbs().maxCoeff());
554 if (!(numext::isfinite)(scale)) {
555 m_info = NoConvergence;
556 m_isInitialized = true;
557 m_eigenvectorsOk = false;
558 return *this;
559 }
560 }
561
562 if (computeEigenvectors) {
563 m_eivec.setIdentity(diag.size(), diag.size());
564 }
565 // Use per-deflation-block scaling (like LAPACK's DSTERF) to avoid losing
566 // precision when the tridiagonal entries span a wide range of magnitudes.
567 m_info =
568 internal::computeFromTridiagonal_impl<true>(m_eivalues, m_subdiag, m_maxIterations, computeEigenvectors, m_eivec);
569
570 m_isInitialized = true;
571 m_eigenvectorsOk = computeEigenvectors;
572 return *this;
573}
574
575namespace internal {
591template <bool PerBlockScaling, typename MatrixType, typename DiagType, typename SubDiagType>
592EIGEN_DEVICE_FUNC ComputationInfo computeFromTridiagonal_impl(DiagType& diag, SubDiagType& subdiag,
593 const Index maxIterations, bool computeEigenvectors,
594 MatrixType& eivec) {
595 ComputationInfo info;
596
597 Index n = diag.size();
598 Index end = n - 1;
599 Index start = 0;
600 Index iter = 0; // total number of iterations
601
602 using RealScalar = typename DiagType::RealScalar;
603 const RealScalar considerAsZero = (std::numeric_limits<RealScalar>::min)();
604 const RealScalar precision = NumTraits<RealScalar>::epsilon();
605 const RealScalar precision_inv = RealScalar(1) / precision;
606
607 // Helper lambda for the deflation test.
608 auto deflate = [&](Index lo, Index hi) {
609 for (Index i = lo; i < hi; ++i) {
610 const RealScalar absSubdiag = numext::abs(subdiag[i]);
611 if (absSubdiag < considerAsZero) {
612 subdiag[i] = RealScalar(0);
613 } else if (!PerBlockScaling) {
614 // Homogeneous in the global scale: both sides are linear in it, so prescaling cannot change which couplings
615 // are discarded (the previous test was quadratic on the left).
616 if (absSubdiag <= precision * numext::maxi(numext::abs(diag[i]), numext::abs(diag[i + 1]))) {
617 subdiag[i] = RealScalar(0);
618 }
619 } else {
620 const RealScalar scaled_subdiag = precision_inv * subdiag[i];
621 if (scaled_subdiag * scaled_subdiag <= (numext::abs(diag[i]) + numext::abs(diag[i + 1]))) {
622 subdiag[i] = RealScalar(0);
623 }
624 }
625 }
626 };
627
628 // For per-block scaling, track the currently scaled block and its scale factor.
629 // When the outer loop identifies a block outside the scaled region, unscale the old
630 // block and scale the new one. This keeps the same outer loop structure (one QR step
631 // per iteration) while ensuring each block is processed in scaled coordinates.
632 Index scaled_start = -1, scaled_end = -1;
633 RealScalar block_norm = RealScalar(0);
634 safe_scaling_factors<RealScalar> blockFactors;
635 const auto restore_block = [&]() {
636 if (scaled_start < 0) return;
637 auto diagonal = diag.segment(scaled_start, scaled_end - scaled_start + 1);
638 auto offDiagonal = subdiag.segment(scaled_start, scaled_end - scaled_start);
639 safe_scaling<RealScalar>::unscale_in_place(diagonal, block_norm, blockFactors);
640 safe_scaling<RealScalar>::unscale_in_place(offDiagonal, block_norm, blockFactors);
641 };
642
643 while (end > 0) {
644 deflate(start, end);
645
646 // Find the largest unreduced block at the end of the matrix.
647 while (end > 0 && numext::is_exactly_zero(subdiag[end - 1])) {
648 end--;
649 }
650 if (end <= 0) break;
651
652 // if we spent too many iterations, we give up
653 iter++;
654 if (iter > maxIterations * n) break;
655
656 start = end - 1;
657 while (start > 0 && !numext::is_exactly_zero(subdiag[start - 1])) start--;
658
659 if (PerBlockScaling) {
660 // Check if we've moved to a different block than the one currently scaled.
661 if (start != scaled_start || end != scaled_end) {
662 restore_block();
663 // Compute the norm and scale the new block to O(1).
664 block_norm = RealScalar(0);
665 for (Index i = start; i <= end; ++i) block_norm = numext::maxi(block_norm, numext::abs(diag[i]));
666 for (Index i = start; i < end; ++i) block_norm = numext::maxi(block_norm, numext::abs(subdiag[i]));
667 auto diagonal = diag.segment(start, end - start + 1);
668 auto offDiagonal = subdiag.segment(start, end - start);
669 blockFactors = safe_scaling<RealScalar>::scale_to(diagonal, diagonal, block_norm);
670 safe_scaling<RealScalar>::scale_to(offDiagonal, offDiagonal, block_norm, blockFactors);
671 scaled_start = start;
672 scaled_end = end;
673 }
674 }
675
676 internal::tridiagonal_qr_step(diag.data(), subdiag.data(), start, end,
677 computeEigenvectors ? &eivec : static_cast<MatrixType*>(nullptr));
678 }
679
680 // Unscale any remaining scaled block.
681 if (PerBlockScaling) restore_block();
682 if (iter <= maxIterations * n)
683 info = Success;
684 else
685 info = NoConvergence;
686
687 // Sort eigenvalues and corresponding vectors.
688 // TODO: make the sort optional and use a more efficient sorting algorithm.
689 if (info == Success) {
690 for (Index i = 0; i < n - 1; ++i) {
691 // Scalar argmin: the vectorized minCoeff path can return the wrong
692 // index on targets whose SIMD reduction flushes subnormals (32-bit ARM
693 // NEON always treats subnormal inputs to vminq_f32 as zero), which
694 // corrupts ordering when eigenvalues span the subnormal range.
695 Index k = i;
696 RealScalar min_val = diag[i];
697 for (Index j = i + 1; j < n; ++j) {
698 if (diag[j] < min_val) {
699 min_val = diag[j];
700 k = j;
701 }
702 }
703 if (k != i) {
704 numext::swap(diag[i], diag[k]);
705 if (computeEigenvectors) eivec.col(i).swap(eivec.col(k));
706 }
707 }
708 }
709 return info;
710}
711
712template <typename SolverType, int Size, bool IsComplex, bool IsDirect>
713struct direct_selfadjoint_eigenvalues {
714 EIGEN_DEVICE_FUNC static inline void run(SolverType& eig, const typename SolverType::MatrixType& A, int options) {
715 eig.compute(A, options);
716 }
717};
718
719template <typename SolverType, int Size>
720struct direct_selfadjoint_eigensolver_kernel;
721
722template <typename SolverType>
723struct direct_selfadjoint_eigensolver_kernel<SolverType, 3> {
724 using MatrixType = typename SolverType::MatrixType;
725 using VectorType = typename SolverType::RealVectorType;
726 using Scalar = typename SolverType::Scalar;
727 using EigenvectorsType = typename SolverType::EigenvectorsType;
728 using PlainMatrixType = typename SolverType::PlainMatrixType;
729
734 EIGEN_DEVICE_FUNC static inline void computeRoots(const MatrixType& m, VectorType& roots) {
735 EIGEN_USING_STD(sqrt)
736 EIGEN_USING_STD(atan2)
737 EIGEN_USING_STD(cos)
738 EIGEN_USING_STD(sin)
739 const Scalar s_inv3 = Scalar(1) / Scalar(3);
740 const Scalar s_sqrt3 = sqrt(Scalar(3));
741
742 // The characteristic equation is x^3 - c2*x^2 + c1*x - c0 = 0. The
743 // eigenvalues are the roots to this equation, all guaranteed to be
744 // real-valued, because the matrix is symmetric.
745 Scalar c0 = m(0, 0) * m(1, 1) * m(2, 2) + Scalar(2) * m(1, 0) * m(2, 0) * m(2, 1) - m(0, 0) * m(2, 1) * m(2, 1) -
746 m(1, 1) * m(2, 0) * m(2, 0) - m(2, 2) * m(1, 0) * m(1, 0);
747 Scalar c1 = m(0, 0) * m(1, 1) - m(1, 0) * m(1, 0) + m(0, 0) * m(2, 2) - m(2, 0) * m(2, 0) + m(1, 1) * m(2, 2) -
748 m(2, 1) * m(2, 1);
749 Scalar c2 = m(0, 0) + m(1, 1) + m(2, 2);
750
751 // Construct the parameters used in classifying the roots of the equation
752 // and in solving the equation for the roots in closed form.
753 Scalar c2_over_3 = c2 * s_inv3;
754 Scalar a_over_3 = (c2 * c2_over_3 - c1) * s_inv3;
755 a_over_3 = numext::maxi(a_over_3, Scalar(0));
756
757 Scalar half_b = Scalar(0.5) * (c0 + c2_over_3 * (Scalar(2) * c2_over_3 * c2_over_3 - c1));
758
759 Scalar q = a_over_3 * a_over_3 * a_over_3 - half_b * half_b;
760 q = numext::maxi(q, Scalar(0));
761
762 // Compute the eigenvalues by solving for the roots of the polynomial.
763 Scalar rho = sqrt(a_over_3);
764 Scalar theta = atan2(sqrt(q), half_b) * s_inv3; // since sqrt(q) > 0, atan2 is in [0, pi] and theta is in [0, pi/3]
765 Scalar cos_theta = cos(theta);
766 Scalar sin_theta = sin(theta);
767 // roots are already sorted, since cos is monotonically decreasing on [0, pi]
768 roots(0) = c2_over_3 - rho * (cos_theta + s_sqrt3 * sin_theta); // == 2*rho*cos(theta+2pi/3)
769 roots(1) = c2_over_3 - rho * (cos_theta - s_sqrt3 * sin_theta); // == 2*rho*cos(theta+ pi/3)
770 roots(2) = c2_over_3 + Scalar(2) * rho * cos_theta;
771 }
772
773 EIGEN_DEVICE_FUNC static inline bool extract_kernel(PlainMatrixType& mat, Ref<VectorType> res,
774 Ref<VectorType> representative) {
775 EIGEN_USING_STD(abs);
776 EIGEN_USING_STD(sqrt);
777 Index i0;
778 // Find non-zero column i0 (by construction, there must exist a non zero coefficient on the diagonal):
779 mat.diagonal().cwiseAbs().maxCoeff(&i0);
780 // mat.col(i0) is a good candidate for an orthogonal vector to the current eigenvector,
781 // so let's save it:
782 representative = mat.col(i0);
783 Scalar n0, n1;
784 VectorType c0, c1;
785 n0 = (c0 = representative.cross(mat.col((i0 + 1) % 3))).squaredNorm();
786 n1 = (c1 = representative.cross(mat.col((i0 + 2) % 3))).squaredNorm();
787 if (n0 > n1)
788 res = c0 / sqrt(n0);
789 else
790 res = c1 / sqrt(n1);
791
792 return true;
793 }
794
795 EIGEN_DEVICE_FUNC static void run(PlainMatrixType& scaledMat, VectorType& eivals, EigenvectorsType& eivecs,
796 bool computeEigenvectors, const Scalar& maxCoeff) {
797 // compute the eigenvalues
798 computeRoots(scaledMat, eivals);
799
800 // computeRoots produces theoretically sorted roots, but floating-point
801 // rounding in the trigonometric formulas can break the ordering.
802 // Enforce sorting with a fixed 3-element compare-swap network.
803 if (eivals(0) > eivals(1)) numext::swap(eivals(0), eivals(1));
804 if (eivals(1) > eivals(2)) numext::swap(eivals(1), eivals(2));
805 if (eivals(0) > eivals(1)) numext::swap(eivals(0), eivals(1));
806
807 // compute the eigenvectors
808 if (computeEigenvectors) {
809 if ((eivals(2) - eivals(0)) <= Eigen::NumTraits<Scalar>::epsilon() * maxCoeff) {
810 // All three eigenvalues are numerically the same
811 eivecs.setIdentity();
812 } else {
813 PlainMatrixType tmp;
814 tmp = scaledMat;
815
816 // Compute the eigenvector of the most distinct eigenvalue
817 Scalar d0 = eivals(2) - eivals(1);
818 Scalar d1 = eivals(1) - eivals(0);
819 Index k(0), l(2);
820 if (d0 > d1) {
821 numext::swap(k, l);
822 d0 = d1;
823 }
824
825 // Compute the eigenvector of index k
826 {
827 tmp.diagonal().array() -= eivals(k);
828 // By construction, 'tmp' is of rank 2, and its kernel corresponds to the respective eigenvector.
829 extract_kernel(tmp, eivecs.col(k), eivecs.col(l));
830 }
831
832 // Compute eigenvector of index l
833 if (d0 <= 2 * Eigen::NumTraits<Scalar>::epsilon() * d1) {
834 // If d0 is too small, then the two other eigenvalues are numerically the same,
835 // and thus we only have to ortho-normalize the near orthogonal vector we saved above.
836 eivecs.col(l) -= eivecs.col(k).dot(eivecs.col(l)) * eivecs.col(k);
837 eivecs.col(l).normalize();
838 } else {
839 tmp = scaledMat;
840 tmp.diagonal().array() -= eivals(l);
841
842 VectorType dummy;
843 extract_kernel(tmp, eivecs.col(l), dummy);
844 }
845
846 // Compute last eigenvector from the other two
847 eivecs.col(1) = eivecs.col(2).cross(eivecs.col(0)).normalized();
848 }
849 }
850 }
851};
852
853// 2x2 direct eigenvalues decomposition, code from Hauke Heibel
854template <typename SolverType>
855struct direct_selfadjoint_eigensolver_kernel<SolverType, 2> {
856 using MatrixType = typename SolverType::MatrixType;
857 using VectorType = typename SolverType::RealVectorType;
858 using Scalar = typename SolverType::Scalar;
859 using EigenvectorsType = typename SolverType::EigenvectorsType;
860 using PlainMatrixType = typename SolverType::PlainMatrixType;
861
862 EIGEN_DEVICE_FUNC static inline void computeRoots(const MatrixType& m, VectorType& roots) {
863 EIGEN_USING_STD(sqrt);
864 const Scalar t0 = Scalar(0.5) * sqrt(numext::abs2(m(0, 0) - m(1, 1)) + Scalar(4) * numext::abs2(m(1, 0)));
865 const Scalar t1 = Scalar(0.5) * (m(0, 0) + m(1, 1));
866 roots(0) = t1 - t0;
867 roots(1) = t1 + t0;
868 }
869
870 EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE static void run(PlainMatrixType& scaledMat, VectorType& eivals,
871 EigenvectorsType& eivecs, bool computeEigenvectors,
872 const Scalar&) {
873 EIGEN_USING_STD(sqrt);
874 EIGEN_USING_STD(abs);
875
876 // Compute the eigenvalues
877 computeRoots(scaledMat, eivals);
878
879 // compute the eigen vectors
880 if (computeEigenvectors) {
881 if ((eivals(1) - eivals(0)) <= abs(eivals(1)) * Eigen::NumTraits<Scalar>::epsilon()) {
882 eivecs.setIdentity();
883 } else {
884 scaledMat.diagonal().array() -= eivals(1);
885 Scalar a2 = numext::abs2(scaledMat(0, 0));
886 Scalar c2 = numext::abs2(scaledMat(1, 1));
887 Scalar b2 = numext::abs2(scaledMat(1, 0));
888 if (a2 > c2) {
889 eivecs.col(1) << -scaledMat(1, 0), scaledMat(0, 0);
890 eivecs.col(1) /= sqrt(a2 + b2);
891 } else {
892 eivecs.col(1) << -scaledMat(1, 1), scaledMat(1, 0);
893 eivecs.col(1) /= sqrt(c2 + b2);
894 }
895
896 // The partner is already normalized; this rotation preserves its norm.
897 eivecs.col(0) << -eivecs(1, 1), eivecs(0, 1);
898 }
899 }
900 }
901};
902
903template <typename SolverType, int Size>
904struct direct_selfadjoint_eigenvalues<SolverType, Size, false, true> {
905 using MatrixType = typename SolverType::MatrixType;
906 using PlainMatrixType = typename SolverType::PlainMatrixType;
907 using Scalar = typename SolverType::Scalar;
908
909 // Only host-side long double limits need more range than double.
910 using Limit = std::conditional_t<std::is_same<Scalar, long double>::value, long double, double>;
911
912 EIGEN_DEVICE_FUNC static constexpr Limit power_of_two(int exponent) {
913 Limit result = 1;
914 for (; exponent > 0; --exponent) result *= 2;
915 for (; exponent < 0; ++exponent) result /= 2;
916 return result;
917 }
918
919 EIGEN_DEVICE_FUNC static bool safe_without_scaling(const Scalar& magnitude, true_type) {
920 // The quadratic/cubic kernels have degree 2/6. Reserve 12 exponent bits
921 // for intermediate growth and keep flushed terms below epsilon*mu^degree.
922 constexpr int degree = Size == 3 ? 6 : 2;
923 constexpr int lower = std::numeric_limits<Scalar>::min_exponent - 1 + std::numeric_limits<Scalar>::digits - 1 + 12;
924 constexpr int upper = std::numeric_limits<Scalar>::max_exponent - 1 - 12;
925 constexpr int minExponent = lower >= 0 ? (lower + degree - 1) / degree : lower / degree;
926 constexpr int maxExponent = upper >= 0 ? upper / degree : (upper - degree + 1) / degree;
927 constexpr Limit minimum = power_of_two(minExponent);
928 constexpr Limit maximum = power_of_two(maxExponent);
929 return magnitude >= Scalar(minimum) && magnitude <= Scalar(maximum);
930 }
931
932 EIGEN_DEVICE_FUNC static bool safe_without_scaling(const Scalar& magnitude, false_type) {
933 return magnitude >= Scalar(0.25) && magnitude <= Scalar(2) &&
934 NumTraits<Scalar>::epsilon() / Scalar(4096) >= (std::numeric_limits<Scalar>::min)();
935 }
936
937 EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE static void run_scaled(SolverType& solver, const MatrixType& mat,
938 const Scalar& shift, Scalar centeredMax, int options) {
939 PlainMatrixType scaledMat = mat.template selfadjointView<Lower>();
940 scaledMat.diagonal().array() -= shift;
941 centeredMax = safe_scaling<Scalar>::recover_flushed_max_coeff(scaledMat, centeredMax);
942 const auto factors = safe_scaling<Scalar>::scale_to(scaledMat, scaledMat, centeredMax);
943 direct_selfadjoint_eigensolver_kernel<SolverType, Size>::run(scaledMat, solver.m_eivalues, solver.m_eivec,
945 scaledMat.cwiseAbs().maxCoeff());
946 safe_scaling<Scalar>::unscale_in_place(solver.m_eivalues, centeredMax, factors);
947 }
948
949 EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE static void run_centered(SolverType& solver, const MatrixType& mat,
950 int options) {
951 const bool computeEigenvectors = (options & ComputeEigenvectors) == ComputeEigenvectors;
952 PlainMatrixType scaledMat = mat.template selfadjointView<Lower>();
953 const Scalar first = scaledMat(0, 0);
954 Scalar shift = first;
955 for (Index i = 1; i < Size; ++i) shift += (scaledMat(i, i) - first) / Scalar(Size);
956 scaledMat.diagonal().array() -= shift;
957 // Centering can leave a much smaller matrix; normalize again for the cubic and cross-product formulas.
958 const Scalar centeredMax = scaledMat.cwiseAbs().maxCoeff();
959 if (safe_without_scaling(centeredMax, supports_power_of_two_scaling<Scalar>())) {
960 direct_selfadjoint_eigensolver_kernel<SolverType, Size>::run(scaledMat, solver.m_eivalues, solver.m_eivec,
961 computeEigenvectors, centeredMax);
962 } else {
963 run_scaled(solver, mat, shift, centeredMax, options);
964 }
965 solver.m_eivalues.array() += shift;
966 solver.m_info = Success;
967 solver.m_isInitialized = true;
968 solver.m_eigenvectorsOk = computeEigenvectors;
969 }
970
971 EIGEN_DEVICE_FUNC static void run_large(SolverType& solver, PlainMatrixType& scaledMat, int options, false_type) {
972 run_centered(solver, scaledMat, options);
973 }
974
975 EIGEN_DEVICE_FUNC static void run_large(SolverType& solver, PlainMatrixType& scaledMat, int options, true_type) {
976 // The cubic loses accuracy near repeated roots; at the exponent limit that error can overflow a finite result.
977 solver.compute(scaledMat, options);
978 }
979
980 EIGEN_DEVICE_FUNC EIGEN_DONT_INLINE static void run_prescaled(SolverType& solver, const MatrixType& mat,
981 const Scalar& maxCoeff, int options) {
982 PlainMatrixType scaledMat = mat.template selfadjointView<Lower>();
983 const Scalar recoveredMax = safe_scaling<Scalar>::recover_flushed_max_coeff(scaledMat, maxCoeff);
984 const auto inputFactors = safe_scaling<Scalar>::scale_to(scaledMat, scaledMat, recoveredMax);
985 if (maxCoeff > (NumTraits<Scalar>::highest() * Scalar(0.5)) / Scalar(Size)) {
986 run_large(solver, scaledMat, options,
987 bool_constant<(Size == 3 && supports_power_of_two_scaling<Scalar>::value)>());
988 } else {
989 run_centered(solver, scaledMat, options);
990 }
991 safe_scaling<Scalar>::unscale_in_place(solver.m_eivalues, recoveredMax, inputFactors);
992 }
993
994 EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE static void run(SolverType& solver, const MatrixType& mat, int options) {
995 eigen_assert(mat.rows() == Size && mat.cols() == Size);
996 eigen_assert((options & ~(EigVecMask | GenEigMask)) == 0 && (options & EigVecMask) != EigVecMask &&
997 "invalid option parameter");
998 PlainMatrixType scaledMat = mat.template selfadjointView<Lower>();
999 const Scalar maxCoeff = scaledMat.cwiseAbs().maxCoeff();
1000 // Bound centered coefficients by 2*M and eigenvalues by Size*M. Prescale tiny inputs before any FTZ-sensitive
1001 // subtraction, and large inputs before centering or restoring the centered eigenvalues could overflow.
1002 // Halve highest() first: IBM double-double division can overflow internally at the maximum value.
1003 if (EIGEN_PREDICT_FALSE(maxCoeff < (std::numeric_limits<Scalar>::min)() / NumTraits<Scalar>::epsilon() ||
1004 maxCoeff > (NumTraits<Scalar>::highest() * Scalar(0.5)) / Scalar(Size))) {
1005 run_prescaled(solver, mat, maxCoeff, options);
1006 } else {
1007 run_centered(solver, mat, options);
1008 }
1009 }
1010};
1011
1012} // namespace internal
1013
1014template <typename MatrixType>
1016 const MatrixType& matrix, int options) {
1017 internal::direct_selfadjoint_eigenvalues<SelfAdjointEigenSolver, Size, NumTraits<Scalar>::IsComplex>::run(
1018 *this, matrix, options);
1019 return *this;
1020}
1021
1022namespace internal {
1023
1024// Francis implicit QR step; the rotations accumulate into *matrixQ when it is non-null.
1025template <typename RealScalar, typename Index, typename MatrixQType>
1026EIGEN_DEVICE_FUNC static void tridiagonal_qr_step(RealScalar* diag, RealScalar* subdiag, Index start, Index end,
1027 MatrixQType* matrixQ) {
1028 // Wilkinson Shift.
1029 RealScalar td = (diag[end - 1] - diag[end]) * RealScalar(0.5);
1030 RealScalar e = subdiag[end - 1];
1031 RealScalar mu = diag[end];
1032 if (numext::is_exactly_zero(td)) {
1033 mu -= numext::abs(e);
1034 } else if (!numext::is_exactly_zero(e)) {
1035 const RealScalar e2 = numext::abs2(e);
1036 if (numext::is_exactly_zero(e2)) {
1037 // Scaling prevents e^2 and td^2 from overflowing, but e^2 can still underflow. A subdiagonal that survives
1038 // deflation satisfies (e/epsilon)^2 > 2*abs(td), so td/e remains bounded in this branch. LAPACK's xSTEQR uses
1039 // this dimensionless form; keeping td/e inside hypot prevents fast-math from reassociating the correction into
1040 // an underflowing e^2 expression.
1041 const RealScalar ratio = td / e;
1042 const RealScalar h = numext::hypot(ratio, RealScalar(1));
1043 mu -= e / (ratio + (ratio > RealScalar(0) ? h : -h));
1044 } else {
1045 const RealScalar h = numext::hypot(td, e);
1046 mu -= e2 / (td + (td > RealScalar(0) ? h : -h));
1047 }
1048 }
1049
1050 RealScalar x = diag[start] - mu;
1051 RealScalar z = subdiag[start];
1052 // If z ever becomes zero, the Givens rotation will be the identity and
1053 // z will stay zero for all future iterations.
1054 for (Index k = start; k < end && !numext::is_exactly_zero(z); ++k) {
1055 JacobiRotation<RealScalar> rot;
1056 rot.makeGivens(x, z);
1057
1058 // do T = G' T G. The two new diagonal entries and the new off-diagonal are all expressed below
1059 // as functions of the original difference diff = diag[k] - diag[k+1] and the original subdiag[k],
1060 // so capture diff and compute delta/subdiag[k] before overwriting the diagonal.
1061 const RealScalar diff = diag[k] - diag[k + 1];
1062
1063 // Update the 2x2 diagonal block by the symmetric change delta, applied with opposite signs to
1064 // diag[k] and diag[k+1]. A similarity leaves the trace invariant, so this preserves diag[k] +
1065 // diag[k+1] exactly. Forming the two new diagonal entries from independent explicit formulas
1066 // (diag[k] = c^2 d_k - 2 c s e_k + s^2 d_{k+1}, diag[k+1] = s^2 d_k + 2 c s e_k + c^2 d_{k+1})
1067 // instead lets the two roundings drift the trace a little on every rotation, an error that
1068 // accumulates over the O(n^2) rotations of a full solve and measurably degrades the eigenvalues.
1069 // Algebraically delta = s^2 (d_k - d_{k+1}) + 2 c s e_k, which is exactly d_k minus the formula above.
1070 const RealScalar delta = rot.s() * (rot.s() * diff + RealScalar(2) * rot.c() * subdiag[k]);
1071 // The exact (G' T G) off-diagonal, c*s*(d_k - d_{k+1}) + (c^2 - s^2)*e_k, written in the same diff
1072 // form rather than via the c*sdk - s*dkp1 temporaries.
1073 subdiag[k] = rot.c() * rot.s() * diff + (rot.c() * rot.c() - rot.s() * rot.s()) * subdiag[k];
1074 diag[k] -= delta;
1075 diag[k + 1] += delta;
1076
1077 if (k > start) subdiag[k - 1] = rot.c() * subdiag[k - 1] - rot.s() * z;
1078
1079 // "Chasing the bulge" to return to triangular form.
1080 x = subdiag[k];
1081 if (k < end - 1) {
1082 z = -rot.s() * subdiag[k + 1];
1083 subdiag[k + 1] = rot.c() * subdiag[k + 1];
1084 }
1085
1086 // apply the givens rotation to the unit matrix Q = Q * G
1087 if (matrixQ) {
1088 // Contiguous column-major storage is rotated through a dynamic-size map: the rotation kernel then dispatches
1089 // on the runtime alignment for fixed-size matrices too, and rounds as it always did. Other storage (a
1090 // row-major Ref<>) is rotated through the matrix type itself.
1091 EIGEN_IF_CONSTEXPR (!MatrixQType::IsRowMajor && int(MatrixQType::InnerStrideAtCompileTime) == 1) {
1092 using Scalar = typename MatrixQType::Scalar;
1093 Map<Matrix<Scalar, Dynamic, Dynamic, ColMajor>, Unaligned, OuterStride<>> q(
1094 matrixQ->data(), matrixQ->rows(), matrixQ->cols(), OuterStride<>(matrixQ->outerStride()));
1095 q.applyOnTheRight(k, k + 1, rot);
1096 } else {
1097 matrixQ->applyOnTheRight(k, k + 1, rot);
1098 }
1099 }
1100 }
1101}
1102
1103} // end namespace internal
1104
1105} // end namespace Eigen
1106
1107#endif // EIGEN_SELFADJOINTEIGENSOLVER_H
Computes eigenvalues and eigenvectors of the generalized selfadjoint eigen problem.
Definition GeneralizedSelfAdjointEigenSolver.h:52
The matrix class, also used for vectors and row-vectors.
Definition Matrix.h:188
Computes eigenvalues and eigenvectors of selfadjoint matrices.
Definition SelfAdjointEigenSolver.h:83
PlainMatrixType operatorExp() const
Computes the matrix exponential of the matrix.
Definition SelfAdjointEigenSolver.h:376
SelfAdjointEigenSolver & compute(const EigenBase< InputType > &matrix, int options=ComputeEigenvectors)
Computes eigendecomposition of given matrix.
SelfAdjointEigenSolver & computeFromTridiagonal(const RealVectorType &diag, const SubDiagonalType &subdiag, int options=ComputeEigenvectors)
Computes the eigen decomposition from a tridiagonal symmetric matrix.
Definition SelfAdjointEigenSolver.h:542
SelfAdjointEigenSolver(EigenBase< InputType > &matrix, int options=ComputeEigenvectors)
Constructor for inplace decomposition .
Definition SelfAdjointEigenSolver.h:211
typename MatrixType::Scalar Scalar
Scalar type for matrices of type MatrixType_.
Definition SelfAdjointEigenSolver.h:94
typename internal::plain_col_type< MatrixType, Scalar >::type VectorType
Type for vector of eigenvalues as returned by eigenvalues().
Definition SelfAdjointEigenSolver.h:125
SelfAdjointEigenSolver()
Default constructor for fixed-size matrices.
Definition SelfAdjointEigenSolver.h:140
ComputationInfo info() const
Reports whether previous computation was successful.
Definition SelfAdjointEigenSolver.h:410
Matrix< Scalar, Size, ColsAtCompileTime, Options, MatrixType::MaxRowsAtCompileTime, MaxColsAtCompileTime > PlainMatrixType
Plain matrix type with the shape and storage options of MatrixType_; MatrixType_ itself unless that i...
Definition SelfAdjointEigenSolver.h:99
PlainMatrixType operatorInverseSqrt() const
Computes the inverse square root of the matrix.
Definition SelfAdjointEigenSolver.h:400
std::conditional_t< internal::is_ref< MatrixType >::value, MatrixType, Matrix< Scalar, Size, Size, ColMajor, MaxColsAtCompileTime, MaxColsAtCompileTime > > EigenvectorsType
Type of the matrix returned by eigenvectors().
Definition SelfAdjointEigenSolver.h:106
typename NumTraits< Scalar >::Real RealScalar
Real scalar type for MatrixType_.
Definition SelfAdjointEigenSolver.h:116
PlainMatrixType operatorSqrt() const
Computes the positive-definite square root of the matrix.
Definition SelfAdjointEigenSolver.h:360
Eigen::Index Index
Definition SelfAdjointEigenSolver.h:95
SelfAdjointEigenSolver(Index size)
Constructor, pre-allocates memory for dynamic-size matrices.
Definition SelfAdjointEigenSolver.h:162
const RealVectorType & eigenvalues() const
Returns the eigenvalues of given matrix.
Definition SelfAdjointEigenSolver.h:338
static const int m_maxIterations
Maximum number of iterations.
Definition SelfAdjointEigenSolver.h:420
const EigenvectorsType & eigenvectors() const
Returns the eigenvectors of given matrix.
Definition SelfAdjointEigenSolver.h:317
SelfAdjointEigenSolver(const EigenBase< InputType > &matrix, int options=ComputeEigenvectors)
Constructor; computes eigendecomposition of given matrix.
Definition SelfAdjointEigenSolver.h:187
SelfAdjointEigenSolver & computeDirect(const MatrixType &matrix, int options=ComputeEigenvectors)
Computes eigendecomposition of given matrix primarily using a closed-form algorithm.
Definition SelfAdjointEigenSolver.h:1015
Tridiagonal decomposition of a selfadjoint matrix.
Definition Tridiagonalization.h:71
ComputationInfo
Definition Constants.h:455
@ Unaligned
Definition Constants.h:236
@ Success
Definition Constants.h:457
@ NoConvergence
Definition Constants.h:461
@ ComputeEigenvectors
Definition Constants.h:406
Definition EigenBase.h:34
constexpr Derived & derived()
Definition EigenBase.h:50
Holds information about the various numeric (i.e. scalar) types allowed by Eigen.
Definition NumTraits.h:233