Home | History | Annotate | Download | only in linear
      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 import org.apache.commons.math.linear.MatrixVisitorException;
     24 import org.apache.commons.math.exception.util.LocalizedFormats;
     25 
     26 /**
     27  * Implementation of RealMatrix using a double[][] array to store entries and
     28  * <a href="http://www.math.gatech.edu/~bourbaki/math2601/Web-notes/2num.pdf">
     29  * LU decomposition</a> to support linear system
     30  * solution and inverse.
     31  * <p>
     32  * The LU decomposition is performed as needed, to support the following operations: <ul>
     33  * <li>solve</li>
     34  * <li>isSingular</li>
     35  * <li>getDeterminant</li>
     36  * <li>inverse</li> </ul></p>
     37  * <p>
     38  * <strong>Usage notes</strong>:<br>
     39  * <ul><li>
     40  * The LU decomposition is cached and reused on subsequent calls.
     41  * If data are modified via references to the underlying array obtained using
     42  * <code>getDataRef()</code>, then the stored LU decomposition will not be
     43  * discarded.  In this case, you need to explicitly invoke
     44  * <code>LUDecompose()</code> to recompute the decomposition
     45  * before using any of the methods above.</li>
     46  * <li>
     47  * As specified in the {@link RealMatrix} interface, matrix element indexing
     48  * is 0-based -- e.g., <code>getEntry(0, 0)</code>
     49  * returns the element in the first row, first column of the matrix.</li></ul>
     50  * </p>
     51  *
     52  * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 fvr. 2011) $
     53  */
     54 public class Array2DRowRealMatrix 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 Array2DRowRealMatrix() {
     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 Array2DRowRealMatrix(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 #Array2DRowRealMatrix(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 #Array2DRowRealMatrix(double[][], boolean)
     94      */
     95     public Array2DRowRealMatrix(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 #Array2DRowRealMatrix(double[][])
    114      */
    115     public Array2DRowRealMatrix(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(
    126                       LocalizedFormats.AT_LEAST_ONE_ROW);
    127             }
    128             final int nCols = d[0].length;
    129             if (nCols == 0) {
    130                 throw MathRuntimeException.createIllegalArgumentException(
    131                       LocalizedFormats.AT_LEAST_ONE_COLUMN);
    132             }
    133             for (int r = 1; r < nRows; r++) {
    134                 if (d[r].length != nCols) {
    135                     throw MathRuntimeException.createIllegalArgumentException(
    136                           LocalizedFormats.DIFFERENT_ROWS_LENGTHS, nCols, d[r].length);
    137                 }
    138             }
    139             data = d;
    140         }
    141     }
    142 
    143     /**
    144      * Create a new (column) RealMatrix using <code>v</code> as the
    145      * data for the unique column of the <code>v.length x 1</code> matrix
    146      * created.
    147      * <p>The input array is copied, not referenced.</p>
    148      *
    149      * @param v column vector holding data for new matrix
    150      */
    151     public Array2DRowRealMatrix(final double[] v) {
    152         final int nRows = v.length;
    153         data = new double[nRows][1];
    154         for (int row = 0; row < nRows; row++) {
    155             data[row][0] = v[row];
    156         }
    157     }
    158 
    159     /** {@inheritDoc} */
    160     @Override
    161     public RealMatrix createMatrix(final int rowDimension, final int columnDimension)
    162         throws IllegalArgumentException {
    163         return new Array2DRowRealMatrix(rowDimension, columnDimension);
    164     }
    165 
    166     /** {@inheritDoc} */
    167     @Override
    168     public RealMatrix copy() {
    169         return new Array2DRowRealMatrix(copyOut(), false);
    170     }
    171 
    172     /** {@inheritDoc} */
    173     @Override
    174     public RealMatrix add(final RealMatrix m)
    175         throws IllegalArgumentException {
    176         try {
    177             return add((Array2DRowRealMatrix) m);
    178         } catch (ClassCastException cce) {
    179             return super.add(m);
    180         }
    181     }
    182 
    183     /**
    184      * Compute the sum of this and <code>m</code>.
    185      *
    186      * @param m    matrix to be added
    187      * @return     this + m
    188      * @throws  IllegalArgumentException if m is not the same size as this
    189      */
    190     public Array2DRowRealMatrix add(final Array2DRowRealMatrix m)
    191         throws IllegalArgumentException {
    192 
    193         // safety check
    194         MatrixUtils.checkAdditionCompatible(this, m);
    195 
    196         final int rowCount    = getRowDimension();
    197         final int columnCount = getColumnDimension();
    198         final double[][] outData = new double[rowCount][columnCount];
    199         for (int row = 0; row < rowCount; row++) {
    200             final double[] dataRow    = data[row];
    201             final double[] mRow       = m.data[row];
    202             final double[] outDataRow = outData[row];
    203             for (int col = 0; col < columnCount; col++) {
    204                 outDataRow[col] = dataRow[col] + mRow[col];
    205             }
    206         }
    207 
    208         return new Array2DRowRealMatrix(outData, false);
    209 
    210     }
    211 
    212     /** {@inheritDoc} */
    213     @Override
    214     public RealMatrix subtract(final RealMatrix m)
    215         throws IllegalArgumentException {
    216         try {
    217             return subtract((Array2DRowRealMatrix) m);
    218         } catch (ClassCastException cce) {
    219             return super.subtract(m);
    220         }
    221     }
    222 
    223     /**
    224      * Compute  this minus <code>m</code>.
    225      *
    226      * @param m    matrix to be subtracted
    227      * @return     this + m
    228      * @throws  IllegalArgumentException if m is not the same size as this
    229      */
    230     public Array2DRowRealMatrix subtract(final Array2DRowRealMatrix m)
    231         throws IllegalArgumentException {
    232 
    233         // safety check
    234         MatrixUtils.checkSubtractionCompatible(this, m);
    235 
    236         final int rowCount    = getRowDimension();
    237         final int columnCount = getColumnDimension();
    238         final double[][] outData = new double[rowCount][columnCount];
    239         for (int row = 0; row < rowCount; row++) {
    240             final double[] dataRow    = data[row];
    241             final double[] mRow       = m.data[row];
    242             final double[] outDataRow = outData[row];
    243             for (int col = 0; col < columnCount; col++) {
    244                 outDataRow[col] = dataRow[col] - mRow[col];
    245             }
    246         }
    247 
    248         return new Array2DRowRealMatrix(outData, false);
    249 
    250     }
    251 
    252     /** {@inheritDoc} */
    253     @Override
    254     public RealMatrix multiply(final RealMatrix m)
    255         throws IllegalArgumentException {
    256         try {
    257             return multiply((Array2DRowRealMatrix) m);
    258         } catch (ClassCastException cce) {
    259             return super.multiply(m);
    260         }
    261     }
    262 
    263     /**
    264      * Returns the result of postmultiplying this by <code>m</code>.
    265      * @param m    matrix to postmultiply by
    266      * @return     this*m
    267      * @throws     IllegalArgumentException
    268      *             if columnDimension(this) != rowDimension(m)
    269      */
    270     public Array2DRowRealMatrix multiply(final Array2DRowRealMatrix m)
    271         throws IllegalArgumentException {
    272 
    273         // safety check
    274         MatrixUtils.checkMultiplicationCompatible(this, m);
    275 
    276         final int nRows = this.getRowDimension();
    277         final int nCols = m.getColumnDimension();
    278         final int nSum = this.getColumnDimension();
    279         final double[][] outData = new double[nRows][nCols];
    280         for (int row = 0; row < nRows; row++) {
    281             final double[] dataRow    = data[row];
    282             final double[] outDataRow = outData[row];
    283             for (int col = 0; col < nCols; col++) {
    284                 double sum = 0;
    285                 for (int i = 0; i < nSum; i++) {
    286                     sum += dataRow[i] * m.data[i][col];
    287                 }
    288                 outDataRow[col] = sum;
    289             }
    290         }
    291 
    292         return new Array2DRowRealMatrix(outData, false);
    293 
    294     }
    295 
    296     /** {@inheritDoc} */
    297     @Override
    298     public double[][] getData() {
    299         return copyOut();
    300     }
    301 
    302     /**
    303      * Returns a reference to the underlying data array.
    304      * <p>
    305      * Does <strong>not</strong> make a fresh copy of the underlying data.</p>
    306      *
    307      * @return 2-dimensional array of entries
    308      */
    309     public double[][] getDataRef() {
    310         return data;
    311     }
    312 
    313     /** {@inheritDoc} */
    314     @Override
    315     public void setSubMatrix(final double[][] subMatrix, final int row, final int column)
    316     throws MatrixIndexException {
    317         if (data == null) {
    318             if (row > 0) {
    319                 throw MathRuntimeException.createIllegalStateException(
    320                       LocalizedFormats.FIRST_ROWS_NOT_INITIALIZED_YET, row);
    321             }
    322             if (column > 0) {
    323                 throw MathRuntimeException.createIllegalStateException(
    324                       LocalizedFormats.FIRST_COLUMNS_NOT_INITIALIZED_YET, column);
    325             }
    326             final int nRows = subMatrix.length;
    327             if (nRows == 0) {
    328                 throw MathRuntimeException.createIllegalArgumentException(
    329                       LocalizedFormats.AT_LEAST_ONE_ROW);
    330             }
    331 
    332             final int nCols = subMatrix[0].length;
    333             if (nCols == 0) {
    334                 throw MathRuntimeException.createIllegalArgumentException(
    335                       LocalizedFormats.AT_LEAST_ONE_COLUMN);
    336             }
    337             data = new double[subMatrix.length][nCols];
    338             for (int i = 0; i < data.length; ++i) {
    339                 if (subMatrix[i].length != nCols) {
    340                     throw MathRuntimeException.createIllegalArgumentException(
    341                           LocalizedFormats.DIFFERENT_ROWS_LENGTHS, 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                       LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension());
    360         }
    361     }
    362 
    363     /** {@inheritDoc} */
    364     @Override
    365     public void setEntry(final int row, final int column, final double value)
    366         throws MatrixIndexException {
    367         try {
    368             data[row][column] = value;
    369         } catch (ArrayIndexOutOfBoundsException e) {
    370             throw new MatrixIndexException(
    371                       LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension());
    372         }
    373     }
    374 
    375     /** {@inheritDoc} */
    376     @Override
    377     public void addToEntry(final int row, final int column, final double increment)
    378         throws MatrixIndexException {
    379         try {
    380             data[row][column] += increment;
    381         } catch (ArrayIndexOutOfBoundsException e) {
    382             throw new MatrixIndexException(
    383                       LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension());
    384         }
    385     }
    386 
    387     /** {@inheritDoc} */
    388     @Override
    389     public void multiplyEntry(final int row, final int column, final double factor)
    390         throws MatrixIndexException {
    391         try {
    392             data[row][column] *= factor;
    393         } catch (ArrayIndexOutOfBoundsException e) {
    394             throw new MatrixIndexException(
    395                       LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension());
    396         }
    397     }
    398 
    399     /** {@inheritDoc} */
    400     @Override
    401     public int getRowDimension() {
    402         return (data == null) ? 0 : data.length;
    403     }
    404 
    405     /** {@inheritDoc} */
    406     @Override
    407     public int getColumnDimension() {
    408         return ((data == null) || (data[0] == null)) ? 0 : data[0].length;
    409     }
    410 
    411     /** {@inheritDoc} */
    412     @Override
    413     public double[] operate(final double[] v)
    414         throws IllegalArgumentException {
    415         final int nRows = this.getRowDimension();
    416         final int nCols = this.getColumnDimension();
    417         if (v.length != nCols) {
    418             throw MathRuntimeException.createIllegalArgumentException(
    419                   LocalizedFormats.VECTOR_LENGTH_MISMATCH, v.length, nCols);
    420         }
    421         final double[] out = new double[nRows];
    422         for (int row = 0; row < nRows; row++) {
    423             final double[] dataRow = data[row];
    424             double sum = 0;
    425             for (int i = 0; i < nCols; i++) {
    426                 sum += dataRow[i] * v[i];
    427             }
    428             out[row] = sum;
    429         }
    430         return out;
    431     }
    432 
    433     /** {@inheritDoc} */
    434     @Override
    435     public double[] preMultiply(final double[] v)
    436         throws IllegalArgumentException {
    437 
    438         final int nRows = getRowDimension();
    439         final int nCols = getColumnDimension();
    440         if (v.length != nRows) {
    441             throw MathRuntimeException.createIllegalArgumentException(
    442                   LocalizedFormats.VECTOR_LENGTH_MISMATCH, v.length, nRows);
    443         }
    444 
    445         final double[] out = new double[nCols];
    446         for (int col = 0; col < nCols; ++col) {
    447             double sum = 0;
    448             for (int i = 0; i < nRows; ++i) {
    449                 sum += data[i][col] * v[i];
    450             }
    451             out[col] = sum;
    452         }
    453 
    454         return out;
    455 
    456     }
    457 
    458     /** {@inheritDoc} */
    459     @Override
    460     public double walkInRowOrder(final RealMatrixChangingVisitor visitor)
    461         throws MatrixVisitorException {
    462         final int rows    = getRowDimension();
    463         final int columns = getColumnDimension();
    464         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
    465         for (int i = 0; i < rows; ++i) {
    466             final double[] rowI = data[i];
    467             for (int j = 0; j < columns; ++j) {
    468                 rowI[j] = visitor.visit(i, j, rowI[j]);
    469             }
    470         }
    471         return visitor.end();
    472     }
    473 
    474     /** {@inheritDoc} */
    475     @Override
    476     public double walkInRowOrder(final RealMatrixPreservingVisitor visitor)
    477         throws MatrixVisitorException {
    478         final int rows    = getRowDimension();
    479         final int columns = getColumnDimension();
    480         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
    481         for (int i = 0; i < rows; ++i) {
    482             final double[] rowI = data[i];
    483             for (int j = 0; j < columns; ++j) {
    484                 visitor.visit(i, j, rowI[j]);
    485             }
    486         }
    487         return visitor.end();
    488     }
    489 
    490     /** {@inheritDoc} */
    491     @Override
    492     public double walkInRowOrder(final RealMatrixChangingVisitor visitor,
    493                                  final int startRow, final int endRow,
    494                                  final int startColumn, final int endColumn)
    495         throws MatrixIndexException, MatrixVisitorException {
    496         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
    497         visitor.start(getRowDimension(), getColumnDimension(),
    498                       startRow, endRow, startColumn, endColumn);
    499         for (int i = startRow; i <= endRow; ++i) {
    500             final double[] rowI = data[i];
    501             for (int j = startColumn; j <= endColumn; ++j) {
    502                 rowI[j] = visitor.visit(i, j, rowI[j]);
    503             }
    504         }
    505         return visitor.end();
    506     }
    507 
    508     /** {@inheritDoc} */
    509     @Override
    510     public double walkInRowOrder(final RealMatrixPreservingVisitor visitor,
    511                                  final int startRow, final int endRow,
    512                                  final int startColumn, final int endColumn)
    513         throws MatrixIndexException, MatrixVisitorException {
    514         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
    515         visitor.start(getRowDimension(), getColumnDimension(),
    516                       startRow, endRow, startColumn, endColumn);
    517         for (int i = startRow; i <= endRow; ++i) {
    518             final double[] rowI = data[i];
    519             for (int j = startColumn; j <= endColumn; ++j) {
    520                 visitor.visit(i, j, rowI[j]);
    521             }
    522         }
    523         return visitor.end();
    524     }
    525 
    526     /** {@inheritDoc} */
    527     @Override
    528     public double walkInColumnOrder(final RealMatrixChangingVisitor visitor)
    529         throws MatrixVisitorException {
    530         final int rows    = getRowDimension();
    531         final int columns = getColumnDimension();
    532         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
    533         for (int j = 0; j < columns; ++j) {
    534             for (int i = 0; i < rows; ++i) {
    535                 final double[] rowI = data[i];
    536                 rowI[j] = visitor.visit(i, j, rowI[j]);
    537             }
    538         }
    539         return visitor.end();
    540     }
    541 
    542     /** {@inheritDoc} */
    543     @Override
    544     public double walkInColumnOrder(final RealMatrixPreservingVisitor visitor)
    545         throws MatrixVisitorException {
    546         final int rows    = getRowDimension();
    547         final int columns = getColumnDimension();
    548         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
    549         for (int j = 0; j < columns; ++j) {
    550             for (int i = 0; i < rows; ++i) {
    551                 visitor.visit(i, j, data[i][j]);
    552             }
    553         }
    554         return visitor.end();
    555     }
    556 
    557     /** {@inheritDoc} */
    558     @Override
    559     public double walkInColumnOrder(final RealMatrixChangingVisitor visitor,
    560                                     final int startRow, final int endRow,
    561                                     final int startColumn, final int endColumn)
    562         throws MatrixIndexException, MatrixVisitorException {
    563         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
    564         visitor.start(getRowDimension(), getColumnDimension(),
    565                       startRow, endRow, startColumn, endColumn);
    566         for (int j = startColumn; j <= endColumn; ++j) {
    567             for (int i = startRow; i <= endRow; ++i) {
    568                 final double[] rowI = data[i];
    569                 rowI[j] = visitor.visit(i, j, rowI[j]);
    570             }
    571         }
    572         return visitor.end();
    573     }
    574 
    575     /** {@inheritDoc} */
    576     @Override
    577     public double walkInColumnOrder(final RealMatrixPreservingVisitor visitor,
    578                                     final int startRow, final int endRow,
    579                                     final int startColumn, final int endColumn)
    580         throws MatrixIndexException, MatrixVisitorException {
    581         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
    582         visitor.start(getRowDimension(), getColumnDimension(),
    583                       startRow, endRow, startColumn, endColumn);
    584         for (int j = startColumn; j <= endColumn; ++j) {
    585             for (int i = startRow; i <= endRow; ++i) {
    586                 visitor.visit(i, j, data[i][j]);
    587             }
    588         }
    589         return visitor.end();
    590     }
    591 
    592     /**
    593      * Returns a fresh copy of the underlying data array.
    594      *
    595      * @return a copy of the underlying data array.
    596      */
    597     private double[][] copyOut() {
    598         final int nRows = this.getRowDimension();
    599         final double[][] out = new double[nRows][this.getColumnDimension()];
    600         // can't copy 2-d array in one shot, otherwise get row references
    601         for (int i = 0; i < nRows; i++) {
    602             System.arraycopy(data[i], 0, out[i], 0, data[i].length);
    603         }
    604         return out;
    605     }
    606 
    607     /**
    608      * Replaces data with a fresh copy of the input array.
    609      * <p>
    610      * Verifies that the input array is rectangular and non-empty.</p>
    611      *
    612      * @param in data to copy in
    613      * @throws IllegalArgumentException if input array is empty or not
    614      *    rectangular
    615      * @throws NullPointerException if input array is null
    616      */
    617     private void copyIn(final double[][] in) {
    618         setSubMatrix(in, 0, 0);
    619     }
    620 
    621 }
    622