From f48c07bdd88ca52abc0d5b3b7665dd4e340b3237 Mon Sep 17 00:00:00 2001 From: josdejong Date: Wed, 25 Sep 2013 21:59:35 +0200 Subject: [PATCH] Getting or setting a subset of a matrix will automatically squeezed/unsqueezed the submatrix --- HISTORY.md | 4 ++++ docs/matrices.md | 4 ++-- examples/expressions.js | 4 ++-- lib/function/expression/parse.js | 12 ++++++------ lib/type/Matrix.js | 14 ++++++++++++-- test/function/expression/parse.test.js | 20 ++++++++++---------- test/function/matrix/subset.test.js | 2 ++ test/type/matrix.test.js | 12 +++++++----- 8 files changed, 45 insertions(+), 27 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index ff31fd9ad..e6cf87849 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -4,6 +4,10 @@ https://github.com/josdejong/mathjs ## not yet released, version 0.14.0 +*WARNING: version 0.14 is incompatible with previous versions.* + +- Getting a subset of a matrix will automatically squeeze the resulting subset, + setting a subset of a matrix will automatically unsqueeze the given subset. - Removed concatenation of nested arrays in the expression parser. You can now input nested arrays like in JavaScript. Matrices can be concatenated using the function `concat`. diff --git a/docs/matrices.md b/docs/matrices.md index f339163c9..b1382e41d 100644 --- a/docs/matrices.md +++ b/docs/matrices.md @@ -41,8 +41,8 @@ parser = math.parser(); parser.eval('a = [1, 2; 3, 4]'); // Matrix, [[1, 2], [3, 4]] parser.eval('b = zeros(2, 2)'); // Matrix, [[0, 0], [0, 0]] -parser.eval('b(1, 1:2) = [[5, 6]]'); // Matrix, [[5, 6], [0, 0]] -parser.eval('b(2, :) = [[7, 8]]'); // Matrix, [[5, 6], [7, 8]] +parser.eval('b(1, 1:2) = [5, 6]'); // Matrix, [[5, 6], [0, 0]] +parser.eval('b(2, :) = [7, 8]'); // Matrix, [[5, 6], [7, 8]] parser.eval('c = a * b'); // Matrix, [[19, 22], [43, 50]] parser.eval('d = c(2, 1)'); // 43 parser.eval('e = c(2, 1:end)'); // Matrix, [[43, 50]] diff --git a/examples/expressions.js b/examples/expressions.js index 49f34c9e5..b693bb5d9 100644 --- a/examples/expressions.js +++ b/examples/expressions.js @@ -135,8 +135,8 @@ print(parser.eval('f(2, 3)')); // 8 console.log('\nmanipulate matrices'); print(parser.eval('k = [1, 2; 3, 4]')); // [[1, 2], [3, 4]] print(parser.eval('l = zeros(2, 2)')); // [[0, 0], [0, 0]] -print(parser.eval('l(1, 1:2) = [[5, 6]]')); // [[5, 6], [0, 0]] -print(parser.eval('l(2, :) = [[7, 8]]')); // [[5, 6], [7, 8]] +print(parser.eval('l(1, 1:2) = [5, 6]')); // [[5, 6], [0, 0]] +print(parser.eval('l(2, :) = [7, 8]')); // [[5, 6], [7, 8]] print(parser.eval('m = k * l')); // [[19, 22], [43, 50]] print(parser.eval('n = m(2, 1)')); // 43 print(parser.eval('n = m(:, 1)')); // [[19], [43]] diff --git a/lib/function/expression/parse.js b/lib/function/expression/parse.js index f59a7aa05..1407090f7 100644 --- a/lib/function/expression/parse.js +++ b/lib/function/expression/parse.js @@ -16,7 +16,7 @@ module.exports = function (math) { BlockNode = require('../../expression/node/BlockNode.js'), ConstantNode = require('../../expression/node/ConstantNode.js'), FunctionNode = require('../../expression/node/FunctionNode.js'), - MatrixNode = require('../../expression/node/MatrixNode.js'), + ArrayNode = require('../../expression/node/ArrayNode.js'), OperatorNode = require('../../expression/node/OperatorNode.js'), ParamsNode = require('../../expression/node/ParamsNode.js'), RangeNode = require('../../expression/node/RangeNode.js'), @@ -1042,7 +1042,7 @@ module.exports = function (math) { /** * parse the matrix * @param {Scope} scope - * @return {Node} A MatrixNode + * @return {Node} node * @private */ function parseMatrix (scope) { @@ -1098,7 +1098,7 @@ module.exports = function (math) { } } - array = new MatrixNode(params); + array = new ArrayNode(params); } else { // 1 dimensional vector @@ -1113,7 +1113,7 @@ module.exports = function (math) { else { // this is an empty matrix "[ ]" getToken(); - array = new MatrixNode([]); + array = new ArrayNode([]); } // parse parameters @@ -1128,7 +1128,7 @@ module.exports = function (math) { /** * Parse a single comma-separated row from a matrix, like 'a, b, c' * @param {Scope} scope - * @return {MatrixNode} node + * @return {ArrayNode} node */ function parseRow (scope) { var params = [parseAssignment(scope)]; @@ -1152,7 +1152,7 @@ module.exports = function (math) { } } - return new MatrixNode(params); + return new ArrayNode(params); } /** diff --git a/lib/type/Matrix.js b/lib/type/Matrix.js index a07884695..a4a93fcc6 100644 --- a/lib/type/Matrix.js +++ b/lib/type/Matrix.js @@ -299,10 +299,14 @@ function _set (matrix, index, submatrix) { submatrix = submatrix.valueOf(); } + // calculate the size of the submatrix + var subsize = array.size(submatrix); + if (isScalar) { // set a scalar - // check whether submatrix is no matrix/array - if (array.size(submatrix.valueOf()).length != 0) { + + // check whether submatrix is a scalar + if (subsize.length != 0) { throw new TypeError('Scalar value expected'); } @@ -314,6 +318,12 @@ function _set (matrix, index, submatrix) { } else { // set a submatrix + + // unsqueeze the submatrix when needed + for (var i = 0, ii = size.length - subsize.length; i < ii; i++) { + submatrix = [submatrix]; + } + var newSize = matrix._size.concat(); _setSubmatrix (matrix._data, newSize, index, 0, submatrix); if (!object.deepEqual(matrix._size, newSize)) { diff --git a/test/function/expression/parse.test.js b/test/function/expression/parse.test.js index 98d4b1aff..3be80909b 100644 --- a/test/function/expression/parse.test.js +++ b/test/function/expression/parse.test.js @@ -172,12 +172,12 @@ describe('parse', function() { [7,8,9] ]) }; - assert.deepEqual(parseAndEval('a(2, :)', scope), new Matrix([[4,5,6]])); - assert.deepEqual(parseAndEval('a(2, :2)', scope), new Matrix([[4,5]])); - assert.deepEqual(parseAndEval('a(2, :end-1)', scope), new Matrix([[4,5]])); - assert.deepEqual(parseAndEval('a(2, 2:)', scope), new Matrix([[5,6]])); - assert.deepEqual(parseAndEval('a(2, 2:3)', scope), new Matrix([[5,6]])); - assert.deepEqual(parseAndEval('a(2, 1:2:3)', scope), new Matrix([[4,6]])); + assert.deepEqual(parseAndEval('a(2, :)', scope), new Matrix([4,5,6])); + assert.deepEqual(parseAndEval('a(2, :2)', scope), new Matrix([4,5])); + assert.deepEqual(parseAndEval('a(2, :end-1)', scope), new Matrix([4,5])); + assert.deepEqual(parseAndEval('a(2, 2:)', scope), new Matrix([5,6])); + assert.deepEqual(parseAndEval('a(2, 2:3)', scope), new Matrix([5,6])); + assert.deepEqual(parseAndEval('a(2, 1:2:3)', scope), new Matrix([4,6])); assert.deepEqual(parseAndEval('a(:, 2)', scope), new Matrix([[2],[5],[8]])); assert.deepEqual(parseAndEval('a(:2, 2)', scope), new Matrix([[2],[5]])); assert.deepEqual(parseAndEval('a(:end-1, 2)', scope), new Matrix([[2],[5]])); @@ -219,7 +219,7 @@ describe('parse', function() { assert.deepEqual(parseAndEval('a(1:3,1:2)', scope), new Matrix([[100,2],[3,10],[0,12]])); scope.b = [[1,2],[3,4]]; - assert.deepEqual(parseAndEval('b(1,:)', scope), [[1, 2]]); // TODO: matrix should be squeezed + assert.deepEqual(parseAndEval('b(1,:)', scope), [1, 2]); }); it('should get/set the matrix correctly for 3d matrices', function() { @@ -249,10 +249,10 @@ describe('parse', function() { ] ]); assert.deepEqual(parseAndEval('size(f)', scope), new Matrix([2,2,2])); - assert.deepEqual(parseAndEval('f(:,:,1)', scope), new Matrix([[[1],[2]],[[3],[4]]])); // TODO: last dimension should be squeezed - assert.deepEqual(parseAndEval('f(:,:,2)', scope), new Matrix([[[5],[6]],[[7],[8]]])); // TODO: last dimension should be squeezed + assert.deepEqual(parseAndEval('f(:,:,1)', scope), new Matrix([[[1],[2]],[[3],[4]]])); + assert.deepEqual(parseAndEval('f(:,:,2)', scope), new Matrix([[[5],[6]],[[7],[8]]])); assert.deepEqual(parseAndEval('f(:,2,:)', scope), new Matrix([[[2,6]],[[4,8]]])); - assert.deepEqual(parseAndEval('f(2,:,:)', scope), new Matrix([[[3,7],[4,8]]])); + assert.deepEqual(parseAndEval('f(2,:,:)', scope), new Matrix([[3,7],[4,8]])); parseAndEval('a=diag([1,2,3,4])', scope); assert.deepEqual(parseAndEval('a(3:end, 3:end)', scope), new Matrix([[3,0],[0,4]])); diff --git a/test/function/matrix/subset.test.js b/test/function/matrix/subset.test.js index fd5068646..8c9ccda19 100644 --- a/test/function/matrix/subset.test.js +++ b/test/function/matrix/subset.test.js @@ -49,6 +49,8 @@ describe('subset', function() { var d = [[1,2], [3,4]]; var g = matrix([[1,2], [3,4]]); + // TODO: test getting subset of an array and matrix + it('should set the right subset of an array', function() { assert.deepEqual(d, [[1,2], [3,4]]); assert.deepEqual(subset(d, index([0,2], 1), [[-2],[-4]]), [[1,-2], [3,-4]]); diff --git a/test/type/matrix.test.js b/test/type/matrix.test.js index 53dbb3a1b..27b1b65a5 100644 --- a/test/type/matrix.test.js +++ b/test/type/matrix.test.js @@ -143,8 +143,8 @@ describe('matrix', function() { assert.deepEqual(m.size(), [3,3]); assert.deepEqual(m.subset(index(1,1)), 5); assert.deepEqual(m.subset(index([0,2],[0,2])).valueOf(), [[1,2],[4,5]]); - assert.deepEqual(m.subset(index(1, [1,3])).valueOf(), [[5,6]]); - assert.deepEqual(m.subset(index(0, [1,3])).valueOf(), [[2,3]]); + assert.deepEqual(m.subset(index(1, [1,3])).valueOf(), [5,6]); + assert.deepEqual(m.subset(index(0, [1,3])).valueOf(), [2,3]); assert.deepEqual(m.subset(index([1,3], 1)).valueOf(), [[5],[8]]); assert.deepEqual(m.subset(index([1,3], 2)).valueOf(), [[6],[9]]); @@ -154,8 +154,8 @@ describe('matrix', function() { assert.deepEqual(m.subset(index([0,2],[0,2],[0,2])).valueOf(), m.valueOf()); assert.deepEqual(m.subset(index(0,0,0)), 1); assert.deepEqual(m.subset(index(1,1,1)).valueOf(), 8); - assert.deepEqual(m.subset(index(1,1,[0,2])).valueOf(), [[[7,8]]]); - assert.deepEqual(m.subset(index(1,[0,2],1)).valueOf(), [[[6],[8]]]); + assert.deepEqual(m.subset(index(1,1,[0,2])).valueOf(), [7,8]); + assert.deepEqual(m.subset(index(1,[0,2],1)).valueOf(), [[6],[8]]); }); it('should throw an error if the given subset is invalid', function() { @@ -185,7 +185,9 @@ describe('matrix', function() { m.resize([3,3]); assert.deepEqual(m.valueOf(), [[0,0,0],[0,0,0],[0,0,0]]); m.subset(index([1,3], [1,3]), [[1,2],[3,4]]); - assert.deepEqual(m.valueOf(), [[0,0,0],[0,1,2],[0,3,4]]); + assert.deepEqual(m.valueOf(), [[0,0,0],[0,1,2],[0,3,4]]); + m.subset(index(0, [0,3]), [5,6,7]); // unsqueezes the submatrix + assert.deepEqual(m.valueOf(), [[5,6,7],[0,1,2],[0,3,4]]); }); it('should resize the matrix if the replacement subset is different size than selected subset', function() {