OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
SVD.hpp
1#ifndef JAMA_SVD_H
2#define JAMA_SVD_H
3
4#include <algorithm>
5
7// for min(), max() below
8#include <cmath>
9// for abs() below
10
11using std::hypot;
14
15namespace JAMA {
16 /** Singular Value Decomposition.
17 <P>
18 For an m-by-n matrix A with m >= n, the singular value decomposition is
19 an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and
20 an n-by-n orthogonal matrix V so that A = U*S*V'.
21 <P>
22 The singular values, sigma(k) = S(k,k), are ordered so that
23 sigma(0) >= sigma(1) >= ... >= sigma(n-1).
24 <P>
25 The singular value decompostion always exists, so the constructor will
26 never fail. The matrix condition number and the effective numerical
27 rank can be computed from this decomposition.
28
29 <p>
30 (Adapted from JAMA, a Java Matrix Library, developed by jointly
31 by the Mathworks and NIST; see http://math.nist.gov/javanumerics/jama).
32 */
33 template<class Real>
34 class SVD {
37 int m, n;
38
39 public:
40 SVD(const DynamicRectMatrix<Real>& Arg) {
41 m = Arg.getNRow();
42 n = Arg.getNCol();
43 int nu = std::min(m, n);
44 s = DynamicVector<Real>(std::min(m + 1, n));
45 U = DynamicRectMatrix<Real>(m, nu, Real(0));
48 DynamicVector<Real> work(m);
50
51 int wantu = 1; /* boolean */
52 int wantv = 1; /* boolean */
53 int i = 0, j = 0, k = 0;
54
55 // Reduce A to bidiagonal form, storing the diagonal elements
56 // in s and the super-diagonal elements in e.
57
58 int nct = std::min(m - 1, n);
59 int nrt = std::max(0, std::min(n - 2, m));
60
61 for (k = 0; k < std::max(nct, nrt); k++) {
62 if (k < nct) {
63 // Compute the transformation for the k-th column and
64 // place the k-th diagonal in s(k).
65 // Compute 2-norm of k-th column without under/overflow.
66 s(k) = 0;
67 for (i = k; i < m; i++) {
68 s(k) = std::hypot(s(k), A(i, k));
69 }
70 if (s(k) != 0.0) {
71 if (A(k, k) < 0.0) { s(k) = -s(k); }
72 for (i = k; i < m; i++) {
73 A(i, k) /= s(k);
74 }
75 A(k, k) += 1.0;
76 }
77 s(k) = -s(k);
78 }
79 for (j = k + 1; j < n; j++) {
80 if ((k < nct) && (s(k) != 0.0)) {
81 // Apply the transformation.
82
83 Real t(0.0);
84 for (i = k; i < m; i++) {
85 t += A(i, k) * A(i, j);
86 }
87 t = -t / A(k, k);
88 for (i = k; i < m; i++) {
89 A(i, j) += t * A(i, k);
90 }
91 }
92
93 // Place the k-th row of A into e for the
94 // subsequent calculation of the row transformation.
95
96 e(j) = A(k, j);
97 }
98 if (wantu & (k < nct)) {
99 // Place the transformation in U for subsequent back
100 // multiplication.
101
102 for (i = k; i < m; i++) {
103 U(i, k) = A(i, k);
104 }
105 }
106 if (k < nrt) {
107 // Compute the k-th row transformation and place the
108 // k-th super-diagonal in e(k).
109 // Compute 2-norm without under/overflow.
110 e(k) = 0;
111 for (i = k + 1; i < n; i++) {
112 e(k) = std::hypot(e(k), e(i));
113 }
114 if (e(k) != 0.0) {
115 if (e(k + 1) < 0.0) { e(k) = -e(k); }
116 for (i = k + 1; i < n; i++) {
117 e(i) /= e(k);
118 }
119 e(k + 1) += 1.0;
120 }
121 e(k) = -e(k);
122 if ((k + 1 < m) & (e(k) != 0.0)) {
123 // Apply the transformation.
124
125 for (i = k + 1; i < m; i++) {
126 work(i) = 0.0;
127 }
128 for (j = k + 1; j < n; j++) {
129 for (i = k + 1; i < m; i++) {
130 work(i) += e(j) * A(i, j);
131 }
132 }
133 for (j = k + 1; j < n; j++) {
134 Real t(-e(j) / e(k + 1));
135 for (i = k + 1; i < m; i++) {
136 A(i, j) += t * work(i);
137 }
138 }
139 }
140 if (wantv) {
141 // Place the transformation in V for subsequent
142 // back multiplication.
143
144 for (i = k + 1; i < n; i++) {
145 V(i, k) = e(i);
146 }
147 }
148 }
149 }
150
151 // Set up the final bidiagonal matrix or order p.
152
153 int p = std::min(n, m + 1);
154 if (nct < n) { s(nct) = A(nct, nct); }
155 if (m < p) { s(p - 1) = 0.0; }
156 if (nrt + 1 < p) { e(nrt) = A(nrt, p - 1); }
157 e(p - 1) = 0.0;
158
159 // If required, generate U.
160
161 if (wantu) {
162 for (j = nct; j < nu; j++) {
163 for (i = 0; i < m; i++) {
164 U(i, j) = 0.0;
165 }
166 U(j, j) = 1.0;
167 }
168 for (k = nct - 1; k >= 0; k--) {
169 if (s(k) != 0.0) {
170 for (j = k + 1; j < nu; j++) {
171 Real t(0.0);
172 for (i = k; i < m; i++) {
173 t += U(i, k) * U(i, j);
174 }
175 t = -t / U(k, k);
176 for (i = k; i < m; i++) {
177 U(i, j) += t * U(i, k);
178 }
179 }
180 for (i = k; i < m; i++) {
181 U(i, k) = -U(i, k);
182 }
183 U(k, k) = 1.0 + U(k, k);
184 for (i = 0; i < k - 1; i++) {
185 U(i, k) = 0.0;
186 }
187 } else {
188 for (i = 0; i < m; i++) {
189 U(i, k) = 0.0;
190 }
191 U(k, k) = 1.0;
192 }
193 }
194 }
195
196 // If required, generate V.
197
198 if (wantv) {
199 for (k = n - 1; k >= 0; k--) {
200 if ((k < nrt) & (e(k) != 0.0)) {
201 for (j = k + 1; j < nu; j++) {
202 Real t(0.0);
203 for (i = k + 1; i < n; i++) {
204 t += V(i, k) * V(i, j);
205 }
206 t = -t / V(k + 1, k);
207 for (i = k + 1; i < n; i++) {
208 V(i, j) += t * V(i, k);
209 }
210 }
211 }
212 for (i = 0; i < n; i++) {
213 V(i, k) = 0.0;
214 }
215 V(k, k) = 1.0;
216 }
217 }
218
219 // Main iteration loop for the singular values.
220
221 int pp = p - 1;
222 int iter = 0;
223 Real eps(std::pow(2.0, -52.0));
224 while (p > 0) {
225 int k = 0;
226 int kase = 0;
227
228 // Here is where a test for too many iterations would go.
229
230 // This section of the program inspects for
231 // negligible elements in the s and e arrays. On
232 // completion the variables kase and k are set as follows.
233
234 // kase = 1 if s(p) and e(k-1) are negligible and k<p
235 // kase = 2 if s(k) is negligible and k<p
236 // kase = 3 if e(k-1) is negligible, k<p, and
237 // s(k), ..., s(p) are not negligible (qr step).
238 // kase = 4 if e(p-1) is negligible (convergence).
239
240 for (k = p - 2; k >= -1; k--) {
241 if (k == -1) { break; }
242 if (std::abs(e(k)) <= eps * (std::abs(s(k)) + std::abs(s(k + 1)))) {
243 e(k) = 0.0;
244 break;
245 }
246 }
247 if (k == p - 2) {
248 kase = 4;
249 } else {
250 int ks;
251 for (ks = p - 1; ks >= k; ks--) {
252 if (ks == k) { break; }
253 Real t((ks != p ? std::abs(e(ks)) : 0.) +
254 (ks != k + 1 ? std::abs(e(ks - 1)) : 0.));
255 if (std::abs(s(ks)) <= eps * t) {
256 s(ks) = 0.0;
257 break;
258 }
259 }
260 if (ks == k) {
261 kase = 3;
262 } else if (ks == p - 1) {
263 kase = 1;
264 } else {
265 kase = 2;
266 k = ks;
267 }
268 }
269 k++;
270
271 // Perform the task indicated by kase.
272
273 switch (kase) {
274 // Deflate negligible s(p).
275
276 case 1: {
277 Real f(e(p - 2));
278 e(p - 2) = 0.0;
279 for (j = p - 2; j >= k; j--) {
280 Real t(std::hypot(s(j), f));
281 Real cs(s(j) / t);
282 Real sn(f / t);
283 s(j) = t;
284 if (j != k) {
285 f = -sn * e(j - 1);
286 e(j - 1) = cs * e(j - 1);
287 }
288 if (wantv) {
289 for (i = 0; i < n; i++) {
290 t = cs * V(i, j) + sn * V(i, p - 1);
291 V(i, p - 1) = -sn * V(i, j) + cs * V(i, p - 1);
292 V(i, j) = t;
293 }
294 }
295 }
296 } break;
297
298 // Split at negligible s(k).
299
300 case 2: {
301 Real f(e(k - 1));
302 e(k - 1) = 0.0;
303 for (j = k; j < p; j++) {
304 Real t(std::hypot(s(j), f));
305 Real cs(s(j) / t);
306 Real sn(f / t);
307 s(j) = t;
308 f = -sn * e(j);
309 e(j) = cs * e(j);
310 if (wantu) {
311 for (i = 0; i < m; i++) {
312 t = cs * U(i, j) + sn * U(i, k - 1);
313 U(i, k - 1) = -sn * U(i, j) + cs * U(i, k - 1);
314 U(i, j) = t;
315 }
316 }
317 }
318 } break;
319
320 // Perform one qr step.
321
322 case 3: {
323 // Calculate the shift.
324
325 Real scale =
326 std::max(std::max(std::max(std::max(std::abs(s(p - 1)),
327 std::abs(s(p - 2))),
328 std::abs(e(p - 2))),
329 std::abs(s(k))),
330 std::abs(e(k)));
331 Real sp = s(p - 1) / scale;
332 Real spm1 = s(p - 2) / scale;
333 Real epm1 = e(p - 2) / scale;
334 Real sk = s(k) / scale;
335 Real ek = e(k) / scale;
336 Real b = ((spm1 + sp) * (spm1 - sp) + epm1 * epm1) / 2.0;
337 Real c = (sp * epm1) * (sp * epm1);
338 Real shift = 0.0;
339 if ((b != 0.0) || (c != 0.0)) {
340 shift = std::sqrt(b * b + c);
341 if (b < 0.0) { shift = -shift; }
342 shift = c / (b + shift);
343 }
344 Real f = (sk + sp) * (sk - sp) + shift;
345 Real g = sk * ek;
346
347 // Chase zeros.
348
349 for (j = k; j < p - 1; j++) {
350 Real t = std::hypot(f, g);
351 Real cs = f / t;
352 Real sn = g / t;
353 if (j != k) { e(j - 1) = t; }
354 f = cs * s(j) + sn * e(j);
355 e(j) = cs * e(j) - sn * s(j);
356 g = sn * s(j + 1);
357 s(j + 1) = cs * s(j + 1);
358 if (wantv) {
359 for (i = 0; i < n; i++) {
360 t = cs * V(i, j) + sn * V(i, j + 1);
361 V(i, j + 1) = -sn * V(i, j) + cs * V(i, j + 1);
362 V(i, j) = t;
363 }
364 }
365 t = std::hypot(f, g);
366 cs = f / t;
367 sn = g / t;
368 s(j) = t;
369 f = cs * e(j) + sn * s(j + 1);
370 s(j + 1) = -sn * e(j) + cs * s(j + 1);
371 g = sn * e(j + 1);
372 e(j + 1) = cs * e(j + 1);
373 if (wantu && (j < m - 1)) {
374 for (i = 0; i < m; i++) {
375 t = cs * U(i, j) + sn * U(i, j + 1);
376 U(i, j + 1) = -sn * U(i, j) + cs * U(i, j + 1);
377 U(i, j) = t;
378 }
379 }
380 }
381 e(p - 2) = f;
382 iter = iter + 1;
383 } break;
384
385 // Convergence.
386
387 case 4: {
388 // Make the singular values positive.
389
390 if (s(k) <= 0.0) {
391 s(k) = (s(k) < 0.0 ? -s(k) : 0.0);
392 if (wantv) {
393 for (i = 0; i <= pp; i++) {
394 V(i, k) = -V(i, k);
395 }
396 }
397 }
398
399 // Order the singular values.
400
401 while (k < pp) {
402 if (s(k) >= s(k + 1)) { break; }
403 Real t = s(k);
404 s(k) = s(k + 1);
405 s(k + 1) = t;
406 if (wantv && (k < n - 1)) {
407 for (i = 0; i < n; i++) {
408 t = V(i, k + 1);
409 V(i, k + 1) = V(i, k);
410 V(i, k) = t;
411 }
412 }
413 if (wantu && (k < m - 1)) {
414 for (i = 0; i < m; i++) {
415 t = U(i, k + 1);
416 U(i, k + 1) = U(i, k);
417 U(i, k) = t;
418 }
419 }
420 k++;
421 }
422 iter = 0;
423 p--;
424 } break;
425 }
426 }
427 }
428
429 void getU(DynamicRectMatrix<Real>& A) {
430 int minm = std::min(m + 1, n);
431
432 A = DynamicRectMatrix<Real>(m, minm);
433
434 for (int i = 0; i < m; i++)
435 for (int j = 0; j < minm; j++)
436 A(i, j) = U(i, j);
437 }
438
439 /* Return the right singular vectors */
440 void getV(DynamicRectMatrix<Real>& A) { A = V; }
441
442 /** Return the one-dimensional array of singular values */
444
445 /** Return the diagonal matrix of singular values
446 @return S
447 */
449 A = DynamicRectMatrix<Real>(n, n);
450 for (int i = 0; i < n; i++) {
451 for (int j = 0; j < n; j++) {
452 A(i, j) = 0.0;
453 }
454 A(i, i) = s(i);
455 }
456 }
457
458 /** Two norm (std::max(S)) */
459 Real norm2() { return s(0); }
460
461 /** Two norm of condition number (std::max(S)/std::min(S)) */
462 Real cond() { return s(0) / s(std::min(m, n) - 1); }
463
464 /** Effective numerical matrix rank
465 @return Number of nonnegligible singular values.
466 */
467 int rank() {
468 Real eps = std::pow(2.0, -52.0);
469 Real tol = std::max(m, n) * s(0) * eps;
470 int r = 0;
471 for (int i = 0; i < s.size(); i++) {
472 if (s(i) > tol) { r++; }
473 }
474 return r;
475 }
476 };
477} // namespace JAMA
478#endif
479// JAMA_SVD_H
void getS(DynamicRectMatrix< Real > &A)
Return the diagonal matrix of singular values.
Definition SVD.hpp:448
int rank()
Effective numerical matrix rank.
Definition SVD.hpp:467
Real norm2()
Two norm (std::max(S)).
Definition SVD.hpp:459
Real cond()
Two norm of condition number (std::max(S)/stdmin(S)).
Definition SVD.hpp:462
void getSingularValues(DynamicVector< Real > &x)
Return the one-dimensional array of singular values.
Definition SVD.hpp:443
Rectangular matrix class with contiguous flat storage.
Dynamically-sized vector class.