Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
sat_base.h
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
14// Basic types and classes used by the sat solver.
15
16#ifndef ORTOOLS_SAT_SAT_BASE_H_
17#define ORTOOLS_SAT_SAT_BASE_H_
18
19#include <algorithm>
20#include <cstdint>
21#include <deque>
22#include <functional>
23#include <ostream>
24#include <string>
25#include <utility>
26#include <vector>
27
28#include "absl/base/attributes.h"
29#include "absl/log/check.h"
30#include "absl/strings/str_format.h"
31#include "absl/types/span.h"
35#include "ortools/util/bitset.h"
37
38namespace operations_research {
39namespace sat {
40
41// Index of a variable (>= 0).
42DEFINE_STRONG_INDEX_TYPE(BooleanVariable);
43const BooleanVariable kNoBooleanVariable(-1);
44
45// Index of a literal (>= 0), see Literal below.
47const LiteralIndex kNoLiteralIndex(-1);
48
49// Special values used in some API to indicate a literal that is always true
50// or always false.
51const LiteralIndex kTrueLiteralIndex(-2);
52const LiteralIndex kFalseLiteralIndex(-3);
53
54// A literal is used to represent a variable or its negation. If it represents
55// the variable it is said to be positive. If it represent its negation, it is
56// said to be negative. We support two representations as an integer.
57//
58// The "signed" encoding of a literal is convenient for input/output and is used
59// in the cnf file format. For a 0-based variable index x, (x + 1) represent the
60// variable x and -(x + 1) represent its negation. The signed value 0 is an
61// undefined literal and this class can never contain it.
62//
63// The "index" encoding of a literal is convenient as an index to an array
64// and is the one used internally for efficiency. It is always positive or zero,
65// and for a 0-based variable index x, (x << 1) encode the variable x and the
66// same number XOR 1 encode its negation.
67class Literal {
68 public:
69 explicit Literal(int signed_value)
70 : index_(signed_value > 0 ? ((signed_value - 1) << 1)
71 : ((-signed_value - 1) << 1) ^ 1) {
72 CHECK_NE(signed_value, 0);
73 }
74
75 Literal() = default;
76 explicit Literal(LiteralIndex index) : index_(index.value()) {}
77 Literal(BooleanVariable variable, bool is_positive)
78 : index_(is_positive ? (variable.value() << 1)
79 : (variable.value() << 1) ^ 1) {}
80
81 // We want a literal to be implicitly converted to a LiteralIndex().
82 // Before this, we used to have many literal.Index() that didn't add anything.
83 //
84 // TODO(user): LiteralIndex might not even be needed, but because of the
85 // signed value business, it is still safer with it.
86 operator LiteralIndex() const { return Index(); } // NOLINT
87
88 BooleanVariable Variable() const { return BooleanVariable(index_ >> 1); }
89 bool IsPositive() const { return !(index_ & 1); }
90 bool IsNegative() const { return (index_ & 1); }
91
92 LiteralIndex Index() const { return LiteralIndex(index_); }
93 LiteralIndex NegatedIndex() const { return LiteralIndex(index_ ^ 1); }
94
95 int SignedValue() const {
96 return (index_ & 1) ? -((index_ >> 1) + 1) : ((index_ >> 1) + 1);
97 }
98
99 Literal Negated() const { return Literal(NegatedIndex()); }
100
101 std::string DebugString() const {
102 if (index_ == kNoLiteralIndex.value()) return "NA";
103 return absl::StrFormat("%+d", SignedValue());
104 }
105
106 bool operator==(Literal other) const { return index_ == other.index_; }
107 bool operator!=(Literal other) const { return index_ != other.index_; }
108 bool operator<(const Literal& other) const { return index_ < other.index_; }
109
110 template <typename H>
111 friend H AbslHashValue(H h, Literal literal) {
112 return H::combine(std::move(h), literal.index_);
113 }
114
115 private:
116 int index_;
117};
118
119inline std::ostream& operator<<(std::ostream& os, Literal literal) {
120 os << literal.DebugString();
121 return os;
122}
123
124template <typename Sink, typename... T>
125void AbslStringify(Sink& sink, Literal arg) {
126 absl::Format(&sink, "%s", arg.DebugString());
127}
128
129inline std::ostream& operator<<(std::ostream& os,
130 absl::Span<const Literal> literals) {
131 os << "[";
132 bool first = true;
133 for (const Literal literal : literals) {
134 if (first) {
135 first = false;
136 } else {
137 os << ",";
138 }
139 os << literal.DebugString();
140 }
141 os << "]";
142 return os;
143}
144
145inline std::ostream& operator<<(std::ostream& os,
146 absl::Span<const LiteralIndex> literals) {
147 os << "[";
148 bool first = true;
149 for (const LiteralIndex index : literals) {
150 if (first) {
151 first = false;
152 } else {
153 os << ",";
154 }
155 os << Literal(index).DebugString();
156 }
157 os << "]";
158 return os;
159}
160
161// Only used for testing to use the classical SAT notation for a literal. This
162// allows to write Literals({+1, -4, +3}) for the clause with BooleanVariable 0
163// and 2 appearing positively and 3 negatively.
164inline std::vector<Literal> Literals(absl::Span<const int> input) {
165 std::vector<Literal> result(input.size());
166 for (int i = 0; i < result.size(); ++i) {
167 result[i] = Literal(input[i]);
168 }
169 return result;
170}
171
172// Holds the current variable assignment of the solver.
173// Each variable can be unassigned or be assigned to true or false.
175 public:
177 explicit VariablesAssignment(int num_variables) { Resize(num_variables); }
178
179 // This type is neither copyable nor movable.
182 void Resize(int num_variables) {
183 assignment_.Resize(LiteralIndex(num_variables << 1));
184 }
185
186 // Makes the given literal true by assigning its underlying variable to either
187 // true or false depending on the literal sign. This can only be called on an
188 // unassigned variable.
190 DCHECK(!VariableIsAssigned(literal.Variable()));
191 assignment_.Set(literal.Index());
192 }
193
194 // Unassign the variable corresponding to the given literal.
195 // This can only be called on an assigned variable.
196 void UnassignLiteral(Literal literal) {
197 DCHECK(VariableIsAssigned(literal.Variable()));
198 assignment_.ClearTwoBits(literal.Index());
199 }
200
201 // Literal getters. Note that both can be false, in which case the
202 // corresponding variable is not assigned.
203 bool LiteralIsFalse(Literal literal) const {
204 return assignment_.IsSet(literal.NegatedIndex());
205 }
206 bool LiteralIsTrue(Literal literal) const {
207 return assignment_.IsSet(literal.Index());
208 }
209 bool LiteralIsAssigned(Literal literal) const {
210 return assignment_.AreOneOfTwoBitsSet(literal.Index());
211 }
212
213 // Returns true iff the given variable is assigned.
214 bool VariableIsAssigned(BooleanVariable var) const {
215 return assignment_.AreOneOfTwoBitsSet(LiteralIndex(var.value() << 1));
216 }
217
218 // Returns the literal of the given variable that is assigned to true.
219 // That is, depending on the variable, it can be the positive literal or the
220 // negative one. Only call this on an assigned variable.
221 Literal GetTrueLiteralForAssignedVariable(BooleanVariable var) const {
222 DCHECK(VariableIsAssigned(var));
223 return Literal(var, assignment_.IsSet(LiteralIndex(var.value() << 1)));
224 }
225
226 int NumberOfVariables() const { return assignment_.size().value() / 2; }
227
228 // Expose internal for performance critical code.
229 // You should not use this in normal code.
231
232 private:
233 // The encoding is as follows:
234 // - assignment_.IsSet(literal.Index()) means literal is true.
235 // - assignment_.IsSet(literal.Index() ^ 1]) means literal is false.
236 // - If both are false, then the variable (and the literal) is unassigned.
237 Bitset64<LiteralIndex> assignment_;
238
239 friend class AssignmentView;
240};
241
242// For "hot" loop, it is better not to reload the Bitset64 pointer on each
243// check.
245 public:
246 explicit AssignmentView(const VariablesAssignment& assignment)
247 : view_(assignment.assignment_.const_view()) {}
248
249 bool LiteralIsFalse(Literal literal) const {
250 return view_[literal.NegatedIndex()];
251 }
252
253 bool LiteralIsTrue(Literal literal) const { return view_[literal.Index()]; }
254
255 private:
257};
258
259// The ID of a clause.
260DEFINE_STRONG_INT_TYPE(ClauseId, int64_t);
261constexpr ClauseId kNoClauseId(0);
262
263// A generator for ClauseIds. Not thread-safe.
265 public:
266 ClauseId GetNextId() { return ClauseId(next_id_++); }
267
268 private:
269 ClauseId next_id_ = ClauseId(1);
270};
271
272// Forward declaration.
273class SatClause;
274class SatPropagator;
275
276// Information about a variable assignment.
278 // The decision level at which this assignment was made. This starts at 0 and
279 // increases each time the solver takes a search decision.
280 //
281 // TODO(user): We may be able to get rid of that for faster enqueues. Most of
282 // the code only need to know if this is 0 or the highest level, and for the
283 // LBD computation, the literal of the conflict are already ordered by level,
284 // so we could do it fairly efficiently.
285 //
286 // TODO(user): We currently don't support more than 2^28 decision levels. That
287 // should be enough for most practical problem, but we should fail properly if
288 // this limit is reached.
289 uint32_t level : 28;
290
291 // The type of assignment (see AssignmentType below).
292 //
293 // Note(user): We currently don't support more than 16 types of assignment.
294 // This is checked in RegisterPropagator().
295 mutable uint32_t type : 4;
296
297 // The index of this assignment in the trail.
298 int32_t trail_index;
299
300 std::string DebugString() const {
301 return absl::StrFormat("level:%d type:%d trail_index:%d", level, type,
303 }
304};
305static_assert(sizeof(AssignmentInfo) == 8,
306 "ERROR_AssignmentInfo_is_not_well_compacted");
307
308// Each literal on the trail will have an associated propagation "type" which is
309// either one of these special types or the id of a propagator.
311 static constexpr int kCachedReason = 0;
312 static constexpr int kUnitReason = 1;
313 static constexpr int kSearchDecision = 2;
314 static constexpr int kSameReasonAs = 3;
315
316 // Propagator ids starts from there and are created dynamically.
317 static constexpr int kFirstFreePropagationId = 4;
318};
319
320// A Boolean "decision" taken by the solver.
327
328// The solver trail stores the assignment made by the solver in order.
329// This class is responsible for maintaining the assignment of each variable
330// and the information of each assignment.
331class Trail {
332 public:
334 current_info_.trail_index = 0;
335 current_info_.level = 0;
336 }
337
338 // This type is neither copyable nor movable.
339 Trail(const Trail&) = delete;
340 Trail& operator=(const Trail&) = delete;
341
342 void Resize(int num_variables);
343
344 // Registers a propagator. This assigns a unique id to this propagator and
345 // calls SetPropagatorId() on it.
346 void RegisterPropagator(SatPropagator* propagator);
347
348 // Enqueues the assignment that make the given literal true on the trail. This
349 // should only be called on unassigned variables.
350 void Enqueue(Literal true_literal, int propagator_id) {
351 DCHECK(!assignment_.VariableIsAssigned(true_literal.Variable()));
352 trail_[current_info_.trail_index] = true_literal;
353 current_info_.type = propagator_id;
354 info_[true_literal.Variable()] = current_info_;
355 assignment_.AssignFromTrueLiteral(true_literal);
356 ++current_info_.trail_index;
357 }
358 void EnqueueAtLevel(Literal true_literal, int propagator_id, int level) {
359 Enqueue(true_literal, propagator_id);
360 if (use_chronological_backtracking_) {
361 info_[true_literal.Variable()].level = level;
362 }
363 }
364
365 // Using this is faster as it caches all the vectors data.
366 // Warning: call to this cannot be interleaved with normal enqueue.
367 // only use in hot-loops.
369 public:
371 Literal* trail_ptr, AssignmentInfo* current_info,
372 AssignmentInfo* info_ptr, VariablesAssignment* assignment,
374 : trail_ptr_(trail_ptr),
375 current_info_(current_info),
376 info_ptr_(info_ptr),
377 bitset_(assignment->GetBitsetView()),
378 clause_ids_(clause_ids) {}
379
380 void EnqueueAtLevel(Literal true_literal, int level) {
381 bitset_.Set(true_literal);
382 AssignmentInfo* info = info_ptr_ + true_literal.Variable().value();
383 *info = *current_info_;
384 info->level = level;
385 trail_ptr_[current_info_->trail_index++] = true_literal;
386 }
387
388 void EnqueueWithUnitReason(Literal true_literal, ClauseId clause_id) {
389 DCHECK_NE(clause_id, kNoClauseId);
390 const BooleanVariable var = true_literal.Variable();
391 if (var.value() >= clause_ids_->size()) {
392 clause_ids_->resize(var.value() + 1, kNoClauseId);
393 }
394 (*clause_ids_)[var] = clause_id;
395
396 bitset_.Set(true_literal);
397 AssignmentInfo* info = info_ptr_ + true_literal.Variable().value();
398 *info = *current_info_;
399 info->level = 0;
401 trail_ptr_[current_info_->trail_index++] = true_literal;
402 }
403
404 bool LiteralIsTrue(Literal literal) const {
405 return bitset_[literal.Index()];
406 }
407 bool LiteralIsFalse(Literal literal) const {
408 return bitset_[literal.NegatedIndex()];
409 }
410
411 private:
412 Literal* trail_ptr_;
413 AssignmentInfo* current_info_;
414 AssignmentInfo* info_ptr_;
417 };
418 EnqueueHelper GetEnqueueHelper(int propagator_id) {
419 current_info_.type = propagator_id;
420 return EnqueueHelper(trail_.data(), &current_info_, info_.data(),
421 &assignment_, &clause_ids_);
422 }
423
424 // Specific Enqueue() for search decisions.
425 void EnqueueSearchDecision(Literal true_literal) {
426 decisions_[current_decision_level_] =
427 LiteralWithTrailIndex(true_literal, Index());
428 current_info_.level = ++current_decision_level_;
430 }
431
432 // Specific Enqueue() for assumptions.
433 void EnqueueAssumption(Literal assumptions) {
434 if (current_decision_level_ == 0) {
435 // Special decision. This should never be accessed.
436 decisions_[0] = LiteralWithTrailIndex(Literal(), Index());
437 current_info_.level = ++current_decision_level_;
438 }
439 CHECK_EQ(current_decision_level_, 1);
441 }
442
443 void OverrideDecision(int level, Literal literal) {
444 decisions_[level].literal = literal;
445 }
446
447 // Allows to recover the list of decisions.
448 // Note that the Decisions() vector is always of size NumVariables(), and that
449 // only the first CurrentDecisionLevel() entries have a meaning. The decision
450 // made at level l is Decisions()[l - 1] (there are no decisions at level 0).
451 const std::vector<LiteralWithTrailIndex>& Decisions() const {
452 return decisions_;
453 }
454
455 // Specific Enqueue() version for a fixed variable.
456 void EnqueueWithUnitReason(Literal true_literal) {
458 }
459
460 // Specific Enqueue() version for unit clauses.
461 void EnqueueWithUnitReason(ClauseId clause_id, Literal true_literal) {
462 MaybeSetClauseId(true_literal, clause_id);
464 }
465
466 // Some constraints propagate a lot of literals at once. In these cases, it is
467 // more efficient to have all the propagated literals except the first one
468 // referring to the reason of the first of them.
470 BooleanVariable reference_var) {
471 reference_var_with_same_reason_as_[true_literal.Variable()] = reference_var;
474 info_[true_literal.Variable()].level = Info(reference_var).level;
475 }
476 }
477
478 // Enqueues the given literal using the current content of
479 // GetEmptyVectorToStoreReason() as the reason. This API is a bit more
480 // lenient and does not require the literal to be unassigned. If it is
481 // already assigned to false, then MutableConflict() will be set appropriately
482 // and this will return false otherwise this will enqueue the literal and
483 // returns true.
484 ABSL_MUST_USE_RESULT bool EnqueueWithStoredReason(ClauseId clause_id,
485 Literal true_literal) {
486 if (assignment_.LiteralIsTrue(true_literal)) return true;
487 if (assignment_.LiteralIsFalse(true_literal)) {
488 *MutableConflict() = reasons_repository_[Index()];
489 MutableConflict()->push_back(true_literal);
490 failing_clause_id_ = clause_id;
491 return false;
492 }
493
494 MaybeSetClauseId(true_literal, clause_id);
496 const BooleanVariable var = true_literal.Variable();
497 reasons_[var] = reasons_repository_[info_[var].trail_index];
498 old_type_[var] = info_[var].type;
499 info_[var].type = AssignmentType::kCachedReason;
500 DCHECK_EQ(old_type_[var], AssignmentType::kCachedReason);
502 uint32_t level = 0;
503 for (const Literal literal : reasons_[var]) {
504 level = std::max(level, Info(literal.Variable()).level);
505 }
506 info_[var].level = level;
507 }
508 return true;
509 }
510
511 // Returns the reason why this variable was assigned.
512 //
513 // Note that this shouldn't be called on a variable at level zero, because we
514 // don't cleanup the reason data for these variables but the underlying
515 // clauses may have been deleted.
516 //
517 // If conflict_id >= 0, this indicate that this was called as part of the
518 // first-UIP procedure. It has a few implication:
519 // - The reason do not need to be cached and can be adapted to the current
520 // conflict.
521 // - Some data can be reused between two calls about the same conflict.
522 // - Note however that if the reason is a simple clause, we shouldn't adapt
523 // it because we rely on extra fact in the first UIP code where we detect
524 // subsumed clauses for instance.
525 absl::Span<const Literal> Reason(BooleanVariable var,
526 int64_t conflict_id = -1) const;
527
528 // Returns the "type" of an assignment (see AssignmentType). Note that this
529 // function never returns kSameReasonAs or kCachedReason, it instead returns
530 // the initial type that caused this assignment. As such, it is different
531 // from Info(var).type and the latter should not be used outside this class.
532 int AssignmentType(BooleanVariable var) const;
533
534 // Returns the ID of the clause which is the reason for the unit clause
535 // containing the given variable, or kNoClauseId if there is none. The
536 // variable must have been enqueued at level zero with a kUnitReason.
537 ClauseId GetUnitClauseId(BooleanVariable var) const {
539 DCHECK_EQ(Info(var).level, 0);
540 if (var.value() >= clause_ids_.size()) return kNoClauseId;
541 return clause_ids_[var];
542 }
543
544 // Returns the ID of the clause which is the reason why the given variable was
545 // enqueued, or kNoClauseId if there is none. The variable must have been
546 // enqueued with EnqueueWithStoredReason().
547 ClauseId GetStoredReasonClauseId(BooleanVariable var) const {
549 if (var.value() >= clause_ids_.size()) return kNoClauseId;
550 return clause_ids_[var];
551 }
552
553 // If a variable was propagated with EnqueueWithSameReasonAs(), returns its
554 // reference variable. Otherwise return the given variable.
555 BooleanVariable ReferenceVarWithSameReason(BooleanVariable var) const;
556
557 // This can be used to get a location at which the reason for the literal
558 // at trail_index on the trail can be stored. This clears the vector before
559 // returning it.
560 std::vector<Literal>* GetEmptyVectorToStoreReason(int trail_index) const {
561 if (trail_index >= reasons_repository_.size()) {
562 reasons_repository_.resize(trail_index + 1);
563 }
564 reasons_repository_[trail_index].clear();
565 return &reasons_repository_[trail_index];
566 }
567
568 // Shortcut for GetEmptyVectorToStoreReason(Index()).
569 std::vector<Literal>* GetEmptyVectorToStoreReason() const {
571 }
572
573 // Explicitly overwrite the reason so that the given propagator will be
574 // asked for it. This is currently only used by the BinaryImplicationGraph.
575 // Note: Care must be taken not to break the lrat proof!
576 void ChangeReason(int trail_index, int propagator_id) {
577 const BooleanVariable var = trail_[trail_index].Variable();
578 info_[var].type = propagator_id;
579 old_type_[var] = propagator_id;
580 }
581
582 // On bactrack we should always do:
583 //
584 // const int target_trail_index = PrepareBacktrack(level);
585 // ...
586 // Untrail(target_trail_index);
587 int PrepareBacktrack(int level) {
588 current_decision_level_ = level;
589 current_info_.level = level;
590 return decisions_[level].trail_index;
591 }
592
593 // Reverts the trail and underlying assignment to the given target trail
594 // index. Note that we do not touch the assignment info.
595 void Untrail(int target_trail_index) {
596 const int index = Index();
597 num_untrailed_enqueues_ += index - target_trail_index;
598 for (int i = target_trail_index; i < index; ++i) {
599 assignment_.UnassignLiteral(trail_[i]);
600 }
601 current_info_.trail_index = target_trail_index;
602 if (use_chronological_backtracking_) {
603 ReimplyAll(index);
604 }
605 }
606
607 int CurrentDecisionLevel() const { return current_info_.level; }
608
609 // Generic interface to set the current failing clause.
610 //
611 // Returns the address of a vector where a client can store the current
612 // conflict. This vector will be returned by the FailingClause() call.
613 std::vector<Literal>* MutableConflict() {
614 ++conflict_timestamp_;
615 failing_sat_clause_ = nullptr;
616 failing_clause_id_ = kNoClauseId;
617 return &conflict_;
618 }
619
620 // This should increase on each call to MutableConflict().
621 int64_t conflict_timestamp() const { return conflict_timestamp_; }
622
623 // Returns the last conflict.
624 absl::Span<const Literal> FailingClause() const {
625 if (DEBUG_MODE && debug_checker_ != nullptr) {
626 CHECK(debug_checker_(conflict_));
627 }
628 return conflict_;
629 }
630
631 // Specific SatClause interface so we can update the conflict clause activity.
632 // Note that MutableConflict() automatically sets this to nullptr, so we can
633 // know whether or not the last conflict was caused by a clause.
635 failing_sat_clause_ = clause;
636 failing_clause_id_ = kNoClauseId;
637 }
638 SatClause* FailingSatClause() const { return failing_sat_clause_; }
639
640 // Returns the LRAT ID of the failing clause. This ID is only set if a
641 // conflict is detected in EnqueueWithStoredReason().
642 ClauseId FailingClauseId() const { return failing_clause_id_; }
643
644 // Getters.
645 int NumVariables() const { return trail_.size(); }
646 int64_t NumberOfEnqueues() const { return num_untrailed_enqueues_ + Index(); }
647 int Index() const { return current_info_.trail_index; }
648 // This accessor can return trail_.end(). operator[] cannot. This allows
649 // normal std:vector operations, such as assign(begin, end).
650 std::vector<Literal>::const_iterator IteratorAt(int index) const {
651 return trail_.begin() + index;
652 }
653 const Literal& operator[](int index) const { return trail_[index]; }
654 const VariablesAssignment& Assignment() const { return assignment_; }
655 const AssignmentInfo& Info(BooleanVariable var) const {
656 DCHECK_GE(var, 0);
657 DCHECK_LT(var, info_.size());
658 return info_[var];
659 }
660
661 int AssignmentLevel(Literal lit) const { return Info(lit.Variable()).level; }
662
663 // Print the current literals on the trail.
664 std::string DebugString() const {
665 std::string result;
666 for (int i = 0; i < current_info_.trail_index; ++i) {
667 if (!result.empty()) result += " ";
668 result += trail_[i].DebugString();
669 }
670 return result;
671 }
672
674 std::function<bool(absl::Span<const Literal> clause)> checker) {
675 debug_checker_ = std::move(checker);
676 }
677
679 return use_chronological_backtracking_;
680 }
681
683 CHECK_EQ(CurrentDecisionLevel(), 0);
684 use_chronological_backtracking_ = enable;
685 }
686
687 using ConflictResolutionFunction = std::function<void(
688 std::vector<Literal>* conflict,
689 std::vector<Literal>* reason_used_to_infer_the_conflict,
690 std::vector<SatClause*>* subsumed_clauses)>;
691
693 resolution_ = std::move(resolution);
694 }
695
699
700 int NumReimplicationsOnLastUntrail() const { return last_num_reimplication_; }
701
702 private:
703 ConflictResolutionFunction resolution_;
704
705 void MaybeSetClauseId(Literal true_literal, ClauseId clause_id) {
706 if (clause_id != kNoClauseId) {
707 const BooleanVariable var = true_literal.Variable();
708 if (var.value() >= clause_ids_.size()) {
709 clause_ids_.resize(var.value() + 1, kNoClauseId);
710 }
711 clause_ids_[var] = clause_id;
712 }
713 }
714
715 // Finds all literals between the current trail index and the given one
716 // assigned at the current level or lower, and re-enqueues them with the same
717 // reason.
718 void ReimplyAll(int old_trail_index);
719
720 bool use_chronological_backtracking_ = false;
721 int64_t num_reimplied_literals_ = 0;
722 int64_t num_untrailed_enqueues_ = 0;
723 AssignmentInfo current_info_;
724 VariablesAssignment assignment_;
725 std::vector<Literal> trail_;
726 int64_t conflict_timestamp_ = 0;
727 std::vector<Literal> conflict_;
728 util_intops::StrongVector<BooleanVariable, AssignmentInfo> info_;
729 // The ID of unit clauses (literals enqueued at level 0 with a kUnitReason)
730 // and of reason clauses for literals enqueued with a stored reason.
731 util_intops::StrongVector<BooleanVariable, ClauseId> clause_ids_;
732 SatClause* failing_sat_clause_;
733 ClauseId failing_clause_id_;
734
735 // Data used by EnqueueWithSameReasonAs().
736 util_intops::StrongVector<BooleanVariable, BooleanVariable>
737 reference_var_with_same_reason_as_;
738
739 // Reason cache. Mutable since we want the API to be the same whether the
740 // reason are cached or not.
741 //
742 // When a reason is computed for the first time, we change the type of the
743 // variable assignment to kCachedReason so that we know that if it is needed
744 // again the reason can just be retrieved by a direct access to reasons_. The
745 // old type is saved in old_type_ and can be retrieved by
746 // AssignmentType().
747 //
748 // Note(user): Changing the type is not "clean" but it is efficient. The idea
749 // is that it is important to do as little as possible when pushing/popping
750 // literals on the trail. Computing the reason happens a lot less often, so it
751 // is okay to do slightly more work then. Note also, that we don't need to
752 // do anything on "untrail", the kCachedReason type will be overwritten when
753 // the same variable is assigned again.
754 //
755 // TODO(user): An alternative would be to change the sign of the type. This
756 // would remove the need for a separate old_type_ vector, but it requires
757 // more bits for the type filed in AssignmentInfo.
758 //
759 // Note that we use a deque for the reason repository so that if we add
760 // variables, the memory address of the vectors (kept in reasons_) are still
761 // valid.
762 mutable std::deque<std::vector<Literal>> reasons_repository_;
763 mutable util_intops::StrongVector<BooleanVariable, absl::Span<const Literal>>
764 reasons_;
765 mutable util_intops::StrongVector<BooleanVariable, int> old_type_;
766
767 // This is used by RegisterPropagator() and Reason().
768 std::vector<SatPropagator*> propagators_;
769
770 std::function<bool(absl::Span<const Literal> clause)> debug_checker_ =
771 nullptr;
772
773 int last_num_reimplication_ = 0;
774
775 // The stack of decisions taken by the solver. They are stored in [0,
776 // current_decision_level_). The vector is of size num_variables_ so it can
777 // store all the decisions. This is done this way because in some situation we
778 // need to remember the previously taken decisions after a backtrack.
779 int current_decision_level_ = 0;
780 std::vector<LiteralWithTrailIndex> decisions_;
781};
782
783// Base class for all the SAT constraints.
785 public:
786 explicit SatPropagator(const std::string& name)
788
789 // This type is neither copyable nor movable.
790 SatPropagator(const SatPropagator&) = delete;
792 virtual ~SatPropagator() = default;
793
794 // Sets/Gets this propagator unique id.
795 void SetPropagatorId(int id) { propagator_id_ = id; }
796 int PropagatorId() const { return propagator_id_; }
797
798 // Inspects the trail from propagation_trail_index_ until at least one literal
799 // is propagated. Returns false iff a conflict is detected (in which case
800 // trail->SetFailingClause() must be called).
801 //
802 // This must update propagation_trail_index_ so that all the literals before
803 // it have been propagated. In particular, if nothing was propagated, then
804 // PropagationIsDone() must return true.
805 virtual bool Propagate(Trail* trail) = 0;
806
807 // Reverts the state so that all the literals with a trail index greater or
808 // equal to the given one are not processed for propagation. Note that the
809 // trail current decision level is already reverted before this is called.
810 //
811 // TODO(user): Currently this is called at each Backtrack(), but we could
812 // bundle the calls in case multiple conflict one after the other are detected
813 // even before the Propagate() call of a SatPropagator is called.
814 //
815 // TODO(user): It is not yet 100% the case, but this can be guaranteed to be
816 // called with a trail index that will always be the start of a new decision
817 // level.
818 virtual void Untrail(const Trail& /*trail*/, int trail_index) {
819 propagation_trail_index_ = std::min(propagation_trail_index_, trail_index);
820 }
821
822 // Called if the implication at `old_trail_index` remains true after
823 // backtracking. If this propagator supports reimplication it should call
824 // `trail->EnqueueAtLevel`.
825 // This will be called after Untrail() when backtracking.
826 virtual void Reimply(Trail* /*trail*/, int /*old_trail_index*/) {
827 // It is inefficient and unexpected to call this on a propagator that
828 // doesn't support reimplication.
829 LOG(DFATAL) << "Reimply not implemented for " << name_ << ".";
830 }
831
832 // Explains why the literal at given trail_index was propagated by returning a
833 // reason for this propagation. This will only be called for literals that are
834 // on the trail and were propagated by this class.
835 //
836 // The interpretation is that because all the literals of a reason were
837 // assigned to false, we could deduce the assignment of the given variable.
838 //
839 // The returned Span has to be valid until the literal is untrailed. A client
840 // can use trail_.GetEmptyVectorToStoreReason() if it doesn't have a memory
841 // location that already contains the reason.
842 //
843 // If conlict id is positive, then this is called during first UIP resolution
844 // and we will backtrack over this literal right away, so we don't need to
845 // have a span that survive more than once.
846 virtual absl::Span<const Literal> Reason(const Trail& /*trail*/,
847 int /*trail_index*/,
848 int64_t /*conflict_id*/) const {
849 LOG(FATAL) << "Not implemented.";
850 return {};
851 }
852
853 // Returns true if all the preconditions for Propagate() are satisfied.
854 // This is just meant to be used in a DCHECK.
855 bool PropagatePreconditionsAreSatisfied(const Trail& trail) const;
856
857 // Returns true iff all the trail was inspected by this propagator.
858 bool PropagationIsDone(const Trail& trail) const {
859 return propagation_trail_index_ == trail.Index();
860 }
861
862 const std::string& name() const { return name_; }
863
864 // Small optimization: If a propagator does not contain any "constraints"
865 // there is no point calling propagate on it. Before each propagation, the
866 // solver will checks for emptiness, and construct an optimized list of
867 // propagator before looping many time over the list.
868 virtual bool IsEmpty() const { return false; }
869
870 protected:
871 const std::string name_;
874};
875
876// ######################## Implementations below ########################
877
878// TODO(user): A few of these method should be moved in a .cc
879
881 const Trail& trail) const {
882 if (propagation_trail_index_ > trail.Index()) {
883 LOG(INFO) << "Issue in '" << name_ << ":"
884 << " propagation_trail_index_=" << propagation_trail_index_
885 << " trail_.Index()=" << trail.Index();
886 return false;
887 }
888 if (propagation_trail_index_ < trail.Index() &&
889 trail.Info(trail[propagation_trail_index_].Variable()).level >
890 trail.CurrentDecisionLevel()) {
891 LOG(INFO) << "Issue in '" << name_ << "':"
892 << " propagation_trail_index_=" << propagation_trail_index_
893 << " trail_.Index()=" << trail.Index()
894 << " level_at_propagation_index="
896 << " current_decision_level=" << trail.CurrentDecisionLevel();
897 return false;
898 }
899 return true;
900}
901
902inline void Trail::Resize(int num_variables) {
903 assignment_.Resize(num_variables);
904 info_.resize(num_variables);
905 trail_.resize(num_variables);
906 reasons_.resize(num_variables);
907
908 // TODO(user): these vectors are not always used. Initialize them
909 // dynamically.
910 old_type_.resize(num_variables);
911 reference_var_with_same_reason_as_.resize(num_variables);
912
913 // The +1 is a bit tricky, it is because in
914 // EnqueueDecisionAndBacktrackOnConflict() we artificially enqueue the
915 // decision before checking if it is not already assigned.
916 decisions_.resize(num_variables + 1);
917}
918
919inline void Trail::RegisterPropagator(SatPropagator* propagator) {
920 if (propagators_.empty()) {
921 propagators_.resize(AssignmentType::kFirstFreePropagationId);
922 }
923 CHECK_LT(propagators_.size(), 16);
924 VLOG(2) << "Registering propagator " << propagator->name() << " with id "
925 << propagators_.size();
926 propagator->SetPropagatorId(propagators_.size());
927 propagators_.push_back(propagator);
928}
929
931 BooleanVariable var) const {
932 DCHECK(Assignment().VariableIsAssigned(var));
933 // Note that we don't use AssignmentType() here.
934 if (info_[var].type == AssignmentType::kSameReasonAs) {
935 var = reference_var_with_same_reason_as_[var];
936 DCHECK(Assignment().VariableIsAssigned(var));
937 DCHECK_NE(info_[var].type, AssignmentType::kSameReasonAs);
938 }
939 return var;
940}
941
942inline int Trail::AssignmentType(BooleanVariable var) const {
943 if (info_[var].type == AssignmentType::kSameReasonAs) {
944 var = reference_var_with_same_reason_as_[var];
945 DCHECK_NE(info_[var].type, AssignmentType::kSameReasonAs);
946 }
947 const int type = info_[var].type;
948 return type != AssignmentType::kCachedReason ? type : old_type_[var];
949}
950
951inline absl::Span<const Literal> Trail::Reason(BooleanVariable var,
952 int64_t conflict_id) const {
953 // Special case for AssignmentType::kSameReasonAs to avoid a recursive call.
955
956 // Fast-track for cached reason.
957 if (info_[var].type == AssignmentType::kCachedReason) {
958 if (DEBUG_MODE && debug_checker_ != nullptr) {
959 std::vector<Literal> clause;
960 clause.assign(reasons_[var].begin(), reasons_[var].end());
961 clause.push_back(assignment_.GetTrueLiteralForAssignedVariable(var));
962 CHECK(debug_checker_(clause)) << " for cached reason";
963 }
964 return reasons_[var];
965 }
966
967 const AssignmentInfo& info = info_[var];
968 if (info.type == AssignmentType::kUnitReason ||
970 reasons_[var] = {};
971 } else {
972 DCHECK_LT(info.type, propagators_.size());
973 DCHECK(propagators_[info.type] != nullptr) << info.type;
974 reasons_[var] =
975 propagators_[info.type]->Reason(*this, info.trail_index, conflict_id);
976 }
977 old_type_[var] = info.type;
978 info_[var].type = AssignmentType::kCachedReason;
979 if (DEBUG_MODE && debug_checker_ != nullptr) {
980 std::vector<Literal> clause;
981 clause.assign(reasons_[var].begin(), reasons_[var].end());
982 clause.push_back(assignment_.GetTrueLiteralForAssignedVariable(var));
983 CHECK(debug_checker_(clause)) << "for propagator_id=" << old_type_[var];
984 }
985 return reasons_[var];
986}
987
988inline void Trail::ReimplyAll(int old_trail_index) {
989 const int64_t initial_num_reimplied = num_reimplied_literals_;
990 for (int i = Index(); i < old_trail_index; ++i) {
991 const Literal literal = trail_[i];
992 const AssignmentInfo& info = Info(literal.Variable());
993 if (info.level > current_info_.level) continue;
994 CHECK_LE(Index(), i);
995 CHECK(!Assignment().VariableIsAssigned(literal.Variable()));
997 // The reference variable must already be re-implied at this level, so we
998 // can just re-enqueue it without having to tell the propagator.
999 DCHECK_EQ(Info(ReferenceVarWithSameReason(literal.Variable())).level,
1000 info.level);
1001 DCHECK_LT(
1003 Index());
1005 } else {
1006 const int original_type = AssignmentType(literal.Variable());
1007 if (original_type >= AssignmentType::kFirstFreePropagationId) {
1008 propagators_[original_type]->Reimply(this, i);
1009 } else if (original_type == AssignmentType::kCachedReason) {
1010 std::swap(reasons_repository_[Index()], reasons_repository_[i]);
1011 reasons_[literal.Variable()] = reasons_repository_[Index()];
1012 EnqueueAtLevel(literal, original_type, info.level);
1013 } else if (info.type == AssignmentType::kUnitReason || info.level == 0) {
1014 CHECK(!Assignment().LiteralIsFalse(literal));
1016 }
1017 }
1018 num_reimplied_literals_ += assignment_.LiteralIsTrue(literal);
1019 }
1020 last_num_reimplication_ = num_reimplied_literals_ - initial_num_reimplied;
1021}
1022
1023} // namespace sat
1024} // namespace operations_research
1025
1026#endif // ORTOOLS_SAT_SAT_BASE_H_
AssignmentView(const VariablesAssignment &assignment)
Definition sat_base.h:246
bool LiteralIsFalse(Literal literal) const
Definition sat_base.h:249
bool LiteralIsTrue(Literal literal) const
Definition sat_base.h:253
bool operator<(const Literal &other) const
Definition sat_base.h:108
std::string DebugString() const
Definition sat_base.h:101
LiteralIndex NegatedIndex() const
Definition sat_base.h:93
bool operator!=(Literal other) const
Definition sat_base.h:107
friend H AbslHashValue(H h, Literal literal)
Definition sat_base.h:111
Literal(LiteralIndex index)
Definition sat_base.h:76
LiteralIndex Index() const
Definition sat_base.h:92
BooleanVariable Variable() const
Definition sat_base.h:88
Literal(BooleanVariable variable, bool is_positive)
Definition sat_base.h:77
bool operator==(Literal other) const
Definition sat_base.h:106
virtual void Reimply(Trail *, int)
Definition sat_base.h:826
virtual absl::Span< const Literal > Reason(const Trail &, int, int64_t) const
Definition sat_base.h:846
bool PropagatePreconditionsAreSatisfied(const Trail &trail) const
Definition sat_base.h:880
SatPropagator(const std::string &name)
Definition sat_base.h:786
virtual void Untrail(const Trail &, int trail_index)
Definition sat_base.h:818
virtual bool Propagate(Trail *trail)=0
bool PropagationIsDone(const Trail &trail) const
Definition sat_base.h:858
SatPropagator(const SatPropagator &)=delete
const std::string & name() const
Definition sat_base.h:862
SatPropagator & operator=(const SatPropagator &)=delete
void EnqueueWithUnitReason(Literal true_literal, ClauseId clause_id)
Definition sat_base.h:388
void EnqueueAtLevel(Literal true_literal, int level)
Definition sat_base.h:380
EnqueueHelper(Literal *trail_ptr, AssignmentInfo *current_info, AssignmentInfo *info_ptr, VariablesAssignment *assignment, util_intops::StrongVector< BooleanVariable, ClauseId > *clause_ids)
Definition sat_base.h:370
bool LiteralIsTrue(Literal literal) const
Definition sat_base.h:404
bool LiteralIsFalse(Literal literal) const
Definition sat_base.h:407
void EnqueueAtLevel(Literal true_literal, int propagator_id, int level)
Definition sat_base.h:358
BooleanVariable ReferenceVarWithSameReason(BooleanVariable var) const
Definition sat_base.h:930
std::vector< Literal >::const_iterator IteratorAt(int index) const
Definition sat_base.h:650
void EnableChronologicalBacktracking(bool enable)
Definition sat_base.h:682
ConflictResolutionFunction GetConflictResolutionFunction()
Definition sat_base.h:696
std::vector< Literal > * MutableConflict()
Definition sat_base.h:613
int64_t NumberOfEnqueues() const
Definition sat_base.h:646
const Literal & operator[](int index) const
Definition sat_base.h:653
int AssignmentType(BooleanVariable var) const
Definition sat_base.h:942
Trail(const Trail &)=delete
void EnqueueSearchDecision(Literal true_literal)
Definition sat_base.h:425
void SetConflictResolutionFunction(ConflictResolutionFunction resolution)
Definition sat_base.h:692
std::string DebugString() const
Definition sat_base.h:664
void Enqueue(Literal true_literal, int propagator_id)
Definition sat_base.h:350
void Untrail(int target_trail_index)
Definition sat_base.h:595
ClauseId GetUnitClauseId(BooleanVariable var) const
Definition sat_base.h:537
void EnqueueWithSameReasonAs(Literal true_literal, BooleanVariable reference_var)
Definition sat_base.h:469
void EnqueueAssumption(Literal assumptions)
Definition sat_base.h:433
absl::Span< const Literal > Reason(BooleanVariable var, int64_t conflict_id=-1) const
Definition sat_base.h:951
std::vector< Literal > * GetEmptyVectorToStoreReason() const
Definition sat_base.h:569
int NumReimplicationsOnLastUntrail() const
Definition sat_base.h:700
int PrepareBacktrack(int level)
Definition sat_base.h:587
std::function< void( std::vector< Literal > *conflict, std::vector< Literal > *reason_used_to_infer_the_conflict, std::vector< SatClause * > *subsumed_clauses)> ConflictResolutionFunction
Definition sat_base.h:687
void RegisterDebugChecker(std::function< bool(absl::Span< const Literal > clause)> checker)
Definition sat_base.h:673
void EnqueueWithUnitReason(Literal true_literal)
Definition sat_base.h:456
ABSL_MUST_USE_RESULT bool EnqueueWithStoredReason(ClauseId clause_id, Literal true_literal)
Definition sat_base.h:484
EnqueueHelper GetEnqueueHelper(int propagator_id)
Definition sat_base.h:418
void ChangeReason(int trail_index, int propagator_id)
Definition sat_base.h:576
void OverrideDecision(int level, Literal literal)
Definition sat_base.h:443
int AssignmentLevel(Literal lit) const
Definition sat_base.h:661
void SetFailingSatClause(SatClause *clause)
Definition sat_base.h:634
ClauseId GetStoredReasonClauseId(BooleanVariable var) const
Definition sat_base.h:547
bool ChronologicalBacktrackingEnabled() const
Definition sat_base.h:678
Trail & operator=(const Trail &)=delete
const AssignmentInfo & Info(BooleanVariable var) const
Definition sat_base.h:655
std::vector< Literal > * GetEmptyVectorToStoreReason(int trail_index) const
Definition sat_base.h:560
void RegisterPropagator(SatPropagator *propagator)
Definition sat_base.h:919
ClauseId FailingClauseId() const
Definition sat_base.h:642
SatClause * FailingSatClause() const
Definition sat_base.h:638
const std::vector< LiteralWithTrailIndex > & Decisions() const
Definition sat_base.h:451
absl::Span< const Literal > FailingClause() const
Definition sat_base.h:624
int64_t conflict_timestamp() const
Definition sat_base.h:621
void Resize(int num_variables)
Definition sat_base.h:902
void EnqueueWithUnitReason(ClauseId clause_id, Literal true_literal)
Definition sat_base.h:461
const VariablesAssignment & Assignment() const
Definition sat_base.h:654
bool VariableIsAssigned(BooleanVariable var) const
Definition sat_base.h:214
bool LiteralIsAssigned(Literal literal) const
Definition sat_base.h:209
Bitset64< LiteralIndex >::View GetBitsetView()
Definition sat_base.h:230
VariablesAssignment & operator=(const VariablesAssignment &)=delete
Literal GetTrueLiteralForAssignedVariable(BooleanVariable var) const
Definition sat_base.h:221
bool LiteralIsFalse(Literal literal) const
Definition sat_base.h:203
VariablesAssignment(const VariablesAssignment &)=delete
bool LiteralIsTrue(Literal literal) const
Definition sat_base.h:206
void resize(size_type new_size)
const LiteralIndex kNoLiteralIndex(-1)
const LiteralIndex kTrueLiteralIndex(-2)
std::ostream & operator<<(std::ostream &os, const BoolVar &var)
Definition cp_model.cc:89
constexpr ClauseId kNoClauseId(0)
const LiteralIndex kFalseLiteralIndex(-3)
std::vector< Literal > Literals(absl::Span< const int > input)
Definition sat_base.h:164
void AbslStringify(Sink &sink, EdgePosition e)
const BooleanVariable kNoBooleanVariable(-1)
OR-Tools root namespace.
ClosedInterval::Iterator end(ClosedInterval interval)
const bool DEBUG_MODE
Definition radix_sort.h:266
ClosedInterval::Iterator begin(ClosedInterval interval)
static int input(yyscan_t yyscanner)
#define DEFINE_STRONG_INT_TYPE(type_name, value_type)
#define DEFINE_STRONG_INDEX_TYPE(index_type_name)
static constexpr int kFirstFreePropagationId
Definition sat_base.h:317