delpi  0.0.1
DElta-complete LP solver
Loading...
Searching...
No Matches
ExpressionCell.cpp
1
6#include "delpi/symbolic/ExpressionCell.h"
7
8#include <map>
9#include <unordered_map>
10#include <utility>
11#include <vector>
12
13#include "delpi/util/error.h"
14#include "delpi/util/hash.hpp"
15
16namespace delpi {
17intrusive_ptr<ExpressionCell> ExpressionCell::New() { return intrusive_ptr(new ExpressionCell()); }
18intrusive_ptr<ExpressionCell> ExpressionCell::New(Variable var) {
19 return intrusive_ptr(new ExpressionCell{std::move(var)});
20}
21intrusive_ptr<ExpressionCell> ExpressionCell::New(Expression::Addend linear_monomial) {
22 return intrusive_ptr(new ExpressionCell{std::move(linear_monomial)});
23}
24intrusive_ptr<ExpressionCell> ExpressionCell::New(Addends addends) {
25 return intrusive_ptr(new ExpressionCell{std::move(addends)});
26}
27intrusive_ptr<ExpressionCell> ExpressionCell::Copy(const ExpressionCell& o) {
28 return {intrusive_ptr(new ExpressionCell{o.addends_})};
29}
30
31ExpressionCell::ExpressionCell(Variable var) : hash_{0} { addends_.emplace(std::move(var), 1); }
32ExpressionCell::ExpressionCell(Addend linear_monomial) : hash_{0} {
33 addends_.emplace(std::move(linear_monomial.first), std::move(linear_monomial.second));
34}
35ExpressionCell::ExpressionCell(Addends addends) : hash_{0}, addends_{std::move(addends)} {}
36
37std::vector<Variable> ExpressionCell::variables() const {
38 std::vector<Variable> vars;
39 vars.reserve(addends_.size());
40 for (auto& [var, coeff] : addends_) vars.emplace_back(var);
41 return vars;
42}
43
44bool ExpressionCell::equal_to(const ExpressionCell& o) const noexcept {
45 if (this == &o) return true;
46 return std::ranges::equal(
47 addends_, o.addends_,
48 [](const std::pair<const Variable, mpq_class>& p1, const std::pair<const Variable, mpq_class>& p2) {
49 return p1.first.equal_to(p2.first) && p1.second == p2.second;
50 });
51}
52bool ExpressionCell::less(const ExpressionCell& o) const noexcept {
53 // Compare the two maps.
54 if (this == &o) return false;
55 return std::ranges::lexicographical_compare(
56 addends_, o.addends_,
57 [](const std::pair<const Variable, mpq_class>& p1, const std::pair<const Variable, mpq_class>& p2) {
58 const auto& [var1, val1] = p1;
59 const auto& [var2, val2] = p2;
60 if (var1.less(var2)) return true;
61 if (var2.less(var1)) return false;
62 return val1 < val2;
63 });
64}
65std::size_t ExpressionCell::hash() const noexcept {
66 if (hash_ == 0) hash_ = hash::hash_value<Addends>{}(addends_);
67 return hash_;
68}
69
70ExpressionCell& ExpressionCell::Add(const Variable& var, const mpq_class& coeff) {
71 if (coeff == 0) return *this;
72 hash_ = 0;
73 if (const auto it = addends_.find(var); addends_.end() == it) {
74 addends_.emplace(var, coeff);
75 } else {
76 mpq_class new_coeff{coeff + it->second};
77 if (0 == new_coeff) {
78 addends_.erase(it);
79 } else {
80 it->second = std::move(new_coeff);
81 }
82 }
83 return *this;
84}
85ExpressionCell& ExpressionCell::Multiply(const mpq_class& coeff) {
86 if (coeff == 1) return *this;
87 hash_ = 0;
88 if (coeff == 0) addends_.clear();
89 for (auto& it : addends_) it.second *= coeff;
90 return *this;
91}
92ExpressionCell& ExpressionCell::Divide(const mpq_class& coeff) {
93 if (coeff == 1) return *this;
94 if (coeff == 0) DELPI_RUNTIME_ERROR("Division by 0");
95 hash_ = 0;
96 for (auto& it : addends_) it.second /= coeff;
97 return *this;
98}
99
100template <MapFromTo<Variable, mpq_class> T>
101mpq_class ExpressionCell::Evaluate(const T& env) const {
102 return std::accumulate(addends_.begin(), addends_.end(), mpq_class{0},
103 [&env](const mpq_class& init, const std::pair<const Variable, mpq_class>& p) {
104 // Without the cast, it would return an expression template
105 return static_cast<mpq_class>(init + env.at(p.first) * p.second);
106 });
107}
108Expression ExpressionCell::Substitute(const SubstitutionMap& s) const {
109 Expression ret{};
110 for (const auto& [var, coeff] : addends_) {
111 ret.Add(s.contains(var) ? s.at(var) : var, coeff);
112 }
113 return ret;
114}
115
116std::ostream& ExpressionCell::Print(std::ostream& os) const {
117 bool print_plus{false};
118 os << "(";
119 for (auto& [var, coeff] : addends_) {
120 PrintAddend(os, print_plus, var, coeff);
121 print_plus = true;
122 }
123 os << ")";
124 return os;
125}
126std::ostream& ExpressionCell::PrintAddend(std::ostream& os, const bool print_plus, const Variable& var,
127 const mpq_class& coeff) {
128 if (coeff > 0.0) {
129 if (print_plus) {
130 os << " + ";
131 }
132 // Do not print "1 * t"
133 if (coeff != 1.0) {
134 os << coeff << " * ";
135 }
136 } else {
137 // Instead of printing "+ (- E)", just print "- E".
138 os << " - ";
139 if (coeff != -1.0) {
140 os << (-coeff) << " * ";
141 }
142 }
143 return os << var;
144}
145
146template mpq_class ExpressionCell::Evaluate(const std::map<Variable, mpq_class>& env) const;
147template mpq_class ExpressionCell::Evaluate(const std::unordered_map<Variable, mpq_class>& env) const;
148
149} // namespace delpi
Symbolic expression representing an addition between linear monomials.
std::size_t hash_
Cached hash of the object.
ExpressionCell & Add(const Variable &var, const mpq_class &coeff)
Add a linear monomial , where is a constant and is a Variable, to the current expression.
mpq_class Evaluate(const T &env={}) const
Evaluates using a given environment (by default, an empty environment).
Expression::Addends addends_
Map between each variable and it coefficient as terms of the summation.
ExpressionCell & Divide(const mpq_class &coeff)
Divide all terms of the summation by a coeff.
ExpressionCell & Multiply(const mpq_class &coeff)
Multiply all terms of the summation by a coeff.
Expression Substitute(const SubstitutionMap &s) const
Create a copy of this expression, replacing all occurrences of the variables in s with corresponding ...
Represents a symbolic form of an expression.
Definition Expression.h:37
Expression & Add(const Variable &var, const mpq_class &coeff)
Add a linear monomial , where is a constant and is a Variable, to the current expression.
Real symbolic variable.
Definition Variable.h:20
Pointer to a generic object that supports intrusive reference counting.
Global namespace for the delpi library.