SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
ConflictGenerator.h
Go to the documentation of this file.
1 #pragma once
2 
4 
5 namespace smtrat {
6 namespace mcsat {
7 /**
8  * @brief A Fourier-Motzkin based backend
9  *
10  * Preprocessing of constraints:
11  *
12  * The input is a constraint c: p*x~q which can be used as a bound on x with p,q multivariate polynomials.
13  * If x only occurs linearly in c, this decomposition is possible.
14  * If p is zero, then c is conflicting iff !(0~q). If this is the case, we can return (c && p=0) -> 0~q as explanation.
15  * Otherwise, we evaluate c over the partial model and obtain x~r, where r is a rational.
16  * To properly perform the elimination step detailed below, we additionally store whether p is negative over the current assignment as a Boolean.
17  *
18  * We store (c,p,q,r,n) for each bound.
19  *
20  *
21  * FM elimination:
22  *
23  * Given a lower bound l and an upper bound u, the elimination is as follows:
24  * Conflict if l.r >= u.r (or strict, if both relations from c are weak)
25  * l.q * u.p < u.q * l.p
26  *
27  * If exactly one of u.p and l.p was found to be negative, the relation has to be inverted.
28  * If u.p or l.p are not constants, we additionally have to add a literal stating that their sign does not change.
29  *
30  * For all bounds involved, we add b.p < 0 resp. b.p > 0 as side condition to the explanation.
31  *
32  *
33  * Handling of "not equal":
34  *
35  * For linear arithmetic, a bound i belonging to a constraint with relation != can be in conflict with
36  * * a bound e for a constraint with = iff i.r == e.r, then we return i.c && e.c -> i.q * e.p != e.q * i.p
37  * * two bounds l, u with >= resp. <= as relation and i.r == l.r == u.r, then we return i.c && l.c && u.c && (l.q * u.p = u.q * l.p) -> l.q * i.p != i.q * l.p
38  *
39  * For all bounds b involved, we add b.p != 0 as side condition to the explanation.
40  */
41 namespace fm {
42 
43 
44 using carl::operator<<;
45 
46 inline bool isSubset(const carl::Variables& subset, const carl::Variables& superset) {
47  return std::includes(superset.begin(), superset.end(), subset.begin(), subset.end());
48 }
49 
50 struct Bound {
55  bool neg;
57  friend std::ostream& operator<<(std::ostream& os, const Bound& dt);
58 };
59 
60 inline std::ostream& operator<<(std::ostream& os, const Bound& b) {
61  os << "(" << b.constr << ", " << b.p << ", " << b.q << ", " << b.r << ", " << b.neg << ")";
62  return os;
63 }
64 
65 template<class Comparator>
67 
68  #define mcsat_yield(callback, result) if (callback(std::move(result))) { return; }
69 
70 
71 private:
72  const std::vector<ConstraintT>& mBounds;
73  const Model& mModel;
74  carl::Variable mVariable;
75 
76  Comparator comparator;
77 
78 public:
79  ConflictGenerator(const std::vector<ConstraintT>& bounds, const Model& m, carl::Variable v) : mBounds(bounds), mModel(m), mVariable(v) {}
80 
81 private:
83  ConstraintT res = ConstraintT(b.p, carl::Relation::NEQ);
84  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Side condition on " << b.p << ": " << res);
85  return res;
86  }
87 
89  ConstraintT res = ConstraintT(b.p, b.neg ? carl::Relation::LESS : carl::Relation::GREATER);
90  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Side condition on " << b.p << ": " << res);
91  return res;
92  }
93 
94  FormulasT conflictLowerAndUpperBound(const Bound& lower, const Bound& upper) {
95  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Lower bound " << lower << " in conflict with upper bound " << upper);
96  bool strict = carl::is_strict(lower.constr.relation()) || carl::is_strict(upper.constr.relation());
97  carl::Relation rel = (lower.neg xor upper.neg) ? (strict ? carl::Relation::GREATER : carl::Relation::GEQ) : (strict ? carl::Relation::LESS : carl::Relation::LEQ);
98  FormulasT res;
99  res.emplace_back(lower.constr.negation());
100  res.emplace_back(upper.constr.negation());
101  res.emplace_back(ConstraintT(lower.q*upper.p - upper.q*lower.p, rel));
102  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Conflict: " << lower.q << " * " << upper.p << " " << rel << " " << upper.q << " * " << lower.p);
103  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "-> " << res.back());
104  res.emplace_back(sideConditionLoUp(lower).negation());
105  res.emplace_back(sideConditionLoUp(upper).negation());
106  return res;
107  }
108 
110  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Equality " << eq << " in conflict with inqequality " << ineq);
111  FormulasT expl;
112  expl.emplace_back(ineq.constr.negation());
113  expl.emplace_back(eq.constr.negation());
114  expl.emplace_back(ConstraintT(eq.q*ineq.p - ineq.q*eq.p, carl::Relation::NEQ));
115  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Explanation: " << expl[0].negated() << " && " << expl[1].negated() << " -> " << expl[2]);
116  expl.emplace_back(sideCondition(eq).negation());
117  expl.emplace_back(sideCondition(ineq).negation());
118  return expl;
119  }
120 
121  FormulasT conflictInequalitiesAndInequality(const Bound& lower, const Bound& upper, const Bound& ineq) {
122  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Lower bound " << lower << " and upper bound " << upper << " in conflict with inqequality " << ineq);
123  FormulasT expl;
124  expl.emplace_back(ineq.constr.negation());
125  expl.emplace_back(lower.constr.negation());
126  expl.emplace_back(upper.constr.negation());
127  expl.emplace_back(ConstraintT(lower.q*upper.p - upper.q*lower.p, carl::Relation::EQ).negation());
128  expl.emplace_back(ConstraintT(lower.q*ineq.p - ineq.q*lower.p, carl::Relation::NEQ));
129  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Explanation: " << expl[0].negated() << " && " << expl[1].negated() << " && " << expl[2].negated() << " && " << expl[3].negated() << " -> " << expl[4]);
130  expl.emplace_back(sideCondition(ineq).negation());
131  expl.emplace_back(sideConditionLoUp(lower).negation());
132  expl.emplace_back(sideConditionLoUp(upper).negation());
133  return expl;
134  }
135 
136 public:
137 
138  template<typename Callback>
139  void generateExplanation(Callback&& callback) {
140  std::vector<Bound> mLower;
141  std::vector<Bound> mUpper;
142  std::multimap<Rational, Bound> mInequalities;
143  std::vector<Bound> mEqualities;
144  std::vector<std::pair<Bound, Bound>> mBoundPair;
145 
146  // initialize bounds
147  for (const auto& b: mBounds) {
148  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Processing bound " << b);
149 
150  if (!b.variables().has(mVariable)) {
151  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Discarding bound " << b << " because it does not contain " << mVariable);
152  continue;
153  }
154 
155  if (b.var_info(mVariable).max_degree() > 1) {
156  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Discarding bound " << b << " because " << mVariable << " occurs nonlinearly");
157  continue;
158  }
159  auto p = b.coefficient(mVariable, 1);
160  if (carl::is_zero(p)) {
161  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Discarding bound " << b << " because it does not contain " << mVariable);
162  continue;
163  }
164 
165  auto evalp = carl::evaluate(p, mModel);
166  if (!evalp.isRational()) {
167  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Discarding bound " << b << " because " << p << " did not evaluate to a rational");
168  continue;
169  }
170  assert(evalp.isRational());
171 
172  auto q = p * mVariable - b.lhs();
173  auto evalq = carl::evaluate(q, mModel);
174  if (!evalq.isRational()) {
175  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Discarding bound " << b << " because " << q << " did not evaluate to a rational");
176  continue;
177  }
178  assert(evalq.isRational());
179 
180  if (carl::is_zero(evalp.asRational())) {
181  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Discarding bound " << b << " because it does not contain " << mVariable << " after we evaluate it");
182 
183  if (!carl::evaluate(Rational(0), b.relation(), evalq.asRational())) {
184  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Bound " << b << " unsat because p is zero and q does not comply");
185  FormulasT expl;
186  expl.emplace_back(b.negation());
187  expl.emplace_back(ConstraintT(p, carl::Relation::EQ).negation());
188  expl.emplace_back(ConstraintT(-q, b.relation()));
189 
190  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Explanation: " << expl[0].negated() << " && " << expl[1].negated() << " -> " << expl[2]);
191  mcsat_yield(callback, expl);
192  }
193 
194  continue;
195  }
196  bool negated = evalp.asRational() < 0;
197 
198  Rational r = evalq.asRational() / evalp.asRational();
199 
200  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Considering bound " << b << " for " << mVariable);
201  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Decomposed into " << p << " * " << mVariable << " ~ " << q << ", " << mVariable << " ~ " << r);
202  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Coefficient is " << evalp.asRational());
203 
204  switch (b.relation()) {
205  case carl::Relation::LESS:
206  case carl::Relation::LEQ:
207  if (negated) {
208  mLower.emplace_back(b, p, q, r, negated);
209  } else {
210  mUpper.emplace_back(b, p, q, r, negated);
211  }
212  break;
213  case carl::Relation::EQ:
214  mLower.emplace_back(b, p, q, r, negated);
215  mUpper.emplace_back(b, p, q, r, negated);
216  mEqualities.emplace_back(b, p, q, r, negated);
217  break;
218  case carl::Relation::NEQ:
219  mInequalities.emplace(r, Bound(b, p, q, r, negated));
220  break;
221  case carl::Relation::GEQ:
222  case carl::Relation::GREATER:
223  if (negated) {
224  mUpper.emplace_back(b, p, q, r, negated);
225  } else {
226  mLower.emplace_back(b, p, q, r, negated);
227  }
228  break;
229  }
230  }
231  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Lower: " << mLower);
232  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Upper: " << mUpper);
233  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Inequ: " << mInequalities);
234 
235  // look for FM constraints
236  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Looking for conflicts between lower and upper bounds");
237 
238  std::sort(mLower.begin(), mLower.end(), comparator);
239  if (comparator.symmetric) {
240  std::sort(mUpper.rbegin(), mUpper.rend(), comparator);
241  }
242  else {
243  std::sort(mUpper.begin(), mUpper.end(), comparator);
244  }
245 
246  for (const Bound& lower : mLower) {
247  for (const Bound& upper : mUpper) {
248  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Combining " << lower << " and " << upper);
249  bool strict = carl::is_strict(lower.constr.relation()) || carl::is_strict(upper.constr.relation());
250 
251  if (lower.r < upper.r) {
252  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Not in conflict");
253  continue;
254  }
255 
256  if (lower.r == upper.r && !strict) {
257  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Not in conflict");
258  mBoundPair.push_back(std::make_pair(lower, upper));
259  continue;
260  }
261 
262  mcsat_yield(callback, conflictLowerAndUpperBound(lower, upper));
263  }
264  }
265 
266  // handle inequalities
267  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Looking for conflicts with inequalities");
268 
269  for (const auto& eq : mEqualities) {
270  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Considering equality " << eq.constr);
271  auto it = mInequalities.find(eq.r);
272  if (it != mInequalities.end()) {
273  mcsat_yield(callback, conflictEqualityAndInequality(eq, it->second));
274  }
275  }
276 
277  for (const auto& bounds : mBoundPair) {
278  SMTRAT_LOG_DEBUG("smtrat.mcsat.fm", "Considering lower bound " << bounds.first << " and upper bound " << bounds.second);
279  auto it = mInequalities.find(bounds.first.r);
280  if (it != mInequalities.end()) {
281  mcsat_yield(callback, conflictInequalitiesAndInequality(bounds.first, bounds.second, it->second));
282  }
283  }
284  }
285 };
286 
287 /**
288  * Does not order anything.
289  */
291  bool symmetric = false;
292 
293  bool operator()(const Bound&, const Bound&) const {
294  return false;
295  }
296 };
297 
298 /**
299  * This heuristic chooses the explanation excluding the largest interval.
300  */
302  bool symmetric = true;
303 
304  bool operator()(const Bound& b1, const Bound& b2) const {
305  return b1.r < b2.r;
306  }
307 };
308 
309 /**
310  * This heuristic chooses the explanation excluding the smallest interval.
311  */
313  bool symmetric = true;
314 
315  bool operator()(const Bound& b1, const Bound& b2) const {
316  return b1.r > b2.r;
317  }
318 };
319 
320 /**
321  * This heuristic tries to minimize the number of variables occuring in the explanation.
322  * It is a 2-approximation to the lowest possible number of variables in an explanation.
323  */
325  bool symmetric = false;
326 
327  bool operator()(const Bound& b1, const Bound& b2) const {
328  return b1.constr.variables().size() < b2.constr.variables().size();
329  }
330 };
331 
332 }
333 }
334 }
#define mcsat_yield(callback, result)
void sort(T *array, int size, LessThan lt)
Definition: Sort.h:67
Rational evaluate(carl::Variable v, const Poly &p, const Rational &r)
Definition: utils.h:11
bool isSubset(const carl::Variables &subset, const carl::Variables &superset)
std::ostream & operator<<(std::ostream &os, const Bound &b)
bool includes(const VariableRange &superset, const carl::Variables &subset)
Class to create the formulas for axioms.
carl::Model< Rational, Poly > Model
Definition: model.h:13
carl::MultivariatePolynomial< Rational > Poly
Definition: types.h:25
carl::Constraint< Poly > ConstraintT
Definition: types.h:29
carl::Formulas< Poly > FormulasT
Definition: types.h:39
mpq_class Rational
Definition: types.h:19
#define SMTRAT_LOG_DEBUG(channel, msg)
Definition: logging.h:35
Bound(ConstraintT constr, Poly p, Poly q, Rational r, bool neg)
friend std::ostream & operator<<(std::ostream &os, const Bound &dt)
FormulasT conflictEqualityAndInequality(const Bound &eq, const Bound &ineq)
ConstraintT sideCondition(const Bound &b)
FormulasT conflictInequalitiesAndInequality(const Bound &lower, const Bound &upper, const Bound &ineq)
const std::vector< ConstraintT > & mBounds
ConflictGenerator(const std::vector< ConstraintT > &bounds, const Model &m, carl::Variable v)
ConstraintT sideConditionLoUp(const Bound &b)
void generateExplanation(Callback &&callback)
FormulasT conflictLowerAndUpperBound(const Bound &lower, const Bound &upper)
bool operator()(const Bound &, const Bound &) const
This heuristic chooses the explanation excluding the largest interval.
bool operator()(const Bound &b1, const Bound &b2) const
This heuristic chooses the explanation excluding the smallest interval.
bool operator()(const Bound &b1, const Bound &b2) const
This heuristic tries to minimize the number of variables occuring in the explanation.
bool operator()(const Bound &b1, const Bound &b2) const