23#include "absl/container/btree_map.h"
24#include "absl/types/span.h"
38 const std::vector<IntegerVariable>& vars) {
44 absl::btree_map<IntegerValue, std::vector<Literal>> value_to_literals;
46 for (
const IntegerVariable
var : vars) {
49 value_to_literals[entry.value].push_back(entry.literal);
54 for (
const auto& entry : value_to_literals) {
55 if (entry.second.size() > 1) {
63 if (value_to_literals.size() == vars.size()) {
64 for (
const auto& entry : value_to_literals) {
72 const std::vector<AffineExpression>& expressions) {
74 if (expressions.empty())
return;
78 model->TakeOwnership(constraint);
83 const std::vector<IntegerVariable>& vars) {
85 if (vars.empty())
return;
86 std::vector<AffineExpression> expressions;
87 expressions.reserve(vars.size());
88 for (
const IntegerVariable
var : vars) {
94 model->TakeOwnership(constraint);
99 const std::vector<IntegerVariable>& variables) {
101 if (variables.size() < 3)
return;
107 model->TakeOwnership(constraint);
114 : num_variables_(variables.size()),
115 variables_(
std::move(variables)),
117 integer_trail_(integer_trail) {
121 variable_min_value_.resize(num_variables_);
122 variable_max_value_.resize(num_variables_);
123 variable_literal_index_.resize(num_variables_);
124 int num_fixed_variables = 0;
125 for (
int x = 0; x < num_variables_; x++) {
126 variable_min_value_[x] = integer_trail_->
LowerBound(variables_[x]).value();
127 variable_max_value_[x] = integer_trail_->
UpperBound(variables_[x]).value();
130 min_value =
std::min(min_value, variable_min_value_[x]);
131 max_value =
std::max(max_value, variable_max_value_[x]);
135 if (variable_min_value_[x] == variable_max_value_[x]) {
136 num_fixed_variables++;
147 int64_t size = variable_max_value_[x] - variable_min_value_[x] + 1;
150 int64_t
value = entry.value.value();
152 if (
value < variable_min_value_[x] || variable_max_value_[x] <
value) {
155 variable_literal_index_[x][
value - variable_min_value_[x]] =
156 entry.literal.Index();
159 min_all_values_ = min_value;
160 num_all_values_ = max_value - min_value + 1;
162 successor_.resize(num_variables_);
163 variable_to_value_.assign(num_variables_, -1);
164 visiting_.resize(num_variables_);
165 variable_visited_from_.resize(num_variables_);
166 residual_graph_successors_.resize(num_variables_ + num_all_values_ + 1);
167 component_number_.resize(num_variables_ + num_all_values_ + 1);
171 const int id = watcher->
Register(
this);
173 for (
const auto& literal_indices : variable_literal_index_) {
174 for (
const LiteralIndex li : literal_indices) {
185LiteralIndex AllDifferentConstraint::VariableLiteralIndexOf(
int x,
187 return (
value < variable_min_value_[x] || variable_max_value_[x] <
value)
189 : variable_literal_index_[x][
value - variable_min_value_[x]];
192inline bool AllDifferentConstraint::VariableHasPossibleValue(
int x,
194 LiteralIndex li = VariableLiteralIndexOf(x,
value);
201bool AllDifferentConstraint::MakeAugmentingPath(
int start) {
206 int num_to_visit = 0;
209 visiting_[num_to_visit++] =
start;
210 variable_visited_[
start] =
true;
211 variable_visited_from_[
start] = -1;
213 while (num_visited < num_to_visit) {
215 const int node = visiting_[num_visited++];
217 for (
const int value : successor_[node]) {
218 if (value_visited_[
value])
continue;
219 value_visited_[
value] =
true;
220 if (value_to_variable_[
value] == -1) {
222 int path_node = node;
223 int path_value =
value;
224 while (path_node != -1) {
225 int old_value = variable_to_value_[path_node];
226 variable_to_value_[path_node] = path_value;
227 value_to_variable_[path_value] = path_node;
228 path_node = variable_visited_from_[path_node];
229 path_value = old_value;
234 const int next_node = value_to_variable_[
value];
235 variable_visited_[next_node] =
true;
236 visiting_[num_to_visit++] = next_node;
237 variable_visited_from_[next_node] = node;
261 prev_matching_ = variable_to_value_;
262 value_to_variable_.assign(num_all_values_, -1);
263 variable_to_value_.assign(num_variables_, -1);
264 for (
int x = 0; x < num_variables_; x++) {
265 successor_[x].clear();
266 const int64_t min_value = integer_trail_->
LowerBound(variables_[x]).value();
267 const int64_t max_value = integer_trail_->
UpperBound(variables_[x]).value();
269 if (VariableHasPossibleValue(x,
value)) {
270 const int offset_value =
value - min_all_values_;
272 successor_[x].push_back(offset_value);
275 if (successor_[x].size() == 1) {
276 const int offset_value = successor_[x][0];
277 if (value_to_variable_[offset_value] == -1) {
278 value_to_variable_[offset_value] = x;
279 variable_to_value_[x] = offset_value;
287 for (
int x = 0; x < num_variables_; x++) {
288 for (
const int offset_value : successor_[x]) {
289 if (value_to_variable_[offset_value] != -1 &&
290 value_to_variable_[offset_value] != x) {
291 LOG(
FATAL) <<
"Should have been propagated by AllDifferentBinary()!";
298 for (
int x = 0; x < num_variables_; x++) {
299 if (variable_to_value_[x] != -1)
continue;
300 const int prev_value = prev_matching_[x];
301 if (prev_value == -1 || value_to_variable_[prev_value] != -1)
continue;
303 if (VariableHasPossibleValue(x, prev_matching_[x] + min_all_values_)) {
304 variable_to_value_[x] = prev_matching_[x];
305 value_to_variable_[prev_matching_[x]] = x;
311 for (; x < num_variables_; x++) {
312 if (variable_to_value_[x] == -1) {
313 value_visited_.assign(num_all_values_,
false);
314 variable_visited_.assign(num_variables_,
false);
315 MakeAugmentingPath(x);
317 if (variable_to_value_[x] == -1)
break;
323 if (x < num_variables_) {
327 for (
int y = 0; y < num_variables_; y++) {
328 if (!variable_visited_[y])
continue;
329 for (
int value = variable_min_value_[y];
value <= variable_max_value_[y];
331 const LiteralIndex li = VariableLiteralIndexOf(y,
value);
332 if (li >= 0 && !value_visited_[
value - min_all_values_]) {
334 conflict->push_back(
Literal(li));
343 for (
int x = 0; x < num_variables_; x++) {
344 residual_graph_successors_[x].clear();
345 for (
const int succ : successor_[x]) {
346 if (succ != variable_to_value_[x]) {
347 residual_graph_successors_[x].push_back(num_variables_ + succ);
351 for (
int offset_value = 0; offset_value < num_all_values_; offset_value++) {
352 residual_graph_successors_[num_variables_ + offset_value].clear();
353 if (value_to_variable_[offset_value] != -1) {
354 residual_graph_successors_[num_variables_ + offset_value].push_back(
355 value_to_variable_[offset_value]);
358 const int dummy_node = num_variables_ + num_all_values_;
359 residual_graph_successors_[dummy_node].clear();
360 if (num_variables_ < num_all_values_) {
361 for (
int x = 0; x < num_variables_; x++) {
362 residual_graph_successors_[dummy_node].push_back(x);
364 for (
int offset_value = 0; offset_value < num_all_values_; offset_value++) {
365 if (value_to_variable_[offset_value] == -1) {
366 residual_graph_successors_[num_variables_ + offset_value].push_back(
374 explicit SccOutput(std::vector<int>* c) : components(c) {}
375 void emplace_back(
int const*
b,
int const* e) {
376 for (
int const* it =
b; it < e; ++it) {
377 (*components)[*it] = num_components;
381 int num_components = 0;
382 std::vector<int>* components;
384 SccOutput scc_output(&component_number_);
386 static_cast<int>(residual_graph_successors_.size()),
387 residual_graph_successors_, &scc_output);
390 for (
int x = 0; x < num_variables_; x++) {
391 if (successor_[x].size() == 1)
continue;
392 for (
const int offset_value : successor_[x]) {
393 const int value_node = offset_value + num_variables_;
394 if (variable_to_value_[x] != offset_value &&
395 component_number_[x] != component_number_[value_node] &&
396 VariableHasPossibleValue(x, offset_value + min_all_values_)) {
401 value_visited_.assign(num_all_values_,
false);
402 variable_visited_.assign(num_variables_,
false);
404 const int old_variable = value_to_variable_[offset_value];
405 variable_to_value_[old_variable] = -1;
406 const int old_value = variable_to_value_[x];
407 value_to_variable_[old_value] = -1;
408 variable_to_value_[x] = offset_value;
409 value_to_variable_[offset_value] = x;
411 value_visited_[offset_value] =
true;
412 MakeAugmentingPath(old_variable);
413 DCHECK_EQ(variable_to_value_[old_variable], -1);
416 for (
int y = 0; y < num_variables_; y++) {
417 if (!variable_visited_[y])
continue;
418 for (
int value = variable_min_value_[y];
420 const LiteralIndex li = VariableLiteralIndexOf(y,
value);
421 if (li >= 0 && !value_visited_[
value - min_all_values_]) {
423 reason->push_back(
Literal(li));
428 const LiteralIndex li =
429 VariableLiteralIndexOf(x, offset_value + min_all_values_);
441 const std::vector<AffineExpression>& expressions,
443 : integer_trail_(integer_trail) {
444 CHECK(!expressions.empty());
447 const int capacity = expressions.size() + 2;
448 index_to_start_index_.resize(
capacity);
449 index_to_end_index_.resize(
capacity);
450 index_is_present_.resize(
capacity,
false);
453 for (
int i = 0; i < expressions.size(); ++i) {
454 bounds_.push_back({expressions[i]});
455 negated_bounds_.push_back({expressions[i].Negated()});
460 if (!PropagateLowerBounds())
return false;
465 const bool result = PropagateLowerBounds();
470void AllDifferentBoundsPropagator::FillHallReason(IntegerValue hall_lb,
471 IntegerValue hall_ub) {
472 integer_reason_.clear();
473 const int limit = GetIndex(hall_ub);
474 for (
int i = GetIndex(hall_lb); i <= limit; ++i) {
481int AllDifferentBoundsPropagator::FindStartIndexAndCompressPath(
int index) {
483 int start_index =
index;
485 const int next = index_to_start_index_[start_index];
486 if (start_index ==
next)
break;
492 const int next = index_to_start_index_[
index];
493 if (start_index ==
next)
break;
494 index_to_start_index_[
index] = start_index;
500bool AllDifferentBoundsPropagator::PropagateLowerBounds() {
502 for (CachedBounds& entry : bounds_) {
503 entry.lb = integer_trail_->
LowerBound(entry.expr);
504 entry.ub = integer_trail_->
UpperBound(entry.expr);
507 [](CachedBounds
a, CachedBounds
b) { return a.lb < b.lb; });
512 int num_in_window = 1;
515 IntegerValue min_lb = bounds_.front().lb;
517 const int size = bounds_.size();
518 for (
int i = 1; i < size; ++i) {
519 const IntegerValue lb = bounds_[i].lb;
524 if (lb <= min_lb + IntegerValue(num_in_window - 1)) {
530 if (num_in_window > 1) {
531 absl::Span<CachedBounds> window(&bounds_[
start], num_in_window);
532 if (!PropagateLowerBoundsInternal(min_lb, window)) {
544 if (num_in_window > 1) {
545 absl::Span<CachedBounds> window(&bounds_[
start], num_in_window);
546 return PropagateLowerBoundsInternal(min_lb, window);
552bool AllDifferentBoundsPropagator::PropagateLowerBoundsInternal(
553 IntegerValue min_lb, absl::Span<CachedBounds>
bounds) {
554 hall_starts_.clear();
559 base_ = min_lb - IntegerValue(1);
562 for (
const int i : indices_to_clear_) {
563 index_is_present_[i] =
false;
565 indices_to_clear_.clear();
569 [](CachedBounds
a, CachedBounds
b) { return a.ub < b.ub; });
570 for (
const CachedBounds entry :
bounds) {
571 const AffineExpression expr = entry.expr;
576 const IntegerValue lb = entry.lb;
577 const int lb_index = GetIndex(lb);
578 const bool value_is_covered = index_is_present_[lb_index];
581 if (value_is_covered) {
582 const int hall_index =
585 if (hall_index < hall_ends_.size() && hall_starts_[hall_index] <= lb) {
586 const IntegerValue hs = hall_starts_[hall_index];
587 const IntegerValue he = hall_ends_[hall_index];
588 FillHallReason(hs, he);
589 integer_reason_.push_back(expr.GreaterOrEqual(hs));
590 if (!integer_trail_->
SafeEnqueue(expr.GreaterOrEqual(he + 1),
602 int new_index = lb_index;
603 int start_index = lb_index;
604 int end_index = lb_index;
605 if (value_is_covered) {
606 start_index = FindStartIndexAndCompressPath(new_index);
607 new_index = index_to_end_index_[start_index] + 1;
608 end_index = new_index;
610 if (index_is_present_[new_index - 1]) {
611 start_index = FindStartIndexAndCompressPath(new_index - 1);
614 if (index_is_present_[new_index + 1]) {
615 end_index = index_to_end_index_[new_index + 1];
616 index_to_start_index_[new_index + 1] = start_index;
620 index_to_end_index_[start_index] = end_index;
624 index_to_start_index_[new_index] = start_index;
625 index_to_expr_[new_index] = expr;
626 index_is_present_[new_index] =
true;
627 indices_to_clear_.push_back(new_index);
636 const IntegerValue
end = GetValue(end_index);
646 if (
end == entry.ub) {
647 const IntegerValue
start = GetValue(start_index);
648 while (!hall_starts_.empty() &&
start <= hall_starts_.back()) {
649 hall_starts_.pop_back();
650 hall_ends_.pop_back();
652 DCHECK(hall_ends_.empty() || hall_ends_.back() <
start);
653 hall_starts_.push_back(
start);
654 hall_ends_.push_back(
end);
662 const int id = watcher->
Register(
this);
663 for (
const CachedBounds& entry : bounds_) {
#define DCHECK_NE(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK(condition)
#define DCHECK_EQ(val1, val2)
void RegisterWith(GenericLiteralWatcher *watcher)
AllDifferentBoundsPropagator(const std::vector< AffineExpression > &expressions, IntegerTrail *integer_trail)
void RegisterWith(GenericLiteralWatcher *watcher)
AllDifferentConstraint(std::vector< IntegerVariable > variables, IntegerEncoder *encoder, Trail *trail, IntegerTrail *integer_trail)
void WatchLiteral(Literal l, int id, int watch_index=-1)
void WatchAffineExpression(AffineExpression e, int id)
void SetPropagatorPriority(int id, int priority)
int Register(PropagatorInterface *propagator)
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)
void FullyEncodeVariable(IntegerVariable var)
std::vector< ValueLiteralPair > FullDomainEncoding(IntegerVariable var) const
bool VariableIsFullyEncoded(IntegerVariable var) const
IntegerValue UpperBound(IntegerVariable i) const
ABSL_MUST_USE_RESULT bool SafeEnqueue(IntegerLiteral i_lit, absl::Span< const IntegerLiteral > integer_reason)
IntegerValue LowerBound(IntegerVariable i) const
Class that owns everything related to a particular optimization model.
std::vector< Literal > * GetEmptyVectorToStoreReason(int trail_index) const
const VariablesAssignment & Assignment() const
ABSL_MUST_USE_RESULT bool EnqueueWithStoredReason(Literal true_literal)
std::vector< Literal > * MutableConflict()
bool VariableIsAssigned(BooleanVariable var) const
bool LiteralIsFalse(Literal literal) const
SharedBoundsManager * bounds
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
std::function< std::vector< ValueLiteralPair >(Model *)> FullyEncodeVariable(IntegerVariable var)
std::function< void(Model *)> AllDifferentAC(const std::vector< IntegerVariable > &variables)
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
const IntegerVariable kNoIntegerVariable(-1)
const LiteralIndex kTrueLiteralIndex(-2)
std::function< void(Model *)> AtMostOneConstraint(const std::vector< Literal > &literals)
const LiteralIndex kFalseLiteralIndex(-3)
std::function< void(Model *)> AllDifferentBinary(const std::vector< IntegerVariable > &vars)
std::function< void(Model *)> AllDifferentOnBounds(const std::vector< AffineExpression > &expressions)
Collection of objects used to extend the Constraint Solver library.
void IncrementalSort(int max_comparisons, Iterator begin, Iterator end, Compare comp=Compare{}, bool is_stable=false)
std::optional< int64_t > end
void FindStronglyConnectedComponents(const NodeIndex num_nodes, const Graph &graph, SccOutput *components)
IntegerLiteral GreaterOrEqual(IntegerValue bound) const
IntegerLiteral LowerOrEqual(IntegerValue bound) const