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] = &lt;rowArray[i], outArray[j]&gt;.  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}