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; } /** * 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)); } }