Program Listing for File cc.cpp¶
↰ Return to documentation for file (SeQuant/domain/mbpt/models/cc.cpp
)
#include <SeQuant/core/expr.hpp>
#include <SeQuant/core/rational.hpp>
#include <SeQuant/core/runtime.hpp>
#include <SeQuant/domain/mbpt/context.hpp>
#include <SeQuant/domain/mbpt/convention.hpp>
#include <SeQuant/domain/mbpt/models/cc.hpp>
#include <SeQuant/domain/mbpt/op.hpp>
#include <SeQuant/domain/mbpt/spin.hpp>
#include <cassert>
#include <cstdint>
#include <memory>
#include <new>
#include <stdexcept>
#include <utility>
namespace sequant::mbpt {
CC::CC(size_t n, Ansatz a) : N(n), ansatz_(a) {}
CC::Ansatz CC::ansatz() const { return ansatz_; }
bool CC::unitary() const {
return ansatz_ == Ansatz::U || ansatz_ == Ansatz::oU;
}
ExprPtr CC::sim_tr(ExprPtr expr, size_t commutator_rank) {
const bool skip_singles = ansatz_ == Ansatz::oT || ansatz_ == Ansatz::oU;
auto transform_op_op_pdt = [this, &commutator_rank,
skip_singles](const ExprPtr& expr) {
assert(expr.is<op_t>() || expr.is<Product>());
auto result = expr;
auto op_Sk = result;
for (size_t k = 1; k <= commutator_rank; ++k) {
ExprPtr op_Sk_comm_w_S;
op_Sk_comm_w_S =
op_Sk * T(N, skip_singles); // traditional SR ansatz: [O,T] = (O T)_c
if (unitary()) // unitary SR ansatz: [O,T-T^+] = (O T)_c + (T^+ O)_c
op_Sk_comm_w_S += adjoint(T(N, skip_singles)) * op_Sk;
op_Sk = ex<Constant>(rational{1, k}) * op_Sk_comm_w_S;
simplify(op_Sk);
result += op_Sk;
}
return result;
};
if (expr.is<op_t>()) {
return transform_op_op_pdt(expr);
} else if (expr.is<Product>()) {
auto& product = expr.as<Product>();
// Expand product as sum
if (ranges::any_of(product.factors(), [](const auto& factor) {
return factor.template is<Sum>();
})) {
expr = sequant::expand(expr);
simplify(expr);
return sim_tr(expr, commutator_rank);
} else {
return transform_op_op_pdt(expr);
}
} else if (expr.is<Sum>()) {
auto result = sequant::transform_reduce(
*expr, ex<Sum>(),
[](const ExprPtr& running_total, const ExprPtr& summand) {
return running_total + summand;
},
[=](const auto& op_product) {
return transform_op_op_pdt(op_product);
});
return result;
} else if (expr.is<Constant>() || expr.is<Variable>())
return expr;
else
throw std::invalid_argument(
"CC::sim_tr(expr): Unsupported expression type");
}
std::vector<ExprPtr> CC::t(size_t commutator_rank, size_t pmax, size_t pmin) {
pmax = (pmax == std::numeric_limits<size_t>::max() ? N : pmax);
assert(commutator_rank >= 1 && "commutator rank should be >= 1");
assert(pmax >= pmin && "pmax should be >= pmin");
// 1. construct hbar(op) in canonical form
auto hbar = sim_tr(H(), commutator_rank);
// 2. project onto each manifold, screen, lower to tensor form and wick it
std::vector<ExprPtr> result(pmax + 1);
for (std::int64_t p = pmax; p >= static_cast<std::int64_t>(pmin); --p) {
// 2.a. screen out terms that cannot give nonzero after projection onto
// <p|
std::shared_ptr<Sum>
hbar_p; // products that can produce excitations of rank p
std::shared_ptr<Sum>
hbar_le_p; // keeps products that can produce excitations rank <=p
for (auto& term : *hbar) {
assert(term->is<Product>() || term->is<op_t>());
if (raises_vacuum_up_to_rank(term, p)) {
if (!hbar_le_p)
hbar_le_p = std::make_shared<Sum>(ExprPtrList{term});
else
hbar_le_p->append(term);
if (raises_vacuum_to_rank(term, p)) {
if (!hbar_p)
hbar_p = std::make_shared<Sum>(ExprPtrList{term});
else
hbar_p->append(term);
}
}
}
hbar = hbar_le_p;
// 2.b project onto <p| (i.e., multiply by P(p) if p>0) and compute VEV
result.at(p) = vac_av(p != 0 ? P(nₚ(p)) * hbar_p : hbar_p);
}
return result;
}
std::vector<ExprPtr> CC::λ(size_t commutator_rank) {
assert(commutator_rank >= 1 && "commutator rank should be >= 1");
assert(!unitary() && "there is no need for CC::λ for unitary ansatz");
// construct hbar
auto hbar = sim_tr(H(), commutator_rank - 1);
const auto One = ex<Constant>(1);
auto lhbar = simplify((One + Λ(N)) * hbar);
const auto op_connect =
concat(default_op_connections(),
std::vector<std::pair<OpType, OpType>>{{OpType::h, OpType::A},
{OpType::f, OpType::A},
{OpType::g, OpType::A},
{OpType::h, OpType::S},
{OpType::f, OpType::S},
{OpType::g, OpType::S}});
// 2. project onto each manifold, screen, lower to tensor form and wick it
std::vector<ExprPtr> result(N + 1);
for (auto p = N; p >= 1; --p) {
// 2.a. screen out terms that cannot give nonzero after projection onto
// <P|
std::shared_ptr<Sum>
hbar_p; // products that can produce excitations of rank p
std::shared_ptr<Sum>
hbar_le_p; // keeps products that can produce excitations rank <=p
for (auto& term : *lhbar) { // pick terms from lhbar
assert(term->is<Product>() || term->is<op_t>());
if (lowers_rank_or_lower_to_vacuum(term, p)) {
if (!hbar_le_p)
hbar_le_p = std::make_shared<Sum>(ExprPtrList{term});
else
hbar_le_p->append(term);
if (lowers_rank_to_vacuum(term, p)) {
if (!hbar_p)
hbar_p = std::make_shared<Sum>(ExprPtrList{term});
else
hbar_p->append(term);
}
}
}
lhbar = hbar_le_p;
// 2.b multiply by adjoint of P(p) (i.e., P(-p)) on the right side and
// compute VEV
result.at(p) = vac_av(hbar_p * P(nₚ(-p)), op_connect);
}
return result;
}
std::vector<ExprPtr> CC::t_pt(size_t order, size_t rank) {
assert(order == 1 &&
"sequant::mbpt::CC::t_pt(): only first-order perturbation is "
"supported now");
assert(rank == 1 &&
"sequant::mbpt::CC::t_pt(): only one-body perturbation "
"operator is supported now");
assert(ansatz_ == Ansatz::T && "unitary ansatz is not yet supported");
// construct h1_bar
// truncate h1_bar at rank 2 for one-body perturbation
// operator and at rank 4 for two-body perturbation operator
const auto h1_truncate_at = rank == 1 ? 2 : 4;
auto h1_bar = sim_tr(H_pt(1, rank), h1_truncate_at);
// construct [hbar, T(1)]
auto hbar_pert = sim_tr(H(), 3) * T_pt(order, N);
// [Eq. 34, WIREs Comput Mol Sci. 2019; 9:e1406]
auto expr = simplify(h1_bar + hbar_pert);
// connectivity:
// connect t and t1 with {h,f,g}
// connect h1 with t
const auto op_connect =
concat(default_op_connections(),
std::vector<std::pair<OpType, OpType>>{{OpType::h, OpType::t_1},
{OpType::f, OpType::t_1},
{OpType::g, OpType::t_1},
{OpType::h_1, OpType::t}});
std::vector<ExprPtr> result(N + 1);
for (auto p = N; p >= 1; --p) {
auto freq_term = ex<Variable>(L"ω") * P(nₚ(p)) * T_pt_(order, p);
result.at(p) = vac_av(P(nₚ(p)) * expr, op_connect) - vac_av(freq_term);
}
return result;
}
std::vector<ExprPtr> CC::λ_pt(size_t order, size_t rank) {
assert(order == 1 &&
"sequant::mbpt::CC::λ_pt(): only first-order perturbation is "
"supported now");
assert(rank == 1 &&
"sequant::mbpt::CC::λ_pt(): only one-body perturbation "
"operator is supported now");
assert(ansatz_ == Ansatz::T && "unitary ansatz is not yet supported");
// construct hbar
auto hbar = sim_tr(H(), 4);
// construct h1_bar
// truncate h1_bar at rank 2 for one-body perturbation
// operator and at rank 4 for two-body perturbation operator
const auto h1_truncate_at = rank == 1 ? 2 : 4;
auto h1_bar = sim_tr(H_pt(1, rank), h1_truncate_at);
// construct [hbar, T(1)]
auto hbar_pert = sim_tr(H(), 3) * T_pt(order, N);
// [Eq. 35, WIREs Comput Mol Sci. 2019; 9:e1406]
const auto One = ex<Constant>(1);
auto expr =
simplify((One + Λ(N)) * (h1_bar + hbar_pert) + Λ_pt(order, N) * hbar);
// connectivity:
// t and t1 with {h,f,g}
// projectors with {h,f,g}
// h1 with t
// h1 with projectors
const auto op_connect =
concat(default_op_connections(),
std::vector<std::pair<OpType, OpType>>{{OpType::h, OpType::t_1},
{OpType::f, OpType::t_1},
{OpType::g, OpType::t_1},
{OpType::h_1, OpType::t},
{OpType::h, OpType::A},
{OpType::f, OpType::A},
{OpType::g, OpType::A},
{OpType::h, OpType::S},
{OpType::f, OpType::S},
{OpType::g, OpType::S},
{OpType::h_1, OpType::A},
{OpType::h_1, OpType::S}});
std::vector<ExprPtr> result(N + 1);
for (auto p = N; p >= 1; --p) {
auto freq_term = ex<Variable>(L"ω") * Λ_pt_(order, p) * P(nₚ(-p));
result.at(p) = vac_av(expr * P(nₚ(-p)), op_connect) + vac_av(freq_term);
}
return result;
}
std::vector<ExprPtr> CC::eom_r(nₚ np, nₕ nh) {
assert(!unitary() && "Unitary ansatz is not yet supported");
assert(np > 0 || nh > 0 && "Unsupported excitation order");
assert(np == nh &&
"Only EE-EOM-CC has been tested ... remove this assert to try "
"Fock-space EOM-CC");
if (np != nh)
assert(
get_default_context().spbasis() != SPBasis::spinfree &&
"spin-free basis does not yet support non particle-conserving cases");
// construct hbar
auto hbar = sim_tr(H(), 4);
// hbar * R
auto hbar_R = hbar * R(np, nh);
// connectivity:
// default connections + connect R with {h,f,g}
const auto op_connect =
concat(default_op_connections(),
std::vector<std::pair<OpType, OpType>>{{OpType::h, OpType::R},
{OpType::f, OpType::R},
{OpType::g, OpType::R}});
// initialize result vector
std::vector<ExprPtr> result;
using std::max;
auto idx = max(np, nh); // index for populating the result vector
result.resize(idx + 1);
// start from the highest excitation order, go down to the lowest possible
for (std::int64_t rp = np, rh = nh; rp > 0 || rh > 0; --rp, --rh) {
// project with <rp, rh| (i.e., multiply P(rp, rh)) and compute VEV
result.at(idx) = vac_av(P(nₚ(rp), nₕ(rh)) * hbar_R, op_connect);
idx--; // index decrement
}
return result;
}
std::vector<ExprPtr> CC::eom_l(nₚ np, nₕ nh) {
assert(!unitary() && "Unitary ansatz is not yet supported");
assert(np > 0 || nh > 0 && "Unsupported excitation order");
assert(np == nh &&
"Only EE-EOM-CC has been tested ... remove this assert to try "
"Fock-space EOM-CC");
if (np != nh)
assert(get_default_context().spbasis() != SPBasis::spinfree &&
"spin-free basis does not support non particle-conserving cases");
// construct hbar
auto hbar = sim_tr(H(), 4);
// L * hbar
auto L_hbar = L(np, nh) * hbar;
// connectivity:
// default connections + connect H with projectors
const auto op_connect =
concat(default_op_connections(),
std::vector<std::pair<OpType, OpType>>{{OpType::h, OpType::A},
{OpType::f, OpType::A},
{OpType::g, OpType::A},
{OpType::h, OpType::S},
{OpType::f, OpType::S},
{OpType::g, OpType::S}});
// initialize result vector
std::vector<ExprPtr> result;
using std::max;
auto idx = max(np, nh); // index for populating the result vector
result.resize(idx + 1);
// start from the highest excitation order, go down to the lowest possible
for (std::int64_t rp = np, rh = nh; rp > 0 || rh > 0; --rp, --rh) {
// right project with |rp,rh> (i.e., multiply P(-np, -rh)) and compute VEV
result.at(idx) = vac_av(L_hbar * P(nₚ(-rp), nₕ(-rh)), op_connect);
idx--; // index decrement
}
return result;
}
} // namespace sequant::mbpt