Program Listing for File op.hpp

Return to documentation for file (SeQuant/domain/mbpt/op.hpp)

//
// Created by Eduard Valeyev on 2019-03-26.
//

#ifndef SEQUANT_DOMAIN_MBPT_OP_HPP
#define SEQUANT_DOMAIN_MBPT_OP_HPP

#include <SeQuant/domain/mbpt/fwd.hpp>

#include <SeQuant/domain/mbpt/spin.hpp>

#include <SeQuant/core/attr.hpp>
#include <SeQuant/core/container.hpp>
#include <SeQuant/core/context.hpp>
#include <SeQuant/core/expr.hpp>
#include <SeQuant/core/hash.hpp>
#include <SeQuant/core/index.hpp>
#include <SeQuant/core/interval.hpp>
#include <SeQuant/core/math.hpp>
#include <SeQuant/core/op.hpp>
#include <SeQuant/core/rational.hpp>
#include <SeQuant/core/space.hpp>
#include <SeQuant/core/utility/strong.hpp>

#include <range/v3/iterator/basic_iterator.hpp>
#include <range/v3/range/conversion.hpp>
#include <range/v3/range/primitives.hpp>
#include <range/v3/view/map.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/view.hpp>
#include <range/v3/view/zip.hpp>

#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <initializer_list>
#include <iterator>
#include <map>
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <vector>

namespace sequant {
namespace mbpt {

DEFINE_STRONG_TYPE_FOR_INTEGER(nₚ, std::int64_t);  // define nₚ
DEFINE_STRONG_TYPE_FOR_INTEGER(nₕ, std::int64_t);  // define nₕ

#ifndef DEFINE_SINGLE_SIGNED_ARGUMENT_OP_VARIANT
#define DEFINE_SINGLE_SIGNED_ARGUMENT_OP_VARIANT(OP)                      \
  inline ExprPtr OP(std::int64_t Rank) { return OP(nₚ(Rank), nₕ(Rank)); } \
  inline ExprPtr OP(nₚ Rank) { return OP(Rank, nₕ(Rank)); }
#endif  // DEFINE_SINGLE_SIGNED_ARGUMENT_OP_VARIANT

template <typename QuantumNumbers>
bool is_vacuum(QuantumNumbers qns);

inline IndexSpace make_space(const IndexSpace::Type& type) {
  return get_default_context().index_space_registry()->retrieve(type,
                                                                Spin::any);
}

enum class OpType {
  h,
  f,
  f̃,
  θ,
  g,
  t,
  λ,
  A,
  S,
  L,
  R,
  R12,
  GR,
  C,
  RDM,
  RDMCumulant,
  δ,
  h_1,
  t_1,
  λ_1,
  invalid
};

inline const std::map<OpType, std::wstring> optype2label{
    {OpType::h, L"h"},
    {OpType::f, L"f"},
    {OpType::f̃, L"f̃"},
    {OpType::g, L"g"},
    {OpType::θ, L"θ"},
    {OpType::t, L"t"},
    {OpType::λ, L"λ"},
    {OpType::A, L"A"},
    {OpType::S, L"S"},
    {OpType::L, L"L"},
    {OpType::R, L"R"},
    {OpType::R12, L"F"},
    {OpType::GR, L"GR"},
    {OpType::C, L"C"},
    {OpType::RDM, L"γ"},
    // see https://en.wikipedia.org/wiki/Cumulant
    {OpType::RDMCumulant, L"κ"},
    {OpType::δ, L"δ"},
    {OpType::h_1, L"h¹"},
    {OpType::t_1, L"t¹"},
    {OpType::λ_1, L"λ¹"}};

inline const std::map<std::wstring, OpType> label2optype =
    ranges::views::zip(ranges::views::values(optype2label),
                       ranges::views::keys(optype2label)) |
    ranges::to<std::map<std::wstring, OpType>>();

enum class OpClass { ex, deex, gen };

std::vector<std::wstring> cardinal_tensor_labels();

std::wstring to_wstring(OpType op);
OpClass to_class(OpType op);



template <typename QuantumNumbers, Statistics S = Statistics::FermiDirac>
class Operator;

template <typename QuantumNumbers>
using FOperator = Operator<QuantumNumbers, Statistics::FermiDirac>;
template <typename QuantumNumbers>
using BOperator = Operator<QuantumNumbers, Statistics::BoseEinstein>;

template <Statistics S>
class Operator<void, S> : public Expr, public Labeled {
 protected:
  Operator() = default;
  Operator(std::function<std::wstring_view()> label_generator,
           std::function<ExprPtr()> tensor_form_generator)
      : label_generator_(std::move(label_generator)),
        tensor_form_generator_(tensor_form_generator) {}

