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