3 * @author YOUR NAME <YOUR EMAIL ADDRESS>
6 * Created on 2016-11-23.
9 #include "PBPPModule.h"
11 #include "Encoders/RNSEncoder.h"
14 // #define PRINT_STATS
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)
27 mCardinalityEncoder.problem_size = _formula->size();
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);
40 template<class Settings>
41 PBPPModule<Settings>::~PBPPModule()
44 template<class Settings>
45 bool PBPPModule<Settings>::informCore( const FormulaT& )
47 return true; // This should be adapted according to your implementation.
50 template<class Settings>
51 void PBPPModule<Settings>::init()
54 template<class Settings>
55 bool PBPPModule<Settings>::addCore( ModuleInput::const_iterator _subformula )
57 const FormulaT& formula = _subformula->formula();
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());
67 addConstraints(formula);
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);
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);
91 ConstraintT normalizedConstraint = constraint;
92 if (Settings::NORMALIZE_CONSTRAINTS) {
94 std::cout << "Constraint before normalization: \t" << normalizedConstraint << std::endl;
96 // normalize and hence hopefully simplify the formula
97 std::pair<std::optional<FormulaT>, ConstraintT> normalizedConstraintWithBoolPart = mNormalizer.normalize(constraint);
99 // extract the normalized constraint and pass along the rest
101 std::cout << "Constraint after normalization: \t"
102 << normalizedConstraintWithBoolPart.first
104 << normalizedConstraintWithBoolPart.second << std::endl;
107 normalizedConstraint = normalizedConstraintWithBoolPart.second;
108 if (normalizedConstraintWithBoolPart.first) {
109 extractConstraints(*normalizedConstraintWithBoolPart.first);
113 mConstraints.push_back(normalizedConstraint);
114 formulaByConstraint[normalizedConstraint] = formula;
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);
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) {
131 // we only expect a boolean combination of constraints
132 assert("The FormulaType passed was not valid. Expected Constraint, BooleanCombination or TRUE" && false);
136 template<class Settings>
137 Answer PBPPModule<Settings>::checkCore()
139 // 1. Preprocessing - ignore some constraints and gather informations
140 for (const auto& constraint : mConstraints) {
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);
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)))
156 encoderByConstraint[constraint] = encoder;
157 conversionSizeByConstraint[constraint] = curEncoderSize;
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);
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);
174 liaConstraints.push_back(constraint);
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);
184 std::cout << "After Step 1 - Encoding as LIA: " << liaConstraints << std::endl;
185 std::cout << "After Step 1 - Encoding as Bool: " << boolConstraints << std::endl;
188 std::set<carl::Variable> variablesInLIA;
189 for (const auto& constraint : liaConstraints) {
190 for (const auto& cvar : constraint.variables()) {
191 variablesInLIA.insert(cvar);
195 // 3. add more constraints to LIA part to refine the relaxation
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);
206 for (const auto& var : variablesInLIA) {
207 if (inspectedVariables.find(var) != inspectedVariables.end()) continue;
209 for (const auto& constraint : boolConstraints) {
210 if (additionallyLIAEncodedBoolConstraints.find(constraint) != additionallyLIAEncodedBoolConstraints.end()) {
214 bool constraintContainsCurrentVariable = constraint.variables().has(var);
215 bool constraintContainsSubstitutedVariable = variableSubstitutions.find(var) != variableSubstitutions.end();
217 if (!isTrivial(constraint) && (constraintContainsCurrentVariable || constraintContainsSubstitutedVariable)) {
218 inspectedVariables.insert(var);
219 liaConstraints.push_back(constraint);
220 additionallyLIAEncodedBoolConstraints.insert(constraint);
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);
233 std::cout << "After Step 3 - Encoding as LIA: " << liaConstraints << std::endl;
234 std::cout << "After Step 3 - Encoding as Bool: " << boolConstraints << std::endl;
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);
242 liaConstraints.push_back(constraint);
247 std::cout << "Encoded using " << encoderByConstraint[constraint]->name() << " " << constraint << " \t as \t " << *boolEncoding << std::endl;
249 for (const auto& var : constraint.variables()) {
250 variablesInBooleanPart.insert(var);
253 boolEncodings[constraint] = *boolEncoding;
256 for (const auto& constraint : liaConstraints) {
257 //addSubformulaToPassedFormula(forwardAsArithmetic(constraint, variablesInBooleanPart), formulaByConstraint[constraint]);
258 liaConstraintFormula[constraint] = forwardAsArithmetic(constraint, variablesInBooleanPart);
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;
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());
272 Answer ans = runBackends();
274 generateTrivialInfeasibleSubset();
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];
289 } else if (formula.is_boolean_combination() && formula.is_nary()) {
291 for (const auto& subformula : formula.subformulas()) {
292 subforms.push_back(restoreFormula(subformula));
295 return FormulaT(formula.type(), subforms);
297 // we don't care and return the formula - for example boolean literals. That's totally fine.
303 * Converts Constraint into a LRA formula.
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();
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()));
314 for(const auto& it : variables){
315 if (mVariablesCache.find(it) != mVariablesCache.end()) continue;
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()));
322 for(const auto& it : formula.lhs()){
323 if (it.is_constant()) {
328 lhs = lhs + it.coeff() * mVariablesCache[it.single_variable()];
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
336 for (auto var : variables) {
337 if (variableSetIntersection.find(var) != variableSetIntersection.end()) continue;
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)));
345 // bounds and connection do hold globally, forward them here
346 addSubformulaToPassedFormula(FormulaT(carl::FormulaType::AND, boolConnection, FormulaT(carl::FormulaType::AND, bounds)));
348 return constraintFormula;
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);
369 return FormulaT(carl::FormulaType::AND, std::move(subformulas));
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) {
384 template<typename Settings>
385 bool PBPPModule<Settings>::encodeAsBooleanFormula(const ConstraintT& constraint) {
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;
392 // this would be a simple encoding
393 //encode = encode && (constraint.relation() == carl::Relation::EQ || carl::abs(constraint.lhs().constant_part()) <= 1);
395 encode = encode || Settings::ENCODE_IF_POSSIBLE;
400 template<typename Settings>
401 bool PBPPModule<Settings>::isTrivial(const ConstraintT& constraint) {
402 bool trivial = false;
404 trivial = trivial || constraint.variables().size() <= 1;
405 trivial = trivial || (constraint.lhs().constant_part() == 0 && mCardinalityEncoder.canEncode(constraint));
410 template<class Settings>
411 void PBPPModule<Settings>::removeCore( ModuleInput::const_iterator _subformula )
413 // remove the constraint according to the given input again
414 const FormulaT& formula = _subformula->formula();
416 if (formula.type() != carl::FormulaType::CONSTRAINT){
420 const ConstraintT constraint = formula.constraint();
421 if (!constraint.isPseudoBoolean()) {
425 for (auto it = mConstraints.begin(); it != mConstraints.end(); ++it) {
426 if (*it == constraint) {
427 mConstraints.erase(it);
432 for (auto it = liaConstraints.begin(); it != liaConstraints.end(); ++it) {
433 if (*it == constraint) {
434 mConstraints.erase(it);
439 for (auto it = boolConstraints.begin(); it != boolConstraints.end(); ++it) {
440 if (*it == constraint) {
441 mConstraints.erase(it);
446 formulaByConstraint.erase(constraint);
450 template<class Settings>
451 void PBPPModule<Settings>::updateModel() const
454 if( is_sat(solverState()) )