SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
NewCoveringModule.tpp
Go to the documentation of this file.
1 /**
2  * @file NewCovering.cpp
3  * @author Philip Kroll <Philip.Kroll@rwth-aachen.de>
4  *
5  * @version 2021-07-08
6  * Created on 2021-07-08.
7  */
8 
9 #include "NewCoveringModule.h"
10 
11 namespace smtrat {
12 template<class Settings>
13 NewCoveringModule<Settings>::NewCoveringModule(const ModuleInput* _formula, Conditionals& _conditionals, Manager* _manager)
14  : Module(_formula, _conditionals, _manager) {
15  SMTRAT_LOG_DEBUG("smtrat.covering", "Init New Covering Module");
16  // Pass informations about the settings to the statistics
17  // TODO re-enable statistics
18  // SMTRAT_STATISTICS_CALL(getStatistics().setVariableOrderingType(mcsat::get_name(Settings::variableOrderingStrategy)));
19  // SMTRAT_STATISTICS_CALL(getStatistics().setCoveringHeuristicType(cadcells::representation::get_name(Settings::covering_heuristic)));
20  // SMTRAT_STATISTICS_CALL(getStatistics().setOperatorType(cadcells::operators::get_name(Settings::op)));
21  // SMTRAT_STATISTICS_CALL(getStatistics().setSamplingAlgorithm(get_name(Settings::sampling_algorithm)));
22  // SMTRAT_STATISTICS_CALL(getStatistics().setIsSampleOutsideAlgorithm(get_name(Settings::is_sample_outside_algorithm)));
23  // SMTRAT_STATISTICS_CALL(getStatistics().setIncremental(Settings::incremental));
24  // SMTRAT_STATISTICS_CALL(getStatistics().setBacktracking(Settings::backtracking));
25 }
26 
27 template<class Settings>
28 NewCoveringModule<Settings>::~NewCoveringModule() {}
29 
30 template<class Settings>
31 bool NewCoveringModule<Settings>::informCore(const FormulaT& _constraint) {
32  // Gather all possible constraints for the initial (complete!) variable ordering
33  assert(_constraint.type() == carl::FormulaType::CONSTRAINT);
34  mAllConstraints.push_back(_constraint.constraint());
35  return true;
36 }
37 
38 template<class Settings>
39 void NewCoveringModule<Settings>::init() {}
40 
41 template<class Settings>
42 bool NewCoveringModule<Settings>::addCore(ModuleInput::const_iterator _subformula) {
43  // Incremental call
44  assert(_subformula->formula().type() == carl::FormulaType::CONSTRAINT);
45  mUnknownConstraints.push_back(_subformula->formula());
46  //std::erase(mRemoveConstraints,_subformula->formula());
47  mRemoveConstraints.erase(std::remove(mRemoveConstraints.begin(), mRemoveConstraints.end(), _subformula->formula()), mRemoveConstraints.end());
48  SMTRAT_LOG_DEBUG("smtrat.covering", "Adding new unknown constraint: " << _subformula->formula().constraint());
49  return true;
50 }
51 
52 template<class Settings>
53 void NewCoveringModule<Settings>::removeCore(ModuleInput::const_iterator _subformula) {
54  // Backtracking
55  assert(_subformula->formula().type() == carl::FormulaType::CONSTRAINT);
56  mRemoveConstraints.push_back(_subformula->formula());
57  //std::erase(mUnknownConstraints,_subformula->formula());
58  mUnknownConstraints.erase(std::remove(mUnknownConstraints.begin(), mUnknownConstraints.end(), _subformula->formula()), mUnknownConstraints.end());
59  SMTRAT_LOG_DEBUG("smtrat.covering", "Adding to remove constraint: " << _subformula->formula().constraint());
60 }
61 
62 template<class Settings>
63 void NewCoveringModule<Settings>::updateModel() const {
64  carl::carlVariables vars(carl::variable_type_filter::real());
65  rReceivedFormula().gatherVariables(vars);
66  clearModel();
67  for (const auto& pair : mLastAssignment) {
68  if (vars.has(pair.first)) {
69  mModel.assign(pair.first,carl::convert<typename Poly::RootType>(pair.second));
70  }
71  }
72 }
73 
74 template<class Settings>
75 size_t NewCoveringModule<Settings>::addConstraintsSAT() {
76  SMTRAT_LOG_DEBUG("smtrat.covering", "Adding constraints: " << mUnknownConstraints);
77  // Hard case:
78  // Recheck the unknown constraints with the last satisfying assignment
79  SMTRAT_LOG_DEBUG("smtrat.covering", "Rechecking the unknown constraints with the last satisfying assignment");
80  std::size_t lowestLevelWithUnsatisfiedConstraint = mVariableOrdering.size() + 1;
81  bool foundUnsatisfiedConstraint = false;
82 
83  std::map<size_t, std::vector<ConstraintT>> constraintsByLevel;
84 
85  for (const auto& formula : mUnknownConstraints) {
86  ConstraintT constraint = formula.constraint();
87  size_t level = helper::level_of(mVariableOrdering, constraint.lhs()) - 1;
88  constraintsByLevel[level].push_back(std::move(constraint));
89  }
90  mUnknownConstraints.clear();
91 
92  SMTRAT_LOG_DEBUG("smtrat.covering", "Build constraint map: " << constraintsByLevel);
93 
94  // Now iterate over the constraints by level, starting with lowest first (the keys in a std::map are implicitly sorted using std::less)
95  for (const auto& levelConstraints : constraintsByLevel) {
96  SMTRAT_LOG_DEBUG("smtrat.covering", "Checking level " << levelConstraints.first);
97  if (foundUnsatisfiedConstraint) break;
98  for (const auto& constraint : levelConstraints.second) {
99  if (carl::evaluate(carl::convert<cadcells::Polynomial>(*backend.getContext(), constraint.constr()), mLastAssignment) != true) {
100  // This constraint is unsat with the last assignment
101  SMTRAT_LOG_DEBUG("smtrat.covering", "Found unsatisfied constraint on level:" << levelConstraints.first);
102  foundUnsatisfiedConstraint = true;
103  lowestLevelWithUnsatisfiedConstraint = levelConstraints.first;
104  break;
105  }
106  }
107  }
108 
109  // We can add the new constraints to the backend now
110  for (const auto& levelConstraints : constraintsByLevel) {
111  backend.addConstraint(levelConstraints.first, std::move(levelConstraints.second));
112  }
113  constraintsByLevel.clear();
114 
115  return lowestLevelWithUnsatisfiedConstraint;
116 }
117 
118 template<class Settings>
119 void NewCoveringModule<Settings>::removeConstraintsSAT() {
120  SMTRAT_LOG_DEBUG("smtrat.covering", "Removing constraints: " << mRemoveConstraints);
121  // Easy case:
122  // we can just remove the constraints and the corresponding stored information
123  // this does not change the current satisfying assignment/ satisfyability of the given set of constraints
124  for (const auto& constraint : mRemoveConstraints) {
125  backend.removeConstraint(std::move(constraint.constraint()));
126  }
127  mRemoveConstraints.clear();
128 }
129 
130 template<class Settings>
131 void NewCoveringModule<Settings>::addConstraintsUNSAT() {
132  SMTRAT_LOG_DEBUG("smtrat.covering", "Adding constraints: " << mUnknownConstraints);
133  // Easy case:
134  // Add all unknown constraints to backend
135  // We can do this with no further calculations, as the model stays unsatisfiable
136  for (const auto& constraint : mUnknownConstraints) {
137  backend.addConstraint(std::move(constraint.constraint()));
138  }
139  mUnknownConstraints.clear();
140 }
141 
142 template<class Settings>
143 bool NewCoveringModule<Settings>::removeConstraintsUNSAT() {
144  SMTRAT_LOG_DEBUG("smtrat.covering", "Removing constraints: " << mRemoveConstraints);
145  assert(backend.getCoveringInformation()[0].isFullCovering());
146  // Hard case:
147  // We have to remove the constraints and the corresponding stored information (derivations with the constraint as origin)
148  // This might include information in the stored full coverings
149  // If not: Nothing changes and the model stays unsatisfiable
150  // If yes: the model might become satisfiable or stays unsatisfiable -> Needs recalculation of the covering at level 0
151  // If level 0 is still full after removal of constraints: the model is still unsatisfiable
152  // If level 0 is not full after removal of constraints: the model might become satisfiable or stays unsatisfiable -> Needs recalculation off all the higher levels : TODO Ask Jasper if this is correct
153 
154  // First: remove all constraints from the backend, this also removes the corresponding derivations and invalidates the coverings, if they used a removed derivations
155  for (const auto& constraint : mRemoveConstraints) {
156  backend.removeConstraint(std::move(constraint.constraint()));
157  }
158  mRemoveConstraints.clear();
159 
160  // Second: Check if the covering on level 0 has changed/ was invalidated
161  bool hasChanged = backend.getCoveringInformation()[0].isUnknownCovering();
162 
163  SMTRAT_LOG_DEBUG("smtrat.covering", "Covering on level 0 has changed: " << hasChanged);
164 
165  // Third: If the covering has changed, we need to recompute it
166  if (hasChanged) {
167  backend.getCoveringInformation()[0].computeCovering();
168  }
169 
170  SMTRAT_LOG_DEBUG("smtrat.covering", "Covering on level is still full: " << backend.getCoveringInformation()[0].isFullCovering());
171 
172  return backend.getCoveringInformation()[0].isFullCovering();
173 }
174 
175 template<class Settings>
176 std::optional<Answer> NewCoveringModule<Settings>::doBacktracking() {
177  // This function is called when we have constraints to remove and no constraints to add
178  // assert(mBacktracking);
179  SMTRAT_STATISTICS_CALL(getStatistics().calledBacktrackingOnly());
180 
181  if (mLastAnswer == Answer::SAT) {
182  // Easy case
183  removeConstraintsSAT();
184  return Answer::SAT;
185  } else {
186  // Hard case
187  bool stillFullCovering = removeConstraintsUNSAT();
188  if (stillFullCovering) {
189  // The assignment is still unsatisfiable
190 
191  // we have to recompute the infeasible subset
192  mInfeasibleSubsets.push_back(backend.getInfeasibleSubset());
193  SMTRAT_LOG_DEBUG("smtrat.covering", "Infeasible Subset: " << mInfeasibleSubsets.back());
194  return Answer::UNSAT;
195  } else {
196  backend.resetStoredData(1);
197  return std::nullopt;
198  }
199  }
200 }
201 
202 template<typename Settings>
203 std::optional<Answer> NewCoveringModule<Settings>::doIncremental() {
204  // This function is called when we have constraints to add and no constraints to remove
205  // assert(mIncremental);
206  SMTRAT_STATISTICS_CALL(getStatistics().calledIncrementalOnly());
207 
208  if (mLastAnswer == Answer::SAT) {
209  // Hard case:
210  auto lowestLevel = addConstraintsSAT();
211 
212  if (lowestLevel == mVariableOrdering.size() + 1) {
213  // The assignment is still satisfiable
214  return Answer::SAT;
215  } else {
216  // Remove all the data from the levels higher than lowestLevel
217  SMTRAT_LOG_DEBUG("smtrat.covering", "Removing data from level " << lowestLevel);
218  backend.resetStoredData(lowestLevel);
219  return std::nullopt;
220  }
221  } else {
222  // Easy Case:
223  addConstraintsUNSAT();
224  // NOTE: Trivially the infeasible subset did not change
225  return Answer::UNSAT;
226  }
227 }
228 
229 template<typename Settings>
230 std::optional<Answer> NewCoveringModule<Settings>::doIncrementalAndBacktracking() {
231  // This function is called when we have constraints to add and constraints to remove
232  // assert(mBacktracking && mIncremental);
233 
234  SMTRAT_STATISTICS_CALL(getStatistics().calledIncrementalAndBacktracking());
235 
236  // First make sure that the vectors are disjoint
237  FormulasT intersection;
238  // we need to sort both vectors to make sure that the intersection is correct
239  std::sort(mRemoveConstraints.begin(), mRemoveConstraints.end());
240  std::sort(mUnknownConstraints.begin(), mUnknownConstraints.end());
241  std::set_intersection(mUnknownConstraints.begin(), mUnknownConstraints.end(), mRemoveConstraints.begin(), mRemoveConstraints.end(), std::back_inserter(intersection));
242  assert(intersection.size() == 0);
243 
244  if (mLastAnswer == Answer::SAT) {
245  // first trivially remove the constraints
246  removeConstraintsSAT();
247  // then add the constraints
248  auto lowestLevel = addConstraintsSAT();
249  if (lowestLevel == mVariableOrdering.size() + 1) {
250  // The assignment is still satisfiable
251  return Answer::SAT;
252  } else {
253  // Remove all the data from the levels higher than lowestLevel
254  backend.resetStoredData(lowestLevel);
255  return std::nullopt;
256  }
257  } else {
258  // first trivially add the constraints
259  addConstraintsUNSAT();
260  // then remove the constraints
261  bool stillFullCovering = removeConstraintsUNSAT();
262  if (stillFullCovering) {
263  // The assignment is still unsatisfiable
264  // we have to recompute the infeasible subset
265  mInfeasibleSubsets.push_back(backend.getInfeasibleSubset());
266  SMTRAT_LOG_DEBUG("smtrat.covering", "Infeasible Subset: " << mInfeasibleSubsets.back());
267  return Answer::UNSAT;
268  } else {
269  // delete everything, but keep level 0
270  backend.resetStoredData(1);
271  return std::nullopt;
272  }
273  }
274 }
275 
276 template<typename Settings>
277 Answer NewCoveringModule<Settings>::checkCore() {
278 
279  SMTRAT_STATISTICS_CALL(getStatistics().called());
280 
281  SMTRAT_LOG_DEBUG("smtrat.covering", "Check Core called with unknown constraints: " << mUnknownConstraints);
282  SMTRAT_LOG_DEBUG("smtrat.covering", "Check Core called with remove constraints: " << mRemoveConstraints);
283  SMTRAT_LOG_DEBUG("smtrat.covering", "Last Answer: " << mLastAnswer);
284  SMTRAT_LOG_DEBUG("smtrat.covering", "Check Core called with last assignment: " << mLastAssignment);
285 
286  // Check if this is the first time checkCore is called
287  if (mVariableOrdering.empty()) {
288 
289  // Init variable odering, we use the variable ordering which was declared in the settings
290  mVariableOrdering = mcsat::calculate_variable_order<Settings::variableOrderingStrategy>(mAllConstraints);
291 
292  SMTRAT_STATISTICS_CALL(getStatistics().setDimension(mVariableOrdering.size()));
293  SMTRAT_STATISTICS_CALL(cadcells::statistics().set_max_level(mVariableOrdering.size()));
294 
295  // We can clear mAllConstraints now, as we don't need it anymore -> Its only needed to calculate the variable ordering
296  mAllConstraints.clear();
297 
298  SMTRAT_LOG_DEBUG("smtrat.covering", "Got Variable Ordering : " << mVariableOrdering);
299 
300  // init backend
301  backend.init(mVariableOrdering);
302 
303  // Add unknown constraints to backend
304  for (const auto& constraint : mUnknownConstraints) {
305  // Asserts that it is indeed a constraint
306  backend.addConstraint(constraint.constraint());
307  }
308 
309  mUnknownConstraints.clear();
310  } else if (mLastAnswer == Answer::UNSAT || mLastAnswer == Answer::SAT) {
311  if (mUnknownConstraints.empty() && mRemoveConstraints.empty()) {
312  // We dont have any new constraints and can just return the last value derived by the backend
313  // Why does this even happen??
314  processAnswer();
315  return mLastAnswer;
316  }
317 
318  if (mRemoveConstraints.empty() && !mUnknownConstraints.empty()) {
319  // we only have constraints to add, and no constraints to remove
320  // INCREMENTAL CALL ONLY
321 
322  if (Settings::incremental) {
323  auto ans = doIncremental();
324  // check if we can trivially deduce the answer
325  if (ans) {
326  mLastAnswer = *ans;
327  processAnswer();
328  return *ans;
329  }
330  } else {
331  // Dont do incremental, just add constraints and delete all stored data
332  backend.resetStoredData(0);
333  for (auto& constraint : mUnknownConstraints) {
334  backend.addConstraint(constraint.constraint());
335  }
336  mUnknownConstraints.clear();
337  }
338 
339  } else if (!mRemoveConstraints.empty() && mUnknownConstraints.empty()) {
340  // We only have constraints to remove, and no constraints to add
341  // BACKTRACKING CALL ONLY
342 
343  if (Settings::backtracking) {
344  auto ans = doBacktracking();
345  // check if we can trivially deduce the answer
346  if (ans) {
347  mLastAnswer = *ans;
348  processAnswer();
349  return *ans;
350  }
351  } else {
352  // Dont do backtracking, just remove constraints and delete all stored data
353  backend.resetStoredData(0);
354  for (auto& constraint : mRemoveConstraints) {
355  backend.removeConstraint(constraint.constraint());
356  }
357  mRemoveConstraints.clear();
358  }
359 
360  } else if (!mRemoveConstraints.empty() && !mUnknownConstraints.empty()) {
361  // We have both constraints to add and constraints to remove
362  // INCREMENTAL AND BACKTRACKING CALL
363  if (Settings::incremental && Settings::backtracking) {
364 
365  auto ans = doIncrementalAndBacktracking();
366  // check if we can trivially deduce the answer
367  if (ans) {
368  mLastAnswer = *ans;
369  processAnswer();
370  return *ans;
371  }
372  } else {
373  // Dont do incremental and backtracking, just add/remove constraints and delete all stored data
374  backend.resetStoredData(0);
375  for (auto& constraint : mUnknownConstraints) {
376  backend.addConstraint(constraint.constraint());
377  }
378  mUnknownConstraints.clear();
379  for (auto& constraint : mRemoveConstraints) {
380  backend.removeConstraint(constraint.constraint());
381  }
382  mRemoveConstraints.clear();
383  }
384  }
385  } else {
386  // The last call returned UNKNOWN
387  // we have to delete all stored information and completely re-initialize the backend
388  backend.resetStoredData(0);
389  backend.resetDerivationToConstraintMap();
390  for (auto& constraint : mUnknownConstraints) {
391  backend.addConstraint(constraint.constraint());
392  }
393  mUnknownConstraints.clear();
394  for (auto& constraint : mRemoveConstraints) {
395  backend.removeConstraint(constraint.constraint());
396  }
397  mRemoveConstraints.clear();
398  }
399 
400  // As getUnsatCover is recursive we always have to start at level 0
401  mLastAnswer = backend.getUnsatCover(0);
402 
403  SMTRAT_LOG_DEBUG("smtrat.covering", "Check Core returned: " << mLastAnswer);
404 
405  if (mLastAnswer == Answer::UNSAT || mLastAnswer == Answer::SAT) {
406  processAnswer();
407  } else {
408  // Answer is UNKNOWN and something went wrong
409  SMTRAT_LOG_DEBUG("smtrat.covering", "Backend encountered an error: " << mLastAnswer);
410 
411  // remove all stored information in the backend
412  backend.resetStoredData(0);
413  mLastAssignment.clear(); // There is no satisfying assignment
414 
415  // run complete backend
416  for(auto iter_recv = rReceivedFormula().begin(); iter_recv != rReceivedFormula().end(); ++iter_recv) {
417  addReceivedSubformulaToPassedFormula(iter_recv);
418  }
419  auto result = runBackends();
420  //assert(result == Answer::SAT || result == Answer::UNSAT);
421  if (result == Answer::SAT) {
422  getBackendsModel();
423  } else if (result == Answer::UNSAT) {
424  getInfeasibleSubsets();
425  }
426  return result;
427  }
428 
429  return mLastAnswer;
430 }
431 
432 template<typename Settings>
433 void NewCoveringModule<Settings>::processAnswer() {
434  if (mLastAnswer == Answer::UNSAT) {
435  mInfeasibleSubsets.push_back(backend.getInfeasibleSubset());
436  SMTRAT_LOG_DEBUG("smtrat.covering", "Infeasible Subset: " << mInfeasibleSubsets.back());
437  mLastAssignment.clear(); // There is no satisfying assignment
438 
439  } else if (mLastAnswer == Answer::SAT) {
440  mLastAssignment = backend.getCurrentAssignment();
441  }
442  updateModel(); // Update the model according to the last assignment from the backend
443 }
444 
445 } // namespace smtrat
446