17#include <Eigen/SparseCore>
22#include <oneapi/tbb.h>
24#include <spdlog/spdlog.h>
27template <
typename Derived>
29 : hamiltonian(std::make_unique<typename
System<Derived>::
operator_t>(std::move(basis))) {}
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>
48 hamiltonian = std::make_unique<operator_t>(*other.
hamiltonian);
56template <
typename Derived>
67template <
typename Derived>
70template <
typename Derived>
71const Derived &System<Derived>::derived()
const {
72 return static_cast<const Derived &
>(*this);
75template <
typename Derived>
77 if (hamiltonian_requires_construction) {
78 construct_hamiltonian();
79 hamiltonian_requires_construction =
false;
81 return hamiltonian->get_basis();
84template <
typename Derived>
86 if (hamiltonian_requires_construction) {
87 construct_hamiltonian();
88 hamiltonian_requires_construction =
false;
90 if (!hamiltonian_is_diagonal) {
91 throw std::runtime_error(
"The Hamiltonian has not been diagonalized yet.");
93 return hamiltonian->get_basis();
96template <
typename Derived>
98 if (hamiltonian_requires_construction) {
99 construct_hamiltonian();
100 hamiltonian_requires_construction =
false;
102 if (!hamiltonian_is_diagonal) {
103 throw std::runtime_error(
"The Hamiltonian has not been diagonalized yet.");
105 return hamiltonian->get_matrix().diagonal().real();
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;
115 return hamiltonian->get_matrix();
118template <
typename Derived>
121 if (hamiltonian_requires_construction) {
122 construct_hamiltonian();
123 hamiltonian_requires_construction =
false;
125 return hamiltonian->get_transformation();
128template <
typename Derived>
131 if (hamiltonian_requires_construction) {
132 construct_hamiltonian();
133 hamiltonian_requires_construction =
false;
135 return hamiltonian->get_rotator(alpha, beta, gamma);
138template <
typename Derived>
140 if (hamiltonian_requires_construction) {
141 construct_hamiltonian();
142 hamiltonian_requires_construction =
false;
144 return hamiltonian->get_sorter(labels);
147template <
typename Derived>
148std::vector<IndicesOfBlock>
150 if (hamiltonian_requires_construction) {
151 construct_hamiltonian();
152 hamiltonian_requires_construction =
false;
154 return hamiltonian->get_indices_of_blocks(labels);
157template <
typename Derived>
159 if (hamiltonian_requires_construction) {
160 construct_hamiltonian();
161 hamiltonian_requires_construction =
false;
163 hamiltonian = std::make_unique<operator_t>(hamiltonian->transformed(transformation));
167 blockdiagonalizing_labels.clear();
172template <
typename Derived>
174 if (hamiltonian_requires_construction) {
175 construct_hamiltonian();
176 hamiltonian_requires_construction =
false;
178 hamiltonian = std::make_unique<operator_t>(hamiltonian->transformed(transformation));
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;
192 if (hamiltonian_is_diagonal) {
196 Eigen::SparseMatrix<scalar_t, Eigen::RowMajor> eigenvectors;
197 Eigen::SparseMatrix<scalar_t, Eigen::RowMajor> eigenenergies;
200 if (!blockdiagonalizing_labels.empty()) {
201 auto sorter = hamiltonian->get_sorter(blockdiagonalizing_labels);
202 hamiltonian = std::make_unique<operator_t>(hamiltonian->transformed(sorter));
206 auto blocks = hamiltonian->get_indices_of_blocks(blockdiagonalizing_labels);
208 assert((blockdiagonalizing_labels.empty() && blocks.size() == 1) ||
209 !blockdiagonalizing_labels.empty());
211 SPDLOG_DEBUG(
"Diagonalizing the Hamiltonian with {} blocks.", blocks.size());
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()
221 hamiltonian->get_matrix().block(blocks[idx].start, blocks[idx].start,
222 blocks[idx].size(), blocks[idx].size()),
223 min_eigenenergy, max_eigenenergy, rtol)
225 hamiltonian->get_matrix().block(blocks[idx].start, blocks[idx].start,
226 blocks[idx].size(), blocks[idx].size()),
228 eigenvectors_blocks[idx] = eigensys.eigenvectors;
229 eigenenergies_blocks[idx] = eigensys.eigenvalues;
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]);
243 num_rows += matrix.rows();
244 num_cols += matrix.cols();
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());
250 eigenvectors.resize(num_rows, num_cols);
251 eigenenergies.resize(num_cols, num_cols);
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(
264 eigenvectors.insert(it.row() + offset_rows, it.col() + offset_cols) =
268 offset_rows += matrix.rows();
269 offset_cols += matrix.cols();
271 eigenvectors.makeCompressed();
274 eigenvectors.nonZeros() ==
275 std::accumulate(non_zeros_per_inner_index.begin(), non_zeros_per_inner_index.end(), 0));
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);
284 offset += matrix.size();
286 eigenenergies.makeCompressed();
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(
294 if (std::abs(it.value()) > std::abs(map_col_to_max[it.col()])) {
295 map_col_to_max[it.col()] = it.value();
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];
306 phase_matrix.makeCompressed();
308 eigenvectors = eigenvectors * phase_matrix;
312 hamiltonian->get_matrix() = eigenenergies;
313 hamiltonian->get_basis() = hamiltonian->get_basis()->transformed(eigenvectors);
315 hamiltonian_is_diagonal =
true;
320template <
typename Derived>
322 if (hamiltonian_requires_construction) {
323 construct_hamiltonian();
324 hamiltonian_requires_construction =
false;
326 return hamiltonian_is_diagonal;
const Eigen::SparseMatrix< scalar_t, Eigen::RowMajor > & get_matrix() const
std::vector< IndicesOfBlock > get_indices_of_blocks(const std::vector< TransformationType > &labels) const override
bool hamiltonian_is_diagonal
std::vector< TransformationType > blockdiagonalizing_labels
System< Derived > & diagonalize(const DiagonalizerInterface< scalar_t > &diagonalizer, std::optional< real_t > min_eigenenergy={}, std::optional< real_t > max_eigenenergy={}, double rtol=1e-6)
Transformation< scalar_t > get_rotator(real_t alpha, real_t beta, real_t gamma) const override
const Transformation< scalar_t > & get_transformation() const override
typename traits::CrtpTraits< Derived >::operator_t operator_t
std::shared_ptr< const basis_t > get_basis() const
System< Derived > & operator=(const System &other)
bool hamiltonian_requires_construction
Sorting get_sorter(const std::vector< TransformationType > &labels) const override
typename traits::CrtpTraits< Derived >::real_t real_t
System(std::shared_ptr< const basis_t > basis)
Eigen::VectorX< real_t > get_eigenenergies() const
System< Derived > & transform(const Transformation< scalar_t > &transformation)
std::shared_ptr< const basis_t > get_eigenbasis() const
std::unique_ptr< operator_t> hamiltonian
Matrix< Type, Dynamic, 1 > VectorX