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"
53 template <
typename ValueType>
109 void Add(
const Solution& solution);
122 ABSL_EXCLUSIVE_LOCKS_REQUIRED(
mutex_);
144 IntegerValue inner_objective_value);
172 std::vector<std::vector<double>> solutions_;
173 mutable absl::Mutex mutex_;
180 std::vector<std::function<void(CpSolverResponse*)>>
callbacks;
219 std::function<
void(std::vector<int64_t>*)> postprocessor);
224 std::function<
void(CpSolverResponse*)> postprocessor);
229 std::function<
void(CpSolverResponse*)> postprocessor);
241 std::function<
void(
const CpSolverResponse&)>
callback);
293 IntegerValue lb, IntegerValue ub);
298 void NewSolution(absl::Span<const int64_t> solution_values,
299 const std::string& solution_info,
Model*
model =
nullptr);
329 dump_prefix_ = dump_prefix;
337 double frequency_seconds,
338 absl::Time* last_logging_time);
344 return &first_solution_solvers_should_stop_;
350 debug_solution_.assign(solution.begin(), solution.end());
352 const std::vector<int64_t>&
DebugSolution()
const {
return debug_solution_; }
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_);
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_);
368 CpSolverResponse GetResponseInternal(
369 absl::Span<const int64_t> variable_values,
370 const std::
string& solution_info) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
372 const SatParameters& parameters_;
375 CpObjectiveProto const* objective_or_null_ =
nullptr;
377 mutable
absl::Mutex mutex_;
380 double absolute_gap_limit_ ABSL_GUARDED_BY(mutex_) = 0.0;
381 double relative_gap_limit_ ABSL_GUARDED_BY(mutex_) = 0.0;
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_);
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();
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());
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;
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_);
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_);
420 std::
string dump_prefix_;
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_);
429 std::vector<CpSolverResponse> subsolver_responses_ ABSL_GUARDED_BY(mutex_);
431 std::atomic<
bool> first_solution_solvers_should_stop_ = false;
433 std::vector<int64_t> debug_solution_;
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);
456 void FixVariablesFromPartialSolution(
457 const std::vector<int64_t>& solution,
458 const std::vector<int>& variables_to_fix);
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);
476 int NumBoundsExported(
const std::string& worker_name);
482 debug_solution_.assign(solution.begin(), solution.end());
486 const int num_variables_;
487 const CpModelProto& model_proto_;
492 std::vector<int64_t> lower_bounds_ ABSL_GUARDED_BY(mutex_);
493 std::vector<int64_t> upper_bounds_ ABSL_GUARDED_BY(mutex_);
495 ABSL_GUARDED_BY(mutex_);
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_);
504 std::vector<int64_t> debug_solution_;
517 void AddBinaryClause(
int id,
int lit1,
int lit2);
523 void GetUnseenBinaryClauses(
int id,
524 std::vector<std::pair<int, int>>* new_clauses);
528 void SetWorkerNameForId(
int id,
const std::string& worker_name);
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;
549 absl::flat_hash_map<int, std::string> id_to_worker_name_;
558 void AddStats(absl::Span<
const std::pair<std::string, int64_t>> stats);
565 absl::flat_hash_map<std::string, int64_t> stats_ ABSL_GUARDED_BY(mutex_);
568 template <
typename ValueType>
570 absl::MutexLock mutex_lock(&mutex_);
571 return solutions_.size();
574 template <
typename ValueType>
577 absl::MutexLock mutex_lock(&mutex_);
578 return solutions_[i];
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];
589 template <
typename ValueType>
592 absl::BitGenRef random)
const {
593 absl::MutexLock mutex_lock(&mutex_);
594 const int64_t best_rank = solutions_[0].rank;
603 const int kExplorationThreshold = 100;
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);
616 if (tmp_indices_.empty()) {
617 index = absl::Uniform<int>(random, 0, solutions_.size());
619 index = tmp_indices_[absl::Uniform<int>(random, 0, tmp_indices_.size())];
621 solutions_[
index].num_selected++;
622 return solutions_[
index];
625 template <
typename ValueType>
627 if (num_solutions_to_keep_ <= 0)
return;
628 absl::MutexLock mutex_lock(&mutex_);
629 AddInternal(solution);
632 template <
typename ValueType>
635 int worse_solution_index = 0;
636 for (
int i = 0; i < new_solutions_.size(); ++i) {
638 if (new_solutions_[i] == solution)
return;
639 if (new_solutions_[worse_solution_index] < new_solutions_[i]) {
640 worse_solution_index = i;
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;
650 template <
typename ValueType>
652 absl::MutexLock mutex_lock(&mutex_);
653 if (new_solutions_.empty())
return;
655 solutions_.insert(solutions_.end(), new_solutions_.begin(),
656 new_solutions_.end());
657 new_solutions_.clear();
664 if (solutions_.size() > num_solutions_to_keep_) {
665 solutions_.resize(num_solutions_to_keep_);
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;
675 num_synchronization_++;
bool LoggingIsEnabled() const
Class that owns everything related to a particular optimization model.
void LoadDebugSolution(absl::Span< const int64_t > solution)
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(absl::Span< const int64_t > solution_values, IntegerValue inner_objective_value)
bool ProblemIsSolved() const
SharedSolutionRepository< int64_t > * MutableSolutionsRepository()
void InitializeObjective(const CpModelProto &cp_model)
CpSolverResponse GetResponse()
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()
IntegerValue GetInnerObjectiveUpperBound()
IntegerValue SynchronizedInnerObjectiveUpperBound()
IntegerValue SynchronizedInnerObjectiveLowerBound()
bool LoggingIsEnabled() const
void DisplayImprovementStatistics()
double GapIntegral() const
void NotifyThatImprovingProblemIsInfeasible(const std::string &worker_info)
void SetSynchronizationMode(bool always_synchronize)
void SetUpdateGapIntegralOnEachChange(bool set)
IntegerValue BestSolutionInnerObjectiveValue()
void AddUnsatCore(const std::vector< int > &core)
void SetGapLimitsFromParameters(const SatParameters ¶meters)
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)
IntegerValue GetInnerObjectiveLowerBound()
void UnregisterCallback(int callback_id)
const std::vector< int64_t > & DebugSolution() const
SharedResponseManager(Model *model)
void UpdateInnerObjectiveBounds(const std::string &update_info, IntegerValue lb, IntegerValue ub)
void Add(const Solution &solution)
Solution GetRandomBiasedSolution(absl::BitGenRef random) const
std::vector< int > tmp_indices_ ABSL_GUARDED_BY(mutex_)
SharedSolutionRepository(int num_solutions_to_keep)
Solution GetSolution(int index) const
const int num_solutions_to_keep_
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_)
SharedStatistics()=default
CpModelProto const * model_proto
SharedResponseManager * response
void STLStableSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void FillSolveStatsInResponse(Model *model, CpSolverResponse *response)
Collection of objects used to extend the Constraint Solver library.
std::vector< std::function< void(CpSolverResponse *)> > callbacks
bool operator<(const Solution &other) const
std::vector< ValueType > variable_values
bool operator==(const Solution &other) const
#define VLOG(verboselevel)