added a getQT() method to QRDecomposition interface
compute Q by transposing QT rather than the other way round for efficiency JIRA: MATH-223 git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/branches/MATH_2_0@699850 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
19a6674a57
commit
8260edaaf6
|
@ -57,6 +57,15 @@ public interface QRDecomposition extends DecompositionSolver {
|
||||||
*/
|
*/
|
||||||
RealMatrix getQ() throws IllegalStateException;
|
RealMatrix getQ() throws IllegalStateException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the transpose of the matrix Q of the decomposition.
|
||||||
|
* <p>Q is an orthogonal matrix</p>
|
||||||
|
* @return the Q matrix
|
||||||
|
* @exception IllegalStateException if {@link
|
||||||
|
* DecompositionSolver#decompose(RealMatrix) decompose} has not been called
|
||||||
|
*/
|
||||||
|
RealMatrix getQT() throws IllegalStateException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Householder reflector vectors.
|
* Returns the Householder reflector vectors.
|
||||||
* <p>H is a lower trapezoidal matrix whose columns represent
|
* <p>H is a lower trapezoidal matrix whose columns represent
|
||||||
|
|
|
@ -52,6 +52,9 @@ public class QRDecompositionImpl implements QRDecomposition {
|
||||||
/** Cached value of Q. */
|
/** Cached value of Q. */
|
||||||
private RealMatrix cachedQ;
|
private RealMatrix cachedQ;
|
||||||
|
|
||||||
|
/** Cached value of QT. */
|
||||||
|
private RealMatrix cachedQT;
|
||||||
|
|
||||||
/** Cached value of R. */
|
/** Cached value of R. */
|
||||||
private RealMatrix cachedR;
|
private RealMatrix cachedR;
|
||||||
|
|
||||||
|
@ -87,9 +90,10 @@ public class QRDecompositionImpl implements QRDecomposition {
|
||||||
final int n = matrix.getColumnDimension();
|
final int n = matrix.getColumnDimension();
|
||||||
qrt = matrix.transpose().getData();
|
qrt = matrix.transpose().getData();
|
||||||
rDiag = new double[n];
|
rDiag = new double[n];
|
||||||
cachedQ = null;
|
cachedQ = null;
|
||||||
cachedR = null;
|
cachedQT = null;
|
||||||
cachedH = null;
|
cachedR = null;
|
||||||
|
cachedH = null;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The QR decomposition of a matrix A is calculated using Householder
|
* The QR decomposition of a matrix A is calculated using Householder
|
||||||
|
@ -191,15 +195,24 @@ public class QRDecompositionImpl implements QRDecomposition {
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
public RealMatrix getQ()
|
public RealMatrix getQ()
|
||||||
throws IllegalStateException {
|
throws IllegalStateException {
|
||||||
|
|
||||||
if (cachedQ == null) {
|
if (cachedQ == null) {
|
||||||
|
cachedQ = getQT().transpose();
|
||||||
|
}
|
||||||
|
return cachedQ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
public RealMatrix getQT()
|
||||||
|
throws IllegalStateException {
|
||||||
|
|
||||||
|
if (cachedQ == null) {
|
||||||
|
|
||||||
checkDecomposed();
|
checkDecomposed();
|
||||||
|
|
||||||
// Q is supposed to be m x m
|
// QT is supposed to be m x m
|
||||||
final int n = qrt.length;
|
final int n = qrt.length;
|
||||||
final int m = qrt[0].length;
|
final int m = qrt[0].length;
|
||||||
double[][] q = new double[m][m];
|
double[][] qT = new double[m][m];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Q = Q1 Q2 ... Q_m, so Q is formed by first constructing Q_m and then
|
* Q = Q1 Q2 ... Q_m, so Q is formed by first constructing Q_m and then
|
||||||
|
@ -207,34 +220,35 @@ public class QRDecompositionImpl implements QRDecomposition {
|
||||||
* succession to the result
|
* succession to the result
|
||||||
*/
|
*/
|
||||||
for (int minor = m - 1; minor >= Math.min(m, n); minor--) {
|
for (int minor = m - 1; minor >= Math.min(m, n); minor--) {
|
||||||
q[minor][minor]=1;
|
qT[minor][minor]=1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int minor = Math.min(m,n)-1; minor >= 0; minor--){
|
for (int minor = Math.min(m,n)-1; minor >= 0; minor--){
|
||||||
final double[] qrtMinor = qrt[minor];
|
final double[] qrtMinor = qrt[minor];
|
||||||
q[minor][minor] = 1;
|
qT[minor][minor] = 1;
|
||||||
if (qrtMinor[minor] != 0.0) {
|
if (qrtMinor[minor] != 0.0) {
|
||||||
for (int col = minor; col < m; col++) {
|
for (int col = minor; col < m; col++) {
|
||||||
|
final double[] qTCol = qT[col];
|
||||||
double alpha = 0;
|
double alpha = 0;
|
||||||
for (int row = minor; row < m; row++) {
|
for (int row = minor; row < m; row++) {
|
||||||
alpha -= q[row][col] * qrtMinor[row];
|
alpha -= qTCol[row] * qrtMinor[row];
|
||||||
}
|
}
|
||||||
alpha /= rDiag[minor] * qrtMinor[minor];
|
alpha /= rDiag[minor] * qrtMinor[minor];
|
||||||
|
|
||||||
for (int row = minor; row < m; row++) {
|
for (int row = minor; row < m; row++) {
|
||||||
q[row][col] -= alpha * qrtMinor[row];
|
qTCol[row] -= alpha * qrtMinor[row];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cache the matrix for subsequent calls
|
// cache the matrix for subsequent calls
|
||||||
cachedQ = new RealMatrixImpl(q, false);
|
cachedQT = new RealMatrixImpl(qT, false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the cached matrix
|
// return the cached matrix
|
||||||
return cachedQ;
|
return cachedQT;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,31 +109,31 @@ public class QRDecompositionImplTest extends TestCase {
|
||||||
/** test the orthogonality of Q */
|
/** test the orthogonality of Q */
|
||||||
public void testQOrthogonal() {
|
public void testQOrthogonal() {
|
||||||
RealMatrix matrix = new RealMatrixImpl(testData3x3NonSingular, false);
|
RealMatrix matrix = new RealMatrixImpl(testData3x3NonSingular, false);
|
||||||
matrix = new QRDecompositionImpl(matrix).getQ();
|
RealMatrix q = new QRDecompositionImpl(matrix).getQ();
|
||||||
|
RealMatrix qT = new QRDecompositionImpl(matrix).getQT();
|
||||||
RealMatrix eye = MatrixUtils.createRealIdentityMatrix(3);
|
RealMatrix eye = MatrixUtils.createRealIdentityMatrix(3);
|
||||||
double norm = matrix.transpose().multiply(matrix).subtract(eye)
|
double norm = qT.multiply(q).subtract(eye).getNorm();
|
||||||
.getNorm();
|
|
||||||
assertEquals("3x3 nonsingular Q'Q = I", 0, norm, normTolerance);
|
assertEquals("3x3 nonsingular Q'Q = I", 0, norm, normTolerance);
|
||||||
|
|
||||||
matrix = new RealMatrixImpl(testData3x3Singular, false);
|
matrix = new RealMatrixImpl(testData3x3Singular, false);
|
||||||
matrix = new QRDecompositionImpl(matrix).getQ();
|
q = new QRDecompositionImpl(matrix).getQ();
|
||||||
|
qT = new QRDecompositionImpl(matrix).getQT();
|
||||||
eye = MatrixUtils.createRealIdentityMatrix(3);
|
eye = MatrixUtils.createRealIdentityMatrix(3);
|
||||||
norm = matrix.transpose().multiply(matrix).subtract(eye)
|
norm = qT.multiply(q).subtract(eye).getNorm();
|
||||||
.getNorm();
|
|
||||||
assertEquals("3x3 singular Q'Q = I", 0, norm, normTolerance);
|
assertEquals("3x3 singular Q'Q = I", 0, norm, normTolerance);
|
||||||
|
|
||||||
matrix = new RealMatrixImpl(testData3x4, false);
|
matrix = new RealMatrixImpl(testData3x4, false);
|
||||||
matrix = new QRDecompositionImpl(matrix).getQ();
|
q = new QRDecompositionImpl(matrix).getQ();
|
||||||
|
qT = new QRDecompositionImpl(matrix).getQT();
|
||||||
eye = MatrixUtils.createRealIdentityMatrix(3);
|
eye = MatrixUtils.createRealIdentityMatrix(3);
|
||||||
norm = matrix.transpose().multiply(matrix).subtract(eye)
|
norm = qT.multiply(q).subtract(eye).getNorm();
|
||||||
.getNorm();
|
|
||||||
assertEquals("3x4 Q'Q = I", 0, norm, normTolerance);
|
assertEquals("3x4 Q'Q = I", 0, norm, normTolerance);
|
||||||
|
|
||||||
matrix = new RealMatrixImpl(testData4x3, false);
|
matrix = new RealMatrixImpl(testData4x3, false);
|
||||||
matrix = new QRDecompositionImpl(matrix).getQ();
|
q = new QRDecompositionImpl(matrix).getQ();
|
||||||
|
qT = new QRDecompositionImpl(matrix).getQT();
|
||||||
eye = MatrixUtils.createRealIdentityMatrix(4);
|
eye = MatrixUtils.createRealIdentityMatrix(4);
|
||||||
norm = matrix.transpose().multiply(matrix).subtract(eye)
|
norm = qT.multiply(q).subtract(eye).getNorm();
|
||||||
.getNorm();
|
|
||||||
assertEquals("4x3 Q'Q = I", 0, norm, normTolerance);
|
assertEquals("4x3 Q'Q = I", 0, norm, normTolerance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,6 +337,8 @@ public class QRDecompositionImplTest extends TestCase {
|
||||||
// check values against known references
|
// check values against known references
|
||||||
RealMatrix q = qr.getQ();
|
RealMatrix q = qr.getQ();
|
||||||
assertEquals(0, q.subtract(qRef).getNorm(), 1.0e-13);
|
assertEquals(0, q.subtract(qRef).getNorm(), 1.0e-13);
|
||||||
|
RealMatrix qT = qr.getQT();
|
||||||
|
assertEquals(0, qT.subtract(qRef.transpose()).getNorm(), 1.0e-13);
|
||||||
RealMatrix r = qr.getR();
|
RealMatrix r = qr.getR();
|
||||||
assertEquals(0, r.subtract(rRef).getNorm(), 1.0e-13);
|
assertEquals(0, r.subtract(rRef).getNorm(), 1.0e-13);
|
||||||
RealMatrix h = qr.getH();
|
RealMatrix h = qr.getH();
|
||||||
|
|
Loading…
Reference in New Issue