/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.math.linear; import java.io.Serializable; import java.math.BigDecimal; import org.apache.commons.math.MathRuntimeException; import org.apache.commons.math.exception.util.LocalizedFormats; /** * Implementation of {@link BigMatrix} using a BigDecimal[][] array to store entries * and <a href="http://www.math.gatech.edu/~bourbaki/math2601/Web-notes/2num.pdf"> * LU decompostion</a> to support linear system * solution and inverse. * <p> * The LU decompostion is performed as needed, to support the following operations: <ul> * <li>solve</li> * <li>isSingular</li> * <li>getDeterminant</li> * <li>inverse</li> </ul></p> * <p> * <strong>Usage notes</strong>:<br> * <ul><li> * The LU decomposition is stored and reused on subsequent calls. If matrix * data are modified using any of the public setXxx methods, the saved * decomposition is discarded. If data are modified via references to the * underlying array obtained using <code>getDataRef()</code>, then the stored * LU decomposition will not be discarded. In this case, you need to * explicitly invoke <code>LUDecompose()</code> to recompute the decomposition * before using any of the methods above.</li> * <li> * As specified in the {@link BigMatrix} interface, matrix element indexing * is 0-based -- e.g., <code>getEntry(0, 0)</code> * returns the element in the first row, first column of the matrix.</li></ul></p> * * @deprecated as of 2.0, replaced by {@link Array2DRowFieldMatrix} with a {@link * org.apache.commons.math.util.BigReal} parameter * @version $Revision: 1042376 $ $Date: 2010-12-05 16:54:55 +0100 (dim. 05 déc. 2010) $ */ @Deprecated public class BigMatrixImpl implements BigMatrix, Serializable { /** BigDecimal 0 */ static final BigDecimal ZERO = new BigDecimal(0); /** BigDecimal 1 */ static final BigDecimal ONE = new BigDecimal(1); /** Bound to determine effective singularity in LU decomposition */ private static final BigDecimal TOO_SMALL = new BigDecimal(10E-12); /** Serialization id */ private static final long serialVersionUID = -1011428905656140431L; /** Entries of the matrix */ protected BigDecimal data[][] = null; /** Entries of cached LU decomposition. * All updates to data (other than luDecompose()) *must* set this to null */ protected BigDecimal lu[][] = null; /** Permutation associated with LU decomposition */ protected int[] permutation = null; /** Parity of the permutation associated with the LU decomposition */ protected int parity = 1; /** Rounding mode for divisions **/ private int roundingMode = BigDecimal.ROUND_HALF_UP; /*** BigDecimal scale ***/ private int scale = 64; /** * Creates a matrix with no data */ public BigMatrixImpl() { } /** * Create a new BigMatrix with the supplied row and column dimensions. * * @param rowDimension the number of rows in the new matrix * @param columnDimension the number of columns in the new matrix * @throws IllegalArgumentException if row or column dimension is not * positive */ public BigMatrixImpl(int rowDimension, int columnDimension) { if (rowDimension < 1 ) { throw MathRuntimeException.createIllegalArgumentException( LocalizedFormats.INSUFFICIENT_DIMENSION, rowDimension, 1); } if (columnDimension < 1) { throw MathRuntimeException.createIllegalArgumentException( LocalizedFormats.INSUFFICIENT_DIMENSION, columnDimension, 1); } data = new BigDecimal[rowDimension][columnDimension]; lu = null; } /** * Create a new BigMatrix using <code>d</code> as the underlying * data array. * <p>The input array is copied, not referenced. This constructor has * the same effect as calling {@link #BigMatrixImpl(BigDecimal[][], boolean)} * with the second argument set to <code>true</code>.</p> * * @param d data for new matrix * @throws IllegalArgumentException if <code>d</code> is not rectangular * (not all rows have the same length) or empty * @throws NullPointerException if <code>d</code> is null */ public BigMatrixImpl(BigDecimal[][] d) { this.copyIn(d); lu = null; } /** * Create a new BigMatrix using the input array as the underlying * data array. * <p>If an array is built specially in order to be embedded in a * BigMatrix and not used directly, the <code>copyArray</code> may be * set to <code>false</code. This will prevent the copying and improve * performance as no new array will be built and no data will be copied.</p> * @param d data for new matrix * @param copyArray if true, the input array will be copied, otherwise * it will be referenced * @throws IllegalArgumentException if <code>d</code> is not rectangular * (not all rows have the same length) or empty * @throws NullPointerException if <code>d</code> is null * @see #BigMatrixImpl(BigDecimal[][]) */ public BigMatrixImpl(BigDecimal[][] d, boolean copyArray) { if (copyArray) { copyIn(d); } else { if (d == null) { throw new NullPointerException(); } final int nRows = d.length; if (nRows == 0) { throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW); } final int nCols = d[0].length; if (nCols == 0) { throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN); } for (int r = 1; r < nRows; r++) { if (d[r].length != nCols) { throw MathRuntimeException.createIllegalArgumentException( LocalizedFormats.DIFFERENT_ROWS_LENGTHS, nCols, d[r].length); } } data = d; } lu = null; } /** * Create a new BigMatrix using <code>d</code> as the underlying * data array. * <p>Since the underlying array will hold <code>BigDecimal</code> * instances, it will be created.</p> * * @param d data for new matrix * @throws IllegalArgumentException if <code>d</code> is not rectangular * (not all rows have the same length) or empty * @throws NullPointerException if <code>d</code> is null */ public BigMatrixImpl(double[][] d) { final int nRows = d.length; if (nRows == 0) { throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW); } final int nCols = d[0].length; if (nCols == 0) { throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN); } for (int row = 1; row < nRows; row++) { if (d[row].length != nCols) { throw MathRuntimeException.createIllegalArgumentException( LocalizedFormats.DIFFERENT_ROWS_LENGTHS, nCols, d[row].length); } } this.copyIn(d); lu = null; } /** * Create a new BigMatrix using the values represented by the strings in * <code>d</code> as the underlying data array. * * @param d data for new matrix * @throws IllegalArgumentException if <code>d</code> is not rectangular * (not all rows have the same length) or empty * @throws NullPointerException if <code>d</code> is null */ public BigMatrixImpl(String[][] d) { final int nRows = d.length; if (nRows == 0) { throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW); } final int nCols = d[0].length; if (nCols == 0) { throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN); } for (int row = 1; row < nRows; row++) { if (d[row].length != nCols) { throw MathRuntimeException.createIllegalArgumentException( LocalizedFormats.DIFFERENT_ROWS_LENGTHS, nCols, d[row].length); } } this.copyIn(d); lu = null; } /** * Create a new (column) BigMatrix using <code>v</code> as the * data for the unique column of the <code>v.length x 1</code> matrix * created. * <p> * The input array is copied, not referenced.</p> * * @param v column vector holding data for new matrix */ public BigMatrixImpl(BigDecimal[] v) { final int nRows = v.length; data = new BigDecimal[nRows][1]; for (int row = 0; row < nRows; row++) { data[row][0] = v[row]; } } /** * Create a new BigMatrix which is a copy of this. * * @return the cloned matrix */ public BigMatrix copy() { return new BigMatrixImpl(this.copyOut(), false); } /** * Compute the sum of this and <code>m</code>. * * @param m matrix to be added * @return this + m * @throws IllegalArgumentException if m is not the same size as this */ public BigMatrix add(BigMatrix m) throws IllegalArgumentException { try { return add((BigMatrixImpl) m); } catch (ClassCastException cce) { // safety check MatrixUtils.checkAdditionCompatible(this, m); final int rowCount = getRowDimension(); final int columnCount = getColumnDimension(); final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; for (int row = 0; row < rowCount; row++) { final BigDecimal[] dataRow = data[row]; final BigDecimal[] outDataRow = outData[row]; for (int col = 0; col < columnCount; col++) { outDataRow[col] = dataRow[col].add(m.getEntry(row, col)); } } return new BigMatrixImpl(outData, false); } } /** * Compute the sum of this and <code>m</code>. * * @param m matrix to be added * @return this + m * @throws IllegalArgumentException if m is not the same size as this */ public BigMatrixImpl add(BigMatrixImpl m) throws IllegalArgumentException { // safety check MatrixUtils.checkAdditionCompatible(this, m); final int rowCount = getRowDimension(); final int columnCount = getColumnDimension(); final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; for (int row = 0; row < rowCount; row++) { final BigDecimal[] dataRow = data[row]; final BigDecimal[] mRow = m.data[row]; final BigDecimal[] outDataRow = outData[row]; for (int col = 0; col < columnCount; col++) { outDataRow[col] = dataRow[col].add(mRow[col]); } } return new BigMatrixImpl(outData, false); } /** * Compute this minus <code>m</code>. * * @param m matrix to be subtracted * @return this + m * @throws IllegalArgumentException if m is not the same size as this */ public BigMatrix subtract(BigMatrix m) throws IllegalArgumentException { try { return subtract((BigMatrixImpl) m); } catch (ClassCastException cce) { // safety check MatrixUtils.checkSubtractionCompatible(this, m); final int rowCount = getRowDimension(); final int columnCount = getColumnDimension(); final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; for (int row = 0; row < rowCount; row++) { final BigDecimal[] dataRow = data[row]; final BigDecimal[] outDataRow = outData[row]; for (int col = 0; col < columnCount; col++) { outDataRow[col] = dataRow[col].subtract(getEntry(row, col)); } } return new BigMatrixImpl(outData, false); } } /** * Compute this minus <code>m</code>. * * @param m matrix to be subtracted * @return this + m * @throws IllegalArgumentException if m is not the same size as this */ public BigMatrixImpl subtract(BigMatrixImpl m) throws IllegalArgumentException { // safety check MatrixUtils.checkSubtractionCompatible(this, m); final int rowCount = getRowDimension(); final int columnCount = getColumnDimension(); final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; for (int row = 0; row < rowCount; row++) { final BigDecimal[] dataRow = data[row]; final BigDecimal[] mRow = m.data[row]; final BigDecimal[] outDataRow = outData[row]; for (int col = 0; col < columnCount; col++) { outDataRow[col] = dataRow[col].subtract(mRow[col]); } } return new BigMatrixImpl(outData, false); } /** * Returns the result of adding d to each entry of this. * * @param d value to be added to each entry * @return d + this */ public BigMatrix scalarAdd(BigDecimal d) { final int rowCount = getRowDimension(); final int columnCount = getColumnDimension(); final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; for (int row = 0; row < rowCount; row++) { final BigDecimal[] dataRow = data[row]; final BigDecimal[] outDataRow = outData[row]; for (int col = 0; col < columnCount; col++) { outDataRow[col] = dataRow[col].add(d); } } return new BigMatrixImpl(outData, false); } /** * Returns the result of multiplying each entry of this by <code>d</code> * @param d value to multiply all entries by * @return d * this */ public BigMatrix scalarMultiply(BigDecimal d) { final int rowCount = getRowDimension(); final int columnCount = getColumnDimension(); final BigDecimal[][] outData = new BigDecimal[rowCount][columnCount]; for (int row = 0; row < rowCount; row++) { final BigDecimal[] dataRow = data[row]; final BigDecimal[] outDataRow = outData[row]; for (int col = 0; col < columnCount; col++) { outDataRow[col] = dataRow[col].multiply(d); } } return new BigMatrixImpl(outData, false); } /** * Returns the result of postmultiplying this by <code>m</code>. * @param m matrix to postmultiply by * @return this*m * @throws IllegalArgumentException * if columnDimension(this) != rowDimension(m) */ public BigMatrix multiply(BigMatrix m) throws IllegalArgumentException { try { return multiply((BigMatrixImpl) m); } catch (ClassCastException cce) { // safety check MatrixUtils.checkMultiplicationCompatible(this, m); final int nRows = this.getRowDimension(); final int nCols = m.getColumnDimension(); final int nSum = this.getColumnDimension(); final BigDecimal[][] outData = new BigDecimal[nRows][nCols]; for (int row = 0; row < nRows; row++) { final BigDecimal[] dataRow = data[row]; final BigDecimal[] outDataRow = outData[row]; for (int col = 0; col < nCols; col++) { BigDecimal sum = ZERO; for (int i = 0; i < nSum; i++) { sum = sum.add(dataRow[i].multiply(m.getEntry(i, col))); } outDataRow[col] = sum; } } return new BigMatrixImpl(outData, false); } } /** * Returns the result of postmultiplying this by <code>m</code>. * @param m matrix to postmultiply by * @return this*m * @throws IllegalArgumentException * if columnDimension(this) != rowDimension(m) */ public BigMatrixImpl multiply(BigMatrixImpl m) throws IllegalArgumentException { // safety check MatrixUtils.checkMultiplicationCompatible(this, m); final int nRows = this.getRowDimension(); final int nCols = m.getColumnDimension(); final int nSum = this.getColumnDimension(); final BigDecimal[][] outData = new BigDecimal[nRows][nCols]; for (int row = 0; row < nRows; row++) { final BigDecimal[] dataRow = data[row]; final BigDecimal[] outDataRow = outData[row]; for (int col = 0; col < nCols; col++) { BigDecimal sum = ZERO; for (int i = 0; i < nSum; i++) { sum = sum.add(dataRow[i].multiply(m.data[i][col])); } outDataRow[col] = sum; } } return new BigMatrixImpl(outData, false); } /** * Returns the result premultiplying this by <code>m</code>. * @param m matrix to premultiply by * @return m * this * @throws IllegalArgumentException * if rowDimension(this) != columnDimension(m) */ public BigMatrix preMultiply(BigMatrix m) throws IllegalArgumentException { return m.multiply(this); } /** * Returns matrix entries as a two-dimensional array. * <p> * Makes a fresh copy of the underlying data.</p> * * @return 2-dimensional array of entries */ public BigDecimal[][] getData() { return copyOut(); } /** * Returns matrix entries as a two-dimensional array. * <p> * Makes a fresh copy of the underlying data converted to * <code>double</code> values.</p> * * @return 2-dimensional array of entries */ public double[][] getDataAsDoubleArray() { final int nRows = getRowDimension(); final int nCols = getColumnDimension(); final double d[][] = new double[nRows][nCols]; for (int i = 0; i < nRows; i++) { for (int j = 0; j < nCols; j++) { d[i][j] = data[i][j].doubleValue(); } } return d; } /** * Returns a reference to the underlying data array. * <p> * Does not make a fresh copy of the underlying data.</p> * * @return 2-dimensional array of entries */ public BigDecimal[][] getDataRef() { return data; } /*** * Gets the rounding mode for division operations * The default is {@link java.math.BigDecimal#ROUND_HALF_UP} * @see BigDecimal * @return the rounding mode. */ public int getRoundingMode() { return roundingMode; } /*** * Sets the rounding mode for decimal divisions. * @see BigDecimal * @param roundingMode rounding mode for decimal divisions */ public void setRoundingMode(int roundingMode) { this.roundingMode = roundingMode; } /*** * Sets the scale for division operations. * The default is 64 * @see BigDecimal * @return the scale */ public int getScale() { return scale; } /*** * Sets the scale for division operations. * @see BigDecimal * @param scale scale for division operations */ public void setScale(int scale) { this.scale = scale; } /** * Returns the <a href="http://mathworld.wolfram.com/MaximumAbsoluteRowSumNorm.html"> * maximum absolute row sum norm</a> of the matrix. * * @return norm */ public BigDecimal getNorm() { BigDecimal maxColSum = ZERO; for (int col = 0; col < this.getColumnDimension(); col++) { BigDecimal sum = ZERO; for (int row = 0; row < this.getRowDimension(); row++) { sum = sum.add(data[row][col].abs()); } maxColSum = maxColSum.max(sum); } return maxColSum; } /** * Gets a submatrix. Rows and columns are indicated * counting from 0 to n-1. * * @param startRow Initial row index * @param endRow Final row index * @param startColumn Initial column index * @param endColumn Final column index * @return The subMatrix containing the data of the * specified rows and columns * @exception MatrixIndexException if row or column selections are not valid */ public BigMatrix getSubMatrix(int startRow, int endRow, int startColumn, int endColumn) throws MatrixIndexException { MatrixUtils.checkRowIndex(this, startRow); MatrixUtils.checkRowIndex(this, endRow); if (startRow > endRow) { throw new MatrixIndexException(LocalizedFormats.INITIAL_ROW_AFTER_FINAL_ROW, startRow, endRow); } MatrixUtils.checkColumnIndex(this, startColumn); MatrixUtils.checkColumnIndex(this, endColumn); if (startColumn > endColumn) { throw new MatrixIndexException(LocalizedFormats.INITIAL_COLUMN_AFTER_FINAL_COLUMN, startColumn, endColumn); } final BigDecimal[][] subMatrixData = new BigDecimal[endRow - startRow + 1][endColumn - startColumn + 1]; for (int i = startRow; i <= endRow; i++) { System.arraycopy(data[i], startColumn, subMatrixData[i - startRow], 0, endColumn - startColumn + 1); } return new BigMatrixImpl(subMatrixData, false); } /** * Gets a submatrix. Rows and columns are indicated * counting from 0 to n-1. * * @param selectedRows Array of row indices must be non-empty * @param selectedColumns Array of column indices must be non-empty * @return The subMatrix containing the data in the * specified rows and columns * @exception MatrixIndexException if supplied row or column index arrays * are not valid */ public BigMatrix getSubMatrix(int[] selectedRows, int[] selectedColumns) throws MatrixIndexException { if (selectedRows.length * selectedColumns.length == 0) { if (selectedRows.length == 0) { throw new MatrixIndexException(LocalizedFormats.EMPTY_SELECTED_ROW_INDEX_ARRAY); } throw new MatrixIndexException(LocalizedFormats.EMPTY_SELECTED_COLUMN_INDEX_ARRAY); } final BigDecimal[][] subMatrixData = new BigDecimal[selectedRows.length][selectedColumns.length]; try { for (int i = 0; i < selectedRows.length; i++) { final BigDecimal[] subI = subMatrixData[i]; final BigDecimal[] dataSelectedI = data[selectedRows[i]]; for (int j = 0; j < selectedColumns.length; j++) { subI[j] = dataSelectedI[selectedColumns[j]]; } } } catch (ArrayIndexOutOfBoundsException e) { // we redo the loop with checks enabled // in order to generate an appropriate message for (final int row : selectedRows) { MatrixUtils.checkRowIndex(this, row); } for (final int column : selectedColumns) { MatrixUtils.checkColumnIndex(this, column); } } return new BigMatrixImpl(subMatrixData, false); } /** * Replace the submatrix starting at <code>row, column</code> using data in * the input <code>subMatrix</code> array. Indexes are 0-based. * <p> * Example:<br> * Starting with <pre> * 1 2 3 4 * 5 6 7 8 * 9 0 1 2 * </pre> * and <code>subMatrix = {{3, 4} {5,6}}</code>, invoking * <code>setSubMatrix(subMatrix,1,1))</code> will result in <pre> * 1 2 3 4 * 5 3 4 8 * 9 5 6 2 * </pre></p> * * @param subMatrix array containing the submatrix replacement data * @param row row coordinate of the top, left element to be replaced * @param column column coordinate of the top, left element to be replaced * @throws MatrixIndexException if subMatrix does not fit into this * matrix from element in (row, column) * @throws IllegalArgumentException if <code>subMatrix</code> is not rectangular * (not all rows have the same length) or empty * @throws NullPointerException if <code>subMatrix</code> is null * @since 1.1 */ public void setSubMatrix(BigDecimal[][] subMatrix, int row, int column) throws MatrixIndexException { final int nRows = subMatrix.length; if (nRows == 0) { throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_ROW); } final int nCols = subMatrix[0].length; if (nCols == 0) { throw MathRuntimeException.createIllegalArgumentException(LocalizedFormats.AT_LEAST_ONE_COLUMN); } for (int r = 1; r < nRows; r++) { if (subMatrix[r].length != nCols) { throw MathRuntimeException.createIllegalArgumentException( LocalizedFormats.DIFFERENT_ROWS_LENGTHS, nCols, subMatrix[r].length); } } if (data == null) { if (row > 0) { throw MathRuntimeException.createIllegalStateException( LocalizedFormats.FIRST_ROWS_NOT_INITIALIZED_YET, row); } if (column > 0) { throw MathRuntimeException.createIllegalStateException( LocalizedFormats.FIRST_COLUMNS_NOT_INITIALIZED_YET, column); } data = new BigDecimal[nRows][nCols]; System.arraycopy(subMatrix, 0, data, 0, subMatrix.length); } else { MatrixUtils.checkRowIndex(this, row); MatrixUtils.checkColumnIndex(this, column); MatrixUtils.checkRowIndex(this, nRows + row - 1); MatrixUtils.checkColumnIndex(this, nCols + column - 1); } for (int i = 0; i < nRows; i++) { System.arraycopy(subMatrix[i], 0, data[row + i], column, nCols); } lu = null; } /** * Returns the entries in row number <code>row</code> * as a row matrix. Row indices start at 0. * * @param row the row to be fetched * @return row matrix * @throws MatrixIndexException if the specified row index is invalid */ public BigMatrix getRowMatrix(int row) throws MatrixIndexException { MatrixUtils.checkRowIndex(this, row); final int ncols = this.getColumnDimension(); final BigDecimal[][] out = new BigDecimal[1][ncols]; System.arraycopy(data[row], 0, out[0], 0, ncols); return new BigMatrixImpl(out, false); } /** * Returns the entries in column number <code>column</code> * as a column matrix. Column indices start at 0. * * @param column the column to be fetched * @return column matrix * @throws MatrixIndexException if the specified column index is invalid */ public BigMatrix getColumnMatrix(int column) throws MatrixIndexException { MatrixUtils.checkColumnIndex(this, column); final int nRows = this.getRowDimension(); final BigDecimal[][] out = new BigDecimal[nRows][1]; for (int row = 0; row < nRows; row++) { out[row][0] = data[row][column]; } return new BigMatrixImpl(out, false); } /** * Returns the entries in row number <code>row</code> as an array. * <p> * Row indices start at 0. A <code>MatrixIndexException</code> is thrown * unless <code>0 <= row < rowDimension.</code></p> * * @param row the row to be fetched * @return array of entries in the row * @throws MatrixIndexException if the specified row index is not valid */ public BigDecimal[] getRow(int row) throws MatrixIndexException { MatrixUtils.checkRowIndex(this, row); final int ncols = this.getColumnDimension(); final BigDecimal[] out = new BigDecimal[ncols]; System.arraycopy(data[row], 0, out, 0, ncols); return out; } /** * Returns the entries in row number <code>row</code> as an array * of double values. * <p> * Row indices start at 0. A <code>MatrixIndexException</code> is thrown * unless <code>0 <= row < rowDimension.</code></p> * * @param row the row to be fetched * @return array of entries in the row * @throws MatrixIndexException if the specified row index is not valid */ public double[] getRowAsDoubleArray(int row) throws MatrixIndexException { MatrixUtils.checkRowIndex(this, row); final int ncols = this.getColumnDimension(); final double[] out = new double[ncols]; for (int i=0;i<ncols;i++) { out[i] = data[row][i].doubleValue(); } return out; } /** * Returns the entries in column number <code>col</code> as an array. * <p> * Column indices start at 0. A <code>MatrixIndexException</code> is thrown * unless <code>0 <= column < columnDimension.</code></p> * * @param col the column to be fetched * @return array of entries in the column * @throws MatrixIndexException if the specified column index is not valid */ public BigDecimal[] getColumn(int col) throws MatrixIndexException { MatrixUtils.checkColumnIndex(this, col); final int nRows = this.getRowDimension(); final BigDecimal[] out = new BigDecimal[nRows]; for (int i = 0; i < nRows; i++) { out[i] = data[i][col]; } return out; } /** * Returns the entries in column number <code>col</code> as an array * of double values. * <p> * Column indices start at 0. A <code>MatrixIndexException</code> is thrown * unless <code>0 <= column < columnDimension.</code></p> * * @param col the column to be fetched * @return array of entries in the column * @throws MatrixIndexException if the specified column index is not valid */ public double[] getColumnAsDoubleArray(int col) throws MatrixIndexException { MatrixUtils.checkColumnIndex(this, col); final int nrows = this.getRowDimension(); final double[] out = new double[nrows]; for (int i=0;i<nrows;i++) { out[i] = data[i][col].doubleValue(); } return out; } /** * Returns the entry in the specified row and column. * <p> * Row and column indices start at 0 and must satisfy * <ul> * <li><code>0 <= row < rowDimension</code></li> * <li><code> 0 <= column < columnDimension</code></li> * </ul> * otherwise a <code>MatrixIndexException</code> is thrown.</p> * * @param row row location of entry to be fetched * @param column column location of entry to be fetched * @return matrix entry in row,column * @throws MatrixIndexException if the row or column index is not valid */ public BigDecimal getEntry(int row, int column) throws MatrixIndexException { try { return data[row][column]; } catch (ArrayIndexOutOfBoundsException e) { throw new MatrixIndexException( LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension()); } } /** * Returns the entry in the specified row and column as a double. * <p> * Row and column indices start at 0 and must satisfy * <ul> * <li><code>0 <= row < rowDimension</code></li> * <li><code> 0 <= column < columnDimension</code></li> * </ul> * otherwise a <code>MatrixIndexException</code> is thrown.</p> * * @param row row location of entry to be fetched * @param column column location of entry to be fetched * @return matrix entry in row,column * @throws MatrixIndexException if the row * or column index is not valid */ public double getEntryAsDouble(int row, int column) throws MatrixIndexException { return getEntry(row,column).doubleValue(); } /** * Returns the transpose matrix. * * @return transpose matrix */ public BigMatrix transpose() { final int nRows = this.getRowDimension(); final int nCols = this.getColumnDimension(); final BigDecimal[][] outData = new BigDecimal[nCols][nRows]; for (int row = 0; row < nRows; row++) { final BigDecimal[] dataRow = data[row]; for (int col = 0; col < nCols; col++) { outData[col][row] = dataRow[col]; } } return new BigMatrixImpl(outData, false); } /** * Returns the inverse matrix if this matrix is invertible. * * @return inverse matrix * @throws InvalidMatrixException if this is not invertible */ public BigMatrix inverse() throws InvalidMatrixException { return solve(MatrixUtils.createBigIdentityMatrix(getRowDimension())); } /** * Returns the determinant of this matrix. * * @return determinant * @throws InvalidMatrixException if matrix is not square */ public BigDecimal getDeterminant() throws InvalidMatrixException { if (!isSquare()) { throw new NonSquareMatrixException(getRowDimension(), getColumnDimension()); } if (isSingular()) { // note: this has side effect of attempting LU decomp if lu == null return ZERO; } else { BigDecimal det = (parity == 1) ? ONE : ONE.negate(); for (int i = 0; i < getRowDimension(); i++) { det = det.multiply(lu[i][i]); } return det; } } /** * Is this a square matrix? * @return true if the matrix is square (rowDimension = columnDimension) */ public boolean isSquare() { return getColumnDimension() == getRowDimension(); } /** * Is this a singular matrix? * @return true if the matrix is singular */ public boolean isSingular() { if (lu == null) { try { luDecompose(); return false; } catch (InvalidMatrixException ex) { return true; } } else { // LU decomp must have been successfully performed return false; // so the matrix is not singular } } /** * Returns the number of rows in the matrix. * * @return rowDimension */ public int getRowDimension() { return data.length; } /** * Returns the number of columns in the matrix. * * @return columnDimension */ public int getColumnDimension() { return data[0].length; } /** * Returns the <a href="http://mathworld.wolfram.com/MatrixTrace.html"> * trace</a> of the matrix (the sum of the elements on the main diagonal). * * @return trace * * @throws IllegalArgumentException if this matrix is not square. */ public BigDecimal getTrace() throws IllegalArgumentException { if (!isSquare()) { throw new NonSquareMatrixException(getRowDimension(), getColumnDimension()); } BigDecimal trace = data[0][0]; for (int i = 1; i < this.getRowDimension(); i++) { trace = trace.add(data[i][i]); } return trace; } /** * Returns the result of multiplying this by the vector <code>v</code>. * * @param v the vector to operate on * @return this*v * @throws IllegalArgumentException if columnDimension != v.size() */ public BigDecimal[] operate(BigDecimal[] v) throws IllegalArgumentException { if (v.length != getColumnDimension()) { throw MathRuntimeException.createIllegalArgumentException( LocalizedFormats.VECTOR_LENGTH_MISMATCH, v.length, getColumnDimension() ); } final int nRows = this.getRowDimension(); final int nCols = this.getColumnDimension(); final BigDecimal[] out = new BigDecimal[nRows]; for (int row = 0; row < nRows; row++) { BigDecimal sum = ZERO; for (int i = 0; i < nCols; i++) { sum = sum.add(data[row][i].multiply(v[i])); } out[row] = sum; } return out; } /** * Returns the result of multiplying this by the vector <code>v</code>. * * @param v the vector to operate on * @return this*v * @throws IllegalArgumentException if columnDimension != v.size() */ public BigDecimal[] operate(double[] v) throws IllegalArgumentException { final BigDecimal bd[] = new BigDecimal[v.length]; for (int i = 0; i < bd.length; i++) { bd[i] = new BigDecimal(v[i]); } return operate(bd); } /** * Returns the (row) vector result of premultiplying this by the vector <code>v</code>. * * @param v the row vector to premultiply by * @return v*this * @throws IllegalArgumentException if rowDimension != v.size() */ public BigDecimal[] preMultiply(BigDecimal[] v) throws IllegalArgumentException { final int nRows = this.getRowDimension(); if (v.length != nRows) { throw MathRuntimeException.createIllegalArgumentException( LocalizedFormats.VECTOR_LENGTH_MISMATCH, v.length, nRows ); } final int nCols = this.getColumnDimension(); final BigDecimal[] out = new BigDecimal[nCols]; for (int col = 0; col < nCols; col++) { BigDecimal sum = ZERO; for (int i = 0; i < nRows; i++) { sum = sum.add(data[i][col].multiply(v[i])); } out[col] = sum; } return out; } /** * Returns a matrix of (column) solution vectors for linear systems with * coefficient matrix = this and constant vectors = columns of * <code>b</code>. * * @param b array of constants forming RHS of linear systems to * to solve * @return solution array * @throws IllegalArgumentException if this.rowDimension != row dimension * @throws InvalidMatrixException if this matrix is not square or is singular */ public BigDecimal[] solve(BigDecimal[] b) throws IllegalArgumentException, InvalidMatrixException { final int nRows = this.getRowDimension(); if (b.length != nRows) { throw MathRuntimeException.createIllegalArgumentException( LocalizedFormats.VECTOR_LENGTH_MISMATCH, b.length, nRows); } final BigMatrix bMatrix = new BigMatrixImpl(b); final BigDecimal[][] solution = ((BigMatrixImpl) (solve(bMatrix))).getDataRef(); final BigDecimal[] out = new BigDecimal[nRows]; for (int row = 0; row < nRows; row++) { out[row] = solution[row][0]; } return out; } /** * Returns a matrix of (column) solution vectors for linear systems with * coefficient matrix = this and constant vectors = columns of * <code>b</code>. * * @param b array of constants forming RHS of linear systems to * to solve * @return solution array * @throws IllegalArgumentException if this.rowDimension != row dimension * @throws InvalidMatrixException if this matrix is not square or is singular */ public BigDecimal[] solve(double[] b) throws IllegalArgumentException, InvalidMatrixException { final BigDecimal bd[] = new BigDecimal[b.length]; for (int i = 0; i < bd.length; i++) { bd[i] = new BigDecimal(b[i]); } return solve(bd); } /** * Returns a matrix of (column) solution vectors for linear systems with * coefficient matrix = this and constant vectors = columns of * <code>b</code>. * * @param b matrix of constant vectors forming RHS of linear systems to * to solve * @return matrix of solution vectors * @throws IllegalArgumentException if this.rowDimension != row dimension * @throws InvalidMatrixException if this matrix is not square or is singular */ public BigMatrix solve(BigMatrix b) throws IllegalArgumentException, InvalidMatrixException { if (b.getRowDimension() != getRowDimension()) { throw MathRuntimeException.createIllegalArgumentException( LocalizedFormats.DIMENSIONS_MISMATCH_2x2, b.getRowDimension(), b.getColumnDimension(), getRowDimension(), "n"); } if (!isSquare()) { throw new NonSquareMatrixException(getRowDimension(), getColumnDimension()); } if (this.isSingular()) { // side effect: compute LU decomp throw new SingularMatrixException(); } final int nCol = this.getColumnDimension(); final int nColB = b.getColumnDimension(); final int nRowB = b.getRowDimension(); // Apply permutations to b final BigDecimal[][] bp = new BigDecimal[nRowB][nColB]; for (int row = 0; row < nRowB; row++) { final BigDecimal[] bpRow = bp[row]; for (int col = 0; col < nColB; col++) { bpRow[col] = b.getEntry(permutation[row], col); } } // Solve LY = b for (int col = 0; col < nCol; col++) { for (int i = col + 1; i < nCol; i++) { final BigDecimal[] bpI = bp[i]; final BigDecimal[] luI = lu[i]; for (int j = 0; j < nColB; j++) { bpI[j] = bpI[j].subtract(bp[col][j].multiply(luI[col])); } } } // Solve UX = Y for (int col = nCol - 1; col >= 0; col--) { final BigDecimal[] bpCol = bp[col]; final BigDecimal luDiag = lu[col][col]; for (int j = 0; j < nColB; j++) { bpCol[j] = bpCol[j].divide(luDiag, scale, roundingMode); } for (int i = 0; i < col; i++) { final BigDecimal[] bpI = bp[i]; final BigDecimal[] luI = lu[i]; for (int j = 0; j < nColB; j++) { bpI[j] = bpI[j].subtract(bp[col][j].multiply(luI[col])); } } } return new BigMatrixImpl(bp, false); } /** * Computes a new * <a href="http://www.math.gatech.edu/~bourbaki/math2601/Web-notes/2num.pdf"> * LU decompostion</a> for this matrix, storing the result for use by other methods. * <p> * <strong>Implementation Note</strong>:<br> * Uses <a href="http://www.damtp.cam.ac.uk/user/fdl/people/sd/lectures/nummeth98/linear.htm"> * Crout's algortithm</a>, with partial pivoting.</p> * <p> * <strong>Usage Note</strong>:<br> * This method should rarely be invoked directly. Its only use is * to force recomputation of the LU decomposition when changes have been * made to the underlying data using direct array references. Changes * made using setXxx methods will trigger recomputation when needed * automatically.</p> * * @throws InvalidMatrixException if the matrix is non-square or singular. */ public void luDecompose() throws InvalidMatrixException { final int nRows = this.getRowDimension(); final int nCols = this.getColumnDimension(); if (nRows != nCols) { throw new NonSquareMatrixException(getRowDimension(), getColumnDimension()); } lu = this.getData(); // Initialize permutation array and parity permutation = new int[nRows]; for (int row = 0; row < nRows; row++) { permutation[row] = row; } parity = 1; // Loop over columns for (int col = 0; col < nCols; col++) { BigDecimal sum = ZERO; // upper for (int row = 0; row < col; row++) { final BigDecimal[] luRow = lu[row]; sum = luRow[col]; for (int i = 0; i < row; i++) { sum = sum.subtract(luRow[i].multiply(lu[i][col])); } luRow[col] = sum; } // lower int max = col; // permutation row BigDecimal largest = ZERO; for (int row = col; row < nRows; row++) { final BigDecimal[] luRow = lu[row]; sum = luRow[col]; for (int i = 0; i < col; i++) { sum = sum.subtract(luRow[i].multiply(lu[i][col])); } luRow[col] = sum; // maintain best permutation choice if (sum.abs().compareTo(largest) == 1) { largest = sum.abs(); max = row; } } // Singularity check if (lu[max][col].abs().compareTo(TOO_SMALL) <= 0) { lu = null; throw new SingularMatrixException(); } // Pivot if necessary if (max != col) { BigDecimal tmp = ZERO; for (int i = 0; i < nCols; i++) { tmp = lu[max][i]; lu[max][i] = lu[col][i]; lu[col][i] = tmp; } int temp = permutation[max]; permutation[max] = permutation[col]; permutation[col] = temp; parity = -parity; } // Divide the lower elements by the "winning" diagonal elt. final BigDecimal luDiag = lu[col][col]; for (int row = col + 1; row < nRows; row++) { final BigDecimal[] luRow = lu[row]; luRow[col] = luRow[col].divide(luDiag, scale, roundingMode); } } } /** * Get a string representation for this matrix. * @return a string representation for this matrix */ @Override public String toString() { StringBuilder res = new StringBuilder(); res.append("BigMatrixImpl{"); if (data != null) { for (int i = 0; i < data.length; i++) { if (i > 0) { res.append(","); } res.append("{"); for (int j = 0; j < data[0].length; j++) { if (j > 0) { res.append(","); } res.append(data[i][j]); } res.append("}"); } } res.append("}"); return res.toString(); } /** * Returns true iff <code>object</code> is a * <code>BigMatrixImpl</code> instance with the same dimensions as this * and all corresponding matrix entries are equal. BigDecimal.equals * is used to compare corresponding entries. * * @param object the object to test equality against. * @return true if object equals this */ @Override public boolean equals(Object object) { if (object == this ) { return true; } if (object instanceof BigMatrixImpl == false) { return false; } final BigMatrix m = (BigMatrix) object; final int nRows = getRowDimension(); final int nCols = getColumnDimension(); if (m.getColumnDimension() != nCols || m.getRowDimension() != nRows) { return false; } for (int row = 0; row < nRows; row++) { final BigDecimal[] dataRow = data[row]; for (int col = 0; col < nCols; col++) { if (!dataRow[col].equals(m.getEntry(row, col))) { return false; } } } return true; } /** * Computes a hashcode for the matrix. * * @return hashcode for matrix */ @Override public int hashCode() { int ret = 7; final int nRows = getRowDimension(); final int nCols = getColumnDimension(); ret = ret * 31 + nRows; ret = ret * 31 + nCols; for (int row = 0; row < nRows; row++) { final BigDecimal[] dataRow = data[row]; for (int col = 0; col < nCols; col++) { ret = ret * 31 + (11 * (row+1) + 17 * (col+1)) * dataRow[col].hashCode(); } } return ret; } //------------------------ Protected methods /** * Returns the LU decomposition as a BigMatrix. * Returns a fresh copy of the cached LU matrix if this has been computed; * otherwise the composition is computed and cached for use by other methods. * Since a copy is returned in either case, changes to the returned matrix do not * affect the LU decomposition property. * <p> * The matrix returned is a compact representation of the LU decomposition. * Elements below the main diagonal correspond to entries of the "L" matrix; * elements on and above the main diagonal correspond to entries of the "U" * matrix.</p> * <p> * Example: <pre> * * Returned matrix L U * 2 3 1 1 0 0 2 3 1 * 5 4 6 5 1 0 0 4 6 * 1 7 8 1 7 1 0 0 8 * </pre> * * The L and U matrices satisfy the matrix equation LU = permuteRows(this), <br> * where permuteRows reorders the rows of the matrix to follow the order determined * by the <a href=#getPermutation()>permutation</a> property.</p> * * @return LU decomposition matrix * @throws InvalidMatrixException if the matrix is non-square or singular. */ protected BigMatrix getLUMatrix() throws InvalidMatrixException { if (lu == null) { luDecompose(); } return new BigMatrixImpl(lu); } /** * Returns the permutation associated with the lu decomposition. * The entries of the array represent a permutation of the numbers 0, ... , nRows - 1. * <p> * Example: * permutation = [1, 2, 0] means current 2nd row is first, current third row is second * and current first row is last.</p> * <p> * Returns a fresh copy of the array.</p> * * @return the permutation */ protected int[] getPermutation() { final int[] out = new int[permutation.length]; System.arraycopy(permutation, 0, out, 0, permutation.length); return out; } //------------------------ Private methods /** * Returns a fresh copy of the underlying data array. * * @return a copy of the underlying data array. */ private BigDecimal[][] copyOut() { final int nRows = this.getRowDimension(); final BigDecimal[][] out = new BigDecimal[nRows][this.getColumnDimension()]; // can't copy 2-d array in one shot, otherwise get row references for (int i = 0; i < nRows; i++) { System.arraycopy(data[i], 0, out[i], 0, data[i].length); } return out; } /** * Replaces data with a fresh copy of the input array. * <p> * Verifies that the input array is rectangular and non-empty.</p> * * @param in data to copy in * @throws IllegalArgumentException if input array is emtpy or not * rectangular * @throws NullPointerException if input array is null */ private void copyIn(BigDecimal[][] in) { setSubMatrix(in,0,0); } /** * Replaces data with a fresh copy of the input array. * * @param in data to copy in */ private void copyIn(double[][] in) { final int nRows = in.length; final int nCols = in[0].length; data = new BigDecimal[nRows][nCols]; for (int i = 0; i < nRows; i++) { final BigDecimal[] dataI = data[i]; final double[] inI = in[i]; for (int j = 0; j < nCols; j++) { dataI[j] = new BigDecimal(inI[j]); } } lu = null; } /** * Replaces data with BigDecimals represented by the strings in the input * array. * * @param in data to copy in */ private void copyIn(String[][] in) { final int nRows = in.length; final int nCols = in[0].length; data = new BigDecimal[nRows][nCols]; for (int i = 0; i < nRows; i++) { final BigDecimal[] dataI = data[i]; final String[] inI = in[i]; for (int j = 0; j < nCols; j++) { dataI[j] = new BigDecimal(inI[j]); } } lu = null; } }