iterative-solver 0.0
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.h>
8
9#include "Logger.h"
10#include "subspace/Matrix.h"
11
12#include <algorithm>
13#include <cassert>
14#include <cmath>
15#include <complex>
16#include <cstddef>
17#include <iomanip>
18#include <list>
19#include <numeric>
20#include <span>
21#include <type_traits>
22
23namespace molpro::linalg::itsolv {
24
25template <typename value_type>
26std::list<SVD<value_type>> svd_eigen_jacobi(size_t nrows, size_t ncols, const array::Span<value_type>& m,
27 double threshold) {
28 auto mat = Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(m.data(), nrows, ncols);
29#if EIGEN_VERSION_AT_LEAST(3, 4, 90)
30 // Cast to unsigned int to avoid -Wdeprecated-enum-enum-conversion: the two
31 // Eigen flags belong to different enum types but are meant to be ORed here.
32 auto svd = Eigen::JacobiSVD<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(
33 mat, static_cast<unsigned int>(Eigen::ComputeThinV) |
34 static_cast<unsigned int>(Eigen::NoQRPreconditioner));
35#else
36 auto svd = Eigen::JacobiSVD<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>, Eigen::NoQRPreconditioner>(
37 mat, Eigen::ComputeThinV);
38#endif
39 auto svd_system = std::list<SVD<value_type>>{};
40 auto sv = svd.singularValues();
41 for (int i = int(ncols) - 1; i >= 0; --i) {
42 if (std::abs(sv(i)) < threshold) { // TODO: This seems to discard values ABOVE the threshold, not below it. it's
43 auto t = SVD<value_type>{}; // also not scaling this threshold relative to the max singular value - find out why
44 t.value = sv(i);
45 t.v.reserve(ncols);
46 for (size_t j = 0; j < ncols; ++j) {
47 t.v.emplace_back(svd.matrixV()(j, i));
48 }
49 svd_system.emplace_back(std::move(t));
50 }
51 }
52 return svd_system;
53}
54
55template <typename value_type>
56std::list<SVD<value_type>> svd_eigen_bdcsvd(size_t nrows, size_t ncols, const array::Span<value_type>& m,
57 double threshold) {
58 auto mat = Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(m.data(), nrows, ncols);
59 auto svd = Eigen::BDCSVD<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(mat, Eigen::ComputeThinV);
60 auto svd_system = std::list<SVD<value_type>>{};
61 auto sv = svd.singularValues();
62 for (int i = int(ncols) - 1; i >= 0; --i) {
63 if (std::abs(sv(i)) < threshold) {
64 auto t = SVD<value_type>{};
65 t.value = sv(i);
66 t.v.reserve(ncols);
67 for (size_t j = 0; j < ncols; ++j) {
68 t.v.emplace_back(svd.matrixV()(j, i));
69 }
70 svd_system.emplace_back(std::move(t));
71 }
72 }
73 return svd_system;
74}
75
76#if defined HAVE_CBLAS
77template <typename value_type>
78std::list<SVD<value_type>> svd_lapacke_dgesdd(size_t nrows, size_t ncols, const array::Span<value_type>& mat,
79 double threshold) {
80 int info;
81 int m = nrows;
82 int n = ncols;
83 int sdim = std::min(m, n);
84 std::vector<double> sv(sdim), u(nrows * nrows), v(ncols * ncols);
85 info = LAPACKE_dgesdd(LAPACK_ROW_MAJOR, 'A', int(nrows), int(ncols), const_cast<double*>(mat.data()), int(ncols),
86 sv.data(), u.data(), int(nrows), v.data(), int(ncols));
87 auto svd_system = std::list<SVD<value_type>>{};
88 for (int i = int(ncols) - 1; i >= 0; --i) {
89 if (std::abs(sv[i]) < threshold) {
90 auto t = SVD<value_type>{};
91 t.value = sv[i];
92 t.v.reserve(ncols);
93 for (size_t j = 0; j < ncols; ++j) {
94 t.v.emplace_back(v[i * ncols + j]);
95 }
96 svd_system.emplace_back(std::move(t));
97 }
98 }
99 return svd_system;
100}
101
102template <typename value_type>
103std::list<SVD<value_type>> svd_lapacke_dgesvd(size_t nrows, size_t ncols, const array::Span<value_type>& mat,
104 double threshold) {
105 int info;
106 int m = nrows;
107 int n = ncols;
108 int sdim = std::min(m, n);
109 std::vector<double> sv(sdim), u(nrows * nrows), v(ncols * ncols);
110 std::vector<double> superb(sdim - 1);
111 info = LAPACKE_dgesvd(LAPACK_ROW_MAJOR, 'N', 'A', int(nrows), int(ncols), const_cast<double*>(mat.data()), int(ncols),
112 sv.data(), u.data(), int(nrows), v.data(), int(ncols), superb.data());
113 auto svd_system = std::list<SVD<value_type>>{};
114 for (int i = int(ncols) - 1; i >= 0; --i) {
115 if (std::abs(sv[i]) < threshold) {
116 auto t = SVD<value_type>{};
117 t.value = sv[i];
118 t.v.reserve(ncols);
119 for (size_t j = 0; j < ncols; ++j) {
120 t.v.emplace_back(v[i * ncols + j]);
121 }
122 svd_system.emplace_back(std::move(t));
123 }
124 }
125 return svd_system;
126}
127
128#endif
129
130#ifdef MOLPRO
131extern "C" int dsyev_c(char, char, int, double*, int, double*);
132#endif
133
143inline int eigensolver_lapacke_dsyev(std::span<const double> matrix, std::span<double> eigenvectors,
144 std::span<double> eigenvalues, const size_t dimension) {
145
146 // validate input
147 if (eigenvectors.size() != matrix.size()) {
148 throw std::runtime_error("Matrix of eigenvectors and input matrix are not the same size!");
149 }
150
151 if (eigenvectors.size() != dimension * dimension || eigenvalues.size() != dimension) {
152 throw std::runtime_error("Size of eigenvectors/eigenvlaues do not match dimension!");
153 }
154
155 // magic letters
156 // static const char compute_eigenvalues = 'N';
157 static const char compute_eigenvalues_eigenvectors = 'V';
158 // static const char store_upper_triangle = 'U';
159 static const char store_lower_triangle = 'L';
160
161 // copy input matrix (lapack overwrites)
162 std::copy(matrix.begin(), matrix.end(), eigenvectors.begin());
163
164 // set lapack vars
165 lapack_int status;
166 lapack_int leading_dimension = dimension;
167 lapack_int order = dimension;
168
169 // call to lapack
170#ifdef MOLPRO
171 status = dsyev_c(compute_eigenvalues_eigenvectors, store_lower_triangle, order, eigenvectors.data(),
172 leading_dimension, eigenvalues.data());
173#else
174 status = LAPACKE_dsyev(LAPACK_COL_MAJOR, compute_eigenvalues_eigenvectors, store_lower_triangle, order,
175 eigenvectors.data(), leading_dimension, eigenvalues.data());
176#endif
177
178 return status;
179}
180
188inline std::list<SVD<double>> eigensolver_lapacke_dsyev(size_t dimension, std::span<const double> matrix) {
189 std::vector<double> eigvecs(dimension * dimension);
190 std::vector<double> eigvals(dimension);
191
192 // call to lapack
193 int success = eigensolver_lapacke_dsyev(matrix, eigvecs, eigvals, dimension);
194 if (success < 0) {
195 throw std::invalid_argument("Invalid argument of eigensolver_lapacke_dsyev: ");
196 }
197 if (success > 0) {
198 throw std::runtime_error("Lapacke_dsyev (eigensolver) failed to converge. "
199 " elements of an intermediate tridiagonal form did not converge to zero.");
200 }
201
202 auto eigensystem = std::list<SVD<double>>{};
203
204 // populate eigensystem
205 for (int i = dimension - 1; i >= 0;
206 i--) { // note: flipping this axis gives parity with results of eigen::jacobiSVD
207 auto temp_eigenproblem = SVD<double>{};
208 temp_eigenproblem.value = eigvals[i];
209 for (size_t j = 0; j < dimension; j++) {
210 temp_eigenproblem.v.emplace_back(eigvecs[j + (dimension * i)]);
211 }
212 eigensystem.emplace_back(temp_eigenproblem);
213 }
214
215 return eigensystem;
216}
217
225template <typename value_type>
226size_t get_rank(std::span<value_type> eigenvalues, value_type threshold) {
227 if (eigenvalues.size() == 0) {
228 return 0;
229 }
230 value_type max = *max_element(eigenvalues.begin(), eigenvalues.end());
231 value_type threshold_scaled = threshold * max;
232 size_t count =
233 std::count_if(eigenvalues.begin(), eigenvalues.end(), [&](auto const& val) { return val >= threshold_scaled; });
234 return count;
235}
236
244template <typename value_type>
245size_t get_rank(std::list<SVD<value_type>> svd_system, value_type threshold) {
246 // compute max
247 value_type max_value = 0;
248 std::list<SVD<double>>::iterator it;
249 for (it = svd_system.begin(); it != svd_system.end(); it++) {
250 if (it->value > max_value) {
251 max_value = it->value;
252 }
253 }
254 // scale threshold
255 value_type threshold_scaled = threshold * max_value;
256
257 size_t rank = 0;
258 // get rank
259 for (it = svd_system.begin(); it != svd_system.end(); it++) {
260 if (it->value > threshold_scaled) {
261 rank += 1;
262 }
263 }
264 return rank;
265}
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, double threshold,
269 bool hermitian, bool reduce_to_rank) {
270 std::list<SVD<value_type>> svds;
271 assert(m.size() == nrows * ncols);
272 if (m.empty())
273 return {};
274 if (hermitian) {
275 assert(nrows == ncols);
276 svds = eigensolver_lapacke_dsyev(nrows, m);
277 for (auto s = svds.begin(); s != svds.end();)
278 if (s->value > threshold)
279 s = svds.erase(s);
280 else
281 ++s;
282 } else {
283 //#if defined HAVE_LAPACKE
284 // if (nrows > 16)
285 // return svd_lapacke_dgesdd<value_type>(nrows, ncols, m, threshold);
286 // return svd_lapacke_dgesvd<value_type>(nrows, ncols, m, threshold);
287 //#endif
288 svds = svd_eigen_jacobi<value_type>(nrows, ncols, m, threshold);
289 // return svd_eigen_bdcsvd<value_type>(nrows, ncols, m, threshold);
290 }
291
292 // reduce to rank
293 if (reduce_to_rank) {
294 int rank = get_rank(svds, threshold);
295 for (int i = ncols; i > rank; i--) {
296 svds.pop_back();
297 }
298 }
299 return svds;
300}
301
302template <typename value_type, typename std::enable_if_t<is_complex<value_type>{}, int>>
303std::list<SVD<value_type>> svd_system(size_t nrows, size_t ncols, const array::Span<value_type>& m, double threshold,
304 bool hermitian, bool reduce_to_rank) {
305 assert(false); // Complex not implemented here
306 return {};
307}
308
309template <typename value_type>
310void printMatrix(const std::vector<value_type>& m, size_t rows, size_t cols, std::string title, std::ostream& s) {
311 s << title << "\n"
312 << Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(m.data(), rows, cols) << std::endl;
313}
314
315template <typename value_type, typename std::enable_if_t<is_complex<value_type>{}, int>>
316void eigenproblem(std::vector<value_type>& eigenvectors, std::vector<value_type>& eigenvalues,
317 const std::vector<value_type>& matrix, const std::vector<value_type>& metric, size_t dimension,
318 bool hermitian, double svdThreshold, int verbosity, bool condone_complex) {
319 assert(false); // Complex not implemented here
320}
321
322template <typename value_type, typename std::enable_if_t<!is_complex<value_type>{}, std::nullptr_t>>
323void eigenproblem(std::vector<value_type>& eigenvectors, std::vector<value_type>& eigenvalues,
324 const std::vector<value_type>& matrix, const std::vector<value_type>& metric, size_t dimension,
325 bool hermitian, double svdThreshold, int verbosity, bool condone_complex) {
326 using MatrixT = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;
327 using ComplexMatrixT = Eigen::Matrix<std::complex<value_type>, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;
328 using MatrixRowMajT = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
329 using VectorT = Eigen::Vector<value_type, Eigen::Dynamic>;
330 using ComplexVectorT = Eigen::Vector<std::complex<value_type>, Eigen::Dynamic>;
331
332 auto prof = molpro::Profiler::single();
333 prof->start("itsolv::eigenproblem");
334 Eigen::Map<const MatrixRowMajT> HrowMajor(
335 matrix.data(), dimension, dimension);
336 MatrixT H(dimension, dimension);
337 H = HrowMajor;
338 Eigen::Map<const MatrixT> S(metric.data(), dimension, dimension);
339 ComplexMatrixT subspaceEigenvectors;
340 ComplexVectorT subspaceEigenvalues;
341
342 // initialisation of variables
343 VectorT metricEvals(dimension);
344 MatrixT metricEvecs(dimension, dimension);
345 int rank = 0;
346
347 // Perform an eigenvalue decomposition of the metric
348 // Note: Since the metric must necessarily be hermitian (and due to its real-valuedness in this
349 // function therefore symmetric), we can use lapacke_dsyev for this
350 int success = eigensolver_lapacke_dsyev(metric, { metricEvecs.data(), dimension * dimension },
351 { metricEvals.data(), dimension }, dimension);
352 if (success != 0) {
353 throw std::runtime_error("Eigensolver did not converge");
354 }
355 rank = get_rank(std::span(metricEvals.data(), dimension), svdThreshold);
356
357 if (verbosity > 1 && rank < S.cols())
358 molpro::cout << "SVD rank " << rank << " in subspace of dimension " << S.cols() << std::endl;
359 if (verbosity > 2 && rank < S.cols())
360 molpro::cout << "singular values " << metricEvals.transpose() << std::endl;
361
362 // Transform H into a symmetrically orthogonalized basis via (S^{-1/2})^\dagger H S^{-1/2}
363 // taking into account the possibility of rank-deficiency of S (aka: zero SV)
364 // Note that since S is hermitian and positive (semi-)definite, its SVD is equal to its
365 // eigendecomposition
366 auto svmh = metricEvals.head(rank);
367 for (auto k = 0; k < rank; k++) {
368 assert(std::abs(svmh(k)) <= 1e-14 || svmh(k) >= 0); // metric is supposed to be positive (semi-)definite
369 svmh(k) = svmh(k) > 1e-14 ? 1 / std::sqrt(svmh(k)) : 0;
370 }
371 auto Hbar =
372 svmh.asDiagonal() * metricEvecs.leftCols(rank).adjoint() * H * metricEvecs.leftCols(rank) * svmh.asDiagonal();
373
374 // Perform an eigendecomposition of the transformed matrix
375 Eigen::EigenSolver<MatrixT> s(Hbar);
376 subspaceEigenvalues = s.eigenvalues();
377 if (s.eigenvalues().imag().norm() < 1e-10) {
378 // real eigenvalues
379 subspaceEigenvalues = subspaceEigenvalues.real();
380 subspaceEigenvectors = s.eigenvectors();
381 // complex eigenvectors need to be rotated
382 // assume that they come in consecutive pairs
383 for (int i = 0; i < subspaceEigenvectors.cols() - 1; i++) {
384 if (subspaceEigenvectors.col(i).imag().norm() <= 1e-10) {
385 continue;
386 }
387
388 const int j = i + 1;
389 if (std::abs(subspaceEigenvalues(i) - subspaceEigenvalues(j)) >= 1e-10 or
390 subspaceEigenvectors.col(j).imag().norm() <= 1e-10) {
391 continue;
392 }
393
394 // For a real-valued matrix, eigenvectors can always be chosen to be real. If we have a complex eigenvector,
395 // it's complex conjugate must also be an eigenvector with the same eigenvalue. We can combine these two
396 // vectors as either u + u^* = 2 Re(u) or i*(u - u^*) = -2 Im(u).
397 // In other words, the real and imaginary part of u are the corresponding real-valued eigenvectors.
398 subspaceEigenvectors.col(j) = subspaceEigenvectors.col(i).imag() / subspaceEigenvectors.col(i).imag().norm();
399 subspaceEigenvectors.col(i) = subspaceEigenvectors.col(i).real() / subspaceEigenvectors.col(i).real().norm();
400 }
401
402 // Convert eigenvectors back into original basis (minus singular dimensions)
403 subspaceEigenvectors = metricEvecs.leftCols(rank) * svmh.asDiagonal() * subspaceEigenvectors;
404 } else {
405 // complex eigenvalues
406#ifdef __INTEL_COMPILER
407 molpro::cout << "Hbar\n" << Hbar << std::endl;
408 molpro::cout << "Eigenvalues\n" << s.eigenvalues() << std::endl;
409 molpro::cout << "Eigenvectors\n" << s.eigenvectors() << std::endl;
410 throw std::runtime_error("Intel compiler does not support working with complex eigen3 entities properly");
411#endif
412
413 // Convert eigenvectors back into original basis (minus singular dimensions)
414 subspaceEigenvectors = metricEvecs.leftCols(rank) * svmh.asDiagonal() * s.eigenvectors();
415 }
416
417 // Determine order of eigenvalues such that they come in non-descending order of their real part
418 Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> perm(subspaceEigenvalues.size());
419 perm.setIdentity();
420 std::ranges::sort(perm.indices(), std::less<>{}, [&subspaceEigenvalues](auto idx) { return subspaceEigenvalues[idx].real(); });
421
422 // Apply determined order to eigenvalues and -vectors
423 subspaceEigenvectors = subspaceEigenvectors * perm;
424 subspaceEigenvalues = perm.transpose() * subspaceEigenvalues;
425
426
427 // TODO: Need to address the case of near-zero eigenvalues (as below for non-hermitian case) and clean-up
428 // non-hermitian case
429
430 if (!hermitian) {
431 for (auto repeat = 0; repeat < 1; ++repeat)
432 for (Eigen::Index k = 0; k < subspaceEigenvectors.cols(); k++) {
433 if (std::abs(subspaceEigenvalues(k)) < 1e-12) {
434 // special case of zero eigenvalue -- make some real non-zero vector definitely in the null space
435 subspaceEigenvectors.col(k).real() += double(0.3256897) * subspaceEigenvectors.col(k).imag();
436 subspaceEigenvectors.col(k).imag().setZero();
437 }
438
439 auto ovl = subspaceEigenvectors.col(k).dot(S * subspaceEigenvectors.col(k));
440 // S is supposed to be positive (semi-)definite implying that ovl must be a non-negative real number
441 assert(std::abs(ovl.imag()) < 1e-10);
442 assert(ovl.real() >= 0);
443 subspaceEigenvectors.col(k) /= std::sqrt(ovl.real());
444 }
445 }
446
447 // Fix indeterminate phase of eigenvectors by requiring the max component to be positive
448 for (std::size_t i = 0; i < subspaceEigenvectors.cols(); ++i) {
449 const auto &col = subspaceEigenvectors.col(i);
450 auto it = std::ranges::max_element(col, std::less<>{}, [](auto val) { return std::abs(val); });
451 auto idx = std::distance(col.begin(), it);
452 if (subspaceEigenvectors.col(i)[idx].real() < 0) {
453 subspaceEigenvectors.col(i) *= -1;
454 }
455 }
456
457 if (condone_complex) {
458 for (Eigen::Index root = 0; root < Hbar.cols(); ++root) {
459 if (subspaceEigenvalues(root).imag() != 0) {
460 // Same procedure as we did for the metric's eigenvectors further up
461 subspaceEigenvalues(root) = subspaceEigenvalues(root + 1) = subspaceEigenvalues(root).real();
462 subspaceEigenvectors.col(root) = subspaceEigenvectors.col(root).real();
463 subspaceEigenvectors.col(root + 1) = subspaceEigenvectors.col(root + 1).imag();
464 ++root;
465 }
466 }
467 }
468
469 if ((subspaceEigenvectors - subspaceEigenvectors.real()).norm() > 1e-10 or
470 (subspaceEigenvalues - subspaceEigenvalues.real()).norm() > 1e-10) {
471 throw std::runtime_error("unexpected complex solution found");
472 }
473
474 eigenvectors.resize(dimension * Hbar.cols());
475 eigenvalues.resize(Hbar.cols());
476
477 Eigen::Map<MatrixT>(eigenvectors.data(), dimension, Hbar.cols()) =
478 subspaceEigenvectors.real();
479 Eigen::Map<VectorT> ev(eigenvalues.data(), Hbar.cols());
480 ev = subspaceEigenvalues.real();
481
482 prof->stop();
483}
484
485template <typename value_type, typename std::enable_if_t<is_complex<value_type>{}, int>>
486void solve_LinearEquations(std::vector<value_type>& solution, std::vector<value_type>& eigenvalues,
487 const std::vector<value_type>& matrix, const std::vector<value_type>& metric,
488 const std::vector<value_type>& rhs, const size_t dimension, size_t nroot,
489 double augmented_hessian, double svdThreshold, int verbosity) {
490 assert(false); // Complex not implemented here
491}
492
493template <typename value_type, typename std::enable_if_t<!is_complex<value_type>{}, std::nullptr_t>>
494void solve_LinearEquations(std::vector<value_type>& solution, std::vector<value_type>& eigenvalues,
495 const std::vector<value_type>& matrix, const std::vector<value_type>& metric,
496 const std::vector<value_type>& rhs, const size_t dimension, size_t nroot,
497 double augmented_hessian, double svdThreshold, int verbosity) {
498 const Eigen::Index nX = dimension;
499 solution.resize(nX * nroot);
500 // std::cout << "augmented_hessian "<<augmented_hessian<<std::endl;
501 if (augmented_hessian > 0) { // Augmented hessian
502 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> subspaceMatrix;
503 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> subspaceOverlap;
504 subspaceMatrix.conservativeResize(nX + 1, nX + 1);
505 subspaceOverlap.conservativeResize(nX + 1, nX + 1);
506 subspaceMatrix.block(0, 0, nX, nX) =
507 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(matrix.data(), nX, nX);
508 subspaceOverlap.block(0, 0, nX, nX) =
509 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(metric.data(), nX, nX);
510 eigenvalues.resize(nroot);
511 for (size_t root = 0; root < nroot; root++) {
512 for (Eigen::Index i = 0; i < nX; i++) {
513 subspaceMatrix(i, nX) = subspaceMatrix(nX, i) = -augmented_hessian * rhs[i + nX * root];
514 subspaceOverlap(i, nX) = subspaceOverlap(nX, i) = 0;
515 }
516 subspaceMatrix(nX, nX) = 0;
517 subspaceOverlap(nX, nX) = 1;
518 // std::cout << "subspace augmented hessian subspaceMatrix\n"<<subspaceMatrix<<std::endl;
519 // std::cout << "subspace augmented hessian subspaceOverlap\n"<<subspaceOverlap<<std::endl;
520
521 Eigen::GeneralizedEigenSolver<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>> s(subspaceMatrix,
522 subspaceOverlap);
523 auto eval = s.eigenvalues();
524 auto evec = s.eigenvectors();
525 Eigen::Index imax = 0;
526 for (Eigen::Index i = 0; i < nX + 1; i++)
527 if (eval(i).real() < eval(imax).real())
528 imax = i;
529 eigenvalues[root] = eval(imax).real();
530 auto Solution = evec.col(imax).real().head(nX) / (augmented_hessian * evec.real()(nX, imax));
531 for (auto k = 0; k < nX; k++)
532 solution[k + nX * root] = Solution(k);
533 // std::cout << "subspace augmented hessian solution\n"<<Solution<<std::endl;
534 }
535 } else { // straight solution of linear equations
536 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> subspaceMatrixR(
537 matrix.data(), nX, nX);
538 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> RHS_R(rhs.data(), nX,
539 nroot);
540 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> subspaceMatrix = subspaceMatrixR;
541 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> RHS = RHS_R;
542 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> Solution;
543// std::cout << "solve_LinearEquations RHS_R\n"<<RHS_R<<std::endl;
544// for (size_t i=0; i<RHS_R.cols()*RHS_R.rows(); ++i)
545// std::cout << " "<<RHS_R.data()[i];
546// std::cout << std::endl;
547// std::cout << "solve_LinearEquations RHS\n"<<RHS<<std::endl;
548// for (size_t i=0; i<RHS.cols()*RHS.rows(); ++i)
549// std::cout << " "<<RHS.data()[i];
550// std::cout << std::endl;
551 Solution = subspaceMatrix.householderQr().solve(RHS);
552 // std::cout << "subspace linear equations solution\n"<<Solution<<std::endl;
553 for (size_t root = 0; root < nroot; root++)
554 for (auto k = 0; k < nX; k++)
555 solution[k + nX * root] = Solution(k, root);
556 }
557}
558
559template <typename value_type, typename std::enable_if_t<!is_complex<value_type>{}, std::nullptr_t>>
560void solve_DIIS(std::vector<value_type>& solution, const std::vector<value_type>& matrix, const size_t dimension,
561 double svdThreshold, int verbosity) {
562 auto nAug = dimension + 1;
563 // auto nQ = dimension - 1;
564 solution.resize(dimension);
565 // if (nQ > 0) {
566 Eigen::VectorXd Rhs(nAug), Coeffs(nAug);
567 Eigen::MatrixXd BAug(nAug, nAug);
568 // Eigen::Matrix<value_type, Eigen::Dynamic, 1> Rhs(nQ), Coeffs(nQ);
569 // Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> B(nQ, nQ);
570 //
571 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>> subspaceMatrix(matrix.data(), dimension,
572 dimension);
573 BAug.block(0, 0, dimension, dimension) = subspaceMatrix;
574 for (size_t i = 0; i < dimension; ++i) {
575 BAug(dimension, i) = BAug(i, dimension) = -1;
576 Rhs(i) = 0;
577 }
578 BAug(dimension, dimension) = 0;
579 Rhs(dimension) = -1;
580 //
581 // molpro::cout << "BAug:" << std::endl << BAug << std::endl;
582 // molpro::cout << "Rhs:" << std::endl << Rhs << std::endl;
583
584 // invert the system, determine extrapolation coefficients.
585 Eigen::JacobiSVD<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>> svd(BAug, Eigen::ComputeThinU |
586 Eigen::ComputeThinV);
587
588 // std::cout << "svd thresholds " << svdThreshold << "," << svd.singularValues().maxCoeff() << std::endl;
589 // std::cout << "singular values " << svd.singularValues().transpose() << std::endl;
590 svd.setThreshold(svdThreshold * svd.singularValues().maxCoeff() * 0);
591 // molpro::cout << "svdThreshold "<<svdThreshold<<std::endl;
592 // molpro::cout << "U\n"<<svd.matrixU()<<std::endl;
593 // molpro::cout << "V\n"<<svd.matrixV()<<std::endl;
594 // molpro::cout << "singularValues\n"<<svd.singularValues()<<std::endl;
595 Coeffs = svd.solve(Rhs).head(dimension);
596 // Coeffs = BAug.fullPivHouseholderQr().solve(Rhs);
597 // molpro::cout << "Coeffs "<<Coeffs.transpose()<<std::endl;
598 if (verbosity > 1)
599 molpro::cout << "Combination of iteration vectors: " << Coeffs.transpose() << std::endl;
600 for (size_t k = 0; k < (size_t)Coeffs.rows(); k++) {
601 if (std::isnan(std::abs(Coeffs(k)))) {
602 molpro::cout << "B:" << std::endl << BAug << std::endl;
603 molpro::cout << "Rhs:" << std::endl << Rhs << std::endl;
604 molpro::cout << "Combination of iteration vectors: " << Coeffs.transpose() << std::endl;
605 throw std::overflow_error("NaN detected in DIIS submatrix solution");
606 }
607 solution[k] = Coeffs(k);
608 }
609}
610} // namespace molpro::linalg::itsolv
611
612
614
628template <typename value_type, typename value_type_abs>
629auto redundant_parameters(const subspace::Matrix<value_type>& overlap, const size_t oR, const size_t nR,
630 const value_type_abs svd_thresh, Logger& logger) {
631 auto prof = molpro::Profiler::single();
632 prof->start("itsolv::svd_system");
633 logger.trace("redundant_parameters()");
634 auto redundant_params = std::vector<int>{};
635 auto rspace_indices = std::vector<int>(nR);
636 std::iota(std::begin(rspace_indices), std::end(rspace_indices), 0);
637 auto svd = svd_system(overlap.rows(), overlap.cols(),
638 array::Span(const_cast<value_type*>(overlap.data().data()), overlap.size()), svd_thresh, true);
639 prof->stop();
640 prof->start("find redundant parameters");
641 for (const auto& singular_system : svd) {
642 if (!rspace_indices.empty()) {
643 auto rspace_contribution = std::vector<value_type_abs>{};
644 for (auto i : rspace_indices)
645 rspace_contribution.push_back(std::abs(singular_system.v.at(oR + i)));
646 auto it_min = std::max_element(std::begin(rspace_contribution), std::end(rspace_contribution));
647 auto imin = std::distance(std::begin(rspace_contribution), it_min);
648 redundant_params.push_back(rspace_indices[imin]);
649 rspace_indices.erase(std::begin(rspace_indices) + imin);
650 std::stringstream ss;
651 ss << std::setprecision(3) << "redundant parameter found, i = " << redundant_params.back()
652 << ", svd.value = " << singular_system.value
653 << ", svd.v[i] = " << singular_system.v[oR + redundant_params.back()];
654 logger.info(ss.str());
655 }
656 }
657 prof->stop();
658 return redundant_params;
659}
660
661}
662
663#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
bool empty() const
Definition: Span.h:79
size_type size() const
Definition: Span.h:77
iterator data()
Definition: Span.h:66
Definition: Logger.h:428
void info(std::string_view message, Ts &&...args) const
Definition: Logger.h:491
void trace(std::string_view message, Ts &&...args) const
Definition: Logger.h:481
static std::shared_ptr< Profiler > single()
Definition: helper-implementation.h:613
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:629
4-parameter interpolation of a 1-dimensional function given two points for which function values and ...
Definition: helper.h:11
std::list< SVD< value_type > > svd_eigen_bdcsvd(size_t nrows, size_t ncols, const array::Span< value_type > &m, double threshold)
Definition: helper-implementation.h:56
int eigensolver_lapacke_dsyev(std::span< const double > matrix, std::span< double > eigenvectors, std::span< double > eigenvalues, const size_t dimension)
Definition: helper-implementation.h:143
std::list< SVD< value_type > > svd_system(size_t nrows, size_t ncols, const array::Span< value_type > &m, double 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
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, double augmented_hessian, double svdThreshold, int verbosity)
Definition: helper-implementation.h:486
size_t get_rank(std::vector< value_type > eigenvalues, value_type threshold)
void solve_DIIS(std::vector< value_type > &solution, const std::vector< value_type > &matrix, size_t dimension, double svdThreshold, int verbosity=0)
Definition: helper-implementation.h:560
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, double svdThreshold, int verbosity, bool condone_complex)
Definition: helper-implementation.h:316
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:310
std::list< SVD< value_type > > svd_eigen_jacobi(size_t nrows, size_t ncols, const array::Span< value_type > &m, double threshold)
Definition: helper-implementation.h:26
Stores a singular value and corresponding left and right singular vectors.
Definition: helper.h:20
value_type value
Definition: helper.h:22