UNPKG

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