19 #if !defined(__PORTABLE_PLATFORM__) 22 #endif // __PORTABLE_PLATFORM__ 24 #include "absl/container/flat_hash_set.h" 25 #include "absl/random/random.h" 37 ABSL_FLAG(
bool, cp_model_dump_solutions,
false,
38 "DEBUG ONLY. If true, all the intermediate solution will be dumped " 39 "under '\"FLAGS_cp_model_dump_prefix\" + \"solution_xxx.pb.txt\"'.");
42 std::string, cp_model_load_debug_solution,
"",
43 "DEBUG ONLY. When this is set to a non-empty file name, " 44 "we will interpret this as an internal solution which can be used for " 45 "debugging. For instance we use it to identify wrong cuts/reasons.");
54 if (
response.solution().empty())
return;
58 solution.variable_values.assign(
response.solution().begin(),
65 solution.rank = -
response.best_objective_bound();
71 std::vector<double> lp_solution) {
72 if (lp_solution.empty())
return;
76 solution.variable_values = std::move(lp_solution);
79 absl::MutexLock mutex_lock(&
mutex_);
80 solution.rank = -num_synchronization_;
85 absl::MutexLock mutex_lock(&mutex_);
86 return !solutions_.empty();
90 absl::MutexLock mutex_lock(&mutex_);
91 std::vector<double> solution;
92 if (solutions_.empty())
return solution;
94 solution = std::move(solutions_.back());
95 solutions_.pop_back();
100 const std::vector<double>& lp_solution) {
101 absl::MutexLock mutex_lock(&mutex_);
102 solutions_.push_back(lp_solution);
109 solutions_(parameters_.solution_pool_size()),
114 std::string ProgressMessage(
const std::string& event_or_solution_count,
115 double time_in_seconds,
double obj_best,
116 double obj_lb,
double obj_ub,
117 const std::string& solution_info) {
118 const std::string obj_next =
119 obj_lb <= obj_ub ? absl::StrFormat(
"next:[%.9g,%.9g]", obj_lb, obj_ub)
121 return absl::StrFormat(
"#%-5s %6.2fs best:%-5.9g %-15s %s",
122 event_or_solution_count, time_in_seconds, obj_best,
123 obj_next, solution_info);
126 std::string SatProgressMessage(
const std::string& event_or_solution_count,
127 double time_in_seconds,
128 const std::string& solution_info) {
129 return absl::StrFormat(
"#%-5s %6.2fs %s", event_or_solution_count,
130 time_in_seconds, solution_info);
136 absl::MutexLock mutex_lock(&mutex_);
138 absl::StrFormat(
"#Model %6.2fs %s", wall_timer_.
Get(),
message));
143 objective_or_null_ = &cp_model.
objective();
147 IntegerValue(domain.
Max()));
150 objective_or_null_ =
nullptr;
155 absl::MutexLock mutex_lock(&mutex_);
156 update_integral_on_each_change_ = set;
160 absl::MutexLock mutex_lock(&mutex_);
161 UpdateGapIntegralInternal();
164 void SharedResponseManager::UpdateGapIntegralInternal() {
165 if (objective_or_null_ ==
nullptr)
return;
168 const double time_delta = current_time - last_gap_integral_time_stamp_;
176 const double factor =
178 const double bounds_delta = std::log(1 + factor * last_absolute_gap_);
179 gap_integral_ += time_delta * bounds_delta;
182 last_gap_integral_time_stamp_ = current_time;
184 std::max(0.0, static_cast<double>(inner_objective_upper_bound_) -
185 static_cast<double>(inner_objective_lower_bound_));
190 absl::MutexLock mutex_lock(&mutex_);
191 if (objective_or_null_ ==
nullptr)
return;
192 absolute_gap_limit_ =
parameters.absolute_gap_limit();
193 relative_gap_limit_ =
parameters.relative_gap_limit();
196 void SharedResponseManager::TestGapLimitsIfNeeded() {
200 if (update_integral_on_each_change_) UpdateGapIntegralInternal();
204 if (absolute_gap_limit_ == 0 && relative_gap_limit_ == 0)
return;
207 if (inner_objective_lower_bound_ > inner_objective_upper_bound_)
return;
210 const double user_best =
212 const double user_bound =
214 const double gap = std::abs(user_best - user_bound);
215 if (gap <= absolute_gap_limit_) {
216 SOLVER_LOG(logger_,
"Absolute gap limit of ", absolute_gap_limit_,
223 shared_time_limit_->
Stop();
225 if (gap /
std::max(1.0, std::abs(user_best)) < relative_gap_limit_) {
226 SOLVER_LOG(logger_,
"Relative gap limit of ", relative_gap_limit_,
231 shared_time_limit_->
Stop();
236 const std::string& update_info, IntegerValue lb, IntegerValue ub) {
237 absl::MutexLock mutex_lock(&mutex_);
238 CHECK(objective_or_null_ !=
nullptr);
245 if (inner_objective_lower_bound_ > inner_objective_upper_bound_) {
250 (lb > inner_objective_lower_bound_ || ub < inner_objective_upper_bound_);
251 if (lb > inner_objective_lower_bound_) {
256 DCHECK_LE(inner_objective_upper_bound_, best_solution_objective_value_);
257 inner_objective_lower_bound_ =
258 std::min(best_solution_objective_value_, lb.value());
260 if (ub < inner_objective_upper_bound_) {
261 inner_objective_upper_bound_ = ub.value();
263 if (inner_objective_lower_bound_ > inner_objective_upper_bound_) {
270 if (update_integral_on_each_change_) UpdateGapIntegralInternal();
272 SatProgressMessage(
"Done", wall_timer_.
Get(), update_info));
284 RegisterObjectiveBoundImprovement(update_info);
285 SOLVER_LOG(logger_, ProgressMessage(
"Bound", wall_timer_.
Get(), best,
286 new_lb, new_ub, update_info));
288 if (change) TestGapLimitsIfNeeded();
295 const std::string& worker_info) {
296 absl::MutexLock mutex_lock(&mutex_);
305 inner_objective_lower_bound_ = best_solution_objective_value_;
306 if (update_integral_on_each_change_) UpdateGapIntegralInternal();
312 SatProgressMessage(
"Done", wall_timer_.
Get(), worker_info));
316 absl::MutexLock mutex_lock(&mutex_);
317 best_response_.clear_sufficient_assumptions_for_infeasibility();
318 for (
const int ref : core) {
319 best_response_.add_sufficient_assumptions_for_infeasibility(ref);
324 absl::MutexLock mutex_lock(&mutex_);
325 return IntegerValue(inner_objective_lower_bound_);
329 absl::MutexLock mutex_lock(&mutex_);
330 return IntegerValue(inner_objective_upper_bound_);
334 absl::MutexLock mutex_lock(&mutex_);
335 synchronized_inner_objective_lower_bound_ =
336 IntegerValue(inner_objective_lower_bound_);
337 synchronized_inner_objective_upper_bound_ =
338 IntegerValue(inner_objective_upper_bound_);
342 absl::MutexLock mutex_lock(&mutex_);
343 return synchronized_inner_objective_lower_bound_;
347 absl::MutexLock mutex_lock(&mutex_);
348 return synchronized_inner_objective_upper_bound_;
352 absl::MutexLock mutex_lock(&mutex_);
353 return IntegerValue(best_solution_objective_value_);
357 absl::MutexLock mutex_lock(&mutex_);
358 return gap_integral_;
362 std::function<
void(std::vector<int64_t>*)> postprocessor) {
363 absl::MutexLock mutex_lock(&mutex_);
364 solution_postprocessors_.push_back(postprocessor);
369 absl::MutexLock mutex_lock(&mutex_);
370 postprocessors_.push_back(postprocessor);
375 absl::MutexLock mutex_lock(&mutex_);
376 final_postprocessors_.push_back(postprocessor);
381 absl::MutexLock mutex_lock(&mutex_);
382 const int id = next_callback_id_++;
383 callbacks_.emplace_back(
id, std::move(
callback));
388 absl::MutexLock mutex_lock(&mutex_);
389 for (
int i = 0; i < callbacks_.size(); ++i) {
390 if (callbacks_[i].first == callback_id) {
391 callbacks_.erase(callbacks_.begin() + i);
395 LOG(DFATAL) <<
"Callback id " << callback_id <<
" not registered.";
399 FillObjectiveValuesInBestResponse();
405 std::vector<int64_t> solution(result.
solution().begin(),
407 for (
int i = solution_postprocessors_.size(); --i >= 0;) {
408 solution_postprocessors_[i](&solution);
412 for (
int i = postprocessors_.size(); --i >= 0;) {
413 postprocessors_[i](&result);
419 absl::MutexLock mutex_lock(&mutex_);
424 std::vector<int64_t> temp;
425 for (
int i = 0; i < solutions_.NumSolutions(); ++i) {
426 temp = solutions_.GetSolution(i).variable_values;
427 for (
int i = solution_postprocessors_.size(); --i >= 0;) {
428 solution_postprocessors_[i](&temp);
431 temp.begin(), temp.end());
434 for (
int i = final_postprocessors_.size(); --i >= 0;) {
435 final_postprocessors_[i](&result);
441 void SharedResponseManager::FillObjectiveValuesInBestResponse() {
442 if (objective_or_null_ ==
nullptr)
return;
446 best_response_.clear_objective_value();
447 best_response_.clear_best_objective_bound();
448 best_response_.clear_inner_objective_lower_bound();
455 best_response_.set_objective_value(
458 best_response_.set_objective_value(
463 best_response_.set_inner_objective_lower_bound(
465 best_response_.set_best_objective_bound(
469 best_response_.set_gap_integral(gap_integral_);
474 absl::MutexLock mutex_lock(&mutex_);
480 solution.variable_values.assign(
response.solution().begin(),
482 solutions_.
Add(solution);
485 if (objective_or_null_ !=
nullptr) {
486 const int64_t objective_value =
492 solution.variable_values.assign(
response.solution().begin(),
494 solution.rank = objective_value;
495 solutions_.
Add(solution);
499 if (objective_value > inner_objective_upper_bound_)
return;
505 DCHECK_GE(objective_value, inner_objective_lower_bound_);
507 DCHECK_LT(objective_value, best_solution_objective_value_);
508 best_solution_objective_value_ = objective_value;
511 inner_objective_upper_bound_ = objective_value - 1;
517 if (update_integral_on_each_change_) {
518 solutions_.Synchronize();
529 best_response_.set_solution_info(
response.solution_info());
530 *best_response_.mutable_solution() =
response.solution();
533 if (objective_or_null_ !=
nullptr &&
534 inner_objective_lower_bound_ > inner_objective_upper_bound_) {
541 std::string solution_info =
response.solution_info();
542 if (
model !=
nullptr) {
544 const int64_t num_fixed =
model->Get<
SatSolver>()->NumFixedVariables();
545 absl::StrAppend(&solution_info,
" fixed_bools:", num_fixed,
"/",
549 if (objective_or_null_ !=
nullptr) {
558 RegisterSolutionFound(solution_info);
559 SOLVER_LOG(logger_, ProgressMessage(absl::StrCat(num_solutions_),
560 wall_timer_.
Get(), best, lb, ub,
563 SOLVER_LOG(logger_, SatProgressMessage(absl::StrCat(num_solutions_),
564 wall_timer_.
Get(), solution_info));
570 TestGapLimitsIfNeeded();
571 if (!callbacks_.empty()) {
572 SetStatsFromModelInternal(
model);
574 for (
const auto& pair : callbacks_) {
579 #if !defined(__PORTABLE_PLATFORM__) 582 if (absl::GetFlag(FLAGS_cp_model_dump_solutions)) {
583 const std::string
file =
584 absl::StrCat(dump_prefix_,
"solution_", num_solutions_,
".pb.txt");
585 LOG(
INFO) <<
"Dumping solution to '" <<
file <<
"'.";
588 #endif // __PORTABLE_PLATFORM__ 592 #if !defined(__PORTABLE_PLATFORM__) 593 if (absl::GetFlag(FLAGS_cp_model_load_debug_solution).empty())
return;
597 LOG(
INFO) <<
"Reading solution from '" 598 << absl::GetFlag(FLAGS_cp_model_load_debug_solution) <<
"'.";
604 debug_solution.resize(
606 for (
int i = 0; i <
response.solution().size(); ++i) {
607 if (!mapping.IsInteger(i))
continue;
608 const IntegerVariable
var = mapping.Integer(i);
616 if (objective_def ==
nullptr)
return;
618 const IntegerVariable objective_var = objective_def->
objective_var;
619 const int64_t objective_value =
621 debug_solution[objective_var] = objective_value;
622 debug_solution[
NegationOf(objective_var)] = -objective_value;
623 #endif // __PORTABLE_PLATFORM__ 627 absl::MutexLock mutex_lock(&mutex_);
628 SetStatsFromModelInternal(
model);
631 void SharedResponseManager::SetStatsFromModelInternal(
Model*
model) {
632 if (
model ==
nullptr)
return;
635 best_response_.set_num_booleans(sat_solver->NumVariables());
636 best_response_.set_num_branches(sat_solver->num_branches());
637 best_response_.set_num_conflicts(sat_solver->num_failures());
638 best_response_.set_num_binary_propagations(sat_solver->num_propagations());
639 best_response_.set_num_restarts(sat_solver->num_restarts());
640 best_response_.set_num_integer_propagations(
641 integer_trail ==
nullptr ? 0 : integer_trail->num_enqueues());
643 best_response_.set_wall_time(
time_limit->GetElapsedTime());
644 best_response_.set_deterministic_time(
647 int64_t num_lp_iters = 0;
650 num_lp_iters += lp->total_num_simplex_iterations();
652 best_response_.set_num_lp_iterations(num_lp_iters);
656 absl::MutexLock mutex_lock(&mutex_);
662 if (improvement_info.empty())
return "";
665 for (
int i = 0; i < improvement_info.size(); ++i) {
666 if (!std::isalnum(improvement_info[i]) && improvement_info[i] !=
'_') {
667 return improvement_info.substr(0, i);
671 return improvement_info;
674 void SharedResponseManager::RegisterSolutionFound(
675 const std::string& improvement_info) {
676 if (improvement_info.empty())
return;
680 void SharedResponseManager::RegisterObjectiveBoundImprovement(
681 const std::string& improvement_info) {
682 if (improvement_info.empty() || improvement_info ==
"initial domain")
return;
687 absl::MutexLock mutex_lock(&mutex_);
688 if (!primal_improvements_count_.empty()) {
689 SOLVER_LOG(logger_,
"Solutions found per subsolver:");
690 for (
const auto& entry : primal_improvements_count_) {
691 SOLVER_LOG(logger_,
" '", entry.first,
"': ", entry.second);
694 if (!dual_improvements_count_.empty()) {
696 SOLVER_LOG(logger_,
"Objective bounds found per subsolver:");
697 for (
const auto& entry : dual_improvements_count_) {
698 SOLVER_LOG(logger_,
" '", entry.first,
"': ", entry.second);
706 lower_bounds_(num_variables_, std::numeric_limits<int64_t>::
min()),
707 upper_bounds_(num_variables_, std::numeric_limits<int64_t>::
max()),
708 synchronized_lower_bounds_(num_variables_,
709 std::numeric_limits<int64_t>::
min()),
710 synchronized_upper_bounds_(num_variables_,
711 std::numeric_limits<int64_t>::
max()) {
712 changed_variables_since_last_synchronize_.ClearAndResize(num_variables_);
713 for (
int i = 0; i < num_variables_; ++i) {
714 lower_bounds_[i] =
model_proto.variables(i).domain(0);
715 const int domain_size =
model_proto.variables(i).domain_size();
716 upper_bounds_[i] =
model_proto.variables(i).domain(domain_size - 1);
717 synchronized_lower_bounds_[i] = lower_bounds_[i];
718 synchronized_upper_bounds_[i] = upper_bounds_[i];
724 const std::vector<int>& variables,
725 const std::vector<int64_t>& new_lower_bounds,
726 const std::vector<int64_t>& new_upper_bounds) {
727 CHECK_EQ(variables.size(), new_lower_bounds.size());
728 CHECK_EQ(variables.size(), new_upper_bounds.size());
729 int num_improvements = 0;
731 absl::MutexLock mutex_lock(&mutex_);
732 for (
int i = 0; i < variables.size(); ++i) {
733 const int var = variables[i];
734 if (
var >= num_variables_)
continue;
735 const int64_t old_lb = lower_bounds_[
var];
736 const int64_t old_ub = upper_bounds_[
var];
737 const int64_t new_lb = new_lower_bounds[i];
738 const int64_t new_ub = new_upper_bounds[i];
739 const bool changed_lb = new_lb > old_lb;
740 const bool changed_ub = new_ub < old_ub;
742 if (!changed_lb && !changed_ub)
continue;
745 lower_bounds_[
var] = new_lb;
748 upper_bounds_[
var] = new_ub;
750 changed_variables_since_last_synchronize_.Set(
var);
755 if (num_improvements > 0) {
756 VLOG(2) << worker_name <<
" exports " << num_improvements
765 const std::vector<int64_t>& solution,
766 const std::vector<int>& variables_to_fix) {
767 absl::MutexLock mutex_lock(&mutex_);
773 for (
const int var : variables_to_fix) {
774 const int64_t
value = solution[
var];
776 VLOG(1) <<
"Incompatibility in FixVariablesFromPartialSolution() " 777 <<
"var: " <<
var <<
" value: " <<
value <<
" bounds: [" 778 << lower_bounds_[
var] <<
"," << upper_bounds_[
var] <<
"]";
784 for (
const int var : variables_to_fix) {
785 const int64_t old_lb = lower_bounds_[
var];
786 const int64_t old_ub = upper_bounds_[
var];
787 const bool changed_lb = solution[
var] > old_lb;
788 const bool changed_ub = solution[
var] < old_ub;
789 if (!changed_lb && !changed_ub)
continue;
791 lower_bounds_[
var] = solution[
var];
792 upper_bounds_[
var] = solution[
var];
793 changed_variables_since_last_synchronize_.Set(
var);
798 absl::MutexLock mutex_lock(&mutex_);
800 changed_variables_since_last_synchronize_.PositionsSetAtLeastOnce()) {
801 synchronized_lower_bounds_[
var] = lower_bounds_[
var];
802 synchronized_upper_bounds_[
var] = upper_bounds_[
var];
803 for (
int j = 0; j < id_to_changed_variables_.size(); ++j) {
804 id_to_changed_variables_[j].Set(
var);
807 changed_variables_since_last_synchronize_.ClearAll();
811 absl::MutexLock mutex_lock(&mutex_);
812 const int id = id_to_changed_variables_.size();
813 id_to_changed_variables_.resize(
id + 1);
814 id_to_changed_variables_[id].ClearAndResize(num_variables_);
815 for (
int var = 0;
var < num_variables_; ++
var) {
819 if (lb != synchronized_lower_bounds_[
var] ||
820 ub != synchronized_upper_bounds_[
var]) {
821 id_to_changed_variables_[id].Set(
var);
828 int id, std::vector<int>* variables, std::vector<int64_t>* new_lower_bounds,
829 std::vector<int64_t>* new_upper_bounds) {
831 new_lower_bounds->clear();
832 new_upper_bounds->clear();
834 absl::MutexLock mutex_lock(&mutex_);
835 for (
const int var : id_to_changed_variables_[
id].PositionsSetAtLeastOnce()) {
836 variables->push_back(
var);
837 new_lower_bounds->push_back(synchronized_lower_bounds_[
var]);
838 new_upper_bounds->push_back(synchronized_upper_bounds_[
var]);
840 id_to_changed_variables_[id].ClearAll();
IntegerValue GetInnerObjectiveUpperBound()
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
::operations_research::sat::CpSolverSolution * add_additional_solutions()
void SetStatsFromModel(Model *model)
#define SOLVER_LOG(logger,...)
#define CHECK_GE(val1, val2)
Class that owns everything related to a particular optimization model.
void AddNewSolution(const std::vector< double > &lp_solution)
IntegerVariable NumIntegerVariables() const
ModelSharedTimeLimit * time_limit
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
IntegerValue GetInnerObjectiveLowerBound()
absl::Status SetTextProto(const absl::string_view &filename, const google::protobuf::Message &proto, int flags)
void AddSolutionPostprocessor(std::function< void(std::vector< int64_t > *)> postprocessor)
#define VLOG(verboselevel)
void UnregisterCallback(int callback_id)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
int64_t ScaleInnerObjectiveValue(const CpObjectiveProto &proto, int64_t value)
void NewSolution(const CpSolverResponse &response, Model *model)
void AddFinalResponsePostprocessor(std::function< void(CpSolverResponse *)> postprocessor)
void NewLPSolution(std::vector< double > lp_solution)
SharedResponseManager(Model *model)
::PROTOBUF_NAMESPACE_ID::RepeatedField< int64_t > * mutable_solution()
bool fill_additional_solutions_in_response() const
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
int64_t Max() const
Returns the max value of the domain.
int NumVariables(const VariablesProto &variables)
void AddUnsatCore(const std::vector< int > &core)
void UpdateInnerObjectiveBounds(const std::string &update_info, IntegerValue lb, IntegerValue ub)
void LogMessage(std::string message)
std::string ExtractSubSolverName(const std::string &improvement_info)
void DisplayImprovementStatistics()
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
int64_t Min() const
Returns the min value of the domain.
CpSolverResponse GetResponse(bool full_response=true)
IntegerValue BestSolutionInnerObjectiveValue()
double ScaleObjectiveValue(const CpObjectiveProto &proto, int64_t value)
void Add(const Solution &solution)
int AddSolutionCallback(std::function< void(const CpSolverResponse &)> callback)
bool ProblemIsSolved() const
int64_t solution(int index) const
void InitializeObjective(const CpModelProto &cp_model)
SharedBoundsManager(const CpModelProto &model_proto)
const ::operations_research::sat::CpObjectiveProto & objective() const
IntegerVariable objective_var
void AddInternal(const Solution &solution) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_)
ABSL_FLAG(bool, cp_model_dump_solutions, false, "DEBUG ONLY. If true, all the intermediate solution will be dumped " "under '\"FLAGS_cp_model_dump_prefix\" + \"solution_xxx.pb.txt\"'.")
#define DCHECK_GE(val1, val2)
SharedResponseManager * response
#define CHECK_EQ(val1, val2)
::operations_research::sat::CpSolverStatus status() const
bool has_objective() const
void SetUpdateGapIntegralOnEachChange(bool set)
CpModelProto const * model_proto
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
void FixVariablesFromPartialSolution(const std::vector< int64_t > &solution, const std::vector< int > &variables_to_fix)
bool enumerate_all_solutions() const
We call domain any subset of Int64 = [kint64min, kint64max].
::PROTOBUF_NAMESPACE_ID::RepeatedField< int64_t > * mutable_values()
void ReportPotentialNewBounds(const CpModelProto &model_proto, const std::string &worker_name, const std::vector< int > &variables, const std::vector< int64_t > &new_lower_bounds, const std::vector< int64_t > &new_upper_bounds)
void NotifyThatImprovingProblemIsInfeasible(const std::string &worker_info)
IntegerValue SynchronizedInnerObjectiveUpperBound()
bool LoggingIsEnabled() const
absl::Status GetTextProto(const absl::string_view &filename, google::protobuf::Message *proto, int flags)
void AddResponsePostprocessor(std::function< void(CpSolverResponse *)> postprocessor)
bool HasNewSolution() const
#define DCHECK_LE(val1, val2)
double GapIntegral() const
void GetChangedBounds(int id, std::vector< int > *variables, std::vector< int64_t > *new_lower_bounds, std::vector< int64_t > *new_upper_bounds)
Collection of objects used to extend the Constraint Solver library.
int64_t ComputeInnerObjective(const CpObjectiveProto &objective, const CpSolverResponse &response)
IntegerValue SynchronizedInnerObjectiveLowerBound()
double GetElapsedDeterministicTime() const
std::vector< double > GetNewSolution()
void SetGapLimitsFromParameters(const SatParameters ¶meters)
double scaling_factor() const
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
bool IsEmpty() const
Returns true if this is the empty set.
#define DCHECK_LT(val1, val2)
void LoadDebugSolution(Model *)
int64_t domain(int index) const
void NewRelaxationSolution(const CpSolverResponse &response)