2 * @file IntBlastModule.tpp
3 * @author Andreas Krueger <andreas.krueger@rwth-aachen.de>
4 * @author Florian Corzilius <corzilius@cs.rwth-aachen.de>
7 #include "IntBlastModule.h"
9 #define INTBLAST_DEBUG_ENABLED 0
10 #define INTBLAST_DEBUG(x) do { \
11 if (INTBLAST_DEBUG_ENABLED) { std::cout << "[IntBlast] " << x << std::endl; } \
20 template<class Settings>
21 IntBlastModule<Settings>::IntBlastModule( const ModuleInput* _formula, Conditionals& _conditionals, Manager* _manager ):
22 Module( _formula, _conditionals, _manager ),
24 mBoundsInRestriction(),
26 mNonlinearInputVariables(),
27 mpICPInput(new ModuleInput()),
28 mICPFoundAnswer( std::vector< std::atomic_bool* >( 1, new std::atomic_bool( false ) ) ),
29 mICP(mpICPInput, mICPFoundAnswer),
30 mConstraintFromBounds(carl::fresh_boolean_variable()),
31 mProcessedFormulasFromICP(),
32 mpBVInput(new ModuleInput()),
33 mpBVSolver(new BVSolver()),
35 mSolutionOrigin(SolutionOrigin::NONE),
42 // TODO: Do we have to do some more initialization stuff here? Settings?
49 template<class Settings>
50 IntBlastModule<Settings>::~IntBlastModule()
53 while( !mICPFoundAnswer.empty() )
55 std::atomic_bool* toDel = mICPFoundAnswer.back();
56 mICPFoundAnswer.pop_back();
59 mICPFoundAnswer.clear();
65 template<class Settings>
66 bool IntBlastModule<Settings>::informCore( const FormulaT& _constraint )
68 informBackends(_constraint);
72 template<class Settings>
73 void IntBlastModule<Settings>::init()
76 template<class Settings>
77 bool IntBlastModule<Settings>::addCore( ModuleInput::const_iterator _subformula )
79 const FormulaT& formula = _subformula->formula();
80 INTBLAST_DEBUG("ADD " << formula);
82 std::vector<ConstraintT> containedConstraints;
83 carl::arithmetic_constraints(formula, containedConstraints);
86 * Steps that are applied for every constraint in formula
89 for(const ConstraintT& constraint : containedConstraints) {
90 // Retrieve all arithmetic variables in formula
91 carl::carlVariables nonlinearVariablesInFormula;
92 const Poly& poly = constraint.lhs();
93 carl::Variables variablesInFormula = carl::arithmetic_variables(formula).as_set();
94 for(auto termIt = poly.begin();termIt != poly.end();++termIt) {
95 if(termIt->num_variables() > 1 || ! termIt->is_linear()) {
96 carl::variables(*termIt, nonlinearVariablesInFormula);
100 // Update 'mInputVariables' and 'mNonlinearInputVariables' sets
101 for(auto& variable : variablesInFormula) {
102 mInputVariables.add(variable, formula);
104 for(auto& variable : nonlinearVariablesInFormula) {
105 mNonlinearInputVariables.add(variable, formula);
108 // Introduce substitutes in ICP
109 if(Settings::apply_icp) {
110 addSubstitutesToICP(constraint, formula);
113 // Update mPolyParents (child->parent relationship)
114 addPolyParents(constraint);
118 * Steps that are applied if the formula is only a single constraint
121 if(formula.type() == carl::FormulaType::CONSTRAINT) {
122 // Update mBoundsFromInput using the new formula
123 mBoundsFromInput.addBound(formula.constraint(), formula);
125 // Pass global formula to ICP
126 if(Settings::apply_icp) {
127 addConstraintFormulaToICP(formula);
132 * Steps that are applied for the whole formula
135 // Schedule formula for encoding to BV logic
136 mFormulasToEncode.insert(formula);
138 // Add new formula to backend
139 addReceivedSubformulaToPassedFormula(_subformula);
141 return ! mBoundsFromInput.isConflicting();
144 template<class Settings>
145 void IntBlastModule<Settings>::removeCore( ModuleInput::const_iterator _subformula )
147 const FormulaT& formula = _subformula->formula();
148 INTBLAST_DEBUG("REMOVE " << formula);
150 mInputVariables.removeOrigin(formula);
151 mNonlinearInputVariables.removeOrigin(formula);
153 if(formula.type() == carl::FormulaType::CONSTRAINT) {
154 mBoundsFromInput.removeBound(formula.constraint(), formula);
156 // mBoundsInRestriction: updated by updateBoundsFromICP() in next check
158 if(Settings::apply_icp) {
159 removeOriginFromICP(formula);
161 removeOriginFromBV(formula);
163 mFormulasToEncode.erase(formula);
166 template<class Settings>
167 void IntBlastModule<Settings>::updateModel() const
170 if(solverState() == SAT)
172 switch(mSolutionOrigin) {
173 case SolutionOrigin::ICP:
174 updateModelFromICP();
176 case SolutionOrigin::BV:
179 case SolutionOrigin::BACKEND:
188 template<class Settings>
189 carl::BVTerm IntBlastModule<Settings>::encodeBVConstant(const Integer& _constant, const BVAnnotation& _type) const
191 assert(_constant >= _type.lower_bound() && _constant <= _type.upper_bound());
192 carl::BVValue constValue(_type.width(), _constant - _type.offset());
193 return carl::BVTerm(carl::BVTermType::CONSTANT, constValue);
196 template<class Settings>
197 Integer IntBlastModule<Settings>::decodeBVConstant(const carl::BVValue& _value, const BVAnnotation& _type) const
200 Integer converted(0);
202 // Unsigned conversion from binary to integer
203 for(std::size_t position = 0;position<_value.width();++position) {
204 if(_value[position]) {
205 converted += summand;
207 summand *= Integer(2);
210 // For negative numbers in two's complement, subtract 2^width from result
211 if(_type.isSigned() && _value[_value.width()-1]) {
212 converted -= summand;
215 return converted + _type.offset();
218 template<class Settings>
219 carl::BVTerm IntBlastModule<Settings>::resizeBVTerm(const AnnotatedBVTerm& _term, std::size_t _width) const
221 assert(_width >= _term.type().width());
223 if(_width == _term.type().width()) {
226 carl::BVTermType ext = (_term.type().isSigned() ? carl::BVTermType::EXT_S : carl::BVTermType::EXT_U);
227 return carl::BVTerm(ext, _term.term(), _width - _term.type().width());
231 template<class Settings>
232 BlastedPoly IntBlastModule<Settings>::blastSum(const BlastedPoly& _summand1, const BlastedPoly& _summand2)
234 if(_summand1.is_constant() && _summand2.is_constant()) {
235 return BlastedPoly(_summand1.constant() + _summand2.constant());
236 } else if(_summand1.is_constant() || _summand2.is_constant()) {
237 const BlastedPoly& constantSummand = (_summand1.is_constant() ? _summand1 : _summand2);
238 const BlastedPoly& termSummand = (_summand1.is_constant() ? _summand2 : _summand1);
239 const BVAnnotation& termType = termSummand.term().type();
241 return BlastedPoly(AnnotatedBVTerm(termType.withOffset(termType.offset() + constantSummand.constant()), termSummand.term().term()));
243 BVAnnotation annotation = BVAnnotation::forSum(_summand1.term().type(), _summand2.term().type());
245 carl::BVTerm bvSummand1 = resizeBVTerm(_summand1.term(), annotation.width());
246 carl::BVTerm bvSummand2 = resizeBVTerm(_summand2.term(), annotation.width());
247 carl::BVTerm bvSum(carl::BVTermType::ADD, bvSummand1, bvSummand2);
249 if(Settings::allow_encoding_into_complex_bvterms) {
250 return BlastedPoly(AnnotatedBVTerm(annotation, bvSum), FormulasT());
252 AnnotatedBVTerm sum(annotation);
253 FormulasT constraints;
254 constraints.push_back(FormulaT(carl::BVConstraint::create(carl::BVCompareRelation::EQ, bvSum, sum.term())));
255 return BlastedPoly(sum, constraints);
260 template<class Settings>
261 BlastedPoly IntBlastModule<Settings>::blastProduct(const BlastedPoly& _factor1, const BlastedPoly& _factor2)
263 if(_factor1.is_constant() && _factor2.is_constant()) {
264 return BlastedPoly(_factor1.constant() * _factor2.constant());
265 } else if(_factor1.is_constant() || _factor2.is_constant()) {
266 const BlastedPoly& constantFactor = (_factor1.is_constant() ? _factor1 : _factor2);
267 const BlastedPoly& variableFactor = (_factor1.is_constant() ? _factor2 : _factor1);
268 const BVAnnotation& variableType = variableFactor.term().type();
270 bool constantNegative = constantFactor.constant() < 0;
271 BVAnnotation constantType(chooseWidth(constantFactor.constant(), 0, constantNegative), constantNegative, 0);
272 BVAnnotation annotation = BVAnnotation::forProduct(variableType.withOffset(0), constantType).withOffset(variableType.offset() * constantFactor.constant());
274 carl::BVTerm bvConstantFactor = encodeBVConstant(constantFactor.constant(), annotation);
275 carl::BVTerm bvVariableFactor = resizeBVTerm(variableFactor.term(), annotation.width());
276 carl::BVTerm bvProduct(carl::BVTermType::MUL, bvConstantFactor, bvVariableFactor);
278 if(Settings::allow_encoding_into_complex_bvterms) {
279 return BlastedPoly(AnnotatedBVTerm(annotation, bvProduct), FormulasT());
281 AnnotatedBVTerm product(annotation);
282 FormulasT constraints;
283 constraints.push_back(FormulaT(carl::BVConstraint::create(carl::BVCompareRelation::EQ, bvProduct, product.term())));
284 return BlastedPoly(product, constraints);
287 BVAnnotation annotation = BVAnnotation::forProduct(_factor1.term().type(), _factor2.term().type());
289 carl::BVTerm bvFactor1 = resizeBVTerm(_factor1.term(), annotation.width());
290 carl::BVTerm bvFactor2 = resizeBVTerm(_factor2.term(), annotation.width());
291 carl::BVTerm bvProduct(carl::BVTermType::MUL, bvFactor1, bvFactor2);
293 if(Settings::allow_encoding_into_complex_bvterms) {
294 return BlastedPoly(AnnotatedBVTerm(annotation, bvProduct), FormulasT());
296 AnnotatedBVTerm product(annotation);
297 FormulasT constraints;
298 constraints.push_back(FormulaT(carl::BVConstraint::create(carl::BVCompareRelation::EQ, bvProduct, product.term())));
299 return BlastedPoly(product, constraints);
304 template<class Settings>
305 const BlastedConstr& IntBlastModule<Settings>::blastConstrTree(const ConstrTree& _constraint, FormulasT& _collectedFormulas)
307 const BlastedPoly& left = blastPolyTree(_constraint.left(), _collectedFormulas);
308 const BlastedPoly& right = blastPolyTree(_constraint.right(), _collectedFormulas);
310 auto blastedConstrIt = mConstrBlastings.find(_constraint.constraint());
312 if(blastedConstrIt != mConstrBlastings.end()) {
313 // This tree has already been encoded. Add its formulas to _collectedFormulas
314 // and return the previously blasted node.
315 const FormulasT& constraints = blastedConstrIt->second.constraints();
316 _collectedFormulas.insert(_collectedFormulas.end(), constraints.begin(), constraints.end());
317 return blastedConstrIt->second;
320 BlastedConstr blasted;
321 bool simpleBlast = false;
323 if(left.is_constant() && right.is_constant()) {
324 blasted = BlastedConstr(evaluateRelation(_constraint.relation(), left.constant(), right.constant()));
327 assert(left.is_constant() || right.is_constant()); // Our ConstrTree is constructed like that
329 const BlastedPoly& constantPol = left.is_constant() ? left : right;
330 const BlastedPoly& variablePol = left.is_constant() ? right : left;
331 const Integer& constant = constantPol.constant();
333 // Assuming "variable op constant" now (i.e., constant is on the right side)
334 carl::Relation relation = left.is_constant() ? carl::turn_around(_constraint.relation()) : _constraint.relation();
336 if(relation == carl::Relation::EQ || relation == carl::Relation::NEQ) {
337 // is the constant outside the range of the variable?
338 if(constant < variablePol.lower_bound() || constant > variablePol.upper_bound()) {
339 blasted = BlastedConstr(_constraint.relation() == carl::Relation::NEQ);
342 } else { // relation is one of LESS, GREATER, LEQ, GEQ
343 bool lowerEvaluation = evaluateRelation(_constraint.relation(), left.lower_bound(), constant);
344 bool upperEvaluation = evaluateRelation(_constraint.relation(), left.upper_bound(), constant);
346 if(lowerEvaluation == upperEvaluation) {
347 blasted = BlastedConstr(lowerEvaluation);
353 // The constraint cannot be decided from the range of variablePol alone.
354 // Translate into BV logic
356 carl::BVTerm bvConstant = encodeBVConstant(constantPol.constant(), variablePol.term().type());
358 carl::BVCompareRelation rel;
361 case carl::Relation::EQ:
362 rel = carl::BVCompareRelation::EQ;
364 case carl::Relation::NEQ:
365 rel = carl::BVCompareRelation::NEQ;
367 case carl::Relation::LESS:
368 rel = (variablePol.term().type().isSigned() ? carl::BVCompareRelation::SLT : carl::BVCompareRelation::ULT);
370 case carl::Relation::GREATER:
371 rel = (variablePol.term().type().isSigned() ? carl::BVCompareRelation::SGT : carl::BVCompareRelation::UGT);
373 case carl::Relation::LEQ:
374 rel = (variablePol.term().type().isSigned() ? carl::BVCompareRelation::SLE : carl::BVCompareRelation::ULE);
376 case carl::Relation::GEQ:
377 rel = (variablePol.term().type().isSigned() ? carl::BVCompareRelation::SGE : carl::BVCompareRelation::UGE);
383 blasted = BlastedConstr(FormulaT(carl::BVConstraint::create(rel, left.term().term(), bvConstant)));
387 _collectedFormulas.insert(_collectedFormulas.end(), blasted.constraints().begin(), blasted.constraints().end());
388 return mConstrBlastings.insert(std::make_pair(_constraint.constraint(), blasted)).first->second;
391 template<class Settings>
392 bool IntBlastModule<Settings>::evaluateRelation(carl::Relation _relation, const Integer& _first, const Integer& _second) const
395 case carl::Relation::EQ: return _first == _second;
396 case carl::Relation::NEQ: return _first != _second;
397 case carl::Relation::LESS: return _first < _second;
398 case carl::Relation::GREATER: return _first > _second;
399 case carl::Relation::LEQ: return _first <= _second;
400 case carl::Relation::GEQ: return _first >= _second;
406 template<class Settings>
407 const BlastedPoly& IntBlastModule<Settings>::blastPolyTree(const PolyTree& _poly, FormulasT& _collectedFormulas)
409 const BlastedPoly* left = nullptr;
410 const BlastedPoly* right = nullptr;
412 if(_poly.type() == PolyTree::Type::SUM || _poly.type() == PolyTree::Type::PRODUCT) {
413 left = &(blastPolyTree(_poly.left(), _collectedFormulas));
414 right = &(blastPolyTree(_poly.right(), _collectedFormulas));
417 auto blastedPolyIt = mPolyBlastings.find(_poly.poly());
419 if(blastedPolyIt != mPolyBlastings.end()) {
420 // This tree has already been encoded. Add its formulas to _collectedFormulas
421 // and return the previously blasted node.
422 const FormulasT& constraints = blastedPolyIt->second.constraints();
423 _collectedFormulas.insert(_collectedFormulas.end(), constraints.begin(), constraints.end());
424 return blastedPolyIt->second;
427 // Tree has not yet been encoded (only its subtrees, if any).
431 switch(_poly.type()) {
432 case PolyTree::Type::CONSTANT: {
433 blasted = BlastedPoly(_poly.constant());
436 case PolyTree::Type::VARIABLE: {
437 // This should not happen.
438 // Variables should have been blasted by 'blastVariable' method upfront.
442 case PolyTree::Type::SUM:
443 case PolyTree::Type::PRODUCT: {
444 BlastedPoly intermediate = (_poly.type() == PolyTree::Type::SUM) ?
445 blastSum(*left, *right) :
446 blastProduct(*left, *right);
448 if(Settings::apply_icp) {
449 // Obtain range from ICP substitute
450 carl::Variable substitute = mSubstitutes.at(_poly.poly());
451 IntegerInterval interval = get_num(mBoundsInRestriction.getInterval(substitute));
452 if(interval.lower_bound_type() == carl::BoundType::WEAK && interval.upper_bound_type() == carl::BoundType::WEAK) {
453 auto shrunk = shrinkToRange(intermediate, interval);
454 blasted = shrunk.first;
456 mShrunkPolys.insert(_poly.poly());
459 INTBLAST_DEBUG("Bad ICP interval for " << _poly.poly() << ": " << interval);
461 blasted = intermediate;
464 blasted = intermediate;
474 _collectedFormulas.insert(_collectedFormulas.end(), blasted.constraints().begin(), blasted.constraints().end());
475 return mPolyBlastings.insert(std::make_pair(_poly.poly(), blasted)).first->second;
478 template<class Settings>
479 std::pair<BlastedPoly, bool> IntBlastModule<Settings>::shrinkToRange(const BlastedPoly& _input, const IntegerInterval& _interval) const
481 if(_interval.lower_bound_type() != carl::BoundType::WEAK || _interval.upper_bound_type() != carl::BoundType::WEAK) {
485 assert(_input.lower_bound() <= _interval.lower() && _input.upper_bound() >= _interval.upper());
487 if(_interval.is_point_interval()) {
488 if(_input.is_constant()) {
489 return std::make_pair(_input, false);
491 FormulasT constraints(_input.constraints());
492 carl::BVTerm bvConstant = encodeBVConstant(_interval.lower(), _input.term().type());
493 constraints.push_back(FormulaT(carl::BVConstraint::create(carl::BVCompareRelation::EQ, _input.term().term(), bvConstant)));
494 return std::make_pair(BlastedPoly(_interval.lower(), constraints), true);
498 // Interval is not a point interval, and interval is included in _input interval.
499 // This also implies that _input is not constant.
500 // Let's see whether resizing actually has any benefits.
502 const BVAnnotation& inputType = _input.term().type();
504 std::size_t newWidth = std::max(
505 chooseWidth(_interval.lower() - inputType.offset(), 0, inputType.isSigned()),
506 chooseWidth(_interval.upper() - inputType.offset(), 0, inputType.isSigned())
509 assert(newWidth <= inputType.width());
510 if(newWidth == inputType.width()) {
511 // Resizing is not possible, return _input without modifications
512 return std::make_pair(_input, false);
515 // Resize to a new, smaller BlastedPoly
516 FormulasT constraints(_input.constraints());
517 AnnotatedBVTerm newTerm(inputType.withWidth(newWidth));
519 constraints.push_back(FormulaT(carl::BVConstraint::create(carl::BVCompareRelation::EQ,
520 carl::BVTerm(carl::BVTermType::EXTRACT, _input.term().term(), newWidth-1, 0),
523 // add constraint which ensures a safe resizing
524 carl::BVTermType extend = inputType.isSigned() ? carl::BVTermType::EXT_S : carl::BVTermType::EXT_U;
525 constraints.push_back(FormulaT(carl::BVConstraint::create(
526 carl::BVCompareRelation::EQ,
527 carl::BVTerm(extend, newTerm.term(), inputType.width() - newWidth),
531 return std::make_pair(BlastedPoly(newTerm, constraints), true);
534 template<class Settings>
535 FormulasT IntBlastModule<Settings>::blastConstraint(const ConstraintT& _constraint)
537 ConstrTree constraintTree(_constraint);
538 FormulasT blastedFormulas;
540 blastConstrTree(constraintTree, blastedFormulas);
541 return blastedFormulas;
544 template<class Settings>
545 Answer IntBlastModule<Settings>::checkCore()
547 mSolutionOrigin = SolutionOrigin::NONE;
549 // Choose blastings for new variables,
550 // and ensure compatibility of previous blastings for all variables
552 bool reachedMaxWidth = false;
553 for(auto variableWO : mInputVariables)
555 const carl::Variable& variable = variableWO.element();
556 bool linear = ! mNonlinearInputVariables.contains(variable);
557 IntegerInterval interval = get_num(mBoundsFromInput.getInterval(variable));
559 Poly variablePoly(variable);
560 auto blastingIt = mPolyBlastings.find(variablePoly);
561 bool needsBlasting = (blastingIt == mPolyBlastings.end() || reblastingNeeded(blastingIt->second, interval, linear));
565 if(blastingIt != mPolyBlastings.end()) {
566 unblastVariable(variable);
568 switch( blastVariable(variable, interval, linear) )
571 return callBackends();
573 reachedMaxWidth = true;
581 if(INTBLAST_DEBUG_ENABLED) {
582 INTBLAST_DEBUG("Blastings:");
583 for(auto blaPa : mPolyBlastings) {
584 INTBLAST_DEBUG(blaPa.first << " --> " << blaPa.second);
587 INTBLAST_DEBUG("Substitutes:");
588 for(auto substi : mSubstitutes) {
589 INTBLAST_DEBUG(substi.first << " --> " << substi.second);
593 Answer icpAnswer = UNKNOWN;
595 if(Settings::apply_icp) {
597 if(INTBLAST_DEBUG_ENABLED) {
598 INTBLAST_DEBUG("Running ICP on these formulas:");
600 for(const auto& formulaWO : *mpICPInput) {
601 INTBLAST_DEBUG(" - " << formulaWO.formula());
602 for(const auto& origin : formulaWO.origins()) {
603 INTBLAST_DEBUG(" (o) " << origin);
607 icpAnswer = mICP.check();
608 INTBLAST_DEBUG("icpAnswer: " << icpAnswer);
610 if(icpAnswer == SAT && rReceivedFormula().satisfiedBy( mICP.model() ) == 1) {
611 mSolutionOrigin = SolutionOrigin::ICP;
616 if(icpAnswer != UNSAT) {
617 if(Settings::apply_icp) {
618 INTBLAST_DEBUG("Updating bounds from ICP.");
620 updateBoundsFromICP();
623 while(! mFormulasToEncode.empty()) {
624 auto firstFormulaToEncode = mFormulasToEncode.begin();
625 const FormulaT& formulaToEncode = *firstFormulaToEncode;
626 encodeFormulaToBV(formulaToEncode);
627 mFormulasToEncode.erase(firstFormulaToEncode);
630 INTBLAST_DEBUG("Running BV solver.");
632 Answer bvAnswer = mpBVSolver->check();
633 INTBLAST_DEBUG("Answer from BV solver: " << bvAnswer);
635 if(bvAnswer == SAT) {
636 mSolutionOrigin = SolutionOrigin::BV;
641 // At this point, the restriction is unsatisfiable
642 // (determined either by the ICP module or by the BV solver).
645 if( reachedMaxWidth )
647 //updateOutsideRestrictionConstraint(icpAnswer == UNSAT);
648 return callBackends();
650 if( rReceivedFormula().containsRealVariables() )
652 bool originalBoundsCovered = true;
653 for(auto variableWO : mInputVariables)
655 const carl::Variable& variable = variableWO.element();
656 IntegerInterval interval = get_num(mBoundsFromInput.getInterval(variable));
658 Poly variablePoly(variable);
659 auto blastingIt = mPolyBlastings.find( Poly(variable) );
660 assert( blastingIt != mPolyBlastings.end() );
661 const carl::Interval<Integer>& blastBounds = blastingIt->second.term().type().bounds();
662 if( blastBounds != interval && !blastBounds.contains( interval ) )
664 originalBoundsCovered = false;
668 if( originalBoundsCovered )
670 generateTrivialInfeasibleSubset();
676 template<class Settings>
677 Answer IntBlastModule<Settings>::callBackends()
679 INTBLAST_DEBUG("Running backend.");
680 Answer backendAnswer = runBackends();
681 INTBLAST_DEBUG("Answer from backend: " << backendAnswer);
682 mSolutionOrigin = SolutionOrigin::BACKEND;
684 if(backendAnswer == UNSAT) {
685 getInfeasibleSubsets();
688 return backendAnswer;
691 template<class Settings>
692 void IntBlastModule<Settings>::encodeFormulaToBV(const FormulaT& _formula)
694 INTBLAST_DEBUG("Formula " << _formula << " encoded to BV:");
696 FormulasT bitvectorConstraints;
697 std::function<FormulaT(FormulaT)> encodeConstraints = std::bind(&IntBlastModule::encodeConstraintToBV, this, std::placeholders::_1, &bitvectorConstraints);
698 FormulaT bitvectorFormula = carl::visit_result(_formula, encodeConstraints);
700 addFormulaToBV(bitvectorFormula, _formula);
702 for(const FormulaT& bitvectorConstraint : bitvectorConstraints) {
703 addFormulaToBV(bitvectorConstraint, _formula);
707 template<class Settings>
708 FormulaT IntBlastModule<Settings>::encodeConstraintToBV(const FormulaT& _original, FormulasT* _collectedBitvectorConstraints)
710 if(_original.type() == carl::FormulaType::CONSTRAINT && _original.constraint().integer_valued())
712 ConstrTree constraintTree(_original.constraint());
713 const BlastedConstr& blastedConstraint = blastConstrTree(constraintTree, *_collectedBitvectorConstraints);
714 return blastedConstraint.formula();
719 template<class Settings>
720 bool IntBlastModule<Settings>::reblastingNeeded(const BlastedPoly& _previousBlasting, const IntegerInterval& _interval, bool _linear) const
722 if(_previousBlasting.is_constant()) {
723 return ! _interval.is_point_interval() || _interval.lower() != _previousBlasting.constant();
726 const BVAnnotation& previousType = _previousBlasting.term().type();
728 if(previousType.hasOffset() && ! _linear) {
731 return previousType.bounds() != _interval && !previousType.bounds().contains(_interval);
734 template<class Settings>
735 void IntBlastModule<Settings>::unblastVariable(const carl::Variable& _variable)
737 if(Settings::apply_icp) {
738 removeBoundRestrictionsFromICP(_variable);
740 unblastPoly(Poly(_variable));
743 template<class Settings>
744 int IntBlastModule<Settings>::blastVariable( const carl::Variable& _variable, const IntegerInterval& _interval, bool _allowOffset )
746 Poly variablePoly( _variable );
747 if( _interval.is_point_interval() )
749 mPolyBlastings[variablePoly] = BlastedPoly( _interval.lower() );
752 std::size_t maxWidth = Settings::max_variable_encoding_width;
753 _allowOffset = _allowOffset && Settings::use_offsets_in_encoding;
756 * If interval is unbounded:
757 * Start with no offset, signed, maximum width (tempType)
758 * If offset is not allowed
759 * Remove sign if interval is semi-positive
761 * If lower bound of interval is higher than tempType's lower bound, shift range using offset and remove sign
762 * (similar for upper bound)
764 * If interval is bounded:
766 * Lower bound as offset, width "as small as possible" (at most maximum width), unsigned
768 * Make signed iff interval not semipositive
769 * Width "as small as possible" (at most maximum width)
772 BVAnnotation blastedType;
774 if( _interval.lower_bound_type() == carl::BoundType::INFTY || _interval.upper_bound_type() == carl::BoundType::INFTY )
778 BVAnnotation tempType( maxWidth, true, 0 );
780 if( _interval.lower_bound_type() != carl::BoundType::INFTY && _interval.lower() > tempType.lower_bound() )
782 blastedType = BVAnnotation( maxWidth, false, _interval.lower() );
784 else if( _interval.upper_bound_type() != carl::BoundType::INFTY && _interval.upper() < tempType.upper_bound() )
786 blastedType = BVAnnotation( maxWidth, false, _interval.upper() - (tempType.upper_bound() - tempType.lower_bound()) );
790 blastedType = tempType;
797 blastedType = BVAnnotation( maxWidth, !_interval.is_semi_positive(), 0 );
803 // interval is bounded
804 std::size_t width = 0;
807 width = chooseWidth( _interval.upper() - _interval.lower(), maxWidth, false );
808 blastedType = BVAnnotation( width, false, _interval.lower() );
812 if( _interval.is_semi_positive() )
814 width = chooseWidth( _interval.upper(), maxWidth, false );
815 blastedType = BVAnnotation( width, false, 0 );
817 else // @todo: do something clever, if semi-negative
819 std::size_t widthForUpper = chooseWidth( _interval.upper(), maxWidth, true );
820 std::size_t widthForLower = chooseWidth(_interval.lower(), maxWidth, true );
821 width = std::max( widthForUpper, widthForLower );
822 blastedType = BVAnnotation( width, true, 0 );
826 ret = width <= maxWidth ? 1 : 0;
829 if( Settings::apply_icp )
831 addBoundRestrictionsToICP( _variable, blastedType );
834 mPolyBlastings[variablePoly] = BlastedPoly( AnnotatedBVTerm(blastedType) );
838 template<class Settings>
839 void IntBlastModule<Settings>::addBoundRestrictionsToICP(carl::Variable _variable, const BVAnnotation& blastedType)
841 addFormulaToICP(FormulaT(ConstraintT(_variable, carl::Relation::GEQ, blastedType.lower_bound())), mConstraintFromBounds);
842 addFormulaToICP(FormulaT(ConstraintT(_variable, carl::Relation::LEQ, blastedType.upper_bound())), mConstraintFromBounds);
845 template<class Settings>
846 void IntBlastModule<Settings>::removeBoundRestrictionsFromICP(carl::Variable _variable)
848 auto& blastedVariable = mPolyBlastings.at(Poly(_variable));
850 if(! blastedVariable.is_constant()) {
851 const BVAnnotation& blastedType = blastedVariable.term().type();
853 removeFormulaFromICP(FormulaT(ConstraintT(_variable, carl::Relation::GEQ, blastedType.lower_bound())), mConstraintFromBounds);
854 removeFormulaFromICP(FormulaT(ConstraintT(_variable, carl::Relation::LEQ, blastedType.upper_bound())), mConstraintFromBounds);
858 template<class Settings>
859 std::size_t IntBlastModule<Settings>::chooseWidth(const Integer& _numberToCover, std::size_t _maxWidth, bool _signed) const
861 assert(_numberToCover >= 0 || _signed);
862 std::size_t width = 1;
863 carl::Interval<Integer> interval(Integer(_signed ? -1 : 0), Integer(_signed ? 0 : 1));
864 while((width < _maxWidth || _maxWidth == 0) && !interval.contains(_numberToCover)) {
866 interval.set_lower(interval.lower() * 2);
867 interval.set_upper((interval.upper()+1)*2 - 1);
872 template<class Settings>
873 void IntBlastModule<Settings>::updateBoundsFromICP()
875 for(const FormulaT& formula : mProcessedFormulasFromICP) {
876 mBoundsInRestriction.removeBound(formula.constraint(), formula);
878 mProcessedFormulasFromICP.clear();
880 for(ModuleInput::const_iterator fwo=mICP.rPassedFormula().begin(); fwo != mICP.rPassedFormula().end(); fwo++) {
881 if(INTBLAST_DEBUG_ENABLED) INTBLAST_DEBUG("from ICP: " << fwo->formula());
882 mBoundsInRestriction.addBound(fwo->formula().constraint(), fwo->formula());
883 mProcessedFormulasFromICP.push_back(fwo->formula());
886 FormulasT icpBounds = mICP.getCurrentBoxAsFormulas();
887 for( auto& f : icpBounds ) {
888 if(INTBLAST_DEBUG_ENABLED) INTBLAST_DEBUG("from ICP: " << f);
889 mBoundsInRestriction.addBound(f.constraint(), f);
890 mProcessedFormulasFromICP.push_back(f);
892 recheckShrunkPolys();
895 template<class Settings>
896 void IntBlastModule<Settings>::updateOutsideRestrictionConstraint(bool _fromICPOnly)
898 FormulasT outsideConstraints;
900 auto& inputBounds = mBoundsFromInput.getEvalIntervalMap();
901 for(auto& variableWithOrigins : mInputVariables) {
902 const carl::Variable& variable = variableWithOrigins.element();
903 auto& blastedVar = mPolyBlastings.at(Poly(variable));
904 Integer blastedLowerBound = (blastedVar.is_constant() ? blastedVar.constant() : blastedVar.term().type().lower_bound());
905 Integer blastedUpperBound = (blastedVar.is_constant() ? blastedVar.constant() : blastedVar.term().type().upper_bound());
906 auto inputBoundsIt = inputBounds.find(variable);
908 if(inputBoundsIt == inputBounds.end() || get_num(inputBoundsIt->second).contains(blastedLowerBound - 1)) {
909 outsideConstraints.push_back(FormulaT(ConstraintT(variable, carl::Relation::LESS, blastedLowerBound)));
912 if(inputBoundsIt == inputBounds.end() || get_num(inputBoundsIt->second).contains(blastedUpperBound + 1)) {
913 outsideConstraints.push_back(FormulaT(ConstraintT(variable, carl::Relation::GREATER, blastedUpperBound)));
917 FormulaT newOutsideConstraint(carl::FormulaType::OR, outsideConstraints);
919 // Construct origins of the "outside restriction" constraint.
920 // They should be picked in a suitable way for the infeasible subset derivation.
922 // If the ICP module has returned UNSAT, we can use the infeasible subset from ICP.
923 // The origins of the constraints are turned into a conjunction and inserted as
924 // one origin of the "outside restriction" constraint.
926 // If the ICP module has returned UNKNOWN, and the BV module has returned UNSAT,
927 // the infeasible subset of the BV module is not sufficient: When encoding into
928 // Bitvector logic, the contracted bounds from ICP are used and become an implicit
929 // constraint of the Bitvector problem. However, the ICP module currently does not
930 // give us any insights about the constraints which have been used to contract
931 // a certain clause. Therefore, we cannot create a reasonable infeasible subset here.
932 // Instead, we use the conjunction of all input formulas as origin of the
933 // "outside restriction" constraint.
938 const std::vector<FormulaSetT>& infeasibleInRestriction = mICP.infeasibleSubsets();
939 ModuleInput* restrictionInput = mpICPInput;
941 for(const FormulaSetT& infeasibleSubset : infeasibleInRestriction) {
942 FormulasT originsOfInfSubset;
943 for(const FormulaT& infSubsetElement : infeasibleSubset) {
944 // Collect origins of element (i.e. subformulas of the received formula of IntBlastModule)
945 ModuleInput::const_iterator posInModuleInput = restrictionInput->find(infSubsetElement);
946 assert(posInModuleInput != restrictionInput->end());
947 if(posInModuleInput->hasOrigins()) {
948 bool isConstraintFromRestriction = false;
949 for(const auto& origin : posInModuleInput->origins()) {
950 if(origin == mConstraintFromBounds) {
951 isConstraintFromRestriction = true;
956 if(! isConstraintFromRestriction) {
957 collectOrigins(*findBestOrigin(posInModuleInput->origins()), originsOfInfSubset);
961 origins.insert(FormulaT(carl::FormulaType::AND, originsOfInfSubset));
964 FormulasT allInputFormulas;
965 for(const auto& inputFormula : rReceivedFormula()) {
966 allInputFormulas.push_back(inputFormula.formula());
968 origins.insert(FormulaT(carl::FormulaType::AND, allInputFormulas));
971 if(INTBLAST_DEBUG_ENABLED) {
972 INTBLAST_DEBUG("'outside restriction' formula has origins:");
973 for(const FormulaT& origin : origins) {
974 INTBLAST_DEBUG("- " << origin);
978 addSubformulaToPassedFormula(newOutsideConstraint, std::make_shared<std::vector<FormulaT>>(origins.begin(), origins.end()));
981 template<class Settings>
982 void IntBlastModule<Settings>::addFormulaToICP(const FormulaT& _formula, const FormulaT& _origin)
984 INTBLAST_DEBUG("-[ICP+]-> " << _formula);
985 INTBLAST_DEBUG(" (origin: " << _origin << ")");
986 auto insertionResult = mpICPInput->add(_formula, _origin);
988 if(insertionResult.second) {
989 mICP.inform(_formula);
990 mICP.add(insertionResult.first);
994 template<class Settings>
995 void IntBlastModule<Settings>::addSubstitutesToICP(const ConstraintT& _constraint, const FormulaT& _origin)
997 // Create a substitute for every node of the constraint tree of _formula
998 // in order to receive bounds from ICP for these expressions
1000 ConstrTree constraintTree(_constraint);
1002 // Walk through the ConstraintTree in a breadth-first search
1003 std::list<PolyTree> nodesForSubstitution;
1004 nodesForSubstitution.push_back(constraintTree.left());
1005 nodesForSubstitution.push_back(constraintTree.right());
1007 while(! nodesForSubstitution.empty()) {
1008 const PolyTree& currentPoly = nodesForSubstitution.front();
1012 switch(currentPoly.type()) {
1013 case PolyTree::Type::SUM:
1014 substituteEq = Poly(getICPSubstitute(currentPoly.left().poly()))
1015 + getICPSubstitute(currentPoly.right().poly());
1017 case PolyTree::Type::PRODUCT:
1018 substituteEq = Poly(getICPSubstitute(currentPoly.left().poly()))
1019 * getICPSubstitute(currentPoly.right().poly());
1022 substituteEq = currentPoly.poly();
1025 substituteEq -= getICPSubstitute(currentPoly.poly());
1026 FormulaT constraintForICP(substituteEq, carl::Relation::EQ);
1027 addFormulaToICP(constraintForICP, _origin);
1029 if(currentPoly.type() == PolyTree::Type::SUM || currentPoly.type() == PolyTree::Type::PRODUCT) {
1030 // Schedule left and right subtree of current PolyTree
1031 // for being visited in the breadth-first search
1032 nodesForSubstitution.push_back(currentPoly.left());
1033 nodesForSubstitution.push_back(currentPoly.right());
1036 nodesForSubstitution.pop_front();
1040 template<class Settings>
1041 void IntBlastModule<Settings>::addConstraintFormulaToICP(const FormulaT& _formula)
1043 assert(_formula.type() == carl::FormulaType::CONSTRAINT);
1044 ConstrTree constraintTree(_formula.constraint());
1046 // Add the root (the constraint itself) to ICP
1047 Poly rootPoly(getICPSubstitute(constraintTree.left().poly()));
1048 rootPoly -= getICPSubstitute(constraintTree.right().poly());
1049 ConstraintT rootConstraint(rootPoly, constraintTree.relation());
1051 addFormulaToICP(FormulaT(rootConstraint), _formula);
1054 template<class Settings>
1055 carl::Variable::Arg IntBlastModule<Settings>::getICPSubstitute(const Poly& _poly)
1057 auto substituteLookup = mSubstitutes.find(_poly);
1058 if(substituteLookup != mSubstitutes.end()) {
1059 return substituteLookup->second;
1062 mSubstitutes[_poly] = carl::fresh_integer_variable();
1063 return mSubstitutes[_poly];
1066 template<class Settings>
1067 void IntBlastModule<Settings>::removeFormulaFromICP(const FormulaT& _formula, const FormulaT& _origin)
1069 auto formulaInInput = mpICPInput->find(_formula);
1070 if(formulaInInput != mpICPInput->end()) {
1071 if(mpICPInput->removeOrigin(formulaInInput, _origin)) {
1072 INTBLAST_DEBUG("-[ICP-]-> " << _formula);
1073 mICP.remove(formulaInInput);
1074 mpICPInput->erase(formulaInInput);
1079 template<class Settings>
1080 void IntBlastModule<Settings>::removeOriginFromICP(const FormulaT& _origin)
1082 ModuleInput::iterator icpFormula = mpICPInput->begin();
1083 while(icpFormula != mpICPInput->end())
1085 // Remove the given formula from the set of origins.
1086 if(mpICPInput->removeOrigin(icpFormula, _origin)) {
1087 INTBLAST_DEBUG("-[ICP-]-> " << icpFormula->formula());
1088 mICP.remove(icpFormula);
1089 icpFormula = mpICPInput->erase(icpFormula);
1096 template<class Settings>
1097 void IntBlastModule<Settings>::addFormulaToBV(const FormulaT& _formula, const FormulaT& _origin)
1099 INTBLAST_DEBUG("-[BV +]-> " << _formula);
1100 INTBLAST_DEBUG(" (origin: " << _origin << ")");
1102 auto insertionResult = mpBVInput->add(_formula, _origin);
1103 if(insertionResult.second) { // The formula was fresh
1104 mpBVSolver->inform(_formula);
1105 mpBVSolver->add(_formula);
1109 template<class Settings>
1110 void IntBlastModule<Settings>::removeOriginFromBV(const FormulaT& _origin)
1112 ModuleInput::iterator bvFormula = mpBVInput->begin();
1113 while(bvFormula != mpBVInput->end())
1115 // Remove the given formula from the set of origins.
1116 if(mpBVInput->removeOrigin(bvFormula, _origin)) {
1117 mpBVSolver->removeFormula(bvFormula->formula());
1118 bvFormula = mpBVInput->erase(bvFormula);
1125 template<class Settings>
1126 void IntBlastModule<Settings>::updateModelFromICP() const
1130 mModel = mICP.model();
1133 template<class Settings>
1134 void IntBlastModule<Settings>::updateModelFromBV() const
1137 const Model& bvModel = mpBVSolver->model();
1139 // Transfer all non-bitvector assignments
1140 for(const auto& varAndValue : bvModel) {
1141 if(! varAndValue.first.isBVVariable()) {
1142 mModel.insert(varAndValue);
1146 // For each input variable, look up bitvector value and convert it back to integer
1147 for(auto inputVariableIt = mInputVariables.cbegin();inputVariableIt != mInputVariables.cend();++inputVariableIt) {
1148 auto& inputVariable = *inputVariableIt;
1149 const carl::Variable& variable = inputVariable.element();
1150 const BlastedPoly& blasting = mPolyBlastings.at(Poly(variable));
1152 if(blasting.is_constant()) {
1153 auto modelValue = RAN(blasting.constant());
1154 mModel.emplace(ModelVariable(variable), ModelValue(modelValue));
1156 const carl::BVTerm& blastedTerm = blasting.term().term();
1157 assert(blastedTerm.type() == carl::BVTermType::VARIABLE);
1158 const carl::BVVariable& bvVariable = blastedTerm.variable();
1160 Integer integerValue(0);
1162 auto bvValueInModel = bvModel.find(ModelVariable(bvVariable));
1163 if(bvValueInModel != bvModel.end()) {
1164 const carl::BVValue& bvValue = bvValueInModel->second.asBVValue();
1165 integerValue = decodeBVConstant(bvValue, blasting.term().type());
1167 // If the bitvector variable does not appear in the model of the
1168 // BV solver, the BV solver has not received any constraints
1169 // containing this variable. This implies that an arbitrary value is allowed.
1170 // We simply use constant 0.
1171 mModel.emplace(ModelVariable(variable), ModelValue(integerValue));
1176 template<class Settings>
1177 typename IntBlastModule<Settings>::IntegerInterval IntBlastModule<Settings>::get_num(const RationalInterval& _interval) const
1179 return IntegerInterval(
1180 carl::get_num(_interval.lower()), _interval.lower_bound_type(),
1181 carl::get_num(_interval.upper()), _interval.upper_bound_type()
1185 template<class Settings>
1186 void IntBlastModule<Settings>::addPolyParents(const ConstraintT& _constraint)
1188 ConstrTree constraintTree(_constraint);
1191 std::list<PolyTree> nodes;
1192 nodes.push_back(constraintTree.left());
1193 nodes.push_back(constraintTree.right());
1195 while(! nodes.empty()) {
1196 const PolyTree& current = nodes.front();
1198 if(current.type() == PolyTree::Type::SUM || current.type() == PolyTree::Type::PRODUCT) {
1199 addPolyParent(current.left().poly(), current.poly());
1200 addPolyParent(current.right().poly(), current.poly());
1201 nodes.push_back(current.left());
1202 nodes.push_back(current.right());
1209 template<class Settings>
1210 void IntBlastModule<Settings>::addPolyParent(const Poly& _child, const Poly& _parent)
1212 auto inserted = mPolyParents.insert(std::make_pair(_child, std::set<Poly>()));
1213 inserted.first->second.insert(_parent);
1216 template<class Settings>
1217 std::set<Poly> IntBlastModule<Settings>::parentalClosure(std::set<Poly> _children)
1219 std::set<Poly> closure(_children);
1220 std::list<Poly> incomplete(_children.begin(), _children.end());
1222 while(! incomplete.empty()) {
1223 const Poly& current = incomplete.front();
1224 for(auto parent : mPolyParents[current]) {
1225 auto parentInserted = closure.insert(parent);
1226 if(parentInserted.second) {
1227 incomplete.push_back(parent);
1230 incomplete.pop_front();
1236 template<class Settings>
1237 void IntBlastModule<Settings>::recheckShrunkPolys()
1239 auto& icpBounds = mBoundsInRestriction.getEvalIntervalMap();
1240 std::set<Poly> polysToReencode;
1242 for(const Poly& shrunkPoly : mShrunkPolys) {
1243 auto& blastedPoly = mPolyBlastings.at(shrunkPoly);
1244 auto& substitute = mSubstitutes.at(shrunkPoly);
1246 IntegerInterval encodedBounds = IntegerInterval(blastedPoly.lower_bound(), blastedPoly.upper_bound());
1247 auto icpBoundsIt = icpBounds.find(substitute);
1249 if(icpBoundsIt == icpBounds.end() || ! encodedBounds.contains(get_num(icpBoundsIt->second))) {
1250 polysToReencode.insert(shrunkPoly);
1254 if(! polysToReencode.empty()) {
1255 unblastPolys(polysToReencode);
1259 template<class Settings>
1260 void IntBlastModule<Settings>::unblastPoly(const Poly& _polys)
1262 std::set<Poly> polySet;
1263 polySet.insert(_polys);
1264 unblastPolys(polySet);
1267 template<class Settings>
1268 void IntBlastModule<Settings>::unblastPolys(const std::set<Poly>& _polys)
1270 std::set<Poly> obsolete = parentalClosure(_polys);
1272 // Erase all PolyBlastings belonging to the polynomials in 'obsolete'
1273 auto polyIt = mPolyBlastings.begin();
1274 while(polyIt != mPolyBlastings.end()) {
1275 if(obsolete.find(polyIt->first) != obsolete.end()) {
1276 mShrunkPolys.erase(polyIt->first);
1277 polyIt = mPolyBlastings.erase(polyIt);
1283 // Erase all ConstrBlastings which used a PolyBlasting that is now deleted
1284 auto constrIt = mConstrBlastings.begin();
1285 while(constrIt != mConstrBlastings.end()) {
1286 ConstrTree constrTree(constrIt->first);
1287 if(obsolete.find(constrTree.left().poly()) != obsolete.end()) {
1288 constrIt = mConstrBlastings.erase(constrIt);
1294 // Reencode all formulas which contain constraints whose
1295 // ConstrBlastings is now deleted
1296 for(const auto& inputFormula : rReceivedFormula()) {
1297 std::vector<ConstraintT> constraintsInFormula;
1298 arithmetic_constraints(inputFormula.formula(),constraintsInFormula);
1300 bool needsReencoding = false;
1301 for(const ConstraintT& constraint : constraintsInFormula) {
1302 if(mConstrBlastings.find(constraint) == mConstrBlastings.end()) {
1303 needsReencoding = true;
1308 if(needsReencoding) {
1309 removeOriginFromBV(inputFormula.formula());
1310 mFormulasToEncode.insert(inputFormula.formula());