Google OR-Tools v9.15
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 AtLocalOptimum() override {
3017 const bool ok = monitors_[active_monitor_]->AtLocalOptimum();
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
3220namespace {
3221class ApplyBoundDecisionBuilder : public DecisionBuilder {
3222 public:
3223 explicit ApplyBoundDecisionBuilder(OptimizeVar* optimize_var)
3224 : optimize_var_(optimize_var) {}
3225 ~ApplyBoundDecisionBuilder() override = default;
3226 Decision* Next(Solver*) override {
3227 optimize_var_->ApplyBound();
3228 return nullptr;
3229 }
3230
3231 private:
3232 OptimizeVar* optimize_var_;
3233};
3234} // namespace
3235
3237 if (!solver()->SolveAndCommit(
3238 solver()->RevAlloc(new ApplyBoundDecisionBuilder(this)))) {
3239 if (on_optimal_found_) {
3240 // TODO(user): Support multiple objectives.
3241 const int64_t value = CurrentInternalValue(0);
3242 on_optimal_found_(objective_vars()[0] == minimization_vars()[0]
3243 ? value
3244 : CapOpp(value));
3245 }
3246 solver()->Fail();
3247 }
3248}
3249
3252 return true;
3253 } else {
3254 // This code should never return false in sequential mode because
3255 // ApplyBound should have been called before. In parallel, this is
3256 // no longer true. That is why we keep it there, just in case.
3257 for (int i = 0; i < Size(); ++i) {
3258 IntVar* const minimization_var = MinimizationVar(i);
3259 // In unchecked mode, variables are unbound and the solution should be
3260 // accepted.
3261 if (!minimization_var->Bound()) return true;
3262 const int64_t value = minimization_var->Value();
3263 if (value == CurrentInternalValue(i)) continue;
3264 return value < CurrentInternalValue(i);
3265 }
3266 return false;
3267 }
3268}
3269
3271 DCHECK(AcceptSolution());
3273}
3274
3275std::string OptimizeVar::Name() const { return "objective"; }
3276
3277std::string OptimizeVar::DebugString() const {
3278 std::string out;
3279 for (int i = 0; i < Size(); ++i) {
3280 absl::StrAppendFormat(
3281 &out, "%s%s(%s, step = %d, best = %d)", i == 0 ? "" : "; ",
3282 Maximize(i) ? "MaximizeVar" : "MinimizeVar",
3283 ObjectiveVar(i)->DebugString(), Step(i), BestValue(i));
3284 }
3285 return out;
3286}
3287
3288OptimizeVar* Solver::MakeMinimize(IntVar* const v, int64_t step) {
3289 return RevAlloc(new OptimizeVar(this, false, v, step));
3290}
3291
3292OptimizeVar* Solver::MakeMaximize(IntVar* const v, int64_t step) {
3293 return RevAlloc(new OptimizeVar(this, true, v, step));
3294}
3295
3296OptimizeVar* Solver::MakeOptimize(bool maximize, IntVar* const v,
3297 int64_t step) {
3298 return RevAlloc(new OptimizeVar(this, maximize, v, step));
3299}
3300
3302 std::vector<IntVar*> variables,
3303 std::vector<int64_t> steps) {
3304 return RevAlloc(new OptimizeVar(this, std::move(maximize),
3305 std::move(variables), std::move(steps)));
3306}
3307
3308namespace {
3309class WeightedOptimizeVar : public OptimizeVar {
3310 public:
3311 WeightedOptimizeVar(Solver* solver, bool maximize,
3312 const std::vector<IntVar*>& sub_objectives,
3313 const std::vector<int64_t>& weights, int64_t step)
3314 : OptimizeVar(solver, maximize,
3315 solver->MakeScalProd(sub_objectives, weights)->Var(), step),
3316 sub_objectives_(sub_objectives),
3317 weights_(weights) {
3318 CHECK_EQ(sub_objectives.size(), weights.size());
3319 }
3320
3321 ~WeightedOptimizeVar() override {}
3322 std::string Name() const override;
3323
3324 private:
3325 const std::vector<IntVar*> sub_objectives_;
3326 const std::vector<int64_t> weights_;
3327};
3328
3329std::string WeightedOptimizeVar::Name() const { return "weighted objective"; }
3330} // namespace
3331
3333 bool maximize, const std::vector<IntVar*>& sub_objectives,
3334 const std::vector<int64_t>& weights, int64_t step) {
3335 return RevAlloc(
3336 new WeightedOptimizeVar(this, maximize, sub_objectives, weights, step));
3337}
3338
3340 const std::vector<IntVar*>& sub_objectives,
3341 const std::vector<int64_t>& weights, int64_t step) {
3342 return RevAlloc(
3343 new WeightedOptimizeVar(this, false, sub_objectives, weights, step));
3344}
3345
3347 const std::vector<IntVar*>& sub_objectives,
3348 const std::vector<int64_t>& weights, int64_t step) {
3349 return RevAlloc(
3350 new WeightedOptimizeVar(this, true, sub_objectives, weights, step));
3351}
3352
3354 bool maximize, const std::vector<IntVar*>& sub_objectives,
3355 const std::vector<int>& weights, int64_t step) {
3356 return MakeWeightedOptimize(maximize, sub_objectives, ToInt64Vector(weights),
3357 step);
3358}
3359
3361 const std::vector<IntVar*>& sub_objectives, const std::vector<int>& weights,
3362 int64_t step) {
3363 return MakeWeightedMinimize(sub_objectives, ToInt64Vector(weights), step);
3364}
3365
3367 const std::vector<IntVar*>& sub_objectives, const std::vector<int>& weights,
3368 int64_t step) {
3369 return MakeWeightedMaximize(sub_objectives, ToInt64Vector(weights), step);
3370}
3371
3372// ---------- Metaheuristics ---------
3373
3374namespace {
3375class Metaheuristic : public ObjectiveMonitor {
3376 public:
3377 Metaheuristic(Solver* solver, const std::vector<bool>& maximize,
3378 std::vector<IntVar*> objectives, std::vector<int64_t> steps);
3379 ~Metaheuristic() override {}
3380
3381 void EnterSearch() override;
3382 void RefuteDecision(Decision* d) override;
3383};
3384
3385Metaheuristic::Metaheuristic(Solver* solver, const std::vector<bool>& maximize,
3386 std::vector<IntVar*> objectives,
3387 std::vector<int64_t> steps)
3388 : ObjectiveMonitor(solver, maximize, std::move(objectives),
3389 std::move(steps)) {}
3390
3391void Metaheuristic::EnterSearch() {
3392 ObjectiveMonitor::EnterSearch();
3393 // TODO(user): Remove this when fast local search works with
3394 // metaheuristics.
3395 solver()->SetUseFastLocalSearch(false);
3396}
3397
3398void Metaheuristic::RefuteDecision(Decision*) {
3399 for (int i = 0; i < Size(); ++i) {
3400 const int64_t objective_value = MinimizationVar(i)->Min();
3401 if (objective_value > BestInternalValue(i)) break;
3402 if (objective_value <= CapSub(BestInternalValue(i), Step(i))) return;
3403 }
3404 solver()->Fail();
3405}
3406
3407// ---------- Tabu Search ----------
3408
3409class TabuSearch : public Metaheuristic {
3410 public:
3411 TabuSearch(Solver* solver, const std::vector<bool>& maximize,
3412 std::vector<IntVar*> objectives, std::vector<int64_t> steps,
3413 const std::vector<IntVar*>& vars, int64_t keep_tenure,
3414 int64_t forbid_tenure, double tabu_factor);
3415 ~TabuSearch() override {}
3416 void EnterSearch() override;
3417 void ApplyDecision(Decision* d) override;
3418 bool AtSolution() override;
3419 bool AcceptSolution() override;
3420 bool AtLocalOptimum() override;
3421 bool AcceptDelta(Assignment* delta, Assignment* deltadelta) override;
3422 void AcceptNeighbor() override;
3423 void BeginNextDecision(DecisionBuilder* const) override {
3424 if (stop_search_) solver()->Fail();
3425 }
3426 void RefuteDecision(Decision* const d) override {
3427 Metaheuristic::RefuteDecision(d);
3428 if (stop_search_) solver()->Fail();
3429 }
3430 std::string DebugString() const override { return "Tabu Search"; }
3431
3432 protected:
3433 struct VarValue {
3434 int var_index;
3435 int64_t value;
3436 int64_t stamp;
3437 };
3438 typedef std::list<VarValue> TabuList;
3439
3440 virtual std::vector<IntVar*> CreateTabuVars();
3441 const TabuList& forbid_tabu_list() { return forbid_tabu_list_; }
3442 IntVar* vars(int index) const { return vars_[index]; }
3443
3444 private:
3445 void AgeList(int64_t tenure, TabuList* list);
3446 void AgeLists();
3447 int64_t TabuLimit() const {
3448 return (synced_keep_tabu_list_.size() + synced_forbid_tabu_list_.size()) *
3449 tabu_factor_;
3450 }
3451
3452 const std::vector<IntVar*> vars_;
3453 Assignment::IntContainer assignment_container_;
3454 bool has_stored_assignment_ = false;
3455 std::vector<int64_t> last_values_;
3456 TabuList keep_tabu_list_;
3457 TabuList synced_keep_tabu_list_;
3458 int64_t keep_tenure_;
3459 TabuList forbid_tabu_list_;
3460 TabuList synced_forbid_tabu_list_;
3461 int64_t forbid_tenure_;
3462 double tabu_factor_;
3463 int64_t stamp_;
3464 int64_t solution_count_ = 0;
3465 bool stop_search_ = false;
3466 std::vector<int64_t> delta_values_;
3467 SparseBitset<> delta_vars_;
3468 std::vector<int> var_index_to_index_;
3469};
3470
3471TabuSearch::TabuSearch(Solver* solver, const std::vector<bool>& maximize,
3472 std::vector<IntVar*> objectives,
3473 std::vector<int64_t> steps,
3474 const std::vector<IntVar*>& vars, int64_t keep_tenure,
3475 int64_t forbid_tenure, double tabu_factor)
3476 : Metaheuristic(solver, maximize, std::move(objectives), std::move(steps)),
3477 vars_(vars),
3478 last_values_(Size(), std::numeric_limits<int64_t>::max()),
3479 keep_tenure_(keep_tenure),
3480 forbid_tenure_(forbid_tenure),
3481 tabu_factor_(tabu_factor),
3482 stamp_(0),
3483 delta_values_(vars.size(), 0),
3484 delta_vars_(vars.size()) {
3485 for (int index = 0; index < vars_.size(); ++index) {
3486 assignment_container_.FastAdd(vars_[index]);
3487 DCHECK_EQ(vars_[index], assignment_container_.Element(index).Var());
3488 const int var_index = vars_[index]->index();
3489 if (var_index >= var_index_to_index_.size()) {
3490 var_index_to_index_.resize(var_index + 1, -1);
3491 }
3492 var_index_to_index_[var_index] = index;
3493 }
3494}
3495
3496void TabuSearch::EnterSearch() {
3497 Metaheuristic::EnterSearch();
3498 solver()->SetUseFastLocalSearch(true);
3499 stamp_ = 0;
3500 has_stored_assignment_ = false;
3501 solution_count_ = 0;
3502 stop_search_ = false;
3503}
3504
3505void TabuSearch::ApplyDecision(Decision* const d) {
3506 Solver* const s = solver();
3507 if (d == s->balancing_decision()) {
3508 return;
3509 }
3510
3511 synced_keep_tabu_list_ = keep_tabu_list_;
3512 synced_forbid_tabu_list_ = forbid_tabu_list_;
3513 Constraint* tabu_ct = nullptr;
3514 {
3515 // Creating vectors in a scope to make sure they get deleted before
3516 // adding the tabu constraint which could fail and lead to a leak.
3517 const std::vector<IntVar*> tabu_vars = CreateTabuVars();
3518 if (!tabu_vars.empty()) {
3519 IntVar* tabu_var = s->MakeIsGreaterOrEqualCstVar(
3520 s->MakeSum(tabu_vars)->Var(), TabuLimit());
3521 // Aspiration criterion
3522 // Accept a neighbor if it improves the best solution found so far.
3523 IntVar* aspiration = MakeMinimizationVarsLessOrEqualWithStepsStatus(
3524 [this](int i) { return BestInternalValue(i); });
3525 tabu_ct = s->MakeSumGreaterOrEqual({aspiration, tabu_var}, int64_t{1});
3526 }
3527 }
3528 if (tabu_ct != nullptr) s->AddConstraint(tabu_ct);
3529
3530 // Go downhill to the next local optimum
3531 if (CurrentInternalValuesAreConstraining()) {
3532 MakeMinimizationVarsLessOrEqualWithSteps(
3533 [this](int i) { return CurrentInternalValue(i); });
3534 }
3535}
3536
3537bool TabuSearch::AcceptSolution() {
3538 // Avoid cost plateaus which lead to tabu cycles.
3539 if (found_initial_solution_) {
3540 for (int i = 0; i < Size(); ++i) {
3541 if (last_values_[i] != MinimizationVar(i)->Min()) {
3542 return true;
3543 }
3544 }
3545 return false;
3546 }
3547 return true;
3548}
3549
3550std::vector<IntVar*> TabuSearch::CreateTabuVars() {
3551 Solver* const s = solver();
3552
3553 // Tabu criterion
3554 // A variable in the "keep" list must keep its value, a variable in the
3555 // "forbid" list must not take its value in the list. The tabu criterion is
3556 // softened by the tabu factor which gives the number of violations to
3557 // the tabu criterion which is tolerated; a factor of 1 means no violations
3558 // allowed, a factor of 0 means all violations allowed.
3559 std::vector<IntVar*> tabu_vars;
3560 for (const auto [var_index, value, unused_stamp] : keep_tabu_list_) {
3561 tabu_vars.push_back(s->MakeIsEqualCstVar(vars(var_index), value));
3562 }
3563 for (const auto [var_index, value, unused_stamp] : forbid_tabu_list_) {
3564 tabu_vars.push_back(s->MakeIsDifferentCstVar(vars(var_index), value));
3565 }
3566 return tabu_vars;
3567}
3568
3569bool TabuSearch::AtSolution() {
3570 ++solution_count_;
3571 if (!ObjectiveMonitor::AtSolution()) {
3572 return false;
3573 }
3574 for (int i = 0; i < Size(); ++i) {
3575 last_values_[i] = CurrentInternalValue(i);
3576 }
3577
3578 // New solution found: add new assignments to tabu lists; this is only
3579 // done after the first local optimum (stamp_ != 0)
3580 if (0 != stamp_ && has_stored_assignment_) {
3581 for (int index = 0; index < vars_.size(); ++index) {
3582 IntVar* var = vars(index);
3583 const int64_t old_value = assignment_container_.Element(index).Value();
3584 const int64_t new_value = var->Value();
3585 if (old_value != new_value) {
3586 if (keep_tenure_ > 0) {
3587 keep_tabu_list_.push_front({index, new_value, stamp_});
3588 }
3589 if (forbid_tenure_ > 0) {
3590 forbid_tabu_list_.push_front({index, old_value, stamp_});
3591 }
3592 }
3593 }
3594 }
3595 assignment_container_.Store();
3596 has_stored_assignment_ = true;
3597
3598 return true;
3599}
3600
3601bool TabuSearch::AtLocalOptimum() {
3602 solver()->SetUseFastLocalSearch(false);
3603 // If no solution has been accepted since the last local optimum, and no tabu
3604 // lists are active, stop the search.
3605 if (stamp_ > 0 && solution_count_ == 0 && keep_tabu_list_.empty() &&
3606 forbid_tabu_list_.empty()) {
3607 stop_search_ = true;
3608 }
3609 solution_count_ = 0;
3610 AgeLists();
3611 for (int i = 0; i < Size(); ++i) {
3612 SetCurrentInternalValue(i, std::numeric_limits<int64_t>::max());
3613 }
3614 return found_initial_solution_;
3615}
3616
3617bool TabuSearch::AcceptDelta(Assignment* delta, Assignment* deltadelta) {
3618 if (delta == nullptr) return true;
3619 if (!Metaheuristic::AcceptDelta(delta, deltadelta)) return false;
3620 if (synced_keep_tabu_list_.empty() && synced_forbid_tabu_list_.empty()) {
3621 return true;
3622 }
3623 const Assignment::IntContainer& delta_container = delta->IntVarContainer();
3624 // Detect LNS, bail out quickly in this case without filtering.
3625 for (const IntVarElement& element : delta_container.elements()) {
3626 if (!element.Bound()) return true;
3627 }
3628 delta_vars_.ResetAllToFalse();
3629 for (const IntVarElement& element : delta_container.elements()) {
3630 const int var_index = element.Var()->index();
3631 if (var_index >= var_index_to_index_.size()) continue;
3632 const int index = var_index_to_index_[var_index];
3633 if (index == -1) continue;
3634 delta_values_[index] = element.Value();
3635 delta_vars_.Set(index);
3636 }
3637 int num_respected = 0;
3638 auto get_value = [this](int var_index) {
3639 return delta_vars_[var_index]
3640 ? delta_values_[var_index]
3641 : assignment_container_.Element(var_index).Value();
3642 };
3643 const int64_t tabu_limit = TabuLimit();
3644 for (const auto [var_index, value, unused_stamp] : synced_keep_tabu_list_) {
3645 if (get_value(var_index) == value) {
3646 if (++num_respected >= tabu_limit) return true;
3647 }
3648 }
3649 for (const auto [var_index, value, unused_stamp] : synced_forbid_tabu_list_) {
3650 if (get_value(var_index) != value) {
3651 if (++num_respected >= tabu_limit) return true;
3652 }
3653 }
3654 if (num_respected >= tabu_limit) return true;
3655 // Aspiration
3656 // TODO(user): Add proper support for lex-objectives with steps.
3657 if (Size() == 1) {
3658 if (Maximize(0)) {
3659 delta->SetObjectiveMinFromIndex(0, CapAdd(BestInternalValue(0), Step(0)));
3660 } else {
3661 delta->SetObjectiveMaxFromIndex(0, CapSub(BestInternalValue(0), Step(0)));
3662 }
3663 } else {
3664 for (int i = 0; i < Size(); ++i) {
3665 if (Maximize(i)) {
3666 delta->SetObjectiveMinFromIndex(i, BestInternalValue(i));
3667 } else {
3668 delta->SetObjectiveMaxFromIndex(i, BestInternalValue(i));
3669 }
3670 }
3671 }
3672 // TODO(user): Add support for plateau removal.
3673 return true;
3674}
3675
3676void TabuSearch::AcceptNeighbor() {
3677 if (0 != stamp_) {
3678 AgeLists();
3679 }
3680}
3681
3682void TabuSearch::AgeList(int64_t tenure, TabuList* list) {
3683 while (!list->empty() && list->back().stamp < stamp_ - tenure) {
3684 list->pop_back();
3685 }
3686}
3687
3688void TabuSearch::AgeLists() {
3689 AgeList(keep_tenure_, &keep_tabu_list_);
3690 AgeList(forbid_tenure_, &forbid_tabu_list_);
3691 ++stamp_;
3692}
3693
3694class GenericTabuSearch : public TabuSearch {
3695 public:
3696 GenericTabuSearch(Solver* solver, bool maximize, IntVar* objective,
3697 int64_t step, const std::vector<IntVar*>& vars,
3698 int64_t forbid_tenure)
3699 : TabuSearch(solver, {maximize}, {objective}, {step}, vars, 0,
3700 forbid_tenure, 1) {}
3701 std::string DebugString() const override { return "Generic Tabu Search"; }
3702
3703 protected:
3704 std::vector<IntVar*> CreateTabuVars() override;
3705};
3706
3707std::vector<IntVar*> GenericTabuSearch::CreateTabuVars() {
3708 Solver* const s = solver();
3709
3710 // Tabu criterion
3711 // At least one element of the forbid_tabu_list must change value.
3712 std::vector<IntVar*> forbid_values;
3713 for (const auto [var_index, value, unused_stamp] : forbid_tabu_list()) {
3714 forbid_values.push_back(s->MakeIsDifferentCstVar(vars(var_index), value));
3715 }
3716 std::vector<IntVar*> tabu_vars;
3717 if (!forbid_values.empty()) {
3718 tabu_vars.push_back(s->MakeIsGreaterCstVar(s->MakeSum(forbid_values), 0));
3719 }
3720 return tabu_vars;
3721}
3722
3723} // namespace
3724
3726 int64_t step,
3727 const std::vector<IntVar*>& vars,
3728 int64_t keep_tenure,
3729 int64_t forbid_tenure,
3730 double tabu_factor) {
3731 return RevAlloc(new TabuSearch(this, {maximize}, {objective}, {step}, vars,
3732 keep_tenure, forbid_tenure, tabu_factor));
3733}
3734
3736 const std::vector<bool>& maximize, std::vector<IntVar*> objectives,
3737 std::vector<int64_t> steps, const std::vector<IntVar*>& vars,
3738 int64_t keep_tenure, int64_t forbid_tenure, double tabu_factor) {
3739 return RevAlloc(new TabuSearch(this, maximize, std::move(objectives),
3740 std::move(steps), vars, keep_tenure,
3741 forbid_tenure, tabu_factor));
3742}
3743
3745 bool maximize, IntVar* const v, int64_t step,
3746 const std::vector<IntVar*>& tabu_vars, int64_t forbid_tenure) {
3747 return RevAlloc(
3748 new GenericTabuSearch(this, maximize, v, step, tabu_vars, forbid_tenure));
3749}
3750
3751// ---------- Simulated Annealing ----------
3752
3753namespace {
3754class SimulatedAnnealing : public Metaheuristic {
3755 public:
3756 SimulatedAnnealing(Solver* solver, const std::vector<bool>& maximize,
3757 std::vector<IntVar*> objectives,
3758 std::vector<int64_t> steps,
3759 std::vector<int64_t> initial_temperatures);
3760 ~SimulatedAnnealing() override {}
3761 void ApplyDecision(Decision* d) override;
3762 bool AtLocalOptimum() override;
3763 void AcceptNeighbor() override;
3764 std::string DebugString() const override { return "Simulated Annealing"; }
3765
3766 private:
3767 double Temperature(int index) const {
3768 return iteration_ > 0
3769 ? (1.0 * temperature0_[index]) / iteration_ // Cauchy annealing
3770 : 0;
3771 }
3772
3773 const std::vector<int64_t> temperature0_;
3774 int64_t iteration_;
3775 std::mt19937 rand_;
3776};
3777
3778SimulatedAnnealing::SimulatedAnnealing(
3779 Solver* solver, const std::vector<bool>& maximize,
3780 std::vector<IntVar*> objectives, std::vector<int64_t> steps,
3781 std::vector<int64_t> initial_temperatures)
3782 : Metaheuristic(solver, maximize, std::move(objectives), std::move(steps)),
3783 temperature0_(std::move(initial_temperatures)),
3784 iteration_(0),
3785 rand_(CpRandomSeed()) {}
3786
3787// As a reminder, if s is the current solution, s' the new solution, s' will be
3788// accepted iff:
3789// 1) cost(s') ≤ cost(s) - step
3790// or
3791// 2) P(cost(s) - step, cost(s'), T) ≥ random(0, 1),
3792// where P(e, e', T) = exp(-(e' - e) / T).
3793// 2) is equivalent to exp(-(e' - e) / T) ≥ random(0, 1)
3794// or -(e' - e) / T ≥ log(random(0, 1))
3795// or e' - e ≤ -log(random(0, 1)) * T
3796// or e' ≤ e - log(random(0, 1)) * T.
3797// 2) can therefore be expressed as:
3798// cost(s') ≤ cost(s) - step - log(random(0, 1) * T.
3799// Note that if 1) is true, 2) will be true too as exp(-(e' - e) / T) ≥ 1.
3800void SimulatedAnnealing::ApplyDecision(Decision* const d) {
3801 Solver* const s = solver();
3802 if (d == s->balancing_decision()) {
3803 return;
3804 }
3805 if (CurrentInternalValuesAreConstraining()) {
3806 MakeMinimizationVarsLessOrEqualWithSteps([this](int i) {
3807 const double rand_double = absl::Uniform<double>(rand_, 0.0, 1.0);
3808#if defined(_MSC_VER) || defined(__ANDROID__)
3809 const double rand_log2_double = log(rand_double) / log(2.0L);
3810#else
3811 const double rand_log2_double = log2(rand_double);
3812#endif
3813 const int64_t energy_bound = Temperature(i) * rand_log2_double;
3814 // energy_bound is negative, since we want to allow higher bounds it's
3815 // subtracted from the current bound.
3816 return CapSub(CurrentInternalValue(i), energy_bound);
3817 });
3818 }
3819}
3820
3821bool SimulatedAnnealing::AtLocalOptimum() {
3822 for (int i = 0; i < Size(); ++i) {
3823 SetCurrentInternalValue(i, std::numeric_limits<int64_t>::max());
3824 }
3825 ++iteration_;
3826 if (!found_initial_solution_) return false;
3827 for (int i = 0; i < Size(); ++i) {
3828 if (Temperature(i) <= 0) return false;
3829 }
3830 return true;
3831}
3832
3833void SimulatedAnnealing::AcceptNeighbor() {
3834 if (iteration_ > 0) {
3835 ++iteration_;
3836 }
3837}
3838} // namespace
3839
3841 int64_t step,
3842 int64_t initial_temperature) {
3843 return RevAlloc(new SimulatedAnnealing(this, {maximize}, {v}, {step},
3844 {initial_temperature}));
3845}
3846
3848 const std::vector<bool>& maximize, std::vector<IntVar*> vars,
3849 std::vector<int64_t> steps, std::vector<int64_t> initial_temperatures) {
3850 return RevAlloc(new SimulatedAnnealing(this, maximize, std::move(vars),
3851 std::move(steps),
3852 std::move(initial_temperatures)));
3853}
3854
3855// ---------- Guided Local Search ----------
3856
3857namespace {
3858// GLS penalty management classes. Maintains the penalty frequency for each
3859// (variable, value) pair.
3860
3861// Dense GLS penalties implementation using a matrix to store penalties.
3862class GuidedLocalSearchPenaltiesTable {
3863 public:
3864 struct VarValue {
3865 int64_t var;
3866 int64_t value;
3867 };
3868 explicit GuidedLocalSearchPenaltiesTable(int num_vars);
3869 bool HasPenalties() const { return has_values_; }
3870 void IncrementPenalty(const VarValue& var_value);
3871 int64_t GetPenalty(const VarValue& var_value) const;
3872 void Reset();
3873
3874 private:
3875 std::vector<std::vector<int64_t>> penalties_;
3876 bool has_values_;
3877};
3878
3879GuidedLocalSearchPenaltiesTable::GuidedLocalSearchPenaltiesTable(int num_vars)
3880 : penalties_(num_vars), has_values_(false) {}
3881
3882void GuidedLocalSearchPenaltiesTable::IncrementPenalty(
3883 const VarValue& var_value) {
3884 std::vector<int64_t>& var_penalties = penalties_[var_value.var];
3885 const int64_t value = var_value.value;
3886 if (value >= var_penalties.size()) {
3887 var_penalties.resize(value + 1, 0);
3888 }
3889 ++var_penalties[value];
3890 has_values_ = true;
3891}
3892
3893void GuidedLocalSearchPenaltiesTable::Reset() {
3894 has_values_ = false;
3895 for (int i = 0; i < penalties_.size(); ++i) {
3896 penalties_[i].clear();
3897 }
3898}
3899
3900int64_t GuidedLocalSearchPenaltiesTable::GetPenalty(
3901 const VarValue& var_value) const {
3902 const std::vector<int64_t>& var_penalties = penalties_[var_value.var];
3903 const int64_t value = var_value.value;
3904 return (value >= var_penalties.size()) ? 0 : var_penalties[value];
3905}
3906
3907// Sparse GLS penalties implementation using hash_map to store penalties.
3908class GuidedLocalSearchPenaltiesMap {
3909 public:
3910 struct VarValue {
3911 int64_t var;
3912 int64_t value;
3913
3914 friend bool operator==(const VarValue& lhs, const VarValue& rhs) {
3915 return lhs.var == rhs.var && lhs.value == rhs.value;
3916 }
3917 template <typename H>
3918 friend H AbslHashValue(H h, const VarValue& var_value) {
3919 return H::combine(std::move(h), var_value.var, var_value.value);
3920 }
3921 };
3922 explicit GuidedLocalSearchPenaltiesMap(int num_vars);
3923 bool HasPenalties() const { return (!penalties_.empty()); }
3924 void IncrementPenalty(const VarValue& var_value);
3925 int64_t GetPenalty(const VarValue& var_value) const;
3926 void Reset();
3927
3928 private:
3929 Bitmap penalized_;
3930 absl::flat_hash_map<VarValue, int64_t> penalties_;
3931};
3932
3933GuidedLocalSearchPenaltiesMap::GuidedLocalSearchPenaltiesMap(int num_vars)
3934 : penalized_(num_vars, false) {}
3935
3936void GuidedLocalSearchPenaltiesMap::IncrementPenalty(
3937 const VarValue& var_value) {
3938 ++penalties_[var_value];
3939 penalized_.Set(var_value.var, true);
3940}
3941
3942void GuidedLocalSearchPenaltiesMap::Reset() {
3943 penalties_.clear();
3944 penalized_.Clear();
3945}
3946
3947int64_t GuidedLocalSearchPenaltiesMap::GetPenalty(
3948 const VarValue& var_value) const {
3949 return (penalized_.Get(var_value.var))
3950 ? gtl::FindWithDefault(penalties_, var_value)
3951 : 0;
3952}
3953
3954template <typename P>
3955class GuidedLocalSearch : public Metaheuristic {
3956 public:
3957 GuidedLocalSearch(
3958 Solver* solver, IntVar* objective, bool maximize, int64_t step,
3959 const std::vector<IntVar*>& vars, double penalty_factor,
3960 std::function<std::vector<std::pair<int64_t, int64_t>>(int64_t, int64_t)>
3961 get_equivalent_pairs,
3962 bool reset_penalties_on_new_best_solution);
3963 ~GuidedLocalSearch() override {}
3964 bool AcceptDelta(Assignment* delta, Assignment* deltadelta) override;
3965 void ApplyDecision(Decision* d) override;
3966 bool AtSolution() override;
3967 void EnterSearch() override;
3968 bool AtLocalOptimum() override;
3969 virtual int64_t AssignmentElementPenalty(int index) const = 0;
3970 virtual int64_t AssignmentPenalty(int64_t var, int64_t value) const = 0;
3971 virtual int64_t Evaluate(const Assignment* delta, int64_t current_penalty,
3972 bool incremental) = 0;
3973 virtual IntExpr* MakeElementPenalty(int index) = 0;
3974 std::string DebugString() const override { return "Guided Local Search"; }
3975
3976 protected:
3977 // Array which keeps track of modifications done. This allows to effectively
3978 // revert or commit modifications.
3979 // TODO(user): Expose this in a utility file.
3980 template <typename T, typename IndexType = int64_t>
3981 class DirtyArray {
3982 public:
3983 explicit DirtyArray(IndexType size)
3984 : base_data_(size), modified_data_(size), touched_(size) {}
3985 // Sets a value in the array. This value will be reverted if Revert() is
3986 // called.
3987 void Set(IndexType i, const T& value) {
3988 modified_data_[i] = value;
3989 touched_.Set(i);
3990 }
3991 // Same as Set() but modifies all values of the array.
3992 void SetAll(const T& value) {
3993 for (IndexType i = 0; i < modified_data_.size(); ++i) {
3994 Set(i, value);
3995 }
3996 }
3997 // Returns the modified value in the array.
3998 T Get(IndexType i) const { return modified_data_[i]; }
3999 // Commits all modifications done to the array, effectively copying all
4000 // modifications to the base values.
4001 void Commit() {
4002 for (const IndexType index : touched_.PositionsSetAtLeastOnce()) {
4003 base_data_[index] = modified_data_[index];
4004 }
4005 touched_.ResetAllToFalse();
4006 }
4007 // Reverts all modified values in the array.
4008 void Revert() {
4009 for (const IndexType index : touched_.PositionsSetAtLeastOnce()) {
4010 modified_data_[index] = base_data_[index];
4011 }
4012 touched_.ResetAllToFalse();
4013 }
4014 // Returns the number of values modified since the last call to Commit or
4015 // Revert.
4016 int NumSetValues() const {
4017 return touched_.NumberOfSetCallsWithDifferentArguments();
4018 }
4019
4020 private:
4021 std::vector<T> base_data_;
4022 std::vector<T> modified_data_;
4023 SparseBitset<IndexType> touched_;
4024 };
4025
4026 int64_t GetValue(int64_t index) const {
4027 return assignment_.Element(index).Value();
4028 }
4029 IntVar* GetVar(int64_t index) const {
4030 return assignment_.Element(index).Var();
4031 }
4032 void AddVars(const std::vector<IntVar*>& vars);
4033 int NumPrimaryVars() const { return num_vars_; }
4034 int GetLocalIndexFromVar(IntVar* var) const {
4035 const int var_index = var->index();
4036 return (var_index < var_index_to_local_index_.size())
4037 ? var_index_to_local_index_[var_index]
4038 : -1;
4039 }
4040 void ResetPenalties();
4041
4042 IntVar* penalized_objective_;
4043 Assignment::IntContainer assignment_;
4044 int64_t assignment_penalized_value_;
4045 int64_t old_penalized_value_;
4046 const int num_vars_;
4047 std::vector<int> var_index_to_local_index_;
4048 const double penalty_factor_;
4049 P penalties_;
4050 DirtyArray<int64_t> penalized_values_;
4051 bool incremental_;
4052 std::function<std::vector<std::pair<int64_t, int64_t>>(int64_t, int64_t)>
4053 get_equivalent_pairs_;
4054 const bool reset_penalties_on_new_best_solution_;
4055};
4056
4057template <typename P>
4058GuidedLocalSearch<P>::GuidedLocalSearch(
4059 Solver* solver, IntVar* objective, bool maximize, int64_t step,
4060 const std::vector<IntVar*>& vars, double penalty_factor,
4061 std::function<std::vector<std::pair<int64_t, int64_t>>(int64_t, int64_t)>
4062 get_equivalent_pairs,
4063 bool reset_penalties_on_new_best_solution)
4064 : Metaheuristic(solver, {maximize}, {objective}, {step}),
4065 penalized_objective_(nullptr),
4066 assignment_penalized_value_(0),
4067 old_penalized_value_(0),
4068 num_vars_(vars.size()),
4069 penalty_factor_(penalty_factor),
4070 penalties_(vars.size()),
4071 penalized_values_(vars.size()),
4072 incremental_(false),
4073 get_equivalent_pairs_(std::move(get_equivalent_pairs)),
4074 reset_penalties_on_new_best_solution_(
4075 reset_penalties_on_new_best_solution) {
4076 AddVars(vars);
4077}
4078
4079template <typename P>
4080void GuidedLocalSearch<P>::AddVars(const std::vector<IntVar*>& vars) {
4081 const int offset = assignment_.Size();
4082 if (vars.empty()) return;
4083 assignment_.Resize(offset + vars.size());
4084 for (int i = 0; i < vars.size(); ++i) {
4085 assignment_.AddAtPosition(vars[i], offset + i);
4086 }
4087 const int max_var_index =
4088 (*std::max_element(vars.begin(), vars.end(), [](IntVar* a, IntVar* b) {
4089 return a->index() < b->index();
4090 }))->index();
4091 if (max_var_index >= var_index_to_local_index_.size()) {
4092 var_index_to_local_index_.resize(max_var_index + 1, -1);
4093 }
4094 for (int i = 0; i < vars.size(); ++i) {
4095 var_index_to_local_index_[vars[i]->index()] = offset + i;
4096 }
4097}
4098
4099// Add the following constraint (includes aspiration criterion):
4100// if minimizing,
4101// objective =< Max(current penalized cost - penalized_objective - step,
4102// best solution cost - step)
4103// if maximizing,
4104// objective >= Min(current penalized cost - penalized_objective + step,
4105// best solution cost + step)
4106template <typename P>
4107void GuidedLocalSearch<P>::ApplyDecision(Decision* const d) {
4108 if (d == solver()->balancing_decision()) {
4109 return;
4110 }
4111 assignment_penalized_value_ = 0;
4112 if (penalties_.HasPenalties()) {
4113 // Computing sum of penalties expression.
4114 // Scope needed to avoid potential leak of elements.
4115 {
4116 std::vector<IntVar*> elements;
4117 for (int i = 0; i < num_vars_; ++i) {
4118 elements.push_back(MakeElementPenalty(i)->Var());
4119 const int64_t penalty = AssignmentElementPenalty(i);
4120 penalized_values_.Set(i, penalty);
4121 assignment_penalized_value_ =
4122 CapAdd(assignment_penalized_value_, penalty);
4123 }
4124 solver()->SaveAndSetValue(
4125 reinterpret_cast<void**>(&penalized_objective_),
4126 reinterpret_cast<void*>(solver()->MakeSum(elements)->Var()));
4127 }
4128 penalized_values_.Commit();
4129 old_penalized_value_ = assignment_penalized_value_;
4130 incremental_ = false;
4131 IntExpr* max_pen_exp = solver()->MakeDifference(
4132 CapSub(CurrentInternalValue(0), Step(0)), penalized_objective_);
4133 IntVar* max_exp =
4134 solver()
4135 ->MakeMax(max_pen_exp, CapSub(BestInternalValue(0), Step(0)))
4136 ->Var();
4137 solver()->AddConstraint(
4138 solver()->MakeLessOrEqual(MinimizationVar(0), max_exp));
4139 } else {
4140 penalized_objective_ = nullptr;
4141 const int64_t bound =
4142 (CurrentInternalValue(0) < std::numeric_limits<int64_t>::max())
4143 ? CapSub(CurrentInternalValue(0), Step(0))
4144 : CurrentInternalValue(0);
4145 MinimizationVar(0)->SetMax(bound);
4146 }
4147}
4148
4149template <typename P>
4150void GuidedLocalSearch<P>::ResetPenalties() {
4151 assignment_penalized_value_ = 0;
4152 old_penalized_value_ = 0;
4153 penalized_values_.SetAll(0);
4154 penalized_values_.Commit();
4155 penalties_.Reset();
4156}
4157
4158template <typename P>
4159bool GuidedLocalSearch<P>::AtSolution() {
4160 const int64_t old_best = BestInternalValue(0);
4162 return false;
4163 }
4164 if (penalized_objective_ != nullptr && penalized_objective_->Bound()) {
4165 // If the value of the best solution has changed (aka a new best solution
4166 // has been found), triggering a reset on the penalties to start fresh.
4167 // The immediate consequence is a greedy dive towards a local minimum,
4168 // followed by a new penalization phase.
4169 if (reset_penalties_on_new_best_solution_ &&
4170 old_best != BestInternalValue(0)) {
4171 ResetPenalties();
4172 DCHECK_EQ(CurrentInternalValue(0), BestInternalValue(0));
4173 } else {
4174 // A penalized move has been found.
4175 SetCurrentInternalValue(
4176 0, CapAdd(CurrentInternalValue(0), penalized_objective_->Value()));
4177 }
4178 }
4179 assignment_.Store();
4180 return true;
4181}
4182
4183template <typename P>
4184void GuidedLocalSearch<P>::EnterSearch() {
4185 Metaheuristic::EnterSearch();
4186 solver()->SetUseFastLocalSearch(true);
4187 penalized_objective_ = nullptr;
4188 ResetPenalties();
4189}
4190
4191// GLS filtering; compute the penalized value corresponding to the delta and
4192// modify objective bound accordingly.
4193template <typename P>
4194bool GuidedLocalSearch<P>::AcceptDelta(Assignment* delta,
4195 Assignment* deltadelta) {
4196 if (delta == nullptr && deltadelta == nullptr) return true;
4197 if (!penalties_.HasPenalties()) {
4198 return Metaheuristic::AcceptDelta(delta, deltadelta);
4199 }
4200 int64_t penalty = 0;
4201 if (!deltadelta->Empty()) {
4202 if (!incremental_) {
4203 DCHECK_EQ(penalized_values_.NumSetValues(), 0);
4204 penalty = Evaluate(delta, assignment_penalized_value_, true);
4205 } else {
4206 penalty = Evaluate(deltadelta, old_penalized_value_, true);
4207 }
4208 incremental_ = true;
4209 } else {
4210 if (incremental_) {
4211 penalized_values_.Revert();
4212 }
4213 incremental_ = false;
4214 DCHECK_EQ(penalized_values_.NumSetValues(), 0);
4215 penalty = Evaluate(delta, assignment_penalized_value_, false);
4216 }
4217 old_penalized_value_ = penalty;
4218 if (!delta->HasObjective()) {
4219 delta->AddObjective(ObjectiveVar(0));
4220 }
4221 if (delta->Objective() == ObjectiveVar(0)) {
4222 const int64_t bound =
4223 std::max(CapSub(CapSub(CurrentInternalValue(0), Step(0)), penalty),
4224 CapSub(BestInternalValue(0), Step(0)));
4225 if (Maximize(0)) {
4226 delta->SetObjectiveMin(std::max(CapOpp(bound), delta->ObjectiveMin()));
4227 } else {
4228 delta->SetObjectiveMax(std::min(bound, delta->ObjectiveMax()));
4229 }
4230 }
4231 return true;
4232}
4233
4234// Penalize (var, value) pairs of maximum utility, with
4235// utility(var, value) = cost(var, value) / (1 + penalty(var, value))
4236template <typename P>
4237bool GuidedLocalSearch<P>::AtLocalOptimum() {
4238 solver()->SetUseFastLocalSearch(false);
4239 std::vector<double> utilities(num_vars_);
4240 double max_utility = -std::numeric_limits<double>::infinity();
4241 for (int var = 0; var < num_vars_; ++var) {
4242 const IntVarElement& element = assignment_.Element(var);
4243 if (!element.Bound()) {
4244 // Never synced with a solution, problem infeasible.
4245 return false;
4246 }
4247 const int64_t value = element.Value();
4248 // The fact that we do not penalize loops is influenced by vehicle routing.
4249 // Assuming a cost of 0 in that case.
4250 const int64_t cost = (value != var) ? AssignmentPenalty(var, value) : 0;
4251 const double utility = cost / (penalties_.GetPenalty({var, value}) + 1.0);
4252 utilities[var] = utility;
4253 if (utility > max_utility) max_utility = utility;
4254 }
4255 for (int var = 0; var < num_vars_; ++var) {
4256 if (utilities[var] == max_utility) {
4257 const IntVarElement& element = assignment_.Element(var);
4258 DCHECK(element.Bound());
4259 const int64_t value = element.Value();
4260 if (get_equivalent_pairs_ == nullptr) {
4261 penalties_.IncrementPenalty({var, value});
4262 } else {
4263 for (const auto [other_var, other_value] :
4264 get_equivalent_pairs_(var, value)) {
4265 penalties_.IncrementPenalty({other_var, other_value});
4266 }
4267 }
4268 }
4269 }
4270 SetCurrentInternalValue(0, std::numeric_limits<int64_t>::max());
4271 return true;
4272}
4273
4274template <typename P>
4275class BinaryGuidedLocalSearch : public GuidedLocalSearch<P> {
4276 public:
4277 BinaryGuidedLocalSearch(
4278 Solver* solver, IntVar* objective,
4279 std::function<int64_t(int64_t, int64_t)> objective_function,
4280 bool maximize, int64_t step, const std::vector<IntVar*>& vars,
4281 double penalty_factor,
4282 std::function<std::vector<std::pair<int64_t, int64_t>>(int64_t, int64_t)>
4283 get_equivalent_pairs,
4284 bool reset_penalties_on_new_best_solution);
4285 ~BinaryGuidedLocalSearch() override {}
4286 IntExpr* MakeElementPenalty(int index) override;
4287 int64_t AssignmentElementPenalty(int index) const override;
4288 int64_t AssignmentPenalty(int64_t var, int64_t value) const override;
4289 int64_t Evaluate(const Assignment* delta, int64_t current_penalty,
4290 bool incremental) override;
4291
4292 private:
4293 int64_t PenalizedValue(int64_t i, int64_t j) const;
4294 std::function<int64_t(int64_t, int64_t)> objective_function_;
4295};
4296
4297template <typename P>
4298BinaryGuidedLocalSearch<P>::BinaryGuidedLocalSearch(
4299 Solver* const solver, IntVar* const objective,
4300 std::function<int64_t(int64_t, int64_t)> objective_function, bool maximize,
4301 int64_t step, const std::vector<IntVar*>& vars, double penalty_factor,
4302 std::function<std::vector<std::pair<int64_t, int64_t>>(int64_t, int64_t)>
4303 get_equivalent_pairs,
4304 bool reset_penalties_on_new_best_solution)
4305 : GuidedLocalSearch<P>(solver, objective, maximize, step, vars,
4306 penalty_factor, std::move(get_equivalent_pairs),
4307 reset_penalties_on_new_best_solution),
4308 objective_function_(std::move(objective_function)) {
4309 solver->SetGuidedLocalSearchPenaltyCallback(
4310 [this](int64_t i, int64_t j, int64_t) { return PenalizedValue(i, j); });
4311}
4312
4313template <typename P>
4314IntExpr* BinaryGuidedLocalSearch<P>::MakeElementPenalty(int index) {
4315 return this->solver()->MakeElement(
4316 [this, index](int64_t i) { return PenalizedValue(index, i); },
4317 this->GetVar(index));
4318}
4319
4320template <typename P>
4321int64_t BinaryGuidedLocalSearch<P>::AssignmentElementPenalty(int index) const {
4322 return PenalizedValue(index, this->GetValue(index));
4323}
4324
4325template <typename P>
4326int64_t BinaryGuidedLocalSearch<P>::AssignmentPenalty(int64_t var,
4327 int64_t value) const {
4328 return objective_function_(var, value);
4329}
4330
4331template <typename P>
4332int64_t BinaryGuidedLocalSearch<P>::Evaluate(const Assignment* delta,
4333 int64_t current_penalty,
4334 bool incremental) {
4335 int64_t penalty = current_penalty;
4336 const Assignment::IntContainer& container = delta->IntVarContainer();
4337 for (const IntVarElement& new_element : container.elements()) {
4338 const int index = this->GetLocalIndexFromVar(new_element.Var());
4339 if (index == -1) continue;
4340 penalty = CapSub(penalty, this->penalized_values_.Get(index));
4341 if (new_element.Activated()) {
4342 const int64_t new_penalty = PenalizedValue(index, new_element.Value());
4343 penalty = CapAdd(penalty, new_penalty);
4344 if (incremental) {
4345 this->penalized_values_.Set(index, new_penalty);
4346 }
4347 }
4348 }
4349 return penalty;
4350}
4351
4352// Penalized value for (i, j) = penalty_factor_ * penalty(i, j) * cost (i, j)
4353template <typename P>
4354int64_t BinaryGuidedLocalSearch<P>::PenalizedValue(int64_t i, int64_t j) const {
4355 const int64_t penalty = this->penalties_.GetPenalty({i, j});
4356 // Calls to objective_function_(i, j) can be costly.
4357 if (penalty == 0) return 0;
4358 const double penalized_value_fp =
4359 this->penalty_factor_ * penalty * objective_function_(i, j);
4360 const int64_t penalized_value =
4361 (penalized_value_fp <= std::numeric_limits<int64_t>::max())
4362 ? static_cast<int64_t>(penalized_value_fp)
4363 : std::numeric_limits<int64_t>::max();
4364 return penalized_value;
4365}
4366
4367template <typename P>
4368class TernaryGuidedLocalSearch : public GuidedLocalSearch<P> {
4369 public:
4370 TernaryGuidedLocalSearch(
4371 Solver* solver, IntVar* objective,
4372 std::function<int64_t(int64_t, int64_t, int64_t)> objective_function,
4373 bool maximize, int64_t step, const std::vector<IntVar*>& vars,
4374 const std::vector<IntVar*>& secondary_vars, double penalty_factor,
4375 std::function<std::vector<std::pair<int64_t, int64_t>>(int64_t, int64_t)>
4376 get_equivalent_pairs,
4377 bool reset_penalties_on_new_best_solution);
4378 ~TernaryGuidedLocalSearch() override {}
4379 IntExpr* MakeElementPenalty(int index) override;
4380 int64_t AssignmentElementPenalty(int index) const override;
4381 int64_t AssignmentPenalty(int64_t var, int64_t value) const override;
4382 int64_t Evaluate(const Assignment* delta, int64_t current_penalty,
4383 bool incremental) override;
4384
4385 private:
4386 int64_t PenalizedValue(int64_t i, int64_t j, int64_t k) const;
4387
4388 std::function<int64_t(int64_t, int64_t, int64_t)> objective_function_;
4389 std::vector<int> secondary_values_;
4390};
4391
4392template <typename P>
4393TernaryGuidedLocalSearch<P>::TernaryGuidedLocalSearch(
4394 Solver* const solver, IntVar* const objective,
4395 std::function<int64_t(int64_t, int64_t, int64_t)> objective_function,
4396 bool maximize, int64_t step, const std::vector<IntVar*>& vars,
4397 const std::vector<IntVar*>& secondary_vars, double penalty_factor,
4398 std::function<std::vector<std::pair<int64_t, int64_t>>(int64_t, int64_t)>
4399 get_equivalent_pairs,
4400 bool reset_penalties_on_new_best_solution)
4401 : GuidedLocalSearch<P>(solver, objective, maximize, step, vars,
4402 penalty_factor, std::move(get_equivalent_pairs),
4403 reset_penalties_on_new_best_solution),
4404 objective_function_(std::move(objective_function)),
4405 secondary_values_(this->NumPrimaryVars(), -1) {
4406 this->AddVars(secondary_vars);
4407 solver->SetGuidedLocalSearchPenaltyCallback(
4408 [this](int64_t i, int64_t j, int64_t k) {
4409 return PenalizedValue(i, j, k);
4410 });
4411}
4412
4413template <typename P>
4414IntExpr* TernaryGuidedLocalSearch<P>::MakeElementPenalty(int index) {
4415 Solver* const solver = this->solver();
4416 IntVar* var = solver->MakeIntVar(0, kint64max);
4417 solver->AddConstraint(solver->MakeLightElement(
4418 [this, index](int64_t j, int64_t k) {
4419 return PenalizedValue(index, j, k);
4420 },
4421 var, this->GetVar(index), this->GetVar(this->NumPrimaryVars() + index)));
4422 return var;
4423}
4424
4425template <typename P>
4426int64_t TernaryGuidedLocalSearch<P>::AssignmentElementPenalty(int index) const {
4427 return PenalizedValue(index, this->GetValue(index),
4428 this->GetValue(this->NumPrimaryVars() + index));
4429}
4430
4431template <typename P>
4432int64_t TernaryGuidedLocalSearch<P>::AssignmentPenalty(int64_t var,
4433 int64_t value) const {
4434 return objective_function_(var, value,
4435 this->GetValue(this->NumPrimaryVars() + var));
4436}
4437
4438template <typename P>
4439int64_t TernaryGuidedLocalSearch<P>::Evaluate(const Assignment* delta,
4440 int64_t current_penalty,
4441 bool incremental) {
4442 int64_t penalty = current_penalty;
4443 const Assignment::IntContainer& container = delta->IntVarContainer();
4444 // Collect values for each secondary variable, matching them with their
4445 // corresponding primary variable. Making sure all secondary values are -1 if
4446 // unset.
4447 for (const IntVarElement& new_element : container.elements()) {
4448 const int index = this->GetLocalIndexFromVar(new_element.Var());
4449 if (index != -1 && index < this->NumPrimaryVars()) { // primary variable
4450 secondary_values_[index] = -1;
4451 }
4452 }
4453 for (const IntVarElement& new_element : container.elements()) {
4454 const int index = this->GetLocalIndexFromVar(new_element.Var());
4455 if (!new_element.Activated()) continue;
4456 if (index != -1 && index >= this->NumPrimaryVars()) { // secondary variable
4457 secondary_values_[index - this->NumPrimaryVars()] = new_element.Value();
4458 }
4459 }
4460 for (const IntVarElement& new_element : container.elements()) {
4461 const int index = this->GetLocalIndexFromVar(new_element.Var());
4462 // Only process primary variables.
4463 if (index == -1 || index >= this->NumPrimaryVars()) {
4464 continue;
4465 }
4466 penalty = CapSub(penalty, this->penalized_values_.Get(index));
4467 // Performed and active.
4468 if (new_element.Activated() && secondary_values_[index] != -1) {
4469 const int64_t new_penalty =
4470 PenalizedValue(index, new_element.Value(), secondary_values_[index]);
4471 penalty = CapAdd(penalty, new_penalty);
4472 if (incremental) {
4473 this->penalized_values_.Set(index, new_penalty);
4474 }
4475 }
4476 }
4477 return penalty;
4478}
4479
4480// Penalized value for (i, j) = penalty_factor_ * penalty(i, j) * cost (i, j, k)
4481template <typename P>
4482int64_t TernaryGuidedLocalSearch<P>::PenalizedValue(int64_t i, int64_t j,
4483 int64_t k) const {
4484 const int64_t penalty = this->penalties_.GetPenalty({i, j});
4485 // Calls to objective_function_(i, j, k) can be costly.
4486 if (penalty == 0) return 0;
4487 const double penalized_value_fp =
4488 this->penalty_factor_ * penalty * objective_function_(i, j, k);
4489 const int64_t penalized_value =
4490 (penalized_value_fp < std::numeric_limits<int64_t>::max())
4491 ? static_cast<int64_t>(penalized_value_fp)
4492 : std::numeric_limits<int64_t>::max();
4493 return penalized_value;
4494}
4495} // namespace
4496
4498 bool maximize, IntVar* const objective,
4499 Solver::IndexEvaluator2 objective_function, int64_t step,
4500 const std::vector<IntVar*>& vars, double penalty_factor,
4501 std::function<std::vector<std::pair<int64_t, int64_t>>(int64_t, int64_t)>
4502 get_equivalent_pairs,
4503 bool reset_penalties_on_new_best_solution) {
4504 if (absl::GetFlag(FLAGS_cp_use_sparse_gls_penalties)) {
4505 return RevAlloc(new BinaryGuidedLocalSearch<GuidedLocalSearchPenaltiesMap>(
4506 this, objective, std::move(objective_function), maximize, step, vars,
4507 penalty_factor, std::move(get_equivalent_pairs),
4508 reset_penalties_on_new_best_solution));
4509 } else {
4510 return RevAlloc(
4511 new BinaryGuidedLocalSearch<GuidedLocalSearchPenaltiesTable>(
4512 this, objective, std::move(objective_function), maximize, step,
4513 vars, penalty_factor, std::move(get_equivalent_pairs),
4514 reset_penalties_on_new_best_solution));
4515 }
4516}
4517
4519 bool maximize, IntVar* const objective,
4520 Solver::IndexEvaluator3 objective_function, int64_t step,
4521 const std::vector<IntVar*>& vars,
4522 const std::vector<IntVar*>& secondary_vars, double penalty_factor,
4523 std::function<std::vector<std::pair<int64_t, int64_t>>(int64_t, int64_t)>
4524 get_equivalent_pairs,
4525 bool reset_penalties_on_new_best_solution) {
4526 if (absl::GetFlag(FLAGS_cp_use_sparse_gls_penalties)) {
4527 return RevAlloc(new TernaryGuidedLocalSearch<GuidedLocalSearchPenaltiesMap>(
4528 this, objective, std::move(objective_function), maximize, step, vars,
4529 secondary_vars, penalty_factor, std::move(get_equivalent_pairs),
4530 reset_penalties_on_new_best_solution));
4531 } else {
4532 return RevAlloc(
4533 new TernaryGuidedLocalSearch<GuidedLocalSearchPenaltiesTable>(
4534 this, objective, std::move(objective_function), maximize, step,
4535 vars, secondary_vars, penalty_factor,
4536 std::move(get_equivalent_pairs),
4537 reset_penalties_on_new_best_solution));
4538 }
4539}
4540
4541// ---------- Search Limits ----------
4542
4543// ----- Base Class -----
4544
4546
4553
4555 crossed_ = false;
4556 Init();
4557}
4558
4560 PeriodicCheck();
4561 TopPeriodicCheck();
4562}
4563
4565 PeriodicCheck();
4566 TopPeriodicCheck();
4567}
4568
4570 if (crossed_ || Check()) {
4571 crossed_ = true;
4572 solver()->Fail();
4573 }
4574}
4575
4576void SearchLimit::TopPeriodicCheck() {
4577 if (solver()->TopLevelSearch() != solver()->ActiveSearch()) {
4579 }
4580}
4581
4582// ----- Regular Limit -----
4583
4584RegularLimit::RegularLimit(Solver* const s, absl::Duration time,
4585 int64_t branches, int64_t failures,
4586 int64_t solutions, bool smart_time_check,
4587 bool cumulative)
4588 : SearchLimit(s),
4589 duration_limit_(time),
4590 solver_time_at_limit_start_(s->Now()),
4591 last_time_elapsed_(absl::ZeroDuration()),
4592 check_count_(0),
4593 next_check_(0),
4594 smart_time_check_(smart_time_check),
4595 branches_(branches),
4596 branches_offset_(0),
4597 failures_(failures),
4598 failures_offset_(0),
4599 solutions_(solutions),
4600 solutions_offset_(0),
4601 cumulative_(cumulative) {}
4602
4604
4612
4613void RegularLimit::Copy(const SearchLimit* const limit) {
4614 const RegularLimit* const regular =
4615 reinterpret_cast<const RegularLimit* const>(limit);
4616 duration_limit_ = regular->duration_limit_;
4617 branches_ = regular->branches_;
4618 failures_ = regular->failures_;
4619 solutions_ = regular->solutions_;
4620 smart_time_check_ = regular->smart_time_check_;
4621 cumulative_ = regular->cumulative_;
4622}
4623
4625
4627 Solver* const s = solver();
4628 return s->MakeLimit(wall_time(), branches_, failures_, solutions_,
4629 smart_time_check_);
4630}
4631
4632bool RegularLimit::CheckWithOffset(absl::Duration offset) {
4633 Solver* const s = solver();
4634 // Warning limits might be kint64max, do not move the offset to the rhs
4635 return s->branches() - branches_offset_ >= branches_ ||
4636 s->failures() - failures_offset_ >= failures_ || CheckTime(offset) ||
4637 s->solutions() - solutions_offset_ >= solutions_;
4638}
4639
4641 Solver* const s = solver();
4642 int64_t progress = GetPercent(s->branches(), branches_offset_, branches_);
4643 progress = std::max(progress,
4644 GetPercent(s->failures(), failures_offset_, failures_));
4645 progress = std::max(
4646 progress, GetPercent(s->solutions(), solutions_offset_, solutions_));
4647 if (duration_limit() != absl::InfiniteDuration()) {
4648 progress = std::max(progress, (100 * TimeElapsed()) / duration_limit());
4649 }
4650 return progress;
4651}
4652
4654 Solver* const s = solver();
4655 branches_offset_ = s->branches();
4656 failures_offset_ = s->failures();
4657 solver_time_at_limit_start_ = s->Now();
4658 last_time_elapsed_ = absl::ZeroDuration();
4659 solutions_offset_ = s->solutions();
4660 check_count_ = 0;
4661 next_check_ = 0;
4662}
4663
4665 if (cumulative_) {
4666 // Reduce the limits by the amount consumed during this search
4667 Solver* const s = solver();
4668 branches_ -= s->branches() - branches_offset_;
4669 failures_ -= s->failures() - failures_offset_;
4670 duration_limit_ -= s->Now() - solver_time_at_limit_start_;
4671 solutions_ -= s->solutions() - solutions_offset_;
4672 }
4673}
4674
4675void RegularLimit::UpdateLimits(absl::Duration time, int64_t branches,
4676 int64_t failures, int64_t solutions) {
4677 Init();
4678 duration_limit_ = time;
4679 branches_ = branches;
4680 failures_ = failures;
4681 solutions_ = solutions;
4682}
4683
4685 Solver* const s = solver();
4686 return s->solutions() + s->unchecked_solutions() - solutions_offset_ >=
4687 solutions_;
4688}
4689
4690std::string RegularLimit::DebugString() const {
4691 return absl::StrFormat(
4692 "RegularLimit(crossed = %i, duration_limit = %s, "
4693 "branches = %d, failures = %d, solutions = %d cumulative = %s",
4694 crossed(), absl::FormatDuration(duration_limit()), branches_, failures_,
4695 solutions_, (cumulative_ ? "true" : "false"));
4696}
4697
4712
4713bool RegularLimit::CheckTime(absl::Duration offset) {
4714 return TimeElapsed() >= duration_limit() - offset;
4715}
4716
4717absl::Duration RegularLimit::TimeElapsed() {
4718 const int64_t kMaxSkip = 100;
4719 const int64_t kCheckWarmupIterations = 100;
4720 ++check_count_;
4721 if (duration_limit() != absl::InfiniteDuration() &&
4722 next_check_ <= check_count_) {
4723 Solver* const s = solver();
4724 absl::Duration elapsed = s->Now() - solver_time_at_limit_start_;
4725 if (smart_time_check_ && check_count_ > kCheckWarmupIterations &&
4726 elapsed > absl::ZeroDuration()) {
4727 const int64_t estimated_check_count_at_limit = MathUtil::FastInt64Round(
4728 check_count_ * absl::FDivDuration(duration_limit_, elapsed));
4729 next_check_ =
4730 std::min(check_count_ + kMaxSkip, estimated_check_count_at_limit);
4731 }
4732 last_time_elapsed_ = elapsed;
4733 }
4734 return last_time_elapsed_;
4735}
4736
4738 return MakeLimit(time, std::numeric_limits<int64_t>::max(),
4739 std::numeric_limits<int64_t>::max(),
4740 std::numeric_limits<int64_t>::max(),
4741 /*smart_time_check=*/false, /*cumulative=*/false);
4742}
4743
4745 return MakeLimit(absl::InfiniteDuration(), branches,
4746 std::numeric_limits<int64_t>::max(),
4747 std::numeric_limits<int64_t>::max(),
4748 /*smart_time_check=*/false, /*cumulative=*/false);
4749}
4750
4752 return MakeLimit(absl::InfiniteDuration(),
4753 std::numeric_limits<int64_t>::max(), failures,
4754 std::numeric_limits<int64_t>::max(),
4755 /*smart_time_check=*/false, /*cumulative=*/false);
4756}
4757
4759 return MakeLimit(absl::InfiniteDuration(),
4760 std::numeric_limits<int64_t>::max(),
4761 std::numeric_limits<int64_t>::max(), solutions,
4762 /*smart_time_check=*/false, /*cumulative=*/false);
4763}
4764
4766 int64_t failures, int64_t solutions,
4767 bool smart_time_check, bool cumulative) {
4768 return MakeLimit(absl::Milliseconds(time), branches, failures, solutions,
4769 smart_time_check, cumulative);
4770}
4771
4772RegularLimit* Solver::MakeLimit(absl::Duration time, int64_t branches,
4773 int64_t failures, int64_t solutions,
4774 bool smart_time_check, bool cumulative) {
4775 return RevAlloc(new RegularLimit(this, time, branches, failures, solutions,
4776 smart_time_check, cumulative));
4777}
4778
4780 return MakeLimit(proto.time() == std::numeric_limits<int64_t>::max()
4781 ? absl::InfiniteDuration()
4782 : absl::Milliseconds(proto.time()),
4783 proto.branches(), proto.failures(), proto.solutions(),
4784 proto.smart_time_check(), proto.cumulative());
4785}
4786
4789 proto.set_time(std::numeric_limits<int64_t>::max());
4790 proto.set_branches(std::numeric_limits<int64_t>::max());
4791 proto.set_failures(std::numeric_limits<int64_t>::max());
4792 proto.set_solutions(std::numeric_limits<int64_t>::max());
4793 proto.set_smart_time_check(false);
4794 proto.set_cumulative(false);
4795 return proto;
4796}
4797
4798// ----- Improvement Search Limit -----
4799
4801 Solver* solver, IntVar* objective_var, bool maximize,
4802 double objective_scaling_factor, double objective_offset,
4803 double improvement_rate_coefficient,
4804 int improvement_rate_solutions_distance)
4805 : ImprovementSearchLimit(solver, std::vector<IntVar*>{objective_var},
4806 std::vector<bool>{maximize},
4807 std::vector<double>{objective_scaling_factor},
4808 std::vector<double>{objective_offset},
4809 improvement_rate_coefficient,
4810 improvement_rate_solutions_distance) {}
4811
4813 Solver* solver, std::vector<IntVar*> objective_vars,
4814 std::vector<bool> maximize, std::vector<double> objective_scaling_factors,
4815 std::vector<double> objective_offsets, double improvement_rate_coefficient,
4816 int improvement_rate_solutions_distance)
4818 objective_vars_(std::move(objective_vars)),
4819 maximize_(std::move(maximize)),
4820 objective_scaling_factors_(std::move(objective_scaling_factors)),
4821 objective_offsets_(std::move(objective_offsets)),
4822 improvement_rate_coefficient_(improvement_rate_coefficient),
4823 improvement_rate_solutions_distance_(improvement_rate_solutions_distance),
4824 best_objectives_(objective_vars_.size()),
4825 improvements_(objective_vars_.size()),
4826 thresholds_(objective_vars_.size(),
4827 std::numeric_limits<double>::infinity()) {
4828 Init();
4829}
4830
4832
4837
4839 for (int i = 0; i < objective_vars_.size(); ++i) {
4840 best_objectives_[i] = std::numeric_limits<double>::infinity();
4841 improvements_[i].clear();
4842 thresholds_[i] = std::numeric_limits<double>::infinity();
4843 }
4844 objective_updated_ = false;
4845 gradient_stage_ = true;
4846}
4847
4849 const ImprovementSearchLimit* const improv =
4850 reinterpret_cast<const ImprovementSearchLimit* const>(limit);
4851 objective_vars_ = improv->objective_vars_;
4852 maximize_ = improv->maximize_;
4853 objective_scaling_factors_ = improv->objective_scaling_factors_;
4854 objective_offsets_ = improv->objective_offsets_;
4855 improvement_rate_coefficient_ = improv->improvement_rate_coefficient_;
4856 improvement_rate_solutions_distance_ =
4857 improv->improvement_rate_solutions_distance_;
4858 improvements_ = improv->improvements_;
4859 thresholds_ = improv->thresholds_;
4860 best_objectives_ = improv->best_objectives_;
4861 objective_updated_ = improv->objective_updated_;
4862 gradient_stage_ = improv->gradient_stage_;
4863}
4864
4867 solver(), objective_vars_, maximize_, objective_scaling_factors_,
4868 objective_offsets_, improvement_rate_coefficient_,
4869 improvement_rate_solutions_distance_));
4870}
4871
4873 if (!objective_updated_) {
4874 return false;
4875 }
4876 objective_updated_ = false;
4877
4878 std::vector<double> improvement_rates(improvements_.size());
4879 for (int i = 0; i < improvements_.size(); ++i) {
4880 if (improvements_[i].size() <= improvement_rate_solutions_distance_) {
4881 return false;
4882 }
4883
4884 const auto [cur_obj, cur_neighbors] = improvements_[i].back();
4885 const auto [prev_obj, prev_neighbors] = improvements_[i].front();
4886 DCHECK_GT(cur_neighbors, prev_neighbors);
4887 improvement_rates[i] =
4888 (prev_obj - cur_obj) / (cur_neighbors - prev_neighbors);
4889 if (gradient_stage_) continue;
4890 const double scaled_improvement_rate =
4891 improvement_rate_coefficient_ * improvement_rates[i];
4892 if (scaled_improvement_rate < thresholds_[i]) {
4893 return true;
4894 } else if (scaled_improvement_rate > thresholds_[i]) {
4895 return false;
4896 }
4897 }
4898 if (gradient_stage_ && std::lexicographical_compare(
4899 improvement_rates.begin(), improvement_rates.end(),
4900 thresholds_.begin(), thresholds_.end())) {
4901 thresholds_ = std::move(improvement_rates);
4902 }
4903 return false;
4904}
4905
4907 std::vector<double> scaled_new_objectives(objective_vars_.size());
4908 for (int i = 0; i < objective_vars_.size(); ++i) {
4909 const int64_t new_objective =
4910 objective_vars_[i] != nullptr && objective_vars_[i]->Bound()
4911 ? objective_vars_[i]->Min()
4912 : (maximize_[i] ? solver()
4915 : solver()
4918 // To simplify, we'll consider minimization only in the rest of the code,
4919 // which requires taking the opposite of the objective value if maximizing.
4920 scaled_new_objectives[i] = (maximize_[i] ? -objective_scaling_factors_[i]
4921 : objective_scaling_factors_[i]) *
4922 (new_objective + objective_offsets_[i]);
4923 }
4924 const bool is_improvement = std::lexicographical_compare(
4925 scaled_new_objectives.begin(), scaled_new_objectives.end(),
4926 best_objectives_.begin(), best_objectives_.end());
4927 if (gradient_stage_ && !is_improvement) {
4928 gradient_stage_ = false;
4929 // In case we haven't got enough solutions during the first stage, the
4930 // limit never stops the search.
4931 for (int i = 0; i < objective_vars_.size(); ++i) {
4932 if (thresholds_[i] == std::numeric_limits<double>::infinity()) {
4933 thresholds_[i] = -1;
4934 }
4935 }
4936 }
4937
4938 if (is_improvement) {
4939 objective_updated_ = true;
4940 for (int i = 0; i < objective_vars_.size(); ++i) {
4941 improvements_[i].push_back(
4942 std::make_pair(scaled_new_objectives[i], solver()->neighbors()));
4943 // We need to have 'improvement_rate_solutions_distance_' + 1 element in
4944 // the 'improvements_', so the distance between improvements is
4945 // 'improvement_rate_solutions_distance_'.
4946 if (improvements_[i].size() - 1 > improvement_rate_solutions_distance_) {
4947 improvements_[i].pop_front();
4948 }
4949 DCHECK_LE(improvements_[i].size() - 1,
4950 improvement_rate_solutions_distance_);
4951 }
4952 best_objectives_ = std::move(scaled_new_objectives);
4953 }
4954 return true;
4955}
4956
4958 IntVar* objective_var, bool maximize, double objective_scaling_factor,
4959 double objective_offset, double improvement_rate_coefficient,
4960 int improvement_rate_solutions_distance) {
4962 this, objective_var, maximize, objective_scaling_factor, objective_offset,
4963 improvement_rate_coefficient, improvement_rate_solutions_distance));
4964}
4965
4967 std::vector<IntVar*> objective_vars, std::vector<bool> maximize,
4968 std::vector<double> objective_scaling_factors,
4969 std::vector<double> objective_offsets, double improvement_rate_coefficient,
4970 int improvement_rate_solutions_distance) {
4972 this, std::move(objective_vars), std::move(maximize),
4973 std::move(objective_scaling_factors), std::move(objective_offsets),
4974 improvement_rate_coefficient, improvement_rate_solutions_distance));
4975}
4976
4977// A limit whose Check function is the OR of two underlying limits.
4978namespace {
4979class ORLimit : public SearchLimit {
4980 public:
4981 ORLimit(SearchLimit* limit_1, SearchLimit* limit_2)
4982 : SearchLimit(limit_1->solver()), limit_1_(limit_1), limit_2_(limit_2) {
4983 CHECK(limit_1 != nullptr);
4984 CHECK(limit_2 != nullptr);
4985 CHECK_EQ(limit_1->solver(), limit_2->solver())
4986 << "Illegal arguments: cannot combines limits that belong to different "
4987 << "solvers, because the reversible allocations could delete one and "
4988 << "not the other.";
4989 }
4990
4991 bool CheckWithOffset(absl::Duration offset) override {
4992 // Check being non-const, there may be side effects. So we always call both
4993 // checks.
4994 const bool check_1 = limit_1_->CheckWithOffset(offset);
4995 const bool check_2 = limit_2_->CheckWithOffset(offset);
4996 return check_1 || check_2;
4997 }
4998
4999 void Init() override {
5000 limit_1_->Init();
5001 limit_2_->Init();
5002 }
5003
5004 void Copy(const SearchLimit* const) override {
5005 LOG(FATAL) << "Not implemented.";
5006 }
5007
5008 SearchLimit* MakeClone() const override {
5009 // Deep cloning: the underlying limits are cloned, too.
5010 return solver()->MakeLimit(limit_1_->MakeClone(), limit_2_->MakeClone());
5011 }
5012
5013 void EnterSearch() override {
5014 limit_1_->EnterSearch();
5015 limit_2_->EnterSearch();
5016 }
5017 void BeginNextDecision(DecisionBuilder* const b) override {
5018 limit_1_->BeginNextDecision(b);
5019 limit_2_->BeginNextDecision(b);
5020 }
5021 void PeriodicCheck() override {
5022 limit_1_->PeriodicCheck();
5023 limit_2_->PeriodicCheck();
5024 }
5025 void RefuteDecision(Decision* const d) override {
5026 limit_1_->RefuteDecision(d);
5027 limit_2_->RefuteDecision(d);
5028 }
5029 std::string DebugString() const override {
5030 return absl::StrCat("OR limit (", limit_1_->DebugString(), " OR ",
5031 limit_2_->DebugString(), ")");
5032 }
5033
5034 private:
5035 SearchLimit* const limit_1_;
5036 SearchLimit* const limit_2_;
5037};
5038} // namespace
5039
5041 SearchLimit* const limit_2) {
5042 return RevAlloc(new ORLimit(limit_1, limit_2));
5043}
5044
5045namespace {
5046class CustomLimit : public SearchLimit {
5047 public:
5048 CustomLimit(Solver* s, std::function<bool()> limiter);
5049 bool CheckWithOffset(absl::Duration offset) override;
5050 void Init() override;
5051 void Copy(const SearchLimit* limit) override;
5052 SearchLimit* MakeClone() const override;
5053
5054 private:
5055 std::function<bool()> limiter_;
5056};
5057
5058CustomLimit::CustomLimit(Solver* const s, std::function<bool()> limiter)
5059 : SearchLimit(s), limiter_(std::move(limiter)) {}
5060
5061bool CustomLimit::CheckWithOffset(absl::Duration) {
5062 // TODO(user): Consider the offset in limiter_.
5063 if (limiter_) return limiter_();
5064 return false;
5065}
5066
5067void CustomLimit::Init() {}
5068
5069void CustomLimit::Copy(const SearchLimit* const limit) {
5070 const CustomLimit* const custom =
5071 reinterpret_cast<const CustomLimit* const>(limit);
5072 limiter_ = custom->limiter_;
5073}
5074
5075SearchLimit* CustomLimit::MakeClone() const {
5076 return solver()->RevAlloc(new CustomLimit(solver(), limiter_));
5077}
5078} // namespace
5079
5080SearchLimit* Solver::MakeCustomLimit(std::function<bool()> limiter) {
5081 return RevAlloc(new CustomLimit(this, std::move(limiter)));
5082}
5083
5084// ---------- SolveOnce ----------
5085
5086namespace {
5087class SolveOnce : public DecisionBuilder {
5088 public:
5089 explicit SolveOnce(DecisionBuilder* const db) : db_(db) {
5090 CHECK(db != nullptr);
5091 }
5092
5093 SolveOnce(DecisionBuilder* const db,
5094 const std::vector<SearchMonitor*>& monitors)
5095 : db_(db), monitors_(monitors) {
5096 CHECK(db != nullptr);
5097 }
5098
5099 ~SolveOnce() override {}
5100
5101 Decision* Next(Solver* s) override {
5102 bool res = s->SolveAndCommit(db_, monitors_);
5103 if (!res) {
5104 s->Fail();
5105 }
5106 return nullptr;
5107 }
5108
5109 std::string DebugString() const override {
5110 return absl::StrFormat("SolveOnce(%s)", db_->DebugString());
5111 }
5112
5113 void Accept(ModelVisitor* const visitor) const override {
5114 db_->Accept(visitor);
5115 }
5116
5117 private:
5118 DecisionBuilder* const db_;
5119 std::vector<SearchMonitor*> monitors_;
5120};
5121} // namespace
5122
5124 return RevAlloc(new SolveOnce(db));
5125}
5126
5128 SearchMonitor* const monitor1) {
5129 std::vector<SearchMonitor*> monitors;
5130 monitors.push_back(monitor1);
5131 return RevAlloc(new SolveOnce(db, monitors));
5132}
5133
5135 SearchMonitor* const monitor1,
5136 SearchMonitor* const monitor2) {
5137 std::vector<SearchMonitor*> monitors;
5138 monitors.push_back(monitor1);
5139 monitors.push_back(monitor2);
5140 return RevAlloc(new SolveOnce(db, monitors));
5141}
5142
5144 SearchMonitor* const monitor1,
5145 SearchMonitor* const monitor2,
5146 SearchMonitor* const monitor3) {
5147 std::vector<SearchMonitor*> monitors;
5148 monitors.push_back(monitor1);
5149 monitors.push_back(monitor2);
5150 monitors.push_back(monitor3);
5151 return RevAlloc(new SolveOnce(db, monitors));
5152}
5153
5155 SearchMonitor* const monitor1,
5156 SearchMonitor* const monitor2,
5157 SearchMonitor* const monitor3,
5158 SearchMonitor* const monitor4) {
5159 std::vector<SearchMonitor*> monitors;
5160 monitors.push_back(monitor1);
5161 monitors.push_back(monitor2);
5162 monitors.push_back(monitor3);
5163 monitors.push_back(monitor4);
5164 return RevAlloc(new SolveOnce(db, monitors));
5165}
5166
5168 DecisionBuilder* const db, const std::vector<SearchMonitor*>& monitors) {
5169 return RevAlloc(new SolveOnce(db, monitors));
5170}
5171
5172// ---------- NestedOptimize ----------
5173
5174namespace {
5175class NestedOptimize : public DecisionBuilder {
5176 public:
5177 NestedOptimize(DecisionBuilder* const db, Assignment* const solution,
5178 bool maximize, int64_t step)
5179 : db_(db),
5180 solution_(solution),
5181 maximize_(maximize),
5182 step_(step),
5183 collector_(nullptr) {
5184 CHECK(db != nullptr);
5185 CHECK(solution != nullptr);
5186 CHECK(solution->HasObjective());
5187 AddMonitors();
5188 }
5189
5190 NestedOptimize(DecisionBuilder* const db, Assignment* const solution,
5191 bool maximize, int64_t step,
5192 const std::vector<SearchMonitor*>& monitors)
5193 : db_(db),
5194 solution_(solution),
5195 maximize_(maximize),
5196 step_(step),
5197 monitors_(monitors),
5198 collector_(nullptr) {
5199 CHECK(db != nullptr);
5200 CHECK(solution != nullptr);
5201 CHECK(solution->HasObjective());
5202 AddMonitors();
5203 }
5204
5205 void AddMonitors() {
5206 Solver* const solver = solution_->solver();
5207 collector_ = solver->MakeLastSolutionCollector(solution_);
5208 monitors_.push_back(collector_);
5209 OptimizeVar* const optimize =
5210 solver->MakeOptimize(maximize_, solution_->Objective(), step_);
5211 monitors_.push_back(optimize);
5212 }
5213
5214 Decision* Next(Solver* solver) override {
5215 solver->Solve(db_, monitors_);
5216 if (collector_->solution_count() == 0) {
5217 solver->Fail();
5218 }
5219 collector_->solution(0)->Restore();
5220 return nullptr;
5221 }
5222
5223 std::string DebugString() const override {
5224 return absl::StrFormat("NestedOptimize(db = %s, maximize = %d, step = %d)",
5225 db_->DebugString(), maximize_, step_);
5226 }
5227
5228 void Accept(ModelVisitor* const visitor) const override {
5229 db_->Accept(visitor);
5230 }
5231
5232 private:
5233 DecisionBuilder* const db_;
5234 Assignment* const solution_;
5235 const bool maximize_;
5236 const int64_t step_;
5237 std::vector<SearchMonitor*> monitors_;
5238 SolutionCollector* collector_;
5239};
5240} // namespace
5241
5243 Assignment* const solution,
5244 bool maximize, int64_t step) {
5245 return RevAlloc(new NestedOptimize(db, solution, maximize, step));
5246}
5247
5249 Assignment* const solution,
5250 bool maximize, int64_t step,
5251 SearchMonitor* const monitor1) {
5252 std::vector<SearchMonitor*> monitors;
5253 monitors.push_back(monitor1);
5254 return RevAlloc(new NestedOptimize(db, solution, maximize, step, monitors));
5255}
5256
5258 Assignment* const solution,
5259 bool maximize, int64_t step,
5260 SearchMonitor* const monitor1,
5261 SearchMonitor* const monitor2) {
5262 std::vector<SearchMonitor*> monitors;
5263 monitors.push_back(monitor1);
5264 monitors.push_back(monitor2);
5265 return RevAlloc(new NestedOptimize(db, solution, maximize, step, monitors));
5266}
5267
5269 Assignment* const solution,
5270 bool maximize, int64_t step,
5271 SearchMonitor* const monitor1,
5272 SearchMonitor* const monitor2,
5273 SearchMonitor* const monitor3) {
5274 std::vector<SearchMonitor*> monitors;
5275 monitors.push_back(monitor1);
5276 monitors.push_back(monitor2);
5277 monitors.push_back(monitor3);
5278 return RevAlloc(new NestedOptimize(db, solution, maximize, step, monitors));
5279}
5280
5282 DecisionBuilder* const db, Assignment* const solution, bool maximize,
5283 int64_t step, SearchMonitor* const monitor1, SearchMonitor* const monitor2,
5284 SearchMonitor* const monitor3, SearchMonitor* const monitor4) {
5285 std::vector<SearchMonitor*> monitors;
5286 monitors.push_back(monitor1);
5287 monitors.push_back(monitor2);
5288 monitors.push_back(monitor3);
5289 monitors.push_back(monitor4);
5290 return RevAlloc(new NestedOptimize(db, solution, maximize, step, monitors));
5291}
5292
5294 DecisionBuilder* const db, Assignment* const solution, bool maximize,
5295 int64_t step, const std::vector<SearchMonitor*>& monitors) {
5296 return RevAlloc(new NestedOptimize(db, solution, maximize, step, monitors));
5297}
5298
5299// ---------- Restart ----------
5300
5301namespace {
5302// Luby Strategy
5303int64_t NextLuby(int i) {
5304 DCHECK_GT(i, 0);
5305 DCHECK_LT(i, std::numeric_limits<int32_t>::max());
5306 int64_t power;
5307
5308 // let's find the least power of 2 >= (i+1).
5309 power = 2;
5310 // Cannot overflow, because bounded by kint32max + 1.
5311 while (power < (i + 1)) {
5312 power <<= 1;
5313 }
5314 if (power == i + 1) {
5315 return (power / 2);
5316 }
5317 return NextLuby(i - (power / 2) + 1);
5318}
5319
5320class LubyRestart : public SearchMonitor {
5321 public:
5322 LubyRestart(Solver* const s, int scale_factor)
5323 : SearchMonitor(s),
5324 scale_factor_(scale_factor),
5325 iteration_(1),
5326 current_fails_(0),
5327 next_step_(scale_factor) {
5328 CHECK_GE(scale_factor, 1);
5329 }
5330
5331 ~LubyRestart() override {}
5332
5333 void BeginFail() override {
5334 if (++current_fails_ >= next_step_) {
5335 current_fails_ = 0;
5336 next_step_ = NextLuby(++iteration_) * scale_factor_;
5337 solver()->RestartCurrentSearch();
5338 }
5339 }
5340
5341 void Install() override { ListenToEvent(Solver::MonitorEvent::kBeginFail); }
5342
5343 std::string DebugString() const override {
5344 return absl::StrFormat("LubyRestart(%i)", scale_factor_);
5345 }
5346
5347 private:
5348 const int scale_factor_;
5349 int iteration_;
5350 int64_t current_fails_;
5351 int64_t next_step_;
5352};
5353} // namespace
5354
5356 return RevAlloc(new LubyRestart(this, scale_factor));
5357}
5358
5359// ----- Constant Restart -----
5360
5361namespace {
5362class ConstantRestart : public SearchMonitor {
5363 public:
5364 ConstantRestart(Solver* const s, int frequency)
5365 : SearchMonitor(s), frequency_(frequency), current_fails_(0) {
5366 CHECK_GE(frequency, 1);
5367 }
5368
5369 ~ConstantRestart() override {}
5370
5371 void BeginFail() override {
5372 if (++current_fails_ >= frequency_) {
5373 current_fails_ = 0;
5374 solver()->RestartCurrentSearch();
5375 }
5376 }
5377
5378 void Install() override { ListenToEvent(Solver::MonitorEvent::kBeginFail); }
5379
5380 std::string DebugString() const override {
5381 return absl::StrFormat("ConstantRestart(%i)", frequency_);
5382 }
5383
5384 private:
5385 const int frequency_;
5386 int64_t current_fails_;
5387};
5388} // namespace
5389
5391 return RevAlloc(new ConstantRestart(this, frequency));
5392}
5393
5394// ---------- Symmetry Breaking ----------
5395
5396// The symmetry manager maintains a list of problem symmetries. Each
5397// symmetry is called on each decision and should return a term
5398// representing the boolean status of the symmetrical decision.
5399// e.g. : the decision is x == 3, the symmetrical decision is y == 5
5400// then the symmetry breaker should use
5401// AddIntegerVariableEqualValueClause(y, 5). Once this is done, upon
5402// refutation, for each symmetry breaker, the system adds a constraint
5403// that will forbid the symmetrical variation of the current explored
5404// search tree. This constraint can be expressed very simply just by
5405// keeping the list of current symmetrical decisions.
5406//
5407// This is called Symmetry Breaking During Search (Ian Gent, Barbara
5408// Smith, ECAI 2000).
5409// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.42.3788&rep=rep1&type=pdf
5410//
5412 public:
5414 const std::vector<SymmetryBreaker*>& visitors)
5415 : SearchMonitor(s),
5416 visitors_(visitors),
5417 clauses_(visitors.size()),
5418 decisions_(visitors.size()),
5419 directions_(visitors.size()) { // false = left.
5420 for (int i = 0; i < visitors_.size(); ++i) {
5421 visitors_[i]->set_symmetry_manager_and_index(this, i);
5422 }
5423 }
5424
5425 ~SymmetryManager() override {}
5426
5427 void EndNextDecision(DecisionBuilder* const, Decision* const d) override {
5428 if (d) {
5429 for (int i = 0; i < visitors_.size(); ++i) {
5430 const void* const last = clauses_[i].Last();
5431 d->Accept(visitors_[i]);
5432 if (last != clauses_[i].Last()) {
5433 // Synchroneous push of decision as marker.
5434 decisions_[i].Push(solver(), d);
5435 directions_[i].Push(solver(), false);
5436 }
5437 }
5438 }
5439 }
5440
5441 void RefuteDecision(Decision* d) override {
5442 for (int i = 0; i < visitors_.size(); ++i) {
5443 if (decisions_[i].Last() != nullptr && decisions_[i].LastValue() == d) {
5444 CheckSymmetries(i);
5445 }
5446 }
5447 }
5448
5449 // TODO(user) : Improve speed, cache previous min and build them
5450 // incrementally.
5451 void CheckSymmetries(int index) {
5452 SimpleRevFIFO<IntVar*>::Iterator tmp(&clauses_[index]);
5453 SimpleRevFIFO<bool>::Iterator tmp_dir(&directions_[index]);
5454 Constraint* ct = nullptr;
5455 {
5456 std::vector<IntVar*> guard;
5457 // keep the last entry for later, if loop doesn't exit.
5458 ++tmp;
5459 ++tmp_dir;
5460 while (tmp.ok()) {
5461 IntVar* const term = *tmp;
5462 if (!*tmp_dir) {
5463 if (term->Max() == 0) {
5464 // Premise is wrong. The clause will never apply.
5465 return;
5466 }
5467 if (term->Min() == 0) {
5468 DCHECK_EQ(1, term->Max());
5469 // Premise may be true. Adding to guard vector.
5470 guard.push_back(term);
5471 }
5472 }
5473 ++tmp;
5474 ++tmp_dir;
5475 }
5476 guard.push_back(clauses_[index].LastValue());
5477 directions_[index].SetLastValue(true);
5478 // Given premises: xi = ai
5479 // and a term y != b
5480 // The following is equivalent to
5481 // And(xi == a1) => y != b.
5482 ct = solver()->MakeEquality(solver()->MakeMin(guard), Zero());
5483 }
5484 DCHECK(ct != nullptr);
5485 solver()->AddConstraint(ct);
5486 }
5487
5488 void AddTermToClause(SymmetryBreaker* const visitor, IntVar* const term) {
5489 clauses_[visitor->index_in_symmetry_manager()].Push(solver(), term);
5490 }
5491
5492 std::string DebugString() const override { return "SymmetryManager"; }
5493
5494 private:
5495 const std::vector<SymmetryBreaker*> visitors_;
5496 std::vector<SimpleRevFIFO<IntVar*>> clauses_;
5497 std::vector<SimpleRevFIFO<Decision*>> decisions_;
5498 std::vector<SimpleRevFIFO<bool>> directions_;
5499};
5500
5501// ----- Symmetry Breaker -----
5502
5504 int64_t value) {
5505 CHECK(var != nullptr);
5506 Solver* const solver = var->solver();
5507 IntVar* const term = solver->MakeIsEqualCstVar(var, value);
5508 symmetry_manager()->AddTermToClause(this, term);
5509}
5510
5512 IntVar* const var, int64_t value) {
5513 CHECK(var != nullptr);
5514 Solver* const solver = var->solver();
5515 IntVar* const term = solver->MakeIsGreaterOrEqualCstVar(var, value);
5516 symmetry_manager()->AddTermToClause(this, term);
5517}
5518
5520 IntVar* const var, int64_t value) {
5521 CHECK(var != nullptr);
5522 Solver* const solver = var->solver();
5523 IntVar* const term = solver->MakeIsLessOrEqualCstVar(var, value);
5524 symmetry_manager()->AddTermToClause(this, term);
5525}
5526
5527// ----- API -----
5528
5530 const std::vector<SymmetryBreaker*>& visitors) {
5531 return RevAlloc(new SymmetryManager(this, visitors));
5532}
5533
5535 std::vector<SymmetryBreaker*> visitors;
5536 visitors.push_back(v1);
5537 return MakeSymmetryManager(visitors);
5538}
5539
5541 SymmetryBreaker* const v2) {
5542 std::vector<SymmetryBreaker*> visitors;
5543 visitors.push_back(v1);
5544 visitors.push_back(v2);
5545 return MakeSymmetryManager(visitors);
5546}
5547
5549 SymmetryBreaker* const v2,
5550 SymmetryBreaker* const v3) {
5551 std::vector<SymmetryBreaker*> visitors;
5552 visitors.push_back(v1);
5553 visitors.push_back(v2);
5554 visitors.push_back(v3);
5555 return MakeSymmetryManager(visitors);
5556}
5557
5559 SymmetryBreaker* const v2,
5560 SymmetryBreaker* const v3,
5561 SymmetryBreaker* const v4) {
5562 std::vector<SymmetryBreaker*> visitors;
5563 visitors.push_back(v1);
5564 visitors.push_back(v2);
5565 visitors.push_back(v3);
5566 visitors.push_back(v4);
5567 return MakeSymmetryManager(visitors);
5568}
5569} // 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
virtual Decision * Next(Solver *s)=0
virtual void Accept(ModelVisitor *visitor) const
std::string DebugString() const override
virtual void Accept(DecisionVisitor *visitor) const
Accepts the given visitor.
bool CheckWithOffset(absl::Duration offset) override
Definition search.cc:4872
ImprovementSearchLimit(Solver *solver, IntVar *objective_var, bool maximize, double objective_scaling_factor, double objective_offset, double improvement_rate_coefficient, int improvement_rate_solutions_distance)
Definition search.cc:4800
void Init() override
This method is called when the search limit is initialized.
Definition search.cc:4838
void Copy(const SearchLimit *limit) override
Definition search.cc:4848
SearchLimit * MakeClone() const override
Allocates a clone of the limit.
Definition search.cc:4865
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 * > & minimization_vars() const
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:3236
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:3277
virtual std::string Name() const
Definition search.cc:3275
SearchLimit * MakeClone() const override
Allocates a clone of the limit.
Definition search.cc:4624
bool IsUncheckedSolutionLimitReached() override
Definition search.cc:4684
bool CheckWithOffset(absl::Duration offset) override
Definition search.cc:4632
void Init() override
This method is called when the search limit is initialized.
Definition search.cc:4653
std::string DebugString() const override
Definition search.cc:4690
void Accept(ModelVisitor *visitor) const override
Accepts the given model visitor.
Definition search.cc:4698
void ExitSearch() override
End of the search.
Definition search.cc:4664
RegularLimit(Solver *s, absl::Duration time, int64_t branches, int64_t failures, int64_t solutions, bool smart_time_check, bool cumulative)
Definition search.cc:4584
void Copy(const SearchLimit *limit) override
Definition search.cc:4613
void UpdateLimits(absl::Duration time, int64_t branches, int64_t failures, int64_t solutions)
Definition search.cc:4675
RegularLimit * MakeIdenticalClone() const
Definition search.cc:4626
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.
void BeginNextDecision(DecisionBuilder *b) override
Before calling DecisionBuilder::Next.
Definition search.cc:4559
void PeriodicCheck() override
Periodic call to check limits in long running methods.
Definition search.cc:4569
bool crossed() const
Returns true if the limit has been crossed.
void EnterSearch() override
Internal methods.
Definition search.cc:4554
virtual void Init()=0
This method is called when the search limit is initialized.
void RefuteDecision(Decision *d) override
Before refuting the decision.
Definition search.cc:4564
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)
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
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)
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
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:5123
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:467
Decision * MakeVariableLessOrEqualValue(IntVar *var, int64_t value)
Definition search.cc:1768
SearchMonitor * MakeLubyRestart(int scale_factor)
Definition search.cc:5355
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:3332
SearchMonitor * MakeConstantRestart(int frequency)
Definition search.cc:5390
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:5242
ABSL_MUST_USE_RESULT RegularLimit * MakeSolutionsLimit(int64_t solutions)
Definition search.cc:4758
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:784
ABSL_MUST_USE_RESULT SearchLimit * MakeCustomLimit(std::function< bool()> limiter)
Definition search.cc:5080
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:4751
OptimizeVar * MakeWeightedMinimize(const std::vector< IntVar * > &sub_objectives, const std::vector< int64_t > &weights, int64_t step)
Definition search.cc:3339
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:4737
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:3725
DecisionBuilder * MakePhase(const std::vector< IntVar * > &vars, IntVarStrategy var_str, IntValueStrategy val_str)
Phases on IntVar arrays.
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:4966
OptimizeVar * MakeOptimize(bool maximize, IntVar *v, int64_t step)
Creates a objective with a given sense (true = maximization).
Definition search.cc:3296
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:684
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:3847
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:3735
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:4744
SearchMonitor * MakeSearchLog(int branch_period)
Definition search.cc:334
ObjectiveMonitor * MakeSimulatedAnnealing(bool maximize, IntVar *v, int64_t step, int64_t initial_temperature)
Creates a Simulated Annealing monitor.
Definition search.cc:3840
OptimizeVar * MakeMinimize(IntVar *v, int64_t step)
Creates a minimization objective.
Definition search.cc:3288
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:4497
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:4772
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:3346
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:3744
Assignment * GetOrCreateLocalSearchState()
Returns (or creates) an assignment representing the state of local search.
@ 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:3301
SearchMonitor * MakeSymmetryManager(const std::vector< SymmetryBreaker * > &visitors)
Symmetry Breaking.
Definition search.cc:5529
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:4787
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:3292
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:4957
std::function< int64_t(const IntVar *v, int64_t id)> VariableValueSelector
void Set(IntegerType index)
Definition bitset.h:878
void AddIntegerVariableGreaterOrEqualValueClause(IntVar *var, int64_t value)
Definition search.cc:5511
void AddIntegerVariableLessOrEqualValueClause(IntVar *var, int64_t value)
Definition search.cc:5519
void AddIntegerVariableEqualValueClause(IntVar *var, int64_t value)
Definition search.cc:5503
void EndNextDecision(DecisionBuilder *const, Decision *const d) override
After calling DecisionBuilder::Next, along with the returned decision.
Definition search.cc:5427
void RefuteDecision(Decision *d) override
Before refuting the decision.
Definition search.cc:5441
void AddTermToClause(SymmetryBreaker *const visitor, IntVar *const term)
Definition search.cc:5488
SymmetryManager(Solver *const s, const std::vector< SymmetryBreaker * > &visitors)
Definition search.cc:5413
std::string DebugString() const override
Definition search.cc:5492
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
Definition matchers.h:467
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)
Definition integer.h:1839
OR-Tools root namespace.
int64_t CapAdd(int64_t x, int64_t y)
std::string JoinDebugStringPtr(const std::vector< T > &v, absl::string_view separator)
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)
int64_t CapSub(int64_t x, int64_t y)
std::vector< int64_t > ToInt64Vector(const std::vector< int > &input)
Definition utilities.cc:824
std::string MemoryUsage()
Definition stats.cc:32
BaseAssignVariables::Mode ChooseMode(Solver::IntValueStrategy val_str)
Definition search.cc:2132
int64_t CapOpp(int64_t v)
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