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