MatrixExponential.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. // This file is part of Eigen, a lightweight C++ template library
  2. // for linear algebra.
  3. //
  4. // Copyright (C) 2009, 2010, 2013 Jitse Niesen <jitse@maths.leeds.ac.uk>
  5. // Copyright (C) 2011, 2013 Chen-Pang He <jdh8@ms63.hinet.net>
  6. //
  7. // This Source Code Form is subject to the terms of the Mozilla
  8. // Public License v. 2.0. If a copy of the MPL was not distributed
  9. // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
  10. #ifndef EIGEN_MATRIX_EXPONENTIAL
  11. #define EIGEN_MATRIX_EXPONENTIAL
  12. #include "StemFunction.h"
  13. namespace Eigen {
  14. namespace internal {
  15. /** \brief Scaling operator.
  16. *
  17. * This struct is used by CwiseUnaryOp to scale a matrix by \f$ 2^{-s} \f$.
  18. */
  19. template <typename RealScalar>
  20. struct MatrixExponentialScalingOp
  21. {
  22. /** \brief Constructor.
  23. *
  24. * \param[in] squarings The integer \f$ s \f$ in this document.
  25. */
  26. MatrixExponentialScalingOp(int squarings) : m_squarings(squarings) { }
  27. /** \brief Scale a matrix coefficient.
  28. *
  29. * \param[in,out] x The scalar to be scaled, becoming \f$ 2^{-s} x \f$.
  30. */
  31. inline const RealScalar operator() (const RealScalar& x) const
  32. {
  33. using std::ldexp;
  34. return ldexp(x, -m_squarings);
  35. }
  36. typedef std::complex<RealScalar> ComplexScalar;
  37. /** \brief Scale a matrix coefficient.
  38. *
  39. * \param[in,out] x The scalar to be scaled, becoming \f$ 2^{-s} x \f$.
  40. */
  41. inline const ComplexScalar operator() (const ComplexScalar& x) const
  42. {
  43. using std::ldexp;
  44. return ComplexScalar(ldexp(x.real(), -m_squarings), ldexp(x.imag(), -m_squarings));
  45. }
  46. private:
  47. int m_squarings;
  48. };
  49. /** \brief Compute the (3,3)-Pad&eacute; approximant to the exponential.
  50. *
  51. * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Pad&eacute;
  52. * approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$.
  53. */
  54. template <typename MatA, typename MatU, typename MatV>
  55. void matrix_exp_pade3(const MatA& A, MatU& U, MatV& V)
  56. {
  57. typedef typename MatA::PlainObject MatrixType;
  58. typedef typename NumTraits<typename traits<MatA>::Scalar>::Real RealScalar;
  59. const RealScalar b[] = {120.L, 60.L, 12.L, 1.L};
  60. const MatrixType A2 = A * A;
  61. const MatrixType tmp = b[3] * A2 + b[1] * MatrixType::Identity(A.rows(), A.cols());
  62. U.noalias() = A * tmp;
  63. V = b[2] * A2 + b[0] * MatrixType::Identity(A.rows(), A.cols());
  64. }
  65. /** \brief Compute the (5,5)-Pad&eacute; approximant to the exponential.
  66. *
  67. * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Pad&eacute;
  68. * approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$.
  69. */
  70. template <typename MatA, typename MatU, typename MatV>
  71. void matrix_exp_pade5(const MatA& A, MatU& U, MatV& V)
  72. {
  73. typedef typename MatA::PlainObject MatrixType;
  74. typedef typename NumTraits<typename traits<MatrixType>::Scalar>::Real RealScalar;
  75. const RealScalar b[] = {30240.L, 15120.L, 3360.L, 420.L, 30.L, 1.L};
  76. const MatrixType A2 = A * A;
  77. const MatrixType A4 = A2 * A2;
  78. const MatrixType tmp = b[5] * A4 + b[3] * A2 + b[1] * MatrixType::Identity(A.rows(), A.cols());
  79. U.noalias() = A * tmp;
  80. V = b[4] * A4 + b[2] * A2 + b[0] * MatrixType::Identity(A.rows(), A.cols());
  81. }
  82. /** \brief Compute the (7,7)-Pad&eacute; approximant to the exponential.
  83. *
  84. * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Pad&eacute;
  85. * approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$.
  86. */
  87. template <typename MatA, typename MatU, typename MatV>
  88. void matrix_exp_pade7(const MatA& A, MatU& U, MatV& V)
  89. {
  90. typedef typename MatA::PlainObject MatrixType;
  91. typedef typename NumTraits<typename traits<MatrixType>::Scalar>::Real RealScalar;
  92. const RealScalar b[] = {17297280.L, 8648640.L, 1995840.L, 277200.L, 25200.L, 1512.L, 56.L, 1.L};
  93. const MatrixType A2 = A * A;
  94. const MatrixType A4 = A2 * A2;
  95. const MatrixType A6 = A4 * A2;
  96. const MatrixType tmp = b[7] * A6 + b[5] * A4 + b[3] * A2
  97. + b[1] * MatrixType::Identity(A.rows(), A.cols());
  98. U.noalias() = A * tmp;
  99. V = b[6] * A6 + b[4] * A4 + b[2] * A2 + b[0] * MatrixType::Identity(A.rows(), A.cols());
  100. }
  101. /** \brief Compute the (9,9)-Pad&eacute; approximant to the exponential.
  102. *
  103. * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Pad&eacute;
  104. * approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$.
  105. */
  106. template <typename MatA, typename MatU, typename MatV>
  107. void matrix_exp_pade9(const MatA& A, MatU& U, MatV& V)
  108. {
  109. typedef typename MatA::PlainObject MatrixType;
  110. typedef typename NumTraits<typename traits<MatrixType>::Scalar>::Real RealScalar;
  111. const RealScalar b[] = {17643225600.L, 8821612800.L, 2075673600.L, 302702400.L, 30270240.L,
  112. 2162160.L, 110880.L, 3960.L, 90.L, 1.L};
  113. const MatrixType A2 = A * A;
  114. const MatrixType A4 = A2 * A2;
  115. const MatrixType A6 = A4 * A2;
  116. const MatrixType A8 = A6 * A2;
  117. const MatrixType tmp = b[9] * A8 + b[7] * A6 + b[5] * A4 + b[3] * A2
  118. + b[1] * MatrixType::Identity(A.rows(), A.cols());
  119. U.noalias() = A * tmp;
  120. V = b[8] * A8 + b[6] * A6 + b[4] * A4 + b[2] * A2 + b[0] * MatrixType::Identity(A.rows(), A.cols());
  121. }
  122. /** \brief Compute the (13,13)-Pad&eacute; approximant to the exponential.
  123. *
  124. * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Pad&eacute;
  125. * approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$.
  126. */
  127. template <typename MatA, typename MatU, typename MatV>
  128. void matrix_exp_pade13(const MatA& A, MatU& U, MatV& V)
  129. {
  130. typedef typename MatA::PlainObject MatrixType;
  131. typedef typename NumTraits<typename traits<MatrixType>::Scalar>::Real RealScalar;
  132. const RealScalar b[] = {64764752532480000.L, 32382376266240000.L, 7771770303897600.L,
  133. 1187353796428800.L, 129060195264000.L, 10559470521600.L, 670442572800.L,
  134. 33522128640.L, 1323241920.L, 40840800.L, 960960.L, 16380.L, 182.L, 1.L};
  135. const MatrixType A2 = A * A;
  136. const MatrixType A4 = A2 * A2;
  137. const MatrixType A6 = A4 * A2;
  138. V = b[13] * A6 + b[11] * A4 + b[9] * A2; // used for temporary storage
  139. MatrixType tmp = A6 * V;
  140. tmp += b[7] * A6 + b[5] * A4 + b[3] * A2 + b[1] * MatrixType::Identity(A.rows(), A.cols());
  141. U.noalias() = A * tmp;
  142. tmp = b[12] * A6 + b[10] * A4 + b[8] * A2;
  143. V.noalias() = A6 * tmp;
  144. V += b[6] * A6 + b[4] * A4 + b[2] * A2 + b[0] * MatrixType::Identity(A.rows(), A.cols());
  145. }
  146. /** \brief Compute the (17,17)-Pad&eacute; approximant to the exponential.
  147. *
  148. * After exit, \f$ (V+U)(V-U)^{-1} \f$ is the Pad&eacute;
  149. * approximant of \f$ \exp(A) \f$ around \f$ A = 0 \f$.
  150. *
  151. * This function activates only if your long double is double-double or quadruple.
  152. */
  153. #if LDBL_MANT_DIG > 64
  154. template <typename MatA, typename MatU, typename MatV>
  155. void matrix_exp_pade17(const MatA& A, MatU& U, MatV& V)
  156. {
  157. typedef typename MatA::PlainObject MatrixType;
  158. typedef typename NumTraits<typename traits<MatrixType>::Scalar>::Real RealScalar;
  159. const RealScalar b[] = {830034394580628357120000.L, 415017197290314178560000.L,
  160. 100610229646136770560000.L, 15720348382208870400000.L,
  161. 1774878043152614400000.L, 153822763739893248000.L, 10608466464820224000.L,
  162. 595373117923584000.L, 27563570274240000.L, 1060137318240000.L,
  163. 33924394183680.L, 899510451840.L, 19554575040.L, 341863200.L, 4651200.L,
  164. 46512.L, 306.L, 1.L};
  165. const MatrixType A2 = A * A;
  166. const MatrixType A4 = A2 * A2;
  167. const MatrixType A6 = A4 * A2;
  168. const MatrixType A8 = A4 * A4;
  169. V = b[17] * A8 + b[15] * A6 + b[13] * A4 + b[11] * A2; // used for temporary storage
  170. MatrixType tmp = A8 * V;
  171. tmp += b[9] * A8 + b[7] * A6 + b[5] * A4 + b[3] * A2
  172. + b[1] * MatrixType::Identity(A.rows(), A.cols());
  173. U.noalias() = A * tmp;
  174. tmp = b[16] * A8 + b[14] * A6 + b[12] * A4 + b[10] * A2;
  175. V.noalias() = tmp * A8;
  176. V += b[8] * A8 + b[6] * A6 + b[4] * A4 + b[2] * A2
  177. + b[0] * MatrixType::Identity(A.rows(), A.cols());
  178. }
  179. #endif
  180. template <typename MatrixType, typename RealScalar = typename NumTraits<typename traits<MatrixType>::Scalar>::Real>
  181. struct matrix_exp_computeUV
  182. {
  183. /** \brief Compute Pad&eacute; approximant to the exponential.
  184. *
  185. * Computes \c U, \c V and \c squarings such that \f$ (V+U)(V-U)^{-1} \f$ is a Pad&eacute;
  186. * approximant of \f$ \exp(2^{-\mbox{squarings}}M) \f$ around \f$ M = 0 \f$, where \f$ M \f$
  187. * denotes the matrix \c arg. The degree of the Pad&eacute; approximant and the value of squarings
  188. * are chosen such that the approximation error is no more than the round-off error.
  189. */
  190. static void run(const MatrixType& arg, MatrixType& U, MatrixType& V, int& squarings);
  191. };
  192. template <typename MatrixType>
  193. struct matrix_exp_computeUV<MatrixType, float>
  194. {
  195. template <typename ArgType>
  196. static void run(const ArgType& arg, MatrixType& U, MatrixType& V, int& squarings)
  197. {
  198. using std::frexp;
  199. using std::pow;
  200. const float l1norm = arg.cwiseAbs().colwise().sum().maxCoeff();
  201. squarings = 0;
  202. if (l1norm < 4.258730016922831e-001f) {
  203. matrix_exp_pade3(arg, U, V);
  204. } else if (l1norm < 1.880152677804762e+000f) {
  205. matrix_exp_pade5(arg, U, V);
  206. } else {
  207. const float maxnorm = 3.925724783138660f;
  208. frexp(l1norm / maxnorm, &squarings);
  209. if (squarings < 0) squarings = 0;
  210. MatrixType A = arg.unaryExpr(MatrixExponentialScalingOp<float>(squarings));
  211. matrix_exp_pade7(A, U, V);
  212. }
  213. }
  214. };
  215. template <typename MatrixType>
  216. struct matrix_exp_computeUV<MatrixType, double>
  217. {
  218. typedef typename NumTraits<typename traits<MatrixType>::Scalar>::Real RealScalar;
  219. template <typename ArgType>
  220. static void run(const ArgType& arg, MatrixType& U, MatrixType& V, int& squarings)
  221. {
  222. using std::frexp;
  223. using std::pow;
  224. const RealScalar l1norm = arg.cwiseAbs().colwise().sum().maxCoeff();
  225. squarings = 0;
  226. if (l1norm < 1.495585217958292e-002) {
  227. matrix_exp_pade3(arg, U, V);
  228. } else if (l1norm < 2.539398330063230e-001) {
  229. matrix_exp_pade5(arg, U, V);
  230. } else if (l1norm < 9.504178996162932e-001) {
  231. matrix_exp_pade7(arg, U, V);
  232. } else if (l1norm < 2.097847961257068e+000) {
  233. matrix_exp_pade9(arg, U, V);
  234. } else {
  235. const RealScalar maxnorm = 5.371920351148152;
  236. frexp(l1norm / maxnorm, &squarings);
  237. if (squarings < 0) squarings = 0;
  238. MatrixType A = arg.unaryExpr(MatrixExponentialScalingOp<RealScalar>(squarings));
  239. matrix_exp_pade13(A, U, V);
  240. }
  241. }
  242. };
  243. template <typename MatrixType>
  244. struct matrix_exp_computeUV<MatrixType, long double>
  245. {
  246. template <typename ArgType>
  247. static void run(const ArgType& arg, MatrixType& U, MatrixType& V, int& squarings)
  248. {
  249. #if LDBL_MANT_DIG == 53 // double precision
  250. matrix_exp_computeUV<MatrixType, double>::run(arg, U, V, squarings);
  251. #else
  252. using std::frexp;
  253. using std::pow;
  254. const long double l1norm = arg.cwiseAbs().colwise().sum().maxCoeff();
  255. squarings = 0;
  256. #if LDBL_MANT_DIG <= 64 // extended precision
  257. if (l1norm < 4.1968497232266989671e-003L) {
  258. matrix_exp_pade3(arg, U, V);
  259. } else if (l1norm < 1.1848116734693823091e-001L) {
  260. matrix_exp_pade5(arg, U, V);
  261. } else if (l1norm < 5.5170388480686700274e-001L) {
  262. matrix_exp_pade7(arg, U, V);
  263. } else if (l1norm < 1.3759868875587845383e+000L) {
  264. matrix_exp_pade9(arg, U, V);
  265. } else {
  266. const long double maxnorm = 4.0246098906697353063L;
  267. frexp(l1norm / maxnorm, &squarings);
  268. if (squarings < 0) squarings = 0;
  269. MatrixType A = arg.unaryExpr(MatrixExponentialScalingOp<long double>(squarings));
  270. matrix_exp_pade13(A, U, V);
  271. }
  272. #elif LDBL_MANT_DIG <= 106 // double-double
  273. if (l1norm < 3.2787892205607026992947488108213e-005L) {
  274. matrix_exp_pade3(arg, U, V);
  275. } else if (l1norm < 6.4467025060072760084130906076332e-003L) {
  276. matrix_exp_pade5(arg, U, V);
  277. } else if (l1norm < 6.8988028496595374751374122881143e-002L) {
  278. matrix_exp_pade7(arg, U, V);
  279. } else if (l1norm < 2.7339737518502231741495857201670e-001L) {
  280. matrix_exp_pade9(arg, U, V);
  281. } else if (l1norm < 1.3203382096514474905666448850278e+000L) {
  282. matrix_exp_pade13(arg, U, V);
  283. } else {
  284. const long double maxnorm = 3.2579440895405400856599663723517L;
  285. frexp(l1norm / maxnorm, &squarings);
  286. if (squarings < 0) squarings = 0;
  287. MatrixType A = arg.unaryExpr(MatrixExponentialScalingOp<long double>(squarings));
  288. matrix_exp_pade17(A, U, V);
  289. }
  290. #elif LDBL_MANT_DIG <= 112 // quadruple precison
  291. if (l1norm < 1.639394610288918690547467954466970e-005L) {
  292. matrix_exp_pade3(arg, U, V);
  293. } else if (l1norm < 4.253237712165275566025884344433009e-003L) {
  294. matrix_exp_pade5(arg, U, V);
  295. } else if (l1norm < 5.125804063165764409885122032933142e-002L) {
  296. matrix_exp_pade7(arg, U, V);
  297. } else if (l1norm < 2.170000765161155195453205651889853e-001L) {
  298. matrix_exp_pade9(arg, U, V);
  299. } else if (l1norm < 1.125358383453143065081397882891878e+000L) {
  300. matrix_exp_pade13(arg, U, V);
  301. } else {
  302. const long double maxnorm = 2.884233277829519311757165057717815L;
  303. frexp(l1norm / maxnorm, &squarings);
  304. if (squarings < 0) squarings = 0;
  305. MatrixType A = arg.unaryExpr(MatrixExponentialScalingOp<long double>(squarings));
  306. matrix_exp_pade17(A, U, V);
  307. }
  308. #else
  309. // this case should be handled in compute()
  310. eigen_assert(false && "Bug in MatrixExponential");
  311. #endif
  312. #endif // LDBL_MANT_DIG
  313. }
  314. };
  315. template<typename T> struct is_exp_known_type : false_type {};
  316. template<> struct is_exp_known_type<float> : true_type {};
  317. template<> struct is_exp_known_type<double> : true_type {};
  318. #if LDBL_MANT_DIG <= 112
  319. template<> struct is_exp_known_type<long double> : true_type {};
  320. #endif
  321. template <typename ArgType, typename ResultType>
  322. void matrix_exp_compute(const ArgType& arg, ResultType &result, true_type) // natively supported scalar type
  323. {
  324. typedef typename ArgType::PlainObject MatrixType;
  325. MatrixType U, V;
  326. int squarings;
  327. matrix_exp_computeUV<MatrixType>::run(arg, U, V, squarings); // Pade approximant is (U+V) / (-U+V)
  328. MatrixType numer = U + V;
  329. MatrixType denom = -U + V;
  330. result = denom.partialPivLu().solve(numer);
  331. for (int i=0; i<squarings; i++)
  332. result *= result; // undo scaling by repeated squaring
  333. }
  334. /* Computes the matrix exponential
  335. *
  336. * \param arg argument of matrix exponential (should be plain object)
  337. * \param result variable in which result will be stored
  338. */
  339. template <typename ArgType, typename ResultType>
  340. void matrix_exp_compute(const ArgType& arg, ResultType &result, false_type) // default
  341. {
  342. typedef typename ArgType::PlainObject MatrixType;
  343. typedef typename traits<MatrixType>::Scalar Scalar;
  344. typedef typename NumTraits<Scalar>::Real RealScalar;
  345. typedef typename std::complex<RealScalar> ComplexScalar;
  346. result = arg.matrixFunction(internal::stem_function_exp<ComplexScalar>);
  347. }
  348. } // end namespace Eigen::internal
  349. /** \ingroup MatrixFunctions_Module
  350. *
  351. * \brief Proxy for the matrix exponential of some matrix (expression).
  352. *
  353. * \tparam Derived Type of the argument to the matrix exponential.
  354. *
  355. * This class holds the argument to the matrix exponential until it is assigned or evaluated for
  356. * some other reason (so the argument should not be changed in the meantime). It is the return type
  357. * of MatrixBase::exp() and most of the time this is the only way it is used.
  358. */
  359. template<typename Derived> struct MatrixExponentialReturnValue
  360. : public ReturnByValue<MatrixExponentialReturnValue<Derived> >
  361. {
  362. typedef typename Derived::Index Index;
  363. public:
  364. /** \brief Constructor.
  365. *
  366. * \param src %Matrix (expression) forming the argument of the matrix exponential.
  367. */
  368. MatrixExponentialReturnValue(const Derived& src) : m_src(src) { }
  369. /** \brief Compute the matrix exponential.
  370. *
  371. * \param result the matrix exponential of \p src in the constructor.
  372. */
  373. template <typename ResultType>
  374. inline void evalTo(ResultType& result) const
  375. {
  376. const typename internal::nested_eval<Derived, 10>::type tmp(m_src);
  377. internal::matrix_exp_compute(tmp, result, internal::is_exp_known_type<typename Derived::Scalar>());
  378. }
  379. Index rows() const { return m_src.rows(); }
  380. Index cols() const { return m_src.cols(); }
  381. protected:
  382. const typename internal::ref_selector<Derived>::type m_src;
  383. };
  384. namespace internal {
  385. template<typename Derived>
  386. struct traits<MatrixExponentialReturnValue<Derived> >
  387. {
  388. typedef typename Derived::PlainObject ReturnType;
  389. };
  390. }
  391. template <typename Derived>
  392. const MatrixExponentialReturnValue<Derived> MatrixBase<Derived>::exp() const
  393. {
  394. eigen_assert(rows() == cols());
  395. return MatrixExponentialReturnValue<Derived>(derived());
  396. }
  397. } // end namespace Eigen
  398. #endif // EIGEN_MATRIX_EXPONENTIAL