Program Listing for File csv.cpp

↰ Return to documentation for file (SeQuant/domain/mbpt/rules/csv.cpp)

//
// Created by Eduard Valeyev on 3/8/25.
//

#include <SeQuant/domain/mbpt/rules/csv.hpp>

#include <SeQuant/core/context.hpp>
#include <SeQuant/core/expr.hpp>
#include <SeQuant/core/utility/exception.hpp>
#include <SeQuant/core/utility/indices.hpp>
#include <SeQuant/core/utility/macros.hpp>
#include <SeQuant/core/utility/string.hpp>

#include <range/v3/algorithm/contains.hpp>
#include <range/v3/algorithm/none_of.hpp>
#include <range/v3/view/transform.hpp>

#include <string>

namespace sequant::mbpt {

ExprPtr csv_transform_impl(Tensor const& tnsr, const IndexBasis& csv_basis,
                           bool orthonormal,
                           std::wstring_view coeff_tensor_label) {
  using ranges::views::transform;
  using sequant::reserved::overlap_label;

  if (ranges::none_of(tnsr.const_braket_indices(), &Index::has_proto_indices))
    return nullptr;

  SEQUANT_ASSERT(ranges::none_of(tnsr.aux(), &Index::has_proto_indices));
  SEQUANT_ASSERT(get_default_context().index_basis_registry());
  SEQUANT_ASSERT(
      get_default_context().index_basis_registry()->contains(csv_basis));

  // a basis instance is registered in one space, so an index minted in it
  // cannot keep a leg's space (e.g. its spin)
  auto require_in_basis_space = [&csv_basis](const Index& leg) {
    if (csv_basis.has_basis_instance() && leg.space() != csv_basis.space())
      throw Exception("csv_transform: a CSV leg is in space " +
                      toUtf8(leg.space().base_key()) +
                      ", not in the target basis instance's space " +
                      toUtf8(csv_basis.space().base_key()));
  };

  // shortcut for the CSV overlap if csv_basis is orthonormal
  if (orthonormal && tnsr.label() == overlap_label()) {
    SEQUANT_ASSERT(tnsr.bra_rank() == 1     //
                   && tnsr.ket_rank() == 1  //
                   && tnsr.aux_rank() == 0);

    auto&& bra_idx = tnsr.bra().at(0);
    auto&& ket_idx = tnsr.ket().at(0);
    [[maybe_unused]] const auto bra_has_proto_indices =
        bra_idx.has_proto_indices();
    [[maybe_unused]] const auto ket_has_proto_indices =
        ket_idx.has_proto_indices();
    SEQUANT_ASSERT(bra_has_proto_indices || ket_has_proto_indices);

    if (bra_has_proto_indices && ket_has_proto_indices) {
      const Index& end = ordinal_compare(bra_idx, ket_idx) ? bra_idx : ket_idx;
      require_in_basis_space(end);
      // a fresh dummy: another overlap may share either leg
      const auto dummy_idx = Index::make_tmp_index(
          csv_basis.has_basis_instance() ? csv_basis : IndexBasis(end.space()));

      return ex<Product>(
          1,
          ExprPtrList{ex<Tensor>(coeff_tensor_label,                 //
                                 bra({bra_idx}), ket({dummy_idx})),  //
                      ex<Tensor>(coeff_tensor_label,                 //
                                 bra({dummy_idx}), ket({ket_idx}))});
    } else if ((bra_has_proto_indices ? ket_idx : bra_idx).basis() ==
               csv_basis) {
      // the other leg is in the target basis
      return ex<Product>(
          1,
          ExprPtrList{ex<Tensor>(coeff_tensor_label,  //
                                 bra({bra_idx}), ket({ket_idx}))});
    }
  }

  Product result;
  container::svector<Index> rbra, rket;

  rbra.reserve(tnsr.bra_rank());
  for (auto&& idx : tnsr.bra()) {
    if (idx.has_proto_indices()) {
      require_in_basis_space(idx);
      Index xidx = Index::make_tmp_index(csv_basis);
      result.append(
          1, ex<Tensor>(coeff_tensor_label, bra({idx}), ket({xidx}), aux({})));
      rbra.emplace_back(std::move(xidx));
    } else
      rbra.emplace_back(idx);
  }

  rket.reserve(tnsr.ket_rank());
  for (auto&& idx : tnsr.ket()) {
    if (idx.has_proto_indices()) {
      require_in_basis_space(idx);
      Index xidx = Index::make_tmp_index(csv_basis);
      result.append(
          1, ex<Tensor>(coeff_tensor_label, bra({xidx}), ket({idx}), aux({})));
      rket.emplace_back(std::move(xidx));
    } else
      rket.emplace_back(idx);
  }

  auto xtnsr = ex<Tensor>(tnsr.label(), bra(rbra), ket(rket), tnsr.aux(),
                          tnsr.symmetry(), tnsr.braket_symmetry(),
                          tnsr.column_symmetry());
  result.prepend(1, std::move(xtnsr));

  return ex<Product>(std::move(result));
}

namespace {

ExprPtr csv_transform_rec(
    ExprPtr const& expr, const IndexBasis& csv_basis, bool orthonormal,
    std::wstring const& coeff_tensor_label,
    container::svector<std::wstring> const& tensor_labels) {
  using ranges::views::transform;
  if (expr->is<Sum>())
    return ex<Sum>(
        *expr                                                       //
        | transform([&csv_basis, orthonormal, &coeff_tensor_label,  //
                     &tensor_labels](auto&& x) {
            return csv_transform_rec(x, csv_basis, orthonormal,
                                     coeff_tensor_label, tensor_labels);
          }));
  else if (expr->is<Tensor>()) {
    auto const& tnsr = expr->as<Tensor>();
    if (!ranges::contains(tensor_labels, tnsr.label())) return expr;
    if (ranges::none_of(tnsr.indices(), &Index::has_proto_indices)) return expr;
    return csv_transform_impl(tnsr, csv_basis, orthonormal, coeff_tensor_label);
  } else if (expr->is<Product>()) {
    auto const& prod = expr->as<Product>();

    Product result;
    result.scale(prod.scalar());

    for (auto&& f : prod.factors()) {
      auto trans = csv_transform_rec(f, csv_basis, orthonormal,
                                     coeff_tensor_label, tensor_labels);
      // N.B. do not flatten the product to ensure that CSV transform of
      // each factor is performed before assembling the final product
      // this way for DF-factorized integrals each DF factor is transformed
      // to CSV basis before DF reconstruction
      result.append(1, trans ? trans : f, Product::Flatten::No);
    }

    return ex<Product>(std::move(result));

  } else
    return expr;
}

}  // namespace

ExprPtr csv_transform(ExprPtr const& expr, const IndexBasis& csv_basis,
                      std::wstring const& coeff_tensor_label,
                      container::svector<std::wstring> const& tensor_labels) {
  if (!csv_basis.has_basis_instance())
    return csv_transform_rec(expr, csv_basis, /*orthonormal=*/true,
                             coeff_tensor_label, tensor_labels);
  const auto& registry = get_default_context().index_basis_registry();
  if (!registry || !registry->basis_label(csv_basis))
    throw Exception(
        "csv_transform: the target basis instance " +
        toUtf8(csv_basis.space().base_key()) + ";" +
        std::to_string(*csv_basis.basis_instance()) +
        " is not registered under a name; indices minted in it would print "
        "and be keyed as the space's own basis");
  const auto resolved = registry->resolve(csv_basis);
  return csv_transform_rec(expr, resolved,
                           resolved.metric() == IndexBasisMetric::Unit,
                           coeff_tensor_label, tensor_labels);
}

}  // namespace sequant::mbpt