numerics 0.1.0
Loading...
Searching...
No Matches
svd.cpp
Go to the documentation of this file.
1/// @file linalg/svd/backends/seq/svd.cpp
2/// @brief Sequential one-sided Jacobi SVD.
3///
4/// See svd/svd.cpp for the full algorithm commentary.
5#include "impl.hpp"
6#include <cmath>
7#include <algorithm>
8
9namespace num::backends::seq {
10
11SVDResult svd(const Matrix& A_in, real tol, idx max_sweeps) {
12 constexpr real tiny = 1e-300;
13 idx m = A_in.rows(), n = A_in.cols();
14 idx r = std::min(m, n);
15
16 Matrix A = A_in;
17
18 Matrix V(n, n, 0.0);
19 for (idx i = 0; i < n; ++i)
20 V(i, i) = 1.0;
21
22 idx sweeps = 0;
23 bool converged = false;
24
25 for (idx sweep = 0; sweep < max_sweeps; ++sweep) {
26 real max_cos = 0;
27 for (idx p = 0; p < r - 1; ++p) {
28 for (idx q = p + 1; q < r; ++q) {
29 real alpha = 0, beta = 0, gamma = 0;
30 for (idx i = 0; i < m; ++i) {
31 alpha += A(i, p) * A(i, p);
32 beta += A(i, q) * A(i, q);
33 gamma += A(i, p) * A(i, q);
34 }
35 if (alpha < tiny || beta < tiny)
36 continue;
37
38 real cos_pq = std::abs(gamma) / std::sqrt(alpha * beta);
39 max_cos = std::max(max_cos, cos_pq);
40
41 if (cos_pq < tol)
42 continue;
43
44 real zeta = (beta - alpha) / (2.0 * gamma);
45 real t = std::copysign(1.0, zeta)
46 / (std::abs(zeta) + std::sqrt(1.0 + zeta * zeta));
47 real c = 1.0 / std::sqrt(1.0 + t * t);
48 real s = c * t;
49
50 for (idx i = 0; i < m; ++i) {
51 real aip = A(i, p), aiq = A(i, q);
52 A(i, p) = c * aip - s * aiq;
53 A(i, q) = s * aip + c * aiq;
54 }
55
56 for (idx i = 0; i < n; ++i) {
57 real vip = V(i, p), viq = V(i, q);
58 V(i, p) = c * vip - s * viq;
59 V(i, q) = s * vip + c * viq;
60 }
61 }
62 }
63
64 ++sweeps;
65 if (max_cos < tol) {
66 converged = true;
67 break;
68 }
69 }
70
71 Vector S(r);
72 Matrix U(m, r, 0.0);
73 for (idx j = 0; j < r; ++j) {
74 real nrm = 0;
75 for (idx i = 0; i < m; ++i)
76 nrm += A(i, j) * A(i, j);
77 S[j] = std::sqrt(nrm);
78 if (S[j] > tiny)
79 for (idx i = 0; i < m; ++i)
80 U(i, j) = A(i, j) / S[j];
81 }
82
83 for (idx i = 0; i < r - 1; ++i) {
84 idx max_j = i;
85 for (idx j = i + 1; j < r; ++j)
86 if (S[j] > S[max_j])
87 max_j = j;
88
89 if (max_j != i) {
90 std::swap(S[i], S[max_j]);
91 for (idx k = 0; k < m; ++k)
92 std::swap(U(k, i), U(k, max_j));
93 for (idx k = 0; k < n; ++k)
94 std::swap(V(k, i), V(k, max_j));
95 }
96 }
97
98 Matrix Vt(r, n, 0.0);
99 for (idx i = 0; i < r; ++i)
100 for (idx j = 0; j < n; ++j)
101 Vt(i, j) = V(j, i);
102
103 return {U, S, Vt, sweeps, converged};
104}
105
106} // namespace num::backends::seq
Dense row-major matrix with optional GPU storage.
Definition matrix.hpp:12
constexpr idx rows() const noexcept
Definition matrix.hpp:24
constexpr idx cols() const noexcept
Definition matrix.hpp:25
SVDResult svd(const Matrix &A, real tol, idx max_sweeps)
Definition svd.cpp:11
double real
Definition types.hpp:10
real beta(real a, real b)
B(a, b) – beta function.
Definition math.hpp:248
std::size_t idx
Definition types.hpp:11
constexpr real e
Definition math.hpp:43
real zeta(real x)
zeta(x) – Riemann zeta function
Definition math.hpp:239
std::experimental::simd butterfly for FFT.
Result of a Singular Value Decomposition: A = U * diag(S) * V^T.
Definition svd.hpp:41