Intial Commit

This commit is contained in:
valki
2020-10-17 18:42:50 +02:00
commit 664c6d8ca3
5892 changed files with 759183 additions and 0 deletions

View File

@@ -0,0 +1,387 @@
'use strict';
var util = require('../../../utils/index');
var object = util.object;
function factory (type, config, load, typed) {
var matrix = load(require('../../../type/matrix/function/matrix'));
var abs = load(require('../../arithmetic/abs'));
var addScalar = load(require('../../arithmetic/addScalar'));
var divideScalar = load(require('../../arithmetic/divideScalar'));
var multiplyScalar = load(require('../../arithmetic/multiplyScalar'));
var subtract = load(require('../../arithmetic/subtract'));
var larger = load(require('../../relational/larger'));
var equalScalar = load(require('../../relational/equalScalar'));
var unaryMinus = load(require('../../arithmetic/unaryMinus'));
var SparseMatrix = type.SparseMatrix;
var DenseMatrix = type.DenseMatrix;
var Spa = type.Spa;
/**
* Calculate the Matrix LU decomposition with partial pivoting. Matrix `A` is decomposed in two matrices (`L`, `U`) and a
* row permutation vector `p` where `A[p,:] = L * U`
*
* Syntax:
*
* math.lup(A);
*
* Example:
*
* var m = [[2, 1], [1, 4]];
* var r = math.lup();
* // r = {
* // L: [[1, 0], [0.5, 1]],
* // U: [[2, 1], [0, 3.5]],
* // P: [0, 1]
* // }
*
* See also:
*
* slu, lsolve, lusolve, usolve
*
* @param {Matrix | Array} A A two dimensional matrix or array for which to get the LUP decomposition.
*
* @return {Array<Matrix>} The lower triangular matrix, the upper triangular matrix and the permutation matrix.
*/
var lup = typed('lup', {
'DenseMatrix': function (m) {
return _denseLUP(m);
},
'SparseMatrix': function (m) {
return _sparseLUP(m);
},
'Array': function (a) {
// create dense matrix from array
var m = matrix(a);
// lup, use matrix implementation
var r = _denseLUP(m);
// result
return {
L: r.L.valueOf(),
U: r.U.valueOf(),
p: r.p
};
}
});
var _denseLUP = function (m) {
// rows & columns
var rows = m._size[0];
var columns = m._size[1];
// minimum rows and columns
var n = Math.min(rows, columns);
// matrix array, clone original data
var data = object.clone(m._data);
// l matrix arrays
var ldata = [];
var lsize = [rows, n];
// u matrix arrays
var udata = [];
var usize = [n, columns];
// vars
var i, j, k;
// permutation vector
var p = [];
for (i = 0; i < rows; i++)
p[i] = i;
// loop columns
for (j = 0; j < columns; j++) {
// skip first column in upper triangular matrix
if (j > 0) {
// loop rows
for (i = 0; i < rows; i++) {
// min i,j
var min = Math.min(i, j);
// v[i, j]
var s = 0;
// loop up to min
for (k = 0; k < min; k++) {
// s = l[i, k] - data[k, j]
s = addScalar(s, multiplyScalar(data[i][k], data[k][j]));
}
data[i][j] = subtract(data[i][j], s);
}
}
// row with larger value in cvector, row >= j
var pi = j;
var pabsv = 0;
var vjj = 0;
// loop rows
for (i = j; i < rows; i++) {
// data @ i, j
var v = data[i][j];
// absolute value
var absv = abs(v);
// value is greater than pivote value
if (larger(absv, pabsv)) {
// store row
pi = i;
// update max value
pabsv = absv;
// value @ [j, j]
vjj = v;
}
}
// swap rows (j <-> pi)
if (j !== pi) {
// swap values j <-> pi in p
p[j] = [p[pi], p[pi] = p[j]][0];
// swap j <-> pi in data
DenseMatrix._swapRows(j, pi, data);
}
// check column is in lower triangular matrix
if (j < rows) {
// loop rows (lower triangular matrix)
for (i = j + 1; i < rows; i++) {
// value @ i, j
var vij = data[i][j];
if (!equalScalar(vij, 0)) {
// update data
data[i][j] = divideScalar(data[i][j], vjj);
}
}
}
}
// loop columns
for (j = 0; j < columns; j++) {
// loop rows
for (i = 0; i < rows; i++) {
// initialize row in arrays
if (j === 0) {
// check row exists in upper triangular matrix
if (i < columns) {
// U
udata[i] = [];
}
// L
ldata[i] = [];
}
// check we are in the upper triangular matrix
if (i < j) {
// check row exists in upper triangular matrix
if (i < columns) {
// U
udata[i][j] = data[i][j];
}
// check column exists in lower triangular matrix
if (j < rows) {
// L
ldata[i][j] = 0;
}
continue;
}
// diagonal value
if (i === j) {
// check row exists in upper triangular matrix
if (i < columns) {
// U
udata[i][j] = data[i][j];
}
// check column exists in lower triangular matrix
if (j < rows) {
// L
ldata[i][j] = 1;
}
continue;
}
// check row exists in upper triangular matrix
if (i < columns) {
// U
udata[i][j] = 0;
}
// check column exists in lower triangular matrix
if (j < rows) {
// L
ldata[i][j] = data[i][j];
}
}
}
// l matrix
var l = new DenseMatrix({
data: ldata,
size: lsize
});
// u matrix
var u = new DenseMatrix({
data: udata,
size: usize
});
// p vector
var pv = [];
for (i = 0, n = p.length; i < n; i++)
pv[p[i]] = i;
// return matrices
return {
L: l,
U: u,
p: pv,
toString: function () {
return 'L: ' + this.L.toString() + '\nU: ' + this.U.toString() + '\nP: ' + this.p;
}
};
};
var _sparseLUP = function (m) {
// rows & columns
var rows = m._size[0];
var columns = m._size[1];
// minimum rows and columns
var n = Math.min(rows, columns);
// matrix arrays (will not be modified, thanks to permutation vector)
var values = m._values;
var index = m._index;
var ptr = m._ptr;
// l matrix arrays
var lvalues = [];
var lindex = [];
var lptr = [];
var lsize = [rows, n];
// u matrix arrays
var uvalues = [];
var uindex = [];
var uptr = [];
var usize = [n, columns];
// vars
var i, j, k;
// permutation vectors, (current index -> original index) and (original index -> current index)
var pv_co = [];
var pv_oc = [];
for (i = 0; i < rows; i++) {
pv_co[i] = i;
pv_oc[i] = i;
}
// swap indices in permutation vectors (condition x < y)!
var swapIndeces = function (x, y) {
// find pv indeces getting data from x and y
var kx = pv_oc[x];
var ky = pv_oc[y];
// update permutation vector current -> original
pv_co[kx] = y;
pv_co[ky] = x;
// update permutation vector original -> current
pv_oc[x] = ky;
pv_oc[y] = kx;
};
// loop columns
for (j = 0; j < columns; j++) {
// sparse accumulator
var spa = new Spa();
// check lower triangular matrix has a value @ column j
if (j < rows) {
// update ptr
lptr.push(lvalues.length);
// first value in j column for lower triangular matrix
lvalues.push(1);
lindex.push(j);
}
// update ptr
uptr.push(uvalues.length);
// k0 <= k < k1 where k0 = _ptr[j] && k1 = _ptr[j+1]
var k0 = ptr[j];
var k1 = ptr[j + 1];
// copy column j into sparse accumulator
for (k = k0; k < k1; k++) {
// row
i = index[k];
// copy column values into sparse accumulator (use permutation vector)
spa.set(pv_co[i], values[k]);
}
// skip first column in upper triangular matrix
if (j > 0) {
// loop rows in column j (above diagonal)
spa.forEach(0, j - 1, function (k, vkj) {
// loop rows in column k (L)
SparseMatrix._forEachRow(k, lvalues, lindex, lptr, function (i, vik) {
// check row is below k
if (i > k) {
// update spa value
spa.accumulate(i, unaryMinus(multiplyScalar(vik, vkj)));
}
});
});
}
// row with larger value in spa, row >= j
var pi = j;
var vjj = spa.get(j);
var pabsv = abs(vjj);
// loop values in spa (order by row, below diagonal)
spa.forEach(j + 1, rows - 1, function (x, v) {
// absolute value
var absv = abs(v);
// value is greater than pivote value
if (larger(absv, pabsv)) {
// store row
pi = x;
// update max value
pabsv = absv;
// value @ [j, j]
vjj = v;
}
});
// swap rows (j <-> pi)
if (j !== pi) {
// swap values j <-> pi in L
SparseMatrix._swapRows(j, pi, lsize[1], lvalues, lindex, lptr);
// swap values j <-> pi in U
SparseMatrix._swapRows(j, pi, usize[1], uvalues, uindex, uptr);
// swap values in spa
spa.swap(j, pi);
// update permutation vector (swap values @ j, pi)
swapIndeces(j, pi);
}
// loop values in spa (order by row)
spa.forEach(0, rows - 1, function (x, v) {
// check we are above diagonal
if (x <= j) {
// update upper triangular matrix
uvalues.push(v);
uindex.push(x);
}
else {
// update value
v = divideScalar(v, vjj);
// check value is non zero
if (!equalScalar(v, 0)) {
// update lower triangular matrix
lvalues.push(v);
lindex.push(x);
}
}
});
}
// update ptrs
uptr.push(uvalues.length);
lptr.push(lvalues.length);
// return matrices
return {
L: new SparseMatrix({
values: lvalues,
index: lindex,
ptr: lptr,
size: lsize
}),
U: new SparseMatrix({
values: uvalues,
index: uindex,
ptr: uptr,
size: usize
}),
p: pv_co,
toString: function () {
return 'L: ' + this.L.toString() + '\nU: ' + this.U.toString() + '\nP: ' + this.p;
}
};
};
return lup;
}
exports.name = 'lup';
exports.factory = factory;

View File

@@ -0,0 +1,72 @@
'use strict';
var util = require('../../../utils/index');
var number = util.number,
isInteger = number.isInteger;
function factory (type, config, load, typed) {
var cs_sqr = load(require('../../algebra/sparse/cs_sqr'));
var cs_lu = load(require('../../algebra/sparse/cs_lu'));
/**
* Calculate the Sparse Matrix LU decomposition with full pivoting. Sparse Matrix `A` is decomposed in two matrices (`L`, `U`) and two permutation vectors (`pinv`, `q`) where
*
* `P * A * Q = L * U`
*
* Syntax:
*
* math.slu(A, order, threshold);
*
* See also:
*
* lup, lsolve, usolve, lusolve
*
* @param {SparseMatrix} A A two dimensional sparse matrix for which to get the LU decomposition.
* @param {Number} order The Symbolic Ordering and Analysis order:
* 0 - Natural ordering, no permutation vector q is returned
* 1 - Matrix must be square, symbolic ordering and analisis is performed on M = A + A'
* 2 - Symbolic ordering and analisis is performed on M = A' * A. Dense columns from A' are dropped, A recreated from A'.
* This is appropriatefor LU factorization of unsymmetric matrices.
* 3 - Symbolic ordering and analisis is performed on M = A' * A. This is best used for LU factorization is matrix M has no dense rows.
* A dense row is a row with more than 10*sqr(columns) entries.
* @param {Number} threshold Partial pivoting threshold (1 for partial pivoting)
*
* @return {Object} The lower triangular matrix, the upper triangular matrix and the permutation vectors.
*/
var slu = typed('slu', {
'SparseMatrix, number, number': function (a, order, threshold) {
// verify order
if (!isInteger(order) || order < 0 || order > 3)
throw new Error('Symbolic Ordering and Analysis order must be an integer number in the interval [0, 3]');
// verify threshold
if (threshold < 0 || threshold > 1)
throw new Error('Partial pivoting threshold must be a number from 0 to 1');
// perform symbolic ordering and analysis
var s = cs_sqr(order, a, false);
// perform lu decomposition
var f = cs_lu(a, s, threshold);
// return decomposition
return {
L: f.L,
U: f.U,
p: f.pinv,
q: s.q,
toString: function () {
return 'L: ' + this.L.toString() + '\nU: ' + this.U.toString() + '\np: ' + this.p.toString() + (this.q ? '\nq: ' + this.q.toString() : '') + '\n';
}
};
}
});
return slu;
}
exports.name = 'slu';
exports.factory = factory;

View File

@@ -0,0 +1,714 @@
'use strict';
function factory (type, config, load, typed) {
var parse = load(require('../../expression/parse'));
var simplify = load(require('./simplify'));
var ConstantNode = load(require('../../expression/node/ConstantNode'));
var FunctionNode = load(require('../../expression/node/FunctionNode'));
var OperatorNode = load(require('../../expression/node/OperatorNode'));
var ParenthesisNode = load(require('../../expression/node/ParenthesisNode'));
var SymbolNode = load(require('../../expression/node/SymbolNode'));
/**
* Takes the derivative of an expression expressed in parser Nodes.
* The derivative will be taken over the supplied variable in the
* second parameter. If there are multiple variables in the expression,
* it will return a partial derivative.
*
* This uses rules of differentiation which can be found here:
*
* - [Differentiation rules (Wikipedia)](http://en.wikipedia.org/wiki/Differentiation_rules)
*
* Syntax:
*
* derivative(expr, variable)
* derivative(expr, variable, options)
*
* Examples:
*
* math.derivative('x^2', 'x'); // Node {2 * x}
* math.derivative('x^2', 'x', {simplify: false}); // Node {2 * 1 * x ^ (2 - 1)
* math.derivative('sin(2x)', 'x')); // Node {2 * cos(2 * x)}
* math.derivative('2*x', 'x').eval(); // number 2
* math.derivative('x^2', 'x').eval({x: 4}); // number 8
* var f = math.parse('x^2');
* var x = math.parse('x');
* math.derivative(f, x); // Node {2 * x}
*
* See also:
*
* simplify, parse, eval
*
* @param {Node | string} expr The expression to differentiate
* @param {SymbolNode | string} variable The variable over which to differentiate
* @param {{simplify: boolean}} [options]
* There is one option available, `simplify`, which
* is true by default. When false, output will not
* be simplified.
* @return {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} The derivative of `expr`
*/
var derivative = typed('derivative', {
'Node, SymbolNode, Object': function (expr, variable, options) {
var constNodes = {};
constTag(constNodes, expr, variable.name);
var res = _derivative(expr, constNodes);
return options.simplify ? simplify(res) : res;
},
'Node, SymbolNode': function (expr, variable) {
return derivative(expr, variable, {simplify: true})
},
'string, SymbolNode': function (expr, variable) {
return derivative(parse(expr), variable)
},
'string, SymbolNode, Object': function (expr, variable, options) {
return derivative(parse(expr), variable, options)
},
'string, string': function (expr, variable) {
return derivative(parse(expr), parse(variable))
},
'string, string, Object': function (expr, variable, options) {
return derivative(parse(expr), parse(variable), options)
},
'Node, string': function (expr, variable) {
return derivative(expr, parse(variable))
},
'Node, string, Object': function (expr, variable, options) {
return derivative(expr, parse(variable), options)
}
// TODO: replace the 8 signatures above with 4 as soon as typed-function supports optional arguments
/* TODO: implement and test syntax with order of derivatives -> implement as an option {order: number}
'Node, SymbolNode, ConstantNode': function (expr, variable, {order}) {
var res = expr;
for (var i = 0; i < order; i++) {
var constNodes = {};
constTag(constNodes, expr, variable.name);
res = _derivative(res, constNodes);
}
return res;
}
*/
});
derivative._simplify = true
derivative.toTex = function(deriv) {
return _derivTex.apply(null, deriv.args);
}
var _derivTex = typed('_derivTex', {
'Node, SymbolNode': function (expr, x) {
return _derivTex(expr.toString(), x.toString(), 1);
},
'Node, SymbolNode, ConstantNode': function (expr, x, order) {
return _derivTex(expr.toString(), x.name, order.value);
},
'string, string, number': function (expr, x, order) {
var d;
if (order === 1) {
d = "{d\\over d" + x + "}";
}
else {
d = "{d^{" + order + "}\\over d" + x + "^{" + order + "}}";
}
return d + "\\left[" + expr + "\\right]"
}
});
/**
* Does a depth-first search on the expression tree to identify what Nodes
* are constants (e.g. 2 + 2), and stores the ones that are constants in
* constNodes. Classification is done as follows:
*
* 1. ConstantNodes are constants.
* 2. If there exists a SymbolNode, of which we are differentiating over,
* in the subtree it is not constant.
*
* @param {Object} constNodes Holds the nodes that are constant
* @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} node
* @param {string} varName Variable that we are differentiating
* @return {boolean} if node is constant
*/
// TODO: can we rewrite constTag into a pure function?
var constTag = typed('constTag', {
'Object, ConstantNode, string': function (constNodes, node) {
return constNodes[node] = true;
},
'Object, SymbolNode, string': function (constNodes, node, varName) {
// Treat other variables like constants. For reasoning, see:
// https://en.wikipedia.org/wiki/Partial_derivative
if (node.name != varName) {
return constNodes[node] = true;
}
return false;
},
'Object, ParenthesisNode, string': function (constNodes, node, varName) {
return constTag(constNodes, node.content, varName);
},
'Object, FunctionAssignmentNode, string': function (constNodes, node, varName) {
if (node.params.indexOf(varName) == -1) {
return constNodes[node] = true;
}
return constTag(constNodes, node.expr, varName);
},
'Object, FunctionNode | OperatorNode, string': function (constNodes, node, varName) {
if (node.args.length != 0) {
var isConst = constTag(constNodes, node.args[0], varName);
for (var i = 1; i < node.args.length; ++i) {
isConst = constTag(constNodes, node.args[i], varName) && isConst;
}
if (isConst) {
return constNodes[node] = true;
}
}
return false;
}
});
/**
* Applies differentiation rules.
*
* @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} node
* @param {Object} constNodes Holds the nodes that are constant
* @return {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} The derivative of `expr`
*/
var _derivative = typed('_derivative', {
'ConstantNode, Object': function (node) {
return new ConstantNode('0', node.valueType);
},
'SymbolNode, Object': function (node, constNodes) {
if (constNodes[node] !== undefined) {
return new ConstantNode('0', config.number);
}
return new ConstantNode('1', config.number);
},
'ParenthesisNode, Object': function (node, constNodes) {
return new ParenthesisNode(_derivative(node.content, constNodes));
},
'FunctionAssignmentNode, Object': function (node, constNodes) {
if (constNodes[node] !== undefined) {
return new ConstantNode('0', config.number);
}
return _derivative(node.expr, constNodes);
},
'FunctionNode, Object': function (node, constNodes) {
if (node.args.length != 1) {
funcArgsCheck(node);
}
if (constNodes[node] !== undefined) {
return new ConstantNode('0', config.number);
}
var arg1 = node.args[0];
var arg2;
var div = false; // is output a fraction?
var negative = false; // is output negative?
var funcDerivative;
switch (node.name) {
case 'cbrt':
// d/dx(cbrt(x)) = 1 / (3x^(2/3))
div = true;
funcDerivative = new OperatorNode('*', 'multiply', [
new ConstantNode('3', config.number),
new OperatorNode('^', 'pow', [
arg1,
new OperatorNode('/', 'divide', [
new ConstantNode('2', config.number),
new ConstantNode('3', config.number)
])
])
]);
break;
case 'sqrt':
case 'nthRoot':
// d/dx(sqrt(x)) = 1 / (2*sqrt(x))
if (node.args.length == 1) {
div = true;
funcDerivative = new OperatorNode('*', 'multiply', [
new ConstantNode('2', config.number),
new FunctionNode('sqrt', [arg1])
]);
break;
}
// Rearrange from nthRoot(x, a) -> x^(1/a)
arg2 = new OperatorNode('/', 'divide', [
new ConstantNode('1', config.number),
node.args[1]
]);
// Is a variable?
constNodes[arg2] = constNodes[node.args[1]];
return _derivative(new OperatorNode('^', 'pow', [arg1, arg2]), constNodes);
case 'log10':
arg2 = new ConstantNode('10', config.number);
case 'log':
if (!arg2 && node.args.length == 1) {
// d/dx(log(x)) = 1 / x
funcDerivative = arg1.clone();
} else if (arg2 || constNodes[node.args[1]] !== undefined) {
// d/dx(log(x, c)) = 1 / (x*ln(c))
funcDerivative = new OperatorNode('*', 'multiply', [
arg1.clone(),
new FunctionNode('log', [arg2 || node.args[1]])
]);
} else {
// d/dx(log(f(x), g(x))) = d/dx(log(f(x)) / log(g(x)))
return _derivative(new OperatorNode('/', 'divide', [
new FunctionNode('log', [arg1]),
new FunctionNode('log', [node.args[1]])
]), constNodes);
}
div = true;
break;
case 'exp':
// d/dx(e^x) = e^x
funcDerivative = new FunctionNode('exp', [arg1.clone()]);
break;
case 'sin':
// d/dx(sin(x)) = cos(x)
funcDerivative = new FunctionNode('cos', [arg1.clone()]);
break;
case 'cos':
// d/dx(cos(x)) = -sin(x)
funcDerivative = new OperatorNode('-', 'unaryMinus', [
new FunctionNode('sin', [arg1.clone()])
]);
break;
case 'tan':
// d/dx(tan(x)) = sec(x)^2
funcDerivative = new OperatorNode('^', 'pow', [
new FunctionNode('sec', [arg1.clone()]),
new ConstantNode('2', config.number)
]);
break;
case 'sec':
// d/dx(sec(x)) = sec(x)tan(x)
funcDerivative = new OperatorNode('*', 'multiply', [
node,
new FunctionNode('tan', [arg1.clone()])
]);
break;
case 'csc':
// d/dx(csc(x)) = -csc(x)cot(x)
negative = true;
funcDerivative = new OperatorNode('*', 'multiply', [
node,
new FunctionNode('cot', [arg1.clone()])
]);
break;
case 'cot':
// d/dx(cot(x)) = -csc(x)^2
negative = true;
funcDerivative = new OperatorNode('^', 'pow', [
new FunctionNode('csc', [arg1.clone()]),
new ConstantNode('2', config.number)
]);
break;
case 'asin':
// d/dx(asin(x)) = 1 / sqrt(1 - x^2)
div = true;
funcDerivative = new FunctionNode('sqrt', [
new OperatorNode('-', 'subtract', [
new ConstantNode('1', config.number),
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
])
])
]);
break;
case 'acos':
// d/dx(acos(x)) = -1 / sqrt(1 - x^2)
div = true;
negative = true;
funcDerivative = new FunctionNode('sqrt', [
new OperatorNode('-', 'subtract', [
new ConstantNode('1', config.number),
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
])
])
]);
break;
case 'atan':
// d/dx(atan(x)) = 1 / (x^2 + 1)
div = true;
funcDerivative = new OperatorNode('+', 'add', [
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
]),
new ConstantNode('1', config.number)
]);
break;
case 'asec':
// d/dx(asec(x)) = 1 / (|x|*sqrt(x^2 - 1))
div = true;
funcDerivative = new OperatorNode('*', 'multiply', [
new FunctionNode('abs', [arg1.clone()]),
new FunctionNode('sqrt', [
new OperatorNode('-', 'subtract', [
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
]),
new ConstantNode('1', config.number)
])
])
]);
break;
case 'acsc':
// d/dx(acsc(x)) = -1 / (|x|*sqrt(x^2 - 1))
div = true;
negative = true;
funcDerivative = new OperatorNode('*', 'multiply', [
new FunctionNode('abs', [arg1.clone()]),
new FunctionNode('sqrt', [
new OperatorNode('-', 'subtract', [
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
]),
new ConstantNode('1', config.number)
])
])
]);
break;
case 'acot':
// d/dx(acot(x)) = -1 / (x^2 + 1)
div = true;
negative = true;
funcDerivative = new OperatorNode('+', 'add', [
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
]),
new ConstantNode('1', config.number)
]);
break;
case 'sinh':
// d/dx(sinh(x)) = cosh(x)
funcDerivative = new FunctionNode('cosh', [arg1.clone()]);
break;
case 'cosh':
// d/dx(cosh(x)) = sinh(x)
funcDerivative = new FunctionNode('sinh', [arg1.clone()]);
break;
case 'tanh':
// d/dx(tanh(x)) = sech(x)^2
funcDerivative = new OperatorNode('^', 'pow', [
new FunctionNode('sech', [arg1.clone()]),
new ConstantNode('2', config.number)
]);
break;
case 'sech':
// d/dx(sech(x)) = -sech(x)tanh(x)
negative = true;
funcDerivative = new OperatorNode('*', 'multiply', [
node,
new FunctionNode('tanh', [arg1.clone()])
]);
break;
case 'csch':
// d/dx(csch(x)) = -csch(x)coth(x)
negative = true;
funcDerivative = new OperatorNode('*', 'multiply', [
node,
new FunctionNode('coth', [arg1.clone()])
]);
break;
case 'coth':
// d/dx(coth(x)) = -csch(x)^2
negative = true;
funcDerivative = new OperatorNode('^', 'pow', [
new FunctionNode('csch', [arg1.clone()]),
new ConstantNode('2', config.number)
]);
break;
case 'asinh':
// d/dx(asinh(x)) = 1 / sqrt(x^2 + 1)
div = true;
funcDerivative = new FunctionNode('sqrt', [
new OperatorNode('+', 'add', [
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
]),
new ConstantNode('1', config.number)
])
]);
break;
case 'acosh':
// d/dx(acosh(x)) = 1 / sqrt(x^2 - 1); XXX potentially only for x >= 1 (the real spectrum)
div = true;
funcDerivative = new FunctionNode('sqrt', [
new OperatorNode('-', 'subtract', [
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
]),
new ConstantNode('1', config.number),
])
]);
break;
case 'atanh':
// d/dx(atanh(x)) = 1 / (1 - x^2)
div = true;
funcDerivative = new OperatorNode('-', 'subtract', [
new ConstantNode('1', config.number),
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
])
]);
break;
case 'asech':
// d/dx(asech(x)) = -1 / (x*sqrt(1 - x^2))
div = true;
negative = true;
funcDerivative = new OperatorNode('*', 'multiply', [
arg1.clone(),
new FunctionNode('sqrt', [
new OperatorNode('-', 'subtract', [
new ConstantNode('1', config.number),
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
])
])
])
]);
break;
case 'acsch':
// d/dx(acsch(x)) = -1 / (|x|*sqrt(x^2 + 1))
div = true;
negative = true;
funcDerivative = new OperatorNode('*', 'multiply', [
new FunctionNode('abs', [arg1.clone()]),
new FunctionNode('sqrt', [
new OperatorNode('+', 'add', [
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
]),
new ConstantNode('1', config.number)
])
])
]);
break;
case 'acoth':
// d/dx(acoth(x)) = -1 / (1 - x^2)
div = true;
negative = true;
funcDerivative = new OperatorNode('-', 'subtract', [
new ConstantNode('1', config.number),
new OperatorNode('^', 'pow', [
arg1.clone(),
new ConstantNode('2', config.number)
])
]);
break;
case 'gamma': // Needs digamma function, d/dx(gamma(x)) = gamma(x)digamma(x)
default: throw new Error('Function "' + node.name + '" not supported by derivative');
}
var op, func;
if (div) {
op = '/';
func = 'divide';
} else {
op = '*';
func = 'multiply';
}
/* Apply chain rule to all functions:
F(x) = f(g(x))
F'(x) = g'(x)*f'(g(x)) */
var chainDerivative = _derivative(arg1, constNodes);
if (negative) {
chainDerivative = new OperatorNode('-', 'unaryMinus', [chainDerivative]);
}
return new OperatorNode(op, func, [chainDerivative, funcDerivative]);
},
'OperatorNode, Object': function (node, constNodes) {
if (constNodes[node] !== undefined) {
return new ConstantNode('0', config.number);
}
var arg1 = node.args[0];
var arg2 = node.args[1];
switch (node.op) {
case '+':
case '-':
// d/dx(+/-f(x)) = +/-f'(x)
if (node.args.length == 1) {
return new OperatorNode(node.op, node.fn, [_derivative(arg1, constNodes)]);
}
// Linearity of differentiation, d/dx(f(x) +/- g(x)) = f'(x) +/- g'(x)
return new OperatorNode(node.op, node.fn, [
_derivative(arg1, constNodes),
_derivative(arg2, constNodes)
]);
case '*':
// d/dx(c*f(x)) = c*f'(x)
if (constNodes[arg1] !== undefined || constNodes[arg2] !== undefined) {
var newArgs = (constNodes[arg1] !== undefined)
? [arg1.clone(), _derivative(arg2, constNodes)]
: [arg2.clone(), _derivative(arg1, constNodes)];
return new OperatorNode('*', 'multiply', newArgs);
}
// Product Rule, d/dx(f(x)*g(x)) = f'(x)*g(x) + f(x)*g'(x)
return new OperatorNode('+', 'add', [
new OperatorNode('*', 'multiply', [_derivative(arg1, constNodes), arg2.clone()]),
new OperatorNode('*', 'multiply', [arg1.clone(), _derivative(arg2, constNodes)])
]);
case '/':
// d/dx(f(x) / c) = f'(x) / c
if (constNodes[arg2] !== undefined) {
return new OperatorNode('/', 'divide', [_derivative(arg1, constNodes), arg2]);
}
// Reciprocal Rule, d/dx(c / f(x)) = -c(f'(x)/f(x)^2)
if (constNodes[arg1] !== undefined) {
return new OperatorNode('*', 'multiply', [
new OperatorNode('-', 'unaryMinus', [arg1]),
new OperatorNode('/', 'divide', [
_derivative(arg2, constNodes),
new OperatorNode('^', 'pow', [arg2.clone(), new ConstantNode('2', config.number)])
])
]);
}
// Quotient rule, d/dx(f(x) / g(x)) = (f'(x)g(x) - f(x)g'(x)) / g(x)^2
return new OperatorNode('/', 'divide', [
new OperatorNode('-', 'subtract', [
new OperatorNode('*', 'multiply', [_derivative(arg1, constNodes), arg2.clone()]),
new OperatorNode('*', 'multiply', [arg1.clone(), _derivative(arg2, constNodes)])
]),
new OperatorNode('^', 'pow', [arg2.clone(), new ConstantNode('2', config.number)])
]);
case '^':
if (constNodes[arg1] !== undefined) {
// If is secretly constant; 0^f(x) = 1 (in JS), 1^f(x) = 1
if (arg1.isConstantNode && (arg1.value == '0' || arg1.value == '1')) {
return new ConstantNode('0', config.number);
}
// d/dx(c^f(x)) = c^f(x)*ln(c)*f'(x)
return new OperatorNode('*', 'multiply', [
node,
new OperatorNode('*', 'multiply', [
new FunctionNode('log', [arg1.clone()]),
_derivative(arg2.clone(), constNodes)
])
]);
}
if (constNodes[arg2] !== undefined) {
if (arg2.isConstantNode) {
var expValue = arg2.value;
// If is secretly constant; f(x)^0 = 1 -> d/dx(1) = 0
if (expValue == '0') {
return new ConstantNode('0', config.number);
}
// Ignore exponent; f(x)^1 = f(x)
if (expValue == '1') {
return _derivative(arg1, constNodes);
}
}
// Elementary Power Rule, d/dx(f(x)^c) = c*f'(x)*f(x)^(c-1)
var powMinusOne = new OperatorNode('^', 'pow', [
arg1.clone(),
new OperatorNode('-', 'subtract', [
arg2,
new ConstantNode('1', config.number)
])
]);
return new OperatorNode('*', 'multiply', [
arg2.clone(),
new OperatorNode('*', 'multiply', [
_derivative(arg1, constNodes),
powMinusOne
]),
]);
}
// Functional Power Rule, d/dx(f^g) = f^g*[f'*(g/f) + g'ln(f)]
return new OperatorNode('*', 'multiply', [
new OperatorNode('^', 'pow', [arg1.clone(), arg2.clone()]),
new OperatorNode('+', 'add', [
new OperatorNode('*', 'multiply', [
_derivative(arg1, constNodes),
new OperatorNode('/', 'divide', [arg2.clone(), arg1.clone()])
]),
new OperatorNode('*', 'multiply', [
_derivative(arg2, constNodes),
new FunctionNode('log', [arg1.clone()])
])
])
]);
case '%':
case 'mod':
default: throw new Error('Operator "' + node.op + '" not supported by derivative');
}
}
});
/**
* Ensures the number of arguments for a function are correct,
* and will throw an error otherwise.
*
* @param {FunctionNode} node
*/
function funcArgsCheck(node) {
//TODO add min, max etc
if ((node.name == 'log' || node.name == 'nthRoot') && node.args.length == 2) {
return;
}
// There should be an incorrect number of arguments if we reach here
// Change all args to constants to avoid unidentified
// symbol error when compiling function
for (var i = 0; i < node.args.length; ++i) {
node.args[i] = new ConstantNode(0);
}
node.compile().eval();
throw new Error('Expected TypeError, but none found');
}
return derivative;
}
exports.name = 'derivative';
exports.factory = factory;

View File

@@ -0,0 +1,15 @@
module.exports = [
require('./derivative'),
// simplify
require('./simplify'),
// decomposition
require('./decomposition/lup'),
require('./decomposition/slu'),
// solver
require('./solver/lsolve'),
require('./solver/lusolve'),
require('./solver/usolve')
];

View File

