26#include "absl/memory/memory.h"
27#include "absl/strings/str_cat.h"
28#include "absl/types/span.h"
33#include "ortools/sat/cp_model.pb.h"
46 num_vars_with_negation_ = 2 * num_variables;
47 partition_ = absl::make_unique<DynamicPartition>(num_vars_with_negation_);
49 can_freely_decrease_.
assign(num_vars_with_negation_,
true);
51 shared_buffer_.clear();
52 initial_candidates_.
assign(num_vars_with_negation_, IntegerVariableSpan());
55 dominating_vars_.
assign(num_vars_with_negation_, IntegerVariableSpan());
57 ct_index_for_signature_ = 0;
58 block_down_signatures_.
assign(num_vars_with_negation_, 0);
61void VarDomination::RefinePartition(std::vector<int>* vars) {
62 if (vars->empty())
return;
63 partition_->Refine(*vars);
64 for (
int&
var : *vars) {
65 const IntegerVariable wrapped(
var);
66 can_freely_decrease_[wrapped] =
false;
67 can_freely_decrease_[
NegationOf(wrapped)] =
false;
70 partition_->Refine(*vars);
74 if (phase_ != 0)
return;
76 for (
const int ref : refs) {
79 RefinePartition(&tmp_vars_);
84 absl::Span<const int64_t> coeffs) {
85 if (phase_ != 0)
return;
86 FillTempRanks(
false, {}, refs,
89 for (
int i = 0; i < tmp_ranks_.size(); ++i) {
90 if (i > 0 && tmp_ranks_[i].rank != tmp_ranks_[i - 1].rank) {
91 RefinePartition(&tmp_vars_);
94 tmp_vars_.push_back(tmp_ranks_[i].
var.value());
96 RefinePartition(&tmp_vars_);
101void VarDomination::ProcessTempRanks() {
105 ++ct_index_for_signature_;
106 for (IntegerVariableWithRank& entry : tmp_ranks_) {
107 can_freely_decrease_[entry.var] =
false;
108 block_down_signatures_[entry.var] |= uint64_t{1}
109 << (ct_index_for_signature_ % 64);
110 entry.part = partition_->PartOf(entry.var.value());
113 tmp_ranks_.begin(), tmp_ranks_.end(),
114 [](
const IntegerVariableWithRank&
a,
const IntegerVariableWithRank&
b) {
115 return a.part < b.part;
118 for (
int i = 1; i < tmp_ranks_.size(); ++i) {
119 if (tmp_ranks_[i].part != tmp_ranks_[
start].part) {
120 Initialize({&tmp_ranks_[
start],
static_cast<size_t>(i -
start)});
124 if (
start < tmp_ranks_.size()) {
125 Initialize({&tmp_ranks_[
start], tmp_ranks_.size() -
start});
127 }
else if (phase_ == 1) {
128 FilterUsingTempRanks();
131 CheckUsingTempRanks();
136 absl::Span<const int> enforcements, absl::Span<const int> refs,
137 absl::Span<const int64_t> coeffs) {
138 FillTempRanks(
false, enforcements, refs, coeffs);
143 absl::Span<const int> enforcements, absl::Span<const int> refs,
144 absl::Span<const int64_t> coeffs) {
145 FillTempRanks(
true, enforcements, refs, coeffs);
149void VarDomination::MakeRankEqualToStartOfPart(
150 absl::Span<IntegerVariableWithRank> span) {
151 const int size = span.size();
153 int previous_value = 0;
154 for (
int i = 0; i < size; ++i) {
155 const int64_t
value = span[i].rank;
156 if (
value != previous_value) {
157 previous_value =
value;
160 span[i].rank =
start;
164void VarDomination::Initialize(absl::Span<IntegerVariableWithRank> span) {
167 MakeRankEqualToStartOfPart(span);
169 const int future_start = shared_buffer_.size();
170 int first_start = -1;
174 const int kSizeThreshold = 1000;
175 const int size = span.size();
176 for (
int i =
std::max(0, size - kSizeThreshold); i < size; ++i) {
177 const IntegerVariableWithRank entry = span[i];
178 const int num_candidates = size - entry.rank;
179 if (num_candidates >= kSizeThreshold)
continue;
182 int size_threshold = kSizeThreshold;
185 const int var_part = partition_->PartOf(entry.var.value());
186 const int part_size = partition_->SizeOfPart(var_part);
187 size_threshold =
std::min(size_threshold, part_size);
190 const int current_num_candidates = initial_candidates_[entry.var].
size;
191 if (current_num_candidates != 0) {
192 size_threshold =
std::min(size_threshold, current_num_candidates);
195 if (num_candidates < size_threshold) {
196 if (first_start == -1) first_start = entry.rank;
197 initial_candidates_[entry.var] = {
198 future_start - first_start +
static_cast<int>(entry.rank),
204 if (first_start == -1)
return;
205 for (
int i = first_start; i < size; ++i) {
206 shared_buffer_.push_back(span[i].
var);
222 const int kMaxInitialSize = 50;
223 std::vector<IntegerVariable> cropped_lists;
228 for (IntegerVariable
var(0);
var < num_vars_with_negation_; ++
var) {
229 if (can_freely_decrease_[
var])
continue;
230 const int part = partition_->PartOf(
var.value());
231 const int part_size = partition_->SizeOfPart(part);
233 const int start = buffer_.size();
236 const uint64_t var_sig = block_down_signatures_[
var];
237 const uint64_t not_var_sig = block_down_signatures_[
NegationOf(
var)];
238 const int stored_size = initial_candidates_[
var].
size;
239 if (stored_size == 0 || part_size < stored_size) {
243 for (
const int value : partition_->ElementsInPart(part)) {
244 const IntegerVariable c = IntegerVariable(
value);
249 if (++num_tested > 1000) {
250 is_cropped[
var] =
true;
252 int extra = new_size;
253 while (extra < kMaxInitialSize) {
260 if (can_freely_decrease_[
NegationOf(c)])
continue;
261 if (var_sig & ~block_down_signatures_[c])
continue;
262 if (block_down_signatures_[
NegationOf(c)] & ~not_var_sig)
continue;
264 buffer_.push_back(c);
268 if (new_size > kMaxInitialSize) {
269 is_cropped[
var] =
true;
279 for (
const IntegerVariable c : InitialDominatingCandidates(
var)) {
281 if (can_freely_decrease_[
NegationOf(c)])
continue;
282 if (partition_->PartOf(c.value()) != part)
continue;
283 if (var_sig & ~block_down_signatures_[c])
continue;
284 if (block_down_signatures_[
NegationOf(c)] & ~not_var_sig)
continue;
286 buffer_.push_back(c);
290 if (new_size > kMaxInitialSize) {
291 is_cropped[
var] =
true;
298 dominating_vars_[
var] = {
start, new_size};
305 for (
const IntegerVariable
var : cropped_lists) {
306 if (kMaxInitialSize / 2 < dominating_vars_[
var].size) {
307 dominating_vars_[
var].
size = kMaxInitialSize / 2;
310 for (IntegerVariable
var(0);
var < num_vars_with_negation_; ++
var) {
313 IntegerVariableSpan& s = dominating_vars_[
NegationOf(dom)];
314 if (s.size >= kMaxInitialSize)
continue;
323 for (
const IntegerVariable
var : cropped_lists) {
324 if (!is_cropped[
var])
continue;
325 IntegerVariableSpan& s = dominating_vars_[
var];
326 std::sort(&buffer_[s.start], &buffer_[s.start + s.size]);
327 const auto p = std::unique(&buffer_[s.start], &buffer_[s.start + s.size]);
328 s.size = p - &buffer_[s.start];
332 VLOG(1) <<
"Num initial list that where cropped: " << cropped_lists.size();
333 VLOG(1) <<
"Shared buffer size: " << shared_buffer_.size();
334 VLOG(1) <<
"Buffer size: " << buffer_.size();
344 shared_buffer_.clear();
345 initial_candidates_.
assign(num_vars_with_negation_, IntegerVariableSpan());
348 for (IntegerVariable
var(0);
var < num_vars_with_negation_; ++
var) {
356 for (IntegerVariable
var(0);
var < num_vars_with_negation_; ++
var) {
357 initial_candidates_[
var].start =
start;
359 initial_candidates_[
var].
size = 0;
361 shared_buffer_.resize(
start);
364 for (IntegerVariable
var(0);
var < num_vars_with_negation_; ++
var) {
366 IntegerVariableSpan& span = initial_candidates_[
NegationOf(dom)];
373 tmp_var_to_rank_.
resize(num_vars_with_negation_, -1);
374 for (IntegerVariable
var(0);
var < num_vars_with_negation_; ++
var) {
375 for (
const IntegerVariable dom : InitialDominatingCandidates(
var)) {
376 tmp_var_to_rank_[dom] = 1;
380 IntegerVariableSpan& span = dominating_vars_[
var];
382 if (tmp_var_to_rank_[dom] != 1) {
386 buffer_[span.start + new_size++] = dom;
388 span.size = new_size;
390 for (
const IntegerVariable dom : InitialDominatingCandidates(
var)) {
391 tmp_var_to_rank_[dom] = -1;
395 VLOG(1) <<
"Transpose removed " << num_removed;
400void VarDomination::FillTempRanks(
bool reverse_references,
401 absl::Span<const int> enforcements,
402 absl::Span<const int> refs,
403 absl::Span<const int64_t> coeffs) {
405 if (coeffs.empty()) {
407 for (
const int ref : refs) {
408 const IntegerVariable
var =
410 tmp_ranks_.push_back({
var, 0, 0});
414 for (
int i = 0; i < refs.size(); ++i) {
415 if (coeffs[i] == 0)
continue;
417 reverse_references ?
NegatedRef(refs[i]) : refs[i]);
419 tmp_ranks_.push_back({
var, 0, coeffs[i]});
424 std::sort(tmp_ranks_.begin(), tmp_ranks_.end());
425 MakeRankEqualToStartOfPart({&tmp_ranks_[0], tmp_ranks_.size()});
431 const int enforcement_rank = tmp_ranks_.size();
432 for (
const int ref : enforcements) {
433 tmp_ranks_.push_back(
440void VarDomination::FilterUsingTempRanks() {
442 tmp_var_to_rank_.
resize(num_vars_with_negation_, -1);
443 for (
const IntegerVariableWithRank entry : tmp_ranks_) {
444 tmp_var_to_rank_[entry.var] = entry.rank;
448 for (
const IntegerVariableWithRank entry : tmp_ranks_) {
456 IntegerVariableSpan& span = dominating_vars_[entry.var];
457 if (span.size == 0)
continue;
460 if (tmp_var_to_rank_[candidate] < entry.rank)
continue;
461 buffer_[span.start + new_size++] = candidate;
463 span.size = new_size;
468 for (
const IntegerVariableWithRank entry : tmp_ranks_) {
469 tmp_var_to_rank_[entry.var] = -1;
474void VarDomination::CheckUsingTempRanks() {
475 tmp_var_to_rank_.
resize(num_vars_with_negation_, -1);
476 for (
const IntegerVariableWithRank entry : tmp_ranks_) {
477 tmp_var_to_rank_[entry.var] = entry.rank;
481 for (IntegerVariable
var(0);
var < num_vars_with_negation_; ++
var) {
482 const int var_rank = tmp_var_to_rank_[
var];
483 const int negated_var_rank = tmp_var_to_rank_[
NegationOf(
var)];
489 CHECK_LE(var_rank, tmp_var_to_rank_[dom]);
494 for (
const IntegerVariableWithRank entry : tmp_ranks_) {
495 tmp_var_to_rank_[entry.var] = -1;
504 return can_freely_decrease_[
var];
507absl::Span<const IntegerVariable> VarDomination::InitialDominatingCandidates(
508 IntegerVariable
var)
const {
509 const IntegerVariableSpan span = initial_candidates_[
var];
510 if (span.size == 0)
return absl::Span<const IntegerVariable>();
511 return absl::Span<const IntegerVariable>(&shared_buffer_[span.start],
521 IntegerVariable
var)
const {
522 const IntegerVariableSpan span = dominating_vars_[
var];
523 if (span.size == 0)
return absl::Span<const IntegerVariable>();
524 return absl::Span<const IntegerVariable>(&buffer_[span.start], span.size);
542 for (
const int ref : refs) {
543 const IntegerVariable
var = RefToIntegerVariable(ref);
546 locking_ct_index_[
var] = ct_index;
552 for (
const int ref : refs) {
553 const IntegerVariable
var = RefToIntegerVariable(ref);
561 for (
const int ref : refs) {
562 const IntegerVariable
var = RefToIntegerVariable(ref);
570template <
typename LinearProto>
573 const LinearProto& linear, int64_t min_activity, int64_t max_activity) {
574 const int64_t lb_limit = linear.domain(linear.domain_size() - 2);
575 const int64_t ub_limit = linear.domain(1);
576 const int num_terms = linear.vars_size();
577 for (
int i = 0; i < num_terms; ++i) {
578 int ref = linear.vars(i);
579 int64_t
coeff = linear.coeffs(i);
587 const int64_t term_diff = max_term - min_term;
588 const IntegerVariable
var = RefToIntegerVariable(ref);
591 if (min_activity < lb_limit) {
593 if (min_activity + term_diff < lb_limit) {
596 const IntegerValue slack(lb_limit - min_activity);
597 const IntegerValue var_diff =
599 can_freely_decrease_until_[
var] =
601 IntegerValue(
context.MinOf(ref)) + var_diff);
614 if (max_activity > ub_limit) {
616 if (max_activity - term_diff > ub_limit) {
619 const IntegerValue slack(max_activity - ub_limit);
620 const IntegerValue var_diff =
624 -IntegerValue(
context.MaxOf(ref)) + var_diff);
631 const CpModelProto& cp_model = *
context->working_model;
632 const int num_vars = cp_model.variables_size();
633 for (
int var = 0;
var < num_vars; ++
var) {
639 if (ub_limit == lb) {
640 context->UpdateRuleStats(
"dual: fix variable");
647 const int64_t lb_limit =
649 if (lb_limit == ub) {
650 context->UpdateRuleStats(
"dual: fix variable");
657 if (lb_limit > ub_limit) {
667 context->UpdateRuleStats(
"dual: fix variable with multiple choices");
675 if (lb_limit > lb || ub_limit < ub) {
676 const int64_t new_ub =
683 const int64_t new_lb =
690 context->UpdateRuleStats(
"dual: reduced domain");
700 std::vector<bool> processed(num_vars,
false);
701 for (
int positive_ref = 0; positive_ref < num_vars; ++positive_ref) {
702 if (processed[positive_ref])
continue;
703 if (
context->IsFixed(positive_ref))
continue;
704 const IntegerVariable
var = RefToIntegerVariable(positive_ref);
706 if (num_locks_[
var] == 1 && locking_ct_index_[
var] != -1) {
707 ct_index = locking_ct_index_[
var];
714 const ConstraintProto&
ct =
context->working_model->constraints(ct_index);
715 if (
ct.constraint_case() == ConstraintProto::kAtMostOne) {
716 context->UpdateRuleStats(
"TODO dual: tighten at most one");
720 if (
ct.constraint_case() != ConstraintProto::kBoolAnd)
continue;
721 if (
ct.enforcement_literal().size() != 1)
continue;
726 int a =
ct.enforcement_literal(0);
729 num_locks_[RefToIntegerVariable(
NegatedRef(
a))] == 1) {
732 if (
ct.bool_and().literals().size() != 1)
continue;
733 b =
ct.bool_and().literals(0);
737 for (
const int lhs :
ct.bool_and().literals()) {
739 num_locks_[RefToIntegerVariable(lhs)] == 1) {
751 context->StoreBooleanEqualityRelation(
a,
b);
752 context->UpdateRuleStats(
"dual: enforced equivalence");
761template <
typename LinearExprProto>
763 const LinearExprProto&
proto, int64_t* min_activity,
764 int64_t* max_activity) {
767 const int num_vars =
proto.vars().size();
768 for (
int i = 0; i < num_vars; ++i) {
781 const CpModelProto& cp_model = *
context.working_model;
782 const int num_vars = cp_model.variables().size();
783 var_domination->
Reset(num_vars);
784 dual_bound_strengthening->
Reset(num_vars);
789 for (
int var = 0;
var < num_vars; ++
var) {
801 }
else if (r.
coeff == -1) {
821 std::vector<int> tmp;
822 const int num_constraints = cp_model.constraints_size();
824 for (
int c = 0; c < num_constraints; ++c) {
825 const ConstraintProto&
ct = cp_model.constraints(c);
829 switch (
ct.constraint_case()) {
830 case ConstraintProto::kBoolOr:
835 ct.bool_or().literals(),
838 case ConstraintProto::kBoolAnd:
853 for (
const int ref :
ct.enforcement_literal()) {
856 for (
const int ref :
ct.bool_and().literals()) {
863 case ConstraintProto::kAtMostOne:
866 ct.at_most_one().literals(), c);
869 ct.at_most_one().literals(),
872 case ConstraintProto::kExactlyOne:
874 dual_bound_strengthening->
CannotMove(
ct.exactly_one().literals());
879 case ConstraintProto::kLinear: {
880 FillMinMaxActivity(
context,
ct.linear(), &min_activity,
884 false,
context,
ct.linear(), min_activity, max_activity);
886 const bool domain_is_simple =
ct.linear().domain().size() == 2;
887 const bool free_to_increase =
888 domain_is_simple &&
ct.linear().domain(1) >= max_activity;
889 const bool free_to_decrease =
890 domain_is_simple &&
ct.linear().domain(0) <= min_activity;
891 if (free_to_decrease && free_to_increase)
break;
892 if (free_to_increase) {
895 ct.linear().coeffs());
896 }
else if (free_to_decrease) {
899 ct.linear().coeffs());
902 if (!
ct.enforcement_literal().empty()) {
904 {},
ct.enforcement_literal(), {});
907 ct.linear().coeffs());
917 for (
const int var :
context.ConstraintToVars(c)) {
926 if (cp_model.has_objective()) {
930 context.WriteObjectiveToProto();
932 FillMinMaxActivity(
context, cp_model.objective(), &min_activity,
934 const auto& domain = cp_model.objective().domain();
935 if (
phase == 0 && !domain.empty()) {
937 true,
context, cp_model.objective(), min_activity, max_activity);
939 if (domain.empty() || (domain.size() == 2 && domain[0] <= min_activity)) {
941 {}, cp_model.objective().vars(),
942 cp_model.objective().coeffs());
945 cp_model.objective().coeffs());
954 int64_t num_unconstrained_refs = 0;
955 int64_t num_dominated_refs = 0;
956 int64_t num_dominance_relations = 0;
957 for (
int var = 0;
var < num_vars; ++
var) {
962 num_unconstrained_refs++;
964 num_dominated_refs++;
965 num_dominance_relations +=
970 if (num_unconstrained_refs == 0 && num_dominated_refs == 0)
return;
971 VLOG(1) <<
"Dominance:"
972 <<
" num_unconstrained_refs=" << num_unconstrained_refs
973 <<
" num_dominated_refs=" << num_dominated_refs
974 <<
" num_dominance_relations=" << num_dominance_relations;
979 const CpModelProto& cp_model = *
context->working_model;
980 const int num_vars = cp_model.variables_size();
983 bool work_to_do =
false;
984 for (
int var = 0;
var < num_vars; ++
var) {
992 if (!work_to_do)
return true;
998 const int num_constraints = cp_model.constraints_size();
999 for (
int c = 0; c < num_constraints; ++c) {
1000 const ConstraintProto&
ct = cp_model.constraints(c);
1002 if (
ct.constraint_case() == ConstraintProto::kBoolAnd) {
1003 if (
ct.enforcement_literal().size() != 1)
continue;
1004 const int a =
ct.enforcement_literal(0);
1006 for (
const int b :
ct.bool_and().literals()) {
1010 for (
const IntegerVariable ivar :
1014 context->UpdateRuleStats(
"domination: in implication");
1015 if (!
context->SetLiteralToFalse(
a))
return false;
1022 for (
const IntegerVariable ivar :
1026 context->UpdateRuleStats(
"domination: in implication");
1027 if (!
context->SetLiteralToTrue(
b))
return false;
1035 if (!
ct.enforcement_literal().empty())
continue;
1042 if (
ct.constraint_case() == ConstraintProto::kAtMostOne) {
1043 for (
const int ref :
ct.at_most_one().literals()) {
1046 for (
const int ref :
ct.at_most_one().literals()) {
1047 if (
context->IsFixed(ref))
continue;
1050 if (dominating_ivars.empty())
continue;
1051 for (
const IntegerVariable ivar : dominating_ivars) {
1052 if (!in_constraints[ivar])
continue;
1058 context->UpdateRuleStats(
"domination: in at most one");
1059 if (!
context->SetLiteralToFalse(ref))
return false;
1063 for (
const int ref :
ct.at_most_one().literals()) {
1068 if (
ct.constraint_case() != ConstraintProto::kLinear)
continue;
1070 int num_dominated = 0;
1071 for (
const int var :
context->ConstraintToVars(c)) {
1077 if (num_dominated == 0)
continue;
1080 int64_t min_activity = 0;
1081 int64_t max_activity = 0;
1082 const int num_terms =
ct.linear().vars_size();
1083 for (
int i = 0; i < num_terms; ++i) {
1084 int ref =
ct.linear().vars(i);
1085 int64_t
coeff =
ct.linear().coeffs(i);
1092 min_activity += min_term;
1093 max_activity += max_term;
1095 var_lb_to_ub_diff[ivar] = max_term - min_term;
1096 var_lb_to_ub_diff[
NegationOf(ivar)] = min_term - max_term;
1098 const int64_t rhs_lb =
ct.linear().domain(0);
1099 const int64_t rhs_ub =
ct.linear().domain(
ct.linear().domain_size() - 1);
1100 if (max_activity < rhs_lb || min_activity > rhs_ub) {
1101 return context->NotifyThatModelIsUnsat(
"linear equation unsat.");
1105 for (
int i = 0; i < num_terms; ++i) {
1106 const int ref =
ct.linear().vars(i);
1107 const int64_t
coeff =
ct.linear().coeffs(i);
1109 if (
context->IsFixed(ref))
continue;
1111 for (
const int current_ref : {ref,
NegatedRef(ref)}) {
1112 const absl::Span<const IntegerVariable> dominated_by =
1114 if (dominated_by.empty())
continue;
1116 const bool ub_side = (
coeff > 0) == (current_ref == ref);
1118 if (max_activity <= rhs_ub)
continue;
1120 if (min_activity >= rhs_lb)
continue;
1122 const int64_t slack =
1123 ub_side ? rhs_ub - min_activity : max_activity - rhs_lb;
1128 for (
const IntegerVariable ivar : dominated_by) {
1136 const int64_t lb =
context->MinOf(current_ref);
1138 context->UpdateRuleStats(
"domination: fixed to lb.");
1139 if (!
context->IntersectDomainWith(current_ref,
Domain(lb))) {
1144 const IntegerVariable current_var =
1147 CHECK_GE(var_lb_to_ub_diff[current_var], 0);
1148 max_activity -= var_lb_to_ub_diff[current_var];
1150 CHECK_LE(var_lb_to_ub_diff[current_var], 0);
1151 min_activity -= var_lb_to_ub_diff[current_var];
1153 var_lb_to_ub_diff[current_var] = 0;
1154 var_lb_to_ub_diff[
NegationOf(current_var)] = 0;
1161 int64_t new_ub = lb + diff.value();
1162 if (new_ub < context->MaxOf(current_ref)) {
1166 new_ub =
context->DomainOf(current_ref)
1171 if (new_ub < context->MaxOf(current_ref)) {
1172 context->UpdateRuleStats(
"domination: reduced ub.");
1173 if (!
context->IntersectDomainWith(current_ref,
Domain(lb, new_ub))) {
1178 const IntegerVariable current_var =
1181 CHECK_GE(var_lb_to_ub_diff[current_var], 0);
1182 max_activity -= var_lb_to_ub_diff[current_var];
1184 CHECK_LE(var_lb_to_ub_diff[current_var], 0);
1185 min_activity -= var_lb_to_ub_diff[current_var];
1189 var_lb_to_ub_diff[current_var] = new_diff;
1190 var_lb_to_ub_diff[
NegationOf(current_var)] = -new_diff;
1191 max_activity += new_diff;
1193 var_lb_to_ub_diff[current_var] = -new_diff;
1194 var_lb_to_ub_diff[
NegationOf(current_var)] = +new_diff;
1195 min_activity -= new_diff;
1202 for (
const int ref :
ct.linear().vars()) {
1204 var_lb_to_ub_diff[ivar] = 0;
1222 for (
int positive_ref = 0; positive_ref < num_vars; ++positive_ref) {
1223 if (
context->IsFixed(positive_ref))
continue;
1224 if (!
context->CanBeUsedAsLiteral(positive_ref))
continue;
1225 for (
const int ref : {positive_ref,
NegatedRef(positive_ref)}) {
1228 for (
const IntegerVariable dom :
1230 if (increase_is_forbidden[dom])
continue;
1232 if (
context->IsFixed(dom_ref))
continue;
1233 if (!
context->CanBeUsedAsLiteral(dom_ref))
continue;
1235 context->AddImplication(ref, dom_ref);
1238 increase_is_forbidden[
var] =
true;
1239 increase_is_forbidden[
NegationOf(dom)] =
true;
1243 if (num_added > 0) {
1244 VLOG(1) <<
"Added " << num_added <<
" domination implications.";
1245 context->UpdateNewConstraintsVariableUsage();
1246 context->UpdateRuleStats(
"domination: added implications", num_added);
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define CHECK_LE(val1, val2)
#define VLOG(verboselevel)
void assign(size_type n, const value_type &val)
void resize(size_type new_size)
void push_back(const value_type &x)
We call domain any subset of Int64 = [kint64min, kint64max].
bool Contains(int64_t value) const
Returns true iff value is in Domain.
std::vector< int64_t > FlattenedIntervals() const
This method returns the flattened list of interval bounds of the domain.
int64_t Min() const
Returns the min value of the domain.
bool IsEmpty() const
Returns true if this is the empty set.
bool Strengthen(PresolveContext *context)
void ProcessLinearConstraint(bool is_objective, const PresolveContext &context, const LinearProto &linear, int64_t min_activity, int64_t max_activity)
void Reset(int num_variables)
int64_t CanFreelyDecreaseUntil(int ref) const
void CannotMove(absl::Span< const int > refs)
void CannotIncrease(absl::Span< const int > refs, int ct_index=-1)
void CannotDecrease(absl::Span< const int > refs, int ct_index=-1)
void ActivityShouldNotIncrease(absl::Span< const int > enforcements, absl::Span< const int > refs, absl::Span< const int64_t > coeffs)
void ActivityShouldNotChange(absl::Span< const int > refs, absl::Span< const int64_t > coeffs)
bool CanFreelyDecrease(int ref) const
static int IntegerVariableToRef(IntegerVariable var)
void Reset(int num_variables)
void ActivityShouldNotDecrease(absl::Span< const int > enforcements, absl::Span< const int > refs, absl::Span< const int64_t > coeffs)
absl::Span< const IntegerVariable > DominatingVariables(int ref) const
std::string DominationDebugString(IntegerVariable var) const
static IntegerVariable RefToIntegerVariable(int ref)
void CanOnlyDominateEachOther(absl::Span< const int > refs)
DecisionBuilder *const phase
GurobiMPCallbackContext * context
void STLClearObject(T *obj)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
bool RefIsPositive(int ref)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
const IntegerVariable kNoIntegerVariable(-1)
void DetectDominanceRelations(const PresolveContext &context, VarDomination *var_domination, DualBoundStrengthening *dual_bound_strengthening)
IntegerVariable PositiveVariable(IntegerVariable i)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
bool ExploitDominanceRelations(const VarDomination &var_domination, PresolveContext *context)
Collection of objects used to extend the Constraint Solver library.
Fractional coeff_magnitude