'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.Matrix = undefined; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _fs = require('fs'); var fs = _interopRequireWildcard(_fs); var _sylvester = require('./sylvester'); var _vector = require('./vector'); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function sign(x) { return x < 0 ? -1 : 1; } // augment a matrix M with identity rows/cols function identSize(M, m, n, k) { var e = M.elements; var i = k - 1; while (i--) { var row = []; for (var j = 0; j < n; j++) { row.push(j === i ? 1 : 0); } e.unshift(row); } for (var _i = k - 1; _i < m; _i++) { while (e[_i].length < n) { e[_i].unshift(0); } } return Matrix.create(e); // eslint-disable-line no-use-before-define } function pca(X) { var Sigma = X.transpose().x(X).x(1 / X.rows()); var svd = Sigma.svd(); return { U: svd.U, S: svd.S }; } var Matrix = exports.Matrix = function () { function Matrix() { _classCallCheck(this, Matrix); } _createClass(Matrix, [{ key: 'solve', // solve a system of linear equations (work in progress) value: function solve(b) { var lu = this.lu(); b = lu.P.x(b); var y = lu.L.forwardSubstitute(b); var x = lu.U.backSubstitute(y); return lu.P.x(x); // return this.inv().x(b); } // project a matrix onto a lower dim }, { key: 'pcaProject', value: function pcaProject(k) { var U = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : pca(this).U; var Ureduce = U.slice(1, U.rows(), 1, k); return { Z: this.x(Ureduce), U: U }; } // recover a matrix to a higher dimension }, { key: 'pcaRecover', value: function pcaRecover(U) { var k = this.cols(); var Ureduce = U.slice(1, U.rows(), 1, k); return this.x(Ureduce.transpose()); } // grab the upper triangular part of the matrix }, { key: 'triu', value: function triu(k) { if (!k) { k = 0; } return this.map(function (x, i, j) { return j - i >= k ? x : 0; }); } // unroll a matrix into a vector }, { key: 'unroll', value: function unroll() { var v = []; for (var i = 1; i <= this.cols(); i++) { for (var j = 1; j <= this.rows(); j++) { v.push(this.e(j, i)); } } return _vector.Vector.create(v); } // return a sub-block of the matrix }, { key: 'slice', value: function slice(startRow, endRow, startCol, endCol) { var x = []; if (endRow === 0) { endRow = this.rows(); } if (endCol === 0) { endCol = this.cols(); } for (var i = startRow; i <= endRow; i++) { var row = []; for (var j = startCol; j <= endCol; j++) { row.push(this.e(i, j)); } x.push(row); } return Matrix.create(x); } // Returns element (i,j) of the matrix }, { key: 'e', value: function e(i, j) { if (i < 1 || i > this.elements.length || j < 1 || j > this.elements[0].length) { return null; } return this.elements[i - 1][j - 1]; } // Returns row k of the matrix as a vector }, { key: 'row', value: function row(i) { if (i > this.elements.length) { return null; } return _vector.Vector.create(this.elements[i - 1]); } // Returns column k of the matrix as a vector }, { key: 'col', value: function col(j) { if (j > this.elements[0].length) { return null; } var col = []; var n = this.elements.length; for (var i = 0; i < n; i++) { col.push(this.elements[i][j - 1]); } return _vector.Vector.create(col); } // Returns the number of rows/columns the matrix has }, { key: 'dimensions', value: function dimensions() { return { rows: this.elements.length, cols: this.elements[0].length }; } // Returns the number of rows in the matrix }, { key: 'rows', value: function rows() { return this.elements.length; } // Returns the number of columns in the matrix }, { key: 'cols', value: function cols() { return this.elements[0].length; } }, { key: 'approxEql', value: function approxEql(matrix) { return this.eql(matrix, _sylvester.Sylvester.approxPrecision); } // Returns true iff the matrix is equal to the argument. You can supply // a vector as the argument, in which case the receiver must be a // one-column matrix equal to the vector. }, { key: 'eql', value: function eql(matrix, precision) { var M = matrix.elements || matrix; if (typeof M[0][0] === 'undefined') { M = Matrix.create(M).elements; } if (this.elements.length !== M.length || this.elements[0].length !== M[0].length) { return false; } var i = this.elements.length; var nj = this.elements[0].length; var j = void 0; while (i--) { j = nj; while (j--) { if (Math.abs(this.elements[i][j] - M[i][j]) > (precision || _sylvester.Sylvester.precision)) { return false; } } } return true; } // Returns a copy of the matrix }, { key: 'dup', value: function dup() { return Matrix.create(this.elements); } // Maps the matrix to another matrix (of the same dimensions) according to the given function }, { key: 'map', value: function map(fn) { var els = []; var i = this.elements.length; var nj = this.elements[0].length; var j = void 0; while (i--) { j = nj; els[i] = []; while (j--) { els[i][j] = fn(this.elements[i][j], i + 1, j + 1); } } return Matrix.create(els); } // Returns true iff the argument has the same dimensions as the matrix }, { key: 'isSameSizeAs', value: function isSameSizeAs(matrix) { var M = matrix.elements || matrix; if (typeof M[0][0] === 'undefined') { M = Matrix.create(M).elements; } return this.elements.length === M.length && this.elements[0].length === M[0].length; } // Returns the result of adding the argument to the matrix }, { key: 'add', value: function add(matrix) { if (typeof matrix === 'number') { return this.map(function (x) { return x + matrix; }); } var M = matrix.elements || matrix; if (typeof M[0][0] === 'undefined') { M = Matrix.create(M).elements; } if (!this.isSameSizeAs(M)) { return null; } return this.map(function (x, i, j) { return x + M[i - 1][j - 1]; }); } // Returns the result of subtracting the argument from the matrix }, { key: 'subtract', value: function subtract(matrix) { if (typeof matrix === 'number') { return this.map(function (x) { return x - matrix; }); } var M = matrix.elements || matrix; if (typeof M[0][0] === 'undefined') { M = Matrix.create(M).elements; } if (!this.isSameSizeAs(M)) { return null; } return this.map(function (x, i, j) { return x - M[i - 1][j - 1]; }); } // Returns true iff the matrix can multiply the argument from the left }, { key: 'canMultiplyFromLeft', value: function canMultiplyFromLeft(matrix) { var M = matrix.elements || matrix; if (typeof M[0][0] === 'undefined') { M = Matrix.create(M).elements; } // this.columns should equal matrix.rows return this.elements[0].length === M.length; } // Returns the result of a multiplication-style operation the matrix from the right by the argument. // If the argument is a scalar then just operate on all the elements. If the argument is // a vector, a vector is returned, which saves you having to remember calling // col(1) on the result. }, { key: 'mulOp', value: function mulOp(matrix, op) { if (!matrix.elements) { return this.map(function (x) { return op(x, matrix); }); } var returnVector = Boolean(matrix.modulus); var M = matrix.elements || matrix; if (typeof M[0][0] === 'undefined') { M = Matrix.create(M).elements; } if (!this.canMultiplyFromLeft(M)) { return null; } var e = this.elements; var rowThis = void 0; var rowElem = void 0; var elements = []; var sum = void 0; var m = e.length; var n = M[0].length; var o = e[0].length; var i = m; var j = void 0; var k = void 0; while (i--) { rowElem = []; rowThis = e[i]; j = n; while (j--) { sum = 0; k = o; while (k--) { sum += op(rowThis[k], M[k][j]); } rowElem[j] = sum; } elements[i] = rowElem; } var output = Matrix.create(elements); return returnVector ? output.col(1) : output; } // Returns the result of dividing the matrix from the right by the argument. // If the argument is a scalar then just divide all the elements. If the argument is // a vector, a vector is returned, which saves you having to remember calling // col(1) on the result. }, { key: 'div', value: function div(matrix) { return this.mulOp(matrix, function (x, y) { return x / y; }); } // Returns the result of multiplying the matrix from the right by the argument. // If the argument is a scalar then just multiply all the elements. If the argument is // a vector, a vector is returned, which saves you having to remember calling // col(1) on the result. }, { key: 'multiply', value: function multiply(matrix) { return this.mulOp(matrix, function (x, y) { return x * y; }); } }, { key: 'x', value: function x(matrix) { return this.multiply(matrix); } }, { key: 'elementMultiply', value: function elementMultiply(v) { return this.map(function (k, i, j) { return v.e(i, j) * k; }); } // sum all elements in the matrix }, { key: 'sum', value: function sum() { var sum = 0; this.map(function (x) { // eslint-disable-line array-callback-return sum += x; }); return sum; } // Returns a Vector of each colum averaged. }, { key: 'mean', value: function mean() { var dim = this.dimensions(); var r = []; for (var i = 1; i <= dim.cols; i++) { r.push(this.col(i).sum() / dim.rows); } return _vector.Vector.create(r); } // Returns a Vector of each column's standard deviation }, { key: 'std', value: function std() { var dim = this.dimensions(); var mMean = this.mean(); var r = []; for (var i = 1; i <= dim.cols; i++) { var meanDiff = this.col(i).subtract(mMean.e(i)); meanDiff = meanDiff.multiply(meanDiff); r.push(Math.sqrt(meanDiff.sum() / dim.rows)); } return _vector.Vector.create(r); } }, { key: 'column', value: function column(n) { return this.col(n); } // element-wise log }, { key: 'log', value: function log() { return this.map(function (x) { return Math.log(x); }); } // Returns a submatrix taken from the matrix // Argument order is: start row, start col, nrows, ncols // Element selection wraps if the required index is outside the matrix's bounds, so you could // use this to perform row/column cycling or copy-augmenting. }, { key: 'minor', value: function minor(a, b, c, d) { var elements = []; var ni = c; var i = void 0; var nj = void 0; var j = void 0; var rows = this.elements.length; var cols = this.elements[0].length; while (ni--) { i = c - ni - 1; elements[i] = []; nj = d; while (nj--) { j = d - nj - 1; elements[i][j] = this.elements[(a + i - 1) % rows][(b + j - 1) % cols]; } } return Matrix.create(elements); } // Returns the transpose of the matrix }, { key: 'transpose', value: function transpose() { var rows = this.elements.length; var cols = this.elements[0].length; var elements = []; var i = cols; var j = void 0; while (i--) { j = rows; elements[i] = []; while (j--) { elements[i][j] = this.elements[j][i]; } } return Matrix.create(elements); } // Returns true iff the matrix is square }, { key: 'isSquare', value: function isSquare() { return this.elements.length === this.elements[0].length; } // Returns the (absolute) largest element of the matrix }, { key: 'max', value: function max() { var m = 0; var i = this.elements.length; var nj = this.elements[0].length; var j = void 0; while (i--) { j = nj; while (j--) { if (Math.abs(this.elements[i][j]) > Math.abs(m)) { m = this.elements[i][j]; } } } return m; } // Returns the indeces of the first match found by reading row-by-row from left to right }, { key: 'indexOf', value: function indexOf(x) { var ni = this.elements.length; var i = void 0; var nj = this.elements[0].length; var j = void 0; for (i = 0; i < ni; i++) { for (j = 0; j < nj; j++) { if (this.elements[i][j] === x) { return { i: i + 1, j: j + 1 }; } } } return null; } // If the matrix is square, returns the diagonal elements as a vector. // Otherwise, returns null. }, { key: 'diagonal', value: function diagonal() { if (!this.isSquare) { return null; } var els = []; var n = this.elements.length; for (var i = 0; i < n; i++) { els.push(this.elements[i][i]); } return _vector.Vector.create(els); } // Make the matrix upper (right) triangular by Gaussian elimination. // This method only adds multiples of rows to other rows. No rows are // scaled up or switched, and the determinant is preserved. }, { key: 'toRightTriangular', value: function toRightTriangular() { var M = this.dup(); var els = void 0; var n = this.elements.length; var i = void 0; var j = void 0; var np = this.elements[0].length; var p = void 0; for (i = 0; i < n; i++) { if (M.elements[i][i] === 0) { for (j = i + 1; j < n; j++) { if (M.elements[j][i] !== 0) { els = []; for (p = 0; p < np; p++) { els.push(M.elements[i][p] + M.elements[j][p]); } M.elements[i] = els; break; } } } if (M.elements[i][i] !== 0) { for (j = i + 1; j < n; j++) { var multiplier = M.elements[j][i] / M.elements[i][i]; els = []; for (p = 0; p < np; p++) { // 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 ? 0 : M.elements[j][p] - M.elements[i][p] * multiplier); } M.elements[j] = els; } } } return M; } }, { key: 'toUpperTriangular', value: function toUpperTriangular() { return this.toRightTriangular(); } // Returns the determinant for square matrices }, { key: 'determinant', value: function determinant() { if (!this.isSquare()) { return null; } if (this.cols === 1 && this.rows === 1) { return this.row(1); } if (this.cols === 0 && this.rows === 0) { return 1; } var M = this.toRightTriangular(); var det = M.elements[0][0]; var n = M.elements.length; for (var i = 1; i < n; i++) { det *= M.elements[i][i]; } return det; } }, { key: 'det', value: function det() { return this.determinant(); } // Returns true iff the matrix is singular }, { key: 'isSingular', value: function isSingular() { return this.isSquare() && this.determinant() === 0; } // Returns the trace for square matrices }, { key: 'trace', value: function trace() { if (!this.isSquare()) { return null; } var tr = this.elements[0][0]; var n = this.elements.length; for (var i = 1; i < n; i++) { tr += this.elements[i][i]; } return tr; } }, { key: 'tr', value: function tr() { return this.trace(); } // Returns the rank of the matrix }, { key: 'rank', value: function rank() { var M = this.toRightTriangular(); var rank = 0; var i = this.elements.length; var nj = this.elements[0].length; var j = void 0; while (i--) { j = nj; while (j--) { if (Math.abs(M.elements[i][j]) > _sylvester.Sylvester.precision) { rank++; break; } } } return rank; } }, { key: 'rk', value: function rk() { return this.rank(); } // Returns the result of attaching the given argument to the right-hand side of the matrix }, { key: 'augment', value: function augment(matrix) { var M = matrix.elements || matrix; if (typeof M[0][0] === 'undefined') { M = Matrix.create(M).elements; } var T = this.dup(); var cols = T.elements[0].length; var i = T.elements.length; var nj = M[0].length; var j = void 0; if (i !== M.length) { return null; } while (i--) { j = nj; while (j--) { T.elements[i][cols + j] = M[i][j]; } } return T; } // Returns the inverse (if one exists) using Gauss-Jordan }, { key: 'inverse', value: function inverse() { if (!this.isSquare() || this.isSingular()) { return null; } var n = this.elements.length; var i = n; var j = void 0; var M = this.augment(Matrix.I(n)).toRightTriangular(); var np = M.elements[0].length; var p = void 0; var els = void 0; var divisor = void 0; var inverseElements = []; // Matrix is non-singular so there will be no zeros on the diagonal // Cycle through rows from last to first var newElement = void 0; while (i--) { // First, normalise diagonal elements to 1 els = []; inverseElements[i] = []; divisor = M.elements[i][i]; for (p = 0; p < np; p++) { newElement = M.elements[i][p] / divisor; els.push(newElement); // Shuffle off 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 >= n) { inverseElements[i].push(newElement); } } M.elements[i] = els; // Then, subtract this row from those above it to // give the identity matrix on the left hand side j = i; while (j--) { els = []; for (p = 0; p < np; p++) { els.push(M.elements[j][p] - M.elements[i][p] * M.elements[j][i]); } M.elements[j] = els; } } return Matrix.create(inverseElements); } }, { key: 'inv', value: function inv() { return this.inverse(); } // Returns the result of rounding all the elements }, { key: 'round', value: function round() { return this.map(function (x) { return Math.round(x); }); } // Returns a copy of the matrix with elements set to the given value if they // differ from it by less than Sylvester.precision }, { key: 'snapTo', value: function snapTo(x) { return this.map(function (p) { return Math.abs(p - x) <= _sylvester.Sylvester.precision ? x : p; }); } // Returns a string representation of the matrix }, { key: 'inspect', value: function inspect() { var matrixRows = []; var n = this.elements.length; for (var i = 0; i < n; i++) { matrixRows.push(_vector.Vector.create(this.elements[i]).inspect()); } return matrixRows.join('\n'); } // Returns a array representation of the matrix }, { key: 'toArray', value: function toArray() { var matrixRows = []; var n = this.elements.length; for (var i = 0; i < n; i++) { matrixRows.push(this.elements[i]); } return matrixRows; } // Set the matrix's elements from an array. If the argument passed // is a vector, the resulting matrix will be a single column. }, { key: 'setElements', value: function setElements(els) { var i = void 0; var j = void 0; var elements = els.elements || els; if (typeof elements[0][0] !== 'undefined') { i = elements.length; this.elements = []; while (i--) { j = elements[i].length; this.elements[i] = []; while (j--) { this.elements[i][j] = elements[i][j]; } } return this; } var n = elements.length; this.elements = []; for (i = 0; i < n; i++) { this.elements.push([elements[i]]); } return this; } // return the indexes of the columns with the largest value // for each row }, { key: 'maxColumnIndexes', value: function maxColumnIndexes() { var maxes = []; for (var i = 1; i <= this.rows(); i++) { var max = null; var maxIndex = -1; for (var j = 1; j <= this.cols(); j++) { if (max === null || this.e(i, j) > max) { max = this.e(i, j); maxIndex = j; } } maxes.push(maxIndex); } return _vector.Vector.create(maxes); } // return the largest values in each row }, { key: 'maxColumns', value: function maxColumns() { var maxes = []; for (var i = 1; i <= this.rows(); i++) { var max = null; for (var j = 1; j <= this.cols(); j++) { if (max === null || this.e(i, j) > max) { max = this.e(i, j); } } maxes.push(max); } return _vector.Vector.create(maxes); } // return the indexes of the columns with the smallest values // for each row }, { key: 'minColumnIndexes', value: function minColumnIndexes() { var mins = []; for (var i = 1; i <= this.rows(); i++) { var min = null; var minIndex = -1; for (var j = 1; j <= this.cols(); j++) { if (min === null || this.e(i, j) < min) { min = this.e(i, j); minIndex = j; } } mins.push(minIndex); } return _vector.Vector.create(mins); } // return the smallest values in each row }, { key: 'minColumns', value: function minColumns() { var mins = []; for (var i = 1; i <= this.rows(); i++) { var min = null; for (var j = 1; j <= this.cols(); j++) { if (min === null || this.e(i, j) < min) { min = this.e(i, j); } } mins.push(min); } return _vector.Vector.create(mins); } // perorm a partial pivot on the matrix. essentially move the largest // row below-or-including the pivot and replace the pivot's row with it. // a pivot matrix is returned so multiplication can perform the transform. }, { key: 'partialPivot', value: function partialPivot(k, j, P, A) { var maxIndex = 0; var maxValue = 0; for (var i = k; i <= A.rows(); i++) { if (Math.abs(A.e(i, j)) > maxValue) { maxValue = Math.abs(A.e(k, j)); maxIndex = i; } } if (maxIndex !== k) { var tmp = A.elements[k - 1]; A.elements[k - 1] = A.elements[maxIndex - 1]; A.elements[maxIndex - 1] = tmp; P.elements[k - 1][k - 1] = 0; P.elements[k - 1][maxIndex - 1] = 1; P.elements[maxIndex - 1][maxIndex - 1] = 0; P.elements[maxIndex - 1][k - 1] = 1; } return P; } // solve lower-triangular matrix * x = b via forward substitution }, { key: 'forwardSubstitute', value: function forwardSubstitute(b) { var xa = []; for (var i = 1; i <= this.rows(); i++) { var w = 0; for (var j = 1; j < i; j++) { w += this.e(i, j) * xa[j - 1]; } xa.push((b.e(i) - w) / this.e(i, i)); } return _vector.Vector.create(xa); } // solve an upper-triangular matrix * x = b via back substitution }, { key: 'backSubstitute', value: function backSubstitute(b) { var xa = []; for (var i = this.rows(); i > 0; i--) { var w = 0; for (var j = this.cols(); j > i; j--) { w += this.e(i, j) * xa[this.rows() - j]; } xa.push((b.e(i) - w) / this.e(i, i)); } return _vector.Vector.create(xa.reverse()); } }, { key: 'svdJs', value: function svdJs() { var A = this; var V = Matrix.I(A.rows()); var S = A.transpose(); var U = Matrix.I(A.cols()); var err = Number.MAX_VALUE; var i = 0; var maxLoop = 100; while (err > 2.2737e-13 && i < maxLoop) { var qr = S.transpose().qrJs(); S = qr.R; V = V.x(qr.Q); qr = S.transpose().qrJs(); U = U.x(qr.Q); S = qr.R; var e = S.triu(1).unroll().norm(); var f = S.diagonal().norm(); if (f === 0) { f = 1; } err = e / f; i++; } var ss = S.diagonal(); var s = []; for (var _i2 = 1; _i2 <= ss.cols(); _i2++) { var ssn = ss.e(_i2); s.push(Math.abs(ssn)); if (ssn < 0) { for (var j = 0; j < U.rows(); j++) { V.elements[j][_i2 - 1] = -V.elements[j][_i2 - 1]; } } } return { U: U, S: _vector.Vector.create(s).toDiagonalMatrix(), V: V }; } // QR decomposition in pure javascript }, { key: 'qrJs', value: function qrJs() { var m = this.rows(); var n = this.cols(); var Q = Matrix.I(m); var A = this; for (var k = 1; k < Math.min(m, n); k++) { var ak = A.slice(k, 0, k, k).col(1); var oneZero = [1]; while (oneZero.length <= m - k) { oneZero.push(0); } oneZero = _vector.Vector.create(oneZero); var vk = ak.add(oneZero.x(ak.norm() * sign(ak.e(1)))); var Vk = Matrix.create(vk); var Hk = Matrix.I(m - k + 1).subtract(Vk.x(2).x(Vk.transpose()).div(Vk.transpose().x(Vk).e(1, 1))); var Qk = identSize(Hk, m, n, k); A = Qk.x(A); // slow way to compute Q Q = Q.x(Qk); } return { Q: Q, R: A }; } // pure Javascript LU factorization }, { key: 'luJs', value: function luJs() { var A = this.dup(); var L = Matrix.I(A.rows()); var P = Matrix.I(A.rows()); var U = Matrix.Zeros(A.rows(), A.cols()); var p = 1; for (var k = 1; k <= Math.min(A.cols(), A.rows()); k++) { P = A.partialPivot(k, p, P, A, L); for (var i = k + 1; i <= A.rows(); i++) { var l = A.e(i, p) / A.e(k, p); L.elements[i - 1][k - 1] = l; for (var j = k + 1; j <= A.cols(); j++) { A.elements[i - 1][j - 1] -= A.e(k, j) * l; } } for (var _j = k; _j <= A.cols(); _j++) { U.elements[k - 1][_j - 1] = A.e(k, _j); } if (p < A.cols()) { p++; } } return { L: L, U: U, P: P }; } // Constructor function }], [{ key: 'create', value: function create(aElements) { var M = new Matrix().setElements(aElements); return M; } // Identity matrix of size n }, { key: 'I', value: function I(n) { var els = []; var i = n; var j = void 0; while (i--) { j = n; els[i] = []; while (j--) { els[i][j] = i === j ? 1 : 0; } } return Matrix.create(els); } }, { key: 'loadFile', value: function loadFile(file) { var contents = fs.readFileSync(file, 'utf-8'); var matrix = []; var rowArray = contents.split('\n'); for (var i = 0; i < rowArray.length; i++) { var d = rowArray[i].split(','); if (d.length > 1) { matrix.push(d); } } var M = new Matrix(); return M.setElements(matrix); } // Diagonal matrix - all off-diagonal elements are zero }, { key: 'Diagonal', value: function Diagonal(elements) { var i = elements.length; var M = Matrix.I(i); while (i--) { M.elements[i][i] = elements[i]; } return M; } // Rotation matrix about some axis. If no axis is // supplied, assume we're after a 2D transform }, { key: 'Rotation', value: function Rotation(theta, a) { if (!a) { return Matrix.create([[Math.cos(theta), -Math.sin(theta)], [Math.sin(theta), Math.cos(theta)]]); } var axis = a.dup(); if (axis.elements.length !== 3) { return null; } var mod = axis.modulus(); var x = axis.elements[0] / mod; var y = axis.elements[1] / mod; var z = axis.elements[2] / mod; var s = Math.sin(theta); // Formula derived here: http://www.gamedev.net/reference/articles/article1199.asp // That proof rotates the co-ordinate system so theta // becomes -theta and sin becomes -sin here. var c = Math.cos(theta); var t = 1 - c; return Matrix.create([[t * x * x + c, t * x * y - s * z, t * x * z + s * y], [t * x * y + s * z, t * y * y + c, t * y * z - s * x], [t * x * z - s * y, t * y * z + s * x, t * z * z + c]]); } // Special case rotations }, { key: 'RotationX', value: function RotationX(t) { var c = Math.cos(t); var s = Math.sin(t); return Matrix.create([[1, 0, 0], [0, c, -s], [0, s, c]]); } }, { key: 'RotationY', value: function RotationY(t) { var c = Math.cos(t); var s = Math.sin(t); return Matrix.create([[c, 0, s], [0, 1, 0], [-s, 0, c]]); } }, { key: 'RotationZ', value: function RotationZ(t) { var c = Math.cos(t); var s = Math.sin(t); return Matrix.create([[c, -s, 0], [s, c, 0], [0, 0, 1]]); } // Random matrix of n rows, m columns }, { key: 'Random', value: function Random(n, m) { if (arguments.length === 1) { m = n; } return Matrix.Zero(n, m).map(function () { return Math.random(); }); } }, { key: 'Fill', value: function Fill(n, m, v) { if (arguments.length === 2) { v = m; m = n; } var els = []; var i = n; var j = void 0; while (i--) { j = m; els[i] = []; while (j--) { els[i][j] = v; } } return Matrix.create(els); } // Matrix filled with zeros }, { key: 'Zero', value: function Zero(n, m) { return Matrix.Fill(n, m, 0); } // Matrix filled with zeros }, { key: 'Zeros', value: function Zeros(n, m) { return Matrix.Zero(n, m); } // Matrix filled with ones }, { key: 'One', value: function One(n, m) { return Matrix.Fill(n, m, 1); } // Matrix filled with ones }, { key: 'Ones', value: function Ones(n, m) { return Matrix.One(n, m); } }]); return Matrix; }(); // otherwise use the slower pure Javascript versions Matrix.prototype.svd = Matrix.prototype.svdJs; Matrix.prototype.qr = Matrix.prototype.qrJs; Matrix.prototype.lu = Matrix.prototype.luJs;