View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.math.linear;
19  
20  import java.io.Serializable;
21  import org.apache.commons.math.util.MathUtils;
22  
23  
24  /**
25   * Implementation of RealMatrix using a double[][] array to store entries and
26   * <a href="http://www.math.gatech.edu/~bourbaki/math2601/Web-notes/2num.pdf">
27   * LU decomposition</a> to support linear system
28   * solution and inverse.
29   * <p>
30   * The LU decomposition is performed as needed, to support the following operations: <ul>
31   * <li>solve</li>
32   * <li>isSingular</li>
33   * <li>getDeterminant</li>
34   * <li>inverse</li> </ul></p>
35   * <p>
36   * <strong>Usage notes</strong>:<br>
37   * <ul><li>
38   * The LU decomposition is cached and reused on subsequent calls.   
39   * If data are modified via references to the underlying array obtained using
40   * <code>getDataRef()</code>, then the stored LU decomposition will not be
41   * discarded.  In this case, you need to explicitly invoke 
42   * <code>LUDecompose()</code> to recompute the decomposition
43   * before using any of the methods above.</li>
44   * <li>
45   * As specified in the {@link RealMatrix} interface, matrix element indexing
46   * is 0-based -- e.g., <code>getEntry(0, 0)</code>
47   * returns the element in the first row, first column of the matrix.</li></ul>
48   * </p>
49   *
50   * @version $Revision: 680166 $ $Date: 2008-07-27 21:15:22 +0200 (dim., 27 juil. 2008) $
51   */
52  public class RealMatrixImpl implements RealMatrix, Serializable {
53      
54      /** Serializable version identifier */
55      private static final long serialVersionUID = 4970229902484487012L;
56  
57      /** Entries of the matrix */
58      protected double data[][] = null;
59  
60      /** Entries of cached LU decomposition.
61       *  All updates to data (other than luDecompose()) *must* set this to null
62       */
63      protected double lu[][] = null;
64  
65      /** Permutation associated with LU decomposition */
66      protected int[] permutation = null;
67  
68      /** Parity of the permutation associated with the LU decomposition */
69      protected int parity = 1;
70  
71      /** Bound to determine effective singularity in LU decomposition */
72      private static final double TOO_SMALL = 10E-12;
73  
74      /**
75       * Creates a matrix with no data
76       */
77      public RealMatrixImpl() {
78      }
79  
80      /**
81       * Create a new RealMatrix with the supplied row and column dimensions.
82       *
83       * @param rowDimension  the number of rows in the new matrix
84       * @param columnDimension  the number of columns in the new matrix
85       * @throws IllegalArgumentException if row or column dimension is not
86       *  positive
87       */
88      public RealMatrixImpl(int rowDimension, int columnDimension) {
89          if (rowDimension <= 0 || columnDimension <= 0) {
90              throw new IllegalArgumentException(
91                      "row and column dimensions must be postive");
92          }
93          data = new double[rowDimension][columnDimension];
94          lu = null;
95      }
96  
97      /**
98       * Create a new RealMatrix using the input array as the underlying
99       * data array.
100      * <p>The input array is copied, not referenced. This constructor has
101      * the same effect as calling {@link #RealMatrixImpl(double[][], boolean)}
102      * with the second argument set to <code>true</code>.</p>
103      *
104      * @param d data for new matrix
105      * @throws IllegalArgumentException if <code>d</code> is not rectangular
106      *  (not all rows have the same length) or empty
107      * @throws NullPointerException if <code>d</code> is null
108      * @see #RealMatrixImpl(double[][], boolean)
109      */
110     public RealMatrixImpl(double[][] d) {
111         copyIn(d);
112         lu = null;
113     }
114 
115     /**
116      * Create a new RealMatrix using the input array as the underlying
117      * data array.
118      * <p>If an array is built specially in order to be embedded in a
119      * RealMatrix and not used directly, the <code>copyArray</code> may be
120      * set to <code>false</code. This will prevent the copying and improve
121      * performance as no new array will be built and no data will be copied.</p>
122      * @param d data for new matrix
123      * @param copyArray if true, the input array will be copied, otherwise
124      * it will be referenced
125      * @throws IllegalArgumentException if <code>d</code> is not rectangular
126      *  (not all rows have the same length) or empty
127      * @throws NullPointerException if <code>d</code> is null
128      * @see #RealMatrixImpl(double[][])
129      */
130     public RealMatrixImpl(double[][] d, boolean copyArray) {
131         if (copyArray) {
132             copyIn(d);
133         } else {
134             if (d == null) {
135                 throw new NullPointerException();
136             }   
137             final int nRows = d.length;
138             if (nRows == 0) {
139                 throw new IllegalArgumentException("Matrix must have at least one row."); 
140             }
141             final int nCols = d[0].length;
142             if (nCols == 0) {
143                 throw new IllegalArgumentException("Matrix must have at least one column."); 
144             }
145             for (int r = 1; r < nRows; r++) {
146                 if (d[r].length != nCols) {
147                     throw new IllegalArgumentException("All input rows must have the same length.");
148                 }
149             }       
150             data = d;
151         }
152         lu = null;
153     }
154 
155     /**
156      * Create a new (column) RealMatrix using <code>v</code> as the
157      * data for the unique column of the <code>v.length x 1</code> matrix
158      * created.
159      * <p>The input array is copied, not referenced.</p>
160      *
161      * @param v column vector holding data for new matrix
162      */
163     public RealMatrixImpl(double[] v) {
164         final int nRows = v.length;
165         data = new double[nRows][1];
166         for (int row = 0; row < nRows; row++) {
167             data[row][0] = v[row];
168         }
169     }
170 
171     /**
172      * Create a new RealMatrix which is a copy of this.
173      *
174      * @return  the cloned matrix
175      */
176     public RealMatrix copy() {
177         return new RealMatrixImpl(copyOut(), false);
178     }
179 
180     /**
181      * Compute the sum of this and <code>m</code>.
182      *
183      * @param m    matrix to be added
184      * @return     this + m
185      * @throws  IllegalArgumentException if m is not the same size as this
186      */
187     public RealMatrix add(RealMatrix m) throws IllegalArgumentException {
188         try {
189             return add((RealMatrixImpl) m);
190         } catch (ClassCastException cce) {
191             final int rowCount    = getRowDimension();
192             final int columnCount = getColumnDimension();
193             if (columnCount != m.getColumnDimension() || rowCount != m.getRowDimension()) {
194                 throw new IllegalArgumentException("matrix dimension mismatch");
195             }
196             final double[][] outData = new double[rowCount][columnCount];
197             for (int row = 0; row < rowCount; row++) {
198                 final double[] dataRow    = data[row];
199                 final double[] outDataRow = outData[row];
200                 for (int col = 0; col < columnCount; col++) {
201                     outDataRow[col] = dataRow[col] + m.getEntry(row, col);
202                 }  
203             }
204             return new RealMatrixImpl(outData, false);
205         }
206     }
207 
208     /**
209      * Compute the sum of this and <code>m</code>.
210      *
211      * @param m    matrix to be added
212      * @return     this + m
213      * @throws  IllegalArgumentException if m is not the same size as this
214      */
215     public RealMatrixImpl add(RealMatrixImpl m) throws IllegalArgumentException {
216         final int rowCount    = getRowDimension();
217         final int columnCount = getColumnDimension();
218         if (columnCount != m.getColumnDimension() || rowCount != m.getRowDimension()) {
219             throw new IllegalArgumentException("matrix dimension mismatch");
220         }
221         final double[][] outData = new double[rowCount][columnCount];
222         for (int row = 0; row < rowCount; row++) {
223             final double[] dataRow    = data[row];
224             final double[] mRow       = m.data[row];
225             final double[] outDataRow = outData[row];
226             for (int col = 0; col < columnCount; col++) {
227                 outDataRow[col] = dataRow[col] + mRow[col];
228             }  
229         }
230         return new RealMatrixImpl(outData, false);
231     }
232 
233     /**
234      * Compute  this minus <code>m</code>.
235      *
236      * @param m    matrix to be subtracted
237      * @return     this + m
238      * @throws  IllegalArgumentException if m is not the same size as this
239      */
240     public RealMatrix subtract(RealMatrix m) throws IllegalArgumentException {
241         try {
242             return subtract((RealMatrixImpl) m);
243         } catch (ClassCastException cce) {
244             final int rowCount    = getRowDimension();
245             final int columnCount = getColumnDimension();
246             if (columnCount != m.getColumnDimension() || rowCount != m.getRowDimension()) {
247                 throw new IllegalArgumentException("matrix dimension mismatch");
248             }
249             final double[][] outData = new double[rowCount][columnCount];
250             for (int row = 0; row < rowCount; row++) {
251                 final double[] dataRow    = data[row];
252                 final double[] outDataRow = outData[row];
253                 for (int col = 0; col < columnCount; col++) {
254                     outDataRow[col] = dataRow[col] - m.getEntry(row, col);
255                 }  
256             }
257             return new RealMatrixImpl(outData, false);
258         }
259     }
260 
261     /**
262      * Compute  this minus <code>m</code>.
263      *
264      * @param m    matrix to be subtracted
265      * @return     this + m
266      * @throws  IllegalArgumentException if m is not the same size as this
267      */
268     public RealMatrixImpl subtract(RealMatrixImpl m) throws IllegalArgumentException {
269         final int rowCount    = getRowDimension();
270         final int columnCount = getColumnDimension();
271         if (columnCount != m.getColumnDimension() || rowCount != m.getRowDimension()) {
272             throw new IllegalArgumentException("matrix dimension mismatch");
273         }
274         final double[][] outData = new double[rowCount][columnCount];
275         for (int row = 0; row < rowCount; row++) {
276             final double[] dataRow    = data[row];
277             final double[] mRow       = m.data[row];
278             final double[] outDataRow = outData[row];
279             for (int col = 0; col < columnCount; col++) {
280                 outDataRow[col] = dataRow[col] - mRow[col];
281             }  
282         }
283         return new RealMatrixImpl(outData, false);
284     }
285 
286     /**
287      * Returns the result of adding d to each entry of this.
288      *
289      * @param d    value to be added to each entry
290      * @return     d + this
291      */
292     public RealMatrix scalarAdd(double d) {
293         final int rowCount    = getRowDimension();
294         final int columnCount = getColumnDimension();
295         final double[][] outData = new double[rowCount][columnCount];
296         for (int row = 0; row < rowCount; row++) {
297             final double[] dataRow    = data[row];
298             final double[] outDataRow = outData[row];
299             for (int col = 0; col < columnCount; col++) {
300                 outDataRow[col] = dataRow[col] + d;
301             }
302         }
303         return new RealMatrixImpl(outData, false);
304     }
305 
306     /**
307      * Returns the result of multiplying each entry of this by <code>d</code>
308      * @param d  value to multiply all entries by
309      * @return d * this
310      */
311     public RealMatrix scalarMultiply(double d) {
312         final int rowCount    = getRowDimension();
313         final int columnCount = getColumnDimension();
314         final double[][] outData = new double[rowCount][columnCount];
315         for (int row = 0; row < rowCount; row++) {
316             final double[] dataRow    = data[row];
317             final double[] outDataRow = outData[row];
318             for (int col = 0; col < columnCount; col++) {
319                 outDataRow[col] = dataRow[col] * d;
320             }
321         }
322         return new RealMatrixImpl(outData, false);
323     }
324 
325     /**
326      * Returns the result of postmultiplying this by <code>m</code>.
327      * @param m    matrix to postmultiply by
328      * @return     this*m
329      * @throws     IllegalArgumentException
330      *             if columnDimension(this) != rowDimension(m)
331      */
332     public RealMatrix multiply(RealMatrix m) throws IllegalArgumentException {
333         try {
334             return multiply((RealMatrixImpl) m);
335         } catch (ClassCastException cce) {
336             if (this.getColumnDimension() != m.getRowDimension()) {
337                 throw new IllegalArgumentException("Matrices are not multiplication compatible.");
338             }
339             final int nRows = this.getRowDimension();
340             final int nCols = m.getColumnDimension();
341             final int nSum = this.getColumnDimension();
342             final double[][] outData = new double[nRows][nCols];
343             for (int row = 0; row < nRows; row++) {
344                 final double[] dataRow    = data[row];
345                 final double[] outDataRow = outData[row];
346                 for (int col = 0; col < nCols; col++) {
347                     double sum = 0;
348                     for (int i = 0; i < nSum; i++) {
349                         sum += dataRow[i] * m.getEntry(i, col);
350                     }
351                     outDataRow[col] = sum;
352                 }
353             }
354             return new RealMatrixImpl(outData, false);
355         }
356     }
357 
358     /**
359      * Returns the result of postmultiplying this by <code>m</code>.
360      * @param m    matrix to postmultiply by
361      * @return     this*m
362      * @throws     IllegalArgumentException
363      *             if columnDimension(this) != rowDimension(m)
364      */
365     public RealMatrixImpl multiply(RealMatrixImpl m) throws IllegalArgumentException {
366         if (this.getColumnDimension() != m.getRowDimension()) {
367             throw new IllegalArgumentException("Matrices are not multiplication compatible.");
368         }
369         final int nRows = this.getRowDimension();
370         final int nCols = m.getColumnDimension();
371         final int nSum = this.getColumnDimension();
372         final double[][] outData = new double[nRows][nCols];
373         for (int row = 0; row < nRows; row++) {
374             final double[] dataRow    = data[row];
375             final double[] outDataRow = outData[row];
376             for (int col = 0; col < nCols; col++) {
377                 double sum = 0;
378                 for (int i = 0; i < nSum; i++) {
379                     sum += dataRow[i] * m.data[i][col];
380                 }
381                 outDataRow[col] = sum;
382             }
383         }            
384         return new RealMatrixImpl(outData, false);
385     }
386 
387     /**
388      * Returns the result of premultiplying this by <code>m</code>.
389      * @param m    matrix to premultiply by
390      * @return     m * this
391      * @throws     IllegalArgumentException
392      *             if rowDimension(this) != columnDimension(m)
393      */
394     public RealMatrix preMultiply(RealMatrix m) throws IllegalArgumentException {
395         return m.multiply(this);
396     }
397 
398     /**
399      * Returns matrix entries as a two-dimensional array.
400      * <p>
401      * Makes a fresh copy of the underlying data.</p>
402      *
403      * @return    2-dimensional array of entries
404      */
405     public double[][] getData() {
406         return copyOut();
407     }
408 
409     /**
410      * Returns a reference to the underlying data array.
411      * <p>
412      * Does not make a fresh copy of the underlying data.</p>
413      *
414      * @return 2-dimensional array of entries
415      */
416     public double[][] getDataRef() {
417         return data;
418     }
419 
420     /**
421      *
422      * @return norm
423      */
424     public double getNorm() {
425         double maxColSum = 0;
426         for (int col = 0; col < this.getColumnDimension(); col++) {
427             double sum = 0;
428             for (int row = 0; row < this.getRowDimension(); row++) {
429                 sum += Math.abs(data[row][col]);
430             }
431             maxColSum = Math.max(maxColSum, sum);
432         }
433         return maxColSum;
434     }
435     
436     /**
437      * Gets a submatrix. Rows and columns are indicated
438      * counting from 0 to n-1.
439      *
440      * @param startRow Initial row index
441      * @param endRow Final row index
442      * @param startColumn Initial column index
443      * @param endColumn Final column index
444      * @return The subMatrix containing the data of the
445      *         specified rows and columns
446      * @exception MatrixIndexException if row or column selections are not valid
447      */
448     public RealMatrix getSubMatrix(int startRow, int endRow,
449                                    int startColumn, int endColumn)
450         throws MatrixIndexException {
451         if (startRow < 0 || startRow > endRow || endRow > data.length ||
452              startColumn < 0 || startColumn > endColumn ||
453              endColumn > data[0].length) {
454             throw new MatrixIndexException(
455                     "invalid row or column index selection");
456         }
457         final double[][] subMatrixData =
458             new double[endRow - startRow + 1][endColumn - startColumn + 1];
459         for (int i = startRow; i <= endRow; i++) {
460             System.arraycopy(data[i], startColumn,
461                              subMatrixData[i - startRow], 0,
462                              endColumn - startColumn + 1);
463         }
464         return new RealMatrixImpl(subMatrixData, false);
465     }
466     
467     /**
468      * Gets a submatrix. Rows and columns are indicated
469      * counting from 0 to n-1.
470      *
471      * @param selectedRows Array of row indices must be non-empty
472      * @param selectedColumns Array of column indices must be non-empty
473      * @return The subMatrix containing the data in the
474      *     specified rows and columns
475      * @exception MatrixIndexException  if supplied row or column index arrays
476      *     are not valid
477      */
478     public RealMatrix getSubMatrix(int[] selectedRows, int[] selectedColumns)
479         throws MatrixIndexException {
480         if (selectedRows.length * selectedColumns.length == 0) {
481             throw new MatrixIndexException(
482                     "selected row and column index arrays must be non-empty");
483         }
484         final double[][] subMatrixData =
485             new double[selectedRows.length][selectedColumns.length];
486         try  {
487             for (int i = 0; i < selectedRows.length; i++) {
488                 final double[] subI = subMatrixData[i];
489                 final double[] dataSelectedI = data[selectedRows[i]];
490                 for (int j = 0; j < selectedColumns.length; j++) {
491                     subI[j] = dataSelectedI[selectedColumns[j]];
492                 }
493             }
494         } catch (ArrayIndexOutOfBoundsException e) {
495             throw new MatrixIndexException("matrix dimension mismatch");
496         }
497         return new RealMatrixImpl(subMatrixData, false);
498     } 
499 
500     /**
501      * Replace the submatrix starting at <code>row, column</code> using data in
502      * the input <code>subMatrix</code> array. Indexes are 0-based.
503      * <p> 
504      * Example:<br>
505      * Starting with <pre>
506      * 1  2  3  4
507      * 5  6  7  8
508      * 9  0  1  2
509      * </pre>
510      * and <code>subMatrix = {{3, 4} {5,6}}</code>, invoking 
511      * <code>setSubMatrix(subMatrix,1,1))</code> will result in <pre>
512      * 1  2  3  4
513      * 5  3  4  8
514      * 9  5  6  2
515      * </pre></p>
516      * 
517      * @param subMatrix  array containing the submatrix replacement data
518      * @param row  row coordinate of the top, left element to be replaced
519      * @param column  column coordinate of the top, left element to be replaced
520      * @throws MatrixIndexException  if subMatrix does not fit into this 
521      *    matrix from element in (row, column) 
522      * @throws IllegalArgumentException if <code>subMatrix</code> is not rectangular
523      *  (not all rows have the same length) or empty
524      * @throws NullPointerException if <code>subMatrix</code> is null
525      * @since 1.1
526      */
527     public void setSubMatrix(double[][] subMatrix, int row, int column) 
528         throws MatrixIndexException {
529         if ((row < 0) || (column < 0)){
530             throw new MatrixIndexException
531                 ("invalid row or column index selection");          
532         }
533         final int nRows = subMatrix.length;
534         if (nRows == 0) {
535             throw new IllegalArgumentException(
536             "Matrix must have at least one row."); 
537         }
538         final int nCols = subMatrix[0].length;
539         if (nCols == 0) {
540             throw new IllegalArgumentException(
541             "Matrix must have at least one column."); 
542         }
543         for (int r = 1; r < nRows; r++) {
544             if (subMatrix[r].length != nCols) {
545                 throw new IllegalArgumentException(
546                 "All input rows must have the same length.");
547             }
548         }       
549         if (data == null) {
550             if ((row > 0)||(column > 0)) throw new MatrixIndexException
551                 ("matrix must be initialized to perfom this method");
552             data = new double[nRows][nCols];
553             System.arraycopy(subMatrix, 0, data, 0, subMatrix.length);          
554         }   
555         if (((nRows + row) > this.getRowDimension()) ||
556             (nCols + column > this.getColumnDimension()))
557             throw new MatrixIndexException(
558                     "invalid row or column index selection");                   
559         for (int i = 0; i < nRows; i++) {
560             System.arraycopy(subMatrix[i], 0, data[row + i], column, nCols);
561         } 
562         lu = null;
563     }
564     
565     /**
566      * Returns the entries in row number <code>row</code> as a row matrix.
567      * Row indices start at 0.
568      * 
569      * @param row  the row to be fetched
570      * @return row matrix
571      * @throws MatrixIndexException if the specified row index is invalid
572      */
573     public RealMatrix getRowMatrix(int row) throws MatrixIndexException {
574         if ( !isValidCoordinate( row, 0)) {
575             throw new MatrixIndexException("illegal row argument");
576         }
577         final int ncols = this.getColumnDimension();
578         final double[][] out = new double[1][ncols]; 
579         System.arraycopy(data[row], 0, out[0], 0, ncols);
580         return new RealMatrixImpl(out, false);
581     }
582     
583     /**
584      * Returns the entries in column number <code>column</code>
585      * as a column matrix.  Column indices start at 0.
586      *
587      * @param column the column to be fetched
588      * @return column matrix
589      * @throws MatrixIndexException if the specified column index is invalid
590      */
591     public RealMatrix getColumnMatrix(int column) throws MatrixIndexException {
592         if ( !isValidCoordinate( 0, column)) {
593             throw new MatrixIndexException("illegal column argument");
594         }
595         final int nRows = this.getRowDimension();
596         final double[][] out = new double[nRows][1]; 
597         for (int row = 0; row < nRows; row++) {
598             out[row][0] = data[row][column];
599         }
600         return new RealMatrixImpl(out, false);
601     }
602 
603     /** {@inheritDoc} */
604     public RealVector getColumnVector(int column) throws MatrixIndexException {
605         return new RealVectorImpl(getColumn(column), false);
606     }
607 
608     /** {@inheritDoc} */
609     public RealVector getRowVector(int row) throws MatrixIndexException {
610         return new RealVectorImpl(getRow(row), false);
611     }
612 
613     /**
614      * Returns the entries in row number <code>row</code> as an array.
615      * <p>
616      * Row indices start at 0.  A <code>MatrixIndexException</code> is thrown
617      * unless <code>0 <= row < rowDimension.</code></p>
618      *
619      * @param row the row to be fetched
620      * @return array of entries in the row
621      * @throws MatrixIndexException if the specified row index is not valid
622      */
623     public double[] getRow(int row) throws MatrixIndexException {
624         if ( !isValidCoordinate( row, 0 ) ) {
625             throw new MatrixIndexException("illegal row argument");
626         }
627         final int ncols = this.getColumnDimension();
628         final double[] out = new double[ncols];
629         System.arraycopy(data[row], 0, out, 0, ncols);
630         return out;
631     }
632 
633     /**
634      * Returns the entries in column number <code>col</code> as an array.
635      * <p>
636      * Column indices start at 0.  A <code>MatrixIndexException</code> is thrown
637      * unless <code>0 <= column < columnDimension.</code></p>
638      *
639      * @param col the column to be fetched
640      * @return array of entries in the column
641      * @throws MatrixIndexException if the specified column index is not valid
642      */
643     public double[] getColumn(int col) throws MatrixIndexException {
644         if ( !isValidCoordinate(0, col) ) {
645             throw new MatrixIndexException("illegal column argument");
646         }
647         final int nRows = this.getRowDimension();
648         final double[] out = new double[nRows];
649         for (int row = 0; row < nRows; row++) {
650             out[row] = data[row][col];
651         }
652         return out;
653     }
654 
655     /**
656      * Returns the entry in the specified row and column.
657      * <p>
658      * Row and column indices start at 0 and must satisfy 
659      * <ul>
660      * <li><code>0 <= row < rowDimension</code></li>
661      * <li><code> 0 <= column < columnDimension</code></li>
662      * </ul>
663      * otherwise a <code>MatrixIndexException</code> is thrown.</p>
664      * 
665      * @param row  row location of entry to be fetched
666      * @param column  column location of entry to be fetched
667      * @return matrix entry in row,column
668      * @throws MatrixIndexException if the row or column index is not valid
669      */
670     public double getEntry(int row, int column)
671         throws MatrixIndexException {
672         try {
673             return data[row][column];
674         } catch (ArrayIndexOutOfBoundsException e) {
675             throw new MatrixIndexException("matrix entry does not exist");
676         }
677     }
678 
679     /**
680      * Returns the transpose matrix.
681      *
682      * @return transpose matrix
683      */
684     public RealMatrix transpose() {
685         final int nRows = getRowDimension();
686         final int nCols = getColumnDimension();
687         final double[][] outData = new double[nCols][nRows];
688         for (int row = 0; row < nRows; row++) {
689             final double[] dataRow = data[row];
690             for (int col = 0; col < nCols; col++) {
691                 outData[col][row] = dataRow[col];
692             }
693         }
694         return new RealMatrixImpl(outData, false);
695     }
696 
697     /**
698      * Returns the inverse matrix if this matrix is invertible.
699      *
700      * @return inverse matrix
701      * @throws InvalidMatrixException if this is not invertible
702      */
703     public RealMatrix inverse() throws InvalidMatrixException {
704         return solve(MatrixUtils.createRealIdentityMatrix(getRowDimension()));
705     }
706 
707     /**
708      * @return determinant
709      * @throws InvalidMatrixException if matrix is not square
710      */
711     public double getDeterminant() throws InvalidMatrixException {
712         if (!isSquare()) {
713             throw new InvalidMatrixException("matrix is not square");
714         }
715         if (isSingular()) {   // note: this has side effect of attempting LU decomp if lu == null
716             return 0d;
717         } else {
718             double det = parity;
719             for (int i = 0; i < this.getRowDimension(); i++) {
720                 det *= lu[i][i];
721             }
722             return det;
723         }
724     }
725 
726     /**
727      * @return true if the matrix is square (rowDimension = columnDimension)
728      */
729     public boolean isSquare() {
730         return (this.getColumnDimension() == this.getRowDimension());
731     }
732 
733     /**
734      * @return true if the matrix is singular
735      */
736     public boolean isSingular() {
737         if (lu == null) {
738             try {
739                 luDecompose();
740                 return false;
741             } catch (InvalidMatrixException ex) {
742                 return true;
743             }
744         } else { // LU decomp must have been successfully performed
745             return false; // so the matrix is not singular
746         }
747     }
748 
749     /**
750      * @return rowDimension
751      */
752     public int getRowDimension() {
753         return data.length;
754     }
755 
756     /**
757      * @return columnDimension
758      */
759     public int getColumnDimension() {
760         return data[0].length;
761     }
762 
763     /**
764      * @return trace
765      * @throws IllegalArgumentException if the matrix is not square
766      */
767     public double getTrace() throws IllegalArgumentException {
768         if (!isSquare()) {
769             throw new IllegalArgumentException("matrix is not square");
770         }
771         double trace = data[0][0];
772         for (int i = 1; i < this.getRowDimension(); i++) {
773             trace += data[i][i];
774         }
775         return trace;
776     }
777 
778     /**
779      * @param v vector to operate on
780      * @throws IllegalArgumentException if columnDimension != v.length
781      * @return resulting vector
782      */
783     public double[] operate(double[] v) throws IllegalArgumentException {
784         final int nRows = this.getRowDimension();
785         final int nCols = this.getColumnDimension();
786         if (v.length != nCols) {
787             throw new IllegalArgumentException("vector has wrong length");
788         }
789         final double[] out = new double[nRows];
790         for (int row = 0; row < nRows; row++) {
791             final double[] dataRow = data[row];
792             double sum = 0;
793             for (int i = 0; i < nCols; i++) {
794                 sum += dataRow[i] * v[i];
795             }
796             out[row] = sum;
797         }
798         return out;
799     }
800 
801     /** {@inheritDoc} */
802     public RealVector operate(RealVector v) throws IllegalArgumentException {
803         try {
804             return operate((RealVectorImpl) v);
805         } catch (ClassCastException cce) {
806             final int nRows = this.getRowDimension();
807             final int nCols = this.getColumnDimension();
808             if (v.getDimension() != nCols) {
809                 throw new IllegalArgumentException("vector has wrong length");
810             }
811             final double[] out = new double[nRows];
812             for (int row = 0; row < nRows; row++) {
813                 final double[] dataRow = data[row];
814                 double sum = 0;
815                 for (int i = 0; i < nCols; i++) {
816                     sum += dataRow[i] * v.getEntry(i);
817                 }
818                 out[row] = sum;
819             }
820             return new RealVectorImpl(out, false);
821         }
822     }
823 
824     /**
825      * Returns the result of multiplying this by the vector <code>v</code>.
826      *
827      * @param v the vector to operate on
828      * @return this*v
829      * @throws IllegalArgumentException if columnDimension != v.size()
830      */
831     public RealVectorImpl operate(RealVectorImpl v) throws IllegalArgumentException {
832         return new RealVectorImpl(operate(v.getDataRef()), false);
833     }
834 
835     /**
836      * @param v vector to premultiply by
837      * @throws IllegalArgumentException if rowDimension != v.length
838      * @return resulting matrix
839      */
840     public double[] preMultiply(double[] v) throws IllegalArgumentException {
841         final int nRows = this.getRowDimension();
842         if (v.length != nRows) {
843             throw new IllegalArgumentException("vector has wrong length");
844         }
845         final int nCols = this.getColumnDimension();
846         final double[] out = new double[nCols];
847         for (int col = 0; col < nCols; col++) {
848             double sum = 0;
849             for (int i = 0; i < nRows; i++) {
850                 sum += data[i][col] * v[i];
851             }
852             out[col] = sum;
853         }
854         return out;
855     }
856 
857     /** {@inheritDoc} */
858     public RealVector preMultiply(RealVector v) throws IllegalArgumentException {
859         try {
860             return preMultiply((RealVectorImpl) v);
861         } catch (ClassCastException cce) {
862             final int nRows = this.getRowDimension();
863             if (v.getDimension() != nRows) {
864                 throw new IllegalArgumentException("vector has wrong length");
865             }
866             final int nCols = this.getColumnDimension();
867             final double[] out = new double[nCols];
868             for (int col = 0; col < nCols; col++) {
869                 double sum = 0;
870                 for (int i = 0; i < nRows; i++) {
871                     sum += data[i][col] * v.getEntry(i);
872                 }
873                 out[col] = sum;
874             }
875             return new RealVectorImpl(out, false);
876         }
877     }
878 
879     /**
880      * Returns the (row) vector result of premultiplying this by the vector <code>v</code>.
881      *
882      * @param v the row vector to premultiply by
883      * @return v*this
884      * @throws IllegalArgumentException if rowDimension != v.size()
885      */
886     RealVectorImpl preMultiply(RealVectorImpl v) throws IllegalArgumentException {
887         return new RealVectorImpl(preMultiply(v.getDataRef()), false);
888     }
889 
890     /**
891      * Returns a matrix of (column) solution vectors for linear systems with
892      * coefficient matrix = this and constant vectors = columns of
893      * <code>b</code>.
894      *
895      * @param b  array of constant forming RHS of linear systems to
896      * to solve
897      * @return solution array
898      * @throws IllegalArgumentException if this.rowDimension != row dimension
899      * @throws InvalidMatrixException if this matrix is not square or is singular
900      */
901     public double[] solve(double[] b) throws IllegalArgumentException, InvalidMatrixException {
902 
903         final int nRows = this.getRowDimension();
904         final int nCol  = this.getColumnDimension();
905 
906         if (b.length != nRows) {
907             throw new IllegalArgumentException("constant vector has wrong length");
908         }
909         if (!isSquare()) {
910             throw new InvalidMatrixException("coefficient matrix is not square");
911         }
912         if (isSingular()) { // side effect: compute LU decomp
913             throw new InvalidMatrixException("Matrix is singular.");
914         }
915 
916         final double[] bp = new double[nRows];
917 
918         // Apply permutations to b
919         for (int row = 0; row < nRows; row++) {
920             bp[row] = b[permutation[row]];
921         }
922 
923         // Solve LY = b
924         for (int col = 0; col < nCol; col++) {
925             for (int i = col + 1; i < nCol; i++) {
926                 bp[i] -= bp[col] * lu[i][col];
927             }
928         }
929 
930         // Solve UX = Y
931         for (int col = nCol - 1; col >= 0; col--) {
932             bp[col] /= lu[col][col];
933             for (int i = 0; i < col; i++) {
934                 bp[i] -= bp[col] * lu[i][col];
935             }
936         }
937 
938         return bp;
939 
940     }
941 
942     /** {@inheritDoc} */
943     public RealVector solve(RealVector b)
944         throws IllegalArgumentException, InvalidMatrixException {
945         try {
946             return solve((RealVectorImpl) b);
947         } catch (ClassCastException cce) {
948 
949             final int nRows = this.getRowDimension();
950             final int nCol  = this.getColumnDimension();
951 
952             if (b.getDimension() != nRows) {
953                 throw new IllegalArgumentException("constant vector has wrong length");
954             }
955             if (!isSquare()) {
956                 throw new InvalidMatrixException("coefficient matrix is not square");
957             }
958             if (isSingular()) { // side effect: compute LU decomp
959                 throw new InvalidMatrixException("Matrix is singular.");
960             }
961 
962             final double[] bp = new double[nRows];
963 
964             // Apply permutations to b
965             for (int row = 0; row < nRows; row++) {
966                 bp[row] = b.getEntry(permutation[row]);
967             }
968 
969             // Solve LY = b
970             for (int col = 0; col < nCol; col++) {
971                 for (int i = col + 1; i < nCol; i++) {
972                     bp[i] -= bp[col] * lu[i][col];
973                 }
974             }
975 
976             // Solve UX = Y
977             for (int col = nCol - 1; col >= 0; col--) {
978                 bp[col] /= lu[col][col];
979                 for (int i = 0; i < col; i++) {
980                     bp[i] -= bp[col] * lu[i][col];
981                 }
982             }
983 
984             return new RealVectorImpl(bp, false);
985 
986         }
987     }
988 
989     /**
990      * Returns the solution vector for a linear system with coefficient
991      * matrix = this and constant vector = <code>b</code>.
992      *
993      * @param b  constant vector
994      * @return vector of solution values to AX = b, where A is *this
995      * @throws IllegalArgumentException if this.rowDimension != b.length
996      * @throws InvalidMatrixException if this matrix is not square or is singular
997      */
998     RealVectorImpl solve(RealVectorImpl b)
999         throws IllegalArgumentException, InvalidMatrixException {
1000         return new RealVectorImpl(solve(b.getDataRef()), false);
1001     }
1002 
1003     /**
1004      * Returns a matrix of (column) solution vectors for linear systems with
1005      * coefficient matrix = this and constant vectors = columns of
1006      * <code>b</code>.
1007      *
1008      * @param b  matrix of constant vectors forming RHS of linear systems to
1009      * to solve
1010      * @return matrix of solution vectors
1011      * @throws IllegalArgumentException if this.rowDimension != row dimension
1012      * @throws InvalidMatrixException if this matrix is not square or is singular
1013      */
1014     public RealMatrix solve(RealMatrix b) throws IllegalArgumentException, InvalidMatrixException  {
1015         if (b.getRowDimension() != this.getRowDimension()) {
1016             throw new IllegalArgumentException("Incorrect row dimension");
1017         }
1018         if (!this.isSquare()) {
1019             throw new InvalidMatrixException("coefficient matrix is not square");
1020         }
1021         if (this.isSingular()) { // side effect: compute LU decomp
1022             throw new InvalidMatrixException("Matrix is singular.");
1023         }
1024 
1025         final int nCol  = this.getColumnDimension();
1026         final int nColB = b.getColumnDimension();
1027         final int nRowB = b.getRowDimension();
1028 
1029         // Apply permutations to b
1030         final double[][] bp = new double[nRowB][nColB];
1031         for (int row = 0; row < nRowB; row++) {
1032             final double[] bpRow = bp[row];
1033             final int pRow = permutation[row];
1034             for (int col = 0; col < nColB; col++) {
1035                 bpRow[col] = b.getEntry(pRow, col);
1036             }
1037         }
1038 
1039         // Solve LY = b
1040         for (int col = 0; col < nCol; col++) {
1041             final double[] bpCol = bp[col];
1042             for (int i = col + 1; i < nCol; i++) {
1043                 final double[] bpI = bp[i];
1044                 final double luICol = lu[i][col];
1045                 for (int j = 0; j < nColB; j++) {
1046                     bpI[j] -= bpCol[j] * luICol;
1047                 }
1048             }
1049         }
1050 
1051         // Solve UX = Y
1052         for (int col = nCol - 1; col >= 0; col--) {
1053             final double[] bpCol = bp[col];
1054             final double luDiag = lu[col][col];
1055             for (int j = 0; j < nColB; j++) {
1056                 bpCol[j] /= luDiag;
1057             }
1058             for (int i = 0; i < col; i++) {
1059                 final double[] bpI = bp[i];
1060                 final double luICol = lu[i][col];
1061                 for (int j = 0; j < nColB; j++) {
1062                     bpI[j] -= bpCol[j] * luICol;
1063                 }
1064             }
1065         }
1066 
1067         return new RealMatrixImpl(bp, false);
1068 
1069     }
1070 
1071     /**
1072      * Computes a new
1073      * <a href="http://www.math.gatech.edu/~bourbaki/math2601/Web-notes/2num.pdf">
1074      * LU decomposition</a> for this matrix, storing the result for use by other methods.
1075      * <p>
1076      * <strong>Implementation Note</strong>:<br>
1077      * Uses <a href="http://www.damtp.cam.ac.uk/user/fdl/people/sd/lectures/nummeth98/linear.htm">
1078      * Crout's algorithm</a>, with partial pivoting.</p>
1079      * <p>
1080      * <strong>Usage Note</strong>:<br>
1081      * This method should rarely be invoked directly. Its only use is
1082      * to force recomputation of the LU decomposition when changes have been
1083      * made to the underlying data using direct array references. Changes