This commit is contained in:
rjbaucells 2015-05-04 11:26:37 -04:00
parent b2067268be
commit 472299d341

View File

@ -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;
}