24#include "absl/cleanup/cleanup.h"
25#include "absl/container/inlined_vector.h"
26#include "absl/log/check.h"
27#include "absl/log/log.h"
28#include "absl/log/vlog_is_on.h"
29#include "absl/types/span.h"
45void AppendLowerBoundReasonIfValid(IntegerVariable var,
47 std::vector<IntegerLiteral>* reason) {
49 reason->push_back(i_trail.LowerBoundAsLiteral(var));
56 if (!VLOG_IS_ON(1))
return;
57 if (shared_stats_ ==
nullptr)
return;
58 std::vector<std::pair<std::string, int64_t>> stats;
59 stats.push_back({
"precedences/num_cycles", num_cycles_});
60 stats.push_back({
"precedences/num_pushes", num_pushes_});
62 {
"precedences/num_enforcement_pushes", num_enforcement_pushes_});
63 shared_stats_->AddStats(stats);
71 if (literal.
Index() >= literal_to_new_impacted_arcs_.size())
continue;
75 for (
const ArcIndex arc_index :
76 literal_to_new_impacted_arcs_[literal.
Index()]) {
77 if (--arc_counts_[arc_index] == 0) {
78 const ArcInfo& arc = arcs_[arc_index];
79 PushConditionalRelations(arc);
80 impacted_arcs_[arc.tail_var].push_back(arc_index);
86 for (
const ArcIndex arc_index :
87 literal_to_new_impacted_arcs_[literal.
Index()]) {
88 if (arc_counts_[arc_index] > 0)
continue;
89 const ArcInfo& arc = arcs_[arc_index];
90 const IntegerValue new_head_lb =
91 integer_trail_->LowerBound(arc.tail_var) + ArcOffset(arc);
92 if (new_head_lb > integer_trail_->LowerBound(arc.head_var)) {
93 if (!EnqueueAndCheck(arc, new_head_lb, trail_))
return false;
99 InitializeBFQueueWithModifiedNodes();
100 if (!BellmanFordTarjan(trail_))
return false;
110 DCHECK(NoPropagationLeft(*trail_));
114 if (!PropagateOptionalArcs(trail_))
return false;
117 modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
123 if (var >= impacted_arcs_.size())
return true;
124 for (
const ArcIndex arc_index : impacted_arcs_[var]) {
125 const ArcInfo& arc = arcs_[arc_index];
126 const IntegerValue new_head_lb =
127 integer_trail_->LowerBound(arc.tail_var) + ArcOffset(arc);
128 if (new_head_lb > integer_trail_->LowerBound(arc.head_var)) {
129 if (!EnqueueAndCheck(arc, new_head_lb, trail_))
return false;
136void PrecedencesPropagator::PushConditionalRelations(
const ArcInfo& arc) {
141 const IntegerValue offset = ArcOffset(arc);
143 arc.presence_literals,
152 modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
156 if (literal.
Index() >= literal_to_new_impacted_arcs_.size())
continue;
157 for (
const ArcIndex arc_index :
158 literal_to_new_impacted_arcs_[literal.
Index()]) {
159 if (arc_counts_[arc_index]++ == 0) {
160 const ArcInfo& arc = arcs_[arc_index];
161 impacted_arcs_[arc.tail_var].pop_back();
167void PrecedencesPropagator::AdjustSizeFor(IntegerVariable
i) {
168 const int index = std::max(
i.value(),
NegationOf(
i).value());
169 if (index >= impacted_arcs_.size()) {
172 for (IntegerVariable var(impacted_arcs_.size()); var <= index; ++var) {
175 impacted_arcs_.
resize(index + 1);
176 impacted_potential_arcs_.
resize(index + 1);
180void PrecedencesPropagator::AddArc(
181 IntegerVariable tail, IntegerVariable head, IntegerValue offset,
182 IntegerVariable offset_var, absl::Span<const Literal> presence_literals) {
188 absl::InlinedVector<Literal, 6> enforcement_literals;
190 for (
const Literal l : presence_literals) {
191 enforcement_literals.push_back(l);
195 if (trail_->CurrentDecisionLevel() == 0) {
197 for (
const Literal l : enforcement_literals) {
198 if (trail_->Assignment().LiteralIsTrue(Literal(l))) {
200 }
else if (trail_->Assignment().LiteralIsFalse(Literal(l))) {
203 enforcement_literals[new_size++] = l;
205 enforcement_literals.resize(new_size);
213 VLOG(1) <<
"Self arc! This could be presolved. "
214 <<
"var:" << tail <<
" offset:" << offset
215 <<
" offset_var:" << offset_var
216 <<
" conditioned_by:" << presence_literals;
222 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(offset_var);
223 if (lb == integer_trail_->LevelZeroUpperBound(offset_var)) {
230 if (!enforcement_literals.empty()) {
231 const OptionalArcIndex arc_index(potential_arcs_.size());
232 potential_arcs_.push_back(
233 {tail, head, offset, offset_var, enforcement_literals});
234 impacted_potential_arcs_[tail].push_back(arc_index);
235 impacted_potential_arcs_[
NegationOf(head)].push_back(arc_index);
237 impacted_potential_arcs_[offset_var].push_back(arc_index);
243 IntegerVariable tail_var;
244 IntegerVariable head_var;
245 IntegerVariable offset_var;
247 std::vector<InternalArc> to_add;
255 to_add.push_back({tail, head, offset_var});
256 to_add.push_back({offset_var, head, tail});
264 for (
const InternalArc a : to_add) {
279 modified_vars_.Set(a.tail_var);
283 const ArcIndex arc_index(arcs_.size());
285 {a.tail_var, a.head_var, offset, a.offset_var, enforcement_literals});
286 auto& presence_literals = arcs_.back().presence_literals;
288 if (presence_literals.empty()) {
289 impacted_arcs_[a.tail_var].push_back(arc_index);
291 for (
const Literal l : presence_literals) {
292 if (l.Index() >= literal_to_new_impacted_arcs_.size()) {
293 literal_to_new_impacted_arcs_.resize(l.Index().value() + 1);
295 literal_to_new_impacted_arcs_[l.Index()].push_back(arc_index);
299 if (trail_->CurrentDecisionLevel() == 0) {
300 arc_counts_.push_back(presence_literals.size());
302 arc_counts_.push_back(0);
303 for (
const Literal l : presence_literals) {
304 if (!trail_->Assignment().LiteralIsTrue(l)) {
305 ++arc_counts_.back();
308 CHECK(presence_literals.empty() || arc_counts_.back() > 0);
315 IntegerValue offset) {
316 DCHECK_EQ(trail_->CurrentDecisionLevel(), 0);
317 if (i1 < impacted_arcs_.size() && i2 < impacted_arcs_.size()) {
318 for (
const ArcIndex index : impacted_arcs_[i1]) {
319 const ArcInfo& arc = arcs_[index];
320 if (arc.head_var == i2) {
321 const IntegerValue current = ArcOffset(arc);
322 if (offset <= current) {
341bool PrecedencesPropagator::PropagateOptionalArcs(
Trail* trail) {
344 if (var >= impacted_potential_arcs_.size())
continue;
348 for (
const OptionalArcIndex arc_index : impacted_potential_arcs_[var]) {
349 const ArcInfo& arc = potential_arcs_[arc_index];
350 int num_not_true = 0;
352 for (
const Literal l : arc.presence_literals) {
358 if (num_not_true != 1)
continue;
363 const IntegerValue tail_lb = integer_trail_->
LowerBound(arc.tail_var);
364 const IntegerValue head_ub = integer_trail_->
UpperBound(arc.head_var);
365 if (tail_lb + ArcOffset(arc) > head_ub) {
366 integer_reason_.clear();
367 integer_reason_.push_back(
369 integer_reason_.push_back(
371 AppendLowerBoundReasonIfValid(arc.offset_var, *integer_trail_,
373 literal_reason_.clear();
374 for (
const Literal l : arc.presence_literals) {
375 if (l != to_propagate) literal_reason_.push_back(l.Negated());
377 ++num_enforcement_pushes_;
378 if (!integer_trail_->EnqueueLiteral(to_propagate.
Negated(),
379 literal_reason_, integer_reason_)) {
388IntegerValue PrecedencesPropagator::ArcOffset(
const ArcInfo& arc)
const {
391 : integer_trail_->
LowerBound(arc.offset_var));
394bool PrecedencesPropagator::EnqueueAndCheck(
const ArcInfo& arc,
395 IntegerValue new_head_lb,
398 DCHECK_GT(new_head_lb, integer_trail_->LowerBound(arc.head_var));
405 literal_reason_.clear();
406 for (
const Literal l : arc.presence_literals) {
407 literal_reason_.push_back(l.Negated());
410 integer_reason_.clear();
411 integer_reason_.push_back(integer_trail_->LowerBoundAsLiteral(arc.tail_var));
412 AppendLowerBoundReasonIfValid(arc.offset_var, *integer_trail_,
423 if (new_head_lb > integer_trail_->UpperBound(arc.head_var)) {
424 const IntegerValue slack =
425 new_head_lb - integer_trail_->UpperBound(arc.head_var) - 1;
426 integer_reason_.push_back(
427 integer_trail_->UpperBoundAsLiteral(arc.head_var));
428 std::vector<IntegerValue> coeffs(integer_reason_.size(), IntegerValue(1));
429 integer_trail_->RelaxLinearReason(slack, coeffs, &integer_reason_);
430 return integer_trail_->ReportConflict(literal_reason_, integer_reason_);
433 return integer_trail_->Enqueue(
435 literal_reason_, integer_reason_);
438bool PrecedencesPropagator::NoPropagationLeft(
const Trail& trail)
const {
439 const int num_nodes = impacted_arcs_.size();
440 for (IntegerVariable var(0); var < num_nodes; ++var) {
441 for (
const ArcIndex arc_index : impacted_arcs_[var]) {
442 const ArcInfo& arc = arcs_[arc_index];
443 if (integer_trail_->LowerBound(arc.tail_var) + ArcOffset(arc) >
444 integer_trail_->LowerBound(arc.head_var)) {
452void PrecedencesPropagator::InitializeBFQueueWithModifiedNodes() {
455 const int num_nodes = impacted_arcs_.size();
456 bf_in_queue_.resize(num_nodes,
false);
457 for (
const int node : bf_queue_) bf_in_queue_[node] =
false;
459 DCHECK(std::none_of(bf_in_queue_.begin(), bf_in_queue_.end(),
460 [](
bool v) { return v; }));
461 for (
const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
462 if (var >= num_nodes)
continue;
463 bf_queue_.push_back(var.value());
464 bf_in_queue_[var.value()] =
true;
468void PrecedencesPropagator::CleanUpMarkedArcsAndParents() {
471 const int num_nodes = impacted_arcs_.size();
472 for (
const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
473 if (var >= num_nodes)
continue;
474 const ArcIndex parent_arc_index = bf_parent_arc_of_[var.value()];
475 if (parent_arc_index != -1) {
476 arcs_[parent_arc_index].is_marked =
false;
477 bf_parent_arc_of_[var.value()] = -1;
478 bf_can_be_skipped_[var.value()] =
false;
481 DCHECK(std::none_of(bf_parent_arc_of_.begin(), bf_parent_arc_of_.end(),
482 [](ArcIndex v) { return v != -1; }));
483 DCHECK(std::none_of(bf_can_be_skipped_.begin(), bf_can_be_skipped_.end(),
484 [](
bool v) { return v; }));
487bool PrecedencesPropagator::DisassembleSubtree(
488 int source,
int target, std::vector<bool>* can_be_skipped) {
492 tmp_vector_.push_back(source);
493 while (!tmp_vector_.empty()) {
494 const int tail = tmp_vector_.back();
495 tmp_vector_.pop_back();
496 for (
const ArcIndex arc_index : impacted_arcs_[IntegerVariable(tail)]) {
497 const ArcInfo& arc = arcs_[arc_index];
499 arc.is_marked =
false;
500 if (arc.head_var.value() == target)
return true;
501 DCHECK(!(*can_be_skipped)[arc.head_var.value()]);
502 (*can_be_skipped)[arc.head_var.value()] =
true;
503 tmp_vector_.push_back(arc.head_var.value());
510void PrecedencesPropagator::AnalyzePositiveCycle(
511 ArcIndex first_arc,
Trail* trail, std::vector<Literal>* must_be_all_true,
512 std::vector<Literal>* literal_reason,
513 std::vector<IntegerLiteral>* integer_reason) {
514 must_be_all_true->clear();
515 literal_reason->clear();
516 integer_reason->clear();
519 const IntegerVariable first_arc_head = arcs_[first_arc].head_var;
520 ArcIndex arc_index = first_arc;
521 std::vector<ArcIndex> arc_on_cycle;
527 const int num_nodes = impacted_arcs_.size();
528 while (arc_on_cycle.size() <= num_nodes) {
529 arc_on_cycle.push_back(arc_index);
530 const ArcInfo& arc = arcs_[arc_index];
531 if (arc.tail_var == first_arc_head)
break;
532 arc_index = bf_parent_arc_of_[arc.tail_var.value()];
533 CHECK_NE(arc_index, ArcIndex(-1));
535 CHECK_NE(arc_on_cycle.size(), num_nodes + 1) <<
"Infinite loop.";
539 for (
const ArcIndex arc_index : arc_on_cycle) {
540 const ArcInfo& arc = arcs_[arc_index];
541 sum += ArcOffset(arc);
542 AppendLowerBoundReasonIfValid(arc.offset_var, *integer_trail_,
544 for (
const Literal l : arc.presence_literals) {
545 literal_reason->push_back(l.Negated());
559bool PrecedencesPropagator::BellmanFordTarjan(
Trail* trail) {
560 const int num_nodes = impacted_arcs_.size();
563 bf_can_be_skipped_.resize(num_nodes,
false);
564 bf_parent_arc_of_.resize(num_nodes, ArcIndex(-1));
566 ::absl::MakeCleanup([
this]() { CleanUpMarkedArcsAndParents(); });
569 while (!bf_queue_.empty()) {
570 const int node = bf_queue_.front();
571 bf_queue_.pop_front();
572 bf_in_queue_[node] =
false;
582 if (bf_can_be_skipped_[node]) {
583 DCHECK_NE(bf_parent_arc_of_[node], -1);
584 DCHECK(!arcs_[bf_parent_arc_of_[node]].is_marked);
588 const IntegerValue tail_lb =
589 integer_trail_->LowerBound(IntegerVariable(node));
590 for (
const ArcIndex arc_index : impacted_arcs_[IntegerVariable(node)]) {
591 const ArcInfo& arc = arcs_[arc_index];
592 DCHECK_EQ(arc.tail_var, node);
593 const IntegerValue candidate = tail_lb + ArcOffset(arc);
594 if (candidate > integer_trail_->LowerBound(arc.head_var)) {
595 if (!EnqueueAndCheck(arc, candidate, trail))
return false;
603 if (DisassembleSubtree(arc.head_var.value(), arc.tail_var.value(),
604 &bf_can_be_skipped_)) {
605 std::vector<Literal> must_be_all_true;
606 AnalyzePositiveCycle(arc_index, trail, &must_be_all_true,
607 &literal_reason_, &integer_reason_);
608 if (must_be_all_true.empty()) {
610 return integer_trail_->ReportConflict(literal_reason_,
614 for (
const Literal l : must_be_all_true) {
615 if (trail_->Assignment().LiteralIsFalse(l)) {
616 literal_reason_.push_back(l);
617 return integer_trail_->ReportConflict(literal_reason_,
621 for (
const Literal l : must_be_all_true) {
622 if (trail_->Assignment().LiteralIsTrue(l))
continue;
623 if (!integer_trail_->EnqueueLiteral(l, literal_reason_,
638 if (bf_parent_arc_of_[arc.head_var.value()] != -1) {
639 arcs_[bf_parent_arc_of_[arc.head_var.value()]].is_marked =
false;
648 const IntegerValue new_bound = integer_trail_->LowerBound(arc.head_var);
649 if (new_bound == candidate) {
650 bf_parent_arc_of_[arc.head_var.value()] = arc_index;
651 arcs_[arc_index].is_marked =
true;
655 bf_parent_arc_of_[arc.head_var.value()] = -1;
660 bf_can_be_skipped_[arc.head_var.value()] =
false;
661 if (!bf_in_queue_[arc.head_var.value()] && new_bound >= candidate) {
662 bf_queue_.push_back(arc.head_var.value());
663 bf_in_queue_[arc.head_var.value()] =
true;
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
void PushConditionalRelation(absl::Span< const Literal > enforcements, LinearExpression2Index index, IntegerValue rhs)
void WatchLowerBound(IntegerVariable var, int id, int watch_index=-1)
IntegerValue LowerBound(IntegerVariable i) const
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
IntegerValue UpperBound(IntegerVariable i) const
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
LiteralIndex Index() const
bool PropagateOutgoingArcs(IntegerVariable var)
bool AddPrecedenceWithOffsetIfNew(IntegerVariable i1, IntegerVariable i2, IntegerValue offset)
void AddPrecedenceWithOffset(IntegerVariable i1, IntegerVariable i2, IntegerValue offset)
~PrecedencesPropagator() override
void Untrail(const Trail &trail, int trail_index) final
int propagation_trail_index_
const VariablesAssignment & Assignment() const
bool LiteralIsFalse(Literal literal) const
bool LiteralIsTrue(Literal literal) const
void resize(size_type new_size)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
const IntegerVariable kNoIntegerVariable(-1)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
static LinearExpression2 Difference(IntegerVariable v1, IntegerVariable v2)