TRIQS/triqs_modest 3.3.0
Modular Electronic Structure Toolkit
Loading...
Searching...
No Matches
embedding.hpp
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#pragma once
7#include "./downfolding.hpp"
8#include "loaders.hpp"
9#include "utils/defs.hpp"
10#include "utils/gf_supp.hpp"
11#include "utils/nda_supp.hpp"
12#include "utils/range_supp.hpp"
13#include "utils/embed_supp.hpp"
14#include <fmt/ranges.h>
15
16namespace triqs {
17 using block_matrix_t = std::vector<nda::matrix<dcomplex>>;
18 using block2_matrix_t = nda::array<nda::matrix<dcomplex>, 2>;
19} // namespace triqs
20
21namespace triqs::modest {
22
32 class embedding {
33
34 // decomposition of C used by Sigma
35 std::vector<long> sigma_embed_decomp;
36
37 // decomposition of each impurity
38 std::vector<std::vector<long>> imp_decomps;
39
40 // names of the sigma (and tau) indices
41 std::vector<std::string> _sigma_names;
42
43 // names of the alpha indices
44 std::vector<std::string> alpha_names;
45
46 // stream
47 friend std::ostream &operator<<(std::ostream &out, embedding const &E);
48
50 friend void h5_write(h5::group g, std::string const &name, embedding const &x);
51 friend void h5_read(h5::group g, std::string const &name, embedding &x);
52
53 public:
55
57 struct imp_block_t {
58
59 long imp_idx = 0;
60 long gamma = 0;
61 long tau = 0;
62
63 imp_block_t() = default;
64 imp_block_t(long n_imp, long gamma, long tau) : imp_idx(n_imp), gamma(gamma), tau(tau) {}
65 friend bool operator==(imp_block_t const &, imp_block_t const &) = default;
66
67 // FIXME : merge with format_as
68 friend std::ostream &operator<<(std::ostream &out, imp_block_t const &x) {
69 return out << fmt::format("(imp_idx = {}, γ = {}, τ = {})", x.imp_idx, x.gamma, x.tau);
70 }
71 };
72
74
75 private:
76 // For each imp, for all (γ, τ), a list of all (α, σ) such that ψ[α, σ] ==(n_imp, γ, τ)
77 nda::array<imp_block_t, 2> psi; // ψ [α, σ] --> (n_imp, γ, τ)
78
79 // reverse ψ [n_imp] -> (γ, τ) -> [(α, σ)]
80 std::vector<nda::array<std::vector<std::array<long, 2>>, 2>> reverse_psi;
81
82 public:
93 embedding(std::vector<long> sigma_embed_decomposition, std::vector<std::vector<long>> imp_decompositions, nda::array<imp_block_t, 2> psi,
94 std::vector<std::string> sigma_names);
95
97
99 embedding() = default; // for h5_read
100
102
105
110 imp_block_t operator[](long alpha, long sigma) const { return psi(alpha, sigma); }
111
113 [[nodiscard]] nda::array<imp_block_t, 2> psi_map() const { return psi; }
114
116 [[nodiscard]] long n_alpha() const { return psi.extent(0); }
117
119 [[nodiscard]] long n_sigma() const { return psi.extent(1); }
120
122 [[nodiscard]] long n_gamma(long imp_idx) const { return long(imp_decomps[imp_idx].size()); }
123
125 [[nodiscard]] long n_impurities() const { return long(imp_decomps.size()); }
126
128 [[nodiscard]] std::vector<std::string> sigma_names() const { return _sigma_names; };
129
131 [[nodiscard]] std::vector<long> imp_decomposition(long imp_idx) const { return imp_decomps[imp_idx]; };
132
134 C2PY_IGNORE gf_struct2_t embed_block_structure() const;
135
137 std::vector<gf_struct_t> imp_block_structure() const;
138
140 std::string description(bool verbosity = false) const;
142
143 // ****************** Operation methods to change the embedding **********************
144
150
151 // --------------------------------------------------------
152 bool operator==(embedding const &other) const = default;
153 // --------------------------------------------------------
154
164 embedding drop_imp(long imp_idx) const;
165
178 embedding replace_imp(long imp_idx_old, long imp_idx_new) const;
179
189 embedding swap_sigma(long alpha) const;
190
200 embedding swap_sigma(std::vector<long> alphas) const;
201
211 embedding slice_sigma() const;
212
224 embedding split_imp(long imp_idx, std::function<bool(long)> p) const;
225
236 embedding split_imp(long imp_idx, std::vector<long> const &block_list) const;
237
238 embedding split_imp(long imp_idx, std::initializer_list<const char *> x) = delete;
239
252 embedding split_imp_block(long imp_idx, long gamma, std::vector<long> const &new_dims) const;
253
266
268
269 //------------------------------------------------------------------------------------
270
286 template <typename Mesh> std::vector<std::pair<block_gf<Mesh, matrix_valued>, block_matrix_t>> make_zero_imp_self_energies(Mesh const &mesh) {
287
288 auto make_block_matrix = [](auto const &gf_struct) {
289 return gf_struct | stdv::transform([](auto &x) {
290 auto bl_size = x.second;
291 return nda::zeros<dcomplex>(bl_size, bl_size);
292 })
293 | tl::to<std::vector<nda::matrix<dcomplex>>>();
294 };
295
296 auto make_self_energy = [&](auto const &gf_struct) {
297 auto Sigma_static = make_block_matrix(gf_struct);
298 auto Sigma_dynamic = block_gf<Mesh, matrix_valued>{mesh, gf_struct};
299 return std::make_pair(Sigma_dynamic, Sigma_static);
300 };
301
302 return imp_block_structure() | stdv::transform(make_self_energy) | tl::to<std::vector>();
303 }
304 //------------------------------------------------------------------------------------
305
311
312 // ****************************************************
313 // Embedding Extract/Embed methods
314 // ****************************************************
315
316 //--------------------- Extract ---------------------------------------
317
338 template <int Rank> std::vector<std::vector<nda::array<dcomplex, Rank>>> extract(std::vector<nda::array<dcomplex, Rank>> const &X) const {
339
340 using array_t = nda::array<dcomplex, Rank>;
341
342 // Validate input dimensions
343 if (long(X.size()) != n_sigma())
344 throw std::runtime_error(
345 fmt::format("[embedding::extract] Wrong number of spin channels: got {} but embedding has n_sigma = {}", X.size(), n_sigma()));
346
347 auto dim_C = stdr::fold_left(sigma_embed_decomp, 0L, std::plus<>());
348 for (long sigma = 0; sigma < n_sigma(); ++sigma) {
349 auto actual_dim = X[sigma].extent(detail::has_frequency<Rank> ? 1 : 0);
350 if (actual_dim != dim_C)
351 throw std::runtime_error(fmt::format(
352 "[embedding::extract] Dimension mismatch for spin channel {}: got {} but expected C-space dimension {}", sigma, actual_dim, dim_C));
353 }
354
355 // Does this rank have a frequency index?
356 [[maybe_unused]] long n_w = 0;
357 if constexpr (detail::has_frequency<Rank>) { n_w = X[0].extent(0); }
358
359 // Extract from the array X the blocks for each (alpha, sigma)
360 auto embed_blocks = nda::array<array_t, 2>(n_alpha(), n_sigma());
361 for (auto &&[alpha, r_alpha] : enumerated_sub_slices(sigma_embed_decomp)) {
362 for (auto sigma : range(n_sigma())) { embed_blocks(alpha, sigma) = detail::extract_diagonal(X[sigma], r_alpha); }
363 }
364
365 // Lambda to extract one impurity using the reverse_psi map
366 auto imp_gf_stru_list = imp_block_structure();
367 auto extract_one_imp = [&](long n_imp) -> std::vector<array_t> {
368 std::vector<array_t> blocks_imp;
369 for (auto [bl_name, bl_size] : imp_gf_stru_list[n_imp]) { blocks_imp.push_back(detail::make_zero_array<Rank, dcomplex>(bl_size, n_w)); }
370 auto const &rpsi = reverse_psi[n_imp];
371 for (auto [gamma, tau] : rpsi.indices()) {
372 auto [alpha, sigma] = rpsi(gamma, tau)[0];
373 blocks_imp[gamma + n_gamma(n_imp) * tau] = embed_blocks(alpha, sigma);
374 }
375 return blocks_imp;
376 };
377
378 // Apply lambda to each impurity.
379 return range(n_impurities()) | stdv::transform(extract_one_imp) | tl::to<std::vector>();
380 }
381
382 //--------------------- Embed -----------------------------------------
383
408 template <int Rank> std::vector<nda::array<dcomplex, Rank>> embed(std::vector<std::vector<nda::array<dcomplex, Rank>>> const &imps_blocks) const {
409
410 using array_t = nda::array<dcomplex, Rank>;
411
412 // Validate input structure
413 if (long(imps_blocks.size()) != n_impurities())
414 throw std::runtime_error(
415 fmt::format("[embedding::embed] Wrong number of impurities: got {} but embedding has {}", imps_blocks.size(), n_impurities()));
416
417 auto imp_gf_stru = imp_block_structure();
418 for (long imp = 0; imp < n_impurities(); ++imp) {
419 auto expected_n_blocks = long(imp_gf_stru[imp].size());
420 if (long(imps_blocks[imp].size()) != expected_n_blocks)
421 throw std::runtime_error(
422 fmt::format("[embedding::embed] Impurity {} has {} blocks but embedding expects {}", imp, imps_blocks[imp].size(), expected_n_blocks));
423 for (long b = 0; b < expected_n_blocks; ++b) {
424 auto expected_dim = imp_gf_stru[imp][b].second;
425 auto actual_dim = imps_blocks[imp][b].extent(detail::has_frequency<Rank> ? 1 : 0);
426 if (actual_dim != expected_dim)
427 throw std::runtime_error(
428 fmt::format("[embedding::embed] Impurity {}, block {} has dimension {} but embedding expects {}", imp, b, actual_dim, expected_dim));
429 }
430 }
431
432 // Does this rank have a frequency index?
433 [[maybe_unused]] long n_w = 0;
434 if constexpr (detail::has_frequency<Rank>) { n_w = imps_blocks[0][0].extent(0); }
435
436 // Build the embedded array
437 auto embed_blocks = nda::array<array_t, 2>(n_alpha(), n_sigma());
438 for (auto &&[alpha, sigma] : psi.indices()) {
439 auto bl_size = sigma_embed_decomp[alpha];
440 embed_blocks(alpha, sigma) = detail::make_zero_array<Rank, dcomplex>(bl_size, n_w);
441 }
442
443 // Use psi map to fill the embedded blocks array
444 for (auto &&[block, m] : zip(embed_blocks, psi)) {
445 if (m.imp_idx == -1) continue;
446 block = imps_blocks[m.imp_idx][m.gamma + n_gamma(m.imp_idx) * m.tau];
447 }
448
449 // Total dimension of the C space
450 auto dim_C = stdr::fold_left(sigma_embed_decomp, 0, std::plus<>());
451
452 // Build the result from the embedded view and return
453 std::vector<array_t> result;
454 for (auto sigma : range(n_sigma())) {
455 auto full_tensor = detail::make_zero_array<Rank, dcomplex>(dim_C, n_w);
456 for (auto &&[alpha, r_alpha] : enumerated_sub_slices(sigma_embed_decomp)) {
457 detail::embed_diagonal(full_tensor, r_alpha, embed_blocks(alpha, sigma));
458 }
459 result.push_back(std::move(full_tensor));
460 }
461
462 return result;
463 }
464
476 template <typename Mesh> block2_gf<Mesh, matrix_valued> embed(std::vector<block_gf<Mesh, matrix_valued>> const &Sigma_imp_vec) const {
477 // Check number of impurities
478 if (long(Sigma_imp_vec.size()) != n_impurities())
479 throw std::runtime_error(fmt::format("[embedding::embed] Wrong number of impurity self-energies: got {} but embedding has {} impurities",
480 Sigma_imp_vec.size(), n_impurities()));
481 // Check that all meshes are the same for Sigma_imp_vec
482 if (not all_equal(Sigma_imp_vec | stdv::transform([](auto &&x) -> decltype(auto) { return x[0].mesh(); })))
483 throw std::runtime_error{"[embedding_desc::embed]: meshes of solvers are not all equal"};
484
485 // build the result
486 auto const &mesh = Sigma_imp_vec[0][0].mesh();
487 auto Sigma_embed = make_block2_gf(mesh, this->embed_block_structure());
488 for (auto &&[S, m] : zip(Sigma_embed, psi)) {
489 if (m.imp_idx == -1) continue;
490 S() = Sigma_imp_vec[m.imp_idx][m.gamma + n_gamma(m.imp_idx) * m.tau];
491 }
492 return Sigma_embed;
493 }
494
502 template <typename Mesh> std::vector<block_gf<Mesh, matrix_valued>> extract(block2_gf<Mesh, matrix_valued> const &g_loc) const {
503 // Check that the decomposition of g_loc matches either sigma_embed_decomp
504 if (auto decomp = get_struct(g_loc).dims(r_all, 0) | tl::to<std::vector>(); decomp != this->sigma_embed_decomp) {
505 if (decomp.size() != 1) throw std::runtime_error{"extract: g should have decomp = sigma_embedding_decomp or [1]"};
506 return extract(decomposition_view(g_loc, this->embed_block_structure()));
507 }
508
509 auto imp_gf_stru_list = imp_block_structure();
510 auto extract_one_imp = [&](long n_imp) {
511 auto gimp = block_gf{g_loc(0, 0).mesh(), imp_gf_stru_list[n_imp]};
512 auto const &rpsi = reverse_psi[n_imp];
513 for (auto [gamma, tau] : rpsi.indices()) {
514 if (rpsi(gamma, tau).empty()) continue;
515 auto [alpha, sigma] = rpsi(gamma, tau)[0];
516 gimp[gamma + n_gamma(n_imp) * tau].data() = g_loc(alpha, sigma).data();
517 }
518 return gimp;
519 };
520
521 return range(n_impurities()) | stdv::transform(extract_one_imp) | tl::to<std::vector>();
522 }
523
534 template <typename Mesh>
535 std::pair<block2_gf<Mesh, matrix_valued>, block2_matrix_t> embed(std::vector<block_gf<Mesh, matrix_valued>> const &Sigma_imp_vec,
536 std::vector<block_matrix_t> const &Sigma_imp_static_vec) const {
537 if (Sigma_imp_vec.size() != Sigma_imp_static_vec.size()) {
538 throw std::runtime_error(fmt::format("The lists of self-energies are not equal {} != {}", Sigma_imp_vec.size(), Sigma_imp_static_vec.size()));
539 }
540 return {embed(Sigma_imp_vec), embed(Sigma_imp_static_vec)};
541 }
542
553 block2_matrix_t embed(std::vector<block_matrix_t> const &Sigma_imp_static_vec) const;
554
565 std::vector<block_matrix_t> extract(block2_matrix_t const &matrix_C) const;
567 };
568
569 // ***************************** Free functions/factories *******************************************
570
577
587 embedding make_embedding(std::vector<std::string> const &spin_names, nda::array<std::vector<long>, 2> const &block_decomposition,
588 std::vector<long> const &atom_to_imp);
590
600 embedding make_embedding(std::vector<std::string> const &spin_names, std::vector<std::vector<long>> const &block_decomposition,
601 std::vector<long> const &atom_to_imp);
602
617 embedding make_embedding(local_space const &C_space, bool use_atom_equivalences = true, bool use_atom_decomp = false);
618
619 // -----------------------------------------------------------------------
634 inline std::pair<one_body_elements_on_grid, embedding> make_embedding_with_clusters(one_body_elements_on_grid obe,
635 std::vector<std::vector<long>> const &atom_partition) {
636 auto new_obe = permute_local_space(atom_partition, obe);
637 auto E = make_embedding(new_obe.C_space, false);
638 return {new_obe, E};
639 }
640
642
643 template std::vector<std::vector<nda::array<dcomplex, 2>>> embedding::extract(std::vector<nda::array<dcomplex, 2>> const &) const;
644 template std::vector<std::vector<nda::array<dcomplex, 3>>> embedding::extract(std::vector<nda::array<dcomplex, 3>> const &) const;
645 template std::vector<std::vector<nda::array<dcomplex, 4>>> embedding::extract(std::vector<nda::array<dcomplex, 4>> const &) const;
646 template std::vector<std::vector<nda::array<dcomplex, 5>>> embedding::extract(std::vector<nda::array<dcomplex, 5>> const &) const;
647
648 template std::vector<nda::array<dcomplex, 2>> embedding::embed(std::vector<std::vector<nda::array<dcomplex, 2>>> const &) const;
649 template std::vector<nda::array<dcomplex, 3>> embedding::embed(std::vector<std::vector<nda::array<dcomplex, 3>>> const &) const;
650 template std::vector<nda::array<dcomplex, 4>> embedding::embed(std::vector<std::vector<nda::array<dcomplex, 4>>> const &) const;
651 template std::vector<nda::array<dcomplex, 5>> embedding::embed(std::vector<std::vector<nda::array<dcomplex, 5>>> const &) const;
652
653#define INSTANTIATE(Mesh) \
654 template block2_gf<Mesh, matrix_valued> embedding::embed(std::vector<block_gf<Mesh, matrix_valued>> const &) const; \
655 template std::pair<block2_gf<Mesh, matrix_valued>, block2_matrix_t> embedding::embed(std::vector<block_gf<Mesh, matrix_valued>> const &, \
656 std::vector<block_matrix_t> const &) const; \
657 template std::vector<block_gf<Mesh, matrix_valued>> embedding::extract(block2_gf<Mesh, matrix_valued> const &) const; \
658 template std::vector<std::pair<block_gf<Mesh, matrix_valued>, block_matrix_t>> embedding::make_zero_imp_self_energies(Mesh const &);
659 INSTANTIATE(imfreq);
660 INSTANTIATE(refreq);
661 INSTANTIATE(dlr_imfreq);
662#undef INSTANTIATE
663
664} // namespace triqs::modest
nda::array< imp_block_t, 2 > psi_map() const
The mapping table .
std::vector< gf_struct_t > imp_block_structure() const
Block structure (names and dimensions) for each impurity solver.
friend void h5_write(h5::group g, std::string const &name, embedding const &x)
h5 read/write
Definition h5.cpp:104
long n_impurities() const
Number of impurities.
long n_gamma(long imp_idx) const
Number of blocks in for the [imp_idx].
long n_sigma() const
Number of blocks in for the .
bool operator==(embedding const &other) const =default
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)
Construct an embedding object.
Definition embedding.cpp:17
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
Extract Green's function containers.
long n_alpha() const
Number of blocks in for the .
embedding swap_sigma(long alpha) const
Swap the (spin) assignment for block alpha.
std::vector< long > imp_decomposition(long imp_idx) const
The impurity decomposition.
C2PY_IGNORE gf_struct2_t embed_block_structure() const
Block structure (names and dimensions) for the embedded self-energy .
std::string description(bool verbosity=false) const
Summarize the embedding object.
Definition printing.cpp:101
embedding slice_sigma() const
Slice the embedding to a single channel ("ud").
friend std::ostream & operator<<(std::ostream &out, embedding const &E)
Definition printing.cpp:167
embedding drop_imp(long imp_idx) const
Remove an impurity from the embedding table .
embedding split_imp(long imp_idx, std::function< bool(long)> p) const
Split impurity imp_idx using a predicate.
friend void h5_read(h5::group g, std::string const &name, embedding &x)
Definition h5.cpp:96
std::vector< std::pair< block_gf< Mesh, matrix_valued >, block_matrix_t > > make_zero_imp_self_energies(Mesh const &mesh)
Create zero-initialized impurity self-energies.
embedding replace_imp(long imp_idx_old, long imp_idx_new) const
Redirect all entries from one impurity to another.
embedding split_imp(long imp_idx, std::initializer_list< const char * > x)=delete
std::vector< std::vector< nda::array< dcomplex, Rank > > > extract(std::vector< nda::array< dcomplex, Rank > > const &X) const
Extract impurity data from embedded arrays.
embedding split_imp_block(long imp_idx, long gamma, std::vector< long > const &new_dims) const
Split a single block of an impurity into multiple blocks.
imp_block_t operator[](long alpha, long sigma) const
Bracket accessor: .
block2_gf< Mesh, matrix_valued > embed(std::vector< block_gf< Mesh, matrix_valued > > const &Sigma_imp_vec) const
Embed Green's function containers.
std::pair< block2_gf< Mesh, matrix_valued >, block2_matrix_t > embed(std::vector< block_gf< Mesh, matrix_valued > > const &Sigma_imp_vec, std::vector< block_matrix_t > const &Sigma_imp_static_vec) const
Embed pairs Green's function containers and matrices.
std::vector< nda::array< dcomplex, Rank > > embed(std::vector< std::vector< nda::array< dcomplex, Rank > > > const &imps_blocks) const
Embed impurity data into the full correlated space.
embedding merge_embed_block_by_imp() const
Merge consecutive blocks belonging to the same impurity into a single block.
#define INSTANTIATE(Mesh)
std::pair< one_body_elements_on_grid, embedding > make_embedding_with_clusters(one_body_elements_on_grid obe, std::vector< std::vector< long > > const &atom_partition)
Make an embedding for clusters of atoms.
gf_struct2_t get_struct(block2_gf< Mesh, matrix_valued > const &g)
Definition gf_supp.hpp:52
block2_gf< Mesh, matrix_valued > make_block2_gf(Mesh const &mesh, gf_struct2_t const &gf_s)
Definition gf_supp.hpp:41
block2_gf< Mesh > decomposition_view(block2_gf< Mesh > const &g, gf_struct2_t const &stru)
Definition gf_supp.hpp:58
embedding make_embedding(std::vector< std::string > const &spin_names, nda::array< std::vector< long >, 2 > const &block_decomposition, std::vector< long > const &atom_to_imp)
Definition embedding.cpp:40
one_body_elements_on_grid permute_local_space(std::vector< std::vector< long > > const &atom_partition, one_body_elements_on_grid const &obe)
std::vector< nda::matrix< dcomplex > > block_matrix_t
Definition embedding.hpp:17
static constexpr auto r_all
Definition defs.hpp:33
nda::array< nda::matrix< dcomplex >, 2 > block2_matrix_t
Definition embedding.hpp:18
bool all_equal(R const &r)
Determines if all elements in the given range are equal.
generator< std::pair< long, nda::range > > enumerated_sub_slices(auto sub_div)
A one-body elements struct where all of the underlying data exists on a fixed momentum grid.