2 * @file STropModule.cpp
3 * @author Ă–mer Sali <oemer.sali@rwth-aachen.de>
6 * Created on 2017-09-13.
9 #include "STropModule.h"
10 #include <carl-formula/formula/functions/NNF.h>
13 template<class Settings>
14 STropModule<Settings>::STropModule(const ModuleInput* _formula, Conditionals& _conditionals, Manager* _manager)
15 : Module(_formula, _conditionals, _manager)
17 , mChangedSeparators()
18 , mRelationalConflicts(0)
19 , mLinearizationConflicts()
20 , mCheckedWithBackends(false)
23 template<class Settings>
24 bool STropModule<Settings>::addCore(ModuleInput::const_iterator _subformula) {
25 addReceivedSubformulaToPassedFormula(_subformula);
26 const FormulaT& formula = _subformula->formula();
27 if (formula.type() == carl::FormulaType::FALSE) {
28 SMTRAT_STATISTICS_CALL(mStatistics.answer_by(STropModuleStatistics::AnswerBy::PARSER));
29 mInfeasibleSubsets.push_back({formula});
32 if(Settings::mode != Mode::INCREMENTAL_CONSTRAINTS) {
33 return mInfeasibleSubsets.empty();
35 if (formula.type() == carl::FormulaType::CONSTRAINT) {
36 /// Normalize the left hand side of the constraint and turn the relation accordingly
37 auto constr = subtropical::normalize(formula.constraint().constr());
39 /// Store the normalized constraint and mark the separator object as changed
40 SeparatorGroup& separator{mSeparators.emplace(constr.lhs(), constr.lhs()).first->second};
41 separator.mRelations.insert(constr.relation());
42 mChangedSeparators.insert(&separator);
44 /// Check if the asserted constraint affects the amount of equations
45 if(!separator.mEquationInduced){
46 if(separator.mRelations.count(carl::Relation::GEQ) * separator.mRelations.count(carl::Relation::LEQ)
47 + separator.mRelations.count(carl::Relation::EQ) > 0){
48 separator.mEquationInduced = true;
49 ++mRelationalConflicts;
53 /// Check if the asserted relation trivially conflicts with other asserted relations
54 switch (constr.relation()) {
55 case carl::Relation::EQ:
56 if (separator.mRelations.count(carl::Relation::NEQ))
57 mInfeasibleSubsets.push_back({
58 FormulaT(constr.lhs(), carl::Relation::EQ),
59 FormulaT(constr.lhs(), carl::Relation::NEQ)
61 if (separator.mRelations.count(carl::Relation::LESS))
62 mInfeasibleSubsets.push_back({
63 FormulaT(constr.lhs(), carl::Relation::EQ),
64 FormulaT(constr.lhs(), carl::Relation::LESS)
66 if (separator.mRelations.count(carl::Relation::GREATER))
67 mInfeasibleSubsets.push_back({
68 FormulaT(constr.lhs(), carl::Relation::EQ),
69 FormulaT(constr.lhs(), carl::Relation::GREATER)
72 case carl::Relation::NEQ:
73 if (separator.mRelations.count(carl::Relation::EQ))
74 mInfeasibleSubsets.push_back({
75 FormulaT(constr.lhs(), carl::Relation::NEQ),
76 FormulaT(constr.lhs(), carl::Relation::EQ)
79 case carl::Relation::LESS:
80 if (separator.mRelations.count(carl::Relation::EQ))
81 mInfeasibleSubsets.push_back({
82 FormulaT(constr.lhs(), carl::Relation::LESS),
83 FormulaT(constr.lhs(), carl::Relation::EQ)
85 if (separator.mRelations.count(carl::Relation::GEQ))
86 mInfeasibleSubsets.push_back({
87 FormulaT(constr.lhs(), carl::Relation::LESS),
88 FormulaT(constr.lhs(), carl::Relation::GEQ)
91 case carl::Relation::LEQ:
92 if (separator.mRelations.count(carl::Relation::GREATER))
93 mInfeasibleSubsets.push_back({
94 FormulaT(constr.lhs(), constr.relation()),
95 FormulaT(constr.lhs(), carl::Relation::GREATER)
98 case carl::Relation::GREATER:
99 if (separator.mRelations.count(carl::Relation::EQ))
100 mInfeasibleSubsets.push_back({
101 FormulaT(constr.lhs(), carl::Relation::GREATER),
102 FormulaT(constr.lhs(), carl::Relation::EQ)
104 if (separator.mRelations.count(carl::Relation::LEQ))
105 mInfeasibleSubsets.push_back({
106 FormulaT(constr.lhs(), carl::Relation::GREATER),
107 FormulaT(constr.lhs(), carl::Relation::LEQ)
110 case carl::Relation::GEQ:
111 if (separator.mRelations.count(carl::Relation::LESS))
112 mInfeasibleSubsets.push_back({
113 FormulaT(constr.lhs(), constr.relation()),
114 FormulaT(constr.lhs(), carl::Relation::LESS)
121 if (mInfeasibleSubsets.empty()) {
122 SMTRAT_STATISTICS_CALL(mStatistics.answer_by(STropModuleStatistics::AnswerBy::TRIVIAL_UNSAT));
124 return mInfeasibleSubsets.empty();
127 template<class Settings>
128 void STropModule<Settings>::removeCore(ModuleInput::const_iterator _subformula) {
129 if(Settings::mode != Mode::INCREMENTAL_CONSTRAINTS){
132 const FormulaT& formula = _subformula->formula();
133 if (formula.type() == carl::FormulaType::CONSTRAINT) {
134 /// Normalize the left hand side of the constraint and turn the relation accordingly
135 auto constr = subtropical::normalize(formula.constraint().constr());
137 /// Retrieve the normalized constraint and mark the separator object as changed
138 SeparatorGroup& separator{mSeparators.at(constr.lhs())};
139 separator.mRelations.erase(constr.relation());
140 mChangedSeparators.insert(&separator);
142 /// Check if the removed constraint affects the amount of equations
143 if(separator.mEquationInduced){
144 if(separator.mRelations.count(carl::Relation::GEQ) * separator.mRelations.count(carl::Relation::LEQ)
145 + separator.mRelations.count(carl::Relation::EQ) == 0){
146 separator.mEquationInduced = false;
147 --mRelationalConflicts;
153 template<class Settings>
154 void STropModule<Settings>::updateModel() const {
155 if(Settings::mode == Mode::TRANSFORM_EQUATION || mSolverState != Answer::SAT) {
158 if (!mModelComputed) {
159 if (mCheckedWithBackends) {
162 excludeNotReceivedVariablesFromModel();
165 mModel = mEncoding.construct_assignment(mLRAModule.model(), FormulaT(rReceivedFormula()));
167 mModelComputed = true;
171 template<class Settings>
172 Answer STropModule<Settings>::checkCore() {
173 SMTRAT_TIME_START(theoryStart);
174 // Report unsatisfiability if the already found conflicts are still unresolved
175 if (!mInfeasibleSubsets.empty()) {
176 SMTRAT_TIME_FINISH(mStatistics.theory_timer(), theoryStart);
177 SMTRAT_STATISTICS_CALL(mStatistics.answer_by(STropModuleStatistics::AnswerBy::TRIVIAL_UNSAT));
178 if (Settings::output_only) {
179 SMTRAT_VALIDATION_ADD("smtrat.subtropical", "transformation", FormulaT(carl::FormulaType::FALSE), true);
181 return Answer::UNSAT;
184 if constexpr(Settings::mode == Mode::INCREMENTAL_CONSTRAINTS) {
185 assert(!Settings::output_only);
186 // Predicate that decides if the given conflict is a subset of the asserted constraints
187 const auto hasConflict = [&](const Conflict& conflict) {
191 [&](const auto& conflictEntry) {
192 return ((conflictEntry.second == subtropical::Direction::NEGATIVE
193 || conflictEntry.second == subtropical::Direction::BOTH)
194 && (conflictEntry.first->mRelations.count(carl::Relation::LESS)
195 || conflictEntry.first->mRelations.count(carl::Relation::LEQ)))
196 || ((conflictEntry.second == subtropical::Direction::POSITIVE
197 || conflictEntry.second == subtropical::Direction::BOTH)
198 && (conflictEntry.first->mRelations.count(carl::Relation::GREATER)
199 || conflictEntry.first->mRelations.count(carl::Relation::GEQ)))
200 || (conflictEntry.second == subtropical::Direction::BOTH
201 && conflictEntry.first->mRelations.count(carl::Relation::NEQ));
206 // Apply the method only if the asserted formula is not trivially undecidable
207 if (!mRelationalConflicts
208 && rReceivedFormula().is_constraint_conjunction()
209 && std::none_of(mLinearizationConflicts.begin(), mLinearizationConflicts.end(), hasConflict)) {
210 // Update the linearization of all changed separators
211 for (SeparatorGroup *separatorPtr : mChangedSeparators) {
212 SeparatorGroup& separator{*separatorPtr};
214 // Determine the direction that shall be active
215 std::optional<subtropical::Direction> direction;
216 if (!separator.mRelations.empty()) {
217 if (separator.mActiveDirection
218 && *separator.mActiveDirection == subtropical::Direction::NEGATIVE
219 && ((separator.mRelations.count(carl::Relation::LESS)
220 || separator.mRelations.count(carl::Relation::LEQ)))) {
221 direction = subtropical::Direction::NEGATIVE;
222 } else if (separator.mActiveDirection
223 && *separator.mActiveDirection == subtropical::Direction::POSITIVE
224 && ((separator.mRelations.count(carl::Relation::GREATER)
225 || separator.mRelations.count(carl::Relation::GEQ)))) {
226 direction = subtropical::Direction::POSITIVE;
228 direction = subtropical::direction(*separator.mRelations.rbegin());
232 // Update the linearization if the direction has changed
233 if (separator.mActiveDirection != direction) {
234 if (separator.mActiveDirection) {
235 remove_lra_formula(separator.mActiveFormula);
236 separator.mActiveFormula = FormulaT(carl::FormulaType::FALSE);
238 separator.mActiveDirection = direction;
239 if (separator.mActiveDirection) {
240 separator.mActiveFormula = mEncoding.encode_separator(separator.mSeparator, *separator.mActiveDirection, Settings::separatorType);
241 add_lra_formula(separator.mActiveFormula);
245 mChangedSeparators.clear();
247 // Restrict the normal vector component of integral variables to positive values
248 std::set<carl::Variable> toErase;
249 std::transform(mActiveIntegerFormulas.begin(), mActiveIntegerFormulas.end(), std::inserter(toErase, toErase.end()), [](auto pair){ return pair.first; });
250 for (auto& variable : carl::variables(FormulaT(rReceivedFormula()))) {
251 toErase.erase(variable);
252 if (variable.type() == carl::VariableType::VT_INT && mActiveIntegerFormulas.find(variable) == mActiveIntegerFormulas.end()) {
253 mActiveIntegerFormulas[variable] = mEncoding.encode_integer(variable);
254 add_lra_formula(mActiveIntegerFormulas[variable]);
257 for (const auto& variable : toErase) {
258 remove_lra_formula(mActiveIntegerFormulas[variable]);
259 mActiveIntegerFormulas.erase(variable);
262 // Check the constructed linearization with the LRA solver
263 SMTRAT_STATISTICS_CALL(mStatistics.transformation_applicable());
264 if (mLRAModule.check(true) == Answer::SAT) {
265 SMTRAT_TIME_FINISH(mStatistics.theory_timer(), theoryStart);
266 SMTRAT_STATISTICS_CALL(mStatistics.answer_by(STropModuleStatistics::AnswerBy::METHOD));
267 mCheckedWithBackends = false;
270 /// Learn the conflicting set of separators to avoid its recheck in the future
271 for (const FormulaSetT& lra_conflict : mLRAModule.infeasibleSubsets()) {
272 carl::carlVariables variables;
273 for (const FormulaT& formula : lra_conflict)
274 carl::variables(formula, variables);
276 for (const auto& separatorsEntry : mSeparators) {
277 const SeparatorGroup& separator{separatorsEntry.second};
278 if (separator.mActiveDirection
279 && variables.has(separator.mSeparator.bias))
280 conflict.emplace_back(&separator, *separator.mActiveDirection);
282 mLinearizationConflicts.emplace_back(std::move(conflict));
286 } else if constexpr(Settings::mode == Mode::TRANSFORM_EQUATION) {
287 SMTRAT_TIME_START(transformationStart);
288 FormulaT negationFreeFormula = carl::to_nnf(FormulaT(rReceivedFormula()));
289 if (negationFreeFormula.type() == carl::FormulaType::FALSE) {
290 SMTRAT_STATISTICS_CALL(mStatistics.answer_by(STropModuleStatistics::AnswerBy::PARSER));
291 std::set<FormulaT> infset;
292 for (const auto& f : rReceivedFormula()) {
293 infset.insert(f.formula());
295 mInfeasibleSubsets.push_back(infset);
296 if (Settings::output_only) {
297 SMTRAT_VALIDATION_ADD("smtrat.subtropical", "transformation", FormulaT(carl::FormulaType::FALSE), true);
299 return Answer::UNSAT;
300 } else if (negationFreeFormula.type() == carl::FormulaType::TRUE) {
301 SMTRAT_STATISTICS_CALL(mStatistics.answer_by(STropModuleStatistics::AnswerBy::PARSER));
302 if (Settings::output_only) {
303 SMTRAT_VALIDATION_ADD("smtrat.subtropical", "transformation", FormulaT(carl::FormulaType::TRUE), true);
307 FormulaT equation = subtropical::transform_to_equation(negationFreeFormula);
308 SMTRAT_TIME_FINISH(mStatistics.transformation_timer(), transformationStart);
309 if(equation.type() != carl::FormulaType::FALSE) {
310 subtropical::Separator separator(equation.constraint().lhs().normalize());
311 auto direction = subtropical::direction_for_equality(separator);
313 SMTRAT_TIME_FINISH(mStatistics.theory_timer(), theoryStart);
314 SMTRAT_STATISTICS_CALL(mStatistics.answer_by(STropModuleStatistics::AnswerBy::METHOD));
315 SMTRAT_STATISTICS_CALL(mStatistics.transformation_applicable());
316 mCheckedWithBackends = false;
317 if (Settings::output_only) {
318 SMTRAT_VALIDATION_ADD("smtrat.subtropical", "transformation", FormulaT(carl::FormulaType::TRUE), true);
322 SMTRAT_STATISTICS_CALL(mStatistics.transformation_applicable());
323 if (!Settings::output_only) {
325 mLRAModule.add(mEncoding.encode_separator(separator, *direction, Settings::separatorType));
326 if (mLRAModule.check(true) == Answer::SAT) {
327 SMTRAT_TIME_FINISH(mStatistics.theory_timer(), theoryStart);
328 SMTRAT_STATISTICS_CALL(mStatistics.answer_by(STropModuleStatistics::AnswerBy::METHOD));
329 mCheckedWithBackends = false;
333 SMTRAT_VALIDATION_ADD("smtrat.subtropical", "transformation", mEncoding.encode_separator(separator, *direction, Settings::separatorType), true);
334 return Answer::UNKNOWN;
339 static_assert(Settings::mode == Mode::TRANSFORM_FORMULA || Settings::mode == Mode::TRANSFORM_FORMULA_ALT);
340 SMTRAT_TIME_START(transformationStart);
341 FormulaT negationFreeFormula = carl::to_nnf(FormulaT(rReceivedFormula()));
342 if (negationFreeFormula.type() == carl::FormulaType::FALSE) {
343 SMTRAT_STATISTICS_CALL(mStatistics.answer_by(STropModuleStatistics::AnswerBy::PARSER));
344 std::set<FormulaT> infset;
345 for (const auto& f : rReceivedFormula()) {
346 infset.insert(f.formula());
348 mInfeasibleSubsets.push_back(infset);
349 if (Settings::output_only) {
350 SMTRAT_VALIDATION_ADD("smtrat.subtropical", "transformation", FormulaT(carl::FormulaType::FALSE), true);
352 return Answer::UNSAT;
353 } else if (negationFreeFormula.type() == carl::FormulaType::TRUE) {
354 SMTRAT_STATISTICS_CALL(mStatistics.answer_by(STropModuleStatistics::AnswerBy::PARSER));
355 if (Settings::output_only) {
356 SMTRAT_VALIDATION_ADD("smtrat.subtropical", "transformation", FormulaT(carl::FormulaType::TRUE), true);
360 FormulaT translatedFormula = (Settings::mode == Mode::TRANSFORM_FORMULA) ? subtropical::encode_as_formula(negationFreeFormula, mEncoding, Settings::separatorType) : subtropical::encode_as_formula_alt(negationFreeFormula, mEncoding, Settings::separatorType);
361 SMTRAT_TIME_FINISH(mStatistics.transformation_timer(), transformationStart);
362 if(translatedFormula.type() != carl::FormulaType::FALSE){
363 SMTRAT_STATISTICS_CALL(mStatistics.transformation_applicable());
364 if (!Settings::output_only) {
366 mLRAModule.add(translatedFormula);
367 if (mLRAModule.check(true) == Answer::SAT) {
368 SMTRAT_TIME_FINISH(mStatistics.theory_timer(), theoryStart);
369 SMTRAT_STATISTICS_CALL(mStatistics.answer_by(STropModuleStatistics::AnswerBy::METHOD));
370 mCheckedWithBackends = false;
374 SMTRAT_VALIDATION_ADD("smtrat.subtropical", "transformation", translatedFormula, true);
375 return Answer::UNKNOWN;
380 // Check the asserted formula with the backends
381 if (Settings::output_only) {
382 SMTRAT_VALIDATION_ADD("smtrat.subtropical", "transformation", FormulaT(carl::FormulaType::FALSE), true);
383 return Answer::UNKNOWN;
385 SMTRAT_STATISTICS_CALL(mStatistics.failed());
386 mCheckedWithBackends = true;
387 Answer answer = runBackends();
388 SMTRAT_TIME_FINISH(mStatistics.theory_timer(), theoryStart);
389 SMTRAT_STATISTICS_CALL(mStatistics.answer_by(STropModuleStatistics::AnswerBy::BACKEND));
390 if (answer == Answer::UNSAT)
391 getInfeasibleSubsets();
396 template<class Settings>
397 inline void STropModule<Settings>::add_lra_formula(const FormulaT& formula) {
398 mLRAModule.add(formula);
401 template<class Settings>
402 inline void STropModule<Settings>::remove_lra_formula(const FormulaT& formula) {
403 if (formula.type() == carl::FormulaType::AND) {
404 auto iter = mLRAModule.formulaBegin();
405 for (const auto& subformula : formula.subformulas()) {
406 iter = mLRAModule.remove(std::find(iter, mLRAModule.formulaEnd(), subformula));
410 mLRAModule.remove(std::find(mLRAModule.formulaBegin(), mLRAModule.formulaEnd(), formula));