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