SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
PBPPModule.tpp
Go to the documentation of this file.
1 /**
2  * @file PBPP.cpp
3  * @author YOUR NAME <YOUR EMAIL ADDRESS>
4  *
5  * @version 2016-11-23
6  * Created on 2016-11-23.
7  */
8 
9 #include "PBPPModule.h"
10 
11 #include "Encoders/RNSEncoder.h"
12 
13 // #define DEBUG_PBPP
14 // #define PRINT_STATS
15 
16 
17 namespace smtrat
18 {
19 
20  template<class Settings>
21  PBPPModule<Settings>::PBPPModule(const ModuleInput* _formula, Conditionals& _conditionals, Manager* _manager):
22  Module( _formula, _conditionals, _manager )
23 #ifdef SMTRAT_DEVOPTION_Statistics
24  , mStatistics(Settings::moduleName)
25 #endif
26  {
27  mCardinalityEncoder.problem_size = _formula->size();
28 
29  // Initialize the encoders which we are allowed to use
30  if (Settings::use_rns_transformation) mEncoders.push_back(&mRNSEncoder);
31  if (Settings::use_card_transformation) mEncoders.push_back(&mCardinalityEncoder);
32  if (Settings::use_mixed_transformation) mEncoders.push_back(&mMixedSignEncoder);
33  if (Settings::use_long_transformation) mEncoders.push_back(&mLongFormulaEncoder);
34  if (Settings::use_short_transformation) mEncoders.push_back(&mShortFormulaEncoder);
35  if (Settings::use_commander_transformation) mEncoders.push_back(&mExactlyOneCommanderEncoder);
36  if (Settings::use_totalizer_transformation) mEncoders.push_back(&mTotalizerEncoder);
37 
38  }
39 
40  template<class Settings>
41  PBPPModule<Settings>::~PBPPModule()
42  {}
43 
44  template<class Settings>
45  bool PBPPModule<Settings>::informCore( const FormulaT& )
46  {
47  return true; // This should be adapted according to your implementation.
48  }
49 
50  template<class Settings>
51  void PBPPModule<Settings>::init()
52  {}
53 
54  template<class Settings>
55  bool PBPPModule<Settings>::addCore( ModuleInput::const_iterator _subformula )
56  {
57  const FormulaT& formula = _subformula->formula();
58 
59  /* TODO remove
60  whats the point of this here?
61  if (objective() != carl::Variable::NO_VARIABLE) {
62  for (const auto& var: objectiveFunction().gatherVariables()) {
63  mVariablesCache.emplace(var, carl::fresh_integer_variable());
64  }
65  }*/
66 
67  addConstraints(formula);
68 
69  return true;
70  }
71 
72  template<class Settings>
73  void PBPPModule<Settings>::addConstraints(const FormulaT& formula) {
74  if (formula.is_boolean_combination() && formula.is_nary()) {
75  SMTRAT_LOG_DEBUG("smtrat.pbc", "Searching for embedded constraints in " << formula);
76  for (const auto& subformula : formula.subformulas()) {
77  addConstraints(subformula);
78  }
79 
80  return;
81  }
82 
83  if (formula.type() == carl::FormulaType::CONSTRAINT && formula.constraint().isPseudoBoolean()) {
84  ConstraintT constraint = formula.constraint();
85  if (!constraint.isPseudoBoolean()) {
86  // eg an objective function - just forward it
87  addSubformulaToPassedFormula(formula, formula);
88  return;
89  }
90 
91  ConstraintT normalizedConstraint = constraint;
92  if (Settings::NORMALIZE_CONSTRAINTS) {
93  #ifdef DEBUG_PBPP
94  std::cout << "Constraint before normalization: \t" << normalizedConstraint << std::endl;
95  #endif
96  // normalize and hence hopefully simplify the formula
97  std::pair<std::optional<FormulaT>, ConstraintT> normalizedConstraintWithBoolPart = mNormalizer.normalize(constraint);
98 
99  // extract the normalized constraint and pass along the rest
100  #ifdef DEBUG_PBPP
101  std::cout << "Constraint after normalization: \t"
102  << normalizedConstraintWithBoolPart.first
103  << " and "
104  << normalizedConstraintWithBoolPart.second << std::endl;
105  #endif
106 
107  normalizedConstraint = normalizedConstraintWithBoolPart.second;
108  if (normalizedConstraintWithBoolPart.first) {
109  extractConstraints(*normalizedConstraintWithBoolPart.first);
110  }
111  }
112 
113  mConstraints.push_back(normalizedConstraint);
114  formulaByConstraint[normalizedConstraint] = formula;
115  }
116  }
117 
118  template<class Settings>
119  void PBPPModule<Settings>::extractConstraints(const FormulaT& formula) {
120  if (formula.is_boolean_combination()) {
121  assert(formula.type() == carl::FormulaType::AND);
122  for (const auto& subformula: formula.subformulas()) {
123  extractConstraints(subformula);
124  }
125  } else if (formula.type() == carl::FormulaType::CONSTRAINT) {
126  mConstraints.push_back(formula.constraint());
127  formulaByConstraint[formula.constraint()] = formula;
128  } else if (formula.type() == carl::FormulaType::TRUE) {
129  return;
130  } else {
131  // we only expect a boolean combination of constraints
132  assert("The FormulaType passed was not valid. Expected Constraint, BooleanCombination or TRUE" && false);
133  }
134  }
135 
136  template<class Settings>
137  Answer PBPPModule<Settings>::checkCore()
138  {
139  // 1. Preprocessing - ignore some constraints and gather informations
140  for (const auto& constraint : mConstraints) {
141 
142  // we can also check a mode which only uses simplex and does not encode
143  if (Settings::USE_LIA_ONLY) {
144  liaConstraints.push_back(constraint);
145  continue;
146  }
147 
148  // store information about the constraint
149  Rational encodingSize = std::numeric_limits<size_t>::max();
150  for (const auto encoder : mEncoders) {
151  Rational curEncoderSize = encoder->encodingSize(constraint);
152  encodingSize = conversionSizeByConstraint[constraint];
153  if (encoder->canEncode(constraint) &&
154  (curEncoderSize <= encodingSize || conversionSizeByConstraint[constraint] == Rational(0)))
155  {
156  encoderByConstraint[constraint] = encoder;
157  conversionSizeByConstraint[constraint] = curEncoderSize;
158  }
159  }
160 
161  if (encoderByConstraint.find(constraint) == encoderByConstraint.end()) {
162  SMTRAT_LOG_INFO("smtrat.pbc", "There is no encoder for constraint " << constraint);
163  // if we do not know how to encode the constraint, use LIA!
164  liaConstraints.push_back(constraint);
165  continue;
166  }
167 
168  // by now we know which encoder we want to use if we actually want to convert to a boolean formula.
169  // Now check whether it is "benefitial" to use the boolean encoding, i.e. whether we introduce more
170  // new formulas than we already have.
171  if (encodeAsBooleanFormula(constraint)) {
172  boolConstraints.push_back(constraint);
173  } else {
174  liaConstraints.push_back(constraint);
175  }
176  }
177 
178  auto constraintComperator = [](const ConstraintT& left, const ConstraintT& right) { return left.variables().size() < right.variables().size();};
179  // sort by number of variables ascending
180  std::sort(boolConstraints.begin(), boolConstraints.end(), constraintComperator);
181  std::sort(liaConstraints.begin(), liaConstraints.end(), constraintComperator);
182 
183  #ifdef DEBUG_PBPP
184  std::cout << "After Step 1 - Encoding as LIA: " << liaConstraints << std::endl;
185  std::cout << "After Step 1 - Encoding as Bool: " << boolConstraints << std::endl;
186  #endif
187 
188  std::set<carl::Variable> variablesInLIA;
189  for (const auto& constraint : liaConstraints) {
190  for (const auto& cvar : constraint.variables()) {
191  variablesInLIA.insert(cvar);
192  }
193  }
194 
195  // 3. add more constraints to LIA part to refine the relaxation
196 
197  // since access to variablesInLIA would invalidate the iterator we instead save which variables we already inspected
198  std::set<carl::Variable> inspectedVariables;
199  std::set<ConstraintT> additionallyLIAEncodedBoolConstraints;
200  std::map<carl::Variable, carl::Variable>& variablesFromNormalization = mNormalizer.substitutedVariables();
201  std::set<carl::Variable> variableSubstitutions;
202  for (const auto& pair : variablesFromNormalization) {
203  variableSubstitutions.insert(pair.second);
204  }
205 
206  for (const auto& var : variablesInLIA) {
207  if (inspectedVariables.find(var) != inspectedVariables.end()) continue;
208 
209  for (const auto& constraint : boolConstraints) {
210  if (additionallyLIAEncodedBoolConstraints.find(constraint) != additionallyLIAEncodedBoolConstraints.end()) {
211  continue;
212  }
213 
214  bool constraintContainsCurrentVariable = constraint.variables().has(var);
215  bool constraintContainsSubstitutedVariable = variableSubstitutions.find(var) != variableSubstitutions.end();
216 
217  if (!isTrivial(constraint) && (constraintContainsCurrentVariable || constraintContainsSubstitutedVariable)) {
218  inspectedVariables.insert(var);
219  liaConstraints.push_back(constraint);
220  additionallyLIAEncodedBoolConstraints.insert(constraint);
221  }
222  }
223  }
224 
225  for (const auto& additionalConstraint : additionallyLIAEncodedBoolConstraints) {
226  auto it = std::find(boolConstraints.begin(), boolConstraints.end(), additionalConstraint);
227  if (it != boolConstraints.end()) {
228  boolConstraints.erase(it);
229  }
230  }
231 
232  #ifdef DEBUG_PBPP
233  std::cout << "After Step 3 - Encoding as LIA: " << liaConstraints << std::endl;
234  std::cout << "After Step 3 - Encoding as Bool: " << boolConstraints << std::endl;
235  #endif
236 
237  // 4. encode all remaining constraints as bool
238  std::set<carl::Variable> variablesInBooleanPart;
239  for (const auto& constraint : boolConstraints){
240  std::optional<FormulaT> boolEncoding = encoderByConstraint[constraint]->encode(constraint);
241  if (!boolEncoding) {
242  liaConstraints.push_back(constraint);
243  continue;
244  }
245 
246  #ifdef DEBUG_PBPP
247  std::cout << "Encoded using " << encoderByConstraint[constraint]->name() << " " << constraint << " \t as \t " << *boolEncoding << std::endl;
248  #endif
249  for (const auto& var : constraint.variables()) {
250  variablesInBooleanPart.insert(var);
251  }
252 
253  boolEncodings[constraint] = *boolEncoding;
254  }
255 
256  for (const auto& constraint : liaConstraints) {
257  //addSubformulaToPassedFormula(forwardAsArithmetic(constraint, variablesInBooleanPart), formulaByConstraint[constraint]);
258  liaConstraintFormula[constraint] = forwardAsArithmetic(constraint, variablesInBooleanPart);
259  }
260 
261  #ifdef PRINT_STATS
262  std::cout << "Encoded " << mConstraints.size() - liaConstraints.size() << " as bool and "
263  << liaConstraints.size() << " as LIA. "
264  << ((double)(mConstraints.size() - liaConstraints.size()))/(double)mConstraints.size() * 100 << "% are directly encoded." << std::endl;
265  #endif
266 
267  for (const auto& subformula : rReceivedFormula()) {
268  SMTRAT_LOG_INFO("smtrat.pbc", "rFormula " << subformula.formula() << " forwarded as \t" << restoreFormula(subformula.formula()));
269  addSubformulaToPassedFormula(restoreFormula(subformula.formula()), subformula.formula());
270  }
271 
272  Answer ans = runBackends();
273  if (ans == UNSAT) {
274  generateTrivialInfeasibleSubset();
275  }
276 
277  return ans;
278  }
279 
280  template<typename Settings>
281  FormulaT PBPPModule<Settings>::restoreFormula(const FormulaT& formula) {
282  if (formula.type() == carl::FormulaType::CONSTRAINT) {
283  // this one we may have to substitute
284  const ConstraintT& constraint = formula.constraint();
285  if (boolEncodings.find(constraint) != boolEncodings.end()) return boolEncodings[constraint];
286  if (liaConstraintFormula.find(constraint) != liaConstraintFormula.end()) return liaConstraintFormula[constraint];
287 
288  return formula;
289  } else if (formula.is_boolean_combination() && formula.is_nary()) {
290  FormulasT subforms;
291  for (const auto& subformula : formula.subformulas()) {
292  subforms.push_back(restoreFormula(subformula));
293  }
294 
295  return FormulaT(formula.type(), subforms);
296  } else {
297  // we don't care and return the formula - for example boolean literals. That's totally fine.
298  return formula;
299  }
300  }
301 
302  /*
303  * Converts Constraint into a LRA formula.
304  */
305  template<typename Settings>
306  FormulaT PBPPModule<Settings>::forwardAsArithmetic(const ConstraintT& formula, const std::set<carl::Variable>& boolVariables){
307  carl::Variables variables = formula.variables().as_set();
308 
309  std::set<carl::Variable> variableSetIntersection;
310  std::set_intersection(variables.begin(), variables.end(),
311  boolVariables.begin(), boolVariables.end(),
312  std::inserter(variableSetIntersection, variableSetIntersection.end()));
313 
314  for(const auto& it : variables){
315  if (mVariablesCache.find(it) != mVariablesCache.end()) continue;
316 
317  // add the variable since there is no integer coupling just yet.
318  mVariablesCache.insert(std::pair<carl::Variable, carl::Variable>(it, carl::fresh_integer_variable()));
319  }
320 
321  Poly lhs;
322  for(const auto& it : formula.lhs()){
323  if (it.is_constant()) {
324  lhs += it.coeff();
325  continue;
326  }
327 
328  lhs = lhs + it.coeff() * mVariablesCache[it.single_variable()];
329  }
330 
331  FormulaT constraintFormula = FormulaT(lhs, formula.relation());
332  FormulaT boolConnection = interconnectVariables(variableSetIntersection);
333  // it remains to specify bounds to on the new integer variables, however, it is enough
334  // to specify bounds to variables \setminus variableSetIntersection
335  FormulasT bounds;
336  for (auto var : variables) {
337  if (variableSetIntersection.find(var) != variableSetIntersection.end()) continue;
338 
339  // variable is not in intersection, add discrete bounds
340  ConstraintT equalOne((Poly(mVariablesCache[var]) - Rational(1)), carl::Relation::EQ);
341  ConstraintT equalZero(Poly(mVariablesCache[var]), carl::Relation::EQ);
342  bounds.push_back(FormulaT(carl::FormulaType::OR, FormulaT(equalOne), FormulaT(equalZero)));
343  }
344 
345  // bounds and connection do hold globally, forward them here
346  addSubformulaToPassedFormula(FormulaT(carl::FormulaType::AND, boolConnection, FormulaT(carl::FormulaType::AND, bounds)));
347 
348  return constraintFormula;
349  }
350 
351  template<typename Settings>
352  FormulaT PBPPModule<Settings>::interconnectVariables(const std::set<carl::Variable>& variables){
353  FormulasT subformulas;
354  for(const auto& var : variables){
355  if(std::find(mConnectedVars.begin(), mConnectedVars.end(), var) == mConnectedVars.end()){
356  //The variables are not interconnected
357  mConnectedVars.push_back(var);
358  FormulaT boolVar = FormulaT(var);
359  Poly intVar(mVariablesCache.find(var)->second);
360  FormulaT subformulaA = FormulaT(intVar - Rational(1), carl::Relation::EQ);
361  FormulaT subformulaB = FormulaT(carl::FormulaType::IMPLIES, boolVar, subformulaA);
362  FormulaT subformulaC = FormulaT(intVar, carl::Relation::EQ);
363  FormulaT subformulaD = FormulaT(carl::FormulaType::IMPLIES, boolVar.negated(), subformulaC);
364  FormulaT newFormula = FormulaT(carl::FormulaType::AND, subformulaB, subformulaD);
365  subformulas.push_back(newFormula);
366 
367  }
368  }
369  return FormulaT(carl::FormulaType::AND, std::move(subformulas));
370  }
371 
372  template<typename Settings>
373  bool PBPPModule<Settings>::isAllCoefficientsEqual(const ConstraintT& constraint) {
374  Rational coefficient = constraint.lhs().lcoeff();
375  for (const auto& term : constraint.lhs()) {
376  if (term.coeff() != coefficient) {
377  return false;
378  }
379  }
380 
381  return true;
382  }
383 
384  template<typename Settings>
385  bool PBPPModule<Settings>::encodeAsBooleanFormula(const ConstraintT& constraint) {
386  bool encode = true;
387 
388  // we do not encode very large formulas
389  encode = encode && conversionSizeByConstraint[constraint] <= Settings::MAX_NEW_RELATIVE_FORMULA_SIZE * rReceivedFormula().size();
390  //encode = encode && constraint.variables().size() <= 3;
391 
392  // this would be a simple encoding
393  //encode = encode && (constraint.relation() == carl::Relation::EQ || carl::abs(constraint.lhs().constant_part()) <= 1);
394 
395  encode = encode || Settings::ENCODE_IF_POSSIBLE;
396 
397  return encode;
398  }
399 
400  template<typename Settings>
401  bool PBPPModule<Settings>::isTrivial(const ConstraintT& constraint) {
402  bool trivial = false;
403 
404  trivial = trivial || constraint.variables().size() <= 1;
405  trivial = trivial || (constraint.lhs().constant_part() == 0 && mCardinalityEncoder.canEncode(constraint));
406 
407  return trivial;
408  }
409 
410  template<class Settings>
411  void PBPPModule<Settings>::removeCore( ModuleInput::const_iterator _subformula )
412  {
413  // remove the constraint according to the given input again
414  const FormulaT& formula = _subformula->formula();
415 
416  if (formula.type() != carl::FormulaType::CONSTRAINT){
417  return;
418  }
419 
420  const ConstraintT constraint = formula.constraint();
421  if (!constraint.isPseudoBoolean()) {
422  return;
423  }
424 
425  for (auto it = mConstraints.begin(); it != mConstraints.end(); ++it) {
426  if (*it == constraint) {
427  mConstraints.erase(it);
428  return;
429  }
430  }
431 
432  for (auto it = liaConstraints.begin(); it != liaConstraints.end(); ++it) {
433  if (*it == constraint) {
434  mConstraints.erase(it);
435  return;
436  }
437  }
438 
439  for (auto it = boolConstraints.begin(); it != boolConstraints.end(); ++it) {
440  if (*it == constraint) {
441  mConstraints.erase(it);
442  return;
443  }
444  }
445 
446  formulaByConstraint.erase(constraint);
447 
448  }
449 
450  template<class Settings>
451  void PBPPModule<Settings>::updateModel() const
452  {
453  mModel.clear();
454  if( is_sat(solverState()) )
455  {
456  getBackendsModel();
457  }
458  }
459 }
460 
461