@@ -0,0 +1,610 @@
'use strict';
function factory (type, config, load, typed) {
var parse = load(require('../../expression/parse'));
var ConstantNode = load(require('../../expression/node/ConstantNode'));
var FunctionNode = load(require('../../expression/node/FunctionNode'));
var OperatorNode = load(require('../../expression/node/OperatorNode'));
var ParenthesisNode = load(require('../../expression/node/ParenthesisNode'));
var SymbolNode = load(require('../../expression/node/SymbolNode'));
var Node = load(require('../../expression/node/Node'));
var simplifyConstant = load(require('./simplify/simplifyConstant'));
var util = load(require('./simplify/util'));
var isCommutative = util.isCommutative;
var isAssociative = util.isAssociative;
var flatten = util.flatten;
var unflattenr = util.unflattenr;
var unflattenl = util.unflattenl;
var createMakeNodeFunction = util.createMakeNodeFunction;
/**
* Simplify an expression tree.
*
* It's possible to pass a custom set of rules to the function as second
* argument. A rule can be specified as an object, string, or function:
*
* var rules = [
* { l: 'n1*n3 + n2*n3', r: '(n1+n2)*n3' },
* 'n1*n3 + n2*n3 -> (n1+n2)*n3',
* function (node) {
* // ... return a new node or return the node unchanged
* return node
* }
* ]
*
*
* The default list with rules is exposed on the function as `simplify.rules`
* and can be used as a basis to built a set of custom rules.
*
* For more details on the theory, see:
*
* - [Strategies for simplifying math expressions (Stackoverflow)](http://stackoverflow.com/questions/7540227/strategies-for-simplifying-math-expressions)
* - [Symbolic computation - Simplification (Wikipedia)](https://en.wikipedia.org/wiki/Symbolic_computation#Simplification)
*
* Syntax:
*
* simplify(expr)
* simplify(expr, rules)
*
* Examples:
*
* math.simplify('2 * 1 * x ^ (2 - 1)'); // Node {2 * x}
* var f = math.parse('2 * 1 * x ^ (2 - 1)');
* math.simplify(f); // Node {2 * x}
*
* See also:
*
* derivative, parse, eval
*
* @param {Node | string} expr
* The expression to be simplified
* @param {Array<{l:string, r: string} | string | function>} [rules]
* Optional list with custom rules
* @return {Node} Returns the simplified form of `expr`
*/
var simplify = typed('simplify', {
'string': function (expr) {
return simplify(parse(expr), simplify.rules);
},
'string, Array': function (expr, rules) {
return simplify(parse(expr), rules);
},
'Node': function (expr) {
return simplify(expr, simplify.rules);
},
'Node, Array': function (expr, rules) {
rules = _buildRules(rules);
var res = removeParens(expr);
var after = res.toString({parenthesis: 'all'});
var before = null;
while(before != after) {
lastsym = 0;
before = after;
for (var i=0; i<rules.length; i++) {
if (typeof rules[i] === 'function') {
res = rules[i](res);
}
else {
flatten(res);
res = applyRule(res, rules[i]);
}
unflattenl(res); // using left-heavy binary tree here since custom rule functions may expect it
}
after = res.toString({parenthesis: 'all'});
}
return res;
}
});
function removeParens(node) {
return node.transform(function(node, path, parent) {
if(node.isParenthesisNode) {
return node.content;
}
else {
return node;
}
});
}
// Array of strings, used to build the ruleSet.
// Each l (left side) and r (right side) are parsed by
// the expression parser into a node tree.
// Left hand sides are matched to subtrees within the
// expression to be parsed and replaced with the right
// hand side.
// TODO: Add support for constraints on constants (either in the form of a '=' expression or a callback [callback allows things like comparing symbols alphabetically])
// To evaluate lhs constants for rhs constants, use: { l: 'c1+c2', r: 'c3', evaluate: 'c3 = c1 + c2' }. Multiple assignments are separated by ';' in block format.
// It is possible to get into an infinite loop with conflicting rules
simplify.rules = [
{ l: 'n^0', r: '1' },
{ l: '0*n', r: '0' },
{ l: 'n/n', r: '1'},
{ l: 'n^1', r: 'n' },
{ l: '+n1', r:'n1' },
{ l: 'n--n1', r:'n+n1' },
{ l: 'log(e)', r:'1' },
// temporary rules
{ l: 'n-n1', r:'n+-n1' }, // temporarily replace 'subtract' so we can further flatten the 'add' operator
{ l: '-(c*C)', r: '(-c) * C' }, // make non-constant terms positive
{ l: '-C', r: '(-1) * C' },
{ l: 'n/n1^n2', r:'n*n1^-n2' }, // temporarily replace 'divide' so we can further flatten the 'multiply' operator
{ l: 'n/n1', r:'n*n1^-1' },
// collect like factors
{ l: 'n*n', r: 'n^2' },
{ l: 'n * n^n1', r: 'n^(n1+1)' },
{ l: 'n^n1 * n^n2', r: 'n^(n1+n2)' },
// collect like terms
{ l: 'n+n', r: '2*n' },
{ l: 'n+-n', r: '0' },
{ l: 'n1*n2 + n2', r: '(n1+1)*n2' },
{ l: 'n1*n3 + n2*n3', r: '(n1+n2)*n3' },
simplifyConstant,
{ l: '(-n)*n1', r: '-(n*n1)' }, // make factors positive (and undo 'make non-constant terms positive')
// ordering of constants
{ l: 'c+C', r: 'C+c', context: { 'add': { commutative:false } } },
{ l: 'C*c', r: 'c*C', context: { 'multiply': { commutative:false } } },
// undo temporary rules
{ l: '(-1) * n', r: '-n' },
{ l: 'n+-n1', r:'n-n1' }, // undo replace 'subtract'
{ l: 'n*(n1^-1)', r:'n/n1' }, // undo replace 'divide'
{ l: 'n*n1^-n2', r:'n/n1^n2' },
{ l: 'n1^-1', r:'1/n1' },
{ l: 'n*(n1/n2)', r:'(n*n1)/n2' }, // '*' before '/'
{ l: 'n-(n1+n2)', r:'n-n1-n2' }, // '-' before '+'
// { l: '(n1/n2)/n3', r: 'n1/(n2*n3)' },
// { l: '(n*n1)/(n*n2)', r: 'n1/n2' },
{ l: '1*n', r: 'n' } // this pattern can be produced by simplifyConstant
];
/**
* Parse the string array of rules into nodes
*
* Example syntax for rules:
*
* Position constants to the left in a product:
* { l: 'n1 * c1', r: 'c1 * n1' }
* n1 is any Node, and c1 is a ConstantNode.
*
* Apply difference of squares formula:
* { l: '(n1 - n2) * (n1 + n2)', r: 'n1^2 - n2^2' }
* n1, n2 mean any Node.
*
* Short hand notation:
* 'n1 * c1 -> c1 * n1'
*/
function _buildRules(rules) {
// Array of rules to be used to simplify expressions
var ruleSet = [];
for(var i=0; i<rules.length; i++) {
var rule = rules[i];
var newRule;
var ruleType = typeof rule;
switch (ruleType) {
case 'string':
var lr = rule.split('->');
if (lr.length !== 2) {
throw SyntaxError('Could not parse rule: ' + rule);
}
rule = {l: lr[0], r: lr[1]};
/* falls through */
case 'object':
newRule = {
l: removeParens(parse(rule.l)),
r: removeParens(parse(rule.r)),
}
if(rule.context) {
newRule.evaluate = rule.context;
}
if(rule.evaluate) {
newRule.evaluate = parse(rule.evaluate);
}
if (newRule.l.isOperatorNode && isAssociative(newRule.l)) {
var makeNode = createMakeNodeFunction(newRule.l);
var expandsym = _getExpandPlaceholderSymbol();
newRule.expanded = {};
newRule.expanded.l = makeNode([newRule.l.clone(), expandsym]);
// Push the expandsym into the deepest possible branch.
// This helps to match the newRule against nodes returned from getSplits() later on.
flatten(newRule.expanded.l);
unflattenr(newRule.expanded.l);
newRule.expanded.r = makeNode([newRule.r, expandsym]);
}
break;
case 'function':
newRule = rule;
break;
default:
throw TypeError('Unsupported type of rule: ' + ruleType);
}
// console.log('Adding rule: ' + rules[i]);
// console.log(newRule);
ruleSet.push(newRule);
}
return ruleSet;
}
var lastsym = 0;
function _getExpandPlaceholderSymbol() {
return new SymbolNode('_p'+lastsym++);
}
/**
* Returns a simplfied form of node, or the original node if no simplification was possible.
*
* @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} node
* @return {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} The simplified form of `expr`, or the original node if no simplification was possible.
*/
var applyRule = typed('applyRule', {
'Node, Object': function (node, rule) {
//console.log('Entering applyRule(' + node.toString() + ')');
// Do not clone node unless we find a match
var res = node;
// First replace our child nodes with their simplified versions
// If a child could not be simplified, the assignments will have
// no effect since the node is returned unchanged
if (res instanceof OperatorNode || res instanceof FunctionNode) {
if (res.args) {
for(var i=0; i<res.args.length; i++) {
res.args[i] = applyRule(res.args[i], rule);
}
}
}
else if(res instanceof ParenthesisNode) {
if(res.content) {
res.content = applyRule(res.content, rule);
}
}
// Try to match a rule against this node
var repl = rule.r;
var matches = _ruleMatch(rule.l, res)[0];
// If the rule is associative operator, we can try matching it while allowing additional terms.
// This allows us to match rules like 'n+n' to the expression '(1+x)+x' or even 'x+1+x' if the operator is commutative.
if (!matches && rule.expanded) {
repl = rule.expanded.r;
matches = _ruleMatch(rule.expanded.l, res)[0];
}
if (matches) {
// var before = res.toString({parenthesis: 'all'});
// Create a new node by cloning the rhs of the matched rule
res = repl.clone();
// Replace placeholders with their respective nodes
//console.log('Traversing rule ' + res);
res = res.transform(function(n, path, parent) {
if(n.isSymbolNode) {
if(matches.placeholders.hasOwnProperty(n.name)) {
var replace = matches.placeholders[n.name].clone();
return replace;
}
}
return n;
});
// var after = res.toString({parenthesis: 'all'});
// console.log('Simplified ' + before + ' to ' + after);
}
return res;
}
});
/**
* Get (binary) combinations of a flattened binary node
* e.g. +(node1, node2, node3) -> [
* +(node1, +(node2, node3)),
* +(node2, +(node1, node3)),
* +(node3, +(node1, node2))]
*
*/
function getSplits(node, context) {
var res = [];
var right, rightArgs;
var makeNode = createMakeNodeFunction(node);
if (isCommutative(node, context)) {
for (var i=0; i<node.args.length; i++) {
rightArgs = node.args.slice(0);
rightArgs.splice(i, 1);
right = (rightArgs.length === 1) ? rightArgs[0] : makeNode(rightArgs);
res.push(makeNode([node.args[i], right]));
}
}
else {
rightArgs = node.args.slice(1);
right = (rightArgs.length === 1) ? rightArgs[0] : makeNode(rightArgs);
res.push(makeNode([node.args[0], right]));
}
return res;
}
/**
* Returns the set union of two match-placeholders or null if there is a conflict.
*/
function mergeMatch(match1, match2) {
var res = {placeholders:{}};
// Some matches may not have placeholders; this is OK
if (!match1.placeholders && !match2.placeholders) {
return res;
}
else if (!match1.placeholders) {
return match2;
}
else if (!match2.placeholders) {
return match1;
}
// Placeholders with the same key must match exactly
for (var key in match1.placeholders) {
res.placeholders[key] = match1.placeholders[key];
if (match2.placeholders.hasOwnProperty(key)) {
if (!_exactMatch(match1.placeholders[key], match2.placeholders[key] )) {
return null;
}
}
}
for (var key in match2.placeholders) {
res.placeholders[key] = match2.placeholders[key];
}
return res;
}
/**
* Combine two lists of matches by applying mergeMatch to the cartesian product of two lists of matches.
* Each list represents matches found in one child of a node.
*/
function combineChildMatches(list1, list2) {
var res = [];
if (list1.length === 0 || list2.length === 0) {
return res;
}
var merged;
for (var i1 = 0; i1 < list1.length; i1++) {
for (var i2 = 0; i2 < list2.length; i2++) {
merged = mergeMatch(list1[i1], list2[i2]);
if (merged) {
res.push(merged);
}
}
}
return res;
}
/**
* Combine multiple lists of matches by applying mergeMatch to the cartesian product of two lists of matches.
* Each list represents matches found in one child of a node.
* Returns a list of unique matches.
*/
function mergeChildMatches(childMatches) {
if (childMatches.length === 0) {
return childMatches;
}
var sets = childMatches.reduce(combineChildMatches);
var uniqueSets = [];
var unique = {};
for(var i = 0; i < sets.length; i++) {
var s = JSON.stringify(sets[i]);
if (!unique[s]) {
unique[s] = true;
uniqueSets.push(sets[i]);
}
}
return uniqueSets;
}
/**
* Determines whether node matches rule.
*
* @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} rule
* @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} node
* @return {Object} Information about the match, if it exists.
*/
function _ruleMatch(rule, node, isSplit) {
// console.log('Entering _ruleMatch(' + JSON.stringify(rule) + ', ' + JSON.stringify(node) + ')');
// console.log('rule = ' + rule);
// console.log('node = ' + node);
// console.log('Entering _ruleMatch(' + rule.toString() + ', ' + node.toString() + ')');
var res = [{placeholders:{}}];
if (rule instanceof OperatorNode && node instanceof OperatorNode
|| rule instanceof FunctionNode && node instanceof FunctionNode) {
// If the rule is an OperatorNode or a FunctionNode, then node must match exactly
if (rule instanceof OperatorNode) {
if (rule.op !== node.op || rule.fn !== node.fn) {
return [];
}
}
else if (rule instanceof FunctionNode) {
if (rule.name !== node.name) {
return [];
}
}
// rule and node match. Search the children of rule and node.
if (node.args.length === 1 && rule.args.length === 1 || !isAssociative(node) || isSplit) {
// Expect non-associative operators to match exactly
var childMatches = [];
for (var i = 0; i < rule.args.length; i++) {
var childMatch = _ruleMatch(rule.args[i], node.args[i]);
if (childMatch.length === 0) {
// Child did not match, so stop searching immediately
return [];
}
// The child matched, so add the information returned from the child to our result
childMatches.push(childMatch);
}
res = mergeChildMatches(childMatches);
}
else if (node.args.length >= 2 && rule.args.length === 2) { // node is flattened, rule is not
// Associative operators/functions can be split in different ways so we check if the rule matches each
// them and return their union.
var splits = getSplits(node, rule.context);
var splitMatches = [];
for(var i = 0; i < splits.length; i++) {
var matchSet = _ruleMatch(rule, splits[i], true); // recursing at the same tree depth here
splitMatches = splitMatches.concat(matchSet);
}
return splitMatches;
}
else if (rule.args.length > 2) {
throw Error('Unexpected non-binary associative function: ' + rule.toString());
}
else {
// Incorrect number of arguments in rule and node, so no match
return [];
}
}
else if (rule instanceof SymbolNode) {
// If the rule is a SymbolNode, then it carries a special meaning
// according to the first character of the symbol node name.
// c.* matches a ConstantNode
// n.* matches any node
if (rule.name.length === 0) {
throw new Error('Symbol in rule has 0 length...!?');
}
if (rule.name[0] == 'n' || rule.name.substring(0,2) == '_p') {
// rule matches _anything_, so assign this node to the rule.name placeholder
// Assign node to the rule.name placeholder.
// Our parent will check for matches among placeholders.
res[0].placeholders[rule.name] = node;
}
else if (rule.name[0] == 'v') {
// rule matches any variable thing (not a ConstantNode)
if(!node.isConstantNode) {
res[0].placeholders[rule.name] = node;
}
else {
// Mis-match: rule was expecting something other than a ConstantNode
return [];
}
}
else if (rule.name[0] == 'C') {
// rule matches anything but a ConstantNode
if(node instanceof ConstantNode) {
// Mis-match: rule was expecting not a ConstantNode
return [];
}
else {
res[0].placeholders[rule.name] = node;
}
}
else if (rule.name[0] == 'c') {
// rule matches any ConstantNode
if(node instanceof ConstantNode) {
res[0].placeholders[rule.name] = node;
}
else {
// Mis-match: rule was expecting a ConstantNode
return [];
}
}
else {
throw new Error('Invalid symbol in rule: ' + rule.name);
}
}
else if (rule instanceof ConstantNode) {
// Literal constant in our rule, so much match node exactly
if(rule.value === node.value) {
// The constants match
}
else {
return [];
}
}
else {
// Some other node was encountered which we aren't prepared for, so no match
return [];
}
// It's a match!
// console.log('_ruleMatch(' + rule.toString() + ', ' + node.toString() + ') found a match');
return res;
}
/**
* Determines whether p and q (and all their children nodes) are identical.
*
* @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} p
* @param {ConstantNode | SymbolNode | ParenthesisNode | FunctionNode | OperatorNode} q
* @return {Object} Information about the match, if it exists.
*/
function _exactMatch(p, q) {
if(p instanceof ConstantNode && q instanceof ConstantNode) {
if(p.value !== q.value) {
return false;
}
}
else if(p instanceof SymbolNode && q instanceof SymbolNode) {
if(p.name !== q.name) {
return false;
}
}
else if(p instanceof OperatorNode && q instanceof OperatorNode
|| p instanceof FunctionNode && q instanceof FunctionNode) {
if (p instanceof OperatorNode) {
if (p.op !== q.op || p.fn !== q.fn) {
return false;
}
}
else if (p instanceof FunctionNode) {
if (p.name !== q.name) {
return false;
}
}
if(p.args.length !== q.args.length) {
return false;
}
for(var i=0; i<p.args.length; i++) {
if(!_exactMatch(p.args[i], q.args[i])) {
return false;
}
}
}
else {
return false;
}
return true;
}
return simplify;
}
exports.name = 'simplify';
exports.factory = factory;

View File

@@ -0,0 +1,217 @@
'use strict';
var digits = require('./../../../utils/number').digits;
// TODO this could be improved by simplifying seperated constants under associative and commutative operators
function factory(type, config, load, typed, math) {
var util = load(require('./util'));
var isCommutative = util.isCommutative;
var isAssociative = util.isAssociative;
var allChildren = util.allChildren;
var createMakeNodeFunction = util.createMakeNodeFunction;
var ConstantNode = math.expression.node.ConstantNode;
var OperatorNode = math.expression.node.OperatorNode;
function simplifyConstant(expr) {
var res = foldFraction(expr);
return res.isNode ? res : _toNode(res);
}
function _eval(fnname, args) {
try {
return _toNumber(math[fnname].apply(null, args));
}
catch (ignore) {
// sometimes the implicit type conversion causes the evaluation to fail, so we'll try again using just numbers
args = args.map(function(x){ return x.valueOf(); });
return _toNumber(math[fnname].apply(null, args));
}
}
var _toNode = typed({
'Fraction': _fractionToNode,
'number': _numberToNode,
'BigNumber': function(s) {
return _numberToNode(s._toNumber());
},
'Complex': function(s) {
throw 'Cannot convert Complex number to Node';
}
});
var _toNumber = typed({
'Fraction': function(s) { return s; },
'BigNumber': function(s) {
if (s.decimalPlaces() <= 15) {
return math.fraction(s.toNumber())
}
return s.toNumber();
},
'number': function(s) {
if (digits(s) <= 15) {
return math.fraction(s);
}
return s;
},
'Complex': function(s) {
if (s.im !== 0) {
return s;
}
if (digits(s.re) <= 15) {
return math.fraction(s.re);
}
return s.re;
},
});
function _numberToNode(n) {
if (n < 0) {
return new OperatorNode('-', 'unaryMinus', [new ConstantNode(-n)])
}
return new ConstantNode(n);
}
function _fractionToNode(f) {
var n;
var vn = f.s*f.n;
if (vn < 0) {
n = new OperatorNode('-', 'unaryMinus', [new ConstantNode(-vn)])
}
else {
n = new ConstantNode(vn);
}
if (f.d === 1) {
return n;
}
return new OperatorNode('/', 'divide', [n, new ConstantNode(f.d)]);
}
/*
* Create a binary tree from a list of Fractions and Nodes.
* Tries to fold Fractions by evaluating them until the first Node in the list is hit, so
* `args` should be sorted to have the Fractions at the start (if the operator is commutative).
* @param args - list of Fractions and Nodes
* @param fn - evaluator for the binary operation evaluator that accepts two Fractions
* @param makeNode - creates a binary OperatorNode/FunctionNode from a list of child Nodes
* if args.length is 1, returns args[0]
* @return - Either a Node representing a binary expression or Fraction
*/
function foldOp(fn, args, makeNode) {
return args.reduce(function(a, b) {
if (!a.isNode && !b.isNode) {
try {
return _eval(fn, [a,b]);
}
catch (ignoreandcontinue) {}
a = _toNode(a);
b = _toNode(b);
}
else if (!a.isNode) {
a = _toNode(a);
}
else if (!b.isNode) {
b = _toNode(b);
}
return makeNode([a, b]);
});
}
// destroys the original node and returns a folded one
function foldFraction(node) {
switch(node.type) {
case 'SymbolNode':
return node;
case 'ConstantNode':
return _toNumber(node.value);
case 'FunctionNode':
if (math[node.name] && math[node.name].rawArgs) {
return node;
}
/* falls through */
case 'OperatorNode':
var fn = node.fn.toString();
var args;
var res;
var makeNode = createMakeNodeFunction(node);
if (node.args.length === 1) {
args = [foldFraction(node.args[0])];
if (!args[0].isNode) {
res = _eval(fn, args);
}
else {
res = makeNode(args);
}
}
else if (isAssociative(node)) {
args = allChildren(node);
args = args.map(foldFraction);
if (isCommutative(fn)) {
// commutative binary operator
var consts = [], vars = [];
for (var i=0; i < args.length; i++) {
if (!args[i].isNode) {
consts.push(args[i]);
}
else {
vars.push(args[i]);
}
}
if (consts.length > 1) {
res = foldOp(fn, consts, makeNode);
vars.unshift(res);
res = foldOp(fn, vars, makeNode);
}
else {
// we won't change the children order since it's not neccessary
res = foldOp(fn, args, makeNode);
}
}
else {
// non-commutative binary operator
res = foldOp(fn, args, makeNode);
}
}
else {
// non-associative binary operator
args = node.args.map(foldFraction);
res = foldOp(fn, args, makeNode);
}
return res;
case 'ParenthesisNode':
// remove the uneccessary parenthesis
return foldFraction(node.content);
case 'AccessorNode':
/* falls through */
case 'ArrayNode':
/* falls through */
case 'AssignmentNode':
/* falls through */
case 'BlockNode':
/* falls through */
case 'FunctionAssignmentNode':
/* falls through */
case 'IndexNode':
/* falls through */
case 'ObjectNode':
/* falls through */
case 'RangeNode':
/* falls through */
case 'UpdateNode':
/* falls through */
case 'ConditionalNode':
/* falls through */
default:
throw 'Unimplemented node type in simplifyConstant: '+node.type;
}
}
return simplifyConstant;
}
exports.math = true;
exports.name = 'simplifyConstant';
exports.path = 'algebra.simplify';
exports.factory = factory;

View File

@@ -0,0 +1,158 @@
'use strict';
function factory(type, config, load, typed, math) {
var FunctionNode = math.expression.node.FunctionNode;
var OperatorNode = math.expression.node.OperatorNode;
var SymbolNode = math.expression.node.SymbolNode;
// TODO commutative/associative properties rely on the arguments
// e.g. multiply is not commutative for matrices
// The properties should be calculated from an argument to simplify, or possibly something in math.config
// the other option is for typed() to specify a return type so that we can evaluate the type of arguments
var commutative = {
'add': true,
'multiply': true
}
var associative = {
'add': true,
'multiply': true
}
function isCommutative(node, context) {
if (!node.args || node.args.length <=1) {
return true;
}
var name = node.fn.toString();
if (context && context.hasOwnProperty(name) && context[name].hasOwnProperty('commutative')) {
return context[name].commutative;
}
return commutative[name] || false;
}
function isAssociative(node, context) {
if (!node.args || node.args.length <=1) {
return true;
}
var name = node.fn.toString();
if (context && context.hasOwnProperty(name) && context[name].hasOwnProperty('associative')) {
return context[name].associative;
}
return associative[name] || false;
}
/**
* Flatten all associative operators in an expression tree.
* Assumes parentheses have already been removed.
*/
function flatten(node) {
if (!node.args || node.args.length === 0) {
return node;
}
node.args = allChildren(node);
for (var i=0; i<node.args.length; i++) {
flatten(node.args[i]);
}
}
/**
* Get the children of a node as if it has been flattened.
* TODO implement for FunctionNodes
*/
function allChildren(node) {
var op;
var children = [];
var findChildren = function(node) {
for (var i = 0; i < node.args.length; i++) {
var child = node.args[i];
if (child.isOperatorNode && op === child.op) {
findChildren(child);
}
else {
children.push(child);
}
}
};
if (node.isOperatorNode && isAssociative(node)) {
op = node.op;
findChildren(node);
return children;
}
else {
return node.args;
}
}
/**
* Unflatten all flattened operators to a right-heavy binary tree.
*/
function unflattenr(node) {
if (!node.args || node.args.length === 0) {
return;
}
var makeNode = createMakeNodeFunction(node);
var l = node.args.length;
for (var i = 0; i < l; i++) {
unflattenr(node.args[i])
}
if (l > 2 && isAssociative(node)) {
var curnode = node.args.pop();
while (node.args.length > 0) {
curnode = makeNode([node.args.pop(), curnode]);
}
node.args = curnode.args;
}
}
/**
* Unflatten all flattened operators to a left-heavy binary tree.
*/
function unflattenl(node) {
if (!node.args || node.args.length === 0) {
return;
}
var makeNode = createMakeNodeFunction(node);
var l = node.args.length;
for (var i = 0; i < l; i++) {
unflattenl(node.args[i])
}
if (l > 2 && isAssociative(node)) {
var curnode = node.args.shift();
while (node.args.length > 0) {
curnode = makeNode([curnode, node.args.shift()]);
}
node.args = curnode.args;
}
}
function createMakeNodeFunction(node) {
if (node.isOperatorNode) {
return function(args){
try{
return new OperatorNode(node.op, node.fn, args);
} catch(err){
console.error(err);
return [];
}
};
}
else {
return function(args){
return new FunctionNode(new SymbolNode(node.name), args);
};
}
}
return {
createMakeNodeFunction: createMakeNodeFunction,
isCommutative: isCommutative,
isAssociative: isAssociative,
flatten: flatten,
allChildren: allChildren,
unflattenr: unflattenr,
unflattenl: unflattenl
};
}
exports.factory = factory;
exports.math = true;

View File

@@ -0,0 +1,187 @@
'use strict';
function factory (type, config, load, typed) {
var matrix = load(require('../../../type/matrix/function/matrix'));
var divideScalar = load(require('../../arithmetic/divideScalar'));
var multiplyScalar = load(require('../../arithmetic/multiplyScalar'));
var subtract = load(require('../../arithmetic/subtract'));
var equalScalar = load(require('../../relational/equalScalar'));
var solveValidation = load(require('./utils/solveValidation'));
var DenseMatrix = type.DenseMatrix;
/**
* Solves the linear equation system by forwards substitution. Matrix must be a lower triangular matrix.
*
* `L * x = b`
*
* Syntax:
*
* math.lsolve(L, b);
*
* Examples:
*
* var a = [[-2, 3], [2, 1]];
* var b = [11, 9];
* var x = lsolve(a, b); // [[-5.5], [20]]
*
* See also:
*
* lup, slu, usolve, lusolve
*
* @param {Matrix, Array} L A N x N matrix or array (L)
* @param {Matrix, Array} b A column vector with the b values
*
* @return {DenseMatrix | Array} A column vector with the linear system solution (x)
*/
var lsolve = typed('lsolve', {
'SparseMatrix, Array | Matrix': function (m, b) {
// process matrix
return _sparseForwardSubstitution(m, b);
},
'DenseMatrix, Array | Matrix': function (m, b) {
// process matrix
return _denseForwardSubstitution(m, b);
},
'Array, Array | Matrix': function (a, b) {
// create dense matrix from array
var m = matrix(a);
// use matrix implementation
var r = _denseForwardSubstitution(m, b);
// result
return r.valueOf();
}
});
var _denseForwardSubstitution = function (m, b) {
// validate matrix and vector, return copy of column vector b
b = solveValidation(m, b, true);
// column vector data
var bdata = b._data;
// rows & columns
var rows = m._size[0];
var columns = m._size[1];
// result
var x = [];
// data
var data = m._data;
// forward solve m * x = b, loop columns
for (var j = 0; j < columns; j++) {
// b[j]
var bj = bdata[j][0] || 0;
// x[j]
var xj;
// forward substitution (outer product) avoids inner looping when bj == 0
if (!equalScalar(bj, 0)) {
// value @ [j, j]
var vjj = data[j][j];
// check vjj
if (equalScalar(vjj, 0)) {
// system cannot be solved
throw new Error('Linear system cannot be solved since matrix is singular');
}
// calculate xj
xj = divideScalar(bj, vjj);
// loop rows
for (var i = j + 1; i < rows; i++) {
// update copy of b
bdata[i] = [subtract(bdata[i][0] || 0, multiplyScalar(xj, data[i][j]))];
}
}
else {
// zero @ j
xj = 0;
}
// update x
x[j] = [xj];
}
// return vector
return new DenseMatrix({
data: x,
size: [rows, 1]
});
};
var _sparseForwardSubstitution = function (m, b) {
// validate matrix and vector, return copy of column vector b
b = solveValidation(m, b, true);
// column vector data
var bdata = b._data;
// rows & columns
var rows = m._size[0];
var columns = m._size[1];
// matrix arrays
var values = m._values;
var index = m._index;
var ptr = m._ptr;
// vars
var i, k;
// result
var x = [];
// forward solve m * x = b, loop columns
for (var j = 0; j < columns; j++) {
// b[j]
var bj = bdata[j][0] || 0;
// forward substitution (outer product) avoids inner looping when bj == 0
if (!equalScalar(bj, 0)) {
// value @ [j, j]
var vjj = 0;
// lower triangular matrix values & index (column j)
var jvalues = [];
var jindex = [];
// last index in column
var l = ptr[j + 1];
// values in column, find value @ [j, j]
for (k = ptr[j]; k < l; k++) {
// row
i = index[k];
// check row (rows are not sorted!)
if (i === j) {
// update vjj
vjj = values[k];
}
else if (i > j) {
// store lower triangular
jvalues.push(values[k]);
jindex.push(i);
}
}
// at this point we must have a value @ [j, j]
if (equalScalar(vjj, 0)) {
// system cannot be solved, there is no value @ [j, j]
throw new Error('Linear system cannot be solved since matrix is singular');
}
// calculate xj
var xj = divideScalar(bj, vjj);
// loop lower triangular
for (k = 0, l = jindex.length; k < l; k++) {
// row
i = jindex[k];
// update copy of b
bdata[i] = [subtract(bdata[i][0] || 0, multiplyScalar(xj, jvalues[k]))];
}
// update x
x[j] = [xj];
}
else {
// update x
x[j] = [0];
}
}
// return vector
return new DenseMatrix({
data: x,
size: [rows, 1]
});
};
return lsolve;
}
exports.name = 'lsolve';
exports.factory = factory;

View File

@@ -0,0 +1,125 @@
'use strict';
var isArray = Array.isArray;
function factory (type, config, load, typed) {
var matrix = load(require('../../../type/matrix/function/matrix'));
var lup = load(require('../decomposition/lup'));
var slu = load(require('../decomposition/slu'));
var cs_ipvec = load(require('../sparse/cs_ipvec'));
var solveValidation = load(require('./utils/solveValidation'));
var usolve = load(require('./usolve'));
var lsolve = load(require('./lsolve'));
/**
* Solves the linear system `A * x = b` where `A` is an [n x n] matrix and `b` is a [n] column vector.
*
* Syntax:
*
* math.lusolve(A, b) // returns column vector with the solution to the linear system A * x = b
* math.lusolve(lup, b) // returns column vector with the solution to the linear system A * x = b, lup = math.lup(A)
*
* Examples:
*
* var m = [[1, 0, 0, 0], [0, 2, 0, 0], [0, 0, 3, 0], [0, 0, 0, 4]];
*
* var x = math.lusolve(m, [-1, -1, -1, -1]); // x = [[-1], [-0.5], [-1/3], [-0.25]]
*
* var f = math.lup(m);
* var x1 = math.lusolve(f, [-1, -1, -1, -1]); // x1 = [[-1], [-0.5], [-1/3], [-0.25]]
* var x2 = math.lusolve(f, [1, 2, 1, -1]); // x2 = [[1], [1], [1/3], [-0.25]]
*
* var a = [[-2, 3], [2, 1]];
* var b = [11, 9];
* var x = lusolve(a, b); // [[-5.5], [20]]
*
* See also:
*
* lup, slu, lsolve, usolve
*
* @param {Matrix | Array | Object} A Invertible Matrix or the Matrix LU decomposition
* @param {Matrix | Array} b Column Vector
* @param {number} [order] The Symbolic Ordering and Analysis order, see slu for details. Matrix must be a SparseMatrix
* @param {Number} [threshold] Partial pivoting threshold (1 for partial pivoting), see slu for details. Matrix must be a SparseMatrix.
*
* @return {DenseMatrix | Array} Column vector with the solution to the linear system A * x = b
*/
var lusolve = typed('lusolve', {
'Array, Array | Matrix': function (a, b) {
// convert a to matrix
a = matrix(a);
// matrix lup decomposition
var d = lup(a);
// solve
var x = _lusolve(d.L, d.U, d.p, null, b);
// convert result to array
return x.valueOf();
},
'DenseMatrix, Array | Matrix': function (a, b) {
// matrix lup decomposition
var d = lup(a);
// solve
return _lusolve(d.L, d.U, d.p, null, b);
},
'SparseMatrix, Array | Matrix': function (a, b) {
// matrix lup decomposition
var d = lup(a);
// solve
return _lusolve(d.L, d.U, d.p, null, b);
},
'SparseMatrix, Array | Matrix, number, number': function (a, b, order, threshold) {
// matrix lu decomposition
var d = slu(a, order, threshold);
// solve
return _lusolve(d.L, d.U, d.p, d.q, b);
},
'Object, Array | Matrix': function (d, b) {
// solve
return _lusolve(d.L, d.U, d.p, d.q, b);
}
});
var _toMatrix = function (a) {
// check it is a matrix
if (a && a.isMatrix === true)
return a;
// check array
if (isArray(a))
return matrix(a);
// throw
throw new TypeError('Invalid Matrix LU decomposition');
};
var _lusolve = function (l, u, p, q, b) {
// verify L, U, P
l = _toMatrix(l);
u = _toMatrix(u);
// validate matrix and vector
b = solveValidation(l, b, false);
// apply row permutations if needed (b is a DenseMatrix)
if (p)
b._data = cs_ipvec(p, b._data);
// use forward substitution to resolve L * y = b
var y = lsolve(l, b);
// use backward substitution to resolve U * x = y
var x = usolve(u, y);
// apply column permutations if needed (x is a DenseMatrix)
if (q)
x._data = cs_ipvec(q, x._data);
// return solution
return x;
};
return lusolve;
}
exports.name = 'lusolve';
exports.factory = factory;

View File

@@ -0,0 +1,188 @@
'use strict';
function factory (type, config, load, typed) {
var matrix = load(require('../../../type/matrix/function/matrix'));
var divideScalar = load(require('../../arithmetic/divideScalar'));
var multiplyScalar = load(require('../../arithmetic/multiplyScalar'));
var subtract = load(require('../../arithmetic/subtract'));
var equalScalar = load(require('../../relational/equalScalar'));
var solveValidation = load(require('./utils/solveValidation'));
var DenseMatrix = type.DenseMatrix;
/**
* Solves the linear equation system by backward substitution. Matrix must be an upper triangular matrix.
*
* `U * x = b`
*
* Syntax:
*
* math.usolve(U, b);
*
* Examples:
*
* var a = [[-2, 3], [2, 1]];
* var b = [11, 9];
* var x = usolve(a, b); // [[8], [9]]
*
* See also:
*
* lup, slu, usolve, lusolve
*
* @param {Matrix, Array} U A N x N matrix or array (U)
* @param {Matrix, Array} b A column vector with the b values
*
* @return {DenseMatrix | Array} A column vector with the linear system solution (x)
*/
var usolve = typed('usolve', {
'SparseMatrix, Array | Matrix': function (m, b) {
// process matrix
return _sparseBackwardSubstitution(m, b);
},
'DenseMatrix, Array | Matrix': function (m, b) {
// process matrix
return _denseBackwardSubstitution(m, b);
},
'Array, Array | Matrix': function (a, b) {
// create dense matrix from array
var m = matrix(a);
// use matrix implementation
var r = _denseBackwardSubstitution(m, b);
// result
return r.valueOf();
}
});
var _denseBackwardSubstitution = function (m, b) {
// validate matrix and vector, return copy of column vector b
b = solveValidation(m, b, true);
// column vector data
var bdata = b._data;
// rows & columns
var rows = m._size[0];
var columns = m._size[1];
// result
var x = [];
// arrays
var data = m._data;
// backward solve m * x = b, loop columns (backwards)
for (var j = columns - 1; j >= 0 ; j--) {
// b[j]
var bj = bdata[j][0] || 0;
// x[j]
var xj;
// backward substitution (outer product) avoids inner looping when bj == 0
if (!equalScalar(bj, 0)) {
// value @ [j, j]
var vjj = data[j][j];
// check vjj
if (equalScalar(vjj, 0)) {
// system cannot be solved
throw new Error('Linear system cannot be solved since matrix is singular');
}
// calculate xj
xj = divideScalar(bj, vjj);
// loop rows
for (var i = j - 1; i >= 0; i--) {
// update copy of b
bdata[i] = [subtract(bdata[i][0] || 0, multiplyScalar(xj, data[i][j]))];
}
}
else {
// zero value @ j
xj = 0;
}
// update x
x[j] = [xj];
}
// return column vector
return new DenseMatrix({
data: x,
size: [rows, 1]
});
};
var _sparseBackwardSubstitution = function (m, b) {
// validate matrix and vector, return copy of column vector b
b = solveValidation(m, b, true);
// column vector data
var bdata = b._data;
// rows & columns
var rows = m._size[0];
var columns = m._size[1];
// matrix arrays
var values = m._values;
var index = m._index;
var ptr = m._ptr;
// vars
var i, k;
// result
var x = [];
// backward solve m * x = b, loop columns (backwards)
for (var j = columns - 1; j >= 0 ; j--) {
// b[j]
var bj = bdata[j][0] || 0;
// backward substitution (outer product) avoids inner looping when bj == 0
if (!equalScalar(bj, 0)) {
// value @ [j, j]
var vjj = 0;
// upper triangular matrix values & index (column j)
var jvalues = [];
var jindex = [];
// first & last indeces in column
var f = ptr[j];
var l = ptr[j + 1];
// values in column, find value @ [j, j], loop backwards
for (k = l - 1; k >= f; k--) {
// row
i = index[k];
// check row
if (i === j) {
// update vjj
vjj = values[k];
}
else if (i < j) {
// store upper triangular
jvalues.push(values[k]);
jindex.push(i);
}
}
// at this point we must have a value @ [j, j]
if (equalScalar(vjj, 0)) {
// system cannot be solved, there is no value @ [j, j]
throw new Error('Linear system cannot be solved since matrix is singular');
}
// calculate xj
var xj = divideScalar(bj, vjj);
// loop upper triangular
for (k = 0, l = jindex.length; k < l; k++) {
// row
i = jindex[k];
// update copy of b
bdata[i] = [subtract(bdata[i][0], multiplyScalar(xj, jvalues[k]))];
}
// update x
x[j] = [xj];
}
else {
// update x
x[j] = [0];
}
}
// return vector
return new DenseMatrix({
data: x,
size: [rows, 1]
});
};
return usolve;
}
exports.name = 'usolve';
exports.factory = factory;

View File

@@ -0,0 +1,162 @@
'use strict';
var util = require('../../../../utils/index');
var string = util.string;
var array = util.array;
var isArray = Array.isArray;
function factory (type) {
var DenseMatrix = type.DenseMatrix;
/**
* Validates matrix and column vector b for backward/forward substitution algorithms.
*
* @param {Matrix} m An N x N matrix
* @param {Array | Matrix} b A column vector
* @param {Boolean} copy Return a copy of vector b
*
* @return {DenseMatrix} Dense column vector b
*/
var solveValidation = function (m, b, copy) {
// matrix size
var size = m.size();
// validate matrix dimensions
if (size.length !== 2)
throw new RangeError('Matrix must be two dimensional (size: ' + string.format(size) + ')');
// rows & columns
var rows = size[0];
var columns = size[1];
// validate rows & columns
if (rows !== columns)
throw new RangeError('Matrix must be square (size: ' + string.format(size) + ')');
// vars
var data, i, bdata;
// check b is matrix
if (b && b.isMatrix === true) {
// matrix size
var msize = b.size();
// vector
if (msize.length === 1) {
// check vector length
if (msize[0] !== rows)
throw new RangeError('Dimension mismatch. Matrix columns must match vector length.');
// create data array
data = [];
// matrix data (DenseMatrix)
bdata = b._data;
// loop b data
for (i = 0; i < rows; i++) {
// row array
data[i] = [bdata[i]];
}
// return Dense Matrix
return new DenseMatrix({
data: data,
size: [rows, 1],
datatype: b._datatype
});
}
// two dimensions
if (msize.length === 2) {
// array must be a column vector
if (msize[0] !== rows || msize[1] !== 1)
throw new RangeError('Dimension mismatch. Matrix columns must match vector length.');
// check matrix type
if (b.isDenseMatrix === true) {
// check a copy is needed
if (copy) {
// create data array
data = [];
// matrix data (DenseMatrix)
bdata = b._data;
// loop b data
for (i = 0; i < rows; i++) {
// row array
data[i] = [bdata[i][0]];
}
// return Dense Matrix
return new DenseMatrix({
data: data,
size: [rows, 1],
datatype: b._datatype
});
}
// b is already a column vector
return b;
}
// create data array
data = [];
for (i = 0; i < rows; i++)
data[i] = [0];
// sparse matrix arrays
var values = b._values;
var index = b._index;
var ptr = b._ptr;
// loop values in column 0
for (var k1 = ptr[1], k = ptr[0]; k < k1; k++) {
// row
i = index[k];
// add to data
data[i][0] = values[k];
}
// return Dense Matrix
return new DenseMatrix({
data: data,
size: [rows, 1],
datatype: b._datatype
});
}
// throw error
throw new RangeError('Dimension mismatch. Matrix columns must match vector length.');
}
// check b is array
if (isArray(b)) {
// size
var asize = array.size(b);
// check matrix dimensions, vector
if (asize.length === 1) {
// check vector length
if (asize[0] !== rows)
throw new RangeError('Dimension mismatch. Matrix columns must match vector length.');
// create data array
data = [];
// loop b
for (i = 0; i < rows; i++) {
// row array
data[i] = [b[i]];
}
// return Dense Matrix
return new DenseMatrix({
data: data,
size: [rows, 1]
});
}
if (asize.length === 2) {
// array must be a column vector
if (asize[0] !== rows || asize[1] !== 1)
throw new RangeError('Dimension mismatch. Matrix columns must match vector length.');
// create data array
data = [];
// loop b data
for (i = 0; i < rows; i++) {
// row array
data[i] = [b[i][0]];
}
// return Dense Matrix
return new DenseMatrix({
data: data,
size: [rows, 1]
});
}
// throw error
throw new RangeError('Dimension mismatch. Matrix columns must match vector length.');
}
};
return solveValidation;
}
exports.factory = factory;

View File

