matrix_test.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. // Copyright 2007 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. goog.provide('goog.math.MatrixTest');
  15. goog.setTestOnly('goog.math.MatrixTest');
  16. goog.require('goog.math.Matrix');
  17. goog.require('goog.testing.jsunit');
  18. function testConstuctorWithGoodArray() {
  19. var a1 = [[1, 2], [2, 3], [4, 5]];
  20. var m1 = new goog.math.Matrix(a1);
  21. assertArrayEquals('1. Internal array should be the same', m1.toArray(), a1);
  22. assertEquals(3, m1.getSize().height);
  23. assertEquals(2, m1.getSize().width);
  24. var a2 = [[-61, 45, 123], [11112, 343, 1235]];
  25. var m2 = new goog.math.Matrix(a2);
  26. assertArrayEquals('2. Internal array should be the same', m2.toArray(), a2);
  27. assertEquals(2, m2.getSize().height);
  28. assertEquals(3, m2.getSize().width);
  29. var a3 = [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3], [4, 4, 4, 4]];
  30. var m3 = new goog.math.Matrix(a3);
  31. assertArrayEquals('3. Internal array should be the same', m3.toArray(), a3);
  32. assertEquals(4, m3.getSize().height);
  33. assertEquals(4, m3.getSize().width);
  34. }
  35. function testConstructorWithBadArray() {
  36. assertThrows('1. All arrays should be of equal length', function() {
  37. new goog.math.Matrix([[1, 2, 3], [1, 2], [1]]);
  38. });
  39. assertThrows('2. All arrays should be of equal length', function() {
  40. new goog.math.Matrix([[1, 2], [1, 2], [1, 2, 3, 4]]);
  41. });
  42. assertThrows('3. Arrays should contain only numeric values', function() {
  43. new goog.math.Matrix([[1, 2], [1, 2], [1, 'a']]);
  44. });
  45. assertThrows('4. Arrays should contain only numeric values', function() {
  46. new goog.math.Matrix([[1, 2], [1, 2], [1, {a: 3}]]);
  47. });
  48. assertThrows('5. Arrays should contain only numeric values', function() {
  49. new goog.math.Matrix([[1, 2], [1, 2], [1, [1, 2, 3]]]);
  50. });
  51. }
  52. function testConstructorWithGoodNumbers() {
  53. var m1 = new goog.math.Matrix(2, 2);
  54. assertEquals('Height should be 2', 2, m1.getSize().height);
  55. assertEquals('Width should be 2', 2, m1.getSize().width);
  56. var m2 = new goog.math.Matrix(4, 2);
  57. assertEquals('Height should be 4', 4, m2.getSize().height);
  58. assertEquals('Width should be 2', 2, m2.getSize().width);
  59. var m3 = new goog.math.Matrix(4, 6);
  60. assertEquals('Height should be 4', 4, m3.getSize().height);
  61. assertEquals('Width should be 6', 6, m3.getSize().width);
  62. }
  63. function testConstructorWithBadNumbers() {
  64. assertThrows('1. Negative argument should have errored', function() {
  65. new goog.math.Matrix(-4, 6);
  66. });
  67. assertThrows('2. Negative argument should have errored', function() {
  68. new goog.math.Matrix(4, -6);
  69. });
  70. assertThrows('3. Zero argument should have errored', function() {
  71. new goog.math.Matrix(4, 0);
  72. });
  73. assertThrows('4. Zero argument should have errored', function() {
  74. new goog.math.Matrix(0, 1);
  75. });
  76. }
  77. function testConstructorWithMatrix() {
  78. var a1 = [[1, 2], [2, 3], [4, 5]];
  79. var m1 = new goog.math.Matrix(a1);
  80. var m2 = new goog.math.Matrix(m1);
  81. assertArrayEquals(
  82. 'Internal arrays should be the same', m1.toArray(), m2.toArray());
  83. assertNotEquals(
  84. 'Should be different objects', goog.getUid(m1), goog.getUid(m2));
  85. }
  86. function testCreateIdentityMatrix() {
  87. var m1 = goog.math.Matrix.createIdentityMatrix(3);
  88. assertArrayEquals([[1, 0, 0], [0, 1, 0], [0, 0, 1]], m1.toArray());
  89. var m2 = goog.math.Matrix.createIdentityMatrix(4);
  90. assertArrayEquals(
  91. [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], m2.toArray());
  92. }
  93. function testIsValidArrayWithGoodArrays() {
  94. var fn = goog.math.Matrix.isValidArray;
  95. assertTrue('2x2 array should be fine', fn([[1, 2], [3, 5]]));
  96. assertTrue('3x2 array should be fine', fn([[1, 2, 3], [3, 5, 6]]));
  97. assertTrue(
  98. '3x3 array should be fine', fn([[1, 2, 3], [3, 5, 6], [10, 10, 10]]));
  99. assertTrue('[[1]] should be fine', fn([[1]]));
  100. assertTrue('1D array should work', fn([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]));
  101. assertTrue(
  102. 'Negs and decimals should be ok',
  103. fn([[0], [-4], [-10], [1.2345], [123.53]]));
  104. assertTrue('Hex, Es and decimals are ok', fn([[0x100, 10E-2], [1.213, 213]]));
  105. }
  106. function testIsValidArrayWithBadArrays() {
  107. var fn = goog.math.Matrix.isValidArray;
  108. assertFalse('Arrays should have same size', fn([[1, 2], [3]]));
  109. assertFalse('Arrays should have same size 2', fn([[1, 2], [3, 4, 5]]));
  110. assertFalse('2D arrays are ok', fn([[1, 2], [3, 4], []]));
  111. assertFalse('Values should be numeric', fn([[1, 2], [3, 'a']]));
  112. assertFalse('Values can not be strings', fn([['bah'], ['foo']]));
  113. assertFalse('Flat array not supported', fn([1, 2, 3, 4, 5]));
  114. }
  115. function testForEach() {
  116. var m = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
  117. var count = 0, sum = 0, xs = '', ys = '';
  118. goog.math.Matrix.forEach(m, function(val, x, y) {
  119. count++;
  120. sum += val;
  121. xs += x;
  122. ys += y;
  123. });
  124. assertEquals('forEach should have visited every item', 9, count);
  125. assertEquals('forEach should have summed all values', 45, sum);
  126. assertEquals('Xs should have been visited in order', '000111222', xs);
  127. assertEquals('Ys should have been visited sequentially', '012012012', ys);
  128. }
  129. function testMap() {
  130. var m1 = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
  131. var m2 = goog.math.Matrix.map(m1, function(val, x, y) { return val + 1; });
  132. assertArrayEquals([[2, 3, 4], [5, 6, 7], [8, 9, 10]], m2.toArray());
  133. }
  134. function testSetValueAt() {
  135. var m = new goog.math.Matrix(3, 3);
  136. for (var x = 0; x < 3; x++) {
  137. for (var y = 0; y < 3; y++) {
  138. m.setValueAt(x, y, 3 * x - y);
  139. }
  140. }
  141. assertArrayEquals([[0, -1, -2], [3, 2, 1], [6, 5, 4]], m.toArray());
  142. }
  143. function testGetValueAt() {
  144. var m = new goog.math.Matrix([[0, -1, -2], [3, 2, 1], [6, 5, 4]]);
  145. for (var x = 0; x < 3; x++) {
  146. for (var y = 0; y < 3; y++) {
  147. assertEquals(
  148. 'Value at (x, y) should equal 3x - y', 3 * x - y, m.getValueAt(x, y));
  149. }
  150. }
  151. assertNull('Out of bounds value should be null', m.getValueAt(-1, 2));
  152. assertNull('Out of bounds value should be null', m.getValueAt(-1, 0));
  153. assertNull('Out of bounds value should be null', m.getValueAt(0, 4));
  154. }
  155. function testSum1() {
  156. var m1 = new goog.math.Matrix([[1, 1, 1], [2, 2, 2], [3, 3, 3]]);
  157. var m2 = new goog.math.Matrix([[3, 3, 3], [2, 2, 2], [1, 1, 1]]);
  158. assertArrayEquals(
  159. 'Sum should be all the 4s', [[4, 4, 4], [4, 4, 4], [4, 4, 4]],
  160. m1.add(m2).toArray());
  161. assertArrayEquals(
  162. 'Addition should be commutative', m1.add(m2).toArray(),
  163. m2.add(m1).toArray());
  164. }
  165. function testSum2() {
  166. var m1 = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
  167. var m2 = new goog.math.Matrix([[-1, -2, -3], [-4, -5, -6], [-7, -8, -9]]);
  168. assertArrayEquals(
  169. 'Sum should be all 0s', [[0, 0, 0], [0, 0, 0], [0, 0, 0]],
  170. m1.add(m2).toArray());
  171. assertArrayEquals(
  172. 'Addition should be commutative', m1.add(m2).toArray(),
  173. m2.add(m1).toArray());
  174. }
  175. function testSubtract1() {
  176. var m1 = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
  177. var m2 = new goog.math.Matrix([[5, 5, 5], [5, 5, 5], [5, 5, 5]]);
  178. assertArrayEquals(
  179. [[-4, -3, -2], [-1, 0, 1], [2, 3, 4]], m1.subtract(m2).toArray());
  180. assertArrayEquals(
  181. [[4, 3, 2], [1, 0, -1], [-2, -3, -4]], m2.subtract(m1).toArray());
  182. }
  183. function testSubtract2() {
  184. var m1 = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
  185. var m2 = new goog.math.Matrix([[-1, -2, -3], [-4, -5, -6], [-7, -8, -9]]);
  186. assertArrayEquals(
  187. [[2, 4, 6], [8, 10, 12], [14, 16, 18]], m1.subtract(m2).toArray());
  188. assertArrayEquals(
  189. [[-2, -4, -6], [-8, -10, -12], [-14, -16, -18]],
  190. m2.subtract(m1).toArray());
  191. }
  192. function testScalarMultiplication() {
  193. var m1 = new goog.math.Matrix([[1, 1, 1], [2, 2, 2], [3, 3, 3]]);
  194. assertArrayEquals(
  195. [[2, 2, 2], [4, 4, 4], [6, 6, 6]], m1.multiply(2).toArray());
  196. assertArrayEquals(
  197. [[3, 3, 3], [6, 6, 6], [9, 9, 9]], m1.multiply(3).toArray());
  198. assertArrayEquals(
  199. [[4, 4, 4], [8, 8, 8], [12, 12, 12]], m1.multiply(4).toArray());
  200. var m2 = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
  201. assertArrayEquals(
  202. [[2, 4, 6], [8, 10, 12], [14, 16, 18]], m2.multiply(2).toArray());
  203. }
  204. function testMatrixMultiplication() {
  205. var m1 = new goog.math.Matrix([[1, 2], [3, 4]]);
  206. var m2 = new goog.math.Matrix([[3, 4], [5, 6]]);
  207. // m1 * m2
  208. assertArrayEquals(
  209. [[1 * 3 + 2 * 5, 1 * 4 + 2 * 6], [3 * 3 + 4 * 5, 3 * 4 + 4 * 6]],
  210. m1.multiply(m2).toArray());
  211. // m2 * m1 != m1 * m2
  212. assertArrayEquals(
  213. [[3 * 1 + 4 * 3, 3 * 2 + 4 * 4], [5 * 1 + 6 * 3, 5 * 2 + 6 * 4]],
  214. m2.multiply(m1).toArray());
  215. var m3 = new goog.math.Matrix([[1, 2, 3, 4], [5, 6, 7, 8]]);
  216. var m4 =
  217. new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]);
  218. // m3 * m4
  219. assertArrayEquals(
  220. [
  221. [
  222. 1 * 1 + 2 * 4 + 3 * 7 + 4 * 10, 1 * 2 + 2 * 5 + 3 * 8 + 4 * 11,
  223. 1 * 3 + 2 * 6 + 3 * 9 + 4 * 12
  224. ],
  225. [
  226. 5 * 1 + 6 * 4 + 7 * 7 + 8 * 10, 5 * 2 + 6 * 5 + 7 * 8 + 8 * 11,
  227. 5 * 3 + 6 * 6 + 7 * 9 + 8 * 12
  228. ]
  229. ],
  230. m3.multiply(m4).toArray());
  231. assertThrows(
  232. 'Matrix dimensions should not line up.', function() { m4.multiply(m3); });
  233. }
  234. function testMatrixMultiplicationIsAssociative() {
  235. var A = new goog.math.Matrix([[1, 2], [3, 4]]);
  236. var B = new goog.math.Matrix([[3, 4], [5, 6]]);
  237. var C = new goog.math.Matrix([[2, 7], [9, 1]]);
  238. assertArrayEquals(
  239. 'A(BC) == (AB)C', A.multiply(B.multiply(C)).toArray(),
  240. A.multiply(B).multiply(C).toArray());
  241. }
  242. function testMatrixMultiplicationIsDistributive() {
  243. var A = new goog.math.Matrix([[1, 2], [3, 4]]);
  244. var B = new goog.math.Matrix([[3, 4], [5, 6]]);
  245. var C = new goog.math.Matrix([[2, 7], [9, 1]]);
  246. assertArrayEquals(
  247. 'A(B + C) = AB + AC', A.multiply(B.add(C)).toArray(),
  248. A.multiply(B).add(A.multiply(C)).toArray());
  249. assertArrayEquals(
  250. '(A + B)C = AC + BC', A.add(B).multiply(C).toArray(),
  251. A.multiply(C).add(B.multiply(C)).toArray());
  252. }
  253. function testTranspose() {
  254. var m = new goog.math.Matrix([[1, 3, 1], [0, -6, 0]]);
  255. var t = [[1, 0], [3, -6], [1, 0]];
  256. assertArrayEquals(t, m.getTranspose().toArray());
  257. }
  258. function testAppendColumns() {
  259. var m = new goog.math.Matrix([[1, 3, 2], [2, 0, 1], [5, 2, 2]]);
  260. var b = new goog.math.Matrix([[4], [3], [1]]);
  261. var result = [[1, 3, 2, 4], [2, 0, 1, 3], [5, 2, 2, 1]];
  262. assertArrayEquals(result, m.appendColumns(b).toArray());
  263. }
  264. function testAppendRows() {
  265. var m = new goog.math.Matrix([[1, 3, 2], [2, 0, 1], [5, 2, 2]]);
  266. var b = new goog.math.Matrix([[4, 3, 1]]);
  267. var result = [[1, 3, 2], [2, 0, 1], [5, 2, 2], [4, 3, 1]];
  268. assertArrayEquals(result, m.appendRows(b).toArray());
  269. }
  270. function testSubmatrixByDeletion() {
  271. var m = new goog.math.Matrix(
  272. [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16]]);
  273. var arr = [[1, 2, 3], [5, 6, 7], [13, 14, 15]];
  274. assertArrayEquals(arr, m.getSubmatrixByDeletion_(2, 3).toArray());
  275. }
  276. function testMinor() {
  277. var m = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
  278. assertEquals(-3, m.getMinor_(0, 0));
  279. }
  280. function testCofactor() {
  281. var m = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
  282. assertEquals(6, m.getCofactor_(0, 1));
  283. }
  284. function testDeterminantForOneByOneMatrix() {
  285. var m = new goog.math.Matrix([[3]]);
  286. assertEquals(3, m.getDeterminant());
  287. }
  288. function testDeterminant() {
  289. var m = new goog.math.Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
  290. assertEquals(0, m.getDeterminant());
  291. }
  292. function testGetSubmatrix() {
  293. var m = new goog.math.Matrix(
  294. [[2, -1, 0, 1, 0, 0], [-1, 2, -1, 0, 1, 0], [0, -1, 2, 0, 0, 1]]);
  295. var sub1 = [[2, -1, 0], [-1, 2, -1], [0, -1, 2]];
  296. assertArrayEquals(sub1, m.getSubmatrixByCoordinates_(0, 0, 2, 2).toArray());
  297. var sub2 = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
  298. assertArrayEquals(sub2, m.getSubmatrixByCoordinates_(0, 3).toArray());
  299. }
  300. function testGetReducedRowEchelonForm() {
  301. var m = new goog.math.Matrix(
  302. [[2, -1, 0, 1, 0, 0], [-1, 2, -1, 0, 1, 0], [0, -1, 2, 0, 0, 1]]);
  303. var expected = new goog.math.Matrix(
  304. [[1, 0, 0, .75, .5, .25], [0, 1, 0, .5, 1, .5], [0, 0, 1, .25, .5, .75]]);
  305. assertTrue(expected.equals(m.getReducedRowEchelonForm()));
  306. }
  307. function testInverse() {
  308. var m1 = new goog.math.Matrix([[2, -1, 0], [-1, 2, -1], [0, -1, 2]]);
  309. var expected1 =
  310. new goog.math.Matrix([[.75, .5, .25], [.5, 1, .5], [.25, .5, .75]]);
  311. assertTrue(expected1.equals(m1.getInverse()));
  312. var m2 = new goog.math.Matrix([[4, 8], [7, -2]]);
  313. var expected2 = new goog.math.Matrix([[.03125, .125], [.10936, -.0625]]);
  314. assertTrue(expected2.equals(m2.getInverse(), .0001));
  315. var m3 = new goog.math.Matrix([[0, 0], [0, 0]]);
  316. assertNull(m3.getInverse());
  317. var m4 = new goog.math.Matrix([[2]]);
  318. var expected4 = new goog.math.Matrix([[.5]]);
  319. assertTrue(expected4.equals(m4.getInverse(), .0001));
  320. var m5 = new goog.math.Matrix([[0]]);
  321. assertNull(m5.getInverse());
  322. }
  323. function testEquals() {
  324. var a1 = new goog.math.Matrix(
  325. [[1, 0, 0, .75, .5, .25], [0, 1, 0, .5, 1, .5], [0, 0, 1, .25, .5, .75]]);
  326. var a2 = new goog.math.Matrix(
  327. [[1, 0, 0, .75, .5, .25], [0, 1, 0, .5, 1, .5], [0, 0, 1, .25, .5, .75]]);
  328. var a3 = new goog.math.Matrix([
  329. [1, 0, 0, .749, .5, .25], [0, 1, 0, .5, 1, .5], [0, 0, 1, .25, .5, .75]
  330. ]);
  331. assertTrue(a1.equals(a2));
  332. assertTrue(a1.equals(a3, .01));
  333. assertFalse(a1.equals(a3, .001));
  334. }