17#ifndef ORTOOLS_SAT_CLAUSE_H_
18#define ORTOOLS_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/functional/any_invocable.h"
32#include "absl/functional/function_ref.h"
33#include "absl/log/check.h"
34#include "absl/types/span.h"
64 void operator delete(
void* p) {
69 int size()
const {
return size_; }
70 bool empty()
const {
return size_ == 0; }
80 const Literal*
end()
const {
return &(literals_[size_]); }
96 return absl::Span<const Literal>(&(literals_[1]), size_ - 1);
100 absl::Span<const Literal>
AsSpan()
const {
101 return absl::Span<const Literal>(&(literals_[0]), size_);
116 Literal* literals() {
return &(literals_[0]); }
121 void Clear() { size_ = 0; }
129 bool RemoveFixedLiteralsAndTestIfTrue(
const VariablesAssignment& assignment);
133 void Rewrite(absl::Span<const Literal> new_clause) {
135 for (
const Literal l : new_clause) literals_[size_++] = l;
142 Literal literals_[0];
191 void Resize(
int num_variables);
195 absl::Span<const Literal>
Reason(
const Trail& trail,
int trail_index,
196 int64_t conflict_id)
const final;
212 bool AddClause(ClauseId
id, absl::Span<const Literal> literals,
Trail* trail,
214 bool AddClause(absl::Span<const Literal> literals);
219 Trail* trail,
int lbd);
223 return deletion_counters_;
255 return clauses_info_.contains(clause);
259 return &clauses_info_;
262 auto it = clauses_info_.find(clause);
263 if (it == clauses_info_.end())
return 0;
264 return it->second.lbd;
267 clauses_info_.erase(clause);
270 for (
auto& entry : clauses_info_) {
271 entry.second.activity *= scaling_factor;
282 return num_inspected_clause_literals_;
286 int64_t
literal_size()
const {
return needs_cleaning_.size().value(); }
296 const auto it = clause_id_.find(clause);
297 return it != clause_id_.end() ? it->second :
kNoClauseId;
301 clause_id_[clause] = id;
323 if (n > clauses_.size()) n = clauses_.size();
324 return absl::MakeSpan(clauses_).subspan(clauses_.size() - n);
330 return num_to_minimize_index_resets_;
336 if (to_first_minimize_index_ > 0)
return;
337 to_first_minimize_index_ = clauses_.size();
360 SatClause* clause, absl::Span<const Literal> new_clause,
361 absl::Span<const ClauseId> clause_ids = {});
369 Literal true_literal);
371 Literal true_literal, absl::Span<const ClauseId> clause_ids = {});
406 return watchers_on_false_[false_literal];
410 absl::AnyInvocable<
void(
int lbd, ClauseId
id, absl::Span<const Literal>)>
411 add_clause_callback) {
412 add_clause_callback_ = std::move(add_clause_callback);
417 absl::AnyInvocable<void(
int lbd, ClauseId
id, absl::Span<const Literal>)>
419 return std::move(add_clause_callback_);
451 absl::Span<const Literal> literals, std::vector<ClauseId>* clause_ids,
453 std::optional<absl::FunctionRef<ClauseId(
int,
int)>> root_literals = {});
458 bool AttachAndPropagate(SatClause* clause,
Trail* trail);
463 void AttachOnFalse(Literal literal, Literal blocking_literal,
469 ClauseIdGenerator* clause_id_generator_;
475 std::vector<SatClause*> reasons_;
480 bool is_clean_ =
true;
482 const SatParameters& parameters_;
483 const VariablesAssignment& assignment_;
484 BinaryImplicationGraph* implication_graph_;
488 std::vector<int64_t> deletion_counters_;
489 int64_t num_inspected_clauses_ = 0;
490 int64_t num_inspected_clause_literals_ = 0;
491 int64_t num_watched_clauses_ = 0;
492 int64_t num_lbd_promotions_ = 0;
496 bool all_clauses_are_attached_ =
true;
503 std::vector<SatClause*> clauses_;
506 int to_minimize_index_ = 0;
508 int num_to_minimize_index_resets_ = 0;
509 int to_first_minimize_index_ = 0;
510 int to_probe_index_ = 0;
512 absl::flat_hash_map<const SatClause*, ClauseId> clause_id_;
514 absl::flat_hash_map<SatClause*, ClauseInfo> clauses_info_;
516 LratProofHandler* lrat_proof_handler_ =
nullptr;
519 std::vector<ClauseId> clause_ids_scratchpad_;
521 absl::AnyInvocable<void(
int lbd, ClauseId
id, absl::Span<const Literal>)>
522 add_clause_callback_ =
nullptr;
525 std::vector<int> marked_trail_indices_heap_;
526 std::vector<ClauseId> tmp_clause_ids_for_append_clauses_fixing_;
553 std::pair<int, int> p(c.a.SignedValue(), c.b.SignedValue());
554 if (p.first > p.second) std::swap(p.first, p.second);
555 if (set_.find(p) == set_.end()) {
557 newly_added_.push_back(c);
564 const std::vector<BinaryClause>&
newly_added()
const {
return newly_added_; }
568 absl::flat_hash_set<std::pair<int, int>> set_;
569 std::vector<BinaryClause> newly_added_;
573class LratEquivalenceHelper;
631 absl::Span<const Literal>
Reason(
const Trail& trail,
int trail_index,
632 int64_t conflict_id)
const final;
636 void Resize(
int num_variables);
643 bool IsEmpty() const final {
return no_constraint_ever_added_; }
661 bool delete_non_representative_id =
true) {
662 return AddBinaryClauseInternal(
id, a,
b,
false,
663 delete_non_representative_id);
673 if (a.
Variable() >
b.Variable()) std::swap(a,
b);
674 const auto it = clause_id_.find(std::make_pair(a,
b));
675 return it != clause_id_.end() ? it->second :
kNoClauseId;
688 add_binary_callback_ = f;
702 ABSL_MUST_USE_RESULT
bool AddAtMostOne(absl::Span<const Literal> at_most_one);
716 std::vector<ClauseId>* clause_ids,
717 bool also_use_decisions);
726 std::vector<ClauseId>* clause_ids);
747 bool IsDag()
const {
return is_dag_; }
753 return reverse_topological_order_;
759 return implications_and_amos_[l].literals();
766 if (l.
Index() >= representative_of_.size())
return l;
768 return Literal(representative_of_[l]);
794 int64_t max_num_explored_nodes = 1e8);
812 int64_t max_num_explored_nodes = 1e8,
813 double* dtime =
nullptr);
825 absl::Span<const Literal> literals, absl::Span<const double> lp_values,
826 absl::Span<const double> reduced_costs);
833 std::vector<Literal>* literals);
853 CHECK_EQ(num_redundant_literals_ % 2, 0);
854 return num_redundant_literals_;
860 CHECK_EQ(num_current_equivalences_ % 2, 0);
861 return num_current_equivalences_;
866 return num_redundant_implications_;
875 for (
const auto& list : implications_and_amos_) {
876 result += list.num_literals();
888 template <
typename Output>
893 absl::flat_hash_set<std::pair<LiteralIndex, LiteralIndex>>
895 for (LiteralIndex
i(0);
i < implications_and_amos_.size(); ++
i) {
897 for (
const Literal b : implications_and_amos_[
i].literals()) {
902 if (a <
b && duplicate_detection.insert({a, b}).second) {
903 out->AddBinaryClause(a,
b);
914 return AddBinaryClauseInternal(
id, a,
b,
true);
936 return estimated_sizes_[literal];
949 BooleanVariable var, std::deque<std::vector<Literal>>* postsolve_clauses);
952 std::deque<std::vector<Literal>>* postsolve_clauses);
958 int64_t
WorkDone()
const {
return work_done_in_mark_descendants_; }
969 template <
bool use_weight = true>
971 absl::Span<const Literal> at_most_one,
997 return implications_and_amos_[lit].offsets();
1004 absl::Span<const ClauseId> clause_ids = {});
1010 bool change_reason =
false,
1011 bool delete_non_representative_id =
true);
1014 void NotifyPossibleDuplicate(
Literal a);
1019 ABSL_MUST_USE_RESULT
bool CleanUpImplicationList(
Literal a);
1022 if (a.
Variable() >
b.Variable()) std::swap(a,
b);
1023 clause_id_[{a,
b}] = id;
1030 template <
bool fill_clause_
ids = false>
1031 void RemoveRedundantLiterals(std::vector<Literal>* conflict,
1032 std::vector<ClauseId>* clause_ids =
nullptr);
1036 void AppendImplicationChain(Literal literal,
1037 std::vector<ClauseId>* clause_ids);
1042 template <
bool fill_implied_by = false>
1043 absl::Span<const Literal> MarkDescendants(Literal root);
1049 std::vector<Literal> ExpandAtMostOne(absl::Span<const Literal> at_most_one,
1050 int64_t max_num_explored_nodes);
1053 std::vector<std::pair<int, int>> FilterAndSortAtMostOnes(
1054 absl::Span<std::vector<Literal>> at_most_ones);
1063 ABSL_MUST_USE_RESULT
bool CleanUpAndAddAtMostOnes(
int base_index);
1066 bool InvariantsAreOk();
1070 absl::Span<const Literal> AtMostOne(
int start)
const;
1074 ModelRandomGenerator* random_;
1076 ClauseIdGenerator* clause_id_generator_;
1077 LratProofHandler* lrat_proof_handler_ =
nullptr;
1083 bool no_constraint_ever_added_ =
true;
1087 std::deque<Literal> reasons_;
1090 absl::flat_hash_map<std::pair<Literal, Literal>, ClauseId> clause_id_;
1093 std::vector<std::pair<Literal, Literal>> changed_reasons_on_trail_;
1102 implications_and_amos_;
1106 std::vector<Literal> to_clean_;
1108 std::vector<Literal> at_most_one_buffer_;
1109 const int at_most_one_max_expansion_size_;
1110 int at_most_one_iterator_ = 0;
1121 std::vector<std::vector<Literal>> tmp_cuts_;
1124 int64_t num_propagations_ = 0;
1125 int64_t num_inspections_ = 0;
1126 int64_t num_minimization_ = 0;
1127 int64_t num_literals_removed_ = 0;
1128 int64_t num_redundant_implications_ = 0;
1129 int64_t num_redundant_literals_ = 0;
1130 int64_t num_current_equivalences_ = 0;
1140 std::vector<Literal> tmp_to_keep_;
1152 std::vector<Literal> dfs_stack_;
1156 int64_t work_done_in_mark_descendants_ = 0;
1157 std::vector<Literal> bfs_stack_;
1166 std::vector<std::pair<Literal, Literal>> tmp_removed_;
1169 bool is_dag_ =
false;
1170 std::vector<LiteralIndex> reverse_topological_order_;
1175 std::vector<Literal> direct_implications_;
1176 std::vector<Literal> direct_implications_of_negated_literal_;
1182 int num_processed_fixed_variables_ = 0;
1184 bool enable_sharing_ =
true;
1185 std::function<void(Literal, Literal)> add_binary_callback_ =
nullptr;
1188extern template std::vector<Literal>
1190 const absl::Span<const Literal> at_most_one,
1194extern template std::vector<Literal>
1196 const absl::Span<const Literal> at_most_one,
const std::vector< BinaryClause > & newly_added() const
BinaryClauseManager()=default
BinaryClauseManager(const BinaryClauseManager &)=delete
BinaryClauseManager & operator=(const BinaryClauseManager &)=delete
int64_t num_propagations() const
void AppendImplicationChains(absl::Span< const Literal > literals, std::vector< ClauseId > *clause_ids)
void ResetAtMostOneIterator()
bool ComputeTransitiveReduction(bool log_info=false)
Literal RepresentativeOf(Literal l) const
int64_t num_redundant_literals() const
void ClearImpliedLiterals()
void Resize(int num_variables)
absl::Span< const int > AtMostOneIndices(Literal lit) const
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 Reimply(Trail *trail, int old_trail_index) final
void ExtractAllBinaryClauses(Output *out) const
BinaryImplicationGraph(const BinaryImplicationGraph &)=delete
bool Propagate(Trail *trail) final
int64_t num_minimization() const
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)
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 > Implications(Literal l) const
absl::Span< const Literal > GetAllImpliedLiterals(Literal root)
void CleanupAllRemovedAndFixedVariables()
int64_t NumImplicationOnVariableRemoval(BooleanVariable var)
bool AddBinaryClause(ClauseId id, Literal a, Literal b, bool delete_non_representative_id=true)
int64_t num_redundant_implications() const
bool IsEmpty() const final
bool AddBinaryClause(Literal a, Literal b)
bool RemoveDuplicatesAndFixedVariables()
int DirectImplicationsEstimatedSize(Literal literal) const
void RemoveFixedVariables()
absl::Span< const Literal > NextAtMostOne()
bool FixLiteral(Literal true_literal, absl::Span< const ClauseId > clause_ids={})
int64_t ComputeNumImplicationsForLog() const
BinaryImplicationGraph(Model *model)
bool AddBinaryClauseAndChangeReason(ClauseId id, Literal a, Literal b)
bool AddImplication(Literal a, Literal b)
void RemoveBooleanVariable(BooleanVariable var, std::deque< std::vector< Literal > > *postsolve_clauses)
void MinimizeConflictFirst(const Trail &trail, std::vector< Literal > *c, SparseBitset< BooleanVariable > *marked, std::vector< ClauseId > *clause_ids, bool also_use_decisions)
bool IsRedundant(Literal l) const
bool LiteralIsImplied(Literal l) const
bool DetectEquivalences(bool log_info=false)
void SetAdditionCallback(std::function< void(Literal, Literal)> f)
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
bool FindFailedLiteralAroundVar(BooleanVariable var, bool *is_unsat)
BinaryImplicationGraph & operator=(const BinaryImplicationGraph &)=delete
LiteralIndex RandomImpliedLiteral(Literal lhs)
ClauseId GetClauseId(Literal a, Literal b) const
ABSL_MUST_USE_RESULT bool AddAtMostOne(absl::Span< const Literal > at_most_one)
int64_t num_current_equivalences() const
bool IsRemoved(Literal l) const
std::vector< absl::Span< const Literal > > HeuristicAmoPartition(std::vector< Literal > *literals)
~BinaryImplicationGraph() override
friend class LratEquivalenceHelper
void EnableSharing(bool enable)
int64_t num_inspections() const
SatClause * InprocessingAddClause(absl::Span< const Literal > new_clause)
int64_t num_lbd_promotions() const
int64_t num_removable_clauses() const
bool AddClause(ClauseId id, absl::Span< const Literal > literals, Trail *trail, int lbd)
ABSL_MUST_USE_RESULT bool InprocessingRewriteClause(SatClause *clause, absl::Span< const Literal > new_clause, absl::Span< const ClauseId > clause_ids={})
absl::Span< SatClause * > LastNClauses(int n)
SatClause * NextClauseToMinimize()
absl::Span< const int64_t > DeletionCounters() const
void LazyDelete(SatClause *clause, DeletionSourceForStat source)
int64_t num_clauses() const
bool IsRemovable(SatClause *const clause) const
bool RemoveFixedLiteralsAndTestIfTrue(SatClause *clause)
~ClauseManager() override
ABSL_MUST_USE_RESULT bool InprocessingFixLiteral(Literal true_literal, absl::Span< const ClauseId > clause_ids={})
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
ClauseManager(const ClauseManager &)=delete
ABSL_MUST_USE_RESULT bool InprocessingAddUnitClause(ClauseId unit_clause_id, Literal true_literal)
SatClause * AddRemovableClause(ClauseId id, absl::Span< const Literal > literals, Trail *trail, int lbd)
int64_t literal_size() const
int64_t num_inspected_clause_literals() const
void SetAddClauseCallback(absl::AnyInvocable< void(int lbd, ClauseId id, absl::Span< const Literal >)> add_clause_callback)
void ChangeLbdIfBetter(SatClause *clause, int new_lbd)
SatClause * NextClauseToProbe()
ClauseId ReasonClauseId(Literal literal) const
int64_t num_watched_clauses() const
void Reimply(Trail *trail, int old_trail_index) final
ClauseManager(Model *model)
int LbdOrZeroIfNotRemovable(SatClause *const clause) const
SatClause * ReasonClauseOrNull(BooleanVariable var) const
void EnsureNewClauseIndexInitialized()
SatClause * ReasonClause(int trail_index) const
absl::flat_hash_map< SatClause *, ClauseInfo > * mutable_clauses_info()
int64_t NumToMinimizeIndexResets() const
const std::vector< SatClause * > & AllClausesInCreationOrder() const
void Resize(int num_variables)
ClauseManager & operator=(const ClauseManager &)=delete
void Attach(SatClause *clause, Trail *trail)
SatClause * NextNewClauseToMinimize()
void SetClauseId(const SatClause *clause, ClauseId id)
absl::AnyInvocable< void(int lbd, ClauseId id, absl::Span< const Literal >)> TakeAddClauseCallback()
void AppendClauseIdsFixing(absl::Span< const Literal > literals, std::vector< ClauseId > *clause_ids, LiteralIndex decision=kNoLiteralIndex, std::optional< absl::FunctionRef< ClauseId(int, int)> > root_literals={})
const std::vector< Watcher > & WatcherListOnFalse(Literal false_literal) const
ClauseId GetClauseId(const SatClause *clause) const
void KeepClauseForever(SatClause *const clause)
bool ClauseIsUsedAsReason(SatClause *clause) const
void RescaleClauseActivities(double scaling_factor)
bool Propagate(Trail *trail) final
LiteralIndex Index() const
BooleanVariable Variable() const
const Literal * end() const
Literal FirstLiteral() const
static SatClause * Create(absl::Span< const Literal > literals)
absl::Span< const Literal > AsSpan() const
const Literal * begin() const
absl::Span< const Literal > PropagationReason() const
bool IsSatisfied(const VariablesAssignment &assignment) const
Literal PropagatedLiteral() const
friend class ClauseManager
std::string DebugString() const
Literal SecondLiteral() const
SatPropagator(const std::string &name)
const LiteralIndex kNoLiteralIndex(-1)
constexpr ClauseId kNoClauseId(0)
@ SUBSUMPTION_INPROCESSING
@ SUBSUMPTION_CONFLICT_EXTRA
bool operator!=(BinaryClause o) const
BinaryClause(Literal _a, Literal _b)
bool operator==(BinaryClause o) const
int32_t num_cleanup_rounds_since_last_bumped
Watcher(SatClause *c, Literal b, int i=2)