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