/Users/lyon/j4p/src/math/linearAlgebra/QRDecomposition.java

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