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