OR-Tools  9.1
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 <algorithm>
15#include <cstdint>
16#include <functional>
17#include <limits>
18#include <list>
19#include <memory>
20#include <queue>
21#include <string>
22#include <utility>
23#include <vector>
24
25#include "absl/base/casts.h"
26#include "absl/container/flat_hash_map.h"
27#include "absl/memory/memory.h"
28#include "absl/strings/str_cat.h"
29#include "absl/strings/str_format.h"
30#include "absl/strings/str_join.h"
31#include "absl/time/time.h"
32#include "ortools/base/bitmap.h"
34#include "ortools/base/hash.h"
37#include "ortools/base/macros.h"
41#include "ortools/base/timer.h"
46
47ABSL_FLAG(bool, cp_use_sparse_gls_penalties, false,
48 "Use sparse implementation to store Guided Local Search penalties");
49ABSL_FLAG(bool, cp_log_to_vlog, false,
50 "Whether search related logging should be "
51 "vlog or info.");
52ABSL_FLAG(int64_t, cp_large_domain_no_splitting_limit, 0xFFFFF,
53 "Size limit to allow holes in variables from the strategy.");
54namespace operations_research {
55
56// ---------- Search Log ---------
57
58SearchLog::SearchLog(Solver* const s, OptimizeVar* const obj, IntVar* const var,
59 double scaling_factor, double offset,
60 std::function<std::string()> display_callback,
61 bool display_on_new_solutions_only, int period)
62 : SearchMonitor(s),
63 period_(period),
64 timer_(new WallTimer),
65 var_(var),
66 obj_(obj),
67 scaling_factor_(scaling_factor),
68 offset_(offset),
69 display_callback_(std::move(display_callback)),
70 display_on_new_solutions_only_(display_on_new_solutions_only),
71 nsol_(0),
72 tick_(0),
73 objective_min_(std::numeric_limits<int64_t>::max()),
74 objective_max_(std::numeric_limits<int64_t>::min()),
75 min_right_depth_(std::numeric_limits<int32_t>::max()),
76 max_depth_(0),
77 sliding_min_depth_(0),
78 sliding_max_depth_(0) {
79 CHECK(obj == nullptr || var == nullptr)
80 << "Either var or obj need to be nullptr.";
81}
82
84
85std::string SearchLog::DebugString() const { return "SearchLog"; }
86
88 const std::string buffer =
89 absl::StrFormat("Start search (%s)", MemoryUsage());
90 OutputLine(buffer);
91 timer_->Restart();
92 min_right_depth_ = std::numeric_limits<int32_t>::max();
93}
94
96 const int64_t branches = solver()->branches();
97 int64_t ms = timer_->GetInMs();
98 if (ms == 0) {
99 ms = 1;
100 }
101 const std::string buffer = absl::StrFormat(
102 "End search (time = %d ms, branches = %d, failures = %d, %s, speed = %d "
103 "branches/s)",
104 ms, branches, solver()->failures(), MemoryUsage(), branches * 1000 / ms);
105 OutputLine(buffer);
106}
107
109 Maintain();
110 const int depth = solver()->SearchDepth();
111 std::string obj_str = "";
112 int64_t current = 0;
113 bool objective_updated = false;
114 const auto scaled_str = [this](int64_t value) {
115 if (scaling_factor_ != 1.0 || offset_ != 0.0) {
116 return absl::StrFormat("%d (%.8lf)", value,
117 scaling_factor_ * (value + offset_));
118 } else {
119 return absl::StrCat(value);
120 }
121 };
122 if (obj_ != nullptr && obj_->Var()->Bound()) {
123 current = obj_->Var()->Value();
124 obj_str = obj_->Print();
125 objective_updated = true;
126 } else if (var_ != nullptr && var_->Bound()) {
127 current = var_->Value();
128 absl::StrAppend(&obj_str, scaled_str(current), ", ");
129 objective_updated = true;
130 } else {
132 absl::StrAppend(&obj_str, scaled_str(current), ", ");
133 objective_updated = true;
134 }
135 if (objective_updated) {
136 if (current > objective_min_) {
137 absl::StrAppend(&obj_str,
138 "objective minimum = ", scaled_str(objective_min_), ", ");
139 } else {
140 objective_min_ = current;
141 }
142 if (current < objective_max_) {
143 absl::StrAppend(&obj_str,
144 "objective maximum = ", scaled_str(objective_max_), ", ");
145 } else {
146 objective_max_ = current;
147 }
148 }
149 std::string log;
150 absl::StrAppendFormat(&log,
151 "Solution #%d (%stime = %d ms, branches = %d,"
152 " failures = %d, depth = %d",
153 nsol_++, obj_str, timer_->GetInMs(),
154 solver()->branches(), solver()->failures(), depth);
155 if (!solver()->SearchContext().empty()) {
156 absl::StrAppendFormat(&log, ", %s", solver()->SearchContext());
157 }
158 if (solver()->neighbors() != 0) {
159 absl::StrAppendFormat(&log,
160 ", neighbors = %d, filtered neighbors = %d,"
161 " accepted neighbors = %d",
162 solver()->neighbors(), solver()->filtered_neighbors(),
163 solver()->accepted_neighbors());
164 }
165 absl::StrAppendFormat(&log, ", %s", MemoryUsage());
166 const int progress = solver()->TopProgressPercent();
167 if (progress != SearchMonitor::kNoProgress) {
168 absl::StrAppendFormat(&log, ", limit = %d%%", progress);
169 }
170 if (display_callback_) {
171 absl::StrAppendFormat(&log, ", %s", display_callback_());
172 }
173 log.append(")");
174 OutputLine(log);
175 return false;
176}
177
179
181
183 std::string buffer = absl::StrFormat(
184 "Finished search tree (time = %d ms, branches = %d,"
185 " failures = %d",
186 timer_->GetInMs(), solver()->branches(), solver()->failures());
187 if (solver()->neighbors() != 0) {
188 absl::StrAppendFormat(&buffer,
189 ", neighbors = %d, filtered neighbors = %d,"
190 " accepted neigbors = %d",
191 solver()->neighbors(), solver()->filtered_neighbors(),
192 solver()->accepted_neighbors());
193 }
194 absl::StrAppendFormat(&buffer, ", %s", MemoryUsage());
195 if (!display_on_new_solutions_only_ && display_callback_) {
196 absl::StrAppendFormat(&buffer, ", %s", display_callback_());
197 }
198 buffer.append(")");
199 OutputLine(buffer);
200}
201
202void SearchLog::ApplyDecision(Decision* const decision) {
203 Maintain();
204 const int64_t b = solver()->branches();
205 if (b % period_ == 0 && b > 0) {
207 }
208}
209
210void SearchLog::RefuteDecision(Decision* const decision) {
211 min_right_depth_ = std::min(min_right_depth_, solver()->SearchDepth());
212 ApplyDecision(decision);
213}
214
216 std::string buffer =
217 absl::StrFormat("%d branches, %d ms, %d failures", solver()->branches(),
218 timer_->GetInMs(), solver()->failures());
219 if (min_right_depth_ != std::numeric_limits<int32_t>::max() &&
220 max_depth_ != 0) {
221 const int depth = solver()->SearchDepth();
222 absl::StrAppendFormat(&buffer, ", tree pos=%d/%d/%d minref=%d max=%d",
223 sliding_min_depth_, depth, sliding_max_depth_,
224 min_right_depth_, max_depth_);
225 sliding_min_depth_ = depth;
226 sliding_max_depth_ = depth;
227 }
228 if (obj_ != nullptr &&
229 objective_min_ != std::numeric_limits<int64_t>::max() &&
230 objective_max_ != std::numeric_limits<int64_t>::min()) {
231 absl::StrAppendFormat(&buffer,
232 ", objective minimum = %d"
233 ", objective maximum = %d",
234 objective_min_, objective_max_);
235 }
236 const int progress = solver()->TopProgressPercent();
237 if (progress != SearchMonitor::kNoProgress) {
238 absl::StrAppendFormat(&buffer, ", limit = %d%%", progress);
239 }
240 OutputLine(buffer);
241}
242
244 const int current_depth = solver()->SearchDepth();
245 sliding_min_depth_ = std::min(current_depth, sliding_min_depth_);
246 sliding_max_depth_ = std::max(current_depth, sliding_max_depth_);
247 max_depth_ = std::max(current_depth, max_depth_);
248}
249
250void SearchLog::BeginInitialPropagation() { tick_ = timer_->GetInMs(); }
251
253 const int64_t delta = std::max<int64_t>(timer_->GetInMs() - tick_, 0);
254 const std::string buffer = absl::StrFormat(
255 "Root node processed (time = %d ms, constraints = %d, %s)", delta,
256 solver()->constraints(), MemoryUsage());
257 OutputLine(buffer);
258}
259
260void SearchLog::OutputLine(const std::string& line) {
261 if (absl::GetFlag(FLAGS_cp_log_to_vlog)) {
262 VLOG(1) << line;
263 } else {
264 LOG(INFO) << line;
265 }
266}
267
268std::string SearchLog::MemoryUsage() {
269 static const int64_t kDisplayThreshold = 2;
270 static const int64_t kKiloByte = 1024;
271 static const int64_t kMegaByte = kKiloByte * kKiloByte;
272 static const int64_t kGigaByte = kMegaByte * kKiloByte;
273 const int64_t memory_usage = Solver::MemoryUsage();
274 if (memory_usage > kDisplayThreshold * kGigaByte) {
275 return absl::StrFormat("memory used = %.2lf GB",
276 memory_usage * 1.0 / kGigaByte);
277 } else if (memory_usage > kDisplayThreshold * kMegaByte) {
278 return absl::StrFormat("memory used = %.2lf MB",
279 memory_usage * 1.0 / kMegaByte);
280 } else if (memory_usage > kDisplayThreshold * kKiloByte) {
281 return absl::StrFormat("memory used = %2lf KB",
282 memory_usage * 1.0 / kKiloByte);
283 } else {
284 return absl::StrFormat("memory used = %d", memory_usage);
285 }
286}
287
289 return MakeSearchLog(branch_period, static_cast<IntVar*>(nullptr));
290}
291
292SearchMonitor* Solver::MakeSearchLog(int branch_period, IntVar* const var) {
293 return MakeSearchLog(branch_period, var, nullptr);
294}
295
297 int branch_period, std::function<std::string()> display_callback) {
298 return MakeSearchLog(branch_period, static_cast<IntVar*>(nullptr),
299 std::move(display_callback));
300}
301
303 int branch_period, IntVar* const var,
304 std::function<std::string()> display_callback) {
305 return RevAlloc(new SearchLog(this, nullptr, var, 1.0, 0.0,
306 std::move(display_callback), true,
307 branch_period));
308}
309
311 OptimizeVar* const opt_var) {
312 return MakeSearchLog(branch_period, opt_var, nullptr);
313}
314
316 int branch_period, OptimizeVar* const opt_var,
317 std::function<std::string()> display_callback) {
318 return RevAlloc(new SearchLog(this, opt_var, nullptr, 1.0, 0.0,
319 std::move(display_callback), true,
320 branch_period));
321}
322
324 return RevAlloc(new SearchLog(this, parameters.objective, parameters.variable,
325 parameters.scaling_factor, parameters.offset,
326 std::move(parameters.display_callback),
327 parameters.display_on_new_solutions_only,
328 parameters.branch_period));
329}
330
331// ---------- Search Trace ----------
332namespace {
333class SearchTrace : public SearchMonitor {
334 public:
335 SearchTrace(Solver* const s, const std::string& prefix)
336 : SearchMonitor(s), prefix_(prefix) {}
337 ~SearchTrace() override {}
338
339 void EnterSearch() override {
340 LOG(INFO) << prefix_ << " EnterSearch(" << solver()->SolveDepth() << ")";
341 }
342 void RestartSearch() override {
343 LOG(INFO) << prefix_ << " RestartSearch(" << solver()->SolveDepth() << ")";
344 }
345 void ExitSearch() override {
346 LOG(INFO) << prefix_ << " ExitSearch(" << solver()->SolveDepth() << ")";
347 }
348 void BeginNextDecision(DecisionBuilder* const b) override {
349 LOG(INFO) << prefix_ << " BeginNextDecision(" << b << ") ";
350 }
351 void EndNextDecision(DecisionBuilder* const b, Decision* const d) override {
352 if (d) {
353 LOG(INFO) << prefix_ << " EndNextDecision(" << b << ", " << d << ") ";
354 } else {
355 LOG(INFO) << prefix_ << " EndNextDecision(" << b << ") ";
356 }
357 }
358 void ApplyDecision(Decision* const d) override {
359 LOG(INFO) << prefix_ << " ApplyDecision(" << d << ") ";
360 }
361 void RefuteDecision(Decision* const d) override {
362 LOG(INFO) << prefix_ << " RefuteDecision(" << d << ") ";
363 }
364 void AfterDecision(Decision* const d, bool apply) override {
365 LOG(INFO) << prefix_ << " AfterDecision(" << d << ", " << apply << ") ";
366 }
367 void BeginFail() override {
368 LOG(INFO) << prefix_ << " BeginFail(" << solver()->SearchDepth() << ")";
369 }
370 void EndFail() override {
371 LOG(INFO) << prefix_ << " EndFail(" << solver()->SearchDepth() << ")";
372 }
373 void BeginInitialPropagation() override {
374 LOG(INFO) << prefix_ << " BeginInitialPropagation()";
375 }
376 void EndInitialPropagation() override {
377 LOG(INFO) << prefix_ << " EndInitialPropagation()";
378 }
379 bool AtSolution() override {
380 LOG(INFO) << prefix_ << " AtSolution()";
381 return false;
382 }
383 bool AcceptSolution() override {
384 LOG(INFO) << prefix_ << " AcceptSolution()";
385 return true;
386 }
387 void NoMoreSolutions() override {
388 LOG(INFO) << prefix_ << " NoMoreSolutions()";
389 }
390
391 std::string DebugString() const override { return "SearchTrace"; }
392
393 private:
394 const std::string prefix_;
395};
396} // namespace
397
398SearchMonitor* Solver::MakeSearchTrace(const std::string& prefix) {
399 return RevAlloc(new SearchTrace(this, prefix));
400}
401
402// ---------- Callback-based search monitors ----------
403namespace {
404class AtSolutionCallback : public SearchMonitor {
405 public:
406 AtSolutionCallback(Solver* const solver, std::function<void()> callback)
407 : SearchMonitor(solver), callback_(std::move(callback)) {}
408 ~AtSolutionCallback() override {}
409 bool AtSolution() override;
410
411 private:
412 const std::function<void()> callback_;
413};
414
415bool AtSolutionCallback::AtSolution() {
416 callback_();
417 return false;
418}
419
420} // namespace
421
423 return RevAlloc(new AtSolutionCallback(this, std::move(callback)));
424}
425
426namespace {
427class EnterSearchCallback : public SearchMonitor {
428 public:
429 EnterSearchCallback(Solver* const solver, std::function<void()> callback)
430 : SearchMonitor(solver), callback_(std::move(callback)) {}
431 ~EnterSearchCallback() override {}
432 void EnterSearch() override;
433
434 private:
435 const std::function<void()> callback_;
436};
437
438void EnterSearchCallback::EnterSearch() { callback_(); }
439
440} // namespace
441
443 return RevAlloc(new EnterSearchCallback(this, std::move(callback)));
444}
445
446namespace {
447class ExitSearchCallback : public SearchMonitor {
448 public:
449 ExitSearchCallback(Solver* const solver, std::function<void()> callback)
450 : SearchMonitor(solver), callback_(std::move(callback)) {}
451 ~ExitSearchCallback() override {}
452 void ExitSearch() override;
453
454 private:
455 const std::function<void()> callback_;
456};
457
458void ExitSearchCallback::ExitSearch() { callback_(); }
459
460} // namespace
461
463 return RevAlloc(new ExitSearchCallback(this, std::move(callback)));
464}
465
466// ---------- Composite Decision Builder --------
467
468namespace {
469class CompositeDecisionBuilder : public DecisionBuilder {
470 public:
471 CompositeDecisionBuilder();
472 explicit CompositeDecisionBuilder(const std::vector<DecisionBuilder*>& dbs);
473 ~CompositeDecisionBuilder() override;
474 void Add(DecisionBuilder* const db);
475 void AppendMonitors(Solver* const solver,
476 std::vector<SearchMonitor*>* const monitors) override;
477 void Accept(ModelVisitor* const visitor) const override;
478
479 protected:
480 std::vector<DecisionBuilder*> builders_;
481};
482
483CompositeDecisionBuilder::CompositeDecisionBuilder() {}
484
485CompositeDecisionBuilder::CompositeDecisionBuilder(
486 const std::vector<DecisionBuilder*>& dbs) {
487 for (int i = 0; i < dbs.size(); ++i) {
488 Add(dbs[i]);
489 }
490}
491
492CompositeDecisionBuilder::~CompositeDecisionBuilder() {}
493
494void CompositeDecisionBuilder::Add(DecisionBuilder* const db) {
495 if (db != nullptr) {
496 builders_.push_back(db);
497 }
498}
499
500void CompositeDecisionBuilder::AppendMonitors(
501 Solver* const solver, std::vector<SearchMonitor*>* const monitors) {
502 for (DecisionBuilder* const db : builders_) {
503 db->AppendMonitors(solver, monitors);
504 }
505}
506
507void CompositeDecisionBuilder::Accept(ModelVisitor* const visitor) const {
508 for (DecisionBuilder* const db : builders_) {
509 db->Accept(visitor);
510 }
511}
512} // namespace
513
514// ---------- Compose Decision Builder ----------
515
516namespace {
517class ComposeDecisionBuilder : public CompositeDecisionBuilder {
518 public:
519 ComposeDecisionBuilder();
520 explicit ComposeDecisionBuilder(const std::vector<DecisionBuilder*>& dbs);
521 ~ComposeDecisionBuilder() override;
522 Decision* Next(Solver* const s) override;
523 std::string DebugString() const override;
524
525 private:
526 int start_index_;
527};
528
529ComposeDecisionBuilder::ComposeDecisionBuilder() : start_index_(0) {}
530
531ComposeDecisionBuilder::ComposeDecisionBuilder(
532 const std::vector<DecisionBuilder*>& dbs)
533 : CompositeDecisionBuilder(dbs), start_index_(0) {}
534
535ComposeDecisionBuilder::~ComposeDecisionBuilder() {}
536
537Decision* ComposeDecisionBuilder::Next(Solver* const s) {
538 const int size = builders_.size();
539 for (int i = start_index_; i < size; ++i) {
540 Decision* d = builders_[i]->Next(s);
541 if (d != nullptr) {
542 s->SaveAndSetValue(&start_index_, i);
543 return d;
544 }
545 }
546 s->SaveAndSetValue(&start_index_, size);
547 return nullptr;
548}
549
550std::string ComposeDecisionBuilder::DebugString() const {
551 return absl::StrFormat("ComposeDecisionBuilder(%s)",
553}
554} // namespace
555
556DecisionBuilder* Solver::Compose(DecisionBuilder* const db1,
557 DecisionBuilder* const db2) {
558 ComposeDecisionBuilder* c = RevAlloc(new ComposeDecisionBuilder());
559 c->Add(db1);
560 c->Add(db2);
561 return c;
562}
563
564DecisionBuilder* Solver::Compose(DecisionBuilder* const db1,
565 DecisionBuilder* const db2,
566 DecisionBuilder* const db3) {
567 ComposeDecisionBuilder* c = RevAlloc(new ComposeDecisionBuilder());
568 c->Add(db1);
569 c->Add(db2);
570 c->Add(db3);
571 return c;
572}
573
574DecisionBuilder* Solver::Compose(DecisionBuilder* const db1,
575 DecisionBuilder* const db2,
576 DecisionBuilder* const db3,
577 DecisionBuilder* const db4) {
578 ComposeDecisionBuilder* c = RevAlloc(new ComposeDecisionBuilder());
579 c->Add(db1);
580 c->Add(db2);
581 c->Add(db3);
582 c->Add(db4);
583 return c;
584}
585
586DecisionBuilder* Solver::Compose(const std::vector<DecisionBuilder*>& dbs) {
587 if (dbs.size() == 1) {
588 return dbs[0];
589 }
590 return RevAlloc(new ComposeDecisionBuilder(dbs));
591}
592
593// ---------- ClosureDecision ---------
594
595namespace {
596class ClosureDecision : public Decision {
597 public:
598 ClosureDecision(Solver::Action apply, Solver::Action refute)
599 : apply_(std::move(apply)), refute_(std::move(refute)) {}
600 ~ClosureDecision() override {}
601
602 void Apply(Solver* const s) override { apply_(s); }
603
604 void Refute(Solver* const s) override { refute_(s); }
605
606 std::string DebugString() const override { return "ClosureDecision"; }
607
608 private:
609 Solver::Action apply_;
610 Solver::Action refute_;
611};
612} // namespace
613
614Decision* Solver::MakeDecision(Action apply, Action refute) {
615 return RevAlloc(new ClosureDecision(std::move(apply), std::move(refute)));
616}
617
618// ---------- Try Decision Builder ----------
619
620namespace {
621
622class TryDecisionBuilder;
623
624class TryDecision : public Decision {
625 public:
626 explicit TryDecision(TryDecisionBuilder* const try_builder);
627 ~TryDecision() override;
628 void Apply(Solver* const solver) override;
629 void Refute(Solver* const solver) override;
630 std::string DebugString() const override { return "TryDecision"; }
631
632 private:
633 TryDecisionBuilder* const try_builder_;
634};
635
636class TryDecisionBuilder : public CompositeDecisionBuilder {
637 public:
638 TryDecisionBuilder();
639 explicit TryDecisionBuilder(const std::vector<DecisionBuilder*>& dbs);
640 ~TryDecisionBuilder() override;
641 Decision* Next(Solver* const solver) override;
642 std::string DebugString() const override;
643 void AdvanceToNextBuilder(Solver* const solver);
644
645 private:
646 TryDecision try_decision_;
647 int current_builder_;
648 bool start_new_builder_;
649};
650
651TryDecision::TryDecision(TryDecisionBuilder* const try_builder)
652 : try_builder_(try_builder) {}
653
654TryDecision::~TryDecision() {}
655
656void TryDecision::Apply(Solver* const solver) {}
657
658void TryDecision::Refute(Solver* const solver) {
659 try_builder_->AdvanceToNextBuilder(solver);
660}
661
662TryDecisionBuilder::TryDecisionBuilder()
663 : CompositeDecisionBuilder(),
664 try_decision_(this),
665 current_builder_(-1),
666 start_new_builder_(true) {}
667
668TryDecisionBuilder::TryDecisionBuilder(const std::vector<DecisionBuilder*>& dbs)
669 : CompositeDecisionBuilder(dbs),
670 try_decision_(this),
671 current_builder_(-1),
672 start_new_builder_(true) {}
673
674TryDecisionBuilder::~TryDecisionBuilder() {}
675
676Decision* TryDecisionBuilder::Next(Solver* const solver) {
677 if (current_builder_ < 0) {
678 solver->SaveAndSetValue(&current_builder_, 0);
679 start_new_builder_ = true;
680 }
681 if (start_new_builder_) {
682 start_new_builder_ = false;
683 return &try_decision_;
684 } else {
685 return builders_[current_builder_]->Next(solver);
686 }
687}
688
689std::string TryDecisionBuilder::DebugString() const {
690 return absl::StrFormat("TryDecisionBuilder(%s)",
692}
693
694void TryDecisionBuilder::AdvanceToNextBuilder(Solver* const solver) {
695 ++current_builder_;
696 start_new_builder_ = true;
697 if (current_builder_ >= builders_.size()) {
698 solver->Fail();
699 }
700}
701
702} // namespace
703
705 DecisionBuilder* const db2) {
706 TryDecisionBuilder* try_db = RevAlloc(new TryDecisionBuilder());
707 try_db->Add(db1);
708 try_db->Add(db2);
709 return try_db;
710}
711
713 DecisionBuilder* const db2,
714 DecisionBuilder* const db3) {
715 TryDecisionBuilder* try_db = RevAlloc(new TryDecisionBuilder());
716 try_db->Add(db1);
717 try_db->Add(db2);
718 try_db->Add(db3);
719 return try_db;
720}
721
723 DecisionBuilder* const db2,
724 DecisionBuilder* const db3,
725 DecisionBuilder* const db4) {
726 TryDecisionBuilder* try_db = RevAlloc(new TryDecisionBuilder());
727 try_db->Add(db1);
728 try_db->Add(db2);
729 try_db->Add(db3);
730 try_db->Add(db4);
731 return try_db;
732}
733
734DecisionBuilder* Solver::Try(const std::vector<DecisionBuilder*>& dbs) {
735 return RevAlloc(new TryDecisionBuilder(dbs));
736}
737
738// ---------- Variable Assignments ----------
739
740// ----- BaseAssignmentSelector -----
741
742namespace {
743class BaseVariableAssignmentSelector : public BaseObject {
744 public:
745 BaseVariableAssignmentSelector(Solver* solver,
746 const std::vector<IntVar*>& vars)
747 : solver_(solver),
748 vars_(vars),
750 last_unbound_(vars.size() - 1) {}
751
752 ~BaseVariableAssignmentSelector() override {}
753
754 virtual int64_t SelectValue(const IntVar* v, int64_t id) = 0;
755
756 // Returns -1 if no variable are suitable.
757 virtual int64_t ChooseVariable() = 0;
758
759 int64_t ChooseVariableWrapper() {
760 int64_t i;
761 for (i = first_unbound_.Value(); i <= last_unbound_.Value(); ++i) {
762 if (!vars_[i]->Bound()) {
763 break;
764 }
765 }
766 first_unbound_.SetValue(solver_, i);
767 if (i > last_unbound_.Value()) {
768 return -1;
769 }
770 for (i = last_unbound_.Value(); i >= first_unbound_.Value(); --i) {
771 if (!vars_[i]->Bound()) {
772 break;
773 }
774 }
775 last_unbound_.SetValue(solver_, i);
776 return ChooseVariable();
777 }
778
779 void Accept(ModelVisitor* const visitor) const {
780 visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension);
781 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
782 vars_);
783 visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension);
784 }
785
786 const std::vector<IntVar*>& vars() const { return vars_; }
787
788 protected:
789 Solver* const solver_;
790 std::vector<IntVar*> vars_;
791 Rev<int64_t> first_unbound_;
792 Rev<int64_t> last_unbound_;
793};
794
795// ----- Choose first unbound --
796
797int64_t ChooseFirstUnbound(Solver* solver, const std::vector<IntVar*>& vars,
798 int64_t first_unbound, int64_t last_unbound) {
799 for (int64_t i = first_unbound; i <= last_unbound; ++i) {
800 if (!vars[i]->Bound()) {
801 return i;
802 }
803 }
804 return -1;
805}
806
807// ----- Choose Min Size Lowest Min -----
808
809int64_t ChooseMinSizeLowestMin(Solver* solver, const std::vector<IntVar*>& vars,
810 int64_t first_unbound, int64_t last_unbound) {
811 uint64_t best_size = std::numeric_limits<uint64_t>::max();
812 int64_t best_min = std::numeric_limits<int64_t>::max();
813 int64_t best_index = -1;
814 for (int64_t i = first_unbound; i <= last_unbound; ++i) {
815 IntVar* const var = vars[i];
816 if (!var->Bound()) {
817 if (var->Size() < best_size ||
818 (var->Size() == best_size && var->Min() < best_min)) {
819 best_size = var->Size();
820 best_min = var->Min();
821 best_index = i;
822 }
823 }
824 }
825 return best_index;
826}
827
828// ----- Choose Min Size Highest Min -----
829
830int64_t ChooseMinSizeHighestMin(Solver* solver,
831 const std::vector<IntVar*>& vars,
832 int64_t first_unbound, int64_t last_unbound) {
833 uint64_t best_size = std::numeric_limits<uint64_t>::max();
834 int64_t best_min = std::numeric_limits<int64_t>::min();
835 int64_t best_index = -1;
836 for (int64_t i = first_unbound; i <= last_unbound; ++i) {
837 IntVar* const var = vars[i];
838 if (!var->Bound()) {
839 if (var->Size() < best_size ||
840 (var->Size() == best_size && var->Min() > best_min)) {
841 best_size = var->Size();
842 best_min = var->Min();
843 best_index = i;
844 }
845 }
846 }
847 return best_index;
848}
849
850// ----- Choose Min Size Lowest Max -----
851
852int64_t ChooseMinSizeLowestMax(Solver* solver, const std::vector<IntVar*>& vars,
853 int64_t first_unbound, int64_t last_unbound) {
854 uint64_t best_size = std::numeric_limits<uint64_t>::max();
855 int64_t best_max = std::numeric_limits<int64_t>::max();
856 int64_t best_index = -1;
857 for (int64_t i = first_unbound; i <= last_unbound; ++i) {
858 IntVar* const var = vars[i];
859 if (!var->Bound()) {
860 if (var->Size() < best_size ||
861 (var->Size() == best_size && var->Max() < best_max)) {
862 best_size = var->Size();
863 best_max = var->Max();
864 best_index = i;
865 }
866 }
867 }
868 return best_index;
869}
870
871// ----- Choose Min Size Highest Max -----
872
873int64_t ChooseMinSizeHighestMax(Solver* solver,
874 const std::vector<IntVar*>& vars,
875 int64_t first_unbound, int64_t last_unbound) {
876 uint64_t best_size = std::numeric_limits<uint64_t>::max();
877 int64_t best_max = std::numeric_limits<int64_t>::min();
878 int64_t best_index = -1;
879 for (int64_t i = first_unbound; i <= last_unbound; ++i) {
880 IntVar* const var = vars[i];
881 if (!var->Bound()) {
882 if (var->Size() < best_size ||
883 (var->Size() == best_size && var->Max() > best_max)) {
884 best_size = var->Size();
885 best_max = var->Max();
886 best_index = i;
887 }
888 }
889 }
890 return best_index;
891}
892
893// ----- Choose Lowest Min --
894
895int64_t ChooseLowestMin(Solver* solver, const std::vector<IntVar*>& vars,
896 int64_t first_unbound, int64_t last_unbound) {
897 int64_t best_min = std::numeric_limits<int64_t>::max();
898 int64_t best_index = -1;
899 for (int64_t i = first_unbound; i <= last_unbound; ++i) {
900 IntVar* const var = vars[i];
901 if (!var->Bound()) {
902 if (var->Min() < best_min) {
903 best_min = var->Min();
904 best_index = i;
905 }
906 }
907 }
908 return best_index;
909}
910
911// ----- Choose Highest Max -----
912
913int64_t ChooseHighestMax(Solver* solver, const std::vector<IntVar*>& vars,
914 int64_t first_unbound, int64_t last_unbound) {
915 int64_t best_max = std::numeric_limits<int64_t>::min();
916 int64_t best_index = -1;
917 for (int64_t i = first_unbound; i <= last_unbound; ++i) {
918 IntVar* const var = vars[i];
919 if (!var->Bound()) {
920 if (var->Max() > best_max) {
921 best_max = var->Max();
922 best_index = i;
923 }
924 }
925 }
926 return best_index;
927}
928
929// ----- Choose Lowest Size --
930
931int64_t ChooseMinSize(Solver* solver, const std::vector<IntVar*>& vars,
932 int64_t first_unbound, int64_t last_unbound) {
933 uint64_t best_size = std::numeric_limits<uint64_t>::max();
934 int64_t best_index = -1;
935 for (int64_t i = first_unbound; i <= last_unbound; ++i) {
936 IntVar* const var = vars[i];
937 if (!var->Bound()) {
938 if (var->Size() < best_size) {
939 best_size = var->Size();
940 best_index = i;
941 }
942 }
943 }
944 return best_index;
945}
946
947// ----- Choose Highest Size -----
948
949int64_t ChooseMaxSize(Solver* solver, const std::vector<IntVar*>& vars,
950 int64_t first_unbound, int64_t last_unbound) {
951 uint64_t best_size = 0;
952 int64_t best_index = -1;
953 for (int64_t i = first_unbound; i <= last_unbound; ++i) {
954 IntVar* const var = vars[i];
955 if (!var->Bound()) {
956 if (var->Size() > best_size) {
957 best_size = var->Size();
958 best_index = i;
959 }
960 }
961 }
962 return best_index;
963}
964
965// ----- Choose Highest Regret -----
966
967class HighestRegretSelectorOnMin : public BaseObject {
968 public:
969 explicit HighestRegretSelectorOnMin(const std::vector<IntVar*>& vars)
970 : iterators_(vars.size()) {
971 for (int64_t i = 0; i < vars.size(); ++i) {
972 iterators_[i] = vars[i]->MakeDomainIterator(true);
973 }
974 }
975 ~HighestRegretSelectorOnMin() override {}
976 int64_t Choose(Solver* const s, const std::vector<IntVar*>& vars,
977 int64_t first_unbound, int64_t last_unbound);
978 std::string DebugString() const override { return "MaxRegretSelector"; }
979
980 int64_t ComputeRegret(IntVar* var, int64_t index) const {
981 DCHECK(!var->Bound());
982 const int64_t vmin = var->Min();
983 IntVarIterator* const iterator = iterators_[index];
984 iterator->Init();
985 iterator->Next();
986 return iterator->Value() - vmin;
987 }
988
989 private:
990 std::vector<IntVarIterator*> iterators_;
991};
992
993int64_t HighestRegretSelectorOnMin::Choose(Solver* const s,
994 const std::vector<IntVar*>& vars,
995 int64_t first_unbound,
996 int64_t last_unbound) {
997 int64_t best_regret = 0;
998 int64_t index = -1;
999 for (int64_t i = first_unbound; i <= last_unbound; ++i) {
1000 IntVar* const var = vars[i];
1001 if (!var->Bound()) {
1002 const int64_t regret = ComputeRegret(var, i);
1003 if (regret > best_regret) {
1004 best_regret = regret;
1005 index = i;
1006 }
1007 }
1008 }
1009 return index;
1010}
1011
1012// ----- Choose random unbound --
1013
1014int64_t ChooseRandom(Solver* solver, const std::vector<IntVar*>& vars,
1015 int64_t first_unbound, int64_t last_unbound) {
1016 const int64_t span = last_unbound - first_unbound + 1;
1017 const int64_t shift = solver->Rand32(span);
1018 for (int64_t i = 0; i < span; ++i) {
1019 const int64_t index = (i + shift) % span + first_unbound;
1020 if (!vars[index]->Bound()) {
1021 return index;
1022 }
1023 }
1024 return -1;
1025}
1026
1027// ----- Choose min eval -----
1028
1029class CheapestVarSelector : public BaseObject {
1030 public:
1031 explicit CheapestVarSelector(std::function<int64_t(int64_t)> var_evaluator)
1032 : var_evaluator_(std::move(var_evaluator)) {}
1033 ~CheapestVarSelector() override {}
1034 int64_t Choose(Solver* const s, const std::vector<IntVar*>& vars,
1035 int64_t first_unbound, int64_t last_unbound);
1036 std::string DebugString() const override { return "CheapestVarSelector"; }
1037
1038 private:
1039 std::function<int64_t(int64_t)> var_evaluator_;
1040};
1041
1042int64_t CheapestVarSelector::Choose(Solver* const s,
1043 const std::vector<IntVar*>& vars,
1044 int64_t first_unbound,
1045 int64_t last_unbound) {
1046 int64_t best_eval = std::numeric_limits<int64_t>::max();
1047 int64_t index = -1;
1048 for (int64_t i = first_unbound; i <= last_unbound; ++i) {
1049 if (!vars[i]->Bound()) {
1050 const int64_t eval = var_evaluator_(i);
1051 if (eval < best_eval) {
1052 best_eval = eval;
1053 index = i;
1054 }
1055 }
1056 }
1057 return index;
1058}
1059
1060// ----- Path selector -----
1061// Follow path, where var[i] is represents the next of i
1062
1063class PathSelector : public BaseObject {
1064 public:
1065 PathSelector() : first_(std::numeric_limits<int64_t>::max()) {}
1066 ~PathSelector() override {}
1067 int64_t Choose(Solver* const s, const std::vector<IntVar*>& vars,
1068 int64_t first_unbound, int64_t last_unbound);
1069 std::string DebugString() const override { return "ChooseNextOnPath"; }
1070
1071 private:
1072 bool UpdateIndex(const std::vector<IntVar*>& vars, int64_t* index) const;
1073 bool FindPathStart(const std::vector<IntVar*>& vars, int64_t* index) const;
1074
1075 Rev<int64_t> first_;
1076};
1077
1078int64_t PathSelector::Choose(Solver* const s, const std::vector<IntVar*>& vars,
1079 int64_t first_unbound, int64_t last_unbound) {
1080 int64_t index = first_.Value();
1081 if (!UpdateIndex(vars, &index)) {
1082 return -1;
1083 }
1084 int64_t count = 0;
1085 while (vars[index]->Bound()) {
1086 index = vars[index]->Value();
1087 if (!UpdateIndex(vars, &index)) {
1088 return -1;
1089 }
1090 ++count;
1091 if (count >= vars.size() &&
1092 !FindPathStart(vars, &index)) { // Cycle detected
1093 return -1;
1094 }
1095 }
1096 first_.SetValue(s, index);
1097 return index;
1098}
1099
1100bool PathSelector::UpdateIndex(const std::vector<IntVar*>& vars,
1101 int64_t* index) const {
1102 if (*index >= vars.size()) {
1103 if (!FindPathStart(vars, index)) {
1104 return false;
1105 }
1106 }
1107 return true;
1108}
1109
1110// Select variables on a path:
1111// 1. Try to extend an existing route: look for an unbound variable, to which
1112// some other variable points.
1113// 2. If no such road is found, try to find a start node of a route: look for
1114// an unbound variable, to which no other variable can point.
1115// 3. If everything else fails, pick the first unbound variable.
1116bool PathSelector::FindPathStart(const std::vector<IntVar*>& vars,
1117 int64_t* index) const {
1118 // Try to extend an existing path
1119 for (int64_t i = vars.size() - 1; i >= 0; --i) {
1120 if (vars[i]->Bound()) {
1121 const int64_t next = vars[i]->Value();
1122 if (next < vars.size() && !vars[next]->Bound()) {
1123 *index = next;
1124 return true;
1125 }
1126 }
1127 }
1128 // Pick path start
1129 for (int64_t i = vars.size() - 1; i >= 0; --i) {
1130 if (!vars[i]->Bound()) {
1131 bool has_possible_prev = false;
1132 for (int64_t j = 0; j < vars.size(); ++j) {
1133 if (vars[j]->Contains(i)) {
1134 has_possible_prev = true;
1135 break;
1136 }
1137 }
1138 if (!has_possible_prev) {
1139 *index = i;
1140 return true;
1141 }
1142 }
1143 }
1144 // Pick first unbound
1145 for (int64_t i = 0; i < vars.size(); ++i) {
1146 if (!vars[i]->Bound()) {
1147 *index = i;
1148 return true;
1149 }
1150 }
1151 return false;
1152}
1153
1154// ----- Select min -----
1155
1156int64_t SelectMinValue(const IntVar* v, int64_t id) { return v->Min(); }
1157
1158// ----- Select max -----
1159
1160int64_t SelectMaxValue(const IntVar* v, int64_t id) { return v->Max(); }
1161
1162// ----- Select random -----
1163
1164int64_t SelectRandomValue(const IntVar* v, int64_t id) {
1165 const uint64_t span = v->Max() - v->Min() + 1;
1166 if (span > absl::GetFlag(FLAGS_cp_large_domain_no_splitting_limit)) {
1167 // Do not create holes in large domains.
1168 return v->Min();
1169 }
1170 const uint64_t size = v->Size();
1171 Solver* const s = v->solver();
1172 if (size > span / 4) { // Dense enough, we can try to find the
1173 // value randomly.
1174 for (;;) {
1175 const int64_t value = v->Min() + s->Rand64(span);
1176 if (v->Contains(value)) {
1177 return value;
1178 }
1179 }
1180 } else { // Not dense enough, we will count.
1181 int64_t index = s->Rand64(size);
1182 if (index <= size / 2) {
1183 for (int64_t i = v->Min(); i <= v->Max(); ++i) {
1184 if (v->Contains(i)) {
1185 if (--index == 0) {
1186 return i;
1187 }
1188 }
1189 }
1190 CHECK_LE(index, 0);
1191 } else {
1192 for (int64_t i = v->Max(); i > v->Min(); --i) {
1193 if (v->Contains(i)) {
1194 if (--index == 0) {
1195 return i;
1196 }
1197 }
1198 }
1199 CHECK_LE(index, 0);
1200 }
1201 }
1202 return 0;
1203}
1204
1205// ----- Select center -----
1206
1207int64_t SelectCenterValue(const IntVar* v, int64_t id) {
1208 const int64_t vmin = v->Min();
1209 const int64_t vmax = v->Max();
1210 if (vmax - vmin > absl::GetFlag(FLAGS_cp_large_domain_no_splitting_limit)) {
1211 // Do not create holes in large domains.
1212 return vmin;
1213 }
1214 const int64_t mid = (vmin + vmax) / 2;
1215 if (v->Contains(mid)) {
1216 return mid;
1217 }
1218 const int64_t diameter = vmax - mid; // always greater than mid - vmix.
1219 for (int64_t i = 1; i <= diameter; ++i) {
1220 if (v->Contains(mid - i)) {
1221 return mid - i;
1222 }
1223 if (v->Contains(mid + i)) {
1224 return mid + i;
1225 }
1226 }
1227 return 0;
1228}
1229
1230// ----- Select center -----
1231
1232int64_t SelectSplitValue(const IntVar* v, int64_t id) {
1233 const int64_t vmin = v->Min();
1234 const int64_t vmax = v->Max();
1235 const uint64_t delta = vmax - vmin;
1236 const int64_t mid = vmin + delta / 2;
1237 return mid;
1238}
1239
1240// ----- Select the value yielding the cheapest "eval" for a var -----
1241
1242class CheapestValueSelector : public BaseObject {
1243 public:
1244 CheapestValueSelector(std::function<int64_t(int64_t, int64_t)> eval,
1245 std::function<int64_t(int64_t)> tie_breaker)
1246 : eval_(std::move(eval)), tie_breaker_(std::move(tie_breaker)) {}
1247 ~CheapestValueSelector() override {}
1248 int64_t Select(const IntVar* v, int64_t id);
1249 std::string DebugString() const override { return "CheapestValue"; }
1250
1251 private:
1252 std::function<int64_t(int64_t, int64_t)> eval_;
1253 std::function<int64_t(int64_t)> tie_breaker_;
1254 std::vector<int64_t> cache_;
1255};
1256
1257int64_t CheapestValueSelector::Select(const IntVar* v, int64_t id) {
1258 cache_.clear();
1259 int64_t best = std::numeric_limits<int64_t>::max();
1260 std::unique_ptr<IntVarIterator> it(v->MakeDomainIterator(false));
1261 for (const int64_t i : InitAndGetValues(it.get())) {
1262 int64_t eval = eval_(id, i);
1263 if (eval < best) {
1264 best = eval;
1265 cache_.clear();
1266 cache_.push_back(i);
1267 } else if (eval == best) {
1268 cache_.push_back(i);
1269 }
1270 }
1271 DCHECK_GT(cache_.size(), 0);
1272 if (tie_breaker_ == nullptr || cache_.size() == 1) {
1273 return cache_.back();
1274 } else {
1275 return cache_[tie_breaker_(cache_.size())];
1276 }
1277}
1278
1279// ----- Select the best value for the var, based on a comparator callback -----
1280
1281// The comparator should be a total order, but does not have to be a strict
1282// ordering. If there is a tie between two values val1 and val2, i.e. if
1283// !comparator(var_id, val1, val2) && !comparator(var_id, val2, val1), then
1284// the lowest value wins.
1285// comparator(var_id, val1, val2) == true means than val1 should be preferred
1286// over val2 for variable var_id.
1287class BestValueByComparisonSelector : public BaseObject {
1288 public:
1289 explicit BestValueByComparisonSelector(
1291 : comparator_(std::move(comparator)) {}
1292 ~BestValueByComparisonSelector() override {}
1293 int64_t Select(const IntVar* v, int64_t id);
1294 std::string DebugString() const override {
1295 return "BestValueByComparisonSelector";
1296 }
1297
1298 private:
1300};
1301
1302int64_t BestValueByComparisonSelector::Select(const IntVar* v, int64_t id) {
1303 std::unique_ptr<IntVarIterator> it(v->MakeDomainIterator(false));
1304 it->Init();
1305 DCHECK(it->Ok()); // At least one value.
1306 int64_t best_value = it->Value();
1307 for (it->Next(); it->Ok(); it->Next()) {
1308 const int64_t candidate_value = it->Value();
1309 if (comparator_(id, candidate_value, best_value)) {
1310 best_value = candidate_value;
1311 }
1312 }
1313 return best_value;
1314}
1315
1316// ----- VariableAssignmentSelector -----
1317
1318class VariableAssignmentSelector : public BaseVariableAssignmentSelector {
1319 public:
1320 VariableAssignmentSelector(Solver* solver, const std::vector<IntVar*>& vars,
1321 Solver::VariableIndexSelector var_selector,
1322 Solver::VariableValueSelector value_selector,
1323 const std::string& name)
1324 : BaseVariableAssignmentSelector(solver, vars),
1325 var_selector_(std::move(var_selector)),
1326 value_selector_(std::move(value_selector)),
1327 name_(name) {}
1328 ~VariableAssignmentSelector() override {}
1329 int64_t SelectValue(const IntVar* var, int64_t id) override {
1330 return value_selector_(var, id);
1331 }
1332 int64_t ChooseVariable() override {
1333 return var_selector_(solver_, vars_, first_unbound_.Value(),
1334 last_unbound_.Value());
1335 }
1336 std::string DebugString() const override;
1337
1338 private:
1339 Solver::VariableIndexSelector var_selector_;
1340 Solver::VariableValueSelector value_selector_;
1341 const std::string name_;
1342};
1343
1344std::string VariableAssignmentSelector::DebugString() const {
1345 return absl::StrFormat("%s(%s)", name_, JoinDebugStringPtr(vars_, ", "));
1346}
1347
1348// ----- Base Global Evaluator-based selector -----
1349
1350class BaseEvaluatorSelector : public BaseVariableAssignmentSelector {
1351 public:
1352 BaseEvaluatorSelector(Solver* solver, const std::vector<IntVar*>& vars,
1353 std::function<int64_t(int64_t, int64_t)> evaluator);
1354 ~BaseEvaluatorSelector() override {}
1355
1356 protected:
1357 struct Element {
1358 Element() : var(0), value(0) {}
1359 Element(int64_t i, int64_t j) : var(i), value(j) {}
1360 int64_t var;
1361 int64_t value;
1362 };
1363
1364 std::string DebugStringInternal(const std::string& name) const {
1365 return absl::StrFormat("%s(%s)", name, JoinDebugStringPtr(vars_, ", "));
1366 }
1367
1368 std::function<int64_t(int64_t, int64_t)> evaluator_;
1369};
1370
1371BaseEvaluatorSelector::BaseEvaluatorSelector(
1372 Solver* solver, const std::vector<IntVar*>& vars,
1373 std::function<int64_t(int64_t, int64_t)> evaluator)
1374 : BaseVariableAssignmentSelector(solver, vars),
1375 evaluator_(std::move(evaluator)) {}
1376
1377// ----- Global Dynamic Evaluator-based selector -----
1378
1379class DynamicEvaluatorSelector : public BaseEvaluatorSelector {
1380 public:
1381 DynamicEvaluatorSelector(Solver* solver, const std::vector<IntVar*>& vars,
1382 std::function<int64_t(int64_t, int64_t)> evaluator,
1383 std::function<int64_t(int64_t)> tie_breaker);
1384 ~DynamicEvaluatorSelector() override {}
1385 int64_t SelectValue(const IntVar* var, int64_t id) override;
1386 int64_t ChooseVariable() override;
1387 std::string DebugString() const override;
1388
1389 private:
1390 int64_t first_;
1391 std::function<int64_t(int64_t)> tie_breaker_;
1392 std::vector<Element> cache_;
1393};
1394
1395DynamicEvaluatorSelector::DynamicEvaluatorSelector(
1396 Solver* solver, const std::vector<IntVar*>& vars,
1397 std::function<int64_t(int64_t, int64_t)> evaluator,
1398 std::function<int64_t(int64_t)> tie_breaker)
1399 : BaseEvaluatorSelector(solver, vars, std::move(evaluator)),
1400 first_(-1),
1401 tie_breaker_(std::move(tie_breaker)) {}
1402
1403int64_t DynamicEvaluatorSelector::SelectValue(const IntVar* var, int64_t id) {
1404 return cache_[first_].value;
1405}
1406
1407int64_t DynamicEvaluatorSelector::ChooseVariable() {
1408 int64_t best_evaluation = std::numeric_limits<int64_t>::max();
1409 cache_.clear();
1410 for (int64_t i = 0; i < vars_.size(); ++i) {
1411 const IntVar* const var = vars_[i];
1412 if (!var->Bound()) {
1413 std::unique_ptr<IntVarIterator> it(var->MakeDomainIterator(false));
1414 for (const int64_t j : InitAndGetValues(it.get())) {
1415 const int64_t value = evaluator_(i, j);
1416 if (value < best_evaluation) {
1417 best_evaluation = value;
1418 cache_.clear();
1419 cache_.push_back(Element(i, j));
1420 } else if (value == best_evaluation && tie_breaker_) {
1421 cache_.push_back(Element(i, j));
1422 }
1423 }
1424 }
1425 }
1426
1427 if (cache_.empty()) {
1428 return -1;
1429 }
1430
1431 if (tie_breaker_ == nullptr || cache_.size() == 1) {
1432 first_ = 0;
1433 return cache_.front().var;
1434 } else {
1435 first_ = tie_breaker_(cache_.size());
1436 return cache_[first_].var;
1437 }
1438}
1439
1440std::string DynamicEvaluatorSelector::DebugString() const {
1441 return DebugStringInternal("AssignVariablesOnDynamicEvaluator");
1442}
1443
1444// ----- Global Dynamic Evaluator-based selector -----
1445
1446class StaticEvaluatorSelector : public BaseEvaluatorSelector {
1447 public:
1448 StaticEvaluatorSelector(
1449 Solver* solver, const std::vector<IntVar*>& vars,
1450 const std::function<int64_t(int64_t, int64_t)>& evaluator);
1451 ~StaticEvaluatorSelector() override {}
1452 int64_t SelectValue(const IntVar* var, int64_t id) override;
1453 int64_t ChooseVariable() override;
1454 std::string DebugString() const override;
1455
1456 private:
1457 class Compare {
1458 public:
1459 explicit Compare(std::function<int64_t(int64_t, int64_t)> evaluator)
1460 : evaluator_(std::move(evaluator)) {}
1461 bool operator()(const Element& lhs, const Element& rhs) const {
1462 const int64_t value_lhs = Value(lhs);
1463 const int64_t value_rhs = Value(rhs);
1464 return value_lhs < value_rhs ||
1465 (value_lhs == value_rhs &&
1466 (lhs.var < rhs.var ||
1467 (lhs.var == rhs.var && lhs.value < rhs.value)));
1468 }
1469 int64_t Value(const Element& element) const {
1470 return evaluator_(element.var, element.value);
1471 }
1472
1473 private:
1474 std::function<int64_t(int64_t, int64_t)> evaluator_;
1475 };
1476
1477 Compare comp_;
1478 std::vector<Element> elements_;
1479 int64_t first_;
1480};
1481
1482StaticEvaluatorSelector::StaticEvaluatorSelector(
1483 Solver* solver, const std::vector<IntVar*>& vars,
1484 const std::function<int64_t(int64_t, int64_t)>& evaluator)
1485 : BaseEvaluatorSelector(solver, vars, evaluator),
1486 comp_(evaluator),
1487 first_(-1) {}
1488
1489int64_t StaticEvaluatorSelector::SelectValue(const IntVar* var, int64_t id) {
1490 return elements_[first_].value;
1491}
1492
1493int64_t StaticEvaluatorSelector::ChooseVariable() {
1494 if (first_ == -1) { // first call to select. update assignment costs
1495 // Two phases: compute size then filland sort
1496 int64_t element_size = 0;
1497 for (int64_t i = 0; i < vars_.size(); ++i) {
1498 if (!vars_[i]->Bound()) {
1499 element_size += vars_[i]->Size();
1500 }
1501 }
1502 elements_.resize(element_size);
1503 int count = 0;
1504 for (int i = 0; i < vars_.size(); ++i) {
1505 const IntVar* const var = vars_[i];
1506 if (!var->Bound()) {
1507 std::unique_ptr<IntVarIterator> it(var->MakeDomainIterator(false));
1508 for (const int64_t value : InitAndGetValues(it.get())) {
1509 elements_[count++] = Element(i, value);
1510 }
1511 }
1512 }
1513 // Sort is stable here given the tie-breaking rules in comp_.
1514 std::sort(elements_.begin(), elements_.end(), comp_);
1515 solver_->SaveAndSetValue<int64_t>(&first_, 0);
1516 }
1517 for (int64_t i = first_; i < elements_.size(); ++i) {
1518 const Element& element = elements_[i];
1519 IntVar* const var = vars_[element.var];
1520 if (!var->Bound() && var->Contains(element.value)) {
1521 solver_->SaveAndSetValue(&first_, i);
1522 return element.var;
1523 }
1524 }
1525 solver_->SaveAndSetValue(&first_, static_cast<int64_t>(elements_.size()));
1526 return -1;
1527}
1528
1529std::string StaticEvaluatorSelector::DebugString() const {
1530 return DebugStringInternal("AssignVariablesOnStaticEvaluator");
1531}
1532
1533// ----- AssignOneVariableValue decision -----
1534
1535class AssignOneVariableValue : public Decision {
1536 public:
1537 AssignOneVariableValue(IntVar* const v, int64_t val);
1538 ~AssignOneVariableValue() override {}
1539 void Apply(Solver* const s) override;
1540 void Refute(Solver* const s) override;
1541 std::string DebugString() const override;
1542 void Accept(DecisionVisitor* const visitor) const override {
1543 visitor->VisitSetVariableValue(var_, value_);
1544 }
1545
1546 private:
1547 IntVar* const var_;
1548 int64_t value_;
1549};
1550
1551AssignOneVariableValue::AssignOneVariableValue(IntVar* const v, int64_t val)
1552 : var_(v), value_(val) {}
1553
1554std::string AssignOneVariableValue::DebugString() const {
1555 return absl::StrFormat("[%s == %d] or [%s != %d]", var_->DebugString(),
1556 value_, var_->DebugString(), value_);
1557}
1558
1559void AssignOneVariableValue::Apply(Solver* const s) { var_->SetValue(value_); }
1560
1561void AssignOneVariableValue::Refute(Solver* const s) {
1562 var_->RemoveValue(value_);
1563}
1564} // namespace
1565
1566Decision* Solver::MakeAssignVariableValue(IntVar* const var, int64_t val) {
1567 return RevAlloc(new AssignOneVariableValue(var, val));
1568}
1569
1570// ----- AssignOneVariableValueOrFail decision -----
1571
1572namespace {
1573class AssignOneVariableValueOrFail : public Decision {
1574 public:
1575 AssignOneVariableValueOrFail(IntVar* const v, int64_t value);
1576 ~AssignOneVariableValueOrFail() override {}
1577 void Apply(Solver* const s) override;
1578 void Refute(Solver* const s) override;
1579 std::string DebugString() const override;
1580 void Accept(DecisionVisitor* const visitor) const override {
1581 visitor->VisitSetVariableValue(var_, value_);
1582 }
1583
1584 private:
1585 IntVar* const var_;
1586 const int64_t value_;
1587};
1588
1589AssignOneVariableValueOrFail::AssignOneVariableValueOrFail(IntVar* const v,
1590 int64_t value)
1591 : var_(v), value_(value) {}
1592
1593std::string AssignOneVariableValueOrFail::DebugString() const {
1594 return absl::StrFormat("[%s == %d] or fail", var_->DebugString(), value_);
1595}
1596
1597void AssignOneVariableValueOrFail::Apply(Solver* const s) {
1598 var_->SetValue(value_);
1599}
1600
1601void AssignOneVariableValueOrFail::Refute(Solver* const s) { s->Fail(); }
1602} // namespace
1603
1605 int64_t value) {
1606 return RevAlloc(new AssignOneVariableValueOrFail(var, value));
1607}
1608
1609// ----- AssignOneVariableValueOrDoNothing decision -----
1610
1611namespace {
1612class AssignOneVariableValueDoNothing : public Decision {
1613 public:
1614 AssignOneVariableValueDoNothing(IntVar* const v, int64_t value)
1615 : var_(v), value_(value) {}
1616 ~AssignOneVariableValueDoNothing() override {}
1617 void Apply(Solver* const s) override { var_->SetValue(value_); }
1618 void Refute(Solver* const s) override {}
1619 std::string DebugString() const override {
1620 return absl::StrFormat("[%s == %d] or []", var_->DebugString(), value_);
1621 }
1622 void Accept(DecisionVisitor* const visitor) const override {
1623 visitor->VisitSetVariableValue(var_, value_);
1624 }
1625
1626 private:
1627 IntVar* const var_;
1628 const int64_t value_;
1629};
1630
1631} // namespace
1632
1634 int64_t value) {
1635 return RevAlloc(new AssignOneVariableValueDoNothing(var, value));
1636}
1637
1638// ----- AssignOneVariableValue decision -----
1639
1640namespace {
1641class SplitOneVariable : public Decision {
1642 public:
1643 SplitOneVariable(IntVar* const v, int64_t val, bool start_with_lower_half);
1644 ~SplitOneVariable() override {}
1645 void Apply(Solver* const s) override;
1646 void Refute(Solver* const s) override;
1647 std::string DebugString() const override;
1648 void Accept(DecisionVisitor* const visitor) const override {
1649 visitor->VisitSplitVariableDomain(var_, value_, start_with_lower_half_);
1650 }
1651
1652 private:
1653 IntVar* const var_;
1654 const int64_t value_;
1655 const bool start_with_lower_half_;
1656};
1657
1658SplitOneVariable::SplitOneVariable(IntVar* const v, int64_t val,
1659 bool start_with_lower_half)
1660 : var_(v), value_(val), start_with_lower_half_(start_with_lower_half) {}
1661
1662std::string SplitOneVariable::DebugString() const {
1663 if (start_with_lower_half_) {
1664 return absl::StrFormat("[%s <= %d]", var_->DebugString(), value_);
1665 } else {
1666 return absl::StrFormat("[%s >= %d]", var_->DebugString(), value_);
1667 }
1668}
1669
1670void SplitOneVariable::Apply(Solver* const s) {
1671 if (start_with_lower_half_) {
1672 var_->SetMax(value_);
1673 } else {
1674 var_->SetMin(value_ + 1);
1675 }
1676}
1677
1678void SplitOneVariable::Refute(Solver* const s) {
1679 if (start_with_lower_half_) {
1680 var_->SetMin(value_ + 1);
1681 } else {
1682 var_->SetMax(value_);
1683 }
1684}
1685} // namespace
1686
1688 bool start_with_lower_half) {
1689 return RevAlloc(new SplitOneVariable(var, val, start_with_lower_half));
1690}
1691
1693 int64_t value) {
1694 return MakeSplitVariableDomain(var, value, true);
1695}
1696
1698 int64_t value) {
1699 return MakeSplitVariableDomain(var, value, false);
1700}
1701
1702// ----- AssignVariablesValues decision -----
1703
1704namespace {
1705class AssignVariablesValues : public Decision {
1706 public:
1707 AssignVariablesValues(const std::vector<IntVar*>& vars,
1708 const std::vector<int64_t>& values);
1709 ~AssignVariablesValues() override {}
1710 void Apply(Solver* const s) override;
1711 void Refute(Solver* const s) override;
1712 std::string DebugString() const override;
1713 void Accept(DecisionVisitor* const visitor) const override {
1714 for (int i = 0; i < vars_.size(); ++i) {
1715 visitor->VisitSetVariableValue(vars_[i], values_[i]);
1716 }
1717 }
1718
1719 virtual void Accept(ModelVisitor* const visitor) const {
1720 visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension);
1721 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
1722 vars_);
1723 visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension);
1724 }
1725
1726 private:
1727 const std::vector<IntVar*> vars_;
1728 const std::vector<int64_t> values_;
1729};
1730
1731AssignVariablesValues::AssignVariablesValues(const std::vector<IntVar*>& vars,
1732 const std::vector<int64_t>& values)
1733 : vars_(vars), values_(values) {}
1734
1735std::string AssignVariablesValues::DebugString() const {
1736 std::string out;
1737 for (int i = 0; i < vars_.size(); ++i) {
1738 absl::StrAppendFormat(&out, "[%s == %d]", vars_[i]->DebugString(),
1739 values_[i]);
1740 }
1741 return out;
1742}
1743
1744void AssignVariablesValues::Apply(Solver* const s) {
1745 for (int i = 0; i < vars_.size(); ++i) {
1746 vars_[i]->SetValue(values_[i]);
1747 }
1748}
1749
1750void AssignVariablesValues::Refute(Solver* const s) {
1751 std::vector<IntVar*> terms;
1752 for (int i = 0; i < vars_.size(); ++i) {
1753 IntVar* term = s->MakeBoolVar();
1754 s->MakeIsDifferentCstCt(vars_[i], values_[i], term);
1755 terms.push_back(term);
1756 }
1757 s->AddConstraint(s->MakeSumGreaterOrEqual(terms, 1));
1758}
1759} // namespace
1760
1762 const std::vector<IntVar*>& vars, const std::vector<int64_t>& values) {
1763 CHECK_EQ(vars.size(), values.size());
1764 return RevAlloc(new AssignVariablesValues(vars, values));
1765}
1766
1767// ----- AssignAllVariables -----
1768
1769namespace {
1770class BaseAssignVariables : public DecisionBuilder {
1771 public:
1772 enum Mode {
1773 ASSIGN,
1774 SPLIT_LOWER,
1775 SPLIT_UPPER,
1776 };
1777
1778 BaseAssignVariables(BaseVariableAssignmentSelector* const selector, Mode mode)
1779 : selector_(selector), mode_(mode) {}
1780
1781 ~BaseAssignVariables() override;
1782 Decision* Next(Solver* const s) override;
1783 std::string DebugString() const override;
1784 static BaseAssignVariables* MakePhase(
1785 Solver* const s, const std::vector<IntVar*>& vars,
1786 Solver::VariableIndexSelector var_selector,
1787 Solver::VariableValueSelector value_selector,
1788 const std::string& value_selector_name, BaseAssignVariables::Mode mode);
1789
1790 static Solver::VariableIndexSelector MakeVariableSelector(
1791 Solver* const s, const std::vector<IntVar*>& vars,
1793 switch (str) {
1797 return ChooseFirstUnbound;
1799 return ChooseRandom;
1801 return ChooseMinSizeLowestMin;
1803 return ChooseMinSizeHighestMin;
1805 return ChooseMinSizeLowestMax;
1807 return ChooseMinSizeHighestMax;
1809 return ChooseLowestMin;
1811 return ChooseHighestMax;
1813 return ChooseMinSize;
1815 return ChooseMaxSize;
1817 HighestRegretSelectorOnMin* const selector =
1818 s->RevAlloc(new HighestRegretSelectorOnMin(vars));
1819 return [selector](Solver* solver, const std::vector<IntVar*>& vars,
1820 int first_unbound, int last_unbound) {
1821 return selector->Choose(solver, vars, first_unbound, last_unbound);
1822 };
1823 }
1824 case Solver::CHOOSE_PATH: {
1825 PathSelector* const selector = s->RevAlloc(new PathSelector());
1826 return [selector](Solver* solver, const std::vector<IntVar*>& vars,
1827 int first_unbound, int last_unbound) {
1828 return selector->Choose(solver, vars, first_unbound, last_unbound);
1829 };
1830 }
1831 default:
1832 LOG(FATAL) << "Unknown int var strategy " << str;
1833 return nullptr;
1834 }
1835 }
1836
1837 static Solver::VariableValueSelector MakeValueSelector(
1838 Solver* const s, Solver::IntValueStrategy val_str) {
1839 switch (val_str) {
1843 return SelectMinValue;
1845 return SelectMaxValue;
1847 return SelectRandomValue;
1849 return SelectCenterValue;
1851 return SelectSplitValue;
1853 return SelectSplitValue;
1854 default:
1855 LOG(FATAL) << "Unknown int value strategy " << val_str;
1856 return nullptr;
1857 }
1858 }
1859
1860 void Accept(ModelVisitor* const visitor) const override {
1861 selector_->Accept(visitor);
1862 }
1863
1864 protected:
1865 BaseVariableAssignmentSelector* const selector_;
1866 const Mode mode_;
1867};
1868
1869BaseAssignVariables::~BaseAssignVariables() {}
1870
1871Decision* BaseAssignVariables::Next(Solver* const s) {
1872 const std::vector<IntVar*>& vars = selector_->vars();
1873 int id = selector_->ChooseVariableWrapper();
1874 if (id >= 0 && id < vars.size()) {
1875 IntVar* const var = vars[id];
1876 const int64_t value = selector_->SelectValue(var, id);
1877 switch (mode_) {
1878 case ASSIGN:
1879 return s->RevAlloc(new AssignOneVariableValue(var, value));
1880 case SPLIT_LOWER:
1881 return s->RevAlloc(new SplitOneVariable(var, value, true));
1882 case SPLIT_UPPER:
1883 return s->RevAlloc(new SplitOneVariable(var, value, false));
1884 }
1885 }
1886 return nullptr;
1887}
1888
1889std::string BaseAssignVariables::DebugString() const {
1890 return selector_->DebugString();
1891}
1892
1893BaseAssignVariables* BaseAssignVariables::MakePhase(
1894 Solver* const s, const std::vector<IntVar*>& vars,
1895 Solver::VariableIndexSelector var_selector,
1896 Solver::VariableValueSelector value_selector,
1897 const std::string& value_selector_name, BaseAssignVariables::Mode mode) {
1898 BaseVariableAssignmentSelector* const selector =
1899 s->RevAlloc(new VariableAssignmentSelector(
1900 s, vars, std::move(var_selector), std::move(value_selector),
1901 value_selector_name));
1902 return s->RevAlloc(new BaseAssignVariables(selector, mode));
1903}
1904
1905std::string ChooseVariableName(Solver::IntVarStrategy var_str) {
1906 switch (var_str) {
1910 return "ChooseFirstUnbound";
1912 return "ChooseRandom";
1914 return "ChooseMinSizeLowestMin";
1916 return "ChooseMinSizeHighestMin";
1918 return "ChooseMinSizeLowestMax";
1920 return "ChooseMinSizeHighestMax";
1922 return "ChooseLowestMin";
1924 return "ChooseHighestMax";
1926 return "ChooseMinSize";
1928 return "ChooseMaxSize;";
1930 return "HighestRegretSelectorOnMin";
1932 return "PathSelector";
1933 default:
1934 LOG(FATAL) << "Unknown int var strategy " << var_str;
1935 return "";
1936 }
1937}
1938
1939std::string SelectValueName(Solver::IntValueStrategy val_str) {
1940 switch (val_str) {
1944 return "SelectMinValue";
1946 return "SelectMaxValue";
1948 return "SelectRandomValue";
1950 return "SelectCenterValue";
1952 return "SelectSplitValue";
1954 return "SelectSplitValue";
1955 default:
1956 LOG(FATAL) << "Unknown int value strategy " << val_str;
1957 return "";
1958 }
1959}
1960
1961std::string BuildHeuristicsName(Solver::IntVarStrategy var_str,
1962 Solver::IntValueStrategy val_str) {
1963 return ChooseVariableName(var_str) + "_" + SelectValueName(val_str);
1964}
1965} // namespace
1966
1968 Solver::IntVarStrategy var_str,
1969 Solver::IntValueStrategy val_str) {
1970 std::vector<IntVar*> vars(1);
1971 vars[0] = v0;
1972 return MakePhase(vars, var_str, val_str);
1973}
1974
1976 Solver::IntVarStrategy var_str,
1977 Solver::IntValueStrategy val_str) {
1978 std::vector<IntVar*> vars(2);
1979 vars[0] = v0;
1980 vars[1] = v1;
1981 return MakePhase(vars, var_str, val_str);
1982}
1983
1985 IntVar* const v2,
1986 Solver::IntVarStrategy var_str,
1987 Solver::IntValueStrategy val_str) {
1988 std::vector<IntVar*> vars(3);
1989 vars[0] = v0;
1990 vars[1] = v1;
1991 vars[2] = v2;
1992 return MakePhase(vars, var_str, val_str);
1993}
1994
1996 IntVar* const v2, IntVar* const v3,
1997 Solver::IntVarStrategy var_str,
1998 Solver::IntValueStrategy val_str) {
1999 std::vector<IntVar*> vars(4);
2000 vars[0] = v0;
2001 vars[1] = v1;
2002 vars[2] = v2;
2003 vars[3] = v3;
2004 return MakePhase(vars, var_str, val_str);
2005}
2006
2007BaseAssignVariables::Mode ChooseMode(Solver::IntValueStrategy val_str) {
2008 BaseAssignVariables::Mode mode = BaseAssignVariables::ASSIGN;
2009 if (val_str == Solver::SPLIT_LOWER_HALF) {
2010 mode = BaseAssignVariables::SPLIT_LOWER;
2011 } else if (val_str == Solver::SPLIT_UPPER_HALF) {
2012 mode = BaseAssignVariables::SPLIT_UPPER;
2013 }
2014 return mode;
2015}
2016
2017DecisionBuilder* Solver::MakePhase(const std::vector<IntVar*>& vars,
2018 Solver::IntVarStrategy var_str,
2019 Solver::IntValueStrategy val_str) {
2020 Solver::VariableIndexSelector var_selector =
2021 BaseAssignVariables::MakeVariableSelector(this, vars, var_str);
2022 Solver::VariableValueSelector value_selector =
2023 BaseAssignVariables::MakeValueSelector(this, val_str);
2024 const std::string name = BuildHeuristicsName(var_str, val_str);
2025 return BaseAssignVariables::MakePhase(
2026 this, vars, var_selector, value_selector, name, ChooseMode(val_str));
2027}
2028
2029DecisionBuilder* Solver::MakePhase(const std::vector<IntVar*>& vars,
2030 Solver::IndexEvaluator1 var_evaluator,
2031 Solver::IntValueStrategy val_str) {
2032 CHECK(var_evaluator != nullptr);
2033 CheapestVarSelector* const var_selector =
2034 RevAlloc(new CheapestVarSelector(std::move(var_evaluator)));
2035 Solver::VariableIndexSelector choose_variable =
2036 [var_selector](Solver* solver, const std::vector<IntVar*>& vars,
2037 int first_unbound, int last_unbound) {
2038 return var_selector->Choose(solver, vars, first_unbound, last_unbound);
2039 };
2040 Solver::VariableValueSelector select_value =
2041 BaseAssignVariables::MakeValueSelector(this, val_str);
2042 const std::string name = "ChooseCheapestVariable_" + SelectValueName(val_str);
2043 return BaseAssignVariables::MakePhase(
2044 this, vars, choose_variable, select_value, name, ChooseMode(val_str));
2045}
2046
2047DecisionBuilder* Solver::MakePhase(const std::vector<IntVar*>& vars,
2048 Solver::IntVarStrategy var_str,
2049 Solver::IndexEvaluator2 value_evaluator) {
2050 Solver::VariableIndexSelector choose_variable =
2051 BaseAssignVariables::MakeVariableSelector(this, vars, var_str);
2052 CheapestValueSelector* const value_selector =
2053 RevAlloc(new CheapestValueSelector(std::move(value_evaluator), nullptr));
2054 Solver::VariableValueSelector select_value =
2055 [value_selector](const IntVar* var, int64_t id) {
2056 return value_selector->Select(var, id);
2057 };
2058 const std::string name = ChooseVariableName(var_str) + "_SelectCheapestValue";
2059 return BaseAssignVariables::MakePhase(this, vars, choose_variable,
2060 select_value, name,
2061 BaseAssignVariables::ASSIGN);
2062}
2063
2065 const std::vector<IntVar*>& vars, IntVarStrategy var_str,
2066 VariableValueComparator var_val1_val2_comparator) {
2067 Solver::VariableIndexSelector choose_variable =
2068 BaseAssignVariables::MakeVariableSelector(this, vars, var_str);
2069 BestValueByComparisonSelector* const value_selector = RevAlloc(
2070 new BestValueByComparisonSelector(std::move(var_val1_val2_comparator)));
2071 Solver::VariableValueSelector select_value =
2072 [value_selector](const IntVar* var, int64_t id) {
2073 return value_selector->Select(var, id);
2074 };
2075 return BaseAssignVariables::MakePhase(this, vars, choose_variable,
2076 select_value, "CheapestValue",
2077 BaseAssignVariables::ASSIGN);
2078}
2079
2080DecisionBuilder* Solver::MakePhase(const std::vector<IntVar*>& vars,
2081 Solver::IndexEvaluator1 var_evaluator,
2082 Solver::IndexEvaluator2 value_evaluator) {
2083 CheapestVarSelector* const var_selector =
2084 RevAlloc(new CheapestVarSelector(std::move(var_evaluator)));
2085 Solver::VariableIndexSelector choose_variable =
2086 [var_selector](Solver* solver, const std::vector<IntVar*>& vars,
2087 int first_unbound, int last_unbound) {
2088 return var_selector->Choose(solver, vars, first_unbound, last_unbound);
2089 };
2090 CheapestValueSelector* value_selector =
2091 RevAlloc(new CheapestValueSelector(std::move(value_evaluator), nullptr));
2092 Solver::VariableValueSelector select_value =
2093 [value_selector](const IntVar* var, int64_t id) {
2094 return value_selector->Select(var, id);
2095 };
2096 return BaseAssignVariables::MakePhase(this, vars, choose_variable,
2097 select_value, "CheapestValue",
2098 BaseAssignVariables::ASSIGN);
2099}
2100
2101DecisionBuilder* Solver::MakePhase(const std::vector<IntVar*>& vars,
2102 Solver::IntVarStrategy var_str,
2103 Solver::IndexEvaluator2 value_evaluator,
2104 Solver::IndexEvaluator1 tie_breaker) {
2105 Solver::VariableIndexSelector choose_variable =
2106 BaseAssignVariables::MakeVariableSelector(this, vars, var_str);
2107 CheapestValueSelector* value_selector = RevAlloc(new CheapestValueSelector(
2108 std::move(value_evaluator), std::move(tie_breaker)));
2109 Solver::VariableValueSelector select_value =
2110 [value_selector](const IntVar* var, int64_t id) {
2111 return value_selector->Select(var, id);
2112 };
2113 return BaseAssignVariables::MakePhase(this, vars, choose_variable,
2114 select_value, "CheapestValue",
2115 BaseAssignVariables::ASSIGN);
2116}
2117
2118DecisionBuilder* Solver::MakePhase(const std::vector<IntVar*>& vars,
2119 Solver::IndexEvaluator1 var_evaluator,
2120 Solver::IndexEvaluator2 value_evaluator,
2121 Solver::IndexEvaluator1 tie_breaker) {
2122 CheapestVarSelector* const var_selector =
2123 RevAlloc(new CheapestVarSelector(std::move(var_evaluator)));
2124 Solver::VariableIndexSelector choose_variable =
2125 [var_selector](Solver* solver, const std::vector<IntVar*>& vars,
2126 int first_unbound, int last_unbound) {
2127 return var_selector->Choose(solver, vars, first_unbound, last_unbound);
2128 };
2129 CheapestValueSelector* value_selector = RevAlloc(new CheapestValueSelector(
2130 std::move(value_evaluator), std::move(tie_breaker)));
2131 Solver::VariableValueSelector select_value =
2132 [value_selector](const IntVar* var, int64_t id) {
2133 return value_selector->Select(var, id);
2134 };
2135 return BaseAssignVariables::MakePhase(this, vars, choose_variable,
2136 select_value, "CheapestValue",
2137 BaseAssignVariables::ASSIGN);
2138}
2139
2140DecisionBuilder* Solver::MakePhase(const std::vector<IntVar*>& vars,
2143 return MakePhase(vars, std::move(eval), nullptr, str);
2144}
2145
2146DecisionBuilder* Solver::MakePhase(const std::vector<IntVar*>& vars,
2148 Solver::IndexEvaluator1 tie_breaker,
2150 BaseVariableAssignmentSelector* selector = nullptr;
2151 switch (str) {
2153 // TODO(user): support tie breaker
2154 selector = RevAlloc(new StaticEvaluatorSelector(this, vars, eval));
2155 break;
2156 }
2158 selector = RevAlloc(new DynamicEvaluatorSelector(this, vars, eval,
2159 std::move(tie_breaker)));
2160 break;
2161 }
2162 }
2163 return RevAlloc(
2164 new BaseAssignVariables(selector, BaseAssignVariables::ASSIGN));
2165}
2166
2167// ----- AssignAllVariablesFromAssignment decision builder -----
2168
2169namespace {
2170class AssignVariablesFromAssignment : public DecisionBuilder {
2171 public:
2172 AssignVariablesFromAssignment(const Assignment* const assignment,
2173 DecisionBuilder* const db,
2174 const std::vector<IntVar*>& vars)
2175 : assignment_(assignment), db_(db), vars_(vars), iter_(0) {}
2176
2177 ~AssignVariablesFromAssignment() override {}
2178
2179 Decision* Next(Solver* const s) override {
2180 if (iter_ < vars_.size()) {
2181 IntVar* const var = vars_[iter_++];
2182 return s->RevAlloc(
2183 new AssignOneVariableValue(var, assignment_->Value(var)));
2184 } else {
2185 return db_->Next(s);
2186 }
2187 }
2188
2189 void Accept(ModelVisitor* const visitor) const override {
2190 visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension);
2191 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
2192 vars_);
2193 visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension);
2194 }
2195
2196 private:
2197 const Assignment* const assignment_;
2198 DecisionBuilder* const db_;
2199 const std::vector<IntVar*> vars_;
2200 int iter_;
2201};
2202} // namespace
2203
2205 Assignment* const assignment, DecisionBuilder* const db,
2206 const std::vector<IntVar*>& vars) {
2207 return RevAlloc(new AssignVariablesFromAssignment(assignment, db, vars));
2208}
2209
2210// ---------- Solution Collectors -----------
2211
2212// ----- Base Class -----
2213
2215 const Assignment* const assignment)
2216 : SearchMonitor(solver),
2217 prototype_(assignment == nullptr ? nullptr : new Assignment(assignment)) {
2218}
2219
2221 : SearchMonitor(solver), prototype_(new Assignment(solver)) {}
2222
2224 for (auto& data : solution_data_) {
2225 delete data.solution;
2226 }
2228}
2229
2231 if (prototype_ != nullptr) {
2232 prototype_->Add(var);
2233 }
2234}
2235
2236void SolutionCollector::Add(const std::vector<IntVar*>& vars) {
2237 if (prototype_ != nullptr) {
2238 prototype_->Add(vars);
2239 }
2240}
2241
2243 if (prototype_ != nullptr) {
2244 prototype_->Add(var);
2245 }
2246}
2247
2248void SolutionCollector::Add(const std::vector<IntervalVar*>& vars) {
2249 if (prototype_ != nullptr) {
2250 prototype_->Add(vars);
2251 }
2252}
2253
2255 if (prototype_ != nullptr) {
2256 prototype_->Add(var);
2257 }
2258}
2259
2260void SolutionCollector::Add(const std::vector<SequenceVar*>& vars) {
2261 if (prototype_ != nullptr) {
2262 prototype_->Add(vars);
2263 }
2264}
2265
2267 if (prototype_ != nullptr && objective != nullptr) {
2268 prototype_->AddObjective(objective);
2269 }
2270}
2271
2273 for (auto& data : solution_data_) {
2274 delete data.solution;
2275 }
2277 solution_data_.clear();
2278 recycle_solutions_.clear();
2279}
2280
2283}
2284
2286 if (!solution_data_.empty()) {
2287 FreeSolution(solution_data_.back().solution);
2288 solution_data_.pop_back();
2289 }
2290}
2291
2294 Assignment* solution = nullptr;
2295 if (prototype_ != nullptr) {
2296 if (!recycle_solutions_.empty()) {
2298 DCHECK(solution != nullptr);
2299 recycle_solutions_.pop_back();
2300 } else {
2301 solution = new Assignment(prototype_.get());
2302 }
2303 solution->Store();
2304 }
2305 SolutionData data;
2306 data.solution = solution;
2307 data.time = solver()->wall_time();
2308 data.branches = solver()->branches();
2309 data.failures = solver()->failures();
2310 if (solution != nullptr) {
2312 } else {
2313 data.objective_value = 0;
2314 }
2315 return data;
2316}
2317
2319 if (solution != nullptr) {
2320 recycle_solutions_.push_back(solution);
2321 }
2322}
2323
2325 CHECK_GE(n, 0) << "wrong index in solution getter";
2326 CHECK_LT(n, solution_data_.size()) << "wrong index in solution getter";
2327}
2328
2330 check_index(n);
2331 return solution_data_[n].solution;
2332}
2333
2335
2336int64_t SolutionCollector::wall_time(int n) const {
2337 check_index(n);
2338 return solution_data_[n].time;
2339}
2340
2341int64_t SolutionCollector::branches(int n) const {
2342 check_index(n);
2343 return solution_data_[n].branches;
2344}
2345
2346int64_t SolutionCollector::failures(int n) const {
2347 check_index(n);
2348 return solution_data_[n].failures;
2349}
2350
2352 check_index(n);
2353 return solution_data_[n].objective_value;
2354}
2355
2356int64_t SolutionCollector::Value(int n, IntVar* const var) const {
2357 return solution(n)->Value(var);
2358}
2359
2360int64_t SolutionCollector::StartValue(int n, IntervalVar* const var) const {
2361 return solution(n)->StartValue(var);
2362}
2363
2365 return solution(n)->DurationValue(var);
2366}
2367
2368int64_t SolutionCollector::EndValue(int n, IntervalVar* const var) const {
2369 return solution(n)->EndValue(var);
2370}
2371
2373 return solution(n)->PerformedValue(var);
2374}
2375
2377 int n, SequenceVar* const var) const {
2378 return solution(n)->ForwardSequence(var);
2379}
2380
2382 int n, SequenceVar* const var) const {
2383 return solution(n)->BackwardSequence(var);
2384}
2385
2386const std::vector<int>& SolutionCollector::Unperformed(
2387 int n, SequenceVar* const var) const {
2388 return solution(n)->Unperformed(var);
2389}
2390
2391namespace {
2392// ----- First Solution Collector -----
2393
2394// Collect first solution, useful when looking satisfaction problems
2395class FirstSolutionCollector : public SolutionCollector {
2396 public:
2397 FirstSolutionCollector(Solver* const s, const Assignment* const a);
2398 explicit FirstSolutionCollector(Solver* const s);
2399 ~FirstSolutionCollector() override;
2400 void EnterSearch() override;
2401 bool AtSolution() override;
2402 std::string DebugString() const override;
2403
2404 private:
2405 bool done_;
2406};
2407
2408FirstSolutionCollector::FirstSolutionCollector(Solver* const s,
2409 const Assignment* const a)
2410 : SolutionCollector(s, a), done_(false) {}
2411
2412FirstSolutionCollector::FirstSolutionCollector(Solver* const s)
2413 : SolutionCollector(s), done_(false) {}
2414
2415FirstSolutionCollector::~FirstSolutionCollector() {}
2416
2417void FirstSolutionCollector::EnterSearch() {
2419 done_ = false;
2420}
2421
2422bool FirstSolutionCollector::AtSolution() {
2423 if (!done_) {
2424 PushSolution();
2425 done_ = true;
2426 }
2427 return false;
2428}
2429
2430std::string FirstSolutionCollector::DebugString() const {
2431 if (prototype_ == nullptr) {
2432 return "FirstSolutionCollector()";
2433 } else {
2434 return "FirstSolutionCollector(" + prototype_->DebugString() + ")";
2435 }
2436}
2437} // namespace
2438
2440 const Assignment* const assignment) {
2441 return RevAlloc(new FirstSolutionCollector(this, assignment));
2442}
2443
2445 return RevAlloc(new FirstSolutionCollector(this));
2446}
2447
2448// ----- Last Solution Collector -----
2449
2450// Collect last solution, useful when optimizing
2451namespace {
2452class LastSolutionCollector : public SolutionCollector {
2453 public:
2454 LastSolutionCollector(Solver* const s, const Assignment* const a);
2455 explicit LastSolutionCollector(Solver* const s);
2456 ~LastSolutionCollector() override;
2457 bool AtSolution() override;
2458 std::string DebugString() const override;
2459};
2460
2461LastSolutionCollector::LastSolutionCollector(Solver* const s,
2462 const Assignment* const a)
2463 : SolutionCollector(s, a) {}
2464
2465LastSolutionCollector::LastSolutionCollector(Solver* const s)
2466 : SolutionCollector(s) {}
2467
2468LastSolutionCollector::~LastSolutionCollector() {}
2469
2470bool LastSolutionCollector::AtSolution() {
2471 PopSolution();
2472 PushSolution();
2473 return true;
2474}
2475
2476std::string LastSolutionCollector::DebugString() const {
2477 if (prototype_ == nullptr) {
2478 return "LastSolutionCollector()";
2479 } else {
2480 return "LastSolutionCollector(" + prototype_->DebugString() + ")";
2481 }
2482}
2483} // namespace
2484
2486 const Assignment* const assignment) {
2487 return RevAlloc(new LastSolutionCollector(this, assignment));
2488}
2489
2491 return RevAlloc(new LastSolutionCollector(this));
2492}
2493
2494// ----- Best Solution Collector -----
2495
2496namespace {
2497class BestValueSolutionCollector : public SolutionCollector {
2498 public:
2499 BestValueSolutionCollector(Solver* const s, const Assignment* const a,
2500 bool maximize);
2501 BestValueSolutionCollector(Solver* const s, bool maximize);
2502 ~BestValueSolutionCollector() override {}
2503 void EnterSearch() override;
2504 bool AtSolution() override;
2505 std::string DebugString() const override;
2506
2507 public:
2508 const bool maximize_;
2509 int64_t best_;
2510};
2511
2512BestValueSolutionCollector::BestValueSolutionCollector(
2513 Solver* const s, const Assignment* const a, bool maximize)
2514 : SolutionCollector(s, a),
2515 maximize_(maximize),
2516 best_(maximize ? std::numeric_limits<int64_t>::min()
2517 : std::numeric_limits<int64_t>::max()) {}
2518
2519BestValueSolutionCollector::BestValueSolutionCollector(Solver* const s,
2520 bool maximize)
2521 : SolutionCollector(s),
2522 maximize_(maximize),
2523 best_(maximize ? std::numeric_limits<int64_t>::min()
2524 : std::numeric_limits<int64_t>::max()) {}
2525
2526void BestValueSolutionCollector::EnterSearch() {
2527 SolutionCollector::EnterSearch();
2529 : std::numeric_limits<int64_t>::max();
2530}
2531
2532bool BestValueSolutionCollector::AtSolution() {
2533 if (prototype_ != nullptr) {
2534 const IntVar* objective = prototype_->Objective();
2535 if (objective != nullptr) {
2536 if (maximize_ && (solution_count() == 0 || objective->Max() > best_)) {
2537 PopSolution();
2538 PushSolution();
2539 best_ = objective->Max();
2540 } else if (!maximize_ &&
2541 (solution_count() == 0 || objective->Min() < best_)) {
2542 PopSolution();
2543 PushSolution();
2544 best_ = objective->Min();
2545 }
2546 }
2547 }
2548 return true;
2549}
2550
2551std::string BestValueSolutionCollector::DebugString() const {
2552 if (prototype_ == nullptr) {
2553 return "BestValueSolutionCollector()";
2554 } else {
2555 return "BestValueSolutionCollector(" + prototype_->DebugString() + ")";
2556 }
2557}
2558} // namespace
2559
2560SolutionCollector* Solver::MakeBestValueSolutionCollector(
2561 const Assignment* const assignment, bool maximize) {
2562 return RevAlloc(new BestValueSolutionCollector(this, assignment, maximize));
2563}
2564
2565SolutionCollector* Solver::MakeBestValueSolutionCollector(bool maximize) {
2566 return RevAlloc(new BestValueSolutionCollector(this, maximize));
2567}
2568
2569// ----- N Best Solution Collector -----
2570
2571namespace {
2572class NBestValueSolutionCollector : public SolutionCollector {
2573 public:
2574 NBestValueSolutionCollector(Solver* const solver,
2575 const Assignment* const assignment,
2576 int solution_count, bool maximize);
2577 NBestValueSolutionCollector(Solver* const solver, int solution_count,
2578 bool maximize);
2579 ~NBestValueSolutionCollector() override { Clear(); }
2580 void EnterSearch() override;
2581 void ExitSearch() override;
2582 bool AtSolution() override;
2583 std::string DebugString() const override;
2584
2585 public:
2586 void Clear();
2587
2588 const bool maximize_;
2589 std::priority_queue<std::pair<int64_t, SolutionData>> solutions_pq_;
2591};
2592
2593NBestValueSolutionCollector::NBestValueSolutionCollector(
2594 Solver* const solver, const Assignment* const assignment,
2595 int solution_count, bool maximize)
2596 : SolutionCollector(solver, assignment),
2597 maximize_(maximize),
2598 solution_count_(solution_count) {}
2599
2600NBestValueSolutionCollector::NBestValueSolutionCollector(Solver* const solver,
2601 int solution_count,
2602 bool maximize)
2603 : SolutionCollector(solver),
2604 maximize_(maximize),
2605 solution_count_(solution_count) {}
2606
2607void NBestValueSolutionCollector::EnterSearch() {
2608 SolutionCollector::EnterSearch();
2609 // TODO(user): Remove this when fast local search works with
2610 // multiple solutions collected.
2611 if (solution_count_ > 1) {
2612 solver()->SetUseFastLocalSearch(false);
2613 }
2614 Clear();
2615}
2616
2617void NBestValueSolutionCollector::ExitSearch() {
2618 while (!solutions_pq_.empty()) {
2619 Push(solutions_pq_.top().second);
2620 solutions_pq_.pop();
2621 }
2622}
2623
2624bool NBestValueSolutionCollector::AtSolution() {
2625 if (prototype_ != nullptr) {
2626 const IntVar* objective = prototype_->Objective();
2627 if (objective != nullptr) {
2628 const int64_t objective_value =
2629 maximize_ ? CapSub(0, objective->Max()) : objective->Min();
2630 if (solutions_pq_.size() < solution_count_) {
2631 solutions_pq_.push(
2632 {objective_value, BuildSolutionDataForCurrentState()});
2633 } else if (!solutions_pq_.empty()) {
2634 const auto& top = solutions_pq_.top();
2635 if (top.first > objective_value) {
2636 FreeSolution(solutions_pq_.top().second.solution);
2637 solutions_pq_.pop();
2638 solutions_pq_.push(
2639 {objective_value, BuildSolutionDataForCurrentState()});
2640 }
2641 }
2642 }
2643 }
2644 return true;
2645}
2646
2647std::string NBestValueSolutionCollector::DebugString() const {
2648 if (prototype_ == nullptr) {
2649 return "NBestValueSolutionCollector()";
2650 } else {
2651 return "NBestValueSolutionCollector(" + prototype_->DebugString() + ")";
2652 }
2653}
2654
2655void NBestValueSolutionCollector::Clear() {
2656 while (!solutions_pq_.empty()) {
2657 delete solutions_pq_.top().second.solution;
2658 solutions_pq_.pop();
2659 }
2660}
2661
2662} // namespace
2663
2664SolutionCollector* Solver::MakeNBestValueSolutionCollector(
2665 const Assignment* const assignment, int solution_count, bool maximize) {
2666 if (solution_count == 1) {
2667 return MakeBestValueSolutionCollector(assignment, maximize);
2668 }
2669 return RevAlloc(new NBestValueSolutionCollector(this, assignment,
2670 solution_count, maximize));
2671}
2672
2673SolutionCollector* Solver::MakeNBestValueSolutionCollector(int solution_count,
2674 bool maximize) {
2675 if (solution_count == 1) {
2676 return MakeBestValueSolutionCollector(maximize);
2677 }
2678 return RevAlloc(
2679 new NBestValueSolutionCollector(this, solution_count, maximize));
2680}
2681
2682// ----- All Solution Collector -----
2683
2684// collect all solutions
2685namespace {
2686class AllSolutionCollector : public SolutionCollector {
2687 public:
2688 AllSolutionCollector(Solver* const s, const Assignment* const a);
2689 explicit AllSolutionCollector(Solver* const s);
2690 ~AllSolutionCollector() override;
2691 bool AtSolution() override;
2692 std::string DebugString() const override;
2693};
2694
2695AllSolutionCollector::AllSolutionCollector(Solver* const s,
2696 const Assignment* const a)
2697 : SolutionCollector(s, a) {}
2698
2699AllSolutionCollector::AllSolutionCollector(Solver* const s)
2700 : SolutionCollector(s) {}
2701
2702AllSolutionCollector::~AllSolutionCollector() {}
2703
2704bool AllSolutionCollector::AtSolution() {
2705 PushSolution();
2706 return true;
2707}
2708
2709std::string AllSolutionCollector::DebugString() const {
2710 if (prototype_ == nullptr) {
2711 return "AllSolutionCollector()";
2712 } else {
2713 return "AllSolutionCollector(" + prototype_->DebugString() + ")";
2714 }
2715}
2716} // namespace
2717
2719 const Assignment* const assignment) {
2720 return RevAlloc(new AllSolutionCollector(this, assignment));
2721}
2722
2724 return RevAlloc(new AllSolutionCollector(this));
2725}
2726
2727// ---------- Objective Management ----------
2728
2729OptimizeVar::OptimizeVar(Solver* const s, bool maximize, IntVar* const a,
2730 int64_t step)
2731 : SearchMonitor(s),
2732 var_(a),
2733 step_(step),
2734 best_(std::numeric_limits<int64_t>::max()),
2735 maximize_(maximize),
2736 found_initial_solution_(false) {
2737 CHECK_GT(step_, 0);
2738 // TODO(user): Store optimization direction in Solver. Besides making the
2739 // code simpler it would also having two monitors optimizing in opposite
2740 // directions.
2741 if (maximize) {
2743 } else {
2745 }
2746}
2747
2749
2752 if (maximize_) {
2754 } else {
2756 }
2757}
2758
2760 if (solver()->SearchDepth() == 0) { // after a restart.
2761 ApplyBound();
2762 }
2763}
2764
2767 if (maximize_) {
2768 var_->SetMin(best_ + step_);
2769 } else {
2770 var_->SetMax(best_ - step_);
2771 }
2772 }
2773}
2774
2776
2778 const int64_t val = var_->Value();
2780 return true;
2781 } else {
2782 // This code should never return false in sequential mode because
2783 // ApplyBound should have been called before. In parallel, this is
2784 // no longer true. That is why we keep it there, just in case.
2785 return (maximize_ && val > best_) || (!maximize_ && val < best_);
2786 }
2787}
2788
2790 int64_t val = var_->Value();
2791 if (maximize_) {
2793 best_ = val;
2794 } else {
2796 best_ = val;
2797 }
2799 return true;
2800}
2801
2803 if (delta != nullptr) {
2804 const bool delta_has_objective = delta->HasObjective();
2805 if (!delta_has_objective) {
2806 delta->AddObjective(var_);
2807 }
2808 if (delta->Objective() == var_) {
2809 const Assignment* const local_search_state =
2811 if (maximize_) {
2812 const int64_t delta_min_objective =
2813 delta_has_objective ? delta->ObjectiveMin()
2815 const int64_t min_objective =
2816 local_search_state->HasObjective()
2817 ? CapAdd(local_search_state->ObjectiveMin(), step_)
2819 delta->SetObjectiveMin(
2820 std::max({var_->Min(), min_objective, delta_min_objective}));
2821
2822 } else {
2823 const int64_t delta_max_objective =
2824 delta_has_objective ? delta->ObjectiveMax()
2826 const int64_t max_objective =
2827 local_search_state->HasObjective()
2828 ? CapSub(local_search_state->ObjectiveMax(), step_)
2830 delta->SetObjectiveMax(
2831 std::min({var_->Max(), max_objective, delta_max_objective}));
2832 }
2833 }
2834 }
2835 return true;
2836}
2837
2838std::string OptimizeVar::Print() const {
2839 return absl::StrFormat("objective value = %d, ", var_->Value());
2840}
2841
2842std::string OptimizeVar::DebugString() const {
2843 std::string out;
2844 if (maximize_) {
2845 out = "MaximizeVar(";
2846 } else {
2847 out = "MinimizeVar(";
2848 }
2849 absl::StrAppendFormat(&out, "%s, step = %d, best = %d)", var_->DebugString(),
2850 step_, best_);
2851 return out;
2852}
2853
2854void OptimizeVar::Accept(ModelVisitor* const visitor) const {
2859 var_);
2861}
2862
2863OptimizeVar* Solver::MakeMinimize(IntVar* const v, int64_t step) {
2864 return RevAlloc(new OptimizeVar(this, false, v, step));
2865}
2866
2867OptimizeVar* Solver::MakeMaximize(IntVar* const v, int64_t step) {
2868 return RevAlloc(new OptimizeVar(this, true, v, step));
2869}
2870
2871OptimizeVar* Solver::MakeOptimize(bool maximize, IntVar* const v,
2872 int64_t step) {
2873 return RevAlloc(new OptimizeVar(this, maximize, v, step));
2874}
2875
2876namespace {
2877class WeightedOptimizeVar : public OptimizeVar {
2878 public:
2879 WeightedOptimizeVar(Solver* solver, bool maximize,
2880 const std::vector<IntVar*>& sub_objectives,
2881 const std::vector<int64_t>& weights, int64_t step)
2882 : OptimizeVar(solver, maximize,
2883 solver->MakeScalProd(sub_objectives, weights)->Var(), step),
2884 sub_objectives_(sub_objectives),
2885 weights_(weights) {
2886 CHECK_EQ(sub_objectives.size(), weights.size());
2887 }
2888
2889 ~WeightedOptimizeVar() override {}
2890 std::string Print() const override;
2891
2892 private:
2893 const std::vector<IntVar*> sub_objectives_;
2894 const std::vector<int64_t> weights_;
2895
2896 DISALLOW_COPY_AND_ASSIGN(WeightedOptimizeVar);
2897};
2898
2899std::string WeightedOptimizeVar::Print() const {
2900 std::string result(OptimizeVar::Print());
2901 result.append("\nWeighted Objective:\n");
2902 for (int i = 0; i < sub_objectives_.size(); ++i) {
2903 absl::StrAppendFormat(&result, "Variable %s,\tvalue %d,\tweight %d\n",
2904 sub_objectives_[i]->name(),
2905 sub_objectives_[i]->Value(), weights_[i]);
2906 }
2907 return result;
2908}
2909} // namespace
2910
2912 bool maximize, const std::vector<IntVar*>& sub_objectives,
2913 const std::vector<int64_t>& weights, int64_t step) {
2914 return RevAlloc(
2915 new WeightedOptimizeVar(this, maximize, sub_objectives, weights, step));
2916}
2917
2919 const std::vector<IntVar*>& sub_objectives,
2920 const std::vector<int64_t>& weights, int64_t step) {
2921 return RevAlloc(
2922 new WeightedOptimizeVar(this, false, sub_objectives, weights, step));
2923}
2924
2926 const std::vector<IntVar*>& sub_objectives,
2927 const std::vector<int64_t>& weights, int64_t step) {
2928 return RevAlloc(
2929 new WeightedOptimizeVar(this, true, sub_objectives, weights, step));
2930}
2931
2933 bool maximize, const std::vector<IntVar*>& sub_objectives,
2934 const std::vector<int>& weights, int64_t step) {
2935 return MakeWeightedOptimize(maximize, sub_objectives, ToInt64Vector(weights),
2936 step);
2937}
2938
2940 const std::vector<IntVar*>& sub_objectives, const std::vector<int>& weights,
2941 int64_t step) {
2942 return MakeWeightedMinimize(sub_objectives, ToInt64Vector(weights), step);
2943}
2944
2946 const std::vector<IntVar*>& sub_objectives, const std::vector<int>& weights,
2947 int64_t step) {
2948 return MakeWeightedMaximize(sub_objectives, ToInt64Vector(weights), step);
2949}
2950
2951// ---------- Metaheuristics ---------
2952
2953namespace {
2954class Metaheuristic : public SearchMonitor {
2955 public:
2956 Metaheuristic(Solver* const solver, bool maximize, IntVar* objective,
2957 int64_t step);
2958 ~Metaheuristic() override {}
2959
2960 bool AtSolution() override;
2961 void EnterSearch() override;
2962 void RefuteDecision(Decision* const d) override;
2963 bool AcceptDelta(Assignment* delta, Assignment* deltadelta) override;
2964
2965 protected:
2966 IntVar* const objective_;
2967 int64_t step_;
2968 int64_t current_;
2969 int64_t best_;
2970 bool maximize_;
2971};
2972
2973Metaheuristic::Metaheuristic(Solver* const solver, bool maximize,
2974 IntVar* objective, int64_t step)
2975 : SearchMonitor(solver),
2976 objective_(objective),
2977 step_(step),
2978 current_(std::numeric_limits<int64_t>::max()),
2979 best_(std::numeric_limits<int64_t>::max()),
2980 maximize_(maximize) {}
2981
2982bool Metaheuristic::AtSolution() {
2983 current_ = objective_->Value();
2984 if (maximize_) {
2986 } else {
2988 }
2989 return true;
2990}
2991
2992void Metaheuristic::EnterSearch() {
2993 // TODO(user): Remove this when fast local search works with
2994 // metaheuristics.
2995 solver()->SetUseFastLocalSearch(false);
2996 if (maximize_) {
2997 best_ = objective_->Min();
2999 } else {
3000 best_ = objective_->Max();
3002 }
3003}
3004
3005void Metaheuristic::RefuteDecision(Decision* d) {
3006 if (maximize_) {
3007 if (objective_->Max() < best_ + step_) {
3008 solver()->Fail();
3009 }
3010 } else if (objective_->Min() > best_ - step_) {
3011 solver()->Fail();
3012 }
3013}
3014
3015bool Metaheuristic::AcceptDelta(Assignment* delta, Assignment* deltadelta) {
3016 if (delta != nullptr) {
3017 if (!delta->HasObjective()) {
3018 delta->AddObjective(objective_);
3019 }
3020 if (delta->Objective() == objective_) {
3021 if (maximize_) {
3022 delta->SetObjectiveMin(
3023 std::max(objective_->Min(), delta->ObjectiveMin()));
3024 } else {
3025 delta->SetObjectiveMax(
3026 std::min(objective_->Max(), delta->ObjectiveMax()));
3027 }
3028 }
3029 }
3030 return true;
3031}
3032
3033// ---------- Tabu Search ----------
3034
3035class TabuSearch : public Metaheuristic {
3036 public:
3037 TabuSearch(Solver* const s, bool maximize, IntVar* objective, int64_t step,
3038 const std::vector<IntVar*>& vars, int64_t keep_tenure,
3039 int64_t forbid_tenure, double tabu_factor);
3040 ~TabuSearch() override {}
3041 void EnterSearch() override;
3042 void ApplyDecision(Decision* d) override;
3043 bool AtSolution() override;
3044 bool LocalOptimum() override;
3045 void AcceptNeighbor() override;
3046 std::string DebugString() const override { return "Tabu Search"; }
3047
3048 protected:
3049 struct VarValue {
3050 VarValue(IntVar* const var, int64_t value, int64_t stamp)
3051 : var_(var), value_(value), stamp_(stamp) {}
3052 IntVar* const var_;
3053 const int64_t value_;
3054 const int64_t stamp_;
3055 };
3056 typedef std::list<VarValue> TabuList;
3057
3058 virtual std::vector<IntVar*> CreateTabuVars();
3059 const TabuList& forbid_tabu_list() { return forbid_tabu_list_; }
3060
3061 private:
3062 void AgeList(int64_t tenure, TabuList* list);
3063 void AgeLists();
3064
3065 const std::vector<IntVar*> vars_;
3066 Assignment assignment_;
3067 int64_t last_;
3068 TabuList keep_tabu_list_;
3069 int64_t keep_tenure_;
3070 TabuList forbid_tabu_list_;
3071 int64_t forbid_tenure_;
3072 double tabu_factor_;
3073 int64_t stamp_;
3074 bool found_initial_solution_;
3075
3076 DISALLOW_COPY_AND_ASSIGN(TabuSearch);
3077};
3078
3079TabuSearch::TabuSearch(Solver* const s, bool maximize, IntVar* objective,
3080 int64_t step, const std::vector<IntVar*>& vars,
3081 int64_t keep_tenure, int64_t forbid_tenure,
3082 double tabu_factor)
3083 : Metaheuristic(s, maximize, objective, step),
3084 vars_(vars),
3085 assignment_(s),
3086 last_(std::numeric_limits<int64_t>::max()),
3087 keep_tenure_(keep_tenure),
3088 forbid_tenure_(forbid_tenure),
3089 tabu_factor_(tabu_factor),
3090 stamp_(0),
3091 found_initial_solution_(false) {
3092 assignment_.Add(vars_);
3093}
3094
3095void TabuSearch::EnterSearch() {
3096 Metaheuristic::EnterSearch();
3097 found_initial_solution_ = false;
3098}
3099
3100void TabuSearch::ApplyDecision(Decision* const d) {
3101 Solver* const s = solver();
3102 if (d == s->balancing_decision()) {
3103 return;
3104 }
3105 // Aspiration criterion
3106 // Accept a neighbor if it improves the best solution found so far
3107 IntVar* aspiration = s->MakeBoolVar();
3108 if (maximize_) {
3109 s->AddConstraint(s->MakeIsGreaterOrEqualCstCt(
3110 objective_, CapAdd(best_, step_), aspiration));
3111 } else {
3112 s->AddConstraint(s->MakeIsLessOrEqualCstCt(objective_, CapSub(best_, step_),
3113 aspiration));
3114 }
3115
3116 IntVar* tabu_var = nullptr;
3117 {
3118 // Creating the vector in a scope to make sure it gets deleted before
3119 // adding further constraints which could fail and lead to a leak.
3120 const std::vector<IntVar*> tabu_vars = CreateTabuVars();
3121 if (!tabu_vars.empty()) {
3122 tabu_var = s->MakeIsGreaterOrEqualCstVar(s->MakeSum(tabu_vars)->Var(),
3123 tabu_vars.size() * tabu_factor_);
3124 }
3125 }
3126
3127 if (tabu_var != nullptr) {
3128 s->AddConstraint(
3129 s->MakeGreaterOrEqual(s->MakeSum(aspiration, tabu_var), int64_t{1}));
3130 }
3131
3132 // Go downhill to the next local optimum
3133 if (maximize_) {
3135 ? current_ + step_
3136 : current_;
3137 s->AddConstraint(s->MakeGreaterOrEqual(objective_, bound));
3138 } else {
3140 ? current_ - step_
3141 : current_;
3142 s->AddConstraint(s->MakeLessOrEqual(objective_, bound));
3143 }
3144
3145 // Avoid cost plateau's which lead to tabu cycles
3146 if (found_initial_solution_) {
3147 s->AddConstraint(s->MakeNonEquality(objective_, last_));
3148 }
3149}
3150
3151std::vector<IntVar*> TabuSearch::CreateTabuVars() {
3152 Solver* const s = solver();
3153
3154 // Tabu criterion
3155 // A variable in the "keep" list must keep its value, a variable in the
3156 // "forbid" list must not take its value in the list. The tabu criterion is
3157 // softened by the tabu factor which gives the number of violations to
3158 // the tabu criterion which is tolerated; a factor of 1 means no violations
3159 // allowed, a factor of 0 means all violations allowed.
3160 std::vector<IntVar*> tabu_vars;
3161 for (const VarValue& vv : keep_tabu_list_) {
3162 tabu_vars.push_back(s->MakeIsEqualCstVar(vv.var_, vv.value_));
3163 }
3164 for (const VarValue& vv : forbid_tabu_list_) {
3165 tabu_vars.push_back(s->MakeIsDifferentCstVar(vv.var_, vv.value_));
3166 }
3167 return tabu_vars;
3168}
3169
3170bool TabuSearch::AtSolution() {
3171 if (!Metaheuristic::AtSolution()) {
3172 return false;
3173 }
3174 found_initial_solution_ = true;
3175 last_ = current_;
3176
3177 // New solution found: add new assignments to tabu lists; this is only
3178 // done after the first local optimum (stamp_ != 0)
3179 if (0 != stamp_) {
3180 for (int i = 0; i < vars_.size(); ++i) {
3181 IntVar* const var = vars_[i];
3182 const int64_t old_value = assignment_.Value(var);
3183 const int64_t new_value = var->Value();
3184 if (old_value != new_value) {
3185 if (keep_tenure_ > 0) {
3186 VarValue keep_value(var, new_value, stamp_);
3187 keep_tabu_list_.push_front(keep_value);
3188 }
3189 if (forbid_tenure_ > 0) {
3190 VarValue forbid_value(var, old_value, stamp_);
3191 forbid_tabu_list_.push_front(forbid_value);
3192 }
3193 }
3194 }
3195 }
3196 assignment_.Store();
3197
3198 return true;
3199}
3200
3201bool TabuSearch::LocalOptimum() {
3202 AgeLists();
3203 if (maximize_) {
3205 } else {
3207 }
3208 return found_initial_solution_;
3209}
3210
3212 if (0 != stamp_) {
3213 AgeLists();
3214 }
3215}
3216
3217void TabuSearch::AgeList(int64_t tenure, TabuList* list) {
3218 while (!list->empty() && list->back().stamp_ < stamp_ - tenure) {
3219 list->pop_back();
3220 }
3221}
3222
3223void TabuSearch::AgeLists() {
3224 AgeList(keep_tenure_, &keep_tabu_list_);
3225 AgeList(forbid_tenure_, &forbid_tabu_list_);
3226 ++stamp_;
3227}
3228
3229class GenericTabuSearch : public TabuSearch {
3230 public:
3231 GenericTabuSearch(Solver* const s, bool maximize, IntVar* objective,
3232 int64_t step, const std::vector<IntVar*>& vars,
3233 int64_t forbid_tenure)
3234 : TabuSearch(s, maximize, objective, step, vars, 0, forbid_tenure, 1) {}
3235 std::string DebugString() const override { return "Generic Tabu Search"; }
3236
3237 protected:
3238 std::vector<IntVar*> CreateTabuVars() override;
3239};
3240
3241std::vector<IntVar*> GenericTabuSearch::CreateTabuVars() {
3242 Solver* const s = solver();
3243
3244 // Tabu criterion
3245 // At least one element of the forbid_tabu_list must change value.
3246 std::vector<IntVar*> forbid_values;
3247 for (const VarValue& vv : forbid_tabu_list()) {
3248 forbid_values.push_back(s->MakeIsDifferentCstVar(vv.var_, vv.value_));
3249 }
3250 std::vector<IntVar*> tabu_vars;
3251 if (!forbid_values.empty()) {
3252 tabu_vars.push_back(s->MakeIsGreaterCstVar(s->MakeSum(forbid_values), 0));
3253 }
3254 return tabu_vars;
3255}
3256
3257} // namespace
3258
3259SearchMonitor* Solver::MakeTabuSearch(bool maximize, IntVar* const v,
3260 int64_t step,
3261 const std::vector<IntVar*>& vars,
3262 int64_t keep_tenure,
3263 int64_t forbid_tenure,
3264 double tabu_factor) {
3265 return RevAlloc(new TabuSearch(this, maximize, v, step, vars, keep_tenure,
3266 forbid_tenure, tabu_factor));
3267}
3268
3269SearchMonitor* Solver::MakeGenericTabuSearch(
3270 bool maximize, IntVar* const v, int64_t step,
3271 const std::vector<IntVar*>& tabu_vars, int64_t forbid_tenure) {
3272 return RevAlloc(
3273 new GenericTabuSearch(this, maximize, v, step, tabu_vars, forbid_tenure));
3274}
3275
3276// ---------- Simulated Annealing ----------
3277
3278namespace {
3279class SimulatedAnnealing : public Metaheuristic {
3280 public:
3281 SimulatedAnnealing(Solver* const s, bool maximize, IntVar* objective,
3282 int64_t step, int64_t initial_temperature);
3283 ~SimulatedAnnealing() override {}
3284 void EnterSearch() override;
3285 void ApplyDecision(Decision* d) override;
3286 bool AtSolution() override;
3287 bool LocalOptimum() override;
3288 void AcceptNeighbor() override;
3289 std::string DebugString() const override { return "Simulated Annealing"; }
3290
3291 private:
3292 double Temperature() const;
3293
3294 const int64_t temperature0_;
3295 int64_t iteration_;
3296 std::mt19937 rand_;
3297 bool found_initial_solution_;
3298
3299 DISALLOW_COPY_AND_ASSIGN(SimulatedAnnealing);
3300};
3301
3302SimulatedAnnealing::SimulatedAnnealing(Solver* const s, bool maximize,
3303 IntVar* objective, int64_t step,
3304 int64_t initial_temperature)
3305 : Metaheuristic(s, maximize, objective, step),
3306 temperature0_(initial_temperature),
3307 iteration_(0),
3308 rand_(CpRandomSeed()),
3309 found_initial_solution_(false) {}
3310
3311void SimulatedAnnealing::EnterSearch() {
3312 Metaheuristic::EnterSearch();
3313 found_initial_solution_ = false;
3314}
3315
3316void SimulatedAnnealing::ApplyDecision(Decision* const d) {
3317 Solver* const s = solver();
3318 if (d == s->balancing_decision()) {
3319 return;
3320 }
3321 const double rand_double = absl::Uniform<double>(rand_, 0.0, 1.0);
3322#if defined(_MSC_VER) || defined(__ANDROID__)
3323 const double rand_log2_double = log(rand_double) / log(2.0L);
3324#else
3325 const double rand_log2_double = log2(rand_double);
3326#endif
3327 const int64_t energy_bound = Temperature() * rand_log2_double;
3328 if (maximize_) {
3330 ? current_ + step_ + energy_bound
3331 : current_;
3332 s->AddConstraint(s->MakeGreaterOrEqual(objective_, bound));
3333 } else {
3335 ? current_ - step_ - energy_bound
3336 : current_;
3337 s->AddConstraint(s->MakeLessOrEqual(objective_, bound));
3338 }
3339}
3340
3341bool SimulatedAnnealing::AtSolution() {
3342 if (!Metaheuristic::AtSolution()) {
3343 return false;
3344 }
3345 found_initial_solution_ = true;
3346 return true;
3347}
3348
3349bool SimulatedAnnealing::LocalOptimum() {
3350 if (maximize_) {
3352 } else {
3354 }
3355 ++iteration_;
3356 return found_initial_solution_ && Temperature() > 0;
3357}
3358
3360 if (iteration_ > 0) {
3361 ++iteration_;
3362 }
3363}
3364
3365double SimulatedAnnealing::Temperature() const {
3366 if (iteration_ > 0) {
3367 return (1.0 * temperature0_) / iteration_; // Cauchy annealing
3368 } else {
3369 return 0.;
3370 }
3371}
3372} // namespace
3373
3375 int64_t step,
3376 int64_t initial_temperature) {
3377 return RevAlloc(
3378 new SimulatedAnnealing(this, maximize, v, step, initial_temperature));
3379}
3380
3381// ---------- Guided Local Search ----------
3382
3383typedef std::pair<int64_t, int64_t> Arc;
3384
3385namespace {
3386// Base GLS penalties abstract class. Maintains the penalty frequency for each
3387// (variable, value) arc.
3388class GuidedLocalSearchPenalties {
3389 public:
3390 virtual ~GuidedLocalSearchPenalties() {}
3391 virtual bool HasValues() const = 0;
3392 virtual void Increment(const Arc& arc) = 0;
3393 virtual int64_t Value(const Arc& arc) const = 0;
3394 virtual void Reset() = 0;
3395};
3396
3397// Dense GLS penalties implementation using a matrix to store penalties.
3398class GuidedLocalSearchPenaltiesTable : public GuidedLocalSearchPenalties {
3399 public:
3400 explicit GuidedLocalSearchPenaltiesTable(int size);
3401 ~GuidedLocalSearchPenaltiesTable() override {}
3402 bool HasValues() const override { return has_values_; }
3403 void Increment(const Arc& arc) override;
3404 int64_t Value(const Arc& arc) const override;
3405 void Reset() override;
3406
3407 private:
3408 std::vector<std::vector<int64_t>> penalties_;
3409 bool has_values_;
3410};
3411
3412GuidedLocalSearchPenaltiesTable::GuidedLocalSearchPenaltiesTable(int size)
3413 : penalties_(size), has_values_(false) {}
3414
3415void GuidedLocalSearchPenaltiesTable::Increment(const Arc& arc) {
3416 std::vector<int64_t>& first_penalties = penalties_[arc.first];
3417 const int64_t second = arc.second;
3418 if (second >= first_penalties.size()) {
3419 first_penalties.resize(second + 1, 0);
3420 }
3421 ++first_penalties[second];
3422 has_values_ = true;
3423}
3424
3425void GuidedLocalSearchPenaltiesTable::Reset() {
3426 has_values_ = false;
3427 for (int i = 0; i < penalties_.size(); ++i) {
3428 penalties_[i].clear();
3429 }
3430}
3431
3432int64_t GuidedLocalSearchPenaltiesTable::Value(const Arc& arc) const {
3433 const std::vector<int64_t>& first_penalties = penalties_[arc.first];
3434 const int64_t second = arc.second;
3435 if (second >= first_penalties.size()) {
3436 return 0;
3437 } else {
3438 return first_penalties[second];
3439 }
3440}
3441
3442// Sparse GLS penalties implementation using hash_map to store penalties.
3443class GuidedLocalSearchPenaltiesMap : public GuidedLocalSearchPenalties {
3444 public:
3445 explicit GuidedLocalSearchPenaltiesMap(int size);
3446 ~GuidedLocalSearchPenaltiesMap() override {}
3447 bool HasValues() const override { return (!penalties_.empty()); }
3448 void Increment(const Arc& arc) override;
3449 int64_t Value(const Arc& arc) const override;
3450 void Reset() override;
3451
3452 private:
3453 Bitmap penalized_;
3454 absl::flat_hash_map<Arc, int64_t> penalties_;
3455};
3456
3457GuidedLocalSearchPenaltiesMap::GuidedLocalSearchPenaltiesMap(int size)
3458 : penalized_(size, false) {}
3459
3460void GuidedLocalSearchPenaltiesMap::Increment(const Arc& arc) {
3461 ++penalties_[arc];
3462 penalized_.Set(arc.first, true);
3463}
3464
3465void GuidedLocalSearchPenaltiesMap::Reset() {
3466 penalties_.clear();
3467 penalized_.Clear();
3468}
3469
3470int64_t GuidedLocalSearchPenaltiesMap::Value(const Arc& arc) const {
3471 if (penalized_.Get(arc.first)) {
3472 return gtl::FindWithDefault(penalties_, arc, 0);
3473 }
3474 return 0;
3475}
3476
3477class GuidedLocalSearch : public Metaheuristic {
3478 public:
3479 GuidedLocalSearch(Solver* const s, IntVar* objective, bool maximize,
3480 int64_t step, const std::vector<IntVar*>& vars,
3481 double penalty_factor);
3482 ~GuidedLocalSearch() override {}
3483 bool AcceptDelta(Assignment* delta, Assignment* deltadelta) override;
3484 void ApplyDecision(Decision* d) override;
3485 bool AtSolution() override;
3486 void EnterSearch() override;
3487 bool LocalOptimum() override;
3488 virtual int64_t AssignmentElementPenalty(const Assignment& assignment,
3489 int index) = 0;
3490 virtual int64_t AssignmentPenalty(const Assignment& assignment, int index,
3491 int64_t next) = 0;
3492 virtual bool EvaluateElementValue(const Assignment::IntContainer& container,
3493 int64_t index, int* container_index,
3494 int64_t* penalty) = 0;
3495 virtual IntExpr* MakeElementPenalty(int index) = 0;
3496 std::string DebugString() const override { return "Guided Local Search"; }
3497
3498 protected:
3499 struct Comparator {
3500 bool operator()(const std::pair<Arc, double>& i,
3501 const std::pair<Arc, double>& j) {
3502 return i.second > j.second;
3503 }
3504 };
3505
3506 int64_t Evaluate(const Assignment* delta, int64_t current_penalty,
3507 const int64_t* const out_values, bool cache_delta_values);
3508
3510 Assignment assignment_;
3513 const std::vector<IntVar*> vars_;
3514 absl::flat_hash_map<const IntVar*, int64_t> indices_;
3515 const double penalty_factor_;
3516 std::unique_ptr<GuidedLocalSearchPenalties> penalties_;
3517 std::unique_ptr<int64_t[]> current_penalized_values_;
3518 std::unique_ptr<int64_t[]> delta_cache_;
3520};
3521
3522GuidedLocalSearch::GuidedLocalSearch(Solver* const s, IntVar* objective,
3523 bool maximize, int64_t step,
3524 const std::vector<IntVar*>& vars,
3525 double penalty_factor)
3526 : Metaheuristic(s, maximize, objective, step),
3527 penalized_objective_(nullptr),
3528 assignment_(s),
3531 vars_(vars),
3532 penalty_factor_(penalty_factor),
3533 incremental_(false) {
3534 if (!vars.empty()) {
3535 // TODO(user): Remove scoped_array.
3536 assignment_.Add(vars_);
3537 current_penalized_values_ = absl::make_unique<int64_t[]>(vars_.size());
3538 delta_cache_ = absl::make_unique<int64_t[]>(vars_.size());
3539 memset(current_penalized_values_.get(), 0,
3540 vars_.size() * sizeof(*current_penalized_values_.get()));
3541 }
3542 for (int i = 0; i < vars_.size(); ++i) {
3543 indices_[vars_[i]] = i;
3544 }
3545 if (absl::GetFlag(FLAGS_cp_use_sparse_gls_penalties)) {
3546 penalties_ = absl::make_unique<GuidedLocalSearchPenaltiesMap>(vars_.size());
3547 } else {
3548 penalties_ =
3549 absl::make_unique<GuidedLocalSearchPenaltiesTable>(vars_.size());
3550 }
3551}
3552
3553// Add the following constraint (includes aspiration criterion):
3554// if minimizing,
3555// objective =< Max(current penalized cost - penalized_objective - step,
3556// best solution cost - step)
3557// if maximizing,
3558// objective >= Min(current penalized cost - penalized_objective + step,
3559// best solution cost + step)
3560void GuidedLocalSearch::ApplyDecision(Decision* const d) {
3561 if (d == solver()->balancing_decision()) {
3562 return;
3563 }
3565 if (penalties_->HasValues()) {
3566 // Computing sum of penalties expression.
3567 // Scope needed to avoid potential leak of elements.
3568 {
3569 std::vector<IntVar*> elements;
3570 for (int i = 0; i < vars_.size(); ++i) {
3571 elements.push_back(MakeElementPenalty(i)->Var());
3572 const int64_t penalty = AssignmentElementPenalty(assignment_, i);
3573 current_penalized_values_[i] = penalty;
3574 delta_cache_[i] = penalty;
3577 }
3578 penalized_objective_ = solver()->MakeSum(elements)->Var();
3579 }
3581 incremental_ = false;
3582 if (maximize_) {
3583 IntExpr* min_pen_exp =
3584 solver()->MakeDifference(current_ + step_, penalized_objective_);
3585 IntVar* min_exp = solver()->MakeMin(min_pen_exp, best_ + step_)->Var();
3586 solver()->AddConstraint(
3587 solver()->MakeGreaterOrEqual(objective_, min_exp));
3588 } else {
3589 IntExpr* max_pen_exp =
3590 solver()->MakeDifference(current_ - step_, penalized_objective_);
3591 IntVar* max_exp = solver()->MakeMax(max_pen_exp, best_ - step_)->Var();
3592 solver()->AddConstraint(solver()->MakeLessOrEqual(objective_, max_exp));
3593 }
3594 } else {
3595 penalized_objective_ = nullptr;
3596 if (maximize_) {
3598 ? current_ + step_
3599 : current_;
3600 objective_->SetMin(bound);
3601 } else {
3603 ? current_ - step_
3604 : current_;
3605 objective_->SetMax(bound);
3606 }
3607 }
3608}
3609
3610bool GuidedLocalSearch::AtSolution() {
3611 if (!Metaheuristic::AtSolution()) {
3612 return false;
3613 }
3614 if (penalized_objective_ != nullptr) { // In case no move has been found
3615 current_ += penalized_objective_->Value();
3616 }
3617 assignment_.Store();
3618 return true;
3619}
3620
3621void GuidedLocalSearch::EnterSearch() {
3622 Metaheuristic::EnterSearch();
3623 penalized_objective_ = nullptr;
3626 memset(current_penalized_values_.get(), 0,
3627 vars_.size() * sizeof(*current_penalized_values_.get()));
3628 penalties_->Reset();
3629}
3630
3631// GLS filtering; compute the penalized value corresponding to the delta and
3632// modify objective bound accordingly.
3633bool GuidedLocalSearch::AcceptDelta(Assignment* delta, Assignment* deltadelta) {
3634 if (delta != nullptr || deltadelta != nullptr) {
3635 if (!penalties_->HasValues()) {
3636 return Metaheuristic::AcceptDelta(delta, deltadelta);
3637 }
3638 int64_t penalty = 0;
3639 if (!deltadelta->Empty()) {
3640 if (!incremental_) {
3641 penalty = Evaluate(delta, assignment_penalized_value_,
3642 current_penalized_values_.get(), true);
3643 } else {
3644 penalty = Evaluate(deltadelta, old_penalized_value_, delta_cache_.get(),
3645 true);
3646 }
3647 incremental_ = true;
3648 } else {
3649 if (incremental_) {
3650 for (int i = 0; i < vars_.size(); ++i) {
3652 }
3654 }
3655 incremental_ = false;
3656 penalty = Evaluate(delta, assignment_penalized_value_,
3657 current_penalized_values_.get(), false);
3658 }
3659 old_penalized_value_ = penalty;
3660 if (!delta->HasObjective()) {
3661 delta->AddObjective(objective_);
3662 }
3663 if (delta->Objective() == objective_) {
3664 if (maximize_) {
3665 delta->SetObjectiveMin(
3667 CapAdd(best_, step_)),
3668 delta->ObjectiveMin()));
3669 } else {
3670 delta->SetObjectiveMax(
3672 CapSub(best_, step_)),
3673 delta->ObjectiveMax()));
3674 }
3675 }
3676 }
3677 return true;
3678}
3679
3680int64_t GuidedLocalSearch::Evaluate(const Assignment* delta,
3681 int64_t current_penalty,
3682 const int64_t* const out_values,
3683 bool cache_delta_values) {
3684 int64_t penalty = current_penalty;
3685 const Assignment::IntContainer& container = delta->IntVarContainer();
3686 const int size = container.Size();
3687 for (int i = 0; i < size; ++i) {
3688 const IntVarElement& new_element = container.Element(i);
3689 IntVar* var = new_element.Var();
3690 int64_t index = -1;
3691 if (gtl::FindCopy(indices_, var, &index)) {
3692 penalty = CapSub(penalty, out_values[index]);
3693 int64_t new_penalty = 0;
3694 if (EvaluateElementValue(container, index, &i, &new_penalty)) {
3695 penalty = CapAdd(penalty, new_penalty);
3696 if (cache_delta_values) {
3697 delta_cache_[index] = new_penalty;
3698 }
3699 }
3700 }
3701 }
3702 return penalty;
3703}
3704
3705// Penalize all the most expensive arcs (var, value) according to their utility:
3706// utility(i, j) = cost(i, j) / (1 + penalty(i, j))
3707bool GuidedLocalSearch::LocalOptimum() {
3708 std::vector<std::pair<Arc, double>> utility(vars_.size());
3709 for (int i = 0; i < vars_.size(); ++i) {
3710 if (!assignment_.Bound(vars_[i])) {
3711 // Never synced with a solution, problem infeasible.
3712 return false;
3713 }
3714 const int64_t var_value = assignment_.Value(vars_[i]);
3715 const int64_t value =
3716 (var_value != i) ? AssignmentPenalty(assignment_, i, var_value) : 0;
3717 const Arc arc(i, var_value);
3718 const int64_t penalty = penalties_->Value(arc);
3719 utility[i] = std::pair<Arc, double>(arc, value / (penalty + 1.0));
3720 }
3721 Comparator comparator;
3722 std::sort(utility.begin(), utility.end(), comparator);
3723 int64_t utility_value = utility[0].second;
3724 penalties_->Increment(utility[0].first);
3725 for (int i = 1; i < utility.size() && utility_value == utility[i].second;
3726 ++i) {
3727 penalties_->Increment(utility[i].first);
3728 }
3729 if (maximize_) {
3731 } else {
3733 }
3734 return true;
3735}
3736
3737class BinaryGuidedLocalSearch : public GuidedLocalSearch {
3738 public:
3739 BinaryGuidedLocalSearch(
3740 Solver* const solver, IntVar* const objective,
3741 std::function<int64_t(int64_t, int64_t)> objective_function,
3742 bool maximize, int64_t step, const std::vector<IntVar*>& vars,
3743 double penalty_factor);
3744 ~BinaryGuidedLocalSearch() override {}
3745 IntExpr* MakeElementPenalty(int index) override;
3746 int64_t AssignmentElementPenalty(const Assignment& assignment,
3747 int index) override;
3748 int64_t AssignmentPenalty(const Assignment& assignment, int index,
3749 int64_t next) override;
3750 bool EvaluateElementValue(const Assignment::IntContainer& container,
3751 int64_t index, int* container_index,
3752 int64_t* penalty) override;
3753
3754 private:
3755 int64_t PenalizedValue(int64_t i, int64_t j);
3756 std::function<int64_t(int64_t, int64_t)> objective_function_;
3757};
3758
3759BinaryGuidedLocalSearch::BinaryGuidedLocalSearch(
3760 Solver* const solver, IntVar* const objective,
3761 std::function<int64_t(int64_t, int64_t)> objective_function, bool maximize,
3762 int64_t step, const std::vector<IntVar*>& vars, double penalty_factor)
3763 : GuidedLocalSearch(solver, objective, maximize, step, vars,
3764 penalty_factor),
3765 objective_function_(std::move(objective_function)) {}
3766
3767IntExpr* BinaryGuidedLocalSearch::MakeElementPenalty(int index) {
3768 return solver()->MakeElement(
3769 [this, index](int64_t i) { return PenalizedValue(index, i); },
3770 vars_[index]);
3771}
3772
3773int64_t BinaryGuidedLocalSearch::AssignmentElementPenalty(
3774 const Assignment& assignment, int index) {
3775 return PenalizedValue(index, assignment.Value(vars_[index]));
3776}
3777
3778int64_t BinaryGuidedLocalSearch::AssignmentPenalty(const Assignment& assignment,
3779 int index, int64_t next) {
3780 return objective_function_(index, next);
3781}
3782
3783bool BinaryGuidedLocalSearch::EvaluateElementValue(
3784 const Assignment::IntContainer& container, int64_t index,
3785 int* container_index, int64_t* penalty) {
3786 const IntVarElement& element = container.Element(*container_index);
3787 if (element.Activated()) {
3788 *penalty = PenalizedValue(index, element.Value());
3789 return true;
3790 }
3791 return false;
3792}
3793
3794// Penalized value for (i, j) = penalty_factor_ * penalty(i, j) * cost (i, j)
3795int64_t BinaryGuidedLocalSearch::PenalizedValue(int64_t i, int64_t j) {
3796 const Arc arc(i, j);
3797 const int64_t penalty = penalties_->Value(arc);
3798 if (penalty != 0) { // objective_function_->Run(i, j) can be costly
3799 const double penalized_value_fp =
3800 penalty_factor_ * penalty * objective_function_(i, j);
3801 const int64_t penalized_value =
3803 ? static_cast<int64_t>(penalized_value_fp)
3805 if (maximize_) {
3806 return -penalized_value;
3807 } else {
3808 return penalized_value;
3809 }
3810 } else {
3811 return 0;
3812 }
3813}
3814
3815class TernaryGuidedLocalSearch : public GuidedLocalSearch {
3816 public:
3817 TernaryGuidedLocalSearch(
3818 Solver* const solver, IntVar* const objective,
3819 std::function<int64_t(int64_t, int64_t, int64_t)> objective_function,
3820 bool maximize, int64_t step, const std::vector<IntVar*>& vars,
3821 const std::vector<IntVar*>& secondary_vars, double penalty_factor);
3822 ~TernaryGuidedLocalSearch() override {}
3823 IntExpr* MakeElementPenalty(int index) override;
3824 int64_t AssignmentElementPenalty(const Assignment& assignment,
3825 int index) override;
3826 int64_t AssignmentPenalty(const Assignment& assignment, int index,
3827 int64_t next) override;
3828 bool EvaluateElementValue(const Assignment::IntContainer& container,
3829 int64_t index, int* container_index,
3830 int64_t* penalty) override;
3831
3832 private:
3833 int64_t PenalizedValue(int64_t i, int64_t j, int64_t k);
3834 int64_t GetAssignmentSecondaryValue(const Assignment::IntContainer& container,
3835 int index, int* container_index) const;
3836
3837 const std::vector<IntVar*> secondary_vars_;
3838 std::function<int64_t(int64_t, int64_t, int64_t)> objective_function_;
3839};
3840
3841TernaryGuidedLocalSearch::TernaryGuidedLocalSearch(
3842 Solver* const solver, IntVar* const objective,
3843 std::function<int64_t(int64_t, int64_t, int64_t)> objective_function,
3844 bool maximize, int64_t step, const std::vector<IntVar*>& vars,
3845 const std::vector<IntVar*>& secondary_vars, double penalty_factor)
3846 : GuidedLocalSearch(solver, objective, maximize, step, vars,
3847 penalty_factor),
3848 secondary_vars_(secondary_vars),
3849 objective_function_(std::move(objective_function)) {
3850 if (!secondary_vars.empty()) {
3851 assignment_.Add(secondary_vars);
3852 }
3853}
3854
3855IntExpr* TernaryGuidedLocalSearch::MakeElementPenalty(int index) {
3856 return solver()->MakeElement(
3857 [this, index](int64_t i, int64_t j) {
3858 return PenalizedValue(index, i, j);
3859 },
3860 vars_[index], secondary_vars_[index]);
3861}
3862
3863int64_t TernaryGuidedLocalSearch::AssignmentElementPenalty(
3864 const Assignment& assignment, int index) {
3865 return PenalizedValue(index, assignment.Value(vars_[index]),
3866 assignment.Value(secondary_vars_[index]));
3867}
3868
3869int64_t TernaryGuidedLocalSearch::AssignmentPenalty(
3870 const Assignment& assignment, int index, int64_t next) {
3871 return objective_function_(index, next,
3872 assignment.Value(secondary_vars_[index]));
3873}
3874
3875bool TernaryGuidedLocalSearch::EvaluateElementValue(
3876 const Assignment::IntContainer& container, int64_t index,
3877 int* container_index, int64_t* penalty) {
3878 const IntVarElement& element = container.Element(*container_index);
3879 if (element.Activated()) {
3880 *penalty = PenalizedValue(
3881 index, element.Value(),
3882 GetAssignmentSecondaryValue(container, index, container_index));
3883 return true;
3884 }
3885 return false;
3886}
3887
3888// Penalized value for (i, j) = penalty_factor_ * penalty(i, j) * cost (i, j)
3889int64_t TernaryGuidedLocalSearch::PenalizedValue(int64_t i, int64_t j,
3890 int64_t k) {
3891 const Arc arc(i, j);
3892 const int64_t penalty = penalties_->Value(arc);
3893 if (penalty != 0) { // objective_function_(i, j, k) can be costly
3894 const double penalized_value_fp =
3895 penalty_factor_ * penalty * objective_function_(i, j, k);
3896 const int64_t penalized_value =
3898 ? static_cast<int64_t>(penalized_value_fp)
3900 if (maximize_) {
3901 return -penalized_value;
3902 } else {
3903 return penalized_value;
3904 }
3905 } else {
3906 return 0;
3907 }
3908}
3909
3910int64_t TernaryGuidedLocalSearch::GetAssignmentSecondaryValue(
3911 const Assignment::IntContainer& container, int index,
3912 int* container_index) const {
3913 const IntVar* secondary_var = secondary_vars_[index];
3914 int hint_index = *container_index + 1;
3915 if (hint_index > 0 && hint_index < container.Size() &&
3916 secondary_var == container.Element(hint_index).Var()) {
3917 *container_index = hint_index;
3918 return container.Element(hint_index).Value();
3919 } else {
3920 return container.Element(secondary_var).Value();
3921 }
3922}
3923} // namespace
3924
3925SearchMonitor* Solver::MakeGuidedLocalSearch(
3926 bool maximize, IntVar* const objective,
3927 Solver::IndexEvaluator2 objective_function, int64_t step,
3928 const std::vector<IntVar*>& vars, double penalty_factor) {
3929 return RevAlloc(new BinaryGuidedLocalSearch(
3930 this, objective, std::move(objective_function), maximize, step, vars,
3931 penalty_factor));
3932}
3933
3934SearchMonitor* Solver::MakeGuidedLocalSearch(
3935 bool maximize, IntVar* const objective,
3936 Solver::IndexEvaluator3 objective_function, int64_t step,
3937 const std::vector<IntVar*>& vars,
3938 const std::vector<IntVar*>& secondary_vars, double penalty_factor) {
3939 return RevAlloc(new TernaryGuidedLocalSearch(
3940 this, objective, std::move(objective_function), maximize, step, vars,
3941 secondary_vars, penalty_factor));
3942}
3943
3944// ---------- Search Limits ----------
3945
3946// ----- Base Class -----
3947
3948SearchLimit::~SearchLimit() {}
3949
3950void SearchLimit::EnterSearch() {
3951 crossed_ = false;
3952 Init();
3953}
3954
3955void SearchLimit::BeginNextDecision(DecisionBuilder* const b) {
3956 PeriodicCheck();
3957 TopPeriodicCheck();
3958}
3959
3960void SearchLimit::RefuteDecision(Decision* const d) {
3961 PeriodicCheck();
3962 TopPeriodicCheck();
3963}
3964
3965void SearchLimit::PeriodicCheck() {
3966 if (crossed_ || Check()) {
3967 crossed_ = true;
3968 solver()->Fail();
3969 }
3970}
3971
3972void SearchLimit::TopPeriodicCheck() {
3973 if (solver()->TopLevelSearch() != solver()->ActiveSearch()) {
3974 solver()->TopPeriodicCheck();
3975 }
3976}
3977
3978// ----- Regular Limit -----
3979
3980RegularLimit::RegularLimit(Solver* const s, absl::Duration time,
3981 int64_t branches, int64_t failures,
3982 int64_t solutions, bool smart_time_check,
3983 bool cumulative)
3984 : SearchLimit(s),
3985 duration_limit_(time),
3986 solver_time_at_limit_start_(s->Now()),
3987 last_time_elapsed_(absl::ZeroDuration()),
3988 check_count_(0),
3989 next_check_(0),
3990 smart_time_check_(smart_time_check),
3991 branches_(branches),
3992 branches_offset_(0),
3993 failures_(failures),
3994 failures_offset_(0),
3995 solutions_(solutions),
3996 solutions_offset_(0),
3997 cumulative_(cumulative) {}
3998
4000
4001void RegularLimit::Copy(const SearchLimit* const limit) {
4002 const RegularLimit* const regular =
4003 reinterpret_cast<const RegularLimit* const>(limit);
4004 duration_limit_ = regular->duration_limit_;
4005 branches_ = regular->branches_;
4006 failures_ = regular->failures_;
4007 solutions_ = regular->solutions_;
4008 smart_time_check_ = regular->smart_time_check_;
4009 cumulative_ = regular->cumulative_;
4010}
4011
4013
4015 Solver* const s = solver();
4016 return s->MakeLimit(wall_time(), branches_, failures_, solutions_,
4017 smart_time_check_);
4018}
4019
4021 Solver* const s = solver();
4022 // Warning limits might be kint64max, do not move the offset to the rhs
4023 return s->branches() - branches_offset_ >= branches_ ||
4024 s->failures() - failures_offset_ >= failures_ || CheckTime() ||
4025 s->solutions() - solutions_offset_ >= solutions_;
4026}
4027
4029 Solver* const s = solver();
4030 int64_t progress = GetPercent(s->branches(), branches_offset_, branches_);
4031 progress = std::max(progress,
4032 GetPercent(s->failures(), failures_offset_, failures_));
4033 progress = std::max(
4034 progress, GetPercent(s->solutions(), solutions_offset_, solutions_));
4035 if (duration_limit() != absl::InfiniteDuration()) {
4036 progress = std::max(progress, (100 * TimeElapsed()) / duration_limit());
4037 }
4038 return progress;
4039}
4040
4042 Solver* const s = solver();
4043 branches_offset_ = s->branches();
4044 failures_offset_ = s->failures();
4045 solver_time_at_limit_start_ = s->Now();
4046 last_time_elapsed_ = absl::ZeroDuration();
4047 solutions_offset_ = s->solutions();
4048 check_count_ = 0;
4049 next_check_ = 0;
4050}
4051
4053 if (cumulative_) {
4054 // Reduce the limits by the amount consumed during this search
4055 Solver* const s = solver();
4056 branches_ -= s->branches() - branches_offset_;
4057 failures_ -= s->failures() - failures_offset_;
4058 duration_limit_ -= s->Now() - solver_time_at_limit_start_;
4059 solutions_ -= s->solutions() - solutions_offset_;
4060 }
4061}
4062
4063void RegularLimit::UpdateLimits(absl::Duration time, int64_t branches,
4064 int64_t failures, int64_t solutions) {
4065 duration_limit_ = time;
4066 branches_ = branches;
4067 failures_ = failures;
4068 solutions_ = solutions;
4069}
4070
4072 Solver* const s = solver();
4073 return s->solutions() + s->unchecked_solutions() - solutions_offset_ >=
4074 solutions_;
4075}
4076
4077std::string RegularLimit::DebugString() const {
4078 return absl::StrFormat(
4079 "RegularLimit(crossed = %i, duration_limit = %s, "
4080 "branches = %d, failures = %d, solutions = %d cumulative = %s",
4081 crossed(), absl::FormatDuration(duration_limit()), branches_, failures_,
4082 solutions_, (cumulative_ ? "true" : "false"));
4083}
4084
4085void RegularLimit::Accept(ModelVisitor* const visitor) const {
4089 branches_);
4091 failures_);
4093 solutions_);
4095 smart_time_check_);
4098}
4099
4100bool RegularLimit::CheckTime() { return TimeElapsed() >= duration_limit(); }
4101
4102absl::Duration RegularLimit::TimeElapsed() {
4103 const int64_t kMaxSkip = 100;
4104 const int64_t kCheckWarmupIterations = 100;
4105 ++check_count_;
4106 if (duration_limit() != absl::InfiniteDuration() &&
4107 next_check_ <= check_count_) {
4108 Solver* const s = solver();
4109 absl::Duration elapsed = s->Now() - solver_time_at_limit_start_;
4110 if (smart_time_check_ && check_count_ > kCheckWarmupIterations &&
4111 elapsed > absl::ZeroDuration()) {
4112 const int64_t estimated_check_count_at_limit = MathUtil::FastInt64Round(
4113 check_count_ * absl::FDivDuration(duration_limit_, elapsed));
4114 next_check_ =
4115 std::min(check_count_ + kMaxSkip, estimated_check_count_at_limit);
4116 }
4117 last_time_elapsed_ = elapsed;
4118 }
4119 return last_time_elapsed_;
4120}
4121
4126 /*smart_time_check=*/false, /*cumulative=*/false);
4127}
4128
4130 return MakeLimit(absl::InfiniteDuration(), branches,
4133 /*smart_time_check=*/false, /*cumulative=*/false);
4134}
4135
4137 return MakeLimit(absl::InfiniteDuration(),
4140 /*smart_time_check=*/false, /*cumulative=*/false);
4141}
4142
4144 return MakeLimit(absl::InfiniteDuration(),
4147 /*smart_time_check=*/false, /*cumulative=*/false);
4148}
4149
4150RegularLimit* Solver::MakeLimit(int64_t time, int64_t branches,
4151 int64_t failures, int64_t solutions,
4152 bool smart_time_check, bool cumulative) {
4153 return MakeLimit(absl::Milliseconds(time), branches, failures, solutions,
4154 smart_time_check, cumulative);
4155}
4156
4157RegularLimit* Solver::MakeLimit(absl::Duration time, int64_t branches,
4158 int64_t failures, int64_t solutions,
4159 bool smart_time_check, bool cumulative) {
4161 smart_time_check, cumulative));
4162}
4163
4166 ? absl::InfiniteDuration()
4167 : absl::Milliseconds(proto.time()),
4168 proto.branches(), proto.failures(), proto.solutions(),
4169 proto.smart_time_check(), proto.cumulative());
4170}
4171
4178 proto.set_smart_time_check(false);
4179 proto.set_cumulative(false);
4180 return proto;
4181}
4182
4183// ----- Improvement Search Limit -----
4184
4186 Solver* const s, IntVar* objective_var, bool maximize,
4187 double objective_scaling_factor, double objective_offset,
4188 double improvement_rate_coefficient,
4189 int improvement_rate_solutions_distance)
4190 : SearchLimit(s),
4191 objective_var_(objective_var),
4192 maximize_(maximize),
4193 objective_scaling_factor_(objective_scaling_factor),
4194 objective_offset_(objective_offset),
4195 improvement_rate_coefficient_(improvement_rate_coefficient),
4196 improvement_rate_solutions_distance_(
4197 improvement_rate_solutions_distance) {
4198 Init();
4199}
4200
4202
4204 best_objective_ = maximize_ ? -std::numeric_limits<double>::infinity()
4205 : std::numeric_limits<double>::infinity();
4206 threshold_ = std::numeric_limits<double>::infinity();
4207 objective_updated_ = false;
4208 gradient_stage_ = true;
4209}
4210
4212 const ImprovementSearchLimit* const improv =
4213 reinterpret_cast<const ImprovementSearchLimit* const>(limit);
4214 objective_var_ = improv->objective_var_;
4215 maximize_ = improv->maximize_;
4216 objective_scaling_factor_ = improv->objective_scaling_factor_;
4217 objective_offset_ = improv->objective_offset_;
4218 improvement_rate_coefficient_ = improv->improvement_rate_coefficient_;
4219 improvement_rate_solutions_distance_ =
4220 improv->improvement_rate_solutions_distance_;
4221 improvements_ = improv->improvements_;
4222 threshold_ = improv->threshold_;
4223 best_objective_ = improv->best_objective_;
4224 objective_updated_ = improv->objective_updated_;
4225 gradient_stage_ = improv->gradient_stage_;
4226}
4227
4229 Solver* const s = solver();
4230 return s->MakeImprovementLimit(
4231 objective_var_, maximize_, objective_scaling_factor_, objective_offset_,
4232 improvement_rate_coefficient_, improvement_rate_solutions_distance_);
4233}
4234
4236 if (!objective_updated_) {
4237 return false;
4238 }
4239 objective_updated_ = false;
4240
4241 if (improvements_.size() <= improvement_rate_solutions_distance_) {
4242 return false;
4243 }
4244
4245 const std::pair<double, int64_t> cur = improvements_.back();
4246 const std::pair<double, int64_t> prev = improvements_.front();
4247 DCHECK_GT(cur.second, prev.second);
4248 double improvement_rate =
4249 std::abs(prev.first - cur.first) / (cur.second - prev.second);
4250 if (gradient_stage_) {
4251 threshold_ = fmin(threshold_, improvement_rate);
4252 } else if (improvement_rate_coefficient_ * improvement_rate < threshold_) {
4253 return true;
4254 }
4255
4256 return false;
4257}
4258
4260 const int64_t new_objective =
4261 objective_var_ != nullptr && objective_var_->Bound()
4262 ? objective_var_->Value()
4263 : (maximize_
4266
4267 const double scaled_new_objective =
4268 objective_scaling_factor_ * (new_objective + objective_offset_);
4269
4270 const bool is_improvement = maximize_
4271 ? scaled_new_objective > best_objective_
4272 : scaled_new_objective < best_objective_;
4273
4274 if (gradient_stage_ && !is_improvement) {
4275 gradient_stage_ = false;
4276 // In case we haven't got enough solutions during the first stage, the limit
4277 // never stops the search.
4278 if (threshold_ == std::numeric_limits<double>::infinity()) {
4279 threshold_ = -1;
4280 }
4281 }
4282
4283 if (is_improvement) {
4284 best_objective_ = scaled_new_objective;
4285 objective_updated_ = true;
4286 improvements_.push_back(
4287 std::make_pair(scaled_new_objective, solver()->neighbors()));
4288 // We need to have 'improvement_rate_solutions_distance_' + 1 element in the
4289 // 'improvements_', so the distance between improvements is
4290 // 'improvement_rate_solutions_distance_'.
4291 if (improvements_.size() - 1 > improvement_rate_solutions_distance_) {
4292 improvements_.pop_front();
4293 }
4294 DCHECK_LE(improvements_.size() - 1, improvement_rate_solutions_distance_);
4295 }
4296
4297 return true;
4298}
4299
4301 IntVar* objective_var, bool maximize, double objective_scaling_factor,
4302 double objective_offset, double improvement_rate_coefficient,
4303 int improvement_rate_solutions_distance) {
4305 this, objective_var, maximize, objective_scaling_factor, objective_offset,
4306 improvement_rate_coefficient, improvement_rate_solutions_distance));
4307}
4308
4309// A limit whose Check function is the OR of two underlying limits.
4310namespace {
4311class ORLimit : public SearchLimit {
4312 public:
4313 ORLimit(SearchLimit* limit_1, SearchLimit* limit_2)
4314 : SearchLimit(limit_1->solver()), limit_1_(limit_1), limit_2_(limit_2) {
4315 CHECK(limit_1 != nullptr);
4316 CHECK(limit_2 != nullptr);
4317 CHECK_EQ(limit_1->solver(), limit_2->solver())
4318 << "Illegal arguments: cannot combines limits that belong to different "
4319 << "solvers, because the reversible allocations could delete one and "
4320 << "not the other.";
4321 }
4322
4323 bool Check() override {
4324 // Check being non-const, there may be side effects. So we always call both
4325 // checks.
4326 const bool check_1 = limit_1_->Check();
4327 const bool check_2 = limit_2_->Check();
4328 return check_1 || check_2;
4329 }
4330
4331 void Init() override {
4332 limit_1_->Init();
4333 limit_2_->Init();
4334 }
4335
4336 void Copy(const SearchLimit* const limit) override {
4337 LOG(FATAL) << "Not implemented.";
4338 }
4339
4340 SearchLimit* MakeClone() const override {
4341 // Deep cloning: the underlying limits are cloned, too.
4342 return solver()->MakeLimit(limit_1_->MakeClone(), limit_2_->MakeClone());
4343 }
4344
4345 void EnterSearch() override {
4346 limit_1_->EnterSearch();
4347 limit_2_->EnterSearch();
4348 }
4349 void BeginNextDecision(DecisionBuilder* const b) override {
4350 limit_1_->BeginNextDecision(b);
4351 limit_2_->BeginNextDecision(b);
4352 }
4353 void PeriodicCheck() override {
4354 limit_1_->PeriodicCheck();
4355 limit_2_->PeriodicCheck();
4356 }
4357 void RefuteDecision(Decision* const d) override {
4358 limit_1_->RefuteDecision(d);
4359 limit_2_->RefuteDecision(d);
4360 }
4361 std::string DebugString() const override {
4362 return absl::StrCat("OR limit (", limit_1_->DebugString(), " OR ",
4363 limit_2_->DebugString(), ")");
4364 }
4365
4366 private:
4367 SearchLimit* const limit_1_;
4368 SearchLimit* const limit_2_;
4369};
4370} // namespace
4371
4373 SearchLimit* const limit_2) {
4374 return RevAlloc(new ORLimit(limit_1, limit_2));
4375}
4376
4377namespace {
4378class CustomLimit : public SearchLimit {
4379 public:
4380 CustomLimit(Solver* const s, std::function<bool()> limiter);
4381 bool Check() override;
4382 void Init() override;
4383 void Copy(const SearchLimit* const limit) override;
4384 SearchLimit* MakeClone() const override;
4385
4386 private:
4387 std::function<bool()> limiter_;
4388};
4389
4390CustomLimit::CustomLimit(Solver* const s, std::function<bool()> limiter)
4391 : SearchLimit(s), limiter_(std::move(limiter)) {}
4392
4393bool CustomLimit::Check() {
4394 if (limiter_) return limiter_();
4395 return false;
4396}
4397
4398void CustomLimit::Init() {}
4399
4400void CustomLimit::Copy(const SearchLimit* const limit) {
4401 const CustomLimit* const custom =
4402 reinterpret_cast<const CustomLimit* const>(limit);
4403 limiter_ = custom->limiter_;
4404}
4405
4406SearchLimit* CustomLimit::MakeClone() const {
4407 return solver()->RevAlloc(new CustomLimit(solver(), limiter_));
4408}
4409} // namespace
4410
4411SearchLimit* Solver::MakeCustomLimit(std::function<bool()> limiter) {
4412 return RevAlloc(new CustomLimit(this, std::move(limiter)));
4413}
4414
4415// ---------- SolveOnce ----------
4416
4417namespace {
4418class SolveOnce : public DecisionBuilder {
4419 public:
4420 explicit SolveOnce(DecisionBuilder* const db) : db_(db) {
4421 CHECK(db != nullptr);
4422 }
4423
4424 SolveOnce(DecisionBuilder* const db,
4425 const std::vector<SearchMonitor*>& monitors)
4426 : db_(db), monitors_(monitors) {
4427 CHECK(db != nullptr);
4428 }
4429
4430 ~SolveOnce() override {}
4431
4432 Decision* Next(Solver* s) override {
4433 bool res = s->SolveAndCommit(db_, monitors_);
4434 if (!res) {
4435 s->Fail();
4436 }
4437 return nullptr;
4438 }
4439
4440 std::string DebugString() const override {
4441 return absl::StrFormat("SolveOnce(%s)", db_->DebugString());
4442 }
4443
4444 void Accept(ModelVisitor* const visitor) const override {
4445 db_->Accept(visitor);
4446 }
4447
4448 private:
4449 DecisionBuilder* const db_;
4450 std::vector<SearchMonitor*> monitors_;
4451};
4452} // namespace
4453
4455 return RevAlloc(new SolveOnce(db));
4456}
4457
4459 SearchMonitor* const monitor1) {
4460 std::vector<SearchMonitor*> monitors;
4461 monitors.push_back(monitor1);
4462 return RevAlloc(new SolveOnce(db, monitors));
4463}
4464
4466 SearchMonitor* const monitor1,
4467 SearchMonitor* const monitor2) {
4468 std::vector<SearchMonitor*> monitors;
4469 monitors.push_back(monitor1);
4470 monitors.push_back(monitor2);
4471 return RevAlloc(new SolveOnce(db, monitors));
4472}
4473
4475 SearchMonitor* const monitor1,
4476 SearchMonitor* const monitor2,
4477 SearchMonitor* const monitor3) {
4478 std::vector<SearchMonitor*> monitors;
4479 monitors.push_back(monitor1);
4480 monitors.push_back(monitor2);
4481 monitors.push_back(monitor3);
4482 return RevAlloc(new SolveOnce(db, monitors));
4483}
4484
4486 SearchMonitor* const monitor1,
4487 SearchMonitor* const monitor2,
4488 SearchMonitor* const monitor3,
4489 SearchMonitor* const monitor4) {
4490 std::vector<SearchMonitor*> monitors;
4491 monitors.push_back(monitor1);
4492 monitors.push_back(monitor2);
4493 monitors.push_back(monitor3);
4494 monitors.push_back(monitor4);
4495 return RevAlloc(new SolveOnce(db, monitors));
4496}
4497
4499 DecisionBuilder* const db, const std::vector<SearchMonitor*>& monitors) {
4500 return RevAlloc(new SolveOnce(db, monitors));
4501}
4502
4503// ---------- NestedOptimize ----------
4504
4505namespace {
4506class NestedOptimize : public DecisionBuilder {
4507 public:
4508 NestedOptimize(DecisionBuilder* const db, Assignment* const solution,
4509 bool maximize, int64_t step)
4510 : db_(db),
4511 solution_(solution),
4512 maximize_(maximize),
4513 step_(step),
4514 collector_(nullptr) {
4515 CHECK(db != nullptr);
4516 CHECK(solution != nullptr);
4517 CHECK(solution->HasObjective());
4518 AddMonitors();
4519 }
4520
4521 NestedOptimize(DecisionBuilder* const db, Assignment* const solution,
4522 bool maximize, int64_t step,
4523 const std::vector<SearchMonitor*>& monitors)
4524 : db_(db),
4525 solution_(solution),
4526 maximize_(maximize),
4527 step_(step),
4528 monitors_(monitors),
4529 collector_(nullptr) {
4530 CHECK(db != nullptr);
4531 CHECK(solution != nullptr);
4532 CHECK(solution->HasObjective());
4533 AddMonitors();
4534 }
4535
4536 void AddMonitors() {
4537 Solver* const solver = solution_->solver();
4538 collector_ = solver->MakeLastSolutionCollector(solution_);
4539 monitors_.push_back(collector_);
4540 OptimizeVar* const optimize =
4541 solver->MakeOptimize(maximize_, solution_->Objective(), step_);
4542 monitors_.push_back(optimize);
4543 }
4544
4545 Decision* Next(Solver* solver) override {
4546 solver->Solve(db_, monitors_);
4547 if (collector_->solution_count() == 0) {
4548 solver->Fail();
4549 }
4550 collector_->solution(0)->Restore();
4551 return nullptr;
4552 }
4553
4554 std::string DebugString() const override {
4555 return absl::StrFormat("NestedOptimize(db = %s, maximize = %d, step = %d)",
4556 db_->DebugString(), maximize_, step_);
4557 }
4558
4559 void Accept(ModelVisitor* const visitor) const override {
4560 db_->Accept(visitor);
4561 }
4562
4563 private:
4564 DecisionBuilder* const db_;
4565 Assignment* const solution_;
4566 const bool maximize_;
4567 const int64_t step_;
4568 std::vector<SearchMonitor*> monitors_;
4569 SolutionCollector* collector_;
4570};
4571} // namespace
4572
4574 Assignment* const solution,
4575 bool maximize, int64_t step) {
4576 return RevAlloc(new NestedOptimize(db, solution, maximize, step));
4577}
4578
4580 Assignment* const solution,
4581 bool maximize, int64_t step,
4582 SearchMonitor* const monitor1) {
4583 std::vector<SearchMonitor*> monitors;
4584 monitors.push_back(monitor1);
4585 return RevAlloc(new NestedOptimize(db, solution, maximize, step, monitors));
4586}
4587
4589 Assignment* const solution,
4590 bool maximize, int64_t step,
4591 SearchMonitor* const monitor1,
4592 SearchMonitor* const monitor2) {
4593 std::vector<SearchMonitor*> monitors;
4594 monitors.push_back(monitor1);
4595 monitors.push_back(monitor2);
4596 return RevAlloc(new NestedOptimize(db, solution, maximize, step, monitors));
4597}
4598
4600 Assignment* const solution,
4601 bool maximize, int64_t step,
4602 SearchMonitor* const monitor1,
4603 SearchMonitor* const monitor2,
4604 SearchMonitor* const monitor3) {
4605 std::vector<SearchMonitor*> monitors;
4606 monitors.push_back(monitor1);
4607 monitors.push_back(monitor2);
4608 monitors.push_back(monitor3);
4609 return RevAlloc(new NestedOptimize(db, solution, maximize, step, monitors));
4610}
4611
4613 DecisionBuilder* const db, Assignment* const solution, bool maximize,
4614 int64_t step, SearchMonitor* const monitor1, SearchMonitor* const monitor2,
4615 SearchMonitor* const monitor3, SearchMonitor* const monitor4) {
4616 std::vector<SearchMonitor*> monitors;
4617 monitors.push_back(monitor1);
4618 monitors.push_back(monitor2);
4619 monitors.push_back(monitor3);
4620 monitors.push_back(monitor4);
4621 return RevAlloc(new NestedOptimize(db, solution, maximize, step, monitors));
4622}
4623
4625 DecisionBuilder* const db, Assignment* const solution, bool maximize,
4626 int64_t step, const std::vector<SearchMonitor*>& monitors) {
4627 return RevAlloc(new NestedOptimize(db, solution, maximize, step, monitors));
4628}
4629
4630// ---------- Restart ----------
4631
4632namespace {
4633// Luby Strategy
4634int64_t NextLuby(int i) {
4635 DCHECK_GT(i, 0);
4637 int64_t power;
4638
4639 // let's find the least power of 2 >= (i+1).
4640 power = 2;
4641 // Cannot overflow, because bounded by kint32max + 1.
4642 while (power < (i + 1)) {
4643 power <<= 1;
4644 }
4645 if (power == i + 1) {
4646 return (power / 2);
4647 }
4648 return NextLuby(i - (power / 2) + 1);
4649}
4650
4651class LubyRestart : public SearchMonitor {
4652 public:
4653 LubyRestart(Solver* const s, int scale_factor)
4654 : SearchMonitor(s),
4655 scale_factor_(scale_factor),
4656 iteration_(1),
4657 current_fails_(0),
4658 next_step_(scale_factor) {
4659 CHECK_GE(scale_factor, 1);
4660 }
4661
4662 ~LubyRestart() override {}
4663
4664 void BeginFail() override {
4665 if (++current_fails_ >= next_step_) {
4666 current_fails_ = 0;
4667 next_step_ = NextLuby(++iteration_) * scale_factor_;
4668 solver()->RestartCurrentSearch();
4669 }
4670 }
4671
4672 std::string DebugString() const override {
4673 return absl::StrFormat("LubyRestart(%i)", scale_factor_);
4674 }
4675
4676 private:
4677 const int scale_factor_;
4678 int iteration_;
4679 int64_t current_fails_;
4680 int64_t next_step_;
4681};
4682} // namespace
4683
4685 return RevAlloc(new LubyRestart(this, scale_factor));
4686}
4687
4688// ----- Constant Restart -----
4689
4690namespace {
4691class ConstantRestart : public SearchMonitor {
4692 public:
4693 ConstantRestart(Solver* const s, int frequency)
4694 : SearchMonitor(s), frequency_(frequency), current_fails_(0) {
4695 CHECK_GE(frequency, 1);
4696 }
4697
4698 ~ConstantRestart() override {}
4699
4700 void BeginFail() override {
4701 if (++current_fails_ >= frequency_) {
4702 current_fails_ = 0;
4703 solver()->RestartCurrentSearch();
4704 }
4705 }
4706
4707 std::string DebugString() const override {
4708 return absl::StrFormat("ConstantRestart(%i)", frequency_);
4709 }
4710
4711 private:
4712 const int frequency_;
4713 int64_t current_fails_;
4714};
4715} // namespace
4716
4718 return RevAlloc(new ConstantRestart(this, frequency));
4719}
4720
4721// ---------- Symmetry Breaking ----------
4722
4723// The symmetry manager maintains a list of problem symmetries. Each
4724// symmetry is called on each decision and should return a term
4725// representing the boolean status of the symmetrical decision.
4726// e.g. : the decision is x == 3, the symmetrical decision is y == 5
4727// then the symmetry breaker should use
4728// AddIntegerVariableEqualValueClause(y, 5). Once this is done, upon
4729// refutation, for each symmetry breaker, the system adds a constraint
4730// that will forbid the symmetrical variation of the current explored
4731// search tree. This constraint can be expressed very simply just by
4732// keeping the list of current symmetrical decisions.
4733//
4734// This is called Symmetry Breaking During Search (Ian Gent, Barbara
4735// Smith, ECAI 2000).
4736// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.42.3788&rep=rep1&type=pdf
4737//
4739 public:
4741 const std::vector<SymmetryBreaker*>& visitors)
4742 : SearchMonitor(s),
4743 visitors_(visitors),
4744 clauses_(visitors.size()),
4745 decisions_(visitors.size()),
4746 directions_(visitors.size()) { // false = left.
4747 for (int i = 0; i < visitors_.size(); ++i) {
4748 visitors_[i]->set_symmetry_manager_and_index(this, i);
4749 }
4750 }
4751
4752 ~SymmetryManager() override {}
4753
4754 void EndNextDecision(DecisionBuilder* const db, Decision* const d) override {
4755 if (d) {
4756 for (int i = 0; i < visitors_.size(); ++i) {
4757 const void* const last = clauses_[i].Last();
4758 d->Accept(visitors_[i]);
4759 if (last != clauses_[i].Last()) {
4760 // Synchroneous push of decision as marker.
4761 decisions_[i].Push(solver(), d);
4762 directions_[i].Push(solver(), false);
4763 }
4764 }
4765 }
4766 }
4767
4768 void RefuteDecision(Decision* d) override {
4769 for (int i = 0; i < visitors_.size(); ++i) {
4770 if (decisions_[i].Last() != nullptr && decisions_[i].LastValue() == d) {
4771 CheckSymmetries(i);
4772 }
4773 }
4774 }
4775
4776 // TODO(user) : Improve speed, cache previous min and build them
4777 // incrementally.
4780 SimpleRevFIFO<bool>::Iterator tmp_dir(&directions_[index]);
4781 Constraint* ct = nullptr;
4782 {
4783 std::vector<IntVar*> guard;
4784 // keep the last entry for later, if loop doesn't exit.
4785 ++tmp;
4786 ++tmp_dir;
4787 while (tmp.ok()) {
4788 IntVar* const term = *tmp;
4789 if (!*tmp_dir) {
4790 if (term->Max() == 0) {
4791 // Premise is wrong. The clause will never apply.
4792 return;
4793 }
4794 if (term->Min() == 0) {
4795 DCHECK_EQ(1, term->Max());
4796 // Premise may be true. Adding to guard vector.
4797 guard.push_back(term);
4798 }
4799 }
4800 ++tmp;
4801 ++tmp_dir;
4802 }
4803 guard.push_back(clauses_[index].LastValue());
4804 directions_[index].SetLastValue(true);
4805 // Given premises: xi = ai
4806 // and a term y != b
4807 // The following is equivalent to
4808 // And(xi == a1) => y != b.
4809 ct = solver()->MakeEquality(solver()->MakeMin(guard), Zero());
4810 }
4811 DCHECK(ct != nullptr);
4813 }
4814
4815 void AddTermToClause(SymmetryBreaker* const visitor, IntVar* const term) {
4816 clauses_[visitor->index_in_symmetry_manager()].Push(solver(), term);
4817 }
4818
4819 std::string DebugString() const override { return "SymmetryManager"; }
4820
4821 private:
4822 const std::vector<SymmetryBreaker*> visitors_;
4823 std::vector<SimpleRevFIFO<IntVar*>> clauses_;
4824 std::vector<SimpleRevFIFO<Decision*>> decisions_;
4825 std::vector<SimpleRevFIFO<bool>> directions_;
4826};
4827
4828// ----- Symmetry Breaker -----
4829
4831 int64_t value) {
4832 CHECK(var != nullptr);
4833 Solver* const solver = var->solver();
4834 IntVar* const term = solver->MakeIsEqualCstVar(var, value);
4835 symmetry_manager()->AddTermToClause(this, term);
4836}
4837
4839 IntVar* const var, int64_t value) {
4840 CHECK(var != nullptr);
4841 Solver* const solver = var->solver();
4842 IntVar* const term = solver->MakeIsGreaterOrEqualCstVar(var, value);
4843 symmetry_manager()->AddTermToClause(this, term);
4844}
4845
4847 IntVar* const var, int64_t value) {
4848 CHECK(var != nullptr);
4849 Solver* const solver = var->solver();
4850 IntVar* const term = solver->MakeIsLessOrEqualCstVar(var, value);
4851 symmetry_manager()->AddTermToClause(this, term);
4852}
4853
4854// ----- API -----
4855
4857 const std::vector<SymmetryBreaker*>& visitors) {
4858 return RevAlloc(new SymmetryManager(this, visitors));
4859}
4860
4862 std::vector<SymmetryBreaker*> visitors;
4863 visitors.push_back(v1);
4864 return MakeSymmetryManager(visitors);
4865}
4866
4868 SymmetryBreaker* const v2) {
4869 std::vector<SymmetryBreaker*> visitors;
4870 visitors.push_back(v1);
4871 visitors.push_back(v2);
4872 return MakeSymmetryManager(visitors);
4873}
4874
4876 SymmetryBreaker* const v2,
4877 SymmetryBreaker* const v3) {
4878 std::vector<SymmetryBreaker*> visitors;
4879 visitors.push_back(v1);
4880 visitors.push_back(v2);
4881 visitors.push_back(v3);
4882 return MakeSymmetryManager(visitors);
4883}
4884
4886 SymmetryBreaker* const v2,
4887 SymmetryBreaker* const v3,
4888 SymmetryBreaker* const v4) {
4889 std::vector<SymmetryBreaker*> visitors;
4890 visitors.push_back(v1);
4891 visitors.push_back(v2);
4892 visitors.push_back(v3);
4893 visitors.push_back(v4);
4894 return MakeSymmetryManager(visitors);
4895}
4896} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK(condition)
Definition: base/logging.h:491
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:888
#define CHECK_LT(val1, val2)
Definition: base/logging.h:701
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
#define CHECK_GE(val1, val2)
Definition: base/logging.h:702
#define CHECK_GT(val1, val2)
Definition: base/logging.h:703
#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
#define DCHECK(condition)
Definition: base/logging.h:885
#define CHECK_LE(val1, val2)
Definition: base/logging.h:700
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:886
#define VLOG(verboselevel)
Definition: base/logging.h:979
An Assignment is a variable -> domains mapping, used to report solutions to the user.
const std::vector< int > & Unperformed(const SequenceVar *const var) const
const std::vector< int > & BackwardSequence(const SequenceVar *const var) const
int64_t EndValue(const IntervalVar *const var) const
int64_t StartValue(const IntervalVar *const var) const
int64_t PerformedValue(const IntervalVar *const var) const
int64_t DurationValue(const IntervalVar *const var) const
const std::vector< int > & ForwardSequence(const SequenceVar *const var) const
int64_t Value(const IntVar *const var) const
A BaseObject is the root of all reversibly allocated objects.
void Set(uint32_t index, bool value)
Definition: bitmap.h:62
bool Get(uint32_t index) const
Definition: bitmap.h:58
A constraint is the main modeling object.
A DecisionBuilder is responsible for creating the search tree.
A Decision represents a choice point in the search tree.
virtual void Accept(DecisionVisitor *const visitor) const
Accepts the given visitor.
virtual void Apply(Solver *const s)=0
Apply will be called first when the decision is executed.
virtual void Refute(Solver *const s)=0
Refute will be called after a backtrack.
std::string DebugString() const override
bool Check() override
This method is called to check the status of the limit.
Definition: search.cc:4235
void Init() override
This method is called when the search limit is initialized.
Definition: search.cc:4203
void Copy(const SearchLimit *const limit) override
Copy a limit.
Definition: search.cc:4211
bool AtSolution() override
This method is called when a valid solution is found.
Definition: search.cc:4259
ImprovementSearchLimit(Solver *const s, IntVar *objective_var, bool maximize, double objective_scaling_factor, double objective_offset, double improvement_rate_coefficient, int improvement_rate_solutions_distance)
Definition: search.cc:4185
SearchLimit * MakeClone() const override
Allocates a clone of the limit.
Definition: search.cc:4228
The class IntExpr is the base of all integer expressions in constraint programming.
virtual bool Bound() const
Returns true if the min and the max of the expression are equal.
virtual void SetValue(int64_t v)
This method sets the value of the expression.
virtual int64_t Min() const =0
virtual void SetMax(int64_t m)=0
virtual void SetMin(int64_t m)=0
virtual int64_t Max() const =0
The class IntVar is a subset of IntExpr.
virtual int64_t Value() const =0
This method returns the value of the variable.
Interval variables are often used in scheduling.
static int64_t FastInt64Round(double x)
Definition: mathutil.h:138
static const char kSolutionLimitArgument[]
virtual void VisitIntegerArgument(const std::string &arg_name, int64_t value)
Visit integer arguments.
static const char kBranchesLimitArgument[]
static const char kSmartTimeCheckArgument[]
virtual void BeginVisitExtension(const std::string &type)
virtual void EndVisitExtension(const std::string &type)
static const char kVariableGroupExtension[]
static const char kFailuresLimitArgument[]
virtual void VisitIntegerExpressionArgument(const std::string &arg_name, IntExpr *const argument)
Visit integer expression argument.
This class encapsulates an objective.
void EnterSearch() override
Beginning of the search.
Definition: search.cc:2750
void BeginNextDecision(DecisionBuilder *const db) override
Before calling DecisionBuilder::Next.
Definition: search.cc:2759
OptimizeVar(Solver *const s, bool maximize, IntVar *const a, int64_t step)
Definition: search.cc:2729
void Accept(ModelVisitor *const visitor) const override
Accepts the given model visitor.
Definition: search.cc:2854
bool AcceptSolution() override
This method is called when a solution is found.
Definition: search.cc:2777
virtual std::string Print() const
Definition: search.cc:2838
bool AtSolution() override
This method is called when a valid solution is found.
Definition: search.cc:2789
void RefuteDecision(Decision *const d) override
Before refuting the decision.
Definition: search.cc:2775
IntVar * Var() const
Returns the variable that is optimized.
bool AcceptDelta(Assignment *delta, Assignment *deltadelta) override
Internal methods.
Definition: search.cc:2802
std::string DebugString() const override
Definition: search.cc:2842
Usual limit based on wall_time, number of explored branches and number of failures in the search tree...
bool Check() override
This method is called to check the status of the limit.
Definition: search.cc:4020
absl::Duration duration_limit() const
bool IsUncheckedSolutionLimitReached() override
Returns true if the limit of solutions has been reached including unchecked solutions.
Definition: search.cc:4071
void UpdateLimits(absl::Duration time, int64_t branches, int64_t failures, int64_t solutions)
Definition: search.cc:4063
void Init() override
This method is called when the search limit is initialized.
Definition: search.cc:4041
void ExitSearch() override
End of the search.
Definition: search.cc:4052
int ProgressPercent() override
Returns a percentage representing the propress of the search before reaching limits.
Definition: search.cc:4028
void Accept(ModelVisitor *const visitor) const override
Accepts the given model visitor.
Definition: search.cc:4085
void Copy(const SearchLimit *const limit) override
Copy a limit.
Definition: search.cc:4001
RegularLimit * MakeIdenticalClone() const
Definition: search.cc:4014
std::string DebugString() const override
Definition: search.cc:4077
SearchLimit * MakeClone() const override
Allocates a clone of the limit.
Definition: search.cc:4012
Base class of all search limits.
bool crossed() const
Returns true if the limit has been crossed.
The base class of all search logs that periodically outputs information when the search is running.
void BeginFail() override
Just when the failure occurs.
Definition: search.cc:180
virtual void OutputLine(const std::string &line)
Definition: search.cc:260
void EnterSearch() override
Beginning of the search.
Definition: search.cc:87
void RefuteDecision(Decision *const decision) override
Before refuting the decision.
Definition: search.cc:210
void ExitSearch() override
End of the search.
Definition: search.cc:95
SearchLog(Solver *const s, OptimizeVar *const obj, IntVar *const var, double scaling_factor, double offset, std::function< std::string()> display_callback, bool display_on_new_solutions_only, int period)
Definition: search.cc:58
void BeginInitialPropagation() override
Before the initial propagation.
Definition: search.cc:250
void NoMoreSolutions() override
When the search tree is finished.
Definition: search.cc:182
void ApplyDecision(Decision *const decision) override
Before applying the decision.
Definition: search.cc:202
bool AtSolution() override
This method is called when a valid solution is found.
Definition: search.cc:108
std::string DebugString() const override
Definition: search.cc:85
void AcceptUncheckedNeighbor() override
After accepting an unchecked neighbor during local search.
Definition: search.cc:178
void EndInitialPropagation() override
After the initial propagation.
Definition: search.cc:252
A search monitor is a simple set of callbacks to monitor all search events.
virtual void ExitSearch()
End of the search.
virtual bool AtSolution()
This method is called when a valid solution is found.
A sequence variable is a variable whose domain is a set of possible orderings of the interval variabl...
This iterator is not stable with respect to deletion.
This class is the root class of all solution collectors.
void EnterSearch() override
Beginning of the search.
Definition: search.cc:2272
void Push(const SolutionData &data)
void PushSolution()
Push the current state as a new solution.
Definition: search.cc:2281
void AddObjective(IntVar *const objective)
Definition: search.cc:2266
std::vector< Assignment * > recycle_solutions_
std::vector< SolutionData > solution_data_
void Add(IntVar *const var)
Add API.
Definition: search.cc:2230
int solution_count() const
Returns how many solutions were stored during the search.
Definition: search.cc:2334
int64_t Value(int n, IntVar *const var) const
This is a shortcut to get the Value of 'var' in the nth solution.
Definition: search.cc:2356
const std::vector< int > & Unperformed(int n, SequenceVar *const var) const
This is a shortcut to get the list of unperformed of 'var' in the nth solution.
Definition: search.cc:2386
SolutionData BuildSolutionDataForCurrentState()
Definition: search.cc:2293
int64_t DurationValue(int n, IntervalVar *const var) const
This is a shortcut to get the DurationValue of 'var' in the nth solution.
Definition: search.cc:2364
int64_t StartValue(int n, IntervalVar *const var) const
This is a shortcut to get the StartValue of 'var' in the nth solution.
Definition: search.cc:2360
Assignment * solution(int n) const
Returns the nth solution.
Definition: search.cc:2329
int64_t EndValue(int n, IntervalVar *const var) const
This is a shortcut to get the EndValue of 'var' in the nth solution.
Definition: search.cc:2368
int64_t objective_value(int n) const
Returns the objective value of the nth solution.
Definition: search.cc:2351
int64_t wall_time(int n) const
Returns the wall time in ms for the nth solution.
Definition: search.cc:2336
int64_t branches(int n) const
Returns the number of branches when the nth solution was found.
Definition: search.cc:2341
int64_t PerformedValue(int n, IntervalVar *const var) const
This is a shortcut to get the PerformedValue of 'var' in the nth solution.
Definition: search.cc:2372
const std::vector< int > & ForwardSequence(int n, SequenceVar *const var) const
This is a shortcut to get the ForwardSequence of 'var' in the nth solution.
Definition: search.cc:2376
void FreeSolution(Assignment *solution)
Definition: search.cc:2318
int64_t failures(int n) const
Returns the number of failures encountered at the time of the nth solution.
Definition: search.cc:2346
std::unique_ptr< Assignment > prototype_
SolutionCollector(Solver *const solver, const Assignment *assignment)
Definition: search.cc:2214
void PopSolution()
Remove and delete the last popped solution.
Definition: search.cc:2285
std::string DebugString() const override
const std::vector< int > & BackwardSequence(int n, SequenceVar *const var) const
This is a shortcut to get the BackwardSequence of 'var' in the nth solution.
Definition: search.cc:2381
int64_t neighbors() const
The number of neighbors created.
SearchMonitor * MakeLubyRestart(int scale_factor)
This search monitor will restart the search periodically.
Definition: search.cc:4684
SolutionCollector * MakeAllSolutionCollector()
Collect all solutions of the search.
Definition: search.cc:2723
RegularLimit * MakeSolutionsLimit(int64_t solutions)
Creates a search limit that constrains the number of solutions found during the search.
Definition: search.cc:4143
OptimizeVar * MakeWeightedMinimize(const std::vector< IntVar * > &sub_objectives, const std::vector< int64_t > &weights, int64_t step)
Creates a minimization weighted objective.
Definition: search.cc:2918
SolutionCollector * MakeLastSolutionCollector()
Collect the last solution of the search.
Definition: search.cc:2490
Decision * MakeAssignVariableValueOrDoNothing(IntVar *const var, int64_t value)
Definition: search.cc:1633
SearchMonitor * MakeAtSolutionCallback(std::function< void()> callback)
Definition: search.cc:422
int64_t branches() const
The number of branches explored since the creation of the solver.
SearchLimit * MakeCustomLimit(std::function< bool()> limiter)
Callback-based search limit.
Definition: search.cc:4411
Constraint * MakeEquality(IntExpr *const left, IntExpr *const right)
left == right
Definition: range_cst.cc:512
OptimizeVar * MakeOptimize(bool maximize, IntVar *const v, int64_t step)
Creates a objective with a given sense (true = maximization).
Definition: search.cc:2871
IntVar * MakeIsGreaterOrEqualCstVar(IntExpr *const var, int64_t value)
status var of (var >= value)
Definition: expr_cst.cc:679
ConstraintSolverParameters parameters() const
Stored Parameters.
SearchMonitor * MakeSymmetryManager(const std::vector< SymmetryBreaker * > &visitors)
Symmetry Breaking.
Definition: search.cc:4856
RegularLimit * MakeFailuresLimit(int64_t failures)
Creates a search limit that constrains the number of failures that can happen when exploring the sear...
Definition: search.cc:4136
absl::Time Now() const
The 'absolute time' as seen by the solver.
std::function< int64_t(int64_t, int64_t, int64_t)> IndexEvaluator3
Assignment * GetOrCreateLocalSearchState()
Returns (or creates) an assignment representing the state of local search.
DecisionBuilder * MakeNestedOptimize(DecisionBuilder *const db, Assignment *const solution, bool maximize, int64_t step)
NestedOptimize will collapse a search tree described by a decision builder 'db' and a set of monitors...
Definition: search.cc:4573
DecisionBuilder * Try(DecisionBuilder *const db1, DecisionBuilder *const db2)
Creates a decision builder which will create a search tree where each decision builder is called from...
Definition: search.cc:704
RegularLimit * MakeBranchesLimit(int64_t branches)
Creates a search limit that constrains the number of branches explored in the search tree.
Definition: search.cc:4129
OptimizeVar * MakeMaximize(IntVar *const v, int64_t step)
Creates a maximization objective.
Definition: search.cc:2867
SearchMonitor * MakeSearchLog(int branch_period)
The SearchMonitors below will display a periodic search log on LOG(INFO) every branch_period branches...
Definition: search.cc:288
IntValueStrategy
This enum describes the strategy used to select the next variable value to set.
@ INT_VALUE_SIMPLE
The simple selection is ASSIGN_MIN_VALUE.
@ ASSIGN_CENTER_VALUE
Selects the first possible value which is the closest to the center of the domain of the selected var...
@ SPLIT_UPPER_HALF
Split the domain in two around the center, and choose the lower part first.
@ 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.
@ INT_VALUE_DEFAULT
The default behavior is ASSIGN_MIN_VALUE.
@ ASSIGN_MAX_VALUE
Selects the max value of the selected variable.
@ SPLIT_LOWER_HALF
Split the domain in two around the center, and choose the lower part first.
RegularLimit * MakeLimit(absl::Duration time, int64_t branches, int64_t failures, int64_t solutions, bool smart_time_check=false, bool cumulative=false)
Limits the search with the 'time', 'branches', 'failures' and 'solutions' limits.
Definition: search.cc:4157
std::function< int64_t(Solver *solver, const std::vector< IntVar * > &vars, int64_t first_unbound, int64_t last_unbound)> VariableIndexSelector
std::function< int64_t(int64_t, int64_t)> IndexEvaluator2
OptimizeVar * MakeMinimize(IntVar *const v, int64_t step)
Creates a minimization objective.
Definition: search.cc:2863
void AddConstraint(Constraint *const c)
Adds the constraint 'c' to the model.
Decision * MakeAssignVariablesValues(const std::vector< IntVar * > &vars, const std::vector< int64_t > &values)
Definition: search.cc:1761
DecisionBuilder * MakeSolveOnce(DecisionBuilder *const db)
SolveOnce will collapse a search tree described by a decision builder 'db' and a set of monitors and ...
Definition: search.cc:4454
int64_t wall_time() const
DEPRECATED: Use Now() instead.
OptimizeVar * MakeWeightedMaximize(const std::vector< IntVar * > &sub_objectives, const std::vector< int64_t > &weights, int64_t step)
Creates a maximization weigthed objective.
Definition: search.cc:2925
int SearchDepth() const
Gets the search depth of the current active search.
int64_t unchecked_solutions() const
The number of unchecked solutions found by local search.
int64_t failures() const
The number of failures encountered since the creation of the solver.
ImprovementSearchLimit * MakeImprovementLimit(IntVar *objective_var, bool maximize, double objective_scaling_factor, double objective_offset, double improvement_rate_coefficient, int improvement_rate_solutions_distance)
Limits the search based on the improvements of 'objective_var'.
Definition: search.cc:4300
SearchMonitor * MakeConstantRestart(int frequency)
This search monitor will restart the search periodically after 'frequency' failures.
Definition: search.cc:4717
EvaluatorStrategy
This enum is used by Solver::MakePhase to specify how to select variables and values during the searc...
@ CHOOSE_STATIC_GLOBAL_BEST
Pairs are compared at the first call of the selector, and results are cached.
@ CHOOSE_DYNAMIC_GLOBAL_BEST
Pairs are compared each time a variable is selected.
static int64_t MemoryUsage()
Current memory usage in bytes.
void set_optimization_direction(OptimizationDirection direction)
IntVar * MakeIsLessOrEqualCstVar(IntExpr *const var, int64_t value)
status var of (var <= value)
Definition: expr_cst.cc:779
SearchMonitor * MakeSimulatedAnnealing(bool maximize, IntVar *const v, int64_t step, int64_t initial_temperature)
Creates a Simulated Annealing monitor.
Definition: search.cc:3374
RegularLimitParameters MakeDefaultRegularLimitParameters() const
Creates a regular limit proto containing default values.
Definition: search.cc:4172
RegularLimit * MakeTimeLimit(absl::Duration time)
Creates a search limit that constrains the running time.
Definition: search.cc:4122
SearchMonitor * MakeSearchTrace(const std::string &prefix)
Creates a search monitor that will trace precisely the behavior of the search.
Definition: search.cc:398
int TopProgressPercent()
Returns a percentage representing the propress of the search before reaching the limits of the top-le...
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_MIN_SIZE
Among unbound variables, select the variable with the smallest size.
@ CHOOSE_FIRST_UNBOUND
Select the first unbound variable.
@ CHOOSE_PATH
Selects the next unbound variable on a path, the path being defined by the variables: var[i] correspo...
@ CHOOSE_HIGHEST_MAX
Among unbound variables, select the variable with the highest maximal value.
@ CHOOSE_MIN_SIZE_LOWEST_MIN
Among unbound variables, select the variable with the smallest size, i.e., the smallest number of pos...
@ INT_VAR_DEFAULT
The default behavior is CHOOSE_FIRST_UNBOUND.
@ CHOOSE_MIN_SIZE_HIGHEST_MAX
Among unbound variables, select the variable with the smallest size, i.e., the smallest number of pos...
@ CHOOSE_MAX_REGRET_ON_MIN
Among unbound variables, select the variable with the largest gap between the first and the second va...
@ CHOOSE_MIN_SIZE_HIGHEST_MIN
Among unbound variables, select the variable with the smallest size, i.e., the smallest number of pos...
@ CHOOSE_MAX_SIZE
Among unbound variables, select the variable with the highest size.
@ INT_VAR_SIMPLE
The simple selection is CHOOSE_FIRST_UNBOUND.
@ CHOOSE_MIN_SIZE_LOWEST_MAX
Among unbound variables, select the variable with the smallest size, i.e., the smallest number of pos...
@ CHOOSE_LOWEST_MIN
Among unbound variables, select the variable with the smallest minimal value.
DecisionBuilder * MakePhase(const std::vector< IntVar * > &vars, IntVarStrategy var_str, IntValueStrategy val_str)
Phases on IntVar arrays.
Definition: search.cc:2017
std::function< int64_t(const IntVar *v, int64_t id)> VariableValueSelector
SearchMonitor * MakeEnterSearchCallback(std::function< void()> callback)
--— Callback-based search monitors --—
Definition: search.cc:442
std::function< void(Solver *)> Action
SolutionCollector * MakeFirstSolutionCollector()
Collect the first solution of the search.
Definition: search.cc:2444
OptimizeVar * MakeWeightedOptimize(bool maximize, const std::vector< IntVar * > &sub_objectives, const std::vector< int64_t > &weights, int64_t step)
Creates a weighted objective with a given sense (true = maximization).
Definition: search.cc:2911
std::function< int64_t(int64_t)> IndexEvaluator1
Callback typedefs.
SearchMonitor * MakeExitSearchCallback(std::function< void()> callback)
Definition: search.cc:462
Decision * MakeSplitVariableDomain(IntVar *const var, int64_t val, bool start_with_lower_half)
Definition: search.cc:1687
DecisionBuilder * MakeDecisionBuilderFromAssignment(Assignment *const assignment, DecisionBuilder *const db, const std::vector< IntVar * > &vars)
Returns a decision builder for which the left-most leaf corresponds to assignment,...
Definition: search.cc:2204
Decision * MakeVariableLessOrEqualValue(IntVar *const var, int64_t value)
Definition: search.cc:1692
IntVar * MakeIsEqualCstVar(IntExpr *const var, int64_t value)
status var of (var == value)
Definition: expr_cst.cc:462
int64_t solutions() const
The number of solutions found since the start of the search.
std::function< bool(int64_t, int64_t, int64_t)> VariableValueComparator
T * RevAlloc(T *object)
Registers the given object as being reversible.
Decision * MakeAssignVariableValueOrFail(IntVar *const var, int64_t value)
Definition: search.cc:1604
Decision * MakeVariableGreaterOrEqualValue(IntVar *const var, int64_t value)
Definition: search.cc:1697
A symmetry breaker is an object that will visit a decision and create the 'symmetrical' decision in r...
void AddIntegerVariableLessOrEqualValueClause(IntVar *const var, int64_t value)
Definition: search.cc:4846
void AddIntegerVariableEqualValueClause(IntVar *const var, int64_t value)
Definition: search.cc:4830
void AddIntegerVariableGreaterOrEqualValueClause(IntVar *const var, int64_t value)
Definition: search.cc:4838
void AddTermToClause(SymmetryBreaker *const visitor, IntVar *const term)
Definition: search.cc:4815
void EndNextDecision(DecisionBuilder *const db, Decision *const d) override
After calling DecisionBuilder::Next, along with the returned decision.
Definition: search.cc:4754
void RefuteDecision(Decision *d) override
Before refuting the decision.
Definition: search.cc:4768
SymmetryManager(Solver *const s, const std::vector< SymmetryBreaker * > &visitors)
Definition: search.cc:4740
std::string DebugString() const override
Definition: search.cc:4819
int64_t b
int64_t a
std::vector< IntVarIterator * > iterators_
Block * next
SatParameters parameters
CpModelProto proto
const std::string name
const Constraint * ct
MPCallback * callback
const int64_t offset_
Definition: interval.cc:2108
const int INFO
Definition: log_severity.h:31
const int FATAL
Definition: log_severity.h:32
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: macros.h:29
Definition: cleanup.h:22
void STLDeleteElements(T *container)
Definition: stl_util.h:372
bool FindCopy(const Collection &collection, const Key &key, Value *const value)
Definition: map_util.h:185
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:29
std::function< int64_t(const Model &)> Value(IntegerVariable v)
Definition: integer.h:1544
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapSub(int64_t x, int64_t y)
int64_t Zero()
NOLINT.
std::pair< int64_t, int64_t > Arc
Definition: search.cc:3383
std::string JoinDebugStringPtr(const std::vector< T > &v, const std::string &separator)
Definition: string_array.h:45
bool AcceptDelta(Search *const search, Assignment *delta, Assignment *deltadelta)
std::vector< int64_t > ToInt64Vector(const std::vector< int > &input)
Definition: utilities.cc:828
void AcceptNeighbor(Search *const search)
BaseAssignVariables::Mode ChooseMode(Solver::IntValueStrategy val_str)
Definition: search.cc:2007
STL namespace.
int index
Definition: pack.cc:509
int64_t time
Definition: resource.cc:1691
int64_t delta
Definition: resource.cc:1692
int64_t bound
std::priority_queue< std::pair< int64_t, SolutionData > > solutions_pq_
Definition: search.cc:2589
int64_t assignment_penalized_value_
Definition: search.cc:3511
const double penalty_factor_
Definition: search.cc:3515
int64_t step_
Definition: search.cc:2967
std::vector< DecisionBuilder * > builders_
Definition: search.cc:480
std::unique_ptr< int64_t[]> delta_cache_
Definition: search.cc:3518
BaseVariableAssignmentSelector *const selector_
Definition: search.cc:1865
int64_t best_
Definition: search.cc:2509
std::unique_ptr< int64_t[]> current_penalized_values_
Definition: search.cc:3517
const int solution_count_
Definition: search.cc:2590
int64_t var
Definition: search.cc:1360
int64_t current_
Definition: search.cc:2968
ABSL_FLAG(bool, cp_use_sparse_gls_penalties, false, "Use sparse implementation to store Guided Local Search penalties")
IntVar * penalized_objective_
Definition: search.cc:3509
const bool maximize_
Definition: search.cc:2508
std::vector< IntVar * > vars_
Definition: search.cc:790
IntVar *const objective_
Definition: search.cc:2966
Rev< int64_t > first_unbound_
Definition: search.cc:791
Rev< int64_t > last_unbound_
Definition: search.cc:792
std::function< int64_t(int64_t, int64_t)> evaluator_
Definition: search.cc:1368
Solver *const solver_
Definition: search.cc:789
int64_t value
Definition: search.cc:1361
int64_t old_penalized_value_
Definition: search.cc:3512
bool incremental_
Definition: search.cc:3519
absl::flat_hash_map< const IntVar *, int64_t > indices_
Definition: search.cc:3514
const int64_t stamp_
Definition: search.cc:3054
const Mode mode_
Definition: search.cc:1866
Creates a search monitor from logging parameters.