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"
55 IntegerValue offset) {
77 if (!is_built_ && max_node >= graph_.
num_nodes()) {
84 absl::Span<const Literal> enforcements, IntegerVariable
a,
85 IntegerVariable
b, IntegerValue rhs) {
88 for (
const Literal l : enforcements) {
99 const auto key = GetKey(
a,
b);
101 const auto [it, inserted] = best_relations_.insert({key, rhs});
103 if (rhs >= it->second)
return;
108 const int new_index = conditional_stack_.size();
109 const auto [it, inserted] = conditional_relations_.insert({key, new_index});
111 CreateLevelEntryIfNeeded();
112 conditional_stack_.emplace_back(-1, rhs, key, enforcements);
114 const int new_size = std::max(
a.value(),
b.value()) + 1;
115 if (new_size > conditional_after_.size()) {
116 conditional_after_.
resize(new_size);
123 const int prev_entry = it->second;
124 DCHECK_LT(rhs, conditional_stack_[prev_entry].rhs);
127 it->second = new_index;
128 CreateLevelEntryIfNeeded();
129 conditional_stack_.emplace_back(prev_entry, rhs, key, enforcements);
133void PrecedenceRelations::CreateLevelEntryIfNeeded() {
135 if (!level_to_stack_size_.empty() &&
136 level_to_stack_size_.back().first == current)
138 level_to_stack_size_.push_back({current, conditional_stack_.size()});
143 while (!level_to_stack_size_.empty() &&
144 level_to_stack_size_.back().first > level) {
145 const int target = level_to_stack_size_.back().second;
146 DCHECK_GE(conditional_stack_.size(), target);
147 while (conditional_stack_.size() > target) {
148 const ConditionalEntry& back = conditional_stack_.back();
149 if (back.prev_entry != -1) {
150 conditional_relations_[back.key] = back.prev_entry;
151 UpdateBestRelation(back.key, conditional_stack_[back.prev_entry].rhs);
154 conditional_relations_.erase(back.key);
156 DCHECK_EQ(conditional_after_[back.key.first].back(),
158 DCHECK_EQ(conditional_after_[back.key.second].back(),
160 conditional_after_[back.key.first].pop_back();
161 conditional_after_[back.key.second].pop_back();
163 conditional_stack_.pop_back();
165 level_to_stack_size_.pop_back();
170 IntegerVariable
b)
const {
171 const auto it = root_relations_.find(GetKey(
a,
NegationOf(
b)));
172 if (it != root_relations_.end()) {
179 IntegerVariable
a, IntegerVariable
b)
const {
180 const auto it = conditional_relations_.find(GetKey(
a,
NegationOf(
b)));
181 if (it == conditional_relations_.end())
return {};
183 const ConditionalEntry& entry = conditional_stack_[it->second];
185 for (
const Literal l : entry.enforcements) {
189 const IntegerValue root_level_offset =
GetOffset(
a,
b);
190 const IntegerValue conditional_offset = -entry.rhs;
191 if (conditional_offset <= root_level_offset)
return {};
194 return entry.enforcements;
198 IntegerVariable
a, IntegerVariable
b)
const {
199 const auto it = best_relations_.find(GetKey(
a,
NegationOf(
b)));
200 if (it != best_relations_.end()) {
203 DCHECK(!root_relations_.contains(GetKey(
a,
NegationOf(
b))));
204 DCHECK(!conditional_relations_.contains(GetKey(
a,
NegationOf(
b))));
209 if (is_built_)
return;
212 const int num_nodes = graph_.
num_nodes();
219 CHECK(arc_offsets_.empty());
221 for (
const auto [var_pair, negated_offset] : root_relations_) {
227 const IntegerValue offset = -negated_offset;
228 if (offset < 0)
continue;
232 const IntegerVariable
tail = var_pair.first;
235 arc_offsets_.push_back(offset);
236 CHECK_LT(var_pair.second, before.size());
240 const IntegerVariable
tail = var_pair.second;
243 arc_offsets_.push_back(offset);
244 CHECK_LT(var_pair.second, before.size());
249 std::vector<int> permutation;
250 graph_.
Build(&permutation);
267 bool graph_has_cycle =
false;
268 topological_order_.clear();
269 while (sorter.
GetNext(&
next, &graph_has_cycle,
nullptr)) {
270 topological_order_.push_back(IntegerVariable(
next));
271 if (graph_has_cycle) {
276 is_dag_ = !graph_has_cycle;
280 if (!is_dag_)
return;
283 const int kWorkLimit = 1e6;
284 for (
const IntegerVariable tail_var : topological_order_) {
285 if (++work > kWorkLimit)
break;
287 DCHECK_EQ(tail_var.value(), graph_.
Tail(
arc));
288 const IntegerVariable head_var = IntegerVariable(graph_.
Head(
arc));
289 const IntegerValue arc_offset = arc_offsets_[
arc];
291 if (++work > kWorkLimit)
break;
292 if (AddInternal(tail_var, head_var, arc_offset)) {
296 for (
const IntegerVariable before_var : before[tail_var]) {
297 if (++work > kWorkLimit)
break;
298 const IntegerValue offset =
299 -root_relations_.at(GetKey(before_var,
NegationOf(tail_var))) +
301 if (AddInternal(before_var, head_var, offset)) {
308 VLOG(2) <<
"Full precedences. Work=" << work
309 <<
" Relations=" << root_relations_.size();
313 absl::Span<const IntegerVariable> vars,
314 std::vector<FullIntegerPrecedence>* output) {
316 if (!is_built_)
Build();
317 if (!is_dag_)
return;
319 VLOG(2) <<
"num_nodes: " << graph_.
num_nodes()
320 <<
" num_arcs: " << graph_.
num_arcs() <<
" is_dag: " << is_dag_;
327 absl::flat_hash_set<IntegerVariable> is_interesting;
328 absl::flat_hash_set<IntegerVariable> to_consider(vars.begin(), vars.end());
329 absl::flat_hash_map<IntegerVariable,
330 absl::flat_hash_map<IntegerVariable, IntegerValue>>
331 vars_before_with_offset;
332 absl::flat_hash_map<IntegerVariable, IntegerValue> tail_map;
333 for (
const IntegerVariable tail_var : topological_order_) {
334 if (!to_consider.contains(tail_var) &&
335 !vars_before_with_offset.contains(tail_var)) {
343 const auto it = vars_before_with_offset.find(tail_var);
344 if (it != vars_before_with_offset.end()) {
345 tail_map = it->second;
350 CHECK_EQ(tail_var.value(), graph_.
Tail(
arc));
351 const IntegerVariable head_var = IntegerVariable(graph_.
Head(
arc));
352 const IntegerValue arc_offset = arc_offsets_[
arc];
355 if (tail_map.empty() && !to_consider.contains(tail_var))
continue;
357 auto& to_update = vars_before_with_offset[head_var];
358 for (
const auto& [var_before, offset] : tail_map) {
359 if (!to_update.contains(var_before)) {
360 to_update[var_before] = arc_offset + offset;
362 to_update[var_before] =
363 std::max(arc_offset + offset, to_update[var_before]);
366 if (to_consider.contains(tail_var)) {
367 if (!to_update.contains(tail_var)) {
368 to_update[tail_var] = arc_offset;
370 to_update[tail_var] = std::max(arc_offset, to_update[tail_var]);
378 if (to_update.size() > tail_map.size() + 1) {
379 is_interesting.insert(head_var);
381 is_interesting.erase(head_var);
389 if (!is_interesting.contains(tail_var))
continue;
390 if (tail_map.size() == 1)
continue;
395 for (
int i = 0;
i < vars.size(); ++
i) {
396 const auto offset_it = tail_map.find(vars[
i]);
397 if (offset_it == tail_map.end())
continue;
399 data.
offsets.push_back(offset_it->second);
400 min_offset = std::min(data.
offsets.back(), min_offset);
402 output->push_back(std::move(data));
407 absl::Span<const IntegerVariable> vars,
408 std::vector<PrecedenceData>* output) {
410 const int needed_size =
411 std::max(after_.size(), conditional_after_.size()) + 1;
412 var_to_degree_.
resize(needed_size);
413 var_to_last_index_.
resize(needed_size);
414 var_with_positive_degree_.resize(needed_size);
415 tmp_precedences_.clear();
419 int num_relevants = 0;
420 IntegerVariable* var_with_positive_degree = var_with_positive_degree_.data();
421 int* var_to_degree = var_to_degree_.
data();
422 int* var_to_last_index = var_to_last_index_.
data();
423 const auto process = [&](
int index, absl::Span<const IntegerVariable> v) {
424 for (
const IntegerVariable after : v) {
425 DCHECK_LT(after, needed_size);
426 if (var_to_degree[after.value()] == 0) {
427 var_with_positive_degree[num_relevants++] = after;
430 if (var_to_last_index[after.value()] ==
index)
continue;
433 tmp_precedences_.push_back({after,
index});
434 var_to_degree[after.value()]++;
435 var_to_last_index[after.value()] =
index;
440 const IntegerVariable
var = vars[
index];
441 if (
var < after_.size()) {
444 if (
var < conditional_after_.size()) {
445 process(
index, conditional_after_[
var]);
453 for (
int i = 0;
i < num_relevants; ++
i) {
454 const IntegerVariable
var = var_with_positive_degree[
i];
455 const int degree = var_to_degree[
var.value()];
461 var_to_degree[
var.value()] = -1;
465 output->resize(
start);
466 for (
const auto& precedence : tmp_precedences_) {
468 const int pos = var_to_degree[precedence.var.value()]++;
469 if (pos < 0)
continue;
470 (*output)[pos] = precedence;
475 for (
int i = 0;
i < num_relevants; ++
i) {
476 const IntegerVariable
var = var_with_positive_degree[
i];
477 var_to_degree[
var.value()] = 0;
483void AppendLowerBoundReasonIfValid(IntegerVariable
var,
485 std::vector<IntegerLiteral>* reason) {
494 if (!VLOG_IS_ON(1))
return;
495 if (shared_stats_ ==
nullptr)
return;
496 std::vector<std::pair<std::string, int64_t>> stats;
497 stats.push_back({
"precedences/num_cycles", num_cycles_});
498 stats.push_back({
"precedences/num_pushes", num_pushes_});
500 {
"precedences/num_enforcement_pushes", num_enforcement_pushes_});
509 if (
literal.Index() >= literal_to_new_impacted_arcs_.size())
continue;
514 literal_to_new_impacted_arcs_[
literal.Index()]) {
515 if (--arc_counts_[arc_index] == 0) {
516 const ArcInfo&
arc = arcs_[arc_index];
517 PushConditionalRelations(
arc);
525 literal_to_new_impacted_arcs_[
literal.Index()]) {
526 if (arc_counts_[arc_index] > 0)
continue;
527 const ArcInfo&
arc = arcs_[arc_index];
528 const IntegerValue new_head_lb =
530 if (new_head_lb > integer_trail_->
LowerBound(
arc.head_var)) {
531 if (!EnqueueAndCheck(
arc, new_head_lb, trail_))
return false;
537 InitializeBFQueueWithModifiedNodes();
538 if (!BellmanFordTarjan(trail_))
return false;
548 DCHECK(NoPropagationLeft(*trail_));
552 PropagateOptionalArcs(trail_);
561 if (
var >= impacted_arcs_.size())
return true;
562 for (
const ArcIndex arc_index : impacted_arcs_[
var]) {
563 const ArcInfo&
arc = arcs_[arc_index];
564 const IntegerValue new_head_lb =
566 if (new_head_lb > integer_trail_->
LowerBound(
arc.head_var)) {
567 if (!EnqueueAndCheck(
arc, new_head_lb, trail_))
return false;
574void PrecedencesPropagator::PushConditionalRelations(
const ArcInfo&
arc) {
579 const IntegerValue offset = ArcOffset(
arc);
593 if (
literal.Index() >= literal_to_new_impacted_arcs_.size())
continue;
595 literal_to_new_impacted_arcs_[
literal.Index()]) {
596 if (arc_counts_[arc_index]++ == 0) {
597 const ArcInfo&
arc = arcs_[arc_index];
598 impacted_arcs_[
arc.tail_var].pop_back();
604void PrecedencesPropagator::AdjustSizeFor(IntegerVariable
i) {
606 if (
index >= impacted_arcs_.size()) {
609 for (IntegerVariable
var(impacted_arcs_.size());
var <=
index; ++
var) {
617void PrecedencesPropagator::AddArc(
618 IntegerVariable
tail, IntegerVariable
head, IntegerValue offset,
619 IntegerVariable offset_var, absl::Span<const Literal> presence_literals) {
625 absl::InlinedVector<Literal, 6> enforcement_literals;
627 for (
const Literal l : presence_literals) {
628 enforcement_literals.push_back(l);
634 for (
const Literal l : enforcement_literals) {
640 enforcement_literals[new_size++] = l;
642 enforcement_literals.resize(new_size);
650 VLOG(1) <<
"Self arc! This could be presolved. " <<
"var:" <<
tail
651 <<
" offset:" << offset <<
" offset_var:" << offset_var
652 <<
" conditioned_by:" << presence_literals;
666 if (!enforcement_literals.empty()) {
667 const OptionalArcIndex arc_index(potential_arcs_.size());
669 {
tail,
head, offset, offset_var, enforcement_literals});
673 impacted_potential_arcs_[offset_var].
push_back(arc_index);
679 IntegerVariable tail_var;
680 IntegerVariable head_var;
681 IntegerVariable offset_var;
683 std::vector<InternalArc> to_add;
691 to_add.push_back({
tail,
head, offset_var});
692 to_add.push_back({offset_var,
head,
tail});
700 for (
const InternalArc
a : to_add) {
715 modified_vars_.
Set(
a.tail_var);
719 const ArcIndex arc_index(arcs_.size());
721 {
a.tail_var,
a.head_var, offset,
a.offset_var, enforcement_literals});
722 auto& presence_literals = arcs_.back().presence_literals;
724 if (presence_literals.empty()) {
725 impacted_arcs_[
a.tail_var].
push_back(arc_index);
727 for (
const Literal l : presence_literals) {
728 if (l.Index() >= literal_to_new_impacted_arcs_.size()) {
729 literal_to_new_impacted_arcs_.
resize(l.Index().value() + 1);
731 literal_to_new_impacted_arcs_[l.Index()].
push_back(arc_index);
736 arc_counts_.
push_back(presence_literals.size());
739 for (
const Literal l : presence_literals) {
741 ++arc_counts_.back();
744 CHECK(presence_literals.empty() || arc_counts_.back() > 0);
751 IntegerValue offset) {
753 if (i1 < impacted_arcs_.size() && i2 < impacted_arcs_.size()) {
756 if (
arc.head_var == i2) {
757 const IntegerValue current = ArcOffset(
arc);
758 if (offset <= current) {
777void PrecedencesPropagator::PropagateOptionalArcs(
Trail* trail) {
780 if (
var >= impacted_potential_arcs_.size())
continue;
784 for (
const OptionalArcIndex arc_index : impacted_potential_arcs_[
var]) {
785 const ArcInfo&
arc = potential_arcs_[arc_index];
786 int num_not_true = 0;
788 for (
const Literal l :
arc.presence_literals) {
794 if (num_not_true != 1)
continue;
799 const IntegerValue tail_lb = integer_trail_->
LowerBound(
arc.tail_var);
800 const IntegerValue head_ub = integer_trail_->
UpperBound(
arc.head_var);
801 if (tail_lb + ArcOffset(
arc) > head_ub) {
802 integer_reason_.clear();
803 integer_reason_.push_back(
805 integer_reason_.push_back(
807 AppendLowerBoundReasonIfValid(
arc.offset_var, *integer_trail_,
809 literal_reason_.clear();
810 for (
const Literal l :
arc.presence_literals) {
811 if (l != to_propagate) literal_reason_.push_back(l.Negated());
813 ++num_enforcement_pushes_;
821IntegerValue PrecedencesPropagator::ArcOffset(
const ArcInfo&
arc)
const {
827bool PrecedencesPropagator::EnqueueAndCheck(
const ArcInfo&
arc,
828 IntegerValue new_head_lb,
831 DCHECK_GT(new_head_lb, integer_trail_->
LowerBound(
arc.head_var));
838 literal_reason_.clear();
839 for (
const Literal l :
arc.presence_literals) {
840 literal_reason_.push_back(l.Negated());
843 integer_reason_.clear();
845 AppendLowerBoundReasonIfValid(
arc.offset_var, *integer_trail_,
856 if (new_head_lb > integer_trail_->
UpperBound(
arc.head_var)) {
857 const IntegerValue slack =
859 integer_reason_.push_back(
861 std::vector<IntegerValue> coeffs(integer_reason_.size(), IntegerValue(1));
863 return integer_trail_->
ReportConflict(literal_reason_, integer_reason_);
866 return integer_trail_->
Enqueue(
868 literal_reason_, integer_reason_);
871bool PrecedencesPropagator::NoPropagationLeft(
const Trail& trail)
const {
872 const int num_nodes = impacted_arcs_.size();
873 for (IntegerVariable
var(0);
var < num_nodes; ++
var) {
874 for (
const ArcIndex arc_index : impacted_arcs_[
var]) {
875 const ArcInfo&
arc = arcs_[arc_index];
885void PrecedencesPropagator::InitializeBFQueueWithModifiedNodes() {
888 const int num_nodes = impacted_arcs_.size();
889 bf_in_queue_.resize(num_nodes,
false);
890 for (
const int node : bf_queue_) bf_in_queue_[node] =
false;
892 DCHECK(std::none_of(bf_in_queue_.begin(), bf_in_queue_.end(),
893 [](
bool v) { return v; }));
895 if (
var >= num_nodes)
continue;
896 bf_queue_.push_back(
var.value());
897 bf_in_queue_[
var.value()] =
true;
901void PrecedencesPropagator::CleanUpMarkedArcsAndParents() {
904 const int num_nodes = impacted_arcs_.size();
906 if (
var >= num_nodes)
continue;
907 const ArcIndex parent_arc_index = bf_parent_arc_of_[
var.value()];
908 if (parent_arc_index != -1) {
909 arcs_[parent_arc_index].is_marked =
false;
910 bf_parent_arc_of_[
var.value()] = -1;
911 bf_can_be_skipped_[
var.value()] =
false;
914 DCHECK(std::none_of(bf_parent_arc_of_.begin(), bf_parent_arc_of_.end(),
915 [](
ArcIndex v) { return v != -1; }));
916 DCHECK(std::none_of(bf_can_be_skipped_.begin(), bf_can_be_skipped_.end(),
917 [](
bool v) { return v; }));
920bool PrecedencesPropagator::DisassembleSubtree(
921 int source,
int target, std::vector<bool>* can_be_skipped) {
925 tmp_vector_.push_back(source);
926 while (!tmp_vector_.empty()) {
927 const int tail = tmp_vector_.back();
928 tmp_vector_.pop_back();
929 for (
const ArcIndex arc_index : impacted_arcs_[IntegerVariable(
tail)]) {
930 const ArcInfo&
arc = arcs_[arc_index];
932 arc.is_marked =
false;
933 if (
arc.head_var.value() == target)
return true;
934 DCHECK(!(*can_be_skipped)[
arc.head_var.value()]);
935 (*can_be_skipped)[
arc.head_var.value()] =
true;
936 tmp_vector_.push_back(
arc.head_var.value());
943void PrecedencesPropagator::AnalyzePositiveCycle(
944 ArcIndex first_arc, Trail* trail, std::vector<Literal>* must_be_all_true,
945 std::vector<Literal>* literal_reason,
946 std::vector<IntegerLiteral>* integer_reason) {
947 must_be_all_true->clear();
948 literal_reason->clear();
949 integer_reason->clear();
952 const IntegerVariable first_arc_head = arcs_[first_arc].head_var;
954 std::vector<ArcIndex> arc_on_cycle;
960 const int num_nodes = impacted_arcs_.size();
961 while (arc_on_cycle.size() <= num_nodes) {
962 arc_on_cycle.push_back(arc_index);
963 const ArcInfo&
arc = arcs_[arc_index];
964 if (
arc.tail_var == first_arc_head)
break;
965 arc_index = bf_parent_arc_of_[
arc.tail_var.value()];
968 CHECK_NE(arc_on_cycle.size(), num_nodes + 1) <<
"Infinite loop.";
972 for (
const ArcIndex arc_index : arc_on_cycle) {
973 const ArcInfo&
arc = arcs_[arc_index];
974 sum += ArcOffset(
arc);
975 AppendLowerBoundReasonIfValid(
arc.offset_var, *integer_trail_,
977 for (
const Literal l :
arc.presence_literals) {
978 literal_reason->push_back(l.Negated());
992bool PrecedencesPropagator::BellmanFordTarjan(Trail* trail) {
993 const int num_nodes = impacted_arcs_.size();
996 bf_can_be_skipped_.resize(num_nodes,
false);
997 bf_parent_arc_of_.resize(num_nodes,
ArcIndex(-1));
999 ::absl::MakeCleanup([
this]() { CleanUpMarkedArcsAndParents(); });
1002 while (!bf_queue_.empty()) {
1003 const int node = bf_queue_.front();
1004 bf_queue_.pop_front();
1005 bf_in_queue_[node] =
false;
1015 if (bf_can_be_skipped_[node]) {
1016 DCHECK_NE(bf_parent_arc_of_[node], -1);
1017 DCHECK(!arcs_[bf_parent_arc_of_[node]].is_marked);
1021 const IntegerValue tail_lb =
1022 integer_trail_->
LowerBound(IntegerVariable(node));
1023 for (
const ArcIndex arc_index : impacted_arcs_[IntegerVariable(node)]) {
1024 const ArcInfo&
arc = arcs_[arc_index];
1025 DCHECK_EQ(
arc.tail_var, node);
1026 const IntegerValue candidate = tail_lb + ArcOffset(
arc);
1028 if (!EnqueueAndCheck(
arc, candidate, trail))
return false;
1036 if (DisassembleSubtree(
arc.head_var.value(),
arc.tail_var.value(),
1037 &bf_can_be_skipped_)) {
1038 std::vector<Literal> must_be_all_true;
1039 AnalyzePositiveCycle(arc_index, trail, &must_be_all_true,
1040 &literal_reason_, &integer_reason_);
1041 if (must_be_all_true.empty()) {
1047 for (
const Literal l : must_be_all_true) {
1049 literal_reason_.push_back(l);
1054 for (
const Literal l : must_be_all_true) {
1069 if (bf_parent_arc_of_[
arc.head_var.value()] != -1) {
1070 arcs_[bf_parent_arc_of_[
arc.head_var.value()]].is_marked =
false;
1079 const IntegerValue new_bound = integer_trail_->
LowerBound(
arc.head_var);
1080 if (new_bound == candidate) {
1081 bf_parent_arc_of_[
arc.head_var.value()] = arc_index;
1082 arcs_[arc_index].is_marked =
true;
1086 bf_parent_arc_of_[
arc.head_var.value()] = -1;
1091 bf_can_be_skipped_[
arc.head_var.value()] =
false;
1092 if (!bf_in_queue_[
arc.head_var.value()] && new_bound >= candidate) {
1093 bf_queue_.push_back(
arc.head_var.value());
1094 bf_in_queue_[
arc.head_var.value()] =
true;
1106 r.enforcement =
lit;
1122 relations_.push_back(std::move(r));
1125bool GreaterThanAtLeastOneOfDetector::AddRelationFromIndices(
1126 IntegerVariable
var, absl::Span<const Literal> clause,
1127 absl::Span<const int> indices,
Model*
model) {
1128 std::vector<AffineExpression> exprs;
1129 std::vector<Literal> selectors;
1130 absl::flat_hash_set<LiteralIndex> used;
1134 for (
const int index : indices) {
1135 Relation r = relations_[
index];
1146 exprs.push_back(AffineExpression(r.b.var, r.b.coeff, -r.rhs));
1150 if (var_lb >= integer_trail->LevelZeroUpperBound(exprs.back())) {
1156 selectors.push_back(r.enforcement);
1157 used.insert(r.enforcement);
1162 std::vector<Literal> enforcements;
1163 for (
const Literal l : clause) {
1164 if (!used.contains(l.Index())) {
1165 enforcements.push_back(l.Negated());
1171 if (used.size() <= 1)
return false;
1174 GreaterThanAtLeastOneOfPropagator* constraint =
1175 new GreaterThanAtLeastOneOfPropagator(
var, exprs, selectors, enforcements,
1177 constraint->RegisterWith(
model->GetOrCreate<GenericLiteralWatcher>());
1178 model->TakeOwnership(constraint);
1182int GreaterThanAtLeastOneOfDetector::
1183 AddGreaterThanAtLeastOneOfConstraintsFromClause(
1184 const absl::Span<const Literal> clause, Model*
model) {
1185 CHECK_EQ(
model->GetOrCreate<Trail>()->CurrentDecisionLevel(), 0);
1186 if (clause.size() < 2)
return 0;
1189 std::vector<std::pair<IntegerVariable, int>> infos;
1190 for (
const Literal l : clause) {
1191 if (l.Index() >= lit_to_relations_->size())
continue;
1192 for (
const int index : (*lit_to_relations_)[l.Index()]) {
1195 infos.push_back({r.a.var,
index});
1198 infos.push_back({r.b.var,
index});
1202 if (infos.size() <= 1)
return 0;
1205 std::stable_sort(infos.begin(), infos.end());
1208 int num_added_constraints = 0;
1209 std::vector<int> indices;
1210 for (
int i = 0;
i < infos.size();) {
1212 const IntegerVariable
var = infos[
start].first;
1215 for (;
i < infos.size() && infos[
i].first ==
var; ++
i) {
1216 indices.push_back(infos[
i].second);
1220 if (indices.size() < 2)
continue;
1226 if (indices.size() + 1 < clause.size())
continue;
1228 if (AddRelationFromIndices(
var, clause, indices,
model)) {
1229 ++num_added_constraints;
1232 ++num_added_constraints;
1235 return num_added_constraints;
1238int GreaterThanAtLeastOneOfDetector::
1239 AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(Model*
model) {
1241 auto* solver =
model->GetOrCreate<SatSolver>();
1248 if (r.a.var >= var_to_relations.size()) {
1249 var_to_relations.resize(r.a.var + 1);
1251 var_to_relations[r.a.var].push_back(
index);
1254 if (r.b.var >= var_to_relations.size()) {
1255 var_to_relations.resize(r.b.var + 1);
1257 var_to_relations[r.b.var].push_back(
index);
1261 int num_added_constraints = 0;
1262 for (IntegerVariable target(0); target < var_to_relations.size(); ++target) {
1263 if (var_to_relations[target].
size() <= 1)
continue;
1264 if (
time_limit->LimitReached())
return num_added_constraints;
1269 solver->Backtrack(0);
1270 if (solver->ModelIsUnsat())
return num_added_constraints;
1271 std::vector<Literal> clause;
1272 for (
const int index : var_to_relations[target]) {
1274 if (solver->Assignment().LiteralIsFalse(
literal))
continue;
1276 solver->EnqueueDecisionAndBacktrackOnConflict(
literal.Negated());
1280 clause = solver->GetLastIncompatibleDecisions();
1281 for (Literal& ref : clause) ref = ref.Negated();
1285 solver->Backtrack(0);
1286 if (clause.size() <= 1)
continue;
1289 const absl::btree_set<Literal> clause_set(clause.begin(), clause.end());
1291 std::vector<int> indices;
1292 for (
const int index : var_to_relations[target]) {
1294 if (clause_set.contains(
literal)) {
1295 indices.push_back(
index);
1300 if (AddRelationFromIndices(target, clause, indices,
model)) {
1301 ++num_added_constraints;
1303 if (AddRelationFromIndices(
NegationOf(target), clause, indices,
model)) {
1304 ++num_added_constraints;
1308 solver->Backtrack(0);
1309 return num_added_constraints;
1319 int num_added_constraints = 0;
1320 SOLVER_LOG(logger,
"[Precedences] num_relations=", relations_.size(),
1321 " num_clauses=", clauses->AllClausesInCreationOrder().size());
1325 std::vector<LiteralIndex> keys;
1326 const int num_relations = relations_.size();
1327 keys.reserve(num_relations);
1328 for (
int i = 0;
i < num_relations; ++
i) {
1329 keys.push_back(relations_[
i].enforcement.Index());
1332 std::make_unique<CompactVectorVector<LiteralIndex, int>>();
1342 if (!auto_detect_clauses &&
1343 clauses->AllClausesInCreationOrder().size() < 1e6) {
1351 for (
const SatClause* clause : clauses->AllClausesInCreationOrder()) {
1352 if (
time_limit->LimitReached())
return num_added_constraints;
1353 if (solver->ModelIsUnsat())
return num_added_constraints;
1354 num_added_constraints += AddGreaterThanAtLeastOneOfConstraintsFromClause(
1355 clause->AsSpan(),
model);
1362 const int num_booleans = solver->NumVariables();
1363 if (num_booleans < 1e6) {
1364 for (
int i = 0;
i < num_booleans; ++
i) {
1365 if (
time_limit->LimitReached())
return num_added_constraints;
1366 if (solver->ModelIsUnsat())
return num_added_constraints;
1367 num_added_constraints +=
1368 AddGreaterThanAtLeastOneOfConstraintsFromClause(
1369 {
Literal(BooleanVariable(
i),
true),
1370 Literal(BooleanVariable(
i),
false)},
1376 num_added_constraints +=
1377 AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(
model);
1380 if (num_added_constraints > 0) {
1381 SOLVER_LOG(logger,
"[Precedences] Added ", num_added_constraints,
1382 " GreaterThanAtLeastOneOf() constraints.");
1386 lit_to_relations_.reset(
nullptr);
1388 return num_added_constraints;
void ClearAndResize(IntegerType size)
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
void Set(IntegerType index)
void WatchLowerBound(IntegerVariable var, int id, int watch_index=-1)
int AddGreaterThanAtLeastOneOfConstraints(Model *model, bool auto_detect_clauses=false)
void Add(Literal lit, LinearTerm a, LinearTerm b, IntegerValue lhs, IntegerValue rhs)
Adds a relation lit => a + b \in [lhs, rhs].
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
void RelaxLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, std::vector< IntegerLiteral > *reason) const
IntegerValue UpperBound(IntegerVariable i) const
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
IntegerVariable NumIntegerVariables() const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
void EnqueueLiteral(Literal literal, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Returns globally valid lower/upper bound on the given integer variable.
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit)
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_
void AddStats(absl::Span< const std::pair< std::string, int64_t > > stats)
Adds a bunch of stats, adding count for the same key together.
int CurrentDecisionLevel() const
const VariablesAssignment & Assignment() const
bool LiteralIsFalse(Literal literal) const
bool LiteralIsTrue(Literal literal) const
NodeIndexType num_nodes() const
ArcIndexType num_arcs() const
Returns the number of valid arcs in the graph.
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)
BeginEndWrapper< OutgoingArcIterator > OutgoingArcs(NodeIndexType node) const
NodeIndexType Tail(ArcIndexType arc) const
void ReserveArcs(ArcIndexType bound) override
void AddNode(NodeIndexType node)
NodeIndexType Head(ArcIndexType arc) const
void AddEdge(int from, int to)
bool GetNext(int *next_node_index, bool *cyclic, std::vector< int > *output_cycle_nodes=nullptr)
value_type * data()
– Pass-through methods to STL vector ----------------------------------—
void push_back(const value_type &val)
void resize(size_type new_size)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void STLClearObject(T *obj)
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(const std::vector< 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)
std::vector< IntegerValue > offsets
std::vector< int > indices
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
#define SOLVER_LOG(logger,...)