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