Home | History | Annotate | Download | only in MatrixFunctions
      1 // This file is part of Eigen, a lightweight C++ template library
      2 // for linear algebra.
      3 //
      4 // Copyright (C) 2012, 2013 Chen-Pang He <jdh8 (at) ms63.hinet.net>
      5 //
      6 // This Source Code Form is subject to the terms of the Mozilla
      7 // Public License v. 2.0. If a copy of the MPL was not distributed
      8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
      9 
     10 #ifndef EIGEN_MATRIX_POWER
     11 #define EIGEN_MATRIX_POWER
     12 
     13 namespace Eigen {
     14 
     15 template<typename MatrixType> class MatrixPower;
     16 
     17 template<typename MatrixType>
     18 class MatrixPowerRetval : public ReturnByValue< MatrixPowerRetval<MatrixType> >
     19 {
     20   public:
     21     typedef typename MatrixType::RealScalar RealScalar;
     22     typedef typename MatrixType::Index Index;
     23 
     24     MatrixPowerRetval(MatrixPower<MatrixType>& pow, RealScalar p) : m_pow(pow), m_p(p)
     25     { }
     26 
     27     template<typename ResultType>
     28     inline void evalTo(ResultType& res) const
     29     { m_pow.compute(res, m_p); }
     30 
     31     Index rows() const { return m_pow.rows(); }
     32     Index cols() const { return m_pow.cols(); }
     33 
     34   private:
     35     MatrixPower<MatrixType>& m_pow;
     36     const RealScalar m_p;
     37     MatrixPowerRetval& operator=(const MatrixPowerRetval&);
     38 };
     39 
     40 template<typename MatrixType>
     41 class MatrixPowerAtomic
     42 {
     43   private:
     44     enum {
     45       RowsAtCompileTime = MatrixType::RowsAtCompileTime,
     46       MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime
     47     };
     48     typedef typename MatrixType::Scalar Scalar;
     49     typedef typename MatrixType::RealScalar RealScalar;
     50     typedef std::complex<RealScalar> ComplexScalar;
     51     typedef typename MatrixType::Index Index;
     52     typedef Array<Scalar, RowsAtCompileTime, 1, ColMajor, MaxRowsAtCompileTime> ArrayType;
     53 
     54     const MatrixType& m_A;
     55     RealScalar m_p;
     56 
     57     void computePade(int degree, const MatrixType& IminusT, MatrixType& res) const;
     58     void compute2x2(MatrixType& res, RealScalar p) const;
     59     void computeBig(MatrixType& res) const;
     60     static int getPadeDegree(float normIminusT);
     61     static int getPadeDegree(double normIminusT);
     62     static int getPadeDegree(long double normIminusT);
     63     static ComplexScalar computeSuperDiag(const ComplexScalar&, const ComplexScalar&, RealScalar p);
     64     static RealScalar computeSuperDiag(RealScalar, RealScalar, RealScalar p);
     65 
     66   public:
     67     MatrixPowerAtomic(const MatrixType& T, RealScalar p);
     68     void compute(MatrixType& res) const;
     69 };
     70 
     71 template<typename MatrixType>
     72 MatrixPowerAtomic<MatrixType>::MatrixPowerAtomic(const MatrixType& T, RealScalar p) :
     73   m_A(T), m_p(p)
     74 { eigen_assert(T.rows() == T.cols()); }
     75 
     76 template<typename MatrixType>
     77 void MatrixPowerAtomic<MatrixType>::compute(MatrixType& res) const
     78 {
     79   res.resizeLike(m_A);
     80   switch (m_A.rows()) {
     81     case 0:
     82       break;
     83     case 1:
     84       res(0,0) = std::pow(m_A(0,0), m_p);
     85       break;
     86     case 2:
     87       compute2x2(res, m_p);
     88       break;
     89     default:
     90       computeBig(res);
     91   }
     92 }
     93 
     94 template<typename MatrixType>
     95 void MatrixPowerAtomic<MatrixType>::computePade(int degree, const MatrixType& IminusT, MatrixType& res) const
     96 {
     97   int i = degree<<1;
     98   res = (m_p-degree) / ((i-1)<<1) * IminusT;
     99   for (--i; i; --i) {
    100     res = (MatrixType::Identity(IminusT.rows(), IminusT.cols()) + res).template triangularView<Upper>()
    101 	.solve((i==1 ? -m_p : i&1 ? (-m_p-(i>>1))/(i<<1) : (m_p-(i>>1))/((i-1)<<1)) * IminusT).eval();
    102   }
    103   res += MatrixType::Identity(IminusT.rows(), IminusT.cols());
    104 }
    105 
    106 // This function assumes that res has the correct size (see bug 614)
    107 template<typename MatrixType>
    108 void MatrixPowerAtomic<MatrixType>::compute2x2(MatrixType& res, RealScalar p) const
    109 {
    110   using std::abs;
    111   using std::pow;
    112 
    113   ArrayType logTdiag = m_A.diagonal().array().log();
    114   res.coeffRef(0,0) = pow(m_A.coeff(0,0), p);
    115 
    116   for (Index i=1; i < m_A.cols(); ++i) {
    117     res.coeffRef(i,i) = pow(m_A.coeff(i,i), p);
    118     if (m_A.coeff(i-1,i-1) == m_A.coeff(i,i))
    119       res.coeffRef(i-1,i) = p * pow(m_A.coeff(i,i), p-1);
    120     else if (2*abs(m_A.coeff(i-1,i-1)) < abs(m_A.coeff(i,i)) || 2*abs(m_A.coeff(i,i)) < abs(m_A.coeff(i-1,i-1)))
    121       res.coeffRef(i-1,i) = (res.coeff(i,i)-res.coeff(i-1,i-1)) / (m_A.coeff(i,i)-m_A.coeff(i-1,i-1));
    122     else
    123       res.coeffRef(i-1,i) = computeSuperDiag(m_A.coeff(i,i), m_A.coeff(i-1,i-1), p);
    124     res.coeffRef(i-1,i) *= m_A.coeff(i-1,i);
    125   }
    126 }
    127 
    128 template<typename MatrixType>
    129 void MatrixPowerAtomic<MatrixType>::computeBig(MatrixType& res) const
    130 {
    131   const int digits = std::numeric_limits<RealScalar>::digits;
    132   const RealScalar maxNormForPade = digits <=  24? 4.3386528e-1f:                           // sigle precision
    133 				    digits <=  53? 2.789358995219730e-1:                    // double precision
    134 				    digits <=  64? 2.4471944416607995472e-1L:               // extended precision
    135 				    digits <= 106? 1.1016843812851143391275867258512e-1L:   // double-double
    136 						   9.134603732914548552537150753385375e-2L; // quadruple precision
    137   MatrixType IminusT, sqrtT, T = m_A.template triangularView<Upper>();
    138   RealScalar normIminusT;
    139   int degree, degree2, numberOfSquareRoots = 0;
    140   bool hasExtraSquareRoot = false;
    141 
    142   /* FIXME
    143    * For singular T, norm(I - T) >= 1 but maxNormForPade < 1, leads to infinite
    144    * loop.  We should move 0 eigenvalues to bottom right corner.  We need not
    145    * worry about tiny values (e.g. 1e-300) because they will reach 1 if
    146    * repetitively sqrt'ed.
    147    *
    148    * If the 0 eigenvalues are semisimple, they can form a 0 matrix at the
    149    * bottom right corner.
    150    *
    151    * [ T  A ]^p   [ T^p  (T^-1 T^p A) ]
    152    * [      ]   = [                   ]
    153    * [ 0  0 ]     [  0         0      ]
    154    */
    155   for (Index i=0; i < m_A.cols(); ++i)
    156     eigen_assert(m_A(i,i) != RealScalar(0));
    157 
    158   while (true) {
    159     IminusT = MatrixType::Identity(m_A.rows(), m_A.cols()) - T;
    160     normIminusT = IminusT.cwiseAbs().colwise().sum().maxCoeff();
    161     if (normIminusT < maxNormForPade) {
    162       degree = getPadeDegree(normIminusT);
    163       degree2 = getPadeDegree(normIminusT/2);
    164       if (degree - degree2 <= 1 || hasExtraSquareRoot)
    165 	break;
    166       hasExtraSquareRoot = true;
    167     }
    168     MatrixSquareRootTriangular<MatrixType>(T).compute(sqrtT);
    169     T = sqrtT.template triangularView<Upper>();
    170     ++numberOfSquareRoots;
    171   }
    172   computePade(degree, IminusT, res);
    173 
    174   for (; numberOfSquareRoots; --numberOfSquareRoots) {
    175     compute2x2(res, std::ldexp(m_p, -numberOfSquareRoots));
    176     res = res.template triangularView<Upper>() * res;
    177   }
    178   compute2x2(res, m_p);
    179 }
    180 
    181 template<typename MatrixType>
    182 inline int MatrixPowerAtomic<MatrixType>::getPadeDegree(float normIminusT)
    183 {
    184   const float maxNormForPade[] = { 2.8064004e-1f /* degree = 3 */ , 4.3386528e-1f };
    185   int degree = 3;
    186   for (; degree <= 4; ++degree)
    187     if (normIminusT <= maxNormForPade[degree - 3])
    188       break;
    189   return degree;
    190 }
    191 
    192 template<typename MatrixType>
    193 inline int MatrixPowerAtomic<MatrixType>::getPadeDegree(double normIminusT)
    194 {
    195   const double maxNormForPade[] = { 1.884160592658218e-2 /* degree = 3 */ , 6.038881904059573e-2, 1.239917516308172e-1,
    196       1.999045567181744e-1, 2.789358995219730e-1 };
    197   int degree = 3;
    198   for (; degree <= 7; ++degree)
    199     if (normIminusT <= maxNormForPade[degree - 3])
    200       break;
    201   return degree;
    202 }
    203 
    204 template<typename MatrixType>
    205 inline int MatrixPowerAtomic<MatrixType>::getPadeDegree(long double normIminusT)
    206 {
    207 #if   LDBL_MANT_DIG == 53
    208   const int maxPadeDegree = 7;
    209   const double maxNormForPade[] = { 1.884160592658218e-2L /* degree = 3 */ , 6.038881904059573e-2L, 1.239917516308172e-1L,
    210       1.999045567181744e-1L, 2.789358995219730e-1L };
    211 #elif LDBL_MANT_DIG <= 64
    212   const int maxPadeDegree = 8;
    213   const double maxNormForPade[] = { 6.3854693117491799460e-3L /* degree = 3 */ , 2.6394893435456973676e-2L,
    214       6.4216043030404063729e-2L, 1.1701165502926694307e-1L, 1.7904284231268670284e-1L, 2.4471944416607995472e-1L };
    215 #elif LDBL_MANT_DIG <= 106
    216   const int maxPadeDegree = 10;
    217   const double maxNormForPade[] = { 1.0007161601787493236741409687186e-4L /* degree = 3 */ ,
    218       1.0007161601787493236741409687186e-3L, 4.7069769360887572939882574746264e-3L, 1.3220386624169159689406653101695e-2L,
    219       2.8063482381631737920612944054906e-2L, 4.9625993951953473052385361085058e-2L, 7.7367040706027886224557538328171e-2L,
    220       1.1016843812851143391275867258512e-1L };
    221 #else
    222   const int maxPadeDegree = 10;
    223   const double maxNormForPade[] = { 5.524506147036624377378713555116378e-5L /* degree = 3 */ ,
    224       6.640600568157479679823602193345995e-4L, 3.227716520106894279249709728084626e-3L,
    225       9.619593944683432960546978734646284e-3L, 2.134595382433742403911124458161147e-2L,
    226       3.908166513900489428442993794761185e-2L, 6.266780814639442865832535460550138e-2L,
    227       9.134603732914548552537150753385375e-2L };
    228 #endif
    229   int degree = 3;
    230   for (; degree <= maxPadeDegree; ++degree)
    231     if (normIminusT <= maxNormForPade[degree - 3])
    232       break;
    233   return degree;
    234 }
    235 
    236 template<typename MatrixType>
    237 inline typename MatrixPowerAtomic<MatrixType>::ComplexScalar
    238 MatrixPowerAtomic<MatrixType>::computeSuperDiag(const ComplexScalar& curr, const ComplexScalar& prev, RealScalar p)
    239 {
    240   ComplexScalar logCurr = std::log(curr);
    241   ComplexScalar logPrev = std::log(prev);
    242   int unwindingNumber = std::ceil((numext::imag(logCurr - logPrev) - M_PI) / (2*M_PI));
    243   ComplexScalar w = numext::atanh2(curr - prev, curr + prev) + ComplexScalar(0, M_PI*unwindingNumber);
    244   return RealScalar(2) * std::exp(RealScalar(0.5) * p * (logCurr + logPrev)) * std::sinh(p * w) / (curr - prev);
    245 }
    246 
    247 template<typename MatrixType>
    248 inline typename MatrixPowerAtomic<MatrixType>::RealScalar
    249 MatrixPowerAtomic<MatrixType>::computeSuperDiag(RealScalar curr, RealScalar prev, RealScalar p)
    250 {
    251   RealScalar w = numext::atanh2(curr - prev, curr + prev);
    252   return 2 * std::exp(p * (std::log(curr) + std::log(prev)) / 2) * std::sinh(p * w) / (curr - prev);
    253 }
    254 
    255 /**
    256  * \ingroup MatrixFunctions_Module
    257  *
    258  * \brief Class for computing matrix powers.
    259  *
    260  * \tparam MatrixType  type of the base, expected to be an instantiation
    261  * of the Matrix class template.
    262  *
    263  * This class is capable of computing real/complex matrices raised to
    264  * an arbitrary real power. Meanwhile, it saves the result of Schur
    265  * decomposition if an non-integral power has even been calculated.
    266  * Therefore, if you want to compute multiple (>= 2) matrix powers
    267  * for the same matrix, using the class directly is more efficient than
    268  * calling MatrixBase::pow().
    269  *
    270  * Example:
    271  * \include MatrixPower_optimal.cpp
    272  * Output: \verbinclude MatrixPower_optimal.out
    273  */
    274 template<typename MatrixType>
    275 class MatrixPower
    276 {
    277   private:
    278     enum {
    279       RowsAtCompileTime = MatrixType::RowsAtCompileTime,
    280       ColsAtCompileTime = MatrixType::ColsAtCompileTime,
    281       MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
    282       MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
    283     };
    284     typedef typename MatrixType::Scalar Scalar;
    285     typedef typename MatrixType::RealScalar RealScalar;
    286     typedef typename MatrixType::Index Index;
    287 
    288   public:
    289     /**
    290      * \brief Constructor.
    291      *
    292      * \param[in] A  the base of the matrix power.
    293      *
    294      * The class stores a reference to A, so it should not be changed
    295      * (or destroyed) before evaluation.
    296      */
    297     explicit MatrixPower(const MatrixType& A) : m_A(A), m_conditionNumber(0)
    298     { eigen_assert(A.rows() == A.cols()); }
    299 
    300     /**
    301      * \brief Returns the matrix power.
    302      *
    303      * \param[in] p  exponent, a real scalar.
    304      * \return The expression \f$ A^p \f$, where A is specified in the
    305      * constructor.
    306      */
    307     const MatrixPowerRetval<MatrixType> operator()(RealScalar p)
    308     { return MatrixPowerRetval<MatrixType>(*this, p); }
    309 
    310     /**
    311      * \brief Compute the matrix power.
    312      *
    313      * \param[in]  p    exponent, a real scalar.
    314      * \param[out] res  \f$ A^p \f$ where A is specified in the
    315      * constructor.
    316      */
    317     template<typename ResultType>
    318     void compute(ResultType& res, RealScalar p);
    319 
    320     Index rows() const { return m_A.rows(); }
    321     Index cols() const { return m_A.cols(); }
    322 
    323   private:
    324     typedef std::complex<RealScalar> ComplexScalar;
    325     typedef Matrix<ComplexScalar, RowsAtCompileTime, ColsAtCompileTime, MatrixType::Options,
    326               MaxRowsAtCompileTime, MaxColsAtCompileTime> ComplexMatrix;
    327 
    328     typename MatrixType::Nested m_A;
    329     MatrixType m_tmp;
    330     ComplexMatrix m_T, m_U, m_fT;
    331     RealScalar m_conditionNumber;
    332 
    333     RealScalar modfAndInit(RealScalar, RealScalar*);
    334 
    335     template<typename ResultType>
    336     void computeIntPower(ResultType&, RealScalar);
    337 
    338     template<typename ResultType>
    339     void computeFracPower(ResultType&, RealScalar);
    340 
    341     template<int Rows, int Cols, int Options, int MaxRows, int MaxCols>
    342     static void revertSchur(
    343         Matrix<ComplexScalar, Rows, Cols, Options, MaxRows, MaxCols>& res,
    344         const ComplexMatrix& T,
    345         const ComplexMatrix& U);
    346 
    347     template<int Rows, int Cols, int Options, int MaxRows, int MaxCols>
    348     static void revertSchur(
    349         Matrix<RealScalar, Rows, Cols, Options, MaxRows, MaxCols>& res,
    350         const ComplexMatrix& T,
    351         const ComplexMatrix& U);
    352 };
    353 
    354 template<typename MatrixType>
    355 template<typename ResultType>
    356 void MatrixPower<MatrixType>::compute(ResultType& res, RealScalar p)
    357 {
    358   switch (cols()) {
    359     case 0:
    360       break;
    361     case 1:
    362       res(0,0) = std::pow(m_A.coeff(0,0), p);
    363       break;
    364     default:
    365       RealScalar intpart, x = modfAndInit(p, &intpart);
    366       computeIntPower(res, intpart);
    367       computeFracPower(res, x);
    368   }
    369 }
    370 
    371 template<typename MatrixType>
    372 typename MatrixPower<MatrixType>::RealScalar
    373 MatrixPower<MatrixType>::modfAndInit(RealScalar x, RealScalar* intpart)
    374 {
    375   typedef Array<RealScalar, RowsAtCompileTime, 1, ColMajor, MaxRowsAtCompileTime> RealArray;
    376 
    377   *intpart = std::floor(x);
    378   RealScalar res = x - *intpart;
    379 
    380   if (!m_conditionNumber && res) {
    381     const ComplexSchur<MatrixType> schurOfA(m_A);
    382     m_T = schurOfA.matrixT();
    383     m_U = schurOfA.matrixU();
    384 
    385     const RealArray absTdiag = m_T.diagonal().array().abs();
    386     m_conditionNumber = absTdiag.maxCoeff() / absTdiag.minCoeff();
    387   }
    388 
    389   if (res>RealScalar(0.5) && res>(1-res)*std::pow(m_conditionNumber, res)) {
    390     --res;
    391     ++*intpart;
    392   }
    393   return res;
    394 }
    395 
    396 template<typename MatrixType>
    397 template<typename ResultType>
    398 void MatrixPower<MatrixType>::computeIntPower(ResultType& res, RealScalar p)
    399 {
    400   RealScalar pp = std::abs(p);
    401 
    402   if (p<0)  m_tmp = m_A.inverse();
    403   else      m_tmp = m_A;
    404 
    405   res = MatrixType::Identity(rows(), cols());
    406   while (pp >= 1) {
    407     if (std::fmod(pp, 2) >= 1)
    408       res = m_tmp * res;
    409     m_tmp *= m_tmp;
    410     pp /= 2;
    411   }
    412 }
    413 
    414 template<typename MatrixType>
    415 template<typename ResultType>
    416 void MatrixPower<MatrixType>::computeFracPower(ResultType& res, RealScalar p)
    417 {
    418   if (p) {
    419     eigen_assert(m_conditionNumber);
    420     MatrixPowerAtomic<ComplexMatrix>(m_T, p).compute(m_fT);
    421     revertSchur(m_tmp, m_fT, m_U);
    422     res = m_tmp * res;
    423   }
    424 }
    425 
    426 template<typename MatrixType>
    427 template<int Rows, int Cols, int Options, int MaxRows, int MaxCols>
    428 inline void MatrixPower<MatrixType>::revertSchur(
    429     Matrix<ComplexScalar, Rows, Cols, Options, MaxRows, MaxCols>& res,
    430     const ComplexMatrix& T,
    431     const ComplexMatrix& U)
    432 { res.noalias() = U * (T.template triangularView<Upper>() * U.adjoint()); }
    433 
    434 template<typename MatrixType>
    435 template<int Rows, int Cols, int Options, int MaxRows, int MaxCols>
    436 inline void MatrixPower<MatrixType>::revertSchur(
    437     Matrix<RealScalar, Rows, Cols, Options, MaxRows, MaxCols>& res,
    438     const ComplexMatrix& T,
    439     const ComplexMatrix& U)
    440 { res.noalias() = (U * (T.template triangularView<Upper>() * U.adjoint())).real(); }
    441 
    442 /**
    443  * \ingroup MatrixFunctions_Module
    444  *
    445  * \brief Proxy for the matrix power of some matrix (expression).
    446  *
    447  * \tparam Derived  type of the base, a matrix (expression).
    448  *
    449  * This class holds the arguments to the matrix power until it is
    450  * assigned or evaluated for some other reason (so the argument
    451  * should not be changed in the meantime). It is the return type of
    452  * MatrixBase::pow() and related functions and most of the
    453  * time this is the only way it is used.
    454  */
    455 template<typename Derived>
    456 class MatrixPowerReturnValue : public ReturnByValue< MatrixPowerReturnValue<Derived> >
    457 {
    458   public:
    459     typedef typename Derived::PlainObject PlainObject;
    460     typedef typename Derived::RealScalar RealScalar;
    461     typedef typename Derived::Index Index;
    462 
    463     /**
    464      * \brief Constructor.
    465      *
    466      * \param[in] A  %Matrix (expression), the base of the matrix power.
    467      * \param[in] p  scalar, the exponent of the matrix power.
    468      */
    469     MatrixPowerReturnValue(const Derived& A, RealScalar p) : m_A(A), m_p(p)
    470     { }
    471 
    472     /**
    473      * \brief Compute the matrix power.
    474      *
    475      * \param[out] result  \f$ A^p \f$ where \p A and \p p are as in the
    476      * constructor.
    477      */
    478     template<typename ResultType>
    479     inline void evalTo(ResultType& res) const
    480     { MatrixPower<PlainObject>(m_A.eval()).compute(res, m_p); }
    481 
    482     Index rows() const { return m_A.rows(); }
    483     Index cols() const { return m_A.cols(); }
    484 
    485   private:
    486     const Derived& m_A;
    487     const RealScalar m_p;
    488     MatrixPowerReturnValue& operator=(const MatrixPowerReturnValue&);
    489 };
    490 
    491 namespace internal {
    492 
    493 template<typename MatrixPowerType>
    494 struct traits< MatrixPowerRetval<MatrixPowerType> >
    495 { typedef typename MatrixPowerType::PlainObject ReturnType; };
    496 
    497 template<typename Derived>
    498 struct traits< MatrixPowerReturnValue<Derived> >
    499 { typedef typename Derived::PlainObject ReturnType; };
    500 
    501 }
    502 
    503 template<typename Derived>
    504 const MatrixPowerReturnValue<Derived> MatrixBase<Derived>::pow(const RealScalar& p) const
    505 { return MatrixPowerReturnValue<Derived>(derived(), p); }
    506 
    507 } // namespace Eigen
    508 
    509 #endif // EIGEN_MATRIX_POWER
    510