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();
}
}