algebra
Version:
means completeness and balancing, from the Arabic word الجبر
2,057 lines (1,658 loc) • 55.2 kB
JavaScript
require=(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
const no = require('not-defined')
const staticProps = require('static-props')
const pkg = require('./package.json')
/**
* Prepend package name to error message
*/
function msg (str) {
return pkg.name + ': ' + str
}
const error = {}
staticProps(error)({
argumentIsNotInGroup: msg('argument is not contained in group set'),
equalityIsNotReflexive: msg('"equality" is not reflexive'),
identityIsNotInGroup: msg('"identity" must be contained in group set'),
identityIsNotNeutral: msg('"identity" is not neutral')
})
/**
* Defines an algebra group structure
*
* @param {Object} given
* @param {*} given.identity a.k.a neutral element
* @param {Function} given.contains
* @param {Function} given.equality
* @param {Function} given.compositionLaw
* @param {Function} given.inversion
* @param {Object} [naming]
* @param {String} [naming.identity=zero]
* @param {String} [naming.contains=contains]
* @param {String} [naming.equality=equality]
* @param {String} [naming.disequality=disequality]
* @param {String} [naming.compositionLaw=addition]
* @param {String} [naming.inversion=negation]
* @param {String} [naming.inverseCompositionLaw=subtraction]
* @param {String} [naming.notContains=notContains]
*
* @returns {Object} group
*/
function algebraGroup (given, naming) {
if (no(given)) given = {}
if (no(naming)) naming = {}
// default attribute naming
const defaultNaming = {
compositionLaw: 'addition',
contains: 'contains',
disequality: 'disequality',
equality: 'equality',
identity: 'zero',
inverseCompositionLaw: 'subtraction',
inversion: 'negation',
notContains: 'notContains'
}
/**
* Returns a prop custom name or its default
*
* @param {String} name
*
* @returns {String} actualName
*/
function prop (name) {
if (typeof naming[name] === 'string') return naming[name]
else return defaultNaming[name]
}
/**
* Wraps operator by checking if arguments are contained in group.
*
* @param {Object} given operators
* @param {String} operator name
* @param {Number} arity
*
* @returns {Function} internalOperator
*/
function internalOperator (given, operator, arity) {
return function () {
const args = [].slice.call(arguments, 0, arity)
if (contains.apply(null, args)) {
return given[operator].apply(null, args)
} else {
throw new TypeError(error.argumentIsNotInGroup)
}
}
}
// operators
const secureCompositionLaw = internalOperator(given, 'compositionLaw', 2)
const secureInversion = internalOperator(given, 'inversion', 1)
function compositionLaw () {
return [].slice.call(arguments).reduce(secureCompositionLaw)
}
function contains () {
const arg = [].slice.call(arguments)
for (var i in arg) {
if (!given.contains(arg[i])) {
return false
}
}
return true
}
function notContains (a) { return !contains(a) }
function disequality (a, b) { return !given.equality(a, b) }
function inverseCompositionLaw (a) {
const rest = [].slice.call(arguments, 1)
return secureCompositionLaw(a, rest.map(secureInversion).reduce(secureCompositionLaw))
}
// identity element
const e = given.identity
// Check that e=e.
if (given.equality(e, e) !== true) {
throw new TypeError(error.equalityIsNotReflexive)
}
if (!given.contains(e)) {
throw new TypeError(error.identityIsNotInGroup)
}
// Check that e+e=e.
if (!given.equality(given.compositionLaw(e, e), e)) {
throw new TypeError(error.identityIsNotNeutral)
}
const definition = {}
definition[prop('identity')] = e
// Wrap functions otherwise staticProps will treat them as getters.
definition[prop('contains')] = () => contains
definition[prop('notContains')] = () => notContains
definition[prop('compositionLaw')] = () => compositionLaw
definition[prop('inversion')] = () => secureInversion
definition[prop('inverseCompositionLaw')] = () => inverseCompositionLaw
definition[prop('equality')] = () => given.equality
definition[prop('disequality')] = () => disequality
const group = {}
// Add immutable props to group.
staticProps(group)(definition)
return group
}
staticProps(algebraGroup)({ error })
module.exports = algebraGroup
},{"./package.json":2,"not-defined":12,"static-props":13}],2:[function(require,module,exports){
module.exports={
"_args": [
[
"algebra-group@0.6.2",
"/Users/io/github.com/fibo/algebra"
]
],
"_from": "algebra-group@0.6.2",
"_id": "algebra-group@0.6.2",
"_inBundle": false,
"_integrity": "sha512-//pQGrgIU/Yn9B3UlquJao+GNgKILnD4j5SPpKxQCVe234Mqce6VclldvuDNa8r8HpY67up8UyY8IwIeQeVVAA==",
"_location": "/algebra-group",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "algebra-group@0.6.2",
"name": "algebra-group",
"escapedName": "algebra-group",
"rawSpec": "0.6.2",
"saveSpec": null,
"fetchSpec": "0.6.2"
},
"_requiredBy": [
"/algebra-ring"
],
"_resolved": "https://registry.npmjs.org/algebra-group/-/algebra-group-0.6.2.tgz",
"_spec": "0.6.2",
"_where": "/Users/io/github.com/fibo/algebra",
"author": {
"name": "Gianluca Casati",
"url": "http://g14n.info"
},
"bugs": {
"url": "https://github.com/fibo/algebra-group/issues"
},
"dependencies": {
"not-defined": "^2.0.1",
"static-props": "^1.1.1"
},
"description": "defines and algebra group structure",
"devDependencies": {
"dot-editorconfig": "^1.1.0",
"pre-commit": "^1.2.2",
"standa": "^2.0.1",
"tape": "^4.9.0"
},
"homepage": "http://g14n.info/algebra-group",
"keywords": [
"algebra"
],
"license": "MIT",
"main": "algebra-group.js",
"name": "algebra-group",
"pre-commit": [
"lint",
"test",
"check-deps"
],
"repository": {
"type": "git",
"url": "git://github.com/fibo/algebra-group.git"
},
"scripts": {
"check-deps": "npm outdated",
"lint": "standa",
"postversion": "git push origin v${npm_package_version}; npm publish; git push origin master",
"test": "NODE_PATH=. tape test.js"
},
"version": "0.6.2"
}
},{}],3:[function(require,module,exports){
const group = require('algebra-group')
const staticProps = require('static-props')
const pkg = require('./package.json')
/**
* Prepend package name to error message
*/
function msg (str) {
return pkg.name + ': ' + str
}
const error = {
cannotDivideByZero: msg('Cannot divide by zero'),
doesNotContainIdentity: msg('"identity" must be contained in ring set'),
identityIsNotNeutral: msg('"identity" is not neutral')
}
/**
* Define an algebra ring structure
*
* @param {Array} identities
* @param {*} identities[0] a.k.a zero
* @param {*} identities[1] a.k.a uno
* @param {Object} given operator functions
* @param {Function} given.contains
* @param {Function} given.equality
* @param {Function} given.addition
* @param {Function} given.negation
* @param {Function} given.multiplication
* @param {Function} given.inversion
*
* @returns {Object} ring
*/
function algebraRing (identities, given) {
// A ring is a group, with multiplication.
const ring = group({
identity: identities[0],
contains: given.contains,
equality: given.equality,
compositionLaw: given.addition,
inversion: given.negation
})
// operators
function multiplication () {
return [].slice.call(arguments).reduce(given.multiplication)
}
function inversion (a) {
if (ring.equality(a, ring.zero)) {
throw new TypeError(error.cannotDivideByZero)
}
return given.inversion(a)
}
function division (a) {
const rest = [].slice.call(arguments, 1)
return given.multiplication(a, rest.map(inversion).reduce(given.multiplication))
}
ring.multiplication = multiplication
ring.inversion = inversion
ring.division = division
// Multiplicative identity.
const one = identities[1]
if (ring.notContains(one)) {
throw new TypeError(error.doesNotContainIdentity)
}
// Check that one*one=one.
if (ring.disequality(given.multiplication(one, one), one)) {
throw new TypeError(error.identityIsNotNeutral)
}
if (ring.notContains(identities[1])) {
throw new TypeError(error.doesNotContainIdentity)
}
ring.one = identities[1]
return ring
}
staticProps(algebraRing)({ error: error })
module.exports = algebraRing
},{"./package.json":4,"algebra-group":1,"static-props":13}],4:[function(require,module,exports){
module.exports={
"_args": [
[
"algebra-ring@0.6.4",
"/Users/io/github.com/fibo/algebra"
]
],
"_from": "algebra-ring@0.6.4",
"_id": "algebra-ring@0.6.4",
"_inBundle": false,
"_integrity": "sha512-ydV8zKNp7T/t2zSENFqO5MJe/xNQBYLL5sXkC6Cqa9WEwuKOWAPMHEqhuO0n7iPqugE5AYoodTltLIPZt9QOag==",
"_location": "/algebra-ring",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "algebra-ring@0.6.4",
"name": "algebra-ring",
"escapedName": "algebra-ring",
"rawSpec": "0.6.4",
"saveSpec": null,
"fetchSpec": "0.6.4"
},
"_requiredBy": [
"/cayley-dickson"
],
"_resolved": "https://registry.npmjs.org/algebra-ring/-/algebra-ring-0.6.4.tgz",
"_spec": "0.6.4",
"_where": "/Users/io/github.com/fibo/algebra",
"author": {
"name": "Gianluca Casati",
"url": "http://g14n.info"
},
"bugs": {
"url": "https://github.com/fibo/algebra-ring/issues"
},
"dependencies": {
"algebra-group": "^0.6.2",
"static-props": "^1.1.2"
},
"description": "defines an algebra ring structure",
"devDependencies": {
"dot-editorconfig": "^1.1.1",
"pre-commit": "^1.1.2",
"standa": "^12.0.1",
"tape": "^4.9.2"
},
"homepage": "http://g14n.info/algebra-ring",
"keywords": [
"algebra",
"ring",
"structure"
],
"license": "MIT",
"main": "algebra-ring.js",
"name": "algebra-ring",
"pre-commit": [
"lint",
"test",
"check-deps"
],
"repository": {
"type": "git",
"url": "git+https://github.com/fibo/algebra-ring.git"
},
"scripts": {
"check-deps": "npm outdated",
"lint": "standa --fix",
"postversion": "git push origin v${npm_package_version}; npm publish; git push origin master",
"test": "NODE_PATH=. tape test.js"
},
"version": "0.6.4"
}
},{}],5:[function(require,module,exports){
var ring = require('algebra-ring')
var twoPow = Math.pow.bind(null, 2)
/**
* Turn unary operator on single value to operator on n values.
*/
function arrayfy1 (operator, dim) {
if (dim === 1) {
return operator
} else {
return function (a) {
var b = []
for (var i = 0; i < dim; i++) {
b.push(operator(a[i]))
}
return b
}
}
}
/**
* Turn binary operator on single value to operator on n values.
*/
function arrayfy2 (operator, dim) {
if (dim === 1) {
return operator
} else {
return function (a, b) {
var c = []
for (var i = 0; i < dim; i++) {
c.push(operator(a[i], b[i]))
}
return c
}
}
}
/**
* Iterate Cayley-Disckson construction
*
* @params {Object} given field
* @params {*} given.zero
* @params {*} given.one
* @params {Function} given.equality
* @params {Function} given.contains
* @params {Function} given.addition
* @params {Function} given.negation
* @params {Function} given.multiplication
* @params {Function} given.inversion
* @params {Number} iterations
*
* @returns {Object} algebra
*/
function iterateCayleyDickson (given, iterations) {
var field = ring([given.zero, given.one], given)
if (iterations === 0) {
return field
}
var fieldZero = field.zero
var fieldOne = field.one
var fieldAddition = field.addition
var fieldMultiplication = field.multiplication
var fieldNegation = field.negation
var fieldDisequality = field.disequality
var fieldNotContains = field.notContains
// identities
var one = []
var zero = []
var dim = twoPow(iterations)
one.push(fieldOne)
zero.push(fieldZero)
for (var i = 1; i < dim; i++) {
one.push(fieldZero)
zero.push(fieldZero)
}
// operators
function equality (a, b) {
for (var i = 0; i < dim; i++) {
if (fieldDisequality(a[i], b[i])) {
return false
}
}
return true
}
function contains (a) {
for (var i = 0; i < dim; i++) {
if (fieldNotContains(a[i])) {
return false
}
}
return true
}
function buildConjugation (fieldNegation, iterations) {
if (iterations === 0) {
return function (a) { return a }
}
var dim = twoPow(iterations)
// b -> p looks like complex conjugation simmetry (:
function conjugation (b) {
var p = [b[0]]
for (var i = 1; i < dim; i++) {
p.push(fieldNegation(b[i]))
}
return p
}
return conjugation
}
var conjugation = buildConjugation(fieldNegation, iterations)
function buildMultiplication (fieldAddition, fieldNegation, fieldMultiplication, iterations) {
if (iterations === 0) {
return function (a, b) {
return fieldMultiplication(a[0], b[0])
}
}
var dim = twoPow(iterations)
var halfDim = twoPow(iterations - 1)
var add = arrayfy2(fieldAddition, halfDim)
var conj = buildConjugation(fieldNegation, iterations - 1)
var mul = buildMultiplication(fieldAddition, fieldNegation, fieldMultiplication, iterations - 1)
var neg = arrayfy1(fieldNegation, halfDim)
function multiplication (a, b) {
// a = (p, q)
// b = (r, s)
var p = []
var q = []
var r = []
var s = []
for (var i1 = 0; i1 < halfDim; i1++) {
p.push(a[i1])
r.push(b[i1])
}
for (var i2 = halfDim; i2 < dim; i2++) {
q.push(a[i2])
s.push(b[i2])
}
// var denote conj(x) as x`
//
// Multiplication law is given by
//
// (p, q)(r, s) = (pr - s`q, sp + qr`)
var t = add(mul(p, r), neg(mul(conj(s), q)))
var u = add(mul(s, p), mul(q, conj(r)))
if (halfDim === 1) {
return [t, u]
} else {
var c = []
for (var i3 = 0; i3 < halfDim; i3++) {
c.push(t[i3])
}
for (var i4 = 0; i4 < halfDim; i4++) {
c.push(u[i4])
}
return c
}
}
return multiplication
}
var multiplication = buildMultiplication(fieldAddition, fieldNegation, fieldMultiplication, iterations)
function norm (a) {
var n = fieldZero
var squares = multiplication(a, conjugation(a))
for (var i = 0; i < dim; i++) {
n = fieldAddition(n, squares[i])
}
return n
}
function inversion (a) {
var n = norm(a)
var b = conjugation(a)
for (var i = 0; i < dim; i++) {
b[i] = field.division(b[i], n)
}
return b
}
var addition = arrayfy2(fieldAddition, dim)
var negation = arrayfy1(fieldNegation, dim)
// Cayley-Dickson construction take a field as input but the result can be often a ring,
// this means that it can be *not-commutative*.
// To elevate it to an algebra, we need a bilinear form which is given by the norm.
var algebra = ring([zero, one], {
contains,
equality,
addition,
negation,
multiplication,
inversion
})
algebra.conjugation = conjugation
algebra.norm = norm
return algebra
}
module.exports = iterateCayleyDickson
},{"algebra-ring":3}],6:[function(require,module,exports){
function indicesPermutations (accumulator, currentValue, index, array) {
const arrayLength = array.length
const result = []
if (arrayLength === 1) {
for (let i = 0; i < currentValue; i++) {
result.push([i])
}
} else {
const arrayWithoutLastElement = array.slice(0, arrayLength - 1)
const previousIteration = arrayWithoutLastElement.reduce(indicesPermutations, [])
for (let l = 0; l < previousIteration.length; l++) {
for (let k = 0; k < currentValue; k++) {
result.push(previousIteration[l].concat(k))
}
}
}
return result
}
module.exports = exports.default = indicesPermutations
},{}],7:[function(require,module,exports){
var no = require('not-defined')
/**
* Convert a pair of indices to a 1-dimensional index
*
* @function
* @param {Number} i index row
* @param {Number} j index column
* @param {Number} numCols
*
* @returns {Number} index
*/
function matrixToArrayIndex (i, j, numCols) {
return j + i * numCols
}
/**
* Compute the sub-matrix formed by deleting the i-th row and j-th column
*
* @function
*
* @param {Array} data set
* @param {Number} numRows
* @param {Number} numCols
* @param {Number} row index deleted
* @param {Number} col index deleted
*
* @returns {Array} sub data-set
*/
function subMatrix (data, numRows, numCols, row, col) {
var sub = []
for (var i = 0; i < numRows; i++) {
for (var j = 0; j < numCols; j++) {
if ((i !== row) && (j !== col)) {
sub.push(data[matrixToArrayIndex(i, j, numCols)])
}
}
}
return sub
}
/**
* Computes the determinant of a matrix using Laplace's formula
*
* See https://en.wikipedia.org/wiki/Laplace_expansion
*
* @function
*
* @param {Array} data, lenght must be a square.
* @param {Object} [scalar]
* @param {Function} [scalar.addition = (a, b) -> a + b ]
* @param {Function} [scalar.multiplication = (a, b) -> a * b ]
* @param {Function} [scalar.negation = (a) -> -a ]
* @param {Number} [order], defaults to Math.sqrt(data.length)
*
* @returns {*} det
*/
function determinant (data, scalar, order) {
// Recursion will stop here:
// the determinant of a 1x1 matrix is its only element.
if (data.length === 1) return data[0]
if (no(order)) order = Math.sqrt(data.length)
if (order % 1 !== 0) {
throw new TypeError('data.lenght must be a square')
}
// Default to common real number field.
if (no(scalar)) {
scalar = {
addition: function (a, b) { return a + b },
multiplication: function (a, b) { return a * b },
negation: function (a) { return -a }
}
}
var det
// TODO choose best row or column to start from, i.e. the one with more zeros
// by now we start from first row, and walk by column
// needs scalar.isZero
//
// is scalar.isZero is a function will be used, but should remain optional
var startingRow = 0
for (var col = 0; col < order; col++) {
var subData = subMatrix(data, order, order, startingRow, col)
// +-- Recursion here.
// ↓
var cofactor = determinant(subData, scalar, order - 1)
if ((startingRow + col) % 2 === 1) {
cofactor = scalar.negation(cofactor)
}
var index = matrixToArrayIndex(startingRow, col, order)
if (no(det)) {
det = scalar.multiplication(data[index], cofactor) // first iteration
} else {
det = scalar.addition(det, scalar.multiplication(data[index], cofactor))
}
}
return det
}
module.exports = determinant
},{"not-defined":12}],8:[function(require,module,exports){
var no = require('not-defined')
var staticProps = require('static-props')
var pkg = require('./package.json')
/**
* Prepend package name to error message
*/
function msg (str) {
return pkg.name + ': ' + str
}
var error = {}
staticProps(error)({
leftMatrixNotCompatible: msg('Cannot multiply matrix at left side'),
rightMatrixNotCompatible: msg('Cannot multiply matrix at right side')
})
var matrixToArrayIndex = (i, j, numCols) => (j + i * numCols)
/**
* Multiply two matrices, row by column.
*
* @param {Number} customOperator
* @param {Function} [customOperator.addition]
* @param {Function} [customOperator.multiplication]
*
* @returns {Function} operator
*/
function matrixMultiplication (customOperator) {
// operators
if (no(customOperator)) customOperator = {}
var add = customOperator.addition
var mul = customOperator.multiplication
// Default to operators over Reals.
if (no(add)) add = (a, b) => (a + b)
if (no(mul)) mul = (a, b) => (a * b)
/**
* @param {Number} middle
*
* @returns {Function} mul
*/
return function (middle) {
/**
* @param {Array} leftMatrix
* @param {Array} rightMatrix
*
* @returns {Array} matrix
*/
return function (leftMatrix, rightMatrix) {
// Compatibilty check.
var cols = rightMatrix.length / middle // right num cols
var rows = leftMatrix.length / middle // left num rows
var colsIsNotInteger = Math.floor(cols) !== cols
var rowsIsNotInteger = Math.floor(rows) !== rows
if (colsIsNotInteger) throw new TypeError(error.rightMatrixNotCompatible)
if (rowsIsNotInteger) throw new TypeError(error.leftMatrixNotCompatible)
// Compute result data.
var data = []
for (var i = 0; i < rows; i++) {
for (var j = 0; j < cols; j++) {
var leftIndex = matrixToArrayIndex(i, 0, middle)
var rightIndex = matrixToArrayIndex(0, j, cols)
var rightElement = rightMatrix[rightIndex]
var leftElement = leftMatrix[leftIndex]
var element = mul(leftElement, rightElement)
for (var k = 1; k < middle; k++) {
leftIndex = matrixToArrayIndex(i, k, middle)
rightIndex = matrixToArrayIndex(k, j, cols)
rightElement = rightMatrix[rightIndex]
leftElement = leftMatrix[leftIndex]
element = add(element, mul(rightElement, leftElement))
}
data.push(element)
}
}
return data
}
}
}
staticProps(matrixMultiplication)({ error })
module.exports = matrixMultiplication
},{"./package.json":9,"not-defined":12,"static-props":13}],9:[function(require,module,exports){
module.exports={
"_args": [
[
"matrix-multiplication@0.5.2",
"/Users/io/github.com/fibo/algebra"
]
],
"_from": "matrix-multiplication@0.5.2",
"_id": "matrix-multiplication@0.5.2",
"_inBundle": false,
"_integrity": "sha512-rr3Adfxn9cktAn8zYAkYiDbFZFkFflwjm9oSm5drBIQJPjFoqUlT9nq7aMwXpr+Nr4uurQKgxy+9pfk5X2YmYA==",
"_location": "/matrix-multiplication",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "matrix-multiplication@0.5.2",
"name": "matrix-multiplication",
"escapedName": "matrix-multiplication",
"rawSpec": "0.5.2",
"saveSpec": null,
"fetchSpec": "0.5.2"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/matrix-multiplication/-/matrix-multiplication-0.5.2.tgz",
"_spec": "0.5.2",
"_where": "/Users/io/github.com/fibo/algebra",
"author": {
"name": "Gianluca Casati",
"url": "http://g14n.info"
},
"bugs": {
"url": "https://github.com/fibo/matrix-multiplication/issues"
},
"dependencies": {
"not-defined": "^2.0.1",
"static-props": "^1.1.1"
},
"description": "implements row by column multiplication",
"devDependencies": {
"pre-commit": "^1.2.2",
"standa": "^1.0.2",
"tape": "^4.8.0"
},
"homepage": "http://g14n.info/matrix-multiplication",
"keywords": [
"algebra"
],
"license": "MIT",
"main": "matrix-multiplication.js",
"name": "matrix-multiplication",
"pre-commit": [
"lint",
"test",
"check-deps"
],
"repository": {
"type": "git",
"url": "git://github.com/fibo/matrix-multiplication.git"
},
"scripts": {
"check-deps": "npm outdated",
"lint": "standa",
"postversion": "git push origin v${npm_package_version}; npm publish; git push origin master",
"test": "NODE_PATH=. tape test.js"
},
"version": "0.5.2"
}
},{}],10:[function(require,module,exports){
var staticProps = require('static-props')
var pkg = require('./package.json')
/**
* Prepend package name to error message
*/
function msg (str) {
return pkg.name + ': ' + str
}
var error = {}
staticProps(error)({
outOfBoundIndex: msg('Index exceeds its bound')
})
/**
* Maps multidimensional array indices to monodimensional array index
*
* Given
*
* dimensions d_1, d_2, d_3 .. d_n
* and
* indices i_1, i_2, i_3 .. i_n
*
* index is computed by formula
* index = i_n + i_(n-1) * d_n + i_(n-2) * d_n * d_(n-1) + ... + i_2 * d_n * d_(n-1) * ... * d_3 + i_1 * d_n * ... * d_2
*
* @param {Array} dimensions
* @param {Array} indices
* @returns {Number} index
*/
function multiDimArrayIndex (dimensions, indices) {
// Check that indices fit inside dimensions shape.
for (var i = 0; i < dimensions.length; i++) {
if (indices[i] > dimensions[i]) {
throw new TypeError(error.outOfBoundIndex)
}
}
var order = dimensions.length
// Handle order 1
if (order === 1) return indices[0]
//* index = i_n + i_(n-1) * d_n + i_(n-2) * d_n * d_(n-1) + ... + i_2 * d_n * d_(n-1) * ... * d_3 + i_1 * d_n * ... * d_2
var n = order - 1
var factor = dimensions[n] // d_n
var index = indices[n] + factor * indices[n - 1] // i_n + i_(n-1) * d_n
for (var j = 2; j < order; j++) {
factor *= dimensions[n - j]
index += factor * indices[n - j]
}
return index
}
staticProps(multiDimArrayIndex)({ error: error })
module.exports = multiDimArrayIndex
},{"./package.json":11,"static-props":13}],11:[function(require,module,exports){
module.exports={
"_args": [
[
"multidim-array-index@0.6.0",
"/Users/io/github.com/fibo/algebra"
]
],
"_from": "multidim-array-index@0.6.0",
"_id": "multidim-array-index@0.6.0",
"_inBundle": false,
"_integrity": "sha512-ojHXo7TNXU8i/MxkbC6BqLPR0z1Elr77PuX0xCLoQUSdo/53UjlRBcrDiaOyoLscQp1j84+qQTG1WwHPl6Vz/g==",
"_location": "/multidim-array-index",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "multidim-array-index@0.6.0",
"name": "multidim-array-index",
"escapedName": "multidim-array-index",
"rawSpec": "0.6.0",
"saveSpec": null,
"fetchSpec": "0.6.0"
},
"_requiredBy": [
"/",
"/tensor-contraction"
],
"_resolved": "https://registry.npmjs.org/multidim-array-index/-/multidim-array-index-0.6.0.tgz",
"_spec": "0.6.0",
"_where": "/Users/io/github.com/fibo/algebra",
"author": {
"name": "Gianluca Casati",
"url": "http://g14n.info"
},
"bugs": {
"url": "https://github.com/fibo/multidim-array-index/issues"
},
"dependencies": {
"static-props": "^1.0.0"
},
"description": "maps multidimensional array indices to monodimensional array index",
"devDependencies": {
"dot-editorconfig": "^1.1.0",
"pre-commit": "^1.2.2",
"standa": "^2.0.1",
"tape": "^4.9.0"
},
"homepage": "http://g14n.info/multidim-array-index",
"keywords": [
"array",
"multidim",
"index"
],
"license": "MIT",
"main": "multidim-array-index.js",
"name": "multidim-array-index",
"pre-commit": [
"check-deps",
"lint",
"test"
],
"repository": {
"type": "git",
"url": "git://github.com/fibo/multidim-array-index.git"
},
"scripts": {
"check-deps": "npm outdated",
"lint": "standa",
"postversion": "git push origin v${npm_package_version}; npm publish; git push origin master",
"test": "NODE_PATH=. tape test.js"
},
"version": "0.6.0"
}
},{}],12:[function(require,module,exports){
module.exports=function(x){return x==null||(typeof x == 'number'&&isNaN(x))||(x.length<1&&typeof x!='function')||(typeof x=='object'&&Object.keys(x).length<1)}
},{}],13:[function(require,module,exports){
/**
* @param {Object} obj
* @returns {Function}
*/
function staticProps (obj) {
/**
* @param {Object} props
* @param {Boolean} [enumerable]
*/
return function (props, enumerable) {
var staticProps = {}
for (var propName in props) {
var staticProp = {
configurable: false,
enumerable: enumerable
}
var prop = props[propName]
if (typeof prop === 'function') {
staticProp.get = prop
} else {
staticProp.value = prop
staticProp.writable = false
}
staticProps[propName] = staticProp
}
Object.defineProperties(obj, staticProps)
}
}
module.exports = exports.default = staticProps
},{}],14:[function(require,module,exports){
// In browserify context, fall back to a no op.
module.exports = function (cb) { cb() }
},{}],15:[function(require,module,exports){
var indicesPermutations = require('indices-permutations')
var multiDimArrayIndex = require('multidim-array-index')
/**
* Computes tensor contraction
*
* @params {Function} addition
* @params {Array} indicesPair
* @params {Array} tensorDim
* @params {Array} tensorData
* @returns {Array} contractedTensorData
*/
function tensorContraction (addition, indicesPair, tensorDim, tensorData) {
// Sort indices pair, otherwise algorithm gets unnecessary complicated.
indicesPair.sort()
var p0 = indicesPair[0]
var p1 = indicesPair[1]
var dim0 = tensorDim[p0]
var dim1 = tensorDim[p1]
if (dim0 !== dim1) {
throw new TypeError('Contraction indices does not have the same dimension: ' +
p0 + '-th index = ' + dim0 + ' but ' + p1 + '-th index = ' + dim1 + '.')
}
function varyingTensorDim (result, element, index) {
if ((index !== p0) && (index !== p1)) {
result.push(element)
}
return result
}
function copyArray (result, element) {
result.push(element)
return result
}
function sumOverVarying (tensorData) {
return function (result, varyingCombination) {
var firstCombination = varyingCombination.reduce(copyArray, [])
firstCombination.splice(p0, 0, 0)
firstCombination.splice(p1, 0, 0)
var firstIndex = multiDimArrayIndex(tensorDim, firstCombination)
var element = tensorData[firstIndex]
for (var i = 1; i < dim0; i++) {
var combination = varyingCombination.reduce(copyArray, [])
combination.splice(p0, 0, i)
combination.splice(p1, 0, i)
var index = multiDimArrayIndex(tensorDim, combination)
element = addition(element, tensorData[index])
}
result.push(element)
return result
}
}
// If given tensor has order 2, the contracted tensor will be a scalar
// so it makes sense to return an element, not an array.
// Furthermore, varyingTensorDim will be an empty array so generic algorithm
// will not even be triggered. Then it will be simply computed the trace.
if (tensorDim.length === 2) {
var trace = tensorData[0]
for (var i = 1; i < dim0; i++) {
var combination = [i, i]
var index = multiDimArrayIndex(tensorDim, combination)
trace = addition(trace, tensorData[index])
}
return trace
} else {
return tensorDim
.reduce(varyingTensorDim, [])
.reduce(indicesPermutations, [])
.reduce(sumOverVarying(tensorData), [])
}
}
module.exports = tensorContraction
},{"indices-permutations":6,"multidim-array-index":10}],16:[function(require,module,exports){
const Boole = {
zero: false,
one: true,
contains: (a) => (typeof a === 'boolean'),
addition: (a, b) => (a || b),
equality: (a, b) => (a === b),
negation: (a) => (a),
multiplication: (a, b) => (a && b),
inversion: (a) => (a)
}
module.exports = Boole
},{}],17:[function(require,module,exports){
const CayleyDickson = require('cayley-dickson')
const no = require('not-defined')
const Ring = require('./Ring.js')
/**
* A composition algebra is one of ℝ, ℂ, ℍ, O:
* Real, Complex, Quaternion, Octonion.
*
* https://en.wikipedia.org/wiki/Composition_algebra
*
* @param {Object} ringDefinition
* @param {Number} [num] of CayleyDickson construction iterations. Can be 1, 2, 4 or 8.
*
* @returns {Object} Scalar
*/
function CompositionAlgebra (ringDefinition, num) {
if (no(num)) num = 1
const logBase2 = [1, 2, 4, 8].indexOf(num)
if (logBase2 === -1) {
throw new TypeError('Argument n must be 1, 2, 4 or 8')
}
return Ring(CayleyDickson(ringDefinition, logBase2))
}
module.exports = CompositionAlgebra
},{"./Ring.js":19,"cayley-dickson":5,"not-defined":12}],18:[function(require,module,exports){
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
},{"./itemsPool.js":22,"./toData.js":24,"laplace-determinant":7,"matrix-multiplication":8,"multidim-array-index":10,"static-props":13,"tensor-contraction":15}],19:[function(require,module,exports){
const staticProps = require('static-props')
const toData = require('./toData.js')
/**
* @param {Object} ring definition
*
* @returns {Function} Scalar
*/
function Ring ({
addition,
conjugation,
contains,
division,
equality,
inversion,
multiplication,
negation,
notContains,
one,
subtraction,
zero
}) {
function scalarAddition (scalar1, scalar2) {
const scalarData1 = toData(scalar1)
const scalarData2 = toData(scalar2)
return addition(scalarData1, scalarData2)
}
function scalarConjugation (scalar) {
const scalarData = toData(scalar)
return conjugation(scalarData)
}
function scalarContains (scalar) {
const scalarData = toData(scalar)
return contains(scalarData)
}
function scalarEquality (scalar1, scalar2) {
const scalarData1 = toData(scalar1)
const scalarData2 = toData(scalar2)
return equality(scalarData1, scalarData2)
}
function scalarDisequality (scalar1, scalar2) {
return !scalarEquality(scalar1, scalar2)
}
function scalarDivision (scalar1, scalar2) {
const scalarData1 = toData(scalar1)
const scalarData2 = toData(scalar2)
if (Scalar.equality(zero, scalarData2)) {
throw new Error('Cannot divide by zero')
}
return division(scalarData1, scalarData2)
}
function scalarInversion (scalar) {
const scalarData = toData(scalar)
if (Scalar.equality(zero, scalarData)) {
throw new Error('Cannot invert zero')
}
return inversion(scalarData)
}
function scalarMultiplication (scalar1, scalar2) {
const scalarData1 = toData(scalar1)
const scalarData2 = toData(scalar2)
return multiplication(scalarData1, scalarData2)
}
function scalarNegation (scalar) {
const scalarData = toData(scalar)
return negation(scalarData)
}
function scalarSubtraction (scalar1, scalar2) {
const scalarData1 = toData(scalar1)
const scalarData2 = toData(scalar2)
return subtraction(scalarData1, scalarData2)
}
/**
* Scalar element.
*/
class Scalar {
constructor (data) {
// validate data
if (notContains(data)) {
throw new TypeError('Invalid data = ' + data)
}
const enumerable = true
staticProps(this)({ data }, enumerable)
// Method aliases.
staticProps(this)({
add: () => this.addition,
conj: () => this.conjugation,
div: () => this.division,
eq: () => this.equality,
equal: () => this.equality,
equals: () => this.equality,
inv: () => this.inversion,
mul: () => this.multiplication,
ne: () => this.disequality,
neg: () => this.negation,
notEqual: () => this.disequality,
notEquals: () => this.disequality,
sub: () => this.subtraction
})
}
addition (scalar) {
const data = scalarAddition(this, scalar)
return new Scalar(data)
}
belongsTo ({ contains }) {
return contains(this.data)
}
conjugation () {
const data = scalarConjugation(this)
return new Scalar(data)
}
disequality (scalar) {
return scalarDisequality(this, scalar)
}
division (scalar) {
const data = scalarDivision(this, scalar)
return new Scalar(data)
}
equality (scalar) {
return scalarEquality(this, scalar)
}
inversion () {
const data = scalarInversion(this)
return new Scalar(data)
}
multiplication (scalar) {
const data = scalarMultiplication(this, scalar)
return new Scalar(data)
}
negation () {
const data = scalarNegation(this)
return new Scalar(data)
}
subtraction (scalar) {
const data = scalarSubtraction(this, scalar)
return new Scalar(data)
}
}
staticProps(Scalar)({
zero,
one
})
// Scalar static operators.
staticProps(Scalar)({
addition: () => scalarAddition,
conjugation: () => scalarConjugation,
contains: () => scalarContains,
disequality: () => scalarDisequality,
division: () => scalarDivision,
equality: () => scalarEquality,
inversion: () => scalarInversion,
multiplication: () => scalarMultiplication,
negation: () => scalarNegation,
subtraction: () => scalarSubtraction
})
staticProps(Scalar)({
add: () => Scalar.addition,
conj: () => Scalar.conjugation,
div: () => Scalar.division,
eq: () => Scalar.equality,
equal: () => Scalar.equality,
equals: () => Scalar.equality,
inv: () => Scalar.inversion,
mul: () => Scalar.multiplication,
ne: () => Scalar.disequality,
notEqual: () => Scalar.disequality,
notEquals: () => Scalar.disequality,
neg: () => Scalar.negation,
sub: () => Scalar.subtraction
})
return Scalar
}
module.exports = Ring
},{"./toData.js":24,"static-props":13}],20:[function(require,module,exports){
const CompositionAlgebra = require('./CompositionAlgebra.js')
function Scalar (ringDefinition) {
return CompositionAlgebra(ringDefinition)
}
module.exports = Scalar
},{"./CompositionAlgebra.js":17}],21:[function(require,module,exports){
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 (vec