iterative-solver 0.0
 
Loading...
Searching...
No Matches
helper-implementation.h
1#ifndef LINEARALGEBRA_SRC_MOLPRO_LINALG_ITERATIVESOLVER_HELPER_IMPLEMENTATION_H_
2#define LINEARALGEBRA_SRC_MOLPRO_LINALG_ITERATIVESOLVER_HELPER_IMPLEMENTATION_H_
3#include <Eigen/Dense>
4
5#include <molpro/Profiler.h>
6#include <molpro/lapacke.h>
7#include <molpro/linalg/itsolv/helper-dispatch.h>
8#include <molpro/linalg/itsolv/helper.h>
9
10#include "Logger.h"
11#include "subspace/Matrix.h"
12
13#include <algorithm>
14#include <cassert>
15#include <cmath>
16#include <complex>
17#include <cstddef>
18#include <iomanip>
19#include <list>
20#include <numeric>
21#include <span>
22#include <type_traits>
23
24namespace molpro::linalg::itsolv {
25
26template <typename value_type>
27std::list<SVD<value_type>> svd_eigen_jacobi(size_t nrows, size_t ncols, const array::Span<value_type>& m,
29 auto mat = Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(m.data(), nrows, ncols);
30#if EIGEN_VERSION_AT_LEAST(3, 4, 90)
31 // Cast to unsigned int to avoid -Wdeprecated-enum-enum-conversion: the two
32 // Eigen flags belong to different enum types but are meant to be ORed here.
33 auto svd = Eigen::JacobiSVD<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(
34 mat, static_cast<unsigned int>(Eigen::ComputeThinV) |
35 static_cast<unsigned int>(Eigen::NoQRPreconditioner));
36#else
37 auto svd = Eigen::JacobiSVD<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>, Eigen::NoQRPreconditioner>(
38 mat, Eigen::ComputeThinV);
39#endif
40 auto svd_system = std::list<SVD<value_type>>{};
41 auto sv = svd.singularValues();
42 for (int i = int(ncols) - 1; i >= 0; --i) {
43 if (std::abs(sv(i)) < threshold) { // TODO: This seems to discard values ABOVE the threshold, not below it. it's
44 auto t = SVD<value_type>{}; // also not scaling this threshold relative to the max singular value - find out why
45 t.value = sv(i);
46 t.v.reserve(ncols);
47 for (size_t j = 0; j < ncols; ++j) {
48 t.v.emplace_back(svd.matrixV()(j, i));
49 }
50 svd_system.emplace_back(std::move(t));
51 }
52 }
53 return svd_system;
54}
55
56template <typename value_type>
57std::list<SVD<value_type>> svd_eigen_bdcsvd(size_t nrows, size_t ncols, const array::Span<value_type>& m,
59 auto mat = Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(m.data(), nrows, ncols);
60 auto svd = Eigen::BDCSVD<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(mat, Eigen::ComputeThinV);
61 auto svd_system = std::list<SVD<value_type>>{};
62 auto sv = svd.singularValues();
63 for (int i = int(ncols) - 1; i >= 0; --i) {
64 if (std::abs(sv(i)) < threshold) {
65 auto t = SVD<value_type>{};
66 t.value = sv(i);
67 t.v.reserve(ncols);
68 for (size_t j = 0; j < ncols; ++j) {
69 t.v.emplace_back(svd.matrixV()(j, i));
70 }
71 svd_system.emplace_back(std::move(t));
72 }
73 }
74 return svd_system;
75}
76
77#ifdef HAVE_LAPACKE
84template <typename value_type>
85std::list<SVD<value_type>> svd_lapacke_gesdd(size_t nrows, size_t ncols, const array::Span<value_type>& mat,
87 static_assert(has_lapack_kernel_v<value_type>, "LAPACK has no ?gesdd kernel for this scalar type");
88 const size_t sdim = std::min(nrows, ncols);
89 // ?gesdd destroys its input, so hand it a copy
90 std::vector<value_type> a(mat.begin(), mat.end());
91 std::vector<real_type_t<value_type>> sv(sdim);
92 std::vector<value_type> u(nrows * nrows), v(ncols * ncols);
93 const auto info =
94 lapack::gesdd(LAPACK_ROW_MAJOR, 'A', lapack_int(nrows), lapack_int(ncols), a.data(), lapack_int(ncols), sv.data(),
95 u.data(), lapack_int(nrows), v.data(), lapack_int(ncols));
96 if (info != 0)
97 throw std::runtime_error("?gesdd (singular value decomposition) failed with info = " + std::to_string(info));
98 auto svd_system = std::list<SVD<value_type>>{};
99 for (int i = int(ncols) - 1; i >= 0; --i) {
100 if (std::abs(sv[i]) < threshold) {
101 auto t = SVD<value_type>{};
102 t.value = sv[i];
103 t.v.reserve(ncols);
104 for (size_t j = 0; j < ncols; ++j) {
105 t.v.emplace_back(v[i * ncols + j]);
106 }
107 svd_system.emplace_back(std::move(t));
108 }
109 }
110 return svd_system;
111}
112
114template <typename value_type>
115std::list<SVD<value_type>> svd_lapacke_gesvd(size_t nrows, size_t ncols, const array::Span<value_type>& mat,
117 static_assert(has_lapack_kernel_v<value_type>, "LAPACK has no ?gesvd kernel for this scalar type");
118 const size_t sdim = std::min(nrows, ncols);
119 // ?gesvd destroys its input, so hand it a copy
120 std::vector<value_type> a(mat.begin(), mat.end());
121 std::vector<real_type_t<value_type>> sv(sdim);
122 std::vector<value_type> u(nrows * nrows), v(ncols * ncols);
123 std::vector<real_type_t<value_type>> superb(sdim > 0 ? sdim - 1 : 0);
124 const auto info = lapack::gesvd(LAPACK_ROW_MAJOR, 'N', 'A', lapack_int(nrows), lapack_int(ncols), a.data(),
125 lapack_int(ncols), sv.data(), u.data(), lapack_int(nrows), v.data(),
126 lapack_int(ncols), superb.data());
127 if (info != 0)
128 throw std::runtime_error("?gesvd (singular value decomposition) failed with info = " + std::to_string(info));
129 auto svd_system = std::list<SVD<value_type>>{};
130 for (int i = int(ncols) - 1; i >= 0; --i) {
131 if (std::abs(sv[i]) < threshold) {
132 auto t = SVD<value_type>{};
133 t.value = sv[i];
134 t.v.reserve(ncols);
135 for (size_t j = 0; j < ncols; ++j) {
136 t.v.emplace_back(v[i * ncols + j]);
137 }
138 svd_system.emplace_back(std::move(t));
139 }
140 }
141 return svd_system;
142}
143
144#endif
145
153inline int eigensolver_lapacke_dsyev(std::span<const double> matrix, std::span<double> eigenvectors,
154 std::span<double> eigenvalues, const size_t dimension) {
155 return eigensolver_hermitian<double>(matrix, eigenvectors, eigenvalues, dimension);
156}
157
159inline std::list<SVD<double>> eigensolver_lapacke_dsyev(size_t dimension, std::span<const double> matrix) {
160 return eigensolver_hermitian<double>(dimension, matrix);
161}
162
170template <typename value_type>
171size_t get_rank(std::span<const value_type> eigenvalues, value_type threshold) {
172 if (eigenvalues.size() == 0) {
173 return 0;
174 }
175 value_type max = *max_element(eigenvalues.begin(), eigenvalues.end());
176 value_type threshold_scaled = threshold * max;
177 size_t count =
178 std::count_if(eigenvalues.begin(), eigenvalues.end(), [&](auto const& val) { return val >= threshold_scaled; });
179 return count;
180}
181
189template <typename value_type>
191 // a singular value, and the eigenvalue of a hermitian matrix, is real even for a complex problem
193 typename std::list<SVD<value_type>>::iterator it;
194 for (it = svd_system.begin(); it != svd_system.end(); it++) {
195 if (real_part(it->value) > max_value) {
196 max_value = real_part(it->value);
197 }
198 }
199 // scale threshold
201
202 size_t rank = 0;
203 // get rank
204 for (it = svd_system.begin(); it != svd_system.end(); it++) {
205 if (real_part(it->value) > threshold_scaled) {
206 rank += 1;
207 }
208 }
209 return rank;
210}
211
212namespace detail {
213
221template <typename value_type>
222std::list<SVD<value_type>> svd_system_impl(size_t nrows, size_t ncols, const array::Span<value_type>& m,
224 std::list<SVD<value_type>> svds;
225 assert(m.size() == nrows * ncols);
226 if (m.empty())
227 return {};
228 if (hermitian) {
229 assert(nrows == ncols);
230 // m arrives row-major, as subspace::Matrix stores it, while eigensolver_hermitian reads its input
231 // column-major. For a complex hermitian matrix the two differ by a conjugation, so transpose it.
232 using matrix_type = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;
233 using row_major_type = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
234 const matrix_type a = Eigen::Map<const row_major_type>(m.data(), nrows, ncols);
235 // Only the eigenpairs at or below the threshold are kept, and a matrix comfortably positive definite, as the
236 // overlap of a well-conditioned subspace is, has none: a Cholesky factorisation shows that at a fraction of the
237 // cost of the eigendecomposition.
238 if (eigenvalues_certainly_above<value_type>(std::span<const value_type>{a.data(), size_t(a.size())}, nrows,
239 threshold))
240 return {};
241 svds = eigensolver_hermitian<value_type>(nrows, std::span<const value_type>{a.data(), size_t(a.size())});
242 for (auto s = svds.begin(); s != svds.end();)
243 if (real_part(s->value) > threshold)
244 s = svds.erase(s);
245 else
246 ++s;
247 } else {
248 // The general (non-hermitian) decomposition goes through Eigen for every scalar type: the
249 // subspace matrices are small, and ?gesdd/?gesvd (svd_lapacke_gesdd/svd_lapacke_gesvd above)
250 // offer nothing here that would justify differing results between precisions.
252 // return svd_eigen_bdcsvd<value_type>(nrows, ncols, m, threshold);
253 }
254
255 // reduce to rank
256 if (reduce_to_rank) {
257 int rank = get_rank(svds, threshold);
258 for (int i = ncols; i > rank; i--) {
259 svds.pop_back();
260 }
261 }
262 return svds;
263}
264
265} // namespace detail
266
267template <typename value_type, typename std::enable_if_t<!is_complex<value_type>{}, std::nullptr_t>>
268std::list<SVD<value_type>> svd_system(size_t nrows, size_t ncols, const array::Span<value_type>& m,
270 return detail::svd_system_impl<value_type>(nrows, ncols, m, threshold, hermitian, reduce_to_rank);
271}
272
273template <typename value_type, typename std::enable_if_t<is_complex<value_type>{}, int>>
274std::list<SVD<value_type>> svd_system(size_t nrows, size_t ncols, const array::Span<value_type>& m,
276 return detail::svd_system_impl<value_type>(nrows, ncols, m, threshold, hermitian, reduce_to_rank);
277}
278
279template <typename value_type>
280void printMatrix(const std::vector<value_type>& m, size_t rows, size_t cols, std::string title, std::ostream& s) {
281 s << title << "\n"
282 << Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(m.data(), rows, cols) << std::endl;
283}
284
285namespace detail {
286
293template <typename real_type>
295 bool operator()(const std::complex<real_type>& lhs, const std::complex<real_type>& rhs) const {
296 using std::abs;
297 if (lhs.real() != rhs.real()) {
298 return lhs.real() < rhs.real();
299 }
300 if (abs(lhs.imag()) != abs(rhs.imag())) {
301 // This fixes the order of distinct complex eigenvalue pairs that share the same real part
302 return abs(lhs.imag()) < abs(rhs.imag());
303 }
304 // This fixes the order within a complex eigenvalue pair
305 return lhs.imag() < rhs.imag();
306 }
307};
308
310template <typename value_type>
312 using matrix_type = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;
316 int rank = 0;
317};
318
330template <typename value_type>
332 const std::vector<value_type>& metric, size_t dimension,
333 real_type_t<value_type> svdThreshold, int verbosity) {
334 using std::abs;
335 using std::sqrt;
337 using matrix_type = typename orthogonalised_subspace<value_type>::matrix_type;
338 using row_major_type = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
339 // a metric eigenvalue below this marks a singular direction; calibrated for double precision
341
343 const matrix_type H = Eigen::Map<const row_major_type>(matrix.data(), dimension, dimension);
344 result.S = Eigen::Map<const row_major_type>(metric.data(), dimension, dimension);
345
346 // Perform an eigenvalue decomposition of the metric. The metric is necessarily hermitian, so the
347 // hermitian eigensolver applies; it reads its input column-major, which is why S was materialised
348 // above rather than mapped straight onto the caller's row-major buffer.
349 Eigen::Vector<real_t, Eigen::Dynamic> metricEvals(dimension);
350 matrix_type metricEvecs(dimension, dimension);
352 std::span<const value_type>{result.S.data(), size_t(result.S.size())},
353 {metricEvecs.data(), dimension * dimension}, {metricEvals.data(), dimension}, dimension);
354 if (success != 0) {
355 throw std::runtime_error("Eigensolver did not converge");
356 }
357 result.rank = int(get_rank<real_t>(std::span<const real_t>{metricEvals.data(), dimension}, svdThreshold));
358 const int rank = result.rank;
359
360 if (verbosity > 1 && rank < int(dimension))
361 molpro::cout << "SVD rank " << rank << " in subspace of dimension " << dimension << std::endl;
362 if (verbosity > 2 && rank < int(dimension))
363 molpro::cout << "singular values " << metricEvals.transpose() << std::endl;
364
365 // Transform H into a symmetrically orthogonalized basis via (S^{-1/2})^\dagger H S^{-1/2}
366 // taking into account the possibility of rank-deficiency of S (aka: zero SV).
367 // The eigenvalues come out in ascending order, so the retained directions are the trailing block.
368 Eigen::Vector<value_type, Eigen::Dynamic> svmh(rank);
369 for (int k = 0; k < rank; k++) {
370 const real_t lambda = metricEvals(dimension - rank + k);
371 assert(abs(lambda) <= svdThreshold || lambda >= 0); // metric is supposed to be positive (semi-)definite
372 svmh(k) = lambda > null_metric_eigenvalue_tol ? value_type(1 / sqrt(lambda)) : value_type(0);
373 }
374 const auto retained = metricEvecs.rightCols(rank);
375 result.Hbar = svmh.asDiagonal() * retained.adjoint() * H * retained * svmh.asDiagonal();
376 result.transformation = retained * svmh.asDiagonal();
377 return result;
378}
379
398template <typename value_type, typename matrix_type, typename vector_type>
399bool hermitian_lowest_by_cholesky(const std::vector<value_type>& matrix, const std::vector<value_type>& metric,
400 size_t dimension, size_t nroots, real_type_t<value_type> svdThreshold,
401 matrix_type& eigenvectors, vector_type& eigenvalues) {
403 using colmajor_type = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;
404 using row_major_type = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
405 if (!has_lapack_kernel_v<value_type> || nroots == 0 || nroots >= dimension)
406 return false;
407 const auto Hr = Eigen::Map<const row_major_type>(matrix.data(), dimension, dimension);
408 const auto Sr = Eigen::Map<const row_major_type>(metric.data(), dimension, dimension);
409 const colmajor_type H = (Hr + Hr.adjoint()) / value_type(2);
410 const colmajor_type S = (Sr + Sr.adjoint()) / value_type(2);
412 colmajor_type evecs(dimension, nroots);
413 Eigen::Vector<real_t, Eigen::Dynamic> evals(nroots);
415 std::span<const value_type>{H.data(), size_t(H.size())}, std::span<const value_type>{S.data(), size_t(S.size())},
416 std::span<value_type>{evecs.data(), size_t(evecs.size())},
417 std::span<real_t>{evals.data(), nroots}, dimension, nroots, min_rcond))
418 return false;
419 eigenvectors = std::move(evecs);
420 eigenvalues = evals.template cast<typename vector_type::Scalar>();
421 return true;
422}
423
424} // namespace detail
425
426template <typename value_type, typename std::enable_if_t<is_complex<value_type>{}, int>>
427void eigenproblem(std::vector<value_type>& eigenvectors, std::vector<value_type>& eigenvalues,
428 const std::vector<value_type>& matrix, const std::vector<value_type>& metric, size_t dimension,
430 std::vector<std::pair<std::size_t, value_type>>* imag_eval_parts, size_t nroots) {
431 using std::abs;
432 using std::sqrt;
434 using matrix_type = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;
435 using vector_type = Eigen::Vector<value_type, Eigen::Dynamic>;
436 // A complex scalar represents a complex eigenvalue directly, so, unlike the real overload, this one
437 // never has to split an eigenvalue into a real part and a separately tracked imaginary part.
438 if (imag_eval_parts)
439 imag_eval_parts->clear();
441
442 auto prof = molpro::Profiler::single();
443 prof->start("itsolv::eigenproblem");
445 matrix_type subspaceEigenvectors;
446 matrix_type S; // the metric, which only a non-hermitian problem needs again
447 {
448 matrix_type lowest_evecs;
449 Eigen::Vector<real_t, Eigen::Dynamic> lowest_evals;
450 if (hermitian && detail::hermitian_lowest_by_cholesky<value_type>(matrix, metric, dimension, nroots, svdThreshold,
454 } else {
455 const auto subspace = detail::orthogonalise_subspace<value_type>(matrix, metric, dimension, svdThreshold, verbosity);
456 const int rank = subspace.rank;
457 if (rank == 0) {
458 eigenvectors.clear();
459 eigenvalues.clear();
460 prof->stop();
461 return;
462 }
463 if (!hermitian)
464 S = subspace.S;
465
466 if (hermitian) {
467 // A self-adjoint Hbar has a real spectrum, and only the lowest nroots eigenpairs are wanted
468 const auto wanted = std::min<size_t>(nroots, size_t(rank));
469 const matrix_type Hsym = (subspace.Hbar + subspace.Hbar.adjoint()) / value_type(2);
470 Eigen::Vector<real_t, Eigen::Dynamic> evals(wanted);
471 matrix_type evecs(rank, wanted);
472 if (eigensolver_hermitian_lowest<value_type>(std::span<const value_type>{Hsym.data(), size_t(Hsym.size())},
473 {evecs.data(), size_t(evecs.size())}, {evals.data(), wanted},
474 size_t(rank), wanted) != 0)
475 throw std::runtime_error("Eigensolver of the subspace matrix did not converge");
477 subspaceEigenvectors = subspace.transformation * evecs;
478 } else {
479 // Perform an eigendecomposition of the transformed matrix. Hbar is not self-adjoint in general, so
480 // its spectrum is complex -- which is exactly what the scalar type of this overload can hold.
481 Eigen::ComplexEigenSolver<matrix_type> s(subspace.Hbar);
482 if (s.info() != Eigen::Success) {
483 throw std::runtime_error("Eigensolver of the subspace matrix did not converge");
484 }
485 subspaceEigenvalues = s.eigenvalues();
486 // Convert eigenvectors back into original basis (minus singular dimensions)
487 subspaceEigenvectors = subspace.transformation * s.eigenvectors();
488 }
489 }
490 }
491
492 // Determine order of eigenvalues such that they come in non-descending order of their real part
493 // (and non-descending order of imaginary part, in case of equal real parts)
494 Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> perm(subspaceEigenvalues.size());
495 perm.setIdentity();
496 std::ranges::sort(perm.indices(), detail::eigenvalue_order<real_t>{},
497 [&subspaceEigenvalues](auto idx) { return subspaceEigenvalues[idx]; });
500
501 if (!hermitian) {
502 // The eigenvectors of a self-adjoint Hbar are orthonormal, so back-transforming them already
503 // yields an S-orthonormal set; otherwise they have to be normalised explicitly.
504 for (Eigen::Index k = 0; k < subspaceEigenvectors.cols(); k++) {
505 const auto ovl = subspaceEigenvectors.col(k).dot(S * subspaceEigenvectors.col(k));
506 // S is supposed to be positive (semi-)definite implying that ovl must be a non-negative real number
508 if (ovl.real() > null_eigenvalue_tol)
509 subspaceEigenvectors.col(k) /= value_type(sqrt(ovl.real()));
510 }
511 }
512
513 // Fix indeterminate phase of eigenvectors by requiring the max component to be real and positive
514 for (Eigen::Index i = 0; i < subspaceEigenvectors.cols(); ++i) {
515 Eigen::Index pivot = 0;
516 for (Eigen::Index j = 1; j < subspaceEigenvectors.rows(); ++j)
518 pivot = j;
520 if (magnitude > 0)
522 }
523
524 const auto nsolutions = subspaceEigenvectors.cols();
525 eigenvectors.resize(dimension * nsolutions);
526 eigenvalues.resize(nsolutions);
527 Eigen::Map<matrix_type>(eigenvectors.data(), dimension, nsolutions) = subspaceEigenvectors;
528 Eigen::Map<vector_type>(eigenvalues.data(), nsolutions) = subspaceEigenvalues;
529 prof->stop();
530}
531
532template <typename value_type, typename std::enable_if_t<!is_complex<value_type>{}, std::nullptr_t>>
533void eigenproblem(std::vector<value_type>& eigenvectors, std::vector<value_type>& eigenvalues,
534 const std::vector<value_type>& matrix, const std::vector<value_type>& metric, size_t dimension,
536 std::vector<std::pair<std::size_t, value_type>>* imag_eval_parts, size_t nroots) {
537 // let ADL pick up the overloads of extended- and arbitrary-precision scalar types
538 using std::abs;
539 using std::sqrt;
540 // Tolerances calibrated for double precision, rescaled to the precision actually in use
541 const value_type zero_tol = precision_scaled<value_type>(1e-10); // a quantity that ought to vanish
542 const value_type null_eigenvalue_tol = precision_scaled<value_type>(1e-12); // an eigenvalue that vanishes
543 using MatrixT = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;
544 using ComplexMatrixT = Eigen::Matrix<std::complex<value_type>, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;
545 using VectorT = Eigen::Vector<value_type, Eigen::Dynamic>;
546 using ComplexVectorT = Eigen::Vector<std::complex<value_type>, Eigen::Dynamic>;
547
548 auto prof = molpro::Profiler::single();
549 prof->start("itsolv::eigenproblem");
552 MatrixT S; // the metric, which only a non-hermitian problem needs again
553 {
556 if (hermitian && detail::hermitian_lowest_by_cholesky<value_type>(matrix, metric, dimension, nroots, svdThreshold,
560 } else {
561 const auto subspace = detail::orthogonalise_subspace<value_type>(matrix, metric, dimension, svdThreshold, verbosity);
562 const int rank = subspace.rank;
563 const auto& Hbar = subspace.Hbar;
564 if (rank == 0) {
565 eigenvectors.clear();
566 eigenvalues.clear();
567 prof->stop();
568 return;
569 }
570 if (!hermitian)
571 S = subspace.S;
572
573 if (hermitian) {
574 // A hermitian problem has real eigenvalues and eigenvectors. Hbar is then symmetric up to rounding, which
575 // compiler-dependent floating-point contraction can make large enough for the general eigensolver to return a
576 // complex pair for (near-)degenerate eigenvalues. Solving the symmetric part with the self-adjoint solver rules that
577 // out.
578 // Only the lowest nroots eigenpairs are wanted, which LAPACK computes without the full decomposition: once a P
579 // space makes the subspace large, the full one would dominate every iteration.
580 const auto wanted = std::min<size_t>(nroots, size_t(rank));
581 const MatrixT Hsym = (Hbar + Hbar.transpose()) / value_type(2);
583 MatrixT evecs(rank, wanted);
584 if (eigensolver_hermitian_lowest<value_type>(std::span<const value_type>{Hsym.data(), size_t(Hsym.size())},
585 {evecs.data(), size_t(evecs.size())}, {evals.data(), wanted},
586 size_t(rank), wanted) != 0)
587 throw std::runtime_error("Eigensolver of the subspace matrix did not converge");
589 subspaceEigenvectors = (subspace.transformation * evecs).template cast<std::complex<value_type>>();
590 } else {
591 // Perform an eigendecomposition of the transformed matrix
592 Eigen::EigenSolver<MatrixT> s(Hbar);
593 subspaceEigenvalues = s.eigenvalues();
594 if (s.eigenvalues().imag().norm() < zero_tol) {
595 // real eigenvalues
597 subspaceEigenvectors = s.eigenvectors();
598 // complex eigenvectors need to be rotated
599 // assume that they come in consecutive pairs
600 for (int i = 0; i < subspaceEigenvectors.cols() - 1; i++) {
601 if (subspaceEigenvectors.col(i).imag().norm() <= zero_tol) {
602 continue;
603 }
604
605 const int j = i + 1;
607 subspaceEigenvectors.col(j).imag().norm() <= zero_tol) {
608 continue;
609 }
610
611 // For a real-valued matrix, eigenvectors can always be chosen to be real. If we have a complex eigenvector,
612 // it's complex conjugate must also be an eigenvector with the same eigenvalue. We can combine these two
613 // vectors as either u + u^* = 2 Re(u) or i*(u - u^*) = -2 Im(u).
614 // In other words, the real and imaginary part of u are the corresponding real-valued eigenvectors.
615 subspaceEigenvectors.col(j) = subspaceEigenvectors.col(i).imag() / subspaceEigenvectors.col(i).imag().norm();
616 subspaceEigenvectors.col(i) = subspaceEigenvectors.col(i).real() / subspaceEigenvectors.col(i).real().norm();
617 }
618
619 // Convert eigenvectors back into original basis (minus singular dimensions)
620 subspaceEigenvectors = subspace.transformation * subspaceEigenvectors;
621 } else {
622 // complex eigenvalues
623 #ifdef __INTEL_COMPILER
624 molpro::cout << "Hbar\n" << Hbar << std::endl;
625 molpro::cout << "Eigenvalues\n" << s.eigenvalues() << std::endl;
626 molpro::cout << "Eigenvectors\n" << s.eigenvectors() << std::endl;
627 throw std::runtime_error("Intel compiler does not support working with complex eigen3 entities properly");
628 #endif
629
630 // Convert eigenvectors back into original basis (minus singular dimensions)
631 subspaceEigenvectors = subspace.transformation * s.eigenvectors();
632 }
633 }
634 }
635 }
636
637 // Determine order of eigenvalues such that they come in non-descending order of their real part
638 // (and non-descending order of imaginary part, in case of equal real parts)
639 Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> perm(subspaceEigenvalues.size());
640 perm.setIdentity();
641 std::ranges::sort(perm.indices(), detail::eigenvalue_order<real_type_t<value_type>>{},
642 [&subspaceEigenvalues](auto idx) { return subspaceEigenvalues[idx]; });
643
644 // Apply determined order to eigenvalues and -vectors
647
648
649 // TODO: Need to address the case of near-zero eigenvalues (as below for non-hermitian case) and clean-up
650 // non-hermitian case
651
652 if (!hermitian) {
653 for (auto repeat = 0; repeat < 1; ++repeat)
654 for (Eigen::Index k = 0; k < subspaceEigenvectors.cols(); k++) {
656 // special case of zero eigenvalue -- make some real non-zero vector definitely in the null space
657 subspaceEigenvectors.col(k).real() += value_type(0.3256897) * subspaceEigenvectors.col(k).imag();
658 subspaceEigenvectors.col(k).imag().setZero();
659 }
660
661 auto ovl = subspaceEigenvectors.col(k).dot(S * subspaceEigenvectors.col(k));
662 // S is supposed to be positive (semi-)definite implying that ovl must be a non-negative real number
663 assert(abs(ovl.imag()) < zero_tol);
664 assert(ovl.real() > 0);
665 subspaceEigenvectors.col(k) /= sqrt(ovl.real());
666 }
667 }
668
669 // Fix indeterminate phase of eigenvectors by requiring the max component to be positive
670 for (std::size_t i = 0; i < subspaceEigenvectors.cols(); ++i) {
671 const auto &col = subspaceEigenvectors.col(i);
672 auto it = std::ranges::max_element(col, std::less<>{}, [](auto val) { return abs(val); });
673 auto idx = std::distance(col.begin(), it);
674 if (subspaceEigenvectors.col(i)[idx].real() < 0) {
675 subspaceEigenvectors.col(i) *= -1;
676 }
677 }
678
679 if (imag_eval_parts) {
680 // Complex eigenvalues are tolerable -> process them to be able to represent everything
681 // by real-valued vectors
682 imag_eval_parts->clear();
683
684 for (Eigen::Index root = 0; root < subspaceEigenvalues.size(); ++root) {
685 if (subspaceEigenvalues(root).imag() == 0) {
686 continue;
687 }
688
689 // Complex-valued eigenvalues must appear as complex conjugate pairs
690 assert(root + 1 < subspaceEigenvalues.size());
691 assert(abs(std::conj(subspaceEigenvalues(root)) - subspaceEigenvalues(root + 1)) < zero_tol);
692
693 imag_eval_parts->emplace_back(root, subspaceEigenvalues(root).imag());
694 imag_eval_parts->emplace_back(root + 1, -subspaceEigenvalues(root).imag());
695
696 // Set the eigenvalue pair to their real-part only (imaginary part is tracked separately in imag_eval_parts)
697 subspaceEigenvalues(root) = subspaceEigenvalues(root + 1) = subspaceEigenvalues(root).real();
698
699 // Pretend the real and imaginary part were separate eigenvectors (this is required in order
700 // to represent all data without the need for using complex numbers).
701 // However, as the eigenvalues are not degenerate, the real and imaginary parts of the eigenvectors
702 // are in fact NOT eigenvectors themselves.
703 // If the true eigenvectors are required, they can easily be recovered from the real and imaginary
704 // parts we store here.
705 subspaceEigenvectors.col(root + 1) = subspaceEigenvectors.col(root).imag();
706 subspaceEigenvectors.col(root) = subspaceEigenvectors.col(root).real();
707
708 // Skip the second eigenvalue in the pair of complex conjugate eigenvalues
709 ++root;
710 }
711 }
712
715 throw std::runtime_error("unexpected complex solution found");
716 }
717
718 const auto nsolutions = subspaceEigenvalues.size();
719 eigenvectors.resize(dimension * nsolutions);
720 eigenvalues.resize(nsolutions);
721
722 Eigen::Map<MatrixT>(eigenvectors.data(), dimension, nsolutions) = subspaceEigenvectors.real();
723 Eigen::Map<VectorT> ev(eigenvalues.data(), nsolutions);
724 ev = subspaceEigenvalues.real();
725
726 prof->stop();
727}
728
729namespace detail {
730
737template <typename value_type>
738void solve_LinearEquations_impl(std::vector<value_type>& solution, std::vector<value_type>& eigenvalues,
739 const std::vector<value_type>& matrix, const std::vector<value_type>& metric,
740 const std::vector<value_type>& rhs, const size_t dimension, size_t nroot,
742 int verbosity) {
743 const Eigen::Index nX = dimension;
744 solution.resize(nX * nroot);
745 // std::cout << "augmented_hessian "<<augmented_hessian<<std::endl;
746 if (augmented_hessian > 0) { // Augmented hessian
747 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> subspaceMatrix;
748 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> subspaceOverlap;
749 subspaceMatrix.conservativeResize(nX + 1, nX + 1);
750 subspaceOverlap.conservativeResize(nX + 1, nX + 1);
751 // both arrive row-major, as subspace::Matrix stores them; reading them column-major would
752 // transpose, which is invisible for a real symmetric matrix but conjugates a hermitian one
753 using row_major_type = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
754 subspaceMatrix.block(0, 0, nX, nX) = Eigen::Map<const row_major_type>(matrix.data(), nX, nX);
755 subspaceOverlap.block(0, 0, nX, nX) = Eigen::Map<const row_major_type>(metric.data(), nX, nX);
756 eigenvalues.resize(nroot);
757 for (size_t root = 0; root < nroot; root++) {
758 for (Eigen::Index i = 0; i < nX; i++) {
759 // rhs is a row-major dimension x nroot matrix, as the straight-solve branch below also reads it
760 subspaceMatrix(i, nX) = -augmented_hessian * rhs[i * nroot + root];
761 // the augmented matrix is hermitian, which for a real element type means simply symmetric
763 subspaceOverlap(i, nX) = subspaceOverlap(nX, i) = 0;
764 }
765 subspaceMatrix(nX, nX) = 0;
766 subspaceOverlap(nX, nX) = 1;
767 // std::cout << "subspace augmented hessian subspaceMatrix\n"<<subspaceMatrix<<std::endl;
768 // std::cout << "subspace augmented hessian subspaceOverlap\n"<<subspaceOverlap<<std::endl;
769
770 if constexpr (is_complex<value_type>{}) {
771 // Eigen's GeneralizedEigenSolver is real-only, so reduce to a standard problem the same way
772 // eigenproblem() does: the augmented overlap is hermitian and positive semi-definite.
773 using matrix_type = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;
774 std::vector<value_type> augmented_matrix(subspaceMatrix.size()), augmented_overlap(subspaceOverlap.size());
775 // orthogonalise_subspace() reads its arguments row-major
776 Eigen::Map<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(
777 augmented_matrix.data(), nX + 1, nX + 1) = subspaceMatrix;
778 Eigen::Map<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(
779 augmented_overlap.data(), nX + 1, nX + 1) = subspaceOverlap;
780 const auto augmented = detail::orthogonalise_subspace<value_type>(augmented_matrix, augmented_overlap,
781 nX + 1, svdThreshold, verbosity);
782 if (augmented.rank == 0)
783 throw std::runtime_error("augmented Hessian: the augmented overlap matrix has no non-singular direction");
784 Eigen::ComplexEigenSolver<matrix_type> s(augmented.Hbar);
785 if (s.info() != Eigen::Success)
786 throw std::runtime_error("augmented Hessian: eigensolver did not converge");
787 auto eval = s.eigenvalues();
788 Eigen::Index imax = 0;
789 for (Eigen::Index i = 0; i < eval.size(); i++)
790 if (eval(i).real() < eval(imax).real())
791 imax = i;
792 eigenvalues[root] = eval(imax);
793 const Eigen::Vector<value_type, Eigen::Dynamic> evec = augmented.transformation * s.eigenvectors().col(imax);
794 const value_type scale = value_type(augmented_hessian) * evec(nX);
795 for (Eigen::Index k = 0; k < nX; k++)
796 solution[k + nX * root] = evec(k) / scale;
797 } else {
798 Eigen::GeneralizedEigenSolver<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>> s(subspaceMatrix,
800 auto eval = s.eigenvalues();
801 auto evec = s.eigenvectors();
802 Eigen::Index imax = 0;
803 for (Eigen::Index i = 0; i < nX + 1; i++)
804 if (eval(i).real() < eval(imax).real())
805 imax = i;
806 eigenvalues[root] = eval(imax).real();
807 auto Solution = evec.col(imax).real().head(nX) / (augmented_hessian * evec.real()(nX, imax));
808 for (auto k = 0; k < nX; k++)
809 solution[k + nX * root] = Solution(k);
810 // std::cout << "subspace augmented hessian solution\n"<<Solution<<std::endl;
811 }
812 }
813 } else { // straight solution of linear equations
814 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> subspaceMatrixR(
815 matrix.data(), nX, nX);
816 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> RHS_R(rhs.data(), nX,
817 nroot);
818 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> subspaceMatrix = subspaceMatrixR;
819 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> RHS = RHS_R;
820 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> Solution;
821// std::cout << "solve_LinearEquations RHS_R\n"<<RHS_R<<std::endl;
822// for (size_t i=0; i<RHS_R.cols()*RHS_R.rows(); ++i)
823// std::cout << " "<<RHS_R.data()[i];
824// std::cout << std::endl;
825// std::cout << "solve_LinearEquations RHS\n"<<RHS<<std::endl;
826// for (size_t i=0; i<RHS.cols()*RHS.rows(); ++i)
827// std::cout << " "<<RHS.data()[i];
828// std::cout << std::endl;
829 Solution = subspaceMatrix.householderQr().solve(RHS);
830 // std::cout << "subspace linear equations solution\n"<<Solution<<std::endl;
831 for (size_t root = 0; root < nroot; root++)
832 for (auto k = 0; k < nX; k++)
833 solution[k + nX * root] = Solution(k, root);
834 }
835}
836
837} // namespace detail
838
839template <typename value_type, typename std::enable_if_t<is_complex<value_type>{}, int>>
840void solve_LinearEquations(std::vector<value_type>& solution, std::vector<value_type>& eigenvalues,
841 const std::vector<value_type>& matrix, const std::vector<value_type>& metric,
842 const std::vector<value_type>& rhs, const size_t dimension, size_t nroot,
844 int verbosity) {
845 detail::solve_LinearEquations_impl<value_type>(solution, eigenvalues, matrix, metric, rhs, dimension, nroot,
846 augmented_hessian, svdThreshold, verbosity);
847}
848
849template <typename value_type, typename std::enable_if_t<!is_complex<value_type>{}, std::nullptr_t>>
850void solve_LinearEquations(std::vector<value_type>& solution, std::vector<value_type>& eigenvalues,
851 const std::vector<value_type>& matrix, const std::vector<value_type>& metric,
852 const std::vector<value_type>& rhs, const size_t dimension, size_t nroot,
854 int verbosity) {
855 detail::solve_LinearEquations_impl<value_type>(solution, eigenvalues, matrix, metric, rhs, dimension, nroot,
856 augmented_hessian, svdThreshold, verbosity);
857}
858
859namespace detail {
860
867template <typename value_type>
868void solve_DIIS_impl(std::vector<value_type>& solution, const std::vector<value_type>& matrix, const size_t dimension,
869 real_type_t<value_type> svdThreshold, int verbosity) {
870 // let ADL pick up the overloads of extended- and arbitrary-precision scalar types
871 using std::abs;
872 using std::isnan;
873 using VectorT = Eigen::Vector<value_type, Eigen::Dynamic>;
874 using MatrixT = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>;
875 auto nAug = dimension + 1;
876 // auto nQ = dimension - 1;
877 solution.resize(dimension);
878 // if (nQ > 0) {
881 // Eigen::Matrix<value_type, Eigen::Dynamic, 1> Rhs(nQ), Coeffs(nQ);
882 // Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> B(nQ, nQ);
883 //
884 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>> subspaceMatrix(matrix.data(), dimension,
885 dimension);
886 BAug.block(0, 0, dimension, dimension) = subspaceMatrix;
887 for (size_t i = 0; i < dimension; ++i) {
888 BAug(dimension, i) = BAug(i, dimension) = -1;
889 Rhs(i) = 0;
890 }
891 BAug(dimension, dimension) = 0;
892 Rhs(dimension) = -1;
893 //
894 // molpro::cout << "BAug:" << std::endl << BAug << std::endl;
895 // molpro::cout << "Rhs:" << std::endl << Rhs << std::endl;
896
897 // invert the system, determine extrapolation coefficients.
898 Eigen::JacobiSVD<MatrixT> svd(BAug, Eigen::ComputeThinU | Eigen::ComputeThinV);
899
900 // std::cout << "svd thresholds " << svdThreshold << "," << svd.singularValues().maxCoeff() << std::endl;
901 // std::cout << "singular values " << svd.singularValues().transpose() << std::endl;
902 svd.setThreshold(svdThreshold * svd.singularValues().maxCoeff() * 0);
903 // molpro::cout << "svdThreshold "<<svdThreshold<<std::endl;
904 // molpro::cout << "U\n"<<svd.matrixU()<<std::endl;
905 // molpro::cout << "V\n"<<svd.matrixV()<<std::endl;
906 // molpro::cout << "singularValues\n"<<svd.singularValues()<<std::endl;
907 Coeffs = svd.solve(Rhs).head(dimension);
908 // Coeffs = BAug.fullPivHouseholderQr().solve(Rhs);
909 // molpro::cout << "Coeffs "<<Coeffs.transpose()<<std::endl;
910 if (verbosity > 1)
911 molpro::cout << "Combination of iteration vectors: " << Coeffs.transpose() << std::endl;
912 for (size_t k = 0; k < (size_t)Coeffs.rows(); k++) {
913 if (isnan(abs(Coeffs(k)))) {
914 molpro::cout << "B:" << std::endl << BAug << std::endl;
915 molpro::cout << "Rhs:" << std::endl << Rhs << std::endl;
916 molpro::cout << "Combination of iteration vectors: " << Coeffs.transpose() << std::endl;
917 throw std::overflow_error("NaN detected in DIIS submatrix solution");
918 }
919 solution[k] = Coeffs(k);
920 }
921}
922
923} // namespace detail
924
925template <typename value_type, typename std::enable_if_t<is_complex<value_type>{}, int>>
926void solve_DIIS(std::vector<value_type>& solution, const std::vector<value_type>& matrix, const size_t dimension,
927 real_type_t<value_type> svdThreshold, int verbosity) {
928 detail::solve_DIIS_impl<value_type>(solution, matrix, dimension, svdThreshold, verbosity);
929}
930
931template <typename value_type, typename std::enable_if_t<!is_complex<value_type>{}, std::nullptr_t>>
932void solve_DIIS(std::vector<value_type>& solution, const std::vector<value_type>& matrix, const size_t dimension,
933 real_type_t<value_type> svdThreshold, int verbosity) {
934 detail::solve_DIIS_impl<value_type>(solution, matrix, dimension, svdThreshold, verbosity);
935}
936
937} // namespace molpro::linalg::itsolv
938
939
941
955template <typename value_type, typename value_type_abs>
956auto redundant_parameters(const subspace::Matrix<value_type>& overlap, const size_t oR, const size_t nR,
957 const value_type_abs svd_thresh, Logger& logger) {
958 auto prof = molpro::Profiler::single();
959 prof->start("itsolv::svd_system");
960 logger.trace("redundant_parameters()");
961 auto redundant_params = std::vector<int>{};
962 auto rspace_indices = std::vector<int>(nR);
963 std::iota(std::begin(rspace_indices), std::end(rspace_indices), 0);
964 auto svd = svd_system(overlap.rows(), overlap.cols(),
965 array::Span(const_cast<value_type*>(overlap.data().data()), overlap.size()), svd_thresh, true);
966 prof->stop();
967 prof->start("find redundant parameters");
968 for (const auto& singular_system : svd) {
969 if (!rspace_indices.empty()) {
970 auto rspace_contribution = std::vector<value_type_abs>{};
971 for (auto i : rspace_indices)
972 rspace_contribution.push_back(std::abs(singular_system.v.at(oR + i)));
973 auto it_max = std::max_element(std::begin(rspace_contribution), std::end(rspace_contribution));
974 auto imax = std::distance(std::begin(rspace_contribution), it_max);
976 rspace_indices.erase(std::begin(rspace_indices) + imax);
977 std::stringstream ss;
978 ss << std::setprecision(3) << "redundant parameter found, i = " << redundant_params.back()
979 << ", svd.value = " << singular_system.value
980 << ", svd.v[i] = " << singular_system.v[oR + redundant_params.back()];
981 logger.info(ss.str());
982 }
983 }
984 prof->stop();
985 return redundant_params;
986}
987
988}
989
990#endif // LINEARALGEBRA_SRC_MOLPRO_LINALG_ITERATIVESOLVER_HELPER_IMPLEMENTATION_H_
Non-owning container taking a pointer to the data buffer and its size and exposing routines for itera...
Definition Span.h:31
iterator begin()
Definition Span.h:72
iterator end()
Definition Span.h:76
Definition Logger.h:444
void info(std::string_view message, Ts &&...args) const
Definition Logger.h:507
void trace(std::string_view message, Ts &&...args) const
Definition Logger.h:497
Matrix container that allows simple data access, slicing, copying and resizing without loosing data.
Definition Matrix.h:32
static std::shared_ptr< Profiler > single()
Definition helper-dispatch.h:223
bool hermitian_lowest_by_cholesky(const std::vector< value_type > &matrix, const std::vector< value_type > &metric, size_t dimension, size_t nroots, real_type_t< value_type > svdThreshold, matrix_type &eigenvectors, vector_type &eigenvalues)
The lowest nroots eigenpairs of a hermitian H C = S C E by Cholesky reduction, where that is safe.
Definition helper-implementation.h:399
void solve_DIIS_impl(std::vector< value_type > &solution, const std::vector< value_type > &matrix, const size_t dimension, real_type_t< value_type > svdThreshold, int verbosity)
Implementation of solve_DIIS(), shared by its real and complex overloads.
Definition helper-implementation.h:868
std::list< SVD< value_type > > svd_system_impl(size_t nrows, size_t ncols, const array::Span< value_type > &m, real_type_t< value_type > threshold, bool hermitian, bool reduce_to_rank)
Implementation of svd_system(), shared by its real and complex overloads.
Definition helper-implementation.h:222
void solve_LinearEquations_impl(std::vector< value_type > &solution, std::vector< value_type > &eigenvalues, const std::vector< value_type > &matrix, const std::vector< value_type > &metric, const std::vector< value_type > &rhs, const size_t dimension, size_t nroot, real_type_t< value_type > augmented_hessian, real_type_t< value_type > svdThreshold, int verbosity)
Implementation of solve_LinearEquations(), shared by its real and complex overloads.
Definition helper-implementation.h:738
orthogonalised_subspace< value_type > orthogonalise_subspace(const std::vector< value_type > &matrix, const std::vector< value_type > &metric, size_t dimension, real_type_t< value_type > svdThreshold, int verbosity)
Reduce H C = S C E to the standard eigenproblem Hbar U = U E, whose solution is C = X U.
Definition helper-implementation.h:331
auto redundant_parameters(const subspace::Matrix< value_type > &overlap, const size_t oR, const size_t nR, const value_type_abs svd_thresh, Logger &logger)
Deduces a set of parameters that are redundant due to linear dependencies.
Definition helper-implementation.h:956
4-parameter interpolation of a 1-dimensional function given two points for which function values and ...
Definition helper.h:14
std::list< SVD< value_type > > svd_eigen_bdcsvd(size_t nrows, size_t ncols, const array::Span< value_type > &m, real_type_t< value_type > threshold)
Definition helper-implementation.h:57
std::unique_ptr< LinearEigensystem< R, Q, P > > create_LinearEigensystem(const LinearEigensystemOptions &options, const std::shared_ptr< ArrayHandlers< R, Q, P > > &handlers=std::make_shared< molpro::linalg::itsolv::ArrayHandlers< R, Q, P > >())
Definition SolverFactory.h:111
void solve_LinearEquations(std::vector< value_type > &solution, std::vector< value_type > &eigenvalues, const std::vector< value_type > &matrix, const std::vector< value_type > &metric, const std::vector< value_type > &rhs, size_t dimension, size_t nroot, real_type_t< value_type > augmented_hessian, real_type_t< value_type > svdThreshold, int verbosity)
Definition helper-implementation.h:840
int eigensolver_lapacke_dsyev(std::span< const double > matrix, std::span< double > eigenvectors, std::span< double > eigenvalues, const size_t dimension)
Eigen-decomposition of a real symmetric matrix in double precision.
Definition helper-implementation.h:153
void solve_DIIS(std::vector< value_type > &solution, const std::vector< value_type > &matrix, size_t dimension, real_type_t< value_type > svdThreshold, int verbosity=0)
Definition helper-implementation.h:926
std::list< SVD< value_type > > svd_eigen_jacobi(size_t nrows, size_t ncols, const array::Span< value_type > &m, real_type_t< value_type > threshold)
Definition helper-implementation.h:27
std::list< SVD< value_type > > svd_system(size_t nrows, size_t ncols, const array::Span< value_type > &m, real_type_t< value_type > threshold, bool hermitian=false, bool reduce_to_rank=false)
Performs singular value decomposition and returns SVD objects for singular values less than threshold...
Definition helper-implementation.h:268
size_t get_rank(std::span< const value_type > eigenvalues, value_type threshold)
Definition helper-implementation.h:171
void eigenproblem(std::vector< value_type > &eigenvectors, std::vector< value_type > &eigenvalues, const std::vector< value_type > &matrix, const std::vector< value_type > &metric, size_t dimension, bool hermitian, real_type_t< value_type > svdThreshold, int verbosity, std::vector< std::pair< std::size_t, value_type > > *imag_eval_parts=nullptr, size_t nroots=std::numeric_limits< size_t >::max())
Definition helper-implementation.h:427
void printMatrix(const std::vector< value_type > &, size_t rows, size_t cols, std::string title="", std::ostream &s=molpro::cout)
Definition helper-implementation.h:280
real_type_t< T > real_part(const T &x)
The real part of a scalar; the value itself for a real type.
Definition scalar_traits.h:58
T conjugate(const T &x)
Complex conjugate, staying within the scalar type; the identity for a real type.
Definition scalar_traits.h:47
typename real_type< T >::type real_type_t
The real type underlying T, i.e. T itself for a real type and U for std::complex<U>.
Definition scalar_traits.h:43
Definition scalar_traits.h:19
Stores a singular value and corresponding left and right singular vectors.
Definition helper.h:27
value_type value
Definition helper.h:29
Orders eigenvalues by non-descending real part, then by imaginary part.
Definition helper-implementation.h:294
bool operator()(const std::complex< real_type > &lhs, const std::complex< real_type > &rhs) const
Definition helper-implementation.h:295
A generalised eigenproblem reduced to a standard one by symmetric orthogonalisation of the metric.
Definition helper-implementation.h:311
Eigen::Matrix< value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor > matrix_type
Definition helper-implementation.h:312
matrix_type S
the metric, column-major
Definition helper-implementation.h:313
int rank
the number of directions of the metric that were kept
Definition helper-implementation.h:316
matrix_type transformation
X, of dimension x rank, with X^dagger S X = I.
Definition helper-implementation.h:314
matrix_type Hbar
X^dagger H X, of rank x rank.
Definition helper-implementation.h:315