expm.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.createExpm = void 0;
  6. var _is = require("../../utils/is.js");
  7. var _string = require("../../utils/string.js");
  8. var _factory = require("../../utils/factory.js");
  9. var name = 'expm';
  10. var dependencies = ['typed', 'abs', 'add', 'identity', 'inv', 'multiply'];
  11. var createExpm = /* #__PURE__ */(0, _factory.factory)(name, dependencies, function (_ref) {
  12. var typed = _ref.typed,
  13. abs = _ref.abs,
  14. add = _ref.add,
  15. identity = _ref.identity,
  16. inv = _ref.inv,
  17. multiply = _ref.multiply;
  18. /**
  19. * Compute the matrix exponential, expm(A) = e^A. The matrix must be square.
  20. * Not to be confused with exp(a), which performs element-wise
  21. * exponentiation.
  22. *
  23. * The exponential is calculated using the Padé approximant with scaling and
  24. * squaring; see "Nineteen Dubious Ways to Compute the Exponential of a
  25. * Matrix," by Moler and Van Loan.
  26. *
  27. * Syntax:
  28. *
  29. * math.expm(x)
  30. *
  31. * Examples:
  32. *
  33. * const A = [[0,2],[0,0]]
  34. * math.expm(A) // returns [[1,2],[0,1]]
  35. *
  36. * See also:
  37. *
  38. * exp
  39. *
  40. * @param {Matrix} x A square Matrix
  41. * @return {Matrix} The exponential of x
  42. */
  43. return typed(name, {
  44. Matrix: function Matrix(A) {
  45. // Check matrix size
  46. var size = A.size();
  47. if (size.length !== 2 || size[0] !== size[1]) {
  48. throw new RangeError('Matrix must be square ' + '(size: ' + (0, _string.format)(size) + ')');
  49. }
  50. var n = size[0];
  51. // Desired accuracy of the approximant (The actual accuracy
  52. // will be affected by round-off error)
  53. var eps = 1e-15;
  54. // The Padé approximant is not so accurate when the values of A
  55. // are "large", so scale A by powers of two. Then compute the
  56. // exponential, and square the result repeatedly according to
  57. // the identity e^A = (e^(A/m))^m
  58. // Compute infinity-norm of A, ||A||, to see how "big" it is
  59. var infNorm = infinityNorm(A);
  60. // Find the optimal scaling factor and number of terms in the
  61. // Padé approximant to reach the desired accuracy
  62. var params = findParams(infNorm, eps);
  63. var q = params.q;
  64. var j = params.j;
  65. // The Pade approximation to e^A is:
  66. // Rqq(A) = Dqq(A) ^ -1 * Nqq(A)
  67. // where
  68. // Nqq(A) = sum(i=0, q, (2q-i)!p! / [ (2q)!i!(q-i)! ] A^i
  69. // Dqq(A) = sum(i=0, q, (2q-i)!q! / [ (2q)!i!(q-i)! ] (-A)^i
  70. // Scale A by 1 / 2^j
  71. var Apos = multiply(A, Math.pow(2, -j));
  72. // The i=0 term is just the identity matrix
  73. var N = identity(n);
  74. var D = identity(n);
  75. // Initialization (i=0)
  76. var factor = 1;
  77. // Initialization (i=1)
  78. var AposToI = Apos; // Cloning not necessary
  79. var alternate = -1;
  80. for (var i = 1; i <= q; i++) {
  81. if (i > 1) {
  82. AposToI = multiply(AposToI, Apos);
  83. alternate = -alternate;
  84. }
  85. factor = factor * (q - i + 1) / ((2 * q - i + 1) * i);
  86. N = add(N, multiply(factor, AposToI));
  87. D = add(D, multiply(factor * alternate, AposToI));
  88. }
  89. var R = multiply(inv(D), N);
  90. // Square j times
  91. for (var _i = 0; _i < j; _i++) {
  92. R = multiply(R, R);
  93. }
  94. return (0, _is.isSparseMatrix)(A) ? A.createSparseMatrix(R) : R;
  95. }
  96. });
  97. function infinityNorm(A) {
  98. var n = A.size()[0];
  99. var infNorm = 0;
  100. for (var i = 0; i < n; i++) {
  101. var rowSum = 0;
  102. for (var j = 0; j < n; j++) {
  103. rowSum += abs(A.get([i, j]));
  104. }
  105. infNorm = Math.max(rowSum, infNorm);
  106. }
  107. return infNorm;
  108. }
  109. /**
  110. * Find the best parameters for the Pade approximant given
  111. * the matrix norm and desired accuracy. Returns the first acceptable
  112. * combination in order of increasing computational load.
  113. */
  114. function findParams(infNorm, eps) {
  115. var maxSearchSize = 30;
  116. for (var k = 0; k < maxSearchSize; k++) {
  117. for (var q = 0; q <= k; q++) {
  118. var j = k - q;
  119. if (errorEstimate(infNorm, q, j) < eps) {
  120. return {
  121. q: q,
  122. j: j
  123. };
  124. }
  125. }
  126. }
  127. throw new Error('Could not find acceptable parameters to compute the matrix exponential (try increasing maxSearchSize in expm.js)');
  128. }
  129. /**
  130. * Returns the estimated error of the Pade approximant for the given
  131. * parameters.
  132. */
  133. function errorEstimate(infNorm, q, j) {
  134. var qfac = 1;
  135. for (var i = 2; i <= q; i++) {
  136. qfac *= i;
  137. }
  138. var twoqfac = qfac;
  139. for (var _i2 = q + 1; _i2 <= 2 * q; _i2++) {
  140. twoqfac *= _i2;
  141. }
  142. var twoqp1fac = twoqfac * (2 * q + 1);
  143. return 8.0 * Math.pow(infNorm / Math.pow(2, j), 2 * q) * qfac * qfac / (twoqfac * twoqp1fac);
  144. }
  145. });
  146. exports.createExpm = createExpm;