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  
22  import org.apache.commons.math.MathRuntimeException;
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: 783702 $ $Date: 2009-06-11 04:54:02 -0400 (Thu, 11 Jun 2009) $
51   * @deprecated as of 2.0 replaced by {@link Array2DRowRealMatrix}
52   */
53  @Deprecated
54  public class RealMatrixImpl extends AbstractRealMatrix implements Serializable {
55      
56      /** Serializable version identifier */
57      private static final long serialVersionUID = -1067294169172445528L;
58  
59      /** Entries of the matrix */
60      protected double data[][];
61  
62      /**
63       * Creates a matrix with no data
64       */
65      public RealMatrixImpl() {
66      }
67  
68      /**
69       * Create a new RealMatrix with the supplied row and column dimensions.
70       *
71       * @param rowDimension  the number of rows in the new matrix
72       * @param columnDimension  the number of columns in the new matrix
73       * @throws IllegalArgumentException if row or column dimension is not
74       *  positive
75       */
76      public RealMatrixImpl(final int rowDimension, final int columnDimension)
77          throws IllegalArgumentException {
78          super(rowDimension, columnDimension);
79          data = new double[rowDimension][columnDimension];
80      }
81  
82      /**
83       * Create a new RealMatrix using the input array as the underlying
84       * data array.
85       * <p>The input array is copied, not referenced. This constructor has
86       * the same effect as calling {@link #RealMatrixImpl(double[][], boolean)}
87       * with the second argument set to <code>true</code>.</p>
88       *
89       * @param d data for new matrix
90       * @throws IllegalArgumentException if <code>d</code> is not rectangular
91       *  (not all rows have the same length) or empty
92       * @throws NullPointerException if <code>d</code> is null
93       * @see #RealMatrixImpl(double[][], boolean)
94       */
95      public RealMatrixImpl(final double[][] d)
96          throws IllegalArgumentException, NullPointerException {
97          copyIn(d);
98      }
99  
100     /**
101      * Create a new RealMatrix using the input array as the underlying
102      * data array.
103      * <p>If an array is built specially in order to be embedded in a
104      * RealMatrix and not used directly, the <code>copyArray</code> may be
105      * set to <code>false</code. This will prevent the copying and improve
106      * performance as no new array will be built and no data will be copied.</p>
107      * @param d data for new matrix
108      * @param copyArray if true, the input array will be copied, otherwise
109      * it will be referenced
110      * @throws IllegalArgumentException if <code>d</code> is not rectangular
111      *  (not all rows have the same length) or empty
112      * @throws NullPointerException if <code>d</code> is null
113      * @see #RealMatrixImpl(double[][])
114      */
115     public RealMatrixImpl(final double[][] d, final boolean copyArray)
116         throws IllegalArgumentException, NullPointerException {
117         if (copyArray) {
118             copyIn(d);
119         } else {
120             if (d == null) {
121                 throw new NullPointerException();
122             }   
123             final int nRows = d.length;
124             if (nRows == 0) {
125                 throw MathRuntimeException.createIllegalArgumentException("matrix must have at least one row"); 
126             }
127             final int nCols = d[0].length;
128             if (nCols == 0) {
129                 throw MathRuntimeException.createIllegalArgumentException("matrix must have at least one column"); 
130             }
131             for (int r = 1; r < nRows; r++) {
132                 if (d[r].length != nCols) {
133                     throw MathRuntimeException.createIllegalArgumentException(
134                             "some rows have length {0} while others have length {1}",
135                             nCols, d[r].length);
136                 }
137             }       
138             data = d;
139         }
140     }
141 
142     /**
143      * Create a new (column) RealMatrix using <code>v</code> as the
144      * data for the unique column of the <code>v.length x 1</code> matrix
145      * created.
146      * <p>The input array is copied, not referenced.</p>
147      *
148      * @param v column vector holding data for new matrix
149      */
150     public RealMatrixImpl(final double[] v) {
151         final int nRows = v.length;
152         data = new double[nRows][1];
153         for (int row = 0; row < nRows; row++) {
154             data[row][0] = v[row];
155         }
156     }
157 
158     /** {@inheritDoc} */
159     @Override
160     public RealMatrix createMatrix(final int rowDimension, final int columnDimension)
161         throws IllegalArgumentException {
162         return new RealMatrixImpl(rowDimension, columnDimension);
163     }
164 
165     /** {@inheritDoc} */
166     @Override
167     public RealMatrix copy() {
168         return new RealMatrixImpl(copyOut(), false);
169     }
170 
171     /** {@inheritDoc} */
172     @Override
173     public RealMatrix add(final RealMatrix m)
174         throws IllegalArgumentException {
175         try {
176             return add((RealMatrixImpl) m);
177         } catch (ClassCastException cce) {
178             return super.add(m);
179         }
180     }
181 
182     /**
183      * Compute the sum of this and <code>m</code>.
184      *
185      * @param m    matrix to be added
186      * @return     this + m
187      * @throws  IllegalArgumentException if m is not the same size as this
188      */
189     public RealMatrixImpl add(final RealMatrixImpl m)
190         throws IllegalArgumentException {
191 
192         // safety check
193         MatrixUtils.checkAdditionCompatible(this, m);
194 
195         final int rowCount    = getRowDimension();
196         final int columnCount = getColumnDimension();
197         final double[][] outData = new double[rowCount][columnCount];
198         for (int row = 0; row < rowCount; row++) {
199             final double[] dataRow    = data[row];
200             final double[] mRow       = m.data[row];
201             final double[] outDataRow = outData[row];
202             for (int col = 0; col < columnCount; col++) {
203                 outDataRow[col] = dataRow[col] + mRow[col];
204             }
205         }
206 
207         return new RealMatrixImpl(outData, false);
208 
209     }
210 
211     /** {@inheritDoc} */
212     @Override
213     public RealMatrix subtract(final RealMatrix m)
214         throws IllegalArgumentException {
215         try {
216             return subtract((RealMatrixImpl) m);
217         } catch (ClassCastException cce) {
218             return super.subtract(m);
219         }
220     }
221 
222     /**
223      * Compute  this minus <code>m</code>.
224      *
225      * @param m    matrix to be subtracted
226      * @return     this + m
227      * @throws  IllegalArgumentException if m is not the same size as this
228      */
229     public RealMatrixImpl subtract(final RealMatrixImpl m)
230         throws IllegalArgumentException {
231 
232         // safety check
233         MatrixUtils.checkSubtractionCompatible(this, m);
234 
235         final int rowCount    = getRowDimension();
236         final int columnCount = getColumnDimension();
237         final double[][] outData = new double[rowCount][columnCount];
238         for (int row = 0; row < rowCount; row++) {
239             final double[] dataRow    = data[row];
240             final double[] mRow       = m.data[row];
241             final double[] outDataRow = outData[row];
242             for (int col = 0; col < columnCount; col++) {
243                 outDataRow[col] = dataRow[col] - mRow[col];
244             }
245         }
246 
247         return new RealMatrixImpl(outData, false);
248 
249     }
250 
251     /** {@inheritDoc} */
252     @Override
253     public RealMatrix multiply(final RealMatrix m)
254         throws IllegalArgumentException {
255         try {
256             return multiply((RealMatrixImpl) m);
257         } catch (ClassCastException cce) {
258             return super.multiply(m);
259         }
260     }
261 
262     /**
263      * Returns the result of postmultiplying this by <code>m</code>.
264      * @param m    matrix to postmultiply by
265      * @return     this*m
266      * @throws     IllegalArgumentException
267      *             if columnDimension(this) != rowDimension(m)
268      */
269     public RealMatrixImpl multiply(final RealMatrixImpl m)
270         throws IllegalArgumentException {
271 
272         // safety check
273         MatrixUtils.checkMultiplicationCompatible(this, m);
274 
275         final int nRows = this.getRowDimension();
276         final int nCols = m.getColumnDimension();
277         final int nSum = this.getColumnDimension();
278         final double[][] outData = new double[nRows][nCols];
279         for (int row = 0; row < nRows; row++) {
280             final double[] dataRow    = data[row];
281             final double[] outDataRow = outData[row];
282             for (int col = 0; col < nCols; col++) {
283                 double sum = 0;
284                 for (int i = 0; i < nSum; i++) {
285                     sum += dataRow[i] * m.data[i][col];
286                 }
287                 outDataRow[col] = sum;
288             }
289         }
290 
291         return new RealMatrixImpl(outData, false);
292 
293     }
294 
295     /** {@inheritDoc} */
296     @Override
297     public double[][] getData() {
298         return copyOut();
299     }
300 
301     /**
302      * Returns a reference to the underlying data array.
303      * <p>
304      * Does <strong>not</strong> make a fresh copy of the underlying data.</p>
305      *
306      * @return 2-dimensional array of entries
307      */
308     public double[][] getDataRef() {
309         return data;
310     }
311 
312     /** {@inheritDoc} */
313     @Override
314     public void setSubMatrix(final double[][] subMatrix, final int row, final int column) 
315     throws MatrixIndexException {
316         if (data == null) {
317             if (row > 0) {
318                 throw MathRuntimeException.createIllegalStateException(
319                         "first {0} rows are not initialized yet",
320                         row);
321             }
322             if (column > 0) {
323                 throw MathRuntimeException.createIllegalStateException(
324                         "first {0} columns are not initialized yet",
325                         column);
326             }
327             final int nRows = subMatrix.length;
328             if (nRows == 0) {
329                 throw MathRuntimeException.createIllegalArgumentException("matrix must have at least one row"); 
330             }
331 
332             final int nCols = subMatrix[0].length;
333             if (nCols == 0) {
334                 throw MathRuntimeException.createIllegalArgumentException("matrix must have at least one column"); 
335             }
336             data = new double[subMatrix.length][nCols];
337             for (int i = 0; i < data.length; ++i) {
338                 if (subMatrix[i].length != nCols) {
339                     throw MathRuntimeException.createIllegalArgumentException(
340                             "some rows have length {0} while others have length {1}",
341                             nCols, subMatrix[i].length); 
342                 }
343                 System.arraycopy(subMatrix[i], 0, data[i + row], column, nCols);
344             }
345         } else {
346             super.setSubMatrix(subMatrix, row, column);
347         }
348 
349     }
350 
351     /** {@inheritDoc} */
352     @Override
353     public double getEntry(final int row, final int column)
354         throws MatrixIndexException {
355         try {
356             return data[row][column];
357         } catch (ArrayIndexOutOfBoundsException e) {
358             throw new MatrixIndexException(
359                     "no entry at indices ({0}, {1}) in a {2}x{3} matrix",
360                     row, column, getRowDimension(), getColumnDimension());
361         }
362     }
363 
364     /** {@inheritDoc} */
365     @Override
366     public void setEntry(final int row, final int column, final double value)
367         throws MatrixIndexException {
368         try {
369             data[row][column] = value;
370         } catch (ArrayIndexOutOfBoundsException e) {
371             throw new MatrixIndexException(
372                     "no entry at indices ({0}, {1}) in a {2}x{3} matrix",
373                     row, column, getRowDimension(), getColumnDimension());
374         }
375     }
376 
377     /** {@inheritDoc} */
378     @Override
379     public void addToEntry(final int row, final int column, final double increment)
380         throws MatrixIndexException {
381         try {
382             data[row][column] += increment;
383         } catch (ArrayIndexOutOfBoundsException e) {
384             throw new MatrixIndexException(
385                     "no entry at indices ({0}, {1}) in a {2}x{3} matrix",
386                     row, column, getRowDimension(), getColumnDimension());
387         }      
388     }
389 
390     /** {@inheritDoc} */
391     @Override
392     public void multiplyEntry(final int row, final int column, final double factor)
393         throws MatrixIndexException {
394         try {
395             data[row][column] *= factor;
396         } catch (ArrayIndexOutOfBoundsException e) {
397             throw new MatrixIndexException(
398                     "no entry at indices ({0}, {1}) in a {2}x{3} matrix",
399                     row, column, getRowDimension(), getColumnDimension());
400         }      
401     }
402 
403     /** {@inheritDoc} */
404     @Override
405     public int getRowDimension() {
406         return (data == null) ? 0 : data.length;
407     }
408 
409     /** {@inheritDoc} */
410     @Override
411     public int getColumnDimension() {
412         return ((data == null) || (data[0] == null)) ? 0 : data[0].length;
413     }
414 
415     /** {@inheritDoc} */
416     @Override
417     public double[] operate(final double[] v)
418         throws IllegalArgumentException {
419         final int nRows = this.getRowDimension();
420         final int nCols = this.getColumnDimension();
421         if (v.length != nCols) {
422             throw MathRuntimeException.createIllegalArgumentException(
423                     "vector length mismatch: got {0} but expected {1}",
424                     v.length, nCols);
425         }
426         final double[] out = new double[nRows];
427         for (int row = 0; row < nRows; row++) {
428             final double[] dataRow = data[row];
429             double sum = 0;
430             for (int i = 0; i < nCols; i++) {
431                 sum += dataRow[i] * v[i];
432             }
433             out[row] = sum;
434         }
435         return out;
436     }
437 
438     /** {@inheritDoc} */
439     @Override
440     public double[] preMultiply(final double[] v)
441         throws IllegalArgumentException {
442 
443         final int nRows = getRowDimension();
444         final int nCols = getColumnDimension();
445         if (v.length != nRows) {
446             throw MathRuntimeException.createIllegalArgumentException(
447                     "vector length mismatch: got {0} but expected {1}",
448                     v.length, nRows);
449         }
450 
451         final double[] out = new double[nCols];
452         for (int col = 0; col < nCols; ++col) {
453             double sum = 0;
454             for (int i = 0; i < nRows; ++i) {
455                 sum += data[i][col] * v[i];
456             }
457             out[col] = sum;
458         }
459 
460         return out;
461 
462     }
463 
464     /** {@inheritDoc} */
465     @Override
466     public double walkInRowOrder(final RealMatrixChangingVisitor visitor)
467         throws MatrixVisitorException {
468         final int rows    = getRowDimension();
469         final int columns = getColumnDimension();
470         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
471         for (int i = 0; i < rows; ++i) {
472             final double[] rowI = data[i];
473             for (int j = 0; j < columns; ++j) {
474                 rowI[j] = visitor.visit(i, j, rowI[j]);
475             }
476         }
477         return visitor.end();
478     }
479 
480     /** {@inheritDoc} */
481     @Override
482     public double walkInRowOrder(final RealMatrixPreservingVisitor visitor)
483         throws MatrixVisitorException {
484         final int rows    = getRowDimension();
485         final int columns = getColumnDimension();
486         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
487         for (int i = 0; i < rows; ++i) {
488             final double[] rowI = data[i];
489             for (int j = 0; j < columns; ++j) {
490                 visitor.visit(i, j, rowI[j]);
491             }
492         }
493         return visitor.end();
494     }
495 
496     /** {@inheritDoc} */
497     @Override
498     public double walkInRowOrder(final RealMatrixChangingVisitor visitor,
499                                  final int startRow, final int endRow,
500                                  final int startColumn, final int endColumn)
501         throws MatrixIndexException, MatrixVisitorException {
502         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
503         visitor.start(getRowDimension(), getColumnDimension(),
504                       startRow, endRow, startColumn, endColumn);
505         for (int i = startRow; i <= endRow; ++i) {
506             final double[] rowI = data[i];
507             for (int j = startColumn; j <= endColumn; ++j) {
508                 rowI[j] = visitor.visit(i, j, rowI[j]);
509             }
510         }
511         return visitor.end();
512     }
513 
514     /** {@inheritDoc} */
515     @Override
516     public double walkInRowOrder(final RealMatrixPreservingVisitor visitor,
517                                  final int startRow, final int endRow,
518                                  final int startColumn, final int endColumn)
519         throws MatrixIndexException, MatrixVisitorException {
520         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
521         visitor.start(getRowDimension(), getColumnDimension(),
522                       startRow, endRow, startColumn, endColumn);
523         for (int i = startRow; i <= endRow; ++i) {
524             final double[] rowI = data[i];
525             for (int j = startColumn; j <= endColumn; ++j) {
526                 visitor.visit(i, j, rowI[j]);
527             }
528         }
529         return visitor.end();
530     }
531 
532     /** {@inheritDoc} */
533     @Override
534     public double walkInColumnOrder(final RealMatrixChangingVisitor visitor)
535         throws MatrixVisitorException {
536         final int rows    = getRowDimension();
537         final int columns = getColumnDimension();
538         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
539         for (int j = 0; j < columns; ++j) {
540             for (int i = 0; i < rows; ++i) {
541                 final double[] rowI = data[i];
542                 rowI[j] = visitor.visit(i, j, rowI[j]);
543             }
544         }
545         return visitor.end();
546     }
547 
548     /** {@inheritDoc} */
549     @Override
550     public double walkInColumnOrder(final RealMatrixPreservingVisitor visitor)
551         throws MatrixVisitorException {
552         final int rows    = getRowDimension();
553         final int columns = getColumnDimension();
554         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
555         for (int j = 0; j < columns; ++j) {
556             for (int i = 0; i < rows; ++i) {
557                 visitor.visit(i, j, data[i][j]);
558             }
559         }
560         return visitor.end();
561     }
562 
563     /** {@inheritDoc} */
564     @Override
565     public double walkInColumnOrder(final RealMatrixChangingVisitor visitor,
566                                     final int startRow, final int endRow,
567                                     final int startColumn, final int endColumn)
568         throws MatrixIndexException, MatrixVisitorException {
569         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
570         visitor.start(getRowDimension(), getColumnDimension(),
571                       startRow, endRow, startColumn, endColumn);
572         for (int j = startColumn; j <= endColumn; ++j) {
573             for (int i = startRow; i <= endRow; ++i) {
574                 final double[] rowI = data[i];
575                 rowI[j] = visitor.visit(i, j, rowI[j]);
576             }
577         }
578         return visitor.end();
579     }
580 
581     /** {@inheritDoc} */
582     @Override
583     public double walkInColumnOrder(final RealMatrixPreservingVisitor visitor,
584                                     final int startRow, final int endRow,
585                                     final int startColumn, final int endColumn)
586         throws MatrixIndexException, MatrixVisitorException {
587         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
588         visitor.start(getRowDimension(), getColumnDimension(),
589                       startRow, endRow, startColumn, endColumn);
590         for (int j = startColumn; j <= endColumn; ++j) {
591             for (int i = startRow; i <= endRow; ++i) {
592                 visitor.visit(i, j, data[i][j]);
593             }
594         }
595         return visitor.end();
596     }
597 
598     /**
599      * Returns a fresh copy of the underlying data array.
600      *
601      * @return a copy of the underlying data array.
602      */
603     private double[][] copyOut() {
604         final int nRows = this.getRowDimension();
605         final double[][] out = new double[nRows][this.getColumnDimension()];
606         // can't copy 2-d array in one shot, otherwise get row references
607         for (int i = 0; i < nRows; i++) {
608             System.arraycopy(data[i], 0, out[i], 0, data[i].length);
609         }
610         return out;
611     }
612 
613     /**
614      * Replaces data with a fresh copy of the input array.
615      * <p>
616      * Verifies that the input array is rectangular and non-empty.</p>
617      *
618      * @param in data to copy in
619      * @throws IllegalArgumentException if input array is empty or not
620      *    rectangular
621      * @throws NullPointerException if input array is null
622      */
623     private void copyIn(final double[][] in) {
624         setSubMatrix(in, 0, 0);
625     }
626 
627 }