001/* A library for mathematical operations on matrices of Fractions.
002
003 Copyright (c) 2004-2014 The Regents of the University of California.
004 All rights reserved.
005
006 Permission is hereby granted, without written agreement and without
007 license or royalty fees, to use, copy, modify, and distribute this
008 software and its documentation for any purpose, provided that the above
009 copyright notice and the following two paragraphs appear in all copies
010 of this software.
011
012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016 SUCH DAMAGE.
017
018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023 ENHANCEMENTS, OR MODIFICATIONS.
024
025 PT_COPYRIGHT_VERSION_2
026 COPYRIGHTENDKEY
027
028 */
029package ptolemy.math;
030
031//////////////////////////////////////////////////////////////////////////
032//// FractionMatrixMath
033
034/**
035 A library for mathematical operations on matrices of Fractions.
036
037 <p>Rows and column numbers of matrices are specified with zero-based indices.
038 All calls expect matrix arguments to be non-null. In addition, all
039 rows of the matrix are expected to have the same number of columns.
040
041 @author Adam Cataldo
042 @version $Id$
043 @since Ptolemy II 5.0
044 @Pt.ProposedRating Red (acataldo)
045 @Pt.AcceptedRating Red (cxh)
046 */
047public class FractionMatrixMath {
048    // private constructor prevents construction of this class.
049    private FractionMatrixMath() {
050    }
051
052    /** Return a new matrix that is constructed from the argument by
053     *  adding the second argument to every element.
054     *  @param matrix A matrix of Fractions.
055     *  @param z The Fraction to add.
056     *  @return A new matrix of Fractions.
057     */
058    public static final Fraction[][] add(Fraction[][] matrix, Fraction z) {
059        Fraction[][] returnValue = new Fraction[_rows(matrix)][_columns(
060                matrix)];
061
062        for (int i = 0; i < _rows(matrix); i++) {
063            for (int j = 0; j < _columns(matrix); j++) {
064                returnValue[i][j] = matrix[i][j].add(z);
065            }
066        }
067
068        return returnValue;
069    }
070
071    /** Return a new matrix that is constructed from the argument by
072     *  adding the second matrix to the first one.  If the two
073     *  matrices are not the same size, throw an
074     *  IllegalArgumentException.
075     *  @param matrix1 The first matrix of Fractions.
076     *  @param matrix2 The second matrix of Fractions.
077     *  @return A new matrix of Fractions.  */
078    public static final Fraction[][] add(final Fraction[][] matrix1,
079            final Fraction[][] matrix2) {
080        _checkSameDimension("add", matrix1, matrix2);
081
082        Fraction[][] returnValue = new Fraction[_rows(matrix1)][_columns(
083                matrix1)];
084
085        for (int i = 0; i < _rows(matrix1); i++) {
086            for (int j = 0; j < _columns(matrix1); j++) {
087                returnValue[i][j] = matrix1[i][j].add(matrix2[i][j]);
088            }
089        }
090
091        return returnValue;
092    }
093
094    /** Return a new matrix that is a copy of the matrix argument.
095     *  @param matrix A matrix of Fractions.
096     *  @return A new matrix of Fractions.
097     */
098    public static final Fraction[][] allocCopy(final Fraction[][] matrix) {
099        return crop(matrix, 0, 0, _rows(matrix), _columns(matrix));
100    }
101
102    /** Return a new matrix that is a sub-matrix of the input
103     *  matrix argument. The row and column from which to start
104     *  and the number of rows and columns to span are specified.
105     *  @param matrix A matrix of Fractions.
106     *  @param rowStart An int specifying which row to start on.
107     *  @param colStart An int specifying which column to start on.
108     *  @param rowSpan An int specifying how many rows to copy.
109     *  @param colSpan An int specifying how many columns to copy.
110     */
111    public static final Fraction[][] crop(final Fraction[][] matrix,
112            final int rowStart, final int colStart, final int rowSpan,
113            final int colSpan) {
114        Fraction[][] returnValue = new Fraction[rowSpan][colSpan];
115
116        for (int i = 0; i < rowSpan; i++) {
117            System.arraycopy(matrix[rowStart + i], colStart, returnValue[i], 0,
118                    colSpan);
119        }
120
121        return returnValue;
122    }
123
124    /** Return a new matrix that is constructed by placing the
125     *  elements of the input array on the diagonal of the square
126     *  matrix, starting from the top left corner down to the bottom
127     *  right corner. All other elements are zero. The size of of the
128     *  matrix is n x n, where n is the length of the input array.
129     */
130    public static final Fraction[][] diag(final Fraction[] array) {
131        int n = array.length;
132
133        Fraction[][] returnValue = new Fraction[n][n];
134
135        // Assume the matrix is zero-filled.
136        for (int i = 0; i < n; i++) {
137            for (int j = 0; j < n; j++) {
138                if (i == j) {
139                    returnValue[i][j] = array[i];
140                } else {
141                    returnValue[i][j] = new Fraction(0, 1);
142                }
143            }
144        }
145
146        return returnValue;
147    }
148
149    /** Return a new matrix that is constructed from the argument by
150     *  dividing the second argument to every element.
151     *  @param matrix A matrix of Fractions.
152     *  @param z The Fraction to divide by.
153     *  @return A new matrix of Fractions.
154     */
155    public static final Fraction[][] divide(Fraction[][] matrix, Fraction z) {
156        Fraction[][] returnValue = new Fraction[_rows(matrix)][_columns(
157                matrix)];
158
159        for (int i = 0; i < _rows(matrix); i++) {
160            for (int j = 0; j < _columns(matrix); j++) {
161                returnValue[i][j] = matrix[i][j].divide(z);
162            }
163        }
164
165        return returnValue;
166    }
167
168    /** Return a new matrix that is constructed by element by element
169     *  division of the two matrix arguments. Each element of the
170     *  first matrix is divided by the corresponding element of the
171     *  second matrix.  If the two matrices are not the same size,
172     *  throw an IllegalArgumentException.
173     */
174    public static final Fraction[][] divideElements(final Fraction[][] matrix1,
175            final Fraction[][] matrix2) {
176        int rows = _rows(matrix1);
177        int columns = _columns(matrix1);
178
179        _checkSameDimension("divideElements", matrix1, matrix2);
180
181        Fraction[][] returnValue = new Fraction[rows][columns];
182
183        for (int i = 0; i < rows; i++) {
184            for (int j = 0; j < columns; j++) {
185                returnValue[i][j] = matrix1[i][j].divide(matrix2[i][j]);
186            }
187        }
188
189        return returnValue;
190    }
191
192    /** Return a new array that is filled with the contents of the matrix.
193     *  The Fractions are stored row by row, i.e. using the notation
194     *  (row, column), the entries of the array are in the following order
195     *  for a (m, n) matrix :
196     *  (0, 0), (0, 1), (0, 2), ... , (0, n-1), (1, 0), (1, 1), ..., (m-1)(n-1)
197     *  @param matrix A matrix of Fractions.
198     *  @return A new array of Fractions.
199     */
200    public static final Fraction[] fromMatrixToArray(
201            final Fraction[][] matrix) {
202        return fromMatrixToArray(matrix, _rows(matrix), _columns(matrix));
203    }
204
205    /** Return a new array that is filled with the contents of the matrix.
206     *  The maximum numbers of rows and columns to copy are specified so
207     *  that entries lying outside of this range can be ignored. The
208     *  maximum rows to copy cannot exceed the number of rows in the matrix,
209     *  and the maximum columns to copy cannot exceed the number of columns
210     *  in the matrix.
211     *  The Fractions are stored row by row, i.e. using the notation
212     *  (row, column), the entries of the array are in the following order
213     *  for a matrix, limited to m rows and n columns :
214     *  (0, 0), (0, 1), (0, 2), ... , (0, n-1), (1, 0), (1, 1), ..., (m-1)(n-1)
215     *  @param matrix A matrix of Fractions.
216     *  @return A new array of Fractions.
217     */
218    public static final Fraction[] fromMatrixToArray(final Fraction[][] matrix,
219            int maxRow, int maxCol) {
220        Fraction[] returnValue = new Fraction[maxRow * maxCol];
221
222        for (int i = 0; i < maxRow; i++) {
223            System.arraycopy(matrix[i], 0, returnValue, i * maxCol, maxCol);
224        }
225
226        return returnValue;
227    }
228
229    /** Return an new identity matrix with the specified dimension. The
230     *  matrix is square, so only one dimension specifier is needed.
231     *  @return Identity matrix in fractions
232     */
233    public static final Fraction[][] identity(final int dim) {
234        Fraction[][] returnValue = new Fraction[dim][dim];
235
236        // we rely on the fact Java fills the allocated matrix with 0's
237        for (int i = 0; i < dim; i++) {
238            for (int j = 0; j < dim; j++) {
239                if (i == j) {
240                    returnValue[i][i] = new Fraction(1, 1);
241                } else {
242                    returnValue[i][j] = new Fraction(0, 1);
243                }
244            }
245        }
246
247        return returnValue;
248    }
249
250    /** Return a new matrix that is constructed by multiplying the matrix
251     *  by a scaleFactor.
252     *  @param matrix The matrix of Fractions.
253     *  @param scaleFactor The Fraction to multiply by.
254     *  @return The resulting matrix of Fractions.
255     */
256    public static final Fraction[][] multiply(final Fraction[][] matrix,
257            final Fraction scaleFactor) {
258        int rows = _rows(matrix);
259        int columns = _columns(matrix);
260
261        Fraction[][] returnValue = new Fraction[rows][columns];
262
263        for (int i = 0; i < rows; i++) {
264            for (int j = 0; j < columns; j++) {
265                returnValue[i][j] = matrix[i][j].multiply(scaleFactor);
266            }
267        }
268
269        return returnValue;
270    }
271
272    /** Return a new array that is constructed from the argument by
273     *  pre-multiplying the matrix by an array (treated as a row vector).
274     *  The number of rows of the matrix must equal the number of elements
275     *  in the array. The returned array will have a length equal to the number
276     *  of columns of the matrix.
277     *  @param array The array of Fractions.
278     *  @param matrix The matrix of Fractions.
279     *  @return The resulting matrix of Fractions.
280     */
281    public static final Fraction[] multiply(final Fraction[] array,
282            final Fraction[][] matrix) {
283        int rows = _rows(matrix);
284        int columns = _columns(matrix);
285
286        if (rows != array.length) {
287            throw new IllegalArgumentException(
288                    "preMultiply : array does not have the same number of "
289                            + "elements (" + array.length
290                            + ") as the number of rows " + "of the matrix ("
291                            + rows + ")");
292        }
293
294        Fraction[] returnValue = new Fraction[columns];
295
296        for (int i = 0; i < columns; i++) {
297            Fraction sum = new Fraction(0, 1);
298
299            for (int j = 0; j < rows; j++) {
300                sum = sum.add(matrix[j][i].multiply(array[j]));
301            }
302
303            returnValue[i] = sum;
304        }
305
306        return returnValue;
307    }
308
309    /** Return a new array that is constructed from the argument by
310     *  post-multiplying the matrix by an array (treated as a column vector).
311     *  The number of columns of the matrix must equal the number of elements
312     *  in the array. The returned array will have a length equal to the number
313     *  of rows of the matrix.
314     *  @param matrix The matrix of Fractions.
315     *  @param array The array of Fractions.
316     *  @return The resulting matrix of Fractions.
317     */
318    public static final Fraction[] multiply(final Fraction[][] matrix,
319            final Fraction[] array) {
320        int rows = _rows(matrix);
321        int columns = _columns(matrix);
322
323        if (columns != array.length) {
324            throw new IllegalArgumentException(
325                    "postMultiply() : array does not have the same number "
326                            + "of elements (" + array.length
327                            + ") as the number of " + "columns of the matrix ("
328                            + columns + ")");
329        }
330
331        Fraction[] returnValue = new Fraction[rows];
332
333        for (int i = 0; i < rows; i++) {
334            Fraction sum = new Fraction(0, 1);
335
336            for (int j = 0; j < columns; j++) {
337                sum = sum.add(matrix[i][j].multiply(array[j]));
338            }
339
340            returnValue[i] = sum;
341        }
342
343        return returnValue;
344    }
345
346    /** Return a new matrix that is constructed from the argument by
347     *  multiplying the first matrix by the second one.
348     *  Note this operation is not commutative,
349     *  so care must be taken in the ordering of the arguments.
350     *  The number of columns of matrix1
351     *  must equal the number of rows of matrix2. If matrix1 is of
352     *  size m x n, and matrix2 is of size n x p, the returned matrix
353     *  will have size m x p.
354     *
355     *  <p>Note that this method is different from the other multiply()
356     *  methods in that this method does not do pointwise multiplication.
357     *
358     *  @see #multiplyElements(Fraction[][], Fraction[][])
359     *  @param matrix1 The first matrix of Fractions.
360     *  @param matrix2 The second matrix of Fractions.
361     *  @return A new matrix of ints.
362     *  @exception ArithmeticException If the matrix dimensions don't match up.
363     */
364    public static final Fraction[][] multiply(Fraction[][] matrix1,
365            Fraction[][] matrix2) throws ArithmeticException {
366        if (_columns(matrix1) != _rows(matrix2)) {
367            throw new ArithmeticException(
368                    "Number of columns (" + _columns(matrix1)
369                            + ") of matrix1 does note equal number of rows ("
370                            + _rows(matrix2) + ") of matrix2.");
371        }
372
373        Fraction[][] returnValue = new Fraction[_rows(matrix1)][_columns(
374                matrix2)];
375
376        for (int i = 0; i < _rows(matrix1); i++) {
377            for (int j = 0; j < _columns(matrix2); j++) {
378                Fraction sum = new Fraction(0, 1);
379
380                for (int k = 0; k < _columns(matrix1); k++) {
381                    sum = sum.add(matrix1[i][k].multiply(matrix2[k][j]));
382                }
383
384                returnValue[i][j] = sum;
385            }
386        }
387
388        return returnValue;
389    }
390
391    /** Return a new matrix that is constructed by element by element
392     *  multiplication of the two matrix arguments.  If the two
393     *  matrices are not the same size, throw an
394     *  IllegalArgumentException.
395     *  <p>Note that this method does pointwise matrix multiplication.
396     * @param matrix1 The first matrix of Fractions.
397     *  @param matrix2 The second matrix of Fractions.
398     *  @return A new matrix of ints.
399     */
400    public static final Fraction[][] multiplyElements(
401            final Fraction[][] matrix1, final Fraction[][] matrix2) {
402        int rows = _rows(matrix1);
403        int columns = _columns(matrix1);
404
405        _checkSameDimension("multiplyElements", matrix1, matrix2);
406
407        Fraction[][] returnValue = new Fraction[rows][columns];
408
409        for (int i = 0; i < rows; i++) {
410            for (int j = 0; j < columns; j++) {
411                returnValue[i][j] = matrix1[i][j].multiply(matrix2[i][j]);
412            }
413        }
414
415        return returnValue;
416    }
417
418    /** Return a new matrix that is the additive inverse of the
419     *  argument matrix.
420     *  @param matrix the input matrix of Fractions.
421     *  @return the output matrix of Fractions.
422     */
423    public static final Fraction[][] negative(final Fraction[][] matrix) {
424        int rows = _rows(matrix);
425        int columns = _columns(matrix);
426
427        Fraction[][] returnValue = new Fraction[rows][columns];
428
429        for (int i = 0; i < rows; i++) {
430            for (int j = 0; j < columns; j++) {
431                returnValue[i][j] = matrix[i][j].negate();
432            }
433        }
434
435        return returnValue;
436    }
437
438    /** Return a new matrix that is constructed from the argument by
439     *  subtracting the second matrix from the first one.  If the two
440     *  matrices are not the same size, throw an
441     *  IllegalArgumentException.
442     *  @param matrix1 The matrix to be subtracted from.
443     *  @param matrix2 The matrix to subtract.
444     *  @return The difference matrix.
445     */
446    public static final Fraction[][] subtract(final Fraction[][] matrix1,
447            final Fraction[][] matrix2) {
448        _checkSameDimension("subtract", matrix1, matrix2);
449
450        int rows = _rows(matrix1);
451        int columns = _columns(matrix1);
452
453        Fraction[][] returnValue = new Fraction[rows][columns];
454
455        for (int i = 0; i < rows; i++) {
456            for (int j = 0; j < columns; j++) {
457                returnValue[i][j] = matrix1[i][j].subtract(matrix2[i][j]);
458            }
459        }
460
461        return returnValue;
462    }
463
464    /** Return the sum of the elements of a matrix.
465     *  @param matrix The input matrix.
466     *  @return The sum of the elements of the matrix.
467     */
468    public static final Fraction sum(final Fraction[][] matrix) {
469        Fraction sum = new Fraction(0, 1);
470
471        for (Fraction[] element : matrix) {
472            for (int j = 0; j < element.length; j++) {
473                sum = sum.add(element[j]);
474            }
475        }
476
477        return sum;
478    }
479
480    /** Return a new matrix that is formed by converting the Fractions in
481     *  the argument matrix to doubles.
482     *  @param matrix An matrix of Fractions.
483     *  @return A new matrix of doubles.
484     */
485    public static final double[][] toDoubleMatrix(final Fraction[][] matrix) {
486        int rows = _rows(matrix);
487        int columns = _columns(matrix);
488
489        double[][] returnValue = new double[rows][columns];
490
491        for (int i = 0; i < rows; i++) {
492            for (int j = 0; j < columns; j++) {
493                returnValue[i][j] = matrix[i][j].toDouble();
494            }
495        }
496
497        return returnValue;
498    }
499
500    /** Return a new matrix of Fractions that is initialized from a 1-D array.
501     *  The format of the array must be (0, 0), (0, 1), ..., (0, n-1), (1, 0),
502     *  (1, 1), ..., (m-1, n-1) where the output matrix is to be m x n and
503     *  entries are denoted by (row, column).
504     *  @param array An array of Fraction.
505     *  @param rows An integer representing the number of rows of the new
506     *  matrix.
507     *  @param cols An integer representing the number of columns of the new
508     *  matrix.
509     *  @return A new matrix of Fractions.
510     */
511    public static final Fraction[][] toMatrixFromArray(Fraction[] array,
512            int rows, int cols) {
513        Fraction[][] returnValue = new Fraction[rows][cols];
514
515        for (int i = 0; i < rows; i++) {
516            System.arraycopy(array, i * cols, returnValue[i], 0, cols);
517        }
518
519        return returnValue;
520    }
521
522    /** Return a new String representing the matrix, formatted as
523     *  in Java array initializers.
524     */
525    public static final String toString(final Fraction[][] matrix) {
526        return toString(matrix, ", ", "{", "}", "{", ", ", "}");
527    }
528
529    /** Return a new String representing the matrix, formatted as
530     *  specified by the ArrayStringFormat argument.
531     *  To get a String in the Ptolemy expression language format,
532     *  call this method with ArrayStringFormat.exprASFormat as the
533     *  format argument.
534     */
535    public static final String toString(final Fraction[][] matrix,
536            String elementDelimiter, String matrixBegin, String matrixEnd,
537            String vectorBegin, String vectorDelimiter, String vectorEnd) {
538        StringBuffer sb = new StringBuffer();
539        sb.append(matrixBegin);
540
541        for (int i = 0; i < _rows(matrix); i++) {
542            sb.append(vectorBegin);
543
544            for (int j = 0; j < _columns(matrix); j++) {
545                sb.append(matrix[i][j].toString());
546
547                if (j < _columns(matrix) - 1) {
548                    sb.append(elementDelimiter);
549                }
550            }
551
552            sb.append(vectorEnd);
553
554            if (i < _rows(matrix) - 1) {
555                sb.append(vectorDelimiter);
556            }
557        }
558
559        sb.append(matrixEnd);
560
561        return new String(sb);
562    }
563
564    /** Return the trace of a square matrix, which is the sum of the
565     *  diagonal entries A<sub>11</sub> + A<sub>22</sub> + ... + A<sub>nn</sub>
566     *  Throw an IllegalArgumentException if the matrix is not square.
567     *  Note that the trace of a matrix is equal to the sum of its eigenvalues.
568     *  @param matrix A square matrix.
569     *  @return The trace of this matrix.
570     */
571    public static final Fraction trace(final Fraction[][] matrix) {
572        int dim = _checkSquare("trace", matrix);
573        Fraction sum = new Fraction(0, 1);
574
575        for (int i = 0; i < dim; i++) {
576            sum = sum.add(matrix[i][i]);
577        }
578
579        return sum;
580    }
581
582    /** Return a new matrix that is constructed by transposing the input
583     *  matrix. If the input matrix is m x n, the output matrix will be
584     *  n x m.
585     *  @param matrix The input matrix.
586     *  @return The matrix transpose.
587     */
588    public static final Fraction[][] transpose(final Fraction[][] matrix) {
589        int rows = _rows(matrix);
590        int columns = _columns(matrix);
591
592        Fraction[][] returnValue = new Fraction[columns][rows];
593
594        for (int i = 0; i < rows; i++) {
595            for (int j = 0; j < columns; j++) {
596                returnValue[j][i] = matrix[i][j];
597            }
598        }
599
600        return returnValue;
601    }
602
603    /** Check that the two matrix arguments are of the same dimension.
604     *  If they are not, an IllegalArgumentException is thrown.
605     *  @param caller A string representing the caller method name.
606     *  @param matrix1 A matrix of ints.
607     *  @param matrix2 A matrix of ints.
608     */
609    protected static final void _checkSameDimension(final String caller,
610            final Fraction[][] matrix1, final Fraction[][] matrix2) {
611        int rows = _rows(matrix1);
612        int columns = _columns(matrix1);
613
614        if (rows != _rows(matrix2) || columns != _columns(matrix2)) {
615            throw new IllegalArgumentException(
616                    "ptolemy.math.FractionMatrixMath." + caller
617                            + "() : one matrix " + _dimensionString(matrix1)
618                            + " is not the same size as another matrix "
619                            + _dimensionString(matrix2) + ".");
620        }
621    }
622
623    /** Check that the argument matrix is a square matrix. If the matrix is not
624     *  square, an IllegalArgumentException is thrown.
625     *  @param caller A string representing the caller method name.
626     *  @param matrix A matrix of Fractions.
627     *  @return The dimension of the square matrix.
628     */
629    protected static final int _checkSquare(final String caller,
630            final Fraction[][] matrix) {
631        if (_rows(matrix) != _columns(matrix)) {
632            throw new IllegalArgumentException(
633                    "ptolemy.math.FractionMatrixMath." + caller
634                            + "() : matrix argument " + _dimensionString(matrix)
635                            + " is not a square matrix.");
636        }
637
638        return _rows(matrix);
639    }
640
641    /** Return the number of columns of a matrix.
642     *  @param matrix The matrix.
643     *  @return The number of columns.
644     */
645    protected static final int _columns(final Fraction[][] matrix) {
646        return matrix[0].length;
647    }
648
649    /** Return a string that describes the number of rows and columns.
650     *  @param matrix The matrix that is to be described.
651     *  @return a string describing the dimensions of this matrix.
652     */
653    protected static final String _dimensionString(final Fraction[][] matrix) {
654        return "[" + _rows(matrix) + " x " + _columns(matrix) + "]";
655    }
656
657    /** Return the number of rows of a matrix. */
658    protected static final int _rows(final Fraction[][] matrix) {
659        return matrix.length;
660    }
661}