OR-Tools  9.6
synchronization.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_SYNCHRONIZATION_H_
15 #define OR_TOOLS_SAT_SYNCHRONIZATION_H_
16 
17 #include <cstdint>
18 #include <deque>
19 #include <functional>
20 #include <limits>
21 #include <string>
22 #include <utility>
23 #include <vector>
24 
25 #include "absl/base/thread_annotations.h"
26 #include "absl/container/btree_map.h"
27 #include "absl/container/flat_hash_map.h"
28 #include "absl/container/flat_hash_set.h"
29 #include "absl/random/bit_gen_ref.h"
30 #include "absl/random/random.h"
31 #include "absl/synchronization/mutex.h"
32 #include "absl/time/time.h"
34 #include "ortools/base/logging.h"
35 #include "ortools/base/stl_util.h"
36 #include "ortools/base/timer.h"
37 #include "ortools/sat/cp_model.pb.h"
38 #include "ortools/sat/integer.h"
39 #include "ortools/sat/model.h"
40 #include "ortools/sat/sat_base.h"
41 #include "ortools/sat/sat_parameters.pb.h"
42 #include "ortools/sat/util.h"
43 #include "ortools/util/bitset.h"
44 #include "ortools/util/logging.h"
45 
46 namespace operations_research {
47 namespace sat {
48 
49 // Thread-safe. Keeps a set of n unique best solution found so far.
50 //
51 // TODO(user): Maybe add some criteria to only keep solution with an objective
52 // really close to the best solution.
53 template <typename ValueType>
55  public:
56  explicit SharedSolutionRepository(int num_solutions_to_keep)
57  : num_solutions_to_keep_(num_solutions_to_keep) {}
58 
59  // The solution format used by this class.
60  struct Solution {
61  // Solution with lower "rank" will be preferred
62  //
63  // TODO(user): Some LNS code assume that for the SharedSolutionRepository
64  // this rank is actually the unscaled internal minimization objective.
65  // Remove this assumptions by simply recomputing this value since it is not
66  // too costly to do so.
67  int64_t rank = 0;
68 
69  std::vector<ValueType> variable_values;
70 
71  std::string info;
72 
73  // Number of time this was returned by GetRandomBiasedSolution(). We use
74  // this information during the selection process.
75  //
76  // Should be private: only SharedSolutionRepository should modify this.
77  mutable int num_selected = 0;
78 
79  bool operator==(const Solution& other) const {
80  return rank == other.rank && variable_values == other.variable_values;
81  }
82  bool operator<(const Solution& other) const {
83  if (rank != other.rank) {
84  return rank < other.rank;
85  }
86  return variable_values < other.variable_values;
87  }
88  };
89 
90  // Returns the number of current solution in the pool. This will never
91  // decrease.
92  int NumSolutions() const;
93 
94  // Returns the solution #i where i must be smaller than NumSolutions().
95  Solution GetSolution(int index) const;
96 
97  // Returns the variable value of variable 'var_index' from solution
98  // 'solution_index' where solution_index must be smaller than NumSolutions()
99  // and 'var_index' must be smaller than number of variables.
100  ValueType GetVariableValueInSolution(int var_index, int solution_index) const;
101 
102  // Returns a random solution biased towards good solutions.
103  Solution GetRandomBiasedSolution(absl::BitGenRef random) const;
104 
105  // Add a new solution. Note that it will not be added to the pool of solution
106  // right away. One must call Synchronize for this to happen.
107  //
108  // Works in O(num_solutions_to_keep_).
109  void Add(const Solution& solution);
110 
111  // Updates the current pool of solution with the one recently added. Note that
112  // we use a stable ordering of solutions, so the final pool will be
113  // independent on the order of the calls to AddSolution() provided that the
114  // set of added solutions is the same.
115  //
116  // Works in O(num_solutions_to_keep_).
117  void Synchronize();
118 
119  protected:
120  // Helper method for adding the solutions once the mutex is acquired.
121  void AddInternal(const Solution& solution)
122  ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
123 
125  mutable absl::Mutex mutex_;
126  int64_t num_synchronization_ ABSL_GUARDED_BY(mutex_) = 0;
127 
128  // Our two solutions pools, the current one and the new one that will be
129  // merged into the current one on each Synchronize() calls.
130  mutable std::vector<int> tmp_indices_ ABSL_GUARDED_BY(mutex_);
131  std::vector<Solution> solutions_ ABSL_GUARDED_BY(mutex_);
132  std::vector<Solution> new_solutions_ ABSL_GUARDED_BY(mutex_);
133 };
134 
135 // This is currently only used to store feasible solution from our 'relaxation'
136 // LNS generators which in turn are used to generate some RINS neighborhood.
138  : public SharedSolutionRepository<int64_t> {
139  public:
140  explicit SharedRelaxationSolutionRepository(int num_solutions_to_keep)
141  : SharedSolutionRepository<int64_t>(num_solutions_to_keep) {}
142 
143  void NewRelaxationSolution(absl::Span<const int64_t> solution_values,
144  IntegerValue inner_objective_value);
145 };
146 
148  public:
149  explicit SharedLPSolutionRepository(int num_solutions_to_keep)
150  : SharedSolutionRepository<double>(num_solutions_to_keep) {}
151 
152  void NewLPSolution(std::vector<double> lp_solution);
153 };
154 
155 // Set of partly filled solutions. They are meant to be finished by some lns
156 // worker.
157 //
158 // The solutions are stored as a vector of doubles. The value at index i
159 // represents the solution value of model variable indexed i. Note that some
160 // values can be infinity which should be interpreted as 'unknown' solution
161 // value for that variable. These solutions can not necessarily be completed to
162 // complete feasible solutions.
164  public:
165  bool HasNewSolution() const;
166  std::vector<double> GetNewSolution();
167 
168  void AddNewSolution(const std::vector<double>& lp_solution);
169 
170  private:
171  // New solutions are added and removed from the back.
172  std::vector<std::vector<double>> solutions_;
173  mutable absl::Mutex mutex_;
174 };
175 
176 // Used by FillSolveStatsInResponse() to extract statistic to put in a
177 // CpSolverResponse. The callbacks registered here are supposed to only modify
178 // the statistic fields, nothing else.
180  std::vector<std::function<void(CpSolverResponse*)>> callbacks;
181 };
182 
183 // Get the solve statistics from the associated model classes and fills the
184 // response with them.
185 void FillSolveStatsInResponse(Model* model, CpSolverResponse* response);
186 
187 // Manages the global best response kept by the solver. This class is
188 // responsible for logging the progress of the solutions and bounds as they are
189 // found.
190 //
191 // All functions are thread-safe except if specified otherwise.
193  public:
194  explicit SharedResponseManager(Model* model);
195 
196  // Loads the initial objective bounds and keep a reference to the objective to
197  // properly display the scaled bounds. This is optional if the model has no
198  // objective.
199  //
200  // This function is not thread safe.
201  void InitializeObjective(const CpModelProto& cp_model);
202 
203  // Reports OPTIMAL and stop the search if any gap limit are specified and
204  // crossed. By default, we only stop when we have the true optimal, which is
205  // well defined since we are solving our pure integer problem exactly.
206  void SetGapLimitsFromParameters(const SatParameters& parameters);
207 
208  // Returns the current solver response. That is the best known response at the
209  // time of the call with the best feasible solution and objective bounds.
210  //
211  // We will do more postprocessing by calling all the
212  // AddFinalSolutionPostprocessor() postprocesors. Note that the response given
213  // to the AddSolutionCallback() will not call them.
214  CpSolverResponse GetResponse();
215 
216  // These will be called in REVERSE order on any feasible solution returned
217  // to the user.
219  std::function<void(std::vector<int64_t>*)> postprocessor);
220 
221  // These "postprocessing" steps will be applied in REVERSE order of
222  // registration to all solution passed to the callbacks.
224  std::function<void(CpSolverResponse*)> postprocessor);
225 
226  // These "postprocessing" steps will only be applied after the others to the
227  // solution returned by GetResponse().
229  std::function<void(CpSolverResponse*)> postprocessor);
230 
231  // Adds a callback that will be called on each new solution (for
232  // statisfiablity problem) or each improving new solution (for an optimization
233  // problem). Returns its id so it can be unregistered if needed.
234  //
235  // Note that adding a callback is not free since the solution will be
236  // postsolved before this is called.
237  //
238  // Note that currently the class is waiting for the callback to finish before
239  // accepting any new updates. That could be changed if needed.
241  std::function<void(const CpSolverResponse&)> callback);
242  void UnregisterCallback(int callback_id);
243 
244  // The "inner" objective is the CpModelProto objective without scaling/offset.
245  // Note that these bound correspond to valid bound for the problem of finding
246  // a strictly better objective than the current one. Thus the lower bound is
247  // always a valid bound for the global problem, but the upper bound is NOT.
248  IntegerValue GetInnerObjectiveLowerBound();
249  IntegerValue GetInnerObjectiveUpperBound();
250 
251  // These functions return the same as the non-synchronized() version but
252  // only the values at the last time Synchronize() was called.
253  void Synchronize();
256 
257  // Returns the current best solution inner objective value or kInt64Max if
258  // there is no solution.
259  IntegerValue BestSolutionInnerObjectiveValue();
260 
261  // Returns the integral of the log of the absolute gap over deterministic
262  // time. This is mainly used to compare how fast the gap closes on a
263  // particular instance. Or to evaluate how efficient our LNS code is improving
264  // solution.
265  //
266  // Note: The integral will start counting on the first UpdateGapIntegral()
267  // call, since before the difference is assumed to be zero.
268  //
269  // Important: To report a proper deterministic integral, we only update it
270  // on UpdateGapIntegral() which should be called in the main subsolver
271  // synchronization loop.
272  //
273  // Note(user): In the litterature, people use the relative gap to the optimal
274  // solution (or the best known one), but this is ill defined in many case
275  // (like if the optimal cost is zero), so I prefer this version.
276  double GapIntegral() const;
277  void UpdateGapIntegral();
278 
279  // Sets this to true to have the "real" but non-deterministic primal integral.
280  // If this is true, then there is no need to manually call
281  // UpdateGapIntegral() but it is not an issue to do so.
282  void SetUpdateGapIntegralOnEachChange(bool set);
283 
284  // Sets this to false, it you want new solutions to wait for the Synchronize()
285  // call.
286  // The default 'true' indicates that all solutions passed through
287  // NewSolution() are always propagated to the best response and to the
288  // solution manager.
289  void SetSynchronizationMode(bool always_synchronize);
290 
291  // Updates the inner objective bounds.
292  void UpdateInnerObjectiveBounds(const std::string& update_info,
293  IntegerValue lb, IntegerValue ub);
294 
295  // Reads the new solution from the response and update our state. For an
296  // optimization problem, we only do something if the solution is strictly
297  // improving.
298  void NewSolution(absl::Span<const int64_t> solution_values,
299  const std::string& solution_info, Model* model = nullptr);
300 
301  // Changes the solution to reflect the fact that the "improving" problem is
302  // infeasible. This means that if we have a solution, we have proven
303  // optimality, otherwise the global problem is infeasible.
304  //
305  // Note that this shouldn't be called before the solution is actually
306  // reported. We check for this case in NewSolution().
307  void NotifyThatImprovingProblemIsInfeasible(const std::string& worker_info);
308 
309  // Adds to the shared response a subset of assumptions that are enough to
310  // make the problem infeasible.
311  void AddUnsatCore(const std::vector<int>& core);
312 
313  // Returns true if we found the optimal solution or the problem was proven
314  // infeasible. Note that if the gap limit is reached, we will also report
315  // OPTIMAL and consider the problem solved.
316  bool ProblemIsSolved() const;
317 
318  // Returns the underlying solution repository where we keep a set of best
319  // solutions.
321  return solutions_;
322  }
324  return &solutions_;
325  }
326 
327  // Debug only. Set dump prefix for solutions written to file.
328  void set_dump_prefix(const std::string& dump_prefix) {
329  dump_prefix_ = dump_prefix;
330  }
331 
332  // Display improvement stats.
334 
335  void LogMessage(const std::string& prefix, const std::string& message);
336  void LogPeriodicMessage(const std::string& prefix, const std::string& message,
337  double frequency_seconds,
338  absl::Time* last_logging_time);
339  bool LoggingIsEnabled() const { return logger_->LoggingIsEnabled(); }
340 
341  void AppendResponseToBeMerged(const CpSolverResponse& response);
342 
343  std::atomic<bool>* first_solution_solvers_should_stop() {
344  return &first_solution_solvers_should_stop_;
345  }
346 
347  // We just store a loaded DebugSolution here. Note that this is supposed to be
348  // stored once and then never change, so we do not need a mutex.
349  void LoadDebugSolution(absl::Span<const int64_t> solution) {
350  debug_solution_.assign(solution.begin(), solution.end());
351  }
352  const std::vector<int64_t>& DebugSolution() const { return debug_solution_; }
353 
354  private:
355  void TestGapLimitsIfNeeded() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
356  void FillObjectiveValuesInResponse(CpSolverResponse* response) const
357  ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
358  void UpdateGapIntegralInternal() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
359 
360  void RegisterSolutionFound(const std::string& improvement_info)
361  ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
362  void RegisterObjectiveBoundImprovement(const std::string& improvement_info)
363  ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
364  void UpdateBestStatus(const CpSolverStatus& status)
365  ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
366 
367  // Generates a response for callbacks and GetResponse().
368  CpSolverResponse GetResponseInternal(
369  absl::Span<const int64_t> variable_values,
370  const std::string& solution_info) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
371 
372  const SatParameters& parameters_;
373  const WallTimer& wall_timer_;
374  ModelSharedTimeLimit* shared_time_limit_;
375  CpObjectiveProto const* objective_or_null_ = nullptr;
376 
377  mutable absl::Mutex mutex_;
378 
379  // Gap limits.
380  double absolute_gap_limit_ ABSL_GUARDED_BY(mutex_) = 0.0;
381  double relative_gap_limit_ ABSL_GUARDED_BY(mutex_) = 0.0;
382 
383  CpSolverStatus best_status_ ABSL_GUARDED_BY(mutex_) = CpSolverStatus::UNKNOWN;
384  CpSolverStatus synchronized_best_status_ ABSL_GUARDED_BY(mutex_) =
385  CpSolverStatus::UNKNOWN;
386  std::vector<int> unsat_cores_ ABSL_GUARDED_BY(mutex_);
387  SharedSolutionRepository<int64_t> solutions_ ABSL_GUARDED_BY(mutex_);
388 
389  int num_solutions_ ABSL_GUARDED_BY(mutex_) = 0;
390  int64_t inner_objective_lower_bound_ ABSL_GUARDED_BY(mutex_) =
391  std::numeric_limits<int64_t>::min();
392  int64_t inner_objective_upper_bound_ ABSL_GUARDED_BY(mutex_) =
393  std::numeric_limits<int64_t>::max();
394  int64_t best_solution_objective_value_ ABSL_GUARDED_BY(mutex_) =
395  std::numeric_limits<int64_t>::max();
396 
397  bool always_synchronize_ ABSL_GUARDED_BY(mutex_) = true;
398  IntegerValue synchronized_inner_objective_lower_bound_ ABSL_GUARDED_BY(
399  mutex_) = IntegerValue(std::numeric_limits<int64_t>::min());
400  IntegerValue synchronized_inner_objective_upper_bound_ ABSL_GUARDED_BY(
401  mutex_) = IntegerValue(std::numeric_limits<int64_t>::max());
402 
403  bool update_integral_on_each_change_ ABSL_GUARDED_BY(mutex_) = false;
404  double gap_integral_ ABSL_GUARDED_BY(mutex_) = 0.0;
405  double last_absolute_gap_ ABSL_GUARDED_BY(mutex_) = 0.0;
406  double last_gap_integral_time_stamp_ ABSL_GUARDED_BY(mutex_) = 0.0;
407 
408  int next_callback_id_ ABSL_GUARDED_BY(mutex_) = 0;
409  std::vector<std::pair<int, std::function<void(const CpSolverResponse&)>>>
410  callbacks_ ABSL_GUARDED_BY(mutex_);
411 
412  std::vector<std::function<void(std::vector<int64_t>*)>>
413  solution_postprocessors_ ABSL_GUARDED_BY(mutex_);
414  std::vector<std::function<void(CpSolverResponse*)>> postprocessors_
415  ABSL_GUARDED_BY(mutex_);
416  std::vector<std::function<void(CpSolverResponse*)>> final_postprocessors_
417  ABSL_GUARDED_BY(mutex_);
418 
419  // Dump prefix.
420  std::string dump_prefix_;
421 
422  // Used for statistics of the improvements found by workers.
423  absl::btree_map<std::string, int> primal_improvements_count_
424  ABSL_GUARDED_BY(mutex_);
425  absl::btree_map<std::string, int> dual_improvements_count_
426  ABSL_GUARDED_BY(mutex_);
427 
428  SolverLogger* logger_;
429  std::vector<CpSolverResponse> subsolver_responses_ ABSL_GUARDED_BY(mutex_);
430 
431  std::atomic<bool> first_solution_solvers_should_stop_ = false;
432 
433  std::vector<int64_t> debug_solution_;
434 };
435 
436 // This class manages a pool of lower and upper bounds on a set of variables in
437 // a parallel context.
439  public:
440  explicit SharedBoundsManager(const CpModelProto& model_proto);
441 
442  // Reports a set of locally improved variable bounds to the shared bounds
443  // manager. The manager will compare these bounds changes against its
444  // global state, and incorporate the improving ones.
445  void ReportPotentialNewBounds(const std::string& worker_name,
446  const std::vector<int>& variables,
447  const std::vector<int64_t>& new_lower_bounds,
448  const std::vector<int64_t>& new_upper_bounds);
449 
450  // If we solved a small independent component of the full problem, then we can
451  // in most situation fix the solution on this subspace.
452  //
453  // Note that because there can be more than one optimal solution on an
454  // independent subproblem, it is important to do that in a locked fashion, and
455  // reject future incompatible fixing.
456  void FixVariablesFromPartialSolution(
457  const std::vector<int64_t>& solution,
458  const std::vector<int>& variables_to_fix);
459 
460  // Returns a new id to be used in GetChangedBounds(). This is just an ever
461  // increasing sequence starting from zero. Note that the class is not designed
462  // to have too many of these.
463  int RegisterNewId();
464 
465  // When called, returns the set of bounds improvements since
466  // the last time this method was called with the same id.
467  void GetChangedBounds(int id, std::vector<int>* variables,
468  std::vector<int64_t>* new_lower_bounds,
469  std::vector<int64_t>* new_upper_bounds);
470 
471  // Publishes any new bounds so that GetChangedBounds() will reflect the latest
472  // state.
473  void Synchronize();
474 
475  void LogStatistics(SolverLogger* logger);
476  int NumBoundsExported(const std::string& worker_name);
477 
478  // If non-empty, we will check that all bounds update contains this solution.
479  // Note that this might fail once we reach optimality and we might have wrong
480  // bounds, but if it fail before that it can help find bugs.
481  void LoadDebugSolution(absl::Span<const int64_t> solution) {
482  debug_solution_.assign(solution.begin(), solution.end());
483  }
484 
485  private:
486  const int num_variables_;
487  const CpModelProto& model_proto_;
488 
489  absl::Mutex mutex_;
490 
491  // These are always up to date.
492  std::vector<int64_t> lower_bounds_ ABSL_GUARDED_BY(mutex_);
493  std::vector<int64_t> upper_bounds_ ABSL_GUARDED_BY(mutex_);
494  SparseBitset<int> changed_variables_since_last_synchronize_
495  ABSL_GUARDED_BY(mutex_);
496 
497  // These are only updated on Synchronize().
498  std::vector<int64_t> synchronized_lower_bounds_ ABSL_GUARDED_BY(mutex_);
499  std::vector<int64_t> synchronized_upper_bounds_ ABSL_GUARDED_BY(mutex_);
500  std::deque<SparseBitset<int>> id_to_changed_variables_
501  ABSL_GUARDED_BY(mutex_);
502  absl::btree_map<std::string, int> bounds_exported_ ABSL_GUARDED_BY(mutex_);
503 
504  std::vector<int64_t> debug_solution_;
505 };
506 
507 // This class holds all the binary clauses that were found and shared by the
508 // workers.
509 //
510 // It is thread-safe.
511 //
512 // Note that this uses literal as encoded in a cp_model.proto. Thus, the
513 // literals can be negative numbers.
515  public:
516  explicit SharedClausesManager(bool always_synchronize);
517  void AddBinaryClause(int id, int lit1, int lit2);
518 
519  // Fills new_clauses with
520  // {{lit1 of clause1, lit2 of clause1},
521  // {lit1 of clause2, lit2 of clause2},
522  // ...}
523  void GetUnseenBinaryClauses(int id,
524  std::vector<std::pair<int, int>>* new_clauses);
525 
526  // Ids are used to identify which worker is exporting/importing clauses.
527  int RegisterNewId();
528  void SetWorkerNameForId(int id, const std::string& worker_name);
529 
530  // Search statistics.
531  void LogStatistics(SolverLogger* logger);
532 
533  // Unlocks waiting binary clauses for workers if always_synchronize is false.
534  void Synchronize();
535 
536  private:
537  absl::Mutex mutex_;
538  // Cache to avoid adding the same clause twice.
539  absl::flat_hash_set<std::pair<int, int>> added_binary_clauses_set_
540  ABSL_GUARDED_BY(mutex_);
541  std::vector<std::pair<int, int>> added_binary_clauses_
542  ABSL_GUARDED_BY(mutex_);
543  std::vector<int> id_to_last_processed_binary_clause_ ABSL_GUARDED_BY(mutex_);
544  std::vector<int64_t> id_to_clauses_exported_;
545  int last_visible_clause_ ABSL_GUARDED_BY(mutex_) = 0;
546  const bool always_synchronize_ = true;
547 
548  // Used for reporting statistics.
549  absl::flat_hash_map<int, std::string> id_to_worker_name_;
550 };
551 
552 // Simple class to add statistics by name and print them at the end.
554  public:
555  SharedStatistics() = default;
556 
557  // Adds a bunch of stats, adding count for the same key together.
558  void AddStats(absl::Span<const std::pair<std::string, int64_t>> stats);
559 
560  // Logs all the added stats.
561  void Log(SolverLogger* logger);
562 
563  private:
564  absl::Mutex mutex_;
565  absl::flat_hash_map<std::string, int64_t> stats_ ABSL_GUARDED_BY(mutex_);
566 };
567 
568 template <typename ValueType>
570  absl::MutexLock mutex_lock(&mutex_);
571  return solutions_.size();
572 }
573 
574 template <typename ValueType>
577  absl::MutexLock mutex_lock(&mutex_);
578  return solutions_[i];
579 }
580 
581 template <typename ValueType>
583  int var_index, int solution_index) const {
584  absl::MutexLock mutex_lock(&mutex_);
585  return solutions_[solution_index].variable_values[var_index];
586 }
587 
588 // TODO(user): Experiments on the best distribution.
589 template <typename ValueType>
592  absl::BitGenRef random) const {
593  absl::MutexLock mutex_lock(&mutex_);
594  const int64_t best_rank = solutions_[0].rank;
595 
596  // As long as we have solution with the best objective that haven't been
597  // explored too much, we select one uniformly. Otherwise, we select a solution
598  // from the pool uniformly.
599  //
600  // Note(user): Because of the increase of num_selected, this is dependent on
601  // the order of call. It should be fine for "determinism" because we do
602  // generate the task of a batch always in the same order.
603  const int kExplorationThreshold = 100;
604 
605  // Select all the best solution with a low enough selection count.
606  tmp_indices_.clear();
607  for (int i = 0; i < solutions_.size(); ++i) {
608  const auto& solution = solutions_[i];
609  if (solution.rank == best_rank &&
610  solution.num_selected <= kExplorationThreshold) {
611  tmp_indices_.push_back(i);
612  }
613  }
614 
615  int index = 0;
616  if (tmp_indices_.empty()) {
617  index = absl::Uniform<int>(random, 0, solutions_.size());
618  } else {
619  index = tmp_indices_[absl::Uniform<int>(random, 0, tmp_indices_.size())];
620  }
621  solutions_[index].num_selected++;
622  return solutions_[index];
623 }
624 
625 template <typename ValueType>
627  if (num_solutions_to_keep_ <= 0) return;
628  absl::MutexLock mutex_lock(&mutex_);
629  AddInternal(solution);
630 }
631 
632 template <typename ValueType>
634  const Solution& solution) {
635  int worse_solution_index = 0;
636  for (int i = 0; i < new_solutions_.size(); ++i) {
637  // Do not add identical solution.
638  if (new_solutions_[i] == solution) return;
639  if (new_solutions_[worse_solution_index] < new_solutions_[i]) {
640  worse_solution_index = i;
641  }
642  }
643  if (new_solutions_.size() < num_solutions_to_keep_) {
644  new_solutions_.push_back(solution);
645  } else if (solution < new_solutions_[worse_solution_index]) {
646  new_solutions_[worse_solution_index] = solution;
647  }
648 }
649 
650 template <typename ValueType>
652  absl::MutexLock mutex_lock(&mutex_);
653  if (new_solutions_.empty()) return;
654 
655  solutions_.insert(solutions_.end(), new_solutions_.begin(),
656  new_solutions_.end());
657  new_solutions_.clear();
658 
659  // We use a stable sort to keep the num_selected count for the already
660  // existing solutions.
661  //
662  // TODO(user): Introduce a notion of orthogonality to diversify the pool?
664  if (solutions_.size() > num_solutions_to_keep_) {
665  solutions_.resize(num_solutions_to_keep_);
666  }
667 
668  if (!solutions_.empty()) {
669  VLOG(2) << "Solution pool update:"
670  << " num_solutions=" << solutions_.size()
671  << " min_rank=" << solutions_[0].rank
672  << " max_rank=" << solutions_.back().rank;
673  }
674 
675  num_synchronization_++;
676 }
677 
678 } // namespace sat
679 } // namespace operations_research
680 
681 #endif // OR_TOOLS_SAT_SYNCHRONIZATION_H_
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
void LoadDebugSolution(absl::Span< const int64_t > solution)
void AddNewSolution(const std::vector< double > &lp_solution)
void NewLPSolution(std::vector< double > lp_solution)
void NewRelaxationSolution(absl::Span< const int64_t > solution_values, IntegerValue inner_objective_value)
SharedSolutionRepository< int64_t > * MutableSolutionsRepository()
void InitializeObjective(const CpModelProto &cp_model)
void LogPeriodicMessage(const std::string &prefix, const std::string &message, double frequency_seconds, absl::Time *last_logging_time)
void AddSolutionPostprocessor(std::function< void(std::vector< int64_t > *)> postprocessor)
void AddFinalResponsePostprocessor(std::function< void(CpSolverResponse *)> postprocessor)
const SharedSolutionRepository< int64_t > & SolutionsRepository() const
void set_dump_prefix(const std::string &dump_prefix)
void LoadDebugSolution(absl::Span< const int64_t > solution)
std::atomic< bool > * first_solution_solvers_should_stop()
void NotifyThatImprovingProblemIsInfeasible(const std::string &worker_info)
void SetSynchronizationMode(bool always_synchronize)
void AddUnsatCore(const std::vector< int > &core)
void SetGapLimitsFromParameters(const SatParameters &parameters)
void AppendResponseToBeMerged(const CpSolverResponse &response)
void AddResponsePostprocessor(std::function< void(CpSolverResponse *)> postprocessor)
int AddSolutionCallback(std::function< void(const CpSolverResponse &)> callback)
void NewSolution(absl::Span< const int64_t > solution_values, const std::string &solution_info, Model *model=nullptr)
void LogMessage(const std::string &prefix, const std::string &message)
const std::vector< int64_t > & DebugSolution() const
void UpdateInnerObjectiveBounds(const std::string &update_info, IntegerValue lb, IntegerValue ub)
Solution GetRandomBiasedSolution(absl::BitGenRef random) const
std::vector< int > tmp_indices_ ABSL_GUARDED_BY(mutex_)
int64_t num_synchronization_ ABSL_GUARDED_BY(mutex_)=0
std::vector< Solution > new_solutions_ ABSL_GUARDED_BY(mutex_)
void AddInternal(const Solution &solution) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_)
ValueType GetVariableValueInSolution(int var_index, int solution_index) const
std::vector< Solution > solutions_ ABSL_GUARDED_BY(mutex_)
SatParameters parameters
CpModelProto const * model_proto
SharedResponseManager * response
absl::Status status
Definition: g_gurobi.cc:41
GRBmodel * model
MPCallback * callback
int index
Definition: cleanup.h:22
void STLStableSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition: stl_util.h:75
void FillSolveStatsInResponse(Model *model, CpSolverResponse *response)
Collection of objects used to extend the Constraint Solver library.
std::vector< std::function< void(CpSolverResponse *)> > callbacks
std::string message
Definition: trace.cc:399
#define VLOG(verboselevel)
Definition: vlog.h:39