20 #include "absl/container/flat_hash_set.h"
32 const std::vector<IntegerVariable>& vars) {
38 std::map<IntegerValue, std::vector<Literal>> value_to_literals;
40 for (
const IntegerVariable
var : vars) {
43 value_to_literals[entry.value].push_back(entry.literal);
48 for (
const auto& entry : value_to_literals) {
49 if (entry.second.size() > 1) {
57 if (value_to_literals.size() == vars.size()) {
58 for (
const auto& entry : value_to_literals) {
66 const std::vector<IntegerVariable>& vars) {
68 if (vars.empty())
return;
72 model->TakeOwnership(constraint);
77 const std::vector<IntegerVariable>& variables) {
79 if (variables.size() < 3)
return;
85 model->TakeOwnership(constraint);
92 : num_variables_(variables.size()),
93 variables_(std::move(variables)),
95 integer_trail_(integer_trail) {
99 variable_min_value_.resize(num_variables_);
100 variable_max_value_.resize(num_variables_);
101 variable_literal_index_.resize(num_variables_);
102 int num_fixed_variables = 0;
103 for (
int x = 0; x < num_variables_; x++) {
104 variable_min_value_[x] = integer_trail_->
LowerBound(variables_[x]).value();
105 variable_max_value_[x] = integer_trail_->
UpperBound(variables_[x]).value();
108 min_value =
std::min(min_value, variable_min_value_[x]);
109 max_value =
std::max(max_value, variable_max_value_[x]);
113 if (variable_min_value_[x] == variable_max_value_[x]) {
114 num_fixed_variables++;
125 int64 size = variable_max_value_[x] - variable_min_value_[x] + 1;
130 if (
value < variable_min_value_[x] || variable_max_value_[x] <
value) {
133 variable_literal_index_[x][
value - variable_min_value_[x]] =
134 entry.literal.Index();
137 min_all_values_ = min_value;
138 num_all_values_ = max_value - min_value + 1;
140 successor_.resize(num_variables_);
141 variable_to_value_.assign(num_variables_, -1);
142 visiting_.resize(num_variables_);
143 variable_visited_from_.resize(num_variables_);
144 residual_graph_successors_.resize(num_variables_ + num_all_values_ + 1);
145 component_number_.resize(num_variables_ + num_all_values_ + 1);
149 const int id = watcher->
Register(
this);
151 for (
const auto& literal_indices : variable_literal_index_) {
152 for (
const LiteralIndex li : literal_indices) {
163 LiteralIndex AllDifferentConstraint::VariableLiteralIndexOf(
int x,
165 return (
value < variable_min_value_[x] || variable_max_value_[x] <
value)
167 : variable_literal_index_[x][
value - variable_min_value_[x]];
170 inline bool AllDifferentConstraint::VariableHasPossibleValue(
int x,
172 LiteralIndex li = VariableLiteralIndexOf(x,
value);
179 bool AllDifferentConstraint::MakeAugmentingPath(
int start) {
184 int num_to_visit = 0;
187 visiting_[num_to_visit++] = start;
188 variable_visited_[start] =
true;
189 variable_visited_from_[start] = -1;
191 while (num_visited < num_to_visit) {
193 const int node = visiting_[num_visited++];
195 for (
const int value : successor_[node]) {
196 if (value_visited_[
value])
continue;
197 value_visited_[
value] =
true;
198 if (value_to_variable_[
value] == -1) {
200 int path_node = node;
201 int path_value =
value;
202 while (path_node != -1) {
203 int old_value = variable_to_value_[path_node];
204 variable_to_value_[path_node] = path_value;
205 value_to_variable_[path_value] = path_node;
206 path_node = variable_visited_from_[path_node];
207 path_value = old_value;
212 const int next_node = value_to_variable_[
value];
213 variable_visited_[next_node] =
true;
214 visiting_[num_to_visit++] = next_node;
215 variable_visited_from_[next_node] = node;
239 prev_matching_ = variable_to_value_;
240 value_to_variable_.assign(num_all_values_, -1);
241 variable_to_value_.assign(num_variables_, -1);
242 for (
int x = 0; x < num_variables_; x++) {
243 successor_[x].clear();
244 const int64 min_value = integer_trail_->
LowerBound(variables_[x]).value();
245 const int64 max_value = integer_trail_->
UpperBound(variables_[x]).value();
247 if (VariableHasPossibleValue(x,
value)) {
248 const int offset_value =
value - min_all_values_;
250 successor_[x].push_back(offset_value);
253 if (successor_[x].size() == 1) {
254 const int offset_value = successor_[x][0];
255 if (value_to_variable_[offset_value] == -1) {
256 value_to_variable_[offset_value] = x;
257 variable_to_value_[x] = offset_value;
265 for (
int x = 0; x < num_variables_; x++) {
266 for (
const int offset_value : successor_[x]) {
267 if (value_to_variable_[offset_value] != -1 &&
268 value_to_variable_[offset_value] != x) {
269 LOG(
FATAL) <<
"Should have been propagated by AllDifferentBinary()!";
276 for (
int x = 0; x < num_variables_; x++) {
277 if (variable_to_value_[x] != -1)
continue;
278 const int prev_value = prev_matching_[x];
279 if (prev_value == -1 || value_to_variable_[prev_value] != -1)
continue;
281 if (VariableHasPossibleValue(x, prev_matching_[x] + min_all_values_)) {
282 variable_to_value_[x] = prev_matching_[x];
283 value_to_variable_[prev_matching_[x]] = x;
289 for (; x < num_variables_; x++) {
290 if (variable_to_value_[x] == -1) {
291 value_visited_.assign(num_all_values_,
false);
292 variable_visited_.assign(num_variables_,
false);
293 MakeAugmentingPath(x);
295 if (variable_to_value_[x] == -1)
break;
301 if (x < num_variables_) {
305 for (
int y = 0; y < num_variables_; y++) {
306 if (!variable_visited_[y])
continue;
307 for (
int value = variable_min_value_[y];
value <= variable_max_value_[y];
309 const LiteralIndex li = VariableLiteralIndexOf(y,
value);
310 if (li >= 0 && !value_visited_[
value - min_all_values_]) {
312 conflict->push_back(
Literal(li));
321 for (
int x = 0; x < num_variables_; x++) {
322 residual_graph_successors_[x].clear();
323 for (
const int succ : successor_[x]) {
324 if (succ != variable_to_value_[x]) {
325 residual_graph_successors_[x].push_back(num_variables_ + succ);
329 for (
int offset_value = 0; offset_value < num_all_values_; offset_value++) {
330 residual_graph_successors_[num_variables_ + offset_value].clear();
331 if (value_to_variable_[offset_value] != -1) {
332 residual_graph_successors_[num_variables_ + offset_value].push_back(
333 value_to_variable_[offset_value]);
336 const int dummy_node = num_variables_ + num_all_values_;
337 residual_graph_successors_[dummy_node].clear();
338 if (num_variables_ < num_all_values_) {
339 for (
int x = 0; x < num_variables_; x++) {
340 residual_graph_successors_[dummy_node].push_back(x);
342 for (
int offset_value = 0; offset_value < num_all_values_; offset_value++) {
343 if (value_to_variable_[offset_value] == -1) {
344 residual_graph_successors_[num_variables_ + offset_value].push_back(
352 explicit SccOutput(std::vector<int>* c) : components(c) {}
353 void emplace_back(
int const*
b,
int const* e) {
354 for (
int const* it =
b; it < e; ++it) {
355 (*components)[*it] = num_components;
359 int num_components = 0;
360 std::vector<int>* components;
362 SccOutput scc_output(&component_number_);
364 static_cast<int>(residual_graph_successors_.size()),
365 residual_graph_successors_, &scc_output);
368 for (
int x = 0; x < num_variables_; x++) {
369 if (successor_[x].size() == 1)
continue;
370 for (
const int offset_value : successor_[x]) {
371 const int value_node = offset_value + num_variables_;
372 if (variable_to_value_[x] != offset_value &&
373 component_number_[x] != component_number_[value_node] &&
374 VariableHasPossibleValue(x, offset_value + min_all_values_)) {
379 value_visited_.assign(num_all_values_,
false);
380 variable_visited_.assign(num_variables_,
false);
382 const int old_variable = value_to_variable_[offset_value];
383 variable_to_value_[old_variable] = -1;
384 const int old_value = variable_to_value_[x];
385 value_to_variable_[old_value] = -1;
386 variable_to_value_[x] = offset_value;
387 value_to_variable_[offset_value] = x;
389 value_visited_[offset_value] =
true;
390 MakeAugmentingPath(old_variable);
391 DCHECK_EQ(variable_to_value_[old_variable], -1);
394 for (
int y = 0; y < num_variables_; y++) {
395 if (!variable_visited_[y])
continue;
396 for (
int value = variable_min_value_[y];
398 const LiteralIndex li = VariableLiteralIndexOf(y,
value);
399 if (li >= 0 && !value_visited_[
value - min_all_values_]) {
401 reason->push_back(
Literal(li));
406 const LiteralIndex li =
407 VariableLiteralIndexOf(x, offset_value + min_all_values_);
419 const std::vector<IntegerVariable>& vars,
IntegerTrail* integer_trail)
420 : integer_trail_(integer_trail) {
421 CHECK(!vars.empty());
424 const int capacity = vars.size() + 2;
425 index_to_start_index_.resize(
capacity);
426 index_to_end_index_.resize(
capacity);
429 for (
int i = 0; i < vars.size(); ++i) {
430 vars_.push_back({vars[i]});
431 negated_vars_.push_back({
NegationOf(vars[i])});
436 if (!PropagateLowerBounds())
return false;
440 std::swap(vars_, negated_vars_);
441 const bool result = PropagateLowerBounds();
442 std::swap(vars_, negated_vars_);
446 void AllDifferentBoundsPropagator::FillHallReason(IntegerValue hall_lb,
447 IntegerValue hall_ub) {
448 integer_reason_.clear();
449 const int limit = GetIndex(hall_ub);
450 for (
int i = GetIndex(hall_lb); i <= limit; ++i) {
451 const IntegerVariable
var = index_to_var_[i];
457 int AllDifferentBoundsPropagator::FindStartIndexAndCompressPath(
int index) {
459 int start_index =
index;
461 const int next = index_to_start_index_[start_index];
462 if (start_index ==
next)
break;
468 const int next = index_to_start_index_[
index];
469 if (start_index ==
next)
break;
470 index_to_start_index_[
index] = start_index;
476 bool AllDifferentBoundsPropagator::PropagateLowerBounds() {
478 for (VarValue& entry : vars_) {
479 entry.lb = integer_trail_->
LowerBound(entry.var);
480 entry.ub = integer_trail_->
UpperBound(entry.var);
483 [](VarValue
a, VarValue
b) { return a.lb < b.lb; });
488 int num_in_window = 1;
491 IntegerValue min_lb =
vars_.front().lb;
493 const int size =
vars_.size();
494 for (
int i = 1; i < size; ++i) {
495 const IntegerValue lb =
vars_[i].lb;
500 if (lb <= min_lb + IntegerValue(num_in_window - 1)) {
506 if (num_in_window > 1) {
507 absl::Span<VarValue> window(&vars_[start], num_in_window);
508 if (!PropagateLowerBoundsInternal(min_lb, window)) {
520 if (num_in_window > 1) {
521 absl::Span<VarValue> window(&vars_[start], num_in_window);
522 return PropagateLowerBoundsInternal(min_lb, window);
528 bool AllDifferentBoundsPropagator::PropagateLowerBoundsInternal(
529 IntegerValue min_lb, absl::Span<VarValue> vars) {
530 hall_starts_.clear();
535 base_ = min_lb - IntegerValue(1);
538 for (
const int i : indices_to_clear_) {
541 indices_to_clear_.clear();
544 std::sort(vars.begin(), vars.end(),
545 [](VarValue
a, VarValue
b) { return a.ub < b.ub; });
546 for (
const VarValue entry : vars) {
547 const IntegerVariable
var = entry.var;
552 const IntegerValue lb = entry.lb;
553 const int lb_index = GetIndex(lb);
554 const bool value_is_covered = PointIsPresent(lb_index);
557 if (value_is_covered) {
558 const int hall_index =
559 std::lower_bound(hall_ends_.begin(), hall_ends_.end(), lb) -
561 if (hall_index < hall_ends_.size() && hall_starts_[hall_index] <= lb) {
562 const IntegerValue hs = hall_starts_[hall_index];
563 const IntegerValue he = hall_ends_[hall_index];
564 FillHallReason(hs, he);
568 {}, integer_reason_)) {
579 int new_index = lb_index;
580 int start_index = lb_index;
581 int end_index = lb_index;
582 if (value_is_covered) {
583 start_index = FindStartIndexAndCompressPath(new_index);
584 new_index = index_to_end_index_[start_index] + 1;
585 end_index = new_index;
587 if (PointIsPresent(new_index - 1)) {
588 start_index = FindStartIndexAndCompressPath(new_index - 1);
591 if (PointIsPresent(new_index + 1)) {
592 end_index = index_to_end_index_[new_index + 1];
593 index_to_start_index_[new_index + 1] = start_index;
597 index_to_end_index_[start_index] = end_index;
601 index_to_start_index_[new_index] = start_index;
602 index_to_var_[new_index] =
var;
603 indices_to_clear_.push_back(new_index);
611 const IntegerValue end = GetValue(end_index);
621 if (end == entry.ub) {
622 const IntegerValue start = GetValue(start_index);
623 while (!hall_starts_.empty() && start <= hall_starts_.back()) {
624 hall_starts_.pop_back();
625 hall_ends_.pop_back();
627 DCHECK(hall_ends_.empty() || hall_ends_.back() < start);
628 hall_starts_.push_back(start);
629 hall_ends_.push_back(end);
637 const int id = watcher->
Register(
this);
638 for (
const VarValue entry :
vars_) {