1 | /* |
---|
2 | * This software is a cooperative product of The MathWorks and the National |
---|
3 | * Institute of Standards and Technology (NIST) which has been released to the |
---|
4 | * public domain. Neither The MathWorks nor NIST assumes any responsibility |
---|
5 | * whatsoever for its use by other parties, and makes no guarantees, expressed |
---|
6 | * or implied, about its quality, reliability, or any other characteristic. |
---|
7 | */ |
---|
8 | |
---|
9 | /* |
---|
10 | * QRDecomposition.java |
---|
11 | * Copyright (C) 1999 The Mathworks and NIST |
---|
12 | * |
---|
13 | */ |
---|
14 | |
---|
15 | package weka.core.matrix; |
---|
16 | |
---|
17 | import weka.core.RevisionHandler; |
---|
18 | import weka.core.RevisionUtils; |
---|
19 | |
---|
20 | import java.io.Serializable; |
---|
21 | |
---|
22 | /** |
---|
23 | * QR Decomposition. |
---|
24 | * <P> |
---|
25 | * For an m-by-n matrix A with m >= n, the QR decomposition is an m-by-n |
---|
26 | * orthogonal matrix Q and an n-by-n upper triangular matrix R so that A = Q*R. |
---|
27 | * <P> |
---|
28 | * The QR decompostion always exists, even if the matrix does not have full |
---|
29 | * rank, so the constructor will never fail. The primary use of the QR |
---|
30 | * decomposition is in the least squares solution of nonsquare systems of |
---|
31 | * simultaneous linear equations. This will fail if isFullRank() returns false. |
---|
32 | * <p/> |
---|
33 | * Adapted from the <a href="http://math.nist.gov/javanumerics/jama/" target="_blank">JAMA</a> package. |
---|
34 | * |
---|
35 | * @author The Mathworks and NIST |
---|
36 | * @author Fracpete (fracpete at waikato dot ac dot nz) |
---|
37 | * @version $Revision: 5953 $ |
---|
38 | */ |
---|
39 | public class QRDecomposition |
---|
40 | implements Serializable, RevisionHandler { |
---|
41 | |
---|
42 | /** for serialization */ |
---|
43 | private static final long serialVersionUID = -5013090736132211418L; |
---|
44 | |
---|
45 | /** |
---|
46 | * Array for internal storage of decomposition. |
---|
47 | * @serial internal array storage. |
---|
48 | */ |
---|
49 | private double[][] QR; |
---|
50 | |
---|
51 | /** |
---|
52 | * Row and column dimensions. |
---|
53 | * @serial column dimension. |
---|
54 | * @serial row dimension. |
---|
55 | */ |
---|
56 | private int m, n; |
---|
57 | |
---|
58 | /** |
---|
59 | * Array for internal storage of diagonal of R. |
---|
60 | * @serial diagonal of R. |
---|
61 | */ |
---|
62 | private double[] Rdiag; |
---|
63 | |
---|
64 | /** |
---|
65 | * QR Decomposition, computed by Householder reflections. |
---|
66 | * @param A Rectangular matrix |
---|
67 | */ |
---|
68 | public QRDecomposition(Matrix A) { |
---|
69 | // Initialize. |
---|
70 | QR = A.getArrayCopy(); |
---|
71 | m = A.getRowDimension(); |
---|
72 | n = A.getColumnDimension(); |
---|
73 | Rdiag = new double[n]; |
---|
74 | |
---|
75 | // Main loop. |
---|
76 | for (int k = 0; k < n; k++) { |
---|
77 | // Compute 2-norm of k-th column without under/overflow. |
---|
78 | double nrm = 0; |
---|
79 | for (int i = k; i < m; i++) { |
---|
80 | nrm = Maths.hypot(nrm,QR[i][k]); |
---|
81 | } |
---|
82 | |
---|
83 | if (nrm != 0.0) { |
---|
84 | // Form k-th Householder vector. |
---|
85 | if (QR[k][k] < 0) { |
---|
86 | nrm = -nrm; |
---|
87 | } |
---|
88 | for (int i = k; i < m; i++) { |
---|
89 | QR[i][k] /= nrm; |
---|
90 | } |
---|
91 | QR[k][k] += 1.0; |
---|
92 | |
---|
93 | // Apply transformation to remaining columns. |
---|
94 | for (int j = k+1; j < n; j++) { |
---|
95 | double s = 0.0; |
---|
96 | for (int i = k; i < m; i++) { |
---|
97 | s += QR[i][k]*QR[i][j]; |
---|
98 | } |
---|
99 | s = -s/QR[k][k]; |
---|
100 | for (int i = k; i < m; i++) { |
---|
101 | QR[i][j] += s*QR[i][k]; |
---|
102 | } |
---|
103 | } |
---|
104 | } |
---|
105 | Rdiag[k] = -nrm; |
---|
106 | } |
---|
107 | } |
---|
108 | |
---|
109 | /** |
---|
110 | * Is the matrix full rank? |
---|
111 | * @return true if R, and hence A, has full rank. |
---|
112 | */ |
---|
113 | public boolean isFullRank() { |
---|
114 | for (int j = 0; j < n; j++) { |
---|
115 | if (Rdiag[j] == 0) |
---|
116 | return false; |
---|
117 | } |
---|
118 | return true; |
---|
119 | } |
---|
120 | |
---|
121 | /** |
---|
122 | * Return the Householder vectors |
---|
123 | * @return Lower trapezoidal matrix whose columns define the reflections |
---|
124 | */ |
---|
125 | public Matrix getH() { |
---|
126 | Matrix X = new Matrix(m,n); |
---|
127 | double[][] H = X.getArray(); |
---|
128 | for (int i = 0; i < m; i++) { |
---|
129 | for (int j = 0; j < n; j++) { |
---|
130 | if (i >= j) { |
---|
131 | H[i][j] = QR[i][j]; |
---|
132 | } else { |
---|
133 | H[i][j] = 0.0; |
---|
134 | } |
---|
135 | } |
---|
136 | } |
---|
137 | return X; |
---|
138 | } |
---|
139 | |
---|
140 | /** |
---|
141 | * Return the upper triangular factor |
---|
142 | * @return R |
---|
143 | */ |
---|
144 | public Matrix getR() { |
---|
145 | Matrix X = new Matrix(n,n); |
---|
146 | double[][] R = X.getArray(); |
---|
147 | for (int i = 0; i < n; i++) { |
---|
148 | for (int j = 0; j < n; j++) { |
---|
149 | if (i < j) { |
---|
150 | R[i][j] = QR[i][j]; |
---|
151 | } else if (i == j) { |
---|
152 | R[i][j] = Rdiag[i]; |
---|
153 | } else { |
---|
154 | R[i][j] = 0.0; |
---|
155 | } |
---|
156 | } |
---|
157 | } |
---|
158 | return X; |
---|
159 | } |
---|
160 | |
---|
161 | /** |
---|
162 | * Generate and return the (economy-sized) orthogonal factor |
---|
163 | * @return Q |
---|
164 | */ |
---|
165 | public Matrix getQ() { |
---|
166 | Matrix X = new Matrix(m,n); |
---|
167 | double[][] Q = X.getArray(); |
---|
168 | for (int k = n-1; k >= 0; k--) { |
---|
169 | for (int i = 0; i < m; i++) { |
---|
170 | Q[i][k] = 0.0; |
---|
171 | } |
---|
172 | Q[k][k] = 1.0; |
---|
173 | for (int j = k; j < n; j++) { |
---|
174 | if (QR[k][k] != 0) { |
---|
175 | double s = 0.0; |
---|
176 | for (int i = k; i < m; i++) { |
---|
177 | s += QR[i][k]*Q[i][j]; |
---|
178 | } |
---|
179 | s = -s/QR[k][k]; |
---|
180 | for (int i = k; i < m; i++) { |
---|
181 | Q[i][j] += s*QR[i][k]; |
---|
182 | } |
---|
183 | } |
---|
184 | } |
---|
185 | } |
---|
186 | return X; |
---|
187 | } |
---|
188 | |
---|
189 | /** |
---|
190 | * Least squares solution of A*X = B |
---|
191 | * @param B A Matrix with as many rows as A and any number of columns. |
---|
192 | * @return X that minimizes the two norm of Q*R*X-B. |
---|
193 | * @exception IllegalArgumentException Matrix row dimensions must agree. |
---|
194 | * @exception RuntimeException Matrix is rank deficient. |
---|
195 | */ |
---|
196 | public Matrix solve(Matrix B) { |
---|
197 | if (B.getRowDimension() != m) { |
---|
198 | throw new IllegalArgumentException("Matrix row dimensions must agree."); |
---|
199 | } |
---|
200 | if (!this.isFullRank()) { |
---|
201 | throw new RuntimeException("Matrix is rank deficient."); |
---|
202 | } |
---|
203 | |
---|
204 | // Copy right hand side |
---|
205 | int nx = B.getColumnDimension(); |
---|
206 | double[][] X = B.getArrayCopy(); |
---|
207 | |
---|
208 | // Compute Y = transpose(Q)*B |
---|
209 | for (int k = 0; k < n; k++) { |
---|
210 | for (int j = 0; j < nx; j++) { |
---|
211 | double s = 0.0; |
---|
212 | for (int i = k; i < m; i++) { |
---|
213 | s += QR[i][k]*X[i][j]; |
---|
214 | } |
---|
215 | s = -s/QR[k][k]; |
---|
216 | for (int i = k; i < m; i++) { |
---|
217 | X[i][j] += s*QR[i][k]; |
---|
218 | } |
---|
219 | } |
---|
220 | } |
---|
221 | // Solve R*X = Y; |
---|
222 | for (int k = n-1; k >= 0; k--) { |
---|
223 | for (int j = 0; j < nx; j++) { |
---|
224 | X[k][j] /= Rdiag[k]; |
---|
225 | } |
---|
226 | for (int i = 0; i < k; i++) { |
---|
227 | for (int j = 0; j < nx; j++) { |
---|
228 | X[i][j] -= X[k][j]*QR[i][k]; |
---|
229 | } |
---|
230 | } |
---|
231 | } |
---|
232 | return (new Matrix(X,n,nx).getMatrix(0,n-1,0,nx-1)); |
---|
233 | } |
---|
234 | |
---|
235 | /** |
---|
236 | * Returns the revision string. |
---|
237 | * |
---|
238 | * @return the revision |
---|
239 | */ |
---|
240 | public String getRevision() { |
---|
241 | return RevisionUtils.extract("$Revision: 5953 $"); |
---|
242 | } |
---|
243 | } |
---|