OR-Tools  8.0
synchronization.cc
Go to the documentation of this file.
1 // Copyright 2010-2018 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
15 
16 #if !defined(__PORTABLE_PLATFORM__)
17 #include "ortools/base/file.h"
19 #endif // __PORTABLE_PLATFORM__
20 
21 #include "absl/container/flat_hash_set.h"
22 #include "absl/random/random.h"
24 #include "ortools/base/stl_util.h"
27 #include "ortools/sat/integer.h"
28 #include "ortools/sat/model.h"
29 #include "ortools/sat/sat_base.h"
31 
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.");
41 
42 namespace operations_research {
43 namespace sat {
44 
46  const CpSolverResponse& response) {
47  // Note that the Add() method already applies mutex lock. So we don't need it
48  // here.
49 
50  if (response.solution().empty()) return;
51 
52  // Add this solution to the pool.
54  solution.variable_values.assign(response.solution().begin(),
55  response.solution().end());
56  // For now we use the negated lower bound as the "internal objective" to
57  // prefer solution with an higher bound.
58  //
59  // Note: If the model doesn't have objective, the best_objective_bound is set
60  // to default value 0.
61  solution.rank = -response.best_objective_bound();
62 
63  Add(solution);
64 }
65 
67  const std::vector<double>& lp_solution) {
68  // Note that the Add() method already applies mutex lock. So we don't need it
69  // here.
70 
71  if (lp_solution.empty()) return;
72 
73  // Add this solution to the pool.
75  solution.variable_values.assign(lp_solution.begin(), lp_solution.end());
76 
77  // We always prefer to keep the solution from the last synchronize batch.
78  absl::MutexLock mutex_lock(&mutex_);
79  solution.rank = -num_synchronization_;
80 
81  AddInternal(solution);
82 }
83 
85  absl::MutexLock mutex_lock(&mutex_);
86  return !solutions_.empty();
87 }
88 
90  absl::MutexLock mutex_lock(&mutex_);
91  std::vector<double> solution;
92  if (solutions_.empty()) return solution;
93 
94  solution = std::move(solutions_.back());
95  solutions_.pop_back();
96  return solution;
97 }
98 
100  const std::vector<double>& lp_solution) {
101  absl::MutexLock mutex_lock(&mutex_);
102  solutions_.push_back(lp_solution);
103 }
104 
105 // TODO(user): Experiments and play with the num_solutions_to_keep parameter.
107  bool enumerate_all_solutions,
108  const CpModelProto* proto,
109  const WallTimer* wall_timer,
110  SharedTimeLimit* shared_time_limit)
111  : log_updates_(log_updates),
112  enumerate_all_solutions_(enumerate_all_solutions),
113  model_proto_(*proto),
114  wall_timer_(*wall_timer),
115  shared_time_limit_(shared_time_limit),
116  solutions_(/*num_solutions_to_keep=*/3) {}
117 
118 namespace {
119 
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);
128 }
129 
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);
135 }
136 
137 } // namespace
138 
140  absl::MutexLock mutex_lock(&mutex_);
141  if (!model_proto_.has_objective()) return;
142 
143  const double current_time = shared_time_limit_->GetElapsedDeterministicTime();
144  const double time_delta = current_time - last_primal_integral_time_stamp_;
145  last_primal_integral_time_stamp_ = current_time;
146 
147  // We use the log of the absolute objective gap.
148  //
149  // Using the log should count no solution as just log(2*64) = 18, and
150  // otherwise just compare order of magnitude which seems nice. Also, It is
151  // more easy to compare the primal integral with the total 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;
159 }
160 
162  const SatParameters& parameters) {
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();
167 }
168 
169 void SharedResponseManager::TestGapLimitsIfNeeded() {
170  if (absolute_gap_limit_ == 0 && relative_gap_limit_ == 0) return;
171  if (best_solution_objective_value_ >= kMaxIntegerValue) return;
172  if (inner_objective_lower_bound_ <= kMinIntegerValue) return;
173 
174  const CpObjectiveProto& obj = model_proto_.objective();
175  const double user_best =
176  ScaleObjectiveValue(obj, best_solution_objective_value_);
177  const double user_bound =
178  ScaleObjectiveValue(obj, inner_objective_lower_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.";
183  best_response_.set_status(CpSolverStatus::OPTIMAL);
184 
185  // Note(user): Some code path in single-thread assumes that the problem
186  // can only be solved when they have proven infeasibility and do not check
187  // the ProblemIsSolved() method. So we force a stop here.
188  shared_time_limit_->Stop();
189  }
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.";
193  best_response_.set_status(CpSolverStatus::OPTIMAL);
194 
195  // Same as above.
196  shared_time_limit_->Stop();
197  }
198 }
199 
201  const std::string& worker_info, IntegerValue lb, IntegerValue ub) {
202  absl::MutexLock mutex_lock(&mutex_);
203  CHECK(model_proto_.has_objective());
204 
205  // The problem is already solved!
206  //
207  // TODO(user): A thread might not be notified right away that the new bounds
208  // that it is pushing make the problem infeasible. Fix that. For now we just
209  // abort early here to avoid logging the "#Done" message multiple times.
210  if (inner_objective_lower_bound_ > inner_objective_upper_bound_) {
211  return;
212  }
213 
214  const bool change =
215  (lb > inner_objective_lower_bound_ || ub < inner_objective_upper_bound_);
216  if (lb > inner_objective_lower_bound_) {
217  // When the improving problem is infeasible, it is possible to report
218  // arbitrary high inner_objective_lower_bound_. We make sure it never cross
219  // the current best solution, so that we always report globablly valid lower
220  // 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());
224  }
225  if (ub < inner_objective_upper_bound_) {
226  inner_objective_upper_bound_ = ub.value();
227  }
228  if (inner_objective_lower_bound_ > inner_objective_upper_bound_) {
229  if (best_response_.status() == CpSolverStatus::FEASIBLE ||
230  best_response_.status() == CpSolverStatus::OPTIMAL) {
231  best_response_.set_status(CpSolverStatus::OPTIMAL);
232  } else {
233  best_response_.set_status(CpSolverStatus::INFEASIBLE);
234  }
235  if (log_updates_) LogNewSatSolution("Done", wall_timer_.Get(), worker_info);
236  return;
237  }
238  if (log_updates_ && change) {
239  const CpObjectiveProto& obj = model_proto_.objective();
240  const double best =
241  ScaleObjectiveValue(obj, best_solution_objective_value_);
242  double new_lb = ScaleObjectiveValue(obj, inner_objective_lower_bound_);
243  double new_ub = ScaleObjectiveValue(obj, inner_objective_upper_bound_);
244  if (model_proto_.objective().scaling_factor() < 0) {
245  std::swap(new_lb, new_ub);
246  }
247  LogNewSolution("Bound", wall_timer_.Get(), best, new_lb, new_ub,
248  worker_info);
249  }
250  if (change) TestGapLimitsIfNeeded();
251 }
252 
253 // Invariant: the status always start at UNKNOWN and can only evolve as follow:
254 // UNKNOWN -> FEASIBLE -> OPTIMAL
255 // UNKNOWN -> INFEASIBLE
257  const std::string& worker_info) {
258  absl::MutexLock mutex_lock(&mutex_);
259  if (best_response_.status() == CpSolverStatus::FEASIBLE ||
260  best_response_.status() == CpSolverStatus::OPTIMAL) {
261  // We also use this status to indicate that we enumerated all solutions to
262  // a feasible problem.
263  best_response_.set_status(CpSolverStatus::OPTIMAL);
264  if (!model_proto_.has_objective()) {
265  best_response_.set_all_solutions_were_found(true);
266  }
267 
268  // We just proved that the best solution cannot be improved uppon, so we
269  // have a new lower bound.
270  inner_objective_lower_bound_ = best_solution_objective_value_;
271  } else {
272  CHECK_EQ(num_solutions_, 0);
273  best_response_.set_status(CpSolverStatus::INFEASIBLE);
274  }
275  if (log_updates_) LogNewSatSolution("Done", wall_timer_.Get(), worker_info);
276 }
277 
278 void SharedResponseManager::AddUnsatCore(const std::vector<int>& core) {
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);
283  }
284 }
285 
287  absl::MutexLock mutex_lock(&mutex_);
288  return IntegerValue(inner_objective_lower_bound_);
289 }
290 
292  absl::MutexLock mutex_lock(&mutex_);
293  return IntegerValue(inner_objective_upper_bound_);
294 }
295 
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_);
302 }
303 
305  absl::MutexLock mutex_lock(&mutex_);
306  return synchronized_inner_objective_lower_bound_;
307 }
308 
310  absl::MutexLock mutex_lock(&mutex_);
311  return synchronized_inner_objective_upper_bound_;
312 }
313 
315  absl::MutexLock mutex_lock(&mutex_);
316  return IntegerValue(best_solution_objective_value_);
317 }
318 
320  absl::MutexLock mutex_lock(&mutex_);
321  return primal_integral_;
322 }
323 
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));
329  return id;
330 }
331 
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);
337  return;
338  }
339  }
340  LOG(DFATAL) << "Callback id " << callback_id << " not registered.";
341 }
342 
344  absl::MutexLock mutex_lock(&mutex_);
345  FillObjectiveValuesInBestResponse();
346  return best_response_;
347 }
348 
349 void SharedResponseManager::FillObjectiveValuesInBestResponse() {
350  if (!model_proto_.has_objective()) return;
351  const CpObjectiveProto& obj = model_proto_.objective();
352 
353  if (best_response_.status() == CpSolverStatus::INFEASIBLE) {
354  best_response_.clear_objective_value();
355  best_response_.clear_best_objective_bound();
356  return;
357  }
358 
359  // Set the objective value.
360  // If we don't have any solution, we use our inner bound.
361  if (best_response_.status() == CpSolverStatus::UNKNOWN) {
362  best_response_.set_objective_value(
363  ScaleObjectiveValue(obj, inner_objective_upper_bound_));
364  } else {
365  best_response_.set_objective_value(
366  ScaleObjectiveValue(obj, best_solution_objective_value_));
367  }
368 
369  // Update the best bound in the response.
370  best_response_.set_best_objective_bound(
371  ScaleObjectiveValue(obj, inner_objective_lower_bound_));
372 
373  // Update the primal integral.
374  best_response_.set_primal_integral(primal_integral_);
375 }
376 
377 void SharedResponseManager::NewSolution(const CpSolverResponse& response,
378  Model* model) {
379  absl::MutexLock mutex_lock(&mutex_);
380 
381  if (model_proto_.has_objective()) {
382  const int64 objective_value =
383  ComputeInnerObjective(model_proto_.objective(), response);
384 
385  // Add this solution to the pool, even if it is not improving.
386  if (!response.solution().empty()) {
388  solution.variable_values.assign(response.solution().begin(),
389  response.solution().end());
390  solution.rank = objective_value;
391  solutions_.Add(solution);
392  }
393 
394  // Ignore any non-strictly improving solution.
395  if (objective_value > inner_objective_upper_bound_) return;
396 
397  // Our inner_objective_lower_bound_ should be a globaly valid bound, until
398  // the problem become infeasible (i.e the lb > ub) in which case the bound
399  // is no longer globally valid. Here, because we have a strictly improving
400  // solution, we shouldn't be in the infeasible setting yet.
401  DCHECK_GE(objective_value, inner_objective_lower_bound_);
402 
403  DCHECK_LT(objective_value, best_solution_objective_value_);
404  best_solution_objective_value_ = objective_value;
405 
406  // Update the new bound.
407  inner_objective_upper_bound_ = objective_value - 1;
408  }
409 
410  // Note that the objective will be filled by
411  // FillObjectiveValuesInBestResponse().
412  if (!model_proto_.has_objective() && !enumerate_all_solutions_) {
413  best_response_.set_status(CpSolverStatus::OPTIMAL);
414  } else {
415  best_response_.set_status(CpSolverStatus::FEASIBLE);
416  }
417 
418  best_response_.set_solution_info(response.solution_info());
419  *best_response_.mutable_solution() = response.solution();
420  *best_response_.mutable_solution_lower_bounds() =
421  response.solution_lower_bounds();
422  *best_response_.mutable_solution_upper_bounds() =
423  response.solution_upper_bounds();
424 
425  // Mark model as OPTIMAL if the inner bound crossed.
426  if (model_proto_.has_objective() &&
427  inner_objective_lower_bound_ > inner_objective_upper_bound_) {
428  best_response_.set_status(CpSolverStatus::OPTIMAL);
429  }
430 
431  // Logging.
432  ++num_solutions_;
433  if (log_updates_) {
434  std::string solution_info = response.solution_info();
435  if (model != nullptr) {
436  absl::StrAppend(&solution_info,
437  " num_bool:", model->Get<Trail>()->NumVariables());
438  }
439 
440  if (model_proto_.has_objective()) {
441  const CpObjectiveProto& obj = model_proto_.objective();
442  const double best =
443  ScaleObjectiveValue(obj, best_solution_objective_value_);
444  double lb = ScaleObjectiveValue(obj, inner_objective_lower_bound_);
445  double ub = ScaleObjectiveValue(obj, inner_objective_upper_bound_);
446  if (model_proto_.objective().scaling_factor() < 0) {
447  std::swap(lb, ub);
448  }
449  LogNewSolution(absl::StrCat(num_solutions_), wall_timer_.Get(), best, lb,
450  ub, solution_info);
451  } else {
452  LogNewSatSolution(absl::StrCat(num_solutions_), wall_timer_.Get(),
453  solution_info);
454  }
455  }
456 
457  // Call callbacks.
458  // Note that we cannot call function that try to get the mutex_ here.
459  TestGapLimitsIfNeeded();
460  if (!callbacks_.empty()) {
461  FillObjectiveValuesInBestResponse();
462  SetStatsFromModelInternal(model);
463  for (const auto& pair : callbacks_) {
464  pair.second(best_response_);
465  }
466  }
467 
468 #if !defined(__PORTABLE_PLATFORM__)
469  // We protect solution dumping with log_updates as LNS subsolvers share
470  // another solution manager, and we do not want to dump those.
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 << "'.";
475  CHECK_OK(file::SetTextProto(file, best_response_, file::Defaults()));
476  }
477 #endif // __PORTABLE_PLATFORM__
478 }
479 
481 #if !defined(__PORTABLE_PLATFORM__)
482  if (FLAGS_cp_model_load_debug_solution.empty()) return;
483  if (model->Get<DebugSolution>() != nullptr) return; // Already loaded.
484 
485  CpSolverResponse response;
486  LOG(INFO) << "Reading solution from '" << FLAGS_cp_model_load_debug_solution
487  << "'.";
488  CHECK_OK(file::GetTextProto(FLAGS_cp_model_load_debug_solution, &response,
489  file::Defaults()));
490 
491  const auto& mapping = *model->GetOrCreate<CpModelMapping>();
492  auto& debug_solution = *model->GetOrCreate<DebugSolution>();
493  debug_solution.resize(
494  model->GetOrCreate<IntegerTrail>()->NumIntegerVariables().value());
495  for (int i = 0; i < response.solution().size(); ++i) {
496  if (!mapping.IsInteger(i)) continue;
497  const IntegerVariable var = mapping.Integer(i);
498  debug_solution[var] = response.solution(i);
499  debug_solution[NegationOf(var)] = -response.solution(i);
500  }
501 
502  // The objective variable is usually not part of the proto, but it is still
503  // nice to have it, so we recompute it here.
504  auto* objective_def = model->Get<ObjectiveDefinition>();
505  if (objective_def == nullptr) return;
506 
507  const IntegerVariable objective_var = objective_def->objective_var;
508  const int64 objective_value =
509  ComputeInnerObjective(model_proto_.objective(), response);
510  debug_solution[objective_var] = objective_value;
511  debug_solution[NegationOf(objective_var)] = -objective_value;
512 #endif // __PORTABLE_PLATFORM__
513 }
514 
516  absl::MutexLock mutex_lock(&mutex_);
517  SetStatsFromModelInternal(model);
518 }
519 
520 void SharedResponseManager::SetStatsFromModelInternal(Model* model) {
521  if (model == nullptr) return;
522  auto* sat_solver = model->Get<SatSolver>();
523  auto* integer_trail = model->Get<IntegerTrail>();
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());
530  auto* time_limit = model->Get<TimeLimit>();
531  best_response_.set_wall_time(time_limit->GetElapsedTime());
532  best_response_.set_deterministic_time(
533  time_limit->GetElapsedDeterministicTime());
534 }
535 
537  absl::MutexLock mutex_lock(&mutex_);
538  return best_response_.status() == CpSolverStatus::OPTIMAL ||
539  best_response_.status() == CpSolverStatus::INFEASIBLE;
540 }
541 
543  : num_variables_(model_proto.variables_size()),
544  model_proto_(model_proto),
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];
556  }
557 }
558 
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());
566 
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;
577  CHECK_GE(var, 0);
578  if (!changed_lb && !changed_ub) continue;
579 
580  if (changed_lb) {
581  lower_bounds_[var] = new_lb;
582  }
583  if (changed_ub) {
584  upper_bounds_[var] = new_ub;
585  }
586  changed_variables_since_last_synchronize_.Set(var);
587 
588  if (VLOG_IS_ON(3)) {
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, ")")
592  : var_proto.name();
593  LOG(INFO) << " '" << worker_name << "' exports new bounds for "
594  << var_name << ": from [" << old_lb << ", " << old_ub
595  << "] to [" << new_lb << ", " << new_ub << "]";
596  }
597  }
598 }
599 
601  absl::MutexLock mutex_lock(&mutex_);
602  for (const int var :
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);
608  }
609  }
610  changed_variables_since_last_synchronize_.ClearAll();
611 }
612 
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);
625  }
626  }
627  return id;
628 }
629 
631  int id, std::vector<int>* variables, std::vector<int64>* new_lower_bounds,
632  std::vector<int64>* new_upper_bounds) {
633  variables->clear();
634  new_lower_bounds->clear();
635  new_upper_bounds->clear();
636 
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]);
642  }
643  id_to_changed_variables_[id].ClearAll();
644 }
645 
646 } // namespace sat
647 } // namespace operations_research
synchronization.h
var
IntVar * var
Definition: expr_array.cc:1858
response
SharedResponseManager * response
Definition: cp_model_solver.cc:2065
operations_research::sat::FEASIBLE
@ FEASIBLE
Definition: cp_model.pb.h:230
operations_research::sat::SharedBoundsManager::RegisterNewId
int RegisterNewId()
Definition: synchronization.cc:613
min
int64 min
Definition: alldiff_cst.cc:138
integral_types.h
operations_research::sat::ObjectiveDefinition
Definition: cp_model_loader.h:38
CHECK_OK
#define CHECK_OK(x)
Definition: base/logging.h:29
operations_research::sat::SharedBoundsManager::ReportPotentialNewBounds
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)
Definition: synchronization.cc:559
operations_research::sat::IntegerTrail
Definition: integer.h:523
cp_model.pb.h
operations_research::SharedTimeLimit::Stop
void Stop()
Definition: time_limit.h:363
max
int64 max
Definition: alldiff_cst.cc:139
operations_research::sat::SharedLPSolutionRepository::NewLPSolution
void NewLPSolution(const std::vector< double > &lp_solution)
Definition: synchronization.cc:66
operations_research::sat::SharedResponseManager::SharedResponseManager
SharedResponseManager(bool log_updates, bool enumerate_all_solutions, const CpModelProto *proto, const WallTimer *wall_timer, SharedTimeLimit *shared_time_limit)
Definition: synchronization.cc:106
time_limit.h
operations_research::sat::SharedResponseManager::ProblemIsSolved
bool ProblemIsSolved() const
Definition: synchronization.cc:536
WallTimer::Get
double Get() const
Definition: timer.h:45
operations_research::sat::SharedSolutionRepository
Definition: synchronization.h:43
operations_research::sat::SharedBoundsManager::SharedBoundsManager
SharedBoundsManager(const CpModelProto &model_proto)
Definition: synchronization.cc:542
operations_research::sat::SharedIncompleteSolutionManager::AddNewSolution
void AddNewSolution(const std::vector< double > &lp_solution)
Definition: synchronization.cc:99
operations_research::sat::SharedResponseManager::UpdatePrimalIntegral
void UpdatePrimalIntegral()
Definition: synchronization.cc:139
operations_research::sat::SharedResponseManager::GetResponse
CpSolverResponse GetResponse()
Definition: synchronization.cc:343
file::SetTextProto
absl::Status SetTextProto(const absl::string_view &filename, const google::protobuf::Message &proto, int flags)
Definition: file.cc:277
operations_research::sat::SharedResponseManager::LoadDebugSolution
void LoadDebugSolution(Model *)
Definition: synchronization.cc:480
operations_research::sat::SatSolver
Definition: sat_solver.h:58
model_proto
CpModelProto const * model_proto
Definition: cp_model_solver.cc:2061
model.h
file::GetTextProto
absl::Status GetTextProto(const absl::string_view &filename, google::protobuf::Message *proto, int flags)
Definition: file.cc:267
operations_research::sat::SharedResponseManager::PrimalIntegral
double PrimalIntegral() const
Definition: synchronization.cc:319
operations_research
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
Definition: dense_doubly_linked_list.h:21
operations_research::sat::NegationOf
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:42
operations_research::sat::SharedResponseManager::NotifyThatImprovingProblemIsInfeasible
void NotifyThatImprovingProblemIsInfeasible(const std::string &worker_info)
Definition: synchronization.cc:256
kint64min
static const int64 kint64min
Definition: integral_types.h:60
operations_research::sat::UNKNOWN
@ UNKNOWN
Definition: cp_model.pb.h:228
int64
int64_t int64
Definition: integral_types.h:34
operations_research::TimeLimit
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:105
sat_base.h
operations_research::sat::SharedBoundsManager::GetChangedBounds
void GetChangedBounds(int id, std::vector< int > *variables, std::vector< int64 > *new_lower_bounds, std::vector< int64 > *new_upper_bounds)
Definition: synchronization.cc:630
operations_research::sat::IntegerTrail::NumIntegerVariables
IntegerVariable NumIntegerVariables() const
Definition: integer.h:548
operations_research::sat::SharedSolutionRepository< double >::mutex_
absl::Mutex mutex_
Definition: synchronization.h:114
operations_research::sat::SharedIncompleteSolutionManager::GetNewSolution
std::vector< double > GetNewSolution()
Definition: synchronization.cc:89
file.h
operations_research::sat::INFEASIBLE
@ INFEASIBLE
Definition: cp_model.pb.h:231
operations_research::sat::ScaleObjectiveValue
double ScaleObjectiveValue(const CpObjectiveProto &proto, int64 value)
Definition: cp_model_utils.h:128
wall_timer
WallTimer * wall_timer
Definition: cp_model_solver.cc:2062
time_limit
SharedTimeLimit * time_limit
Definition: cp_model_solver.cc:2063
DEFINE_bool
DEFINE_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\"'.")
operations_research::sat::SharedIncompleteSolutionManager::HasNewSolution
bool HasNewSolution() const
Definition: synchronization.cc:84
DEFINE_string
DEFINE_string(cp_model_load_debug_solution, "", "DEBUG ONLY. When this is set to a non-empty file name, " "we will interpret this as an internal solution which can be used for " "debugging. For instance we use it to identify wrong cuts/reasons.")
operations_research::sat::SharedResponseManager::GetInnerObjectiveLowerBound
IntegerValue GetInnerObjectiveLowerBound()
Definition: synchronization.cc:286
operations_research::sat::kMaxIntegerValue
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
operations_research::sat::SharedBoundsManager::Synchronize
void Synchronize()
Definition: synchronization.cc:600
operations_research::sat::SharedSolutionRepository< double >::AddInternal
void AddInternal(const Solution &solution) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_)
Definition: synchronization.h:444
operations_research::sat::SharedSolutionRepository< int64 >::Add
void Add(const Solution &solution)
Definition: synchronization.h:438
operations_research::sat::SharedResponseManager::NewSolution
void NewSolution(const CpSolverResponse &response, Model *model)
Definition: synchronization.cc:377
operations_research::sat::Model
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
operations_research::sat::OPTIMAL
@ OPTIMAL
Definition: cp_model.pb.h:232
WallTimer
Definition: timer.h:23
operations_research::sat::ComputeInnerObjective
int64 ComputeInnerObjective(const CpObjectiveProto &objective, const CpSolverResponse &response)
Definition: cp_model_utils.cc:520
operations_research::SharedTimeLimit
Definition: time_limit.h:338
operations_research::sat::SharedResponseManager::SynchronizedInnerObjectiveUpperBound
IntegerValue SynchronizedInnerObjectiveUpperBound()
Definition: synchronization.cc:309
operations_research::sat::SharedResponseManager::Synchronize
void Synchronize()
Definition: synchronization.cc:296
operations_research::sat::SharedResponseManager::AddSolutionCallback
int AddSolutionCallback(std::function< void(const CpSolverResponse &)> callback)
Definition: synchronization.cc:324
callback
MPCallback * callback
Definition: gurobi_interface.cc:440
model
GRBmodel * model
Definition: gurobi_interface.cc:195
operations_research::sat::ObjectiveDefinition::objective_var
IntegerVariable objective_var
Definition: cp_model_loader.h:41
operations_research::sat::SharedResponseManager::AddUnsatCore
void AddUnsatCore(const std::vector< int > &core)
Definition: synchronization.cc:278
operations_research::sat::SharedResponseManager::GetInnerObjectiveUpperBound
IntegerValue GetInnerObjectiveUpperBound()
Definition: synchronization.cc:291
operations_research::sat::SharedResponseManager::BestSolutionInnerObjectiveValue
IntegerValue BestSolutionInnerObjectiveValue()
Definition: synchronization.cc:314
operations_research::sat::SharedResponseManager::UnregisterCallback
void UnregisterCallback(int callback_id)
Definition: synchronization.cc:332
operations_research::sat::kMinIntegerValue
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
stl_util.h
file::Defaults
int Defaults()
Definition: base/file.h:119
operations_research::sat::SharedResponseManager::SetGapLimitsFromParameters
void SetGapLimitsFromParameters(const SatParameters &parameters)
Definition: synchronization.cc:161
file
Definition: file.cc:141
operations_research::sat::DebugSolution
Definition: integer.h:238
operations_research::sat::SharedResponseManager::SynchronizedInnerObjectiveLowerBound
IntegerValue SynchronizedInnerObjectiveLowerBound()
Definition: synchronization.cc:304
operations_research::SharedTimeLimit::GetElapsedDeterministicTime
double GetElapsedDeterministicTime() const
Definition: time_limit.h:383
cp_model_loader.h
operations_research::sat::SharedResponseManager::SetStatsFromModel
void SetStatsFromModel(Model *model)
Definition: synchronization.cc:515
proto
CpModelProto proto
Definition: cp_model_fz_solver.cc:106
operations_research::sat::CpModelMapping
Definition: cp_model_loader.h:63
operations_research::sat::Trail::NumVariables
int NumVariables() const
Definition: sat_base.h:376
parameters
SatParameters parameters
Definition: cp_model_fz_solver.cc:107
operations_research::sat::SharedResponseManager::UpdateInnerObjectiveBounds
void UpdateInnerObjectiveBounds(const std::string &worker_info, IntegerValue lb, IntegerValue ub)
Definition: synchronization.cc:200
integer.h
operations_research::sat::Trail
Definition: sat_base.h:233
kint64max
static const int64 kint64max
Definition: integral_types.h:62
operations_research::sat::SharedRelaxationSolutionRepository::NewRelaxationSolution
void NewRelaxationSolution(const CpSolverResponse &response)
Definition: synchronization.cc:45
cp_model_utils.h