17#ifndef OR_TOOLS_SAT_CLAUSE_H_
18#define OR_TOOLS_SAT_CLAUSE_H_
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"
64 void operator delete(
void* p) {
69 int size()
const {
return size_; }
79 const Literal*
end()
const {
return &(literals_[size_]); }
95 return absl::Span<const Literal>(&(literals_[1]), size_ - 1);
99 absl::Span<const Literal>
AsSpan()
const {
100 return absl::Span<const Literal>(&(literals_[0]), size_);
123 Literal* literals() {
return &(literals_[0]); }
128 void Clear() { size_ = 0; }
132 void Rewrite(absl::Span<const Literal> new_clause) {
134 for (
const Literal l : new_clause) literals_[size_++] = l;
141 Literal literals_[0];
172 void Resize(
int num_variables);
176 absl::Span<const Literal>
Reason(
const Trail& trail,
int trail_index,
177 int64_t conflict_id)
const final;
185 bool AddClause(absl::Span<const Literal> literals,
Trail* trail,
int lbd);
186 bool AddClause(absl::Span<const Literal> literals);
191 Trail* trail,
int lbd);
226 return clauses_info_.contains(clause);
230 return &clauses_info_;
236 return num_inspected_clause_literals_;
240 int64_t
literal_size()
const {
return needs_cleaning_.size().value(); }
246 drat_proof_handler_ = drat_proof_handler;
269 if (to_first_minimize_index_ > 0)
return;
270 to_first_minimize_index_ = clauses_.size();
292 SatClause* clause, absl::Span<const Literal> new_clause);
327 return watchers_on_false_[false_literal];
331 absl::AnyInvocable<
void(
int lbd, absl::Span<const Literal>)>
332 add_clause_callback) {
333 add_clause_callback_ = std::move(add_clause_callback);
338 absl::AnyInvocable<void(
int lbd, absl::Span<const Literal>)>
340 return std::move(add_clause_callback_);
350 bool PropagateOnFalse(
Literal false_literal,
Trail* trail);
365 std::vector<SatClause*> reasons_;
370 bool is_clean_ =
true;
375 int64_t num_inspected_clauses_;
376 int64_t num_inspected_clause_literals_;
377 int64_t num_watched_clauses_;
381 bool all_clauses_are_attached_ =
true;
388 std::vector<SatClause*> clauses_;
391 int to_minimize_index_ = 0;
392 int to_first_minimize_index_ = 0;
393 int to_probe_index_ = 0;
396 absl::flat_hash_map<SatClause*, ClauseInfo> clauses_info_;
400 absl::AnyInvocable<void(
int lbd, absl::Span<const Literal>)>
401 add_clause_callback_ =
nullptr;
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()) {
431 newly_added_.push_back(c);
438 const std::vector<BinaryClause>&
newly_added()
const {
return newly_added_; }
442 absl::flat_hash_set<std::pair<int, int>> set_;
443 std::vector<BinaryClause> newly_added_;
494 stats_(
"BinaryImplicationGraph"),
495 time_limit_(model->GetOrCreate<
TimeLimit>()),
497 trail_(model->GetOrCreate<
Trail>()),
498 at_most_one_max_expansion_size_(
500 ->at_most_one_max_expansion_size()) {
501 trail_->RegisterPropagator(
this);
510 LOG(INFO) << stats_.StatString();
511 LOG(INFO) <<
"num_redundant_implications " << num_redundant_implications_;
517 absl::Span<const Literal>
Reason(
const Trail& trail,
int trail_index,
518 int64_t conflict_id)
const final;
521 void Resize(
int num_variables);
528 bool IsEmpty() const final {
return no_constraint_ever_added_; }
555 add_binary_callback_ = f;
569 ABSL_MUST_USE_RESULT
bool AddAtMostOne(absl::Span<const Literal> at_most_one);
582 std::vector<Literal>* c);
586 std::vector<Literal>* c,
587 absl::BitGenRef random);
608 bool IsDag()
const {
return is_dag_; }
614 return reverse_topological_order_;
620 return implications_[l];
627 if (l.
Index() >= representative_of_.size())
return l;
629 return Literal(representative_of_[l]);
655 int64_t max_num_explored_nodes = 1e8);
673 int64_t max_num_explored_nodes = 1e8,
674 double* dtime =
nullptr);
686 absl::Span<const Literal> literals, absl::Span<const double> lp_values,
687 absl::Span<const double> reduced_costs);
694 std::vector<Literal>* literals);
714 CHECK_EQ(num_redundant_literals_ % 2, 0);
715 return num_redundant_literals_;
720 return num_redundant_implications_;
729 for (
const auto& list : implications_) result += list.size();
740 template <
typename Output>
745 absl::flat_hash_set<std::pair<LiteralIndex, LiteralIndex>>
747 for (LiteralIndex
i(0);
i < implications_.size(); ++
i) {
749 for (
const Literal b : implications_[
i]) {
754 if (a <
b && duplicate_detection.insert({a, b}).second) {
755 out->AddBinaryClause(a,
b);
762 drat_proof_handler_ = drat_proof_handler;
769 CHECK(trail_->Assignment().LiteralIsTrue(new_reason));
770 reasons_[trail_index] = new_reason.
Negated();
793 return estimated_sizes_[literal];
806 BooleanVariable var, std::deque<std::vector<Literal>>* postsolve_clauses);
809 std::deque<std::vector<Literal>>* postsolve_clauses);
815 int64_t
WorkDone()
const {
return work_done_in_mark_descendants_; }
822 template <
bool use_weight = true>
824 absl::Span<const Literal> at_most_one,
840 void NotifyPossibleDuplicate(
Literal a);
844 bool FixLiteral(
Literal true_literal);
847 void RemoveRedundantLiterals(std::vector<Literal>* conflict);
851 absl::Span<const Literal> MarkDescendants(
Literal root);
857 std::vector<Literal> ExpandAtMostOne(absl::Span<const Literal> at_most_one,
858 int64_t max_num_explored_nodes);
861 std::vector<std::pair<int, int>> FilterAndSortAtMostOnes(
862 absl::Span<std::vector<Literal>> at_most_ones);
871 ABSL_MUST_USE_RESULT
bool CleanUpAndAddAtMostOnes(
int base_index);
874 bool InvariantsAreOk();
878 absl::Span<const Literal> AtMostOne(
int start)
const;
889 bool no_constraint_ever_added_ =
true;
893 std::deque<Literal> reasons_;
910 std::vector<Literal> to_clean_;
925 std::vector<Literal> at_most_one_buffer_;
926 const int at_most_one_max_expansion_size_;
927 int at_most_one_iterator_ = 0;
938 std::vector<std::vector<Literal>> tmp_cuts_;
941 int64_t num_propagations_ = 0;
942 int64_t num_inspections_ = 0;
943 int64_t num_minimization_ = 0;
944 int64_t num_literals_removed_ = 0;
945 int64_t num_redundant_implications_ = 0;
946 int64_t num_redundant_literals_ = 0;
957 std::vector<Literal> dfs_stack_;
961 int64_t work_done_in_mark_descendants_ = 0;
962 std::vector<Literal> bfs_stack_;
971 std::vector<std::pair<Literal, Literal>> tmp_removed_;
974 bool is_dag_ =
false;
975 std::vector<LiteralIndex> reverse_topological_order_;
980 std::vector<Literal> direct_implications_;
981 std::vector<Literal> direct_implications_of_negated_literal_;
987 int num_processed_fixed_variables_ = 0;
989 bool enable_sharing_ =
true;
990 std::function<void(
Literal,
Literal)> add_binary_callback_ =
nullptr;
993extern template std::vector<Literal>
995 const absl::Span<const Literal> at_most_one,
999extern template std::vector<Literal>
1001 const absl::Span<const Literal> at_most_one,
Base class to print a nice summary of a group of statistics.
const std::vector< BinaryClause > & newly_added() const
Returns the newly added BinaryClause since the last ClearNewlyAdded() call.
BinaryClauseManager()=default
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).
void ResetAtMostOneIterator()
Restarts the at_most_one iterator.
bool ComputeTransitiveReduction(bool log_info=false)
Literal RepresentativeOf(Literal l) const
int64_t num_redundant_literals() const
void Resize(int num_variables)
Resizes the data structure.
const std::vector< std::vector< Literal > > & GenerateAtMostOnesWithLargeWeight(absl::Span< const Literal > literals, absl::Span< const double > lp_values, absl::Span< const double > reduced_costs)
void ExtractAllBinaryClauses(Output *out) const
BinaryImplicationGraph(const BinaryImplicationGraph &)=delete
This type is neither copyable nor movable.
bool Propagate(Trail *trail) final
SatPropagator interface.
int64_t num_minimization() const
MinimizeClause() stats.
void RemoveDuplicates()
Clean up implications list that might have duplicates.
void MinimizeConflictWithReachability(std::vector< Literal > *c)
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.
int64_t num_literals_removed() const
bool TransformIntoMaxCliques(std::vector< std::vector< Literal > > *at_most_ones, int64_t max_num_explored_nodes=1e8)
absl::Span< const Literal > GetAllImpliedLiterals(Literal root)
void CleanupAllRemovedAndFixedVariables()
int64_t NumImplicationOnVariableRemoval(BooleanVariable var)
int64_t num_redundant_implications() const
Number of implications removed by transitive reduction.
bool IsEmpty() const final
Returns true if no constraints where ever added to this class.
bool AddBinaryClause(Literal a, Literal b)
int DirectImplicationsEstimatedSize(Literal literal) const
void RemoveFixedVariables()
absl::Span< const Literal > NextAtMostOne()
Returns the next at_most_one, or a span of size 0 when finished.
int64_t ComputeNumImplicationsForLog() const
BinaryImplicationGraph(Model *model)
bool AddImplication(Literal a, Literal b)
void RemoveBooleanVariable(BooleanVariable var, std::deque< std::vector< Literal > > *postsolve_clauses)
For all possible a => var => b, add a => b.
bool IsRedundant(Literal l) const
void MinimizeConflictExperimental(const Trail &trail, std::vector< Literal > *c)
void MinimizeConflictFirstWithTransitiveReduction(const Trail &trail, std::vector< Literal > *c, absl::BitGenRef random)
bool DetectEquivalences(bool log_info=false)
void SetDratProofHandler(DratProofHandler *drat_proof_handler)
void SetAdditionCallback(std::function< void(Literal, Literal)> f)
void MinimizeConflictFirst(const Trail &trail, std::vector< Literal > *c, SparseBitset< BooleanVariable > *marked)
const std::vector< Literal > & DirectImplications(Literal literal)
absl::Span< const Literal > Reason(const Trail &trail, int trail_index, int64_t conflict_id) const final
bool MergeAtMostOnes(absl::Span< std::vector< Literal > > at_most_ones, int64_t max_num_explored_nodes=1e8, double *dtime=nullptr)
void RemoveAllRedundantVariables(std::deque< std::vector< Literal > > *postsolve_clauses)
int64_t literal_size() const
const std::vector< LiteralIndex > & ReverseTopologicalOrder() const
void ChangeReason(int trail_index, Literal new_reason)
bool FindFailedLiteralAroundVar(BooleanVariable var, bool *is_unsat)
BinaryImplicationGraph & operator=(const BinaryImplicationGraph &)=delete
LiteralIndex RandomImpliedLiteral(Literal lhs)
ABSL_MUST_USE_RESULT bool AddAtMostOne(absl::Span< const Literal > at_most_one)
bool IsRemoved(Literal l) const
std::vector< absl::Span< const Literal > > HeuristicAmoPartition(std::vector< Literal > *literals)
const absl::InlinedVector< Literal, 6 > & Implications(Literal l) const
~BinaryImplicationGraph() override
void EnableSharing(bool enable)
int64_t num_inspections() const
Number of literals inspected by this class during propagation.
SatClause * InprocessingAddClause(absl::Span< const Literal > new_clause)
void ResetToProbeIndex()
Restart the scans.
int64_t num_removable_clauses() const
void LazyDetach(SatClause *clause)
void InprocessingRemoveClause(SatClause *clause)
These must only be called between [Detach/Attach]AllClauses() calls.
SatClause * NextClauseToMinimize()
void ResetToMinimizeIndex()
int64_t num_clauses() const
bool IsRemovable(SatClause *const clause) const
void Detach(SatClause *clause)
~ClauseManager() override
void DeleteRemovedClauses()
absl::Span< const Literal > Reason(const Trail &trail, int trail_index, int64_t conflict_id) const final
int64_t num_inspected_clauses() const
Total number of clauses inspected during calls to PropagateOnFalse().
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.
int64_t literal_size() const
The number of different literals (always twice the number of variables).
int64_t num_inspected_clause_literals() const
SatClause * NextClauseToProbe()
Returns the next clause to probe in round-robin order.
int64_t num_watched_clauses() const
Number of clauses currently watched.
absl::AnyInvocable< void(int lbd, absl::Span< const Literal >)> TakeAddClauseCallback()
void SetDratProofHandler(DratProofHandler *drat_proof_handler)
ClauseManager(Model *model)
--— ClauseManager --—
void EnsureNewClauseIndexInitialized()
SatClause * ReasonClause(int trail_index) const
void SetAddClauseCallback(absl::AnyInvocable< void(int lbd, absl::Span< const Literal >)> add_clause_callback)
absl::flat_hash_map< SatClause *, ClauseInfo > * mutable_clauses_info()
SatClause * AddRemovableClause(absl::Span< const Literal > literals, Trail *trail, int lbd)
const std::vector< SatClause * > & AllClausesInCreationOrder() const
void Resize(int num_variables)
Must be called before adding clauses referring to such variables.
ClauseManager & operator=(const ClauseManager &)=delete
ABSL_MUST_USE_RESULT bool InprocessingFixLiteral(Literal true_literal)
This one do not need the clause to be detached.
void Attach(SatClause *clause, Trail *trail)
SatClause * NextNewClauseToMinimize()
ABSL_MUST_USE_RESULT bool InprocessingRewriteClause(SatClause *clause, absl::Span< const Literal > new_clause)
const std::vector< Watcher > & WatcherListOnFalse(Literal false_literal) const
bool Propagate(Trail *trail) final
SatPropagator API.
LiteralIndex Index() const
const Literal * end() const
Literal FirstLiteral() const
static SatClause * Create(absl::Span< const Literal > literals)
--— SatClause --—
absl::Span< const Literal > AsSpan() const
Returns a Span<> representation of the clause.
const Literal * begin() const
Allows for range based iteration: for (Literal literal : clause) {}.
absl::Span< const Literal > PropagationReason() const
bool IsSatisfied(const VariablesAssignment &assignment) const
Literal PropagatedLiteral() const
friend class ClauseManager
int size() const
Number of literals in the clause.
std::string DebugString() const
bool RemoveFixedLiteralsAndTestIfTrue(const VariablesAssignment &assignment)
Literal SecondLiteral() const
SatPropagator(const std::string &name)
const LiteralIndex kNoLiteralIndex(-1)
In SWIG mode, we don't want anything besides these top-level includes.
#define IF_STATS_ENABLED(instructions)
A binary clause. This is used by BinaryClauseManager.
bool operator!=(BinaryClause o) const
BinaryClause(Literal _a, Literal _b)
bool operator==(BinaryClause o) const
bool protected_during_next_cleanup
Watcher(SatClause *c, Literal b, int i=2)