 public:
  virtual ~Operator() = default;

  std::wstring_view label() const override {
    assert(label_generator_);
    return label_generator_();
  }

  virtual ExprPtr tensor_form() const {
    assert(tensor_form_generator_);
    return tensor_form_generator_();
  }

  bool is_cnumber() const override { return false; }

 private:
  std::function<std::wstring_view()> label_generator_;
  std::function<ExprPtr()> tensor_form_generator_;
};

using FOperatorBase = FOperator<void>;
using BOperatorBase = BOperator<void>;

struct default_qns_tag {};

// clang-format off

// clang-format on
template <typename QNV = std::int64_t, typename Tag = default_qns_tag>
class QuantumNumberChange
    : public container::svector<
          boost::numeric::interval<std::make_signed_t<QNV>>, 8> {
 public:
  using QNC = std::make_signed_t<QNV>;  // change in quantum numbers
  using interval_t = boost::numeric::interval<QNC>;
  using base_type =
      container::svector<boost::numeric::interval<std::make_signed_t<QNV>>, 8>;
  using this_type = QuantumNumberChange<QNV, Tag>;

  const std::size_t size() const {
    if (get_default_context().vacuum() == Vacuum::Physical) {
      return 2;
    } else if (get_default_context().vacuum() == Vacuum::SingleProduct) {
      auto isr = get_default_context().index_space_registry();
      const auto& isr_base_spaces = isr->base_spaces();
      assert(isr_base_spaces.size() > 0);
      return isr_base_spaces.size() * 2;
    } else {
      throw std::logic_error("unknown Vacuum type");
    }
  }

  QuantumNumberChange() {
    this->resize(this->size());
    assert(this->base().size() != 0);
    std::fill(this->begin(), this->end(), interval_t{});
  }

  template <typename I, typename Range,
            typename = std::enable_if_t<
                meta::is_range_v<std::remove_reference_t<Range>> &&
                std::is_convertible_v<I, interval_t>>>
  explicit QuantumNumberChange(Range&& i) : QuantumNumberChange() {
    assert(i.size() == size());
    std::copy(i.begin(), i.end(), this->begin());
  }

  template <typename I, typename = std::enable_if_t<std::is_convertible_v<
                            std::initializer_list<I>, interval_t>>>
  explicit QuantumNumberChange(
      std::initializer_list<std::initializer_list<I>> i)
      : QuantumNumberChange() {
    assert(i.size() == size());
#ifndef NDEBUG
    if (std::find_if(i.begin(), i.end(),
                     [](const auto& ii) { return ii.size() != 2; }) != i.end())
      throw std::invalid_argument(
          "QuantumNumberChange<N>(initializer_list<initializer_list> i): each "
          "element of i must contain 2 elements");
#endif
    for (std::size_t c = 0; c != size(); ++c) {
      this->operator[](c) = interval_t{*((i.begin() + c)->begin()),
                                       *((i.begin() + c)->begin() + 1)};
    }
  }

  QuantumNumberChange& operator+=(const QuantumNumberChange& other) {
    for (std::size_t c = 0; c != size(); ++c) this->operator[](c) += other[c];
    return *this;
  }

  bool operator==(const this_type& b) const {
    return std::equal(
        this->begin(), this->end(), b.begin(),
        [](const auto& ia, const auto& ib) { return equal(ia, ib); });
  }
  bool operator!=(const this_type& b) const { return !this->operator==(b); }

  // determines the number of physical vacuum creators and annihilators for the
  // active particle and hole space from the Context. for general operators this
  // is not defined. for example: O_{e_1}^{i_1 m_1} a_{i_1 m_1}^{e_1} asking for
  // the active particle annihilators in this example is nonsense and will
  // return -1.

  interval_t ncre_particles() {
    const auto& qnvec = this->base();
    auto isr = get_default_context().index_space_registry();
    const auto& base_spaces = isr->base_spaces();
    interval_t result = 0;
    for (unsigned int i = 0; i < base_spaces.size(); i++) {
      const auto& base_space = base_spaces[i];
      const auto intersect_type =
          base_space.attr()
              .intersection(isr->particle_space(base_space.qns()).attr())
              .type();
      if (IndexSpace::Type{} != intersect_type) {
        result += qnvec[2 * i];
      }
    }
    return result;
  }

  interval_t nann_particles() {
    const auto& qnvec = this->base();
    auto isr = get_default_context().index_space_registry();
    const auto& base_spaces = isr->base_spaces();
    interval_t result = 0;
    for (unsigned int i = 0; i < base_spaces.size(); i++) {
      const auto& base_space = base_spaces[i];
      const auto intersect_type =
          base_space.attr()
              .intersection(isr->particle_space(base_space.qns()).attr())
              .type();
      if (IndexSpace::Type{} != intersect_type) {
        result += qnvec[2 * i + 1];
      }
    }
    return result;
  }

  interval_t ncre_holes() {
    const auto& qnvec = this->base();
    auto isr = get_default_context().index_space_registry();
    const auto& base_spaces = isr->base_spaces();
    interval_t result = 0;
    for (unsigned int i = 0; i < base_spaces.size(); i++) {
      const auto& base_space = base_spaces[i];
      const auto intersect_type =
          base_space.attr()
              .intersection(isr->hole_space(base_space.qns()).attr())
              .type();
      if (IndexSpace::Type{} != intersect_type) {
        result += qnvec[2 * i];
      }
    }
    return result;
  }

  interval_t nann_holes() {
    const auto& qnvec = this->base();
    auto isr = get_default_context().index_space_registry();
    const auto& base_spaces = isr->base_spaces();
    interval_t result = 0;
    for (unsigned int i = 0; i < base_spaces.size(); i++) {
      const auto& base_space = base_spaces[i];
      const auto intersect_type =
          base_space.attr()
              .intersection(isr->hole_space(base_space.qns()).attr())
              .type();
      if (IndexSpace::Type{} != intersect_type) {
        result += qnvec[2 * i + 1];
      }
    }
    return result;
  }

  template <typename I>
  bool in(I i) {
    return boost::numeric::in(static_cast<int64_t>(i), this->front());
  }

  template <typename I, typename = std::enable_if_t<std::is_integral_v<I>>>
  bool in(std::initializer_list<I> i) {
    assert(i.size() == size());
    std::array<I, 4> i_arr;
    std::copy(i.begin(), i.end(), i_arr.begin());
    return this->in(i_arr);
  }

  bool overlaps_with(base_type i) {
    for (std::size_t c = 0; c != this->size(); ++c) {
      if (!boost::numeric::overlap(i[c], this->operator[](c))) {
        return false;
      }
    }
    return true;
  }

  auto hash_value() const {
    assert(size() > 0);
    auto val = sequant::hash::value(this->operator[](0));
    for (std::size_t c = 1; c != size(); ++c) {
      sequant::hash::combine(val, sequant::hash::value(this->operator[](c)));
    }
    return val;
  }

 private:
  auto& base() { return static_cast<base_type&>(*this); }
};

template <std::size_t N, typename Tag, typename QNV>
inline bool operator==(const QuantumNumberChange<Tag, QNV>& a,
                       const QuantumNumberChange<Tag, QNV>& b) {
  return a.operator==(b);
}

template <std::size_t N, typename Tag, typename QNV>
inline bool operator!=(const QuantumNumberChange<Tag, QNV>& a,
                       const QuantumNumberChange<Tag, QNV>& b) {
  return !(a == b);
}

template <std::size_t N, typename Tag, typename QNV, typename I,
          typename = std::enable_if_t<N == 1 && std::is_integral_v<I>>>
inline bool operator==(const QuantumNumberChange<Tag, QNV>& a, I b) {
  return a.operator==(b);
}

template <std::size_t N, typename Tag, typename QNV>
inline bool equal(const QuantumNumberChange<Tag, QNV>& a,
                  const QuantumNumberChange<Tag, QNV>& b) {
  return operator==(a, b);
}

template <std::size_t N, typename Tag, typename QNV, typename I,
          typename = std::enable_if_t<N == 1 && std::is_integral_v<I>>>
inline bool operator!=(const QuantumNumberChange<Tag, QNV>& a, I b) {
  return a.operator!=(b);
}

// clang-format off
// clang-format on
using qns_t = mbpt::QuantumNumberChange<>;
using qninterval_t = typename qns_t::interval_t;
using qnc_t = qns_t;
using op_t = mbpt::Operator<qnc_t>;

qns_t combine(qns_t, qns_t);

qns_t excitation_type_qns(std::size_t k,
                          IndexSpace::QuantumNumbers SQN = Spin::any);

qns_t interval_excitation_type_qns(std::size_t k,
                                   IndexSpace::QuantumNumbers SQN = Spin::any);

qns_t deexcitation_type_qns(std::size_t k,
                            IndexSpace::QuantumNumbers SQN = Spin::any);

qns_t interval_deexcitation_type_qns(
    std::size_t k, IndexSpace::QuantumNumbers SQN = Spin::any);

qns_t general_type_qns(std::size_t k);

qns_t generic_excitation_qns(std::size_t particle_rank, std::size_t hole_rank,
                             IndexSpace particle_space, IndexSpace hole_space,
                             IndexSpace::QuantumNumbers SQN = Spin::any);

qns_t generic_deexcitation_qns(std::size_t particle_rank, std::size_t hole_rank,
                               IndexSpace particle_space, IndexSpace hole_space,
                               IndexSpace::QuantumNumbers SQN = Spin::any);

inline namespace op {
namespace tensor {
ExprPtr vac_av(ExprPtr expr,
               std::vector<std::pair<int, int>> nop_connections = {},
               bool use_top = true);
}  // namespace tensor
}  // namespace op

}  // namespace mbpt

mbpt::qns_t adjoint(mbpt::qns_t qns);

namespace mbpt {

// clang-format off

// clang-format on
template <Statistics S>
class OpMaker {
 public:
  template <typename IndexSpaceTypeRange1, typename IndexSpaceTypeRange2>
  OpMaker(OpType op, const cre<IndexSpaceTypeRange1>& cre_list,
          const ann<IndexSpaceTypeRange2>& ann_list)
      : op_(op),
        cre_spaces_(cre_list.begin(), cre_list.end()),
        ann_spaces_(ann_list.begin(), ann_list.end()) {
    assert(ncreators() > 0 || nannihilators() > 0);
  }