@@ -0,0 +1,573 @@
'use strict';
function factory (type, config, load) {
var cs_flip = load(require('./cs_flip'));
var cs_fkeep = load(require('./cs_fkeep'));
var cs_tdfs = load(require('./cs_tdfs'));
var add = load(require('../../arithmetic/add'));
var multiply = load(require('../../arithmetic/multiply'));
var transpose = load(require('../../matrix/transpose'));
/**
* Approximate minimum degree ordering. The minimum degree algorithm is a widely used
* heuristic for finding a permutation P so that P*A*P' has fewer nonzeros in its factorization
* than A. It is a gready method that selects the sparsest pivot row and column during the course
* of a right looking sparse Cholesky factorization.
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*
* @param {Number} order 0: Natural, 1: Cholesky, 2: LU, 3: QR
* @param {Matrix} m Sparse Matrix
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_amd = function (order, a) {
// check input parameters
if (!a || order <= 0 || order > 3)
return null;
// a matrix arrays
var asize = a._size;
// rows and columns
var m = asize[0];
var n = asize[1];
// initialize vars
var lemax = 0;
// dense threshold
var dense = Math.max(16, 10 * Math.sqrt(n));
dense = Math.min(n - 2, dense);
// create target matrix C
var cm = _createTargetMatrix(order, a, m, n, dense);
// drop diagonal entries
cs_fkeep(cm, _diag, null);
// C matrix arrays
var cindex = cm._index;
var cptr = cm._ptr;
// number of nonzero elements in C
var cnz = cptr[n];
// allocate result (n+1)
var P = [];
// create workspace (8 * (n + 1))
var W = [];
var len = 0; // first n + 1 entries
var nv = n + 1; // next n + 1 entries
var next = 2 * (n + 1); // next n + 1 entries
var head = 3 * (n + 1); // next n + 1 entries
var elen = 4 * (n + 1); // next n + 1 entries
var degree = 5 * (n + 1); // next n + 1 entries
var w = 6 * (n + 1); // next n + 1 entries
var hhead = 7 * (n + 1); // last n + 1 entries
// use P as workspace for last
var last = P;
// initialize quotient graph
var mark = _initializeQuotientGraph(n, cptr, W, len, head, last, next, hhead, nv, w, elen, degree);
// initialize degree lists
var nel = _initializeDegreeLists(n, cptr, W, degree, elen, w, dense, nv, head, last, next);
// minimum degree node
var mindeg = 0;
// vars
var i, j, k, k1, k2, e, pj, ln, nvi, pk, eln, p1, p2, pn, h, d;
// while (selecting pivots) do
while (nel < n) {
// select node of minimum approximate degree. amd() is now ready to start eliminating the graph. It first
// finds a node k of minimum degree and removes it from its degree list. The variable nel keeps track of thow
// many nodes have been eliminated.
for (k = -1; mindeg < n && (k = W[head + mindeg]) == -1; mindeg++);
if (W[next + k] != -1)
last[W[next + k]] = -1;
// remove k from degree list
W[head + mindeg] = W[next + k];
// elenk = |Ek|
var elenk = W[elen + k];
// # of nodes k represents
var nvk = W[nv + k];
// W[nv + k] nodes of A eliminated
nel += nvk;
// Construct a new element. The new element Lk is constructed in place if |Ek| = 0. nv[i] is
// negated for all nodes i in Lk to flag them as members of this set. Each node i is removed from the
// degree lists. All elements e in Ek are absorved into element k.
var dk = 0;
// flag k as in Lk
W[nv + k] = -nvk;
var p = cptr[k];
// do in place if W[elen + k] == 0
var pk1 = (elenk === 0) ? p : cnz;
var pk2 = pk1;
for (k1 = 1; k1 <= elenk + 1; k1++) {
if (k1 > elenk) {
// search the nodes in k
e = k;
// list of nodes starts at cindex[pj]
pj = p;
// length of list of nodes in k
ln = W[len + k] - elenk;
}
else {
// search the nodes in e
e = cindex[p++];
pj = cptr[e];
// length of list of nodes in e
ln = W[len + e];
}
for (k2 = 1; k2 <= ln; k2++) {
i = cindex[pj++];
// check node i dead, or seen
if ((nvi = W[nv + i]) <= 0)
continue;
// W[degree + Lk] += size of node i
dk += nvi;
// negate W[nv + i] to denote i in Lk
W[nv + i] = -nvi;
// place i in Lk
cindex[pk2++] = i;
if (W[next + i] != -1)
last[W[next + i]] = last[i];
// check we need to remove i from degree list
if (last[i] != -1)
W[next + last[i]] = W[next + i];
else
W[head + W[degree + i]] = W[next + i];
}
if (e != k) {
// absorb e into k
cptr[e] = cs_flip(k);
// e is now a dead element
W[w + e] = 0;
}
}
// cindex[cnz...nzmax] is free
if (elenk !== 0)
cnz = pk2;
// external degree of k - |Lk\i|
W[degree + k] = dk;
// element k is in cindex[pk1..pk2-1]
cptr[k] = pk1;
W[len + k] = pk2 - pk1;
// k is now an element
W[elen + k] = -2;
// Find set differences. The scan1 function now computes the set differences |Le \ Lk| for all elements e. At the start of the
// scan, no entry in the w array is greater than or equal to mark.
// clear w if necessary
mark = _wclear(mark, lemax, W, w, n);
// scan 1: find |Le\Lk|
for (pk = pk1; pk < pk2; pk++) {
i = cindex[pk];
// check if W[elen + i] empty, skip it
if ((eln = W[elen + i]) <= 0)
continue;
// W[nv + i] was negated
nvi = -W[nv + i];
var wnvi = mark - nvi;
// scan Ei
for (p = cptr[i], p1 = cptr[i] + eln - 1; p <= p1; p++) {
e = cindex[p];
if (W[w + e] >= mark) {
// decrement |Le\Lk|
W[w + e] -= nvi;
}
else if (W[w + e] !== 0) {
// ensure e is a live element, 1st time e seen in scan 1
W[w + e] = W[degree + e] + wnvi;
}
}
}
// degree update
// The second pass computes the approximate degree di, prunes the sets Ei and Ai, and computes a hash
// function h(i) for all nodes in Lk.
// scan2: degree update
for (pk = pk1; pk < pk2; pk++) {
// consider node i in Lk
i = cindex[pk];
p1 = cptr[i];
p2 = p1 + W[elen + i] - 1;
pn = p1;
// scan Ei
for (h = 0, d = 0, p = p1; p <= p2; p++) {
e = cindex[p];
// check e is an unabsorbed element
if (W[w + e] !== 0) {
// dext = |Le\Lk|
var dext = W[w + e] - mark;
if (dext > 0) {
// sum up the set differences
d += dext;
// keep e in Ei
cindex[pn++] = e;
// compute the hash of node i
h += e;
}
else {
// aggressive absorb. e->k
cptr[e] = cs_flip(k);
// e is a dead element
W[w + e] = 0;
}
}
}
// W[elen + i] = |Ei|
W[elen + i] = pn - p1 + 1;
var p3 = pn;
var p4 = p1 + W[len + i];
// prune edges in Ai
for (p = p2 + 1; p < p4; p++) {
j = cindex[p];
// check node j dead or in Lk
var nvj = W[nv + j];
if (nvj <= 0)
continue;
// degree(i) += |j|
d += nvj;
// place j in node list of i
cindex[pn++] = j;
// compute hash for node i
h += j;
}
// check for mass elimination
if (d === 0) {
// absorb i into k
cptr[i] = cs_flip(k);
nvi = -W[nv + i];
// |Lk| -= |i|
dk -= nvi;
// |k| += W[nv + i]
nvk += nvi;
nel += nvi;
W[nv + i] = 0;
// node i is dead
W[elen + i] = -1;
}
else {
// update degree(i)
W[degree + i] = Math.min(W[degree + i], d);
// move first node to end
cindex[pn] = cindex[p3];
// move 1st el. to end of Ei
cindex[p3] = cindex[p1];
// add k as 1st element in of Ei
cindex[p1] = k;
// new len of adj. list of node i
W[len + i] = pn - p1 + 1;
// finalize hash of i
h = (h < 0 ? -h : h) % n;
// place i in hash bucket
W[next + i] = W[hhead + h];
W[hhead + h] = i;
// save hash of i in last[i]
last[i] = h;
}
}
// finalize |Lk|
W[degree + k] = dk;
lemax = Math.max(lemax, dk);
// clear w
mark = _wclear(mark + lemax, lemax, W, w, n);
// Supernode detection. Supernode detection relies on the hash function h(i) computed for each node i.
// If two nodes have identical adjacency lists, their hash functions wil be identical.
for (pk = pk1; pk < pk2; pk++) {
i = cindex[pk];
// check i is dead, skip it
if (W[nv + i] >= 0)
continue;
// scan hash bucket of node i
h = last[i];
i = W[hhead + h];
// hash bucket will be empty
W[hhead + h] = -1;
for (; i != -1 && W[next + i] != -1; i = W[next + i], mark++) {
ln = W[len + i];
eln = W[elen + i];
for (p = cptr[i] + 1; p <= cptr[i] + ln - 1; p++)
W[w + cindex[p]] = mark;
var jlast = i;
// compare i with all j
for (j = W[next + i]; j != -1; ) {
var ok = W[len + j] === ln && W[elen + j] === eln;
for (p = cptr[j] + 1; ok && p <= cptr[j] + ln - 1; p++) {
// compare i and j
if (W[w + cindex[p]] != mark)
ok = 0;
}
// check i and j are identical
if (ok) {
// absorb j into i
cptr[j] = cs_flip(i);
W[nv + i] += W[nv + j];
W[nv + j] = 0;
// node j is dead
W[elen + j] = -1;
// delete j from hash bucket
j = W[next + j];
W[next + jlast] = j;
}
else {
// j and i are different
jlast = j;
j = W[next + j];
}
}
}
}
// Finalize new element. The elimination of node k is nearly complete. All nodes i in Lk are scanned one last time.
// Node i is removed from Lk if it is dead. The flagged status of nv[i] is cleared.
for (p = pk1, pk = pk1; pk < pk2; pk++) {
i = cindex[pk];
// check i is dead, skip it
if ((nvi = -W[nv + i]) <= 0)
continue;
// restore W[nv + i]
W[nv + i] = nvi;
// compute external degree(i)
d = W[degree + i] + dk - nvi;
d = Math.min(d, n - nel - nvi);
if (W[head + d] != -1)
last[W[head + d]] = i;
// put i back in degree list
W[next + i] = W[head + d];
last[i] = -1;
W[head + d] = i;
// find new minimum degree
mindeg = Math.min(mindeg, d);
W[degree + i] = d;
// place i in Lk
cindex[p++] = i;
}
// # nodes absorbed into k
W[nv + k] = nvk;
// length of adj list of element k
if ((W[len + k] = p - pk1) === 0) {
// k is a root of the tree
cptr[k] = -1;
// k is now a dead element
W[w + k] = 0;
}
if (elenk !== 0) {
// free unused space in Lk
cnz = p;
}
}
// Postordering. The elimination is complete, but no permutation has been computed. All that is left
// of the graph is the assembly tree (ptr) and a set of dead nodes and elements (i is a dead node if
// nv[i] is zero and a dead element if nv[i] > 0). It is from this information only that the final permutation
// is computed. The tree is restored by unflipping all of ptr.
// fix assembly tree
for (i = 0; i < n; i++)
cptr[i] = cs_flip(cptr[i]);
for (j = 0; j <= n; j++)
W[head + j] = -1;
// place unordered nodes in lists
for (j = n; j >= 0; j--) {
// skip if j is an element
if (W[nv + j] > 0)
continue;
// place j in list of its parent
W[next + j] = W[head + cptr[j]];
W[head + cptr[j]] = j;
}
// place elements in lists
for (e = n; e >= 0; e--) {
// skip unless e is an element
if (W[nv + e] <= 0)
continue;
if (cptr[e] != -1) {
// place e in list of its parent
W[next + e] = W[head + cptr[e]];
W[head + cptr[e]] = e;
}
}
// postorder the assembly tree
for (k = 0, i = 0; i <= n; i++) {
if (cptr[i] == -1)
k = cs_tdfs(i, k, W, head, next, P, w);
}
// remove last item in array
P.splice(P.length - 1, 1);
// return P
return P;
};
/**
* Creates the matrix that will be used by the approximate minimum degree ordering algorithm. The function accepts the matrix M as input and returns a permutation
* vector P. The amd algorithm operates on a symmetrix matrix, so one of three symmetric matrices is formed.
*
* Order: 0
* A natural ordering P=null matrix is returned.
*
* Order: 1
* Matrix must be square. This is appropriate for a Cholesky or LU factorization.
* P = M + M'
*
* Order: 2
* Dense columns from M' are dropped, M recreated from M'. This is appropriatefor LU factorization of unsymmetric matrices.
* P = M' * M
*
* Order: 3
* This is best used for QR factorization or LU factorization is matrix M has no dense rows. A dense row is a row with more than 10*sqr(columns) entries.
* P = M' * M
*/
var _createTargetMatrix = function (order, a, m, n, dense) {
// compute A'
var at = transpose(a);
// check order = 1, matrix must be square
if (order === 1 && n === m) {
// C = A + A'
return add(a, at);
}
// check order = 2, drop dense columns from M'
if (order == 2) {
// transpose arrays
var tindex = at._index;
var tptr = at._ptr;
// new column index
var p2 = 0;
// loop A' columns (rows)
for (var j = 0; j < m; j++) {
// column j of AT starts here
var p = tptr[j];
// new column j starts here
tptr[j] = p2;
// skip dense col j
if (tptr[j + 1] - p > dense)
continue;
// map rows in column j of A
for (var p1 = tptr[j + 1]; p < p1; p++)
tindex[p2++] = tindex[p];
}
// finalize AT
tptr[m] = p2;
// recreate A from new transpose matrix
a = transpose(at);
// use A' * A
return multiply(at, a);
}
// use A' * A, square or rectangular matrix
return multiply(at, a);
};
/**
* Initialize quotient graph. There are four kind of nodes and elements that must be represented:
*
* - A live node is a node i (or a supernode) that has not been selected as a pivot nad has not been merged into another supernode.
* - A dead node i is one that has been removed from the graph, having been absorved into r = flip(ptr[i]).
* - A live element e is one that is in the graph, having been formed when node e was selected as the pivot.
* - A dead element e is one that has benn absorved into a subsequent element s = flip(ptr[e]).
*/
var _initializeQuotientGraph = function (n, cptr, W, len, head, last, next, hhead, nv, w, elen, degree) {
// Initialize quotient graph
for (var k = 0; k < n; k++)
W[len + k] = cptr[k + 1] - cptr[k];
W[len + n] = 0;
// initialize workspace
for (var i = 0; i <= n; i++) {
// degree list i is empty
W[head + i] = -1;
last[i] = -1;
W[next + i] = -1;
// hash list i is empty
W[hhead + i] = -1;
// node i is just one node
W[nv + i] = 1;
// node i is alive
W[w + i] = 1;
// Ek of node i is empty
W[elen + i] = 0;
// degree of node i
W[degree + i] = W[len + i];
}
// clear w
var mark = _wclear(0, 0, W, w, n);
// n is a dead element
W[elen + n] = -2;
// n is a root of assembly tree
cptr[n] = -1;
// n is a dead element
W[w + n] = 0;
// return mark
return mark;
};
/**
* Initialize degree lists. Each node is placed in its degree lists. Nodes of zero degree are eliminated immediately. Nodes with
* degree >= dense are alsol eliminated and merged into a placeholder node n, a dead element. Thes nodes will appera last in the
* output permutation p.
*/
var _initializeDegreeLists = function (n, cptr, W, degree, elen, w, dense, nv, head, last, next) {
// result
var nel = 0;
// loop columns
for (var i = 0; i < n; i++) {
// degree @ i
var d = W[degree + i];
// check node i is empty
if (d === 0) {
// element i is dead
W[elen + i] = -2;
nel++;
// i is a root of assembly tree
cptr[i] = -1;
W[w + i] = 0;
}
else if (d > dense) {
// absorb i into element n
W[nv + i] = 0;
// node i is dead
W[elen + i] = -1;
nel++;
cptr[i] = cs_flip(n);
W[nv + n]++;
}
else {
var h = W[head + d];
if (h != -1)
last[h] = i;
// put node i in degree list d
W[next + i] = W[head + d];
W[head + d] = i;
}
}
return nel;
};
var _wclear = function(mark, lemax, W, w, n) {
if (mark < 2 || (mark + lemax < 0)) {
for (var k = 0; k < n; k++) {
if (W[w + k] !== 0)
W[w + k] = 1;
}
mark = 2 ;
}
// at this point, W [0..n-1] < mark holds
return mark;
};
var _diag = function (i, j) {
return i != j;
};
return cs_amd;
}
exports.name = 'cs_amd';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,155 @@
'use strict';
function factory (type, config, load) {
var divideScalar = load(require('../../arithmetic/divideScalar'));
var sqrt = load(require('../../arithmetic/sqrt'));
var subtract = load(require('../../arithmetic/subtract'));
var multiply = load(require('../../arithmetic/multiply'));
var im = load(require('../../complex/im'));
var re = load(require('../../complex/re'));
var conj = load(require('../../complex/conj'));
var equal = load(require('../../relational/equal'));
var smallerEq = load(require('../../relational/smallerEq'));
var cs_symperm = load(require('./cs_symperm'));
var cs_ereach = load(require('./cs_ereach'));
var SparseMatrix = type.SparseMatrix;
/**
* Computes the Cholesky factorization of matrix A. It computes L and P so
* L * L' = P * A * P'
*
* @param {Matrix} m The A Matrix to factorize, only upper triangular part used
* @param {Object} s The symbolic analysis from cs_schol()
*
* @return {Number} The numeric Cholesky factorization of A or null
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_chol = function (m, s) {
// validate input
if (!m)
return null;
// m arrays
var size = m._size;
// columns
var n = size[1];
// symbolic analysis result
var parent = s.parent;
var cp = s.cp;
var pinv = s.pinv;
// L arrays
var lvalues = [];
var lindex = [];
var lptr = [];
// L
var L = new SparseMatrix({
values: lvalues,
index: lindex,
ptr: lptr,
size:[n, n]
});
// vars
var c = []; // (2 * n)
var x = []; // (n);
// compute C = P * A * P'
var cm = pinv ? cs_symperm (m, pinv, 1) : m;
// C matrix arrays
var cvalues = cm._values;
var cindex = cm._index;
var cptr = cm._ptr;
// vars
var k, p;
// initialize variables
for (k = 0; k < n; k++)
lptr[k] = c[k] = cp[k];
// compute L(k,:) for L*L' = C
for (k = 0; k < n; k++) {
// nonzero pattern of L(k,:)
var top = cs_ereach(cm, k, parent, c);
// x (0:k) is now zero
x[k] = 0;
// x = full(triu(C(:,k)))
for (p = cptr[k]; p < cptr[k+1]; p++) {
if (cindex[p] <= k)
x[cindex[p]] = cvalues[p];
}
// d = C(k,k)
var d = x[k];
// clear x for k+1st iteration
x[k] = 0;
// solve L(0:k-1,0:k-1) * x = C(:,k)
for (; top < n; top++) {
// s[top..n-1] is pattern of L(k,:)
var i = s[top];
// L(k,i) = x (i) / L(i,i)
var lki = divideScalar(x[i], lvalues[lptr[i]]);
// clear x for k+1st iteration
x[i] = 0;
for (p = lptr[i] + 1; p < c[i]; p++) {
// row
var r = lindex[p];
// update x[r]
x[r] = subtract(x[r], multiply(lvalues[p], lki));
}
// d = d - L(k,i)*L(k,i)
d = subtract(d, multiply(lki, conj(lki)));
p = c[i]++;
// store L(k,i) in column i
lindex[p] = k;
lvalues[p] = conj(lki);
}
// compute L(k,k)
if (smallerEq(re(d), 0) || !equal(im(d), 0)) {
// not pos def
return null;
}
p = c[k]++;
// store L(k,k) = sqrt(d) in column k
lindex[p] = k;
lvalues[p] = sqrt(d);
}
// finalize L
lptr[n] = cp[n];
// P matrix
var P;
// check we need to calculate P
if (pinv) {
// P arrays
var pvalues = [];
var pindex = [];
var pptr = [];
// create P matrix
for (p = 0; p < n; p++) {
// initialize ptr (one value per column)
pptr[p] = p;
// index (apply permutation vector)
pindex.push(pinv[p]);
// value 1
pvalues.push(1);
}
// update ptr
pptr[n] = n;
// P
P = new SparseMatrix({
values: pvalues,
index: pindex,
ptr: pptr,
size: [n, n]
});
}
// return L & P
return {
L: L,
P: P
};
};
return cs_chol;
}
exports.name = 'cs_chol';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,121 @@
'use strict';
function factory (type, config, load) {
var transpose = load(require('../../matrix/transpose'));
var cs_leaf = load(require('./cs_leaf'));
/**
* Computes the column counts using the upper triangular part of A.
* It transposes A internally, none of the input parameters are modified.
*
* @param {Matrix} a The sparse matrix A
*
* @param {Matrix} ata Count the columns of A'A instead
*
* @return An array of size n of the column counts or null on error
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_counts = function (a, parent, post, ata) {
// check inputs
if (!a || !parent || !post)
return null;
// a matrix arrays
var asize = a._size;
// rows and columns
var m = asize[0];
var n = asize[1];
// variables
var i, j, k, J, p, p0, p1;
// workspace size
var s = 4 * n + (ata ? (n + m + 1) : 0);
// allocate workspace
var w = []; // (s)
var ancestor = 0; // first n entries
var maxfirst = n; // next n entries
var prevleaf = 2 * n; // next n entries
var first = 3 * n; // next n entries
var head = 4 * n; // next n + 1 entries (used when ata is true)
var next = 5 * n + 1; // last entries in workspace
// clear workspace w[0..s-1]
for (k = 0; k < s; k++)
w[k] = -1;
// allocate result
var colcount = []; // (n);
// AT = A'
var at = transpose(a);
// at arrays
var tindex = at._index;
var tptr = at._ptr;
// find w[first + j]
for (k = 0; k < n; k++) {
j = post[k];
// colcount[j]=1 if j is a leaf
colcount[j] = (w[first + j] == -1) ? 1 : 0;
for (; j != -1 && w[first + j] == -1; j = parent[j])
w[first + j] = k;
}
// initialize ata if needed
if (ata) {
// invert post
for (k = 0; k < n; k++)
w[post[k]] = k;
// loop rows (columns in AT)
for (i = 0; i < m; i++) {
// values in column i of AT
for (k = n, p0 = tptr[i], p1 = tptr[i + 1], p = p0; p < p1; p++)
k = Math.min(k, w[tindex[p]]);
// place row i in linked list k
w[next + i] = w[head + k];
w[head + k] = i;
}
}
// each node in its own set
for (i = 0; i < n; i++)
w[ancestor + i] = i;
for (k = 0; k < n; k++) {
// j is the kth node in postordered etree
j = post[k];
// check j is not a root
if (parent[j] != -1)
colcount[parent[j]]--;
// J=j for LL'=A case
for (J = (ata ? w[head + k] : j); J != -1; J = (ata ? w[next + J] : -1)) {
for (p = tptr[J]; p < tptr[J+1]; p++) {
i = tindex[p];
var r = cs_leaf(i, j, w, first, maxfirst, prevleaf, ancestor);
// check A(i,j) is in skeleton
if (r.jleaf >= 1)
colcount[j]++;
// check account for overlap in q
if (r.jleaf == 2)
colcount[r.q]--;
}
}
if (parent[j] != -1)
w[ancestor + j] = parent[j];
}
// sum up colcount's of each child
for (j = 0; j < n; j++) {
if (parent[j] != -1)
colcount[parent[j]] += colcount[j];
}
return colcount;
};
return cs_counts;
}
exports.name = 'cs_counts';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,38 @@
'use strict';
function factory () {
/**
* It sets the p[i] equal to the sum of c[0] through c[i-1].
*
* @param {Array} ptr The Sparse Matrix ptr array
* @param {Array} c The Sparse Matrix ptr array
* @param {Number} n The number of columns
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_cumsum = function (ptr, c, n) {
// variables
var i;
var nz = 0;
for (i = 0; i < n; i++) {
// initialize ptr @ i
ptr[i] = nz;
// increment number of nonzeros
nz += c[i];
// also copy p[0..n-1] back into c[0..n-1]
c[i] = ptr[i];
}
// finalize ptr
ptr[n] = nz;
// return sum (c [0..n-1])
return nz;
};
return cs_cumsum;
}
exports.name = 'cs_cumsum';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,85 @@
'use strict';
function factory (type, config, load) {
var cs_marked = load(require('./cs_marked'));
var cs_mark = load(require('./cs_mark'));
var cs_unflip = load(require('./cs_unflip'));
/**
* Depth-first search computes the nonzero pattern xi of the directed graph G (Matrix) starting
* at nodes in B (see cs_reach()).
*
* @param {Number} j The starting node for the DFS algorithm
* @param {Matrix} g The G matrix to search, ptr array modified, then restored
* @param {Number} top Start index in stack xi[top..n-1]
* @param {Number} k The kth column in B
* @param {Array} xi The nonzero pattern xi[top] .. xi[n - 1], an array of size = 2 * n
* The first n entries is the nonzero pattern, the last n entries is the stack
* @param {Array} pinv The inverse row permutation vector, must be null for L * x = b
*
* @return {Number} New value of top
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_dfs = function (j, g, top, xi, pinv) {
// g arrays
var index = g._index;
var ptr = g._ptr;
var size = g._size;
// columns
var n = size[1];
// vars
var i, p, p2;
// initialize head
var head = 0;
// initialize the recursion stack
xi[0] = j;
// loop
while (head >= 0) {
// get j from the top of the recursion stack
j = xi[head];
// apply permutation vector
var jnew = pinv ? pinv[j] : j;
// check node j is marked
if (!cs_marked(ptr, j)) {
// mark node j as visited
cs_mark(ptr, j);
// update stack (last n entries in xi)
xi[n + head] = jnew < 0 ? 0 : cs_unflip(ptr[jnew]);
}
// node j done if no unvisited neighbors
var done = 1;
// examine all neighbors of j, stack (last n entries in xi)
for (p = xi[n + head], p2 = jnew < 0 ? 0 : cs_unflip(ptr[jnew+1]); p < p2; p++) {
// consider neighbor node i
i = index[p];
// check we have visited node i, skip it
if (cs_marked(ptr, i))
continue;
// pause depth-first search of node j, update stack (last n entries in xi)
xi[n + head] = p;
// start dfs at node i
xi[++head] = i;
// node j is not done
done = 0;
// break, to start dfs(i)
break;
}
// check depth-first search at node j is done
if (done) {
// remove j from the recursion stack
head--;
// and place in the output stack
xi[--top] = j;
}
}
return top;
};
return cs_dfs;
}
exports.name = 'cs_dfs';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,72 @@
'use strict';
function factory (type, config, load) {
var cs_marked = load(require('./cs_marked'));
var cs_mark = load(require('./cs_mark'));
/**
* Find nonzero pattern of Cholesky L(k,1:k-1) using etree and triu(A(:,k))
*
* @param {Matrix} a The A matrix
* @param {Number} k The kth column in A
* @param {Array} parent The parent vector from the symbolic analysis result
* @param {Array} w The nonzero pattern xi[top] .. xi[n - 1], an array of size = 2 * n
* The first n entries is the nonzero pattern, the last n entries is the stack
*
* @return {Number} The index for the nonzero pattern
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_ereach = function (a, k, parent, w) {
// a arrays
var aindex = a._index;
var aptr = a._ptr;
var asize = a._size;
// columns
var n = asize[1];
// initialize top
var top = n;
// vars
var p, p0, p1, len;
// mark node k as visited
cs_mark(w, k);
// loop values & index for column k
for (p0 = aptr[k], p1 = aptr[k + 1], p = p0; p < p1; p++) {
// A(i,k) is nonzero
var i = aindex[p];
// only use upper triangular part of A
if (i > k)
continue;
// traverse up etree
for (len = 0; !cs_marked(w, i); i = parent[i]) {
// L(k,i) is nonzero, last n entries in w
w[n + len++] = i;
// mark i as visited
cs_mark(w, i);
}
while (len > 0) {
// decrement top & len
--top;
--len;
// push path onto stack, last n entries in w
w[n + top] = w[n + len];
}
}
// unmark all nodes
for (p = top; p < n; p++) {
// use stack value, last n entries in w
cs_mark(w, w[n + p]);
}
// unmark node k
cs_mark(w, k);
// s[top..n-1] contains pattern of L(k,:)
return top;
};
return cs_ereach;
}
exports.name = 'cs_ereach';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,76 @@
'use strict';
function factory () {
/**
* Computes the elimination tree of Matrix A (using triu(A)) or the
* elimination tree of A'A without forming A'A.
*
* @param {Matrix} a The A Matrix
* @param {boolean} ata A value of true the function computes the etree of A'A
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_etree = function (a, ata) {
// check inputs
if (!a)
return null;
// a arrays
var aindex = a._index;
var aptr = a._ptr;
var asize = a._size;
// rows & columns
var m = asize[0];
var n = asize[1];
// allocate result
var parent = []; // (n)
// allocate workspace
var w = []; // (n + (ata ? m : 0))
var ancestor = 0; // first n entries in w
var prev = n; // last m entries (ata = true)
var i, inext;
// check we are calculating A'A
if (ata) {
// initialize workspace
for (i = 0; i < m; i++)
w[prev + i] = -1;
}
// loop columns
for (var k = 0; k < n; k++) {
// node k has no parent yet
parent[k] = -1;
// nor does k have an ancestor
w[ancestor + k] = -1;
// values in column k
for (var p0 = aptr[k], p1 = aptr[k + 1], p = p0; p < p1; p++) {
// row
var r = aindex[p];
// node
i = ata ? (w[prev + r]) : r;
// traverse from i to k
for (; i != -1 && i < k; i = inext) {
// inext = ancestor of i
inext = w[ancestor + i];
// path compression
w[ancestor + i] = k;
// check no anc., parent is k
if (inext == -1)
parent[i] = k;
}
if (ata)
w[prev + r] = k;
}
}
return parent;
};
return cs_etree;
}
exports.name = 'cs_etree';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,65 @@
'use strict';
function factory () {
/**
* Keeps entries in the matrix when the callback function returns true, removes the entry otherwise
*
* @param {Matrix} a The sparse matrix
* @param {function} callback The callback function, function will be invoked with the following args:
* - The entry row
* - The entry column
* - The entry value
* - The state parameter
* @param {any} other The state
*
* @return The number of nonzero elements in the matrix
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_fkeep = function (a, callback, other) {
// a arrays
var avalues = a._values;
var aindex = a._index;
var aptr = a._ptr;
var asize = a._size;
// columns
var n = asize[1];
// nonzero items
var nz = 0;
// loop columns
for (var j = 0; j < n; j++) {
// get current location of col j
var p = aptr[j];
// record new location of col j
aptr[j] = nz;
for (; p < aptr[j+1]; p++) {
// check we need to keep this item
if (callback(aindex[p], j, avalues ? avalues[p] : 1, other)) {
// keep A(i,j)
aindex[nz] = aindex[p];
// check we need to process values (pattern only)
if (avalues)
avalues[nz] = avalues[p];
// increment nonzero items
nz++;
}
}
}
// finalize A
aptr[n] = nz;
// trim arrays
aindex.splice(nz, aindex.length - nz);
// check we need to process values (pattern only)
if (avalues)
avalues.splice(nz, avalues.length - nz);
// return number of nonzero items
return (nz);
};
return cs_fkeep;
}
exports.name = 'cs_fkeep';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,22 @@
'use strict';
function factory () {
/**
* This function "flips" its input about the integer -1.
*
* @param {Number} i The value to flip
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_flip = function (i) {
// flip the value
return -i - 2;
};
return cs_flip;
}
exports.name = 'cs_flip';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,41 @@
'use strict';
function factory () {
/**
* Permutes a vector; x = P'b. In MATLAB notation, x(p)=b.
*
* @param {Array} p The permutation vector of length n. null value denotes identity
* @param {Array} b The input vector
*
* @return {Array} The output vector x = P'b
*/
var cs_ipvec = function (p, b, n) {
// vars
var k;
var n = b.length;
var x = [];
// check permutation vector was provided, p = null denotes identity
if (p) {
// loop vector
for (k = 0; k < n; k++) {
// apply permutation
x[p[k]] = b[k];
}
}
else {
// loop vector
for (k = 0; k < n; k++) {
// x[i] = b[i]
x[k] = b[k];
}
}
return x;
};
return cs_ipvec;
}
exports.name = 'cs_ipvec';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,66 @@
'use strict';
function factory () {
/**
* This function determines if j is a leaf of the ith row subtree.
* Consider A(i,j), node j in ith row subtree and return lca(jprev,j)
*
* @param {Number} i The ith row subtree
* @param {Number} j The node to test
* @param {Array} w The workspace array
* @param {Number} first The index offset within the workspace for the first array
* @param {Number} maxfirst The index offset within the workspace for the maxfirst array
* @param {Number} prevleaf The index offset within the workspace for the prevleaf array
* @param {Number} ancestor The index offset within the workspace for the ancestor array
*
* @return {Object}
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_leaf = function (i, j, w, first, maxfirst, prevleaf, ancestor) {
var s, sparent, jprev;
// our result
var jleaf = 0;
var q;
// check j is a leaf
if (i <= j || w[first + j] <= w[maxfirst + i])
return (-1);
// update max first[j] seen so far
w[maxfirst + i] = w[first + j];
// jprev = previous leaf of ith subtree
jprev = w[prevleaf + i];
w[prevleaf + i] = j;
// check j is first or subsequent leaf
if (jprev === -1) {
// 1st leaf, q = root of ith subtree
jleaf = 1;
q = i;
}
else {
// update jleaf
jleaf = 2;
// q = least common ancester (jprev,j)
for (q = jprev; q != w[ancestor + q]; q = w[ancestor + q]);
for (s = jprev; s != q; s = sparent) {
// path compression
sparent = w[ancestor + s];
w[ancestor + s] = q;
}
}
return {
jleaf: jleaf,
q: q
};
};
return cs_leaf;
}
exports.name = 'cs_leaf';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,178 @@
'use strict';
function factory (type, config, load) {
var abs = load(require('../../arithmetic/abs'));
var divideScalar = load(require('../../arithmetic/divideScalar'));
var multiply = load(require('../../arithmetic/multiply'));
var larger = load(require('../../relational/larger'));
var largerEq = load(require('../../relational/largerEq'));
var cs_spsolve = load(require('./cs_spsolve'));
var SparseMatrix = type.SparseMatrix;
/**
* Computes the numeric LU factorization of the sparse matrix A. Implements a Left-looking LU factorization
* algorithm that computes L and U one column at a tume. At the kth step, it access columns 1 to k-1 of L
* and column k of A. Given the fill-reducing column ordering q (see parameter s) computes L, U and pinv so
* L * U = A(p, q), where p is the inverse of pinv.
*
* @param {Matrix} m The A Matrix to factorize
* @param {Object} s The symbolic analysis from cs_sqr(). Provides the fill-reducing
* column ordering q
* @param {Number} tol Partial pivoting threshold (1 for partial pivoting)
*
* @return {Number} The numeric LU factorization of A or null
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_lu = function (m, s, tol) {
// validate input
if (!m)
return null;
// m arrays
var size = m._size;
// columns
var n = size[1];
// symbolic analysis result
var q;
var lnz = 100;
var unz = 100;
// update symbolic analysis parameters
if (s) {
q = s.q;
lnz = s.lnz || lnz;
unz = s.unz || unz;
}
// L arrays
var lvalues = []; // (lnz)
var lindex = []; // (lnz);
var lptr = []; // (n + 1);
// L
var L = new SparseMatrix({
values: lvalues,
index: lindex,
ptr: lptr,
size: [n, n]
});
// U arrays
var uvalues = []; // (unz);
var uindex = []; // (unz);
var uptr = []; // (n + 1);
// U
var U = new SparseMatrix({
values: uvalues,
index: uindex,
ptr: uptr,
size: [n, n]
});
// inverse of permutation vector
var pinv = []; // (n);
// vars
var i, p;
// allocate arrays
var x = []; // (n);
var xi = []; // (2 * n);
// initialize variables
for (i = 0; i < n; i++) {
// clear workspace
x[i] = 0;
// no rows pivotal yet
pinv[i] = -1;
// no cols of L yet
lptr[i + 1] = 0;
}
// reset number of nonzero elements in L and U
lnz = 0;
unz = 0;
// compute L(:,k) and U(:,k)
for (var k = 0; k < n; k++) {
// update ptr
lptr[k] = lnz;
uptr[k] = unz;
// apply column permutations if needed
var col = q ? q[k] : k;
// solve triangular system, x = L\A(:,col)
var top = cs_spsolve(L, m, col, xi, x, pinv, 1);
// find pivot
var ipiv = -1;
var a = -1;
// loop xi[] from top -> n
for (p = top; p < n; p++) {
// x[i] is nonzero
i = xi[p];
// check row i is not yet pivotal
if (pinv[i] < 0) {
// absolute value of x[i]
var xabs = abs(x[i]);
// check absoulte value is greater than pivot value
if (larger(xabs, a)) {
// largest pivot candidate so far
a = xabs;
ipiv = i;
}
}
else {
// x(i) is the entry U(pinv[i],k)
uindex[unz] = pinv[i];
uvalues[unz++] = x[i];
}
}
// validate we found a valid pivot
if (ipiv == -1 || a <= 0)
return null;
// update actual pivot column, give preference to diagonal value
if (pinv[col] < 0 && largerEq(abs(x[col]), multiply(a, tol)))
ipiv = col;
// the chosen pivot
var pivot = x[ipiv];
// last entry in U(:,k) is U(k,k)
uindex[unz] = k;
uvalues[unz++] = pivot;
// ipiv is the kth pivot row
pinv[ipiv] = k;
// first entry in L(:,k) is L(k,k) = 1
lindex[lnz] = ipiv;
lvalues[lnz++] = 1;
// L(k+1:n,k) = x / pivot
for (p = top; p < n; p++) {
// row
i = xi[p];
// check x(i) is an entry in L(:,k)
if (pinv[i] < 0) {
// save unpermuted row in L
lindex[lnz] = i;
// scale pivot column
lvalues[lnz++] = divideScalar(x[i], pivot);
}
// x[0..n-1] = 0 for next k
x[i] = 0;
}
}
// update ptr
lptr[n] = lnz;
uptr[n] = unz;
// fix row indices of L for final pinv
for (p = 0; p < lnz; p++)
lindex[p] = pinv[lindex[p]];
// trim arrays
lvalues.splice(lnz, lvalues.length - lnz);
lindex.splice(lnz, lindex.length - lnz);
uvalues.splice(unz, uvalues.length - unz);
uindex.splice(unz, uindex.length - unz);
// return LU factor
return {
L: L,
U: U,
pinv: pinv
};
};
return cs_lu;
}
exports.name = 'cs_lu';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,25 @@
'use strict';
function factory (type, config, load) {
var cs_flip = load(require('./cs_flip'));
/**
* Marks the node at w[j]
*
* @param {Array} w The array
* @param {Number} j The array index
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_mark = function (w, j) {
// mark w[j]
w[j] = cs_flip(w [j]);
};
return cs_mark;
}
exports.name = 'cs_mark';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,23 @@
'use strict';
function factory () {
/**
* Checks if the node at w[j] is marked
*
* @param {Array} w The array
* @param {Number} j The array index
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_marked = function (w, j) {
// check node is marked
return w[j] < 0;
};
return cs_marked;
}
exports.name = 'cs_marked';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,71 @@
'use strict';
function factory (type) {
var SparseMatrix = type.SparseMatrix;
/**
* Permutes a sparse matrix C = P * A * Q
*
* @param {Matrix} a The Matrix A
* @param {Array} pinv The row permutation vector
* @param {Array} q The column permutation vector
* @param {boolean} values Create a pattern matrix (false), values and pattern otherwise
*
* @return {Matrix} C = P * A * Q, null on error
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_permute = function (a, pinv, q, values) {
// a arrays
var avalues = a._values;
var aindex = a._index;
var aptr = a._ptr;
var asize = a._size;
var adt = a._datatype;
// rows & columns
var m = asize[0];
var n = asize[1];
// c arrays
var cvalues = values && a._values ? [] : null;
var cindex = []; // (aptr[n]);
var cptr = []; // (n + 1);
// initialize vars
var nz = 0;
// loop columns
for (var k = 0; k < n; k++) {
// column k of C is column q[k] of A
cptr[k] = nz;
// apply column permutation
var j = q ? (q[k]) : k;
// loop values in column j of A
for (var t0 = aptr[j], t1 = aptr[j + 1], t = t0; t < t1; t++) {
// row i of A is row pinv[i] of C
var r = pinv ? pinv[aindex[t]] : aindex[t];
// index
cindex[nz] = r;
// check we need to populate values
if (cvalues)
cvalues[nz] = avalues[t];
// increment number of nonzero elements
nz++;
}
}
// finalize the last column of C
cptr[n] = nz;
// return C matrix
return new SparseMatrix({
values: cvalues,
index: cindex,
ptr: cptr,
size: [m, n],
datatype: adt
});
};
return cs_permute;
}
exports.name = 'cs_permute';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,59 @@
'use strict';
function factory (type, config, load) {
var cs_tdfs = load(require('./cs_tdfs'));
/**
* Post order a tree of forest
*
* @param {Array} parent The tree or forest
* @param {Number} n Number of columns
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_post = function (parent, n) {
// check inputs
if (!parent)
return null;
// vars
var k = 0;
var j;
// allocate result
var post = []; // (n);
// workspace, head: first n entries, next: next n entries, stack: last n entries
var w = []; // (3 * n);
var head = 0;
var next = n;
var stack = 2 * n;
// initialize workspace
for (j = 0; j < n; j++) {
// empty linked lists
w[head + j] = -1;
}
// traverse nodes in reverse order
for (j = n-1; j >= 0; j--) {
// check j is a root
if (parent[j] == -1)
continue;
// add j to list of its parent
w[next + j] = w[head + parent[j]];
w[head + parent[j]] = j;
}
// loop nodes
for (j = 0; j < n; j++) {
// skip j if it is not a root
if (parent[j] != -1)
continue;
// depth-first search
k = cs_tdfs(j, k, w, head, next, post, stack);
}
return post;
};
return cs_post;
}
exports.name = 'cs_post';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,61 @@
'use strict';
function factory (type, config, load) {
var cs_dfs = load(require('./cs_dfs'));
var cs_marked = load(require('./cs_marked'));
var cs_mark = load(require('./cs_mark'));
/**
* The cs_reach function computes X = Reach(B), where B is the nonzero pattern of the n-by-1
* sparse column of vector b. The function returns the set of nodes reachable from any node in B. The
* nonzero pattern xi of the solution x to the sparse linear system Lx=b is given by X=Reach(B).
*
* @param {Matrix} g The G matrix
* @param {Matrix} b The B matrix
* @param {Number} k The kth column in B
* @param {Array} xi The nonzero pattern xi[top] .. xi[n - 1], an array of size = 2 * n
* The first n entries is the nonzero pattern, the last n entries is the stack
* @param {Array} pinv The inverse row permutation vector
*
* @return {Number} The index for the nonzero pattern
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_reach = function (g, b, k, xi, pinv) {
// g arrays
var gptr = g._ptr;
var gsize = g._size;
// b arrays
var bindex = b._index;
var bptr = b._ptr;
// columns
var n = gsize[1];
// vars
var p, p0, p1;
// initialize top
var top = n;
// loop column indeces in B
for (p0 = bptr[k], p1 = bptr[k + 1], p = p0; p < p1; p++) {
// node i
var i = bindex[p];
// check node i is marked
if (!cs_marked(gptr, i)) {
// start a dfs at unmarked node i
top = cs_dfs(i, g, top, xi, pinv);
}
}
// loop columns from top -> n - 1
for (p = top; p < n; p++) {
// restore G
cs_mark(gptr, xi[p]);
}
return top;
};
return cs_reach;
}
exports.name = 'cs_reach';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,86 @@
'use strict';
function factory (type, config, load) {
var divideScalar = load(require('../../arithmetic/divideScalar'));
var multiply = load(require('../../arithmetic/multiply'));
var subtract = load(require('../../arithmetic/subtract'));
var cs_reach = load(require('./cs_reach'));
/**
* The function cs_spsolve() computes the solution to G * x = bk, where bk is the
* kth column of B. When lo is true, the function assumes G = L is lower triangular with the
* diagonal entry as the first entry in each column. When lo is true, the function assumes G = U
* is upper triangular with the diagonal entry as the last entry in each column.
*
* @param {Matrix} g The G matrix
* @param {Matrix} b The B matrix
* @param {Number} k The kth column in B
* @param {Array} xi The nonzero pattern xi[top] .. xi[n - 1], an array of size = 2 * n
* The first n entries is the nonzero pattern, the last n entries is the stack
* @param {Array} x The soluton to the linear system G * x = b
* @param {Array} pinv The inverse row permutation vector, must be null for L * x = b
* @param {boolean} lo The lower (true) upper triangular (false) flag
*
* @return {Number} The index for the nonzero pattern
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_spsolve = function (g, b, k, xi, x, pinv, lo) {
// g arrays
var gvalues = g._values;
var gindex = g._index;
var gptr = g._ptr;
var gsize = g._size;
// columns
var n = gsize[1];
// b arrays
var bvalues = b._values;
var bindex = b._index;
var bptr = b._ptr;
// vars
var p, p0, p1, q;
// xi[top..n-1] = cs_reach(B(:,k))
var top = cs_reach(g, b, k, xi, pinv);
// clear x
for (p = top; p < n; p++)
x[xi[p]] = 0;
// scatter b
for (p0 = bptr[k], p1 = bptr[k + 1], p = p0; p < p1; p++)
x[bindex[p]] = bvalues[p];
// loop columns
for (var px = top; px < n; px++) {
// x array index for px
var j = xi[px];
// apply permutation vector (U x = b), j maps to column J of G
var J = pinv ? pinv[j] : j;
// check column J is empty
if (J < 0)
continue;
// column value indeces in G, p0 <= p < p1
p0 = gptr[J];
p1 = gptr[J + 1];
// x(j) /= G(j,j)
x[j] = divideScalar(x[j], gvalues[lo ? p0 : (p1 - 1)]);
// first entry L(j,j)
p = lo ? (p0 + 1) : p0;
q = lo ? (p1) : (p1 - 1);
// loop
for ( ; p < q ; p++) {
// row
var i = gindex[p];
// x(i) -= G(i,j) * x(j)
x[i] = subtract(x[i], multiply(gvalues[p], x[j]));
}
}
// return top of stack
return top;
};
return cs_spsolve;
}
exports.name = 'cs_spsolve';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,163 @@
'use strict';
function factory (type, config, load) {
var cs_amd = load(require('./cs_amd'));
var cs_permute = load(require('./cs_permute'));
var cs_etree = load(require('./cs_etree'));
var cs_post = load(require('./cs_post'));
var cs_counts = load(require('./cs_counts'));
/**
* Symbolic ordering and analysis for QR and LU decompositions.
*
* @param {Number} order The ordering strategy (see cs_amd for more details)
* @param {Matrix} a The A matrix
* @param {boolean} qr Symbolic ordering and analysis for QR decomposition (true) or
* symbolic ordering and analysis for LU decomposition (false)
*
* @return {Object} The Symbolic ordering and analysis for matrix A
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_sqr = function (order, a, qr) {
// a arrays
var aptr = a._ptr;
var asize = a._size;
// columns
var n = asize[1];
// vars
var k;
// symbolic analysis result
var s = {};
// fill-reducing ordering
s.q = cs_amd(order, a);
// validate results
if (order && !s.q)
return null;
// QR symbolic analysis
if (qr) {
// apply permutations if needed
var c = order ? cs_permute(a, null, s.q, 0) : a;
// etree of C'*C, where C=A(:,q)
s.parent = cs_etree(c, 1);
// post order elimination tree
var post = cs_post (s.parent, n);
// col counts chol(C'*C)
s.cp = cs_counts(c, s.parent, post, 1);
// check we have everything needed to calculate number of nonzero elements
if (c && s.parent && s.cp && _vcount(c, s)) {
// calculate number of nonzero elements
for (s.unz = 0, k = 0; k < n; k++)
s.unz += s.cp[k];
}
}
else {
// for LU factorization only, guess nnz(L) and nnz(U)
s.unz = 4 * (aptr[n]) + n;
s.lnz = s.unz;
}
// return result S
return s;
};
/**
* Compute nnz(V) = s.lnz, s.pinv, s.leftmost, s.m2 from A and s.parent
*/
var _vcount = function (a, s) {
// a arrays
var aptr = a._ptr;
var aindex = a._index;
var asize = a._size;
// rows & columns
var m = asize[0];
var n = asize[1];
// initialize s arrays
s.pinv = []; // (m + n);
s.leftmost = []; // (m);
// vars
var parent = s.parent;
var pinv = s.pinv;
var leftmost = s.leftmost;
// workspace, next: first m entries, head: next n entries, tail: next n entries, nque: next n entries
var w = []; // (m + 3 * n);
var next = 0;
var head = m;
var tail = m + n;
var nque = m + 2 * n;
// vars
var i, k, p, p0, p1;
// initialize w
for (k = 0; k < n; k++) {
// queue k is empty
w[head + k] = -1;
w[tail + k] = -1;
w[nque + k] = 0;
}
// initialize row arrays
for (i = 0; i < m; i++)
leftmost[i] = -1;
// loop columns backwards
for (k = n - 1; k >= 0; k--) {
// values & index for column k
for (p0 = aptr[k], p1 = aptr[k + 1], p = p0; p < p1; p++) {
// leftmost[i] = min(find(A(i,:)))
leftmost[aindex[p]] = k;
}
}
// scan rows in reverse order
for (i = m - 1; i >= 0; i--) {
// row i is not yet ordered
pinv[i] = -1;
k = leftmost[i];
// check row i is empty
if (k == -1)
continue;
// first row in queue k
if (w[nque + k]++ === 0)
w[tail + k] = i;
// put i at head of queue k
w[next + i] = w[head + k];
w[head + k] = i;
}
s.lnz = 0;
s.m2 = m;
// find row permutation and nnz(V)
for (k = 0; k < n; k++) {
// remove row i from queue k
i = w[head + k];
// count V(k,k) as nonzero
s.lnz++;
// add a fictitious row
if (i < 0)
i = s.m2++;
// associate row i with V(:,k)
pinv[i] = k;
// skip if V(k+1:m,k) is empty
if (--nque[k] <= 0)
continue;
// nque[k] is nnz (V(k+1:m,k))
s.lnz += w[nque + k];
// move all rows to parent of k
var pa = parent[k];
if (pa != -1) {
if (w[nque + pa] === 0)
w[tail + pa] = w[tail + k];
w[next + w[tail + k]] = w[head + pa];
w[head + pa] = w[next + i];
w[nque + pa] += w[nque + k];
}
}
for (i = 0; i < m; i++) {
if (pinv[i] < 0)
pinv[i] = k++;
}
return true;
};
return cs_sqr;
}
exports.name = 'cs_sqr';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,95 @@
'use strict';
function factory (type, config, load) {
var cs_cumsum = load(require('./cs_cumsum'));
var conj = load(require('../../complex/conj'));
var SparseMatrix = type.SparseMatrix;
/**
* Computes the symmetric permutation of matrix A accessing only
* the upper triangular part of A.
*
* C = P * A * P'
*
* @param {Matrix} a The A matrix
* @param {Array} pinv The inverse of permutation vector
* @param {boolean} values Process matrix values (true)
*
* @return {Matrix} The C matrix, C = P * A * P'
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_symperm = function (a, pinv, values) {
// A matrix arrays
var avalues = a._values;
var aindex = a._index;
var aptr = a._ptr;
var asize = a._size;
// columns
var n = asize[1];
// C matrix arrays
var cvalues = values && avalues ? [] : null;
var cindex = []; // (nz);
var cptr = []; // (n + 1);
// variables
var i, i2, j, j2, p, p0, p1;
// create workspace vector
var w = []; // (n);
// count entries in each column of C
for (j = 0; j < n; j++) {
// column j of A is column j2 of C
j2 = pinv ? pinv[j] : j;
// loop values in column j
for (p0 = aptr[j], p1 = aptr[j + 1], p = p0; p < p1; p++) {
// row
i = aindex[p];
// skip lower triangular part of A
if (i > j)
continue;
// row i of A is row i2 of C
i2 = pinv ? pinv[i] : i;
// column count of C
w[Math.max(i2, j2)]++;
}
}
// compute column pointers of C
cs_cumsum(cptr, w, n);
// loop columns
for (j = 0; j < n; j++) {
// column j of A is column j2 of C
j2 = pinv ? pinv[j] : j;
// loop values in column j
for (p0 = aptr[j], p1 = aptr[j + 1], p = p0; p < p1; p++) {
// row
i = aindex[p];
// skip lower triangular part of A
if (i > j)
continue;
// row i of A is row i2 of C
i2 = pinv ? pinv[i] : i;
// C index for column j2
var q = w[Math.max(i2, j2)]++;
// update C index for entry q
cindex[q] = Math.min(i2, j2);
// check we need to process values
if (cvalues)
cvalues[q] = (i2 <= j2) ? avalues[p] : conj(avalues[p]);
}
}
// return C matrix
return new SparseMatrix({
values: cvalues,
index: cindex,
ptr: cptr,
size: [n, n]
});
};
return cs_symperm;
}
exports.name = 'cs_symperm';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,52 @@
'use strict';
function factory () {
/**
* Depth-first search and postorder of a tree rooted at node j
*
* @param {Number} j The tree node
* @param {Number} k
* @param {Array} w The workspace array
* @param {Number} head The index offset within the workspace for the head array
* @param {Number} next The index offset within the workspace for the next array
* @param {Array} post The post ordering array
* @param {Number} stack The index offset within the workspace for the stack array
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_tdfs = function (j, k, w, head, next, post, stack) {
// variables
var top = 0;
// place j on the stack
w[stack] = j;
// while (stack is not empty)
while (top >= 0) {
// p = top of stack
var p = w[stack + top];
// i = youngest child of p
var i = w[head + p];
if (i == -1) {
// p has no unordered children left
top--;
// node p is the kth postordered node
post[k++] = p;
}
else {
// remove i from children of p
w[head + p] = w[next + i];
// increment top
++top;
// start dfs on child node i
w[stack + top] = i;
}
}
return k;
};
return cs_tdfs;
}
exports.name = 'cs_tdfs';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,24 @@
'use strict';
function factory (type, config, load) {
var cs_flip = load(require('./cs_flip'));
/**
* Flips the value if it is negative of returns the same value otherwise.
*
* @param {Number} i The value to flip
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
var cs_unflip = function (i) {
// flip the value if it is negative
return i < 0 ? cs_flip(i) : i;
};
return cs_unflip;
}
exports.name = 'cs_unflip';
exports.path = 'sparse';
exports.factory = factory;

View File

@@ -0,0 +1,61 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
/**
* Calculate the absolute value of a number. For matrices, the function is
* evaluated element wise.
*
* Syntax:
*
* math.abs(x)
*
* Examples:
*
* math.abs(3.5); // returns number 3.5
* math.abs(-4.2); // returns number 4.2
*
* math.abs([3, -5, -1, 0, 2]); // returns Array [3, 5, 1, 0, 2]
*
* See also:
*
* sign
*
* @param {number | BigNumber | Fraction | Complex | Array | Matrix | Unit} x
* A number or matrix for which to get the absolute value
* @return {number | BigNumber | Fraction | Complex | Array | Matrix | Unit}
* Absolute value of `x`
*/
var abs = typed('abs', {
'number': Math.abs,
'Complex': function (x) {
return x.abs();
},
'BigNumber': function (x) {
return x.abs();
},
'Fraction': function (x) {
return x.abs();
},
'Array | Matrix': function (x) {
// deep map collection, skip zeros since abs(0) = 0
return deepMap(x, abs, true);
},
'Unit': function(x) {
return x.abs();
}
});
abs.toTex = {1: '\\left|${args[0]}\\right|'};
return abs;
}
exports.name = 'abs';
exports.factory = factory;

