inv.js (5770B)
1 import { isMatrix } from '../../utils/is.js'; 2 import { arraySize } from '../../utils/array.js'; 3 import { factory } from '../../utils/factory.js'; 4 import { format } from '../../utils/string.js'; 5 var name = 'inv'; 6 var dependencies = ['typed', 'matrix', 'divideScalar', 'addScalar', 'multiply', 'unaryMinus', 'det', 'identity', 'abs']; 7 export var createInv = /* #__PURE__ */factory(name, dependencies, _ref => { 8 var { 9 typed, 10 matrix, 11 divideScalar, 12 addScalar, 13 multiply, 14 unaryMinus, 15 det, 16 identity, 17 abs 18 } = _ref; 19 20 /** 21 * Calculate the inverse of a square matrix. 22 * 23 * Syntax: 24 * 25 * math.inv(x) 26 * 27 * Examples: 28 * 29 * math.inv([[1, 2], [3, 4]]) // returns [[-2, 1], [1.5, -0.5]] 30 * math.inv(4) // returns 0.25 31 * 1 / 4 // returns 0.25 32 * 33 * See also: 34 * 35 * det, transpose 36 * 37 * @param {number | Complex | Array | Matrix} x Matrix to be inversed 38 * @return {number | Complex | Array | Matrix} The inverse of `x`. 39 */ 40 return typed(name, { 41 'Array | Matrix': function ArrayMatrix(x) { 42 var size = isMatrix(x) ? x.size() : arraySize(x); 43 44 switch (size.length) { 45 case 1: 46 // vector 47 if (size[0] === 1) { 48 if (isMatrix(x)) { 49 return matrix([divideScalar(1, x.valueOf()[0])]); 50 } else { 51 return [divideScalar(1, x[0])]; 52 } 53 } else { 54 throw new RangeError('Matrix must be square ' + '(size: ' + format(size) + ')'); 55 } 56 57 case 2: 58 // two dimensional array 59 { 60 var rows = size[0]; 61 var cols = size[1]; 62 63 if (rows === cols) { 64 if (isMatrix(x)) { 65 return matrix(_inv(x.valueOf(), rows, cols), x.storage()); 66 } else { 67 // return an Array 68 return _inv(x, rows, cols); 69 } 70 } else { 71 throw new RangeError('Matrix must be square ' + '(size: ' + format(size) + ')'); 72 } 73 } 74 75 default: 76 // multi dimensional array 77 throw new RangeError('Matrix must be two dimensional ' + '(size: ' + format(size) + ')'); 78 } 79 }, 80 any: function any(x) { 81 // scalar 82 return divideScalar(1, x); // FIXME: create a BigNumber one when configured for bignumbers 83 } 84 }); 85 /** 86 * Calculate the inverse of a square matrix 87 * @param {Array[]} mat A square matrix 88 * @param {number} rows Number of rows 89 * @param {number} cols Number of columns, must equal rows 90 * @return {Array[]} inv Inverse matrix 91 * @private 92 */ 93 94 function _inv(mat, rows, cols) { 95 var r, s, f, value, temp; 96 97 if (rows === 1) { 98 // this is a 1 x 1 matrix 99 value = mat[0][0]; 100 101 if (value === 0) { 102 throw Error('Cannot calculate inverse, determinant is zero'); 103 } 104 105 return [[divideScalar(1, value)]]; 106 } else if (rows === 2) { 107 // this is a 2 x 2 matrix 108 var d = det(mat); 109 110 if (d === 0) { 111 throw Error('Cannot calculate inverse, determinant is zero'); 112 } 113 114 return [[divideScalar(mat[1][1], d), divideScalar(unaryMinus(mat[0][1]), d)], [divideScalar(unaryMinus(mat[1][0]), d), divideScalar(mat[0][0], d)]]; 115 } else { 116 // this is a matrix of 3 x 3 or larger 117 // calculate inverse using gauss-jordan elimination 118 // https://en.wikipedia.org/wiki/Gaussian_elimination 119 // http://mathworld.wolfram.com/MatrixInverse.html 120 // http://math.uww.edu/~mcfarlat/inverse.htm 121 // make a copy of the matrix (only the arrays, not of the elements) 122 var A = mat.concat(); 123 124 for (r = 0; r < rows; r++) { 125 A[r] = A[r].concat(); 126 } // create an identity matrix which in the end will contain the 127 // matrix inverse 128 129 130 var B = identity(rows).valueOf(); // loop over all columns, and perform row reductions 131 132 for (var c = 0; c < cols; c++) { 133 // Pivoting: Swap row c with row r, where row r contains the largest element A[r][c] 134 var ABig = abs(A[c][c]); 135 var rBig = c; 136 r = c + 1; 137 138 while (r < rows) { 139 if (abs(A[r][c]) > ABig) { 140 ABig = abs(A[r][c]); 141 rBig = r; 142 } 143 144 r++; 145 } 146 147 if (ABig === 0) { 148 throw Error('Cannot calculate inverse, determinant is zero'); 149 } 150 151 r = rBig; 152 153 if (r !== c) { 154 temp = A[c]; 155 A[c] = A[r]; 156 A[r] = temp; 157 temp = B[c]; 158 B[c] = B[r]; 159 B[r] = temp; 160 } // eliminate non-zero values on the other rows at column c 161 162 163 var Ac = A[c]; 164 var Bc = B[c]; 165 166 for (r = 0; r < rows; r++) { 167 var Ar = A[r]; 168 var Br = B[r]; 169 170 if (r !== c) { 171 // eliminate value at column c and row r 172 if (Ar[c] !== 0) { 173 f = divideScalar(unaryMinus(Ar[c]), Ac[c]); // add (f * row c) to row r to eliminate the value 174 // at column c 175 176 for (s = c; s < cols; s++) { 177 Ar[s] = addScalar(Ar[s], multiply(f, Ac[s])); 178 } 179 180 for (s = 0; s < cols; s++) { 181 Br[s] = addScalar(Br[s], multiply(f, Bc[s])); 182 } 183 } 184 } else { 185 // normalize value at Acc to 1, 186 // divide each value on row r with the value at Acc 187 f = Ac[c]; 188 189 for (s = c; s < cols; s++) { 190 Ar[s] = divideScalar(Ar[s], f); 191 } 192 193 for (s = 0; s < cols; s++) { 194 Br[s] = divideScalar(Br[s], f); 195 } 196 } 197 } 198 } 199 200 return B; 201 } 202 } 203 });