SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
CADPreprocessor.cpp
Go to the documentation of this file.
1 #include "CADPreprocessor.h"
2 
3 #include <carl-arith/constraint/Substitution.h>
4 #include <carl-formula/formula/functions/Complexity.h>
5 
6 namespace smtrat::cad {
7 
9 
10 namespace preprocessor {
11 
12 inline std::size_t complexity(const std::vector<FormulaT>& origin) {
13  return std::accumulate(origin.begin(), origin.end(), static_cast<std::size_t>(0),
14  [](std::size_t i, const auto& f){ return i + carl::complexity(f); }
15  );
16 }
17 
19  for (auto it = mOrigins.begin(); it != mOrigins.end(); ) {
20  for (auto oit = it->second.begin(); oit != it->second.end(); ) {
21  auto keep = std::find(oit->begin(), oit->end(), f) == oit->end();
22  if (keep) ++oit;
23  else oit = it->second.erase(oit);
24  }
25  if (it->second.empty()) {
26  it = mOrigins.erase(it);
27  } else {
28  ++it;
29  }
30  }
31 }
32 
33 void Origins::add(const FormulaT& f, const std::vector<FormulaT>& origin) {
34  auto it = mOrigins.find(f);
35  if (it == mOrigins.end()) {
36  mOrigins.emplace(f, std::vector<std::vector<FormulaT>>({origin}));
37  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Adding new origins " << f << " -> " << origin);
38  return;
39  }
40  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Adding new origins " << f << " -> " << origin);
41  it->second.emplace_back(origin);
42  std::sort(it->second.begin(), it->second.end(),
43  [](const auto& a, const auto& b){
44  if (a.size() != b.size()) return a.size() < b.size();
45  return complexity(a) < complexity(b);
46  }
47  );
48 }
49 
50 void Origins::remove(const FormulaT& f) {
51  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Erasing " << f << " from Origins: " << mOrigins);
52  mOrigins.erase(f);
53  cleanOrigins(f);
54  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Result Origins: " << mOrigins);
55 }
56 
57 const std::vector<FormulaT>& Origins::get(const FormulaT& f) const {
58  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Looking for " << f << " in Origins: " << mOrigins);
59  auto it = mOrigins.find(f);
60  assert(it != mOrigins.end());
61  return it->second.front();
62 }
63 
64 bool Origins::resolve(std::set<FormulaT>& conflict) const {
65  bool didReplacement = false;
66  for (const auto& origins: mOrigins) {
67  const auto& c = origins.second.front();
68  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Considering origin " << origins.first << " -> " << c);
69  if (c.size() == 1 && c.front() == origins.first) {
70  continue;
71  }
72  if (conflict.erase(origins.first) > 0) {
73  conflict.insert(c.begin(), c.end());
74  didReplacement = true;
75  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Replaced " << origins.first << " by origins " << c);
76  }
77  }
78  return didReplacement;
79 }
80 
81 bool AssignmentCollector::extractValueAssignments(std::map<ConstraintT, ConstraintT>& constraints) {
82  bool foundAssignment = false;
83  for (auto it = constraints.begin(); it != constraints.end(); ) {
84  auto assignment = get_assignment(it->second);
85  if (assignment && mModel.find(assignment->first) == mModel.end()) {
86  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Newly extracted " << assignment->first << " = " << assignment->second);
87  mModel.emplace(assignment->first, assignment->second);
88  mReasons.emplace(assignment->first, it->first);
89  mConstraints.emplace(it->first, assignment->first);
90  it = constraints.erase(it);
91  foundAssignment = true;
92  } else {
93  SMTRAT_LOG_TRACE("smtrat.cad.pp", "No assignment from " << it->second);
94  ++it;
95  }
96  }
97  return foundAssignment;
98 }
99 bool AssignmentCollector::extractParametricAssignments(std::map<ConstraintT, ConstraintT>& constraints) {
100  bool foundAssignment = false;
101  for (auto it = constraints.begin(); it != constraints.end(); ) {
102  auto substitution = carl::get_substitution(it->second);
103  if (substitution && mModel.find(substitution->first) == mModel.end()) {
104  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Newly extracted " << substitution->first << " = " << substitution->second);
105  mModel.emplace(substitution->first, carl::createSubstitution<Rational,Poly,ModelPolynomialSubstitution>(substitution->second));
106  mReasons.emplace(substitution->first, it->first);
107  mConstraints.emplace(it->first, substitution->first);
108  it = constraints.erase(it);
109  foundAssignment = true;
110  break;
111  } else {
112  SMTRAT_LOG_TRACE("smtrat.cad.pp", "No assignment from " << it->second);
113  ++it;
114  }
115  }
116  return foundAssignment;
117 }
118 
119 bool AssignmentCollector::extractAssignments(std::map<ConstraintT, ConstraintT>& constraints) {
120  if (extractValueAssignments(constraints)) return true;
122 }
123 
124 AssignmentCollector::CollectionResult AssignmentCollector::simplify(std::map<ConstraintT, ConstraintT>& constraints) {
125  bool changed = false;
126  for (auto& c: constraints) {
127  auto tmp = carl::substitute(c.second, mModel);
128  if (tmp != c.second && constraints.find(tmp) == constraints.end()) {
129  changed = true;
130  c.second = tmp;
131  }
132  if (c.second.is_consistent() == 0) {
133  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Simplification found conflict in " << c.first << " (" << c.second << ")");
134  return c.first;
135  }
136  }
137  return changed;
138 }
139 
140 AssignmentCollector::CollectionResult AssignmentCollector::collect(std::map<ConstraintT, ConstraintT>& constraints) {
141  auto sres = simplify(constraints);
142  if (std::holds_alternative<ConstraintT>(sres)) {
143  return sres;
144  }
145  assert(std::holds_alternative<bool>(sres));
146  bool foundNew = std::get<bool>(sres);
147  bool continueSearch = true;
148  while (continueSearch) {
149  continueSearch = extractAssignments(constraints);
150  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Extracted assignments " << mModel << " from " << constraints);
151  auto sres = simplify(constraints);
152  if (std::holds_alternative<ConstraintT>(sres)) {
153  return sres;
154  }
155  assert(std::holds_alternative<bool>(sres));
156  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "After simplification with " << mModel << ": " << constraints);
157  foundNew = foundNew || continueSearch || std::get<bool>(sres);
158  }
159  return foundNew;
160 }
161 
162 bool ResultantRule::addPoly(const Poly& poly) {
163  if (is_zero(poly)) return true;
164  SMTRAT_LOG_TRACE("smtrat.cad.pp", "Adding poly " << poly << " under ordering " << mVars);
165  std::size_t level = 0;
166  UPoly p = carl::to_univariate_polynomial(poly, mVars[level]);
167  while (carl::is_constant(p)) {
168  ++level;
169  p = carl::to_univariate_polynomial(poly, mVars[level]);
170  }
171  SMTRAT_LOG_TRACE("smtrat.cad.pp", "Inserting " << p << " into level " << level);
172  SMTRAT_LOG_TRACE("smtrat.cad.pp", "Into " << mData);
173  mData[level].emplace_back(p);
174  return true;
175 }
176 
177 bool ResultantRule::addPoly(const UPoly& poly, std::size_t level, const std::vector<FormulaT>& origin) {
178  if (poly.is_number()) return true;
179  SMTRAT_LOG_TRACE("smtrat.cad.pp", "Adding poly " << poly << " under ordering " << mVars);
180  Poly mp(poly);
181  UPoly p = poly;
182  assert(p.main_var() == mVars[level]);
183  while (carl::is_constant(p)) {
184  ++level;
185  p = carl::to_univariate_polynomial(mp, mVars[level]);
186  }
187  SMTRAT_LOG_TRACE("smtrat.cad.pp", "Inserting " << p << " into level " << level);
188  SMTRAT_LOG_TRACE("smtrat.cad.pp", "Into " << mData);
189  if (std::find(mData[level].begin(), mData[level].end(), p) != mData[level].end()) {
190  return true;
191  }
192  mData[level].emplace_back(p);
194  mOrigins.add(FormulaT(mp, carl::Relation::EQ), origin);
195  SMTRAT_LOG_TRACE("smtrat.cad.pp", "Origins: " << mOrigins.mOrigins);
196  mNewECs.emplace_back(cons);
197  if (cons.is_consistent() == 0) return false;
198  return true;
199 }
200 
201 std::optional<std::vector<FormulaT>> ResultantRule::computeResultants(std::size_t level) {
202  for (std::size_t pid = 0; pid < mData[level].size(); ++pid) {
203  for (std::size_t qid = pid + 1; qid < mData[level].size(); ++qid) {
204  auto r = projection::resultant(mVars[level + 1], mData[level][pid], mData[level][qid]);
205  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Resultant(" << mData[level][pid] << ", " << mData[level][qid] << ") = " << r);
206  std::vector<FormulaT> origin;
207  const auto& op = mOrigins.get(FormulaT(Poly(mData[level][pid]), carl::Relation::EQ));
208  origin.insert(origin.end(), op.begin(), op.end());
209  const auto& oq = mOrigins.get(FormulaT(Poly(mData[level][qid]), carl::Relation::EQ));
210  origin.insert(origin.end(), oq.begin(), oq.end());
211  if (!addPoly(r, level + 1, origin)) {
212  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Found direct conflict due to " << origin);
213  return origin;
214  }
215  }
216  }
217  return std::nullopt;
218 }
219 
220 std::optional<std::vector<FormulaT>> ResultantRule::compute(const std::map<ConstraintT,ConstraintT>& constraints) {
221  mConstraints.clear();
222  mData.assign(mVars.size(), {});
223  mNewECs.clear();
224  for (const auto& c: constraints) {
225  mConstraints.emplace_back(c.first);
226  if (!addPoly(c.second.lhs())) {
227  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Found direct conflict due to " << c.first);
228  return std::vector<FormulaT>({ FormulaT(c.first) });
229  }
230  }
231 
232  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Computing resultants from" << std::endl << mData);
233  for (std::size_t level = 0; level < mData.size() - 1; ++level) {
234  auto conflict = computeResultants(level);
235  if (conflict) return conflict;
236  }
237  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Done.");
238  return std::nullopt;
239 }
240 
241 }
242 
244  mDerivedEqualities.clear();
245  mModel.clear();
246  mAssignments.reasons().clear();
247  mAssignments.constraints().clear();
248  for (auto& i: mInequalities) {
249  i.second = i.first;
250  }
251 }
252 
254  auto it = std::remove(mEqualities.begin(), mEqualities.end(), c);
255  mEqualities.erase(it, mEqualities.end());
256  resetCached();
257 }
258 
259 bool CADPreprocessor::addEqualities(const std::vector<ConstraintT>& constraints) {
260  bool addedNew = false;
261  for (const auto& c: constraints) {
262  if (mAssignments.constraints().find(c) != mAssignments.constraints().end()) continue;
263  if (mDerivedEqualities.try_emplace(c, c).second) {
264  addedNew = true;
265  }
266  }
267  return addedNew;
268 }
269 
270 std::vector<ConstraintT> CADPreprocessor::collectDerivedEqualities() const {
271  std::vector<ConstraintT> cur;
272  for (const auto& c: mDerivedEqualities) {
273  cur.emplace_back(c.second);
274  }
275  return cur;
276 }
277 
278 bool CADPreprocessor::collectOriginsOfConflict(std::set<FormulaT>& conflict, const std::map<ConstraintT, ConstraintT>& constraints) const {
279  bool didReplacement = false;
280  for (const auto& c: constraints) {
281  if (c.first == c.second) continue;
282  if (conflict.erase(FormulaT(c.second)) > 0) {
283  conflict.emplace(c.first);
284  didReplacement = true;
285  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Replaced " << c.second << " by origin " << c.first);
286  }
287  }
288  return didReplacement;
289 }
290 
291 bool CADPreprocessor::addModelToConflict(std::set<FormulaT>& conflict, carl::Variables& added) const {
292  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Adding necessary parts of model to conflict: " << conflict);
293  carl::carlVariables vars;
294  bool changed = false;
295  for (const auto& f: conflict) carl::variables(f,vars);
296  while (!vars.empty()) {
297  carl::carlVariables newvars;
298  for (auto v: vars) {
299  auto it = mAssignments.reasons().find(v);
300  if (it == mAssignments.reasons().end()) continue;
301  if (added.find(v) != added.end()) continue;
302  added.insert(v);
303  auto cit = conflict.emplace(it->second);
304  changed = true;
305  if (cit.second) {
306  carl::variables(*cit.first, newvars);
307  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Added " << it->second << " to conflict.");
308  }
309  }
310  vars = newvars;
311  }
312  return changed;
313 }
314 
316  if (c.relation() == carl::Relation::EQ) {
317  mEqualities.emplace_back(c);
318  } else {
319  mInequalities.emplace(c, c);
320  }
321  mOrigins.add(FormulaT(c), { FormulaT(c) });
322  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Origins: " << mOrigins.mOrigins);
323  SMTRAT_LOG_TRACE("smtrat.cad.pp", "Added " << c << " to " << std::endl << *this);
324 }
325 
327  if (c.relation() == carl::Relation::EQ) {
328  removeEquality(c);
329  } else {
330  auto it = mInequalities.find(c);
331  assert(it != mInequalities.end());
332  mInequalities.erase(it);
333  }
335  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Origins: " << mOrigins.mOrigins);
336  SMTRAT_LOG_TRACE("smtrat.cad.pp", "Removed " << c << " from " << std::endl << *this);
337 }
338 
340  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Starting with:" << std::endl << *this);
341  bool changed = addEqualities(mEqualities);
342  while (changed) {
343  changed = false;
344  if (settings_cadpp().disable_variable_elimination) {
345  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Variable elimination is disabled");
346  } else {
347  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Collecting assignments from:" << std::endl << *this);
348  auto collectResult = mAssignments.collect(mDerivedEqualities);
349  if (std::holds_alternative<ConstraintT>(collectResult)) {
350  mConflict = { FormulaT(std::get<ConstraintT>(collectResult)) };
351  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Immediate conflict due to " << *mConflict);
352  return false;
353  }
354  assert(std::holds_alternative<bool>(collectResult));
355  if (std::get<bool>(collectResult) == true) {
356  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Collected assignments:" << std::endl << *this);
357  for (const auto& de: mDerivedEqualities) {
358  if (mOrigins.mOrigins.find(FormulaT(de.second)) == mOrigins.mOrigins.end()) {
359  mOrigins.add(FormulaT(de.second), {FormulaT(de.first)});
360  }
361  }
362  changed = true;
363  }
364  }
365  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Origins are now:" << std::endl << mOrigins.mOrigins);
366 
367  if (settings_cadpp().disable_resultants) {
368  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Resultant rule is disabled");
369  } else {
370  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Computing Resultants from:" << std::endl << *this);
371  auto conflict = mResultants.compute(mDerivedEqualities);
372  if (conflict.has_value()) {
373  mConflict = std::set<FormulaT>(conflict->begin(), conflict->end());
374  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Immediate conflict due to " << *mConflict);
375  return false;
376  } else {
377  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Computed resultants:" << std::endl << *this << std::endl << "-> " << mResultants.getNewECs());
379  changed = true;
380  }
381  }
382  }
383  }
384  for (auto& c: mInequalities) {
385  carl::substitute_inplace(c.second, mModel);
386  if (c.second.is_consistent() == 0) {
387  mConflict = { FormulaT(c.first) };
388  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Immediate conflict due to " << *mConflict);
389  return false;
390  }
391  }
392  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "After preprocessing:" << std::endl << *this);
393  return true;
394 }
395 
396 std::set<FormulaT> CADPreprocessor::getConflict() const {
397  assert(mConflict);
398  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Building MIS from immediate conflict " << *mConflict);
399  std::set<FormulaT> res = *mConflict;
400  postprocessConflict(res);
401  return res;
402 }
403 
404 void CADPreprocessor::postprocessConflict(std::set<FormulaT>& mis) const {
405  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Postprocessing conflict: " << mis << " based on" << std::endl << *this);
407  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Resolved inequalities: " << mis);
408  }
409  bool changed = true;
410  carl::Variables modelAdded;
411  while (changed) {
412  changed = false;
414  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Resolved equalities: " << mis);
415  changed = true;
416  }
417  if (mOrigins.resolve(mis)) {
418  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Resolved resultants: " << mis);
419  changed = true;
420  }
421  if (addModelToConflict(mis, modelAdded)) {
422  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Resolved model: " << mis);
423  changed = true;
424  }
425  }
426  SMTRAT_LOG_DEBUG("smtrat.cad.pp", "Postprocessed conflict: " << mis);
427 }
428 
429 }
std::vector< ConstraintT > mEqualities
Equalities from the input.
std::map< ConstraintT, ConstraintT > mDerivedEqualities
Derived set of equalities, essentially mEqualities - mModel.
bool collectOriginsOfConflict(std::set< FormulaT > &conflict, const std::map< ConstraintT, ConstraintT > &constraints) const
Replace constraints that have been modified by its origins in the given conflict.
bool addEqualities(const std::vector< ConstraintT > &constraints)
std::set< FormulaT > getConflict() const
void postprocessConflict(std::set< FormulaT > &mis) const
preprocessor::ResultantRule mResultants
The resultant rule.
void removeEquality(const ConstraintT &c)
std::optional< std::set< FormulaT > > mConflict
preprocessor::AssignmentCollector mAssignments
The assignment collector.
void removeConstraint(const ConstraintT &c)
bool addModelToConflict(std::set< FormulaT > &conflict, carl::Variables &added) const
bool preprocess()
Performs the preprocessing.
Model mModel
The model used for variable elimination.
std::vector< ConstraintT > collectDerivedEqualities() const
void addConstraint(const ConstraintT &c)
std::map< ConstraintT, ConstraintT > mInequalities
Inequalities from the input.
preprocessor::Origins mOrigins
Origins of new formulas.
bool extractAssignments(std::map< ConstraintT, ConstraintT > &constraints)
std::map< ConstraintT, carl::Variable > mConstraints
std::variant< bool, ConstraintT > CollectionResult
Result of an assignment collection.
CollectionResult simplify(std::map< ConstraintT, ConstraintT > &constraints)
CollectionResult collect(std::map< ConstraintT, ConstraintT > &constraints)
bool extractParametricAssignments(std::map< ConstraintT, ConstraintT > &constraints)
bool extractValueAssignments(std::map< ConstraintT, ConstraintT > &constraints)
std::map< carl::Variable, ConstraintT > mReasons
Reasons for the assignment of variables.
std::vector< std::vector< UPoly > > mData
std::vector< ConstraintT > mNewECs
std::vector< ConstraintT > mConstraints
std::optional< std::vector< FormulaT > > compute(const std::map< ConstraintT, ConstraintT > &constraints)
std::optional< std::vector< FormulaT > > computeResultants(std::size_t level)
const std::vector< carl::Variable > & mVars
void sort(T *array, int size, LessThan lt)
Definition: Sort.h:67
static bool find(V &ts, const T &t)
Definition: Alg.h:47
static void remove(V &ts, const T &t)
Definition: Alg.h:36
std::size_t complexity(const std::vector< FormulaT > &origin)
Poly resultant(carl::Variable variable, const Poly &p, const Poly &q)
Computes the resultant of two polynomials.
Definition: utils.h:48
carl::UnivariatePolynomial< Poly > UPoly
Definition: common.h:17
const auto & settings_cadpp()
bool is_constant(const T &t)
Checks whether the constraint is constant, i.e.
bool substitute(const FormulaT &constr, const carl::Variable var, const carl::vs::Term< Poly > &term, FormulaT &result)
Definition: VSHelper.h:138
std::vector< carl::Variable > vars(const std::pair< QuantifierType, std::vector< carl::Variable >> &p)
Definition: QEQuery.h:43
carl::Formula< Poly > FormulaT
Definition: types.h:37
carl::MultivariatePolynomial< Rational > Poly
Definition: types.h:25
carl::Constraint< Poly > ConstraintT
Definition: types.h:29
#define SMTRAT_LOG_TRACE(channel, msg)
Definition: logging.h:36
#define SMTRAT_LOG_DEBUG(channel, msg)
Definition: logging.h:35
const std::vector< FormulaT > & get(const FormulaT &f) const
bool resolve(std::set< FormulaT > &conflict) const
void cleanOrigins(const FormulaT &f)
void add(const FormulaT &f, const std::vector< FormulaT > &origin)
std::map< FormulaT, std::vector< std::vector< FormulaT > > > mOrigins