Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
sat_solver.cc
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cstddef>
18#include <cstdint>
19#include <limits>
20#include <memory>
21#include <string>
22#include <utility>
23#include <vector>
24
25#include "absl/base/attributes.h"
26#include "absl/container/btree_set.h"
27#include "absl/container/flat_hash_map.h"
28#include "absl/log/check.h"
29#include "absl/meta/type_traits.h"
30#include "absl/strings/str_cat.h"
31#include "absl/strings/str_format.h"
32#include "absl/types/span.h"
35#include "ortools/base/timer.h"
38#include "ortools/sat/clause.h"
40#include "ortools/sat/model.h"
42#include "ortools/sat/restart.h"
45#include "ortools/sat/sat_parameters.pb.h"
46#include "ortools/sat/util.h"
47#include "ortools/util/bitset.h"
50#include "ortools/util/stats.h"
53
54namespace operations_research {
55namespace sat {
56
58 owned_model_.reset(model_);
59 model_->Register<SatSolver>(this);
60 logger_ = model_->GetOrCreate<SolverLogger>();
61}
62
64 : model_(model),
65 binary_implication_graph_(model->GetOrCreate<BinaryImplicationGraph>()),
66 clauses_propagator_(model->GetOrCreate<ClauseManager>()),
67 pb_constraints_(model->GetOrCreate<PbConstraints>()),
68 track_binary_clauses_(false),
69 trail_(model->GetOrCreate<Trail>()),
70 time_limit_(model->GetOrCreate<TimeLimit>()),
71 parameters_(model->GetOrCreate<SatParameters>()),
72 restart_(model->GetOrCreate<RestartPolicy>()),
73 decision_policy_(model->GetOrCreate<SatDecisionPolicy>()),
74 logger_(model->GetOrCreate<SolverLogger>()),
75 clause_activity_increment_(1.0),
76 same_reason_identifier_(*trail_),
77 is_relevant_for_core_computation_(true),
78 drat_proof_handler_(nullptr),
79 stats_("SatSolver") {
80 InitializePropagators();
81}
82
84
85void SatSolver::SetNumVariables(int num_variables) {
86 SCOPED_TIME_STAT(&stats_);
87 CHECK_GE(num_variables, num_variables_);
88
89 num_variables_ = num_variables;
90 binary_implication_graph_->Resize(num_variables);
91 clauses_propagator_->Resize(num_variables);
92 trail_->Resize(num_variables);
93 decision_policy_->IncreaseNumVariables(num_variables);
94 pb_constraints_->Resize(num_variables);
95 same_reason_identifier_.Resize(num_variables);
96
97 // The +1 is a bit tricky, it is because in
98 // EnqueueDecisionAndBacktrackOnConflict() we artificially enqueue the
99 // decision before checking if it is not already assigned.
100 decisions_.resize(num_variables + 1);
101}
102
103int64_t SatSolver::num_branches() const { return counters_.num_branches; }
104
105int64_t SatSolver::num_failures() const { return counters_.num_failures; }
106
108 return trail_->NumberOfEnqueues() - counters_.num_branches;
109}
110
111int64_t SatSolver::num_backtracks() const { return counters_.num_backtracks; }
112
113int64_t SatSolver::num_restarts() const { return counters_.num_restarts; }
114
116 // Each of these counters mesure really basic operations. The weight are just
117 // an estimate of the operation complexity. Note that these counters are never
118 // reset to zero once a SatSolver is created.
119 //
120 // TODO(user): Find a better procedure to fix the weight than just educated
121 // guess.
122 return 1e-8 * (8.0 * trail_->NumberOfEnqueues() +
123 1.0 * binary_implication_graph_->num_inspections() +
124 4.0 * clauses_propagator_->num_inspected_clauses() +
125 1.0 * clauses_propagator_->num_inspected_clause_literals() +
126
127 // Here there is a factor 2 because of the untrail.
128 20.0 * pb_constraints_->num_constraint_lookups() +
129 2.0 * pb_constraints_->num_threshold_updates() +
130 1.0 * pb_constraints_->num_inspected_constraint_literals());
131}
132
133const SatParameters& SatSolver::parameters() const {
134 SCOPED_TIME_STAT(&stats_);
135 return *parameters_;
136}
137
138void SatSolver::SetParameters(const SatParameters& parameters) {
139 SCOPED_TIME_STAT(&stats_);
140 *parameters_ = parameters;
141 restart_->Reset();
143 logger_->EnableLogging(parameters.log_search_progress() || VLOG_IS_ON(1));
144 logger_->SetLogToStdOut(parameters.log_to_stdout());
145}
146
147bool SatSolver::IsMemoryLimitReached() const {
148 const int64_t memory_usage =
150 const int64_t kMegaByte = 1024 * 1024;
151 return memory_usage > kMegaByte * parameters_->max_memory_in_mb();
152}
153
154bool SatSolver::SetModelUnsat() {
155 model_is_unsat_ = true;
156 return false;
157}
158
159bool SatSolver::AddClauseDuringSearch(absl::Span<const Literal> literals) {
160 if (model_is_unsat_) return false;
161
162 // Let filter clauses if we are at level zero
163 if (trail_->CurrentDecisionLevel() == 0) {
164 return AddProblemClause(literals, /*is_safe=*/false);
165 }
166
167 const int index = trail_->Index();
168 if (literals.empty()) return SetModelUnsat();
169 if (literals.size() == 1) return AddUnitClause(literals[0]);
170 if (literals.size() == 2) {
171 // TODO(user): We generate in some corner cases clauses with
172 // literals[0].Variable() == literals[1].Variable(). Avoid doing that and
173 // adding such binary clauses to the graph?
174 if (!binary_implication_graph_->AddBinaryClause(literals[0], literals[1])) {
175 CHECK_EQ(CurrentDecisionLevel(), 0);
176 return SetModelUnsat();
177 }
178 } else {
179 if (!clauses_propagator_->AddClause(literals)) {
180 CHECK_EQ(CurrentDecisionLevel(), 0);
181 return SetModelUnsat();
182 }
183 }
184
185 // Tricky: Even if nothing new is propagated, calling Propagate() might, via
186 // the LP, deduce new things. This is problematic because some code assumes
187 // that when we create newly associated literals, nothing else changes.
188 if (trail_->Index() == index) return true;
189 return FinishPropagation();
190}
191
193 return AddProblemClause({true_literal});
194}
195
199
203
204// Note(user): we assume there is no duplicate literals in the clauses added
205// here if is_safe is true. Most of the code works, but some advanced algo might
206// be wrong/suboptimal if this is the case. So even when presolve is off we need
207// some "cleanup" to enforce this invariant. Alternatively we could have robut
208// algo in all the stack, but that seems a worse design.
209bool SatSolver::AddProblemClause(absl::Span<const Literal> literals,
210 bool is_safe) {
211 SCOPED_TIME_STAT(&stats_);
212 if (model_is_unsat_) return false;
213
214 // Filter already assigned literals.
215 if (CurrentDecisionLevel() == 0) {
216 literals_scratchpad_.clear();
217 for (const Literal l : literals) {
218 if (trail_->Assignment().LiteralIsTrue(l)) return true;
219 if (trail_->Assignment().LiteralIsFalse(l)) continue;
220 literals_scratchpad_.push_back(l);
221 }
222 } else {
223 literals_scratchpad_.assign(literals.begin(), literals.end());
224 }
225
226 if (!is_safe) {
227 gtl::STLSortAndRemoveDuplicates(&literals_scratchpad_);
228 for (int i = 0; i + 1 < literals_scratchpad_.size(); ++i) {
229 if (literals_scratchpad_[i] == literals_scratchpad_[i + 1].Negated()) {
230 return true;
231 }
232 }
233 }
234
235 if (!AddProblemClauseInternal(literals_scratchpad_)) return false;
236
237 // Tricky: The PropagationIsDone() condition shouldn't change anything for a
238 // pure SAT problem, however in the CP-SAT context, calling Propagate() can
239 // tigger computation (like the LP) even if no domain changed since the last
240 // call. We do not want to do that.
241 if (!PropagationIsDone() && !Propagate()) {
242 return SetModelUnsat();
243 }
244 return true;
245}
246
247bool SatSolver::AddProblemClauseInternal(absl::Span<const Literal> literals) {
248 SCOPED_TIME_STAT(&stats_);
249 if (DEBUG_MODE && CurrentDecisionLevel() == 0) {
250 for (const Literal l : literals) {
251 CHECK(!trail_->Assignment().LiteralIsAssigned(l));
252 }
253 }
254
255 if (literals.empty()) return SetModelUnsat();
256
257 if (literals.size() == 1) {
258 if (drat_proof_handler_ != nullptr) {
259 // Note that we will output problem unit clauses twice, but that is a
260 // small price to pay for having a single variable fixing API.
261 drat_proof_handler_->AddClause({literals[0]});
262 }
263 trail_->EnqueueWithUnitReason(literals[0]);
264 } else if (literals.size() == 2) {
265 // TODO(user): Make sure the presolve do not generate such clauses.
266 if (literals[0] == literals[1]) {
267 // Literal must be true.
268 trail_->EnqueueWithUnitReason(literals[0]);
269 } else if (literals[0] == literals[1].Negated()) {
270 // Always true.
271 return true;
272 } else {
273 AddBinaryClauseInternal(literals[0], literals[1]);
274 }
275 } else {
276 if (!clauses_propagator_->AddClause(literals, trail_, /*lbd=*/-1)) {
277 return SetModelUnsat();
278 }
279 }
280
281 return true;
282}
283
284bool SatSolver::AddLinearConstraintInternal(
285 const std::vector<LiteralWithCoeff>& cst, Coefficient rhs,
286 Coefficient max_value) {
287 SCOPED_TIME_STAT(&stats_);
289 if (rhs < 0) return SetModelUnsat(); // Unsatisfiable constraint.
290 if (rhs >= max_value) return true; // Always satisfied constraint.
291
292 // Since the constraint is in canonical form, the coefficients are sorted.
293 const Coefficient min_coeff = cst.front().coefficient;
294 const Coefficient max_coeff = cst.back().coefficient;
295
296 // A linear upper bounded constraint is a clause if the only problematic
297 // assignment is the one where all the literals are true.
298 if (max_value - min_coeff <= rhs) {
299 // This constraint is actually a clause. It is faster to treat it as one.
300 literals_scratchpad_.clear();
301 for (const LiteralWithCoeff& term : cst) {
302 literals_scratchpad_.push_back(term.literal.Negated());
303 }
304 return AddProblemClauseInternal(literals_scratchpad_);
305 }
306
307 // Detect at most one constraints. Note that this use the fact that the
308 // coefficient are sorted.
309 if (!parameters_->use_pb_resolution() && max_coeff <= rhs &&
310 2 * min_coeff > rhs) {
311 literals_scratchpad_.clear();
312 for (const LiteralWithCoeff& term : cst) {
313 literals_scratchpad_.push_back(term.literal);
314 }
315 if (!binary_implication_graph_->AddAtMostOne(literals_scratchpad_)) {
316 return SetModelUnsat();
317 }
318 return true;
319 }
320
321 // TODO(user): If this constraint forces all its literal to false (when rhs is
322 // zero for instance), we still add it. Optimize this?
323 return pb_constraints_->AddConstraint(cst, rhs, trail_);
324}
325
326void SatSolver::CanonicalizeLinear(std::vector<LiteralWithCoeff>* cst,
327 Coefficient* bound_shift,
328 Coefficient* max_value) {
329 // This block removes assigned literals from the constraint.
330 Coefficient fixed_variable_shift(0);
331 {
332 int index = 0;
333 for (const LiteralWithCoeff& term : *cst) {
334 if (trail_->Assignment().LiteralIsFalse(term.literal)) continue;
335 if (trail_->Assignment().LiteralIsTrue(term.literal)) {
336 CHECK(SafeAddInto(-term.coefficient, &fixed_variable_shift));
337 continue;
338 }
339 (*cst)[index] = term;
340 ++index;
341 }
342 cst->resize(index);
343 }
344
345 // Now we canonicalize.
346 // TODO(user): fix variables that must be true/false and remove them.
347 Coefficient bound_delta(0);
348 CHECK(ComputeBooleanLinearExpressionCanonicalForm(cst, &bound_delta,
349 max_value));
350
351 CHECK(SafeAddInto(bound_delta, bound_shift));
352 CHECK(SafeAddInto(fixed_variable_shift, bound_shift));
353}
354
355bool SatSolver::AddLinearConstraint(bool use_lower_bound,
356 Coefficient lower_bound,
357 bool use_upper_bound,
358 Coefficient upper_bound,
359 std::vector<LiteralWithCoeff>* cst) {
360 SCOPED_TIME_STAT(&stats_);
361 CHECK_EQ(CurrentDecisionLevel(), 0);
362 if (model_is_unsat_) return false;
363
364 Coefficient bound_shift(0);
365
366 if (use_upper_bound) {
367 Coefficient max_value(0);
368 CanonicalizeLinear(cst, &bound_shift, &max_value);
369 const Coefficient rhs =
370 ComputeCanonicalRhs(upper_bound, bound_shift, max_value);
371 if (!AddLinearConstraintInternal(*cst, rhs, max_value)) {
372 return SetModelUnsat();
373 }
374 }
375
376 if (use_lower_bound) {
377 // We need to "re-canonicalize" in case some literal were fixed while we
378 // processed one direction.
379 Coefficient max_value(0);
380 CanonicalizeLinear(cst, &bound_shift, &max_value);
381
382 // We transform the constraint into an upper-bounded one.
383 for (int i = 0; i < cst->size(); ++i) {
384 (*cst)[i].literal = (*cst)[i].literal.Negated();
385 }
386 const Coefficient rhs =
387 ComputeNegatedCanonicalRhs(lower_bound, bound_shift, max_value);
388 if (!AddLinearConstraintInternal(*cst, rhs, max_value)) {
389 return SetModelUnsat();
390 }
391 }
392
393 // Tricky: The PropagationIsDone() condition shouldn't change anything for a
394 // pure SAT problem, however in the CP-SAT context, calling Propagate() can
395 // tigger computation (like the LP) even if no domain changed since the last
396 // call. We do not want to do that.
397 if (!PropagationIsDone() && !Propagate()) {
398 return SetModelUnsat();
399 }
400 return true;
401}
402
403int SatSolver::AddLearnedClauseAndEnqueueUnitPropagation(
404 const std::vector<Literal>& literals, bool is_redundant) {
405 SCOPED_TIME_STAT(&stats_);
406
407 if (literals.size() == 1) {
408 // A length 1 clause fix a literal for all the search.
409 // ComputeBacktrackLevel() should have returned 0.
410 CHECK_EQ(CurrentDecisionLevel(), 0);
411 trail_->EnqueueWithUnitReason(literals[0]);
412 return /*lbd=*/1;
413 }
414
415 if (literals.size() == 2) {
416 if (track_binary_clauses_) {
417 // This clause MUST be knew, otherwise something is wrong.
418 CHECK(binary_clauses_.Add(BinaryClause(literals[0], literals[1])));
419 }
420 CHECK(binary_implication_graph_->AddBinaryClause(literals[0], literals[1]));
421 return /*lbd=*/2;
422 }
423
424 CleanClauseDatabaseIfNeeded();
425
426 // Important: Even though the only literal at the last decision level has
427 // been unassigned, its level was not modified, so ComputeLbd() works.
428 const int lbd = ComputeLbd(literals);
429 if (is_redundant && lbd > parameters_->clause_cleanup_lbd_bound()) {
430 --num_learned_clause_before_cleanup_;
431
432 SatClause* clause =
433 clauses_propagator_->AddRemovableClause(literals, trail_, lbd);
434
435 // BumpClauseActivity() must be called after clauses_info_[clause] has
436 // been created or it will have no effect.
437 (*clauses_propagator_->mutable_clauses_info())[clause].lbd = lbd;
438 BumpClauseActivity(clause);
439 } else {
440 CHECK(clauses_propagator_->AddClause(literals, trail_, lbd));
441 }
442 return lbd;
443}
444
446 CHECK_EQ(CurrentDecisionLevel(), 0);
447 trail_->RegisterPropagator(propagator);
448 external_propagators_.push_back(propagator);
449 InitializePropagators();
450}
451
453 CHECK_EQ(CurrentDecisionLevel(), 0);
454 CHECK(last_propagator_ == nullptr);
455 trail_->RegisterPropagator(propagator);
456 last_propagator_ = propagator;
457 InitializePropagators();
458}
459
460UpperBoundedLinearConstraint* SatSolver::ReasonPbConstraintOrNull(
461 BooleanVariable var) const {
462 // It is important to deal properly with "SameReasonAs" variables here.
464 const AssignmentInfo& info = trail_->Info(var);
465 if (trail_->AssignmentType(var) == pb_constraints_->PropagatorId()) {
466 return pb_constraints_->ReasonPbConstraint(info.trail_index);
467 }
468 return nullptr;
469}
470
471SatClause* SatSolver::ReasonClauseOrNull(BooleanVariable var) const {
472 DCHECK(trail_->Assignment().VariableIsAssigned(var));
473 const AssignmentInfo& info = trail_->Info(var);
474 if (trail_->AssignmentType(var) == clauses_propagator_->PropagatorId()) {
475 return clauses_propagator_->ReasonClause(info.trail_index);
476 }
477 return nullptr;
478}
479
481 debug_assignment_.Resize(num_variables_.value());
482 for (BooleanVariable i(0); i < num_variables_; ++i) {
483 debug_assignment_.AssignFromTrueLiteral(
485 }
486}
487
488void SatSolver::LoadDebugSolution(const std::vector<Literal>& solution) {
489 debug_assignment_.Resize(num_variables_.value());
490 for (BooleanVariable var(0); var < num_variables_; ++var) {
491 if (!debug_assignment_.VariableIsAssigned(var)) continue;
492 debug_assignment_.UnassignLiteral(Literal(var, true));
493 }
494 for (const Literal l : solution) {
495 debug_assignment_.AssignFromTrueLiteral(l);
496 }
497
498 // We should only call this with complete solution.
499 for (BooleanVariable var(0); var < num_variables_; ++var) {
500 CHECK(debug_assignment_.VariableIsAssigned(var));
501 }
502}
503
504void SatSolver::AddBinaryClauseInternal(Literal a, Literal b) {
505 if (track_binary_clauses_) {
506 // Abort if this clause was already added.
507 if (!binary_clauses_.Add(BinaryClause(a, b))) return;
508 }
509
510 if (!binary_implication_graph_->AddBinaryClause(a, b)) {
511 CHECK_EQ(CurrentDecisionLevel(), 0);
512 SetModelUnsat();
513 }
514}
515
516bool SatSolver::ClauseIsValidUnderDebugAssignment(
517 absl::Span<const Literal> clause) const {
518 if (debug_assignment_.NumberOfVariables() == 0) return true;
519 for (Literal l : clause) {
520 if (l.Variable() >= debug_assignment_.NumberOfVariables() ||
521 debug_assignment_.LiteralIsTrue(l)) {
522 return true;
523 }
524 }
525 return false;
526}
527
528bool SatSolver::PBConstraintIsValidUnderDebugAssignment(
529 const std::vector<LiteralWithCoeff>& cst, const Coefficient rhs) const {
530 Coefficient sum(0.0);
531 for (LiteralWithCoeff term : cst) {
532 if (term.literal.Variable() >= debug_assignment_.NumberOfVariables()) {
533 continue;
534 }
535 if (debug_assignment_.LiteralIsTrue(term.literal)) {
536 sum += term.coefficient;
537 }
538 }
539 return sum <= rhs;
540}
541
542namespace {
543
544// Returns true iff 'b' is subsumed by 'a' (i.e 'a' is included in 'b').
545// This is slow and only meant to be used in DCHECKs.
546bool ClauseSubsumption(const std::vector<Literal>& a, SatClause* b) {
547 std::vector<Literal> superset(b->begin(), b->end());
548 std::vector<Literal> subset(a.begin(), a.end());
549 std::sort(superset.begin(), superset.end());
550 std::sort(subset.begin(), subset.end());
551 return std::includes(superset.begin(), superset.end(), subset.begin(),
552 subset.end());
553}
554
555} // namespace
556
558 SCOPED_TIME_STAT(&stats_);
559 if (model_is_unsat_) return kUnsatTrailIndex;
560 DCHECK(PropagationIsDone());
561
562 // We should never enqueue before the assumptions_.
563 if (DEBUG_MODE && !assumptions_.empty()) {
564 CHECK_GE(current_decision_level_, assumption_level_);
565 }
566
567 EnqueueNewDecision(true_literal);
568 if (!FinishPropagation()) return kUnsatTrailIndex;
569 return last_decision_or_backtrack_trail_index_;
570}
571
573 if (model_is_unsat_) return false;
574 if (CurrentDecisionLevel() > assumption_level_) {
575 Backtrack(assumption_level_);
576 return true;
577 }
578 if (!FinishPropagation()) return false;
580}
581
583 if (model_is_unsat_) return false;
584 int num_loop = 0;
585 while (true) {
586 const int old_decision_level = current_decision_level_;
587 if (!Propagate()) {
589 if (model_is_unsat_) return false;
590 if (current_decision_level_ == old_decision_level) {
591 CHECK(!assumptions_.empty());
592 return false;
593 }
594
595 if (++num_loop % 16 == 0 && time_limit_->LimitReached()) {
596 // TODO(user): Exiting like this might cause issue since the propagation
597 // is not "finished" but some code might assume it is. However since we
598 // already might repropagate in the LP constraint, most of the code
599 // should support "not finished propagation".
600 return true;
601 }
602 continue;
603 }
604 break;
605 }
606 DCHECK(PropagationIsDone());
607 return true;
608}
609
611 if (model_is_unsat_) return false;
612 assumption_level_ = 0;
613 assumptions_.clear();
614 Backtrack(0);
615 return FinishPropagation();
616}
617
619 const std::vector<Literal>& assumptions) {
620 if (!ResetToLevelZero()) return false;
621 if (assumptions.empty()) return true;
622
623 // For assumptions and core-based search, it is really important to add as
624 // many binary clauses as possible. This is because we do not wan to miss any
625 // early core of size 2.
627
628 DCHECK(assumptions_.empty());
629 assumption_level_ = 1;
630 assumptions_ = assumptions;
632}
633
634// Note that we do not count these as "branches" for a reporting purpose.
636 if (model_is_unsat_) return false;
637 if (CurrentDecisionLevel() >= assumption_level_) return true;
638
639 if (CurrentDecisionLevel() == 0 && !assumptions_.empty()) {
640 // When assumptions_ is not empty, the first "decision" actually contains
641 // multiple one, and we should never use its literal.
642 CHECK_EQ(current_decision_level_, 0);
643 last_decision_or_backtrack_trail_index_ = trail_->Index();
644 decisions_[0] = Decision(trail_->Index(), Literal());
645
646 ++current_decision_level_;
647 trail_->SetDecisionLevel(current_decision_level_);
648
649 // We enqueue all assumptions at once at decision level 1.
650 int num_decisions = 0;
651 for (const Literal lit : assumptions_) {
652 if (Assignment().LiteralIsTrue(lit)) continue;
653 if (Assignment().LiteralIsFalse(lit)) {
654 // See GetLastIncompatibleDecisions().
655 *trail_->MutableConflict() = {lit.Negated(), lit};
656 if (num_decisions == 0) {
657 // This is needed to avoid an empty level that cause some CHECK fail.
658 current_decision_level_ = 0;
659 trail_->SetDecisionLevel(0);
660 }
661 return false;
662 }
663 ++num_decisions;
664 trail_->EnqueueSearchDecision(lit);
665 }
666
667 // Corner case: all assumptions are fixed at level zero, we ignore them.
668 if (num_decisions == 0) {
669 current_decision_level_ = 0;
670 trail_->SetDecisionLevel(0);
671 return ResetToLevelZero();
672 }
673
674 // Now that everything is enqueued, we propagate.
675 return FinishPropagation();
676 }
677
678 DCHECK(assumptions_.empty());
679 const int64_t old_num_branches = counters_.num_branches;
680 const SatSolver::Status status = ReapplyDecisionsUpTo(assumption_level_ - 1);
681 counters_.num_branches = old_num_branches;
682 assumption_level_ = CurrentDecisionLevel();
683 return (status == SatSolver::FEASIBLE);
684}
685
687 SCOPED_TIME_STAT(&stats_);
688 if (model_is_unsat_) return;
689
690 ++counters_.num_failures;
691 const int conflict_trail_index = trail_->Index();
692 const int conflict_decision_level = current_decision_level_;
693
694 // A conflict occurred, compute a nice reason for this failure.
695 same_reason_identifier_.Clear();
696 const int max_trail_index = ComputeMaxTrailIndex(trail_->FailingClause());
697 if (!assumptions_.empty() && !trail_->FailingClause().empty()) {
698 // If the failing clause only contains literal at the assumptions level,
699 // we cannot use the ComputeFirstUIPConflict() code as we might have more
700 // than one decision.
701 //
702 // TODO(user): We might still want to "learn" the clause, especially if
703 // it reduces to only one literal in which case we can just fix it.
704 const int highest_level =
705 DecisionLevel((*trail_)[max_trail_index].Variable());
706 if (highest_level == 1) return;
707 }
708
709 ComputeFirstUIPConflict(max_trail_index, &learned_conflict_,
710 &reason_used_to_infer_the_conflict_,
711 &subsumed_clauses_);
712
713 // An empty conflict means that the problem is UNSAT.
714 if (learned_conflict_.empty()) return (void)SetModelUnsat();
715 DCHECK(IsConflictValid(learned_conflict_));
716 DCHECK(ClauseIsValidUnderDebugAssignment(learned_conflict_));
717
718 // Update the activity of all the variables in the first UIP clause.
719 // Also update the activity of the last level variables expanded (and
720 // thus discarded) during the first UIP computation. Note that both
721 // sets are disjoint.
722 decision_policy_->BumpVariableActivities(learned_conflict_);
723 decision_policy_->BumpVariableActivities(reason_used_to_infer_the_conflict_);
724 if (parameters_->also_bump_variables_in_conflict_reasons()) {
725 ComputeUnionOfReasons(learned_conflict_, &extra_reason_literals_);
726 decision_policy_->BumpVariableActivities(extra_reason_literals_);
727 }
728
729 // Bump the clause activities.
730 // Note that the activity of the learned clause will be bumped too
731 // by AddLearnedClauseAndEnqueueUnitPropagation().
732 if (trail_->FailingSatClause() != nullptr) {
733 BumpClauseActivity(trail_->FailingSatClause());
734 }
735 BumpReasonActivities(reason_used_to_infer_the_conflict_);
736
737 // Decay the activities.
738 decision_policy_->UpdateVariableActivityIncrement();
739 UpdateClauseActivityIncrement();
740 pb_constraints_->UpdateActivityIncrement();
741
742 // Hack from Glucose that seems to perform well.
743 const int period = parameters_->glucose_decay_increment_period();
744 const double max_decay = parameters_->glucose_max_decay();
745 if (counters_.num_failures % period == 0 &&
746 parameters_->variable_activity_decay() < max_decay) {
747 parameters_->set_variable_activity_decay(
748 parameters_->variable_activity_decay() +
749 parameters_->glucose_decay_increment());
750 }
751
752 // PB resolution.
753 // There is no point using this if the conflict and all the reasons involved
754 // in its resolution were clauses.
755 bool compute_pb_conflict = false;
756 if (parameters_->use_pb_resolution()) {
757 compute_pb_conflict = (pb_constraints_->ConflictingConstraint() != nullptr);
758 if (!compute_pb_conflict) {
759 for (Literal lit : reason_used_to_infer_the_conflict_) {
760 if (ReasonPbConstraintOrNull(lit.Variable()) != nullptr) {
761 compute_pb_conflict = true;
762 break;
763 }
764 }
765 }
766 }
767
768 // TODO(user): Note that we use the clause above to update the variable
769 // activities and not the pb conflict. Experiment.
770 if (compute_pb_conflict) {
771 pb_conflict_.ClearAndResize(num_variables_.value());
772 Coefficient initial_slack(-1);
773 if (pb_constraints_->ConflictingConstraint() == nullptr) {
774 // Generic clause case.
775 Coefficient num_literals(0);
776 for (Literal literal : trail_->FailingClause()) {
777 pb_conflict_.AddTerm(literal.Negated(), Coefficient(1.0));
778 ++num_literals;
779 }
780 pb_conflict_.AddToRhs(num_literals - 1);
781 } else {
782 // We have a pseudo-Boolean conflict, so we start from there.
783 pb_constraints_->ConflictingConstraint()->AddToConflict(&pb_conflict_);
784 pb_constraints_->ClearConflictingConstraint();
785 initial_slack =
786 pb_conflict_.ComputeSlackForTrailPrefix(*trail_, max_trail_index + 1);
787 }
788
789 int pb_backjump_level;
790 ComputePBConflict(max_trail_index, initial_slack, &pb_conflict_,
791 &pb_backjump_level);
792 if (pb_backjump_level == -1) return (void)SetModelUnsat();
793
794 // Convert the conflict into the vector<LiteralWithCoeff> form.
795 std::vector<LiteralWithCoeff> cst;
796 pb_conflict_.CopyIntoVector(&cst);
797 DCHECK(PBConstraintIsValidUnderDebugAssignment(cst, pb_conflict_.Rhs()));
798
799 // Check if the learned PB conflict is just a clause:
800 // all its coefficient must be 1, and the rhs must be its size minus 1.
801 bool conflict_is_a_clause = (pb_conflict_.Rhs() == cst.size() - 1);
802 if (conflict_is_a_clause) {
803 for (LiteralWithCoeff term : cst) {
804 if (term.coefficient != Coefficient(1)) {
805 conflict_is_a_clause = false;
806 break;
807 }
808 }
809 }
810
811 if (!conflict_is_a_clause) {
812 // Use the PB conflict.
813 DCHECK_GT(pb_constraints_->NumberOfConstraints(), 0);
814 CHECK_LT(pb_backjump_level, CurrentDecisionLevel());
815 Backtrack(pb_backjump_level);
816 CHECK(pb_constraints_->AddLearnedConstraint(cst, pb_conflict_.Rhs(),
817 trail_));
818 CHECK_GT(trail_->Index(), last_decision_or_backtrack_trail_index_);
819 counters_.num_learned_pb_literals += cst.size();
820 return;
821 }
822
823 // Continue with the normal clause flow, but use the PB conflict clause
824 // if it has a lower backjump level.
825 if (pb_backjump_level < ComputeBacktrackLevel(learned_conflict_)) {
826 subsumed_clauses_.clear(); // Because the conflict changes.
827 learned_conflict_.clear();
828 is_marked_.ClearAndResize(num_variables_);
829 int max_level = 0;
830 int max_index = 0;
831 for (LiteralWithCoeff term : cst) {
832 DCHECK(Assignment().LiteralIsTrue(term.literal));
833 DCHECK_EQ(term.coefficient, 1);
834 const int level = trail_->Info(term.literal.Variable()).level;
835 if (level == 0) continue;
836 if (level > max_level) {
837 max_level = level;
838 max_index = learned_conflict_.size();
839 }
840 learned_conflict_.push_back(term.literal.Negated());
841
842 // The minimization functions below expect the conflict to be marked!
843 // TODO(user): This is error prone, find a better way?
844 is_marked_.Set(term.literal.Variable());
845 }
846 CHECK(!learned_conflict_.empty());
847 std::swap(learned_conflict_.front(), learned_conflict_[max_index]);
848 DCHECK(IsConflictValid(learned_conflict_));
849 }
850 }
851
852 // Minimizing the conflict with binary clauses first has two advantages.
853 // First, there is no need to compute a reason for the variables eliminated
854 // this way. Second, more variables may be marked (in is_marked_) and
855 // MinimizeConflict() can take advantage of that. Because of this, the
856 // LBD of the learned conflict can change.
857 DCHECK(ClauseIsValidUnderDebugAssignment(learned_conflict_));
858 if (!binary_implication_graph_->IsEmpty()) {
859 if (parameters_->binary_minimization_algorithm() ==
860 SatParameters::BINARY_MINIMIZATION_FIRST) {
861 binary_implication_graph_->MinimizeConflictFirst(
862 *trail_, &learned_conflict_, &is_marked_);
863 } else if (parameters_->binary_minimization_algorithm() ==
864 SatParameters::
865 BINARY_MINIMIZATION_FIRST_WITH_TRANSITIVE_REDUCTION) {
866 binary_implication_graph_->MinimizeConflictFirstWithTransitiveReduction(
867 *trail_, &learned_conflict_,
869 }
870 DCHECK(IsConflictValid(learned_conflict_));
871 }
872
873 // Minimize the learned conflict.
874 MinimizeConflict(&learned_conflict_);
875
876 // Minimize it further with binary clauses?
877 if (!binary_implication_graph_->IsEmpty()) {
878 // Note that on the contrary to the MinimizeConflict() above that
879 // just uses the reason graph, this minimization can change the
880 // clause LBD and even the backtracking level.
881 switch (parameters_->binary_minimization_algorithm()) {
882 case SatParameters::NO_BINARY_MINIMIZATION:
883 ABSL_FALLTHROUGH_INTENDED;
884 case SatParameters::BINARY_MINIMIZATION_FIRST:
885 ABSL_FALLTHROUGH_INTENDED;
886 case SatParameters::BINARY_MINIMIZATION_FIRST_WITH_TRANSITIVE_REDUCTION:
887 break;
888 case SatParameters::BINARY_MINIMIZATION_WITH_REACHABILITY:
889 binary_implication_graph_->MinimizeConflictWithReachability(
890 &learned_conflict_);
891 break;
892 case SatParameters::EXPERIMENTAL_BINARY_MINIMIZATION:
893 binary_implication_graph_->MinimizeConflictExperimental(
894 *trail_, &learned_conflict_);
895 break;
896 }
897 DCHECK(IsConflictValid(learned_conflict_));
898 }
899
900 // We notify the decision before backtracking so that we can save the phase.
901 // The current heuristic is to try to take a trail prefix for which there is
902 // currently no conflict (hence just before the last decision was taken).
903 //
904 // TODO(user): It is unclear what the best heuristic is here. Both the current
905 // trail index or the trail before the current decision perform well, but
906 // using the full trail seems slightly better even though it will contain the
907 // current conflicting literal.
908 decision_policy_->BeforeConflict(trail_->Index());
909
910 // Backtrack and add the reason to the set of learned clause.
911 counters_.num_literals_learned += learned_conflict_.size();
912 Backtrack(ComputeBacktrackLevel(learned_conflict_));
913 DCHECK(ClauseIsValidUnderDebugAssignment(learned_conflict_));
914
915 // Note that we need to output the learned clause before cleaning the clause
916 // database. This is because we already backtracked and some of the clauses
917 // that were needed to infer the conflict may not be "reasons" anymore and
918 // may be deleted.
919 if (drat_proof_handler_ != nullptr) {
920 drat_proof_handler_->AddClause(learned_conflict_);
921 }
922
923 // Because we might change the conflict with this minimization algorithm, we
924 // cannot just subsume clauses with it blindly.
925 //
926 // TODO(user): Either remove that algorithm or support subsumption by just
927 // checking if it is okay to do so, or doing it on the fly while computing the
928 // first UIP.
929 if (parameters_->minimization_algorithm() == SatParameters::EXPERIMENTAL) {
930 subsumed_clauses_.clear();
931 }
932
933 // Detach any subsumed clause. They will actually be deleted on the next
934 // clause cleanup phase.
935 bool is_redundant = true;
936 if (!subsumed_clauses_.empty() &&
937 parameters_->subsumption_during_conflict_analysis()) {
938 for (SatClause* clause : subsumed_clauses_) {
939 DCHECK(ClauseSubsumption(learned_conflict_, clause));
940 if (!clauses_propagator_->IsRemovable(clause)) {
941 is_redundant = false;
942 }
943 clauses_propagator_->LazyDetach(clause);
944 }
945 clauses_propagator_->CleanUpWatchers();
946 counters_.num_subsumed_clauses += subsumed_clauses_.size();
947 }
948
949 // Create and attach the new learned clause.
950 const int conflict_lbd = AddLearnedClauseAndEnqueueUnitPropagation(
951 learned_conflict_, is_redundant);
952 restart_->OnConflict(conflict_trail_index, conflict_decision_level,
953 conflict_lbd);
954}
955
956SatSolver::Status SatSolver::ReapplyDecisionsUpTo(
957 int max_level, int* first_propagation_index) {
958 SCOPED_TIME_STAT(&stats_);
959 DCHECK(assumptions_.empty());
960 int decision_index = current_decision_level_;
961 while (decision_index <= max_level) {
962 DCHECK_GE(decision_index, current_decision_level_);
963 const Literal previous_decision = decisions_[decision_index].literal;
964 ++decision_index;
965 if (Assignment().LiteralIsTrue(previous_decision)) {
966 // Note that this particular position in decisions_ will be overridden,
967 // but that is fine since this is a consequence of the previous decision,
968 // so we will never need to take it into account again.
969 continue;
970 }
971 if (Assignment().LiteralIsFalse(previous_decision)) {
972 // See GetLastIncompatibleDecisions().
973 *trail_->MutableConflict() = {previous_decision.Negated(),
974 previous_decision};
975 return ASSUMPTIONS_UNSAT;
976 }
977
978 // Not assigned, we try to take it.
979 const int old_level = current_decision_level_;
980 const int index = EnqueueDecisionAndBackjumpOnConflict(previous_decision);
981 if (first_propagation_index != nullptr) {
982 *first_propagation_index = std::min(*first_propagation_index, index);
983 }
984 if (index == kUnsatTrailIndex) return INFEASIBLE;
985 if (current_decision_level_ <= old_level) {
986 // A conflict occurred which backjumped to an earlier decision level.
987 // We potentially backjumped over some valid decisions, so we need to
988 // continue the loop and try to re-enqueue them.
989 //
990 // Note that there is no need to update max_level, because when we will
991 // try to reapply the current "previous_decision" it will result in a
992 // conflict. IMPORTANT: we can't actually optimize this and abort the loop
993 // earlier though, because we need to check that it is conflicting because
994 // it is already propagated to false. There is no guarantee of this
995 // because we learn the first-UIP conflict. If it is not the case, we will
996 // then learn a new conflict, backjump, and continue the loop.
997 decision_index = current_decision_level_;
998 }
999 }
1000 return FEASIBLE;
1001}
1002
1004 Literal true_literal, int* first_propagation_index) {
1005 SCOPED_TIME_STAT(&stats_);
1006 CHECK(PropagationIsDone());
1007 CHECK(assumptions_.empty());
1008 if (model_is_unsat_) return SatSolver::INFEASIBLE;
1009 DCHECK_LT(CurrentDecisionLevel(), decisions_.size());
1010 decisions_[CurrentDecisionLevel()].literal = true_literal;
1011 if (first_propagation_index != nullptr) {
1012 *first_propagation_index = trail_->Index();
1013 }
1014 return ReapplyDecisionsUpTo(CurrentDecisionLevel(), first_propagation_index);
1015}
1016
1018 SCOPED_TIME_STAT(&stats_);
1019 DCHECK(PropagationIsDone());
1020
1021 if (model_is_unsat_) return kUnsatTrailIndex;
1022 const int current_level = CurrentDecisionLevel();
1023 EnqueueNewDecision(true_literal);
1024 if (Propagate()) {
1025 return true;
1026 } else {
1027 Backtrack(current_level);
1028 return false;
1029 }
1030}
1031
1032void SatSolver::Backtrack(int target_level) {
1033 SCOPED_TIME_STAT(&stats_);
1034 // TODO(user): The backtrack method should not be called when the model is
1035 // unsat. Add a DCHECK to prevent that, but before fix the
1036 // bop::BopOptimizerBase architecture.
1037
1038 // Do nothing if the CurrentDecisionLevel() is already correct.
1039 // This is needed, otherwise target_trail_index below will remain at zero and
1040 // that will cause some problems. Note that we could forbid a user to call
1041 // Backtrack() with the current level, but that is annoying when you just
1042 // want to reset the solver with Backtrack(0).
1043 DCHECK(target_level == 0 || !Decisions().empty());
1044 if (CurrentDecisionLevel() == target_level || Decisions().empty()) return;
1045 DCHECK_GE(target_level, 0);
1046 DCHECK_LE(target_level, CurrentDecisionLevel());
1047
1048 // Any backtrack to the root from a positive one is counted as a restart.
1049 counters_.num_backtracks++;
1050 if (target_level == 0) counters_.num_restarts++;
1051
1052 // Per the SatPropagator interface, this is needed before calling Untrail.
1053 trail_->SetDecisionLevel(target_level);
1054
1055 current_decision_level_ = target_level;
1056 const int target_trail_index =
1057 decisions_[current_decision_level_].trail_index;
1058
1059 DCHECK_LT(target_trail_index, trail_->Index());
1060 for (SatPropagator* propagator : propagators_) {
1061 if (propagator->IsEmpty()) continue;
1062 propagator->Untrail(*trail_, target_trail_index);
1063 }
1064 decision_policy_->Untrail(target_trail_index);
1065 trail_->Untrail(target_trail_index);
1066
1067 last_decision_or_backtrack_trail_index_ = trail_->Index();
1068}
1069
1070bool SatSolver::AddBinaryClauses(const std::vector<BinaryClause>& clauses) {
1071 SCOPED_TIME_STAT(&stats_);
1072 CHECK_EQ(CurrentDecisionLevel(), 0);
1073 for (const BinaryClause c : clauses) {
1074 if (!AddBinaryClause(c.a, c.b)) return false;
1075 }
1076 if (!Propagate()) return SetModelUnsat();
1077 return true;
1078}
1079
1080const std::vector<BinaryClause>& SatSolver::NewlyAddedBinaryClauses() {
1081 return binary_clauses_.newly_added();
1082}
1083
1085 binary_clauses_.ClearNewlyAdded();
1086}
1087
1088namespace {
1089// Return the next value that is a multiple of interval.
1090int64_t NextMultipleOf(int64_t value, int64_t interval) {
1091 return interval * (1 + value / interval);
1092}
1093} // namespace
1094
1096 const std::vector<Literal>& assumptions, int64_t max_number_of_conflicts) {
1097 SCOPED_TIME_STAT(&stats_);
1098 if (!ResetWithGivenAssumptions(assumptions)) return UnsatStatus();
1099 return SolveInternal(time_limit_,
1100 max_number_of_conflicts >= 0
1101 ? max_number_of_conflicts
1102 : parameters_->max_number_of_conflicts());
1103}
1104
1105SatSolver::Status SatSolver::StatusWithLog(Status status) {
1106 SOLVER_LOG(logger_, RunningStatisticsString());
1107 SOLVER_LOG(logger_, StatusString(status));
1108 return status;
1109}
1110
1111void SatSolver::SetAssumptionLevel(int assumption_level) {
1112 CHECK_GE(assumption_level, 0);
1113 CHECK_LE(assumption_level, CurrentDecisionLevel());
1114 assumption_level_ = assumption_level;
1115
1116 // New assumption code.
1117 if (!assumptions_.empty()) {
1118 CHECK_EQ(assumption_level, 0);
1119 assumptions_.clear();
1120 }
1121}
1122
1124 return SolveInternal(time_limit == nullptr ? time_limit_ : time_limit,
1125 parameters_->max_number_of_conflicts());
1126}
1127
1129 return SolveInternal(time_limit_, parameters_->max_number_of_conflicts());
1130}
1131
1132void SatSolver::KeepAllClauseUsedToInfer(BooleanVariable variable) {
1133 CHECK(Assignment().VariableIsAssigned(variable));
1134 if (trail_->Info(variable).level == 0) return;
1135 int trail_index = trail_->Info(variable).trail_index;
1136 std::vector<bool> is_marked(trail_index + 1, false); // move to local member.
1137 is_marked[trail_index] = true;
1138 int num = 1;
1139 for (; num > 0 && trail_index >= 0; --trail_index) {
1140 if (!is_marked[trail_index]) continue;
1141 is_marked[trail_index] = false;
1142 --num;
1143
1144 const BooleanVariable var = (*trail_)[trail_index].Variable();
1145 SatClause* clause = ReasonClauseOrNull(var);
1146 if (clause != nullptr) {
1147 // Keep this clause.
1148 clauses_propagator_->mutable_clauses_info()->erase(clause);
1149 }
1151 continue;
1152 }
1153 for (const Literal l : trail_->Reason(var)) {
1154 const AssignmentInfo& info = trail_->Info(l.Variable());
1155 if (info.level == 0) continue;
1156 if (!is_marked[info.trail_index]) {
1157 is_marked[info.trail_index] = true;
1158 ++num;
1159 }
1160 }
1161 }
1162}
1163
1164bool SatSolver::SubsumptionIsInteresting(BooleanVariable variable) {
1165 // TODO(user): other id should probably be safe as long as we do not delete
1166 // the propagators. Note that symmetry is tricky since we would need to keep
1167 // the symmetric clause around in KeepAllClauseUsedToInfer().
1168 const int binary_id = binary_implication_graph_->PropagatorId();
1169 const int clause_id = clauses_propagator_->PropagatorId();
1170
1171 CHECK(Assignment().VariableIsAssigned(variable));
1172 if (trail_->Info(variable).level == 0) return true;
1173 int trail_index = trail_->Info(variable).trail_index;
1174 std::vector<bool> is_marked(trail_index + 1, false); // move to local member.
1175 is_marked[trail_index] = true;
1176 int num = 1;
1177 int num_clause_to_mark_as_non_deletable = 0;
1178 for (; num > 0 && trail_index >= 0; --trail_index) {
1179 if (!is_marked[trail_index]) continue;
1180 is_marked[trail_index] = false;
1181 --num;
1182
1183 const BooleanVariable var = (*trail_)[trail_index].Variable();
1184 const int type = trail_->AssignmentType(var);
1185 if (type == AssignmentType::kSearchDecision) continue;
1186 if (type != binary_id && type != clause_id) return false;
1187 SatClause* clause = ReasonClauseOrNull(var);
1188 if (clause != nullptr && clauses_propagator_->IsRemovable(clause)) {
1189 ++num_clause_to_mark_as_non_deletable;
1190 }
1191 for (const Literal l : trail_->Reason(var)) {
1192 const AssignmentInfo& info = trail_->Info(l.Variable());
1193 if (info.level == 0) continue;
1194 if (!is_marked[info.trail_index]) {
1195 is_marked[info.trail_index] = true;
1196 ++num;
1197 }
1198 }
1199 }
1200 return num_clause_to_mark_as_non_deletable <= 1;
1201}
1202
1203// TODO(user): this is really an in-processing stuff and should be moved out
1204// of here. I think the name for that (or similar) technique is called vivify.
1205// Ideally this should be scheduled after other faster in-processing technique.
1206void SatSolver::TryToMinimizeClause(SatClause* clause) {
1207 CHECK(clause != nullptr);
1208 ++counters_.minimization_num_clauses;
1209
1210 absl::btree_set<LiteralIndex> moved_last;
1211 std::vector<Literal> candidate(clause->begin(), clause->end());
1212
1213 // Note that CP-SAT presolve detect the clauses that share n-1 literals and
1214 // transform them into (n-1 enforcement) => (1 literal per clause). We
1215 // currently do not support that internally, but these clauses will still
1216 // likely be loaded one after the other, so there is an high chance that if we
1217 // call TryToMinimizeClause() on consecutive clauses, there will be a long
1218 // prefix in common !
1219 //
1220 // TODO(user): Exploit this more by choosing a good minimization order?
1221 int longest_valid_prefix = 0;
1222 if (CurrentDecisionLevel() > 0) {
1223 // Quick linear scan to see if first literal is there.
1224 const Literal first_decision = decisions_[0].literal;
1225 for (int i = 0; i < candidate.size(); ++i) {
1226 if (candidate[i].Negated() == first_decision) {
1227 std::swap(candidate[0], candidate[i]);
1228 longest_valid_prefix = 1;
1229 break;
1230 }
1231 }
1232 // Lets compute the full maximum prefix if we have already one match.
1233 if (longest_valid_prefix == 1 && CurrentDecisionLevel() > 1) {
1234 // Lets do the full algo.
1235 absl::flat_hash_map<LiteralIndex, int> indexing;
1236 for (int i = 0; i < candidate.size(); ++i) {
1237 indexing[candidate[i].NegatedIndex()] = i;
1238 }
1239 for (; longest_valid_prefix < CurrentDecisionLevel();
1240 ++longest_valid_prefix) {
1241 const auto it =
1242 indexing.find(decisions_[longest_valid_prefix].literal.Index());
1243 if (it == indexing.end()) break;
1244 std::swap(candidate[longest_valid_prefix], candidate[it->second]);
1245 indexing[candidate[it->second].NegatedIndex()] = it->second;
1246 }
1247 counters_.minimization_num_reused += longest_valid_prefix;
1248 }
1249 }
1250 Backtrack(longest_valid_prefix);
1251
1252 while (!model_is_unsat_) {
1253 // We want each literal in candidate to appear last once in our propagation
1254 // order. We want to do that while maximizing the reutilization of the
1255 // current assignment prefix, that is minimizing the number of
1256 // decision/progagation we need to perform.
1257 const int target_level = MoveOneUnprocessedLiteralLast(
1258 moved_last, CurrentDecisionLevel(), &candidate);
1259 if (target_level == -1) break;
1260 Backtrack(target_level);
1261 while (CurrentDecisionLevel() < candidate.size()) {
1262 if (time_limit_->LimitReached()) return;
1263 const int level = CurrentDecisionLevel();
1264 const Literal literal = candidate[level];
1265 if (Assignment().LiteralIsFalse(literal)) {
1266 candidate.erase(candidate.begin() + level);
1267 continue;
1268 } else if (Assignment().LiteralIsTrue(literal)) {
1269 const int variable_level =
1270 LiteralTrail().Info(literal.Variable()).level;
1271 if (variable_level == 0) {
1272 ProcessNewlyFixedVariablesForDratProof();
1273 counters_.minimization_num_true++;
1274 counters_.minimization_num_removed_literals += clause->size();
1275 Backtrack(0);
1276 clauses_propagator_->Detach(clause);
1277 return;
1278 }
1279
1280 // If literal (at true) wasn't propagated by this clause, then we
1281 // know that this clause is subsumed by other clauses in the database,
1282 // so we can remove it. Note however that we need to make sure we will
1283 // never remove the clauses that subsumes it later.
1284 if (ReasonClauseOrNull(literal.Variable()) != clause &&
1285 SubsumptionIsInteresting(literal.Variable())) {
1286 counters_.minimization_num_subsumed++;
1287 counters_.minimization_num_removed_literals += clause->size();
1288 KeepAllClauseUsedToInfer(literal.Variable());
1289 Backtrack(0);
1290 clauses_propagator_->Detach(clause);
1291 return;
1292 } else {
1293 // Simplify. Note(user): we could only keep in clause the literals
1294 // responsible for the propagation, but because of the subsumption
1295 // above, this is not needed.
1296 if (variable_level + 1 < candidate.size()) {
1297 candidate.resize(variable_level);
1298 candidate.push_back(literal);
1299 }
1300 }
1301 break;
1302 } else {
1303 ++counters_.minimization_num_decisions;
1305 if (clause->IsRemoved()) {
1306 Backtrack(0);
1307 return;
1308 }
1309 if (model_is_unsat_) return;
1310 }
1311 }
1312 if (candidate.empty()) {
1313 model_is_unsat_ = true;
1314 return;
1315 }
1316 moved_last.insert(candidate.back().Index());
1317 }
1318
1319 // Returns if we don't have any minimization.
1320 //
1321 // Note that we don't backtrack right away so maybe if the next clause as
1322 // similar literal, we can reuse the trail prefix!
1323 if (candidate.size() == clause->size()) return;
1324 Backtrack(0);
1325
1326 if (candidate.size() == 1) {
1327 if (drat_proof_handler_ != nullptr) {
1328 drat_proof_handler_->AddClause(candidate);
1329 }
1330 if (!Assignment().VariableIsAssigned(candidate[0].Variable())) {
1331 counters_.minimization_num_removed_literals += clause->size();
1332 trail_->EnqueueWithUnitReason(candidate[0]);
1333 return (void)FinishPropagation();
1334 }
1335 return;
1336 }
1337
1338 if (candidate.size() == 2) {
1339 counters_.minimization_num_removed_literals += clause->size() - 2;
1340
1341 // The order is important for the drat proof.
1342 AddBinaryClauseInternal(candidate[0], candidate[1]);
1343 clauses_propagator_->Detach(clause);
1344
1345 // This is needed in the corner case where this was the first binary clause
1346 // of the problem so that PropagationIsDone() returns true on the newly
1347 // created BinaryImplicationGraph.
1348 return (void)FinishPropagation();
1349 }
1350
1352 clause->size() - candidate.size();
1353
1354 // TODO(user): If the watched literal didn't change, we could just rewrite
1355 // the clause while keeping the two watched literals at the beginning.
1356 if (!clauses_propagator_->InprocessingRewriteClause(clause, candidate)) {
1357 model_is_unsat_ = true;
1358 }
1359}
1360
1361SatSolver::Status SatSolver::SolveInternal(TimeLimit* time_limit,
1362 int64_t max_number_of_conflicts) {
1363 SCOPED_TIME_STAT(&stats_);
1364 if (model_is_unsat_) return INFEASIBLE;
1365
1366 // TODO(user): Because the counter are not reset to zero, this cause the
1367 // metrics / sec to be completely broken except when the solver is used
1368 // for exactly one Solve().
1369 timer_.Restart();
1370
1371 // Display initial statistics.
1372 if (logger_->LoggingIsEnabled()) {
1373 SOLVER_LOG(logger_, "Initial memory usage: ", MemoryUsage());
1374 SOLVER_LOG(logger_, "Number of variables: ", num_variables_.value());
1375 SOLVER_LOG(logger_, "Number of clauses (size > 2): ",
1376 clauses_propagator_->num_clauses());
1377 SOLVER_LOG(logger_, "Number of binary clauses: ",
1378 binary_implication_graph_->num_implications());
1379 SOLVER_LOG(logger_, "Number of linear constraints: ",
1380 pb_constraints_->NumberOfConstraints());
1381 SOLVER_LOG(logger_, "Number of fixed variables: ", trail_->Index());
1382 SOLVER_LOG(logger_, "Number of watched clauses: ",
1383 clauses_propagator_->num_watched_clauses());
1384 SOLVER_LOG(logger_, "Parameters: ", ProtobufShortDebugString(*parameters_));
1385 }
1386
1387 // Variables used to show the search progress.
1388 const int64_t kDisplayFrequency = 10000;
1389 int64_t next_display = parameters_->log_search_progress()
1390 ? NextMultipleOf(num_failures(), kDisplayFrequency)
1391 : std::numeric_limits<int64_t>::max();
1392
1393 // Variables used to check the memory limit every kMemoryCheckFrequency.
1394 const int64_t kMemoryCheckFrequency = 10000;
1395 int64_t next_memory_check =
1396 NextMultipleOf(num_failures(), kMemoryCheckFrequency);
1397
1398 // The max_number_of_conflicts is per solve but the counter is for the whole
1399 // solver.
1400 const int64_t kFailureLimit =
1401 max_number_of_conflicts == std::numeric_limits<int64_t>::max()
1402 ? std::numeric_limits<int64_t>::max()
1403 : counters_.num_failures + max_number_of_conflicts;
1404
1405 // Starts search.
1406 for (;;) {
1407 // Test if a limit is reached.
1408 if (time_limit != nullptr) {
1410 if (time_limit->LimitReached()) {
1411 SOLVER_LOG(logger_, "The time limit has been reached. Aborting.");
1412 return StatusWithLog(LIMIT_REACHED);
1413 }
1414 }
1415 if (num_failures() >= kFailureLimit) {
1416 SOLVER_LOG(logger_, "The conflict limit has been reached. Aborting.");
1417 return StatusWithLog(LIMIT_REACHED);
1418 }
1419
1420 // The current memory checking takes time, so we only execute it every
1421 // kMemoryCheckFrequency conflict. We use >= because counters_.num_failures
1422 // may augment by more than one at each iteration.
1423 //
1424 // TODO(user): Find a better way.
1425 if (counters_.num_failures >= next_memory_check) {
1426 next_memory_check = NextMultipleOf(num_failures(), kMemoryCheckFrequency);
1427 if (IsMemoryLimitReached()) {
1428 SOLVER_LOG(logger_, "The memory limit has been reached. Aborting.");
1429 return StatusWithLog(LIMIT_REACHED);
1430 }
1431 }
1432
1433 // Display search progression. We use >= because counters_.num_failures may
1434 // augment by more than one at each iteration.
1435 if (counters_.num_failures >= next_display) {
1436 SOLVER_LOG(logger_, RunningStatisticsString());
1437 next_display = NextMultipleOf(num_failures(), kDisplayFrequency);
1438 }
1439
1440 const int old_level = current_decision_level_;
1441 if (!Propagate()) {
1442 // A conflict occurred, continue the loop.
1444 if (model_is_unsat_) return StatusWithLog(INFEASIBLE);
1445 if (old_level == current_decision_level_) {
1446 CHECK(!assumptions_.empty());
1447 return StatusWithLog(ASSUMPTIONS_UNSAT);
1448 }
1449 } else {
1450 // We need to reapply any assumptions that are not currently applied.
1451 if (!ReapplyAssumptionsIfNeeded()) return StatusWithLog(UnsatStatus());
1452
1453 // At a leaf?
1454 if (trail_->Index() == num_variables_.value()) {
1455 return StatusWithLog(FEASIBLE);
1456 }
1457
1458 if (restart_->ShouldRestart()) {
1459 Backtrack(assumption_level_);
1460 }
1461
1462 DCHECK_GE(CurrentDecisionLevel(), assumption_level_);
1463 EnqueueNewDecision(decision_policy_->NextBranch());
1464 }
1465 }
1466}
1467
1469 CHECK(time_limit_ != nullptr);
1470 AdvanceDeterministicTime(time_limit_);
1471 const double threshold = time_limit_->GetElapsedDeterministicTime() + dtime;
1472
1473 // Tricky: we don't want TryToMinimizeClause() to delete to_minimize
1474 // while we are processing it.
1475 block_clause_deletion_ = true;
1476
1477 int num_resets = 0;
1478 while (!time_limit_->LimitReached() &&
1479 time_limit_->GetElapsedDeterministicTime() < threshold) {
1480 SatClause* to_minimize = clauses_propagator_->NextClauseToMinimize();
1481 if (to_minimize != nullptr) {
1482 TryToMinimizeClause(to_minimize);
1483 if (model_is_unsat_) return false;
1484 } else {
1485 if (to_minimize == nullptr) {
1486 ++num_resets;
1487 VLOG(1) << "Minimized all clauses, restarting from first one.";
1488 clauses_propagator_->ResetToMinimizeIndex();
1489 }
1490 if (num_resets > 1) break;
1491 }
1492
1493 AdvanceDeterministicTime(time_limit_);
1494 }
1495
1496 // Note(user): In some corner cases, the function above might find a
1497 // feasible assignment. I think it is okay to ignore this special case
1498 // that should only happen on trivial problems and just reset the solver.
1499 const bool result = ResetToLevelZero();
1500
1501 block_clause_deletion_ = false;
1502 clauses_propagator_->DeleteRemovedClauses();
1503 return result;
1504}
1505
1507 SCOPED_TIME_STAT(&stats_);
1508 std::vector<Literal> unsat_assumptions;
1509
1510 is_marked_.ClearAndResize(num_variables_);
1511
1512 int trail_index = 0;
1513 int num_true = 0;
1514 for (const Literal lit : trail_->FailingClause()) {
1515 CHECK(Assignment().LiteralIsAssigned(lit));
1516 if (Assignment().LiteralIsTrue(lit)) {
1517 // literal at true in the conflict must be decision/assumptions that could
1518 // not be taken.
1519 ++num_true;
1520 unsat_assumptions.push_back(lit.Negated());
1521 continue;
1522 }
1523 trail_index =
1524 std::max(trail_index, trail_->Info(lit.Variable()).trail_index);
1525 is_marked_.Set(lit.Variable());
1526 }
1527 CHECK_LE(num_true, 1);
1528
1529 // We just expand the conflict until we only have decisions.
1530 const int limit =
1531 CurrentDecisionLevel() > 0 ? decisions_[0].trail_index : trail_->Index();
1532 CHECK_LT(trail_index, trail_->Index());
1533 while (true) {
1534 // Find next marked literal to expand from the trail.
1535 while (trail_index >= limit &&
1536 !is_marked_[(*trail_)[trail_index].Variable()]) {
1537 --trail_index;
1538 }
1539 if (trail_index < limit) break;
1540 const Literal marked_literal = (*trail_)[trail_index];
1541 --trail_index;
1542
1543 if (trail_->AssignmentType(marked_literal.Variable()) ==
1545 unsat_assumptions.push_back(marked_literal);
1546 } else {
1547 // Marks all the literals of its reason.
1548 for (const Literal literal : trail_->Reason(marked_literal.Variable())) {
1549 const BooleanVariable var = literal.Variable();
1550 const int level = DecisionLevel(var);
1551 if (level > 0 && !is_marked_[var]) is_marked_.Set(var);
1552 }
1553 }
1554 }
1555
1556 // We reverse the assumptions so they are in the same order as the one in
1557 // which the decision were made.
1558 std::reverse(unsat_assumptions.begin(), unsat_assumptions.end());
1559 return unsat_assumptions;
1560}
1561
1562void SatSolver::BumpReasonActivities(const std::vector<Literal>& literals) {
1563 SCOPED_TIME_STAT(&stats_);
1564 for (const Literal literal : literals) {
1565 const BooleanVariable var = literal.Variable();
1566 if (DecisionLevel(var) > 0) {
1567 SatClause* clause = ReasonClauseOrNull(var);
1568 if (clause != nullptr) {
1569 BumpClauseActivity(clause);
1570 } else {
1571 UpperBoundedLinearConstraint* pb_constraint =
1572 ReasonPbConstraintOrNull(var);
1573 if (pb_constraint != nullptr) {
1574 // TODO(user): Because one pb constraint may propagate many literals,
1575 // this may bias the constraint activity... investigate other policy.
1576 pb_constraints_->BumpActivity(pb_constraint);
1577 }
1578 }
1579 }
1580 }
1581}
1582
1583void SatSolver::BumpClauseActivity(SatClause* clause) {
1584 // We only bump the activity of the clauses that have some info. So if we know
1585 // that we will keep a clause forever, we don't need to create its Info. More
1586 // than the speed, this allows to limit as much as possible the activity
1587 // rescaling.
1588 auto it = clauses_propagator_->mutable_clauses_info()->find(clause);
1589 if (it == clauses_propagator_->mutable_clauses_info()->end()) return;
1590
1591 // Check if the new clause LBD is below our threshold to keep this clause
1592 // indefinitely. Note that we use a +1 here because the LBD of a newly learned
1593 // clause decrease by 1 just after the backjump.
1594 const int new_lbd = ComputeLbd(*clause);
1595 if (new_lbd + 1 <= parameters_->clause_cleanup_lbd_bound()) {
1596 clauses_propagator_->mutable_clauses_info()->erase(clause);
1597 return;
1598 }
1599
1600 // Eventually protect this clause for the next cleanup phase.
1601 switch (parameters_->clause_cleanup_protection()) {
1602 case SatParameters::PROTECTION_NONE:
1603 break;
1604 case SatParameters::PROTECTION_ALWAYS:
1605 it->second.protected_during_next_cleanup = true;
1606 break;
1607 case SatParameters::PROTECTION_LBD:
1608 // This one is similar to the one used by the Glucose SAT solver.
1609 //
1610 // TODO(user): why the +1? one reason may be that the LBD of a conflict
1611 // decrease by 1 just after the backjump...
1612 if (new_lbd + 1 < it->second.lbd) {
1613 it->second.protected_during_next_cleanup = true;
1614 it->second.lbd = new_lbd;
1615 }
1616 }
1617
1618 // Increase the activity.
1619 const double activity = it->second.activity += clause_activity_increment_;
1620 if (activity > parameters_->max_clause_activity_value()) {
1621 RescaleClauseActivities(1.0 / parameters_->max_clause_activity_value());
1622 }
1623}
1624
1625void SatSolver::RescaleClauseActivities(double scaling_factor) {
1626 SCOPED_TIME_STAT(&stats_);
1627 clause_activity_increment_ *= scaling_factor;
1628 for (auto& entry : *clauses_propagator_->mutable_clauses_info()) {
1629 entry.second.activity *= scaling_factor;
1630 }
1631}
1632
1633void SatSolver::UpdateClauseActivityIncrement() {
1634 SCOPED_TIME_STAT(&stats_);
1635 clause_activity_increment_ *= 1.0 / parameters_->clause_activity_decay();
1636}
1637
1638bool SatSolver::IsConflictValid(const std::vector<Literal>& literals) {
1639 SCOPED_TIME_STAT(&stats_);
1640 if (literals.empty()) return false;
1641 const int highest_level = DecisionLevel(literals[0].Variable());
1642 for (int i = 1; i < literals.size(); ++i) {
1643 const int level = DecisionLevel(literals[i].Variable());
1644 if (level <= 0 || level >= highest_level) return false;
1645 }
1646 return true;
1647}
1648
1649int SatSolver::ComputeBacktrackLevel(const std::vector<Literal>& literals) {
1650 SCOPED_TIME_STAT(&stats_);
1651 DCHECK_GT(CurrentDecisionLevel(), 0);
1652
1653 // We want the highest decision level among literals other than the first one.
1654 // Note that this level will always be smaller than that of the first literal.
1655 //
1656 // Note(user): if the learned clause is of size 1, we backtrack all the way to
1657 // the beginning. It may be possible to follow another behavior, but then the
1658 // code require some special cases in
1659 // AddLearnedClauseAndEnqueueUnitPropagation() to fix the literal and not
1660 // backtrack over it. Also, subsequent propagated variables may not have a
1661 // correct level in this case.
1662 int backtrack_level = 0;
1663 for (int i = 1; i < literals.size(); ++i) {
1664 const int level = DecisionLevel(literals[i].Variable());
1665 backtrack_level = std::max(backtrack_level, level);
1666 }
1667 DCHECK_LT(backtrack_level, DecisionLevel(literals[0].Variable()));
1668 DCHECK_LE(DecisionLevel(literals[0].Variable()), CurrentDecisionLevel());
1669 return backtrack_level;
1670}
1671
1672template <typename LiteralList>
1673int SatSolver::ComputeLbd(const LiteralList& literals) {
1674 SCOPED_TIME_STAT(&stats_);
1675 const int limit =
1676 parameters_->count_assumption_levels_in_lbd() ? 0 : assumption_level_;
1677
1678 // We know that the first literal is always of the highest level.
1679 is_level_marked_.ClearAndResize(
1680 SatDecisionLevel(DecisionLevel(literals.begin()->Variable()) + 1));
1681 for (const Literal literal : literals) {
1682 const SatDecisionLevel level(DecisionLevel(literal.Variable()));
1683 DCHECK_GE(level, 0);
1684 if (level > limit && !is_level_marked_[level]) {
1685 is_level_marked_.Set(level);
1686 }
1687 }
1688 return is_level_marked_.NumberOfSetCallsWithDifferentArguments();
1689}
1690
1691std::string SatSolver::StatusString(Status status) const {
1692 const double time_in_s = timer_.Get();
1693 return absl::StrFormat("\n status: %s\n", SatStatusString(status)) +
1694 absl::StrFormat(" time: %fs\n", time_in_s) +
1695 absl::StrFormat(" memory: %s\n", MemoryUsage()) +
1696 absl::StrFormat(
1697 " num failures: %d (%.0f /sec)\n", counters_.num_failures,
1698 static_cast<double>(counters_.num_failures) / time_in_s) +
1699 absl::StrFormat(
1700 " num branches: %d (%.0f /sec)\n", counters_.num_branches,
1701 static_cast<double>(counters_.num_branches) / time_in_s) +
1702 absl::StrFormat(" num propagations: %d (%.0f /sec)\n",
1704 static_cast<double>(num_propagations()) / time_in_s) +
1705 absl::StrFormat(" num binary propagations: %d\n",
1706 binary_implication_graph_->num_propagations()) +
1707 absl::StrFormat(" num binary inspections: %d\n",
1708 binary_implication_graph_->num_inspections()) +
1709 absl::StrFormat(
1710 " num binary redundant implications: %d\n",
1711 binary_implication_graph_->num_redundant_implications()) +
1712 absl::StrFormat(
1713 " num classic minimizations: %d"
1714 " (literals removed: %d)\n",
1715 counters_.num_minimizations, counters_.num_literals_removed) +
1716 absl::StrFormat(
1717 " num binary minimizations: %d"
1718 " (literals removed: %d)\n",
1719 binary_implication_graph_->num_minimization(),
1720 binary_implication_graph_->num_literals_removed()) +
1721 absl::StrFormat(" num inspected clauses: %d\n",
1722 clauses_propagator_->num_inspected_clauses()) +
1723 absl::StrFormat(" num inspected clause_literals: %d\n",
1724 clauses_propagator_->num_inspected_clause_literals()) +
1725 absl::StrFormat(
1726 " num learned literals: %d (avg: %.1f /clause)\n",
1727 counters_.num_literals_learned,
1728 1.0 * counters_.num_literals_learned / counters_.num_failures) +
1729 absl::StrFormat(
1730 " num learned PB literals: %d (avg: %.1f /clause)\n",
1731 counters_.num_learned_pb_literals,
1732 1.0 * counters_.num_learned_pb_literals / counters_.num_failures) +
1733 absl::StrFormat(" num subsumed clauses: %d\n",
1734 counters_.num_subsumed_clauses) +
1735 absl::StrFormat(" minimization_num_clauses: %d\n",
1736 counters_.minimization_num_clauses) +
1737 absl::StrFormat(" minimization_num_decisions: %d\n",
1738 counters_.minimization_num_decisions) +
1739 absl::StrFormat(" minimization_num_true: %d\n",
1740 counters_.minimization_num_true) +
1741 absl::StrFormat(" minimization_num_subsumed: %d\n",
1742 counters_.minimization_num_subsumed) +
1743 absl::StrFormat(" minimization_num_removed_literals: %d\n",
1745 absl::StrFormat(" pb num threshold updates: %d\n",
1746 pb_constraints_->num_threshold_updates()) +
1747 absl::StrFormat(" pb num constraint lookups: %d\n",
1748 pb_constraints_->num_constraint_lookups()) +
1749 absl::StrFormat(" pb num inspected constraint literals: %d\n",
1750 pb_constraints_->num_inspected_constraint_literals()) +
1751 restart_->InfoString() +
1752 absl::StrFormat(" deterministic time: %f\n", deterministic_time());
1753}
1754
1755std::string SatSolver::RunningStatisticsString() const {
1756 const double time_in_s = timer_.Get();
1757 return absl::StrFormat(
1758 "%6.2fs, mem:%s, fails:%d, depth:%d, clauses:%d, tmp:%d, bin:%u, "
1759 "restarts:%d, vars:%d",
1760 time_in_s, MemoryUsage(), counters_.num_failures, CurrentDecisionLevel(),
1761 clauses_propagator_->num_clauses() -
1762 clauses_propagator_->num_removable_clauses(),
1763 clauses_propagator_->num_removable_clauses(),
1764 binary_implication_graph_->num_implications(), restart_->NumRestarts(),
1765 num_variables_.value() - num_processed_fixed_variables_);
1766}
1767
1768void SatSolver::ProcessNewlyFixedVariablesForDratProof() {
1769 if (drat_proof_handler_ == nullptr) return;
1770 if (CurrentDecisionLevel() != 0) return;
1771
1772 // We need to output the literals that are fixed so we can remove all
1773 // clauses that contains them. Note that this doesn't seems to be needed
1774 // for drat-trim.
1775 //
1776 // TODO(user): Ideally we could output such literal as soon as they are fixed,
1777 // but this is not that easy to do. Spend some time to find a cleaner
1778 // alternative? Currently this works, but:
1779 // - We will output some fixed literals twice since we already output learnt
1780 // clauses of size one.
1781 // - We need to call this function when needed.
1782 Literal temp;
1783 for (; drat_num_processed_fixed_variables_ < trail_->Index();
1784 ++drat_num_processed_fixed_variables_) {
1785 temp = (*trail_)[drat_num_processed_fixed_variables_];
1786 drat_proof_handler_->AddClause({&temp, 1});
1787 }
1788}
1789
1791 SCOPED_TIME_STAT(&stats_);
1792 DCHECK_EQ(CurrentDecisionLevel(), 0);
1793 int num_detached_clauses = 0;
1794 int num_binary = 0;
1795
1796 ProcessNewlyFixedVariablesForDratProof();
1797
1798 // We remove the clauses that are always true and the fixed literals from the
1799 // others. Note that none of the clause should be all false because we should
1800 // have detected a conflict before this is called.
1801 for (SatClause* clause : clauses_propagator_->AllClausesInCreationOrder()) {
1802 if (clause->IsRemoved()) continue;
1803
1804 const size_t old_size = clause->size();
1805 if (clause->RemoveFixedLiteralsAndTestIfTrue(trail_->Assignment())) {
1806 // The clause is always true, detach it.
1807 clauses_propagator_->LazyDetach(clause);
1808 ++num_detached_clauses;
1809 continue;
1810 }
1811
1812 const size_t new_size = clause->size();
1813 if (new_size == old_size) continue;
1814
1815 if (drat_proof_handler_ != nullptr) {
1816 CHECK_GT(new_size, 0);
1817 drat_proof_handler_->AddClause({clause->begin(), new_size});
1818 drat_proof_handler_->DeleteClause({clause->begin(), old_size});
1819 }
1820
1821 if (new_size == 2) {
1822 // This clause is now a binary clause, treat it separately. Note that
1823 // it is safe to do that because this clause can't be used as a reason
1824 // since we are at level zero and the clause is not satisfied.
1825 AddBinaryClauseInternal(clause->FirstLiteral(), clause->SecondLiteral());
1826 clauses_propagator_->LazyDetach(clause);
1827 ++num_binary;
1828 continue;
1829 }
1830 }
1831
1832 // Note that we will only delete the clauses during the next database cleanup.
1833 clauses_propagator_->CleanUpWatchers();
1834 if (num_detached_clauses > 0 || num_binary > 0) {
1835 VLOG(1) << trail_->Index() << " fixed variables at level 0. " << "Detached "
1836 << num_detached_clauses << " clauses. " << num_binary
1837 << " converted to binary.";
1838 }
1839
1840 // We also clean the binary implication graph.
1841 // Tricky: If we added the first binary clauses above, the binary graph
1842 // is not in "propagated" state as it should be, so we call Propagate() so
1843 // all the checks are happy.
1844 CHECK(binary_implication_graph_->Propagate(trail_));
1845 binary_implication_graph_->RemoveFixedVariables();
1846 num_processed_fixed_variables_ = trail_->Index();
1847 deterministic_time_of_last_fixed_variables_cleanup_ = deterministic_time();
1848}
1849
1850bool SatSolver::PropagationIsDone() const {
1851 for (SatPropagator* propagator : propagators_) {
1852 if (propagator->IsEmpty()) continue;
1853 if (!propagator->PropagationIsDone(*trail_)) return false;
1854 }
1855 return true;
1856}
1857
1858// TODO(user): Support propagating only the "first" propagators. That can
1859// be useful for probing/in-processing, so we can control if we do only the SAT
1860// part or the full integer part...
1862 SCOPED_TIME_STAT(&stats_);
1863 DCHECK(!ModelIsUnsat());
1864
1865 while (true) {
1866 // Because we might potentially iterate often on this list below, we remove
1867 // empty propagators.
1868 //
1869 // TODO(user): This might not really be needed.
1870 non_empty_propagators_.clear();
1871 for (SatPropagator* propagator : propagators_) {
1872 if (!propagator->IsEmpty()) {
1873 non_empty_propagators_.push_back(propagator);
1874 }
1875 }
1876
1877 while (true) {
1878 // The idea here is to abort the inspection as soon as at least one
1879 // propagation occurs so we can loop over and test again the highest
1880 // priority constraint types using the new information.
1881 //
1882 // Note that the first propagators_ should be the
1883 // binary_implication_graph_ and that its Propagate() functions will not
1884 // abort on the first propagation to be slightly more efficient.
1885 const int old_index = trail_->Index();
1886 for (SatPropagator* propagator : non_empty_propagators_) {
1887 DCHECK(propagator->PropagatePreconditionsAreSatisfied(*trail_));
1888 if (!propagator->Propagate(trail_)) return false;
1889 if (trail_->Index() > old_index) break;
1890 }
1891 if (trail_->Index() == old_index) break;
1892 }
1893
1894 // In some corner cases, we might add new constraint during propagation,
1895 // which might trigger new propagator addition or some propagator to become
1896 // non-empty() now.
1897 if (PropagationIsDone()) return true;
1898 }
1899 return true;
1900}
1901
1902void SatSolver::InitializePropagators() {
1903 propagators_.clear();
1904 propagators_.push_back(binary_implication_graph_);
1905 propagators_.push_back(clauses_propagator_);
1906 propagators_.push_back(pb_constraints_);
1907 for (int i = 0; i < external_propagators_.size(); ++i) {
1908 propagators_.push_back(external_propagators_[i]);
1909 }
1910 if (last_propagator_ != nullptr) {
1911 propagators_.push_back(last_propagator_);
1912 }
1913}
1914
1915bool SatSolver::ResolvePBConflict(BooleanVariable var,
1916 MutableUpperBoundedLinearConstraint* conflict,
1917 Coefficient* slack) {
1918 const int trail_index = trail_->Info(var).trail_index;
1919
1920 // This is the slack of the conflict < trail_index
1921 DCHECK_EQ(*slack, conflict->ComputeSlackForTrailPrefix(*trail_, trail_index));
1922
1923 // Pseudo-Boolean case.
1924 UpperBoundedLinearConstraint* pb_reason = ReasonPbConstraintOrNull(var);
1925 if (pb_reason != nullptr) {
1926 pb_reason->ResolvePBConflict(*trail_, var, conflict, slack);
1927 return false;
1928 }
1929
1930 // Generic clause case.
1931 Coefficient multiplier(1);
1932
1933 // TODO(user): experiment and choose the "best" algo.
1934 const int algorithm = 1;
1935 switch (algorithm) {
1936 case 1:
1937 // We reduce the conflict slack to 0 before adding the clause.
1938 // The advantage of this method is that the coefficients stay small.
1939 conflict->ReduceSlackTo(*trail_, trail_index, *slack, Coefficient(0));
1940 break;
1941 case 2:
1942 // No reduction, we add the lower possible multiple.
1943 multiplier = *slack + 1;
1944 break;
1945 default:
1946 // No reduction, the multiple is chosen to cancel var.
1947 multiplier = conflict->GetCoefficient(var);
1948 }
1949
1950 Coefficient num_literals(1);
1951 conflict->AddTerm(
1953 multiplier);
1954 for (Literal literal : trail_->Reason(var)) {
1955 DCHECK_NE(literal.Variable(), var);
1956 DCHECK(Assignment().LiteralIsFalse(literal));
1957 conflict->AddTerm(literal.Negated(), multiplier);
1958 ++num_literals;
1959 }
1960 conflict->AddToRhs((num_literals - 1) * multiplier);
1961
1962 // All the algorithms above result in a new slack of -1.
1963 *slack = -1;
1964 DCHECK_EQ(*slack, conflict->ComputeSlackForTrailPrefix(*trail_, trail_index));
1965 return true;
1966}
1967
1968void SatSolver::EnqueueNewDecision(Literal literal) {
1969 SCOPED_TIME_STAT(&stats_);
1970 CHECK(!Assignment().VariableIsAssigned(literal.Variable()));
1971
1972 // We are back at level 0. This can happen because of a restart, or because
1973 // we proved that some variables must take a given value in any satisfiable
1974 // assignment. Trigger a simplification of the clauses if there is new fixed
1975 // variables. Note that for efficiency reason, we don't do that too often.
1976 //
1977 // TODO(user): Do more advanced preprocessing?
1978 if (CurrentDecisionLevel() == 0) {
1979 const double kMinDeterministicTimeBetweenCleanups = 1.0;
1980 if (num_processed_fixed_variables_ < trail_->Index() &&
1982 deterministic_time_of_last_fixed_variables_cleanup_ +
1983 kMinDeterministicTimeBetweenCleanups) {
1985 }
1986 }
1987
1988 counters_.num_branches++;
1989 last_decision_or_backtrack_trail_index_ = trail_->Index();
1990 decisions_[current_decision_level_] = Decision(trail_->Index(), literal);
1991 ++current_decision_level_;
1992 trail_->SetDecisionLevel(current_decision_level_);
1994}
1995
1996std::string SatSolver::DebugString(const SatClause& clause) const {
1997 std::string result;
1998 for (const Literal literal : clause) {
1999 if (!result.empty()) {
2000 result.append(" || ");
2001 }
2002 const std::string value =
2004 ? "true"
2005 : (trail_->Assignment().LiteralIsFalse(literal) ? "false"
2006 : "undef");
2007 result.append(absl::StrFormat("%s(%s)", literal.DebugString(), value));
2008 }
2009 return result;
2010}
2011
2012int SatSolver::ComputeMaxTrailIndex(absl::Span<const Literal> clause) const {
2013 SCOPED_TIME_STAT(&stats_);
2014 int trail_index = -1;
2015 for (const Literal literal : clause) {
2016 trail_index =
2017 std::max(trail_index, trail_->Info(literal.Variable()).trail_index);
2018 }
2019 return trail_index;
2020}
2021
2022// This method will compute a first UIP conflict
2023// http://www.cs.tau.ac.il/~msagiv/courses/ATP/iccad2001_final.pdf
2024// http://gauss.ececs.uc.edu/SAT/articles/FAIA185-0131.pdf
2025void SatSolver::ComputeFirstUIPConflict(
2026 int max_trail_index, std::vector<Literal>* conflict,
2027 std::vector<Literal>* reason_used_to_infer_the_conflict,
2028 std::vector<SatClause*>* subsumed_clauses) {
2029 SCOPED_TIME_STAT(&stats_);
2030 const int64_t conflict_id = counters_.num_failures;
2031
2032 // This will be used to mark all the literals inspected while we process the
2033 // conflict and the reasons behind each of its variable assignments.
2034 is_marked_.ClearAndResize(num_variables_);
2035
2036 conflict->clear();
2037 reason_used_to_infer_the_conflict->clear();
2038 subsumed_clauses->clear();
2039 if (max_trail_index == -1) return;
2040
2041 // max_trail_index is the maximum trail index appearing in the failing_clause
2042 // and its level (Which is almost always equals to the CurrentDecisionLevel(),
2043 // except for symmetry propagation).
2044 DCHECK_EQ(max_trail_index, ComputeMaxTrailIndex(trail_->FailingClause()));
2045 int trail_index = max_trail_index;
2046 const int highest_level = DecisionLevel((*trail_)[trail_index].Variable());
2047 if (highest_level == 0) return;
2048
2049 // To find the 1-UIP conflict clause, we start by the failing_clause, and
2050 // expand each of its literal using the reason for this literal assignment to
2051 // false. The is_marked_ set allow us to never expand the same literal twice.
2052 //
2053 // The expansion is not done (i.e. stop) for literals that were assigned at a
2054 // decision level below the current one. If the level of such literal is not
2055 // zero, it is added to the conflict clause.
2056 //
2057 // Now, the trick is that we use the trail to expand the literal of the
2058 // current level in a very specific order. Namely the reverse order of the one
2059 // in which they were inferred. We stop as soon as
2060 // num_literal_at_highest_level_that_needs_to_be_processed is exactly one.
2061 //
2062 // This last literal will be the first UIP because by definition all the
2063 // propagation done at the current level will pass though it at some point.
2064 absl::Span<const Literal> clause_to_expand = trail_->FailingClause();
2065 SatClause* sat_clause = trail_->FailingSatClause();
2066 DCHECK(!clause_to_expand.empty());
2067 int num_literal_at_highest_level_that_needs_to_be_processed = 0;
2068 while (true) {
2069 int num_new_vars_at_positive_level = 0;
2070 int num_vars_at_positive_level_in_clause_to_expand = 0;
2071 for (const Literal literal : clause_to_expand) {
2072 const BooleanVariable var = literal.Variable();
2073 const int level = DecisionLevel(var);
2074 if (level == 0) continue;
2075 ++num_vars_at_positive_level_in_clause_to_expand;
2076 if (!is_marked_[var]) {
2077 is_marked_.Set(var);
2078 ++num_new_vars_at_positive_level;
2079 if (level == highest_level) {
2080 ++num_literal_at_highest_level_that_needs_to_be_processed;
2081 } else {
2082 // Note that all these literals are currently false since the clause
2083 // to expand was used to infer the value of a literal at this level.
2084 DCHECK(trail_->Assignment().LiteralIsFalse(literal));
2085 conflict->push_back(literal);
2086 }
2087 }
2088 }
2089
2090 // If there is new variables, then all the previously subsumed clauses are
2091 // not subsumed anymore.
2092 if (num_new_vars_at_positive_level > 0) {
2093 // TODO(user): We could still replace all these clauses with the current
2094 // conflict.
2095 subsumed_clauses->clear();
2096 }
2097
2098 // This check if the new conflict is exactly equal to clause_to_expand.
2099 // Since we just performed an union, comparing the size is enough. When this
2100 // is true, then the current conflict subsumes the reason whose underlying
2101 // clause is given by sat_clause.
2102 if (sat_clause != nullptr &&
2103 num_vars_at_positive_level_in_clause_to_expand ==
2104 conflict->size() +
2105 num_literal_at_highest_level_that_needs_to_be_processed) {
2106 subsumed_clauses->push_back(sat_clause);
2107 }
2108
2109 // Find next marked literal to expand from the trail.
2110 DCHECK_GT(num_literal_at_highest_level_that_needs_to_be_processed, 0);
2111 while (!is_marked_[(*trail_)[trail_index].Variable()]) {
2112 --trail_index;
2113 DCHECK_GE(trail_index, 0);
2114 DCHECK_EQ(DecisionLevel((*trail_)[trail_index].Variable()),
2115 highest_level);
2116 }
2117
2118 if (num_literal_at_highest_level_that_needs_to_be_processed == 1) {
2119 // We have the first UIP. Add its negation to the conflict clause.
2120 // This way, after backtracking to the proper level, the conflict clause
2121 // will be unit, and infer the negation of the UIP that caused the fail.
2122 conflict->push_back((*trail_)[trail_index].Negated());
2123
2124 // To respect the function API move the first UIP in the first position.
2125 std::swap(conflict->back(), conflict->front());
2126 break;
2127 }
2128
2129 const Literal literal = (*trail_)[trail_index];
2130 reason_used_to_infer_the_conflict->push_back(literal);
2131
2132 // If we already encountered the same reason, we can just skip this literal
2133 // which is what setting clause_to_expand to the empty clause do.
2134 if (same_reason_identifier_.FirstVariableWithSameReason(
2135 literal.Variable()) != literal.Variable()) {
2136 clause_to_expand = {};
2137 } else {
2138 clause_to_expand = trail_->Reason(literal.Variable(), conflict_id);
2139 }
2140 sat_clause = ReasonClauseOrNull(literal.Variable());
2141
2142 --num_literal_at_highest_level_that_needs_to_be_processed;
2143 --trail_index;
2144 }
2145}
2146
2147void SatSolver::ComputeUnionOfReasons(const std::vector<Literal>& input,
2148 std::vector<Literal>* literals) {
2149 tmp_mark_.ClearAndResize(num_variables_);
2150 literals->clear();
2151 for (const Literal l : input) tmp_mark_.Set(l.Variable());
2152 for (const Literal l : input) {
2153 for (const Literal r : trail_->Reason(l.Variable())) {
2154 if (!tmp_mark_[r.Variable()]) {
2155 tmp_mark_.Set(r.Variable());
2156 literals->push_back(r);
2157 }
2158 }
2159 }
2160 for (const Literal l : input) tmp_mark_.Clear(l.Variable());
2161 for (const Literal l : *literals) tmp_mark_.Clear(l.Variable());
2162}
2163
2164// TODO(user): Remove the literals assigned at level 0.
2165void SatSolver::ComputePBConflict(int max_trail_index,
2166 Coefficient initial_slack,
2167 MutableUpperBoundedLinearConstraint* conflict,
2168 int* pb_backjump_level) {
2169 SCOPED_TIME_STAT(&stats_);
2170 int trail_index = max_trail_index;
2171
2172 // First compute the slack of the current conflict for the assignment up to
2173 // trail_index. It must be negative since this is a conflict.
2174 Coefficient slack = initial_slack;
2175 DCHECK_EQ(slack,
2176 conflict->ComputeSlackForTrailPrefix(*trail_, trail_index + 1));
2177 CHECK_LT(slack, 0) << "We don't have a conflict!";
2178
2179 // Iterate backward over the trail.
2180 int backjump_level = 0;
2181 while (true) {
2182 const BooleanVariable var = (*trail_)[trail_index].Variable();
2183 --trail_index;
2184
2185 if (conflict->GetCoefficient(var) > 0 &&
2186 trail_->Assignment().LiteralIsTrue(conflict->GetLiteral(var))) {
2187 if (parameters_->minimize_reduction_during_pb_resolution()) {
2188 // When this parameter is true, we don't call ReduceCoefficients() at
2189 // every loop. However, it is still important to reduce the "current"
2190 // variable coefficient, because this can impact the value of the new
2191 // slack below.
2192 conflict->ReduceGivenCoefficient(var);
2193 }
2194
2195 // This is the slack one level before (< Info(var).trail_index).
2196 slack += conflict->GetCoefficient(var);
2197
2198 // This can't happen at the beginning, but may happen later.
2199 // It means that even without var assigned, we still have a conflict.
2200 if (slack < 0) continue;
2201
2202 // At this point, just removing the last assignment lift the conflict.
2203 // So we can abort if the true assignment before that is at a lower level
2204 // TODO(user): Somewhat inefficient.
2205 // TODO(user): We could abort earlier...
2206 const int current_level = DecisionLevel(var);
2207 int i = trail_index;
2208 while (i >= 0) {
2209 const BooleanVariable previous_var = (*trail_)[i].Variable();
2210 if (conflict->GetCoefficient(previous_var) > 0 &&
2211 trail_->Assignment().LiteralIsTrue(
2212 conflict->GetLiteral(previous_var))) {
2213 break;
2214 }
2215 --i;
2216 }
2217 if (i < 0 || DecisionLevel((*trail_)[i].Variable()) < current_level) {
2218 backjump_level = i < 0 ? 0 : DecisionLevel((*trail_)[i].Variable());
2219 break;
2220 }
2221
2222 // We can't abort, So resolve the current variable.
2224 const bool clause_used = ResolvePBConflict(var, conflict, &slack);
2225
2226 // At this point, we have a negative slack. Note that ReduceCoefficients()
2227 // will not change it. However it may change the slack value of the next
2228 // iteration (when we will no longer take into account the true literal
2229 // with highest trail index).
2230 //
2231 // Note that the trail_index has already been decremented, it is why
2232 // we need the +1 in the slack computation.
2233 const Coefficient slack_only_for_debug =
2235 ? conflict->ComputeSlackForTrailPrefix(*trail_, trail_index + 1)
2236 : Coefficient(0);
2237
2238 if (clause_used) {
2239 // If a clause was used, we know that slack has the correct value.
2240 if (!parameters_->minimize_reduction_during_pb_resolution()) {
2241 conflict->ReduceCoefficients();
2242 }
2243 } else {
2244 // TODO(user): The function below can take most of the running time on
2245 // some instances. The goal is to have slack updated to its new value
2246 // incrementally, but we are not here yet.
2247 if (parameters_->minimize_reduction_during_pb_resolution()) {
2248 slack =
2249 conflict->ComputeSlackForTrailPrefix(*trail_, trail_index + 1);
2250 } else {
2251 slack = conflict->ReduceCoefficientsAndComputeSlackForTrailPrefix(
2252 *trail_, trail_index + 1);
2253 }
2254 }
2255 DCHECK_EQ(slack, slack_only_for_debug);
2256 CHECK_LT(slack, 0);
2257 if (conflict->Rhs() < 0) {
2258 *pb_backjump_level = -1;
2259 return;
2260 }
2261 }
2262 }
2263
2264 // Reduce the conflit coefficients if it is not already done.
2265 // This is important to avoid integer overflow.
2266 if (!parameters_->minimize_reduction_during_pb_resolution()) {
2267 conflict->ReduceCoefficients();
2268 }
2269
2270 // Double check.
2271 // The sum of the literal with level <= backjump_level must propagate.
2272 std::vector<Coefficient> sum_for_le_level(backjump_level + 2, Coefficient(0));
2273 std::vector<Coefficient> max_coeff_for_ge_level(backjump_level + 2,
2274 Coefficient(0));
2275 int size = 0;
2276 Coefficient max_sum(0);
2277 for (BooleanVariable var : conflict->PossibleNonZeros()) {
2278 const Coefficient coeff = conflict->GetCoefficient(var);
2279 if (coeff == 0) continue;
2280 max_sum += coeff;
2281 ++size;
2282 if (!trail_->Assignment().VariableIsAssigned(var) ||
2283 DecisionLevel(var) > backjump_level) {
2284 max_coeff_for_ge_level[backjump_level + 1] =
2285 std::max(max_coeff_for_ge_level[backjump_level + 1], coeff);
2286 } else {
2287 const int level = DecisionLevel(var);
2288 if (trail_->Assignment().LiteralIsTrue(conflict->GetLiteral(var))) {
2289 sum_for_le_level[level] += coeff;
2290 }
2291 max_coeff_for_ge_level[level] =
2292 std::max(max_coeff_for_ge_level[level], coeff);
2293 }
2294 }
2295
2296 // Compute the cumulative version.
2297 for (int i = 1; i < sum_for_le_level.size(); ++i) {
2298 sum_for_le_level[i] += sum_for_le_level[i - 1];
2299 }
2300 for (int i = max_coeff_for_ge_level.size() - 2; i >= 0; --i) {
2301 max_coeff_for_ge_level[i] =
2302 std::max(max_coeff_for_ge_level[i], max_coeff_for_ge_level[i + 1]);
2303 }
2304
2305 // Compute first propagation level. -1 means that the problem is UNSAT.
2306 // Note that the first propagation level may be < backjump_level!
2307 if (sum_for_le_level[0] > conflict->Rhs()) {
2308 *pb_backjump_level = -1;
2309 return;
2310 }
2311 for (int i = 0; i <= backjump_level; ++i) {
2312 const Coefficient level_sum = sum_for_le_level[i];
2313 CHECK_LE(level_sum, conflict->Rhs());
2314 if (conflict->Rhs() - level_sum < max_coeff_for_ge_level[i + 1]) {
2315 *pb_backjump_level = i;
2316 return;
2317 }
2318 }
2319 LOG(FATAL) << "The code should never reach here.";
2320}
2321
2322void SatSolver::MinimizeConflict(std::vector<Literal>* conflict) {
2323 SCOPED_TIME_STAT(&stats_);
2324
2325 const int old_size = conflict->size();
2326 switch (parameters_->minimization_algorithm()) {
2327 case SatParameters::NONE:
2328 return;
2329 case SatParameters::SIMPLE: {
2330 MinimizeConflictSimple(conflict);
2331 break;
2332 }
2333 case SatParameters::RECURSIVE: {
2334 MinimizeConflictRecursively(conflict);
2335 break;
2336 }
2337 case SatParameters::EXPERIMENTAL: {
2338 MinimizeConflictExperimental(conflict);
2339 break;
2340 }
2341 }
2342 if (conflict->size() < old_size) {
2343 ++counters_.num_minimizations;
2344 counters_.num_literals_removed += old_size - conflict->size();
2345 }
2346}
2347
2348// This simple version just looks for any literal that is directly infered by
2349// other literals of the conflict. It is directly infered if the literals of its
2350// reason clause are either from level 0 or from the conflict itself.
2351//
2352// Note that because of the assignment structure, there is no need to process
2353// the literals of the conflict in order. While exploring the reason for a
2354// literal assignment, there will be no cycles.
2355void SatSolver::MinimizeConflictSimple(std::vector<Literal>* conflict) {
2356 SCOPED_TIME_STAT(&stats_);
2357 const int current_level = CurrentDecisionLevel();
2358
2359 // Note that is_marked_ is already initialized and that we can start at 1
2360 // since the first literal of the conflict is the 1-UIP literal.
2361 int index = 1;
2362 for (int i = 1; i < conflict->size(); ++i) {
2363 const BooleanVariable var = (*conflict)[i].Variable();
2364 bool can_be_removed = false;
2365 if (DecisionLevel(var) != current_level) {
2366 // It is important not to call Reason(var) when it can be avoided.
2367 const absl::Span<const Literal> reason = trail_->Reason(var);
2368 if (!reason.empty()) {
2369 can_be_removed = true;
2370 for (Literal literal : reason) {
2371 if (DecisionLevel(literal.Variable()) == 0) continue;
2372 if (!is_marked_[literal.Variable()]) {
2373 can_be_removed = false;
2374 break;
2375 }
2376 }
2377 }
2378 }
2379 if (!can_be_removed) {
2380 (*conflict)[index] = (*conflict)[i];
2381 ++index;
2382 }
2383 }
2384 conflict->erase(conflict->begin() + index, conflict->end());
2385}
2386
2387// This is similar to MinimizeConflictSimple() except that for each literal of
2388// the conflict, the literals of its reason are recursively expanded using their
2389// reason and so on. The recursion loops until we show that the initial literal
2390// can be infered from the conflict variables alone, or if we show that this is
2391// not the case. The result of any variable expansion will be cached in order
2392// not to be expended again.
2393void SatSolver::MinimizeConflictRecursively(std::vector<Literal>* conflict) {
2394 SCOPED_TIME_STAT(&stats_);
2395
2396 // is_marked_ will contains all the conflict literals plus the literals that
2397 // have been shown to depends only on the conflict literals. is_independent_
2398 // will contains the literals that have been shown NOT to depends only on the
2399 // conflict literals. The too set are exclusive for non-conflict literals, but
2400 // a conflict literal (which is always marked) can be independent if we showed
2401 // that it can't be removed from the clause.
2402 //
2403 // Optimization: There is no need to call is_marked_.ClearAndResize() or to
2404 // mark the conflict literals since this was already done by
2405 // ComputeFirstUIPConflict().
2406 is_independent_.ClearAndResize(num_variables_);
2407
2408 // min_trail_index_per_level_ will always be reset to all
2409 // std::numeric_limits<int>::max() at the end. This is used to prune the
2410 // search because any literal at a given level with an index smaller or equal
2411 // to min_trail_index_per_level_[level] can't be redundant.
2412 if (CurrentDecisionLevel() >= min_trail_index_per_level_.size()) {
2413 min_trail_index_per_level_.resize(CurrentDecisionLevel() + 1,
2414 std::numeric_limits<int>::max());
2415 }
2416
2417 // Compute the number of variable at each decision levels. This will be used
2418 // to pruned the DFS because we know that the minimized conflict will have at
2419 // least one variable of each decision levels. Because such variable can't be
2420 // eliminated using lower decision levels variable otherwise it will have been
2421 // propagated.
2422 //
2423 // Note(user): Because is_marked_ may actually contains literals that are
2424 // implied if the 1-UIP literal is false, we can't just iterate on the
2425 // variables of the conflict here.
2426 for (BooleanVariable var : is_marked_.PositionsSetAtLeastOnce()) {
2427 const int level = DecisionLevel(var);
2428 min_trail_index_per_level_[level] = std::min(
2429 min_trail_index_per_level_[level], trail_->Info(var).trail_index);
2430 }
2431
2432 // Remove the redundant variable from the conflict. That is the ones that can
2433 // be infered by some other variables in the conflict.
2434 // Note that we can skip the first position since this is the 1-UIP.
2435 int index = 1;
2436 for (int i = 1; i < conflict->size(); ++i) {
2437 const BooleanVariable var = (*conflict)[i].Variable();
2438 const AssignmentInfo& info = trail_->Info(var);
2439 if (time_limit_->LimitReached() ||
2440 info.type == AssignmentType::kSearchDecision ||
2441 info.trail_index <= min_trail_index_per_level_[info.level] ||
2442 !CanBeInferedFromConflictVariables(var)) {
2443 // Mark the conflict variable as independent. Note that is_marked_[var]
2444 // will still be true.
2445 is_independent_.Set(var);
2446 (*conflict)[index] = (*conflict)[i];
2447 ++index;
2448 }
2449 }
2450 conflict->resize(index);
2451
2452 // Reset min_trail_index_per_level_. We use the sparse version only if it
2453 // involves less than half the size of min_trail_index_per_level_.
2454 const int threshold = min_trail_index_per_level_.size() / 2;
2455 if (is_marked_.PositionsSetAtLeastOnce().size() < threshold) {
2456 for (BooleanVariable var : is_marked_.PositionsSetAtLeastOnce()) {
2457 min_trail_index_per_level_[DecisionLevel(var)] =
2458 std::numeric_limits<int>::max();
2459 }
2460 } else {
2461 min_trail_index_per_level_.clear();
2462 }
2463}
2464
2465bool SatSolver::CanBeInferedFromConflictVariables(BooleanVariable variable) {
2466 // Test for an already processed variable with the same reason.
2467 {
2468 DCHECK(is_marked_[variable]);
2469 const BooleanVariable v =
2470 same_reason_identifier_.FirstVariableWithSameReason(variable);
2471 if (v != variable) return !is_independent_[v];
2472 }
2473
2474 // This function implement an iterative DFS from the given variable. It uses
2475 // the reason clause as adjacency lists. dfs_stack_ can be seens as the
2476 // recursive call stack of the variable we are currently processing. All its
2477 // adjacent variable will be pushed into variable_to_process_, and we will
2478 // then dequeue them one by one and process them.
2479 //
2480 // Note(user): As of 03/2014, --cpu_profile seems to indicate that using
2481 // dfs_stack_.assign(1, variable) is slower. My explanation is that the
2482 // function call is not inlined.
2483 dfs_stack_.clear();
2484 dfs_stack_.push_back(variable);
2485 variable_to_process_.clear();
2486 variable_to_process_.push_back(variable);
2487
2488 // First we expand the reason for the given variable.
2489 for (const Literal literal : trail_->Reason(variable)) {
2490 const BooleanVariable var = literal.Variable();
2491 DCHECK_NE(var, variable);
2492 if (is_marked_[var]) continue;
2493 const AssignmentInfo& info = trail_->Info(var);
2494 if (info.level == 0) {
2495 // Note that this is not needed if the solver is not configured to produce
2496 // an unsat proof. However, the (level == 0) test should always be false
2497 // in this case because there will never be literals of level zero in any
2498 // reason when we don't want a proof.
2499 is_marked_.Set(var);
2500 continue;
2501 }
2502 if (info.trail_index <= min_trail_index_per_level_[info.level] ||
2503 info.type == AssignmentType::kSearchDecision || is_independent_[var]) {
2504 return false;
2505 }
2506 variable_to_process_.push_back(var);
2507 }
2508
2509 // Then we start the DFS.
2510 while (!variable_to_process_.empty()) {
2511 const BooleanVariable current_var = variable_to_process_.back();
2512 if (current_var == dfs_stack_.back()) {
2513 // We finished the DFS of the variable dfs_stack_.back(), this can be seen
2514 // as a recursive call terminating.
2515 if (dfs_stack_.size() > 1) {
2516 DCHECK(!is_marked_[current_var]);
2517 is_marked_.Set(current_var);
2518 }
2519 variable_to_process_.pop_back();
2520 dfs_stack_.pop_back();
2521 continue;
2522 }
2523
2524 // If this variable became marked since the we pushed it, we can skip it.
2525 if (is_marked_[current_var]) {
2526 variable_to_process_.pop_back();
2527 continue;
2528 }
2529
2530 // This case will never be encountered since we abort right away as soon
2531 // as an independent variable is found.
2532 DCHECK(!is_independent_[current_var]);
2533
2534 // Test for an already processed variable with the same reason.
2535 {
2536 const BooleanVariable v =
2537 same_reason_identifier_.FirstVariableWithSameReason(current_var);
2538 if (v != current_var) {
2539 if (is_independent_[v]) break;
2540 DCHECK(is_marked_[v]);
2541 variable_to_process_.pop_back();
2542 continue;
2543 }
2544 }
2545
2546 // Expand the variable. This can be seen as making a recursive call.
2547 dfs_stack_.push_back(current_var);
2548 bool abort_early = false;
2549 for (Literal literal : trail_->Reason(current_var)) {
2550 const BooleanVariable var = literal.Variable();
2551 DCHECK_NE(var, current_var);
2552 const AssignmentInfo& info = trail_->Info(var);
2553 if (info.level == 0 || is_marked_[var]) continue;
2554 if (info.trail_index <= min_trail_index_per_level_[info.level] ||
2555 info.type == AssignmentType::kSearchDecision ||
2556 is_independent_[var]) {
2557 abort_early = true;
2558 break;
2559 }
2560 variable_to_process_.push_back(var);
2561 }
2562 if (abort_early) break;
2563 }
2564
2565 // All the variable left on the dfs_stack_ are independent.
2566 for (const BooleanVariable var : dfs_stack_) {
2567 is_independent_.Set(var);
2568 }
2569 return dfs_stack_.empty();
2570}
2571
2572namespace {
2573
2574struct WeightedVariable {
2575 WeightedVariable(BooleanVariable v, int w) : var(v), weight(w) {}
2576
2577 BooleanVariable var;
2579};
2580
2581// Lexical order, by larger weight, then by smaller variable number
2582// to break ties
2583struct VariableWithLargerWeightFirst {
2584 bool operator()(const WeightedVariable& wv1,
2585 const WeightedVariable& wv2) const {
2586 return (wv1.weight > wv2.weight ||
2587 (wv1.weight == wv2.weight && wv1.var < wv2.var));
2588 }
2589};
2590} // namespace.
2591
2592// This function allows a conflict variable to be replaced by another variable
2593// not originally in the conflict. Greater reduction and backtracking can be
2594// achieved this way, but the effect of this is not clear.
2595//
2596// TODO(user): More investigation needed. This seems to help on the Hanoi
2597// problems, but degrades performance on others.
2598//
2599// TODO(user): Find a reference for this? neither minisat nor glucose do that,
2600// they just do MinimizeConflictRecursively() with a different implementation.
2601// Note that their behavior also make more sense with the way they (and we) bump
2602// the variable activities.
2603void SatSolver::MinimizeConflictExperimental(std::vector<Literal>* conflict) {
2604 SCOPED_TIME_STAT(&stats_);
2605
2606 // First, sort the variables in the conflict by decreasing decision levels.
2607 // Also initialize is_marked_ to true for all conflict variables.
2608 is_marked_.ClearAndResize(num_variables_);
2609 const int current_level = CurrentDecisionLevel();
2610 std::vector<WeightedVariable> variables_sorted_by_level;
2611 for (Literal literal : *conflict) {
2612 const BooleanVariable var = literal.Variable();
2613 is_marked_.Set(var);
2614 const int level = DecisionLevel(var);
2615 if (level < current_level) {
2616 variables_sorted_by_level.push_back(WeightedVariable(var, level));
2617 }
2618 }
2619 std::sort(variables_sorted_by_level.begin(), variables_sorted_by_level.end(),
2620 VariableWithLargerWeightFirst());
2621
2622 // Then process the reason of the variable with highest level first.
2623 std::vector<BooleanVariable> to_remove;
2624 for (WeightedVariable weighted_var : variables_sorted_by_level) {
2625 const BooleanVariable var = weighted_var.var;
2626
2627 // A nullptr reason means that this was a decision variable from the
2628 // previous levels.
2629 const absl::Span<const Literal> reason = trail_->Reason(var);
2630 if (reason.empty()) continue;
2631
2632 // Compute how many and which literals from the current reason do not appear
2633 // in the current conflict. Level 0 literals are ignored.
2634 std::vector<Literal> not_contained_literals;
2635 for (const Literal reason_literal : reason) {
2636 const BooleanVariable reason_var = reason_literal.Variable();
2637
2638 // We ignore level 0 variables.
2639 if (DecisionLevel(reason_var) == 0) continue;
2640
2641 // We have a reason literal whose variable is not yet seen.
2642 // If there is more than one, break right away, we will not minimize the
2643 // current conflict with this variable.
2644 if (!is_marked_[reason_var]) {
2645 not_contained_literals.push_back(reason_literal);
2646 if (not_contained_literals.size() > 1) break;
2647 }
2648 }
2649 if (not_contained_literals.empty()) {
2650 // This variable will be deleted from the conflict. Note that we don't
2651 // unmark it. This is because this variable can be infered from the other
2652 // variables in the conflict, so it is okay to skip it when processing the
2653 // reasons of other variables.
2654 to_remove.push_back(var);
2655 } else if (not_contained_literals.size() == 1) {
2656 // Replace the literal from variable var with the only
2657 // not_contained_literals from the current reason.
2658 to_remove.push_back(var);
2659 is_marked_.Set(not_contained_literals.front().Variable());
2660 conflict->push_back(not_contained_literals.front());
2661 }
2662 }
2663
2664 // Unmark the variable that should be removed from the conflict.
2665 for (BooleanVariable var : to_remove) {
2666 is_marked_.Clear(var);
2667 }
2668
2669 // Remove the now unmarked literals from the conflict.
2670 int index = 0;
2671 for (int i = 0; i < conflict->size(); ++i) {
2672 const Literal literal = (*conflict)[i];
2673 if (is_marked_[literal.Variable()]) {
2674 (*conflict)[index] = literal;
2675 ++index;
2676 }
2677 }
2678 conflict->erase(conflict->begin() + index, conflict->end());
2679}
2680
2681void SatSolver::CleanClauseDatabaseIfNeeded() {
2682 if (num_learned_clause_before_cleanup_ > 0) return;
2683 SCOPED_TIME_STAT(&stats_);
2684
2685 // Creates a list of clauses that can be deleted. Note that only the clauses
2686 // that appear in clauses_info can potentially be removed.
2687 typedef std::pair<SatClause*, ClauseInfo> Entry;
2688 std::vector<Entry> entries;
2689 auto& clauses_info = *(clauses_propagator_->mutable_clauses_info());
2690 for (auto& entry : clauses_info) {
2691 if (ClauseIsUsedAsReason(entry.first)) continue;
2692 if (entry.second.protected_during_next_cleanup) {
2693 entry.second.protected_during_next_cleanup = false;
2694 continue;
2695 }
2696 entries.push_back(entry);
2697 }
2698 const int num_protected_clauses = clauses_info.size() - entries.size();
2699
2700 if (parameters_->clause_cleanup_ordering() == SatParameters::CLAUSE_LBD) {
2701 // Order the clauses by decreasing LBD and then increasing activity.
2702 std::sort(entries.begin(), entries.end(),
2703 [](const Entry& a, const Entry& b) {
2704 if (a.second.lbd == b.second.lbd) {
2705 return a.second.activity < b.second.activity;
2706 }
2707 return a.second.lbd > b.second.lbd;
2708 });
2709 } else {
2710 // Order the clauses by increasing activity and then decreasing LBD.
2711 std::sort(entries.begin(), entries.end(),
2712 [](const Entry& a, const Entry& b) {
2713 if (a.second.activity == b.second.activity) {
2714 return a.second.lbd > b.second.lbd;
2715 }
2716 return a.second.activity < b.second.activity;
2717 });
2718 }
2719
2720 // The clause we want to keep are at the end of the vector.
2721 int num_kept_clauses =
2722 (parameters_->clause_cleanup_target() > 0)
2723 ? std::min(static_cast<int>(entries.size()),
2724 parameters_->clause_cleanup_target())
2725 : static_cast<int>(parameters_->clause_cleanup_ratio() *
2726 static_cast<double>(entries.size()));
2727
2728 int num_deleted_clauses = entries.size() - num_kept_clauses;
2729
2730 // Tricky: Because the order of the clauses_info iteration is NOT
2731 // deterministic (pointer keys), we also keep all the clauses which have the
2732 // same LBD and activity as the last one so the behavior is deterministic.
2733 if (num_kept_clauses > 0) {
2734 while (num_deleted_clauses > 0) {
2735 const ClauseInfo& a = entries[num_deleted_clauses].second;
2736 const ClauseInfo& b = entries[num_deleted_clauses - 1].second;
2737 if (a.activity != b.activity || a.lbd != b.lbd) break;
2738 --num_deleted_clauses;
2739 ++num_kept_clauses;
2740 }
2741 }
2742 if (num_deleted_clauses > 0) {
2743 entries.resize(num_deleted_clauses);
2744 for (const Entry& entry : entries) {
2745 SatClause* clause = entry.first;
2746 counters_.num_literals_forgotten += clause->size();
2747 clauses_propagator_->LazyDetach(clause);
2748 }
2749 clauses_propagator_->CleanUpWatchers();
2750
2751 // TODO(user): If the need arise, we could avoid this linear scan on the
2752 // full list of clauses by not keeping the clauses from clauses_info there.
2753 if (!block_clause_deletion_) {
2754 clauses_propagator_->DeleteRemovedClauses();
2755 }
2756 }
2757
2758 num_learned_clause_before_cleanup_ = parameters_->clause_cleanup_period();
2759 VLOG(1) << "Database cleanup, #protected:" << num_protected_clauses
2760 << " #kept:" << num_kept_clauses
2761 << " #deleted:" << num_deleted_clauses;
2762}
2763
2765 switch (status) {
2766 case SatSolver::ASSUMPTIONS_UNSAT:
2767 return "ASSUMPTIONS_UNSAT";
2768 case SatSolver::INFEASIBLE:
2769 return "INFEASIBLE";
2770 case SatSolver::FEASIBLE:
2771 return "FEASIBLE";
2772 case SatSolver::LIMIT_REACHED:
2773 return "LIMIT_REACHED";
2774 }
2775 // Fallback. We don't use "default:" so the compiler will return an error
2776 // if we forgot one enum case above.
2777 LOG(DFATAL) << "Invalid SatSolver::Status " << status;
2778 return "UNKNOWN";
2779}
2780
2781void MinimizeCore(SatSolver* solver, std::vector<Literal>* core) {
2782 std::vector<Literal> result;
2783 if (!solver->ResetToLevelZero()) return;
2784 for (const Literal lit : *core) {
2785 if (solver->Assignment().LiteralIsTrue(lit)) continue;
2786 result.push_back(lit);
2787 if (solver->Assignment().LiteralIsFalse(lit)) break;
2788 if (!solver->EnqueueDecisionIfNotConflicting(lit)) break;
2789 }
2790 if (result.size() < core->size()) {
2791 VLOG(1) << "minimization " << core->size() << " -> " << result.size();
2792 *core = result;
2793 }
2794}
2795
2796} // namespace sat
2797} // namespace operations_research
IntegerValue size
int64_t max
double Get() const
Definition timer.h:46
void Restart()
Definition timer.h:36
void EnableLogging(bool enable)
Definition logging.h:46
void SetLogToStdOut(bool enable)
Should all messages be displayed on stdout ?
Definition logging.h:52
bool LoggingIsEnabled() const
Returns true iff logging is enabled.
Definition logging.h:49
void ClearAndResize(IntegerType size)
Definition bitset.h:839
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
Definition bitset.h:878
void Clear(IntegerType index)
Definition bitset.h:874
void Set(IntegerType index)
Definition bitset.h:864
int NumberOfSetCallsWithDifferentArguments() const
Definition bitset.h:875
std::string StatString() const
Definition stats.cc:77
double GetElapsedDeterministicTime() const
Definition time_limit.h:217
void ResetLimitFromParameters(const Parameters &parameters)
Definition time_limit.h:482
const std::vector< BinaryClause > & newly_added() const
Returns the newly added BinaryClause since the last ClearNewlyAdded() call.
Definition clause.h:428
int64_t num_propagations() const
Number of literal propagated by this class (including conflicts).
Definition clause.h:666
void Resize(int num_variables)
Resizes the data structure.
Definition clause.cc:501
bool Propagate(Trail *trail) final
SatPropagator interface.
Definition clause.cc:839
int64_t num_minimization() const
MinimizeClause() stats.
Definition clause.h:672
void MinimizeConflictWithReachability(std::vector< Literal > *c)
Definition clause.cc:864
int64_t num_redundant_implications() const
Number of implications removed by transitive reduction.
Definition clause.h:688
bool IsEmpty() const final
Returns true if there is no constraints in this class.
Definition clause.h:514
bool AddBinaryClause(Literal a, Literal b)
Definition clause.cc:531
void MinimizeConflictExperimental(const Trail &trail, std::vector< Literal > *c)
Definition clause.cc:1025
void MinimizeConflictFirstWithTransitiveReduction(const Trail &trail, std::vector< Literal > *c, absl::BitGenRef random)
Definition clause.cc:963
void MinimizeConflictFirst(const Trail &trail, std::vector< Literal > *c, SparseBitset< BooleanVariable > *marked)
Definition clause.cc:941
ABSL_MUST_USE_RESULT bool AddAtMostOne(absl::Span< const Literal > at_most_one)
Definition clause.cc:591
int64_t num_inspections() const
Number of literals inspected by this class during propagation.
Definition clause.h:669
void LazyDetach(SatClause *clause)
Definition clause.cc:315
bool IsRemovable(SatClause *const clause) const
Definition clause.h:223
void Detach(SatClause *clause)
Definition clause.cc:322
int64_t num_inspected_clauses() const
Total number of clauses inspected during calls to PropagateOnFalse().
Definition clause.h:232
bool AddClause(absl::Span< const Literal > literals, Trail *trail, int lbd)
Adds a new clause and perform initial propagation for this clause only.
Definition clause.cc:224
int64_t num_inspected_clause_literals() const
Definition clause.h:233
int64_t num_watched_clauses() const
Number of clauses currently watched.
Definition clause.h:241
SatClause * ReasonClause(int trail_index) const
Definition clause.cc:216
absl::flat_hash_map< SatClause *, ClauseInfo > * mutable_clauses_info()
Definition clause.h:227
SatClause * AddRemovableClause(absl::Span< const Literal > literals, Trail *trail, int lbd)
Definition clause.cc:232
const std::vector< SatClause * > & AllClausesInCreationOrder() const
Definition clause.h:215
void Resize(int num_variables)
Must be called before adding clauses referring to such variables.
Definition clause.cc:88
ABSL_MUST_USE_RESULT bool InprocessingRewriteClause(SatClause *clause, absl::Span< const Literal > new_clause)
Definition clause.cc:388
void AddClause(absl::Span< const Literal > clause)
void DeleteClause(absl::Span< const Literal > clause)
BooleanVariable Variable() const
Definition sat_base.h:87
void Register(T *non_owned_class)
Definition model.h:179
Coefficient ComputeSlackForTrailPrefix(const Trail &trail, int trail_index) const
void AddToRhs(Coefficient value)
Adds a non-negative value to this constraint Rhs().
void CopyIntoVector(std::vector< LiteralWithCoeff > *output)
Copies this constraint into a vector<LiteralWithCoeff> representation.
bool AddConstraint(const std::vector< LiteralWithCoeff > &cst, Coefficient rhs, Trail *trail)
int64_t num_constraint_lookups() const
Some statistics.
void Resize(int num_variables)
Changes the number of variables.
int NumberOfConstraints() const
Returns the number of constraints managed by this class.
UpperBoundedLinearConstraint * ReasonPbConstraint(int trail_index) const
void BumpActivity(UpperBoundedLinearConstraint *constraint)
bool AddLearnedConstraint(const std::vector< LiteralWithCoeff > &cst, Coefficient rhs, Trail *trail)
UpperBoundedLinearConstraint * ConflictingConstraint()
Contain the logic to decide when to restart a SAT tree search.
Definition restart.h:32
int NumRestarts() const
Returns the number of restarts since the last Reset().
Definition restart.h:55
void Reset()
Resets the policy using the current model parameters.
Definition restart.cc:30
std::string InfoString() const
Returns a string with the current restart statistics.
Definition restart.cc:180
void OnConflict(int conflict_trail_index, int conflict_decision_level, int conflict_lbd)
Definition restart.cc:151
void BumpVariableActivities(absl::Span< const Literal > literals)
void Untrail(int target_trail_index)
Called on Untrail() so that we can update the set of possible decisions.
Base class for all the SAT constraints.
Definition sat_base.h:533
Status EnqueueDecisionAndBacktrackOnConflict(Literal true_literal, int *first_propagation_index=nullptr)
bool AddLinearConstraint(bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, std::vector< LiteralWithCoeff > *cst)
Status SolveWithTimeLimit(TimeLimit *time_limit)
bool AddBinaryClauses(const std::vector< BinaryClause > &clauses)
bool AddProblemClause(absl::Span< const Literal > literals, bool is_safe=true)
bool MinimizeByPropagation(double dtime)
const std::vector< BinaryClause > & NewlyAddedBinaryClauses()
void ProcessNewlyFixedVariables()
Simplifies the problem when new variables are assigned at level 0.
bool ResetWithGivenAssumptions(const std::vector< Literal > &assumptions)
int64_t num_branches() const
Some statistics since the creation of the solver.
void LoadDebugSolution(const std::vector< Literal > &solution)
std::vector< Literal > GetLastIncompatibleDecisions()
bool AddBinaryClause(Literal a, Literal b)
Same as AddProblemClause() below, but for small clauses.
int EnqueueDecisionAndBackjumpOnConflict(Literal true_literal)
void Backtrack(int target_level)
const SatParameters & parameters() const
bool EnqueueDecisionIfNotConflicting(Literal true_literal)
bool AddClauseDuringSearch(absl::Span< const Literal > literals)
ABSL_MUST_USE_RESULT bool Propagate()
void SetAssumptionLevel(int assumption_level)
ABSL_MUST_USE_RESULT bool AddUnitClause(Literal true_literal)
bool AddTernaryClause(Literal a, Literal b, Literal c)
void AdvanceDeterministicTime(TimeLimit *limit)
Definition sat_solver.h:475
Status ResetAndSolveWithGivenAssumptions(const std::vector< Literal > &assumptions, int64_t max_number_of_conflicts=-1)
const VariablesAssignment & Assignment() const
Definition sat_solver.h:388
ABSL_MUST_USE_RESULT bool ResetToLevelZero()
void AddLastPropagator(SatPropagator *propagator)
const std::vector< Decision > & Decisions() const
Definition sat_solver.h:385
ABSL_MUST_USE_RESULT bool FinishPropagation()
const Trail & LiteralTrail() const
Definition sat_solver.h:387
void SetNumVariables(int num_variables)
Definition sat_solver.cc:85
void AddPropagator(SatPropagator *propagator)
void SetParameters(const SatParameters &parameters)
BooleanVariable ReferenceVarWithSameReason(BooleanVariable var) const
Definition sat_base.h:659
std::vector< Literal > * MutableConflict()
Definition sat_base.h:433
int64_t NumberOfEnqueues() const
Definition sat_base.h:454
void SetDecisionLevel(int level)
Changes the decision level used by the next Enqueue().
Definition sat_base.h:426
int AssignmentType(BooleanVariable var) const
Definition sat_base.h:671
void EnqueueSearchDecision(Literal true_literal)
Specific Enqueue() version for the search decision.
Definition sat_base.h:323
void Untrail(int target_trail_index)
Definition sat_base.h:416
absl::Span< const Literal > Reason(BooleanVariable var, int64_t conflict_id=-1) const
Definition sat_base.h:680
void EnqueueWithUnitReason(Literal true_literal)
Specific Enqueue() version for a fixed variable.
Definition sat_base.h:328
const AssignmentInfo & Info(BooleanVariable var) const
Definition sat_base.h:463
void RegisterPropagator(SatPropagator *propagator)
Definition sat_base.h:650
SatClause * FailingSatClause() const
Definition sat_base.h:450
absl::Span< const Literal > FailingClause() const
Returns the last conflict.
Definition sat_base.h:439
void Resize(int num_variables)
Definition sat_base.h:638
const VariablesAssignment & Assignment() const
Definition sat_base.h:462
void AddToConflict(MutableUpperBoundedLinearConstraint *conflict)
BooleanVariable FirstVariableWithSameReason(BooleanVariable var)
void Clear()
Clears the cache. Call this before each conflict analysis.
bool VariableIsAssigned(BooleanVariable var) const
Returns true iff the given variable is assigned.
Definition sat_base.h:196
bool LiteralIsAssigned(Literal literal) const
Definition sat_base.h:191
Literal GetTrueLiteralForAssignedVariable(BooleanVariable var) const
Definition sat_base.h:203
bool LiteralIsFalse(Literal literal) const
Definition sat_base.h:185
bool LiteralIsTrue(Literal literal) const
Definition sat_base.h:188
int64_t b
Definition table.cc:45
int64_t a
Definition table.cc:44
SatParameters parameters
int64_t value
IntVar * var
absl::Status status
Definition g_gurobi.cc:44
double upper_bound
double lower_bound
GRBmodel * model
int lit
int literal
time_limit
Definition solve.cc:22
int index
const bool DEBUG_MODE
Definition macros.h:24
double solution
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition stl_util.h:58
std::string SatStatusString(SatSolver::Status status)
Returns a string representation of a SatSolver::Status.
Coefficient ComputeNegatedCanonicalRhs(Coefficient lower_bound, Coefficient bound_shift, Coefficient max_value)
bool BooleanLinearExpressionIsCanonical(absl::Span< const LiteralWithCoeff > cst)
Returns true iff the Boolean linear expression is in canonical form.
Coefficient ComputeCanonicalRhs(Coefficient upper_bound, Coefficient bound_shift, Coefficient max_value)
const int kUnsatTrailIndex
A constant used by the EnqueueDecision*() API.
Definition sat_solver.h:58
int MoveOneUnprocessedLiteralLast(const absl::btree_set< LiteralIndex > &processed, int relevant_prefix_size, std::vector< Literal > *literals)
Definition util.cc:344
void MinimizeCore(SatSolver *solver, std::vector< Literal > *core)
bool ComputeBooleanLinearExpressionCanonicalForm(std::vector< LiteralWithCoeff > *cst, Coefficient *bound_shift, Coefficient *max_value)
In SWIG mode, we don't want anything besides these top-level includes.
std::string ProtobufShortDebugString(const P &message)
Definition proto_utils.h:41
std::string MemoryUsage()
Returns the current thread's total memory usage in an human-readable string.
Definition stats.cc:31
bool SafeAddInto(IntegerType a, IntegerType *b)
STL namespace.
trees with all degrees equal w the current value of w
int64_t weight
Definition pack.cc:510
static int input(yyscan_t yyscanner)
IntervalVar * interval
Definition resource.cc:101
#define IF_STATS_ENABLED(instructions)
Definition stats.h:417
#define SCOPED_TIME_STAT(stats)
Definition stats.h:418
Information about a variable assignment.
Definition sat_base.h:242
int32_t trail_index
The index of this assignment in the trail.
Definition sat_base.h:263
A binary clause. This is used by BinaryClauseManager.
Definition clause.h:395
Represents a term in a pseudo-Boolean formula.
int64_t minimization_num_clauses
TryToMinimizeClause() stats.
Definition sat_solver.h:422
int64_t num_minimizations
Minimization stats.
Definition sat_solver.h:410
int64_t num_literals_learned
Clause learning /deletion stats.
Definition sat_solver.h:417
#define SOLVER_LOG(logger,...)
Definition logging.h:109