14#ifndef OR_TOOLS_SAT_SYNCHRONIZATION_H_
15#define OR_TOOLS_SAT_SYNCHRONIZATION_H_
28#include "absl/base/thread_annotations.h"
29#include "absl/container/btree_map.h"
30#include "absl/container/flat_hash_map.h"
31#include "absl/container/flat_hash_set.h"
32#include "absl/random/bit_gen_ref.h"
33#include "absl/random/random.h"
34#include "absl/strings/string_view.h"
35#include "absl/synchronization/mutex.h"
36#include "absl/time/time.h"
37#include "absl/types/span.h"
41#include "ortools/sat/cp_model.pb.h"
44#include "ortools/sat/sat_parameters.pb.h"
57template <
typename ValueType>
61 absl::string_view
name =
"")
129 absl::MutexLock mutex_lock(&
mutex_);
171 void AddSolution(
const std::vector<double>& lp_solution);
179 absl::MutexLock mutex_lock(&mutex_);
185 mutable absl::Mutex mutex_;
186 std::deque<std::vector<double>> solutions_ ABSL_GUARDED_BY(mutex_);
187 int64_t num_added_ ABSL_GUARDED_BY(mutex_) = 0;
188 mutable int64_t num_queried_ ABSL_GUARDED_BY(mutex_) = 0;
195 std::vector<std::function<void(CpSolverResponse*)>>
callbacks;
234 std::function<
void(std::vector<int64_t>*)> postprocessor);
239 std::function<
void(CpSolverResponse*)> postprocessor);
244 std::function<
void(CpSolverResponse*)> postprocessor);
256 std::function<
void(
const CpSolverResponse&)>
callback);
269 std::function<std::string(
const CpSolverResponse&)>
callback);
327 IntegerValue lb, IntegerValue ub);
332 void NewSolution(absl::Span<const int64_t> solution_values,
333 const std::string& solution_info,
Model*
model =
nullptr);
363 dump_prefix_ = dump_prefix;
378 return &first_solution_solvers_should_stop_;
386 const std::vector<int64_t>&
DebugSolution()
const {
return debug_solution_; }
389 void TestGapLimitsIfNeeded() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
390 void FillObjectiveValuesInResponse(CpSolverResponse* response) const
391 ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
392 void UpdateGapIntegralInternal() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
394 void RegisterSolutionFound(const
std::
string& improvement_info,
396 ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
397 void RegisterObjectiveBoundImprovement(const
std::
string& improvement_info)
398 ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
399 void UpdateBestStatus(const CpSolverStatus&
status)
400 ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
403 CpSolverResponse GetResponseInternal(
404 absl::Span<const int64_t> variable_values,
405 const
std::
string& solution_info) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
407 const SatParameters& parameters_;
410 CpObjectiveProto const* objective_or_null_ =
nullptr;
412 mutable
absl::Mutex mutex_;
415 double absolute_gap_limit_ ABSL_GUARDED_BY(mutex_) = 0.0;
416 double relative_gap_limit_ ABSL_GUARDED_BY(mutex_) = 0.0;
418 CpSolverStatus best_status_ ABSL_GUARDED_BY(mutex_) = CpSolverStatus::UNKNOWN;
419 CpSolverStatus synchronized_best_status_ ABSL_GUARDED_BY(mutex_) =
420 CpSolverStatus::UNKNOWN;
421 std::vector<
int> unsat_cores_ ABSL_GUARDED_BY(mutex_);
424 int num_solutions_ ABSL_GUARDED_BY(mutex_) = 0;
425 int64_t inner_objective_lower_bound_ ABSL_GUARDED_BY(mutex_) =
426 std::numeric_limits<int64_t>::
min();
427 int64_t inner_objective_upper_bound_ ABSL_GUARDED_BY(mutex_) =
428 std::numeric_limits<int64_t>::
max();
429 int64_t best_solution_objective_value_ ABSL_GUARDED_BY(mutex_) =
430 std::numeric_limits<int64_t>::
max();
432 bool always_synchronize_ ABSL_GUARDED_BY(mutex_) = true;
433 IntegerValue synchronized_inner_objective_lower_bound_ ABSL_GUARDED_BY(
434 mutex_) = IntegerValue(
std::numeric_limits<int64_t>::
min());
435 IntegerValue synchronized_inner_objective_upper_bound_ ABSL_GUARDED_BY(
436 mutex_) = IntegerValue(
std::numeric_limits<int64_t>::
max());
438 bool update_integral_on_each_change_ ABSL_GUARDED_BY(mutex_) = false;
439 double gap_integral_ ABSL_GUARDED_BY(mutex_) = 0.0;
440 double last_absolute_gap_ ABSL_GUARDED_BY(mutex_) = 0.0;
441 double last_gap_integral_time_stamp_ ABSL_GUARDED_BY(mutex_) = 0.0;
443 int next_callback_id_ ABSL_GUARDED_BY(mutex_) = 0;
444 std::vector<
std::pair<
int,
std::function<
void(const CpSolverResponse&)>>>
445 callbacks_ ABSL_GUARDED_BY(mutex_);
447 int next_search_log_callback_id_ ABSL_GUARDED_BY(mutex_) = 0;
449 std::pair<
int,
std::function<
std::
string(const CpSolverResponse&)>>>
450 search_log_callbacks_ ABSL_GUARDED_BY(mutex_);
452 int next_best_bound_callback_id_ ABSL_GUARDED_BY(mutex_) = 0;
453 std::vector<
std::pair<
int,
std::function<
void(
double)>>> best_bound_callbacks_
454 ABSL_GUARDED_BY(mutex_);
456 std::vector<
std::function<
void(
std::vector<int64_t>*)>>
457 solution_postprocessors_ ABSL_GUARDED_BY(mutex_);
458 std::vector<
std::function<
void(CpSolverResponse*)>> postprocessors_
459 ABSL_GUARDED_BY(mutex_);
460 std::vector<
std::function<
void(CpSolverResponse*)>> final_postprocessors_
461 ABSL_GUARDED_BY(mutex_);
464 std::
string dump_prefix_;
467 absl::btree_map<
std::
string,
int> primal_improvements_count_
468 ABSL_GUARDED_BY(mutex_);
469 absl::btree_map<
std::
string,
int> primal_improvements_min_rank_
470 ABSL_GUARDED_BY(mutex_);
471 absl::btree_map<
std::
string,
int> primal_improvements_max_rank_
472 ABSL_GUARDED_BY(mutex_);
474 absl::btree_map<
std::
string,
int> dual_improvements_count_
475 ABSL_GUARDED_BY(mutex_);
478 absl::flat_hash_map<
std::
string,
int> throttling_ids_ ABSL_GUARDED_BY(mutex_);
480 int bounds_logging_id_;
481 std::vector<CpSolverResponse> subsolver_responses_ ABSL_GUARDED_BY(mutex_);
483 std::atomic<
bool> first_solution_solvers_should_stop_ = false;
485 std::vector<int64_t> debug_solution_;
497 void ReportPotentialNewBounds(
const std::string& worker_name,
498 const std::vector<int>& variables,
499 const std::vector<int64_t>& new_lower_bounds,
500 const std::vector<int64_t>& new_upper_bounds);
508 void FixVariablesFromPartialSolution(
509 const std::vector<int64_t>&
solution,
510 const std::vector<int>& variables_to_fix);
519 void GetChangedBounds(
int id, std::vector<int>* variables,
520 std::vector<int64_t>* new_lower_bounds,
521 std::vector<int64_t>* new_upper_bounds);
525 void UpdateDomains(std::vector<Domain>* domains);
532 int NumBoundsExported(
const std::string& worker_name);
543 dump_prefix_ = dump_prefix;
547 const int num_variables_;
548 const CpModelProto& model_proto_;
553 std::vector<int64_t> lower_bounds_ ABSL_GUARDED_BY(mutex_);
554 std::vector<int64_t> upper_bounds_ ABSL_GUARDED_BY(mutex_);
556 ABSL_GUARDED_BY(mutex_);
557 int64_t total_num_improvements_ ABSL_GUARDED_BY(mutex_) = 0;
560 std::vector<int64_t> synchronized_lower_bounds_ ABSL_GUARDED_BY(mutex_);
561 std::vector<int64_t> synchronized_upper_bounds_ ABSL_GUARDED_BY(mutex_);
562 std::deque<SparseBitset<int>> id_to_changed_variables_
563 ABSL_GUARDED_BY(mutex_);
564 absl::btree_map<std::string, int> bounds_exported_ ABSL_GUARDED_BY(mutex_);
566 std::vector<int64_t> debug_solution_;
567 std::string dump_prefix_;
568 int export_counter_ = 0;
591 static constexpr int kMinClauseSize = 3;
592 static constexpr int kMaxClauseSize = 8;
594 static constexpr int kMaxLiteralsPerBatch = 4096 /
sizeof(int);
599 static constexpr int kMaxBufferedLiterals = kMaxLiteralsPerBatch;
610 bool Add(absl::Span<const int> clause) ABSL_LOCKS_EXCLUDED(mutex_);
617 bool Delete(absl::Span<const int> clause) ABSL_LOCKS_EXCLUDED(mutex_);
626 int max_clauses_to_export) ABSL_LOCKS_EXCLUDED(mutex_);
630 int NumBufferedLiteralsOfSize(
int size) const ABSL_LOCKS_EXCLUDED(mutex_) {
631 absl::MutexLock lock(&mutex_);
632 return NumLiteralsOfSize(
size);
634 int NumBufferedLiterals() const ABSL_LOCKS_EXCLUDED(mutex_);
638 bool CanAccept(
int size,
int lbd) const;
644 void RemoveWorstClauses();
646 int lbd_threshold() const ABSL_LOCKS_EXCLUDED(mutex_) {
647 absl::MutexLock lock(&mutex_);
648 return lbd_threshold_;
650 void set_lbd_threshold(
int lbd) ABSL_LOCKS_EXCLUDED(mutex_);
653 static size_t HashClause(absl::Span<const int> clause,
size_t hash_seed = 0);
656 bool BlockClause(absl::Span<const int> clause)
657 ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
658 std::vector<int>* MutableBufferForSize(
int size)
659 ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
660 return &clauses_by_size_[
size - kMinClauseSize];
662 absl::Span<const int> BufferForSize(
int size)
const
663 ABSL_SHARED_LOCKS_REQUIRED(mutex_) {
664 return clauses_by_size_[
size - kMinClauseSize];
666 absl::Span<const int> NextClause(
int size)
const
667 ABSL_SHARED_LOCKS_REQUIRED(mutex_);
668 void PopClause(
int size) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
670 int NumClausesOfSize(
int size)
const ABSL_SHARED_LOCKS_REQUIRED(mutex_);
671 int NumLiteralsOfSize(
int size)
const ABSL_SHARED_LOCKS_REQUIRED(mutex_);
673 mutable absl::Mutex mutex_;
674 int lbd_threshold_ ABSL_GUARDED_BY(mutex_) = 2;
675 absl::flat_hash_set<size_t> fingerprints_ ABSL_GUARDED_BY(mutex_);
676 std::array<std::vector<int>, kMaxClauseSize - kMinClauseSize + 1>
677 clauses_by_size_ ABSL_GUARDED_BY(mutex_);
690 absl::Duration share_frequency);
691 void AddBinaryClause(
int id,
int lit1,
int lit2);
696 std::vector<absl::Span<const int>> GetUnseenClauses(
int id);
702 void GetUnseenBinaryClauses(
int id,
703 std::vector<std::pair<int, int>>* new_clauses);
707 void SetWorkerNameForId(
int id,
const std::string& worker_name);
712 absl::ReaderMutexLock mutex_lock(&mutex_);
713 return &id_to_clause_stream_[id];
724 static constexpr int kMinBatches = 10;
729 absl::flat_hash_set<std::pair<int, int>> added_binary_clauses_set_
730 ABSL_GUARDED_BY(mutex_);
731 std::vector<std::pair<int, int>> added_binary_clauses_
732 ABSL_GUARDED_BY(mutex_);
733 std::vector<int> id_to_last_processed_binary_clause_ ABSL_GUARDED_BY(mutex_);
734 int last_visible_binary_clause_ ABSL_GUARDED_BY(mutex_) = 0;
740 std::vector<int> id_to_last_returned_batch_ ABSL_GUARDED_BY(mutex_);
741 std::vector<int> id_to_last_finished_batch_ ABSL_GUARDED_BY(mutex_);
742 std::deque<CompactVectorVector<int>> batches_ ABSL_GUARDED_BY(mutex_);
743 std::deque<UniqueClauseStream> id_to_clause_stream_ ABSL_GUARDED_BY(mutex_);
744 WallTimer share_timer_ ABSL_GUARDED_BY(mutex_);
746 const bool always_synchronize_ =
true;
747 const absl::Duration share_frequency_;
750 std::vector<int64_t> id_to_clauses_exported_;
751 absl::flat_hash_map<int, std::string> id_to_worker_name_;
760 void AddStats(absl::Span<
const std::pair<std::string, int64_t>> stats);
767 absl::flat_hash_map<std::string, int64_t> stats_ ABSL_GUARDED_BY(mutex_);
770template <
typename ValueType>
772 absl::MutexLock mutex_lock(&mutex_);
773 return solutions_.size();
776template <
typename ValueType>
779 absl::MutexLock mutex_lock(&mutex_);
781 return solutions_[
i];
784template <
typename ValueType>
786 absl::MutexLock mutex_lock(&mutex_);
787 CHECK_GT(solutions_.size(), 0);
788 return solutions_[0].rank;
791template <
typename ValueType>
793 int var_index,
int solution_index)
const {
794 absl::MutexLock mutex_lock(&mutex_);
795 return solutions_[solution_index].variable_values[
var_index];
799template <
typename ValueType>
802 absl::BitGenRef random)
const {
803 absl::MutexLock mutex_lock(&mutex_);
805 const int64_t best_rank = solutions_[0].rank;
814 const int kExplorationThreshold = 100;
817 tmp_indices_.clear();
818 for (
int i = 0;
i < solutions_.size(); ++
i) {
821 solution.num_selected <= kExplorationThreshold) {
822 tmp_indices_.push_back(
i);
827 if (tmp_indices_.empty()) {
828 index = absl::Uniform<int>(random, 0, solutions_.size());
830 index = tmp_indices_[absl::Uniform<int>(random, 0, tmp_indices_.size())];
832 solutions_[
index].num_selected++;
833 return solutions_[
index];
836template <
typename ValueType>
838 if (num_solutions_to_keep_ <= 0)
return;
839 absl::MutexLock mutex_lock(&mutex_);
844template <
typename ValueType>
846 absl::MutexLock mutex_lock(&mutex_);
847 if (new_solutions_.empty())
return;
849 solutions_.insert(solutions_.end(), new_solutions_.begin(),
850 new_solutions_.end());
851 new_solutions_.clear();
858 if (solutions_.size() > num_solutions_to_keep_) {
859 solutions_.resize(num_solutions_to_keep_);
862 if (!solutions_.empty()) {
863 VLOG(2) <<
"Solution pool update:" <<
" num_solutions=" << solutions_.size()
864 <<
" min_rank=" << solutions_[0].rank
865 <<
" max_rank=" << solutions_.back().rank;
868 num_synchronization_++;
The model "singleton" shared time limit.
void LoadDebugSolution(absl::Span< const int64_t > solution)
void set_dump_prefix(absl::string_view dump_prefix)
Debug only. Set dump prefix for solutions written to file.
UniqueClauseStream * GetClauseStream(int id)
std::vector< double > PopLast()
If there are no solution, this return an empty vector.
void AddSolution(const std::vector< double > &lp_solution)
std::vector< std::string > TableLineStats() const
void NewLPSolution(std::vector< double > lp_solution)
SharedLPSolutionRepository(int num_solutions_to_keep)
void AppendResponseToBeMerged(const CpSolverResponse &response)
void AddUnsatCore(const std::vector< int > &core)
const std::vector< int64_t > & DebugSolution() const
IntegerValue GetInnerObjectiveLowerBound()
void LogMessageWithThrottling(const std::string &prefix, const std::string &message)
void SetUpdateGapIntegralOnEachChange(bool set)
bool ProblemIsSolved() const
SharedSolutionRepository< int64_t > * MutableSolutionsRepository()
SharedResponseManager(Model *model)
double GapIntegral() const
void LogMessage(absl::string_view prefix, absl::string_view message)
Wrapper around our SolverLogger, but protected by mutex.
void SetSynchronizationMode(bool always_synchronize)
int AddLogCallback(std::function< std::string(const CpSolverResponse &)> callback)
int AddBestBoundCallback(std::function< void(double)> callback)
void UnregisterLogCallback(int callback_id)
void LoadDebugSolution(absl::Span< const int64_t > solution)
void UnregisterCallback(int callback_id)
IntegerValue BestSolutionInnerObjectiveValue()
void UnregisterBestBoundCallback(int callback_id)
void DisplayImprovementStatistics()
Display improvement stats.
bool LoggingIsEnabled() const
void InitializeObjective(const CpModelProto &cp_model)
std::atomic< bool > * first_solution_solvers_should_stop()
void SetGapLimitsFromParameters(const SatParameters ¶meters)
void AddFinalResponsePostprocessor(std::function< void(CpSolverResponse *)> postprocessor)
void NotifyThatImprovingProblemIsInfeasible(const std::string &worker_info)
void set_dump_prefix(absl::string_view dump_prefix)
Debug only. Set dump prefix for solutions written to file.
void AddResponsePostprocessor(std::function< void(CpSolverResponse *)> postprocessor)
CpSolverResponse GetResponse()
int AddSolutionCallback(std::function< void(const CpSolverResponse &)> callback)
IntegerValue GetInnerObjectiveUpperBound()
void AddSolutionPostprocessor(std::function< void(std::vector< int64_t > *)> postprocessor)
void NewSolution(absl::Span< const int64_t > solution_values, const std::string &solution_info, Model *model=nullptr)
const SharedSolutionRepository< int64_t > & SolutionsRepository() const
void UpdateInnerObjectiveBounds(const std::string &update_info, IntegerValue lb, IntegerValue ub)
Updates the inner objective bounds.
Solution GetSolution(int index) const
Returns the solution i where i must be smaller than NumSolutions().
void Add(const Solution &solution)
int64_t num_added_ ABSL_GUARDED_BY(mutex_)=0
int64_t num_queried_ ABSL_GUARDED_BY(mutex_)=0
std::vector< int > tmp_indices_ ABSL_GUARDED_BY(mutex_)
Solution GetRandomBiasedSolution(absl::BitGenRef random) const
Returns a random solution biased towards good solutions.
int64_t GetBestRank() const
SharedSolutionRepository(int num_solutions_to_keep, absl::string_view name="")
std::vector< Solution > new_solutions_ ABSL_GUARDED_BY(mutex_)
std::vector< std::string > TableLineStats() const
std::vector< Solution > solutions_ ABSL_GUARDED_BY(mutex_)
const int num_solutions_to_keep_
ValueType GetVariableValueInSolution(int var_index, int solution_index) const
int64_t num_synchronization_ ABSL_GUARDED_BY(mutex_)=0
Simple class to add statistics by name and print them at the end.
SharedStatistics()=default
UniqueClauseStream(const UniqueClauseStream &)=delete
Move only - this is an expensive class to copy.
UniqueClauseStream(UniqueClauseStream &&)=default
const std::string name
A name for logging purposes.
void STLStableSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void FillSolveStatsInResponse(Model *model, CpSolverResponse *response)
std::string FormatCounter(int64_t num)
Prints a positive number with separators for easier reading (ex: 1'348'065).
std::string FormatName(absl::string_view name)
This is used to format our table first row entry.
In SWIG mode, we don't want anything besides these top-level includes.
std::vector< std::function< void(CpSolverResponse *)> > callbacks
The solution format used by this class.
bool operator==(const Solution &other) const
bool operator<(const Solution &other) const
std::vector< ValueType > variable_values