package tim.prune.function.estimate.jama; /** * The Java Matrix Class provides the fundamental operations of numerical linear algebra. * Original authors The MathWorks, Inc. and the National Institute of Standards and Technology * The original public domain code has now been modified and reduced to only contain * the use of QR Decomposition of rectangular matrices, to solve least squares regression, * and is placed under GPL2 with the rest of the GpsPrune code. */ public class Matrix { /** Array for internal storage of elements */ private double[][] _matrix; /** Row and column dimensions */ private int _m, _n; /** * Construct an m-by-n matrix of zeros * @param inM Number of rows * @param inN Number of colums */ public Matrix(int inM, int inN) { _m = inM; _n = inN; _matrix = new double[inM][inN]; } /** * Construct a matrix from a 2-D array * @param A Two-dimensional array of doubles. * @exception IllegalArgumentException All rows must have the same length */ public Matrix(double[][] A) { _m = A.length; _n = A[0].length; for (int i = 0; i < _m; i++) { if (A[i].length != _n) { throw new IllegalArgumentException("All rows must have the same length."); } } _matrix = A; } /** * Construct a matrix quickly without checking arguments. * @param inA Two-dimensional array of doubles. * @param inM Number of rows * @param inN Number of columns */ public Matrix(double[][] inA, int inM, int inN) { _matrix = inA; _m = inM; _n = inN; } /* * ------------------------ Public Methods ------------------------ */ /** * Set a value in a cell of the matrix * @param inRow row index * @param inCol column index * @param inValue value to set */ public void setValue(int inRow, int inCol, double inValue) { _matrix[inRow][inCol] = inValue; } /** * Access the internal two-dimensional array. * @return Pointer to the two-dimensional array of matrix elements. */ public double[][] getArray() { return _matrix; } /** * Copy the internal two-dimensional array. * @return Two-dimensional array copy of matrix elements. */ public double[][] getArrayCopy() { double[][] C = new double[_m][_n]; for (int i = 0; i < _m; i++) { for (int j = 0; j < _n; j++) { C[i][j] = _matrix[i][j]; } } return C; } /** * Get a single element. * @param inRow Row index * @param inCol Column index * @return A(inRow,inCol) * @exception ArrayIndexOutOfBoundsException */ public double get(int inRow, int inCol) { return _matrix[inRow][inCol]; } /** @return number of rows _m */ public int getNumRows() { return _m; } /** @return number of columns _n */ public int getNumColumns() { return _n; } /** * Get a submatrix * @param i0 Initial row index * @param i1 Final row index * @param j0 Initial column index * @param j1 Final column index * @return A(i0:i1,j0:j1) * @exception ArrayIndexOutOfBoundsException */ public Matrix getMatrix(int i0, int i1, int j0, int j1) { Matrix X = new Matrix(i1 - i0 + 1, j1 - j0 + 1); double[][] B = X.getArray(); try { for (int i = i0; i <= i1; i++) { for (int j = j0; j <= j1; j++) { B[i - i0][j - j0] = _matrix[i][j]; } } } catch (ArrayIndexOutOfBoundsException e) { throw new ArrayIndexOutOfBoundsException("Submatrix indices"); } return X; } /** * Linear algebraic matrix multiplication, A * B * @param B another matrix * @return Matrix product, A * B * @exception IllegalArgumentException if matrix dimensions don't agree */ public Matrix times(Matrix B) { if (B._m != _n) { throw new IllegalArgumentException("Matrix inner dimensions must agree."); } Matrix X = new Matrix(_m, B._n); double[][] C = X.getArray(); double[] Bcolj = new double[_n]; for (int j = 0; j < B._n; j++) { for (int k = 0; k < _n; k++) { Bcolj[k] = B._matrix[k][j]; } for (int i = 0; i < _m; i++) { double[] Arowi = _matrix[i]; double s = 0; for (int k = 0; k < _n; k++) { s += Arowi[k] * Bcolj[k]; } C[i][j] = s; } } return X; } /** * Subtract the other matrix from this one * @param B another matrix * @return difference this - B * @exception IllegalArgumentException if matrix dimensions don't agree */ public Matrix minus(Matrix B) { if (B._m != _m || B._n != _n) { throw new IllegalArgumentException("Matrix dimensions must agree."); } Matrix result = new Matrix(_m, _n); for (int i = 0; i < _m; i++) { for (int j = 0; j < _n; j++) { result.setValue(i, j, get(i, j) - B.get(i, j)); } } return result; } /** * Divide each element of this matrix by the corresponding element in the other one * @param B another matrix * @return this[i,j]/other[i,j] * @exception IllegalArgumentException if matrix dimensions don't agree */ public Matrix divideEach(Matrix B) { if (B._m != _m || B._n != _n) { throw new IllegalArgumentException("Matrix dimensions must agree."); } Matrix result = new Matrix(_m, _n); for (int i = 0; i < _m; i++) { for (int j = 0; j < _n; j++) { result.setValue(i, j, get(i, j) / B.get(i, j)); } } return result; } /** * Solve A*X = B * @param B right hand side * @return least squares solution */ public Matrix solve(Matrix B) { return new QRDecomposition(this).solve(B); } /** * @return the average absolute value of all the elements in the matrix */ public double getAverageAbsValue() { double total = 0.0; for (int i = 0; i < _m; i++) { for (int j = 0; j < _n; j++) { total += Math.abs(_matrix[i][j]); } } return total / _m / _n; } /** * Primitive output for debugging */ public String toString() { StringBuilder builder = new StringBuilder(); builder.append('('); for (int i = 0; i < _m; i++) { builder.append('('); for (int j = 0; j < _n; j++) { builder.append((_matrix[i][j])); builder.append(", "); } builder.append(") "); } builder.append(')'); return builder.toString(); } }