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