UNPKG

nerdamer-ts

Version:

javascript light-weight symbolic math expression evaluator

361 lines 13.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Matrix = void 0; const Symbol_1 = require("./Symbol"); const Vector_1 = require("./Vector"); const Utils_1 = require("../Core/Utils"); const Errors_1 = require("../Core/Errors"); const Core_1 = require("../Functions/Core"); const LaTeX_1 = require("../LaTeX/LaTeX"); class Matrix { constructor(...m) { this.custom = true; var l = m.length, i, el = []; if ((0, Utils_1.isMatrix)(m)) { // if it's a matrix then make a clone for (i = 0; i < l; i++) { el.push(m[i].slice(0)); } } else { var row, lw, rl; for (i = 0; i < l; i++) { row = m[i]; if ((0, Utils_1.isVector)(row)) row = row.elements; if (!Array.isArray(row)) row = [row]; rl = row.length; if (lw && lw !== rl) (0, Errors_1.err)('Unable to create Matrix. Row dimensions do not match!'); el.push(row); lw = rl; } } this.elements = el; } static identity(n) { var m = new Matrix(); for (var i = 0; i < n; i++) { m.elements.push([]); for (var j = 0; j < n; j++) { m.set(i, j, i === j ? new Symbol_1.Symbol(1) : new Symbol_1.Symbol(0)); } } return m; } static fromArray(arr) { return new Matrix(...arr); } static zeroMatrix(rows, cols) { var m = new Matrix(); for (var i = 0; i < rows; i++) { m.elements.push(Vector_1.Vector.arrayPrefill(cols, new Symbol_1.Symbol(0))); } return m; } // needs be true to let the parser know not to try to cast it to a symbol get(row, column) { if (!this.elements[row]) return undefined; return this.elements[row][column]; } map(f, raw_values) { var M = new Matrix(); this.each(function (e, i, j) { M.set(i, j, f.call(M, e), raw_values); }); return M; } set(row, column, value, raw) { if (!this.elements[row]) this.elements[row] = []; this.elements[row][column] = raw ? value : ((0, Utils_1.isSymbol)(value) ? value : new Symbol_1.Symbol(value)); } cols() { return this.elements[0].length; } rows() { return this.elements.length; } row(n) { if (!n || n > this.cols()) return []; return this.elements[n - 1]; } col(n) { var nr = this.rows(), col = []; if (n > this.cols() || !n) return col; for (var i = 0; i < nr; i++) { col.push(this.elements[i][n - 1]); } return col; } eachElement(fn) { var nr = this.rows(), nc = this.cols(), i, j; for (i = 0; i < nr; i++) { for (j = 0; j < nc; j++) { fn.call(this, this.elements[i][j], i, j); } } } // ported from Sylvester.js determinant() { if (!this.isSquare()) { return null; } var M = this.toRightTriangular(); var det = M.elements[0][0], n = M.elements.length - 1, k = n, i; do { i = k - n + 1; det = (0, Core_1.multiply)(det, M.elements[i][i]); } while (--n); return det; } isSquare() { return this.elements.length === this.elements[0].length; } isSingular() { return this.isSquare() && this.determinant() === 0; } augment(m) { var r = this.rows(), rr = m.rows(); if (r !== rr) (0, Errors_1.err)("Cannot augment matrix. Rows don't match."); for (var i = 0; i < r; i++) { this.elements[i] = this.elements[i].concat(m.elements[i]); } return this; } clone() { var r = this.rows(), c = this.cols(), m = new Matrix(); for (var i = 0; i < r; i++) { m.elements[i] = []; for (var j = 0; j < c; j++) { var symbol = this.elements[i][j]; m.elements[i][j] = (0, Utils_1.isSymbol)(symbol) ? symbol.clone() : symbol; } } return m; } // ported from Sylvester.js invert() { if (!this.isSquare()) (0, Errors_1.err)('Matrix is not square!'); return (0, Utils_1.block)('SAFE', function () { var ni = this.elements.length, ki = ni, i, j; var imatrix = Matrix.identity(ni); var M = this.augment(imatrix).toRightTriangular(); var np, kp = M.elements[0].length, p, els, divisor; var inverse_elements = [], new_element; // Matrix is non-singular so there will be no zeros on the diagonal // Cycle through rows from last to first do { i = ni - 1; // First, normalise diagonal elements to 1 els = []; np = kp; inverse_elements[i] = []; divisor = M.elements[i][i]; do { p = kp - np; new_element = (0, Core_1.divide)(M.elements[i][p], divisor.clone()); els.push(new_element); // Shuffle of the current row of the right hand side into the results // array as it will not be modified by later runs through this loop if (p >= ki) { inverse_elements[i].push(new_element); } } while (--np); M.elements[i] = els; // Then, subtract this row from those above it to // give the identity matrix on the left hand side for (j = 0; j < i; j++) { els = []; np = kp; do { p = kp - np; els.push((0, Core_1.subtract)(M.elements[j][p].clone(), (0, Core_1.multiply)(M.elements[i][p].clone(), M.elements[j][i].clone()))); } while (--np); M.elements[j] = els; } } while (--ni); return Matrix.fromArray(inverse_elements); }, undefined, this); } // ported from Sylvester.js toRightTriangular() { return (0, Utils_1.block)('SAFE', function () { var M = this.clone(), els, fel, nel, n = this.elements.length, k = n, i, np, kp = this.elements[0].length, p; do { i = k - n; fel = M.elements[i][i]; if (fel.valueOf() === 0) { for (var j = i + 1; j < k; j++) { nel = M.elements[j][i]; if (nel && nel.valueOf() !== 0) { els = []; np = kp; do { p = kp - np; els.push((0, Core_1.add)(M.elements[i][p].clone(), M.elements[j][p].clone())); } while (--np); M.elements[i] = els; break; } } } var fel = M.elements[i][i]; if (fel.valueOf() !== 0) { for (j = i + 1; j < k; j++) { var multiplier = (0, Core_1.divide)(M.elements[j][i].clone(), M.elements[i][i].clone()); els = []; np = kp; do { p = kp - np; // Elements with column numbers up to an including the number // of the row that we're subtracting can safely be set straight to // zero, since that's the point of this routine and it avoids having // to loop over and correct rounding errors later els.push(p <= i ? new Symbol_1.Symbol(0) : (0, Core_1.subtract)(M.elements[j][p].clone(), (0, Core_1.multiply)(M.elements[i][p].clone(), multiplier.clone()))); } while (--np); M.elements[j] = els; } } } while (--n); return M; }, undefined, this); } transpose() { var rows = this.elements.length, cols = this.elements[0].length; var M = new Matrix(), ni = cols, i, nj, j; do { i = cols - ni; M.elements[i] = []; nj = rows; do { j = rows - nj; M.elements[i][j] = this.elements[j][i].clone(); } while (--nj); } while (--ni); return M; } // Returns true if the matrix can multiply the argument from the left canMultiplyFromLeft(matrix) { var l = (0, Utils_1.isMatrix)(matrix) ? matrix.elements.length : matrix.length; // this.columns should equal matrix.rows return (this.elements[0].length === l); } sameSize(matrix) { return this.rows() === matrix.rows() && this.cols() === matrix.cols(); } multiply(matrix) { return (0, Utils_1.block)('SAFE', function () { var M = matrix.elements || matrix; if (!this.canMultiplyFromLeft(M)) { if (this.sameSize(matrix)) { var MM = new Matrix(); var rows = this.rows(); for (var i = 0; i < rows; i++) { var e = (0, Core_1.multiply)(new Vector_1.Vector(this.elements[i]), new Vector_1.Vector(matrix.elements[i])); MM.elements[i] = e.elements; } return MM; } return null; } var ni = this.elements.length, ki = ni, i, nj, kj = M[0].length, j; var cols = this.elements[0].length, elements = [], sum, nc, c; do { i = ki - ni; elements[i] = []; nj = kj; do { j = kj - nj; sum = new Symbol_1.Symbol(0); nc = cols; do { c = cols - nc; sum = (0, Core_1.add)(sum, (0, Core_1.multiply)(this.elements[i][c], M[c][j])); } while (--nc); elements[i][j] = sum; } while (--nj); } while (--ni); return Matrix.fromArray(elements); }, undefined, this); } add(matrix, callback) { var M = new Matrix(); if (this.sameSize(matrix)) { this.eachElement(function (e, i, j) { var result = (0, Core_1.add)(e.clone(), matrix.elements[i][j].clone()); if (callback) { result = callback.call(M, result, e, matrix.elements[i][j]); } M.set(i, j, result); }); } return M; } subtract(matrix, callback) { var M = new Matrix(); if (this.sameSize(matrix)) { this.eachElement(function (e, i, j) { var result = (0, Core_1.subtract)(e.clone(), matrix.elements[i][j].clone()); if (callback) { result = callback.call(M, result, e, matrix.elements[i][j]); } M.set(i, j, result); }); } return M; } negate() { this.each(function (e) { return e.negate(); }); return this; } toVector() { if (this.rows() === 1 || this.cols() === 1) { var v = new Vector_1.Vector(); v.elements = this.elements; return v; } return this; } toString(newline, to_decimal) { var l = this.rows(), s = []; newline = newline === undefined ? '\n' : newline; for (var i = 0; i < l; i++) { s.push('[' + this.elements[i].map(function (x) { var v = to_decimal ? x.multiplier.toDecimal() : x.toString(); return x !== undefined ? v : ''; }).join(',') + ']'); } return 'matrix' + (0, Utils_1.inBrackets)(s.join(',')); } text() { return 'matrix(' + this.elements.toString('') + ')'; } latex(option) { var cols = this.cols(), elements = this.elements; return (0, Utils_1.format)('\\begin{vmatrix}{0}\\end{vmatrix}', function () { var tex = []; for (var row in elements) { var row_tex = []; for (var i = 0; i < cols; i++) { row_tex.push(LaTeX_1.LaTeX.latex.call(this.$LaTeX, elements[row][i], option)); } tex.push(row_tex.join(' & ')); } return tex.join(' \\cr '); }); } each(fn) { return this.eachElement(fn); } } exports.Matrix = Matrix; //# sourceMappingURL=Matrix.js.map