UNPKG

algebra

Version:

means completeness and balancing, from the Arabic word الجبر

298 lines (230 loc) 6.97 kB
const determinant = require('laplace-determinant') const multiplication = require('matrix-multiplication') const multiDimArrayIndex = require('multidim-array-index') const staticProps = require('static-props') const tensorContraction = require('tensor-contraction') const itemsPool = require('./itemsPool.js') const toData = require('./toData.js') /** * Space of m x n matrices * * ``` * const R = algebra.R * * const R2x2 = algebra.MatrixSpace(R)(2) * ``` * * @param {Object} Scalar * * @returns {Function} anonymous with signature (numRows[, numCols]) */ function MatrixSpace (Scalar) { const { addition, equality, subtraction } = Scalar const contraction = tensorContraction.bind(null, addition) const enumerable = true /** * @param {Number} numRows * @param {Number} [numCols] if not defined it defaults to a square matrix. * * @returns {class} Matrix */ return function (numRows, numCols) { if (typeof numCols === 'undefined') numCols = numRows const dimension = numRows * numCols const indices = [numRows, numCols] const isSquare = (numRows === numCols) /** * Determinant computation is defined only if it is a square matrix. */ function computeDeterminant (matrix) { const data = toData(matrix) return determinant(data, Scalar, numRows) } /** * Matrix addition is the scalar addition for every item. */ function matrixAddition (matrix1, matrix2) { const matrixData1 = toData(matrix1) const matrixData2 = toData(matrix2) const result = [] for (let i = 0; i < dimension; i++) { result.push(addition(matrixData1[i], matrixData2[i])) } return result } /** * Matrix equality checks that all elements are equal. * It also tries to check if numCols and numRows correspond. */ function matrixEquality (matrix1, matrix2) { if (matrix1 instanceof Matrix && matrix2 instanceof Matrix) { if (matrix1.numCols !== matrix2.numCols) { return false } if (matrix1.numRows !== matrix2.numRows) { return false } } const matrixData1 = toData(matrix1) const matrixData2 = toData(matrix2) if (matrixData1.length !== matrixData2.length) { return false } for (let i = 0; i < dimension; i++) { if (!equality(matrixData1[i], matrixData2[i])) { return false } } return true } /** * Multiplies row by column to the right. * * @param {Object|Array} rightMatrix * * @returns {Object} matrix */ function matrixMultiplication (leftMatrix, rightMatrix) { const leftMatrixData = toData(leftMatrix) const rightMatrixData = toData(rightMatrix) const rowByColumnMultiplication = multiplication(Scalar)(numCols) return rowByColumnMultiplication(leftMatrixData, rightMatrixData) } /** * Matrix subtraction is the scalar subtraction for every item. */ function matrixSubtraction (matrix1, matrix2) { const matrixData1 = toData(matrix1) const matrixData2 = toData(matrix2) const result = [] for (let i = 0; i < dimension; i++) { result.push(subtraction(matrixData1[i], matrixData2[i])) } return result } /** * Calculates the matrix trace. * * @see {@link https://en.wikipedia.org/wiki/Trace_(linear_algebra)} * * @param {Object|Array} matrix * * @returns {Object} scalar */ function computeTrace (matrix) { const matrixData = toData(matrix) return contraction([0, 1], indices, matrixData) } /** * Calculates the transpose of a matrix. * * @param {Object|Array} matrix * * @returns {Array} matrix */ function transpose (matrix) { const matrixData = toData(matrix) const transposedData = [] for (let i = 0; i < numRows; i++) { for (let j = 0; j < numCols; j++) { const index = multiDimArrayIndex([numRows, numCols], [i, j]) const transposedIndex = multiDimArrayIndex([numCols, numRows], [j, i]) transposedData[transposedIndex] = matrixData[index] } } return transposedData } /** * Matrix element. */ class Matrix { constructor (data) { staticProps(this)({ data, numCols, numRows }, enumerable) staticProps(this)({ Scalar, tr: () => this.transposed }) if (isSquare) { staticProps(this)({ determinant: () => { const result = computeDeterminant(this) return new Scalar(result) }, trace: () => { const result = computeTrace(this) return new Scalar(result) } }) } } equality (matrix) { return matrixEquality(this, matrix) } get transposed () { const transposedElements = transpose(this) // Get a class matrix in the transposed matrix space. // Note that numCols and numRows order as arguments is inverted. const TransposedMatrix = MatrixSpace(Scalar)(numCols, numRows) return new TransposedMatrix(transposedElements) } addition (matrix) { const result = matrixAddition(this, matrix) return new Matrix(result) } multiplication (matrix) { const result = matrixMultiplication(this, matrix) return new Matrix(result) } subtraction (matrix) { const result = matrixSubtraction(this, matrix) return new Matrix(result) } } // Method aliases. Matrix.prototype.add = Matrix.prototype.addition Matrix.prototype.eq = Matrix.prototype.equality Matrix.prototype.equal = Matrix.prototype.equality Matrix.prototype.mul = Matrix.prototype.multiplication Matrix.prototype.sub = Matrix.prototype.subtraction staticProps(Matrix)({ numCols, numRows }) // Matrix static operators. staticProps(Matrix)({ addition: () => matrixAddition, equality: () => matrixEquality, multiplication: () => matrixMultiplication, subtraction: () => matrixSubtraction, transpose: () => transpose }) staticProps(Matrix)({ add: Matrix.addition, eq: Matrix.equality, mul: Matrix.multiplication, sub: Matrix.subtraction, tr: Matrix.transpose }) if (isSquare) { Matrix.prototype.det = Matrix.prototype.determinant staticProps(Matrix)({ determinant: () => computeDeterminant, trace: () => computeTrace }) staticProps(Matrix)({ det: Matrix.determinant }) } return Matrix } } itemsPool.set('MatrixSpace', MatrixSpace) module.exports = MatrixSpace