16 #if !defined(__PORTABLE_PLATFORM__)
21 #include "absl/container/flat_hash_set.h"
22 #include "absl/random/random.h"
34 "DEBUG ONLY. If true, all the intermediate solution will be dumped "
35 "under '\"FLAGS_cp_model_dump_prefix\" + \"solution_xxx.pb.txt\"'.");
38 std::string, cp_model_load_debug_solution,
"",
39 "DEBUG ONLY. When this is set to a non-empty file name, "
40 "we will interpret this as an internal solution which can be used for "
41 "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 std::vector<double> lp_solution) {
68 if (lp_solution.empty())
return;
72 solution.variable_values = std::move(lp_solution);
75 absl::MutexLock mutex_lock(&
mutex_);
76 solution.rank = -num_synchronization_;
81 absl::MutexLock mutex_lock(&mutex_);
82 return !solutions_.empty();
86 absl::MutexLock mutex_lock(&mutex_);
87 std::vector<double> solution;
88 if (solutions_.empty())
return solution;
90 solution = std::move(solutions_.back());
91 solutions_.pop_back();
96 const std::vector<double>& lp_solution) {
97 absl::MutexLock mutex_lock(&mutex_);
98 solutions_.push_back(lp_solution);
103 bool enumerate_all_solutions,
104 const CpModelProto*
proto,
107 : log_updates_(log_updates),
108 enumerate_all_solutions_(enumerate_all_solutions),
109 model_proto_(*
proto),
111 shared_time_limit_(shared_time_limit),
116 void LogNewSolution(
const std::string& event_or_solution_count,
117 double time_in_seconds,
double obj_best,
double obj_lb,
118 double obj_ub,
const std::string& solution_info) {
119 const std::string obj_next =
120 absl::StrFormat(
"next:[%.9g,%.9g]", obj_lb, obj_ub);
121 LOG(
INFO) << absl::StrFormat(
"#%-5s %6.2fs best:%-5.9g %-15s %s",
122 event_or_solution_count, time_in_seconds,
123 obj_best, obj_next, solution_info);
126 void LogNewSatSolution(
const std::string& event_or_solution_count,
127 double time_in_seconds,
128 const std::string& solution_info) {
129 LOG(
INFO) << absl::StrFormat(
"#%-5s %6.2fs %s", event_or_solution_count,
130 time_in_seconds, solution_info);
136 absl::MutexLock mutex_lock(&mutex_);
137 update_integral_on_each_change_ = set;
141 absl::MutexLock mutex_lock(&mutex_);
142 UpdatePrimalIntegralInternal();
145 void SharedResponseManager::UpdatePrimalIntegralInternal() {
146 if (!model_proto_.has_objective())
return;
149 const double time_delta = current_time - last_primal_integral_time_stamp_;
156 const CpObjectiveProto& obj = model_proto_.objective();
157 const double factor =
158 obj.scaling_factor() != 0.0 ? std::abs(obj.scaling_factor()) : 1.0;
159 const double bounds_delta = std::log(1 + factor * last_absolute_gap_);
160 primal_integral_ += time_delta * bounds_delta;
163 last_primal_integral_time_stamp_ = current_time;
165 std::max(0.0,
static_cast<double>(inner_objective_upper_bound_) -
166 static_cast<double>(inner_objective_lower_bound_));
171 absl::MutexLock mutex_lock(&mutex_);
172 if (!model_proto_.has_objective())
return;
173 absolute_gap_limit_ =
parameters.absolute_gap_limit();
174 relative_gap_limit_ =
parameters.relative_gap_limit();
177 void SharedResponseManager::TestGapLimitsIfNeeded() {
181 if (update_integral_on_each_change_) UpdatePrimalIntegralInternal();
183 if (absolute_gap_limit_ == 0 && relative_gap_limit_ == 0)
return;
187 const CpObjectiveProto& obj = model_proto_.objective();
188 const double user_best =
190 const double user_bound =
192 const double gap = std::abs(user_best - user_bound);
193 if (gap <= absolute_gap_limit_) {
195 <<
"Absolute gap limit of " << absolute_gap_limit_ <<
" reached.";
201 shared_time_limit_->
Stop();
203 if (gap /
std::max(1.0, std::abs(user_best)) < relative_gap_limit_) {
205 <<
"Relative gap limit of " << relative_gap_limit_ <<
" reached.";
209 shared_time_limit_->
Stop();
214 const std::string& worker_info, IntegerValue lb, IntegerValue ub) {
215 absl::MutexLock mutex_lock(&mutex_);
216 CHECK(model_proto_.has_objective());
223 if (inner_objective_lower_bound_ > inner_objective_upper_bound_) {
228 (lb > inner_objective_lower_bound_ || ub < inner_objective_upper_bound_);
229 if (lb > inner_objective_lower_bound_) {
234 DCHECK_LE(inner_objective_upper_bound_, best_solution_objective_value_);
235 inner_objective_lower_bound_ =
236 std::min(best_solution_objective_value_, lb.value());
238 if (ub < inner_objective_upper_bound_) {
239 inner_objective_upper_bound_ = ub.value();
241 if (inner_objective_lower_bound_ > inner_objective_upper_bound_) {
248 if (update_integral_on_each_change_) UpdatePrimalIntegralInternal();
249 if (log_updates_) LogNewSatSolution(
"Done", wall_timer_.
Get(), worker_info);
252 if (log_updates_ && change) {
253 const CpObjectiveProto& obj = model_proto_.objective();
258 if (model_proto_.objective().scaling_factor() < 0) {
259 std::swap(new_lb, new_ub);
261 LogNewSolution(
"Bound", wall_timer_.
Get(), best, new_lb, new_ub,
264 if (change) TestGapLimitsIfNeeded();
271 const std::string& worker_info) {
272 absl::MutexLock mutex_lock(&mutex_);
278 if (!model_proto_.has_objective()) {
279 best_response_.set_all_solutions_were_found(
true);
284 inner_objective_lower_bound_ = best_solution_objective_value_;
285 if (update_integral_on_each_change_) UpdatePrimalIntegralInternal();
290 if (log_updates_) LogNewSatSolution(
"Done", wall_timer_.
Get(), worker_info);
294 absl::MutexLock mutex_lock(&mutex_);
295 best_response_.clear_sufficient_assumptions_for_infeasibility();
296 for (
const int ref : core) {
297 best_response_.add_sufficient_assumptions_for_infeasibility(ref);
302 absl::MutexLock mutex_lock(&mutex_);
303 return IntegerValue(inner_objective_lower_bound_);
307 absl::MutexLock mutex_lock(&mutex_);
308 return IntegerValue(inner_objective_upper_bound_);
312 absl::MutexLock mutex_lock(&mutex_);
313 synchronized_inner_objective_lower_bound_ =
314 IntegerValue(inner_objective_lower_bound_);
315 synchronized_inner_objective_upper_bound_ =
316 IntegerValue(inner_objective_upper_bound_);
320 absl::MutexLock mutex_lock(&mutex_);
321 return synchronized_inner_objective_lower_bound_;
325 absl::MutexLock mutex_lock(&mutex_);
326 return synchronized_inner_objective_upper_bound_;
330 absl::MutexLock mutex_lock(&mutex_);
331 return IntegerValue(best_solution_objective_value_);
335 absl::MutexLock mutex_lock(&mutex_);
336 return primal_integral_;
340 std::function<
void(
const CpSolverResponse&)>
callback) {
341 absl::MutexLock mutex_lock(&mutex_);
342 const int id = next_callback_id_++;
343 callbacks_.emplace_back(
id, std::move(
callback));
348 absl::MutexLock mutex_lock(&mutex_);
349 for (
int i = 0; i < callbacks_.size(); ++i) {
350 if (callbacks_[i].first == callback_id) {
351 callbacks_.erase(callbacks_.begin() + i);
355 LOG(DFATAL) <<
"Callback id " << callback_id <<
" not registered.";
359 absl::MutexLock mutex_lock(&mutex_);
360 FillObjectiveValuesInBestResponse();
361 return best_response_;
364 void SharedResponseManager::FillObjectiveValuesInBestResponse() {
365 if (!model_proto_.has_objective())
return;
366 const CpObjectiveProto& obj = model_proto_.objective();
369 best_response_.clear_objective_value();
370 best_response_.clear_best_objective_bound();
377 best_response_.set_objective_value(
380 best_response_.set_objective_value(
385 best_response_.set_best_objective_bound(
389 best_response_.set_primal_integral(primal_integral_);
394 absl::MutexLock mutex_lock(&mutex_);
396 if (model_proto_.has_objective()) {
397 const int64 objective_value =
403 solution.variable_values.assign(
response.solution().begin(),
405 solution.rank = objective_value;
406 solutions_.
Add(solution);
410 if (objective_value > inner_objective_upper_bound_)
return;
416 DCHECK_GE(objective_value, inner_objective_lower_bound_);
418 DCHECK_LT(objective_value, best_solution_objective_value_);
419 best_solution_objective_value_ = objective_value;
422 inner_objective_upper_bound_ = objective_value - 1;
427 if (!model_proto_.has_objective() && !enumerate_all_solutions_) {
433 best_response_.set_solution_info(
response.solution_info());
434 *best_response_.mutable_solution() =
response.solution();
435 *best_response_.mutable_solution_lower_bounds() =
437 *best_response_.mutable_solution_upper_bounds() =
441 if (model_proto_.has_objective() &&
442 inner_objective_lower_bound_ > inner_objective_upper_bound_) {
449 std::string solution_info =
response.solution_info();
450 if (
model !=
nullptr) {
453 absl::StrAppend(&solution_info,
" fixed_bools:", num_fixed,
"/",
457 if (model_proto_.has_objective()) {
458 const CpObjectiveProto& obj = model_proto_.objective();
463 if (model_proto_.objective().scaling_factor() < 0) {
466 LogNewSolution(absl::StrCat(num_solutions_), wall_timer_.
Get(), best, lb,
469 LogNewSatSolution(absl::StrCat(num_solutions_), wall_timer_.
Get(),
476 TestGapLimitsIfNeeded();
477 if (!callbacks_.empty()) {
478 FillObjectiveValuesInBestResponse();
479 SetStatsFromModelInternal(
model);
480 for (
const auto& pair : callbacks_) {
481 pair.second(best_response_);
485 #if !defined(__PORTABLE_PLATFORM__)
488 if (absl::GetFlag(FLAGS_cp_model_dump_solutions) && log_updates_) {
489 const std::string
file =
490 absl::StrCat(dump_prefix_,
"solution_", num_solutions_,
".pbtxt");
491 LOG(
INFO) <<
"Dumping solution to '" <<
file <<
"'.";
498 #if !defined(__PORTABLE_PLATFORM__)
499 if (absl::GetFlag(FLAGS_cp_model_load_debug_solution).empty())
return;
503 LOG(
INFO) <<
"Reading solution from '"
504 << absl::GetFlag(FLAGS_cp_model_load_debug_solution) <<
"'.";
510 debug_solution.resize(
512 for (
int i = 0; i <
response.solution().size(); ++i) {
513 if (!mapping.IsInteger(i))
continue;
514 const IntegerVariable
var = mapping.Integer(i);
522 if (objective_def ==
nullptr)
return;
524 const IntegerVariable objective_var = objective_def->
objective_var;
525 const int64 objective_value =
527 debug_solution[objective_var] = objective_value;
528 debug_solution[
NegationOf(objective_var)] = -objective_value;
533 absl::MutexLock mutex_lock(&mutex_);
534 SetStatsFromModelInternal(
model);
537 void SharedResponseManager::SetStatsFromModelInternal(
Model*
model) {
538 if (
model ==
nullptr)
return;
541 best_response_.set_num_booleans(sat_solver->NumVariables());
542 best_response_.set_num_branches(sat_solver->num_branches());
543 best_response_.set_num_conflicts(sat_solver->num_failures());
544 best_response_.set_num_binary_propagations(sat_solver->num_propagations());
545 best_response_.set_num_restarts(sat_solver->num_restarts());
546 best_response_.set_num_integer_propagations(
547 integer_trail ==
nullptr ? 0 : integer_trail->num_enqueues());
549 best_response_.set_wall_time(
time_limit->GetElapsedTime());
550 best_response_.set_deterministic_time(
553 int64 num_lp_iters = 0;
556 num_lp_iters += lp->total_num_simplex_iterations();
558 best_response_.set_num_lp_iterations(num_lp_iters);
562 absl::MutexLock mutex_lock(&mutex_);
570 lower_bounds_(num_variables_,
kint64min),
571 upper_bounds_(num_variables_,
kint64max),
572 synchronized_lower_bounds_(num_variables_,
kint64min),
573 synchronized_upper_bounds_(num_variables_,
kint64max) {
574 changed_variables_since_last_synchronize_.ClearAndResize(num_variables_);
575 for (
int i = 0; i < num_variables_; ++i) {
576 lower_bounds_[i] =
model_proto.variables(i).domain(0);
577 const int domain_size =
model_proto.variables(i).domain_size();
578 upper_bounds_[i] =
model_proto.variables(i).domain(domain_size - 1);
579 synchronized_lower_bounds_[i] = lower_bounds_[i];
580 synchronized_upper_bounds_[i] = upper_bounds_[i];
585 const CpModelProto&
model_proto,
const std::string& worker_name,
586 const std::vector<int>& variables,
587 const std::vector<int64>& new_lower_bounds,
588 const std::vector<int64>& new_upper_bounds) {
589 CHECK_EQ(variables.size(), new_lower_bounds.size());
590 CHECK_EQ(variables.size(), new_upper_bounds.size());
591 int num_improvements = 0;
593 absl::MutexLock mutex_lock(&mutex_);
594 for (
int i = 0; i < variables.size(); ++i) {
595 const int var = variables[i];
596 if (
var >= num_variables_)
continue;
597 const int64 old_lb = lower_bounds_[
var];
598 const int64 old_ub = upper_bounds_[
var];
599 const int64 new_lb = new_lower_bounds[i];
600 const int64 new_ub = new_upper_bounds[i];
601 const bool changed_lb = new_lb > old_lb;
602 const bool changed_ub = new_ub < old_ub;
604 if (!changed_lb && !changed_ub)
continue;
607 lower_bounds_[
var] = new_lb;
610 upper_bounds_[
var] = new_ub;
612 changed_variables_since_last_synchronize_.Set(
var);
617 if (num_improvements > 0) {
618 VLOG(2) << worker_name <<
" exports " << num_improvements
624 absl::MutexLock mutex_lock(&mutex_);
626 changed_variables_since_last_synchronize_.PositionsSetAtLeastOnce()) {
627 synchronized_lower_bounds_[
var] = lower_bounds_[
var];
628 synchronized_upper_bounds_[
var] = upper_bounds_[
var];
629 for (
int j = 0; j < id_to_changed_variables_.size(); ++j) {
630 id_to_changed_variables_[j].Set(
var);
633 changed_variables_since_last_synchronize_.ClearAll();
637 absl::MutexLock mutex_lock(&mutex_);
638 const int id = id_to_changed_variables_.size();
639 id_to_changed_variables_.resize(
id + 1);
640 id_to_changed_variables_[id].ClearAndResize(num_variables_);
641 for (
int var = 0;
var < num_variables_; ++
var) {
642 const int64 lb = model_proto_.variables(
var).domain(0);
643 const int domain_size = model_proto_.variables(
var).domain_size();
644 const int64 ub = model_proto_.variables(
var).domain(domain_size - 1);
645 if (lb != synchronized_lower_bounds_[
var] ||
646 ub != synchronized_upper_bounds_[
var]) {
647 id_to_changed_variables_[id].Set(
var);
654 int id, std::vector<int>* variables, std::vector<int64>* new_lower_bounds,
655 std::vector<int64>* new_upper_bounds) {
657 new_lower_bounds->clear();
658 new_upper_bounds->clear();
660 absl::MutexLock mutex_lock(&mutex_);
661 for (
const int var : id_to_changed_variables_[
id].PositionsSetAtLeastOnce()) {
662 variables->push_back(
var);
663 new_lower_bounds->push_back(synchronized_lower_bounds_[
var]);
664 new_upper_bounds->push_back(synchronized_upper_bounds_[
var]);
666 id_to_changed_variables_[id].ClearAll();
#define LOG_IF(severity, condition)
#define DCHECK_LE(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK_LT(val1, val2)
#define VLOG(verboselevel)
double GetElapsedDeterministicTime() const
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
IntegerVariable NumIntegerVariables() const
Class that owns everything related to a particular optimization model.
SharedBoundsManager(const CpModelProto &model_proto)
void GetChangedBounds(int id, std::vector< int > *variables, std::vector< int64 > *new_lower_bounds, std::vector< int64 > *new_upper_bounds)
void ReportPotentialNewBounds(const CpModelProto &model_proto, const std::string &worker_name, const std::vector< int > &variables, const std::vector< int64 > &new_lower_bounds, const std::vector< int64 > &new_upper_bounds)
void AddNewSolution(const std::vector< double > &lp_solution)
std::vector< double > GetNewSolution()
bool HasNewSolution() const
void NewLPSolution(std::vector< double > lp_solution)
void NewRelaxationSolution(const CpSolverResponse &response)
bool ProblemIsSolved() const
CpSolverResponse GetResponse()
void SetStatsFromModel(Model *model)
SharedResponseManager(bool log_updates, bool enumerate_all_solutions, const CpModelProto *proto, const WallTimer *wall_timer, SharedTimeLimit *shared_time_limit)
double PrimalIntegral() const
void UpdatePrimalIntegral()
IntegerValue GetInnerObjectiveUpperBound()
IntegerValue SynchronizedInnerObjectiveUpperBound()
IntegerValue SynchronizedInnerObjectiveLowerBound()
void UpdateInnerObjectiveBounds(const std::string &worker_info, IntegerValue lb, IntegerValue ub)
void NewSolution(const CpSolverResponse &response, Model *model)
void NotifyThatImprovingProblemIsInfeasible(const std::string &worker_info)
IntegerValue BestSolutionInnerObjectiveValue()
void AddUnsatCore(const std::vector< int > &core)
void SetGapLimitsFromParameters(const SatParameters ¶meters)
int AddSolutionCallback(std::function< void(const CpSolverResponse &)> callback)
void SetUpdatePrimalIntegralOnEachChange(bool set)
void LoadDebugSolution(Model *)
IntegerValue GetInnerObjectiveLowerBound()
void UnregisterCallback(int callback_id)
void Add(const Solution &solution)
void AddInternal(const Solution &solution) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_)
CpModelProto const * model_proto
SharedResponseManager * response
SharedTimeLimit * time_limit
static const int64 kint64max
static const int64 kint64min
absl::Status GetTextProto(const absl::string_view &filename, google::protobuf::Message *proto, int flags)
absl::Status SetTextProto(const absl::string_view &filename, const google::protobuf::Message &proto, int flags)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
int64 ComputeInnerObjective(const CpObjectiveProto &objective, const CpSolverResponse &response)
double ScaleObjectiveValue(const CpObjectiveProto &proto, int64 value)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
IntegerVariable objective_var
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\"'.")