30 void AppendLowerBoundReasonIfValid(IntegerVariable
var,
31 const IntegerTrail& i_trail,
32 std::vector<IntegerLiteral>* reason) {
34 reason->push_back(i_trail.LowerBoundAsLiteral(
var));
43 while (propagation_trail_index_ < trail_->
Index()) {
45 if (
literal.Index() >= literal_to_new_impacted_arcs_.
size())
continue;
50 literal_to_new_impacted_arcs_[
literal.Index()]) {
51 if (--arc_counts_[arc_index] == 0) {
52 const ArcInfo& arc = arcs_[arc_index];
53 impacted_arcs_[arc.tail_var].
push_back(arc_index);
60 literal_to_new_impacted_arcs_[
literal.Index()]) {
61 if (arc_counts_[arc_index] > 0)
continue;
62 const ArcInfo& arc = arcs_[arc_index];
64 const IntegerValue new_head_lb =
65 integer_trail_->
LowerBound(arc.tail_var) + ArcOffset(arc);
66 if (new_head_lb > integer_trail_->
LowerBound(arc.head_var)) {
67 if (!EnqueueAndCheck(arc, new_head_lb, trail_))
return false;
73 InitializeBFQueueWithModifiedNodes();
74 if (!BellmanFordTarjan(trail_))
return false;
84 DCHECK(NoPropagationLeft(*trail_));
88 PropagateOptionalArcs(trail_);
96 for (
const ArcIndex arc_index : impacted_arcs_[
var]) {
97 const ArcInfo& arc = arcs_[arc_index];
99 const IntegerValue new_head_lb =
100 integer_trail_->
LowerBound(arc.tail_var) + ArcOffset(arc);
101 if (new_head_lb > integer_trail_->
LowerBound(arc.head_var)) {
102 if (!EnqueueAndCheck(arc, new_head_lb, trail_))
return false;
117 if (
literal.Index() >= literal_to_new_impacted_arcs_.
size())
continue;
119 literal_to_new_impacted_arcs_[
literal.Index()]) {
120 if (arc_counts_[arc_index]++ == 0) {
121 const ArcInfo& arc = arcs_[arc_index];
122 impacted_arcs_[arc.tail_var].
pop_back();
133 const std::vector<IntegerVariable>& vars,
134 std::vector<IntegerPrecedences>* output) {
135 tmp_sorted_vars_.clear();
136 tmp_precedences_.clear();
138 const IntegerVariable
var = vars[
index];
140 if (
var >= impacted_arcs_.
size())
continue;
141 for (
const ArcIndex arc_index : impacted_arcs_[
var]) {
142 const ArcInfo& arc = arcs_[arc_index];
145 IntegerValue offset = arc.offset;
147 offset += integer_trail_->
LowerBound(arc.offset_var);
154 if (offset < 0)
continue;
156 if (var_to_degree_[arc.head_var] == 0) {
157 tmp_sorted_vars_.push_back(
158 {arc.head_var, integer_trail_->
LowerBound(arc.head_var)});
164 if (var_to_last_index_[arc.head_var] ==
index)
continue;
166 var_to_last_index_[arc.head_var] =
index;
167 var_to_degree_[arc.head_var]++;
168 tmp_precedences_.push_back(
169 {
index, arc.head_var, arc_index.value(), offset});
180 std::sort(tmp_sorted_vars_.begin(), tmp_sorted_vars_.end());
186 for (
const SortedVar pair : tmp_sorted_vars_) {
187 const int degree = var_to_degree_[pair.var];
189 var_to_degree_[pair.var] = start;
193 var_to_degree_[pair.var] = -1;
196 output->resize(start);
198 if (var_to_degree_[precedence.var] < 0)
continue;
199 (*output)[var_to_degree_[precedence.var]++] = precedence;
204 for (
const SortedVar pair : tmp_sorted_vars_) {
205 var_to_degree_[pair.var] = 0;
210 int arc_index, IntegerValue min_offset,
211 std::vector<Literal>* literal_reason,
212 std::vector<IntegerLiteral>* integer_reason)
const {
213 const ArcInfo& arc = arcs_[
ArcIndex(arc_index)];
214 for (
const Literal l : arc.presence_literals) {
215 literal_reason->push_back(l.
Negated());
220 arc.offset_var, min_offset - arc.offset));
224 void PrecedencesPropagator::AdjustSizeFor(IntegerVariable i) {
239 void PrecedencesPropagator::AddArc(
240 IntegerVariable
tail, IntegerVariable
head, IntegerValue offset,
241 IntegerVariable offset_var, absl::Span<const Literal> presence_literals) {
248 absl::InlinedVector<Literal, 6> enforcement_literals;
250 for (
const Literal l : presence_literals) {
251 enforcement_literals.push_back(l);
254 enforcement_literals.push_back(
258 enforcement_literals.push_back(
263 enforcement_literals.push_back(
268 for (
const Literal l : enforcement_literals) {
274 enforcement_literals[new_size++] = l;
276 enforcement_literals.resize(new_size);
283 VLOG(1) <<
"Self arc! This could be presolved. "
284 <<
"var:" <<
tail <<
" offset:" << offset
285 <<
" offset_var:" << offset_var
286 <<
" conditioned_by:" << presence_literals;
292 const IntegerValue lb = integer_trail_->
LowerBound(offset_var);
293 if (lb == integer_trail_->
UpperBound(offset_var)) {
300 if (!enforcement_literals.empty()) {
301 const OptionalArcIndex arc_index(potential_arcs_.
size());
303 {
tail,
head, offset, offset_var, enforcement_literals});
307 impacted_potential_arcs_[offset_var].
push_back(arc_index);
313 IntegerVariable tail_var;
314 IntegerVariable head_var;
315 IntegerVariable offset_var;
317 std::vector<InternalArc> to_add;
325 to_add.push_back({
tail,
head, offset_var});
326 to_add.push_back({offset_var,
head,
tail});
334 for (
const InternalArc
a : to_add) {
349 modified_vars_.
Set(
a.tail_var);
355 {
a.tail_var,
a.head_var, offset,
a.offset_var, enforcement_literals});
356 auto& presence_literals = arcs_.
back().presence_literals;
360 const Literal to_remove =
362 const auto it = std::find(presence_literals.begin(),
363 presence_literals.end(), to_remove);
364 if (it != presence_literals.end()) presence_literals.erase(it);
367 if (presence_literals.empty()) {
368 impacted_arcs_[
a.tail_var].
push_back(arc_index);
370 for (
const Literal l : presence_literals) {
371 if (l.Index() >= literal_to_new_impacted_arcs_.
size()) {
372 literal_to_new_impacted_arcs_.
resize(l.Index().value() + 1);
374 literal_to_new_impacted_arcs_[l.Index()].
push_back(arc_index);
377 arc_counts_.
push_back(presence_literals.size());
386 void PrecedencesPropagator::PropagateOptionalArcs(Trail* trail) {
388 if (
var >= impacted_potential_arcs_.
size())
break;
392 for (
const OptionalArcIndex arc_index : impacted_potential_arcs_[
var]) {
393 const ArcInfo& arc = potential_arcs_[arc_index];
394 int num_not_true = 0;
395 Literal to_propagate;
396 for (
const Literal l : arc.presence_literals) {
397 if (!trail->Assignment().LiteralIsTrue(l)) {
402 if (num_not_true != 1)
continue;
403 if (trail->Assignment().LiteralIsFalse(to_propagate))
continue;
407 const IntegerValue tail_lb = integer_trail_->
LowerBound(arc.tail_var);
408 const IntegerValue head_ub = integer_trail_->
UpperBound(arc.head_var);
409 if (tail_lb + ArcOffset(arc) > head_ub) {
410 integer_reason_.clear();
411 integer_reason_.push_back(
413 integer_reason_.push_back(
415 AppendLowerBoundReasonIfValid(arc.offset_var, *integer_trail_,
417 literal_reason_.clear();
418 for (
const Literal l : arc.presence_literals) {
419 if (l != to_propagate) literal_reason_.push_back(l.Negated());
421 integer_trail_->
EnqueueLiteral(to_propagate.Negated(), literal_reason_,
428 IntegerValue PrecedencesPropagator::ArcOffset(
const ArcInfo& arc)
const {
431 : integer_trail_->
LowerBound(arc.offset_var));
434 bool PrecedencesPropagator::EnqueueAndCheck(
const ArcInfo& arc,
435 IntegerValue new_head_lb,
437 DCHECK_GT(new_head_lb, integer_trail_->
LowerBound(arc.head_var));
444 literal_reason_.clear();
445 for (
const Literal l : arc.presence_literals) {
446 literal_reason_.push_back(l.Negated());
449 integer_reason_.clear();
451 AppendLowerBoundReasonIfValid(arc.offset_var, *integer_trail_,
462 if (new_head_lb > integer_trail_->
UpperBound(arc.head_var)) {
463 const IntegerValue slack =
464 new_head_lb - integer_trail_->
UpperBound(arc.head_var) - 1;
465 integer_reason_.push_back(
467 std::vector<IntegerValue> coeffs(integer_reason_.size(), IntegerValue(1));
470 if (!integer_trail_->
IsOptional(arc.head_var)) {
471 return integer_trail_->
ReportConflict(literal_reason_, integer_reason_);
475 if (trail->Assignment().LiteralIsFalse(l)) {
476 literal_reason_.push_back(l);
477 return integer_trail_->
ReportConflict(literal_reason_, integer_reason_);
479 integer_trail_->
EnqueueLiteral(l, literal_reason_, integer_reason_);
485 return integer_trail_->
Enqueue(
487 literal_reason_, integer_reason_);
490 bool PrecedencesPropagator::NoPropagationLeft(
const Trail& trail)
const {
491 const int num_nodes = impacted_arcs_.
size();
492 for (IntegerVariable
var(0);
var < num_nodes; ++
var) {
493 for (
const ArcIndex arc_index : impacted_arcs_[
var]) {
494 const ArcInfo& arc = arcs_[arc_index];
496 if (integer_trail_->
LowerBound(arc.tail_var) + ArcOffset(arc) >
505 void PrecedencesPropagator::InitializeBFQueueWithModifiedNodes() {
508 const int num_nodes = impacted_arcs_.
size();
509 bf_in_queue_.resize(num_nodes,
false);
510 for (
const int node : bf_queue_) bf_in_queue_[node] =
false;
512 DCHECK(std::none_of(bf_in_queue_.begin(), bf_in_queue_.end(),
513 [](
bool v) { return v; }));
515 if (
var >= num_nodes)
continue;
516 bf_queue_.push_back(
var.value());
517 bf_in_queue_[
var.value()] =
true;
521 void PrecedencesPropagator::CleanUpMarkedArcsAndParents() {
524 const int num_nodes = impacted_arcs_.
size();
526 if (
var >= num_nodes)
continue;
527 const ArcIndex parent_arc_index = bf_parent_arc_of_[
var.value()];
528 if (parent_arc_index != -1) {
529 arcs_[parent_arc_index].is_marked =
false;
530 bf_parent_arc_of_[
var.value()] = -1;
531 bf_can_be_skipped_[
var.value()] =
false;
534 DCHECK(std::none_of(bf_parent_arc_of_.begin(), bf_parent_arc_of_.end(),
535 [](
ArcIndex v) { return v != -1; }));
536 DCHECK(std::none_of(bf_can_be_skipped_.begin(), bf_can_be_skipped_.end(),
537 [](
bool v) { return v; }));
540 bool PrecedencesPropagator::DisassembleSubtree(
541 int source,
int target, std::vector<bool>* can_be_skipped) {
545 tmp_vector_.push_back(source);
546 while (!tmp_vector_.empty()) {
547 const int tail = tmp_vector_.back();
548 tmp_vector_.pop_back();
549 for (
const ArcIndex arc_index : impacted_arcs_[IntegerVariable(
tail)]) {
550 const ArcInfo& arc = arcs_[arc_index];
552 arc.is_marked =
false;
553 if (arc.head_var.value() == target)
return true;
554 DCHECK(!(*can_be_skipped)[arc.head_var.value()]);
555 (*can_be_skipped)[arc.head_var.value()] =
true;
556 tmp_vector_.push_back(arc.head_var.value());
563 void PrecedencesPropagator::AnalyzePositiveCycle(
564 ArcIndex first_arc, Trail* trail, std::vector<Literal>* must_be_all_true,
565 std::vector<Literal>* literal_reason,
566 std::vector<IntegerLiteral>* integer_reason) {
567 must_be_all_true->clear();
568 literal_reason->clear();
569 integer_reason->clear();
572 const IntegerVariable first_arc_head = arcs_[first_arc].head_var;
574 std::vector<ArcIndex> arc_on_cycle;
580 const int num_nodes = impacted_arcs_.
size();
581 while (arc_on_cycle.size() <= num_nodes) {
582 arc_on_cycle.push_back(arc_index);
583 const ArcInfo& arc = arcs_[arc_index];
584 if (arc.tail_var == first_arc_head)
break;
585 arc_index = bf_parent_arc_of_[arc.tail_var.value()];
588 CHECK_NE(arc_on_cycle.size(), num_nodes + 1) <<
"Infinite loop.";
592 for (
const ArcIndex arc_index : arc_on_cycle) {
593 const ArcInfo& arc = arcs_[arc_index];
594 sum += ArcOffset(arc);
595 AppendLowerBoundReasonIfValid(arc.offset_var, *integer_trail_,
597 for (
const Literal l : arc.presence_literals) {
598 literal_reason->push_back(l.Negated());
606 if (integer_trail_->
IsOptional(arc.head_var)) {
607 must_be_all_true->push_back(
622 bool PrecedencesPropagator::BellmanFordTarjan(Trail* trail) {
623 const int num_nodes = impacted_arcs_.
size();
626 bf_can_be_skipped_.resize(num_nodes,
false);
627 bf_parent_arc_of_.resize(num_nodes,
ArcIndex(-1));
632 while (!bf_queue_.empty()) {
633 const int node = bf_queue_.front();
634 bf_queue_.pop_front();
635 bf_in_queue_[node] =
false;
645 if (bf_can_be_skipped_[node]) {
646 DCHECK_NE(bf_parent_arc_of_[node], -1);
647 DCHECK(!arcs_[bf_parent_arc_of_[node]].is_marked);
651 const IntegerValue tail_lb =
652 integer_trail_->
LowerBound(IntegerVariable(node));
653 for (
const ArcIndex arc_index : impacted_arcs_[IntegerVariable(node)]) {
654 const ArcInfo& arc = arcs_[arc_index];
655 DCHECK_EQ(arc.tail_var, node);
656 const IntegerValue candidate = tail_lb + ArcOffset(arc);
657 if (candidate > integer_trail_->
LowerBound(arc.head_var)) {
659 if (!EnqueueAndCheck(arc, candidate, trail))
return false;
667 if (DisassembleSubtree(arc.head_var.value(), arc.tail_var.value(),
668 &bf_can_be_skipped_)) {
669 std::vector<Literal> must_be_all_true;
670 AnalyzePositiveCycle(arc_index, trail, &must_be_all_true,
671 &literal_reason_, &integer_reason_);
672 if (must_be_all_true.empty()) {
677 for (
const Literal l : must_be_all_true) {
679 literal_reason_.push_back(l);
684 for (
const Literal l : must_be_all_true) {
699 if (bf_parent_arc_of_[arc.head_var.value()] != -1) {
700 arcs_[bf_parent_arc_of_[arc.head_var.value()]].is_marked =
false;
709 const IntegerValue new_bound = integer_trail_->
LowerBound(arc.head_var);
710 if (new_bound == candidate) {
711 bf_parent_arc_of_[arc.head_var.value()] = arc_index;
712 arcs_[arc_index].is_marked =
true;
716 bf_parent_arc_of_[arc.head_var.value()] = -1;
721 bf_can_be_skipped_[arc.head_var.value()] =
false;
722 if (!bf_in_queue_[arc.head_var.value()] && new_bound >= candidate) {
723 bf_queue_.push_back(arc.head_var.value());
724 bf_in_queue_[arc.head_var.value()] =
true;
732 int PrecedencesPropagator::AddGreaterThanAtLeastOneOfConstraintsFromClause(
733 const absl::Span<const Literal> clause, Model*
model) {
734 CHECK_EQ(
model->GetOrCreate<Trail>()->CurrentDecisionLevel(), 0);
735 if (clause.size() < 2)
return 0;
738 std::vector<ArcInfo> infos;
739 for (
const Literal l : clause) {
740 if (l.Index() >= literal_to_new_impacted_arcs_.
size())
continue;
741 for (
const ArcIndex arc_index : literal_to_new_impacted_arcs_[l.Index()]) {
742 const ArcInfo& arc = arcs_[arc_index];
743 if (arc.presence_literals.size() != 1)
continue;
747 infos.push_back(arc);
750 if (infos.size() <= 1)
return 0;
754 std::stable_sort(infos.begin(), infos.end(),
755 [](
const ArcInfo&
a,
const ArcInfo&
b) {
756 return a.head_var < b.head_var;
760 int num_added_constraints = 0;
761 auto* solver =
model->GetOrCreate<SatSolver>();
762 for (
int i = 0; i < infos.size();) {
764 const IntegerVariable head_var = infos[start].head_var;
765 for (i++; i < infos.size() && infos[i].head_var == head_var; ++i) {
767 const absl::Span<ArcInfo> arcs(&infos[start], i - start);
770 if (arcs.size() < 2)
continue;
775 if (arcs.size() + 1 < clause.size())
continue;
777 std::vector<IntegerVariable> vars;
778 std::vector<IntegerValue> offsets;
779 std::vector<Literal> selectors;
780 std::vector<Literal> enforcements;
783 for (
const Literal l : clause) {
785 for (; j < arcs.size() && l == arcs[j].presence_literals.front(); ++j) {
787 vars.push_back(arcs[j].tail_var);
788 offsets.push_back(arcs[j].offset);
795 selectors.push_back(l);
798 enforcements.push_back(l.Negated());
804 if (enforcements.size() + 1 == clause.size())
continue;
806 ++num_added_constraints;
809 if (!solver->FinishPropagation())
return num_added_constraints;
811 return num_added_constraints;
814 int PrecedencesPropagator::
815 AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(Model*
model) {
817 auto* solver =
model->GetOrCreate<SatSolver>();
821 for (
ArcIndex arc_index(0); arc_index < arcs_.
size(); ++arc_index) {
822 const ArcInfo& arc = arcs_[arc_index];
826 if (arc.tail_var == arc.head_var)
continue;
827 if (arc.presence_literals.size() != 1)
continue;
829 if (arc.head_var >= incoming_arcs_.size()) {
830 incoming_arcs_.
resize(arc.head_var.value() + 1);
832 incoming_arcs_[arc.head_var].push_back(arc_index);
835 int num_added_constraints = 0;
836 for (IntegerVariable target(0); target < incoming_arcs_.size(); ++target) {
837 if (incoming_arcs_[target].size() <= 1)
continue;
838 if (
time_limit->LimitReached())
return num_added_constraints;
843 solver->Backtrack(0);
844 if (solver->IsModelUnsat())
return num_added_constraints;
845 std::vector<Literal> clause;
846 for (
const ArcIndex arc_index : incoming_arcs_[target]) {
847 const Literal
literal = arcs_[arc_index].presence_literals.
front();
848 if (solver->Assignment().LiteralIsFalse(
literal))
continue;
850 const int old_level = solver->CurrentDecisionLevel();
851 solver->EnqueueDecisionAndBacktrackOnConflict(
literal.Negated());
852 if (solver->IsModelUnsat())
return num_added_constraints;
853 const int new_level = solver->CurrentDecisionLevel();
854 if (new_level <= old_level) {
855 clause = solver->GetLastIncompatibleDecisions();
859 solver->Backtrack(0);
861 if (clause.size() > 1) {
863 const std::set<Literal> clause_set(clause.begin(), clause.end());
864 std::vector<ArcIndex> arcs_in_clause;
865 for (
const ArcIndex arc_index : incoming_arcs_[target]) {
866 const Literal
literal(arcs_[arc_index].presence_literals.front());
868 arcs_in_clause.push_back(arc_index);
872 VLOG(2) << arcs_in_clause.size() <<
"/" << incoming_arcs_[target].size();
874 ++num_added_constraints;
875 std::vector<IntegerVariable> vars;
876 std::vector<IntegerValue> offsets;
877 std::vector<Literal> selectors;
878 for (
const ArcIndex a : arcs_in_clause) {
879 vars.push_back(arcs_[
a].tail_var);
880 offsets.push_back(arcs_[
a].offset);
881 selectors.push_back(Literal(arcs_[
a].presence_literals.front()));
884 if (!solver->FinishPropagation())
return num_added_constraints;
888 return num_added_constraints;
892 VLOG(1) <<
"Detecting GreaterThanAtLeastOneOf() constraints...";
896 int num_added_constraints = 0;
904 if (clauses->AllClausesInCreationOrder().size() < 1e6) {
912 for (
const SatClause* clause : clauses->AllClausesInCreationOrder()) {
913 if (
time_limit->LimitReached())
return num_added_constraints;
914 if (solver->IsModelUnsat())
return num_added_constraints;
915 num_added_constraints += AddGreaterThanAtLeastOneOfConstraintsFromClause(
916 clause->AsSpan(),
model);
919 num_added_constraints +=
920 AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(
model);
923 VLOG(1) <<
"Added " << num_added_constraints
924 <<
" GreaterThanAtLeastOneOf() constraints.";
925 return num_added_constraints;