  OpMaker(OpType op, ncre nc, nann na);

  OpMaker(OpType op, std::size_t rank);

  OpMaker(OpType op, ncre nc, nann na, const cre<IndexSpace>& cre_space,
          const ann<IndexSpace>& ann_space);

  enum class UseDepIdx {
    Bra,
    Ket,
    None
  };

  // clang-format off
  // clang-format on
  ExprPtr operator()(std::optional<UseDepIdx> dep_opt = {},
                     std::optional<Symmetry> opsymm_opt = {}) const;

  template <typename TensorGenerator>
  static ExprPtr make(const container::svector<IndexSpace>& creators,
                      const container::svector<IndexSpace>& annihilators,
                      TensorGenerator&& tensor_generator,
                      UseDepIdx dep = UseDepIdx::None) {
    const bool symm =
        get_default_context().spbasis() ==
        SPBasis::spinorbital;  // antisymmetrize if spin-orbital basis
    const auto dep_bra = dep == UseDepIdx::Bra;
    const auto dep_ket = dep == UseDepIdx::Ket;

    // not sure what it means to use nonsymmetric operator if nbra != nket
    if (!symm) assert(ranges::size(creators) == ranges::size(annihilators));

    auto make_idx_vector = [](const auto& spacetypes) {
      container::svector<Index> result;
      const auto n = spacetypes.size();
      result.reserve(n);
      for (size_t i = 0; i != n; ++i) {
        auto space = spacetypes[i];
        result.push_back(Index::make_tmp_index(space));
      }
      return result;
    };

    auto make_depidx_vector = [](const auto& spacetypes, auto&& protoidxs) {
      const auto n = spacetypes.size();
      container::svector<Index> result;
      result.reserve(n);
      for (size_t i = 0; i != n; ++i) {
        auto space = spacetypes[i];
        result.push_back(Index::make_tmp_index(space, protoidxs, true));
      }
      return result;
    };

    container::svector<Index> creidxs, annidxs;
    if (dep_bra) {
      annidxs = make_idx_vector(annihilators);
      creidxs = make_depidx_vector(creators, annidxs);
    } else if (dep_ket) {
      creidxs = make_idx_vector(creators);
      annidxs = make_depidx_vector(annihilators, creidxs);
    } else {
      creidxs = make_idx_vector(creators);
      annidxs = make_idx_vector(annihilators);
    }

    using ranges::size;
    const auto mult =
        symm ? factorial(size(creators)) * factorial(size(annihilators))
             : factorial(size(creators));
    const auto opsymm = symm ? (S == Statistics::FermiDirac ? Symmetry::antisymm
                                                            : Symmetry::symm)
                             : Symmetry::nonsymm;
    return ex<Constant>(rational{1, mult}) *
           tensor_generator(creidxs, annidxs, opsymm) *
           ex<NormalOperator<S>>(cre(creidxs), ann(annidxs),
                                 get_default_context().vacuum());
  }

