Program Listing for File vac_av.ipp

Return to documentation for file (SeQuant/domain/mbpt/vac_av.ipp)

//
// Created by Eduard Valeyev on 8/2/23.
//

//  operator-level vac_av is same for SR and MR, to be included from {sr,mr}.cpp

#ifndef SEQUANT_DOMAIN_MBPT_VAC_AV_IPP
#define SEQUANT_DOMAIN_MBPT_VAC_AV_IPP

ExprPtr vac_av(
    ExprPtr expr,
    std::vector<std::pair<std::wstring, std::wstring>> op_connections,
    bool skip_clone) {
  // use cloned expr to avoid side effects
  if (!skip_clone) expr = expr->clone();

  auto vac_av_product = [&op_connections](ExprPtr expr) {
    assert(expr.is<Product>());
    // extract scalar and factors
    const auto scalar = expr.as<Product>().scalar();
    auto factors = expr.as<Product>().factors();
    // remove Variable types from the Product temporarily
    auto variables = factors | ranges::views::filter([](const auto& factor) {
                       return factor.template is<Variable>();
                     }) |
                     ranges::to_vector;

    auto factors_filtered = factors |
                            ranges::views::filter([](const auto& factor) {
                              return !(factor.template is<Variable>());
                            }) |
                            ranges::to<decltype(factors)>;
    // construct Product with filtered factors
    auto product =
        ex<Product>(scalar, factors_filtered.begin(), factors_filtered.end());

    // compute connections
    std::vector<std::pair<int, int>> connections;
    {
      std::map<std::wstring, std::vector<int>>
          oplbl2pos;  // maps operator labels to the operator positions in the
                      // product
      int pos = 0;
      bool ops_only = true;
      for (const auto& factor : product.as<Product>()) {
        if (factor.is<op_t>()) {
          const auto& op = factor.as<op_t>();
          const std::wstring op_lbl = std::wstring(op.label());
          const auto it = oplbl2pos.find(op_lbl);
          if (it == oplbl2pos.end()) {  // new label
            oplbl2pos.emplace(op_lbl, std::vector<int>{pos});
          } else {
            it->second.emplace_back(pos);
          }
          ++pos;
        } else if (factor.is<FNOperator>() || factor.is<BNOperator>()) {
          ++pos;  // skip FNOperator and BNOperator
          ops_only = false;
        }
      }

      // if composed of ops only, screen out products with zero VEV
      if (ops_only) {
        if (!can_change_qns(product, qns_t{})) {
          return ex<Constant>(0);
        }
      }

      for (const auto& [op1_lbl, op2_lbl] : op_connections) {
        auto it1 = oplbl2pos.find(op1_lbl);
        auto it2 = oplbl2pos.find(op2_lbl);
        if (it1 == oplbl2pos.end() || it2 == oplbl2pos.end())
          continue;  // one of the op labels is not present in the product
        const auto& [dummy1, op1_indices] = *it1;
        const auto& [dummy2, op2_indices] = *it2;
        for (const auto& op1_idx : op1_indices) {
          for (const auto& op2_idx : op2_indices) {
            if (op1_idx < op2_idx) {  // N.B. connections are directional
              connections.emplace_back(op1_idx, op2_idx);
            }
          }
        }
      }
    }

    // lower to tensor form
    lower_to_tensor_form(product);
    simplify(product);

    // compute VEV
    auto vev = tensor::vac_av(product, connections, /* use_topology = */ true);
    // restore Variable types to the Product
    if (!variables.empty())
      ranges::for_each(variables, [&vev](const auto& var) { vev *= var; });

    return simplify(vev);
  };

  ExprPtr result;
  if (expr.is<Product>()) {
    // expand sums in a product
    if (ranges::any_of(expr.as<Product>().factors(), [](const auto& factor) {
          return factor.template is<Sum>();
        })) {
      expr = expand(expr);
      simplify(expr);  // condense equivalent terms after expansion
      return vac_av(expr, op_connections, /* skip_clone = */ true);
    } else
      return vac_av_product(expr);
  } else if (expr.is<Sum>()) {
    result = sequant::transform_reduce(
        *expr, ex<Sum>(),
        [](const ExprPtr& running_total, const ExprPtr& summand) {
          return running_total + summand;
        },
        [&op_connections](const auto& op_product) {
          return vac_av(op_product, op_connections, /* skip_clone = */ true);
        });
    simplify(result);  // combine possible equivalent summands
    return result;
  } else if (expr.is<op_t>()) {
    return ex<Constant>(
        0);  // expectation value of a normal-ordered operator is 0
  } else if (expr.is<Constant>() || expr.is<Variable>()) {
    return expr;  // vacuum is normalized
  }
  throw std::invalid_argument(
      "mpbt::*::vac_av(expr): unknown expression type");
}

ExprPtr vac_av(
    ExprPtr expr,
    std::vector<std::pair<mbpt::OpType, mbpt::OpType>> op_connections,
    bool skip_clone) {
  return vac_av(expr, to_label_connections(op_connections), skip_clone);
}

#endif  // SEQUANT_DOMAIN_MBPT_VAC_AV_IPP