mathjs
Version:
Math.js is an extensive math library for JavaScript and Node.js. It features a flexible expression parser with support for symbolic computation, comes with a large set of built-in functions and constants, and offers an integrated solution to work with dif
207 lines (194 loc) • 5.88 kB
JavaScript
import { isMatrix } from '../../utils/is'
import { arraySize } from '../../utils/array'
import { factory } from '../../utils/factory'
import { format } from '../../utils/string'
const name = 'inv'
const dependencies = [
'typed',
'matrix',
'divideScalar',
'addScalar',
'multiply',
'unaryMinus',
'det',
'identity',
'abs'
]
export const createInv = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, divideScalar, addScalar, multiply, unaryMinus, det, identity, abs }) => {
/**
* Calculate the inverse of a square matrix.
*
* Syntax:
*
* math.inv(x)
*
* Examples:
*
* math.inv([[1, 2], [3, 4]]) // returns [[-2, 1], [1.5, -0.5]]
* math.inv(4) // returns 0.25
* 1 / 4 // returns 0.25
*
* See also:
*
* det, transpose
*
* @param {number | Complex | Array | Matrix} x Matrix to be inversed
* @return {number | Complex | Array | Matrix} The inverse of `x`.
*/
return typed(name, {
'Array | Matrix': function (x) {
const size = isMatrix(x) ? x.size() : arraySize(x)
switch (size.length) {
case 1:
// vector
if (size[0] === 1) {
if (isMatrix(x)) {
return matrix([
divideScalar(1, x.valueOf()[0])
])
} else {
return [
divideScalar(1, x[0])
]
}
} else {
throw new RangeError('Matrix must be square ' +
'(size: ' + format(size) + ')')
}
case 2:
// two dimensional array
{
const rows = size[0]
const cols = size[1]
if (rows === cols) {
if (isMatrix(x)) {
return matrix(
_inv(x.valueOf(), rows, cols),
x.storage()
)
} else {
// return an Array
return _inv(x, rows, cols)
}
} else {
throw new RangeError('Matrix must be square ' +
'(size: ' + format(size) + ')')
}
}
default:
// multi dimensional array
throw new RangeError('Matrix must be two dimensional ' +
'(size: ' + format(size) + ')')
}
},
any: function (x) {
// scalar
return divideScalar(1, x) // FIXME: create a BigNumber one when configured for bignumbers
}
})
/**
* Calculate the inverse of a square matrix
* @param {Array[]} mat A square matrix
* @param {number} rows Number of rows
* @param {number} cols Number of columns, must equal rows
* @return {Array[]} inv Inverse matrix
* @private
*/
function _inv (mat, rows, cols) {
let r, s, f, value, temp
if (rows === 1) {
// this is a 1 x 1 matrix
value = mat[0][0]
if (value === 0) {
throw Error('Cannot calculate inverse, determinant is zero')
}
return [[
divideScalar(1, value)
]]
} else if (rows === 2) {
// this is a 2 x 2 matrix
const d = det(mat)
if (d === 0) {
throw Error('Cannot calculate inverse, determinant is zero')
}
return [
[
divideScalar(mat[1][1], d),
divideScalar(unaryMinus(mat[0][1]), d)
],
[
divideScalar(unaryMinus(mat[1][0]), d),
divideScalar(mat[0][0], d)
]
]
} else {
// this is a matrix of 3 x 3 or larger
// calculate inverse using gauss-jordan elimination
// https://en.wikipedia.org/wiki/Gaussian_elimination
// http://mathworld.wolfram.com/MatrixInverse.html
// http://math.uww.edu/~mcfarlat/inverse.htm
// make a copy of the matrix (only the arrays, not of the elements)
const A = mat.concat()
for (r = 0; r < rows; r++) {
A[r] = A[r].concat()
}
// create an identity matrix which in the end will contain the
// matrix inverse
const B = identity(rows).valueOf()
// loop over all columns, and perform row reductions
for (let c = 0; c < cols; c++) {
// Pivoting: Swap row c with row r, where row r contains the largest element A[r][c]
let ABig = abs(A[c][c])
let rBig = c
r = c + 1
while (r < rows) {
if (abs(A[r][c]) > ABig) {
ABig = abs(A[r][c])
rBig = r
}
r++
}
if (ABig === 0) {
throw Error('Cannot calculate inverse, determinant is zero')
}
r = rBig
if (r !== c) {
temp = A[c]; A[c] = A[r]; A[r] = temp
temp = B[c]; B[c] = B[r]; B[r] = temp
}
// eliminate non-zero values on the other rows at column c
const Ac = A[c]
const Bc = B[c]
for (r = 0; r < rows; r++) {
const Ar = A[r]
const Br = B[r]
if (r !== c) {
// eliminate value at column c and row r
if (Ar[c] !== 0) {
f = divideScalar(unaryMinus(Ar[c]), Ac[c])
// add (f * row c) to row r to eliminate the value
// at column c
for (s = c; s < cols; s++) {
Ar[s] = addScalar(Ar[s], multiply(f, Ac[s]))
}
for (s = 0; s < cols; s++) {
Br[s] = addScalar(Br[s], multiply(f, Bc[s]))
}
}
} else {
// normalize value at Acc to 1,
// divide each value on row r with the value at Acc
f = Ac[c]
for (s = c; s < cols; s++) {
Ar[s] = divideScalar(Ar[s], f)
}
for (s = 0; s < cols; s++) {
Br[s] = divideScalar(Br[s], f)
}
}
}
}
return B
}
}
})