View File

@@ -0,0 +1,164 @@
'use strict';
var extend = require('../../utils/object').extend;
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
var addScalar = load(require('./addScalar'));
var latex = require('../../utils/latex.js');
var algorithm01 = load(require('../../type/matrix/utils/algorithm01'));
var algorithm04 = load(require('../../type/matrix/utils/algorithm04'));
var algorithm10 = load(require('../../type/matrix/utils/algorithm10'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Add two or more values, `x + y`.
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.add(x, y)
* math.add(x, y, z, ...)
*
* Examples:
*
* math.add(2, 3); // returns number 5
* math.add(2, 3, 4); // returns number 9
*
* var a = math.complex(2, 3);
* var b = math.complex(-4, 1);
* math.add(a, b); // returns Complex -2 + 4i
*
* math.add([1, 2, 3], 4); // returns Array [5, 6, 7]
*
* var c = math.unit('5 cm');
* var d = math.unit('2.1 mm');
* math.add(c, d); // returns Unit 52.1 mm
*
* math.add("2.3", "4"); // returns number 6.3
*
* See also:
*
* subtract, sum
*
* @param {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} x First value to add
* @param {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} y Second value to add
* @return {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} Sum of `x` and `y`
*/
var add = typed('add', extend({
// we extend the signatures of addScalar with signatures dealing with matrices
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse + sparse
c = algorithm04(x, y, addScalar);
break;
default:
// sparse + dense
c = algorithm01(y, x, addScalar, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense + sparse
c = algorithm01(x, y, addScalar, false);
break;
default:
// dense + dense
c = algorithm13(x, y, addScalar);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return add(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return add(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return add(x, matrix(y));
},
'Matrix, any': function (x, y) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm10(x, y, addScalar, false);
break;
default:
c = algorithm14(x, y, addScalar, false);
break;
}
return c;
},
'any, Matrix': function (x, y) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm10(y, x, addScalar, true);
break;
default:
c = algorithm14(y, x, addScalar, true);
break;
}
return c;
},
'Array, any': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, addScalar, false).valueOf();
},
'any, Array': function (x, y) {
// use matrix implementation
return algorithm14(matrix(y), x, addScalar, true).valueOf();
},
'any, any': addScalar,
'any, any, ...any': function (x, y, rest) {
var result = add(x, y);
for (var i = 0; i < rest.length; i++) {
result = add(result, rest[i]);
}
return result;
}
}, addScalar.signatures));
add.toTex = {
2: '\\left(${args[0]}' + latex.operators['add'] + '${args[1]}\\right)'
};
return add;
}
exports.name = 'add';
exports.factory = factory;

View File

@@ -0,0 +1,51 @@
'use strict';
function factory(type, config, load, typed) {
/**
* Add two scalar values, `x + y`.
* This function is meant for internal use: it is used by the public function
* `add`
*
* This function does not support collections (Array or Matrix), and does
* not validate the number of of inputs.
*
* @param {number | BigNumber | Fraction | Complex | Unit} x First value to add
* @param {number | BigNumber | Fraction | Complex} y Second value to add
* @return {number | BigNumber | Fraction | Complex | Unit} Sum of `x` and `y`
* @private
*/
var add = typed('add', {
'number, number': function (x, y) {
return x + y;
},
'Complex, Complex': function (x, y) {
return x.add(y);
},
'BigNumber, BigNumber': function (x, y) {
return x.plus(y);
},
'Fraction, Fraction': function (x, y) {
return x.add(y);
},
'Unit, Unit': function (x, y) {
if (x.value == null) throw new Error('Parameter x contains a unit with undefined value');
if (y.value == null) throw new Error('Parameter y contains a unit with undefined value');
if (!x.equalBase(y)) throw new Error('Units do not match');
var res = x.clone();
res.value = add(res.value, y.value);
res.fixPrefix = false;
return res;
}
});
return add;
}
exports.factory = factory;

View File

@@ -0,0 +1,185 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
var unaryMinus = load(require('./unaryMinus'));
var isNegative = load(require('../utils/isNegative'));
var matrix = load(require('../../type/matrix/function/matrix'));
/**
* Calculate the cubic root of a value.
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.cbrt(x)
* math.cbrt(x, allRoots)
*
* Examples:
*
* math.cbrt(27); // returns 3
* math.cube(3); // returns 27
* math.cbrt(-64); // returns -4
* math.cbrt(math.unit('27 m^3')); // returns Unit 3 m
* math.cbrt([27, 64, 125]); // returns [3, 4, 5]
*
* var x = math.complex('8i');
* math.cbrt(x); // returns Complex 1.7320508075689 + i
* math.cbrt(x, true); // returns Matrix [
* // 1.7320508075689 + i
* // -1.7320508075689 + i
* // -2i
* // ]
*
* See also:
*
* square, sqrt, cube
*
* @param {number | BigNumber | Complex | Unit | Array | Matrix} x
* Value for which to calculate the cubic root.
* @param {boolean} [allRoots] Optional, false by default. Only applicable
* when `x` is a number or complex number. If true, all complex
* roots are returned, if false (default) the principal root is
* returned.
* @return {number | BigNumber | Complex | Unit | Array | Matrix}
* Returns the cubic root of `x`
*/
var cbrt = typed('cbrt', {
'number': _cbrtNumber,
// note: signature 'number, boolean' is also supported,
// created by typed as it knows how to convert number to Complex
'Complex': _cbrtComplex,
'Complex, boolean': _cbrtComplex,
'BigNumber': function (x) {
return x.cbrt();
},
'Unit': _cbrtUnit,
'Array | Matrix': function (x) {
// deep map collection, skip zeros since cbrt(0) = 0
return deepMap(x, cbrt, true);
}
});
/**
* Calculate the cubic root for a complex number
* @param {Complex} x
* @param {boolean} [allRoots] If true, the function will return an array
* with all three roots. If false or undefined,
* the principal root is returned.
* @returns {Complex | Array.<Complex> | Matrix.<Complex>} Returns the cubic root(s) of x
* @private
*/
function _cbrtComplex(x, allRoots) {
// https://www.wikiwand.com/en/Cube_root#/Complex_numbers
var arg_3 = x.arg() / 3;
var abs = x.abs();
// principal root:
var principal = new type.Complex(_cbrtNumber(abs), 0).mul(
new type.Complex(0, arg_3).exp());
if (allRoots) {
var all = [
principal,
new type.Complex(_cbrtNumber(abs), 0).mul(
new type.Complex(0, arg_3 + Math.PI * 2 / 3).exp()),
new type.Complex(_cbrtNumber(abs), 0).mul(
new type.Complex(0, arg_3 - Math.PI * 2 / 3).exp())
];
return (config.matrix === 'Array') ? all : matrix(all);
}
else {
return principal;
}
}
/**
* Calculate the cubic root for a Unit
* @param {Unit} x
* @return {Unit} Returns the cubic root of x
* @private
*/
function _cbrtUnit(x) {
if(x.value && x.value.isComplex) {
var result = x.clone();
result.value = 1.0;
result = result.pow(1.0/3); // Compute the units
result.value = _cbrtComplex(x.value); // Compute the value
return result;
}
else {
var negate = isNegative(x.value);
if (negate) {
x.value = unaryMinus(x.value);
}
// TODO: create a helper function for this
var third;
if (x.value && x.value.isBigNumber) {
third = new type.BigNumber(1).div(3);
}
else if (x.value && x.value.isFraction) {
third = new type.Fraction(1, 3);
}
else {
third = 1/3;
}
var result = x.pow(third);
if (negate) {
result.value = unaryMinus(result.value);
}
return result;
}
}
cbrt.toTex = {1: '\\sqrt[3]{${args[0]}}'};
return cbrt;
}
/**
* Calculate cbrt for a number
*
* Code from es6-shim.js:
* https://github.com/paulmillr/es6-shim/blob/master/es6-shim.js#L1564-L1577
*
* @param {number} x
* @returns {number | Complex} Returns the cubic root of x
* @private
*/
var _cbrtNumber = Math.cbrt || function (x) {
if (x === 0) {
return x;
}
var negate = x < 0;
var result;
if (negate) {
x = -x;
}
if (isFinite(x)) {
result = Math.exp(Math.log(x) / 3);
// from http://en.wikipedia.org/wiki/Cube_root#Numerical_methods
result = (x / (result * result) + (2 * result)) / 3;
} else {
result = x;
}
return negate ? -result : result;
};
exports.name = 'cbrt';
exports.factory = factory;

View File

@@ -0,0 +1,61 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
/**
* Round a value towards plus infinity
* If `x` is complex, both real and imaginary part are rounded towards plus infinity.
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.ceil(x)
*
* Examples:
*
* math.ceil(3.2); // returns number 4
* math.ceil(3.8); // returns number 4
* math.ceil(-4.2); // returns number -4
* math.ceil(-4.7); // returns number -4
*
* var c = math.complex(3.2, -2.7);
* math.ceil(c); // returns Complex 4 - 2i
*
* math.ceil([3.2, 3.8, -4.7]); // returns Array [4, 4, -4]
*
* See also:
*
* floor, fix, round
*
* @param {number | BigNumber | Fraction | Complex | Array | Matrix} x Number to be rounded
* @return {number | BigNumber | Fraction | Complex | Array | Matrix} Rounded value
*/
var ceil = typed('ceil', {
'number': Math.ceil,
'Complex': function (x) {
return x.ceil();
},
'BigNumber': function (x) {
return x.ceil();
},
'Fraction': function (x) {
return x.ceil();
},
'Array | Matrix': function (x) {
// deep map collection, skip zeros since ceil(0) = 0
return deepMap(x, ceil, true);
}
});
ceil.toTex = {1: '\\left\\lceil${args[0]}\\right\\rceil'};
return ceil;
}
exports.name = 'ceil';
exports.factory = factory;

View File

@@ -0,0 +1,64 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
/**
* Compute the cube of a value, `x * x * x`.
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.cube(x)
*
* Examples:
*
* math.cube(2); // returns number 8
* math.pow(2, 3); // returns number 8
* math.cube(4); // returns number 64
* 4 * 4 * 4; // returns number 64
*
* math.cube([1, 2, 3, 4]); // returns Array [1, 8, 27, 64]
*
* See also:
*
* multiply, square, pow, cbrt
*
* @param {number | BigNumber | Fraction | Complex | Array | Matrix | Unit} x Number for which to calculate the cube
* @return {number | BigNumber | Fraction | Complex | Array | Matrix | Unit} Cube of x
*/
var cube = typed('cube', {
'number': function (x) {
return x * x * x;
},
'Complex': function (x) {
return x.mul(x).mul(x); // Is faster than pow(x, 3)
},
'BigNumber': function (x) {
return x.times(x).times(x);
},
'Fraction': function (x) {
return x.pow(3); // Is faster than mul()mul()mul()
},
'Array | Matrix': function (x) {
// deep map collection, skip zeros since cube(0) = 0
return deepMap(x, cube, true);
},
'Unit': function(x) {
return x.pow(3);
}
});
cube.toTex = {1: '\\left(${args[0]}\\right)^3'};
return cube;
}
exports.name = 'cube';
exports.factory = factory;

View File

@@ -0,0 +1,89 @@
'use strict';
var extend = require('../../utils/object').extend;
function factory (type, config, load, typed) {
var divideScalar = load(require('./divideScalar'));
var multiply = load(require('./multiply'));
var inv = load(require('../matrix/inv'));
var matrix = load(require('../../type/matrix/function/matrix'));
var algorithm11 = load(require('../../type/matrix/utils/algorithm11'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Divide two values, `x / y`.
* To divide matrices, `x` is multiplied with the inverse of `y`: `x * inv(y)`.
*
* Syntax:
*
* math.divide(x, y)
*
* Examples:
*
* math.divide(2, 3); // returns number 0.6666666666666666
*
* var a = math.complex(5, 14);
* var b = math.complex(4, 1);
* math.divide(a, b); // returns Complex 2 + 3i
*
* var c = [[7, -6], [13, -4]];
* var d = [[1, 2], [4, 3]];
* math.divide(c, d); // returns Array [[-9, 4], [-11, 6]]
*
* var e = math.unit('18 km');
* math.divide(e, 4.5); // returns Unit 4 km
*
* See also:
*
* multiply
*
* @param {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} x Numerator
* @param {number | BigNumber | Fraction | Complex | Array | Matrix} y Denominator
* @return {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} Quotient, `x / y`
*/
var divide = typed('divide', extend({
// we extend the signatures of divideScalar with signatures dealing with matrices
'Array | Matrix, Array | Matrix': function (x, y) {
// TODO: implement matrix right division using pseudo inverse
// http://www.mathworks.nl/help/matlab/ref/mrdivide.html
// http://www.gnu.org/software/octave/doc/interpreter/Arithmetic-Ops.html
// http://stackoverflow.com/questions/12263932/how-does-gnu-octave-matrix-division-work-getting-unexpected-behaviour
return multiply(x, inv(y));
},
'Matrix, any': function (x, y) {
// result
var c;
// process storage format
switch (x.storage()) {
case 'sparse':
c = algorithm11(x, y, divideScalar, false);
break;
case 'dense':
c = algorithm14(x, y, divideScalar, false);
break;
}
return c;
},
'Array, any': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, divideScalar, false).valueOf();
},
'any, Array | Matrix': function (x, y) {
return multiply(x, inv(y));
}
}, divideScalar.signatures));
divide.toTex = {2: '\\frac{${args[0]}}{${args[1]}}'};
return divide;
}
exports.name = 'divide';
exports.factory = factory;

View File

@@ -0,0 +1,59 @@
'use strict';
function factory(type, config, load, typed) {
var multiplyScalar = load(require('./multiplyScalar'));
/**
* Divide two scalar values, `x / y`.
* This function is meant for internal use: it is used by the public functions
* `divide` and `inv`.
*
* This function does not support collections (Array or Matrix), and does
* not validate the number of of inputs.
*
* @param {number | BigNumber | Fraction | Complex | Unit} x Numerator
* @param {number | BigNumber | Fraction | Complex} y Denominator
* @return {number | BigNumber | Fraction | Complex | Unit} Quotient, `x / y`
* @private
*/
var divideScalar = typed('divide', {
'number, number': function (x, y) {
return x / y;
},
'Complex, Complex': function (x, y) {
return x.div(y);
},
'BigNumber, BigNumber': function (x, y) {
return x.div(y);
},
'Fraction, Fraction': function (x, y) {
return x.div(y);
},
'Unit, number | Fraction | BigNumber': function (x, y) {
var res = x.clone();
// TODO: move the divide function to Unit.js, it uses internals of Unit
res.value = divideScalar(((res.value === null) ? res._normalize(1) : res.value), y);
return res;
},
'number | Fraction | BigNumber, Unit': function (x, y) {
var res = y.pow(-1);
// TODO: move the divide function to Unit.js, it uses internals of Unit
res.value = multiplyScalar(((res.value === null) ? res._normalize(1) : res.value), x);
return res;
},
'Unit, Unit': function (x, y) {
return x.divide(y);
}
});
return divideScalar;
}
exports.factory = factory;

View File

@@ -0,0 +1,145 @@
'use strict';
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
var divideScalar = load(require('./divideScalar'));
var latex = require('../../utils/latex');
var algorithm02 = load(require('../../type/matrix/utils/algorithm02'));
var algorithm03 = load(require('../../type/matrix/utils/algorithm03'));
var algorithm07 = load(require('../../type/matrix/utils/algorithm07'));
var algorithm11 = load(require('../../type/matrix/utils/algorithm11'));
var algorithm12 = load(require('../../type/matrix/utils/algorithm12'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Divide two matrices element wise. The function accepts both matrices and
* scalar values.
*
* Syntax:
*
* math.dotDivide(x, y)
*
* Examples:
*
* math.dotDivide(2, 4); // returns 0.5
*
* a = [[9, 5], [6, 1]];
* b = [[3, 2], [5, 2]];
*
* math.dotDivide(a, b); // returns [[3, 2.5], [1.2, 0.5]]
* math.divide(a, b); // returns [[1.75, 0.75], [-1.75, 2.25]]
*
* See also:
*
* divide, multiply, dotMultiply
*
* @param {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} x Numerator
* @param {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} y Denominator
* @return {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} Quotient, `x ./ y`
*/
var dotDivide = typed('dotDivide', {
'any, any': divideScalar,
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse ./ sparse
c = algorithm07(x, y, divideScalar, false);
break;
default:
// sparse ./ dense
c = algorithm02(y, x, divideScalar, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense ./ sparse
c = algorithm03(x, y, divideScalar, false);
break;
default:
// dense ./ dense
c = algorithm13(x, y, divideScalar);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return dotDivide(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return dotDivide(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return dotDivide(x, matrix(y));
},
'Matrix, any': function (x, y) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm11(x, y, divideScalar, false);
break;
default:
c = algorithm14(x, y, divideScalar, false);
break;
}
return c;
},
'any, Matrix': function (x, y) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm12(y, x, divideScalar, true);
break;
default:
c = algorithm14(y, x, divideScalar, true);
break;
}
return c;
},
'Array, any': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, divideScalar, false).valueOf();
},
'any, Array': function (x, y) {
// use matrix implementation
return algorithm14(matrix(y), x, divideScalar, true).valueOf();
}
});
dotDivide.toTex = {
2: '\\left(${args[0]}' + latex.operators['dotDivide'] + '${args[1]}\\right)'
};
return dotDivide;
}
exports.name = 'dotDivide';
exports.factory = factory;

View File

@@ -0,0 +1,143 @@
'use strict';
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
var multiplyScalar = load(require('./multiplyScalar'));
var latex = require('../../utils/latex');
var algorithm02 = load(require('../../type/matrix/utils/algorithm02'));
var algorithm09 = load(require('../../type/matrix/utils/algorithm09'));
var algorithm11 = load(require('../../type/matrix/utils/algorithm11'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Multiply two matrices element wise. The function accepts both matrices and
* scalar values.
*
* Syntax:
*
* math.dotMultiply(x, y)
*
* Examples:
*
* math.dotMultiply(2, 4); // returns 8
*
* a = [[9, 5], [6, 1]];
* b = [[3, 2], [5, 2]];
*
* math.dotMultiply(a, b); // returns [[27, 10], [30, 2]]
* math.multiply(a, b); // returns [[52, 28], [23, 14]]
*
* See also:
*
* multiply, divide, dotDivide
*
* @param {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} x Left hand value
* @param {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} y Right hand value
* @return {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} Multiplication of `x` and `y`
*/
var dotMultiply = typed('dotMultiply', {
'any, any': multiplyScalar,
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse .* sparse
c = algorithm09(x, y, multiplyScalar, false);
break;
default:
// sparse .* dense
c = algorithm02(y, x, multiplyScalar, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense .* sparse
c = algorithm02(x, y, multiplyScalar, false);
break;
default:
// dense .* dense
c = algorithm13(x, y, multiplyScalar);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return dotMultiply(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return dotMultiply(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return dotMultiply(x, matrix(y));
},
'Matrix, any': function (x, y) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm11(x, y, multiplyScalar, false);
break;
default:
c = algorithm14(x, y, multiplyScalar, false);
break;
}
return c;
},
'any, Matrix': function (x, y) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm11(y, x, multiplyScalar, true);
break;
default:
c = algorithm14(y, x, multiplyScalar, true);
break;
}
return c;
},
'Array, any': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, multiplyScalar, false).valueOf();
},
'any, Array': function (x, y) {
// use matrix implementation
return algorithm14(matrix(y), x, multiplyScalar, true).valueOf();
}
});
dotMultiply.toTex = {
2: '\\left(${args[0]}' + latex.operators['dotMultiply'] + '${args[1]}\\right)'
};
return dotMultiply;
}
exports.name = 'dotMultiply';
exports.factory = factory;

View File

@@ -0,0 +1,141 @@
'use strict';
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
var pow = load(require('./pow'));
var latex = require('../../utils/latex');
var algorithm03 = load(require('../../type/matrix/utils/algorithm03'));
var algorithm07 = load(require('../../type/matrix/utils/algorithm07'));
var algorithm11 = load(require('../../type/matrix/utils/algorithm11'));
var algorithm12 = load(require('../../type/matrix/utils/algorithm12'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Calculates the power of x to y element wise.
*
* Syntax:
*
* math.dotPow(x, y)
*
* Examples:
*
* math.dotPow(2, 3); // returns number 8
*
* var a = [[1, 2], [4, 3]];
* math.dotPow(a, 2); // returns Array [[1, 4], [16, 9]]
* math.pow(a, 2); // returns Array [[9, 8], [16, 17]]
*
* See also:
*
* pow, sqrt, multiply
*
* @param {number | BigNumber | Complex | Unit | Array | Matrix} x The base
* @param {number | BigNumber | Complex | Unit | Array | Matrix} y The exponent
* @return {number | BigNumber | Complex | Unit | Array | Matrix} The value of `x` to the power `y`
*/
var dotPow = typed('dotPow', {
'any, any': pow,
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse .^ sparse
c = algorithm07(x, y, pow, false);
break;
default:
// sparse .^ dense
c = algorithm03(y, x, pow, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense .^ sparse
c = algorithm03(x, y, pow, false);
break;
default:
// dense .^ dense
c = algorithm13(x, y, pow);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return dotPow(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return dotPow(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return dotPow(x, matrix(y));
},
'Matrix, any': function (x, y) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm11(x, y, dotPow, false);
break;
default:
c = algorithm14(x, y, dotPow, false);
break;
}
return c;
},
'any, Matrix': function (x, y) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm12(y, x, dotPow, true);
break;
default:
c = algorithm14(y, x, dotPow, true);
break;
}
return c;
},
'Array, any': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, dotPow, false).valueOf();
},
'any, Array': function (x, y) {
// use matrix implementation
return algorithm14(matrix(y), x, dotPow, true).valueOf();
}
});
dotPow.toTex = {
2: '\\left(${args[0]}' + latex.operators['dotPow'] + '${args[1]}\\right)'
};
return dotPow;
}
exports.name = 'dotPow';
exports.factory = factory;

View File

@@ -0,0 +1,57 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
/**
* Calculate the exponent of a value.
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.exp(x)
*
* Examples:
*
* math.exp(2); // returns number 7.3890560989306495
* math.pow(math.e, 2); // returns number 7.3890560989306495
* math.log(math.exp(2)); // returns number 2
*
* math.exp([1, 2, 3]);
* // returns Array [
* // 2.718281828459045,
* // 7.3890560989306495,
* // 20.085536923187668
* // ]
*
* See also:
*
* log, pow
*
* @param {number | BigNumber | Complex | Array | Matrix} x A number or matrix to exponentiate
* @return {number | BigNumber | Complex | Array | Matrix} Exponent of `x`
*/
var exp = typed('exp', {
'number': Math.exp,
'Complex': function (x) {
return x.exp();
},
'BigNumber': function (x) {
return x.exp();
},
'Array | Matrix': function (x) {
// TODO: exp(sparse) should return a dense matrix since exp(0)==1
return deepMap(x, exp);
}
});
exp.toTex = {1: '\\exp\\left(${args[0]}\\right)'};
return exp;
}
exports.name = 'exp';
exports.factory = factory;

View File

@@ -0,0 +1,65 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
/**
* Round a value towards zero.
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.fix(x)
*
* Examples:
*
* math.fix(3.2); // returns number 3
* math.fix(3.8); // returns number 3
* math.fix(-4.2); // returns number -4
* math.fix(-4.7); // returns number -4
*
* var c = math.complex(3.2, -2.7);
* math.fix(c); // returns Complex 3 - 2i
*
* math.fix([3.2, 3.8, -4.7]); // returns Array [3, 3, -4]
*
* See also:
*
* ceil, floor, round
*
* @param {number | BigNumber | Fraction | Complex | Array | Matrix} x Number to be rounded
* @return {number | BigNumber | Fraction | Complex | Array | Matrix} Rounded value
*/
var fix = typed('fix', {
'number': function (x) {
return (x > 0) ? Math.floor(x) : Math.ceil(x);
},
'Complex': function (x) {
return new type.Complex(
(x.re > 0) ? Math.floor(x.re) : Math.ceil(x.re),
(x.im > 0) ? Math.floor(x.im) : Math.ceil(x.im)
);
},
'BigNumber': function (x) {
return x.isNegative() ? x.ceil() : x.floor();
},
'Fraction': function (x) {
return x.s < 0 ? x.ceil() : x.floor();
},
'Array | Matrix': function (x) {
// deep map collection, skip zeros since fix(0) = 0
return deepMap(x, fix, true);
}
});
fix.toTex = {1: '\\mathrm{${name}}\\left(${args[0]}\\right)'};
return fix;
}
exports.name = 'fix';
exports.factory = factory;

