20#include "absl/container/btree_set.h"
21#include "absl/container/inlined_vector.h"
22#include "absl/types/span.h"
43void AppendLowerBoundReasonIfValid(IntegerVariable
var,
44 const IntegerTrail& i_trail,
45 std::vector<IntegerLiteral>* reason) {
47 reason->push_back(i_trail.LowerBoundAsLiteral(
var));
56 while (propagation_trail_index_ < trail_->
Index()) {
58 if (
literal.Index() >= literal_to_new_impacted_arcs_.
size())
continue;
63 literal_to_new_impacted_arcs_[
literal.Index()]) {
64 if (--arc_counts_[arc_index] == 0) {
65 const ArcInfo&
arc = arcs_[arc_index];
73 literal_to_new_impacted_arcs_[
literal.Index()]) {
74 if (arc_counts_[arc_index] > 0)
continue;
75 const ArcInfo&
arc = arcs_[arc_index];
77 const IntegerValue new_head_lb =
80 if (!EnqueueAndCheck(
arc, new_head_lb, trail_))
return false;
86 InitializeBFQueueWithModifiedNodes();
87 if (!BellmanFordTarjan(trail_))
return false;
97 DCHECK(NoPropagationLeft(*trail_));
101 PropagateOptionalArcs(trail_);
110 if (
var >= impacted_arcs_.
size())
return true;
111 for (
const ArcIndex arc_index : impacted_arcs_[
var]) {
112 const ArcInfo&
arc = arcs_[arc_index];
114 const IntegerValue new_head_lb =
116 if (new_head_lb > integer_trail_->
LowerBound(
arc.head_var)) {
117 if (!EnqueueAndCheck(
arc, new_head_lb, trail_))
return false;
132 if (
literal.Index() >= literal_to_new_impacted_arcs_.
size())
continue;
134 literal_to_new_impacted_arcs_[
literal.Index()]) {
135 if (arc_counts_[arc_index]++ == 0) {
136 const ArcInfo&
arc = arcs_[arc_index];
148 const std::vector<IntegerVariable>& vars,
149 std::vector<IntegerPrecedences>* output) {
150 tmp_sorted_vars_.clear();
151 tmp_precedences_.clear();
153 const IntegerVariable
var = vars[
index];
155 if (
var >= impacted_arcs_.
size())
continue;
156 for (
const ArcIndex arc_index : impacted_arcs_[
var]) {
157 const ArcInfo&
arc = arcs_[arc_index];
160 IntegerValue offset =
arc.offset;
169 if (offset < 0)
continue;
171 if (var_to_degree_[
arc.head_var] == 0) {
172 tmp_sorted_vars_.push_back(
179 if (var_to_last_index_[
arc.head_var] ==
index)
continue;
181 var_to_last_index_[
arc.head_var] =
index;
182 var_to_degree_[
arc.head_var]++;
183 tmp_precedences_.push_back(
184 {
index,
arc.head_var, arc_index.value(), offset});
195 std::sort(tmp_sorted_vars_.begin(), tmp_sorted_vars_.end());
201 for (
const SortedVar pair : tmp_sorted_vars_) {
202 const int degree = var_to_degree_[pair.var];
204 var_to_degree_[pair.var] =
start;
208 var_to_degree_[pair.var] = -1;
213 if (var_to_degree_[precedence.var] < 0)
continue;
214 (*output)[var_to_degree_[precedence.var]++] = precedence;
219 for (
const SortedVar pair : tmp_sorted_vars_) {
220 var_to_degree_[pair.var] = 0;
225 int arc_index, IntegerValue min_offset,
226 std::vector<Literal>* literal_reason,
227 std::vector<IntegerLiteral>* integer_reason)
const {
229 for (
const Literal l :
arc.presence_literals) {
235 arc.offset_var, min_offset -
arc.offset));
239void PrecedencesPropagator::AdjustSizeFor(IntegerVariable i) {
254void PrecedencesPropagator::AddArc(
255 IntegerVariable
tail, IntegerVariable
head, IntegerValue offset,
256 IntegerVariable offset_var, absl::Span<const Literal> presence_literals) {
263 absl::InlinedVector<Literal, 6> enforcement_literals;
265 for (
const Literal l : presence_literals) {
266 enforcement_literals.push_back(l);
269 enforcement_literals.push_back(
273 enforcement_literals.push_back(
278 enforcement_literals.push_back(
283 for (
const Literal l : enforcement_literals) {
289 enforcement_literals[new_size++] = l;
291 enforcement_literals.resize(new_size);
298 VLOG(1) <<
"Self arc! This could be presolved. "
299 <<
"var:" <<
tail <<
" offset:" << offset
300 <<
" offset_var:" << offset_var
301 <<
" conditioned_by:" << presence_literals;
307 const IntegerValue lb = integer_trail_->
LowerBound(offset_var);
308 if (lb == integer_trail_->
UpperBound(offset_var)) {
315 if (!enforcement_literals.empty()) {
316 const OptionalArcIndex arc_index(potential_arcs_.
size());
318 {
tail,
head, offset, offset_var, enforcement_literals});
322 impacted_potential_arcs_[offset_var].
push_back(arc_index);
328 IntegerVariable tail_var;
329 IntegerVariable head_var;
330 IntegerVariable offset_var;
332 std::vector<InternalArc> to_add;
340 to_add.push_back({
tail,
head, offset_var});
341 to_add.push_back({offset_var,
head,
tail});
349 for (
const InternalArc
a : to_add) {
364 modified_vars_.
Set(
a.tail_var);
370 {
a.tail_var,
a.head_var, offset,
a.offset_var, enforcement_literals});
371 auto& presence_literals = arcs_.
back().presence_literals;
375 const Literal to_remove =
377 const auto it = std::find(presence_literals.begin(),
378 presence_literals.end(), to_remove);
379 if (it != presence_literals.end()) presence_literals.erase(it);
382 if (presence_literals.empty()) {
383 impacted_arcs_[
a.tail_var].
push_back(arc_index);
385 for (
const Literal l : presence_literals) {
386 if (l.Index() >= literal_to_new_impacted_arcs_.
size()) {
387 literal_to_new_impacted_arcs_.
resize(l.Index().value() + 1);
389 literal_to_new_impacted_arcs_[l.Index()].
push_back(arc_index);
392 arc_counts_.
push_back(presence_literals.size());
401void PrecedencesPropagator::PropagateOptionalArcs(Trail* trail) {
404 if (
var >= impacted_potential_arcs_.
size())
continue;
408 for (
const OptionalArcIndex arc_index : impacted_potential_arcs_[
var]) {
409 const ArcInfo&
arc = potential_arcs_[arc_index];
410 int num_not_true = 0;
411 Literal to_propagate;
412 for (
const Literal l :
arc.presence_literals) {
413 if (!trail->Assignment().LiteralIsTrue(l)) {
418 if (num_not_true != 1)
continue;
419 if (trail->Assignment().LiteralIsFalse(to_propagate))
continue;
423 const IntegerValue tail_lb = integer_trail_->
LowerBound(
arc.tail_var);
424 const IntegerValue head_ub = integer_trail_->
UpperBound(
arc.head_var);
425 if (tail_lb + ArcOffset(
arc) > head_ub) {
426 integer_reason_.clear();
427 integer_reason_.push_back(
429 integer_reason_.push_back(
431 AppendLowerBoundReasonIfValid(
arc.offset_var, *integer_trail_,
433 literal_reason_.clear();
434 for (
const Literal l :
arc.presence_literals) {
435 if (l != to_propagate) literal_reason_.push_back(l.Negated());
437 integer_trail_->
EnqueueLiteral(to_propagate.Negated(), literal_reason_,
444IntegerValue PrecedencesPropagator::ArcOffset(
const ArcInfo&
arc)
const {
450bool PrecedencesPropagator::EnqueueAndCheck(
const ArcInfo&
arc,
451 IntegerValue new_head_lb,
460 literal_reason_.clear();
461 for (
const Literal l :
arc.presence_literals) {
462 literal_reason_.push_back(l.Negated());
465 integer_reason_.clear();
467 AppendLowerBoundReasonIfValid(
arc.offset_var, *integer_trail_,
478 if (new_head_lb > integer_trail_->
UpperBound(
arc.head_var)) {
479 const IntegerValue slack =
481 integer_reason_.push_back(
483 std::vector<IntegerValue> coeffs(integer_reason_.size(), IntegerValue(1));
487 return integer_trail_->
ReportConflict(literal_reason_, integer_reason_);
491 if (trail->Assignment().LiteralIsFalse(l)) {
492 literal_reason_.push_back(l);
493 return integer_trail_->
ReportConflict(literal_reason_, integer_reason_);
495 integer_trail_->
EnqueueLiteral(l, literal_reason_, integer_reason_);
501 return integer_trail_->
Enqueue(
503 literal_reason_, integer_reason_);
506bool PrecedencesPropagator::NoPropagationLeft(
const Trail& trail)
const {
507 const int num_nodes = impacted_arcs_.
size();
508 for (IntegerVariable
var(0);
var < num_nodes; ++
var) {
509 for (
const ArcIndex arc_index : impacted_arcs_[
var]) {
510 const ArcInfo&
arc = arcs_[arc_index];
521void PrecedencesPropagator::InitializeBFQueueWithModifiedNodes() {
524 const int num_nodes = impacted_arcs_.
size();
525 bf_in_queue_.resize(num_nodes,
false);
526 for (
const int node : bf_queue_) bf_in_queue_[node] =
false;
528 DCHECK(std::none_of(bf_in_queue_.begin(), bf_in_queue_.end(),
529 [](
bool v) { return v; }));
531 if (
var >= num_nodes)
continue;
532 bf_queue_.push_back(
var.value());
533 bf_in_queue_[
var.value()] =
true;
537void PrecedencesPropagator::CleanUpMarkedArcsAndParents() {
540 const int num_nodes = impacted_arcs_.
size();
542 if (
var >= num_nodes)
continue;
543 const ArcIndex parent_arc_index = bf_parent_arc_of_[
var.value()];
544 if (parent_arc_index != -1) {
545 arcs_[parent_arc_index].is_marked =
false;
546 bf_parent_arc_of_[
var.value()] = -1;
547 bf_can_be_skipped_[
var.value()] =
false;
550 DCHECK(std::none_of(bf_parent_arc_of_.begin(), bf_parent_arc_of_.end(),
551 [](
ArcIndex v) { return v != -1; }));
552 DCHECK(std::none_of(bf_can_be_skipped_.begin(), bf_can_be_skipped_.end(),
553 [](
bool v) { return v; }));
556bool PrecedencesPropagator::DisassembleSubtree(
557 int source,
int target, std::vector<bool>* can_be_skipped) {
561 tmp_vector_.push_back(source);
562 while (!tmp_vector_.empty()) {
563 const int tail = tmp_vector_.back();
564 tmp_vector_.pop_back();
565 for (
const ArcIndex arc_index : impacted_arcs_[IntegerVariable(
tail)]) {
566 const ArcInfo&
arc = arcs_[arc_index];
568 arc.is_marked =
false;
569 if (
arc.head_var.value() == target)
return true;
570 DCHECK(!(*can_be_skipped)[
arc.head_var.value()]);
571 (*can_be_skipped)[
arc.head_var.value()] =
true;
572 tmp_vector_.push_back(
arc.head_var.value());
579void PrecedencesPropagator::AnalyzePositiveCycle(
580 ArcIndex first_arc, Trail* trail, std::vector<Literal>* must_be_all_true,
581 std::vector<Literal>* literal_reason,
582 std::vector<IntegerLiteral>* integer_reason) {
583 must_be_all_true->clear();
584 literal_reason->clear();
585 integer_reason->clear();
588 const IntegerVariable first_arc_head = arcs_[first_arc].head_var;
590 std::vector<ArcIndex> arc_on_cycle;
596 const int num_nodes = impacted_arcs_.
size();
597 while (arc_on_cycle.size() <= num_nodes) {
598 arc_on_cycle.push_back(arc_index);
599 const ArcInfo&
arc = arcs_[arc_index];
600 if (
arc.tail_var == first_arc_head)
break;
601 arc_index = bf_parent_arc_of_[
arc.tail_var.value()];
604 CHECK_NE(arc_on_cycle.size(), num_nodes + 1) <<
"Infinite loop.";
608 for (
const ArcIndex arc_index : arc_on_cycle) {
609 const ArcInfo&
arc = arcs_[arc_index];
610 sum += ArcOffset(
arc);
611 AppendLowerBoundReasonIfValid(
arc.offset_var, *integer_trail_,
613 for (
const Literal l :
arc.presence_literals) {
614 literal_reason->push_back(l.Negated());
623 must_be_all_true->push_back(
638bool PrecedencesPropagator::BellmanFordTarjan(Trail* trail) {
639 const int num_nodes = impacted_arcs_.
size();
642 bf_can_be_skipped_.resize(num_nodes,
false);
643 bf_parent_arc_of_.resize(num_nodes,
ArcIndex(-1));
648 while (!bf_queue_.empty()) {
649 const int node = bf_queue_.front();
650 bf_queue_.pop_front();
651 bf_in_queue_[node] =
false;
661 if (bf_can_be_skipped_[node]) {
663 DCHECK(!arcs_[bf_parent_arc_of_[node]].is_marked);
667 const IntegerValue tail_lb =
668 integer_trail_->
LowerBound(IntegerVariable(node));
669 for (
const ArcIndex arc_index : impacted_arcs_[IntegerVariable(node)]) {
670 const ArcInfo&
arc = arcs_[arc_index];
672 const IntegerValue candidate = tail_lb + ArcOffset(
arc);
675 if (!EnqueueAndCheck(
arc, candidate, trail))
return false;
683 if (DisassembleSubtree(
arc.head_var.value(),
arc.tail_var.value(),
684 &bf_can_be_skipped_)) {
685 std::vector<Literal> must_be_all_true;
686 AnalyzePositiveCycle(arc_index, trail, &must_be_all_true,
687 &literal_reason_, &integer_reason_);
688 if (must_be_all_true.empty()) {
693 for (
const Literal l : must_be_all_true) {
695 literal_reason_.push_back(l);
700 for (
const Literal l : must_be_all_true) {
715 if (bf_parent_arc_of_[
arc.head_var.value()] != -1) {
716 arcs_[bf_parent_arc_of_[
arc.head_var.value()]].is_marked =
false;
725 const IntegerValue new_bound = integer_trail_->
LowerBound(
arc.head_var);
726 if (new_bound == candidate) {
727 bf_parent_arc_of_[
arc.head_var.value()] = arc_index;
728 arcs_[arc_index].is_marked =
true;
732 bf_parent_arc_of_[
arc.head_var.value()] = -1;
737 bf_can_be_skipped_[
arc.head_var.value()] =
false;
738 if (!bf_in_queue_[
arc.head_var.value()] && new_bound >= candidate) {
739 bf_queue_.push_back(
arc.head_var.value());
740 bf_in_queue_[
arc.head_var.value()] =
true;
748int PrecedencesPropagator::AddGreaterThanAtLeastOneOfConstraintsFromClause(
749 const absl::Span<const Literal> clause, Model*
model) {
750 CHECK_EQ(
model->GetOrCreate<Trail>()->CurrentDecisionLevel(), 0);
751 if (clause.size() < 2)
return 0;
754 std::vector<ArcInfo> infos;
755 for (
const Literal l : clause) {
756 if (l.Index() >= literal_to_new_impacted_arcs_.
size())
continue;
757 for (
const ArcIndex arc_index : literal_to_new_impacted_arcs_[l.Index()]) {
758 const ArcInfo&
arc = arcs_[arc_index];
759 if (
arc.presence_literals.size() != 1)
continue;
766 if (infos.size() <= 1)
return 0;
770 std::stable_sort(infos.begin(), infos.end(),
771 [](
const ArcInfo&
a,
const ArcInfo&
b) {
772 return a.head_var < b.head_var;
776 int num_added_constraints = 0;
777 auto* solver =
model->GetOrCreate<SatSolver>();
778 for (
int i = 0; i < infos.size();) {
780 const IntegerVariable head_var = infos[
start].head_var;
781 for (i++; i < infos.size() && infos[i].head_var == head_var; ++i) {
783 const absl::Span<ArcInfo> arcs(&infos[
start], i -
start);
786 if (arcs.size() < 2)
continue;
791 if (arcs.size() + 1 < clause.size())
continue;
793 std::vector<IntegerVariable> vars;
794 std::vector<IntegerValue> offsets;
795 std::vector<Literal> selectors;
796 std::vector<Literal> enforcements;
799 for (
const Literal l : clause) {
801 for (; j < arcs.size() && l == arcs[j].presence_literals.front(); ++j) {
803 vars.push_back(arcs[j].tail_var);
804 offsets.push_back(arcs[j].offset);
811 selectors.push_back(l);
814 enforcements.push_back(l.Negated());
820 if (enforcements.size() + 1 == clause.size())
continue;
822 ++num_added_constraints;
825 if (!solver->FinishPropagation())
return num_added_constraints;
827 return num_added_constraints;
830int PrecedencesPropagator::
831 AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(Model*
model) {
833 auto* solver =
model->GetOrCreate<SatSolver>();
837 for (
ArcIndex arc_index(0); arc_index < arcs_.
size(); ++arc_index) {
838 const ArcInfo&
arc = arcs_[arc_index];
842 if (
arc.tail_var ==
arc.head_var)
continue;
843 if (
arc.presence_literals.size() != 1)
continue;
845 if (
arc.head_var >= incoming_arcs_.size()) {
846 incoming_arcs_.
resize(
arc.head_var.value() + 1);
848 incoming_arcs_[
arc.head_var].push_back(arc_index);
851 int num_added_constraints = 0;
852 for (IntegerVariable target(0); target < incoming_arcs_.size(); ++target) {
853 if (incoming_arcs_[target].size() <= 1)
continue;
854 if (
time_limit->LimitReached())
return num_added_constraints;
859 solver->Backtrack(0);
860 if (solver->IsModelUnsat())
return num_added_constraints;
861 std::vector<Literal> clause;
862 for (
const ArcIndex arc_index : incoming_arcs_[target]) {
863 const Literal
literal = arcs_[arc_index].presence_literals.
front();
864 if (solver->Assignment().LiteralIsFalse(
literal))
continue;
866 const int old_level = solver->CurrentDecisionLevel();
867 solver->EnqueueDecisionAndBacktrackOnConflict(
literal.Negated());
868 if (solver->IsModelUnsat())
return num_added_constraints;
869 const int new_level = solver->CurrentDecisionLevel();
870 if (new_level <= old_level) {
871 clause = solver->GetLastIncompatibleDecisions();
875 solver->Backtrack(0);
877 if (clause.size() > 1) {
879 const absl::btree_set<Literal> clause_set(clause.begin(), clause.end());
880 std::vector<ArcIndex> arcs_in_clause;
881 for (
const ArcIndex arc_index : incoming_arcs_[target]) {
882 const Literal
literal(arcs_[arc_index].presence_literals.front());
884 arcs_in_clause.push_back(arc_index);
888 VLOG(2) << arcs_in_clause.size() <<
"/" << incoming_arcs_[target].size();
890 ++num_added_constraints;
891 std::vector<IntegerVariable> vars;
892 std::vector<IntegerValue> offsets;
893 std::vector<Literal> selectors;
894 for (
const ArcIndex a : arcs_in_clause) {
895 vars.push_back(arcs_[
a].tail_var);
896 offsets.push_back(arcs_[
a].offset);
897 selectors.push_back(Literal(arcs_[
a].presence_literals.front()));
900 if (!solver->FinishPropagation())
return num_added_constraints;
904 return num_added_constraints;
908 VLOG(1) <<
"Detecting GreaterThanAtLeastOneOf() constraints...";
912 int num_added_constraints = 0;
920 if (
clauses->AllClausesInCreationOrder().size() < 1e6) {
929 if (
time_limit->LimitReached())
return num_added_constraints;
930 if (solver->IsModelUnsat())
return num_added_constraints;
931 num_added_constraints += AddGreaterThanAtLeastOneOfConstraintsFromClause(
932 clause->AsSpan(),
model);
939 const int num_booleans = solver->NumVariables();
940 if (num_booleans < 1e6) {
941 for (
int i = 0; i < num_booleans; ++i) {
942 if (
time_limit->LimitReached())
return num_added_constraints;
943 if (solver->IsModelUnsat())
return num_added_constraints;
944 num_added_constraints +=
945 AddGreaterThanAtLeastOneOfConstraintsFromClause(
946 {
Literal(BooleanVariable(i),
true),
947 Literal(BooleanVariable(i),
false)},
953 num_added_constraints +=
954 AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(
model);
957 VLOG(1) <<
"Added " << num_added_constraints
958 <<
" GreaterThanAtLeastOneOf() constraints.";
959 return num_added_constraints;
#define DCHECK_NE(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GT(val1, val2)
#define CHECK_NE(val1, val2)
#define DCHECK_GT(val1, val2)
#define DCHECK(condition)
#define DCHECK_EQ(val1, val2)
#define VLOG(verboselevel)
void resize(size_type new_size)
void push_back(const value_type &x)
void Set(IntegerType index)
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
void ClearAndResize(IntegerType size)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
void WatchLowerBound(IntegerVariable var, int id, int watch_index=-1)
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
bool IsCurrentlyIgnored(IntegerVariable i) const
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
void EnqueueLiteral(Literal literal, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
IntegerValue UpperBound(IntegerVariable i) const
void RelaxLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, std::vector< IntegerLiteral > *reason) const
IntegerValue LowerBound(IntegerVariable i) const
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
Literal IsIgnoredLiteral(IntegerVariable i) const
bool IsOptional(IntegerVariable i) const
IntegerVariable NumIntegerVariables() const
Class that owns everything related to a particular optimization model.
void AddPrecedenceReason(int arc_index, IntegerValue min_offset, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason) const
void ComputePrecedences(const std::vector< IntegerVariable > &vars, std::vector< IntegerPrecedences > *output)
int AddGreaterThanAtLeastOneOfConstraints(Model *model)
void Untrail(const Trail &trail, int trail_index) final
bool PropagateOutgoingArcs(IntegerVariable var)
int propagation_trail_index_
const VariablesAssignment & Assignment() const
int CurrentDecisionLevel() const
bool LiteralIsTrue(Literal literal) const
bool LiteralIsFalse(Literal literal) const
SharedClausesManager * clauses
ModelSharedTimeLimit * time_limit
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
bool ContainsKey(const Collection &collection, const Key &key)
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
const IntegerVariable kNoIntegerVariable(-1)
std::function< void(Model *)> GreaterThanAtLeastOneOf(IntegerVariable target_var, const absl::Span< const IntegerVariable > vars, const absl::Span< const IntegerValue > offsets, const absl::Span< const Literal > selectors)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Collection of objects used to extend the Constraint Solver library.
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)