%%%%%%%%%%%%%%%%%%% % XLiFE++ is an extended library of finite elements written in C++ % Copyright (C) 2014 Lunéville, Eric; Kielbasiewicz, Nicolas; Lafranche, Yvon; Nguyen, Manh-Ha; Chambeyron, Colin % % This program is free software: you can redistribute it and/or modify % it under the terms of the GNU General Public License as published by % the Free Software Foundation, either version 3 of the License, or % (at your option) any later version. % This program is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details. % You should have received a copy of the GNU General Public License % along with this program. If not, see . %%%%%%%%%%%%%%%%%%% \section{{\libtitle eigenCore} sub-library} Most of the standard eigenvalue algorithms exploit projection processes in order to extract approximate eigenvectors from a given subspace. The basic idea of a projection method is to extract an approximate eigenvector from a specified low-dimensional subspace. If these approximation can be extracted, a small matrix eigenvalue problem is obtained. The numerical solution of the small $m\times m$ eigenvalue problem will be treated by the {\lib eigenCore} library. The {\lib eigenCore} library is organized in multi-level, each of which utilizes subroutines provided by the lower one. \begin{itemize} \item {\lib utils} : the lowest level provides various basic operations \item {\lib decomposition} : the second level implements several decomposition algorithms \item {\lib eigenSolver} : the highest level solves different eigen problem \end{itemize} \subsection{{\libtitle utils} part} This part contains several classes which work on different basic operations. \subsubsection{The {\classtitle VectorEigenDense} class} Class {\class VectorEigenDense} is derived directly from {\class Vector}. Besides taking advantages of all funtionalities of {\class Vector}, this class supplements some particular functions to calculate Householder transformation. \vspace{.1cm} \begin{lstlisting} template class VectorEigenDense : public Vector { public: typedef typename VectorEigenDense::type_t Scalar; typedef typename NumTraits::RealScalar RealScalar; typedef typename VectorEigenDense::it_vk it_vk; typedef typename VectorEigenDense::cit_vk cit_vk; VectorEigenDense() : Vector(), acType_(_col) {} VectorEigenDense(const Dimen l) : Vector(l, K()), acType_(_col) {} ... \end{lstlisting} \vspace{.1cm} This class offers some additional functions to calculate House holder values. \vspace{.1cm} \begin{lstlisting}[]{} void makeHouseHolderInPlace(K& tau, Real& beta); void makeHouseHolderInPlace(K& tau, Real& beta, Number tail); void makeHouseHolder(VectorEigenDense& essential, K& tau, Real& beta) const; \end{lstlisting} \vspace{.2cm} Note that the in the future, we may improve calculation performance of eigensolver for {\em dense} matrix by changing this class {\class VectorEigenDense}. By using the same block of allocated data, we can gain more efficiencies. However, for the moment, it's sufficient to have this class inherited from class {\class Vector} \displayInfos{library=eigenSolvers, test={test\_EigenSolverUtils.cpp}, header dep={Traits.hpp}} \subsubsection{The {\classtitle MatrixEigenDense} class} Class {\class MatrixEigenDense} inherits from the fundamental class {\class Matrix}. Basically, this class reuses all functionalities of its father in addition to some methods to support computing House holder, Cholesky composition and Jacobian rotation. \vspace{.1cm} \begin{lstlisting} template class MatrixEigenDense : public Matrix { public: typedef typename MatrixEigenDense::type_t Scalar; typedef typename NumTraits::RealScalar RealScalar; typedef typename std::vector::iterator it_vk; typedef typename std::vector::const_iterator cit_vk; MatrixEigenDense() : Matrix(), col_(1) {} MatrixEigenDense(const Dimen r) : Matrix(r,r), col_(r) {} ... \end{lstlisting} \vspace{.1cm} Householder computation can be invoked with \vspace{.1cm} \begin{lstlisting}[]{} void applyHouseholderOnTheLeft(const VectorEigenDense& essential, const K& tau); void applyHouseholderOnTheRight(const VectorEigenDense& essential, const K& tau); \end{lstlisting} \vspace{.1cm} Cholesky decomposition can be calculated by \vspace{.1cm} \begin{lstlisting}[]{} MatrixEigenDense cholesky() const; \end{lstlisting} \vspace{.1cm} or one can call Jacobian rotation with \vspace{.1cm} \begin{lstlisting}[]{} template inline void applyOnTheLeft(Index p, Index q, const JacobiRotation& j); template inline void applyOnTheRight(Index p, Index q, const JacobiRotation& j); \end{lstlisting} \vspace{.1cm} Similar to class {\class VectorEigenDense}, in the future, this class may be improved to make {\lib eigenCore} more efficient. \displayInfos{library=eigenSolvers, test={test\_EigenSolverUtils.cpp}, header dep={Traits.hpp}} \subsubsection{The {\classtitle JacobiRotation} class} Class {\class JacobiRotation} represents a {\em Jacobian} or {\em Givens} rotation, 2D rotation in a plane defined by its cosine and sine. \vspace{.1cm} \begin{lstlisting} template class JacobiRotation { public: typedef typename NumTraits::RealScalar RealScalar; /** Default constructor without any initialization. */ JacobiRotation() {} /** Construct a planar rotation from a cosine-sine pair (\a c, \c s). */ JacobiRotation(const Scalar& c, const Scalar& s) : c_(c), s_(s) {} Scalar& c() { return c_; } Scalar c() const { return c_; } Scalar& s() { return s_; } Scalar s() const { return s_; } ... \end{lstlisting} \vspace{.1cm} The class provide two basic operations: making a Jacobi rotation and applying this rotation on both the right and left side of a selfadjoint $2\times2$ matrix; making a Givens rotation and applying this rotation on the left side of a vector. \vspace{.1cm} \begin{lstlisting}[deletekeywords={[3] x, y, z}] template bool JacobiRotation::makeJacobi(RealScalar x, Scalar y, RealScalar z); template void JacobiRotation::makeGivens(const Scalar& p, const Scalar& q, Scalar* z); \end{lstlisting} \vspace{.1cm} \displayInfos{library=eigenSolvers, test={test\_EigenSolverUtils.cpp}, header dep={Traits.hpp}} \subsubsection{The {\classtitle NumTraits} class} {\class NumTraits} greatly facilitate the management of the sort of extra parameters that come up during the implementation of eigensolver algorithm. \begin{lstlisting} template struct NumTraits {}; template<> struct NumTraits { enum { IsComplex = 0 }; static inline bool isReal() { return true;} static inline bool isComplex() { return false;} static inline Real zero() { return Real(0.0);} static inline Real one() { return Real(1.0);} static inline Real imag(const Real& v) { return v;} static inline Real real(const Real& v) { return v;} static inline Real value(const Real& v) { return v;} static inline Real abs2(const Real& v) { return v*v;} static inline Real epsilon() { return std::numeric_limits::epsilon(); } typedef Real RealScalar; typedef Complex ComplexScalar; }; template<> struct NumTraits { enum { IsComplex = 1 }; static inline bool isReal() {return false;} static inline bool isComplex() { return true;} static inline Complex zero() { return Complex(0.0,0.0);} static inline Complex one() { return Complex(1.0,0.0);} static inline Real imag(const Complex& v) { return v.imag();} static inline Real real(const Complex& v) { return v.real();} static inline Complex value(const Complex& v) { return v;} static inline Real abs2(const Complex& v) { return v.real()*v.real() + v.imag()*v.imag();} typedef Real RealScalar; typedef Complex ComplexScalar; }; \end{lstlisting} \vspace{.1cm} \displayInfos{library=eigenSolvers, test={test\_EigenSolverUtils.cpp}, header dep={utils.h}} \subsection{{\libtitle decomposition} part} All classes of this section utilize functions provided from the lower level - {\class utils} - to implement various decomposition algorithms, which play an important role in solving different eigen problems. \subsubsection{The {\classtitle Tridiagonalization} class} Class {\class Tridiagonalization} performs a tridiagonal decomposition of a self-adjoint matrix $ A$ such that: $A = Q T Q^* $ where $Q$ is unitary and $ T $ a real symmetric tridiagonal matrix. A tridiagonal matrix is a matrix which has non-zero elements only on the main diagonal and the first diagonal below and above it. This class is used in SelfAdjointEigenSolver to compute the eigenvalues and eigenvectors of a selfadjoint matrix. \vspace{.1cm} \begin{lstlisting}[deletekeywords={[3] size}] template class Tridiagonalization { public: /** \brief Synonym for the template parameter \p _MatrixType. */ typedef _MatrixType MatrixType; typedef typename MatrixType::type_t Scalar; typedef typename NumTraits::RealScalar RealScalar; typedef VectorEigenDense CoeffVectorType; typedef VectorEigenDense DiagonalType; ... Tridiagonalization(Dimen size); Tridiagonalization(const MatrixType& matrix); ... \end{lstlisting} \vspace{.1cm} This class comes with default constructor with size of the matrix whose tridiagonal decomposition will be computed. In fact, this parameter is just a hint. After being created, object of this class can be used to calculate any square {\em dense} matrix with \vspace{.1cm} \begin{lstlisting}[]{} Tridiagonalization& compute(const MatrixType& matrix); \end{lstlisting} \vspace{.1cm} The unitary matrix $Q$ can only be invoked after calling the {\itshape compute}: \vspace{.1cm} \begin{lstlisting}[]{} MatrixType matrixQ(); \end{lstlisting} \vspace{.1cm} "Householder coefficients" can be retrieved by \vspace{.1cm} \begin{lstlisting}[]{} inline CoeffVectorType householderCoefficients() const; \end{lstlisting} \vspace{.1cm} We don't need the full-form of matrix $T$ in computation of eigenvalues; however its real diagonal and its real sub-diagonal are essential in calculating eigenvalues of the matrix. These two values can be retrieved with: \vspace{.1cm} \begin{lstlisting}[]{} DiagonalReturnType diagonal() const; SubDiagonalReturnType subDiagonal() const; \end{lstlisting} \vspace{.1cm} For example, the following $5\times 5$ real symmetric matrix \[ A=\left[ \begin{array}{cccccc} 1.36 & -0.816 & 0.521 & 1.43 & -0.144\\ -0.816 & -0.659 & 0.794 & -0.173 & -0.406 \\ 0.521 & 0.794 & -0.541 & 0.461& 0.179 \\ 1.43& -0.173 & 0.461& -1.43 & 0.822 \\ -0.144 & -0.406 & 0.179 & 0.822& -1.37 \end{array} \right] \] after being tridiagonalized, has diagonal and sub-diagonal vectors\\ \mbox{}\hspace{10 mm} diagonal = ( 1.36 -0.659 -0.541 -1.43 -1.37 ) \\ \mbox{}\hspace{10 mm} sub-diagonal = ( -0.816 0.794 0.461 0.822 ) \displayInfos{library=eigenSolvers, test={test\_EigenSolverDecomposition.cpp}, header dep={Traits.hpp, VectorEigenDense.hpp, HouseHolderSequence.hpp}} \subsubsection{The {\classtitle HessenbergDecomposition} class } Class {\class HessenbergDecomposition} performs an Hessenberg decomposition of a matrix $ A $. In the real case, the Hessenberg decomposition consists of an orthogonal matrix $ Q $ and a Hessenberg matrix $ H $ such that $ A = Q H Q^T $. An orthogonal matrix is a matrix whose inverse equals its transpose ($ Q^{-1} = Q^T $). A Hessenberg matrix has zeros below the subdiagonal, so it is almost upper triangular. The Hessenberg decomposition of a complex matrix is $ A = Q H Q^* $ with $ Q $ unitary (that is, $ Q^{-1} = Q^* $). \\ \vspace{.1cm} \begin{lstlisting}[deletekeywords={[3] size}] template class HessenbergDecomposition { public: /** \brief Synonym for the template parameter \p _MatrixType. */ typedef _MatrixType MatrixType; /** \brief Scalar type for matrices of type #MatrixType. */ typedef typename MatrixType::type_t Scalar; /** \brief Type for vector of Householder coefficients. * * This is column vector with entries of type #Scalar. The length of the * vector is one less than the size of #MatrixType, */ typedef VectorEigenDense CoeffVectorType; /** \brief Return type of matrixQ() */ typedef typename HouseholderSequence::ConjugateReturnType HouseholderSequenceType; /** \brief Default constructor; the decomposition will be computed later. * * \param [in] size The size of the matrix whose Hessenberg decomposition will be computed. * * The default constructor is useful in cases in which the user intends to * perform decompositions via compute(). The \p size parameter is only * used as a hint. It is not an error to give a wrong \p size, but it may * impair performance. * * \sa compute() for an example. */ HessenbergDecomposition(Number size) : matrix_(size,size), isInitialized_(false) { if(size>1) hCoeffs_.resize(size-1); } ... \end{lstlisting} \vspace{.1cm} We can use the constructor which computes the Hessenberg decomposition at construction time. \vspace{.1cm} \begin{lstlisting}[]{} HessenbergDecomposition(const MatrixType&); \end{lstlisting} \vspace{.1cm} Once the decomposition is computed, we can use the {\cmd matrixH()} and {\cmd matrixQ()} functions to construct the matrices $H$ and $Q$ in the decomposition. \vspace{.1cm} \begin{lstlisting}[]{} MatrixType matrixQ(); MatrixType matrixH() const; \end{lstlisting} \vspace{.1cm} Alternatively, calling the function {\cmd compute()} to compute the Hessenberg decomposition of a given matrix. then we can use these two functions above \vspace{.1cm} \begin{lstlisting}[]{} HessenbergDecomposition& compute(const MatrixType& matrix); \end{lstlisting} \vspace{.1cm} For instance, the following $5\times 5$ real symmetric matrix \[ A=\left[ \begin{array}{cccccc} 1.36 & -0.816 & 0.521 & 1.43 & -0.144\\ -0.816 & -0.659 & 0.794 & -0.173 & -0.406 \\ 0.521 & 0.794 & -0.541 & 0.461& 0.179 \\ 1.43& -0.173 & 0.461& -1.43 & 0.822 \\ -0.144 & -0.406 & 0.179 & 0.822& -1.37 \end{array} \right] \] has Hessenberg decomposition \footnotesize \[ H=\left[ \begin{array}{cccccc} 1.36 & 1.7328972849 & 0 & 0 & 0 \\ 1.7328972849& -1.1933420269& -0.96533126903& -9.1846580094e-17& 2.021583842e-16 \\ 0 & -0.96533126903& -1.2812029745& 0.21380585197& 1.9428902931e-16 \\ 0 & 0 & 0.21380585197 & -1.6885671008& 0.34719629211 \\ 0 & 0 & 0& 0.34719629211& 0.16311210222 \end{array} \right] \] \normalsize and unitary matrix \footnotesize \[ Q=\left[ \begin{array}{cccccc} 1 & 0 & 0 & 0 & 0\\ 0& -0.47088769029& 0.12629876626& -0.67227759101& -0.55709626223 \\ 0& 0.30065255716& -0.19453390601& 0.43672469898 & -0.82524913607 \\ 0& 0.82520759451& 0.045097442878& -0.56297063107& -0.0079192904815 \\ 0 & -0.083097827699& -0.97168482632& -0.20094388887& 0.092438643767 \end{array} \right] \] \normalsize \displayInfos{library=eigenSolvers, test={test\_EigenSolverDecomposition.cpp}, header dep={Traits.hpp, VectorEigenDense.hpp}} \subsubsection{The {\classtitle RealSchur} class } Class {\class RealSchur} performs a real Schur decomposition of a square matrix. Given a real square matrix $A$, this class computes the real Schur decomposition: $ A = U T U^T $ where $U$ is a real orthogonal matrix and $T$ is a real quasi-triangular matrix. An orthogonal matrix is a matrix whose inverse is equal to its transpose, $ U^{-1} = U^T $. A quasi-triangular matrix is a block-triangular matrix whose diagonal consists of 1-by-1 blocks and 2-by-2 blocks with complex eigenvalues. The eigenvalues of the blocks on the diagonal of $T$ are the same as the eigenvalues of the matrix $A$, and thus the real Schur decomposition is used in {\class RealEigenSolver} to compute the eigendecomposition of a matrix. \vspace{.1cm} \begin{lstlisting}[deletekeywords={[3] size}] template class RealSchur { public: typedef _MatrixType MatrixType; typedef typename MatrixType::type_t Scalar; typedef typename NumTraits::ComplexScalar ComplexScalar; typedef VectorEigenDense EigenvalueType; typedef VectorEigenDense ColumnVectorType; /** \brief Default constructor. * * \param [in] size Positive integer, size of the matrix whose Schur decomposition will be computed. * * The default constructor is useful in cases in which the user intends to * perform decompositions via compute(). The \p size parameter is only * used as a hint. It is not an error to give a wrong \p size, but it may * impair performance. * * \sa compute() for an example. */ RealSchur(Index size) : matT_(size, size), matU_(size, size), hess_(size), maxIterations_(40), isInitialized_(false), matUisUptodate_(false), info_(_noConvergence) { } ... \end{lstlisting} \vspace{.1cm} We can use the constructor which computes the real Schur decomposition at construction time. \vspace{.1cm} \begin{lstlisting}[]{} RealSchur(const MatrixType& matrix, bool computeU = true); \end{lstlisting} \vspace{.1cm} Once the decomposition is computed, we can use the {\cmd matrixU()} and {\cmd matrixT()} functions to retrieve the matrices $U$ and $T$ in the decomposition. \vspace{.1cm} \begin{lstlisting}[]{} MatrixType matrixU() const; MatrixType matrixT() const; \end{lstlisting} \vspace{.1cm} Alternatively calling the function {\cmd compute()} to compute the real Schur decomposition of a given matrix, after that we are able to use these two functions above. \\ The implementation of Schur decomposition of real square matrix is adapted from \url{http://math.nist.gov/javanumerics/jama/}, whose code is based on \eispack. The Schur decomposition is computed by first reducing the matrix to $Hessenberg$ form using the class {\class HessenbergDecomposition}. The $Hessenberg$ matrix is then reduced to triangular form by performing $Francis QR$ iterations with implicit double shift.\\ There are several auxiliary functions in order to implement this algorithm : \vspace{.1cm} \begin{lstlisting}[deletekeywords={[3] norm}] Scalar computeNormOfT(); Index findSmallSubdiagEntry(Index iu, Scalar norm); void splitOffTwoRows(Index iu, bool computeU, Scalar exshift); void computeShift(Index iu, Index iter, Scalar& exshift, Vector3s& shiftInfo); void initFrancisQRStep(Index il, Index iu, const Vector3s& shiftInfo, Index& im, Vector3s& firstHouseholderVector); void performFrancisQRStep(Index il, Index im, Index iu, bool computeU, const Vector3s& firstHouseholderVector); \end{lstlisting} \vspace{.1cm} For instance, the following $5\times 5$ non symmetric matrix \[ A=\left[ \begin{array}{cccccc} 1.8779& -0.5583& -0.2099& 0.2696& 0.1097\\ 0.9407& -0.3114& -1.6989& 0.4943& 1.1287 \\ 0.7873& -0.57& 0.6076& -1.4831& -0.29 \\ -0.8759& -1.0257& -0.1178& -1.0203& 1.2616 \\ 0.3199& -0.9087& 0.6992& -0.447& 0.4754 \end{array} \right] \] has the quasi-triangular matrix in the Schur decomposition \footnotesize \[ H=\left[ \begin{array}{cccccc} 1.6552531332& -0.30767059895 & -0.45435806468 & 1.5602779282 & 0.68358491675\\ 0 & 1.1891303949 & 1.9306398993 & 0.96785347442 & 0.57970343469 \\ 0 & -1.3969331077& 0.22387492903 & -0.78380738142& 1.27402357 \\ 0 & 0 & 0 & 0.092450608769& -0.5563858236 \\ 0 & 0 & 0 & 0 & -1.5315090659 \end{array} \right] \] \normalsize and the orthogonal matrix in the Schur decomposition. \footnotesize \[ Q=\left[ \begin{array}{cccccc} 0.72665944996& 0.28908942548& -0.090578168382& 0.61658006012& 0.0042394246225 \\ 0.13365823926& 0.75252664471 & -0.11720311204 & -0.52512343394& -0.35545454525 \\ 0.50462706182 & -0.59105620177& -0.20754951129& -0.34476029525& -0.4838053679 \\ -0.1068784212 & 0.010284495663 & 0.747282602 & 0.23512571367& -0.61217305049 \\ 0.43362431945& -0.026033604674& 0.61363746549 & -0.41222645012& 0.51459752796 \end{array} \right] \] \normalsize \displayInfos{library=eigenSolvers, test={test\_EigenSolverDecomposition.cpp}, header dep={Jacobi.hpp, VectorEigenDense.hpp, HessenbergDecomposition.hpp}} \subsection{{\libtitle eigenSolver} main part} In this section, there are solvers for three kinds of eigen problem : \begin{itemize} \item Hermitian eigenproblem \item Generalized Hermitian eigenproblem \item Non-Hermitian (real) eigenproblem \end{itemize} The following presents eigen solver classes corresponding to each kind of eigen problem. \subsubsection{The {\classtitle SelfAdjointEigenSolver} class} Class {\class SelfAdjointEigenSolver} computes eigenvalues and eigenvectors of self-adjoint matrices. A matrix $A$ is selfadjoint if it equals its adjoint. For real matrices, this means that the matrix is symmetric: it equals its transpose. \\ This class computes the eigenvalues and eigenvectors of a self-adjoint matrix. These are the scalars $ \lambda $ and vectors $ v $ such that $ Av = \lambda v $. The eigenvalues of a self-adjoint matrix are always real. If $ D $ is a diagonal matrix with the eigenvalues on the diagonal, and $ V $ is a matrix with the eigenvectors as its columns, then $ A = V D V^{-1} $ (for self-adjoint matrices, the matrix $ V $ is always invertible). This is called the eigendecomposition. The implemented algorithm exploits the fact that the matrix is self-adjoint, making it faster and more accurate than the general purpose eigenvalue algorithms implemented in RealEigenSolver.\\ \vspace{.1cm} \begin{lstlisting} template class SelfAdjointEigenSolver { public: typedef _MatrixType MatrixType; /** \brief Scalar type for matrices of type \p _MatrixType. */ typedef typename MatrixType::type_t Scalar; /** \brief Real scalar type for \p _MatrixType. * * This is just \c Scalar if #Scalar is real (e.g., \c float or * \c double), and the type of the real part of \c Scalar if #Scalar is * complex. */ typedef typename NumTraits::RealScalar RealScalar; /** \brief Type for vector of eigenvalues as returned by eigenvalues(). * * This is a column vector with entries of type #RealScalar. * The length of the vector is the size of \p _MatrixType. */ typedef VectorEigenDense RealVectorType; typedef Tridiagonalization TridiagonalizationType; /** \brief Default constructor for fixed-size matrices. * * The default constructor is useful in cases in which the user intends to * perform decompositions via compute(). * */ SelfAdjointEigenSolver() : eivec_(), eivalues_(), subdiag_(), maxIterations_(30), isInitialized_(false) { } ... \end{lstlisting} \vspace{.1cm} We can use the constructor which computes the eigenvalues and eigenvectors at construction time. \vspace{.1cm} \begin{lstlisting}[]{} SelfAdjointEigenSolver(const MatrixType& matrix, Int options = _computeEigenVector); \end{lstlisting} \vspace{.1cm} Once the eigenvalue and eigenvectors are computed, they can be retrieved with \vspace{.1cm} \begin{lstlisting}[]{} const RealVectorType& eigenvalues() const; const MatrixType& eigenvectors() const; \end{lstlisting} \vspace{.1cm} Alternatively, we can call the function {\cmd compute()} to compute the eigenvalues and eigenvectors of a given matrix. \vspace{.1cm} \begin{lstlisting}[]{} SelfAdjointEigenSolver& compute(const MatrixType& matrix, Int options = _computeEigenVector); \end{lstlisting} \vspace{.1cm} This implementation uses a symmetric QR algorithm. The matrix is first reduced to tridiagonal form using the {\class Tridiagonalization} class. The tridiagonal matrix is then brought to diagonal form with implicit symmetric QR steps with Wilkinson shift.\\ For instance, the following $5\times 5$ real symmetric matrix \[ A=\left[ \begin{array}{cccccc} 1.36 & -0.816 & 0.521 & 1.43 & -0.144\\ -0.816 & -0.659 & 0.794 & -0.173 & -0.406 \\ 0.521 & 0.794 & -0.541 & 0.461& 0.179 \\ 1.43& -0.173 & 0.461& -1.43 & 0.822 \\ -0.144 & -0.406 & 0.179 & 0.822& -1.37 \end{array} \right] \] has eigenvalues and corresponding (column) eigenvector\\ \mbox{}\hspace{10 mm} eigenvalues = ( -2.64925 -1.76682 -0.742793 0.22723 2.29163 ) \\ \footnotesize \[ \mbox{\normalsize eigenvectors =} \left[ \begin{array}{cccccc} -0.32648814917& -0.098193931162& 0.34709885718& -0.010999922777& 0.87359305479 \\ -0.20806598152& -0.64229394812& 0.22733262857 & 0.66233712946 & -0.23194058392 \\ 0.050094522003 & 0.62960023585& -0.16376394055 & 0.73987891396& 0.16387387324 \\ 0.72069515152 & -0.39678270513 & -0.40156098976& 0.11452959441& 0.38573789934 \\ -0.57289010572& -0.15486595502& -0.79985773898& -0.025508690809 & 0.085967242812 \end{array} \right] \] \normalsize \displayInfos{library=eigenSolvers, test={test\_EigenSolverInternCore.cpp}, header dep={VectorEigenDense.hpp, Tridiagonalization.hpp}} \subsubsection{The {\classtitle GeneralizedSelfAdjointEigenSolver} class} Class {\class GeneralizedSelfAdjointEigenSolver} computes eigenvalues and eigenvectors of the generalized self-adjoint eigen problem. \\ This class solves the generalized eigenvalue problem $ Av = \lambda Bv $. In this case, the matrix $ A $ should be self-adjoint and the matrix $ B $ should be positive definite. \vspace{.1cm} \begin{lstlisting}[deletekeywords={[3] size}] template class GeneralizedSelfAdjointEigenSolver : public SelfAdjointEigenSolver<_MatrixType> { typedef SelfAdjointEigenSolver<_MatrixType> Base; public: typedef _MatrixType MatrixType; /** \brief Default constructor for fixed-size matrices. * * The default constructor is useful in cases in which the user intends to * perform decompositions via compute(). */ GeneralizedSelfAdjointEigenSolver() : Base() {} /** \brief Constructor, pre-allocates memory for dynamic-size matrices. * * \param [in] size Positive integer, size of the matrix whose * eigenvalues and eigenvectors will be computed. * * \sa compute() for an example */ GeneralizedSelfAdjointEigenSolver(Index size) : Base(size) {} ... \end{lstlisting} \vspace{.1cm} We can use the constructor which computes the eigenvalues and eigenvectors at construction time. \vspace{.1cm} \begin{lstlisting}[]{} GeneralizedSelfAdjointEigenSolver(const MatrixType& matA, const MatrixType& matB, int options = _computeEigenVector|_Ax_lBx) : Base(matA.numOfCols()); \end{lstlisting} \vspace{.1cm} Alternatively, we can call the function {\itshape compute()} to compute the eigenvalues and eigenvectors of a given matrix. \vspace{.1cm} \begin{lstlisting}[]{} GeneralizedSelfAdjointEigenSolver& compute(const MatrixType& matA, const MatrixType& matB, int options = _computeEigenVector|_Ax_lBx); \end{lstlisting} \vspace{.1cm} Eigenvalues and eigenvectors can be retrieved with same functions of {\class SelfAdjointEigenSolver} \vspace{.1cm} \begin{lstlisting}[]{} const RealVectorType& eigenvalues() const; const MatrixType& eigenvectors() const; \end{lstlisting} \vspace{.1cm} For instance, the following $5\times 5$ real symmetric matrix $A$ and positive definite matrix $B$ \[ A=\left[ \begin{array}{cccccc} 1.36 & -0.816 & 0.521 & 1.43 & -0.144\\ -0.816 & -0.659 & 0.794 & -0.173 & -0.406 \\ 0.521 & 0.794 & -0.541 & 0.461& 0.179 \\ 1.43& -0.173 & 0.461& -1.43 & 0.822 \\ -0.144 & -0.406 & 0.179 & 0.822& -1.37 \end{array} \right] \] \[ B=\left[ \begin{array}{cccccc} 1.1981& 1.4405& 1.5367& 1.0971& 1.1276\\ 1.4405& 2.1207& 1.9115& 1.3836& 1.642\\ 1.5367& 1.9115& 2.4828& 1.7702& 1.7543\\ 1.0971& 1.3836& 1.7702& 1.4359& 1.2428\\ 1.1276& 1.642& 1.7543& 1.2428& 1.424 \end{array} \right] \] has eigenvalues and corresponding (column) eigenvector\\ \mbox{}\hspace{10 mm} eigenvalues = ( -40.0667 -12.6587 -3.08602 0.0522151 40.3448 ) \\ \footnotesize \[ \mbox{\normalsize eigenvectors =} \left[ \begin{array}{cccccc} -2.5241477328& -0.01326907506 & 0.83333293087 & -0.040348533316 & 4.3939678718\\ 3.7607674197 & 0.3488243247 & 1.1073133113 & -0.3174355818 & -4.3303408407 \\ 3.0396247156 & 2.615639864 & -0.34676637691 & -0.36756457169 & -3.9258019948 \\ 0.45843793898 & -2.0051996759 & -0.65863554474 & -0.039469282032 & 1.4762595546 \\ -6.6376554636 & -1.9381010232 & -0.97022016252 & 0.070809133211 & 4.9718019236 \end{array} \right] \] \normalsize \displayInfos{library=eigenSolvers, test={test\_EigenSolverInternCore.cpp}, header dep={Tridiagonalization.hpp}} \subsubsection{The {\classtitle RealEigenSolver} class} Class {\class RealEigenSolver} computes eigenvalues and eigenvectors of general real matrices. \\ The eigenvalues and eigenvectors of a matrix $ A $ are scalars $ \lambda $ and vectors $ v $ such that $ Av = \lambda v $. If $ D $ is a diagonal matrix with the eigenvalues on the diagonal, and $ V $ is a matrix with the eigenvectors as its columns, then $ A V = V D $. The matrix $ V $ is almost always invertible, in which case we have $ A = V D V^{-1} $. \\ The eigenvalues and eigenvectors of the matrix may be complex, even when the matrix is real. However, we can choose real matrices $ V $ and $ D $ satisfying $ A V = V D $, just like the eigendecomposition, if the matrix $ D $ is not required to be diagonal, but if it is allowed to have blocks of the form\[ \begin{bmatrix} u & v \\ -v & u \end{bmatrix} \] (where $ u $ and $ v $ are real numbers) on the diagonal. These blocks correspond to complex eigenvalue pairs $ u \pm iv $. We call this variant of the eigendecomposition the pseudo-eigendecomposition. \vspace{.1cm} \begin{lstlisting}[deletekeywords={[3] size}] template class RealEigenSolver { public: /** \brief Synonym for the template parameter \p _MatrixType. */ typedef _MatrixType MatrixType; /** \brief Scalar type for matrices of type #MatrixType. */ typedef typename MatrixType::type_t Scalar; typedef typename NumTraits::RealScalar RealScalar; typedef VectorEigenDense ColumnVectorType; typedef VectorEigenDense VectorType; /** \brief Complex scalar type for #MatrixType. * * This is \c std::complex if #Scalar is real (e.g., * \c float or \c double) and just \c Scalar if #Scalar is * complex. */ typedef typename NumTraits::ComplexScalar ComplexScalar; typedef VectorEigenDense ComplexVectorType; /** \brief Type for vector of eigenvalues as returned by eigenvalues(). * * This is a column vector with entries of type #ComplexScalar. * The length of the vector is the size of #MatrixType. */ typedef VectorEigenDense EigenvalueType; /** \brief Type for matrix of eigenvectors as returned by eigenvectors(). * * This is a square matrix with entries of type #ComplexScalar. * The size is the same as the size of #MatrixType. */ typedef MatrixEigenDense EigenvectorsType; /** \brief Default constructor. * * The default constructor is useful in cases in which the user intends to * perform decompositions via RealEigenSolver::compute(const MatrixType&, bool). * * \sa compute() for an example. */ RealEigenSolver() : eivec_(), eivalues_(), isInitialized_(false), realSchur_(), matT_() {} /** * \brief Default constructor with memory preallocation * * Like the default constructor but with preallocation of the internal data * according to the specified problem \a size. * \sa RealEigenSolver() */ RealEigenSolver(Index size) : eivec_(size, size), eivalues_(size), isInitialized_(false), eigenvectorsOk_(false), realSchur_(size), matT_(size, size) {} ... \end{lstlisting} \vspace{.1cm} Same as other two eigensolvers, we can use the constructor which computes the eigenvalues and eigenvectors at construction time. \vspace{.1cm} \begin{lstlisting}[]{} RealEigenSolver(const MatrixType& matrix, bool computeEigenvectors = true); \end{lstlisting} \vspace{.1cm} Once the eigenvalue and eigenvectors are computed, they can be retrieved with \vspace{.1cm} \begin{lstlisting}[]{} const EigenvalueType& eigenvalues() const; EigenvectorsType eigenvectors() const; \end{lstlisting} \vspace{.1cm} Alternatively, we can call the function {\cmd compute()} to compute the eigenvalues and eigenvectors of a given matrix. \vspace{.1cm} \begin{lstlisting}[]{} RealEigenSolver& compute(const MatrixType& matrix, bool computeEigenvectors = true); \end{lstlisting} \vspace{.1cm} This implementation first reduces the matrix to real Schur form using the {\class RealSchur} class. The Schur decomposition is then used to compute the eigenvalues and eigenvectors.\\ For instance, the following $5\times 5$ non symmetric matrix \[ A=\left[ \begin{array}{cccccc} 1.8779& -0.5583& -0.2099& 0.2696& 0.1097\\ 0.9407& -0.3114& -1.6989& 0.4943& 1.1287 \\ 0.7873& -0.57& 0.6076& -1.4831& -0.29 \\ -0.8759& -1.0257& -0.1178& -1.0203& 1.2616 \\ 0.3199& -0.9087& 0.6992& -0.447& 0.4754 \end{array} \right] \] has eigenvalues and corresponding (column) eigenvector\\ \mbox{}\hspace{10 mm} eigenvalues = ( (1.65525,0) (0.706503,1.56973) (0.706503,-1.56973) (0.0924506,0) (-1.53151,0) ) \\ \[ \mbox{eigenvectors =} \left[ \begin{array}{ccccc} (0.727,0)& (0.177,-0.001)& (0.177,0.001)& (-0.230,0)& (-0.041,0) \\ (0.133,0)& (0.534,-0.218)& (0.534,0.218)& (-0.664,0)& (-0.392,0) \\ (0.504,0)& (-0.444,0.0814) & (-0.444,-0.0814)& (-0.336,0)& (-0.587,0)\\ (-0.106,0)& (0.011,0.457)& (0.0118,-0.457)& (0.135,0)& (-0.695,0) \\ (0.433,0)& (-0.036,0.469)& (-0.036,-0.469)& (-0.611,0)& (-0.121,0) \end{array} \right] \] \displayInfos{library=eigenSolvers, test={test\_EigenSolverInternCore.cpp}, header dep={VectorEigenDense.hpp, MatrixEigenDense.hpp, RealSchur.hpp}}