1 /*! 2 * 3 * JSMatrix : JavaScript matrix and vector implementation 4 * version 0.6 (2012-04-20) 5 * 6 * Copyright (C) 2011 - 2012 Jan Stransky 7 * 8 * Czech Technical University, Faculty of Civil Engineering, 9 * Department of Structural Mechanics, 166 29 Prague, Czech Republic 10 * 11 * 12 * JSMatrix is free software: you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation, either version 3 of the License, or 15 * (at your option) any later version. 16 * 17 * JSMatrix is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program. If not, see <http://www.gnu.org/licenses/>. 24 */ 25 26 /** 27 * @fileOverview JavaScript vector and matrix implementation. 28 * This file contains JavaScript implementation of vectors and matrices, i.e 1d and 2d arrays of real (floating point) numbers and corresponding mathematical operations. The objects creation is inspired by Numerical Python (numpy) package ( v = vec([1,2,3]), m = mat([[1,2,3],[4,5,6],[7,8,9]]) ). The implementation is such that coefficient access can be done directly by a=v[1], v[1]=3., a=m[1][0], m[1][0] = 4. (it is simple and easy, but no dimension is checked), or (in safer but longer way) by get/set method, where dimensions are checked ( a=v.get(1), v.set(1,3.), a=m.get(1,0), m.set(1,0,4.) ). Access function numbering is 0-based(!) (first element has index 0). 29 * <br /><br /> 30 * When a method returns a new object (e.g. ret = m1.add(m2); ), if operation is successfull (e.g. dimensions agree), the object is returned, if operation fails (e.g. dimensions are mismatched), null value is returned (inspired by <a href="http://sylvester.jcoglan.com/">Sylvester project</a>). 31 * <br /><br /> 32 * In vector-matrix context, vectors are considered in their natural representation (row x column vectors). For example in matrix*vector multiplication the vector is considered as 1d column matrix, while in vector*matrix the vector is considered as 1d row matrix (similar approach is used in Mathematica program). In documantetion, vectors are considered as 1d column matrix (e.g. vec^T indiceates transposition of vec, i.e. 1d row matrix). 33 * <br /><br /> 34 * Method naming is usualy following (example is on matrix transposition): verb infinitive (e.g. transpose) is used for function changing receiver - receiver is changed (to its transposition for example) and nothing is returned. Verb past simple (e.g. transposed) is used for function returning new object (transposed copy of receiver for example), receiver is not changed. Names beSomethingOf (e.g. beTranspositionOf) indicates that receiver is modified to become something (transposition of given matrix for example), receiver is changed and given parameter is not. Names isSomething or canSomething (e.g. isTranspositionOf) returns usually bools (true if receiver is transposition of given matrix for example). 35 * <br /><br /> 36 * Current implementation contains: basic operations (coefficient access, +, -, *, /, +=, -=, *=, vector norms), testing operations, subvector/submatrix operations, solving linear system of equations, eigenvalues and eigenvector solving, matrix decmpositions (LU, LDU, Cholesky, QR, SVD, Schur, spectral, polar) and meny others, see 37 * <br /><br /> 38 * For more information see <a href="http://mech.fsv.cvut.cz/~stransky/software/jsmatrix/">project homepage</a> or further documentation. 39 * <br /><br /> 40 * JSMatrix is a free software distributed under <a href='http://www.gnu.org/licenses/gpl.html'>GNU GPL license</a>. 41 * <br /><br /> 42 * Example:<br /> 43 * v = Vector(); // creates empty vector object<br /> 44 * v = Vector(4); // creates (zeroed) vector with 4 elements<br /> 45 * v = Vector.create([2,4,3]); // creates vector corresponding to given array (length 3, elements=2,4,3)<br /> 46 * a = v[1]; // a = 4.<br /> 47 * a[1] = 5.; // sets 5. to a[1]<br /> 48 * a[9] = 3.; // sets 3. to a[9] (out of bounds, no error!!)<br /> 49 * a.set(9,3.); // sets 3. to a[9], checks bounds, finds that index 9 is out of bounds and does nothing<br /> 50 * <br /> 51 * References:<br /> 52 * [1] Quarteroni, A., Sacco, R. and Saleri, F. 2010. Numerical Mathematics, 2nd edition. Springer-Verlag New York<br /> 53 54 * [2] http://en.wikipedia.org/wiki/Matrix_decomposition<br /> 55 * [3] Press, W. H., Teukolsky, S. A., Vetterling, W. T. and Flannery, B. P. 2007. The Numerical Recipes: The Art of Scientific Computing, Third Edition<br /> 56 * [4] inspiration from <a href="http://www.oofem.org>OOFEM project</a><br /> 57 * [5] inspiration from <a href="http://sylvester.jcoglan.com/">Sylvester project</a><br /> 58 * @author <a href="http://mech.fsv.cvut.cz/~stransky/">Jan Stránský</a> 59 * @version 0.6 (2012-04-20) 60 */ 61 62 63 // constants for easier manipulation 64 /**constant ( = 0 )*/ var JSM_X = 0; 65 /**constant ( = 1 )*/ var JSM_Y = 1; 66 /**constant ( = 2 )*/ var JSM_Z = 2; 67 /**constant ( = 1e-8)*/ var JSM_TOL = 1e-8; 68 /**constant ( = 1e-32)*/ var JSM_TOL2 = 1e-32; 69 /**constant ( = 10000.)*/ var JSM_ROUNDFACTOR = 10000.; 70 71 72 // console messages 73 /**#@+ * @ignore*/ 74 /* 75 JSMLogger = function() { 76 this.warn = function(msg) { if (window.console) { if (window.console.warn) { window.console.warn(msg); } } } 77 this.debug = function(msg) { if (window.console) { if (window.console.debug) { window.console.debug(msg); } } } 78 this.log = function(msg) { if (window.console) { if (window.console.log) { window.console.debug(msg); } } } 79 } 80 var jsmLogger = new JSMLogger(); 81 var JSM_DEBUG = false; 82 */ 83 /**#@-*/ 84 85 86 87 88 89 90 /** Vector implementation 91 * @class Represents a vector (1D matrix) of real (floating point) numbers 92 * @param {int} [nElements=0] number of elements of newly created Vector object 93 * @property {int} [nElements=0] Number of vector elements 94 */ 95 Vector = function(nElements) { 96 this.nElements = nElements || 0; 97 for (var e=0; e<this.nElements; e++) { this[e] = 0.; } 98 } 99 100 /** Empties receiver (resizes it to size 0) 101 * @example 102 * v = vec([1,2,3,4]); 103 * v.empty(); // v = Vector([]) 104 */ 105 Vector.prototype.empty = function() { 106 this.resize(0); 107 } 108 109 /** Tests if receiver is empty (has size 0) 110 * @returns true if receiver is empty, false otherwise 111 * @example 112 * v = new Vector(3); 113 * b1 = v.isEmpty(); // b1 = false 114 * v.empty(); 115 * b2 = v.isEmpty(); // b2 = true 116 */ 117 Vector.prototype.isEmpty = function() { 118 return this.nElements==0; 119 } 120 121 /** Zeroes all elements of receiver 122 * @example 123 * v = vec([1,2,3,4]); 124 * v.zero(); // v = Vector([0,0,0,0]) 125 */ 126 Vector.prototype.zero = function() { 127 for (var e=0; e<this.nElements; e++) { this[e] = 0.; } 128 } 129 130 /** Sets all elements of receiver to 1.0 131 * @example 132 * v = vec([1,2,3,4]); 133 * v.beFullOfOnes(); // v = Vector([1,1,1,1]) 134 */ 135 Vector.prototype.beFullOfOnes = function() { 136 for (var e=0; e<this.nElements; e++) { this[e] = 1.; } 137 } 138 139 /** Alias for beFullOfOnes(), see {@link Vector#beFullOfOnes} 140 * @function 141 */ 142 Vector.prototype.one = Vector.prototype.beFullOfOnes; 143 144 /** Returns size (numer of elements) of receiver 145 * @returns {int} length (number of elements) of receiver 146 * @example 147 * v = vec([1,5,8,2]); 148 * s = v.size(); // s = 4 149 */ 150 Vector.prototype.size = function() { 151 return this.nElements; 152 } 153 154 /** Returns sum of all elements of receiver 155 * @returns {float} sum of all elements of receiver 156 * @example 157 * v = vec([1,2,3]); 158 * s = v.sum(); // s = 6 159 */ 160 Vector.prototype.sum = function() { 161 var ret = 0.; 162 for (var e=0; e<this.nElements; e++) { ret += this[e]; } 163 return ret; 164 } 165 166 /** Returns maximal value of receiver elements 167 * @returns {float} maximal value of receiver elements 168 * @example 169 * v = vec([1,-4,3,2]); 170 * m = v.max(); // m = 3 171 */ 172 Vector.prototype.max = function() { 173 ret = -1e64; 174 for (var e=0; e<this.nElements; e++) { 175 if (this[e] > ret) { ret = this[e]; } 176 } 177 return ret; 178 } 179 180 /** Returns minimal value of receiver elements 181 * @returns {float} minimal value of receiver elements 182 * @example 183 * v = vec([3,3,1,2]); 184 * m = v.min(); // m = 1 185 */ 186 Vector.prototype.min = function() { 187 ret = 1e64; 188 for (var e=0; e<this.nElements; e++) { 189 if (this[e] < ret) { ret = this[e]; } 190 } 191 return ret; 192 } 193 194 /** Returns index of first occurrence of given value 195 * @param {float} val value whose index will be returned 196 * @returns {int} index of first occurrence of val 197 * @example 198 * v = vec([5,6,1,2,6,8,6,2]); 199 * i1 = v.indexOf(2); // i1 = 3 200 * i2 = v.indexOf(6); // i2 = 1 201 * i3 = v.indexOf(8); // i3 = 5 202 */ 203 Vector.prototype.indexOf = function(val) { 204 for (var e=0; e<this.nElements; e++) { 205 if (Math.abs(this[e]-val) < JSM_TOL) { return e; } 206 } 207 return null; 208 } 209 210 /** Testing vectors equality 211 * @param {Vector} vec vector to be compared with receiver 212 * @returns {bool} true if this==vec, false otherwise 213 * @example 214 * v1 = vec([1,2,3]); 215 * v2 = vec([1,2,2.999999999]); 216 * v3 = vec([1,2,1]); 217 * t1 = v1.isEqualTo(v2); // t1 = true 218 * t2 = v1.isEqualTo(v3); // t2 = false 219 */ 220 Vector.prototype.isEqualTo = function(vec) { 221 if (!this.isSameSizeAs(vec)) { return false; } 222 for (var e=0; e<this.nElements; e++) { 223 if (Math.abs(this[e]-vec[e]) > JSM_TOL) { return false; } 224 } 225 return true; 226 } 227 228 /** Coefficient access function, returns one element of receiver ( ret = this[r] ). Vector.get(e) is almost equivalent to direct Vector[e] access method 229 * @param {int} e index of element to be returned 230 * @returns {float} value of the e-th element 231 * @example 232 * v = vec([4,7,2,4]); 233 * a = v.get(1); // a = 7 234 * a = v[1]; // a = 7 235 * a = v.get(9); // a = null 236 * a = v[9]; // a = undefined 237 */ 238 Vector.prototype.get = function(e) { 239 if (e<0 || e>=this.nElements) { /*jsmLogger.warn('Vector.get: out fo bounds');*/ return null; } 240 return this[e]; 241 } 242 243 /** Coefficient access function, sets one element of receiver ( this[r] = val ). Vector.set(e) is almost equivalent to direct Vector[e] access method 244 * @param {int} e index of element to be set 245 * @param {float} val value to be set 246 * @example 247 * v = vec([4,7,2,4]); 248 * v.set(1,68.); // v = Vector([4,68,2,4]) 249 * v[1] = 43.; // v = Vector([4,43,2,4]) 250 * v.set(9,68.); // nothing is done - out of bounds 251 * // v = Vector([4,43,2,4]) 252 * v[9] = 43.; 253 * /// new object this[9] is created, but no reflection to actual Vector is made 254 * // v = Vector([4,43,2,4]) 255 */ 256 Vector.prototype.set = function(e,val) { 257 if (e<0 || e>=this.nElements) { /*jsmLogger.warn('Vector.set: out of bounds');*/ return; } 258 this[e] = val; 259 } 260 261 /** Coefficient access function, increment one element of receiver ( this[r] += val ). Vector.incr(e,val) is much safer than direct Vector[e] += val (see example) 262 * @param {int} e index of element to be incremented 263 * @param {float} val value to be added 264 * @example 265 * v = vec([4,7,2,4]); 266 * v.incr(1,4.); // v = Vector([4,11,2,4]) 267 * v[1] += 4.; // v = Vector([4,15,2,4]) 268 * v.incr(9,4.); // nothing is done, out of bounds 269 * // v = Vector([4,15,2,4]) 270 * /// v[9] += 4. would result into error (v[9] is not defined) 271 */ 272 Vector.prototype.incr = function(e,val) { 273 if (e<0 || e>=this.nElements) { /*jsmLogger.warn('Vector.incr: out of bounds');*/ return; } 274 if (val) { this[e] += val; } 275 } 276 277 /** Testing vectors size equality 278 * @param {Vector} vec vector to be tested 279 * @returns {bool} true if vec and receiver has same size, false otherwise 280 * @example 281 * a = vec([1,2,3,4]); 282 * b = vec([5,6,7,8,9,10,11,12]); 283 * c = vec([13,14,15,16]); 284 * t1 = a.isSameSizeAs(b); // t1 = false 285 * t2 = a.isSameSizeAs(c); // t2 = true 286 */ 287 Vector.prototype.isSameSizeAs = function(vec) { 288 return this.nElements==vec.nElements; 289 } 290 291 /** Testing if receiver can multiply given matrix from left ( this^T * mat ) 292 * @param {Matrix} mat matrix to be tested 293 * @returns {bool} true if the multiplication is possible, false otherwise 294 * @example 295 * v = vec([1,2,3]); 296 * m1 = mat([[11,12,13],[21,22,23]]); 297 * m2 = mat([[11,12],[21,22],[31,32]]); 298 * t1 = v.canMultiplyMat(m1); // t1 = true 299 * t2 = v.canMultiplyMat(m2); // t2 = false 300 */ 301 Vector.prototype.canMultiplyMat = function(mat) { 302 return this.nElements == mat.nCols; 303 } 304 305 /** Alias for canMultiplyMat(), see {@link Vector#canMultiplyMat} 306 * @function 307 */ 308 canMultiplyMatFromLeft = Vector.prototype.canMultiplyMat; 309 310 /** Sets elements of receiver form given Array object 311 * @param {[floats]} arry Array object containing new receiver elements 312 * @example 313 * v.fromArray([3,6,5,2]); // v = Vector([3,6,5,2]) 314 */ 315 Vector.prototype.fromArray = function(arry) { 316 var n = arry.length; 317 var e; 318 for (e=0; e<n; e++) { this[e] = arry[e]; } 319 for (e=n; e<this.nElements; e++) { delete this[e]; } 320 this.nElements = n; 321 } 322 323 /** Returns elements of receiver as an Array object 324 * @returns {[floats]} Array object containing receiver's elements 325 * @example 326 * v =vec([3,4,5,6]); 327 * s = v.toArray(); // s = [3,4,5,6] 328 */ 329 Vector.prototype.toArray = function() { 330 var ret = []; 331 for (var i=0; i<this.nElements; i++) { ret[i] = this[i]; } 332 return ret; 333 } 334 335 /** Returns sum of receiver and vector ( ret = this + vec ) 336 * @param {Vector} vec 2nd vector of the sum 337 * @returns {Vector} sum of receiver and vec 338 * @example 339 * v1 = vec([1,2,3]); 340 * v2 = vec([6,1,5]); 341 * v4 = v1.add(v2); // v4 = Vector([7,3,8]) 342 */ 343 Vector.prototype.add = function(vec) { 344 if (!this.isSameSizeAs(vec)) { /*jsmLogger.warn('Vector.add: dimensions mismatched');*/ return null; } 345 var ret = new Vector(); 346 for (var e=0; e<this.nElements; e++) { ret[e] = this[e] + vec[e]; } 347 ret.nElements = this.nElements; 348 return ret; 349 } 350 351 /** Add given vector to receiver ( this += vec ) 352 * @param {Vector} vec vector to be added 353 * @example 354 * v1 = vec([1,2,3]); 355 * v2 = vec([6,1,5]); 356 * v1.iadd(v2); // v1 = Vector([7,3,8]) 357 */ 358 Vector.prototype.iadd = function(vec) { 359 if (!this.isSameSizeAs(vec)) { /*jsmLogger.warn('Vector.iadd: dimensions mismatched');*/ return; } 360 for (var e=0; e<this.nElements; e++) { this[e] += vec[e]; } 361 } 362 363 /** Modifies receiver to become sum of two vectors ( this = vec1 + vec2 ). Receiver's size is adjusted 364 * @param {Vector} vec1 1st vector of the sum 365 * @param {Vector} vec2 2nd vector of the sum 366 * @example 367 * v = vec([3,4]); 368 * v1 = vec([1,2,3]); 369 * v2 = vec([6,1,5]); 370 * v.beSumOf(v1,v2); // v = Vector([7,3,8]) 371 */ 372 Vector.prototype.beSumOf = function(vec1,vec2) { 373 if (!vec1.isSameSizeAs(vec2)) { /*jsmLogger.warn('Vector.beSumOf: dimensions mismatched');*/ return; } 374 var e, n = vec1.nElements; 375 for (e=0; e<n; e++) { this[e] = vec1[e] + vec2[e]; } 376 for (e=n; e<this.nElements; e++) { delete this[e]; } 377 this.nElements = n; 378 } 379 380 /** Returns difference of receiver and vector ( ret = this - vec ) 381 * @param {Vector} vec 2nd vector of subtraction 382 * @returns {Vector} difference of receiver and vec 383 * @example 384 * v1 = vec([1,2,3]); 385 * v2 = vec([6,1,5]); 386 * v4 = v1.sub(v2); // v4 = Vector([-5,1,-2]) 387 */ 388 Vector.prototype.sub = function(vec) { 389 if (!this.isSameSizeAs(vec)) { /*jsmLogger.warn('Vector.sub: dimensions mismatched');*/ return null; } 390 var ret = new Vector(); 391 for (var e=0; e<this.nElements; e++) { ret[e] = this[e] - vec[e]; } 392 ret.nElements = this.nElements; 393 return ret; 394 } 395 396 /** Subtract given vector to receiver ( this -= vec ) 397 * @param {Vector} vec vector to be subtracted 398 * @example 399 * v1 = vec([1,2,3]); 400 * v1.isub(v2); // v1 = Vector([-5,1,-2]) 401 */ 402 Vector.prototype.isub = function(vec) { 403 if (!this.isSameSizeAs(vec)) { /*jsmLogger.warn('Vector.isub: dimensions mismatched');*/ return; } 404 for (var e=0; e<this.nElements; e++) { this[e] -= vec[e]; } 405 } 406 407 /** Modifies receiver to become difference of two vectors ( this = vec1 - vec2 ). Receiver's size is adjusted 408 * @param {Vector} vec1 1st vector of the difference 409 * @param {Vector} vec2 2nd vector of the difference 410 * @example 411 * v = vec([4,5]); 412 * v1 = vec([1,2,3]); 413 * v2 = vec([6,1,5]); 414 * v.beDifferenceOf(v1,v2); // v = Vector([-5,1,-2]) 415 */ 416 Vector.prototype.beDifferenceOf = function(vec1,vec2) { 417 if (!vec1.isSameSizeAs(vec2)) { /*jsmLogger.warn('Vector.beDifferenceOf: dimensions mismatched');*/ return; } 418 var e, n = vec1.nElements; 419 for (e=0; e<n; e++) { this[e] = vec1[e] - vec2[e]; } 420 for (e=n; e<this.nElements; e++) { delete this[e]; } 421 this.nElements = n; 422 } 423 424 /** Returns dot (inner) product of receiver and given vector 425 * @param {Vector} vec vector for dot product 426 * @param {int} [n=0] dot product will be made from first n elements. If not specified, zero or negative, all elements are used 427 * @returns {float} dot product of receiver and vec from first n elements 428 * @example 429 * v1 = vec([1,2,3]); 430 * v2 = vec([6,1,5]); 431 * d2 = v1.dot(v2,2); // d2 = 8 432 */ 433 Vector.prototype.dot = function(vec,n) { 434 var n = n || -1; 435 if (n<=0 || n>this.nElements) { n = this.nElements; } 436 if (n > vec.nElements) { /*jsmLogger.warn('Vector.dot: wrong number of elements');*/ return null; } 437 var ret = 0.; 438 for (var e=0; e<n; e++) { ret += this[e]*vec[e]; } 439 return ret; 440 } 441 442 /** Returns squared (Euclidean) norm of receiver (ret = this.^T * this ) 443 * @returns {float} squared Euclidean norm of receiver 444 * @example 445 * v = vec([4,2,4]); 446 * n = v.squaredNorm(); // n = 36 447 */ 448 Vector.prototype.squaredNorm = function() { 449 return this.dot(this); 450 } 451 452 /** Returns (Euclidean) norm of receiver (ret = sqrt(this^T * this) ) 453 * @returns {float} Euclidean norm of receiver 454 * @example 455 * v = vec([4,2,4]); 456 * n = v.norm(); // n = 6 457 */ 458 Vector.prototype.norm = function() { 459 return Math.sqrt(this.dot(this)); 460 } 461 462 /** Normlize receiver ( this.norm()=1. ) 463 * @example 464 * v = vec([4,2,4]); 465 * v.normalize(); // v = Vector([0.6666,0.3333,0.6666]) 466 */ 467 Vector.prototype.normalize = function() { 468 var n = this.norm(); 469 if (n==0.) { /*jsmLogger.warn('Vector.normalize: norm == 0.');*/ return; } 470 this.imulf(1/n); 471 } 472 473 /** Returns normalized copy of receiver ( ret.norm()=1. ) 474 * @returns {Vector} normalized copy of receiver 475 * @example 476 * v1 = vec([4,2,4]); 477 * v2 = v1.normalized(); // v2 = Vector([0.6666,0.3333,0.6666]) 478 */ 479 Vector.prototype.normalized = function() { 480 var n = this.norm(); 481 if (n==0.) { /*jsmLogger.warn('Vector.normalized: norm == 0.');*/ return null; } 482 var e, ret = new Vector(), nn=1/n; 483 for (e=0; e<this.nElements; e++) { ret[e] = this[e]*nn; } 484 ret.nElements = this.nElements; 485 return ret; 486 } 487 488 /** Returns squared energy norm of receiver (ret = this^T*mat*this) 489 * @param {Matrix} mat marix of norm 490 * @returns {float} squared energy norm of receiver 491 * @example 492 * v = vec([1,2,3]); 493 * m = mat([[8,0,0],[0,2,1],[0,1,3]]); 494 * n = v.squaredEnergyNorm(m); // n = 55 495 */ 496 Vector.prototype.squaredEnergyNorm = function(mat) { 497 var mat = mat; 498 return this.mulm(mat).dot(this); 499 } 500 501 /** Returns energy norm of receiver (ret = sqrt(this*mat*this)) 502 * @param {Matrix} mat marix of norm 503 * @returns {float} energy norm of receiver 504 * @example 505 * v = vec([1,2,3]); 506 * m = mat([[8,0,0],[0,2,1],[0,1,3]]); 507 * n = v.energyNorm(m); // n = 7.4162 508 */ 509 Vector.prototype.energyNorm = function(mat) { 510 var mat = mat; 511 return Math.sqrt(this.squaredEnergyNorm(mat)); 512 } 513 514 /** Returns dyadic (tensor, outer, direct,..) product of receiver and given vector 515 * @param {Vector} vec vector for multiplication 516 * @returns {Matrix} dyadic product of receiver and vec 517 * @example 518 * v1 = vec([1,2,3]); 519 * v2 = vec([4,5,6]); 520 * m = v1.dyadic(v2); // m = Matrix([[4,5,6],[8,10,12],[12,15,18]]) 521 */ 522 Vector.prototype.dyadic = function(vec) { 523 var vec = vec; 524 var ret = new Matrix(); 525 for (var r=0; r<this.nElements; r++) { 526 ret[r] = {}; 527 for (var c=0; c<vec.nElements; c++) { ret[r][c] = this[r] * vec[c]; } 528 } 529 ret.nRows = this.nElements; 530 ret.nCols = vec.nElements; 531 return ret; 532 } 533 534 /** Alias for dyadic() (inspired by LaTeX symbol for dyadic product operation), see {@link Vector#dyadic} 535 * @function 536 */ 537 Vector.prototype.otimes = Vector.prototype.dyadic; 538 539 /** Alias for dyadic(), see {@link Vector#dyadic} 540 * @function 541 */ 542 Vector.prototype.outer = Vector.prototype.dyadic; 543 544 /** Alias for dyadic(), see {@link Vector#dyadic} 545 * @function 546 */ 547 Vector.prototype.tensor = Vector.prototype.dyadic; 548 549 /** Returns vector (cross) product of receiver and given vector. Both receiver and given vector have to be of length 3. 550 * @param {Vector} vec 2nd vector of multiplication 551 * @returns {Vector} cross product of receiver and vec 552 * @example 553 * v1 = vec([1,2,3]); 554 * v2 = vec([4,5,6]); 555 * v3 = v1.cross(v2); // v3 = Vector([-3,6,-3]) 556 */ 557 Vector.prototype.cross = function(vec) { 558 if (this.nElements!=3 || vec.nElements!=3) { /*jsmLogger.warn('Vector.cross: wrong dimensions');*/ return null; } 559 return Vector.create([ 560 this[1]*vec[2]-this[2]*vec[1], 561 this[2]*vec[0]-this[0]*vec[2], 562 this[0]*vec[1]-this[1]*vec[0]]); 563 } 564 565 /** Modifies receiver to become cross product of two vectors. Both given vectors have to be of length 3. Receiver's size is adjusted 566 * @param {Vector} vec1 1st vector for multiplication 567 * @param {Vector} vec1 2nd vector for multiplication 568 * @example 569 * v = vec([5,6]); 570 * v1 = vec([1,2,3]); 571 * v2 = vec([4,5,6]); 572 * v.beCrossProductOf(v1,v2); // v = Vector([-3,6,-3]) 573 */ 574 Vector.prototype.beCrossProductOf = function(vec1,vec2) { 575 if (vec1.nElements!=3 || vec2.nElements!=3) { /*jsmLogger.warn('Vector.beCrossProductOf: wrong dimensions');*/ return; } 576 this[0] = vec1[1]*vec2[2]-vec1[2]*vec2[1]; 577 this[1] = vec1[2]*vec2[0]-vec1[0]*vec2[2]; 578 this[2] = vec1[0]*vec2[1]-vec1[1]*vec2[0]; 579 for (var e=3; e<this.nElements; e++) { delete this[e]; } 580 this.nElements = 3; 581 } 582 583 /** Returns negative of receiver ( ret = -this ) 584 * @returns {Vector} negative of receiver 585 * @example 586 * v1 = vec([1,2,3]); 587 * v2 = v1.negated(); // v2 = Vector([-1,-2,-3]) 588 */ 589 Vector.prototype.negated = function() { 590 return this.mulf(-1.); 591 } 592 593 /** Alias for negated(), see {@link Vector#negated} 594 * @function 595 */ 596 Vector.prototype.neg = Vector.prototype.negated; 597 598 /** Negate receiver ( ret *= -1., ret = -ret ) 599 * @example 600 * v1 = vec([1,2,3]); 601 * v1.negate(); // v1 = Vector([-1,-2,-3]) 602 */ 603 Vector.prototype.negate = function() { 604 for (var e=0; e<this.nElements; e++) { this[e] *= -1.; } 605 } 606 607 /** Alias for neagete(), see {@link Vector#negate} 608 * @function 609 */ 610 Vector.prototype.ineg = Vector.prototype.negate; 611 612 /** Modifies receiver to become negative of given vector (this = -vec). Receiver's size is adjusted 613 * @param {Vector} vec given vector 614 * @example 615 * v1 = vec([4,5]) 616 * v2 = vec([1,2,3]); 617 * v1.beNegativeOf(v2); // v1 = Vector([-1,-2,-3]) 618 */ 619 Vector.prototype.beNegativeOf = function(vec) { 620 var e, n = vec.nElements; 621 for (e=0; e<n; e++) { this[e] = -vec[e]; } 622 for (e=n; e<this.nElements; e++) { delete this[e]; } 623 this.nElements = n; 624 } 625 626 /** Returns receiver multiplied by float f ( ret = this * f ) 627 * @param {float} f float multiplier 628 * @returns {Vector} copy of receiver multiplied by f 629 * @example 630 * v1 = vec([1,2,3]); 631 * v2 = v1.mulf(3); // v2 = Vector([3,6,9]) 632 */ 633 Vector.prototype.mulf = function(f) { 634 var ret = new Vector(); 635 for (var e=0; e<this.nElements; e++) { ret[e] = f*this[e]; } 636 ret.nElements = this.nElements; 637 return ret; 638 } 639 640 /** Multiply receiver by float f ( this *= f ) 641 * @param {float} f float multiplier 642 * @example 643 * v = vec([1,2,3]); 644 * v.imulf(3); // v = Vector([3,6,9]) 645 */ 646 Vector.prototype.imulf = function(f) { 647 for (var e=0; e<this.nElements; e++) { this[e] *= f; } 648 } 649 650 /** Returns product of receiver and given matrix ( ret = this^T * mat ) 651 * @param {Matrix} mat matrix to multiply 652 * @returns {Vector} copy of receiver multiplied by mat 653 * @example 654 * v1 = vec([1,2,3]); 655 * m = mat([[11,12,13],[21,22,23],[31,32,33]]) 656 * v2 = v1.mulm(m); // v2 = Vector([146,152,158]) 657 */ 658 Vector.prototype.mulm = function(mat) { 659 if (!this.canMultiplyMat(mat)) { /*jsmLogger.warn('Vector.mulm: dimensions mismatched');*/ return null; } 660 var ret = new Vector(), temp, nc = mat.nCols, nr = this.nElements; 661 for (var c=0; c<nc; c++) { 662 temp = 0.; 663 for (var r=0; r<nr; r++) { 664 temp += this[r]*mat[r][c]; 665 } 666 ret[c] = temp; 667 } 668 ret.nElements = nc; 669 return ret; 670 } 671 672 /** Returns product of receiver and given multiplier (float or Matrix) 673 * @param {Matrix|float} what matrix or float to multiply 674 * @returns {Vector} copy of receiver multiplied by what 675 * @example 676 * v1 = vec([1,2,3]); 677 * v2 = v1.mul(mat([[11,12,13],[21,22,23],[31,32,33]])); // v2 = Vector([146,152,158]) 678 * v3 = v1.mul(3); // v3 = Vector([3,6,9]) 679 */ 680 Vector.prototype.mul = function(what) { 681 if (what instanceof Matrix) { return this.mulm(what); } 682 if ((typeof what).toLowerCase() == 'number') { return this.mulf(what); } 683 /*jsmLogger.warn('Vector.mul: wrong argument');*/ 684 return null; 685 } 686 687 /** Alias for mul(), see {@link Vector#mul} 688 * @function 689 */ 690 Vector.prototype.x = Vector.prototype.mul; 691 692 /** Modifies receiver to become product of given matrix and vector ( this = mat * vec ). Receiver's size is adjusted 693 * @param {Matrix} mat matrix of multiplication 694 * @param {Vector} vec vector of multiplication 695 * @example 696 * v = vec([4,5]); 697 * m = mat([[11,12,13],[21,22,23],[31,32,33]]); 698 * v1 = vec([1,2,3]); 699 * v.beProductOf(m,v1); // v = Vector([74,134,194]) 700 */ 701 Vector.prototype.beProductOf = function(mat,vec) { 702 if (!mat.canMultiplyVec(vec)) { /*jsmLogger.warn('Vector.beProductOf: dimensions mismatched');*/ return; } 703 var nr = mat.nRows; 704 var nc = mat.nCols; 705 var temp, r, c; 706 for (r=0; r<nr; r++) { 707 temp = 0.; 708 for (c=0; c<nc; c++) { 709 temp += mat[r][c]*vec[c]; 710 } 711 this[r] = temp; 712 } 713 for (r=nr; r<this.nElements; r++) { delete this[r]; } 714 this.nElements = nr; 715 } 716 717 /** Modifies receiver to become product of given vector and matrix ( this = vec^T * mat ) 718 * @param {Vector} vec vector of multiplication 719 * @param {Matrix} mat matrix of multiplication 720 * @example 721 * v= vec([5,6]); 722 * v1 = vec([1,2,3]); 723 * m = mat([[11,12,13],[21,22,23],[31,32,33]]) 724 * v.beTProductOf(v1,m); // v = Vector([146,152,158]) 725 */ 726 Vector.prototype.beTProductOf = function(vec,mat) { 727 if (!vec.canMultiplyMat(mat)) { /*jsmLogger.warn('Vector.beTProductOf: dimensions mismatched');*/ return; } 728 var nr = mat.nRows; 729 var nc = mat.nCols; 730 var temp, r, c; 731 for (c=0; c<nc; c++) { 732 temp = 0.; 733 for (r=0; r<nr; r++) { 734 temp += vec[r]*mat[r][c]; 735 } 736 this[c] = temp; 737 } 738 for (c=nc; c<this.nElements; c++) { delete this[c]; } 739 this.nElements = nc; 740 } 741 742 /** Returns subvector of receiver. ret = this[coeffs] 743 * @param {[floats]|Vector} coeffs array containing coefficients of desired subvector 744 * @returns {Vector} desired subvector 745 * @example 746 * v1 = vec([4,7,9,1,8,3,2,4,6,7,5,1,6,9]); 747 * v2 = v1.getSubVector([2,3,6,8,9]); // v2 = Vector([9,1,2,6,7]) 748 */ 749 Vector.prototype.getSubVector = function(coeffs) { 750 var n = coeffs.nElements==undefined? coeffs.length : coeffs.nElements; 751 if (n>this.nElements) { /*jsmLogger.warn('Vector.getSubVector: wrong argument dimensions');*/ return null; } 752 var e, ce; 753 var ret = new Vector(); 754 for (e=0; e<n; e++) { 755 ce = coeffs[e]; 756 if (ce<0 || ce>=this.nElements) { /*jsmLogger.warn('Vector.getSubVector: wrong argument values');*/ return null; } 757 ret[e] = this[coeffs[e]]; 758 } 759 ret.nElements = n; 760 return ret; 761 } 762 763 /** Alias for getSubVector, see {@link Vector#getSubVector} 764 * @function 765 */ 766 Vector.prototype.getsv = Vector.prototype.getSubVector; 767 768 /** Modifies receiver to become subvector of given vector (this = vec[coeffs]). Receiver's size is adjusted 769 * @param {Vector} vec vector to get subvector from 770 * @param {[floats]|Vector} coeffs array containing coefficients of desired subvector 771 * @example 772 * v1 = vec([4,7,9,1,8,3,2,4,6,7,5,1,6,9]); 773 * v2 = vec([3,4]); 774 * v2.beSubVectorOf(v1,[2,3,6,8,9]); // v2 = Vector([9,1,2,6,7]) 775 */ 776 Vector.prototype.beSubVectorOf = function(vec,coeffs) { 777 var n = coeffs.nElements==undefined? coeffs.length : coeffs.nElements; 778 if (n>vec.nElements) { /*jsmLogger.warn('Vector.beSubVectorOf: wrong argument dimensions');*/ return; } 779 var e; 780 for (e=0; e<n; e++) { if (coeffs[e]<0 || coeffs[e]>=vec.nElements) { /*jsmLogger.warn('Vector.beSubVectorOf: wrong argument values');*/ return; } } 781 for (e=0; e<n; e++) { this[e] = vec[coeffs[e]]; } 782 for (e=n; e<this.nElements; e++) { delete this[e]; } 783 this.nElements = n; 784 } 785 786 /** Sets subvector at defined positions of receiver ( this[coeffs]=vec ) 787 * @param {[floats]|Vector} coeffs array containing coefficients to be set 788 * @param {Vector} vec vector to be set 789 * @example 790 * v = vec([4,7,9,1,8,3,2,4,6,7,5,1,6,9]); 791 * v.setSubVector([2,3,6,8,9],vec([1.1,2.2,3.3,4.4,5.5])); // v = Vector([4,7,1.1,2.2,8,3,3.3,4,4.4,5.5,5,1,6,9]) 792 */ 793 Vector.prototype.setSubVector = function(coeffs,vec) { 794 var n = coeffs.nElements==undefined? coeffs.length : coeffs.nElements; 795 var vi = vec.nElements; 796 if (vi != n) { /*jsmLogger.warn('Vector.setSubVector: wrong argument dimensions');*/ return; } 797 if (n>this.nElements) { /*jsmLogger.warn('Vector.setSubVector: wrong argument dimensions');*/ return; } 798 var e; 799 for (e=0; e<n; e++) { if (coeffs[e]<0 || coeffs[e]>=this.nElements) { /*jsmLogger.warn('Vector.setSubVector: wrong argument values');*/ return; } } 800 for (e=0; e<n; e++) { this[coeffs[e]] = vec[e]; } 801 } 802 803 /** Alias for setSubVector, see {@link Vector#setSubVector} 804 * @function 805 */ 806 Vector.prototype.setsv = Vector.prototype.setSubVector; 807 808 /** Increment subvector at defined positions of receiver (this[coeffs] += vec) 809 * @param {[floats]|Vector} coeffs array containing coefficients to be incremented 810 * @param {Vector} vec vector to be added 811 * @example 812 * v = vec([4,7,9,1,8,3,2,4,6,7,5,1,6,9]); 813 * v.incrSubVector([2,3,6,8,9],vec([1.1,2.2,3.3,4.4,5.5])); // v = Vector([4,7,10.1,3.2,8,3,5.3,4,10.4,12.5,5,1,6,9]) 814 */ 815 Vector.prototype.incrSubVector = function(coeffs,vec) { 816 var n = coeffs.nElements==undefined? coeffs.length : coeffs.nElements; 817 var vi = vec.nElements; 818 if (vi != n) { /*jsmLogger.warn('Vector.incrSubVector: wrong argument dimensions');*/ return; } 819 if (n>this.nElements) { /*jsmLogger.warn('Vector.incrSubVector: wrong argument dimensions');*/ return; } 820 var e; 821 for (e=0; e<n; e++) { if (coeffs[e]<0 || coeffs[e]>=this.nElements) { /*jsmLogger.warn('Vector.incrSubVector: wrong argument values');*/ return; } } 822 for (e=0; e<n; e++) { this[coeffs[e]] += vec[e]; } 823 } 824 825 /** Alias for incrSubVector, see {@link Vector#incrSubVector} 826 * @function 827 */ 828 Vector.prototype.incrsv = Vector.prototype.incrSubVector; 829 830 /** Decrement subvector at defined positions of receiver (this[coeffs] -= vec) 831 * @param {[floats]|Vector} coeffs array containing coefficients to be decremented 832 * @param {Vector} vec vector to be subtracted 833 * @example 834 * v = vec([4,7,9,1,8,3,2,4,6,7,5,1,6,9]); 835 * v.decrSubVector([2,3,6,8,9],vec([1.1,2.2,3.3,4.4,5.5])); // v = Vector([4,7,7.9,-1.2,8,3,-1.3,4,1.6,1.5,5,1,6,9]) 836 */ 837 Vector.prototype.decrSubVector = function(coeffs,vec) { 838 var n = coeffs.nElements==undefined? coeffs.length : coeffs.nElements; 839 var vi = vec.nElements; 840 if (vi != n) { /*jsmLogger.warn('Vector.decrSubVector: wrong argument dimensions');*/ return; } 841 if (n>this.nElements) { /*jsmLogger.warn('Vector.decrSubVector: wrong argument dimensions');*/ return; } 842 var e; 843 for (e=0; e<n; e++) { if (coeffs[e]<0 || coeffs[e]>=this.nElements) { /*jsmLogger.warn('Vector.decrSubVector: wrong argument values');*/ return; } } 844 for (e=0; e<n; e++) { this[coeffs[e]] -= vec[e]; } 845 } 846 847 /** Alias for decrSubVector, see {@link Vector#decrSubVector} 848 * @function 849 */ 850 Vector.prototype.decrsv = Vector.prototype.decrSubVector; 851 852 /** assemble receiver to given vector ( vec[coeffs] += this ) 853 * @param {Vector} vec vector where receiver is assembled 854 * @param {[floats]|Vector} coeffs array containing coefficients of vec to be incremented 855 * @example 856 * v1 = vec([5,8,2,4,3,5]); 857 * v2 = vec([1,2,3]); 858 * v2.assemble(v1,[5,1,2]); // v1 = Vector([5,10,5,4,3,6]) 859 */ 860 Vector.prototype.assemble = function(vec,coeffs) { 861 var n = coeffs.nElements==undefined? coeffs.length : coeffs.nElements; 862 var vi = this.nElements; 863 if (vi != n) { /*jsmLogger.warn('Vector.assemble: wrong argument dimensions');*/ return; } 864 if (n>vec.nElements) { /*jsmLogger.warn('Vector.assemble: wrong argument dimensions');*/ return; } 865 var e; 866 for (e=0; e<n; e++) { if (coeffs[e]<0 || coeffs[e]>=vec.nElements) { /*jsmLogger.warn('Vector.assemble: wrong argument values');*/ return; } } 867 for (e=0; e<n; e++) { vec[coeffs[e]] += this[e]; } 868 } 869 870 /** Modifies receiver to become row of given matrix - indexing from 0. Receiver's size is adjusted 871 * @param {Matix} mat given matrix 872 * @param {float} r desired row (indexing from 0) 873 * @example 874 * v = vec([1,2]); 875 * m = mat([[11,12,13],[21,22,23],[31,32,33]]); 876 * v.beRowOf(m,1) // v = Vector([21,22,23]) 877 */ 878 Vector.prototype.beRowOf = function(mat,r) { 879 var c, nc = mat.nCols; 880 for (c=0; c<nc; c++) { this[c] = mat[r][c]; } 881 for (c=nc; n<this.nElements; c++) { delete this[c]; } 882 this.nElements = nc; 883 } 884 885 /** Modifies receiver to become column of given matrix - indexing from 0. Receiver's size is adjusted 886 * @param {Matix} mat given matrix 887 * @param {float} c desired column (indexing from 0) 888 * @example 889 * v = vec([1,2]); 890 * m = mat([[11,12,13],[21,22,23],[31,32,33]]); 891 * v.beColOf(m,1) // v = Vector([12,22,32]) 892 */ 893 Vector.prototype.beColOf = function(mat,c) { 894 var r, nr = mat.nRows; 895 for (r=0; r<nr; r++) { this[r] = mat[r][c]; } 896 for (r=nr; n<this.nElements; r++) { delete this[r]; } 897 this.nElements = nr; 898 } 899 900 /** Alias for beColOf(), see {@link Vector#beColOf} 901 * @function 902 */ 903 Vector.prototype.beColumnOf = Vector.prototype.beColOf; 904 905 /** Returns copy of receiver 906 * @returns {Vector} copy of receiver 907 * @example 908 * v1 = vec([1,2,3]); 909 * v2 = v1; 910 * v3 = v1.copied(); 911 * v1.set(1,6); 912 * // v1; = Vector([1,6,3]) 913 * // v2; = Vector([1,6,3]) 914 * // v3; = Vector([1,2,3]) 915 */ 916 Vector.prototype.copied = function() { 917 var ret = new Vector(), e, n = this.nElements; 918 for (e=0; e<n; e++) { ret[e] = this[e]; } 919 ret.nElements = n; 920 return ret; 921 } 922 923 /** Alias for copied(), see {@link Vector#copied} 924 * @function 925 */ 926 Vector.prototype.copy = Vector.prototype.copied; 927 928 /** Modifies receiver to become copy of given vector (this = vec). Receiver's size is adjusted 929 * @param {Vector} vec vector to be copied to receiver 930 * @example 931 * v1 = vec([4,5]); 932 * v2 = vec([1,2,3]); 933 * v1.beCopyOf(v2) // v2 = Vector([1,2,3]) 934 */ 935 Vector.prototype.beCopyOf = function(vec) { 936 var s = vec.nElements, e; 937 for (e=0; e<s; e++) { this[e] = vec[e]; } 938 for (e=s; e<this.nElements; e++) { delete this[e]; } 939 this.nElements = s; 940 } 941 942 /** Returns string representation of receiver 943 * @returns {string} string representation of receiver 944 * @example 945 * v1 = vec([0.5999999999999999,2,3]); 946 * s = v1.toString(); // s = 'Vector([ 1, 2, 3 ])' 947 */ 948 Vector.prototype.toString = function() { 949 var nn = this.nElements, e, val; 950 if (nn == 0) { return "Vector([])\n"; } 951 var ret = "Vector([ "; 952 for (e=0; e<nn-1; e++) { 953 val = this[e]; 954 //val = (Math.abs(val) < JSM_TOL)? 0. : Math.round(val*JSM_ROUNDFACTOR)/JSM_ROUNDFACTOR; 955 ret += val.toExponential(3)+", "; 956 } 957 val = this[nn-1]; 958 //val = (Math.abs(val) < JSM_TOL)? 0. : Math.round(val*JSM_ROUNDFACTOR)/JSM_ROUNDFACTOR; 959 ret += val.toExponential(3)+" ])\n"; 960 return ret; 961 } 962 963 /** Swaps two elements of receiver 964 * @param {int} e1 index of first element to swap 965 * @param {int} e2 index of second element to swap 966 * @example 967 * v = vec([1,2,3,4,5,6,7]); 968 * v.swapElements(1,4); // v = Vector([1,5,3,4,2,6,7]) 969 */ 970 Vector.prototype.swapElements = function(e1,e2) { 971 if (e1==e2) { return; } 972 if (e1<0 || e2<0 || e1>=this.nElements || e2>=this.nElements) { /*jsmLogger.warn('Vector.swapElements: wrong coefficients');*/ return; } 973 var temp = this[e1]; 974 this[e1] = this[e2]; 975 this[e2] = temp; 976 } 977 978 /** Appends given float/vector to the end of receiver 979 * @param {float|Vector|[Vectors]} newElems new element(s) to be appended 980 * @example 981 * v = vec([1,2]); 982 * v.appendElements(4); // v = Vector([1,2,4]); 983 * v.appendElements(vec([7,6])); // v = Vector([1,2,4,7,6]); 984 * v.appendElements([vec([1,2]),vec([3,4])]); // v = Vector([1,2,4,6,7,1,2,3,4]) 985 */ 986 Vector.prototype.appendElements = function(newElems) { 987 if (newElems instanceof Vector) { 988 var n = newElems.nElements, e; 989 for (e=0; e<n; e++) { 990 this[this.nElements] = newElems[e]; 991 this.nElements++; 992 } 993 return; 994 } 995 if (newElems instanceof Array) { 996 var n = newElems.length, e; 997 for (e=0; e<n; e++) { 998 this.appendElements(newElems[e]); 999 } 1000 return; 1001 } 1002 if ((typeof newElems).toLowerCase() == 'number') { 1003 this[this.nElements] = newElems; 1004 this.nElements++; 1005 return; 1006 } 1007 } 1008 1009 /** Permute elements of receiver according to given coefficients 1010 * @param {[ints]|Vector} coeffs coefficients of permutation 1011 * @param {bool} [backward=false] if false, receiver is permutated to given coefficients (forward). If true, from given coefficients (backward) 1012 * @example 1013 * v = vec([7,9,6]); 1014 * v.permuteElements([1,2,0]); // v = Vector([9,6,7]) 1015 * v.permuteElements([1,2,0],true); // v = Vector([7,9,6]) 1016 */ 1017 Vector.prototype.permuteElements = function(coeffs,backward) { 1018 var n; 1019 if (coeffs.nElements==undefined) { // coeffs == Array 1020 n = coeffs.length; 1021 var coeffs = coeffs.slice(); 1022 } else { // coeffs == Vector 1023 n = coeffs.nElements; 1024 var coeffs = coeffs.toArray(); 1025 } 1026 if (n != this.nElements) { /*jsmLogger.warn('Vector.permuteElements: wrong argument size');*/ return; } 1027 for (var e=0; e<n; e++) { if (e<0 || e>=this.nElements) { /*jsmLogger.warn('Vector.permuteElements: wrong argument values');*/ return; } } 1028 var backward = backward || false; 1029 var ee; 1030 if (backward) { 1031 for (var e=0; e<n; e++) { 1032 ee = coeffs[e]; 1033 while (ee!=e) { 1034 this.swapElements(e,ee); 1035 coeffs[e] = coeffs[ee]; 1036 coeffs[ee] = ee; 1037 ee = coeffs[e]; 1038 } 1039 } 1040 } else { 1041 for (var e=n-1; e>0; e--) { 1042 ee = coeffs[e]; 1043 this.swapElements(e,ee); 1044 coeffs[coeffs.indexOf(e)] = ee; 1045 } 1046 } 1047 return; 1048 } 1049 1050 /** Returns permutated copy of receiver according to given coefficients 1051 * @param {[ints]|Vector} coeffs coefficients of permutation. The content will be changed(!), use a copy if you need to preserve the content 1052 * @param {bool} [backward=false] if false, receiver is permutated to given coefficients (forward). If true, from given coefficients (backward) 1053 * @returns {Vector} permutated copy of receiver 1054 * @example 1055 * v1 = vec([7,9,6]); 1056 * v2 = v1.elementsPermutation([1,2,0]); // v2 = Vector([9,6,7]) 1057 * v3 = v1.elementsPermutation([1,2,0],true); // v3 = Vector([6,7,9]) 1058 */ 1059 Vector.prototype.elementsPermutation = function(coeffs,backward) { 1060 var n = coeffs.nElements==undefined? coeffs.length : coeffs.nElements; 1061 if (n != this.nElements) { /*jsmLogger.warn('Vector.elementsPermutation: wrong argument size');*/ return; } 1062 for (var e=0; e<n; e++) { if (e<0 || e>=this.nElements) { /*jsmLogger.warn('Vector.elementsPermutation: wrong argument values');*/ return; } } 1063 var ret = new Vector(); 1064 if (backward) { 1065 for (e=0; e<this.nElements; e++) { 1066 ret[coeffs[e]] = this[e]; 1067 } 1068 } else { 1069 for (e=0; e<this.nElements; e++) { 1070 ret[e] = this[coeffs[e]]; 1071 } 1072 } 1073 ret.nElements = this.nElements; 1074 return ret; 1075 } 1076 1077 /** Resize receiver according to given size (delete extra elements or add zero elements) 1078 * @param {int} nElements new number of elements 1079 * @example 1080 * v1 = vec([4,7,9,1,7,3]) 1081 * v2 = vec([4,6]); 1082 * v1.resize(4); // v1 = Vector([4,7,9,1]) 1083 * v2.resize(4); // v2 = Vector([4,6,0,0]) 1084 */ 1085 Vector.prototype.resize = function(nElements) { 1086 if (nElements==this.nElements) { return; } 1087 if (nElements > this.nElements) { 1088 for (var e=this.nElements; e<nElements; e++) { this[e] = 0.; } 1089 this.nElements = nElements; 1090 return; 1091 } 1092 if (nElements < this.nElements) { 1093 for (var e=nElements; e<this.nElements; e++) { delete this[e]; } 1094 this.nElements = nElements; 1095 return; 1096 } 1097 } 1098 1099 /** Returns resized copy of receiver according to given size (delete extra elements or add zero elements) 1100 * @param {int} nElements new number of elements 1101 * @returns {Vector} resized copy of receiver 1102 * @example 1103 * v1 = vec([4,7,9,1]); 1104 * v2 = v1.resized(2); // v2 = Vector([4,7]) 1105 * v3 = v1.resized(6); // v3 = Vector([4,7,9,1,0,0]) 1106 */ 1107 Vector.prototype.resized = function(nElements) { 1108 var e, ret = new Vector(); 1109 if (nElements<=this.nElements) { 1110 for (e=0; e<nElements; e++) { ret[e] = this[e]; } 1111 } else { 1112 for (e=0; e<this.nElements; e++) { ret[e] = this[e]; } 1113 for (e=this.nElements; e<nElements; e++) { ret[e] = 0.; } 1114 } 1115 ret.nElements = nElements; 1116 return ret; 1117 } 1118 1119 /** Returns matrix with receiver's elements on its diagonal 1120 * @returns {Matrix} diagonal matrix with receiver's elements on its diagonal 1121 * @example 1122 * v = vec([1,2,3]); 1123 * m = v.toDiagonalMatrix(); // m = Matrix([[1,0,0],[0,2,0],[0,0,3]]) 1124 */ 1125 Vector.prototype.toDiagonalMatrix = function() { 1126 var ret = Matrix.Zeros(this.nElements,this.nElements); 1127 for (var e=0; e<this.nElements; e++) { ret[e][e] = this[e]; } 1128 return ret; 1129 } 1130 1131 /** Modifies receiver to contain diagonal elements of mat ( this = diag(mat) ) 1132 * @param {Matrix} mat matrix whose diagonal is copied to receiver 1133 * @example 1134 * v = vec([6,7]); 1135 * m = mat([[1,2,3],[4,5,6],[7,8,9]]); 1136 * v.beDiagonalFrom(m); // v = Vector([1,5,9]) 1137 */ 1138 Vector.prototype.beDiagonalFrom = function(mat) { 1139 if (!mat.isSquare()) { /*jsmLogger.warn('Vector.beDiagonalFrom: non-square matrix');*/ return; } 1140 var e, s = mat.nRows; 1141 for (e=0; e<s; e++) { this[e] = mat[e][e]; } 1142 for (e=s; s<this.nElements; e++) { delete this[e]; } 1143 this.nElements = s; 1144 } 1145 1146 /** Returns matrix with receiver's elements on diagonal (if no argument is specified, see{@link Vector#toDiagonalMatrix}) or modifies receiver to contain diagonal elements of given matrix (if given matrix is specified, see{@link Vector#beDiagonalFrom}) 1147 * @param {Matrix} [mat=undefined] matrix whose diagonal is copied to receiver. If not specified, diagnal matrix is returned 1148 * @returns {Matrix|undefined} if no input parameter, returns diagonal matrix, otherwise nothing 1149 * @example 1150 * v = vec([6,7]); 1151 * m1 = mat([[1,2,3],[4,5,6],[7,8,9]]); 1152 * v.diag(m1); // v = Vector([1,5,9]) 1153 * m2 = v.diag(); // m2 = Matrix([[1,0,0],[0,5,0],[0,0,9]]) 1154 */ 1155 Vector.prototype.diag = function(mat) { 1156 if (mat==undefined) { return this.toDiagonalMatrix(); } 1157 this.beDiagonalFrom(mat); 1158 } 1159 1160 /** Modifies receiver to be a solution of linear system of equations mat*this = rhs. Receiver's size is adjusted 1161 * @param {Matrix} mat matrix of the system 1162 * @param {Vector} rhs vector of right hand side 1163 * @param {string} [method="default"] see {@link Matrix#linSolve} 1164 * @param {bool} [saveOrig=true] see {@link Matrix#linSolve} 1165 * @param {[Matrices]} [precompDecomps=undefined] see {@link Matrix#linSolve} 1166 * @example 1167 * v = vec([2,3]); 1168 * a = mat([[1,2,9],[8,3,2],[3,7,3]]); 1169 * b = vec([32,20,26]); 1170 * v.beSolutionOf(a,b); // v = Vector([1,2,3]) 1171 */ 1172 Vector.prototype.beSolutionOf = function(mat,rhs,method,saveOrig,precompDecomps) { 1173 var saveOrig = saveOrig==undefined? true : saveOrig; 1174 var mat = saveOrig? mat.copy() : mat; 1175 var rhs = rhs; 1176 this.beCopyOf(rhs); 1177 mat.linSolve(this,method,false,precompDecomps); 1178 //mat.gaussianElimination(this,'default',false); 1179 } 1180 1181 /** Constructs new Vector from given array 1182 * @param {[floats]} [arry=[]] array containing elements of new vector 1183 * @returns {Vector} new Vector object 1184 * @example 1185 * v = Vector.create([1,2,3,4]); // v = Vector([1,2,3,4]) 1186 */ 1187 Vector.create = function(arry) { 1188 var a = arry || []; 1189 var n = a.length; 1190 var ret = new Vector(); 1191 for (var i=0; i<n; i++) { ret[i] = a[i]; } 1192 ret.nElements = n; 1193 return ret; 1194 } 1195 1196 /** Creates unit vector in x direction 1197 * @returns {Vector} unit x vector 1198 * @example 1199 * v = Vector.UnitX(); // v = Vector([1,0,0]) 1200 */ 1201 Vector.UnitX = function() { return Vector.create([1,0,0]); } 1202 1203 /** Creates unit vector in y direction 1204 * @returns {Vector} unit y vector 1205 * @example 1206 * v = Vector.UnitY(); // v = Vector([0,1,0]) 1207 */ 1208 Vector.UnitY = function() { return Vector.create([0,1,0]); } 1209 1210 /** Creates unit vector in z direction 1211 * @returns {Vector} unit z vector 1212 * @example 1213 * v = Vector.UnitZ(); // v = Vector([0,0,1]) 1214 */ 1215 Vector.UnitZ = function() { return Vector.create([0,0,1]); } 1216 1217 /** Creates a vector full of zeros 1218 * @param {int} [nElements=0] number of elements 1219 * @returns {Vector} new vector full of zeros 1220 * @example 1221 * v = Vector.Zeros(4); // v = Vector([0,0,0,0]) 1222 */ 1223 Vector.Zeros = function(nElements) { 1224 var nElements = nElements || 0; 1225 return new Vector(nElements) 1226 } 1227 1228 /** Creates a vector full of ones 1229 * @param {int} [nElements=0] number of elements 1230 * @returns {Vector} new vector full of zeros 1231 * @example 1232 * v = Vector.Ones(6); // v = Vector([1,1,1,1,1,1]) 1233 */ 1234 Vector.Ones = function(nElements) { 1235 var nElements = nElements || 0; 1236 var r, ret = new Vector(); 1237 for (r=0; r<nElements; r++) { ret[r] = 1.; } 1238 ret.nElements = nElements; 1239 return ret; 1240 } 1241 1242 1243 1244 1245 1246 1247 1248 /** Matrix implementation 1249 * @class Represents 2D matrix of real (floating point) numbers 1250 * @param {int} [nRows=0] number or rows of newly created Matrix 1251 * @param {int} [nCols=0] number or columns of newly created Matrix 1252 * @property {int} [nRows=0] number of rows of receiver 1253 * @property {int} [nCols=0] number of columns of receiver 1254 */ 1255 Matrix = function(nRows,nCols) { 1256 this.nRows = nRows || 0; 1257 this.nCols = nCols || 0; 1258 var r,c; 1259 for (r=0; r<this.nRows; r++) { 1260 this[r] = {}; 1261 for (c=0; c<this.nCols; c++) { this[r][c] = 0.; } 1262 } 1263 } 1264 1265 /** Empties receiver (resizes it to size 0) 1266 * @example 1267 * m = mat([[1,2],[3,4]]); 1268 * m.empty(); // m = Matrix([]) 1269 */ 1270 Matrix.prototype.empty = function() { 1271 this.resize(0,0); 1272 } 1273 1274 /** Tests if receiver is empty (has size 0,0) 1275 * @returns true if receiver is empty, false otherwise 1276 * @example 1277 * v1 = new Matrix(2,3); 1278 * v2 = new Matrix(); 1279 * b1 = v1.isEmpty(); // b1 = false 1280 * b2 = v2.isEmpty(); // b2 = true 1281 */ 1282 Matrix.prototype.isEmpty = function() { 1283 return this.nRows==0 && this.nCols==0; 1284 } 1285 1286 /** Zeroes all elements of receiver 1287 * @example 1288 * m = mat([[1,2,3],[4,5,6],[7,8,9]]); 1289 * m.zero(); // m = Matrix([[0,0,0],[0,0,0],[0,0,0]]) 1290 */ 1291 Matrix.prototype.zero = function() { 1292 for (var r=0; r<this.nRows; r++) { 1293 for (var c=0; c<this.nCols; c++) { this[r][c] = 0.; } 1294 } 1295 } 1296 1297 /** Sets all elements of receiver to 1.0 1298 * @example 1299 * m = mat([[2,3,4],[5,6,7]]); 1300 * m.beFullOfOnes(); // m = Matrix([[1,1,1],[1,1,1]]) 1301 */ 1302 Matrix.prototype.beFullOfOnes = function() { 1303 for (var r=0; r<this.nRows; r++) { 1304 for (var c=0; c<this.nCols; c++) { this[r][c] = 1.; } 1305 } 1306 } 1307 1308 /** Alias for beFullOfOnes(), see {@link Matrix#beFullOfOnes} 1309 * @function 1310 */ 1311 Matrix.prototype.one = Matrix.prototype.beFullOfOnes; 1312 1313 /** Modifies receiver to be unit matrix 1314 * @param {int} [newSize=0] new size or receiver. If omitted or 0, current size is considered (then required square matrix), else resized to newSize 1315 * @example 1316 * m = mat([[1],[2]]); 1317 * m.beUnitMatrix(); // m = Matrix([[1],[2]]) 1318 * /// nothing happened, m is not square 1319 * m.beUnitMatrix(2); // m = Matrix([[1,0],[0,1]]) 1320 * m = mat([[2,3],[4,5]]); 1321 * m.beUnitMatrix(); // m = Matrix([[1,0],[0,1]]) 1322 */ 1323 Matrix.prototype.beUnitMatrix = function(newSize) { 1324 var newSize = newSize || 0; 1325 if (newSize<=0) { 1326 if (!this.isSquare) { /*jsmLogger.warn('Matrix.beUnitMatrix: matrix is not square');*/ return; } 1327 this.zero(); 1328 for (var i=0; i<this.nRows; i++) { this[i][i] = 1.; } 1329 return; 1330 } 1331 var r,c; 1332 this.resize(newSize,newSize); 1333 for (r=0; r<newSize; r++) { this[r][r] = 1.; } 1334 } 1335 1336 /** Returns size of receiver as [nRows,nCols] 1337 * @returns {[int,int]} size of receiver 1338 * @example 1339 * m = mat([[1,2,3],[4,5,6]]); 1340 * s = m.size(); // s = [2,3] 1341 */ 1342 Matrix.prototype.size = function() { 1343 return [this.nRows,this.nCols]; 1344 } 1345 1346 /** Returns trace (sum of diagonal elements) of the receiver 1347 * @returns {float} trace of the receiver 1348 * @example 1349 * m = mat([[1,2,3],[4,5,6],[7,8,9]]) 1350 * tr = m.trace() // tr = 15 1351 */ 1352 Matrix.prototype.trace = function() { 1353 if (!this.isSquare()) { /*jsmLogger.warn('Matrix.trace: matrix is not square');*/ return null; } 1354 var ret = 0.; 1355 for (var e=0; e<this.nRows; e++) { 1356 ret += this[e][e]; 1357 } 1358 return ret; 1359 } 1360 1361 /** Alias for trace, see {@link Matrix#trace} 1362 * @function 1363 */ 1364 Matrix.prototype.diagSum = Matrix.prototype.trace; 1365 1366 /** Returns product of diagonal elements of the receiver 1367 * @returns {float} product of diagonal elements of the receiver 1368 * @example 1369 * m = mat([[1,2,3],[4,5,6],[7,8,9]]); 1370 * p = m.diagProduct(); // p = 45 1371 */ 1372 Matrix.prototype.diagProduct = function() { 1373 if (!this.isSquare()) { /*jsmLogger.warn('Matrix.trace: matrix is not square');*/ return null; } 1374 var ret = 1.; 1375 for (var e=0; e<this.nRows; e++) { 1376 ret *= this[e][e]; 1377 if (ret==0.) { return 0.; } 1378 } 1379 return ret; 1380 } 1381 1382 /** Alias for diagProduct, see {@link Matrix#diagProduct} 1383 * @function 1384 */ 1385 Matrix.prototype.dprod = Matrix.prototype.diagProduct; 1386 1387 /** Coefficient access function, returns one element of receiver ( ret = this[r][c] ). Matrix.get(r,c) is much safer than direct Matrix[r][c] access method 1388 * @param {int} r index of row of element to be returned 1389 * @param {int} c index of column of element to be returned 1390 * @returns {float} value of the element at r-th row and c-th column 1391 * @example 1392 * m = mat([[11,12,13],[21,22,23],[31,32,33]]); 1393 * g = m.get(1,2); // g = 23 1394 * g = m[1][2]; // g = 23 1395 * /// c out of bounds 1396 * g = m.get(1,5); // g = null 1397 * g = m[1][5]; // g = undefined 1398 * /// r out of bounds 1399 * g = m.get(6,5) // g = null 1400 * /// g = m[6][5]; would be error (m[6] is not defined) 1401 */ 1402 Matrix.prototype.get = function(r,c) { 1403 if (r<0 || c<0) { /*jsmLogger.warn('Matrix.get: coefficients out of bounds');*/ return null; } 1404 if (r>=this.nRows || c>=this.nCols) { return null; } 1405 return this[r][c]; 1406 } 1407 1408 /** Coefficient access function, sets one element of receiver ( this[r][c] = val ). Matrix.set(r,c,val) is much safer than direct Matrix[r][c]=val access method 1409 * @param {int} r index of row of element to be set 1410 * @param {int} c index of column of element to be set 1411 * @param {float} val value to be set 1412 * @example 1413 * m = mat([[11,12,13],[21,22,23],[31,32,33]]); 1414 * m.set(1,2,3.45); // m = Matrix([[11,12,13],[21,22,3.45],[31,32,33]]) 1415 * m[1][2] = 3.45; // m = Matrix([[11,12,13],[21,22,3.45],[31,32,33]]) 1416 * m = mat([[11,12,13],[21,22,23],[31,32,33]]); 1417 * /// c out of bounds (nothing is done by both appraches) 1418 * m.set(1,5,8.45); // m = Matrix([[11,12,13],[21,22,3.45],[31,32,33]]) 1419 * m[1][5] = 8.45; // m = Matrix([[11,12,13],[21,22,3.45],[31,32,33]]) 1420 * /// r out of bounds 1421 * m.set(6,5,8.45); // m = Matrix([[11,12,13],[21,22,3.45],[31,32,33]]) 1422 * /// m[6][5] = 8.45; would be error (m[6] is not defined) 1423 */ 1424 Matrix.prototype.set = function(r,c,val) { 1425 if (r<0 || c<0) { /*jsmLogger.warn('Matrix.set: coefficients out of bounds');*/ return; } 1426 if (r>=this.nRows || c>=this.nCols) { return; } 1427 this[r][c] = val; 1428 } 1429 1430 /** Coefficient access function, increment one element of receiver ( this[r][c] += val ). Matrix.incr(r,c,val) is much safer than direct Matrix[r][c]+=val access method 1431 * @param {int} r index of row of element to be incremented 1432 * @param {int} c index of column of element to be incremented 1433 * @param {float} val value to be add 1434 * @example 1435 * m = mat([[11,12,13],[21,22,23],[31,32,33]]); 1436 * m.incr(1,2,3.45); // m = Matrix([[11,12,13],[21,22,26.45],[31,32,33]]) 1437 * m[1][2] += 3.45; // m = Matrix([[11,12,13],[21,22,29.9],[31,32,33]]) 1438 * /// c out of bounds 1439 * m.incr(1,5,3.45); // m = Matrix([[11,12,13],[21,22,29.9],[31,32,33]]) 1440 * /// m[1][5] += 3.45); would be error (m[1][5] is not defined) 1441 * /// r out of bounds 1442 * m.incr(6,5,3.45); // m = Matrix([[11,12,13],[21,22,29.9],[31,32,33]]) 1443 * /// m[6][5] += 3.45; would be error (m[6] is not defined) 1444 */ 1445 Matrix.prototype.incr = function(r,c,val) { 1446 if (r<0 || c<0) { /*jsmLogger.warn('Matrix.incr: coefficients out of bounds');*/ return; } 1447 if (r>=this.nRows || c>=this.nCols) { return; } 1448 if (val) { this[r][c] += val; } 1449 } 1450 1451 /** Sets elements of receiver form given Array object 1452 * @param {[floats](2D)} arry array containing new receiver elements 1453 * @example 1454 * m = mat([[3,2],[1,1]]) 1455 m.fromArray([[1,2,3],[4,5,6],[7,8,9]]); // m = Matrix([[1,2,3],[4,5,6],[7,8,9]]) 1456 */ 1457 Matrix.prototype.fromArray = function(arry) { 1458 var r, c, nr = arry.length, nc = arry[0].length; 1459 for (r=0; r<nr; r++) { 1460 if (r >= this.nRows) { this[r] = {}; } 1461 for (c=0; c<nc; c++) { 1462 this[r][c] = arry[r][c]; 1463 } 1464 for (c=nc; c<this.nCols; c++) { delete this[r][c]; } 1465 } 1466 for (r=nr; r<this.nRows; r++) { delete this[r]; } 1467 this.nRows = nr; 1468 this.nCols = nc; 1469 } 1470 1471 /** Returns elements of receiver as an Array object 1472 * @returns {[floats](2D)} array containing receiver's elements 1473 * @example 1474 * m =mat([[11,12],[21,22]]); 1475 * a = m.toArray(); // a = [[11,12],[21,22]] 1476 */ 1477 Matrix.prototype.toArray = function() { 1478 var r, c, ret = [] 1479 for (r=0; r<this.nRows; r++) { 1480 ret[r] = []; 1481 for (c=0; c<this.nCols; c++) { 1482 ret[r][c] = this[r][c]; 1483 } 1484 } 1485 return ret; 1486 } 1487 1488 /** Returns sum of receiver and matrix ( ret = this + mat ) 1489 * @param {Matrix} mat matrix to be added 1490 * @returns {Matrix} sum of receiver and matrix 1491 * @example 1492 * m1 = mat([[1,2],[3,4]]); 1493 * m2 = mat([[2,5],[3,2]]); 1494 * m3 = m1.add(m2); // m3 = Matrix([[3,7],[6,6]]) 1495 */ 1496 Matrix.prototype.add = function(mat) { 1497 if (!this.isSameSizeAs(mat)) { /*jsmLogger.warn('Matrix.add: dimensions mismatched');*/ return null; } 1498 var r, c, nr = this.nRows, nc = this.nCols, ret = new Matrix(); 1499 for (r=0; r<nr; r++) { 1500 ret[r] = {}; 1501 for (c=0; c<nc; c++) { ret[r][c] = this[r][c] + mat[r][c]; } 1502 } 1503 ret.nRows = nr; 1504 ret.nCols = nc; 1505 return ret; 1506 } 1507 1508 /** Add given matrix to receiver ( this += mat ) 1509 * @param {Matrix} mat matrix to be added 1510 * @example 1511 * m1 = mat([[1,2],[3,4]]); 1512 * m2 = mat([[2,5],[3,2]]); 1513 * m1.iadd(m2); // m1 = Matrix([[3,7],[6,6]]) 1514 */ 1515 Matrix.prototype.iadd = function(mat) { 1516 if (!this.isSameSizeAs(mat)) { /*jsmLogger.warn('Matrix.iadd: dimensions mismatched');*/ return; } 1517 for (var r=0; r<this.nRows; r++) { 1518 for (var c=0; c<this.nCols; c++) { this[r][c] += mat[r][c]; } 1519 } 1520 } 1521 1522 /** Modifies receiver to become sum of two matrices ( this = mat1 + mat2 ). Receiver's size is adjusted 1523 * @param {Matrix} mat1 1st matrix of the sum 1524 * @param {Matrix} mat2 2nd matrix of the sum 1525 * @example 1526 * m = mat([[5],[9]]); 1527 * m1 = mat([[1,2],[3,4]]); 1528 * m2 = mat([[2,5],[3,2]]); 1529 * m.beSumOf(m1,m2); // m = Matrix([[3,7],[6,6]]) 1530 */ 1531 Matrix.prototype.beSumOf = function(mat1,mat2) { 1532 if (!mat1.isSameSizeAs(mat2)) { /*jsmLogger.warn('Matrix.beSumOf: dimensions mismatched');*/ return; } 1533 var r, c , nr = mat1.nRows, nc = mat1.nCols; 1534 for (r=0; r<nr; r++) { 1535 if (r >= this.nRows) { this[r] = {}; } 1536 for (c=0; c<nc; c++) { this[r][c] = mat1[r][c] + mat2[r][c]; } 1537 for (c=nc; c<this.nCols; c++) { delete this[r][c]; } 1538 } 1539 for (r=nr; r<this.nRows; r++) { delete this[r]; } 1540 this.nRows = nr; 1541 this.nCols = nc; 1542 } 1543 1544 /** Returns difference of receiver and matrix ( ret = this - mat ) 1545 * @param {Matrix} mat matrix to be added 1546 * @returns {Matrix} sum of receiver and matrix 1547 * @example 1548 * m1 = mat([[1,2],[3,4]]); 1549 * m2 = mat([[2,5],[3,2]]); 1550 * m3 = m1.sub(m2); // m3 = Matrix([[-1,-3],[0,2]]) 1551 */ 1552 Matrix.prototype.sub = function(mat) { 1553 if (!this.isSameSizeAs(mat)) { /*jsmLogger.warn('Matrix.sub: dimensions mismatched');*/ return null; } 1554 var r, c, nr = this.nRows, nc = this.nCols, ret = new Matrix(); 1555 for (r=0; r<nr; r++) { 1556 ret[r] = {}; 1557 for (c=0; c<nc; c++) { ret[r][c] = this[r][c] - mat[r][c]; } 1558 } 1559 ret.nRows = nr; 1560 ret.nCols = nc; 1561 return ret; 1562 } 1563 1564 /** Subtract given matrix to receiver ( this -= mat ) 1565 * @param {Matrix} mat matrix to be added 1566 * @example 1567 * m1 = mat([[1,2],[3,4]]); 1568 * m2 = mat([[2,5],[3,2]]); 1569 * m1.isub(m2); // m1 = Matrix([[-1,-3],[0,2]]) 1570 */ 1571 Matrix.prototype.isub = function(mat) { 1572 if (!this.isSameSizeAs(mat)) { /*jsmLogger.warn('Matrix.isub: dimensions mismatched');*/ return; } 1573 for (var r=0; r<this.nRows; r++) { 1574 for (var c=0; c<this.nCols; c++) { this[r][c] -= mat[r][c]; } 1575 } 1576 } 1577 1578 /** Modifies receiver to become difference of two matrices ( this = mat1 - mat2 ). Receiver's size is adjusted 1579 * @param {Matrix} mat1 1st matrix of the difference 1580 * @param {Matrix} mat2 2nd matrix of the difference 1581 * @example 1582 * m = mat([[5],[9]]); 1583 * m1 = mat([[1,2],[3,4]]); 1584 * m2 = mat([[2,5],[3,2]]); 1585 * m.beDifferenceOf(m1,m2); // m = Matrix([[-1,-3],[0,2]]) 1586 */ 1587 Matrix.prototype.beDifferenceOf = function(mat1,mat2) { 1588 if (!mat1.isSameSizeAs(mat2)) { /*jsmLogger.warn('Matrix.beDifferenceOf: dimensions mismatched');*/ return; } 1589 var r, c , nr = mat1.nRows, nc = mat1.nCols; 1590 for (r=0; r<nr; r++) { 1591 if (r >= this.nRows) { this[r] = {}; } 1592 for (c=0; c<nc; c++) { this[r][c] = mat1[r][c] - mat2[r][c]; } 1593 for (c=nc; c<this.nCols; c++) { delete this[r][c]; } 1594 } 1595 for (r=nr; r<this.nRows; r++) { delete this[r]; } 1596 this.nRows = nr; 1597 this.nCols = nc; 1598 } 1599 1600 /** Returns negative of receiver ( ret = -this ) 1601 * @returns {Matrix} negative of receiver 1602 * @example 1603 * m1 = mat([[1,2,3],[4,5,6]]); 1604 * m2 = m1.negated(); // m2 = Matrix([[-1,-2,-3],[-4,-5,-6]]) 1605 */ 1606 Matrix.prototype.negated = function() { 1607 return this.mulf(-1.); 1608 } 1609 1610 /** Alias for neageted(), see {@link Matrix#negated} 1611 * @function 1612 */ 1613 Matrix.prototype.neg = Matrix.prototype.negated; 1614 1615 /** Negate receiver ( ret *= -1., ret = -ret ) 1616 * @example 1617 * m1 = mat([[1,2,3],[4,5,6]]); 1618 * m1.negate(); // m1 = Matrix([[-1,-2,-3],[-4,-5,-6]]) 1619 */ 1620 Matrix.prototype.negate = function() { 1621 for (var r=0; r<this.nRows; r++) { 1622 for (var c=0; c<this.nCols; c++) { this[r][c] *= -1.; } 1623 } 1624 } 1625 1626 /** Alias for neagete(), see {@link Matrix#negate} 1627 * @function 1628 */ 1629 Matrix.prototype.ineg = Matrix.prototype.negate; 1630 1631 /** Modifies receiver to become negative of given matrix (this = -vec). Receiver's size is adjusted 1632 * @param {Matrix} mat given matrix 1633 * @example 1634 * m1 = mat([[2],[3]]); 1635 * m2 = mat([[1,2,3],[4,5,6]]); 1636 * m1.beNegativeOf(m2); // m1 = Matrix([[-1,-2,-3],[-4,-5,-6]]) 1637 */ 1638 Matrix.prototype.beNegativeOf = function(mat) { 1639 var r, c , nr = mat.nRows, nc = mat.nCols; 1640 for (r=0; r<nr; r++) { 1641 if (r >= this.nRows) { this[r] = {}; } 1642 for (c=0; c<nc; c++) { this[r][c] = -mat[r][c]; } 1643 for (c=nc; c<this.nCols; c++) { delete this[r][c]; } 1644 } 1645 for (r=nr; r<this.nRows; r++) { delete this[r]; } 1646 this.nRows = nr; 1647 this.nCols = nc; 1648 } 1649 1650 /** Returns one row of receiver 1651 * @param {int} r index of row to return 1652 * @returns {Vector} r-th row of receiver as vector 1653 * @example 1654 * m = mat([[1,2,3],[4,5,6],[7,8,9]]); 1655 * v = m.getRow(1); // v = Vector([4,5,6]) 1656 */ 1657 Matrix.prototype.getRow = function(r) { 1658 var ret = new Vector(); 1659 for (var c=0; c<this.nCols; c++) { ret[c] = this[r][c]; } 1660 ret.nElements = this.nCols; 1661 return ret; 1662 } 1663 1664 /** Returns one column of receiver 1665 * @param {int} c index of column to return 1666 * @returns {Vector} c-th column of receiver as vector 1667 * @example 1668 * m = mat([[1,2,3],[4,5,6],[7,8,9]]); 1669 * v = m.getCol(1); // v = Vector([2,5,8]) 1670 */ 1671 Matrix.prototype.getCol = function(c) { 1672 var ret = new Vector() 1673 for (var r=0; r<this.nRows; r++) { ret[r] = this[r][c]; } 1674 ret.nElements = this.nRows; 1675 return ret; 1676 } 1677 1678 /** Sets one row of receiver 1679 * @param {int} r index of row to set 1680 * @param {Vector} vec vector to be set as new r-th row 1681 * @example 1682 * mat([[1,2,3],[4,5,6],[7,8,9]]); 1683 * m.setRow(1,vec([11,12,13])); // m = Matrix([[1,2,3],[11,12,13],[7,8,9]]) 1684 */ 1685 Matrix.prototype.setRow = function(r,vec) { 1686 if (r<0 || r>=this.nRows) { /*jsmLogger.warn('Matrix.setRow: wrong row');*/ return; } 1687 var n = vec.nElements; 1688 if (this.nCols != n) { /*jsmLogger.warn('Matrix.setRow: wrong argument');*/ return; } 1689 for (var c=0; c<this.nCols; c++) { this[r][c] = vec[c]; } 1690 } 1691 1692 /** Sets one column of receiver 1693 * @param {int} c index of column to set 1694 * @param {Vector} vec vector to be set as new c-th column 1695 * @example 1696 * m = mat([[1,2,3],[4,5,6],[7,8,9]]); 1697 * m.setCol(1,vec([11,12,13])); // m = Matrix([[1,11,3],[4,12,6],[7,13,9]]) 1698 */ 1699 Matrix.prototype.setCol = function(c,vec) { 1700 if (c<0 || c>=this.nCols) { /*jsmLogger.warn('Matrix.setCol: wrong column');*/ return; } 1701 var n = vec.nElements; 1702 if (this.nRows != n) { /*jsmLogger.warn('Matrix.setCol: wrong argument');*/ return; } 1703 for (var r=0; r<this.nRows; r++) { this[r][c] = vec[r]; } 1704 } 1705 1706 /** Incerements one row of receiver 1707 * @param {int} r index of row to set 1708 * @param {Vector} vec vector to be incremented to r-th row 1709 * @example 1710 * m = mat([[1,2,3],[4,5,6],[7,8,9]]); 1711 * m.incrRow(1,vec([11,12,13])); // m = Matrix([[1,2,3],[15,17,19],[7,8,9]]) 1712 */ 1713 Matrix.prototype.incrRow = function(r,vec) { 1714 if (r<0 || r>=this.nRows) { /*jsmLogger.warn('Matrix.incrRow: wrong column');*/ return; } 1715 var n = vec.nElements; 1716 if (this.nCols != n) { /*jsmLogger.warn('Matrix.incrRow: wrong argument');*/ return; } 1717 for (var c=0; c<this.nCols; c++) { this[r][c] += vec[c]; } 1718 } 1719 1720 /** Incerements one column of receiver 1721 * @param {int} c index of column to set 1722 * @param {Vector} vec vector to be incremented to c-th column 1723 * @example 1724 * m = mat([[1,2,3],[4,5,6],[7,8,9]]); 1725 * m.incrCol(1,vec([11,12,13])); // m = Matrix([[1,13,3],[4,17,6],[7,21,9]]) 1726 */ 1727 Matrix.prototype.incrCol = function(c,vec) { 1728 if (c<0 || c>=this.nCols) { /*jsmLogger.warn('Matrix.incrCol: wrong column');*/ return; } 1729 var n = vec.nElements; 1730 if (this.nRows != n) { /*jsmLogger.warn('Matrix.incrCol: wrong argument');*/ return; } 1731 for (var r=0; r<this.nRows; r++) { this[r][c] += vec[r]; } 1732 } 1733 1734 /** Decerements one row of receiver 1735 * @param {int} r index of row to set 1736 * @param {Vector} vec vector to be decremented from r-th row 1737 * @example 1738 * m = mat([[1,2,3],[4,5,6],[7,8,9]]); 1739 * m.decrRow(1,vec([11,12,13])); // m = Matrix([[1,2,3],[-7,-7,-7],[7,8,9]]) 1740 */ 1741 Matrix.prototype.decrRow = function(r,vec) { 1742 if (r<0 || r>=this.nRows) { /*jsmLogger.warn('Matrix.decrRow: wrong column');*/ return; } 1743 var n = vec.nElements; 1744 if (this.nCols != n) { /*jsmLogger.warn('Matrix.decrRow: wrong argument');*/ return; } 1745 for (var c=0; c<this.nCols; c++) { this[r][c] -= vec[c]; } 1746 } 1747 1748 /** Decerements one column of receiver 1749 * @param {int} c index of column to set 1750 * @param {Vector} vec vector to be decremented from c-th column 1751 * @example 1752 * m = mat([[1,2,3],[4,5,6],[7,8,9]]); 1753 * m.decrCol(1,vec([11,12,13])); // m = Matrix([[1,-9,3],[4,-7,6],[7,-5,9]]) 1754 */ 1755 Matrix.prototype.decrCol = function(c,vec) { 1756 if (c<0 || c>=this.nCols) { /*jsmLogger.warn('Matrix.decrCol: wrong column');*/ return; } 1757 var n = vec.nElements; 1758 if (this.nRows != n) { /*jsmLogger.warn('Matrix.decrCol: wrong argument');*/ return; } 1759 for (var r=0; r<this.nRows; r++) { this[r][c] -= vec[r]; } 1760 } 1761 1762 /** Swaps two rows of receiver 1763 * @param {int} r1 index of first row to swap 1764 * @param {int} r2 index of second row to swap 1765 * @example 1766 * m = mat([[11,12,13],[21,22,23],[31,32,33]]); 1767 * m.swapRows(0,2); // m = Matrix([[31,32,33],[21,22,23],[11,12,13]]) 1768 */ 1769 Matrix.prototype.swapRows = function(r1,r2) { 1770 if (r1==r2) { return; } 1771 if (r1<0 || r1>=this.nRows || r2<0 || r2>=this.nRows) { /*jsmLogger.warn('Matrix.swapRows: wrong coefficients');*/ return; } 1772 var temp = this[r1]; 1773 this[r1] = this[r2]; 1774 this[r2] = temp; 1775 } 1776 1777 /** Swaps two columns of receiver 1778 * @param {int} c1 index of first row to swap 1779 * @param {int} c2 index of second row to swap 1780 * @example 1781 * m = mat([[11,12,13],[21,22,23],[31,32,33]]); 1782 * m.swapCols(0,2); // m = Matrix([[13,12,11],[23,22,21],[33,32,31]]) 1783 */ 1784 Matrix.prototype.swapCols = function(c1,c2) { 1785 if (c1==c2) { return; } 1786 if (c1<0 || c1>=this.nCols || c2<0 || c2>=this.nCols) { /*jsmLogger.warn('Matrix.swapCols: wrong coefficients');*/ return; } 1787 var temp; 1788 for (var r=0; r<this.nRows; r++) { 1789 temp = this[r][c1]; 1790 this[r][c1] = this[r][c2]; 1791 this[r][c2] = temp; 1792 } 1793 } 1794 1795 /** Permute rows of receiver according to given coefficients 1796 * @param {[ints]|Vector} coeffs coefficients of row permutation 1797 * @param {bool} [backward=false] if false, receiver's rows is permutated to given coefficients (forward). If true, from given coefficients (backward) 1798 * @example 1799 * m = mat([[11,12,13],[21,22,23],[31,32,33]]); 1800 * m.permuteRows([1,2,0]); // m = Matrix([[21,22,23],[31,32,33],[11,12,13]]) 1801 * m.permuteRows([1,2,0],true); // m = mat([[11,12,13],[21,22,23],[31,32,33]]) 1802 */ 1803 Matrix.prototype.permuteRows = function(coeffs,backward) { 1804 var n, r, coeffs; 1805 if (coeffs.nElements==undefined) { // coeffs == Array 1806 n = coeffs.length; 1807 coeffs = coeffs.slice(); 1808 } else { // coeffs == Vector 1809 n = coeffs.nElements; 1810 coeffs = coeffs.toArray(); 1811 } 1812 if (n != this.nRows) { /*jsmLogger.warn('Matrix.permuteRows: wrong argument size');*/ return; } 1813 for (var r=0; r<n; r++) { if (r<0 || r>=this.nRows) { /*jsmLogger.warn('Matrix.permuteRows: wrong argument values');*/ return; } } 1814 var backward = backward || false; 1815 var rr; 1816 if (backward) { 1817 for (r=0; r<n; r++) { 1818 rr = coeffs[r]; 1819 while (rr!=r) { 1820 this.swapRows(r,rr); 1821 coeffs[r] = coeffs[rr]; 1822 coeffs[rr] = rr; 1823 rr = coeffs[r]; 1824 } 1825 } 1826 } else { 1827 for (r=n-1; r>0; r--) { 1828 rr = coeffs[r]; 1829 this.swapRows(r,rr); 1830 coeffs[coeffs.indexOf(r)] = rr; 1831 } 1832 } 1833 return; 1834 } 1835 1836 1837 /** Returns copy of receiver with rows permutated according to given coefficients 1838 * @param {[ints]|Vector} coeffs coefficients of permutation. 1839 * @param {bool} [backward=false] if false, receiver is permutated to given coefficients (forward). If true, from given coefficients (backward) 1840 * @returns {Matrix} copy of receiver with permutated rows 1841 * @example 1842 * m1 = mat([[11,12,13],[21,22,23],[31,32,33]]); 1843 * m2 = m1.rowPermutation([1,2,0]); // m2 = Matrix([[21,22,23],[31,32,33],[11,12,13]]) 1844 * m3 = m1.rowPermutation([1,2,0],true); // m3 = mat([[31,32,33],[11,12,13],[21,22,23]]) 1845 */ 1846 Matrix.prototype.rowPermutation = function(coeffs,backward) { 1847 var r, c, n = coeffs.nElements==undefined? coeffs.length : coeffs.nElements; 1848 if (n != this.nRows) { /*jsmLogger.warn('Matrix.rowPermutation: wrong argument size');*/ return null; } 1849 for (var r=0; r<n; r++) { if (r<0 || r>=this.nRows) { /*jsmLogger.warn('Matrix.rowPermutation: wrong argument values');*/ return; } } 1850 var ret = new Matrix(); 1851 if (backward) { 1852 for (r=0; r<this.nRows; r++) { ret[r] = {}; } 1853 for (r=0; r<this.nRows; r++) { 1854 for (c=0; c<this.nCols; c++) { 1855 ret[coeffs[r]][c] = this[r][c]; 1856 } 1857 } 1858 } else { 1859 for (r=0; r<this.nRows; r++) { 1860 ret[r] = {}; 1861 for (c=0; c<this.nCols; c++) { 1862 ret[r][c] = this[coeffs[r]][c]; 1863 } 1864 } 1865 } 1866 ret.nRows = this.nRows; 1867 ret.nCols = this.nCols; 1868 return ret; 1869 } 1870 1871 /** Returns receiver multiplied by float f ( ret = this * f ) 1872 * @param {float} f float multiplier 1873 * @returns {Matrix} copy of receiver multiplied by f 1874 * @example 1875 * m1 = mat([[1,2,3],[4,5,6]]); 1876 * m2 = m1.mulf(3); // m2 = Matrix([[3,6,9],[12,15,18]]) 1877 */ 1878 Matrix.prototype.mulf = function(f) { 1879 var ret = new Matrix(), r, c, nr = this.nRows, nc = this.nCols; 1880 for (r=0; r<nr; r++) { 1881 ret[r] = {}; 1882 for (c=0; c<nc; c++) { 1883 ret[r][c] = f*this[r][c]; 1884 } 1885 } 1886 ret.nRows = nr; 1887 ret.nCols = nc; 1888 return ret; 1889 } 1890 1891 /** Multiply receiver by float f ( this *= f ) 1892 * @param {float} f float multiplier 1893 * @example 1894 * m = mat([[1,2,3],[4,5,6]]); 1895 * m.imulf(3); // m = Matrix([[3,6,9],[12,15,18]]) 1896 */ 1897 Matrix.prototype.imulf = function(f) { 1898 for (var r=0; r<this.nRows; r++) { 1899 for (var c=0; c<this.nCols; c++) { this[r][c] *= f; } 1900 } 1901 } 1902 1903 /** Returns product of receiver and given vector ( ret = this * vec ) 1904 * @param {Vector} vec vector to multiply 1905 * @returns {Vector} copy of receiver multiplied by vec 1906 * @example 1907 * m = mat([[11,12,13],[21,22,23],[31,32,33]]); 1908 * v1 = vec([1,2,3]); 1909 * v2 = m.mulv(v1); // v2 = Vector([74,134,194]) 1910 */ 1911 Matrix.prototype.mulv = function(vec) { 1912 if (!this.canMultiplyVec(vec)) { /*jsmLogger.warn('Matrix.mulv: dimensions mismatched');*/ return null; } 1913 var ret = new Vector(); 1914 var temp; 1915 for (var r=0; r<this.nRows; r++) { 1916 temp = 0.; 1917 for (var c=0; c<this.nCols; c++) { 1918 temp += this[r][c]*vec[c] 1919 } 1920 ret[r] = temp; 1921 } 1922 ret.nElements = this.nRows 1923 return ret; 1924 } 1925 1926 /** Returns product of receiver and given matrix ( ret = this * mat ) 1927 * @param {Matrix} mat matrix to multiply 1928 * @returns {Matrix} copy of receiver multiplied by mat 1929 * @example 1930 * m1 = mat([[11,12],[21,22]]); 1931 * m2 = mat([[1,2],[3,4]]); 1932 * m3 = m1.mulm(m2); // m3 = Matrix([[47,70],[87,130]]) 1933 */ 1934 Matrix.prototype.mulm = function(mat) { 1935 if (!this.canMultiplyMat(mat)) { /*jsmLogger.warn('Matrix.mulm: dimensions mismatched');*/ return null; } 1936 var ret = new Matrix(), r, c, k; 1937 var temp; 1938 for (r=0; r<this.nRows; r++) { 1939 ret[r] = {}; 1940 for (c=0; c<mat.nCols; c++) { 1941 temp = 0.; 1942 for (k=0; k<this.nCols; k++) { 1943 temp += this[r][k]*mat[k][c]; 1944 } 1945 ret[r][c] = temp; 1946 } 1947 } 1948 ret.nRows = this.nRows; 1949 ret.nCols = mat.nCols; 1950 return ret; 1951 } 1952 1953 /** Returns product of receiver and given multiplier (Matrix, Vector or float) 1954 * @param {Matrix|Vector|float} what matrix, vector or float to multiply 1955 * @returns {Matrix|Vector} copy of receiver multiplied by what 1956 * @example 1957 * m1 = mat([[1,2,3],[4,5,6]]); 1958 * m2 = m1.mul(3); // m2 = Matrix([[3,6,9],[12,15,18]]) 1959 * // 1960 * m3 = mat([[11,12,13],[21,22,23],[31,32,33]]); 1961 * v1 = vec([1,2,3]); 1962 * v2 = m3.mul(v1) // v2 = Vector([74,134,194]); 1963 * // 1964 * m4 = mat([[11,12],[21,22]]); 1965 * m5 = mat([[1,2],[3,4]]); 1966 * m6 = m4.mul(m5); // m6 = Matrix([[47,70],[87,130]]) 1967 */ 1968 Matrix.prototype.mul = function(what) { 1969 if (what instanceof Vector) { return this.mulv(what); } 1970 if (what instanceof Matrix) { return this.mulm(what); } 1971 if ((typeof what).toLowerCase() == 'number') { return this.mulf(what); } 1972 /*jsmLogger.warn('Matrix.mul: wrong argument');*/ 1973 return null; 1974 } 1975 1976 /** Alias for mul(), see {@link Matrix#mul} 1977 * @function 1978 */ 1979 Matrix.prototype.x = Matrix.prototype.mul; 1980 1981 /** Modifies receiver to become dyadic product of two vectors (this = vec1*vec2^T ). Receiver's size is adjusted 1982 * @param {Vector} vec1 1st vector of product 1983 * @param {Vector} vec2 2nd vector of product 1984 * @example 1985 * m = mat([[4],[5]]); 1986 * v1 = vec([1,2,3]); 1987 * v2 = vec([4,5,6]); 1988 * m.beDyadicProductOf(v1,v2); // m = Matrix([[4,5,6],[8,10,12],[12,15,18]]) 1989 */ 1990 Matrix.prototype.beDyadicProductOf = function(vec1,vec2) { 1991 var r, c, nr = vec1.nElements, nc = vec2.nElements; 1992 for (r=0; r<nr; r++) { 1993 if (r >= this.nRows) { this[r] = {}; } 1994 for (c=0; c<nc; c++) { this[r][c] = vec1[r]*vec2[c]; } 1995 for (c=nc; c<this.nCols; c++) { delete this[r][c]; } 1996 } 1997 for (r=nr; r<this.nRows; r++) { delete this[r]; } 1998 this.nRows = nr; 1999 this.nCols = nc; 2000 } 2001 2002 /** Modifies receiver to become product of two matrices (this = mat1*mat2 ). Receiver's size is adjusted 2003 * @param {Matrix} mat1 1st matrix of product 2004 * @param {Matrix} mat2 2nd matrix of product 2005 * @example 2006 * m1 = mat([[1],[2]]); 2007 * m2 = mat([[11,12],[21,22]]); 2008 * m3 = mat([[1,2],[3,4]]); 2009 * m1.beProductOf(m2,m3); // m1 = Matrix([[47,70],[87,130]]) 2010 */ 2011 Matrix.prototype.beProductOf = function(mat1,mat2) { 2012 if (!mat1.canMultiplyMat(mat2)) { /*jsmLogger.warn('Matrix.beProductOf: dimensions mismatched');*/ return; } 2013 var r, c, k, temp, nr = mat1.nRows; nc = mat2.nCols; 2014 for (r=0; r<nr; r++) { 2015 if (r >= this.nRows) { this[r] = {}; } 2016 for (c=0; c<nc; c++) { 2017 temp = 0.; 2018 for (k=0; k<mat1.nCols; k++) { temp += mat1[r][k]*mat2[k][c]; } 2019 this[r][c] = temp; 2020 } 2021 for (c=nc; c<this.nCols; c++) { delete this[r][c]; } 2022 } 2023 for (r=nr; r<this.nRows; r++) { delete this[r]; } 2024 this.nRows = nr; 2025 this.nCols = nc; 2026 } 2027 2028 /** Modifies receiver to become product of two matrices (this = mat1^T*mat2 ) 2029 * @param {Vector} mat1 1st matrix of product 2030 * @param {Vector} mat2 2nd matrix of product 2031 * @example 2032 * m1 = mat([[1],[2]]); 2033 * m2 = mat([[11,21],[12,22]]); 2034 * m3 = mat([[1,2],[3,4]]); 2035 * m1.beTProductOf(m2,m3); // m1 = Matrix([[47,70],[87,130]]) 2036 */ 2037 Matrix.prototype.beTProductOf = function(mat1,mat2) { 2038 var r, c, k, temp, nr = mat1.nCols; nc = mat2.nCols; 2039 if (mat1.nRows != mat2.nRows) { /*jsmLogger.warn('Matrix.beTProductOf: dimensions mismatched');*/ return; } 2040 for (r=0; r<nr; r++) { 2041 if (r >= this.nRows) { this[r] = {}; } 2042 for (c=0; c<nc; c++) { 2043 temp = 0.; 2044 for (k=0; k<mat1.nRows; k++) { temp += mat1[k][r]*mat2[k][c]; } 2045 this[r][c] = temp; 2046 } 2047 for (c=nc; c<this.nCols; c++) { delete this[r][c]; } 2048 } 2049 for (r=nr; r<this.nRows; r++) { delete this[r]; } 2050 this.nRows = nr; 2051 this.nCols = nc; 2052 } 2053 2054 /** Modifies receiver to become product of two matrices (this = mat1*mat2^T ) 2055 * @param {Vector} mat1 1st matrix of product 2056 * @param {Vector} mat2 2nd matrix of product 2057 * @example 2058 * m1 = mat([[1],[2]]); 2059 * m2 = mat([[11,12],[21,22]]); 2060 * m3 = mat([[1,3],[2,4]]); 2061 * m1.beProductTOf(m2,m3); // m1 = Matrix([[47,70],[87,130]]) 2062 */ 2063 Matrix.prototype.beProductTOf = function(mat1,mat2) { 2064 var r, c, k, temp, nr = mat1.nRows; nc = mat2.nRows; 2065 if (mat1.nCols != mat2.nCols) { /*jsmLogger.warn('Matrix.beProductTOf: dimensions mismatched');*/ return; } 2066 for (r=0; r<nr; r++) { 2067 if (r >= this.nRows) { this[r] = {}; } 2068 for (c=0; c<nc; c++) { 2069 temp = 0.; 2070 for (k=0; k<mat1.nCols; k++) { temp += mat1[r][k]*mat2[c][k]; } 2071 this[r][c] = temp; 2072 } 2073 for (c=nc; c<this.nCols; c++) { delete this[r][c]; } 2074 } 2075 for (r=nr; r<this.nRows; r++) { delete this[r]; } 2076 this.nRows = nr; 2077 this.nCols = nc; 2078 } 2079 2080 /** Modifies receiver to become product of two matrices (this = mat1^T*mat2^T ) 2081 * @param {Vector} mat1 1st matrix of product 2082 * @param {Vector} mat2 2nd matrix of product 2083 * @example 2084 * m1 = mat([[1],[2]]); 2085 * m2 = mat([[11,21],[12,22]]); 2086 * m3 = mat([[1,3],[2,4]]); 2087 * m1.beTProductTOf(m2,m3); // m1 = Matrix([[47,70],[87,130]]) 2088 */ 2089 Matrix.prototype.beTProductTOf = function(mat1,mat2) { 2090 var r, c, k, temp, nr = mat1.nRows; nc = mat2.nCols; 2091 if (mat1.nCols != mat2.nCols) { /*jsmLogger.warn('Matrix.beTProductTOf: dimensions mismatched');*/ return; } 2092 for (r=0; r<nr; r++) { 2093 if (r >= this.nRows) { this[r] = {}; } 2094 for (c=0; c<nc; c++) { 2095 temp = 0.; 2096 for (k=0; k<mat1.nRows; k++) { temp += mat1[k][r]*mat2[c][k]; } 2097 this[r][c] = temp; 2098 } 2099 for (c=nc; c<this.nCols; c++) { delete this[r][c]; } 2100 } 2101 for (r=nr; r<this.nRows; r++) { delete this[r]; } 2102 this.nRows = nr; 2103 this.nCols = nc; 2104 } 2105 2106 /** Returns transposition of receiver ( ret = this^T ) 2107 * @returns {Matrix} transposed copy of receiver 2108 * @example 2109 * m1 = mat([[1,2,3],[4,5,6]]); 2110 * m2 = m1.transposed(); // m2 = Matrix([[1,4],[2,5],[3,6]]) 2111 */ 2112 Matrix.prototype.transposed = function() { 2113 var ret = new Matrix(this.nCols,this.nRows); 2114 for (var r=0; r<this.nCols; r++) { 2115 ret[r] = {}; 2116 for (var c=0; c<this.nRows; c++) { 2117 ret[r][c] = this[c][r]; 2118 } 2119 } 2120 return ret; 2121 } 2122 2123 /** Alias for transposed(), see {@link Matrix#transposed} 2124 * @function 2125 */ 2126 Matrix.prototype.T = Matrix.prototype.transposed; 2127 2128 /** Modifies receiver to become transposition of itself ( this = this^T ) 2129 * @example 2130 * m = mat([[1,2,3],[4,5,6]]); 2131 * m.transpose(); // m = Matrix([[1,4],[2,5],[3,6]]) 2132 * m.transpose(); // m = Matrix([[1,2,3],[4,5,6]]) 2133 */ 2134 Matrix.prototype.transpose = function() { 2135 var temp, r, c, nr = this.nRows, nc = this.nCols, n=Math.min(nr,nc); 2136 for (r=0; r<n; r++) { 2137 for (c=r+1; c<n; c++) { 2138 temp = this[r][c]; 2139 this[r][c] = this[c][r]; 2140 this[c][r] = temp; 2141 } 2142 } 2143 if (nr>nc) { 2144 for (r=nc; r<nr; r++) { 2145 for (c=0; c<nc; c++) { 2146 this[c][r] = this[r][c]; 2147 } 2148 delete this[r]; 2149 } 2150 } else if (nr<nc) { 2151 for (c=nr; c<nc; c++) { 2152 this[c] = {}; 2153 for (r=0; r<nr; r++) { 2154 this[c][r] = this[r][c]; 2155 delete this[r][c]; 2156 } 2157 } 2158 } 2159 this.nRows = nc; 2160 this.nCols = nr; 2161 } 2162 2163 /** Modifies receiver to become transposition of given matrix (this = mat^T ). Receiver's size is adjusted 2164 * @param {Matrix} mat matrix of transposition 2165 * @example 2166 * m1 = mat([[4],[5]]); 2167 * m2 = mat([[1,2,3],[4,5,6]]); 2168 * m1.beTranspositionOf(m2) // m1 = mat([[1,4],[2,5],[3,6]]) 2169 */ 2170 Matrix.prototype.beTranspositionOf = function(mat) { 2171 var r, c, nr = mat.nCols, nc = mat.nRows; 2172 for (r=0; r<nr; r++) { 2173 if (r >= this.nRows) { this[r] = {}; } 2174 for (c=0; c<nc; c++) { this[r][c] = mat[c][r]; } 2175 for (c=nc; c<this.nCols; c++) { delete this[r][c]; } 2176 } 2177 for (r=nr; r<this.nRows; r++) { delete this[r]; } 2178 this.nRows = nr; 2179 this.nCols = nc; 2180 } 2181 2182 /** Symmetrize receiver ( this = .5*(this+this^T) ). Receiver must be square 2183 * @example 2184 * m = mat([[1,2,3],[0,-1,5],[-1,1,7]]) 2185 * m.symmetrize(); // m = Matrix([[1,1,1],[1,-1,3],[1,3,7]]) 2186 */ 2187 Matrix.prototype.symmetrize = function() { 2188 if (!this.isSquare()) { /*jsmLogger.warn('Matrix.symmetrize: matrix is not square');*/ return; } 2189 var r, c, temp, n = this.nRows; 2190 for (r=0; r<n-1; r++) { 2191 for (c=r+1; c<n; c++) { 2192 temp = .5*(this[r][c]+this[c][r]); 2193 this[r][c] = temp; 2194 this[c][r] = temp; 2195 } 2196 } 2197 } 2198 2199 /** Returns symmetric part of receiver ( ret = .5*(this+this^T) ). Receiver must be square 2200 * @example 2201 * m1 = mat([[1,2,3],[0,-1,5],[-1,1,7]]) 2202 * m2 = m1.symmetrized(); // m2 = Matrix([[1,1,1],[1,-1,3],[1,3,7]]) 2203 */ 2204 Matrix.prototype.symmetrized = function() { 2205 if (!this.isSquare()) { /*jsmLogger.warn('Matrix.symmetrized: matrix is not square');*/ return null; } 2206 ret = new Matrix(); 2207 var r, c, temp, n = this.nRows; 2208 for (r=0; r<n; r++) { ret[r] = {}; } 2209 for (r=0; r<n; r++) { ret[r][r] = this[r][r]; } 2210 for (r=0; r<n-1; r++) { 2211 for (c=r+1; c<n; c++) { 2212 temp = .5*(this[r][c]+this[c][r]); 2213 ret[r][c] = temp; 2214 ret[c][r] = temp; 2215 } 2216 } 2217 ret.nRows = n; 2218 ret.nCols = n; 2219 return ret; 2220 } 2221 2222 /** Alias for symmetrized(), see {@link Matrix#symmetrized} 2223 * @function 2224 */ 2225 Matrix.prototype.giveSymmetricPart = Matrix.prototype.symmetrized; 2226 2227 /** Modifies receiver to become symmetric part of given matrix (this = .5*(mat+mat^T) ). mat has to be square 2228 * @param {Matrix} mat given matrix. Receiver's size is adjusted 2229 * @example 2230 * m1 = mat([[1],[3]]); 2231 * m2 = mat([[1,2,3],[0,-1,5],[-1,1,7]]) 2232 * m1.beSymmetricPartOf(m2); // m1 = Matrix([[1,1,1],[1,-1,3],[1,3,7]]) 2233 */ 2234 Matrix.prototype.beSymmetricPartOf = function(mat) { 2235 if (!mat.isSquare()) { /*jsmLogger.warn('Matrix.beSymmetricPartOf: argument matrix is not square');*/ return; } 2236 var r, c, temp, n = mat.nRows; 2237 for (r=0; r<n; r++) { 2238 if (r>=this.nRows) { this[r] = {}; } 2239 this[r][r] = mat[r][r]; 2240 } 2241 for (r=0; r<n-1; r++) { 2242 for (c=r+1; c<n; c++) { 2243 temp = .5*(mat[r][c]+mat[c][r]); 2244 this[r][c] = temp; 2245 this[c][r] = temp; 2246 } 2247 for (c=n; c<this.nCols; c++) { delete this[r][c]; } 2248 } 2249 for (r=n; r<this.nRows; r++) { delete this[r]; } 2250 this.nRows = n; 2251 this.nCols = n; 2252 } 2253 2254 /** Anti-symmetrize receiver ( this = this - .5*(this+this^T) ). Receiver must be square 2255 * @example 2256 * m = mat([[1,2,3],[0,-1,5],[-1,1,7]]) 2257 * m.antiSymmetrize(); // m = Matrix([[0,1,2],[-1,0,2],[-2,-2,0]]) 2258 */ 2259 Matrix.prototype.antiSymmetrize = function() { 2260 if (!this.isSquare()) { /*jsmLogger.warn('Matrix.antiSymmetrize: matrix is not square');*/ return; } 2261 var r, c, temp, n = this.nRows; 2262 for (r=0; r<n; r++) { this[r][r] = 0.; } 2263 for (r=0; r<n; r++) { 2264 for (c=r+1; c<n; c++) { 2265 temp = .5*(this[r][c]+this[c][r]); 2266 this[r][c] -= temp; 2267 this[c][r] -= temp; 2268 } 2269 } 2270 } 2271 2272 /** Returns anti-symetric part of receiver ( ret = this - .5*(this+this^T) ). Receiver must be square 2273 * @example 2274 * m1 = mat([[1,2,3],[0,-1,5],[-1,1,7]]) 2275 * m2 = m1.antiSymmetrized(); // m2 = Matrix([[0,1,2],[-1,0,3],[-2,0,0]]) 2276 */ 2277 Matrix.prototype.antiSymmetrized = function() { 2278 if (!this.isSquare()) { /*jsmLogger.warn('Matrix.antiSymmetrized: matrix is not square');*/ return null; } 2279 ret = new Matrix(); 2280 var r, c, temp, n = this.nRows; 2281 for (r=0; r<n; r++) { ret[r] = {}; } 2282 for (r=0; r<n; r++) { ret[r][r] = 0.; } 2283 for (r=0; r<n-1; r++) { 2284 for (c=r+1; c<n; c++) { 2285 temp = .5*(this[r][c]+this[c][r]); 2286 ret[r][c] = this[r][c] - temp; 2287 ret[c][r] = this[c][r] - temp; 2288 } 2289 } 2290 ret.nRows = n; 2291 ret.nCols = n; 2292 return ret; 2293 } 2294 2295 /** Alias for symetrized(), see {@link Matrix#antiSymmetrized} 2296 * @function 2297 */ 2298 Matrix.prototype.giveAntiSymmetricPart = Matrix.prototype.antiSymmetrized; 2299 2300 /** Modifies receiver to become anti-symmetric part of given matrix (this = mat - .5*(mat+mat^T) ). mat has to be square 2301 * @param {Matrix} mat given matrix 2302 * @example 2303 * m1 = mat([[1],[3]]); 2304 * m2 = mat([[1,2,3],[0,-1,5],[-1,1,7]]) 2305 * m1.beAntiSymmetricPartOf(m2); // m1 = Matrix([[0,1,2],[-1,0,3],[-2,0,0]]) 2306 */ 2307 Matrix.prototype.beAntiSymmetricPartOf = function(mat) { 2308 if (!mat.isSquare()) { /*jsmLogger.warn('Matrix.beAntiSymmetricPartOf: argument matrix is not square');*/ return; } 2309 var r, c, temp, n = mat.nRows; 2310 for (r=0; r<n; r++) { 2311 if (r>=this.nRows) { this[r] = {}; } 2312 this[r][r] = 0.; 2313 } 2314 for (r=0; r<n; r++) { 2315 for (c=r+1; c<n; c++) { 2316 temp = .5*(mat[r][c]+mat[c][r]); 2317 this[r][c] = mat[r][c] - temp; 2318 this[c][r] = mat[c][r] - temp; 2319 } 2320 for (c=n; c<this.nCols; c++) { delete this[r][c]; } 2321 } 2322 for (r=n; r<this.nRows; r++) { delete this[r]; } 2323 this.nRows = n; 2324 this.nCols = n; 2325 } 2326 2327 /** Returns submatrix of receiver ( ret = this[rows][cols] ) 2328 * @param {[floats]|Vector} rows array containing rows coefficients of desired submatrix 2329 * @param {[floats]|Vector} cols array containing columns coefficients of desired submatrix 2330 * @returns {Matrix} desired submatrix 2331 * @example 2332 * m1 = mat([[11,12,13,14],[21,22,23,24],[31,32,33,34],[41,42,43,44]]); 2333 * m2 = m1.getSubMatrix([1,2],[0,2]); // m2 = Matrix([[21,23],[31,33]]) 2334 */ 2335 Matrix.prototype.getSubMatrix = function(rows,cols) { 2336 var nr = rows.nElements==undefined? rows.length : rows.nElements; 2337 var nc = cols.nElements==undefined? cols.length : cols.nElements; 2338 if (nr>this.nRows || nc>this.nCols) { /*jsmLogger.warn('Matrix.getSubMatrix: wrong argument dimensions');*/ return null; } 2339 var r,c,rr,cc; 2340 var ret = new Matrix(); 2341 for (r=0; r<nr; r++) { 2342 ret[r] = {}; 2343 for (c=0; c<nc; c++) { 2344 rr = rows[r]; cc = cols[c]; 2345 if (rr<0 || rr>=this.nRows) { /*jsmLogger.warn('Matrix.getSubMatrix: wrong argument values');*/ return null; } 2346 if (cc<0 || cc>=this.nCols) { /*jsmLogger.warn('Matrix.getSubMatrix: wrong argument values');*/ return null; } 2347 ret[r][c] = this[rr][cc]; 2348 } 2349 } 2350 ret.nRows = nr; 2351 ret.nCols = nc; 2352 return ret; 2353 } 2354 2355 /** Alias for getSubMatrix, see {@link Matrix#getSubMatrix} 2356 * @function 2357 */ 2358 Matrix.prototype.getsm = Matrix.prototype.getSubMatrix; 2359 2360 /** Sets receiver to be submatrix of given matrix ( this = mat[rows][cols] ) 2361 * @param {Matrix} mat matrix to get submatrix from 2362 * @param {[floats]|Vector} rows array containing rows coefficients of desired submatrix 2363 * @param {[floats]|Vector} cols array containing columns coefficients of desired submatrix 2364 * @returns {Matrix} desired submatrix 2365 * @example 2366 * m1 = mat([[11,12,13,14],[21,22,23,24],[31,32,33,34],[41,42,43,44]]); 2367 * m2 = mat([[1],[2]]); 2368 * m2.beSubMatrixOf(m1,[1,2],[0,2]); // m2 = Matrix([[21,23],[31,33]]) 2369 */ 2370 Matrix.prototype.beSubMatrixOf = function(mat,rows,cols) { 2371 var nr = rows.nElements==undefined? rows.length : rows.nElements; 2372 var nc = cols.nElements==undefined? cols.length : cols.nElements; 2373 if (nr>mat.nRows || nc>mat.nCols) { /*jsmLogger.warn('Matrix.beSubMatrixOf: wrong argument dimensions');*/ return null; } 2374 var r,c; 2375 for (r=0; r<n; r++) { if (rows[r]<0 || rows[r]>=mat.nRows) { /*jsmLogger.warn('Matrix.beSubMatrixOf: wrong argument values');*/ return; } } 2376 for (c=0; c<n; c++) { if (cols[c]<0 || cols[c]>=mat.nCols) { /*jsmLogger.warn('Matrix.beSubMatrixOf: wrong argument values');*/ return; } } 2377 for (r=0; r<nr; r++) { 2378 this[r] = {}; 2379 for (c=0; c<nc; c++) { this[r][c] = mat[rows[r]][cols[c]]; } 2380 for (c=nc; c<this.nCols; c++) { delete this[r][c]; } 2381 } 2382 for (r=nr; r<this.nRows; r++) { delete this[r]; } 2383 this.nRows = nr; 2384 this.nCols = nc; 2385 } 2386 2387 /** Sets submatrix at defined positions of receiver ( this[rows][cols] = mat ) 2388 * @param {[floats]|Vector} rows array containing rows coefficients to be set 2389 * @param {[floats]|Vector} cols array containing columns coefficients to be set 2390 * @param {Matrix} mat matrix to be set on desired positions 2391 * @example 2392 * m = mat([[11,12,13,14],[21,22,23,24],[31,32,33,34],[41,42,43,44]]); 2393 * m.setSubMatrix([1,2],[0,2],mat([[66,77],[88,99]])); 2394 * // m = Matrix([[11,12,13,14],[66,22,77,24],[88,32,99,34],[41,42,43,44]]) 2395 */ 2396 Matrix.prototype.setSubMatrix = function(rows,cols,mat) { 2397 var nr = rows.nElements==undefined? rows.length : rows.nElements; 2398 var nc = cols.nElements==undefined? cols.length : cols.nElements; 2399 var mnr = mat.nRows, mnc=mat.nCols; 2400 if (mnr != nr || mnc != nc) { /*jsmLogger.warn('Matrix.setSubMatrix: wrong argument dimensions');*/ return; } 2401 if (nr>this.nRows || nc>this.nCols) { /*jsmLogger.warn('Matrix.setSubMatrix: wrong argument dimensions');*/ return; } 2402 var r,c; 2403 for (r=0; r<n; r++) { if (rows[r]<0 || rows[r]>=this.nRows) { /*jsmLogger.warn('Matrix.setSubMatrix: wrong argument values');*/ return; } } 2404 for (c=0; c<n; c++) { if (cols[c]<0 || cols[c]>=this.nCols) { /*jsmLogger.warn('Matrix.setSubMatrix: wrong argument values');*/ return; } } 2405 for (r=0; r<nr; r++) { 2406 for (c=0; c<nc; c++) { this[rows[r]][cols[c]] = mat[r][c]; } 2407 } 2408 } 2409 2410 /** Alias for setSubMatrix, see {@link Matrix#setSubMatrix} 2411 * @function 2412 */ 2413 Matrix.prototype.setsm = Matrix.prototype.setSubMatrix; 2414 2415 /** Increment submatrix at defined positions of receiver ( this[rows][cols] += mat ) 2416 * @param {[floats]|Vector} rows array containing rows coefficients to be incrementes 2417 * @param {[floats]|Vector} cols array containing columns coefficients to be incremented 2418 * @param {Matrix} mat matrix to be incremented on desired positions 2419 * @example 2420 * m = mat([[11,12,13,14],[21,22,23,24],[31,32,33,34],[41,42,43,44]]); 2421 * m.incrSubMatrix([1,2],[0,2],mat([[66,77],[88,99]])); 2422 * // m = Matrix([11,12,13,14],[87,22,100,24],[119,32,132,34],[41,42,43,44]]) 2423 */ 2424 Matrix.prototype.incrSubMatrix = function(rows,cols,mat) { 2425 var nr = rows.nElements==undefined? rows.length : rows.nElements; 2426 var nc = cols.nElements==undefined? cols.length : cols.nElements; 2427 var mnr = mat.nRows, mnc=mat.nCols; 2428 if (mnr != nr || mnc != nc) { /*jsmLogger.warn('Matrix.incrSubMatrix: wrong argument dimensions');*/ return; } 2429 if (nr>this.nRows || nc>this.nCols) { /*jsmLogger.warn('Matrix.incrSubMatrix: wrong argument dimensions');*/ return; } 2430 var r,c; 2431 for (r=0; r<n; r++) { if (rows[r]<0 || rows[r]>=this.nRows) { /*jsmLogger.warn('Matrix.incrSubMatrix: wrong argument values');*/ return; } } 2432 for (c=0; c<n; c++) { if (cols[c]<0 || cols[c]>=this.nCols) { /*jsmLogger.warn('Matrix.incrSubMatrix: wrong argument values');*/ return; } } 2433 for (r=0; r<nr; r++) { 2434 for (c=0; c<nc; c++) { this[rows[r]][cols[c]] += mat[r][c]; } 2435 } 2436 } 2437 2438 /** Alias for incrSubMatrix, see {@link Matrix#incrSubMatrix} 2439 * @function 2440 */ 2441 Matrix.prototype.incrsm = Matrix.prototype.incrSubMatrix; 2442 2443 /** Decrement submatrix at defined positions of receiver ( this[rows][cols] -= mat ) 2444 * @param {[floats]|Vector} rows array containing rows coefficients to be decrementes 2445 * @param {[floats]|Vector} cols array containing columns coefficients to be decremented 2446 * @param {Matrix} mat matrix to be decremented on desired positions 2447 * @example 2448 * m = mat([[11,12,13,14],[21,22,23,24],[31,32,33,34],[41,42,43,44]]); 2449 * m.decrSubMatrix([1,2],[0,2],mat([[66,77],[88,99]])); 2450 * // m = Matrix([11,12,13,14],[-45,22,-54,24],[-57,32,-66,34],[41,42,43,44]]) 2451 */ 2452 Matrix.prototype.decrSubMatrix = function(rows,cols,mat) { 2453 var nr = rows.nElements==undefined? rows.length : rows.nElements; 2454 var nc = cols.nElements==undefined? cols.length : cols.nElements; 2455 var mnr = mat.nRows, mnc=mat.nCols; 2456 if (mnr != nr || mnc != nc) { /*jsmLogger.warn('Matrix.decrSubMatrix: wrong argument dimensions');*/ return; } 2457 if (nr>this.nRows || nc>this.nCols) { /*jsmLogger.warn('Matrix.decrSubMatrix: wrong argument dimensions');*/ return; } 2458 var r,c; 2459 for (r=0; r<n; r++) { if (rows[r]<0 || rows[r]>=this.nRows) { /*jsmLogger.warn('Matrix.decrSubMatrix: wrong argument values');*/ return; } } 2460 for (c=0; c<n; c++) { if (cols[c]<0 || cols[c]>=this.nCols) { /*jsmLogger.warn('Matrix.decrSubMatrix: wrong argument values');*/ return; } } 2461 for (r=0; r<nr; r++) { 2462 for (c=0; c<nc; c++) { this[rows[r]][cols[c]] -= mat[r][c]; } 2463 } 2464 } 2465 2466 /** Alias for decrSubMatrix, see {@link Matrix#decrSubMatrix} 2467 * @function 2468 */ 2469 Matrix.prototype.decrsm = Matrix.prototype.decrSubMatrix; 2470 2471 /** assemble receiver to given matrix ( mat[rows][cols] += this ) 2472 * @param {Matrix} mat matrix where receiver is assembled 2473 * @param {[floats]|Vector} rows array containing row coefficients of mat to be incremented 2474 * @param {[floats]|Vector} cols array containing column coefficients of mat to be incremented 2475 * @example 2476 * m1 = mat([[11,12,13,14],[21,22,23,24],[31,32,33,34],[41,42,43,44]]); 2477 * m2 = mat([[66,77],[88,99]]); 2478 * m2.assemble(m1,[1,2],[0,2]); 2479 * // m1 = Matrix([11,12,13,14],[87,22,100,24],[119,32,132,34],[41,42,43,44]]) 2480 */ 2481 Matrix.prototype.assemble = function(mat,rows,cols) { 2482 var nr = rows.nElements==undefined? rows.length : rows.nElements; 2483 var nc = cols.nElements==undefined? cols.length : cols.nElements; 2484 var mnr = this.nRows, mnc = this.nCols; 2485 if (mnr != nr || mnc != nc) { /*jsmLogger.warn('Matrix.assemble: wrong argument dimensions');*/ return; } 2486 if (nr>mat.nRows || nc>mat.nCols) { /*jsmLogger.warn('Matrix.assemble: wrong argument dimensions');*/ return; } 2487 var r,c; 2488 for (r=0; r<nr; r++) { if (rows[r]<0 || rows[r]>=mat.nRows) { /*jsmLogger.warn('Matrix.assemble: wrong argument values');*/ return; } } 2489 for (c=0; c<nc; c++) { if (cols[c]<0 || cols[c]>=mat.nCols) { /*jsmLogger.warn('Matrix.assemble: wrong argument values');*/ return; } } 2490 for (r=0; r<nr; r++) { 2491 for (c=0; c<nc; c++) { mat[rows[r]][cols[c]] += this[r][c]; } 2492 } 2493 } 2494 2495 /** Returns copy of receiver 2496 * @returns {Matrix} copy of receiver 2497 * @example 2498 * m1 = mat([[11,12],[21,22]]); 2499 * m2 = m1; 2500 * m3 = m1.copied(); 2501 * m1.set(1,0,6); 2502 * // m1 = Matrix([[11,12],[6,22]]) 2503 * // m2 = Matrix([[11,12],[6,22]]) 2504 * // m3 = Matrix([[11,12],[21,22]]) 2505 */ 2506 Matrix.prototype.copied = function() { 2507 var ret = new Matrix(); 2508 for (var r=0; r<this.nRows; r++) { 2509 ret[r] = {}; 2510 for (var c=0; c<this.nCols; c++) { 2511 ret[r][c] = this[r][c]; 2512 } 2513 } 2514 ret.nRows = this.nRows; 2515 ret.nCols = this.nCols; 2516 return ret; 2517 } 2518 2519 /** Alias for copied(), see {@link Matrix#copied} 2520 * @function 2521 */ 2522 Matrix.prototype.copy = Matrix.prototype.copied; 2523 2524 /** Modifies receiver to become copy of given matrix (this = mat). Receiver's size is adjusted 2525 * @param {Matrix} mat matrix to be copied to receiver 2526 * @example 2527 * m1 = mat([[4],[5]]); 2528 * m2 = mat([[1,2,3],[4,5,6]]); 2529 * m1.beCopyOf(m2); // m1 = Matrix([[1,2,3],[4,5,6]]) 2530 */ 2531 Matrix.prototype.beCopyOf = function(mat) { 2532 var r, c, nr = mat.nRows, nc = mat.nCols; 2533 for (r=0; r<nr; r++) { 2534 if (r >= this.nRows) { this[r] = {}; } 2535 for (c=0; c<nc; c++) { this[r][c] = mat[r][c]; } 2536 for (c=nc; c<this.nCols; c++) { delete this[r][c]; } 2537 } 2538 for (r=nr; r<this.nRows; r++) { delete this[r]; } 2539 this.nRows = nr; 2540 this.nCols = nc; 2541 } 2542 2543 /** Returns vector containing receiver's diagonal elements 2544 * @returns {Vector} vector containing receiver's diagonal elements 2545 * @example 2546 * m = mat([[1,2,3],[4,5,6],[7,8,9]]); 2547 * v = m.diag(); // v = Vector([1,5,9]) 2548 */ 2549 Matrix.prototype.diagonalToVector = function() { 2550 if (!this.isSquare()) { /*jsmLogger.warn('Matrix.diagonalToVector: matrix is not square');*/ return null; } 2551 var ret = new Vector() 2552 for (var r=0; r<this.nRows; r++) { ret[r] = this[r][r]; } 2553 ret.nElements = this.nRows; 2554 return ret; 2555 } 2556 2557 /** Alias for diagonalToVector(), see {@link Matrix#diagonalToVector} 2558 * @function 2559 */ 2560 Matrix.prototype.diag = Matrix.prototype.diagonalToVector; 2561 2562 /** Returns string representation of receiver 2563 * @returns {string} string representation of receiver 2564 * @example 2565 * m = mat([[1,2,3],[4,5,6],[7,8,9]]); 2566 * s = m.toString(); // s = 'Matrix([[1,2,3],[4,5,6],[7,8,9]])' 2567 */ 2568 Matrix.prototype.toString = function() { 2569 var nr = this.nRows; 2570 var nc = this.nCols; 2571 var r,c,val; 2572 var ret = "Matrix([[ "; 2573 if (nr == 0) { return ret+"]])"; } 2574 if (nc == 0) { 2575 for (r=0; r<nr-1; r++) { 2576 ret += " [], "; 2577 } 2578 return ret + "])"; 2579 } 2580 for (r=0; r<nr-1; r++) { 2581 for (c=0; c<nc-1; c++) { 2582 val = this[r][c]; 2583 //val = (Math.abs(val) < JSM_TOL)? 0. : Math.round(val*JSM_ROUNDFACTOR)/JSM_ROUNDFACTOR; 2584 ret += val.toExponential(3)+", "; 2585 } 2586 val = this[r][nc-1]; 2587 //val = (Math.abs(val) < JSM_TOL)? 0. : Math.round(val*JSM_ROUNDFACTOR)/JSM_ROUNDFACTOR; 2588 ret += val.toExponential(3)+" ], [ "; 2589 } 2590 for (c=0; c<nc-1; c++) { 2591 val = this[nr-1][c]; 2592 //val = (Math.abs(val) < JSM_TOL)? 0. : Math.round(val*JSM_ROUNDFACTOR)/JSM_ROUNDFACTOR; 2593 ret += val.toExponential(3)+", "; 2594 } 2595 val = this[nr-1][nc-1]; 2596 //val = (Math.abs(val) < JSM_TOL)? 0. : Math.round(val*JSM_ROUNDFACTOR)/JSM_ROUNDFACTOR; 2597 ret += val.toExponential(3)+" ]])" 2598 return ret; 2599 } 2600 2601 /** Resize receiver according to given size (delete extra elements or add zero elements) 2602 * @param {int} nRows new number of rows 2603 * @param {int} nCols new number of columns 2604 * @example 2605 * m1 = mat([[11,12,13,14],[21,22,23,24],[31,32,33,34],[41,42,43,44]]); 2606 * m2 = mat([[11,12],[21,22]]); 2607 * m1.resize(3,3); // m1 = Matrix([[11,12,13],[21,22,23],[31,32,33]]) 2608 * m2.resize(3,3); // m2 = Matrix([[11,12,0],[21,22,0],[0,0,0]]) 2609 */ 2610 Matrix.prototype.resize = function(nRows,nCols) { 2611 if (this.nRows < nRows) { 2612 for (var r=this.nRows; r<nRows; r++) { 2613 this[r] = {}; 2614 for (var c=0; c<this.nCols; c++) { 2615 this[r][c] = 0.; 2616 } 2617 } 2618 this.nRows = nRows; 2619 } 2620 if (this.nRows > nRows) { 2621 for (var r=nRows; r<this.nRows; r++) { delete this[r]; } 2622 this.nRows = nRows; 2623 } 2624 if (this.nCols < nCols) { 2625 for (var r=0; r<this.nRows; r++) { 2626 for (var c=this.nCols; c<nCols; c++) { 2627 this[r][c] = 0.; 2628 } 2629 } 2630 this.nCols = nCols; 2631 } 2632 if (this.nCols > nCols) { 2633 for (var r=0; r<this.nRows; r++) { 2634 for (var c=nCols; c<this.nCols; c++) { 2635 delete this[r][c]; 2636 } 2637 } 2638 this.nCols = nCols; 2639 } 2640 } 2641 2642 /** Returns resized copy of receiver according to given size (delete extra elements or add zero elements) 2643 * @param {int} nRows new number of rows 2644 * @param {int} nCols new number of columns 2645 * @returns {Matrix} resized copy of receiver 2646 * @example 2647 * m1 = mat([[11,12,13],[21,22,23],[31,32,33]]); 2648 * m2 = m1.resized(2,4); // m2 = Matrix([[11,12,13,0],[21,22,23,0]]) 2649 * m3 = m1.resized(4,2); // m3 = Matrix([[11,12],[21,22],[31,32],[0,0]]) 2650 */ 2651 Matrix.prototype.resized = function(nRows,nCols) { 2652 var r, c, ret = new Matrix(); 2653 for (var r=0; r<nRows; r++) { ret[r] = {}; } 2654 if (nRows<=this.nRows) { 2655 if (nCols<=this.nCols) { 2656 for (r=0; r<nRows; r++) { 2657 for (c=0; c<nCols; c++) { ret[r][c] = this[r][c]; } 2658 } 2659 } else { 2660 for (r=0; r<nRows; r++) { 2661 for (c=0; c<this.nCols; c++) { ret[r][c] = this[r][c]; } 2662 for (c=this.nCols; c<nCols; c++) { ret[r][c] = 0.; } 2663 } 2664 } 2665 } else { 2666 if (nCols<=this.nCols) { 2667 for (r=0; r<this.nRows; r++) { 2668 for (c=0; c<nCols; c++) { ret[r][c] = this[r][c]; } 2669 } 2670 for (r=this.nRows; r<nRows; r++) { 2671 for (c=0; c<nCols; c++) { ret[r][c] = 0.; } 2672 } 2673 } else { 2674 for (r=0; r<this.nRows; r++) { 2675 for (c=0; c<this.nRows; c++) { ret[r][c] = this[r][c]; } 2676 for (c=this.nCols; c<nCols; c++) { ret[r][c] = 0.; } 2677 } 2678 for (r=this.nRows; r<nRows; r++) { 2679 for (c=0; c<nCols; c++) { ret[r][c] = 0.; } 2680 } 2681 } 2682 } 2683 ret.nRows = nRows; 2684 ret.nCols = nCols; 2685 return ret; 2686 } 2687 2688 /** Appends vector(s)/matrix(s) as row(s) to receiver 2689 * @param {Vector|Matrix|[Vectors]|[Matrices]} rows new row(s) to be appended 2690 * @example 2691 * m = mat([[1,2],[3,4]]); 2692 * m.appendRows(vec([7,6])); 2693 * // m = Matrix([[1,2],[3,4],[7,6]]) 2694 * m.appendRows(mat([[1,3],[2,4]])); 2695 * // m = Matrix([[1,2],[3,4],[7,6],[1,3],[2,4]]) 2696 * m = mat([[1,2],[3,4]]); 2697 * m.appendRows([vec([2,1]),vec([33,44])]); 2698 * // m = Matrix([[1,2],[3,4],[2,1],[33,44]]) 2699 * m = mat([[1,2],[3,4]]); 2700 * m.appendRows([mat([[11,12],[21,22]]),mat([[5,6],[7,8]])]); 2701 * // m = Matrix([[1,2],[3,4],[11,12],[21,22],[5,6],[7,8]]) 2702 */ 2703 Matrix.prototype.appendRows = function(rows) { 2704 if (rows instanceof Vector) { 2705 if (rows.nElements != this.nCols) { /*jsmLogger.warn('Matrix.appendRows: wrong argument dimensions');*/ return; } 2706 this[this.nRows] = {}; 2707 for (var c=0; c<this.nCols; c++) { this[this.nRows][c] = rows[c]; } 2708 this.nRows++; 2709 return; 2710 } 2711 if (rows instanceof Matrix) { 2712 if (rows.nCols != this.nCols) { /*jsmLogger.warn('Matrix.appendRows: wrong argument dimensions');*/ return; } 2713 for (var r=0; r<rows.nRows; r++) { 2714 this[this.nRows] = {}; 2715 for (var c=0; c<this.nCols; c++) { this[this.nRows][c] = rows[r][c]; } 2716 this.nRows++; 2717 } 2718 return; 2719 } 2720 if (rows instanceof Array) { 2721 for (var i=0; i<rows.length; i++) { 2722 this.appendRows(rows[i]); 2723 } 2724 } 2725 } 2726 2727 /** Appends vector(s)/matrix(s) as column(s) to receiver 2728 * @param {Vector|Matrix|[Vectors]|[Matrices]} cols new row(s) to be appended 2729 * @example 2730 * m = mat([[1,2],[3,4]]); 2731 * m.appendCols(vec([7,6])); 2732 * // m = Matrix([[1,2,7],[3,4,6]]) 2733 * m.appendCols(mat([[1,3],[2,4]])); 2734 * // m = Matrix([[1,2,7,1,3],[3,4,6,2,4]]) 2735 * m = mat([[1,2],[3,4]]); 2736 * m.appendCols([vec([2,1]),vec([33,44])]); 2737 * // m = Matrix([[1,2,2,33],[3,4,1,44]]) 2738 * m = mat([[1,2],[3,4]]); 2739 * m.appendCols([mat([[11,12],[21,22]]),mat([[9,8],[7,6]])]); 2740 * // m = Matrix([[1,2,11,12,9,8],[3,4,21,22,7,6]]) 2741 */ 2742 Matrix.prototype.appendCols = function(cols) { 2743 if (cols instanceof Vector) { 2744 if (cols.nElements != this.nRows) { /*jsmLogger.warn('Matrix.appendCols: wrong argument dimensions');*/ return; } 2745 for (var r=0; r<this.nRows; r++) { this[r][this.nCols] = cols[r]; } 2746 this.nCols++; 2747 return; 2748 } 2749 if (cols instanceof Matrix) { 2750 if (cols.nRows != this.nRows) { /*jsmLogger.warn('Matrix.appendCols: wrong argument dimensions');*/ return; } 2751 for (var r=0; r<this.nRows; r++) { 2752 for (var c=0; c<cols.nCols; c++) { this[r][this.nCols+c] = cols[r][c]; } 2753 } 2754 this.nCols += cols.nCols; 2755 return; 2756 } 2757 if (cols instanceof Array) { 2758 for (var i=0; i<cols.length; i++) { 2759 this.appendCols(cols[i]); 2760 } 2761 } 2762 } 2763 2764 /** Testing matrix equality 2765 * @param {Matrix} mat matrix to be compared with receiver 2766 * @returns {bool} true if this==mat, false otherwise 2767 * @example 2768 * a = mat([[1,2,3],[4,5,6],[7,8,9]]); 2769 * b = mat([[1,2,2.999999999],[4.0000000000002,5,6],[7,8,8.9999999999]]); 2770 * c = mat([[1,2,3],[4,5,6],[7,8,9],[10,11,12]]); 2771 * t1 = a.isEqualTo(b); // t1 = true 2772 * t2 = a.isEqualTo(c); // t2 = false 2773 */ 2774 Matrix.prototype.isEqualTo = function(mat) { 2775 if (!this.isSameSizeAs(mat)) { return false; } 2776 for (var r=0; r<this.nRows; r++) { 2777 for (var c=0; c<this.nCols; c++) { 2778 if (Math.abs(this[r][c] - mat[r][c]) > JSM_TOL) { return false; } 2779 } 2780 } 2781 return true; 2782 } 2783 2784 /** Testing matrix size equality 2785 * @param {Matrix} mat matrix to be tested 2786 * @returns {bool} true if mat and receiver has same size, false otherwise 2787 * @example 2788 * a = mat([[1,2,3],[4,5,6]]); 2789 * b = mat([[5,6,7],[8,9,10],[11,12,13]]); 2790 * c = mat([[14,15,16],[17,18,19]]); 2791 * t1 = a.isSameSizeAs(b); // t1 = false 2792 * t2 = a.isSameSizeAs(c); // t2 = true 2793 */ 2794 Matrix.prototype.isSameSizeAs = function(mat) { 2795 return (this.nRows==mat.nRows && this.nCols==mat.nCols); 2796 } 2797 2798 /** Testing if receiver can multiply given matrix ( this * mat ) 2799 * @param {Matrix} mat matrix to be tested 2800 * @returns {bool} true if the multiplication is possible, false otherwise 2801 * @example 2802 * m1 = mat([[1,2,3],[4,5,6]]); 2803 * m2 = mat([[7,8,9],[10,11,12]]); 2804 * m3 = mat([[11,12],[21,22],[31,32]]); 2805 * t1 = m1.canMultiplyMat(m2); // t1 = false 2806 * t2 = m1.canMultiplyMat(m3); // t2 = true 2807 */ 2808 Matrix.prototype.canMultiplyMat = function(mat) { 2809 return (this.nCols==mat.nRows); 2810 } 2811 2812 /** Testing if receiver can multiply given vector ( this * vec ) 2813 * @param {Vector} vec vector to be tested 2814 * @returns {bool} true if the multiplication is possible, false otherwise 2815 * @example 2816 * m = mat([[1,2,3],[4,5,6]]); 2817 * v1 = vec([1,2,3]); 2818 * v2 = vec([4,5]); 2819 * t1 = m.canMultiplyVec(v1); // t1 = true 2820 * t2 = m.canMultiplyVec(v2); // t2 = false 2821 */ 2822 Matrix.prototype.canMultiplyVec = function(vec) { 2823 return (this.nCols==vec.nElements); 2824 } 2825 2826 /** Testing if receiver is square 2827 * @returns {bool} true if receiver is square matrix, false otherwise 2828 * @example 2829 * m1 = mat([[1,2,3],[4,5,6]]); 2830 * m2 = mat([[1,2,3],[4,5,6],[7,8,9]]); 2831 * t1 = m1.isSquare(); // t1 = false 2832 * t2 = m2.isSquare(); // t2 = true 2833 */ 2834 Matrix.prototype.isSquare = function() { 2835 return (this.nRows==this.nCols); 2836 } 2837 2838 /** Testing if receiver is symmetric 2839 * @returns {bool} true if receiver is symmetric, false otherwise 2840 * @example 2841 * m1 = mat([[11,12,13],[21,22,23],[31,32,33]]); 2842 * m2 = mat([[11,12,13],[12,22,23],[13,23,33]]); 2843 * t1 = m1.isSymmetric(); // t1 = false 2844 * t2 = m2.isSymmetric(); // t2 = true 2845 */ 2846 Matrix.prototype.isSymmetric = function() { 2847 if (!this.isSquare()) { return false; } 2848 for (var r=0; r<this.nRows; r++) { 2849 for (var c=r+1; c<this.nCols; c++) { 2850 if (Math.abs(this[r][c] - this[c][r]) > JSM_TOL) { return false; } 2851 } 2852 } 2853 return true; 2854 } 2855 2856 /** Testing if receiver is transposition of given matrix 2857 * @param {Matrix} mat given matrix 2858 * @returns {bool} true if this==mat^T, false otherwise 2859 * @example 2860 * m1 = mat([[1,2,3],[4,5,6]]); 2861 * m2 = mat([[1,4],[2,5],[3,6]]); 2862 * m3 = mat([[1,2],[4,5]]); 2863 * b1 = m1.isTranspositionOf(m2); // b1 = true 2864 * b2 = m1.isTranspositionOf(m3); // b2 = false 2865 */ 2866 Matrix.prototype.isTranspositionOf = function(mat) { 2867 if (this.nRows!=mat.nCols || this.nCols!=mat.nRows) { return false; } 2868 var r,c; 2869 for (r=0; r<this.nRows; r++) { 2870 for (c=0; c<this.nCols; c++) { 2871 if (Math.abs(this[r][c]-mat[c][r]) > JSM_TOL) { return false; } 2872 } 2873 } 2874 return true; 2875 } 2876 2877 /** Testing if receiver is lower triangular matrix 2878 * @returns {bool} true if receiver is lower triangular, false otherwise 2879 * @example 2880 * m1 = mat([[1,2,3],[4,5,6],[7,8,9]]); 2881 * m2 = mat([[1,0,0],[4,5,0],[7,8,9]]); 2882 * t1 = m1.isLowerTriangular(); // t1 = false 2883 * t2 = m2.isLowerTriangular(); // t2 = true 2884 */ 2885 Matrix.prototype.isLowerTriangular = function() { 2886 for (var r=0; r<this.nRows; r++) { 2887 for (var c=r+1; c<this.nCols; c++) { 2888 if (Math.abs(this[r][c]) > JSM_TOL) { return false; } 2889 } 2890 } 2891 return true; 2892 } 2893 2894 /** Testing if receiver is upper triangular matrix 2895 * @returns {bool} true if receiver is upper triangular, false otherwise 2896 * @example 2897 * m1 = mat([[1,2,3],[4,5,6],[7,8,9]]); 2898 * m2 = mat([[1,2,3],[0,5,6],[0,0,9]]); 2899 * t1 = m1.isUpperTriangular(); // t1 = false 2900 * t2 = m2.isUpperTriangular(); // t2 = true 2901 */ 2902 Matrix.prototype.isUpperTriangular = function() { 2903 for (var r=0; r<this.nRows; r++) { 2904 for (var c=0; c<r; c++) { 2905 if (Math.abs(this[r][c]) > JSM_TOL) { return false; } 2906 } 2907 } 2908 return true; 2909 } 2910 2911 /** Test receiver's singularity 2912 * @returns {bool} true if receiver is singular (if abs(this.det()) < JSM_TOL), false otherwise 2913 * @example 2914 * a = mat([[1,2,3],[2,4,6],[4,5,6]]); 2915 * b = mat([[4,2,1],[2,5,3],[1,3,6]]); 2916 * s1 = a.isSingular(); // s1 = true 2917 * s2 = b.isSingular(); // s2 = false 2918 */ 2919 Matrix.prototype.isSingular = function() { 2920 var d = this.determinant(); 2921 // matrix is singular <=> det(matrix)==0 2922 return isNaN(d)? true : (Math.abs(d) < JSM_TOL); 2923 } 2924 2925 /** Test if receiver is identity matrix 2926 * @returns {bool} true if receiver is identity, false otherwise 2927 * @example 2928 * m1 = mat([[1,0,0],[0,1,0],[0,0,-1]]); 2929 * m2 = mat([[1,0,0],[0,1,0],[0,0,1]]); 2930 * i1 = m1.isIdentity() // i1 = false 2931 * i2 = m2.isIdentity() // i2 = true 2932 */ 2933 Matrix.prototype.isIdentity = function() { 2934 if (!this.isSquare()) { return false; } 2935 var r, c; 2936 for (var r=0; r<this.nRows; r++) { 2937 if (Math.abs(this[r][r]-1.) > JSM_TOL) { return false; } 2938 } 2939 for (var r=1; r<this.nRows; r++) { 2940 for (var c=r+1; c<this.nCols; c++) { 2941 if (Math.abs(this[r][c]) > JSM_TOL) { return false; } 2942 if (Math.abs(this[c][r]) > JSM_TOL) { return false; } 2943 } 2944 } 2945 return true; 2946 } 2947 2948 /** Alias for isIdentity, see {@link Matrix#isIdentity} 2949 * @function 2950 */ 2951 Matrix.prototype.isUnitMatrix = Matrix.prototype.isIdentity; 2952 2953 /** Test if receiver is zero matrix (full of zeroes) 2954 * @returns {bool} true if receiver is zero, false otherwise 2955 * @example 2956 * m1 = mat([[0,0,0],[0,1e-16,0],[0,0,0]]); 2957 * m2 = mat([[1,0,0],[0,1,0],[0,0,1]]); 2958 * i1 = m1.isZero() // i1 = true 2959 * i2 = m2.isZero() // i2 = false 2960 */ 2961 Matrix.prototype.isZero = function() { 2962 for (var i=0; i<this.nRows; i++) { 2963 for (var j=0; j<this.nCols; j++) { 2964 if (Math.abs(this[i][j]) > JSM_TOL) { return false; } 2965 } 2966 } 2967 return true; 2968 } 2969 2970 /** Alias for isZero(), see {@link Matrix#isZero} 2971 * @function 2972 */ 2973 Matrix.prototype.containsOnlyZeros = Matrix.prototype.isZero; 2974 2975 /** Test if receiver is matrix full of ones 2976 * @returns {bool} true if receiver is full of ones, false otherwise 2977 * @example 2978 * m1 = mat([[1,1,1],[1,.999999999999,1],[1,1,1]]); 2979 * m2 = mat([[1,0,0],[0,1,0],[0,0,1]]); 2980 * i1 = m1.isOne() // i1 = true 2981 * i2 = m2.isOne() // i2 = false 2982 */ 2983 Matrix.prototype.isOne = function() { 2984 for (var r=0; r<this.nRows; r++) { 2985 for (var c=0; c<this.nCols; c++) { 2986 if (Math.abs(this[r][c]-1.) > 2*JSM_TOL) { return false; } 2987 } 2988 } 2989 return true; 2990 } 2991 2992 /** Alias for isOne(), see {@link Matrix#isOne} 2993 * @function 2994 */ 2995 Matrix.prototype.containsOnlyOnes = Matrix.prototype.isOne; 2996 2997 /** Tests receiver's orthogonality (i.e. this*this^T = I => this^-1 = this^T) 2998 * @returns {bool} true if receiver is orthogonal, false otherwise 2999 * @example 3000 * m1 = Matrix.Householder(vec([0,1,0])) 3001 * // m1 = Matrix([[1,0,0],[0,-1,0],[0,0,1]]); 3002 * m2 = mat([[0,0,1],[1,0,0],[0,1,0]]) 3003 * m3 = mat([[1,2,3],[4,5,6],[7,8,9]]) 3004 * m4 = mat([[0.8,0,0.6],[0,1,0],[-0.8,0,0.6]]); 3005 * b1 = m1.isOrthogonal() // b1 = true 3006 * b2 = m2.isOrthogonal() // b2 = true 3007 * b3 = m3.isOrthogonal() // b3 = false 3008 * b4 = m4.isOrthogonal() // b4 = true 3009 */ 3010 Matrix.prototype.isOrthogonal = function() { 3011 var temp = new Matrix(); 3012 temp.beProductTOf(this,this); 3013 return temp.isIdentity(); 3014 } 3015 3016 /** Tests receiver's positive definitness 3017 * @returns {bool} true if receiver is positive definite (if Cholesky decomposition can be performed), false otherwise 3018 * @example 3019 * m1 = mat([[9,2,1],[2,8,3],[1,3,7]]) 3020 * m2 = mat([[-9,2,1],[2,8,3],[1,3,7]]) 3021 * b1 = m1.isPositiveDefinite() // b1 = true 3022 * b2 = m2.isPositiveDefinite() // b2 = false 3023 */ 3024 Matrix.prototype.isPositiveDefinite = function() { 3025 // matrix is positive definite <=> cholesky decomposition can be performed 3026 return this.choleskyDecomposition() != null; 3027 } 3028 3029 /** Tests receiver's involutariness (if this*this=I -> this^-1=this) 3030 * @returns {bool} true if receiver is involutary, false otherwise 3031 * @example 3032 * m = mat([[1,0,0],[0,-1,0],[0,0,-1]]) 3033 * t = m.isInvolutary() // t = true 3034 */ 3035 Matrix.prototype.isInvolutary = function() { 3036 var temp = new Matrix(); 3037 temp.beProductOf(this,this); 3038 return temp.isIdentity(); 3039 } 3040 3041 /** Returns row permutation matrix using implicit partial pivoting (for each column find row with relatively highest element at that column and place that line to the position such that the highest element is on diagonal. Lines that are placed in this way are not considered for further steps) 3042 * @param {bool} [saveOrig=true] if true, returns permutated copy of receiver. If false, permutate receiver and return this 3043 * @Returns {[Matrix,[ints]]} permutated matrix and array containing coefficients of original matrix according to position in the permutated one ( such that this.rowPermutation(coeffs) = ret ) 3044 * @example 3045 * m1 = mat([[1,2,4,9],[1,8,1,1],[9,4,5,3],[1,2,6,2]]); 3046 * m2 = m1.implicitPartialPivotPermutation(); 3047 * // m2[0] = Matrix([[9,4,5,3],[1,8,1,1],[1,2,6,2],[1,2,4,9]]) 3048 * // m2[1] = [2,1,3,0] 3049 * // m1 = Matrix([[1,2,4,9],[1,8,1,1],[9,4,5,3],[1,2,6,2]]) 3050 * m3 = m1.rowPermutation(m2[1]); // m3 = Matrix([[9,4,5,3],[1,8,1,1],[1,2,6,2],[1,2,4,9]]) 3051 * m4 = m2[0].rowPermutation(m2[1],true); // m4 = Matrix([[1,2,4,9],[1,8,1,1],[9,4,5,3],[1,2,6,2]]) 3052 * m1 = m1.implicitPartialPivotPermutation(false)[0]; 3053 * // m1 = Matrix([[9,4,5,3],[1,8,1,1],[1,2,6,2],[1,2,4,9]]) 3054 */ 3055 Matrix.prototype.implicitPartialPivotPermutation = function(saveOrig) { 3056 var saveOrig = saveOrig==undefined? true : saveOrig; 3057 var n = this.nRows; 3058 var ret = saveOrig? this.copy() : this; 3059 var vv = []; 3060 var coeffs = range(n); 3061 var big, i, j, k; 3062 for (i=0; i<n; i++) { 3063 big = 0.; 3064 for (j=0; j<n; j++) { 3065 if ((temp=Math.abs(ret[i][j])) > big) { big=temp; } 3066 } 3067 if (big == 0.0) { /*jsmLogger.warn('Matrix.implicitPartialPivotPermutation: singular matrix');*/ return null; } 3068 vv[i]=1.0/big; 3069 } 3070 for (k=0; k<n; k++) { 3071 big=0.0; 3072 for (i=k; i<n; i++) { 3073 temp = vv[i]*Math.abs(ret[i][k]); 3074 if (temp > big) { 3075 big = temp; 3076 imax = i; 3077 } 3078 } 3079 if (k != imax) { 3080 ret.swapRows(k,imax); 3081 vv[imax]=vv[k]; 3082 temp = coeffs[k] 3083 coeffs[k] = imax; 3084 coeffs[imax] = temp; 3085 } 3086 } 3087 return [ret,coeffs]; 3088 } 3089 3090 /** Returns vector x as a solution of this*ret = vec, receiver is assumed to be lower triangular matrix 3091 * @param {Vector} vec right hand side 3092 * @param {bool} [saveOrig=true] if true, returns vec will be unchanged and new vector will be returned. If false, solution will be saved to vec and vec will be returned 3093 * @Returns {Vector} solution of this*ret = vec 3094 * @example 3095 * a = mat([[1,0,0,0],[2,3,0,0],[4,5,6,0],[7,8,9,10]]); 3096 * y = vec([4,17,43,80]); 3097 * x = a.forwardSubstitution(y); 3098 * // x = Vector([4,3,2,1]) 3099 * // y = Vector([4,17,43,80]) 3100 * check = a.mul(x); // check = Vector([4,17,43,80]) 3101 * // x = Vector([4,3,2,1]) 3102 * // y = Vector([4,17,43,80]) 3103 * yy = y.copy(); 3104 * // yy = Vector([4,17,43,80]) 3105 * a.forwardSubstitution(yy,false); 3106 * // yy = Vector([4,3,2,1]) 3107 */ 3108 Matrix.prototype.forwardSubstitution = function(vec,saveOrig) { 3109 if (!this.isSquare() || !this.canMultiplyVec(vec)) { /*jsmLogger.warn('Matrix.forwardSubstitution: dimensions mismatched');*/ return null; } 3110 var saveOrig = saveOrig==undefined? true : saveOrig; 3111 var temp; 3112 var n = this.nRows; 3113 var ret = saveOrig? vec.copy() : vec; 3114 var i, j; 3115 /* this is lower triangular 3116 * this*ret = vec 3117 * 3118 * |t00 0 0 0 ... 0 | |ret0| |vec0| 3119 * |t10 t11 0 0 ... 0 | * |ret1| = |vec1| 3120 * |t20 t21 t22 0 ... 0 | |ret2| |vec2| 3121 * 3122 * ret0 = vec0/t00 3123 * ret1 = (vec1-t10*ret0)/t11 3124 * ret2 = (vec2-t20*ret0-t21*ret1)/t22 3125 * ... 3126 * retI = (vecI - sum_{j=0,I-1}(tIj*retj))/tII 3127 */ 3128 for (i=0; i<n; i++) { 3129 temp = ret[i]; 3130 for (j=0; j<i; j++) { 3131 temp -= this[i][j]*ret[j]; 3132 } 3133 ret[i] = temp/this[i][i]; 3134 } 3135 return ret; 3136 } 3137 3138 /** Returns vector x as a solution of this*ret = vec, receiver is assumed to be upper triangular matrix 3139 * @param {Vector} vec right hand side 3140 * @param {bool} [saveOrig=true] if true, returns vec will be unchanged and new vector will be returned. If false, solution will be saved to vec and vec will be returned 3141 * @Returns {Vector} solution of this*ret = vec 3142 * @example 3143 * a = mat([[1,2,3,4],[0,5,6,7],[0,0,8,9],[0,0,0,10]]); 3144 * y = vec([20,34,25,10]); 3145 * x = a.backwardSubstitution(y); 3146 * // x = Vector([4,3,2,1]) 3147 * // y = Vector([20,34,25,10]) 3148 * check = a.mul(x); // check = Vector([20,34,25,10]) 3149 * // x = Vector([4,3,2,1]) 3150 * // y = Vector([20,34,25,10]) 3151 * yy = y.copy(); 3152 * // yy = Vector([20,34,25,10]) 3153 * a.backwardSubstitution(yy,false); 3154 * // yy = Vector([4,3,2,1]) 3155 */ 3156 Matrix.prototype.backwardSubstitution = function(vec,saveOrig) { 3157 if (!this.isSquare() || !this.canMultiplyVec(vec)) { /*jsmLogger.warn('Matrix.backwardSubstitution: dimensions mismatched');*/ return null; } 3158 var saveOrig = saveOrig==undefined? true : saveOrig; 3159 var temp; 3160 var n = this.nRows; 3161 var ret = saveOrig? vec.copy() : vec; 3162 var i, j; 3163 /* this is upper triangular 3164 * this*ret = vec 3165 * 3166 * | ... (N, M=N-1, L=N-2) 3167 * |0 ... tLL tLM tLN| |retL| |vecL| 3168 * |0 ... 0 tMM tMN| * |retM| = |vecM| 3169 * |0 ... 0 0 tNN| |retN| |vecN| 3170 * 3171 * retN = vecN/tNN 3172 * retM = (vecM-tMN*retN)/tMM 3173 * retL = (vecL-tLN*retN-tLM*retM)/tMM 3174 * ... 3175 * retI = (vecI-sum_{j=I+1,N}(tIj*retj))/tII 3176 */ 3177 for (i=n-1; i>=0; i--) { 3178 temp = ret[i]; 3179 for (j=i+1; j<n; j++) { 3180 temp -= this[i][j]*ret[j]; 3181 } 3182 ret[i] = temp/this[i][i]; 3183 } 3184 return ret; 3185 } 3186 3187 /** Returns vector as a solution of system of linear equations using gaussian elimination method (this * ret = rhs --> ret ) 3188 * @param {Vector} rhs vector of right hand sides 3189 * @param {string} [pivoting="default"] what type of pivoting to use. "default" and "nopivot" stands for no pivoting, "partpivot" for implicit partial pivoting (only row interchanges) 3190 * @returns {Vector} vector of solution 3191 * @example 3192 * a = mat([[1,2,9],[8,3,2],[3,7,3]]); 3193 * b = vec([32,20,26]); 3194 * x = a.gaussianElimination(b); // x = Vector([1,2,3]) 3195 * x = a.gaussianElimination(b,'partpivot'); // x = Vector([1,2,3]) 3196 * a.gaussianElimination(b,'nopivot',false); 3197 * // a = Matrix([[1,2,9],[8,-13,-70],[3,1,-29.384615384615387]]) 3198 * // b = Vector([1,2,3]) 3199 */ 3200 Matrix.prototype.gaussianElimination = function(rhs,pivoting,saveOrig) { 3201 if (!this.canMultiplyVec(rhs) || !this.isSquare()) { /*jsmLogger.warn('Matrix.gaussianElimination: dimensions mismatched');*/ return null; } 3202 var pivoting = pivoting || "default"; 3203 var saveOrig = saveOrig==undefined? true : saveOrig; 3204 var n = this.nRows; 3205 var i, j, k; 3206 if (pivoting == "nopivot" || pivoting=="default") { 3207 var a = saveOrig? this.copy() : this; 3208 var b = saveOrig? rhs.copy() : rhs; 3209 for (k=0; k<n-1; k++) { 3210 akk = a[k][k]; // "pivot" 3211 for (i=k+1; i<n; i++) { 3212 for (j=k+1; j<n; j++) { 3213 a[i][j] -= a[k][j]*a[i][k]/akk; 3214 } 3215 b[i] -= b[k]*a[i][k]/akk; 3216 } 3217 } 3218 return a.backwardSubstitution(b,false); 3219 } 3220 if (pivoting == "partpivot") { 3221 var a = this.implicitPartialPivotPermutation(saveOrig); 3222 var b = saveOrig? rhs.copy() : rhs; 3223 b.permuteElements(a[1]); 3224 return a[0].gaussianElimination(b,"nopivot",false); 3225 } 3226 return this.gaussianElimination(rhs,"default",saveOrig); 3227 } 3228 3229 /** Returns matrix, whose columns are solutions of system of equations this*ret = rhs 3230 * @param {Matrix,[Vectors],Vector} rhs matrix/vector/array of vectors representing right hand sides 3231 * @param {string} [pivoting="default"] what type of pivoting to use. "default" and "nopivot" stands for no pivoting, "partpivot" for implicit partial pivoting (only row interchanges) 3232 * @param {bool} [saveOrig=true] if true, receiver is not changed. If false, solution will be saved to rhs and rhs will be returned 3233 * @returns {Matrix} matrix, whose columns are solution for particular right hand sides 3234 * @example 3235 * a = mat([[1,2,9],[8,3,2],[3,7,3]]); 3236 * b1 = vec([32,20,26]); 3237 * b2 = vec([16,32,26]); 3238 * b3 = [b1.copy(),b2.copy()]; 3239 * b4 = mat([b1.toArray(),b2.toArray()]).T(); 3240 * x1 = a.gaussJordanElimination(b1); 3241 * // x1 = Vector([ 1, 2, 3 ]) 3242 * x2 = a.gaussJordanElimination(b2); 3243 * // x2 = Vector([ 3, 2, 1 ]) 3244 * x3 = a.gaussJordanElimination(b3); 3245 * // x3 = [Vector([ 1, 2, 3 ]) ,Vector([ 3, 2, 1 ])] 3246 * x4 = a.gaussJordanElimination(b4); 3247 * // x4 = Matrix([[ 1, 3 ], [ 2, 2 ], [ 3, 1 ]]) 3248 * x5 = a.gaussJordanElimination(b1,'nopivot');; 3249 * // x5 = Vector([ 1, 2, 3 ]) 3250 * x6 = a.gaussJordanElimination(b2,'nopivot'); 3251 * // x6 = Vector([ 3, 2, 1 ]) 3252 * x7 = a.gaussJordanElimination(b3,'nopivot'); 3253 * // x7 = Vector([ 1, 2, 3 ]) ,Vector([ 3, 2, 1 ]) 3254 * x8 = a.gaussJordanElimination(b4,'nopivot'); 3255 * // x8 = Matrix([[ 1, 3 ], [ 2, 2 ], [ 3, 1 ]]) 3256 * a.gaussJordanElimination(b4,'nopivot',false); 3257 * // b4 = Matrix([[ 1, 3 ], [ 2, 2 ], [ 3, 1 ]]) 3258 */ 3259 Matrix.prototype.gaussJordanElimination = function(rhs,pivoting,saveOrig) { 3260 if (!this.isSquare) { /*jsmLogger.warn('Matrix.gaussJordanElimination: dimensions mismatched');*/ return null; } 3261 var rhsInstance = (rhs instanceof Vector)? 1 : ((rhs instanceof Array)? 2 : ((rhs instanceof Matrix)? 3 : 4)); 3262 if (!(rhsInstance==1 || rhsInstance==2 || rhsInstance==3)) { /*jsmLogger.warn('Matrix.gaussJordanElimination: wrong argument');*/ return null; } 3263 var pivoting = pivoting || "default"; 3264 var saveOrig = saveOrig==undefined? true : saveOrig; 3265 var i, j, k, akk; 3266 var n = this.nRows; 3267 var nrhs = rhsInstance==2? rhs.length : (rhsInstance==3? rhs.nCols : 1); 3268 if (pivoting=="default" || pivoting=="nopivot") { 3269 var a = saveOrig? this.copy() : this; 3270 var b; 3271 if (rhsInstance==1 || rhsInstance==3) { b = saveOrig? rhs.copy() : rhs; } 3272 else { 3273 b = []; 3274 for (i=0; i<nrhs; i++) { b[i] = saveOrig? rhs[i].copy() : rhs[i]; } 3275 } 3276 for (k=0; k<n; k++) { 3277 akk = a[k][k]; 3278 for (i=k+1; i<n; i++) { 3279 for (j=k+1; j<n; j++) { 3280 a[i][j] -= a[k][j]*a[i][k]/akk; 3281 } 3282 switch (rhsInstance) { 3283 case 1: b[i] -= b[k]*a[i][k]/akk; break; 3284 case 2: for (j=0; j<nrhs; j++) { b[j][i] -= b[j][k]*a[i][k]/akk; } break; 3285 case 3: for (j=0; j<nrhs; j++) { b[i][j] -= b[k][j]*a[i][k]/akk; } break; 3286 } 3287 } 3288 } 3289 for (k=n-1; k>=0; k--) { 3290 akk = a[k][k]; 3291 for (i=k-1; i>=0; i--) { 3292 for (j=n-1; j>k; j--) { 3293 a[i][j] -= a[k][j]*a[i][k]/akk; 3294 } 3295 switch (rhsInstance) { 3296 case 1: b[i] -= b[k]*a[i][k]/akk; break; 3297 case 2: for (j=0; j<nrhs; j++) { b[j][i] -= b[j][k]*a[i][k]/akk; } break; 3298 case 3: for (j=0; j<nrhs; j++) { b[i][j] -= b[k][j]*a[i][k]/akk; } break; 3299 } 3300 } 3301 } 3302 for (k=0; k<n; k++) { 3303 akk = a[k][k]; 3304 switch (rhsInstance) { 3305 case 1: b[k] /= akk; break; 3306 case 2: for (j=0; j<nrhs; j++) { b[j][k] /= akk; } break; 3307 case 3: for (j=0; j<nrhs; j++) { b[k][j] /= akk; } break; 3308 } 3309 } 3310 return b; 3311 } 3312 if (pivoting == "partpivot") { 3313 var a = this.implicitPartialPivotPermutation(saveOrig); 3314 var temp; 3315 switch (rhsInstance) { 3316 case 1: temp = saveOrig? rhs.copy() : rhs; temp.permuteElements(a[1]); break; 3317 case 2: 3318 temp = []; 3319 for (i=0; i<nrhs; i++) { 3320 temp[i] = saveOrig? rhs[i].copy() : rhs[i]; 3321 temp[i].permuteElements(a[1]); 3322 } 3323 break; 3324 case 3: 3325 temp = saveOrig? rhs.copy() : rhs; 3326 temp.permuteRows(a[1]); 3327 break; 3328 } 3329 return a[0].gaussJordanElimination(temp,"nopivot",false); 3330 } 3331 return this.gaussJordanElimination(rhs,"default",saveOrig); 3332 } 3333 3334 /** Returns lower triangular matrix of Cholesky decomposition of receiver. Receiver must be square, symmetric and positive definite. ( ret * ret^T = this ) 3335 * @param {bool} [saveOrig=true] if true, receiver is not changed. If false, solution will be saved to receiver 3336 * @param {bool} [checkSymm=false] if true, test of receiver symmetricity is performed, otherwise not 3337 * @returns {Matrix} lower triangular Cholesky matrix 3338 * @example 3339 * a = mat([[1,2,4],[2,13,23],[4,23,77]]); 3340 * l = a.choleskyDecomposition(); // l = Matrix([[1,0,0],[2,3,0],[4,5,6]]) 3341 * check = l.x(l.T()); // check = Matrix([[1,2,4],[2,13,23],[4,23,77]]); 3342 */ 3343 Matrix.prototype.choleskyDecomposition = function(saveOrig,checkSymm) { 3344 var checkSymm = checkSymm==undefined? true : check; 3345 if (!this.isSquare()) { /*jsmLogger.warn('Matrix.choleskyDecomposition: matrix is not square');*/ return null; } 3346 if (checkSymm) { 3347 if (!this.isSymmetric()) { /*jsmLogger.warn('Matrix.choleskyDecomposition: matrix is not symmetric');*/ return null; } 3348 } 3349 var saveOrig = saveOrig==undefined? true : saveOrig; 3350 var n = this.nRows; 3351 var a = saveOrig? this.copy() : this; 3352 var aii, temp, i, j, k; 3353 /* 3354 * |t00 t01 t02 ..| |l00 0 0 ..| |l00 l10 l20 ..| 3355 * |t01 t11 t12 ..|=|l10 l11 0 ..|*| 0 l11 l21 ..| 3356 * |t02 t12 t22 ..| |l20 l21 l22 ..| | 0 0 l22 ..| 3357 * | .......... | | .......... | | .......... | 3358 * 3359 * t00 = l00*l00 -> l00 = sqrt(t00) 3360 * t01 = l00*l10 -> l10 = t01/l00 3361 * t02 = l00*l20 -> l20 = t02/l00 3362 * t11 = l10*l10+l11*l11 -> l11 = sqrt(t11-l00*l00) 3363 * t12 = l10*l20+l11*l21 -> l21 = (t12-l10*l20)/l11 3364 * t22 = l20*l20+l21*l21+l22*l22 -> l22=sqrt(t22-l20*l20-l21*l21) 3365 * t32 = l30*l20+l31*l21+l32*l22 -> l32 = (t32-l30*l20-l31*l21)/l22 3366 * ... 3367 * lii = sqrt(tii - sum_{k=0,i-1}(lik*lik)) 3368 * lij = (tij-sum_{k=0,j-1}(lik*ljk))/lii 3369 */ 3370 for (i=0; i<n; i++) { 3371 aii = a[i][i]; 3372 for (k=0; k<i; k++) { aii -= a[i][k]*a[i][k]; } 3373 if (aii<=0.) { /*jsmLogger.warn('Matrix.choleskyDecomposition: matrix is not positive definite');*/ return null; } 3374 aii = Math.sqrt(aii); 3375 a[i][i] = aii; 3376 for (j=i+1; j<n; j++) { 3377 temp = a[j][i]; 3378 for (k=0; k<i; k++) { temp -= a[i][k]*a[j][k]; } 3379 a[j][i] = temp/aii; 3380 } 3381 for (k=i+1; k<n; k++) { a[i][k] = 0.; } 3382 } 3383 return a; 3384 } 3385 3386 /** Alias for Cholesky decomposition, see {@link Matrix#choleskyDecomposition} 3387 * @function 3388 */ 3389 Matrix.prototype.chol = Matrix.prototype.choleskyDecomposition; 3390 3391 /** Alias for Cholesky decomposition, see {@link Matrix#choleskyDecomposition} 3392 * @function 3393 */ 3394 Matrix.prototype.lltDecomposition = Matrix.prototype.choleskyDecomposition; 3395 3396 /** Returns lower (L) and upper (U) triangular matrix of the receiver such that L*U=this. Diagonal elements of L are all 1. Receiver must be square. 3397 * @param {string} [pivoting="default"] what type of pivoting to use. "default" and "nopivot" stands for no pivoting, "partpivot" for implicit partial pivoting (only row interchanges) 3398 * @returns {[Matrix,Matrix]} [L,U], lower and upper triangular matrices 3399 * @example 3400 * a = mat([[6,5,4],[12,13,10],[18,27,21]]); 3401 * lu = a.luDecomposition(); 3402 * l = lu[0]; // l = Matrix([[1,0,0],[2,1,0],[3,4,1]]) 3403 * u = lu[1]; // u = Matrix([[6,5,4],[0,3,2],[0,0,1]]) 3404 * check = l.x(u); // check = Matrix([[6,5,4],[12,13,10],[18,27,21]]) 3405 */ 3406 Matrix.prototype.luDecomposition = function(pivoting) { 3407 if (!this.isSquare()) { /*jsmLogger.warn('Matrix.luDecomposition: matrix is not square');*/ return null; } 3408 var pivoting = pivoting || "default"; 3409 var n = this.nRows; 3410 var l = Matrix.Zeros(n,n); 3411 var u = Matrix.Zeros(n,n); 3412 var i, j, k, imax; 3413 var temp; 3414 /* 3415 * |t00 t01 t02 ..| |l00 0 0 ..| |u00 u01 u02 ..| 3416 * |t10 t11 t12 ..|=|l10 l11 0 ..|*| 0 u11 u12 ..| 3417 * |t20 t21 t22 ..| |l20 l21 l22 ..| | 0 0 u22 ..| 3418 * 3419 * assumtion: lkk = 1. 3420 * t00 = l00*u00 -> l00=1, u00=t00 3421 * t01 = l00*u01 -> u01=t01/l00 3422 * t02 = l00*u02 -> u02=t02/l00 3423 * ... 3424 * t10 = l10*u00 -> l10=t10/u00 3425 * t11 = l10*u01+l11*u11 -> l11=1, u11=(t11-l10*u01) 3426 * t12 = l10*u02+l11*u12 -> u12=t12-l10*u02 3427 * t13 = l10*u03+l11*u13 -> u13=t13-l10*u03 3428 * ... 3429 * t20 = l20*u00 -> l20=t10/u00 3430 * t21 = l20*u01+l21*u11 -> l21=(t21-l20*u01)/u11 3431 * t22 = l20*u02+l21*u12+l22*u22 -> l22=1, u22=(t22-l20*u02-l21*u12) 3432 * ... 3433 * lkk = 1. 3434 * ukj = tkj - sum_{i=0,k-1}(lki*uij) 3435 * ljk = tjk - sum_{i=0,k-1}(lji*uik) 3436 */ 3437 if (pivoting=="default" || pivoting=="nopivot") { 3438 for (k=0; k<n; k++) { 3439 l[k][k] = 1.; 3440 for (j=k; j<n; j++) { 3441 temp = this[k][j]; 3442 for (i=0; i<k; i++) { 3443 temp -= l[k][i]*u[i][j]; 3444 } 3445 u[k][j] = temp; 3446 } 3447 for (j=k+1; j<n; j++) { 3448 temp = this[j][k] 3449 for (i=0; i<k; i++) { 3450 temp -= l[j][i]*u[i][k] 3451 } 3452 l[j][k] = temp/u[k][k] 3453 } 3454 } 3455 return [l,u] 3456 } 3457 if (pivoting == "partpivot") { 3458 var a = this.implicitPartialPivotPermutation() 3459 var ret = a[0].luDecomposition("nopivot"); 3460 ret[2] = a[1]; 3461 return ret; 3462 } 3463 return this.luDecomposition("default"); 3464 } 3465 3466 /** Returns lower triangular (L), diagonal (D) and upper triangular (U) matrix of the receiver such that L*D*U=this. Diagonal elements of both L and U are all 1. Receiver must be square. 3467 * @param {string} [pivoting="default"] what type of pivoting to use. "default" and "nopivot" stands for no pivoting, "partpivot" for implicit partial pivoting (only row interchanges) 3468 * @returns {[Matrix,Vector,Matrix]} [L,D,U], lower, diagonal and upper triangular matrices 3469 * @example 3470 * a = mat([[2,10,8],[4,23,22],[6,42,52]]); 3471 * ldu = a.lduDecomposition(); 3472 * l = ldu[0]; // l = Matrix([[1,0,0],[2,1,0],[3,4,1]]) 3473 * d = ldu[1]; // d = Vector([2,3,4]) 3474 * u = ldu[2]; // u = Matrix([[1,5,4],[0,1,2],[0,0,1]]) 3475 * check = l.x(d.diag()).x(u) // check = Matrix([[2,10,8],[4,23,22],[6,42,52]]); 3476 */ 3477 Matrix.prototype.lduDecomposition = function(pivoting) { 3478 if (!this.isSquare()) { /*jsmLogger.warn('Matrix.lduDecomposition: matrix is not square');*/ return null; } 3479 var pivoting = pivoting || "default"; 3480 if (pivoting=="default" || pivoting=="nopivot") { 3481 var n = this.nRows; 3482 var l = Matrix.Zeros(n,n); 3483 var d = Vector.Zeros(n); 3484 var u = Matrix.Zeros(n,n); 3485 var temp; 3486 for (var i=0; i<n; i++) { 3487 l[i][i] = 1.; 3488 u[i][i] = 1.; 3489 for (var j=0; j<i; j++) { 3490 temp = this[i][j]; 3491 for (var k=0; k<j; k++) { 3492 temp -= l[i][k]*d[k]*u[k][j]; 3493 } 3494 l[i][j] = temp/d[j]; 3495 } 3496 temp = this[i][i]; 3497 for (var k=0; k<i; k++) { 3498 temp -= l[i][k]*d[k]*u[k][i]; 3499 } 3500 d[i] = temp; 3501 for (var j=i+1; j<n; j++) { 3502 temp = this[i][j]; 3503 for (var k=0; k<i; k++) { 3504 temp -= l[i][k]*d[k]*u[k][j]; 3505 } 3506 u[i][j] = temp/d[i]; 3507 } 3508 } 3509 return [l,d,u]; 3510 } 3511 if (pivoting = "partpivot") { 3512 var a = this.implicitPartialPivotPermutation(); 3513 var ret = a.lduDecomposition("nopivot"); 3514 ret[3] = a[1]; 3515 return ret; 3516 } 3517 return this.lduDecomposition("default"); 3518 } 3519 3520 /* Returns orthogonal matrix Q and upper triangular matrix R of QR decomposition of receiver such that Q*R=this. Implemented for square matrices only 3521 * @param {string} [method="default"] method for solution. "default" and "grammschmidt" stand for Gramm-Schmidt process, "householder" for using Householder transformations 3522 * @param {bool} [saveOrig=true] if true, receiver is not changed. If false, solution (R matrix) will be saved to this and this will be returned 3523 * @returns {[Matrix,Matrix]} Q and R of QR decomposition of receiver 3524 * @example 3525 * a = mat([[0.8,1.6,6],[0,4,5],[-0.6,-1.2,3]]) 3526 * qr = a.qrDecomposition(); 3527 * q = qr[0]; // q = Matrix([[0.8,0,0.6],[0,1,0],[-0.6,0,0.8]]) 3528 * r = qr[1]; // r = Matrix([[1,2,3],[0,4,5],[0,0,6]]) 3529 * check = q.x(r); // check = Matrix([[0.8,1.6,6],[0,4,5],[-0.6,-1.2,3]]) 3530 * o1 = q.isOrthogonal(); // o1 = true 3531 * o2 = r.isUpperTriangular(); // o2 = true 3532 * // 3533 * qr = a.qrDecomposition('householder'); 3534 * q = qr[0]; // q = Matrix([[ 0.11624763874381927, 0.24071784839360483, 0.9636085325230572 ], [ 0.9299811099505543, -0.3670351351744072, -0.02050230920261827 ], [ 0.34874291623145787, 0.8985210776672174, -0.26653001963403705 ]]) 3535 * r = qr[1]; // r = Matrix([[ 8.602325267042627, 5.463639020959507, 5.1148961047280475 ], [ 2.5252117732828282e-17, 5.669977834934511, 1.5968411725120326 ], [ -2.206040279013976e-16, 4.440892098500626e-16, 8.323937536263006 ]]) 3536 * check = q.x(r); // check = Matrix([[1,2,9],[8,3,4],[3,7,1]]) 3537 * o1 = q.isOrthogonal(); // o1 = true 3538 * o2 = r.isUpperTriangular(); // o2 = true 3539 */ 3540 Matrix.prototype.qrDecomposition = function(method,saveOrig) { 3541 if (!this.isSquare()) { /*jsmLogger.warn('Matrix.qrDecomposition: matrix is not square');*/ return null; } 3542 var saveOrig = saveOrig==undefined? true : saveOrig; 3543 var method = method || "default"; 3544 var n = this.nRows; 3545 if (method=="default" || method=="grammschmidt" || method=="gramm-schmidt" || method=="gs") { 3546 //TODO 3547 proj = function(ee,aa) { return ee.mulf(ee.dot(aa)/(ee.dot(ee))); } 3548 var r = saveOrig? this.copy() : this; 3549 var q = new Matrix(n,n); 3550 var a, e=[]; 3551 var j,k; 3552 for (k=0; k<n; k++) { 3553 a = r.getCol(k); 3554 e[k] = a.copy(); 3555 for (j=0; j<k; j++) { e[k].isub(proj(e[j],a)); } 3556 e[k].normalize(); 3557 for (j=0; j<=k; j++) { r[j][k] = e[j].dot(a); } 3558 for (j=k+1; j<n; j++) { r[j][k] = 0.; } 3559 q.setCol(k,e[k]); 3560 } 3561 return [q,r]; 3562 } 3563 if (method=="householder") { 3564 //TODO 3565 var r = saveOrig? this.copy() : this; 3566 var e = Vector.Zeros(n); 3567 e[0] = 1.; 3568 var a, x, rkn, qtemp, q=Matrix.Identity(n), piv; 3569 // TODO operation reduction 3570 for (var k=0; k<n-1; k++) { 3571 e.resize(n-k); 3572 rkn = range(k,n); 3573 a = r.getSubMatrix(rkn,rkn); 3574 x = a.getCol(0); 3575 qtemp = Matrix.Identity(n); 3576 piv = x[k]>0.? -1. : 1.; 3577 qtemp.setSubMatrix(rkn,rkn,Matrix.Householder(x.add(e.mulf(piv*x.norm())))); 3578 r = qtemp.mulm(r); 3579 q = q.mulm(qtemp.transposed()); 3580 } 3581 return [q,r]; 3582 } 3583 return this.qrDecomposition("default",saveOrig); 3584 } 3585 3586 /** Returns vector/array of vectors/matrix as a solution of system of linear equations (this*ret=rhs --> ret). Receiver must be square. TODO: solution with LU decomposition with permutation 3587 * @param {Vector|[Vectors]|Matrix} rhs vector of right hand sides. Array of vectors or Matrix as rhs is supported only with "gaussjordan" method 3588 * @param {string} [method="default"] method of the solution. Implemented methods are "default" and "gauss" for gaussian elimination, "gaussjordan" for Gauss-Jordan elimination (supporting more than one right hand sides in form of array of vectors or matrix with columns as rhs), "lu" for using LU decomposition, "ldu" for using LDU decomposition, "cholesky" for using Cholesky decomposition, "qr" for using QR decomposition 3589 * @param {bool} [saveOrig=true] if true, receiver and rhs is not changed. If false, solution will be saved to rhs and rhs will be returned and receiver will be changed 3590 * @param {[Matrices]} [precompDecomps=undefined] array of Matrices objects, used for solution using matrix decomposition as precomputed values (the decomposition is performed within linSolve function if args parameter is not specified) 3591 * @returns {Vector|[Vectors]|Matrix} vector/array of vectors/matrix of solution 3592 * @example 3593 * a = mat([[1,2,9],[8,3,2],[3,7,3]]); 3594 * b = vec([32,20,26]); 3595 * x1 = a.linSolve(b); // x1 = Vector([1,2,3]) 3596 * x2 = a.linSolve(b,'gauss'); // x2 = Vector([1,2,3]) 3597 * x3 = a.linSolve(b,'gaussjordan'); // x3 = Vector([1,2,3]) 3598 * x4 = a.linSolve(b,'lu'); // x4 = Vector([1,2,3]) 3599 * x5 = a.linSolve(b,'ldu'); // x5 = Vector([1,2,3]) 3600 * x7 = a.linSolve(b,'qr'); // x7 = Vector([1,2,3]) 3601 * a2 = mat([[1,2,4],[2,13,23],[4,23,77]]); 3602 * b2 = vec([17,97,281]); 3603 * x6 = a2.linSolve(b2,'cholesky'); // x6 = Vector([1,2,3]) 3604 */ 3605 Matrix.prototype.linSolve = function(rhs,method,saveOrig,precompDecomps) { 3606 var saveOrig = saveOrig==undefined? true : saveOrig; 3607 var method = method || "default"; 3608 method = method.toLowerCase(); 3609 if (method=="default" || method=="gauss" || method=="gaussian") { // gaussian elimination 3610 return this.gaussianElimination(rhs,"partpivot",saveOrig); 3611 } 3612 if (method=="gaussjordan" || method=="gauss-jordan" || method=="gj") { // gauss-jordan elimination 3613 return this.gaussJordanElimination(rhs,"partpivot",saveOrig); 3614 } 3615 if (method=="lu") { // use LU factorization 3616 var lu = precompDecomps || this.luDecomposition(); 3617 var l = lu[0]; 3618 var u = lu[1]; 3619 var y; 3620 if (rhs instanceof Vector) { 3621 y = l.forwardSubstitution(rhs,saveOrig); 3622 return u.backwardSubstitution(y,false); 3623 } 3624 if (rhs instanceof Matrix) { 3625 var ret = saveOrig? Matrix.Zeros(this.nRows,rhs.nCols) : rhs; 3626 for (var i=0; i<ret.nCols; i++) { 3627 y = l.forwardSubstitution(rhs.getCol(i),false); 3628 ret.setCol(i,u.backwardSubstitution(y,false)); 3629 } 3630 return ret; 3631 } 3632 if (rhs instanceof Array) { 3633 return null; 3634 // TODO 3635 } 3636 } 3637 if (method=="ldu") { // use LDU decomposition 3638 var ldu = precompDecomps || this.lduDecomposition("nopivot",true); 3639 var l = ldu[0]; 3640 var d = (ldu[1] instanceof Matrix)? ldu[1].diag() : ldu[1]; 3641 var u = ldu[2]; 3642 var y = l.forwardSubstitution(rhs,saveOrig); 3643 for (var r=0; r<this.nRows; r++) { y[r] /= d[r]; } 3644 return u.backwardSubstitution(y,false); 3645 } 3646 if (method=="cholesky" || method=="chol") { // use Cholesky's decomposition 3647 var l = precompDecomps || this.choleskyDecomposition(); 3648 if (l==null) { /*jsmLogger.warn('Matrix.linSolve: matrix is not positive definite');*/ return null; } 3649 var y = l.forwardSubstitution(rhs,saveOrig); 3650 return l.transposed().backwardSubstitution(y,false); 3651 } 3652 if (method=="qr") { // use QR decomposition 3653 var qr = precompDecomps || this.qrDecomposition("grammschmidt",saveOrig); 3654 return qr[1].backwardSubstitution(qr[0].transposed().mulv(rhs),false); 3655 } 3656 return this.linSolve(rhs,"default",saveOrig,precompDecomps); 3657 } 3658 3659 /** returns Schur form of receiver (upper triangular matrix with eigenvalues of the receiver on the diagonal). QR algorithm is used for calculation 3660 * @param {bool} [saveOrig=true] if true, returns new matrix, if false the Schur form is formed on original matrix 3661 * @param {int} [maxiter=1000] maximum number of iterations 3662 * @returns {Matrix} Schur form of receiver (upper triangular matrix with eigenvalues on diagonal) 3663 * @example 3664 * m1 = mat([[4,2,3,1],[3,6,5,7],[5,3,8,1],[2,3,4,5]]); 3665 * m2 = m1.schurForm(); // m2 = Matrix([[ 15.6072, -3.6605, -0.7006, 2.3149 ], [ 0, 4.1916, 3.3807, 2.0775 ], [ 0, 0, 2.2012, 0.0769 ], [ 0, 0, 0, 1 ]]) 3666 * b = m2.isUpperTriangular(); // b = true 3667 */ 3668 Matrix.prototype.schurForm = function(saveOrig,maxiter) { 3669 //return this.schurDecomposition(saveOrig,maxiter)[1]; 3670 if (!this.isSquare()) { /*jsmLogger.warn('Matrix.schurForm: matrix is not square');*/ return null; } 3671 var n = this.nRows; 3672 var saveOrig = saveOrig==undefined? true : saveOrig; 3673 var maxiter = maxiter || 1000; 3674 var qr, s = saveOrig? this.copy() : this; 3675 var rhos, rhosNew=Vector.Zeros(n), rhosRatio=Vector.Zeros(n), maxRhosRatio, i, q, r, iter=0, qq=Matrix.Identity(n); 3676 for (i=0; i<n; i++) { rhosNew[i] = 0.; } 3677 do { 3678 qr = s.qrDecomposition() 3679 q = qr[0]; 3680 r = qr[1]; 3681 s = r.mulm(q); 3682 rhos = rhosNew.copy(); 3683 rhosNew = s.diagonalToVector(); 3684 for (i=0; i<n; i++) { rhosRatio[i] = Math.abs((rhosNew[i] - rhos[i])/rhosNew[i]); } 3685 maxRhosRatio = 0.; 3686 for (var i=0; i<n; i++) { 3687 if (rhosRatio[i] > maxRhosRatio) { maxRhosRatio = rhosRatio[i]; } 3688 } 3689 iter++; 3690 if (iter > maxiter) { 3691 if (maxRhosRatio > JSM_TOL) { /*jsmLogger.warn('Matrix.schurForm: maximum number of iterations reached');*/ return null; } 3692 break; 3693 } 3694 } while (maxRhosRatio > JSM_TOL2) 3695 return s; 3696 } 3697 3698 /** returns orthogonal matrix Q and upper triangular matrix S as Schur decomposition of receiver such that this=Q*S*Q^-1. Columns of Q are eigenvectors of receiver and diagonal elements of S are eigenvalues of receiver 3699 * @param {bool} [saveOrig=true] if true, returns new matrices, if false, S is formed into original matrix 3700 * @param {int} [maxiter=1000] maximum number of iterations 3701 * @returns {[Matrix,Matrix]} Q,S - Scur decomposition of receiver 3702 * @example 3703 * m1 = mat([[4,2,3,1],[3,6,5,7],[5,3,8,1],[2,3,4,5]]); 3704 * qs = m1.schurDecomposition(); 3705 * q = qs[0]; s = qs[1]; 3706 * // s = Matrix([[ 15.6072, -3.6605, -0.7006, 2.3149 ], [ 0, 4.1916, 3.3807, 2.0775 ], [ 0, 0, 2.2012, 0.0769 ], [ 0, 0, 0, 1 ]]) 3707 * qr = s.qrDecomposition(); 3708 * q1 = qr[0]; r = qr[1]; 3709 * // q1 = Matrix([[ 1, 0, 0, 0 ], [ 0, 1, 0, 0 ], [ 0, 0, 1, 0 ], [ 0, 0, 0, 1 ]]) 3710 * // r = Matrix([[ 15.6072, -3.6605, -0.7006, 2.3149 ], [ 0, 4.1916, 3.3807, 2.0775 ], [ 0, 0, 2.2012, 0.0769 ], [ 0, 0, 0, 1 ]]) 3711 * b1 = q.isOrthogonal() // b1 = true 3712 * b2 = s.isUpperTriangular() // b2 = true 3713 * c = q.mulm(s).mulm(q.inv()); // c = Matrix([[4,2,3,1],[3,6,5,7],[5,3,8,1],[2,3,4,5]]) 3714 */ 3715 Matrix.prototype.schurDecomposition = function(saveOrig,maxiter) { 3716 if (!this.isSquare()) { /*jsmLogger.warn('Matrix.schurDecomposition: matrix is not square');*/ return null; } 3717 var n = this.nRows; 3718 var saveOrig = saveOrig==undefined? true : saveOrig; 3719 var maxiter = maxiter || 1000; 3720 var qr, s = saveOrig? this.copy() : this; 3721 var rhos, rhosNew=Vector.Zeros(n), rhosRatio=Vector.Zeros(n), maxRhosRatio, i, q, r, iter=0, qq=Matrix.Identity(n); 3722 for (i=0; i<n; i++) { rhosNew[i] = 0.; } 3723 do { 3724 // A(k)=Q(k)*R(k), A(k+1)=R(k)*Q(k)=Q(k)^T*A(k)*Q(k) 3725 // A(k+1) converges to Schur form 3726 // Furthermore Q=sum_k(Q(k)), S=A(N)=Q^T*this*Q -> this=Q*S*Q^T 3727 // (Q is othogonal) 3728 qr = s.qrDecomposition() 3729 q = qr[0]; 3730 qq = qq.mulm(q); 3731 r = qr[1]; 3732 s = r.mulm(q); 3733 rhos = rhosNew.copy(); 3734 rhosNew = s.diagonalToVector(); 3735 for (i=0; i<n; i++) { rhosRatio[i] = Math.abs((rhosNew[i] - rhos[i])/rhosNew[i]); } 3736 maxRhosRatio = 0.; 3737 for (var i=0; i<n; i++) { 3738 if (rhosRatio[i] > maxRhosRatio) { maxRhosRatio = rhosRatio[i]; } 3739 } 3740 iter++; 3741 if (iter > maxiter) { 3742 if (maxRhosRatio > JSM_TOL) { /*jsmLogger.warn('Matrix.schurDecomposition: maximum number of iterations reached');*/ return null; } 3743 break; 3744 } 3745 } while (maxRhosRatio > JSM_TOL2) 3746 return [qq,s]; 3747 } 3748 3749 /** Returns orthogonal matrix U, diagonal matrix S and orthogonal matrix V of singular value decomposition (SVD) of receiver such that this = U*S*V^T. U is computed as orthonormalized iegenvectors of this^T*this, V as orhonormalized eigenvectors of this*this^T and diagonal components of S as square roots of eigenvalues of this^T*this (TODO faster algorithm). this*V = U*S, A^T*U = V*S. Current implementation allows only square matrices (TODO gegeral-shape dmatrices) 3750 * @returns {[Matrix,Vector,Matrix]} [U,S,V] orthogonal matrix U, diagonal of matrix S and orthogonal matrix V of svd decomposition of receiver 3751 * @example 3752 * m1 = mat([[3,2,1],[2,5,2],[1,2,6]]); 3753 * m1 = mat([[1.6,0.6,0.],[-1.2,0.8,0.],[0.,0.,3.]]) 3754 * svd = m1.singularValueDecomposition(); 3755 * u = svd[0]; // u = Matrix([[0.6,0.8,0],[0.8,-0.6,0],[0,0,1]]) 3756 * s = svd[1]; // s = Vector([1,2,3]) 3757 * v = svd[2]; // v = Matrix([[0,1,0],[1,0,0],[0,0,1]]) 3758 * b1 = u.isOrthogonal() // b1 = true 3759 * b2 = v.isOrthogonal() // b2 = true 3760 * c = u.mulm(s.diag()).mulm(v.transposed()); 3761 * // c = mat([[1.6,0.6,0.],[-1.2,0.8,0.],[0.,0.,3.]]) 3762 */ 3763 Matrix.prototype.singularValueDecomposition = function() { 3764 if (!this.isSquare()) { return null; } 3765 var aat = this.mulm(this.transposed()); 3766 var naat = aat.nRows; 3767 var ata = this.transposed().mulm(this); 3768 var nata = ata.nRows; 3769 var aatEig = aat.eigSymmetric({nEigModes:'all'}); 3770 var ataEig = ata.eigSymmetric({nEigModes:'all'}); 3771 var U = Matrix.Zeros(aat.nRows,aat.nCols); 3772 var V = Matrix.Zeros(ata.nRows,ata.nCols); 3773 for (var n=0; n<naat; n++) { U.setCol(n,aatEig[1][n]); } 3774 for (var n=0; n<nata; n++) { V.setCol(n,ataEig[1][n]); } 3775 s = []; 3776 for (var n=0; n<nata; n++) { s[n] = Math.sqrt(ataEig[0][n]); } 3777 return [U,Vector.create(s),V]; 3778 } 3779 3780 /** Alias for singular value decomposition, see {@link Matrix.singularValueDecomposition} 3781 * @function 3782 */ 3783 Matrix.prototype.svd = Matrix.prototype.singularValueDecomposition; 3784 3785 /* * Returns pseudoinverse of receiver 3786 * @param {string} [method="defualt"] method of evaluation. Options are 'defualt' for ( ret = (this^T*this)^-1 * this^T ), 'svd' for SVD decomposition ( this=U*S*V^T -> this^+ = V*S^+*U^T, A^+ means pseudoinverse) 3787 * @param {[Matrix,Matrix,Matrix]} [precompSvd=undefined] precomputed SVD decomposition of receiver 3788 * @returns {Matrix} pseudoinverse of receiver 3789 * @ example 3790 * m1 = mat([[2,-4,5],[6,0,3],[2,-4,5],[6,0,3]]); 3791 * b = vec([1,3,-1,3]); 3792 * m2 = m1.pseudoinverse(); 3793 * // m2 = 3794 * x2 = m2.mulv(b); 3795 * // x2 = 3796 * c2 = m1.mulv(x2) 3797 * // c2 = 3798 * / 3799 pseudoinverse: function(method,precompSvd) { 3800 var method = method || 'defualt' 3801 method = method.toLowerCase(); 3802 //if (method=='default') { 3803 //var t = this.inversed(); 3804 //return (t.mulm(this)).inversed().mulm(t); 3805 //} 3806 if (method=='svd' || method=='default') { 3807 var svd = precompSvd? precompSvd : this.singularValueDecomposition(); 3808 var u = svd[0]; 3809 var s = svd[1]; 3810 var v = svd[2]; 3811 var n = s.nElements; 3812 var ss = new Matrix(n,n); 3813 for (var i=0; i<n; i++) { 3814 if (Math.abs(s[i]) > JSM_TOL) { ss[i][i] = 1./s[i]; } 3815 } 3816 return v.mulm(ss).mulm(u.transposed()); 3817 } 3818 return this.pseudoinverse('default',precompSvd); 3819 } 3820 */ 3821 3822 /** Returns orthogonal matrix R and symmetric matrix U of polar decomposition of the receiver such that this=R*U. The decomposition is computed from SVD decomposition (this = U*S*V^T -> R = U*V^T, U = V*S*V^T 3823 * @param {[Matrix,Matrix,Matrix]} [precompSvd=undefined] precomputed SVD decomposition 3824 * @returns {[Matrix,Matrix]} [R,U] orthogonal matrix R and symmetric matrix U of polar decomposition of receiver 3825 * @example 3826 * m = mat([[1.6,.6,0],[-1.2,0.8,0],[0,0,3]]) 3827 * ru = m.polarDecomposition(); 3828 * r = ru[0]; // r = Matrix([[0.8,0.6,0],[-0.6,0.8,0],[0,0,1]]) 3829 * u = ru[1]; // u = Matrix([[2,0,0],[0,1,0],[0,0,3]]) 3830 * b1 = r.isOrthogonal(); // b1 = true 3831 * b2 = u.isSymmetric(); // b2 = true 3832 * c = r.mulm(u); // c = Matrix([[1.6,.6,0],[-1.2,0.8,0],[0,0,3]]) 3833 */ 3834 Matrix.prototype.polarDecomposition = function(precompSvd) { 3835 if (!this.isSquare) { /*jsmLogger.warn('Matrix.polarDecomposition: matrix is not square');*/ return null; } 3836 var svd = precompSvd? precompSvd : this.singularValueDecomposition(); 3837 var u = svd[0]; 3838 var s = svd[1]; 3839 var v = svd[2]; 3840 return [u.mulm(v.transposed()), v.mulm(s.diag()).mulm(v.transposed())]; 3841 } 3842 3843 /** Returns eigenvalues (L) and eigenvectors (P) of symmetric receiver ( (this-lambda*mat)*phi = 0 ). Eigenvectors are normalized with respect to mat 3844 * @param {Object} params object of parameters 3845 * @param {Matrix} [params.mat=Matrix.Identity] mat in (this-lambda*mat)*phi = 0 ). If mat==Matix.Identity, then the "classical" eigenvalue problem is solved ( (this-lambda*I)*phi = 0 -> this*phi=lambda*phi) 3846 * @param {int|string("all")} [params.nEigModes='all'] number of first eigenvalues and eigenvectors to be returned. if "all" is, then all eigenvalues are returned 3847 * @param {string} [params.method="default"] method of the solution. "default" and "invit" stands for Stodola's inverse iterations methods with Gramm-Schmidt orthogonalization, "subspace" for subspace iterations 3848 @param {bool} [params.highest=true] find params.nEigModes highest eigenvalues if true, params.nEigModes lowest eigenvalues otherwise 3849 * @param {int} [params.maxiter=1000] maximum number of iterations 3850 * @returns {[[floats],[Vectors]]} [[lambda1,lambda2,...,lambdaN],[phi1,phi2,...,phiN]], oi is i-th eigenvalue, vi is i-th eigenvector. N = nEigModes 3851 * // 3852 * @example 3853 * k = mat([[9,3,0,0],[3,8,2,0],[0,2,7,2],[0,0,2,8]]); 3854 * m = Matrix.Diagonal([3,2,4,3]); 3855 * e = k.eigSymmetric({mat:m,nEigModes:2,highest:false}); 3856 * ls = e[0]; 3857 * ps = e[1]; 3858 * l1 = ls[0]; // l1 = 1.2504038497315775 3859 * l2 = ls[1]; // l2 = 2.279120228657416 3860 * p1 = ps[0]; // p1 = Vector([ 0.1288306767139194, -0.22540165581364102, 0.4265175098731745, -0.20077135620035116 ]) 3861 * p2 = ps[1]; // p2 = Vector([ 0.4514687335612619, -0.32545467450354726, -0.11713473474653795, 0.2014979513549984 ]) 3862 * // 3863 * e = k.eigSymmetric({mat:m,nEigModes:2,highest:true,method:'subspace'}); 3864 * ls = e[0]; 3865 * ps = e[1]; 3866 * l3 = ls[1]; // l3 = 2.948867853467429 3867 * l4 = ls[0]; // l4 = 4.938274734810245 3868 * p3 = ps[1]; // p3 = Vector([ 0.14681878659487543, -0.007507144503266177, -0.21233717286242818, -0.5016212772448967 ]) 3869 * p4 = ps[0]; // p4 = Vector([ 0.3022519091588433, 0.585847242190032, 0.09630780190740544, 0.028264211960396433 ]) 3870 * // 3871 * c1 = k.x(p1).sub(m.x(p1).x(l1)); // c1 = Vector([0,0,0,0]) 3872 * c2 = k.x(p2).sub(m.x(p2).x(l2)); // c2 = Vector([0,0,0,0]) 3873 * c3 = k.x(p3).sub(m.x(p3) .x(l3)); // c3 = Vector([0,0,0,0]) 3874 * c4 = k.x(p4).sub(m.x(p4).x(l4)); // c4 = Vector([0,0,0,0]) 3875 */ 3876 Matrix.prototype.eigSymmetric = function(params) { 3877 if (!this.isSquare()) { /*jsmLogger.warn('Matrix.eigSymmetric: matrix is not square');*/ return null; } 3878 var params = params || {}; 3879 var mat = params.mat || Matrix.Identity(this.nRows,this.nCols); 3880 if (!mat.isSquare() || !this.isSameSizeAs(mat)) { /*jsmLogger.warn('Matrix.eigSymmetric: dimensions mismatched');*/ return null; } 3881 var nEigModes = params.nEigModes || this.nRows; // nEigModes first eigenvalues and eigenvectors will be returned 3882 if (nEigModes=="all") { nEigModes = this.nRows; } 3883 else if (nEigModes > this.nRows) { nEigModes = this.nRows; } 3884 var method = params.method || "default"; 3885 var highest = params.highest || false; 3886 var maxiter = params.maxiter || 1000; 3887 if (method=="default" || method=="invit") { // Stodola's inverse iteration with Gramm-Schmidt orthogonalization 3888 var DM = highest? mat.inv().mulm(this) : this.inv().mulm(mat) 3889 var eigVals = []; 3890 var eigVecs = []; 3891 var eigVecsMulmM = []; 3892 var rho, rhoRatio, rhoNew, iter; 3893 var xk = Vector.Zeros(this.nRows); 3894 for (var k=0; k<nEigModes; k++) { 3895 xk.one(); 3896 iter = 0; 3897 rhoNew = 0.; 3898 do { 3899 xk = DM.mulv(xk); 3900 for (var j=0; j<k; j++) { xk.isub(eigVecs[j].mulf(eigVecsMulmM[j].dot(xk))); } // Gramm-Schmidt orthogonalization 3901 xk.imulf(1./xk.energyNorm(mat)); 3902 rho = rhoNew; 3903 rhoNew = xk.squaredEnergyNorm(this) / xk.squaredEnergyNorm(mat) 3904 rhoRatio = Math.abs((rhoNew-rho)/rhoNew); 3905 iter++; 3906 if (iter > maxiter) { 3907 if (rhoRatio > JSM_TOL) { 3908 /*jsmLogger.warn('Matrix.eigSymmetric: maximum number of iterations reached');*/ 3909 return null; 3910 } 3911 break; 3912 } 3913 } while (rhoRatio > JSM_TOL2) 3914 eigVals[k] = rhoNew; 3915 eigVecs[k] = xk.copy(); 3916 eigVecsMulmM[k] = xk.mulm(mat); 3917 } 3918 return [eigVals,eigVecs]; 3919 } 3920 if (method == "subspace") { // subspace iteration 3921 var eigVals = []; 3922 var eigVecs = []; 3923 var n = this.nRows; 3924 var kk, mm, r, q; 3925 var rhos, rhosRatio, rhoNews, maxRhosRatio, iter; 3926 var xk = Vector.Zeros(this.nRows); 3927 x = Matrix.Ones(n,nEigModes); 3928 for (var i=0; i<n; i++) { x.set(i,i,0.); } 3929 lu = highest? mat.luDecomposition() : this.luDecomposition() 3930 rhosNew = []; 3931 for (var n=0; n<nEigModes; n++) { rhosNew[n] = 0.; } 3932 rhosRatio = rhosNew.slice(); 3933 iter = 0; 3934 do { 3935 x = highest? mat.linSolve(this.mulm(x),"lu",false,lu) : this.linSolve(mat.mulm(x),"lu",false,lu) 3936 kk = x.transposed().mulm(this).mulm(x); 3937 mm = x.transposed().mulm(mat).mulm(x); 3938 r = kk.eigSymmetric({mat:mm,nEigModes:nEigModes,highest:highest}); 3939 q = Matrix.Zeros(nEigModes,nEigModes); 3940 for (var j=0; j<nEigModes; j++) { 3941 q.setCol(j,r[1][j]); 3942 } 3943 x = x.mulm(q); 3944 rhos = rhosNew.slice(); 3945 rhosNew = r[0].slice(); 3946 for (var n=0; n<nEigModes; n++) { rhosRatio[n] = Math.abs((rhosNew[n] - rhos[n])/rhosNew[n]); } 3947 maxRhosRatio = 0.; 3948 for (var n=0; n<nEigModes; n++) { 3949 if (rhosRatio[n] > maxRhosRatio) { maxRhosRatio = rhosRatio[n]; } 3950 } 3951 iter++; 3952 if (iter > maxiter) { 3953 if (maxRhosRatio > JSM_TOL) { 3954 return null; 3955 /*jsmLogger.warn('Matrix.eigSymmetric: maximum number of iterations reached');*/ 3956 } 3957 break; 3958 } 3959 } while (maxRhosRatio > JSM_TOL2) 3960 for (var i=0; i<nEigModes; i++) { 3961 eigVals.push(r[0][i]); 3962 eigVecs.push(x.getCol(i)); 3963 } 3964 return [eigVals,eigVecs]; 3965 } 3966 params.method = "default"; 3967 return this.eig(params); 3968 } 3969 3970 /** Returns eigenvalues of receiver (this*x = ret*x) 3971 * @param {string} [method="default"] method of solution. "default and "schur" stand for similar transformation of receiver into schur form 3972 * @returns {[floats]} eigenvalues of receiver 3973 * @example 3974 * m1 = mat([[4,2,3,1],[3,6,5,7],[5,3,8,1],[2,3,4,5]]); 3975 * v1 = m1.schurForm().diag().toArray(); 3976 * // v1 = [15.6072, 4.1916, 2.2012, 1 ] 3977 * v2 = m1.eigVals(); 3978 * // v2 = [15.6072, 4.1916, 2.2012, 1 ] 3979 */ 3980 Matrix.prototype.eigVals = function(method) { 3981 var method = method || "default"; 3982 method = method.toLowerCase(); 3983 if (method=="default" || method=="schur") { 3984 return this.schurForm().diagonalToVector().toArray(); 3985 } 3986 return this.eigVals("default"); 3987 } 3988 3989 /** Returns orthogonal matrix Q and diagonal matrix L of eigendecomposition of the receiver such that this=P*L*P^-1 (obtained from eigenvalue equation this*P = P*L). If receiver is symmetric, P is orthogonal. Columns of P are eigenvectors of this and diagonal elements of L are corresponding eigenvalues 3990 * @param {string} [method="default"] method of eigenvalues solution. See {@link Matrix#eig} 3991 * @returns {[Matrix,Matrix]} [P,L] matrix P and diagonal matrix L of eigendecomposition of receiver 3992 * @example 3993 * m1 = mat([[4,2,3,1],[3,6,5,7],[5,3,8,1],[2,3,4,5]]); 3994 * pl = m1.eigenDecomposition() 3995 * p = pl[0] 3996 * l = pl[1] 3997 * // p = 3998 * // l = 3999 * c = p.mulm(l).mulm(p.inv()) 4000 * // c = 4001 */ 4002 Matrix.prototype.eigenDecomposition = function(method,precompSchurDecomp) { 4003 if (!this.isSquare) { /*jsmLogger.warn('Matrix.eigenDecomposition: matrix is not square');*/ return null; } 4004 var method = method || "default" 4005 if (method=="default") { 4006 var i, j, n=this.nRows; 4007 var sd = precompSchurDecomp? precompSchurDecomp : this.schurDecomposition() 4008 var q = sd[0]; 4009 var s = sd[1]; 4010 var l = new Matrix(n,n); 4011 for (i=0; i<n; i++) { l[i][i] = s[i][i]; } 4012 var p = Matrix.Zeros(n,n); 4013 var t11 = new Matrix(), nn, lambda, v = new Vector(), y = new Vector(n); 4014 p.setCol(0,q.getCol(0)); 4015 for (i=1; i<n; i++) { 4016 ii = range(i); 4017 t11.beSubMatrixOf(s,ii,ii); 4018 lambda = s[i][i]; 4019 v.resize(i); 4020 for (j=0; j<i; j++) { 4021 t11[j][j] -= lambda; 4022 v[j] = s[j][i]; 4023 } 4024 y.beSolutionOf(t11,v); 4025 y.negate(); 4026 y.appendElements(1.); 4027 y.resize(n); 4028 p.setCol(i,q.mulv(y)); 4029 } 4030 return [p,l]; 4031 } 4032 if (method=="symm" || method=="symmeig" || method=="eigsymm" || method=="eigsymmetric") { 4033 var n = this.nRows; 4034 var e = this.eigSymmetric(); 4035 var p = Matrix.Zeros(n,n); 4036 var l = Matrix.Zeros(n,n); 4037 for (var i=0; i<n; i++) { 4038 p.setCol(i,e[1][i]); 4039 l[i][i] = e[0][i]; 4040 } 4041 return [p,l] 4042 } 4043 return this.eigenDecomposition("default") 4044 } 4045 4046 4047 /** Returns spectral decomposition of receiver, identical to {@link Matrix#eigenDecomposition} 4048 * @function 4049 */ 4050 Matrix.prototype.spectralDecomposition = Matrix.prototype.eigenDecomposition; 4051 4052 /* * Returns generalized eigenvalues of receiver (this*x = ret*mat*x) 4053 * @param {Matrix} mat matrix in generalized eigenvalue equation this*x = ret*mat*x 4054 * @param {string} [method="default"] method of solution. "default and "inv" stand for solution mat-1*this*x = ret*x (standard eigenvalue problem), "qz" for qz decomposition 4055 * @returns {[floats]} eigenvalues of receiver 4056 * @ example 4057 * / 4058 generalizedEigVals: function(mat,method) { 4059 var method = method || "default"; 4060 method = method.toLowerCase(); 4061 if (method=="default" || method=="inv") { 4062 return mat.inv().mulm(this).eigVals(); 4063 } 4064 if (method=="qz" || method=="qzdecomposition") { 4065 var qz = this.qzDecomposition(mat); 4066 //TODO 4067 return null; 4068 } 4069 return this.generalizedEigVals(mat); 4070 } 4071 */ 4072 4073 /** Returns inversion of receiver 4074 * @param {string} [method="default"] method used for solution. Options are "default" and "gaussjordan" for Gauss-Jordan method, "lu" for solution with LU decomposition, "eigen" with eigen decomposition, "symmeigen" with eigen decomposition when receiver is symmetric, "svd" for SVD decomposition 4075 * @param {[Matrices]} [precompDecomps=undefined] precomputed matrix decompositions if we want to use some 4076 * @returns {Matrix} inversion of receiver 4077 * @example 4078 * a = mat([[1,2,2],[1,0,1],[1,2,1]]); 4079 * i1 = a.inversed(); // i1 = 4080 * c = a.mulm(i1) // c = Matrix([[1,0,0],[0,1,0],[0,0,1]]) 4081 * i2 = a.inversed('lu') // i2 = Matrix([[-1,1,1],[0,-0.5,0.5],[1,0,-1]]) 4082 * i3 = a.inversed('eigen') // i3 = Matrix([[-1,1,1],[0,-0.5,0.5],[1,0,-1]]) 4083 * i4 = a.inversed('svd') // i4 = Matrix([[-1,1,1],[0,-0.5,0.5],[1,0,-1]]) 4084 * check = a.x(i1); // check = Matrix([[1,0,0],[0,1,0],[0,0,1]]) 4085 */ 4086 Matrix.prototype.inversed = function(method,precompDecomps) { 4087 if (!this.isSquare) { /*jsmLogger.warn('Matrix.inversed: matrix is not square');*/ return null; } 4088 var method = method || "default"; 4089 method = method.toLowerCase(); 4090 if (method=="default" || method=="gaussjordan") { 4091 /* solves this*ret = I with Gauss-Jordan ellimination*/ 4092 return this.gaussJordanElimination(Matrix.Identity(this.nRows,this.nRows)); 4093 } 4094 if (method=="lu") { 4095 /* solves this*ret[:,i] = I[:,i] for i=0,1,..,this.nRows with the help of LU decomposition*/ 4096 var lu = precompDecomps? precompDecomps : this.luDecomposition(); 4097 var n = this.nRows; 4098 var ret = new Matrix(n,n); 4099 var vec = Vector.Zeros(n); 4100 for (var i=0; i<n; i++) { 4101 vec.zero(); 4102 vec[i] = 1.; 4103 this.linSolve(vec,"lu",false,lu); 4104 ret.setCol(i,vec) 4105 } 4106 return ret; 4107 } 4108 if (method=="chol" || method=='cholesky') { 4109 /* solves this*ret[:,i] = I[:,i] for i=0,1,..,this.nRows with the help of Cholesky decomposition*/ 4110 var l = precompDecomps? precompDecomps : this.choleskyDecomposition(); 4111 if (l==null) { return null; } 4112 var n = this.nRows; 4113 var ret = new Matrix(n,n); 4114 var vec = Vector.Zeros(n); 4115 for (var i=0; i<n; i++) { 4116 vec.zero(); 4117 vec[i] = 1.; 4118 this.linSolve(vec,"cholesky",false,l); 4119 ret.setCol(i,vec) 4120 } 4121 return ret; 4122 } 4123 if (method=="eigen" || method=="eig") { 4124 /* this = V*L*V^-1 -> this^-1=V*L^-1*V^-1. As L is diagonal, the inversion is easy to compute*/ 4125 var e = precompDecomps? precompDecomps : this.eigenDecomposition(); 4126 var p = e[0]; 4127 var l = e[1]; 4128 for (var i=0; i<this.nRows; i++) { l[i][i] = 1./l[i][i]; } 4129 return p.mulm(l).mulm(p.inversed()); 4130 } 4131 if (method=="symmeig" || method=="eigsymm" || method=="eigsymmetric") { 4132 var e = precompDecomps? precompDecomps : this.eigenDecomposition("symm"); 4133 var p = e[0]; 4134 var l = e[1]; 4135 for (var i=0; i<this.nRows; i++) { l[i][i] = 1./l[i][i]; } 4136 return p.mulm(l).mulm(p.transposed()); 4137 } 4138 if (method=="svd") { 4139 svd = precompDecomps? precompDecomps : this.singularValueDecomposition(); 4140 var u=svd[0], s=svd[1], v=svd[2]; 4141 var n = s.nElements; 4142 var ss = new Matrix(n,n); 4143 for (var i=0; i<n; i++) { if (s[i] > JSM_TOL) { ss[i][i] = 1./s[i]; } } 4144 return v.mulm(ss).mulm(u.transposed()); 4145 } 4146 return this.inversed("default"); 4147 } 4148 4149 /** Alias for inversion, see {@link Matrix#inversed} 4150 * @function 4151 */ 4152 Matrix.prototype.inv = Matrix.prototype.inversed; 4153 4154 /** Sets receiver as an inversion of given matrix 4155 * @param {Matrices} mat given matrix 4156 * @param {string} [method="default"] see {@link Matrix#inversed} for details} 4157 * @param {[Matrices]} [precompDecomps=undefined] precomputed matrix decompositions if we want to use some 4158 * @example 4159 * m1 = mat([[1],[2]]); 4160 * m2 = mat([[1,2,2],[1,0,1],[1,2,1]]); 4161 * m1.beInverseOf(m2); // m1 = Matrix([[2.3,-1.4,-0.8],[-1.4,1.2,0.4],[-0.8,0.4,0.8]]) 4162 */ 4163 Matrix.prototype.beInverseOf = function(mat,method,precompDecomps) { 4164 if (!mat.isSquare()) { /*jsmLogger.warn('Matrix.beInverseOf: matrix is not square');*/ return; } 4165 var method = method || "default"; 4166 method = method.toLowerCase(); 4167 if (method=="default" || method=="gaussjordan") { 4168 var temp = mat.copy(); 4169 this.beUnitMatrix(mat.nRows); 4170 temp.gaussJordanElimination(this,"nopivot",false) 4171 return 4172 } else { // TODO 4173 this.beCopyOf(mat.inversed(method,precompDecomps)); 4174 } 4175 this.beInverseOf(mat,"default",precompDecomps); 4176 } 4177 4178 /** Returns determinant of receiver. 4179 * @param {string} [method="default"] used method. Options are "default" and "lu" for using LU decomposition, "schurForm" or "schur" for using schur form, "schurDecomposition" for using schur decomposition, "eigen" or "eigenDecomposition" for using eigendecomposition 4180 * @param {[Matrices]} precompDecomps precomputed matrix decompositions if we want to use some 4181 * @returns {float} determinant of receiver 4182 * @example 4183 * a = mat([[4,2,1],[2,5,3],[1,3,6]]); 4184 * d = a.determinant(); // d = 67 4185 * d = a.determinant('schur'); // d = 67 4186 * d = a.determinant('schurdecomposition'); // d = 67 4187 * d = a.determinant('eigen'); // d = 67 4188 */ 4189 Matrix.prototype.determinant = function(method,precompDecomps) { 4190 if (!this.isSquare()) { /*jsmLogger.warn('Matrix.determinant: matrix is not square');*/ return null; } 4191 var method = method || "default" 4192 method = method.toLowerCase(); 4193 var mats = precompDecomps || undefined 4194 if (method=="default" || method=="lu") { 4195 // transform matrix to lower and upper triangular. Determinant is then product of diagonal elements. As L has 1. on diagonal, det is product of diagnal elements of U only 4196 u = precompDecomps? precompDecomps[1] : this.luDecomposition()[1]; 4197 return u.diagProduct(); 4198 } 4199 if (method=="schurform" || method=="schur") { 4200 // create schur form (upper triangular matrix with eigenvalues on diagonal). It is triangular, so det is product of diagonal elements (as well as product of eigenvalues) 4201 var s = precompDecomps? precompDecomps : this.schurForm(); 4202 return s.diagProduct(); 4203 } 4204 if (method=="schurdecomposition") { 4205 // see case:"schurform" 4206 var s = precompDecomps? precompDecomps : this.schurDecomposition()[1]; 4207 return s.diagProduct(); 4208 } 4209 if (method=="eigen" || method=="eigendecomposition") { 4210 var l = precompDecomps? precompDecomps[1] : this.eigenDecomposition()[1] 4211 return l.diagProduct(); 4212 } 4213 return this.determinant("default",precompDecomps); 4214 } 4215 4216 /** Alias for determinant, see {@link Matrix#determinant} 4217 * @function 4218 */ 4219 Matrix.prototype.det = Matrix.prototype.determinant; 4220 4221 /** Alias for determinant, see {@link Matrix#determinant} 4222 * @function 4223 */ 4224 Matrix.prototype.giveDeterminant = Matrix.prototype.determinant; 4225 4226 /** Constructs new Matrix from given 2D array 4227 * @param {[floats](2D)} [arry=[]] array containing elements of new matrix 4228 * @returns {Matrix} new Matrix object 4229 * @example 4230 * m = Matrix.create([[1,2,3],[4,5,6],[7,8,9]]) // m = Matrix([[1,2,3],[4,5,6],[7,8,9]]) 4231 */ 4232 Matrix.create = function(arry) { 4233 if (arry==undefined) { return new Matrix(); } 4234 if (arry.length==0) { return new Matrix(); } 4235 var a = arry; 4236 var nRows = a.length, nCols = a[0].length, r, c; 4237 ret = new Matrix(); 4238 for (var r=0; r<nRows; r++) { 4239 ret[r] = {}; 4240 for (var c=0; c<nCols; c++) { 4241 ret[r][c] = arry[r][c]; 4242 } 4243 } 4244 ret.nRows = nRows; 4245 ret.nCols = nCols; 4246 return ret; 4247 } 4248 4249 /** Constructs new diagonal matrix from given 1D array 4250 * @param {[floats]|Vector} arry array or vector containing elements of new matrix 4251 * @returns {Matrix} new Matrix object 4252 * @example 4253 * m1 = Matrix.Diagonal([1,2,3]); // m1 = Matrix([[1,0,0],[0,2,0],[0,0,3]]) 4254 * m2 = Matrix.Diagonal(vec([1,2,3])); // m2 = Matrix([[1,0,0],[0,2,0],[0,0,3]]) 4255 */ 4256 Matrix.Diagonal = function(arry) { 4257 var s = arry.nElements==undefined? arry.length : arry.nElements; 4258 var ret = new Matrix(s,s); 4259 for (var i=0; i<s; i++) { 4260 ret[i][i] = arry[i]; 4261 } 4262 return ret; 4263 } 4264 4265 /** Constructs new identity matrix of given size 4266 * @param {int} size size of returned matrix 4267 * @returns {Matrix} identity matri 4268 * @example 4269 * m = Matrix.Identity(4) // m = Matrix([[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]) 4270 */ 4271 Matrix.Identity = function(size) { 4272 var ret = new Matrix(size,size); 4273 for (var i=0; i<size; i++) { 4274 ret[i][i] = 1.; 4275 } 4276 return ret; 4277 } 4278 4279 /** Alias for Matrix.Identity(), see {@link Matrix#Identity} 4280 * @function 4281 */ 4282 Matrix.I = Matrix.Identity; 4283 4284 /** Alias for Matrix.Identity(), see {@link Matrix#Identity} 4285 * @function 4286 */ 4287 Matrix.UnitMatrix = Matrix.Identity; 4288 4289 /** Creates a matrix of given size full of zeros 4290 * @param {int} [nRows=0] number of rows 4291 * @param {int} [nCols=0] number of columns 4292 * @returns {Matrix} new matrix full of zero 4293 * @example 4294 * m = Matrix.Zeros(2,3); // m = Matrix([[0,0,0],[0,0,0]]) 4295 */ 4296 Matrix.Zeros = function(nRows,nCols) { 4297 return new Matrix(nRows,nCols); 4298 } 4299 4300 /** Creates a matrix of given size full of ones 4301 * @param {int} [nRows=0] number of rows 4302 * @param {int} [nCols=0] number of columns 4303 * @returns {Matrix} new vector full of zero 4304 * @example 4305 * m = Matrix.Ones(2,3); // m = Matrix([[1,1,1],[1,1,1]]) 4306 */ 4307 Matrix.Ones = function(nRows,nCols) { 4308 var nRows = nRows || 0; 4309 var nCols = nCols || 0; 4310 var ret = new Matrix(); 4311 for (var r=0; r<nRows; r++) { 4312 ret[r] = {}; 4313 for (var c=0; c<nCols; c++) { 4314 ret[r][c] = 1.; 4315 } 4316 } 4317 ret.nRows = nRows; 4318 ret.nCols = nCols; 4319 return ret; 4320 } 4321 4322 /** Creates a Householder matrix from given vector (ret = I - 2*v*v^T/(v^T*v). Householder matrix is symmetric, orthogonal and involutary 4323 * @param {Vector} vec=0 Householder vector to form Householder matrix 4324 * @param {Matrix} [precompI=undefined] precomputed identity matrix to save some time of creation 4325 * @returns {Matrix} Householder matrix 4326 * @example 4327 * m1 = Matrix.Householder(vec([0,1,0])) 4328 * // m1 = Matrix([[1,0,0],[0,-1,0],[0,0,1]]) 4329 * b1 = m1.isSymmetric() // b1 = true 4330 * b2 = m1.isOrthogonal() // b2 = true 4331 * b3 = m1.isInvolutary() // b3 = true 4332 * m2 = mat([[1,2,3],[4,5,6],[7,8,9]]); 4333 * v = m2.getCol(0); 4334 * q = Matrix.Householder(v.sub(vec([1,0,0]).mulf(v.norm()))); 4335 * m3 = q.mulm(m2); 4336 * // m3 = 4337 */ 4338 Matrix.Householder = function(vec,precompI) { 4339 var nElements = vec.nElements==undefined? vec.length : vec.nElements; 4340 return (precompI? precompI : Matrix.Identity(nElements)).sub(vec.dyadic(vec).mulf(2./vec.dot(vec))); 4341 } 4342 4343 4344 4345 4346 4347 4348 4349 /* ************************************************** 4350 * 4351 * Initializing functions (shotcuts constructors) 4352 * 4353 ***************************************************/ 4354 4355 /** Returns new Matrix object from given array 4356 * @param {[floats](2D)} [arry=[]] array containing matrix elements 4357 * @returns {Matrix} new Matrix object 4358 * @example 4359 * m = mat([[1,2,3],[4,5,6],[7,8,9]]) // m = Matrix([[1,2,3],[4,5,6],[7,8,9]]) 4360 */ 4361 mat = Matrix.create; 4362 4363 /** Returns new Vector object from given array 4364 * @param {[floats]} [arry=[]] array containing vector elements 4365 * @returns {Vector} new Vector object 4366 * @example 4367 * v = vec([1,2,3,4]) // v = Vector([1,2,3,4]) 4368 */ 4369 vec = Vector.create; 4370 4371 /** Returns new Vector or Matrix full of given size full of zeros 4372 * @param {int} nRows number of rows of returned object 4373 * @param {int} [nCols] if specified, new Matrix of size (nRows,nCols) is returned, new vector of size nRows otherwise 4374 * @returns {Matrix|Vector} new Matrix or Vector object 4375 * @example 4376 * m = zeros(2,3); // m = Matrix([[0,0,0],[0,0,0]]) 4377 * v = zeros(4) // v = Vector([0,0,0,0]) 4378 */ 4379 zeros = function(nRows,nCols) { 4380 if (nCols) { return Matrix.Zeros(nRows,nCols); } 4381 if (nRows) { return Vector.Zeros(nRows); } 4382 return null; 4383 } 4384 4385 /** Returns new Vector or Matrix full of given size full of ones 4386 * @param {int} nRows number of rows of returned object 4387 * @param {int} [nCols] if specified, new Matrix of size (nRows,nCols) is returned, new vector of size nRows otherwise 4388 * @returns {Matrix|Vector} new Matrix or Vector object 4389 * @example 4390 * m = zeros(2,3); // m = Matrix([[1,1,1],[1,1,1]]) 4391 * v = zeros(4) // v = Vector([1,1,1,1]) 4392 */ 4393 ones = function(nRows,nCols) { 4394 if (nCols) { return Matrix.Ones(nRows,nCols); } 4395 if (nRows) { return Vector.Ones(nRows); } 4396 return null; 4397 } 4398 4399 /** Returned identity matrix of given size, see {@link Matrix#Identity} 4400 * @param {int} size number of rows and columns of returned matrix 4401 * @returns {Matrix} nRows x nRows identity matrix 4402 * @example 4403 * m = identity(3); // m = Matrix([[1,0,0],[0,1,0],[0,0,1]]) 4404 */ 4405 identity = Matrix.Identity; 4406 4407 /** Alias for dentity(), see {@link identity} 4408 */ 4409 eye = Matrix.Identity; 4410 4411 /** Alias for dentity(), see {@link identity} 4412 */ 4413 unitMatrix = Matrix.Identity; 4414 4415 4416 /* *********************************************************** 4417 * 4418 * Other functions 4419 * 4420 ************************************************************/ 4421 4422 /** Linear system equation solver. Returns vector x as a solution of a*ret=rhs, see {@link Matrix#linSolve} for input desription 4423 * @param {Matrix} a 4424 * @param {Vector|[Vectors]|Matrix} rhs 4425 * @param {bool} [saveOrig=true] 4426 * @param {int} [method="default"] 4427 * @param {[Matrices]} [precompDecomps] 4428 * @returns {Vector|[Vectors]|Matrix} solution x of a*x=rhs 4429 */ 4430 linSolve = function(a,rhs,saveOrig,method,precompDecomps) { return a.linSolve(rhs,saveOrig,method,precompDecomps); } 4431 4432 4433 /** Eigenvalues and eigenvectors solver. Returns first nEigModes eigenvales (lambda) and eigenvectors (phi) of problem mat1*phi=lambda*mat2*phi. See {@link Matrix#eig} for input description. Eigenvectors are normalized with respect to mat2 4434 * @param {Object} params object of parameters 4435 * @param {Matrix} params.mat1 4436 * @param {Matrix} [params.mat2=Matrix.Identity] 4437 * @param {int} [params.nEigModes=1] 4438 * @param {int} [method="default"] 4439 @param {bool} [params.highest=false] 4440 * @param {int} [params.maxiter=1000] 4441 * @returns {[[floats],[Vectors]]} [[lambda1,lambda2,...,lambdaN],[phi1,phi2,...,phiN]], lambdai is i-th eigenvale, phii is i-th eigenvector. N = nEigModes 4442 */ 4443 eigSolve = function(params) { return params.mat1.eig({mat:params.mat2,nEigModes:params.nEigModes,method:params.method,highest:params.highest,maxiter:params.maxiter}); } 4444 4445 4446 4447 /** Returns sequence (array) with start, stop, step. Inspired by <a href="www.python.org">Python</a> syntax 4448 * @param {float|int} a start/stop (according to b, see example) 4449 * @param {float|int} [b] stop 4450 * @param {float|int} [c=1] step 4451 * @returns {[floats]} array containing given sequence 4452 * @example 4453 * r = range(8); // r = [0,1,2,3,4,5,6,7] 4454 * r = range(2,8); // r = [2,3,4,5,7] 4455 * r = range(2,8,2); // r = [2,4,6] 4456 */ 4457 range = function(a,b,c) { 4458 var start; 4459 var step; 4460 var stop; 4461 if (b==null && c==null) { 4462 start = 0; 4463 stop = a; 4464 step = 1; 4465 } else if (c==null) { 4466 start = a; 4467 stop = b; 4468 step = 1; 4469 } else { 4470 start = a; 4471 stop = b; 4472 step = c; 4473 } 4474 var ret = [] 4475 var val = start; 4476 while (val < stop) { 4477 ret.push(val); 4478 val += step; 4479 } 4480 return ret; 4481 } 4482 4483 /**@ignore*/ 4484 Array.prototype.indexOf = function(val) { 4485 for (var i=0; i<this.length; i++) { 4486 if (this[i] === val) { return i; } 4487 } 4488 } 4489