SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
ExplanationGenerator.h
Go to the documentation of this file.
1 #pragma once
2 
3 
6 #include <smtrat-common/model.h>
7 
8 #include <carl-arith/core/Common.h>
9 
10 namespace smtrat {
11 namespace mcsat {
12 namespace nlsat {
13 
14 namespace helper {
15  /**
16  * Construct a formula representing a variable comparison.
17  * Simplify to a regular constraint if possible.
18  */
20  auto constraint = carl::as_constraint(vc);
21  if (constraint) {
22  SMTRAT_LOG_DEBUG("smtrat.nlsat", "Simplified " << vc << " to " << *constraint);
23  return FormulaT(ConstraintT(*constraint));
24  }
25  return FormulaT(std::move(vc));
26  }
27 
28  /**
29  * Construct an atomic formula representing a variable being equal to the given multivariate root. "v = root(..)"
30  */
31  template<typename MVRootParams>
32  FormulaT buildEquality(carl::Variable var, const MVRootParams& mvp) {
33  SMTRAT_LOG_DEBUG("smtrat.nlsat", "building: " << var << " = " << MultivariateRootT(mvp.first, mvp.second, var));
35  }
36  /**
37  * Construct an atomic formula representing a variable being less than the given multivariate root. "v < root(..)"
38  */
39  template<typename MVRootParams>
40  FormulaT buildBelow(carl::Variable var, const MVRootParams& mvp) {
41  SMTRAT_LOG_DEBUG("smtrat.nlsat", "building: " << var << " < " << MultivariateRootT(mvp.first, mvp.second, var));
42  return buildFormulaFromVC(VariableComparisonT(var, MultivariateRootT(mvp.first, mvp.second, var), carl::Relation::LESS));
43  }
44  /**
45  * Construct an atomic formula representing a variable being greater than the given multivariate root. "v > root(..)"
46  */
47  template<typename MVRootParams>
48  FormulaT buildAbove(carl::Variable var, const MVRootParams& mvp) {
49  SMTRAT_LOG_DEBUG("smtrat.nlsat", "building: " << var << " > " << MultivariateRootT(mvp.first, mvp.second, var));
50  return buildFormulaFromVC(VariableComparisonT(var, MultivariateRootT(mvp.first, mvp.second, var), carl::Relation::GREATER));
51  }
52 
53  /**
54  * Transform constraints represented as atomic formualas into the easier to
55  * use objects of the Constraint class.
56  */
57  inline
58  std::set<ConstraintT> convertToConstraints(std::vector<FormulaT> constraintAtoms) {
59  std::set<ConstraintT> cons;
60  for (const auto& cAtom: constraintAtoms) {
61  SMTRAT_LOG_DEBUG("smtrat.nlsat", "Adding " << cAtom << " to " << cons);
62  if (cAtom.type() == carl::FormulaType::CONSTRAINT) {
63  SMTRAT_LOG_DEBUG("smtrat.nlsat", "Adding " << cAtom);
64  cons.emplace(cAtom.constraint());
65  } else if (cAtom.type() == carl::FormulaType::VARCOMPARE) {
66  // Note that we only add the polynomials here and don't really care about the relation
67  // var ~ rootexpr(poly)
68  // -> poly to ensure that the root exists
69  //carl::Relation rel = cAtom.variable_comparison().negated() ? inverse(cAtom.variable_comparison().relation()) : cAtom.variable_comparison().relation();
70  SMTRAT_LOG_DEBUG("smtrat.nlsat", "Adding bound " << cAtom << " -> " << carl::defining_polynomial(cAtom.variable_comparison()));
71  cons.emplace(carl::defining_polynomial(cAtom.variable_comparison()), carl::Relation::NEQ);
72  // removed (makes no sense):
73  // -> var - poly to ensure that the relation still holds
74  //cons.emplace(Poly(cAtom.variable_comparison().var()) - carl::defining_polynomial(cAtom.variable_comparison()), rel);
75  } else if (cAtom.type() == carl::FormulaType::VARASSIGN) {
76  SMTRAT_LOG_WARN("smtrat.nlsat", "Variable assignment " << cAtom << " should never get here!");
77  assert(false);
78  SMTRAT_LOG_DEBUG("smtrat.nlsat", "Adding assignment " << cAtom);
79  const VariableComparisonT& vc = cAtom.variable_assignment();
80  cons.emplace(carl::defining_polynomial(vc), carl::Relation::EQ);
81  } else {
82  SMTRAT_LOG_ERROR("smtrat.nlsat", "Unsupported formula type: " << cAtom);
83  assert(false);
84  }
85  SMTRAT_LOG_DEBUG("smtrat.nlsat", "-> " << cons);
86  }
87  return cons;
88  }
89 
90 } // namespace helper
91 
93 private:
98  };
99 
101  // Store the original constraintAtoms, because they need to be added "raw" into the explanantion
102  std::vector<FormulaT> mConstraints;
105 
106  /**
107  * Construct expressions for the bounds of the CADCell component (a single sector/section)
108  * at the given level.
109  * @param boundsAsAtoms Output argument for cell component bounds at level as atomic formulas.
110  */
111  void generateBoundsFor(FormulasT& boundsAsAtoms, carl::Variable var, std::size_t level, const Model& model) const {
112  auto val = mModel.evaluated(var);
113  assert(val.isRational() || val.isRAN());
114  RAN value = val.isRational() ? RAN(val.asRational()) : val.asRAN();
115  SMTRAT_LOG_DEBUG("smtrat.nlsat", "Generating bounds for " << var << " = " << value);
116  std::optional<std::pair<RAN,FormulaT>> lower;
117  std::optional<std::pair<RAN,FormulaT>> upper;
118 
119  for (std::size_t pid = 0; pid < mProjection.size(level); pid++) {
120  const auto& poly = mProjection.getPolynomialById(level, pid);
121  if (carl::is_zero(carl::substitute(poly, model))) continue;
122  auto polyvars = carl::variables(poly);
123  polyvars.erase(poly.main_var());
124  auto list = carl::real_roots(poly, *carl::get_ran_assignment(polyvars, mModel));
125  if (list.is_nullified()) continue;
126  assert(list.is_univariate());
127  SMTRAT_LOG_DEBUG("smtrat.nlsat", "Looking at " << poly << " with roots " << list.roots());
128  // Find the closest roots/rootIdx around value.
129  std::size_t rootID = 1;
130  for (const auto& root: list.roots()) {
131  auto param = std::make_pair(Poly(poly), rootID);
132  SMTRAT_LOG_TRACE("smtrat.nlsat", root << " -> " << param);
133  if (root < value) {
134  if (!lower || (root > lower->first)) {
135  lower = std::make_pair(root, helper::buildAbove(var, param));
136  SMTRAT_LOG_TRACE("smtrat.nlsat", "new lower bound: " << lower->second);
137  }
138  } else if (root == value) {
139  lower = std::make_pair(root, helper::buildEquality(var, param));
140  upper = *lower;
141  SMTRAT_LOG_TRACE("smtrat.nlsat", "new exact root: " << lower->second);
142  } else {
143  if (!upper || (root < upper->first)) {
144  upper = std::make_pair(root, helper::buildBelow(var, param));
145  SMTRAT_LOG_TRACE("smtrat.nlsat", "new upper bound: " << upper->second);
146  }
147  }
148  rootID++;
149  }
150  }
151  if (lower) {
152  SMTRAT_LOG_DEBUG("smtrat.nlsat", "Lower bound:" << lower->second);
153  boundsAsAtoms.push_back(lower->second);
154  }
155  if (upper && lower != upper) {
156  SMTRAT_LOG_DEBUG("smtrat.nlsat", "Upper bound:" << upper->second);
157  boundsAsAtoms.push_back(upper->second);
158  }
159  }
160 public:
161  ExplanationGenerator(const std::vector<FormulaT>& constraints, const std::vector<carl::Variable>& vars, carl::Variable, const Model& model):
162  mModel(model),
163  mConstraints(constraints),
165  [&](const auto& p, std::size_t cid, bool isBound){ mProjection.addPolynomial(cad::projection::normalize(p), cid, isBound); },
166  [&](const auto& p, std::size_t cid, bool isBound){ mProjection.addEqConstraint(cad::projection::normalize(p), cid, isBound); },
167  [&](const auto& p, std::size_t cid, bool isBound){ mProjection.removePolynomial(cad::projection::normalize(p), cid, isBound); }
168  ),
170  {
171  SMTRAT_LOG_TRACE("smtrat.nlsat", "Reset to " << vars);
173  mProjection.reset();
174  std::set<ConstraintT> cons = helper::convertToConstraints(mConstraints);
175 
176  for (const auto& c: cons) {
177  mCADConstraints.add(c);
178  }
179 
180  SMTRAT_LOG_DEBUG("smtrat.cad.projection", "Starting with projection \n" << mProjection);
181 
182  for (std::size_t level = 2; level < mCADConstraints.vars().size(); level++) {
183  mProjection.projectNextLevel(level);
184  SMTRAT_LOG_DEBUG("smtrat.cad.projection", "After projecting into level " << level << "\n" << mProjection);
185  }
186 
187  SMTRAT_LOG_DEBUG("smtrat.nlsat", "Projection is\n" << mProjection);
188  }
189 
190  /**
191  * Construct explanation in non-clausal form (without the impliedAtom/implication).
192  * It consists of atomic formulas as
193  * expressions for cell component bounds and the constraintAtoms for which a CAD was
194  * constructed. Each cell component creates zero, one or two explanation atoms.
195  * The impliedAtom is not added to facilitate conversion into a clausal form
196  * afterwards.
197  * */
198  void generateExplanation(const FormulaT& impliedAtom, std::vector<FormulasT>& explainAtomsinLvls) const {
199  // Model with assignments for variables we already have constructed bounds for
200  Model submodel; // Start empty
201  explainAtomsinLvls.resize(mCADConstraints.vars().size());
202 
203  // Start from the bottom to generate bound constraints.
204  for (int level = static_cast<int>(mCADConstraints.vars().size()) - 1; level >= 0; level--) {
205  std::size_t lvl = static_cast<std::size_t>(level);
206  carl::Variable varAtLvl = mCADConstraints.vars()[lvl];
207  if (mModel.find(varAtLvl) == mModel.end()) continue;
208  generateBoundsFor(explainAtomsinLvls[lvl], varAtLvl, lvl+1, submodel);
209  SMTRAT_LOG_DEBUG("smtrat.nlsat", "Cell bounds for " << varAtLvl << ": " << explainAtomsinLvls[lvl]);
210  submodel.emplace(varAtLvl, mModel.evaluated(varAtLvl));
211  }
212 
213  SMTRAT_LOG_DEBUG("smtrat.nlsat", "Collecting constraints from " << mConstraints);
214  for (const auto& constraintAtom: mConstraints) {
215  SMTRAT_LOG_DEBUG("smtrat.nlsat", "Considering " << constraintAtom);
216  if (constraintAtom == impliedAtom.negated()) {
217  SMTRAT_LOG_DEBUG("smtrat.nlsat", "Skipping " << constraintAtom);
218  continue;
219  }
220  explainAtomsinLvls.back().emplace_back(constraintAtom);
221  }
222  SMTRAT_LOG_DEBUG("smtrat.nlsat", "Final: " << explainAtomsinLvls.back() << " -> " << impliedAtom);
223  }
224 
225  /**
226  * Compute explanation in clausal form.
227  */
228  mcsat::Explanation getExplanation(const FormulaT& impliedAtom) const {
229  std::vector<FormulasT> explainAtomsInLevels;
230  generateExplanation(impliedAtom, explainAtomsInLevels);
231  // Convert e1,..,en => I into clause -e1,...,-en, I
232  FormulasT explainClauseLiterals;
233  for (const auto& explainAtoms: explainAtomsInLevels) {
234  for (const auto& explainAtom: explainAtoms) {
235  explainClauseLiterals.emplace_back(explainAtom.negated());
236  }
237  }
238  if (!impliedAtom.is_true()) explainClauseLiterals.emplace_back(impliedAtom);
239  return FormulaT(carl::FormulaType::OR, std::move(explainClauseLiterals));
240  }
241 };
242 
243 }
244 }
245 }
std::size_t add(const ConstraintT &c)
void reset(const std::vector< carl::Variable > &vars)
const std::vector< carl::Variable > & vars() const
void generateExplanation(const FormulaT &impliedAtom, std::vector< FormulasT > &explainAtomsinLvls) const
Construct explanation in non-clausal form (without the impliedAtom/implication).
mcsat::Explanation getExplanation(const FormulaT &impliedAtom) const
Compute explanation in clausal form.
cad::CADConstraints< ProjectionSettings::backtracking > mCADConstraints
void generateBoundsFor(FormulasT &boundsAsAtoms, carl::Variable var, std::size_t level, const Model &model) const
Construct expressions for the bounds of the CADCell component (a single sector/section) at the given ...
cad::ModelBasedProjectionT< ProjectionSettings > mProjection
ExplanationGenerator(const std::vector< FormulaT > &constraints, const std::vector< carl::Variable > &vars, carl::Variable, const Model &model)
int var(Lit p)
Definition: SolverTypes.h:64
Poly normalize(const Poly &p)
Normalizes the given Poly by removing constant and duplicate factors.
Definition: utils.h:32
Incrementality
Definition: Settings.h:7
ProjectionType
Definition: Settings.h:9
Polynomial::RootType RAN
Definition: common.h:24
FormulaT buildEquality(carl::Variable var, const MVRootParams &mvp)
Construct an atomic formula representing a variable being equal to the given multivariate root.
std::set< ConstraintT > convertToConstraints(std::vector< FormulaT > constraintAtoms)
Transform constraints represented as atomic formualas into the easier to use objects of the Constrain...
FormulaT buildFormulaFromVC(VariableComparisonT &&vc)
Construct a formula representing a variable comparison.
FormulaT buildBelow(carl::Variable var, const MVRootParams &mvp)
Construct an atomic formula representing a variable being less than the given multivariate root.
FormulaT buildAbove(carl::Variable var, const MVRootParams &mvp)
Construct an atomic formula representing a variable being greater than the given multivariate root.
bool substitute(const FormulaT &constr, const carl::Variable var, const carl::vs::Term< Poly > &term, FormulaT &result)
Definition: VSHelper.h:138
std::variant< FormulaT, ClauseChain > Explanation
Definition: smtrat-mcsat.h:14
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::MultivariateRoot< Poly > MultivariateRootT
Definition: model.h:24
carl::Formulas< Poly > FormulasT
Definition: types.h:39
carl::VariableComparison< Poly > VariableComparisonT
Definition: types.h:35
#define SMTRAT_LOG_TRACE(channel, msg)
Definition: logging.h:36
#define SMTRAT_LOG_WARN(channel, msg)
Definition: logging.h:33
#define SMTRAT_LOG_ERROR(channel, msg)
Definition: logging.h:32
#define SMTRAT_LOG_DEBUG(channel, msg)
Definition: logging.h:35