OR-Tools  9.2
local_search.cc
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#include <algorithm>
15#include <cstdint>
16#include <iterator>
17#include <limits>
18#include <map>
19#include <memory>
20#include <numeric>
21#include <set>
22#include <string>
23#include <utility>
24#include <vector>
25
26#include "absl/container/flat_hash_map.h"
27#include "absl/container/flat_hash_set.h"
28#include "absl/memory/memory.h"
29#include "absl/random/distributions.h"
30#include "absl/random/random.h"
31#include "absl/strings/str_cat.h"
33#include "ortools/base/hash.h"
37#include "ortools/base/macros.h"
43
44ABSL_FLAG(int, cp_local_search_sync_frequency, 16,
45 "Frequency of checks for better solutions in the solution pool.");
46
47ABSL_FLAG(int, cp_local_search_tsp_opt_size, 13,
48 "Size of TSPs solved in the TSPOpt operator.");
49
50ABSL_FLAG(int, cp_local_search_tsp_lns_size, 10,
51 "Size of TSPs solved in the TSPLns operator.");
52
53ABSL_FLAG(bool, cp_use_empty_path_symmetry_breaker, true,
54 "If true, equivalent empty paths are removed from the neighborhood "
55 "of PathOperators");
56
57namespace operations_research {
58
59// Utility methods to ensure the communication between local search and the
60// search.
61
62// Returns true if a local optimum has been reached and cannot be improved.
63bool LocalOptimumReached(Search* const search);
64
65// Returns true if the search accepts the delta (actually checking this by
66// calling AcceptDelta on the monitors of the search).
67bool AcceptDelta(Search* const search, Assignment* delta,
68 Assignment* deltadelta);
69
70// Notifies the search that a neighbor has been accepted by local search.
71void AcceptNeighbor(Search* const search);
72void AcceptUncheckedNeighbor(Search* const search);
73
74// ----- Base operator class for operators manipulating IntVars -----
75
77 Assignment* deltadelta) {
78 CHECK(delta != nullptr);
79 VLOG(2) << DebugString() << "::MakeNextNeighbor(delta=("
80 << delta->DebugString() << "), deltadelta=("
81 << (deltadelta ? deltadelta->DebugString() : std::string("nullptr"));
82 while (true) {
83 RevertChanges(true);
84
85 if (!MakeOneNeighbor()) {
86 return false;
87 }
88
89 if (ApplyChanges(delta, deltadelta)) {
90 VLOG(2) << "Delta (" << DebugString() << ") = " << delta->DebugString();
91 return true;
92 }
93 }
94 return false;
95}
96// TODO(user): Make this a pure virtual.
98
99// ----- Base Large Neighborhood Search operator -----
100
101BaseLns::BaseLns(const std::vector<IntVar*>& vars)
103
105
107 fragment_.clear();
108 if (NextFragment()) {
109 for (int candidate : fragment_) {
110 Deactivate(candidate);
111 }
112 return true;
113 }
114 return false;
115}
116
117void BaseLns::OnStart() { InitFragments(); }
118
120
122 if (index >= 0 && index < Size()) {
123 fragment_.push_back(index);
124 }
125}
126
127int BaseLns::FragmentSize() const { return fragment_.size(); }
128
129// ----- Simple Large Neighborhood Search operator -----
130
131// Frees number_of_variables (contiguous in vars) variables.
132
133namespace {
134class SimpleLns : public BaseLns {
135 public:
136 SimpleLns(const std::vector<IntVar*>& vars, int number_of_variables)
137 : BaseLns(vars), index_(0), number_of_variables_(number_of_variables) {
138 CHECK_GT(number_of_variables_, 0);
139 }
140 ~SimpleLns() override {}
141 void InitFragments() override { index_ = 0; }
142 bool NextFragment() override;
143 std::string DebugString() const override { return "SimpleLns"; }
144
145 private:
146 int index_;
147 const int number_of_variables_;
148};
149
150bool SimpleLns::NextFragment() {
151 const int size = Size();
152 if (index_ < size) {
153 for (int i = index_; i < index_ + number_of_variables_; ++i) {
154 AppendToFragment(i % size);
155 }
156 ++index_;
157 return true;
158 }
159 return false;
160}
161
162// ----- Random Large Neighborhood Search operator -----
163
164// Frees up to number_of_variables random variables.
165
166class RandomLns : public BaseLns {
167 public:
168 RandomLns(const std::vector<IntVar*>& vars, int number_of_variables,
169 int32_t seed)
170 : BaseLns(vars), rand_(seed), number_of_variables_(number_of_variables) {
171 CHECK_GT(number_of_variables_, 0);
172 CHECK_LE(number_of_variables_, Size());
173 }
174 ~RandomLns() override {}
175 bool NextFragment() override;
176
177 std::string DebugString() const override { return "RandomLns"; }
178
179 private:
180 std::mt19937 rand_;
181 const int number_of_variables_;
182};
183
184bool RandomLns::NextFragment() {
185 DCHECK_GT(Size(), 0);
186 for (int i = 0; i < number_of_variables_; ++i) {
187 AppendToFragment(absl::Uniform<int>(rand_, 0, Size()));
188 }
189 return true;
190}
191} // namespace
192
194 const std::vector<IntVar*>& vars, int number_of_variables) {
195 return MakeRandomLnsOperator(vars, number_of_variables, CpRandomSeed());
196}
197
199 const std::vector<IntVar*>& vars, int number_of_variables, int32_t seed) {
200 return RevAlloc(new RandomLns(vars, number_of_variables, seed));
201}
202
203// ----- Move Toward Target Local Search operator -----
204
205// A local search operator that compares the current assignment with a target
206// one, and that generates neighbors corresponding to a single variable being
207// changed from its current value to its target value.
208namespace {
209class MoveTowardTargetLS : public IntVarLocalSearchOperator {
210 public:
211 MoveTowardTargetLS(const std::vector<IntVar*>& variables,
212 const std::vector<int64_t>& target_values)
213 : IntVarLocalSearchOperator(variables),
214 target_(target_values),
215 // Initialize variable_index_ at the number of the of variables minus
216 // one, so that the first to be tried (after one increment) is the one
217 // of index 0.
218 variable_index_(Size() - 1) {
219 CHECK_EQ(target_values.size(), variables.size()) << "Illegal arguments.";
220 }
221
222 ~MoveTowardTargetLS() override {}
223
224 std::string DebugString() const override { return "MoveTowardTargetLS"; }
225
226 protected:
227 // Make a neighbor assigning one variable to its target value.
228 bool MakeOneNeighbor() override {
229 while (num_var_since_last_start_ < Size()) {
230 ++num_var_since_last_start_;
231 variable_index_ = (variable_index_ + 1) % Size();
232 const int64_t target_value = target_.at(variable_index_);
233 const int64_t current_value = OldValue(variable_index_);
234 if (current_value != target_value) {
235 SetValue(variable_index_, target_value);
236 return true;
237 }
238 }
239 return false;
240 }
241
242 private:
243 void OnStart() override {
244 // Do not change the value of variable_index_: this way, we keep going from
245 // where we last modified something. This is because we expect that most
246 // often, the variables we have just checked are less likely to be able
247 // to be changed to their target values than the ones we have not yet
248 // checked.
249 //
250 // Consider the case where oddly indexed variables can be assigned to their
251 // target values (no matter in what order they are considered), while even
252 // indexed ones cannot. Restarting at index 0 each time an odd-indexed
253 // variable is modified will cause a total of Theta(n^2) neighbors to be
254 // generated, while not restarting will produce only Theta(n) neighbors.
255 CHECK_GE(variable_index_, 0);
256 CHECK_LT(variable_index_, Size());
257 num_var_since_last_start_ = 0;
258 }
259
260 // Target values
261 const std::vector<int64_t> target_;
262
263 // Index of the next variable to try to restore
264 int64_t variable_index_;
265
266 // Number of variables checked since the last call to OnStart().
267 int64_t num_var_since_last_start_;
268};
269} // namespace
270
272 const Assignment& target) {
273 typedef std::vector<IntVarElement> Elements;
274 const Elements& elements = target.IntVarContainer().elements();
275 // Copy target values and construct the vector of variables
276 std::vector<IntVar*> vars;
277 std::vector<int64_t> values;
278 vars.reserve(target.NumIntVars());
279 values.reserve(target.NumIntVars());
280 for (const auto& it : elements) {
281 vars.push_back(it.Var());
282 values.push_back(it.Value());
283 }
284 return MakeMoveTowardTargetOperator(vars, values);
285}
286
288 const std::vector<IntVar*>& variables,
289 const std::vector<int64_t>& target_values) {
290 return RevAlloc(new MoveTowardTargetLS(variables, target_values));
291}
292
293// ----- ChangeValue Operators -----
294
295ChangeValue::ChangeValue(const std::vector<IntVar*>& vars)
296 : IntVarLocalSearchOperator(vars), index_(0) {}
297
299
301 const int size = Size();
302 while (index_ < size) {
303 const int64_t value = ModifyValue(index_, Value(index_));
304 SetValue(index_, value);
305 ++index_;
306 return true;
307 }
308 return false;
309}
310
311void ChangeValue::OnStart() { index_ = 0; }
312
313// Increments the current value of variables.
314
315namespace {
316class IncrementValue : public ChangeValue {
317 public:
318 explicit IncrementValue(const std::vector<IntVar*>& vars)
319 : ChangeValue(vars) {}
320 ~IncrementValue() override {}
321 int64_t ModifyValue(int64_t index, int64_t value) override {
322 return value + 1;
323 }
324
325 std::string DebugString() const override { return "IncrementValue"; }
326};
327
328// Decrements the current value of variables.
329
330class DecrementValue : public ChangeValue {
331 public:
332 explicit DecrementValue(const std::vector<IntVar*>& vars)
333 : ChangeValue(vars) {}
334 ~DecrementValue() override {}
335 int64_t ModifyValue(int64_t index, int64_t value) override {
336 return value - 1;
337 }
338
339 std::string DebugString() const override { return "DecrementValue"; }
340};
341} // namespace
342
343// ----- Path-based Operators -----
344
345PathOperator::PathOperator(const std::vector<IntVar*>& next_vars,
346 const std::vector<IntVar*>& path_vars,
347 IterationParameters iteration_parameters)
348 : IntVarLocalSearchOperator(next_vars, true),
349 number_of_nexts_(next_vars.size()),
350 ignore_path_vars_(path_vars.empty()),
351 next_base_to_increment_(iteration_parameters.number_of_base_nodes),
352 base_nodes_(iteration_parameters.number_of_base_nodes),
353 base_alternatives_(iteration_parameters.number_of_base_nodes),
354 base_sibling_alternatives_(iteration_parameters.number_of_base_nodes),
355 end_nodes_(iteration_parameters.number_of_base_nodes),
356 base_paths_(iteration_parameters.number_of_base_nodes),
357 just_started_(false),
358 first_start_(true),
359 iteration_parameters_(std::move(iteration_parameters)),
360 optimal_paths_enabled_(false),
361 alternative_index_(next_vars.size(), -1) {
362 DCHECK_GT(iteration_parameters_.number_of_base_nodes, 0);
363 if (!ignore_path_vars_) {
364 AddVars(path_vars);
365 }
366 path_basis_.push_back(0);
367 for (int i = 1; i < base_nodes_.size(); ++i) {
368 if (!OnSamePathAsPreviousBase(i)) path_basis_.push_back(i);
369 }
370 if ((path_basis_.size() > 2) ||
371 (!next_vars.empty() && !next_vars.back()
372 ->solver()
373 ->parameters()
374 .skip_locally_optimal_paths())) {
375 iteration_parameters_.skip_locally_optimal_paths = false;
376 }
377}
378
379void PathOperator::Reset() { optimal_paths_.clear(); }
380
381void PathOperator::OnStart() {
382 optimal_paths_enabled_ = false;
383 InitializeBaseNodes();
384 InitializeAlternatives();
386}
387
389 while (IncrementPosition()) {
390 // Need to revert changes here since MakeNeighbor might have returned false
391 // and have done changes in the previous iteration.
392 RevertChanges(true);
393 if (MakeNeighbor()) {
394 return true;
395 }
396 }
397 return false;
398}
399
401 if (ignore_path_vars_) {
402 return true;
403 }
404 if (index < number_of_nexts_) {
405 int path_index = index + number_of_nexts_;
406 return Value(path_index) == OldValue(path_index);
407 }
408 int next_index = index - number_of_nexts_;
409 return Value(next_index) == OldValue(next_index);
410}
411
412bool PathOperator::MoveChain(int64_t before_chain, int64_t chain_end,
413 int64_t destination) {
414 if (destination == before_chain || destination == chain_end) return false;
415 DCHECK(CheckChainValidity(before_chain, chain_end, destination) &&
416 !IsPathEnd(chain_end) && !IsPathEnd(destination));
417 const int64_t destination_path = Path(destination);
418 const int64_t after_chain = Next(chain_end);
419 SetNext(chain_end, Next(destination), destination_path);
420 if (!ignore_path_vars_) {
421 int current = destination;
422 int next = Next(before_chain);
423 while (current != chain_end) {
424 SetNext(current, next, destination_path);
425 current = next;
426 next = Next(next);
427 }
428 } else {
429 SetNext(destination, Next(before_chain), destination_path);
430 }
431 SetNext(before_chain, after_chain, Path(before_chain));
432 return true;
433}
434
435bool PathOperator::ReverseChain(int64_t before_chain, int64_t after_chain,
436 int64_t* chain_last) {
437 if (CheckChainValidity(before_chain, after_chain, -1)) {
438 int64_t path = Path(before_chain);
439 int64_t current = Next(before_chain);
440 if (current == after_chain) {
441 return false;
442 }
443 int64_t current_next = Next(current);
444 SetNext(current, after_chain, path);
445 while (current_next != after_chain) {
446 const int64_t next = Next(current_next);
447 SetNext(current_next, current, path);
448 current = current_next;
449 current_next = next;
450 }
451 SetNext(before_chain, current, path);
452 *chain_last = current;
453 return true;
454 }
455 return false;
456}
457
458bool PathOperator::MakeActive(int64_t node, int64_t destination) {
459 if (!IsPathEnd(destination)) {
460 int64_t destination_path = Path(destination);
461 SetNext(node, Next(destination), destination_path);
462 SetNext(destination, node, destination_path);
463 return true;
464 }
465 return false;
466}
467
468bool PathOperator::MakeChainInactive(int64_t before_chain, int64_t chain_end) {
469 const int64_t kNoPath = -1;
470 if (CheckChainValidity(before_chain, chain_end, -1) &&
471 !IsPathEnd(chain_end)) {
472 const int64_t after_chain = Next(chain_end);
473 int64_t current = Next(before_chain);
474 while (current != after_chain) {
475 const int64_t next = Next(current);
476 SetNext(current, current, kNoPath);
477 current = next;
478 }
479 SetNext(before_chain, after_chain, Path(before_chain));
480 return true;
481 }
482 return false;
483}
484
485bool PathOperator::SwapActiveAndInactive(int64_t active, int64_t inactive) {
486 if (active == inactive) return false;
487 const int64_t prev = Prev(active);
488 return MakeChainInactive(prev, active) && MakeActive(inactive, prev);
489}
490
491bool PathOperator::IncrementPosition() {
492 const int base_node_size = iteration_parameters_.number_of_base_nodes;
493
494 if (!just_started_) {
495 const int number_of_paths = path_starts_.size();
496 // Finding next base node positions.
497 // Increment the position of inner base nodes first (higher index nodes);
498 // if a base node is at the end of a path, reposition it at the start
499 // of the path and increment the position of the preceding base node (this
500 // action is called a restart).
501 int last_restarted = base_node_size;
502 for (int i = base_node_size - 1; i >= 0; --i) {
503 if (base_nodes_[i] < number_of_nexts_ && i <= next_base_to_increment_) {
504 if (ConsiderAlternatives(i)) {
505 // Iterate on sibling alternatives.
506 const int sibling_alternative_index =
507 GetSiblingAlternativeIndex(base_nodes_[i]);
508 if (sibling_alternative_index >= 0) {
509 if (base_sibling_alternatives_[i] <
510 alternative_sets_[sibling_alternative_index].size() - 1) {
511 ++base_sibling_alternatives_[i];
512 break;
513 }
514 base_sibling_alternatives_[i] = 0;
515 }
516 // Iterate on base alternatives.
517 const int alternative_index = alternative_index_[base_nodes_[i]];
518 if (alternative_index >= 0) {
519 if (base_alternatives_[i] <
520 alternative_sets_[alternative_index].size() - 1) {
521 ++base_alternatives_[i];
522 break;
523 }
524 base_alternatives_[i] = 0;
525 base_sibling_alternatives_[i] = 0;
526 }
527 }
528 base_alternatives_[i] = 0;
529 base_sibling_alternatives_[i] = 0;
530 base_nodes_[i] = OldNext(base_nodes_[i]);
531 if (iteration_parameters_.accept_path_end_base ||
532 !IsPathEnd(base_nodes_[i]))
533 break;
534 }
535 base_alternatives_[i] = 0;
536 base_sibling_alternatives_[i] = 0;
537 base_nodes_[i] = StartNode(i);
538 last_restarted = i;
539 }
540 next_base_to_increment_ = base_node_size;
541 // At the end of the loop, base nodes with indexes in
542 // [last_restarted, base_node_size[ have been restarted.
543 // Restarted base nodes are then repositioned by the virtual
544 // GetBaseNodeRestartPosition to reflect position constraints between
545 // base nodes (by default GetBaseNodeRestartPosition leaves the nodes
546 // at the start of the path).
547 // Base nodes are repositioned in ascending order to ensure that all
548 // base nodes "below" the node being repositioned have their final
549 // position.
550 for (int i = last_restarted; i < base_node_size; ++i) {
551 base_alternatives_[i] = 0;
552 base_sibling_alternatives_[i] = 0;
553 base_nodes_[i] = GetBaseNodeRestartPosition(i);
554 }
555 if (last_restarted > 0) {
556 return CheckEnds();
557 }
558 // If all base nodes have been restarted, base nodes are moved to new paths.
559 // First we mark the current paths as locally optimal if they have been
560 // completely explored.
561 if (optimal_paths_enabled_ &&
562 iteration_parameters_.skip_locally_optimal_paths) {
563 if (path_basis_.size() > 1) {
564 for (int i = 1; i < path_basis_.size(); ++i) {
565 optimal_paths_[num_paths_ *
566 start_to_path_[StartNode(path_basis_[i - 1])] +
567 start_to_path_[StartNode(path_basis_[i])]] = true;
568 }
569 } else {
570 optimal_paths_[num_paths_ * start_to_path_[StartNode(path_basis_[0])] +
571 start_to_path_[StartNode(path_basis_[0])]] = true;
572 }
573 }
574 std::vector<int> current_starts(base_node_size);
575 for (int i = 0; i < base_node_size; ++i) {
576 current_starts[i] = StartNode(i);
577 }
578 // Exploration of next paths can lead to locally optimal paths since we are
579 // exploring them from scratch.
580 optimal_paths_enabled_ = true;
581 while (true) {
582 for (int i = base_node_size - 1; i >= 0; --i) {
583 const int next_path_index = base_paths_[i] + 1;
584 if (next_path_index < number_of_paths) {
585 base_paths_[i] = next_path_index;
586 base_alternatives_[i] = 0;
587 base_sibling_alternatives_[i] = 0;
588 base_nodes_[i] = path_starts_[next_path_index];
589 if (i == 0 || !OnSamePathAsPreviousBase(i)) {
590 break;
591 }
592 } else {
593 base_paths_[i] = 0;
594 base_alternatives_[i] = 0;
595 base_sibling_alternatives_[i] = 0;
596 base_nodes_[i] = path_starts_[0];
597 }
598 }
599 if (!iteration_parameters_.skip_locally_optimal_paths) return CheckEnds();
600 // If the new paths have already been completely explored, we can
601 // skip them from now on.
602 if (path_basis_.size() > 1) {
603 for (int j = 1; j < path_basis_.size(); ++j) {
604 if (!optimal_paths_[num_paths_ * start_to_path_[StartNode(
605 path_basis_[j - 1])] +
606 start_to_path_[StartNode(path_basis_[j])]]) {
607 return CheckEnds();
608 }
609 }
610 } else {
611 if (!optimal_paths_[num_paths_ *
612 start_to_path_[StartNode(path_basis_[0])] +
613 start_to_path_[StartNode(path_basis_[0])]]) {
614 return CheckEnds();
615 }
616 }
617 // If we are back to paths we just iterated on or have reached the end
618 // of the neighborhood search space, we can stop.
619 if (!CheckEnds()) return false;
620 bool stop = true;
621 for (int i = 0; i < base_node_size; ++i) {
622 if (StartNode(i) != current_starts[i]) {
623 stop = false;
624 break;
625 }
626 }
627 if (stop) return false;
628 }
629 } else {
630 just_started_ = false;
631 return true;
632 }
633 return CheckEnds();
634}
635
636void PathOperator::InitializePathStarts() {
637 // Detect nodes which do not have any possible predecessor in a path; these
638 // nodes are path starts.
639 int max_next = -1;
640 std::vector<bool> has_prevs(number_of_nexts_, false);
641 for (int i = 0; i < number_of_nexts_; ++i) {
642 const int next = OldNext(i);
643 if (next < number_of_nexts_) {
644 has_prevs[next] = true;
645 }
646 max_next = std::max(max_next, next);
647 }
648 // Update locally optimal paths.
649 if (optimal_paths_.empty() &&
650 iteration_parameters_.skip_locally_optimal_paths) {
651 num_paths_ = 0;
652 start_to_path_.clear();
654 for (int i = 0; i < number_of_nexts_; ++i) {
655 if (!has_prevs[i]) {
657 ++num_paths_;
658 }
659 }
660 optimal_paths_.resize(num_paths_ * num_paths_, false);
661 }
662 if (iteration_parameters_.skip_locally_optimal_paths) {
663 for (int i = 0; i < number_of_nexts_; ++i) {
664 if (!has_prevs[i]) {
665 int current = i;
666 while (!IsPathEnd(current)) {
667 if ((OldNext(current) != prev_values_[current])) {
668 for (int j = 0; j < num_paths_; ++j) {
669 optimal_paths_[num_paths_ * start_to_path_[i] + j] = false;
670 optimal_paths_[num_paths_ * j + start_to_path_[i]] = false;
671 }
672 break;
673 }
674 current = OldNext(current);
675 }
676 }
677 }
678 }
679 // Create a list of path starts, dropping equivalent path starts of
680 // currently empty paths.
681 std::vector<bool> empty_found(number_of_nexts_, false);
682 std::vector<int64_t> new_path_starts;
683 const bool use_empty_path_symmetry_breaker =
684 absl::GetFlag(FLAGS_cp_use_empty_path_symmetry_breaker);
685 for (int i = 0; i < number_of_nexts_; ++i) {
686 if (!has_prevs[i]) {
687 if (use_empty_path_symmetry_breaker && IsPathEnd(OldNext(i))) {
688 if (iteration_parameters_.start_empty_path_class != nullptr) {
689 if (empty_found[iteration_parameters_.start_empty_path_class(i)])
690 continue;
691 empty_found[iteration_parameters_.start_empty_path_class(i)] = true;
692 }
693 }
694 new_path_starts.push_back(i);
695 }
696 }
697 if (!first_start_) {
698 // Synchronizing base_paths_ with base node positions. When the last move
699 // was performed a base node could have been moved to a new route in which
700 // case base_paths_ needs to be updated. This needs to be done on the path
701 // starts before we re-adjust base nodes for new path starts.
702 std::vector<int> node_paths(max_next + 1, -1);
703 for (int i = 0; i < path_starts_.size(); ++i) {
704 int node = path_starts_[i];
705 while (!IsPathEnd(node)) {
706 node_paths[node] = i;
707 node = OldNext(node);
708 }
709 node_paths[node] = i;
710 }
711 for (int j = 0; j < iteration_parameters_.number_of_base_nodes; ++j) {
712 // Always restart from first alternative.
713 base_alternatives_[j] = 0;
714 base_sibling_alternatives_[j] = 0;
715 if (IsInactive(base_nodes_[j]) || node_paths[base_nodes_[j]] == -1) {
716 // Base node was made inactive or was moved to a new path, reposition
717 // the base node to the start of the path on which it was.
718 base_nodes_[j] = path_starts_[base_paths_[j]];
719 } else {
720 base_paths_[j] = node_paths[base_nodes_[j]];
721 }
722 }
723 // Re-adjust current base_nodes and base_paths to take into account new
724 // path starts (there could be fewer if a new path was made empty, or more
725 // if nodes were added to a formerly empty path).
726 int new_index = 0;
727 absl::flat_hash_set<int> found_bases;
728 for (int i = 0; i < path_starts_.size(); ++i) {
729 int index = new_index;
730 // Note: old and new path starts are sorted by construction.
731 while (index < new_path_starts.size() &&
732 new_path_starts[index] < path_starts_[i]) {
733 ++index;
734 }
735 const bool found = (index < new_path_starts.size() &&
736 new_path_starts[index] == path_starts_[i]);
737 if (found) {
738 new_index = index;
739 }
740 for (int j = 0; j < iteration_parameters_.number_of_base_nodes; ++j) {
741 if (base_paths_[j] == i && !found_bases.contains(j)) {
742 found_bases.insert(j);
743 base_paths_[j] = new_index;
744 // If the current position of the base node is a removed empty path,
745 // readjusting it to the last visited path start.
746 if (!found) {
747 base_nodes_[j] = new_path_starts[new_index];
748 }
749 }
750 }
751 }
752 }
753 path_starts_.swap(new_path_starts);
754}
755
756void PathOperator::InitializeInactives() {
757 inactives_.clear();
758 for (int i = 0; i < number_of_nexts_; ++i) {
759 inactives_.push_back(OldNext(i) == i);
760 }
761}
762
763void PathOperator::InitializeBaseNodes() {
764 // Inactive nodes must be detected before determining new path starts.
765 InitializeInactives();
766 InitializePathStarts();
767 if (first_start_ || InitPosition()) {
768 // Only do this once since the following starts will continue from the
769 // preceding position
770 for (int i = 0; i < iteration_parameters_.number_of_base_nodes; ++i) {
771 base_paths_[i] = 0;
772 base_nodes_[i] = path_starts_[0];
773 }
774 first_start_ = false;
775 }
776 for (int i = 0; i < iteration_parameters_.number_of_base_nodes; ++i) {
777 // If base node has been made inactive, restart from path start.
778 int64_t base_node = base_nodes_[i];
779 if (RestartAtPathStartOnSynchronize() || IsInactive(base_node)) {
780 base_node = path_starts_[base_paths_[i]];
781 base_nodes_[i] = base_node;
782 }
783 end_nodes_[i] = base_node;
784 }
785 // Repair end_nodes_ in case some must be on the same path and are not anymore
786 // (due to other operators moving these nodes).
787 for (int i = 1; i < iteration_parameters_.number_of_base_nodes; ++i) {
789 !OnSamePath(base_nodes_[i - 1], base_nodes_[i])) {
790 const int64_t base_node = base_nodes_[i - 1];
791 base_nodes_[i] = base_node;
792 end_nodes_[i] = base_node;
793 base_paths_[i] = base_paths_[i - 1];
794 }
795 }
796 for (int i = 0; i < iteration_parameters_.number_of_base_nodes; ++i) {
797 base_alternatives_[i] = 0;
798 base_sibling_alternatives_[i] = 0;
799 }
800 just_started_ = true;
801}
802
803void PathOperator::InitializeAlternatives() {
804 active_in_alternative_set_.resize(alternative_sets_.size(), -1);
805 for (int i = 0; i < alternative_sets_.size(); ++i) {
806 const int64_t current_active = active_in_alternative_set_[i];
807 if (current_active >= 0 && !IsInactive(current_active)) continue;
808 for (int64_t index : alternative_sets_[i]) {
809 if (!IsInactive(index)) {
810 active_in_alternative_set_[i] = index;
811 break;
812 }
813 }
814 }
815}
816
817bool PathOperator::OnSamePath(int64_t node1, int64_t node2) const {
818 if (IsInactive(node1) != IsInactive(node2)) {
819 return false;
820 }
821 for (int node = node1; !IsPathEnd(node); node = OldNext(node)) {
822 if (node == node2) {
823 return true;
824 }
825 }
826 for (int node = node2; !IsPathEnd(node); node = OldNext(node)) {
827 if (node == node1) {
828 return true;
829 }
830 }
831 return false;
832}
833
834// Rejects chain if chain_end is not after before_chain on the path or if
835// the chain contains exclude. Given before_chain is the node before the
836// chain, if before_chain and chain_end are the same the chain is rejected too.
837// Also rejects cycles (cycle detection is detected through chain length
838// overflow).
839bool PathOperator::CheckChainValidity(int64_t before_chain, int64_t chain_end,
840 int64_t exclude) const {
841 if (before_chain == chain_end || before_chain == exclude) return false;
842 int64_t current = before_chain;
843 int chain_size = 0;
844 while (current != chain_end) {
845 if (chain_size > number_of_nexts_) {
846 return false;
847 }
848 if (IsPathEnd(current)) {
849 return false;
850 }
851 current = Next(current);
852 ++chain_size;
853 if (current == exclude) {
854 return false;
855 }
856 }
857 return true;
858}
859
860// ----- 2Opt -----
861
862// Reverses a sub-chain of a path. It is called 2Opt because it breaks
863// 2 arcs on the path; resulting paths are called 2-optimal.
864// Possible neighbors for the path 1 -> 2 -> 3 -> 4 -> 5
865// (where (1, 5) are first and last nodes of the path and can therefore not be
866// moved):
867// 1 -> 3 -> 2 -> 4 -> 5
868// 1 -> 4 -> 3 -> 2 -> 5
869// 1 -> 2 -> 4 -> 3 -> 5
870class TwoOpt : public PathOperator {
871 public:
872 TwoOpt(const std::vector<IntVar*>& vars,
873 const std::vector<IntVar*>& secondary_vars,
874 std::function<int(int64_t)> start_empty_path_class)
875 : PathOperator(vars, secondary_vars, 2, true, true,
876 std::move(start_empty_path_class)),
877 last_base_(-1),
878 last_(-1) {}
879 ~TwoOpt() override {}
880 bool MakeNeighbor() override;
881 bool IsIncremental() const override { return true; }
882
883 std::string DebugString() const override { return "TwoOpt"; }
884
885 protected:
886 bool OnSamePathAsPreviousBase(int64_t base_index) override {
887 // Both base nodes have to be on the same path.
888 return true;
889 }
890 int64_t GetBaseNodeRestartPosition(int base_index) override {
891 return (base_index == 0) ? StartNode(0) : BaseNode(0);
892 }
893
894 private:
895 void OnNodeInitialization() override { last_ = -1; }
896
897 int64_t last_base_;
898 int64_t last_;
899};
900
903 if (last_base_ != BaseNode(0) || last_ == -1) {
904 RevertChanges(false);
905 if (IsPathEnd(BaseNode(0))) {
906 last_ = -1;
907 return false;
908 }
909 last_base_ = BaseNode(0);
910 last_ = Next(BaseNode(0));
911 int64_t chain_last;
912 if (ReverseChain(BaseNode(0), BaseNode(1), &chain_last)
913 // Check there are more than one node in the chain (reversing a
914 // single node is a NOP).
915 && last_ != chain_last) {
916 return true;
917 }
918 last_ = -1;
919 return false;
920 }
921 const int64_t to_move = Next(last_);
922 DCHECK_EQ(Next(to_move), BaseNode(1));
923 return MoveChain(last_, to_move, BaseNode(0));
924}
925
926// ----- Relocate -----
927
928// Moves a sub-chain of a path to another position; the specified chain length
929// is the fixed length of the chains being moved. When this length is 1 the
930// operator simply moves a node to another position.
931// Possible neighbors for the path 1 -> 2 -> 3 -> 4 -> 5, for a chain length
932// of 2 (where (1, 5) are first and last nodes of the path and can
933// therefore not be moved):
934// 1 -> 4 -> 2 -> 3 -> 5
935// 1 -> 3 -> 4 -> 2 -> 5
936//
937// Using Relocate with chain lengths of 1, 2 and 3 together is equivalent to
938// the OrOpt operator on a path. The OrOpt operator is a limited version of
939// 3Opt (breaks 3 arcs on a path).
940
941class Relocate : public PathOperator {
942 public:
943 Relocate(const std::vector<IntVar*>& vars,
944 const std::vector<IntVar*>& secondary_vars, const std::string& name,
945 std::function<int(int64_t)> start_empty_path_class,
946 int64_t chain_length = 1LL, bool single_path = false)
947 : PathOperator(vars, secondary_vars, 2, true, false,
948 std::move(start_empty_path_class)),
949 chain_length_(chain_length),
950 single_path_(single_path),
951 name_(name) {
952 CHECK_GT(chain_length_, 0);
953 }
954 Relocate(const std::vector<IntVar*>& vars,
955 const std::vector<IntVar*>& secondary_vars,
956 std::function<int(int64_t)> start_empty_path_class,
957 int64_t chain_length = 1LL, bool single_path = false)
958 : Relocate(vars, secondary_vars,
959 absl::StrCat("Relocate<", chain_length, ">"),
960 std::move(start_empty_path_class), chain_length, single_path) {
961 }
962 ~Relocate() override {}
963 bool MakeNeighbor() override;
964
965 std::string DebugString() const override { return name_; }
966
967 protected:
968 bool OnSamePathAsPreviousBase(int64_t base_index) override {
969 // Both base nodes have to be on the same path when it's the single path
970 // version.
971 return single_path_;
972 }
973
974 private:
975 const int64_t chain_length_;
976 const bool single_path_;
977 const std::string name_;
978};
979
981 DCHECK(!single_path_ || StartNode(0) == StartNode(1));
982 const int64_t destination = BaseNode(1);
983 DCHECK(!IsPathEnd(destination));
984 const int64_t before_chain = BaseNode(0);
985 int64_t chain_end = before_chain;
986 for (int i = 0; i < chain_length_; ++i) {
987 if (IsPathEnd(chain_end) || chain_end == destination) {
988 return false;
989 }
990 chain_end = Next(chain_end);
991 }
992 return !IsPathEnd(chain_end) &&
993 MoveChain(before_chain, chain_end, destination);
994}
995
996// ----- Exchange -----
997
998// Exchanges the positions of two nodes.
999// Possible neighbors for the path 1 -> 2 -> 3 -> 4 -> 5
1000// (where (1, 5) are first and last nodes of the path and can therefore not
1001// be moved):
1002// 1 -> 3 -> 2 -> 4 -> 5
1003// 1 -> 4 -> 3 -> 2 -> 5
1004// 1 -> 2 -> 4 -> 3 -> 5
1005
1006class Exchange : public PathOperator {
1007 public:
1008 Exchange(const std::vector<IntVar*>& vars,
1009 const std::vector<IntVar*>& secondary_vars,
1010 std::function<int(int64_t)> start_empty_path_class)
1011 : PathOperator(vars, secondary_vars, 2, true, false,
1012 std::move(start_empty_path_class)) {}
1013 ~Exchange() override {}
1014 bool MakeNeighbor() override;
1015
1016 std::string DebugString() const override { return "Exchange"; }
1017};
1018
1020 const int64_t prev_node0 = BaseNode(0);
1021 const int64_t node0 = Next(prev_node0);
1022 if (IsPathEnd(node0)) return false;
1023 const int64_t prev_node1 = BaseNode(1);
1024 const int64_t node1 = Next(prev_node1);
1025 if (IsPathEnd(node1)) return false;
1026 const bool ok = MoveChain(prev_node0, node0, prev_node1);
1027 return MoveChain(Prev(node1), node1, prev_node0) || ok;
1028}
1029
1030// ----- Cross -----
1031
1032// Cross echanges the starting chains of 2 paths, including exchanging the
1033// whole paths.
1034// First and last nodes are not moved.
1035// Possible neighbors for the paths 1 -> 2 -> 3 -> 4 -> 5 and 6 -> 7 -> 8
1036// (where (1, 5) and (6, 8) are first and last nodes of the paths and can
1037// therefore not be moved):
1038// 1 -> 7 -> 3 -> 4 -> 5 6 -> 2 -> 8
1039// 1 -> 7 -> 4 -> 5 6 -> 2 -> 3 -> 8
1040// 1 -> 7 -> 5 6 -> 2 -> 3 -> 4 -> 8
1041
1042class Cross : public PathOperator {
1043 public:
1044 Cross(const std::vector<IntVar*>& vars,
1045 const std::vector<IntVar*>& secondary_vars,
1046 std::function<int(int64_t)> start_empty_path_class)
1047 : PathOperator(vars, secondary_vars, 2, true, true,
1048 std::move(start_empty_path_class)) {}
1049 ~Cross() override {}
1050 bool MakeNeighbor() override;
1051
1052 std::string DebugString() const override { return "Cross"; }
1053};
1054
1056 const int64_t start0 = StartNode(0);
1057 const int64_t start1 = StartNode(1);
1058 if (start1 == start0) return false;
1059 const int64_t node0 = BaseNode(0);
1060 if (node0 == start0) return false;
1061 const int64_t node1 = BaseNode(1);
1062 if (node1 == start1) return false;
1063 if (!IsPathEnd(node0) && !IsPathEnd(node1)) {
1064 // If two paths are equivalent don't exchange them.
1065 if (PathClass(0) == PathClass(1) && IsPathEnd(Next(node0)) &&
1066 IsPathEnd(Next(node1))) {
1067 return false;
1068 }
1069 return MoveChain(start0, node0, start1) && MoveChain(node0, node1, start0);
1070 }
1071 if (!IsPathEnd(node0)) return MoveChain(start0, node0, start1);
1072 if (!IsPathEnd(node1)) return MoveChain(start1, node1, start0);
1073 return false;
1074}
1075
1076// ----- BaseInactiveNodeToPathOperator -----
1077// Base class of path operators which make inactive nodes active.
1078
1080 public:
1082 const std::vector<IntVar*>& vars,
1083 const std::vector<IntVar*>& secondary_vars, int number_of_base_nodes,
1084 std::function<int(int64_t)> start_empty_path_class)
1085 : PathOperator(vars, secondary_vars, number_of_base_nodes, false, false,
1086 std::move(start_empty_path_class)),
1087 inactive_node_(0) {
1088 // TODO(user): Activate skipping optimal paths.
1089 }
1091
1092 protected:
1093 bool MakeOneNeighbor() override;
1094 int64_t GetInactiveNode() const { return inactive_node_; }
1095
1096 private:
1097 void OnNodeInitialization() override;
1098
1099 int inactive_node_;
1100};
1101
1102void BaseInactiveNodeToPathOperator::OnNodeInitialization() {
1103 for (int i = 0; i < Size(); ++i) {
1104 if (IsInactive(i)) {
1105 inactive_node_ = i;
1106 return;
1107 }
1108 }
1109 inactive_node_ = Size();
1110}
1111
1113 while (inactive_node_ < Size()) {
1114 if (!IsInactive(inactive_node_) || !PathOperator::MakeOneNeighbor()) {
1115 ResetPosition();
1116 ++inactive_node_;
1117 } else {
1118 return true;
1119 }
1120 }
1121 return false;
1122}
1123
1124// ----- MakeActiveOperator -----
1125
1126// MakeActiveOperator inserts an inactive node into a path.
1127// Possible neighbors for the path 1 -> 2 -> 3 -> 4 with 5 inactive (where 1 and
1128// 4 are first and last nodes of the path) are:
1129// 1 -> 5 -> 2 -> 3 -> 4
1130// 1 -> 2 -> 5 -> 3 -> 4
1131// 1 -> 2 -> 3 -> 5 -> 4
1132
1134 public:
1135 MakeActiveOperator(const std::vector<IntVar*>& vars,
1136 const std::vector<IntVar*>& secondary_vars,
1137 std::function<int(int64_t)> start_empty_path_class)
1138 : BaseInactiveNodeToPathOperator(vars, secondary_vars, 1,
1139 std::move(start_empty_path_class)) {}
1141 bool MakeNeighbor() override;
1142
1143 std::string DebugString() const override { return "MakeActiveOperator"; }
1144};
1145
1147 return MakeActive(GetInactiveNode(), BaseNode(0));
1148}
1149
1150// ---- RelocateAndMakeActiveOperator -----
1151
1152// RelocateAndMakeActiveOperator relocates a node and replaces it by an inactive
1153// node.
1154// The idea is to make room for inactive nodes.
1155// Possible neighbor for paths 0 -> 4, 1 -> 2 -> 5 and 3 inactive is:
1156// 0 -> 2 -> 4, 1 -> 3 -> 5.
1157// TODO(user): Naming is close to MakeActiveAndRelocate but this one is
1158// correct; rename MakeActiveAndRelocate if it is actually used.
1160 public:
1162 const std::vector<IntVar*>& vars,
1163 const std::vector<IntVar*>& secondary_vars,
1164 std::function<int(int64_t)> start_empty_path_class)
1165 : BaseInactiveNodeToPathOperator(vars, secondary_vars, 2,
1166 std::move(start_empty_path_class)) {}
1168 bool MakeNeighbor() override {
1169 const int64_t before_node_to_move = BaseNode(1);
1170 const int64_t node = Next(before_node_to_move);
1171 return !IsPathEnd(node) &&
1172 MoveChain(before_node_to_move, node, BaseNode(0)) &&
1173 MakeActive(GetInactiveNode(), before_node_to_move);
1174 }
1175
1176 std::string DebugString() const override {
1177 return "RelocateAndMakeActiveOpertor";
1178 }
1179};
1180
1181// ----- MakeActiveAndRelocate -----
1182
1183// MakeActiveAndRelocate makes a node active next to a node being relocated.
1184// Possible neighbor for paths 0 -> 4, 1 -> 2 -> 5 and 3 inactive is:
1185// 0 -> 3 -> 2 -> 4, 1 -> 5.
1186
1188 public:
1189 MakeActiveAndRelocate(const std::vector<IntVar*>& vars,
1190 const std::vector<IntVar*>& secondary_vars,
1191 std::function<int(int64_t)> start_empty_path_class)
1192 : BaseInactiveNodeToPathOperator(vars, secondary_vars, 2,
1193 std::move(start_empty_path_class)) {}
1195 bool MakeNeighbor() override;
1196
1197 std::string DebugString() const override {
1198 return "MakeActiveAndRelocateOperator";
1199 }
1200};
1201
1203 const int64_t before_chain = BaseNode(1);
1204 const int64_t chain_end = Next(before_chain);
1205 const int64_t destination = BaseNode(0);
1206 return !IsPathEnd(chain_end) &&
1207 MoveChain(before_chain, chain_end, destination) &&
1208 MakeActive(GetInactiveNode(), destination);
1209}
1210
1211// ----- MakeInactiveOperator -----
1212
1213// MakeInactiveOperator makes path nodes inactive.
1214// Possible neighbors for the path 1 -> 2 -> 3 -> 4 (where 1 and 4 are first
1215// and last nodes of the path) are:
1216// 1 -> 3 -> 4 & 2 inactive
1217// 1 -> 2 -> 4 & 3 inactive
1218
1220 public:
1221 MakeInactiveOperator(const std::vector<IntVar*>& vars,
1222 const std::vector<IntVar*>& secondary_vars,
1223 std::function<int(int64_t)> start_empty_path_class)
1224 : PathOperator(vars, secondary_vars, 1, true, false,
1225 std::move(start_empty_path_class)) {}
1227 bool MakeNeighbor() override {
1228 const int64_t base = BaseNode(0);
1229 return MakeChainInactive(base, Next(base));
1230 }
1231
1232 std::string DebugString() const override { return "MakeInactiveOperator"; }
1233};
1234
1235// ----- RelocateAndMakeInactiveOperator -----
1236
1237// RelocateAndMakeInactiveOperator relocates a node to a new position and makes
1238// the node which was at that position inactive.
1239// Possible neighbors for paths 0 -> 2 -> 4, 1 -> 3 -> 5 are:
1240// 0 -> 3 -> 4, 1 -> 5 & 2 inactive
1241// 0 -> 4, 1 -> 2 -> 5 & 3 inactive
1242
1244 public:
1246 const std::vector<IntVar*>& vars,
1247 const std::vector<IntVar*>& secondary_vars,
1248 std::function<int(int64_t)> start_empty_path_class)
1249 : PathOperator(vars, secondary_vars, 2, true, false,
1250 std::move(start_empty_path_class)) {}
1252 bool MakeNeighbor() override {
1253 const int64_t destination = BaseNode(1);
1254 const int64_t before_to_move = BaseNode(0);
1255 const int64_t node_to_inactivate = Next(destination);
1256 if (node_to_inactivate == before_to_move || IsPathEnd(node_to_inactivate) ||
1257 !MakeChainInactive(destination, node_to_inactivate)) {
1258 return false;
1259 }
1260 const int64_t node = Next(before_to_move);
1261 return !IsPathEnd(node) && MoveChain(before_to_move, node, destination);
1262 }
1263
1264 std::string DebugString() const override {
1265 return "RelocateAndMakeInactiveOperator";
1266 }
1267};
1268
1269// ----- MakeChainInactiveOperator -----
1270
1271// Operator which makes a "chain" of path nodes inactive.
1272// Possible neighbors for the path 1 -> 2 -> 3 -> 4 (where 1 and 4 are first
1273// and last nodes of the path) are:
1274// 1 -> 3 -> 4 with 2 inactive
1275// 1 -> 2 -> 4 with 3 inactive
1276// 1 -> 4 with 2 and 3 inactive
1277
1279 public:
1280 MakeChainInactiveOperator(const std::vector<IntVar*>& vars,
1281 const std::vector<IntVar*>& secondary_vars,
1282 std::function<int(int64_t)> start_empty_path_class)
1283 : PathOperator(vars, secondary_vars, 2, true, false,
1284 std::move(start_empty_path_class)) {}
1286 bool MakeNeighbor() override {
1287 return MakeChainInactive(BaseNode(0), BaseNode(1));
1288 }
1289
1290 std::string DebugString() const override {
1291 return "MakeChainInactiveOperator";
1292 }
1293
1294 protected:
1295 bool OnSamePathAsPreviousBase(int64_t base_index) override {
1296 // Start and end of chain (defined by both base nodes) must be on the same
1297 // path.
1298 return true;
1299 }
1300
1301 int64_t GetBaseNodeRestartPosition(int base_index) override {
1302 // Base node 1 must be after base node 0.
1303 return (base_index == 0) ? StartNode(base_index) : BaseNode(base_index - 1);
1304 }
1305};
1306
1307// ----- SwapActiveOperator -----
1308
1309// SwapActiveOperator replaces an active node by an inactive one.
1310// Possible neighbors for the path 1 -> 2 -> 3 -> 4 with 5 inactive (where 1 and
1311// 4 are first and last nodes of the path) are:
1312// 1 -> 5 -> 3 -> 4 & 2 inactive
1313// 1 -> 2 -> 5 -> 4 & 3 inactive
1314
1316 public:
1317 SwapActiveOperator(const std::vector<IntVar*>& vars,
1318 const std::vector<IntVar*>& secondary_vars,
1319 std::function<int(int64_t)> start_empty_path_class)
1320 : BaseInactiveNodeToPathOperator(vars, secondary_vars, 1,
1321 std::move(start_empty_path_class)) {}
1323 bool MakeNeighbor() override;
1324
1325 std::string DebugString() const override { return "SwapActiveOperator"; }
1326};
1327
1329 const int64_t base = BaseNode(0);
1330 return MakeChainInactive(base, Next(base)) &&
1331 MakeActive(GetInactiveNode(), base);
1332}
1333
1334// ----- ExtendedSwapActiveOperator -----
1335
1336// ExtendedSwapActiveOperator makes an inactive node active and an active one
1337// inactive. It is similar to SwapActiveOperator excepts that it tries to
1338// insert the inactive node in all possible positions instead of just the
1339// position of the node made inactive.
1340// Possible neighbors for the path 1 -> 2 -> 3 -> 4 with 5 inactive (where 1 and
1341// 4 are first and last nodes of the path) are:
1342// 1 -> 5 -> 3 -> 4 & 2 inactive
1343// 1 -> 3 -> 5 -> 4 & 2 inactive
1344// 1 -> 5 -> 2 -> 4 & 3 inactive
1345// 1 -> 2 -> 5 -> 4 & 3 inactive
1346
1348 public:
1349 ExtendedSwapActiveOperator(const std::vector<IntVar*>& vars,
1350 const std::vector<IntVar*>& secondary_vars,
1351 std::function<int(int64_t)> start_empty_path_class)
1352 : BaseInactiveNodeToPathOperator(vars, secondary_vars, 2,
1353 std::move(start_empty_path_class)) {}
1355 bool MakeNeighbor() override;
1356
1357 std::string DebugString() const override {
1358 return "ExtendedSwapActiveOperator";
1359 }
1360};
1361
1363 const int64_t base0 = BaseNode(0);
1364 const int64_t base1 = BaseNode(1);
1365 if (Next(base0) == base1) {
1366 return false;
1367 }
1368 return MakeChainInactive(base0, Next(base0)) &&
1369 MakeActive(GetInactiveNode(), base1);
1370}
1371
1372// ----- TSP-based operators -----
1373
1374// Sliding TSP operator
1375// Uses an exact dynamic programming algorithm to solve the TSP corresponding
1376// to path sub-chains.
1377// For a subchain 1 -> 2 -> 3 -> 4 -> 5 -> 6, solves the TSP on nodes A, 2, 3,
1378// 4, 5, where A is a merger of nodes 1 and 6 such that cost(A,i) = cost(1,i)
1379// and cost(i,A) = cost(i,6).
1380
1381class TSPOpt : public PathOperator {
1382 public:
1383 TSPOpt(const std::vector<IntVar*>& vars,
1384 const std::vector<IntVar*>& secondary_vars,
1385 Solver::IndexEvaluator3 evaluator, int chain_length);
1386 ~TSPOpt() override {}
1387 bool MakeNeighbor() override;
1388
1389 std::string DebugString() const override { return "TSPOpt"; }
1390
1391 private:
1392 std::vector<std::vector<int64_t>> cost_;
1394 hamiltonian_path_solver_;
1395 Solver::IndexEvaluator3 evaluator_;
1396 const int chain_length_;
1397};
1398
1399TSPOpt::TSPOpt(const std::vector<IntVar*>& vars,
1400 const std::vector<IntVar*>& secondary_vars,
1401 Solver::IndexEvaluator3 evaluator, int chain_length)
1402 : PathOperator(vars, secondary_vars, 1, true, false, nullptr),
1403 hamiltonian_path_solver_(cost_),
1404 evaluator_(std::move(evaluator)),
1405 chain_length_(chain_length) {}
1406
1408 std::vector<int64_t> nodes;
1409 int64_t chain_end = BaseNode(0);
1410 for (int i = 0; i < chain_length_ + 1; ++i) {
1411 nodes.push_back(chain_end);
1412 if (IsPathEnd(chain_end)) {
1413 break;
1414 }
1415 chain_end = Next(chain_end);
1416 }
1417 if (nodes.size() <= 3) {
1418 return false;
1419 }
1420 int64_t chain_path = Path(BaseNode(0));
1421 const int size = nodes.size() - 1;
1422 cost_.resize(size);
1423 for (int i = 0; i < size; ++i) {
1424 cost_[i].resize(size);
1425 cost_[i][0] = evaluator_(nodes[i], nodes[size], chain_path);
1426 for (int j = 1; j < size; ++j) {
1427 cost_[i][j] = evaluator_(nodes[i], nodes[j], chain_path);
1428 }
1429 }
1430 hamiltonian_path_solver_.ChangeCostMatrix(cost_);
1431 std::vector<PathNodeIndex> path;
1432 hamiltonian_path_solver_.TravelingSalesmanPath(&path);
1433 CHECK_EQ(size + 1, path.size());
1434 for (int i = 0; i < size - 1; ++i) {
1435 SetNext(nodes[path[i]], nodes[path[i + 1]], chain_path);
1436 }
1437 SetNext(nodes[path[size - 1]], nodes[size], chain_path);
1438 return true;
1439}
1440
1441// TSP-base lns
1442// Randomly merge consecutive nodes until n "meta"-nodes remain and solve the
1443// corresponding TSP. This can be seen as a large neighborhood search operator
1444// although decisions are taken with the operator.
1445// This is an "unlimited" neighborhood which must be stopped by search limits.
1446// To force diversification, the operator iteratively forces each node to serve
1447// as base of a meta-node.
1448
1449class TSPLns : public PathOperator {
1450 public:
1451 TSPLns(const std::vector<IntVar*>& vars,
1452 const std::vector<IntVar*>& secondary_vars,
1453 Solver::IndexEvaluator3 evaluator, int tsp_size);
1454 ~TSPLns() override {}
1455 bool MakeNeighbor() override;
1456
1457 std::string DebugString() const override { return "TSPLns"; }
1458
1459 protected:
1460 bool MakeOneNeighbor() override;
1461
1462 private:
1463 void OnNodeInitialization() override {
1464 // NOTE: Avoid any computations if there are no vars added.
1465 has_long_enough_paths_ = Size() != 0;
1466 }
1467
1468 std::vector<std::vector<int64_t>> cost_;
1469 HamiltonianPathSolver<int64_t, std::vector<std::vector<int64_t>>>
1470 hamiltonian_path_solver_;
1471 Solver::IndexEvaluator3 evaluator_;
1472 const int tsp_size_;
1473 std::mt19937 rand_;
1474 bool has_long_enough_paths_;
1475};
1476
1477TSPLns::TSPLns(const std::vector<IntVar*>& vars,
1478 const std::vector<IntVar*>& secondary_vars,
1479 Solver::IndexEvaluator3 evaluator, int tsp_size)
1480 : PathOperator(vars, secondary_vars, 1, true, false, nullptr),
1481 hamiltonian_path_solver_(cost_),
1482 evaluator_(std::move(evaluator)),
1483 tsp_size_(tsp_size),
1484 rand_(CpRandomSeed()),
1485 has_long_enough_paths_(true) {
1486 CHECK_GE(tsp_size_, 0);
1487 cost_.resize(tsp_size_);
1488 for (int i = 0; i < tsp_size_; ++i) {
1489 cost_[i].resize(tsp_size_);
1490 }
1491}
1492
1494 while (has_long_enough_paths_) {
1495 has_long_enough_paths_ = false;
1497 return true;
1498 }
1499 Var(0)->solver()->TopPeriodicCheck();
1500 }
1501 return false;
1502}
1503
1505 const int64_t base_node = BaseNode(0);
1506 std::vector<int64_t> nodes;
1507 for (int64_t node = StartNode(0); !IsPathEnd(node); node = Next(node)) {
1508 nodes.push_back(node);
1509 }
1510 if (nodes.size() <= tsp_size_) {
1511 return false;
1512 }
1513 has_long_enough_paths_ = true;
1514 // Randomly select break nodes (final nodes of a meta-node, after which
1515 // an arc is relaxed.
1516 absl::flat_hash_set<int64_t> breaks_set;
1517 // Always add base node to break nodes (diversification)
1518 breaks_set.insert(base_node);
1519 CHECK(!nodes.empty()); // Should have been caught earlier.
1520 while (breaks_set.size() < tsp_size_) {
1521 breaks_set.insert(nodes[absl::Uniform<int>(rand_, 0, nodes.size())]);
1522 }
1523 CHECK_EQ(breaks_set.size(), tsp_size_);
1524 // Setup break node indexing and internal meta-node cost (cost of partial
1525 // route starting at first node of the meta-node and ending at its last node);
1526 // this cost has to be added to the TSP matrix cost in order to respect the
1527 // triangle inequality.
1528 std::vector<int> breaks;
1529 std::vector<int64_t> meta_node_costs;
1530 int64_t cost = 0;
1531 int64_t node = StartNode(0);
1532 int64_t node_path = Path(node);
1533 while (!IsPathEnd(node)) {
1534 int64_t next = Next(node);
1535 if (breaks_set.contains(node)) {
1536 breaks.push_back(node);
1537 meta_node_costs.push_back(cost);
1538 cost = 0;
1539 } else {
1540 cost = CapAdd(cost, evaluator_(node, next, node_path));
1541 }
1542 node = next;
1543 }
1544 meta_node_costs[0] += cost;
1545 CHECK_EQ(breaks.size(), tsp_size_);
1546 // Setup TSP cost matrix
1547 CHECK_EQ(meta_node_costs.size(), tsp_size_);
1548 for (int i = 0; i < tsp_size_; ++i) {
1549 cost_[i][0] =
1550 CapAdd(meta_node_costs[i],
1551 evaluator_(breaks[i], Next(breaks[tsp_size_ - 1]), node_path));
1552 for (int j = 1; j < tsp_size_; ++j) {
1553 cost_[i][j] =
1554 CapAdd(meta_node_costs[i],
1555 evaluator_(breaks[i], Next(breaks[j - 1]), node_path));
1556 }
1557 cost_[i][i] = 0;
1558 }
1559 // Solve TSP and inject solution in delta (only if it leads to a new solution)
1560 hamiltonian_path_solver_.ChangeCostMatrix(cost_);
1561 std::vector<PathNodeIndex> path;
1562 hamiltonian_path_solver_.TravelingSalesmanPath(&path);
1563 bool nochange = true;
1564 for (int i = 0; i < path.size() - 1; ++i) {
1565 if (path[i] != i) {
1566 nochange = false;
1567 break;
1568 }
1569 }
1570 if (nochange) {
1571 return false;
1572 }
1573 CHECK_EQ(0, path[path.size() - 1]);
1574 for (int i = 0; i < tsp_size_ - 1; ++i) {
1575 SetNext(breaks[path[i]], OldNext(breaks[path[i + 1] - 1]), node_path);
1576 }
1577 SetNext(breaks[path[tsp_size_ - 1]], OldNext(breaks[tsp_size_ - 1]),
1578 node_path);
1579 return true;
1580}
1581
1582// ----- Lin Kernighan -----
1583
1584// For each variable in vars, stores the 'size' pairs(i,j) with the smallest
1585// value according to evaluator, where i is the index of the variable in vars
1586// and j is in the domain of the variable.
1587// Note that the resulting pairs are sorted.
1588// Works in O(size) per variable on average (same approach as qsort)
1589
1591 public:
1593 const PathOperator& path_operator, int size);
1595 void Initialize();
1596 const std::vector<int>& Neighbors(int index) const;
1597
1598 virtual std::string DebugString() const { return "NearestNeighbors"; }
1599
1600 private:
1601 void ComputeNearest(int row);
1602
1603 std::vector<std::vector<int>> neighbors_;
1604 Solver::IndexEvaluator3 evaluator_;
1605 const PathOperator& path_operator_;
1606 const int size_;
1607 bool initialized_;
1608
1609 DISALLOW_COPY_AND_ASSIGN(NearestNeighbors);
1610};
1611
1613 const PathOperator& path_operator, int size)
1614 : evaluator_(std::move(evaluator)),
1615 path_operator_(path_operator),
1616 size_(size),
1617 initialized_(false) {}
1618
1620 // TODO(user): recompute if node changes path ?
1621 if (!initialized_) {
1622 initialized_ = true;
1623 for (int i = 0; i < path_operator_.number_of_nexts(); ++i) {
1624 neighbors_.push_back(std::vector<int>());
1625 ComputeNearest(i);
1626 }
1627 }
1628}
1629
1630const std::vector<int>& NearestNeighbors::Neighbors(int index) const {
1631 return neighbors_[index];
1632}
1633
1634void NearestNeighbors::ComputeNearest(int row) {
1635 // Find size_ nearest neighbors for row of index 'row'.
1636 const int path = path_operator_.Path(row);
1637 const IntVar* var = path_operator_.Var(row);
1638 const int64_t var_min = var->Min();
1639 const int var_size = var->Max() - var_min + 1;
1640 using ValuedIndex = std::pair<int64_t /*value*/, int /*index*/>;
1641 std::vector<ValuedIndex> neighbors(var_size);
1642 for (int i = 0; i < var_size; ++i) {
1643 const int index = i + var_min;
1644 neighbors[i] = std::make_pair(evaluator_(row, index, path), index);
1645 }
1646 if (var_size > size_) {
1647 std::nth_element(neighbors.begin(), neighbors.begin() + size_ - 1,
1648 neighbors.end());
1649 }
1650
1651 // Setup global neighbor matrix for row row_index
1652 for (int i = 0; i < std::min(size_, var_size); ++i) {
1653 neighbors_[row].push_back(neighbors[i].second);
1654 }
1655 std::sort(neighbors_[row].begin(), neighbors_[row].end());
1656}
1657
1659 public:
1660 LinKernighan(const std::vector<IntVar*>& vars,
1661 const std::vector<IntVar*>& secondary_vars,
1662 const Solver::IndexEvaluator3& evaluator, bool topt);
1663 ~LinKernighan() override;
1664 bool MakeNeighbor() override;
1665
1666 std::string DebugString() const override { return "LinKernighan"; }
1667
1668 private:
1669 void OnNodeInitialization() override;
1670
1671 static const int kNeighbors;
1672
1673 bool InFromOut(int64_t in_i, int64_t in_j, int64_t* out, int64_t* gain);
1674
1675 Solver::IndexEvaluator3 const evaluator_;
1676 NearestNeighbors neighbors_;
1677 absl::flat_hash_set<int64_t> marked_;
1678 const bool topt_;
1679};
1680
1681// While the accumulated local gain is positive, perform a 2opt or a 3opt move
1682// followed by a series of 2opt moves. Return a neighbor for which the global
1683// gain is positive.
1684
1685LinKernighan::LinKernighan(const std::vector<IntVar*>& vars,
1686 const std::vector<IntVar*>& secondary_vars,
1687 const Solver::IndexEvaluator3& evaluator, bool topt)
1688 : PathOperator(vars, secondary_vars, 1, true, false, nullptr),
1689 evaluator_(evaluator),
1690 neighbors_(evaluator, *this, kNeighbors),
1691 topt_(topt) {}
1692
1694
1695void LinKernighan::OnNodeInitialization() { neighbors_.Initialize(); }
1696
1698 marked_.clear();
1699 int64_t node = BaseNode(0);
1700 int64_t path = Path(node);
1701 int64_t base = node;
1702 int64_t next = Next(node);
1703 if (IsPathEnd(next)) return false;
1704 int64_t out = -1;
1705 int64_t gain = 0;
1706 marked_.insert(node);
1707 if (topt_) { // Try a 3opt first
1708 if (!InFromOut(node, next, &out, &gain)) return false;
1709 marked_.insert(next);
1710 marked_.insert(out);
1711 const int64_t node1 = out;
1712 if (IsPathEnd(node1)) return false;
1713 const int64_t next1 = Next(node1);
1714 if (IsPathEnd(next1)) return false;
1715 if (!InFromOut(node1, next1, &out, &gain)) return false;
1716 marked_.insert(next1);
1717 marked_.insert(out);
1718 if (!CheckChainValidity(out, node1, node) || !MoveChain(out, node1, node)) {
1719 return false;
1720 }
1721 const int64_t next_out = Next(out);
1722 const int64_t in_cost = evaluator_(node, next_out, path);
1723 const int64_t out_cost = evaluator_(out, next_out, path);
1724 if (CapAdd(CapSub(gain, in_cost), out_cost) > 0) return true;
1725 node = out;
1726 if (IsPathEnd(node)) return false;
1727 next = next_out;
1728 if (IsPathEnd(next)) return false;
1729 }
1730 // Try 2opts
1731 while (InFromOut(node, next, &out, &gain)) {
1732 marked_.insert(next);
1733 marked_.insert(out);
1734 int64_t chain_last;
1735 if (!ReverseChain(node, out, &chain_last)) {
1736 return false;
1737 }
1738 int64_t in_cost = evaluator_(base, chain_last, path);
1739 int64_t out_cost = evaluator_(chain_last, out, path);
1740 if (CapAdd(CapSub(gain, in_cost), out_cost) > 0) {
1741 return true;
1742 }
1743 node = chain_last;
1744 if (IsPathEnd(node)) {
1745 return false;
1746 }
1747 next = out;
1748 if (IsPathEnd(next)) {
1749 return false;
1750 }
1751 }
1752 return false;
1753}
1754
1755const int LinKernighan::kNeighbors = 5 + 1;
1756
1757bool LinKernighan::InFromOut(int64_t in_i, int64_t in_j, int64_t* out,
1758 int64_t* gain) {
1759 const std::vector<int>& nexts = neighbors_.Neighbors(in_j);
1760 int64_t best_gain = std::numeric_limits<int64_t>::min();
1761 int64_t path = Path(in_i);
1762 int64_t out_cost = evaluator_(in_i, in_j, path);
1763 const int64_t current_gain = CapAdd(*gain, out_cost);
1764 for (int k = 0; k < nexts.size(); ++k) {
1765 const int64_t next = nexts[k];
1766 if (next != in_j) {
1767 int64_t in_cost = evaluator_(in_j, next, path);
1768 int64_t new_gain = CapSub(current_gain, in_cost);
1769 if (new_gain > 0 && next != Next(in_j) && marked_.count(in_j) == 0 &&
1770 marked_.count(next) == 0) {
1771 if (best_gain < new_gain) {
1772 *out = next;
1773 best_gain = new_gain;
1774 }
1775 }
1776 }
1777 }
1778 *gain = best_gain;
1779 return (best_gain > std::numeric_limits<int64_t>::min());
1780}
1781
1782// ----- Path-based Large Neighborhood Search -----
1783
1784// Breaks "number_of_chunks" chains of "chunk_size" arcs, and deactivate all
1785// inactive nodes if "unactive_fragments" is true.
1786// As a special case, if chunk_size=0, then we break full paths.
1787
1788class PathLns : public PathOperator {
1789 public:
1790 PathLns(const std::vector<IntVar*>& vars,
1791 const std::vector<IntVar*>& secondary_vars, int number_of_chunks,
1792 int chunk_size, bool unactive_fragments)
1793 : PathOperator(vars, secondary_vars, number_of_chunks, true, true,
1794 nullptr),
1795 number_of_chunks_(number_of_chunks),
1796 chunk_size_(chunk_size),
1797 unactive_fragments_(unactive_fragments) {
1798 CHECK_GE(chunk_size_, 0);
1799 }
1800 ~PathLns() override {}
1801 bool MakeNeighbor() override;
1802
1803 std::string DebugString() const override { return "PathLns"; }
1804 bool HasFragments() const override { return true; }
1805
1806 private:
1807 inline bool ChainsAreFullPaths() const { return chunk_size_ == 0; }
1808 void DeactivateChain(int64_t node);
1809 void DeactivateUnactives();
1810
1811 const int number_of_chunks_;
1812 const int chunk_size_;
1813 const bool unactive_fragments_;
1814};
1815
1817 if (ChainsAreFullPaths()) {
1818 // Reject the current position as a neighbor if any of its base node
1819 // isn't at the start of a path.
1820 // TODO(user): make this more efficient.
1821 for (int i = 0; i < number_of_chunks_; ++i) {
1822 if (BaseNode(i) != StartNode(i)) return false;
1823 }
1824 }
1825 for (int i = 0; i < number_of_chunks_; ++i) {
1826 DeactivateChain(BaseNode(i));
1827 }
1828 DeactivateUnactives();
1829 return true;
1830}
1831
1832void PathLns::DeactivateChain(int64_t node) {
1833 for (int i = 0, current = node;
1834 (ChainsAreFullPaths() || i < chunk_size_) && !IsPathEnd(current);
1835 ++i, current = Next(current)) {
1836 Deactivate(current);
1837 if (!ignore_path_vars_) {
1838 Deactivate(number_of_nexts_ + current);
1839 }
1840 }
1841}
1842
1843void PathLns::DeactivateUnactives() {
1844 if (unactive_fragments_) {
1845 for (int i = 0; i < Size(); ++i) {
1846 if (IsInactive(i)) {
1847 Deactivate(i);
1848 if (!ignore_path_vars_) {
1850 }
1851 }
1852 }
1853 }
1854}
1855
1856// ----- Limit the number of neighborhoods explored -----
1857
1859 public:
1860 NeighborhoodLimit(LocalSearchOperator* const op, int64_t limit)
1861 : operator_(op), limit_(limit), next_neighborhood_calls_(0) {
1862 CHECK(op != nullptr);
1863 CHECK_GT(limit, 0);
1864 }
1865
1866 void Start(const Assignment* assignment) override {
1867 next_neighborhood_calls_ = 0;
1868 operator_->Start(assignment);
1869 }
1870
1871 bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override {
1872 if (next_neighborhood_calls_ >= limit_) {
1873 return false;
1874 }
1875 ++next_neighborhood_calls_;
1876 return operator_->MakeNextNeighbor(delta, deltadelta);
1877 }
1878
1879 bool HoldsDelta() const override { return operator_->HoldsDelta(); }
1880
1881 std::string DebugString() const override { return "NeighborhoodLimit"; }
1882
1883 private:
1884 LocalSearchOperator* const operator_;
1885 const int64_t limit_;
1886 int64_t next_neighborhood_calls_;
1887};
1888
1890 LocalSearchOperator* const op, int64_t limit) {
1891 return RevAlloc(new NeighborhoodLimit(op, limit));
1892}
1893
1894// ----- Concatenation of operators -----
1895
1896namespace {
1897class CompoundOperator : public LocalSearchOperator {
1898 public:
1899 CompoundOperator(std::vector<LocalSearchOperator*> operators,
1900 std::function<int64_t(int, int)> evaluator);
1901 ~CompoundOperator() override {}
1902 void Reset() override;
1903 void Start(const Assignment* assignment) override;
1904 bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override;
1905 bool HasFragments() const override { return has_fragments_; }
1906 bool HoldsDelta() const override { return true; }
1907
1908 std::string DebugString() const override {
1909 return operators_.empty()
1910 ? ""
1911 : operators_[operator_indices_[index_]]->DebugString();
1912 }
1913 const LocalSearchOperator* Self() const override {
1914 return operators_.empty() ? this
1915 : operators_[operator_indices_[index_]]->Self();
1916 }
1917
1918 private:
1919 class OperatorComparator {
1920 public:
1921 OperatorComparator(std::function<int64_t(int, int)> evaluator,
1922 int active_operator)
1923 : evaluator_(std::move(evaluator)), active_operator_(active_operator) {}
1924 bool operator()(int lhs, int rhs) const {
1925 const int64_t lhs_value = Evaluate(lhs);
1926 const int64_t rhs_value = Evaluate(rhs);
1927 return lhs_value < rhs_value || (lhs_value == rhs_value && lhs < rhs);
1928 }
1929
1930 private:
1931 int64_t Evaluate(int operator_index) const {
1932 return evaluator_(active_operator_, operator_index);
1933 }
1934
1935 std::function<int64_t(int, int)> evaluator_;
1936 const int active_operator_;
1937 };
1938
1939 int64_t index_;
1940 std::vector<LocalSearchOperator*> operators_;
1941 std::vector<int> operator_indices_;
1942 std::function<int64_t(int, int)> evaluator_;
1943 Bitset64<> started_;
1944 const Assignment* start_assignment_;
1945 bool has_fragments_;
1946};
1947
1948CompoundOperator::CompoundOperator(std::vector<LocalSearchOperator*> operators,
1949 std::function<int64_t(int, int)> evaluator)
1950 : index_(0),
1951 operators_(std::move(operators)),
1952 evaluator_(std::move(evaluator)),
1953 started_(operators_.size()),
1954 start_assignment_(nullptr),
1955 has_fragments_(false) {
1956 operators_.erase(std::remove(operators_.begin(), operators_.end(), nullptr),
1957 operators_.end());
1958 operator_indices_.resize(operators_.size());
1959 std::iota(operator_indices_.begin(), operator_indices_.end(), 0);
1960 for (LocalSearchOperator* const op : operators_) {
1961 if (op->HasFragments()) {
1962 has_fragments_ = true;
1963 break;
1964 }
1965 }
1966}
1967
1968void CompoundOperator::Reset() {
1969 for (LocalSearchOperator* const op : operators_) {
1970 op->Reset();
1971 }
1972}
1973
1974void CompoundOperator::Start(const Assignment* assignment) {
1975 start_assignment_ = assignment;
1976 started_.ClearAll();
1977 if (!operators_.empty()) {
1978 OperatorComparator comparator(evaluator_, operator_indices_[index_]);
1979 std::sort(operator_indices_.begin(), operator_indices_.end(), comparator);
1980 index_ = 0;
1981 }
1982}
1983
1984bool CompoundOperator::MakeNextNeighbor(Assignment* delta,
1985 Assignment* deltadelta) {
1986 if (!operators_.empty()) {
1987 do {
1988 // TODO(user): keep copy of delta in case MakeNextNeighbor
1989 // pollutes delta on a fail.
1990 const int64_t operator_index = operator_indices_[index_];
1991 if (!started_[operator_index]) {
1992 operators_[operator_index]->Start(start_assignment_);
1993 started_.Set(operator_index);
1994 }
1995 if (!operators_[operator_index]->HoldsDelta()) {
1996 delta->Clear();
1997 }
1998 if (operators_[operator_index]->MakeNextNeighbor(delta, deltadelta)) {
1999 return true;
2000 }
2001 ++index_;
2002 delta->Clear();
2003 if (index_ == operators_.size()) {
2004 index_ = 0;
2005 }
2006 } while (index_ != 0);
2007 }
2008 return false;
2009}
2010
2011int64_t CompoundOperatorNoRestart(int size, int active_index,
2012 int operator_index) {
2013 return (operator_index < active_index) ? size + operator_index - active_index
2014 : operator_index - active_index;
2015}
2016
2017int64_t CompoundOperatorRestart(int active_index, int operator_index) {
2018 return 0;
2019}
2020} // namespace
2021
2023 const std::vector<LocalSearchOperator*>& ops) {
2024 return ConcatenateOperators(ops, false);
2025}
2026
2028 const std::vector<LocalSearchOperator*>& ops, bool restart) {
2029 if (restart) {
2030 std::function<int64_t(int, int)> eval = CompoundOperatorRestart;
2031 return ConcatenateOperators(ops, eval);
2032 }
2033 const int size = ops.size();
2034 return ConcatenateOperators(ops, [size](int i, int j) {
2035 return CompoundOperatorNoRestart(size, i, j);
2036 });
2037}
2038
2040 const std::vector<LocalSearchOperator*>& ops,
2041 std::function<int64_t(int, int)> evaluator) {
2042 return RevAlloc(new CompoundOperator(ops, std::move(evaluator)));
2043}
2044
2045namespace {
2046class RandomCompoundOperator : public LocalSearchOperator {
2047 public:
2048 explicit RandomCompoundOperator(std::vector<LocalSearchOperator*> operators);
2049 RandomCompoundOperator(std::vector<LocalSearchOperator*> operators,
2050 int32_t seed);
2051 ~RandomCompoundOperator() override {}
2052 void Reset() override;
2053 void Start(const Assignment* assignment) override;
2054 bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override;
2055 bool HoldsDelta() const override { return true; }
2056
2057 std::string DebugString() const override { return "RandomCompoundOperator"; }
2058 // TODO(user): define Self method.
2059
2060 private:
2061 std::mt19937 rand_;
2062 const std::vector<LocalSearchOperator*> operators_;
2063 bool has_fragments_;
2064};
2065
2066void RandomCompoundOperator::Start(const Assignment* assignment) {
2067 for (LocalSearchOperator* const op : operators_) {
2068 op->Start(assignment);
2069 }
2070}
2071
2072RandomCompoundOperator::RandomCompoundOperator(
2073 std::vector<LocalSearchOperator*> operators)
2074 : RandomCompoundOperator(std::move(operators), CpRandomSeed()) {}
2075
2076RandomCompoundOperator::RandomCompoundOperator(
2077 std::vector<LocalSearchOperator*> operators, int32_t seed)
2078 : rand_(seed), operators_(std::move(operators)), has_fragments_(false) {
2079 for (LocalSearchOperator* const op : operators_) {
2080 if (op->HasFragments()) {
2081 has_fragments_ = true;
2082 break;
2083 }
2084 }
2085}
2086
2087void RandomCompoundOperator::Reset() {
2088 for (LocalSearchOperator* const op : operators_) {
2089 op->Reset();
2090 }
2091}
2092
2093bool RandomCompoundOperator::MakeNextNeighbor(Assignment* delta,
2094 Assignment* deltadelta) {
2095 const int size = operators_.size();
2096 std::vector<int> indices(size);
2097 std::iota(indices.begin(), indices.end(), 0);
2098 std::shuffle(indices.begin(), indices.end(), rand_);
2099 for (int index : indices) {
2100 if (!operators_[index]->HoldsDelta()) {
2101 delta->Clear();
2102 }
2103 if (operators_[index]->MakeNextNeighbor(delta, deltadelta)) {
2104 return true;
2105 }
2106 delta->Clear();
2107 }
2108 return false;
2109}
2110} // namespace
2111
2113 const std::vector<LocalSearchOperator*>& ops) {
2114 return RevAlloc(new RandomCompoundOperator(ops));
2115}
2116
2118 const std::vector<LocalSearchOperator*>& ops, int32_t seed) {
2119 return RevAlloc(new RandomCompoundOperator(ops, seed));
2120}
2121
2122namespace {
2123class MultiArmedBanditCompoundOperator : public LocalSearchOperator {
2124 public:
2125 explicit MultiArmedBanditCompoundOperator(
2126 std::vector<LocalSearchOperator*> operators, double memory_coefficient,
2127 double exploration_coefficient, bool maximize);
2128 ~MultiArmedBanditCompoundOperator() override {}
2129 void Reset() override;
2130 void Start(const Assignment* assignment) override;
2131 bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override;
2132 bool HoldsDelta() const override { return true; }
2133
2134 std::string DebugString() const override {
2135 return operators_.empty()
2136 ? ""
2137 : operators_[operator_indices_[index_]]->DebugString();
2138 }
2139 const LocalSearchOperator* Self() const override {
2140 return operators_.empty() ? this
2141 : operators_[operator_indices_[index_]]->Self();
2142 }
2143
2144 private:
2145 double Score(int index);
2146 int index_;
2147 std::vector<LocalSearchOperator*> operators_;
2148 Bitset64<> started_;
2149 const Assignment* start_assignment_;
2150 bool has_fragments_;
2151 std::vector<int> operator_indices_;
2152 int64_t last_objective_;
2153 std::vector<double> avg_improvement_;
2154 int num_neighbors_;
2155 std::vector<double> num_neighbors_per_operator_;
2156 const bool maximize_;
2157 // Sets how much the objective improvement of previous accepted neighbors
2158 // influence the current average improvement. The formula is
2159 // avg_improvement +=
2160 // memory_coefficient * (current_improvement - avg_improvement).
2161 const double memory_coefficient_;
2162 // Sets how often we explore rarely used and unsuccessful in the past
2163 // operators. Operators are sorted by
2164 // avg_improvement_[i] + exploration_coefficient_ *
2165 // sqrt(2 * log(1 + num_neighbors_) / (1 + num_neighbors_per_operator_[i]));
2166 const double exploration_coefficient_;
2167};
2168
2169MultiArmedBanditCompoundOperator::MultiArmedBanditCompoundOperator(
2170 std::vector<LocalSearchOperator*> operators, double memory_coefficient,
2171 double exploration_coefficient, bool maximize)
2172 : index_(0),
2173 operators_(std::move(operators)),
2174 started_(operators_.size()),
2175 start_assignment_(nullptr),
2176 has_fragments_(false),
2177 last_objective_(std::numeric_limits<int64_t>::max()),
2178 num_neighbors_(0),
2179 maximize_(maximize),
2180 memory_coefficient_(memory_coefficient),
2181 exploration_coefficient_(exploration_coefficient) {
2182 DCHECK_GE(memory_coefficient_, 0);
2183 DCHECK_LE(memory_coefficient_, 1);
2184 DCHECK_GE(exploration_coefficient_, 0);
2185 operators_.erase(std::remove(operators_.begin(), operators_.end(), nullptr),
2186 operators_.end());
2187 operator_indices_.resize(operators_.size());
2188 std::iota(operator_indices_.begin(), operator_indices_.end(), 0);
2189 num_neighbors_per_operator_.resize(operators_.size(), 0);
2190 avg_improvement_.resize(operators_.size(), 0);
2191 for (LocalSearchOperator* const op : operators_) {
2192 if (op->HasFragments()) {
2193 has_fragments_ = true;
2194 break;
2195 }
2196 }
2197}
2198
2199void MultiArmedBanditCompoundOperator::Reset() {
2200 for (LocalSearchOperator* const op : operators_) {
2201 op->Reset();
2202 }
2203}
2204
2205double MultiArmedBanditCompoundOperator::Score(int index) {
2206 return avg_improvement_[index] +
2207 exploration_coefficient_ *
2208 sqrt(2 * log(1 + num_neighbors_) /
2209 (1 + num_neighbors_per_operator_[index]));
2210}
2211
2212void MultiArmedBanditCompoundOperator::Start(const Assignment* assignment) {
2213 start_assignment_ = assignment;
2214 started_.ClearAll();
2215 if (operators_.empty()) return;
2216
2217 const double objective = assignment->ObjectiveValue();
2218
2219 if (objective == last_objective_) return;
2220 // Skip a neighbor evaluation if last_objective_ hasn't been set yet.
2221 if (last_objective_ == std::numeric_limits<int64_t>::max()) {
2222 last_objective_ = objective;
2223 return;
2224 }
2225
2226 const double improvement =
2227 maximize_ ? objective - last_objective_ : last_objective_ - objective;
2228 if (improvement < 0) {
2229 return;
2230 }
2231 last_objective_ = objective;
2232 avg_improvement_[operator_indices_[index_]] +=
2233 memory_coefficient_ *
2234 (improvement - avg_improvement_[operator_indices_[index_]]);
2235
2236 std::sort(operator_indices_.begin(), operator_indices_.end(),
2237 [this](int lhs, int rhs) {
2238 const double lhs_score = Score(lhs);
2239 const double rhs_score = Score(rhs);
2240 return lhs_score > rhs_score ||
2241 (lhs_score == rhs_score && lhs < rhs);
2242 });
2243
2244 index_ = 0;
2245}
2246
2247bool MultiArmedBanditCompoundOperator::MakeNextNeighbor(
2248 Assignment* delta, Assignment* deltadelta) {
2249 if (operators_.empty()) return false;
2250 do {
2251 const int operator_index = operator_indices_[index_];
2252 if (!started_[operator_index]) {
2253 operators_[operator_index]->Start(start_assignment_);
2254 started_.Set(operator_index);
2255 }
2256 if (!operators_[operator_index]->HoldsDelta()) {
2257 delta->Clear();
2258 }
2259 if (operators_[operator_index]->MakeNextNeighbor(delta, deltadelta)) {
2260 ++num_neighbors_;
2261 ++num_neighbors_per_operator_[operator_index];
2262 return true;
2263 }
2264 ++index_;
2265 delta->Clear();
2266 if (index_ == operators_.size()) {
2267 index_ = 0;
2268 }
2269 } while (index_ != 0);
2270 return false;
2271}
2272} // namespace
2273
2275 const std::vector<LocalSearchOperator*>& ops, double memory_coefficient,
2276 double exploration_coefficient, bool maximize) {
2277 return RevAlloc(new MultiArmedBanditCompoundOperator(
2278 ops, memory_coefficient, exploration_coefficient, maximize));
2279}
2280
2281// ----- Operator factory -----
2282
2283template <class T>
2285 Solver* solver, const std::vector<IntVar*>& vars,
2286 const std::vector<IntVar*>& secondary_vars,
2287 std::function<int(int64_t)> start_empty_path_class) {
2288 return solver->RevAlloc(
2289 new T(vars, secondary_vars, std::move(start_empty_path_class)));
2290}
2291
2292#define MAKE_LOCAL_SEARCH_OPERATOR(OperatorClass) \
2293 template <> \
2294 LocalSearchOperator* MakeLocalSearchOperator<OperatorClass>( \
2295 Solver * solver, const std::vector<IntVar*>& vars, \
2296 const std::vector<IntVar*>& secondary_vars, \
2297 std::function<int(int64_t)> start_empty_path_class) { \
2298 return solver->RevAlloc(new OperatorClass( \
2299 vars, secondary_vars, std::move(start_empty_path_class))); \
2300 }
2301
2306MAKE_LOCAL_SEARCH_OPERATOR(MakeActiveOperator)
2307MAKE_LOCAL_SEARCH_OPERATOR(MakeInactiveOperator)
2308MAKE_LOCAL_SEARCH_OPERATOR(MakeChainInactiveOperator)
2309MAKE_LOCAL_SEARCH_OPERATOR(SwapActiveOperator)
2310MAKE_LOCAL_SEARCH_OPERATOR(ExtendedSwapActiveOperator)
2311MAKE_LOCAL_SEARCH_OPERATOR(MakeActiveAndRelocate)
2312MAKE_LOCAL_SEARCH_OPERATOR(RelocateAndMakeActiveOperator)
2313MAKE_LOCAL_SEARCH_OPERATOR(RelocateAndMakeInactiveOperator)
2314
2315#undef MAKE_LOCAL_SEARCH_OPERATOR
2316
2317LocalSearchOperator* Solver::MakeOperator(const std::vector<IntVar*>& vars,
2319 return MakeOperator(vars, std::vector<IntVar*>(), op);
2320}
2321
2323 const std::vector<IntVar*>& vars,
2324 const std::vector<IntVar*>& secondary_vars,
2326 LocalSearchOperator* result = nullptr;
2327 switch (op) {
2328 case Solver::TWOOPT: {
2329 result = RevAlloc(new TwoOpt(vars, secondary_vars, nullptr));
2330 break;
2331 }
2332 case Solver::OROPT: {
2333 std::vector<LocalSearchOperator*> operators;
2334 for (int i = 1; i < 4; ++i) {
2335 operators.push_back(RevAlloc(
2336 new Relocate(vars, secondary_vars, absl::StrCat("OrOpt<", i, ">"),
2337 nullptr, i, true)));
2338 }
2339 result = ConcatenateOperators(operators);
2340 break;
2341 }
2342 case Solver::RELOCATE: {
2343 result = MakeLocalSearchOperator<Relocate>(this, vars, secondary_vars,
2344 nullptr);
2345 break;
2346 }
2347 case Solver::EXCHANGE: {
2348 result = MakeLocalSearchOperator<Exchange>(this, vars, secondary_vars,
2349 nullptr);
2350 break;
2351 }
2352 case Solver::CROSS: {
2353 result =
2354 MakeLocalSearchOperator<Cross>(this, vars, secondary_vars, nullptr);
2355 break;
2356 }
2357 case Solver::MAKEACTIVE: {
2358 result = MakeLocalSearchOperator<MakeActiveOperator>(
2359 this, vars, secondary_vars, nullptr);
2360 break;
2361 }
2362 case Solver::MAKEINACTIVE: {
2363 result = MakeLocalSearchOperator<MakeInactiveOperator>(
2364 this, vars, secondary_vars, nullptr);
2365 break;
2366 }
2368 result = MakeLocalSearchOperator<MakeChainInactiveOperator>(
2369 this, vars, secondary_vars, nullptr);
2370 break;
2371 }
2372 case Solver::SWAPACTIVE: {
2373 result = MakeLocalSearchOperator<SwapActiveOperator>(
2374 this, vars, secondary_vars, nullptr);
2375 break;
2376 }
2378 result = MakeLocalSearchOperator<ExtendedSwapActiveOperator>(
2379 this, vars, secondary_vars, nullptr);
2380 break;
2381 }
2382 case Solver::PATHLNS: {
2383 result = RevAlloc(new PathLns(vars, secondary_vars, 2, 3, false));
2384 break;
2385 }
2386 case Solver::FULLPATHLNS: {
2387 result = RevAlloc(new PathLns(vars, secondary_vars,
2388 /*number_of_chunks=*/1,
2389 /*chunk_size=*/0,
2390 /*unactive_fragments=*/true));
2391 break;
2392 }
2393 case Solver::UNACTIVELNS: {
2394 result = RevAlloc(new PathLns(vars, secondary_vars, 1, 6, true));
2395 break;
2396 }
2397 case Solver::INCREMENT: {
2398 if (secondary_vars.empty()) {
2399 result = RevAlloc(new IncrementValue(vars));
2400 } else {
2401 LOG(FATAL) << "Operator " << op
2402 << " does not support secondary variables";
2403 }
2404 break;
2405 }
2406 case Solver::DECREMENT: {
2407 if (secondary_vars.empty()) {
2408 result = RevAlloc(new DecrementValue(vars));
2409 } else {
2410 LOG(FATAL) << "Operator " << op
2411 << " does not support secondary variables";
2412 }
2413 break;
2414 }
2415 case Solver::SIMPLELNS: {
2416 if (secondary_vars.empty()) {
2417 result = RevAlloc(new SimpleLns(vars, 1));
2418 } else {
2419 LOG(FATAL) << "Operator " << op
2420 << " does not support secondary variables";
2421 }
2422 break;
2423 }
2424 default:
2425 LOG(FATAL) << "Unknown operator " << op;
2426 }
2427 return result;
2428}
2429
2431 const std::vector<IntVar*>& vars, Solver::IndexEvaluator3 evaluator,
2433 return MakeOperator(vars, std::vector<IntVar*>(), std::move(evaluator), op);
2434}
2435
2437 const std::vector<IntVar*>& vars,
2438 const std::vector<IntVar*>& secondary_vars,
2439 Solver::IndexEvaluator3 evaluator,
2441 LocalSearchOperator* result = nullptr;
2442 switch (op) {
2443 case Solver::LK: {
2444 std::vector<LocalSearchOperator*> operators;
2445 operators.push_back(RevAlloc(
2446 new LinKernighan(vars, secondary_vars, evaluator, /*topt=*/false)));
2447 operators.push_back(RevAlloc(
2448 new LinKernighan(vars, secondary_vars, evaluator, /*topt=*/true)));
2449 result = ConcatenateOperators(operators);
2450 break;
2451 }
2452 case Solver::TSPOPT: {
2453 result = RevAlloc(
2454 new TSPOpt(vars, secondary_vars, evaluator,
2455 absl::GetFlag(FLAGS_cp_local_search_tsp_opt_size)));
2456 break;
2457 }
2458 case Solver::TSPLNS: {
2459 result = RevAlloc(
2460 new TSPLns(vars, secondary_vars, evaluator,
2461 absl::GetFlag(FLAGS_cp_local_search_tsp_lns_size)));
2462 break;
2463 }
2464 default:
2465 LOG(FATAL) << "Unknown operator " << op;
2466 }
2467 return result;
2468}
2469
2470namespace {
2471// Classes for Local Search Operation used in Local Search filters.
2472
2473class SumOperation {
2474 public:
2475 SumOperation() : value_(0) {}
2476 void Init() { value_ = 0; }
2477 void Update(int64_t update) { value_ = CapAdd(value_, update); }
2478 void Remove(int64_t remove) { value_ = CapSub(value_, remove); }
2479 int64_t value() const { return value_; }
2480 void set_value(int64_t new_value) { value_ = new_value; }
2481
2482 private:
2483 int64_t value_;
2484};
2485
2486class ProductOperation {
2487 public:
2488 ProductOperation() : value_(1) {}
2489 void Init() { value_ = 1; }
2490 void Update(int64_t update) { value_ *= update; }
2491 void Remove(int64_t remove) {
2492 if (remove != 0) {
2493 value_ /= remove;
2494 }
2495 }
2496 int64_t value() const { return value_; }
2497 void set_value(int64_t new_value) { value_ = new_value; }
2498
2499 private:
2500 int64_t value_;
2501};
2502
2503class MinOperation {
2504 public:
2505 void Init() { values_set_.clear(); }
2506 void Update(int64_t update) { values_set_.insert(update); }
2507 void Remove(int64_t remove) { values_set_.erase(remove); }
2508 int64_t value() const {
2509 return (!values_set_.empty()) ? *values_set_.begin() : 0;
2510 }
2511 void set_value(int64_t new_value) {}
2512
2513 private:
2514 std::set<int64_t> values_set_;
2515};
2516
2517class MaxOperation {
2518 public:
2519 void Init() { values_set_.clear(); }
2520 void Update(int64_t update) { values_set_.insert(update); }
2521 void Remove(int64_t remove) { values_set_.erase(remove); }
2522 int64_t value() const {
2523 return (!values_set_.empty()) ? *values_set_.rbegin() : 0;
2524 }
2525 void set_value(int64_t new_value) {}
2526
2527 private:
2528 std::set<int64_t> values_set_;
2529};
2530
2531// Always accepts deltas, cost 0.
2532class AcceptFilter : public LocalSearchFilter {
2533 public:
2534 std::string DebugString() const override { return "AcceptFilter"; }
2535 bool Accept(const Assignment* delta, const Assignment* deltadelta,
2536 int64_t obj_min, int64_t obj_max) override {
2537 return true;
2538 }
2539 void Synchronize(const Assignment* assignment,
2540 const Assignment* delta) override {}
2541};
2542} // namespace
2543
2545 return RevAlloc(new AcceptFilter());
2546}
2547
2548namespace {
2549// Never accepts deltas, cost 0.
2550class RejectFilter : public LocalSearchFilter {
2551 public:
2552 std::string DebugString() const override { return "RejectFilter"; }
2553 bool Accept(const Assignment* delta, const Assignment* deltadelta,
2554 int64_t obj_min, int64_t obj_max) override {
2555 return false;
2556 }
2557 void Synchronize(const Assignment* assignment,
2558 const Assignment* delta) override {}
2559};
2560} // namespace
2561
2563 return RevAlloc(new RejectFilter());
2564}
2565
2566PathState::PathState(int num_nodes, std::vector<int> path_start,
2567 std::vector<int> path_end)
2568 : num_nodes_(num_nodes),
2569 num_paths_(path_start.size()),
2570 num_nodes_threshold_(std::max(16, 4 * num_nodes_)) // Arbitrary value.
2571{
2572 DCHECK_EQ(path_start.size(), num_paths_);
2573 DCHECK_EQ(path_end.size(), num_paths_);
2574 for (int p = 0; p < num_paths_; ++p) {
2575 path_start_end_.push_back({path_start[p], path_end[p]});
2576 }
2577 // Initial state is all unperformed: paths go from start to end directly.
2578 committed_index_.assign(num_nodes_, -1);
2579 committed_nodes_.assign(2 * num_paths_, {-1, -1});
2580 chains_.assign(num_paths_ + 1, {-1, -1}); // Reserve 1 more for sentinel.
2581 paths_.assign(num_paths_, {-1, -1});
2582 for (int path = 0; path < num_paths_; ++path) {
2583 const int index = 2 * path;
2584 const PathStartEnd start_end = path_start_end_[path];
2585 committed_index_[start_end.start] = index;
2586 committed_index_[start_end.end] = index + 1;
2587
2588 committed_nodes_[index] = {start_end.start, path};
2589 committed_nodes_[index + 1] = {start_end.end, path};
2590
2591 chains_[path] = {index, index + 2};
2592 paths_[path] = {path, path + 1};
2593 }
2594 chains_[num_paths_] = {0, 0}; // Sentinel.
2595 // Nodes that are not starts or ends are loops.
2596 for (int node = 0; node < num_nodes_; ++node) {
2597 if (committed_index_[node] != -1) continue; // node is start or end.
2598 committed_index_[node] = committed_nodes_.size();
2599 committed_nodes_.push_back({node, -1});
2600 }
2601 path_has_changed_.assign(num_paths_, false);
2602}
2603
2605 const PathBounds bounds = paths_[path];
2606 return PathState::ChainRange(chains_.data() + bounds.begin_index,
2607 chains_.data() + bounds.end_index,
2608 committed_nodes_.data());
2609}
2610
2612 const PathBounds bounds = paths_[path];
2613 return PathState::NodeRange(chains_.data() + bounds.begin_index,
2614 chains_.data() + bounds.end_index,
2615 committed_nodes_.data());
2616}
2617
2618void PathState::MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm() {
2619 int num_visited_changed_arcs = 0;
2620 const int num_changed_arcs = tail_head_indices_.size();
2621 const int num_committed_nodes = committed_nodes_.size();
2622 // For every path, find all its chains.
2623 for (const int path : changed_paths_) {
2624 const int old_chain_size = chains_.size();
2625 const ChainBounds bounds = chains_[paths_[path].begin_index];
2626 const int start_index = bounds.begin_index;
2627 const int end_index = bounds.end_index - 1;
2628 int current_index = start_index;
2629 while (true) {
2630 // Look for smallest non-visited tail_index that is no smaller than
2631 // current_index.
2632 int selected_arc = -1;
2633 int selected_tail_index = num_committed_nodes;
2634 for (int i = num_visited_changed_arcs; i < num_changed_arcs; ++i) {
2635 const int tail_index = tail_head_indices_[i].tail_index;
2636 if (current_index <= tail_index && tail_index < selected_tail_index) {
2637 selected_arc = i;
2638 selected_tail_index = tail_index;
2639 }
2640 }
2641 // If there is no such tail index, or more generally if the next chain
2642 // would be cut by end of path,
2643 // stack {current_index, end_index + 1} in chains_, and go to next path.
2644 // Otherwise, stack {current_index, tail_index+1} in chains_,
2645 // set current_index = head_index, set pair to visited.
2646 if (start_index <= current_index && current_index <= end_index &&
2647 end_index < selected_tail_index) {
2648 chains_.emplace_back(current_index, end_index + 1);
2649 break;
2650 } else {
2651 chains_.emplace_back(current_index, selected_tail_index + 1);
2652 current_index = tail_head_indices_[selected_arc].head_index;
2653 std::swap(tail_head_indices_[num_visited_changed_arcs],
2654 tail_head_indices_[selected_arc]);
2655 ++num_visited_changed_arcs;
2656 }
2657 }
2658 const int new_chain_size = chains_.size();
2659 paths_[path] = {old_chain_size, new_chain_size};
2660 }
2661 chains_.emplace_back(0, 0); // Sentinel.
2662}
2663
2664void PathState::MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm() {
2665 // TRICKY: For each changed path, we want to generate a sequence of chains
2666 // that represents the path in the changed state.
2667 // First, notice that if we add a fake end->start arc for each changed path,
2668 // then all chains will be from the head of an arc to the tail of an arc.
2669 // A way to generate the changed chains and paths would be, for each path,
2670 // to start from a fake arc's head (the path start), go down the path until
2671 // the tail of an arc, and go to the next arc until we return on the fake arc,
2672 // enqueuing the [head, tail] chains as we go.
2673 // In turn, to do that, we need to know which arc to go to.
2674 // If we sort all heads and tails by index in two separate arrays,
2675 // the head_index and tail_index at the same rank are such that
2676 // [head_index, tail_index] is a chain. Moreover, the arc that must be visited
2677 // after head_index's arc is tail_index's arc.
2678
2679 // Add a fake end->start arc for each path.
2680 for (const int path : changed_paths_) {
2681 const PathStartEnd start_end = path_start_end_[path];
2682 tail_head_indices_.push_back(
2683 {committed_index_[start_end.end], committed_index_[start_end.start]});
2684 }
2685
2686 // Generate pairs (tail_index, arc) and (head_index, arc) for all arcs,
2687 // sort those pairs by index.
2688 const int num_arc_indices = tail_head_indices_.size();
2689 arcs_by_tail_index_.resize(num_arc_indices);
2690 arcs_by_head_index_.resize(num_arc_indices);
2691 for (int i = 0; i < num_arc_indices; ++i) {
2692 arcs_by_tail_index_[i] = {tail_head_indices_[i].tail_index, i};
2693 arcs_by_head_index_[i] = {tail_head_indices_[i].head_index, i};
2694 }
2695 std::sort(arcs_by_tail_index_.begin(), arcs_by_tail_index_.end());
2696 std::sort(arcs_by_head_index_.begin(), arcs_by_head_index_.end());
2697 // Generate the map from arc to next arc in path.
2698 next_arc_.resize(num_arc_indices);
2699 for (int i = 0; i < num_arc_indices; ++i) {
2700 next_arc_[arcs_by_head_index_[i].arc] = arcs_by_tail_index_[i].arc;
2701 }
2702
2703 // Generate chains: for every changed path, start from its fake arc,
2704 // jump to next_arc_ until going back to fake arc,
2705 // enqueuing chains as we go.
2706 const int first_fake_arc = num_arc_indices - changed_paths_.size();
2707 for (int fake_arc = first_fake_arc; fake_arc < num_arc_indices; ++fake_arc) {
2708 const int new_path_begin = chains_.size();
2709 int32_t arc = fake_arc;
2710 do {
2711 const int chain_begin = tail_head_indices_[arc].head_index;
2712 arc = next_arc_[arc];
2713 const int chain_end = tail_head_indices_[arc].tail_index + 1;
2714 chains_.emplace_back(chain_begin, chain_end);
2715 } while (arc != fake_arc);
2716 const int path = changed_paths_[fake_arc - first_fake_arc];
2717 const int new_path_end = chains_.size();
2718 paths_[path] = {new_path_begin, new_path_end};
2719 }
2720 chains_.emplace_back(0, 0); // Sentinel.
2721}
2722
2724 if (is_invalid_) return;
2725 // Filter out unchanged arcs from changed_arcs_,
2726 // translate changed arcs to changed arc indices.
2727 // Fill changed_paths_ while we hold node_path.
2728 DCHECK_EQ(chains_.size(), num_paths_ + 1); // One per path + sentinel.
2729 DCHECK(changed_paths_.empty());
2730 tail_head_indices_.clear();
2731 int num_changed_arcs = 0;
2732 for (const auto& arc : changed_arcs_) {
2733 int node, next;
2734 std::tie(node, next) = arc;
2735 const int node_index = committed_index_[node];
2736 const int next_index = committed_index_[next];
2737 const int node_path = committed_nodes_[node_index].path;
2738 if (next != node &&
2739 (next_index != node_index + 1 || node_path == -1)) { // New arc.
2740 tail_head_indices_.push_back({node_index, next_index});
2741 changed_arcs_[num_changed_arcs++] = {node, next};
2742 if (node_path != -1 && !path_has_changed_[node_path]) {
2743 path_has_changed_[node_path] = true;
2744 changed_paths_.push_back(node_path);
2745 }
2746 } else if (node == next && node_path != -1) { // New loop.
2747 changed_arcs_[num_changed_arcs++] = {node, node};
2748 }
2749 }
2750 changed_arcs_.resize(num_changed_arcs);
2751
2752 if (tail_head_indices_.size() + changed_paths_.size() <= 8) {
2753 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
2754 } else {
2755 MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
2756 }
2757}
2758
2760 DCHECK(!IsInvalid());
2761 if (committed_nodes_.size() < num_nodes_threshold_) {
2762 IncrementalCommit();
2763 } else {
2764 FullCommit();
2765 }
2766}
2767
2769 is_invalid_ = false;
2770 chains_.resize(num_paths_ + 1); // One per path + sentinel.
2771 for (const int path : changed_paths_) {
2772 paths_[path] = {path, path + 1};
2773 path_has_changed_[path] = false;
2774 }
2775 changed_paths_.clear();
2776 changed_arcs_.clear();
2777}
2778
2779void PathState::CopyNewPathAtEndOfNodes(int path) {
2780 // Copy path's nodes, chain by chain.
2781 const int new_path_begin_index = committed_nodes_.size();
2782 const PathBounds path_bounds = paths_[path];
2783 for (int i = path_bounds.begin_index; i < path_bounds.end_index; ++i) {
2784 const ChainBounds chain_bounds = chains_[i];
2785 committed_nodes_.insert(committed_nodes_.end(),
2786 committed_nodes_.data() + chain_bounds.begin_index,
2787 committed_nodes_.data() + chain_bounds.end_index);
2788 }
2789 const int new_path_end_index = committed_nodes_.size();
2790 // Set new nodes' path member to path.
2791 for (int i = new_path_begin_index; i < new_path_end_index; ++i) {
2792 committed_nodes_[i].path = path;
2793 }
2794}
2795
2796// TODO(user): Instead of copying paths at the end systematically,
2797// reuse some of the memory when possible.
2798void PathState::IncrementalCommit() {
2799 const int new_nodes_begin = committed_nodes_.size();
2800 for (const int path : ChangedPaths()) {
2801 const int chain_begin = committed_nodes_.size();
2802 CopyNewPathAtEndOfNodes(path);
2803 const int chain_end = committed_nodes_.size();
2804 chains_[path] = {chain_begin, chain_end};
2805 }
2806 // Re-index all copied nodes.
2807 const int new_nodes_end = committed_nodes_.size();
2808 for (int i = new_nodes_begin; i < new_nodes_end; ++i) {
2809 committed_index_[committed_nodes_[i].node] = i;
2810 }
2811 // New loops stay in place: only change their path to -1,
2812 // committed_index_ does not change.
2813 for (const auto& arc : ChangedArcs()) {
2814 int node, next;
2815 std::tie(node, next) = arc;
2816 if (node != next) continue;
2817 const int index = committed_index_[node];
2818 committed_nodes_[index].path = -1;
2819 }
2820 // Committed part of the state is set up, erase incremental changes.
2821 Revert();
2822}
2823
2824void PathState::FullCommit() {
2825 // Copy all paths at the end of committed_nodes_,
2826 // then remove all old committed_nodes_.
2827 const int old_num_nodes = committed_nodes_.size();
2828 for (int path = 0; path < num_paths_; ++path) {
2829 const int new_path_begin = committed_nodes_.size() - old_num_nodes;
2830 CopyNewPathAtEndOfNodes(path);
2831 const int new_path_end = committed_nodes_.size() - old_num_nodes;
2832 chains_[path] = {new_path_begin, new_path_end};
2833 }
2834 committed_nodes_.erase(committed_nodes_.begin(),
2835 committed_nodes_.begin() + old_num_nodes);
2836
2837 // Reindex path nodes, then loop nodes.
2838 constexpr int kUnindexed = -1;
2839 committed_index_.assign(num_nodes_, kUnindexed);
2840 int index = 0;
2841 for (const CommittedNode committed_node : committed_nodes_) {
2842 committed_index_[committed_node.node] = index++;
2843 }
2844 for (int node = 0; node < num_nodes_; ++node) {
2845 if (committed_index_[node] != kUnindexed) continue;
2846 committed_index_[node] = index++;
2847 committed_nodes_.push_back({node, -1});
2848 }
2849 // Committed part of the state is set up, erase incremental changes.
2850 Revert();
2851}
2852
2853namespace {
2854
2855class PathStateFilter : public LocalSearchFilter {
2856 public:
2857 std::string DebugString() const override { return "PathStateFilter"; }
2858 PathStateFilter(std::unique_ptr<PathState> path_state,
2859 const std::vector<IntVar*>& nexts);
2860 void Relax(const Assignment* delta, const Assignment* deltadelta) override;
2861 bool Accept(const Assignment* delta, const Assignment* deltadelta,
2862 int64_t objective_min, int64_t objective_max) override {
2863 return true;
2864 }
2865 void Synchronize(const Assignment* delta,
2866 const Assignment* deltadelta) override{};
2867 void Commit(const Assignment* assignment, const Assignment* delta) override;
2868 void Revert() override;
2869 void Reset() override;
2870
2871 private:
2872 const std::unique_ptr<PathState> path_state_;
2873 // Map IntVar* index to node, offset by the min index in nexts.
2874 std::vector<int> variable_index_to_node_;
2875 int index_offset_;
2876 // Used only in Reset(), this is a member variable to avoid reallocation.
2877 std::vector<bool> node_is_assigned_;
2878};
2879
2880PathStateFilter::PathStateFilter(std::unique_ptr<PathState> path_state,
2881 const std::vector<IntVar*>& nexts)
2882 : path_state_(std::move(path_state)) {
2883 {
2884 int min_index = std::numeric_limits<int>::max();
2885 int max_index = std::numeric_limits<int>::min();
2886 for (const IntVar* next : nexts) {
2887 const int index = next->index();
2888 min_index = std::min<int>(min_index, index);
2889 max_index = std::max<int>(max_index, index);
2890 }
2891 variable_index_to_node_.resize(max_index - min_index + 1, -1);
2892 index_offset_ = min_index;
2893 }
2894
2895 for (int node = 0; node < nexts.size(); ++node) {
2896 const int index = nexts[node]->index() - index_offset_;
2897 variable_index_to_node_[index] = node;
2898 }
2899}
2900
2901void PathStateFilter::Relax(const Assignment* delta,
2902 const Assignment* deltadelta) {
2903 path_state_->Revert();
2904 for (const IntVarElement& var_value : delta->IntVarContainer().elements()) {
2905 if (var_value.Var() == nullptr) continue;
2906 const int index = var_value.Var()->index() - index_offset_;
2907 if (index < 0 || variable_index_to_node_.size() <= index) continue;
2908 const int node = variable_index_to_node_[index];
2909 if (node == -1) continue;
2910 if (var_value.Bound()) {
2911 path_state_->ChangeNext(node, var_value.Value());
2912 } else {
2913 path_state_->Revert();
2914 path_state_->SetInvalid();
2915 break;
2916 }
2917 }
2918 path_state_->CutChains();
2919}
2920
2921void PathStateFilter::Reset() {
2922 path_state_->Revert();
2923 // Set all paths of path state to empty start -> end paths,
2924 // and all nonstart/nonend nodes to node -> node loops.
2925 const int num_nodes = path_state_->NumNodes();
2926 node_is_assigned_.assign(num_nodes, false);
2927 const int num_paths = path_state_->NumPaths();
2928 for (int path = 0; path < num_paths; ++path) {
2929 const int start = path_state_->Start(path);
2930 const int end = path_state_->End(path);
2931 path_state_->ChangeNext(start, end);
2932 node_is_assigned_[start] = true;
2933 node_is_assigned_[end] = true;
2934 }
2935 for (int node = 0; node < num_nodes; ++node) {
2936 if (!node_is_assigned_[node]) path_state_->ChangeNext(node, node);
2937 }
2938 path_state_->CutChains();
2939 path_state_->Commit();
2940}
2941
2942// The solver does not guarantee that a given Commit() corresponds to
2943// the previous Relax() (or that there has been a call to Relax()),
2944// so we replay the full change call sequence.
2945void PathStateFilter::Commit(const Assignment* assignment,
2946 const Assignment* delta) {
2947 path_state_->Revert();
2948 if (delta == nullptr || delta->Empty()) {
2949 Relax(assignment, nullptr);
2950 } else {
2951 Relax(delta, nullptr);
2952 }
2953 path_state_->Commit();
2954}
2955
2956void PathStateFilter::Revert() { path_state_->Revert(); }
2957
2958} // namespace
2959
2961 std::unique_ptr<PathState> path_state,
2962 const std::vector<IntVar*>& nexts) {
2963 PathStateFilter* filter = new PathStateFilter(std::move(path_state), nexts);
2964 return solver->RevAlloc(filter);
2965}
2966
2968 const PathState* path_state, std::vector<Interval> path_capacity,
2969 std::vector<int> path_class, std::vector<std::vector<Interval>> demand,
2970 std::vector<Interval> node_capacity)
2971 : path_state_(path_state),
2972 path_capacity_(std::move(path_capacity)),
2973 path_class_(std::move(path_class)),
2974 demand_(std::move(demand)),
2975 node_capacity_(std::move(node_capacity)),
2976 index_(path_state_->NumNodes(), 0),
2977 maximum_partial_demand_layer_size_(
2978 std::max(16, 4 * path_state_->NumNodes())) // 16 and 4 are arbitrary.
2979{
2980 const int num_nodes = path_state_->NumNodes();
2981 const int num_paths = path_state_->NumPaths();
2982 DCHECK_EQ(num_paths, path_capacity_.size());
2983 DCHECK_EQ(num_paths, path_class_.size());
2984 const int maximum_rmq_exponent = MostSignificantBitPosition32(num_nodes);
2985 partial_demand_sums_rmq_.resize(maximum_rmq_exponent + 1);
2986 previous_nontrivial_index_.reserve(maximum_partial_demand_layer_size_);
2987 FullCommit();
2988}
2989
2991 if (path_state_->IsInvalid()) return true;
2992 for (const int path : path_state_->ChangedPaths()) {
2993 const Interval path_capacity = path_capacity_[path];
2994 if (path_capacity.min == std::numeric_limits<int64_t>::min() &&
2995 path_capacity.max == std::numeric_limits<int64_t>::max()) {
2996 continue;
2997 }
2998 const int path_class = path_class_[path];
2999 // Loop invariant: capacity_used is nonempty and within path_capacity.
3000 Interval capacity_used = node_capacity_[path_state_->Start(path)];
3001 capacity_used = {std::max(capacity_used.min, path_capacity.min),
3002 std::min(capacity_used.max, path_capacity.max)};
3003 if (capacity_used.min > capacity_used.max) return false;
3004
3005 for (const auto chain : path_state_->Chains(path)) {
3006 const int first_node = chain.First();
3007 const int last_node = chain.Last();
3008
3009 const int first_index = index_[first_node];
3010 const int last_index = index_[last_node];
3011
3012 const int chain_path = path_state_->Path(first_node);
3013 const int chain_path_class =
3014 chain_path == -1 ? -1 : path_class_[chain_path];
3015 // Call the RMQ if the chain size is large enough;
3016 // the optimal value was found with the associated benchmark in tests,
3017 // in particular BM_UnaryDimensionChecker<ChangeSparsity::kSparse, *>.
3018 constexpr int kMinRangeSizeForRMQ = 4;
3019 if (last_index - first_index > kMinRangeSizeForRMQ &&
3020 path_class == chain_path_class &&
3021 SubpathOnlyHasTrivialNodes(first_index, last_index)) {
3022 // Compute feasible values of capacity_used that will not violate
3023 // path_capacity. This is done by considering the worst cases
3024 // using a range min/max query.
3025 const Interval min_max =
3026 GetMinMaxPartialDemandSum(first_index, last_index);
3027 const Interval prev_sum = partial_demand_sums_rmq_[0][first_index - 1];
3028 const Interval min_max_delta = {CapSub(min_max.min, prev_sum.min),
3029 CapSub(min_max.max, prev_sum.max)};
3030 capacity_used = {
3031 std::max(capacity_used.min,
3032 CapSub(path_capacity.min, min_max_delta.min)),
3033 std::min(capacity_used.max,
3034 CapSub(path_capacity.max, min_max_delta.max))};
3035 if (capacity_used.min > capacity_used.max) return false;
3036 // Move to last node's state, which is valid since we did not return.
3037 const Interval last_sum = partial_demand_sums_rmq_[0][last_index];
3038 capacity_used = {
3039 CapSub(CapAdd(capacity_used.min, last_sum.min), prev_sum.min),
3040 CapSub(CapAdd(capacity_used.max, last_sum.max), prev_sum.max)};
3041 } else {
3042 for (const int node : chain) {
3043 const Interval node_capacity = node_capacity_[node];
3044 capacity_used = {std::max(capacity_used.min, node_capacity.min),
3045 std::min(capacity_used.max, node_capacity.max)};
3046 if (capacity_used.min > capacity_used.max) return false;
3047 const Interval demand = demand_[path_class][node];
3048 capacity_used = {CapAdd(capacity_used.min, demand.min),
3049 CapAdd(capacity_used.max, demand.max)};
3050 capacity_used = {std::max(capacity_used.min, path_capacity.min),
3051 std::min(capacity_used.max, path_capacity.max)};
3052 }
3053 }
3054 }
3055 if (std::max(capacity_used.min, path_capacity.min) >
3056 std::min(capacity_used.max, path_capacity.max)) {
3057 return false;
3058 }
3059 }
3060 return true;
3061}
3062
3064 const int current_layer_size = partial_demand_sums_rmq_[0].size();
3065 int change_size = path_state_->ChangedPaths().size();
3066 for (const int path : path_state_->ChangedPaths()) {
3067 for (const auto chain : path_state_->Chains(path)) {
3068 change_size += chain.NumNodes();
3069 }
3070 }
3071 if (current_layer_size + change_size <= maximum_partial_demand_layer_size_) {
3072 IncrementalCommit();
3073 } else {
3074 FullCommit();
3075 }
3076}
3077
3078void UnaryDimensionChecker::IncrementalCommit() {
3079 for (const int path : path_state_->ChangedPaths()) {
3080 const int begin_index = partial_demand_sums_rmq_[0].size();
3081 AppendPathDemandsToSums(path);
3082 UpdateRMQStructure(begin_index, partial_demand_sums_rmq_[0].size());
3083 }
3084}
3085
3086void UnaryDimensionChecker::FullCommit() {
3087 // Clear all structures.
3088 previous_nontrivial_index_.clear();
3089 for (auto& sums : partial_demand_sums_rmq_) sums.clear();
3090 // Append all paths.
3091 const int num_paths = path_state_->NumPaths();
3092 for (int path = 0; path < num_paths; ++path) {
3093 const int begin_index = partial_demand_sums_rmq_[0].size();
3094 AppendPathDemandsToSums(path);
3095 UpdateRMQStructure(begin_index, partial_demand_sums_rmq_[0].size());
3096 }
3097}
3098
3099void UnaryDimensionChecker::AppendPathDemandsToSums(int path) {
3100 const int path_class = path_class_[path];
3101 Interval demand_sum = {0, 0};
3102 int previous_nontrivial_index = -1;
3103 int index = partial_demand_sums_rmq_[0].size();
3104 // Value of partial_demand_sums_rmq_ at node_index-1 must be the sum
3105 // of all demands of nodes before node.
3106 partial_demand_sums_rmq_[0].push_back(demand_sum);
3107 previous_nontrivial_index_.push_back(-1);
3108 ++index;
3109
3110 for (const int node : path_state_->Nodes(path)) {
3111 index_[node] = index;
3112 const Interval demand = demand_[path_class][node];
3113 demand_sum = {CapAdd(demand_sum.min, demand.min),
3114 CapAdd(demand_sum.max, demand.max)};
3115 partial_demand_sums_rmq_[0].push_back(demand_sum);
3116
3117 const Interval node_capacity = node_capacity_[node];
3118 if (demand.min != demand.max ||
3119 node_capacity.min != std::numeric_limits<int64_t>::min() ||
3120 node_capacity.max != std::numeric_limits<int64_t>::max()) {
3121 previous_nontrivial_index = index;
3122 }
3123 previous_nontrivial_index_.push_back(previous_nontrivial_index);
3124 ++index;
3125 }
3126}
3127
3128void UnaryDimensionChecker::UpdateRMQStructure(int begin_index, int end_index) {
3129 // The max layer is the one used by
3130 // GetMinMaxPartialDemandSum(begin_index, end_index - 1).
3131 const int maximum_rmq_exponent =
3132 MostSignificantBitPosition32(end_index - 1 - begin_index);
3133 for (int layer = 1, window_size = 1; layer <= maximum_rmq_exponent;
3134 ++layer, window_size *= 2) {
3135 partial_demand_sums_rmq_[layer].resize(end_index);
3136 for (int i = begin_index; i < end_index - window_size; ++i) {
3137 const Interval i1 = partial_demand_sums_rmq_[layer - 1][i];
3138 const Interval i2 = partial_demand_sums_rmq_[layer - 1][i + window_size];
3139 partial_demand_sums_rmq_[layer][i] = {std::min(i1.min, i2.min),
3140 std::max(i1.max, i2.max)};
3141 }
3142 std::copy(
3143 partial_demand_sums_rmq_[layer - 1].begin() + end_index - window_size,
3144 partial_demand_sums_rmq_[layer - 1].begin() + end_index,
3145 partial_demand_sums_rmq_[layer].begin() + end_index - window_size);
3146 }
3147}
3148
3149// TODO(user): since this is called only when
3150// last_node_index - first_node_index is large enough,
3151// the lower layers of partial_demand_sums_rmq_ are never used.
3152// For instance, if this is only called when the range size is > 4
3153// and paths are <= 32 nodes long, then we only need layers 0, 2, 3, and 4.
3154// To compare, on a 512 < #nodes <= 1024 problem, this uses layers in [0, 10].
3155UnaryDimensionChecker::Interval
3156UnaryDimensionChecker::GetMinMaxPartialDemandSum(int first_node_index,
3157 int last_node_index) const {
3158 DCHECK_LE(0, first_node_index);
3159 DCHECK_LT(first_node_index, last_node_index);
3160 DCHECK_LT(last_node_index, partial_demand_sums_rmq_[0].size());
3161 // Find largest window_size = 2^layer such that
3162 // first_node_index < last_node_index - window_size + 1.
3163 const int layer =
3164 MostSignificantBitPosition32(last_node_index - first_node_index);
3165 const int window_size = 1 << layer;
3166 // Classical range min/max query in O(1).
3167 const Interval i1 = partial_demand_sums_rmq_[layer][first_node_index];
3168 const Interval i2 =
3169 partial_demand_sums_rmq_[layer][last_node_index - window_size + 1];
3170 return {std::min(i1.min, i2.min), std::max(i1.max, i2.max)};
3171}
3172
3173bool UnaryDimensionChecker::SubpathOnlyHasTrivialNodes(
3174 int first_node_index, int last_node_index) const {
3175 DCHECK_LE(0, first_node_index);
3176 DCHECK_LT(first_node_index, last_node_index);
3177 DCHECK_LT(last_node_index, previous_nontrivial_index_.size());
3178 return first_node_index > previous_nontrivial_index_[last_node_index];
3179}
3180
3181namespace {
3182
3183class UnaryDimensionFilter : public LocalSearchFilter {
3184 public:
3185 std::string DebugString() const override { return name_; }
3186 UnaryDimensionFilter(std::unique_ptr<UnaryDimensionChecker> checker,
3187 const std::string& dimension_name)
3188 : checker_(std::move(checker)),
3189 name_(absl::StrCat("UnaryDimensionFilter(", dimension_name, ")")) {}
3190
3191 bool Accept(const Assignment* delta, const Assignment* deltadelta,
3192 int64_t objective_min, int64_t objective_max) override {
3193 return checker_->Check();
3194 }
3195
3196 void Synchronize(const Assignment* assignment,
3197 const Assignment* delta) override {
3198 checker_->Commit();
3199 }
3200
3201 private:
3202 std::unique_ptr<UnaryDimensionChecker> checker_;
3203 const std::string name_;
3204};
3205
3206} // namespace
3207
3209 Solver* solver, std::unique_ptr<UnaryDimensionChecker> checker,
3210 const std::string& dimension_name) {
3211 UnaryDimensionFilter* filter =
3212 new UnaryDimensionFilter(std::move(checker), dimension_name);
3213 return solver->RevAlloc(filter);
3214}
3215
3216namespace {
3217// ----- Variable domain filter -----
3218// Rejects assignments to values outside the domain of variables
3219
3220class VariableDomainFilter : public LocalSearchFilter {
3221 public:
3222 VariableDomainFilter() {}
3223 ~VariableDomainFilter() override {}
3224 bool Accept(const Assignment* delta, const Assignment* deltadelta,
3225 int64_t objective_min, int64_t objective_max) override;
3226 void Synchronize(const Assignment* assignment,
3227 const Assignment* delta) override {}
3228
3229 std::string DebugString() const override { return "VariableDomainFilter"; }
3230};
3231
3232bool VariableDomainFilter::Accept(const Assignment* delta,
3233 const Assignment* deltadelta,
3234 int64_t objective_min,
3235 int64_t objective_max) {
3236 const Assignment::IntContainer& container = delta->IntVarContainer();
3237 const int size = container.Size();
3238 for (int i = 0; i < size; ++i) {
3239 const IntVarElement& element = container.Element(i);
3240 if (element.Activated() && !element.Var()->Contains(element.Value())) {
3241 return false;
3242 }
3243 }
3244 return true;
3245}
3246} // namespace
3247
3249 return RevAlloc(new VariableDomainFilter());
3250}
3251
3252// ----- IntVarLocalSearchFilter -----
3253
3254const int IntVarLocalSearchFilter::kUnassigned = -1;
3255
3257 const std::vector<IntVar*>& vars) {
3258 AddVars(vars);
3259}
3260
3261void IntVarLocalSearchFilter::AddVars(const std::vector<IntVar*>& vars) {
3262 if (!vars.empty()) {
3263 for (int i = 0; i < vars.size(); ++i) {
3264 const int index = vars[i]->index();
3265 if (index >= var_index_to_index_.size()) {
3266 var_index_to_index_.resize(index + 1, kUnassigned);
3267 }
3268 var_index_to_index_[index] = i + vars_.size();
3269 }
3270 vars_.insert(vars_.end(), vars.begin(), vars.end());
3271 values_.resize(vars_.size(), /*junk*/ 0);
3272 var_synced_.resize(vars_.size(), false);
3273 }
3274}
3275
3277
3279 const Assignment* delta) {
3280 if (delta == nullptr || delta->Empty()) {
3281 var_synced_.assign(var_synced_.size(), false);
3282 SynchronizeOnAssignment(assignment);
3283 } else {
3285 }
3287}
3288
3290 const Assignment* assignment) {
3291 const Assignment::IntContainer& container = assignment->IntVarContainer();
3292 const int size = container.Size();
3293 for (int i = 0; i < size; ++i) {
3294 const IntVarElement& element = container.Element(i);
3295 IntVar* const var = element.Var();
3296 if (var != nullptr) {
3297 if (i < vars_.size() && vars_[i] == var) {
3298 values_[i] = element.Value();
3299 var_synced_[i] = true;
3300 } else {
3301 const int64_t kUnallocated = -1;
3302 int64_t index = kUnallocated;
3303 if (FindIndex(var, &index)) {
3304 values_[index] = element.Value();
3305 var_synced_[index] = true;
3306 }
3307 }
3308 }
3309 }
3310}
3311
3312// ----- Sum Objective filter ------
3313// Maintains the sum of costs of variables, where the subclass implements
3314// CostOfSynchronizedVariable() and FillCostOfBoundDeltaVariable() to compute
3315// the cost of a variable depending on its value.
3316// An assignment is accepted by this filter if the total cost is allowed
3317// depending on the relation defined by filter_enum:
3318// - Solver::LE -> total_cost <= objective_max.
3319// - Solver::GE -> total_cost >= objective_min.
3320// - Solver::EQ -> the conjunction of LE and GE.
3321namespace {
3322class SumObjectiveFilter : public IntVarLocalSearchFilter {
3323 public:
3324 SumObjectiveFilter(const std::vector<IntVar*>& vars,
3327 primary_vars_size_(vars.size()),
3328 synchronized_costs_(new int64_t[vars.size()]),
3329 delta_costs_(new int64_t[vars.size()]),
3330 filter_enum_(filter_enum),
3331 synchronized_sum_(std::numeric_limits<int64_t>::min()),
3332 delta_sum_(std::numeric_limits<int64_t>::min()),
3333 incremental_(false) {
3334 for (int i = 0; i < vars.size(); ++i) {
3335 synchronized_costs_[i] = 0;
3336 delta_costs_[i] = 0;
3337 }
3338 }
3339 ~SumObjectiveFilter() override {
3340 delete[] synchronized_costs_;
3341 delete[] delta_costs_;
3342 }
3343 bool Accept(const Assignment* delta, const Assignment* deltadelta,
3344 int64_t objective_min, int64_t objective_max) override {
3345 if (delta == nullptr) {
3346 return false;
3347 }
3348 if (deltadelta->Empty()) {
3349 if (incremental_) {
3350 for (int i = 0; i < primary_vars_size_; ++i) {
3352 }
3354 }
3355 incremental_ = false;
3357 CostOfChanges(delta, synchronized_costs_, false));
3358 } else {
3359 if (incremental_) {
3360 delta_sum_ =
3361 CapAdd(delta_sum_, CostOfChanges(deltadelta, delta_costs_, true));
3362 } else {
3364 CostOfChanges(delta, synchronized_costs_, true));
3365 }
3366 incremental_ = true;
3367 }
3368 switch (filter_enum_) {
3369 case Solver::LE: {
3370 return delta_sum_ <= objective_max;
3371 }
3372 case Solver::GE: {
3373 return delta_sum_ >= objective_min;
3374 }
3375 case Solver::EQ: {
3376 return objective_min <= delta_sum_ && delta_sum_ <= objective_max;
3377 }
3378 default: {
3379 LOG(ERROR) << "Unknown local search filter enum value";
3380 return false;
3381 }
3382 }
3383 }
3384 // If the variable is synchronized, returns its associated cost, otherwise
3385 // returns 0.
3386 virtual int64_t CostOfSynchronizedVariable(int64_t index) = 0;
3387 // If the variable is bound, fills new_cost with the cost associated to the
3388 // variable's valuation in container, and returns true. Otherwise, fills
3389 // new_cost with 0, and returns false.
3390 virtual bool FillCostOfBoundDeltaVariable(
3391 const Assignment::IntContainer& container, int index,
3392 int* container_index, int64_t* new_cost) = 0;
3393 bool IsIncremental() const override { return true; }
3394
3395 std::string DebugString() const override { return "SumObjectiveFilter"; }
3396
3397 int64_t GetSynchronizedObjectiveValue() const override {
3398 return synchronized_sum_;
3399 }
3400 int64_t GetAcceptedObjectiveValue() const override { return delta_sum_; }
3401
3402 protected:
3404 int64_t* const synchronized_costs_;
3405 int64_t* const delta_costs_;
3408 int64_t delta_sum_;
3410
3411 private:
3412 void OnSynchronize(const Assignment* delta) override {
3414 for (int i = 0; i < primary_vars_size_; ++i) {
3415 const int64_t cost = CostOfSynchronizedVariable(i);
3417 delta_costs_[i] = cost;
3419 }
3421 incremental_ = false;
3422 }
3423 int64_t CostOfChanges(const Assignment* changes,
3424 const int64_t* const old_costs,
3425 bool cache_delta_values) {
3426 int64_t total_cost = 0;
3427 const Assignment::IntContainer& container = changes->IntVarContainer();
3428 const int size = container.Size();
3429 for (int i = 0; i < size; ++i) {
3430 const IntVarElement& new_element = container.Element(i);
3431 IntVar* const var = new_element.Var();
3432 int64_t index = -1;
3433 if (FindIndex(var, &index) && index < primary_vars_size_) {
3434 total_cost = CapSub(total_cost, old_costs[index]);
3435 int64_t new_cost = 0LL;
3436 if (FillCostOfBoundDeltaVariable(container, index, &i, &new_cost)) {
3437 total_cost = CapAdd(total_cost, new_cost);
3438 }
3439 if (cache_delta_values) {
3440 delta_costs_[index] = new_cost;
3441 }
3442 }
3443 }
3444 return total_cost;
3445 }
3446};
3447
3448class BinaryObjectiveFilter : public SumObjectiveFilter {
3449 public:
3450 BinaryObjectiveFilter(const std::vector<IntVar*>& vars,
3451 Solver::IndexEvaluator2 value_evaluator,
3453 : SumObjectiveFilter(vars, filter_enum),
3454 value_evaluator_(std::move(value_evaluator)) {}
3455 ~BinaryObjectiveFilter() override {}
3456 int64_t CostOfSynchronizedVariable(int64_t index) override {
3458 ? value_evaluator_(index, IntVarLocalSearchFilter::Value(index))
3459 : 0;
3460 }
3461 bool FillCostOfBoundDeltaVariable(const Assignment::IntContainer& container,
3462 int index, int* container_index,
3463 int64_t* new_cost) override {
3464 const IntVarElement& element = container.Element(*container_index);
3465 if (element.Activated()) {
3466 *new_cost = value_evaluator_(index, element.Value());
3467 return true;
3468 }
3469 const IntVar* var = element.Var();
3470 if (var->Bound()) {
3471 *new_cost = value_evaluator_(index, var->Min());
3472 return true;
3473 }
3474 *new_cost = 0;
3475 return false;
3476 }
3477
3478 private:
3479 Solver::IndexEvaluator2 value_evaluator_;
3480};
3481
3482class TernaryObjectiveFilter : public SumObjectiveFilter {
3483 public:
3484 TernaryObjectiveFilter(const std::vector<IntVar*>& vars,
3485 const std::vector<IntVar*>& secondary_vars,
3486 Solver::IndexEvaluator3 value_evaluator,
3488 : SumObjectiveFilter(vars, filter_enum),
3489 secondary_vars_offset_(vars.size()),
3490 value_evaluator_(std::move(value_evaluator)) {
3491 IntVarLocalSearchFilter::AddVars(secondary_vars);
3493 }
3494 ~TernaryObjectiveFilter() override {}
3495 int64_t CostOfSynchronizedVariable(int64_t index) override {
3496 DCHECK_LT(index, secondary_vars_offset_);
3498 ? value_evaluator_(index, IntVarLocalSearchFilter::Value(index),
3500 index + secondary_vars_offset_))
3501 : 0;
3502 }
3503 bool FillCostOfBoundDeltaVariable(const Assignment::IntContainer& container,
3504 int index, int* container_index,
3505 int64_t* new_cost) override {
3506 DCHECK_LT(index, secondary_vars_offset_);
3507 *new_cost = 0LL;
3508 const IntVarElement& element = container.Element(*container_index);
3509 const IntVar* secondary_var =
3510 IntVarLocalSearchFilter::Var(index + secondary_vars_offset_);
3511 if (element.Activated()) {
3512 const int64_t value = element.Value();
3513 int hint_index = *container_index + 1;
3514 if (hint_index < container.Size() &&
3515 secondary_var == container.Element(hint_index).Var()) {
3516 *new_cost = value_evaluator_(index, value,
3517 container.Element(hint_index).Value());
3518 *container_index = hint_index;
3519 } else {
3520 *new_cost = value_evaluator_(index, value,
3521 container.Element(secondary_var).Value());
3522 }
3523 return true;
3524 }
3525 const IntVar* var = element.Var();
3526 if (var->Bound() && secondary_var->Bound()) {
3527 *new_cost = value_evaluator_(index, var->Min(), secondary_var->Min());
3528 return true;
3529 }
3530 *new_cost = 0;
3531 return false;
3532 }
3533
3534 private:
3535 int secondary_vars_offset_;
3536 Solver::IndexEvaluator3 value_evaluator_;
3537};
3538} // namespace
3539
3541 const std::vector<IntVar*>& vars, Solver::IndexEvaluator2 values,
3542 Solver::LocalSearchFilterBound filter_enum) {
3543 return RevAlloc(
3544 new BinaryObjectiveFilter(vars, std::move(values), filter_enum));
3545}
3546
3548 const std::vector<IntVar*>& vars,
3549 const std::vector<IntVar*>& secondary_vars, Solver::IndexEvaluator3 values,
3550 Solver::LocalSearchFilterBound filter_enum) {
3551 return RevAlloc(new TernaryObjectiveFilter(vars, secondary_vars,
3552 std::move(values), filter_enum));
3553}
3554
3556 int64_t initial_max) {
3557 DCHECK(state_is_valid_);
3558 DCHECK_LE(initial_min, initial_max);
3559 initial_variable_bounds_.push_back({initial_min, initial_max});
3560 variable_bounds_.push_back({initial_min, initial_max});
3561 variable_is_relaxed_.push_back(false);
3562
3563 const int variable_index = variable_bounds_.size() - 1;
3564 return {this, variable_index};
3565}
3566
3567void LocalSearchState::RelaxVariableBounds(int variable_index) {
3568 DCHECK(state_is_valid_);
3569 DCHECK(0 <= variable_index && variable_index < variable_is_relaxed_.size());
3570 if (!variable_is_relaxed_[variable_index]) {
3571 variable_is_relaxed_[variable_index] = true;
3572 saved_variable_bounds_trail_.emplace_back(variable_bounds_[variable_index],
3573 variable_index);
3574 variable_bounds_[variable_index] = initial_variable_bounds_[variable_index];
3575 }
3576}
3577
3578int64_t LocalSearchState::VariableMin(int variable_index) const {
3579 DCHECK(state_is_valid_);
3580 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3581 return variable_bounds_[variable_index].min;
3582}
3583
3584int64_t LocalSearchState::VariableMax(int variable_index) const {
3585 DCHECK(state_is_valid_);
3586 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3587 return variable_bounds_[variable_index].max;
3588}
3589
3590bool LocalSearchState::TightenVariableMin(int variable_index,
3591 int64_t min_value) {
3592 DCHECK(state_is_valid_);
3593 DCHECK(variable_is_relaxed_[variable_index]);
3594 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3595 Bounds& bounds = variable_bounds_[variable_index];
3596 if (bounds.max < min_value) {
3597 state_is_valid_ = false;
3598 }
3599 bounds.min = std::max(bounds.min, min_value);
3600 return state_is_valid_;
3601}
3602
3603bool LocalSearchState::TightenVariableMax(int variable_index,
3604 int64_t max_value) {
3605 DCHECK(state_is_valid_);
3606 DCHECK(variable_is_relaxed_[variable_index]);
3607 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3608 Bounds& bounds = variable_bounds_[variable_index];
3609 if (bounds.min > max_value) {
3610 state_is_valid_ = false;
3611 }
3612 bounds.max = std::min(bounds.max, max_value);
3613 return state_is_valid_;
3614}
3615
3616// TODO(user): When the class has more users, find a threshold ratio of
3617// saved/total variables under which a sparse clear would be more efficient
3618// for both Commit() and Revert().
3620 DCHECK(state_is_valid_);
3621 saved_variable_bounds_trail_.clear();
3622 variable_is_relaxed_.assign(variable_is_relaxed_.size(), false);
3623}
3624
3626 for (const auto& bounds_index : saved_variable_bounds_trail_) {
3627 DCHECK(variable_is_relaxed_[bounds_index.second]);
3628 variable_bounds_[bounds_index.second] = bounds_index.first;
3629 }
3630 saved_variable_bounds_trail_.clear();
3631 variable_is_relaxed_.assign(variable_is_relaxed_.size(), false);
3632 state_is_valid_ = true;
3633}
3634
3635// ----- LocalSearchProfiler -----
3636
3638 public:
3640 std::string DebugString() const override { return "LocalSearchProfiler"; }
3641 void RestartSearch() override {
3642 operator_stats_.clear();
3643 filter_stats_.clear();
3644 }
3645 void ExitSearch() override {
3646 // Update times for current operator when the search ends.
3647 if (solver()->TopLevelSearch() == solver()->ActiveSearch()) {
3648 UpdateTime();
3649 }
3650 }
3652 LocalSearchStatistics statistics_proto;
3653 for (ProfiledDecisionBuilder* db : profiled_decision_builders_) {
3654 if (db->seconds() == 0) continue;
3656 first_solution_statistics =
3657 statistics_proto.add_first_solution_statistics();
3658 first_solution_statistics->set_strategy(db->name());
3659 first_solution_statistics->set_duration_seconds(db->seconds());
3660 }
3661 std::vector<const LocalSearchOperator*> operators;
3662 for (const auto& stat : operator_stats_) {
3663 operators.push_back(stat.first);
3664 }
3665 std::sort(
3666 operators.begin(), operators.end(),
3667 [this](const LocalSearchOperator* op1, const LocalSearchOperator* op2) {
3668 return gtl::FindOrDie(operator_stats_, op1).neighbors >
3669 gtl::FindOrDie(operator_stats_, op2).neighbors;
3670 });
3671 for (const LocalSearchOperator* const op : operators) {
3672 const OperatorStats& stats = gtl::FindOrDie(operator_stats_, op);
3674 local_search_operator_statistics =
3675 statistics_proto.add_local_search_operator_statistics();
3676 local_search_operator_statistics->set_local_search_operator(
3677 op->DebugString());
3678 local_search_operator_statistics->set_num_neighbors(stats.neighbors);
3679 local_search_operator_statistics->set_num_filtered_neighbors(
3680 stats.filtered_neighbors);
3681 local_search_operator_statistics->set_num_accepted_neighbors(
3682 stats.accepted_neighbors);
3683 local_search_operator_statistics->set_duration_seconds(stats.seconds);
3684 }
3685 std::vector<const LocalSearchFilter*> filters;
3686 for (const auto& stat : filter_stats_) {
3687 filters.push_back(stat.first);
3688 }
3689 std::sort(filters.begin(), filters.end(),
3690 [this](const LocalSearchFilter* filter1,
3691 const LocalSearchFilter* filter2) {
3692 return gtl::FindOrDie(filter_stats_, filter1).calls >
3693 gtl::FindOrDie(filter_stats_, filter2).calls;
3694 });
3695 for (const LocalSearchFilter* const filter : filters) {
3696 const FilterStats& stats = gtl::FindOrDie(filter_stats_, filter);
3698 local_search_filter_statistics =
3699 statistics_proto.add_local_search_filter_statistics();
3700 local_search_filter_statistics->set_local_search_filter(
3701 filter->DebugString());
3702 local_search_filter_statistics->set_num_calls(stats.calls);
3703 local_search_filter_statistics->set_num_rejects(stats.rejects);
3704 local_search_filter_statistics->set_duration_seconds(stats.seconds);
3705 }
3706 statistics_proto.set_total_num_neighbors(solver()->neighbors());
3707 statistics_proto.set_total_num_filtered_neighbors(
3708 solver()->filtered_neighbors());
3709 statistics_proto.set_total_num_accepted_neighbors(
3710 solver()->accepted_neighbors());
3711 return statistics_proto;
3712 }
3713 std::string PrintOverview() const {
3714 size_t max_name_size = 0;
3715 std::vector<const LocalSearchOperator*> operators;
3716 for (const auto& stat : operator_stats_) {
3717 operators.push_back(stat.first);
3718 max_name_size =
3719 std::max(max_name_size, stat.first->DebugString().length());
3720 }
3721 std::sort(
3722 operators.begin(), operators.end(),
3723 [this](const LocalSearchOperator* op1, const LocalSearchOperator* op2) {
3724 return gtl::FindOrDie(operator_stats_, op1).neighbors >
3725 gtl::FindOrDie(operator_stats_, op2).neighbors;
3726 });
3727 std::string overview = "Local search operator statistics:\n";
3728 absl::StrAppendFormat(&overview,
3729 "%*s | Neighbors | Filtered | Accepted | Time (s)\n",
3730 max_name_size, "");
3731 OperatorStats total_stats;
3732 for (const LocalSearchOperator* const op : operators) {
3733 const OperatorStats& stats = gtl::FindOrDie(operator_stats_, op);
3734 const std::string& name = op->DebugString();
3735 absl::StrAppendFormat(&overview, "%*s | %9ld | %8ld | %8ld | %7.2g\n",
3736 max_name_size, name, stats.neighbors,
3737 stats.filtered_neighbors, stats.accepted_neighbors,
3738 stats.seconds);
3739 total_stats.neighbors += stats.neighbors;
3740 total_stats.filtered_neighbors += stats.filtered_neighbors;
3741 total_stats.accepted_neighbors += stats.accepted_neighbors;
3742 total_stats.seconds += stats.seconds;
3743 }
3744 absl::StrAppendFormat(&overview, "%*s | %9ld | %8ld | %8ld | %7.2g\n",
3745 max_name_size, "Total", total_stats.neighbors,
3746 total_stats.filtered_neighbors,
3747 total_stats.accepted_neighbors, total_stats.seconds);
3748 max_name_size = 0;
3749 std::vector<const LocalSearchFilter*> filters;
3750 for (const auto& stat : filter_stats_) {
3751 filters.push_back(stat.first);
3752 max_name_size =
3753 std::max(max_name_size, stat.first->DebugString().length());
3754 }
3755 std::sort(filters.begin(), filters.end(),
3756 [this](const LocalSearchFilter* filter1,
3757 const LocalSearchFilter* filter2) {
3758 return gtl::FindOrDie(filter_stats_, filter1).calls >
3759 gtl::FindOrDie(filter_stats_, filter2).calls;
3760 });
3761 absl::StrAppendFormat(&overview,
3762 "Local search filter statistics:\n%*s | Calls | "
3763 " Rejects | Time (s) "
3764 "| Rejects/s\n",
3765 max_name_size, "");
3766 FilterStats total_filter_stats;
3767 for (const LocalSearchFilter* const filter : filters) {
3768 const FilterStats& stats = gtl::FindOrDie(filter_stats_, filter);
3769 const std::string& name = filter->DebugString();
3770 absl::StrAppendFormat(&overview, "%*s | %9ld | %9ld | %7.2g | %7.2g\n",
3771 max_name_size, name, stats.calls, stats.rejects,
3772 stats.seconds, stats.rejects / stats.seconds);
3773 total_filter_stats.calls += stats.calls;
3774 total_filter_stats.rejects += stats.rejects;
3775 total_filter_stats.seconds += stats.seconds;
3776 }
3777 absl::StrAppendFormat(
3778 &overview, "%*s | %9ld | %9ld | %7.2g | %7.2g\n", max_name_size,
3779 "Total", total_filter_stats.calls, total_filter_stats.rejects,
3780 total_filter_stats.seconds,
3781 total_filter_stats.rejects / total_filter_stats.seconds);
3782 return overview;
3783 }
3784 void BeginOperatorStart() override {}
3785 void EndOperatorStart() override {}
3787 if (last_operator_ != op->Self()) {
3788 UpdateTime();
3789 last_operator_ = op->Self();
3790 }
3791 }
3792 void EndMakeNextNeighbor(const LocalSearchOperator* op, bool neighbor_found,
3793 const Assignment* delta,
3794 const Assignment* deltadelta) override {
3795 if (neighbor_found) {
3796 operator_stats_[op->Self()].neighbors++;
3797 }
3798 }
3799 void BeginFilterNeighbor(const LocalSearchOperator* op) override {}
3801 bool neighbor_found) override {
3802 if (neighbor_found) {
3803 operator_stats_[op->Self()].filtered_neighbors++;
3804 }
3805 }
3806 void BeginAcceptNeighbor(const LocalSearchOperator* op) override {}
3808 bool neighbor_found) override {
3809 if (neighbor_found) {
3810 operator_stats_[op->Self()].accepted_neighbors++;
3811 }
3812 }
3813 void BeginFiltering(const LocalSearchFilter* filter) override {
3814 filter_stats_[filter].calls++;
3815 filter_timer_.Start();
3816 }
3817 void EndFiltering(const LocalSearchFilter* filter, bool reject) override {
3818 filter_timer_.Stop();
3819 auto& stats = filter_stats_[filter];
3820 stats.seconds += filter_timer_.Get();
3821 if (reject) {
3822 stats.rejects++;
3823 }
3824 }
3826 ProfiledDecisionBuilder* profiled_db) {
3827 profiled_decision_builders_.push_back(profiled_db);
3828 }
3829 void Install() override { SearchMonitor::Install(); }
3830
3831 private:
3832 void UpdateTime() {
3833 if (last_operator_ != nullptr) {
3834 timer_.Stop();
3835 operator_stats_[last_operator_].seconds += timer_.Get();
3836 }
3837 timer_.Start();
3838 }
3839
3840 struct OperatorStats {
3841 int64_t neighbors = 0;
3842 int64_t filtered_neighbors = 0;
3843 int64_t accepted_neighbors = 0;
3844 double seconds = 0;
3845 };
3846
3847 struct FilterStats {
3848 int64_t calls = 0;
3849 int64_t rejects = 0;
3850 double seconds = 0;
3851 };
3852 WallTimer timer_;
3853 WallTimer filter_timer_;
3854 const LocalSearchOperator* last_operator_ = nullptr;
3855 absl::flat_hash_map<const LocalSearchOperator*, OperatorStats>
3856 operator_stats_;
3857 absl::flat_hash_map<const LocalSearchFilter*, FilterStats> filter_stats_;
3858 // Profiled decision builders.
3859 std::vector<ProfiledDecisionBuilder*> profiled_decision_builders_;
3860};
3861
3863 DecisionBuilder* db) {
3865 ProfiledDecisionBuilder* profiled_db =
3867 local_search_profiler_->AddFirstSolutionProfiledDecisionBuilder(
3868 profiled_db);
3869 return profiled_db;
3870 }
3871 return db;
3872}
3873
3875 monitor->Install();
3876}
3877
3879 if (solver->IsLocalSearchProfilingEnabled()) {
3880 return new LocalSearchProfiler(solver);
3881 }
3882 return nullptr;
3883}
3884
3885void DeleteLocalSearchProfiler(LocalSearchProfiler* monitor) { delete monitor; }
3886
3887std::string Solver::LocalSearchProfile() const {
3888 if (local_search_profiler_ != nullptr) {
3889 return local_search_profiler_->PrintOverview();
3890 }
3891 return "";
3892}
3893
3895 if (local_search_profiler_ != nullptr) {
3896 return local_search_profiler_->ExportToLocalSearchStatistics();
3897 }
3898 return LocalSearchStatistics();
3899}
3900
3901void LocalSearchFilterManager::InitializeForcedEvents() {
3902 const int num_events = filter_events_.size();
3903 int next_forced_event = num_events;
3904 next_forced_events_.resize(num_events);
3905 for (int i = num_events - 1; i >= 0; --i) {
3906 next_forced_events_[i] = next_forced_event;
3907 if (filter_events_[i].filter->IsIncremental() ||
3908 (filter_events_[i].event_type == FilterEventType::kRelax &&
3909 next_forced_event != num_events)) {
3910 next_forced_event = i;
3911 }
3912 }
3913}
3914
3916 std::vector<LocalSearchFilter*> filters)
3917 : synchronized_value_(std::numeric_limits<int64_t>::min()),
3918 accepted_value_(std::numeric_limits<int64_t>::min()) {
3919 filter_events_.reserve(2 * filters.size());
3920 for (LocalSearchFilter* filter : filters) {
3921 filter_events_.push_back({filter, FilterEventType::kRelax});
3922 }
3923 for (LocalSearchFilter* filter : filters) {
3924 filter_events_.push_back({filter, FilterEventType::kAccept});
3925 }
3926 InitializeForcedEvents();
3927}
3928
3930 std::vector<FilterEvent> filter_events)
3931 : filter_events_(std::move(filter_events)),
3932 synchronized_value_(std::numeric_limits<int64_t>::min()),
3933 accepted_value_(std::numeric_limits<int64_t>::min()) {
3934 InitializeForcedEvents();
3935}
3936
3937// Filters' Revert() must be called in the reverse order in which their
3938// Relax() was called.
3940 for (int i = last_event_called_; i >= 0; --i) {
3941 auto [filter, event_type] = filter_events_[i];
3942 if (event_type == FilterEventType::kRelax) filter->Revert();
3943 }
3944 last_event_called_ = -1;
3945}
3946
3947// TODO(user): the behaviour of Accept relies on the initial order of
3948// filters having at most one filter with negative objective values,
3949// this could be fixed by having filters return their general bounds.
3951 const Assignment* delta,
3952 const Assignment* deltadelta,
3953 int64_t objective_min,
3954 int64_t objective_max) {
3955 Revert();
3956 accepted_value_ = 0;
3957 bool ok = true;
3958 const int num_events = filter_events_.size();
3959 for (int i = 0; i < num_events;) {
3960 last_event_called_ = i;
3961 auto [filter, event_type] = filter_events_[last_event_called_];
3962 switch (event_type) {
3963 case FilterEventType::kAccept: {
3964 if (monitor != nullptr) monitor->BeginFiltering(filter);
3965 const bool accept = filter->Accept(
3966 delta, deltadelta, CapSub(objective_min, accepted_value_),
3967 CapSub(objective_max, accepted_value_));
3968 ok &= accept;
3969 if (monitor != nullptr) monitor->EndFiltering(filter, !accept);
3970 if (ok) {
3971 accepted_value_ =
3972 CapAdd(accepted_value_, filter->GetAcceptedObjectiveValue());
3973 // TODO(user): handle objective min.
3974 ok = accepted_value_ <= objective_max;
3975 }
3976 break;
3977 }
3978 case FilterEventType::kRelax: {
3979 filter->Relax(delta, deltadelta);
3980 break;
3981 }
3982 default:
3983 LOG(FATAL) << "Unknown filter event type.";
3984 }
3985 // If the candidate is rejected, forced events must still be called.
3986 if (ok) {
3987 ++i;
3988 } else {
3989 i = next_forced_events_[i];
3990 }
3991 }
3992 return ok;
3993}
3994
3996 const Assignment* delta) {
3997 // If delta is nullptr or empty, then assignment may be a partial solution.
3998 // Send a signal to Relaxing filters to inform them,
3999 // so they can show the partial solution as a change from the empty solution.
4000 const bool reset_to_assignment = delta == nullptr || delta->Empty();
4001 // Relax in the forward direction.
4002 for (auto [filter, event_type] : filter_events_) {
4003 switch (event_type) {
4004 case FilterEventType::kAccept: {
4005 break;
4006 }
4007 case FilterEventType::kRelax: {
4008 if (reset_to_assignment) {
4009 filter->Reset();
4010 filter->Relax(assignment, nullptr);
4011 } else {
4012 filter->Relax(delta, nullptr);
4013 }
4014 break;
4015 }
4016 default:
4017 LOG(FATAL) << "Unknown filter event type.";
4018 }
4019 }
4020 // Synchronize/Commit backwards, so filters can read changes from their
4021 // dependencies before those are synchronized/committed.
4022 synchronized_value_ = 0;
4023 for (auto [filter, event_type] : ::gtl::reversed_view(filter_events_)) {
4024 switch (event_type) {
4025 case FilterEventType::kAccept: {
4026 filter->Synchronize(assignment, delta);
4027 synchronized_value_ = CapAdd(synchronized_value_,
4028 filter->GetSynchronizedObjectiveValue());
4029 break;
4030 }
4031 case FilterEventType::kRelax: {
4032 filter->Commit(assignment, delta);
4033 break;
4034 }
4035 default:
4036 LOG(FATAL) << "Unknown filter event type.";
4037 }
4038 }
4039}
4040
4041// ----- Finds a neighbor of the assignment passed -----
4042
4044 public:
4045 FindOneNeighbor(Assignment* const assignment, IntVar* objective,
4046 SolutionPool* const pool,
4047 LocalSearchOperator* const ls_operator,
4048 DecisionBuilder* const sub_decision_builder,
4049 const RegularLimit* const limit,
4050 LocalSearchFilterManager* filter_manager);
4051 ~FindOneNeighbor() override {}
4052 Decision* Next(Solver* const solver) override;
4053 std::string DebugString() const override { return "FindOneNeighbor"; }
4054
4055 private:
4056 bool FilterAccept(Solver* solver, Assignment* delta, Assignment* deltadelta,
4057 int64_t objective_min, int64_t objective_max);
4058 void SynchronizeAll(Solver* solver);
4059
4060 Assignment* const assignment_;
4061 IntVar* const objective_;
4062 std::unique_ptr<Assignment> reference_assignment_;
4063 std::unique_ptr<Assignment> last_synchronized_assignment_;
4064 Assignment* const filter_assignment_delta_;
4065 SolutionPool* const pool_;
4066 LocalSearchOperator* const ls_operator_;
4067 DecisionBuilder* const sub_decision_builder_;
4068 RegularLimit* limit_;
4069 const RegularLimit* const original_limit_;
4070 bool neighbor_found_;
4071 LocalSearchFilterManager* const filter_manager_;
4072 int64_t solutions_since_last_check_;
4073 int64_t check_period_;
4074 Assignment last_checked_assignment_;
4075 bool has_checked_assignment_ = false;
4076};
4077
4078// reference_assignment_ is used to keep track of the last assignment on which
4079// operators were started, assignment_ corresponding to the last successful
4080// neighbor.
4081// last_synchronized_assignment_ keeps track of the last assignment on which
4082// filters were synchronized and is used to compute the filter_assignment_delta_
4083// when synchronizing again.
4085 IntVar* objective, SolutionPool* const pool,
4086 LocalSearchOperator* const ls_operator,
4087 DecisionBuilder* const sub_decision_builder,
4088 const RegularLimit* const limit,
4089 LocalSearchFilterManager* filter_manager)
4090 : assignment_(assignment),
4091 objective_(objective),
4092 reference_assignment_(new Assignment(assignment_)),
4093 filter_assignment_delta_(assignment->solver()->MakeAssignment()),
4094 pool_(pool),
4095 ls_operator_(ls_operator),
4096 sub_decision_builder_(sub_decision_builder),
4097 limit_(nullptr),
4098 original_limit_(limit),
4099 neighbor_found_(false),
4100 filter_manager_(filter_manager),
4101 solutions_since_last_check_(0),
4102 check_period_(
4103 assignment_->solver()->parameters().check_solution_period()),
4104 last_checked_assignment_(assignment) {
4105 CHECK(nullptr != assignment);
4106 CHECK(nullptr != ls_operator);
4107
4108 Solver* const solver = assignment_->solver();
4109 // If limit is nullptr, default limit is 1 solution
4110 if (nullptr == limit) {
4111 limit_ = solver->MakeSolutionsLimit(1);
4112 } else {
4113 limit_ = limit->MakeIdenticalClone();
4114 // TODO(user): Support skipping neighborhood checks for limits accepting
4115 // more than one solution (e.g. best accept). For now re-enabling systematic
4116 // checks.
4117 if (limit_->solutions() != 1) {
4118 VLOG(1) << "Disabling neighbor-check skipping outside of first accept.";
4119 check_period_ = 1;
4120 }
4121 }
4122 // TODO(user): Support skipping neighborhood checks with LNS (at least on
4123 // the non-LNS operators).
4124 if (ls_operator->HasFragments()) {
4125 VLOG(1) << "Disabling neighbor-check skipping for LNS.";
4126 check_period_ = 1;
4127 }
4128
4129 if (!reference_assignment_->HasObjective()) {
4130 reference_assignment_->AddObjective(objective_);
4131 }
4132}
4133
4135 CHECK(nullptr != solver);
4136
4137 if (original_limit_ != nullptr) {
4138 limit_->Copy(original_limit_);
4139 }
4140
4141 if (!last_checked_assignment_.HasObjective()) {
4142 last_checked_assignment_.AddObjective(assignment_->Objective());
4143 }
4144
4145 if (!neighbor_found_) {
4146 // Only called on the first call to Next(), reference_assignment_ has not
4147 // been synced with assignment_ yet
4148
4149 // Keeping the code in case a performance problem forces us to
4150 // use the old code with a zero test on pool_.
4151 // reference_assignment_->CopyIntersection(assignment_);
4152 pool_->Initialize(assignment_);
4153 SynchronizeAll(solver);
4154 }
4155
4156 {
4157 // Another assignment is needed to apply the delta
4158 Assignment* assignment_copy =
4159 solver->MakeAssignment(reference_assignment_.get());
4160 int counter = 0;
4161
4162 DecisionBuilder* restore = solver->MakeRestoreAssignment(assignment_copy);
4163 if (sub_decision_builder_) {
4164 restore = solver->Compose(restore, sub_decision_builder_);
4165 }
4166 Assignment* delta = solver->MakeAssignment();
4167 Assignment* deltadelta = solver->MakeAssignment();
4168 while (true) {
4169 if (!ls_operator_->HoldsDelta()) {
4170 delta->Clear();
4171 }
4172 delta->ClearObjective();
4173 deltadelta->Clear();
4174 solver->TopPeriodicCheck();
4175 if (++counter >= absl::GetFlag(FLAGS_cp_local_search_sync_frequency) &&
4176 pool_->SyncNeeded(reference_assignment_.get())) {
4177 // TODO(user) : SyncNeed(assignment_) ?
4178 counter = 0;
4179 SynchronizeAll(solver);
4180 }
4181
4182 bool has_neighbor = false;
4183 if (!limit_->Check()) {
4184 solver->GetLocalSearchMonitor()->BeginMakeNextNeighbor(ls_operator_);
4185 has_neighbor = ls_operator_->MakeNextNeighbor(delta, deltadelta);
4187 ls_operator_, has_neighbor, delta, deltadelta);
4188 }
4189
4190 if (has_neighbor && !solver->IsUncheckedSolutionLimitReached()) {
4191 solver->neighbors_ += 1;
4192 // All filters must be called for incrementality reasons.
4193 // Empty deltas must also be sent to incremental filters; can be needed
4194 // to resync filters on non-incremental (empty) moves.
4195 // TODO(user): Don't call both if no filter is incremental and one
4196 // of them returned false.
4197 solver->GetLocalSearchMonitor()->BeginFilterNeighbor(ls_operator_);
4198 const bool mh_filter =
4199 AcceptDelta(solver->ParentSearch(), delta, deltadelta);
4200 int64_t objective_min = std::numeric_limits<int64_t>::min();
4201 int64_t objective_max = std::numeric_limits<int64_t>::max();
4202 if (objective_) {
4203 objective_min = objective_->Min();
4204 objective_max = objective_->Max();
4205 }
4206 if (delta->HasObjective() && delta->Objective() == objective_) {
4207 objective_min = std::max(objective_min, delta->ObjectiveMin());
4208 objective_max = std::min(objective_max, delta->ObjectiveMax());
4209 }
4210 const bool move_filter = FilterAccept(solver, delta, deltadelta,
4211 objective_min, objective_max);
4213 ls_operator_, mh_filter && move_filter);
4214 if (!mh_filter || !move_filter) {
4215 if (filter_manager_ != nullptr) filter_manager_->Revert();
4216 continue;
4217 }
4218 solver->filtered_neighbors_ += 1;
4219 if (delta->HasObjective()) {
4220 if (!assignment_copy->HasObjective()) {
4221 assignment_copy->AddObjective(delta->Objective());
4222 }
4223 if (!assignment_->HasObjective()) {
4224 assignment_->AddObjective(delta->Objective());
4225 last_checked_assignment_.AddObjective(delta->Objective());
4226 }
4227 }
4228 assignment_copy->CopyIntersection(reference_assignment_.get());
4229 assignment_copy->CopyIntersection(delta);
4230 solver->GetLocalSearchMonitor()->BeginAcceptNeighbor(ls_operator_);
4231 const bool check_solution = (solutions_since_last_check_ == 0) ||
4232 !solver->UseFastLocalSearch() ||
4233 // LNS deltas need to be restored
4234 !delta->AreAllElementsBound();
4235 if (has_checked_assignment_) solutions_since_last_check_++;
4236 if (solutions_since_last_check_ >= check_period_) {
4237 solutions_since_last_check_ = 0;
4238 }
4239 const bool accept = !check_solution || solver->SolveAndCommit(restore);
4240 solver->GetLocalSearchMonitor()->EndAcceptNeighbor(ls_operator_,
4241 accept);
4242 if (accept) {
4243 solver->accepted_neighbors_ += 1;
4244 if (check_solution) {
4245 solver->SetSearchContext(solver->ParentSearch(),
4246 ls_operator_->DebugString());
4247 assignment_->Store();
4248 last_checked_assignment_.CopyIntersection(assignment_);
4249 neighbor_found_ = true;
4250 has_checked_assignment_ = true;
4251 return nullptr;
4252 }
4253 solver->SetSearchContext(solver->ActiveSearch(),
4254 ls_operator_->DebugString());
4255 assignment_->CopyIntersection(assignment_copy);
4256 assignment_->SetObjectiveValue(
4257 filter_manager_ ? filter_manager_->GetAcceptedObjectiveValue()
4258 : 0);
4259 // Advancing local search to the current solution without
4260 // checking.
4261 // TODO(user): support the case were limit_ accepts more than
4262 // one solution (e.g. best accept).
4263 AcceptUncheckedNeighbor(solver->ParentSearch());
4264 solver->IncrementUncheckedSolutionCounter();
4265 pool_->RegisterNewSolution(assignment_);
4266 SynchronizeAll(solver);
4267 // NOTE: SynchronizeAll() sets neighbor_found_ to false, force it
4268 // back to true when skipping checks.
4269 neighbor_found_ = true;
4270 } else {
4271 if (filter_manager_ != nullptr) filter_manager_->Revert();
4272 if (check_period_ > 1 && has_checked_assignment_) {
4273 // Filtering is not perfect, disabling fast local search and
4274 // resynchronizing with the last checked solution.
4275 // TODO(user): Restore state of local search operators to
4276 // make sure we are exploring neighbors in the same order. This can
4277 // affect the local optimum found.
4278 VLOG(1) << "Imperfect filtering detected, backtracking to last "
4279 "checked solution and checking all solutions.";
4280 check_period_ = 1;
4281 solutions_since_last_check_ = 0;
4282 pool_->RegisterNewSolution(&last_checked_assignment_);
4283 SynchronizeAll(solver);
4284 assignment_->CopyIntersection(&last_checked_assignment_);
4285 }
4286 }
4287 } else {
4288 if (neighbor_found_) {
4289 // In case the last checked assignment isn't the current one, restore
4290 // it to make sure the solver knows about it, especially if this is
4291 // the end of the search.
4292 // TODO(user): Compare assignments in addition to their cost.
4293 if (last_checked_assignment_.ObjectiveValue() !=
4294 assignment_->ObjectiveValue()) {
4295 // If restoring fails this means filtering is not perfect and the
4296 // solver will consider the last checked assignment.
4297 assignment_copy->CopyIntersection(assignment_);
4298 if (!solver->SolveAndCommit(restore)) solver->Fail();
4299 last_checked_assignment_.CopyIntersection(assignment_);
4300 has_checked_assignment_ = true;
4301 return nullptr;
4302 }
4303 AcceptNeighbor(solver->ParentSearch());
4304 // Keeping the code in case a performance problem forces us to
4305 // use the old code with a zero test on pool_.
4306 // reference_assignment_->CopyIntersection(assignment_);
4307 pool_->RegisterNewSolution(assignment_);
4308 SynchronizeAll(solver);
4309 } else {
4310 break;
4311 }
4312 }
4313 }
4314 }
4315 solver->Fail();
4316 return nullptr;
4317}
4318
4319bool FindOneNeighbor::FilterAccept(Solver* solver, Assignment* delta,
4320 Assignment* deltadelta,
4321 int64_t objective_min,
4322 int64_t objective_max) {
4323 if (filter_manager_ == nullptr) return true;
4324 LocalSearchMonitor* const monitor = solver->GetLocalSearchMonitor();
4325 return filter_manager_->Accept(monitor, delta, deltadelta, objective_min,
4326 objective_max);
4327}
4328
4329namespace {
4330
4331template <typename Container>
4332void AddDeltaElements(const Container& old_container,
4333 const Container& new_container, Assignment* delta) {
4334 for (const auto& new_element : new_container.elements()) {
4335 const auto var = new_element.Var();
4336 const auto old_element_ptr = old_container.ElementPtrOrNull(var);
4337 if (old_element_ptr == nullptr || *old_element_ptr != new_element) {
4338 delta->FastAdd(var)->Copy(new_element);
4339 }
4340 }
4341}
4342
4343void MakeDelta(const Assignment* old_assignment,
4344 const Assignment* new_assignment, Assignment* delta) {
4345 DCHECK_NE(delta, nullptr);
4346 delta->Clear();
4347 AddDeltaElements(old_assignment->IntVarContainer(),
4348 new_assignment->IntVarContainer(), delta);
4349 AddDeltaElements(old_assignment->IntervalVarContainer(),
4350 new_assignment->IntervalVarContainer(), delta);
4351 AddDeltaElements(old_assignment->SequenceVarContainer(),
4352 new_assignment->SequenceVarContainer(), delta);
4353}
4354} // namespace
4355
4356void FindOneNeighbor::SynchronizeAll(Solver* solver) {
4357 Assignment* const reference_assignment = reference_assignment_.get();
4358 pool_->GetNextSolution(reference_assignment);
4359 neighbor_found_ = false;
4360 limit_->Init();
4361 solver->GetLocalSearchMonitor()->BeginOperatorStart();
4362 ls_operator_->Start(reference_assignment);
4363 if (filter_manager_ != nullptr) {
4364 Assignment* delta = nullptr;
4365 if (last_synchronized_assignment_ == nullptr) {
4366 last_synchronized_assignment_ =
4367 absl::make_unique<Assignment>(reference_assignment);
4368 } else {
4369 MakeDelta(last_synchronized_assignment_.get(), reference_assignment,
4370 filter_assignment_delta_);
4371 delta = filter_assignment_delta_;
4372 last_synchronized_assignment_->Copy(reference_assignment);
4373 }
4374 filter_manager_->Synchronize(reference_assignment_.get(), delta);
4375 }
4376 solver->GetLocalSearchMonitor()->EndOperatorStart();
4377}
4378
4379// ---------- Local Search Phase Parameters ----------
4380
4382 public:
4386 RegularLimit* const limit,
4388 : objective_(objective),
4389 solution_pool_(pool),
4390 ls_operator_(ls_operator),
4391 sub_decision_builder_(sub_decision_builder),
4392 limit_(limit),
4393 filter_manager_(filter_manager) {}
4395 std::string DebugString() const override {
4396 return "LocalSearchPhaseParameters";
4397 }
4398
4399 IntVar* objective() const { return objective_; }
4400 SolutionPool* solution_pool() const { return solution_pool_; }
4401 LocalSearchOperator* ls_operator() const { return ls_operator_; }
4403 return sub_decision_builder_;
4404 }
4405 RegularLimit* limit() const { return limit_; }
4407 return filter_manager_;
4408 }
4409
4410 private:
4411 IntVar* const objective_;
4412 SolutionPool* const solution_pool_;
4413 LocalSearchOperator* const ls_operator_;
4414 DecisionBuilder* const sub_decision_builder_;
4415 RegularLimit* const limit_;
4416 LocalSearchFilterManager* const filter_manager_;
4417};
4418
4420 IntVar* objective, LocalSearchOperator* const ls_operator,
4421 DecisionBuilder* const sub_decision_builder) {
4423 ls_operator, sub_decision_builder,
4424 nullptr, nullptr);
4425}
4426
4428 IntVar* objective, LocalSearchOperator* const ls_operator,
4429 DecisionBuilder* const sub_decision_builder, RegularLimit* const limit) {
4431 ls_operator, sub_decision_builder,
4432 limit, nullptr);
4433}
4434
4436 IntVar* objective, LocalSearchOperator* const ls_operator,
4437 DecisionBuilder* const sub_decision_builder, RegularLimit* const limit,
4438 LocalSearchFilterManager* filter_manager) {
4440 ls_operator, sub_decision_builder,
4441 limit, filter_manager);
4442}
4443
4445 IntVar* objective, SolutionPool* const pool,
4446 LocalSearchOperator* const ls_operator,
4447 DecisionBuilder* const sub_decision_builder) {
4448 return MakeLocalSearchPhaseParameters(objective, pool, ls_operator,
4449 sub_decision_builder, nullptr, nullptr);
4450}
4451
4453 IntVar* objective, SolutionPool* const pool,
4454 LocalSearchOperator* const ls_operator,
4455 DecisionBuilder* const sub_decision_builder, RegularLimit* const limit) {
4456 return MakeLocalSearchPhaseParameters(objective, pool, ls_operator,
4457 sub_decision_builder, limit, nullptr);
4458}
4459
4461 IntVar* objective, SolutionPool* const pool,
4462 LocalSearchOperator* const ls_operator,
4463 DecisionBuilder* const sub_decision_builder, RegularLimit* const limit,
4464 LocalSearchFilterManager* filter_manager) {
4465 return RevAlloc(new LocalSearchPhaseParameters(objective, pool, ls_operator,
4466 sub_decision_builder, limit,
4467 filter_manager));
4468}
4469
4470namespace {
4471// ----- NestedSolve decision wrapper -----
4472
4473// This decision calls a nested Solve on the given DecisionBuilder in its
4474// left branch; does nothing in the left branch.
4475// The state of the decision corresponds to the result of the nested Solve:
4476// DECISION_PENDING - Nested Solve not called yet
4477// DECISION_FAILED - Nested Solve failed
4478// DECISION_FOUND - Nested Solve succeeded
4479
4480class NestedSolveDecision : public Decision {
4481 public:
4482 // This enum is used internally to tag states in the local search tree.
4483 enum StateType { DECISION_PENDING, DECISION_FAILED, DECISION_FOUND };
4484
4485 NestedSolveDecision(DecisionBuilder* const db, bool restore,
4486 const std::vector<SearchMonitor*>& monitors);
4487 NestedSolveDecision(DecisionBuilder* const db, bool restore);
4488 ~NestedSolveDecision() override {}
4489 void Apply(Solver* const solver) override;
4490 void Refute(Solver* const solver) override;
4491 std::string DebugString() const override { return "NestedSolveDecision"; }
4492 int state() const { return state_; }
4493
4494 private:
4495 DecisionBuilder* const db_;
4496 bool restore_;
4497 std::vector<SearchMonitor*> monitors_;
4498 int state_;
4499};
4500
4501NestedSolveDecision::NestedSolveDecision(
4502 DecisionBuilder* const db, bool restore,
4503 const std::vector<SearchMonitor*>& monitors)
4504 : db_(db),
4505 restore_(restore),
4506 monitors_(monitors),
4507 state_(DECISION_PENDING) {
4508 CHECK(nullptr != db);
4509}
4510
4511NestedSolveDecision::NestedSolveDecision(DecisionBuilder* const db,
4512 bool restore)
4513 : db_(db), restore_(restore), state_(DECISION_PENDING) {
4514 CHECK(nullptr != db);
4515}
4516
4517void NestedSolveDecision::Apply(Solver* const solver) {
4518 CHECK(nullptr != solver);
4519 if (restore_) {
4520 if (solver->Solve(db_, monitors_)) {
4521 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FOUND));
4522 } else {
4523 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FAILED));
4524 }
4525 } else {
4526 if (solver->SolveAndCommit(db_, monitors_)) {
4527 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FOUND));
4528 } else {
4529 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FAILED));
4530 }
4531 }
4532}
4533
4534void NestedSolveDecision::Refute(Solver* const solver) {}
4535
4536// ----- Local search decision builder -----
4537
4538// Given a first solution (resulting from either an initial assignment or the
4539// result of a decision builder), it searches for neighbors using a local
4540// search operator. The first solution corresponds to the first leaf of the
4541// search.
4542// The local search applies to the variables contained either in the assignment
4543// or the vector of variables passed.
4544
4545class LocalSearch : public DecisionBuilder {
4546 public:
4547 LocalSearch(Assignment* const assignment, IntVar* objective,
4548 SolutionPool* const pool, LocalSearchOperator* const ls_operator,
4549 DecisionBuilder* const sub_decision_builder,
4550 RegularLimit* const limit,
4551 LocalSearchFilterManager* filter_manager);
4552 // TODO(user): find a way to not have to pass vars here: redundant with
4553 // variables in operators
4554 LocalSearch(const std::vector<IntVar*>& vars, IntVar* objective,
4555 SolutionPool* const pool, DecisionBuilder* const first_solution,
4556 LocalSearchOperator* const ls_operator,
4557 DecisionBuilder* const sub_decision_builder,
4558 RegularLimit* const limit,
4559 LocalSearchFilterManager* filter_manager);
4560 LocalSearch(const std::vector<IntVar*>& vars, IntVar* objective,
4561 SolutionPool* const pool, DecisionBuilder* const first_solution,
4562 DecisionBuilder* const first_solution_sub_decision_builder,
4563 LocalSearchOperator* const ls_operator,
4564 DecisionBuilder* const sub_decision_builder,
4565 RegularLimit* const limit,
4566 LocalSearchFilterManager* filter_manager);
4567 LocalSearch(const std::vector<SequenceVar*>& vars, IntVar* objective,
4568 SolutionPool* const pool, DecisionBuilder* const first_solution,
4569 LocalSearchOperator* const ls_operator,
4570 DecisionBuilder* const sub_decision_builder,
4571 RegularLimit* const limit,
4572 LocalSearchFilterManager* filter_manager);
4573 ~LocalSearch() override;
4574 Decision* Next(Solver* const solver) override;
4575 std::string DebugString() const override { return "LocalSearch"; }
4576 void Accept(ModelVisitor* const visitor) const override;
4577
4578 protected:
4579 void PushFirstSolutionDecision(DecisionBuilder* first_solution);
4580 void PushLocalSearchDecision();
4581
4582 private:
4583 Assignment* assignment_;
4584 IntVar* const objective_ = nullptr;
4585 SolutionPool* const pool_;
4586 LocalSearchOperator* const ls_operator_;
4587 DecisionBuilder* const first_solution_sub_decision_builder_;
4588 DecisionBuilder* const sub_decision_builder_;
4589 std::vector<NestedSolveDecision*> nested_decisions_;
4590 int nested_decision_index_;
4591 RegularLimit* const limit_;
4592 LocalSearchFilterManager* const filter_manager_;
4593 bool has_started_;
4594};
4595
4596LocalSearch::LocalSearch(Assignment* const assignment, IntVar* objective,
4597 SolutionPool* const pool,
4598 LocalSearchOperator* const ls_operator,
4599 DecisionBuilder* const sub_decision_builder,
4600 RegularLimit* const limit,
4601 LocalSearchFilterManager* filter_manager)
4602 : assignment_(nullptr),
4603 objective_(objective),
4604 pool_(pool),
4605 ls_operator_(ls_operator),
4606 first_solution_sub_decision_builder_(sub_decision_builder),
4607 sub_decision_builder_(sub_decision_builder),
4608 nested_decision_index_(0),
4609 limit_(limit),
4610 filter_manager_(filter_manager),
4611 has_started_(false) {
4612 CHECK(nullptr != assignment);
4613 CHECK(nullptr != ls_operator);
4614 Solver* const solver = assignment->solver();
4615 assignment_ = solver->GetOrCreateLocalSearchState();
4616 assignment_->Copy(assignment);
4617 DecisionBuilder* restore = solver->MakeRestoreAssignment(assignment);
4618 PushFirstSolutionDecision(restore);
4619 PushLocalSearchDecision();
4620}
4621
4622LocalSearch::LocalSearch(const std::vector<IntVar*>& vars, IntVar* objective,
4623 SolutionPool* const pool,
4624 DecisionBuilder* const first_solution,
4625 LocalSearchOperator* const ls_operator,
4626 DecisionBuilder* const sub_decision_builder,
4627 RegularLimit* const limit,
4628 LocalSearchFilterManager* filter_manager)
4629 : assignment_(nullptr),
4630 objective_(objective),
4631 pool_(pool),
4632 ls_operator_(ls_operator),
4633 first_solution_sub_decision_builder_(sub_decision_builder),
4634 sub_decision_builder_(sub_decision_builder),
4635 nested_decision_index_(0),
4636 limit_(limit),
4637 filter_manager_(filter_manager),
4638 has_started_(false) {
4639 CHECK(nullptr != first_solution);
4640 CHECK(nullptr != ls_operator);
4641 CHECK(!vars.empty());
4642 Solver* const solver = vars[0]->solver();
4643 assignment_ = solver->GetOrCreateLocalSearchState();
4644 assignment_->Add(vars);
4645 PushFirstSolutionDecision(first_solution);
4646 PushLocalSearchDecision();
4647}
4648
4649LocalSearch::LocalSearch(
4650 const std::vector<IntVar*>& vars, IntVar* objective,
4651 SolutionPool* const pool, DecisionBuilder* const first_solution,
4652 DecisionBuilder* const first_solution_sub_decision_builder,
4653 LocalSearchOperator* const ls_operator,
4654 DecisionBuilder* const sub_decision_builder, RegularLimit* const limit,
4655 LocalSearchFilterManager* filter_manager)
4656 : assignment_(nullptr),
4657 objective_(objective),
4658 pool_(pool),
4659 ls_operator_(ls_operator),
4660 first_solution_sub_decision_builder_(first_solution_sub_decision_builder),
4661 sub_decision_builder_(sub_decision_builder),
4662 nested_decision_index_(0),
4663 limit_(limit),
4664 filter_manager_(filter_manager),
4665 has_started_(false) {
4666 CHECK(nullptr != first_solution);
4667 CHECK(nullptr != ls_operator);
4668 CHECK(!vars.empty());
4669 Solver* const solver = vars[0]->solver();
4670 assignment_ = solver->GetOrCreateLocalSearchState();
4671 assignment_->Add(vars);
4672 PushFirstSolutionDecision(first_solution);
4673 PushLocalSearchDecision();
4674}
4675
4676LocalSearch::LocalSearch(const std::vector<SequenceVar*>& vars,
4677 IntVar* objective, SolutionPool* const pool,
4678 DecisionBuilder* const first_solution,
4679 LocalSearchOperator* const ls_operator,
4680 DecisionBuilder* const sub_decision_builder,
4681 RegularLimit* const limit,
4682 LocalSearchFilterManager* filter_manager)
4683 : assignment_(nullptr),
4684 objective_(objective),
4685 pool_(pool),
4686 ls_operator_(ls_operator),
4687 first_solution_sub_decision_builder_(sub_decision_builder),
4688 sub_decision_builder_(sub_decision_builder),
4689 nested_decision_index_(0),
4690 limit_(limit),
4691 filter_manager_(filter_manager),
4692 has_started_(false) {
4693 CHECK(nullptr != first_solution);
4694 CHECK(nullptr != ls_operator);
4695 CHECK(!vars.empty());
4696 Solver* const solver = vars[0]->solver();
4697 assignment_ = solver->GetOrCreateLocalSearchState();
4698 assignment_->Add(vars);
4699 PushFirstSolutionDecision(first_solution);
4700 PushLocalSearchDecision();
4701}
4702
4703LocalSearch::~LocalSearch() {}
4704
4705// Model Visitor support.
4706void LocalSearch::Accept(ModelVisitor* const visitor) const {
4707 DCHECK(assignment_ != nullptr);
4708 visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension);
4709 // We collect decision variables from the assignment.
4710 const std::vector<IntVarElement>& elements =
4711 assignment_->IntVarContainer().elements();
4712 if (!elements.empty()) {
4713 std::vector<IntVar*> vars;
4714 for (const IntVarElement& elem : elements) {
4715 vars.push_back(elem.Var());
4716 }
4717 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
4718 vars);
4719 }
4720 const std::vector<IntervalVarElement>& interval_elements =
4721 assignment_->IntervalVarContainer().elements();
4722 if (!interval_elements.empty()) {
4723 std::vector<IntervalVar*> interval_vars;
4724 for (const IntervalVarElement& elem : interval_elements) {
4725 interval_vars.push_back(elem.Var());
4726 }
4727 visitor->VisitIntervalArrayArgument(ModelVisitor::kIntervalsArgument,
4728 interval_vars);
4729 }
4730 visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension);
4731}
4732
4733// This is equivalent to a multi-restart decision builder
4734// TODO(user): abstract this from the local search part
4735// TODO(user): handle the case where the tree depth is not enough to hold
4736// all solutions.
4737
4738Decision* LocalSearch::Next(Solver* const solver) {
4739 CHECK(nullptr != solver);
4740 CHECK_LT(0, nested_decisions_.size());
4741 if (!has_started_) {
4742 nested_decision_index_ = 0;
4743 solver->SaveAndSetValue(&has_started_, true);
4744 } else if (nested_decision_index_ < 0) {
4745 solver->Fail();
4746 }
4747 NestedSolveDecision* decision = nested_decisions_[nested_decision_index_];
4748 const int state = decision->state();
4749 switch (state) {
4750 case NestedSolveDecision::DECISION_FAILED: {
4751 // A local optimum has been reached. The search will continue only if we
4752 // accept up-hill moves (due to metaheuristics). In this case we need to
4753 // reset neighborhood optimal routes.
4754 ls_operator_->Reset();
4755 if (!LocalOptimumReached(solver->ActiveSearch())) {
4756 nested_decision_index_ = -1; // Stop the search
4757 }
4758 solver->Fail();
4759 return nullptr;
4760 }
4761 case NestedSolveDecision::DECISION_PENDING: {
4762 // TODO(user): Find a way to make this balancing invisible to the
4763 // user (no increase in branch or fail counts for instance).
4764 const int32_t kLocalSearchBalancedTreeDepth = 32;
4765 const int depth = solver->SearchDepth();
4766 if (depth < kLocalSearchBalancedTreeDepth) {
4767 return solver->balancing_decision();
4768 }
4769 if (depth > kLocalSearchBalancedTreeDepth) {
4770 solver->Fail();
4771 }
4772 return decision;
4773 }
4774 case NestedSolveDecision::DECISION_FOUND: {
4775 // Next time go to next decision
4776 if (nested_decision_index_ + 1 < nested_decisions_.size()) {
4777 ++nested_decision_index_;
4778 }
4779 return nullptr;
4780 }
4781 default: {
4782 LOG(ERROR) << "Unknown local search state";
4783 return nullptr;
4784 }
4785 }
4786 return nullptr;
4787}
4788
4789void LocalSearch::PushFirstSolutionDecision(DecisionBuilder* first_solution) {
4790 CHECK(first_solution);
4791 Solver* const solver = assignment_->solver();
4792 DecisionBuilder* store = solver->MakeStoreAssignment(assignment_);
4793 DecisionBuilder* first_solution_and_store = solver->Compose(
4794 solver->MakeProfiledDecisionBuilderWrapper(first_solution),
4795 first_solution_sub_decision_builder_, store);
4796 std::vector<SearchMonitor*> monitor;
4797 monitor.push_back(limit_);
4798 nested_decisions_.push_back(solver->RevAlloc(
4799 new NestedSolveDecision(first_solution_and_store, false, monitor)));
4800}
4801
4802void LocalSearch::PushLocalSearchDecision() {
4803 Solver* const solver = assignment_->solver();
4804 DecisionBuilder* find_neighbors = solver->RevAlloc(
4805 new FindOneNeighbor(assignment_, objective_, pool_, ls_operator_,
4806 sub_decision_builder_, limit_, filter_manager_));
4807 nested_decisions_.push_back(
4808 solver->RevAlloc(new NestedSolveDecision(find_neighbors, false)));
4809}
4810
4811class DefaultSolutionPool : public SolutionPool {
4812 public:
4813 DefaultSolutionPool() {}
4814
4815 ~DefaultSolutionPool() override {}
4816
4817 void Initialize(Assignment* const assignment) override {
4818 reference_assignment_ = absl::make_unique<Assignment>(assignment);
4819 }
4820
4821 void RegisterNewSolution(Assignment* const assignment) override {
4822 reference_assignment_->CopyIntersection(assignment);
4823 }
4824
4825 void GetNextSolution(Assignment* const assignment) override {
4826 assignment->CopyIntersection(reference_assignment_.get());
4827 }
4828
4829 bool SyncNeeded(Assignment* const local_assignment) override { return false; }
4830
4831 std::string DebugString() const override { return "DefaultSolutionPool"; }
4832
4833 private:
4834 std::unique_ptr<Assignment> reference_assignment_;
4835};
4836} // namespace
4837
4839 return RevAlloc(new DefaultSolutionPool());
4840}
4841
4844 return RevAlloc(new LocalSearch(
4845 assignment, parameters->objective(), parameters->solution_pool(),
4846 parameters->ls_operator(), parameters->sub_decision_builder(),
4847 parameters->limit(), parameters->filter_manager()));
4848}
4849
4851 const std::vector<IntVar*>& vars, DecisionBuilder* first_solution,
4853 return RevAlloc(new LocalSearch(
4854 vars, parameters->objective(), parameters->solution_pool(),
4855 first_solution, parameters->ls_operator(),
4856 parameters->sub_decision_builder(), parameters->limit(),
4857 parameters->filter_manager()));
4858}
4859
4861 const std::vector<IntVar*>& vars, DecisionBuilder* first_solution,
4862 DecisionBuilder* first_solution_sub_decision_builder,
4864 return RevAlloc(new LocalSearch(
4865 vars, parameters->objective(), parameters->solution_pool(),
4866 first_solution, first_solution_sub_decision_builder,
4867 parameters->ls_operator(), parameters->sub_decision_builder(),
4868 parameters->limit(), parameters->filter_manager()));
4869}
4870
4872 const std::vector<SequenceVar*>& vars, DecisionBuilder* first_solution,
4874 return RevAlloc(new LocalSearch(
4875 vars, parameters->objective(), parameters->solution_pool(),
4876 first_solution, parameters->ls_operator(),
4877 parameters->sub_decision_builder(), parameters->limit(),
4878 parameters->filter_manager()));
4879}
4880} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK(condition)
Definition: base/logging.h:495
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:892
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:891
#define CHECK_LT(val1, val2)
Definition: base/logging.h:705
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:702
#define CHECK_GE(val1, val2)
Definition: base/logging.h:706
#define CHECK_GT(val1, val2)
Definition: base/logging.h:707
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:894
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:895
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:893
#define LOG(severity)
Definition: base/logging.h:420
#define DCHECK(condition)
Definition: base/logging.h:889
#define CHECK_LE(val1, val2)
Definition: base/logging.h:704
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:890
#define VLOG(verboselevel)
Definition: base/logging.h:983
void Start()
Definition: timer.h:31
void Stop()
Definition: timer.h:39
double Get() const
Definition: timer.h:45
const E & Element(const V *const var) const
const std::vector< E > & elements() const
An Assignment is a variable -> domains mapping, used to report solutions to the user.
const IntervalContainer & IntervalVarContainer() const
void CopyIntersection(const Assignment *assignment)
Copies the intersection of the two assignments to the current assignment.
AssignmentContainer< IntVar, IntVarElement > IntContainer
const IntContainer & IntVarContainer() const
BaseInactiveNodeToPathOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, int number_of_base_nodes, std::function< int(int64_t)> start_empty_path_class)
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
This is the base class for building an Lns operator.
virtual bool NextFragment()=0
void AppendToFragment(int index)
BaseLns(const std::vector< IntVar * > &vars)
bool MakeOneNeighbor() override
This method should not be overridden. Override NextFragment() instead.
A BaseObject is the root of all reversibly allocated objects.
virtual std::string DebugString() const
void Set(IndexType i)
Definition: bitset.h:495
ChangeValue(const std::vector< IntVar * > &vars)
virtual int64_t ModifyValue(int64_t index, int64_t value)=0
bool MakeOneNeighbor() override
This method should not be overridden. Override ModifyValue() instead.
bool MakeNeighbor() override
Cross(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::string DebugString() const override
A DecisionBuilder is responsible for creating the search tree.
A Decision represents a choice point in the search tree.
Exchange(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::string DebugString() const override
ExtendedSwapActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
FindOneNeighbor(Assignment *const assignment, IntVar *objective, SolutionPool *const pool, LocalSearchOperator *const ls_operator, DecisionBuilder *const sub_decision_builder, const RegularLimit *const limit, LocalSearchFilterManager *filter_manager)
Decision * Next(Solver *const solver) override
This is the main method of the decision builder class.
std::string DebugString() const override
virtual int64_t Min() const =0
virtual int64_t Max() const =0
The class IntVar is a subset of IntExpr.
IntVar * Var() override
Creates a variable from the expression.
void SynchronizeOnAssignment(const Assignment *assignment)
virtual void OnSynchronize(const Assignment *delta)
void Synchronize(const Assignment *assignment, const Assignment *delta) override
This method should not be overridden.
bool FindIndex(IntVar *const var, int64_t *index) const
IntVarLocalSearchFilter(const std::vector< IntVar * > &vars)
void AddVars(const std::vector< IntVar * > &vars)
Add variables to "track" to the filter.
Specialization of LocalSearchOperator built from an array of IntVars which specifies the scope of the...
bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta) override
Redefines MakeNextNeighbor to export a simpler interface.
Definition: local_search.cc:76
virtual bool MakeOneNeighbor()
Creates a new neighbor.
Definition: local_search.cc:97
LinKernighan(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, const Solver::IndexEvaluator3 &evaluator, bool topt)
std::string DebugString() const override
Local Search Filters are used for fast neighbor pruning.
Filter manager: when a move is made, filters are executed to decide whether the solution is feasible ...
LocalSearchFilterManager(std::vector< FilterEvent > filter_events)
bool Accept(LocalSearchMonitor *const monitor, const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max)
Returns true iff all filters return true, and the sum of their accepted objectives is between objecti...
void Synchronize(const Assignment *assignment, const Assignment *delta)
Synchronizes all filters to assignment.
virtual void EndMakeNextNeighbor(const LocalSearchOperator *op, bool neighbor_found, const Assignment *delta, const Assignment *deltadelta)=0
virtual void EndAcceptNeighbor(const LocalSearchOperator *op, bool neighbor_found)=0
virtual void BeginMakeNextNeighbor(const LocalSearchOperator *op)=0
virtual void EndFiltering(const LocalSearchFilter *filter, bool reject)=0
virtual void BeginFilterNeighbor(const LocalSearchOperator *op)=0
virtual void BeginAcceptNeighbor(const LocalSearchOperator *op)=0
virtual void BeginFiltering(const LocalSearchFilter *filter)=0
virtual void EndFilterNeighbor(const LocalSearchOperator *op, bool neighbor_found)=0
The base class for all local search operators.
virtual const LocalSearchOperator * Self() const
virtual bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta)=0
virtual void Start(const Assignment *assignment)=0
LocalSearchFilterManager *const filter_manager() const
LocalSearchPhaseParameters(IntVar *objective, SolutionPool *const pool, LocalSearchOperator *ls_operator, DecisionBuilder *sub_decision_builder, RegularLimit *const limit, LocalSearchFilterManager *filter_manager)
void BeginFiltering(const LocalSearchFilter *filter) override
void Install() override
Install itself on the solver.
void BeginOperatorStart() override
Local search operator events.
void RestartSearch() override
Restart the search.
void EndMakeNextNeighbor(const LocalSearchOperator *op, bool neighbor_found, const Assignment *delta, const Assignment *deltadelta) override
LocalSearchStatistics ExportToLocalSearchStatistics() const
void BeginMakeNextNeighbor(const LocalSearchOperator *op) override
void EndAcceptNeighbor(const LocalSearchOperator *op, bool neighbor_found) override
void BeginAcceptNeighbor(const LocalSearchOperator *op) override
void ExitSearch() override
End of the search.
void EndFilterNeighbor(const LocalSearchOperator *op, bool neighbor_found) override
void EndFiltering(const LocalSearchFilter *filter, bool reject) override
void BeginFilterNeighbor(const LocalSearchOperator *op) override
std::string DebugString() const override
void AddFirstSolutionProfiledDecisionBuilder(ProfiledDecisionBuilder *profiled_db)
LocalSearchVariable AddVariable(int64_t initial_min, int64_t initial_max)
::operations_research::LocalSearchStatistics_LocalSearchOperatorStatistics * add_local_search_operator_statistics()
::operations_research::LocalSearchStatistics_LocalSearchFilterStatistics * add_local_search_filter_statistics()
::operations_research::LocalSearchStatistics_FirstSolutionStatistics * add_first_solution_statistics()
MakeActiveAndRelocate(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::string DebugString() const override
MakeActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::string DebugString() const override
int64_t GetBaseNodeRestartPosition(int base_index) override
Returns the index of the node to which the base node of index base_index must be set to when it reach...
bool OnSamePathAsPreviousBase(int64_t base_index) override
Returns true if a base node has to be on the same path as the "previous" base node (base node of inde...
MakeChainInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
MakeInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::string DebugString() const override
static const char kVariableGroupExtension[]
const std::vector< int > & Neighbors(int index) const
NearestNeighbors(Solver::IndexEvaluator3 evaluator, const PathOperator &path_operator, int size)
virtual std::string DebugString() const
bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta) override
NeighborhoodLimit(LocalSearchOperator *const op, int64_t limit)
void Start(const Assignment *assignment) override
std::string DebugString() const override
PathLns(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, int number_of_chunks, int chunk_size, bool unactive_fragments)
bool HasFragments() const override
std::string DebugString() const override
Base class of the local search operators dedicated to path modifications (a path is a set of nodes li...
int64_t StartNode(int i) const
Returns the start node of the ith base node.
bool IsInactive(int64_t node) const
Returns true if node is inactive.
virtual bool ConsiderAlternatives(int64_t base_index) const
Indicates if alternatives should be considered when iterating over base nodes.
int PathClass(int i) const
Returns the class of the path of the ith base node.
virtual void OnNodeInitialization()
Called by OnStart() after initializing node information.
virtual bool OnSamePathAsPreviousBase(int64_t base_index)
Returns true if a base node has to be on the same path as the "previous" base node (base node of inde...
int number_of_nexts() const
Number of next variables.
bool CheckChainValidity(int64_t before_chain, int64_t chain_end, int64_t exclude) const
Returns true if the chain is a valid path without cycles from before_chain to chain_end and does not ...
virtual bool RestartAtPathStartOnSynchronize()
When the operator is being synchronized with a new solution (when Start() is called),...
bool IsPathEnd(int64_t node) const
Returns true if node is the last node on the path; defined by the fact that node is outside the range...
int64_t Next(int64_t node) const
Returns the node after node in the current delta.
bool MoveChain(int64_t before_chain, int64_t chain_end, int64_t destination)
Moves the chain starting after the node before_chain and ending at the node chain_end after the node ...
bool MakeActive(int64_t node, int64_t destination)
Insert the inactive node after destination.
bool ReverseChain(int64_t before_chain, int64_t after_chain, int64_t *chain_last)
Reverses the chain starting after before_chain and ending before after_chain.
void SetNext(int64_t from, int64_t to, int64_t path)
Sets 'to' to be the node after 'from' on the given path.
int64_t Prev(int64_t node) const
Returns the node before node in the current delta.
int64_t OldNext(int64_t node) const
bool SkipUnchanged(int index) const override
bool SwapActiveAndInactive(int64_t active, int64_t inactive)
Replaces active by inactive in the current path, making active inactive.
void ResetPosition()
Reset the position of the operator to its position when Start() was last called; this can be used to ...
virtual int64_t GetBaseNodeRestartPosition(int base_index)
Returns the index of the node to which the base node of index base_index must be set to when it reach...
int64_t BaseNode(int i) const
Returns the ith base node of the operator.
int GetSiblingAlternativeIndex(int node) const
Returns the index of the alternative set of the sibling of node.
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
int64_t Path(int64_t node) const
Returns the index of the path to which node belongs in the current delta.
virtual bool InitPosition() const
Returns true if the operator needs to restart its initial position at each call to Start()
PathOperator(const std::vector< IntVar * > &next_vars, const std::vector< IntVar * > &path_vars, IterationParameters iteration_parameters)
Builds an instance of PathOperator from next and path variables.
bool MakeChainInactive(int64_t before_chain, int64_t chain_end)
Makes the nodes on the chain starting after before_chain and ending at chain_end inactive.
const std::vector< int > & ChangedPaths() const
NodeRange Nodes(int path) const
const std::vector< std::pair< int, int > > & ChangedArcs() const
ChainRange Chains(int path) const
PathState(int num_nodes, std::vector< int > path_start, std::vector< int > path_end)
Usual limit based on wall_time, number of explored branches and number of failures in the search tree...
bool Check() override
This method is called to check the status of the limit.
Definition: search.cc:4071
void Init() override
This method is called when the search limit is initialized.
Definition: search.cc:4092
void Copy(const SearchLimit *const limit) override
Copy a limit.
Definition: search.cc:4052
RegularLimit * MakeIdenticalClone() const
Definition: search.cc:4065
RelocateAndMakeActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
RelocateAndMakeInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
Relocate(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, int64_t chain_length=1LL, bool single_path=false)
Relocate(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, const std::string &name, std::function< int(int64_t)> start_empty_path_class, int64_t chain_length=1LL, bool single_path=false)
bool OnSamePathAsPreviousBase(int64_t base_index) override
Returns true if a base node has to be on the same path as the "previous" base node (base node of inde...
std::string DebugString() const override
virtual void Install()
Registers itself on the solver such that it gets notified of the search and propagation events.
This class is used to manage a pool of solutions.
virtual bool SyncNeeded(Assignment *const local_assignment)=0
This method checks if the local solution needs to be updated with an external one.
virtual void RegisterNewSolution(Assignment *const assignment)=0
This method is called when a new solution has been accepted by the local search.
virtual void GetNextSolution(Assignment *const assignment)=0
This method is called when the local search starts a new neighborhood to initialize the default assig...
virtual void Initialize(Assignment *const assignment)=0
This method is called to initialize the solution pool with the assignment from the local search.
DecisionBuilder * MakeProfiledDecisionBuilderWrapper(DecisionBuilder *db)
Activates profiling on a decision builder.
ABSL_MUST_USE_RESULT RegularLimit * MakeSolutionsLimit(int64_t solutions)
Creates a search limit that constrains the number of solutions found during the search.
Definition: search.cc:4194
LocalSearchFilter * MakeVariableDomainFilter()
bool SolveAndCommit(DecisionBuilder *const db, const std::vector< SearchMonitor * > &monitors)
SolveAndCommit using a decision builder and up to three search monitors, usually one for the objectiv...
LocalSearchOperator * MakeMoveTowardTargetOperator(const Assignment &target)
Creates a local search operator that tries to move the assignment of some variables toward a target.
LocalSearchStatistics GetLocalSearchStatistics() const
Returns detailed local search statistics.
ConstraintSolverParameters parameters() const
Stored Parameters.
std::function< int64_t(int64_t, int64_t, int64_t)> IndexEvaluator3
void SetSearchContext(Search *search, const std::string &search_context)
void TopPeriodicCheck()
Performs PeriodicCheck on the top-level search; for instance, can be called from a nested solve to ch...
std::function< int64_t(int64_t, int64_t)> IndexEvaluator2
LocalSearchOperator * ConcatenateOperators(const std::vector< LocalSearchOperator * > &ops)
Creates a local search operator which concatenates a vector of operators.
LocalSearchFilter * MakeRejectFilter()
LocalSearchFilter * MakeAcceptFilter()
Local Search Filters.
LocalSearchOperator * MakeRandomLnsOperator(const std::vector< IntVar * > &vars, int number_of_variables)
Creates a large neighborhood search operator which creates fragments (set of relaxed variables) with ...
LocalSearchOperator * RandomConcatenateOperators(const std::vector< LocalSearchOperator * > &ops)
Randomized version of local search concatenator; calls a random operator at each call to MakeNextNeig...
LocalSearchOperators
This enum is used in Solver::MakeOperator to specify the neighborhood to create.
@ EXCHANGE
Operator which exchanges the positions of two nodes.
@ MAKEINACTIVE
Operator which makes path nodes inactive.
@ RELOCATE
Relocate neighborhood with length of 1 (see OROPT comment).
@ SWAPACTIVE
Operator which replaces an active node by an inactive one.
@ SIMPLELNS
Operator which defines one neighbor per variable.
@ INCREMENT
Operator which defines one neighbor per variable.
@ MAKECHAININACTIVE
Operator which makes a "chain" of path nodes inactive.
@ TWOOPT
Operator which reverses a sub-chain of a path.
@ FULLPATHLNS
Operator which relaxes one entire path and all inactive nodes, thus defining num_paths neighbors.
@ EXTENDEDSWAPACTIVE
Operator which makes an inactive node active and an active one inactive.
@ OROPT
Relocate: OROPT and RELOCATE.
@ PATHLNS
Operator which relaxes two sub-chains of three consecutive arcs each.
@ UNACTIVELNS
Operator which relaxes all inactive nodes and one sub-chain of six consecutive arcs.
@ MAKEACTIVE
Operator which inserts an inactive node into a path.
@ DECREMENT
Operator which defines a neighborhood to decrement values.
@ CROSS
Operator which cross exchanges the starting chains of 2 paths, including exchanging the whole paths.
LocalSearchPhaseParameters * MakeLocalSearchPhaseParameters(IntVar *objective, LocalSearchOperator *const ls_operator, DecisionBuilder *const sub_decision_builder)
Local Search Phase Parameters.
bool IsLocalSearchProfilingEnabled() const
Returns whether we are profiling local search.
IntVarLocalSearchFilter * MakeSumObjectiveFilter(const std::vector< IntVar * > &vars, IndexEvaluator2 values, Solver::LocalSearchFilterBound filter_enum)
Search * ActiveSearch() const
Returns the active search, nullptr outside search.
LocalSearchOperator * MakeNeighborhoodLimit(LocalSearchOperator *const op, int64_t limit)
Creates a local search operator that wraps another local search operator and limits the number of nei...
LocalSearchMonitor * GetLocalSearchMonitor() const
Returns the local search monitor.
SolutionPool * MakeDefaultSolutionPool()
Solution Pool.
bool UseFastLocalSearch() const
Returns true if fast local search is enabled.
LocalSearchOperator * MakeOperator(const std::vector< IntVar * > &vars, LocalSearchOperators op)
Local Search Operators.
std::string LocalSearchProfile() const
Returns local search profiling information in a human readable format.
Solver(const std::string &name)
Solver API.
DecisionBuilder * MakeLocalSearchPhase(Assignment *const assignment, LocalSearchPhaseParameters *const parameters)
Local Search decision builders factories.
LocalSearchOperator * MultiArmedBanditConcatenateOperators(const std::vector< LocalSearchOperator * > &ops, double memory_coefficient, double exploration_coefficient, bool maximize)
Creates a local search operator which concatenates a vector of operators.
Assignment * MakeAssignment()
This method creates an empty assignment.
DecisionBuilder * Compose(DecisionBuilder *const db1, DecisionBuilder *const db2)
Creates a decision builder which sequentially composes decision builders.
Definition: search.cc:556
DecisionBuilder * MakeStoreAssignment(Assignment *assignment)
Returns a DecisionBuilder which stores an Assignment (calls void Assignment::Store())
DecisionBuilder * MakeRestoreAssignment(Assignment *assignment)
Returns a DecisionBuilder which restores an Assignment (calls void Assignment::Restore())
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
T * RevAlloc(T *object)
Registers the given object as being reversible.
EvaluatorLocalSearchOperators
This enum is used in Solver::MakeOperator associated with an evaluator to specify the neighborhood to...
@ TSPOPT
Sliding TSP operator.
@ LK
Lin-Kernighan local search.
LocalSearchFilterBound
This enum is used in Solver::MakeLocalSearchObjectiveFilter.
@ GE
Move is accepted when the current objective value >= objective.Min.
@ LE
Move is accepted when the current objective value <= objective.Max.
@ EQ
Move is accepted when the current objective value is in the interval objective.Min .
SwapActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::string DebugString() const override
TSPLns(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int tsp_size)
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
std::string DebugString() const override
TSPOpt(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int chain_length)
std::string DebugString() const override
int64_t GetBaseNodeRestartPosition(int base_index) override
Returns the index of the node to which the base node of index base_index must be set to when it reach...
bool MakeNeighbor() override
bool IsIncremental() const override
TwoOpt(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
bool OnSamePathAsPreviousBase(int64_t base_index) override
Returns true if a base node has to be on the same path as the "previous" base node (base node of inde...
std::string DebugString() const override
UnaryDimensionChecker(const PathState *path_state, std::vector< Interval > path_capacity, std::vector< int > path_class, std::vector< std::vector< Interval > > demand, std::vector< Interval > node_capacity)
const int64_t & Value(int64_t index) const
Returns the value in the current assignment of the variable of given index.
IntVar * Var(int64_t index) const
Returns the variable of given index.
Block * next
SatParameters parameters
SharedBoundsManager * bounds
const std::string name
int64_t value
IntVar * var
Definition: expr_array.cc:1874
const int64_t limit_
int64_t *const synchronized_costs_
ABSL_FLAG(int, cp_local_search_sync_frequency, 16, "Frequency of checks for better solutions in the solution pool.")
Solver::LocalSearchFilterBound filter_enum_
int64_t synchronized_sum_
int64_t delta_sum_
int64_t *const delta_costs_
const int primary_vars_size_
#define MAKE_LOCAL_SEARCH_OPERATOR(OperatorClass)
bool incremental_
const int FATAL
Definition: log_severity.h:32
RowIndex row
Definition: markowitz.cc:182
Definition: cleanup.h:22
ReverseView< Container > reversed_view(const Container &c)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:206
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:262
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
LocalSearchOperator * MakeLocalSearchOperator(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
Operator Factories.
void InstallLocalSearchProfiler(LocalSearchProfiler *monitor)
LocalSearchFilter * MakeUnaryDimensionFilter(Solver *solver, std::unique_ptr< UnaryDimensionChecker > checker, const std::string &dimension_name)
int64_t CapSub(int64_t x, int64_t y)
void DeleteLocalSearchProfiler(LocalSearchProfiler *monitor)
bool AcceptDelta(Search *const search, Assignment *delta, Assignment *deltadelta)
void AcceptNeighbor(Search *const search)
int MostSignificantBitPosition32(uint32_t n)
Definition: bitset.h:273
LocalSearchFilter * MakePathStateFilter(Solver *solver, std::unique_ptr< PathState > path_state, const std::vector< IntVar * > &nexts)
bool LocalOptimumReached(Search *const search)
void AcceptUncheckedNeighbor(Search *const search)
LocalSearchProfiler * BuildLocalSearchProfiler(Solver *solver)
STL namespace.
int index
Definition: pack.cc:509
int64_t demand
Definition: resource.cc:125
int64_t delta
Definition: resource.cc:1692
int64_t cost
int nodes
const bool maximize_
Definition: search.cc:2559
IntVar *const objective_
Definition: search.cc:3017
std::function< int64_t(int64_t, int64_t)> evaluator_
Definition: search.cc:1368
std::optional< int64_t > end
int64_t start
Set of parameters used to configure how the neighnorhood is traversed.
bool accept_path_end_base
True if path ends should be considered when iterating over neighbors.
int number_of_base_nodes
Number of nodes needed to define a neighbor.
std::function< int(int64_t)> start_empty_path_class
Callback returning an index such that if c1 = start_empty_path_class(StartNode(p1)),...
bool skip_locally_optimal_paths
Skip paths which have been proven locally optimal.