OR-Tools  8.0
default_search.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 
14 #include <cstddef>
15 #include <functional>
16 #include <limits>
17 #include <memory>
18 #include <string>
19 #include <utility>
20 #include <vector>
21 
22 #include "absl/container/flat_hash_set.h"
23 #include "absl/strings/str_format.h"
26 #include "ortools/base/logging.h"
27 #include "ortools/base/macros.h"
28 #include "ortools/base/stl_util.h"
33 
34 DEFINE_int32(cp_impact_divider, 10, "Divider for continuous update.");
35 
36 namespace operations_research {
37 
38 namespace {
39 // Default constants for search phase parameters.
40 const int kDefaultNumberOfSplits = 100;
41 const int kDefaultHeuristicPeriod = 100;
42 const int kDefaultHeuristicNumFailuresLimit = 30;
43 const bool kDefaultUseLastConflict = true;
44 } // namespace
45 
46 DefaultPhaseParameters::DefaultPhaseParameters()
47  : var_selection_schema(DefaultPhaseParameters::CHOOSE_MAX_SUM_IMPACT),
48  value_selection_schema(DefaultPhaseParameters::SELECT_MIN_IMPACT),
49  initialization_splits(kDefaultNumberOfSplits),
50  run_all_heuristics(true),
51  heuristic_period(kDefaultHeuristicPeriod),
52  heuristic_num_failures_limit(kDefaultHeuristicNumFailuresLimit),
53  persistent_impact(true),
54  random_seed(CpRandomSeed()),
55  display_level(DefaultPhaseParameters::NORMAL),
56  use_last_conflict(kDefaultUseLastConflict),
57  decision_builder(nullptr) {}
58 
59 namespace {
60 // ----- DomainWatcher -----
61 
62 // This class follows the domains of variables and will report the log of the
63 // search space of all integer variables.
64 class DomainWatcher {
65  public:
66  DomainWatcher(const std::vector<IntVar*>& vars, int cache_size)
67  : vars_(vars) {
68  cached_log_.Init(cache_size);
69  }
70 
71  double LogSearchSpaceSize() {
72  double result = 0.0;
73  for (int index = 0; index < vars_.size(); ++index) {
74  result += cached_log_.Log2(vars_[index]->Size());
75  }
76  return result;
77  }
78 
79  double Log2(int64 size) const { return cached_log_.Log2(size); }
80 
81  private:
82  std::vector<IntVar*> vars_;
83  CachedLog cached_log_;
84  DISALLOW_COPY_AND_ASSIGN(DomainWatcher);
85 };
86 
87 // ---------- FindVar decision visitor ---------
88 
89 class FindVar : public DecisionVisitor {
90  public:
91  enum Operation { NONE, ASSIGN, SPLIT_LOW, SPLIT_HIGH };
92 
93  FindVar() : var_(nullptr), value_(0), operation_(NONE) {}
94 
95  ~FindVar() override {}
96 
97  void VisitSetVariableValue(IntVar* const var, int64 value) override {
98  var_ = var;
99  value_ = value;
100  operation_ = ASSIGN;
101  }
102 
103  void VisitSplitVariableDomain(IntVar* const var, int64 value,
104  bool start_with_lower_half) override {
105  var_ = var;
106  value_ = value;
107  operation_ = start_with_lower_half ? SPLIT_LOW : SPLIT_HIGH;
108  }
109 
110  void VisitScheduleOrPostpone(IntervalVar* const var, int64 est) override {
111  operation_ = NONE;
112  }
113 
114  virtual void VisitTryRankFirst(SequenceVar* const sequence, int index) {
115  operation_ = NONE;
116  }
117 
118  virtual void VisitTryRankLast(SequenceVar* const sequence, int index) {
119  operation_ = NONE;
120  }
121 
122  void VisitUnknownDecision() override { operation_ = NONE; }
123 
124  // Returns the current variable.
125  IntVar* const var() const {
126  CHECK_NE(operation_, NONE);
127  return var_;
128  }
129 
130  // Returns the value of the current variable.
131  int64 value() const {
132  CHECK_NE(operation_, NONE);
133  return value_;
134  }
135 
136  Operation operation() const { return operation_; }
137 
138  std::string DebugString() const override {
139  return "FindVar decision visitor";
140  }
141 
142  private:
143  IntVar* var_;
144  int64 value_;
145  Operation operation_;
146 };
147 
148 // ----- Auxiliary decision builders to init impacts -----
149 
150 // This class initialize impacts by scanning each value of the domain
151 // of the variable.
152 class InitVarImpacts : public DecisionBuilder {
153  public:
154  // ----- main -----
155  InitVarImpacts()
156  : var_(nullptr),
157  update_impact_callback_(nullptr),
158  new_start_(false),
159  var_index_(0),
160  value_index_(-1),
161  update_impact_closure_([this]() { UpdateImpacts(); }),
162  updater_(update_impact_closure_) {
163  CHECK(update_impact_closure_ != nullptr);
164  }
165 
166  ~InitVarImpacts() override {}
167 
168  void UpdateImpacts() {
169  // the Min is always the value we just set.
170  update_impact_callback_(var_index_, var_->Min());
171  }
172 
173  void Init(IntVar* const var, IntVarIterator* const iterator, int var_index) {
174  var_ = var;
175  iterator_ = iterator;
176  var_index_ = var_index;
177  new_start_ = true;
178  value_index_ = 0;
179  }
180 
181  Decision* Next(Solver* const solver) override {
182  CHECK(var_ != nullptr);
183  CHECK(iterator_ != nullptr);
184  if (new_start_) {
185  active_values_.clear();
186  for (const int64 value : InitAndGetValues(iterator_)) {
187  active_values_.push_back(value);
188  }
189  new_start_ = false;
190  }
191  if (value_index_ == active_values_.size()) {
192  return nullptr;
193  }
194  updater_.var_ = var_;
195  updater_.value_ = active_values_[value_index_];
196  value_index_++;
197  return &updater_;
198  }
199 
200  void set_update_impact_callback(std::function<void(int, int64)> callback) {
201  update_impact_callback_ = std::move(callback);
202  }
203 
204  private:
205  // ----- helper decision -----
206  class AssignCallFail : public Decision {
207  public:
208  explicit AssignCallFail(const std::function<void()>& update_impact_closure)
209  : var_(nullptr),
210  value_(0),
211  update_impact_closure_(update_impact_closure) {
212  CHECK(update_impact_closure_ != nullptr);
213  }
214  ~AssignCallFail() override {}
215  void Apply(Solver* const solver) override {
216  CHECK(var_ != nullptr);
217  var_->SetValue(value_);
218  // We call the closure on the part that cannot fail.
219  update_impact_closure_();
220  solver->Fail();
221  }
222  void Refute(Solver* const solver) override {}
223  // Public data for easy access.
224  IntVar* var_;
225  int64 value_;
226 
227  private:
228  const std::function<void()>& update_impact_closure_;
229  DISALLOW_COPY_AND_ASSIGN(AssignCallFail);
230  };
231 
232  IntVar* var_;
233  std::function<void(int, int64)> update_impact_callback_;
234  bool new_start_;
235  IntVarIterator* iterator_;
236  int var_index_;
237  std::vector<int64> active_values_;
238  int value_index_;
239  std::function<void()> update_impact_closure_;
240  AssignCallFail updater_;
241 };
242 
243 // This class initialize impacts by scanning at most 'split_size'
244 // intervals on the domain of the variable.
245 
246 class InitVarImpactsWithSplits : public DecisionBuilder {
247  public:
248  // ----- helper decision -----
249  class AssignIntervalCallFail : public Decision {
250  public:
251  explicit AssignIntervalCallFail(
252  const std::function<void()>& update_impact_closure)
253  : var_(nullptr),
254  value_min_(0),
255  value_max_(0),
256  update_impact_closure_(update_impact_closure) {
257  CHECK(update_impact_closure_ != nullptr);
258  }
259  ~AssignIntervalCallFail() override {}
260  void Apply(Solver* const solver) override {
261  CHECK(var_ != nullptr);
262  var_->SetRange(value_min_, value_max_);
263  // We call the closure on the part that cannot fail.
264  update_impact_closure_();
265  solver->Fail();
266  }
267  void Refute(Solver* const solver) override {}
268 
269  // Public for easy access.
270  IntVar* var_;
273 
274  private:
275  const std::function<void()>& update_impact_closure_;
276  DISALLOW_COPY_AND_ASSIGN(AssignIntervalCallFail);
277  };
278 
279  // ----- main -----
280 
281  explicit InitVarImpactsWithSplits(int split_size)
282  : var_(nullptr),
283  update_impact_callback_(nullptr),
284  new_start_(false),
285  var_index_(0),
286  min_value_(0),
287  max_value_(0),
288  split_size_(split_size),
289  split_index_(-1),
290  update_impact_closure_([this]() { UpdateImpacts(); }),
291  updater_(update_impact_closure_) {
292  CHECK(update_impact_closure_ != nullptr);
293  }
294 
295  ~InitVarImpactsWithSplits() override {}
296 
297  void UpdateImpacts() {
298  for (const int64 value : InitAndGetValues(iterator_)) {
299  update_impact_callback_(var_index_, value);
300  }
301  }
302 
303  void Init(IntVar* const var, IntVarIterator* const iterator, int var_index) {
304  var_ = var;
305  iterator_ = iterator;
306  var_index_ = var_index;
307  new_start_ = true;
308  split_index_ = 0;
309  }
310 
311  int64 IntervalStart(int index) const {
312  const int64 length = max_value_ - min_value_ + 1;
313  return (min_value_ + length * index / split_size_);
314  }
315 
316  Decision* Next(Solver* const solver) override {
317  if (new_start_) {
318  min_value_ = var_->Min();
319  max_value_ = var_->Max();
320  new_start_ = false;
321  }
322  if (split_index_ == split_size_) {
323  return nullptr;
324  }
325  updater_.var_ = var_;
326  updater_.value_min_ = IntervalStart(split_index_);
327  split_index_++;
328  if (split_index_ == split_size_) {
329  updater_.value_max_ = max_value_;
330  } else {
331  updater_.value_max_ = IntervalStart(split_index_) - 1;
332  }
333  return &updater_;
334  }
335 
336  void set_update_impact_callback(std::function<void(int, int64)> callback) {
337  update_impact_callback_ = std::move(callback);
338  }
339 
340  private:
341  IntVar* var_;
342  std::function<void(int, int64)> update_impact_callback_;
343  bool new_start_;
344  IntVarIterator* iterator_;
345  int var_index_;
346  int64 min_value_;
347  int64 max_value_;
348  const int split_size_;
349  int split_index_;
350  std::function<void()> update_impact_closure_;
351  AssignIntervalCallFail updater_;
352 };
353 
354 // ----- ImpactRecorder
355 
356 // This class will record the impacts of all assignment of values to
357 // variables. Its main output is to find the optimal pair (variable/value)
358 // based on default phase parameters.
359 class ImpactRecorder : public SearchMonitor {
360  public:
361  static const int kLogCacheSize;
362  static const double kPerfectImpact;
363  static const double kFailureImpact;
364  static const double kInitFailureImpact;
365  static const int kUninitializedVarIndex;
366 
367  ImpactRecorder(Solver* const solver, DomainWatcher* const domain_watcher,
368  const std::vector<IntVar*>& vars,
369  DefaultPhaseParameters::DisplayLevel display_level)
370  : SearchMonitor(solver),
371  domain_watcher_(domain_watcher),
372  vars_(vars),
373  size_(vars.size()),
374  current_log_space_(0.0),
375  impacts_(size_),
376  original_min_(size_, 0LL),
377  domain_iterators_(new IntVarIterator*[size_]),
378  display_level_(display_level),
379  current_var_(kUninitializedVarIndex),
380  current_value_(0),
381  init_done_(false) {
382  for (int i = 0; i < size_; ++i) {
383  domain_iterators_[i] = vars_[i]->MakeDomainIterator(true);
384  var_map_[vars_[i]] = i;
385  }
386  }
387 
388  void ApplyDecision(Decision* const d) override {
389  if (!init_done_) {
390  return;
391  }
392  d->Accept(&find_var_);
393  if (find_var_.operation() == FindVar::ASSIGN &&
394  gtl::ContainsKey(var_map_, find_var_.var())) {
395  current_var_ = var_map_[find_var_.var()];
396  current_value_ = find_var_.value();
397  current_log_space_ = domain_watcher_->LogSearchSpaceSize();
398  } else {
399  current_var_ = kUninitializedVarIndex;
400  current_value_ = 0;
401  }
402  }
403 
404  void AfterDecision(Decision* const d, bool apply) override {
405  if (init_done_ && current_var_ != kUninitializedVarIndex) {
406  if (current_log_space_ > 0.0) {
407  const double log_space = domain_watcher_->LogSearchSpaceSize();
408  if (apply) {
409  const double impact = kPerfectImpact - log_space / current_log_space_;
410  UpdateImpact(current_var_, current_value_, impact);
411  current_var_ = kUninitializedVarIndex;
412  current_value_ = 0;
413  }
414  current_log_space_ = log_space;
415  }
416  }
417  }
418 
419  void BeginFail() override {
420  if (init_done_ && current_var_ != kUninitializedVarIndex) {
421  UpdateImpact(current_var_, current_value_, kFailureImpact);
422  current_var_ = kUninitializedVarIndex;
423  current_value_ = 0;
424  }
425  }
426 
427  void ResetAllImpacts() {
428  for (int i = 0; i < size_; ++i) {
429  original_min_[i] = vars_[i]->Min();
430  // By default, we init impacts to 2.0 -> equivalent to failure.
431  // This will be overwritten to real impact values on valid domain
432  // values during the FirstRun() method.
433  impacts_[i].resize(vars_[i]->Max() - vars_[i]->Min() + 1,
435  }
436 
437  for (int i = 0; i < size_; ++i) {
438  for (int j = 0; j < impacts_[i].size(); ++j) {
439  impacts_[i][j] = kInitFailureImpact;
440  }
441  }
442  }
443 
444  void UpdateImpact(int var_index, int64 value, double impact) {
445  const int64 value_index = value - original_min_[var_index];
446  const double current_impact = impacts_[var_index][value_index];
447  const double new_impact =
448  (current_impact * (FLAGS_cp_impact_divider - 1) + impact) /
449  FLAGS_cp_impact_divider;
450  impacts_[var_index][value_index] = new_impact;
451  }
452 
453  void InitImpact(int var_index, int64 value) {
454  const double log_space = domain_watcher_->LogSearchSpaceSize();
455  const double impact = kPerfectImpact - log_space / current_log_space_;
456  const int64 value_index = value - original_min_[var_index];
457  DCHECK_LT(var_index, size_);
458  DCHECK_LT(value_index, impacts_[var_index].size());
459  impacts_[var_index][value_index] = impact;
460  init_count_++;
461  }
462 
463  void FirstRun(int64 splits) {
464  Solver* const s = solver();
465  current_log_space_ = domain_watcher_->LogSearchSpaceSize();
466  if (display_level_ != DefaultPhaseParameters::NONE) {
467  LOG(INFO) << " - initial log2(SearchSpace) = " << current_log_space_;
468  }
469  const int64 init_time = s->wall_time();
470  ResetAllImpacts();
471  int64 removed_counter = 0;
472  FirstRunVariableContainers* container =
473  s->RevAlloc(new FirstRunVariableContainers(this, splits));
474  // Loop on the variables, scan domains and initialize impacts.
475  for (int var_index = 0; var_index < size_; ++var_index) {
476  IntVar* const var = vars_[var_index];
477  if (var->Bound()) {
478  continue;
479  }
480  IntVarIterator* const iterator = domain_iterators_[var_index];
481  DecisionBuilder* init_decision_builder = nullptr;
482  const bool no_split = var->Size() < splits;
483  if (no_split) {
484  // The domain is small enough, we scan it completely.
485  container->without_split()->set_update_impact_callback(
486  container->update_impact_callback());
487  container->without_split()->Init(var, iterator, var_index);
488  init_decision_builder = container->without_split();
489  } else {
490  // The domain is too big, we scan it in initialization_splits
491  // intervals.
492  container->with_splits()->set_update_impact_callback(
493  container->update_impact_callback());
494  container->with_splits()->Init(var, iterator, var_index);
495  init_decision_builder = container->with_splits();
496  }
497  // Reset the number of impacts initialized.
498  init_count_ = 0;
499  // Use Solve() to scan all values of one variable.
500  s->Solve(init_decision_builder);
501 
502  // If we have not initialized all values, then they can be removed.
503  // As the iterator is not stable w.r.t. deletion, we need to store
504  // removed values in an intermediate vector.
505  if (init_count_ != var->Size() && no_split) {
506  container->ClearRemovedValues();
507  for (const int64 value : InitAndGetValues(iterator)) {
508  const int64 value_index = value - original_min_[var_index];
509  if (impacts_[var_index][value_index] == kInitFailureImpact) {
510  container->PushBackRemovedValue(value);
511  }
512  }
513  CHECK(container->HasRemovedValues()) << var->DebugString();
514  removed_counter += container->NumRemovedValues();
515  const double old_log = domain_watcher_->Log2(var->Size());
516  var->RemoveValues(container->removed_values());
517  current_log_space_ += domain_watcher_->Log2(var->Size()) - old_log;
518  }
519  }
520  if (display_level_ != DefaultPhaseParameters::NONE) {
521  if (removed_counter) {
522  LOG(INFO) << " - init done, time = " << s->wall_time() - init_time
523  << " ms, " << removed_counter
524  << " values removed, log2(SearchSpace) = "
525  << current_log_space_;
526  } else {
527  LOG(INFO) << " - init done, time = " << s->wall_time() - init_time
528  << " ms";
529  }
530  }
531  s->SaveAndSetValue(&init_done_, true);
532  }
533 
534  // This method scans the domain of one variable and returns the sum
535  // of the impacts of all values in its domain, along with the value
536  // with minimal impact.
537  void ScanVarImpacts(int var_index, int64* const best_impact_value,
538  double* const var_impacts,
539  DefaultPhaseParameters::VariableSelection var_select,
540  DefaultPhaseParameters::ValueSelection value_select) {
541  CHECK(best_impact_value != nullptr);
542  CHECK(var_impacts != nullptr);
543  double max_impact = -std::numeric_limits<double>::max();
544  double min_impact = std::numeric_limits<double>::max();
545  double sum_var_impact = 0.0;
546  int64 min_impact_value = -1;
547  int64 max_impact_value = -1;
548  for (const int64 value : InitAndGetValues(domain_iterators_[var_index])) {
549  const int64 value_index = value - original_min_[var_index];
550  DCHECK_LT(var_index, size_);
551  DCHECK_LT(value_index, impacts_[var_index].size());
552  const double current_impact = impacts_[var_index][value_index];
553  sum_var_impact += current_impact;
554  if (current_impact > max_impact) {
555  max_impact = current_impact;
556  max_impact_value = value;
557  }
558  if (current_impact < min_impact) {
559  min_impact = current_impact;
560  min_impact_value = value;
561  }
562  }
563 
564  switch (var_select) {
565  case DefaultPhaseParameters::CHOOSE_MAX_AVERAGE_IMPACT: {
566  *var_impacts = sum_var_impact / vars_[var_index]->Size();
567  break;
568  }
569  case DefaultPhaseParameters::CHOOSE_MAX_VALUE_IMPACT: {
570  *var_impacts = max_impact;
571  break;
572  }
573  default: {
574  *var_impacts = sum_var_impact;
575  break;
576  }
577  }
578 
579  switch (value_select) {
580  case DefaultPhaseParameters::SELECT_MIN_IMPACT: {
581  *best_impact_value = min_impact_value;
582  break;
583  }
584  case DefaultPhaseParameters::SELECT_MAX_IMPACT: {
585  *best_impact_value = max_impact_value;
586  break;
587  }
588  }
589  }
590 
591  std::string DebugString() const override { return "ImpactRecorder"; }
592 
593  private:
594  // A container for the variables needed in FirstRun that is reversibly
595  // allocable.
596  class FirstRunVariableContainers : public BaseObject {
597  public:
598  FirstRunVariableContainers(ImpactRecorder* impact_recorder, int64 splits)
599  : update_impact_callback_(
600  [impact_recorder](int var_index, int64 value) {
601  impact_recorder->InitImpact(var_index, value);
602  }),
603  removed_values_(),
604  without_splits_(),
605  with_splits_(splits) {}
606  std::function<void(int, int64)> update_impact_callback() const {
607  return update_impact_callback_;
608  }
609  void PushBackRemovedValue(int64 value) { removed_values_.push_back(value); }
610  bool HasRemovedValues() const { return !removed_values_.empty(); }
611  void ClearRemovedValues() { removed_values_.clear(); }
612  size_t NumRemovedValues() const { return removed_values_.size(); }
613  const std::vector<int64>& removed_values() const { return removed_values_; }
614  InitVarImpacts* without_split() { return &without_splits_; }
615  InitVarImpactsWithSplits* with_splits() { return &with_splits_; }
616 
617  std::string DebugString() const override {
618  return "FirstRunVariableContainers";
619  }
620 
621  private:
622  const std::function<void(int, int64)> update_impact_callback_;
623  std::vector<int64> removed_values_;
624  InitVarImpacts without_splits_;
625  InitVarImpactsWithSplits with_splits_;
626  };
627 
628  DomainWatcher* const domain_watcher_;
629  std::vector<IntVar*> vars_;
630  const int size_;
631  double current_log_space_;
632  // impacts_[i][j] stores the average search space reduction when assigning
633  // original_min_[i] + j to variable i.
634  std::vector<std::vector<double> > impacts_;
635  std::vector<int64> original_min_;
636  std::unique_ptr<IntVarIterator*[]> domain_iterators_;
637  int64 init_count_;
638  const DefaultPhaseParameters::DisplayLevel display_level_;
639  int current_var_;
640  int64 current_value_;
641  FindVar find_var_;
642  absl::flat_hash_map<const IntVar*, int> var_map_;
643  bool init_done_;
644 
645  DISALLOW_COPY_AND_ASSIGN(ImpactRecorder);
646 };
647 
648 const int ImpactRecorder::kLogCacheSize = 1000;
649 const double ImpactRecorder::kPerfectImpact = 1.0;
650 const double ImpactRecorder::kFailureImpact = 1.0;
651 const double ImpactRecorder::kInitFailureImpact = 2.0;
653 
654 // This structure stores 'var[index] (left?==:!=) value'.
655 class ChoiceInfo {
656  public:
657  ChoiceInfo() : value_(0), var_(nullptr), left_(false) {}
658 
659  ChoiceInfo(IntVar* const var, int64 value, bool left)
660  : value_(value), var_(var), left_(left) {}
661 
662  std::string DebugString() const {
663  return absl::StrFormat("%s %s %d", var_->name(), (left_ ? "==" : "!="),
664  value_);
665  }
666 
667  IntVar* var() const { return var_; }
668 
669  bool left() const { return left_; }
670 
671  int64 value() const { return value_; }
672 
673  void set_left(bool left) { left_ = left; }
674 
675  private:
676  int64 value_;
677  IntVar* var_;
678  bool left_;
679 };
680 
681 // ---------- Heuristics ----------
682 
683 class RunHeuristicsAsDives : public Decision {
684  public:
685  RunHeuristicsAsDives(Solver* const solver, const std::vector<IntVar*>& vars,
686  DefaultPhaseParameters::DisplayLevel level,
687  bool run_all_heuristics, int random_seed,
688  int heuristic_period, int heuristic_num_failures_limit)
689  : heuristic_limit_(nullptr),
690  display_level_(level),
691  run_all_heuristics_(run_all_heuristics),
692  random_(random_seed),
693  heuristic_period_(heuristic_period),
694  heuristic_branch_count_(0),
695  heuristic_runs_(0) {
696  Init(solver, vars, heuristic_num_failures_limit);
697  }
698 
699  ~RunHeuristicsAsDives() override { gtl::STLDeleteElements(&heuristics_); }
700 
701  void Apply(Solver* const solver) override {
702  if (!RunAllHeuristics(solver)) {
703  solver->Fail();
704  }
705  }
706 
707  void Refute(Solver* const solver) override {}
708 
709  bool ShouldRun() {
710  if (heuristic_period_ <= 0) {
711  return false;
712  }
713  ++heuristic_branch_count_;
714  return heuristic_branch_count_ % heuristic_period_ == 0;
715  }
716 
717  bool RunOneHeuristic(Solver* const solver, int index) {
718  HeuristicWrapper* const wrapper = heuristics_[index];
719  heuristic_runs_++;
720 
721  const bool result =
722  solver->SolveAndCommit(wrapper->phase, heuristic_limit_);
723  if (result && display_level_ != DefaultPhaseParameters::NONE) {
724  LOG(INFO) << " --- solution found by heuristic " << wrapper->name
725  << " --- ";
726  }
727  return result;
728  }
729 
730  bool RunAllHeuristics(Solver* const solver) {
731  if (run_all_heuristics_) {
732  for (int index = 0; index < heuristics_.size(); ++index) {
733  for (int run = 0; run < heuristics_[index]->runs; ++run) {
734  if (RunOneHeuristic(solver, index)) {
735  return true;
736  }
737  }
738  }
739  return false;
740  } else {
741  DCHECK_GT(heuristics_.size(), 0);
742  const int index = absl::Uniform<int>(random_, 0, heuristics_.size());
743  return RunOneHeuristic(solver, index);
744  }
745  }
746 
747  int Rand32(int size) {
748  DCHECK_GT(size, 0);
749  return absl::Uniform<int>(random_, 0, size);
750  }
751 
752  void Init(Solver* const solver, const std::vector<IntVar*>& vars,
753  int heuristic_num_failures_limit) {
754  const int kRunOnce = 1;
755  const int kRunMore = 2;
756  const int kRunALot = 3;
757 
758  heuristics_.push_back(new HeuristicWrapper(
759  solver, vars, Solver::CHOOSE_MIN_SIZE_LOWEST_MIN,
760  Solver::ASSIGN_MIN_VALUE, "AssignMinValueToMinDomainSize", kRunOnce));
761 
762  heuristics_.push_back(new HeuristicWrapper(
763  solver, vars, Solver::CHOOSE_MIN_SIZE_HIGHEST_MAX,
764  Solver::ASSIGN_MAX_VALUE, "AssignMaxValueToMinDomainSize", kRunOnce));
765 
766  heuristics_.push_back(
767  new HeuristicWrapper(solver, vars, Solver::CHOOSE_MIN_SIZE_LOWEST_MIN,
768  Solver::ASSIGN_CENTER_VALUE,
769  "AssignCenterValueToMinDomainSize", kRunOnce));
770 
771  heuristics_.push_back(new HeuristicWrapper(
772  solver, vars, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_RANDOM_VALUE,
773  "AssignRandomValueToFirstUnbound", kRunALot));
774 
775  heuristics_.push_back(new HeuristicWrapper(
776  solver, vars, Solver::CHOOSE_RANDOM, Solver::ASSIGN_MIN_VALUE,
777  "AssignMinValueToRandomVariable", kRunMore));
778 
779  heuristics_.push_back(new HeuristicWrapper(
780  solver, vars, Solver::CHOOSE_RANDOM, Solver::ASSIGN_MAX_VALUE,
781  "AssignMaxValueToRandomVariable", kRunMore));
782 
783  heuristics_.push_back(new HeuristicWrapper(
784  solver, vars, Solver::CHOOSE_RANDOM, Solver::ASSIGN_RANDOM_VALUE,
785  "AssignRandomValueToRandomVariable", kRunMore));
786 
787  heuristic_limit_ = solver->MakeFailuresLimit(heuristic_num_failures_limit);
788  }
789 
790  int heuristic_runs() const { return heuristic_runs_; }
791 
792  private:
793  // This class wraps one heuristic with extra information: name and
794  // number of runs.
795  struct HeuristicWrapper {
796  HeuristicWrapper(Solver* const solver, const std::vector<IntVar*>& vars,
797  Solver::IntVarStrategy var_strategy,
798  Solver::IntValueStrategy value_strategy,
799  const std::string& heuristic_name, int heuristic_runs)
800  : phase(solver->MakePhase(vars, var_strategy, value_strategy)),
801  name(heuristic_name),
802  runs(heuristic_runs) {}
803 
804  // The decision builder we are going to use in this dive.
805  DecisionBuilder* const phase;
806  // A name for logging purposes.
807  const std::string name;
808  // How many times we will run this particular heuristic in case the
809  // parameter run_all_heuristics is true. This is useful for random
810  // heuristics where it makes sense to run them more than once.
811  const int runs;
812  };
813 
814  std::vector<HeuristicWrapper*> heuristics_;
815  SearchMonitor* heuristic_limit_;
816  DefaultPhaseParameters::DisplayLevel display_level_;
817  bool run_all_heuristics_;
818  std::mt19937 random_;
819  const int heuristic_period_;
820  int heuristic_branch_count_;
821  int heuristic_runs_;
822 };
823 
824 // ---------- DefaultIntegerSearch ----------
825 
826 // Default phase decision builder.
827 class DefaultIntegerSearch : public DecisionBuilder {
828  public:
829  static const double kSmallSearchSpaceLimit;
830 
831  DefaultIntegerSearch(Solver* const solver, const std::vector<IntVar*>& vars,
832  const DefaultPhaseParameters& parameters)
833  : vars_(vars),
834  parameters_(parameters),
835  domain_watcher_(vars, ImpactRecorder::kLogCacheSize),
836  impact_recorder_(solver, &domain_watcher_, vars,
837  parameters.display_level),
838  heuristics_(solver, vars_, parameters_.display_level,
839  parameters_.run_all_heuristics, parameters_.random_seed,
840  parameters_.heuristic_period,
841  parameters_.heuristic_num_failures_limit),
842  find_var_(),
843  last_int_var_(nullptr),
844  last_int_value_(0),
845  last_operation_(FindVar::NONE),
846  last_conflict_count_(0),
847  init_done_(false) {}
848 
849  ~DefaultIntegerSearch() override {}
850 
851  Decision* Next(Solver* const solver) override {
852  CheckInit(solver);
853 
854  if (heuristics_.ShouldRun()) {
855  return &heuristics_;
856  }
857 
858  Decision* const decision = parameters_.decision_builder != nullptr
859  ? parameters_.decision_builder->Next(solver)
860  : ImpactNext(solver);
861 
862  // Returns early if the search tree is finished anyway.
863  if (decision == nullptr) {
864  ClearLastDecision();
865  return nullptr;
866  }
867 
868  // The main goal of last conflict is to branch on a decision
869  // variable different from the one being evaluated. We need to
870  // retrieve first the variable in the current decision.
871  decision->Accept(&find_var_);
872  IntVar* const decision_var =
873  find_var_.operation() != FindVar::NONE ? find_var_.var() : nullptr;
874 
875  // We will hijack the search heuristics if
876  // - we use last conflict
877  // - we have stored the last decision from the search heuristics
878  // - the variable stored is different from the variable of the current
879  // decision
880  // - this variable is not bound already
881  // Furthermore, each case will also verify that the stored decision is
882  // compatible with the current domain variable.
883  if (parameters_.use_last_conflict && last_int_var_ != nullptr &&
884  !last_int_var_->Bound() &&
885  (decision_var == nullptr || decision_var != last_int_var_)) {
886  switch (last_operation_) {
887  case FindVar::ASSIGN: {
888  if (last_int_var_->Contains(last_int_value_)) {
889  Decision* const assign =
890  solver->MakeAssignVariableValue(last_int_var_, last_int_value_);
891  ClearLastDecision();
892  last_conflict_count_++;
893  return assign;
894  }
895  break;
896  }
897  case FindVar::SPLIT_LOW: {
898  if (last_int_var_->Max() > last_int_value_ &&
899  last_int_var_->Min() <= last_int_value_) {
900  Decision* const split = solver->MakeVariableLessOrEqualValue(
901  last_int_var_, last_int_value_);
902  ClearLastDecision();
903  last_conflict_count_++;
904  return split;
905  }
906  break;
907  }
908  case FindVar::SPLIT_HIGH: {
909  if (last_int_var_->Min() < last_int_value_ &&
910  last_int_var_->Max() >= last_int_value_) {
911  Decision* const split = solver->MakeVariableGreaterOrEqualValue(
912  last_int_var_, last_int_value_);
913  ClearLastDecision();
914  last_conflict_count_++;
915  return split;
916  }
917  break;
918  }
919  default: {
920  break;
921  }
922  }
923  }
924 
925  if (parameters_.use_last_conflict) {
926  // Store the last decision to replay it upon failure.
927  decision->Accept(&find_var_);
928  if (find_var_.operation() != FindVar::NONE) {
929  last_int_var_ = find_var_.var();
930  last_int_value_ = find_var_.value();
931  last_operation_ = find_var_.operation();
932  }
933  }
934 
935  return decision;
936  }
937 
938  void ClearLastDecision() {
939  last_int_var_ = nullptr;
940  last_int_value_ = 0;
941  last_operation_ = FindVar::NONE;
942  }
943 
944  void AppendMonitors(Solver* const solver,
945  std::vector<SearchMonitor*>* const extras) override {
946  CHECK(solver != nullptr);
947  CHECK(extras != nullptr);
948  if (parameters_.decision_builder == nullptr) {
949  extras->push_back(&impact_recorder_);
950  }
951  }
952 
953  void Accept(ModelVisitor* const visitor) const override {
954  visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension);
955  visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
956  vars_);
957  visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension);
958  }
959 
960  std::string DebugString() const override {
961  std::string out = "DefaultIntegerSearch(";
962 
963  if (parameters_.decision_builder == nullptr) {
964  out.append("Impact Based Search, ");
965  } else {
966  out.append(parameters_.decision_builder->DebugString());
967  out.append(", ");
968  }
969  out.append(JoinDebugStringPtr(vars_, ", "));
970  out.append(")");
971  return out;
972  }
973 
974  std::string StatString() const {
975  const int runs = heuristics_.heuristic_runs();
976  std::string result;
977  if (runs > 0) {
978  if (!result.empty()) {
979  result.append(", ");
980  }
981  if (runs == 1) {
982  result.append("1 heuristic run");
983  } else {
984  absl::StrAppendFormat(&result, "%d heuristic runs", runs);
985  }
986  }
987  if (last_conflict_count_ > 0) {
988  if (!result.empty()) {
989  result.append(", ");
990  }
991  if (last_conflict_count_ == 1) {
992  result.append("1 last conflict hint");
993  } else {
994  absl::StrAppendFormat(&result, "%d last conflict hints",
995  last_conflict_count_);
996  }
997  }
998  return result;
999  }
1000 
1001  private:
1002  void CheckInit(Solver* const solver) {
1003  if (init_done_) {
1004  return;
1005  }
1006  if (parameters_.decision_builder == nullptr) {
1007  // Decide if we are doing impacts, no if one variable is too big.
1008  for (int i = 0; i < vars_.size(); ++i) {
1009  if (vars_[i]->Max() - vars_[i]->Min() > 0xFFFFFF) {
1010  if (parameters_.display_level == DefaultPhaseParameters::VERBOSE) {
1011  LOG(INFO) << "Domains are too large, switching to simple "
1012  << "heuristics";
1013  }
1014  solver->SaveValue(
1015  reinterpret_cast<void**>(&parameters_.decision_builder));
1016  parameters_.decision_builder =
1017  solver->MakePhase(vars_, Solver::CHOOSE_MIN_SIZE_LOWEST_MIN,
1018  Solver::ASSIGN_MIN_VALUE);
1019  solver->SaveAndSetValue(&init_done_, true);
1020  return;
1021  }
1022  }
1023  // No if the search space is too small.
1024  if (domain_watcher_.LogSearchSpaceSize() < kSmallSearchSpaceLimit) {
1025  if (parameters_.display_level == DefaultPhaseParameters::VERBOSE) {
1026  LOG(INFO) << "Search space is too small, switching to simple "
1027  << "heuristics";
1028  }
1029  solver->SaveValue(
1030  reinterpret_cast<void**>(&parameters_.decision_builder));
1031  parameters_.decision_builder = solver->MakePhase(
1032  vars_, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE);
1033  solver->SaveAndSetValue(&init_done_, true);
1034  return;
1035  }
1036 
1037  if (parameters_.display_level != DefaultPhaseParameters::NONE) {
1038  LOG(INFO) << "Init impact based search phase on " << vars_.size()
1039  << " variables, initialization splits = "
1040  << parameters_.initialization_splits
1041  << ", heuristic_period = " << parameters_.heuristic_period
1042  << ", run_all_heuristics = "
1043  << parameters_.run_all_heuristics;
1044  }
1045  // Init the impacts.
1046  impact_recorder_.FirstRun(parameters_.initialization_splits);
1047  }
1048  if (parameters_.persistent_impact) {
1049  init_done_ = true;
1050  } else {
1051  solver->SaveAndSetValue(&init_done_, true);
1052  }
1053  }
1054 
1055  // This method will do an exhaustive scan of all domains of all
1056  // variables to select the variable with the maximal sum of impacts
1057  // per value in its domain, and then select the value with the
1058  // minimal impact.
1059  Decision* ImpactNext(Solver* const solver) {
1060  IntVar* var = nullptr;
1061  int64 value = 0;
1062  double best_var_impact = -std::numeric_limits<double>::max();
1063  for (int i = 0; i < vars_.size(); ++i) {
1064  if (!vars_[i]->Bound()) {
1065  int64 current_value = 0;
1066  double current_var_impact = 0.0;
1067  impact_recorder_.ScanVarImpacts(i, &current_value, &current_var_impact,
1068  parameters_.var_selection_schema,
1069  parameters_.value_selection_schema);
1070  if (current_var_impact > best_var_impact) {
1071  var = vars_[i];
1072  value = current_value;
1073  best_var_impact = current_var_impact;
1074  }
1075  }
1076  }
1077  if (var == nullptr) {
1078  return nullptr;
1079  } else {
1080  return solver->MakeAssignVariableValue(var, value);
1081  }
1082  }
1083 
1084  // ----- data members -----
1085 
1086  std::vector<IntVar*> vars_;
1087  DefaultPhaseParameters parameters_;
1088  DomainWatcher domain_watcher_;
1089  ImpactRecorder impact_recorder_;
1090  RunHeuristicsAsDives heuristics_;
1091  FindVar find_var_;
1092  IntVar* last_int_var_;
1093  int64 last_int_value_;
1094  FindVar::Operation last_operation_;
1095  int last_conflict_count_;
1096  bool init_done_;
1097 };
1098 
1100 } // namespace
1101 
1102 // ---------- API ----------
1103 
1104 std::string DefaultPhaseStatString(DecisionBuilder* db) {
1105  DefaultIntegerSearch* const dis = dynamic_cast<DefaultIntegerSearch*>(db);
1106  return dis != nullptr ? dis->StatString() : "";
1107 }
1108 
1109 DecisionBuilder* Solver::MakeDefaultPhase(const std::vector<IntVar*>& vars) {
1110  DefaultPhaseParameters parameters;
1111  return MakeDefaultPhase(vars, parameters);
1112 }
1113 
1114 DecisionBuilder* Solver::MakeDefaultPhase(
1115  const std::vector<IntVar*>& vars,
1116  const DefaultPhaseParameters& parameters) {
1117  return RevAlloc(new DefaultIntegerSearch(this, vars, parameters));
1118 }
1119 } // namespace operations_research
var
IntVar * var
Definition: expr_array.cc:1858
kUninitializedVarIndex
static const int kUninitializedVarIndex
Definition: default_search.cc:365
integral_types.h
max
int64 max
Definition: alldiff_cst.cc:139
value_min_
int64 value_min_
Definition: default_search.cc:271
logging.h
kPerfectImpact
static const double kPerfectImpact
Definition: default_search.cc:362
value
int64 value
Definition: demon_profiler.cc:43
runs
const int runs
Definition: default_search.cc:811
macros.h
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
int64
int64_t int64
Definition: integral_types.h:34
constraint_solveri.h
index
int index
Definition: pack.cc:508
kLogCacheSize
static const int kLogCacheSize
Definition: default_search.cc:361
kFailureImpact
static const double kFailureImpact
Definition: default_search.cc:363
operations_research::DefaultPhaseStatString
std::string DefaultPhaseStatString(DecisionBuilder *db)
Definition: default_search.cc:1104
constraint_solver.h
string_array.h
DEFINE_int32
DEFINE_int32(cp_impact_divider, 10, "Divider for continuous update.")
cached_log.h
iterator_
IntVarIterator *const iterator_
Definition: expressions.cc:2180
callback
MPCallback * callback
Definition: gurobi_interface.cc:440
stl_util.h
vars_
const std::vector< IntVar * > vars_
Definition: alldiff_cst.cc:43
phase
DecisionBuilder *const phase
Definition: default_search.cc:805
DISALLOW_COPY_AND_ASSIGN
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: macros.h:29
gtl::STLDeleteElements
void STLDeleteElements(T *container)
Definition: stl_util.h:372
kSmallSearchSpaceLimit
static const double kSmallSearchSpaceLimit
Definition: default_search.cc:829
commandlineflags.h
parameters
SatParameters parameters
Definition: cp_model_fz_solver.cc:107
name
const std::string name
Definition: default_search.cc:807
operations_research::JoinDebugStringPtr
std::string JoinDebugStringPtr(const std::vector< T > &v, const std::string &separator)
Definition: string_array.h:45
value_max_
int64 value_max_
Definition: default_search.cc:272
kInitFailureImpact
static const double kInitFailureImpact
Definition: default_search.cc:364
gtl::ContainsKey
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:170