14#ifndef OR_TOOLS_SAT_SYNCHRONIZATION_H_
15#define OR_TOOLS_SAT_SYNCHRONIZATION_H_
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"
37#include "ortools/sat/cp_model.pb.h"
41#include "ortools/sat/sat_parameters.pb.h"
54template <
typename ValueType>
110 void Add(
const Solution& solution);
123 ABSL_EXCLUSIVE_LOCKS_REQUIRED(
mutex_);
172 std::vector<std::vector<double>> solutions_;
173 mutable absl::Mutex mutex_;
206 CpSolverResponse
GetResponse(
bool full_response =
true);
211 std::function<
void(std::vector<int64_t>*)> postprocessor);
216 std::function<
void(CpSolverResponse*)> postprocessor);
221 std::function<
void(CpSolverResponse*)> postprocessor);
233 std::function<
void(
const CpSolverResponse&)>
callback);
278 IntegerValue lb, IntegerValue ub);
332 dump_prefix_ = dump_prefix;
340 absl::Time* last_logging_time);
348 absl::MutexLock mutex_lock(&mutex_);
349 return &best_response_;
353 void TestGapLimitsIfNeeded() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
354 void FillObjectiveValuesInBestResponse()
355 ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
357 ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
358 void UpdateGapIntegralInternal() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
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_);
366 CpSolverResponse GetResponseInternal() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
368 const SatParameters& parameters_;
371 CpObjectiveProto const* objective_or_null_ =
nullptr;
373 mutable
absl::Mutex mutex_;
376 double absolute_gap_limit_ ABSL_GUARDED_BY(mutex_) = 0.0;
377 double relative_gap_limit_ ABSL_GUARDED_BY(mutex_) = 0.0;
379 CpSolverResponse best_response_ ABSL_GUARDED_BY(mutex_);
382 int num_solutions_ ABSL_GUARDED_BY(mutex_) = 0;
383 int64_t inner_objective_lower_bound_ ABSL_GUARDED_BY(mutex_) =
384 std::numeric_limits<int64_t>::
min();
385 int64_t inner_objective_upper_bound_ ABSL_GUARDED_BY(mutex_) =
386 std::numeric_limits<int64_t>::
max();
387 int64_t best_solution_objective_value_ ABSL_GUARDED_BY(mutex_) =
388 std::numeric_limits<int64_t>::
max();
390 IntegerValue synchronized_inner_objective_lower_bound_ ABSL_GUARDED_BY(
391 mutex_) = IntegerValue(
std::numeric_limits<int64_t>::
min());
392 IntegerValue synchronized_inner_objective_upper_bound_ ABSL_GUARDED_BY(
393 mutex_) = IntegerValue(
std::numeric_limits<int64_t>::
max());
395 bool update_integral_on_each_change_ ABSL_GUARDED_BY(mutex_) = false;
396 double gap_integral_ ABSL_GUARDED_BY(mutex_) = 0.0;
397 double last_absolute_gap_ ABSL_GUARDED_BY(mutex_) = 0.0;
398 double last_gap_integral_time_stamp_ ABSL_GUARDED_BY(mutex_) = 0.0;
400 int next_callback_id_ ABSL_GUARDED_BY(mutex_) = 0;
401 std::vector<
std::pair<
int,
std::function<
void(const CpSolverResponse&)>>>
402 callbacks_ ABSL_GUARDED_BY(mutex_);
404 std::vector<
std::function<
void(
std::vector<int64_t>*)>>
405 solution_postprocessors_ ABSL_GUARDED_BY(mutex_);
406 std::vector<
std::function<
void(CpSolverResponse*)>> postprocessors_
407 ABSL_GUARDED_BY(mutex_);
408 std::vector<
std::function<
void(CpSolverResponse*)>> final_postprocessors_
409 ABSL_GUARDED_BY(mutex_);
412 std::
string dump_prefix_;
415 absl::btree_map<
std::
string,
int> primal_improvements_count_
416 ABSL_GUARDED_BY(mutex_);
417 absl::btree_map<
std::
string,
int> dual_improvements_count_
418 ABSL_GUARDED_BY(mutex_);
432 void ReportPotentialNewBounds(
const CpModelProto&
model_proto,
433 const std::string& worker_name,
434 const std::vector<int>& variables,
435 const std::vector<int64_t>& new_lower_bounds,
436 const std::vector<int64_t>& new_upper_bounds);
444 void FixVariablesFromPartialSolution(
445 const std::vector<int64_t>& solution,
446 const std::vector<int>& variables_to_fix);
455 void GetChangedBounds(
int id, std::vector<int>* variables,
456 std::vector<int64_t>* new_lower_bounds,
457 std::vector<int64_t>* new_upper_bounds);
464 int NumBoundsExported(
const std::string& worker_name);
467 const int num_variables_;
468 const CpModelProto& model_proto_;
473 std::vector<int64_t> lower_bounds_ ABSL_GUARDED_BY(mutex_);
474 std::vector<int64_t> upper_bounds_ ABSL_GUARDED_BY(mutex_);
476 ABSL_GUARDED_BY(mutex_);
479 std::vector<int64_t> synchronized_lower_bounds_ ABSL_GUARDED_BY(mutex_);
480 std::vector<int64_t> synchronized_upper_bounds_ ABSL_GUARDED_BY(mutex_);
481 std::deque<SparseBitset<int>> id_to_changed_variables_
482 ABSL_GUARDED_BY(mutex_);
483 absl::btree_map<std::string, int> bounds_exported_ ABSL_GUARDED_BY(mutex_);
495 void AddBinaryClause(
int id,
int lit1,
int lit2);
499 void GetUnseenBinaryClauses(
int id,
500 std::vector<std::pair<int, int>>* new_clauses);
503 void SetWorkerNameForId(
int id,
const std::string& worker_name);
511 absl::flat_hash_set<std::pair<int, int>> added_binary_clauses_set_
512 ABSL_GUARDED_BY(mutex_);
513 std::vector<std::pair<int, int>> added_binary_clauses_
514 ABSL_GUARDED_BY(mutex_);
515 std::vector<int64_t> id_to_last_processed_binary_clause_
516 ABSL_GUARDED_BY(mutex_);
517 std::vector<int64_t> id_to_clauses_exported_;
520 absl::flat_hash_map<int, std::string> id_to_worker_name_;
523template <
typename ValueType>
525 absl::MutexLock mutex_lock(&mutex_);
526 return solutions_.size();
529template <
typename ValueType>
532 absl::MutexLock mutex_lock(&mutex_);
533 return solutions_[i];
536template <
typename ValueType>
538 int var_index,
int solution_index)
const {
539 absl::MutexLock mutex_lock(&mutex_);
540 return solutions_[solution_index].variable_values[var_index];
544template <
typename ValueType>
547 absl::BitGenRef random)
const {
548 absl::MutexLock mutex_lock(&mutex_);
549 const int64_t best_rank = solutions_[0].rank;
558 const int kExplorationThreshold = 100;
561 tmp_indices_.clear();
562 for (
int i = 0; i < solutions_.size(); ++i) {
563 const auto& solution = solutions_[i];
564 if (solution.rank == best_rank &&
565 solution.num_selected <= kExplorationThreshold) {
566 tmp_indices_.push_back(i);
571 if (tmp_indices_.empty()) {
572 index = absl::Uniform<int>(random, 0, solutions_.size());
574 index = tmp_indices_[absl::Uniform<int>(random, 0, tmp_indices_.size())];
576 solutions_[
index].num_selected++;
577 return solutions_[
index];
580template <
typename ValueType>
582 if (num_solutions_to_keep_ == 0)
return;
583 absl::MutexLock mutex_lock(&mutex_);
584 AddInternal(solution);
587template <
typename ValueType>
590 int worse_solution_index = 0;
591 for (
int i = 0; i < new_solutions_.size(); ++i) {
593 if (new_solutions_[i] == solution)
return;
594 if (new_solutions_[worse_solution_index] < new_solutions_[i]) {
595 worse_solution_index = i;
598 if (new_solutions_.size() < num_solutions_to_keep_) {
599 new_solutions_.push_back(solution);
600 }
else if (solution < new_solutions_[worse_solution_index]) {
601 new_solutions_[worse_solution_index] = solution;
605template <
typename ValueType>
607 absl::MutexLock mutex_lock(&mutex_);
608 if (new_solutions_.empty())
return;
610 solutions_.insert(solutions_.end(), new_solutions_.begin(),
611 new_solutions_.end());
612 new_solutions_.clear();
619 if (solutions_.size() > num_solutions_to_keep_) {
620 solutions_.resize(num_solutions_to_keep_);
623 if (!solutions_.empty()) {
624 VLOG(2) <<
"Solution pool update:"
625 <<
" num_solutions=" << solutions_.size()
626 <<
" min_rank=" << solutions_[0].rank
627 <<
" max_rank=" << solutions_.back().rank;
630 num_synchronization_++;
#define CHECK_GE(val1, val2)
#define VLOG(verboselevel)
bool LoggingIsEnabled() const
Class that owns everything related to a particular optimization model.
void AddNewSolution(const std::vector< double > &lp_solution)
std::vector< double > GetNewSolution()
bool HasNewSolution() const
SharedLPSolutionRepository(int num_solutions_to_keep)
void NewLPSolution(std::vector< double > lp_solution)
SharedRelaxationSolutionRepository(int num_solutions_to_keep)
void NewRelaxationSolution(const CpSolverResponse &response)
bool ProblemIsSolved() const
void InitializeObjective(const CpModelProto &cp_model)
void SetStatsFromModel(Model *model)
CpSolverResponse GetResponse(bool full_response=true)
void AddSolutionPostprocessor(std::function< void(std::vector< int64_t > *)> postprocessor)
SharedSolutionRepository< int64_t > * MutableSolutionsRepository()
void AddFinalResponsePostprocessor(std::function< void(CpSolverResponse *)> postprocessor)
void set_dump_prefix(const std::string &dump_prefix)
IntegerValue GetInnerObjectiveUpperBound()
void LogPeriodicMessage(const std::string &prefix, const std::string &message, absl::Time *last_logging_time)
IntegerValue SynchronizedInnerObjectiveUpperBound()
IntegerValue SynchronizedInnerObjectiveLowerBound()
bool LoggingIsEnabled() const
void NewSolution(const CpSolverResponse &response, Model *model)
void DisplayImprovementStatistics()
double GapIntegral() const
void NotifyThatImprovingProblemIsInfeasible(const std::string &worker_info)
void SetUpdateGapIntegralOnEachChange(bool set)
IntegerValue BestSolutionInnerObjectiveValue()
void AddUnsatCore(const std::vector< int > &core)
void SetGapLimitsFromParameters(const SatParameters ¶meters)
const SharedSolutionRepository< int64_t > & SolutionsRepository() const
CpSolverResponse * MutableResponse()
void AddResponsePostprocessor(std::function< void(CpSolverResponse *)> postprocessor)
int AddSolutionCallback(std::function< void(const CpSolverResponse &)> callback)
void LogMessage(const std::string &prefix, const std::string &message)
void LoadDebugSolution(Model *)
IntegerValue GetInnerObjectiveLowerBound()
void UnregisterCallback(int callback_id)
SharedResponseManager(Model *model)
void UpdateInnerObjectiveBounds(const std::string &update_info, IntegerValue lb, IntegerValue ub)
void Add(const Solution &solution)
std::vector< Solution > new_solutions_ ABSL_GUARDED_BY(mutex_)
Solution GetRandomBiasedSolution(absl::BitGenRef random) const
SharedSolutionRepository(int num_solutions_to_keep)
Solution GetSolution(int index) const
const int num_solutions_to_keep_
std::vector< int > tmp_indices_ ABSL_GUARDED_BY(mutex_)
std::vector< Solution > solutions_ ABSL_GUARDED_BY(mutex_)
int64_t num_synchronization_ ABSL_GUARDED_BY(mutex_)=0
void AddInternal(const Solution &solution) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_)
ValueType GetVariableValueInSolution(int var_index, int solution_index) const
CpModelProto const * model_proto
SharedResponseManager * response
void STLStableSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Collection of objects used to extend the Constraint Solver library.
bool operator<(const Solution &other) const
std::vector< ValueType > variable_values
bool operator==(const Solution &other) const