Program Listing for File sum.cpp¶
↰ Return to documentation for file (SeQuant/core/expressions/sum.cpp)
#include <SeQuant/core/expressions/expr_algorithms.hpp>
#include <SeQuant/core/expressions/expr_ptr.hpp>
#include <SeQuant/core/expressions/sum.hpp>
#include <SeQuant/core/hash.hpp>
#include <SeQuant/core/logger.hpp>
#include <SeQuant/core/utility/macros.hpp>
namespace sequant {
Sum::Sum(ExprPtrList summands) {
// use append to flatten out Sum summands
for (auto &&summand : summands) {
append(std::forward<decltype(summand)>(summand));
}
}
Sum::Sum(summands_type &&summands, move_only_tag)
: summands_(std::move(summands)) {
std::size_t pos = 0;
for (auto it = summands_.begin(); it != summands_.end(); ++it) {
auto &summand = *it;
bool do_erase = false;
if (summand->is_zero()) {
do_erase = true;
} else if (summand->is<Constant>()) {
auto summand_constant = summand.as_shared_ptr<Constant>();
if (constant_summand_idx_) { // add up to the existing constant ...
SEQUANT_ASSERT(summands_.at(*constant_summand_idx_)->is<Constant>());
summands_[*constant_summand_idx_].as<Constant>() += *summand_constant;
do_erase = true;
} else { // or memorize the position of the constant
constant_summand_idx_ = pos;
}
}
// erase if needed
if (do_erase) {
summands_.erase(it);
it = summands_.begin();
std::advance(it, pos);
} else
++pos;
}
}
Sum &Sum::append(ExprPtr summand) {
SEQUANT_ASSERT(summand);
if (!summand->is<Sum>()) {
if (!summand->is_zero()) { // exclude zeros
if (summand->is<Constant>()) { // add up constants
// immediately, if possible
auto summand_constant = summand.as_shared_ptr<Constant>();
if (constant_summand_idx_) {
SEQUANT_ASSERT(summands_.at(*constant_summand_idx_)->is<Constant>());
summands_[*constant_summand_idx_].as<Constant>() += *summand;
} else {
summands_.push_back(summand->clone());
constant_summand_idx_ = summands_.size() - 1;
}
} else {
summands_.push_back(summand->clone());
}
reset_hash_value();
}
} else { // this recursively flattens Sum summands
for (auto &subsummand : *summand) this->append(subsummand);
}
return *this;
}
Sum &Sum::prepend(ExprPtr summand) {
SEQUANT_ASSERT(summand);
if (!summand->is<Sum>()) {
if (!summand->is_zero()) {
// exclude zeros
if (summand->is<Constant>()) {
auto summand_constant = summand.as_shared_ptr<Constant>();
if (constant_summand_idx_) { // add up to the existing constant ...
SEQUANT_ASSERT(summands_.at(*constant_summand_idx_)->is<Constant>());
summands_[*constant_summand_idx_].as<Constant>() += *summand_constant;
} else { // or include the nonzero constant and update
// constant_summand_idx_
summands_.insert(summands_.begin(), summand->clone());
constant_summand_idx_ = 0;
}
} else {
summands_.insert(summands_.begin(), summand->clone());
if (constant_summand_idx_) // if have a constant, update its position
++*constant_summand_idx_;
}
reset_hash_value();
}
} else { // this recursively flattens Sum summands
for (auto &subsummand : *summand) this->prepend(subsummand);
}
return *this;
}
const Sum::summands_type &Sum::summands() const { return summands_; }
const ExprPtr &Sum::summand(size_t i) const { return summands_.at(i); }
ExprPtr Sum::take_n(size_t count) const {
const auto e = (count >= summands_.size() ? summands_.end()
: (summands_.begin() + count));
return ex<Sum>(summands_.begin(), e);
}
ExprPtr Sum::take_n(size_t offset, size_t count) const {
const auto offset_plus_count = offset + count;
const auto b = (offset >= summands_.size() ? summands_.end()
: (summands_.begin() + offset));
const auto e = (offset_plus_count >= summands_.size()
? summands_.end()
: (summands_.begin() + offset_plus_count));
return ex<Sum>(b, e);
}
bool Sum::empty() const { return summands_.empty(); }
std::size_t Sum::size() const { return summands_.size(); }
std::wstring Sum::to_latex() const {
std::wstring result;
result = L"{ \\bigl(";
std::size_t counter = 0;
for (const auto &i : summands()) {
const auto i_is_product = i->is<Product>();
if (!i_is_product) {
result += (counter == 0) ? i->to_latex() : (L" + " + i->to_latex());
} else { // i_is_product
const auto i_prod = i->as<Product>();
const auto scalar = i_prod.scalar();
if (scalar.real() < 0 || (scalar.real() == 0 && scalar.imag() < 0)) {
result += L" - " + i_prod.to_latex(true);
} else {
result += (counter == 0) ? i->to_latex() : (L" + " + i->to_latex());
}
}
++counter;
}
result += L"\\bigr) }";
return result;
}
Expr::type_id_type Sum::type_id() const { return Expr::get_type_id<Sum>(); }
ExprPtr Sum::clone() const {
auto cloned_summands =
summands() |
ranges::views::transform([](const ExprPtr &ptr) { return ptr->clone(); });
return ex<Sum>(ranges::begin(cloned_summands), ranges::end(cloned_summands));
}
void Sum::adjoint() {
using namespace ranges;
auto adj_summands = summands() | views::transform([](auto &&expr) {
return ::sequant::adjoint(expr);
});
*this = Sum(ranges::begin(adj_summands), ranges::end(adj_summands));
}
ExprPtr Sum::canonicalize_impl(bool multipass, CanonicalizeOptions opts) {
if (Logger::instance().canonicalize)
std::wcout << "Sum::canonicalize_impl: input = "
<< to_latex_align(shared_from_this()) << std::endl;
const auto npasses = multipass ? 2 : 1;
for (auto pass = 0; pass != npasses; ++pass) {
const auto rapid = (pass % 2 == 0);
// canonicalizing TNs in a sum requires treating named indices as
// meaningful/distinct
auto opts_copy = opts;
opts_copy.ignore_named_index_labels =
CanonicalizeOptions::IgnoreNamedIndexLabel::No;
if (rapid) {
opts_copy.method = CanonicalizationMethod::Lexicographic;
} else
opts_copy.method = opts.method | CanonicalizationMethod::Topological;
// recursively canonicalize summands ...
// using for_each and direct access to summands
sequant::for_each(summands_, [&opts_copy, &rapid](ExprPtr &summand) {
ExprPtr bp;
if (rapid) {
bp = summand->rapid_canonicalize(opts_copy);
} else {
bp = summand->canonicalize(opts_copy);
}
if (bp) {
SEQUANT_ASSERT(bp->template is<Constant>());
summand = ex<Product>(std::static_pointer_cast<Constant>(bp)->value(),
ExprPtrList{summand});
}
});
if (Logger::instance().canonicalize)
std::wcout << "Sum::canonicalize_impl (pass=" << pass
<< "): after canonicalizing summands = "
<< to_latex_align(shared_from_this()) << std::endl;
HashingAccumulator acc;
for (auto &summand : summands_) {
acc.append(summand);
}
// last pass? sort by hash then by Expr::operator<
// N.B. no point in differentiating between canonicalization methods here
// since need to sort in both cases
auto new_sum =
(pass == npasses - 1) ? acc.make_canonicalized_sum() : acc.make_sum();
using std::swap;
swap(*this, *new_sum);
if (Logger::instance().canonicalize)
std::wcout << "Sum::canonicalize_impl (pass=" << pass
<< "): after reducing summands = "
<< to_latex_align(shared_from_this()) << std::endl;
}
return {}; // side effects are absorbed into summands
}
Sum &Sum::operator+=(const Expr &that) {
this->append(const_cast<Expr &>(that).shared_from_this());
return *this;
}
Sum &Sum::operator-=(const Expr &that) {
if (that.is<Constant>())
this->append(ex<Constant>(-that.as<Constant>().value()));
else
this->append(ex<Product>(
-1, ExprPtrList{const_cast<Expr &>(that).shared_from_this()}));
return *this;
}
ExprIterator Sum::begin_subexpr() {
if (!summands_.empty()) {
reset_hash_value();
}
return ExprIterator{summands_.data()};
}
ExprIterator Sum::end_subexpr() {
// N.B. handing out a mutable iterator into summands_ invalidates the
// memoized hash, regardless of which end of the range it points at
// (`*(--end())` mutates just as `*begin()` does)
if (!summands_.empty()) {
reset_hash_value();
}
return ExprIterator{summands_.data() + summands_.size()};
}
ConstExprIterator Sum::begin_subexpr() const {
return ConstExprIterator{summands_.data()};
}
ConstExprIterator Sum::end_subexpr() const {
return ConstExprIterator{summands_.data() + summands_.size()};
}
Expr::hash_type Sum::memoizing_hash() const {
auto compute_hash = [this]() {
if (summands_.size() == 1)
return summands_[0]->hash_value();
else {
auto deref_summands =
summands() |
ranges::views::transform(
[](const ExprPtr &ptr) -> const Expr & { return *ptr; });
auto value = hash::range(ranges::begin(deref_summands),
ranges::end(deref_summands));
return value;
}
};
if (!hash_value_) {
hash_value_ = compute_hash();
} else {
SEQUANT_ASSERT(*hash_value_ == compute_hash());
}
return *hash_value_;
}
ExprPtr Sum::canonicalize(CanonicalizeOptions opt) {
return canonicalize_impl(true, opt);
}
ExprPtr Sum::rapid_canonicalize(CanonicalizeOptions opts) {
SEQUANT_ASSERT(opts.method == CanonicalizationMethod::Rapid);
return canonicalize_impl(false, opts);
}
bool Sum::static_equal(const Expr &that) const {
const auto &that_cast = static_cast<const Sum &>(that);
if (summands().size() == that_cast.summands().size()) {
if (this->empty()) return true;
// compare hash values first
if (this->hash_value() ==
that.hash_value()) // hash values agree -> do full comparison
return std::equal(begin_subexpr(), end_subexpr(), that.begin_subexpr(),
expr_ptr_comparer);
else
return false;
} else
return false;
}
HashingAccumulator &HashingAccumulator::append(ExprPtr summand, bool flatten) {
// flatten, if needed
if (flatten && summand.is<Sum>()) {
for (auto &subsummand : summand.as<Sum>().summands()) {
this->append(subsummand, flatten);
}
return *this;
}
// process summand as a whole
auto it = summands_.find(summand);
if (it == summands_.end()) {
summands_.emplace(summand);
} else { // found existing term with the same hash
auto existing_summand = *it;
if (summand.template is<Product>()) {
if (existing_summand.is<Product>()) {
// both are products - add them
existing_summand.as<Product>().add_identical(
summand.template as<Product>());
} else {
// convert existing term to product and add
auto product_copy = std::make_shared<Product>(summand->clone());
product_copy->add_identical(existing_summand);
summands_.erase(it);
summands_.emplace(std::move(product_copy));
}
} else {
if (existing_summand.is<Product>()) {
existing_summand.as<Product>().add_identical(summand);
} else {
// neither is a product - create new product
auto product_form = std::make_shared<Product>();
product_form->append(2, summand.template as<Expr>());
summands_.erase(it);
summands_.emplace(std::move(product_form));
}
}
}
return *this;
}
SumPtr HashingAccumulator::make_sum_impl(bool canonicalize) {
Sum::summands_type summands;
summands.reserve(summands_.size());
for (auto summand : summands_) {
if (!summand->is_zero()) {
summands.push_back(summand);
}
}
if (canonicalize) {
ranges::sort(summands, [](const auto &e1, const auto &e2) {
if (e1->hash_value() == e2->hash_value()) {
return e1 < e2;
} else {
return e1->hash_value() < e2->hash_value();
}
});
}
return std::make_shared<Sum>(std::move(summands), Sum::move_only_tag{});
}
SumPtr HashingAccumulator::make_sum() { return make_sum_impl(false); }
SumPtr HashingAccumulator::make_canonicalized_sum() {
return make_sum_impl(true);
}
ExprPtr HashingAccumulator::make_expr(bool canonicalize) {
if (summands_.size() == 0) {
return ex<Constant>(0);
} else if (summands_.size() == 1)
return *(summands_.begin());
else
return make_sum_impl(canonicalize);
}
bool HashingAccumulator::empty() const { return summands_.empty(); }
} // namespace sequant