TRIQS/TRIQS 4.0.0
Researching Interacting Quantum Systems
Loading...
Searching...
No Matches
atom_diag.cpp
1// Copyright (c) 2017-2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA)
2// Copyright (c) 2017-2018 Centre national de la recherche scientifique (CNRS)
3// Copyright (c) 2018-2023 Simons Foundation
4// Copyright (c) 2017 Igor Krivenko
5//
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You may obtain a copy of the License at
17// https://www.gnu.org/licenses/gpl-3.0.txt
18//
19// Authors: Maxime Charlebois, Michel Ferrero, Igor Krivenko, Olivier Parcollet, Hugo U. R. Strand, Nils Wentzell
20
21#include "./worker.hpp"
22#include "../atom_diag.hpp"
23#include "../../arrays.hpp"
28
29#include <itertools/itertools.hpp>
30
31#include <cstdlib>
32#include <iostream>
33#include <sstream>
34#include <string>
35#include <utility>
36#include <vector>
37
38using namespace triqs::arrays;
39
40namespace triqs::atom_diag {
41
42// Methods of atom_diag
43#define ATOM_DIAG_CONSTRUCTOR(ARGS) template <bool Complex> atom_diag<Complex>::atom_diag ARGS
44#define ATOM_DIAG_METHOD(RET, F) template <bool Complex> auto atom_diag<Complex>::F->RET
45
46 ATOM_DIAG_CONSTRUCTOR((many_body_op_t const &h, fundamental_operator_set const &fops, std::vector<many_body_op_t> const &qn_vector))
47 : h_atomic(h), fops(fops), full_hs(fops), vacuum(full_hs.size()) {
48 atom_diag_worker<Complex>{this}.partition_with_qn(qn_vector);
49 fill_first_eigenstate_of_subspace();
50 compute_vacuum();
51 }
52
53 // -----------------------------------------------------------------
54
55 ATOM_DIAG_CONSTRUCTOR((many_body_op_t const &h, fundamental_operator_set const &fops, many_body_op_t const &hyb))
56 : h_atomic(h), fops(fops), full_hs(fops), vacuum(full_hs.size()) {
57 atom_diag_worker<Complex>{this}.autopartition(hyb);
58 fill_first_eigenstate_of_subspace();
59 compute_vacuum();
60 }
61
62 // -----------------------------------------------------------------
63
64 ATOM_DIAG_CONSTRUCTOR((many_body_op_t const &h, fundamental_operator_set const &fops))
65 : h_atomic(h), fops(fops), full_hs(fops), vacuum(full_hs.size()) {
66 atom_diag_worker<Complex>{this}.autopartition();
67 fill_first_eigenstate_of_subspace();
68 compute_vacuum();
69 }
70
71 ATOM_DIAG_CONSTRUCTOR((many_body_op_t const &h, fundamental_operator_set const &fops, int n_min, int n_max))
72 : h_atomic(h), fops(fops), full_hs(fops), vacuum(full_hs.size()) {
73 atom_diag_worker<Complex>{this, n_min, n_max}.autopartition();
74 fill_first_eigenstate_of_subspace();
75 compute_vacuum();
76 }
77
78 // -----------------------------------------------------------------
79
80 ATOM_DIAG_METHOD(void, fill_first_eigenstate_of_subspace()) {
81 // Calculate the index of the first eigenstate of each block
82 first_eigenstate_of_subspace.resize(n_subspaces());
83 if (n_subspaces() == 0) return;
84 first_eigenstate_of_subspace[0] = 0;
85 for (int sp = 1; sp < n_subspaces(); ++sp) first_eigenstate_of_subspace[sp] = first_eigenstate_of_subspace[sp - 1] + get_subspace_dim(sp - 1);
86 }
87
88 // -----------------------------------------------------------------
89
90 ATOM_DIAG_METHOD(void, compute_vacuum()) {
91 // Compute vacuum vector in the eigenbasis
92 vacuum() = 0;
93 for (auto sp : range(sub_hilbert_spaces.size())) {
94 if (sub_hilbert_spaces[sp].has_state(fock_state_t(0))) {
95 vacuum_subspace_index = sp;
96 vacuum(index_range_of_subspace(sp)) = dagger(eigensystems[sp].unitary_matrix)(range::all, 0);
97 break;
98 }
99 }
100 }
101
102 // -----------------------------------------------------------------
103
104 ATOM_DIAG_METHOD(std::vector<std::vector<double>>, get_energies() const) {
105 std::vector<std::vector<double>> R;
106 for (auto const &es : eigensystems) R.emplace_back(es.eigenvalues.begin(), es.eigenvalues.end());
107 return R;
108 }
109
110 // -----------------------------------------------------------------
111 // Given a monomial (ccccc), and a subspace B, returns
112 // - the subspace connected by ccccc from B
113 // - the corresponding matrix (not necessarily square)
114
115 template <bool Complex>
116 auto atom_diag<Complex>::get_matrix_element_of_monomial(operators::monomial_t const &op_vec, int B) const -> std::pair<int, matrix_t> {
117
119
120 matrix_t m;
121 int Bp = -1;
122
123 for (auto [i_index, i] : itertools::enumerate(get_fock_states(B))) {
124 state<class hilbert_space, scalar_t, true> initial_st(full_hs, i);
125
126 auto final_st = monomial_op(initial_st);
127
128 // The initializer for i_index is needed here because of the Core Language Defect #2313.
129 // https://stackoverflow.com/a/46115028
130 EXPECTS(final_st.nterms() <= 1);
131 foreach (final_st, [&, i_idx = i_index](fock_state_t j, scalar_t x) {
132 for (auto const &sp : sub_hilbert_spaces) {
133 if (sp.has_state(j)) {
134 Bp = sp.get_index();
135
136 if (m.empty()) { m = matrix_t::zeros({get_subspace_dim(Bp), get_subspace_dim(B)}); }
137
138 m(sp.get_state_index(j), i_idx) = x;
139 break;
140 }
141 }
142 });
143 }
144
145 if (Bp == -1)
146 return {-1, std::move(m)};
147 else { return {Bp, dagger(get_unitary_matrix(Bp)) * m * get_unitary_matrix(B)}; }
148 }
149
150 // -----------------------------------------------------------------
151
152 ATOM_DIAG_METHOD(op_block_mat_t, get_op_mat(many_body_op_t const &op) const) {
153 op_block_mat_t op_mat(n_subspaces());
154
155 for (auto b : range(n_subspaces())) {
156 for (auto const &term : op) {
157 auto [bb, mat] = get_matrix_element_of_monomial(term.monomial, b);
158 if (bb == -1) continue;
159
160 if (op_mat.connection(b) == -1) {
161 op_mat.connection(b) = bb;
162 op_mat.block_mat[b] = term.coef * mat;
163 } else if (op_mat.connection(b) != bb) {
164 TRIQS_RUNTIME_ERROR << "ERROR: <atom_diag::get_op_mat> Monomials in operator does not connect the same subspaces.";
165 } else {
166 op_mat.block_mat[b] += term.coef * mat;
167 }
168 }
169
170 // Given the environment variable CHECK_ISSUE833 was set by the user
171 // throw an exception if the result of this function was previously effected by issue 833
172 // https://github.com/TRIQS/triqs/issues/833
173 static const bool check_issue833 = std::getenv("CHECK_ISSUE833");
174 if (check_issue833) {
175 auto U_left = dagger(eigensystems[op_mat.connection(b)].unitary_matrix);
176 auto U_right = eigensystems[b].unitary_matrix;
177 auto diff = op_mat.block_mat[b] - U_left * op_mat.block_mat[b] * U_right;
178 if (max_element(abs(make_regular(diff))) > 1e-10)
180 << "ERROR: The result of the get_op_mat function was previously affected by issue 833 (https://github.com/TRIQS/triqs/issues/833).\n"
181 "If you used the function prior to release 3.1.0 of TRIQS the result was incorrect.";
182 }
183 }
184 return op_mat;
185 }
186
187#undef ATOM_DIAG_METHOD
188
189 // -----------------------------------------------------------------
190
191 // Free functions
192
193 template <bool Complex> void h5_write(h5::group fg, std::string const &name, atom_diag<Complex> const &ad) {
194 using matrix_t = typename atom_diag<Complex>::matrix_t;
195 auto gr = fg.create_group(name);
196 write_hdf5_format(gr, ad); // NOLINT
197
198 h5_write_attribute(gr, "fops", ad.fops); // NOLINT
199 h5::write(gr, "h_atomic", ad.h_atomic);
200 h5::write(gr, "full_hs", ad.full_hs);
201 h5::write(gr, "sub_hilbert_spaces", ad.sub_hilbert_spaces);
202 h5::write(gr, "eigensystems", ad.eigensystems);
203 h5::write(gr, "creation_connection", ad.creation_connection);
204 h5::write(gr, "annihilation_connection", ad.annihilation_connection);
205
206 auto write_sparse = [&](std::string na, std::vector<std::vector<matrix_t>> const &Mvv) {
207 auto gr2 = gr.create_group(na);
208 for (int i = 0; i < Mvv.size(); ++i)
209 for (int j = 0; j < Mvv[i].size(); ++j)
210 if (!Mvv[i][j].is_empty()) h5::write(gr2, std::to_string(i) + ' ' + std::to_string(j), Mvv[i][j]);
211 };
212
213 write_sparse("c_matrices", ad.c_matrices);
214 write_sparse("cdag_matrices", ad.cdag_matrices);
215
216 h5::write(gr, "gs_energy", ad.gs_energy);
217 h5::write(gr, "vacuum_subspace_index", ad.vacuum_subspace_index);
218 h5::write(gr, "vacuum", ad.vacuum);
219 h5::write(gr, "quantum_numbers", ad.quantum_numbers);
220 }
221
222 // -----------------------------------------------------------------
223 template <bool Complex> void h5_read(h5::group fg, std::string const &name, atom_diag<Complex> &ad) {
224 using matrix_t = typename atom_diag<Complex>::matrix_t;
225 auto gr = fg.open_group(name);
226
227 h5_read_attribute(gr, "fops", ad.fops); // NOLINT
228 h5::try_read(gr, "h_atomic", ad.h_atomic);
229 h5::read(gr, "full_hs", ad.full_hs);
230 h5::read(gr, "sub_hilbert_spaces", ad.sub_hilbert_spaces);
231 h5::read(gr, "eigensystems", ad.eigensystems);
232 h5::read(gr, "creation_connection", ad.creation_connection);
233 h5::read(gr, "annihilation_connection", ad.annihilation_connection);
234
235 auto read_sparse = [&](std::string na, std::vector<std::vector<matrix_t>> &Mvv) {
236 Mvv.resize(ad.creation_connection.extent(0), std::vector<matrix_t>(ad.creation_connection.extent(1)));
237 auto gr2 = gr.open_group(na);
238 for (auto s : gr2.get_all_dataset_names()) {
239 std::stringstream ss(s);
240 std::string item1, item2;
241 std::getline(ss, item1, ' ');
242 std::getline(ss, item2, ' ');
243 int i = std::stoi(item1), j = std::stoi(item2);
244 h5::read(gr2, s, Mvv[i][j]);
245 }
246 };
247
248 read_sparse("c_matrices", ad.c_matrices);
249 read_sparse("cdag_matrices", ad.cdag_matrices);
250
251 h5::read(gr, "gs_energy", ad.gs_energy);
252 h5::read(gr, "vacuum_subspace_index", ad.vacuum_subspace_index);
253 h5::read(gr, "vacuum", ad.vacuum);
254 h5::try_read(gr, "quantum_numbers", ad.quantum_numbers);
255 ad.fill_first_eigenstate_of_subspace();
256 }
257
258 // -----------------------------------------------------------------
259
260 template <bool Complex> std::ostream &operator<<(std::ostream &os, atom_diag<Complex> const &ad) {
261 os << "Dimension of full Hilbert space: " << ad.get_full_hilbert_space_dim() << std::endl;
262 os << "Number of invariant subspaces: " << ad.n_subspaces() << std::endl;
263 for (int n_sp = 0; n_sp < ad.n_subspaces(); ++n_sp) {
264 os << "Subspace " << n_sp << ", ";
265 os << "lowest energy level : " << ad.eigensystems[n_sp].eigenvalues[0] << std::endl;
266 os << "Subspace dimension = " << ad.eigensystems[n_sp].eigenvalues.size() << std::endl;
267 //os << "-------------------------" << std::endl;
268 }
269 return os;
270 }
271
272 // -----------------------------------------------------------------
273
274 // Explicit instantiations for real and complex atom_diag
275 template class atom_diag<false>;
276 template class atom_diag<true>;
277
278 template void h5_write(h5::group fg, std::string const &, atom_diag<false> const &);
279 template void h5_write(h5::group fg, std::string const &, atom_diag<true> const &);
280 template void h5_read(h5::group fg, std::string const &, atom_diag<false> &);
281 template void h5_read(h5::group fg, std::string const &, atom_diag<true> &);
282
283 template std::ostream &operator<<(std::ostream &, atom_diag<true> const &);
284 template std::ostream &operator<<(std::ostream &, atom_diag<false> const &);
285
286} // namespace triqs::atom_diag
int size() const
Get the total number of blocks.
Backward-compatibility umbrella header pulling in the nda array library.
Provides a lightweight exact diagonalization solver for fermionic Hamiltonians.
Lightweight exact diagonalization solver for finite fermionic Hamiltonians.
Definition atom_diag.hpp:91
matrix< scalar_t > matrix_t
Dense matrix type with scalar entries of type scalar_t.
Definition atom_diag.hpp:97
std::vector< fock_state_t > const & get_fock_states(int sp_index) const
Get the Fock states spanning invariant subspace .
std::pair< int, matrix_t > get_matrix_element_of_monomial(operators::monomial_t const &op_vec, int B) const
Get the matrix representation of a monomial operator restricted to source subspace .
triqs::operators::many_body_operator_generic< scalar_t > many_body_op_t
Many-body operator type compatible with scalar_t.
int get_subspace_dim(int sp_index) const
Get the dimension of invariant subspace .
std::conditional_t< Complex, std::complex< double >, double > scalar_t
Scalar type of the matrix elements: double or std::complex<double>.
Definition atom_diag.hpp:94
matrix< scalar_t > const & get_unitary_matrix(int sp_index) const
Get the unitary matrix mapping the Fock basis of subspace to its eigenbasis.
Class representing a fundamental operator set.
Representation of a many-body operator acting on many-body states.
void h5_read_attribute(h5::object obj, std::string const &name, fundamental_operator_set &fops)
void h5_write_attribute(h5::object obj, std::string const &name, fundamental_operator_set const &fops)
uint64_t fock_state_t
Integer type representing a fermionic fock state .
nda::matrix< double > matrix_t
Matrix type for transformations involving real and reciprocal space vectors.
std::vector< canonical_ops_t > monomial_t
Type used to represent a monomial of canonical second quantization operators.
#define TRIQS_RUNTIME_ERROR
Throw a triqs::runtime_error with the current source location.
string to_string(string const &str)
Identity overload for std::string.
Provides a fundamental operator set class.
Provides a fast imperative representation of a many-body operator acting on states.
Provides generic many-body operators.
Provides a type for many-body states in a Hilbert (Fock) space.