View File

@@ -0,0 +1,60 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
/**
* Round a value towards minus infinity.
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.floor(x)
*
* Examples:
*
* math.floor(3.2); // returns number 3
* math.floor(3.8); // returns number 3
* math.floor(-4.2); // returns number -5
* math.floor(-4.7); // returns number -5
*
* var c = math.complex(3.2, -2.7);
* math.floor(c); // returns Complex 3 - 3i
*
* math.floor([3.2, 3.8, -4.7]); // returns Array [3, 3, -5]
*
* See also:
*
* ceil, fix, round
*
* @param {number | BigNumber | Fraction | Complex | Array | Matrix} x Number to be rounded
* @return {number | BigNumber | Fraction | Complex | Array | Matrix} Rounded value
*/
var floor = typed('floor', {
'number': Math.floor,
'Complex': function (x) {
return x.floor();
},
'BigNumber': function (x) {
return x.floor();
},
'Fraction': function (x) {
return x.floor();
},
'Array | Matrix': function (x) {
// deep map collection, skip zeros since floor(0) = 0
return deepMap(x, floor, true);
}
});
floor.toTex = {1: '\\left\\lfloor${args[0]}\\right\\rfloor'};
return floor;
}
exports.name = 'floor';
exports.factory = factory;

View File

@@ -0,0 +1,199 @@
'use strict';
var isInteger = require('../../utils/number').isInteger;
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
var algorithm01 = load(require('../../type/matrix/utils/algorithm01'));
var algorithm04 = load(require('../../type/matrix/utils/algorithm04'));
var algorithm10 = load(require('../../type/matrix/utils/algorithm10'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Calculate the greatest common divisor for two or more values or arrays.
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.gcd(a, b)
* math.gcd(a, b, c, ...)
*
* Examples:
*
* math.gcd(8, 12); // returns 4
* math.gcd(-4, 6); // returns 2
* math.gcd(25, 15, -10); // returns 5
*
* math.gcd([8, -4], [12, 6]); // returns [4, 2]
*
* See also:
*
* lcm, xgcd
*
* @param {... number | BigNumber | Fraction | Array | Matrix} args Two or more integer numbers
* @return {number | BigNumber | Fraction | Array | Matrix} The greatest common divisor
*/
var gcd = typed('gcd', {
'number, number': _gcd,
'BigNumber, BigNumber': _gcdBigNumber,
'Fraction, Fraction': function (x, y) {
return x.gcd(y);
},
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse + sparse
c = algorithm04(x, y, gcd);
break;
default:
// sparse + dense
c = algorithm01(y, x, gcd, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense + sparse
c = algorithm01(x, y, gcd, false);
break;
default:
// dense + dense
c = algorithm13(x, y, gcd);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return gcd(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return gcd(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return gcd(x, matrix(y));
},
'Matrix, number | BigNumber': function (x, y) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm10(x, y, gcd, false);
break;
default:
c = algorithm14(x, y, gcd, false);
break;
}
return c;
},
'number | BigNumber, Matrix': function (x, y) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm10(y, x, gcd, true);
break;
default:
c = algorithm14(y, x, gcd, true);
break;
}
return c;
},
'Array, number | BigNumber': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, gcd, false).valueOf();
},
'number | BigNumber, Array': function (x, y) {
// use matrix implementation
return algorithm14(matrix(y), x, gcd, true).valueOf();
},
// TODO: need a smarter notation here
'Array | Matrix | number | BigNumber, Array | Matrix | number | BigNumber, ...Array | Matrix | number | BigNumber': function (a, b, args) {
var res = gcd(a, b);
for (var i = 0; i < args.length; i++) {
res = gcd(res, args[i]);
}
return res;
}
});
gcd.toTex = '\\gcd\\left(${args}\\right)';
return gcd;
/**
* Calculate gcd for BigNumbers
* @param {BigNumber} a
* @param {BigNumber} b
* @returns {BigNumber} Returns greatest common denominator of a and b
* @private
*/
function _gcdBigNumber(a, b) {
if (!a.isInt() || !b.isInt()) {
throw new Error('Parameters in function gcd must be integer numbers');
}
// http://en.wikipedia.org/wiki/Euclidean_algorithm
var zero = new type.BigNumber(0);
while (!b.isZero()) {
var r = a.mod(b);
a = b;
b = r;
}
return a.lt(zero) ? a.neg() : a;
}
}
/**
* Calculate gcd for numbers
* @param {number} a
* @param {number} b
* @returns {number} Returns the greatest common denominator of a and b
* @private
*/
function _gcd(a, b) {
if (!isInteger(a) || !isInteger(b)) {
throw new Error('Parameters in function gcd must be integer numbers');
}
// http://en.wikipedia.org/wiki/Euclidean_algorithm
var r;
while (b != 0) {
r = a % b;
a = b;
b = r;
}
return (a < 0) ? -a : a;
}
exports.name = 'gcd';
exports.factory = factory;

View File

@@ -0,0 +1,84 @@
'use strict';
var flatten = require('../../utils/array').flatten;
function factory (type, config, load, typed) {
var abs = load(require('./abs'));
var add = load(require('./addScalar'));
var divide = load(require('./divideScalar'));
var multiply = load(require('./multiplyScalar'));
var sqrt = load(require('./sqrt'));
var smaller = load(require('../relational/smaller'));
var isPositive = load(require('../utils/isPositive'));
/**
* Calculate the hypotenusa of a list with values. The hypotenusa is defined as:
*
* hypot(a, b, c, ...) = sqrt(a^2 + b^2 + c^2 + ...)
*
* For matrix input, the hypotenusa is calculated for all values in the matrix.
*
* Syntax:
*
* math.hypot(a, b, ...)
* math.hypot([a, b, c, ...])
*
* Examples:
*
* math.hypot(3, 4); // 5
* math.hypot(3, 4, 5); // 7.0710678118654755
* math.hypot([3, 4, 5]); // 7.0710678118654755
* math.hypot(-2); // 2
*
* See also:
*
* abs, norm
*
* @param {... number | BigNumber} args
* @return {number | BigNumber} Returns the hypothenusa of the input values.
*/
var hypot = typed('hypot', {
'... number | BigNumber': _hypot,
'Array': function (x) {
return hypot.apply(hypot, flatten(x));
},
'Matrix': function (x) {
return hypot.apply(hypot, flatten(x.toArray()));
}
});
/**
* Calculate the hypotenusa for an Array with values
* @param {Array.<number | BigNumber>} args
* @return {number | BigNumber} Returns the result
* @private
*/
function _hypot (args) {
// code based on `hypot` from es6-shim:
// https://github.com/paulmillr/es6-shim/blob/master/es6-shim.js#L1619-L1633
var result = 0;
var largest = 0;
for (var i = 0; i < args.length; i++) {
var value = abs(args[i]);
if (smaller(largest, value)) {
result = multiply(result, multiply(divide(largest, value), divide(largest, value)));
result = add(result, 1);
largest = value;
} else {
result = add(result, isPositive(value) ? multiply(divide(value, largest), divide(value, largest)) : value);
}
}
return multiply(largest, sqrt(result));
}
hypot.toTex = '\\hypot\\left(${args}\\right)';
return hypot;
}
exports.name = 'hypot';
exports.factory = factory;

View File

@@ -0,0 +1,33 @@
module.exports = [
require('./abs'),
require('./add'),
require('./addScalar'),
require('./cbrt'),
require('./ceil'),
require('./cube'),
require('./divide'),
require('./dotDivide'),
require('./dotMultiply'),
require('./dotPow'),
require('./exp'),
require('./fix'),
require('./floor'),
require('./gcd'),
require('./hypot'),
require('./lcm'),
require('./log'),
require('./log10'),
require('./mod'),
require('./multiply'),
require('./norm'),
require('./nthRoot'),
require('./pow'),
require('./round'),
require('./sign'),
require('./sqrt'),
require('./square'),
require('./subtract'),
require('./unaryMinus'),
require('./unaryPlus'),
require('./xgcd')
];

View File

@@ -0,0 +1,214 @@
'use strict';
var isInteger = require('../../utils/number').isInteger;
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
var algorithm02 = load(require('../../type/matrix/utils/algorithm02'));
var algorithm06 = load(require('../../type/matrix/utils/algorithm06'));
var algorithm11 = load(require('../../type/matrix/utils/algorithm11'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Calculate the least common multiple for two or more values or arrays.
*
* lcm is defined as:
*
* lcm(a, b) = abs(a * b) / gcd(a, b)
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.lcm(a, b)
* math.lcm(a, b, c, ...)
*
* Examples:
*
* math.lcm(4, 6); // returns 12
* math.lcm(6, 21); // returns 42
* math.lcm(6, 21, 5); // returns 210
*
* math.lcm([4, 6], [6, 21]); // returns [12, 42]
*
* See also:
*
* gcd, xgcd
*
* @param {... number | BigNumber | Array | Matrix} args Two or more integer numbers
* @return {number | BigNumber | Array | Matrix} The least common multiple
*/
var lcm = typed('lcm', {
'number, number': _lcm,
'BigNumber, BigNumber': _lcmBigNumber,
'Fraction, Fraction': function (x, y) {
return x.lcm(y);
},
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse + sparse
c = algorithm06(x, y, lcm);
break;
default:
// sparse + dense
c = algorithm02(y, x, lcm, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense + sparse
c = algorithm02(x, y, lcm, false);
break;
default:
// dense + dense
c = algorithm13(x, y, lcm);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return lcm(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return lcm(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return lcm(x, matrix(y));
},
'Matrix, number | BigNumber': function (x, y) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm11(x, y, lcm, false);
break;
default:
c = algorithm14(x, y, lcm, false);
break;
}
return c;
},
'number | BigNumber, Matrix': function (x, y) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm11(y, x, lcm, true);
break;
default:
c = algorithm14(y, x, lcm, true);
break;
}
return c;
},
'Array, number | BigNumber': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, lcm, false).valueOf();
},
'number | BigNumber, Array': function (x, y) {
// use matrix implementation
return algorithm14(matrix(y), x, lcm, true).valueOf();
},
// TODO: need a smarter notation here
'Array | Matrix | number | BigNumber, Array | Matrix | number | BigNumber, ...Array | Matrix | number | BigNumber': function (a, b, args) {
var res = lcm(a, b);
for (var i = 0; i < args.length; i++) {
res = lcm(res, args[i]);
}
return res;
}
});
lcm.toTex = undefined; // use default template
return lcm;
/**
* Calculate lcm for two BigNumbers
* @param {BigNumber} a
* @param {BigNumber} b
* @returns {BigNumber} Returns the least common multiple of a and b
* @private
*/
function _lcmBigNumber(a, b) {
if (!a.isInt() || !b.isInt()) {
throw new Error('Parameters in function lcm must be integer numbers');
}
if (a.isZero() || b.isZero()) {
return new type.BigNumber(0);
}
// http://en.wikipedia.org/wiki/Euclidean_algorithm
// evaluate lcm here inline to reduce overhead
var prod = a.times(b);
while (!b.isZero()) {
var t = b;
b = a.mod(t);
a = t;
}
return prod.div(a).abs();
}
}
/**
* Calculate lcm for two numbers
* @param {number} a
* @param {number} b
* @returns {number} Returns the least common multiple of a and b
* @private
*/
function _lcm (a, b) {
if (!isInteger(a) || !isInteger(b)) {
throw new Error('Parameters in function lcm must be integer numbers');
}
if (a == 0 || b == 0) {
return 0;
}
// http://en.wikipedia.org/wiki/Euclidean_algorithm
// evaluate lcm here inline to reduce overhead
var t;
var prod = a * b;
while (b != 0) {
t = b;
b = a % t;
a = t;
}
return Math.abs(prod / a);
}
exports.name = 'lcm';
exports.factory = factory;

View File

@@ -0,0 +1,86 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
var divideScalar = load(require('./divideScalar'));
/**
* Calculate the logarithm of a value.
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.log(x)
* math.log(x, base)
*
* Examples:
*
* math.log(3.5); // returns 1.252762968495368
* math.exp(math.log(2.4)); // returns 2.4
*
* math.pow(10, 4); // returns 10000
* math.log(10000, 10); // returns 4
* math.log(10000) / math.log(10); // returns 4
*
* math.log(1024, 2); // returns 10
* math.pow(2, 10); // returns 1024
*
* See also:
*
* exp, log10
*
* @param {number | BigNumber | Complex | Array | Matrix} x
* Value for which to calculate the logarithm.
* @param {number | BigNumber | Complex} [base=e]
* Optional base for the logarithm. If not provided, the natural
* logarithm of `x` is calculated.
* @return {number | BigNumber | Complex | Array | Matrix}
* Returns the logarithm of `x`
*/
var log = typed('log', {
'number': function (x) {
if (x >= 0 || config.predictable) {
return Math.log(x);
}
else {
// negative value -> complex value computation
return new type.Complex(x, 0).log();
}
},
'Complex': function (x) {
return x.log();
},
'BigNumber': function (x) {
if (!x.isNegative() || config.predictable) {
return x.ln();
}
else {
// downgrade to number, return Complex valued result
return new type.Complex(x.toNumber(), 0).log();
}
},
'Array | Matrix': function (x) {
return deepMap(x, log);
},
'any, any': function (x, base) {
// calculate logarithm for a specified base, log(x, base)
return divideScalar(log(x), log(base));
}
});
log.toTex = {
1: '\\ln\\left(${args[0]}\\right)',
2: '\\log_{${args[1]}}\\left(${args[0]}\\right)'
};
return log;
}
exports.name = 'log';
exports.factory = factory;

View File

@@ -0,0 +1,78 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
/**
* Calculate the 10-base logarithm of a value. This is the same as calculating `log(x, 10)`.
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.log10(x)
*
* Examples:
*
* math.log10(0.00001); // returns -5
* math.log10(10000); // returns 4
* math.log(10000) / math.log(10); // returns 4
* math.pow(10, 4); // returns 10000
*
* See also:
*
* exp, log
*
* @param {number | BigNumber | Complex | Array | Matrix} x
* Value for which to calculate the logarithm.
* @return {number | BigNumber | Complex | Array | Matrix}
* Returns the 10-base logarithm of `x`
*/
var log10 = typed('log10', {
'number': function (x) {
if (x >= 0 || config.predictable) {
return _log10(x);
}
else {
// negative value -> complex value computation
return new type.Complex(x, 0).log().div(Math.LN10);
}
},
'Complex': function (x) {
return new type.Complex(x).log().div(Math.LN10);
},
'BigNumber': function (x) {
if (!x.isNegative() || config.predictable) {
return x.log();
}
else {
// downgrade to number, return Complex valued result
return new type.Complex(x.toNumber(), 0).log().div(Math.LN10);
}
},
'Array | Matrix': function (x) {
return deepMap(x, log10);
}
});
log10.toTex = {1: '\\log_{10}\\left(${args[0]}\\right)'};
return log10;
}
/**
* Calculate the 10-base logarithm of a number
* @param {number} x
* @return {number}
* @private
*/
var _log10 = Math.log10 || function (x) {
return Math.log(x) / Math.LN10;
};
exports.name = 'log10';
exports.factory = factory;

View File

@@ -0,0 +1,184 @@
'use strict';
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
var latex = require('../../utils/latex');
var algorithm02 = load(require('../../type/matrix/utils/algorithm02'));
var algorithm03 = load(require('../../type/matrix/utils/algorithm03'));
var algorithm05 = load(require('../../type/matrix/utils/algorithm05'));
var algorithm11 = load(require('../../type/matrix/utils/algorithm11'));
var algorithm12 = load(require('../../type/matrix/utils/algorithm12'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Calculates the modulus, the remainder of an integer division.
*
* For matrices, the function is evaluated element wise.
*
* The modulus is defined as:
*
* x - y * floor(x / y)
*
* See http://en.wikipedia.org/wiki/Modulo_operation.
*
* Syntax:
*
* math.mod(x, y)
*
* Examples:
*
* math.mod(8, 3); // returns 2
* math.mod(11, 2); // returns 1
*
* function isOdd(x) {
* return math.mod(x, 2) != 0;
* }
*
* isOdd(2); // returns false
* isOdd(3); // returns true
*
* See also:
*
* divide
*
* @param {number | BigNumber | Fraction | Array | Matrix} x Dividend
* @param {number | BigNumber | Fraction | Array | Matrix} y Divisor
* @return {number | BigNumber | Fraction | Array | Matrix} Returns the remainder of `x` divided by `y`.
*/
var mod = typed('mod', {
'number, number': _mod,
'BigNumber, BigNumber': function (x, y) {
return y.isZero() ? x : x.mod(y);
},
'Fraction, Fraction': function (x, y) {
return x.mod(y);
},
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// mod(sparse, sparse)
c = algorithm05(x, y, mod, false);
break;
default:
// mod(sparse, dense)
c = algorithm02(y, x, mod, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// mod(dense, sparse)
c = algorithm03(x, y, mod, false);
break;
default:
// mod(dense, dense)
c = algorithm13(x, y, mod);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return mod(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return mod(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return mod(x, matrix(y));
},
'Matrix, any': function (x, y) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm11(x, y, mod, false);
break;
default:
c = algorithm14(x, y, mod, false);
break;
}
return c;
},
'any, Matrix': function (x, y) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm12(y, x, mod, true);
break;
default:
c = algorithm14(y, x, mod, true);
break;
}
return c;
},
'Array, any': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, mod, false).valueOf();
},
'any, Array': function (x, y) {
// use matrix implementation
return algorithm14(matrix(y), x, mod, true).valueOf();
}
});
mod.toTex = {
2: '\\left(${args[0]}' + latex.operators['mod'] + '${args[1]}\\right)'
};
return mod;
/**
* Calculate the modulus of two numbers
* @param {number} x
* @param {number} y
* @returns {number} res
* @private
*/
function _mod(x, y) {
if (y > 0) {
// We don't use JavaScript's % operator here as this doesn't work
// correctly for x < 0 and x == 0
// see http://en.wikipedia.org/wiki/Modulo_operation
return x - y * Math.floor(x / y);
}
else if (y === 0) {
return x;
}
else { // y < 0
// TODO: implement mod for a negative divisor
throw new Error('Cannot calculate mod for a negative divisor');
}
}
}
exports.name = 'mod';
exports.factory = factory;

View File

@@ -0,0 +1,970 @@
'use strict';
var extend = require('../../utils/object').extend;
var array = require('../../utils/array');
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
var matrix = load(require('../../type/matrix/function/matrix'));
var addScalar = load(require('./addScalar'));
var multiplyScalar = load(require('./multiplyScalar'));
var equalScalar = load(require('../relational/equalScalar'));
var algorithm11 = load(require('../../type/matrix/utils/algorithm11'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
var DenseMatrix = type.DenseMatrix;
var SparseMatrix = type.SparseMatrix;
/**
* Multiply two or more values, `x * y`.
* For matrices, the matrix product is calculated.
*
* Syntax:
*
* math.multiply(x, y)
* math.multiply(x, y, z, ...)
*
* Examples:
*
* math.multiply(4, 5.2); // returns number 20.8
* math.multiply(2, 3, 4); // returns number 24
*
* var a = math.complex(2, 3);
* var b = math.complex(4, 1);
* math.multiply(a, b); // returns Complex 5 + 14i
*
* var c = [[1, 2], [4, 3]];
* var d = [[1, 2, 3], [3, -4, 7]];
* math.multiply(c, d); // returns Array [[7, -6, 17], [13, -4, 33]]
*
* var e = math.unit('2.1 km');
* math.multiply(3, e); // returns Unit 6.3 km
*
* See also:
*
* divide, prod, cross, dot
*
* @param {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} x First value to multiply
* @param {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} y Second value to multiply
* @return {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} Multiplication of `x` and `y`
*/
var multiply = typed('multiply', extend({
// we extend the signatures of multiplyScalar with signatures dealing with matrices
'Array, Array': function (x, y) {
// check dimensions
_validateMatrixDimensions(array.size(x), array.size(y));
// use dense matrix implementation
var m = multiply(matrix(x), matrix(y));
// return array or scalar
return (m && m.isMatrix === true) ? m.valueOf() : m;
},
'Matrix, Matrix': function (x, y) {
// dimensions
var xsize = x.size();
var ysize = y.size();
// check dimensions
_validateMatrixDimensions(xsize, ysize);
// process dimensions
if (xsize.length === 1) {
// process y dimensions
if (ysize.length === 1) {
// Vector * Vector
return _multiplyVectorVector(x, y, xsize[0]);
}
// Vector * Matrix
return _multiplyVectorMatrix(x, y);
}
// process y dimensions
if (ysize.length === 1) {
// Matrix * Vector
return _multiplyMatrixVector(x, y);
}
// Matrix * Matrix
return _multiplyMatrixMatrix(x, y);
},
'Matrix, Array': function (x, y) {
// use Matrix * Matrix implementation
return multiply(x, matrix(y));
},
'Array, Matrix': function (x, y) {
// use Matrix * Matrix implementation
return multiply(matrix(x, y.storage()), y);
},
'Matrix, any': function (x, y) {
// result
var c;
// process storage format
switch (x.storage()) {
case 'sparse':
c = algorithm11(x, y, multiplyScalar, false);
break;
case 'dense':
c = algorithm14(x, y, multiplyScalar, false);
break;
}
return c;
},
'any, Matrix': function (x, y) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm11(y, x, multiplyScalar, true);
break;
case 'dense':
c = algorithm14(y, x, multiplyScalar, true);
break;
}
return c;
},
'Array, any': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, multiplyScalar, false).valueOf();
},
'any, Array': function (x, y) {
// use matrix implementation
return algorithm14(matrix(y), x, multiplyScalar, true).valueOf();
},
'any, any': multiplyScalar,
'any, any, ...any': function (x, y, rest) {
var result = multiply(x, y);
for (var i = 0; i < rest.length; i++) {
result = multiply(result, rest[i]);
}
return result;
}
}, multiplyScalar.signatures));
var _validateMatrixDimensions = function (size1, size2) {
// check left operand dimensions
switch (size1.length) {
case 1:
// check size2
switch (size2.length) {
case 1:
// Vector x Vector
if (size1[0] !== size2[0]) {
// throw error
throw new RangeError('Dimension mismatch in multiplication. Vectors must have the same length');
}
break;
case 2:
// Vector x Matrix
if (size1[0] !== size2[0]) {
// throw error
throw new RangeError('Dimension mismatch in multiplication. Vector length (' + size1[0] + ') must match Matrix rows (' + size2[0] + ')');
}
break;
default:
throw new Error('Can only multiply a 1 or 2 dimensional matrix (Matrix B has ' + size2.length + ' dimensions)');
}
break;
case 2:
// check size2
switch (size2.length) {
case 1:
// Matrix x Vector
if (size1[1] !== size2[0]) {
// throw error
throw new RangeError('Dimension mismatch in multiplication. Matrix columns (' + size1[1] + ') must match Vector length (' + size2[0] + ')');
}
break;
case 2:
// Matrix x Matrix
if (size1[1] !== size2[0]) {
// throw error
throw new RangeError('Dimension mismatch in multiplication. Matrix A columns (' + size1[1] + ') must match Matrix B rows (' + size2[0] + ')');
}
break;
default:
throw new Error('Can only multiply a 1 or 2 dimensional matrix (Matrix B has ' + size2.length + ' dimensions)');
}
break;
default:
throw new Error('Can only multiply a 1 or 2 dimensional matrix (Matrix A has ' + size1.length + ' dimensions)');
}
};
/**
* C = A * B
*
* @param {Matrix} a Dense Vector (N)
* @param {Matrix} b Dense Vector (N)
*
* @return {number} Scalar value
*/
var _multiplyVectorVector = function (a, b, n) {
// check empty vector
if (n === 0)
throw new Error('Cannot multiply two empty vectors');
// a dense
var adata = a._data;
var adt = a._datatype;
// b dense
var bdata = b._data;
var bdt = b._datatype;
// datatype
var dt;
// addScalar signature to use
var af = addScalar;
// multiplyScalar signature to use
var mf = multiplyScalar;
// process data types
if (adt && bdt && adt === bdt && typeof adt === 'string') {
// datatype
dt = adt;
// find signatures that matches (dt, dt)
af = typed.find(addScalar, [dt, dt]);
mf = typed.find(multiplyScalar, [dt, dt]);
}
// result (do not initialize it with zero)
var c = mf(adata[0], bdata[0]);
// loop data
for (var i = 1; i < n; i++) {
// multiply and accumulate
c = af(c, mf(adata[i], bdata[i]));
}
return c;
};
/**
* C = A * B
*
* @param {Matrix} a Dense Vector (M)
* @param {Matrix} b Matrix (MxN)
*
* @return {Matrix} Dense Vector (N)
*/
var _multiplyVectorMatrix = function (a, b) {
// process storage
switch (b.storage()) {
case 'dense':
return _multiplyVectorDenseMatrix(a, b);
}
throw new Error('Not implemented');
};
/**
* C = A * B
*
* @param {Matrix} a Dense Vector (M)
* @param {Matrix} b Dense Matrix (MxN)
*
* @return {Matrix} Dense Vector (N)
*/
var _multiplyVectorDenseMatrix = function (a, b) {
// a dense
var adata = a._data;
var asize = a._size;
var adt = a._datatype;
// b dense
var bdata = b._data;
var bsize = b._size;
var bdt = b._datatype;
// rows & columns
var alength = asize[0];
var bcolumns = bsize[1];
// datatype
var dt;
// addScalar signature to use
var af = addScalar;
// multiplyScalar signature to use
var mf = multiplyScalar;
// process data types
if (adt && bdt && adt === bdt && typeof adt === 'string') {
// datatype
dt = adt;
// find signatures that matches (dt, dt)
af = typed.find(addScalar, [dt, dt]);
mf = typed.find(multiplyScalar, [dt, dt]);
}
// result
var c = [];
// loop matrix columns
for (var j = 0; j < bcolumns; j++) {
// sum (do not initialize it with zero)
var sum = mf(adata[0], bdata[0][j]);
// loop vector
for (var i = 1; i < alength; i++) {
// multiply & accumulate
sum = af(sum, mf(adata[i], bdata[i][j]));
}
c[j] = sum;
}
// return matrix
return new DenseMatrix({
data: c,
size: [bcolumns],
datatype: dt
});
};
/**
* C = A * B
*
* @param {Matrix} a Matrix (MxN)
* @param {Matrix} b Dense Vector (N)
*
* @return {Matrix} Dense Vector (M)
*/
var _multiplyMatrixVector = function (a, b) {
// process storage
switch (a.storage()) {
case 'dense':
return _multiplyDenseMatrixVector(a, b);
case 'sparse':
return _multiplySparseMatrixVector(a, b);
}
};
/**
* C = A * B
*
* @param {Matrix} a Matrix (MxN)
* @param {Matrix} b Matrix (NxC)
*
* @return {Matrix} Matrix (MxC)
*/
var _multiplyMatrixMatrix = function (a, b) {
// process storage
switch (a.storage()) {
case 'dense':
// process storage
switch (b.storage()) {
case 'dense':
return _multiplyDenseMatrixDenseMatrix(a, b);
case 'sparse':
return _multiplyDenseMatrixSparseMatrix(a, b);
}
break;
case 'sparse':
// process storage
switch (b.storage()) {
case 'dense':
return _multiplySparseMatrixDenseMatrix(a, b);
case 'sparse':
return _multiplySparseMatrixSparseMatrix(a, b);
}
break;
}
};
/**
* C = A * B
*
* @param {Matrix} a DenseMatrix (MxN)
* @param {Matrix} b Dense Vector (N)
*
* @return {Matrix} Dense Vector (M)
*/
var _multiplyDenseMatrixVector = function (a, b) {
// a dense
var adata = a._data;
var asize = a._size;
var adt = a._datatype;
// b dense
var bdata = b._data;
var bdt = b._datatype;
// rows & columns
var arows = asize[0];
var acolumns = asize[1];
// datatype
var dt;
// addScalar signature to use
var af = addScalar;
// multiplyScalar signature to use
var mf = multiplyScalar;
// process data types
if (adt && bdt && adt === bdt && typeof adt === 'string') {
// datatype
dt = adt;
// find signatures that matches (dt, dt)
af = typed.find(addScalar, [dt, dt]);
mf = typed.find(multiplyScalar, [dt, dt]);
}
// result
var c = [];
// loop matrix a rows
for (var i = 0; i < arows; i++) {
// current row
var row = adata[i];
// sum (do not initialize it with zero)
var sum = mf(row[0], bdata[0]);
// loop matrix a columns
for (var j = 1; j < acolumns; j++) {
// multiply & accumulate
sum = af(sum, mf(row[j], bdata[j]));
}
c[i] = sum;
}
// return matrix
return new DenseMatrix({
data: c,
size: [arows],
datatype: dt
});
};
/**
* C = A * B
*
* @param {Matrix} a DenseMatrix (MxN)
* @param {Matrix} b DenseMatrix (NxC)
*
* @return {Matrix} DenseMatrix (MxC)
*/
var _multiplyDenseMatrixDenseMatrix = function (a, b) {
// a dense
var adata = a._data;
var asize = a._size;
var adt = a._datatype;
// b dense
var bdata = b._data;
var bsize = b._size;
var bdt = b._datatype;
// rows & columns
var arows = asize[0];
var acolumns = asize[1];
var bcolumns = bsize[1];
// datatype
var dt;
// addScalar signature to use
var af = addScalar;
// multiplyScalar signature to use
var mf = multiplyScalar;
// process data types
if (adt && bdt && adt === bdt && typeof adt === 'string') {
// datatype
dt = adt;
// find signatures that matches (dt, dt)
af = typed.find(addScalar, [dt, dt]);
mf = typed.find(multiplyScalar, [dt, dt]);
}
// result
var c = [];
// loop matrix a rows
for (var i = 0; i < arows; i++) {
// current row
var row = adata[i];
// initialize row array
c[i] = [];
// loop matrix b columns
for (var j = 0; j < bcolumns; j++) {
// sum (avoid initializing sum to zero)
var sum = mf(row[0], bdata[0][j]);
// loop matrix a columns
for (var x = 1; x < acolumns; x++) {
// multiply & accumulate
sum = af(sum, mf(row[x], bdata[x][j]));
}
c[i][j] = sum;
}
}
// return matrix
return new DenseMatrix({
data: c,
size: [arows, bcolumns],
datatype: dt
});
};
/**
* C = A * B
*
* @param {Matrix} a DenseMatrix (MxN)
* @param {Matrix} b SparseMatrix (NxC)
*
* @return {Matrix} SparseMatrix (MxC)
*/
var _multiplyDenseMatrixSparseMatrix = function (a, b) {
// a dense
var adata = a._data;
var asize = a._size;
var adt = a._datatype;
// b sparse
var bvalues = b._values;
var bindex = b._index;
var bptr = b._ptr;
var bsize = b._size;
var bdt = b._datatype;
// validate b matrix
if (!bvalues)
throw new Error('Cannot multiply Dense Matrix times Pattern only Matrix');
// rows & columns
var arows = asize[0];
var bcolumns = bsize[1];
// datatype
var dt;
// addScalar signature to use
var af = addScalar;
// multiplyScalar signature to use
var mf = multiplyScalar;
// equalScalar signature to use
var eq = equalScalar;
// zero value
var zero = 0;
// process data types
if (adt && bdt && adt === bdt && typeof adt === 'string') {
// datatype
dt = adt;
// find signatures that matches (dt, dt)
af = typed.find(addScalar, [dt, dt]);
mf = typed.find(multiplyScalar, [dt, dt]);
eq = typed.find(equalScalar, [dt, dt]);
// convert 0 to the same datatype
zero = typed.convert(0, dt);
}
// result
var cvalues = [];
var cindex = [];
var cptr = [];
// c matrix
var c = new SparseMatrix({
values : cvalues,
index: cindex,
ptr: cptr,
size: [arows, bcolumns],
datatype: dt
});
// loop b columns
for (var jb = 0; jb < bcolumns; jb++) {
// update ptr
cptr[jb] = cindex.length;
// indeces in column jb
var kb0 = bptr[jb];
var kb1 = bptr[jb + 1];
// do not process column jb if no data exists
if (kb1 > kb0) {
// last row mark processed
var last = 0;
// loop a rows
for (var i = 0; i < arows; i++) {
// column mark
var mark = i + 1;
// C[i, jb]
var cij;
// values in b column j
for (var kb = kb0; kb < kb1; kb++) {
// row
var ib = bindex[kb];
// check value has been initialized
if (last !== mark) {
// first value in column jb
cij = mf(adata[i][ib], bvalues[kb]);
// update mark
last = mark;
}
else {
// accumulate value
cij = af(cij, mf(adata[i][ib], bvalues[kb]));
}
}
// check column has been processed and value != 0
if (last === mark && !eq(cij, zero)) {
// push row & value
cindex.push(i);
cvalues.push(cij);
}
}
}
}
// update ptr
cptr[bcolumns] = cindex.length;
// return sparse matrix
return c;
};
/**
* C = A * B
*
* @param {Matrix} a SparseMatrix (MxN)
* @param {Matrix} b Dense Vector (N)
*
* @return {Matrix} SparseMatrix (M, 1)
*/
var _multiplySparseMatrixVector = function (a, b) {
// a sparse
var avalues = a._values;
var aindex = a._index;
var aptr = a._ptr;
var adt = a._datatype;
// validate a matrix
if (!avalues)
throw new Error('Cannot multiply Pattern only Matrix times Dense Matrix');
// b dense
var bdata = b._data;
var bdt = b._datatype;
// rows & columns
var arows = a._size[0];
var brows = b._size[0];
// result
var cvalues = [];
var cindex = [];
var cptr = [];
// datatype
var dt;
// addScalar signature to use
var af = addScalar;
// multiplyScalar signature to use
var mf = multiplyScalar;
// equalScalar signature to use
var eq = equalScalar;
// zero value
var zero = 0;
// process data types
if (adt && bdt && adt === bdt && typeof adt === 'string') {
// datatype
dt = adt;
// find signatures that matches (dt, dt)
af = typed.find(addScalar, [dt, dt]);
mf = typed.find(multiplyScalar, [dt, dt]);
eq = typed.find(equalScalar, [dt, dt]);
// convert 0 to the same datatype
zero = typed.convert(0, dt);
}
// workspace
var x = [];
// vector with marks indicating a value x[i] exists in a given column
var w = [];
// update ptr
cptr[0] = 0;
// rows in b
for (var ib = 0; ib < brows; ib++) {
// b[ib]
var vbi = bdata[ib];
// check b[ib] != 0, avoid loops
if (!eq(vbi, zero)) {
// A values & index in ib column
for (var ka0 = aptr[ib], ka1 = aptr[ib + 1], ka = ka0; ka < ka1; ka++) {
// a row
var ia = aindex[ka];
// check value exists in current j
if (!w[ia]) {
// ia is new entry in j
w[ia] = true;
// add i to pattern of C
cindex.push(ia);
// x(ia) = A
x[ia] = mf(vbi, avalues[ka]);
}
else {
// i exists in C already
x[ia] = af(x[ia], mf(vbi, avalues[ka]));
}
}
}
}
// copy values from x to column jb of c
for (var p1 = cindex.length, p = 0; p < p1; p++) {
// row
var ic = cindex[p];
// copy value
cvalues[p] = x[ic];
}
// update ptr
cptr[1] = cindex.length;
// return sparse matrix
return new SparseMatrix({
values : cvalues,
index: cindex,
ptr: cptr,
size: [arows, 1],
datatype: dt
});
};
/**
* C = A * B
*
* @param {Matrix} a SparseMatrix (MxN)
* @param {Matrix} b DenseMatrix (NxC)
*
* @return {Matrix} SparseMatrix (MxC)
*/
var _multiplySparseMatrixDenseMatrix = function (a, b) {
// a sparse
var avalues = a._values;
var aindex = a._index;
var aptr = a._ptr;
var adt = a._datatype;
// validate a matrix
if (!avalues)
throw new Error('Cannot multiply Pattern only Matrix times Dense Matrix');
// b dense
var bdata = b._data;
var bdt = b._datatype;
// rows & columns
var arows = a._size[0];
var brows = b._size[0];
var bcolumns = b._size[1];
// datatype
var dt;
// addScalar signature to use
var af = addScalar;
// multiplyScalar signature to use
var mf = multiplyScalar;
// equalScalar signature to use
var eq = equalScalar;
// zero value
var zero = 0;
// process data types
if (adt && bdt && adt === bdt && typeof adt === 'string') {
// datatype
dt = adt;
// find signatures that matches (dt, dt)
af = typed.find(addScalar, [dt, dt]);
mf = typed.find(multiplyScalar, [dt, dt]);
eq = typed.find(equalScalar, [dt, dt]);
// convert 0 to the same datatype
zero = typed.convert(0, dt);
}
// result
var cvalues = [];
var cindex = [];
var cptr = [];
// c matrix
var c = new SparseMatrix({
values : cvalues,
index: cindex,
ptr: cptr,
size: [arows, bcolumns],
datatype: dt
});
// workspace
var x = [];
// vector with marks indicating a value x[i] exists in a given column
var w = [];
// loop b columns
for (var jb = 0; jb < bcolumns; jb++) {
// update ptr
cptr[jb] = cindex.length;
// mark in workspace for current column
var mark = jb + 1;
// rows in jb
for (var ib = 0; ib < brows; ib++) {
// b[ib, jb]
var vbij = bdata[ib][jb];
// check b[ib, jb] != 0, avoid loops
if (!eq(vbij, zero)) {
// A values & index in ib column
for (var ka0 = aptr[ib], ka1 = aptr[ib + 1], ka = ka0; ka < ka1; ka++) {
// a row
var ia = aindex[ka];
// check value exists in current j
if (w[ia] !== mark) {
// ia is new entry in j
w[ia] = mark;
// add i to pattern of C
cindex.push(ia);
// x(ia) = A
x[ia] = mf(vbij, avalues[ka]);
}
else {
// i exists in C already
x[ia] = af(x[ia], mf(vbij, avalues[ka]));
}
}
}
}
// copy values from x to column jb of c
for (var p0 = cptr[jb], p1 = cindex.length, p = p0; p < p1; p++) {
// row
var ic = cindex[p];
// copy value
cvalues[p] = x[ic];
}
}
// update ptr
cptr[bcolumns] = cindex.length;
// return sparse matrix
return c;
};
/**
* C = A * B
*
* @param {Matrix} a SparseMatrix (MxN)
* @param {Matrix} b SparseMatrix (NxC)
*
* @return {Matrix} SparseMatrix (MxC)
*/
var _multiplySparseMatrixSparseMatrix = function (a, b) {
// a sparse
var avalues = a._values;
var aindex = a._index;
var aptr = a._ptr;
var adt = a._datatype;
// b sparse
var bvalues = b._values;
var bindex = b._index;
var bptr = b._ptr;
var bdt = b._datatype;
// rows & columns
var arows = a._size[0];
var bcolumns = b._size[1];
// flag indicating both matrices (a & b) contain data
var values = avalues && bvalues;
// datatype
var dt;
// addScalar signature to use
var af = addScalar;
// multiplyScalar signature to use
var mf = multiplyScalar;
// process data types
if (adt && bdt && adt === bdt && typeof adt === 'string') {
// datatype
dt = adt;
// find signatures that matches (dt, dt)
af = typed.find(addScalar, [dt, dt]);
mf = typed.find(multiplyScalar, [dt, dt]);
}
// result
var cvalues = values ? [] : undefined;
var cindex = [];
var cptr = [];
// c matrix
var c = new SparseMatrix({
values : cvalues,
index: cindex,
ptr: cptr,
size: [arows, bcolumns],
datatype: dt
});
// workspace
var x = values ? [] : undefined;
// vector with marks indicating a value x[i] exists in a given column
var w = [];
// variables
var ka, ka0, ka1, kb, kb0, kb1, ia, ib;
// loop b columns
for (var jb = 0; jb < bcolumns; jb++) {
// update ptr
cptr[jb] = cindex.length;
// mark in workspace for current column
var mark = jb + 1;
// B values & index in j
for (kb0 = bptr[jb], kb1 = bptr[jb + 1], kb = kb0; kb < kb1; kb++) {
// b row
ib = bindex[kb];
// check we need to process values
if (values) {
// loop values in a[:,ib]
for (ka0 = aptr[ib], ka1 = aptr[ib + 1], ka = ka0; ka < ka1; ka++) {
// row
ia = aindex[ka];
// check value exists in current j
if (w[ia] !== mark) {
// ia is new entry in j
w[ia] = mark;
// add i to pattern of C
cindex.push(ia);
// x(ia) = A
x[ia] = mf(bvalues[kb], avalues[ka]);
}
else {
// i exists in C already
x[ia] = af(x[ia], mf(bvalues[kb], avalues[ka]));
}
}
}
else {
// loop values in a[:,ib]
for (ka0 = aptr[ib], ka1 = aptr[ib + 1], ka = ka0; ka < ka1; ka++) {
// row
ia = aindex[ka];
// check value exists in current j
if (w[ia] !== mark) {
// ia is new entry in j
w[ia] = mark;
// add i to pattern of C
cindex.push(ia);
}
}
}
}
// check we need to process matrix values (pattern matrix)
if (values) {
// copy values from x to column jb of c
for (var p0 = cptr[jb], p1 = cindex.length, p = p0; p < p1; p++) {
// row
var ic = cindex[p];
// copy value
cvalues[p] = x[ic];
}
}
}
// update ptr
cptr[bcolumns] = cindex.length;
// return sparse matrix
return c;
};
multiply.toTex = {
2: '\\left(${args[0]}' + latex.operators['multiply'] + '${args[1]}\\right)'
};
return multiply;
}
exports.name = 'multiply';
exports.factory = factory;

