001/* A library for mathematical operations on matrices of floats. 002 003 Some algorithms are from 004 005 [1] Embree, Paul M. and Bruce Kimble. "C Language Algorithms for Digital 006 Signal Processing". Prentice Hall. Englewood Cliffs, NJ, 1991. 007 008 Copyright (c) 1998-2013 The Regents of the University of California. 009 All rights reserved. 010 011 Permission is hereby granted, without written agreement and without 012 license or royalty fees, to use, copy, modify, and distribute this 013 software and its documentation for any purpose, provided that the above 014 copyright notice and the following two paragraphs appear in all copies 015 of this software. 016 017 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 018 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 019 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 020 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 021 SUCH DAMAGE. 022 023 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 024 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 025 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 026 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 027 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 028 ENHANCEMENTS, OR MODIFICATIONS. 029 030 PT_COPYRIGHT_VERSION_2 031 COPYRIGHTENDKEY 032 033 */ 034package ptolemy.math; 035 036////////////////////////////////////////////////////////////////////////// 037//// FloatMatrixMath 038 039/** 040 This class provides a library for mathematical operations on 041 matrices of floats. 042 043 Rows and column numbers of matrices are specified with zero-based indices. 044 045 All calls expect matrix arguments to be non-null. In addition, all 046 rows of the matrix are expected to have the same number of columns. 047 048 @author Jeff Tsay 049 @version $Id$ 050 @since Ptolemy II 1.0 051 @Pt.ProposedRating Yellow (ctsay) 052 @Pt.AcceptedRating Yellow (ctsay) 053 */ 054public class FloatMatrixMath { 055 // private constructor prevents construction of this class. 056 private FloatMatrixMath() { 057 } 058 059 /** Return a new matrix that is constructed from the argument by 060 * adding the second argument to every element. 061 * @param matrix A matrix of floats. 062 * @param z The float number to add. 063 * @return A new matrix of floats. 064 */ 065 public static final float[][] add(float[][] matrix, float z) { 066 float[][] returnValue = new float[_rows(matrix)][_columns(matrix)]; 067 068 for (int i = 0; i < _rows(matrix); i++) { 069 for (int j = 0; j < _columns(matrix); j++) { 070 returnValue[i][j] = matrix[i][j] + z; 071 } 072 } 073 074 return returnValue; 075 } 076 077 /** Return a new matrix that is constructed from the argument by 078 * adding the second matrix to the first one. If the two 079 * matrices are not the same size, throw an 080 * IllegalArgumentException. 081 * @param matrix1 The first matrix of floats. 082 * @param matrix2 The second matrix of floats. 083 * @return A new matrix of floats. 084 */ 085 public static final float[][] add(final float[][] matrix1, 086 final float[][] matrix2) { 087 _checkSameDimension("add", matrix1, matrix2); 088 089 float[][] returnValue = new float[_rows(matrix1)][_columns(matrix1)]; 090 091 for (int i = 0; i < _rows(matrix1); i++) { 092 for (int j = 0; j < _columns(matrix1); j++) { 093 returnValue[i][j] = matrix1[i][j] + matrix2[i][j]; 094 } 095 } 096 097 return returnValue; 098 } 099 100 /** Return a new matrix that is a copy of the matrix argument. 101 * @param matrix A matrix of floats. 102 * @return A new matrix of floats. 103 */ 104 public static final float[][] allocCopy(final float[][] matrix) { 105 return crop(matrix, 0, 0, _rows(matrix), _columns(matrix)); 106 } 107 108 /** Return a new array that is formed by applying an instance of a 109 * FloatBinaryOperation to each element in the input matrix, 110 * using z as the left operand in all cases and the matrix elements 111 * as the right operands (op.operate(z, matrix[i][j])). 112 */ 113 public static final float[][] applyBinaryOperation(FloatBinaryOperation op, 114 final float z, final float[][] matrix) { 115 int rows = _rows(matrix); 116 int columns = _columns(matrix); 117 118 float[][] returnValue = new float[rows][columns]; 119 120 for (int i = 0; i < rows; i++) { 121 for (int j = 0; j < columns; j++) { 122 returnValue[i][j] = op.operate(z, matrix[i][j]); 123 } 124 } 125 126 return returnValue; 127 } 128 129 /** Return a new array that is formed by applying an instance of a 130 * FloatBinaryOperation to each element in the input matrix, 131 * using the matrix elements as the left operands and z as the right 132 * operand in all cases (op.operate(matrix[i][j], z)). 133 */ 134 public static final float[][] applyBinaryOperation(FloatBinaryOperation op, 135 final float[][] matrix, final float z) { 136 int rows = _rows(matrix); 137 int columns = _columns(matrix); 138 139 float[][] returnValue = new float[rows][columns]; 140 141 for (int i = 0; i < rows; i++) { 142 for (int j = 0; j < columns; j++) { 143 returnValue[i][j] = op.operate(matrix[i][j], z); 144 } 145 } 146 147 return returnValue; 148 } 149 150 /** Return a new array that is formed by applying an instance of a 151 * FloatBinaryOperation to the two matrices, element by element, 152 * using the elements of the first matrix as the left operands 153 * and the elements of the second matrix as the right operands. 154 * (op.operate(matrix1[i][j], matrix2[i][j])). If the matrices 155 * are not the same size, throw an IllegalArgumentException. 156 */ 157 public static final float[][] applyBinaryOperation(FloatBinaryOperation op, 158 final float[][] matrix1, final float[][] matrix2) { 159 int rows = _rows(matrix1); 160 int columns = _columns(matrix1); 161 162 _checkSameDimension("applyBinaryOperation", matrix1, matrix2); 163 164 float[][] returnValue = new float[rows][columns]; 165 166 for (int i = 0; i < rows; i++) { 167 for (int j = 0; j < columns; j++) { 168 returnValue[i][j] = op.operate(matrix1[i][j], matrix2[i][j]); 169 } 170 } 171 172 return returnValue; 173 } 174 175 /** Return a new array that is formed by applying an instance of a 176 * FloatUnaryOperation to each element in the input matrix 177 * (op.operate(matrix[i][j])). 178 */ 179 public static final float[][] applyUnaryOperation( 180 final FloatUnaryOperation op, final float[][] matrix) { 181 int rows = _rows(matrix); 182 int columns = _columns(matrix); 183 184 float[][] returnValue = new float[rows][columns]; 185 186 for (int i = 0; i < rows; i++) { 187 for (int j = 0; j < columns; j++) { 188 returnValue[i][j] = op.operate(matrix[i][j]); 189 } 190 } 191 192 return returnValue; 193 } 194 195 /** Return a new matrix that is a sub-matrix of the input 196 * matrix argument. The row and column from which to start 197 * and the number of rows and columns to span are specified. 198 * @param matrix A matrix of floats. 199 * @param rowStart An int specifying which row to start on. 200 * @param colStart An int specifying which column to start on. 201 * @param rowSpan An int specifying how many rows to copy. 202 * @param colSpan An int specifying how many columns to copy. 203 */ 204 public static final float[][] crop(final float[][] matrix, 205 final int rowStart, final int colStart, final int rowSpan, 206 final int colSpan) { 207 float[][] returnValue = new float[rowSpan][colSpan]; 208 209 for (int i = 0; i < rowSpan; i++) { 210 System.arraycopy(matrix[rowStart + i], colStart, returnValue[i], 0, 211 colSpan); 212 } 213 214 return returnValue; 215 } 216 217 /** Return the determinant of a square matrix. 218 * If the matrix is not square, throw an IllegalArgumentException. 219 * This algorithm uses LU decomposition, and is taken from [1] 220 */ 221 public static final float determinant(final float[][] matrix) { 222 _checkSquare("determinant", matrix); 223 224 float[][] a; 225 float det = 1.0f; 226 int n = _rows(matrix); 227 228 a = allocCopy(matrix); 229 230 for (int pivot = 0; pivot < n - 1; pivot++) { 231 // find the biggest absolute pivot 232 float big = Math.abs(a[pivot][pivot]); 233 int swapRow = 0; // initialize for no swap 234 235 for (int row = pivot + 1; row < n; row++) { 236 float absElement = Math.abs(a[row][pivot]); 237 238 if (absElement > big) { 239 swapRow = row; 240 big = absElement; 241 } 242 } 243 244 // unless swapRow is still zero we must swap two rows 245 if (swapRow != 0) { 246 float[] aPtr = a[pivot]; 247 a[pivot] = a[swapRow]; 248 a[swapRow] = aPtr; 249 250 // change sign of determinant because of swap 251 det *= -a[pivot][pivot]; 252 } else { 253 // calculate the determinant by the product of the pivots 254 det *= a[pivot][pivot]; 255 } 256 257 // if almost singular matrix, give up now 258 // FIXME use epsilon instead of this ugly constant 259 if (Math.abs(det) <= 1E-12f) { 260 return det; 261 } 262 263 float pivotInverse = 1.0f / a[pivot][pivot]; 264 265 for (int col = pivot + 1; col < n; col++) { 266 a[pivot][col] *= pivotInverse; 267 } 268 269 for (int row = pivot + 1; row < n; row++) { 270 float temp = a[row][pivot]; 271 272 for (int col = pivot + 1; col < n; col++) { 273 a[row][col] -= a[pivot][col] * temp; 274 } 275 } 276 } 277 278 // last pivot, no reduction required 279 det *= a[n - 1][n - 1]; 280 281 return det; 282 } 283 284 /** Return a new matrix that is constructed by placing the 285 * elements of the input array on the diagonal of the square 286 * matrix, starting from the top left corner down to the bottom 287 * right corner. All other elements are zero. The size of of the 288 * matrix is n x n, where n is the length of the input array. 289 */ 290 public static final float[][] diag(final float[] array) { 291 int n = array.length; 292 293 float[][] returnValue = new float[n][n]; 294 295 // assume the matrix is zero-filled 296 for (int i = 0; i < n; i++) { 297 returnValue[i][i] = array[i]; 298 } 299 300 return returnValue; 301 } 302 303 /** Return a new matrix that is constructed from the argument by 304 * dividing the second argument to every element. 305 * @param matrix A matrix of floats. 306 * @param z The float number to divide. 307 * @return A new matrix of floats. 308 */ 309 public static final float[][] divide(float[][] matrix, float z) { 310 float[][] returnValue = new float[_rows(matrix)][_columns(matrix)]; 311 312 for (int i = 0; i < _rows(matrix); i++) { 313 for (int j = 0; j < _columns(matrix); j++) { 314 returnValue[i][j] = matrix[i][j] / z; 315 } 316 } 317 318 return returnValue; 319 } 320 321 /** Return a new matrix that is constructed by element by element 322 * division of the two matrix arguments. Each element of the 323 * first matrix is divided by the corresponding element of the 324 * second matrix. If the two matrices are not the same size, 325 * throw an IllegalArgumentException. 326 */ 327 public static final float[][] divideElements(final float[][] matrix1, 328 final float[][] matrix2) { 329 int rows = _rows(matrix1); 330 int columns = _columns(matrix1); 331 332 _checkSameDimension("divideElements", matrix1, matrix2); 333 334 float[][] returnValue = new float[rows][columns]; 335 336 for (int i = 0; i < rows; i++) { 337 for (int j = 0; j < columns; j++) { 338 returnValue[i][j] = matrix1[i][j] / matrix2[i][j]; 339 } 340 } 341 342 return returnValue; 343 } 344 345 /** Return a new array that is filled with the contents of the matrix. 346 * The floats are stored row by row, i.e. using the notation 347 * (row, column), the entries of the array are in the following order 348 * for a (m, n) matrix : 349 * (0, 0), (0, 1), (0, 2), ... , (0, n-1), (1, 0), (1, 1), ..., (m-1)(n-1) 350 * @param matrix A matrix of floats. 351 * @return A new array of floats. 352 */ 353 public static final float[] fromMatrixToArray(final float[][] matrix) { 354 return fromMatrixToArray(matrix, _rows(matrix), _columns(matrix)); 355 } 356 357 /** Return a new array that is filled with the contents of the matrix. 358 * The maximum numbers of rows and columns to copy are specified so 359 * that entries lying outside of this range can be ignored. The 360 * maximum rows to copy cannot exceed the number of rows in the matrix, 361 * and the maximum columns to copy cannot exceed the number of columns 362 * in the matrix. 363 * The floats are stored row by row, i.e. using the notation 364 * (row, column), the entries of the array are in the following order 365 * for a matrix, limited to m rows and n columns : 366 * (0, 0), (0, 1), (0, 2), ... , (0, n-1), (1, 0), (1, 1), ..., (m-1)(n-1) 367 * @param matrix A matrix of floats. 368 * @return A new array of floats. 369 */ 370 public static final float[] fromMatrixToArray(final float[][] matrix, 371 int maxRow, int maxCol) { 372 float[] returnValue = new float[maxRow * maxCol]; 373 374 for (int i = 0; i < maxRow; i++) { 375 System.arraycopy(matrix[i], 0, returnValue, i * maxCol, maxCol); 376 } 377 378 return returnValue; 379 } 380 381 /** Return a new matrix, which is defined by Aij = 1/(i+j+1), 382 * the Hilbert matrix. The matrix is square with one 383 * dimension specifier required. This matrix is useful because it always 384 * has an inverse. 385 */ 386 public static final float[][] hilbert(final int dim) { 387 float[][] returnValue = new float[dim][dim]; 388 389 for (int i = 0; i < dim; i++) { 390 for (int j = 0; j < dim; j++) { 391 returnValue[i][j] = 1.0f / (i + j + 1); 392 } 393 } 394 395 return returnValue; 396 } 397 398 /** Return an new identity matrix with the specified dimension. The 399 * matrix is square, so only one dimension specifier is needed. 400 */ 401 public static final float[][] identity(final int dim) { 402 float[][] returnValue = new float[dim][dim]; 403 404 // we rely on the fact Java fills the allocated matrix with 0's 405 for (int i = 0; i < dim; i++) { 406 returnValue[i][i] = 1.0f; 407 } 408 409 return returnValue; 410 } 411 412 /** Return a new matrix that is constructed by inverting the input 413 * matrix. If the input matrix is singular, null is returned. 414 * This method is from [1] 415 */ 416 public static final float[][] inverse(final float[][] A) { 417 _checkSquare("inverse", A); 418 419 int n = _rows(A); 420 421 float[][] Ai = allocCopy(A); 422 423 // We depend on each of the elements being initialized to 0 424 int[] pivotFlag = new int[n]; 425 int[] swapCol = new int[n]; 426 int[] swapRow = new int[n]; 427 428 int irow = 0; 429 int icol = 0; 430 431 for (int i = 0; i < n; i++) { // n iterations of pivoting 432 433 // find the biggest pivot element 434 float big = 0.0f; 435 436 for (int row = 0; row < n; row++) { 437 if (pivotFlag[row] == 0) { 438 for (int col = 0; col < n; col++) { 439 if (pivotFlag[col] == 0) { 440 float absElement = Math.abs(Ai[row][col]); 441 442 if (absElement >= big) { 443 big = absElement; 444 irow = row; 445 icol = col; 446 } 447 } 448 } 449 } 450 } 451 452 pivotFlag[icol]++; 453 454 // swap rows to make this diagonal the biggest absolute pivot 455 if (irow != icol) { 456 for (int col = 0; col < n; col++) { 457 float temp = Ai[irow][col]; 458 Ai[irow][col] = Ai[icol][col]; 459 Ai[icol][col] = temp; 460 } 461 } 462 463 // store what we swapped 464 swapRow[i] = irow; 465 swapCol[i] = icol; 466 467 // if the pivot is zero, the matrix is singular 468 if (Ai[icol][icol] == 0.0f) { 469 return null; 470 } 471 472 // divide the row by the pivot 473 float pivotInverse = 1.0f / Ai[icol][icol]; 474 Ai[icol][icol] = 1.0f; // pivot = 1 to avoid round off 475 476 for (int col = 0; col < n; col++) { 477 Ai[icol][col] *= pivotInverse; 478 } 479 480 // fix the other rows by subtracting 481 for (int row = 0; row < n; row++) { 482 if (row != icol) { 483 float temp = Ai[row][icol]; 484 Ai[row][icol] = 0.0f; 485 486 for (int col = 0; col < n; col++) { 487 Ai[row][col] -= Ai[icol][col] * temp; 488 } 489 } 490 } 491 } 492 493 // fix the effect of all the swaps for final answer 494 for (int swap = n - 1; swap >= 0; swap--) { 495 if (swapRow[swap] != swapCol[swap]) { 496 for (int row = 0; row < n; row++) { 497 float temp = Ai[row][swapRow[swap]]; 498 Ai[row][swapRow[swap]] = Ai[row][swapCol[swap]]; 499 Ai[row][swapCol[swap]] = temp; 500 } 501 } 502 } 503 504 return Ai; 505 } 506 507 /** Replace the first matrix argument elements with the values of 508 * the second matrix argument. The second matrix argument must be 509 * large enough to hold all the values of second matrix argument. 510 * @param destMatrix A matrix of floats, used as the destination. 511 * @param srcMatrix A matrix of floats, used as the source. 512 */ 513 public static final void matrixCopy(final float[][] srcMatrix, 514 final float[][] destMatrix) { 515 matrixCopy(srcMatrix, 0, 0, destMatrix, 0, 0, _rows(srcMatrix), 516 _columns(srcMatrix)); 517 } 518 519 /** Replace the first matrix argument's values, in the specified row 520 * and column range, with the second matrix argument's values, starting 521 * from specified row and column of the second matrix. 522 * @param srcMatrix A matrix of floats, used as the destination. 523 * @param srcRowStart An int specifying the starting row of the source. 524 * @param srcColStart An int specifying the starting column of the 525 * source. 526 * @param destMatrix A matrix of floats, used as the destination. 527 * @param destRowStart An int specifying the starting row of the dest. 528 * @param destColStart An int specifying the starting column of the 529 * dest. 530 * @param rowSpan An int specifying how many rows to copy. 531 * @param colSpan An int specifying how many columns to copy. 532 */ 533 public static final void matrixCopy(final float[][] srcMatrix, 534 final int srcRowStart, final int srcColStart, 535 final float[][] destMatrix, final int destRowStart, 536 final int destColStart, final int rowSpan, final int colSpan) { 537 // We should verify the parameters here 538 for (int i = 0; i < rowSpan; i++) { 539 System.arraycopy(srcMatrix[srcRowStart + i], srcColStart, 540 destMatrix[destRowStart + i], destColStart, colSpan); 541 } 542 } 543 544 /** Return a new matrix that is constructed by multiplying the matrix 545 * by a scaleFactor. 546 */ 547 public static final float[][] multiply(final float[][] matrix, 548 final float scaleFactor) { 549 int rows = _rows(matrix); 550 int columns = _columns(matrix); 551 552 float[][] returnValue = new float[rows][columns]; 553 554 for (int i = 0; i < rows; i++) { 555 for (int j = 0; j < columns; j++) { 556 returnValue[i][j] = matrix[i][j] * scaleFactor; 557 } 558 } 559 560 return returnValue; 561 } 562 563 /** Return a new array that is constructed from the argument by 564 * pre-multiplying the array (treated as a row vector) by a matrix. 565 * The number of rows of the matrix must equal the number of elements 566 * in the array. The returned array will have a length equal to the number 567 * of columns of the matrix. 568 */ 569 public static final float[] multiply(final float[][] matrix, 570 final float[] array) { 571 int rows = _rows(matrix); 572 int columns = _columns(matrix); 573 574 if (rows != array.length) { 575 throw new IllegalArgumentException( 576 "preMultiply : array does not have the same number of " 577 + "elements (" + array.length 578 + ") as the number of rows " + "of the matrix (" 579 + rows + ")"); 580 } 581 582 float[] returnValue = new float[columns]; 583 584 for (int i = 0; i < columns; i++) { 585 float sum = 0.0f; 586 587 for (int j = 0; j < rows; j++) { 588 sum += matrix[j][i] * array[j]; 589 } 590 591 returnValue[i] = sum; 592 } 593 594 return returnValue; 595 } 596 597 /** Return a new array that is constructed from the argument by 598 * post-multiplying the matrix by an array (treated as a row vector). 599 * The number of columns of the matrix must equal the number of elements 600 * in the array. The returned array will have a length equal to the number 601 * of rows of the matrix. 602 */ 603 public static final float[] multiply(final float[] array, 604 final float[][] matrix) { 605 int rows = _rows(matrix); 606 int columns = _columns(matrix); 607 608 if (columns != array.length) { 609 throw new IllegalArgumentException( 610 "postMultiply() : array does not have the same number " 611 + "of elements (" + array.length 612 + ") as the number of " + "columns of the matrix (" 613 + columns + ")"); 614 } 615 616 float[] returnValue = new float[rows]; 617 618 for (int i = 0; i < rows; i++) { 619 float sum = 0.0f; 620 621 for (int j = 0; j < columns; j++) { 622 sum += matrix[i][j] * array[j]; 623 } 624 625 returnValue[i] = sum; 626 } 627 628 return returnValue; 629 } 630 631 /** Return a new matrix that is constructed from the argument by 632 * multiplying the first matrix by the second one. 633 * Note this operation is not commutative, 634 * so care must be taken in the ordering of the arguments. 635 * The number of columns of matrix1 636 * must equal the number of rows of matrix2. If matrix1 is of 637 * size m x n, and matrix2 is of size n x p, the returned matrix 638 * will have size m x p. 639 * 640 * <p>Note that this method is different from the other multiply() 641 * methods in that this method does not do pointwise multiplication. 642 * 643 * @see #multiplyElements(float[][], float[][]) 644 * @param matrix1 The first matrix of floats. 645 * @param matrix2 The second matrix of floats. 646 * @return A new matrix of floats. 647 */ 648 public static final float[][] multiply(float[][] matrix1, 649 float[][] matrix2) { 650 float[][] returnValue = new float[_rows(matrix1)][matrix2[0].length]; 651 652 for (int i = 0; i < _rows(matrix1); i++) { 653 for (int j = 0; j < matrix2[0].length; j++) { 654 float sum = 0.0f; 655 656 for (int k = 0; k < matrix2.length; k++) { 657 sum += matrix1[i][k] * matrix2[k][j]; 658 } 659 660 returnValue[i][j] = sum; 661 } 662 } 663 664 return returnValue; 665 } 666 667 /** Return a new matrix that is constructed by element by element 668 * multiplication of the two matrix arguments. If the two 669 * matrices are not the same size, throw an 670 * IllegalArgumentException. 671 * <p>Note that this method does pointwise matrix multiplication. 672 * See {@link #multiply(float[][], float[][])} for standard 673 * matrix multiplication. 674 */ 675 public static final float[][] multiplyElements(final float[][] matrix1, 676 final float[][] matrix2) { 677 int rows = _rows(matrix1); 678 int columns = _columns(matrix1); 679 680 _checkSameDimension("multiplyElements", matrix1, matrix2); 681 682 float[][] returnValue = new float[rows][columns]; 683 684 for (int i = 0; i < rows; i++) { 685 for (int j = 0; j < columns; j++) { 686 returnValue[i][j] = matrix1[i][j] * matrix2[i][j]; 687 } 688 } 689 690 return returnValue; 691 } 692 693 /** Return a new matrix that is the additive inverse of the 694 * argument matrix. 695 */ 696 public static final float[][] negative(final float[][] matrix) { 697 int rows = _rows(matrix); 698 int columns = _columns(matrix); 699 700 float[][] returnValue = new float[rows][columns]; 701 702 for (int i = 0; i < rows; i++) { 703 for (int j = 0; j < columns; j++) { 704 returnValue[i][j] = -matrix[i][j]; 705 } 706 } 707 708 return returnValue; 709 } 710 711 /** Return a new matrix that is formed by orthogonalizing the 712 * columns of the input matrix (the column vectors are 713 * orthogonal). If not all columns are linearly independent, the 714 * output matrix will contain a column of zeros for all redundant 715 * input columns. 716 */ 717 public static final float[][] orthogonalizeColumns(final float[][] matrix) { 718 Object[] orthoInfo = _orthogonalizeRows(transpose(matrix)); 719 return transpose((float[][]) orthoInfo[0]); 720 } 721 722 /** Return a new matrix that is formed by orthogonalizing the rows of the 723 * input matrix (the row vectors are orthogonal). If not all rows are 724 * linearly independent, the output matrix will contain a row of zeros 725 * for all redundant input rows. 726 */ 727 public static final float[][] orthogonalizeRows(final float[][] matrix) { 728 Object[] orthoInfo = _orthogonalizeRows(matrix); 729 return (float[][]) orthoInfo[0]; 730 } 731 732 /** Return a new matrix that is formed by orthogonalizing the 733 * columns of the input matrix (the column vectors are orthogonal 734 * and have norm 1). If not all columns are linearly independent, 735 * the output matrix will contain a column of zeros for all 736 * redundant input columns. 737 */ 738 public static final float[][] orthonormalizeColumns( 739 final float[][] matrix) { 740 return transpose(orthogonalizeRows(transpose(matrix))); 741 } 742 743 /** Return a new matrix that is formed by orthonormalizing the 744 * rows of the input matrix (the row vectors are orthogonal and 745 * have norm 1). If not all rows are linearly independent, the 746 * output matrix will contain a row of zeros for all redundant 747 * input rows. 748 */ 749 public static final float[][] orthonormalizeRows(final float[][] matrix) { 750 int rows = _rows(matrix); 751 752 Object[] orthoInfo = _orthogonalizeRows(matrix); 753 float[][] orthogonalMatrix = (float[][]) orthoInfo[0]; 754 float[] oneOverNormSquaredArray = (float[]) orthoInfo[2]; 755 756 for (int i = 0; i < rows; i++) { 757 orthogonalMatrix[i] = FloatArrayMath.scale(orthogonalMatrix[i], 758 (float) Math.sqrt(oneOverNormSquaredArray[i])); 759 } 760 761 return orthogonalMatrix; 762 } 763 764 /** Return a pair of matrices that are the decomposition of the 765 * input matrix (which must have linearly independent column 766 * vectors), which is m x n, into the matrix product of Q, which 767 * is m x n with orthonormal column vectors, and R, which is an 768 * invertible n x n upper triangular matrix. Throw an 769 * IllegalArgumentException if the columns vectors of the input 770 * matrix are not linearly independent. 771 * @param matrix The input matrix of floats. 772 * @return The pair of newly allocated matrices of floats, 773 * out[0] = Q, out[1] = R. 774 */ 775 public static final float[][][] qr(final float[][] matrix) { 776 int columns = _columns(matrix); 777 778 /* Find an orthogonal basis using _orthogonalizeRows(). Note 779 * that _orthogonalizeRows() orthogonalizes row vectors, so 780 * we have use the transpose of input matrix to orthogonalize 781 * its columns vectors. The output will be the transpose of 782 * Q. 783 */ 784 Object[] orthoRowInfo = _orthogonalizeRows(transpose(matrix)); 785 786 float[][] qT = (float[][]) orthoRowInfo[0]; 787 788 // get the dot product matrix, dp[j][i] = <inColumn[i], outColumn[j]> 789 float[][] dotProducts = (float[][]) orthoRowInfo[1]; 790 791 // Normalize the row vectors of qT (column vectors of Q) by 792 // dividing by the norm of each row vector. To compute R, 793 // normalize each row of dotProducts by dividing each row the 794 // norm of each column vector of Q. 795 float[] oneOverNormSquaredArray = (float[]) orthoRowInfo[2]; 796 797 // check that all columns were linearly independent 798 Integer nullity = (Integer) orthoRowInfo[3]; 799 800 if (nullity.intValue() > 0) { 801 throw new IllegalArgumentException("qr() : not all column " 802 + "vectors are linearly independent."); 803 } 804 805 for (int i = 0; i < columns; i++) { 806 float oneOverNorm = (float) Math.sqrt(oneOverNormSquaredArray[i]); 807 qT[i] = FloatArrayMath.scale(qT[i], oneOverNorm); 808 809 // R is upper triangular, so normalize only upper elements 810 for (int j = i; j < columns; j++) { 811 dotProducts[i][j] *= oneOverNorm; 812 } 813 } 814 815 return new float[][][] { transpose(qT), dotProducts }; 816 } 817 818 /** Return a new matrix that is constructed from the argument by 819 * subtracting the second matrix from the first one. If the two 820 * matrices are not the same size, throw an 821 * IllegalArgumentException. 822 */ 823 public static final float[][] subtract(final float[][] matrix1, 824 final float[][] matrix2) { 825 _checkSameDimension("subtract", matrix1, matrix2); 826 827 int rows = _rows(matrix1); 828 int columns = _columns(matrix1); 829 830 float[][] returnValue = new float[rows][columns]; 831 832 for (int i = 0; i < rows; i++) { 833 for (int j = 0; j < columns; j++) { 834 returnValue[i][j] = matrix1[i][j] - matrix2[i][j]; 835 } 836 } 837 838 return returnValue; 839 } 840 841 /** Return the sum of the elements of a matrix. 842 * @return The sum of the elements of the matrix. 843 */ 844 public static final float sum(final float[][] matrix) { 845 float sum = 0.0f; 846 847 for (float[] element : matrix) { 848 for (int j = 0; j < element.length; j++) { 849 sum += element[j]; 850 } 851 } 852 853 return sum; 854 } 855 856 /** Return a new matrix that is formed by converting the floats 857 * in the argument matrix to complex numbers. Each complex number 858 * has a real part equal to the value in the argument matrix and a 859 * zero imaginary part. 860 * 861 * @param matrix A matrix of floats. 862 * @return A new matrix of complex numbers. 863 */ 864 public static final Complex[][] toComplexMatrix(final float[][] matrix) { 865 int rows = _rows(matrix); 866 int columns = _columns(matrix); 867 868 Complex[][] returnValue = new Complex[rows][columns]; 869 870 for (int i = 0; i < rows; i++) { 871 for (int j = 0; j < columns; j++) { 872 returnValue[i][j] = new Complex(matrix[i][j], 0.0); 873 } 874 } 875 876 return returnValue; 877 } 878 879 /** Return a new matrix that is formed by converting the floats in 880 * the argument matrix to doubles. 881 * @param matrix An matrix of float. 882 * @return A new matrix of doubles. 883 */ 884 public static final double[][] toDoubleMatrix(final float[][] matrix) { 885 int rows = _rows(matrix); 886 int columns = _columns(matrix); 887 888 double[][] returnValue = new double[rows][columns]; 889 890 for (int i = 0; i < rows; i++) { 891 for (int j = 0; j < columns; j++) { 892 returnValue[i][j] = matrix[i][j]; 893 } 894 } 895 896 return returnValue; 897 } 898 899 /** Return a new matrix that is formed by converting the floats in 900 * the argument matrix to integers. 901 * @param matrix An matrix of float. 902 * @return A new matrix of integers. 903 */ 904 public static final int[][] toIntegerMatrix(final float[][] matrix) { 905 int rows = _rows(matrix); 906 int columns = _columns(matrix); 907 908 int[][] returnValue = new int[rows][columns]; 909 910 for (int i = 0; i < rows; i++) { 911 for (int j = 0; j < columns; j++) { 912 returnValue[i][j] = (int) matrix[i][j]; 913 } 914 } 915 916 return returnValue; 917 } 918 919 /** Return a new matrix that is formed by converting the floats in 920 * the argument matrix to longs. 921 * @param matrix An matrix of float. 922 * @return A new matrix of longs. 923 */ 924 public static final long[][] toLongMatrix(final float[][] matrix) { 925 int rows = _rows(matrix); 926 int columns = _columns(matrix); 927 928 long[][] returnValue = new long[rows][columns]; 929 930 for (int i = 0; i < rows; i++) { 931 for (int j = 0; j < columns; j++) { 932 returnValue[i][j] = (long) matrix[i][j]; 933 } 934 } 935 936 return returnValue; 937 } 938 939 /** Return a new matrix of floats that is initialized from a 1-D 940 * array. The format of the array must be (0, 0), (0, 1), ..., 941 * (0, n-1), (1, 0), (1, 1), ..., (m-1, n-1) where the output 942 * matrix is to be m x n and entries are denoted by (row, 943 * column). 944 * @param array An array of floats. 945 * @param rows An integer representing the number of rows of the new 946 * matrix. 947 * @param cols An integer representing the number of columns of the new 948 * matrix. 949 * @return A new matrix of floats. 950 */ 951 public static final float[][] toMatrixFromArray(float[] array, int rows, 952 int cols) { 953 float[][] returnValue = new float[rows][cols]; 954 955 for (int i = 0; i < rows; i++) { 956 System.arraycopy(array, i * cols, returnValue[i], 0, cols); 957 } 958 959 return returnValue; 960 } 961 962 /** Return a new String representing the matrix, formatted as 963 * in Java array initializers. 964 */ 965 public static final String toString(final float[][] matrix) { 966 return toString(matrix, ", ", "{", "}", "{", ", ", "}"); 967 } 968 969 /** Return a new String representing the matrix, formatted as 970 * specified by the ArrayStringFormat argument. 971 * To get a String in the Ptolemy expression language format, 972 * call this method with ArrayStringFormat.exprASFormat as the 973 * format argument. 974 */ 975 public static final String toString(final float[][] matrix, 976 String elementDelimiter, String matrixBegin, String matrixEnd, 977 String vectorBegin, String vectorDelimiter, String vectorEnd) { 978 StringBuffer sb = new StringBuffer(); 979 sb.append(matrixBegin); 980 981 for (int i = 0; i < _rows(matrix); i++) { 982 sb.append(vectorBegin); 983 984 for (int j = 0; j < _columns(matrix); j++) { 985 sb.append(Float.toString(matrix[i][j])); 986 987 if (j < _columns(matrix) - 1) { 988 sb.append(elementDelimiter); 989 } 990 } 991 992 sb.append(vectorEnd); 993 994 if (i < _rows(matrix) - 1) { 995 sb.append(vectorDelimiter); 996 } 997 } 998 999 sb.append(matrixEnd); 1000 1001 return new String(sb); 1002 } 1003 1004 /** Return the trace of a square matrix, which is the sum of the 1005 * diagonal entries A<sub>11</sub> + A<sub>22</sub> + ... + A<sub>nn</sub> 1006 * Throw an IllegalArgumentException if the matrix is not square. 1007 * Note that the trace of a matrix is equal to the sum of its eigenvalues. 1008 */ 1009 public static final float trace(final float[][] matrix) { 1010 int dim = _checkSquare("trace", matrix); 1011 float sum = 0.0f; 1012 1013 for (int i = 0; i < dim; i++) { 1014 sum += matrix[i][i]; 1015 } 1016 1017 return sum; 1018 } 1019 1020 /** Return a new matrix that is constructed by transposing the input 1021 * matrix. If the input matrix is m x n, the output matrix will be 1022 * n x m. 1023 */ 1024 public static final float[][] transpose(final float[][] matrix) { 1025 int rows = _rows(matrix); 1026 int columns = _columns(matrix); 1027 1028 float[][] returnValue = new float[columns][rows]; 1029 1030 for (int i = 0; i < rows; i++) { 1031 for (int j = 0; j < columns; j++) { 1032 returnValue[j][i] = matrix[i][j]; 1033 } 1034 } 1035 1036 return returnValue; 1037 } 1038 1039 /** Return true if the elements of the two matrices differ by no more 1040 * than the specified distance. If <i>distance</i> is negative, return false. 1041 * @param matrix1 The first matrix. 1042 * @param matrix2 The second matrix. 1043 * @param distance The distance to use for comparison. 1044 * @return True if the elements of the two matrices are within the 1045 * specified distance. 1046 * @exception IllegalArgumentException If the matrices do not have the same dimension. 1047 * This is a run-time exception, so it need not be declared explicitly. 1048 */ 1049 public static final boolean within(final float[][] matrix1, 1050 final float[][] matrix2, float distance) { 1051 int rows = _rows(matrix1); 1052 int columns = _columns(matrix1); 1053 1054 _checkSameDimension("within", matrix1, matrix2); 1055 1056 for (int i = 0; i < rows; i++) { 1057 for (int j = 0; j < columns; j++) { 1058 if (matrix1[i][j] > matrix2[i][j] + distance 1059 || matrix1[i][j] < matrix2[i][j] - distance) { 1060 return false; 1061 } 1062 } 1063 } 1064 1065 return true; 1066 } 1067 1068 /** Return true if the elements of the two matrices differ by no more 1069 * than the specified distances. If any element of <i>errorMatrix</i> is 1070 * negative, return false. 1071 * @param matrix1 The first matrix. 1072 * @param matrix2 The second matrix. 1073 * @param errorMatrix The distance to use for comparison. 1074 * @return True if the elements of the two matrices are within the 1075 * specified distance. 1076 * @exception IllegalArgumentException If the matrices do not have the same dimension. 1077 * This is a run-time exception, so it need not be declared explicitly. 1078 */ 1079 public static final boolean within(final float[][] matrix1, 1080 final float[][] matrix2, final float[][] errorMatrix) { 1081 int rows = _rows(matrix1); 1082 int columns = _columns(matrix1); 1083 1084 _checkSameDimension("within", matrix1, matrix2); 1085 _checkSameDimension("within", matrix1, errorMatrix); 1086 1087 for (int i = 0; i < rows; i++) { 1088 for (int j = 0; j < columns; j++) { 1089 if (matrix1[i][j] > matrix2[i][j] + errorMatrix[i][j] 1090 || matrix1[i][j] < matrix2[i][j] - errorMatrix[i][j]) { 1091 return false; 1092 } 1093 } 1094 } 1095 1096 return true; 1097 } 1098 1099 /** Check that the two matrix arguments are of the same dimension. 1100 * If they are not, an IllegalArgumentException is thrown. 1101 * @param caller A string representing the caller method name. 1102 * @param matrix1 A matrix of floats. 1103 * @param matrix2 A matrix of floats. 1104 */ 1105 protected static final void _checkSameDimension(final String caller, 1106 final float[][] matrix1, final float[][] matrix2) { 1107 int rows = _rows(matrix1); 1108 int columns = _columns(matrix1); 1109 1110 if (rows != _rows(matrix2) || columns != _columns(matrix2)) { 1111 throw new IllegalArgumentException("ptolemy.math.FloatMatrixMath." 1112 + caller + "() : one matrix " + _dimensionString(matrix1) 1113 + " is not the same size as another matrix " 1114 + _dimensionString(matrix2) + "."); 1115 } 1116 } 1117 1118 /** Check that the argument matrix is a square matrix. If the matrix is not 1119 * square, an IllegalArgumentException is thrown. 1120 * @param caller A string representing the caller method name. 1121 * @param matrix A matrix of floats. 1122 * @return The dimension of the square matrix. 1123 */ 1124 protected static final int _checkSquare(final String caller, 1125 final float[][] matrix) { 1126 if (_rows(matrix) != _columns(matrix)) { 1127 throw new IllegalArgumentException("ptolemy.math.FloatMatrixMath." 1128 + caller + "() : matrix argument " 1129 + _dimensionString(matrix) + " is not a square matrix."); 1130 } 1131 1132 return _rows(matrix); 1133 } 1134 1135 /** Return the number of columns of a matrix. */ 1136 protected static final int _columns(final float[][] matrix) { 1137 return matrix[0].length; 1138 } 1139 1140 /** Return a string that describes the number of rows and columns. 1141 * @param matrix The matrix that is to be described. 1142 * @return a string describing the dimensions of this matrix. 1143 */ 1144 protected static final String _dimensionString(final float[][] matrix) { 1145 return "[" + _rows(matrix) + " x " + _columns(matrix) + "]"; 1146 } 1147 1148 /** Given a set of row vectors rowArrays[0] ... rowArrays[n-1], compute : 1149 * <ol> 1150 * <li> A new set of row vectors out[0] ... out[n-1] which are the 1151 * orthogonalized versions of each input row vector. If a row 1152 * vector rowArray[i] is a linear combination of the last 0 1153 * .. i - 1 row vectors, set array[i] to an array of 0's 1154 * (array[i] being the 0 vector is a special case of 1155 * this). Put the result in returnValue[0].<br> 1156 * <li> An n x n matrix containing the dot products of the input 1157 * row vectors and the output row vectors, 1158 * dotProductMatrix[j][i] = <rowArray[i], outArray[j]>. Put 1159 * the result in returnValue[1].<br> 1160 * <li> An array containing 1 / (norm(outArray[i])<sup>2</sup>), 1161 * with n entries. Put the result in returnValue[2].<br> 1162 * <li> A count of the number of rows that were found to be linear 1163 * combinations of previous rows. Replace those rows with rows of 1164 * zeros. The count is equal to the nullity of the transpose of 1165 * the input matrix. Wrap the count with an Integer, and put it in 1166 * returnValue[3].<br> 1167 * </ol> 1168 * Orthogonalization is done with the Gram-Schmidt process. 1169 */ 1170 protected static final Object[] _orthogonalizeRows( 1171 final float[][] rowArrays) { 1172 int rows = rowArrays.length; 1173 int columns = rowArrays[0].length; 1174 int nullity = 0; 1175 1176 float[][] orthogonalMatrix = new float[rows][]; 1177 1178 float[] oneOverNormSquaredArray = new float[rows]; 1179 1180 // A matrix containing the dot products of the input row 1181 // vectors and output row vectors, dotProductMatrix[j][i] = 1182 // <rowArray[i], outArray[j]> 1183 float[][] dotProductMatrix = new float[rows][rows]; 1184 1185 for (int i = 0; i < rows; i++) { 1186 // Get a reference to the row vector. 1187 float[] refArray = rowArrays[i]; 1188 1189 // Initialize row vector. 1190 float[] rowArray = refArray; 1191 1192 // Subtract projections onto all previous vectors. 1193 for (int j = 0; j < i; j++) { 1194 // Save the dot product for future use for QR decomposition. 1195 float dotProduct = FloatArrayMath.dotProduct(refArray, 1196 orthogonalMatrix[j]); 1197 1198 dotProductMatrix[j][i] = dotProduct; 1199 1200 rowArray = FloatArrayMath.subtract(rowArray, 1201 FloatArrayMath.scale(orthogonalMatrix[j], 1202 dotProduct * oneOverNormSquaredArray[j])); 1203 } 1204 1205 // Compute the dot product between the input and output vector 1206 // for the diagonal entry of dotProductMatrix. 1207 dotProductMatrix[i][i] = FloatArrayMath.dotProduct(refArray, 1208 rowArray); 1209 1210 // Check the norm to find zero rows, and save the 1 / 1211 // norm^2 for later computation. 1212 float normSquared = FloatArrayMath.sumOfSquares(rowArray); 1213 1214 if (normSquared == 0.0f) { 1215 if (i == 0) { 1216 // The input row was the zero vector, we now have 1217 // a reference to it. Set the row to a new zero 1218 // vector to ensure the output memory is entirely 1219 // disjoint from the input memory. 1220 orthogonalMatrix[i] = new float[columns]; 1221 } else { 1222 // Reuse the memory allocated by the last 1223 // subtract() call -- the row is all zeros. 1224 orthogonalMatrix[i] = rowArray; 1225 } 1226 1227 // Set the normalizing factor to 0.0f to avoid division by 0, 1228 // it works because the projection onto the zero vector yields 1229 // zero. 1230 oneOverNormSquaredArray[i] = 0.0f; 1231 1232 nullity++; 1233 } else { 1234 orthogonalMatrix[i] = rowArray; 1235 oneOverNormSquaredArray[i] = 1.0f / normSquared; 1236 } 1237 } 1238 1239 return new Object[] { orthogonalMatrix, dotProductMatrix, 1240 oneOverNormSquaredArray, Integer.valueOf(nullity) }; 1241 } 1242 1243 /** Return the number of rows of a matrix. 1244 * @param matrix The matrix. 1245 * @return The number of rows. 1246 */ 1247 protected static final int _rows(final float[][] matrix) { 1248 return matrix.length; 1249 } 1250}