1*bf2c3715SXin Li // This file is part of Eigen, a lightweight C++ template library 2*bf2c3715SXin Li // for linear algebra. 3*bf2c3715SXin Li // 4*bf2c3715SXin Li // Copyright (C) 2012 Alexey Korepanov <[email protected]> 5*bf2c3715SXin Li // 6*bf2c3715SXin Li // This Source Code Form is subject to the terms of the Mozilla 7*bf2c3715SXin Li // Public License v. 2.0. If a copy of the MPL was not distributed 8*bf2c3715SXin Li // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. 9*bf2c3715SXin Li 10*bf2c3715SXin Li #ifndef EIGEN_REAL_QZ_H 11*bf2c3715SXin Li #define EIGEN_REAL_QZ_H 12*bf2c3715SXin Li 13*bf2c3715SXin Li namespace Eigen { 14*bf2c3715SXin Li 15*bf2c3715SXin Li /** \eigenvalues_module \ingroup Eigenvalues_Module 16*bf2c3715SXin Li * 17*bf2c3715SXin Li * 18*bf2c3715SXin Li * \class RealQZ 19*bf2c3715SXin Li * 20*bf2c3715SXin Li * \brief Performs a real QZ decomposition of a pair of square matrices 21*bf2c3715SXin Li * 22*bf2c3715SXin Li * \tparam _MatrixType the type of the matrix of which we are computing the 23*bf2c3715SXin Li * real QZ decomposition; this is expected to be an instantiation of the 24*bf2c3715SXin Li * Matrix class template. 25*bf2c3715SXin Li * 26*bf2c3715SXin Li * Given a real square matrices A and B, this class computes the real QZ 27*bf2c3715SXin Li * decomposition: \f$ A = Q S Z \f$, \f$ B = Q T Z \f$ where Q and Z are 28*bf2c3715SXin Li * real orthogonal matrixes, T is upper-triangular matrix, and S is upper 29*bf2c3715SXin Li * quasi-triangular matrix. An orthogonal matrix is a matrix whose 30*bf2c3715SXin Li * inverse is equal to its transpose, \f$ U^{-1} = U^T \f$. A quasi-triangular 31*bf2c3715SXin Li * matrix is a block-triangular matrix whose diagonal consists of 1-by-1 32*bf2c3715SXin Li * blocks and 2-by-2 blocks where further reduction is impossible due to 33*bf2c3715SXin Li * complex eigenvalues. 34*bf2c3715SXin Li * 35*bf2c3715SXin Li * The eigenvalues of the pencil \f$ A - z B \f$ can be obtained from 36*bf2c3715SXin Li * 1x1 and 2x2 blocks on the diagonals of S and T. 37*bf2c3715SXin Li * 38*bf2c3715SXin Li * Call the function compute() to compute the real QZ decomposition of a 39*bf2c3715SXin Li * given pair of matrices. Alternatively, you can use the 40*bf2c3715SXin Li * RealQZ(const MatrixType& B, const MatrixType& B, bool computeQZ) 41*bf2c3715SXin Li * constructor which computes the real QZ decomposition at construction 42*bf2c3715SXin Li * time. Once the decomposition is computed, you can use the matrixS(), 43*bf2c3715SXin Li * matrixT(), matrixQ() and matrixZ() functions to retrieve the matrices 44*bf2c3715SXin Li * S, T, Q and Z in the decomposition. If computeQZ==false, some time 45*bf2c3715SXin Li * is saved by not computing matrices Q and Z. 46*bf2c3715SXin Li * 47*bf2c3715SXin Li * Example: \include RealQZ_compute.cpp 48*bf2c3715SXin Li * Output: \include RealQZ_compute.out 49*bf2c3715SXin Li * 50*bf2c3715SXin Li * \note The implementation is based on the algorithm in "Matrix Computations" 51*bf2c3715SXin Li * by Gene H. Golub and Charles F. Van Loan, and a paper "An algorithm for 52*bf2c3715SXin Li * generalized eigenvalue problems" by C.B.Moler and G.W.Stewart. 53*bf2c3715SXin Li * 54*bf2c3715SXin Li * \sa class RealSchur, class ComplexSchur, class EigenSolver, class ComplexEigenSolver 55*bf2c3715SXin Li */ 56*bf2c3715SXin Li 57*bf2c3715SXin Li template<typename _MatrixType> class RealQZ 58*bf2c3715SXin Li { 59*bf2c3715SXin Li public: 60*bf2c3715SXin Li typedef _MatrixType MatrixType; 61*bf2c3715SXin Li enum { 62*bf2c3715SXin Li RowsAtCompileTime = MatrixType::RowsAtCompileTime, 63*bf2c3715SXin Li ColsAtCompileTime = MatrixType::ColsAtCompileTime, 64*bf2c3715SXin Li Options = MatrixType::Options, 65*bf2c3715SXin Li MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime, 66*bf2c3715SXin Li MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime 67*bf2c3715SXin Li }; 68*bf2c3715SXin Li typedef typename MatrixType::Scalar Scalar; 69*bf2c3715SXin Li typedef std::complex<typename NumTraits<Scalar>::Real> ComplexScalar; 70*bf2c3715SXin Li typedef Eigen::Index Index; ///< \deprecated since Eigen 3.3 71*bf2c3715SXin Li 72*bf2c3715SXin Li typedef Matrix<ComplexScalar, ColsAtCompileTime, 1, Options & ~RowMajor, MaxColsAtCompileTime, 1> EigenvalueType; 73*bf2c3715SXin Li typedef Matrix<Scalar, ColsAtCompileTime, 1, Options & ~RowMajor, MaxColsAtCompileTime, 1> ColumnVectorType; 74*bf2c3715SXin Li 75*bf2c3715SXin Li /** \brief Default constructor. 76*bf2c3715SXin Li * 77*bf2c3715SXin Li * \param [in] size Positive integer, size of the matrix whose QZ decomposition will be computed. 78*bf2c3715SXin Li * 79*bf2c3715SXin Li * The default constructor is useful in cases in which the user intends to 80*bf2c3715SXin Li * perform decompositions via compute(). The \p size parameter is only 81*bf2c3715SXin Li * used as a hint. It is not an error to give a wrong \p size, but it may 82*bf2c3715SXin Li * impair performance. 83*bf2c3715SXin Li * 84*bf2c3715SXin Li * \sa compute() for an example. 85*bf2c3715SXin Li */ 86*bf2c3715SXin Li explicit RealQZ(Index size = RowsAtCompileTime==Dynamic ? 1 : RowsAtCompileTime) : m_S(size,size)87*bf2c3715SXin Li m_S(size, size), 88*bf2c3715SXin Li m_T(size, size), 89*bf2c3715SXin Li m_Q(size, size), 90*bf2c3715SXin Li m_Z(size, size), 91*bf2c3715SXin Li m_workspace(size*2), 92*bf2c3715SXin Li m_maxIters(400), 93*bf2c3715SXin Li m_isInitialized(false), 94*bf2c3715SXin Li m_computeQZ(true) 95*bf2c3715SXin Li {} 96*bf2c3715SXin Li 97*bf2c3715SXin Li /** \brief Constructor; computes real QZ decomposition of given matrices 98*bf2c3715SXin Li * 99*bf2c3715SXin Li * \param[in] A Matrix A. 100*bf2c3715SXin Li * \param[in] B Matrix B. 101*bf2c3715SXin Li * \param[in] computeQZ If false, A and Z are not computed. 102*bf2c3715SXin Li * 103*bf2c3715SXin Li * This constructor calls compute() to compute the QZ decomposition. 104*bf2c3715SXin Li */ 105*bf2c3715SXin Li RealQZ(const MatrixType& A, const MatrixType& B, bool computeQZ = true) : 106*bf2c3715SXin Li m_S(A.rows(),A.cols()), 107*bf2c3715SXin Li m_T(A.rows(),A.cols()), 108*bf2c3715SXin Li m_Q(A.rows(),A.cols()), 109*bf2c3715SXin Li m_Z(A.rows(),A.cols()), 110*bf2c3715SXin Li m_workspace(A.rows()*2), 111*bf2c3715SXin Li m_maxIters(400), 112*bf2c3715SXin Li m_isInitialized(false), 113*bf2c3715SXin Li m_computeQZ(true) 114*bf2c3715SXin Li { 115*bf2c3715SXin Li compute(A, B, computeQZ); 116*bf2c3715SXin Li } 117*bf2c3715SXin Li 118*bf2c3715SXin Li /** \brief Returns matrix Q in the QZ decomposition. 119*bf2c3715SXin Li * 120*bf2c3715SXin Li * \returns A const reference to the matrix Q. 121*bf2c3715SXin Li */ matrixQ()122*bf2c3715SXin Li const MatrixType& matrixQ() const { 123*bf2c3715SXin Li eigen_assert(m_isInitialized && "RealQZ is not initialized."); 124*bf2c3715SXin Li eigen_assert(m_computeQZ && "The matrices Q and Z have not been computed during the QZ decomposition."); 125*bf2c3715SXin Li return m_Q; 126*bf2c3715SXin Li } 127*bf2c3715SXin Li 128*bf2c3715SXin Li /** \brief Returns matrix Z in the QZ decomposition. 129*bf2c3715SXin Li * 130*bf2c3715SXin Li * \returns A const reference to the matrix Z. 131*bf2c3715SXin Li */ matrixZ()132*bf2c3715SXin Li const MatrixType& matrixZ() const { 133*bf2c3715SXin Li eigen_assert(m_isInitialized && "RealQZ is not initialized."); 134*bf2c3715SXin Li eigen_assert(m_computeQZ && "The matrices Q and Z have not been computed during the QZ decomposition."); 135*bf2c3715SXin Li return m_Z; 136*bf2c3715SXin Li } 137*bf2c3715SXin Li 138*bf2c3715SXin Li /** \brief Returns matrix S in the QZ decomposition. 139*bf2c3715SXin Li * 140*bf2c3715SXin Li * \returns A const reference to the matrix S. 141*bf2c3715SXin Li */ matrixS()142*bf2c3715SXin Li const MatrixType& matrixS() const { 143*bf2c3715SXin Li eigen_assert(m_isInitialized && "RealQZ is not initialized."); 144*bf2c3715SXin Li return m_S; 145*bf2c3715SXin Li } 146*bf2c3715SXin Li 147*bf2c3715SXin Li /** \brief Returns matrix S in the QZ decomposition. 148*bf2c3715SXin Li * 149*bf2c3715SXin Li * \returns A const reference to the matrix S. 150*bf2c3715SXin Li */ matrixT()151*bf2c3715SXin Li const MatrixType& matrixT() const { 152*bf2c3715SXin Li eigen_assert(m_isInitialized && "RealQZ is not initialized."); 153*bf2c3715SXin Li return m_T; 154*bf2c3715SXin Li } 155*bf2c3715SXin Li 156*bf2c3715SXin Li /** \brief Computes QZ decomposition of given matrix. 157*bf2c3715SXin Li * 158*bf2c3715SXin Li * \param[in] A Matrix A. 159*bf2c3715SXin Li * \param[in] B Matrix B. 160*bf2c3715SXin Li * \param[in] computeQZ If false, A and Z are not computed. 161*bf2c3715SXin Li * \returns Reference to \c *this 162*bf2c3715SXin Li */ 163*bf2c3715SXin Li RealQZ& compute(const MatrixType& A, const MatrixType& B, bool computeQZ = true); 164*bf2c3715SXin Li 165*bf2c3715SXin Li /** \brief Reports whether previous computation was successful. 166*bf2c3715SXin Li * 167*bf2c3715SXin Li * \returns \c Success if computation was successful, \c NoConvergence otherwise. 168*bf2c3715SXin Li */ info()169*bf2c3715SXin Li ComputationInfo info() const 170*bf2c3715SXin Li { 171*bf2c3715SXin Li eigen_assert(m_isInitialized && "RealQZ is not initialized."); 172*bf2c3715SXin Li return m_info; 173*bf2c3715SXin Li } 174*bf2c3715SXin Li 175*bf2c3715SXin Li /** \brief Returns number of performed QR-like iterations. 176*bf2c3715SXin Li */ iterations()177*bf2c3715SXin Li Index iterations() const 178*bf2c3715SXin Li { 179*bf2c3715SXin Li eigen_assert(m_isInitialized && "RealQZ is not initialized."); 180*bf2c3715SXin Li return m_global_iter; 181*bf2c3715SXin Li } 182*bf2c3715SXin Li 183*bf2c3715SXin Li /** Sets the maximal number of iterations allowed to converge to one eigenvalue 184*bf2c3715SXin Li * or decouple the problem. 185*bf2c3715SXin Li */ setMaxIterations(Index maxIters)186*bf2c3715SXin Li RealQZ& setMaxIterations(Index maxIters) 187*bf2c3715SXin Li { 188*bf2c3715SXin Li m_maxIters = maxIters; 189*bf2c3715SXin Li return *this; 190*bf2c3715SXin Li } 191*bf2c3715SXin Li 192*bf2c3715SXin Li private: 193*bf2c3715SXin Li 194*bf2c3715SXin Li MatrixType m_S, m_T, m_Q, m_Z; 195*bf2c3715SXin Li Matrix<Scalar,Dynamic,1> m_workspace; 196*bf2c3715SXin Li ComputationInfo m_info; 197*bf2c3715SXin Li Index m_maxIters; 198*bf2c3715SXin Li bool m_isInitialized; 199*bf2c3715SXin Li bool m_computeQZ; 200*bf2c3715SXin Li Scalar m_normOfT, m_normOfS; 201*bf2c3715SXin Li Index m_global_iter; 202*bf2c3715SXin Li 203*bf2c3715SXin Li typedef Matrix<Scalar,3,1> Vector3s; 204*bf2c3715SXin Li typedef Matrix<Scalar,2,1> Vector2s; 205*bf2c3715SXin Li typedef Matrix<Scalar,2,2> Matrix2s; 206*bf2c3715SXin Li typedef JacobiRotation<Scalar> JRs; 207*bf2c3715SXin Li 208*bf2c3715SXin Li void hessenbergTriangular(); 209*bf2c3715SXin Li void computeNorms(); 210*bf2c3715SXin Li Index findSmallSubdiagEntry(Index iu); 211*bf2c3715SXin Li Index findSmallDiagEntry(Index f, Index l); 212*bf2c3715SXin Li void splitOffTwoRows(Index i); 213*bf2c3715SXin Li void pushDownZero(Index z, Index f, Index l); 214*bf2c3715SXin Li void step(Index f, Index l, Index iter); 215*bf2c3715SXin Li 216*bf2c3715SXin Li }; // RealQZ 217*bf2c3715SXin Li 218*bf2c3715SXin Li /** \internal Reduces S and T to upper Hessenberg - triangular form */ 219*bf2c3715SXin Li template<typename MatrixType> hessenbergTriangular()220*bf2c3715SXin Li void RealQZ<MatrixType>::hessenbergTriangular() 221*bf2c3715SXin Li { 222*bf2c3715SXin Li 223*bf2c3715SXin Li const Index dim = m_S.cols(); 224*bf2c3715SXin Li 225*bf2c3715SXin Li // perform QR decomposition of T, overwrite T with R, save Q 226*bf2c3715SXin Li HouseholderQR<MatrixType> qrT(m_T); 227*bf2c3715SXin Li m_T = qrT.matrixQR(); 228*bf2c3715SXin Li m_T.template triangularView<StrictlyLower>().setZero(); 229*bf2c3715SXin Li m_Q = qrT.householderQ(); 230*bf2c3715SXin Li // overwrite S with Q* S 231*bf2c3715SXin Li m_S.applyOnTheLeft(m_Q.adjoint()); 232*bf2c3715SXin Li // init Z as Identity 233*bf2c3715SXin Li if (m_computeQZ) 234*bf2c3715SXin Li m_Z = MatrixType::Identity(dim,dim); 235*bf2c3715SXin Li // reduce S to upper Hessenberg with Givens rotations 236*bf2c3715SXin Li for (Index j=0; j<=dim-3; j++) { 237*bf2c3715SXin Li for (Index i=dim-1; i>=j+2; i--) { 238*bf2c3715SXin Li JRs G; 239*bf2c3715SXin Li // kill S(i,j) 240*bf2c3715SXin Li if(m_S.coeff(i,j) != 0) 241*bf2c3715SXin Li { 242*bf2c3715SXin Li G.makeGivens(m_S.coeff(i-1,j), m_S.coeff(i,j), &m_S.coeffRef(i-1, j)); 243*bf2c3715SXin Li m_S.coeffRef(i,j) = Scalar(0.0); 244*bf2c3715SXin Li m_S.rightCols(dim-j-1).applyOnTheLeft(i-1,i,G.adjoint()); 245*bf2c3715SXin Li m_T.rightCols(dim-i+1).applyOnTheLeft(i-1,i,G.adjoint()); 246*bf2c3715SXin Li // update Q 247*bf2c3715SXin Li if (m_computeQZ) 248*bf2c3715SXin Li m_Q.applyOnTheRight(i-1,i,G); 249*bf2c3715SXin Li } 250*bf2c3715SXin Li // kill T(i,i-1) 251*bf2c3715SXin Li if(m_T.coeff(i,i-1)!=Scalar(0)) 252*bf2c3715SXin Li { 253*bf2c3715SXin Li G.makeGivens(m_T.coeff(i,i), m_T.coeff(i,i-1), &m_T.coeffRef(i,i)); 254*bf2c3715SXin Li m_T.coeffRef(i,i-1) = Scalar(0.0); 255*bf2c3715SXin Li m_S.applyOnTheRight(i,i-1,G); 256*bf2c3715SXin Li m_T.topRows(i).applyOnTheRight(i,i-1,G); 257*bf2c3715SXin Li // update Z 258*bf2c3715SXin Li if (m_computeQZ) 259*bf2c3715SXin Li m_Z.applyOnTheLeft(i,i-1,G.adjoint()); 260*bf2c3715SXin Li } 261*bf2c3715SXin Li } 262*bf2c3715SXin Li } 263*bf2c3715SXin Li } 264*bf2c3715SXin Li 265*bf2c3715SXin Li /** \internal Computes vector L1 norms of S and T when in Hessenberg-Triangular form already */ 266*bf2c3715SXin Li template<typename MatrixType> computeNorms()267*bf2c3715SXin Li inline void RealQZ<MatrixType>::computeNorms() 268*bf2c3715SXin Li { 269*bf2c3715SXin Li const Index size = m_S.cols(); 270*bf2c3715SXin Li m_normOfS = Scalar(0.0); 271*bf2c3715SXin Li m_normOfT = Scalar(0.0); 272*bf2c3715SXin Li for (Index j = 0; j < size; ++j) 273*bf2c3715SXin Li { 274*bf2c3715SXin Li m_normOfS += m_S.col(j).segment(0, (std::min)(size,j+2)).cwiseAbs().sum(); 275*bf2c3715SXin Li m_normOfT += m_T.row(j).segment(j, size - j).cwiseAbs().sum(); 276*bf2c3715SXin Li } 277*bf2c3715SXin Li } 278*bf2c3715SXin Li 279*bf2c3715SXin Li 280*bf2c3715SXin Li /** \internal Look for single small sub-diagonal element S(res, res-1) and return res (or 0) */ 281*bf2c3715SXin Li template<typename MatrixType> findSmallSubdiagEntry(Index iu)282*bf2c3715SXin Li inline Index RealQZ<MatrixType>::findSmallSubdiagEntry(Index iu) 283*bf2c3715SXin Li { 284*bf2c3715SXin Li using std::abs; 285*bf2c3715SXin Li Index res = iu; 286*bf2c3715SXin Li while (res > 0) 287*bf2c3715SXin Li { 288*bf2c3715SXin Li Scalar s = abs(m_S.coeff(res-1,res-1)) + abs(m_S.coeff(res,res)); 289*bf2c3715SXin Li if (s == Scalar(0.0)) 290*bf2c3715SXin Li s = m_normOfS; 291*bf2c3715SXin Li if (abs(m_S.coeff(res,res-1)) < NumTraits<Scalar>::epsilon() * s) 292*bf2c3715SXin Li break; 293*bf2c3715SXin Li res--; 294*bf2c3715SXin Li } 295*bf2c3715SXin Li return res; 296*bf2c3715SXin Li } 297*bf2c3715SXin Li 298*bf2c3715SXin Li /** \internal Look for single small diagonal element T(res, res) for res between f and l, and return res (or f-1) */ 299*bf2c3715SXin Li template<typename MatrixType> findSmallDiagEntry(Index f,Index l)300*bf2c3715SXin Li inline Index RealQZ<MatrixType>::findSmallDiagEntry(Index f, Index l) 301*bf2c3715SXin Li { 302*bf2c3715SXin Li using std::abs; 303*bf2c3715SXin Li Index res = l; 304*bf2c3715SXin Li while (res >= f) { 305*bf2c3715SXin Li if (abs(m_T.coeff(res,res)) <= NumTraits<Scalar>::epsilon() * m_normOfT) 306*bf2c3715SXin Li break; 307*bf2c3715SXin Li res--; 308*bf2c3715SXin Li } 309*bf2c3715SXin Li return res; 310*bf2c3715SXin Li } 311*bf2c3715SXin Li 312*bf2c3715SXin Li /** \internal decouple 2x2 diagonal block in rows i, i+1 if eigenvalues are real */ 313*bf2c3715SXin Li template<typename MatrixType> splitOffTwoRows(Index i)314*bf2c3715SXin Li inline void RealQZ<MatrixType>::splitOffTwoRows(Index i) 315*bf2c3715SXin Li { 316*bf2c3715SXin Li using std::abs; 317*bf2c3715SXin Li using std::sqrt; 318*bf2c3715SXin Li const Index dim=m_S.cols(); 319*bf2c3715SXin Li if (abs(m_S.coeff(i+1,i))==Scalar(0)) 320*bf2c3715SXin Li return; 321*bf2c3715SXin Li Index j = findSmallDiagEntry(i,i+1); 322*bf2c3715SXin Li if (j==i-1) 323*bf2c3715SXin Li { 324*bf2c3715SXin Li // block of (S T^{-1}) 325*bf2c3715SXin Li Matrix2s STi = m_T.template block<2,2>(i,i).template triangularView<Upper>(). 326*bf2c3715SXin Li template solve<OnTheRight>(m_S.template block<2,2>(i,i)); 327*bf2c3715SXin Li Scalar p = Scalar(0.5)*(STi(0,0)-STi(1,1)); 328*bf2c3715SXin Li Scalar q = p*p + STi(1,0)*STi(0,1); 329*bf2c3715SXin Li if (q>=0) { 330*bf2c3715SXin Li Scalar z = sqrt(q); 331*bf2c3715SXin Li // one QR-like iteration for ABi - lambda I 332*bf2c3715SXin Li // is enough - when we know exact eigenvalue in advance, 333*bf2c3715SXin Li // convergence is immediate 334*bf2c3715SXin Li JRs G; 335*bf2c3715SXin Li if (p>=0) 336*bf2c3715SXin Li G.makeGivens(p + z, STi(1,0)); 337*bf2c3715SXin Li else 338*bf2c3715SXin Li G.makeGivens(p - z, STi(1,0)); 339*bf2c3715SXin Li m_S.rightCols(dim-i).applyOnTheLeft(i,i+1,G.adjoint()); 340*bf2c3715SXin Li m_T.rightCols(dim-i).applyOnTheLeft(i,i+1,G.adjoint()); 341*bf2c3715SXin Li // update Q 342*bf2c3715SXin Li if (m_computeQZ) 343*bf2c3715SXin Li m_Q.applyOnTheRight(i,i+1,G); 344*bf2c3715SXin Li 345*bf2c3715SXin Li G.makeGivens(m_T.coeff(i+1,i+1), m_T.coeff(i+1,i)); 346*bf2c3715SXin Li m_S.topRows(i+2).applyOnTheRight(i+1,i,G); 347*bf2c3715SXin Li m_T.topRows(i+2).applyOnTheRight(i+1,i,G); 348*bf2c3715SXin Li // update Z 349*bf2c3715SXin Li if (m_computeQZ) 350*bf2c3715SXin Li m_Z.applyOnTheLeft(i+1,i,G.adjoint()); 351*bf2c3715SXin Li 352*bf2c3715SXin Li m_S.coeffRef(i+1,i) = Scalar(0.0); 353*bf2c3715SXin Li m_T.coeffRef(i+1,i) = Scalar(0.0); 354*bf2c3715SXin Li } 355*bf2c3715SXin Li } 356*bf2c3715SXin Li else 357*bf2c3715SXin Li { 358*bf2c3715SXin Li pushDownZero(j,i,i+1); 359*bf2c3715SXin Li } 360*bf2c3715SXin Li } 361*bf2c3715SXin Li 362*bf2c3715SXin Li /** \internal use zero in T(z,z) to zero S(l,l-1), working in block f..l */ 363*bf2c3715SXin Li template<typename MatrixType> pushDownZero(Index z,Index f,Index l)364*bf2c3715SXin Li inline void RealQZ<MatrixType>::pushDownZero(Index z, Index f, Index l) 365*bf2c3715SXin Li { 366*bf2c3715SXin Li JRs G; 367*bf2c3715SXin Li const Index dim = m_S.cols(); 368*bf2c3715SXin Li for (Index zz=z; zz<l; zz++) 369*bf2c3715SXin Li { 370*bf2c3715SXin Li // push 0 down 371*bf2c3715SXin Li Index firstColS = zz>f ? (zz-1) : zz; 372*bf2c3715SXin Li G.makeGivens(m_T.coeff(zz, zz+1), m_T.coeff(zz+1, zz+1)); 373*bf2c3715SXin Li m_S.rightCols(dim-firstColS).applyOnTheLeft(zz,zz+1,G.adjoint()); 374*bf2c3715SXin Li m_T.rightCols(dim-zz).applyOnTheLeft(zz,zz+1,G.adjoint()); 375*bf2c3715SXin Li m_T.coeffRef(zz+1,zz+1) = Scalar(0.0); 376*bf2c3715SXin Li // update Q 377*bf2c3715SXin Li if (m_computeQZ) 378*bf2c3715SXin Li m_Q.applyOnTheRight(zz,zz+1,G); 379*bf2c3715SXin Li // kill S(zz+1, zz-1) 380*bf2c3715SXin Li if (zz>f) 381*bf2c3715SXin Li { 382*bf2c3715SXin Li G.makeGivens(m_S.coeff(zz+1, zz), m_S.coeff(zz+1,zz-1)); 383*bf2c3715SXin Li m_S.topRows(zz+2).applyOnTheRight(zz, zz-1,G); 384*bf2c3715SXin Li m_T.topRows(zz+1).applyOnTheRight(zz, zz-1,G); 385*bf2c3715SXin Li m_S.coeffRef(zz+1,zz-1) = Scalar(0.0); 386*bf2c3715SXin Li // update Z 387*bf2c3715SXin Li if (m_computeQZ) 388*bf2c3715SXin Li m_Z.applyOnTheLeft(zz,zz-1,G.adjoint()); 389*bf2c3715SXin Li } 390*bf2c3715SXin Li } 391*bf2c3715SXin Li // finally kill S(l,l-1) 392*bf2c3715SXin Li G.makeGivens(m_S.coeff(l,l), m_S.coeff(l,l-1)); 393*bf2c3715SXin Li m_S.applyOnTheRight(l,l-1,G); 394*bf2c3715SXin Li m_T.applyOnTheRight(l,l-1,G); 395*bf2c3715SXin Li m_S.coeffRef(l,l-1)=Scalar(0.0); 396*bf2c3715SXin Li // update Z 397*bf2c3715SXin Li if (m_computeQZ) 398*bf2c3715SXin Li m_Z.applyOnTheLeft(l,l-1,G.adjoint()); 399*bf2c3715SXin Li } 400*bf2c3715SXin Li 401*bf2c3715SXin Li /** \internal QR-like iterative step for block f..l */ 402*bf2c3715SXin Li template<typename MatrixType> step(Index f,Index l,Index iter)403*bf2c3715SXin Li inline void RealQZ<MatrixType>::step(Index f, Index l, Index iter) 404*bf2c3715SXin Li { 405*bf2c3715SXin Li using std::abs; 406*bf2c3715SXin Li const Index dim = m_S.cols(); 407*bf2c3715SXin Li 408*bf2c3715SXin Li // x, y, z 409*bf2c3715SXin Li Scalar x, y, z; 410*bf2c3715SXin Li if (iter==10) 411*bf2c3715SXin Li { 412*bf2c3715SXin Li // Wilkinson ad hoc shift 413*bf2c3715SXin Li const Scalar 414*bf2c3715SXin Li a11=m_S.coeff(f+0,f+0), a12=m_S.coeff(f+0,f+1), 415*bf2c3715SXin Li a21=m_S.coeff(f+1,f+0), a22=m_S.coeff(f+1,f+1), a32=m_S.coeff(f+2,f+1), 416*bf2c3715SXin Li b12=m_T.coeff(f+0,f+1), 417*bf2c3715SXin Li b11i=Scalar(1.0)/m_T.coeff(f+0,f+0), 418*bf2c3715SXin Li b22i=Scalar(1.0)/m_T.coeff(f+1,f+1), 419*bf2c3715SXin Li a87=m_S.coeff(l-1,l-2), 420*bf2c3715SXin Li a98=m_S.coeff(l-0,l-1), 421*bf2c3715SXin Li b77i=Scalar(1.0)/m_T.coeff(l-2,l-2), 422*bf2c3715SXin Li b88i=Scalar(1.0)/m_T.coeff(l-1,l-1); 423*bf2c3715SXin Li Scalar ss = abs(a87*b77i) + abs(a98*b88i), 424*bf2c3715SXin Li lpl = Scalar(1.5)*ss, 425*bf2c3715SXin Li ll = ss*ss; 426*bf2c3715SXin Li x = ll + a11*a11*b11i*b11i - lpl*a11*b11i + a12*a21*b11i*b22i 427*bf2c3715SXin Li - a11*a21*b12*b11i*b11i*b22i; 428*bf2c3715SXin Li y = a11*a21*b11i*b11i - lpl*a21*b11i + a21*a22*b11i*b22i 429*bf2c3715SXin Li - a21*a21*b12*b11i*b11i*b22i; 430*bf2c3715SXin Li z = a21*a32*b11i*b22i; 431*bf2c3715SXin Li } 432*bf2c3715SXin Li else if (iter==16) 433*bf2c3715SXin Li { 434*bf2c3715SXin Li // another exceptional shift 435*bf2c3715SXin Li x = m_S.coeff(f,f)/m_T.coeff(f,f)-m_S.coeff(l,l)/m_T.coeff(l,l) + m_S.coeff(l,l-1)*m_T.coeff(l-1,l) / 436*bf2c3715SXin Li (m_T.coeff(l-1,l-1)*m_T.coeff(l,l)); 437*bf2c3715SXin Li y = m_S.coeff(f+1,f)/m_T.coeff(f,f); 438*bf2c3715SXin Li z = 0; 439*bf2c3715SXin Li } 440*bf2c3715SXin Li else if (iter>23 && !(iter%8)) 441*bf2c3715SXin Li { 442*bf2c3715SXin Li // extremely exceptional shift 443*bf2c3715SXin Li x = internal::random<Scalar>(-1.0,1.0); 444*bf2c3715SXin Li y = internal::random<Scalar>(-1.0,1.0); 445*bf2c3715SXin Li z = internal::random<Scalar>(-1.0,1.0); 446*bf2c3715SXin Li } 447*bf2c3715SXin Li else 448*bf2c3715SXin Li { 449*bf2c3715SXin Li // Compute the shifts: (x,y,z,0...) = (AB^-1 - l1 I) (AB^-1 - l2 I) e1 450*bf2c3715SXin Li // where l1 and l2 are the eigenvalues of the 2x2 matrix C = U V^-1 where 451*bf2c3715SXin Li // U and V are 2x2 bottom right sub matrices of A and B. Thus: 452*bf2c3715SXin Li // = AB^-1AB^-1 + l1 l2 I - (l1+l2)(AB^-1) 453*bf2c3715SXin Li // = AB^-1AB^-1 + det(M) - tr(M)(AB^-1) 454*bf2c3715SXin Li // Since we are only interested in having x, y, z with a correct ratio, we have: 455*bf2c3715SXin Li const Scalar 456*bf2c3715SXin Li a11 = m_S.coeff(f,f), a12 = m_S.coeff(f,f+1), 457*bf2c3715SXin Li a21 = m_S.coeff(f+1,f), a22 = m_S.coeff(f+1,f+1), 458*bf2c3715SXin Li a32 = m_S.coeff(f+2,f+1), 459*bf2c3715SXin Li 460*bf2c3715SXin Li a88 = m_S.coeff(l-1,l-1), a89 = m_S.coeff(l-1,l), 461*bf2c3715SXin Li a98 = m_S.coeff(l,l-1), a99 = m_S.coeff(l,l), 462*bf2c3715SXin Li 463*bf2c3715SXin Li b11 = m_T.coeff(f,f), b12 = m_T.coeff(f,f+1), 464*bf2c3715SXin Li b22 = m_T.coeff(f+1,f+1), 465*bf2c3715SXin Li 466*bf2c3715SXin Li b88 = m_T.coeff(l-1,l-1), b89 = m_T.coeff(l-1,l), 467*bf2c3715SXin Li b99 = m_T.coeff(l,l); 468*bf2c3715SXin Li 469*bf2c3715SXin Li x = ( (a88/b88 - a11/b11)*(a99/b99 - a11/b11) - (a89/b99)*(a98/b88) + (a98/b88)*(b89/b99)*(a11/b11) ) * (b11/a21) 470*bf2c3715SXin Li + a12/b22 - (a11/b11)*(b12/b22); 471*bf2c3715SXin Li y = (a22/b22-a11/b11) - (a21/b11)*(b12/b22) - (a88/b88-a11/b11) - (a99/b99-a11/b11) + (a98/b88)*(b89/b99); 472*bf2c3715SXin Li z = a32/b22; 473*bf2c3715SXin Li } 474*bf2c3715SXin Li 475*bf2c3715SXin Li JRs G; 476*bf2c3715SXin Li 477*bf2c3715SXin Li for (Index k=f; k<=l-2; k++) 478*bf2c3715SXin Li { 479*bf2c3715SXin Li // variables for Householder reflections 480*bf2c3715SXin Li Vector2s essential2; 481*bf2c3715SXin Li Scalar tau, beta; 482*bf2c3715SXin Li 483*bf2c3715SXin Li Vector3s hr(x,y,z); 484*bf2c3715SXin Li 485*bf2c3715SXin Li // Q_k to annihilate S(k+1,k-1) and S(k+2,k-1) 486*bf2c3715SXin Li hr.makeHouseholderInPlace(tau, beta); 487*bf2c3715SXin Li essential2 = hr.template bottomRows<2>(); 488*bf2c3715SXin Li Index fc=(std::max)(k-1,Index(0)); // first col to update 489*bf2c3715SXin Li m_S.template middleRows<3>(k).rightCols(dim-fc).applyHouseholderOnTheLeft(essential2, tau, m_workspace.data()); 490*bf2c3715SXin Li m_T.template middleRows<3>(k).rightCols(dim-fc).applyHouseholderOnTheLeft(essential2, tau, m_workspace.data()); 491*bf2c3715SXin Li if (m_computeQZ) 492*bf2c3715SXin Li m_Q.template middleCols<3>(k).applyHouseholderOnTheRight(essential2, tau, m_workspace.data()); 493*bf2c3715SXin Li if (k>f) 494*bf2c3715SXin Li m_S.coeffRef(k+2,k-1) = m_S.coeffRef(k+1,k-1) = Scalar(0.0); 495*bf2c3715SXin Li 496*bf2c3715SXin Li // Z_{k1} to annihilate T(k+2,k+1) and T(k+2,k) 497*bf2c3715SXin Li hr << m_T.coeff(k+2,k+2),m_T.coeff(k+2,k),m_T.coeff(k+2,k+1); 498*bf2c3715SXin Li hr.makeHouseholderInPlace(tau, beta); 499*bf2c3715SXin Li essential2 = hr.template bottomRows<2>(); 500*bf2c3715SXin Li { 501*bf2c3715SXin Li Index lr = (std::min)(k+4,dim); // last row to update 502*bf2c3715SXin Li Map<Matrix<Scalar,Dynamic,1> > tmp(m_workspace.data(),lr); 503*bf2c3715SXin Li // S 504*bf2c3715SXin Li tmp = m_S.template middleCols<2>(k).topRows(lr) * essential2; 505*bf2c3715SXin Li tmp += m_S.col(k+2).head(lr); 506*bf2c3715SXin Li m_S.col(k+2).head(lr) -= tau*tmp; 507*bf2c3715SXin Li m_S.template middleCols<2>(k).topRows(lr) -= (tau*tmp) * essential2.adjoint(); 508*bf2c3715SXin Li // T 509*bf2c3715SXin Li tmp = m_T.template middleCols<2>(k).topRows(lr) * essential2; 510*bf2c3715SXin Li tmp += m_T.col(k+2).head(lr); 511*bf2c3715SXin Li m_T.col(k+2).head(lr) -= tau*tmp; 512*bf2c3715SXin Li m_T.template middleCols<2>(k).topRows(lr) -= (tau*tmp) * essential2.adjoint(); 513*bf2c3715SXin Li } 514*bf2c3715SXin Li if (m_computeQZ) 515*bf2c3715SXin Li { 516*bf2c3715SXin Li // Z 517*bf2c3715SXin Li Map<Matrix<Scalar,1,Dynamic> > tmp(m_workspace.data(),dim); 518*bf2c3715SXin Li tmp = essential2.adjoint()*(m_Z.template middleRows<2>(k)); 519*bf2c3715SXin Li tmp += m_Z.row(k+2); 520*bf2c3715SXin Li m_Z.row(k+2) -= tau*tmp; 521*bf2c3715SXin Li m_Z.template middleRows<2>(k) -= essential2 * (tau*tmp); 522*bf2c3715SXin Li } 523*bf2c3715SXin Li m_T.coeffRef(k+2,k) = m_T.coeffRef(k+2,k+1) = Scalar(0.0); 524*bf2c3715SXin Li 525*bf2c3715SXin Li // Z_{k2} to annihilate T(k+1,k) 526*bf2c3715SXin Li G.makeGivens(m_T.coeff(k+1,k+1), m_T.coeff(k+1,k)); 527*bf2c3715SXin Li m_S.applyOnTheRight(k+1,k,G); 528*bf2c3715SXin Li m_T.applyOnTheRight(k+1,k,G); 529*bf2c3715SXin Li // update Z 530*bf2c3715SXin Li if (m_computeQZ) 531*bf2c3715SXin Li m_Z.applyOnTheLeft(k+1,k,G.adjoint()); 532*bf2c3715SXin Li m_T.coeffRef(k+1,k) = Scalar(0.0); 533*bf2c3715SXin Li 534*bf2c3715SXin Li // update x,y,z 535*bf2c3715SXin Li x = m_S.coeff(k+1,k); 536*bf2c3715SXin Li y = m_S.coeff(k+2,k); 537*bf2c3715SXin Li if (k < l-2) 538*bf2c3715SXin Li z = m_S.coeff(k+3,k); 539*bf2c3715SXin Li } // loop over k 540*bf2c3715SXin Li 541*bf2c3715SXin Li // Q_{n-1} to annihilate y = S(l,l-2) 542*bf2c3715SXin Li G.makeGivens(x,y); 543*bf2c3715SXin Li m_S.applyOnTheLeft(l-1,l,G.adjoint()); 544*bf2c3715SXin Li m_T.applyOnTheLeft(l-1,l,G.adjoint()); 545*bf2c3715SXin Li if (m_computeQZ) 546*bf2c3715SXin Li m_Q.applyOnTheRight(l-1,l,G); 547*bf2c3715SXin Li m_S.coeffRef(l,l-2) = Scalar(0.0); 548*bf2c3715SXin Li 549*bf2c3715SXin Li // Z_{n-1} to annihilate T(l,l-1) 550*bf2c3715SXin Li G.makeGivens(m_T.coeff(l,l),m_T.coeff(l,l-1)); 551*bf2c3715SXin Li m_S.applyOnTheRight(l,l-1,G); 552*bf2c3715SXin Li m_T.applyOnTheRight(l,l-1,G); 553*bf2c3715SXin Li if (m_computeQZ) 554*bf2c3715SXin Li m_Z.applyOnTheLeft(l,l-1,G.adjoint()); 555*bf2c3715SXin Li m_T.coeffRef(l,l-1) = Scalar(0.0); 556*bf2c3715SXin Li } 557*bf2c3715SXin Li 558*bf2c3715SXin Li template<typename MatrixType> compute(const MatrixType & A_in,const MatrixType & B_in,bool computeQZ)559*bf2c3715SXin Li RealQZ<MatrixType>& RealQZ<MatrixType>::compute(const MatrixType& A_in, const MatrixType& B_in, bool computeQZ) 560*bf2c3715SXin Li { 561*bf2c3715SXin Li 562*bf2c3715SXin Li const Index dim = A_in.cols(); 563*bf2c3715SXin Li 564*bf2c3715SXin Li eigen_assert (A_in.rows()==dim && A_in.cols()==dim 565*bf2c3715SXin Li && B_in.rows()==dim && B_in.cols()==dim 566*bf2c3715SXin Li && "Need square matrices of the same dimension"); 567*bf2c3715SXin Li 568*bf2c3715SXin Li m_isInitialized = true; 569*bf2c3715SXin Li m_computeQZ = computeQZ; 570*bf2c3715SXin Li m_S = A_in; m_T = B_in; 571*bf2c3715SXin Li m_workspace.resize(dim*2); 572*bf2c3715SXin Li m_global_iter = 0; 573*bf2c3715SXin Li 574*bf2c3715SXin Li // entrance point: hessenberg triangular decomposition 575*bf2c3715SXin Li hessenbergTriangular(); 576*bf2c3715SXin Li // compute L1 vector norms of T, S into m_normOfS, m_normOfT 577*bf2c3715SXin Li computeNorms(); 578*bf2c3715SXin Li 579*bf2c3715SXin Li Index l = dim-1, 580*bf2c3715SXin Li f, 581*bf2c3715SXin Li local_iter = 0; 582*bf2c3715SXin Li 583*bf2c3715SXin Li while (l>0 && local_iter<m_maxIters) 584*bf2c3715SXin Li { 585*bf2c3715SXin Li f = findSmallSubdiagEntry(l); 586*bf2c3715SXin Li // now rows and columns f..l (including) decouple from the rest of the problem 587*bf2c3715SXin Li if (f>0) m_S.coeffRef(f,f-1) = Scalar(0.0); 588*bf2c3715SXin Li if (f == l) // One root found 589*bf2c3715SXin Li { 590*bf2c3715SXin Li l--; 591*bf2c3715SXin Li local_iter = 0; 592*bf2c3715SXin Li } 593*bf2c3715SXin Li else if (f == l-1) // Two roots found 594*bf2c3715SXin Li { 595*bf2c3715SXin Li splitOffTwoRows(f); 596*bf2c3715SXin Li l -= 2; 597*bf2c3715SXin Li local_iter = 0; 598*bf2c3715SXin Li } 599*bf2c3715SXin Li else // No convergence yet 600*bf2c3715SXin Li { 601*bf2c3715SXin Li // if there's zero on diagonal of T, we can isolate an eigenvalue with Givens rotations 602*bf2c3715SXin Li Index z = findSmallDiagEntry(f,l); 603*bf2c3715SXin Li if (z>=f) 604*bf2c3715SXin Li { 605*bf2c3715SXin Li // zero found 606*bf2c3715SXin Li pushDownZero(z,f,l); 607*bf2c3715SXin Li } 608*bf2c3715SXin Li else 609*bf2c3715SXin Li { 610*bf2c3715SXin Li // We are sure now that S.block(f,f, l-f+1,l-f+1) is underuced upper-Hessenberg 611*bf2c3715SXin Li // and T.block(f,f, l-f+1,l-f+1) is invertible uper-triangular, which allows to 612*bf2c3715SXin Li // apply a QR-like iteration to rows and columns f..l. 613*bf2c3715SXin Li step(f,l, local_iter); 614*bf2c3715SXin Li local_iter++; 615*bf2c3715SXin Li m_global_iter++; 616*bf2c3715SXin Li } 617*bf2c3715SXin Li } 618*bf2c3715SXin Li } 619*bf2c3715SXin Li // check if we converged before reaching iterations limit 620*bf2c3715SXin Li m_info = (local_iter<m_maxIters) ? Success : NoConvergence; 621*bf2c3715SXin Li 622*bf2c3715SXin Li // For each non triangular 2x2 diagonal block of S, 623*bf2c3715SXin Li // reduce the respective 2x2 diagonal block of T to positive diagonal form using 2x2 SVD. 624*bf2c3715SXin Li // This step is not mandatory for QZ, but it does help further extraction of eigenvalues/eigenvectors, 625*bf2c3715SXin Li // and is in par with Lapack/Matlab QZ. 626*bf2c3715SXin Li if(m_info==Success) 627*bf2c3715SXin Li { 628*bf2c3715SXin Li for(Index i=0; i<dim-1; ++i) 629*bf2c3715SXin Li { 630*bf2c3715SXin Li if(m_S.coeff(i+1, i) != Scalar(0)) 631*bf2c3715SXin Li { 632*bf2c3715SXin Li JacobiRotation<Scalar> j_left, j_right; 633*bf2c3715SXin Li internal::real_2x2_jacobi_svd(m_T, i, i+1, &j_left, &j_right); 634*bf2c3715SXin Li 635*bf2c3715SXin Li // Apply resulting Jacobi rotations 636*bf2c3715SXin Li m_S.applyOnTheLeft(i,i+1,j_left); 637*bf2c3715SXin Li m_S.applyOnTheRight(i,i+1,j_right); 638*bf2c3715SXin Li m_T.applyOnTheLeft(i,i+1,j_left); 639*bf2c3715SXin Li m_T.applyOnTheRight(i,i+1,j_right); 640*bf2c3715SXin Li m_T(i+1,i) = m_T(i,i+1) = Scalar(0); 641*bf2c3715SXin Li 642*bf2c3715SXin Li if(m_computeQZ) { 643*bf2c3715SXin Li m_Q.applyOnTheRight(i,i+1,j_left.transpose()); 644*bf2c3715SXin Li m_Z.applyOnTheLeft(i,i+1,j_right.transpose()); 645*bf2c3715SXin Li } 646*bf2c3715SXin Li 647*bf2c3715SXin Li i++; 648*bf2c3715SXin Li } 649*bf2c3715SXin Li } 650*bf2c3715SXin Li } 651*bf2c3715SXin Li 652*bf2c3715SXin Li return *this; 653*bf2c3715SXin Li } // end compute 654*bf2c3715SXin Li 655*bf2c3715SXin Li } // end namespace Eigen 656*bf2c3715SXin Li 657*bf2c3715SXin Li #endif //EIGEN_REAL_QZ 658