lambda-lanczos 2.0.0
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 <iostream>
5#include <vector>
6#include <complex>
7#include <algorithm>
8#include <functional>
9#include <iterator>
11
12
13namespace lambda_lanczos { namespace tridiagonal_impl {
21template <typename T>
22inline size_t num_of_eigs_smaller_than(T c,
23 const std::vector<T>& alpha,
24 const std::vector<T>& beta) {
25 T q_i = alpha[0] - c;
26 size_t count = 0;
27 size_t m = alpha.size();
28
29 if(q_i < 0){
30 ++count;
31 }
32
33 for(size_t i = 1; i < m; ++i){
34 q_i = alpha[i] - c - beta[i-1]*beta[i-1]/q_i;
35 if(q_i < 0){
36 ++count;
37 }
38 if(q_i == 0){
39 q_i = std::numeric_limits<T>::epsilon();
40 }
41 }
42
43 return count;
44}
45
46
54template <typename T>
55inline T tridiagonal_eigen_limit(const std::vector<T>& alpha,
56 const std::vector<T>& beta) {
57 T r = util::l1_norm(alpha);
58 r += 2*util::l1_norm(beta);
59
60 return r;
61}
62
63
67template <typename T>
68inline T find_mth_eigenvalue(const std::vector<T>& alpha,
69 const std::vector<T>& beta,
70 const size_t m) {
71 T mid;
72 T pmid = std::numeric_limits<T>::max();
73 T r = tridiagonal_eigen_limit(alpha, beta);
74 T lower = -r;
75 T upper = r;
76
77 while(upper-lower > std::min(std::abs(lower), std::abs(upper))*std::numeric_limits<T>::epsilon()) {
78 mid = (lower+upper)*T(0.5);
79
80 if(num_of_eigs_smaller_than(mid, alpha, beta) >= m+1) {
81 upper = mid;
82 } else {
83 lower = mid;
84 }
85
86 if(mid == pmid) {
87 /* This avoids an infinite loop due to zero matrix */
88 break;
89 }
90 pmid = mid;
91 }
92
93 return lower; // The "lower" almost equals the "upper" here.
94}
95
96
100template <typename T>
101inline std::vector<T> compute_tridiagonal_eigenvector_from_eigenvalue(const std::vector<T>& alpha,
102 const std::vector<T>& beta,
103 const size_t index,
104 const T ev) {
105 (void)index; // Unused declaration for compiler
106
107 const auto m = alpha.size();
108 std::vector<T> cv(m);
109
110 cv[m-1] = 1.0;
111
112 if(m >= 2) {
113 cv[m-2] = (ev - alpha[m-1]) * cv[m-1] / beta[m-2];
114 for (size_t k = m-2; k-- > 0;) {
115 cv[k] = ((ev - alpha[k + 1]) * cv[k + 1] - beta[k + 1] * cv[k + 2]) / beta[k];
116 }
117 }
118
119 util::normalize(cv);
120
121 return cv;
122}
123
124
128template <typename T>
129inline void tridiagonal_eigenpairs_bisection(const std::vector<T>& alpha,
130 const std::vector<T>& beta,
131 std::vector<T>& eigenvalues,
132 std::vector<std::vector<T>>& eigenvectors) {
133 const size_t n = alpha.size();
134 eigenvalues.resize(n);
135 eigenvectors.resize(n);
136
137 for(size_t j = 0; j < n; ++j) {
138 eigenvalues[j] = lambda_lanczos::tridiagonal_impl::find_mth_eigenvalue(alpha, beta, j);
139 eigenvectors[j] = lambda_lanczos::tridiagonal_impl::compute_tridiagonal_eigenvector_from_eigenvalue(alpha, beta, j, eigenvalues[j]);
140 }
141}
142
143
159template <typename T>
160inline std::pair<T, T> calc_givens_cs(T a, T b) {
161 if(b == 0) {
162 return std::make_pair(1.0, 0.0);
163 }
164
165 if(a == 0) {
166 return std::make_pair(0.0, 1.0);
167 }
168
169 T x = std::sqrt(a*a + b*b);
170 T c = a / x;
171 T s = b / x;
172
173 return std::make_pair(c, s);
174}
175
176
190template <typename T>
191inline void isqr_step(std::vector<T>& alpha,
192 std::vector<T>& beta,
193 std::vector<std::vector<T>>& q,
194 size_t offset,
195 size_t nsub,
196 bool rotate_matrix) {
197 using std::sqrt;
198 using std::pow;
200
201 if(nsub == 1) {
202 return;
203 }
204
205 T d = (alpha[offset+nsub-2] - alpha[offset+nsub-1]) / (2 * beta[offset+nsub-2]);
206 T mu = alpha[offset+nsub-1] - beta[offset+nsub-2] / (d + sgn(d) * sqrt(d*d + T(1)));
207 T x = alpha[offset+0] - mu;
208
209 T s = 1.0;
210 T c = 1.0;
211 T p = 0.0;
212
213 for(size_t k = 0; k < nsub - 1; ++k) {
214 T z = s*beta[offset+k];
215 T beta_prev = c*beta[offset+k];
216
217 auto cs = calc_givens_cs(x, z);
218 c = std::get<0>(cs);
219 s = std::get<1>(cs);
220
221 if(k > 0) {
222 beta[offset+k-1] = sqrt(x*x + z*z);
223 }
224 T u = ((alpha[offset+k+1] - alpha[offset+k] + p)*s + T(2)*c*beta_prev);
225 alpha[offset+k] = alpha[offset+k] - p + s*u;
226 p = s*u;
227 x = c*u - beta_prev;
228
229 // Keep in mind that q[k][j] is the jth element of the kth eigenvector.
230 // This means an eigenvector is stored as a ROW of the matrix q
231 // in the sense of mathematical notation.
232 if(rotate_matrix) {
233 for(size_t j = 0; j < alpha.size(); ++j) {
234 auto v0 = q[offset+k][j];
235 auto v1 = q[offset+k+1][j];
236
237 q[offset+k][j] = c*v0 + s*v1;
238 q[offset+k+1][j] = -s*v0 + c*v1;
239 }
240 }
241 }
242
243 alpha[offset+nsub-1] = alpha[offset+nsub-1] - p;
244 beta[offset+nsub-2] = x;
245}
246
247
262template <typename T>
263inline void find_subspace(const std::vector<T>& alpha,
264 std::vector<T>& beta,
265 size_t& submatrix_first,
266 size_t& submatrix_last) {
267 const T eps = std::numeric_limits<T>::epsilon() * 0.5;
268 const T safe_min = std::numeric_limits<T>::min();
269 const size_t n = alpha.size();
270
271 /* Overwrite small elements with zero */
272 for(size_t i = 0; i < n-1; ++i) {
273 if(std::abs(beta[i]) < std::sqrt(std::abs(alpha[i]) * std::abs(alpha[i+1]))*eps + safe_min) {
274 beta[i] = 0;
275 }
276 }
277
278 /* Find subspace */
279 while(submatrix_last > 0 && beta[submatrix_last-1] == 0) {
280 submatrix_last--;
281 }
282 submatrix_first = submatrix_last;
283 while(submatrix_first > 0 && beta[submatrix_first-1] != 0) {
284 submatrix_first--;
285 }
286}
287
288
301template <typename T>
302inline size_t tridiagonal_eigenpairs(const std::vector<T>& alpha,
303 const std::vector<T>& beta,
304 std::vector<T>& eigenvalues,
305 std::vector<std::vector<T>>& eigenvectors,
306 bool compute_eigenvector = true) {
307 const size_t n = alpha.size();
308
309 auto alpha_work = alpha;
310 auto beta_work = beta;
311
312 /* Prepare an identity matrix to be transformed into an eigenvector matrix */
313 if(compute_eigenvector) {
315 }
316
317 size_t unconverged_count = 0;
318 size_t submatrix_last_prev = n - 1;
319
320 size_t loop_count = 1;
321 while(true) {
322 size_t submatrix_last = submatrix_last_prev;
323 size_t submatrix_first;
324 find_subspace(alpha_work, beta_work, submatrix_first, submatrix_last);
325
326 const size_t nsub = submatrix_last - submatrix_first + 1;
327 const size_t max_loop_count = nsub * 50;
328
329 if(submatrix_last > 0) {
330 isqr_step(alpha_work,
331 beta_work,
332 eigenvectors,
333 submatrix_first,
334 nsub,
335 compute_eigenvector);
336 } else {
337 break;
338 }
339
340 if(submatrix_last == submatrix_last_prev) {
341 if(loop_count > max_loop_count) {
342 submatrix_last_prev = submatrix_first;
343 unconverged_count++;
344 loop_count = 1;
345 } else {
346 loop_count++;
347 }
348 } else {
349 loop_count = 1;
350 submatrix_last_prev = submatrix_last;
351 }
352 }
353
354 eigenvalues = alpha_work;
355
356 lambda_lanczos::util::sort_eigenpairs(eigenvalues, eigenvectors, compute_eigenvector);
357
358 return unconverged_count;
359}
360
361
372template <typename T>
373inline size_t tridiagonal_eigenvalues(const std::vector<T>& alpha,
374 const std::vector<T>& beta,
375 std::vector<T>& eigenvalues) {
376 std::vector<std::vector<T>> dummy_eigenvectors;
377 return tridiagonal_eigenpairs(alpha, beta, eigenvalues, dummy_eigenvectors, false);
378}
379
380}} // namespace lambda_lanczos::tridiagonal_impl
381
382#endif /* LAMBDA_LANCZOS_TRIDIAGONAL_IMPL_H_ */
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:22
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:373
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:68
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:263
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:129
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:101
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:55
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:191
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:302
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:160
void normalize(std::vector< T > &vec)
Normalizes given vector.
Definition: lambda_lanczos_util.hpp:180
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: lambda_lanczos_util.hpp:242
T sgn(T val)
Return the sign of given value.
Definition: lambda_lanczos_util.hpp:300
void initAsIdentity(std::vector< std::vector< T > > &a, size_t n)
Initializes the given matrix a to an n by n identity matrix.
Definition: lambda_lanczos_util.hpp:226
real_t< T > l1_norm(const std::vector< T > &vec)
Returns 1-norm of given vector.
Definition: lambda_lanczos_util.hpp:189
Definition: eigenpair_manager.hpp:10