25#include "absl/cleanup/cleanup.h"
26#include "absl/container/btree_set.h"
27#include "absl/container/flat_hash_map.h"
28#include "absl/container/flat_hash_set.h"
29#include "absl/container/inlined_vector.h"
30#include "absl/log/check.h"
31#include "absl/meta/type_traits.h"
32#include "absl/types/span.h"
57 IntegerValue offset) {
59 if (integer_trail_->LevelZeroUpperBound(tail) + offset <=
60 integer_trail_->LevelZeroLowerBound(head)) {
66 if (tail == head)
return false;
72 if (offset <=
GetOffset(tail, head))
return false;
73 AddInternal(tail, head, offset);
79 if (!is_built_ && max_node >= graph_.num_nodes()) {
80 graph_.AddNode(max_node);
86 absl::Span<const Literal> enforcements, IntegerVariable a,
87 IntegerVariable
b, IntegerValue rhs) {
90 for (
const Literal l : enforcements) {
91 CHECK(trail_->Assignment().LiteralIsTrue(l));
95 if (enforcements.empty() || trail_->CurrentDecisionLevel() == 0) {
101 const auto key = GetKey(a,
b);
103 const auto [it, inserted] = best_relations_.insert({key, rhs});
105 if (rhs >= it->second)
return;
110 const int new_index = conditional_stack_.size();
111 const auto [it, inserted] = conditional_relations_.insert({key, new_index});
113 CreateLevelEntryIfNeeded();
114 conditional_stack_.emplace_back(-1, rhs, key, enforcements);
116 const int new_size = std::max(a.value(),
b.value()) + 1;
117 if (new_size > conditional_after_.size()) {
118 conditional_after_.resize(new_size);
125 const int prev_entry = it->second;
126 DCHECK_LT(rhs, conditional_stack_[prev_entry].rhs);
129 it->second = new_index;
130 CreateLevelEntryIfNeeded();
131 conditional_stack_.emplace_back(prev_entry, rhs, key, enforcements);
135void PrecedenceRelations::CreateLevelEntryIfNeeded() {
137 if (!level_to_stack_size_.empty() &&
138 level_to_stack_size_.back().first == current)
140 level_to_stack_size_.push_back({current, conditional_stack_.size()});
145 while (!level_to_stack_size_.empty() &&
146 level_to_stack_size_.back().first > level) {
147 const int target = level_to_stack_size_.back().second;
148 DCHECK_GE(conditional_stack_.size(), target);
149 while (conditional_stack_.size() > target) {
150 const ConditionalEntry& back = conditional_stack_.back();
151 if (back.prev_entry != -1) {
152 conditional_relations_[back.key] = back.prev_entry;
153 UpdateBestRelation(back.key, conditional_stack_[back.prev_entry].rhs);
156 conditional_relations_.erase(back.key);
158 DCHECK_EQ(conditional_after_[back.key.first].back(),
160 DCHECK_EQ(conditional_after_[back.key.second].back(),
162 conditional_after_[back.key.first].pop_back();
163 conditional_after_[back.key.second].pop_back();
165 conditional_stack_.pop_back();
167 level_to_stack_size_.pop_back();
172 IntegerVariable
b)
const {
173 const auto it = root_relations_.find(GetKey(a,
NegationOf(
b)));
174 if (it != root_relations_.end()) {
181 IntegerVariable a, IntegerVariable
b)
const {
182 const auto it = conditional_relations_.find(GetKey(a,
NegationOf(
b)));
183 if (it == conditional_relations_.end())
return {};
185 const ConditionalEntry& entry = conditional_stack_[it->second];
187 for (
const Literal l : entry.enforcements) {
188 CHECK(trail_->Assignment().LiteralIsTrue(l));
191 const IntegerValue root_level_offset =
GetOffset(a,
b);
192 const IntegerValue conditional_offset = -entry.rhs;
193 if (conditional_offset <= root_level_offset)
return {};
196 return entry.enforcements;
200 IntegerVariable a, IntegerVariable
b)
const {
201 const auto it = best_relations_.find(GetKey(a,
NegationOf(
b)));
202 if (it != best_relations_.end()) {
205 DCHECK(!root_relations_.contains(GetKey(a,
NegationOf(
b))));
206 DCHECK(!conditional_relations_.contains(GetKey(a,
NegationOf(
b))));
211 if (is_built_)
return;
214 const int num_nodes = graph_.num_nodes();
220 CHECK(arc_offsets_.empty());
221 graph_.ReserveArcs(2 * root_relations_.size());
223 std::pair<std::pair<IntegerVariable, IntegerVariable>, IntegerValue>>
224 root_relations_sorted(root_relations_.begin(), root_relations_.end());
225 std::sort(root_relations_sorted.begin(), root_relations_sorted.end());
226 for (
const auto [var_pair, negated_offset] : root_relations_sorted) {
232 const IntegerValue offset = -negated_offset;
233 if (offset < 0)
continue;
237 const IntegerVariable tail = var_pair.first;
238 const IntegerVariable head =
NegationOf(var_pair.second);
239 graph_.AddArc(tail.value(), head.value());
240 arc_offsets_.push_back(offset);
241 CHECK_LT(var_pair.second, before.size());
245 const IntegerVariable tail = var_pair.second;
246 const IntegerVariable head =
NegationOf(var_pair.first);
247 graph_.AddArc(tail.value(), head.value());
248 arc_offsets_.push_back(offset);
249 CHECK_LT(var_pair.second, before.size());
254 std::vector<int> permutation;
255 graph_.Build(&permutation);
268 for (
int arc = 0; arc < graph_.num_arcs(); ++arc) {
269 sorter.
AddEdge(graph_.Tail(arc), graph_.Head(arc));
272 bool graph_has_cycle =
false;
273 topological_order_.clear();
274 while (sorter.
GetNext(&next, &graph_has_cycle,
nullptr)) {
275 topological_order_.push_back(IntegerVariable(next));
276 if (graph_has_cycle) {
281 is_dag_ = !graph_has_cycle;
285 if (!is_dag_)
return;
288 const int kWorkLimit = 1e6;
289 for (
const IntegerVariable tail_var : topological_order_) {
290 if (++work > kWorkLimit)
break;
291 for (
const int arc : graph_.OutgoingArcs(tail_var.value())) {
292 DCHECK_EQ(tail_var.value(), graph_.Tail(arc));
293 const IntegerVariable head_var = IntegerVariable(graph_.Head(arc));
294 const IntegerValue arc_offset = arc_offsets_[arc];
296 if (++work > kWorkLimit)
break;
297 if (AddInternal(tail_var, head_var, arc_offset)) {
301 for (
const IntegerVariable before_var : before[tail_var]) {
302 if (++work > kWorkLimit)
break;
303 const IntegerValue offset =
304 -root_relations_.at(GetKey(before_var,
NegationOf(tail_var))) +
306 if (AddInternal(before_var, head_var, offset)) {
313 VLOG(2) <<
"Full precedences. Work=" << work
314 <<
" Relations=" << root_relations_.size();
318 absl::Span<const IntegerVariable> vars,
319 std::vector<FullIntegerPrecedence>* output) {
321 if (!is_built_)
Build();
322 if (!is_dag_)
return;
324 VLOG(2) <<
"num_nodes: " << graph_.num_nodes()
325 <<
" num_arcs: " << graph_.num_arcs() <<
" is_dag: " << is_dag_;
332 absl::flat_hash_set<IntegerVariable> is_interesting;
333 absl::flat_hash_set<IntegerVariable> to_consider(vars.begin(), vars.end());
334 absl::flat_hash_map<IntegerVariable,
335 absl::flat_hash_map<IntegerVariable, IntegerValue>>
336 vars_before_with_offset;
337 absl::flat_hash_map<IntegerVariable, IntegerValue> tail_map;
338 for (
const IntegerVariable tail_var : topological_order_) {
339 if (!to_consider.contains(tail_var) &&
340 !vars_before_with_offset.contains(tail_var)) {
348 const auto it = vars_before_with_offset.find(tail_var);
349 if (it != vars_before_with_offset.end()) {
350 tail_map = it->second;
354 for (
const int arc : graph_.OutgoingArcs(tail_var.value())) {
355 CHECK_EQ(tail_var.value(), graph_.Tail(arc));
356 const IntegerVariable head_var = IntegerVariable(graph_.Head(arc));
357 const IntegerValue arc_offset = arc_offsets_[arc];
360 if (tail_map.empty() && !to_consider.contains(tail_var))
continue;
362 auto& to_update = vars_before_with_offset[head_var];
363 for (
const auto& [var_before, offset] : tail_map) {
364 if (!to_update.contains(var_before)) {
365 to_update[var_before] = arc_offset + offset;
367 to_update[var_before] =
368 std::max(arc_offset + offset, to_update[var_before]);
371 if (to_consider.contains(tail_var)) {
372 if (!to_update.contains(tail_var)) {
373 to_update[tail_var] = arc_offset;
375 to_update[tail_var] = std::max(arc_offset, to_update[tail_var]);
383 if (to_update.size() > tail_map.size() + 1) {
384 is_interesting.insert(head_var);
386 is_interesting.erase(head_var);
394 if (!is_interesting.contains(tail_var))
continue;
395 if (tail_map.size() == 1)
continue;
400 for (
int i = 0;
i < vars.size(); ++
i) {
401 const auto offset_it = tail_map.find(vars[
i]);
402 if (offset_it == tail_map.end())
continue;
404 data.
offsets.push_back(offset_it->second);
405 min_offset = std::min(data.
offsets.back(), min_offset);
407 output->push_back(std::move(data));
412 absl::Span<const IntegerVariable> vars,
413 std::vector<PrecedenceData>* output) {
415 const int needed_size =
416 std::max(after_.size(), conditional_after_.size()) + 1;
417 var_to_degree_.resize(needed_size);
418 var_to_last_index_.resize(needed_size);
419 var_with_positive_degree_.resize(needed_size);
420 tmp_precedences_.clear();
424 int num_relevants = 0;
425 IntegerVariable* var_with_positive_degree = var_with_positive_degree_.data();
426 int* var_to_degree = var_to_degree_.data();
427 int* var_to_last_index = var_to_last_index_.data();
428 const auto process = [&](
int index, absl::Span<const IntegerVariable> v) {
429 for (
const IntegerVariable after : v) {
430 DCHECK_LT(after, needed_size);
431 if (var_to_degree[after.value()] == 0) {
432 var_with_positive_degree[num_relevants++] = after;
435 if (var_to_last_index[after.value()] == index)
continue;
438 tmp_precedences_.push_back({after, index});
439 var_to_degree[after.value()]++;
440 var_to_last_index[after.value()] = index;
444 for (
int index = 0; index < vars.size(); ++index) {
445 const IntegerVariable var = vars[index];
446 if (var < after_.size()) {
447 process(index, after_[var]);
449 if (var < conditional_after_.size()) {
450 process(index, conditional_after_[var]);
458 for (
int i = 0;
i < num_relevants; ++
i) {
459 const IntegerVariable var = var_with_positive_degree[
i];
460 const int degree = var_to_degree[var.value()];
462 var_to_degree[var.value()] = start;
466 var_to_degree[var.value()] = -1;
470 output->resize(start);
471 for (
const auto& precedence : tmp_precedences_) {
473 const int pos = var_to_degree[precedence.var.value()]++;
474 if (pos < 0)
continue;
475 (*output)[pos] = precedence;
480 for (
int i = 0;
i < num_relevants; ++
i) {
481 const IntegerVariable var = var_with_positive_degree[
i];
482 var_to_degree[var.value()] = 0;
488void AppendLowerBoundReasonIfValid(IntegerVariable var,
490 std::vector<IntegerLiteral>* reason) {
499 if (!VLOG_IS_ON(1))
return;
500 if (shared_stats_ ==
nullptr)
return;
501 std::vector<std::pair<std::string, int64_t>> stats;
502 stats.push_back({
"precedences/num_cycles", num_cycles_});
503 stats.push_back({
"precedences/num_pushes", num_pushes_});
505 {
"precedences/num_enforcement_pushes", num_enforcement_pushes_});
506 shared_stats_->AddStats(stats);
514 if (literal.
Index() >= literal_to_new_impacted_arcs_.size())
continue;
519 literal_to_new_impacted_arcs_[literal.
Index()]) {
520 if (--arc_counts_[arc_index] == 0) {
521 const ArcInfo& arc = arcs_[arc_index];
522 PushConditionalRelations(arc);
523 impacted_arcs_[arc.tail_var].push_back(arc_index);
530 literal_to_new_impacted_arcs_[literal.
Index()]) {
531 if (arc_counts_[arc_index] > 0)
continue;
532 const ArcInfo& arc = arcs_[arc_index];
533 const IntegerValue new_head_lb =
534 integer_trail_->LowerBound(arc.tail_var) + ArcOffset(arc);
535 if (new_head_lb > integer_trail_->LowerBound(arc.head_var)) {
536 if (!EnqueueAndCheck(arc, new_head_lb, trail_))
return false;
542 InitializeBFQueueWithModifiedNodes();
543 if (!BellmanFordTarjan(trail_))
return false;
553 DCHECK(NoPropagationLeft(*trail_));
557 PropagateOptionalArcs(trail_);
560 modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
566 if (var >= impacted_arcs_.size())
return true;
567 for (
const ArcIndex arc_index : impacted_arcs_[var]) {
568 const ArcInfo& arc = arcs_[arc_index];
569 const IntegerValue new_head_lb =
570 integer_trail_->LowerBound(arc.tail_var) + ArcOffset(arc);
571 if (new_head_lb > integer_trail_->LowerBound(arc.head_var)) {
572 if (!EnqueueAndCheck(arc, new_head_lb, trail_))
return false;
579void PrecedencesPropagator::PushConditionalRelations(
const ArcInfo& arc) {
584 const IntegerValue offset = ArcOffset(arc);
594 modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
598 if (literal.
Index() >= literal_to_new_impacted_arcs_.size())
continue;
600 literal_to_new_impacted_arcs_[literal.
Index()]) {
601 if (arc_counts_[arc_index]++ == 0) {
602 const ArcInfo& arc = arcs_[arc_index];
603 impacted_arcs_[arc.tail_var].pop_back();
609void PrecedencesPropagator::AdjustSizeFor(IntegerVariable
i) {
610 const int index = std::max(
i.value(),
NegationOf(
i).value());
611 if (index >= impacted_arcs_.size()) {
614 for (IntegerVariable var(impacted_arcs_.size()); var <= index; ++var) {
617 impacted_arcs_.
resize(index + 1);
618 impacted_potential_arcs_.
resize(index + 1);
622void PrecedencesPropagator::AddArc(
623 IntegerVariable tail, IntegerVariable head, IntegerValue offset,
624 IntegerVariable offset_var, absl::Span<const Literal> presence_literals) {
630 absl::InlinedVector<Literal, 6> enforcement_literals;
632 for (
const Literal l : presence_literals) {
633 enforcement_literals.push_back(l);
637 if (trail_->CurrentDecisionLevel() == 0) {
639 for (
const Literal l : enforcement_literals) {
640 if (trail_->Assignment().LiteralIsTrue(Literal(l))) {
642 }
else if (trail_->Assignment().LiteralIsFalse(Literal(l))) {
645 enforcement_literals[new_size++] = l;
647 enforcement_literals.resize(new_size);
655 VLOG(1) <<
"Self arc! This could be presolved. "
656 <<
"var:" << tail <<
" offset:" << offset
657 <<
" offset_var:" << offset_var
658 <<
" conditioned_by:" << presence_literals;
664 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(offset_var);
665 if (lb == integer_trail_->LevelZeroUpperBound(offset_var)) {
672 if (!enforcement_literals.empty()) {
673 const OptionalArcIndex arc_index(potential_arcs_.size());
674 potential_arcs_.push_back(
675 {tail, head, offset, offset_var, enforcement_literals});
676 impacted_potential_arcs_[tail].push_back(arc_index);
677 impacted_potential_arcs_[
NegationOf(head)].push_back(arc_index);
679 impacted_potential_arcs_[offset_var].push_back(arc_index);
685 IntegerVariable tail_var;
686 IntegerVariable head_var;
687 IntegerVariable offset_var;
689 std::vector<InternalArc> to_add;
697 to_add.push_back({tail, head, offset_var});
698 to_add.push_back({offset_var, head, tail});
706 for (
const InternalArc a : to_add) {
721 modified_vars_.Set(a.tail_var);
725 const ArcIndex arc_index(arcs_.size());
727 {a.tail_var, a.head_var, offset, a.offset_var, enforcement_literals});
728 auto& presence_literals = arcs_.back().presence_literals;
730 if (presence_literals.empty()) {
731 impacted_arcs_[a.tail_var].push_back(arc_index);
733 for (
const Literal l : presence_literals) {
734 if (l.Index() >= literal_to_new_impacted_arcs_.size()) {
735 literal_to_new_impacted_arcs_.resize(l.Index().value() + 1);
737 literal_to_new_impacted_arcs_[l.Index()].push_back(arc_index);
741 if (trail_->CurrentDecisionLevel() == 0) {
742 arc_counts_.push_back(presence_literals.size());
744 arc_counts_.push_back(0);
745 for (
const Literal l : presence_literals) {
746 if (!trail_->Assignment().LiteralIsTrue(l)) {
747 ++arc_counts_.back();
750 CHECK(presence_literals.empty() || arc_counts_.back() > 0);
757 IntegerValue offset) {
758 DCHECK_EQ(trail_->CurrentDecisionLevel(), 0);
759 if (i1 < impacted_arcs_.size() && i2 < impacted_arcs_.size()) {
760 for (
const ArcIndex index : impacted_arcs_[i1]) {
761 const ArcInfo& arc = arcs_[index];
762 if (arc.head_var == i2) {
763 const IntegerValue current = ArcOffset(arc);
764 if (offset <= current) {
783void PrecedencesPropagator::PropagateOptionalArcs(
Trail* trail) {
786 if (var >= impacted_potential_arcs_.size())
continue;
790 for (
const OptionalArcIndex arc_index : impacted_potential_arcs_[var]) {
791 const ArcInfo& arc = potential_arcs_[arc_index];
792 int num_not_true = 0;
794 for (
const Literal l : arc.presence_literals) {
800 if (num_not_true != 1)
continue;
805 const IntegerValue tail_lb = integer_trail_->
LowerBound(arc.tail_var);
806 const IntegerValue head_ub = integer_trail_->
UpperBound(arc.head_var);
807 if (tail_lb + ArcOffset(arc) > head_ub) {
808 integer_reason_.clear();
809 integer_reason_.push_back(
811 integer_reason_.push_back(
813 AppendLowerBoundReasonIfValid(arc.offset_var, *integer_trail_,
815 literal_reason_.clear();
816 for (
const Literal l : arc.presence_literals) {
817 if (l != to_propagate) literal_reason_.push_back(l.Negated());
819 ++num_enforcement_pushes_;
820 integer_trail_->EnqueueLiteral(to_propagate.
Negated(), literal_reason_,
827IntegerValue PrecedencesPropagator::ArcOffset(
const ArcInfo& arc)
const {
830 : integer_trail_->
LowerBound(arc.offset_var));
833bool PrecedencesPropagator::EnqueueAndCheck(
const ArcInfo& arc,
834 IntegerValue new_head_lb,
837 DCHECK_GT(new_head_lb, integer_trail_->LowerBound(arc.head_var));
844 literal_reason_.clear();
845 for (
const Literal l : arc.presence_literals) {
846 literal_reason_.push_back(l.Negated());
849 integer_reason_.clear();
850 integer_reason_.push_back(integer_trail_->LowerBoundAsLiteral(arc.tail_var));
851 AppendLowerBoundReasonIfValid(arc.offset_var, *integer_trail_,
862 if (new_head_lb > integer_trail_->UpperBound(arc.head_var)) {
863 const IntegerValue slack =
864 new_head_lb - integer_trail_->UpperBound(arc.head_var) - 1;
865 integer_reason_.push_back(
866 integer_trail_->UpperBoundAsLiteral(arc.head_var));
867 std::vector<IntegerValue> coeffs(integer_reason_.size(), IntegerValue(1));
868 integer_trail_->RelaxLinearReason(slack, coeffs, &integer_reason_);
869 return integer_trail_->ReportConflict(literal_reason_, integer_reason_);
872 return integer_trail_->Enqueue(
874 literal_reason_, integer_reason_);
877bool PrecedencesPropagator::NoPropagationLeft(
const Trail& trail)
const {
878 const int num_nodes = impacted_arcs_.size();
879 for (IntegerVariable var(0); var < num_nodes; ++var) {
880 for (
const ArcIndex arc_index : impacted_arcs_[var]) {
881 const ArcInfo& arc = arcs_[arc_index];
882 if (integer_trail_->LowerBound(arc.tail_var) + ArcOffset(arc) >
883 integer_trail_->LowerBound(arc.head_var)) {
891void PrecedencesPropagator::InitializeBFQueueWithModifiedNodes() {
894 const int num_nodes = impacted_arcs_.size();
895 bf_in_queue_.resize(num_nodes,
false);
896 for (
const int node : bf_queue_) bf_in_queue_[node] =
false;
898 DCHECK(std::none_of(bf_in_queue_.begin(), bf_in_queue_.end(),
899 [](
bool v) { return v; }));
900 for (
const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
901 if (var >= num_nodes)
continue;
902 bf_queue_.push_back(var.value());
903 bf_in_queue_[var.value()] =
true;
907void PrecedencesPropagator::CleanUpMarkedArcsAndParents() {
910 const int num_nodes = impacted_arcs_.size();
911 for (
const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
912 if (var >= num_nodes)
continue;
913 const ArcIndex parent_arc_index = bf_parent_arc_of_[var.value()];
914 if (parent_arc_index != -1) {
915 arcs_[parent_arc_index].is_marked =
false;
916 bf_parent_arc_of_[var.value()] = -1;
917 bf_can_be_skipped_[var.value()] =
false;
920 DCHECK(std::none_of(bf_parent_arc_of_.begin(), bf_parent_arc_of_.end(),
921 [](
ArcIndex v) { return v != -1; }));
922 DCHECK(std::none_of(bf_can_be_skipped_.begin(), bf_can_be_skipped_.end(),
923 [](
bool v) { return v; }));
926bool PrecedencesPropagator::DisassembleSubtree(
927 int source,
int target, std::vector<bool>* can_be_skipped) {
931 tmp_vector_.push_back(source);
932 while (!tmp_vector_.empty()) {
933 const int tail = tmp_vector_.back();
934 tmp_vector_.pop_back();
935 for (
const ArcIndex arc_index : impacted_arcs_[IntegerVariable(tail)]) {
936 const ArcInfo& arc = arcs_[arc_index];
938 arc.is_marked =
false;
939 if (arc.head_var.value() == target)
return true;
940 DCHECK(!(*can_be_skipped)[arc.head_var.value()]);
941 (*can_be_skipped)[arc.head_var.value()] =
true;
942 tmp_vector_.push_back(arc.head_var.value());
949void PrecedencesPropagator::AnalyzePositiveCycle(
950 ArcIndex first_arc,
Trail* trail, std::vector<Literal>* must_be_all_true,
951 std::vector<Literal>* literal_reason,
952 std::vector<IntegerLiteral>* integer_reason) {
953 must_be_all_true->clear();
954 literal_reason->clear();
955 integer_reason->clear();
958 const IntegerVariable first_arc_head = arcs_[first_arc].head_var;
960 std::vector<ArcIndex> arc_on_cycle;
966 const int num_nodes = impacted_arcs_.size();
967 while (arc_on_cycle.size() <= num_nodes) {
968 arc_on_cycle.push_back(arc_index);
969 const ArcInfo& arc = arcs_[arc_index];
970 if (arc.tail_var == first_arc_head)
break;
971 arc_index = bf_parent_arc_of_[arc.tail_var.value()];
974 CHECK_NE(arc_on_cycle.size(), num_nodes + 1) <<
"Infinite loop.";
978 for (
const ArcIndex arc_index : arc_on_cycle) {
979 const ArcInfo& arc = arcs_[arc_index];
980 sum += ArcOffset(arc);
981 AppendLowerBoundReasonIfValid(arc.offset_var, *integer_trail_,
983 for (
const Literal l : arc.presence_literals) {
984 literal_reason->push_back(l.Negated());
998bool PrecedencesPropagator::BellmanFordTarjan(
Trail* trail) {
999 const int num_nodes = impacted_arcs_.size();
1002 bf_can_be_skipped_.resize(num_nodes,
false);
1003 bf_parent_arc_of_.resize(num_nodes,
ArcIndex(-1));
1004 const auto cleanup =
1005 ::absl::MakeCleanup([
this]() { CleanUpMarkedArcsAndParents(); });
1008 while (!bf_queue_.empty()) {
1009 const int node = bf_queue_.front();
1010 bf_queue_.pop_front();
1011 bf_in_queue_[node] =
false;
1021 if (bf_can_be_skipped_[node]) {
1022 DCHECK_NE(bf_parent_arc_of_[node], -1);
1023 DCHECK(!arcs_[bf_parent_arc_of_[node]].is_marked);
1027 const IntegerValue tail_lb =
1028 integer_trail_->LowerBound(IntegerVariable(node));
1029 for (
const ArcIndex arc_index : impacted_arcs_[IntegerVariable(node)]) {
1030 const ArcInfo& arc = arcs_[arc_index];
1031 DCHECK_EQ(arc.tail_var, node);
1032 const IntegerValue candidate = tail_lb + ArcOffset(arc);
1033 if (candidate > integer_trail_->LowerBound(arc.head_var)) {
1034 if (!EnqueueAndCheck(arc, candidate, trail))
return false;
1042 if (DisassembleSubtree(arc.head_var.value(), arc.tail_var.value(),
1043 &bf_can_be_skipped_)) {
1044 std::vector<Literal> must_be_all_true;
1045 AnalyzePositiveCycle(arc_index, trail, &must_be_all_true,
1046 &literal_reason_, &integer_reason_);
1047 if (must_be_all_true.empty()) {
1049 return integer_trail_->ReportConflict(literal_reason_,
1053 for (
const Literal l : must_be_all_true) {
1054 if (trail_->Assignment().LiteralIsFalse(l)) {
1055 literal_reason_.push_back(l);
1056 return integer_trail_->ReportConflict(literal_reason_,
1060 for (
const Literal l : must_be_all_true) {
1061 if (trail_->Assignment().LiteralIsTrue(l))
continue;
1062 integer_trail_->EnqueueLiteral(l, literal_reason_,
1075 if (bf_parent_arc_of_[arc.head_var.value()] != -1) {
1076 arcs_[bf_parent_arc_of_[arc.head_var.value()]].is_marked =
false;
1085 const IntegerValue new_bound = integer_trail_->LowerBound(arc.head_var);
1086 if (new_bound == candidate) {
1087 bf_parent_arc_of_[arc.head_var.value()] = arc_index;
1088 arcs_[arc_index].is_marked =
true;
1092 bf_parent_arc_of_[arc.head_var.value()] = -1;
1097 bf_can_be_skipped_[arc.head_var.value()] =
false;
1098 if (!bf_in_queue_[arc.head_var.value()] && new_bound >= candidate) {
1099 bf_queue_.push_back(arc.head_var.value());
1100 bf_in_queue_[arc.head_var.value()] =
true;
1109 IntegerValue lhs, IntegerValue rhs) {
1127 relations_.push_back(std::move(r));
1133 std::vector<LiteralIndex> keys;
1134 const int num_relations = relations_.size();
1135 keys.reserve(num_relations);
1136 for (
int i = 0;
i < num_relations; ++
i) {
1137 keys.push_back(relations_[
i].enforcement.Index());
1142bool GreaterThanAtLeastOneOfDetector::AddRelationFromIndices(
1143 IntegerVariable var, absl::Span<const Literal> clause,
1144 absl::Span<const int> indices,
Model* model) {
1145 std::vector<AffineExpression> exprs;
1146 std::vector<Literal> selectors;
1147 absl::flat_hash_set<LiteralIndex> used;
1151 for (
const int index : indices) {
1163 exprs.push_back(AffineExpression(r.
b.
var, r.
b.
coeff, -r.
rhs));
1167 if (var_lb >= integer_trail->LevelZeroUpperBound(exprs.back())) {
1179 std::vector<Literal> enforcements;
1180 for (
const Literal l : clause) {
1181 if (!used.contains(l.Index())) {
1182 enforcements.push_back(l.Negated());
1188 if (used.size() <= 1)
return false;
1191 GreaterThanAtLeastOneOfPropagator* constraint =
1192 new GreaterThanAtLeastOneOfPropagator(var, exprs, selectors, enforcements,
1194 constraint->RegisterWith(model->
GetOrCreate<GenericLiteralWatcher>());
1199int GreaterThanAtLeastOneOfDetector::
1200 AddGreaterThanAtLeastOneOfConstraintsFromClause(
1201 const absl::Span<const Literal> clause,
Model* model) {
1202 CHECK_EQ(model->GetOrCreate<Trail>()->CurrentDecisionLevel(), 0);
1203 if (clause.size() < 2)
return 0;
1206 std::vector<std::pair<IntegerVariable, int>> infos;
1207 for (
const Literal l : clause) {
1208 for (
const int index : repository_.relation_indices(l.Index())) {
1209 const Relation& r = repository_.relation(index);
1211 infos.push_back({r.a.var, index});
1214 infos.push_back({r.b.var, index});
1218 if (infos.size() <= 1)
return 0;
1221 std::stable_sort(infos.begin(), infos.end());
1224 int num_added_constraints = 0;
1225 std::vector<int> indices;
1226 for (
int i = 0;
i < infos.size();) {
1227 const int start =
i;
1228 const IntegerVariable var = infos[start].first;
1231 for (;
i < infos.size() && infos[
i].first == var; ++
i) {
1232 indices.push_back(infos[
i].second);
1236 if (indices.size() < 2)
continue;
1242 if (indices.size() + 1 < clause.size())
continue;
1244 if (AddRelationFromIndices(var, clause, indices, model)) {
1245 ++num_added_constraints;
1247 if (AddRelationFromIndices(
NegationOf(var), clause, indices, model)) {
1248 ++num_added_constraints;
1251 return num_added_constraints;
1254int GreaterThanAtLeastOneOfDetector::
1255 AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(
Model* model) {
1256 auto*
time_limit = model->GetOrCreate<TimeLimit>();
1257 auto* solver = model->GetOrCreate<SatSolver>();
1260 util_intops::StrongVector<IntegerVariable, std::vector<int>> var_to_relations;
1261 for (
int index = 0; index < repository_.size(); ++index) {
1262 const Relation& r = repository_.relation(index);
1264 if (r.a.var >= var_to_relations.size()) {
1265 var_to_relations.resize(r.a.var + 1);
1267 var_to_relations[r.a.var].push_back(index);
1270 if (r.b.var >= var_to_relations.size()) {
1271 var_to_relations.resize(r.b.var + 1);
1273 var_to_relations[r.b.var].push_back(index);
1277 int num_added_constraints = 0;
1278 for (IntegerVariable target(0); target < var_to_relations.size(); ++target) {
1279 if (var_to_relations[target].size() <= 1)
continue;
1280 if (
time_limit->LimitReached())
return num_added_constraints;
1285 solver->Backtrack(0);
1286 if (solver->ModelIsUnsat())
return num_added_constraints;
1287 std::vector<Literal> clause;
1288 for (
const int index : var_to_relations[target]) {
1289 const Literal literal = repository_.relation(index).enforcement;
1290 if (solver->Assignment().LiteralIsFalse(literal))
continue;
1292 solver->EnqueueDecisionAndBacktrackOnConflict(literal.Negated());
1296 clause = solver->GetLastIncompatibleDecisions();
1297 for (Literal& ref : clause) ref = ref.Negated();
1301 solver->Backtrack(0);
1302 if (clause.size() <= 1)
continue;
1305 const absl::btree_set<Literal> clause_set(clause.begin(), clause.end());
1307 std::vector<int> indices;
1308 for (
const int index : var_to_relations[target]) {
1309 const Literal literal = repository_.relation(index).enforcement;
1310 if (clause_set.contains(literal)) {
1311 indices.push_back(index);
1316 if (AddRelationFromIndices(target, clause, indices, model)) {
1317 ++num_added_constraints;
1319 if (AddRelationFromIndices(
NegationOf(target), clause, indices, model)) {
1320 ++num_added_constraints;
1324 solver->Backtrack(0);
1325 return num_added_constraints;
1329 Model* model,
bool auto_detect_clauses) {
1335 int num_added_constraints = 0;
1336 SOLVER_LOG(logger,
"[Precedences] num_relations=", repository_.size(),
1337 " num_clauses=", clauses->AllClausesInCreationOrder().size());
1345 if (!auto_detect_clauses &&
1346 clauses->AllClausesInCreationOrder().size() < 1e6) {
1354 for (
const SatClause* clause : clauses->AllClausesInCreationOrder()) {
1355 if (
time_limit->LimitReached())
return num_added_constraints;
1356 if (solver->ModelIsUnsat())
return num_added_constraints;
1357 num_added_constraints += AddGreaterThanAtLeastOneOfConstraintsFromClause(
1358 clause->AsSpan(), model);
1365 const int num_booleans = solver->NumVariables();
1366 if (num_booleans < 1e6) {
1367 for (
int i = 0;
i < num_booleans; ++
i) {
1368 if (
time_limit->LimitReached())
return num_added_constraints;
1369 if (solver->ModelIsUnsat())
return num_added_constraints;
1370 num_added_constraints +=
1371 AddGreaterThanAtLeastOneOfConstraintsFromClause(
1372 {
Literal(BooleanVariable(
i),
true),
1373 Literal(BooleanVariable(
i),
false)},
1379 num_added_constraints +=
1380 AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(model);
1383 if (num_added_constraints > 0) {
1384 SOLVER_LOG(logger,
"[Precedences] Added ", num_added_constraints,
1385 " GreaterThanAtLeastOneOf() constraints.");
1388 return num_added_constraints;
1393 const absl::flat_hash_map<IntegerVariable, IntegerValue>&
input,
1394 absl::flat_hash_map<IntegerVariable, IntegerValue>* output)
const {
1396 if (lit.
Index() >= lit_to_relations_.size())
return true;
1398 auto get_lower_bound = [&](IntegerVariable var) {
1399 const auto it =
input.find(var);
1400 if (it !=
input.end())
return it->second;
1403 auto get_upper_bound = [&](IntegerVariable var) {
1406 auto update_lower_bound_by_var = [&](IntegerVariable var, IntegerValue lb) {
1408 const auto [it, inserted] = output->insert({var, lb});
1410 it->second = std::max(it->second, lb);
1413 auto update_upper_bound_by_var = [&](IntegerVariable var, IntegerValue ub) {
1414 update_lower_bound_by_var(
NegationOf(var), -ub);
1417 IntegerValue lhs, IntegerValue rhs) {
1418 if (a.
coeff == 0)
return;
1422 lhs = lhs -
b.coeff * get_upper_bound(
b.var);
1423 rhs = rhs -
b.coeff * get_lower_bound(
b.var);
1427 for (
const int relation_index : lit_to_relations_[lit]) {
1428 auto r = relations_[relation_index];
1429 r.a.MakeCoeffPositive();
1430 r.b.MakeCoeffPositive();
1431 update_var_bounds(r.a, r.b, r.lhs, r.rhs);
1432 update_var_bounds(r.b, r.a, r.lhs, r.rhs);
1437 for (
const auto [var, lb] : *output) {
static IntegralType CeilOfRatio(IntegralType numerator, IntegralType denominator)
static IntegralType FloorOfRatio(IntegralType numerator, IntegralType denominator)
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
bool PropagateLocalBounds(const IntegerTrail &integer_trail, Literal lit, const absl::flat_hash_map< IntegerVariable, IntegerValue > &input, absl::flat_hash_map< IntegerVariable, IntegerValue > *output) const
const Relation & relation(int index) const
void Add(Literal lit, LinearTerm a, LinearTerm b, IntegerValue lhs, IntegerValue rhs)
Adds a relation lit => a + b \in [lhs, rhs].
void WatchLowerBound(IntegerVariable var, int id, int watch_index=-1)
int AddGreaterThanAtLeastOneOfConstraints(Model *model, bool auto_detect_clauses=false)
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
IntegerValue UpperBound(IntegerVariable i) const
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Returns globally valid lower/upper bound on the given integer variable.
LiteralIndex Index() const
void PushConditionalRelation(absl::Span< const Literal > enforcements, IntegerVariable a, IntegerVariable b, IntegerValue rhs)
void CollectPrecedences(absl::Span< const IntegerVariable > vars, std::vector< PrecedenceData > *output)
bool Add(IntegerVariable tail, IntegerVariable head, IntegerValue offset)
IntegerValue GetConditionalOffset(IntegerVariable a, IntegerVariable b) const
void ComputeFullPrecedences(absl::Span< const IntegerVariable > vars, std::vector< FullIntegerPrecedence > *output)
void SetLevel(int level) final
Called each time we change decision level.
IntegerValue GetOffset(IntegerVariable a, IntegerVariable b) const
absl::Span< const Literal > GetConditionalEnforcements(IntegerVariable a, IntegerVariable b) const
bool PropagateOutgoingArcs(IntegerVariable var)
bool AddPrecedenceWithOffsetIfNew(IntegerVariable i1, IntegerVariable i2, IntegerValue offset)
This version check current precedence. It is however "slow".
void AddPrecedenceWithOffset(IntegerVariable i1, IntegerVariable i2, IntegerValue offset)
~PrecedencesPropagator() override
void Untrail(const Trail &trail, int trail_index) final
int propagation_trail_index_
int CurrentDecisionLevel() const
const VariablesAssignment & Assignment() const
bool LiteralIsFalse(Literal literal) const
bool LiteralIsTrue(Literal literal) const
void AddEdge(int from, int to)
bool GetNext(int *next_node_index, bool *cyclic, std::vector< int > *output_cycle_nodes=nullptr)
void push_back(const value_type &val)
void resize(size_type new_size)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntType IntTypeAbs(IntType t)
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
Returns the vector of the negated variables.
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
IntegerVariable PositiveVariable(IntegerVariable i)
bool VariableIsPositive(IntegerVariable i)
In SWIG mode, we don't want anything besides these top-level includes.
void Permute(const IntVector &permutation, Array *array_to_permute)
static int input(yyscan_t yyscanner)
std::vector< IntegerValue > offsets
std::vector< int > indices
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
::util::DenseIntStableTopologicalSorter DenseIntStableTopologicalSorter
#define SOLVER_LOG(logger,...)