Program Listing for File asy_cost.cpp¶
↰ Return to documentation for file (SeQuant/core/asy_cost.cpp)
#include <SeQuant/core/asy_cost.hpp>
#include <SeQuant/core/container.hpp>
#include <SeQuant/core/meta.hpp>
#include <SeQuant/core/rational.hpp>
#include <SeQuant/core/utility/string.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <range/v3/range/operations.hpp>
#include <range/v3/view/reverse.hpp>
#include <range/v3/view/tail.hpp>
#include <range/v3/view/zip.hpp>
#include <cmath>
#include <limits>
namespace sequant {
AsyCost::AsyCostEntry::AsyCostEntry()
: exponents_{}, prefactor_{0}, is_max_{false} {}
AsyCost::AsyCostEntry::AsyCostEntry(ExponentMap exponents, rational prefactor)
: exponents_{std::move(exponents)}, prefactor_{prefactor}, is_max_{false} {
for (auto it = exponents_.begin(); it != exponents_.end();) {
if (it->second == 0)
it = exponents_.erase(it);
else
++it;
}
if (prefactor_ == 0 || exponents_.empty()) {
exponents_.clear();
prefactor_ = 0;
}
}
AsyCost::AsyCostEntry AsyCost::AsyCostEntry::max() {
AsyCostEntry e;
e.is_max_ = true;
e.prefactor_ = std::numeric_limits<intmax_t>::max();
return e;
}
AsyCost::AsyCostEntry const &AsyCost::AsyCostEntry::zero() {
static AsyCostEntry const zero_cost{};
return zero_cost;
}
AsyCost::ExponentMap const &AsyCost::AsyCostEntry::exponents() const {
return exponents_;
}
rational AsyCost::AsyCostEntry::prefactor() const { return prefactor_; }
void AsyCost::AsyCostEntry::set_prefactor(rational n) const { prefactor_ = n; }
bool AsyCost::AsyCostEntry::is_zero() const {
return !is_max_ && exponents_.empty() && prefactor_ == 0;
}
bool AsyCost::AsyCostEntry::is_max() const { return is_max_; }
std::ostream &AsyCost::AsyCostEntry::stream_out_rational(std::ostream &os,
rational const &r) {
os << numerator(r);
if (denominator(r) != rational{1}) {
os << '/';
os << denominator(r);
}
return os;
}
std::string AsyCost::AsyCostEntry::text() const {
auto oss = std::ostringstream{};
if (is_max_) {
oss << "max";
} else if (is_zero()) {
oss << "zero";
} else {
auto abs_c = abs(prefactor_);
oss << (prefactor_ < abs_c ? "- " : "");
if (abs_c == 1) {
// do nothing
} else {
AsyCostEntry::stream_out_rational(oss, abs_c);
oss << "*";
}
// Spaces print in IndexSpace order (primarily by attr; base_key only
// breaks ties between reserved-attr spaces).
for (auto const &[space, exp] : exponents_) {
if (exp == 0) continue;
oss << toUtf8(space.base_key());
if (exp > 1) oss << "^" << exp;
}
}
return oss.str();
}
std::string AsyCost::AsyCostEntry::to_latex() const {
auto oss = std::ostringstream{};
if (is_max_) {
oss << "\\texttt{max}";
} else if (is_zero()) {
oss << "\\texttt{zero}";
} else {
auto abs_c = abs(prefactor_);
oss << (prefactor_ < abs_c ? "- " : "");
bool frac_mode = abs(denominator(prefactor_)) != 1;
if (!frac_mode && (abs_c != 1)) oss << numerator(prefactor_);
if (frac_mode) {
oss << "\\frac{" //
<< abs(numerator(prefactor_)) //
<< "}{" //
<< denominator(prefactor_) << "}";
}
for (auto const &[space, exp] : exponents_) {
if (exp == 0) continue;
oss << toUtf8(space.base_key());
if (exp > 1) {
oss << "^{" << exp << "}";
}
}
}
return oss.str();
}
bool AsyCost::AsyCostEntry::operator<(const AsyCost::AsyCostEntry &rhs) const {
// The max() sentinel is the greatest entry.
if (is_max_ != rhs.is_max_) return !is_max_;
// Order primarily by the sum of all exponents, i.e. the overall polynomial
// degree (worst-case scaling). When two entries share the same total degree,
// break the tie by comparing exponents space by space, from the greatest
// IndexSpace (highest priority) down to the least, using IndexSpace's own
// ordering: the first space whose exponents differ decides, and a larger
// exponent on a higher-priority space makes the entry larger. Entries with
// identical exponents on every space are equivalent.
auto exp_for = [](ExponentMap const &m, IndexSpace const &s) {
auto it = m.find(s);
return it == m.end() ? std::size_t{0} : it->second;
};
auto total_degree = [](ExponentMap const &m) {
std::size_t sum = 0;
for (auto const &kv : m) sum += kv.second;
return sum;
};
{
auto const l = total_degree(exponents_);
auto const r = total_degree(rhs.exponents_);
if (l != r) return l < r;
}
container::set<IndexSpace> spaces;
for (auto const &kv : exponents_) spaces.insert(kv.first);
for (auto const &kv : rhs.exponents_) spaces.insert(kv.first);
for (auto it = spaces.rbegin(); it != spaces.rend(); ++it) {
auto const l = exp_for(exponents_, *it);
auto const r = exp_for(rhs.exponents_, *it);
if (l != r) return l < r;
}
return false;
}
bool AsyCost::AsyCostEntry::operator==(const AsyCost::AsyCostEntry &rhs) const {
return is_max_ == rhs.is_max_ && exponents_ == rhs.exponents_;
}
bool AsyCost::AsyCostEntry::operator!=(const AsyCost::AsyCostEntry &rhs) const {
return !(*this == rhs);
}
AsyCost::AsyCost(AsyCostEntry c) {
if (!c.is_zero()) cost_.emplace(std::move(c));
}
AsyCost::AsyCost() : AsyCost{AsyCostEntry::zero()} {}
AsyCost::AsyCost(ExponentMap exponents, rational prefactor)
: AsyCost{AsyCostEntry{std::move(exponents), prefactor}} {}
double AsyCost::ops(ExtentMap const &extents) const {
double total = 0;
for (auto const &c : cost_) {
if (c.is_max()) return std::numeric_limits<double>::infinity();
double temp = 1;
for (auto const &[space, exp] : c.exponents()) {
auto it = extents.find(space);
auto const extent =
it != extents.end() ? it->second : space.approximate_size();
temp *= std::pow(static_cast<double>(extent), static_cast<double>(exp));
}
total += boost::numeric_cast<double>(c.prefactor()) * temp;
}
return total;
}
AsyCost const &AsyCost::max() {
static const AsyCost m = AsyCost{AsyCostEntry::max()};
return m;
}
AsyCost const &AsyCost::zero() {
static const AsyCost z = AsyCost{AsyCostEntry::zero()};
return z;
}
AsyCost &AsyCost::operator+=(AsyCost const &other) {
*this = *this + other;
return *this;
}
AsyCost &AsyCost::operator-=(AsyCost const &other) {
*this = *this - other;
return *this;
}
AsyCost operator+(AsyCost const &lhs, AsyCost const &rhs) {
auto sum = lhs;
auto &data = sum.cost_;
for (auto const &c : rhs.cost_) {
if (auto found = data.find(c); found != data.end()) {
found->set_prefactor(found->prefactor() + c.prefactor());
if (found->prefactor() == 0) data.erase(found);
} else {
data.emplace(c);
}
}
return sum;
}
AsyCost operator-(AsyCost const &lhs, AsyCost const &rhs) {
return lhs + (-1 * rhs);
}
AsyCost operator*(AsyCost const &cost, rational scale) {
auto ac = cost;
for (auto &c : ac.cost_) c.set_prefactor(c.prefactor() * scale);
return ac;
}
AsyCost operator*(rational scale, AsyCost const &cost) { return cost * scale; }
AsyCost operator/(AsyCost const &cost, rational scale) {
return cost * (1 / scale);
}
bool operator<(AsyCost const &lhs, AsyCost const &rhs) {
using ranges::views::reverse;
using ranges::views::zip;
for (auto &&[c1, c2] : reverse(zip(lhs.cost_, rhs.cost_))) {
if (c1 < c2)
return true;
else if (c1 == c2) {
if (c1.prefactor() < c2.prefactor())
return true;
else if (c1.prefactor() > c2.prefactor())
return false;
} else
return false;
}
return lhs.cost_.size() < rhs.cost_.size();
}
bool operator==(AsyCost const &lhs, AsyCost const &rhs) {
return lhs.cost_.size() == rhs.cost_.size() && !(lhs < rhs || rhs < lhs);
}
bool operator!=(AsyCost const &lhs, AsyCost const &rhs) {
return !(lhs == rhs);
}
bool operator>(AsyCost const &lhs, AsyCost const &rhs) {
return !(lhs < rhs || lhs == rhs);
}
bool operator<=(AsyCost const &lhs, AsyCost const &rhs) {
return lhs < rhs || lhs == rhs;
}
bool operator>=(AsyCost const &lhs, AsyCost const &rhs) {
return lhs > rhs || lhs == rhs;
}
std::wstring AsyCost::to_latex() const {
auto oss = std::wostringstream{};
if (cost_.empty())
oss << 0;
else {
//
// stream out in reverse so that more expensive terms appear first
auto rev = ranges::views::reverse(cost_);
oss << toUtf16(ranges::front(rev).to_latex());
for (auto &&c : ranges::views::tail(rev))
oss << L" + " << toUtf16(c.to_latex());
}
return oss.str();
}
std::string AsyCost::text() const {
if (*this == AsyCost::zero()) return "0";
std::ostringstream oss{};
// reversed so that more expensive terms appear first
auto rev = ranges::views::reverse(cost_);
oss << ranges::front(rev).text();
for (auto &&c : ranges::views::tail(rev)) oss << " + " << c.text();
return oss.str();
}
} // namespace sequant