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"
28#include "ortools/base/macros.h"
34
35ABSL_FLAG(int, cp_impact_divider, 10, "Divider for continuous update.");
36
37namespace operations_research {
38
39namespace {
40// Default constants for search phase parameters.
41const int kDefaultNumberOfSplits = 100;
42const int kDefaultHeuristicPeriod = 100;
43const int kDefaultHeuristicNumFailuresLimit = 30;
44const 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
60namespace {
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.
65class 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
90class 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.
153class 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
247class 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.
360class 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
654const int ImpactRecorder::kLogCacheSize = 1000;
655const double ImpactRecorder::kPerfectImpact = 1.0;
656const double ImpactRecorder::kFailureImpact = 1.0;
657const double ImpactRecorder::kInitFailureImpact = 2.0;
659
660// This structure stores 'var[index] (left?==:!=) value'.
661class 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
689class 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.
833class 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
1115DecisionBuilder* 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:491
#define CHECK_NE(val1, val2)
Definition: base/logging.h:699
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:891
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:889
#define LOG(severity)
Definition: base/logging.h:416
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.
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)
T * RevAlloc(T *object)
Registers the given object as being reversible.
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.