From fc8dde607d2479e52bece45998dc12944a5cd3fd Mon Sep 17 00:00:00 2001 From: rjbaucells Date: Mon, 23 Mar 2015 00:09:06 -0400 Subject: [PATCH] math.norm() --- lib/function/arithmetic/norm.js | 99 +++++++++++++++--------------- lib/type/matrix/CcsMatrix.js | 21 ++++--- lib/type/matrix/CrsMatrix.js | 21 ++++--- test/type/matrix/CcsMatrix.test.js | 13 ++++ test/type/matrix/CrsMatrix.test.js | 13 ++++ 5 files changed, 103 insertions(+), 64 deletions(-) diff --git a/lib/function/arithmetic/norm.js b/lib/function/arithmetic/norm.js index 4ea91c2f2..380ed6d7f 100644 --- a/lib/function/arithmetic/norm.js +++ b/lib/function/arithmetic/norm.js @@ -3,12 +3,9 @@ module.exports = function (math) { var util = require('../../util/index'), - array = require('../../../lib/util/array'), - BigNumber = math.type.BigNumber, Complex = require('../../type/Complex'), Matrix = math.type.Matrix, - collection = math.collection, isNumber = util.number.isNumber, isBoolean = util['boolean'].isBoolean, @@ -68,11 +65,11 @@ module.exports = function (math) { var re = Math.abs(x.re); var im = Math.abs(x.im); if (re >= im) { - var x = im / re; - return re * Math.sqrt(1 + x * x); + var i = im / re; + return re * Math.sqrt(1 + i * i); } - var y = re / im; - return im * Math.sqrt(1 + y * y); + var j = re / im; + return im * Math.sqrt(1 + j * j); } if (x instanceof BigNumber) { @@ -86,8 +83,13 @@ module.exports = function (math) { } if (isArray(x)) { + // use matrix optimized operations + return norm(math.matrix(x), p); + } + + if (x instanceof Matrix) { // size - var sizeX = array.size(x); + var sizeX = x.size(); // missing p if (p == null) p = 2; @@ -97,21 +99,25 @@ module.exports = function (math) { if (p === Number.POSITIVE_INFINITY || p === 'inf') { // norm(x, Infinity) = max(abs(x)) var n; - math.forEach(x, function (value) { - var v = math.abs(value); - if (!n || math.larger(v, n)) - n = v; - }); + x.forEach( + function (value) { + var v = math.abs(value); + if (!n || math.larger(v, n)) + n = v; + }, + true); return n; } if (p === Number.NEGATIVE_INFINITY || p === '-inf') { // norm(x, -Infinity) = min(abs(x)) var n; - math.forEach(x, function (value) { - var v = math.abs(value); - if (!n || math.smaller(v, n)) - n = v; - }); + x.forEach( + function (value) { + var v = math.abs(value); + if (!n || math.smaller(v, n)) + n = v; + }, + true); return n; } if (p === 'fro') @@ -121,9 +127,11 @@ module.exports = function (math) { if (!math.equal(p, 0)) { // norm(x, p) = sum(abs(xi) ^ p) ^ 1/p var n = 0; - math.forEach(x, function (value) { - n = math.add(math.pow(math.abs(value), p), n); - }); + x.forEach( + function (value) { + n = math.add(math.pow(math.abs(value), p), n); + }, + true); return math.pow(n, 1 / p); } return Number.POSITIVE_INFINITY; @@ -136,39 +144,34 @@ module.exports = function (math) { 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] = math.add(c[j] || 0, math.abs(r[j])); - } - } + x.forEach( + function (value, index) { + var j = index[1]; + c[j] = math.add(c[j] || 0, math.abs(value)); + }, + true); return math.max(c); } 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 = math.add(rs, math.abs(r[j])); - } - if (math.larger(rs, n)) - n = rs; - } - return n; + var r = []; + x.forEach( + function (value, index) { + var i = index[0]; + r[i] = math.add(r[i] || 0, math.abs(value)); + }, + true); + return math.max(r); } if (p === 'fro') { // norm(x) = sqrt(sum(diag(x'x))) - var d = math.diag(math.multiply(math.transpose(x), x)); + var d = x.transpose().multiply(x).diagonal(); var s = 0; - math.forEach(d, function (value) { - s = math.add(value, s); - }); + d.forEach( + function (value) { + s = math.add(s, value); + }, + true); return math.sqrt(s); } if (p == 2) { @@ -180,10 +183,6 @@ module.exports = function (math) { } } - if (x instanceof Matrix) { - return norm(x.valueOf(), p); - } - throw new math.error.UnsupportedTypeError('norm', x); }; }; diff --git a/lib/type/matrix/CcsMatrix.js b/lib/type/matrix/CcsMatrix.js index d96d53968..aec822730 100644 --- a/lib/type/matrix/CcsMatrix.js +++ b/lib/type/matrix/CcsMatrix.js @@ -664,8 +664,9 @@ module.exports = function (math) { * @param {function} callback The callback function is invoked with three * parameters: the value of the element, the index * of the element, and the Matrix being traversed. + * @param {boolean} [skipZeros] Invoke callback function for non-zero values only. */ - CcsMatrix.prototype.forEach = function (callback) { + CcsMatrix.prototype.forEach = function (callback, skipZeros) { // matrix instance var me = this; // rows and columns @@ -682,17 +683,23 @@ module.exports = function (math) { for (var k = k0; k < k1; k++) { // row index var i = this._index[k]; - // zero values - for (var x = p; x < i; x++) - callback(0, [x, j], me); + // check we need to process zeros + if (!skipZeros) { + // zero values + for (var x = p; x < i; x++) + callback(0, [x, j], me); + } // value @ k callback(this._values[k], [i, j], me); // update pointer p = i + 1; } - // zero values - for (var y = p; y < rows; y++) - callback(0, [y, j], me); + // check we need to process zeros + if (!skipZeros) { + // zero values + for (var y = p; y < rows; y++) + callback(0, [y, j], me); + } } }; diff --git a/lib/type/matrix/CrsMatrix.js b/lib/type/matrix/CrsMatrix.js index 7c8af19d6..5cf90065f 100644 --- a/lib/type/matrix/CrsMatrix.js +++ b/lib/type/matrix/CrsMatrix.js @@ -656,8 +656,9 @@ module.exports = function (math) { * @param {function} callback The callback function is invoked with three * parameters: the value of the element, the index * of the element, and the Matrix being traversed. + * @param {boolean} [skipZeros] Invoke callback function for non-zero values only. */ - CrsMatrix.prototype.forEach = function (callback) { + CrsMatrix.prototype.forEach = function (callback, skipZeros) { // matrix instance var me = this; // rows and columns @@ -674,17 +675,23 @@ module.exports = function (math) { for (var k = k0; k < k1; k++) { // column index var j = this._index[k]; - // zero values - for (var x = p; x < j; x++) - callback(0, [i, x], me); + // check we need to process zeros + if (!skipZeros) { + // zero values + for (var x = p; x < j; x++) + callback(0, [i, x], me); + } // value @ k callback(this._values[k], [i, j], me); // update pointer p = j + 1; } - // zero values - for (var y = p; y < columns; y++) - callback(0, [i, y], me); + // check we need to process zeros + if (!skipZeros) { + // zero values + for (var y = p; y < columns; y++) + callback(0, [i, y], me); + } } }; diff --git a/test/type/matrix/CcsMatrix.test.js b/test/type/matrix/CcsMatrix.test.js index f9b3dfdd0..df50ee6c7 100644 --- a/test/type/matrix/CcsMatrix.test.js +++ b/test/type/matrix/CcsMatrix.test.js @@ -1114,6 +1114,19 @@ describe('CcsMatrix', function() { m.forEach(function (value) { output.push(value); }); assert.deepEqual(output, []); }); + + it('should process non-zero values', function() { + var m = new CcsMatrix( + [ + [1, 0], + [0, 0] + ] + ); + var counter = 0; + + m.forEach(function () { counter++; }, true); + assert(counter === 1); + }); it('should invoke callback with parameters value, index, obj', function() { var m = new CcsMatrix([[1,2,3], [4,5,6]]); diff --git a/test/type/matrix/CrsMatrix.test.js b/test/type/matrix/CrsMatrix.test.js index 487837678..774522d43 100644 --- a/test/type/matrix/CrsMatrix.test.js +++ b/test/type/matrix/CrsMatrix.test.js @@ -1095,6 +1095,19 @@ describe('CrsMatrix', function() { assert.deepEqual(output, []); }); + it('should process non-zero values', function() { + var m = new CrsMatrix( + [ + [1, 0], + [0, 0] + ] + ); + var counter = 0; + + m.forEach(function () { counter++; }, true); + assert(counter === 1); + }); + it('should invoke callback with parameters value, index, obj', function() { var m = new CrsMatrix([[1,2,3], [4,5,6]]); var output = [];