22 #include "absl/container/flat_hash_set.h" 34 const std::vector<IntegerVariable>& vars) {
40 std::map<IntegerValue, std::vector<Literal>> value_to_literals;
42 for (
const IntegerVariable
var : vars) {
45 value_to_literals[entry.value].push_back(entry.literal);
50 for (
const auto& entry : value_to_literals) {
51 if (entry.second.size() > 1) {
59 if (value_to_literals.size() == vars.size()) {
60 for (
const auto& entry : value_to_literals) {
68 const std::vector<IntegerVariable>& vars) {
70 if (vars.empty())
return;
74 model->TakeOwnership(constraint);
79 const std::vector<IntegerVariable>& variables) {
81 if (variables.size() < 3)
return;
87 model->TakeOwnership(constraint);
94 : num_variables_(variables.size()),
95 variables_(std::move(variables)),
97 integer_trail_(integer_trail) {
101 variable_min_value_.resize(num_variables_);
102 variable_max_value_.resize(num_variables_);
103 variable_literal_index_.resize(num_variables_);
104 int num_fixed_variables = 0;
105 for (
int x = 0; x < num_variables_; x++) {
106 variable_min_value_[x] = integer_trail_->
LowerBound(variables_[x]).value();
107 variable_max_value_[x] = integer_trail_->
UpperBound(variables_[x]).value();
110 min_value =
std::min(min_value, variable_min_value_[x]);
111 max_value =
std::max(max_value, variable_max_value_[x]);
115 if (variable_min_value_[x] == variable_max_value_[x]) {
116 num_fixed_variables++;
127 int64_t size = variable_max_value_[x] - variable_min_value_[x] + 1;
130 int64_t
value = entry.value.value();
132 if (
value < variable_min_value_[x] || variable_max_value_[x] <
value) {
135 variable_literal_index_[x][
value - variable_min_value_[x]] =
136 entry.literal.Index();
139 min_all_values_ = min_value;
140 num_all_values_ = max_value - min_value + 1;
142 successor_.resize(num_variables_);
143 variable_to_value_.assign(num_variables_, -1);
144 visiting_.resize(num_variables_);
145 variable_visited_from_.resize(num_variables_);
146 residual_graph_successors_.resize(num_variables_ + num_all_values_ + 1);
147 component_number_.resize(num_variables_ + num_all_values_ + 1);
151 const int id = watcher->
Register(
this);
153 for (
const auto& literal_indices : variable_literal_index_) {
154 for (
const LiteralIndex li : literal_indices) {
165 LiteralIndex AllDifferentConstraint::VariableLiteralIndexOf(
int x,
167 return (
value < variable_min_value_[x] || variable_max_value_[x] <
value)
169 : variable_literal_index_[x][
value - variable_min_value_[x]];
172 inline bool AllDifferentConstraint::VariableHasPossibleValue(
int x,
174 LiteralIndex li = VariableLiteralIndexOf(x,
value);
181 bool AllDifferentConstraint::MakeAugmentingPath(
int start) {
186 int num_to_visit = 0;
189 visiting_[num_to_visit++] = start;
190 variable_visited_[start] =
true;
191 variable_visited_from_[start] = -1;
193 while (num_visited < num_to_visit) {
195 const int node = visiting_[num_visited++];
197 for (
const int value : successor_[node]) {
198 if (value_visited_[
value])
continue;
199 value_visited_[
value] =
true;
200 if (value_to_variable_[
value] == -1) {
202 int path_node = node;
203 int path_value =
value;
204 while (path_node != -1) {
205 int old_value = variable_to_value_[path_node];
206 variable_to_value_[path_node] = path_value;
207 value_to_variable_[path_value] = path_node;
208 path_node = variable_visited_from_[path_node];
209 path_value = old_value;
214 const int next_node = value_to_variable_[
value];
215 variable_visited_[next_node] =
true;
216 visiting_[num_to_visit++] = next_node;
217 variable_visited_from_[next_node] = node;
241 prev_matching_ = variable_to_value_;
242 value_to_variable_.assign(num_all_values_, -1);
243 variable_to_value_.assign(num_variables_, -1);
244 for (
int x = 0; x < num_variables_; x++) {
245 successor_[x].clear();
246 const int64_t min_value = integer_trail_->
LowerBound(variables_[x]).value();
247 const int64_t max_value = integer_trail_->
UpperBound(variables_[x]).value();
249 if (VariableHasPossibleValue(x,
value)) {
250 const int offset_value =
value - min_all_values_;
252 successor_[x].push_back(offset_value);
255 if (successor_[x].size() == 1) {
256 const int offset_value = successor_[x][0];
257 if (value_to_variable_[offset_value] == -1) {
258 value_to_variable_[offset_value] = x;
259 variable_to_value_[x] = offset_value;
267 for (
int x = 0; x < num_variables_; x++) {
268 for (
const int offset_value : successor_[x]) {
269 if (value_to_variable_[offset_value] != -1 &&
270 value_to_variable_[offset_value] != x) {
271 LOG(
FATAL) <<
"Should have been propagated by AllDifferentBinary()!";
278 for (
int x = 0; x < num_variables_; x++) {
279 if (variable_to_value_[x] != -1)
continue;
280 const int prev_value = prev_matching_[x];
281 if (prev_value == -1 || value_to_variable_[prev_value] != -1)
continue;
283 if (VariableHasPossibleValue(x, prev_matching_[x] + min_all_values_)) {
284 variable_to_value_[x] = prev_matching_[x];
285 value_to_variable_[prev_matching_[x]] = x;
291 for (; x < num_variables_; x++) {
292 if (variable_to_value_[x] == -1) {
293 value_visited_.assign(num_all_values_,
false);
294 variable_visited_.assign(num_variables_,
false);
295 MakeAugmentingPath(x);
297 if (variable_to_value_[x] == -1)
break;
303 if (x < num_variables_) {
307 for (
int y = 0; y < num_variables_; y++) {
308 if (!variable_visited_[y])
continue;
309 for (
int value = variable_min_value_[y];
value <= variable_max_value_[y];
311 const LiteralIndex li = VariableLiteralIndexOf(y,
value);
312 if (li >= 0 && !value_visited_[
value - min_all_values_]) {
314 conflict->push_back(
Literal(li));
323 for (
int x = 0; x < num_variables_; x++) {
324 residual_graph_successors_[x].clear();
325 for (
const int succ : successor_[x]) {
326 if (succ != variable_to_value_[x]) {
327 residual_graph_successors_[x].push_back(num_variables_ + succ);
331 for (
int offset_value = 0; offset_value < num_all_values_; offset_value++) {
332 residual_graph_successors_[num_variables_ + offset_value].clear();
333 if (value_to_variable_[offset_value] != -1) {
334 residual_graph_successors_[num_variables_ + offset_value].push_back(
335 value_to_variable_[offset_value]);
338 const int dummy_node = num_variables_ + num_all_values_;
339 residual_graph_successors_[dummy_node].clear();
340 if (num_variables_ < num_all_values_) {
341 for (
int x = 0; x < num_variables_; x++) {
342 residual_graph_successors_[dummy_node].push_back(x);
344 for (
int offset_value = 0; offset_value < num_all_values_; offset_value++) {
345 if (value_to_variable_[offset_value] == -1) {
346 residual_graph_successors_[num_variables_ + offset_value].push_back(
354 explicit SccOutput(std::vector<int>* c) : components(c) {}
355 void emplace_back(
int const*
b,
int const* e) {
356 for (
int const* it =
b; it < e; ++it) {
357 (*components)[*it] = num_components;
361 int num_components = 0;
362 std::vector<int>* components;
364 SccOutput scc_output(&component_number_);
366 static_cast<int>(residual_graph_successors_.size()),
367 residual_graph_successors_, &scc_output);
370 for (
int x = 0; x < num_variables_; x++) {
371 if (successor_[x].size() == 1)
continue;
372 for (
const int offset_value : successor_[x]) {
373 const int value_node = offset_value + num_variables_;
374 if (variable_to_value_[x] != offset_value &&
375 component_number_[x] != component_number_[value_node] &&
376 VariableHasPossibleValue(x, offset_value + min_all_values_)) {
381 value_visited_.assign(num_all_values_,
false);
382 variable_visited_.assign(num_variables_,
false);
384 const int old_variable = value_to_variable_[offset_value];
385 variable_to_value_[old_variable] = -1;
386 const int old_value = variable_to_value_[x];
387 value_to_variable_[old_value] = -1;
388 variable_to_value_[x] = offset_value;
389 value_to_variable_[offset_value] = x;
391 value_visited_[offset_value] =
true;
392 MakeAugmentingPath(old_variable);
393 DCHECK_EQ(variable_to_value_[old_variable], -1);
396 for (
int y = 0; y < num_variables_; y++) {
397 if (!variable_visited_[y])
continue;
398 for (
int value = variable_min_value_[y];
400 const LiteralIndex li = VariableLiteralIndexOf(y,
value);
401 if (li >= 0 && !value_visited_[
value - min_all_values_]) {
403 reason->push_back(
Literal(li));
408 const LiteralIndex li =
409 VariableLiteralIndexOf(x, offset_value + min_all_values_);
421 const std::vector<IntegerVariable>& vars,
IntegerTrail* integer_trail)
422 : integer_trail_(integer_trail) {
423 CHECK(!vars.empty());
426 const int capacity = vars.size() + 2;
427 index_to_start_index_.resize(
capacity);
428 index_to_end_index_.resize(
capacity);
431 for (
int i = 0; i < vars.size(); ++i) {
432 vars_.push_back({vars[i]});
433 negated_vars_.push_back({
NegationOf(vars[i])});
438 if (!PropagateLowerBounds())
return false;
443 const bool result = PropagateLowerBounds();
448 void AllDifferentBoundsPropagator::FillHallReason(IntegerValue hall_lb,
449 IntegerValue hall_ub) {
450 integer_reason_.clear();
451 const int limit = GetIndex(hall_ub);
452 for (
int i = GetIndex(hall_lb); i <= limit; ++i) {
453 const IntegerVariable
var = index_to_var_[i];
459 int AllDifferentBoundsPropagator::FindStartIndexAndCompressPath(
int index) {
461 int start_index =
index;
463 const int next = index_to_start_index_[start_index];
464 if (start_index ==
next)
break;
470 const int next = index_to_start_index_[
index];
471 if (start_index ==
next)
break;
472 index_to_start_index_[
index] = start_index;
478 bool AllDifferentBoundsPropagator::PropagateLowerBounds() {
480 for (VarValue& entry : vars_) {
481 entry.lb = integer_trail_->
LowerBound(entry.var);
482 entry.ub = integer_trail_->
UpperBound(entry.var);
485 [](VarValue
a, VarValue
b) {
return a.lb <
b.lb; });
490 int num_in_window = 1;
493 IntegerValue min_lb =
vars_.front().lb;
495 const int size =
vars_.size();
496 for (
int i = 1; i < size; ++i) {
497 const IntegerValue lb =
vars_[i].lb;
502 if (lb <= min_lb + IntegerValue(num_in_window - 1)) {
508 if (num_in_window > 1) {
509 absl::Span<VarValue> window(&vars_[start], num_in_window);
510 if (!PropagateLowerBoundsInternal(min_lb, window)) {
522 if (num_in_window > 1) {
523 absl::Span<VarValue> window(&vars_[start], num_in_window);
524 return PropagateLowerBoundsInternal(min_lb, window);
530 bool AllDifferentBoundsPropagator::PropagateLowerBoundsInternal(
531 IntegerValue min_lb, absl::Span<VarValue> vars) {
532 hall_starts_.clear();
537 base_ = min_lb - IntegerValue(1);
540 for (
const int i : indices_to_clear_) {
543 indices_to_clear_.clear();
546 std::sort(vars.begin(), vars.end(),
547 [](VarValue
a, VarValue
b) {
return a.ub <
b.ub; });
548 for (
const VarValue entry : vars) {
549 const IntegerVariable
var = entry.var;
554 const IntegerValue lb = entry.lb;
555 const int lb_index = GetIndex(lb);
556 const bool value_is_covered = PointIsPresent(lb_index);
559 if (value_is_covered) {
560 const int hall_index =
563 if (hall_index < hall_ends_.size() && hall_starts_[hall_index] <= lb) {
564 const IntegerValue hs = hall_starts_[hall_index];
565 const IntegerValue he = hall_ends_[hall_index];
566 FillHallReason(hs, he);
570 {}, integer_reason_)) {
581 int new_index = lb_index;
582 int start_index = lb_index;
583 int end_index = lb_index;
584 if (value_is_covered) {
585 start_index = FindStartIndexAndCompressPath(new_index);
586 new_index = index_to_end_index_[start_index] + 1;
587 end_index = new_index;
589 if (PointIsPresent(new_index - 1)) {
590 start_index = FindStartIndexAndCompressPath(new_index - 1);
593 if (PointIsPresent(new_index + 1)) {
594 end_index = index_to_end_index_[new_index + 1];
595 index_to_start_index_[new_index + 1] = start_index;
599 index_to_end_index_[start_index] = end_index;
603 index_to_start_index_[new_index] = start_index;
604 index_to_var_[new_index] =
var;
605 indices_to_clear_.push_back(new_index);
613 const IntegerValue end = GetValue(end_index);
623 if (end == entry.ub) {
624 const IntegerValue start = GetValue(start_index);
625 while (!hall_starts_.empty() && start <= hall_starts_.back()) {
626 hall_starts_.pop_back();
627 hall_ends_.pop_back();
629 DCHECK(hall_ends_.empty() || hall_ends_.back() < start);
630 hall_starts_.push_back(start);
631 hall_ends_.push_back(end);
639 const int id = watcher->
Register(
this);
640 for (
const VarValue entry :
vars_) {
std::function< void(Model *)> AllDifferentBinary(const std::vector< IntegerVariable > &vars)
void IncrementalSort(int max_comparisons, Iterator begin, Iterator end, Compare comp=Compare{}, bool is_stable=false)
void RegisterWith(GenericLiteralWatcher *watcher)
Class that owns everything related to a particular optimization model.
bool VariableIsFullyEncoded(IntegerVariable var) const
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
std::vector< Literal > * MutableConflict()
AllDifferentBoundsPropagator(const std::vector< IntegerVariable > &vars, IntegerTrail *integer_trail)
IntegerValue LowerBound(IntegerVariable i) const
bool LiteralIsFalse(Literal literal) const
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
const LiteralIndex kTrueLiteralIndex(-2)
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
std::vector< Literal > * GetEmptyVectorToStoreReason(int trail_index) const
void FullyEncodeVariable(IntegerVariable var)
std::function< void(Model *)> AtMostOneConstraint(const std::vector< Literal > &literals)
#define DCHECK_NE(val1, val2)
bool VariableIsAssigned(BooleanVariable var) const
void SetPropagatorPriority(int id, int priority)
std::function< std::vector< IntegerEncoder::ValueLiteralPair >Model *)> FullyEncodeVariable(IntegerVariable var)
#define DCHECK_GE(val1, val2)
void WatchLiteral(Literal l, int id, int watch_index=-1)
const std::vector< IntVar * > vars_
const LiteralIndex kFalseLiteralIndex(-3)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
#define DCHECK(condition)
AllDifferentConstraint(std::vector< IntegerVariable > variables, IntegerEncoder *encoder, Trail *trail, IntegerTrail *integer_trail)
void FindStronglyConnectedComponents(const NodeIndex num_nodes, const Graph &graph, SccOutput *components)
#define DCHECK_EQ(val1, val2)
void WatchIntegerVariable(IntegerVariable i, int id, int watch_index=-1)
std::function< void(Model *)> AllDifferentAC(const std::vector< IntegerVariable > &variables)
#define DCHECK_LE(val1, val2)
int Register(PropagatorInterface *propagator)
IntegerValue UpperBound(IntegerVariable i) const
Collection of objects used to extend the Constraint Solver library.
const IntegerVariable kNoIntegerVariable(-1)
std::function< void(Model *)> AllDifferentOnBounds(const std::vector< IntegerVariable > &vars)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
std::vector< ValueLiteralPair > FullDomainEncoding(IntegerVariable var) const
const VariablesAssignment & Assignment() const
void RegisterWith(GenericLiteralWatcher *watcher)
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
ABSL_MUST_USE_RESULT bool EnqueueWithStoredReason(Literal true_literal)
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)