View Javadoc

1   package Jama;
2   import Jama.util.*;
3   
4   /** QR Decomposition.
5   <P>
6      For an m-by-n matrix A with m >= n, the QR decomposition is an m-by-n
7      orthogonal matrix Q and an n-by-n upper triangular matrix R so that
8      A = Q*R.
9   <P>
10     The QR decompostion always exists, even if the matrix does not have
11     full rank, so the constructor will never fail.  The primary use of the
12     QR decomposition is in the least squares solution of nonsquare systems
13     of simultaneous linear equations.  This will fail if isFullRank()
14     returns false.
15     @version $Id: QRDecomposition.java,v 1.1.1.1 2010/11/30 21:31:59 jeremy Exp $
16  */
17  
18  public class QRDecomposition implements java.io.Serializable {
19  
20  /* ------------------------
21     Class variables
22   * ------------------------ */
23  
24     /** Array for internal storage of decomposition.
25     @serial internal array storage.
26     */
27     private double[][] QR;
28  
29     /** Row and column dimensions.
30     @serial column dimension.
31     @serial row dimension.
32     */
33     private int m, n;
34  
35     /** Array for internal storage of diagonal of R.
36     @serial diagonal of R.
37     */
38     private double[] Rdiag;
39  
40  /* ------------------------
41     Constructor
42   * ------------------------ */
43  
44     /** QR Decomposition, computed by Householder reflections.
45     @param A    Rectangular matrix
46     */
47  
48     public QRDecomposition (Matrix A) {
49        // Initialize.
50        QR = A.getArrayCopy();
51        m = A.getRowDimension();
52        n = A.getColumnDimension();
53        Rdiag = new double[n];
54  
55        // Main loop.
56        for (int k = 0; k < n; k++) {
57           // Compute 2-norm of k-th column without under/overflow.
58           double nrm = 0;
59           for (int i = k; i < m; i++) {
60              nrm = Maths.hypot(nrm,QR[i][k]);
61           }
62  
63           if (nrm != 0.0) {
64              // Form k-th Householder vector.
65              if (QR[k][k] < 0) {
66                 nrm = -nrm;
67              }
68              for (int i = k; i < m; i++) {
69                 QR[i][k] /= nrm;
70              }
71              QR[k][k] += 1.0;
72  
73              // Apply transformation to remaining columns.
74              for (int j = k+1; j < n; j++) {
75                 double s = 0.0; 
76                 for (int i = k; i < m; i++) {
77                    s += QR[i][k]*QR[i][j];
78                 }
79                 s = -s/QR[k][k];
80                 for (int i = k; i < m; i++) {
81                    QR[i][j] += s*QR[i][k];
82                 }
83              }
84           }
85           Rdiag[k] = -nrm;
86        }
87     }
88  
89  /* ------------------------
90     Public Methods
91   * ------------------------ */
92  
93     /** Is the matrix full rank?
94     @return     true if R, and hence A, has full rank.
95     */
96  
97     public boolean isFullRank () {
98        for (int j = 0; j < n; j++) {
99           if (Rdiag[j] == 0)
100             return false;
101       }
102       return true;
103    }
104 
105    /** Return the Householder vectors
106    @return     Lower trapezoidal matrix whose columns define the reflections
107    */
108 
109    public Matrix getH () {
110       Matrix X = new Matrix(m,n);
111       double[][] H = X.getArray();
112       for (int i = 0; i < m; i++) {
113          for (int j = 0; j < n; j++) {
114             if (i >= j) {
115                H[i][j] = QR[i][j];
116             } else {
117                H[i][j] = 0.0;
118             }
119          }
120       }
121       return X;
122    }
123 
124    /** Return the upper triangular factor
125    @return     R
126    */
127 
128    public Matrix getR () {
129       Matrix X = new Matrix(n,n);
130       double[][] R = X.getArray();
131       for (int i = 0; i < n; i++) {
132          for (int j = 0; j < n; j++) {
133             if (i < j) {
134                R[i][j] = QR[i][j];
135             } else if (i == j) {
136                R[i][j] = Rdiag[i];
137             } else {
138                R[i][j] = 0.0;
139             }
140          }
141       }
142       return X;
143    }
144 
145    /** Generate and return the (economy-sized) orthogonal factor
146    @return     Q
147    */
148 
149    public Matrix getQ () {
150       Matrix X = new Matrix(m,n);
151       double[][] Q = X.getArray();
152       for (int k = n-1; k >= 0; k--) {
153          for (int i = 0; i < m; i++) {
154             Q[i][k] = 0.0;
155          }
156          Q[k][k] = 1.0;
157          for (int j = k; j < n; j++) {
158             if (QR[k][k] != 0) {
159                double s = 0.0;
160                for (int i = k; i < m; i++) {
161                   s += QR[i][k]*Q[i][j];
162                }
163                s = -s/QR[k][k];
164                for (int i = k; i < m; i++) {
165                   Q[i][j] += s*QR[i][k];
166                }
167             }
168          }
169       }
170       return X;
171    }
172 
173    /** Least squares solution of A*X = B
174    @param B    A Matrix with as many rows as A and any number of columns.
175    @return     X that minimizes the two norm of Q*R*X-B.
176    @exception  IllegalArgumentException  Matrix row dimensions must agree.
177    @exception  RuntimeException  Matrix is rank deficient.
178    */
179 
180    public Matrix solve (Matrix B) {
181       if (B.getRowDimension() != m) {
182          throw new IllegalArgumentException("Matrix row dimensions must agree.");
183       }
184       if (!this.isFullRank()) {
185          throw new RuntimeException("Matrix is rank deficient.");
186       }
187       
188       // Copy right hand side
189       int nx = B.getColumnDimension();
190       double[][] X = B.getArrayCopy();
191 
192       // Compute Y = transpose(Q)*B
193       for (int k = 0; k < n; k++) {
194          for (int j = 0; j < nx; j++) {
195             double s = 0.0; 
196             for (int i = k; i < m; i++) {
197                s += QR[i][k]*X[i][j];
198             }
199             s = -s/QR[k][k];
200             for (int i = k; i < m; i++) {
201                X[i][j] += s*QR[i][k];
202             }
203          }
204       }
205       // Solve R*X = Y;
206       for (int k = n-1; k >= 0; k--) {
207          for (int j = 0; j < nx; j++) {
208             X[k][j] /= Rdiag[k];
209          }
210          for (int i = 0; i < k; i++) {
211             for (int j = 0; j < nx; j++) {
212                X[i][j] -= X[k][j]*QR[i][k];
213             }
214          }
215       }
216       return (new Matrix(X,n,nx).getMatrix(0,n-1,0,nx-1));
217    }
218 }