SMT-RAT  24.02
Toolbox for Strategic and Parallel Satisfiability-Modulo-Theories Solving
IncWidthModule.tpp
Go to the documentation of this file.
1 /**
2  * @file IncWidthModule.tpp
3  * @author Florian Corzilius <corzilius@cs.rwth-aachen.de>
4  *
5  * @version 2015-06-29
6  * Created on 2015-06-29.
7  */
8 
9 #include "IncWidthModule.h"
10 #include <carl-formula/formula/functions/Substitution.h>
11 
12 //#define DEBUG_INC_WIDTH_MODULE
13 
14 namespace smtrat
15 {
16  /**
17  * Constructors.
18  */
19 
20  template<class Settings>
21  IncWidthModule<Settings>::IncWidthModule( const ModuleInput* _formula, Conditionals& _conditionals, Manager* _manager ):
22  Module( _formula, _conditionals, _manager ),
23  mRestartCheck( true ),
24  mHalfOfCurrentWidth( carl::pow( Rational(Settings::increment), Settings::start_width-1 ) ),
25  mVariableShifts(),
26  mVarBounds(),
27  mICPFormula( nullptr ),
28  mICPFoundAnswer(),
29  mICP( nullptr ),
30  mICPFormulaPositions()
31  {
32  if( Settings::use_icp )
33  {
34  mICPFormula = new ModuleInput();
35  mICPFoundAnswer.push_back( new std::atomic_bool( false ) );
36  mICP = new ICPModule<ICPSettings4>( mICPFormula, mICPFoundAnswer );
37 
38  }
39  }
40 
41  template<class Settings>
42  IncWidthModule<Settings>::~IncWidthModule()
43  {
44  if( Settings::use_icp )
45  {
46  while( !mICPFoundAnswer.empty() )
47  {
48  std::atomic_bool* toDel = mICPFoundAnswer.back();
49  mICPFoundAnswer.pop_back();
50  delete toDel;
51  }
52  mICPFoundAnswer.clear();
53  delete mICPFormula;
54  delete mICP;
55  }
56  }
57 
58  template<class Settings>
59  bool IncWidthModule<Settings>::addCore( ModuleInput::const_iterator _subformula )
60  {
61  if( _subformula->formula().type() == carl::FormulaType::CONSTRAINT )
62  {
63  if( Settings::use_icp )
64  addToICP( _subformula->formula() );
65  else
66  {
67  if( mVarBounds.addBound( _subformula->formula().constraint(), _subformula->formula() ) )
68  {
69  reset();
70  }
71  }
72  }
73  return !mVarBounds.isConflicting();
74  }
75 
76  template<class Settings>
77  void IncWidthModule<Settings>::removeCore( ModuleInput::const_iterator _subformula )
78  {
79  if( _subformula->formula().type() == carl::FormulaType::CONSTRAINT )
80  {
81  if( Settings::use_icp )
82  removeFromICP( _subformula->formula() );
83  else
84  {
85  if( mVarBounds.removeBound( _subformula->formula().constraint(), _subformula->formula() ) )
86  {
87  reset();
88  }
89  }
90  }
91  }
92 
93  template<class Settings>
94  std::pair<ModuleInput::iterator,bool> IncWidthModule<Settings>::addToICP( const FormulaT& _formula, bool _guaranteedNew )
95  {
96  #ifdef DEBUG_INC_WIDTH_MODULE
97  std::cout << "Add to internal ICPModule: " << _formula << std::endl;
98  #endif
99  auto ret = mICPFormula->add( _formula, false );
100  assert( !_guaranteedNew || ret.second );
101  if( _guaranteedNew )
102  {
103  assert( mICPFormulaPositions.find( _formula ) == mICPFormulaPositions.end() );
104  mICPFormulaPositions.emplace( _formula, ret.first );
105  }
106  mICP->inform( _formula );
107  mICP->add( ret.first );
108  return ret;
109  }
110 
111  template<class Settings>
112  void IncWidthModule<Settings>::removeFromICP( const FormulaT& _formula )
113  {
114  #ifdef DEBUG_INC_WIDTH_MODULE
115  std::cout << "Remove from internal ICPModule: " << _formula << std::endl;
116  #endif
117  auto icpformpos = mICPFormulaPositions.find( _formula );
118  mICP->remove( icpformpos->second );
119  mICPFormula->erase( icpformpos->second );
120  mICPFormulaPositions.erase( icpformpos );
121  }
122 
123  template<class Settings>
124  void IncWidthModule<Settings>::clearICP()
125  {
126  #ifdef DEBUG_INC_WIDTH_MODULE
127  std::cout << "Clear internal ICPModule!" << std::endl;
128  #endif
129  for( const auto& icpformpos : mICPFormulaPositions )
130  {
131  mICP->remove( icpformpos.second );
132  mICPFormula->erase( icpformpos.second );
133  }
134  mICPFormulaPositions.clear();
135  }
136 
137  template<class Settings>
138  void IncWidthModule<Settings>::updateModel() const
139  {
140  mModel.clear();
141  if( solverState() == SAT )
142  {
143  getBackendsModel();
144  for( auto& ass : mModel )
145  {
146  auto varShiftIter = mVariableShifts.find( ass.first.asVariable() );
147  if( varShiftIter != mVariableShifts.end() )
148  {
149  assert( ass.second.isRational() || ass.second.isSqrtEx() || ass.second.isRAN() || ass.second.isSubstitution() );
150  bool varWithNegCoeff = carl::is_negative( varShiftIter->second.lcoeff() );
151  if( ass.second.isRational() )
152  {
153  mModel.assign(ass.first, (varWithNegCoeff ? Rational(-ass.second.asRational()) : ass.second.asRational()) + varShiftIter->second.constant_part());
154  }
155  else if( ass.second.isSubstitution() )
156  {
157  if( varWithNegCoeff )
158  ass.second.asSubstitution()->multiplyBy( -1 );
159  else
160  ass.second.asSubstitution()->add( varShiftIter->second.constant_part() );
161  }
162  else if( ass.second.isSqrtEx() )
163  {
164  mModel.assign(ass.first, (varWithNegCoeff ? ass.second.asSqrtEx()*SqrtEx( Poly( -1 ) ) : ass.second.asSqrtEx()) + SqrtEx( Poly( varShiftIter->second.constant_part() ) ));
165  }
166  else // ass.second.isRAN()
167  {
168  assert(false); // TODO: How to add a value to a RAN
169  RAN bound = RAN(varShiftIter->second.constant_part());
170 // ass.second = ass.second.asRAN()->add( bound );
171  }
172  }
173  }
174  }
175  }
176 
177  template<class Settings>
178  Answer IncWidthModule<Settings>::checkCore()
179  {
180  #ifdef DEBUG_INC_WIDTH_MODULE
181  std::cout << "Check of IncWidthModule:" << std::endl;
182  for( const auto& f : rReceivedFormula() ) std::cout << " " << f.formula() << std::endl;
183  #endif
184  ModuleInput::const_iterator rf = firstUncheckedReceivedSubformula();
185  auto _vars = carl::carlVariables().arithmetic();
186  rReceivedFormula().gatherVariables(_vars);
187  carl::Variables arithVars = _vars.as_set();
188  if( Settings::use_icp )
189  {
190  Answer icpResult = mICP->check();
191  if( icpResult == UNSAT )
192  {
193  for( auto& infsubset : mICP->infeasibleSubsets() )
194  mInfeasibleSubsets.push_back( infsubset );
195  return UNSAT;
196  }
197  }
198  EvalRationalIntervalMap varBounds = Settings::use_icp ? mICP->getCurrentBoxAsIntervals() : mVarBounds.getEvalIntervalMap();
199  if( mRestartCheck )
200  {
201  #ifdef DEBUG_INC_WIDTH_MODULE
202  std::cout << "Shift variables with the domains:" << std::endl;
203  for( auto& v : arithVars )
204  {
205  auto it = varBounds.find( v );
206  if( it == varBounds.end() )
207  std::cout << " " << v << " in " << RationalInterval::unbounded_interval() << std::endl;
208  else
209  std::cout << " " << v << " in " << it->second << std::endl;
210  }
211  std::cout << "Results in:" << std::endl;
212  #endif
213  // Determine the shifts according to the initial variable bounds
214  rf = rReceivedFormula().begin();
215  mRestartCheck = false;
216  for( const auto& vb : varBounds )
217  {
218  if( vb.second.lower_bound_type() != carl::BoundType::INFTY )
219  {
220  // (a,b) -> (0,b-a) or (a,oo) -> (0,oo)
221  mVariableShifts[vb.first] = smtrat::Poly( vb.first ) + vb.second.lower();
222  #ifdef DEBUG_INC_WIDTH_MODULE
223  std::cout << " " << vb.first << " -> " << mVariableShifts[vb.first] << std::endl;
224  #endif
225  }
226  else if( vb.second.upper_bound_type() != carl::BoundType::INFTY )
227  {
228  // (-oo,b) -> (0,oo)
229  mVariableShifts[vb.first] = -smtrat::Poly( vb.first ) + vb.second.upper();
230  #ifdef DEBUG_INC_WIDTH_MODULE
231  std::cout << " " << vb.first << " -> " << mVariableShifts[vb.first] << std::endl;
232  #endif
233  }
234  }
235  }
236  // add all received formula after performing the shift to the passed formula
237  if( Settings::use_icp )
238  clearICP();
239  for( ; rf != rReceivedFormula().end(); ++rf )
240  {
241  FormulaT subResult = carl::substitute(rf->formula(), mVariableShifts );
242  addSubformulaToPassedFormula( subResult, rf->formula() );
243  if( Settings::use_icp && subResult.type() == carl::FormulaType::CONSTRAINT )
244  addToICP( subResult );
245  }
246  std::vector<ModuleInput::iterator> addedBounds;
247  // For all variables add bounds (incrementally widening) until a solution is found or a certain width is reached
248  for(;;)
249  {
250  if( anAnswerFound() )
251  return UNKNOWN;
252  // Check if we exceed the maximally allowed width
253  if( Settings::max_width > 0 && mHalfOfCurrentWidth > carl::pow( Rational(Settings::increment), Settings::max_width-1 ) )
254  {
255  mHalfOfCurrentWidth /= Rational(Settings::increment);
256  #ifdef DEBUG_INC_WIDTH_MODULE
257  std::cout << "Reached maximal width" << std::endl;
258  #endif
259  break;
260  }
261  if( tryToAddBounds( varBounds, arithVars, addedBounds ) )
262  {
263  // If no bound has actually been added, we can directly call the backends on the shifted input without bounds. The result is then terminal.
264  if( addedBounds.empty() )
265  {
266  #ifdef DEBUG_INC_WIDTH_MODULE
267  std::cout << "No bounds added, so go for final check." << std::endl;
268  #endif
269  break;
270  }
271  // Increment the bound width for the next iteration.
272  #ifdef DEBUG_INC_WIDTH_MODULE
273  std::cout << "Update half of width from " << mHalfOfCurrentWidth << " to " << (mHalfOfCurrentWidth*Rational(Settings::increment)) << std::endl;
274  #endif
275 
276  mHalfOfCurrentWidth *= Rational(Settings::increment);
277  Answer ans = runBackends();
278 
279  #ifdef DEBUG_INC_WIDTH_MODULE
280  std::cout << "Calling backends on:" << std::endl;
281  for( const auto& f : rPassedFormula() ) std::cout << " " << f.formula() << std::endl;
282  std::cout << "results in " << ans << std::endl;
283  #endif
284 
285  if( ans == UNSAT )
286  {
287  // Check if infeasible subset does not contain the introduced bounds. Then we know that the input is unsatisfiable.
288  std::vector<Module*>::const_iterator backend = usedBackends().begin();
289  while( backend != usedBackends().end() )
290  {
291  if( (*backend)->solverState() == UNSAT )
292  useInfSubsetIfNoAddedBoundsAreContained( **backend, addedBounds );
293  ++backend;
294  }
295  }
296  if( ans == SAT )
297  return ans;
298  }
299  // Found such an infeasible subset, then return.
300  if( !mInfeasibleSubsets.empty() )
301  {
302  #ifdef DEBUG_INC_WIDTH_MODULE
303  std::cout << "Found an infeasible subset not containing introduced bounds!" << std::endl;
304  #endif
305  return UNSAT;
306  }
307  // Remove the bounds.
308  #ifdef DEBUG_INC_WIDTH_MODULE
309  std::cout << "Remove added bounds!" << std::endl;
310  #endif
311  while( !addedBounds.empty() )
312  {
313  #ifdef DEBUG_INC_WIDTH_MODULE
314  std::cout << "Remove bound: " << addedBounds.back()->formula() << std::endl;
315  #endif
316  eraseSubformulaFromPassedFormula( addedBounds.back(), true );
317  addedBounds.pop_back();
318  }
319  }
320  if( Settings::exclude_searched_space )
321  {
322  // Add the not yet covered search space.
323  FormulasT formulas;
324  for( carl::Variable::Arg var : arithVars )
325  {
326  auto vb = varBounds.find( var );
327  if( vb == varBounds.end() || (vb->second.lower_bound_type() == carl::BoundType::INFTY && vb->second.upper_bound_type() == carl::BoundType::INFTY) )
328  {
329  formulas.push_back( FormulaT( ConstraintT( var, carl::Relation::GREATER, Rational( mHalfOfCurrentWidth ) ) ) );
330  formulas.push_back( FormulaT( ConstraintT( var, carl::Relation::LEQ, -Rational( mHalfOfCurrentWidth ) ) ) );
331  }
332  else
333  {
334  if( vb->second.lower_bound_type() != carl::BoundType::INFTY )
335  formulas.push_back( FormulaT( ConstraintT( var, carl::Relation::GEQ, Rational(2)*mHalfOfCurrentWidth ) ) );
336  else
337  formulas.push_back( FormulaT( ConstraintT( var, carl::Relation::LEQ, -(Rational(2)*mHalfOfCurrentWidth) ) ) );
338  }
339  }
340  if( formulas.size() > 1 )
341  {
342  FormulaT rem( carl::FormulaType::OR, formulas );
343  addSubformulaToPassedFormula( rem );
344  #ifdef DEBUG_INC_WIDTH_MODULE
345  std::cout << " add remainig space " << rem << std::endl;
346  #endif
347  }
348  else if( !formulas.empty() )
349  {
350  addSubformulaToPassedFormula( formulas.back() );
351  #ifdef DEBUG_INC_WIDTH_MODULE
352  std::cout << " add remainig space " << formulas.back() << std::endl;
353  #endif
354  }
355  }
356  if( Settings::use_icp )
357  {
358  clearICP();
359  for( const auto& rformula : rReceivedFormula() )
360  addToICP( rformula.formula() );
361  }
362 
363  Answer ans = runBackends();
364 
365  #ifdef DEBUG_INC_WIDTH_MODULE
366  std::cout << "Final call of backends results in " << ans << std::endl;
367  std::cout << "Calling backends on:" << std::endl;
368  for( const auto& f : rPassedFormula() ) std::cout << " " << f.formula() << std::endl;
369  std::cout << "results in " << ans << std::endl;
370  #endif
371 
372  if( ans == UNSAT )
373  {
374  mInfeasibleSubsets.clear();
375  FormulaSetT infeasibleSubset;
376  // TODO: compute a better infeasible subset
377  for( auto subformula = rReceivedFormula().begin(); subformula != rReceivedFormula().end(); ++subformula )
378  infeasibleSubset.insert( subformula->formula() );
379  mInfeasibleSubsets.push_back( infeasibleSubset );
380  }
381  return ans;
382  }
383 
384  template<class Settings>
385  bool IncWidthModule<Settings>::tryToAddBounds( const EvalRationalIntervalMap& _varBounds, const carl::Variables& _allArithmeticVariables, std::vector<ModuleInput::iterator>& _addedBounds )
386  {
387  #ifdef DEBUG_INC_WIDTH_MODULE
388  std::cout << "Try to add bounds:" << std::endl;
389  #endif
390  if( Settings::use_icp )
391  {
392  std::vector<ModuleInput::iterator> icpsAddedBounds;
393  // First try to contract variable domains with ICP.
394  for( carl::Variable::Arg var : _allArithmeticVariables )
395  {
396  auto vb = _varBounds.find( var );
397  if( (vb == _varBounds.end() || (vb->second.lower_bound_type() == carl::BoundType::INFTY && vb->second.upper_bound_type() == carl::BoundType::INFTY)) )
398  {
399  // Unbounded variable v. Add: mHalfONfCurrentWidth <= v < mHalfOfCurrentWidth
400  ConstraintT boundA( var, carl::Relation::LESS, Settings::exclude_negative_numbers ? Rational(2)*mHalfOfCurrentWidth : Rational( mHalfOfCurrentWidth ) );
401  auto ret = addToICP( FormulaT( boundA ), false );
402  if( ret.second )
403  icpsAddedBounds.push_back( ret.first );
404  ConstraintT boundB( var, carl::Relation::GEQ, Settings::exclude_negative_numbers ? Rational(0) : Rational(-Rational( mHalfOfCurrentWidth )) );
405  ret = addToICP( FormulaT( boundB ), false );
406  if( ret.second )
407  icpsAddedBounds.push_back( ret.first );
408  }
409  else
410  {
411  Rational currentWidth = Rational(2)*mHalfOfCurrentWidth;
412  bool intervalHalfOpen = vb->second.lower_bound_type() == carl::BoundType::INFTY || vb->second.upper_bound_type() == carl::BoundType::INFTY;
413  if( intervalHalfOpen || currentWidth <= (vb->second.lower_bound_type() != carl::BoundType::INFTY ? Rational(-vb->second.lower()) : vb->second.upper()) )
414  {
415  auto ret = addToICP( FormulaT( ConstraintT( var, carl::Relation::LESS, currentWidth ) ), false );
416  if( ret.second )
417  icpsAddedBounds.push_back( ret.first );
418  }
419  }
420  }
421  Answer icpResult = mICP->check();
422  if( icpResult == UNSAT )
423  {
424  useInfSubsetIfNoAddedBoundsAreContained( *mICP, _addedBounds );
425  return false;
426  }
427  else
428  {
429  EvalRationalIntervalMap newvarBounds = mICP->getCurrentBoxAsIntervals();
430  for( const auto& varToInterval : newvarBounds )
431  {
432  const RationalInterval& vi = varToInterval.second;
433  if( vi.lower_bound_type() == carl::BoundType::STRICT )
434  {
435  auto res = addSubformulaToPassedFormula( FormulaT( ConstraintT( varToInterval.first, carl::Relation::GREATER, vi.lower() ) ) );
436  if( res.second )
437  addBound( _addedBounds, res.first );
438  }
439  else
440  {
441  assert( vi.lower_bound_type() == carl::BoundType::WEAK );
442  auto res = addSubformulaToPassedFormula( FormulaT( ConstraintT( varToInterval.first, carl::Relation::GEQ, vi.lower() ) ) );
443  if( res.second )
444  addBound( _addedBounds, res.first );
445  }
446  if( vi.upper_bound_type() == carl::BoundType::STRICT )
447  {
448  auto res = addSubformulaToPassedFormula( FormulaT( ConstraintT( varToInterval.first, carl::Relation::LESS, vi.upper() ) ) );
449  if( res.second )
450  addBound( _addedBounds, res.first );
451  }
452  else
453  {
454  assert( vi.upper_bound_type() == carl::BoundType::WEAK );
455  auto res = addSubformulaToPassedFormula( FormulaT( ConstraintT( varToInterval.first, carl::Relation::LEQ, vi.upper() ) ) );
456  if( res.second )
457  addBound( _addedBounds, res.first );
458  }
459  }
460  }
461  for( const auto& icpFormIter : icpsAddedBounds )
462  {
463  mICP->remove( icpFormIter );
464  mICPFormula->erase( icpFormIter );
465  }
466  }
467  else
468  {
469  // For each variable x add the bounds x >= -mHalfOfCurrentWidth and x <= mHalfOfCurrentWidth
470  for( carl::Variable::Arg var : _allArithmeticVariables )
471  {
472  auto vb = _varBounds.find( var );
473  if( (vb == _varBounds.end() || (vb->second.lower_bound_type() == carl::BoundType::INFTY && vb->second.upper_bound_type() == carl::BoundType::INFTY)) )
474  {
475  // Unbounded variable v. Add: mHalfONfCurrentWidth <= v < mHalfOfCurrentWidth
476  ConstraintT boundA( var, carl::Relation::LESS, Settings::exclude_negative_numbers ? Rational(2)*mHalfOfCurrentWidth : Rational( mHalfOfCurrentWidth ) );
477  auto res = addSubformulaToPassedFormula( FormulaT( boundA ) );
478  if( res.second )
479  addBound( _addedBounds, res.first );
480  ConstraintT boundB( var, carl::Relation::GEQ, Settings::exclude_negative_numbers ? Rational(0) : Rational(-Rational( mHalfOfCurrentWidth )) );
481  res = addSubformulaToPassedFormula( FormulaT( boundB ) );
482  if( res.second )
483  addBound( _addedBounds, res.first );
484  }
485  else
486  {
487  Rational currentWidth = Rational(2)*mHalfOfCurrentWidth;
488  bool intervalHalfOpen = vb->second.lower_bound_type() == carl::BoundType::INFTY || vb->second.upper_bound_type() == carl::BoundType::INFTY;
489  if( intervalHalfOpen || currentWidth <= (vb->second.lower_bound_type() != carl::BoundType::INFTY ? Rational(-vb->second.lower()) : vb->second.upper()) )
490  {
491  auto res = addSubformulaToPassedFormula( FormulaT( ConstraintT( var, carl::Relation::LESS, currentWidth ) ) );
492  if( res.second )
493  addBound( _addedBounds, res.first );
494  }
495  }
496  }
497  }
498  return true;
499  }
500 
501  template<class Settings>
502  void IncWidthModule<Settings>::addBound( std::vector<ModuleInput::iterator>& _addedBounds, ModuleInput::iterator _iterToBound ) const
503  {
504  _addedBounds.push_back( _iterToBound );
505  #ifdef DEBUG_INC_WIDTH_MODULE
506  std::cout << " add " << _iterToBound->formula() << std::endl;
507  #endif
508  }
509 
510  template<class Settings>
511  void IncWidthModule<Settings>::useInfSubsetIfNoAddedBoundsAreContained( const Module& _module, const std::vector<ModuleInput::iterator>& _addedBounds )
512  {
513  const std::vector<FormulaSetT>& backendsInfsubsets = _module.infeasibleSubsets();
514  assert( !backendsInfsubsets.empty() );
515  for( std::vector<FormulaSetT>::const_iterator infSubSet = backendsInfsubsets.begin(); infSubSet != backendsInfsubsets.end(); ++infSubSet )
516  {
517  auto addedBound = _addedBounds.begin();
518  for( ; addedBound != _addedBounds.end(); ++addedBound )
519  {
520  if( std::find( infSubSet->begin(), infSubSet->end(), (*addedBound)->formula() ) != infSubSet->end() )
521  break;
522  }
523  if( addedBound == _addedBounds.end() )
524  {
525  mInfeasibleSubsets.emplace_back();
526  for( const auto& cons : *infSubSet )
527  getOrigins( cons, mInfeasibleSubsets.back() );
528  }
529  }
530  }
531 
532  template<class Settings>
533  void IncWidthModule<Settings>::reset()
534  {
535  mRestartCheck = true;
536  clearPassedFormula();
537  mVariableShifts.clear();
538  mHalfOfCurrentWidth = carl::pow( Rational(Settings::increment), Settings::start_width-1 );
539  }
540 }
541 
542