.. _program_listing_file_SeQuant_domain_mbpt_models_cc.cpp: Program Listing for File cc.cpp =============================== |exhale_lsh| :ref:`Return to documentation for file ` (``SeQuant/domain/mbpt/models/cc.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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() || expr.is()); 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(rational{1, k}) * op_Sk_comm_w_S; simplify(op_Sk); result += op_Sk; } return result; }; if (expr.is()) { return transform_op_op_pdt(expr); } else if (expr.is()) { auto& product = expr.as(); // Expand product as sum if (ranges::any_of(product.factors(), [](const auto& factor) { return factor.template is(); })) { expr = sequant::expand(expr); simplify(expr); return sim_tr(expr, commutator_rank); } else { return transform_op_op_pdt(expr); } } else if (expr.is()) { auto result = sequant::transform_reduce( *expr, ex(), [](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() || expr.is()) return expr; else throw std::invalid_argument( "CC::sim_tr(expr): Unsupported expression type"); } std::vector CC::t(size_t commutator_rank, size_t pmax, size_t pmin) { pmax = (pmax == std::numeric_limits::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 result(pmax + 1); for (std::int64_t p = pmax; p >= static_cast(pmin); --p) { // 2.a. screen out terms that cannot give nonzero after projection onto // hbar_p; // products that can produce excitations of rank p std::shared_ptr hbar_le_p; // keeps products that can produce excitations rank <=p for (auto& term : *hbar) { assert(term->is() || term->is()); if (raises_vacuum_up_to_rank(term, p)) { if (!hbar_le_p) hbar_le_p = std::make_shared(ExprPtrList{term}); else hbar_le_p->append(term); if (raises_vacuum_to_rank(term, p)) { if (!hbar_p) hbar_p = std::make_shared(ExprPtrList{term}); else hbar_p->append(term); } } } hbar = hbar_le_p; // 2.b project onto 0) and compute VEV result.at(p) = vac_av(p != 0 ? P(nₚ(p)) * hbar_p : hbar_p); } return result; } std::vector 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(1); auto lhbar = simplify((One + Λ(N)) * hbar); const auto op_connect = concat(default_op_connections(), std::vector>{{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 result(N + 1); for (auto p = N; p >= 1; --p) { // 2.a. screen out terms that cannot give nonzero after projection onto // hbar_p; // products that can produce excitations of rank p std::shared_ptr hbar_le_p; // keeps products that can produce excitations rank <=p for (auto& term : *lhbar) { // pick terms from lhbar assert(term->is() || term->is()); if (lowers_rank_or_lower_to_vacuum(term, p)) { if (!hbar_le_p) hbar_le_p = std::make_shared(ExprPtrList{term}); else hbar_le_p->append(term); if (lowers_rank_to_vacuum(term, p)) { if (!hbar_p) hbar_p = std::make_shared(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 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>{{OpType::h, OpType::t_1}, {OpType::f, OpType::t_1}, {OpType::g, OpType::t_1}, {OpType::h_1, OpType::t}}); std::vector result(N + 1); for (auto p = N; p >= 1; --p) { auto freq_term = ex(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 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(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>{{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 result(N + 1); for (auto p = N; p >= 1; --p) { auto freq_term = ex(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 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>{{OpType::h, OpType::R}, {OpType::f, OpType::R}, {OpType::g, OpType::R}}); // initialize result vector std::vector 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 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>{{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 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