27#include "absl/log/check.h"
28#include "absl/strings/str_cat.h"
29#include "absl/types/span.h"
31#include "ortools/sat/boolean_problem.pb.h"
34#include "ortools/sat/sat_parameters.pb.h"
62 std::function<
Literal(
int x)> create_lit,
68 node.create_lit_ = std::move(create_lit);
71 node.literals_.push_back(node.create_lit_(
lb));
75 node.for_sorting_ = node.literals_[0].Variable();
81 CHECK(literals_.empty()) <<
"Already initialized";
83 const BooleanVariable first_var_index(solver->
NumVariables());
85 for (
int i = 0;
i < n; ++
i) {
86 literals_.push_back(
Literal(first_var_index +
i,
true));
91 lb_ =
a->lb_ +
b->lb_;
93 depth_ = 1 + std::max(
a->depth_,
b->depth_);
96 for_sorting_ = first_var_index;
101 CHECK_GE(
nodes.size(), 2);
102 CHECK(literals_.empty()) <<
"Already initialized";
105 literals_.push_back(new_literal);
112 std::vector<Literal> clause{new_literal.
Negated()};
115 clause.push_back(node->literals_[0]);
117 depth_ = std::max(node->depth_ + 1, depth_);
118 for_sorting_ = std::min(for_sorting_, node->for_sorting_);
128 CHECK(literals_.empty()) <<
"Already initialized";
129 const BooleanVariable first_var_index(solver->
NumVariables());
131 literals_.emplace_back(first_var_index,
true);
134 ub_ =
a->ub_ +
b->ub_;
135 lb_ =
a->lb_ +
b->lb_;
136 depth_ = 1 + std::max(
a->depth_,
b->depth_);
139 for_sorting_ = std::min(
a->for_sorting_,
b->for_sorting_);
144 CHECK(literals_.empty()) <<
"Already initialized";
147 ub_ =
a->ub_ +
b->ub_;
149 weight_lb_ =
a->lb_ +
b->lb_;
150 lb_ = weight_lb_ + 1;
151 depth_ = 1 + std::max(
a->depth_,
b->depth_);
154 for_sorting_ = std::min(
a->for_sorting_,
b->for_sorting_);
159 if (create_lit_ !=
nullptr) {
160 literals_.emplace_back(create_lit_(
current_ub()));
162 CHECK_NE(solver,
nullptr);
163 literals_.emplace_back(BooleanVariable(solver->
NumVariables()),
true);
166 if (literals_.size() > 1) {
168 literals_[literals_.size() - 2]);
174 if (!literals_.empty()) {
176 while (
i < literals_.size() &&
181 literals_.erase(literals_.begin(), literals_.begin() +
i);
182 while (!literals_.empty() &&
184 literals_.pop_back();
185 ub_ = lb_ + literals_.size();
189 if (weight_lb_ >= lb_)
return Coefficient(0);
190 const Coefficient result = Coefficient(lb_ - weight_lb_) * weight_;
196 CHECK_GT(weight_, 0);
197 const Coefficient num_allowed = (gap / weight_);
198 if (num_allowed > std::numeric_limits<int>::max() / 2)
return;
200 std::max(0, (weight_lb_ - lb_) +
static_cast<int>(num_allowed.value()));
201 if (
size() <= new_size)
return;
202 for (
int i = new_size;
i <
size(); ++
i) {
205 literals_.resize(new_size);
206 ub_ = lb_ + new_size;
211 for (
int i = 1;
i <
size(); ++
i) {
231 const int index = weight_lb_ - lb_;
232 return index < literals_.size() && literals_[
index].Negated() == other;
237 const int index = weight_lb_ - lb_;
238 CHECK_GE(
index, 0) <<
"Not reduced?";
239 while (
index >= literals_.size()) {
242 return literals_[
index].Negated();
246 CHECK_LT(weight_lb_ - lb_, literals_.size());
251 return weight_ == 0 || weight_lb_ >= ub_;
257 absl::StrAppend(&result,
"depth:", depth_);
258 absl::StrAppend(&result,
" [", lb_,
",", lb_ + literals_.size(),
"]");
259 absl::StrAppend(&result,
" ub:", ub_);
260 absl::StrAppend(&result,
" weight:", weight_.value());
261 absl::StrAppend(&result,
" weight_lb:", weight_lb_);
262 absl::StrAppend(&result,
" values:");
263 const size_t limit = 20;
265 for (
int i = 0;
i < std::min(literals_.size(), limit); ++
i) {
275 absl::StrAppend(&result,
" val:", lb_ +
value);
291 std::vector<EncodingNode*> to_process;
292 to_process.push_back(node);
299 const bool complete_encoding =
false;
301 while (!to_process.empty()) {
305 to_process.pop_back();
308 if (
a ==
nullptr)
continue;
309 CHECK_NE(solver,
nullptr);
320 if (
a->current_ub() !=
a->ub()) {
321 CHECK_GE(
a->current_ub() - 1 +
b->lb(), target - 1);
322 if (
a->current_ub() - 1 +
b->lb() < target) {
323 CHECK(
a->IncreaseCurrentUB(solver));
324 to_process.push_back(
a);
329 if (
b->current_ub() !=
b->ub()) {
330 CHECK_GE(
b->current_ub() - 1 +
a->lb(), target - 1);
331 if (
b->current_ub() - 1 +
a->lb() < target) {
332 CHECK(
b->IncreaseCurrentUB(solver));
333 to_process.push_back(
b);
338 for (
int ia =
a->lb(); ia < a->current_ub(); ++ia) {
339 const int ib = target - ia;
340 if (complete_encoding && ib >=
b->lb() && ib < b->current_ub()) {
343 a->GreaterThan(ia),
b->GreaterThan(ib));
345 if (complete_encoding && ib ==
b->ub()) {
350 if (ib - 1 ==
b->lb() - 1) {
354 if ((ib - 1) >=
b->lb() && (ib - 1) <
b->current_ub()) {
364 const int ib = target - (
a->lb() - 1);
365 if ((ib - 1) ==
b->lb() - 1) {
368 if ((ib - 1) >=
b->lb() && (ib - 1) <
b->current_ub()) {
376 const int ib = target -
a->ub();
377 if (complete_encoding && ib >=
b->lb() && ib < b->current_ub()) {
392 std::min(Coefficient(
a->size() +
b->size()),
upper_bound).value();
394 for (
int ia = 0; ia <
a->size(); ++ia) {
395 if (ia +
b->size() <
size) {
406 for (
int ib = 0; ib <
b->size(); ++ib) {
407 if (ib +
a->size() <
size) {
418 for (
int ia = 0; ia <
a->size(); ++ia) {
419 for (
int ib = 0; ib <
b->size(); ++ib) {
420 if (ia + ib <
size) {
425 if (ia + ib + 1 <
size) {
432 b->literal(ib).Negated());
440 const std::vector<EncodingNode*>&
nodes,
442 std::deque<EncodingNode>* repository) {
443 std::deque<EncodingNode*> dq(
nodes.begin(),
nodes.end());
444 while (dq.size() > 1) {
450 dq.push_back(&repository->back());
456struct SortEncodingNodePointers {
457 bool operator()(EncodingNode*
a, EncodingNode*
b)
const {
return *
a < *
b; }
462 Coefficient
weight,
const std::vector<EncodingNode*>&
nodes,
463 SatSolver* solver, std::deque<EncodingNode>* repository) {
464 std::priority_queue<EncodingNode*, std::vector<EncodingNode*>,
465 SortEncodingNodePointers>
467 while (pq.size() > 2) {
473 pq.push(&repository->back());
476 CHECK_EQ(pq.size(), 2);
491bool EncodingNodeByWeight(
const EncodingNode*
a,
const EncodingNode*
b) {
495bool EncodingNodeByDepth(
const EncodingNode*
a,
const EncodingNode*
b) {
496 return a->depth() <
b->depth();
520 n->ApplyWeightUpperBound(gap, solver);
530 switch (solver->
parameters().max_sat_assumption_order()) {
531 case SatParameters::DEFAULT_ASSUMPTION_ORDER:
533 case SatParameters::ORDER_ASSUMPTION_BY_DEPTH:
534 std::sort(
nodes->begin(),
nodes->end(), EncodingNodeByDepth);
536 case SatParameters::ORDER_ASSUMPTION_BY_WEIGHT:
537 std::sort(
nodes->begin(),
nodes->end(), EncodingNodeByWeight);
540 if (solver->
parameters().max_sat_reverse_assumption_order()) {
548 const std::vector<EncodingNode*>&
nodes,
551 std::vector<Literal> assumptions;
553 if (n->weight() >= stratified_lower_bound) {
554 assumptions.push_back(n->GetAssumption(solver));
561 const std::vector<Literal>& core) {
564 for (
int i = 0;
i < core.size(); ++
i) {
576 Coefficient result(0);
578 CHECK_GT(n->weight(), 0);
580 result = std::max(result, n->weight());
587 Coefficient min_weight, Coefficient gap,
591 if (core.size() == 1) {
596 std::vector<EncodingNode*> to_merge;
599 std::vector<EncodingNode*> new_nodes;
600 for (
int i = 0;
i < core.size(); ++
i) {
604 for (; !nodes_[
index]->AssumptionIs(core[
i]); ++
index) {
605 CHECK_LT(
index, nodes_.size());
606 new_nodes.push_back(nodes_[
index]);
608 CHECK_LT(
index, nodes_.size());
612 if (alternative_encoding_ && node->
ub() > node->
lb() + 1) {
616 new_nodes.push_back(node);
619 repository_.push_back(
621 repository_.back().set_depth(node->
depth());
622 node = &repository_.back();
625 to_merge.push_back(node);
632 if (node->
weight() > min_weight) {
634 new_nodes.push_back(node);
639 new_nodes.push_back(nodes_[
index]);
647 bool in_exactly_one = (2 * min_weight) > gap;
653 if (!in_exactly_one) {
655 std::vector<Literal> bool_nodes;
656 absl::flat_hash_map<LiteralIndex, int> node_indices;
657 for (
int i = 0;
i < to_merge.size(); ++
i) {
660 if (node.
size() != 1)
continue;
661 if (node.
ub() != node.
lb() + 1)
continue;
663 if (node_indices.contains(node.
literal(0).
Index()))
continue;
665 bool_nodes.push_back(node.
literal(0));
671 std::vector<int> buffer;
672 std::vector<absl::Span<const Literal>> decomposition;
673 if (params_.core_minimization_level() > 1 && bool_nodes.size() < 300 &&
674 bool_nodes.size() > 1) {
675 const auto& assignment = sat_solver_->
Assignment();
676 const int size = bool_nodes.size();
677 std::vector<std::vector<int>>
graph(
size);
678 for (
int i = 0;
i <
size; ++
i) {
684 for (
int j = 0; j <
size; ++j) {
685 if (
i == j)
continue;
686 if (assignment.LiteralIsFalse(bool_nodes[j])) {
701 for (std::vector<int>& adj :
graph) {
704 const std::vector<absl::Span<int>> index_decompo =
708 std::vector<Literal> new_order;
709 for (
const int i : buffer) new_order.push_back(bool_nodes[
i]);
710 bool_nodes = new_order;
711 for (
const auto span : index_decompo) {
712 if (span.size() == 1)
continue;
713 decomposition.push_back(absl::MakeSpan(
714 bool_nodes.data() + (span.data() - buffer.data()), span.size()));
721 if (decomposition.size() == 1 && decomposition[0].size() == core.size()) {
722 in_exactly_one =
true;
725 int num_in_decompo = 0;
726 if (!in_exactly_one) {
727 for (
const auto amo : decomposition) {
728 num_in_decompo += amo.size();
731 std::vector<EncodingNode*> amo_nodes;
733 const int index = node_indices.at(l.Index());
734 amo_nodes.push_back(to_merge[
index]);
735 to_merge[
index] =
nullptr;
743 to_merge.push_back(n);
745 if (num_in_decompo > 0) {
746 absl::StrAppend(info,
" amo:", decomposition.size(),
747 " lit:", num_in_decompo);
753 if (node ==
nullptr)
continue;
754 to_merge[new_size++] = node;
756 to_merge.resize(new_size);
763 if (in_exactly_one) {
766 node->TransformToBoolean(sat_solver_);
769 absl::StrAppend(info,
" exo");
771 nodes_.push_back(&repository_.back());
776 std::vector<LiteralWithCoeff> cst;
777 cst.reserve(core.size());
779 cst.emplace_back(l.Negated(), Coefficient(1));
782 true, Coefficient(1),
783 true, Coefficient(1), &cst);
788 min_weight, to_merge, sat_solver_, &repository_));
789 absl::StrAppend(info,
" d:", nodes_.back()->depth());
std::vector< absl::Span< const Literal > > HeuristicAmoPartition(std::vector< Literal > *literals)
void InitializeLazyCoreNode(Coefficient weight, EncodingNode *a, EncodingNode *b)
Literal GreaterThan(int i) const
bool AssumptionIs(Literal other) const
GetAssumption() might need to create new literals.
void InitializeFullNode(int n, EncodingNode *a, EncodingNode *b, SatSolver *solver)
Coefficient Reduce(const SatSolver &solver)
void InitializeLazyNode(EncodingNode *a, EncodingNode *b, SatSolver *solver)
int size() const
Accessors to size() and literals in [lb, current_ub).
std::string DebugString(const VariablesAssignment &assignment) const
We use the solver to display the current values of the literals.
static EncodingNode GenericNode(int lb, int ub, std::function< Literal(int x)> create_lit, Coefficient weight)
static EncodingNode ConstantNode(Coefficient weight)
void set_weight(Coefficient w)
void ApplyWeightUpperBound(Coefficient gap, SatSolver *solver)
bool IncreaseCurrentUB(SatSolver *solver)
static EncodingNode LiteralNode(Literal l, Coefficient weight)
Coefficient weight() const
void TransformToBoolean(SatSolver *solver)
Indicate that the node cannot grow further than its current assumption.
EncodingNode * child_b() const
Literal literal(int i) const
EncodingNode * child_a() const
void InitializeAmoNode(absl::Span< EncodingNode *const > nodes, SatSolver *solver)
Literal GetAssumption(SatSolver *solver)
LiteralIndex Index() const
BooleanVariable Variable() const
bool ProcessCore(absl::Span< const Literal > core, Coefficient min_weight, Coefficient gap, std::string *info)
bool AddLinearConstraint(bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, std::vector< LiteralWithCoeff > *cst)
bool AddProblemClause(absl::Span< const Literal > literals, bool is_safe=true)
BooleanVariable NewBooleanVariable()
bool AddBinaryClause(Literal a, Literal b)
Same as AddProblemClause() below, but for small clauses.
bool ModelIsUnsat() const
void Backtrack(int target_level)
const SatParameters & parameters() const
bool EnqueueDecisionIfNotConflicting(Literal true_literal)
ABSL_MUST_USE_RESULT bool AddUnitClause(Literal true_literal)
bool AddTernaryClause(Literal a, Literal b, Literal c)
const VariablesAssignment & Assignment() const
ABSL_MUST_USE_RESULT bool ResetToLevelZero()
void SetNumVariables(int num_variables)
bool LiteralIsFalse(Literal literal) const
bool LiteralIsTrue(Literal literal) const
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
EncodingNode * MergeAllNodesWithDeque(Coefficient upper_bound, const std::vector< EncodingNode * > &nodes, SatSolver *solver, std::deque< EncodingNode > *repository)
std::vector< Literal > ExtractAssumptions(Coefficient stratified_lower_bound, const std::vector< EncodingNode * > &nodes, SatSolver *solver)
void ReduceNodes(Coefficient upper_bound, Coefficient *lower_bound, std::vector< EncodingNode * > *nodes, SatSolver *solver)
EncodingNode LazyMerge(EncodingNode *a, EncodingNode *b, SatSolver *solver)
EncodingNode FullMerge(Coefficient upper_bound, EncodingNode *a, EncodingNode *b, SatSolver *solver)
EncodingNode * LazyMergeAllNodeWithPQAndIncreaseLb(Coefficient weight, const std::vector< EncodingNode * > &nodes, SatSolver *solver, std::deque< EncodingNode > *repository)
Coefficient ComputeCoreMinWeight(const std::vector< EncodingNode * > &nodes, const std::vector< Literal > &core)
void IncreaseNodeSize(EncodingNode *node, SatSolver *solver)
std::vector< absl::Span< int > > AtMostOneDecomposition(const std::vector< std::vector< int > > &graph, absl::BitGenRef random, std::vector< int > *buffer)
Coefficient MaxNodeWeightSmallerThan(const std::vector< EncodingNode * > &nodes, Coefficient upper_bound)
const Coefficient kCoefficientMax(std::numeric_limits< Coefficient::ValueType >::max())
In SWIG mode, we don't want anything besides these top-level includes.