View File

@@ -0,0 +1,57 @@
'use strict';
function factory(type, config, load, typed) {
/**
* Multiply two scalar values, `x * y`.
* This function is meant for internal use: it is used by the public function
* `multiply`
*
* This function does not support collections (Array or Matrix), and does
* not validate the number of of inputs.
*
* @param {number | BigNumber | Fraction | Complex | Unit} x First value to multiply
* @param {number | BigNumber | Fraction | Complex} y Second value to multiply
* @return {number | BigNumber | Fraction | Complex | Unit} Multiplication of `x` and `y`
* @private
*/
var multiplyScalar = typed('multiplyScalar', {
'number, number': function (x, y) {
return x * y;
},
'Complex, Complex': function (x, y) {
return x.mul(y);
},
'BigNumber, BigNumber': function (x, y) {
return x.times(y);
},
'Fraction, Fraction': function (x, y) {
return x.mul(y);
},
'number | Fraction | BigNumber | Complex, Unit': function (x, y) {
var res = y.clone();
res.value = (res.value === null) ? res._normalize(x) : multiplyScalar(res.value, x);
return res;
},
'Unit, number | Fraction | BigNumber | Complex': function (x, y) {
var res = x.clone();
res.value = (res.value === null) ? res._normalize(y) : multiplyScalar(res.value, y);
return res;
},
'Unit, Unit': function (x, y) {
return x.multiply(y);
}
});
return multiplyScalar;
}
exports.factory = factory;

View File

@@ -0,0 +1,215 @@
'use strict';
function factory (type, config, load, typed) {
var abs = load(require('../arithmetic/abs'));
var add = load(require('../arithmetic/add'));
var pow = load(require('../arithmetic/pow'));
var sqrt = load(require('../arithmetic/sqrt'));
var multiply = load(require('../arithmetic/multiply'));
var equalScalar = load(require('../relational/equalScalar'));
var larger = load(require('../relational/larger'));
var smaller = load(require('../relational/smaller'));
var matrix = load(require('../../type/matrix/function/matrix'));
var trace = load(require('../matrix/trace'));
var transpose = load(require('../matrix/transpose'));
/**
* Calculate the norm of a number, vector or matrix.
*
* The second parameter p is optional. If not provided, it defaults to 2.
*
* Syntax:
*
* math.norm(x)
* math.norm(x, p)
*
* Examples:
*
* math.abs(-3.5); // returns 3.5
* math.norm(-3.5); // returns 3.5
*
* math.norm(math.complex(3, -4)); // returns 5
*
* math.norm([1, 2, -3], Infinity); // returns 3
* math.norm([1, 2, -3], -Infinity); // returns 1
*
* math.norm([3, 4], 2); // returns 5
*
* math.norm([[1, 2], [3, 4]], 1) // returns 6
* math.norm([[1, 2], [3, 4]], 'inf'); // returns 7
* math.norm([[1, 2], [3, 4]], 'fro'); // returns 5.477225575051661
*
* See also:
*
* abs, hypot
*
* @param {number | BigNumber | Complex | Array | Matrix} x
* Value for which to calculate the norm
* @param {number | BigNumber | string} [p=2]
* Vector space.
* Supported numbers include Infinity and -Infinity.
* Supported strings are: 'inf', '-inf', and 'fro' (The Frobenius norm)
* @return {number | BigNumber} the p-norm
*/
var norm = typed('norm', {
'number': Math.abs,
'Complex': function (x) {
return x.abs();
},
'BigNumber': function (x) {
// norm(x) = abs(x)
return x.abs();
},
'boolean | null' : function (x) {
// norm(x) = abs(x)
return Math.abs(x);
},
'Array': function (x) {
return _norm(matrix(x), 2);
},
'Matrix': function (x) {
return _norm(x, 2);
},
'number | Complex | BigNumber | boolean | null, number | BigNumber | string': function (x) {
// ignore second parameter, TODO: remove the option of second parameter for these types
return norm(x);
},
'Array, number | BigNumber | string': function (x, p) {
return _norm(matrix(x), p);
},
'Matrix, number | BigNumber | string': function (x, p) {
return _norm(x, p);
}
});
/**
* Calculate the norm for an array
* @param {Array} x
* @param {number | string} p
* @returns {number} Returns the norm
* @private
*/
function _norm (x, p) {
// size
var sizeX = x.size();
// check if it is a vector
if (sizeX.length == 1) {
// check p
if (p === Number.POSITIVE_INFINITY || p === 'inf') {
// norm(x, Infinity) = max(abs(x))
var pinf = 0;
// skip zeros since abs(0) == 0
x.forEach(
function (value) {
var v = abs(value);
if (larger(v, pinf))
pinf = v;
},
true);
return pinf;
}
if (p === Number.NEGATIVE_INFINITY || p === '-inf') {
// norm(x, -Infinity) = min(abs(x))
var ninf;
// skip zeros since abs(0) == 0
x.forEach(
function (value) {
var v = abs(value);
if (!ninf || smaller(v, ninf))
ninf = v;
},
true);
return ninf || 0;
}
if (p === 'fro') {
return _norm(x, 2);
}
if (typeof p === 'number' && !isNaN(p)) {
// check p != 0
if (!equalScalar(p, 0)) {
// norm(x, p) = sum(abs(xi) ^ p) ^ 1/p
var n = 0;
// skip zeros since abs(0) == 0
x.forEach(
function (value) {
n = add(pow(abs(value), p), n);
},
true);
return pow(n, 1 / p);
}
return Number.POSITIVE_INFINITY;
}
// invalid parameter value
throw new Error('Unsupported parameter value');
}
// MxN matrix
if (sizeX.length == 2) {
// check p
if (p === 1) {
// norm(x) = the largest column sum
var c = [];
// result
var maxc = 0;
// skip zeros since abs(0) == 0
x.forEach(
function (value, index) {
var j = index[1];
var cj = add(c[j] || 0, abs(value));
if (larger(cj, maxc))
maxc = cj;
c[j] = cj;
},
true);
return maxc;
}
if (p === Number.POSITIVE_INFINITY || p === 'inf') {
// norm(x) = the largest row sum
var r = [];
// result
var maxr = 0;
// skip zeros since abs(0) == 0
x.forEach(
function (value, index) {
var i = index[0];
var ri = add(r[i] || 0, abs(value));
if (larger(ri, maxr))
maxr = ri;
r[i] = ri;
},
true);
return maxr;
}
if (p === 'fro') {
// norm(x) = sqrt(sum(diag(x'x)))
return sqrt(trace(multiply(transpose(x), x)));
}
if (p === 2) {
// not implemented
throw new Error('Unsupported parameter value, missing implementation of matrix singular value decomposition');
}
// invalid parameter value
throw new Error('Unsupported parameter value');
}
}
norm.toTex = {
1: '\\left\\|${args[0]}\\right\\|',
2: undefined // use default template
};
return norm;
}
exports.name = 'norm';
exports.factory = factory;

View File

@@ -0,0 +1,299 @@
'use strict';
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
var algorithm01 = load(require('../../type/matrix/utils/algorithm01'));
var algorithm02 = load(require('../../type/matrix/utils/algorithm02'));
var algorithm06 = load(require('../../type/matrix/utils/algorithm06'));
var algorithm11 = load(require('../../type/matrix/utils/algorithm11'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Calculate the nth root of a value.
* The principal nth root of a positive real number A, is the positive real
* solution of the equation
*
* x^root = A
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.nthRoot(a)
* math.nthRoot(a, root)
*
* Examples:
*
* math.nthRoot(9, 2); // returns 3, as 3^2 == 9
* math.sqrt(9); // returns 3, as 3^2 == 9
* math.nthRoot(64, 3); // returns 4, as 4^3 == 64
*
* See also:
*
* sqrt, pow
*
* @param {number | BigNumber | Array | Matrix | Complex} a
* Value for which to calculate the nth root
* @param {number | BigNumber} [root=2] The root.
* @return {number | Complex | Array | Matrix} Returns the nth root of `a`
*/
var nthRoot = typed('nthRoot', {
'number': function (x) {
return _nthRoot(x, 2);
},
'number, number': _nthRoot,
'BigNumber': function (x) {
return _bigNthRoot(x, new type.BigNumber(2));
},
'Complex' : function(x) {
return _nthComplexRoot(x, 2);
},
'Complex, number' : _nthComplexRoot,
'BigNumber, BigNumber': _bigNthRoot,
'Array | Matrix': function (x) {
return nthRoot(x, 2);
},
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// density must be one (no zeros in matrix)
if (y.density() === 1) {
// sparse + sparse
c = algorithm06(x, y, nthRoot);
}
else {
// throw exception
throw new Error('Root must be non-zero');
}
break;
default:
// sparse + dense
c = algorithm02(y, x, nthRoot, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// density must be one (no zeros in matrix)
if (y.density() === 1) {
// dense + sparse
c = algorithm01(x, y, nthRoot, false);
}
else {
// throw exception
throw new Error('Root must be non-zero');
}
break;
default:
// dense + dense
c = algorithm13(x, y, nthRoot);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return nthRoot(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return nthRoot(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return nthRoot(x, matrix(y));
},
'Matrix, number | BigNumber': function (x, y) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm11(x, y, nthRoot, false);
break;
default:
c = algorithm14(x, y, nthRoot, false);
break;
}
return c;
},
'number | BigNumber, Matrix': function (x, y) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
// density must be one (no zeros in matrix)
if (y.density() === 1) {
// sparse - scalar
c = algorithm11(y, x, nthRoot, true);
}
else {
// throw exception
throw new Error('Root must be non-zero');
}
break;
default:
c = algorithm14(y, x, nthRoot, true);
break;
}
return c;
},
'Array, number | BigNumber': function (x, y) {
// use matrix implementation
return nthRoot(matrix(x), y).valueOf();
},
'number | BigNumber, Array': function (x, y) {
// use matrix implementation
return nthRoot(x, matrix(y)).valueOf();
}
});
nthRoot.toTex = {2: '\\sqrt[${args[1]}]{${args[0]}}'};
return nthRoot;
/**
* Calculate the nth root of a for BigNumbers, solve x^root == a
* http://rosettacode.org/wiki/Nth_root#JavaScript
* @param {BigNumber} a
* @param {BigNumber} root
* @private
*/
function _bigNthRoot(a, root) {
var precision = type.BigNumber.precision;
var Big = type.BigNumber.clone({precision: precision + 2});
var zero = new type.BigNumber(0);
var one = new Big(1);
var inv = root.isNegative();
if (inv) {
root = root.neg();
}
if (root.isZero()) {
throw new Error('Root must be non-zero');
}
if (a.isNegative() && !root.abs().mod(2).equals(1)) {
throw new Error('Root must be odd when a is negative.');
}
// edge cases zero and infinity
if (a.isZero()) {
return inv ? new Big(Infinity) : 0;
}
if (!a.isFinite()) {
return inv ? zero : a;
}
var x = a.abs().pow(one.div(root));
// If a < 0, we require that root is an odd integer,
// so (-1) ^ (1/root) = -1
x = a.isNeg() ? x.neg() : x;
return new type.BigNumber((inv ? one.div(x) : x).toPrecision(precision));
}
}
/**
* Calculate the nth root of a, solve x^root == a
* http://rosettacode.org/wiki/Nth_root#JavaScript
* @param {number} a
* @param {number} root
* @private
*/
function _nthRoot(a, root) {
var inv = root < 0;
if (inv) {
root = -root;
}
if (root === 0) {
throw new Error('Root must be non-zero');
}
if (a < 0 && (Math.abs(root) % 2 != 1)) {
throw new Error('Root must be odd when a is negative.');
}
// edge cases zero and infinity
if (a == 0) {
return inv ? Infinity : 0;
}
if (!isFinite(a)) {
return inv ? 0 : a;
}
var x = Math.pow(Math.abs(a), 1/root);
// If a < 0, we require that root is an odd integer,
// so (-1) ^ (1/root) = -1
x = a < 0 ? -x : x;
return inv ? 1 / x : x;
// Very nice algorithm, but fails with nthRoot(-2, 3).
// Newton's method has some well-known problems at times:
// https://en.wikipedia.org/wiki/Newton%27s_method#Failure_analysis
/*
var x = 1; // Initial guess
var xPrev = 1;
var i = 0;
var iMax = 10000;
do {
var delta = (a / Math.pow(x, root - 1) - x) / root;
xPrev = x;
x = x + delta;
i++;
}
while (xPrev !== x && i < iMax);
if (xPrev !== x) {
throw new Error('Function nthRoot failed to converge');
}
return inv ? 1 / x : x;
*/
}
/**
* Calculate the nth root of a Complex Number a using De Moviers Theorem.
* @param {Complex} a
* @param {number} root
* @return {Array} array or n Complex Roots in Polar Form.
*/
function _nthComplexRoot(a, root) {
if (root < 0) throw new Error('Root must be greater than zero');
if (root === 0) throw new Error('Root must be non-zero');
if (root % 1 !== 0) throw new Error('Root must be an integer');
var arg = a.arg();
var abs = a.abs();
var roots = [];
var r = Math.pow(abs, 1/root);
for(var k = 0; k < root; k++) {
roots.push({r: r, phi: (arg + 2 * Math.PI * k)/root});
}
return roots;
}
exports.name = 'nthRoot';
exports.factory = factory;

View File

@@ -0,0 +1,184 @@
'use strict';
var isInteger = require('../../utils/number').isInteger;
var size = require('../../utils/array').size;
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
var eye = load(require('../matrix/eye'));
var multiply = load(require('./multiply'));
var matrix = load(require('../../type/matrix/function/matrix'));
var fraction = load(require('../../type/fraction/function/fraction'));
var number = load(require('../../type/number'));
/**
* Calculates the power of x to y, `x ^ y`.
* Matrix exponentiation is supported for square matrices `x`, and positive
* integer exponents `y`.
*
* For cubic roots of negative numbers, the function returns the principal
* root by default. In order to let the function return the real root,
* math.js can be configured with `math.config({predictable: true})`.
* To retrieve all cubic roots of a value, use `math.cbrt(x, true)`.
*
* Syntax:
*
* math.pow(x, y)
*
* Examples:
*
* math.pow(2, 3); // returns number 8
*
* var a = math.complex(2, 3);
* math.pow(a, 2) // returns Complex -5 + 12i
*
* var b = [[1, 2], [4, 3]];
* math.pow(b, 2); // returns Array [[9, 8], [16, 17]]
*
* See also:
*
* multiply, sqrt, cbrt, nthRoot
*
* @param {number | BigNumber | Complex | Array | Matrix} x The base
* @param {number | BigNumber | Complex} y The exponent
* @return {number | BigNumber | Complex | Array | Matrix} The value of `x` to the power `y`
*/
var pow = typed('pow', {
'number, number': _pow,
'Complex, Complex': function (x, y) {
return x.pow(y);
},
'BigNumber, BigNumber': function (x, y) {
if (y.isInteger() || x >= 0 || config.predictable) {
return x.pow(y);
}
else {
return new type.Complex(x.toNumber(), 0).pow(y.toNumber(), 0);
}
},
'Fraction, Fraction': function (x, y) {
if (y.d !== 1) {
if (config.predictable) {
throw new Error('Function pow does not support non-integer exponents for fractions.');
}
else {
return _pow(x.valueOf(), y.valueOf());
}
}
else {
return x.pow(y);
}
},
'Array, number': _powArray,
'Array, BigNumber': function (x, y) {
return _powArray(x, y.toNumber());
},
'Matrix, number': _powMatrix,
'Matrix, BigNumber': function (x, y) {
return _powMatrix(x, y.toNumber());
},
'Unit, number': function (x, y) {
return x.pow(y);
}
});
/**
* Calculates the power of x to y, x^y, for two numbers.
* @param {number} x
* @param {number} y
* @return {number | Complex} res
* @private
*/
function _pow(x, y) {
// Alternatively could define a 'realmode' config option or something, but
// 'predictable' will work for now
if (config.predictable && !isInteger(y) && x < 0) {
// Check to see if y can be represented as a fraction
try {
var yFrac = fraction(y);
var yNum = number(yFrac);
if(y === yNum || Math.abs((y - yNum) / y) < 1e-14) {
if(yFrac.d % 2 === 1) {
return (yFrac.n % 2 === 0 ? 1 : -1) * Math.pow(-x, y);
}
}
}
catch (ex) {
// fraction() throws an error if y is Infinity, etc.
}
// Unable to express y as a fraction, so continue on
}
if (isInteger(y) || x >= 0 || config.predictable) {
return Math.pow(x, y);
}
else {
return new type.Complex(x, 0).pow(y, 0);
}
}
/**
* Calculate the power of a 2d array
* @param {Array} x must be a 2 dimensional, square matrix
* @param {number} y a positive, integer value
* @returns {Array}
* @private
*/
function _powArray(x, y) {
if (!isInteger(y) || y < 0) {
throw new TypeError('For A^b, b must be a positive integer (value is ' + y + ')');
}
// verify that A is a 2 dimensional square matrix
var s = size(x);
if (s.length != 2) {
throw new Error('For A^b, A must be 2 dimensional (A has ' + s.length + ' dimensions)');
}
if (s[0] != s[1]) {
throw new Error('For A^b, A must be square (size is ' + s[0] + 'x' + s[1] + ')');
}
var res = eye(s[0]).valueOf();
var px = x;
while (y >= 1) {
if ((y & 1) == 1) {
res = multiply(px, res);
}
y >>= 1;
px = multiply(px, px);
}
return res;
}
/**
* Calculate the power of a 2d matrix
* @param {Matrix} x must be a 2 dimensional, square matrix
* @param {number} y a positive, integer value
* @returns {Matrix}
* @private
*/
function _powMatrix (x, y) {
return matrix(_powArray(x.valueOf(), y));
}
pow.toTex = {
2: '\\left(${args[0]}\\right)' + latex.operators['pow'] + '{${args[1]}}'
};
return pow;
}
exports.name = 'pow';
exports.factory = factory;

View File

@@ -0,0 +1,174 @@
'use strict';
var isInteger = require('../../utils/number').isInteger;
var toFixed = require('../../utils/number').toFixed;
var deepMap = require('../../utils/collection/deepMap');
var NO_INT = 'Number of decimals in function round must be an integer';
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
var equalScalar = load(require('../relational/equalScalar'));
var zeros = load(require('../matrix/zeros'));
var algorithm11 = load(require('../../type/matrix/utils/algorithm11'));
var algorithm12 = load(require('../../type/matrix/utils/algorithm12'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Round a value towards the nearest integer.
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.round(x)
* math.round(x, n)
*
* Examples:
*
* math.round(3.2); // returns number 3
* math.round(3.8); // returns number 4
* math.round(-4.2); // returns number -4
* math.round(-4.7); // returns number -5
* math.round(math.pi, 3); // returns number 3.142
* math.round(123.45678, 2); // returns number 123.46
*
* var c = math.complex(3.2, -2.7);
* math.round(c); // returns Complex 3 - 3i
*
* math.round([3.2, 3.8, -4.7]); // returns Array [3, 4, -5]
*
* See also:
*
* ceil, fix, floor
*
* @param {number | BigNumber | Fraction | Complex | Array | Matrix} x Number to be rounded
* @param {number | BigNumber | Array} [n=0] Number of decimals
* @return {number | BigNumber | Fraction | Complex | Array | Matrix} Rounded value
*/
var round = typed('round', {
'number': Math.round,
'number, number': function (x, n) {
if (!isInteger(n)) {throw new TypeError(NO_INT);}
if (n < 0 || n > 15) {throw new Error('Number of decimals in function round must be in te range of 0-15');}
return _round(x, n);
},
'Complex': function (x) {
return x.round();
},
'Complex, number': function (x, n) {
if (n % 1) {throw new TypeError(NO_INT);}
return x.round(n);
},
'Complex, BigNumber': function (x, n) {
if (!n.isInteger()) {throw new TypeError(NO_INT);}
var _n = n.toNumber();
return x.round(_n);
},
'number, BigNumber': function (x, n) {
if (!n.isInteger()) {throw new TypeError(NO_INT);}
return new type.BigNumber(x).toDecimalPlaces(n.toNumber());
},
'BigNumber': function (x) {
return x.toDecimalPlaces(0);
},
'BigNumber, BigNumber': function (x, n) {
if (!n.isInteger()) {throw new TypeError(NO_INT);}
return x.toDecimalPlaces(n.toNumber());
},
'Fraction': function (x) {
return x.round();
},
'Fraction, number': function (x, n) {
if (n % 1) {throw new TypeError(NO_INT);}
return x.round(n);
},
'Array | Matrix': function (x) {
// deep map collection, skip zeros since round(0) = 0
return deepMap(x, round, true);
},
'Matrix, number | BigNumber': function (x, y) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm11(x, y, round, false);
break;
default:
c = algorithm14(x, y, round, false);
break;
}
return c;
},
'number | Complex | BigNumber, Matrix': function (x, y) {
// check scalar is zero
if (!equalScalar(x, 0)) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm12(y, x, round, true);
break;
default:
c = algorithm14(y, x, round, true);
break;
}
return c;
}
// do not execute algorithm, result will be a zero matrix
return zeros(y.size(), y.storage());
},
'Array, number | BigNumber': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, round, false).valueOf();
},
'number | Complex | BigNumber, Array': function (x, y) {
// use matrix implementation
return algorithm14(matrix(y), x, round, true).valueOf();
}
});
round.toTex = {
1: '\\left\\lfloor${args[0]}\\right\\rceil',
2: undefined // use default template
};
return round;
}
/**
* round a number to the given number of decimals, or to zero if decimals is
* not provided
* @param {number} value
* @param {number} decimals number of decimals, between 0 and 15 (0 by default)
* @return {number} roundedValue
* @private
*/
function _round (value, decimals) {
return parseFloat(toFixed(value, decimals));
}
exports.name = 'round';
exports.factory = factory;

View File

@@ -0,0 +1,69 @@
'use strict';
var number = require('../../utils/number');
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
/**
* Compute the sign of a value. The sign of a value x is:
*
* - 1 when x > 1
* - -1 when x < 0
* - 0 when x == 0
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.sign(x)
*
* Examples:
*
* math.sign(3.5); // returns 1
* math.sign(-4.2); // returns -1
* math.sign(0); // returns 0
*
* math.sign([3, 5, -2, 0, 2]); // returns [1, 1, -1, 0, 1]
*
* See also:
*
* abs
*
* @param {number | BigNumber | Fraction | Complex | Array | Matrix | Unit} x
* The number for which to determine the sign
* @return {number | BigNumber | Fraction | Complex | Array | Matrix | Unit}e
* The sign of `x`
*/
var sign = typed('sign', {
'number': number.sign,
'Complex': function (x) {
return x.sign();
},
'BigNumber': function (x) {
return new type.BigNumber(x.cmp(0));
},
'Fraction': function (x) {
return new type.Fraction(x.s, 1);
},
'Array | Matrix': function (x) {
// deep map collection, skip zeros since sign(0) = 0
return deepMap(x, sign, true);
},
'Unit': function(x) {
return sign(x.value);
}
});
sign.toTex = {1: '\\mathrm{${name}}\\left(${args[0]}\\right)'};
return sign;
}
exports.name = 'sign';
exports.factory = factory;

View File

@@ -0,0 +1,80 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
/**
* Calculate the square root of a value.
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.sqrt(x)
*
* Examples:
*
* math.sqrt(25); // returns 5
* math.square(5); // returns 25
* math.sqrt(-4); // returns Complex 2i
*
* See also:
*
* square, multiply, cube, cbrt
*
* @param {number | BigNumber | Complex | Array | Matrix | Unit} x
* Value for which to calculate the square root.
* @return {number | BigNumber | Complex | Array | Matrix | Unit}
* Returns the square root of `x`
*/
var sqrt = typed('sqrt', {
'number': _sqrtNumber,
'Complex': function (x) {
return x.sqrt();
},
'BigNumber': function (x) {
if (!x.isNegative() || config.predictable) {
return x.sqrt();
}
else {
// negative value -> downgrade to number to do complex value computation
return _sqrtNumber(x.toNumber());
}
},
'Array | Matrix': function (x) {
// deep map collection, skip zeros since sqrt(0) = 0
return deepMap(x, sqrt, true);
},
'Unit': function (x) {
// Someday will work for complex units when they are implemented
return x.pow(0.5);
}
});
/**
* Calculate sqrt for a number
* @param {number} x
* @returns {number | Complex} Returns the square root of x
* @private
*/
function _sqrtNumber(x) {
if (x >= 0 || config.predictable) {
return Math.sqrt(x);
}
else {
return new type.Complex(x, 0).sqrt();
}
}
sqrt.toTex = {1: '\\sqrt{${args[0]}}'};
return sqrt;
}
exports.name = 'sqrt';
exports.factory = factory;

View File

@@ -0,0 +1,65 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
/**
* Compute the square of a value, `x * x`.
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.square(x)
*
* Examples:
*
* math.square(2); // returns number 4
* math.square(3); // returns number 9
* math.pow(3, 2); // returns number 9
* math.multiply(3, 3); // returns number 9
*
* math.square([1, 2, 3, 4]); // returns Array [1, 4, 9, 16]
*
* See also:
*
* multiply, cube, sqrt, pow
*
* @param {number | BigNumber | Fraction | Complex | Array | Matrix | Unit} x
* Number for which to calculate the square
* @return {number | BigNumber | Fraction | Complex | Array | Matrix | Unit}
* Squared value
*/
var square = typed('square', {
'number': function (x) {
return x * x;
},
'Complex': function (x) {
return x.mul(x);
},
'BigNumber': function (x) {
return x.times(x);
},
'Fraction': function (x) {
return x.mul(x);
},
'Array | Matrix': function (x) {
// deep map collection, skip zeros since square(0) = 0
return deepMap(x, square, true);
},
'Unit': function(x) {
return x.pow(2);
}
});
square.toTex = {1: '\\left(${args[0]}\\right)^2'};
return square;
}
exports.name = 'square';
exports.factory = factory;

View File

@@ -0,0 +1,199 @@
'use strict';
var DimensionError = require('../../error/DimensionError');
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
var matrix = load(require('../../type/matrix/function/matrix'));
var addScalar = load(require('./addScalar'));
var unaryMinus = load(require('./unaryMinus'));
var algorithm01 = load(require('../../type/matrix/utils/algorithm01'));
var algorithm03 = load(require('../../type/matrix/utils/algorithm03'));
var algorithm05 = load(require('../../type/matrix/utils/algorithm05'));
var algorithm10 = load(require('../../type/matrix/utils/algorithm10'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
// TODO: split function subtract in two: subtract and subtractScalar
/**
* Subtract two values, `x - y`.
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.subtract(x, y)
*
* Examples:
*
* math.subtract(5.3, 2); // returns number 3.3
*
* var a = math.complex(2, 3);
* var b = math.complex(4, 1);
* math.subtract(a, b); // returns Complex -2 + 2i
*
* math.subtract([5, 7, 4], 4); // returns Array [1, 3, 0]
*
* var c = math.unit('2.1 km');
* var d = math.unit('500m');
* math.subtract(c, d); // returns Unit 1.6 km
*
* See also:
*
* add
*
* @param {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} x
* Initial value
* @param {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} y
* Value to subtract from `x`
* @return {number | BigNumber | Fraction | Complex | Unit | Array | Matrix}
* Subtraction of `x` and `y`
*/
var subtract = typed('subtract', {
'number, number': function (x, y) {
return x - y;
},
'Complex, Complex': function (x, y) {
return x.sub(y);
},
'BigNumber, BigNumber': function (x, y) {
return x.minus(y);
},
'Fraction, Fraction': function (x, y) {
return x.sub(y);
},
'Unit, Unit': function (x, y) {
if (x.value == null) {
throw new Error('Parameter x contains a unit with undefined value');
}
if (y.value == null) {
throw new Error('Parameter y contains a unit with undefined value');
}
if (!x.equalBase(y)) {
throw new Error('Units do not match');
}
var res = x.clone();
res.value = subtract(res.value, y.value);
res.fixPrefix = false;
return res;
},
'Matrix, Matrix': function (x, y) {
// matrix sizes
var xsize = x.size();
var ysize = y.size();
// check dimensions
if (xsize.length !== ysize.length)
throw new DimensionError(xsize.length, ysize.length);
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse - sparse
c = algorithm05(x, y, subtract);
break;
default:
// sparse - dense
c = algorithm03(y, x, subtract, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense - sparse
c = algorithm01(x, y, subtract, false);
break;
default:
// dense - dense
c = algorithm13(x, y, subtract);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return subtract(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return subtract(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return subtract(x, matrix(y));
},
'Matrix, any': function (x, y) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
// algorithm 7 is faster than 9 since it calls f() for nonzero items only!
c = algorithm10(x, unaryMinus(y), addScalar);
break;
default:
c = algorithm14(x, y, subtract);
break;
}
return c;
},
'any, Matrix': function (x, y) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm10(y, x, subtract, true);
break;
default:
c = algorithm14(y, x, subtract, true);
break;
}
return c;
},
'Array, any': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, subtract, false).valueOf();
},
'any, Array': function (x, y) {
// use matrix implementation
return algorithm14(matrix(y), x, subtract, true).valueOf();
}
});
subtract.toTex = {
2: '\\left(${args[0]}' + latex.operators['subtract'] + '${args[1]}\\right)'
};
return subtract;
}
exports.name = 'subtract';
exports.factory = factory;

View File

@@ -0,0 +1,70 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
/**
* Inverse the sign of a value, apply a unary minus operation.
*
* For matrices, the function is evaluated element wise. Boolean values and
* strings will be converted to a number. For complex numbers, both real and
* complex value are inverted.
*
* Syntax:
*
* math.unaryMinus(x)
*
* Examples:
*
* math.unaryMinus(3.5); // returns -3.5
* math.unaryMinus(-4.2); // returns 4.2
*
* See also:
*
* add, subtract, unaryPlus
*
* @param {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} x Number to be inverted.
* @return {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} Returns the value with inverted sign.
*/
var unaryMinus = typed('unaryMinus', {
'number': function (x) {
return -x;
},
'Complex': function (x) {
return x.neg();
},
'BigNumber': function (x) {
return x.neg();
},
'Fraction': function (x) {
return x.neg();
},
'Unit': function (x) {
var res = x.clone();
res.value = unaryMinus(x.value);
return res;
},
'Array | Matrix': function (x) {
// deep map collection, skip zeros since unaryMinus(0) = 0
return deepMap(x, unaryMinus, true);
}
// TODO: add support for string
});
unaryMinus.toTex = {
1: latex.operators['unaryMinus'] + '\\left(${args[0]}\\right)'
};
return unaryMinus;
}
exports.name = 'unaryMinus';
exports.factory = factory;

View File

@@ -0,0 +1,72 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
/**
* Unary plus operation.
* Boolean values and strings will be converted to a number, numeric values will be returned as is.
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.unaryPlus(x)
*
* Examples:
*
* math.unaryPlus(3.5); // returns 3.5
* math.unaryPlus(1); // returns 1
*
* See also:
*
* unaryMinus, add, subtract
*
* @param {number | BigNumber | Fraction | string | Complex | Unit | Array | Matrix} x
* Input value
* @return {number | BigNumber | Fraction | Complex | Unit | Array | Matrix}
* Returns the input value when numeric, converts to a number when input is non-numeric.
*/
var unaryPlus = typed('unaryPlus', {
'number': function (x) {
return x;
},
'Complex': function (x) {
return x; // complex numbers are immutable
},
'BigNumber': function (x) {
return x; // bignumbers are immutable
},
'Fraction': function (x) {
return x; // fractions are immutable
},
'Unit': function (x) {
return x.clone();
},
'Array | Matrix': function (x) {
// deep map collection, skip zeros since unaryPlus(0) = 0
return deepMap(x, unaryPlus, true);
},
'boolean | string | null': function (x) {
// convert to a number or bignumber
return (config.number == 'BigNumber') ? new type.BigNumber(+x): +x;
}
});
unaryPlus.toTex = {
1: latex.operators['unaryPlus'] + '\\left(${args[0]}\\right)'
};
return unaryPlus;
}
exports.name = 'unaryPlus';
exports.factory = factory;

View File

@@ -0,0 +1,137 @@
'use strict';
var isInteger = require('../../utils/number').isInteger;
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
/**
* Calculate the extended greatest common divisor for two values.
* See http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm.
*
* Syntax:
*
* math.xgcd(a, b)
*
* Examples:
*
* math.xgcd(8, 12); // returns [4, -1, 1]
* math.gcd(8, 12); // returns 4
* math.xgcd(36163, 21199); // returns [1247, -7, 12]
*
* See also:
*
* gcd, lcm
*
* @param {number | BigNumber} a An integer number
* @param {number | BigNumber} b An integer number
* @return {Array} Returns an array containing 3 integers `[div, m, n]`
* where `div = gcd(a, b)` and `a*m + b*n = div`
*/
var xgcd = typed('xgcd', {
'number, number': _xgcd,
'BigNumber, BigNumber': _xgcdBigNumber
// TODO: implement support for Fraction
});
xgcd.toTex = undefined; // use default template
return xgcd;
/**
* Calculate xgcd for two numbers
* @param {number} a
* @param {number} b
* @return {number} result
* @private
*/
function _xgcd (a, b) {
// source: http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
var t, // used to swap two variables
q, // quotient
r, // remainder
x = 0, lastx = 1,
y = 1, lasty = 0;
if (!isInteger(a) || !isInteger(b)) {
throw new Error('Parameters in function xgcd must be integer numbers');
}
while (b) {
q = Math.floor(a / b);
r = a % b;
t = x;
x = lastx - q * x;
lastx = t;
t = y;
y = lasty - q * y;
lasty = t;
a = b;
b = r;
}
var res;
if (a < 0) {
res = [-a, -lastx, -lasty];
}
else {
res = [a, a ? lastx : 0, lasty];
}
return (config.matrix === 'Array') ? res : matrix(res);
}
/**
* Calculate xgcd for two BigNumbers
* @param {BigNumber} a
* @param {BigNumber} b
* @return {BigNumber[]} result
* @private
*/
function _xgcdBigNumber(a, b) {
// source: http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
var t, // used to swap two variables
q, // quotient
r, // remainder
zero = new type.BigNumber(0),
one = new type.BigNumber(1),
x = zero,
lastx = one,
y = one,
lasty = zero;
if (!a.isInt() || !b.isInt()) {
throw new Error('Parameters in function xgcd must be integer numbers');
}
while (!b.isZero()) {
q = a.div(b).floor();
r = a.mod(b);
t = x;
x = lastx.minus(q.times(x));
lastx = t;
t = y;
y = lasty.minus(q.times(y));
lasty = t;
a = b;
b = r;
}
var res;
if (a.lt(zero)) {
res = [a.neg(), lastx.neg(), lasty.neg()];
}
else {
res = [a, !a.isZero() ? lastx : 0, lasty];
}
return (config.matrix === 'Array') ? res : matrix(res);
}
}
exports.name = 'xgcd';
exports.factory = factory;

View File

@@ -0,0 +1,149 @@
'use strict';
var isInteger = require('../../utils/number').isInteger;
var bigBitAnd = require('../../utils/bignumber/bitAnd');
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
var matrix = load(require('../../type/matrix/function/matrix'));
var algorithm02 = load(require('../../type/matrix/utils/algorithm02'));
var algorithm06 = load(require('../../type/matrix/utils/algorithm06'));
var algorithm11 = load(require('../../type/matrix/utils/algorithm11'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Bitwise AND two values, `x & y`.
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.bitAnd(x, y)
*
* Examples:
*
* math.bitAnd(53, 131); // returns number 1
*
* math.bitAnd([1, 12, 31], 42); // returns Array [0, 8, 10]
*
* See also:
*
* bitNot, bitOr, bitXor, leftShift, rightArithShift, rightLogShift
*
* @param {number | BigNumber | Array | Matrix} x First value to and
* @param {number | BigNumber | Array | Matrix} y Second value to and
* @return {number | BigNumber | Array | Matrix} AND of `x` and `y`
*/
var bitAnd = typed('bitAnd', {
'number, number': function (x, y) {
if (!isInteger(x) || !isInteger(y)) {
throw new Error('Integers expected in function bitAnd');
}
return x & y;
},
'BigNumber, BigNumber': bigBitAnd,
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse & sparse
c = algorithm06(x, y, bitAnd, false);
break;
default:
// sparse & dense
c = algorithm02(y, x, bitAnd, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense & sparse
c = algorithm02(x, y, bitAnd, false);
break;
default:
// dense & dense
c = algorithm13(x, y, bitAnd);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return bitAnd(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return bitAnd(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return bitAnd(x, matrix(y));
},
'Matrix, any': function (x, y) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm11(x, y, bitAnd, false);
break;
default:
c = algorithm14(x, y, bitAnd, false);
break;
}
return c;
},
'any, Matrix': function (x, y) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm11(y, x, bitAnd, true);
break;
default:
c = algorithm14(y, x, bitAnd, true);
break;
}
return c;
},
'Array, any': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, bitAnd, false).valueOf();
},
'any, Array': function (x, y) {
// use matrix implementation
return algorithm14(matrix(y), x, bitAnd, true).valueOf();
}
});
bitAnd.toTex = {
2: '\\left(${args[0]}' + latex.operators['bitAnd'] + '${args[1]}\\right)'
};
return bitAnd;
}
exports.name = 'bitAnd';
exports.factory = factory;

