TRIQS/triqs_modest 3.3.0
Brillouin zone summation
Loading...
Searching...
No Matches
embedding.cpp
Go to the documentation of this file.
1// Copyright (c) 2025--present, The Simons Foundation
2// This file is part of TRIQS/modest and is licensed under the terms of GPLv3 or later.
3// SPDX-License-Identifier: GPL-3.0-or-later
4// See LICENSE in the root of this distribution for details.
5
6#include "embedding.hpp"
7#include "utils/gf_supp.hpp"
8#include "utils/defs.hpp"
11#include <ranges>
12//#include <fmt/ranges.h>
13//#include <sstream>
14
15namespace triqs::modest {
16 embedding::embedding(std::vector<long> sigma_embed_decomposition, std::vector<std::vector<long>> imp_decompositions, nda::array<imp_block_t, 2> psi,
17 std::vector<std::string> sigma_names)
18 : sigma_embed_decomp{std::move(sigma_embed_decomposition)},
19 imp_decomps{std::move(imp_decompositions)},
20 _sigma_names{std::move(sigma_names)},
21 psi{std::move(psi)} {
22
23 alpha_names = range(n_alpha()) | //
24 stdv::transform([](auto i) { return std::to_string(i); }) | tl::to<std::vector>();
25
26 // Compute reverse_psi
27 reverse_psi = imp_decomps | stdv::transform([&](auto &v) { return nda::array<std::vector<std::array<long, 2>>, 2>(v.size(), n_sigma()); })
28 | tl::to<std::vector>();
29
30 for (auto [alpha, sigma] : this->psi.indices()) { // beware the psi (argument) has been moved. use 2 names ?
31 auto const &y = this->psi(alpha, sigma);
32 if (y.imp_idx == -1) continue;
33 reverse_psi[y.imp_idx](y.gamma, y.tau).push_back(std::array{alpha, sigma});
34 }
35
36 // FIXME : MAKE ALL NECESSARY CHECKS.... since
37 }
38
39 embedding make_embedding_impl(local_space const &C_space, nda::array<std::vector<long>, 2> const &block_decomposition,
40 std::optional<std::vector<long>> const &atom_to_imp) {
41
42 long n_alpha = stdr::fold_left(block_decomposition(r_all, 0) | stdv::transform([](auto const &x) { return long(x.size()); }), 0, std::plus<>());
43
44 auto embed_bl_sizes = std::vector<long>{};
45 auto imp_decompositions = std::vector<std::vector<long>>{};
46 auto psi = nda::array<embedding::imp_block_t, 2>(n_alpha, C_space.n_sigma());
47
48 auto n_atoms = C_space.atoms_block_decomposition().shape()[0];
49
50 auto const &atom_imp_map = (atom_to_imp) ? *atom_to_imp : range(n_atoms) | tl::to<std::vector>();
51
52 for (auto atom : range(n_atoms)) {
53 for (auto &&[idx, bl_size] : enumerate(block_decomposition(atom, 0))) {
54 for (auto sigma : range(C_space.n_sigma())) { psi(embed_bl_sizes.size(), sigma) = {atom_imp_map[atom], idx, sigma}; }
55 embed_bl_sizes.emplace_back(bl_size);
56 }
57 auto build_impurity = C_space.first_shell_of_its_equiv_cls(atom) == atom;
58 if (build_impurity || !atom_to_imp) imp_decompositions.push_back(std::move(block_decomposition(atom, 0)));
59 }
60 return embedding{std::move(embed_bl_sizes), std::move(imp_decompositions), std::move(psi), C_space.sigma_names()};
61 }
62
63 // -------------------------------------------------------------------------------------------
65 std::unordered_map<long, long> atom_to_cls, cls_to_imp;
66 long imp_idx = 0;
67 for (auto const &[ish, sh] : enumerate(C_space.atomic_shells())) {
68 atom_to_cls[ish] = sh.cls_idx;
69 if (not cls_to_imp.contains(sh.cls_idx)) cls_to_imp[sh.cls_idx] = imp_idx++;
70 }
71 auto atom_to_imp_idx = range(C_space.atomic_shells().size()) | stdv::transform([&](auto const &atom) { return cls_to_imp[atom_to_cls[atom]]; })
72 | tl::to<std::vector>();
73
74 auto decomp = C_space.atoms_block_decomposition();
75
76 return make_embedding_impl(C_space, decomp, atom_to_imp_idx);
77 };
78
79 // -------------------------------------------------------------------------------------------
83
84 // -------------------------------------------------------------------------------------------
85 embedding make_embedding(local_space const &C_space, bool use_atom_equivalences) {
86 return (use_atom_equivalences) ? make_embedding_with_equivalences(C_space) : make_embedding_with_no_equivalences(C_space);
87 }
88
89 // ------------------------------ PRINTING -------------------------------------------------------------
90
91 auto format_as(embedding::imp_block_t const &p) {
92 //return fmt::format("({},{},{})", p.n_imp, p.gamma, p.tau);
93 return fmt::format("(imp_idx = {}, γ = {}, τ = {})", p.imp_idx, p.gamma, p.tau);
94 }
95
96 // -------------------------------------------------------------------------------------------------------
97
98 std::string embedding::description(bool verbosity) const {
99 auto sigma_embed_shape = this->sigma_embed_block_shape();
100 auto impurities_shape_list = this->imp_block_shape();
101
102 std::ostringstream out;
103 auto out1 = indented_ostream(out, 2); // same stream, but shifted by 2 spaces
104 auto out2 = indented_ostream(out, 4);
105 auto out3 = indented_ostream(out, 6);
106
107 if (!verbosity) {
108 out << "Embedding: ";
109 out << fmt::format("{} impurities\n", this->n_impurities());
110 out1 << "Σ_embed block decomposition:\n";
111 auto pr_vec = [](auto const &V) {
112 return fmt::format("{}\n", fmt::join(V | stdv::transform([](auto x) { return fmt::format("{:>3}", x); }), " "));
113 };
114 out2 << "dim_α: " << pr_vec(this->sigma_embed_decomp);
115 out2 << " α: " << pr_vec(range(this->sigma_embed_decomp.size()));
116 out1 << "\nImpurities\n";
117 out2 << "Block dimensions, dim_γ for all γ:\n";
118 for (auto &&[n, dec] : enumerate(this->imp_decomps)) {
119 auto head = fmt::format("[n_imp = {}]", n);
120 out3 << fmt::format("{} dim_γ = {}", head, pr_vec(dec));
121 out3 << fmt::format("{:>{}} γ = {}", " ", head.size(), pr_vec(range(dec.size())));
122 }
123 return out.str();
124 }
125
126 out << "Embedding:\n";
127 out1 << fmt::format("Spin index (σ/τ) names: {}\n\n", this->sigma_names());
128 out1 << "Σ_embed block decomposition:\n";
129 auto pr_vec = [](auto const &V) {
130 return fmt::format("{}\n", fmt::join(V | stdv::transform([](auto x) { return fmt::format("{:>3}", x); }), " "));
131 };
132 out2 << "dim_α: " << pr_vec(this->sigma_embed_decomp);
133 out2 << " α: " << pr_vec(range(this->sigma_embed_decomp.size()));
134 //out << fmt::format(" {}\n", enumerate(E.sigma_embed_decomp));
135 out1 << "\nImpurities\n";
136 out2 << "Block dimensions, dim_γ for all γ:\n";
137 for (auto &&[n, dec] : enumerate(this->imp_decomps)) {
138 auto head = fmt::format("[n_imp = {}]", n);
139 out3 << fmt::format("{} dim_γ = {}", head, pr_vec(dec));
140 out3 << fmt::format("{:>{}} γ = {}", " ", head.size(), pr_vec(range(dec.size())));
141 }
142 out2 << "Gf Block structures for solvers as names, [dim]:\n";
143 for (auto &&[n, ish] : enumerate(impurities_shape_list)) {
144 auto formatted_vec = ish | stdv::transform([](auto &&p) { return fmt::format("{} [{}]", p.first, p.second); }) | tl::to<std::vector>();
145 out3 << fmt::format("[imp_idx = {}] {}\n", n, fmt::join(formatted_vec, ", "));
146 }
147 out1 << "\nMapping ψ(α,σ) = (imp_idx, γ, τ) \n";
148 //out2 << fmt::format("{}", E.psi);
149 auto row_labels = range(this->n_alpha()) | stdv::transform([](auto x) { return fmt::format("α = {}", x); }) | tl::to<std::vector>();
150 auto col_labels = range(this->n_sigma()) | stdv::transform([&](auto i) { return fmt::format("σ = {} / {}", i, this->sigma_names()[i]); })
151 | tl::to<std::vector>();
152 nda::format_as_table(out3, this->psi, row_labels, col_labels);
153
154 return out.str();
155 }
156
157 std::ostream &operator<<(std::ostream &out, embedding const &E) {
158 out << E.description(false);
159 return out;
160 }
161
162 //-----------------------------------------------------------------
163
165 auto res = nda::zeros<long>(n_alpha(), n_sigma());
166 for (long alpha : range(n_alpha())) res(alpha, r_all) = sigma_embed_decomp[alpha];
167 return {.names = {alpha_names, _sigma_names}, .dims = std::move(res)};
168 }
169
170 //-----------------------------------------------------------------
171
172 std::vector<gf_struct_t> embedding::imp_block_shape() const {
173 auto l = [this](std::vector<long> const &decomp) -> gf_struct_t {
174 gf_struct_t res;
175 for (auto sigma : range(n_sigma())) {
176 for (auto const &[idx, bl_size] : enumerate(decomp)) res.emplace_back(fmt::format("{}_{}", _sigma_names[sigma], idx), bl_size);
177 }
178 return res;
179 };
180 return imp_decomps | stdv::transform(l) | tl::to<std::vector>();
181 }
182
183 //-----------------------------------------------------------------
184 embedding embedding::flip_spin(long alpha) const {
185 if (n_sigma() != 2) throw std::runtime_error(fmt::format("Can not flip spin when {} != 2", n_sigma()));
186 auto new_psi = this->psi;
187 for (auto sigma : range(n_sigma())) new_psi(alpha, sigma).tau = 1 - new_psi(alpha, sigma).tau;
188 return {this->sigma_embed_decomp, this->imp_decomps, new_psi, this->_sigma_names};
189 }
190
191 //-----------------------------------------------------------------
192 embedding embedding::flip_spin(std::vector<long> alphas) const {
193 auto res = *this;
194 for (auto alpha : alphas) { res = res.flip_spin(alpha); }
195 return res;
196 }
197
198 //-----------------------------------------------------------------
199
200 embedding embedding::drop(long imp_idx) const {
201 auto new_imp_decomps = this->imp_decomps;
202 auto new_psi = this->psi;
203 new_imp_decomps.erase(begin(new_imp_decomps) + imp_idx);
204 new_psi = nda::map([&](imp_block_t const &b) -> imp_block_t {
205 if (b.imp_idx < imp_idx) return {b.imp_idx, b.gamma, b.tau};
206 if (b.imp_idx > imp_idx) return {b.imp_idx - 1, b.gamma, b.tau};
207 return {-1, 0, 0}; // imp_idx == b.n_imp : we connect to nothing now
208 })(new_psi);
209 return {this->sigma_embed_decomp, new_imp_decomps, new_psi, this->_sigma_names};
210 }
211
212 //-----------------------------------------------------------------
213 // HL: re-implementation of replace. Please review.
214 embedding embedding::replace(long imp_idx_to_remove, long imp_idx_to_replace_with) const {
215 auto new_psi = this->psi;
216 if (imp_block_shape()[imp_idx_to_remove] != imp_block_shape()[imp_idx_to_replace_with])
217 throw std::runtime_error(fmt::format("The number of blocks that imp_to_remove= {} and imp_to_replace_with={} connect to do not match!",
218 imp_idx_to_remove, imp_idx_to_replace_with));
219 new_psi = nda::map([&](imp_block_t const &b) -> imp_block_t {
220 if (b.imp_idx == imp_idx_to_remove) return {imp_idx_to_replace_with, b.gamma, b.tau};
221 return b;
222 })(new_psi);
223 return embedding(this->sigma_embed_decomp, this->imp_decomps, new_psi, this->_sigma_names).drop(imp_idx_to_remove);
224 }
225
226 //-----------------------------------------------------------------
227
228 embedding embedding::split(long imp_idx, std::function<bool(long)> p) const {
229 // auto res = *this; // copy. We will modify the impurities_gf_struct and the table
230 auto new_psi = this->psi;
231 auto new_imp_decomps = this->imp_decomps;
232 auto const &igs = new_imp_decomps[imp_idx];
233 auto indices = nda::range(long(igs.size())) | tl::to<std::vector>(); // {0,1,2, ... n_gamma}
234
235 // separate the block indices according to p. We put the indices in regard to the blocks and partition them
236 auto mid = std::stable_partition(begin(indices), end(indices), p);
237
238 // make 2 new gf_struct from the indices partition
239 auto get_igs = [&](int i) { return igs[i]; };
240 auto stru1 = stdr::subrange{begin(indices), mid} | stdv::transform(get_igs) | tl::to<std::vector>();
241 auto stru2 = stdr::subrange{mid, end(indices)} | stdv::transform(get_igs) | tl::to<std::vector>();
242 long L1 = long(stru1.size());
243
244 // Store the new gf_struct in result at the position of the old impurity model
245 new_imp_decomps[imp_idx] = std::move(stru1);
246 new_imp_decomps.insert(begin(new_imp_decomps) + imp_idx + 1, std::move(stru2));
247
248 // compute the inverse table of indices
249 auto indices_inv = indices;
250 for (auto i : range(indices.size())) indices_inv[indices[i]] = i;
251
252 new_psi = nda::map([&](imp_block_t const &b) -> imp_block_t {
253 if (b.imp_idx < imp_idx) return b;
254 if (b.imp_idx > imp_idx) return {b.imp_idx + 1, b.gamma, b.tau};
255 long new_gamma = indices_inv[b.gamma];
256 return (new_gamma < L1 ? imp_block_t{b.imp_idx, new_gamma, b.tau} : imp_block_t{b.imp_idx + 1, new_gamma - L1, b.tau});
257 })(new_psi);
258 return {this->sigma_embed_decomp, new_imp_decomps, new_psi, this->_sigma_names};
259 }
260
261 // ----------------------------------------------------------------------
262
263 embedding embedding::split(long imp_idx, std::vector<long> const &block_list) const {
264 if (not stdr::all_of(block_list, [&](auto i) { return (i >= 0) and (i < n_gamma(imp_idx)); }))
265 throw std::runtime_error(fmt::format("[embedding::split] The list of block indices {} is incorrect. Indices i should all be 0<= i < N_γ = {}",
266 block_list, n_gamma(imp_idx)));
267 return split(imp_idx, [&](long idx) { return stdr::find(block_list, idx) != block_list.end(); });
268 }
269
270 // ----------------------------------------------------------------------
271
272 block2_matrix_t embedding::embed(std::vector<block_matrix_t> const &Sigma_imp_static_vec) const {
273 auto Sigma_static_embed = nda::array<nda::matrix<dcomplex>, 2>(n_alpha(), n_sigma());
274 for (auto &&[alpha, sigma] : psi.indices()) {
275 auto bl_size = sigma_embed_decomp[alpha];
276 Sigma_static_embed(alpha, sigma) = nda::zeros<dcomplex>(bl_size, bl_size);
277 }
278 for (auto &&[S, m] : zip(Sigma_static_embed, psi)) {
279 if (m.imp_idx == -1) continue;
280 S = Sigma_imp_static_vec[m.imp_idx][m.gamma + n_gamma(m.imp_idx) * m.tau];
281 }
282 return Sigma_static_embed;
283 }
284
285 // ----------------------------------------------------------------------
286 std::vector<block_matrix_t> embedding::extract(block2_matrix_t const &matrix_C) const {
287
288 auto imp_gf_stru_list = imp_block_shape();
289
290 auto matrix_E = nda::matrix<nda::matrix<dcomplex>>(n_alpha(), n_sigma());
291 for (auto [alpha, r_alpha] : enumerated_sub_slices(sigma_embed_decomp)) {
292 for (auto sigma : range(n_sigma())) { matrix_E(alpha, sigma) = matrix_C(0, sigma)(r_alpha, r_alpha); }
293 }
294
295 auto extract_one_imp = [&](long n_imp) {
296 auto matrix_imp = std::vector<nda::matrix<dcomplex>>{};
297 for (auto [bl, bl_size] : imp_gf_stru_list[n_imp]) { matrix_imp.emplace_back(bl_size, bl_size); }
298 auto const &rpsi = reverse_psi[n_imp];
299 for (auto [gamma, tau] : rpsi.indices()) {
300 auto [alpha, sigma] = rpsi(gamma, tau)[0];
301 matrix_imp[gamma + n_gamma(n_imp) * tau] = matrix_E(alpha, sigma);
302 }
303 return matrix_imp;
304 };
305
306 return range(n_impurities()) | stdv::transform(extract_one_imp) | tl::to<std::vector>();
307 }
308
309 // --------------------------------------------------------------------------------------------
310
311 std::vector<std::vector<nda::array<dcomplex, 3>>> embedding::extract(nda::array<dcomplex, 4> const &g_loc) const {
312
313 auto imp_gf_stru_list = imp_block_shape();
314 auto n_w = g_loc.extent(0);
315
316 auto gloc_E = nda::array<nda::array<dcomplex, 3>, 2>(n_alpha(), n_sigma());
317 for (auto [alpha, r_alpha] : enumerated_sub_slices(sigma_embed_decomp)) {
318 for (auto sigma : range(n_sigma())) { gloc_E(alpha, sigma) = g_loc(r_all, sigma, r_alpha, r_alpha); }
319 }
320 auto extract_one_imp = [&](long n_imp) {
321 auto g_imp = std::vector<nda::array<dcomplex, 3>>{};
322 for (auto [bl, bl_size] : imp_gf_stru_list[n_imp]) { g_imp.emplace_back(n_w, bl_size, bl_size); }
323 auto const &rpsi = reverse_psi[n_imp];
324 for (auto [gamma, tau] : rpsi.indices()) {
325 auto [alpha, sigma] = rpsi(gamma, tau)[0];
326 g_imp[gamma + n_gamma(n_imp) * tau] = gloc_E(alpha, sigma);
327 }
328 return g_imp;
329 };
330
331 return range(n_impurities()) | stdv::transform(extract_one_imp) | tl::to<std::vector>();
332 }
333 // --------------------------------------------------------------------------------------------
334
335 nda::array<nda::array<dcomplex, 3>, 2> embedding::embed(std::vector<std::vector<nda::array<dcomplex, 3>>> const &Sigma_imp_vec) const {
336 auto Sigma_embed = nda::array<nda::array<dcomplex, 3>, 2>(n_alpha(), n_sigma());
337 for (auto &&[S, m] : zip(Sigma_embed, psi)) {
338 if (m.imp_idx == -1) continue;
339 S = Sigma_imp_vec[m.imp_idx][m.gamma + n_gamma(m.imp_idx) * m.tau];
340 }
341 return Sigma_embed;
342 }
343 // --------------------------------------------------------------------------------------------
344
345 //FIXME : protect this function as it will only work with a Pi_embed_decomp
346 std::vector<nda::array<dcomplex, 5>> embedding::extract(nda::array<dcomplex, 5> const &pi_loc) const {
347
348 auto Pi_E = std::vector<nda::array<dcomplex, 5>>{};
349 for (auto [alpha, r_alpha] : enumerated_sub_slices(sigma_embed_decomp)) { Pi_E.emplace_back(pi_loc(r_all, r_alpha, r_alpha, r_alpha, r_alpha)); }
350
351 auto extract_one_imp = [&](long n_imp) {
352 auto const &rpsi = reverse_psi[n_imp];
353 auto [alpha, sigma] = rpsi(0, 0)[0];
354 return Pi_E[alpha];
355 };
356
357 return range(n_impurities()) | stdv::transform(extract_one_imp) | tl::to<std::vector>();
358 }
359 // --------------------------------------------------------------------------------------------
360
361 //FIXME : protect this function as it will only work with a Pi_embed_decomp
362 std::vector<nda::array<dcomplex, 5>> embedding::embed(std::vector<nda::array<dcomplex, 5>> const &pi_imp_vec) const {
363 auto Pi_embed = std::vector<nda::array<dcomplex, 5>>(n_alpha());
364 for (auto alpha : range(n_alpha())) {
365 auto const &m = psi(alpha, 0);
366 if (m.imp_idx == -1) continue; // check
367 Pi_embed[alpha] = pi_imp_vec[m.imp_idx];
368 }
369 return Pi_embed;
370 }
371
372} // namespace triqs::modest
A custom output stream that automatically indents each new line by a specified number of spaces.
Definition streams.hpp:88
The embedding class.
Definition embedding.hpp:29
C2PY_IGNORE gf_struct2_t sigma_embed_block_shape() const
Gf block structure for Σ_embed.
long n_impurities() const
Number of impurities.
long n_gamma(long imp_idx) const
Number of blocks in γ for the Σ_imp[imp_idx].
long n_sigma() const
Number of blocks in σ for the Σ_embed.
std::vector< gf_struct_t > imp_block_shape() const
Gf block structure for the impurity solvers.
embedding flip_spin(long alpha) const
Flip the spins (σ) for block α.
embedding(std::vector< long > sigma_embed_decomposition, std::vector< std::vector< long > > imp_decompositions, nda::array< imp_block_t, 2 > psi, std::vector< std::string > sigma_names)
Default constructor.
Definition embedding.cpp:16
std::vector< std::string > sigma_names() const
The names of the sigma indices.
std::vector< block_gf< Mesh, matrix_valued > > extract(block2_gf< Mesh, matrix_valued > const &g_loc) const
embed tensors
long n_alpha() const
Number of blocks in α for the Σ_embed.
std::string description(bool verbosity=false) const
Summarize the embedding object.
Definition embedding.cpp:98
embedding drop(long imp_idx) const
Remove an impurity from the embedding table ψ
embedding split(long imp_idx, std::function< bool(long)> p) const
Predicate p (long block_idx) -> 0 or 1.
embedding replace(long imp_idx_to_remove, long imp_idx_to_replace_with) const
Replaces one impurity in the embedding table ψ.
block2_gf< Mesh, matrix_valued > embed(std::vector< block_gf< Mesh, matrix_valued > > const &Sigma_imp_vec) const
embed single-particle quantities
Describe the atomic orbitals within downfolded space.
long n_sigma() const
Dimension of the σ index.
nda::array< std::vector< long >, 2 > const & atoms_block_decomposition() const
List of all blocks spanning 𝓒 space -> atoms_block_decomposition.
std::vector< atomic_orbs > const & atomic_shells() const
List of all atomic shells spanning the 𝓒 space.
std::vector< std::string > sigma_names() const
names of spin indices for naming blocks in block_gf
long first_shell_of_its_equiv_cls(long idx) const
Given the index idx of an atomic shell, return the index of the first atomic shell of its equivalence...
embedding make_embedding(local_space const &C_space, bool use_atom_equivalences)
Make an embedding from the local space.
Definition embedding.cpp:85
void format_as_table(std::ostream &out, nda::Matrix auto const &mat, auto const &row_labels, auto const &col_labels)
Format the matrix mat as a table, with row/col_labels.
embedding make_embedding_with_no_equivalences(local_space const &C_space)
Definition embedding.cpp:80
std::ostream & operator<<(std::ostream &out, one_body_elements_on_grid const &)
Definition printing.cpp:73
embedding make_embedding_with_equivalences(local_space const &C_space)
Definition embedding.cpp:64
auto format_as(embedding::imp_block_t const &p)
Definition embedding.cpp:91
embedding make_embedding_impl(local_space const &C_space, nda::array< std::vector< long >, 2 > const &block_decomposition, std::optional< std::vector< long > > const &atom_to_imp)
Definition embedding.cpp:39
nda::array< nda::matrix< dcomplex >, 2 > block2_matrix_t
Definition embedding.hpp:15
static constexpr auto r_all
Definition defs.hpp:40
generator< std::pair< long, nda::range > > enumerated_sub_slices(auto sub_div)