expm.js (4788B)
1 "use strict"; 2 3 Object.defineProperty(exports, "__esModule", { 4 value: true 5 }); 6 exports.createExpm = void 0; 7 8 var _is = require("../../utils/is.js"); 9 10 var _string = require("../../utils/string.js"); 11 12 var _factory = require("../../utils/factory.js"); 13 14 var name = 'expm'; 15 var dependencies = ['typed', 'abs', 'add', 'identity', 'inv', 'multiply']; 16 var 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 }); 175 exports.createExpm = createExpm;