View File

@@ -0,0 +1,56 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
var bigBitNot = require('../../utils/bignumber/bitNot');
var isInteger = require('../../utils/number').isInteger;
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
/**
* Bitwise NOT value, `~x`.
* For matrices, the function is evaluated element wise.
* For units, the function is evaluated on the best prefix base.
*
* Syntax:
*
* math.bitNot(x)
*
* Examples:
*
* math.bitNot(1); // returns number -2
*
* math.bitNot([2, -3, 4]); // returns Array [-3, 2, 5]
*
* See also:
*
* bitAnd, bitOr, bitXor, leftShift, rightArithShift, rightLogShift
*
* @param {number | BigNumber | Array | Matrix} x Value to not
* @return {number | BigNumber | Array | Matrix} NOT of `x`
*/
var bitNot = typed('bitNot', {
'number': function (x) {
if (!isInteger(x)) {
throw new Error('Integer expected in function bitNot');
}
return ~x;
},
'BigNumber': bigBitNot,
'Array | Matrix': function (x) {
return deepMap(x, bitNot);
}
});
bitNot.toTex = {
1: latex.operators['bitNot'] + '\\left(${args[0]}\\right)'
};
return bitNot;
}
exports.name = 'bitNot';
exports.factory = factory;

View File

@@ -0,0 +1,149 @@
'use strict';
var isInteger = require('../../utils/number').isInteger;
var bigBitOr = require('../../utils/bignumber/bitOr');
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
var matrix = load(require('../../type/matrix/function/matrix'));
var algorithm01 = load(require('../../type/matrix/utils/algorithm01'));
var algorithm04 = load(require('../../type/matrix/utils/algorithm04'));
var algorithm10 = load(require('../../type/matrix/utils/algorithm10'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Bitwise OR two values, `x | y`.
* For matrices, the function is evaluated element wise.
* For units, the function is evaluated on the lowest print base.
*
* Syntax:
*
* math.bitOr(x, y)
*
* Examples:
*
* math.bitOr(1, 2); // returns number 3
*
* math.bitOr([1, 2, 3], 4); // returns Array [5, 6, 7]
*
* See also:
*
* bitAnd, bitNot, bitXor, leftShift, rightArithShift, rightLogShift
*
* @param {number | BigNumber | Array | Matrix} x First value to or
* @param {number | BigNumber | Array | Matrix} y Second value to or
* @return {number | BigNumber | Array | Matrix} OR of `x` and `y`
*/
var bitOr = typed('bitOr', {
'number, number': function (x, y) {
if (!isInteger(x) || !isInteger(y)) {
throw new Error('Integers expected in function bitOr');
}
return x | y;
},
'BigNumber, BigNumber': bigBitOr,
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse + sparse
c = algorithm04(x, y, bitOr);
break;
default:
// sparse + dense
c = algorithm01(y, x, bitOr, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense + sparse
c = algorithm01(x, y, bitOr, false);
break;
default:
c = algorithm13(x, y, bitOr);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return bitOr(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return bitOr(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return bitOr(x, matrix(y));
},
'Matrix, any': function (x, y) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm10(x, y, bitOr, false);
break;
default:
c = algorithm14(x, y, bitOr, false);
break;
}
return c;
},
'any, Matrix': function (x, y) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm10(y, x, bitOr, true);
break;
default:
c = algorithm14(y, x, bitOr, true);
break;
}
return c;
},
'Array, any': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, bitOr, false).valueOf();
},
'any, Array': function (x, y) {
// use matrix implementation
return algorithm14(matrix(y), x, bitOr, true).valueOf();
}
});
bitOr.toTex = {
2: '\\left(${args[0]}' + latex.operators['bitOr'] + '${args[1]}\\right)'
};
return bitOr;
}
exports.name = 'bitOr';
exports.factory = factory;

View File

@@ -0,0 +1,149 @@
'use strict';
var isInteger = require('../../utils/number').isInteger;
var bigBitXor = require('../../utils/bignumber/bitXor');
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
var matrix = load(require('../../type/matrix/function/matrix'));
var algorithm03 = load(require('../../type/matrix/utils/algorithm03'));
var algorithm07 = load(require('../../type/matrix/utils/algorithm07'));
var algorithm12 = load(require('../../type/matrix/utils/algorithm12'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Bitwise XOR two values, `x ^ y`.
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.bitXor(x, y)
*
* Examples:
*
* math.bitXor(1, 2); // returns number 3
*
* math.bitXor([2, 3, 4], 4); // returns Array [6, 7, 0]
*
* See also:
*
* bitAnd, bitNot, bitOr, leftShift, rightArithShift, rightLogShift
*
* @param {number | BigNumber | Array | Matrix} x First value to xor
* @param {number | BigNumber | Array | Matrix} y Second value to xor
* @return {number | BigNumber | Array | Matrix} XOR of `x` and `y`
*/
var bitXor = typed('bitXor', {
'number, number': function (x, y) {
if (!isInteger(x) || !isInteger(y)) {
throw new Error('Integers expected in function bitXor');
}
return x ^ y;
},
'BigNumber, BigNumber': bigBitXor,
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse + sparse
c = algorithm07(x, y, bitXor);
break;
default:
// sparse + dense
c = algorithm03(y, x, bitXor, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense + sparse
c = algorithm03(x, y, bitXor, false);
break;
default:
// dense + dense
c = algorithm13(x, y, bitXor);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return bitXor(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return bitXor(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return bitXor(x, matrix(y));
},
'Matrix, any': function (x, y) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm12(x, y, bitXor, false);
break;
default:
c = algorithm14(x, y, bitXor, false);
break;
}
return c;
},
'any, Matrix': function (x, y) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm12(y, x, bitXor, true);
break;
default:
c = algorithm14(y, x, bitXor, true);
break;
}
return c;
},
'Array, any': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, bitXor, false).valueOf();
},
'any, Array': function (x, y) {
// use matrix implementation
return algorithm14(matrix(y), x, bitXor, true).valueOf();
}
});
bitXor.toTex = {
2: '\\left(${args[0]}' + latex.operators['bitXor'] + '${args[1]}\\right)'
};
return bitXor;
}
exports.name = 'bitXor';
exports.factory = factory;

View File

@@ -0,0 +1,9 @@
module.exports = [
require('./bitAnd'),
require('./bitNot'),
require('./bitOr'),
require('./bitXor'),
require('./leftShift'),
require('./rightArithShift'),
require('./rightLogShift')
];

View File

@@ -0,0 +1,162 @@
'use strict';
var isInteger = require('../../utils/number').isInteger;
var bigLeftShift = require('../../utils/bignumber/leftShift');
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
var matrix = load(require('../../type/matrix/function/matrix'));
var equalScalar = load(require('../relational/equalScalar'));
var zeros = load(require('../matrix/zeros'));
var algorithm01 = load(require('../../type/matrix/utils/algorithm01'));
var algorithm02 = load(require('../../type/matrix/utils/algorithm02'));
var algorithm08 = load(require('../../type/matrix/utils/algorithm08'));
var algorithm10 = load(require('../../type/matrix/utils/algorithm10'));
var algorithm11 = load(require('../../type/matrix/utils/algorithm11'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Bitwise left logical shift of a value x by y number of bits, `x << y`.
* For matrices, the function is evaluated element wise.
* For units, the function is evaluated on the best prefix base.
*
* Syntax:
*
* math.leftShift(x, y)
*
* Examples:
*
* math.leftShift(1, 2); // returns number 4
*
* math.leftShift([1, 2, 3], 4); // returns Array [16, 32, 64]
*
* See also:
*
* leftShift, bitNot, bitOr, bitXor, rightArithShift, rightLogShift
*
* @param {number | BigNumber | Array | Matrix} x Value to be shifted
* @param {number | BigNumber} y Amount of shifts
* @return {number | BigNumber | Array | Matrix} `x` shifted left `y` times
*/
var leftShift = typed('leftShift', {
'number, number': function (x, y) {
if (!isInteger(x) || !isInteger(y)) {
throw new Error('Integers expected in function leftShift');
}
return x << y;
},
'BigNumber, BigNumber': bigLeftShift,
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse & sparse
c = algorithm08(x, y, leftShift, false);
break;
default:
// sparse & dense
c = algorithm02(y, x, leftShift, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense & sparse
c = algorithm01(x, y, leftShift, false);
break;
default:
// dense & dense
c = algorithm13(x, y, leftShift);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return leftShift(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return leftShift(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return leftShift(x, matrix(y));
},
'Matrix, number | BigNumber': function (x, y) {
// check scalar
if (!equalScalar(y, 0)) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm11(x, y, leftShift, false);
break;
default:
c = algorithm14(x, y, leftShift, false);
break;
}
return c;
}
return x.clone();
},
'number | BigNumber, Matrix': function (x, y) {
// check scalar
if (!equalScalar(x, 0)) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm10(y, x, leftShift, true);
break;
default:
c = algorithm14(y, x, leftShift, true);
break;
}
return c;
}
return zeros(y.size(), y.storage());
},
'Array, number | BigNumber': function (x, y) {
// use matrix implementation
return leftShift(matrix(x), y).valueOf();
},
'number | BigNumber, Array': function (x, y) {
// use matrix implementation
return leftShift(x, matrix(y)).valueOf();
}
});
leftShift.toTex = {
2: '\\left(${args[0]}' + latex.operators['leftShift'] + '${args[1]}\\right)'
};
return leftShift;
}
exports.name = 'leftShift';
exports.factory = factory;

View File

@@ -0,0 +1,162 @@
'use strict';
var isInteger = require('../../utils/number').isInteger;
var bigRightArithShift = require('../../utils/bignumber/rightArithShift');
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
var matrix = load(require('../../type/matrix/function/matrix'));
var equalScalar = load(require('../relational/equalScalar'));
var zeros = load(require('../matrix/zeros'));
var algorithm01 = load(require('../../type/matrix/utils/algorithm01'));
var algorithm02 = load(require('../../type/matrix/utils/algorithm02'));
var algorithm08 = load(require('../../type/matrix/utils/algorithm08'));
var algorithm10 = load(require('../../type/matrix/utils/algorithm10'));
var algorithm11 = load(require('../../type/matrix/utils/algorithm11'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Bitwise right arithmetic shift of a value x by y number of bits, `x >> y`.
* For matrices, the function is evaluated element wise.
* For units, the function is evaluated on the best prefix base.
*
* Syntax:
*
* math.rightArithShift(x, y)
*
* Examples:
*
* math.rightArithShift(4, 2); // returns number 1
*
* math.rightArithShift([16, -32, 64], 4); // returns Array [1, -2, 3]
*
* See also:
*
* bitAnd, bitNot, bitOr, bitXor, rightArithShift, rightLogShift
*
* @param {number | BigNumber | Array | Matrix} x Value to be shifted
* @param {number | BigNumber} y Amount of shifts
* @return {number | BigNumber | Array | Matrix} `x` sign-filled shifted right `y` times
*/
var rightArithShift = typed('rightArithShift', {
'number, number': function (x, y) {
if (!isInteger(x) || !isInteger(y)) {
throw new Error('Integers expected in function rightArithShift');
}
return x >> y;
},
'BigNumber, BigNumber': bigRightArithShift,
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse & sparse
c = algorithm08(x, y, rightArithShift, false);
break;
default:
// sparse & dense
c = algorithm02(y, x, rightArithShift, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense & sparse
c = algorithm01(x, y, rightArithShift, false);
break;
default:
// dense & dense
c = algorithm13(x, y, rightArithShift);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return rightArithShift(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return rightArithShift(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return rightArithShift(x, matrix(y));
},
'Matrix, number | BigNumber': function (x, y) {
// check scalar
if (!equalScalar(y, 0)) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm11(x, y, rightArithShift, false);
break;
default:
c = algorithm14(x, y, rightArithShift, false);
break;
}
return c;
}
return x.clone();
},
'number | BigNumber, Matrix': function (x, y) {
// check scalar
if (!equalScalar(x, 0)) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm10(y, x, rightArithShift, true);
break;
default:
c = algorithm14(y, x, rightArithShift, true);
break;
}
return c;
}
return zeros(y.size(), y.storage());
},
'Array, number | BigNumber': function (x, y) {
// use matrix implementation
return rightArithShift(matrix(x), y).valueOf();
},
'number | BigNumber, Array': function (x, y) {
// use matrix implementation
return rightArithShift(x, matrix(y)).valueOf();
}
});
rightArithShift.toTex = {
2: '\\left(${args[0]}' + latex.operators['rightArithShift'] + '${args[1]}\\right)'
};
return rightArithShift;
}
exports.name = 'rightArithShift';
exports.factory = factory;

View File

@@ -0,0 +1,162 @@
'use strict';
var isInteger = require('../../utils/number').isInteger;
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
var matrix = load(require('../../type/matrix/function/matrix'));
var equalScalar = load(require('../relational/equalScalar'));
var zeros = load(require('../matrix/zeros'));
var algorithm01 = load(require('../../type/matrix/utils/algorithm01'));
var algorithm02 = load(require('../../type/matrix/utils/algorithm02'));
var algorithm08 = load(require('../../type/matrix/utils/algorithm08'));
var algorithm10 = load(require('../../type/matrix/utils/algorithm10'));
var algorithm11 = load(require('../../type/matrix/utils/algorithm11'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Bitwise right logical shift of value x by y number of bits, `x >>> y`.
* For matrices, the function is evaluated element wise.
* For units, the function is evaluated on the best prefix base.
*
* Syntax:
*
* math.rightLogShift(x, y)
*
* Examples:
*
* math.rightLogShift(4, 2); // returns number 1
*
* math.rightLogShift([16, -32, 64], 4); // returns Array [1, 2, 3]
*
* See also:
*
* bitAnd, bitNot, bitOr, bitXor, leftShift, rightLogShift
*
* @param {number | Array | Matrix} x Value to be shifted
* @param {number} y Amount of shifts
* @return {number | Array | Matrix} `x` zero-filled shifted right `y` times
*/
var rightLogShift = typed('rightLogShift', {
'number, number': function (x, y) {
if (!isInteger(x) || !isInteger(y)) {
throw new Error('Integers expected in function rightLogShift');
}
return x >>> y;
},
// 'BigNumber, BigNumber': ..., // TODO: implement BigNumber support for rightLogShift
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse & sparse
c = algorithm08(x, y, rightLogShift, false);
break;
default:
// sparse & dense
c = algorithm02(y, x, rightLogShift, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense & sparse
c = algorithm01(x, y, rightLogShift, false);
break;
default:
// dense & dense
c = algorithm13(x, y, rightLogShift);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return rightLogShift(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return rightLogShift(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return rightLogShift(x, matrix(y));
},
'Matrix, number | BigNumber': function (x, y) {
// check scalar
if (!equalScalar(y, 0)) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm11(x, y, rightLogShift, false);
break;
default:
c = algorithm14(x, y, rightLogShift, false);
break;
}
return c;
}
return x.clone();
},
'number | BigNumber, Matrix': function (x, y) {
// check scalar
if (!equalScalar(x, 0)) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm10(y, x, rightLogShift, true);
break;
default:
c = algorithm14(y, x, rightLogShift, true);
break;
}
return c;
}
return zeros(y.size(), y.storage());
},
'Array, number | BigNumber': function (x, y) {
// use matrix implementation
return rightLogShift(matrix(x), y).valueOf();
},
'number | BigNumber, Array': function (x, y) {
// use matrix implementation
return rightLogShift(x, matrix(y)).valueOf();
}
});
rightLogShift.toTex = {
2: '\\left(${args[0]}' + latex.operators['rightLogShift'] + '${args[1]}\\right)'
};
return rightLogShift;
}
exports.name = 'rightLogShift';
exports.factory = factory;

View File

@@ -0,0 +1,53 @@
'use strict';
function factory (type, config, load, typed) {
var add = load(require('../arithmetic/add'));
var stirlingS2 = load(require('./stirlingS2'));
var isNegative = load(require('../utils/isNegative'));
var isInteger = load(require('../utils/isInteger'));
/**
* The Bell Numbers count the number of partitions of a set. A partition is a pairwise disjoint subset of S whose union is S.
* bellNumbers only takes integer arguments.
* The following condition must be enforced: n >= 0
*
* Syntax:
*
* math.bellNumbers(n)
*
* Examples:
*
* math.bellNumbers(3); // returns 5;
* math.bellNumbers(8); // returns 4140;
*
* See also:
*
* stirlingS2
*
* @param {Number | BigNumber} n Total number of objects in the set
* @return {Number | BigNumber} B(n)
*/
var bellNumbers = typed('bellNumbers', {
'number | BigNumber': function (n) {
if (!isInteger(n) || isNegative(n)) {
throw new TypeError('Non-negative integer value expected in function bellNumbers');
}
// Sum (k=0, n) S(n,k).
var result = 0;
for(var i = 0; i <= n; i++) {
result = add(result, stirlingS2(n, i));
}
return result;
}
});
bellNumbers.toTex = {1: '\\mathrm{B}_{${args[0]}}'};
return bellNumbers;
}
exports.name = 'bellNumbers';
exports.factory = factory;

View File

@@ -0,0 +1,51 @@
'use strict';
function factory (type, config, load, typed) {
var add = load(require('../arithmetic/add'));
var divide = load(require('../arithmetic/divide'));
var multiply = load(require('../arithmetic/multiply'));
var combinations = load(require('../probability/combinations'));
var isNegative = load(require('../utils/isNegative'));
var isInteger = load(require('../utils/isInteger'));
/**
* The Catalan Numbers enumerate combinatorial structures of many different types.
* catalan only takes integer arguments.
* The following condition must be enforced: n >= 0
*
* Syntax:
*
* math.catalan(n)
*
* Examples:
*
* math.catalan(3); // returns 5;
* math.catalan(8); // returns 1430;
*
* See also:
*
* bellNumbers
*
* @param {Number | BigNumber} n nth Catalan number
* @return {Number | BigNumber} Cn(n)
*/
var catalan = typed('catalan', {
'number | BigNumber': function (n) {
if (!isInteger(n) || isNegative(n)) {
throw new TypeError('Non-negative integer value expected in function catalan');
}
return divide(combinations(multiply(n,2), n), add(n,1));
}
});
catalan.toTex = {1: '\\mathrm{C}_{${args[0]}}'};
return catalan;
}
exports.name = 'catalan';
exports.factory = factory;

View File

@@ -0,0 +1,51 @@
'use strict';
function factory (type, config, load, typed) {
var combinations = load(require('../probability/combinations'));
var add = load(require('../arithmetic/addScalar'));
var isPositive = load(require('../utils/isPositive'));
var isInteger = load(require('../utils/isInteger'));
var larger = load(require('../relational/larger'));
/**
* The composition counts of n into k parts.
*
* composition only takes integer arguments.
* The following condition must be enforced: k <= n.
*
* Syntax:
*
* math.composition(n, k)
*
* Examples:
*
* math.composition(5, 3); // returns 6
*
* See also:
*
* combinations
*
* @param {Number | BigNumber} n Total number of objects in the set
* @param {Number | BigNumber} k Number of objects in the subset
* @return {Number | BigNumber} Returns the composition counts of n into k parts.
*/
var composition = typed('composition', {
'number | BigNumber, number | BigNumber': function (n, k) {
if (!isInteger(n) || !isPositive(n) || !isInteger(k) || !isPositive(k)) {
throw new TypeError('Positive integer value expected in function composition');
}
else if (larger(k, n)) {
throw new TypeError('k must be less than or equal to n in function composition');
}
return combinations(add(n, -1), add(k, -1));
}
});
composition.toTex = undefined; // use default template
return composition;
}
exports.name = 'composition';
exports.factory = factory;

View File

@@ -0,0 +1,6 @@
module.exports = [
require('./bellNumbers'),
require('./composition'),
require('./stirlingS2'),
require('./catalan')
];

View File

@@ -0,0 +1,69 @@
'use strict';
function factory (type, config, load, typed) {
var add = load(require('../arithmetic/add'));
var subtract = load(require('../arithmetic/subtract'));
var multiply = load(require('../arithmetic/multiply'));
var divide = load(require('../arithmetic/divide'));
var pow = load(require('../arithmetic/pow'));
var factorial = load(require('../probability/factorial'));
var combinations = load(require('../probability/combinations'));
var isNegative = load(require('../utils/isNegative'));
var isInteger = load(require('../utils/isInteger'));
var larger = load(require('../relational/larger'));
/**
* The Stirling numbers of the second kind, counts the number of ways to partition
* a set of n labelled objects into k nonempty unlabelled subsets.
* stirlingS2 only takes integer arguments.
* The following condition must be enforced: k <= n.
*
* If n = k or k = 1, then s(n,k) = 1
*
* Syntax:
*
* math.stirlingS2(n, k)
*
* Examples:
*
* math.stirlingS2(5, 3); //returns 25
*
* See also:
*
* Bell numbers
*
* @param {Number | BigNumber} n Total number of objects in the set
* @param {Number | BigNumber} k Number of objects in the subset
* @return {Number | BigNumber} S(n,k)
*/
var stirlingS2 = typed('stirlingS2', {
'number | BigNumber, number | BigNumber': function (n, k) {
if (!isInteger(n) || isNegative(n) || !isInteger(k) || isNegative(k)) {
throw new TypeError('Non-negative integer value expected in function stirlingS2');
}
else if (larger(k, n)) {
throw new TypeError('k must be less than or equal to n in function stirlingS2');
}
// 1/k! Sum(i=0 -> k) [(-1)^(k-i)*C(k,j)* i^n]
var kFactorial = factorial(k);
var result = 0;
for(var i = 0; i <= k; i++) {
var negativeOne = pow(-1, subtract(k,i));
var kChooseI = combinations(k,i);
var iPower = pow(i,n);
result = add(result, multiply(multiply(kChooseI, iPower), negativeOne));
}
return divide(result, kFactorial);
}
});
stirlingS2.toTex = {2: '\\mathrm{S}\\left(${args}\\right)'};
return stirlingS2;
}
exports.name = 'stirlingS2';
exports.factory = factory;

View File

@@ -0,0 +1,59 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
/**
* Compute the argument of a complex value.
* For a complex number `a + bi`, the argument is computed as `atan2(b, a)`.
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.arg(x)
*
* Examples:
*
* var a = math.complex(2, 2);
* math.arg(a) / math.pi; // returns number 0.25
*
* var b = math.complex('2 + 3i');
* math.arg(b); // returns number 0.982793723247329
* math.atan2(3, 2); // returns number 0.982793723247329
*
* See also:
*
* re, im, conj, abs
*
* @param {number | BigNumber | Complex | Array | Matrix} x
* A complex number or array with complex numbers
* @return {number | BigNumber | Array | Matrix} The argument of x
*/
var arg = typed('arg', {
'number': function (x) {
return Math.atan2(0, x);
},
'BigNumber': function (x) {
return type.BigNumber.atan2(0, x);
},
'Complex': function (x) {
return x.arg();
},
// TODO: implement BigNumber support for function arg
'Array | Matrix': function (x) {
return deepMap(x, arg);
}
});
arg.toTex = {1: '\\arg\\left(${args[0]}\\right)'};
return arg;
}
exports.name = 'arg';
exports.factory = factory;

View File

@@ -0,0 +1,55 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
/**
* Compute the complex conjugate of a complex value.
* If `x = a+bi`, the complex conjugate of `x` is `a - bi`.
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.conj(x)
*
* Examples:
*
* math.conj(math.complex('2 + 3i')); // returns Complex 2 - 3i
* math.conj(math.complex('2 - 3i')); // returns Complex 2 + 3i
* math.conj(math.complex('-5.2i')); // returns Complex 5.2i
*
* See also:
*
* re, im, arg, abs
*
* @param {number | BigNumber | Complex | Array | Matrix} x
* A complex number or array with complex numbers
* @return {number | BigNumber | Complex | Array | Matrix}
* The complex conjugate of x
*/
var conj = typed('conj', {
'number': function (x) {
return x;
},
'BigNumber': function (x) {
return x;
},
'Complex': function (x) {
return x.conjugate();
},
'Array | Matrix': function (x) {
return deepMap(x, conj);
}
});
conj.toTex = {1: '\\left(${args[0]}\\right)^*'};
return conj;
}
exports.name = 'conj';
exports.factory = factory;

View File

@@ -0,0 +1,57 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
/**
* Get the imaginary part of a complex number.
* For a complex number `a + bi`, the function returns `b`.
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.im(x)
*
* Examples:
*
* var a = math.complex(2, 3);
* math.re(a); // returns number 2
* math.im(a); // returns number 3
*
* math.re(math.complex('-5.2i')); // returns number -5.2
* math.re(math.complex(2.4)); // returns number 0
*
* See also:
*
* re, conj, abs, arg
*
* @param {number | BigNumber | Complex | Array | Matrix} x
* A complex number or array with complex numbers
* @return {number | BigNumber | Array | Matrix} The imaginary part of x
*/
var im = typed('im', {
'number': function (x) {
return 0;
},
'BigNumber': function (x) {
return new type.BigNumber(0);
},
'Complex': function (x) {
return x.im;
},
'Array | Matrix': function (x) {
return deepMap(x, im);
}
});
im.toTex = {1: '\\Im\\left\\lbrace${args[0]}\\right\\rbrace'};
return im;
}
exports.name = 'im';
exports.factory = factory;

View File

@@ -0,0 +1,6 @@
module.exports = [
require('./arg'),
require('./conj'),
require('./im'),
require('./re')
];

View File

@@ -0,0 +1,57 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
/**
* Get the real part of a complex number.
* For a complex number `a + bi`, the function returns `a`.
*
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.re(x)
*
* Examples:
*
* var a = math.complex(2, 3);
* math.re(a); // returns number 2
* math.im(a); // returns number 3
*
* math.re(math.complex('-5.2i')); // returns number 0
* math.re(math.complex(2.4)); // returns number 2.4
*
* See also:
*
* im, conj, abs, arg
*
* @param {number | BigNumber | Complex | Array | Matrix} x
* A complex number or array with complex numbers
* @return {number | BigNumber | Array | Matrix} The real part of x
*/
var re = typed('re', {
'number': function (x) {
return x;
},
'BigNumber': function (x) {
return x;
},
'Complex': function (x) {
return x.re;
},
'Array | Matrix': function (x) {
return deepMap(x, re);
}
});
re.toTex = {1: '\\Re\\left\\lbrace${args[0]}\\right\\rbrace'};
return re;
}
exports.name = 're';
exports.factory = factory;

View File

@@ -0,0 +1,301 @@
'use strict';
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
/**
* Calculates:
* The eucledian distance between two points in 2 and 3 dimensional spaces.
* Distance between point and a line in 2 and 3 dimensional spaces.
* Pairwise distance between a set of 2D or 3D points
* NOTE:
* When substituting coefficients of a line(a, b and c), use ax + by + c = 0 instead of ax + by = c
* For parametric equation of a 3D line, x0, y0, z0, a, b, c are from: (xx0, yy0, zz0) = t(a, b, c)
*
* Syntax:
* math.distance([x1, y1], [x2, y2])
*- math.distance({pointOneX: 4, pointOneY: 5}, {pointTwoX: 2, pointTwoY: 7})
* math.distance([x1, y1, z1], [x2, y2, z2])
* math.distance({pointOneX: 4, pointOneY: 5, pointOneZ: 8}, {pointTwoX: 2, pointTwoY: 7, pointTwoZ: 9})
* math.distance([[A], [B], [C]...])
* math.distance([x1, y1], [LinePtX1, LinePtY1], [LinePtX2, LinePtY2])
* math.distance({pointX: 1, pointY: 4}, {lineOnePtX: 6, lineOnePtY: 3}, {lineTwoPtX: 2, lineTwoPtY: 8})
* math.distance([x1, y1, z1], [LinePtX1, LinePtY1, LinePtZ1], [LinePtX2, LinePtY2, LinePtZ2])
* math.distance({pointX: 1, pointY: 4, pointZ: 7}, {lineOnePtX: 6, lineOnePtY: 3, lineOnePtZ: 4}, {lineTwoPtX: 2, lineTwoPtY: 8, lineTwoPtZ: 5})
* math.distance([x1, y1], [xCoeffLine, yCoeffLine, constant])
* math.distance({pointX: 10, pointY: 10}, {xCoeffLine: 8, yCoeffLine: 1, constant: 3})
* math.distance([x1, y1, z1], [x0, y0, z0, a-tCoeff, b-tCoeff, c-tCoeff]) point and parametric equation of 3D line
* math.distance([x, y, z], [x0, y0, z0, a, b, c])
* math.distance({pointX: 2, pointY: 5, pointZ: 9}, {x0: 4, y0: 6, z0: 3, a: 4, b: 2, c: 0})
*
* Examples:
* math.distance([0,0], [4,4]) // Returns 5.6569
* math.distance(
* {pointOneX: 0, pointOneY: 0},
* {pointTwoX: 10, pointTwoY: 10}) // Returns 14.142135623730951
* math.distance([1, 0, 1], [4, -2, 2]) // Returns 3.74166
* math.distance(
* {pointOneX: 4, pointOneY: 5, pointOneZ: 8},
* {pointTwoX: 2, pointTwoY: 7, pointTwoZ: 9}) // Returns 3
* math.distance([[1, 2], [1, 2], [1, 3]]) // Returns [0, 1, 1]
* math.distance([[1,2,4], [1,2,6], [8,1,3]]) // Returns [2, 7.14142842854285, 7.681145747868608]
* math.distance([10, 10], [8, 1, 3]) // Returns 11.535230316796387
* math.distance([10, 10], [2, 3], [-8, 0]) // Returns 8.759953130362847
* math.distance(
* {pointX: 1, pointY: 4},
* {lineOnePtX: 6, lineOnePtY: 3},
* {lineTwoPtX: 2, lineTwoPtY: 8}) // Returns 2.720549372624744
* math.distance([2, 3, 1], [1, 1, 2, 5, 0, 1]) // Returns 2.3204774044612857
* math.distance(
* {pointX: 2, pointY: 3, pointZ: 1},
* {x0: 1, y0: 1, z0: 2, a: 5, b: 0, c: 1} // Returns 2.3204774044612857
*
* @param {Array | Matrix | Object} x Co-ordinates of first point
* @param {Array | Matrix | Object} y Co-ordinates of second point
* @return {Number | BigNumber} Returns the distance from two/three points
*/
var distance = typed('distance', {
'Array, Array, Array': function(x, y, z){
// Point to Line 2D; (x=Point, y=LinePoint1, z=LinePoint2)
if (x.length == 2 && y.length == 2 && z.length == 2){
if (!_2d(x)) { throw new TypeError('Array with 2 numbers expected for first argument'); }
if (!_2d(y)) { throw new TypeError('Array with 2 numbers expected for second argument'); }
if (!_2d(z)) { throw new TypeError('Array with 2 numbers expected for third argument'); }
var m = (z[1]-z[0])/(y[1]-y[0]);
var xCoeff = m*m*y[0];
var yCoeff = -1*(m*y[0]);
var constant = x[1];
return _distancePointLine2D(x[0], x[1], xCoeff, yCoeff, constant);
}
else{
throw new TypeError('Invalid Arguments: Try again');
}
},
'Object, Object, Object': function(x, y, z){
if (Object.keys(x).length == 2 && Object.keys(y).length == 2 && Object.keys(z).length == 2){
if (!_2d(x)) { throw new TypeError('Values of pointX and pointY should be numbers'); }
if (!_2d(y)) { throw new TypeError('Values of lineOnePtX and lineOnePtY should be numbers'); }
if (!_2d(z)) { throw new TypeError('Values of lineTwoPtX and lineTwoPtY should be numbers'); }
if (x.hasOwnProperty('pointX') && x.hasOwnProperty('pointY') && y.hasOwnProperty('lineOnePtX') &&
y.hasOwnProperty('lineOnePtY') && z.hasOwnProperty('lineTwoPtX') && z.hasOwnProperty('lineTwoPtY')){
var m = (z.lineTwoPtY-z.lineTwoPtX)/(y.lineOnePtY-y.lineOnePtX);
var xCoeff = m*m*y.lineOnePtX;
var yCoeff = -1*(m*y.lineOnePtX);
var constant = x.pointX;
return _distancePointLine2D(x.pointX, x.pointY, xCoeff, yCoeff, constant);
}
else{
throw new TypeError('Key names do not match');
}
}
else{
throw new TypeError('Invalid Arguments: Try again');
}
},
'Array, Array': function(x, y){
// Point to Line 2D; (x=[pointX, pointY], y=[x-coeff, y-coeff, const])
if (x.length == 2 && y.length == 3){
if (!_2d(x)) { throw new TypeError('Array with 2 numbers expected for first argument'); }
if (!_3d(y)) { throw new TypeError('Array with 3 numbers expected for second argument'); }
return _distancePointLine2D(x[0], x[1], y[0], y[1], y[2]);
}
// Point to Line 3D
else if (x.length == 3 && y.length == 6){
if (!_3d(x)) { throw new TypeError('Array with 3 numbers expected for first argument'); }
if (!_parametricLine(y)) { throw new TypeError('Array with 6 numbers expected for second argument'); }
return _distancePointLine3D(x[0], x[1], x[2], y[0], y[1], y[2], y[3], y[4], y[5]);
}
// Point to Point 2D
else if (x.length == 2 && y.length == 2){
if (!_2d(x)) { throw new TypeError('Array with 2 numbers expected for first argument'); }
if (!_2d(y)) { throw new TypeError('Array with 2 numbers expected for second argument'); }
return _distance2d(x[0], x[1], y[0], y[1]);
}
// Point to Point 3D
else if(x.length == 3 && y.length == 3){
if (!_3d(x)) { throw new TypeError('Array with 3 numbers expected for first argument'); }
if (!_3d(y)) { throw new TypeError('Array with 3 numbers expected for second argument'); }
return _distance3d(x[0], x[1], x[2], y[0], y[1], y[2]);
}
else{
throw new TypeError('Invalid Arguments: Try again');
}
},
'Object, Object': function(x, y){
if (Object.keys(x).length == 2 && Object.keys(y).length == 3){
if (!_2d(x)) { throw new TypeError('Values of pointX and pointY should be numbers'); }
if (!_3d(y)) { throw new TypeError('Values of xCoeffLine, yCoeffLine and constant should be numbers'); }
if (x.hasOwnProperty('pointX') && x.hasOwnProperty('pointY') && y.hasOwnProperty('xCoeffLine') &&
y.hasOwnProperty('yCoeffLine') && y.hasOwnProperty('yCoeffLine')){
return _distancePointLine2D(x.pointX, x.pointY, y.xCoeffLine, y.yCoeffLine, y.constant);
}
else{
throw new TypeError('Key names do not match');
}
}
// Point to Line 3D
else if (Object.keys(x).length == 3 && Object.keys(y).length == 6){
if (!_3d(x)) { throw new TypeError('Values of pointX, pointY and pointZ should be numbers'); }
if (!_parametricLine(y)) { throw new TypeError('Values of x0, y0, z0, a, b and c should be numbers'); }
if (x.hasOwnProperty('pointX') && x.hasOwnProperty('pointY') && y.hasOwnProperty('x0') &&
y.hasOwnProperty('y0') && y.hasOwnProperty('z0') && y.hasOwnProperty('a') &&
y.hasOwnProperty('b') && y.hasOwnProperty('c')){
return _distancePointLine3D(x.pointX, x.pointY, x.pointZ, y.x0, y.y0, y.z0, y.a, y.b, y.c);
}
else{
throw new TypeError('Key names do not match');
}
}
// Point to Point 2D
else if (Object.keys(x).length == 2 && Object.keys(y).length == 2){
if (!_2d(x)) { throw new TypeError('Values of pointOneX and pointOneY should be numbers'); }
if (!_2d(y)) { throw new TypeError('Values of pointTwoX and pointTwoY should be numbers'); }
if (x.hasOwnProperty('pointOneX') && x.hasOwnProperty('pointOneY') &&
y.hasOwnProperty('pointTwoX') && y.hasOwnProperty('pointTwoY')){
return _distance2d(x.pointOneX, x.pointOneY, y.pointTwoX, y.pointTwoY);
}
else{
throw new TypeError('Key names do not match');
}
}
// Point to Point 3D
else if(Object.keys(x).length == 3 && Object.keys(y).length == 3){
if (!_3d(x)) { throw new TypeError('Values of pointOneX, pointOneY and pointOneZ should be numbers'); }
if (!_3d(y)) { throw new TypeError('Values of pointTwoX, pointTwoY and pointTwoZ should be numbers'); }
if (x.hasOwnProperty('pointOneX') && x.hasOwnProperty('pointOneY') && x.hasOwnProperty('pointOneZ') &&
y.hasOwnProperty('pointTwoX') && y.hasOwnProperty('pointTwoY') && y.hasOwnProperty('pointTwoZ')){
return _distance3d(x.pointOneX, x.pointOneY, x.pointOneZ, y.pointTwoX, y.pointTwoY, y.pointTwoZ);
}
else {
throw new TypeError('Key names do not match');
}
}
else{
throw new TypeError('Invalid Arguments: Try again');
}
},
'Array': function(arr){
if (!_pairwise(arr)) { throw new TypeError('Incorrect array format entered for pairwise distance calculation'); }
return _distancePairwise(arr);
}
});
return distance;
}
function _2d(a){
// checks if the number of arguments are correct in count and are valid (should be numbers)
if (a.constructor !== Array){
a = _objectToArray(a);
}
return typeof a[0] === 'number' && typeof a[1] === 'number';
}
function _3d(a){
// checks if the number of arguments are correct in count and are valid (should be numbers)
if (a.constructor !== Array){
a = _objectToArray(a);
}
return typeof a[0] === 'number' && typeof a[1] === 'number' && typeof a[2] === 'number';
}
function _parametricLine(a){
if (a.constructor !== Array){
a = _objectToArray(a);
}
return typeof a[0] === 'number' && typeof a[1] === 'number' && typeof a[2] === 'number' &&
typeof a[3] === 'number' && typeof a[4] === 'number' && typeof a[5] === 'number';
}
function _objectToArray(o){
var keys = Object.keys(o);
var a = [];
for (var i = 0; i < keys.length; i++) {
a.push(o[keys[i]]);
}
return a;
}
function _pairwise(a){
//checks for valid arguments passed to _distancePairwise(Array)
if (a[0].length == 2 && typeof a[0][0] === 'number' && typeof a[0][1] === 'number'){
for(var i in a){
if (a[i].length != 2 || typeof a[i][0] !== 'number' || typeof a[i][1] !== 'number'){
return false;
}
}
}
else if (a[0].length == 3 && typeof a[0][0] === 'number' && typeof a[0][1] === 'number' && typeof a[0][2] === 'number'){
for(var i in a){
if (a[i].length != 3 || typeof a[i][0] !== 'number' || typeof a[i][1] !== 'number' || typeof a[i][2] !== 'number'){
return false;
}
}
}
else{
return false;
}
return true;
}
function _distancePointLine2D(x, y, a, b, c){
var num = Math.abs(a*x + b*y + c);
var den = Math.pow((a*a + b*b), 0.5);
var result = (num/den);
return result;
}
function _distancePointLine3D(x, y, z, x0, y0, z0, a, b, c){
var num = [((y0-y)*(c))-((z0-z)*(b)), ((z0-z)*(a))-((x0-x)*(c)), ((x0-x)*(b))-((y0-y)*(a))]
num = Math.pow(num[0]*num[0] + num[1]*num[1] + num[2]*num[2], 0.5);
var den = Math.pow(a*a + b*b + c*c, 0.5);
var result = num/den;
return result;
}
function _distance2d(x1, y1, x2, y2){
var yDiff = y2 - y1;
var xDiff = x2 - x1;
var radicant = yDiff * yDiff + xDiff * xDiff;
var result = Math.pow(radicant, 0.5);
return result;
}
function _distance3d(x1, y1, z1, x2, y2, z2){
var zDiff = z2 - z1;
var yDiff = y2 - y1;
var xDiff = x2 - x1;
var radicant = zDiff * zDiff + yDiff * yDiff + xDiff * xDiff;
var result = Math.pow(radicant, 0.5);
return result;
}
function _distancePairwise(a){
var result = [];
for(var i = 0; i < a.length-1; i++){
for(var j = i+1; j < a.length; j++){
if (a[0].length == 2){
result.push(_distance2d(a[i][0], a[i][1], a[j][0], a[j][1]));
}
else if (a[0].length == 3){
result.push(_distance3d(a[i][0], a[i][1], a[i][2], a[j][0], a[j][1], a[j][2]));
}
}
}
return result;
}
exports.name = 'distance';
exports.factory = factory;

