diff options
Diffstat (limited to 'src/main/java/org/apache/commons/math/linear/BigMatrixImpl.java')
-rw-r--r-- | src/main/java/org/apache/commons/math/linear/BigMatrixImpl.java | 1505 |
1 files changed, 1505 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/math/linear/BigMatrixImpl.java b/src/main/java/org/apache/commons/math/linear/BigMatrixImpl.java new file mode 100644 index 0000000..80643a0 --- /dev/null +++ b/src/main/java/org/apache/commons/math/linear/BigMatrixImpl.java @@ -0,0 +1,1505 @@ +/* + * 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; + } + +} |