Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
sat_solver.cc
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cstddef>
18#include <cstdint>
19#include <limits>
20#include <memory>
21#include <optional>
22#include <string>
23#include <utility>
24#include <vector>
25
26#include "absl/algorithm/container.h"
27#include "absl/container/flat_hash_map.h"
28#include "absl/log/check.h"
29#include "absl/log/log.h"
30#include "absl/log/vlog_is_on.h"
31#include "absl/strings/str_cat.h"
32#include "absl/strings/str_format.h"
33#include "absl/types/span.h"
36#include "ortools/base/timer.h"
39#include "ortools/sat/clause.h"
42#include "ortools/sat/model.h"
44#include "ortools/sat/restart.h"
48#include "ortools/sat/util.h"
49#include "ortools/util/bitset.h"
52#include "ortools/util/stats.h"
55
56namespace operations_research {
57namespace sat {
58
60 owned_model_.reset(model_);
61 model_->Register<SatSolver>(this);
62}
63
65 : model_(model),
66 clause_id_generator_(model->GetOrCreate<ClauseIdGenerator>()),
67 binary_implication_graph_(model->GetOrCreate<BinaryImplicationGraph>()),
68 clauses_propagator_(model->GetOrCreate<ClauseManager>()),
69 enforcement_propagator_(model->GetOrCreate<EnforcementPropagator>()),
70 pb_constraints_(model->GetOrCreate<PbConstraints>()),
71 track_binary_clauses_(false),
72 trail_(model->GetOrCreate<Trail>()),
73 time_limit_(model->GetOrCreate<TimeLimit>()),
74 parameters_(model->GetOrCreate<SatParameters>()),
75 restart_(model->GetOrCreate<RestartPolicy>()),
76 decision_policy_(model->GetOrCreate<SatDecisionPolicy>()),
77 logger_(model->GetOrCreate<SolverLogger>()),
78 clause_activity_increment_(1.0),
79 same_reason_identifier_(*trail_),
80 is_relevant_for_core_computation_(true),
81 lrat_proof_handler_(model->Mutable<LratProofHandler>()),
82 stats_("SatSolver") {
83 trail_->EnableChronologicalBacktracking(
84 parameters_->use_chronological_backtracking());
85 InitializePropagators();
86}
87
88SatSolver::~SatSolver() { IF_STATS_ENABLED(LOG(INFO) << stats_.StatString()); }
89
90void SatSolver::SetNumVariables(int num_variables) {
91 SCOPED_TIME_STAT(&stats_);
92 CHECK_GE(num_variables, num_variables_);
93
94 num_variables_ = num_variables;
95 binary_implication_graph_->Resize(num_variables);
96 clauses_propagator_->Resize(num_variables);
97 trail_->Resize(num_variables);
98 decision_policy_->IncreaseNumVariables(num_variables);
99 pb_constraints_->Resize(num_variables);
100 same_reason_identifier_.Resize(num_variables);
101}
102
103int64_t SatSolver::num_branches() const { return counters_.num_branches; }
104
105int64_t SatSolver::num_failures() const { return counters_.num_failures; }
106
108 return trail_->NumberOfEnqueues() - counters_.num_branches;
109}
110
111int64_t SatSolver::num_backtracks() const { return counters_.num_backtracks; }
112
113int64_t SatSolver::num_restarts() const { return counters_.num_restarts; }
115 return counters_.num_backtracks_to_root;
116}
117
119 // Each of these counters mesure really basic operations. The weight are just
120 // an estimate of the operation complexity. Note that these counters are never
121 // reset to zero once a SatSolver is created.
122 //
123 // TODO(user): Find a better procedure to fix the weight than just educated
124 // guess.
125 return 1e-8 * (8.0 * trail_->NumberOfEnqueues() +
126 1.0 * binary_implication_graph_->num_inspections() +
127 4.0 * clauses_propagator_->num_inspected_clauses() +
128 1.0 * clauses_propagator_->num_inspected_clause_literals() +
129
130 // Here there is a factor 2 because of the untrail.
131 20.0 * pb_constraints_->num_constraint_lookups() +
132 2.0 * pb_constraints_->num_threshold_updates() +
133 1.0 * pb_constraints_->num_inspected_constraint_literals());
134}
135
137 SCOPED_TIME_STAT(&stats_);
138 return *parameters_;
139}
140
142 SCOPED_TIME_STAT(&stats_);
143 *parameters_ = parameters;
144 restart_->Reset();
145 time_limit_->ResetLimitFromParameters(parameters);
146 logger_->EnableLogging(parameters.log_search_progress() || VLOG_IS_ON(1));
147 logger_->SetLogToStdOut(parameters.log_to_stdout());
148}
149
150bool SatSolver::IsMemoryLimitReached() const {
151 const int64_t memory_usage =
153 const int64_t kMegaByte = 1024 * 1024;
154 return memory_usage > kMegaByte * parameters_->max_memory_in_mb();
155}
156
157bool SatSolver::SetModelUnsat() {
158 model_is_unsat_ = true;
159 return false;
160}
161
162bool SatSolver::AddClauseDuringSearch(absl::Span<const Literal> literals) {
163 if (model_is_unsat_) return false;
164
165 // Let filter clauses if we are at level zero
166 if (trail_->CurrentDecisionLevel() == 0) {
167 return AddProblemClause(literals);
168 }
169
170 const int index = trail_->Index();
171 if (literals.empty()) return SetModelUnsat();
172 if (literals.size() == 1) return AddUnitClause(literals[0]);
173 if (literals.size() == 2) {
174 // TODO(user): We generate in some corner cases clauses with
175 // literals[0].Variable() == literals[1].Variable(). Avoid doing that and
176 // adding such binary clauses to the graph?
177 if (!binary_implication_graph_->AddBinaryClause(literals[0], literals[1])) {
178 CHECK_EQ(CurrentDecisionLevel(), 0);
179 return SetModelUnsat();
180 }
181 } else {
182 if (!clauses_propagator_->AddClause(literals)) {
183 CHECK_EQ(CurrentDecisionLevel(), 0);
184 return SetModelUnsat();
185 }
186 }
187
188 // Just disable propagation for these clauses with the new conflict
189 // resolution. TODO(user): we should probably just never propagate.
190 if (parameters_->use_new_integer_conflict_resolution()) return true;
191
192 // Tricky: Even if nothing new is propagated, calling Propagate() might, via
193 // the LP, deduce new things. This is problematic because some code assumes
194 // that when we create newly associated literals, nothing else changes.
195 if (trail_->Index() == index) return true;
196 return FinishPropagation();
197}
198
200 return AddProblemClause({true_literal});
201}
202
206
208 return AddProblemClause({a, b, c});
209}
210
211// Note that we will do a bit of presolve here, which might not always be
212// necessary if we know we are already adding a "clean" clause with no
213// duplicates or literal equivalent to others. However, we found that it is
214// better to make sure we always have "clean" clause in the solver rather than
215// to over-optimize this. In particular, presolve might be disabled or
216// incomplete, so such unclean clause might find their way here.
217bool SatSolver::AddProblemClause(absl::Span<const Literal> literals,
218 bool shared) {
219 SCOPED_TIME_STAT(&stats_);
220 DCHECK_EQ(CurrentDecisionLevel(), 0);
221 if (model_is_unsat_) return false;
222
223 // Filter already assigned literals. Note that we also remap literal in case
224 // we discovered equivalence later in the search.
225 tmp_literals_.clear();
226 if (lrat_proof_handler_ != nullptr) {
227 tmp_clause_ids_.clear();
228 for (Literal l : literals) {
229 const Literal rep = binary_implication_graph_->RepresentativeOf(l);
230 if (trail_->Assignment().LiteralIsTrue(rep)) return true;
231 if (trail_->Assignment().LiteralIsFalse(rep)) {
232 tmp_clause_ids_.push_back(trail_->GetUnitClauseId(rep.Variable()));
233 }
234 if (rep != l) {
235 tmp_clause_ids_.push_back(
236 binary_implication_graph_->GetClauseId(l.Negated(), rep));
237 }
238 if (!trail_->Assignment().LiteralIsFalse(rep)) {
239 tmp_literals_.push_back(rep);
240 }
241 }
242 } else {
243 for (Literal l : literals) {
244 l = binary_implication_graph_->RepresentativeOf(l);
245 if (trail_->Assignment().LiteralIsTrue(l)) return true;
246 if (trail_->Assignment().LiteralIsFalse(l)) continue;
247 tmp_literals_.push_back(l);
248 }
249 }
250
251 // A clause with l and not(l) is trivially true.
252 gtl::STLSortAndRemoveDuplicates(&tmp_literals_);
253 for (int i = 0; i + 1 < tmp_literals_.size(); ++i) {
254 if (tmp_literals_[i] == tmp_literals_[i + 1].Negated()) {
255 return true;
256 }
257 }
258 ClauseId id = kNoClauseId;
259 if (lrat_proof_handler_ != nullptr) {
260 // Add the original problem clause.
261 id = clause_id_generator_->GetNextId();
262 if (shared) {
263 lrat_proof_handler_->AddImportedClause(id, literals);
264 } else {
265 lrat_proof_handler_->AddProblemClause(id, literals);
266 }
267 // If the filtered clause is different, add it (with proof), and delete the
268 // original one.
269 if (!tmp_clause_ids_.empty()) {
270 tmp_clause_ids_.push_back(id);
271 ClauseId new_id = clause_id_generator_->GetNextId();
272 lrat_proof_handler_->AddInferredClause(new_id, tmp_literals_,
273 tmp_clause_ids_);
274 lrat_proof_handler_->DeleteClause(id, literals);
275 id = new_id;
276 }
277 }
278
279 return AddProblemClauseInternal(id, tmp_literals_);
280}
281
282bool SatSolver::AddProblemClauseInternal(ClauseId id,
283 absl::Span<const Literal> literals) {
284 SCOPED_TIME_STAT(&stats_);
285 if (DEBUG_MODE && CurrentDecisionLevel() == 0) {
286 for (const Literal l : literals) {
287 CHECK(!trail_->Assignment().LiteralIsAssigned(l));
288 }
289 }
290
291 if (literals.empty()) return SetModelUnsat();
292
293 if (literals.size() == 1) {
294 trail_->EnqueueWithUnitReason(id, literals[0]);
295 } else if (literals.size() == 2) {
296 // TODO(user): Make sure the presolve do not generate such clauses.
297 if (literals[0] == literals[1]) {
298 // Literal must be true.
299 trail_->EnqueueWithUnitReason(id, literals[0]);
300 } else if (literals[0] == literals[1].Negated()) {
301 // Always true.
302 return true;
303 } else {
304 AddBinaryClauseInternal(id, literals[0], literals[1]);
305 }
306 } else {
307 if (!clauses_propagator_->AddClause(id, literals, trail_, /*lbd=*/-1)) {
308 return SetModelUnsat();
309 }
310 }
311
312 // Tricky: The PropagationIsDone() condition shouldn't change anything for a
313 // pure SAT problem, however in the CP-SAT context, calling Propagate() can
314 // tigger computation (like the LP) even if no domain changed since the last
315 // call. We do not want to do that.
316 if (!PropagationIsDone() && !Propagate()) {
317 // This adds the UNSAT proof to the LRAT handler, if any.
319 return SetModelUnsat();
320 }
321 return true;
322}
323
324bool SatSolver::AddLinearConstraintInternal(
325 const std::vector<Literal>& enforcement_literals,
326 const std::vector<LiteralWithCoeff>& cst, Coefficient rhs,
327 Coefficient max_value) {
328 SCOPED_TIME_STAT(&stats_);
329 DCHECK(BooleanLinearExpressionIsCanonical(enforcement_literals, cst));
330 if (rhs < 0) {
331 // Unsatisfiable constraint if enforced.
332 if (enforcement_literals.empty()) {
333 return SetModelUnsat();
334 } else {
335 tmp_literals_.clear();
336 for (const Literal& literal : enforcement_literals) {
337 tmp_literals_.push_back(literal.Negated());
338 }
339 return AddProblemClauseInternal(kNoClauseId, tmp_literals_);
340 }
341 }
342 if (rhs >= max_value) return true; // Always satisfied constraint.
343
344 // Since the constraint is in canonical form, the coefficients are sorted.
345 const Coefficient min_coeff = cst.front().coefficient;
346 const Coefficient max_coeff = cst.back().coefficient;
347
348 // A linear upper bounded constraint is a clause if the only problematic
349 // assignment is the one where all the literals are true.
350 if (max_value - min_coeff <= rhs) {
351 // This constraint is actually a clause. It is faster to treat it as one.
352 if (enforcement_literals.empty()) {
353 tmp_literals_.clear();
354 for (const LiteralWithCoeff& term : cst) {
355 tmp_literals_.push_back(term.literal.Negated());
356 }
357 return AddProblemClauseInternal(kNoClauseId, tmp_literals_);
358 } else {
359 std::vector<Literal> literals;
360 for (const Literal& literal : enforcement_literals) {
361 literals.push_back(literal.Negated());
362 }
363 for (const LiteralWithCoeff& term : cst) {
364 literals.push_back(term.literal.Negated());
365 }
366 return AddProblemClause(literals);
367 }
368 }
369
370 // Detect at most one constraints. Note that this use the fact that the
371 // coefficient are sorted.
372 if (!parameters_->use_pb_resolution() && max_coeff <= rhs &&
373 2 * min_coeff > rhs && enforcement_literals.empty()) {
374 tmp_literals_.clear();
375 for (const LiteralWithCoeff& term : cst) {
376 tmp_literals_.push_back(term.literal);
377 }
378 if (!binary_implication_graph_->AddAtMostOne(tmp_literals_)) {
379 return SetModelUnsat();
380 }
381 return true;
382 }
383
384 // TODO(user): fix literals with coefficient larger than rhs to false, or
385 // add implication enforcement => not(literal) (and remove them from the
386 // constraint)?
387
388 // TODO(user): If this constraint forces all its literal to false (when rhs is
389 // zero for instance), we still add it. Optimize this?
390 return pb_constraints_->AddConstraint(enforcement_literals, cst, rhs, trail_);
391}
392
393void SatSolver::CanonicalizeLinear(std::vector<LiteralWithCoeff>* cst,
394 Coefficient* bound_shift,
395 Coefficient* max_value) {
396 // This block removes assigned literals from the constraint.
397 Coefficient fixed_variable_shift(0);
398 {
399 int index = 0;
400 for (const LiteralWithCoeff& term : *cst) {
401 if (trail_->Assignment().LiteralIsFalse(term.literal)) continue;
402 if (trail_->Assignment().LiteralIsTrue(term.literal)) {
403 CHECK(SafeAddInto(-term.coefficient, &fixed_variable_shift));
404 continue;
405 }
406 (*cst)[index] = term;
407 ++index;
408 }
409 cst->resize(index);
410 }
411
412 // Now we canonicalize.
413 // TODO(user): fix variables that must be true/false and remove them.
414 Coefficient bound_delta(0);
415 CHECK(ComputeBooleanLinearExpressionCanonicalForm(cst, &bound_delta,
416 max_value));
417
418 CHECK(SafeAddInto(bound_delta, bound_shift));
419 CHECK(SafeAddInto(fixed_variable_shift, bound_shift));
420}
421
422bool SatSolver::AddLinearConstraint(bool use_lower_bound,
423 Coefficient lower_bound,
424 bool use_upper_bound,
425 Coefficient upper_bound,
426 std::vector<Literal>* enforcement_literals,
427 std::vector<LiteralWithCoeff>* cst) {
428 SCOPED_TIME_STAT(&stats_);
429 CHECK_EQ(CurrentDecisionLevel(), 0);
430 if (model_is_unsat_) return false;
431
432 gtl::STLSortAndRemoveDuplicates(enforcement_literals);
433 int num_enforcement_literals = 0;
434 for (int i = 0; i < enforcement_literals->size(); ++i) {
435 const Literal literal = (*enforcement_literals)[i];
436 if (trail_->Assignment().LiteralIsFalse(literal)) {
437 return true;
438 }
439 if (!trail_->Assignment().LiteralIsTrue(literal)) {
440 (*enforcement_literals)[num_enforcement_literals++] = literal;
441 }
442 }
443 enforcement_literals->resize(num_enforcement_literals);
444
445 Coefficient bound_shift(0);
446
447 if (use_upper_bound) {
448 Coefficient max_value(0);
449 CanonicalizeLinear(cst, &bound_shift, &max_value);
450 const Coefficient rhs =
451 ComputeCanonicalRhs(upper_bound, bound_shift, max_value);
452 if (!AddLinearConstraintInternal(*enforcement_literals, *cst, rhs,
453 max_value)) {
454 return SetModelUnsat();
455 }
456 }
457
458 if (use_lower_bound) {
459 // We need to "re-canonicalize" in case some literal were fixed while we
460 // processed one direction.
461 Coefficient max_value(0);
462 CanonicalizeLinear(cst, &bound_shift, &max_value);
463
464 // We transform the constraint into an upper-bounded one.
465 for (int i = 0; i < cst->size(); ++i) {
466 (*cst)[i].literal = (*cst)[i].literal.Negated();
467 }
468 const Coefficient rhs =
469 ComputeNegatedCanonicalRhs(lower_bound, bound_shift, max_value);
470 if (!AddLinearConstraintInternal(*enforcement_literals, *cst, rhs,
471 max_value)) {
472 return SetModelUnsat();
473 }
474 }
475
476 // Tricky: The PropagationIsDone() condition shouldn't change anything for a
477 // pure SAT problem, however in the CP-SAT context, calling Propagate() can
478 // tigger computation (like the LP) even if no domain changed since the last
479 // call. We do not want to do that.
480 if (!PropagationIsDone() && !Propagate()) {
481 return SetModelUnsat();
482 }
483 return true;
484}
485
486int SatSolver::AddLearnedClauseAndEnqueueUnitPropagation(
487 ClauseId clause_id, absl::Span<const Literal> literals, bool is_redundant,
488 int min_lbd_of_subsumed_clauses) {
489 SCOPED_TIME_STAT(&stats_);
490
491 // Note that we might learn more than one conflict per "failure" actually.
492 // TODO(user): this should be called num_conflicts.
493 ++counters_.num_failures;
494
495 if (literals.size() == 1) {
496 // Corner case where we "learn" more than one conflict.
497 if (Assignment().LiteralIsTrue(literals[0])) return 1;
498 CHECK(!Assignment().LiteralIsFalse(literals[0]));
499
500 if (!trail_->ChronologicalBacktrackingEnabled()) {
501 // A length 1 clause fix a literal for all the search.
502 // ComputeBacktrackLevel() should have returned 0.
503 CHECK_EQ(CurrentDecisionLevel(), 0);
504 }
505 trail_->EnqueueWithUnitReason(clause_id, literals[0]);
506 return /*lbd=*/1;
507 }
508
509 if (literals.size() == 2) {
510 if (track_binary_clauses_) {
511 // This clause MUST be new, otherwise something is wrong.
512 CHECK(binary_clauses_.Add(BinaryClause(literals[0], literals[1])));
513 }
514 CHECK(binary_implication_graph_->AddBinaryClause(clause_id, literals[0],
515 literals[1]));
516 return /*lbd=*/2;
517 }
518
519 CleanClauseDatabaseIfNeeded();
520
521 // Important: Even though the only literal at the last decision level has
522 // been unassigned, its level was not modified, so ComputeLbd() works.
523 const int lbd = std::min(min_lbd_of_subsumed_clauses, ComputeLbd(literals));
524 if (is_redundant && lbd > parameters_->clause_cleanup_lbd_bound()) {
525 --num_learned_clause_before_cleanup_;
526
527 SatClause* clause = clauses_propagator_->AddRemovableClause(
528 clause_id, literals, trail_, lbd);
529 BumpClauseActivity(clause);
530 } else {
531 CHECK(clauses_propagator_->AddClause(clause_id, literals, trail_, lbd));
532 }
533 return lbd;
534}
535
537 CHECK_EQ(CurrentDecisionLevel(), 0);
538 trail_->RegisterPropagator(propagator);
539 external_propagators_.push_back(propagator);
540 InitializePropagators();
541}
542
544 CHECK_EQ(CurrentDecisionLevel(), 0);
545 CHECK(last_propagator_ == nullptr);
546 trail_->RegisterPropagator(propagator);
547 last_propagator_ = propagator;
548 InitializePropagators();
549}
550
551UpperBoundedLinearConstraint* SatSolver::ReasonPbConstraintOrNull(
552 BooleanVariable var) const {
553 // It is important to deal properly with "SameReasonAs" variables here.
554 var = trail_->ReferenceVarWithSameReason(var);
555 const AssignmentInfo& info = trail_->Info(var);
556 if (trail_->AssignmentType(var) == pb_constraints_->PropagatorId()) {
557 return pb_constraints_->ReasonPbConstraint(info.trail_index);
558 }
559 return nullptr;
560}
561
563 debug_assignment_.Resize(num_variables_.value());
564 for (BooleanVariable i(0); i < num_variables_; ++i) {
565 debug_assignment_.AssignFromTrueLiteral(
566 trail_->Assignment().GetTrueLiteralForAssignedVariable(i));
567 }
568}
569
570void SatSolver::LoadDebugSolution(absl::Span<const Literal> solution) {
571 debug_assignment_.Resize(num_variables_.value());
572 for (BooleanVariable var(0); var < num_variables_; ++var) {
573 if (!debug_assignment_.VariableIsAssigned(var)) continue;
574 debug_assignment_.UnassignLiteral(Literal(var, true));
575 }
576 for (const Literal l : solution) {
577 debug_assignment_.AssignFromTrueLiteral(l);
578 }
579
580 // We should only call this with complete solution.
581 for (BooleanVariable var(0); var < num_variables_; ++var) {
582 CHECK(debug_assignment_.VariableIsAssigned(var));
583 }
584}
585
586void SatSolver::AddBinaryClauseInternal(ClauseId id, Literal a, Literal b) {
587 if (track_binary_clauses_) {
588 // Abort if this clause was already added.
589 if (!binary_clauses_.Add(BinaryClause(a, b))) return;
590 }
591
592 if (!binary_implication_graph_->AddBinaryClause(id, a, b)) {
593 CHECK_EQ(CurrentDecisionLevel(), 0);
594 SetModelUnsat();
595 }
596}
597
598bool SatSolver::ClauseIsValidUnderDebugAssignment(
599 absl::Span<const Literal> clause) const {
600 if (debug_assignment_.NumberOfVariables() == 0) return true;
601 for (Literal l : clause) {
602 if (l.Variable() >= debug_assignment_.NumberOfVariables() ||
603 debug_assignment_.LiteralIsTrue(l)) {
604 return true;
605 }
606 }
607 return false;
608}
609
610bool SatSolver::PBConstraintIsValidUnderDebugAssignment(
611 absl::Span<const LiteralWithCoeff> cst, const Coefficient rhs) const {
612 Coefficient sum(0.0);
613 for (LiteralWithCoeff term : cst) {
614 if (term.literal.Variable() >= debug_assignment_.NumberOfVariables()) {
615 continue;
616 }
617 if (debug_assignment_.LiteralIsTrue(term.literal)) {
618 sum += term.coefficient;
619 }
620 }
621 return sum <= rhs;
622}
623
625 Literal true_literal, std::optional<ConflictCallback> callback) {
626 SCOPED_TIME_STAT(&stats_);
627 if (model_is_unsat_) return kUnsatTrailIndex;
628 DCHECK(PropagationIsDone());
629
630 // We should never enqueue before the assumptions_.
631 if (DEBUG_MODE && !assumptions_.empty()) {
632 CHECK_GE(trail_->CurrentDecisionLevel(), assumption_level_);
633 }
634
635 EnqueueNewDecision(true_literal);
636 if (!FinishPropagation(callback)) return kUnsatTrailIndex;
637 DCHECK(PropagationIsDone());
638 return last_decision_or_backtrack_trail_index_;
639}
640
641bool SatSolver::FinishPropagation(std::optional<ConflictCallback> callback) {
642 if (model_is_unsat_) return false;
643 int num_loop = 0;
644 while (true) {
645 const int old_decision_level = trail_->CurrentDecisionLevel();
646 if (!Propagate()) {
647 ProcessCurrentConflict(callback);
648 if (model_is_unsat_) return false;
649 if (trail_->CurrentDecisionLevel() == old_decision_level) {
650 CHECK(!assumptions_.empty());
651 return false;
652 }
653
654 if (++num_loop % 16 == 0 && time_limit_->LimitReached()) {
655 // TODO(user): Exiting like this might cause issue since the propagation
656 // is not "finished" but some code might assume it is. However since we
657 // already might repropagate in the LP constraint, most of the code
658 // should support "not finished propagation".
659 return true;
660 }
661 continue;
662 }
663 break;
664 }
665 DCHECK(PropagationIsDone());
666 return true;
667}
668
670 if (model_is_unsat_) return false;
671 assumption_level_ = 0;
672 assumptions_.clear();
673 Backtrack(0);
674 return FinishPropagation();
675}
676
678 const std::vector<Literal>& assumptions) {
679 if (!ResetToLevelZero()) return false;
680 if (assumptions.empty()) return true;
681
682 // For assumptions and core-based search, it is really important to add as
683 // many binary clauses as possible. This is because we do not want to miss any
684 // early core of size 2.
686
687 DCHECK(assumptions_.empty());
688 assumption_level_ = 1;
689 assumptions_ = assumptions;
691}
692
693// Note that we do not count these as "branches" for a reporting purpose.
695 if (model_is_unsat_) return false;
696 if (CurrentDecisionLevel() >= assumption_level_) return true;
697
698 DCHECK(PropagationIsDone());
699 while (CurrentDecisionLevel() == 0 && !assumptions_.empty()) {
700 // When assumptions_ is not empty, the first "decision" actually contains
701 // multiple ones, and we should never use its literal.
702 CHECK_EQ(trail_->CurrentDecisionLevel(), 0);
703 last_decision_or_backtrack_trail_index_ = trail_->Index();
704
705 // We enqueue all assumptions at once at decision level 1.
706 int num_decisions = 0;
707 for (const Literal lit : assumptions_) {
708 if (Assignment().LiteralIsTrue(lit)) continue;
709 if (Assignment().LiteralIsFalse(lit)) {
710 // See GetLastIncompatibleDecisions().
711 *trail_->MutableConflict() = {lit.Negated(), lit};
712 return false;
713 }
714 ++num_decisions;
715 trail_->EnqueueAssumption(lit);
716 }
717
718 // Corner case: all assumptions are fixed at level zero, we ignore them.
719 if (num_decisions == 0) {
720 return ResetToLevelZero();
721 }
722
723 // Now that everything is enqueued, we propagate.
724 // This can backjump if we learn a unit clause, so we must loop.
725 if (!FinishPropagation()) return false;
726 if (CurrentDecisionLevel() > 0) return true;
727 }
728
729 DCHECK(PropagationIsDone());
730 DCHECK(assumptions_.empty());
731 const int64_t old_num_branches = counters_.num_branches;
732 const SatSolver::Status status = ReapplyDecisionsUpTo(assumption_level_ - 1);
733 counters_.num_branches = old_num_branches;
734 assumption_level_ = CurrentDecisionLevel();
735 return (status == SatSolver::FEASIBLE);
736}
737
739 std::optional<ConflictCallback> callback) {
740 SCOPED_TIME_STAT(&stats_);
741 if (model_is_unsat_) return;
742
743 const int conflict_trail_index = trail_->Index();
744
745 // A conflict occurred, compute a nice reason for this failure.
746 //
747 // If the trail as a registered "higher level conflict resolution", pick
748 // this one instead.
749 learned_conflict_.clear();
750 subsumed_clauses_.clear();
751 same_reason_identifier_.Clear();
752
753 subsuming_lrat_index_.clear();
754 subsuming_clauses_.clear();
755 subsuming_groups_.clear();
756 subsumed_clauses_.clear();
757
758 if (trail_->GetConflictResolutionFunction() == nullptr) {
759 const int max_trail_index = ComputeMaxTrailIndex(trail_->FailingClause());
760 if (!assumptions_.empty() && !trail_->FailingClause().empty()) {
761 // If the failing clause only contains literal at the assumptions level,
762 // we cannot use the ComputeFirstUIPConflict() code as we might have more
763 // than one decision.
764 //
765 // TODO(user): We might still want to "learn" the clause, especially if
766 // it reduces to only one literal in which case we can just fix it.
767 int highest_level = 0;
768 for (const Literal l : trail_->FailingClause()) {
769 highest_level =
770 std::max<int>(highest_level, trail_->Info(l.Variable()).level);
771 }
772 if (highest_level == assumption_level_) return;
773 }
774
775 ComputeFirstUIPConflict(max_trail_index, &learned_conflict_,
776 &reason_used_to_infer_the_conflict_);
777 } else {
778 trail_->GetConflictResolutionFunction()(&learned_conflict_,
779 &reason_used_to_infer_the_conflict_,
780 &subsumed_clauses_);
781 if (!assumptions_.empty() && !learned_conflict_.empty() &&
782 AssignmentLevel(learned_conflict_[0].Variable()) <= assumption_level_) {
783 // We have incompatible assumptions, store them there and return.
784 *trail_->MutableConflict() = learned_conflict_;
785 return;
786 }
787
788 CHECK(IsConflictValid(learned_conflict_));
789
790 // Recompute is_marked_.
791 is_marked_.ClearAndResize(num_variables_);
792 for (const Literal l : learned_conflict_) {
793 is_marked_.Set(l.Variable());
794 }
795 for (const Literal l : reason_used_to_infer_the_conflict_) {
796 is_marked_.Set(l.Variable());
797 }
798 }
799
800 DCHECK(IsConflictValid(learned_conflict_));
801 DCHECK(ClauseIsValidUnderDebugAssignment(learned_conflict_));
802
803 std::vector<ClauseId>* clause_ids_for_1iup = &tmp_clause_ids_for_1uip_;
804 if (lrat_proof_handler_ != nullptr) {
805 clause_ids_for_1iup->clear();
806 AppendLratProofFromReasons(reason_used_to_infer_the_conflict_,
807 clause_ids_for_1iup);
808 AppendLratProofForFailingClause(clause_ids_for_1iup);
809 }
810
811 // An empty conflict means that the problem is UNSAT.
812 if (learned_conflict_.empty()) {
813 ClauseId clause_id = kNoClauseId;
814 if (lrat_proof_handler_ != nullptr) {
815 clause_id = clause_id_generator_->GetNextId();
816 if (!lrat_proof_handler_->AddInferredClause(clause_id, learned_conflict_,
817 *clause_ids_for_1iup)) {
818 VLOG(1) << "WARNING: invalid LRAT inferred clause!";
819 }
820 }
821 if (callback.has_value()) {
822 (*callback)(clause_id, learned_conflict_);
823 }
824 return (void)SetModelUnsat();
825 }
826
827 // Update the activity of all the variables in the first UIP clause.
828 // Also update the activity of the last level variables expanded (and
829 // thus discarded) during the first UIP computation. Note that both
830 // sets are disjoint.
831 decision_policy_->BumpVariableActivities(learned_conflict_);
832 decision_policy_->BumpVariableActivities(reason_used_to_infer_the_conflict_);
833 if (parameters_->also_bump_variables_in_conflict_reasons()) {
834 ComputeUnionOfReasons(learned_conflict_, &extra_reason_literals_);
835 decision_policy_->BumpVariableActivities(extra_reason_literals_);
836 }
837
838 // Bump the clause activities.
839 //
840 // Note that the activity of the learned clause will be bumped too by
841 // AddLearnedClauseAndEnqueueUnitPropagation() after we update the increment.
842 if (trail_->FailingSatClause() != nullptr) {
843 BumpClauseActivity(trail_->FailingSatClause());
844 }
845 BumpReasonActivities(reason_used_to_infer_the_conflict_);
846 UpdateClauseActivityIncrement();
847
848 // Decay the activities.
849 decision_policy_->UpdateVariableActivityIncrement();
850 pb_constraints_->UpdateActivityIncrement();
851
852 // Hack from Glucose that seems to perform well.
853 const int period = parameters_->glucose_decay_increment_period();
854 const double max_decay = parameters_->glucose_max_decay();
855 if (counters_.num_failures % period == 0 &&
856 parameters_->variable_activity_decay() < max_decay) {
857 parameters_->set_variable_activity_decay(
858 parameters_->variable_activity_decay() +
859 parameters_->glucose_decay_increment());
860 }
861
862 // PB resolution.
863 // There is no point using this if the conflict and all the reasons involved
864 // in its resolution were clauses.
865 bool compute_pb_conflict = false;
866 if (parameters_->use_pb_resolution()) {
867 compute_pb_conflict = (pb_constraints_->ConflictingConstraint() != nullptr);
868 if (!compute_pb_conflict) {
869 for (Literal lit : reason_used_to_infer_the_conflict_) {
870 if (ReasonPbConstraintOrNull(lit.Variable()) != nullptr) {
871 compute_pb_conflict = true;
872 break;
873 }
874 }
875 }
876 }
877
878 // TODO(user): Note that we use the clause above to update the variable
879 // activities and not the pb conflict. Experiment.
880 if (compute_pb_conflict) {
881 const int max_trail_index = ComputeMaxTrailIndex(trail_->FailingClause());
882 pb_conflict_.ClearAndResize(num_variables_.value());
883 Coefficient initial_slack(-1);
884 if (pb_constraints_->ConflictingConstraint() == nullptr) {
885 // Generic clause case.
886 Coefficient num_literals(0);
887 for (Literal literal : trail_->FailingClause()) {
888 pb_conflict_.AddTerm(literal.Negated(), Coefficient(1.0));
889 ++num_literals;
890 }
891 pb_conflict_.AddToRhs(num_literals - 1);
892 } else {
893 // We have a pseudo-Boolean conflict, so we start from there.
894 pb_constraints_->ConflictingConstraint()->AddToConflict(&pb_conflict_);
895 pb_constraints_->ClearConflictingConstraint();
896 initial_slack =
897 pb_conflict_.ComputeSlackForTrailPrefix(*trail_, max_trail_index + 1);
898 }
899
900 int pb_backjump_level;
901 ComputePBConflict(max_trail_index, initial_slack, &pb_conflict_,
902 &pb_backjump_level);
903 if (pb_backjump_level == -1) return (void)SetModelUnsat();
904
905 // Convert the conflict into the vector<LiteralWithCoeff> form.
906 std::vector<LiteralWithCoeff> cst;
907 pb_conflict_.CopyIntoVector(&cst);
908 DCHECK(PBConstraintIsValidUnderDebugAssignment(cst, pb_conflict_.Rhs()));
909
910 // Check if the learned PB conflict is just a clause:
911 // all its coefficient must be 1, and the rhs must be its size minus 1.
912 bool conflict_is_a_clause = (pb_conflict_.Rhs() == cst.size() - 1);
913 if (conflict_is_a_clause) {
914 for (LiteralWithCoeff term : cst) {
915 if (term.coefficient != Coefficient(1)) {
916 conflict_is_a_clause = false;
917 break;
918 }
919 }
920 }
921
922 if (!conflict_is_a_clause) {
923 // Use the PB conflict.
924 DCHECK_GT(pb_constraints_->NumberOfConstraints(), 0);
925 CHECK_LT(pb_backjump_level, CurrentDecisionLevel());
926 Backtrack(pb_backjump_level);
927 CHECK(pb_constraints_->AddLearnedConstraint(cst, pb_conflict_.Rhs(),
928 trail_));
929 CHECK_GT(trail_->Index(), last_decision_or_backtrack_trail_index_);
930 counters_.num_learned_pb_literals += cst.size();
931 return;
932 }
933
934 // Continue with the normal clause flow, but use the PB conflict clause
935 // if it has a lower backjump level.
936 if (pb_backjump_level < ComputePropagationLevel(learned_conflict_)) {
937 subsumed_clauses_.clear(); // Because the conflict changes.
938 learned_conflict_.clear();
939 is_marked_.ClearAndResize(num_variables_);
940 int max_level = 0;
941 int max_index = 0;
942 for (LiteralWithCoeff term : cst) {
943 DCHECK(Assignment().LiteralIsTrue(term.literal));
944 DCHECK_EQ(term.coefficient, 1);
945 const int level = trail_->Info(term.literal.Variable()).level;
946 if (level == 0) continue;
947 if (level > max_level) {
948 max_level = level;
949 max_index = learned_conflict_.size();
950 }
951 learned_conflict_.push_back(term.literal.Negated());
952
953 // The minimization functions below expect the conflict to be marked!
954 // TODO(user): This is error prone, find a better way?
955 is_marked_.Set(term.literal.Variable());
956 }
957 CHECK(!learned_conflict_.empty());
958 std::swap(learned_conflict_.front(), learned_conflict_[max_index]);
959 DCHECK(IsConflictValid(learned_conflict_));
960 }
961 }
962
963 // Minimizing the conflict with binary clauses first has two advantages.
964 // First, there is no need to compute a reason for the variables eliminated
965 // this way. Second, more variables may be marked (in is_marked_) and
966 // MinimizeConflict() can take advantage of that. Because of this, the
967 // LBD of the learned conflict can change.
968 std::vector<ClauseId>* clause_ids_for_minimization =
969 &tmp_clause_ids_for_minimization_;
970 clause_ids_for_minimization->clear();
971 // This resizes internal data structures which can be used by
972 // MinimizeConflict() even if the binary implication graph is empty.
973 binary_implication_graph_->ClearImpliedLiterals();
974 DCHECK(ClauseIsValidUnderDebugAssignment(learned_conflict_));
975 if (!binary_implication_graph_->IsEmpty()) {
976 switch (parameters_->binary_minimization_algorithm()) {
978 break;
980 binary_implication_graph_->MinimizeConflictFirst(
981 *trail_, &learned_conflict_, &is_marked_,
982 clause_ids_for_minimization, /*also_use_decisions=*/false);
983 break;
985 binary_implication_graph_->MinimizeConflictFirst(
986 *trail_, &learned_conflict_, &is_marked_,
987 clause_ids_for_minimization, /*also_use_decisions=*/true);
988 break;
989 }
990 DCHECK(IsConflictValid(learned_conflict_));
991 }
992
993 // Minimize the learned conflict.
994 MinimizeConflict(&learned_conflict_, clause_ids_for_minimization);
995
996 // Note that we need to output the learned clause before cleaning the clause
997 // database. This is because we already backtracked and some of the clauses
998 // that were needed to infer the conflict may not be "reasons" anymore and
999 // may be deleted.
1000 ClauseId learned_conflict_clause_id = kNoClauseId;
1001 if (lrat_proof_handler_ != nullptr) {
1002 if (!clause_ids_for_minimization->empty()) {
1003 // Concatenate the minimized conflict proof with the learned conflict
1004 // proof, in this order, and remove duplicate clause IDs.
1005 // TODO(user): keep the duplicates and remove the corresponding check
1006 // in LratChecker?
1007 tmp_clause_id_set_.clear();
1008 tmp_clause_id_set_.insert(clause_ids_for_minimization->begin(),
1009 clause_ids_for_minimization->end());
1010 clause_ids_for_minimization->reserve(clause_ids_for_minimization->size() +
1011 clause_ids_for_1iup->size());
1012 for (const ClauseId clause_id : *clause_ids_for_1iup) {
1013 if (!tmp_clause_id_set_.contains(clause_id)) {
1014 clause_ids_for_minimization->push_back(clause_id);
1015 }
1016 }
1017 clause_ids_for_1iup = clause_ids_for_minimization;
1018 }
1019 learned_conflict_clause_id = clause_id_generator_->GetNextId();
1020 if (!lrat_proof_handler_->AddInferredClause(learned_conflict_clause_id,
1021 learned_conflict_,
1022 *clause_ids_for_1iup)) {
1023 VLOG(1) << "WARNING: invalid LRAT inferred clause!";
1024 }
1025 }
1026 if (callback.has_value()) {
1027 (*callback)(learned_conflict_clause_id, learned_conflict_);
1028 }
1029
1030 // We notify the decision before backtracking so that we can save the phase.
1031 // The current heuristic is to try to take a trail prefix for which there is
1032 // currently no conflict (hence just before the last decision was taken).
1033 //
1034 // TODO(user): It is unclear what the best heuristic is here. Both the current
1035 // trail index or the trail before the current decision perform well, but
1036 // using the full trail seems slightly better even though it will contain the
1037 // current conflicting literal.
1038 decision_policy_->BeforeConflict(trail_->Index());
1039
1040 // Note that this should happen after the new_conflict "proof", but before
1041 // we backtrack and add the new conflict to the clause_propagator_.
1042 const auto [is_redundant, min_lbd_of_subsumed_clauses] =
1043 SubsumptionsInConflictResolution(learned_conflict_clause_id,
1044 learned_conflict_,
1045 reason_used_to_infer_the_conflict_);
1046
1047 // Backtrack and add the reason to the set of learned clause.
1048 counters_.num_literals_learned += learned_conflict_.size();
1049 const int conflict_level =
1050 trail_->Info(learned_conflict_[0].Variable()).level;
1051 const int backjump_levels = CurrentDecisionLevel() - conflict_level;
1052 const bool should_backjump =
1053 !trail_->ChronologicalBacktrackingEnabled() ||
1054 (num_failures() > parameters_->chronological_backtrack_min_conflicts() &&
1055 backjump_levels > parameters_->max_backjump_levels());
1056 const int backtrack_level = should_backjump
1057 ? ComputePropagationLevel(learned_conflict_)
1058 : std::max(0, conflict_level - 1);
1059 Backtrack(backtrack_level);
1060 DCHECK(ClauseIsValidUnderDebugAssignment(learned_conflict_));
1061
1062 // Add the conflict here, so we process all "newly learned" clause in the
1063 // same way.
1064 learned_clauses_.push_back({learned_conflict_clause_id, is_redundant,
1065 min_lbd_of_subsumed_clauses,
1066 std::move(learned_conflict_)});
1067
1068 // Preprocess the new clauses.
1069 // We might need to backtrack further !
1070 for (auto& [id, is_redundant, min_lbd, clause] : learned_clauses_) {
1071 if (clause.empty()) return (void)SetModelUnsat();
1072
1073 // Make sure each clause is "canonicalized" with respect to equivalent
1074 // literals.
1075 //
1076 // TODO(user): Maybe we should do that on each reason before we use them in
1077 // conflict analysis/minimization, but it might be a bit costly.
1078 bool some_change = false;
1079 tmp_clause_ids_.clear();
1080 for (Literal& lit : clause) {
1081 const Literal rep = binary_implication_graph_->RepresentativeOf(lit);
1082 if (rep != lit) {
1083 some_change = true;
1084 if (lrat_proof_handler_ != nullptr) {
1085 // We need not(rep) => not(lit) for the proof.
1086 tmp_clause_ids_.push_back(
1087 binary_implication_graph_->GetClauseId(lit.Negated(), rep));
1088 CHECK_NE(tmp_clause_ids_.back(), kNoClauseId) << lit << " " << rep;
1089 }
1090 lit = rep;
1091 }
1092 }
1093 if (some_change) {
1095
1096 // This shouldn't happen since it is a new learned clause, otherwise
1097 // something is wrong.
1098 for (int i = 1; i < clause.size(); ++i) {
1099 CHECK_NE(clause[i], clause[i - 1].Negated()) << "trivial new clause?";
1100 }
1101
1102 if (lrat_proof_handler_ != nullptr) {
1103 // We need a new clause id for the canonicalized version, and the proof
1104 // for how we derived that canonicalization.
1105 const ClauseId new_id = clause_id_generator_->GetNextId();
1106 tmp_clause_ids_.push_back(id);
1107 lrat_proof_handler_->AddInferredClause(new_id, clause, tmp_clause_ids_);
1108 id = new_id;
1109 }
1110 }
1111
1112 // Tricky: in case of propagation not at the right level we might need to
1113 // backjump further.
1114 int num_false = 0;
1115 for (const Literal l : clause) {
1116 if (Assignment().LiteralIsFalse(l)) ++num_false;
1117 }
1118 if (num_false == clause.size() || clause.size() == 1) {
1119 int max_level = 0;
1120 for (const Literal l : clause) {
1121 const int level = AssignmentLevel(l.Variable());
1122 max_level = std::max(max_level, level);
1123 }
1124 int propag_level = 0;
1125 for (const Literal l : clause) {
1126 const int level = AssignmentLevel(l.Variable());
1127 if (level < max_level) {
1128 propag_level = std::max(propag_level, level);
1129 }
1130 }
1131 Backtrack(propag_level);
1132 }
1133 }
1134
1135 // Learn the new clauses.
1136 int best_lbd = std::numeric_limits<int>::max();
1137 for (const auto& [id, is_redundant, min_lbd, clause] : learned_clauses_) {
1138 DCHECK((lrat_proof_handler_ == nullptr) || (id != kNoClauseId));
1139 const int lbd = AddLearnedClauseAndEnqueueUnitPropagation(
1140 id, clause, is_redundant, min_lbd);
1141 best_lbd = std::min(best_lbd, lbd);
1142 }
1143 restart_->OnConflict(conflict_trail_index, conflict_level, best_lbd);
1144}
1145
1146namespace {
1147
1148// Returns true iff 'b' is subsumed by 'a' (i.e 'a' is included in 'b').
1149// This is slow and only meant to be used in DCHECKs.
1150bool ClauseSubsumption(absl::Span<const Literal> a, SatClause* b) {
1151 std::vector<Literal> superset(b->begin(), b->end());
1152 std::vector<Literal> subset(a.begin(), a.end());
1153 std::sort(superset.begin(), superset.end());
1154 std::sort(subset.begin(), subset.end());
1155 return std::includes(superset.begin(), superset.end(), subset.begin(),
1156 subset.end());
1157}
1158
1159} // namespace
1160
1161std::pair<bool, int> SatSolver::SubsumptionsInConflictResolution(
1162 ClauseId learned_conflict_id, absl::Span<const Literal> conflict,
1163 absl::Span<const Literal> reason_used) {
1164 CHECK_NE(CurrentDecisionLevel(), 0);
1165 learned_clauses_.clear();
1166
1167 // This is used to see if the learned conflict subsumes some clauses.
1168 // Note that conflict is not yet in the clauses_propagator_.
1169 tmp_literal_set_.Resize(Literal(num_variables_, true).Index());
1170 for (const Literal l : conflict) tmp_literal_set_.Set(l);
1171 const auto in_conflict = tmp_literal_set_.const_view();
1172
1173 // This is used to see if the set of decision that implies the conflict
1174 // (further resolution) subsumes some clauses.
1175 //
1176 // TODO(user): Also consider the ALL UIP conflict ? I know other solver do
1177 // learn this version sometimes, or anything in-between. See the concept of
1178 // conflict "shrinking" in the literature.
1179 std::vector<SatClause*> subsumed_by_decisions;
1180 bool decision_is_redundant = true;
1181 int decision_min_lbd = std::numeric_limits<int>::max();
1182 int decisions_clause_size = 0;
1183 if (assumption_level_ == 0 &&
1185 if (/* DISABLES CODE */ (false)) {
1186 // This is shorter but more costly... Note that if any subsumption occur,
1187 // this is the one we will use.
1188 for (const Literal l : GetDecisionsFixing(conflict)) {
1189 ++decisions_clause_size;
1190 tmp_decision_set_.Set(l.Negated());
1191 }
1192 } else {
1193 // Add all the decision up to max_non_decision_level + the one after that
1194 // from the conflict.
1195 tmp_decision_set_.Resize(Literal(num_variables_, true).Index());
1196 int max_non_decision_level = 0;
1197 for (const Literal l : conflict) {
1198 const auto& info = trail_->Info(l.Variable());
1200 max_non_decision_level =
1201 std::max<int>(max_non_decision_level, info.level);
1202 }
1203 }
1204
1205 for (int i = 0; i < max_non_decision_level; ++i) {
1206 // To act like conflict.
1207 const Literal l = Decisions()[i].literal.Negated();
1208 ++decisions_clause_size;
1209 tmp_decision_set_.Set(l);
1210 }
1211 for (const Literal l : conflict) {
1212 const auto& info = trail_->Info(l.Variable());
1214 !tmp_decision_set_[l]) {
1215 ++decisions_clause_size;
1216 tmp_decision_set_.Set(l);
1217 }
1218 }
1219 }
1220 }
1221
1222 // Deal with subsuming_groups_.
1223 // We need to infer the intermediary clause before we subsume them.
1224 //
1225 // TODO(user): We can use the intermediary step to shorten the conflict proof.
1226 ClauseId last_clause_id = kNoClauseId;
1227 int reason_index = 0;
1228 for (int i = 0; i < subsuming_groups_.size(); ++i) {
1229 // If the conflict subsume subsumed_clauses_[i], it will subsume all
1230 // the other clause too, so that will be covered below, and we don't need
1231 // to create that intermediary at all.
1232 const int limit = subsuming_clauses_[i].size() - conflict.size();
1233 int missing = 0;
1234 for (const Literal l : subsuming_clauses_[i]) {
1235 if (!in_conflict[l]) {
1236 ++missing;
1237 if (missing > limit) break;
1238 }
1239 }
1240
1241 // Intermediary conflict is sumbsumed, skip.
1242 if (missing <= limit) continue;
1243
1244 // Intermediary proof to reach this step in the conflict resolution.
1245 ClauseId new_id = kNoClauseId;
1246 if (lrat_proof_handler_ != nullptr) {
1247 tmp_clause_ids_.clear();
1248 is_marked_.ClearAndResize(num_variables_); // Make sure not used anymore
1249
1250 AppendLratProofFromReasons(
1251 reason_used.subspan(reason_index,
1252 subsuming_lrat_index_[i] - reason_index),
1253 &tmp_clause_ids_);
1254 if (last_clause_id == kNoClauseId) {
1255 AppendLratProofForFailingClause(&tmp_clause_ids_);
1256 } else {
1257 tmp_clause_ids_.push_back(last_clause_id);
1258 }
1259
1260 new_id = clause_id_generator_->GetNextId();
1261 last_clause_id = new_id;
1262 reason_index = subsuming_lrat_index_[i];
1263 lrat_proof_handler_->AddInferredClause(new_id, subsuming_clauses_[i],
1264 tmp_clause_ids_);
1265 }
1266
1267 // Then this clause subsumes all entry in the group.
1268 bool new_clause_is_redundant = true;
1269 int new_clause_min_lbd = std::numeric_limits<int>::max();
1270 for (SatClause* clause : subsuming_groups_[i]) {
1271 CHECK_NE(clause->size(), 0); // Not subsumed yet.
1272 if (clauses_propagator_->IsRemovable(clause)) {
1273 new_clause_min_lbd =
1274 std::min(new_clause_min_lbd,
1275 clauses_propagator_->LbdOrZeroIfNotRemovable(clause));
1276 } else {
1277 new_clause_is_redundant = false;
1278 }
1279 DCHECK(ClauseSubsumption(subsuming_clauses_[i], clause));
1280 clauses_propagator_->LazyDelete(
1282 }
1283
1284 // We can only add them after backtracking, since these are currently
1285 // conflict.
1286 learned_clauses_.push_back(
1287 {new_id, new_clause_is_redundant, new_clause_min_lbd,
1288 std::vector<Literal>(subsuming_clauses_[i].begin(),
1289 subsuming_clauses_[i].end())});
1290 }
1291
1292 bool is_redundant = true;
1293 int min_lbd_of_subsumed_clauses = std::numeric_limits<int>::max();
1294 const auto in_decision = tmp_decision_set_.const_view();
1295 const auto maybe_subsume = [&is_redundant, &min_lbd_of_subsumed_clauses,
1296 in_conflict, conflict, in_decision,
1297 decisions_clause_size, &subsumed_by_decisions,
1298 &decision_is_redundant, &decision_min_lbd,
1299 this](SatClause* clause,
1300 DeletionSourceForStat source) {
1301 if (clause == nullptr || clause->empty()) return;
1302
1303 if (IsStrictlyIncluded(in_conflict, conflict.size(), clause->AsSpan())) {
1304 ++counters_.num_subsumed_clauses;
1305 DCHECK(ClauseSubsumption(conflict, clause));
1306 if (clauses_propagator_->IsRemovable(clause)) {
1307 min_lbd_of_subsumed_clauses =
1308 std::min(min_lbd_of_subsumed_clauses,
1309 clauses_propagator_->LbdOrZeroIfNotRemovable(clause));
1310 } else {
1311 is_redundant = false;
1312 }
1313 clauses_propagator_->LazyDelete(clause, source);
1314 return;
1315 }
1316
1317 if (decisions_clause_size > 0 &&
1318 IsStrictlyIncluded(in_decision, decisions_clause_size,
1319 clause->AsSpan())) {
1320 if (clauses_propagator_->IsRemovable(clause)) {
1321 decision_min_lbd =
1322 std::min(decision_min_lbd,
1323 clauses_propagator_->LbdOrZeroIfNotRemovable(clause));
1324 } else {
1325 decision_is_redundant = false;
1326 }
1327 subsumed_by_decisions.push_back(clause);
1328 }
1329 };
1330
1331 // This is faster than conflict analysis, and stronger than the old assumption
1332 // mecanism we had. This is because once the conflict is minimized, we might
1333 // have more subsumptions than the one found during conflict analysis.
1334 //
1335 // Note however that we migth still have subsumption using the intermediate
1336 // conflict. See ComputeFirstUIPConflict().
1337 if (parameters_->subsumption_during_conflict_analysis()) {
1338 for (const Literal l : reason_used) {
1339 // Tricky: these clause might habe been deleted by the subsumption above.
1340 // So ReasonClauseOrNull() must handle that case.
1341 maybe_subsume(clauses_propagator_->ReasonClauseOrNull(l.Variable()),
1343 }
1344 }
1345
1346 if (parameters_->eagerly_subsume_last_n_conflicts() > 0) {
1347 for (SatClause* clause : clauses_propagator_->LastNClauses(
1348 parameters_->eagerly_subsume_last_n_conflicts())) {
1349 maybe_subsume(clause, DeletionSourceForStat::SUBSUMPTION_EAGER);
1350 }
1351 }
1352
1353 if (!subsumed_by_decisions.empty()) {
1354 // This one should always be a subset of the one we tried.
1355 std::vector<Literal> decision_clause;
1356 for (const Literal l : GetDecisionsFixing(conflict)) {
1357 DCHECK(in_decision[l.Negated()]);
1358 decision_clause.push_back(l.Negated());
1359 }
1360
1361 // Construct the proof.
1362 ClauseId new_clause_id = kNoClauseId;
1363 if (lrat_proof_handler_ != nullptr) {
1364 tmp_clause_ids_.clear();
1365 clauses_propagator_->AppendClauseIdsFixing(conflict, &tmp_clause_ids_);
1366 tmp_clause_ids_.push_back(learned_conflict_id);
1367
1368 new_clause_id = clause_id_generator_->GetNextId();
1369 lrat_proof_handler_->AddInferredClause(new_clause_id, decision_clause,
1370 tmp_clause_ids_);
1371 }
1372
1373 // Remove subsumed clause.
1374 for (SatClause* clause : subsumed_by_decisions) {
1375 if (clause->empty()) continue;
1376 DCHECK(ClauseSubsumption(decision_clause, clause));
1377 clauses_propagator_->LazyDelete(
1379 }
1380
1381 // Also learn the "decision" conflict.
1382 learned_clauses_.push_back({new_clause_id, decision_is_redundant,
1383 decision_min_lbd, decision_clause});
1384 }
1385
1386 // Sparse clear.
1387 for (const Literal l : conflict) tmp_literal_set_.Clear(l);
1388 if (decisions_clause_size > 0) {
1389 for (int i = 0; i < CurrentDecisionLevel(); ++i) {
1390 tmp_decision_set_.Clear(Decisions()[i].literal.Negated());
1391 }
1392 }
1393
1394 clauses_propagator_->CleanUpWatchers();
1395 return {is_redundant, min_lbd_of_subsumed_clauses};
1396}
1397
1398void SatSolver::AppendLratProofForFixedLiterals(
1399 absl::Span<const Literal> literals, std::vector<ClauseId>* clause_ids) {
1400 for (const Literal literal : literals) {
1401 const BooleanVariable var = literal.Variable();
1402 if (!is_marked_[var] && trail_->AssignmentLevel(literal) == 0) {
1403 is_marked_.Set(var);
1404 clause_ids->push_back(trail_->GetUnitClauseId(var));
1405 DCHECK_NE(clause_ids->back(), kNoClauseId);
1406 }
1407 }
1408}
1409
1410void SatSolver::AppendLratProofForFailingClause(
1411 std::vector<ClauseId>* clause_ids) {
1412 // Add all the non-yet marked unit-clause.
1413 AppendLratProofForFixedLiterals(trail_->FailingClause(), clause_ids);
1414
1415 // Add the failing SAT clause.
1416 ClauseId failing_clause_id = kNoClauseId;
1417 const SatClause* failing_sat_clause = trail_->FailingSatClause();
1418 if (failing_sat_clause != nullptr) {
1419 failing_clause_id = clauses_propagator_->GetClauseId(failing_sat_clause);
1420 } else if (trail_->FailingClauseId() != kNoClauseId) {
1421 failing_clause_id = trail_->FailingClauseId();
1422 } else {
1423 absl::Span<const Literal> failing_clause = trail_->FailingClause();
1424 if (failing_clause.size() == 2) {
1425 failing_clause_id = binary_implication_graph_->GetClauseId(
1426 failing_clause[0], failing_clause[1]);
1427 }
1428 }
1429 if (failing_clause_id == kNoClauseId) {
1430 failing_clause_id = clause_id_generator_->GetNextId();
1431 lrat_proof_handler_->AddAssumedClause(failing_clause_id,
1432 trail_->FailingClause());
1433 }
1434 clause_ids->push_back(failing_clause_id);
1435}
1436
1437void SatSolver::AppendLratProofFromReasons(absl::Span<const Literal> reasons,
1438 std::vector<ClauseId>* clause_ids) {
1439 // First add all the unit clauses used in the reasons to infer the conflict.
1440 // They can be added in any order since they don't depend on each other.
1441 for (const Literal literal : reasons) {
1442 DCHECK_NE(trail_->AssignmentLevel(literal), 0);
1443 AppendLratProofForFixedLiterals(trail_->Reason(literal.Variable()),
1444 clause_ids);
1445 }
1446
1447 // Then add the clauses which become unit when all the unit clauses above and
1448 // all the literals in learned_conflict_ are assumed to be false, in unit
1449 // propagation order.
1450 for (int i = reasons.size() - 1; i >= 0; --i) {
1451 const Literal literal = reasons[i];
1452 ClauseId clause_id = clauses_propagator_->ReasonClauseId(literal);
1453 if (clause_id == kNoClauseId) {
1454 clause_id = clause_id_generator_->GetNextId();
1455 DCHECK_NE(trail_->AssignmentLevel(literal), 0);
1456 lrat_proof_handler_->AddAssumedClause(clause_id,
1457 trail_->Reason(literal.Variable()));
1458 }
1459 clause_ids->push_back(clause_id);
1460 }
1461}
1462
1463SatSolver::Status SatSolver::ReapplyDecisionsUpTo(
1464 int max_level, int* first_propagation_index) {
1465 SCOPED_TIME_STAT(&stats_);
1466 DCHECK(assumptions_.empty());
1467 int decision_index = trail_->CurrentDecisionLevel();
1468 const auto& decisions = trail_->Decisions();
1469 while (decision_index <= max_level) {
1470 DCHECK_GE(decision_index, trail_->CurrentDecisionLevel());
1471 const Literal previous_decision = decisions[decision_index].literal;
1472 ++decision_index;
1473 if (Assignment().LiteralIsTrue(previous_decision)) {
1474 // Note that this particular position in decisions will be overridden,
1475 // but that is fine since this is a consequence of the previous decision,
1476 // so we will never need to take it into account again.
1477 continue;
1478 }
1479 if (Assignment().LiteralIsFalse(previous_decision)) {
1480 // See GetLastIncompatibleDecisions().
1481 *trail_->MutableConflict() = {previous_decision.Negated(),
1482 previous_decision};
1483 return ASSUMPTIONS_UNSAT;
1484 }
1485
1486 // Not assigned, we try to take it.
1487 const int old_level = trail_->CurrentDecisionLevel();
1488 const int index = EnqueueDecisionAndBackjumpOnConflict(previous_decision);
1489 if (first_propagation_index != nullptr) {
1490 *first_propagation_index = std::min(*first_propagation_index, index);
1491 }
1492 if (index == kUnsatTrailIndex) return INFEASIBLE;
1493 if (trail_->CurrentDecisionLevel() <= old_level) {
1494 // A conflict occurred which backjumped to an earlier decision level.
1495 // We potentially backjumped over some valid decisions, so we need to
1496 // continue the loop and try to re-enqueue them.
1497 //
1498 // Note that there is no need to update max_level, because when we will
1499 // try to reapply the current "previous_decision" it will result in a
1500 // conflict. IMPORTANT: we can't actually optimize this and abort the loop
1501 // earlier though, because we need to check that it is conflicting because
1502 // it is already propagated to false. There is no guarantee of this
1503 // because we learn the first-UIP conflict. If it is not the case, we will
1504 // then learn a new conflict, backjump, and continue the loop.
1505 decision_index = trail_->CurrentDecisionLevel();
1506 }
1507 }
1508 return FEASIBLE;
1509}
1510
1512 Literal true_literal, int* first_propagation_index) {
1513 SCOPED_TIME_STAT(&stats_);
1514 CHECK(PropagationIsDone());
1515 CHECK(assumptions_.empty());
1516 if (model_is_unsat_) return SatSolver::INFEASIBLE;
1517 DCHECK_LT(CurrentDecisionLevel(), trail_->Decisions().size());
1518 trail_->OverrideDecision(CurrentDecisionLevel(), true_literal);
1519 if (first_propagation_index != nullptr) {
1520 *first_propagation_index = trail_->Index();
1521 }
1522 return ReapplyDecisionsUpTo(CurrentDecisionLevel(), first_propagation_index);
1523}
1524
1526 SCOPED_TIME_STAT(&stats_);
1527 if (model_is_unsat_) return false;
1528 DCHECK(PropagationIsDone());
1529
1530 const int current_level = CurrentDecisionLevel();
1531 EnqueueNewDecision(true_literal);
1532 if (Propagate()) {
1533 return true;
1534 } else {
1535 Backtrack(current_level);
1536 return false;
1537 }
1538}
1539
1540void SatSolver::Backtrack(int target_level) {
1541 SCOPED_TIME_STAT(&stats_);
1542 // TODO(user): The backtrack method should not be called when the model is
1543 // unsat. Add a DCHECK to prevent that, but before fix the
1544 // bop::BopOptimizerBase architecture.
1545
1546 // Do nothing if the CurrentDecisionLevel() is already correct.
1547 // This is needed, otherwise target_trail_index below will remain at zero and
1548 // that will cause some problems. Note that we could forbid a user to call
1549 // Backtrack() with the current level, but that is annoying when you just
1550 // want to reset the solver with Backtrack(0).
1551 DCHECK_GE(target_level, 0);
1552 DCHECK_LE(target_level, CurrentDecisionLevel());
1553 if (CurrentDecisionLevel() == target_level) return;
1554
1555 // Any backtrack to the root from a positive one is counted as a restart.
1556 counters_.num_backtracks++;
1557 if (target_level == 0) {
1558 counters_.num_backtracks_to_root++;
1559 }
1560
1561 // Per the SatPropagator interface, this is needed before calling Untrail.
1562 const int target_trail_index = trail_->PrepareBacktrack(target_level);
1563 DCHECK_LT(target_trail_index, trail_->Index());
1564 for (SatPropagator* propagator : propagators_) {
1565 if (propagator->IsEmpty()) continue;
1566 propagator->Untrail(*trail_, target_trail_index);
1567 }
1568 decision_policy_->Untrail(target_trail_index);
1569 trail_->Untrail(target_trail_index);
1570
1571 last_decision_or_backtrack_trail_index_ = trail_->Index();
1572}
1573
1574bool SatSolver::AddBinaryClauses(absl::Span<const BinaryClause> clauses) {
1575 SCOPED_TIME_STAT(&stats_);
1576 CHECK_EQ(CurrentDecisionLevel(), 0);
1577 for (const BinaryClause c : clauses) {
1578 if (!AddBinaryClause(c.a, c.b)) return false;
1579 }
1580 if (!Propagate()) return SetModelUnsat();
1581 return true;
1582}
1583
1584const std::vector<BinaryClause>& SatSolver::NewlyAddedBinaryClauses() {
1585 return binary_clauses_.newly_added();
1586}
1587
1589 binary_clauses_.ClearNewlyAdded();
1590}
1591
1592namespace {
1593// Return the next value that is a multiple of interval.
1594int64_t NextMultipleOf(int64_t value, int64_t interval) {
1595 return interval * (1 + value / interval);
1596}
1597} // namespace
1598
1600 const std::vector<Literal>& assumptions, int64_t max_number_of_conflicts) {
1601 SCOPED_TIME_STAT(&stats_);
1602 if (!ResetWithGivenAssumptions(assumptions)) return UnsatStatus();
1603 DCHECK(PropagationIsDone());
1604 return SolveInternal(time_limit_,
1605 max_number_of_conflicts >= 0
1606 ? max_number_of_conflicts
1607 : parameters_->max_number_of_conflicts());
1608}
1609
1610SatSolver::Status SatSolver::StatusWithLog(Status status) {
1611 SOLVER_LOG(logger_, RunningStatisticsString());
1612 SOLVER_LOG(logger_, StatusString(status));
1613 return status;
1614}
1615
1616void SatSolver::SetAssumptionLevel(int assumption_level) {
1617 CHECK_GE(assumption_level, 0);
1618 CHECK_LE(assumption_level, CurrentDecisionLevel());
1619 assumption_level_ = assumption_level;
1620
1621 // New assumption code.
1622 if (!assumptions_.empty()) {
1623 CHECK_EQ(assumption_level, 0);
1624 assumptions_.clear();
1625 }
1626}
1627
1629 return SolveInternal(time_limit == nullptr ? time_limit_ : time_limit,
1630 parameters_->max_number_of_conflicts());
1631}
1632
1634 return SolveInternal(time_limit_, parameters_->max_number_of_conflicts());
1635}
1636
1637SatSolver::Status SatSolver::SolveInternal(TimeLimit* time_limit,
1638 int64_t max_number_of_conflicts) {
1639 SCOPED_TIME_STAT(&stats_);
1640 if (model_is_unsat_) return INFEASIBLE;
1641
1642 // TODO(user): Because the counter are not reset to zero, this cause the
1643 // metrics / sec to be completely broken except when the solver is used
1644 // for exactly one Solve().
1645 timer_.Restart();
1646
1647 // Display initial statistics.
1648 if (logger_->LoggingIsEnabled()) {
1649 SOLVER_LOG(logger_, "Initial memory usage: ", MemoryUsage());
1650 SOLVER_LOG(logger_, "Number of variables: ", num_variables_.value());
1651 SOLVER_LOG(logger_, "Number of clauses (size > 2): ",
1652 clauses_propagator_->num_clauses());
1653 SOLVER_LOG(logger_, "Number of binary clauses: ",
1654 binary_implication_graph_->ComputeNumImplicationsForLog());
1655 SOLVER_LOG(logger_, "Number of linear constraints: ",
1656 pb_constraints_->NumberOfConstraints());
1657 SOLVER_LOG(logger_, "Number of fixed variables: ", trail_->Index());
1658 SOLVER_LOG(logger_, "Number of watched clauses: ",
1659 clauses_propagator_->num_watched_clauses());
1660 SOLVER_LOG(logger_, "Parameters: ", ProtobufShortDebugString(*parameters_));
1661 }
1662
1663 // Variables used to show the search progress.
1664 const int64_t kDisplayFrequency = 10000;
1665 int64_t next_display = parameters_->log_search_progress()
1666 ? NextMultipleOf(num_failures(), kDisplayFrequency)
1667 : std::numeric_limits<int64_t>::max();
1668
1669 // Variables used to check the memory limit every kMemoryCheckFrequency.
1670 const int64_t kMemoryCheckFrequency = 10000;
1671 int64_t next_memory_check =
1672 NextMultipleOf(num_failures(), kMemoryCheckFrequency);
1673
1674 // The max_number_of_conflicts is per solve but the counter is for the whole
1675 // solver.
1676 const int64_t kFailureLimit =
1677 max_number_of_conflicts == std::numeric_limits<int64_t>::max()
1678 ? std::numeric_limits<int64_t>::max()
1679 : counters_.num_failures + max_number_of_conflicts;
1680
1681 // Starts search.
1682 for (;;) {
1683 // Test if a limit is reached.
1684 if (time_limit != nullptr) {
1685 AdvanceDeterministicTime(time_limit);
1686 if (time_limit->LimitReached()) {
1687 SOLVER_LOG(logger_, "The time limit has been reached. Aborting.");
1688 return StatusWithLog(LIMIT_REACHED);
1689 }
1690 }
1691 if (num_failures() >= kFailureLimit) {
1692 SOLVER_LOG(logger_, "The conflict limit has been reached. Aborting.");
1693 return StatusWithLog(LIMIT_REACHED);
1694 }
1695
1696 // The current memory checking takes time, so we only execute it every
1697 // kMemoryCheckFrequency conflict. We use >= because counters_.num_failures
1698 // may augment by more than one at each iteration.
1699 //
1700 // TODO(user): Find a better way.
1701 if (counters_.num_failures >= next_memory_check) {
1702 next_memory_check = NextMultipleOf(num_failures(), kMemoryCheckFrequency);
1703 if (IsMemoryLimitReached()) {
1704 SOLVER_LOG(logger_, "The memory limit has been reached. Aborting.");
1705 return StatusWithLog(LIMIT_REACHED);
1706 }
1707 }
1708
1709 // Display search progression. We use >= because counters_.num_failures may
1710 // augment by more than one at each iteration.
1711 if (counters_.num_failures >= next_display) {
1712 SOLVER_LOG(logger_, RunningStatisticsString());
1713 next_display = NextMultipleOf(num_failures(), kDisplayFrequency);
1714 }
1715
1716 const int old_level = trail_->CurrentDecisionLevel();
1717 if (!Propagate()) {
1718 // A conflict occurred, continue the loop.
1720 if (model_is_unsat_) return StatusWithLog(INFEASIBLE);
1721 if (old_level == trail_->CurrentDecisionLevel()) {
1722 CHECK(!assumptions_.empty());
1723 return StatusWithLog(ASSUMPTIONS_UNSAT);
1724 }
1725 } else {
1726 // We need to reapply any assumptions that are not currently applied.
1727 if (!ReapplyAssumptionsIfNeeded()) return StatusWithLog(UnsatStatus());
1728
1729 // At a leaf?
1730 if (trail_->Index() == num_variables_.value()) {
1731 return StatusWithLog(FEASIBLE);
1732 }
1733
1734 if (restart_->ShouldRestart()) {
1735 ++counters_.num_restarts;
1736 if (!BacktrackAndPropagateReimplications(assumption_level_)) {
1737 return StatusWithLog(INFEASIBLE);
1738 }
1739 }
1740
1741 DCHECK_GE(CurrentDecisionLevel(), assumption_level_);
1742 EnqueueNewDecision(decision_policy_->NextBranch());
1743 }
1744 }
1745}
1746
1748 std::vector<Literal>* clause = trail_->MutableConflict();
1749 int num_true = 0;
1750 for (int i = 0; i < clause->size(); ++i) {
1751 const Literal literal = (*clause)[i];
1752 if (Assignment().LiteralIsTrue(literal)) {
1753 // literal at true in the conflict must be the last decision/assumption
1754 // that could not be taken. Put it at the front to add to the result
1755 // later.
1756 std::swap((*clause)[i], (*clause)[num_true++]);
1757 }
1758 }
1759 CHECK_LE(num_true, 1);
1760 std::vector<Literal> result =
1761 GetDecisionsFixing(absl::MakeConstSpan(*clause).subspan(num_true));
1762 for (int i = 0; i < num_true; ++i) {
1763 result.push_back((*clause)[i].Negated());
1764 }
1765 return result;
1766}
1767
1769 absl::Span<const Literal> literals) {
1770 SCOPED_TIME_STAT(&stats_);
1771 std::vector<Literal> unsat_assumptions;
1772
1773 tmp_mark_.ClearAndResize(num_variables_);
1774
1775 int trail_index = 0;
1776 for (const Literal lit : literals) {
1777 CHECK(Assignment().LiteralIsAssigned(lit));
1778 trail_index =
1779 std::max(trail_index, trail_->Info(lit.Variable()).trail_index);
1780 tmp_mark_.Set(lit.Variable());
1781 }
1782
1783 // We just expand the reasons recursively until we only have decisions.
1784 const auto& decisions = trail_->Decisions();
1785 const int limit =
1786 CurrentDecisionLevel() > 0 ? decisions[0].trail_index : trail_->Index();
1787 CHECK_LT(trail_index, trail_->Index());
1788 while (true) {
1789 // Find next marked literal to expand from the trail.
1790 while (trail_index >= limit &&
1791 !tmp_mark_[(*trail_)[trail_index].Variable()]) {
1792 --trail_index;
1793 }
1794 if (trail_index < limit) break;
1795 const Literal marked_literal = (*trail_)[trail_index];
1796 --trail_index;
1797
1798 if (trail_->AssignmentType(marked_literal.Variable()) ==
1800 unsat_assumptions.push_back(marked_literal);
1801 } else {
1802 // Marks all the literals of its reason.
1803 for (const Literal literal : trail_->Reason(marked_literal.Variable())) {
1804 const BooleanVariable var = literal.Variable();
1805 const int level = AssignmentLevel(var);
1806 if (level > 0 && !tmp_mark_[var]) tmp_mark_.Set(var);
1807 }
1808 }
1809 }
1810
1811 // We reverse the assumptions so they are in the same order as the one in
1812 // which the decision were made.
1813 std::reverse(unsat_assumptions.begin(), unsat_assumptions.end());
1814 return unsat_assumptions;
1815}
1816
1817void SatSolver::BumpReasonActivities(absl::Span<const Literal> literals) {
1818 SCOPED_TIME_STAT(&stats_);
1819 for (const Literal literal : literals) {
1820 const BooleanVariable var = literal.Variable();
1821 if (AssignmentLevel(var) > 0) {
1822 SatClause* clause = clauses_propagator_->ReasonClauseOrNull(var);
1823 if (clause != nullptr) {
1824 BumpClauseActivity(clause);
1825 } else {
1826 UpperBoundedLinearConstraint* pb_constraint =
1827 ReasonPbConstraintOrNull(var);
1828 if (pb_constraint != nullptr) {
1829 // TODO(user): Because one pb constraint may propagate many literals,
1830 // this may bias the constraint activity... investigate other policy.
1831 pb_constraints_->BumpActivity(pb_constraint);
1832 }
1833 }
1834 }
1835 }
1836}
1837
1838void SatSolver::BumpClauseActivity(SatClause* clause) {
1839 // We only bump the activity of the clauses that have some info. So if we know
1840 // that we will keep a clause forever, we don't need to create its Info. More
1841 // than the speed, this allows to limit as much as possible the activity
1842 // rescaling.
1843 auto it = clauses_propagator_->mutable_clauses_info()->find(clause);
1844 if (it == clauses_propagator_->mutable_clauses_info()->end()) return;
1845
1846 it->second.num_cleanup_rounds_since_last_bumped = 0;
1847
1848 // Increase the activity.
1849 const double activity = it->second.activity += clause_activity_increment_;
1850 if (activity > parameters_->max_clause_activity_value()) {
1851 RescaleClauseActivities(1.0 / parameters_->max_clause_activity_value());
1852 }
1853
1854 // Update this clause LBD using the new decision orders.
1855 // Note that this can keep the clause forever depending on the parameters.
1856 //
1857 // TODO(user): This cause one more hash lookup, probably not a big deal, but
1858 // could be optimized away.
1859 clauses_propagator_->ChangeLbdIfBetter(clause, ComputeLbd(clause->AsSpan()));
1860}
1861
1862void SatSolver::RescaleClauseActivities(double scaling_factor) {
1863 SCOPED_TIME_STAT(&stats_);
1864 clause_activity_increment_ *= scaling_factor;
1865 clauses_propagator_->RescaleClauseActivities(scaling_factor);
1866}
1867
1868void SatSolver::UpdateClauseActivityIncrement() {
1869 SCOPED_TIME_STAT(&stats_);
1870 clause_activity_increment_ *= 1.0 / parameters_->clause_activity_decay();
1871}
1872
1873bool SatSolver::IsConflictValid(absl::Span<const Literal> literals) {
1874 SCOPED_TIME_STAT(&stats_);
1875 if (literals.empty()) return true;
1876 const int highest_level = AssignmentLevel(literals[0].Variable());
1877 if (!trail_->Assignment().LiteralIsFalse(literals[0])) {
1878 VLOG(2) << "not false " << literals[0];
1879 return false;
1880 }
1881 for (int i = 1; i < literals.size(); ++i) {
1882 if (!trail_->Assignment().LiteralIsFalse(literals[i])) {
1883 VLOG(2) << "not false " << literals[i];
1884 return false;
1885 }
1886 const int level = AssignmentLevel(literals[i].Variable());
1887 if (level <= 0 || level >= highest_level) {
1888 VLOG(2) << "Another at highest level or at level zero. level:" << level
1889 << " highest: " << highest_level;
1890 return false;
1891 }
1892 }
1893 return true;
1894}
1895
1896int SatSolver::ComputePropagationLevel(absl::Span<const Literal> literals) {
1897 SCOPED_TIME_STAT(&stats_);
1898 DCHECK_GT(CurrentDecisionLevel(), 0);
1899
1900 // We want the highest decision level among literals other than the first one.
1901 // Note that this level will always be smaller than that of the first literal.
1902 //
1903 // Note(user): if the learned clause is of size 1, we backtrack all the way to
1904 // the beginning. It may be possible to follow another behavior, but then the
1905 // code require some special cases in
1906 // AddLearnedClauseAndEnqueueUnitPropagation() to fix the literal and not
1907 // backtrack over it. Also, subsequent propagated variables may not have a
1908 // correct level in this case.
1909 int propagation_level = 0;
1910 for (int i = 1; i < literals.size(); ++i) {
1911 const int level = AssignmentLevel(literals[i].Variable());
1912 propagation_level = std::max(propagation_level, level);
1913 }
1914 DCHECK_LT(propagation_level, AssignmentLevel(literals[0].Variable()));
1915 DCHECK_LE(AssignmentLevel(literals[0].Variable()), CurrentDecisionLevel());
1916 return propagation_level;
1917}
1918
1919// Tricky: When a new conflict clause is learned, the conflicting literal will
1920// be the only literal at the maximum level. This will not be the case for a
1921// clause used in resolution as the last literal will be propagated. To ensure
1922// this function returns the same value for both new conflicts and existing
1923// clauses, we add 1 to the LBD if this clause has more than 1 literal at the
1924// maximum level, so existing clauses will have an LBD as if they were a new
1925// conflict.
1926int SatSolver::ComputeLbd(absl::Span<const Literal> literals) {
1927 SCOPED_TIME_STAT(&stats_);
1928 const int limit =
1929 parameters_->count_assumption_levels_in_lbd() ? 0 : assumption_level_;
1930 int max_level = 0;
1931 for (const Literal literal : literals) {
1932 max_level = std::max(max_level, AssignmentLevel(literal.Variable()));
1933 }
1934 if (max_level <= limit) return 0;
1935
1936 int num_at_max_level = 0;
1937 is_level_marked_.ClearAndResize(SatDecisionLevel(max_level + 1));
1938 for (const Literal literal : literals) {
1939 const SatDecisionLevel level(AssignmentLevel(literal.Variable()));
1940 DCHECK_GE(level, 0);
1941 num_at_max_level += (level == max_level) ? 1 : 0;
1942 if (level > limit) is_level_marked_.Set(level);
1943 }
1944
1945 return is_level_marked_.NumberOfSetCallsWithDifferentArguments() +
1946 (num_at_max_level > 1 ? 1 : 0);
1947}
1948
1949std::string SatSolver::StatusString(Status status) const {
1950 const double time_in_s = timer_.Get();
1951 return absl::StrFormat("\n status: %s\n", SatStatusString(status)) +
1952 absl::StrFormat(" time: %fs\n", time_in_s) +
1953 absl::StrFormat(" memory: %s\n", MemoryUsage()) +
1954 absl::StrFormat(
1955 " num failures: %d (%.0f /sec)\n", counters_.num_failures,
1956 static_cast<double>(counters_.num_failures) / time_in_s) +
1957 absl::StrFormat(
1958 " num branches: %d (%.0f /sec)\n", counters_.num_branches,
1959 static_cast<double>(counters_.num_branches) / time_in_s) +
1960 absl::StrFormat(" num propagations: %d (%.0f /sec)\n",
1962 static_cast<double>(num_propagations()) / time_in_s) +
1963 absl::StrFormat(" num binary propagations: %d\n",
1964 binary_implication_graph_->num_propagations()) +
1965 absl::StrFormat(" num binary inspections: %d\n",
1966 binary_implication_graph_->num_inspections()) +
1967 absl::StrFormat(
1968 " num binary redundant implications: %d\n",
1969 binary_implication_graph_->num_redundant_implications()) +
1970 absl::StrFormat(
1971 " num classic minimizations: %d"
1972 " (literals removed: %d)\n",
1973 counters_.num_minimizations, counters_.num_literals_removed) +
1974 absl::StrFormat(
1975 " num binary minimizations: %d"
1976 " (literals removed: %d)\n",
1977 binary_implication_graph_->num_minimization(),
1978 binary_implication_graph_->num_literals_removed()) +
1979 absl::StrFormat(" num inspected clauses: %d\n",
1980 clauses_propagator_->num_inspected_clauses()) +
1981 absl::StrFormat(" num inspected clause_literals: %d\n",
1982 clauses_propagator_->num_inspected_clause_literals()) +
1983 absl::StrFormat(
1984 " num learned literals: %d (avg: %.1f /clause)\n",
1985 counters_.num_literals_learned,
1986 1.0 * counters_.num_literals_learned / counters_.num_failures) +
1987 absl::StrFormat(
1988 " num learned PB literals: %d (avg: %.1f /clause)\n",
1989 counters_.num_learned_pb_literals,
1990 1.0 * counters_.num_learned_pb_literals / counters_.num_failures) +
1991 absl::StrFormat(" num subsumed clauses: %d\n",
1992 counters_.num_subsumed_clauses) +
1993 absl::StrFormat(" pb num threshold updates: %d\n",
1994 pb_constraints_->num_threshold_updates()) +
1995 absl::StrFormat(" pb num constraint lookups: %d\n",
1996 pb_constraints_->num_constraint_lookups()) +
1997 absl::StrFormat(" pb num inspected constraint literals: %d\n",
1998 pb_constraints_->num_inspected_constraint_literals()) +
1999 restart_->InfoString() +
2000 absl::StrFormat(" deterministic time: %f\n", deterministic_time());
2001}
2002
2003std::string SatSolver::RunningStatisticsString() const {
2004 const double time_in_s = timer_.Get();
2005 return absl::StrFormat(
2006 "%6.2fs, mem:%s, fails:%d, depth:%d, clauses:%d, tmp:%d, bin:%u, "
2007 "restarts:%d, vars:%d",
2008 time_in_s, MemoryUsage(), counters_.num_failures, CurrentDecisionLevel(),
2009 clauses_propagator_->num_clauses() -
2010 clauses_propagator_->num_removable_clauses(),
2011 clauses_propagator_->num_removable_clauses(),
2012 binary_implication_graph_->ComputeNumImplicationsForLog(),
2013 restart_->NumRestarts(),
2014 num_variables_.value() - num_processed_fixed_variables_);
2015}
2016
2018 SCOPED_TIME_STAT(&stats_);
2019 DCHECK_EQ(CurrentDecisionLevel(), 0);
2020 if (num_processed_fixed_variables_ == trail_->Index()) return;
2021 int num_detached_clauses = 0;
2022 int num_binary = 0;
2023
2024 // We remove the clauses that are always true and the fixed literals from the
2025 // others. Note that none of the clause should be all false because we should
2026 // have detected a conflict before this is called.
2027 const int saved_index = trail_->Index();
2028 for (SatClause* clause : clauses_propagator_->AllClausesInCreationOrder()) {
2029 if (clause->IsRemoved()) continue;
2030
2031 const size_t old_size = clause->size();
2032 if (clauses_propagator_->RemoveFixedLiteralsAndTestIfTrue(clause)) {
2033 ++num_detached_clauses;
2034 continue;
2035 }
2036
2037 const size_t new_size = clause->size();
2038 if (new_size == old_size) continue;
2039
2040 ClauseId new_clause_id = kNoClauseId;
2041 if (lrat_proof_handler_ != nullptr) {
2042 std::vector<ClauseId>& clause_ids = tmp_clause_ids_for_minimization_;
2043 clause_ids.clear();
2044 for (int i = new_size; i < old_size; ++i) {
2045 const Literal fixed_literal = *(clause->begin() + i);
2046 clause_ids.push_back(trail_->GetUnitClauseId(fixed_literal.Variable()));
2047 DCHECK_NE(clause_ids.back(), kNoClauseId);
2048 }
2049 const ClauseId old_clause_id = clauses_propagator_->GetClauseId(clause);
2050 clause_ids.push_back(old_clause_id);
2051 new_clause_id = clause_id_generator_->GetNextId();
2052 lrat_proof_handler_->AddInferredClause(
2053 new_clause_id, {clause->begin(), new_size}, clause_ids);
2054 if (new_size > 2) {
2055 // If the new size is 2 the LRAT clause is deleted as part of the
2056 // LazyDelete(clause, PROMOTED_TO_BINARY) call below. Also the SatClause
2057 // ID must not be changed to the new ID in this case, otherwise we would
2058 // get a SatClause and a binary clause with the same ID, leading to a
2059 // double delete.
2060 lrat_proof_handler_->DeleteClause(old_clause_id,
2061 {clause->begin(), old_size});
2062 clauses_propagator_->SetClauseId(clause, new_clause_id);
2063 }
2064 }
2065
2066 if (new_size == 2) {
2067 // This clause is now a binary clause, treat it separately. Note that
2068 // it is safe to do that because this clause can't be used as a reason
2069 // since we are at level zero and the clause is not satisfied.
2070 AddBinaryClauseInternal(new_clause_id, clause->FirstLiteral(),
2071 clause->SecondLiteral());
2072 clauses_propagator_->LazyDelete(
2074 ++num_binary;
2075
2076 // Tricky: AddBinaryClauseInternal() might fix literal if there is some
2077 // unprocessed equivalent literal, and the binary clause turn out to be
2078 // unary. This shouldn't happen otherwise the logic of
2079 // RemoveFixedLiteralsAndTestIfTrue() might fail.
2080 //
2081 // TODO(user): This still happen in SAT22.Carry_Save_Fast_1.cnf.cnf.xz,
2082 // it might not directly lead to a bug, but should still be fixed.
2083 DCHECK_EQ(trail_->Index(), saved_index);
2084 continue;
2085 }
2086 }
2087
2088 // Note that we will only delete the clauses during the next database cleanup.
2089 clauses_propagator_->CleanUpWatchers();
2090 if (num_detached_clauses > 0 || num_binary > 0) {
2091 VLOG(1) << trail_->Index() << " fixed variables at level 0. " << "Detached "
2092 << num_detached_clauses << " clauses. " << num_binary
2093 << " converted to binary.";
2094 }
2095
2096 // We also clean the binary implication graph.
2097 // Tricky: If we added the first binary clauses above, the binary graph
2098 // is not in "propagated" state as it should be, so we call Propagate() so
2099 // all the checks are happy.
2100 CHECK(binary_implication_graph_->Propagate(trail_));
2101 binary_implication_graph_->RemoveFixedVariables();
2102 num_processed_fixed_variables_ = trail_->Index();
2103 deterministic_time_of_last_fixed_variables_cleanup_ = deterministic_time();
2104}
2105
2107 // If the time limit is reached, then this invariant do not hold.
2108 if (time_limit_->LimitReached()) return true;
2109 for (SatPropagator* propagator : propagators_) {
2110 if (propagator->IsEmpty()) continue;
2111 if (!propagator->PropagationIsDone(*trail_)) return false;
2112 }
2113 return true;
2114}
2115
2116// TODO(user): Support propagating only the "first" propagators. That can
2117// be useful for probing/in-processing, so we can control if we do only the SAT
2118// part or the full integer part...
2120 SCOPED_TIME_STAT(&stats_);
2121 DCHECK(!ModelIsUnsat());
2122
2123 while (true) {
2124 // Because we might potentially iterate often on this list below, we remove
2125 // empty propagators.
2126 //
2127 // TODO(user): This might not really be needed.
2128 non_empty_propagators_.clear();
2129 for (SatPropagator* propagator : propagators_) {
2130 if (!propagator->IsEmpty()) {
2131 non_empty_propagators_.push_back(propagator);
2132 }
2133 }
2134
2135 while (true) {
2136 // The idea here is to abort the inspection as soon as at least one
2137 // propagation occurs so we can loop over and test again the highest
2138 // priority constraint types using the new information.
2139 //
2140 // Note that the first propagators_ should be the
2141 // binary_implication_graph_ and that its Propagate() functions will not
2142 // abort on the first propagation to be slightly more efficient.
2143 const int old_index = trail_->Index();
2144 for (SatPropagator* propagator : non_empty_propagators_) {
2145 DCHECK(propagator->PropagatePreconditionsAreSatisfied(*trail_));
2146 if (!propagator->Propagate(trail_)) return false;
2147 if (trail_->Index() > old_index) break;
2148 }
2149 if (trail_->Index() == old_index) break;
2150 }
2151
2152 // In some corner cases, we might add new constraint during propagation,
2153 // which might trigger new propagator addition or some propagator to become
2154 // non-empty() now.
2155 if (PropagationIsDone()) return true;
2156 }
2157 return true;
2158}
2159
2160void SatSolver::InitializePropagators() {
2161 propagators_.clear();
2162 propagators_.push_back(binary_implication_graph_);
2163 propagators_.push_back(clauses_propagator_);
2164 propagators_.push_back(enforcement_propagator_);
2165 propagators_.push_back(pb_constraints_);
2166 for (int i = 0; i < external_propagators_.size(); ++i) {
2167 propagators_.push_back(external_propagators_[i]);
2168 }
2169 if (last_propagator_ != nullptr) {
2170 propagators_.push_back(last_propagator_);
2171 }
2172}
2173
2174bool SatSolver::ResolvePBConflict(BooleanVariable var,
2176 Coefficient* slack) {
2177 const int trail_index = trail_->Info(var).trail_index;
2178
2179 // This is the slack of the conflict < trail_index
2180 DCHECK_EQ(*slack, conflict->ComputeSlackForTrailPrefix(*trail_, trail_index));
2181
2182 // Pseudo-Boolean case.
2183 UpperBoundedLinearConstraint* pb_reason = ReasonPbConstraintOrNull(var);
2184 if (pb_reason != nullptr) {
2185 pb_reason->ResolvePBConflict(*trail_, var, conflict, slack);
2186 return false;
2187 }
2188
2189 // Generic clause case.
2190 Coefficient multiplier(1);
2191
2192 // TODO(user): experiment and choose the "best" algo.
2193 const int algorithm = 1;
2194 switch (algorithm) {
2195 case 1:
2196 // We reduce the conflict slack to 0 before adding the clause.
2197 // The advantage of this method is that the coefficients stay small.
2198 conflict->ReduceSlackTo(*trail_, trail_index, *slack, Coefficient(0));
2199 break;
2200 case 2:
2201 // No reduction, we add the lower possible multiple.
2202 multiplier = *slack + 1;
2203 break;
2204 default:
2205 // No reduction, the multiple is chosen to cancel var.
2206 multiplier = conflict->GetCoefficient(var);
2207 }
2208
2209 Coefficient num_literals(1);
2210 conflict->AddTerm(
2211 trail_->Assignment().GetTrueLiteralForAssignedVariable(var).Negated(),
2212 multiplier);
2213 for (Literal literal : trail_->Reason(var)) {
2214 DCHECK_NE(literal.Variable(), var);
2215 DCHECK(Assignment().LiteralIsFalse(literal));
2216 conflict->AddTerm(literal.Negated(), multiplier);
2217 ++num_literals;
2218 }
2219 conflict->AddToRhs((num_literals - 1) * multiplier);
2220
2221 // All the algorithms above result in a new slack of -1.
2222 *slack = -1;
2223 DCHECK_EQ(*slack, conflict->ComputeSlackForTrailPrefix(*trail_, trail_index));
2224 return true;
2225}
2226
2227void SatSolver::EnqueueNewDecision(Literal literal) {
2228 SCOPED_TIME_STAT(&stats_);
2229 CHECK(!Assignment().VariableIsAssigned(literal.Variable()));
2230
2231 // We are back at level 0. This can happen because of a restart, or because
2232 // we proved that some variables must take a given value in any satisfiable
2233 // assignment. Trigger a simplification of the clauses if there is new fixed
2234 // variables. Note that for efficiency reason, we don't do that too often.
2235 //
2236 // TODO(user): Do more advanced preprocessing?
2237 if (CurrentDecisionLevel() == 0) {
2238 const double kMinDeterministicTimeBetweenCleanups = 1.0;
2239 if (num_processed_fixed_variables_ < trail_->Index() &&
2241 deterministic_time_of_last_fixed_variables_cleanup_ +
2242 kMinDeterministicTimeBetweenCleanups) {
2244 }
2245 }
2246
2247 counters_.num_branches++;
2248 last_decision_or_backtrack_trail_index_ = trail_->Index();
2249 trail_->EnqueueSearchDecision(literal);
2250}
2251
2252std::string SatSolver::DebugString(const SatClause& clause) const {
2253 std::string result;
2254 for (const Literal literal : clause) {
2255 if (!result.empty()) {
2256 result.append(" || ");
2257 }
2258 const std::string value =
2259 trail_->Assignment().LiteralIsTrue(literal)
2260 ? "true"
2261 : (trail_->Assignment().LiteralIsFalse(literal) ? "false"
2262 : "undef");
2263 result.append(absl::StrFormat("%s(%s)", literal.DebugString(), value));
2264 }
2265 return result;
2266}
2267
2268int SatSolver::ComputeMaxTrailIndex(absl::Span<const Literal> clause) const {
2269 SCOPED_TIME_STAT(&stats_);
2270 int trail_index = -1;
2271 for (const Literal literal : clause) {
2272 trail_index =
2273 std::max(trail_index, trail_->Info(literal.Variable()).trail_index);
2274 }
2275 return trail_index;
2276}
2277
2278// This method will compute a first UIP conflict
2279// http://www.cs.tau.ac.il/~msagiv/courses/ATP/iccad2001_final.pdf
2280// http://gauss.ececs.uc.edu/SAT/articles/FAIA185-0131.pdf
2281void SatSolver::ComputeFirstUIPConflict(
2282 int max_trail_index, std::vector<Literal>* conflict,
2283 std::vector<Literal>* reason_used_to_infer_the_conflict) {
2284 SCOPED_TIME_STAT(&stats_);
2285 const int64_t conflict_id = counters_.num_failures;
2286
2287 Literal previous_literal;
2288
2289 // This will be used to mark all the literals inspected while we process the
2290 // conflict and the reasons behind each of its variable assignments.
2291 is_marked_.ClearAndResize(num_variables_);
2292
2293 conflict->clear();
2294 reason_used_to_infer_the_conflict->clear();
2295 if (max_trail_index == -1) return;
2296
2297 absl::Span<const Literal> conflict_or_reason_to_expand =
2298 trail_->FailingClause();
2299
2300 // max_trail_index is the maximum trail index appearing in the failing_clause
2301 // and its level (Which is almost always equals to the CurrentDecisionLevel(),
2302 // except for symmetry propagation).
2303 DCHECK_EQ(max_trail_index, ComputeMaxTrailIndex(trail_->FailingClause()));
2304 int highest_level = trail_->Info((*trail_)[max_trail_index].Variable()).level;
2305 if (trail_->ChronologicalBacktrackingEnabled()) {
2306 for (const Literal literal : conflict_or_reason_to_expand) {
2307 highest_level =
2308 std::max(highest_level, AssignmentLevel(literal.Variable()));
2309 }
2310 }
2311 if (highest_level == 0) return;
2312
2313 // We use a max-heap to find the literal to eliminate.
2314 struct LiteralWithIndex {
2315 Literal literal;
2316 int index;
2317
2318 bool operator<(const LiteralWithIndex& other) const {
2319 return index < other.index;
2320 }
2321 };
2322 std::vector<LiteralWithIndex> last_level_heap;
2323
2324 // To find the 1-UIP conflict clause, we start by the failing_clause, and
2325 // expand each of its literal using the reason for this literal assignment to
2326 // false. The is_marked_ set allow us to never expand the same literal twice.
2327 //
2328 // The expansion is not done (i.e. stop) for literals that were assigned at a
2329 // decision level below the current one. If the level of such literal is not
2330 // zero, it is added to the conflict clause.
2331 //
2332 // We use a heap to expand the literals of the highest_level by decreasing
2333 // assignment order, aka trail index. We stop when there is a single literal
2334 // left at the higest level.
2335 //
2336 // This last literal will be the first UIP because by definition all the
2337 // propagation done at the current level will pass though it at some point.
2338 SatClause* sat_clause = trail_->FailingSatClause();
2339 DCHECK(!conflict_or_reason_to_expand.empty());
2340 while (true) {
2341 const int old_conflict_size = conflict->size();
2342 const int old_heap_size = last_level_heap.size();
2343
2344 int num_new_vars_at_positive_level = 0;
2345 int num_vars_at_positive_level_in_clause_to_expand = 0;
2346 for (const Literal literal : conflict_or_reason_to_expand) {
2347 const BooleanVariable var = literal.Variable();
2348 const int level = AssignmentLevel(var);
2349 if (level == 0) continue;
2350 DCHECK_LE(level, highest_level);
2351 ++num_vars_at_positive_level_in_clause_to_expand;
2352 if (!is_marked_[var]) {
2353 is_marked_.Set(var);
2354 ++num_new_vars_at_positive_level;
2355 if (level == highest_level) {
2356 last_level_heap.push_back({literal, trail_->Info(var).trail_index});
2357 } else {
2358 // Note that all these literals are currently false since the clause
2359 // to expand was used to infer the value of a literal at this level.
2360 DCHECK(trail_->Assignment().LiteralIsFalse(literal));
2361 conflict->push_back(literal);
2362 }
2363 }
2364 }
2365
2366 // If there is new variables, then all the previously subsumed clauses are
2367 // not subsumed by the current conflict anymore. However they are still
2368 // subsumed by the state of the conflict just before.
2369 //
2370 // TODO(user): Think about minimization of these intermediate conflicts.
2371 if (num_new_vars_at_positive_level > 0) {
2372 if (parameters_->extra_subsumption_during_conflict_analysis() &&
2373 !subsumed_clauses_.empty() &&
2374 reason_used_to_infer_the_conflict->size() > 1) {
2375 // The "old" conflict should subsume all of that.
2376 tmp_literals_.clear();
2377 tmp_literals_.push_back(previous_literal.Negated());
2378 for (int i = 0; i < old_conflict_size; ++i) {
2379 tmp_literals_.push_back((*conflict)[i]);
2380 }
2381 for (int i = 0; i < old_heap_size; ++i) {
2382 tmp_literals_.push_back(last_level_heap[i].literal);
2383 }
2384 if (DEBUG_MODE) {
2385 for (SatClause* clause : subsumed_clauses_) {
2386 CHECK(ClauseSubsumption(tmp_literals_, clause))
2387 << tmp_literals_ << " " << clause->AsSpan();
2388 }
2389 }
2390
2391 subsuming_lrat_index_.push_back(
2392 reason_used_to_infer_the_conflict->size() - 1);
2393 subsuming_clauses_.Add(tmp_literals_);
2394 subsuming_groups_.Add(subsumed_clauses_);
2395 }
2396 subsumed_clauses_.clear();
2397 }
2398
2399 // Restore the heap property.
2400 for (int i = old_heap_size + 1; i <= last_level_heap.size(); ++i) {
2401 std::push_heap(last_level_heap.begin(), last_level_heap.begin() + i);
2402 }
2403
2404 // This check if the new conflict is exactly equal to
2405 // conflict_or_reason_to_expand. Since we just performed an union, comparing
2406 // the size is enough.
2407 //
2408 // When this is true, then the current conflict is equal to the reason we
2409 // just expanded and subsumbes the clause (which has just one extra
2410 // literal).
2411 if (sat_clause != nullptr &&
2412 num_vars_at_positive_level_in_clause_to_expand ==
2413 conflict->size() + last_level_heap.size()) {
2414 subsumed_clauses_.push_back(sat_clause);
2415 }
2416
2417 DCHECK(!last_level_heap.empty());
2418 const Literal literal = (*trail_)[last_level_heap.front().index];
2419 DCHECK(is_marked_[literal.Variable()]);
2420
2421 if (last_level_heap.size() == 1) {
2422 // We have the first UIP. Add its negation to the conflict clause.
2423 // This way, after backtracking to the proper level, the conflict clause
2424 // will be unit, and infer the negation of the UIP that caused the fail.
2425 conflict->push_back(literal.Negated());
2426
2427 // To respect the function API move the first UIP in the first position.
2428 std::swap(conflict->back(), conflict->front());
2429 break;
2430 }
2431
2432 reason_used_to_infer_the_conflict->push_back(literal);
2433
2434 // If we already encountered the same reason, we can just skip this literal
2435 // which is what setting conflict_or_reason_to_expand to the empty clause
2436 // do.
2437 if (same_reason_identifier_.FirstVariableWithSameReason(
2438 literal.Variable()) != literal.Variable()) {
2439 conflict_or_reason_to_expand = {};
2440 } else {
2441 conflict_or_reason_to_expand =
2442 trail_->Reason(literal.Variable(), conflict_id);
2443 }
2444 sat_clause = clauses_propagator_->ReasonClauseOrNull(literal.Variable());
2445
2446 previous_literal = literal;
2447 absl::c_pop_heap(last_level_heap);
2448 last_level_heap.pop_back();
2449 }
2450}
2451
2452void SatSolver::ComputeUnionOfReasons(absl::Span<const Literal> input,
2453 std::vector<Literal>* literals) {
2454 tmp_mark_.ClearAndResize(num_variables_);
2455 literals->clear();
2456 for (const Literal l : input) tmp_mark_.Set(l.Variable());
2457 for (const Literal l : input) {
2458 for (const Literal r : trail_->Reason(l.Variable())) {
2459 if (!tmp_mark_[r.Variable()]) {
2460 tmp_mark_.Set(r.Variable());
2461 literals->push_back(r);
2462 }
2463 }
2464 }
2465 for (const Literal l : input) tmp_mark_.Clear(l.Variable());
2466 for (const Literal l : *literals) tmp_mark_.Clear(l.Variable());
2467}
2468
2469// TODO(user): Remove the literals assigned at level 0.
2470void SatSolver::ComputePBConflict(int max_trail_index,
2471 Coefficient initial_slack,
2473 int* pb_backjump_level) {
2474 SCOPED_TIME_STAT(&stats_);
2475 int trail_index = max_trail_index;
2476
2477 // First compute the slack of the current conflict for the assignment up to
2478 // trail_index. It must be negative since this is a conflict.
2479 Coefficient slack = initial_slack;
2480 DCHECK_EQ(slack,
2481 conflict->ComputeSlackForTrailPrefix(*trail_, trail_index + 1));
2482 CHECK_LT(slack, 0) << "We don't have a conflict!";
2483
2484 // Iterate backward over the trail.
2485 int backjump_level = 0;
2486 while (true) {
2487 const BooleanVariable var = (*trail_)[trail_index].Variable();
2488 --trail_index;
2489
2490 if (conflict->GetCoefficient(var) > 0 &&
2491 trail_->Assignment().LiteralIsTrue(conflict->GetLiteral(var))) {
2492 if (parameters_->minimize_reduction_during_pb_resolution()) {
2493 // When this parameter is true, we don't call ReduceCoefficients() at
2494 // every loop. However, it is still important to reduce the "current"
2495 // variable coefficient, because this can impact the value of the new
2496 // slack below.
2497 conflict->ReduceGivenCoefficient(var);
2498 }
2499
2500 // This is the slack one level before (< Info(var).trail_index).
2501 slack += conflict->GetCoefficient(var);
2502
2503 // This can't happen at the beginning, but may happen later.
2504 // It means that even without var assigned, we still have a conflict.
2505 if (slack < 0) continue;
2506
2507 // At this point, just removing the last assignment lift the conflict.
2508 // So we can abort if the true assignment before that is at a lower level
2509 // TODO(user): Somewhat inefficient.
2510 // TODO(user): We could abort earlier...
2511 const int current_level = AssignmentLevel(var);
2512 int i = trail_index;
2513 while (i >= 0) {
2514 const BooleanVariable previous_var = (*trail_)[i].Variable();
2515 if (conflict->GetCoefficient(previous_var) > 0 &&
2516 trail_->Assignment().LiteralIsTrue(
2517 conflict->GetLiteral(previous_var))) {
2518 break;
2519 }
2520 --i;
2521 }
2522 if (i < 0 || AssignmentLevel((*trail_)[i].Variable()) < current_level) {
2523 backjump_level = i < 0 ? 0 : AssignmentLevel((*trail_)[i].Variable());
2524 break;
2525 }
2526
2527 // We can't abort, So resolve the current variable.
2528 DCHECK_NE(trail_->AssignmentType(var), AssignmentType::kSearchDecision);
2529 const bool clause_used = ResolvePBConflict(var, conflict, &slack);
2530
2531 // At this point, we have a negative slack. Note that ReduceCoefficients()
2532 // will not change it. However it may change the slack value of the next
2533 // iteration (when we will no longer take into account the true literal
2534 // with highest trail index).
2535 //
2536 // Note that the trail_index has already been decremented, it is why
2537 // we need the +1 in the slack computation.
2538 const Coefficient slack_only_for_debug =
2540 ? conflict->ComputeSlackForTrailPrefix(*trail_, trail_index + 1)
2541 : Coefficient(0);
2542
2543 if (clause_used) {
2544 // If a clause was used, we know that slack has the correct value.
2545 if (!parameters_->minimize_reduction_during_pb_resolution()) {
2546 conflict->ReduceCoefficients();
2547 }
2548 } else {
2549 // TODO(user): The function below can take most of the running time on
2550 // some instances. The goal is to have slack updated to its new value
2551 // incrementally, but we are not here yet.
2552 if (parameters_->minimize_reduction_during_pb_resolution()) {
2553 slack =
2554 conflict->ComputeSlackForTrailPrefix(*trail_, trail_index + 1);
2555 } else {
2556 slack = conflict->ReduceCoefficientsAndComputeSlackForTrailPrefix(
2557 *trail_, trail_index + 1);
2558 }
2559 }
2560 DCHECK_EQ(slack, slack_only_for_debug);
2561 CHECK_LT(slack, 0);
2562 if (conflict->Rhs() < 0) {
2563 *pb_backjump_level = -1;
2564 return;
2565 }
2566 }
2567 }
2568
2569 // Reduce the conflict coefficients if it is not already done.
2570 // This is important to avoid integer overflow.
2571 if (!parameters_->minimize_reduction_during_pb_resolution()) {
2572 conflict->ReduceCoefficients();
2573 }
2574
2575 // Double check.
2576 // The sum of the literal with level <= backjump_level must propagate.
2577 std::vector<Coefficient> sum_for_le_level(backjump_level + 2, Coefficient(0));
2578 std::vector<Coefficient> max_coeff_for_ge_level(backjump_level + 2,
2579 Coefficient(0));
2580 int size = 0;
2581 Coefficient max_sum(0);
2582 for (BooleanVariable var : conflict->PossibleNonZeros()) {
2583 const Coefficient coeff = conflict->GetCoefficient(var);
2584 if (coeff == 0) continue;
2585 max_sum += coeff;
2586 ++size;
2587 if (!trail_->Assignment().VariableIsAssigned(var) ||
2588 AssignmentLevel(var) > backjump_level) {
2589 max_coeff_for_ge_level[backjump_level + 1] =
2590 std::max(max_coeff_for_ge_level[backjump_level + 1], coeff);
2591 } else {
2592 const int level = AssignmentLevel(var);
2593 if (trail_->Assignment().LiteralIsTrue(conflict->GetLiteral(var))) {
2594 sum_for_le_level[level] += coeff;
2595 }
2596 max_coeff_for_ge_level[level] =
2597 std::max(max_coeff_for_ge_level[level], coeff);
2598 }
2599 }
2600
2601 // Compute the cumulative version.
2602 for (int i = 1; i < sum_for_le_level.size(); ++i) {
2603 sum_for_le_level[i] += sum_for_le_level[i - 1];
2604 }
2605 for (int i = max_coeff_for_ge_level.size() - 2; i >= 0; --i) {
2606 max_coeff_for_ge_level[i] =
2607 std::max(max_coeff_for_ge_level[i], max_coeff_for_ge_level[i + 1]);
2608 }
2609
2610 // Compute first propagation level. -1 means that the problem is UNSAT.
2611 // Note that the first propagation level may be < backjump_level!
2612 if (sum_for_le_level[0] > conflict->Rhs()) {
2613 *pb_backjump_level = -1;
2614 return;
2615 }
2616 for (int i = 0; i <= backjump_level; ++i) {
2617 const Coefficient level_sum = sum_for_le_level[i];
2618 CHECK_LE(level_sum, conflict->Rhs());
2619 if (conflict->Rhs() - level_sum < max_coeff_for_ge_level[i + 1]) {
2620 *pb_backjump_level = i;
2621 return;
2622 }
2623 }
2624 LOG(FATAL) << "The code should never reach here.";
2625}
2626
2627void SatSolver::MinimizeConflict(std::vector<Literal>* conflict,
2628 std::vector<ClauseId>* clause_ids) {
2629 SCOPED_TIME_STAT(&stats_);
2630
2631 const int old_size = conflict->size();
2632 switch (parameters_->minimization_algorithm()) {
2634 return;
2635 case SatParameters::SIMPLE: {
2636 MinimizeConflictSimple(conflict, clause_ids);
2637 break;
2638 }
2640 MinimizeConflictRecursively(conflict, clause_ids);
2641 break;
2642 }
2643 }
2644 if (conflict->size() < old_size) {
2645 ++counters_.num_minimizations;
2646 counters_.num_literals_removed += old_size - conflict->size();
2647 }
2648}
2649
2650// This simple version just looks for any literal that is directly inferred by
2651// other literals of the conflict. It is directly inferred if the literals of
2652// its reason clause are either from level 0 or from the conflict itself.
2653//
2654// Note that because of the assignment structure, there is no need to process
2655// the literals of the conflict in order (except to fill the LRAT proof in
2656// clause_ids). While exploring the reason for a literal assignment, there will
2657// be no cycles.
2658void SatSolver::MinimizeConflictSimple(std::vector<Literal>* conflict,
2659 std::vector<ClauseId>* clause_ids) {
2660 SCOPED_TIME_STAT(&stats_);
2661 const int current_level = CurrentDecisionLevel();
2662
2663 // Note that is_marked_ is already initialized and that we can start at 1
2664 // since the first literal of the conflict is the 1-UIP literal.
2665 int index = 1;
2666 tmp_literals_.clear();
2667 for (int i = 1; i < conflict->size(); ++i) {
2668 const BooleanVariable var = (*conflict)[i].Variable();
2669 bool can_be_removed = false;
2670 if (AssignmentLevel(var) != current_level) {
2671 // It is important not to call Reason(var) when it can be avoided.
2672 const absl::Span<const Literal> reason = trail_->Reason(var);
2673 if (!reason.empty()) {
2674 can_be_removed = true;
2675 for (Literal literal : reason) {
2676 if (AssignmentLevel(literal.Variable()) == 0) continue;
2677 if (!is_marked_[literal.Variable()]) {
2678 can_be_removed = false;
2679 break;
2680 }
2681 }
2682 if (can_be_removed && lrat_proof_handler_ != nullptr) {
2683 // If BinaryImplicationGraph::MinimizeConflictFirst() was called, some
2684 // marked literals might be indirectly inferred by the 1-UIP literal,
2685 // via some chains of binary clauses. These implication chains must be
2686 // added to the LRAT proof.
2687 binary_implication_graph_->AppendImplicationChains(reason,
2688 clause_ids);
2689 // The reasons of the conflict literals must be added to `clause_ids`
2690 // in trail index order. For this we collect these literals in a
2691 // vector which is sorted at the end.
2692 tmp_literals_.push_back((*conflict)[i].Negated());
2693 }
2694 }
2695 }
2696 if (!can_be_removed) {
2697 (*conflict)[index] = (*conflict)[i];
2698 ++index;
2699 }
2700 }
2701 conflict->erase(conflict->begin() + index, conflict->end());
2702 if (!tmp_literals_.empty()) {
2703 // TODO(user): it should be possible to compute the conflict directly
2704 // in trail index order in ComputeFirstUIPConflict(), in linear time, to
2705 // avoid this sort.
2706 std::sort(tmp_literals_.begin(), tmp_literals_.end(),
2707 [&](Literal a, Literal b) {
2708 return trail_->Info(a.Variable()).trail_index <
2709 trail_->Info(b.Variable()).trail_index;
2710 });
2711 for (const Literal literal : tmp_literals_) {
2712 clause_ids->push_back(clauses_propagator_->ReasonClauseId(literal));
2713 DCHECK_NE(clause_ids->back(), kNoClauseId);
2714 }
2715 }
2716}
2717
2718// This is similar to MinimizeConflictSimple() except that for each literal of
2719// the conflict, the literals of its reason are recursively expanded using their
2720// reason and so on. The recursion loops until we show that the initial literal
2721// can be inferred from the conflict variables alone, or if we show that this is
2722// not the case. The result of any variable expansion will be cached in order
2723// not to be expanded again.
2724void SatSolver::MinimizeConflictRecursively(std::vector<Literal>* conflict,
2725 std::vector<ClauseId>* clause_ids) {
2726 SCOPED_TIME_STAT(&stats_);
2727
2728 // is_marked_ will contain all the conflict literals plus the literals that
2729 // have been shown to depend only on the conflict literals. is_independent_
2730 // will contain the literals that have been shown NOT to depend only on the
2731 // conflict literals. The two sets are exclusive for non-conflict literals,
2732 // but a conflict literal (which is always marked) can be independent if we
2733 // showed that it can't be removed from the clause.
2734 //
2735 // Optimization: There is no need to call is_marked_.ClearAndResize() or to
2736 // mark the conflict literals since this was already done by
2737 // ComputeFirstUIPConflict().
2738 is_independent_.ClearAndResize(num_variables_);
2739
2740 // min_trail_index_per_level_ will always be reset to all
2741 // std::numeric_limits<int>::max() at the end. This is used to prune the
2742 // search because any literal at a given level with an index smaller or equal
2743 // to min_trail_index_per_level_[level] can't be redundant.
2744 if (CurrentDecisionLevel() >= min_trail_index_per_level_.size()) {
2745 min_trail_index_per_level_.resize(CurrentDecisionLevel() + 1,
2746 std::numeric_limits<int>::max());
2747 }
2748
2749 // Compute the number of variables at each decision level. This will be used
2750 // to prune the DFS because we know that the minimized conflict will have at
2751 // least one variable of each decision level. Because such variable can't be
2752 // eliminated using lower decision levels variable otherwise it will have been
2753 // propagated.
2754 //
2755 // Note(user): Because is_marked_ may actually contains literals that are
2756 // implied if the 1-UIP literal is false, we can't just iterate on the
2757 // variables of the conflict here.
2758 for (BooleanVariable var : is_marked_.PositionsSetAtLeastOnce()) {
2759 const int level = AssignmentLevel(var);
2760 min_trail_index_per_level_[level] = std::min(
2761 min_trail_index_per_level_[level], trail_->Info(var).trail_index);
2762 }
2763
2764 // Remove the redundant variables from the conflict. These are the ones that
2765 // can be inferred by some other variables in the conflict. Note that we can
2766 // skip the first position since this is the 1-UIP.
2767 int index = 1;
2768 TimeLimitCheckEveryNCalls time_limit_check(100, time_limit_);
2769
2770 std::vector<Literal>& removed_literals = tmp_literals_;
2771 if (lrat_proof_handler_ != nullptr) {
2772 removed_literals.clear();
2773 is_marked_for_lrat_.CopyFrom(is_marked_);
2774 }
2775
2776 for (int i = 1; i < conflict->size(); ++i) {
2777 const BooleanVariable var = (*conflict)[i].Variable();
2778 const AssignmentInfo& info = trail_->Info(var);
2779 if (time_limit_check.LimitReached() ||
2780 info.type == AssignmentType::kSearchDecision ||
2781 info.trail_index <= min_trail_index_per_level_[info.level] ||
2782 !CanBeInferredFromConflictVariables(var)) {
2783 // Mark the conflict variable as independent. Note that is_marked_[var]
2784 // will still be true.
2785 is_independent_.Set(var);
2786 (*conflict)[index] = (*conflict)[i];
2787 ++index;
2788 } else if (lrat_proof_handler_ != nullptr) {
2789 removed_literals.push_back((*conflict)[i]);
2790 }
2791 }
2792 conflict->resize(index);
2793
2794 if (lrat_proof_handler_ != nullptr && !removed_literals.empty()) {
2795 // TODO(user): it should be possible to compute the conflict directly
2796 // in trail index order in ComputeFirstUIPConflict(), in linear time, to
2797 // avoid this sort.
2798 std::sort(removed_literals.begin(), removed_literals.end(),
2799 [&](Literal a, Literal b) {
2800 return trail_->Info(a.Variable()).trail_index <
2801 trail_->Info(b.Variable()).trail_index;
2802 });
2803 for (const Literal literal : removed_literals) {
2804 AppendInferenceChain(literal.Variable(), clause_ids);
2805 }
2806 }
2807
2808 // Reset min_trail_index_per_level_. We use the sparse version only if it
2809 // involves less than half the size of min_trail_index_per_level_.
2810 const int threshold = min_trail_index_per_level_.size() / 2;
2811 if (is_marked_.PositionsSetAtLeastOnce().size() < threshold) {
2812 for (BooleanVariable var : is_marked_.PositionsSetAtLeastOnce()) {
2813 min_trail_index_per_level_[AssignmentLevel(var)] =
2814 std::numeric_limits<int>::max();
2815 }
2816 } else {
2817 min_trail_index_per_level_.clear();
2818 }
2819}
2820
2821bool SatSolver::CanBeInferredFromConflictVariables(BooleanVariable variable) {
2822 // Test for an already processed variable with the same reason.
2823 {
2824 DCHECK(is_marked_[variable]);
2825 const BooleanVariable v =
2826 same_reason_identifier_.FirstVariableWithSameReason(variable);
2827 if (v != variable) return !is_independent_[v];
2828 }
2829
2830 // This function implements an iterative DFS from the given variable. It uses
2831 // the reason clause as adjacency lists. dfs_stack_ can be seen as the
2832 // recursive call stack of the variable we are currently processing. All its
2833 // adjacent variables will be pushed into variable_to_process_, and we will
2834 // then dequeue them one by one and process them.
2835 //
2836 // Note(user): As of 03/2014, --cpu_profile seems to indicate that using
2837 // dfs_stack_.assign(1, variable) is slower. My explanation is that the
2838 // function call is not inlined.
2839 dfs_stack_.clear();
2840 dfs_stack_.push_back(variable);
2841 variable_to_process_.clear();
2842 variable_to_process_.push_back(variable);
2843
2844 // First we expand the reason for the given variable.
2845 for (const Literal literal : trail_->Reason(variable)) {
2846 const BooleanVariable var = literal.Variable();
2847 DCHECK_NE(var, variable);
2848 if (is_marked_[var]) continue;
2849 const AssignmentInfo& info = trail_->Info(var);
2850 if (info.level == 0) {
2851 // Note that this is not needed if the solver is not configured to produce
2852 // an unsat proof. However, the (level == 0) test should always be false
2853 // in this case because there will never be literals of level zero in any
2854 // reason when we don't want a proof.
2855 is_marked_.Set(var);
2856 continue;
2857 }
2858 if (info.trail_index <= min_trail_index_per_level_[info.level] ||
2859 info.type == AssignmentType::kSearchDecision || is_independent_[var]) {
2860 return false;
2861 }
2862 variable_to_process_.push_back(var);
2863 }
2864
2865 // Then we start the DFS.
2866 while (!variable_to_process_.empty()) {
2867 const BooleanVariable current_var = variable_to_process_.back();
2868 if (current_var == dfs_stack_.back()) {
2869 // We finished the DFS of the variable dfs_stack_.back(), this can be seen
2870 // as a recursive call terminating.
2871 if (dfs_stack_.size() > 1) {
2872 DCHECK(!is_marked_[current_var]);
2873 is_marked_.Set(current_var);
2874 }
2875 variable_to_process_.pop_back();
2876 dfs_stack_.pop_back();
2877 continue;
2878 }
2879
2880 // If this variable became marked since the we pushed it, we can skip it.
2881 if (is_marked_[current_var]) {
2882 variable_to_process_.pop_back();
2883 continue;
2884 }
2885
2886 // This case will never be encountered since we abort right away as soon
2887 // as an independent variable is found.
2888 DCHECK(!is_independent_[current_var]);
2889
2890 // Test for an already processed variable with the same reason.
2891 {
2892 const BooleanVariable v =
2893 same_reason_identifier_.FirstVariableWithSameReason(current_var);
2894 if (v != current_var) {
2895 if (is_independent_[v]) break;
2896 DCHECK(is_marked_[v]);
2897 variable_to_process_.pop_back();
2898 continue;
2899 }
2900 }
2901
2902 // Expand the variable. This can be seen as making a recursive call.
2903 dfs_stack_.push_back(current_var);
2904 bool abort_early = false;
2905 for (Literal literal : trail_->Reason(current_var)) {
2906 const BooleanVariable var = literal.Variable();
2907 DCHECK_NE(var, current_var) << trail_->Info(var).DebugString()
2908 << " old: " << trail_->AssignmentType(var);
2909 const AssignmentInfo& info = trail_->Info(var);
2910 if (info.level == 0 || is_marked_[var]) continue;
2911 if (info.trail_index <= min_trail_index_per_level_[info.level] ||
2912 info.type == AssignmentType::kSearchDecision ||
2913 is_independent_[var]) {
2914 abort_early = true;
2915 break;
2916 }
2917 variable_to_process_.push_back(var);
2918 }
2919 if (abort_early) break;
2920 }
2921
2922 // All the variables left on the dfs_stack_ are independent.
2923 for (const BooleanVariable var : dfs_stack_) {
2924 is_independent_.Set(var);
2925 }
2926 return dfs_stack_.empty();
2927}
2928
2929void SatSolver::AppendInferenceChain(BooleanVariable variable,
2930 std::vector<ClauseId>* clause_ids) {
2931 DCHECK(is_marked_[variable]);
2932 DCHECK(is_marked_for_lrat_[variable]);
2933
2934 // This function implements the same iterative DFS as in
2935 // CanBeInferredFromConflictVariables(). See this method for more details.
2936 dfs_stack_.clear();
2937 dfs_stack_.push_back(variable);
2938 variable_to_process_.clear();
2939 variable_to_process_.push_back(variable);
2940
2941 // First we expand the reason for the given variable.
2942 const auto expand_variable = [&](BooleanVariable variable_to_expand) {
2943 for (const Literal literal : trail_->Reason(variable_to_expand)) {
2944 const BooleanVariable var = literal.Variable();
2945 const AssignmentInfo& info = trail_->Info(var);
2946 if (is_marked_for_lrat_[var] || info.level == 0) {
2947 // If BinaryImplicationGraph::MinimizeConflictFirst() was called, some
2948 // marked literals might be indirectly inferred by the 1-UIP literal,
2949 // via some chains of binary clauses. These implication chains must be
2950 // added to the LRAT proof.
2951 binary_implication_graph_->AppendImplicationChains({literal},
2952 clause_ids);
2953 is_marked_for_lrat_.Set(var);
2954 continue;
2955 }
2956 variable_to_process_.push_back(var);
2957 }
2958 };
2959 expand_variable(variable);
2960
2961 // Then we start the DFS.
2962 while (!variable_to_process_.empty()) {
2963 const BooleanVariable current_var = variable_to_process_.back();
2964 DCHECK(is_marked_[current_var]);
2965 if (current_var == dfs_stack_.back()) {
2966 // We finished the DFS of the variable dfs_stack_.back(), this can be seen
2967 // as a recursive call terminating.
2968 variable_to_process_.pop_back();
2969 dfs_stack_.pop_back();
2970
2971 const Literal current_literal =
2972 trail_->Assignment().GetTrueLiteralForAssignedVariable(current_var);
2973 clause_ids->push_back(
2974 clauses_propagator_->ReasonClauseId(current_literal));
2975 DCHECK_NE(clause_ids->back(), kNoClauseId);
2976 is_marked_for_lrat_.Set(current_var);
2977 continue;
2978 }
2979
2980 // If this variable became marked since the we pushed it, we can skip it.
2981 if (is_marked_for_lrat_[current_var]) {
2982 variable_to_process_.pop_back();
2983 continue;
2984 }
2985
2986 // Test for an already processed variable with the same reason.
2987 {
2988 const BooleanVariable v =
2989 same_reason_identifier_.FirstVariableWithSameReason(current_var);
2990 if (v != current_var) {
2991 DCHECK(is_marked_for_lrat_[v]);
2992 variable_to_process_.pop_back();
2993 continue;
2994 }
2995 }
2996
2997 // Expand the variable. This can be seen as making a recursive call.
2998 dfs_stack_.push_back(current_var);
2999 expand_variable(current_var);
3000 }
3001}
3002
3003namespace {
3004
3005struct WeightedVariable {
3006 WeightedVariable(BooleanVariable v, int w) : var(v), weight(w) {}
3007
3008 BooleanVariable var;
3009 int weight;
3010};
3011
3012// Lexical order, by larger weight, then by smaller variable number
3013// to break ties
3014struct VariableWithLargerWeightFirst {
3015 bool operator()(const WeightedVariable& wv1,
3016 const WeightedVariable& wv2) const {
3017 return (wv1.weight > wv2.weight ||
3018 (wv1.weight == wv2.weight && wv1.var < wv2.var));
3019 }
3020};
3021} // namespace.
3022
3023void SatSolver::CleanClauseDatabaseIfNeeded() {
3024 if (num_learned_clause_before_cleanup_ > 0) return;
3025 SCOPED_TIME_STAT(&stats_);
3026
3027 // Creates a list of clauses that can be deleted. Note that only the clauses
3028 // that appear in clauses_info can potentially be removed.
3029 typedef std::pair<SatClause*, ClauseInfo> Entry;
3030 std::vector<Entry> entries;
3031 auto& clauses_info = *(clauses_propagator_->mutable_clauses_info());
3032 for (auto& entry : clauses_info) {
3033 DCHECK(!entry.first->empty()); // Should have been deleted !
3034 entry.second.num_cleanup_rounds_since_last_bumped++;
3035 if (clauses_propagator_->ClauseIsUsedAsReason(entry.first)) continue;
3036
3037 if (entry.second.lbd <= parameters_->clause_cleanup_lbd_tier1() &&
3038 entry.second.num_cleanup_rounds_since_last_bumped <= 32) {
3039 continue;
3040 }
3041
3042 if (entry.second.lbd <= parameters_->clause_cleanup_lbd_tier2() &&
3043 entry.second.num_cleanup_rounds_since_last_bumped <= 1) {
3044 continue;
3045 }
3046
3047 // The LBD should always have been updated to be <= size.
3048 DCHECK_LE(entry.second.lbd, entry.first->size());
3049 entries.push_back(entry);
3050 }
3051 const int num_protected_clauses =
3052 clauses_propagator_->num_removable_clauses() - entries.size();
3053
3054 if (parameters_->clause_cleanup_ordering() == SatParameters::CLAUSE_LBD) {
3055 // Order the clauses by decreasing LBD and then increasing activity.
3056 std::sort(entries.begin(), entries.end(),
3057 [](const Entry& a, const Entry& b) {
3058 if (a.second.lbd == b.second.lbd) {
3059 return a.second.activity < b.second.activity;
3060 }
3061 return a.second.lbd > b.second.lbd;
3062 });
3063 } else {
3064 // Order the clauses by increasing activity and then decreasing LBD.
3065 std::sort(entries.begin(), entries.end(),
3066 [](const Entry& a, const Entry& b) {
3067 if (a.second.activity == b.second.activity) {
3068 return a.second.lbd > b.second.lbd;
3069 }
3070 return a.second.activity < b.second.activity;
3071 });
3072 }
3073
3074 // The clause we want to keep are at the end of the vector.
3075 int num_kept_clauses =
3076 (parameters_->clause_cleanup_target() > 0)
3077 ? std::min(static_cast<int>(entries.size()),
3078 parameters_->clause_cleanup_target())
3079 : static_cast<int>(parameters_->clause_cleanup_ratio() *
3080 static_cast<double>(entries.size()));
3081
3082 int num_deleted_clauses = entries.size() - num_kept_clauses;
3083
3084 // Tricky: Because the order of the clauses_info iteration is NOT
3085 // deterministic (pointer keys), we also keep all the clauses which have the
3086 // same LBD and activity as the last one so the behavior is deterministic.
3087 if (num_kept_clauses > 0) {
3088 while (num_deleted_clauses > 0) {
3089 const ClauseInfo& a = entries[num_deleted_clauses].second;
3090 const ClauseInfo& b = entries[num_deleted_clauses - 1].second;
3091 if (a.activity != b.activity || a.lbd != b.lbd) break;
3092 --num_deleted_clauses;
3093 ++num_kept_clauses;
3094 }
3095 }
3096 if (num_deleted_clauses > 0) {
3097 entries.resize(num_deleted_clauses);
3098 for (const Entry& entry : entries) {
3099 SatClause* clause = entry.first;
3100 counters_.num_literals_forgotten += clause->size();
3101 clauses_propagator_->LazyDelete(clause,
3102 DeletionSourceForStat::GARBAGE_COLLECTED);
3103 }
3104 clauses_propagator_->CleanUpWatchers();
3105
3106 // TODO(user): If the need arise, we could avoid this linear scan on the
3107 // full list of clauses by not keeping the clauses from clauses_info there.
3108 if (!block_clause_deletion_) {
3109 clauses_propagator_->DeleteRemovedClauses();
3110 }
3111 }
3112
3113 num_learned_clause_before_cleanup_ =
3114 parameters_->clause_cleanup_period() +
3115 (++counters_.num_cleanup_rounds) *
3116 parameters_->clause_cleanup_period_increment();
3117 VLOG(1) << "Database cleanup, #protected:" << num_protected_clauses
3118 << " #kept:" << num_kept_clauses
3119 << " #deleted:" << num_deleted_clauses;
3120 counters_.num_deleted_clauses += num_deleted_clauses;
3121}
3122
3124 switch (status) {
3126 return "ASSUMPTIONS_UNSAT";
3128 return "INFEASIBLE";
3130 return "FEASIBLE";
3132 return "LIMIT_REACHED";
3133 }
3134 // Fallback. We don't use "default:" so the compiler will return an error
3135 // if we forgot one enum case above.
3136 LOG(DFATAL) << "Invalid SatSolver::Status " << status;
3137 return "UNKNOWN";
3138}
3139
3140void MinimizeCore(SatSolver* solver, std::vector<Literal>* core) {
3141 std::vector<Literal> result;
3142 if (!solver->ResetToLevelZero()) return;
3143 for (const Literal lit : *core) {
3144 if (solver->Assignment().LiteralIsTrue(lit)) continue;
3145 result.push_back(lit);
3146 if (solver->Assignment().LiteralIsFalse(lit)) break;
3147 if (!solver->EnqueueDecisionIfNotConflicting(lit)) break;
3148 }
3149 if (result.size() < core->size()) {
3150 VLOG(1) << "minimization " << core->size() << " -> " << result.size();
3151 *core = result;
3152 }
3153}
3154
3155} // namespace sat
3156} // namespace operations_research
void Restart()
Definition timer.h:34
void Resize(IndexType size)
Definition bitset.h:474
ConstView const_view() const
Definition bitset.h:459
void Set(IndexType i)
Definition bitset.h:543
bool AddBinaryClause(ClauseId id, Literal a, Literal b, bool delete_non_representative_id=true)
Definition clause.h:660
SatClause * ReasonClauseOrNull(BooleanVariable var) const
Definition clause.cc:299
BooleanVariable Variable() const
Definition sat_base.h:88
UpperBoundedLinearConstraint * ReasonPbConstraint(int trail_index) const
void BumpActivity(UpperBoundedLinearConstraint *constraint)
static constexpr ConflictMinimizationAlgorithm NONE
static constexpr ClauseOrdering CLAUSE_LBD
static constexpr BinaryMinizationAlgorithm NO_BINARY_MINIMIZATION
static constexpr ConflictMinimizationAlgorithm SIMPLE
static constexpr BinaryMinizationAlgorithm BINARY_MINIMIZATION_FROM_UIP
static constexpr BinaryMinizationAlgorithm BINARY_MINIMIZATION_FROM_UIP_AND_DECISIONS
static constexpr ConflictMinimizationAlgorithm RECURSIVE
Status EnqueueDecisionAndBacktrackOnConflict(Literal true_literal, int *first_propagation_index=nullptr)
void LoadDebugSolution(absl::Span< const Literal > solution)
Status SolveWithTimeLimit(TimeLimit *time_limit)
const std::vector< BinaryClause > & NewlyAddedBinaryClauses()
bool AddProblemClause(absl::Span< const Literal > literals, bool shared=false)
bool AddBinaryClauses(absl::Span< const BinaryClause > clauses)
bool ResetWithGivenAssumptions(const std::vector< Literal > &assumptions)
const std::vector< LiteralWithTrailIndex > & Decisions() const
Definition sat_solver.h:415
std::vector< Literal > GetLastIncompatibleDecisions()
bool AddBinaryClause(Literal a, Literal b)
void Backtrack(int target_level)
const SatParameters & parameters() const
bool EnqueueDecisionIfNotConflicting(Literal true_literal)
bool AddClauseDuringSearch(absl::Span< const Literal > literals)
ABSL_MUST_USE_RESULT bool Propagate()
std::vector< Literal > GetDecisionsFixing(absl::Span< const Literal > literals)
bool AddLinearConstraint(bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, std::vector< Literal > *enforcement_literals, std::vector< LiteralWithCoeff > *cst)
void SetAssumptionLevel(int assumption_level)
ABSL_MUST_USE_RESULT bool FinishPropagation(std::optional< ConflictCallback > callback=std::nullopt)
ABSL_MUST_USE_RESULT bool AddUnitClause(Literal true_literal)
bool AddTernaryClause(Literal a, Literal b, Literal c)
void AdvanceDeterministicTime(TimeLimit *limit)
Definition sat_solver.h:502
Status ResetAndSolveWithGivenAssumptions(const std::vector< Literal > &assumptions, int64_t max_number_of_conflicts=-1)
const VariablesAssignment & Assignment() const
Definition sat_solver.h:420
ABSL_MUST_USE_RESULT bool ResetToLevelZero()
void AddLastPropagator(SatPropagator *propagator)
void ProcessCurrentConflict(std::optional< ConflictCallback > callback=std::nullopt)
bool BacktrackAndPropagateReimplications(int target_level)
Definition sat_solver.h:327
void SetNumVariables(int num_variables)
Definition sat_solver.cc:90
int EnqueueDecisionAndBackjumpOnConflict(Literal true_literal, std::optional< ConflictCallback > callback=std::nullopt)
void AddPropagator(SatPropagator *propagator)
void SetParameters(const SatParameters &parameters)
BooleanVariable ReferenceVarWithSameReason(BooleanVariable var) const
Definition sat_base.h:930
int AssignmentType(BooleanVariable var) const
Definition sat_base.h:942
void EnqueueWithUnitReason(Literal true_literal)
Definition sat_base.h:456
bool ChronologicalBacktrackingEnabled() const
Definition sat_base.h:678
const AssignmentInfo & Info(BooleanVariable var) const
Definition sat_base.h:655
const VariablesAssignment & Assignment() const
Definition sat_base.h:654
bool LiteralIsAssigned(Literal literal) const
Definition sat_base.h:209
bool LiteralIsFalse(Literal literal) const
Definition sat_base.h:203
bool LiteralIsTrue(Literal literal) const
Definition sat_base.h:206
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition stl_util.h:55
std::tuple< int64_t, int64_t, const double > Coefficient
std::string SatStatusString(SatSolver::Status status)
Coefficient ComputeNegatedCanonicalRhs(Coefficient lower_bound, Coefficient bound_shift, Coefficient max_value)
bool BooleanLinearExpressionIsCanonical(absl::Span< const Literal > enforcement_literals, absl::Span< const LiteralWithCoeff > cst)
constexpr ClauseId kNoClauseId(0)
Coefficient ComputeCanonicalRhs(Coefficient upper_bound, Coefficient bound_shift, Coefficient max_value)
bool IsStrictlyIncluded(Bitset64< LiteralIndex >::ConstView in_subset, int subset_size, absl::Span< const Literal > superset)
Definition util.h:897
void MinimizeCore(SatSolver *solver, std::vector< Literal > *core)
bool ComputeBooleanLinearExpressionCanonicalForm(std::vector< LiteralWithCoeff > *cst, Coefficient *bound_shift, Coefficient *max_value)
OR-Tools root namespace.
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
ClosedInterval::Iterator end(ClosedInterval interval)
const bool DEBUG_MODE
Definition radix_sort.h:266
std::string ProtobufShortDebugString(const P &message)
Definition proto_utils.h:46
std::string MemoryUsage()
Definition stats.cc:32
ClosedInterval::Iterator begin(ClosedInterval interval)
bool SafeAddInto(IntegerType a, IntegerType *b)
STL namespace.
trees with all degrees equal w the current value of w
static int input(yyscan_t yyscanner)
#define IF_STATS_ENABLED(instructions)
Definition stats.h:412
#define SCOPED_TIME_STAT(stats)
Definition stats.h:419
#define SOLVER_LOG(logger,...)
Definition logging.h:114