Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
clause.h
Go to the documentation of this file.
1// Copyright 2010-2024 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// This file contains the solver internal representation of the clauses and the
15// classes used for their propagation.
16
17#ifndef OR_TOOLS_SAT_CLAUSE_H_
18#define OR_TOOLS_SAT_CLAUSE_H_
19
20#include <cstdint>
21#include <deque>
22#include <functional>
23#include <string>
24#include <utility>
25#include <vector>
26
27#include "absl/base/attributes.h"
28#include "absl/container/flat_hash_map.h"
29#include "absl/container/flat_hash_set.h"
30#include "absl/container/inlined_vector.h"
31#include "absl/functional/any_invocable.h"
32#include "absl/log/check.h"
33#include "absl/random/bit_gen_ref.h"
34#include "absl/types/span.h"
37#include "ortools/sat/model.h"
39#include "ortools/sat/sat_parameters.pb.h"
40#include "ortools/sat/util.h"
41#include "ortools/util/bitset.h"
42#include "ortools/util/stats.h"
45
46namespace operations_research {
47namespace sat {
48
49// This is how the SatSolver stores a clause. A clause is just a disjunction of
50// literals. In many places, we just use vector<literal> to encode one. But in
51// the critical propagation code, we use this class to remove one memory
52// indirection.
53class SatClause {
54 public:
55 // Creates a sat clause. There must be at least 2 literals.
56 // Clause with one literal fix variable directly and are never constructed.
57 // Note that in practice, we use BinaryImplicationGraph for the clause of size
58 // 2, so this is used for size at least 3.
59 static SatClause* Create(absl::Span<const Literal> literals);
60
61 // Non-sized delete because this is a tail-padded class.
62 void operator delete(void* p) {
63 ::operator delete(p); // non-sized delete
64 }
65
66 // Number of literals in the clause.
67 int size() const { return size_; }
68
69 // We re-use the size to lazily remove clause and notify that they need to be
70 // deleted. It is why this is not called empty() to emphasis that fact. Note
71 // that we never create an initially empty clause, so there is no confusion
72 // with an infeasible model with an empty clause inside.
73 int IsRemoved() const { return size_ == 0; }
74
75 // Allows for range based iteration: for (Literal literal : clause) {}.
76 const Literal* begin() const { return &(literals_[0]); }
77 const Literal* end() const { return &(literals_[size_]); }
78
79 // Returns the first and second literals. These are always the watched
80 // literals if the clause is attached in the LiteralWatchers.
81 Literal FirstLiteral() const { return literals_[0]; }
82 Literal SecondLiteral() const { return literals_[1]; }
83
84 // Returns the literal that was propagated to true. This only works for a
85 // clause that just propagated this literal. Otherwise, this will just returns
86 // a literal of the clause.
87 Literal PropagatedLiteral() const { return literals_[0]; }
88
89 // Returns the reason for the last unit propagation of this clause. The
90 // preconditions are the same as for PropagatedLiteral(). Note that we don't
91 // need to include the propagated literal.
92 absl::Span<const Literal> PropagationReason() const {
93 return absl::Span<const Literal>(&(literals_[1]), size_ - 1);
94 }
95
96 // Returns a Span<> representation of the clause.
97 absl::Span<const Literal> AsSpan() const {
98 return absl::Span<const Literal>(&(literals_[0]), size_);
99 }
100
101 // Removes literals that are fixed. This should only be called at level 0
102 // where a literal is fixed iff it is assigned. Aborts and returns true if
103 // they are not all false.
104 //
105 // Note that the removed literal can still be accessed in the portion [size,
106 // old_size) of literals().
108
109 // Returns true if the clause is satisfied for the given assignment. Note that
110 // the assignment may be partial, so false does not mean that the clause can't
111 // be satisfied by completing the assignment.
112 bool IsSatisfied(const VariablesAssignment& assignment) const;
113
114 std::string DebugString() const;
115
116 private:
117 // The manager needs to permute the order of literals in the clause and
118 // call Clear()/Rewrite.
119 friend class ClauseManager;
120
121 Literal* literals() { return &(literals_[0]); }
122
123 // Marks the clause so that the next call to CleanUpWatchers() can identify it
124 // and actually remove it. We use size_ = 0 for this since the clause will
125 // never be used afterwards.
126 void Clear() { size_ = 0; }
127
128 // Rewrites a clause with another shorter one. Note that the clause shouldn't
129 // be attached when this is called.
130 void Rewrite(absl::Span<const Literal> new_clause) {
131 size_ = 0;
132 for (const Literal l : new_clause) literals_[size_++] = l;
133 }
134
135 int32_t size_;
136
137 // This class store the literals inline, and literals_ mark the starts of the
138 // variable length portion.
139 Literal literals_[0];
140};
141
142// Clause information used for the clause database management. Note that only
143// the clauses that can be removed have an info. The problem clauses and
144// the learned one that we wants to keep forever do not have one.
146 double activity = 0.0;
147 int32_t lbd = 0;
149};
150
152
153// Stores the 2-watched literals data structure. See
154// http://www.cs.berkeley.edu/~necula/autded/lecture24-sat.pdf for
155// detail.
156//
157// This class is also responsible for owning the clause memory and all related
158// information.
160 public:
161 explicit ClauseManager(Model* model);
162
163 // This type is neither copyable nor movable.
164 ClauseManager(const ClauseManager&) = delete;
166
167 ~ClauseManager() override;
168
169 // Must be called before adding clauses referring to such variables.
170 void Resize(int num_variables);
171
172 // SatPropagator API.
173 bool Propagate(Trail* trail) final;
174 absl::Span<const Literal> Reason(const Trail& trail, int trail_index,
175 int64_t conflict_id) const final;
176
177 // Returns the reason of the variable at given trail_index. This only works
178 // for variable propagated by this class and is almost the same as Reason()
179 // with a different return format.
180 SatClause* ReasonClause(int trail_index) const;
181
182 // Adds a new clause and perform initial propagation for this clause only.
183 bool AddClause(absl::Span<const Literal> literals, Trail* trail, int lbd);
184 bool AddClause(absl::Span<const Literal> literals);
185
186 // Same as AddClause() for a removable clause. This is only called on learned
187 // conflict, so this should never have all its literal at false (CHECKED).
188 SatClause* AddRemovableClause(absl::Span<const Literal> literals,
189 Trail* trail, int lbd);
190
191 // Lazily detach the given clause. The deletion will actually occur when
192 // CleanUpWatchers() is called. The later needs to be called before any other
193 // function in this class can be called. This is DCHECKed.
194 //
195 // Note that we remove the clause from clauses_info_ right away.
196 void LazyDetach(SatClause* clause);
197 void CleanUpWatchers();
198
199 // Detaches the given clause right away.
200 //
201 // TODO(user): It might be better to have a "slower" mode in
202 // PropagateOnFalse() that deal with detached clauses in the watcher list and
203 // is activated until the next CleanUpWatchers() calls.
204 void Detach(SatClause* clause);
205
206 // Attaches the given clause. The first two literal of the clause must
207 // be unassigned and the clause must not be already attached.
208 void Attach(SatClause* clause, Trail* trail);
209
210 // Reclaims the memory of the lazily removed clauses (their size was set to
211 // zero) and remove them from AllClausesInCreationOrder() this work in
212 // O(num_clauses()).
214 int64_t num_clauses() const { return clauses_.size(); }
215 const std::vector<SatClause*>& AllClausesInCreationOrder() const {
216 return clauses_;
217 }
218
219 // True if removing this clause will not change the set of feasible solution.
220 // This is the case for clauses that were learned during search. Note however
221 // that some learned clause are kept forever (heuristics) and do not appear
222 // here.
223 bool IsRemovable(SatClause* const clause) const {
224 return clauses_info_.contains(clause);
225 }
226 int64_t num_removable_clauses() const { return clauses_info_.size(); }
227 absl::flat_hash_map<SatClause*, ClauseInfo>* mutable_clauses_info() {
228 return &clauses_info_;
229 }
230
231 // Total number of clauses inspected during calls to PropagateOnFalse().
232 int64_t num_inspected_clauses() const { return num_inspected_clauses_; }
234 return num_inspected_clause_literals_;
235 }
236
237 // The number of different literals (always twice the number of variables).
238 int64_t literal_size() const { return needs_cleaning_.size().value(); }
239
240 // Number of clauses currently watched.
241 int64_t num_watched_clauses() const { return num_watched_clauses_; }
242
243 void SetDratProofHandler(DratProofHandler* drat_proof_handler) {
244 drat_proof_handler_ = drat_proof_handler;
245 }
246
247 // Round-robbing selection of the next clause to minimize/probe.
248 // Note that for minimization we only look at clause kept forever.
249 //
250 // TODO(user): If more indices are needed, switch to a generic API.
252 for (; to_minimize_index_ < clauses_.size(); ++to_minimize_index_) {
253 if (clauses_[to_minimize_index_]->IsRemoved()) continue;
254 if (!IsRemovable(clauses_[to_minimize_index_])) {
255 return clauses_[to_minimize_index_++];
256 }
257 }
258 return nullptr;
259 }
261 for (; to_probe_index_ < clauses_.size(); ++to_probe_index_) {
262 if (clauses_[to_probe_index_]->IsRemoved()) continue;
263 return clauses_[to_probe_index_++];
264 }
265 return nullptr;
266 }
267
268 // Restart the scans.
269 void ResetToProbeIndex() { to_probe_index_ = 0; }
270 void ResetToMinimizeIndex() { to_minimize_index_ = 0; }
271
272 // During an inprocessing phase, it is easier to detach all clause first,
273 // then simplify and then reattach them. Note however that during these
274 // two calls, it is not possible to use the solver unit-progation.
275 //
276 // Important: When reattach is called, we assume that none of their literal
277 // are fixed, so we don't do any special checks.
278 //
279 // These functions can be called multiple-time and do the right things. This
280 // way before doing something, you can call the corresponding function and be
281 // sure to be in a good state. I.e. always AttachAllClauses() before
282 // propagation and DetachAllClauses() before going to do an inprocessing pass
283 // that might transform them.
284 void DetachAllClauses();
285 void AttachAllClauses();
286
287 // These must only be called between [Detach/Attach]AllClauses() calls.
289 ABSL_MUST_USE_RESULT bool InprocessingFixLiteral(Literal true_literal);
290 ABSL_MUST_USE_RESULT bool InprocessingRewriteClause(
291 SatClause* clause, absl::Span<const Literal> new_clause);
292
293 // This can return nullptr if new_clause was of size one or two as these are
294 // treated differently. Note that none of the variable should be fixed in the
295 // given new clause.
296 SatClause* InprocessingAddClause(absl::Span<const Literal> new_clause);
297
298 // Contains, for each literal, the list of clauses that need to be inspected
299 // when the corresponding literal becomes false.
300 struct Watcher {
301 Watcher() = default;
304
305 // Optimization. A literal from the clause that sometimes allow to not even
306 // look at the clause memory when true.
308
309 // Optimization. An index in the clause. Instead of looking for another
310 // literal to watch from the start, we will start from here instead, and
311 // loop around if needed. This allows to avoid bad quadratic corner cases
312 // and lead to an "optimal" complexity. See "Optimal Implementation of
313 // Watched Literals and more General Techniques", Ian P. Gent.
314 //
315 // Note that ideally, this should be part of a SatClause, so it can be
316 // shared across watchers. However, since we have 32 bits for "free" here
317 // because of the struct alignment, we store it here instead.
318 int32_t start_index;
319
321 };
322
323 // This is exposed since some inprocessing code can heuristically exploit the
324 // currently watched literal and blocking literal to do some simplification.
325 const std::vector<Watcher>& WatcherListOnFalse(Literal false_literal) const {
326 return watchers_on_false_[false_literal];
327 }
328
330 absl::AnyInvocable<void(int lbd, absl::Span<const Literal>)>
331 add_clause_callback) {
332 add_clause_callback_ = std::move(add_clause_callback);
333 }
334
335 private:
336 // Attaches the given clause. This eventually propagates a literal which is
337 // enqueued on the trail. Returns false if a contradiction was encountered.
338 bool AttachAndPropagate(SatClause* clause, Trail* trail);
339
340 // Launches all propagation when the given literal becomes false.
341 // Returns false if a contradiction was encountered.
342 bool PropagateOnFalse(Literal false_literal, Trail* trail);
343
344 // Attaches the given clause to the event: the given literal becomes false.
345 // The blocking_literal can be any literal from the clause, it is used to
346 // speed up PropagateOnFalse() by skipping the clause if it is true.
347 void AttachOnFalse(Literal literal, Literal blocking_literal,
348 SatClause* clause);
349
350 // Common code between LazyDetach() and Detach().
351 void InternalDetach(SatClause* clause);
352
354 watchers_on_false_;
355
356 // SatClause reasons by trail_index.
357 std::vector<SatClause*> reasons_;
358
359 // Indicates if the corresponding watchers_on_false_ list need to be
360 // cleaned. The boolean is_clean_ is just used in DCHECKs.
361 SparseBitset<LiteralIndex> needs_cleaning_;
362 bool is_clean_ = true;
363
364 BinaryImplicationGraph* implication_graph_;
365 Trail* trail_;
366
367 int64_t num_inspected_clauses_;
368 int64_t num_inspected_clause_literals_;
369 int64_t num_watched_clauses_;
370 mutable StatsGroup stats_;
371
372 // For DetachAllClauses()/AttachAllClauses().
373 bool all_clauses_are_attached_ = true;
374
375 // All the clauses currently in memory. This vector has ownership of the
376 // pointers. We currently do not use std::unique_ptr<SatClause> because it
377 // can't be used with some STL algorithms like std::partition.
378 //
379 // Note that the unit clauses and binary clause are not kept here.
380 std::vector<SatClause*> clauses_;
381
382 int to_minimize_index_ = 0;
383 int to_probe_index_ = 0;
384
385 // Only contains removable clause.
386 absl::flat_hash_map<SatClause*, ClauseInfo> clauses_info_;
387
388 DratProofHandler* drat_proof_handler_ = nullptr;
389
390 absl::AnyInvocable<void(int lbd, absl::Span<const Literal>)>
391 add_clause_callback_ = nullptr;
392};
393
394// A binary clause. This is used by BinaryClauseManager.
396 BinaryClause(Literal _a, Literal _b) : a(_a), b(_b) {}
397 bool operator==(BinaryClause o) const { return a == o.a && b == o.b; }
398 bool operator!=(BinaryClause o) const { return a != o.a || b != o.b; }
401};
402
403// A simple class to manage a set of binary clauses.
405 public:
407
408 // This type is neither copyable nor movable.
411
412 int NumClauses() const { return set_.size(); }
413
414 // Adds a new binary clause to the manager and returns true if it wasn't
415 // already present.
417 std::pair<int, int> p(c.a.SignedValue(), c.b.SignedValue());
418 if (p.first > p.second) std::swap(p.first, p.second);
419 if (set_.find(p) == set_.end()) {
420 set_.insert(p);
421 newly_added_.push_back(c);
422 return true;
423 }
424 return false;
425 }
426
427 // Returns the newly added BinaryClause since the last ClearNewlyAdded() call.
428 const std::vector<BinaryClause>& newly_added() const { return newly_added_; }
429 void ClearNewlyAdded() { newly_added_.clear(); }
430
431 private:
432 absl::flat_hash_set<std::pair<int, int>> set_;
433 std::vector<BinaryClause> newly_added_;
434};
435
436// Special class to store and propagate clauses of size 2 (i.e. implication).
437// Such clauses are never deleted. Together, they represent the 2-SAT part of
438// the problem. Note that 2-SAT satisfiability is a polynomial problem, but
439// W2SAT (weighted 2-SAT) is NP-complete.
440//
441// TODO(user): Most of the note below are done, but we currently only applies
442// the reduction before the solve. We should consider doing more in-processing.
443// The code could probably still be improved too.
444//
445// Note(user): All the variables in a strongly connected component are
446// equivalent and can be thus merged as one. This is relatively cheap to compute
447// from time to time (linear complexity). We will also get contradiction (a <=>
448// not a) this way. This is done by DetectEquivalences().
449//
450// Note(user): An implication (a => not a) implies that a is false. I am not
451// sure it is worth detecting that because if the solver assign a to true, it
452// will learn that right away. I don't think we can do it faster.
453//
454// Note(user): The implication graph can be pruned. This is called the
455// transitive reduction of a graph. For instance If a => {b,c} and b => {c},
456// then there is no need to store a => {c}. The transitive reduction is unique
457// on an acyclic graph. Computing it will allow for a faster propagation and
458// memory reduction. It is however not cheap. Maybe simple lazy heuristics to
459// remove redundant arcs are better. Note that all the learned clauses we add
460// will never be redundant (but they could introduce cycles). This is done
461// by ComputeTransitiveReduction().
462//
463// Note(user): This class natively support at most one constraints. This is
464// a way to reduced significantly the memory and size of some 2-SAT instances.
465// However, it is not fully exploited for pure SAT problems. See
466// TransformIntoMaxCliques().
467//
468// Note(user): Add a preprocessor to remove duplicates in the implication lists.
469// Note that all the learned clauses we add will never create duplicates.
470//
471// References for most of the above and more:
472// - Brafman RI, "A simplifier for propositional formulas with many binary
473// clauses", IEEE Trans Syst Man Cybern B Cybern. 2004 Feb;34(1):52-9.
474// http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.28.4911
475// - Marijn J. H. Heule, Matti Järvisalo, Armin Biere, "Efficient CNF
476// Simplification Based on Binary Implication Graphs", Theory and Applications
477// of Satisfiability Testing - SAT 2011, Lecture Notes in Computer Science
478// Volume 6695, 2011, pp 201-215
479// http://www.cs.helsinki.fi/u/mjarvisa/papers/heule-jarvisalo-biere.sat11.pdf
481 public:
483 : SatPropagator("BinaryImplicationGraph"),
484 stats_("BinaryImplicationGraph"),
485 time_limit_(model->GetOrCreate<TimeLimit>()),
486 random_(model->GetOrCreate<ModelRandomGenerator>()),
487 trail_(model->GetOrCreate<Trail>()),
488 at_most_one_max_expansion_size_(
489 model->GetOrCreate<SatParameters>()
490 ->at_most_one_max_expansion_size()) {
491 trail_->RegisterPropagator(this);
492 }
493
494 // This type is neither copyable nor movable.
497
500 LOG(INFO) << stats_.StatString();
501 LOG(INFO) << "num_redundant_implications " << num_redundant_implications_;
502 });
503 }
504
505 // SatPropagator interface.
506 bool Propagate(Trail* trail) final;
507 absl::Span<const Literal> Reason(const Trail& trail, int trail_index,
508 int64_t conflict_id) const final;
509
510 // Resizes the data structure.
511 void Resize(int num_variables);
512
513 // Returns true if there is no constraints in this class.
514 bool IsEmpty() const final {
515 return num_implications_ == 0 && at_most_ones_.empty();
516 }
517
518 // Adds the binary clause (a OR b), which is the same as (not a => b).
519 // Note that it is also equivalent to (not b => a).
520 //
521 // Preconditions:
522 // - If we are at root node, then none of the literal should be assigned.
523 // This is Checked and is there to track inefficiency as we do not need a
524 // clause in this case.
525 // - If we are at a positive decision level, we will propagate something if
526 // we can. However, if both literal are false, we will just return false
527 // and do nothing. In all other case, we will return true.
530 return AddBinaryClause(a.Negated(), b);
531 }
532
533 // When set, the callback will be called on ALL newly added binary clauses.
534 //
535 // The EnableSharing() function can be used to disable sharing temporarily for
536 // the clauses that are imported from the Shared repository already.
537 //
538 // TODO(user): this is meant to share clause between workers, hopefully the
539 // contention will not be too high. Double check and maybe add a batch version
540 // were we keep new implication and add them in batches.
541 void EnableSharing(bool enable) { enable_sharing_ = enable; }
542 void SetAdditionCallback(std::function<void(Literal, Literal)> f) {
543 add_binary_callback_ = f;
544 }
545 // An at most one constraint of size n is a compact way to encode n * (n - 1)
546 // implications. This must only be called at level zero.
547 //
548 // Returns false if this creates a conflict. Currently this can only happens
549 // if there is duplicate literal already assigned to true in this constraint.
550 //
551 // TODO(user): Our algorithm could generalize easily to at_most_ones + a list
552 // of literals that will be false if one of the literal in the amo is at one.
553 // It is a way to merge common list of implications.
554 //
555 // If the final AMO size is smaller than at_most_one_expansion_size
556 // parameters, we fully expand it.
557 ABSL_MUST_USE_RESULT bool AddAtMostOne(absl::Span<const Literal> at_most_one);
558
559 // Uses the binary implication graph to minimize the given conflict by
560 // removing literals that implies others. The idea is that if a and b are two
561 // literals from the given conflict and a => b (which is the same as not(b) =>
562 // not(a)) then a is redundant and can be removed.
563 //
564 // Note that removing as many literals as possible is too time consuming, so
565 // we use different heuristics/algorithms to do this minimization.
566 // See the binary_minimization_algorithm SAT parameter and the .cc for more
567 // details about the different algorithms.
568 void MinimizeConflictWithReachability(std::vector<Literal>* c);
569 void MinimizeConflictExperimental(const Trail& trail,
570 std::vector<Literal>* c);
571 void MinimizeConflictFirst(const Trail& trail, std::vector<Literal>* c,
574 std::vector<Literal>* c,
575 absl::BitGenRef random);
576
577 // This must only be called at decision level 0 after all the possible
578 // propagations. It:
579 // - Removes the variable at true from the implications lists.
580 // - Frees the propagation list of the assigned literals.
582
583 // Returns false if the model is unsat, otherwise detects equivalent variable
584 // (with respect to the implications only) and reorganize the propagation
585 // lists accordingly.
586 //
587 // TODO(user): Completely get rid of such literal instead? it might not be
588 // reasonable code-wise to remap our literals in all of our constraints
589 // though.
590 bool DetectEquivalences(bool log_info = false);
591
592 // Returns true if DetectEquivalences() has been called and no new binary
593 // clauses have been added since then. When this is true then there is no
594 // cycle in the binary implication graph (modulo the redundant literals that
595 // form a cycle with their representative).
596 bool IsDag() const { return is_dag_; }
597
598 // One must call DetectEquivalences() first, this is CHECKed.
599 // Returns a list so that if x => y, then x is after y.
600 const std::vector<LiteralIndex>& ReverseTopologicalOrder() const {
601 CHECK(is_dag_);
602 return reverse_topological_order_;
603 }
604
605 // Returns the list of literal "directly" implied by l. Beware that this can
606 // easily change behind your back if you modify the solver state.
607 const absl::InlinedVector<Literal, 6>& Implications(Literal l) const {
608 return implications_[l];
609 }
610
611 // Returns the representative of the equivalence class of l (or l itself if it
612 // is on its own). Note that DetectEquivalences() should have been called to
613 // get any non-trival results.
615 if (l.Index() >= representative_of_.size()) return l;
616 if (representative_of_[l] == kNoLiteralIndex) return l;
617 return Literal(representative_of_[l]);
618 }
619
620 // Prunes the implication graph by calling first DetectEquivalences() to
621 // remove cycle and then by computing the transitive reduction of the
622 // remaining DAG.
623 //
624 // Note that this can be slow (num_literals graph traversals), so we abort
625 // early if we start doing too much work.
626 //
627 // Returns false if the model is detected to be UNSAT (this needs to call
628 // DetectEquivalences() if not already done).
629 bool ComputeTransitiveReduction(bool log_info = false);
630
631 // Another way of representing an implication graph is a list of maximal "at
632 // most one" constraints, each forming a max-clique in the incompatibility
633 // graph. This representation is useful for having a good linear relaxation.
634 //
635 // This function will transform each of the given constraint into a maximal
636 // one in the underlying implication graph. Constraints that are redundant
637 // after other have been expanded (i.e. included into) will be cleared.
638 // Note that the order of constraints will be conserved.
639 //
640 // Returns false if the model is detected to be UNSAT (this needs to call
641 // DetectEquivalences() if not already done).
642 bool TransformIntoMaxCliques(std::vector<std::vector<Literal>>* at_most_ones,
643 int64_t max_num_explored_nodes = 1e8);
644
645 // LP clique cut heuristic. Returns a set of "at most one" constraints on the
646 // given literals or their negation that are violated by the current LP
647 // solution. Note that this assumes that
648 // lp_value(lit) = 1 - lp_value(lit.Negated()).
649 //
650 // The literal and lp_values vector are in one to one correspondence. We will
651 // only generate clique with these literals or their negation.
652 //
653 // TODO(user): Refine the heuristic and unit test!
654 const std::vector<std::vector<Literal>>& GenerateAtMostOnesWithLargeWeight(
655 const std::vector<Literal>& literals,
656 const std::vector<double>& lp_values);
657
658 // Heuristically identify "at most one" between the given literals, swap
659 // them around and return these amo as span inside the literals vector.
660 //
661 // TODO(user): Add a limit to make sure this do not take too much time.
662 std::vector<absl::Span<const Literal>> HeuristicAmoPartition(
663 std::vector<Literal>* literals);
664
665 // Number of literal propagated by this class (including conflicts).
666 int64_t num_propagations() const { return num_propagations_; }
667
668 // Number of literals inspected by this class during propagation.
669 int64_t num_inspections() const { return num_inspections_; }
670
671 // MinimizeClause() stats.
672 int64_t num_minimization() const { return num_minimization_; }
673 int64_t num_literals_removed() const { return num_literals_removed_; }
674
675 // Returns true if this literal is fixed or is equivalent to another literal.
676 // This means that it can just be ignored in most situation.
677 //
678 // Note that the set (and thus number) of redundant literal can only grow over
679 // time. This is because we always use the lowest index as representative of
680 // an equivalent class, so a redundant literal will stay that way.
681 bool IsRedundant(Literal l) const { return is_redundant_[l]; }
682 int64_t num_redundant_literals() const {
683 CHECK_EQ(num_redundant_literals_ % 2, 0);
684 return num_redundant_literals_;
685 }
686
687 // Number of implications removed by transitive reduction.
689 return num_redundant_implications_;
690 }
691
692 // Returns the number of current implications. Note that a => b and not(b)
693 // => not(a) are counted separately since they appear separately in our
694 // propagation lists. The number of size 2 clauses that represent the same
695 // thing is half this number.
696 int64_t num_implications() const { return num_implications_; }
697 int64_t literal_size() const { return implications_.size(); }
698
699 // Extract all the binary clauses managed by this class. The Output type must
700 // support an AddBinaryClause(Literal a, Literal b) function.
701 //
702 // Important: This currently does NOT include at most one constraints.
703 //
704 // TODO(user): When extracting to cp_model.proto we could be more efficient
705 // by extracting bool_and constraint with many lhs terms.
706 template <typename Output>
707 void ExtractAllBinaryClauses(Output* out) const {
708 // TODO(user): Ideally we should just never have duplicate clauses in this
709 // class. But it seems we do in some corner cases, so lets not output them
710 // twice.
711 absl::flat_hash_set<std::pair<LiteralIndex, LiteralIndex>>
712 duplicate_detection;
713 for (LiteralIndex i(0); i < implications_.size(); ++i) {
714 const Literal a = Literal(i).Negated();
715 for (const Literal b : implications_[i]) {
716 // Note(user): We almost always have both a => b and not(b) => not(a) in
717 // our implications_ database. Except if ComputeTransitiveReduction()
718 // was aborted early, but in this case, if only one is present, the
719 // other could be removed, so we shouldn't need to output it.
720 if (a < b && duplicate_detection.insert({a, b}).second) {
721 out->AddBinaryClause(a, b);
722 }
723 }
724 }
725 }
726
727 void SetDratProofHandler(DratProofHandler* drat_proof_handler) {
728 drat_proof_handler_ = drat_proof_handler;
729 }
730
731 // Changes the reason of the variable at trail index to a binary reason.
732 // Note that the implication "new_reason => trail_[trail_index]" should be
733 // part of the implication graph.
734 void ChangeReason(int trail_index, Literal new_reason) {
735 CHECK(trail_->Assignment().LiteralIsTrue(new_reason));
736 reasons_[trail_index] = new_reason.Negated();
737 trail_->ChangeReason(trail_index, propagator_id_);
738 }
739
740 // The literals that are "directly" implied when literal is set to true. This
741 // is not a full "reachability". It includes at most ones propagation. The set
742 // of all direct implications is enough to describe the implications graph
743 // completely.
744 //
745 // When doing blocked clause elimination of bounded variable elimination, one
746 // only need to consider this list and not the full reachability.
747 const std::vector<Literal>& DirectImplications(Literal literal);
748
749 // Returns a random literal in DirectImplications(lhs). Note that this is
750 // biased if lhs appear in some most one, but it is constant time, which is a
751 // lot faster than computing DirectImplications() and then sampling from it.
752 LiteralIndex RandomImpliedLiteral(Literal lhs);
753
754 // A proxy for DirectImplications().size(), However we currently do not
755 // maintain it perfectly. It is exact each time DirectImplications() is
756 // called, and we update it in some situation but we don't deal with fixed
757 // variables, at_most ones and duplicates implications for now.
759 return estimated_sizes_[literal];
760 }
761
762 // Variable elimination by replacing everything of the form a => var => b by
763 // a => b. We ignore any a => a so the number of new implications is not
764 // always just the product of the two direct implication list of var and
765 // not(var). However, if a => var => a, then a and var are equivalent, so this
766 // case will be removed if one run DetectEquivalences() before this.
767 // Similarly, if a => var => not(a) then a must be false and this is detected
768 // and dealt with by FindFailedLiteralAroundVar().
769 bool FindFailedLiteralAroundVar(BooleanVariable var, bool* is_unsat);
770 int64_t NumImplicationOnVariableRemoval(BooleanVariable var);
772 BooleanVariable var, std::deque<std::vector<Literal>>* postsolve_clauses);
773 bool IsRemoved(Literal l) const { return is_removed_[l]; }
775 std::deque<std::vector<Literal>>* postsolve_clauses);
777
778 // ExpandAtMostOneWithWeight() will increase this, so a client can put a limit
779 // on this possibly expansive operation.
780 void ResetWorkDone() { work_done_in_mark_descendants_ = 0; }
781 int64_t WorkDone() const { return work_done_in_mark_descendants_; }
782
783 // Same as ExpandAtMostOne() but try to maximize the weight in the clique.
784 template <bool use_weight = true>
785 std::vector<Literal> ExpandAtMostOneWithWeight(
786 absl::Span<const Literal> at_most_one,
789 expanded_lp_values);
790
791 // Restarts the at_most_one iterator.
792 void ResetAtMostOneIterator() { at_most_one_iterator_ = 0; }
793
794 // Returns the next at_most_one, or a span of size 0 when finished.
795 absl::Span<const Literal> NextAtMostOne();
796
797 // Clean up implications list that might have duplicates.
798 void RemoveDuplicates();
799
800 private:
801 // Mark implications_[a] for cleanup in RemoveDuplicates().
802 void NotifyPossibleDuplicate(Literal a);
803
804 // Simple wrapper to not forget to output newly fixed variable to the DRAT
805 // proof if needed. This will propagate right away the implications.
806 bool FixLiteral(Literal true_literal);
807
808 // Propagates all the direct implications of the given literal becoming true.
809 // Returns false if a conflict was encountered, in which case
810 // trail->SetFailingClause() will be called with the correct size 2 clause.
811 // This calls trail->Enqueue() on the newly assigned literals.
812 bool PropagateOnTrue(Literal true_literal, Trail* trail);
813
814 // Remove any literal whose negation is marked (except the first one).
815 void RemoveRedundantLiterals(std::vector<Literal>* conflict);
816
817 // Fill is_marked_ with all the descendant of root.
818 // Note that this also use bfs_stack_.
819 void MarkDescendants(Literal root);
820
821 // Expands greedily the given at most one until we get a maximum clique in
822 // the underlying incompatibility graph. Note that there is no guarantee that
823 // if this is called with any sub-clique of the result we will get the same
824 // maximal clique.
825 std::vector<Literal> ExpandAtMostOne(absl::Span<const Literal> at_most_one,
826 int64_t max_num_explored_nodes);
827
828 // Process all at most one constraints starting at or after base_index in
829 // at_most_one_buffer_. This replace literal by their representative, remove
830 // fixed literals and deal with duplicates. Return false iff the model is
831 // UNSAT.
832 //
833 // If the final AMO size is smaller than the at_most_one_expansion_size
834 // parameters, we fully expand it.
835 bool CleanUpAndAddAtMostOnes(int base_index);
836
837 // To be used in DCHECKs().
838 bool InvariantsAreOk();
839
840 // Return the at most one encoded at the given start.
841 // Important: this is only valid until a new at_most one is added.
842 absl::Span<const Literal> AtMostOne(int start) const;
843
844 mutable StatsGroup stats_;
845 TimeLimit* time_limit_;
846 ModelRandomGenerator* random_;
847 Trail* trail_;
848 DratProofHandler* drat_proof_handler_ = nullptr;
849
850 // Binary reasons by trail_index. We need a deque because we kept pointers to
851 // elements of this array and this can dynamically change size.
852 std::deque<Literal> reasons_;
853
854 // This is indexed by the Index() of a literal. Each list stores the
855 // literals that are implied if the index literal becomes true.
856 //
857 // Using InlinedVector helps quite a bit because on many problems, a literal
858 // only implies a few others. Note that on a 64 bits computer we get exactly
859 // 6 inlined int32_t elements without extra space, and the size of the inlined
860 // vector is 4 times 64 bits.
861 //
862 // TODO(user): We could be even more efficient since a size of int32_t is
863 // enough for us and we could store in common the inlined/not-inlined size.
865 implications_;
866 int64_t num_implications_ = 0;
867
868 // Used by RemoveDuplicates() and NotifyPossibleDuplicate().
870 std::vector<Literal> to_clean_;
871
872 // Internal representation of at_most_one constraints. Each entry point to the
873 // start of a constraint in the buffer.
874 //
875 // TRICKY: The first literal is actually the size of the at_most_one.
876 // Most users should just use AtMostOne(start).
877 //
878 // When LiteralIndex is true, then all entry in the at most one
879 // constraint must be false except the one referring to LiteralIndex.
880 //
881 // TODO(user): We could be more cache efficient by combining this with
882 // implications_ in some way. Do some propagation speed benchmark.
884 at_most_ones_;
885 std::vector<Literal> at_most_one_buffer_;
886 const int at_most_one_max_expansion_size_;
887 int at_most_one_iterator_ = 0;
888
889 // Invariant: implies_something_[l] should be true iff implications_[l] or
890 // at_most_ones_[l] might be non-empty.
891 //
892 // For problems with a large number of variables and sparse implications_ or
893 // at_most_ones_ entries, checking this is way faster during
894 // MarkDescendants(). See for instance proteindesign122trx11p8.pb.gz.
895 Bitset64<LiteralIndex> implies_something_;
896
897 // Used by GenerateAtMostOnesWithLargeWeight().
898 std::vector<std::vector<Literal>> tmp_cuts_;
899
900 // Some stats.
901 int64_t num_propagations_ = 0;
902 int64_t num_inspections_ = 0;
903 int64_t num_minimization_ = 0;
904 int64_t num_literals_removed_ = 0;
905 int64_t num_redundant_implications_ = 0;
906 int64_t num_redundant_literals_ = 0;
907
908 // Bitset used by MinimizeClause().
909 // TODO(user): use the same one as the one used in the classic minimization
910 // because they are already initialized. Moreover they contains more
911 // information.
913 SparseBitset<LiteralIndex> is_simplified_;
914
915 // Temporary stack used by MinimizeClauseWithReachability().
916 std::vector<Literal> dfs_stack_;
917
918 // Used to limit the work done by ComputeTransitiveReduction() and
919 // TransformIntoMaxCliques().
920 int64_t work_done_in_mark_descendants_ = 0;
921 std::vector<Literal> bfs_stack_;
922
923 // Used by ComputeTransitiveReduction() in case we abort early to maintain
924 // the invariant checked by InvariantsAreOk(). Some of our algo
925 // relies on this to be always true.
926 std::vector<std::pair<Literal, Literal>> tmp_removed_;
927
928 // Filled by DetectEquivalences().
929 bool is_dag_ = false;
930 std::vector<LiteralIndex> reverse_topological_order_;
931 Bitset64<LiteralIndex> is_redundant_;
933
934 // For in-processing and removing variables.
935 std::vector<Literal> direct_implications_;
936 std::vector<Literal> direct_implications_of_negated_literal_;
937 util_intops::StrongVector<LiteralIndex, bool> in_direct_implications_;
940
941 // For RemoveFixedVariables().
942 int num_processed_fixed_variables_ = 0;
943
944 bool enable_sharing_ = true;
945 std::function<void(Literal, Literal)> add_binary_callback_ = nullptr;
946};
947
948extern template std::vector<Literal>
950 const absl::Span<const Literal> at_most_one,
952 const util_intops::StrongVector<LiteralIndex, double>& expanded_lp_values);
953
954extern template std::vector<Literal>
956 const absl::Span<const Literal> at_most_one,
958 const util_intops::StrongVector<LiteralIndex, double>& expanded_lp_values);
959
960} // namespace sat
961} // namespace operations_research
962
963#endif // OR_TOOLS_SAT_CLAUSE_H_
IntegerType size() const
Definition bitset.h:830
Base class to print a nice summary of a group of statistics.
Definition stats.h:128
std::string StatString() const
Definition stats.cc:77
A simple class to manage a set of binary clauses.
Definition clause.h:404
const std::vector< BinaryClause > & newly_added() const
Returns the newly added BinaryClause since the last ClearNewlyAdded() call.
Definition clause.h:428
BinaryClauseManager(const BinaryClauseManager &)=delete
This type is neither copyable nor movable.
BinaryClauseManager & operator=(const BinaryClauseManager &)=delete
int64_t num_propagations() const
Number of literal propagated by this class (including conflicts).
Definition clause.h:666
void ResetAtMostOneIterator()
Restarts the at_most_one iterator.
Definition clause.h:792
bool ComputeTransitiveReduction(bool log_info=false)
Definition clause.cc:1441
Literal RepresentativeOf(Literal l) const
Definition clause.h:614
void Resize(int num_variables)
Resizes the data structure.
Definition clause.cc:501
void ExtractAllBinaryClauses(Output *out) const
Definition clause.h:707
BinaryImplicationGraph(const BinaryImplicationGraph &)=delete
This type is neither copyable nor movable.
bool Propagate(Trail *trail) final
SatPropagator interface.
Definition clause.cc:839
int64_t num_minimization() const
MinimizeClause() stats.
Definition clause.h:672
void RemoveDuplicates()
Clean up implications list that might have duplicates.
Definition clause.cc:519
void MinimizeConflictWithReachability(std::vector< Literal > *c)
Definition clause.cc:864
std::vector< Literal > ExpandAtMostOneWithWeight(absl::Span< const Literal > at_most_one, const util_intops::StrongVector< LiteralIndex, bool > &can_be_included, const util_intops::StrongVector< LiteralIndex, double > &expanded_lp_values)
Same as ExpandAtMostOne() but try to maximize the weight in the clique.
Definition clause.cc:1802
bool TransformIntoMaxCliques(std::vector< std::vector< Literal > > *at_most_ones, int64_t max_num_explored_nodes=1e8)
Definition clause.cc:1658
int64_t NumImplicationOnVariableRemoval(BooleanVariable var)
Definition clause.cc:2237
int64_t num_redundant_implications() const
Number of implications removed by transitive reduction.
Definition clause.h:688
bool IsEmpty() const final
Returns true if there is no constraints in this class.
Definition clause.h:514
bool AddBinaryClause(Literal a, Literal b)
Definition clause.cc:531
int DirectImplicationsEstimatedSize(Literal literal) const
Definition clause.h:758
absl::Span< const Literal > NextAtMostOne()
Returns the next at_most_one, or a span of size 0 when finished.
Definition clause.cc:2466
bool AddImplication(Literal a, Literal b)
Definition clause.h:529
void RemoveBooleanVariable(BooleanVariable var, std::deque< std::vector< Literal > > *postsolve_clauses)
For all possible a => var => b, add a => b.
Definition clause.cc:2257
void MinimizeConflictExperimental(const Trail &trail, std::vector< Literal > *c)
Definition clause.cc:1025
void MinimizeConflictFirstWithTransitiveReduction(const Trail &trail, std::vector< Literal > *c, absl::BitGenRef random)
Definition clause.cc:963
bool DetectEquivalences(bool log_info=false)
Definition clause.cc:1248
void SetDratProofHandler(DratProofHandler *drat_proof_handler)
Definition clause.h:727
void SetAdditionCallback(std::function< void(Literal, Literal)> f)
Definition clause.h:542
void MinimizeConflictFirst(const Trail &trail, std::vector< Literal > *c, SparseBitset< BooleanVariable > *marked)
Definition clause.cc:941
const std::vector< Literal > & DirectImplications(Literal literal)
Definition clause.cc:2140
const std::vector< std::vector< Literal > > & GenerateAtMostOnesWithLargeWeight(const std::vector< Literal > &literals, const std::vector< double > &lp_values)
Definition clause.cc:1894
absl::Span< const Literal > Reason(const Trail &trail, int trail_index, int64_t conflict_id) const final
Definition clause.cc:852
void RemoveAllRedundantVariables(std::deque< std::vector< Literal > > *postsolve_clauses)
Definition clause.cc:2308
const std::vector< LiteralIndex > & ReverseTopologicalOrder() const
Definition clause.h:600
void ChangeReason(int trail_index, Literal new_reason)
Definition clause.h:734
bool FindFailedLiteralAroundVar(BooleanVariable var, bool *is_unsat)
Definition clause.cc:2212
BinaryImplicationGraph & operator=(const BinaryImplicationGraph &)=delete
LiteralIndex RandomImpliedLiteral(Literal lhs)
Definition clause.cc:2185
ABSL_MUST_USE_RESULT bool AddAtMostOne(absl::Span< const Literal > at_most_one)
Definition clause.cc:591
std::vector< absl::Span< const Literal > > HeuristicAmoPartition(std::vector< Literal > *literals)
Definition clause.cc:1983
const absl::InlinedVector< Literal, 6 > & Implications(Literal l) const
Definition clause.h:607
int64_t num_inspections() const
Number of literals inspected by this class during propagation.
Definition clause.h:669
SatClause * InprocessingAddClause(absl::Span< const Literal > new_clause)
Definition clause.cc:436
void ResetToProbeIndex()
Restart the scans.
Definition clause.h:269
void LazyDetach(SatClause *clause)
Definition clause.cc:315
void InprocessingRemoveClause(SatClause *clause)
These must only be called between [Detach/Attach]AllClauses() calls.
Definition clause.cc:379
bool IsRemovable(SatClause *const clause) const
Definition clause.h:223
void Detach(SatClause *clause)
Definition clause.cc:322
absl::Span< const Literal > Reason(const Trail &trail, int trail_index, int64_t conflict_id) const final
Definition clause.cc:210
int64_t num_inspected_clauses() const
Total number of clauses inspected during calls to PropagateOnFalse().
Definition clause.h:232
ClauseManager(const ClauseManager &)=delete
This type is neither copyable nor movable.
bool AddClause(absl::Span< const Literal > literals, Trail *trail, int lbd)
Adds a new clause and perform initial propagation for this clause only.
Definition clause.cc:224
int64_t literal_size() const
The number of different literals (always twice the number of variables).
Definition clause.h:238
int64_t num_inspected_clause_literals() const
Definition clause.h:233
int64_t num_watched_clauses() const
Number of clauses currently watched.
Definition clause.h:241
void SetDratProofHandler(DratProofHandler *drat_proof_handler)
Definition clause.h:243
ClauseManager(Model *model)
--— ClauseManager --—
Definition clause.cc:72
SatClause * ReasonClause(int trail_index) const
Definition clause.cc:216
void SetAddClauseCallback(absl::AnyInvocable< void(int lbd, absl::Span< const Literal >)> add_clause_callback)
Definition clause.h:329
absl::flat_hash_map< SatClause *, ClauseInfo > * mutable_clauses_info()
Definition clause.h:227
SatClause * AddRemovableClause(absl::Span< const Literal > literals, Trail *trail, int lbd)
Definition clause.cc:232
const std::vector< SatClause * > & AllClausesInCreationOrder() const
Definition clause.h:215
void Resize(int num_variables)
Must be called before adding clauses referring to such variables.
Definition clause.cc:88
ClauseManager & operator=(const ClauseManager &)=delete
ABSL_MUST_USE_RESULT bool InprocessingFixLiteral(Literal true_literal)
This one do not need the clause to be detached.
Definition clause.cc:360
void Attach(SatClause *clause, Trail *trail)
Definition clause.cc:295
ABSL_MUST_USE_RESULT bool InprocessingRewriteClause(SatClause *clause, absl::Span< const Literal > new_clause)
Definition clause.cc:388
const std::vector< Watcher > & WatcherListOnFalse(Literal false_literal) const
Definition clause.h:325
bool Propagate(Trail *trail) final
SatPropagator API.
Definition clause.cc:201
LiteralIndex Index() const
Definition sat_base.h:91
const Literal * end() const
Definition clause.h:77
static SatClause * Create(absl::Span< const Literal > literals)
--— SatClause --—
Definition clause.cc:2479
absl::Span< const Literal > AsSpan() const
Returns a Span<> representation of the clause.
Definition clause.h:97
const Literal * begin() const
Allows for range based iteration: for (Literal literal : clause) {}.
Definition clause.h:76
absl::Span< const Literal > PropagationReason() const
Definition clause.h:92
bool IsSatisfied(const VariablesAssignment &assignment) const
Definition clause.cc:2516
Literal PropagatedLiteral() const
Definition clause.h:87
int size() const
Number of literals in the clause.
Definition clause.h:67
std::string DebugString() const
Definition clause.cc:2523
bool RemoveFixedLiteralsAndTestIfTrue(const VariablesAssignment &assignment)
Definition clause.cc:2492
Base class for all the SAT constraints.
Definition sat_base.h:533
void ChangeReason(int trail_index, int propagator_id)
Definition sat_base.h:408
void RegisterPropagator(SatPropagator *propagator)
Definition sat_base.h:650
const VariablesAssignment & Assignment() const
Definition sat_base.h:462
bool LiteralIsTrue(Literal literal) const
Definition sat_base.h:188
int64_t a
Definition table.cc:44
IntVar * var
GRBmodel * model
int literal
const LiteralIndex kNoLiteralIndex(-1)
In SWIG mode, we don't want anything besides these top-level includes.
int64_t start
#define IF_STATS_ENABLED(instructions)
Definition stats.h:417
A binary clause. This is used by BinaryClauseManager.
Definition clause.h:395
bool operator!=(BinaryClause o) const
Definition clause.h:398
BinaryClause(Literal _a, Literal _b)
Definition clause.h:396
bool operator==(BinaryClause o) const
Definition clause.h:397
Watcher(SatClause *c, Literal b, int i=2)
Definition clause.h:302