Loading...
Searching...
No Matches
serial.hpp
Go to the documentation of this file.
1#pragma once
2
3/**
4 * SerialSubspaceDiagonalizer — LAPACK implementation of SubspaceDiagonalizer.
5 *
6 * Uses zhegv/dsygv for generalized eigenvalue problems on a single process.
7 */
8
12#include <algorithm>
13
14namespace q3 {
15
17 public:
18 int solve(int basis_size,
19 Complex* hamiltonian_matrix,
20 Complex* overlap_matrix,
21 Real* eigenvalues,
22 Complex* eigenvector_matrix,
23 bool is_gamma_only = false) override {
24 if (basis_size == 0)
25 return 0;
26
27 int info;
28 int diagonalization_type = 1; // H*x = lambda*S*x
29 char jobz = 'V'; // Compute eigenvalues and eigenvectors
30 char uplo = 'U'; // Upper triangle stored
31
32 const int matrix_elements = basis_size * basis_size;
33 if (is_gamma_only) {
34 /**
35 * Gamma-only: H_sub and S_sub are real symmetric matrices, so dsygv
36 * solves them and its eigenvectors come out real.
37 *
38 * Both are packed to real in place rather than into copies of
39 * their own: the real part of complex entry i is written to double
40 * entry i, which lies inside complex entry i/2 -- already read --
41 * so a forward pass is safe, and the eigenvectors are expanded
42 * back to complex by a backward pass for the same reason. The
43 * matrices are the caller's to overwrite, and the Hamiltonian is
44 * usually the eigenvector buffer itself.
45 */
46 if (hamiltonian_matrix != eigenvector_matrix) {
47 std::copy(
48 hamiltonian_matrix, hamiltonian_matrix + matrix_elements, eigenvector_matrix);
49 }
50 auto* h_real = reinterpret_cast<double*>(eigenvector_matrix);
51 auto* s_real = reinterpret_cast<double*>(overlap_matrix);
52 for (int i = 0; i < matrix_elements; ++i) {
53 h_real[i] = eigenvector_matrix[i].real();
54 s_real[i] = overlap_matrix[i].real();
55 }
56
57 double work_query;
58 int lwork = -1;
59 lapack::dsygv_(&diagonalization_type,
60 &jobz,
61 &uplo,
62 &basis_size,
63 h_real,
64 &basis_size,
65 s_real,
66 &basis_size,
67 eigenvalues,
68 &work_query,
69 &lwork,
70 &info);
71
72 lwork = static_cast<int>(work_query);
73 std::vector<double> work(lwork);
74 lapack::dsygv_(&diagonalization_type,
75 &jobz,
76 &uplo,
77 &basis_size,
78 h_real,
79 &basis_size,
80 s_real,
81 &basis_size,
82 eigenvalues,
83 work.data(),
84 &lwork,
85 &info);
86
87 // Expand the real eigenvectors back to complex, in place, backward.
88 for (int i = matrix_elements - 1; i >= 0; --i) {
89 eigenvector_matrix[i] = Complex(h_real[i], 0.0);
90 }
91 } else {
92 /**
93 * Non-gamma: use zhegv for complex Hermitian matrices. It leaves
94 * the eigenvectors where the Hamiltonian was, so the Hamiltonian
95 * goes into the eigenvector buffer -- unless the caller already
96 * handed the same buffer as both.
97 */
98 if (hamiltonian_matrix != eigenvector_matrix) {
99 std::copy(
100 hamiltonian_matrix, hamiltonian_matrix + matrix_elements, eigenvector_matrix);
101 }
102
103 // Workspace query
104 Complex work_query;
105 int lwork = -1;
106 std::vector<Real> rwork(std::max(1, 3 * basis_size - 2));
107
108 auto* cast_eigenvectors = reinterpret_cast<std::complex<double>*>(eigenvector_matrix);
109 auto* cast_overlap = reinterpret_cast<std::complex<double>*>(overlap_matrix);
110 auto* cast_work_query = reinterpret_cast<std::complex<double>*>(&work_query);
111
112 lapack::zhegv_(&diagonalization_type,
113 &jobz,
114 &uplo,
115 &basis_size,
116 cast_eigenvectors,
117 &basis_size,
118 cast_overlap,
119 &basis_size,
120 eigenvalues,
121 cast_work_query,
122 &lwork,
123 rwork.data(),
124 &info);
125
126 lwork = static_cast<int>(work_query.real());
127 std::vector<Complex> work(lwork);
128 auto* cast_work = reinterpret_cast<std::complex<double>*>(work.data());
129
130 lapack::zhegv_(&diagonalization_type,
131 &jobz,
132 &uplo,
133 &basis_size,
134 cast_eigenvectors,
135 &basis_size,
136 cast_overlap,
137 &basis_size,
138 eigenvalues,
139 cast_work,
140 &lwork,
141 rwork.data(),
142 &info);
143 }
144
145 return info;
146 }
147
148 std::string name() const override {
149 return "Serial (LAPACK)";
150 }
151};
152
153} // namespace q3
int solve(int basis_size, Complex *hamiltonian_matrix, Complex *overlap_matrix, Real *eigenvalues, Complex *eigenvector_matrix, bool is_gamma_only=false) override
Definition serial.hpp:18
std::string name() const override
Definition serial.hpp:148
void zhegv_(const int *itype, const char *jobz, const char *uplo, const int *n, std::complex< double > *a, const int *lda, std::complex< double > *b, const int *ldb, double *w, std::complex< double > *work, const int *lwork, double *rwork, int *info)
void dsygv_(const int *itype, const char *jobz, const char *uplo, const int *n, double *a, const int *lda, double *b, const int *ldb, double *w, double *work, const int *lwork, int *info)
double Real
std::complex< double > Complex