OR-Tools  9.3
sat/util.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_UTIL_H_
15#define OR_TOOLS_SAT_UTIL_H_
16
17#include <cstdint>
18#include <deque>
19#include <vector>
20
22#if !defined(__PORTABLE_PLATFORM__)
23#include "google/protobuf/descriptor.h"
24#endif // __PORTABLE_PLATFORM__
25#include "absl/container/btree_set.h"
26#include "absl/random/bit_gen_ref.h"
27#include "absl/random/random.h"
28#include "absl/types/span.h"
29#include "ortools/sat/model.h"
31#include "ortools/sat/sat_parameters.pb.h"
34
35namespace operations_research {
36namespace sat {
37
38// Returns a in [0, m) such that a * x = 1 modulo m.
39// If gcd(x, m) != 1, there is no inverse, and it returns 0.
40//
41// This DCHECK that x is in [0, m).
42// This is integer overflow safe.
43//
44// Note(user): I didn't find this in a easily usable standard library.
45int64_t ModularInverse(int64_t x, int64_t m);
46
47// Just returns x % m but with a result always in [0, m).
48int64_t PositiveMod(int64_t x, int64_t m);
49
50// If we know that X * coeff % mod = rhs % mod, this returns c such that
51// PositiveMod(X, mod) = c.
52//
53// This requires coeff != 0, mod !=0 and gcd(coeff, mod) == 1.
54// The result will be in [0, mod) but there is no other condition on the sign or
55// magnitude of a and b.
56//
57// This is overflow safe, and when rhs == 0 or abs(mod) == 1, it returns 0.
58int64_t ProductWithModularInverse(int64_t coeff, int64_t mod, int64_t rhs);
59
60// Returns true if the equation a * X + b * Y = cte has some integer solutions.
61// For now, we check that a and b are different from 0 and from int64_t min.
62//
63// There is actually always a solution if cte % gcd(|a|, |b|) == 0. And because
64// a, b and cte fit on an int64_t, if there is a solution, there is one with X
65// and Y fitting on an int64_t.
66//
67// We will divide everything by gcd(a, b) first, so it is why we take reference
68// and the equation can change.
69//
70// If there are solutions, we return one of them (x0, y0).
71// From any such solution, the set of all solutions is given for Z integer by:
72// X = x0 + b * Z;
73// Y = y0 - a * Z;
74//
75// Given a domain for X and Y, it is possible to compute the "exact" domain of Z
76// with our Domain functions. Note however that this will only compute solution
77// where both x-x0 and y-y0 do fit on an int64_t:
78// DomainOf(x).SubtractionWith(x0).InverseMultiplicationBy(b).IntersectionWith(
79// DomainOf(y).SubtractionWith(y0).InverseMultiplicationBy(-a))
80bool SolveDiophantineEquationOfSizeTwo(int64_t& a, int64_t& b, int64_t& cte,
81 int64_t& x0, int64_t& y0);
82
83// The argument must be non-negative.
84int64_t FloorSquareRoot(int64_t a);
85int64_t CeilSquareRoot(int64_t a);
86
87// Returns the multiple of base closest to value. If there is a tie, we return
88// the one closest to zero. This way we have ClosestMultiple(x) =
89// -ClosestMultiple(-x) which is important for how this is used.
90int64_t ClosestMultiple(int64_t value, int64_t base);
91
92// Given a linear equation "sum coeff_i * X_i <= rhs. We can rewrite it using
93// ClosestMultiple() as "base * new_terms + error <= rhs" where error can be
94// bounded using the provided bounds on each variables. This will return true if
95// the error can be ignored and this equation is completely equivalent to
96// new_terms <= new_rhs.
97//
98// This is useful for cases like 9'999 X + 10'0001 Y <= 155'000 where we have
99// weird coefficient (maybe due to scaling). With a base of 10K, this is
100// equivalent to X + Y <= 15.
101//
102// Preconditions: All coeffs are assumed to be positive. You can easily negate
103// all the negative coeffs and corresponding bounds before calling this.
105 int64_t base, const std::vector<int64_t>& coeffs,
106 const std::vector<int64_t>& lbs, const std::vector<int64_t>& ubs,
107 int64_t rhs, int64_t* new_rhs);
108
109// The model "singleton" random engine used in the solver.
110//
111// In test, we usually set use_absl_random() so that the sequence is changed at
112// each invocation. This way, clients do not relly on the wrong assumption that
113// a particular optimal solution will be returned if they are many equivalent
114// ones.
115class ModelRandomGenerator : public absl::BitGenRef {
116 public:
117 // We seed the strategy at creation only. This should be enough for our use
118 // case since the SatParameters is set first before the solver is created. We
119 // also never really need to change the seed afterwards, it is just used to
120 // diversify solves with identical parameters on different Model objects.
122 : absl::BitGenRef(deterministic_random_) {
123 const auto& params = *model->GetOrCreate<SatParameters>();
124 deterministic_random_.seed(params.random_seed());
125 if (params.use_absl_random()) {
126 absl_random_ = absl::BitGen(absl::SeedSeq({params.random_seed()}));
127 absl::BitGenRef::operator=(absl::BitGenRef(absl_random_));
128 }
129 }
130
131 // This is just used to display ABSL_RANDOM_SALT_OVERRIDE in the log so that
132 // it is possible to reproduce a failure more easily while looking at a solver
133 // log.
134 //
135 // TODO(user): I didn't find a cleaner way to log this.
136 void LogSalt() const {}
137
138 private:
139 random_engine_t deterministic_random_;
140 absl::BitGen absl_random_;
141};
142
143// The model "singleton" shared time limit.
145 public:
147 : SharedTimeLimit(model->GetOrCreate<TimeLimit>()) {}
148};
149
150// Randomizes the decision heuristic of the given SatParameters.
151void RandomizeDecisionHeuristic(absl::BitGenRef random,
152 SatParameters* parameters);
153
154// Context: this function is not really generic, but required to be unit-tested.
155// It is used in a clause minimization algorithm when we try to detect if any of
156// the clause literals can be propagated by a subset of the other literal being
157// false. For that, we want to enqueue in the solver all the subset of size n-1.
158//
159// This moves one of the unprocessed literal from literals to the last position.
160// The function tries to do that while preserving the longest possible prefix of
161// literals "amortized" through the calls assuming that we want to move each
162// literal to the last position once.
163//
164// For a vector of size n, if we want to call this n times so that each literal
165// is last at least once, the sum of the size of the changed suffixes will be
166// O(n log n). If we were to use a simpler algorithm (like moving the last
167// unprocessed literal to the last position), this sum would be O(n^2).
168//
169// Returns the size of the common prefix of literals before and after the move,
170// or -1 if all the literals are already processed. The argument
171// relevant_prefix_size is used as a hint when keeping more that this prefix
172// size do not matter. The returned value will always be lower or equal to
173// relevant_prefix_size.
175 const absl::btree_set<LiteralIndex>& processed, int relevant_prefix_size,
176 std::vector<Literal>* literals);
177
178// ============================================================================
179// Implementation.
180// ============================================================================
181
182// Manages incremental averages.
184 public:
185 // Initializes the average with 'initial_average' and number of records to 0.
186 explicit IncrementalAverage(double initial_average)
187 : average_(initial_average) {}
189
190 // Sets the number of records to 0 and average to 'reset_value'.
191 void Reset(double reset_value);
192
193 double CurrentAverage() const { return average_; }
194 int64_t NumRecords() const { return num_records_; }
195
196 void AddData(double new_record);
197
198 private:
199 double average_ = 0.0;
200 int64_t num_records_ = 0;
201};
202
203// Manages exponential moving averages defined as
204// new_average = decaying_factor * old_average
205// + (1 - decaying_factor) * new_record.
206// where 0 < decaying_factor < 1.
208 public:
209 explicit ExponentialMovingAverage(double decaying_factor)
210 : decaying_factor_(decaying_factor) {
211 DCHECK_GE(decaying_factor, 0.0);
212 DCHECK_LE(decaying_factor, 1.0);
213 }
214
215 // Returns exponential moving average for all the added data so far.
216 double CurrentAverage() const { return average_; }
217
218 // Returns the total number of added records so far.
219 int64_t NumRecords() const { return num_records_; }
220
221 void AddData(double new_record);
222
223 private:
224 double average_ = 0.0;
225 int64_t num_records_ = 0;
226 const double decaying_factor_;
227};
228
229// Utility to calculate percentile (First variant) for limited number of
230// records. Reference: https://en.wikipedia.org/wiki/Percentile
231//
232// After the vector is sorted, we assume that the element with index i
233// correspond to the percentile 100*(i+0.5)/size. For percentiles before the
234// first element (resp. after the last one) we return the first element (resp.
235// the last). And otherwise we do a linear interpolation between the two element
236// around the asked percentile.
238 public:
239 explicit Percentile(int record_limit) : record_limit_(record_limit) {}
240
241 void AddRecord(double record);
242
243 // Returns number of stored records.
244 int64_t NumRecords() const { return records_.size(); }
245
246 // Note that this is not fast and runs in O(n log n) for n records.
247 double GetPercentile(double percent);
248
249 private:
250 std::deque<double> records_;
251 const int record_limit_;
252};
253
254// This method tries to compress a list of tuples by merging complementary
255// tuples, that is a set of tuples that only differ on one variable, and that
256// cover the domain of the variable. In that case, it will keep only one tuple,
257// and replace the value for variable by any_value, the equivalent of '*' in
258// regexps.
259//
260// This method is exposed for testing purposes.
261void CompressTuples(absl::Span<const int64_t> domain_sizes, int64_t any_value,
262 std::vector<std::vector<int64_t>>* tuples);
263
264} // namespace sat
265} // namespace operations_research
266
267#endif // OR_TOOLS_SAT_UTIL_H_
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:893
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:895
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
ExponentialMovingAverage(double decaying_factor)
Definition: sat/util.h:209
IncrementalAverage(double initial_average)
Definition: sat/util.h:186
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
double GetPercentile(double percent)
Definition: sat/util.cc:345
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:59
int64_t ClosestMultiple(int64_t value, int64_t base)
Definition: sat/util.cc:211
void CompressTuples(absl::Span< const int64_t > domain_sizes, int64_t any_value, std::vector< std::vector< int64_t > > *tuples)
Definition: sat/util.cc:370
int64_t PositiveMod(int64_t x, int64_t m)
Definition: sat/util.cc:120
int64_t CeilSquareRoot(int64_t a)
Definition: sat/util.cc:203
bool SolveDiophantineEquationOfSizeTwo(int64_t &a, int64_t &b, int64_t &cte, int64_t &x0, int64_t &y0)
Definition: sat/util.cc:147
int64_t FloorSquareRoot(int64_t a)
Definition: sat/util.cc:194
int64_t ModularInverse(int64_t x, int64_t m)
Definition: sat/util.cc:87
int64_t ProductWithModularInverse(int64_t coeff, int64_t mod, int64_t rhs)
Definition: sat/util.cc:125
int MoveOneUnprocessedLiteralLast(const absl::btree_set< LiteralIndex > &processed, int relevant_prefix_size, std::vector< Literal > *literals)
Definition: sat/util.cc:282
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:218
Collection of objects used to extend the Constraint Solver library.
std::mt19937 random_engine_t
Definition: random_engine.h:23
const double coeff