  template <typename TensorGenerator>
  static ExprPtr make(std::initializer_list<IndexSpace::Type> creators,
                      std::initializer_list<IndexSpace::Type> annihilators,
                      TensorGenerator&& tensor_generator,
                      UseDepIdx csv = UseDepIdx::None) {
    container::svector<IndexSpace> cre_vec(creators.begin(), creators.end());
    container::svector<IndexSpace> ann_vec(annihilators.begin(),
                                           annihilators.end());
    return OpMaker::make(cre_vec, ann_vec,
                         std::forward<TensorGenerator>(tensor_generator), csv);
  }

 protected:
  OpType op_;
  container::svector<IndexSpace> cre_spaces_;
  container::svector<IndexSpace> ann_spaces_;

  OpMaker(OpType op);

  [[nodiscard]] const auto ncreators() const { return cre_spaces_.size(); };
  [[nodiscard]] const auto nannihilators() const { return ann_spaces_.size(); };
};

extern template class OpMaker<Statistics::FermiDirac>;
extern template class OpMaker<Statistics::BoseEinstein>;

template <typename QuantumNumbers, Statistics S>
class Operator : public Operator<void, S> {
  using this_type = Operator<QuantumNumbers, S>;
  using base_type = Operator<void, S>;

 protected:
  Operator();

