simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

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 });