lambda-lanczos 2.1.1
Loading...
Searching...
No Matches
lambda_lanczos_tridiagonal_impl.hpp
Go to the documentation of this file.
1#ifndef LAMBDA_LANCZOS_TRIDIAGONAL_IMPL_H_
2#define LAMBDA_LANCZOS_TRIDIAGONAL_IMPL_H_
3
4#include <algorithm>
5#include <complex>
6#include <functional>
7#include <iostream>
8#include <iterator>
9#include <vector>
10
12
13namespace lambda_lanczos {
22template <typename T>
23inline size_t num_of_eigs_smaller_than(T c, const std::vector<T>& alpha, const std::vector<T>& beta) {
24 T q_i = alpha[0] - c;
25 size_t count = 0;
26 size_t m = alpha.size();
27
28 if (q_i < 0) {
29 ++count;
30 }
31
32 for (size_t i = 1; i < m; ++i) {
33 q_i = alpha[i] - c - beta[i - 1] * beta[i - 1] / q_i;
34 if (q_i < 0) {
35 ++count;
36 }
37 if (q_i == 0) {
38 q_i = std::numeric_limits<T>::epsilon();
39 }
40 }
41
42 return count;
43}
44
52template <typename T>
53inline T tridiagonal_eigen_limit(const std::vector<T>& alpha, const std::vector<T>& beta) {
54 T r = util::m_norm(alpha);
55 r += 2 * util::m_norm(beta);
56
57 return r;
58}
59
63template <typename T>
64inline T find_mth_eigenvalue(const std::vector<T>& alpha, const std::vector<T>& beta, const size_t m) {
65 T mid;
66 T pmid = std::numeric_limits<T>::max();
67 T r = tridiagonal_eigen_limit(alpha, beta);
68 T lower = -r;
69 T upper = r;
70
71 while (upper - lower > std::min(std::abs(lower), std::abs(upper)) * std::numeric_limits<T>::epsilon()) {
72 mid = (lower + upper) * T(0.5);
73
74 if (num_of_eigs_smaller_than(mid, alpha, beta) >= m + 1) {
75 upper = mid;
76 } else {
77 lower = mid;
78 }
79
80 if (mid == pmid) {
81 /* This avoids an infinite loop due to zero matrix */
82 break;
83 }
84 pmid = mid;
85 }
86
87 return lower; // The "lower" almost equals the "upper" here.
88}
89
93template <typename T>
94inline std::vector<T> compute_tridiagonal_eigenvector_from_eigenvalue(const std::vector<T>& alpha,
95 const std::vector<T>& beta,
96 const size_t index,
97 const T ev) {
98 (void)index; // Unused declaration for compiler
99
100 const auto m = alpha.size();
101 std::vector<T> cv(m);
102
103 cv[m - 1] = 1.0;
104
105 if (m >= 2) {
106 cv[m - 2] = (ev - alpha[m - 1]) * cv[m - 1] / beta[m - 2];
107 for (size_t k = m - 2; k-- > 0;) {
108 cv[k] = ((ev - alpha[k + 1]) * cv[k + 1] - beta[k + 1] * cv[k + 2]) / beta[k];
109 }
110 }
111
112 util::normalize(cv);
113
114 return cv;
115}
116
120template <typename T>
121inline void tridiagonal_eigenpairs_bisection(const std::vector<T>& alpha,
122 const std::vector<T>& beta,
123 std::vector<T>& eigenvalues,
124 std::vector<std::vector<T>>& eigenvectors) {
125 const size_t n = alpha.size();
126 eigenvalues.resize(n);
127 eigenvectors.resize(n);
128
129 for (size_t j = 0; j < n; ++j) {
130 eigenvalues[j] = lambda_lanczos::tridiagonal_impl::find_mth_eigenvalue(alpha, beta, j);
132 alpha, beta, j, eigenvalues[j]);
133 }
134}
135
151template <typename T>
152inline std::pair<T, T> calc_givens_cs(T a, T b) {
153 if (b == 0) {
154 return std::make_pair(1.0, 0.0);
155 }
156
157 if (a == 0) {
158 return std::make_pair(0.0, 1.0);
159 }
160
161 T x = std::sqrt(a * a + b * b);
162 T c = a / x;
163 T s = b / x;
164
165 return std::make_pair(c, s);
166}
167
181template <typename T>
182inline void isqr_step(std::vector<T>& alpha,
183 std::vector<T>& beta,
184 std::vector<std::vector<T>>& q,
185 size_t offset,
186 size_t nsub,
187 bool rotate_matrix) {
189 using std::pow;
190 using std::sqrt;
191
192 if (nsub == 1) {
193 return;
194 }
195
196 T d = (alpha[offset + nsub - 2] - alpha[offset + nsub - 1]) / (2 * beta[offset + nsub - 2]);
197 T mu = alpha[offset + nsub - 1] - beta[offset + nsub - 2] / (d + sgn(d) * sqrt(d * d + T(1)));
198 T x = alpha[offset + 0] - mu;
199
200 T s = 1.0;
201 T c = 1.0;
202 T p = 0.0;
203
204 for (size_t k = 0; k < nsub - 1; ++k) {
205 T z = s * beta[offset + k];
206 T beta_prev = c * beta[offset + k];
207
208 auto cs = calc_givens_cs(x, z);
209 c = std::get<0>(cs);
210 s = std::get<1>(cs);
211
212 if (k > 0) {
213 beta[offset + k - 1] = sqrt(x * x + z * z);
214 }
215 T u = ((alpha[offset + k + 1] - alpha[offset + k] + p) * s + T(2) * c * beta_prev);
216 alpha[offset + k] = alpha[offset + k] - p + s * u;
217 p = s * u;
218 x = c * u - beta_prev;
219
220 // Keep in mind that q[k][j] is the jth element of the kth eigenvector.
221 // This means an eigenvector is stored as a ROW of the matrix q
222 // in the sense of mathematical notation.
223 if (rotate_matrix) {
224 for (size_t j = 0; j < alpha.size(); ++j) {
225 auto v0 = q[offset + k][j];
226 auto v1 = q[offset + k + 1][j];
227
228 q[offset + k][j] = c * v0 + s * v1;
229 q[offset + k + 1][j] = -s * v0 + c * v1;
230 }
231 }
232 }
233
234 alpha[offset + nsub - 1] = alpha[offset + nsub - 1] - p;
235 beta[offset + nsub - 2] = x;
236}
237
252template <typename T>
253inline void find_subspace(const std::vector<T>& alpha,
254 std::vector<T>& beta,
255 size_t& submatrix_first,
256 size_t& submatrix_last) {
257 const T eps = std::numeric_limits<T>::epsilon() * 0.5;
258 const T safe_min = std::numeric_limits<T>::min();
259 const size_t n = alpha.size();
260
261 /* Overwrite small elements with zero */
262 for (size_t i = 0; i < n - 1; ++i) {
263 if (std::abs(beta[i]) < std::sqrt(std::abs(alpha[i]) * std::abs(alpha[i + 1])) * eps + safe_min) {
264 beta[i] = 0;
265 }
266 }
267
268 /* Find subspace */
269 while (submatrix_last > 0 && beta[submatrix_last - 1] == 0) {
270 submatrix_last--;
271 }
272 submatrix_first = submatrix_last;
273 while (submatrix_first > 0 && beta[submatrix_first - 1] != 0) {
274 submatrix_first--;
275 }
276}
277
290template <typename T>
291inline size_t tridiagonal_eigenpairs(const std::vector<T>& alpha,
292 const std::vector<T>& beta,
293 std::vector<T>& eigenvalues,
294 std::vector<std::vector<T>>& eigenvectors,
295 bool compute_eigenvector = true) {
296 const size_t n = alpha.size();
297
298 auto alpha_work = alpha;
299 auto beta_work = beta;
300
301 /* Prepare an identity matrix to be transformed into an eigenvector matrix */
302 if (compute_eigenvector) {
304 }
305
306 size_t unconverged_count = 0;
307 size_t submatrix_last_prev = n - 1;
308
309 size_t loop_count = 1;
310 while (true) {
311 size_t submatrix_last = submatrix_last_prev;
312 size_t submatrix_first;
313 find_subspace(alpha_work, beta_work, submatrix_first, submatrix_last);
314
315 const size_t nsub = submatrix_last - submatrix_first + 1;
316 const size_t max_loop_count = nsub * 50;
317
318 if (submatrix_last > 0) {
319 isqr_step(alpha_work, beta_work, eigenvectors, submatrix_first, nsub, compute_eigenvector);
320 } else {
321 break;
322 }
323
324 if (submatrix_last == submatrix_last_prev) {
325 if (loop_count > max_loop_count) {
326 submatrix_last_prev = submatrix_first;
327 unconverged_count++;
328 loop_count = 1;
329 } else {
330 loop_count++;
331 }
332 } else {
333 loop_count = 1;
334 submatrix_last_prev = submatrix_last;
335 }
336 }
337
338 eigenvalues = alpha_work;
339
340 lambda_lanczos::util::sort_eigenpairs(eigenvalues, eigenvectors, compute_eigenvector);
341
342 return unconverged_count;
343}
344
355template <typename T>
356inline size_t tridiagonal_eigenvalues(const std::vector<T>& alpha,
357 const std::vector<T>& beta,
358 std::vector<T>& eigenvalues) {
359 std::vector<std::vector<T>> dummy_eigenvectors;
360 return tridiagonal_eigenpairs(alpha, beta, eigenvalues, dummy_eigenvectors, false);
361}
362
363} /* namespace tridiagonal_impl */
364} /* namespace lambda_lanczos */
365
366#endif /* LAMBDA_LANCZOS_TRIDIAGONAL_IMPL_H_ */
Definition lambda_lanczos_tridiagonal_impl.hpp:14
size_t num_of_eigs_smaller_than(T c, const std::vector< T > &alpha, const std::vector< T > &beta)
Finds the number of eigenvalues of given tridiagonal matrix smaller than c.
Definition lambda_lanczos_tridiagonal_impl.hpp:23
size_t tridiagonal_eigenvalues(const std::vector< T > &alpha, const std::vector< T > &beta, std::vector< T > &eigenvalues)
Computes all eigenvalues for given tri-diagonal matrix using the Implicitly Shifted QR algorithm.
Definition lambda_lanczos_tridiagonal_impl.hpp:356
T find_mth_eigenvalue(const std::vector< T > &alpha, const std::vector< T > &beta, const size_t m)
Finds the mth smaller eigenvalue of given tridiagonal matrix.
Definition lambda_lanczos_tridiagonal_impl.hpp:64
void find_subspace(const std::vector< T > &alpha, std::vector< T > &beta, size_t &submatrix_first, size_t &submatrix_last)
Find sub-tridiagonal matrix that remains non-diagonal.
Definition lambda_lanczos_tridiagonal_impl.hpp:253
void tridiagonal_eigenpairs_bisection(const std::vector< T > &alpha, const std::vector< T > &beta, std::vector< T > &eigenvalues, std::vector< std::vector< T > > &eigenvectors)
Computes all eigenpairs (eigenvalues and eigenvectors) for given tri-diagonal matrix.
Definition lambda_lanczos_tridiagonal_impl.hpp:121
std::vector< T > compute_tridiagonal_eigenvector_from_eigenvalue(const std::vector< T > &alpha, const std::vector< T > &beta, const size_t index, const T ev)
Computes an eigenvector corresponding to given eigenvalue for given tri-diagonal matrix.
Definition lambda_lanczos_tridiagonal_impl.hpp:94
T tridiagonal_eigen_limit(const std::vector< T > &alpha, const std::vector< T > &beta)
Computes the upper bound of the absolute value of eigenvalues by Gerschgorin theorem.
Definition lambda_lanczos_tridiagonal_impl.hpp:53
void isqr_step(std::vector< T > &alpha, std::vector< T > &beta, std::vector< std::vector< T > > &q, size_t offset, size_t nsub, bool rotate_matrix)
Performs an implicit shift QR step A = Z^T A Z on given sub tridiagonal matrix A.
Definition lambda_lanczos_tridiagonal_impl.hpp:182
size_t tridiagonal_eigenpairs(const std::vector< T > &alpha, const std::vector< T > &beta, std::vector< T > &eigenvalues, std::vector< std::vector< T > > &eigenvectors, bool compute_eigenvector=true)
Computes all eigenpairs (eigenvalues and eigenvectors) for given tri-diagonal matrix using the Implic...
Definition lambda_lanczos_tridiagonal_impl.hpp:291
std::pair< T, T > calc_givens_cs(T a, T b)
Calculates the cosine and sine of Givens rotation that eliminates a specific element (see detail).
Definition lambda_lanczos_tridiagonal_impl.hpp:152
void normalize(std::vector< T > &vec)
Normalizes given vector.
Definition linear_algebra.hpp:78
void sort_eigenpairs(std::vector< real_t< T > > &eigenvalues, std::vector< std::vector< T > > &eigenvectors, bool sort_eigenvector, const std::function< bool(real_t< T >, real_t< T >)> predicate=std::less< real_t< T > >())
Sorts eigenvalues and eigenvectors with respect to given predicate.
Definition common.hpp:142
T sgn(T val)
Return the sign of given value.
Definition common.hpp:195
real_t< T > m_norm(const std::vector< T > &vec)
Returns Manhattan-like norm of given vector.
Definition linear_algebra.hpp:123
void initAsIdentity(std::vector< std::vector< T > > &a, size_t n)
Initializes the given matrix a to an n by n identity matrix.
Definition linear_algebra.hpp:150
Definition eigenpair_manager.hpp:9