UNPKG

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