pairinteraction
A Rydberg Interaction Calculator
System.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2024 Pairinteraction Developers
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
5
16
17#include <Eigen/SparseCore>
18#include <complex>
19#include <limits>
20#include <memory>
21#include <numeric>
22#include <oneapi/tbb.h>
23#include <optional>
24#include <spdlog/spdlog.h>
25
26namespace pairinteraction {
27template <typename Derived>
28System<Derived>::System(std::shared_ptr<const basis_t> basis)
29 : hamiltonian(std::make_unique<typename System<Derived>::operator_t>(std::move(basis))) {}
30
31template <typename Derived>
33 : hamiltonian(std::make_unique<typename System<Derived>::operator_t>(*other.hamiltonian)),
34 hamiltonian_requires_construction(other.hamiltonian_requires_construction),
35 hamiltonian_is_diagonal(other.hamiltonian_is_diagonal),
36 blockdiagonalizing_labels(other.blockdiagonalizing_labels) {}
38template <typename Derived>
40 : hamiltonian(std::move(other.hamiltonian)),
41 hamiltonian_requires_construction(other.hamiltonian_requires_construction),
42 hamiltonian_is_diagonal(other.hamiltonian_is_diagonal),
43 blockdiagonalizing_labels(std::move(other.blockdiagonalizing_labels)) {}
45template <typename Derived>
47 if (this != &other) {
48 hamiltonian = std::make_unique<operator_t>(*other.hamiltonian);
49 hamiltonian_requires_construction = other.hamiltonian_requires_construction;
50 hamiltonian_is_diagonal = other.hamiltonian_is_diagonal,
51 blockdiagonalizing_labels = other.blockdiagonalizing_labels;
52 }
53 return *this;
54}
56template <typename Derived>
58 if (this != &other) {
59 hamiltonian = std::move(other.hamiltonian);
60 hamiltonian_requires_construction = other.hamiltonian_requires_construction;
61 hamiltonian_is_diagonal = other.hamiltonian_is_diagonal,
62 blockdiagonalizing_labels = std::move(other.blockdiagonalizing_labels);
63 }
64 return *this;
65}
66
67template <typename Derived>
68System<Derived>::~System() = default;
69
70template <typename Derived>
71const Derived &System<Derived>::derived() const {
72 return static_cast<const Derived &>(*this);
73}
74
75template <typename Derived>
76std::shared_ptr<const typename System<Derived>::basis_t> System<Derived>::get_basis() const {
77 if (hamiltonian_requires_construction) {
78 construct_hamiltonian();
79 hamiltonian_requires_construction = false;
80 }
81 return hamiltonian->get_basis();
82}
83
84template <typename Derived>
85std::shared_ptr<const typename System<Derived>::basis_t> System<Derived>::get_eigenbasis() const {
86 if (hamiltonian_requires_construction) {
87 construct_hamiltonian();
88 hamiltonian_requires_construction = false;
89 }
90 if (!hamiltonian_is_diagonal) {
91 throw std::runtime_error("The Hamiltonian has not been diagonalized yet.");
92 }
93 return hamiltonian->get_basis();
94}
95
96template <typename Derived>
98 if (hamiltonian_requires_construction) {
99 construct_hamiltonian();
100 hamiltonian_requires_construction = false;
101 }
102 if (!hamiltonian_is_diagonal) {
103 throw std::runtime_error("The Hamiltonian has not been diagonalized yet.");
104 }
105 return hamiltonian->get_matrix().diagonal().real();
106}
107
108template <typename Derived>
109const Eigen::SparseMatrix<typename System<Derived>::scalar_t, Eigen::RowMajor> &
111 if (hamiltonian_requires_construction) {
112 construct_hamiltonian();
113 hamiltonian_requires_construction = false;
114 }
115 return hamiltonian->get_matrix();
116}
117
118template <typename Derived>
121 if (hamiltonian_requires_construction) {
122 construct_hamiltonian();
123 hamiltonian_requires_construction = false;
124 }
125 return hamiltonian->get_transformation();
126}
127
128template <typename Derived>
131 if (hamiltonian_requires_construction) {
132 construct_hamiltonian();
133 hamiltonian_requires_construction = false;
134 }
135 return hamiltonian->get_rotator(alpha, beta, gamma);
136}
137
138template <typename Derived>
139Sorting System<Derived>::get_sorter(const std::vector<TransformationType> &labels) const {
140 if (hamiltonian_requires_construction) {
141 construct_hamiltonian();
142 hamiltonian_requires_construction = false;
143 }
144 return hamiltonian->get_sorter(labels);
145}
146
147template <typename Derived>
148std::vector<IndicesOfBlock>
149System<Derived>::get_indices_of_blocks(const std::vector<TransformationType> &labels) const {
150 if (hamiltonian_requires_construction) {
151 construct_hamiltonian();
152 hamiltonian_requires_construction = false;
153 }
154 return hamiltonian->get_indices_of_blocks(labels);
155}
156
157template <typename Derived>
159 if (hamiltonian_requires_construction) {
160 construct_hamiltonian();
161 hamiltonian_requires_construction = false;
162 }
163 hamiltonian = std::make_unique<operator_t>(hamiltonian->transformed(transformation));
164
165 // A transformed system might have lost its block-diagonalizability if the
166 // transformation was not a sorting
167 blockdiagonalizing_labels.clear();
168
169 return *this;
170}
171
172template <typename Derived>
174 if (hamiltonian_requires_construction) {
175 construct_hamiltonian();
176 hamiltonian_requires_construction = false;
177 }
178 hamiltonian = std::make_unique<operator_t>(hamiltonian->transformed(transformation));
179
180 return *this;
181}
182
183template <typename Derived>
185 std::optional<real_t> min_eigenenergy,
186 std::optional<real_t> max_eigenenergy, double rtol) {
187 if (hamiltonian_requires_construction) {
188 construct_hamiltonian();
189 hamiltonian_requires_construction = false;
190 }
191
192 if (hamiltonian_is_diagonal) {
193 return *this;
194 }
195
196 Eigen::SparseMatrix<scalar_t, Eigen::RowMajor> eigenvectors;
197 Eigen::SparseMatrix<scalar_t, Eigen::RowMajor> eigenenergies;
198
199 // Sort the Hamiltonian according to the block structure
200 if (!blockdiagonalizing_labels.empty()) {
201 auto sorter = hamiltonian->get_sorter(blockdiagonalizing_labels);
202 hamiltonian = std::make_unique<operator_t>(hamiltonian->transformed(sorter));
203 }
204
205 // Get the indices of the blocks
206 auto blocks = hamiltonian->get_indices_of_blocks(blockdiagonalizing_labels);
207
208 assert((blockdiagonalizing_labels.empty() && blocks.size() == 1) ||
209 !blockdiagonalizing_labels.empty());
210
211 SPDLOG_DEBUG("Diagonalizing the Hamiltonian with {} blocks.", blocks.size());
212
213 // Diagonalize the blocks in parallel
214 std::vector<Eigen::VectorX<real_t>> eigenenergies_blocks(blocks.size());
215 std::vector<Eigen::SparseMatrix<scalar_t, Eigen::RowMajor>> eigenvectors_blocks(blocks.size());
216 oneapi::tbb::parallel_for(
217 oneapi::tbb::blocked_range<size_t>(0, blocks.size()), [&](const auto &range) {
218 for (size_t idx = range.begin(); idx != range.end(); ++idx) {
219 auto eigensys = min_eigenenergy.has_value() || max_eigenenergy.has_value()
220 ? diagonalizer.eigh(
221 hamiltonian->get_matrix().block(blocks[idx].start, blocks[idx].start,
222 blocks[idx].size(), blocks[idx].size()),
223 min_eigenenergy, max_eigenenergy, rtol)
224 : diagonalizer.eigh(
225 hamiltonian->get_matrix().block(blocks[idx].start, blocks[idx].start,
226 blocks[idx].size(), blocks[idx].size()),
227 rtol);
228 eigenvectors_blocks[idx] = eigensys.eigenvectors;
229 eigenenergies_blocks[idx] = eigensys.eigenvalues;
230 }
231 });
232
233 // Get the number of non-zeros per row of the combined eigenvector matrix
234 std::vector<Eigen::Index> non_zeros_per_inner_index;
235 non_zeros_per_inner_index.reserve(hamiltonian->get_matrix().rows());
236 Eigen::Index num_rows = 0;
237 Eigen::Index num_cols = 0;
238 for (const auto &matrix : eigenvectors_blocks) {
239 for (int i = 0; i < matrix.outerSize(); ++i) {
240 non_zeros_per_inner_index.push_back(matrix.outerIndexPtr()[i + 1] -
241 matrix.outerIndexPtr()[i]);
242 }
243 num_rows += matrix.rows();
244 num_cols += matrix.cols();
245 }
246
247 assert(static_cast<size_t>(num_rows) == hamiltonian->get_basis()->get_number_of_kets());
248 assert(static_cast<size_t>(num_cols) <= hamiltonian->get_basis()->get_number_of_states());
249
250 eigenvectors.resize(num_rows, num_cols);
251 eigenenergies.resize(num_cols, num_cols);
252
253 if (num_cols > 0) {
254 // Get the combined eigenvector matrix (in case of an restricted energy range, it is not
255 // square)
256 eigenvectors.reserve(non_zeros_per_inner_index);
257 Eigen::Index offset_rows = 0;
258 Eigen::Index offset_cols = 0;
259 for (const auto &matrix : eigenvectors_blocks) {
260 for (Eigen::Index i = 0; i < matrix.outerSize(); ++i) {
261 for (typename Eigen::SparseMatrix<scalar_t, Eigen::RowMajor>::InnerIterator it(
262 matrix, i);
263 it; ++it) {
264 eigenvectors.insert(it.row() + offset_rows, it.col() + offset_cols) =
265 it.value();
266 }
267 }
268 offset_rows += matrix.rows();
269 offset_cols += matrix.cols();
270 }
271 eigenvectors.makeCompressed();
272
273 assert(
274 eigenvectors.nonZeros() ==
275 std::accumulate(non_zeros_per_inner_index.begin(), non_zeros_per_inner_index.end(), 0));
276
277 // Get the combined eigenenergy matrix
278 eigenenergies.reserve(Eigen::VectorXi::Constant(num_cols, 1));
279 Eigen::Index offset = 0;
280 for (const auto &matrix : eigenenergies_blocks) {
281 for (int i = 0; i < matrix.size(); ++i) {
282 eigenenergies.insert(i + offset, i + offset) = matrix(i);
283 }
284 offset += matrix.size();
285 }
286 eigenenergies.makeCompressed();
287
288 // Fix phase ambiguity
289 std::vector<scalar_t> map_col_to_max(num_cols, 0);
290 for (int row = 0; row < eigenvectors.outerSize(); ++row) {
291 for (typename Eigen::SparseMatrix<scalar_t, Eigen::RowMajor>::InnerIterator it(
292 eigenvectors, row);
293 it; ++it) {
294 if (std::abs(it.value()) > std::abs(map_col_to_max[it.col()])) {
295 map_col_to_max[it.col()] = it.value();
296 }
297 }
298 }
299
300 Eigen::SparseMatrix<scalar_t, Eigen::RowMajor> phase_matrix;
301 phase_matrix.resize(num_cols, num_cols);
302 phase_matrix.reserve(Eigen::VectorXi::Constant(num_cols, 1));
303 for (int i = 0; i < num_cols; ++i) {
304 phase_matrix.insert(i, i) = std::abs(map_col_to_max[i]) / map_col_to_max[i];
305 }
306 phase_matrix.makeCompressed();
307
308 eigenvectors = eigenvectors * phase_matrix;
309 }
310
311 // Store the diagonalized hamiltonian
312 hamiltonian->get_matrix() = eigenenergies;
313 hamiltonian->get_basis() = hamiltonian->get_basis()->transformed(eigenvectors);
314
315 hamiltonian_is_diagonal = true;
316
317 return *this;
318}
319
320template <typename Derived>
322 if (hamiltonian_requires_construction) {
323 construct_hamiltonian();
324 hamiltonian_requires_construction = false;
325 }
326 return hamiltonian_is_diagonal;
327}
328
329// Explicit instantiation
330template class System<SystemAtom<double>>;
332template class System<SystemPair<double>>;
334} // namespace pairinteraction
const Eigen::SparseMatrix< scalar_t, Eigen::RowMajor > & get_matrix() const
Definition: System.cpp:110
std::vector< IndicesOfBlock > get_indices_of_blocks(const std::vector< TransformationType > &labels) const override
Definition: System.cpp:149
std::vector< TransformationType > blockdiagonalizing_labels
Definition: System.hpp:67
System< Derived > & diagonalize(const DiagonalizerInterface< scalar_t > &diagonalizer, std::optional< real_t > min_eigenenergy={}, std::optional< real_t > max_eigenenergy={}, double rtol=1e-6)
Definition: System.cpp:184
Transformation< scalar_t > get_rotator(real_t alpha, real_t beta, real_t gamma) const override
Definition: System.cpp:130
const Transformation< scalar_t > & get_transformation() const override
Definition: System.cpp:120
typename traits::CrtpTraits< Derived >::operator_t operator_t
Definition: System.hpp:31
std::shared_ptr< const basis_t > get_basis() const
Definition: System.cpp:76
System< Derived > & operator=(const System &other)
Definition: System.cpp:46
bool hamiltonian_requires_construction
Definition: System.hpp:65
Sorting get_sorter(const std::vector< TransformationType > &labels) const override
Definition: System.cpp:139
typename traits::CrtpTraits< Derived >::real_t real_t
Definition: System.hpp:28
System(std::shared_ptr< const basis_t > basis)
Definition: System.cpp:28
Eigen::VectorX< real_t > get_eigenenergies() const
Definition: System.cpp:97
System< Derived > & transform(const Transformation< scalar_t > &transformation)
Definition: System.cpp:158
std::shared_ptr< const basis_t > get_eigenbasis() const
Definition: System.cpp:85
std::unique_ptr< operator_t> hamiltonian
Definition: System.hpp:64
Matrix< Type, Dynamic, 1 > VectorX