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