Program Listing for File spin.hpp

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

//
// Created by Eduard Valeyev on 2019-02-27.
//

#ifndef SEQUANT_DOMAIN_MBPT_SPIN_HPP
#define SEQUANT_DOMAIN_MBPT_SPIN_HPP

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

#include <SeQuant/core/container.hpp>
#include <SeQuant/core/expr.hpp>
#include <SeQuant/core/index.hpp>
#include <SeQuant/core/tensor.hpp>

#include <cassert>
#include <cstddef>
#include <initializer_list>
#include <string>
#include <vector>

namespace sequant {

namespace mbpt {

enum class Spin : bitset_t {
  alpha = 0b000001,
  beta = 0b000010,
  any = 0b000011,  // both bits set so that overlap and union work as expected
                   // (any & alpha = alpha, alpha | beta = any)
  free = any,      // syntax sugar
  spinmask = 0b000011  // coincides with any
};

// Spin is a scoped enum, hence not implicitly convertible to
// QuantumNumbersAttr::bitset_t
static_assert(!std::is_convertible_v<sequant::mbpt::Spin, bitset_t>);
// QuantumNumbersAttr::bitset_t cannot be constructed from Spin
static_assert(!std::is_constructible_v<bitset_t, sequant::mbpt::Spin>);
// but Spin can be cast to QuantumNumbersAttr::bitset_t
static_assert(meta::is_statically_castable_v<sequant::mbpt::Spin, bitset_t>);
// Spin cannot be cast to nonsense ...
static_assert(
    !meta::is_statically_castable_v<sequant::mbpt::Spin, std::string>);

inline Spin operator~(Spin s) {
  return static_cast<Spin>(~(static_cast<bitset_t>(s)));
}
inline Spin operator|(Spin s1, Spin s2) {
  return static_cast<Spin>(static_cast<bitset_t>(s1) |
                           static_cast<bitset_t>(s2));
}
inline Spin operator&(Spin s1, Spin s2) {
  return static_cast<Spin>(static_cast<bitset_t>(s1) &
                           static_cast<bitset_t>(s2));
}

inline Spin to_spin(const QuantumNumbersAttr& t) {
  assert((t.to_int32() & static_cast<int>(Spin::spinmask)) != 0);
  return static_cast<Spin>(static_cast<Spin>(t.to_int32()) & Spin::spinmask);
}

inline QuantumNumbersAttr spinannotation_remove(const QuantumNumbersAttr& t) {
  return t.intersection(QuantumNumbersAttr(~Spin::spinmask));
}

template <typename WS, typename = std::enable_if_t<
                           meta::is_wstring_or_view_v<std::decay_t<WS>>>>
std::wstring spinannotation_remove(WS&& label) {
  auto [base, orbital] = Index::make_split_label(label);
  if (base.back() == L'↑' || base.back() == L'↓') {
    base.remove_suffix(1);
  }
  return Index::make_merged_label(base, orbital);
}

template <typename WS, typename = std::enable_if_t<
                           meta::is_wstring_or_view_v<std::decay_t<WS>>>>
std::wstring spinannotation_add(WS&& label, Spin s) {
  auto [base, ordinal] = Index::make_split_label(label);
  switch (s) {
    case Spin::any:
      return std::wstring(label);
    case Spin::alpha:
      return Index::make_merged_label(std::wstring(base) + L"↑", ordinal);
    case Spin::beta:
      return Index::make_merged_label(std::wstring(base) + L"↓", ordinal);
    default:
      assert(false && "invalid quantum number");
      abort();
  }
}

template <typename WS, typename = std::enable_if_t<
                           meta::is_wstring_or_view_v<std::decay_t<WS>>>>
std::wstring spinannotation_replacе(WS&& label, Spin s) {
  auto label_sf = spinannotation_remove(std::forward<WS>(label));
  return spinannotation_add(label_sf, s);
}

}  // namespace mbpt

// make alpha-spin idx
[[nodiscard]] Index make_spinalpha(const Index& idx);

// make beta-spin idx
[[nodiscard]] Index make_spinbeta(const Index& idx);

// make null-spin idx
[[nodiscard]] Index make_spinfree(const Index& idx);

ExprPtr transform_expr(const ExprPtr& expr,
                       const container::map<Index, Index>& index_replacements,
                       Constant::scalar_type scaling_factor = 1);

ExprPtr swap_bra_ket(const ExprPtr& expr);

ExprPtr append_spin(const ExprPtr& expr,
                    const container::map<Index, Index>& index_replacements);

ExprPtr remove_spin(const ExprPtr& expr);

bool spin_symm_tensor(const Tensor& tensor);

bool same_spin_tensor(const Tensor& tensor);

bool can_expand(const Tensor& tensor);

ExprPtr expand_antisymm(const Tensor& tensor, bool skip_spinsymm = false);

ExprPtr expand_antisymm(const ExprPtr& expr, bool skip_spinsymm = false);

bool has_tensor(const ExprPtr& expr, std::wstring label);

container::svector<container::map<Index, Index>> A_maps(const Tensor& A);

ExprPtr remove_tensor(const Product& product, std::wstring label);

ExprPtr remove_tensor(const ExprPtr& expr, std::wstring label);

ExprPtr expand_A_op(const Product& product);

ExprPtr symmetrize_expr(const Product& product);

ExprPtr symmetrize_expr(const ExprPtr& expr);

ExprPtr expand_A_op(const ExprPtr& expr);

container::svector<container::map<Index, Index>> P_maps(
    const Tensor& P, bool keep_canonical = true, bool pair_wise = false);

ExprPtr expand_P_op(const Product& product, bool keep_canonical = true,
                    bool pair_wise = true);

ExprPtr expand_P_op(const ExprPtr& expr, bool keep_canonical = true,
                    bool pair_wise = true);

container::svector<container::map<Index, Index>> S_replacement_maps(
    const Tensor& S);

ExprPtr S_maps(const ExprPtr& expr);


template <typename Seq0, typename Seq1>
std::size_t count_cycles(Seq0&& v0, const Seq1& v1) {
  std::remove_reference_t<Seq0> v(std::forward<Seq0>(v0));
  using T = std::decay_t<decltype(v[0])>;
  assert(ranges::is_permutation(v, v1));

  auto make_null = []() -> T {
    if constexpr (std::is_arithmetic_v<T>) {
      return -1;
    } else if constexpr (std::is_same_v<T, Index>) {
      return L"p_50";
    } else  // unreachable
      abort();
  };

  std::size_t n_cycles = 0;
  const auto null = make_null();
  assert(ranges::contains(v, null) == false);
  assert(ranges::contains(v1, null) == false);
  for (auto it = v.begin(); it != v.end(); ++it) {
    if (*it != null) {
      n_cycles++;
      auto idx = std::distance(v.begin(), it);
      auto it0 = it;
      auto it1 = std::find(v1.begin(), v1.end(), *it0);
      auto idx1 = std::distance(v1.begin(), it1);
      do {
        it0 = std::find(v.begin(), v.end(), v[idx1]);
        it1 = std::find(v1.begin(), v1.end(), *it0);
        idx1 = std::distance(v1.begin(), it1);
        *it0 = null;
      } while (idx1 != idx);
    }
  }
  return n_cycles;
};

ExprPtr closed_shell_spintrace(
    const ExprPtr& expr,
    const container::svector<container::svector<Index>>& ext_index_groups = {});

container::svector<container::svector<Index>> external_indices(Tensor const&);

ExprPtr closed_shell_CC_spintrace(ExprPtr const& expr);

ExprPtr closed_shell_CC_spintrace_rigorous(ExprPtr const& expr);

auto index_list(const ExprPtr& expr);

Tensor swap_spin(const Tensor& t);

ExprPtr swap_spin(const ExprPtr& expr);

ExprPtr merge_tensors(const Tensor& O1, const Tensor& O2);

std::vector<ExprPtr> open_shell_A_op(const Tensor& A);

std::vector<ExprPtr> open_shell_P_op_vector(const Tensor& A);

std::vector<ExprPtr> open_shell_spintrace(
    const ExprPtr& expr,
    const container::svector<container::svector<Index>>& ext_index_groups,
    const int single_spin_case = 0);

std::vector<ExprPtr> open_shell_CC_spintrace(const ExprPtr& expr);

ExprPtr spintrace(
    const ExprPtr& expr,
    container::svector<container::svector<Index>> ext_index_groups = {},
    bool spinfree_index_spaces = true);

ExprPtr factorize_S(const ExprPtr& expression,
                    std::initializer_list<IndexList> ext_index_groups,
                    bool fast_method = true);

ExprPtr biorthogonal_transform(
    const sequant::ExprPtr& expr,
    const container::svector<container::svector<sequant::Index>>&
        ext_index_groups = {},
    double threshold = 1.e-12);

}  // namespace sequant

#endif  // SEQUANT_DOMAIN_MBPT_SPIN_HPP