UNPKG

3.96 kBJavaScriptView Raw
1'use strict'
2
3const util = require('../../utils/index')
4const object = util.object
5const string = util.string
6
7function factory (type, config, load, typed) {
8 const matrix = load(require('../../type/matrix/function/matrix'))
9 const subtract = load(require('../arithmetic/subtract'))
10 const multiply = load(require('../arithmetic/multiply'))
11 const unaryMinus = load(require('../arithmetic/unaryMinus'))
12 const lup = load(require('../algebra/decomposition/lup'))
13
14 /**
15 * Calculate the determinant of a matrix.
16 *
17 * Syntax:
18 *
19 * math.det(x)
20 *
21 * Examples:
22 *
23 * math.det([[1, 2], [3, 4]]) // returns -2
24 *
25 * const A = [
26 * [-2, 2, 3],
27 * [-1, 1, 3],
28 * [2, 0, -1]
29 * ]
30 * math.det(A) // returns 6
31 *
32 * See also:
33 *
34 * inv
35 *
36 * @param {Array | Matrix} x A matrix
37 * @return {number} The determinant of `x`
38 */
39 const det = typed('det', {
40 'any': function (x) {
41 return object.clone(x)
42 },
43
44 'Array | Matrix': function det (x) {
45 let size
46 if (type.isMatrix(x)) {
47 size = x.size()
48 } else if (Array.isArray(x)) {
49 x = matrix(x)
50 size = x.size()
51 } else {
52 // a scalar
53 size = []
54 }
55
56 switch (size.length) {
57 case 0:
58 // scalar
59 return object.clone(x)
60
61 case 1:
62 // vector
63 if (size[0] === 1) {
64 return object.clone(x.valueOf()[0])
65 } else {
66 throw new RangeError('Matrix must be square ' +
67 '(size: ' + string.format(size) + ')')
68 }
69
70 case 2:
71 // two dimensional array
72 const rows = size[0]
73 const cols = size[1]
74 if (rows === cols) {
75 return _det(x.clone().valueOf(), rows, cols)
76 } else {
77 throw new RangeError('Matrix must be square ' +
78 '(size: ' + string.format(size) + ')')
79 }
80
81 default:
82 // multi dimensional array
83 throw new RangeError('Matrix must be two dimensional ' +
84 '(size: ' + string.format(size) + ')')
85 }
86 }
87 })
88
89 det.toTex = { 1: `\\det\\left(\${args[0]}\\right)` }
90
91 return det
92
93 /**
94 * Calculate the determinant of a matrix
95 * @param {Array[]} matrix A square, two dimensional matrix
96 * @param {number} rows Number of rows of the matrix (zero-based)
97 * @param {number} cols Number of columns of the matrix (zero-based)
98 * @returns {number} det
99 * @private
100 */
101 function _det (matrix, rows, cols) {
102 if (rows === 1) {
103 // this is a 1 x 1 matrix
104 return object.clone(matrix[0][0])
105 } else if (rows === 2) {
106 // this is a 2 x 2 matrix
107 // the determinant of [a11,a12;a21,a22] is det = a11*a22-a21*a12
108 return subtract(
109 multiply(matrix[0][0], matrix[1][1]),
110 multiply(matrix[1][0], matrix[0][1])
111 )
112 } else {
113 // Compute the LU decomposition
114 const decomp = lup(matrix)
115
116 // The determinant is the product of the diagonal entries of U (and those of L, but they are all 1)
117 let det = decomp.U[0][0]
118 for (let i = 1; i < rows; i++) {
119 det = multiply(det, decomp.U[i][i])
120 }
121
122 // The determinant will be multiplied by 1 or -1 depending on the parity of the permutation matrix.
123 // This can be determined by counting the cycles. This is roughly a linear time algorithm.
124 let evenCycles = 0
125 let i = 0
126 const visited = []
127 while (true) {
128 while (visited[i]) {
129 i++
130 }
131 if (i >= rows) break
132 let j = i
133 let cycleLen = 0
134 while (!visited[decomp.p[j]]) {
135 visited[decomp.p[j]] = true
136 j = decomp.p[j]
137 cycleLen++
138 }
139 if (cycleLen % 2 === 0) {
140 evenCycles++
141 }
142 }
143
144 return evenCycles % 2 === 0 ? det : unaryMinus(det)
145 }
146 }
147}
148
149exports.name = 'det'
150exports.factory = factory