• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 
MatrixPowerRetval(MatrixPower<MatrixType> & pow,RealScalar p)24     MatrixPowerRetval(MatrixPower<MatrixType>& pow, RealScalar p) : m_pow(pow), m_p(p)
25     { }
26 
27     template<typename ResultType>
evalTo(ResultType & res)28     inline void evalTo(ResultType& res) const
29     { m_pow.compute(res, m_p); }
30 
rows()31     Index rows() const { return m_pow.rows(); }
cols()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>
MatrixPowerAtomic(const MatrixType & T,RealScalar p)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>
compute(MatrixType & res)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>
computePade(int degree,const MatrixType & IminusT,MatrixType & res)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>
compute2x2(MatrixType & res,RealScalar p)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>
computeBig(MatrixType & res)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>
getPadeDegree(float normIminusT)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>
getPadeDegree(double normIminusT)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>
getPadeDegree(long double normIminusT)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
computeSuperDiag(const ComplexScalar & curr,const ComplexScalar & prev,RealScalar p)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
computeSuperDiag(RealScalar curr,RealScalar prev,RealScalar p)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      */
MatrixPower(const MatrixType & A)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      */
operator()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 
rows()320     Index rows() const { return m_A.rows(); }
cols()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>
compute(ResultType & res,RealScalar p)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
modfAndInit(RealScalar x,RealScalar * intpart)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>
computeIntPower(ResultType & res,RealScalar p)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>
computeFracPower(ResultType & res,RealScalar p)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>
revertSchur(Matrix<ComplexScalar,Rows,Cols,Options,MaxRows,MaxCols> & res,const ComplexMatrix & T,const ComplexMatrix & U)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>
revertSchur(Matrix<RealScalar,Rows,Cols,Options,MaxRows,MaxCols> & res,const ComplexMatrix & T,const ComplexMatrix & U)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      */
MatrixPowerReturnValue(const Derived & A,RealScalar p)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>
evalTo(ResultType & res)479     inline void evalTo(ResultType& res) const
480     { MatrixPower<PlainObject>(m_A.eval()).compute(res, m_p); }
481 
rows()482     Index rows() const { return m_A.rows(); }
cols()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