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