 public:
  Operator(std::function<std::wstring_view()> label_generator,
           std::function<ExprPtr()> tensor_form_generator,
           std::function<void(QuantumNumbers&)> qn_action);

  virtual ~Operator();

  QuantumNumbers operator()(const QuantumNumbers& qns = {}) const;

  template <typename I, typename = std::enable_if_t<std::is_integral_v<I>>>
  QuantumNumbers operator()(std::initializer_list<I> qns) const {
    QuantumNumbers result(qns);
    this->apply_to(result);
    return result;
  }

  virtual QuantumNumbers& apply_to(QuantumNumbers& qns) const;

  bool static_less_than(const Expr& that) const override;

  bool commutes_with_atom(const Expr& that) const override;

  void adjoint() override;

  bool is_adjoint_ = false;

 private:
  std::function<void(QuantumNumbers&)> qn_action_;

  bool less_than_rank_of(const this_type& that) const;

  Expr::type_id_type type_id() const override;

  ExprPtr clone() const override;

  std::wstring to_latex() const override;

  Expr::hash_type memoizing_hash() const override;

};  // class Operator

extern template class Operator<qns_t, Statistics::FermiDirac>;
extern template class Operator<qns_t, Statistics::BoseEinstein>;

inline namespace op {
namespace tensor {

using mbpt::nₕ;
using mbpt::nₚ;

// clang-format off
// clang-format on
ExprPtr H_(std::size_t k);

ExprPtr H(std::size_t k = 2);

ExprPtr F(bool use_tensor = true, IndexSpace reference_occupied = {L"", 0});

ExprPtr θ(std::size_t K);

ExprPtr T_(std::size_t K);

ExprPtr T(std::size_t K, bool skip1 = false);

ExprPtr Λ_(std::size_t K);

ExprPtr Λ(std::size_t K);

ExprPtr R_(
    nann na, ncre nc,
    const cre<IndexSpace>& cre_space = cre(get_particle_space(Spin::any)),
    const ann<IndexSpace>& ann_space = ann(get_hole_space(Spin::any)));

ExprPtr R_(nₚ np, nₕ nh);
DEFINE_SINGLE_SIGNED_ARGUMENT_OP_VARIANT(R_);

ExprPtr L_(
    nann na, ncre nc,
    const cre<IndexSpace>& cre_space = cre(get_hole_space(Spin::any)),
    const ann<IndexSpace>& ann_space = ann(get_particle_space(Spin::any)));

ExprPtr L_(nₚ np, nₕ nh);
DEFINE_SINGLE_SIGNED_ARGUMENT_OP_VARIANT(L_);

// clang-format off
// clang-format on
ExprPtr P(nₚ np, nₕ nh);
DEFINE_SINGLE_SIGNED_ARGUMENT_OP_VARIANT(P);

// clang-format off
// clang-format on
ExprPtr A(nₚ np, nₕ nh);
DEFINE_SINGLE_SIGNED_ARGUMENT_OP_VARIANT(A);

ExprPtr S(std::int64_t K);

ExprPtr H_pt(std::size_t order, std::size_t R);

ExprPtr T_pt_(std::size_t order, std::size_t K);

ExprPtr T_pt(std::size_t order, std::size_t K, bool skip1 = false);

ExprPtr Λ_pt_(std::size_t order, std::size_t K);

ExprPtr Λ_pt(std::size_t order, std::size_t K, bool skip1 = false);
}  // namespace tensor
}  // namespace op

inline namespace op {
// clang-format off
// clang-format on
ExprPtr H_(std::size_t k);

ExprPtr H(std::size_t k = 2);

ExprPtr F(bool use_tensor = true, IndexSpace reference_occupied = {L"", 0});

ExprPtr θ(std::size_t K);

ExprPtr T_(std::size_t K);

ExprPtr T(std::size_t K, bool skip1 = false);

ExprPtr Λ_(std::size_t K);

ExprPtr Λ(std::size_t K);

ExprPtr R_(
    nann na, ncre nc,
    const cre<IndexSpace>& cre_space = cre(get_particle_space(Spin::any)),
    const ann<IndexSpace>& ann_space = ann(get_hole_space(Spin::any)));

ExprPtr R_(nₚ np, nₕ nh);
DEFINE_SINGLE_SIGNED_ARGUMENT_OP_VARIANT(R_);

ExprPtr L_(
    nann na, ncre nc,
    const cre<IndexSpace>& cre_space = cre(get_hole_space(Spin::any)),
    const ann<IndexSpace>& ann_space = ann(get_particle_space(Spin::any)));

ExprPtr L_(nₚ np, nₕ nh);
DEFINE_SINGLE_SIGNED_ARGUMENT_OP_VARIANT(L_);

ExprPtr R(nann na, ncre nc,
          const cre<IndexSpace>& cre_space = cre(get_particle_space(Spin::any)),
          const ann<IndexSpace>& ann_space = ann(get_hole_space(Spin::any)));

ExprPtr R(nₚ np, nₕ nh);
DEFINE_SINGLE_SIGNED_ARGUMENT_OP_VARIANT(R);

ExprPtr L(
    nann na, ncre nc,
    const cre<IndexSpace>& cre_space = cre(get_hole_space(Spin::any)),
    const ann<IndexSpace>& ann_space = ann(get_particle_space(Spin::any)));

ExprPtr L(nₚ np, nₕ nh);
DEFINE_SINGLE_SIGNED_ARGUMENT_OP_VARIANT(L);

// clang-format off
// clang-format on
ExprPtr P(nₚ np, nₕ nh);
DEFINE_SINGLE_SIGNED_ARGUMENT_OP_VARIANT(P);

// clang-format off
// clang-format on
ExprPtr A(nₚ np, nₕ nh);
DEFINE_SINGLE_SIGNED_ARGUMENT_OP_VARIANT(A);

ExprPtr S(std::int64_t K);

ExprPtr H_pt(std::size_t order, std::size_t R);

ExprPtr T_pt_(std::size_t order, std::size_t K);

ExprPtr T_pt(std::size_t order, std::size_t K, bool skip1 = false);

ExprPtr Λ_pt_(std::size_t order, std::size_t K);

ExprPtr Λ_pt(std::size_t order, std::size_t K, bool skip1 = false);

bool raises_vacuum_up_to_rank(const ExprPtr& op_or_op_product,
                              const unsigned long k);

bool lowers_rank_or_lower_to_vacuum(const ExprPtr& op_or_op_product,
                                    const unsigned long k);

bool raises_vacuum_to_rank(const ExprPtr& op_or_op_product,
                           const unsigned long k);

bool lowers_rank_to_vacuum(const ExprPtr& op_or_op_product,
                           const unsigned long k);
#include <SeQuant/domain/mbpt/vac_av.hpp>
}  // namespace op
}  // namespace mbpt
}  // namespace sequant

#endif  // SEQUANT_DOMAIN_MBPT_OP_HPP