4
4
#include < stdexcept>
5
5
6
6
std::optional<std::pair<double , diffusion_maps::Vector>>
7
- diffusion_maps::internal::symmetric_power_method (const SparseMatrix &a,
8
- const Vector &x0,
9
- const double tol,
10
- const unsigned max_iters,
11
- const double epsilon) {
7
+ diffusion_maps::internal::symmetric_power_method (
8
+ const SparseMatrix &a, const Vector &x0, const Vector *const betas,
9
+ const std::size_t n_betas, const double tol, const unsigned max_iters) {
12
10
if (a.n_rows () != a.n_cols ()) { // a is not square.
13
11
throw std::invalid_argument (" matrix is not square" );
14
12
}
@@ -17,15 +15,16 @@ diffusion_maps::internal::symmetric_power_method(const SparseMatrix &a,
17
15
}
18
16
19
17
Vector x = x0 / x0.l2_norm ();
20
- double mu_0 = 0 , mu_1 = 0 ;
21
18
22
19
for (unsigned k = 0 ; k < max_iters; ++k) {
23
20
Vector y = a * x;
21
+
22
+ // Orthogonalise y against betas.
23
+ for (std::size_t i = 0 ; i < n_betas; ++i) {
24
+ y -= betas[i] * betas[i].dot (y);
25
+ }
26
+
24
27
const double mu = x.dot (y);
25
- const double denom = (mu - mu_1) - (mu_1 - mu_0);
26
- const double mu_hat = std::abs (denom) < epsilon
27
- ? mu_0
28
- : mu_0 - ((mu_1 - mu_0) * (mu_1 - mu_0)) / denom;
29
28
30
29
const double l2_norm_y = y.l2_norm ();
31
30
if (l2_norm_y == 0 ) { // a has eigenvalue 0.
@@ -35,18 +34,15 @@ diffusion_maps::internal::symmetric_power_method(const SparseMatrix &a,
35
34
y /= l2_norm_y;
36
35
const double err = (x - y).l2_norm ();
37
36
x = y;
38
- if (k >= 3 && err < tol) { // Success.
39
- return std::make_pair (mu_hat , x);
37
+ if (err < tol) { // Success.
38
+ return std::make_pair (mu , x);
40
39
}
41
-
42
- mu_0 = mu_1;
43
- mu_1 = mu;
44
40
}
45
41
46
42
return std::nullopt; // Failed to converge.
47
43
}
48
44
49
- std::vector <std::pair <double , diffusion_maps::Vector>>
45
+ std::pair <std::vector <double >, std::vector< diffusion_maps::Vector>>
50
46
diffusion_maps::internal::eigsh (const SparseMatrix &a, const unsigned k,
51
47
const double tol, const unsigned max_iters,
52
48
const unsigned max_restarts) {
@@ -57,42 +53,39 @@ diffusion_maps::internal::eigsh(const SparseMatrix &a, const unsigned k,
57
53
throw std::invalid_argument (" k cannot be larger than the number of rows" );
58
54
}
59
55
60
- std::vector<std::pair<double , Vector>> eig_pairs;
61
- eig_pairs.reserve (k);
56
+ std::default_random_engine gen;
57
+ std::uniform_real_distribution<double > dist (0 , 1 );
58
+
59
+ std::vector<double > eigenvalues;
60
+ std::vector<Vector> eigenvectors;
61
+ eigenvalues.reserve (k);
62
+ eigenvectors.reserve (k);
62
63
63
64
for (std::size_t i = 0 ; i < k; ++i) {
64
65
for (unsigned restarts = 0 ; restarts < max_restarts; ++restarts) {
65
- // Construct the initial vector for the symmetric power method
66
- // x0 = (A - λᵢ₋₁ I) … (A - λ₁ I) (A - λ₀ I) x
67
- // where x is a random vector. (Annihilation technique.)
66
+ // Generate the initial guess for the eigenvector.
68
67
69
68
Vector x0 (a.n_rows ());
70
-
71
- std::default_random_engine gen;
72
- std::uniform_real_distribution<double > dist (0 , 1 );
73
69
for (std::size_t i = 0 ; i < x0.size (); ++i) {
74
70
x0[i] = dist (gen);
75
71
}
76
72
77
- for (std::size_t j = 0 ; j < i; j++) {
78
- const double lambda_j = eig_pairs[j].first ;
79
- x0 = a * x0 - x0 * lambda_j;
80
- }
81
-
82
73
// Use the symmetric power method to find the i-th eigenvalue and
83
74
// eigenvector.
84
75
85
- const auto eig_pair = symmetric_power_method (a, x0, tol, max_iters);
76
+ const auto eig_pair = symmetric_power_method (
77
+ a, x0, eigenvectors.data (), eigenvectors.size (), tol, max_iters);
86
78
87
79
// Restart if the method does not converge or finds an eigenvalue 0.
88
80
if (!eig_pair.has_value () || eig_pair->first == 0 ) {
89
81
continue ;
90
82
}
91
83
92
- eig_pairs.push_back (eig_pair.value ());
84
+ eigenvalues.push_back (eig_pair->first );
85
+ eigenvectors.push_back (eig_pair->second );
93
86
break ;
94
87
}
95
88
}
96
89
97
- return eig_pairs ;
90
+ return std::make_pair (eigenvalues, eigenvectors) ;
98
91
}
0 commit comments