OR-Tools  9.0
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
const std::vector< IntVar * > vars_
Definition: alldiff_cst.cc:44
int64_t max
Definition: alldiff_cst.cc:140
#define CHECK(condition)
Definition: base/logging.h:498
#define CHECK_NE(val1, val2)
Definition: base/logging.h:706
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:898
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:896
#define LOG(severity)
Definition: base/logging.h:423
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
const int INFO
Definition: log_severity.h:31
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: macros.h:29
void STLDeleteElements(T *container)
Definition: stl_util.h:372
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:200
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
int index
Definition: pack.cc:509
This struct holds all parameters for the default search.