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"
41#include "ortools/sat/sat_parameters.pb.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_(
499 model->GetOrCreate<SatParameters>()
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);
525 return num_implications_ == 0 && at_most_ones_.empty();
553 add_binary_callback_ = f;
567 ABSL_MUST_USE_RESULT
bool AddAtMostOne(absl::Span<const Literal> at_most_one);
580 std::vector<Literal>* c);
584 std::vector<Literal>* c,
585 absl::BitGenRef random);
606 bool IsDag()
const {
return is_dag_; }
612 return reverse_topological_order_;
618 return implications_[l];
625 if (l.
Index() >= representative_of_.size())
return l;
627 return Literal(representative_of_[l]);
653 int64_t max_num_explored_nodes = 1e8);
671 int64_t max_num_explored_nodes = 1e8,
672 double* dtime =
nullptr);
684 absl::Span<const Literal> literals, absl::Span<const double> lp_values,
685 absl::Span<const double> reduced_costs);
692 std::vector<Literal>* literals);
712 CHECK_EQ(num_redundant_literals_ % 2, 0);
713 return num_redundant_literals_;
718 return num_redundant_implications_;
735 template <
typename Output>
740 absl::flat_hash_set<std::pair<LiteralIndex, LiteralIndex>>
742 for (LiteralIndex
i(0);
i < implications_.size(); ++
i) {
744 for (
const Literal b : implications_[
i]) {
749 if (a <
b && duplicate_detection.insert({a, b}).second) {
750 out->AddBinaryClause(a,
b);
757 drat_proof_handler_ = drat_proof_handler;
764 CHECK(trail_->Assignment().LiteralIsTrue(new_reason));
765 reasons_[trail_index] = new_reason.
Negated();
788 return estimated_sizes_[literal];
801 BooleanVariable var, std::deque<std::vector<Literal>>* postsolve_clauses);
804 std::deque<std::vector<Literal>>* postsolve_clauses);
810 int64_t
WorkDone()
const {
return work_done_in_mark_descendants_; }
813 template <
bool use_weight = true>
815 absl::Span<const Literal> at_most_one,
831 void NotifyPossibleDuplicate(
Literal a);
835 bool FixLiteral(
Literal true_literal);
838 void RemoveRedundantLiterals(std::vector<Literal>* conflict);
842 void MarkDescendants(
Literal root);
848 std::vector<Literal> ExpandAtMostOne(absl::Span<const Literal> at_most_one,
849 int64_t max_num_explored_nodes);
852 std::vector<std::pair<int, int>> FilterAndSortAtMostOnes(
853 absl::Span<std::vector<Literal>> at_most_ones);
862 bool CleanUpAndAddAtMostOnes(
int base_index);
865 bool InvariantsAreOk();
869 absl::Span<const Literal> AtMostOne(
int start)
const;
879 std::deque<Literal> reasons_;
893 int64_t num_implications_ = 0;
897 std::vector<Literal> to_clean_;
912 std::vector<Literal> at_most_one_buffer_;
913 const int at_most_one_max_expansion_size_;
914 int at_most_one_iterator_ = 0;
925 std::vector<std::vector<Literal>> tmp_cuts_;
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;
944 std::vector<Literal> dfs_stack_;
948 int64_t work_done_in_mark_descendants_ = 0;
949 std::vector<Literal> bfs_stack_;
958 std::vector<std::pair<Literal, Literal>> tmp_removed_;
961 bool is_dag_ =
false;
962 std::vector<LiteralIndex> reverse_topological_order_;
967 std::vector<Literal> direct_implications_;
968 std::vector<Literal> direct_implications_of_negated_literal_;
974 int num_processed_fixed_variables_ = 0;
976 bool enable_sharing_ =
true;
977 std::function<void(
Literal,
Literal)> add_binary_callback_ =
nullptr;
980extern template std::vector<Literal>
982 const absl::Span<const Literal> at_most_one,
986extern template std::vector<Literal>
988 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.
int64_t num_implications() const
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)
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 there is no constraints in 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.
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)