Google OR-Tools v9.9
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
all_different.h
Go to the documentation of this file.
1// Copyright 2010-2024 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/log/check.h"
24#include "absl/types/span.h"
26#include "ortools/base/types.h"
27#include "ortools/sat/integer.h"
28#include "ortools/sat/model.h"
31
32namespace operations_research {
33namespace 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.
38std::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.
49std::function<void(Model*)> AllDifferentOnBounds(
50 const std::vector<IntegerVariable>& vars);
51std::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.
64std::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;
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 // This type is neither copyable nor movable.
161 delete;
162
163 bool Propagate() final;
164 void RegisterWith(GenericLiteralWatcher* watcher);
165
166 private:
167 // We locally cache the lb/ub for faster sorting and to guarantee some
168 // invariant when we push bounds.
169 struct CachedBounds {
170 AffineExpression expr;
171 IntegerValue lb;
172 IntegerValue ub;
173 };
174
175 // Fills integer_reason_ with the reason why we have the given hall interval.
176 void FillHallReason(IntegerValue hall_lb, IntegerValue hall_ub);
177
178 // Do half the job of Propagate(). This will split the variable into
179 // independent subset, and call PropagateLowerBoundsInternal() on each of
180 // them.
181 bool PropagateLowerBounds();
182 bool PropagateLowerBoundsInternal(IntegerValue min_lb,
183 absl::Span<CachedBounds> bounds);
184
185 // Internally, we will maintain a set of non-consecutive integer intervals of
186 // the form [start, end]. Each point (i.e. IntegerValue) of such interval will
187 // be associated to an unique input expression and via an union-find algorithm
188 // point to its start. The end only make sense for representative.
189 //
190 // TODO(user): Because we don't use rank, we have a worst case complexity of
191 // O(n log n). We could try a normal Union-find data structure, but then we
192 // also have to maintain a start vector.
193 //
194 // Note that during the execution of the algorithm we start from empty
195 // intervals and finish with a set of points of size num_vars.
196 //
197 // The list of all points are maintained in the dense vectors index_to_*_
198 // where we have remapped values to indices (with GetIndex()) to make sure it
199 // always fall into the correct range.
200 int FindStartIndexAndCompressPath(int index);
201
202 int GetIndex(IntegerValue value) const {
203 DCHECK_GE(value, base_);
204 DCHECK_LT(value - base_, index_to_start_index_.size());
205 return (value - base_).value();
206 }
207
208 IntegerValue GetValue(int index) const { return base_ + IntegerValue(index); }
209
210 IntegerTrail* integer_trail_;
211
212 // These vector will be either sorted by lb or by -ub.
213 std::vector<CachedBounds> bounds_;
214 std::vector<CachedBounds> negated_bounds_;
215
216 // The list of Hall intervalls detected so far, sorted.
217 std::vector<IntegerValue> hall_starts_;
218 std::vector<IntegerValue> hall_ends_;
219
220 // Non-consecutive intervals related data-structures.
221 IntegerValue base_;
222 std::vector<int> indices_to_clear_;
223 std::vector<int> index_to_start_index_;
224 std::vector<int> index_to_end_index_;
225 std::vector<bool> index_is_present_;
226 std::vector<AffineExpression> index_to_expr_;
227
228 // Temporary integer reason.
229 std::vector<IntegerLiteral> integer_reason_;
230};
231
232} // namespace sat
233} // namespace operations_research
234
235#endif // OR_TOOLS_SAT_ALL_DIFFERENT_H_
void RegisterWith(GenericLiteralWatcher *watcher)
AllDifferentBoundsPropagator(const AllDifferentBoundsPropagator &)=delete
This type is neither copyable nor movable.
AllDifferentBoundsPropagator & operator=(const AllDifferentBoundsPropagator &)=delete
AllDifferentBoundsPropagator(const std::vector< AffineExpression > &expressions, IntegerTrail *integer_trail)
Implementation of AllDifferentAC().
AllDifferentConstraint(std::vector< IntegerVariable > variables, IntegerEncoder *encoder, Trail *trail, IntegerTrail *integer_trail)
void RegisterWith(GenericLiteralWatcher *watcher)
Base class for CP like propagators.
Definition integer.h:1318
std::unique_ptr< SharedBoundsManager > bounds
These can be nullptr depending on the options.
int64_t value
int index
std::function< void(Model *) AllDifferentOnBounds)(const std::vector< AffineExpression > &expressions)
std::function< void(Model *) AllDifferentAC)(const std::vector< IntegerVariable > &variables)
std::function< void(Model *) AllDifferentBinary)(const std::vector< IntegerVariable > &vars)
In SWIG mode, we don't want anything besides these top-level includes.
const Variable x
Definition qp_tests.cc:127
int64_t start