package tim.prune.function.estimate.jama;
/**
* QR Decomposition.
*
* For an m-by-n matrix A with m >= n, the QR decomposition is an m-by-n
* orthogonal matrix Q and an n-by-n upper triangular matrix R so that A = Q*R.
*
* The QR decomposition always exists, even if the matrix does not have full
* rank, so the constructor will never fail. The primary use of the QR
* decomposition is in the least squares solution of nonsquare systems of
* simultaneous linear equations. This will fail if isFullRank() returns false.
*
* Original authors The MathWorks, Inc. and the National Institute of Standards and Technology
* The original public domain code has now been modified and reduced,
* and is placed under GPL2 with the rest of the GpsPrune code.
*/
public class QRDecomposition
{
/** Array for internal storage of decomposition */
private double[][] _QR;
/** Row and column dimensions */
private int _m, _n;
/** Array for internal storage of diagonal of R */
private double[] _Rdiag;
/**
* QR Decomposition, computed by Householder reflections.
*
* @param inA Rectangular matrix
* @return Structure to access R and the Householder vectors and compute Q.
*/
public QRDecomposition(Matrix inA)
{
// Initialize.
_QR = inA.getArrayCopy();
_m = inA.getNumRows();
_n = inA.getNumColumns();
_Rdiag = new double[_n];
// Main loop.
for (int k = 0; k < _n; k++)
{
// Compute 2-norm of k-th column without under/overflow.
double nrm = 0;
for (int i = k; i < _m; i++) {
nrm = Maths.pythag(nrm, _QR[i][k]);
}
if (nrm != 0.0)
{
// Form k-th Householder vector.
if (_QR[k][k] < 0) {
nrm = -nrm;
}
for (int i = k; i < _m; i++) {
_QR[i][k] /= nrm;
}
_QR[k][k] += 1.0;
// Apply transformation to remaining columns.
for (int j = k + 1; j < _n; j++)
{
double s = 0.0;
for (int i = k; i < _m; i++) {
s += _QR[i][k] * _QR[i][j];
}
s = -s / _QR[k][k];
for (int i = k; i < _m; i++) {
_QR[i][j] += s * _QR[i][k];
}
}
}
_Rdiag[k] = -nrm;
}
}
/*
* ------------------------ Public Methods ------------------------
*/
/**
* Is the matrix full rank?
* @return true if R, and hence A, has full rank.
*/
public boolean isFullRank()
{
for (int j = 0; j < _n; j++) {
if (_Rdiag[j] == 0)
return false;
}
return true;
}
/**
* Return the Householder vectors
* @deprecated
* @return Lower trapezoidal matrix whose columns define the reflections
*/
private Matrix getH()
{
Matrix X = new Matrix(_m, _n);
double[][] H = X.getArray();
for (int i = 0; i < _m; i++) {
for (int j = 0; j < _n; j++) {
if (i >= j) {
H[i][j] = _QR[i][j];
} else {
H[i][j] = 0.0;
}
}
}
return X;
}
/**
* Return the upper triangular factor
* @deprecated
* @return R
*/
private Matrix getR()
{
Matrix X = new Matrix(_n, _n);
double[][] R = X.getArray();
for (int i = 0; i < _n; i++) {
for (int j = 0; j < _n; j++) {
if (i < j) {
R[i][j] = _QR[i][j];
} else if (i == j) {
R[i][j] = _Rdiag[i];
} else {
R[i][j] = 0.0;
}
}
}
return X;
}
/**
* Generate and return the (economy-sized) orthogonal factor
* @deprecated
* @return Q
*/
private Matrix getQ()
{
Matrix X = new Matrix(_m, _n);
double[][] Q = X.getArray();
for (int k = _n - 1; k >= 0; k--) {
for (int i = 0; i < _m; i++) {
Q[i][k] = 0.0;
}
Q[k][k] = 1.0;
for (int j = k; j < _n; j++) {
if (_QR[k][k] != 0) {
double s = 0.0;
for (int i = k; i < _m; i++) {
s += _QR[i][k] * Q[i][j];
}
s = -s / _QR[k][k];
for (int i = k; i < _m; i++) {
Q[i][j] += s * _QR[i][k];
}
}
}
}
return X;
}
/**
* Least squares solution of A*X = B
* @param B A Matrix with as many rows as A and any number of columns
* @return X that minimizes the two norm of Q*R*X-B
* @exception IllegalArgumentException if matrix dimensions don't agree
* @exception RuntimeException if Matrix is rank deficient.
*/
public Matrix solve(Matrix B)
{
if (B.getNumRows() != _m) {
throw new IllegalArgumentException("Matrix row dimensions must agree.");
}
if (!isFullRank()) {
throw new RuntimeException("Matrix is rank deficient.");
}
// Copy right hand side
int nx = B.getNumColumns();
double[][] X = B.getArrayCopy();
// Compute Y = transpose(Q)*B
for (int k = 0; k < _n; k++) {
for (int j = 0; j < nx; j++) {
double s = 0.0;
for (int i = k; i < _m; i++) {
s += _QR[i][k] * X[i][j];
}
s = -s / _QR[k][k];
for (int i = k; i < _m; i++) {
X[i][j] += s * _QR[i][k];
}
}
}
// Solve R*X = Y;
for (int k = _n - 1; k >= 0; k--) {
for (int j = 0; j < nx; j++) {
X[k][j] /= _Rdiag[k];
}
for (int i = 0; i < k; i++) {
for (int j = 0; j < nx; j++) {
X[i][j] -= X[k][j] * _QR[i][k];
}
}
}
return (new Matrix(X, _n, nx).getMatrix(0, _n - 1, 0, nx - 1));
}
}