1 | /*
|
---|
2 | * Licensed to the Apache Software Foundation (ASF) under one or more
|
---|
3 | * contributor license agreements. See the NOTICE file distributed with
|
---|
4 | * this work for additional information regarding copyright ownership.
|
---|
5 | * The ASF licenses this file to You under the Apache License, Version 2.0
|
---|
6 | * (the "License"); you may not use this file except in compliance with
|
---|
7 | * the License. You may obtain a copy of the License at
|
---|
8 | *
|
---|
9 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
10 | *
|
---|
11 | * Unless required by applicable law or agreed to in writing, software
|
---|
12 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
14 | * See the License for the specific language governing permissions and
|
---|
15 | * limitations under the License.
|
---|
16 | */
|
---|
17 |
|
---|
18 | package agents.org.apache.commons.math.linear;
|
---|
19 |
|
---|
20 | import java.util.Arrays;
|
---|
21 |
|
---|
22 | import agents.org.apache.commons.math.MathRuntimeException;
|
---|
23 | import agents.org.apache.commons.math.exception.util.LocalizedFormats;
|
---|
24 | import agents.org.apache.commons.math.util.FastMath;
|
---|
25 |
|
---|
26 |
|
---|
27 | /**
|
---|
28 | * Calculates the QR-decomposition of a matrix.
|
---|
29 | * <p>The QR-decomposition of a matrix A consists of two matrices Q and R
|
---|
30 | * that satisfy: A = QR, Q is orthogonal (Q<sup>T</sup>Q = I), and R is
|
---|
31 | * upper triangular. If A is m×n, Q is m×m and R m×n.</p>
|
---|
32 | * <p>This class compute the decomposition using Householder reflectors.</p>
|
---|
33 | * <p>For efficiency purposes, the decomposition in packed form is transposed.
|
---|
34 | * This allows inner loop to iterate inside rows, which is much more cache-efficient
|
---|
35 | * in Java.</p>
|
---|
36 | *
|
---|
37 | * @see <a href="http://mathworld.wolfram.com/QRDecomposition.html">MathWorld</a>
|
---|
38 | * @see <a href="http://en.wikipedia.org/wiki/QR_decomposition">Wikipedia</a>
|
---|
39 | *
|
---|
40 | * @version $Revision: 990655 $ $Date: 2010-08-29 23:49:40 +0200 (dim. 29 août 2010) $
|
---|
41 | * @since 1.2
|
---|
42 | */
|
---|
43 | public class QRDecompositionImpl implements QRDecomposition {
|
---|
44 |
|
---|
45 | /**
|
---|
46 | * A packed TRANSPOSED representation of the QR decomposition.
|
---|
47 | * <p>The elements BELOW the diagonal are the elements of the UPPER triangular
|
---|
48 | * matrix R, and the rows ABOVE the diagonal are the Householder reflector vectors
|
---|
49 | * from which an explicit form of Q can be recomputed if desired.</p>
|
---|
50 | */
|
---|
51 | private double[][] qrt;
|
---|
52 |
|
---|
53 | /** The diagonal elements of R. */
|
---|
54 | private double[] rDiag;
|
---|
55 |
|
---|
56 | /** Cached value of Q. */
|
---|
57 | private RealMatrix cachedQ;
|
---|
58 |
|
---|
59 | /** Cached value of QT. */
|
---|
60 | private RealMatrix cachedQT;
|
---|
61 |
|
---|
62 | /** Cached value of R. */
|
---|
63 | private RealMatrix cachedR;
|
---|
64 |
|
---|
65 | /** Cached value of H. */
|
---|
66 | private RealMatrix cachedH;
|
---|
67 |
|
---|
68 | /**
|
---|
69 | * Calculates the QR-decomposition of the given matrix.
|
---|
70 | * @param matrix The matrix to decompose.
|
---|
71 | */
|
---|
72 | public QRDecompositionImpl(RealMatrix matrix) {
|
---|
73 |
|
---|
74 | final int m = matrix.getRowDimension();
|
---|
75 | final int n = matrix.getColumnDimension();
|
---|
76 | qrt = matrix.transpose().getData();
|
---|
77 | rDiag = new double[FastMath.min(m, n)];
|
---|
78 | cachedQ = null;
|
---|
79 | cachedQT = null;
|
---|
80 | cachedR = null;
|
---|
81 | cachedH = null;
|
---|
82 |
|
---|
83 | /*
|
---|
84 | * The QR decomposition of a matrix A is calculated using Householder
|
---|
85 | * reflectors by repeating the following operations to each minor
|
---|
86 | * A(minor,minor) of A:
|
---|
87 | */
|
---|
88 | for (int minor = 0; minor < FastMath.min(m, n); minor++) {
|
---|
89 |
|
---|
90 | final double[] qrtMinor = qrt[minor];
|
---|
91 |
|
---|
92 | /*
|
---|
93 | * Let x be the first column of the minor, and a^2 = |x|^2.
|
---|
94 | * x will be in the positions qr[minor][minor] through qr[m][minor].
|
---|
95 | * The first column of the transformed minor will be (a,0,0,..)'
|
---|
96 | * The sign of a is chosen to be opposite to the sign of the first
|
---|
97 | * component of x. Let's find a:
|
---|
98 | */
|
---|
99 | double xNormSqr = 0;
|
---|
100 | for (int row = minor; row < m; row++) {
|
---|
101 | final double c = qrtMinor[row];
|
---|
102 | xNormSqr += c * c;
|
---|
103 | }
|
---|
104 | final double a = (qrtMinor[minor] > 0) ? -FastMath.sqrt(xNormSqr) : FastMath.sqrt(xNormSqr);
|
---|
105 | rDiag[minor] = a;
|
---|
106 |
|
---|
107 | if (a != 0.0) {
|
---|
108 |
|
---|
109 | /*
|
---|
110 | * Calculate the normalized reflection vector v and transform
|
---|
111 | * the first column. We know the norm of v beforehand: v = x-ae
|
---|
112 | * so |v|^2 = <x-ae,x-ae> = <x,x>-2a<x,e>+a^2<e,e> =
|
---|
113 | * a^2+a^2-2a<x,e> = 2a*(a - <x,e>).
|
---|
114 | * Here <x, e> is now qr[minor][minor].
|
---|
115 | * v = x-ae is stored in the column at qr:
|
---|
116 | */
|
---|
117 | qrtMinor[minor] -= a; // now |v|^2 = -2a*(qr[minor][minor])
|
---|
118 |
|
---|
119 | /*
|
---|
120 | * Transform the rest of the columns of the minor:
|
---|
121 | * They will be transformed by the matrix H = I-2vv'/|v|^2.
|
---|
122 | * If x is a column vector of the minor, then
|
---|
123 | * Hx = (I-2vv'/|v|^2)x = x-2vv'x/|v|^2 = x - 2<x,v>/|v|^2 v.
|
---|
124 | * Therefore the transformation is easily calculated by
|
---|
125 | * subtracting the column vector (2<x,v>/|v|^2)v from x.
|
---|
126 | *
|
---|
127 | * Let 2<x,v>/|v|^2 = alpha. From above we have
|
---|
128 | * |v|^2 = -2a*(qr[minor][minor]), so
|
---|
129 | * alpha = -<x,v>/(a*qr[minor][minor])
|
---|
130 | */
|
---|
131 | for (int col = minor+1; col < n; col++) {
|
---|
132 | final double[] qrtCol = qrt[col];
|
---|
133 | double alpha = 0;
|
---|
134 | for (int row = minor; row < m; row++) {
|
---|
135 | alpha -= qrtCol[row] * qrtMinor[row];
|
---|
136 | }
|
---|
137 | alpha /= a * qrtMinor[minor];
|
---|
138 |
|
---|
139 | // Subtract the column vector alpha*v from x.
|
---|
140 | for (int row = minor; row < m; row++) {
|
---|
141 | qrtCol[row] -= alpha * qrtMinor[row];
|
---|
142 | }
|
---|
143 | }
|
---|
144 | }
|
---|
145 | }
|
---|
146 | }
|
---|
147 |
|
---|
148 | /** {@inheritDoc} */
|
---|
149 | public RealMatrix getR() {
|
---|
150 |
|
---|
151 | if (cachedR == null) {
|
---|
152 |
|
---|
153 | // R is supposed to be m x n
|
---|
154 | final int n = qrt.length;
|
---|
155 | final int m = qrt[0].length;
|
---|
156 | cachedR = MatrixUtils.createRealMatrix(m, n);
|
---|
157 |
|
---|
158 | // copy the diagonal from rDiag and the upper triangle of qr
|
---|
159 | for (int row = FastMath.min(m, n) - 1; row >= 0; row--) {
|
---|
160 | cachedR.setEntry(row, row, rDiag[row]);
|
---|
161 | for (int col = row + 1; col < n; col++) {
|
---|
162 | cachedR.setEntry(row, col, qrt[col][row]);
|
---|
163 | }
|
---|
164 | }
|
---|
165 |
|
---|
166 | }
|
---|
167 |
|
---|
168 | // return the cached matrix
|
---|
169 | return cachedR;
|
---|
170 |
|
---|
171 | }
|
---|
172 |
|
---|
173 | /** {@inheritDoc} */
|
---|
174 | public RealMatrix getQ() {
|
---|
175 | if (cachedQ == null) {
|
---|
176 | cachedQ = getQT().transpose();
|
---|
177 | }
|
---|
178 | return cachedQ;
|
---|
179 | }
|
---|
180 |
|
---|
181 | /** {@inheritDoc} */
|
---|
182 | public RealMatrix getQT() {
|
---|
183 |
|
---|
184 | if (cachedQT == null) {
|
---|
185 |
|
---|
186 | // QT is supposed to be m x m
|
---|
187 | final int n = qrt.length;
|
---|
188 | final int m = qrt[0].length;
|
---|
189 | cachedQT = MatrixUtils.createRealMatrix(m, m);
|
---|
190 |
|
---|
191 | /*
|
---|
192 | * Q = Q1 Q2 ... Q_m, so Q is formed by first constructing Q_m and then
|
---|
193 | * applying the Householder transformations Q_(m-1),Q_(m-2),...,Q1 in
|
---|
194 | * succession to the result
|
---|
195 | */
|
---|
196 | for (int minor = m - 1; minor >= FastMath.min(m, n); minor--) {
|
---|
197 | cachedQT.setEntry(minor, minor, 1.0);
|
---|
198 | }
|
---|
199 |
|
---|
200 | for (int minor = FastMath.min(m, n)-1; minor >= 0; minor--){
|
---|
201 | final double[] qrtMinor = qrt[minor];
|
---|
202 | cachedQT.setEntry(minor, minor, 1.0);
|
---|
203 | if (qrtMinor[minor] != 0.0) {
|
---|
204 | for (int col = minor; col < m; col++) {
|
---|
205 | double alpha = 0;
|
---|
206 | for (int row = minor; row < m; row++) {
|
---|
207 | alpha -= cachedQT.getEntry(col, row) * qrtMinor[row];
|
---|
208 | }
|
---|
209 | alpha /= rDiag[minor] * qrtMinor[minor];
|
---|
210 |
|
---|
211 | for (int row = minor; row < m; row++) {
|
---|
212 | cachedQT.addToEntry(col, row, -alpha * qrtMinor[row]);
|
---|
213 | }
|
---|
214 | }
|
---|
215 | }
|
---|
216 | }
|
---|
217 |
|
---|
218 | }
|
---|
219 |
|
---|
220 | // return the cached matrix
|
---|
221 | return cachedQT;
|
---|
222 |
|
---|
223 | }
|
---|
224 |
|
---|
225 | /** {@inheritDoc} */
|
---|
226 | public RealMatrix getH() {
|
---|
227 |
|
---|
228 | if (cachedH == null) {
|
---|
229 |
|
---|
230 | final int n = qrt.length;
|
---|
231 | final int m = qrt[0].length;
|
---|
232 | cachedH = MatrixUtils.createRealMatrix(m, n);
|
---|
233 | for (int i = 0; i < m; ++i) {
|
---|
234 | for (int j = 0; j < FastMath.min(i + 1, n); ++j) {
|
---|
235 | cachedH.setEntry(i, j, qrt[j][i] / -rDiag[j]);
|
---|
236 | }
|
---|
237 | }
|
---|
238 |
|
---|
239 | }
|
---|
240 |
|
---|
241 | // return the cached matrix
|
---|
242 | return cachedH;
|
---|
243 |
|
---|
244 | }
|
---|
245 |
|
---|
246 | /** {@inheritDoc} */
|
---|
247 | public DecompositionSolver getSolver() {
|
---|
248 | return new Solver(qrt, rDiag);
|
---|
249 | }
|
---|
250 |
|
---|
251 | /** Specialized solver. */
|
---|
252 | private static class Solver implements DecompositionSolver {
|
---|
253 |
|
---|
254 | /**
|
---|
255 | * A packed TRANSPOSED representation of the QR decomposition.
|
---|
256 | * <p>The elements BELOW the diagonal are the elements of the UPPER triangular
|
---|
257 | * matrix R, and the rows ABOVE the diagonal are the Householder reflector vectors
|
---|
258 | * from which an explicit form of Q can be recomputed if desired.</p>
|
---|
259 | */
|
---|
260 | private final double[][] qrt;
|
---|
261 |
|
---|
262 | /** The diagonal elements of R. */
|
---|
263 | private final double[] rDiag;
|
---|
264 |
|
---|
265 | /**
|
---|
266 | * Build a solver from decomposed matrix.
|
---|
267 | * @param qrt packed TRANSPOSED representation of the QR decomposition
|
---|
268 | * @param rDiag diagonal elements of R
|
---|
269 | */
|
---|
270 | private Solver(final double[][] qrt, final double[] rDiag) {
|
---|
271 | this.qrt = qrt;
|
---|
272 | this.rDiag = rDiag;
|
---|
273 | }
|
---|
274 |
|
---|
275 | /** {@inheritDoc} */
|
---|
276 | public boolean isNonSingular() {
|
---|
277 |
|
---|
278 | for (double diag : rDiag) {
|
---|
279 | if (diag == 0) {
|
---|
280 | return false;
|
---|
281 | }
|
---|
282 | }
|
---|
283 | return true;
|
---|
284 |
|
---|
285 | }
|
---|
286 |
|
---|
287 | /** {@inheritDoc} */
|
---|
288 | public double[] solve(double[] b)
|
---|
289 | throws IllegalArgumentException, InvalidMatrixException {
|
---|
290 |
|
---|
291 | final int n = qrt.length;
|
---|
292 | final int m = qrt[0].length;
|
---|
293 | if (b.length != m) {
|
---|
294 | throw MathRuntimeException.createIllegalArgumentException(
|
---|
295 | LocalizedFormats.VECTOR_LENGTH_MISMATCH,
|
---|
296 | b.length, m);
|
---|
297 | }
|
---|
298 | if (!isNonSingular()) {
|
---|
299 | throw new SingularMatrixException();
|
---|
300 | }
|
---|
301 |
|
---|
302 | final double[] x = new double[n];
|
---|
303 | final double[] y = b.clone();
|
---|
304 |
|
---|
305 | // apply Householder transforms to solve Q.y = b
|
---|
306 | for (int minor = 0; minor < FastMath.min(m, n); minor++) {
|
---|
307 |
|
---|
308 | final double[] qrtMinor = qrt[minor];
|
---|
309 | double dotProduct = 0;
|
---|
310 | for (int row = minor; row < m; row++) {
|
---|
311 | dotProduct += y[row] * qrtMinor[row];
|
---|
312 | }
|
---|
313 | dotProduct /= rDiag[minor] * qrtMinor[minor];
|
---|
314 |
|
---|
315 | for (int row = minor; row < m; row++) {
|
---|
316 | y[row] += dotProduct * qrtMinor[row];
|
---|
317 | }
|
---|
318 |
|
---|
319 | }
|
---|
320 |
|
---|
321 | // solve triangular system R.x = y
|
---|
322 | for (int row = rDiag.length - 1; row >= 0; --row) {
|
---|
323 | y[row] /= rDiag[row];
|
---|
324 | final double yRow = y[row];
|
---|
325 | final double[] qrtRow = qrt[row];
|
---|
326 | x[row] = yRow;
|
---|
327 | for (int i = 0; i < row; i++) {
|
---|
328 | y[i] -= yRow * qrtRow[i];
|
---|
329 | }
|
---|
330 | }
|
---|
331 |
|
---|
332 | return x;
|
---|
333 |
|
---|
334 | }
|
---|
335 |
|
---|
336 | /** {@inheritDoc} */
|
---|
337 | public RealVector solve(RealVector b)
|
---|
338 | throws IllegalArgumentException, InvalidMatrixException {
|
---|
339 | try {
|
---|
340 | return solve((ArrayRealVector) b);
|
---|
341 | } catch (ClassCastException cce) {
|
---|
342 | return new ArrayRealVector(solve(b.getData()), false);
|
---|
343 | }
|
---|
344 | }
|
---|
345 |
|
---|
346 | /** Solve the linear equation A × X = B.
|
---|
347 | * <p>The A matrix is implicit here. It is </p>
|
---|
348 | * @param b right-hand side of the equation A × X = B
|
---|
349 | * @return a vector X that minimizes the two norm of A × X - B
|
---|
350 | * @throws IllegalArgumentException if matrices dimensions don't match
|
---|
351 | * @throws InvalidMatrixException if decomposed matrix is singular
|
---|
352 | */
|
---|
353 | public ArrayRealVector solve(ArrayRealVector b)
|
---|
354 | throws IllegalArgumentException, InvalidMatrixException {
|
---|
355 | return new ArrayRealVector(solve(b.getDataRef()), false);
|
---|
356 | }
|
---|
357 |
|
---|
358 | /** {@inheritDoc} */
|
---|
359 | public RealMatrix solve(RealMatrix b)
|
---|
360 | throws IllegalArgumentException, InvalidMatrixException {
|
---|
361 |
|
---|
362 | final int n = qrt.length;
|
---|
363 | final int m = qrt[0].length;
|
---|
364 | if (b.getRowDimension() != m) {
|
---|
365 | throw MathRuntimeException.createIllegalArgumentException(
|
---|
366 | LocalizedFormats.DIMENSIONS_MISMATCH_2x2,
|
---|
367 | b.getRowDimension(), b.getColumnDimension(), m, "n");
|
---|
368 | }
|
---|
369 | if (!isNonSingular()) {
|
---|
370 | throw new SingularMatrixException();
|
---|
371 | }
|
---|
372 |
|
---|
373 | final int columns = b.getColumnDimension();
|
---|
374 | final int blockSize = BlockRealMatrix.BLOCK_SIZE;
|
---|
375 | final int cBlocks = (columns + blockSize - 1) / blockSize;
|
---|
376 | final double[][] xBlocks = BlockRealMatrix.createBlocksLayout(n, columns);
|
---|
377 | final double[][] y = new double[b.getRowDimension()][blockSize];
|
---|
378 | final double[] alpha = new double[blockSize];
|
---|
379 |
|
---|
380 | for (int kBlock = 0; kBlock < cBlocks; ++kBlock) {
|
---|
381 | final int kStart = kBlock * blockSize;
|
---|
382 | final int kEnd = FastMath.min(kStart + blockSize, columns);
|
---|
383 | final int kWidth = kEnd - kStart;
|
---|
384 |
|
---|
385 | // get the right hand side vector
|
---|
386 | b.copySubMatrix(0, m - 1, kStart, kEnd - 1, y);
|
---|
387 |
|
---|
388 | // apply Householder transforms to solve Q.y = b
|
---|
389 | for (int minor = 0; minor < FastMath.min(m, n); minor++) {
|
---|
390 | final double[] qrtMinor = qrt[minor];
|
---|
391 | final double factor = 1.0 / (rDiag[minor] * qrtMinor[minor]);
|
---|
392 |
|
---|
393 | Arrays.fill(alpha, 0, kWidth, 0.0);
|
---|
394 | for (int row = minor; row < m; ++row) {
|
---|
395 | final double d = qrtMinor[row];
|
---|
396 | final double[] yRow = y[row];
|
---|
397 | for (int k = 0; k < kWidth; ++k) {
|
---|
398 | alpha[k] += d * yRow[k];
|
---|
399 | }
|
---|
400 | }
|
---|
401 | for (int k = 0; k < kWidth; ++k) {
|
---|
402 | alpha[k] *= factor;
|
---|
403 | }
|
---|
404 |
|
---|
405 | for (int row = minor; row < m; ++row) {
|
---|
406 | final double d = qrtMinor[row];
|
---|
407 | final double[] yRow = y[row];
|
---|
408 | for (int k = 0; k < kWidth; ++k) {
|
---|
409 | yRow[k] += alpha[k] * d;
|
---|
410 | }
|
---|
411 | }
|
---|
412 |
|
---|
413 | }
|
---|
414 |
|
---|
415 | // solve triangular system R.x = y
|
---|
416 | for (int j = rDiag.length - 1; j >= 0; --j) {
|
---|
417 | final int jBlock = j / blockSize;
|
---|
418 | final int jStart = jBlock * blockSize;
|
---|
419 | final double factor = 1.0 / rDiag[j];
|
---|
420 | final double[] yJ = y[j];
|
---|
421 | final double[] xBlock = xBlocks[jBlock * cBlocks + kBlock];
|
---|
422 | int index = (j - jStart) * kWidth;
|
---|
423 | for (int k = 0; k < kWidth; ++k) {
|
---|
424 | yJ[k] *= factor;
|
---|
425 | xBlock[index++] = yJ[k];
|
---|
426 | }
|
---|
427 |
|
---|
428 | final double[] qrtJ = qrt[j];
|
---|
429 | for (int i = 0; i < j; ++i) {
|
---|
430 | final double rIJ = qrtJ[i];
|
---|
431 | final double[] yI = y[i];
|
---|
432 | for (int k = 0; k < kWidth; ++k) {
|
---|
433 | yI[k] -= yJ[k] * rIJ;
|
---|
434 | }
|
---|
435 | }
|
---|
436 |
|
---|
437 | }
|
---|
438 |
|
---|
439 | }
|
---|
440 |
|
---|
441 | return new BlockRealMatrix(n, columns, xBlocks, false);
|
---|
442 |
|
---|
443 | }
|
---|
444 |
|
---|
445 | /** {@inheritDoc} */
|
---|
446 | public RealMatrix getInverse()
|
---|
447 | throws InvalidMatrixException {
|
---|
448 | return solve(MatrixUtils.createRealIdentityMatrix(rDiag.length));
|
---|
449 | }
|
---|
450 |
|
---|
451 | }
|
---|
452 |
|
---|
453 | }
|
---|