TRIQS/TRIQS 4.0.0
Researching Interacting Quantum Systems
Loading...
Searching...
No Matches
imperative_operator.hpp
Go to the documentation of this file.
1// Copyright (c) 2015-2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA)
2// Copyright (c) 2015-2018 Centre national de la recherche scientifique (CNRS)
3// Copyright (c) 2018-2023 Simons Foundation
4// Copyright (c) 2016 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, Nils Wentzell
20
25
26#pragma once
27
29#include "./hilbert_space.hpp"
31
32#include <algorithm>
33#include <cstdint>
34#include <type_traits>
35#include <utility>
36#include <vector>
37
38namespace triqs::hilbert_space {
39
44
86 template <typename HS, typename T = double, bool UseMap = false> class imperative_operator {
87 private:
88 // Precomputed bitmask representation of a single monomial.
89 struct one_term_t {
90 T coeff;
91 uint64_t d_mask, dag_mask, d_count_mask, dag_count_mask;
92 };
93
94 public:
96 using coeff_t = T;
97
99 using hilbert_map_t = std::vector<int>;
100
103
130 hilbert_map_t hmap = hilbert_map_t(), std::vector<sub_hilbert_space> const *subspaces = nullptr)
131 : subspaces_(subspaces), hmap_(std::move(hmap)) {
132
133 if ((hmap_.size() == 0) != !UseMap) TRIQS_RUNTIME_ERROR << "Internal error";
134
135 // ordering matching the canonical form
136 auto greater = [&fops](triqs::operators::canonical_ops_t const &op1, triqs::operators::canonical_ops_t const &op2) {
137 if (op1.dagger != op2.dagger) return op2.dagger;
138 return op1.dagger ? (fops[op1.indices] > fops[op2.indices]) : (fops[op1.indices] < fops[op2.indices]);
139 };
140
141 // The goal here is to have a transcription of the many_body_operator in terms
142 // of simple vectors (maybe the code below could be more elegant)
143 for (auto const &term : op) {
144 // canonical-order working copy; each swap flips the fermionic sign of the coefficient
145 auto monomial = term.monomial;
146 auto coef = term.coef;
147
148 // Sort monomial according to the order established by fops
149 int n = monomial.size();
150 bool swapped = true;
151 while (swapped) {
152 swapped = false;
153 for (int i = 1; i < n; ++i) {
154 if (greater(monomial[i - 1], monomial[i])) {
155 using std::swap;
156 swap(monomial[i - 1], monomial[i]);
157 swapped = true;
158 coef *= coeff_t(-1);
159 }
160 }
161 --n;
162 }
163
164 // Given the environment variable CHECK_ISSUE819 was set by the user
165 // throw an exception if the result of this model was effected by issue 819
166 // https://github.com/TRIQS/triqs/issues/819
167 static const bool check_issue819 = std::getenv("CHECK_ISSUE819");
168 if (check_issue819 && term.coef != coef)
169 TRIQS_RUNTIME_ERROR << "ERROR: The Atom-Diag result of this model is affected by issue 819 (https://github.com/TRIQS/triqs/issues/819).\n"
170 "If you have solved the same model with release 2.2.0, 2.2.1 or 3.0.0 of TRIQS the result was incorrect.";
171
172 // build bitmask representation of the canonical monomial
173 std::vector<int> dag, ndag;
174 uint64_t d_mask = 0, dag_mask = 0;
175 for (auto const &canonical_op : monomial) {
176 (canonical_op.dagger ? dag : ndag).push_back(fops[canonical_op.indices]);
177 (canonical_op.dagger ? dag_mask : d_mask) |= (uint64_t(1) << fops[canonical_op.indices]);
178 }
179 // parity mask: bit i set iff i is not in d AND the number of d-bits > i is odd
180 // used to read off the fermionic sign via parity_number_of_bits in operator()
181 auto compute_count_mask = [](std::vector<int> const &d) {
182 uint64_t mask = 0;
183 bool is_on = (d.size() % 2 == 1);
184 for (int i = 0; i < 64; ++i) {
185 if (std::ranges::find(d, i) != d.end())
186 is_on = !is_on;
187 else if (is_on)
188 mask |= (uint64_t(1) << i);
189 }
190 return mask;
191 };
192 uint64_t d_count_mask = compute_count_mask(ndag), dag_count_mask = compute_count_mask(dag);
193 terms_.push_back(one_term_t{coeff_t(coef), d_mask, dag_mask, d_count_mask, dag_count_mask});
194 }
195 }
196
206 template <typename F> void update(F f) {
207 for (auto &t_i : terms_) f(t_i.coeff);
208 }
209
214 [[nodiscard]] bool is_empty() const { return (terms_.size() == 0); }
215
263 template <typename S, typename... Args> S operator()(S const &psi, Args &&...args) const {
264
265 S target_st = get_target_st(psi);
266 auto const &hs = psi.get_hilbert();
267
268 using amplitude_t = typename S::value_type;
269
270 for (int i = 0; i < terms_.size(); ++i) { // loop over monomials
271 auto M = terms_[i];
272 foreach (psi, [M, &target_st, hs, ... args = std::forward<Args>(args)](int j, typename S::value_type amplitude) {
273 fock_state_t f2 = hs.get_fock_state(j);
274 if ((f2 & M.d_mask) != M.d_mask) return;
275 f2 &= ~M.d_mask;
276 if (((f2 ^ M.dag_mask) & M.dag_mask) != M.dag_mask) return;
277 fock_state_t f3 = ~(~f2 & ~M.dag_mask);
278 auto sign_is_minus = parity_number_of_bits((f2 & M.d_count_mask) ^ (f3 & M.dag_count_mask));
279 // update state vector in target Hilbert space
280 auto ind = target_st.get_hilbert().get_state_index(f3);
281 target_st(ind) += amplitude * apply_if_possible(M.coeff, args...) * (sign_is_minus ? -amplitude_t(1) : amplitude_t(1));
282 }); // foreach
283 }
284 return target_st;
285 }
286
287 private:
288 // Return a zero state in the target Hilbert space of |psi> under this operator, or a default-constructed (empty)
289 // state when UseMap=true and the connection map sends |psi>'s subspace to -1.
290 template <typename S> S get_target_st(S const &psi) const {
291 if constexpr (UseMap) {
292 auto n = hmap_[psi.get_hilbert().get_index()];
293 if (n == -1) return S{};
294 return S{(*subspaces_)[n]};
295 } else {
296 return S(psi.get_hilbert());
297 }
298 }
299
300 // Return true if the number of set bits in v is odd, false if it is even.
301 static bool parity_number_of_bits(uint64_t v) {
302 // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetNaive
303 // v ^= v >> 16;
304 // only ok until 16 orbitals ! assert this or put the >> 16
305 v ^= v >> 8;
306 v ^= v >> 4;
307 v ^= v >> 2;
308 v ^= v >> 1;
309 return v & 0x01;
310 }
311
312 // Return x(args...) when args... is non-empty (coeff_t must be callable with them), x otherwise.
313 // Supports parameterized operators whose coefficients are themselves callable objects.
314 template <typename... Args> static auto apply_if_possible(coeff_t const &x, Args &&...args) -> std::invoke_result_t<coeff_t, Args...> {
315 return x(std::forward<Args>(args)...);
316 }
317 static auto apply_if_possible(coeff_t const &x) -> coeff_t { return x; }
318
319 private:
320 std::vector<one_term_t> terms_;
321 std::vector<sub_hilbert_space> const *subspaces_ = nullptr;
322 hilbert_map_t hmap_;
323 };
324
326
327} // namespace triqs::hilbert_space
Class representing a fundamental operator set.
void update(F f)
Apply a callable object to each coefficient of the operator.
T coeff_t
Coefficient type (might be a callable type).
std::vector< int > hilbert_map_t
Map between subspace indices (only used when UseMap = true).
imperative_operator(triqs::operators::many_body_operator_generic< coeff_t > const &op, fundamental_operator_set const &fops, hilbert_map_t hmap=hilbert_map_t(), std::vector< sub_hilbert_space > const *subspaces=nullptr)
Construct an imperative_operator from a triqs::operators::many_body_operator_generic and a triqs::hil...
bool is_empty() const
Check whether the imperative operator has no terms.
S operator()(S const &psi, Args &&...1) const
Apply the operator to a many-body state and return the resulting state.
imperative_operator()=default
Default constructor creates a zero imperative operator with no terms.
uint64_t fock_state_t
Integer type representing a fermionic fock state .
#define TRIQS_RUNTIME_ERROR
Throw a triqs::runtime_error with the current source location.
Provides a fundamental operator set class.
Provides a class to represent Hilbert (Fock) spaces.
Provides generic many-body operators.
Second quantization creation/annihilation operator.