dlinear  0.0.1
Delta-complete SMT solver for linear programming
Loading...
Searching...
No Matches
CompleteSoplexTheorySolver.cpp
1
6// IWYU pragma: no_include <new>
7#include "CompleteSoplexTheorySolver.h"
8
9#include <algorithm>
10#include <map>
11#include <utility>
12
13#include "dlinear/libs/libsoplex.h"
14#include "dlinear/solver/LpColBound.h"
15#include "dlinear/solver/LpRowSense.h"
16#include "dlinear/symbolic/symbolic.h"
17#include "dlinear/util/Timer.h"
18#include "dlinear/util/exception.h"
19#include "dlinear/util/logging.h"
20
21#define STRICT_VARIABLE_IDX (spx_.numColsRational() - 1)
22
23namespace dlinear {
24
25namespace {
26
27inline std::size_t bool_vector_to_int(const std::vector<bool> &v, const std::set<std::size_t> &positions) {
28 std::size_t result = 0;
29 for (const std::size_t &pos : positions) {
30 result <<= 1;
31 result |= v.at(pos);
32 }
33 return result;
34}
35
36} // namespace
37
38using SoplexStatus = soplex::SPxSolver::Status;
39using soplex::Rational;
40
43
44CompleteSoplexTheorySolver::CompleteSoplexTheorySolver(PredicateAbstractor &predicate_abstractor,
45 const std::string &class_name)
46 : SoplexTheorySolver(predicate_abstractor, class_name),
47 nq_row_to_theory_rows_{},
48 last_nq_status_{},
49 last_theory_rows_to_explanation_{},
50 theory_rows_to_explanations_{},
51 locked_solver_{false} {
52 DLINEAR_ASSERT(config_.precision() == 0, "CompleteSoplexTheorySolver does not support a positive precision");
53 DLINEAR_ASSERT(config_.simplex_sat_phase() == 1, "CompleteSoplexTheorySolver must use phase 1");
54}
55
56void CompleteSoplexTheorySolver::AddVariable(const Variable &var) {
57 auto it = var_to_theory_col_.find(var.get_id());
58 // The variable is already present
59 if (it != var_to_theory_col_.end()) return;
60 SoplexTheorySolver::AddVariable(var);
61}
62
63void CompleteSoplexTheorySolver::AddLiteral(const Variable &formula_var, const Formula &formula) {
64 DLINEAR_ASSERT(!is_consolidated_, "Cannot add literals after consolidation");
65 // Literal is already present
66 if (lit_to_theory_row_.contains(formula_var.get_id())) return;
67
68 DLINEAR_TRACE_FMT("CompleteSoplexTheorySolver::AddLiteral({})", formula);
69
70 // Create the LP solver variables
71 for (const Variable &var : formula.GetFreeVariables()) AddVariable(var);
72
73 spx_sense_.emplace_back(parseLpSense(formula));
74
75 const int spx_row{spx_rows_.num()};
76
77 // Add an inactive constraint with the right coefficients for the decisional variables
78 soplex::DSVectorRational coeffs{ParseRowCoeff(formula)};
79 spx_rows_.add(soplex::LPRowRational(-soplex::infinity, coeffs, soplex::infinity));
80
81 // Update indexes
82 lit_to_theory_row_.emplace(formula_var.get_id(), spx_row);
83 theory_row_to_lit_.emplace_back(formula_var, true);
84 last_nq_status_.push_back(false);
85
86 DLINEAR_ASSERT(static_cast<size_t>(spx_row) == theory_row_to_lit_.size() - 1, "incorrect theory_row_to_lit_.size()");
87 DLINEAR_ASSERT(static_cast<size_t>(spx_row) == spx_sense_.size() - 1, "incorrect spx_sense_.size()");
88 DLINEAR_ASSERT(static_cast<size_t>(spx_row) == spx_rhs_.size() - 1, "incorrect spx_rhs_.size()");
89 DLINEAR_ASSERT(static_cast<size_t>(spx_row) == last_nq_status_.size() - 1, "incorrect spx_rhs_.size()");
90 DLINEAR_DEBUG_FMT("CompleteSoplexTheorySolver::AddLiteral: {} ↦ {}", formula_var, spx_row);
91}
92
93CompleteSoplexTheorySolver::Explanations CompleteSoplexTheorySolver::EnableLiteral(const Literal &lit) {
94 DLINEAR_ASSERT(is_consolidated_, "The solver must be consolidate before enabling a literal");
95 DLINEAR_ASSERT(predicate_abstractor_.var_to_formula_map().contains(lit.var), "var must map to a theory literal");
96
97 Explanations explanations{preprocessor_.EnableLiteral(lit)};
98 if (!explanations.empty()) return explanations;
99
100 const auto &[var, truth] = lit;
101 const auto it_row = lit_to_theory_row_.find(var.get_id());
102 // If the literal was not already among the constraints (rows) of the LP, it must be a learned literal.
103 if (it_row == lit_to_theory_row_.end()) {
104 DLINEAR_TRACE_FMT("CompleteSoplexTheorySolver::EnableLinearLiteral: ignoring ({})", lit);
105 return {};
106 }
107
108 // A non-trivial linear literal from the input problem
109 const int spx_row = it_row->second;
110 // Update the truth value for the current iteration with the last SAT solver assignment
111 theory_row_to_lit_[spx_row].truth = truth;
112
113 const Formula &formula = predicate_abstractor_[var];
114 DLINEAR_TRACE_FMT("CompleteSoplexTheorySolver::EnableLinearLiteral({})", lit);
115
116 // If it is not a simple bound, we need to enable the row in the soplex solver
117 // Later, active strict bounds will also appear in the LP problem as strict rows
118 if (!BoundPreprocessor::IsSimpleBound(formula)) EnableSpxRow(spx_row, truth);
119 return explanations;
120}
121
122SatResult CompleteSoplexTheorySolver::CheckSatCore(mpq_class *actual_precision, Explanations &explanations) {
123 DLINEAR_ASSERT(is_consolidated_, "The solver must be consolidate before checking for sat");
124
125 // Set the bounds for the variables
126 EnableSpxVarBound();
127 // Remove all the disabled rows from the LP solver
128 DisableSpxRows();
129
130 // Now we call the solver
131 DLINEAR_DEBUG_FMT("CompleteSoplexTheorySolver::CheckSat: calling SoPlex (phase {})", config_.simplex_sat_phase());
132
133 // First, check the sat result without taking into account nq constraints
134 SatResult sat_status = SpxCheckSat();
135 DLINEAR_DEBUG_FMT("CompleteSoplexTheorySolver::CheckSat: no nq constraints sat_status = {}", sat_status);
136 if (sat_status != SatResult::SAT_SATISFIABLE) {
137 theory_rows_to_explanations_.insert(last_theory_rows_to_explanation_);
138 DLINEAR_ASSERT(theory_rows_to_explanations_.size() == 1, "theory_rows_to_explanations_ must have size 1");
139 GetExplanation(explanations);
140 return sat_status;
141 }
142
143 // Initialise the iterator with the last nq statuses
144 std::vector<bool> starting_iterator(nq_row_to_theory_rows_.size(), false);
145 for (size_t i = 0; i < nq_row_to_theory_rows_.size(); i++) {
146 starting_iterator[i] = last_nq_status_[nq_row_to_theory_rows_[i]];
147 }
148 BitIncrementIterator it(starting_iterator);
149 DLINEAR_DEBUG_FMT("CompleteSoplexTheorySolver::CheckSat: nq starting iterator size = {}", starting_iterator.size());
150 if (!it->empty()) DLINEAR_TRACE_FMT("CompleteSoplexTheorySolver::CheckSat: nq starting iterator = {}", it);
151
152 // Ensure that all non-equal constraints are enabled based on the current iterator value
153 EnableNqLiterals(starting_iterator, true);
154 do {
155 // Clean up the last explanation
156 last_theory_rows_to_explanation_.clear();
157
158 // Enable the non-equal constraints based on the current iterator value.
159 // If the iterator is empty (there are no not-equal constraints), this will do nothing
160 EnableNqLiterals(*it);
161
162 // Omitting to do this seems to cause problems in soplex
163 spx_.clearBasis();
164
165 // Solve the sub-problem
166 sat_status = SpxCheckSat();
167 DLINEAR_TRACE_FMT("CompleteSoplexTheorySolver::CheckSat: intermediate sat_status = {}", sat_status);
168
169 // If the problem is SAT and not locked, we can return immediately
170 if (sat_status == SatResult::SAT_SATISFIABLE) break;
171
172 // Use some heuristics to update the iterator based on the current explanation.
173 // Otherwise, just increment the iterator with the next configuration and try again
174 if (!UpdateBitIncrementIteratorBasedOnExplanation(it)) break;
175 } while (it);
176
177 *actual_precision = 0;
178 switch (sat_status) {
179 case SatResult::SAT_SATISFIABLE:
180 UpdateModelSolution();
181 DLINEAR_DEBUG("CompleteSoplexTheorySolver::CheckSat: returning sat");
182 return SatResult::SAT_SATISFIABLE;
183 case SatResult::SAT_UNSATISFIABLE:
184 GetExplanation(explanations);
185 DLINEAR_DEBUG("CompleteSoplexTheorySolver::CheckSat: returning unsat");
186 return SatResult::SAT_UNSATISFIABLE;
187 default:
188 DLINEAR_UNREACHABLE();
189 }
190}
191
192SatResult CompleteSoplexTheorySolver::SpxCheckSat() {
193#if 0
194 spx_.writeFile("/home/campus.ncl.ac.uk/c3054737/Programming/phd/dlinear/dump.lp");
195#endif
196 SoplexStatus status = spx_.optimize();
197 Rational max_violation, sum_violation;
198
199 // The status must be OPTIMAL, UNBOUNDED, or INFEASIBLE. Anything else is an error
200 if (status != SoplexStatus::OPTIMAL && status != SoplexStatus::UNBOUNDED && status != SoplexStatus::INFEASIBLE) {
201 DLINEAR_RUNTIME_ERROR_FMT("SoPlex returned {}. That's not allowed here", status);
202 } else if (spx_.getRowViolationRational(max_violation, sum_violation)) {
203 DLINEAR_ASSERT(max_violation.is_zero(), "max_violation must be 0");
204 DLINEAR_DEBUG_FMT("CompleteSoplexTheorySolver::CheckSat: SoPlex returned {}, precision = 0", status);
205 } else {
206 DLINEAR_DEBUG_FMT("CompleteSoplexTheorySolver::CheckSat: SoPlex has returned {}, but no precision", status);
207 }
208
209 switch (status) {
210 case SoplexStatus::OPTIMAL:
211 if (spx_.objValueRational() > 0)
212 return locked_solver_ ? SatResult::SAT_UNSATISFIABLE : SatResult::SAT_SATISFIABLE;
213 UpdateExplanationStrictInfeasible();
214 return SatResult::SAT_UNSATISFIABLE;
215 case SoplexStatus::INFEASIBLE:
216 UpdateExplanationInfeasible();
217 return SatResult::SAT_UNSATISFIABLE;
218 default:
219 DLINEAR_UNREACHABLE();
220 }
221}
222
223void CompleteSoplexTheorySolver::Reset() {
224 SoplexTheorySolver::Reset();
225 nq_row_to_theory_rows_.clear();
226 theory_rows_to_explanations_.clear();
227 nq_explanations_.clear();
228 locked_solver_ = false;
229 last_theory_rows_to_explanation_.clear();
230 single_nq_rows_.clear();
231}
232
233void CompleteSoplexTheorySolver::Consolidate(const Box &box) {
234 if (is_consolidated_) return;
235
236 // Column of the strict auxiliary variable t bound between 0 and 1
237 spx_cols_.add(soplex::LPColRational(1, soplex::DSVectorRational(), 1, 0));
238
239 [[maybe_unused]] const auto rowcount = static_cast<size_t>(spx_rows_.num());
240 [[maybe_unused]] const auto colcount = static_cast<size_t>(spx_cols_.num());
241 DLINEAR_ASSERT_FMT(colcount == theory_col_to_var_.size() + 1, "incorrect theory_col_to_var_.size(): {} vs {}",
242 colcount, theory_col_to_var_.size());
243 DLINEAR_ASSERT(colcount == var_to_theory_col_.size() + 1, "incorrect var_to_theory_col_.size()");
244 DLINEAR_ASSERT(rowcount == spx_sense_.size(), "incorrect spx_sense_.size()");
245 DLINEAR_ASSERT(rowcount == spx_rhs_.size(), "incorrect spx_rhs_.size()");
246 DLINEAR_ASSERT(rowcount == last_nq_status_.size(), "incorrect spx_rhs_.size()");
247 DLINEAR_ASSERT(rowcount == theory_row_to_lit_.size(), "incorrect theory_row_to_lit_.size()");
248 DLINEAR_DEBUG("CompleteSoplexTheorySolver::Consolidate: consolidated");
249
250 SoplexTheorySolver::Consolidate(box);
251}
252
253void CompleteSoplexTheorySolver::EnableNqLiteral(const int spx_row, const bool truth) {
254 last_nq_status_[spx_row] = truth;
255
256 const Rational rhs{spx_rhs_[spx_row].get_mpq_t()};
257
258 soplex::LPRowRational lp_row(spx_.numColsRational());
259 spx_.getRowRational(spx_row, lp_row);
260 soplex::DSVectorRational row_vector{lp_row.rowVector()};
261
262 const int pos = row_vector.pos(STRICT_VARIABLE_IDX);
263 DLINEAR_ASSERT(!row_vector.value(pos).is_zero(), "Coefficient of the strict auxiliary variable cannot be 0");
264 DLINEAR_DEBUG_FMT("CompleteSoplexTheorySolver::EnableNqLiterals: Row({}) ↦ {} {} {}", spx_row, lp_row.lhs(),
265 lp_row.rowVector(), lp_row.rhs());
266
267 row_vector.value(pos) = truth ? -1 : 1;
268
269 lp_row.setLhs(truth ? rhs : -soplex::infinity);
270 lp_row.setRhs(truth ? soplex::infinity : rhs);
271 lp_row.setRowVector(row_vector);
272
273 spx_.changeRowRational(spx_row, lp_row);
274}
275
276void CompleteSoplexTheorySolver::EnableNqLiterals(const std::vector<bool> &nq_status, const bool force) {
277 DLINEAR_TRACE_FMT("CompleteSoplexTheorySolver::EnableNqLiterals: nq_status = {}, force? = {}", nq_status, force);
278 for (size_t i = 0; i < nq_status.size(); i++) {
279 const int &spx_row = nq_row_to_theory_rows_[i];
280 // The row's sense has not changed since last time and the row is not in forced_nq_constraints, skip
281 if (last_nq_status_[spx_row] == nq_status[i] && !force) continue;
282 EnableNqLiteral(spx_row, nq_status[i]);
283 }
284}
285
286void CompleteSoplexTheorySolver::DisableNqLiterals(const std::set<size_t> &nq_constraints) {
287 DLINEAR_TRACE_FMT("CompleteSoplexTheorySolver::DisableNqLiterals: nq_constraints = {}", nq_constraints);
288 for (const size_t i : nq_constraints) {
289 const int &spx_row = nq_row_to_theory_rows_[i];
290 spx_.changeRangeRational(spx_row, -soplex::infinity, soplex::infinity);
291 }
292}
293
294void CompleteSoplexTheorySolver::UpdateExplanationInfeasible() {
295 DLINEAR_ASSERT(last_theory_rows_to_explanation_.empty(), "last explanation must be empty");
296 soplex::VectorRational ray{spx_.numRowsRational()};
297 std::vector<BoundViolationType> bounds_ray(spx_.numColsRational() - 1, BoundViolationType::NO_BOUND_VIOLATION);
298 GetSpxInfeasibilityRay(ray, bounds_ray);
299
300 // For each row in the ray
301 for (int i = 0; i < spx_.numRowsRational(); ++i) {
302 if (ray[i].is_zero()) continue; // The row did not participate in the conflict, ignore it
303 DLINEAR_TRACE_FMT("SoplexSatSolver::UpdateExplanation: ray[{}] = {}", i, ray[i]);
304 // Insert the conflicting row literal to the explanation
305 last_theory_rows_to_explanation_.insert(i);
306
307 // Add all the active bounds for the free variables in the row to the explanation
308 const auto &row_formula = predicate_abstractor_[theory_row_to_lit_[i].var];
309 for (const Variable &var : row_formula.GetFreeVariables()) {
310 const int &theory_col = var_to_theory_col_.at(var.get_id());
311 BoundsToTheoryRows(var, bounds_ray[theory_col], last_theory_rows_to_explanation_);
312 }
313 }
314
315 DLINEAR_ASSERT(!last_theory_rows_to_explanation_.empty(), "explanation must contain at least a violation");
316 DLINEAR_DEBUG_FMT("CompleteSoplexTheorySolver::UpdateExplanationInfeasible: ↦ {}", last_theory_rows_to_explanation_);
317}
318
319void CompleteSoplexTheorySolver::UpdateExplanationStrictInfeasible() {
320 DLINEAR_ASSERT(last_theory_rows_to_explanation_.empty(), "last explanation must be empty");
321
322 // Solution vector. Contains the value of all the variables
323 soplex::VectorRational x{spx_.numColsRational()};
324 [[maybe_unused]] bool has_sol = spx_.getPrimalRational(x);
325 DLINEAR_ASSERT(has_sol, "has_sol must be true");
326 soplex::VectorRational dual{spx_.numRowsRational()};
327 [[maybe_unused]] const bool has_dual = spx_.getDualRational(dual);
328 DLINEAR_ASSERT(has_dual, "has_dual must be true");
329
330 std::set<Variable> visited_variables;
331 for (int i = 0; i < dual.dim(); ++i) {
332 // Skip inactive rows
333 if (dual[i].is_zero()) continue;
334 last_theory_rows_to_explanation_.insert(i);
335 const auto &row_formula = predicate_abstractor_[theory_row_to_lit_[i].var];
336 for (const Variable &var : row_formula.GetFreeVariables()) visited_variables.insert(var);
337 }
338
339 DLINEAR_DEBUG_FMT("Strict active rows: {}", last_theory_rows_to_explanation_);
340
341 // Run through the variables found in the active rows
342 for (const Variable &var_id : visited_variables) {
343 // Add all the bounds for the variable to explanation, if they are active
344 const int theory_col = var_to_theory_col_.at(var_id.get_id());
345 BoundsToTheoryRows(var_id, mpq_class{x[theory_col].backend().data()}, last_theory_rows_to_explanation_);
346 }
347
348 DLINEAR_ASSERT(!last_theory_rows_to_explanation_.empty(), "explanation must contain at least a violation");
349 DLINEAR_DEBUG_FMT("CompleteSoplexTheorySolver::UpdateExplanationStrictInfeasible: ↦ {}",
350 last_theory_rows_to_explanation_);
351}
352
353void CompleteSoplexTheorySolver::GetExplanation(Explanations &explanations) {
354 DLINEAR_ASSERT(!theory_rows_to_explanations_.empty(), "there must be at least one explanation");
355 DLINEAR_ASSERT(std::all_of(theory_rows_to_explanations_.begin(), theory_rows_to_explanations_.end(),
356 [](const std::set<int> &explanation) { return !explanation.empty(); }),
357 "no explanation can be empty");
358 for (const std::set<int> &theory_rows_to_explanation : theory_rows_to_explanations_) {
359 LiteralSet explanation;
360 for (const int &theory_row : theory_rows_to_explanation) explanation.insert(theory_row_to_lit_[theory_row]);
361 explanations.insert(explanation);
362 }
363 DLINEAR_ASSERT(explanations.size() == theory_rows_to_explanations_.size(), "explanations must have the same size");
364}
365
366std::set<size_t> CompleteSoplexTheorySolver::IteratorNqRowsInLastExplanation() const {
367 std::set<size_t> nq_row_to_iterator_index;
368 for (size_t i = 0; i < nq_row_to_theory_rows_.size(); i++) {
369 const int &spx_row = nq_row_to_theory_rows_[i];
370 if (last_theory_rows_to_explanation_.find(spx_row) != last_theory_rows_to_explanation_.end()) {
371 nq_row_to_iterator_index.insert(i);
372 }
373 }
374 return nq_row_to_iterator_index;
375}
376
377bool CompleteSoplexTheorySolver::UpdateBitIncrementIteratorBasedOnExplanation(BitIncrementIterator &bit_iterator) {
378 // The last iteration yielded a feasible solution, but there were some other constraints that were violated before
379 // Unlock the solver and go to the next iteration
380 if (last_theory_rows_to_explanation_.empty()) {
381 DLINEAR_ASSERT(locked_solver_, "The solver must have been locked before");
382 locked_solver_ = false;
383 if (single_nq_rows_.empty()) {
384 ++bit_iterator;
385 } else {
386 for (const auto &[nq_row, nq_row_value] : single_nq_rows_) bit_iterator.Learn(nq_row, !nq_row_value);
387 single_nq_rows_.clear();
388 }
389 EnableNqLiterals(*bit_iterator, true);
390 // If we have an explanation to return, we can stop and return UNSAT
391 return theory_rows_to_explanations_.empty();
392 }
393
394 const std::set<size_t> nq_in_explanation{IteratorNqRowsInLastExplanation()};
395 DLINEAR_TRACE_FMT("CompleteSoplexTheorySolver::CheckSat: nq_in_explanation = {}", nq_in_explanation);
396 DLINEAR_ASSERT(!nq_in_explanation.empty(), "nq_in_explanation must not be empty");
397
398 // Find and update the current explanation for this set of nq constraints
399 auto it = nq_explanations_.find(nq_in_explanation);
400 if (it == nq_explanations_.end()) {
401 DLINEAR_TRACE("CompleteSoplexTheorySolver::CheckSat: explanation not seen yet. Adding to the set");
402 it = nq_explanations_.emplace(nq_in_explanation, nq_in_explanation).first;
403 }
404
405 NqExplanation &nq_explanation = it->second;
406 const size_t idx = bool_vector_to_int(*bit_iterator, nq_in_explanation);
407 // If the same boolean combination of these non-equal rows has been tried before, we can skip it
408 if (!nq_explanation.visited[idx]) {
409 DLINEAR_TRACE("CompleteSoplexTheorySolver::CheckSat: new explanation. Adding the new explanation to the set");
410 nq_explanation.visited[idx] = true;
411 nq_explanation.explanation.insert(last_theory_rows_to_explanation_.cbegin(),
412 last_theory_rows_to_explanation_.cend());
413
414 if (nq_in_explanation.size() == 1)
415 single_nq_rows_.emplace(*nq_in_explanation.begin(), bit_iterator[*nq_in_explanation.begin()]);
416
417 // All boolean combinations of this set of non-equal rows have been tried, so the problem is UNSAT
418 if (std::all_of(nq_explanation.visited.begin(), nq_explanation.visited.end(), [](bool b) { return b; })) {
419 DLINEAR_DEBUG("CompleteSoplexTheorySolver::CheckSat: all combinations of nq_in_explanation tried. Infeasible");
420 theory_rows_to_explanations_.insert(nq_explanation.explanation);
421 }
422 }
423
424 // Remove the last infeasible constraints and try again, ensuring that the solver is locked
425 DLINEAR_DEBUG("CompleteSoplexTheorySolver::CheckSat: removing last infeasible nq constraints and try again");
426 DisableNqLiterals(nq_in_explanation);
427 locked_solver_ = true;
428 return true;
429}
430
431void CompleteSoplexTheorySolver::EnableSpxVarBound() {
432 SoplexTheorySolver::EnableSpxVarBound();
433 // For all columns...
434 for (const auto &[var, theory_bound] : preprocessor_.theory_bounds()) {
435 // ... for each active bound of that column...
436 for (BoundIterator it{theory_bound.GetActiveBound()}; it; ++it) {
437 const auto &[value, bound, lit, expl] = *it;
438 // ... if we are dealing with a strict bound, add the strict row to the LP problem
439 if (bound == LpColBound::SU || bound == LpColBound::SL || bound == LpColBound::D) {
440 SoplexTheorySolver::EnableSpxRow(lit_to_theory_row_.at(lit.var.get_id()));
441 }
442 }
443 }
444}
445
446void CompleteSoplexTheorySolver::EnableSpxRow(int spx_row, bool truth) {
447 const LpRowSense sense = truth ? spx_sense_[spx_row] : !spx_sense_[spx_row];
448 const mpq_class &rhs{spx_rhs_[spx_row]};
449 soplex::LPRowRational lp_row;
450 spx_.getRowRational(spx_row, lp_row);
451 soplex::DSVectorRational row_vector{lp_row.rowVector()};
452 // Remove the strict variable from the row and add it back with the right coefficient
453 int strict_variable_pos = row_vector.pos(STRICT_VARIABLE_IDX);
454 if (strict_variable_pos < 0) {
455 row_vector.add(STRICT_VARIABLE_IDX, 1);
456 strict_variable_pos = row_vector.pos(STRICT_VARIABLE_IDX);
457 }
458 switch (sense) {
459 case LpRowSense::LT:
460 row_vector.value(strict_variable_pos) = 1;
461 break;
462 case LpRowSense::GT:
463 row_vector.value(strict_variable_pos) = -1;
464 break;
465 case LpRowSense::EQ:
466 case LpRowSense::LE:
467 case LpRowSense::GE:
468 row_vector.value(strict_variable_pos) = 0;
469 break;
470 case LpRowSense::NQ:
471 // No need to initialise the strict variable here since it will be enabled with all the nq rows
472 nq_row_to_theory_rows_.push_back(spx_row);
473 break;
474 default:
475 DLINEAR_UNREACHABLE();
476 }
477
478 lp_row.setRowVector(row_vector);
479 lp_row.setLhs(sense == LpRowSense::GE || sense == LpRowSense::EQ || sense == LpRowSense::GT
480 ? Rational(gmp::ToMpq(rhs))
481 : Rational(-soplex::infinity));
482 lp_row.setRhs(sense == LpRowSense::LE || sense == LpRowSense::EQ || sense == LpRowSense::LT
483 ? Rational(gmp::ToMpq(rhs))
484 : Rational(soplex::infinity));
485 spx_.changeRowRational(spx_row, lp_row);
486
487 theory_rows_state_.at(spx_row) = true;
488 DLINEAR_TRACE_FMT("CompleteSoplexTheorySolver::EnableLinearLiteral: Row({}, {}) ↦ {} {} {} | Sense({})", spx_row,
489 theory_row_to_lit_[spx_row], lp_row.lhs(), lp_row.rowVector(), lp_row.rhs(), sense);
490}
491
492CompleteSoplexTheorySolver::NqExplanation::NqExplanation(size_t size) : explanation{}, visited(size, false) {}
493CompleteSoplexTheorySolver::NqExplanation::NqExplanation(const std::set<size_t> &bits)
494 : explanation{}, visited(1 << bits.size(), false) {}
495
496} // namespace dlinear
BitIncrementIterator class.
bool Learn(std::size_t i)
Learn the value of the bit at position i by inverting the bit.
BoundIterator class.
Collection of variables with associated intervals.
Definition Box.h:31
static const mpq_class strict_col_lb_
Zero. Used for the strict variable lower bound.
static const mpq_class strict_col_ub_
One. Used for the strict variable upper bound.
std::set< LiteralSet > Explanations
Set of explanations of the conflict.
Represents a symbolic form of a first-order logic formula.
const Variables & GetFreeVariables() const
Gets free variables (unquantified variables).
Represents a symbolic variable.
Global namespace for the dlinear library.
SatResult
Result of running the theory solver over the literals decided by the SAT solver.
Definition SatResult.h:17
std::set< Literal > LiteralSet
A set of literals.
Definition literal.h:28
LpRowSense parseLpSense(const Formula &f)
Parse the sense from a formula.
LpRowSense
Sense of a linear programming row describing a constraint.
Definition LpRowSense.h:23
Structure used to store the infeasibility explanation of a subset of non-equal constraints.
std::set< int > explanation
Indexes of a subset of non-equal constraints that are part of the explanation.
std::vector< bool > visited
Configuration of the non-equal constraints that have been visited.
A literal is a variable with an associated truth value, indicating whether it is true or false.
Definition literal.h:24
Variable var
Variable.
Definition literal.h:25