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