OR-Tools  9.1
all_different.h
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef OR_TOOLS_SAT_ALL_DIFFERENT_H_
15#define OR_TOOLS_SAT_ALL_DIFFERENT_H_
16
17#include <cstdint>
18#include <functional>
19#include <utility>
20#include <vector>
21
22#include "absl/container/flat_hash_map.h"
24#include "ortools/base/macros.h"
25#include "ortools/sat/integer.h"
26#include "ortools/sat/model.h"
28
29namespace operations_research {
30namespace sat {
31
32// Enforces that the given tuple of variables takes different values. This fully
33// encodes all the variables and simply enforces a <= 1 constraint on each
34// possible values.
35std::function<void(Model*)> AllDifferentBinary(
36 const std::vector<IntegerVariable>& vars);
37
38// Enforces that the given tuple of variables takes different values.
39// Same as AllDifferentBinary() but use a different propagator that only enforce
40// the so called "bound consistency" on the variable domains.
41//
42// Compared to AllDifferentBinary() this doesn't require fully encoding the
43// variables and it is also quite fast. Note that the propagation is different,
44// this will not remove already taken values from inside a domain, but it will
45// propagates more the domain bounds.
46std::function<void(Model*)> AllDifferentOnBounds(
47 const std::vector<IntegerVariable>& vars);
48
49// This constraint forces all variables to take different values. This is meant
50// to be used as a complement to an alldifferent decomposition like
51// AllDifferentBinary(): DO NOT USE WITHOUT ONE. Doing the filtering that the
52// decomposition can do with an appropriate algorithm should be cheaper and
53// yield more accurate explanations.
54//
55// It uses the matching algorithm described in Regin at AAAI1994:
56// "A filtering algorithm for constraints of difference in CSPs".
57//
58// This will fully encode variables.
59std::function<void(Model*)> AllDifferentAC(
60 const std::vector<IntegerVariable>& variables);
61
62// Implementation of AllDifferentAC().
64 public:
65 AllDifferentConstraint(std::vector<IntegerVariable> variables,
66 IntegerEncoder* encoder, Trail* trail,
67 IntegerTrail* integer_trail);
68
69 bool Propagate() final;
71
72 private:
73 // MakeAugmentingPath() is a step in Ford-Fulkerson's augmenting path
74 // algorithm. It changes its current internal state (see vectors below)
75 // to assign a value to the start vertex using an augmenting path.
76 // If it is not possible, it keeps variable_to_value_[start] to -1 and returns
77 // false, otherwise it modifies the current assignment and returns true.
78 // It uses value/variable_visited to mark the nodes it visits during its
79 // search: one can use this information to generate an explanation of failure,
80 // or manipulate it to create what-if scenarios without modifying successor_.
81 bool MakeAugmentingPath(int start);
82
83 // Accessors to the cache of literals.
84 inline LiteralIndex VariableLiteralIndexOf(int x, int64_t value);
85 inline bool VariableHasPossibleValue(int x, int64_t value);
86
87 // This caches all literals of the fully encoded variables.
88 // Values of a given variable are 0-indexed using offsets variable_min_value_,
89 // the set of all values is globally offset using offset min_all_values_.
90 // TODO(user): compare this encoding to a sparser hash_map encoding.
91 const int num_variables_;
92 const std::vector<IntegerVariable> variables_;
93 int64_t min_all_values_;
94 int64_t num_all_values_;
95 std::vector<int64_t> variable_min_value_;
96 std::vector<int64_t> variable_max_value_;
97 std::vector<std::vector<LiteralIndex>> variable_literal_index_;
98
99 // Internal state of MakeAugmentingPath().
100 // value_to_variable_ and variable_to_value_ represent the current assignment;
101 // -1 means not assigned. Otherwise,
102 // variable_to_value_[var] = value <=> value_to_variable_[value] = var.
103 std::vector<std::vector<int>> successor_;
104 std::vector<bool> value_visited_;
105 std::vector<bool> variable_visited_;
106 std::vector<int> value_to_variable_;
107 std::vector<int> variable_to_value_;
108 std::vector<int> prev_matching_;
109 std::vector<int> visiting_;
110 std::vector<int> variable_visited_from_;
111
112 // Internal state of ComputeSCCs().
113 // Variable nodes are indexed by [0, num_variables_),
114 // value nodes by [num_variables_, num_variables_ + num_all_values_),
115 // and a dummy node with index num_variables_ + num_all_values_ is added.
116 // The graph passed to ComputeSCCs() is the residual of the possible graph
117 // by the current matching, i.e. its arcs are:
118 // _ (var, val) if val \in dom(var) and var not matched to val,
119 // _ (val, var) if var matched to val,
120 // _ (val, dummy) if val not matched to any variable,
121 // _ (dummy, var) for all variables.
122 // In the original paper, forbidden arcs are identified by detecting that they
123 // are not in any alternating cycle or alternating path starting at a
124 // free vertex. Adding the dummy node allows to factor the alternating path
125 // part in the alternating cycle, and filter with only the SCC decomposition.
126 // When num_variables_ == num_all_values_, the dummy node is useless,
127 // we add it anyway to simplify the code.
128 std::vector<std::vector<int>> residual_graph_successors_;
129 std::vector<int> component_number_;
130
131 Trail* trail_;
132 IntegerTrail* integer_trail_;
133};
134
135// Implement the all different bound consistent propagator with explanation.
136// That is, given n variables that must be all different, this propagates the
137// bounds of each variables as much as possible. The key is to detect the so
138// called Hall interval which are interval of size k that contains the domain
139// of k variables. Because all the variables must take different values, we can
140// deduce that the domain of the other variables cannot contains such Hall
141// interval.
142//
143// We use a "fast" O(n log n) algorithm.
144//
145// TODO(user): It might be difficult to find something faster than what is
146// implemented here. Some related reference:
147// https://cs.uwaterloo.ca/~vanbeek/Publications/ijcai03_TR.pdf
149 public:
150 AllDifferentBoundsPropagator(const std::vector<IntegerVariable>& vars,
151 IntegerTrail* integer_trail);
152
153 bool Propagate() final;
154 void RegisterWith(GenericLiteralWatcher* watcher);
155
156 private:
157 // We locally cache the lb/ub for faster sorting and to guarantee some
158 // invariant when we push bounds.
159 struct VarValue {
160 IntegerVariable var;
161 IntegerValue lb;
162 IntegerValue ub;
163 };
164
165 // Fills integer_reason_ with the reason why we have the given hall interval.
166 void FillHallReason(IntegerValue hall_lb, IntegerValue hall_ub);
167
168 // Do half the job of Propagate(). This will split the variable into
169 // independent subset, and call PropagateLowerBoundsInternal() on each of
170 // them.
171 bool PropagateLowerBounds();
172 bool PropagateLowerBoundsInternal(IntegerValue min_lb,
173 absl::Span<VarValue> vars);
174
175 // Internally, we will maintain a set of non-consecutive integer intervals of
176 // the form [start, end]. Each point (i.e. IntegerValue) of such interval will
177 // be associated to an unique variable and via an union-find algorithm point
178 // to its start. The end only make sense for representative.
179 //
180 // TODO(user): Because we don't use rank, we have a worst case complexity of
181 // O(n log n). We could try a normal Union-find data structure, but then we
182 // also have to maintain a start vector.
183 //
184 // Note that during the execution of the algorithm we start from empty
185 // intervals and finish with a set of points of size num_vars.
186 //
187 // The list of all points are maintained in the dense vectors index_to_*_
188 // where we have remapped values to indices (with GetIndex()) to make sure it
189 // always fall into the correct range.
190 int FindStartIndexAndCompressPath(int index);
191
192 int GetIndex(IntegerValue value) const {
193 DCHECK_GE(value, base_);
194 DCHECK_LT(value - base_, index_to_start_index_.size());
195 return (value - base_).value();
196 }
197
198 IntegerValue GetValue(int index) const { return base_ + IntegerValue(index); }
199
200 bool PointIsPresent(int index) const {
201 return index_to_var_[index] != kNoIntegerVariable;
202 }
203
204 IntegerTrail* integer_trail_;
205
206 // These vector will be either sorted by lb or by ub.
207 std::vector<VarValue> vars_;
208 std::vector<VarValue> negated_vars_;
209
210 // The list of Hall intervalls detected so far, sorted.
211 std::vector<IntegerValue> hall_starts_;
212 std::vector<IntegerValue> hall_ends_;
213
214 // Non-consecutive intervals related data-structures.
215 IntegerValue base_;
216 std::vector<int> indices_to_clear_;
217 std::vector<int> index_to_start_index_;
218 std::vector<int> index_to_end_index_;
219 std::vector<IntegerVariable> index_to_var_;
220
221 // Temporary integer reason.
222 std::vector<IntegerLiteral> integer_reason_;
223
224 DISALLOW_COPY_AND_ASSIGN(AllDifferentBoundsPropagator);
225};
226
227} // namespace sat
228} // namespace operations_research
229
230#endif // OR_TOOLS_SAT_ALL_DIFFERENT_H_
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:890
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:889
AllDifferentBoundsPropagator(const std::vector< IntegerVariable > &vars, IntegerTrail *integer_trail)
void RegisterWith(GenericLiteralWatcher *watcher)
void RegisterWith(GenericLiteralWatcher *watcher)
AllDifferentConstraint(std::vector< IntegerVariable > variables, IntegerEncoder *encoder, Trail *trail, IntegerTrail *integer_trail)
int64_t value
IntVar * var
Definition: expr_array.cc:1874
std::function< void(Model *)> AllDifferentAC(const std::vector< IntegerVariable > &variables)
std::function< void(Model *)> AllDifferentOnBounds(const std::vector< IntegerVariable > &vars)
const IntegerVariable kNoIntegerVariable(-1)
std::function< void(Model *)> AllDifferentBinary(const std::vector< IntegerVariable > &vars)
Collection of objects used to extend the Constraint Solver library.
int index
Definition: pack.cc:509