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#include <cmath>
5#include <cstddef>
6#include <cassert>
7#include <molpro/Profiler.h>
8#include <molpro/lapacke.h>
9#include <molpro/linalg/itsolv/helper.h>
10
11namespace molpro::linalg::itsolv {
12
13template <typename value_type>
14std::list<SVD<value_type>> svd_eigen_jacobi(size_t nrows, size_t ncols, const array::Span<value_type>& m,
15 double threshold) {
16 auto mat = Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(m.data(), nrows, ncols);
17 auto svd = Eigen::JacobiSVD<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>, Eigen::NoQRPreconditioner>(
18 mat, Eigen::ComputeThinV);
19 auto svd_system = std::list<SVD<value_type>>{};
20 auto sv = svd.singularValues();
21 for (int i = int(ncols) - 1; i >= 0; --i) {
22 if (std::abs(sv(i)) < threshold) { // TODO: This seems to discard values ABOVE the threshold, not below it. it's
23 auto t = SVD<value_type>{}; // also not scaling this threshold relative to the max singular value - find out why
24 t.value = sv(i);
25 t.v.reserve(ncols);
26 for (size_t j = 0; j < ncols; ++j) {
27 t.v.emplace_back(svd.matrixV()(j, i));
28 }
29 svd_system.emplace_back(std::move(t));
30 }
31 }
32 return svd_system;
33}
34
35template <typename value_type>
36std::list<SVD<value_type>> svd_eigen_bdcsvd(size_t nrows, size_t ncols, const array::Span<value_type>& m,
37 double threshold) {
38 auto mat = Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(m.data(), nrows, ncols);
39 auto svd = Eigen::BDCSVD<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(mat, Eigen::ComputeThinV);
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) {
44 auto t = SVD<value_type>{};
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
56#if defined HAVE_CBLAS
57template <typename value_type>
58std::list<SVD<value_type>> svd_lapacke_dgesdd(size_t nrows, size_t ncols, const array::Span<value_type>& mat,
59 double threshold) {
60 int info;
61 int m = nrows;
62 int n = ncols;
63 int sdim = std::min(m, n);
64 std::vector<double> sv(sdim), u(nrows * nrows), v(ncols * ncols);
65 info = LAPACKE_dgesdd(LAPACK_ROW_MAJOR, 'A', int(nrows), int(ncols), const_cast<double*>(mat.data()), int(ncols),
66 sv.data(), u.data(), int(nrows), v.data(), int(ncols));
67 auto svd_system = std::list<SVD<value_type>>{};
68 for (int i = int(ncols) - 1; i >= 0; --i) {
69 if (std::abs(sv[i]) < threshold) {
70 auto t = SVD<value_type>{};
71 t.value = sv[i];
72 t.v.reserve(ncols);
73 for (size_t j = 0; j < ncols; ++j) {
74 t.v.emplace_back(v[i * ncols + j]);
75 }
76 svd_system.emplace_back(std::move(t));
77 }
78 }
79 return svd_system;
80}
81
82template <typename value_type>
83std::list<SVD<value_type>> svd_lapacke_dgesvd(size_t nrows, size_t ncols, const array::Span<value_type>& mat,
84 double threshold) {
85 int info;
86 int m = nrows;
87 int n = ncols;
88 int sdim = std::min(m, n);
89 std::vector<double> sv(sdim), u(nrows * nrows), v(ncols * ncols);
90 double superb[sdim - 1];
91 info = LAPACKE_dgesvd(LAPACK_ROW_MAJOR, 'N', 'A', int(nrows), int(ncols), const_cast<double*>(mat.data()), int(ncols),
92 sv.data(), u.data(), int(nrows), v.data(), int(ncols), superb);
93 auto svd_system = std::list<SVD<value_type>>{};
94 for (int i = int(ncols) - 1; i >= 0; --i) {
95 if (std::abs(sv[i]) < threshold) {
96 auto t = SVD<value_type>{};
97 t.value = sv[i];
98 t.v.reserve(ncols);
99 for (size_t j = 0; j < ncols; ++j) {
100 t.v.emplace_back(v[i * ncols + j]);
101 }
102 svd_system.emplace_back(std::move(t));
103 }
104 }
105 return svd_system;
106}
107
108#endif
109
110#ifdef MOLPRO
111extern "C" int dsyev_c(char, char, int, double*, int, double*);
112#endif
113
123inline int eigensolver_lapacke_dsyev(const std::vector<double>& matrix, std::vector<double>& eigenvectors,
124 std::vector<double>& eigenvalues, const size_t dimension) {
125
126 // validate input
127 if (eigenvectors.size() != matrix.size()) {
128 throw std::runtime_error("Matrix of eigenvectors and input matrix are not the same size!");
129 }
130
131 if (eigenvectors.size() != dimension * dimension || eigenvalues.size() != dimension) {
132 throw std::runtime_error("Size of eigenvectors/eigenvlaues do not match dimension!");
133 }
134
135 // magic letters
136 // static const char compute_eigenvalues = 'N';
137 static const char compute_eigenvalues_eigenvectors = 'V';
138 // static const char store_upper_triangle = 'U';
139 static const char store_lower_triangle = 'L';
140
141 // copy input matrix (lapack overwrites)
142 std::copy(matrix.begin(), matrix.end(), eigenvectors.begin());
143
144 // set lapack vars
145 lapack_int status;
146 lapack_int leading_dimension = dimension;
147 lapack_int order = dimension;
148
149 // call to lapack
150#ifdef MOLPRO
151 status = dsyev_c(compute_eigenvalues_eigenvectors, store_lower_triangle, order, eigenvectors.data(),
152 leading_dimension, eigenvalues.data());
153#else
154 status = LAPACKE_dsyev(LAPACK_COL_MAJOR, compute_eigenvalues_eigenvectors, store_lower_triangle, order,
155 eigenvectors.data(), leading_dimension, eigenvalues.data());
156#endif
157
158 return status;
159}
160
168inline std::list<SVD<double>> eigensolver_lapacke_dsyev(size_t dimension, std::vector<double>& matrix) {
169 std::vector<double> eigvecs(dimension * dimension);
170 std::vector<double> eigvals(dimension);
171
172 // call to lapack
173 int success = eigensolver_lapacke_dsyev(matrix, eigvecs, eigvals, dimension);
174 if (success < 0) {
175 throw std::invalid_argument("Invalid argument of eigensolver_lapacke_dsyev: ");
176 }
177 if (success > 0) {
178 throw std::runtime_error("Lapacke_dsyev (eigensolver) failed to converge. "
179 " elements of an intermediate tridiagonal form did not converge to zero.");
180 }
181
182 auto eigensystem = std::list<SVD<double>>{};
183
184 // populate eigensystem
185 for (int i = dimension - 1; i >= 0;
186 i--) { // note: flipping this axis gives parity with results of eigen::jacobiSVD
187 auto temp_eigenproblem = SVD<double>{};
188 temp_eigenproblem.value = eigvals[i];
189 for (size_t j = 0; j < dimension; j++) {
190 temp_eigenproblem.v.emplace_back(eigvecs[j + (dimension * i)]);
191 }
192 eigensystem.emplace_back(temp_eigenproblem);
193 }
194
195 return eigensystem;
196}
197
206inline std::list<SVD<double>> eigensolver_lapacke_dsyev(size_t dimension,
208 // TODO: this should be the other way around, eigensolver_lapacke_dsyev should take a span by default and this should
209 // wrap it with a vector
210 std::vector<double> v;
211 v.insert(v.begin(), matrix.begin(), matrix.end());
212 return eigensolver_lapacke_dsyev(dimension, v);
213}
214
222template <typename value_type>
223size_t get_rank(std::vector<value_type> eigenvalues, value_type threshold) {
224 if (eigenvalues.size() == 0) {
225 return 0;
226 }
227 value_type max = *max_element(eigenvalues.begin(), eigenvalues.end());
228 value_type threshold_scaled = threshold * max;
229 size_t count =
230 std::count_if(eigenvalues.begin(), eigenvalues.end(), [&](auto const& val) { return val >= threshold_scaled; });
231 return count;
232}
233
241template <typename value_type>
242size_t get_rank(std::list<SVD<value_type>> svd_system, value_type threshold) {
243 // compute max
244 value_type max_value = 0;
245 std::list<SVD<double>>::iterator it;
246 for (it = svd_system.begin(); it != svd_system.end(); it++) {
247 if (it->value > max_value) {
248 max_value = it->value;
249 }
250 }
251 // scale threshold
252 value_type threshold_scaled = threshold * max_value;
253
254 size_t rank = 0;
255 // get rank
256 for (it = svd_system.begin(); it != svd_system.end(); it++) {
257 if (it->value > threshold_scaled) {
258 rank += 1;
259 }
260 }
261 return rank;
262}
263
264template <typename value_type, typename std::enable_if_t<!is_complex<value_type>{}, std::nullptr_t>>
265std::list<SVD<value_type>> svd_system(size_t nrows, size_t ncols, const array::Span<value_type>& m, double threshold,
266 bool hermitian, bool reduce_to_rank) {
267 std::list<SVD<value_type>> svds;
268 assert(m.size() == nrows * ncols);
269 if (m.empty())
270 return {};
271 if (hermitian) {
272 assert(nrows == ncols);
273 svds = eigensolver_lapacke_dsyev(nrows, m);
274 for (auto s = svds.begin(); s != svds.end();)
275 if (s->value > threshold)
276 s = svds.erase(s);
277 else
278 ++s;
279 } else {
280 //#if defined HAVE_LAPACKE
281 // if (nrows > 16)
282 // return svd_lapacke_dgesdd<value_type>(nrows, ncols, m, threshold);
283 // return svd_lapacke_dgesvd<value_type>(nrows, ncols, m, threshold);
284 //#endif
285 svds = svd_eigen_jacobi<value_type>(nrows, ncols, m, threshold);
286 // return svd_eigen_bdcsvd<value_type>(nrows, ncols, m, threshold);
287 }
288
289 // reduce to rank
290 if (reduce_to_rank) {
291 int rank = get_rank(svds, threshold);
292 for (int i = ncols; i > rank; i--) {
293 svds.pop_back();
294 }
295 }
296 return svds;
297}
298
299template <typename value_type, typename std::enable_if_t<is_complex<value_type>{}, int>>
300std::list<SVD<value_type>> svd_system(size_t nrows, size_t ncols, const array::Span<value_type>& m, double threshold,
301 bool hermitian, bool reduce_to_rank) {
302 assert(false); // Complex not implemented here
303 return {};
304}
305
306template <typename value_type>
307void printMatrix(const std::vector<value_type>& m, size_t rows, size_t cols, std::string title, std::ostream& s) {
308 s << title << "\n"
309 << Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(m.data(), rows, cols) << std::endl;
310}
311
312template <typename value_type, typename std::enable_if_t<is_complex<value_type>{}, int>>
313void eigenproblem(std::vector<value_type>& eigenvectors, std::vector<value_type>& eigenvalues,
314 const std::vector<value_type>& matrix, const std::vector<value_type>& metric, size_t dimension,
315 bool hermitian, double svdThreshold, int verbosity, bool condone_complex) {
316 assert(false); // Complex not implemented here
317}
318
319template <typename value_type, typename std::enable_if_t<!is_complex<value_type>{}, std::nullptr_t>>
320void eigenproblem(std::vector<value_type>& eigenvectors, std::vector<value_type>& eigenvalues,
321 const std::vector<value_type>& matrix, const std::vector<value_type>& metric, size_t dimension,
322 bool hermitian, double svdThreshold, int verbosity, bool condone_complex) {
323 auto prof = molpro::Profiler::single();
324 prof->start("itsolv::eigenproblem");
325 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> HrowMajor(
326 matrix.data(), dimension, dimension);
327 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> H(dimension, dimension);
328 H = HrowMajor;
329 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>> S(metric.data(), dimension, dimension);
330 Eigen::MatrixXcd subspaceEigenvectors; // FIXME templating
331 Eigen::VectorXcd subspaceEigenvalues; // FIXME templating
332 // Eigen::GeneralizedEigenSolver<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>> s(H, S);
333
334 // initialisation of variables
335 Eigen::VectorXd singularValues;
336 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> matrixV;
337 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> matrixU;
338 std::vector<double> eigvecs;
339 std::vector<double> eigvals;
340 int rank;
341
342 // if the matrix is hermitian, we can use lapacke_dsyev
343 if (hermitian) {
344 eigvecs.resize(dimension * dimension);
345 eigvals.resize(dimension);
346 int success = eigensolver_lapacke_dsyev(metric, eigvecs, eigvals, dimension);
347 if (success != 0) {
348 throw std::runtime_error("Eigensolver did not converge");
349 }
350 singularValues = Eigen::Map<Eigen::VectorXd>(eigvals.data(), dimension);
351 matrixV = Eigen::Map<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>>(
352 eigvecs.data(), dimension, dimension);
353 matrixU = Eigen::Map<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>>(
354 eigvecs.data(), dimension, dimension);
355 rank = get_rank(eigvals, svdThreshold);
356 } else {
357 Eigen::JacobiSVD<Eigen::MatrixXd> svd(S, Eigen::ComputeThinU | Eigen::ComputeThinV);
358 singularValues = svd.singularValues();
359 matrixV = svd.matrixV();
360 matrixU = svd.matrixU();
361 rank = svd.rank();
362 }
363
364 // svd.setThreshold(svdThreshold);
365 // molpro::cout << "singular values of overlap " << svd.singularValues().transpose() << std::endl;
366 // auto Hbar = svd.solve(H);
367 if (verbosity > 1 && rank < S.cols())
368 molpro::cout << "SVD rank " << rank << " in subspace of dimension " << S.cols() << std::endl;
369 if (verbosity > 2 && rank < S.cols())
370 molpro::cout << "singular values " << singularValues.transpose() << std::endl;
371 auto svmh = singularValues.head(rank);
372 for (auto k = 0; k < rank; k++)
373 svmh(k) = svmh(k) > 1e-14 ? 1 / std::sqrt(svmh(k)) : 0;
374 auto Hbar =
375 (svmh.asDiagonal()) * (matrixU.leftCols(rank).adjoint()) * H * matrixV.leftCols(rank) * (svmh.asDiagonal());
376 // std::cout << "\n\nHbar: \n" << Hbar << "\n\n";
377 // molpro::cout << "S\n"<<S<<std::endl;
378 // molpro::cout << "S singular values"<<(Eigen::DiagonalMatrix<value_type, Eigen::Dynamic,
379 // Eigen::Dynamic>(svd.singularValues().head(svd.rank())))<<std::endl; molpro::cout << "S inverse singular
380 // values"<<Eigen::DiagonalMatrix<value_type,
381 // Eigen::Dynamic>(svd.singularValues().head(svd.rank())).inverse()<<std::endl; molpro::cout << "S singular
382 // values"<<sv<<std::endl; molpro::cout << "H\n"<<H<<std::endl; molpro::cout << "Hbar\n"<<Hbar<<std::endl;
383 Eigen::EigenSolver<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>> s(Hbar);
384 // molpro::cout << "s.eigenvectors()\n"<<s.eigenvectors()<<std::endl;
385 subspaceEigenvalues = s.eigenvalues();
386 if (s.eigenvalues().imag().norm() < 1e-10) { // real eigenvalues
387 // molpro::cout << "eigenvalues near-enough real" << std::endl;
388 subspaceEigenvalues = subspaceEigenvalues.real();
389 subspaceEigenvectors = s.eigenvectors();
390 // complex eigenvectors need to be rotated
391 // assume that they come in consecutive pairs
392 for (int i = 0; i < subspaceEigenvectors.cols(); i++) {
393 if (subspaceEigenvectors.col(i).imag().norm() > 1e-10) {
394 int j = i + 1;
395 if (std::abs(subspaceEigenvalues(i) - subspaceEigenvalues(j)) < 1e-10 and
396 subspaceEigenvectors.col(j).imag().norm() > 1e-10) {
397 subspaceEigenvectors.col(j) = subspaceEigenvectors.col(i).imag() / subspaceEigenvectors.col(i).imag().norm();
398 subspaceEigenvectors.col(i) = subspaceEigenvectors.col(i).real() / subspaceEigenvectors.col(i).real().norm();
399 }
400 }
401 }
402 subspaceEigenvectors = matrixV.leftCols(rank) * svmh.asDiagonal() * subspaceEigenvectors;
403 } else { // complex eigenvectors
404// molpro::cout << "eigenvalues not near-enough real"<<std::endl;
405// molpro::cout << "s.eigenvalues() "<< s.eigenvalues().transpose()<<std::endl;
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 subspaceEigenvectors = matrixV.leftCols(rank) * svmh.asDiagonal() * s.eigenvectors();
413 // std::cout << "subspaceEigenvectors\n" << subspaceEigenvectors << std::endl;
414 }
415
416 {
417 // sort
418 auto eigval = subspaceEigenvalues;
419 auto eigvec = subspaceEigenvectors;
420 std::vector<Eigen::Index> map;
421 for (Eigen::Index k = 0; k < Hbar.cols(); k++) {
422 Eigen::Index ll;
423 for (ll = 0; std::count(map.begin(), map.end(), ll) != 0; ll++)
424 ;
425 for (Eigen::Index l = 0; l < Hbar.cols(); l++) {
426 if (std::count(map.begin(), map.end(), l) == 0) {
427 if (eigval(l).real() < eigval(ll).real())
428 ll = l;
429 }
430 }
431 map.push_back(ll);
432 subspaceEigenvalues(k) = eigval(ll);
433 // molpro::cout << "new sorted eigenvalue "<<k<<", "<<ll<<", "<<eigval(ll)<<std::endl;
434 // molpro::cout << eigvec.col(ll)<<std::endl;
435 subspaceEigenvectors.col(k) = eigvec.col(ll);
436 double maxcomp =0;
437 for (Eigen::Index l = 0; l < Hbar.cols(); l++) {
438 if (std::abs(subspaceEigenvectors.col(k)[l].real()) > std::abs(subspaceEigenvectors.col(k)[maxcomp].real()))
439 maxcomp = l;
440 }
441 if (subspaceEigenvectors.col(k)[maxcomp].real() < 0)
442 subspaceEigenvectors.col(k) = - subspaceEigenvectors.col(k);
443 }
444 }
445
446 // TODO: Need to address the case of near-zero eigenvalues (as below for non-hermitian case) and clean-up
447 // non-hermitian case
448
449 // molpro::cout << "sorted eigenvalues\n"<<subspaceEigenvalues<<std::endl;
450 // molpro::cout << "sorted eigenvectors\n"<<subspaceEigenvectors<<std::endl;
451 // molpro::cout << "hermitian="<<hermitian<<std::endl;
452 if (!hermitian) {
453 Eigen::MatrixXcd ovlTimesVec(subspaceEigenvectors.cols(), subspaceEigenvectors.rows()); // FIXME templating
454 for (auto repeat = 0; repeat < 3; ++repeat)
455 for (Eigen::Index k = 0; k < subspaceEigenvectors.cols(); k++) {
456 if (std::abs(subspaceEigenvalues(k)) <
457 1e-12) { // special case of zero eigenvalue -- make some real non-zero vector definitely in the null space
458 subspaceEigenvectors.col(k).real() += double(0.3256897) * subspaceEigenvectors.col(k).imag();
459 subspaceEigenvectors.col(k).imag().setZero();
460 }
461 if (hermitian)
462 for (Eigen::Index l = 0; l < k; l++) {
463 // auto ovl =
464 // (subspaceEigenvectors.col(l).adjoint() * m_subspaceOverlap * subspaceEigenvectors.col(k))(
465 // 0, 0); (ovlTimesVec.row(l) * subspaceEigenvectors.col(k))(0,0);
466 // ovlTimesVec.row(l).dot(subspaceEigenvectors.col(k));
467 // auto norm =
468 // (subspaceEigenvectors.col(l).adjoint() * subspaceOverlap * subspaceEigenvectors.col(l))(
469 // 0,
470 // 0);
471 // molpro::cout << "k="<<k<<", l="<<l<<", ovl="<<ovl<<" norm="<<norm<<std::endl;
472 // molpro::cout << subspaceEigenvectors.col(k).transpose()<<std::endl;
473 // molpro::cout << subspaceEigenvectors.col(l).transpose()<<std::endl;
474 subspaceEigenvectors.col(k) -= subspaceEigenvectors.col(l) * // ovl;// / norm;
475 ovlTimesVec.row(l).dot(subspaceEigenvectors.col(k));
476 // molpro::cout<<"immediately after projection " << k<<l<<" "<<
477 // (subspaceEigenvectors.col(l).adjoint() * subspaceOverlap * subspaceEigenvectors.col(k))( 0,
478 // 0)<<std::endl;
479 }
480 // for (Eigen::Index l = 0; l < k; l++) molpro::cout<<"after projection loop " << k<<l<<" "<<
481 // (subspaceEigenvectors.col(l).adjoint() * subspaceOverlap * subspaceEigenvectors.col(k))( 0,
482 // 0)<<std::endl; molpro::cout <<
483 // "eigenvector"<<std::endl<<subspaceEigenvectors.col(k).adjoint()<<std::endl;
484 auto ovl =
485 // (subspaceEigenvectors.col(k).adjoint() * subspaceOverlap *
486 // subspaceEigenvectors.col(k))(0,0);
487 subspaceEigenvectors.col(k).adjoint().dot(S * subspaceEigenvectors.col(k));
488 subspaceEigenvectors.col(k) /= std::sqrt(ovl.real());
489 ovlTimesVec.row(k) = subspaceEigenvectors.col(k).adjoint() * S;
490 // for (Eigen::Index l = 0; l < k; l++)
491 // molpro::cout<<"after normalisation " << k<<l<<" "<< (subspaceEigenvectors.col(l).adjoint() *
492 // subspaceOverlap * subspaceEigenvectors.col(k))( 0, 0)<<std::endl; molpro::cout <<
493 // "eigenvector"<<std::endl<<subspaceEigenvectors.col(k).adjoint()<<std::endl;
494 // phase
495 Eigen::Index lmax = 0;
496 for (Eigen::Index l = 0; l < subspaceEigenvectors.rows(); l++) {
497 if (std::abs(subspaceEigenvectors(l, k)) > std::abs(subspaceEigenvectors(lmax, k)))
498 lmax = l;
499 }
500 if (subspaceEigenvectors(lmax, k).real() < 0)
501 subspaceEigenvectors.col(k) = -subspaceEigenvectors.col(k);
502 // for (Eigen::Index l = 0; l < k; l++)
503 // molpro::cout << k<<l<<" "<<
504 // (subspaceEigenvectors.col(l).adjoint() * subspaceOverlap *
505 // subspaceEigenvectors.col(k))( 0, 0)<<std::endl;
506 }
507 }
508 // if (!hermitian) {
509 // molpro::cout << "eigenvalues"<<std::endl<<subspaceEigenvalues<<std::endl;
510 // molpro::cout << "eigenvectors" << std::endl << subspaceEigenvectors << std::endl;
511 // }
512 if (condone_complex) {
513 for (Eigen::Index root = 0; root < Hbar.cols(); ++root) {
514 if (subspaceEigenvalues(root).imag() != 0) {
515 // molpro::cout << "complex eigenvalues: " << subspaceEigenvalues(root) << ", " <<
516 // subspaceEigenvalues(root + 1)
517 // << std::endl;
518 subspaceEigenvalues(root) = subspaceEigenvalues(root + 1) = subspaceEigenvalues(root).real();
519 subspaceEigenvectors.col(root) = subspaceEigenvectors.col(root).real();
520 subspaceEigenvectors.col(root + 1) = subspaceEigenvectors.col(root + 1).imag();
521 ++root;
522 }
523 }
524 }
525 if ((subspaceEigenvectors - subspaceEigenvectors.real()).norm() > 1e-10 or
526 (subspaceEigenvalues - subspaceEigenvalues.real()).norm() > 1e-10) {
527 throw std::runtime_error("unexpected complex solution found");
528 }
529 eigenvectors.resize(dimension * Hbar.cols());
530 eigenvalues.resize(Hbar.cols());
531 // if constexpr (std::is_class<value_type>::value) {
532 Eigen::Map<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(eigenvectors.data(), dimension, Hbar.cols()) =
533 subspaceEigenvectors.real();
534 Eigen::Map<Eigen::Matrix<value_type, Eigen::Dynamic, 1>> ev(eigenvalues.data(), Hbar.cols());
535 ev = subspaceEigenvalues.real();
536
537 // } else {
538 // Eigen::Map<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(m_evec_xx.data(), dimension, dimension)
539 // =
540 // subspaceEigenvectors;
541 // Eigen::Map<Eigen::Matrix<value_type, Eigen::Dynamic, 1>>(eigenvalues.data(), dimension) = subspaceEigenvalues;
542 // }
543 prof->stop();
544}
545
546template <typename value_type, typename std::enable_if_t<is_complex<value_type>{}, int>>
547void solve_LinearEquations(std::vector<value_type>& solution, std::vector<value_type>& eigenvalues,
548 const std::vector<value_type>& matrix, const std::vector<value_type>& metric,
549 const std::vector<value_type>& rhs, const size_t dimension, size_t nroot,
550 double augmented_hessian, double svdThreshold, int verbosity) {
551 assert(false); // Complex not implemented here
552}
553
554template <typename value_type, typename std::enable_if_t<!is_complex<value_type>{}, std::nullptr_t>>
555void solve_LinearEquations(std::vector<value_type>& solution, std::vector<value_type>& eigenvalues,
556 const std::vector<value_type>& matrix, const std::vector<value_type>& metric,
557 const std::vector<value_type>& rhs, const size_t dimension, size_t nroot,
558 double augmented_hessian, double svdThreshold, int verbosity) {
559 const Eigen::Index nX = dimension;
560 solution.resize(nX * nroot);
561 // std::cout << "augmented_hessian "<<augmented_hessian<<std::endl;
562 if (augmented_hessian > 0) { // Augmented hessian
563 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> subspaceMatrix;
564 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> subspaceOverlap;
565 subspaceMatrix.conservativeResize(nX + 1, nX + 1);
566 subspaceOverlap.conservativeResize(nX + 1, nX + 1);
567 subspaceMatrix.block(0, 0, nX, nX) =
568 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(matrix.data(), nX, nX);
569 subspaceOverlap.block(0, 0, nX, nX) =
570 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>>(metric.data(), nX, nX);
571 eigenvalues.resize(nroot);
572 for (size_t root = 0; root < nroot; root++) {
573 for (Eigen::Index i = 0; i < nX; i++) {
574 subspaceMatrix(i, nX) = subspaceMatrix(nX, i) = -augmented_hessian * rhs[i + nX * root];
575 subspaceOverlap(i, nX) = subspaceOverlap(nX, i) = 0;
576 }
577 subspaceMatrix(nX, nX) = 0;
578 subspaceOverlap(nX, nX) = 1;
579 // std::cout << "subspace augmented hessian subspaceMatrix\n"<<subspaceMatrix<<std::endl;
580 // std::cout << "subspace augmented hessian subspaceOverlap\n"<<subspaceOverlap<<std::endl;
581
582 Eigen::GeneralizedEigenSolver<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>> s(subspaceMatrix,
583 subspaceOverlap);
584 auto eval = s.eigenvalues();
585 auto evec = s.eigenvectors();
586 Eigen::Index imax = 0;
587 for (Eigen::Index i = 0; i < nX + 1; i++)
588 if (eval(i).real() < eval(imax).real())
589 imax = i;
590 eigenvalues[root] = eval(imax).real();
591 auto Solution = evec.col(imax).real().head(nX) / (augmented_hessian * evec.real()(nX, imax));
592 for (auto k = 0; k < nX; k++)
593 solution[k + nX * root] = Solution(k);
594 // std::cout << "subspace augmented hessian solution\n"<<Solution<<std::endl;
595 }
596 } else { // straight solution of linear equations
597 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> subspaceMatrixR(
598 matrix.data(), nX, nX);
599 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> RHS_R(rhs.data(), nX,
600 nroot);
601 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> subspaceMatrix = subspaceMatrixR;
602 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> RHS = RHS_R;
603 Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> Solution;
604// std::cout << "solve_LinearEquations RHS_R\n"<<RHS_R<<std::endl;
605// for (size_t i=0; i<RHS_R.cols()*RHS_R.rows(); ++i)
606// std::cout << " "<<RHS_R.data()[i];
607// std::cout << std::endl;
608// std::cout << "solve_LinearEquations RHS\n"<<RHS<<std::endl;
609// for (size_t i=0; i<RHS.cols()*RHS.rows(); ++i)
610// std::cout << " "<<RHS.data()[i];
611// std::cout << std::endl;
612 Solution = subspaceMatrix.householderQr().solve(RHS);
613 // std::cout << "subspace linear equations solution\n"<<Solution<<std::endl;
614 for (size_t root = 0; root < nroot; root++)
615 for (auto k = 0; k < nX; k++)
616 solution[k + nX * root] = Solution(k, root);
617 }
618}
619
620template <typename value_type, typename std::enable_if_t<!is_complex<value_type>{}, std::nullptr_t>>
621void solve_DIIS(std::vector<value_type>& solution, const std::vector<value_type>& matrix, const size_t dimension,
622 double svdThreshold, int verbosity) {
623 auto nAug = dimension + 1;
624 // auto nQ = dimension - 1;
625 solution.resize(dimension);
626 // if (nQ > 0) {
627 Eigen::VectorXd Rhs(nAug), Coeffs(nAug);
628 Eigen::MatrixXd BAug(nAug, nAug);
629 // Eigen::Matrix<value_type, Eigen::Dynamic, 1> Rhs(nQ), Coeffs(nQ);
630 // Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic> B(nQ, nQ);
631 //
632 Eigen::Map<const Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>> subspaceMatrix(matrix.data(), dimension,
633 dimension);
634 BAug.block(0, 0, dimension, dimension) = subspaceMatrix;
635 for (size_t i = 0; i < dimension; ++i) {
636 BAug(dimension, i) = BAug(i, dimension) = -1;
637 Rhs(i) = 0;
638 }
639 BAug(dimension, dimension) = 0;
640 Rhs(dimension) = -1;
641 //
642 // molpro::cout << "BAug:" << std::endl << BAug << std::endl;
643 // molpro::cout << "Rhs:" << std::endl << Rhs << std::endl;
644
645 // invert the system, determine extrapolation coefficients.
646 Eigen::JacobiSVD<Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic>> svd(BAug, Eigen::ComputeThinU |
647 Eigen::ComputeThinV);
648
649 // std::cout << "svd thresholds " << svdThreshold << "," << svd.singularValues().maxCoeff() << std::endl;
650 // std::cout << "singular values " << svd.singularValues().transpose() << std::endl;
651 svd.setThreshold(svdThreshold * svd.singularValues().maxCoeff() * 0);
652 // molpro::cout << "svdThreshold "<<svdThreshold<<std::endl;
653 // molpro::cout << "U\n"<<svd.matrixU()<<std::endl;
654 // molpro::cout << "V\n"<<svd.matrixV()<<std::endl;
655 // molpro::cout << "singularValues\n"<<svd.singularValues()<<std::endl;
656 Coeffs = svd.solve(Rhs).head(dimension);
657 // Coeffs = BAug.fullPivHouseholderQr().solve(Rhs);
658 // molpro::cout << "Coeffs "<<Coeffs.transpose()<<std::endl;
659 if (verbosity > 1)
660 molpro::cout << "Combination of iteration vectors: " << Coeffs.transpose() << std::endl;
661 for (size_t k = 0; k < (size_t)Coeffs.rows(); k++) {
662 if (std::isnan(std::abs(Coeffs(k)))) {
663 molpro::cout << "B:" << std::endl << BAug << std::endl;
664 molpro::cout << "Rhs:" << std::endl << Rhs << std::endl;
665 molpro::cout << "Combination of iteration vectors: " << Coeffs.transpose() << std::endl;
666 throw std::overflow_error("NaN detected in DIIS submatrix solution");
667 }
668 solution[k] = Coeffs(k);
669 }
670}
671} // namespace molpro::linalg::itsolv
672
673#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:30
bool empty() const
Definition: Span.h:78
iterator begin()
Definition: Span.h:68
size_type size() const
Definition: Span.h:76
iterator end()
Definition: Span.h:72
iterator data()
Definition: Span.h:65
static std::shared_ptr< Profiler > single()
4-parameter interpolation of a 1-dimensional function given two points for which function values and ...
Definition: helper.h:10
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:36
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:265
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:547
size_t get_rank(std::vector< value_type > eigenvalues, value_type threshold)
Definition: helper-implementation.h:223
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:621
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:313
int eigensolver_lapacke_dsyev(const std::vector< double > &matrix, std::vector< double > &eigenvectors, std::vector< double > &eigenvalues, const size_t dimension)
Definition: helper-implementation.h:123
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:307
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:14
Stores a singular value and corresponding left and right singular vectors.
Definition: helper.h:19
value_type value
Definition: helper.h:21