UNPKG

ml-matrix

Version:

Matrix manipulation and computation library

1,903 lines (1,694 loc) 143 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var isAnyArray = require('is-any-array'); var rescale = require('ml-array-rescale'); const indent = ' '.repeat(2); const indentData = ' '.repeat(4); /** * @this {Matrix} * @returns {string} */ function inspectMatrix() { return inspectMatrixWithOptions(this); } function inspectMatrixWithOptions(matrix, options = {}) { const { maxRows = 15, maxColumns = 10, maxNumSize = 8, padMinus = 'auto', } = options; return `${matrix.constructor.name} { ${indent}[ ${indentData}${inspectData(matrix, maxRows, maxColumns, maxNumSize, padMinus)} ${indent}] ${indent}rows: ${matrix.rows} ${indent}columns: ${matrix.columns} }`; } function inspectData(matrix, maxRows, maxColumns, maxNumSize, padMinus) { const { rows, columns } = matrix; const maxI = Math.min(rows, maxRows); const maxJ = Math.min(columns, maxColumns); const result = []; if (padMinus === 'auto') { padMinus = false; loop: for (let i = 0; i < maxI; i++) { for (let j = 0; j < maxJ; j++) { if (matrix.get(i, j) < 0) { padMinus = true; break loop; } } } } for (let i = 0; i < maxI; i++) { let line = []; for (let j = 0; j < maxJ; j++) { line.push(formatNumber(matrix.get(i, j), maxNumSize, padMinus)); } result.push(`${line.join(' ')}`); } if (maxJ !== columns) { result[result.length - 1] += ` ... ${columns - maxColumns} more columns`; } if (maxI !== rows) { result.push(`... ${rows - maxRows} more rows`); } return result.join(`\n${indentData}`); } function formatNumber(num, maxNumSize, padMinus) { return ( num >= 0 && padMinus ? ` ${formatNumber2(num, maxNumSize - 1)}` : formatNumber2(num, maxNumSize) ).padEnd(maxNumSize); } function formatNumber2(num, len) { // small.length numbers should be as is let str = num.toString(); if (str.length <= len) return str; // (7)'0.00123' is better then (7)'1.23e-2' // (8)'0.000123' is worse then (7)'1.23e-3', let fix = num.toFixed(len); if (fix.length > len) { fix = num.toFixed(Math.max(0, len - (fix.length - len))); } if ( fix.length <= len && !fix.startsWith('0.000') && !fix.startsWith('-0.000') ) { return fix; } // well, if it's still too long the user should've used longer numbers let exp = num.toExponential(len); if (exp.length > len) { exp = num.toExponential(Math.max(0, len - (exp.length - len))); } return exp.slice(0); } function installMathOperations(AbstractMatrix, Matrix) { AbstractMatrix.prototype.add = function add(value) { if (typeof value === 'number') return this.addS(value); return this.addM(value); }; AbstractMatrix.prototype.addS = function addS(value) { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) + value); } } return this; }; AbstractMatrix.prototype.addM = function addM(matrix) { matrix = Matrix.checkMatrix(matrix); if (this.rows !== matrix.rows || this.columns !== matrix.columns) { throw new RangeError('Matrices dimensions must be equal'); } for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) + matrix.get(i, j)); } } return this; }; AbstractMatrix.add = function add(matrix, value) { const newMatrix = new Matrix(matrix); return newMatrix.add(value); }; AbstractMatrix.prototype.sub = function sub(value) { if (typeof value === 'number') return this.subS(value); return this.subM(value); }; AbstractMatrix.prototype.subS = function subS(value) { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) - value); } } return this; }; AbstractMatrix.prototype.subM = function subM(matrix) { matrix = Matrix.checkMatrix(matrix); if (this.rows !== matrix.rows || this.columns !== matrix.columns) { throw new RangeError('Matrices dimensions must be equal'); } for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) - matrix.get(i, j)); } } return this; }; AbstractMatrix.sub = function sub(matrix, value) { const newMatrix = new Matrix(matrix); return newMatrix.sub(value); }; AbstractMatrix.prototype.subtract = AbstractMatrix.prototype.sub; AbstractMatrix.prototype.subtractS = AbstractMatrix.prototype.subS; AbstractMatrix.prototype.subtractM = AbstractMatrix.prototype.subM; AbstractMatrix.subtract = AbstractMatrix.sub; AbstractMatrix.prototype.mul = function mul(value) { if (typeof value === 'number') return this.mulS(value); return this.mulM(value); }; AbstractMatrix.prototype.mulS = function mulS(value) { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) * value); } } return this; }; AbstractMatrix.prototype.mulM = function mulM(matrix) { matrix = Matrix.checkMatrix(matrix); if (this.rows !== matrix.rows || this.columns !== matrix.columns) { throw new RangeError('Matrices dimensions must be equal'); } for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) * matrix.get(i, j)); } } return this; }; AbstractMatrix.mul = function mul(matrix, value) { const newMatrix = new Matrix(matrix); return newMatrix.mul(value); }; AbstractMatrix.prototype.multiply = AbstractMatrix.prototype.mul; AbstractMatrix.prototype.multiplyS = AbstractMatrix.prototype.mulS; AbstractMatrix.prototype.multiplyM = AbstractMatrix.prototype.mulM; AbstractMatrix.multiply = AbstractMatrix.mul; AbstractMatrix.prototype.div = function div(value) { if (typeof value === 'number') return this.divS(value); return this.divM(value); }; AbstractMatrix.prototype.divS = function divS(value) { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) / value); } } return this; }; AbstractMatrix.prototype.divM = function divM(matrix) { matrix = Matrix.checkMatrix(matrix); if (this.rows !== matrix.rows || this.columns !== matrix.columns) { throw new RangeError('Matrices dimensions must be equal'); } for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) / matrix.get(i, j)); } } return this; }; AbstractMatrix.div = function div(matrix, value) { const newMatrix = new Matrix(matrix); return newMatrix.div(value); }; AbstractMatrix.prototype.divide = AbstractMatrix.prototype.div; AbstractMatrix.prototype.divideS = AbstractMatrix.prototype.divS; AbstractMatrix.prototype.divideM = AbstractMatrix.prototype.divM; AbstractMatrix.divide = AbstractMatrix.div; AbstractMatrix.prototype.mod = function mod(value) { if (typeof value === 'number') return this.modS(value); return this.modM(value); }; AbstractMatrix.prototype.modS = function modS(value) { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) % value); } } return this; }; AbstractMatrix.prototype.modM = function modM(matrix) { matrix = Matrix.checkMatrix(matrix); if (this.rows !== matrix.rows || this.columns !== matrix.columns) { throw new RangeError('Matrices dimensions must be equal'); } for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) % matrix.get(i, j)); } } return this; }; AbstractMatrix.mod = function mod(matrix, value) { const newMatrix = new Matrix(matrix); return newMatrix.mod(value); }; AbstractMatrix.prototype.modulus = AbstractMatrix.prototype.mod; AbstractMatrix.prototype.modulusS = AbstractMatrix.prototype.modS; AbstractMatrix.prototype.modulusM = AbstractMatrix.prototype.modM; AbstractMatrix.modulus = AbstractMatrix.mod; AbstractMatrix.prototype.and = function and(value) { if (typeof value === 'number') return this.andS(value); return this.andM(value); }; AbstractMatrix.prototype.andS = function andS(value) { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) & value); } } return this; }; AbstractMatrix.prototype.andM = function andM(matrix) { matrix = Matrix.checkMatrix(matrix); if (this.rows !== matrix.rows || this.columns !== matrix.columns) { throw new RangeError('Matrices dimensions must be equal'); } for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) & matrix.get(i, j)); } } return this; }; AbstractMatrix.and = function and(matrix, value) { const newMatrix = new Matrix(matrix); return newMatrix.and(value); }; AbstractMatrix.prototype.or = function or(value) { if (typeof value === 'number') return this.orS(value); return this.orM(value); }; AbstractMatrix.prototype.orS = function orS(value) { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) | value); } } return this; }; AbstractMatrix.prototype.orM = function orM(matrix) { matrix = Matrix.checkMatrix(matrix); if (this.rows !== matrix.rows || this.columns !== matrix.columns) { throw new RangeError('Matrices dimensions must be equal'); } for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) | matrix.get(i, j)); } } return this; }; AbstractMatrix.or = function or(matrix, value) { const newMatrix = new Matrix(matrix); return newMatrix.or(value); }; AbstractMatrix.prototype.xor = function xor(value) { if (typeof value === 'number') return this.xorS(value); return this.xorM(value); }; AbstractMatrix.prototype.xorS = function xorS(value) { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) ^ value); } } return this; }; AbstractMatrix.prototype.xorM = function xorM(matrix) { matrix = Matrix.checkMatrix(matrix); if (this.rows !== matrix.rows || this.columns !== matrix.columns) { throw new RangeError('Matrices dimensions must be equal'); } for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) ^ matrix.get(i, j)); } } return this; }; AbstractMatrix.xor = function xor(matrix, value) { const newMatrix = new Matrix(matrix); return newMatrix.xor(value); }; AbstractMatrix.prototype.leftShift = function leftShift(value) { if (typeof value === 'number') return this.leftShiftS(value); return this.leftShiftM(value); }; AbstractMatrix.prototype.leftShiftS = function leftShiftS(value) { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) << value); } } return this; }; AbstractMatrix.prototype.leftShiftM = function leftShiftM(matrix) { matrix = Matrix.checkMatrix(matrix); if (this.rows !== matrix.rows || this.columns !== matrix.columns) { throw new RangeError('Matrices dimensions must be equal'); } for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) << matrix.get(i, j)); } } return this; }; AbstractMatrix.leftShift = function leftShift(matrix, value) { const newMatrix = new Matrix(matrix); return newMatrix.leftShift(value); }; AbstractMatrix.prototype.signPropagatingRightShift = function signPropagatingRightShift(value) { if (typeof value === 'number') return this.signPropagatingRightShiftS(value); return this.signPropagatingRightShiftM(value); }; AbstractMatrix.prototype.signPropagatingRightShiftS = function signPropagatingRightShiftS(value) { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) >> value); } } return this; }; AbstractMatrix.prototype.signPropagatingRightShiftM = function signPropagatingRightShiftM(matrix) { matrix = Matrix.checkMatrix(matrix); if (this.rows !== matrix.rows || this.columns !== matrix.columns) { throw new RangeError('Matrices dimensions must be equal'); } for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) >> matrix.get(i, j)); } } return this; }; AbstractMatrix.signPropagatingRightShift = function signPropagatingRightShift(matrix, value) { const newMatrix = new Matrix(matrix); return newMatrix.signPropagatingRightShift(value); }; AbstractMatrix.prototype.rightShift = function rightShift(value) { if (typeof value === 'number') return this.rightShiftS(value); return this.rightShiftM(value); }; AbstractMatrix.prototype.rightShiftS = function rightShiftS(value) { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) >>> value); } } return this; }; AbstractMatrix.prototype.rightShiftM = function rightShiftM(matrix) { matrix = Matrix.checkMatrix(matrix); if (this.rows !== matrix.rows || this.columns !== matrix.columns) { throw new RangeError('Matrices dimensions must be equal'); } for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) >>> matrix.get(i, j)); } } return this; }; AbstractMatrix.rightShift = function rightShift(matrix, value) { const newMatrix = new Matrix(matrix); return newMatrix.rightShift(value); }; AbstractMatrix.prototype.zeroFillRightShift = AbstractMatrix.prototype.rightShift; AbstractMatrix.prototype.zeroFillRightShiftS = AbstractMatrix.prototype.rightShiftS; AbstractMatrix.prototype.zeroFillRightShiftM = AbstractMatrix.prototype.rightShiftM; AbstractMatrix.zeroFillRightShift = AbstractMatrix.rightShift; AbstractMatrix.prototype.not = function not() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, ~(this.get(i, j))); } } return this; }; AbstractMatrix.not = function not(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.not(); }; AbstractMatrix.prototype.abs = function abs() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.abs(this.get(i, j))); } } return this; }; AbstractMatrix.abs = function abs(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.abs(); }; AbstractMatrix.prototype.acos = function acos() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.acos(this.get(i, j))); } } return this; }; AbstractMatrix.acos = function acos(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.acos(); }; AbstractMatrix.prototype.acosh = function acosh() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.acosh(this.get(i, j))); } } return this; }; AbstractMatrix.acosh = function acosh(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.acosh(); }; AbstractMatrix.prototype.asin = function asin() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.asin(this.get(i, j))); } } return this; }; AbstractMatrix.asin = function asin(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.asin(); }; AbstractMatrix.prototype.asinh = function asinh() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.asinh(this.get(i, j))); } } return this; }; AbstractMatrix.asinh = function asinh(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.asinh(); }; AbstractMatrix.prototype.atan = function atan() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.atan(this.get(i, j))); } } return this; }; AbstractMatrix.atan = function atan(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.atan(); }; AbstractMatrix.prototype.atanh = function atanh() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.atanh(this.get(i, j))); } } return this; }; AbstractMatrix.atanh = function atanh(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.atanh(); }; AbstractMatrix.prototype.cbrt = function cbrt() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.cbrt(this.get(i, j))); } } return this; }; AbstractMatrix.cbrt = function cbrt(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.cbrt(); }; AbstractMatrix.prototype.ceil = function ceil() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.ceil(this.get(i, j))); } } return this; }; AbstractMatrix.ceil = function ceil(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.ceil(); }; AbstractMatrix.prototype.clz32 = function clz32() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.clz32(this.get(i, j))); } } return this; }; AbstractMatrix.clz32 = function clz32(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.clz32(); }; AbstractMatrix.prototype.cos = function cos() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.cos(this.get(i, j))); } } return this; }; AbstractMatrix.cos = function cos(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.cos(); }; AbstractMatrix.prototype.cosh = function cosh() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.cosh(this.get(i, j))); } } return this; }; AbstractMatrix.cosh = function cosh(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.cosh(); }; AbstractMatrix.prototype.exp = function exp() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.exp(this.get(i, j))); } } return this; }; AbstractMatrix.exp = function exp(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.exp(); }; AbstractMatrix.prototype.expm1 = function expm1() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.expm1(this.get(i, j))); } } return this; }; AbstractMatrix.expm1 = function expm1(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.expm1(); }; AbstractMatrix.prototype.floor = function floor() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.floor(this.get(i, j))); } } return this; }; AbstractMatrix.floor = function floor(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.floor(); }; AbstractMatrix.prototype.fround = function fround() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.fround(this.get(i, j))); } } return this; }; AbstractMatrix.fround = function fround(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.fround(); }; AbstractMatrix.prototype.log = function log() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.log(this.get(i, j))); } } return this; }; AbstractMatrix.log = function log(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.log(); }; AbstractMatrix.prototype.log1p = function log1p() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.log1p(this.get(i, j))); } } return this; }; AbstractMatrix.log1p = function log1p(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.log1p(); }; AbstractMatrix.prototype.log10 = function log10() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.log10(this.get(i, j))); } } return this; }; AbstractMatrix.log10 = function log10(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.log10(); }; AbstractMatrix.prototype.log2 = function log2() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.log2(this.get(i, j))); } } return this; }; AbstractMatrix.log2 = function log2(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.log2(); }; AbstractMatrix.prototype.round = function round() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.round(this.get(i, j))); } } return this; }; AbstractMatrix.round = function round(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.round(); }; AbstractMatrix.prototype.sign = function sign() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.sign(this.get(i, j))); } } return this; }; AbstractMatrix.sign = function sign(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.sign(); }; AbstractMatrix.prototype.sin = function sin() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.sin(this.get(i, j))); } } return this; }; AbstractMatrix.sin = function sin(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.sin(); }; AbstractMatrix.prototype.sinh = function sinh() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.sinh(this.get(i, j))); } } return this; }; AbstractMatrix.sinh = function sinh(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.sinh(); }; AbstractMatrix.prototype.sqrt = function sqrt() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.sqrt(this.get(i, j))); } } return this; }; AbstractMatrix.sqrt = function sqrt(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.sqrt(); }; AbstractMatrix.prototype.tan = function tan() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.tan(this.get(i, j))); } } return this; }; AbstractMatrix.tan = function tan(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.tan(); }; AbstractMatrix.prototype.tanh = function tanh() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.tanh(this.get(i, j))); } } return this; }; AbstractMatrix.tanh = function tanh(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.tanh(); }; AbstractMatrix.prototype.trunc = function trunc() { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, Math.trunc(this.get(i, j))); } } return this; }; AbstractMatrix.trunc = function trunc(matrix) { const newMatrix = new Matrix(matrix); return newMatrix.trunc(); }; AbstractMatrix.pow = function pow(matrix, arg0) { const newMatrix = new Matrix(matrix); return newMatrix.pow(arg0); }; AbstractMatrix.prototype.pow = function pow(value) { if (typeof value === 'number') return this.powS(value); return this.powM(value); }; AbstractMatrix.prototype.powS = function powS(value) { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) ** value); } } return this; }; AbstractMatrix.prototype.powM = function powM(matrix) { matrix = Matrix.checkMatrix(matrix); if (this.rows !== matrix.rows || this.columns !== matrix.columns) { throw new RangeError('Matrices dimensions must be equal'); } for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) ** matrix.get(i, j)); } } return this; }; } /** * @private * Check that a row index is not out of bounds * @param {Matrix} matrix * @param {number} index * @param {boolean} [outer] */ function checkRowIndex(matrix, index, outer) { let max = outer ? matrix.rows : matrix.rows - 1; if (index < 0 || index > max) { throw new RangeError('Row index out of range'); } } /** * @private * Check that a column index is not out of bounds * @param {Matrix} matrix * @param {number} index * @param {boolean} [outer] */ function checkColumnIndex(matrix, index, outer) { let max = outer ? matrix.columns : matrix.columns - 1; if (index < 0 || index > max) { throw new RangeError('Column index out of range'); } } /** * @private * Check that the provided vector is an array with the right length * @param {Matrix} matrix * @param {Array|Matrix} vector * @return {Array} * @throws {RangeError} */ function checkRowVector(matrix, vector) { if (vector.to1DArray) { vector = vector.to1DArray(); } if (vector.length !== matrix.columns) { throw new RangeError( 'vector size must be the same as the number of columns', ); } return vector; } /** * @private * Check that the provided vector is an array with the right length * @param {Matrix} matrix * @param {Array|Matrix} vector * @return {Array} * @throws {RangeError} */ function checkColumnVector(matrix, vector) { if (vector.to1DArray) { vector = vector.to1DArray(); } if (vector.length !== matrix.rows) { throw new RangeError('vector size must be the same as the number of rows'); } return vector; } function checkRowIndices(matrix, rowIndices) { if (!isAnyArray.isAnyArray(rowIndices)) { throw new TypeError('row indices must be an array'); } for (let i = 0; i < rowIndices.length; i++) { if (rowIndices[i] < 0 || rowIndices[i] >= matrix.rows) { throw new RangeError('row indices are out of range'); } } } function checkColumnIndices(matrix, columnIndices) { if (!isAnyArray.isAnyArray(columnIndices)) { throw new TypeError('column indices must be an array'); } for (let i = 0; i < columnIndices.length; i++) { if (columnIndices[i] < 0 || columnIndices[i] >= matrix.columns) { throw new RangeError('column indices are out of range'); } } } function checkRange(matrix, startRow, endRow, startColumn, endColumn) { if (arguments.length !== 5) { throw new RangeError('expected 4 arguments'); } checkNumber('startRow', startRow); checkNumber('endRow', endRow); checkNumber('startColumn', startColumn); checkNumber('endColumn', endColumn); if ( startRow > endRow || startColumn > endColumn || startRow < 0 || startRow >= matrix.rows || endRow < 0 || endRow >= matrix.rows || startColumn < 0 || startColumn >= matrix.columns || endColumn < 0 || endColumn >= matrix.columns ) { throw new RangeError('Submatrix indices are out of range'); } } function newArray(length, value = 0) { let array = []; for (let i = 0; i < length; i++) { array.push(value); } return array; } function checkNumber(name, value) { if (typeof value !== 'number') { throw new TypeError(`${name} must be a number`); } } function checkNonEmpty(matrix) { if (matrix.isEmpty()) { throw new Error('Empty matrix has no elements to index'); } } function sumByRow(matrix) { let sum = newArray(matrix.rows); for (let i = 0; i < matrix.rows; ++i) { for (let j = 0; j < matrix.columns; ++j) { sum[i] += matrix.get(i, j); } } return sum; } function sumByColumn(matrix) { let sum = newArray(matrix.columns); for (let i = 0; i < matrix.rows; ++i) { for (let j = 0; j < matrix.columns; ++j) { sum[j] += matrix.get(i, j); } } return sum; } function sumAll(matrix) { let v = 0; for (let i = 0; i < matrix.rows; i++) { for (let j = 0; j < matrix.columns; j++) { v += matrix.get(i, j); } } return v; } function productByRow(matrix) { let sum = newArray(matrix.rows, 1); for (let i = 0; i < matrix.rows; ++i) { for (let j = 0; j < matrix.columns; ++j) { sum[i] *= matrix.get(i, j); } } return sum; } function productByColumn(matrix) { let sum = newArray(matrix.columns, 1); for (let i = 0; i < matrix.rows; ++i) { for (let j = 0; j < matrix.columns; ++j) { sum[j] *= matrix.get(i, j); } } return sum; } function productAll(matrix) { let v = 1; for (let i = 0; i < matrix.rows; i++) { for (let j = 0; j < matrix.columns; j++) { v *= matrix.get(i, j); } } return v; } function varianceByRow(matrix, unbiased, mean) { const rows = matrix.rows; const cols = matrix.columns; const variance = []; for (let i = 0; i < rows; i++) { let sum1 = 0; let sum2 = 0; let x = 0; for (let j = 0; j < cols; j++) { x = matrix.get(i, j) - mean[i]; sum1 += x; sum2 += x * x; } if (unbiased) { variance.push((sum2 - (sum1 * sum1) / cols) / (cols - 1)); } else { variance.push((sum2 - (sum1 * sum1) / cols) / cols); } } return variance; } function varianceByColumn(matrix, unbiased, mean) { const rows = matrix.rows; const cols = matrix.columns; const variance = []; for (let j = 0; j < cols; j++) { let sum1 = 0; let sum2 = 0; let x = 0; for (let i = 0; i < rows; i++) { x = matrix.get(i, j) - mean[j]; sum1 += x; sum2 += x * x; } if (unbiased) { variance.push((sum2 - (sum1 * sum1) / rows) / (rows - 1)); } else { variance.push((sum2 - (sum1 * sum1) / rows) / rows); } } return variance; } function varianceAll(matrix, unbiased, mean) { const rows = matrix.rows; const cols = matrix.columns; const size = rows * cols; let sum1 = 0; let sum2 = 0; let x = 0; for (let i = 0; i < rows; i++) { for (let j = 0; j < cols; j++) { x = matrix.get(i, j) - mean; sum1 += x; sum2 += x * x; } } if (unbiased) { return (sum2 - (sum1 * sum1) / size) / (size - 1); } else { return (sum2 - (sum1 * sum1) / size) / size; } } function centerByRow(matrix, mean) { for (let i = 0; i < matrix.rows; i++) { for (let j = 0; j < matrix.columns; j++) { matrix.set(i, j, matrix.get(i, j) - mean[i]); } } } function centerByColumn(matrix, mean) { for (let i = 0; i < matrix.rows; i++) { for (let j = 0; j < matrix.columns; j++) { matrix.set(i, j, matrix.get(i, j) - mean[j]); } } } function centerAll(matrix, mean) { for (let i = 0; i < matrix.rows; i++) { for (let j = 0; j < matrix.columns; j++) { matrix.set(i, j, matrix.get(i, j) - mean); } } } function getScaleByRow(matrix) { const scale = []; for (let i = 0; i < matrix.rows; i++) { let sum = 0; for (let j = 0; j < matrix.columns; j++) { sum += matrix.get(i, j) ** 2 / (matrix.columns - 1); } scale.push(Math.sqrt(sum)); } return scale; } function scaleByRow(matrix, scale) { for (let i = 0; i < matrix.rows; i++) { for (let j = 0; j < matrix.columns; j++) { matrix.set(i, j, matrix.get(i, j) / scale[i]); } } } function getScaleByColumn(matrix) { const scale = []; for (let j = 0; j < matrix.columns; j++) { let sum = 0; for (let i = 0; i < matrix.rows; i++) { sum += matrix.get(i, j) ** 2 / (matrix.rows - 1); } scale.push(Math.sqrt(sum)); } return scale; } function scaleByColumn(matrix, scale) { for (let i = 0; i < matrix.rows; i++) { for (let j = 0; j < matrix.columns; j++) { matrix.set(i, j, matrix.get(i, j) / scale[j]); } } } function getScaleAll(matrix) { const divider = matrix.size - 1; let sum = 0; for (let j = 0; j < matrix.columns; j++) { for (let i = 0; i < matrix.rows; i++) { sum += matrix.get(i, j) ** 2 / divider; } } return Math.sqrt(sum); } function scaleAll(matrix, scale) { for (let i = 0; i < matrix.rows; i++) { for (let j = 0; j < matrix.columns; j++) { matrix.set(i, j, matrix.get(i, j) / scale); } } } class AbstractMatrix { static from1DArray(newRows, newColumns, newData) { let length = newRows * newColumns; if (length !== newData.length) { throw new RangeError('data length does not match given dimensions'); } let newMatrix = new Matrix(newRows, newColumns); for (let row = 0; row < newRows; row++) { for (let column = 0; column < newColumns; column++) { newMatrix.set(row, column, newData[row * newColumns + column]); } } return newMatrix; } static rowVector(newData) { let vector = new Matrix(1, newData.length); for (let i = 0; i < newData.length; i++) { vector.set(0, i, newData[i]); } return vector; } static columnVector(newData) { let vector = new Matrix(newData.length, 1); for (let i = 0; i < newData.length; i++) { vector.set(i, 0, newData[i]); } return vector; } static zeros(rows, columns) { return new Matrix(rows, columns); } static ones(rows, columns) { return new Matrix(rows, columns).fill(1); } static rand(rows, columns, options = {}) { if (typeof options !== 'object') { throw new TypeError('options must be an object'); } const { random = Math.random } = options; let matrix = new Matrix(rows, columns); for (let i = 0; i < rows; i++) { for (let j = 0; j < columns; j++) { matrix.set(i, j, random()); } } return matrix; } static randInt(rows, columns, options = {}) { if (typeof options !== 'object') { throw new TypeError('options must be an object'); } const { min = 0, max = 1000, random = Math.random } = options; if (!Number.isInteger(min)) throw new TypeError('min must be an integer'); if (!Number.isInteger(max)) throw new TypeError('max must be an integer'); if (min >= max) throw new RangeError('min must be smaller than max'); let interval = max - min; let matrix = new Matrix(rows, columns); for (let i = 0; i < rows; i++) { for (let j = 0; j < columns; j++) { let value = min + Math.round(random() * interval); matrix.set(i, j, value); } } return matrix; } static eye(rows, columns, value) { if (columns === undefined) columns = rows; if (value === undefined) value = 1; let min = Math.min(rows, columns); let matrix = this.zeros(rows, columns); for (let i = 0; i < min; i++) { matrix.set(i, i, value); } return matrix; } static diag(data, rows, columns) { let l = data.length; if (rows === undefined) rows = l; if (columns === undefined) columns = rows; let min = Math.min(l, rows, columns); let matrix = this.zeros(rows, columns); for (let i = 0; i < min; i++) { matrix.set(i, i, data[i]); } return matrix; } static min(matrix1, matrix2) { matrix1 = this.checkMatrix(matrix1); matrix2 = this.checkMatrix(matrix2); let rows = matrix1.rows; let columns = matrix1.columns; let result = new Matrix(rows, columns); for (let i = 0; i < rows; i++) { for (let j = 0; j < columns; j++) { result.set(i, j, Math.min(matrix1.get(i, j), matrix2.get(i, j))); } } return result; } static max(matrix1, matrix2) { matrix1 = this.checkMatrix(matrix1); matrix2 = this.checkMatrix(matrix2); let rows = matrix1.rows; let columns = matrix1.columns; let result = new this(rows, columns); for (let i = 0; i < rows; i++) { for (let j = 0; j < columns; j++) { result.set(i, j, Math.max(matrix1.get(i, j), matrix2.get(i, j))); } } return result; } static checkMatrix(value) { return AbstractMatrix.isMatrix(value) ? value : new Matrix(value); } static isMatrix(value) { return value != null && value.klass === 'Matrix'; } get size() { return this.rows * this.columns; } apply(callback) { if (typeof callback !== 'function') { throw new TypeError('callback must be a function'); } for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { callback.call(this, i, j); } } return this; } to1DArray() { let array = []; for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { array.push(this.get(i, j)); } } return array; } to2DArray() { let copy = []; for (let i = 0; i < this.rows; i++) { copy.push([]); for (let j = 0; j < this.columns; j++) { copy[i].push(this.get(i, j)); } } return copy; } toJSON() { return this.to2DArray(); } isRowVector() { return this.rows === 1; } isColumnVector() { return this.columns === 1; } isVector() { return this.rows === 1 || this.columns === 1; } isSquare() { return this.rows === this.columns; } isEmpty() { return this.rows === 0 || this.columns === 0; } isSymmetric() { if (this.isSquare()) { for (let i = 0; i < this.rows; i++) { for (let j = 0; j <= i; j++) { if (this.get(i, j) !== this.get(j, i)) { return false; } } } return true; } return false; } isDistance() { if (!this.isSymmetric()) return false; for (let i = 0; i < this.rows; i++) { if (this.get(i, i) !== 0) return false; } return true; } isEchelonForm() { let i = 0; let j = 0; let previousColumn = -1; let isEchelonForm = true; let checked = false; while (i < this.rows && isEchelonForm) { j = 0; checked = false; while (j < this.columns && checked === false) { if (this.get(i, j) === 0) { j++; } else if (this.get(i, j) === 1 && j > previousColumn) { checked = true; previousColumn = j; } else { isEchelonForm = false; checked = true; } } i++; } return isEchelonForm; } isReducedEchelonForm() { let i = 0; let j = 0; let previousColumn = -1; let isReducedEchelonForm = true; let checked = false; while (i < this.rows && isReducedEchelonForm) { j = 0; checked = false; while (j < this.columns && checked === false) { if (this.get(i, j) === 0) { j++; } else if (this.get(i, j) === 1 && j > previousColumn) { checked = true; previousColumn = j; } else { isReducedEchelonForm = false; checked = true; } } for (let k = j + 1; k < this.rows; k++) { if (this.get(i, k) !== 0) { isReducedEchelonForm = false; } } i++; } return isReducedEchelonForm; } echelonForm() { let result = this.clone(); let h = 0; let k = 0; while (h < result.rows && k < result.columns) { let iMax = h; for (let i = h; i < result.rows; i++) { if (result.get(i, k) > result.get(iMax, k)) { iMax = i; } } if (result.get(iMax, k) === 0) { k++; } else { result.swapRows(h, iMax); let tmp = result.get(h, k); for (let j = k; j < result.columns; j++) { result.set(h, j, result.get(h, j) / tmp); } for (let i = h + 1; i < result.rows; i++) { let factor = result.get(i, k) / result.get(h, k); result.set(i, k, 0); for (let j = k + 1; j < result.columns; j++) { result.set(i, j, result.get(i, j) - result.get(h, j) * factor); } } h++; k++; } } return result; } reducedEchelonForm() { let result = this.echelonForm(); let m = result.columns; let n = result.rows; let h = n - 1; while (h >= 0) { if (result.maxRow(h) === 0) { h--; } else { let p = 0; let pivot = false; while (p < n && pivot === false) { if (result.get(h, p) === 1) { pivot = true; } else { p++; } } for (let i = 0; i < h; i++) { let factor = result.get(i, p); for (let j = p; j < m; j++) { let tmp = result.get(i, j) - factor * result.get(h, j); result.set(i, j, tmp); } } h--; } } return result; } set() { throw new Error('set method is unimplemented'); } get() { throw new Error('get method is unimplemented'); } repeat(options = {}) { if (typeof options !== 'object') { throw new TypeError('options must be an object'); } const { rows = 1, columns = 1 } = options; if (!Number.isInteger(rows) || rows <= 0) { throw new TypeError('rows must be a positive integer'); } if (!Number.isInteger(columns) || columns <= 0) { throw new TypeError('columns must be a positive integer'); } let matrix = new Matrix(this.rows * rows, this.columns * columns); for (let i = 0; i < rows; i++) { for (let j = 0; j < columns; j++) { matrix.setSubMatrix(this, this.rows * i, this.columns * j); } } return matrix; } fill(value) { for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, value); } } return this; } neg() { return this.mulS(-1); } getRow(index) { checkRowIndex(this, index); let row = []; for (let i = 0; i < this.columns; i++) { row.push(this.get(index, i)); } return row; } getRowVector(index) { return Matrix.rowVector(this.getRow(index)); } setRow(index, array) { checkRowIndex(this, index); array = checkRowVector(this, array); for (let i = 0; i < this.columns; i++) { this.set(index, i, array[i]); } return this; } swapRows(row1, row2) { checkRowIndex(this, row1); checkRowIndex(this, row2); for (let i = 0; i < this.columns; i++) { let temp = this.get(row1, i); this.set(row1, i, this.get(row2, i)); this.set(row2, i, temp); } return this; } getColumn(index) { checkColumnIndex(this, index); let column = []; for (let i = 0; i < this.rows; i++) { column.push(this.get(i, index)); } return column; } getColumnVector(index) { return Matrix.columnVector(this.getColumn(index)); } setColumn(index, array) { checkColumnIndex(this, index); array = checkColumnVector(this, array); for (let i = 0; i < this.rows; i++) { this.set(i, index, array[i]); } return this; } swapColumns(column1, column2) { checkColumnIndex(this, column1); checkColumnIndex(this, column2); for (let i = 0; i < this.rows; i++) { let temp = this.get(i, column1); this.set(i, column1, this.get(i, column2)); this.set(i, column2, temp); } return this; } addRowVector(vector) { vector = checkRowVector(this, vector); for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) + vector[j]); } } return this; } subRowVector(vector) { vector = checkRowVector(this, vector); for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) - vector[j]); } } return this; } mulRowVector(vector) { vector = checkRowVector(this, vector); for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) * vector[j]); } } return this; } divRowVector(vector) { vector = checkRowVector(this, vector); for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) / vector[j]); } } return this; } addColumnVector(vector) { vector = checkColumnVector(this, vector); for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) + vector[i]); } } return this; } subColumnVector(vector) { vector = checkColumnVector(this, vector); for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) - vector[i]); } } return this; } mulColumnVector(vector) { vector = checkColumnVector(this, vector); for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) * vector[i]); } } return this; } divColumnVector(vector) { vector = checkColumnVector(this, vector); for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { this.set(i, j, this.get(i, j) / vector[i]); } } return this; } mulRow(index, value) { checkRowIndex(this, index); for (let i = 0; i < this.columns; i++) { this.set(index, i, this.get(index, i) * value); } return this; } mulColumn(index, value) { checkColumnIndex(this, index); for (let i = 0; i < this.rows; i++) { this.set(i, index, this.get(i, index) * value); } return this; } max(by) { if (this.isEmpty()) { return NaN; } switch (by) { case 'row': { const max = new Array(this.rows).fill(Number.NEGATIVE_INFINITY); for (let row = 0; row < this.rows; row++) { for (let column = 0; column < this.columns; column++) { if (this.get(row, column) > max[row]) { max[row] = this.get(row, column); } } } return max; } case 'column': { const max = new Array(this.columns).fill(Number.NEGATIVE_INFINITY); for (let row = 0; row < this.rows; row++) { for (let column = 0; column < this.columns; column++) { if (this.get(row, column) > max[column]) { max[column] = this.get(row, column); } } } return max; } case undefined: { let max = this.get(0, 0); for (let row = 0; row < this.rows; row++) { for (let column = 0; column < this.columns; column++) { if (this.get(row, column) > max) { max = this.get(row, column); } } } return max; } default: throw new Error(`invalid option: ${by}`); } } maxIndex() { checkNonEmpty(this); let v = this.get(0, 0); let idx = [0, 0]; for (let i = 0; i < this.rows; i++) { for (let j = 0; j < this.columns; j++) { if (this.get(i, j) > v) { v = this.get(i, j); idx[0] = i; idx[1] = j; } } } return idx; } min(by) { if (this.isEmpty()) { return NaN; } switch (by) { case 'row': { const min = new Array(this.rows).fill(Number.POSITIVE_INFINITY); for (let row = 0; row < this.rows; row++) { for (let column = 0; column < this.columns; column++) { if (this.get(row, column) < min[row]) { min[row] = this.get(row, column); } } } return min; } case 'column': { const min = new Array(this.columns).fill(Number.POSITIVE_INFINITY); for (let row = 0; row < this.rows; row++) { for (let column = 0; column < this.columns; column++) { if (this.get(row, column) < min[column]) { min[column] = this.get(row, column); } } } return min; } case undefined: { let min = this.get(0, 0); for (let row = 0; row < this.rows; row++) { for (let column = 0; column < this.columns; column++) { if (this.get(row, column) < min) { min = this.get(row