SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
MCSATMixin.tpp
Go to the documentation of this file.
1 #include "MCSATMixin.h"
2 
3 namespace smtrat {
4 namespace mcsat {
5 
6 template<typename Settings>
7 void MCSATMixin<Settings>::pushTheoryDecision(const FormulaT& assignment, Minisat::Lit decisionLiteral) {
8  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Made theory decision for " << assignment.variable_assignment().var() << ": " << decisionLiteral);
9  assert(mInconsistentVariables.empty());
10  mBackend.pushAssignment( assignment.variable_assignment().var(), assignment.variable_assignment().value(), assignment);
11  mTheoryStack.emplace_back();
12  current().variable = assignment.variable_assignment().var();
13  current().decisionLiteral = decisionLiteral;
14  updateCurrentLevel();
15 }
16 
17 template<typename Settings>
18 void MCSATMixin<Settings>::popTheoryDecision() {
19  assert(!mTheoryStack.empty());
20  mUndecidedVariables.insert(
21  mUndecidedVariables.end(),
22  mTheoryStack.back().decidedVariables.begin(),
23  mTheoryStack.back().decidedVariables.end()
24  );
25  for (const auto var : mTheoryStack.back().decidedVariables) {
26  mTheoryLevels[varid(var)] = std::numeric_limits<std::size_t>::max();
27  mSemanticPropagations.erase(var);
28  }
29  mTheoryStack.pop_back();
30  mInconsistentVariables.clear();
31  // mModelAssignmentCache.clear();
32 }
33 
34 template<typename Settings>
35 void MCSATMixin<Settings>::updateCurrentLevel() {
36  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Updating current level " << current().variable);
37 
38  // Check undecided variables whether they became univariate
39  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Undecided Variables: " << mUndecidedVariables);
40  for (auto vit = mUndecidedVariables.begin(); vit != mUndecidedVariables.end();) {
41  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Looking at " << *vit);
42  if constexpr(!Settings::early_evaluation) {
43  if (computeTheoryLevel(*vit) != level()) {
44  ++vit;
45  continue;
46  }
47  } else {
48  if (mGetter.isTheoryAbstraction(*vit)) {
49  const auto& f = mGetter.reabstractVariable(*vit);
50  auto evalres = carl::evaluate(f, model());
51  if (!evalres.isBool()) {
52  ++vit;
53  continue;
54  } else if (mBackend.isActive(f) && mGetter.getBoolVarValue(*vit) != l_Undef && mGetter.getBoolVarValue(*vit) != Minisat::lbool(evalres.asBool())) {
55  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Found inconsistent variable " << *vit);
56  mInconsistentVariables.push_back(*vit);
57  }
58  }
59  }
60  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Associating " << *vit << " with " << current().variable << " at " << level());
61  current().decidedVariables.push_back(*vit);
62  mTheoryLevels[varid(*vit)] = level();
63  if (mGetter.getBoolVarValue(*vit) == l_Undef) {
64  mSemanticPropagations.insert(*vit);
65  }
66  vit = mUndecidedVariables.erase(vit);
67  }
68  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "-> " << mUndecidedVariables);
69 }
70 
71 template<typename Settings>
72 bool MCSATMixin<Settings>::backtrackTo(Minisat::Lit literal) {
73  std::size_t lvl = level();
74  while (lvl > 0) {
75  if (get(lvl).decisionLiteral == literal) break;
76  lvl--;
77  }
78  SMTRAT_LOG_TRACE("smtrat.sat.mcsat", "Backtracking until " << literal << " on level " << lvl);
79  if (lvl == 0) {
80  SMTRAT_LOG_TRACE("smtrat.sat.mcsat", "Nothing to backtrack for " << literal);
81  return false;
82  }
83 
84  while (level() >= lvl) {
85  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Backtracking theory assignment for " << current().variable);
86 
87  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Model " << model());
88 
89  if (current().decisionLiteral != Minisat::lit_Undef) {
90  mBackend.popAssignment(current().variable);
91  }
92  popTheoryDecision();
93  }
94  return true;
95 }
96 
97 template<typename Settings>
98 Minisat::lbool MCSATMixin<Settings>::evaluateLiteral(Minisat::Lit lit) const {
99  SMTRAT_LOG_TRACE("smtrat.sat.mcsat", "Evaluate " << lit);
100  const FormulaT& f = mGetter.reabstractLiteral(lit);
101  SMTRAT_LOG_TRACE("smtrat.sat.mcsat", "Evaluate " << f << " on " << mBackend.getModel());
102  if (!Settings::early_evaluation && computeTheoryLevel(f) > level()) return l_Undef;
103  SMTRAT_LOG_TRACE("smtrat.sat.mcsat", "Evaluate " << f << " on " << mBackend.getModel());
104  auto res = carl::evaluate(f, mBackend.getModel());
105  if (res.isBool()) {
106  return res.asBool() ? l_True : l_False;
107  }
108  assert(Settings::early_evaluation);
109  return l_Undef;
110 }
111 
112 /**
113  * Checks is given literal is feasible with the current trail.
114  */
115 template<typename Settings>
116 std::pair<bool, std::optional<Explanation>> MCSATMixin<Settings>::isBooleanDecisionFeasible(Minisat::Lit lit, bool always_explain) {
117  auto var = Minisat::var(lit);
118  if (!mGetter.isTheoryAbstraction(var)) return std::make_pair(true, std::nullopt);
119  const auto& f = mGetter.reabstractLiteral(lit);
120 
121  // assert that literal is consistent with the trail
122  assert(evaluateLiteral(lit) != l_False);
123 
124  auto vars = carl::arithmetic_variables(f);
125  for (const auto& v : mBackend.assignedVariables())
126  vars.erase(v);
127 
128  if (vars.size() > 0) {
129  carl::Variable tvar = *(vars.begin());
130 
131  auto res = mBackend.isInfeasible(tvar, f);
132  if (std::holds_alternative<ModelValues>(res)) {
133  /* if (mModelAssignmentCache.empty()) {
134  mModelAssignmentCache.cache(std::get<ModelValues>(res));
135  }*/
136  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Decision " << lit << " (" << f << ") is feasible wrt " << tvar);
137  return std::make_pair(true, std::nullopt);
138  } else {
139  auto& confl = std::get<FormulasT>(res);
140  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Decision " << lit << " (" << f << ") is infeasible wrt " << tvar << " due to " << confl);
141  if (always_explain) {
142  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Explaining " << confl);
143  return std::make_pair(false, mBackend.explain(tvar, confl));
144  } else if (std::find(confl.begin(), confl.end(), f) == confl.end()) {
145  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Conflicting core " << confl << " is independent from decision " << f);
146  return std::make_pair(false, mBackend.explain(tvar, confl));
147  } else {
148  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Check if trail without " << f << " was feasible wrt " << tvar);
149  auto expl = isFeasible(tvar);
150  if (expl) {
151  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Trail without " << f << " was infeasible wrt " << tvar);
152  return std::make_pair(false, std::move(*expl));
153  } else {
154  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Infeasibility wrt " << tvar << " depends truly on " << f);
155  return std::make_pair(false, std::nullopt);
156  }
157  }
158  }
159  } else {
160  return std::make_pair(true, std::nullopt);
161  }
162 }
163 
164 template<typename Settings>
165 std::pair<boost::tribool, std::optional<Explanation>> MCSATMixin<Settings>::propagateBooleanDomain(Minisat::Lit lit) {
166  auto var = Minisat::var(lit);
167  if (!mGetter.isTheoryAbstraction(var)) return std::make_pair(boost::indeterminate, std::nullopt);
168  const auto& f = mGetter.reabstractLiteral(lit);
169 
170  auto vars = carl::arithmetic_variables(f);
171  for (const auto& v : mBackend.assignedVariables())
172  vars.erase(v);
173 
174  assert(vars.size() > 0);
175 
176  carl::Variable tvar = *(vars.begin());
177 
178  auto res_pos = mBackend.isInfeasible(tvar, f);
179  auto res_neg = mBackend.isInfeasible(tvar, f.negated());
180 
181  if (std::holds_alternative<ModelValues>(res_pos) && std::holds_alternative<ModelValues>(res_neg)) {
182  /* if (mModelAssignmentCache.empty()) {
183  mModelAssignmentCache.cache(std::get<ModelValues>(res));
184  }*/
185  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Decision: " << lit << " (" << f << ") and its negation are feasible wrt " << tvar);
186  return std::make_pair(boost::indeterminate, std::nullopt);
187  } else if (std::holds_alternative<ModelValues>(res_pos)) {
188  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Propagation: " << lit << " (" << f << ") is feasible wrt " << tvar);
189  return std::make_pair(true, std::nullopt);
190  } else if (std::holds_alternative<ModelValues>(res_neg)) {
191  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Propagation: negation of " << lit << " (" << f << ") is feasible wrt " << tvar);
192  return std::make_pair(false, std::nullopt);
193  } else {
194  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Conflict: " << lit << " (" << f << ") and its negation are infeasible wrt " << tvar << " due to " << std::get<FormulasT>(res_pos) << " resp. " << std::get<FormulasT>(res_neg));
195 
196  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Check if trail without " << f << " was feasible wrt " << tvar);
197  auto expl = isFeasible(tvar);
198  assert(expl);
199  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Trail without " << f << " was infeasible wrt " << tvar);
200  return std::make_pair(boost::indeterminate, std::move(*expl));
201  }
202 }
203 
204 template<typename Settings>
205 std::size_t MCSATMixin<Settings>::addBooleanVariable(Minisat::Var variable) {
206  while (mVarPropertyCache.size() <= varid(variable)) {
207  mVarPropertyCache.emplace_back();
208  }
209 
210  while (mTheoryLevels.size() <= varid(variable)) {
211  mTheoryLevels.emplace_back();
212  }
213 
214  std::size_t level = computeTheoryLevel(variable);
215  if (!mGetter.isTheoryAbstraction(variable)) {
216  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Ignoring " << variable << " as it is not a theory abstraction");
217  return level;
218  }
219  assert(mGetter.reabstractVariable(variable).type() != carl::FormulaType::VARASSIGN);
220  if (level == std::numeric_limits<std::size_t>::max()) {
221  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Adding " << variable << " to undecided");
222  mUndecidedVariables.push_back(variable);
223  mTheoryLevels[varid(variable)] = std::numeric_limits<std::size_t>::max();
224  } else {
225  SMTRAT_LOG_DEBUG("smtrat.sat.mcsat", "Adding " << variable << " on level " << level);
226  mTheoryStack[level].decidedVariables.push_back(variable);
227  mTheoryLevels[varid(variable)] = level;
228  mSemanticPropagations.insert(variable);
229  }
230  return level;
231 }
232 
233 template<typename Settings>
234 bool MCSATMixin<Settings>::isFormulaUnivariate(const FormulaT& formula, std::size_t level) const {
235  assert(level < mTheoryStack.size());
236  auto vars = carl::arithmetic_variables(formula);
237  for (std::size_t lvl = 1; lvl <= level; lvl++) {
238  vars.erase(variable(lvl));
239  }
240  SMTRAT_LOG_TRACE("smtrat.sat.mcsat", "Checking if " << formula << " is univariate on level " << level << ": " << vars.empty());
241  return vars.empty();
242 }
243 
244 template<typename Settings>
245 void MCSATMixin<Settings>::printClause(std::ostream& os, Minisat::CRef clause) const {
246  const Minisat::Clause& c = mGetter.getClause(clause);
247  os << "(";
248  for (int i = 0; i < c.size(); i++) {
249  if (i > 0) os << " ";
250  if (mGetter.isTheoryAbstraction(var(c[i]))) {
251  os << mGetter.reabstractLiteral(c[i]);
252  } else {
253  os << c[i];
254  }
255  }
256  os << ")";
257 }
258 
259 template<typename Settings>
260 std::ostream& operator<<(std::ostream& os, const MCSATMixin<Settings>& mcm) {
261  os << "Theory Stack: " << mcm.level() << std::endl;
262  for (std::size_t lvl = 0; lvl < mcm.mTheoryStack.size(); lvl++) {
263  const auto& level = mcm.get(lvl);
264  os << lvl << " / " << level.variable << " (" << level.decisionLiteral << ")";
265  if (mcm.model().find(level.variable) != mcm.model().end()) {
266  os << " = " << mcm.model().at(level.variable);
267  }
268  // if (level.variable == mcm.current().variable) {
269  // os << " <<-- Current variable";
270  // }
271  os << std::endl;
272  os << "\tVariables: " << level.decidedVariables << std::endl;
273  }
274  os << "Backend:" << std::endl;
275  os << mcm.mBackend << std::endl;
276  os << "Theory variable mapping:" << std::endl;
277  os << mcm.mTheoryVarMapping.minisatToCarl << std::endl;
278  return os;
279 }
280 
281 }
282 }