OR-Tools  9.6
sat/util.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_UTIL_H_
15 #define OR_TOOLS_SAT_UTIL_H_
16 
17 #include <cmath>
18 #include <cstdint>
19 #include <deque>
20 #include <limits>
21 #include <string>
22 #include <vector>
23 
24 #include "ortools/base/logging.h"
25 #if !defined(__PORTABLE_PLATFORM__)
26 #include "google/protobuf/descriptor.h"
27 #endif // __PORTABLE_PLATFORM__
28 #include "absl/container/btree_set.h"
29 #include "absl/container/inlined_vector.h"
30 #include "absl/random/bit_gen_ref.h"
31 #include "absl/random/random.h"
32 #include "absl/types/span.h"
33 #include "ortools/sat/model.h"
34 #include "ortools/sat/sat_base.h"
35 #include "ortools/sat/sat_parameters.pb.h"
39 
40 namespace operations_research {
41 namespace sat {
42 
43 // Prints a positive number with separators for easier reading (ex: 1'348'065).
44 std::string FormatCounter(int64_t num);
45 
46 // Returns a in [0, m) such that a * x = 1 modulo m.
47 // If gcd(x, m) != 1, there is no inverse, and it returns 0.
48 //
49 // This DCHECK that x is in [0, m).
50 // This is integer overflow safe.
51 //
52 // Note(user): I didn't find this in a easily usable standard library.
53 int64_t ModularInverse(int64_t x, int64_t m);
54 
55 // Just returns x % m but with a result always in [0, m).
56 int64_t PositiveMod(int64_t x, int64_t m);
57 
58 // If we know that X * coeff % mod = rhs % mod, this returns c such that
59 // PositiveMod(X, mod) = c.
60 //
61 // This requires coeff != 0, mod !=0 and gcd(coeff, mod) == 1.
62 // The result will be in [0, mod) but there is no other condition on the sign or
63 // magnitude of a and b.
64 //
65 // This is overflow safe, and when rhs == 0 or abs(mod) == 1, it returns 0.
66 int64_t ProductWithModularInverse(int64_t coeff, int64_t mod, int64_t rhs);
67 
68 // Returns true if the equation a * X + b * Y = cte has some integer solutions.
69 // For now, we check that a and b are different from 0 and from int64_t min.
70 //
71 // There is actually always a solution if cte % gcd(|a|, |b|) == 0. And because
72 // a, b and cte fit on an int64_t, if there is a solution, there is one with X
73 // and Y fitting on an int64_t.
74 //
75 // We will divide everything by gcd(a, b) first, so it is why we take reference
76 // and the equation can change.
77 //
78 // If there are solutions, we return one of them (x0, y0).
79 // From any such solution, the set of all solutions is given for Z integer by:
80 // X = x0 + b * Z;
81 // Y = y0 - a * Z;
82 //
83 // Given a domain for X and Y, it is possible to compute the "exact" domain of Z
84 // with our Domain functions. Note however that this will only compute solution
85 // where both x-x0 and y-y0 do fit on an int64_t:
86 // DomainOf(x).SubtractionWith(x0).InverseMultiplicationBy(b).IntersectionWith(
87 // DomainOf(y).SubtractionWith(y0).InverseMultiplicationBy(-a))
88 bool SolveDiophantineEquationOfSizeTwo(int64_t& a, int64_t& b, int64_t& cte,
89  int64_t& x0, int64_t& y0);
90 
91 // The argument must be non-negative.
92 int64_t FloorSquareRoot(int64_t a);
93 int64_t CeilSquareRoot(int64_t a);
94 
95 // Converts a double to int64_t and cap large magnitudes at kint64min/max.
96 // We also arbitrarily returns 0 for NaNs.
97 //
98 // Note(user): This is similar to SaturatingFloatToInt(), but we use our own
99 // since we need to open source it and the code is simple enough.
100 int64_t SafeDoubleToInt64(double value);
101 
102 // Returns the multiple of base closest to value. If there is a tie, we return
103 // the one closest to zero. This way we have ClosestMultiple(x) =
104 // -ClosestMultiple(-x) which is important for how this is used.
105 int64_t ClosestMultiple(int64_t value, int64_t base);
106 
107 // Given a linear equation "sum coeff_i * X_i <= rhs. We can rewrite it using
108 // ClosestMultiple() as "base * new_terms + error <= rhs" where error can be
109 // bounded using the provided bounds on each variables. This will return true if
110 // the error can be ignored and this equation is completely equivalent to
111 // new_terms <= new_rhs.
112 //
113 // This is useful for cases like 9'999 X + 10'0001 Y <= 155'000 where we have
114 // weird coefficient (maybe due to scaling). With a base of 10K, this is
115 // equivalent to X + Y <= 15.
116 //
117 // Preconditions: All coeffs are assumed to be positive. You can easily negate
118 // all the negative coeffs and corresponding bounds before calling this.
120  int64_t base, const std::vector<int64_t>& coeffs,
121  const std::vector<int64_t>& lbs, const std::vector<int64_t>& ubs,
122  int64_t rhs, int64_t* new_rhs);
123 
124 // The model "singleton" random engine used in the solver.
125 //
126 // In test, we usually set use_absl_random() so that the sequence is changed at
127 // each invocation. This way, clients do not relly on the wrong assumption that
128 // a particular optimal solution will be returned if they are many equivalent
129 // ones.
130 class ModelRandomGenerator : public absl::BitGenRef {
131  public:
132  // We seed the strategy at creation only. This should be enough for our use
133  // case since the SatParameters is set first before the solver is created. We
134  // also never really need to change the seed afterwards, it is just used to
135  // diversify solves with identical parameters on different Model objects.
137  : absl::BitGenRef(deterministic_random_) {
138  const auto& params = *model->GetOrCreate<SatParameters>();
139  deterministic_random_.seed(params.random_seed());
140  if (params.use_absl_random()) {
141  absl_random_ = absl::BitGen(absl::SeedSeq({params.random_seed()}));
142  absl::BitGenRef::operator=(absl::BitGenRef(absl_random_));
143  }
144  }
145 
146  // This is just used to display ABSL_RANDOM_SALT_OVERRIDE in the log so that
147  // it is possible to reproduce a failure more easily while looking at a solver
148  // log.
149  //
150  // TODO(user): I didn't find a cleaner way to log this.
151  void LogSalt() const {}
152 
153  private:
154  random_engine_t deterministic_random_;
155  absl::BitGen absl_random_;
156 };
157 
158 // The model "singleton" shared time limit.
160  public:
162  : SharedTimeLimit(model->GetOrCreate<TimeLimit>()) {}
163 };
164 
165 // Randomizes the decision heuristic of the given SatParameters.
166 void RandomizeDecisionHeuristic(absl::BitGenRef random,
167  SatParameters* parameters);
168 
169 // Context: this function is not really generic, but required to be unit-tested.
170 // It is used in a clause minimization algorithm when we try to detect if any of
171 // the clause literals can be propagated by a subset of the other literal being
172 // false. For that, we want to enqueue in the solver all the subset of size n-1.
173 //
174 // This moves one of the unprocessed literal from literals to the last position.
175 // The function tries to do that while preserving the longest possible prefix of
176 // literals "amortized" through the calls assuming that we want to move each
177 // literal to the last position once.
178 //
179 // For a vector of size n, if we want to call this n times so that each literal
180 // is last at least once, the sum of the size of the changed suffixes will be
181 // O(n log n). If we were to use a simpler algorithm (like moving the last
182 // unprocessed literal to the last position), this sum would be O(n^2).
183 //
184 // Returns the size of the common prefix of literals before and after the move,
185 // or -1 if all the literals are already processed. The argument
186 // relevant_prefix_size is used as a hint when keeping more that this prefix
187 // size do not matter. The returned value will always be lower or equal to
188 // relevant_prefix_size.
190  const absl::btree_set<LiteralIndex>& processed, int relevant_prefix_size,
191  std::vector<Literal>* literals);
192 
193 // Simple DP to compute the maximum reachable value of a "subset sum" under
194 // a given bound (inclusive). Note that we abort as soon as the computation
195 // become too important.
196 //
197 // Precondition: Both bound and all added values must be >= 0.
199  public:
201  explicit MaxBoundedSubsetSum(int64_t bound) { Reset(bound); }
202 
203  // Resets to an empty set of values.
204  // We look for the maximum sum <= bound.
205  void Reset(int64_t bound);
206 
207  // Add a value to the base set for which subset sums will be taken.
208  void Add(int64_t value);
209 
210  // Add a choice of values to the base set for which subset sums will be taken.
211  // Note that even if this doesn't include zero, not taking any choices will
212  // also be an option.
213  void AddChoices(absl::Span<const int64_t> choices);
214 
215  // Adds [0, coeff, 2 * coeff, ... max_value * coeff].
216  void AddMultiples(int64_t coeff, int64_t max_value);
217 
218  // Returns an upper bound (inclusive) on the maximum sum <= bound_.
219  // This might return bound_ if we aborted the computation.
220  int64_t CurrentMax() const { return current_max_; }
221 
222  int64_t Bound() const { return bound_; }
223 
224  private:
225  // This assumes filtered values.
226  void AddChoicesInternal(absl::Span<const int64_t> values);
227 
228  static constexpr int kMaxComplexityPerAdd = 50;
229 
230  int64_t gcd_;
231  int64_t bound_;
232  int64_t current_max_;
233  std::vector<int64_t> sums_;
234  std::vector<bool> expanded_sums_;
235  std::vector<int64_t> filtered_values_;
236 };
237 
238 // Use Dynamic programming to solve a single knapsack. This is used by the
239 // presolver to simplify variables appearing in a single linear constraint.
240 //
241 // Complexity is the best of
242 // - O(num_variables * num_relevant_values ^ 2) or
243 // - O(num_variables * num_relevant_values * max_domain_size).
245  public:
246  // Solves the problem:
247  // - minimize sum costs * X[i]
248  // - subject to sum coeffs[i] * X[i] \in rhs, with X[i] \in Domain(i).
249  //
250  // Returns:
251  // - (solved = false) if complexity is too high.
252  // - (solved = true, infeasible = true) if proven infeasible.
253  // - (solved = true, infeasible = false, solution) otherwise.
254  struct Result {
255  bool solved = false;
256  bool infeasible = false;
257  std::vector<int64_t> solution;
258  };
259  Result Solve(const std::vector<Domain>& domains,
260  const std::vector<int64_t>& coeffs,
261  const std::vector<int64_t>& costs, const Domain& rhs);
262 
263  private:
264  Result InternalSolve(int64_t num_values, const Domain& rhs);
265 
266  // Canonicalized version.
267  std::vector<Domain> domains_;
268  std::vector<int64_t> coeffs_;
269  std::vector<int64_t> costs_;
270 
271  // We only need to keep one state with the same activity.
272  struct State {
274  int64_t value = 0;
275  };
276  std::vector<std::vector<State>> var_activity_states_;
277 };
278 
279 // Manages incremental averages.
281  public:
282  // Initializes the average with 'initial_average' and number of records to 0.
283  explicit IncrementalAverage(double initial_average)
284  : average_(initial_average) {}
286 
287  // Sets the number of records to 0 and average to 'reset_value'.
288  void Reset(double reset_value);
289 
290  double CurrentAverage() const { return average_; }
291  int64_t NumRecords() const { return num_records_; }
292 
293  void AddData(double new_record);
294 
295  private:
296  double average_ = 0.0;
297  int64_t num_records_ = 0;
298 };
299 
300 // Manages exponential moving averages defined as
301 // new_average = decaying_factor * old_average
302 // + (1 - decaying_factor) * new_record.
303 // where 0 < decaying_factor < 1.
305  public:
306  explicit ExponentialMovingAverage(double decaying_factor)
307  : decaying_factor_(decaying_factor) {
308  DCHECK_GE(decaying_factor, 0.0);
309  DCHECK_LE(decaying_factor, 1.0);
310  }
311 
312  // Returns exponential moving average for all the added data so far.
313  double CurrentAverage() const { return average_; }
314 
315  // Returns the total number of added records so far.
316  int64_t NumRecords() const { return num_records_; }
317 
318  void AddData(double new_record);
319 
320  private:
321  double average_ = 0.0;
322  int64_t num_records_ = 0;
323  const double decaying_factor_;
324 };
325 
326 // Utility to calculate percentile (First variant) for limited number of
327 // records. Reference: https://en.wikipedia.org/wiki/Percentile
328 //
329 // After the vector is sorted, we assume that the element with index i
330 // correspond to the percentile 100*(i+0.5)/size. For percentiles before the
331 // first element (resp. after the last one) we return the first element (resp.
332 // the last). And otherwise we do a linear interpolation between the two element
333 // around the asked percentile.
334 class Percentile {
335  public:
336  explicit Percentile(int record_limit) : record_limit_(record_limit) {}
337 
338  void AddRecord(double record);
339 
340  // Returns number of stored records.
341  int64_t NumRecords() const { return records_.size(); }
342 
343  // Note that this is not fast and runs in O(n log n) for n records.
344  double GetPercentile(double percent);
345 
346  private:
347  std::deque<double> records_;
348  const int record_limit_;
349 };
350 
351 // This method tries to compress a list of tuples by merging complementary
352 // tuples, that is a set of tuples that only differ on one variable, and that
353 // cover the domain of the variable. In that case, it will keep only one tuple,
354 // and replace the value for variable by any_value, the equivalent of '*' in
355 // regexps.
356 //
357 // This method is exposed for testing purposes.
359 void CompressTuples(absl::Span<const int64_t> domain_sizes,
360  std::vector<std::vector<int64_t>>* tuples);
361 
362 // Similar to CompressTuples() but produces a final table where each cell is
363 // a set of value. This should result in a table that can still be encoded
364 // efficiently in SAT but with less tuples and thus less extra Booleans. Note
365 // that if a set of value is empty, it is interpreted at "any" so we can gain
366 // some space.
367 //
368 // The passed tuples vector is used as temporary memory and is detroyed.
369 // We interpret kTableAnyValue as an "any" tuple.
370 //
371 // TODO(user): To reduce memory, we could return some absl::Span in the last
372 // layer instead of vector.
373 //
374 // TODO(user): The final compression is depend on the order of the variables.
375 // For instance the table (1,1)(1,2)(1,3),(1,4),(2,3) can either be compressed
376 // as (1,*)(2,3) or (1,{1,2,4})({1,3},3). More experiment are needed to devise
377 // a better heuristic. It might for example be good to call CompressTuples()
378 // first.
379 std::vector<std::vector<absl::InlinedVector<int64_t, 2>>> FullyCompressTuples(
380  absl::Span<const int64_t> domain_sizes,
381  std::vector<std::vector<int64_t>>* tuples);
382 
383 // ============================================================================
384 // Implementation.
385 // ============================================================================
386 
387 inline int64_t SafeDoubleToInt64(double value) {
388  if (std::isnan(value)) return 0;
389  if (value >= static_cast<double>(std::numeric_limits<int64_t>::max())) {
391  }
392  if (value <= static_cast<double>(std::numeric_limits<int64_t>::min())) {
394  }
395  return static_cast<int64_t>(value);
396 }
397 
398 // Tells whether a int128 can be casted to a int64_t that can be negated.
399 inline bool IsNegatableInt64(absl::int128 x) {
400  return x <= absl::int128(std::numeric_limits<int64_t>::max()) &&
401  x > absl::int128(std::numeric_limits<int64_t>::min());
402 }
403 
404 // These functions are copied from MathUtils. However, the original ones are
405 // incompatible with absl::int128 as MathLimits<absl::int128>::kIsInteger ==
406 // false.
407 template <typename IntType, bool ceil>
408 IntType CeilOrFloorOfRatio(IntType numerator, IntType denominator) {
409  static_assert(std::numeric_limits<IntType>::is_integer,
410  "CeilOfRatio is only defined for integral types");
411  DCHECK_NE(0, denominator) << "Division by zero is not supported.";
412  DCHECK(numerator != std::numeric_limits<IntType>::min() || denominator != -1)
413  << "Dividing " << numerator << "by -1 is not supported: it would SIGFPE";
414 
415  const IntType rounded_toward_zero = numerator / denominator;
416  const bool needs_round = (numerator % denominator) != 0;
417  const bool same_sign = (numerator >= 0) == (denominator >= 0);
418 
419  if (ceil) { // Compile-time condition: not an actual branching
420  return rounded_toward_zero + static_cast<IntType>(same_sign && needs_round);
421  } else {
422  return rounded_toward_zero -
423  static_cast<IntType>(!same_sign && needs_round);
424  }
425 }
426 
427 template <typename IntType>
428 IntType CeilOfRatio(IntType numerator, IntType denominator) {
429  return CeilOrFloorOfRatio<IntType, true>(numerator, denominator);
430 }
431 
432 template <typename IntType>
433 IntType FloorOfRatio(IntType numerator, IntType denominator) {
434  return CeilOrFloorOfRatio<IntType, false>(numerator, denominator);
435 }
436 
437 } // namespace sat
438 } // namespace operations_research
439 
440 #endif // OR_TOOLS_SAT_UTIL_H_
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
We call domain any subset of Int64 = [kint64min, kint64max].
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
Result Solve(const std::vector< Domain > &domains, const std::vector< int64_t > &coeffs, const std::vector< int64_t > &costs, const Domain &rhs)
Definition: sat/util.cc:546
ExponentialMovingAverage(double decaying_factor)
Definition: sat/util.h:306
IncrementalAverage(double initial_average)
Definition: sat/util.h:283
void AddChoices(absl::Span< const int64_t > choices)
Definition: sat/util.cc:440
void AddMultiples(int64_t coeff, int64_t max_value)
Definition: sat/util.cc:464
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
double GetPercentile(double percent)
Definition: sat/util.cc:361
int64_t b
int64_t a
SatParameters parameters
int64_t value
GRBmodel * model
Definition: cleanup.h:22
void RandomizeDecisionHeuristic(absl::BitGenRef random, SatParameters *parameters)
Definition: sat/util.cc:61
int64_t ClosestMultiple(int64_t value, int64_t base)
Definition: sat/util.cc:228
IntType CeilOfRatio(IntType numerator, IntType denominator)
Definition: sat/util.h:428
void CompressTuples(absl::Span< const int64_t > domain_sizes, std::vector< std::vector< int64_t >> *tuples)
Definition: sat/util.cc:386
std::vector< std::vector< absl::InlinedVector< int64_t, 2 > > > FullyCompressTuples(absl::Span< const int64_t > domain_sizes, std::vector< std::vector< int64_t >> *tuples)
Definition: sat/util.cc:783
bool IsNegatableInt64(absl::int128 x)
Definition: sat/util.h:399
int64_t PositiveMod(int64_t x, int64_t m)
Definition: sat/util.cc:137
IntType FloorOfRatio(IntType numerator, IntType denominator)
Definition: sat/util.h:433
int64_t CeilSquareRoot(int64_t a)
Definition: sat/util.cc:220
bool SolveDiophantineEquationOfSizeTwo(int64_t &a, int64_t &b, int64_t &cte, int64_t &x0, int64_t &y0)
Definition: sat/util.cc:164
std::string FormatCounter(int64_t num)
Definition: sat/util.cc:48
int64_t FloorSquareRoot(int64_t a)
Definition: sat/util.cc:211
constexpr int64_t kTableAnyValue
Definition: sat/util.h:358
int64_t SafeDoubleToInt64(double value)
Definition: sat/util.h:387
int64_t ModularInverse(int64_t x, int64_t m)
Definition: sat/util.cc:104
int64_t ProductWithModularInverse(int64_t coeff, int64_t mod, int64_t rhs)
Definition: sat/util.cc:142
int MoveOneUnprocessedLiteralLast(const absl::btree_set< LiteralIndex > &processed, int relevant_prefix_size, std::vector< Literal > *literals)
Definition: sat/util.cc:299
IntType CeilOrFloorOfRatio(IntType numerator, IntType denominator)
Definition: sat/util.h:408
bool LinearInequalityCanBeReducedWithClosestMultiple(int64_t base, const std::vector< int64_t > &coeffs, const std::vector< int64_t > &lbs, const std::vector< int64_t > &ubs, int64_t rhs, int64_t *new_rhs)
Definition: sat/util.cc:235
Collection of objects used to extend the Constraint Solver library.
std::mt19937_64 random_engine_t
Definition: random_engine.h:23
int64_t bound
int64_t cost