From 472299d3418b894f320b3087c12e643fa19ea637 Mon Sep 17 00:00:00 2001 From: rjbaucells Date: Mon, 4 May 2015 11:26:37 -0400 Subject: [PATCH] sync --- lib/type/matrix/SparseMatrix.js | 185 +++++++++++++++++++++----------- 1 file changed, 123 insertions(+), 62 deletions(-) diff --git a/lib/type/matrix/SparseMatrix.js b/lib/type/matrix/SparseMatrix.js index dd64fdd76..60625d9af 100644 --- a/lib/type/matrix/SparseMatrix.js +++ b/lib/type/matrix/SparseMatrix.js @@ -9,37 +9,43 @@ var string = util.string; var number = util.number; var isArray = Array.isArray; -var isNumber = util.number.isNumber; -var isInteger = util.number.isInteger; +var isNumber = number.isNumber; +var isInteger = number.isInteger; +var isString = string.isString; var validateIndex = array.validateIndex; function factory (type, config, load) { + + var equalScalar = load(require('../../function/relational/equalScalar')); - var equal = load(require('../../function/relational/equal')); - - var Index = type.Index; - var BigNumber = type.BigNumber; var Matrix = type.Matrix; - function SparseMatrix(data) { + /** + * Sparse Matrix implementation. This type implements a Compressed Column Storage format + * for sparse matrices. + */ + function SparseMatrix(data, datatype) { if (!(this instanceof SparseMatrix)) throw new SyntaxError('Constructor must be called with the new operator'); - + if (datatype && !isString(datatype)) + throw new Error('Invalid datatype: ' + datatype); + if (data instanceof Matrix) { // create from matrix - _createFromMatrix(this, data); + _createFromMatrix(this, data, datatype); } else if (data && isArray(data.index) && isArray(data.ptr) && isArray(data.size)) { - // initialize fields (values can be undefined in pattern matrices) + // initialize fields this._values = data.values; this._index = data.index; this._ptr = data.ptr; this._size = data.size; + this._datatype = datatype || data.datatype; } else if (isArray(data)) { // create from array - _createFromArray(this, data); + _createFromArray(this, data, datatype); } else if (data) { // unsupported type @@ -51,10 +57,11 @@ function factory (type, config, load) { this._index = []; this._ptr = [0]; this._size = [0]; + this._datatype = datatype; } } - var _createFromMatrix = function (matrix, source) { + var _createFromMatrix = function (matrix, source, datatype) { // check matrix type if (source.type === 'SparseMatrix') { // clone arrays @@ -62,18 +69,20 @@ function factory (type, config, load) { matrix._index = object.clone(source._index); matrix._ptr = object.clone(source._ptr); matrix._size = object.clone(source._size); + matrix._datatype = datatype || source._datatype; } else { // build from matrix data - _createFromArray(matrix, source.valueOf()); + _createFromArray(matrix, source.valueOf(), datatype || source._datatype); } }; - var _createFromArray = function (matrix, data) { + var _createFromArray = function (matrix, data, datatype) { // initialize fields matrix._values = []; matrix._index = []; matrix._ptr = []; + matrix._datatype = datatype; // discover rows & columns, do not use math.size() to avoid looping array twice var rows = data.length; var columns = 0; @@ -99,7 +108,7 @@ function factory (type, config, load) { // value var v = row[j]; // check value != 0 - if (!equal(v, 0)) { + if (!equalScalar(v, 0)) { // store value matrix._values.push(v); // index @@ -112,7 +121,7 @@ function factory (type, config, load) { if (j === 0 && columns < 1) columns = 1; // check value != 0 (row is a scalar) - if (!equal(row, 0)) { + if (!equalScalar(row, 0)) { // store value matrix._values.push(row); // index @@ -131,7 +140,7 @@ function factory (type, config, load) { matrix._size = [rows, columns]; }; - SparseMatrix.prototype = new Matrix(); + SparseMatrix.prototype = new type.Matrix(); SparseMatrix.prototype.type = 'SparseMatrix'; @@ -146,6 +155,34 @@ function factory (type, config, load) { SparseMatrix.prototype.storage = function () { return 'sparse'; }; + + /** + * Get the datatype of the data stored in the matrix. + * + * Usage: + * var format = matrix.datatype() // retrieve matrix datatype + * + * @return {string} The datatype. + */ + SparseMatrix.prototype.datatype = function () { + return this._datatype; + }; + + /** + * Get the matrix density. + * + * Usage: + * var density = matrix.density() // retrieve matrix density + * + * @return {number} The matrix density. + */ + SparseMatrix.prototype.density = function () { + // rows & columns + var rows = this._size[0]; + var columns = this._size[1]; + // calculate density + return rows !== 0 && columns !== 0 ? (this._index.length / (rows * columns)) : 0; + }; /** * Get a subset of the matrix, or replace a subset of the matrix. @@ -160,7 +197,11 @@ function factory (type, config, load) { * the matrix is resized. If not provided, * new matrix elements will be filled with zeros. */ - SparseMatrix.prototype.subset = function (index, replacement, defaultValue) { + SparseMatrix.prototype.subset = function (index, replacement, defaultValue) { + // check it is a pattern matrix + if (!this._values) + throw new Error('Cannot invoke subset on a Pattern only matrix'); + // check arguments switch (arguments.length) { case 1: @@ -178,7 +219,7 @@ function factory (type, config, load) { var _getsubset = function (matrix, index) { // check index - if (!(index instanceof Index)) { + if (!(index instanceof type.Index)) { throw new TypeError('Invalid index'); } @@ -212,7 +253,7 @@ function factory (type, config, load) { var _setsubset = function (matrix, index, submatrix, defaultValue) { // check index - if (!(index instanceof Index)) { + if (!(index instanceof type.Index)) { throw new TypeError('Invalid index'); } @@ -222,7 +263,7 @@ function factory (type, config, load) { // calculate the size of the submatrix, and convert it into an Array if needed var sSize; - if (submatrix instanceof Matrix) { + if (submatrix instanceof type.Matrix) { // submatrix size sSize = submatrix.size(); // use array representation @@ -302,6 +343,10 @@ function factory (type, config, load) { if (index.length != this._size.length) throw new DimensionError(index.length, this._size.length); + // check it is a pattern matrix + if (!this._values) + throw new Error('Cannot invoke get on a Pattern only matrix'); + // row and column var i = index[0]; var j = index[1]; @@ -334,6 +379,10 @@ function factory (type, config, load) { if (index.length != this._size.length) throw new DimensionError(index.length, this._size.length); + // check it is a pattern matrix + if (!this._values) + throw new Error('Cannot invoke set on a Pattern only matrix'); + // row and column var i = index[0]; var j = index[1]; @@ -360,7 +409,7 @@ function factory (type, config, load) { // check k is prior to next column k and it is in the correct row if (k < this._ptr[j + 1] && this._index[k] === i) { // check value != 0 - if (!equal(v, 0)) { + if (!equalScalar(v, 0)) { // update value this._values[k] = v; } @@ -454,7 +503,7 @@ function factory (type, config, load) { // value to insert at the time of growing matrix var value = defaultValue || 0; // should we insert the value? - var ins = !equal(value, 0); + var ins = !equalScalar(value, 0); // old columns and rows var r = matrix._size[0]; @@ -561,10 +610,11 @@ function factory (type, config, load) { */ SparseMatrix.prototype.clone = function () { var m = new SparseMatrix({ - values: object.clone(this._values), + values: this._values ? object.clone(this._values) : undefined, index: object.clone(this._index), ptr: object.clone(this._ptr), - size: object.clone(this._size) + size: object.clone(this._size), + datatype: this._datatype }); return m; }; @@ -588,6 +638,9 @@ function factory (type, config, load) { * @return {SparseMatrix} matrix */ SparseMatrix.prototype.map = function (callback, skipZeros) { + // check it is a pattern matrix + if (!this._values) + throw new Error('Cannot invoke map on a Pattern only matrix'); // matrix instance var me = this; // rows and columns @@ -616,7 +669,7 @@ function factory (type, config, load) { // invoke callback v = callback(v, x, y); // check value != 0 - if (!equal(v, 0)) { + if (!equalScalar(v, 0)) { // store value values.push(v); // index @@ -674,6 +727,9 @@ function factory (type, config, load) { * @param {boolean} [skipZeros] Invoke callback function for non-zero values only. */ SparseMatrix.prototype.forEach = function (callback, skipZeros) { + // check it is a pattern matrix + if (!this._values) + throw new Error('Cannot invoke forEach on a Pattern only matrix'); // matrix instance var me = this; // rows and columns @@ -732,31 +788,27 @@ function factory (type, config, load) { var columns = size[1]; // result var a = new Array(rows); + // vars + var i, j; // initialize array - for (var q = 0; q < rows; q++) - a[q] = new Array(columns); + for (i = 0; i < rows; i++) { + a[i] = new Array(columns); + for (j = 0; j < columns; j++) + a[i][j] = 0; + } + // loop columns - for (var j = 0; j < columns; j++) { + for (j = 0; j < columns; j++) { // k0 <= k < k1 where k0 = _ptr[j] && k1 = _ptr[j+1] var k0 = ptr[j]; var k1 = ptr[j + 1]; - // row pointer - var p = 0; // loop k within [k0, k1[ for (var k = k0; k < k1; k++) { // row index - var i = index[k]; - // zeros - for (var x = p; x < i; x++) - a[x][j] = a[x][j] || 0; - // set value, check it is a pattern matrix + i = index[k]; + // set value (use one for pattern matrix) a[i][j] = values ? (copy ? object.clone(values[k]) : values[k]) : 1; - // update pointer - p = i + 1; } - // zero values - for (var y = p; y < rows; y++) - a[y][j] = a[y][j] || 0; } return a; }; @@ -773,8 +825,10 @@ function factory (type, config, load) { // rows and columns var rows = this._size[0]; var columns = this._size[1]; + // density + var density = this.density(); // rows & columns - var str = 'Sparse Matrix [' + string.format(rows, options) + ' x ' + string.format(columns, options) + '] density: ' + string.format(this._values.length / (rows * columns), options) + '\n'; + var str = 'Sparse Matrix [' + string.format(rows, options) + ' x ' + string.format(columns, options) + '] density: ' + string.format(density, options) + '\n'; // loop columns for (var j = 0; j < columns; j++) { // k0 <= k < k1 where k0 = _ptr[j] && k1 = _ptr[j+1] @@ -785,7 +839,7 @@ function factory (type, config, load) { // row index var i = this._index[k]; // append value - str += '\n (' + string.format(i, options) + ', ' + string.format(j, options) + ') ==> ' + string.format(this._values[k], options); + str += '\n (' + string.format(i, options) + ', ' + string.format(j, options) + ') ==> ' + (this._values ? string.format(this._values[k], options) : 'X'); } } return str; @@ -809,7 +863,8 @@ function factory (type, config, load) { values: this._values, index: this._index, ptr: this._ptr, - size: this._size + size: this._size, + datatype: this._datatype }; }; @@ -824,7 +879,7 @@ function factory (type, config, load) { // validate k if any if (k) { // convert BigNumber to a number - if (k instanceof BigNumber) + if (k instanceof type.BigNumber) k = k.toNumber(); // is must be an integer if (!isNumber(k) || !isInteger(k)) { @@ -912,7 +967,7 @@ function factory (type, config, load) { // map size & validate size = size.map(function (s) { // check it is a big number - if (s instanceof BigNumber) { + if (s instanceof type.BigNumber) { // convert it s = s.toNumber(); } @@ -926,7 +981,7 @@ function factory (type, config, load) { // validate k if any if (k) { // convert BigNumber to a number - if (k instanceof BigNumber) + if (k instanceof type.BigNumber) k = k.toNumber(); // is must be an integer if (!isNumber(k) || !isInteger(k)) { @@ -964,7 +1019,7 @@ function factory (type, config, load) { return value[i]; }; } - else if (value instanceof Matrix) { + else if (value instanceof type.Matrix) { // matrix size var ms = value.size(); // validate matrix @@ -1002,7 +1057,7 @@ function factory (type, config, load) { // get value @ i var v = _value(i); // check for zero - if (!equal(v, 0)) { + if (!equalScalar(v, 0)) { // column index.push(i + kSub); // add value @@ -1090,42 +1145,48 @@ function factory (type, config, load) { var ky = _getValueIndex(y, k0, k1, index); // check both rows exist in matrix if (kx < k1 && ky < k1 && index[kx] === x && index[ky] === y) { - // swap values - var v = values[kx]; - values[kx] = values[ky]; - values[ky] = v; + // swap values (check for pattern matrix) + if (values) { + var v = values[kx]; + values[kx] = values[ky]; + values[ky] = v; + } // next column continue; } // check x row exist & no y row if (kx < k1 && index[kx] === x && (ky >= k1 || index[ky] !== y)) { - // value @ x - var vx = values[kx]; + // value @ x (check for pattern matrix) + var vx = values ? values[kx] : undefined; // insert value @ y - values.splice(ky, 0, vx); index.splice(ky, 0, y); + if (values) + values.splice(ky, 0, vx); // remove value @ x (adjust array index if needed) - values.splice(ky <= kx ? kx + 1 : kx, 1); index.splice(ky <= kx ? kx + 1 : kx, 1); + if (values) + values.splice(ky <= kx ? kx + 1 : kx, 1); // next column continue; } // check y row exist & no x row if (ky < k1 && index[ky] === y && (kx >= k1 || index[kx] !== x)) { - // value @ y - var vy = values[ky]; + // value @ y (check for pattern matrix) + var vy = values ? values[ky] : undefined; // insert value @ x - values.splice(kx, 0, vy); index.splice(kx, 0, x); + if (values) + values.splice(kx, 0, vy); // remove value @ y (adjust array index if needed) - values.splice(kx <= ky ? ky + 1 : ky, 1); index.splice(kx <= ky ? ky + 1 : ky, 1); + if (values) + values.splice(kx <= ky ? ky + 1 : ky, 1); } } }; // register this type in the base class Matrix - Matrix._storage.sparse = SparseMatrix; + type.Matrix._storage.sparse = SparseMatrix; return SparseMatrix; }