mirror of
https://github.com/josdejong/mathjs.git
synced 2025-12-08 19:46:04 +00:00
190 lines
5.4 KiB
JavaScript
190 lines
5.4 KiB
JavaScript
'use strict';
|
|
|
|
var collection = require('../../type/collection');
|
|
var size = require('../../../lib/util/array').size;
|
|
|
|
function factory (type, config, load, typed) {
|
|
var abs = load(require('../arithmetic/abs'));
|
|
var add = load(require('../arithmetic/add'));
|
|
var multiply = load(require('../arithmetic/multiply'));
|
|
var pow = load(require('../arithmetic/pow'));
|
|
var sqrt = load(require('../arithmetic/sqrt'));
|
|
var equal = load(require('../relational/equal'));
|
|
var larger = load(require('../relational/larger'));
|
|
var smaller = load(require('../relational/smaller'));
|
|
var max = load(require('../statistics/max'));
|
|
var sum = load(require('../statistics/sum'));
|
|
var diag = load(require('../matrix/diag'));
|
|
var transpose = load(require('../matrix/transpose'));
|
|
|
|
var complexAbs = abs.signatures['Complex'];
|
|
|
|
/**
|
|
* 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
|
|
*
|
|
* @param {Number | BigNumber | Complex | Boolean | Array | Matrix | null} 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': complexAbs,
|
|
|
|
'BigNumber': function (x) {
|
|
return x.abs();
|
|
},
|
|
|
|
'Array | Matrix': function (x) {
|
|
return _norm(x.valueOf(), 2);
|
|
},
|
|
|
|
'number | Complex | BigNumber, number | BigNumber | string': function (x, p) {
|
|
return norm(x);
|
|
},
|
|
|
|
'Array | Matrix, number | BigNumber | string': function (x, p) {
|
|
return _norm(x.valueOf(), p.valueOf());
|
|
}
|
|
});
|
|
|
|
/**
|
|
* 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 = size(x);
|
|
|
|
// 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 n;
|
|
for (var i = 0; i < x.length; i++) {
|
|
var v = abs(x[i]);
|
|
if (!n || larger(v, n)) {
|
|
n = v;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
else if (p === Number.NEGATIVE_INFINITY || p === '-inf') {
|
|
// norm(x, -Infinity) = min(abs(x))
|
|
var n;
|
|
for (var i = 0; i < x.length; i++) {
|
|
var v = abs(x[i]);
|
|
if (!n || smaller(v, n)) {
|
|
n = v;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
else if (p === 'fro') {
|
|
return _norm(x, 2);
|
|
}
|
|
else if (typeof p === 'number' && !isNaN(p)) {
|
|
// check p != 0
|
|
if (!equal(p, 0)) {
|
|
// norm(x, p) = sum(abs(xi) ^ p) ^ 1/p
|
|
var n = 0;
|
|
for (var i = 0; i < x.length; i++) {
|
|
n = add(pow(abs(x[i]), p), n);
|
|
}
|
|
return pow(n, 1 / p);
|
|
}
|
|
return Number.POSITIVE_INFINITY;
|
|
}
|
|
else {
|
|
// invalid parameter value
|
|
throw new Error('Unsupported parameter value');
|
|
}
|
|
}
|
|
else if (sizeX.length == 2) {
|
|
// check p
|
|
if (p === 1) {
|
|
// norm(x) = the largest column sum
|
|
var c = [];
|
|
// loop rows
|
|
for (var i = 0; i < x.length; i++) {
|
|
var r = x[i];
|
|
// loop columns
|
|
for (var j = 0; j < r.length; j++) {
|
|
c[j] = add(c[j] || 0, abs(r[j]));
|
|
}
|
|
}
|
|
return max(c); // TODO: improve performance by immediately calculating the max in the for loops
|
|
}
|
|
else if (p === Number.POSITIVE_INFINITY || p === 'inf') {
|
|
// norm(x) = the largest row sum
|
|
var n = 0;
|
|
// loop rows
|
|
for (var i = 0; i < x.length; i++) {
|
|
var rs = 0;
|
|
var r = x[i];
|
|
// loop columns
|
|
for (var j = 0; j < r.length; j++) {
|
|
rs = add(rs, abs(r[j]));
|
|
}
|
|
if (larger(rs, n)) {
|
|
n = rs;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
else if (p === 'fro') {
|
|
// norm(x) = sqrt(sum(diag(x'x)))
|
|
return sqrt(sum(diag(multiply(transpose(x), x))));
|
|
}
|
|
else if (p === 2) {
|
|
// not implemented
|
|
throw new Error('Unsupported parameter value, missing implementation of matrix singular value decomposition');
|
|
}
|
|
else {
|
|
// invalid parameter value
|
|
throw new Error('Unsupported parameter value');
|
|
}
|
|
}
|
|
}
|
|
|
|
return norm;
|
|
}
|
|
|
|
exports.name = 'norm';
|
|
exports.factory = factory;
|