matrix.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  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. /**
  15. * @fileoverview Class for representing matrices and static helper functions.
  16. */
  17. goog.provide('goog.math.Matrix');
  18. goog.require('goog.array');
  19. goog.require('goog.asserts');
  20. goog.require('goog.math');
  21. goog.require('goog.math.Size');
  22. goog.require('goog.string');
  23. /**
  24. * Class for representing and manipulating matrices.
  25. *
  26. * The entry that lies in the i-th row and the j-th column of a matrix is
  27. * typically referred to as the i,j entry of the matrix.
  28. *
  29. * The m-by-n matrix A would have its entries referred to as:
  30. * [ a0,0 a0,1 a0,2 ... a0,j ... a0,n ]
  31. * [ a1,0 a1,1 a1,2 ... a1,j ... a1,n ]
  32. * [ a2,0 a2,1 a2,2 ... a2,j ... a2,n ]
  33. * [ . . . . . ]
  34. * [ . . . . . ]
  35. * [ . . . . . ]
  36. * [ ai,0 ai,1 ai,2 ... ai,j ... ai,n ]
  37. * [ . . . . . ]
  38. * [ . . . . . ]
  39. * [ . . . . . ]
  40. * [ am,0 am,1 am,2 ... am,j ... am,n ]
  41. *
  42. * @param {!goog.math.Matrix|!Array<!Array<number>>|!goog.math.Size|number} m
  43. * A matrix to copy, a 2D-array to take as a template, a size object for
  44. * dimensions, or the number of rows.
  45. * @param {number=} opt_n Number of columns of the matrix (only applicable if
  46. * the first argument is also numeric).
  47. * @struct
  48. * @constructor
  49. * @final
  50. */
  51. goog.math.Matrix = function(m, opt_n) {
  52. if (m instanceof goog.math.Matrix) {
  53. this.array_ = m.toArray();
  54. } else if (
  55. goog.isArrayLike(m) &&
  56. goog.math.Matrix.isValidArray(
  57. /** @type {!Array<!Array<number>>} */ (m))) {
  58. this.array_ = goog.array.clone(/** @type {!Array<!Array<number>>} */ (m));
  59. } else if (m instanceof goog.math.Size) {
  60. this.array_ = goog.math.Matrix.createZeroPaddedArray_(m.height, m.width);
  61. } else if (goog.isNumber(m) && goog.isNumber(opt_n) && m > 0 && opt_n > 0) {
  62. this.array_ = goog.math.Matrix.createZeroPaddedArray_(
  63. /** @type {number} */ (m), opt_n);
  64. } else {
  65. throw Error('Invalid argument(s) for Matrix contructor');
  66. }
  67. this.size_ = new goog.math.Size(this.array_[0].length, this.array_.length);
  68. };
  69. /**
  70. * Creates a square identity matrix. i.e. for n = 3:
  71. * <pre>
  72. * [ 1 0 0 ]
  73. * [ 0 1 0 ]
  74. * [ 0 0 1 ]
  75. * </pre>
  76. * @param {number} n The size of the square identity matrix.
  77. * @return {!goog.math.Matrix} Identity matrix of width and height {@code n}.
  78. */
  79. goog.math.Matrix.createIdentityMatrix = function(n) {
  80. var rv = [];
  81. for (var i = 0; i < n; i++) {
  82. rv[i] = [];
  83. for (var j = 0; j < n; j++) {
  84. rv[i][j] = i == j ? 1 : 0;
  85. }
  86. }
  87. return new goog.math.Matrix(rv);
  88. };
  89. /**
  90. * Calls a function for each cell in a matrix.
  91. * @param {goog.math.Matrix} matrix The matrix to iterate over.
  92. * @param {function(this:T, number, number, number, !goog.math.Matrix)} fn
  93. * The function to call for every element. This function
  94. * takes 4 arguments (value, i, j, and the matrix)
  95. * and the return value is irrelevant.
  96. * @param {T=} opt_obj The object to be used as the value of 'this'
  97. * within {@code fn}.
  98. * @template T
  99. */
  100. goog.math.Matrix.forEach = function(matrix, fn, opt_obj) {
  101. for (var i = 0; i < matrix.getSize().height; i++) {
  102. for (var j = 0; j < matrix.getSize().width; j++) {
  103. fn.call(opt_obj, matrix.array_[i][j], i, j, matrix);
  104. }
  105. }
  106. };
  107. /**
  108. * Tests whether an array is a valid matrix. A valid array is an array of
  109. * arrays where all arrays are of the same length and all elements are numbers.
  110. * @param {!Array<!Array<number>>} arr An array to test.
  111. * @return {boolean} Whether the array is a valid matrix.
  112. */
  113. goog.math.Matrix.isValidArray = function(arr) {
  114. var len = 0;
  115. for (var i = 0; i < arr.length; i++) {
  116. if (!goog.isArrayLike(arr[i]) || len > 0 && arr[i].length != len) {
  117. return false;
  118. }
  119. for (var j = 0; j < arr[i].length; j++) {
  120. if (!goog.isNumber(arr[i][j])) {
  121. return false;
  122. }
  123. }
  124. if (len == 0) {
  125. len = arr[i].length;
  126. }
  127. }
  128. return len != 0;
  129. };
  130. /**
  131. * Calls a function for every cell in a matrix and inserts the result into a
  132. * new matrix of equal dimensions.
  133. * @param {!goog.math.Matrix} matrix The matrix to iterate over.
  134. * @param {function(this:T, number, number, number, !goog.math.Matrix): number}
  135. * fn The function to call for every element. This function
  136. * takes 4 arguments (value, i, j and the matrix)
  137. * and should return a number, which will be inserted into a new matrix.
  138. * @param {T=} opt_obj The object to be used as the value of 'this'
  139. * within {@code fn}.
  140. * @return {!goog.math.Matrix} A new matrix with the results from {@code fn}.
  141. * @template T
  142. */
  143. goog.math.Matrix.map = function(matrix, fn, opt_obj) {
  144. var m = new goog.math.Matrix(matrix.getSize());
  145. goog.math.Matrix.forEach(matrix, function(value, i, j) {
  146. m.array_[i][j] = fn.call(opt_obj, value, i, j, matrix);
  147. });
  148. return m;
  149. };
  150. /**
  151. * Creates a new zero padded matix.
  152. * @param {number} m Height of matrix.
  153. * @param {number} n Width of matrix.
  154. * @return {!Array<!Array<number>>} The new zero padded matrix.
  155. * @private
  156. */
  157. goog.math.Matrix.createZeroPaddedArray_ = function(m, n) {
  158. var rv = [];
  159. for (var i = 0; i < m; i++) {
  160. rv[i] = [];
  161. for (var j = 0; j < n; j++) {
  162. rv[i][j] = 0;
  163. }
  164. }
  165. return rv;
  166. };
  167. /**
  168. * Internal array representing the matrix.
  169. * @type {!Array<!Array<number>>}
  170. * @private
  171. */
  172. goog.math.Matrix.prototype.array_;
  173. /**
  174. * After construction the Matrix's size is constant and stored in this object.
  175. * @type {!goog.math.Size}
  176. * @private
  177. */
  178. goog.math.Matrix.prototype.size_;
  179. /**
  180. * Returns a new matrix that is the sum of this and the provided matrix.
  181. * @param {goog.math.Matrix} m The matrix to add to this one.
  182. * @return {!goog.math.Matrix} Resultant sum.
  183. */
  184. goog.math.Matrix.prototype.add = function(m) {
  185. if (!goog.math.Size.equals(this.size_, m.getSize())) {
  186. throw Error('Matrix summation is only supported on arrays of equal size');
  187. }
  188. return goog.math.Matrix.map(
  189. this, function(val, i, j) { return val + m.array_[i][j]; });
  190. };
  191. /**
  192. * Appends the given matrix to the right side of this matrix.
  193. * @param {goog.math.Matrix} m The matrix to augment this matrix with.
  194. * @return {!goog.math.Matrix} A new matrix with additional columns on the
  195. * right.
  196. */
  197. goog.math.Matrix.prototype.appendColumns = function(m) {
  198. if (this.size_.height != m.getSize().height) {
  199. throw Error(
  200. 'The given matrix has height ' + m.size_.height + ', but ' +
  201. ' needs to have height ' + this.size_.height + '.');
  202. }
  203. var result =
  204. new goog.math.Matrix(this.size_.height, this.size_.width + m.size_.width);
  205. goog.math.Matrix.forEach(
  206. this, function(value, i, j) { result.array_[i][j] = value; });
  207. goog.math.Matrix.forEach(m, function(value, i, j) {
  208. result.array_[i][this.size_.width + j] = value;
  209. }, this);
  210. return result;
  211. };
  212. /**
  213. * Appends the given matrix to the bottom of this matrix.
  214. * @param {goog.math.Matrix} m The matrix to augment this matrix with.
  215. * @return {!goog.math.Matrix} A new matrix with added columns on the bottom.
  216. */
  217. goog.math.Matrix.prototype.appendRows = function(m) {
  218. if (this.size_.width != m.getSize().width) {
  219. throw Error(
  220. 'The given matrix has width ' + m.size_.width + ', but ' +
  221. ' needs to have width ' + this.size_.width + '.');
  222. }
  223. var result = new goog.math.Matrix(
  224. this.size_.height + m.size_.height, this.size_.width);
  225. goog.math.Matrix.forEach(
  226. this, function(value, i, j) { result.array_[i][j] = value; });
  227. goog.math.Matrix.forEach(m, function(value, i, j) {
  228. result.array_[this.size_.height + i][j] = value;
  229. }, this);
  230. return result;
  231. };
  232. /**
  233. * Returns whether the given matrix equals this matrix.
  234. * @param {goog.math.Matrix} m The matrix to compare to this one.
  235. * @param {number=} opt_tolerance The tolerance when comparing array entries.
  236. * @return {boolean} Whether the given matrix equals this matrix.
  237. */
  238. goog.math.Matrix.prototype.equals = function(m, opt_tolerance) {
  239. if (this.size_.width != m.size_.width) {
  240. return false;
  241. }
  242. if (this.size_.height != m.size_.height) {
  243. return false;
  244. }
  245. var tolerance = opt_tolerance || 0;
  246. for (var i = 0; i < this.size_.height; i++) {
  247. for (var j = 0; j < this.size_.width; j++) {
  248. if (!goog.math.nearlyEquals(
  249. this.array_[i][j], m.array_[i][j], tolerance)) {
  250. return false;
  251. }
  252. }
  253. }
  254. return true;
  255. };
  256. /**
  257. * Returns the determinant of this matrix. The determinant of a matrix A is
  258. * often denoted as |A| and can only be applied to a square matrix.
  259. * @return {number} The determinant of this matrix.
  260. */
  261. goog.math.Matrix.prototype.getDeterminant = function() {
  262. if (!this.isSquare()) {
  263. throw Error('A determinant can only be take on a square matrix');
  264. }
  265. return this.getDeterminant_();
  266. };
  267. /**
  268. * Returns the inverse of this matrix if it exists or null if the matrix is
  269. * not invertible.
  270. * @return {goog.math.Matrix} A new matrix which is the inverse of this matrix.
  271. */
  272. goog.math.Matrix.prototype.getInverse = function() {
  273. if (!this.isSquare()) {
  274. throw Error('An inverse can only be taken on a square matrix.');
  275. }
  276. if (this.getSize().width == 1) {
  277. var a = this.getValueAt(0, 0);
  278. return a == 0 ? null : new goog.math.Matrix([[1 / Number(a)]]);
  279. }
  280. var identity = goog.math.Matrix.createIdentityMatrix(this.size_.height);
  281. var mi = this.appendColumns(identity).getReducedRowEchelonForm();
  282. var i = mi.getSubmatrixByCoordinates_(
  283. 0, 0, identity.size_.width - 1, identity.size_.height - 1);
  284. if (!i.equals(identity)) {
  285. return null; // This matrix was not invertible
  286. }
  287. return mi.getSubmatrixByCoordinates_(0, identity.size_.width);
  288. };
  289. /**
  290. * Transforms this matrix into reduced row echelon form.
  291. * @return {!goog.math.Matrix} A new matrix reduced row echelon form.
  292. */
  293. goog.math.Matrix.prototype.getReducedRowEchelonForm = function() {
  294. var result = new goog.math.Matrix(this);
  295. var col = 0;
  296. // Each iteration puts one row in reduced row echelon form
  297. for (var row = 0; row < result.size_.height; row++) {
  298. if (col >= result.size_.width) {
  299. return result;
  300. }
  301. // Scan each column starting from this row on down for a non-zero value
  302. var i = row;
  303. while (result.array_[i][col] == 0) {
  304. i++;
  305. if (i == result.size_.height) {
  306. i = row;
  307. col++;
  308. if (col == result.size_.width) {
  309. return result;
  310. }
  311. }
  312. }
  313. // Make the row we found the current row with a leading 1
  314. this.swapRows_(i, row);
  315. var divisor = result.array_[row][col];
  316. for (var j = col; j < result.size_.width; j++) {
  317. result.array_[row][j] = result.array_[row][j] / divisor;
  318. }
  319. // Subtract a multiple of this row from each other row
  320. // so that all the other entries in this column are 0
  321. for (i = 0; i < result.size_.height; i++) {
  322. if (i != row) {
  323. var multiple = result.array_[i][col];
  324. for (var j = col; j < result.size_.width; j++) {
  325. result.array_[i][j] -= multiple * result.array_[row][j];
  326. }
  327. }
  328. }
  329. // Move on to the next column
  330. col++;
  331. }
  332. return result;
  333. };
  334. /**
  335. * @return {!goog.math.Size} The dimensions of the matrix.
  336. */
  337. goog.math.Matrix.prototype.getSize = function() {
  338. return this.size_;
  339. };
  340. /**
  341. * Return the transpose of this matrix. For an m-by-n matrix, the transpose
  342. * is the n-by-m matrix which results from turning rows into columns and columns
  343. * into rows
  344. * @return {!goog.math.Matrix} A new matrix A^T.
  345. */
  346. goog.math.Matrix.prototype.getTranspose = function() {
  347. var m = new goog.math.Matrix(this.size_.width, this.size_.height);
  348. goog.math.Matrix.forEach(
  349. this, function(value, i, j) { m.array_[j][i] = value; });
  350. return m;
  351. };
  352. /**
  353. * Retrieves the value of a particular coordinate in the matrix or null if the
  354. * requested coordinates are out of range.
  355. * @param {number} i The i index of the coordinate.
  356. * @param {number} j The j index of the coordinate.
  357. * @return {?number} The value at the specified coordinate.
  358. */
  359. goog.math.Matrix.prototype.getValueAt = function(i, j) {
  360. if (!this.isInBounds_(i, j)) {
  361. return null;
  362. }
  363. return this.array_[i][j];
  364. };
  365. /**
  366. * @return {boolean} Whether the horizontal and vertical dimensions of this
  367. * matrix are the same.
  368. */
  369. goog.math.Matrix.prototype.isSquare = function() {
  370. return this.size_.width == this.size_.height;
  371. };
  372. /**
  373. * Sets the value at a particular coordinate (if the coordinate is within the
  374. * bounds of the matrix).
  375. * @param {number} i The i index of the coordinate.
  376. * @param {number} j The j index of the coordinate.
  377. * @param {number} value The new value for the coordinate.
  378. */
  379. goog.math.Matrix.prototype.setValueAt = function(i, j, value) {
  380. if (!this.isInBounds_(i, j)) {
  381. throw Error(
  382. 'Index out of bounds when setting matrix value, (' + i + ',' + j +
  383. ') in size (' + this.size_.height + ',' + this.size_.width + ')');
  384. }
  385. this.array_[i][j] = value;
  386. };
  387. /**
  388. * Performs matrix or scalar multiplication on a matrix and returns the
  389. * resultant matrix.
  390. *
  391. * Matrix multiplication is defined between two matrices only if the number of
  392. * columns of the first matrix is the same as the number of rows of the second
  393. * matrix. If A is an m-by-n matrix and B is an n-by-p matrix, then their
  394. * product AB is an m-by-p matrix
  395. *
  396. * Scalar multiplication returns a matrix of the same size as the original,
  397. * each value multiplied by the given value.
  398. *
  399. * @param {goog.math.Matrix|number} m Matrix/number to multiply the matrix by.
  400. * @return {!goog.math.Matrix} Resultant product.
  401. */
  402. goog.math.Matrix.prototype.multiply = function(m) {
  403. if (m instanceof goog.math.Matrix) {
  404. if (this.size_.width != m.getSize().height) {
  405. throw Error(
  406. 'Invalid matrices for multiplication. Second matrix ' +
  407. 'should have the same number of rows as the first has columns.');
  408. }
  409. return this.matrixMultiply_(/** @type {!goog.math.Matrix} */ (m));
  410. } else if (goog.isNumber(m)) {
  411. return this.scalarMultiply_(/** @type {number} */ (m));
  412. } else {
  413. throw Error(
  414. 'A matrix can only be multiplied by' +
  415. ' a number or another matrix.');
  416. }
  417. };
  418. /**
  419. * Returns a new matrix that is the difference of this and the provided matrix.
  420. * @param {goog.math.Matrix} m The matrix to subtract from this one.
  421. * @return {!goog.math.Matrix} Resultant difference.
  422. */
  423. goog.math.Matrix.prototype.subtract = function(m) {
  424. if (!goog.math.Size.equals(this.size_, m.getSize())) {
  425. throw Error(
  426. 'Matrix subtraction is only supported on arrays of equal size.');
  427. }
  428. return goog.math.Matrix.map(
  429. this, function(val, i, j) { return val - m.array_[i][j]; });
  430. };
  431. /**
  432. * @return {!Array<!Array<number>>} A 2D internal array representing this
  433. * matrix. Not a clone.
  434. */
  435. goog.math.Matrix.prototype.toArray = function() {
  436. return this.array_;
  437. };
  438. if (goog.DEBUG) {
  439. /**
  440. * Returns a string representation of the matrix. e.g.
  441. * <pre>
  442. * [ 12 5 9 1 ]
  443. * [ 4 16 0 17 ]
  444. * [ 12 5 1 23 ]
  445. * </pre>
  446. *
  447. * @return {string} A string representation of this matrix.
  448. * @override
  449. */
  450. goog.math.Matrix.prototype.toString = function() {
  451. // Calculate correct padding for optimum display of matrix
  452. var maxLen = 0;
  453. goog.math.Matrix.forEach(this, function(val) {
  454. var len = String(val).length;
  455. if (len > maxLen) {
  456. maxLen = len;
  457. }
  458. });
  459. // Build the string
  460. var sb = [];
  461. goog.array.forEach(this.array_, function(row, x) {
  462. sb.push('[ ');
  463. goog.array.forEach(row, function(val, y) {
  464. var strval = String(val);
  465. sb.push(goog.string.repeat(' ', maxLen - strval.length) + strval + ' ');
  466. });
  467. sb.push(']\n');
  468. });
  469. return sb.join('');
  470. };
  471. }
  472. /**
  473. * Returns the signed minor.
  474. * @param {number} i The row index.
  475. * @param {number} j The column index.
  476. * @return {number} The cofactor C[i,j] of this matrix.
  477. * @private
  478. */
  479. goog.math.Matrix.prototype.getCofactor_ = function(i, j) {
  480. return (i + j % 2 == 0 ? 1 : -1) * this.getMinor_(i, j);
  481. };
  482. /**
  483. * Returns the determinant of this matrix. The determinant of a matrix A is
  484. * often denoted as |A| and can only be applied to a square matrix. Same as
  485. * public method but without validation. Implemented using Laplace's formula.
  486. * @return {number} The determinant of this matrix.
  487. * @private
  488. */
  489. goog.math.Matrix.prototype.getDeterminant_ = function() {
  490. if (this.getSize().area() == 1) {
  491. return this.array_[0][0];
  492. }
  493. // We might want to use matrix decomposition to improve running time
  494. // For now we'll do a Laplace expansion along the first row
  495. var determinant = 0;
  496. for (var j = 0; j < this.size_.width; j++) {
  497. determinant += (this.array_[0][j] * this.getCofactor_(0, j));
  498. }
  499. return determinant;
  500. };
  501. /**
  502. * Returns the determinant of the submatrix resulting from the deletion of row i
  503. * and column j.
  504. * @param {number} i The row to delete.
  505. * @param {number} j The column to delete.
  506. * @return {number} The first minor M[i,j] of this matrix.
  507. * @private
  508. */
  509. goog.math.Matrix.prototype.getMinor_ = function(i, j) {
  510. return this.getSubmatrixByDeletion_(i, j).getDeterminant_();
  511. };
  512. /**
  513. * Returns a submatrix contained within this matrix.
  514. * @param {number} i1 The upper row index.
  515. * @param {number} j1 The left column index.
  516. * @param {number=} opt_i2 The lower row index.
  517. * @param {number=} opt_j2 The right column index.
  518. * @return {!goog.math.Matrix} The submatrix contained within the given bounds.
  519. * @private
  520. */
  521. goog.math.Matrix.prototype.getSubmatrixByCoordinates_ = function(
  522. i1, j1, opt_i2, opt_j2) {
  523. var i2 = opt_i2 ? opt_i2 : this.size_.height - 1;
  524. var j2 = opt_j2 ? opt_j2 : this.size_.width - 1;
  525. var result = new goog.math.Matrix(i2 - i1 + 1, j2 - j1 + 1);
  526. goog.math.Matrix.forEach(result, function(value, i, j) {
  527. result.array_[i][j] = this.array_[i1 + i][j1 + j];
  528. }, this);
  529. return result;
  530. };
  531. /**
  532. * Returns a new matrix equal to this one, but with row i and column j deleted.
  533. * @param {number} i The row index of the coordinate.
  534. * @param {number} j The column index of the coordinate.
  535. * @return {!goog.math.Matrix} The value at the specified coordinate.
  536. * @private
  537. */
  538. goog.math.Matrix.prototype.getSubmatrixByDeletion_ = function(i, j) {
  539. var m = new goog.math.Matrix(this.size_.width - 1, this.size_.height - 1);
  540. goog.math.Matrix.forEach(m, function(value, x, y) {
  541. m.setValueAt(x, y, this.array_[x >= i ? x + 1 : x][y >= j ? y + 1 : y]);
  542. }, this);
  543. return m;
  544. };
  545. /**
  546. * Returns whether the given coordinates are contained within the bounds of the
  547. * matrix.
  548. * @param {number} i The i index of the coordinate.
  549. * @param {number} j The j index of the coordinate.
  550. * @return {boolean} The value at the specified coordinate.
  551. * @private
  552. */
  553. goog.math.Matrix.prototype.isInBounds_ = function(i, j) {
  554. return i >= 0 && i < this.size_.height && j >= 0 && j < this.size_.width;
  555. };
  556. /**
  557. * Matrix multiplication is defined between two matrices only if the number of
  558. * columns of the first matrix is the same as the number of rows of the second
  559. * matrix. If A is an m-by-n matrix and B is an n-by-p matrix, then their
  560. * product AB is an m-by-p matrix
  561. *
  562. * @param {goog.math.Matrix} m Matrix to multiply the matrix by.
  563. * @return {!goog.math.Matrix} Resultant product.
  564. * @private
  565. */
  566. goog.math.Matrix.prototype.matrixMultiply_ = function(m) {
  567. var resultMatrix = new goog.math.Matrix(this.size_.height, m.getSize().width);
  568. goog.math.Matrix.forEach(resultMatrix, function(val, x, y) {
  569. var newVal = 0;
  570. for (var i = 0; i < this.size_.width; i++) {
  571. newVal += goog.asserts.assertNumber(this.getValueAt(x, i)) *
  572. goog.asserts.assertNumber(m.getValueAt(i, y));
  573. }
  574. resultMatrix.setValueAt(x, y, newVal);
  575. }, this);
  576. return resultMatrix;
  577. };
  578. /**
  579. * Scalar multiplication returns a matrix of the same size as the original,
  580. * each value multiplied by the given value.
  581. *
  582. * @param {number} m number to multiply the matrix by.
  583. * @return {!goog.math.Matrix} Resultant product.
  584. * @private
  585. */
  586. goog.math.Matrix.prototype.scalarMultiply_ = function(m) {
  587. return goog.math.Matrix.map(this, function(val, x, y) { return val * m; });
  588. };
  589. /**
  590. * Swaps two rows.
  591. * @param {number} i1 The index of the first row to swap.
  592. * @param {number} i2 The index of the second row to swap.
  593. * @private
  594. */
  595. goog.math.Matrix.prototype.swapRows_ = function(i1, i2) {
  596. var tmp = this.array_[i1];
  597. this.array_[i1] = this.array_[i2];
  598. this.array_[i2] = tmp;
  599. };