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