numerics 0.1.0
Loading...
Searching...
No Matches
jacobi_eig.cpp
Go to the documentation of this file.
1/// @file linalg/eigen/backends/seq/jacobi_eig.cpp
2/// @brief Sequential cyclic Jacobi eigensolver.
3///
4/// Reference: Golub & Van Loan "Matrix Computations" section 8.4
5///
6/// Each sweep visits every off-diagonal pair (p,q) and applies a 2x2
7/// similarity rotation that zeros A[p,q]. The rotation angle satisfies:
8///
9/// tau = (A[q,q] - A[p,p]) / (2*A[p,q])
10/// t = sign(tau) / (|tau| + sqrt(1 + tau^2))
11/// c = 1/sqrt(1+t^2), s = c*t
12#include "impl.hpp"
13#include <cmath>
14#include <algorithm>
15#include <stdexcept>
16
17namespace num::backends::seq {
18
19EigenResult eig_sym(const Matrix& A_in, real tol, idx max_sweeps) {
20 if (A_in.rows() != A_in.cols())
21 throw std::invalid_argument("eig_sym: matrix must be square");
22
23 constexpr real rotation_tol = 1e-15;
24 idx n = A_in.rows();
25 Matrix A = A_in;
26 Matrix V(n, n, 0.0);
27 for (idx i = 0; i < n; ++i)
28 V(i, i) = 1.0;
29
30 idx sweeps = 0;
31 bool converged = false;
32
33 for (idx sweep = 0; sweep < max_sweeps; ++sweep) {
34 real off = 0;
35 for (idx p = 0; p < n; ++p)
36 for (idx q = p + 1; q < n; ++q)
37 off += A(p, q) * A(p, q);
38
39 if (std::sqrt(2.0 * off) < tol) {
40 converged = true;
41 break;
42 }
43
44 for (idx p = 0; p < n - 1; ++p) {
45 for (idx q = p + 1; q < n; ++q) {
46 real apq = A(p, q);
47 if (std::abs(apq) < rotation_tol)
48 continue;
49
50 real app = A(p, p), aqq = A(q, q);
51 real tau = (aqq - app) / (2.0 * apq);
52 real t = std::copysign(1.0, tau)
53 / (std::abs(tau) + std::sqrt(1.0 + tau * tau));
54 real c = 1.0 / std::sqrt(1.0 + t * t);
55 real s = c * t;
56
57 A(p, p) = c * c * app - 2.0 * c * s * apq + s * s * aqq;
58 A(q, q) = s * s * app + 2.0 * c * s * apq + c * c * aqq;
59 A(p, q) = A(q, p) = 0.0;
60
61 for (idx r = 0; r < n; ++r) {
62 if (r == p || r == q)
63 continue;
64 real arp = A(r, p), arq = A(r, q);
65 A(r, p) = A(p, r) = c * arp - s * arq;
66 A(r, q) = A(q, r) = s * arp + c * arq;
67 }
68
69 for (idx r = 0; r < n; ++r) {
70 real vrp = V(r, p), vrq = V(r, q);
71 V(r, p) = c * vrp - s * vrq;
72 V(r, q) = s * vrp + c * vrq;
73 }
74 }
75 }
76 ++sweeps;
77 }
78
79 Vector values(n);
80 for (idx i = 0; i < n; ++i)
81 values[i] = A(i, i);
82
83 for (idx i = 0; i < n - 1; ++i) {
84 idx min_j = i;
85 for (idx j = i + 1; j < n; ++j)
86 if (values[j] < values[min_j])
87 min_j = j;
88 if (min_j != i) {
89 std::swap(values[i], values[min_j]);
90 for (idx r = 0; r < n; ++r)
91 std::swap(V(r, i), V(r, min_j));
92 }
93 }
94
95 return {values, V, sweeps, converged};
96}
97
98} // 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
EigenResult eig_sym(const Matrix &A, real tol, idx max_sweeps)
double real
Definition types.hpp:10
std::size_t idx
Definition types.hpp:11
constexpr real e
Definition math.hpp:43
std::experimental::simd butterfly for FFT.
Full eigendecomposition result: A = V * diag(values) * V^T.