diff --git a/triqs/operators/many_body_operator.hpp b/triqs/operators/many_body_operator.hpp new file mode 100644 index 00000000..f55d61ea --- /dev/null +++ b/triqs/operators/many_body_operator.hpp @@ -0,0 +1,345 @@ +/******************************************************************************* + * + * TRIQS: a Toolbox for Research in Interacting Quantum Systems + * + * Copyright (C) 2014 by I. Krivenko, O. Parcollet, M. Ferrero + * + * TRIQS is free software: you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * TRIQS is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * TRIQS. If not, see . + * + ******************************************************************************/ +#pragma once +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace triqs { +namespace utility { + + /** + * many_body_operator is a general operator in second quantification + */ + template + class many_body_operator : + // implements vector space over scalar_t operators + boost::additive>, + boost::multipliable>, + boost::additive, scalar_t>, // op+a a+op op-a + //boost::subtractable2_left, scalar_t>, // a-op + boost::multipliable, scalar_t>, // op*a a*op op/a + boost::dividable, scalar_t> { + + static constexpr scalar_t threshold = std::numeric_limits::epsilon(); + + public: + /// The indices of the C, C^+ operators are a vector of int/string + using indices_t = std::vector>; + + private: + // The canonical operator: a dagger and some indices + struct canonical_ops_t { + bool dagger; + indices_t indices; + // Order: dagger < non dagger, and then indices + // Example: c+_1 < c+_2 < c+_3 < c_3 < c_2 < c_1 + friend bool operator<(canonical_ops_t const& a, canonical_ops_t const& b) { + if (a.dagger != b.dagger) return (a.dagger > b.dagger); + if (a.dagger) // a.indices < b.indices + return std::lexicographical_compare(a.indices.begin(), a.indices.end(), b.indices.begin(), b.indices.end()); + else // b.indices < a.indices + return std::lexicographical_compare(b.indices.begin(), b.indices.end(), a.indices.begin(), a.indices.end()); + } + friend bool operator>(canonical_ops_t const& a, canonical_ops_t const& b) { return b < a; } + friend bool operator==(canonical_ops_t const& a, canonical_ops_t const& b) { + return (a.dagger == b.dagger && a.indices.size() == b.indices.size() && + std::equal(a.indices.begin(), a.indices.end(), b.indices.begin())); + } + template void serialize(Archive& ar, const unsigned int version) { ar& dagger& indices; } + }; + + // Monomial: an ordered set of creation/annihilation operators and comparison + using monomial_t = std::vector; + + friend bool operator<(monomial_t const& m1, monomial_t const& m2) { + return m1.size() != m2.size() ? m1.size() < m2.size() + : std::lexicographical_compare(m1.begin(), m1.end(), m2.begin(), m2.end()); + } + + // Map of all monomials with coefficients + using monomials_map_t = std::map; + + monomials_map_t monomials; + + public: + many_body_operator() = default; + many_body_operator(many_body_operator const&) = default; + many_body_operator(many_body_operator&&) = default; + many_body_operator& operator=(many_body_operator const&) = default; + many_body_operator& operator=(many_body_operator&&) = default; + + template many_body_operator(many_body_operator const& x) { *this = x; } + + template many_body_operator& operator=(many_body_operator const& x) { + monomials.clear(); + for (auto const& y : x.monomials) monomials.insert({y.first, y.second}); + } + + // factory for c, cdag + static many_body_operator make_canonical(bool is_dag, indices_t indices) { + many_body_operator res; + auto m = monomial_t{canonical_ops_t{is_dag, indices}}; + res.monomials.insert({m, 1.0}); + return res; + } + + // We use utility::dressed_iterator to dress iterators + // _cdress is a simple struct of refs to dress the iterators (Cf doc) + struct _cdress { + monomial_t const& monomial; + scalar_t coef; + _cdress(typename monomials_map_t::const_iterator _it) : monomial(_it->first), coef(_it->second) {} + }; + using const_iterator = utility::dressed_iterator; + + public: + // Iterators (only const!) + const_iterator begin() const noexcept { return monomials.begin(); } + const_iterator end() const noexcept { return monomials.end(); } + const_iterator cbegin() const noexcept { return monomials.cbegin(); } + const_iterator cend() const noexcept { return monomials.cend(); } + + // Is zero operator ? + bool is_zero() const { return monomials.empty(); } + + // Algebraic operations involving scalar_t constants + many_body_operator operator-() const { + auto res = *this; + for (auto& m : res.monomials) m.second = -m.second; + return res; + } + + many_body_operator& operator+=(scalar_t alpha) { + bool is_new_monomial; + typename monomials_map_t::iterator it; + std::tie(it, is_new_monomial) = monomials.insert(std::make_pair(monomial_t(0), alpha)); + if (!is_new_monomial) { + it->second += alpha; + erase_zero_monomial(monomials, it); + } + return *this; + } + + many_body_operator& operator-=(scalar_t alpha) { return operator+=(-alpha); } + + friend many_body_operator operator-(scalar_t alpha, many_body_operator const& op) { return -op + alpha; } + //friend many_body_operator operator/ (many_body_operator const & op, scalar_t alpha) { return op/alpha; } + + many_body_operator& operator*=(scalar_t alpha) { + if (std::abs(alpha) < 100*std::abs(threshold)) { + monomials.clear(); + } else { + for (auto& m : monomials) m.second *= alpha; + } + return *this; + } + + many_body_operator& operator/=(scalar_t alpha) { return operator*=(1.0/alpha); } + + // Algebraic operations + many_body_operator& operator+=(many_body_operator const& op) { + bool is_new_monomial; + typename monomials_map_t::iterator it; + for (auto const& m : op.monomials) { + std::tie(it, is_new_monomial) = monomials.insert(m); + if (!is_new_monomial) { + it->second += m.second; + erase_zero_monomial(monomials, it); + } + } + return *this; + } + + many_body_operator& operator-=(many_body_operator const& op) { + bool is_new_monomial; + typename monomials_map_t::iterator it; + for (auto const& m : op.monomials) { + std::tie(it, is_new_monomial) = monomials.insert(std::make_pair(m.first, -m.second)); + if (!is_new_monomial) { + it->second -= m.second; + erase_zero_monomial(monomials, it); + } + } + return *this; + } + + many_body_operator& operator*=(many_body_operator const& op) { + monomials_map_t tmp_map; // product will be stored here + for (auto const& m : monomials) + for (auto const& op_m : op.monomials) { + // prepare an unnormalized product + monomial_t product_m; + product_m.reserve(m.first.size() + op_m.first.size()); + for (auto const& op : m.first) product_m.push_back(op); + for (auto const& op : op_m.first) product_m.push_back(op); + // std::copy(m.first.begin(), m.first.end(), std::back_inserter(product_m)); + // std::copy(op_m.first.begin(), op_m.first.end(), std::back_inserter(product_m)); + normalize_and_insert(product_m, m.second * op_m.second, tmp_map); + } + std::swap(monomials, tmp_map); + return *this; + } + + // implementation details of dagger + // + private: + static double _dagger(double x) { return x; } + static std::complex _dagger(std::complex x) { return conj(x); } + + static canonical_ops_t _dagger(canonical_ops_t const& cop) { + return {!cop.dagger, cop.indices}; + } + + static monomial_t _dagger(monomial_t const& m) { + monomial_t res; + for (auto it = m.rbegin(); it != m.rend(); ++it) res.push_back(_dagger(*it)); + return res; + } + + public: + // dagger + friend many_body_operator dagger(many_body_operator const& op) { + many_body_operator res; + for (auto const& x : op) res.monomials.insert({_dagger(x.monomial), _dagger(x.coef)}); + return res; + } + + // Boost.Serialization + friend class boost::serialization::access; + template void serialize(Archive& ar, const unsigned int version) { ar& monomials; } + + private: + // Normalize a monomial and insert into a map + static void normalize_and_insert(monomial_t& m, scalar_t coeff, monomials_map_t& target) { + // The normalization is done by employing a simple bubble sort algorithms. + // Apart from sorting elements this function keeps track of the sign and + // recursively calls itself if a permutation of two operators produces a new + // monomial + if (m.size() >= 2) { + bool is_swapped; + do { + is_swapped = false; + for (std::size_t n = 1; n < m.size(); ++n) { + canonical_ops_t& prev_index = m[n - 1]; + canonical_ops_t& cur_index = m[n]; + if (prev_index == cur_index) return; // The monomial is effectively zero + if (prev_index > cur_index) { + // Are we swapping C and C^+ with the same indices? + // A bit ugly ... + canonical_ops_t cur_index_flipped_type(cur_index); + cur_index_flipped_type.dagger = !cur_index_flipped_type.dagger; + if (prev_index == cur_index_flipped_type) { + monomial_t new_m; + new_m.reserve(m.size() - 2); + std::copy(m.begin(), m.begin() + n - 1, std::back_inserter(new_m)); + std::copy(m.begin() + n + 1, m.end(), std::back_inserter(new_m)); + normalize_and_insert(new_m, coeff, target); + } + coeff = -coeff; + std::swap(prev_index, cur_index); + is_swapped = true; + } + } + } while (is_swapped); + } + + // Insert the result + bool is_new_monomial; + typename monomials_map_t::iterator it; + std::tie(it, is_new_monomial) = target.insert(std::make_pair(m, coeff)); + if (!is_new_monomial) { + it->second += coeff; + erase_zero_monomial(target, it); + } + } + + // Erase a monomial with a close-to-zero coefficient. + static void erase_zero_monomial(monomials_map_t& m, typename monomials_map_t::iterator& it) { + if (std::abs(it->second) < 100*std::abs(threshold)) m.erase(it); + } + + struct print_visitor : public boost::static_visitor<> { + std::ostream& os; + print_visitor(std::ostream& os_) : os(os_) {} + template void operator()(T const& x) const { os << x; } + }; + + friend std::ostream& operator<<(std::ostream& os, canonical_ops_t const& op) { + if (op.dagger) os << "^+"; + os << "("; + int u = 0; + for (auto const& i : op.indices) { + if (u++) os << ","; + boost::apply_visitor(print_visitor{os}, i); + } + return os << ")"; + } + + friend std::ostream& operator<<(std::ostream& os, monomial_t const& m) { + for (auto const& c : m) { + os << "C" << c; + } + return os; + } + + // Print many_body_operator itself + friend std::ostream& operator<<(std::ostream& os, many_body_operator const& op) { + if (op.monomials.size() != 0) { + bool print_plus = false; + for (auto const& m : op.monomials) { + os << (print_plus ? " + " : "") << m.second; + if (m.first.size()) os << "*"; + os << m.first; + print_plus = true; + } + } else + os << "0"; + return os; + } + }; + + + // ---- factories -------------- + + // Free functions to make creation/annihilation operators + template many_body_operator c(IndexTypes... indices) { + return many_body_operator::make_canonical(false, many_body_operator::indices_t{indices...}); + // need to put many_body_operator::indices_t because {} constructor is explicit !? + } + + template many_body_operator c_dag(IndexTypes... indices) { + return many_body_operator::make_canonical(true, many_body_operator::indices_t{indices...}); + } + + template many_body_operator n(IndexTypes... indices) { + return c_dag(indices...) * c(indices...); + } +} +}