UNPKG

algebra

Version:

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

319 lines (247 loc) 7.64 kB
const itemsPool = require('./itemsPool') const matrixMultiplication = require('matrix-multiplication') const staticProps = require('static-props') const toData = require('./toData') /** * Space of vectors * * ``` * const V = VectorSpace(R)(2) * * const v = new V([1, 2]) * ``` * * @param {Object} Scalar * * @returns {Function} anonymous with signature (dimension) */ function VectorSpace (Scalar) { const { addition, equality, multiplication, subtraction } = Scalar const enumerable = true /** * @param {Number} dimension * * @returns {Function} Vector */ return function (dimension) { /** * Computes the cross product of two vectors. * * It is defined only in dimension 3. * * @param {Object|Array} vector1 * @param {Object|Array} vector2 * * @returns {Array} vector */ function crossProduct (vector1, vector2) { const vectorData1 = toData(vector1) const vectorData2 = toData(vector2) const ux = vectorData1[0] const uy = vectorData1[1] const uz = vectorData1[2] const vx = vectorData2[0] const vy = vectorData2[1] const vz = vectorData2[2] const vector = [] vector.push(subtraction(multiplication(uy, vz), multiplication(uz, vy))) vector.push(subtraction(multiplication(uz, vx), multiplication(ux, vz))) vector.push(subtraction(multiplication(ux, vy), multiplication(uy, vx))) return vector } /** * Multiply a column vector by matrix on right side * * @returns {Object} scalar * @param leftVector * @param rightMatrix */ function multiplicationByMatrix (leftVector, rightMatrix) { const leftVectorData = toData(leftVector) const rightMatrixData = toData(rightMatrix) const rowByColumnMultiplication = matrixMultiplication(Scalar)(dimension) return rowByColumnMultiplication(leftVectorData, rightMatrixData) } /** * Norm of a vector * * Given v = (x1, x2, ... xN) * * norm is defined as n = x1 * x1 + x2 * x2 + ... + xN * xN * * @param {Object|Array} vector * * @returns {Object} scalar */ function norm (vector) { const data = toData(vector) let value = multiplication(data[0], data[0]) for (let i = 1; i < dimension; i++) { value = addition(value, multiplication(data[i], data[i])) } return new Scalar(value) } /** * Scalar product * * @see {@link https://en.wikipedia.org/wiki/Dot_product} * * @param {Object|Array} vector1 * @param {Object|Array} vector2 * * @returns {*} scalar */ function scalarProduct (vector1, vector2) { const vectorData1 = toData(vector1) const vectorData2 = toData(vector2) if (vectorData1.length !== vectorData2.length) { throw new TypeError('Vectors have not the same dimension') } let result = multiplication(vectorData1[0], vectorData2[0]) for (let i = 1; i < dimension; i++) { result = addition(result, multiplication(vectorData1[i], vectorData2[i])) } return result } /** * Vector addition is the scalar addition for every coordinate. */ function vectorAddition (vector1, vector2) { const vectorData1 = toData(vector1) const vectorData2 = toData(vector2) const result = [] for (let i = 0; i < dimension; i++) { result.push(addition(vectorData1[i], vectorData2[i])) } return result } /** * Vector equality checks that all coordinates are equal. */ function vectorEquality (vector1, vector2) { const vectorData1 = toData(vector1) const vectorData2 = toData(vector2) if (vectorData1.length !== vectorData2.length) { return false } for (let i = 0; i < dimension; i++) { if (!equality(vectorData1[i], vectorData2[i])) { return false } } return true } /** * Vector subtraction is the scalar subtraction for every coordinate. */ function vectorSubtraction (vector1, vector2) { const vectorData1 = toData(vector1) const vectorData2 = toData(vector2) const result = [] for (let i = 0; i < dimension; i++) { result.push(subtraction(vectorData1[i], vectorData2[i])) } return result } /** * Vector element. */ class Vector { constructor (data) { staticProps(this)({ data }, enumerable) staticProps(this)({ norm: norm(data), dimension, Scalar }) // Method aliases. staticProps(this)({ add: () => this.addition, eq: () => this.equality, equals: () => this.equality, mul: () => this.multiplication, scalar: () => this.scalarProduct, sub: () => this.subtraction }) } addition (vector) { const result = vectorAddition(this, vector) return new Vector(result) } equality (vector) { return vectorEquality(this, vector) } /** * Multiplication of a vector by a right matrix. * * Actually the vector it is supposed to be transposed, so it * becomes a row-vector, while by convention all vectors are column-vectors, * and after it is transposed, it can be multiplied by a right matrix. * * The transposition happens here implicitly. * * If you do not know what it means, do not worry. It is part of * Geometry first course at the first year of University, and you can * ignore it, since it has no consequences but it is hard to spot. * * I would like to thank and remember here in this comment, my awesome * prof. of Geometry. Thank you, Monti Bragadin. */ multiplication (rightMatrix) { const MatrixSpace = itemsPool.get('MatrixSpace') const leftVectorData = this.data const result = multiplicationByMatrix(leftVectorData, rightMatrix) const rightNumRows = dimension const rightNumCols = result.length / rightNumRows const Matrix = MatrixSpace(Scalar)(rightNumRows, rightNumCols) return new Matrix(result) } scalarProduct (vector) { const result = scalarProduct(this, vector) return new Scalar(result) } subtraction (vector) { const result = vectorSubtraction(this, vector) return new Vector(result) } } staticProps(Vector)({ dimension }, enumerable) // Vector static operators. staticProps(Vector)({ addition: () => vectorAddition, equality: () => vectorEquality, norm: () => norm, scalarProduct: () => scalarProduct, subtraction: () => vectorSubtraction }) staticProps(Vector)({ add: () => Vector.addition, eq: () => Vector.equality, scalar: () => Vector.scalarProduct, sub: () => Vector.subtraction }) function crossProductMethod (vector) { const data = this.data const result = crossProduct(data, vector) return new Vector(result) } if (dimension === 3) { Vector.prototype.cross = crossProductMethod Vector.prototype.crossProduct = crossProductMethod staticProps(Vector)({ crossProduct: () => crossProduct, cross: () => crossProduct }) } return Vector } } itemsPool.set('VectorSpace', VectorSpace) module.exports = VectorSpace