TRIQS/TRIQS 4.0.0
Researching Interacting Quantum Systems
Loading...
Searching...
No Matches
many_body_operator.hpp
Go to the documentation of this file.
1// Copyright (c) 2014-2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA)
2// Copyright (c) 2014-2018 Centre national de la recherche scientifique (CNRS)
3// Copyright (c) 2018-2023 Simons Foundation
4// Copyright (c) 2014-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: Michel Ferrero, Alexander Hampel, Igor Krivenko, Olivier Parcollet, Hugo U. R. Strand, Nils Wentzell
20
25
26#pragma once
27
33
34#include <h5/h5.hpp>
35
36#include <algorithm>
37#include <cmath>
38#include <compare>
39#include <complex>
40#include <cstddef>
41#include <functional>
42#include <map>
43#include <ostream>
44#include <string>
45#include <type_traits>
46#include <utility>
47#include <variant>
48#include <vector>
49
50namespace triqs::operators {
51
56
57 // Elevate triqs::utility::real_or_complex to the `triqs::operators` namespace.
58 using utility::real_or_complex;
59
62
63 // Forward declarations.
64 template <typename T> class many_body_operator_generic;
67
70
73
76
92 bool dagger{false};
93
96
117 auto operator<=>(canonical_ops_t const &b) const {
118 if (dagger != b.dagger) return !dagger <=> !b.dagger; // c+ < c
119
120 // Compare indices element-wise, ordering by type index first (long < string < double < array)
121 auto compare_indices = [](indices_t const &a, indices_t const &b) -> std::strong_ordering {
122 for (size_t i = 0; i < std::min(a.size(), b.size()); ++i) {
123 // Compare by type index first
124 if (a[i].index() != b[i].index()) return a[i].index() <=> b[i].index();
125 // Same type: compare values
126 auto cmp = std::visit(
127 [](auto const &x, auto const &y) -> std::strong_ordering {
128 if constexpr (std::is_same_v<std::decay_t<decltype(x)>, std::decay_t<decltype(y)>>) {
129 if (x == y) return std::strong_ordering::equal;
130 return x < y ? std::strong_ordering::less : std::strong_ordering::greater;
131 }
132 return std::strong_ordering::equal; // unreachable due to index check
133 },
134 a[i], b[i]);
135 if (cmp != std::strong_ordering::equal) return cmp;
136 }
137 return a.size() <=> b.size();
138 };
139
140 return dagger ? compare_indices(indices, b.indices) : compare_indices(b.indices, indices);
141 }
142
149 bool operator==(canonical_ops_t const &b) const { return (*this <=> b) == 0; }
150
155 void serialize(auto &ar) const { ar & dagger & indices; }
156
161 void deserialize(auto &ar) { ar & dagger & indices; }
162 };
163
171 std::ostream &operator<<(std::ostream &os, canonical_ops_t const &op);
172
186 using monomial_t = std::vector<canonical_ops_t>;
187
201 bool operator<(monomial_t const &m1, monomial_t const &m2);
202
210 std::ostream &operator<<(std::ostream &os, monomial_t const &m);
211
230 template <typename T> class many_body_operator_generic {
231 public:
233 using monomials_map_t = std::map<monomial_t, T>;
234
236 using scalar_t = T;
237
240
251 static_assert(std::is_constructible_v<scalar_t, S>);
252 *this = p;
253 }
254
262 if (!is_zero(x)) monomials_.insert({{}, x});
263 }
264
273 if (!is_zero(x)) monomials_.emplace(std::move(monomial), x);
274 }
275
276 struct _cdress;
277 C2PY_IGNORE many_body_operator_generic(_cdress const &term) { normalize_and_insert(term.monomial, term.coef, monomials_); }
278
290 static_assert(std::is_constructible_v<scalar_t, S>);
291 monomials_.clear();
292 for (auto const &y : p.get_monomials()) monomials_.insert(std::make_pair(monomial_t{y.first}, scalar_t(y.second)));
293 return *this;
294 }
295
297 [[nodiscard]] monomials_map_t const &get_monomials() const { return monomials_; }
298
307 for (auto const &m : monomials_)
308 for (auto const &c_cdag_op : m.first) fops.insert_from_indices_t(c_cdag_op.indices);
309 return fops;
310 }
311
322 auto m = monomial_t{canonical_ops_t{.dagger = is_dag, .indices = indices}};
323 res.monomials_.insert({m, scalar_t(1)});
324 return res;
325 }
326
327 // We use utility::dressed_iterator to dress iterators.
328 // _cdress is a simple struct of refs that exposes (monomial, coef) to the iterator.
329 struct C2PY_IGNORE _cdress {
330 monomial_t const &monomial; // NOLINT
331 scalar_t coef;
332 _cdress(typename monomials_map_t::const_iterator _it) : monomial(_it->first), coef(_it->second) {}
333 operator std::pair<std::vector<std::pair<bool, indices_t>>, scalar_t>() const {
334 std::vector<std::pair<bool, indices_t>> tmp_monomial;
335 tmp_monomial.reserve(monomial.size());
336 for (auto cop : monomial) tmp_monomial.emplace_back(cop.dagger, cop.indices);
337 return {tmp_monomial, coef};
338 }
339 };
340
343
345 [[nodiscard]] const_iterator begin() const noexcept { return monomials_.begin(); }
346
348 [[nodiscard]] const_iterator end() const noexcept { return monomials_.end(); }
349
351 [[nodiscard]] const_iterator cbegin() const noexcept { return monomials_.cbegin(); }
352
354 [[nodiscard]] const_iterator cend() const noexcept { return monomials_.cend(); }
355
362 [[nodiscard]] bool is_almost_zero(double precision = 1e-10) const {
363 auto term_is_zero = [precision](auto const &term) {
364 using std::abs;
365 return triqs::utility::is_zero(abs(term.coef), precision);
366 };
367 return std::all_of(this->begin(), this->end(), term_is_zero);
368 }
369
374 [[nodiscard]] bool is_zero() const { return monomials_.empty(); }
375
381 auto res = *this;
382 for (auto &m : res.monomials_) m.second = -m.second;
383 return res;
384 }
385
398 if (is_zero(a)) return *this;
399 bool is_new_monomial{};
400 typename monomials_map_t::iterator it;
401 std::tie(it, is_new_monomial) = monomials_.insert(std::make_pair(monomial_t(0), a));
402 if (!is_new_monomial) {
403 it->second += a;
404 erase_zero_monomial(monomials_, it);
405 }
406 return *this;
407 }
408
419
432 if (is_zero(a)) {
433 monomials_.clear();
434 } else {
435 for (auto &m : monomials_) m.second *= a;
436 }
437 return *this;
438 }
439
450
459
468
477
488
497
506
515
528 bool is_new_monomial{};
529 typename monomials_map_t::iterator it;
530 for (auto const &m : op.monomials_) {
531 std::tie(it, is_new_monomial) = monomials_.insert(m);
532 if (!is_new_monomial) {
533 it->second += m.second;
534 erase_zero_monomial(monomials_, it);
535 }
536 }
537 return *this;
538 }
539
552 bool is_new_monomial{};
553 typename monomials_map_t::iterator it;
554 for (auto const &m : op.monomials_) {
555 std::tie(it, is_new_monomial) = monomials_.insert(std::make_pair(m.first, -m.second));
556 if (!is_new_monomial) {
557 it->second -= m.second;
558 erase_zero_monomial(monomials_, it);
559 }
560 }
561 return *this;
562 }
563
581 monomials_map_t tmp_map; // product will be stored here
582 for (auto const &m : monomials_)
583 for (auto const &op_m : op.monomials_) {
584 monomial_t product_m;
585 product_m.reserve(m.first.size() + op_m.first.size());
586 for (auto const &tmp_op : m.first) product_m.push_back(tmp_op);
587 for (auto const &tmp_op : op_m.first) product_m.push_back(tmp_op);
588 normalize_and_insert(product_m, m.second * op_m.second, tmp_map);
589 }
590 std::swap(monomials_, tmp_map);
591 return *this;
592 }
593
602
611
620
629 bool operator==(many_body_operator_generic const &op) const { return (*this - op).is_zero(); }
630
631 private:
632 // Implementation details of dagger.
633 static canonical_ops_t _dagger(canonical_ops_t const &cop) { return {!cop.dagger, cop.indices}; }
634
635 static monomial_t _dagger(monomial_t const &m) {
636 monomial_t res;
637 for (auto it = m.rbegin(); it != m.rend(); ++it) res.push_back(_dagger(*it));
638 return res;
639 }
640
641 public:
654 for (auto const &x : op) res.monomials_.insert({_dagger(x.monomial), conj(x.coef)});
655 return res;
656 }
657
673 template <typename F> friend many_body_operator_generic transform(many_body_operator_generic const &op, F &&f) { // NOLINT
676 for (auto const &x : op) {
677 auto c = f(x.monomial, x.coef);
678 if (!is_zero(c)) res.monomials_.insert({x.monomial, c});
679 }
680 return res;
681 }
682
684 [[nodiscard]] C2PY_PROPERTY_GET(real) many_body_operator_generic real() const { return operators::real(*this); }
685
687 [[nodiscard]] C2PY_PROPERTY_GET(imag) many_body_operator_generic imag() const { return operators::imag(*this); }
688
696 friend std::ostream &operator<<(std::ostream &os, many_body_operator_generic const &op) {
697 if (op.monomials_.size() != 0) {
698 bool print_plus = false;
699 for (auto const &m : op.monomials_) {
700 os << (print_plus ? " + " : "") << m.second;
701 os << m.first;
702 print_plus = true;
703 }
704 } else
705 os << "0";
706 return os;
707 }
708
713 void serialize(auto &ar) const { ar & monomials_; }
714
719 void deserialize(auto &ar) { ar & monomials_; }
720
722 [[nodiscard]] static std::string hdf5_format() { return "Operator"; }
723
733 friend void h5_write(h5::group g, std::string const &name, many_body_operator const &op, hilbert_space::fundamental_operator_set const &fops);
734
745 friend void h5_write(h5::group g, std::string const &name, many_body_operator_generic const &op) {
746 h5_write(g, name, op, op.make_fundamental_operator_set());
747 }
748
758 friend void h5_read(h5::group g, std::string const &name, many_body_operator &op, hilbert_space::fundamental_operator_set &fops);
759
767 friend void h5_read(h5::group g, std::string const &name, many_body_operator_generic &op) {
769 many_body_operator op_real_cplx;
770 h5_read(g, name, op_real_cplx, fops);
771 op = std::move(op_real_cplx);
772 }
773
774 private:
775 // Normalize a monomial and insert into a map
776 static void normalize_and_insert(monomial_t m, scalar_t coeff, monomials_map_t &target) {
777 // The normalization is done by employing a simple bubble sort algorithms.
778 // Apart from sorting elements this function keeps track of the sign and
779 // recursively calls itself if a permutation of two operators produces a new
780 // monomial
781 if (m.size() >= 2) {
782 bool is_swapped{};
783 do { // NOLINT
784 is_swapped = false;
785 for (int n = 1; n < m.size(); ++n) {
786 canonical_ops_t &prev_index = m[n - 1];
787 canonical_ops_t &cur_index = m[n];
788 if (prev_index == cur_index) return; // The monomial is effectively zero
789 if (prev_index > cur_index) {
790 // Are we swapping C and C^+ with the same indices?
791 // A bit ugly ...
792 canonical_ops_t cur_index_flipped_type(cur_index);
793 cur_index_flipped_type.dagger = !cur_index_flipped_type.dagger;
794 if (prev_index == cur_index_flipped_type) {
795 monomial_t new_m;
796 new_m.reserve(m.size() - 2);
797 std::copy(m.begin(), m.begin() + n - 1, std::back_inserter(new_m));
798 std::copy(m.begin() + n + 1, m.end(), std::back_inserter(new_m));
799 normalize_and_insert(new_m, coeff, target);
800 }
801 coeff = -coeff;
802 std::swap(prev_index, cur_index);
803 is_swapped = true;
804 }
805 }
806 } while (is_swapped);
807 }
808
809 // Insert the result
810 bool is_new_monomial{};
811 typename monomials_map_t::iterator it;
812 std::tie(it, is_new_monomial) = target.insert(std::make_pair(std::move(m), coeff));
813 if (!is_new_monomial) {
814 it->second += coeff;
815 erase_zero_monomial(target, it);
816 }
817 }
818
819 // Erase a monomial with a close-to-zero coefficient.
820 static void erase_zero_monomial(monomials_map_t &m, typename monomials_map_t::iterator &it) {
822 if (is_zero(it->second)) m.erase(it);
823 }
824
825 private:
826 monomials_map_t monomials_;
827 };
828
841 template <typename T1, typename T2>
843 if (!(op1 - op2).is_almost_zero(precision)) TRIQS_RUNTIME_ERROR << "Error in operators::assert_operators_are_close: Terms are different";
844 }
845
857 return transform(op, [](monomial_t const &, T a_i) {
859 return real(a_i);
860 });
861 }
862
874 return transform(op, [](monomial_t const &, T a_i) {
876 return imag(a_i);
877 });
878 }
879
891 template <typename T> bool is_op_hermitian(many_body_operator_generic<T> const &op, double tolerance = 0.0) {
892 return (dagger(op) - op).is_almost_zero(tolerance);
893 }
894
903 template <typename T = real_or_complex, typename... IndexTypes> many_body_operator_generic<T> c(IndexTypes... indices) {
905 }
906
915 template <typename T = real_or_complex, typename... IndexTypes> many_body_operator_generic<T> c_dag(IndexTypes... indices) {
917 }
918
927 template <typename T = real_or_complex, typename... IndexTypes> many_body_operator_generic<T> n(IndexTypes... indices) {
928 return c_dag<T>(indices...) * c<T>(indices...);
929 }
930
932
933} // namespace triqs::operators
934
935// Specialization of `std::hash` for triqs::operators::canonical_ops_t combining the `dagger` flag with each element of `indices`.
936template <> struct std::hash<triqs::operators::canonical_ops_t> {
937 std::size_t operator()(triqs::operators::canonical_ops_t const &c) const noexcept {
938 std::size_t h = std::hash<bool>{}(c.dagger);
939 for (auto const &idx : c.indices) {
940 std::visit(triqs::utility::overloaded{[&](std::array<long, 3> const &a) {
941 for (long x : a) h += std::hash<long>{}(x);
942 },
943 [&](auto const &v) { h += std::hash<std::decay_t<decltype(v)>>{}(v); }},
944 idx);
945 }
946 return h;
947 }
948};
const_view_type operator()() const
Make a const view of *this.
Class representing a fundamental operator set.
triqs::hilbert_space::indices_t indices_t
Index type to represent a single .
many_body_operator_generic operator-() const
Unary minus operator to negate the current many-body operator .
many_body_operator_generic(scalar_t const &x)
Construct a many-body operator .
friend many_body_operator_generic operator*(many_body_operator_generic lhs, many_body_operator_generic const &rhs)
Product of two many-body operators and (Fock-space operator product).
friend void h5_read(h5::group g, std::string const &name, many_body_operator &op, hilbert_space::fundamental_operator_set &fops)
Read a triqs::operators::many_body_operator together with a triqs::hilbert_space::fundamental_operato...
void deserialize(auto &ar)
Deserialize the many-body operator from a generic archive.
hilbert_space::fundamental_operator_set make_fundamental_operator_set() const
Create a minimal fundamental operator set with all single particle state indices that appear in the ...
friend many_body_operator_generic operator-(many_body_operator_generic op, scalar_t a)
Subtract a scalar from a many-body operator .
many_body_operator_generic(scalar_t const &x, monomial_t monomial)
Construct a many-body operator .
friend many_body_operator_generic operator-(scalar_t a, many_body_operator_generic const &op)
Subtract a many-body operator from a scalar .
many_body_operator_generic & operator-=(many_body_operator_generic const &op)
Subtraction assignment operator to subtract the many-body operator from the current many-body operat...
many_body_operator_generic(many_body_operator_generic< S > const &p)
Construct a many-body operator from another many-body operator with a different coefficient type.
const_iterator end() const noexcept
Get a const iterator past the end of the map that contains the monomials and their coefficients.
friend std::ostream & operator<<(std::ostream &os, many_body_operator_generic const &op)
Write a triqs::operators::many_body_operator_generic to a std::ostream.
bool operator==(many_body_operator_generic const &op) const
Equality operator to compare two many-body operators.
many_body_operator_generic & operator/=(scalar_t a)
Division assignment operator to divide the current many-body operator by a scalar .
friend void h5_write(h5::group g, std::string const &name, many_body_operator_generic const &op)
Write a triqs::operators::many_body_operator_generic to HDF5.
many_body_operator_generic()=default
Default constructor creates a zero many-body operator, i.e. with no terms.
friend many_body_operator_generic dagger(many_body_operator_generic const &op)
Compute the Hermitian conjugate (dagger) of the many-body operator .
many_body_operator_generic & operator+=(many_body_operator_generic const &op)
Addition assignment operator to add the many-body operator to the current many-body operator .
friend many_body_operator_generic operator+(many_body_operator_generic lhs, many_body_operator_generic const &rhs)
Sum of two many-body operators and .
bool is_almost_zero(double precision=1e-10) const
Check if the current operator is close to zero.
friend many_body_operator_generic operator+(scalar_t a, many_body_operator_generic op)
Add a many-body operator to a scalar .
many_body_operator_generic & operator+=(scalar_t a)
Addition assignment operator to add a scalar to the current many-body operator .
friend many_body_operator_generic transform(many_body_operator_generic const &op, F &&f)
Transform the coefficients of an operator using a callable object.
many_body_operator_generic & operator=(many_body_operator_generic< S > const &p)
Assignment operator from a many-body operator with a different coefficient type.
const_iterator cend() const noexcept
Get a const iterator past the end of the map that contains the monomials and their coefficients.
const_iterator cbegin() const noexcept
Get a const iterator to the beginning of the map that contains the monomials and their coefficients.
static many_body_operator_generic make_canonical(bool is_dag, indices_t indices)
Create a many-body operator that represents a single canonical operator or .
friend many_body_operator_generic operator/(many_body_operator_generic op, scalar_t a)
Divide a many-body operator by a scalar .
void serialize(auto &ar) const
Serialize the many-body operator to a generic archive.
friend many_body_operator_generic operator*(scalar_t a, many_body_operator_generic op)
Multiply a many-body operator by a scalar on the left.
friend void h5_read(h5::group g, std::string const &name, many_body_operator_generic &op)
Read a triqs::operators::many_body_operator_generic from HDF5.
many_body_operator_generic & operator*=(scalar_t a)
Multiplication assignment operator to multiply the current many-body operator by a scalar .
many_body_operator_generic & operator-=(scalar_t a)
Subtraction assignment operator to subtract a scalar from the current many-body operator .
utility::dressed_iterator< typename monomials_map_t::const_iterator, _cdress > const_iterator
const_iterator begin() const noexcept
Get a const iterator to the beginning of the map that contains the monomials and their coefficients.
static std::string hdf5_format()
HDF5 format tag of the many-body operator.
bool is_zero() const
Check if the current operator is exactly zero.
friend void h5_write(h5::group g, std::string const &name, many_body_operator const &op, hilbert_space::fundamental_operator_set const &fops)
Write a triqs::operators::many_body_operator together with a triqs::hilbert_space::fundamental_operat...
friend many_body_operator_generic operator+(many_body_operator_generic op, scalar_t a)
Add a scalar to a many-body operator .
friend many_body_operator_generic operator-(many_body_operator_generic lhs, many_body_operator_generic const &rhs)
Difference of two many-body operators and .
friend many_body_operator_generic operator*(many_body_operator_generic op, scalar_t a)
Multiply a many-body operator by a scalar on the right.
monomials_map_t const & get_monomials() const
Get the map/dictionary of monomials and their coefficients.
many_body_operator_generic & operator*=(many_body_operator_generic const &op)
Multiplication assignment operator to multiply the current many-body operator by another many-body o...
Type that can represent either a real or a complex number.
STL-compatible iterator wrapper that dresses an underlying iterator with a user-defined view type.
many_body_operator_generic< T > real(many_body_operator_generic< T > const &op)
Get a copy of the given operator with the imaginary parts of all monomial coefficients set to zero.
many_body_operator_generic< T > n(IndexTypes... indices)
Create a number operator .
many_body_operator_generic< double > many_body_operator_real
Many-body operator with real coefficients (see triqs::operators::many_body_operator_generic).
void assert_operators_are_close(many_body_operator_generic< T1 > const &op1, many_body_operator_generic< T2 > const &op2, double precision)
Assert that two many-body operators are close to each other within a given precision.
bool is_op_hermitian(many_body_operator_generic< T > const &op, double tolerance=0.0)
Check if a many-body operator is Hermitian within a given precision.
many_body_operator_generic< real_or_complex > many_body_operator
Many-body operator with real or complex coefficients (see triqs::operators::many_body_operator_generi...
std::vector< canonical_ops_t > monomial_t
Type used to represent a monomial of canonical second quantization operators.
many_body_operator_generic< std::complex< double > > many_body_operator_complex
Many-body operator with complex coefficients (see triqs::operators::many_body_operator_generic).
many_body_operator_generic< T > imag(many_body_operator_generic< T > const &op)
Get a copy of the given operator with the real parts of all monomial coefficients set to zero.
bool operator<(monomial_t const &m1, monomial_t const &m2)
Less-than comparison operator for triqs::operators::monomial_t.
many_body_operator_generic< T > c_dag(IndexTypes... indices)
Create a creation operator .
many_body_operator_generic< T > c(IndexTypes... indices)
Create an annihilation operator .
std::ostream & operator<<(std::ostream &sout, canonical_ops_t const &op)
Write a triqs::operators::canonical_ops_t to a std::ostream.
hilbert_space::fundamental_operator_set::indices_t indices_t
Elevate triqs::hilbert_space::indices_t to the triqs::operators namespace.
#define TRIQS_RUNTIME_ERROR
Throw a triqs::runtime_error with the current source location.
I real(I const &x)
Real part of an integral value.
I imag(I const &x)
Imaginary part of an integral value.
I conj(I const &x)
Complex conjugate of an integral value.
bool is_zero(I const &x)
Exact zero check for integral values.
Provides a fundamental operator set class.
Numeric helpers overloaded for various types.
Second quantization creation/annihilation operator.
indices_t indices
Single particle state index .
void deserialize(auto &ar)
Deserialize the canonical operator from a generic archive.
auto operator<=>(canonical_ops_t const &b) const
Three-way comparison operator for canonical operators.
void serialize(auto &ar) const
Serialize the canonical operator to a generic archive.
bool dagger
True for creation ( ), false for annihilation ( ) operators.
bool operator==(canonical_ops_t const &b) const
Equality operator for canonical operators.
STL-compatible iterator that wraps an underlying iterator and dereferences to a user-supplied dressin...
Provides a type that decides at runtime whether it is real or complex.
Small helpers for working with std::variant types.