UNPKG

uapca

Version:

Uncertainty-aware principal component analysis.

1,789 lines (1,566 loc) 110 kB
// uapca v0.8.0 Copyright 2019 Jochen Görtler (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = global || self, factory(global.uapca = {})); }(this, function (exports) { 'use strict'; var defaultSource = Math.random; var normal = (function sourceRandomNormal(source) { function randomNormal(mu, sigma) { var x, r; mu = mu == null ? 0 : +mu; sigma = sigma == null ? 1 : +sigma; return function() { var y; // If available, use the second previously-generated uniform random. if (x != null) y = x, x = null; // Otherwise, generate a new x and y. else do { x = source() * 2 - 1; y = source() * 2 - 1; r = x * x + y * y; } while (!r || r > 1); return mu + sigma * y * Math.sqrt(-2 * Math.log(r) / r); }; } randomNormal.source = sourceRandomNormal; return randomNormal; })(defaultSource); const toString = Object.prototype.toString; function isAnyArray(object) { return toString.call(object).endsWith('Array]'); } var src = isAnyArray; /** * Computes the maximum of the given values * @param {Array<number>} input * @return {number} */ function max(input) { if (!src(input)) { throw new TypeError('input must be an array'); } if (input.length === 0) { throw new TypeError('input must not be empty'); } var maxValue = input[0]; for (var i = 1; i < input.length; i++) { if (input[i] > maxValue) maxValue = input[i]; } return maxValue; } /** * Computes the minimum of the given values * @param {Array<number>} input * @return {number} */ function min(input) { if (!src(input)) { throw new TypeError('input must be an array'); } if (input.length === 0) { throw new TypeError('input must not be empty'); } var minValue = input[0]; for (var i = 1; i < input.length; i++) { if (input[i] < minValue) minValue = input[i]; } return minValue; } function rescale(input) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (!src(input)) { throw new TypeError('input must be an array'); } else if (input.length === 0) { throw new TypeError('input must not be empty'); } var output; if (options.output !== undefined) { if (!src(options.output)) { throw new TypeError('output option must be an array if specified'); } output = options.output; } else { output = new Array(input.length); } var currentMin = min(input); var currentMax = max(input); if (currentMin === currentMax) { throw new RangeError('minimum and maximum input values are equal. Cannot rescale a constant array'); } var _options$min = options.min, minValue = _options$min === void 0 ? options.autoMinMax ? currentMin : 0 : _options$min, _options$max = options.max, maxValue = _options$max === void 0 ? options.autoMinMax ? currentMax : 1 : _options$max; if (minValue >= maxValue) { throw new RangeError('min option must be smaller than max option'); } var factor = (maxValue - minValue) / (currentMax - currentMin); for (var i = 0; i < input.length; i++) { output[i] = (input[i] - currentMin) * factor + minValue; } return output; } /** * @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 checkIndices(matrix, rowIndices, columnIndices) { return { row: checkRowIndices(matrix, rowIndices), column: checkColumnIndices(matrix, columnIndices), }; } function checkRowIndices(matrix, rowIndices) { if (typeof rowIndices !== 'object') { throw new TypeError('unexpected type for row indices'); } let rowOut = rowIndices.some((r) => { return r < 0 || r >= matrix.rows; }); if (rowOut) { throw new RangeError('row indices are out of range'); } if (!Array.isArray(rowIndices)) rowIndices = Array.from(rowIndices); return rowIndices; } function checkColumnIndices(matrix, columnIndices) { if (typeof columnIndices !== 'object') { throw new TypeError('unexpected type for column indices'); } let columnOut = columnIndices.some((c) => { return c < 0 || c >= matrix.columns; }); if (columnOut) { throw new RangeError('column indices are out of range'); } if (!Array.isArray(columnIndices)) columnIndices = Array.from(columnIndices); return columnIndices; } 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 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 += Math.pow(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 += Math.pow(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 += Math.pow(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); } } } function inspectMatrix() { const indent = ' '.repeat(2); const indentData = ' '.repeat(4); return `${this.constructor.name} { ${indent}[ ${indentData}${inspectData(this, indentData)} ${indent}] ${indent}rows: ${this.rows} ${indent}columns: ${this.columns} }`; } const maxRows = 15; const maxColumns = 10; const maxNumSize = 8; function inspectData(matrix, indent) { const { rows, columns } = matrix; const maxI = Math.min(rows, maxRows); const maxJ = Math.min(columns, maxColumns); const result = []; for (let i = 0; i < maxI; i++) { let line = []; for (let j = 0; j < maxJ; j++) { line.push(formatNumber(matrix.get(i, j))); } 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${indent}`); } function formatNumber(num) { const numStr = String(num); if (numStr.length <= maxNumSize) { return numStr.padEnd(maxNumSize, ' '); } const precise = num.toPrecision(maxNumSize - 2); if (precise.length <= maxNumSize) { return precise; } const exponential = num.toExponential(maxNumSize - 2); const eIndex = exponential.indexOf('e'); const e = exponential.substring(eIndex); return exponential.substring(0, maxNumSize - e.length) + e; } 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, Math.pow(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, Math.pow(this.get(i, j), matrix.get(i, j))); } } return this; }; } 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; } 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; } 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; }