14#ifndef OR_TOOLS_SAT_SYNCHRONIZATION_H_
15#define OR_TOOLS_SAT_SYNCHRONIZATION_H_
30#include "absl/algorithm/container.h"
31#include "absl/base/thread_annotations.h"
32#include "absl/container/btree_map.h"
33#include "absl/container/flat_hash_map.h"
34#include "absl/container/flat_hash_set.h"
35#include "absl/random/bit_gen_ref.h"
36#include "absl/random/random.h"
37#include "absl/strings/string_view.h"
38#include "absl/synchronization/mutex.h"
39#include "absl/time/time.h"
40#include "absl/types/span.h"
44#include "ortools/sat/cp_model.pb.h"
47#include "ortools/sat/sat_parameters.pb.h"
60template <
typename ValueType>
64 absl::string_view name =
"")
118 absl::BitGenRef random)
const;
137 absl::MutexLock mutex_lock(&
mutex_);
184 sol.
rank = num_violations;
202 void AddSolution(
const std::vector<double>& lp_solution);
210 absl::MutexLock mutex_lock(&mutex_);
216 mutable absl::Mutex mutex_;
217 std::deque<std::vector<double>> solutions_ ABSL_GUARDED_BY(mutex_);
218 int64_t num_added_ ABSL_GUARDED_BY(mutex_) = 0;
219 mutable int64_t num_queried_ ABSL_GUARDED_BY(mutex_) = 0;
254 std::function<
void(std::vector<int64_t>*)> postprocessor);
259 std::function<
void(CpSolverResponse*)> postprocessor);
264 std::function<
void(CpSolverResponse*)> postprocessor);
269 std::function<
void(
Model*, CpSolverResponse*)> postprocessor);
285 std::function<
void(
const CpSolverResponse&)> callback);
298 std::function<std::string(
const CpSolverResponse&)> callback);
356 IntegerValue lb, IntegerValue ub);
362 std::shared_ptr<const SharedSolutionRepository<int64_t>::Solution>
363 NewSolution(absl::Span<const int64_t> solution_values,
364 const std::string& solution_info,
Model* model =
nullptr);
394 dump_prefix_ = dump_prefix;
401 void LogMessage(absl::string_view prefix, absl::string_view message);
403 const std::string& message);
409 return &first_solution_solvers_should_stop_;
417 const std::vector<int64_t>&
DebugSolution()
const {
return debug_solution_; }
420 void TestGapLimitsIfNeeded() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
421 void FillObjectiveValuesInResponse(CpSolverResponse* response) const
422 ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
423 void UpdateGapIntegralInternal() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
425 void RegisterSolutionFound(const
std::
string& improvement_info,
427 ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
428 void RegisterObjectiveBoundImprovement(const
std::
string& improvement_info)
429 ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
430 void UpdateBestStatus(const CpSolverStatus& status)
431 ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
434 CpSolverResponse GetResponseInternal(
435 absl::Span<const int64_t> variable_values,
436 const
std::
string& solution_info) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
438 const SatParameters& parameters_;
441 CpObjectiveProto const* objective_or_null_ =
nullptr;
443 mutable
absl::Mutex mutex_;
446 double absolute_gap_limit_ ABSL_GUARDED_BY(mutex_) = 0.0;
447 double relative_gap_limit_ ABSL_GUARDED_BY(mutex_) = 0.0;
449 CpSolverStatus best_status_ ABSL_GUARDED_BY(mutex_) = CpSolverStatus::UNKNOWN;
450 CpSolverStatus synchronized_best_status_ ABSL_GUARDED_BY(mutex_) =
451 CpSolverStatus::UNKNOWN;
452 std::vector<
int> unsat_cores_ ABSL_GUARDED_BY(mutex_);
455 int num_solutions_ ABSL_GUARDED_BY(mutex_) = 0;
456 int64_t inner_objective_lower_bound_ ABSL_GUARDED_BY(mutex_) =
457 std::numeric_limits<int64_t>::min();
458 int64_t inner_objective_upper_bound_ ABSL_GUARDED_BY(mutex_) =
459 std::numeric_limits<int64_t>::max();
460 int64_t best_solution_objective_value_ ABSL_GUARDED_BY(mutex_) =
461 std::numeric_limits<int64_t>::max();
463 bool always_synchronize_ ABSL_GUARDED_BY(mutex_) = true;
464 IntegerValue synchronized_inner_objective_lower_bound_ ABSL_GUARDED_BY(
465 mutex_) = IntegerValue(
std::numeric_limits<int64_t>::min());
466 IntegerValue synchronized_inner_objective_upper_bound_ ABSL_GUARDED_BY(
467 mutex_) = IntegerValue(
std::numeric_limits<int64_t>::max());
469 bool update_integral_on_each_change_ ABSL_GUARDED_BY(mutex_) = false;
470 double gap_integral_ ABSL_GUARDED_BY(mutex_) = 0.0;
471 double last_absolute_gap_ ABSL_GUARDED_BY(mutex_) = 0.0;
472 double last_gap_integral_time_stamp_ ABSL_GUARDED_BY(mutex_) = 0.0;
474 int next_callback_id_ ABSL_GUARDED_BY(mutex_) = 0;
475 std::vector<
std::pair<
int,
std::function<
void(const CpSolverResponse&)>>>
476 callbacks_ ABSL_GUARDED_BY(mutex_);
478 int next_search_log_callback_id_ ABSL_GUARDED_BY(mutex_) = 0;
480 std::pair<
int,
std::function<
std::
string(const CpSolverResponse&)>>>
481 search_log_callbacks_ ABSL_GUARDED_BY(mutex_);
483 int next_best_bound_callback_id_ ABSL_GUARDED_BY(mutex_) = 0;
484 std::vector<
std::pair<
int,
std::function<
void(
double)>>> best_bound_callbacks_
485 ABSL_GUARDED_BY(mutex_);
487 std::vector<
std::function<
void(
std::vector<int64_t>*)>>
488 solution_postprocessors_ ABSL_GUARDED_BY(mutex_);
489 std::vector<
std::function<
void(CpSolverResponse*)>> postprocessors_
490 ABSL_GUARDED_BY(mutex_);
491 std::vector<
std::function<
void(CpSolverResponse*)>> final_postprocessors_
492 ABSL_GUARDED_BY(mutex_);
493 std::vector<
std::function<
void(
Model*, CpSolverResponse*)>>
494 statistics_postprocessors_ ABSL_GUARDED_BY(mutex_);
497 std::
string dump_prefix_;
500 absl::btree_map<
std::
string,
int> primal_improvements_count_
501 ABSL_GUARDED_BY(mutex_);
502 absl::btree_map<
std::
string,
int> primal_improvements_min_rank_
503 ABSL_GUARDED_BY(mutex_);
504 absl::btree_map<
std::
string,
int> primal_improvements_max_rank_
505 ABSL_GUARDED_BY(mutex_);
507 absl::btree_map<
std::
string,
int> dual_improvements_count_
508 ABSL_GUARDED_BY(mutex_);
511 absl::flat_hash_map<
std::
string,
int> throttling_ids_ ABSL_GUARDED_BY(mutex_);
513 int bounds_logging_id_;
514 std::vector<CpSolverResponse> subsolver_responses_ ABSL_GUARDED_BY(mutex_);
516 std::atomic<
bool> first_solution_solvers_should_stop_ = false;
518 std::vector<int64_t> debug_solution_;
531 absl::Span<const int> variables,
532 absl::Span<const int64_t> new_lower_bounds,
533 absl::Span<const int64_t> new_upper_bounds);
545 absl::Span<const int> variables_to_fix);
555 std::vector<int64_t>* new_lower_bounds,
556 std::vector<int64_t>* new_upper_bounds);
578 dump_prefix_ = dump_prefix;
582 const int num_variables_;
583 const CpModelProto& model_proto_;
588 std::vector<int64_t> lower_bounds_ ABSL_GUARDED_BY(mutex_);
589 std::vector<int64_t> upper_bounds_ ABSL_GUARDED_BY(mutex_);
591 ABSL_GUARDED_BY(mutex_);
592 int64_t total_num_improvements_ ABSL_GUARDED_BY(mutex_) = 0;
595 std::vector<int64_t> synchronized_lower_bounds_ ABSL_GUARDED_BY(mutex_);
596 std::vector<int64_t> synchronized_upper_bounds_ ABSL_GUARDED_BY(mutex_);
597 std::deque<SparseBitset<int>> id_to_changed_variables_
598 ABSL_GUARDED_BY(mutex_);
603 int64_t num_exported = 0;
604 int64_t num_symmetric = 0;
606 absl::btree_map<std::string, Counters> bounds_exported_
607 ABSL_GUARDED_BY(mutex_);
610 bool has_symmetry_ =
false;
611 std::vector<int> var_to_representative_;
612 std::vector<int> var_to_orbit_index_;
613 CompactVectorVector<int, int> orbits_;
615 std::vector<int64_t> debug_solution_;
616 std::string dump_prefix_;
617 int export_counter_ = 0;
659 bool Add(absl::Span<const int> clause) ABSL_LOCKS_EXCLUDED(mutex_);
666 bool Delete(absl::Span<const int> clause) ABSL_LOCKS_EXCLUDED(mutex_);
675 int max_clauses_to_export) ABSL_LOCKS_EXCLUDED(mutex_);
680 absl::MutexLock lock(&mutex_);
681 return NumLiteralsOfSize(size);
683 int NumBufferedLiterals() const ABSL_LOCKS_EXCLUDED(mutex_);
687 bool CanAccept(
int size,
int lbd) const;
693 void RemoveWorstClauses();
696 absl::MutexLock lock(&mutex_);
697 return lbd_threshold_;
699 void set_lbd_threshold(
int lbd) ABSL_LOCKS_EXCLUDED(mutex_);
702 static size_t HashClause(absl::Span<const int> clause,
size_t hash_seed = 0);
705 bool BlockClause(absl::Span<const int> clause)
706 ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
707 std::vector<int>* MutableBufferForSize(
int size)
708 ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
709 return &clauses_by_size_[size - kMinClauseSize];
711 absl::Span<const int> BufferForSize(
int size)
const
712 ABSL_SHARED_LOCKS_REQUIRED(mutex_) {
713 return clauses_by_size_[size - kMinClauseSize];
715 absl::Span<const int> NextClause(
int size)
const
716 ABSL_SHARED_LOCKS_REQUIRED(mutex_);
717 void PopClause(
int size) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
719 int NumClausesOfSize(
int size)
const ABSL_SHARED_LOCKS_REQUIRED(mutex_);
720 int NumLiteralsOfSize(
int size)
const ABSL_SHARED_LOCKS_REQUIRED(mutex_);
722 mutable absl::Mutex mutex_;
723 int lbd_threshold_ ABSL_GUARDED_BY(mutex_) = 2;
724 absl::flat_hash_set<size_t> fingerprints_ ABSL_GUARDED_BY(mutex_);
725 std::array<std::vector<int>, kMaxClauseSize - kMinClauseSize + 1>
726 clauses_by_size_ ABSL_GUARDED_BY(mutex_);
739 absl::Duration share_frequency);
752 std::vector<std::pair<int, int>>* new_clauses);
761 absl::ReaderMutexLock mutex_lock(&mutex_);
762 return &id_to_clause_stream_[id];
773 static constexpr int kMinBatches = 10;
778 absl::flat_hash_set<std::pair<int, int>> added_binary_clauses_set_
779 ABSL_GUARDED_BY(mutex_);
780 std::vector<std::pair<int, int>> added_binary_clauses_
781 ABSL_GUARDED_BY(mutex_);
782 std::vector<int> id_to_last_processed_binary_clause_ ABSL_GUARDED_BY(mutex_);
783 int last_visible_binary_clause_ ABSL_GUARDED_BY(mutex_) = 0;
789 std::vector<int> id_to_last_returned_batch_ ABSL_GUARDED_BY(mutex_);
790 std::vector<int> id_to_last_finished_batch_ ABSL_GUARDED_BY(mutex_);
791 std::deque<CompactVectorVector<int>> batches_ ABSL_GUARDED_BY(mutex_);
792 std::deque<UniqueClauseStream> id_to_clause_stream_ ABSL_GUARDED_BY(mutex_);
793 WallTimer share_timer_ ABSL_GUARDED_BY(mutex_);
795 const bool always_synchronize_ =
true;
796 const absl::Duration share_frequency_;
799 std::vector<int64_t> id_to_clauses_exported_;
800 absl::flat_hash_map<int, std::string> id_to_worker_name_;
809 void AddStats(absl::Span<
const std::pair<std::string, int64_t>> stats);
816 absl::flat_hash_map<std::string, int64_t> stats_ ABSL_GUARDED_BY(mutex_);
819template <
typename ValueType>
821 absl::MutexLock mutex_lock(&
mutex_);
822 return solutions_.size();
825template <
typename ValueType>
826std::shared_ptr<const typename SharedSolutionRepository<ValueType>::Solution>
828 absl::MutexLock mutex_lock(&
mutex_);
830 return solutions_[
i];
833template <
typename ValueType>
835 absl::MutexLock mutex_lock(&
mutex_);
836 CHECK_GT(solutions_.size(), 0);
837 return solutions_[0]->rank;
840template <
typename ValueType>
841std::vector<std::shared_ptr<
844 absl::MutexLock mutex_lock(&
mutex_);
846 DCHECK(absl::c_is_sorted(
848 [](
const std::shared_ptr<const Solution>& a,
849 const std::shared_ptr<const Solution>&
b) {
return *a < *
b; }));
850 DCHECK(absl::c_adjacent_find(solutions_,
851 [](
const std::shared_ptr<const Solution>& a,
852 const std::shared_ptr<const Solution>&
b) {
854 }) == solutions_.end());
855 std::vector<std::shared_ptr<const Solution>> result;
856 const int num_solutions = std::min(
static_cast<int>(solutions_.size()), n);
857 result.reserve(num_solutions);
858 for (
int i = 0;
i < num_solutions; ++
i) {
859 result.push_back(solutions_[
i]);
864template <
typename ValueType>
866 int var_index,
int solution_index)
const {
867 absl::MutexLock mutex_lock(&
mutex_);
868 return solutions_[solution_index]->variable_values[var_index];
872template <
typename ValueType>
873std::shared_ptr<const typename SharedSolutionRepository<ValueType>::Solution>
875 absl::BitGenRef random)
const {
876 absl::MutexLock mutex_lock(&
mutex_);
878 const int64_t best_rank = solutions_[0]->rank;
887 const int kExplorationThreshold = 100;
890 tmp_indices_.clear();
891 for (
int i = 0;
i < solutions_.size(); ++
i) {
892 std::shared_ptr<const Solution>
solution = solutions_[
i];
894 solution->num_selected <= kExplorationThreshold) {
895 tmp_indices_.push_back(
i);
900 if (tmp_indices_.empty()) {
901 index = absl::Uniform<int>(random, 0, solutions_.size());
903 index = tmp_indices_[absl::Uniform<int>(random, 0, tmp_indices_.size())];
905 solutions_[index]->num_selected++;
906 return solutions_[index];
909template <
typename ValueType>
910std::shared_ptr<const typename SharedSolutionRepository<ValueType>::Solution>
912 std::shared_ptr<Solution> solution_ptr =
913 std::make_shared<Solution>(std::move(
solution));
916 absl::MutexLock mutex_lock(&
mutex_);
918 new_solutions_.push_back(solution_ptr);
923template <
typename ValueType>
925 absl::MutexLock mutex_lock(&
mutex_);
926 if (new_solutions_.empty())
return;
928 solutions_.insert(solutions_.end(), new_solutions_.begin(),
929 new_solutions_.end());
930 new_solutions_.clear();
937 &solutions_, [](
const std::shared_ptr<Solution>& a,
938 const std::shared_ptr<Solution>&
b) {
return *a < *
b; });
943 if (!solutions_.empty()) {
944 VLOG(2) <<
"Solution pool update:" <<
" num_solutions=" << solutions_.size()
945 <<
" min_rank=" << solutions_[0]->rank
946 <<
" max_rank=" << solutions_.back()->rank;
949 num_synchronization_++;
The model "singleton" shared time limit.
void LoadDebugSolution(absl::Span< const int64_t > solution)
int NumBoundsExported(absl::string_view worker_name)
void set_dump_prefix(absl::string_view dump_prefix)
Debug only. Set dump prefix for solutions written to file.
void UpdateDomains(std::vector< Domain > *domains)
void ReportPotentialNewBounds(const std::string &worker_name, absl::Span< const int > variables, absl::Span< const int64_t > new_lower_bounds, absl::Span< const int64_t > new_upper_bounds)
void LogStatistics(SolverLogger *logger)
void FixVariablesFromPartialSolution(absl::Span< const int64_t > solution, absl::Span< const int > variables_to_fix)
void GetChangedBounds(int id, std::vector< int > *variables, std::vector< int64_t > *new_lower_bounds, std::vector< int64_t > *new_upper_bounds)
SharedBoundsManager(const CpModelProto &model_proto)
int RegisterNewId()
Ids are used to identify which worker is exporting/importing clauses.
void SetWorkerNameForId(int id, absl::string_view worker_name)
void AddBinaryClause(int id, int lit1, int lit2)
UniqueClauseStream * GetClauseStream(int id)
void GetUnseenBinaryClauses(int id, std::vector< std::pair< int, int > > *new_clauses)
std::vector< absl::Span< const int > > GetUnseenClauses(int id)
SharedClausesManager(bool always_synchronize, absl::Duration share_frequency)
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)
SharedLsSolutionRepository()
void AddSolution(std::vector< int64_t > solution, int num_violations)
void AppendResponseToBeMerged(const CpSolverResponse &response)
void AddUnsatCore(const std::vector< int > &core)
const std::vector< int64_t > & DebugSolution() const
void AddStatisticsPostprocessor(std::function< void(Model *, CpSolverResponse *)> postprocessor)
IntegerValue GetInnerObjectiveLowerBound()
void LogMessageWithThrottling(const std::string &prefix, const std::string &message)
void SetUpdateGapIntegralOnEachChange(bool set)
bool ProblemIsSolved() const
std::shared_ptr< const SharedSolutionRepository< int64_t >::Solution > NewSolution(absl::Span< const int64_t > solution_values, const std::string &solution_info, Model *model=nullptr)
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)
const SharedSolutionRepository< int64_t > & SolutionsRepository() const
void FillSolveStatsInResponse(Model *model, CpSolverResponse *response)
void UpdateInnerObjectiveBounds(const std::string &update_info, IntegerValue lb, IntegerValue ub)
Updates the inner objective bounds.
std::shared_ptr< const Solution > GetRandomBiasedSolution(absl::BitGenRef random) const
Returns a random solution biased towards good solutions.
int64_t num_added_ ABSL_GUARDED_BY(mutex_)=0
int64_t num_queried_ ABSL_GUARDED_BY(mutex_)=0
std::vector< std::shared_ptr< const Solution > > GetBestNSolutions(int n) const
std::vector< int > tmp_indices_ ABSL_GUARDED_BY(mutex_)
std::shared_ptr< const Solution > GetSolution(int index) const
Returns the solution i where i must be smaller than NumSolutions().
std::shared_ptr< const Solution > Add(Solution solution)
std::vector< std::shared_ptr< Solution > > new_solutions_ ABSL_GUARDED_BY(mutex_)
int64_t GetBestRank() const
std::vector< std::shared_ptr< Solution > > solutions_ ABSL_GUARDED_BY(mutex_)
SharedSolutionRepository(int num_solutions_to_keep, absl::string_view name="")
std::vector< std::string > TableLineStats() const
const int num_solutions_to_keep_
ValueType GetVariableValueInSolution(int var_index, int solution_index) const
int64_t num_synchronization_ ABSL_GUARDED_BY(mutex_)=0
void AddStats(absl::Span< const std::pair< std::string, int64_t > > stats)
Adds a bunch of stats, adding count for the same key together.
void Log(SolverLogger *logger)
Logs all the added stats.
SharedStatistics()=default
static constexpr int kMaxClauseSize
static constexpr int kMaxBufferedLiterals
int FillUpstreamBuffer(UniqueClauseStream &upstream, int clause_size, int max_clauses_to_export) ABSL_LOCKS_EXCLUDED(mutex_)
UniqueClauseStream(const UniqueClauseStream &)=delete
Move only - this is an expensive class to copy.
int NumBufferedLiteralsOfSize(int size) const ABSL_LOCKS_EXCLUDED(mutex_)
bool Delete(absl::Span< const int > clause) ABSL_LOCKS_EXCLUDED(mutex_)
bool Add(absl::Span< const int > clause) ABSL_LOCKS_EXCLUDED(mutex_)
CompactVectorVector< int > NextBatch() ABSL_LOCKS_EXCLUDED(mutex_)
UniqueClauseStream(UniqueClauseStream &&)=default
int lbd_threshold() const ABSL_LOCKS_EXCLUDED(mutex_)
static constexpr int kMinClauseSize
static constexpr int kMaxLiteralsPerBatch
Export 4KiB of clauses per batch.
void STLStableSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
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.
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
The solution format used by this class.
bool operator==(const Solution &other) const
bool operator<(const Solution &other) const
std::vector< ValueType > variable_values