dlinear  0.0.1
Delta-complete SMT solver for linear programming
Loading...
Searching...
No Matches
BoundImplicator.cpp
1
6#include "BoundImplicator.h"
7
8#include <ranges>
9
10#include "dlinear/util/exception.h"
11
12namespace dlinear {
13
14BoundImplicator::BoundImplicator(const Config& config, std::function<void(const Formula&)> assert,
15 const PredicateAbstractor& predicate_abstractor)
16 : config_{config}, assert_{std::move(assert)}, predicate_abstractor_{predicate_abstractor} {}
17
18template <>
19void BoundImplicator::AddAssertion<1>(const Formula& assertion) {
20 DLINEAR_ASSERT(assertion.GetFreeVariables().size() == 1u, "Assertion must have exactly one free variable");
21 DLINEAR_ASSERT(is_relational(assertion), "Assertion must be relational");
22 const Expression& lhs = get_lhs_expression(assertion);
23 const Expression& rhs = get_rhs_expression(assertion);
24 DLINEAR_ASSERT((is_variable(lhs) || is_multiplication(lhs)) && is_constant(rhs),
25 "lhs must be a variable or multiplication and rhs must be a constant");
26
27 const mpq_class& constant = get_constant_value(rhs);
28 const LpRowSense row_sense = parseLpSense(assertion);
29 const Variable* bool_var = &predicate_abstractor_[assertion];
30
31 if (row_sense == LpRowSense::NQ) return; // It is pointless to add nq constraints
32
33 if (is_variable(lhs)) {
34 const Variable& var = get_variable(lhs);
35 constraints_[var].emplace(constant, row_sense, bool_var);
36 } else {
37 DLINEAR_ASSERT(is_multiplication(lhs), "lhs must be a multiplication");
38 const auto& base_to_exponent_map = get_base_to_exponent_map_in_multiplication(lhs);
39 DLINEAR_ASSERT(base_to_exponent_map.size() == 1, "lhs must have exactly one base");
40 DLINEAR_ASSERT(is_variable(base_to_exponent_map.begin()->first), "lhs' base must be a variable");
41 const Variable& var = get_variable(base_to_exponent_map.begin()->first);
42 DLINEAR_ASSERT(is_variable(var), "lhs base must be a variable");
43 const mpq_class& coeff = get_constant_in_multiplication(lhs);
44 constraints_[var].emplace(constant / coeff, row_sense, bool_var);
45 }
46}
47
50 "Method Propagate should not be called with a frequency of NEVER");
51 for (const auto& [var, assertion] : predicate_abstractor_.var_to_formula_map()) {
52 if (!is_relational(assertion)) {
53 fmt::println("Assertion must be relational. Skipping.");
54 continue;
55 }
56 switch (assertion.GetFreeVariables().size()) {
57 case 1: {
58 AddAssertion<1>(assertion);
59 break;
60 }
61 default:
62 break;
63 }
64 }
66}
67
69 for (const auto& [var, constraints] : constraints_) {
70 if (constraints.size() <= 1) continue;
71 // Propagate simple < and <= constraints
72 // Iterate over the array in order (i.e. [ < = <= ] )
73 // Then (<) implies (<=), (<) implies (not =), a smaller constraint implies a greater constraint
74 // E.g. x < 1 => x <= 1, x < 1 => not (x = 1), x <= 2 => x < 3, x <= 2 => not (x = 3)
75 const Constraint* last_l_constraint = nullptr;
76 for (const Constraint& constraint : constraints) {
77 const auto& [value, sense, bool_var] = constraint;
78 if (sense == LpRowSense::LE || sense == LpRowSense::LT) {
79 if (last_l_constraint != nullptr) {
80 DLINEAR_TRACE_FMT("BoundImplicator::PropagateAssertions: {} => {}",
81 predicate_abstractor_[*last_l_constraint->variable], predicate_abstractor_[*bool_var]);
82 assert_(imply(*last_l_constraint->variable, *bool_var));
83 }
84 last_l_constraint = &constraint;
85 continue;
86 }
87 DLINEAR_ASSERT(sense == LpRowSense::EQ, "Unexpected sense");
88 if (last_l_constraint == nullptr) continue;
89 assert_(imply(*last_l_constraint->variable, !(*bool_var)));
90 DLINEAR_TRACE_FMT("BoundImplicator::PropagateAssertions: {} => {}",
91 predicate_abstractor_[*last_l_constraint->variable], !predicate_abstractor_[*bool_var]);
92 }
93 // Propagate simple > and >= constraints
94 // Iterate over the array in reverse order (i.e. [ > = >= ] )
95 // Then (>) implies (>=), (>) implies (not =), a greater constraint implies a lesser constraint
96 // E.g. x > 1 => x >= 1, x > 1 => not (x = 1), x >= 2 => x > 1, x >= 2 => not (x = 1)
97 const Constraint* last_g_constraint = nullptr;
98 for (const Constraint& constraint : std::views::reverse(constraints)) {
99 const auto& [value, sense, bool_var] = constraint;
100 if (sense == LpRowSense::LE || sense == LpRowSense::LT) {
101 if (last_g_constraint != nullptr) {
102 DLINEAR_TRACE_FMT("BoundImplicator::PropagateAssertions: {} => {}",
103 !predicate_abstractor_[*last_g_constraint->variable], !predicate_abstractor_[*bool_var]);
104 assert_(imply(!*last_g_constraint->variable, !*bool_var));
105 }
106 last_g_constraint = &constraint;
107 continue;
108 }
109 DLINEAR_ASSERT(sense == LpRowSense::EQ, "Unexpected sense");
110 if (last_g_constraint == nullptr) continue;
111 assert_(imply(!*last_g_constraint->variable, !(*bool_var)));
112 DLINEAR_TRACE_FMT("BoundImplicator::PropagateAssertions: {} => {}",
113 !predicate_abstractor_[*last_g_constraint->variable], !predicate_abstractor_[*bool_var]);
114 }
115 // Propagate simple = constraints
116 // Iterate over the array and make so two different consecutive = constraints cannot be true at the same time
117 // Note that this is not complete, since that would require a quadratic number of assertions
118 // E.g. not (x = 1) or not (x = 2), not (x = 2) or not (x = 4)
119 const Constraint* last_eq_constraint = nullptr;
120 for (const Constraint& constraint : constraints) {
121 const auto& [value, sense, bool_var] = constraint;
122 if (sense != LpRowSense::EQ) continue;
123 if (last_eq_constraint != nullptr) {
124 if (value == last_eq_constraint->value) {
125 DLINEAR_TRACE_FMT("BoundImplicator::PropagateAssertions: {} <=> {}",
126 predicate_abstractor_[*last_eq_constraint->variable], predicate_abstractor_[*bool_var]);
127 assert_(iff(*last_eq_constraint->variable, *bool_var));
128 } else {
129 assert_(!*last_eq_constraint->variable || !*bool_var);
130 DLINEAR_TRACE_FMT("BoundImplicator::PropagateAssertions: {} || {}",
131 predicate_abstractor_[*last_eq_constraint->variable], predicate_abstractor_[*bool_var]);
132 }
133 }
134 last_eq_constraint = &constraint;
135 }
136 }
137}
138
139bool BoundImplicator::Constraint::operator==(const BoundImplicator::Constraint& o) const {
140 return value == o.value && row_sense == o.row_sense && variable->equal_to(*o.variable);
141}
142std::strong_ordering BoundImplicator::Constraint::operator<=>(const BoundImplicator::Constraint& o) const {
143 const auto& [value_l, type_l, bool_var_l] = *this;
144 const auto& [value_r, type_r, bool_var_r] = o;
145 if (value_l < value_r) return std::strong_ordering::less;
146 if (value_l > value_r) return std::strong_ordering::greater;
147 if (type_l < type_r) return std::strong_ordering::less;
148 if (type_l > type_r) return std::strong_ordering::greater;
149 if (bool_var_l->less(*bool_var_r)) return std::strong_ordering::less;
150 if (bool_var_l->equal_to(*bool_var_r)) return std::strong_ordering::equal;
151 return std::strong_ordering::greater;
152}
153
154} // namespace dlinear
void PropagateAssertions()
Propagate the sorted constraints by adding them to the context by calling the assert_ function.
const PredicateAbstractor & predicate_abstractor_
Predicate abstractor.
void Propagate()
Use theory reasoning to propagate some simple theory inferences.
std::map< Variable, SortedVector< Constraint > > constraints_
Map of constraints for each variable.
BoundImplicator(const Config &config, std::function< void(const Formula &)> assert, const PredicateAbstractor &predicate_abstractor)
Construct a BoundImplicator.
const Config & config_
Configuration.
void AddAssertion(const Formula &assertion)
Add an assertion to the constraints_ map.
const std::function< void(const Formula &)> assert_
Assertion function. It adds assertions to the context.
Simple dataclass used to store the configuration of the program.
Definition Config.h:38
PreprocessingRunningFrequency actual_bound_implication_frequency() const
Get read-only access to the actual bound_implication_frequency parameter of the configuration.
Definition Config.cpp:82
@ NEVER
Never run this preprocess, effectively disabling it.
Predicate abstraction is a method to convert a first-order logic formula into a Boolean formula.
const std::unordered_map< Variable, Formula > & var_to_formula_map() const
Get read-only access to the map of previously converted formulae to variable of the PredicateAbstract...
Represents a symbolic form of an expression.
Represents a symbolic form of a first-order logic formula.
const Variables & GetFreeVariables() const
Gets free variables (unquantified variables).
Represents a symbolic variable.
bool equal_to(const Variable &v) const
Checks the equality of two variables based on their ID values.
size_type size() const
Returns the number of elements.
Global namespace for the dlinear library.
Formula iff(const Formula &f1, const Formula &f2)
Return a formula f1 ⇔ f2.
Definition symbolic.cpp:39
Formula imply(const Formula &f1, const Formula &f2)
Return a formula f1 ⇒ f2.
Definition symbolic.cpp:34
LpRowSense parseLpSense(const Formula &f)
Parse the sense from a formula.
LpRowSense
Sense of a linear programming row describing a constraint.
Definition LpRowSense.h:23
@ NQ
Not equal to.
@ LE
Less than or equal to.
STL namespace.
mpq_class value
Value of the constraint.
LpRowSense row_sense
Type of the constraint (e.g. <=, <, =, >, >=)
const Variable * variable
Boolean variable encoding the constraint.