View File

@@ -0,0 +1,4 @@
module.exports = [
require('./intersect'),
require('./distance')
];

View File

@@ -0,0 +1,142 @@
'use strict';
function factory (type, config, load, typed) {
var abs = load(require('../arithmetic/abs'));
var add = load(require('../arithmetic/add'));
var matrix = load(require('../../type/matrix/function/matrix'));
var multiply = load(require('../arithmetic/multiply'));
var subtract = load(require('../arithmetic/subtract'));
/**
* Calculates the point of intersection of two lines in two or three dimensions
* and of a line and a plane in three dimensions. The inputs are in the form of
* arrays or 1 dimensional matrices. The line intersection functions return null
* if the lines do not meet.
*
* Note: Fill the plane coefficients as `x + y + z = c` and not as `x + y + z + c = 0`.
*
* Syntax:
*
* math.intersect(endPoint1Line1, endPoint2Line1, endPoint1Line2, endPoint2Line2)
* math.intersect(endPoint1, endPoint2, planeCoefficients)
*
* Examples:
*
* math.intersect([0, 0], [10, 10], [10, 0], [0, 10]); // Returns [5, 5]
* math.intersect([0, 0, 0], [10, 10, 0], [10, 0, 0], [0, 10, 0]); // Returns [5, 5, 0]
* math.intersect([1, 0, 1], [4, -2, 2], [1, 1, 1, 6]); // Returns [7, -4, 3]
*
* @param {Array | Matrix} w Co-ordinates of first end-point of first line
* @param {Array | Matrix} x Co-ordinates of second end-point of first line
* @param {Array | Matrix} y Co-ordinates of first end-point of second line
* OR Co-efficients of the plane's equation
* @param {Array | Matrix} z Co-ordinates of second end-point of second line
* OR null if the calculation is for line and plane
* @return {Array} Returns the point of intersection of lines/lines-planes
*/
var intersect = typed('intersect', {
'Array, Array, Array': function (x, y, plane) {
if (!_3d(x)) { throw new TypeError('Array with 3 numbers expected for first argument'); }
if (!_3d(y)) { throw new TypeError('Array with 3 numbers expected for second argument'); }
if (!_4d(plane)) { throw new TypeError('Array with 4 numbers expected as third argument'); }
return _intersectLinePlane(x[0], x[1], x[2], y[0], y[1], y[2], plane[0], plane[1], plane[2], plane[3]);
},
'Array, Array, Array, Array': function (w, x, y, z) {
if (w.length === 2) {
if (!_2d(w)) { throw new TypeError('Array with 2 numbers expected for first argument'); }
if (!_2d(x)) { throw new TypeError('Array with 2 numbers expected for second argument'); }
if (!_2d(y)) { throw new TypeError('Array with 2 numbers expected for third argument'); }
if (!_2d(z)) { throw new TypeError('Array with 2 numbers expected for fourth argument'); }
return _intersect2d(w, x, y, z);
}
else if (w.length === 3) {
if (!_3d(w)) { throw new TypeError('Array with 3 numbers expected for first argument'); }
if (!_3d(x)) { throw new TypeError('Array with 3 numbers expected for second argument'); }
if (!_3d(y)) { throw new TypeError('Array with 3 numbers expected for third argument'); }
if (!_3d(z)) { throw new TypeError('Array with 3 numbers expected for fourth argument'); }
return _intersect3d(w[0], w[1], w[2], x[0], x[1], x[2], y[0], y[1], y[2], z[0], z[1], z[2]);
}
else {
throw new TypeError('Arrays with two or thee dimensional points expected');
}
},
'Matrix, Matrix, Matrix': function (x, y, plane) {
return matrix(intersect(x.valueOf(), y.valueOf(), plane.valueOf()));
},
'Matrix, Matrix, Matrix, Matrix': function (w, x, y, z) {
// TODO: output matrix type should match input matrix type
return matrix(intersect(w.valueOf(), x.valueOf(), y.valueOf(), z.valueOf()));
}
});
function _2d(x) {
return x.length === 2 && typeof x[0] === 'number' && typeof x[1] === 'number';
}
function _3d(x) {
return x.length === 3 && typeof x[0] === 'number' && typeof x[1] === 'number' && typeof x[2] === 'number';
}
function _4d(x) {
return x.length === 4 && typeof x[0] === 'number' && typeof x[1] === 'number' && typeof x[2] === 'number' && typeof x[3] === 'number';
}
function _intersect2d(p1a, p1b, p2a, p2b){
var o1 = p1a;
var o2 = p2a;
var d1 = subtract(o1, p1b);
var d2 = subtract(o2, p2b);
var det = d1[0]*d2[1] - d2[0]*d1[1];
if (abs(det) < config.epsilon) {
return null;
}
var t = (d2[0]*o1[1] - d2[1]*o1[0] - d2[0]*o2[1] + d2[1]*o2[0]) / det;
return add(multiply(d1, t), o1);
}
function _intersect3d(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4){
var d1343 = (x1 - x3)*(x4 - x3) + (y1 - y3)*(y4 - y3) + (z1 - z3)*(z4 - z3);
var d4321 = (x4 - x3)*(x2 - x1) + (y4 - y3)*(y2 - y1) + (z4 - z3)*(z2 - z1);
var d1321 = (x1 - x3)*(x2 - x1) + (y1 - y3)*(y2 - y1) + (z1 - z3)*(z2 - z1);
var d4343 = (x4 - x3)*(x4 - x3) + (y4 - y3)*(y4 - y3) + (z4 - z3)*(z4 - z3);
var d2121 = (x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1) + (z2 - z1)*(z2 - z1);
var ta = ( d1343*d4321 - d1321*d4343 ) / ( d2121*d4343 - d4321*d4321 );
var tb = ( d1343 + ta * d4321 ) / (d4343);
var pax = x1 + ta * (x2 - x1);
var pay = y1 + ta * (y2 - y1);
var paz = z1 + ta * (z2 - z1);
var pbx = x3 + tb * (x4 - x3);
var pby = y3 + tb * (y4 - y3);
var pbz = z3 + tb * (z4 - z3);
if (pax === pbx && pay === pby && paz === pbz){
return [pax, pay, paz];
}
else{
return null;
}
}
function _intersectLinePlane(x1, y1, z1, x2, y2, z2, x, y, z, c){
var t = (c - x1*x - y1*y - z1*z)/(x2*x + y2*y + z2*z - x1 - y1 - z1);
var px = x1 + t * (x2 - x1);
var py = y1 + t * (y2 - y1);
var pz = z1 + t * (z2 - z1);
return [px, py, pz];
// TODO: Add cases when line is parallel to the plane:
// (a) no intersection,
// (b) line contained in plane
}
return intersect;
}
exports.name = 'intersect';
exports.factory = factory;

View File

@@ -0,0 +1,18 @@
module.exports = [
require('./algebra'),
require('./arithmetic'),
require('./bitwise'),
require('./combinatorics'),
require('./complex'),
require('./geometry'),
require('./logical'),
require('./matrix'),
require('./probability'),
require('./relational'),
require('./special'),
require('./statistics'),
require('./string'),
require('./trigonometry'),
require('./unit'),
require('./utils')
];

View File

@@ -0,0 +1,171 @@
'use strict';
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
var matrix = load(require('../../type/matrix/function/matrix'));
var zeros = load(require('../matrix/zeros'));
var not = load(require('./not'));
var isZero = load(require('../utils/isZero'));
var algorithm02 = load(require('../../type/matrix/utils/algorithm02'));
var algorithm06 = load(require('../../type/matrix/utils/algorithm06'));
var algorithm11 = load(require('../../type/matrix/utils/algorithm11'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Logical `and`. Test whether two values are both defined with a nonzero/nonempty value.
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.and(x, y)
*
* Examples:
*
* math.and(2, 4); // returns true
*
* a = [2, 0, 0];
* b = [3, 7, 0];
* c = 0;
*
* math.and(a, b); // returns [true, false, false]
* math.and(a, c); // returns [false, false, false]
*
* See also:
*
* not, or, xor
*
* @param {number | BigNumber | Complex | Unit | Array | Matrix} x First value to check
* @param {number | BigNumber | Complex | Unit | Array | Matrix} y Second value to check
* @return {boolean | Array | Matrix}
* Returns true when both inputs are defined with a nonzero/nonempty value.
*/
var and = typed('and', {
'number, number': function (x, y) {
return !!(x && y);
},
'Complex, Complex': function (x, y) {
return (x.re !== 0 || x.im !== 0) && (y.re !== 0 || y.im !== 0);
},
'BigNumber, BigNumber': function (x, y) {
return !x.isZero() && !y.isZero() && !x.isNaN() && !y.isNaN();
},
'Unit, Unit': function (x, y) {
return and(x.value, y.value);
},
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse & sparse
c = algorithm06(x, y, and, false);
break;
default:
// sparse & dense
c = algorithm02(y, x, and, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense & sparse
c = algorithm02(x, y, and, false);
break;
default:
// dense & dense
c = algorithm13(x, y, and);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return and(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return and(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return and(x, matrix(y));
},
'Matrix, any': function (x, y) {
// check scalar
if (not(y)) {
// return zero matrix
return zeros(x.size(), x.storage());
}
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm11(x, y, and, false);
break;
default:
c = algorithm14(x, y, and, false);
break;
}
return c;
},
'any, Matrix': function (x, y) {
// check scalar
if (not(x)) {
// return zero matrix
return zeros(x.size(), x.storage());
}
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm11(y, x, and, true);
break;
default:
c = algorithm14(y, x, and, true);
break;
}
return c;
},
'Array, any': function (x, y) {
// use matrix implementation
return and(matrix(x), y).valueOf();
},
'any, Array': function (x, y) {
// use matrix implementation
return and(x, matrix(y)).valueOf();
}
});
and.toTex = {
2: '\\left(${args[0]}' + latex.operators['and'] + '${args[1]}\\right)'
};
return and;
}
exports.name = 'and';
exports.factory = factory;

View File

@@ -0,0 +1,6 @@
module.exports = [
require('./and'),
require('./not'),
require('./or'),
require('./xor')
];

View File

@@ -0,0 +1,63 @@
'use strict';
var deepMap = require('../../utils/collection/deepMap');
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
/**
* Logical `not`. Flips boolean value of a given parameter.
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.not(x)
*
* Examples:
*
* math.not(2); // returns false
* math.not(0); // returns true
* math.not(true); // returns false
*
* a = [2, -7, 0];
* math.not(a); // returns [false, false, true]
*
* See also:
*
* and, or, xor
*
* @param {number | BigNumber | Complex | Unit | Array | Matrix} x First value to check
* @return {boolean | Array | Matrix}
* Returns true when input is a zero or empty value.
*/
var not = typed('not', {
'number': function (x) {
return !x;
},
'Complex': function (x) {
return x.re === 0 && x.im === 0;
},
'BigNumber': function (x) {
return x.isZero() || x.isNaN();
},
'Unit': function (x) {
return not(x.value);
},
'Array | Matrix': function (x) {
return deepMap(x, not);
}
});
not.toTex = {
1: latex.operators['not'] + '\\left(${args[0]}\\right)'
};
return not;
}
exports.name = 'not';
exports.factory = factory;

View File

@@ -0,0 +1,158 @@
'use strict';
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
var matrix = load(require('../../type/matrix/function/matrix'));
var algorithm03 = load(require('../../type/matrix/utils/algorithm03'));
var algorithm05 = load(require('../../type/matrix/utils/algorithm05'));
var algorithm12 = load(require('../../type/matrix/utils/algorithm12'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Logical `or`. Test if at least one value is defined with a nonzero/nonempty value.
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.or(x, y)
*
* Examples:
*
* math.or(2, 4); // returns true
*
* a = [2, 5, 0];
* b = [0, 22, 0];
* c = 0;
*
* math.or(a, b); // returns [true, true, false]
* math.or(b, c); // returns [false, true, false]
*
* See also:
*
* and, not, xor
*
* @param {number | BigNumber | Complex | Unit | Array | Matrix} x First value to check
* @param {number | BigNumber | Complex | Unit | Array | Matrix} y Second value to check
* @return {boolean | Array | Matrix}
* Returns true when one of the inputs is defined with a nonzero/nonempty value.
*/
var or = typed('or', {
'number, number': function (x, y) {
return !!(x || y);
},
'Complex, Complex': function (x, y) {
return (x.re !== 0 || x.im !== 0) || (y.re !== 0 || y.im !== 0);
},
'BigNumber, BigNumber': function (x, y) {
return (!x.isZero() && !x.isNaN()) || (!y.isZero() && !y.isNaN());
},
'Unit, Unit': function (x, y) {
return or(x.value, y.value);
},
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse + sparse
c = algorithm05(x, y, or);
break;
default:
// sparse + dense
c = algorithm03(y, x, or, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense + sparse
c = algorithm03(x, y, or, false);
break;
default:
// dense + dense
c = algorithm13(x, y, or);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return or(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return or(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return or(x, matrix(y));
},
'Matrix, any': function (x, y) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm12(x, y, or, false);
break;
default:
c = algorithm14(x, y, or, false);
break;
}
return c;
},
'any, Matrix': function (x, y) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm12(y, x, or, true);
break;
default:
c = algorithm14(y, x, or, true);
break;
}
return c;
},
'Array, any': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, or, false).valueOf();
},
'any, Array': function (x, y) {
// use matrix implementation
return algorithm14(matrix(y), x, or, true).valueOf();
}
});
or.toTex = {
2: '\\left(${args[0]}' + latex.operators['or'] + '${args[1]}\\right)'
};
return or;
}
exports.name = 'or';
exports.factory = factory;

View File

@@ -0,0 +1,158 @@
'use strict';
function factory (type, config, load, typed) {
var latex = require('../../utils/latex');
var matrix = load(require('../../type/matrix/function/matrix'));
var algorithm03 = load(require('../../type/matrix/utils/algorithm03'));
var algorithm07 = load(require('../../type/matrix/utils/algorithm07'));
var algorithm12 = load(require('../../type/matrix/utils/algorithm12'));
var algorithm13 = load(require('../../type/matrix/utils/algorithm13'));
var algorithm14 = load(require('../../type/matrix/utils/algorithm14'));
/**
* Logical `xor`. Test whether one and only one value is defined with a nonzero/nonempty value.
* For matrices, the function is evaluated element wise.
*
* Syntax:
*
* math.xor(x, y)
*
* Examples:
*
* math.xor(2, 4); // returns false
*
* a = [2, 0, 0];
* b = [2, 7, 0];
* c = 0;
*
* math.xor(a, b); // returns [false, true, false]
* math.xor(a, c); // returns [true, false, false]
*
* See also:
*
* and, not, or
*
* @param {number | BigNumber | Complex | Unit | Array | Matrix} x First value to check
* @param {number | BigNumber | Complex | Unit | Array | Matrix} y Second value to check
* @return {boolean | Array | Matrix}
* Returns true when one and only one input is defined with a nonzero/nonempty value.
*/
var xor = typed('xor', {
'number, number': function (x, y) {
return !!(!!x ^ !!y);
},
'Complex, Complex': function (x, y) {
return ((x.re !== 0 || x.im !== 0) !== (y.re !== 0 || y.im !== 0));
},
'BigNumber, BigNumber': function (x, y) {
return ((!x.isZero() && !x.isNaN()) !== (!y.isZero() && !y.isNaN()));
},
'Unit, Unit': function (x, y) {
return xor(x.value, y.value);
},
'Matrix, Matrix': function (x, y) {
// result
var c;
// process matrix storage
switch (x.storage()) {
case 'sparse':
switch (y.storage()) {
case 'sparse':
// sparse + sparse
c = algorithm07(x, y, xor);
break;
default:
// sparse + dense
c = algorithm03(y, x, xor, true);
break;
}
break;
default:
switch (y.storage()) {
case 'sparse':
// dense + sparse
c = algorithm03(x, y, xor, false);
break;
default:
// dense + dense
c = algorithm13(x, y, xor);
break;
}
break;
}
return c;
},
'Array, Array': function (x, y) {
// use matrix implementation
return xor(matrix(x), matrix(y)).valueOf();
},
'Array, Matrix': function (x, y) {
// use matrix implementation
return xor(matrix(x), y);
},
'Matrix, Array': function (x, y) {
// use matrix implementation
return xor(x, matrix(y));
},
'Matrix, any': function (x, y) {
// result
var c;
// check storage format
switch (x.storage()) {
case 'sparse':
c = algorithm12(x, y, xor, false);
break;
default:
c = algorithm14(x, y, xor, false);
break;
}
return c;
},
'any, Matrix': function (x, y) {
// result
var c;
// check storage format
switch (y.storage()) {
case 'sparse':
c = algorithm12(y, x, xor, true);
break;
default:
c = algorithm14(y, x, xor, true);
break;
}
return c;
},
'Array, any': function (x, y) {
// use matrix implementation
return algorithm14(matrix(x), y, xor, false).valueOf();
},
'any, Array': function (x, y) {
// use matrix implementation
return algorithm14(matrix(y), x, xor, true).valueOf();
}
});
xor.toTex = {
2: '\\left(${args[0]}' + latex.operators['xor'] + '${args[1]}\\right)'
};
return xor;
}
exports.name = 'xor';
exports.factory = factory;

View File

@@ -0,0 +1,144 @@
'use strict';
var clone = require('../../utils/object').clone;
var isInteger = require('../../utils/number').isInteger;
var array = require('../../utils/array');
var IndexError = require('../../error/IndexError');
var DimensionError = require('../../error/DimensionError');
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
/**
* Concatenate two or more matrices.
*
* Syntax:
*
* math.concat(A, B, C, ...)
* math.concat(A, B, C, ..., dim)
*
* Where:
*
* - `dim: number` is a zero-based dimension over which to concatenate the matrices.
* By default the last dimension of the matrices.
*
* Examples:
*
* var A = [[1, 2], [5, 6]];
* var B = [[3, 4], [7, 8]];
*
* math.concat(A, B); // returns [[1, 2, 3, 4], [5, 6, 7, 8]]
* math.concat(A, B, 0); // returns [[1, 2], [5, 6], [3, 4], [7, 8]]
* math.concat('hello', ' ', 'world'); // returns 'hello world'
*
* See also:
*
* size, squeeze, subset, transpose
*
* @param {... Array | Matrix} args Two or more matrices
* @return {Array | Matrix} Concatenated matrix
*/
var concat = typed('concat', {
// TODO: change signature to '...Array | Matrix, dim?' when supported
'...Array | Matrix | number | BigNumber': function (args) {
var i;
var len = args.length;
var dim = -1; // zero-based dimension
var prevDim;
var asMatrix = false;
var matrices = []; // contains multi dimensional arrays
for (i = 0; i < len; i++) {
var arg = args[i];
// test whether we need to return a Matrix (if not we return an Array)
if (arg && arg.isMatrix === true) {
asMatrix = true;
}
if (typeof arg === 'number' || (arg && arg.isBigNumber === true)) {
if (i !== len - 1) {
throw new Error('Dimension must be specified as last argument');
}
// last argument contains the dimension on which to concatenate
prevDim = dim;
dim = arg.valueOf(); // change BigNumber to number
if (!isInteger(dim)) {
throw new TypeError('Integer number expected for dimension');
}
if (dim < 0 || (i > 0 && dim > prevDim)) {
// TODO: would be more clear when throwing a DimensionError here
throw new IndexError(dim, prevDim + 1);
}
}
else {
// this is a matrix or array
var m = clone(arg).valueOf();
var size = array.size(m);
matrices[i] = m;
prevDim = dim;
dim = size.length - 1;
// verify whether each of the matrices has the same number of dimensions
if (i > 0 && dim != prevDim) {
throw new DimensionError(prevDim + 1, dim + 1);
}
}
}
if (matrices.length == 0) {
throw new SyntaxError('At least one matrix expected');
}
var res = matrices.shift();
while (matrices.length) {
res = _concat(res, matrices.shift(), dim, 0);
}
return asMatrix ? matrix(res) : res;
},
'...string': function (args) {
return args.join('');
}
});
concat.toTex = undefined; // use default template
return concat;
}
/**
* Recursively concatenate two matrices.
* The contents of the matrices is not cloned.
* @param {Array} a Multi dimensional array
* @param {Array} b Multi dimensional array
* @param {number} concatDim The dimension on which to concatenate (zero-based)
* @param {number} dim The current dim (zero-based)
* @return {Array} c The concatenated matrix
* @private
*/
function _concat(a, b, concatDim, dim) {
if (dim < concatDim) {
// recurse into next dimension
if (a.length != b.length) {
throw new DimensionError(a.length, b.length);
}
var c = [];
for (var i = 0; i < a.length; i++) {
c[i] = _concat(a[i], b[i], concatDim, dim + 1);
}
return c;
}
else {
// concatenate this dimension
return a.concat(b);
}
}
exports.name = 'concat';
exports.factory = factory;

View File

@@ -0,0 +1,101 @@
'use strict';
var array = require('../../utils/array');
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
var subtract = load(require('../arithmetic/subtract'));
var multiply = load(require('../arithmetic/multiply'));
/**
* Calculate the cross product for two vectors in three dimensional space.
* The cross product of `A = [a1, a2, a3]` and `B = [b1, b2, b3]` is defined
* as:
*
* cross(A, B) = [
* a2 * b3 - a3 * b2,
* a3 * b1 - a1 * b3,
* a1 * b2 - a2 * b1
* ]
*
* If one of the input vectors has a dimension greater than 1, the output
* vector will be a 1x3 (2-dimensional) matrix.
*
* Syntax:
*
* math.cross(x, y)
*
* Examples:
*
* math.cross([1, 1, 0], [0, 1, 1]); // Returns [1, -1, 1]
* math.cross([3, -3, 1], [4, 9, 2]); // Returns [-15, -2, 39]
* math.cross([2, 3, 4], [5, 6, 7]); // Returns [-3, 6, -3]
* math.cross([[1, 2, 3]], [[4], [5], [6]]); // Returns [[-3, 6, -3]]
*
* See also:
*
* dot, multiply
*
* @param {Array | Matrix} x First vector
* @param {Array | Matrix} y Second vector
* @return {Array | Matrix} Returns the cross product of `x` and `y`
*/
var cross = typed('cross', {
'Matrix, Matrix': function (x, y) {
return matrix(_cross(x.toArray(), y.toArray()));
},
'Matrix, Array': function (x, y) {
return matrix(_cross(x.toArray(), y));
},
'Array, Matrix': function (x, y) {
return matrix(_cross(x, y.toArray()));
},
'Array, Array': _cross
});
cross.toTex = {
2: '\\left(${args[0]}\\right)\\times\\left(${args[1]}\\right)'
};
return cross;
/**
* Calculate the cross product for two arrays
* @param {Array} x First vector
* @param {Array} y Second vector
* @returns {Array} Returns the cross product of x and y
* @private
*/
function _cross(x, y) {
var highestDimension = Math.max(array.size(x).length, array.size(y).length);
x = array.squeeze(x);
y = array.squeeze(y);
var xSize = array.size(x);
var ySize = array.size(y);
if (xSize.length != 1 || ySize.length != 1 || xSize[0] != 3 || ySize[0] != 3) {
throw new RangeError('Vectors with length 3 expected ' +
'(Size A = [' + xSize.join(', ') + '], B = [' + ySize.join(', ') + '])');
}
var product = [
subtract(multiply(x[1], y[2]), multiply(x[2], y[1])),
subtract(multiply(x[2], y[0]), multiply(x[0], y[2])),
subtract(multiply(x[0], y[1]), multiply(x[1], y[0]))
];
if (highestDimension > 1) {
return [product];
} else {
return product;
}
}
}
exports.name = 'cross';
exports.factory = factory;

View File

@@ -0,0 +1,168 @@
'use strict';
var util = require('../../utils/index');
var object = util.object;
var string = util.string;
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
var add = load(require('../arithmetic/add'));
var subtract = load(require('../arithmetic/subtract'));
var multiply = load(require('../arithmetic/multiply'));
var unaryMinus = load(require('../arithmetic/unaryMinus'));
/**
* Calculate the determinant of a matrix.
*
* Syntax:
*
* math.det(x)
*
* Examples:
*
* math.det([[1, 2], [3, 4]]); // returns -2
*
* var A = [
* [-2, 2, 3],
* [-1, 1, 3],
* [2, 0, -1]
* ]
* math.det(A); // returns 6
*
* See also:
*
* inv
*
* @param {Array | Matrix} x A matrix
* @return {number} The determinant of `x`
*/
var det = typed('det', {
'any': function (x) {
return object.clone(x);
},
'Array | Matrix': function det (x) {
var size;
if (x && x.isMatrix === true) {
size = x.size();
}
else if (Array.isArray(x)) {
x = matrix(x);
size = x.size();
}
else {
// a scalar
size = [];
}
switch (size.length) {
case 0:
// scalar
return object.clone(x);
case 1:
// vector
if (size[0] == 1) {
return object.clone(x.valueOf()[0]);
}
else {
throw new RangeError('Matrix must be square ' +
'(size: ' + string.format(size) + ')');
}
case 2:
// two dimensional array
var rows = size[0];
var cols = size[1];
if (rows == cols) {
return _det(x.clone().valueOf(), rows, cols);
}
else {
throw new RangeError('Matrix must be square ' +
'(size: ' + string.format(size) + ')');
}
default:
// multi dimensional array
throw new RangeError('Matrix must be two dimensional ' +
'(size: ' + string.format(size) + ')');
}
}
});
det.toTex = {1: '\\det\\left(${args[0]}\\right)'};
return det;
/**
* Calculate the determinant of a matrix
* @param {Array[]} matrix A square, two dimensional matrix
* @param {number} rows Number of rows of the matrix (zero-based)
* @param {number} cols Number of columns of the matrix (zero-based)
* @returns {number} det
* @private
*/
function _det (matrix, rows, cols) {
if (rows == 1) {
// this is a 1 x 1 matrix
return object.clone(matrix[0][0]);
}
else if (rows == 2) {
// this is a 2 x 2 matrix
// the determinant of [a11,a12;a21,a22] is det = a11*a22-a21*a12
return subtract(
multiply(matrix[0][0], matrix[1][1]),
multiply(matrix[1][0], matrix[0][1])
);
}
else {
// this is an n x n matrix
var compute_mu = function (matrix) {
var i, j;
// Compute the matrix with zero lower triangle, same upper triangle,
// and diagonals given by the negated sum of the below diagonal
// elements.
var mu = new Array(matrix.length);
var sum = 0;
for (i = 1; i < matrix.length; i++) {
sum = add(sum, matrix[i][i]);
}
for (i = 0; i < matrix.length; i++) {
mu[i] = new Array(matrix.length);
mu[i][i] = unaryMinus(sum);
for (j = 0; j < i; j++) {
mu[i][j] = 0; // TODO: make bignumber 0 in case of bignumber computation
}
for (j = i + 1; j < matrix.length; j++) {
mu[i][j] = matrix[i][j];
}
if (i+1 < matrix.length) {
sum = subtract(sum, matrix[i + 1][i + 1]);
}
}
return mu;
};
var fa = matrix;
for (var i = 0; i < rows - 1; i++) {
fa = multiply(compute_mu(fa), matrix);
}
if (rows % 2 == 0) {
return unaryMinus(fa[0][0]);
} else {
return fa[0][0];
}
}
}
}
exports.name = 'det';
exports.factory = factory;

View File

@@ -0,0 +1,170 @@
'use strict';
var array = require('../../utils/array');
var clone = require('../../utils/object').clone;
var isInteger = require('../../utils/number').isInteger;
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
/**
* Create a diagonal matrix or retrieve the diagonal of a matrix
*
* When `x` is a vector, a matrix with vector `x` on the diagonal will be returned.
* When `x` is a two dimensional matrix, the matrixes `k`th diagonal will be returned as vector.
* When k is positive, the values are placed on the super diagonal.
* When k is negative, the values are placed on the sub diagonal.
*
* Syntax:
*
* math.diag(X)
* math.diag(X, format)
* math.diag(X, k)
* math.diag(X, k, format)
*
* Examples:
*
* // create a diagonal matrix
* math.diag([1, 2, 3]); // returns [[1, 0, 0], [0, 2, 0], [0, 0, 3]]
* math.diag([1, 2, 3], 1); // returns [[0, 1, 0, 0], [0, 0, 2, 0], [0, 0, 0, 3]]
* math.diag([1, 2, 3], -1); // returns [[0, 0, 0], [1, 0, 0], [0, 2, 0], [0, 0, 3]]
*
* // retrieve the diagonal from a matrix
* var a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
* math.diag(a); // returns [1, 5, 9]
*
* See also:
*
* ones, zeros, eye
*
* @param {Matrix | Array} x A two dimensional matrix or a vector
* @param {number | BigNumber} [k=0] The diagonal where the vector will be filled
* in or retrieved.
* @param {string} [format='dense'] The matrix storage format.
*
* @returns {Matrix | Array} Diagonal matrix from input vector, or diagonal from input matrix.
*/
var diag = typed('diag', {
// FIXME: simplify this huge amount of signatures as soon as typed-function supports optional arguments
'Array': function (x) {
return _diag(x, 0, array.size(x), null);
},
'Array, number': function (x, k) {
return _diag(x, k, array.size(x), null);
},
'Array, BigNumber': function (x, k) {
return _diag(x, k.toNumber(), array.size(x), null);
},
'Array, string': function (x, format) {
return _diag(x, 0, array.size(x), format);
},
'Array, number, string': function (x, k, format) {
return _diag(x, k, array.size(x), format);
},
'Array, BigNumber, string': function (x, k, format) {
return _diag(x, k.toNumber(), array.size(x), format);
},
'Matrix': function (x) {
return _diag(x, 0, x.size(), x.storage());
},
'Matrix, number': function (x, k) {
return _diag(x, k, x.size(), x.storage());
},
'Matrix, BigNumber': function (x, k) {
return _diag(x, k.toNumber(), x.size(), x.storage());
},
'Matrix, string': function (x, format) {
return _diag(x, 0, x.size(), format);
},
'Matrix, number, string': function (x, k, format) {
return _diag(x, k, x.size(), format);
},
'Matrix, BigNumber, string': function (x, k, format) {
return _diag(x, k.toNumber(), x.size(), format);
}
});
diag.toTex = undefined; // use default template
return diag;
/**
* Creeate diagonal matrix from a vector or vice versa
* @param {Array | Matrix} x
* @param {number} k
* @param {string} format Storage format for matrix. If null,
* an Array is returned
* @returns {Array | Matrix}
* @private
*/
function _diag (x, k, size, format) {
if (!isInteger(k)) {
throw new TypeError ('Second parameter in function diag must be an integer');
}
var kSuper = k > 0 ? k : 0;
var kSub = k < 0 ? -k : 0;
// check dimensions
switch (size.length) {
case 1:
return _createDiagonalMatrix(x, k, format, size[0], kSub, kSuper);
case 2:
return _getDiagonal(x, k, format, size, kSub, kSuper);
}
throw new RangeError('Matrix for function diag must be 2 dimensional');
}
function _createDiagonalMatrix(x, k, format, l, kSub, kSuper) {
// matrix size
var ms = [l + kSub, l + kSuper];
// get matrix constructor
var F = type.Matrix.storage(format || 'dense');
// create diagonal matrix
var m = F.diagonal(ms, x, k);
// check we need to return a matrix
return format !== null ? m : m.valueOf();
}
function _getDiagonal(x, k, format, s, kSub, kSuper) {
// check x is a Matrix
if (x && x.isMatrix === true) {
// get diagonal matrix
var dm = x.diagonal(k);
// check we need to return a matrix
if (format !== null) {
// check we need to change matrix format
if (format !== dm.storage())
return matrix(dm, format);
return dm;
}
return dm.valueOf();
}
// vector size
var n = Math.min(s[0] - kSub, s[1] - kSuper);
// diagonal values
var vector = [];
// loop diagonal
for (var i = 0; i < n; i++) {
vector[i] = x[i + kSub][i + kSuper];
}
// check we need to return a matrix
return format !== null ? matrix(vector) : vector;
}
}
exports.name = 'diag';
exports.factory = factory;

View File

@@ -0,0 +1,79 @@
'use strict';
var size = require('../../utils/array').size;
function factory (type, config, load, typed) {
var add = load(require('../arithmetic/add'));
var multiply = load(require('../arithmetic/multiply'));
/**
* Calculate the dot product of two vectors. The dot product of
* `A = [a1, a2, a3, ..., an]` and `B = [b1, b2, b3, ..., bn]` is defined as:
*
* dot(A, B) = a1 * b1 + a2 * b2 + a3 * b3 + ... + an * bn
*
* Syntax:
*
* math.dot(x, y)
*
* Examples:
*
* math.dot([2, 4, 1], [2, 2, 3]); // returns number 15
* math.multiply([2, 4, 1], [2, 2, 3]); // returns number 15
*
* See also:
*
* multiply, cross
*
* @param {Array | Matrix} x First vector
* @param {Array | Matrix} y Second vector
* @return {number} Returns the dot product of `x` and `y`
*/
var dot = typed('dot', {
'Matrix, Matrix': function (x, y) {
return _dot(x.toArray(), y.toArray());
},
'Matrix, Array': function (x, y) {
return _dot(x.toArray(), y);
},
'Array, Matrix': function (x, y) {
return _dot(x, y.toArray());
},
'Array, Array': _dot
});
dot.toTex = {2: '\\left(${args[0]}\\cdot${args[1]}\\right)'};
return dot;
/**
* Calculate the dot product for two arrays
* @param {Array} x First vector
* @param {Array} y Second vector
* @returns {number} Returns the dot product of x and y
* @private
*/
// TODO: double code with math.multiply
function _dot(x, y) {
var xSize= size(x);
var ySize = size(y);
var len = xSize[0];
if (xSize.length !== 1 || ySize.length !== 1) throw new RangeError('Vector expected'); // TODO: better error message
if (xSize[0] != ySize[0]) throw new RangeError('Vectors must have equal length (' + xSize[0] + ' != ' + ySize[0] + ')');
if (len == 0) throw new RangeError('Cannot calculate the dot product of empty vectors');
var prod = 0;
for (var i = 0; i < len; i++) {
prod = add(prod, multiply(x[i], y[i]));
}
return prod;
}
}
exports.name = 'dot';
exports.factory = factory;

View File

@@ -0,0 +1,146 @@
'use strict';
var array = require('../../utils/array');
var isInteger = require('../../utils/number').isInteger;
function factory (type, config, load, typed) {
var matrix = load(require('../../type/matrix/function/matrix'));
/**
* Create a 2-dimensional identity matrix with size m x n or n x n.
* The matrix has ones on the diagonal and zeros elsewhere.
*
* Syntax:
*
* math.eye(n)
* math.eye(n, format)
* math.eye(m, n)
* math.eye(m, n, format)
* math.eye([m, n])
* math.eye([m, n], format)
*
* Examples:
*
* math.eye(3); // returns [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
* math.eye(3, 2); // returns [[1, 0], [0, 1], [0, 0]]
*
* var A = [[1, 2, 3], [4, 5, 6]];
* math.eye(math.size(A)); // returns [[1, 0, 0], [0, 1, 0]]
*
* See also:
*
* diag, ones, zeros, size, range
*
* @param {...number | Matrix | Array} size The size for the matrix
* @param {string} [format] The Matrix storage format
*
* @return {Matrix | Array | number} A matrix with ones on the diagonal.
*/
var eye = typed('eye', {
'': function () {
return (config.matrix === 'Matrix') ? matrix([]) : [];
},
'string': function (format) {
return matrix(format);
},
'number | BigNumber': function (rows) {
return _eye(rows, rows, config.matrix === 'Matrix' ? 'default' : undefined);
},
'number | BigNumber, string': function (rows, format) {
return _eye(rows, rows, format);
},
'number | BigNumber, number | BigNumber': function (rows, cols) {
return _eye(rows, cols, config.matrix === 'Matrix' ? 'default' : undefined);
},
'number | BigNumber, number | BigNumber, string': function (rows, cols, format) {
return _eye(rows, cols, format);
},
'Array': function (size) {
return _eyeVector(size);
},
'Array, string': function (size, format) {
return _eyeVector(size, format);
},
'Matrix': function (size) {
return _eyeVector(size.valueOf(), size.storage());
},
'Matrix, string': function (size, format) {
return _eyeVector(size.valueOf(), format);
}
});
eye.toTex = undefined; // use default template
return eye;
function _eyeVector (size, format) {
switch (size.length) {
case 0: return format ? matrix(format) : [];
case 1: return _eye(size[0], size[0], format);
case 2: return _eye(size[0], size[1], format);
default: throw new Error('Vector containing two values expected');
}
}
/**
* Create an identity matrix
* @param {number | BigNumber} rows
* @param {number | BigNumber} cols
* @param {string} [format]
* @returns {Matrix}
* @private
*/
function _eye (rows, cols, format) {
// BigNumber constructor with the right precision
var Big = (rows && rows.isBigNumber === true)
? type.BigNumber
: (cols && cols.isBigNumber === true)
? type.BigNumber
: null;
if (rows && rows.isBigNumber === true) rows = rows.toNumber();
if (cols && cols.isBigNumber === true) cols = cols.toNumber();
if (!isInteger(rows) || rows < 1) {
throw new Error('Parameters in function eye must be positive integers');
}
if (!isInteger(cols) || cols < 1) {
throw new Error('Parameters in function eye must be positive integers');
}
var one = Big ? new type.BigNumber(1) : 1;
var defaultValue = Big ? new Big(0) : 0;
var size = [rows, cols];
// check we need to return a matrix
if (format) {
// get matrix storage constructor
var F = type.Matrix.storage(format);
// create diagonal matrix (use optimized implementation for storage format)
return F.diagonal(size, one, 0, defaultValue);
}
// create and resize array
var res = array.resize([], size, defaultValue);
// fill in ones on the diagonal
var minimum = rows < cols ? rows : cols;
// fill diagonal
for (var d = 0; d < minimum; d++) {
res[d][d] = one;
}
return res;
}
}
exports.name = 'eye';
exports.factory = factory;

Some files were not shown because too many files have changed in this diff Show More