iterative-solver 0.0
helper-dispatch.h
1#ifndef LINEARALGEBRA_SRC_MOLPRO_LINALG_ITERATIVESOLVER_HELPER_DISPATCH_H_
2#define LINEARALGEBRA_SRC_MOLPRO_LINALG_ITERATIVESOLVER_HELPER_DISPATCH_H_
3
19#include <Eigen/Dense>
20
21#include <molpro/lapacke.h>
22#include <molpro/linalg/itsolv/helper.h>
23
24#include <algorithm>
25#include <complex>
26#include <cstddef>
27#include <list>
28#include <span>
29#include <stdexcept>
30#include <string>
31#include <type_traits>
32#include <vector>
33
34#ifdef MOLPRO
35extern "C" int dsyev_c(char, char, int, double*, int, double*);
36#endif
37
38namespace molpro::linalg::itsolv {
39
47template <typename T>
48struct has_lapack_kernel : std::false_type {};
49#if defined(HAVE_LAPACKE) || defined(MOLPRO)
50template <>
51struct has_lapack_kernel<double> : std::true_type {};
52#endif
53#ifdef HAVE_LAPACKE
54template <>
55struct has_lapack_kernel<float> : std::true_type {};
56template <>
57struct has_lapack_kernel<std::complex<float>> : std::true_type {};
58template <>
59struct has_lapack_kernel<std::complex<double>> : std::true_type {};
60#endif
61template <typename T>
63
64#ifdef HAVE_LAPACKE
71namespace lapack {
72
74inline lapack_int heev(int matrix_layout, char jobz, char uplo, lapack_int n, float* a, lapack_int lda, float* w) {
75 return LAPACKE_ssyev(matrix_layout, jobz, uplo, n, a, lda, w);
76}
77inline lapack_int heev(int matrix_layout, char jobz, char uplo, lapack_int n, double* a, lapack_int lda, double* w) {
78 return LAPACKE_dsyev(matrix_layout, jobz, uplo, n, a, lda, w);
79}
80inline lapack_int heev(int matrix_layout, char jobz, char uplo, lapack_int n, std::complex<float>* a, lapack_int lda,
81 float* w) {
82 return LAPACKE_cheev(matrix_layout, jobz, uplo, n, reinterpret_cast<lapack_complex_float*>(a), lda, w);
83}
84inline lapack_int heev(int matrix_layout, char jobz, char uplo, lapack_int n, std::complex<double>* a, lapack_int lda,
85 double* w) {
86 return LAPACKE_zheev(matrix_layout, jobz, uplo, n, reinterpret_cast<lapack_complex_double*>(a), lda, w);
87}
88
90inline lapack_int gesdd(int matrix_layout, char jobz, lapack_int m, lapack_int n, float* a, lapack_int lda, float* s,
91 float* u, lapack_int ldu, float* vt, lapack_int ldvt) {
92 return LAPACKE_sgesdd(matrix_layout, jobz, m, n, a, lda, s, u, ldu, vt, ldvt);
93}
94inline lapack_int gesdd(int matrix_layout, char jobz, lapack_int m, lapack_int n, double* a, lapack_int lda, double* s,
95 double* u, lapack_int ldu, double* vt, lapack_int ldvt) {
96 return LAPACKE_dgesdd(matrix_layout, jobz, m, n, a, lda, s, u, ldu, vt, ldvt);
97}
98inline lapack_int gesdd(int matrix_layout, char jobz, lapack_int m, lapack_int n, std::complex<float>* a,
99 lapack_int lda, float* s, std::complex<float>* u, lapack_int ldu, std::complex<float>* vt,
100 lapack_int ldvt) {
101 return LAPACKE_cgesdd(matrix_layout, jobz, m, n, reinterpret_cast<lapack_complex_float*>(a), lda, s,
102 reinterpret_cast<lapack_complex_float*>(u), ldu, reinterpret_cast<lapack_complex_float*>(vt),
103 ldvt);
104}
105inline lapack_int gesdd(int matrix_layout, char jobz, lapack_int m, lapack_int n, std::complex<double>* a,
106 lapack_int lda, double* s, std::complex<double>* u, lapack_int ldu, std::complex<double>* vt,
107 lapack_int ldvt) {
108 return LAPACKE_zgesdd(matrix_layout, jobz, m, n, reinterpret_cast<lapack_complex_double*>(a), lda, s,
109 reinterpret_cast<lapack_complex_double*>(u), ldu, reinterpret_cast<lapack_complex_double*>(vt),
110 ldvt);
111}
112
114inline lapack_int gesvd(int matrix_layout, char jobu, char jobvt, lapack_int m, lapack_int n, float* a, lapack_int lda,
115 float* s, float* u, lapack_int ldu, float* vt, lapack_int ldvt, float* superb) {
116 return LAPACKE_sgesvd(matrix_layout, jobu, jobvt, m, n, a, lda, s, u, ldu, vt, ldvt, superb);
117}
118inline lapack_int gesvd(int matrix_layout, char jobu, char jobvt, lapack_int m, lapack_int n, double* a, lapack_int lda,
119 double* s, double* u, lapack_int ldu, double* vt, lapack_int ldvt, double* superb) {
120 return LAPACKE_dgesvd(matrix_layout, jobu, jobvt, m, n, a, lda, s, u, ldu, vt, ldvt, superb);
121}
122inline lapack_int gesvd(int matrix_layout, char jobu, char jobvt, lapack_int m, lapack_int n, std::complex<float>* a,
123 lapack_int lda, float* s, std::complex<float>* u, lapack_int ldu, std::complex<float>* vt,
124 lapack_int ldvt, float* superb) {
125 return LAPACKE_cgesvd(matrix_layout, jobu, jobvt, m, n, reinterpret_cast<lapack_complex_float*>(a), lda, s,
126 reinterpret_cast<lapack_complex_float*>(u), ldu, reinterpret_cast<lapack_complex_float*>(vt),
127 ldvt, superb);
128}
129inline lapack_int gesvd(int matrix_layout, char jobu, char jobvt, lapack_int m, lapack_int n, std::complex<double>* a,
130 lapack_int lda, double* s, std::complex<double>* u, lapack_int ldu, std::complex<double>* vt,
131 lapack_int ldvt, double* superb) {
132 return LAPACKE_zgesvd(matrix_layout, jobu, jobvt, m, n, reinterpret_cast<lapack_complex_double*>(a), lda, s,
133 reinterpret_cast<lapack_complex_double*>(u), ldu, reinterpret_cast<lapack_complex_double*>(vt),
134 ldvt, superb);
135}
136
137} // namespace lapack
138#endif // HAVE_LAPACKE
139
140namespace detail {
141
151template <typename value_type>
152int eigensolver_hermitian_kernel(std::false_type /*use_lapack*/, std::span<value_type> a,
153 std::span<real_type_t<value_type>> w, size_t dimension) {
154 using matrix_type = Eigen::Matrix<value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>;
155 using real_vector_type = Eigen::Vector<real_type_t<value_type>, Eigen::Dynamic>;
156 Eigen::Map<matrix_type> A(a.data(), dimension, dimension);
157 // Eigen, like ?syev with uplo='L', references only the lower triangle and returns ascending eigenvalues
158 Eigen::SelfAdjointEigenSolver<matrix_type> solver(A, Eigen::ComputeEigenvectors);
159 if (solver.info() != Eigen::Success)
160 return 1;
161 Eigen::Map<real_vector_type>(w.data(), dimension) = solver.eigenvalues();
162 A = solver.eigenvectors();
163 return 0;
164}
165
166#if defined(HAVE_LAPACKE) || defined(MOLPRO)
168template <typename value_type>
169int eigensolver_hermitian_kernel(std::true_type /*use_lapack*/, std::span<value_type> a,
170 std::span<real_type_t<value_type>> w, size_t dimension) {
171 constexpr char compute_eigenvalues_eigenvectors = 'V';
172 constexpr char store_lower_triangle = 'L';
173#ifdef MOLPRO
174 if constexpr (std::is_same_v<value_type, double>) {
175 return dsyev_c(compute_eigenvalues_eigenvectors, store_lower_triangle, int(dimension), a.data(), int(dimension),
176 w.data());
177 }
178#endif
179#ifdef HAVE_LAPACKE
180 return int(lapack::heev(LAPACK_COL_MAJOR, compute_eigenvalues_eigenvectors, store_lower_triangle,
181 lapack_int(dimension), a.data(), lapack_int(dimension), w.data()));
182#else
183 throw std::logic_error("no LAPACK kernel available for this scalar type");
184#endif
185}
186#endif
187
188} // namespace detail
189
205template <typename value_type>
206int eigensolver_hermitian(std::span<const value_type> matrix, std::span<value_type> eigenvectors,
207 std::span<real_type_t<value_type>> eigenvalues, const size_t dimension) {
208 // validate input
209 if (eigenvectors.size() != matrix.size()) {
210 throw std::runtime_error("Matrix of eigenvectors and input matrix are not the same size! (" +
211 std::to_string(eigenvectors.size()) + " vs. " + std::to_string(matrix.size()) + ")");
212 }
213
214 if (eigenvectors.size() != dimension * dimension || eigenvalues.size() != dimension) {
215 throw std::runtime_error("Size of eigenvectors/eigenvalues do not match dimension!");
216 }
217
218 // copy input matrix, since the decomposition overwrites it in place
219 std::copy(matrix.begin(), matrix.end(), eigenvectors.begin());
220 if (dimension == 0)
221 return 0;
222
223 return detail::eigensolver_hermitian_kernel<value_type>(has_lapack_kernel<value_type>{}, eigenvectors, eigenvalues,
224 dimension);
225}
226
236template <typename value_type>
237std::list<SVD<value_type>> eigensolver_hermitian(size_t dimension, std::span<const value_type> matrix) {
238 using real_t = real_type_t<value_type>;
239 std::vector<value_type> eigvecs(dimension * dimension);
240 std::vector<real_t> eigvals(dimension);
241
242 const int success = eigensolver_hermitian<value_type>(matrix, eigvecs, eigvals, dimension);
243 if (success < 0) {
244 throw std::invalid_argument("Invalid argument of eigensolver_hermitian: " + std::to_string(-success));
245 }
246 if (success > 0) {
247 throw std::runtime_error("Hermitian eigensolver failed to converge. "
248 " elements of an intermediate tridiagonal form did not converge to zero.");
249 }
250
251 auto eigensystem = std::list<SVD<value_type>>{};
252
253 // populate eigensystem
254 for (int i = int(dimension) - 1; i >= 0;
255 i--) { // note: flipping this axis gives parity with results of eigen::jacobiSVD
256 auto temp_eigenproblem = SVD<value_type>{};
257 temp_eigenproblem.value = eigvals[i];
258 for (size_t j = 0; j < dimension; j++) {
259 temp_eigenproblem.v.emplace_back(eigvecs[j + (dimension * i)]);
260 }
261 eigensystem.emplace_back(temp_eigenproblem);
262 }
263
264 return eigensystem;
265}
266
267} // namespace molpro::linalg::itsolv
268
269#endif // LINEARALGEBRA_SRC_MOLPRO_LINALG_ITERATIVESOLVER_HELPER_DISPATCH_H_
int eigensolver_hermitian_kernel(std::false_type, std::span< value_type > a, std::span< real_type_t< value_type > > w, size_t dimension)
Eigen implementation of the hermitian eigenproblem, used for every scalar type LAPACK does not cover.
Definition: helper-dispatch.h:152
4-parameter interpolation of a 1-dimensional function given two points for which function values and ...
Definition: helper.h:14
int eigensolver_hermitian(std::span< const value_type > matrix, std::span< value_type > eigenvectors, std::span< real_type_t< value_type > > eigenvalues, const size_t dimension)
Eigen-decomposition of a real symmetric / complex hermitian matrix.
Definition: helper-dispatch.h:206
constexpr bool has_lapack_kernel_v
Definition: helper-dispatch.h:62
Stores a singular value and corresponding left and right singular vectors.
Definition: helper.h:24
value_type value
Definition: helper.h:26
Whether the dense kernels of this library dispatch T to LAPACK rather than to Eigen.
Definition: helper-dispatch.h:48