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