16 #if !defined(__PORTABLE_PLATFORM__)
19 #endif // __PORTABLE_PLATFORM__
21 #include "absl/container/flat_hash_set.h"
22 #include "absl/random/random.h"
33 cp_model_dump_solutions,
false,
34 "DEBUG ONLY. If true, all the intermediate solution will be dumped "
35 "under '\"FLAGS_cp_model_dump_prefix\" + \"solution_xxx.pb.txt\"'.");
37 cp_model_load_debug_solution,
"",
38 "DEBUG ONLY. When this is set to a non-empty file name, "
39 "we will interpret this as an internal solution which can be used for "
40 "debugging. For instance we use it to identify wrong cuts/reasons.");
50 if (
response.solution().empty())
return;
54 solution.variable_values.assign(
response.solution().begin(),
61 solution.rank = -
response.best_objective_bound();
67 const std::vector<double>& lp_solution) {
71 if (lp_solution.empty())
return;
75 solution.variable_values.assign(lp_solution.begin(), lp_solution.end());
78 absl::MutexLock mutex_lock(&
mutex_);
79 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);
107 bool enumerate_all_solutions,
108 const CpModelProto*
proto,
111 : log_updates_(log_updates),
112 enumerate_all_solutions_(enumerate_all_solutions),
113 model_proto_(*
proto),
115 shared_time_limit_(shared_time_limit),
120 void LogNewSolution(
const std::string& event_or_solution_count,
121 double time_in_seconds,
double obj_best,
double obj_lb,
122 double obj_ub,
const std::string& solution_info) {
123 const std::string obj_next =
124 absl::StrFormat(
"next:[%.9g,%.9g]", obj_lb, obj_ub);
125 LOG(INFO) << absl::StrFormat(
"#%-5s %6.2fs best:%-5.9g %-15s %s",
126 event_or_solution_count, time_in_seconds,
127 obj_best, obj_next, solution_info);
130 void LogNewSatSolution(
const std::string& event_or_solution_count,
131 double time_in_seconds,
132 const std::string& solution_info) {
133 LOG(INFO) << absl::StrFormat(
"#%-5s %6.2fs %s", event_or_solution_count,
134 time_in_seconds, solution_info);
140 absl::MutexLock mutex_lock(&mutex_);
141 if (!model_proto_.has_objective())
return;
144 const double time_delta = current_time - last_primal_integral_time_stamp_;
145 last_primal_integral_time_stamp_ = current_time;
152 const CpObjectiveProto& obj = model_proto_.objective();
153 const double factor =
154 obj.scaling_factor() != 0.0 ? std::abs(obj.scaling_factor()) : 1.0;
155 const double bounds_delta = std::log(
156 1 + factor * std::abs(
static_cast<double>(inner_objective_upper_bound_) -
157 static_cast<double>(inner_objective_lower_bound_)));
158 primal_integral_ += time_delta * bounds_delta;
163 absl::MutexLock mutex_lock(&mutex_);
164 if (!model_proto_.has_objective())
return;
165 absolute_gap_limit_ =
parameters.absolute_gap_limit();
166 relative_gap_limit_ =
parameters.relative_gap_limit();
169 void SharedResponseManager::TestGapLimitsIfNeeded() {
170 if (absolute_gap_limit_ == 0 && relative_gap_limit_ == 0)
return;
174 const CpObjectiveProto& obj = model_proto_.objective();
175 const double user_best =
177 const double user_bound =
179 const double gap = std::abs(user_best - user_bound);
180 if (gap <= absolute_gap_limit_) {
181 LOG_IF(INFO, log_updates_)
182 <<
"Absolute gap limit of " << absolute_gap_limit_ <<
" reached.";
188 shared_time_limit_->
Stop();
190 if (gap /
std::max(1.0, std::abs(user_best)) < relative_gap_limit_) {
191 LOG_IF(INFO, log_updates_)
192 <<
"Relative gap limit of " << relative_gap_limit_ <<
" reached.";
196 shared_time_limit_->
Stop();
201 const std::string& worker_info, IntegerValue lb, IntegerValue ub) {
202 absl::MutexLock mutex_lock(&mutex_);
203 CHECK(model_proto_.has_objective());
210 if (inner_objective_lower_bound_ > inner_objective_upper_bound_) {
215 (lb > inner_objective_lower_bound_ || ub < inner_objective_upper_bound_);
216 if (lb > inner_objective_lower_bound_) {
221 DCHECK_LE(inner_objective_upper_bound_, best_solution_objective_value_);
222 inner_objective_lower_bound_ =
223 std::min(best_solution_objective_value_, lb.value());
225 if (ub < inner_objective_upper_bound_) {
226 inner_objective_upper_bound_ = ub.value();
228 if (inner_objective_lower_bound_ > inner_objective_upper_bound_) {
235 if (log_updates_) LogNewSatSolution(
"Done", wall_timer_.
Get(), worker_info);
238 if (log_updates_ && change) {
239 const CpObjectiveProto& obj = model_proto_.objective();
244 if (model_proto_.objective().scaling_factor() < 0) {
245 std::swap(new_lb, new_ub);
247 LogNewSolution(
"Bound", wall_timer_.
Get(), best, new_lb, new_ub,
250 if (change) TestGapLimitsIfNeeded();
257 const std::string& worker_info) {
258 absl::MutexLock mutex_lock(&mutex_);
264 if (!model_proto_.has_objective()) {
265 best_response_.set_all_solutions_were_found(
true);
270 inner_objective_lower_bound_ = best_solution_objective_value_;
272 CHECK_EQ(num_solutions_, 0);
275 if (log_updates_) LogNewSatSolution(
"Done", wall_timer_.
Get(), worker_info);
279 absl::MutexLock mutex_lock(&mutex_);
280 best_response_.clear_sufficient_assumptions_for_infeasibility();
281 for (
const int ref : core) {
282 best_response_.add_sufficient_assumptions_for_infeasibility(ref);
287 absl::MutexLock mutex_lock(&mutex_);
288 return IntegerValue(inner_objective_lower_bound_);
292 absl::MutexLock mutex_lock(&mutex_);
293 return IntegerValue(inner_objective_upper_bound_);
297 absl::MutexLock mutex_lock(&mutex_);
298 synchronized_inner_objective_lower_bound_ =
299 IntegerValue(inner_objective_lower_bound_);
300 synchronized_inner_objective_upper_bound_ =
301 IntegerValue(inner_objective_upper_bound_);
305 absl::MutexLock mutex_lock(&mutex_);
306 return synchronized_inner_objective_lower_bound_;
310 absl::MutexLock mutex_lock(&mutex_);
311 return synchronized_inner_objective_upper_bound_;
315 absl::MutexLock mutex_lock(&mutex_);
316 return IntegerValue(best_solution_objective_value_);
320 absl::MutexLock mutex_lock(&mutex_);
321 return primal_integral_;
325 std::function<
void(
const CpSolverResponse&)>
callback) {
326 absl::MutexLock mutex_lock(&mutex_);
327 const int id = next_callback_id_++;
328 callbacks_.emplace_back(
id, std::move(
callback));
333 absl::MutexLock mutex_lock(&mutex_);
334 for (
int i = 0; i < callbacks_.size(); ++i) {
335 if (callbacks_[i].first == callback_id) {
336 callbacks_.erase(callbacks_.begin() + i);
340 LOG(DFATAL) <<
"Callback id " << callback_id <<
" not registered.";
344 absl::MutexLock mutex_lock(&mutex_);
345 FillObjectiveValuesInBestResponse();
346 return best_response_;
349 void SharedResponseManager::FillObjectiveValuesInBestResponse() {
350 if (!model_proto_.has_objective())
return;
351 const CpObjectiveProto& obj = model_proto_.objective();
354 best_response_.clear_objective_value();
355 best_response_.clear_best_objective_bound();
362 best_response_.set_objective_value(
365 best_response_.set_objective_value(
370 best_response_.set_best_objective_bound(
374 best_response_.set_primal_integral(primal_integral_);
379 absl::MutexLock mutex_lock(&mutex_);
381 if (model_proto_.has_objective()) {
382 const int64 objective_value =
388 solution.variable_values.assign(
response.solution().begin(),
390 solution.rank = objective_value;
391 solutions_.
Add(solution);
395 if (objective_value > inner_objective_upper_bound_)
return;
401 DCHECK_GE(objective_value, inner_objective_lower_bound_);
403 DCHECK_LT(objective_value, best_solution_objective_value_);
404 best_solution_objective_value_ = objective_value;
407 inner_objective_upper_bound_ = objective_value - 1;
412 if (!model_proto_.has_objective() && !enumerate_all_solutions_) {
418 best_response_.set_solution_info(
response.solution_info());
419 *best_response_.mutable_solution() =
response.solution();
420 *best_response_.mutable_solution_lower_bounds() =
422 *best_response_.mutable_solution_upper_bounds() =
426 if (model_proto_.has_objective() &&
427 inner_objective_lower_bound_ > inner_objective_upper_bound_) {
434 std::string solution_info =
response.solution_info();
435 if (
model !=
nullptr) {
436 absl::StrAppend(&solution_info,
440 if (model_proto_.has_objective()) {
441 const CpObjectiveProto& obj = model_proto_.objective();
446 if (model_proto_.objective().scaling_factor() < 0) {
449 LogNewSolution(absl::StrCat(num_solutions_), wall_timer_.
Get(), best, lb,
452 LogNewSatSolution(absl::StrCat(num_solutions_), wall_timer_.
Get(),
459 TestGapLimitsIfNeeded();
460 if (!callbacks_.empty()) {
461 FillObjectiveValuesInBestResponse();
462 SetStatsFromModelInternal(
model);
463 for (
const auto& pair : callbacks_) {
464 pair.second(best_response_);
468 #if !defined(__PORTABLE_PLATFORM__)
471 if (FLAGS_cp_model_dump_solutions && log_updates_) {
472 const std::string
file =
473 absl::StrCat(dump_prefix_,
"solution_", num_solutions_,
".pbtxt");
474 LOG(INFO) <<
"Dumping solution to '" <<
file <<
"'.";
477 #endif // __PORTABLE_PLATFORM__
481 #if !defined(__PORTABLE_PLATFORM__)
482 if (FLAGS_cp_model_load_debug_solution.empty())
return;
486 LOG(INFO) <<
"Reading solution from '" << FLAGS_cp_model_load_debug_solution
493 debug_solution.resize(
495 for (
int i = 0; i <
response.solution().size(); ++i) {
496 if (!mapping.IsInteger(i))
continue;
497 const IntegerVariable
var = mapping.Integer(i);
505 if (objective_def ==
nullptr)
return;
507 const IntegerVariable objective_var = objective_def->
objective_var;
508 const int64 objective_value =
510 debug_solution[objective_var] = objective_value;
511 debug_solution[
NegationOf(objective_var)] = -objective_value;
512 #endif // __PORTABLE_PLATFORM__
516 absl::MutexLock mutex_lock(&mutex_);
517 SetStatsFromModelInternal(
model);
520 void SharedResponseManager::SetStatsFromModelInternal(
Model*
model) {
521 if (
model ==
nullptr)
return;
524 best_response_.set_num_booleans(sat_solver->NumVariables());
525 best_response_.set_num_branches(sat_solver->num_branches());
526 best_response_.set_num_conflicts(sat_solver->num_failures());
527 best_response_.set_num_binary_propagations(sat_solver->num_propagations());
528 best_response_.set_num_integer_propagations(
529 integer_trail ==
nullptr ? 0 : integer_trail->num_enqueues());
531 best_response_.set_wall_time(
time_limit->GetElapsedTime());
532 best_response_.set_deterministic_time(
537 absl::MutexLock mutex_lock(&mutex_);
545 lower_bounds_(num_variables_,
kint64min),
546 upper_bounds_(num_variables_,
kint64max),
547 synchronized_lower_bounds_(num_variables_,
kint64min),
548 synchronized_upper_bounds_(num_variables_,
kint64max) {
549 changed_variables_since_last_synchronize_.ClearAndResize(num_variables_);
550 for (
int i = 0; i < num_variables_; ++i) {
551 lower_bounds_[i] =
model_proto.variables(i).domain(0);
552 const int domain_size =
model_proto.variables(i).domain_size();
553 upper_bounds_[i] =
model_proto.variables(i).domain(domain_size - 1);
554 synchronized_lower_bounds_[i] = lower_bounds_[i];
555 synchronized_upper_bounds_[i] = upper_bounds_[i];
560 const CpModelProto&
model_proto,
const std::string& worker_name,
561 const std::vector<int>& variables,
562 const std::vector<int64>& new_lower_bounds,
563 const std::vector<int64>& new_upper_bounds) {
564 CHECK_EQ(variables.size(), new_lower_bounds.size());
565 CHECK_EQ(variables.size(), new_upper_bounds.size());
567 absl::MutexLock mutex_lock(&mutex_);
568 for (
int i = 0; i < variables.size(); ++i) {
569 const int var = variables[i];
570 if (
var >= num_variables_)
continue;
571 const int64 old_lb = lower_bounds_[
var];
572 const int64 old_ub = upper_bounds_[
var];
573 const int64 new_lb = new_lower_bounds[i];
574 const int64 new_ub = new_upper_bounds[i];
575 const bool changed_lb = new_lb > old_lb;
576 const bool changed_ub = new_ub < old_ub;
578 if (!changed_lb && !changed_ub)
continue;
581 lower_bounds_[
var] = new_lb;
584 upper_bounds_[
var] = new_ub;
586 changed_variables_since_last_synchronize_.Set(
var);
589 const IntegerVariableProto& var_proto =
model_proto.variables(
var);
590 const std::string& var_name =
591 var_proto.name().empty() ? absl::StrCat(
"anonymous_var(",
var,
")")
593 LOG(INFO) <<
" '" << worker_name <<
"' exports new bounds for "
594 << var_name <<
": from [" << old_lb <<
", " << old_ub
595 <<
"] to [" << new_lb <<
", " << new_ub <<
"]";
601 absl::MutexLock mutex_lock(&mutex_);
603 changed_variables_since_last_synchronize_.PositionsSetAtLeastOnce()) {
604 synchronized_lower_bounds_[
var] = lower_bounds_[
var];
605 synchronized_upper_bounds_[
var] = upper_bounds_[
var];
606 for (
int j = 0; j < id_to_changed_variables_.size(); ++j) {
607 id_to_changed_variables_[j].Set(
var);
610 changed_variables_since_last_synchronize_.ClearAll();
614 absl::MutexLock mutex_lock(&mutex_);
615 const int id = id_to_changed_variables_.size();
616 id_to_changed_variables_.resize(
id + 1);
617 id_to_changed_variables_[id].ClearAndResize(num_variables_);
618 for (
int var = 0;
var < num_variables_; ++
var) {
619 const int64 lb = model_proto_.variables(
var).domain(0);
620 const int domain_size = model_proto_.variables(
var).domain_size();
621 const int64 ub = model_proto_.variables(
var).domain(domain_size - 1);
622 if (lb != synchronized_lower_bounds_[
var] ||
623 ub != synchronized_upper_bounds_[
var]) {
624 id_to_changed_variables_[id].Set(
var);
631 int id, std::vector<int>* variables, std::vector<int64>* new_lower_bounds,
632 std::vector<int64>* new_upper_bounds) {
634 new_lower_bounds->clear();
635 new_upper_bounds->clear();
637 absl::MutexLock mutex_lock(&mutex_);
638 for (
const int var : id_to_changed_variables_[
id].PositionsSetAtLeastOnce()) {
639 variables->push_back(
var);
640 new_lower_bounds->push_back(synchronized_lower_bounds_[
var]);
641 new_upper_bounds->push_back(synchronized_upper_bounds_[
var]);
643 id_to_changed_variables_[id].ClearAll();