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