SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
CADElimination.cpp
Go to the documentation of this file.
1 #include "CADElimination.h"
2 
3 #include <sstream>
4 
5 namespace smtrat {
6 namespace qe {
7 namespace cad {
8 
10 using smtrat::RAN;
11 
12 CADElimination::CADElimination(const FormulaT& quantifierFreePart, const QEQuery& quantifiers) {
13  mQuantifierFreePart = quantifierFreePart;
15 
16  carl::carlVariables vars = carl::variables(quantifierFreePart);
17  // quantified variables
18  for (const auto& v : mQuantifiers) {
19  mVariables.emplace_back(v.second);
20  vars.erase(v.second);
21  }
22  for (auto v : vars) {
23  mVariables.emplace_back(v);
24  }
25 
26  // number of variables
27  n = mVariables.size();
28  // number of free variables
29  k = mVariables.size() - mQuantifiers.size();
30 
31  carl::arithmetic_constraints(mQuantifierFreePart,mConstraints);
32 }
33 
35  constructCAD();
36 
38 
39  simplifyCAD();
40 
41  if (!isProjectionDefinable()) {
42  SMTRAT_LOG_DEBUG("smtrat.qe", "The CAD is not projection-definable");
44  } else {
45  SMTRAT_LOG_DEBUG("smtrat.qe", "The CAD is already projection-definable");
46  }
47 
48  return constructSolutionFormula();
49 }
50 
52  mCAD.reset(mVariables);
53 
54  for (const auto& c : mConstraints) {
55  mCAD.addPolynomial(c.lhs());
56  }
57 
58  mCAD.project();
59  mCAD.lift();
60 }
61 
62 void CADElimination::updateCAD(std::vector<Poly>& polynomials) {
63  // polynomials with lowest degree will be added first
64  std::sort(polynomials.begin(), polynomials.end());
65 
66  for (const auto& p : polynomials) {
67  mCAD.addPolynomial(p);
68 
69  mCAD.project();
70  mCAD.lift();
71 
72  if (isProjectionDefinable()) {
73  break;
74  }
75  }
76 
78 }
79 
81  std::vector<TreeIT> truthBoundaryCells;
82 
83  // level k
84  for (auto it = tree().begin_depth(k); it != tree().end_depth(); it++) {
85  if (it->isRoot()) {
86  TreeIT c = it;
87  TreeIT b = it.previous();
88  TreeIT d = it.next().next();
89 
90  if (mTruth.find(b)->second != mTruth.find(c)->second || mTruth.find(c)->second != mTruth.find(d)->second) {
91  SMTRAT_LOG_DEBUG("smtrat.qe", mCAD.getLifting().extractSampleMap(c) << " is a truth-boundary cell");
92  truthBoundaryCells.push_back(c);
93  }
94  }
95  }
96  std::vector<std::vector<Poly>> S;
97  for (const auto& cell : truthBoundaryCells) {
98  std::vector<Poly> P;
99  std::size_t numberOfProjectionFactors = mCAD.getProjection().size(n + 1 - k);
100  for (std::size_t id = 0; id < numberOfProjectionFactors; id++) {
101  if (mCAD.getProjection().hasPolynomialById(n + 1 - k, id)) {
102  Poly p(mCAD.getProjection().getPolynomialById(n + 1 - k, id));
103  if (carl::Sign::ZERO == carl::sgn(carl::evaluate(p, mCAD.getLifting().extractSampleMap(cell)))) {
104  P.push_back(p);
105  }
106  }
107  }
108  if (!P.empty()) {
109  S.push_back(P);
110  }
111  }
112  if (!S.empty()) {
113  std::vector<Poly> H = computeHittingSet(S);
114  std::size_t numberOfProjectionFactors = mCAD.getProjection().size(n + 1 - k);
115  for (std::size_t id = 0; id < numberOfProjectionFactors; id++) {
116  if (mCAD.getProjection().hasPolynomialById(n + 1 - k, id)) {
117  Poly p(mCAD.getProjection().getPolynomialById(n + 1 - k, id));
118  if (std::find(H.begin(), H.end(), p) == H.end()) {
119  SMTRAT_LOG_DEBUG("smtrat.qe", "Remove " << p);
120  mCAD.removePolynomial(n + 1 - k, id);
121  }
122  }
123  }
124  H.clear();
125  }
127 
128  // level k-1 and below
129  truthBoundaryCells.clear();
130  S.clear();
131 
132  if (k > 0) {
133  for (std::size_t i = k - 1; i > 0; i--) {
134  std::vector<Poly> N;
135  std::size_t numberOfProjectionFactors = mCAD.getProjection().size(n + 1 - i);
136  for (std::size_t id = 0; id < numberOfProjectionFactors; id++) {
137  if (mCAD.getProjection().hasPolynomialById(n + 1 - i, id)) {
138  if (mCAD.getProjection().testProjectionFactor(n + 1 - i, id)) {
139  N.emplace_back(mCAD.getProjection().getPolynomialById(n + 1 - i, id));
140  }
141  }
142  }
143  for (auto it = tree().begin_depth(i); it != tree().end_depth(); it++) {
144  if (it->isRoot()) {
145  TreeIT c = it;
146  TreeIT b = it.previous();
147  TreeIT d = it.next().next();
148  if (truthBoundaryTest(b, c, d)) {
149  SMTRAT_LOG_DEBUG("smtrat.qe", mCAD.getLifting().extractSampleMap(c) << " is a truth-boundary cell");
150  truthBoundaryCells.push_back(c);
151  }
152  }
153  }
154  for (const auto& cell : truthBoundaryCells) {
155  std::vector<Poly> P;
156  for (std::size_t id = 0; id < numberOfProjectionFactors; id++) {
157  if (mCAD.getProjection().hasPolynomialById(n + 1 - i, id)) {
158  Poly p(mCAD.getProjection().getPolynomialById(n + 1 - i, id));
159  if (carl::Sign::ZERO == carl::sgn(carl::evaluate(p, mCAD.getLifting().extractSampleMap(cell)))) {
160  P.push_back(p);
161  }
162  }
163  }
164  if (!P.empty()) {
165  S.push_back(P);
166  }
167  }
168  if (!S.empty()) {
169  std::vector<Poly> H = computeHittingSet(S);
170  for (std::size_t id = 0; id < numberOfProjectionFactors; id++) {
171  if (mCAD.getProjection().hasPolynomialById(n + 1 - i, id)) {
172  Poly p(mCAD.getProjection().getPolynomialById(n + 1 - i, id));
173  if (std::find(N.begin(), N.end(), p) == N.end() && std::find(H.begin(), H.end(), p) == H.end()) {
174  SMTRAT_LOG_DEBUG("smtrat.qe", "Remove " << p);
175  mCAD.removePolynomial(n + 1 - i, id);
176  }
177  }
178  }
179  H.clear();
180  }
181  truthBoundaryCells.clear();
182  S.clear();
183  }
184  } else {
185  SMTRAT_LOG_DEBUG("smtrat.qe", "Can not simplify CAD further, as there are no free variables.");
186  }
188 }
189 
191  std::vector<TreeIT> children_b = collectChildren(b);
192  std::vector<TreeIT> children_c = collectChildren(c);
193  std::vector<TreeIT> children_d = collectChildren(d);
194 
195  if (tree().max_depth(c) == k - 1) {
196  if (children_b.size() == children_c.size() && children_c.size() == children_d.size()) {
197  for (std::size_t i = 0; i < children_c.size(); i++) {
198  if (mTruth.find(children_b[i])->second != mTruth.find(children_c[i])->second || mTruth.find(children_c[i])->second != mTruth.find(std::next(children_d[i]))->second) {
199  return true;
200  }
201  }
202  }
203  } else {
204  if (children_b.size() == children_c.size() && children_c.size() == children_d.size()) {
205  for (std::size_t i = 0; i < children_c.size(); i++) {
206  if (truthBoundaryTest(children_b[i], children_c[i], children_d[i])) {
207  return true;
208  }
209  }
210  }
211  }
212  return false;
213 }
214 
216  mTruth.clear();
217 
218  // level n
219  for (auto it = tree().begin_leaf(); it != tree().end_leaf(); it++) {
220  Assignment assignment = mCAD.getLifting().extractSampleMap(it);
221  Model model;
222  for (const auto& a : assignment) {
223  model.emplace(a.first, a.second);
224  }
225  bool truthValue = carl::evaluate(mQuantifierFreePart, model).asBool();
226  mTruth.emplace(it, truthValue);
227  }
228 
229  // level n-1 down to level k
230  std::size_t i = n - 1;
231  for (auto quantifier = mQuantifiers.rbegin(); quantifier != mQuantifiers.rend(); quantifier++) {
232  for (auto it = tree().begin_depth(i); it != tree().end_depth(); it++) {
233  if (quantifier->first == QuantifierType::EXISTS) {
234  bool truthValue = false;
235  for (auto child = tree().begin_children(it); child != tree().end_children(it); child++) {
236  truthValue = truthValue || mTruth.find(child)->second;
237  }
238  mTruth.emplace(it, truthValue);
239  }
240  if (quantifier->first == QuantifierType::FORALL) {
241  bool truthValue = true;
242  for (auto child = tree().begin_children(it); child != tree().end_children(it); child++) {
243  truthValue = truthValue && mTruth.find(child)->second;
244  }
245  mTruth.emplace(it, truthValue);
246  }
247  }
248  i = i - 1;
249  }
250 }
251 
253  mSignature.clear();
254 
255  Assignment assignment;
256  std::vector<carl::Sign> signature;
257  for (auto it = tree().begin_depth(k); it != tree().end_depth(); it++) {
258  assignment = mCAD.getLifting().extractSampleMap(it);
259  Model model;
260  for (const auto& a : assignment) {
261  model.emplace(a.first, a.second);
262  }
263  for (std::size_t i = 1; i <= k; i++) {
264  std::size_t numberOfProjectionFactors = mCAD.getProjection().size(n + 1 - i);
265  for (std::size_t id = 0; id < numberOfProjectionFactors; id++) {
266  if (mCAD.getProjection().hasPolynomialById(n + 1 - i, id)) {
267  signature.push_back(carl::sgn(carl::evaluate(Poly(mCAD.getProjection().getPolynomialById(n + 1 - i, id)), assignment)));
268  }
269  }
270  }
271  mSignature.emplace(it, signature);
272 
273  assignment.clear();
274  signature.clear();
275  }
276 }
277 
278 // helper to swap the keys and values of a map
279 template<typename key, typename value>
280 std::multimap<value, key> flip_map(const std::map<key, value>& src) {
281  std::multimap<value, key> dst;
282  for (const auto& s: src) {
283  dst.emplace(s.second, s.first);
284  }
285  return dst;
286 }
287 
289  mCauseConflict.clear();
290 
291  bool projectionDefinable = true;
292  std::vector<TreeIT> samplesOfSameSignature;
293 
295  std::multimap<std::vector<carl::Sign>, TreeIT> signature_flipped = flip_map(mSignature);
296  std::vector<carl::Sign> signature = signature_flipped.begin()->first;
297 
298  for (const auto& s : signature_flipped) {
299  if (signature == s.first) {
300  samplesOfSameSignature.push_back(s.second);
301  } else {
302  for (auto a = samplesOfSameSignature.begin(); a != samplesOfSameSignature.end(); a++) {
303  for (auto b = std::next(a); b != samplesOfSameSignature.end(); b++) {
304  if (mTruth.find(*a)->second != mTruth.find(*b)->second) {
305  mCauseConflict.push_back(std::make_pair(*a, *b));
306  projectionDefinable = false;
307  }
308  }
309  }
310  samplesOfSameSignature.clear();
311  samplesOfSameSignature.push_back(s.second);
312 
313  signature.clear();
314  signature = s.first;
315  }
316  }
317  return projectionDefinable;
318 }
319 
321  for (std::size_t i = k; i >= 1; i--) {
322  std::vector<std::pair<TreeIT, TreeIT>> conflictingPairs;
323  for (const auto& pair : mCauseConflict) {
324  TreeIT a = pair.first;
325  TreeIT b = pair.second;
326  bool equals = false;
327  for (std::size_t level = k; level > i; level--) {
328  a = tree().get_parent(a);
329  b = tree().get_parent(b);
330  if (a == b) {
331  equals = true;
332  }
333  }
334  if (!equals) {
335  if (tree().get_parent(a) == tree().get_parent(b)) {
336  if (*a < *b) {
337  conflictingPairs.push_back(std::make_pair(a, b));
338  } else {
339  conflictingPairs.push_back(std::make_pair(b, a));
340  }
341  }
342  }
343  }
344 
345  std::vector<carl::UnivariatePolynomial<Poly>> setOfProjectionFactors;
346  std::vector<std::vector<carl::UnivariatePolynomial<Poly>>> setOfSetOfProjectionFactors;
347  for (const auto& conflictingPair : conflictingPairs) {
348  std::size_t numberOfProjectionFactors = mCAD.getProjection().size(n + 1 - i);
349  for (std::size_t id = 0; id < numberOfProjectionFactors; id++) {
350  if (mCAD.getProjection().hasPolynomialById(n + 1 - i, id)) {
351  bool zeroInSomeCell = false, vanish = true;
352  carl::UnivariatePolynomial<Poly> projectionFactor = mCAD.getProjection().getPolynomialById(n + 1 - i, id);
353  TreeIT it = conflictingPair.second;
354  while (*conflictingPair.first < *it) {
355  if (carl::Sign::ZERO == carl::sgn(carl::evaluate(Poly(projectionFactor), mCAD.getLifting().extractSampleMap(it)))) {
356  zeroInSomeCell = true;
357  } else {
358  vanish = false;
359  }
360  it = tree().left_sibling(it);
361  }
362  if (vanish) {
363  for (auto child = tree().begin_children(tree().get_parent(conflictingPair.first)); child != tree().end_children(tree().get_parent(conflictingPair.first)); child++) {
364  if (carl::Sign::ZERO != carl::sgn(carl::evaluate(Poly(projectionFactor), mCAD.getLifting().extractSampleMap(child)))) {
365  vanish = false;
366  }
367  }
368  }
369  if (zeroInSomeCell && !vanish) {
370  setOfProjectionFactors.push_back(projectionFactor);
371  }
372  }
373  }
374  if (setOfProjectionFactors.size() != 0) {
375  setOfSetOfProjectionFactors.push_back(setOfProjectionFactors);
376  setOfProjectionFactors.clear();
377  }
378  }
379  std::vector<carl::UnivariatePolynomial<Poly>> hittingSet = computeHittingSet(setOfSetOfProjectionFactors);
380 
381  std::vector<Poly> addToCAD;
382  for (const auto& p : hittingSet) {
383  for (uint nth = 0; nth <= p.degree(); nth++) {
384  Poly nthDerivative(carl::derivative(p, nth));
385  auto factorizationOfTheDerivative = carl::factorization(nthDerivative, false);
386 
387  for (const auto& factor : factorizationOfTheDerivative) {
388  addToCAD.push_back(factor.first);
389  }
390  }
391  }
392 
393  for (const auto& p : addToCAD) {
394  SMTRAT_LOG_DEBUG("smtrat.qe", "Add " << p);
395  }
396 
397  updateCAD(addToCAD);
398 
399  // if projection-definability is already achieved, we are done
400  if (isProjectionDefinable()) {
401  SMTRAT_LOG_DEBUG("smtrat.qe", "The CAD is now projection-definable");
402  break;
403  } else {
404  SMTRAT_LOG_DEBUG("smtrat.qe", "The CAD is still not projection-definable");
405  }
406  }
407 }
408 
410  Assignment assignment;
411  Model model;
412  assignment = mCAD.getLifting().extractSampleMap(sample);
413  for (const auto& a : assignment) {
414  model.emplace(a.first, a.second);
415  }
416  FormulasT L;
417  for (const auto& atomicFormula : mAtomicFormulas) {
418  if (carl::evaluate(atomicFormula, model).asBool()) {
419  L.push_back(atomicFormula);
420  }
421  }
422 
423  assignment.clear();
424  model.clear();
425  FormulasT evaluatedToFalse;
426  std::vector<FormulasT> S;
427  for (const auto& falseSample : mFalseSamples) {
428  assignment = mCAD.getLifting().extractSampleMap(falseSample);
429  for (const auto& a : assignment) {
430  model.emplace(a.first, a.second);
431  }
432  for (const auto& atomicFormula : L) {
433  if (!carl::evaluate(atomicFormula, model).asBool()) {
434  evaluatedToFalse.push_back(atomicFormula);
435  }
436  }
437  assignment.clear();
438  model.clear();
439  if (!evaluatedToFalse.empty()) {
440  S.push_back(evaluatedToFalse);
441  evaluatedToFalse.clear();
442  }
443  }
444 
446  SMTRAT_LOG_DEBUG("smtrat.qe", FormulaT(carl::FormulaType::AND, H) << " is an implicant capturing " << mCAD.getLifting().extractSampleMap(sample));
447  return FormulaT(carl::FormulaType::AND, H);
448 }
449 
451  for (auto sample_iterator = tree().begin_depth(k); sample_iterator != tree().end_depth(); sample_iterator++) {
452  if (mTruth.find(sample_iterator)->second) {
453  mTrueSamples.push_back(sample_iterator);
454  } else {
455  mFalseSamples.push_back(sample_iterator);
456  }
457  }
458  for (std::size_t level = 1; level <= k; level++) {
459  std::size_t numberOfProjectionFactors = mCAD.getProjection().size(n + 1 - level);
460  for (std::size_t id = 0; id < numberOfProjectionFactors; id++) {
461  if (mCAD.getProjection().hasPolynomialById(n + 1 - level, id)) {
462  Poly p(mCAD.getProjection().getPolynomialById(n + 1 - level, id));
464  mAtomicFormulas.emplace_back(ConstraintT(p, carl::Relation::NEQ));
465  mAtomicFormulas.emplace_back(ConstraintT(p, carl::Relation::LESS));
466  mAtomicFormulas.emplace_back(ConstraintT(p, carl::Relation::LEQ));
467  mAtomicFormulas.emplace_back(ConstraintT(p, carl::Relation::GREATER));
468  mAtomicFormulas.emplace_back(ConstraintT(p, carl::Relation::GEQ));
469  }
470  }
471  }
472 
473  FormulasT implicants;
474  bool captured = false;
475  for (auto const& sample : mTrueSamples) {
476  Assignment assignment = mCAD.getLifting().extractSampleMap(sample);
477  Model model;
478  for (const auto& a : assignment) {
479  model.emplace(a.first, a.second);
480  }
481  for (auto const& implicant : implicants) {
482  if (carl::evaluate(implicant, model).asBool()) {
483  captured = true;
484  }
485  }
486  if (!captured) {
487  FormulaT implicant = constructImplicant(sample);
488  implicants.push_back(implicant);
489  }
490  captured = false;
491  }
492 
493  FormulasT i;
494  std::vector<FormulasT> I;
495  for (auto const& sample : mTrueSamples) {
496  Assignment assignment = mCAD.getLifting().extractSampleMap(sample);
497  Model model;
498  for (const auto& a : assignment) {
499  model.emplace(a.first, a.second);
500  }
501  for (auto const& implicant : implicants) {
502  if (carl::evaluate(implicant, model).asBool()) {
503  i.push_back(implicant);
504  }
505  }
506  if (!i.empty()) {
507  I.push_back(i);
508  i.clear();
509  }
510  }
511 
513  return FormulaT(carl::FormulaType::OR, H);
514 }
515 
516 template<typename T>
517 std::vector<T> CADElimination::computeHittingSet(const std::vector<std::vector<T>>& setOfSets) {
518  std::vector<std::vector<T>> SoS(setOfSets);
519 
520  std::vector<T> H;
521  std::map<T, int> U;
522 
523  for (const auto& set : SoS) {
524  for (const auto& element : set) {
525  if (U.find(element) != U.end()) {
526  U.find(element)->second++;
527  } else {
528  U.emplace(element, 1);
529  }
530  }
531  }
532 
533  while (!SoS.empty()) {
534  auto max = U.begin();
535  for (auto it = U.begin(); it != U.end(); it++) {
536  if (it->second > max->second) {
537  max = it;
538  }
539  }
540  H.push_back(max->first);
541  for (auto set = SoS.begin(); set != SoS.end();) {
542  if (std::find(set->begin(), set->end(), max->first) != set->end()) {
543  set = SoS.erase(set);
544  } else {
545  ++set;
546  }
547  }
548  U.erase(max);
549  if (!U.empty()) {
550  for (auto it = U.begin(); it != U.end(); it++) {
551  it->second = 0;
552  }
553  for (const auto& set : SoS) {
554  for (const auto& element : set) {
555  if (U.find(element) != U.end()) {
556  U.find(element)->second++;
557  }
558  }
559  }
560  }
561  }
562 
563  return H;
564 }
565 
566 } // namespace cad
567 } // namespace qe
568 } // namespace smtrat
CADElimination(const FormulaT &quantifierFreePart, const QEQuery &quantifiers)
bool truthBoundaryTest(TreeIT &b, TreeIT &c, TreeIT &d)
std::vector< TreeIT > mTrueSamples
std::vector< std::pair< QuantifierType, carl::Variable > > mQuantifiers
std::map< TreeIT, bool > mTruth
CAD< CADSettings >::TreeIterator TreeIT
void updateCAD(std::vector< Poly > &polynomials)
std::vector< TreeIT > mFalseSamples
std::vector< ConstraintT > mConstraints
std::map< TreeIT, std::vector< carl::Sign > > mSignature
FormulaT constructImplicant(const TreeIT &sample)
std::vector< TreeIT > collectChildren(const TreeIT &it) const
std::vector< carl::Variable > mVariables
std::vector< T > computeHittingSet(const std::vector< std::vector< T >> &setOfSets)
std::vector< std::pair< TreeIT, TreeIT > > mCauseConflict
void sort(T *array, int size, LessThan lt)
Definition: Sort.h:67
static bool find(V &ts, const T &t)
Definition: Alg.h:47
std::map< carl::Variable, RAN > Assignment
Definition: common.h:14
Rational evaluate(carl::Variable v, const Poly &p, const Rational &r)
Definition: utils.h:11
carl::Sign sgn(carl::Variable v, const Poly &p, const RAN &r)
Definition: utils.h:15
std::multimap< value, key > flip_map(const std::map< key, value > &src)
std::optional< FormulaT > qe(const FormulaT &input)
Definition: qe.cpp:14
std::vector< std::pair< QuantifierType, carl::Variable > > flattenQEQuery(const QEQuery &query)
Definition: QEQuery.h:29
std::vector< std::pair< QuantifierType, std::vector< carl::Variable > >> QEQuery
Definition: QEQuery.h:17
std::vector< carl::Variable > vars(const std::pair< QuantifierType, std::vector< carl::Variable >> &p)
Definition: QEQuery.h:43
Class to create the formulas for axioms.
carl::Formula< Poly > FormulaT
Definition: types.h:37
carl::Model< Rational, Poly > Model
Definition: model.h:13
carl::IntRepRealAlgebraicNumber< Rational > RAN
Definition: types.h:47
carl::MultivariatePolynomial< Rational > Poly
Definition: types.h:25
carl::Constraint< Poly > ConstraintT
Definition: types.h:29
carl::Formulas< Poly > FormulasT
Definition: types.h:39
#define SMTRAT_LOG_DEBUG(channel, msg)
Definition: logging.h:35