OR-Tools  9.0
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"
23 #include "ortools/sat/sat_base.h"
26 
27 #if !defined(__PORTABLE_PLATFORM__)
28 #include "google/protobuf/descriptor.h"
29 #endif // __PORTABLE_PLATFORM__
30 
31 namespace operations_research {
32 namespace sat {
33 
34 // The model "singleton" random engine used in the solver.
35 //
36 // In test, we usually set use_absl_random() so that the sequence is changed at
37 // each invocation. This way, clients do not relly on the wrong assumption that
38 // a particular optimal solution will be returned if they are many equivalent
39 // ones.
40 class ModelRandomGenerator : public absl::BitGenRef {
41  public:
42  // We seed the strategy at creation only. This should be enough for our use
43  // case since the SatParameters is set first before the solver is created. We
44  // also never really need to change the seed afterwards, it is just used to
45  // diversify solves with identical parameters on different Model objects.
47  : absl::BitGenRef(deterministic_random_) {
48  const auto& params = *model->GetOrCreate<SatParameters>();
49  deterministic_random_.seed(params.random_seed());
50  if (params.use_absl_random()) {
51  absl_random_ = absl::BitGen(absl::SeedSeq({params.random_seed()}));
52  absl::BitGenRef::operator=(absl::BitGenRef(absl_random_));
53  }
54  }
55 
56  // This is just used to display ABSL_RANDOM_SALT_OVERRIDE in the log so that
57  // it is possible to reproduce a failure more easily while looking at a solver
58  // log.
59  //
60  // TODO(user): I didn't find a cleaner way to log this.
61  void LogSalt() const {}
62 
63  private:
64  random_engine_t deterministic_random_;
65  absl::BitGen absl_random_;
66 };
67 
68 // Randomizes the decision heuristic of the given SatParameters.
69 template <typename URBG>
70 void RandomizeDecisionHeuristic(URBG* random, SatParameters* parameters);
71 
72 // Context: this function is not really generic, but required to be unit-tested.
73 // It is used in a clause minimization algorithm when we try to detect if any of
74 // the clause literals can be propagated by a subset of the other literal being
75 // false. For that, we want to enqueue in the solver all the subset of size n-1.
76 //
77 // This moves one of the unprocessed literal from literals to the last position.
78 // The function tries to do that while preserving the longest possible prefix of
79 // literals "amortized" through the calls assuming that we want to move each
80 // literal to the last position once.
81 //
82 // For a vector of size n, if we want to call this n times so that each literal
83 // is last at least once, the sum of the size of the changed suffixes will be
84 // O(n log n). If we were to use a simpler algorithm (like moving the last
85 // unprocessed literal to the last position), this sum would be O(n^2).
86 //
87 // Returns the size of the common prefix of literals before and after the move,
88 // or -1 if all the literals are already processed. The argument
89 // relevant_prefix_size is used as a hint when keeping more that this prefix
90 // size do not matter. The returned value will always be lower or equal to
91 // relevant_prefix_size.
92 int MoveOneUnprocessedLiteralLast(const std::set<LiteralIndex>& processed,
93  int relevant_prefix_size,
94  std::vector<Literal>* literals);
95 
96 // ============================================================================
97 // Implementation.
98 // ============================================================================
99 
100 template <typename URBG>
101 inline void RandomizeDecisionHeuristic(URBG* random,
102  SatParameters* parameters) {
103 #if !defined(__PORTABLE_PLATFORM__)
104  // Random preferred variable order.
105  const google::protobuf::EnumDescriptor* order_d =
106  SatParameters::VariableOrder_descriptor();
107  parameters->set_preferred_variable_order(
108  static_cast<SatParameters::VariableOrder>(
109  order_d->value(absl::Uniform(*random, 0, order_d->value_count()))
110  ->number()));
111 
112  // Random polarity initial value.
113  const google::protobuf::EnumDescriptor* polarity_d =
114  SatParameters::Polarity_descriptor();
115  parameters->set_initial_polarity(static_cast<SatParameters::Polarity>(
116  polarity_d->value(absl::Uniform(*random, 0, polarity_d->value_count()))
117  ->number()));
118 #endif // __PORTABLE_PLATFORM__
119  // Other random parameters.
120  parameters->set_use_phase_saving(absl::Bernoulli(*random, 0.5));
121  parameters->set_random_polarity_ratio(absl::Bernoulli(*random, 0.5) ? 0.01
122  : 0.0);
123  parameters->set_random_branches_ratio(absl::Bernoulli(*random, 0.5) ? 0.01
124  : 0.0);
125 }
126 
127 // Manages incremental averages.
129  public:
130  // Initializes the average with 'initial_average' and number of records to 0.
131  explicit IncrementalAverage(double initial_average)
132  : average_(initial_average) {}
134 
135  // Sets the number of records to 0 and average to 'reset_value'.
136  void Reset(double reset_value);
137 
138  double CurrentAverage() const { return average_; }
139  int64_t NumRecords() const { return num_records_; }
140 
141  void AddData(double new_record);
142 
143  private:
144  double average_ = 0.0;
145  int64_t num_records_ = 0;
146 };
147 
148 // Manages exponential moving averages defined as
149 // new_average = decaying_factor * old_average
150 // + (1 - decaying_factor) * new_record.
151 // where 0 < decaying_factor < 1.
153  public:
154  explicit ExponentialMovingAverage(double decaying_factor)
155  : decaying_factor_(decaying_factor) {
156  DCHECK_GE(decaying_factor, 0.0);
157  DCHECK_LE(decaying_factor, 1.0);
158  }
159 
160  // Returns exponential moving average for all the added data so far.
161  double CurrentAverage() const { return average_; }
162 
163  // Returns the total number of added records so far.
164  int64_t NumRecords() const { return num_records_; }
165 
166  void AddData(double new_record);
167 
168  private:
169  double average_ = 0.0;
170  int64_t num_records_ = 0;
171  const double decaying_factor_;
172 };
173 
174 // Utility to calculate percentile (First variant) for limited number of
175 // records. Reference: https://en.wikipedia.org/wiki/Percentile
176 //
177 // After the vector is sorted, we assume that the element with index i
178 // correspond to the percentile 100*(i+0.5)/size. For percentiles before the
179 // first element (resp. after the last one) we return the first element (resp.
180 // the last). And otherwise we do a linear interpolation between the two element
181 // around the asked percentile.
182 class Percentile {
183  public:
184  explicit Percentile(int record_limit) : record_limit_(record_limit) {}
185 
186  void AddRecord(double record);
187 
188  // Returns number of stored records.
189  int64_t NumRecords() const { return records_.size(); }
190 
191  // Note that this is not fast and runs in O(n log n) for n records.
192  double GetPercentile(double percent);
193 
194  private:
195  std::deque<double> records_;
196  const int record_limit_;
197 };
198 
199 // This method tries to compress a list of tuples by merging complementary
200 // tuples, that is a set of tuples that only differ on one variable, and that
201 // cover the domain of the variable. In that case, it will keep only one tuple,
202 // and replace the value for variable by any_value, the equivalent of '*' in
203 // regexps.
204 //
205 // This method is exposed for testing purposes.
206 void CompressTuples(absl::Span<const int64_t> domain_sizes, int64_t any_value,
207  std::vector<std::vector<int64_t>>* tuples);
208 
209 } // namespace sat
210 } // namespace operations_research
211 
212 #endif // OR_TOOLS_SAT_UTIL_H_
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:895
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:897
ExponentialMovingAverage(double decaying_factor)
Definition: sat/util.h:154
IncrementalAverage(double initial_average)
Definition: sat/util.h:131
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
double GetPercentile(double percent)
Definition: sat/util.cc:88
void AddRecord(double record)
Definition: sat/util.cc:81
SatParameters parameters
GRBmodel * model
Definition: cleanup.h:22
int MoveOneUnprocessedLiteralLast(const std::set< LiteralIndex > &processed, int relevant_prefix_size, std::vector< Literal > *literals)
Definition: sat/util.cc:25
void CompressTuples(absl::Span< const int64_t > domain_sizes, int64_t any_value, std::vector< std::vector< int64_t >> *tuples)
Definition: sat/util.cc:113
void RandomizeDecisionHeuristic(URBG *random, SatParameters *parameters)
Definition: sat/util.h:101
Collection of objects used to extend the Constraint Solver library.
std::mt19937 random_engine_t
Definition: random_engine.h:23