root / tmp / org.txm.analec.rcp / src matt / JamaPlus / SingularValueDecomposition.java @ 1898
Historique | Voir | Annoter | Télécharger (16,18 ko)
1 |
package JamaPlus; |
---|---|
2 |
import JamaPlus.util.*; |
3 |
|
4 |
/** Singular Value Decomposition.
|
5 |
<P>
|
6 |
For an m-by-n matrix A with m >= n, the singular value decomposition is
|
7 |
an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and
|
8 |
an n-by-n orthogonal matrix V so that A = U*S*V'.
|
9 |
<P>
|
10 |
The singular values, sigma[k] = S[k][k], are ordered so that
|
11 |
sigma[0] >= sigma[1] >= ... >= sigma[n-1].
|
12 |
<P>
|
13 |
The singular value decompostion always exists, so the constructor will
|
14 |
never fail. The matrix condition number and the effective numerical
|
15 |
rank can be computed from this decomposition.
|
16 |
*/
|
17 |
|
18 |
public class SingularValueDecomposition implements java.io.Serializable { |
19 |
|
20 |
/* ------------------------
|
21 |
Class variables
|
22 |
* ------------------------ */
|
23 |
|
24 |
/** Arrays for internal storage of U and V.
|
25 |
@serial internal storage of U.
|
26 |
@serial internal storage of V.
|
27 |
*/
|
28 |
private double[][] U, V; |
29 |
|
30 |
/** Array for internal storage of singular values.
|
31 |
@serial internal storage of singular values.
|
32 |
*/
|
33 |
private double[] s; |
34 |
|
35 |
/** Row and column dimensions.
|
36 |
@serial row dimension.
|
37 |
@serial column dimension.
|
38 |
*/
|
39 |
private int m, n; |
40 |
|
41 |
private boolean transpose; // pour éviter les bugs quand m<n (cf.ci-dessous) |
42 |
|
43 |
/* ------------------------
|
44 |
Constructor
|
45 |
* ------------------------ */
|
46 |
|
47 |
/** Construct the singular value decomposition
|
48 |
@param A Rectangular matrix
|
49 |
@return Structure to access U, S and V.
|
50 |
*/
|
51 |
|
52 |
public SingularValueDecomposition (Matrix Arg) {
|
53 |
|
54 |
// Derived from LINPACK code.
|
55 |
// Initialize.
|
56 |
/* Apparently the failing cases are only a proper subset of (m<n),
|
57 |
so let's not throw error. Correct fix to come later?
|
58 |
if (m<n) {
|
59 |
throw new IllegalArgumentException("Jama SVD only works for m >= n"); }
|
60 |
*/
|
61 |
/*
|
62 |
* Une façon très simple de contourner le problème ci-dessus :
|
63 |
* si m < n on transpose A
|
64 |
* (ce qui revient à échanger U et V, S restant inchangé)
|
65 |
*/
|
66 |
transpose = Arg.getColumnDimension()>Arg.getRowDimension(); |
67 |
Matrix M = transpose ? Arg.transpose() : Arg.copy(); |
68 |
|
69 |
double[][] A = M.getArray(); |
70 |
m = M.getRowDimension(); |
71 |
n = M.getColumnDimension(); |
72 |
|
73 |
s = new double [n]; |
74 |
U = new double [m][n]; |
75 |
V = new double [n][n]; |
76 |
double[] e = new double [n]; |
77 |
double[] work = new double [m]; |
78 |
boolean wantu = true; |
79 |
boolean wantv = true; |
80 |
|
81 |
// Reduce A to bidiagonal form, storing the diagonal elements
|
82 |
// in s and the super-diagonal elements in e.
|
83 |
|
84 |
int nct = Math.min(m-1,n); |
85 |
int nrt = Math.max(0,Math.min(n-2,m)); |
86 |
for (int k = 0; k < Math.max(nct,nrt); k++) { |
87 |
if (k < nct) {
|
88 |
|
89 |
// Compute the transformation for the k-th column and
|
90 |
// place the k-th diagonal in s[k].
|
91 |
// Compute 2-norm of k-th column without under/overflow.
|
92 |
s[k] = 0;
|
93 |
for (int i = k; i < m; i++) { |
94 |
s[k] = Math.hypot(s[k],A[i][k]);
|
95 |
} |
96 |
if (s[k] != 0.0) { |
97 |
if (A[k][k] < 0.0) { |
98 |
s[k] = -s[k]; |
99 |
} |
100 |
for (int i = k; i < m; i++) { |
101 |
A[i][k] /= s[k]; |
102 |
} |
103 |
A[k][k] += 1.0;
|
104 |
} |
105 |
s[k] = -s[k]; |
106 |
} |
107 |
for (int j = k+1; j < n; j++) { |
108 |
if ((k < nct) & (s[k] != 0.0)) { |
109 |
|
110 |
// Apply the transformation.
|
111 |
|
112 |
double t = 0; |
113 |
for (int i = k; i < m; i++) { |
114 |
t += A[i][k]*A[i][j]; |
115 |
} |
116 |
t = -t/A[k][k]; |
117 |
for (int i = k; i < m; i++) { |
118 |
A[i][j] += t*A[i][k]; |
119 |
} |
120 |
} |
121 |
|
122 |
// Place the k-th row of A into e for the
|
123 |
// subsequent calculation of the row transformation.
|
124 |
|
125 |
e[j] = A[k][j]; |
126 |
} |
127 |
if (wantu & (k < nct)) {
|
128 |
|
129 |
// Place the transformation in U for subsequent back
|
130 |
// multiplication.
|
131 |
|
132 |
for (int i = k; i < m; i++) { |
133 |
U[i][k] = A[i][k]; |
134 |
} |
135 |
} |
136 |
if (k < nrt) {
|
137 |
|
138 |
// Compute the k-th row transformation and place the
|
139 |
// k-th super-diagonal in e[k].
|
140 |
// Compute 2-norm without under/overflow.
|
141 |
e[k] = 0;
|
142 |
for (int i = k+1; i < n; i++) { |
143 |
e[k] = Math.hypot(e[k],e[i]);
|
144 |
} |
145 |
if (e[k] != 0.0) { |
146 |
if (e[k+1] < 0.0) { |
147 |
e[k] = -e[k]; |
148 |
} |
149 |
for (int i = k+1; i < n; i++) { |
150 |
e[i] /= e[k]; |
151 |
} |
152 |
e[k+1] += 1.0; |
153 |
} |
154 |
e[k] = -e[k]; |
155 |
if ((k+1 < m) & (e[k] != 0.0)) { |
156 |
|
157 |
// Apply the transformation.
|
158 |
|
159 |
for (int i = k+1; i < m; i++) { |
160 |
work[i] = 0.0;
|
161 |
} |
162 |
for (int j = k+1; j < n; j++) { |
163 |
for (int i = k+1; i < m; i++) { |
164 |
work[i] += e[j]*A[i][j]; |
165 |
} |
166 |
} |
167 |
for (int j = k+1; j < n; j++) { |
168 |
double t = -e[j]/e[k+1]; |
169 |
for (int i = k+1; i < m; i++) { |
170 |
A[i][j] += t*work[i]; |
171 |
} |
172 |
} |
173 |
} |
174 |
if (wantv) {
|
175 |
|
176 |
// Place the transformation in V for subsequent
|
177 |
// back multiplication.
|
178 |
|
179 |
for (int i = k+1; i < n; i++) { |
180 |
V[i][k] = e[i]; |
181 |
} |
182 |
} |
183 |
} |
184 |
} |
185 |
|
186 |
// Set up the final bidiagonal matrix or order p.
|
187 |
|
188 |
int p = Math.min(n,m+1); |
189 |
if (nct < n) {
|
190 |
s[nct] = A[nct][nct]; |
191 |
} |
192 |
if (m < p) {
|
193 |
s[p-1] = 0.0; |
194 |
} |
195 |
if (nrt+1 < p) { |
196 |
e[nrt] = A[nrt][p-1];
|
197 |
} |
198 |
e[p-1] = 0.0; |
199 |
|
200 |
// If required, generate U.
|
201 |
|
202 |
if (wantu) {
|
203 |
for (int j = nct; j < n; j++) { |
204 |
for (int i = 0; i < m; i++) { |
205 |
U[i][j] = 0.0;
|
206 |
} |
207 |
U[j][j] = 1.0;
|
208 |
} |
209 |
for (int k = nct-1; k >= 0; k--) { |
210 |
if (s[k] != 0.0) { |
211 |
for (int j = k+1; j < n; j++) { |
212 |
double t = 0; |
213 |
for (int i = k; i < m; i++) { |
214 |
t += U[i][k]*U[i][j]; |
215 |
} |
216 |
t = -t/U[k][k]; |
217 |
for (int i = k; i < m; i++) { |
218 |
U[i][j] += t*U[i][k]; |
219 |
} |
220 |
} |
221 |
for (int i = k; i < m; i++ ) { |
222 |
U[i][k] = -U[i][k]; |
223 |
} |
224 |
U[k][k] = 1.0 + U[k][k];
|
225 |
for (int i = 0; i < k-1; i++) { |
226 |
U[i][k] = 0.0;
|
227 |
} |
228 |
} else {
|
229 |
for (int i = 0; i < m; i++) { |
230 |
U[i][k] = 0.0;
|
231 |
} |
232 |
U[k][k] = 1.0;
|
233 |
} |
234 |
} |
235 |
} |
236 |
|
237 |
// If required, generate V.
|
238 |
|
239 |
if (wantv) {
|
240 |
for (int k = n-1; k >= 0; k--) { |
241 |
if ((k < nrt) & (e[k] != 0.0)) { |
242 |
for (int j = k+1; j < n; j++) { |
243 |
double t = 0; |
244 |
for (int i = k+1; i < n; i++) { |
245 |
t += V[i][k]*V[i][j]; |
246 |
} |
247 |
t = -t/V[k+1][k];
|
248 |
for (int i = k+1; i < n; i++) { |
249 |
V[i][j] += t*V[i][k]; |
250 |
} |
251 |
} |
252 |
} |
253 |
for (int i = 0; i < n; i++) { |
254 |
V[i][k] = 0.0;
|
255 |
} |
256 |
V[k][k] = 1.0;
|
257 |
} |
258 |
} |
259 |
|
260 |
// Main iteration loop for the singular values.
|
261 |
|
262 |
int pp = p-1; |
263 |
int iter = 0; |
264 |
double eps = Math.pow(2.0,-52.0); |
265 |
double tiny = Math.pow(2.0,-966.0); |
266 |
while (p > 0) { |
267 |
int k,kase;
|
268 |
|
269 |
// Here is where a test for too many iterations would go.
|
270 |
|
271 |
// This section of the program inspects for
|
272 |
// negligible elements in the s and e arrays. On
|
273 |
// completion the variables kase and k are set as follows.
|
274 |
|
275 |
// kase = 1 if s(p) and e[k-1] are negligible and k<p
|
276 |
// kase = 2 if s(k) is negligible and k<p
|
277 |
// kase = 3 if e[k-1] is negligible, k<p, and
|
278 |
// s(k), ..., s(p) are not negligible (qr step).
|
279 |
// kase = 4 if e(p-1) is negligible (convergence).
|
280 |
|
281 |
for (k = p-2; k >= -1; k--) { |
282 |
if (k == -1) { |
283 |
break;
|
284 |
} |
285 |
if (Math.abs(e[k]) <= |
286 |
tiny + eps*(Math.abs(s[k]) + Math.abs(s[k+1]))) { |
287 |
e[k] = 0.0;
|
288 |
break;
|
289 |
} |
290 |
} |
291 |
if (k == p-2) { |
292 |
kase = 4;
|
293 |
} else {
|
294 |
int ks;
|
295 |
for (ks = p-1; ks >= k; ks--) { |
296 |
if (ks == k) {
|
297 |
break;
|
298 |
} |
299 |
double t = (ks != p ? Math.abs(e[ks]) : 0.) + |
300 |
(ks != k+1 ? Math.abs(e[ks-1]) : 0.); |
301 |
if (Math.abs(s[ks]) <= tiny + eps*t) { |
302 |
s[ks] = 0.0;
|
303 |
break;
|
304 |
} |
305 |
} |
306 |
if (ks == k) {
|
307 |
kase = 3;
|
308 |
} else if (ks == p-1) { |
309 |
kase = 1;
|
310 |
} else {
|
311 |
kase = 2;
|
312 |
k = ks; |
313 |
} |
314 |
} |
315 |
k++; |
316 |
|
317 |
// Perform the task indicated by kase.
|
318 |
|
319 |
switch (kase) {
|
320 |
|
321 |
// Deflate negligible s(p).
|
322 |
|
323 |
case 1: { |
324 |
double f = e[p-2]; |
325 |
e[p-2] = 0.0; |
326 |
for (int j = p-2; j >= k; j--) { |
327 |
double t = Math.hypot(s[j],f); |
328 |
double cs = s[j]/t;
|
329 |
double sn = f/t;
|
330 |
s[j] = t; |
331 |
if (j != k) {
|
332 |
f = -sn*e[j-1];
|
333 |
e[j-1] = cs*e[j-1]; |
334 |
} |
335 |
if (wantv) {
|
336 |
for (int i = 0; i < n; i++) { |
337 |
t = cs*V[i][j] + sn*V[i][p-1];
|
338 |
V[i][p-1] = -sn*V[i][j] + cs*V[i][p-1]; |
339 |
V[i][j] = t; |
340 |
} |
341 |
} |
342 |
} |
343 |
} |
344 |
break;
|
345 |
|
346 |
// Split at negligible s(k).
|
347 |
|
348 |
case 2: { |
349 |
double f = e[k-1]; |
350 |
e[k-1] = 0.0; |
351 |
for (int j = k; j < p; j++) { |
352 |
double t = Math.hypot(s[j],f); |
353 |
double cs = s[j]/t;
|
354 |
double sn = f/t;
|
355 |
s[j] = t; |
356 |
f = -sn*e[j]; |
357 |
e[j] = cs*e[j]; |
358 |
if (wantu) {
|
359 |
for (int i = 0; i < m; i++) { |
360 |
t = cs*U[i][j] + sn*U[i][k-1];
|
361 |
U[i][k-1] = -sn*U[i][j] + cs*U[i][k-1]; |
362 |
U[i][j] = t; |
363 |
} |
364 |
} |
365 |
} |
366 |
} |
367 |
break;
|
368 |
|
369 |
// Perform one qr step.
|
370 |
|
371 |
case 3: { |
372 |
|
373 |
// Calculate the shift.
|
374 |
|
375 |
double scale = Math.max(Math.max(Math.max(Math.max( |
376 |
Math.abs(s[p-1]),Math.abs(s[p-2])),Math.abs(e[p-2])), |
377 |
Math.abs(s[k])),Math.abs(e[k])); |
378 |
double sp = s[p-1]/scale; |
379 |
double spm1 = s[p-2]/scale; |
380 |
double epm1 = e[p-2]/scale; |
381 |
double sk = s[k]/scale;
|
382 |
double ek = e[k]/scale;
|
383 |
double b = ((spm1 + sp)*(spm1 - sp) + epm1*epm1)/2.0; |
384 |
double c = (sp*epm1)*(sp*epm1);
|
385 |
double shift = 0.0; |
386 |
if ((b != 0.0) | (c != 0.0)) { |
387 |
shift = Math.sqrt(b*b + c);
|
388 |
if (b < 0.0) { |
389 |
shift = -shift; |
390 |
} |
391 |
shift = c/(b + shift); |
392 |
} |
393 |
double f = (sk + sp)*(sk - sp) + shift;
|
394 |
double g = sk*ek;
|
395 |
|
396 |
// Chase zeros.
|
397 |
|
398 |
for (int j = k; j < p-1; j++) { |
399 |
double t = Math.hypot(f,g); |
400 |
double cs = f/t;
|
401 |
double sn = g/t;
|
402 |
if (j != k) {
|
403 |
e[j-1] = t;
|
404 |
} |
405 |
f = cs*s[j] + sn*e[j]; |
406 |
e[j] = cs*e[j] - sn*s[j]; |
407 |
g = sn*s[j+1];
|
408 |
s[j+1] = cs*s[j+1]; |
409 |
if (wantv) {
|
410 |
for (int i = 0; i < n; i++) { |
411 |
t = cs*V[i][j] + sn*V[i][j+1];
|
412 |
V[i][j+1] = -sn*V[i][j] + cs*V[i][j+1]; |
413 |
V[i][j] = t; |
414 |
} |
415 |
} |
416 |
t = Math.hypot(f,g);
|
417 |
cs = f/t; |
418 |
sn = g/t; |
419 |
s[j] = t; |
420 |
f = cs*e[j] + sn*s[j+1];
|
421 |
s[j+1] = -sn*e[j] + cs*s[j+1]; |
422 |
g = sn*e[j+1];
|
423 |
e[j+1] = cs*e[j+1]; |
424 |
if (wantu && (j < m-1)) { |
425 |
for (int i = 0; i < m; i++) { |
426 |
t = cs*U[i][j] + sn*U[i][j+1];
|
427 |
U[i][j+1] = -sn*U[i][j] + cs*U[i][j+1]; |
428 |
U[i][j] = t; |
429 |
} |
430 |
} |
431 |
} |
432 |
e[p-2] = f;
|
433 |
iter = iter + 1;
|
434 |
} |
435 |
break;
|
436 |
|
437 |
// Convergence.
|
438 |
|
439 |
case 4: { |
440 |
|
441 |
// Make the singular values positive.
|
442 |
|
443 |
if (s[k] <= 0.0) { |
444 |
s[k] = (s[k] < 0.0 ? -s[k] : 0.0); |
445 |
if (wantv) {
|
446 |
for (int i = 0; i <= pp; i++) { |
447 |
V[i][k] = -V[i][k]; |
448 |
} |
449 |
} |
450 |
} |
451 |
|
452 |
// Order the singular values.
|
453 |
|
454 |
while (k < pp) {
|
455 |
if (s[k] >= s[k+1]) { |
456 |
break;
|
457 |
} |
458 |
double t = s[k];
|
459 |
s[k] = s[k+1];
|
460 |
s[k+1] = t;
|
461 |
if (wantv && (k < n-1)) { |
462 |
for (int i = 0; i < n; i++) { |
463 |
t = V[i][k+1]; V[i][k+1] = V[i][k]; V[i][k] = t; |
464 |
} |
465 |
} |
466 |
if (wantu && (k < m-1)) { |
467 |
for (int i = 0; i < m; i++) { |
468 |
t = U[i][k+1]; U[i][k+1] = U[i][k]; U[i][k] = t; |
469 |
} |
470 |
} |
471 |
k++; |
472 |
} |
473 |
iter = 0;
|
474 |
p--; |
475 |
} |
476 |
break;
|
477 |
} |
478 |
} |
479 |
} |
480 |
|
481 |
/* ------------------------
|
482 |
Public Methods
|
483 |
* ------------------------ */
|
484 |
|
485 |
/** Return the left singular vectors
|
486 |
@return U
|
487 |
*/
|
488 |
|
489 |
public Matrix getU () {
|
490 |
return transpose ? new Matrix(V,n,n) : new Matrix(U,m,n); |
491 |
} |
492 |
|
493 |
/** Return the right singular vectors
|
494 |
@return V
|
495 |
*/
|
496 |
|
497 |
public Matrix getV () {
|
498 |
return transpose ? new Matrix(U,m,n) : new Matrix(V,n,n); |
499 |
} |
500 |
|
501 |
/** Return the one-dimensional array of singular values
|
502 |
@return diagonal of S.
|
503 |
*/
|
504 |
|
505 |
public double[] getSingularValues () { |
506 |
return s;
|
507 |
} |
508 |
|
509 |
/** Return the diagonal matrix of singular values
|
510 |
@return S
|
511 |
*/
|
512 |
|
513 |
public Matrix getS () {
|
514 |
Matrix X = new Matrix(n,n);
|
515 |
double[][] S = X.getArray(); |
516 |
for (int i = 0; i < n; i++) { |
517 |
for (int j = 0; j < n; j++) { |
518 |
S[i][j] = 0.0;
|
519 |
} |
520 |
S[i][i] = this.s[i];
|
521 |
} |
522 |
return X;
|
523 |
} |
524 |
|
525 |
/** Two norm
|
526 |
@return max(S)
|
527 |
*/
|
528 |
|
529 |
public double norm2 () { |
530 |
return s[0]; |
531 |
} |
532 |
|
533 |
/** Two norm condition number
|
534 |
@return max(S)/min(S)
|
535 |
*/
|
536 |
|
537 |
public double cond () { |
538 |
return s[0]/s[Math.min(m,n)-1]; |
539 |
} |
540 |
|
541 |
/** Effective numerical matrix rank
|
542 |
@return Number of nonnegligible singular values.
|
543 |
*/
|
544 |
|
545 |
public int rank () { |
546 |
double eps = Math.pow(2.0,-52.0); |
547 |
double tol = Math.max(m,n)*s[0]*eps; |
548 |
int r = 0; |
549 |
for (int i = 0; i < s.length; i++) { |
550 |
if (s[i] > tol) {
|
551 |
r++; |
552 |
} |
553 |
} |
554 |
return r;
|
555 |
} |
556 |
} |