2 * @file IncWidthModule.tpp
3 * @author Florian Corzilius <corzilius@cs.rwth-aachen.de>
6 * Created on 2015-06-29.
9 #include "IncWidthModule.h"
10 #include <carl-formula/formula/functions/Substitution.h>
12 //#define DEBUG_INC_WIDTH_MODULE
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 ) ),
27 mICPFormula( nullptr ),
30 mICPFormulaPositions()
32 if( Settings::use_icp )
34 mICPFormula = new ModuleInput();
35 mICPFoundAnswer.push_back( new std::atomic_bool( false ) );
36 mICP = new ICPModule<ICPSettings4>( mICPFormula, mICPFoundAnswer );
41 template<class Settings>
42 IncWidthModule<Settings>::~IncWidthModule()
44 if( Settings::use_icp )
46 while( !mICPFoundAnswer.empty() )
48 std::atomic_bool* toDel = mICPFoundAnswer.back();
49 mICPFoundAnswer.pop_back();
52 mICPFoundAnswer.clear();
58 template<class Settings>
59 bool IncWidthModule<Settings>::addCore( ModuleInput::const_iterator _subformula )
61 if( _subformula->formula().type() == carl::FormulaType::CONSTRAINT )
63 if( Settings::use_icp )
64 addToICP( _subformula->formula() );
67 if( mVarBounds.addBound( _subformula->formula().constraint(), _subformula->formula() ) )
73 return !mVarBounds.isConflicting();
76 template<class Settings>
77 void IncWidthModule<Settings>::removeCore( ModuleInput::const_iterator _subformula )
79 if( _subformula->formula().type() == carl::FormulaType::CONSTRAINT )
81 if( Settings::use_icp )
82 removeFromICP( _subformula->formula() );
85 if( mVarBounds.removeBound( _subformula->formula().constraint(), _subformula->formula() ) )
93 template<class Settings>
94 std::pair<ModuleInput::iterator,bool> IncWidthModule<Settings>::addToICP( const FormulaT& _formula, bool _guaranteedNew )
96 #ifdef DEBUG_INC_WIDTH_MODULE
97 std::cout << "Add to internal ICPModule: " << _formula << std::endl;
99 auto ret = mICPFormula->add( _formula, false );
100 assert( !_guaranteedNew || ret.second );
103 assert( mICPFormulaPositions.find( _formula ) == mICPFormulaPositions.end() );
104 mICPFormulaPositions.emplace( _formula, ret.first );
106 mICP->inform( _formula );
107 mICP->add( ret.first );
111 template<class Settings>
112 void IncWidthModule<Settings>::removeFromICP( const FormulaT& _formula )
114 #ifdef DEBUG_INC_WIDTH_MODULE
115 std::cout << "Remove from internal ICPModule: " << _formula << std::endl;
117 auto icpformpos = mICPFormulaPositions.find( _formula );
118 mICP->remove( icpformpos->second );
119 mICPFormula->erase( icpformpos->second );
120 mICPFormulaPositions.erase( icpformpos );
123 template<class Settings>
124 void IncWidthModule<Settings>::clearICP()
126 #ifdef DEBUG_INC_WIDTH_MODULE
127 std::cout << "Clear internal ICPModule!" << std::endl;
129 for( const auto& icpformpos : mICPFormulaPositions )
131 mICP->remove( icpformpos.second );
132 mICPFormula->erase( icpformpos.second );
134 mICPFormulaPositions.clear();
137 template<class Settings>
138 void IncWidthModule<Settings>::updateModel() const
141 if( solverState() == SAT )
144 for( auto& ass : mModel )
146 auto varShiftIter = mVariableShifts.find( ass.first.asVariable() );
147 if( varShiftIter != mVariableShifts.end() )
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() )
153 mModel.assign(ass.first, (varWithNegCoeff ? Rational(-ass.second.asRational()) : ass.second.asRational()) + varShiftIter->second.constant_part());
155 else if( ass.second.isSubstitution() )
157 if( varWithNegCoeff )
158 ass.second.asSubstitution()->multiplyBy( -1 );
160 ass.second.asSubstitution()->add( varShiftIter->second.constant_part() );
162 else if( ass.second.isSqrtEx() )
164 mModel.assign(ass.first, (varWithNegCoeff ? ass.second.asSqrtEx()*SqrtEx( Poly( -1 ) ) : ass.second.asSqrtEx()) + SqrtEx( Poly( varShiftIter->second.constant_part() ) ));
166 else // ass.second.isRAN()
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 );
177 template<class Settings>
178 Answer IncWidthModule<Settings>::checkCore()
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;
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 )
190 Answer icpResult = mICP->check();
191 if( icpResult == UNSAT )
193 for( auto& infsubset : mICP->infeasibleSubsets() )
194 mInfeasibleSubsets.push_back( infsubset );
198 EvalRationalIntervalMap varBounds = Settings::use_icp ? mICP->getCurrentBoxAsIntervals() : mVarBounds.getEvalIntervalMap();
201 #ifdef DEBUG_INC_WIDTH_MODULE
202 std::cout << "Shift variables with the domains:" << std::endl;
203 for( auto& v : arithVars )
205 auto it = varBounds.find( v );
206 if( it == varBounds.end() )
207 std::cout << " " << v << " in " << RationalInterval::unbounded_interval() << std::endl;
209 std::cout << " " << v << " in " << it->second << std::endl;
211 std::cout << "Results in:" << std::endl;
213 // Determine the shifts according to the initial variable bounds
214 rf = rReceivedFormula().begin();
215 mRestartCheck = false;
216 for( const auto& vb : varBounds )
218 if( vb.second.lower_bound_type() != carl::BoundType::INFTY )
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;
226 else if( vb.second.upper_bound_type() != carl::BoundType::INFTY )
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;
236 // add all received formula after performing the shift to the passed formula
237 if( Settings::use_icp )
239 for( ; rf != rReceivedFormula().end(); ++rf )
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 );
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
250 if( anAnswerFound() )
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 ) )
255 mHalfOfCurrentWidth /= Rational(Settings::increment);
256 #ifdef DEBUG_INC_WIDTH_MODULE
257 std::cout << "Reached maximal width" << std::endl;
261 if( tryToAddBounds( varBounds, arithVars, addedBounds ) )
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() )
266 #ifdef DEBUG_INC_WIDTH_MODULE
267 std::cout << "No bounds added, so go for final check." << std::endl;
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;
276 mHalfOfCurrentWidth *= Rational(Settings::increment);
277 Answer ans = runBackends();
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;
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() )
291 if( (*backend)->solverState() == UNSAT )
292 useInfSubsetIfNoAddedBoundsAreContained( **backend, addedBounds );
299 // Found such an infeasible subset, then return.
300 if( !mInfeasibleSubsets.empty() )
302 #ifdef DEBUG_INC_WIDTH_MODULE
303 std::cout << "Found an infeasible subset not containing introduced bounds!" << std::endl;
307 // Remove the bounds.
308 #ifdef DEBUG_INC_WIDTH_MODULE
309 std::cout << "Remove added bounds!" << std::endl;
311 while( !addedBounds.empty() )
313 #ifdef DEBUG_INC_WIDTH_MODULE
314 std::cout << "Remove bound: " << addedBounds.back()->formula() << std::endl;
316 eraseSubformulaFromPassedFormula( addedBounds.back(), true );
317 addedBounds.pop_back();
320 if( Settings::exclude_searched_space )
322 // Add the not yet covered search space.
324 for( carl::Variable::Arg var : arithVars )
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) )
329 formulas.push_back( FormulaT( ConstraintT( var, carl::Relation::GREATER, Rational( mHalfOfCurrentWidth ) ) ) );
330 formulas.push_back( FormulaT( ConstraintT( var, carl::Relation::LEQ, -Rational( mHalfOfCurrentWidth ) ) ) );
334 if( vb->second.lower_bound_type() != carl::BoundType::INFTY )
335 formulas.push_back( FormulaT( ConstraintT( var, carl::Relation::GEQ, Rational(2)*mHalfOfCurrentWidth ) ) );
337 formulas.push_back( FormulaT( ConstraintT( var, carl::Relation::LEQ, -(Rational(2)*mHalfOfCurrentWidth) ) ) );
340 if( formulas.size() > 1 )
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;
348 else if( !formulas.empty() )
350 addSubformulaToPassedFormula( formulas.back() );
351 #ifdef DEBUG_INC_WIDTH_MODULE
352 std::cout << " add remainig space " << formulas.back() << std::endl;
356 if( Settings::use_icp )
359 for( const auto& rformula : rReceivedFormula() )
360 addToICP( rformula.formula() );
363 Answer ans = runBackends();
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;
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 );
384 template<class Settings>
385 bool IncWidthModule<Settings>::tryToAddBounds( const EvalRationalIntervalMap& _varBounds, const carl::Variables& _allArithmeticVariables, std::vector<ModuleInput::iterator>& _addedBounds )
387 #ifdef DEBUG_INC_WIDTH_MODULE
388 std::cout << "Try to add bounds:" << std::endl;
390 if( Settings::use_icp )
392 std::vector<ModuleInput::iterator> icpsAddedBounds;
393 // First try to contract variable domains with ICP.
394 for( carl::Variable::Arg var : _allArithmeticVariables )
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)) )
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 );
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 );
407 icpsAddedBounds.push_back( ret.first );
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()) )
415 auto ret = addToICP( FormulaT( ConstraintT( var, carl::Relation::LESS, currentWidth ) ), false );
417 icpsAddedBounds.push_back( ret.first );
421 Answer icpResult = mICP->check();
422 if( icpResult == UNSAT )
424 useInfSubsetIfNoAddedBoundsAreContained( *mICP, _addedBounds );
429 EvalRationalIntervalMap newvarBounds = mICP->getCurrentBoxAsIntervals();
430 for( const auto& varToInterval : newvarBounds )
432 const RationalInterval& vi = varToInterval.second;
433 if( vi.lower_bound_type() == carl::BoundType::STRICT )
435 auto res = addSubformulaToPassedFormula( FormulaT( ConstraintT( varToInterval.first, carl::Relation::GREATER, vi.lower() ) ) );
437 addBound( _addedBounds, res.first );
441 assert( vi.lower_bound_type() == carl::BoundType::WEAK );
442 auto res = addSubformulaToPassedFormula( FormulaT( ConstraintT( varToInterval.first, carl::Relation::GEQ, vi.lower() ) ) );
444 addBound( _addedBounds, res.first );
446 if( vi.upper_bound_type() == carl::BoundType::STRICT )
448 auto res = addSubformulaToPassedFormula( FormulaT( ConstraintT( varToInterval.first, carl::Relation::LESS, vi.upper() ) ) );
450 addBound( _addedBounds, res.first );
454 assert( vi.upper_bound_type() == carl::BoundType::WEAK );
455 auto res = addSubformulaToPassedFormula( FormulaT( ConstraintT( varToInterval.first, carl::Relation::LEQ, vi.upper() ) ) );
457 addBound( _addedBounds, res.first );
461 for( const auto& icpFormIter : icpsAddedBounds )
463 mICP->remove( icpFormIter );
464 mICPFormula->erase( icpFormIter );
469 // For each variable x add the bounds x >= -mHalfOfCurrentWidth and x <= mHalfOfCurrentWidth
470 for( carl::Variable::Arg var : _allArithmeticVariables )
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)) )
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 ) );
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 ) );
483 addBound( _addedBounds, res.first );
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()) )
491 auto res = addSubformulaToPassedFormula( FormulaT( ConstraintT( var, carl::Relation::LESS, currentWidth ) ) );
493 addBound( _addedBounds, res.first );
501 template<class Settings>
502 void IncWidthModule<Settings>::addBound( std::vector<ModuleInput::iterator>& _addedBounds, ModuleInput::iterator _iterToBound ) const
504 _addedBounds.push_back( _iterToBound );
505 #ifdef DEBUG_INC_WIDTH_MODULE
506 std::cout << " add " << _iterToBound->formula() << std::endl;
510 template<class Settings>
511 void IncWidthModule<Settings>::useInfSubsetIfNoAddedBoundsAreContained( const Module& _module, const std::vector<ModuleInput::iterator>& _addedBounds )
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 )
517 auto addedBound = _addedBounds.begin();
518 for( ; addedBound != _addedBounds.end(); ++addedBound )
520 if( std::find( infSubSet->begin(), infSubSet->end(), (*addedBound)->formula() ) != infSubSet->end() )
523 if( addedBound == _addedBounds.end() )
525 mInfeasibleSubsets.emplace_back();
526 for( const auto& cons : *infSubSet )
527 getOrigins( cons, mInfeasibleSubsets.back() );
532 template<class Settings>
533 void IncWidthModule<Settings>::reset()
535 mRestartCheck = true;
536 clearPassedFormula();
537 mVariableShifts.clear();
538 mHalfOfCurrentWidth = carl::pow( Rational(Settings::increment), Settings::start_width-1 );