OR-Tools  9.3
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 // This definition uses the UCB1 exploration bonus for unstructured
2167 // multi-armed bandits.
2168 const double exploration_coefficient_;
2169};
2170
2171MultiArmedBanditCompoundOperator::MultiArmedBanditCompoundOperator(
2172 std::vector<LocalSearchOperator*> operators, double memory_coefficient,
2173 double exploration_coefficient, bool maximize)
2174 : index_(0),
2175 operators_(std::move(operators)),
2176 started_(operators_.size()),
2177 start_assignment_(nullptr),
2178 has_fragments_(false),
2179 last_objective_(std::numeric_limits<int64_t>::max()),
2180 num_neighbors_(0),
2181 maximize_(maximize),
2182 memory_coefficient_(memory_coefficient),
2183 exploration_coefficient_(exploration_coefficient) {
2184 DCHECK_GE(memory_coefficient_, 0);
2185 DCHECK_LE(memory_coefficient_, 1);
2186 DCHECK_GE(exploration_coefficient_, 0);
2187 operators_.erase(std::remove(operators_.begin(), operators_.end(), nullptr),
2188 operators_.end());
2189 operator_indices_.resize(operators_.size());
2190 std::iota(operator_indices_.begin(), operator_indices_.end(), 0);
2191 num_neighbors_per_operator_.resize(operators_.size(), 0);
2192 avg_improvement_.resize(operators_.size(), 0);
2193 for (LocalSearchOperator* const op : operators_) {
2194 if (op->HasFragments()) {
2195 has_fragments_ = true;
2196 break;
2197 }
2198 }
2199}
2200
2201void MultiArmedBanditCompoundOperator::Reset() {
2202 for (LocalSearchOperator* const op : operators_) {
2203 op->Reset();
2204 }
2205}
2206
2207double MultiArmedBanditCompoundOperator::Score(int index) {
2208 return avg_improvement_[index] +
2209 exploration_coefficient_ *
2210 sqrt(2 * log(1 + num_neighbors_) /
2211 (1 + num_neighbors_per_operator_[index]));
2212}
2213
2214void MultiArmedBanditCompoundOperator::Start(const Assignment* assignment) {
2215 start_assignment_ = assignment;
2216 started_.ClearAll();
2217 if (operators_.empty()) return;
2218
2219 const double objective = assignment->ObjectiveValue();
2220
2221 if (objective == last_objective_) return;
2222 // Skip a neighbor evaluation if last_objective_ hasn't been set yet.
2223 if (last_objective_ == std::numeric_limits<int64_t>::max()) {
2224 last_objective_ = objective;
2225 return;
2226 }
2227
2228 const double improvement =
2229 maximize_ ? objective - last_objective_ : last_objective_ - objective;
2230 if (improvement < 0) {
2231 return;
2232 }
2233 last_objective_ = objective;
2234 avg_improvement_[operator_indices_[index_]] +=
2235 memory_coefficient_ *
2236 (improvement - avg_improvement_[operator_indices_[index_]]);
2237
2238 std::sort(operator_indices_.begin(), operator_indices_.end(),
2239 [this](int lhs, int rhs) {
2240 const double lhs_score = Score(lhs);
2241 const double rhs_score = Score(rhs);
2242 return lhs_score > rhs_score ||
2243 (lhs_score == rhs_score && lhs < rhs);
2244 });
2245
2246 index_ = 0;
2247}
2248
2249bool MultiArmedBanditCompoundOperator::MakeNextNeighbor(
2250 Assignment* delta, Assignment* deltadelta) {
2251 if (operators_.empty()) return false;
2252 do {
2253 const int operator_index = operator_indices_[index_];
2254 if (!started_[operator_index]) {
2255 operators_[operator_index]->Start(start_assignment_);
2256 started_.Set(operator_index);
2257 }
2258 if (!operators_[operator_index]->HoldsDelta()) {
2259 delta->Clear();
2260 }
2261 if (operators_[operator_index]->MakeNextNeighbor(delta, deltadelta)) {
2262 ++num_neighbors_;
2263 ++num_neighbors_per_operator_[operator_index];
2264 return true;
2265 }
2266 ++index_;
2267 delta->Clear();
2268 if (index_ == operators_.size()) {
2269 index_ = 0;
2270 }
2271 } while (index_ != 0);
2272 return false;
2273}
2274} // namespace
2275
2277 const std::vector<LocalSearchOperator*>& ops, double memory_coefficient,
2278 double exploration_coefficient, bool maximize) {
2279 return RevAlloc(new MultiArmedBanditCompoundOperator(
2280 ops, memory_coefficient, exploration_coefficient, maximize));
2281}
2282
2283// ----- Operator factory -----
2284
2285template <class T>
2287 Solver* solver, const std::vector<IntVar*>& vars,
2288 const std::vector<IntVar*>& secondary_vars,
2289 std::function<int(int64_t)> start_empty_path_class) {
2290 return solver->RevAlloc(
2291 new T(vars, secondary_vars, std::move(start_empty_path_class)));
2292}
2293
2294#define MAKE_LOCAL_SEARCH_OPERATOR(OperatorClass) \
2295 template <> \
2296 LocalSearchOperator* MakeLocalSearchOperator<OperatorClass>( \
2297 Solver * solver, const std::vector<IntVar*>& vars, \
2298 const std::vector<IntVar*>& secondary_vars, \
2299 std::function<int(int64_t)> start_empty_path_class) { \
2300 return solver->RevAlloc(new OperatorClass( \
2301 vars, secondary_vars, std::move(start_empty_path_class))); \
2302 }
2303
2308MAKE_LOCAL_SEARCH_OPERATOR(MakeActiveOperator)
2309MAKE_LOCAL_SEARCH_OPERATOR(MakeInactiveOperator)
2310MAKE_LOCAL_SEARCH_OPERATOR(MakeChainInactiveOperator)
2311MAKE_LOCAL_SEARCH_OPERATOR(SwapActiveOperator)
2312MAKE_LOCAL_SEARCH_OPERATOR(ExtendedSwapActiveOperator)
2313MAKE_LOCAL_SEARCH_OPERATOR(MakeActiveAndRelocate)
2314MAKE_LOCAL_SEARCH_OPERATOR(RelocateAndMakeActiveOperator)
2315MAKE_LOCAL_SEARCH_OPERATOR(RelocateAndMakeInactiveOperator)
2316
2317#undef MAKE_LOCAL_SEARCH_OPERATOR
2318
2319LocalSearchOperator* Solver::MakeOperator(const std::vector<IntVar*>& vars,
2321 return MakeOperator(vars, std::vector<IntVar*>(), op);
2322}
2323
2325 const std::vector<IntVar*>& vars,
2326 const std::vector<IntVar*>& secondary_vars,
2328 LocalSearchOperator* result = nullptr;
2329 switch (op) {
2330 case Solver::TWOOPT: {
2331 result = RevAlloc(new TwoOpt(vars, secondary_vars, nullptr));
2332 break;
2333 }
2334 case Solver::OROPT: {
2335 std::vector<LocalSearchOperator*> operators;
2336 for (int i = 1; i < 4; ++i) {
2337 operators.push_back(RevAlloc(
2338 new Relocate(vars, secondary_vars, absl::StrCat("OrOpt<", i, ">"),
2339 nullptr, i, true)));
2340 }
2341 result = ConcatenateOperators(operators);
2342 break;
2343 }
2344 case Solver::RELOCATE: {
2345 result = MakeLocalSearchOperator<Relocate>(this, vars, secondary_vars,
2346 nullptr);
2347 break;
2348 }
2349 case Solver::EXCHANGE: {
2350 result = MakeLocalSearchOperator<Exchange>(this, vars, secondary_vars,
2351 nullptr);
2352 break;
2353 }
2354 case Solver::CROSS: {
2355 result =
2356 MakeLocalSearchOperator<Cross>(this, vars, secondary_vars, nullptr);
2357 break;
2358 }
2359 case Solver::MAKEACTIVE: {
2360 result = MakeLocalSearchOperator<MakeActiveOperator>(
2361 this, vars, secondary_vars, nullptr);
2362 break;
2363 }
2364 case Solver::MAKEINACTIVE: {
2365 result = MakeLocalSearchOperator<MakeInactiveOperator>(
2366 this, vars, secondary_vars, nullptr);
2367 break;
2368 }
2370 result = MakeLocalSearchOperator<MakeChainInactiveOperator>(
2371 this, vars, secondary_vars, nullptr);
2372 break;
2373 }
2374 case Solver::SWAPACTIVE: {
2375 result = MakeLocalSearchOperator<SwapActiveOperator>(
2376 this, vars, secondary_vars, nullptr);
2377 break;
2378 }
2380 result = MakeLocalSearchOperator<ExtendedSwapActiveOperator>(
2381 this, vars, secondary_vars, nullptr);
2382 break;
2383 }
2384 case Solver::PATHLNS: {
2385 result = RevAlloc(new PathLns(vars, secondary_vars, 2, 3, false));
2386 break;
2387 }
2388 case Solver::FULLPATHLNS: {
2389 result = RevAlloc(new PathLns(vars, secondary_vars,
2390 /*number_of_chunks=*/1,
2391 /*chunk_size=*/0,
2392 /*unactive_fragments=*/true));
2393 break;
2394 }
2395 case Solver::UNACTIVELNS: {
2396 result = RevAlloc(new PathLns(vars, secondary_vars, 1, 6, true));
2397 break;
2398 }
2399 case Solver::INCREMENT: {
2400 if (secondary_vars.empty()) {
2401 result = RevAlloc(new IncrementValue(vars));
2402 } else {
2403 LOG(FATAL) << "Operator " << op
2404 << " does not support secondary variables";
2405 }
2406 break;
2407 }
2408 case Solver::DECREMENT: {
2409 if (secondary_vars.empty()) {
2410 result = RevAlloc(new DecrementValue(vars));
2411 } else {
2412 LOG(FATAL) << "Operator " << op
2413 << " does not support secondary variables";
2414 }
2415 break;
2416 }
2417 case Solver::SIMPLELNS: {
2418 if (secondary_vars.empty()) {
2419 result = RevAlloc(new SimpleLns(vars, 1));
2420 } else {
2421 LOG(FATAL) << "Operator " << op
2422 << " does not support secondary variables";
2423 }
2424 break;
2425 }
2426 default:
2427 LOG(FATAL) << "Unknown operator " << op;
2428 }
2429 return result;
2430}
2431
2433 const std::vector<IntVar*>& vars, Solver::IndexEvaluator3 evaluator,
2435 return MakeOperator(vars, std::vector<IntVar*>(), std::move(evaluator), op);
2436}
2437
2439 const std::vector<IntVar*>& vars,
2440 const std::vector<IntVar*>& secondary_vars,
2441 Solver::IndexEvaluator3 evaluator,
2443 LocalSearchOperator* result = nullptr;
2444 switch (op) {
2445 case Solver::LK: {
2446 std::vector<LocalSearchOperator*> operators;
2447 operators.push_back(RevAlloc(
2448 new LinKernighan(vars, secondary_vars, evaluator, /*topt=*/false)));
2449 operators.push_back(RevAlloc(
2450 new LinKernighan(vars, secondary_vars, evaluator, /*topt=*/true)));
2451 result = ConcatenateOperators(operators);
2452 break;
2453 }
2454 case Solver::TSPOPT: {
2455 result = RevAlloc(
2456 new TSPOpt(vars, secondary_vars, evaluator,
2457 absl::GetFlag(FLAGS_cp_local_search_tsp_opt_size)));
2458 break;
2459 }
2460 case Solver::TSPLNS: {
2461 result = RevAlloc(
2462 new TSPLns(vars, secondary_vars, evaluator,
2463 absl::GetFlag(FLAGS_cp_local_search_tsp_lns_size)));
2464 break;
2465 }
2466 default:
2467 LOG(FATAL) << "Unknown operator " << op;
2468 }
2469 return result;
2470}
2471
2472namespace {
2473// Classes for Local Search Operation used in Local Search filters.
2474
2475class SumOperation {
2476 public:
2477 SumOperation() : value_(0) {}
2478 void Init() { value_ = 0; }
2479 void Update(int64_t update) { value_ = CapAdd(value_, update); }
2480 void Remove(int64_t remove) { value_ = CapSub(value_, remove); }
2481 int64_t value() const { return value_; }
2482 void set_value(int64_t new_value) { value_ = new_value; }
2483
2484 private:
2485 int64_t value_;
2486};
2487
2488class ProductOperation {
2489 public:
2490 ProductOperation() : value_(1) {}
2491 void Init() { value_ = 1; }
2492 void Update(int64_t update) { value_ *= update; }
2493 void Remove(int64_t remove) {
2494 if (remove != 0) {
2495 value_ /= remove;
2496 }
2497 }
2498 int64_t value() const { return value_; }
2499 void set_value(int64_t new_value) { value_ = new_value; }
2500
2501 private:
2502 int64_t value_;
2503};
2504
2505class MinOperation {
2506 public:
2507 void Init() { values_set_.clear(); }
2508 void Update(int64_t update) { values_set_.insert(update); }
2509 void Remove(int64_t remove) { values_set_.erase(remove); }
2510 int64_t value() const {
2511 return (!values_set_.empty()) ? *values_set_.begin() : 0;
2512 }
2513 void set_value(int64_t new_value) {}
2514
2515 private:
2516 std::set<int64_t> values_set_;
2517};
2518
2519class MaxOperation {
2520 public:
2521 void Init() { values_set_.clear(); }
2522 void Update(int64_t update) { values_set_.insert(update); }
2523 void Remove(int64_t remove) { values_set_.erase(remove); }
2524 int64_t value() const {
2525 return (!values_set_.empty()) ? *values_set_.rbegin() : 0;
2526 }
2527 void set_value(int64_t new_value) {}
2528
2529 private:
2530 std::set<int64_t> values_set_;
2531};
2532
2533// Always accepts deltas, cost 0.
2534class AcceptFilter : public LocalSearchFilter {
2535 public:
2536 std::string DebugString() const override { return "AcceptFilter"; }
2537 bool Accept(const Assignment* delta, const Assignment* deltadelta,
2538 int64_t obj_min, int64_t obj_max) override {
2539 return true;
2540 }
2541 void Synchronize(const Assignment* assignment,
2542 const Assignment* delta) override {}
2543};
2544} // namespace
2545
2547 return RevAlloc(new AcceptFilter());
2548}
2549
2550namespace {
2551// Never accepts deltas, cost 0.
2552class RejectFilter : public LocalSearchFilter {
2553 public:
2554 std::string DebugString() const override { return "RejectFilter"; }
2555 bool Accept(const Assignment* delta, const Assignment* deltadelta,
2556 int64_t obj_min, int64_t obj_max) override {
2557 return false;
2558 }
2559 void Synchronize(const Assignment* assignment,
2560 const Assignment* delta) override {}
2561};
2562} // namespace
2563
2565 return RevAlloc(new RejectFilter());
2566}
2567
2568PathState::PathState(int num_nodes, std::vector<int> path_start,
2569 std::vector<int> path_end)
2570 : num_nodes_(num_nodes),
2571 num_paths_(path_start.size()),
2572 num_nodes_threshold_(std::max(16, 4 * num_nodes_)) // Arbitrary value.
2573{
2574 DCHECK_EQ(path_start.size(), num_paths_);
2575 DCHECK_EQ(path_end.size(), num_paths_);
2576 for (int p = 0; p < num_paths_; ++p) {
2577 path_start_end_.push_back({path_start[p], path_end[p]});
2578 }
2579 // Initial state is all unperformed: paths go from start to end directly.
2580 committed_index_.assign(num_nodes_, -1);
2581 committed_nodes_.assign(2 * num_paths_, {-1, -1});
2582 chains_.assign(num_paths_ + 1, {-1, -1}); // Reserve 1 more for sentinel.
2583 paths_.assign(num_paths_, {-1, -1});
2584 for (int path = 0; path < num_paths_; ++path) {
2585 const int index = 2 * path;
2586 const PathStartEnd start_end = path_start_end_[path];
2587 committed_index_[start_end.start] = index;
2588 committed_index_[start_end.end] = index + 1;
2589
2590 committed_nodes_[index] = {start_end.start, path};
2591 committed_nodes_[index + 1] = {start_end.end, path};
2592
2593 chains_[path] = {index, index + 2};
2594 paths_[path] = {path, path + 1};
2595 }
2596 chains_[num_paths_] = {0, 0}; // Sentinel.
2597 // Nodes that are not starts or ends are loops.
2598 for (int node = 0; node < num_nodes_; ++node) {
2599 if (committed_index_[node] != -1) continue; // node is start or end.
2600 committed_index_[node] = committed_nodes_.size();
2601 committed_nodes_.push_back({node, -1});
2602 }
2603}
2604
2606 const PathBounds bounds = paths_[path];
2607 return PathState::ChainRange(chains_.data() + bounds.begin_index,
2608 chains_.data() + bounds.end_index,
2609 committed_nodes_.data());
2610}
2611
2613 const PathBounds bounds = paths_[path];
2614 return PathState::NodeRange(chains_.data() + bounds.begin_index,
2615 chains_.data() + bounds.end_index,
2616 committed_nodes_.data());
2617}
2618
2619void PathState::ChangePath(int path, const std::vector<ChainBounds>& chains) {
2620 changed_paths_.push_back(path);
2621 const int path_begin_index = chains_.size();
2622 chains_.insert(chains_.end(), chains.begin(), chains.end());
2623 const int path_end_index = chains_.size();
2624 paths_[path] = {path_begin_index, path_end_index};
2625 chains_.emplace_back(0, 0); // Sentinel.
2626}
2627
2628void PathState::ChangeLoops(const std::vector<int>& new_loops) {
2629 for (const int loop : new_loops) {
2630 if (Path(loop) == -1) continue;
2631 changed_loops_.push_back(loop);
2632 }
2633}
2634
2636 DCHECK(!IsInvalid());
2637 if (committed_nodes_.size() < num_nodes_threshold_) {
2638 IncrementalCommit();
2639 } else {
2640 FullCommit();
2641 }
2642}
2643
2645 is_invalid_ = false;
2646 chains_.resize(num_paths_ + 1); // One per path + sentinel.
2647 for (const int path : changed_paths_) {
2648 paths_[path] = {path, path + 1};
2649 }
2650 changed_paths_.clear();
2651 changed_loops_.clear();
2652}
2653
2654void PathState::CopyNewPathAtEndOfNodes(int path) {
2655 // Copy path's nodes, chain by chain.
2656 const int new_path_begin_index = committed_nodes_.size();
2657 const PathBounds path_bounds = paths_[path];
2658 for (int i = path_bounds.begin_index; i < path_bounds.end_index; ++i) {
2659 const ChainBounds chain_bounds = chains_[i];
2660 committed_nodes_.insert(committed_nodes_.end(),
2661 committed_nodes_.data() + chain_bounds.begin_index,
2662 committed_nodes_.data() + chain_bounds.end_index);
2663 }
2664 const int new_path_end_index = committed_nodes_.size();
2665 // Set new nodes' path member to path.
2666 for (int i = new_path_begin_index; i < new_path_end_index; ++i) {
2667 committed_nodes_[i].path = path;
2668 }
2669}
2670
2671// TODO(user): Instead of copying paths at the end systematically,
2672// reuse some of the memory when possible.
2673void PathState::IncrementalCommit() {
2674 const int new_nodes_begin = committed_nodes_.size();
2675 for (const int path : ChangedPaths()) {
2676 const int chain_begin = committed_nodes_.size();
2677 CopyNewPathAtEndOfNodes(path);
2678 const int chain_end = committed_nodes_.size();
2679 chains_[path] = {chain_begin, chain_end};
2680 }
2681 // Re-index all copied nodes.
2682 const int new_nodes_end = committed_nodes_.size();
2683 for (int i = new_nodes_begin; i < new_nodes_end; ++i) {
2684 committed_index_[committed_nodes_[i].node] = i;
2685 }
2686 // New loops stay in place: only change their path to -1,
2687 // committed_index_ does not change.
2688 for (const int loop : ChangedLoops()) {
2689 const int index = committed_index_[loop];
2690 committed_nodes_[index].path = -1;
2691 }
2692 // Committed part of the state is set up, erase incremental changes.
2693 Revert();
2694}
2695
2696void PathState::FullCommit() {
2697 // Copy all paths at the end of committed_nodes_,
2698 // then remove all old committed_nodes_.
2699 const int old_num_nodes = committed_nodes_.size();
2700 for (int path = 0; path < num_paths_; ++path) {
2701 const int new_path_begin = committed_nodes_.size() - old_num_nodes;
2702 CopyNewPathAtEndOfNodes(path);
2703 const int new_path_end = committed_nodes_.size() - old_num_nodes;
2704 chains_[path] = {new_path_begin, new_path_end};
2705 }
2706 committed_nodes_.erase(committed_nodes_.begin(),
2707 committed_nodes_.begin() + old_num_nodes);
2708
2709 // Reindex path nodes, then loop nodes.
2710 constexpr int kUnindexed = -1;
2711 committed_index_.assign(num_nodes_, kUnindexed);
2712 int index = 0;
2713 for (const CommittedNode committed_node : committed_nodes_) {
2714 committed_index_[committed_node.node] = index++;
2715 }
2716 for (int node = 0; node < num_nodes_; ++node) {
2717 if (committed_index_[node] != kUnindexed) continue;
2718 committed_index_[node] = index++;
2719 committed_nodes_.push_back({node, -1});
2720 }
2721 // Committed part of the state is set up, erase incremental changes.
2722 Revert();
2723}
2724
2725namespace {
2726
2727class PathStateFilter : public LocalSearchFilter {
2728 public:
2729 std::string DebugString() const override { return "PathStateFilter"; }
2730 PathStateFilter(std::unique_ptr<PathState> path_state,
2731 const std::vector<IntVar*>& nexts);
2732 void Relax(const Assignment* delta, const Assignment* deltadelta) override;
2733 bool Accept(const Assignment* delta, const Assignment* deltadelta,
2734 int64_t objective_min, int64_t objective_max) override {
2735 return true;
2736 }
2737 void Synchronize(const Assignment* delta,
2738 const Assignment* deltadelta) override{};
2739 void Commit(const Assignment* assignment, const Assignment* delta) override;
2740 void Revert() override;
2741 void Reset() override;
2742
2743 private:
2744 // Used in arc to chain translation, see below.
2745 struct TailHeadIndices {
2748 };
2749 struct IndexArc {
2751 int arc;
2752 bool operator<(const IndexArc& other) const { return index < other.index; }
2753 };
2754
2755 // Translate changed_arcs_ to chains, pass to underlying PathState.
2756 void CutChains();
2757 // From changed_paths_ and changed_arcs_, fill chains_ and paths_.
2758 // Selection-based algorithm in O(n^2), to use for small change sets.
2759 void MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
2760 // From changed_paths_ and changed_arcs_, fill chains_ and paths_.
2761 // Generic algorithm in O(std::sort(n)+n), to use for larger change sets.
2762 void MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
2763
2764 const std::unique_ptr<PathState> path_state_;
2765 // Map IntVar* index to node, offset by the min index in nexts.
2766 std::vector<int> variable_index_to_node_;
2767 int index_offset_;
2768 // Used only in Reset(), class member status avoids reallocations.
2769 std::vector<bool> node_is_assigned_;
2770 std::vector<int> loops_;
2771
2772 // Used in CutChains(), class member status avoids reallocations.
2773 std::vector<int> changed_paths_;
2774 std::vector<bool> path_has_changed_;
2775 std::vector<std::pair<int, int>> changed_arcs_;
2776 std::vector<int> changed_loops_;
2777 std::vector<TailHeadIndices> tail_head_indices_;
2778 std::vector<IndexArc> arcs_by_tail_index_;
2779 std::vector<IndexArc> arcs_by_head_index_;
2780 std::vector<int> next_arc_;
2781 std::vector<PathState::ChainBounds> path_chains_;
2782};
2783
2784PathStateFilter::PathStateFilter(std::unique_ptr<PathState> path_state,
2785 const std::vector<IntVar*>& nexts)
2786 : path_state_(std::move(path_state)) {
2787 {
2788 int min_index = std::numeric_limits<int>::max();
2789 int max_index = std::numeric_limits<int>::min();
2790 for (const IntVar* next : nexts) {
2791 const int index = next->index();
2792 min_index = std::min<int>(min_index, index);
2793 max_index = std::max<int>(max_index, index);
2794 }
2795 variable_index_to_node_.resize(max_index - min_index + 1, -1);
2796 index_offset_ = min_index;
2797 }
2798
2799 for (int node = 0; node < nexts.size(); ++node) {
2800 const int index = nexts[node]->index() - index_offset_;
2801 variable_index_to_node_[index] = node;
2802 }
2803 path_has_changed_.assign(path_state_->NumPaths(), false);
2804}
2805
2806void PathStateFilter::Relax(const Assignment* delta,
2807 const Assignment* deltadelta) {
2808 path_state_->Revert();
2809 changed_arcs_.clear();
2810 for (const IntVarElement& var_value : delta->IntVarContainer().elements()) {
2811 if (var_value.Var() == nullptr) continue;
2812 const int index = var_value.Var()->index() - index_offset_;
2813 if (index < 0 || variable_index_to_node_.size() <= index) continue;
2814 const int node = variable_index_to_node_[index];
2815 if (node == -1) continue;
2816 if (var_value.Bound()) {
2817 changed_arcs_.emplace_back(node, var_value.Value());
2818 } else {
2819 path_state_->Revert();
2820 path_state_->SetInvalid();
2821 return;
2822 }
2823 }
2824 CutChains();
2825}
2826
2827void PathStateFilter::Reset() {
2828 path_state_->Revert();
2829 // Set all paths of path state to empty start -> end paths,
2830 // and all nonstart/nonend nodes to node -> node loops.
2831 const int num_nodes = path_state_->NumNodes();
2832 node_is_assigned_.assign(num_nodes, false);
2833 loops_.clear();
2834 const int num_paths = path_state_->NumPaths();
2835 for (int path = 0; path < num_paths; ++path) {
2836 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
2837 path_state_->ChangePath(
2838 path, {{start_index, start_index + 1}, {end_index - 1, end_index}});
2839 node_is_assigned_[path_state_->Start(path)] = true;
2840 node_is_assigned_[path_state_->End(path)] = true;
2841 }
2842 for (int node = 0; node < num_nodes; ++node) {
2843 if (!node_is_assigned_[node]) loops_.push_back(node);
2844 }
2845 path_state_->ChangeLoops(loops_);
2846 path_state_->Commit();
2847}
2848
2849// The solver does not guarantee that a given Commit() corresponds to
2850// the previous Relax() (or that there has been a call to Relax()),
2851// so we replay the full change call sequence.
2852void PathStateFilter::Commit(const Assignment* assignment,
2853 const Assignment* delta) {
2854 path_state_->Revert();
2855 if (delta == nullptr || delta->Empty()) {
2856 Relax(assignment, nullptr);
2857 } else {
2858 Relax(delta, nullptr);
2859 }
2860 path_state_->Commit();
2861}
2862
2863void PathStateFilter::Revert() { path_state_->Revert(); }
2864
2865void PathStateFilter::CutChains() {
2866 // Filter out unchanged arcs from changed_arcs_,
2867 // translate changed arcs to changed arc indices.
2868 // Fill changed_paths_ while we hold node_path.
2869 for (const int path : changed_paths_) path_has_changed_[path] = false;
2870 changed_paths_.clear();
2871 tail_head_indices_.clear();
2872 changed_loops_.clear();
2873 int num_changed_arcs = 0;
2874 for (const auto [node, next] : changed_arcs_) {
2875 const int node_index = path_state_->CommittedIndex(node);
2876 const int next_index = path_state_->CommittedIndex(next);
2877 const int node_path = path_state_->Path(node);
2878 if (next != node &&
2879 (next_index != node_index + 1 || node_path == -1)) { // New arc.
2880 tail_head_indices_.push_back({node_index, next_index});
2881 changed_arcs_[num_changed_arcs++] = {node, next};
2882 if (node_path != -1 && !path_has_changed_[node_path]) {
2883 path_has_changed_[node_path] = true;
2884 changed_paths_.push_back(node_path);
2885 }
2886 } else if (node == next && node_path != -1) { // New loop.
2887 changed_loops_.push_back(node);
2888 }
2889 }
2890 changed_arcs_.resize(num_changed_arcs);
2891
2892 path_state_->ChangeLoops(changed_loops_);
2893 if (tail_head_indices_.size() + changed_paths_.size() <= 8) {
2894 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
2895 } else {
2896 MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
2897 }
2898}
2899
2900void PathStateFilter::
2901 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm() {
2902 int num_visited_changed_arcs = 0;
2903 const int num_changed_arcs = tail_head_indices_.size();
2904 // For every path, find all its chains.
2905 for (const int path : changed_paths_) {
2906 path_chains_.clear();
2907 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
2908 int current_index = start_index;
2909 while (true) {
2910 // Look for smallest non-visited tail_index that is no smaller than
2911 // current_index.
2912 int selected_arc = -1;
2913 int selected_tail_index = std::numeric_limits<int>::max();
2914 for (int i = num_visited_changed_arcs; i < num_changed_arcs; ++i) {
2915 const int tail_index = tail_head_indices_[i].tail_index;
2916 if (current_index <= tail_index && tail_index < selected_tail_index) {
2917 selected_arc = i;
2918 selected_tail_index = tail_index;
2919 }
2920 }
2921 // If there is no such tail index, or more generally if the next chain
2922 // would be cut by end of path,
2923 // stack {current_index, end_index + 1} in chains_, and go to next path.
2924 // Otherwise, stack {current_index, tail_index+1} in chains_,
2925 // set current_index = head_index, set pair to visited.
2926 if (start_index <= current_index && current_index < end_index &&
2927 end_index <= selected_tail_index) {
2928 path_chains_.emplace_back(current_index, end_index);
2929 break;
2930 } else {
2931 path_chains_.emplace_back(current_index, selected_tail_index + 1);
2932 current_index = tail_head_indices_[selected_arc].head_index;
2933 std::swap(tail_head_indices_[num_visited_changed_arcs],
2934 tail_head_indices_[selected_arc]);
2935 ++num_visited_changed_arcs;
2936 }
2937 }
2938 path_state_->ChangePath(path, path_chains_);
2939 }
2940}
2941
2942void PathStateFilter::MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm() {
2943 // TRICKY: For each changed path, we want to generate a sequence of chains
2944 // that represents the path in the changed state.
2945 // First, notice that if we add a fake end->start arc for each changed path,
2946 // then all chains will be from the head of an arc to the tail of an arc.
2947 // A way to generate the changed chains and paths would be, for each path,
2948 // to start from a fake arc's head (the path start), go down the path until
2949 // the tail of an arc, and go to the next arc until we return on the fake arc,
2950 // enqueuing the [head, tail] chains as we go.
2951 // In turn, to do that, we need to know which arc to go to.
2952 // If we sort all heads and tails by index in two separate arrays,
2953 // the head_index and tail_index at the same rank are such that
2954 // [head_index, tail_index] is a chain. Moreover, the arc that must be visited
2955 // after head_index's arc is tail_index's arc.
2956
2957 // Add a fake end->start arc for each path.
2958 for (const int path : changed_paths_) {
2959 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
2960 tail_head_indices_.push_back({end_index - 1, start_index});
2961 }
2962
2963 // Generate pairs (tail_index, arc) and (head_index, arc) for all arcs,
2964 // sort those pairs by index.
2965 const int num_arc_indices = tail_head_indices_.size();
2966 arcs_by_tail_index_.resize(num_arc_indices);
2967 arcs_by_head_index_.resize(num_arc_indices);
2968 for (int i = 0; i < num_arc_indices; ++i) {
2969 arcs_by_tail_index_[i] = {tail_head_indices_[i].tail_index, i};
2970 arcs_by_head_index_[i] = {tail_head_indices_[i].head_index, i};
2971 }
2972 std::sort(arcs_by_tail_index_.begin(), arcs_by_tail_index_.end());
2973 std::sort(arcs_by_head_index_.begin(), arcs_by_head_index_.end());
2974 // Generate the map from arc to next arc in path.
2975 next_arc_.resize(num_arc_indices);
2976 for (int i = 0; i < num_arc_indices; ++i) {
2977 next_arc_[arcs_by_head_index_[i].arc] = arcs_by_tail_index_[i].arc;
2978 }
2979
2980 // Generate chains: for every changed path, start from its fake arc,
2981 // jump to next_arc_ until going back to fake arc,
2982 // enqueuing chains as we go.
2983 const int first_fake_arc = num_arc_indices - changed_paths_.size();
2984 for (int fake_arc = first_fake_arc; fake_arc < num_arc_indices; ++fake_arc) {
2985 path_chains_.clear();
2986 int32_t arc = fake_arc;
2987 do {
2988 const int chain_begin = tail_head_indices_[arc].head_index;
2989 arc = next_arc_[arc];
2990 const int chain_end = tail_head_indices_[arc].tail_index + 1;
2991 path_chains_.emplace_back(chain_begin, chain_end);
2992 } while (arc != fake_arc);
2993 const int path = changed_paths_[fake_arc - first_fake_arc];
2994 path_state_->ChangePath(path, path_chains_);
2995 }
2996}
2997
2998} // namespace
2999
3001 std::unique_ptr<PathState> path_state,
3002 const std::vector<IntVar*>& nexts) {
3003 PathStateFilter* filter = new PathStateFilter(std::move(path_state), nexts);
3004 return solver->RevAlloc(filter);
3005}
3006
3007UnaryDimensionChecker::UnaryDimensionChecker(
3008 const PathState* path_state, std::vector<Interval> path_capacity,
3009 std::vector<int> path_class,
3010 std::vector<std::function<Interval(int64_t)>> min_max_demand_per_path_class,
3011 std::vector<Interval> node_capacity)
3012 : path_state_(path_state),
3013 path_capacity_(std::move(path_capacity)),
3014 path_class_(std::move(path_class)),
3015 min_max_demand_per_path_class_(std::move(min_max_demand_per_path_class)),
3016 node_capacity_(std::move(node_capacity)),
3017 index_(path_state_->NumNodes(), 0),
3018 maximum_partial_demand_layer_size_(
3019 std::max(16, 4 * path_state_->NumNodes())) // 16 and 4 are arbitrary.
3020{
3021 const int num_nodes = path_state_->NumNodes();
3022 cached_demand_.resize(num_nodes);
3023 const int num_paths = path_state_->NumPaths();
3024 DCHECK_EQ(num_paths, path_capacity_.size());
3025 DCHECK_EQ(num_paths, path_class_.size());
3026 const int maximum_rmq_exponent = MostSignificantBitPosition32(num_nodes);
3027 partial_demand_sums_rmq_.resize(maximum_rmq_exponent + 1);
3028 previous_nontrivial_index_.reserve(maximum_partial_demand_layer_size_);
3029 FullCommit();
3030}
3031
3033 if (path_state_->IsInvalid()) return true;
3034 for (const int path : path_state_->ChangedPaths()) {
3035 const Interval path_capacity = path_capacity_[path];
3036 if (path_capacity.min == std::numeric_limits<int64_t>::min() &&
3037 path_capacity.max == std::numeric_limits<int64_t>::max()) {
3038 continue;
3039 }
3040 const int path_class = path_class_[path];
3041 // Loop invariant: capacity_used is nonempty and within path_capacity.
3042 Interval capacity_used = node_capacity_[path_state_->Start(path)];
3043 capacity_used = {std::max(capacity_used.min, path_capacity.min),
3044 std::min(capacity_used.max, path_capacity.max)};
3045 if (capacity_used.min > capacity_used.max) return false;
3046
3047 for (const auto chain : path_state_->Chains(path)) {
3048 const int first_node = chain.First();
3049 const int last_node = chain.Last();
3050
3051 const int first_index = index_[first_node];
3052 const int last_index = index_[last_node];
3053
3054 const int chain_path = path_state_->Path(first_node);
3055 const int chain_path_class =
3056 chain_path == -1 ? -1 : path_class_[chain_path];
3057 // Call the RMQ if the chain size is large enough;
3058 // the optimal value was found with the associated benchmark in tests,
3059 // in particular BM_UnaryDimensionChecker<ChangeSparsity::kSparse, *>.
3060 constexpr int kMinRangeSizeForRMQ = 4;
3061 const bool chain_is_cached = chain_path_class == path_class;
3062 if (last_index - first_index > kMinRangeSizeForRMQ && chain_is_cached &&
3063 SubpathOnlyHasTrivialNodes(first_index, last_index)) {
3064 // Compute feasible values of capacity_used that will not violate
3065 // path_capacity. This is done by considering the worst cases
3066 // using a range min/max query.
3067 const Interval min_max =
3068 GetMinMaxPartialDemandSum(first_index, last_index);
3069 const Interval prev_sum = partial_demand_sums_rmq_[0][first_index - 1];
3070 const Interval min_max_delta = {CapSub(min_max.min, prev_sum.min),
3071 CapSub(min_max.max, prev_sum.max)};
3072 capacity_used = {
3073 std::max(capacity_used.min,
3074 CapSub(path_capacity.min, min_max_delta.min)),
3075 std::min(capacity_used.max,
3076 CapSub(path_capacity.max, min_max_delta.max))};
3077 if (capacity_used.min > capacity_used.max) return false;
3078 // Move to last node's state, which is valid since we did not return.
3079 const Interval last_sum = partial_demand_sums_rmq_[0][last_index];
3080 capacity_used = {
3081 CapSub(CapAdd(capacity_used.min, last_sum.min), prev_sum.min),
3082 CapSub(CapAdd(capacity_used.max, last_sum.max), prev_sum.max)};
3083 } else {
3084 for (const int node : chain) {
3085 const Interval node_capacity = node_capacity_[node];
3086 capacity_used = {std::max(capacity_used.min, node_capacity.min),
3087 std::min(capacity_used.max, node_capacity.max)};
3088 if (capacity_used.min > capacity_used.max) return false;
3089 const Interval demand =
3090 chain_is_cached
3091 ? cached_demand_[node]
3092 : min_max_demand_per_path_class_[path_class](node);
3093 capacity_used = {CapAdd(capacity_used.min, demand.min),
3094 CapAdd(capacity_used.max, demand.max)};
3095 capacity_used = {std::max(capacity_used.min, path_capacity.min),
3096 std::min(capacity_used.max, path_capacity.max)};
3097 }
3098 }
3099 }
3100 if (std::max(capacity_used.min, path_capacity.min) >
3101 std::min(capacity_used.max, path_capacity.max)) {
3102 return false;
3103 }
3104 }
3105 return true;
3106}
3107
3109 const int current_layer_size = partial_demand_sums_rmq_[0].size();
3110 int change_size = path_state_->ChangedPaths().size();
3111 for (const int path : path_state_->ChangedPaths()) {
3112 for (const auto chain : path_state_->Chains(path)) {
3113 change_size += chain.NumNodes();
3114 }
3115 }
3116 if (current_layer_size + change_size <= maximum_partial_demand_layer_size_) {
3117 IncrementalCommit();
3118 } else {
3119 FullCommit();
3120 }
3121}
3122
3123void UnaryDimensionChecker::IncrementalCommit() {
3124 for (const int path : path_state_->ChangedPaths()) {
3125 const int begin_index = partial_demand_sums_rmq_[0].size();
3126 AppendPathDemandsToSums(path);
3127 UpdateRMQStructure(begin_index, partial_demand_sums_rmq_[0].size());
3128 }
3129}
3130
3131void UnaryDimensionChecker::FullCommit() {
3132 // Clear all structures.
3133 previous_nontrivial_index_.clear();
3134 for (auto& sums : partial_demand_sums_rmq_) sums.clear();
3135 // Append all paths.
3136 const int num_paths = path_state_->NumPaths();
3137 for (int path = 0; path < num_paths; ++path) {
3138 const int begin_index = partial_demand_sums_rmq_[0].size();
3139 AppendPathDemandsToSums(path);
3140 UpdateRMQStructure(begin_index, partial_demand_sums_rmq_[0].size());
3141 }
3142}
3143
3144void UnaryDimensionChecker::AppendPathDemandsToSums(int path) {
3145 const int path_class = path_class_[path];
3146 Interval demand_sum = {0, 0};
3147 int previous_nontrivial_index = -1;
3148 int index = partial_demand_sums_rmq_[0].size();
3149 // Value of partial_demand_sums_rmq_ at node_index-1 must be the sum
3150 // of all demands of nodes before node.
3151 partial_demand_sums_rmq_[0].push_back(demand_sum);
3152 previous_nontrivial_index_.push_back(-1);
3153 ++index;
3154
3155 for (const int node : path_state_->Nodes(path)) {
3156 index_[node] = index;
3157 const Interval demand = min_max_demand_per_path_class_[path_class](node);
3158 cached_demand_[node] = demand;
3159 demand_sum = {CapAdd(demand_sum.min, demand.min),
3160 CapAdd(demand_sum.max, demand.max)};
3161 partial_demand_sums_rmq_[0].push_back(demand_sum);
3162
3163 const Interval node_capacity = node_capacity_[node];
3164 if (demand.min != demand.max ||
3165 node_capacity.min != std::numeric_limits<int64_t>::min() ||
3166 node_capacity.max != std::numeric_limits<int64_t>::max()) {
3167 previous_nontrivial_index = index;
3168 }
3169 previous_nontrivial_index_.push_back(previous_nontrivial_index);
3170 ++index;
3171 }
3172}
3173
3174void UnaryDimensionChecker::UpdateRMQStructure(int begin_index, int end_index) {
3175 // The max layer is the one used by
3176 // GetMinMaxPartialDemandSum(begin_index, end_index - 1).
3177 const int maximum_rmq_exponent =
3178 MostSignificantBitPosition32(end_index - 1 - begin_index);
3179 for (int layer = 1, window_size = 1; layer <= maximum_rmq_exponent;
3180 ++layer, window_size *= 2) {
3181 partial_demand_sums_rmq_[layer].resize(end_index);
3182 for (int i = begin_index; i < end_index - window_size; ++i) {
3183 const Interval i1 = partial_demand_sums_rmq_[layer - 1][i];
3184 const Interval i2 = partial_demand_sums_rmq_[layer - 1][i + window_size];
3185 partial_demand_sums_rmq_[layer][i] = {std::min(i1.min, i2.min),
3186 std::max(i1.max, i2.max)};
3187 }
3188 std::copy(
3189 partial_demand_sums_rmq_[layer - 1].begin() + end_index - window_size,
3190 partial_demand_sums_rmq_[layer - 1].begin() + end_index,
3191 partial_demand_sums_rmq_[layer].begin() + end_index - window_size);
3192 }
3193}
3194
3195// TODO(user): since this is called only when
3196// last_node_index - first_node_index is large enough,
3197// the lower layers of partial_demand_sums_rmq_ are never used.
3198// For instance, if this is only called when the range size is > 4
3199// and paths are <= 32 nodes long, then we only need layers 0, 2, 3, and 4.
3200// To compare, on a 512 < #nodes <= 1024 problem, this uses layers in [0, 10].
3201UnaryDimensionChecker::Interval
3202UnaryDimensionChecker::GetMinMaxPartialDemandSum(int first_node_index,
3203 int last_node_index) const {
3204 DCHECK_LE(0, first_node_index);
3205 DCHECK_LT(first_node_index, last_node_index);
3206 DCHECK_LT(last_node_index, partial_demand_sums_rmq_[0].size());
3207 // Find largest window_size = 2^layer such that
3208 // first_node_index < last_node_index - window_size + 1.
3209 const int layer =
3210 MostSignificantBitPosition32(last_node_index - first_node_index);
3211 const int window_size = 1 << layer;
3212 // Classical range min/max query in O(1).
3213 const Interval i1 = partial_demand_sums_rmq_[layer][first_node_index];
3214 const Interval i2 =
3215 partial_demand_sums_rmq_[layer][last_node_index - window_size + 1];
3216 return {std::min(i1.min, i2.min), std::max(i1.max, i2.max)};
3217}
3218
3219bool UnaryDimensionChecker::SubpathOnlyHasTrivialNodes(
3220 int first_node_index, int last_node_index) const {
3221 DCHECK_LE(0, first_node_index);
3222 DCHECK_LT(first_node_index, last_node_index);
3223 DCHECK_LT(last_node_index, previous_nontrivial_index_.size());
3224 return first_node_index > previous_nontrivial_index_[last_node_index];
3225}
3226
3227namespace {
3228
3229class UnaryDimensionFilter : public LocalSearchFilter {
3230 public:
3231 std::string DebugString() const override { return name_; }
3232 UnaryDimensionFilter(std::unique_ptr<UnaryDimensionChecker> checker,
3233 const std::string& dimension_name)
3234 : checker_(std::move(checker)),
3235 name_(absl::StrCat("UnaryDimensionFilter(", dimension_name, ")")) {}
3236
3237 bool Accept(const Assignment* delta, const Assignment* deltadelta,
3238 int64_t objective_min, int64_t objective_max) override {
3239 return checker_->Check();
3240 }
3241
3242 void Synchronize(const Assignment* assignment,
3243 const Assignment* delta) override {
3244 checker_->Commit();
3245 }
3246
3247 private:
3248 std::unique_ptr<UnaryDimensionChecker> checker_;
3249 const std::string name_;
3250};
3251
3252} // namespace
3253
3255 Solver* solver, std::unique_ptr<UnaryDimensionChecker> checker,
3256 const std::string& dimension_name) {
3257 UnaryDimensionFilter* filter =
3258 new UnaryDimensionFilter(std::move(checker), dimension_name);
3259 return solver->RevAlloc(filter);
3260}
3261
3262namespace {
3263// ----- Variable domain filter -----
3264// Rejects assignments to values outside the domain of variables
3265
3266class VariableDomainFilter : public LocalSearchFilter {
3267 public:
3268 VariableDomainFilter() {}
3269 ~VariableDomainFilter() override {}
3270 bool Accept(const Assignment* delta, const Assignment* deltadelta,
3271 int64_t objective_min, int64_t objective_max) override;
3272 void Synchronize(const Assignment* assignment,
3273 const Assignment* delta) override {}
3274
3275 std::string DebugString() const override { return "VariableDomainFilter"; }
3276};
3277
3278bool VariableDomainFilter::Accept(const Assignment* delta,
3279 const Assignment* deltadelta,
3280 int64_t objective_min,
3281 int64_t objective_max) {
3282 const Assignment::IntContainer& container = delta->IntVarContainer();
3283 const int size = container.Size();
3284 for (int i = 0; i < size; ++i) {
3285 const IntVarElement& element = container.Element(i);
3286 if (element.Activated() && !element.Var()->Contains(element.Value())) {
3287 return false;
3288 }
3289 }
3290 return true;
3291}
3292} // namespace
3293
3295 return RevAlloc(new VariableDomainFilter());
3296}
3297
3298// ----- IntVarLocalSearchFilter -----
3299
3300const int IntVarLocalSearchFilter::kUnassigned = -1;
3301
3303 const std::vector<IntVar*>& vars) {
3304 AddVars(vars);
3305}
3306
3307void IntVarLocalSearchFilter::AddVars(const std::vector<IntVar*>& vars) {
3308 if (!vars.empty()) {
3309 for (int i = 0; i < vars.size(); ++i) {
3310 const int index = vars[i]->index();
3311 if (index >= var_index_to_index_.size()) {
3312 var_index_to_index_.resize(index + 1, kUnassigned);
3313 }
3314 var_index_to_index_[index] = i + vars_.size();
3315 }
3316 vars_.insert(vars_.end(), vars.begin(), vars.end());
3317 values_.resize(vars_.size(), /*junk*/ 0);
3318 var_synced_.resize(vars_.size(), false);
3319 }
3320}
3321
3323
3325 const Assignment* delta) {
3326 if (delta == nullptr || delta->Empty()) {
3327 var_synced_.assign(var_synced_.size(), false);
3328 SynchronizeOnAssignment(assignment);
3329 } else {
3331 }
3333}
3334
3336 const Assignment* assignment) {
3337 const Assignment::IntContainer& container = assignment->IntVarContainer();
3338 const int size = container.Size();
3339 for (int i = 0; i < size; ++i) {
3340 const IntVarElement& element = container.Element(i);
3341 IntVar* const var = element.Var();
3342 if (var != nullptr) {
3343 if (i < vars_.size() && vars_[i] == var) {
3344 values_[i] = element.Value();
3345 var_synced_[i] = true;
3346 } else {
3347 const int64_t kUnallocated = -1;
3348 int64_t index = kUnallocated;
3349 if (FindIndex(var, &index)) {
3350 values_[index] = element.Value();
3351 var_synced_[index] = true;
3352 }
3353 }
3354 }
3355 }
3356}
3357
3358// ----- Sum Objective filter ------
3359// Maintains the sum of costs of variables, where the subclass implements
3360// CostOfSynchronizedVariable() and FillCostOfBoundDeltaVariable() to compute
3361// the cost of a variable depending on its value.
3362// An assignment is accepted by this filter if the total cost is allowed
3363// depending on the relation defined by filter_enum:
3364// - Solver::LE -> total_cost <= objective_max.
3365// - Solver::GE -> total_cost >= objective_min.
3366// - Solver::EQ -> the conjunction of LE and GE.
3367namespace {
3368class SumObjectiveFilter : public IntVarLocalSearchFilter {
3369 public:
3370 SumObjectiveFilter(const std::vector<IntVar*>& vars,
3373 primary_vars_size_(vars.size()),
3374 synchronized_costs_(new int64_t[vars.size()]),
3375 delta_costs_(new int64_t[vars.size()]),
3376 filter_enum_(filter_enum),
3377 synchronized_sum_(std::numeric_limits<int64_t>::min()),
3378 delta_sum_(std::numeric_limits<int64_t>::min()),
3379 incremental_(false) {
3380 for (int i = 0; i < vars.size(); ++i) {
3381 synchronized_costs_[i] = 0;
3382 delta_costs_[i] = 0;
3383 }
3384 }
3385 ~SumObjectiveFilter() override {
3386 delete[] synchronized_costs_;
3387 delete[] delta_costs_;
3388 }
3389 bool Accept(const Assignment* delta, const Assignment* deltadelta,
3390 int64_t objective_min, int64_t objective_max) override {
3391 if (delta == nullptr) {
3392 return false;
3393 }
3394 if (deltadelta->Empty()) {
3395 if (incremental_) {
3396 for (int i = 0; i < primary_vars_size_; ++i) {
3398 }
3400 }
3401 incremental_ = false;
3403 CostOfChanges(delta, synchronized_costs_, false));
3404 } else {
3405 if (incremental_) {
3406 delta_sum_ =
3407 CapAdd(delta_sum_, CostOfChanges(deltadelta, delta_costs_, true));
3408 } else {
3410 CostOfChanges(delta, synchronized_costs_, true));
3411 }
3412 incremental_ = true;
3413 }
3414 switch (filter_enum_) {
3415 case Solver::LE: {
3416 return delta_sum_ <= objective_max;
3417 }
3418 case Solver::GE: {
3419 return delta_sum_ >= objective_min;
3420 }
3421 case Solver::EQ: {
3422 return objective_min <= delta_sum_ && delta_sum_ <= objective_max;
3423 }
3424 default: {
3425 LOG(ERROR) << "Unknown local search filter enum value";
3426 return false;
3427 }
3428 }
3429 }
3430 // If the variable is synchronized, returns its associated cost, otherwise
3431 // returns 0.
3432 virtual int64_t CostOfSynchronizedVariable(int64_t index) = 0;
3433 // If the variable is bound, fills new_cost with the cost associated to the
3434 // variable's valuation in container, and returns true. Otherwise, fills
3435 // new_cost with 0, and returns false.
3436 virtual bool FillCostOfBoundDeltaVariable(
3437 const Assignment::IntContainer& container, int index,
3438 int* container_index, int64_t* new_cost) = 0;
3439 bool IsIncremental() const override { return true; }
3440
3441 std::string DebugString() const override { return "SumObjectiveFilter"; }
3442
3443 int64_t GetSynchronizedObjectiveValue() const override {
3444 return synchronized_sum_;
3445 }
3446 int64_t GetAcceptedObjectiveValue() const override { return delta_sum_; }
3447
3448 protected:
3450 int64_t* const synchronized_costs_;
3451 int64_t* const delta_costs_;
3454 int64_t delta_sum_;
3456
3457 private:
3458 void OnSynchronize(const Assignment* delta) override {
3460 for (int i = 0; i < primary_vars_size_; ++i) {
3461 const int64_t cost = CostOfSynchronizedVariable(i);
3463 delta_costs_[i] = cost;
3465 }
3467 incremental_ = false;
3468 }
3469 int64_t CostOfChanges(const Assignment* changes,
3470 const int64_t* const old_costs,
3471 bool cache_delta_values) {
3472 int64_t total_cost = 0;
3473 const Assignment::IntContainer& container = changes->IntVarContainer();
3474 const int size = container.Size();
3475 for (int i = 0; i < size; ++i) {
3476 const IntVarElement& new_element = container.Element(i);
3477 IntVar* const var = new_element.Var();
3478 int64_t index = -1;
3479 if (FindIndex(var, &index) && index < primary_vars_size_) {
3480 total_cost = CapSub(total_cost, old_costs[index]);
3481 int64_t new_cost = 0LL;
3482 if (FillCostOfBoundDeltaVariable(container, index, &i, &new_cost)) {
3483 total_cost = CapAdd(total_cost, new_cost);
3484 }
3485 if (cache_delta_values) {
3486 delta_costs_[index] = new_cost;
3487 }
3488 }
3489 }
3490 return total_cost;
3491 }
3492};
3493
3494class BinaryObjectiveFilter : public SumObjectiveFilter {
3495 public:
3496 BinaryObjectiveFilter(const std::vector<IntVar*>& vars,
3497 Solver::IndexEvaluator2 value_evaluator,
3499 : SumObjectiveFilter(vars, filter_enum),
3500 value_evaluator_(std::move(value_evaluator)) {}
3501 ~BinaryObjectiveFilter() override {}
3502 int64_t CostOfSynchronizedVariable(int64_t index) override {
3504 ? value_evaluator_(index, IntVarLocalSearchFilter::Value(index))
3505 : 0;
3506 }
3507 bool FillCostOfBoundDeltaVariable(const Assignment::IntContainer& container,
3508 int index, int* container_index,
3509 int64_t* new_cost) override {
3510 const IntVarElement& element = container.Element(*container_index);
3511 if (element.Activated()) {
3512 *new_cost = value_evaluator_(index, element.Value());
3513 return true;
3514 }
3515 const IntVar* var = element.Var();
3516 if (var->Bound()) {
3517 *new_cost = value_evaluator_(index, var->Min());
3518 return true;
3519 }
3520 *new_cost = 0;
3521 return false;
3522 }
3523
3524 private:
3525 Solver::IndexEvaluator2 value_evaluator_;
3526};
3527
3528class TernaryObjectiveFilter : public SumObjectiveFilter {
3529 public:
3530 TernaryObjectiveFilter(const std::vector<IntVar*>& vars,
3531 const std::vector<IntVar*>& secondary_vars,
3532 Solver::IndexEvaluator3 value_evaluator,
3534 : SumObjectiveFilter(vars, filter_enum),
3535 secondary_vars_offset_(vars.size()),
3536 value_evaluator_(std::move(value_evaluator)) {
3537 IntVarLocalSearchFilter::AddVars(secondary_vars);
3539 }
3540 ~TernaryObjectiveFilter() override {}
3541 int64_t CostOfSynchronizedVariable(int64_t index) override {
3542 DCHECK_LT(index, secondary_vars_offset_);
3544 ? value_evaluator_(index, IntVarLocalSearchFilter::Value(index),
3546 index + secondary_vars_offset_))
3547 : 0;
3548 }
3549 bool FillCostOfBoundDeltaVariable(const Assignment::IntContainer& container,
3550 int index, int* container_index,
3551 int64_t* new_cost) override {
3552 DCHECK_LT(index, secondary_vars_offset_);
3553 *new_cost = 0LL;
3554 const IntVarElement& element = container.Element(*container_index);
3555 const IntVar* secondary_var =
3556 IntVarLocalSearchFilter::Var(index + secondary_vars_offset_);
3557 if (element.Activated()) {
3558 const int64_t value = element.Value();
3559 int hint_index = *container_index + 1;
3560 if (hint_index < container.Size() &&
3561 secondary_var == container.Element(hint_index).Var()) {
3562 *new_cost = value_evaluator_(index, value,
3563 container.Element(hint_index).Value());
3564 *container_index = hint_index;
3565 } else {
3566 *new_cost = value_evaluator_(index, value,
3567 container.Element(secondary_var).Value());
3568 }
3569 return true;
3570 }
3571 const IntVar* var = element.Var();
3572 if (var->Bound() && secondary_var->Bound()) {
3573 *new_cost = value_evaluator_(index, var->Min(), secondary_var->Min());
3574 return true;
3575 }
3576 *new_cost = 0;
3577 return false;
3578 }
3579
3580 private:
3581 int secondary_vars_offset_;
3582 Solver::IndexEvaluator3 value_evaluator_;
3583};
3584} // namespace
3585
3587 const std::vector<IntVar*>& vars, Solver::IndexEvaluator2 values,
3588 Solver::LocalSearchFilterBound filter_enum) {
3589 return RevAlloc(
3590 new BinaryObjectiveFilter(vars, std::move(values), filter_enum));
3591}
3592
3594 const std::vector<IntVar*>& vars,
3595 const std::vector<IntVar*>& secondary_vars, Solver::IndexEvaluator3 values,
3596 Solver::LocalSearchFilterBound filter_enum) {
3597 return RevAlloc(new TernaryObjectiveFilter(vars, secondary_vars,
3598 std::move(values), filter_enum));
3599}
3600
3602 int64_t initial_max) {
3603 DCHECK(state_is_valid_);
3604 DCHECK_LE(initial_min, initial_max);
3605 initial_variable_bounds_.push_back({initial_min, initial_max});
3606 variable_bounds_.push_back({initial_min, initial_max});
3607 variable_is_relaxed_.push_back(false);
3608
3609 const int variable_index = variable_bounds_.size() - 1;
3610 return {this, variable_index};
3611}
3612
3613void LocalSearchState::RelaxVariableBounds(int variable_index) {
3614 DCHECK(state_is_valid_);
3615 DCHECK(0 <= variable_index && variable_index < variable_is_relaxed_.size());
3616 if (!variable_is_relaxed_[variable_index]) {
3617 variable_is_relaxed_[variable_index] = true;
3618 saved_variable_bounds_trail_.emplace_back(variable_bounds_[variable_index],
3619 variable_index);
3620 variable_bounds_[variable_index] = initial_variable_bounds_[variable_index];
3621 }
3622}
3623
3624int64_t LocalSearchState::VariableMin(int variable_index) const {
3625 DCHECK(state_is_valid_);
3626 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3627 return variable_bounds_[variable_index].min;
3628}
3629
3630int64_t LocalSearchState::VariableMax(int variable_index) const {
3631 DCHECK(state_is_valid_);
3632 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3633 return variable_bounds_[variable_index].max;
3634}
3635
3636bool LocalSearchState::TightenVariableMin(int variable_index,
3637 int64_t min_value) {
3638 DCHECK(state_is_valid_);
3639 DCHECK(variable_is_relaxed_[variable_index]);
3640 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3641 Bounds& bounds = variable_bounds_[variable_index];
3642 if (bounds.max < min_value) {
3643 state_is_valid_ = false;
3644 }
3645 bounds.min = std::max(bounds.min, min_value);
3646 return state_is_valid_;
3647}
3648
3649bool LocalSearchState::TightenVariableMax(int variable_index,
3650 int64_t max_value) {
3651 DCHECK(state_is_valid_);
3652 DCHECK(variable_is_relaxed_[variable_index]);
3653 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3654 Bounds& bounds = variable_bounds_[variable_index];
3655 if (bounds.min > max_value) {
3656 state_is_valid_ = false;
3657 }
3658 bounds.max = std::min(bounds.max, max_value);
3659 return state_is_valid_;
3660}
3661
3662// TODO(user): When the class has more users, find a threshold ratio of
3663// saved/total variables under which a sparse clear would be more efficient
3664// for both Commit() and Revert().
3666 DCHECK(state_is_valid_);
3667 saved_variable_bounds_trail_.clear();
3668 variable_is_relaxed_.assign(variable_is_relaxed_.size(), false);
3669}
3670
3672 for (const auto& bounds_index : saved_variable_bounds_trail_) {
3673 DCHECK(variable_is_relaxed_[bounds_index.second]);
3674 variable_bounds_[bounds_index.second] = bounds_index.first;
3675 }
3676 saved_variable_bounds_trail_.clear();
3677 variable_is_relaxed_.assign(variable_is_relaxed_.size(), false);
3678 state_is_valid_ = true;
3679}
3680
3681// ----- LocalSearchProfiler -----
3682
3684 public:
3686 std::string DebugString() const override { return "LocalSearchProfiler"; }
3687 void RestartSearch() override {
3688 operator_stats_.clear();
3689 filter_stats_.clear();
3690 }
3691 void ExitSearch() override {
3692 // Update times for current operator when the search ends.
3693 if (solver()->TopLevelSearch() == solver()->ActiveSearch()) {
3694 UpdateTime();
3695 }
3696 }
3697 LocalSearchStatistics ExportToLocalSearchStatistics() const {
3698 LocalSearchStatistics statistics_proto;
3699 for (ProfiledDecisionBuilder* db : profiled_decision_builders_) {
3700 if (db->seconds() == 0) continue;
3701 LocalSearchStatistics::FirstSolutionStatistics* const
3702 first_solution_statistics =
3703 statistics_proto.add_first_solution_statistics();
3704 first_solution_statistics->set_strategy(db->name());
3705 first_solution_statistics->set_duration_seconds(db->seconds());
3706 }
3707 std::vector<const LocalSearchOperator*> operators;
3708 for (const auto& stat : operator_stats_) {
3709 operators.push_back(stat.first);
3710 }
3711 std::sort(
3712 operators.begin(), operators.end(),
3713 [this](const LocalSearchOperator* op1, const LocalSearchOperator* op2) {
3714 return gtl::FindOrDie(operator_stats_, op1).neighbors >
3715 gtl::FindOrDie(operator_stats_, op2).neighbors;
3716 });
3717 for (const LocalSearchOperator* const op : operators) {
3718 const OperatorStats& stats = gtl::FindOrDie(operator_stats_, op);
3719 LocalSearchStatistics::LocalSearchOperatorStatistics* const
3720 local_search_operator_statistics =
3721 statistics_proto.add_local_search_operator_statistics();
3722 local_search_operator_statistics->set_local_search_operator(
3723 op->DebugString());
3724 local_search_operator_statistics->set_num_neighbors(stats.neighbors);
3725 local_search_operator_statistics->set_num_filtered_neighbors(
3726 stats.filtered_neighbors);
3727 local_search_operator_statistics->set_num_accepted_neighbors(
3728 stats.accepted_neighbors);
3729 local_search_operator_statistics->set_duration_seconds(stats.seconds);
3730 }
3731 std::vector<const LocalSearchFilter*> filters;
3732 for (const auto& stat : filter_stats_) {
3733 filters.push_back(stat.first);
3734 }
3735 std::sort(filters.begin(), filters.end(),
3736 [this](const LocalSearchFilter* filter1,
3737 const LocalSearchFilter* filter2) {
3738 return gtl::FindOrDie(filter_stats_, filter1).calls >
3739 gtl::FindOrDie(filter_stats_, filter2).calls;
3740 });
3741 for (const LocalSearchFilter* const filter : filters) {
3742 const FilterStats& stats = gtl::FindOrDie(filter_stats_, filter);
3743 LocalSearchStatistics::LocalSearchFilterStatistics* const
3744 local_search_filter_statistics =
3745 statistics_proto.add_local_search_filter_statistics();
3746 local_search_filter_statistics->set_local_search_filter(
3747 filter->DebugString());
3748 local_search_filter_statistics->set_num_calls(stats.calls);
3749 local_search_filter_statistics->set_num_rejects(stats.rejects);
3750 local_search_filter_statistics->set_duration_seconds(stats.seconds);
3751 }
3752 statistics_proto.set_total_num_neighbors(solver()->neighbors());
3753 statistics_proto.set_total_num_filtered_neighbors(
3754 solver()->filtered_neighbors());
3755 statistics_proto.set_total_num_accepted_neighbors(
3756 solver()->accepted_neighbors());
3757 return statistics_proto;
3758 }
3759 std::string PrintOverview() const {
3760 size_t max_name_size = 0;
3761 std::vector<const LocalSearchOperator*> operators;
3762 for (const auto& stat : operator_stats_) {
3763 operators.push_back(stat.first);
3764 max_name_size =
3765 std::max(max_name_size, stat.first->DebugString().length());
3766 }
3767 std::sort(
3768 operators.begin(), operators.end(),
3769 [this](const LocalSearchOperator* op1, const LocalSearchOperator* op2) {
3770 return gtl::FindOrDie(operator_stats_, op1).neighbors >
3771 gtl::FindOrDie(operator_stats_, op2).neighbors;
3772 });
3773 std::string overview = "Local search operator statistics:\n";
3774 absl::StrAppendFormat(&overview,
3775 "%*s | Neighbors | Filtered | Accepted | Time (s)\n",
3776 max_name_size, "");
3777 OperatorStats total_stats;
3778 for (const LocalSearchOperator* const op : operators) {
3779 const OperatorStats& stats = gtl::FindOrDie(operator_stats_, op);
3780 const std::string& name = op->DebugString();
3781 absl::StrAppendFormat(&overview, "%*s | %9ld | %8ld | %8ld | %7.2g\n",
3782 max_name_size, name, stats.neighbors,
3783 stats.filtered_neighbors, stats.accepted_neighbors,
3784 stats.seconds);
3785 total_stats.neighbors += stats.neighbors;
3786 total_stats.filtered_neighbors += stats.filtered_neighbors;
3787 total_stats.accepted_neighbors += stats.accepted_neighbors;
3788 total_stats.seconds += stats.seconds;
3789 }
3790 absl::StrAppendFormat(&overview, "%*s | %9ld | %8ld | %8ld | %7.2g\n",
3791 max_name_size, "Total", total_stats.neighbors,
3792 total_stats.filtered_neighbors,
3793 total_stats.accepted_neighbors, total_stats.seconds);
3794 max_name_size = 0;
3795 std::vector<const LocalSearchFilter*> filters;
3796 for (const auto& stat : filter_stats_) {
3797 filters.push_back(stat.first);
3798 max_name_size =
3799 std::max(max_name_size, stat.first->DebugString().length());
3800 }
3801 std::sort(filters.begin(), filters.end(),
3802 [this](const LocalSearchFilter* filter1,
3803 const LocalSearchFilter* filter2) {
3804 return gtl::FindOrDie(filter_stats_, filter1).calls >
3805 gtl::FindOrDie(filter_stats_, filter2).calls;
3806 });
3807 absl::StrAppendFormat(&overview,
3808 "Local search filter statistics:\n%*s | Calls | "
3809 " Rejects | Time (s) "
3810 "| Rejects/s\n",
3811 max_name_size, "");
3812 FilterStats total_filter_stats;
3813 for (const LocalSearchFilter* const filter : filters) {
3814 const FilterStats& stats = gtl::FindOrDie(filter_stats_, filter);
3815 const std::string& name = filter->DebugString();
3816 absl::StrAppendFormat(&overview, "%*s | %9ld | %9ld | %7.2g | %7.2g\n",
3817 max_name_size, name, stats.calls, stats.rejects,
3818 stats.seconds, stats.rejects / stats.seconds);
3819 total_filter_stats.calls += stats.calls;
3820 total_filter_stats.rejects += stats.rejects;
3821 total_filter_stats.seconds += stats.seconds;
3822 }
3823 absl::StrAppendFormat(
3824 &overview, "%*s | %9ld | %9ld | %7.2g | %7.2g\n", max_name_size,
3825 "Total", total_filter_stats.calls, total_filter_stats.rejects,
3826 total_filter_stats.seconds,
3827 total_filter_stats.rejects / total_filter_stats.seconds);
3828 return overview;
3829 }
3830 void BeginOperatorStart() override {}
3831 void EndOperatorStart() override {}
3833 if (last_operator_ != op->Self()) {
3834 UpdateTime();
3835 last_operator_ = op->Self();
3836 }
3837 }
3838 void EndMakeNextNeighbor(const LocalSearchOperator* op, bool neighbor_found,
3839 const Assignment* delta,
3840 const Assignment* deltadelta) override {
3841 if (neighbor_found) {
3842 operator_stats_[op->Self()].neighbors++;
3843 }
3844 }
3845 void BeginFilterNeighbor(const LocalSearchOperator* op) override {}
3847 bool neighbor_found) override {
3848 if (neighbor_found) {
3849 operator_stats_[op->Self()].filtered_neighbors++;
3850 }
3851 }
3852 void BeginAcceptNeighbor(const LocalSearchOperator* op) override {}
3854 bool neighbor_found) override {
3855 if (neighbor_found) {
3856 operator_stats_[op->Self()].accepted_neighbors++;
3857 }
3858 }
3859 void BeginFiltering(const LocalSearchFilter* filter) override {
3860 filter_stats_[filter].calls++;
3861 filter_timer_.Start();
3862 }
3863 void EndFiltering(const LocalSearchFilter* filter, bool reject) override {
3864 filter_timer_.Stop();
3865 auto& stats = filter_stats_[filter];
3866 stats.seconds += filter_timer_.Get();
3867 if (reject) {
3868 stats.rejects++;
3869 }
3870 }
3872 ProfiledDecisionBuilder* profiled_db) {
3873 profiled_decision_builders_.push_back(profiled_db);
3874 }
3875 void Install() override { SearchMonitor::Install(); }
3876
3877 private:
3878 void UpdateTime() {
3879 if (last_operator_ != nullptr) {
3880 timer_.Stop();
3881 operator_stats_[last_operator_].seconds += timer_.Get();
3882 }
3883 timer_.Start();
3884 }
3885
3886 struct OperatorStats {
3887 int64_t neighbors = 0;
3888 int64_t filtered_neighbors = 0;
3889 int64_t accepted_neighbors = 0;
3890 double seconds = 0;
3891 };
3892
3893 struct FilterStats {
3894 int64_t calls = 0;
3895 int64_t rejects = 0;
3896 double seconds = 0;
3897 };
3898 WallTimer timer_;
3899 WallTimer filter_timer_;
3900 const LocalSearchOperator* last_operator_ = nullptr;
3901 absl::flat_hash_map<const LocalSearchOperator*, OperatorStats>
3902 operator_stats_;
3903 absl::flat_hash_map<const LocalSearchFilter*, FilterStats> filter_stats_;
3904 // Profiled decision builders.
3905 std::vector<ProfiledDecisionBuilder*> profiled_decision_builders_;
3906};
3907
3909 DecisionBuilder* db) {
3911 ProfiledDecisionBuilder* profiled_db =
3913 local_search_profiler_->AddFirstSolutionProfiledDecisionBuilder(
3914 profiled_db);
3915 return profiled_db;
3916 }
3917 return db;
3918}
3919
3921 monitor->Install();
3922}
3923
3925 if (solver->IsLocalSearchProfilingEnabled()) {
3926 return new LocalSearchProfiler(solver);
3927 }
3928 return nullptr;
3929}
3930
3931void DeleteLocalSearchProfiler(LocalSearchProfiler* monitor) { delete monitor; }
3932
3933std::string Solver::LocalSearchProfile() const {
3934 if (local_search_profiler_ != nullptr) {
3935 return local_search_profiler_->PrintOverview();
3936 }
3937 return "";
3938}
3939
3940LocalSearchStatistics Solver::GetLocalSearchStatistics() const {
3941 if (local_search_profiler_ != nullptr) {
3942 return local_search_profiler_->ExportToLocalSearchStatistics();
3943 }
3944 return LocalSearchStatistics();
3945}
3946
3947void LocalSearchFilterManager::InitializeForcedEvents() {
3948 const int num_events = filter_events_.size();
3949 int next_forced_event = num_events;
3950 next_forced_events_.resize(num_events);
3951 for (int i = num_events - 1; i >= 0; --i) {
3952 next_forced_events_[i] = next_forced_event;
3953 if (filter_events_[i].filter->IsIncremental() ||
3954 (filter_events_[i].event_type == FilterEventType::kRelax &&
3955 next_forced_event != num_events)) {
3956 next_forced_event = i;
3957 }
3958 }
3959}
3960
3962 std::vector<LocalSearchFilter*> filters)
3963 : synchronized_value_(std::numeric_limits<int64_t>::min()),
3964 accepted_value_(std::numeric_limits<int64_t>::min()) {
3965 filter_events_.reserve(2 * filters.size());
3966 for (LocalSearchFilter* filter : filters) {
3967 filter_events_.push_back({filter, FilterEventType::kRelax});
3968 }
3969 for (LocalSearchFilter* filter : filters) {
3970 filter_events_.push_back({filter, FilterEventType::kAccept});
3971 }
3972 InitializeForcedEvents();
3973}
3974
3976 std::vector<FilterEvent> filter_events)
3977 : filter_events_(std::move(filter_events)),
3978 synchronized_value_(std::numeric_limits<int64_t>::min()),
3979 accepted_value_(std::numeric_limits<int64_t>::min()) {
3980 InitializeForcedEvents();
3981}
3982
3983// Filters' Revert() must be called in the reverse order in which their
3984// Relax() was called.
3986 for (int i = last_event_called_; i >= 0; --i) {
3987 auto [filter, event_type] = filter_events_[i];
3988 if (event_type == FilterEventType::kRelax) filter->Revert();
3989 }
3990 last_event_called_ = -1;
3991}
3992
3993// TODO(user): the behaviour of Accept relies on the initial order of
3994// filters having at most one filter with negative objective values,
3995// this could be fixed by having filters return their general bounds.
3997 const Assignment* delta,
3998 const Assignment* deltadelta,
3999 int64_t objective_min,
4000 int64_t objective_max) {
4001 Revert();
4002 accepted_value_ = 0;
4003 bool ok = true;
4004 const int num_events = filter_events_.size();
4005 for (int i = 0; i < num_events;) {
4006 last_event_called_ = i;
4007 auto [filter, event_type] = filter_events_[last_event_called_];
4008 switch (event_type) {
4009 case FilterEventType::kAccept: {
4010 if (monitor != nullptr) monitor->BeginFiltering(filter);
4011 const bool accept = filter->Accept(
4012 delta, deltadelta, CapSub(objective_min, accepted_value_),
4013 CapSub(objective_max, accepted_value_));
4014 ok &= accept;
4015 if (monitor != nullptr) monitor->EndFiltering(filter, !accept);
4016 if (ok) {
4017 accepted_value_ =
4018 CapAdd(accepted_value_, filter->GetAcceptedObjectiveValue());
4019 // TODO(user): handle objective min.
4020 ok = accepted_value_ <= objective_max;
4021 }
4022 break;
4023 }
4024 case FilterEventType::kRelax: {
4025 filter->Relax(delta, deltadelta);
4026 break;
4027 }
4028 default:
4029 LOG(FATAL) << "Unknown filter event type.";
4030 }
4031 // If the candidate is rejected, forced events must still be called.
4032 if (ok) {
4033 ++i;
4034 } else {
4035 i = next_forced_events_[i];
4036 }
4037 }
4038 return ok;
4039}
4040
4042 const Assignment* delta) {
4043 // If delta is nullptr or empty, then assignment may be a partial solution.
4044 // Send a signal to Relaxing filters to inform them,
4045 // so they can show the partial solution as a change from the empty solution.
4046 const bool reset_to_assignment = delta == nullptr || delta->Empty();
4047 // Relax in the forward direction.
4048 for (auto [filter, event_type] : filter_events_) {
4049 switch (event_type) {
4050 case FilterEventType::kAccept: {
4051 break;
4052 }
4053 case FilterEventType::kRelax: {
4054 if (reset_to_assignment) {
4055 filter->Reset();
4056 filter->Relax(assignment, nullptr);
4057 } else {
4058 filter->Relax(delta, nullptr);
4059 }
4060 break;
4061 }
4062 default:
4063 LOG(FATAL) << "Unknown filter event type.";
4064 }
4065 }
4066 // Synchronize/Commit backwards, so filters can read changes from their
4067 // dependencies before those are synchronized/committed.
4068 synchronized_value_ = 0;
4069 for (auto [filter, event_type] : ::gtl::reversed_view(filter_events_)) {
4070 switch (event_type) {
4071 case FilterEventType::kAccept: {
4072 filter->Synchronize(assignment, delta);
4073 synchronized_value_ = CapAdd(synchronized_value_,
4074 filter->GetSynchronizedObjectiveValue());
4075 break;
4076 }
4077 case FilterEventType::kRelax: {
4078 filter->Commit(assignment, delta);
4079 break;
4080 }
4081 default:
4082 LOG(FATAL) << "Unknown filter event type.";
4083 }
4084 }
4085}
4086
4087// ----- Finds a neighbor of the assignment passed -----
4088
4090 public:
4091 FindOneNeighbor(Assignment* const assignment, IntVar* objective,
4092 SolutionPool* const pool,
4093 LocalSearchOperator* const ls_operator,
4094 DecisionBuilder* const sub_decision_builder,
4095 const RegularLimit* const limit,
4096 LocalSearchFilterManager* filter_manager);
4097 ~FindOneNeighbor() override {}
4098 Decision* Next(Solver* const solver) override;
4099 std::string DebugString() const override { return "FindOneNeighbor"; }
4100
4101 private:
4102 bool FilterAccept(Solver* solver, Assignment* delta, Assignment* deltadelta,
4103 int64_t objective_min, int64_t objective_max);
4104 void SynchronizeAll(Solver* solver);
4105
4106 Assignment* const assignment_;
4107 IntVar* const objective_;
4108 std::unique_ptr<Assignment> reference_assignment_;
4109 std::unique_ptr<Assignment> last_synchronized_assignment_;
4110 Assignment* const filter_assignment_delta_;
4111 SolutionPool* const pool_;
4112 LocalSearchOperator* const ls_operator_;
4113 DecisionBuilder* const sub_decision_builder_;
4114 RegularLimit* limit_;
4115 const RegularLimit* const original_limit_;
4116 bool neighbor_found_;
4117 LocalSearchFilterManager* const filter_manager_;
4118 int64_t solutions_since_last_check_;
4119 int64_t check_period_;
4120 Assignment last_checked_assignment_;
4121 bool has_checked_assignment_ = false;
4122};
4123
4124// reference_assignment_ is used to keep track of the last assignment on which
4125// operators were started, assignment_ corresponding to the last successful
4126// neighbor.
4127// last_synchronized_assignment_ keeps track of the last assignment on which
4128// filters were synchronized and is used to compute the filter_assignment_delta_
4129// when synchronizing again.
4131 IntVar* objective, SolutionPool* const pool,
4132 LocalSearchOperator* const ls_operator,
4133 DecisionBuilder* const sub_decision_builder,
4134 const RegularLimit* const limit,
4135 LocalSearchFilterManager* filter_manager)
4136 : assignment_(assignment),
4137 objective_(objective),
4138 reference_assignment_(new Assignment(assignment_)),
4139 filter_assignment_delta_(assignment->solver()->MakeAssignment()),
4140 pool_(pool),
4141 ls_operator_(ls_operator),
4142 sub_decision_builder_(sub_decision_builder),
4143 limit_(nullptr),
4144 original_limit_(limit),
4145 neighbor_found_(false),
4146 filter_manager_(filter_manager),
4147 solutions_since_last_check_(0),
4148 check_period_(
4149 assignment_->solver()->parameters().check_solution_period()),
4150 last_checked_assignment_(assignment) {
4151 CHECK(nullptr != assignment);
4152 CHECK(nullptr != ls_operator);
4153
4154 Solver* const solver = assignment_->solver();
4155 // If limit is nullptr, default limit is 1 solution
4156 if (nullptr == limit) {
4157 limit_ = solver->MakeSolutionsLimit(1);
4158 } else {
4159 limit_ = limit->MakeIdenticalClone();
4160 // TODO(user): Support skipping neighborhood checks for limits accepting
4161 // more than one solution (e.g. best accept). For now re-enabling systematic
4162 // checks.
4163 if (limit_->solutions() != 1) {
4164 VLOG(1) << "Disabling neighbor-check skipping outside of first accept.";
4165 check_period_ = 1;
4166 }
4167 }
4168 // TODO(user): Support skipping neighborhood checks with LNS (at least on
4169 // the non-LNS operators).
4170 if (ls_operator->HasFragments()) {
4171 VLOG(1) << "Disabling neighbor-check skipping for LNS.";
4172 check_period_ = 1;
4173 }
4174
4175 if (!reference_assignment_->HasObjective()) {
4176 reference_assignment_->AddObjective(objective_);
4177 }
4178}
4179
4181 CHECK(nullptr != solver);
4182
4183 if (original_limit_ != nullptr) {
4184 limit_->Copy(original_limit_);
4185 }
4186
4187 if (!last_checked_assignment_.HasObjective()) {
4188 last_checked_assignment_.AddObjective(assignment_->Objective());
4189 }
4190
4191 if (!neighbor_found_) {
4192 // Only called on the first call to Next(), reference_assignment_ has not
4193 // been synced with assignment_ yet
4194
4195 // Keeping the code in case a performance problem forces us to
4196 // use the old code with a zero test on pool_.
4197 // reference_assignment_->CopyIntersection(assignment_);
4198 pool_->Initialize(assignment_);
4199 SynchronizeAll(solver);
4200 }
4201
4202 {
4203 // Another assignment is needed to apply the delta
4204 Assignment* assignment_copy =
4205 solver->MakeAssignment(reference_assignment_.get());
4206 int counter = 0;
4207
4208 DecisionBuilder* restore = solver->MakeRestoreAssignment(assignment_copy);
4209 if (sub_decision_builder_) {
4210 restore = solver->Compose(restore, sub_decision_builder_);
4211 }
4212 Assignment* delta = solver->MakeAssignment();
4213 Assignment* deltadelta = solver->MakeAssignment();
4214 while (true) {
4215 if (!ls_operator_->HoldsDelta()) {
4216 delta->Clear();
4217 }
4218 delta->ClearObjective();
4219 deltadelta->Clear();
4220 solver->TopPeriodicCheck();
4221 if (++counter >= absl::GetFlag(FLAGS_cp_local_search_sync_frequency) &&
4222 pool_->SyncNeeded(reference_assignment_.get())) {
4223 // TODO(user) : SyncNeed(assignment_) ?
4224 counter = 0;
4225 SynchronizeAll(solver);
4226 }
4227
4228 bool has_neighbor = false;
4229 if (!limit_->Check()) {
4230 solver->GetLocalSearchMonitor()->BeginMakeNextNeighbor(ls_operator_);
4231 has_neighbor = ls_operator_->MakeNextNeighbor(delta, deltadelta);
4233 ls_operator_, has_neighbor, delta, deltadelta);
4234 }
4235
4236 if (has_neighbor && !solver->IsUncheckedSolutionLimitReached()) {
4237 solver->neighbors_ += 1;
4238 // All filters must be called for incrementality reasons.
4239 // Empty deltas must also be sent to incremental filters; can be needed
4240 // to resync filters on non-incremental (empty) moves.
4241 // TODO(user): Don't call both if no filter is incremental and one
4242 // of them returned false.
4243 solver->GetLocalSearchMonitor()->BeginFilterNeighbor(ls_operator_);
4244 const bool mh_filter =
4245 AcceptDelta(solver->ParentSearch(), delta, deltadelta);
4246 int64_t objective_min = std::numeric_limits<int64_t>::min();
4247 int64_t objective_max = std::numeric_limits<int64_t>::max();
4248 if (objective_) {
4249 objective_min = objective_->Min();
4250 objective_max = objective_->Max();
4251 }
4252 if (delta->HasObjective() && delta->Objective() == objective_) {
4253 objective_min = std::max(objective_min, delta->ObjectiveMin());
4254 objective_max = std::min(objective_max, delta->ObjectiveMax());
4255 }
4256 const bool move_filter = FilterAccept(solver, delta, deltadelta,
4257 objective_min, objective_max);
4259 ls_operator_, mh_filter && move_filter);
4260 if (!mh_filter || !move_filter) {
4261 if (filter_manager_ != nullptr) filter_manager_->Revert();
4262 continue;
4263 }
4264 solver->filtered_neighbors_ += 1;
4265 if (delta->HasObjective()) {
4266 if (!assignment_copy->HasObjective()) {
4267 assignment_copy->AddObjective(delta->Objective());
4268 }
4269 if (!assignment_->HasObjective()) {
4270 assignment_->AddObjective(delta->Objective());
4271 last_checked_assignment_.AddObjective(delta->Objective());
4272 }
4273 }
4274 assignment_copy->CopyIntersection(reference_assignment_.get());
4275 assignment_copy->CopyIntersection(delta);
4276 solver->GetLocalSearchMonitor()->BeginAcceptNeighbor(ls_operator_);
4277 const bool check_solution = (solutions_since_last_check_ == 0) ||
4278 !solver->UseFastLocalSearch() ||
4279 // LNS deltas need to be restored
4280 !delta->AreAllElementsBound();
4281 if (has_checked_assignment_) solutions_since_last_check_++;
4282 if (solutions_since_last_check_ >= check_period_) {
4283 solutions_since_last_check_ = 0;
4284 }
4285 const bool accept = !check_solution || solver->SolveAndCommit(restore);
4286 solver->GetLocalSearchMonitor()->EndAcceptNeighbor(ls_operator_,
4287 accept);
4288 if (accept) {
4289 solver->accepted_neighbors_ += 1;
4290 if (check_solution) {
4291 solver->SetSearchContext(solver->ParentSearch(),
4292 ls_operator_->DebugString());
4293 assignment_->Store();
4294 last_checked_assignment_.CopyIntersection(assignment_);
4295 neighbor_found_ = true;
4296 has_checked_assignment_ = true;
4297 return nullptr;
4298 }
4299 solver->SetSearchContext(solver->ActiveSearch(),
4300 ls_operator_->DebugString());
4301 assignment_->CopyIntersection(assignment_copy);
4302 assignment_->SetObjectiveValue(
4303 filter_manager_ ? filter_manager_->GetAcceptedObjectiveValue()
4304 : 0);
4305 // Advancing local search to the current solution without
4306 // checking.
4307 // TODO(user): support the case were limit_ accepts more than
4308 // one solution (e.g. best accept).
4309 AcceptUncheckedNeighbor(solver->ParentSearch());
4310 solver->IncrementUncheckedSolutionCounter();
4311 pool_->RegisterNewSolution(assignment_);
4312 SynchronizeAll(solver);
4313 // NOTE: SynchronizeAll() sets neighbor_found_ to false, force it
4314 // back to true when skipping checks.
4315 neighbor_found_ = true;
4316 } else {
4317 if (filter_manager_ != nullptr) filter_manager_->Revert();
4318 if (check_period_ > 1 && has_checked_assignment_) {
4319 // Filtering is not perfect, disabling fast local search and
4320 // resynchronizing with the last checked solution.
4321 // TODO(user): Restore state of local search operators to
4322 // make sure we are exploring neighbors in the same order. This can
4323 // affect the local optimum found.
4324 VLOG(1) << "Imperfect filtering detected, backtracking to last "
4325 "checked solution and checking all solutions.";
4326 check_period_ = 1;
4327 solutions_since_last_check_ = 0;
4328 pool_->RegisterNewSolution(&last_checked_assignment_);
4329 SynchronizeAll(solver);
4330 assignment_->CopyIntersection(&last_checked_assignment_);
4331 }
4332 }
4333 } else {
4334 if (neighbor_found_) {
4335 // In case the last checked assignment isn't the current one, restore
4336 // it to make sure the solver knows about it, especially if this is
4337 // the end of the search.
4338 // TODO(user): Compare assignments in addition to their cost.
4339 if (last_checked_assignment_.ObjectiveValue() !=
4340 assignment_->ObjectiveValue()) {
4341 // If restoring fails this means filtering is not perfect and the
4342 // solver will consider the last checked assignment.
4343 assignment_copy->CopyIntersection(assignment_);
4344 if (!solver->SolveAndCommit(restore)) solver->Fail();
4345 last_checked_assignment_.CopyIntersection(assignment_);
4346 has_checked_assignment_ = true;
4347 return nullptr;
4348 }
4349 AcceptNeighbor(solver->ParentSearch());
4350 // Keeping the code in case a performance problem forces us to
4351 // use the old code with a zero test on pool_.
4352 // reference_assignment_->CopyIntersection(assignment_);
4353 pool_->RegisterNewSolution(assignment_);
4354 SynchronizeAll(solver);
4355 } else {
4356 break;
4357 }
4358 }
4359 }
4360 }
4361 solver->Fail();
4362 return nullptr;
4363}
4364
4365bool FindOneNeighbor::FilterAccept(Solver* solver, Assignment* delta,
4366 Assignment* deltadelta,
4367 int64_t objective_min,
4368 int64_t objective_max) {
4369 if (filter_manager_ == nullptr) return true;
4370 LocalSearchMonitor* const monitor = solver->GetLocalSearchMonitor();
4371 return filter_manager_->Accept(monitor, delta, deltadelta, objective_min,
4372 objective_max);
4373}
4374
4375namespace {
4376
4377template <typename Container>
4378void AddDeltaElements(const Container& old_container,
4379 const Container& new_container, Assignment* delta) {
4380 for (const auto& new_element : new_container.elements()) {
4381 const auto var = new_element.Var();
4382 const auto old_element_ptr = old_container.ElementPtrOrNull(var);
4383 if (old_element_ptr == nullptr || *old_element_ptr != new_element) {
4384 delta->FastAdd(var)->Copy(new_element);
4385 }
4386 }
4387}
4388
4389void MakeDelta(const Assignment* old_assignment,
4390 const Assignment* new_assignment, Assignment* delta) {
4391 DCHECK_NE(delta, nullptr);
4392 delta->Clear();
4393 AddDeltaElements(old_assignment->IntVarContainer(),
4394 new_assignment->IntVarContainer(), delta);
4395 AddDeltaElements(old_assignment->IntervalVarContainer(),
4396 new_assignment->IntervalVarContainer(), delta);
4397 AddDeltaElements(old_assignment->SequenceVarContainer(),
4398 new_assignment->SequenceVarContainer(), delta);
4399}
4400} // namespace
4401
4402void FindOneNeighbor::SynchronizeAll(Solver* solver) {
4403 Assignment* const reference_assignment = reference_assignment_.get();
4404 pool_->GetNextSolution(reference_assignment);
4405 neighbor_found_ = false;
4406 limit_->Init();
4407 solver->GetLocalSearchMonitor()->BeginOperatorStart();
4408 ls_operator_->Start(reference_assignment);
4409 if (filter_manager_ != nullptr) {
4410 Assignment* delta = nullptr;
4411 if (last_synchronized_assignment_ == nullptr) {
4412 last_synchronized_assignment_ =
4413 absl::make_unique<Assignment>(reference_assignment);
4414 } else {
4415 MakeDelta(last_synchronized_assignment_.get(), reference_assignment,
4416 filter_assignment_delta_);
4417 delta = filter_assignment_delta_;
4418 last_synchronized_assignment_->Copy(reference_assignment);
4419 }
4420 filter_manager_->Synchronize(reference_assignment_.get(), delta);
4421 }
4422 solver->GetLocalSearchMonitor()->EndOperatorStart();
4423}
4424
4425// ---------- Local Search Phase Parameters ----------
4426
4428 public:
4432 RegularLimit* const limit,
4434 : objective_(objective),
4435 solution_pool_(pool),
4436 ls_operator_(ls_operator),
4437 sub_decision_builder_(sub_decision_builder),
4438 limit_(limit),
4439 filter_manager_(filter_manager) {}
4441 std::string DebugString() const override {
4442 return "LocalSearchPhaseParameters";
4443 }
4444
4445 IntVar* objective() const { return objective_; }
4446 SolutionPool* solution_pool() const { return solution_pool_; }
4447 LocalSearchOperator* ls_operator() const { return ls_operator_; }
4449 return sub_decision_builder_;
4450 }
4451 RegularLimit* limit() const { return limit_; }
4453 return filter_manager_;
4454 }
4455
4456 private:
4457 IntVar* const objective_;
4458 SolutionPool* const solution_pool_;
4459 LocalSearchOperator* const ls_operator_;
4460 DecisionBuilder* const sub_decision_builder_;
4461 RegularLimit* const limit_;
4462 LocalSearchFilterManager* const filter_manager_;
4463};
4464
4466 IntVar* objective, LocalSearchOperator* const ls_operator,
4467 DecisionBuilder* const sub_decision_builder) {
4469 ls_operator, sub_decision_builder,
4470 nullptr, nullptr);
4471}
4472
4474 IntVar* objective, LocalSearchOperator* const ls_operator,
4475 DecisionBuilder* const sub_decision_builder, RegularLimit* const limit) {
4477 ls_operator, sub_decision_builder,
4478 limit, nullptr);
4479}
4480
4482 IntVar* objective, LocalSearchOperator* const ls_operator,
4483 DecisionBuilder* const sub_decision_builder, RegularLimit* const limit,
4484 LocalSearchFilterManager* filter_manager) {
4486 ls_operator, sub_decision_builder,
4487 limit, filter_manager);
4488}
4489
4491 IntVar* objective, SolutionPool* const pool,
4492 LocalSearchOperator* const ls_operator,
4493 DecisionBuilder* const sub_decision_builder) {
4494 return MakeLocalSearchPhaseParameters(objective, pool, ls_operator,
4495 sub_decision_builder, nullptr, nullptr);
4496}
4497
4499 IntVar* objective, SolutionPool* const pool,
4500 LocalSearchOperator* const ls_operator,
4501 DecisionBuilder* const sub_decision_builder, RegularLimit* const limit) {
4502 return MakeLocalSearchPhaseParameters(objective, pool, ls_operator,
4503 sub_decision_builder, limit, nullptr);
4504}
4505
4507 IntVar* objective, SolutionPool* const pool,
4508 LocalSearchOperator* const ls_operator,
4509 DecisionBuilder* const sub_decision_builder, RegularLimit* const limit,
4510 LocalSearchFilterManager* filter_manager) {
4511 return RevAlloc(new LocalSearchPhaseParameters(objective, pool, ls_operator,
4512 sub_decision_builder, limit,
4513 filter_manager));
4514}
4515
4516namespace {
4517// ----- NestedSolve decision wrapper -----
4518
4519// This decision calls a nested Solve on the given DecisionBuilder in its
4520// left branch; does nothing in the left branch.
4521// The state of the decision corresponds to the result of the nested Solve:
4522// DECISION_PENDING - Nested Solve not called yet
4523// DECISION_FAILED - Nested Solve failed
4524// DECISION_FOUND - Nested Solve succeeded
4525
4526class NestedSolveDecision : public Decision {
4527 public:
4528 // This enum is used internally to tag states in the local search tree.
4529 enum StateType { DECISION_PENDING, DECISION_FAILED, DECISION_FOUND };
4530
4531 NestedSolveDecision(DecisionBuilder* const db, bool restore,
4532 const std::vector<SearchMonitor*>& monitors);
4533 NestedSolveDecision(DecisionBuilder* const db, bool restore);
4534 ~NestedSolveDecision() override {}
4535 void Apply(Solver* const solver) override;
4536 void Refute(Solver* const solver) override;
4537 std::string DebugString() const override { return "NestedSolveDecision"; }
4538 int state() const { return state_; }
4539
4540 private:
4541 DecisionBuilder* const db_;
4542 bool restore_;
4543 std::vector<SearchMonitor*> monitors_;
4544 int state_;
4545};
4546
4547NestedSolveDecision::NestedSolveDecision(
4548 DecisionBuilder* const db, bool restore,
4549 const std::vector<SearchMonitor*>& monitors)
4550 : db_(db),
4551 restore_(restore),
4552 monitors_(monitors),
4553 state_(DECISION_PENDING) {
4554 CHECK(nullptr != db);
4555}
4556
4557NestedSolveDecision::NestedSolveDecision(DecisionBuilder* const db,
4558 bool restore)
4559 : db_(db), restore_(restore), state_(DECISION_PENDING) {
4560 CHECK(nullptr != db);
4561}
4562
4563void NestedSolveDecision::Apply(Solver* const solver) {
4564 CHECK(nullptr != solver);
4565 if (restore_) {
4566 if (solver->Solve(db_, monitors_)) {
4567 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FOUND));
4568 } else {
4569 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FAILED));
4570 }
4571 } else {
4572 if (solver->SolveAndCommit(db_, monitors_)) {
4573 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FOUND));
4574 } else {
4575 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FAILED));
4576 }
4577 }
4578}
4579
4580void NestedSolveDecision::Refute(Solver* const solver) {}
4581
4582// ----- Local search decision builder -----
4583
4584// Given a first solution (resulting from either an initial assignment or the
4585// result of a decision builder), it searches for neighbors using a local
4586// search operator. The first solution corresponds to the first leaf of the
4587// search.
4588// The local search applies to the variables contained either in the assignment
4589// or the vector of variables passed.
4590
4591class LocalSearch : public DecisionBuilder {
4592 public:
4593 LocalSearch(Assignment* const assignment, IntVar* objective,
4594 SolutionPool* const pool, LocalSearchOperator* const ls_operator,
4595 DecisionBuilder* const sub_decision_builder,
4596 RegularLimit* const limit,
4597 LocalSearchFilterManager* filter_manager);
4598 // TODO(user): find a way to not have to pass vars here: redundant with
4599 // variables in operators
4600 LocalSearch(const std::vector<IntVar*>& vars, IntVar* objective,
4601 SolutionPool* const pool, DecisionBuilder* const first_solution,
4602 LocalSearchOperator* const ls_operator,
4603 DecisionBuilder* const sub_decision_builder,
4604 RegularLimit* const limit,
4605 LocalSearchFilterManager* filter_manager);
4606 LocalSearch(const std::vector<IntVar*>& vars, IntVar* objective,
4607 SolutionPool* const pool, DecisionBuilder* const first_solution,
4608 DecisionBuilder* const first_solution_sub_decision_builder,
4609 LocalSearchOperator* const ls_operator,
4610 DecisionBuilder* const sub_decision_builder,
4611 RegularLimit* const limit,
4612 LocalSearchFilterManager* filter_manager);
4613 LocalSearch(const std::vector<SequenceVar*>& vars, IntVar* objective,
4614 SolutionPool* const pool, DecisionBuilder* const first_solution,
4615 LocalSearchOperator* const ls_operator,
4616 DecisionBuilder* const sub_decision_builder,
4617 RegularLimit* const limit,
4618 LocalSearchFilterManager* filter_manager);
4619 ~LocalSearch() override;
4620 Decision* Next(Solver* const solver) override;
4621 std::string DebugString() const override { return "LocalSearch"; }
4622 void Accept(ModelVisitor* const visitor) const override;
4623
4624 protected:
4625 void PushFirstSolutionDecision(DecisionBuilder* first_solution);
4626 void PushLocalSearchDecision();
4627
4628 private:
4629 Assignment* assignment_;
4630 IntVar* const objective_ = nullptr;
4631 SolutionPool* const pool_;
4632 LocalSearchOperator* const ls_operator_;
4633 DecisionBuilder* const first_solution_sub_decision_builder_;
4634 DecisionBuilder* const sub_decision_builder_;
4635 std::vector<NestedSolveDecision*> nested_decisions_;
4636 int nested_decision_index_;
4637 RegularLimit* const limit_;
4638 LocalSearchFilterManager* const filter_manager_;
4639 bool has_started_;
4640};
4641
4642LocalSearch::LocalSearch(Assignment* const assignment, IntVar* objective,
4643 SolutionPool* const pool,
4644 LocalSearchOperator* const ls_operator,
4645 DecisionBuilder* const sub_decision_builder,
4646 RegularLimit* const limit,
4647 LocalSearchFilterManager* filter_manager)
4648 : assignment_(nullptr),
4649 objective_(objective),
4650 pool_(pool),
4651 ls_operator_(ls_operator),
4652 first_solution_sub_decision_builder_(sub_decision_builder),
4653 sub_decision_builder_(sub_decision_builder),
4654 nested_decision_index_(0),
4655 limit_(limit),
4656 filter_manager_(filter_manager),
4657 has_started_(false) {
4658 CHECK(nullptr != assignment);
4659 CHECK(nullptr != ls_operator);
4660 Solver* const solver = assignment->solver();
4661 assignment_ = solver->GetOrCreateLocalSearchState();
4662 assignment_->Copy(assignment);
4663 DecisionBuilder* restore = solver->MakeRestoreAssignment(assignment);
4664 PushFirstSolutionDecision(restore);
4665 PushLocalSearchDecision();
4666}
4667
4668LocalSearch::LocalSearch(const std::vector<IntVar*>& vars, IntVar* objective,
4669 SolutionPool* const pool,
4670 DecisionBuilder* const first_solution,
4671 LocalSearchOperator* const ls_operator,
4672 DecisionBuilder* const sub_decision_builder,
4673 RegularLimit* const limit,
4674 LocalSearchFilterManager* filter_manager)
4675 : assignment_(nullptr),
4676 objective_(objective),
4677 pool_(pool),
4678 ls_operator_(ls_operator),
4679 first_solution_sub_decision_builder_(sub_decision_builder),
4680 sub_decision_builder_(sub_decision_builder),
4681 nested_decision_index_(0),
4682 limit_(limit),
4683 filter_manager_(filter_manager),
4684 has_started_(false) {
4685 CHECK(nullptr != first_solution);
4686 CHECK(nullptr != ls_operator);
4687 CHECK(!vars.empty());
4688 Solver* const solver = vars[0]->solver();
4689 assignment_ = solver->GetOrCreateLocalSearchState();
4690 assignment_->Add(vars);
4691 PushFirstSolutionDecision(first_solution);
4692 PushLocalSearchDecision();
4693}
4694
4695LocalSearch::LocalSearch(
4696 const std::vector<IntVar*>& vars, IntVar* objective,
4697 SolutionPool* const pool, DecisionBuilder* const first_solution,
4698 DecisionBuilder* const first_solution_sub_decision_builder,
4699 LocalSearchOperator* const ls_operator,
4700 DecisionBuilder* const sub_decision_builder, RegularLimit* const limit,
4701 LocalSearchFilterManager* filter_manager)
4702 : assignment_(nullptr),
4703 objective_(objective),
4704 pool_(pool),
4705 ls_operator_(ls_operator),
4706 first_solution_sub_decision_builder_(first_solution_sub_decision_builder),
4707 sub_decision_builder_(sub_decision_builder),
4708 nested_decision_index_(0),
4709 limit_(limit),
4710 filter_manager_(filter_manager),
4711 has_started_(false) {
4712 CHECK(nullptr != first_solution);
4713 CHECK(nullptr != ls_operator);
4714 CHECK(!vars.empty());
4715 Solver* const solver = vars[0]->solver();
4716 assignment_ = solver->GetOrCreateLocalSearchState();
4717 assignment_->Add(vars);
4718 PushFirstSolutionDecision(first_solution);
4719 PushLocalSearchDecision();
4720}
4721
4722LocalSearch::LocalSearch(const std::vector<SequenceVar*>& vars,
4723 IntVar* objective, SolutionPool* const pool,
4724 DecisionBuilder* const first_solution,
4725 LocalSearchOperator* const ls_operator,
4726 DecisionBuilder* const sub_decision_builder,
4727 RegularLimit* const limit,
4728 LocalSearchFilterManager* filter_manager)
4729 : assignment_(nullptr),
4730 objective_(objective),
4731 pool_(pool),
4732 ls_operator_(ls_operator),
4733 first_solution_sub_decision_builder_(sub_decision_builder),
4734 sub_decision_builder_(sub_decision_builder),
4735 nested_decision_index_(0),
4736 limit_(limit),
4737 filter_manager_(filter_manager),
4738 has_started_(false) {
4739 CHECK(nullptr != first_solution);
4740 CHECK(nullptr != ls_operator);
4741 CHECK(!vars.empty());
4742 Solver* const solver = vars[0]->solver();
4743 assignment_ = solver->GetOrCreateLocalSearchState();
4744 assignment_->Add(vars);
4745 PushFirstSolutionDecision(first_solution);
4746 PushLocalSearchDecision();
4747}
4748
4749LocalSearch::~LocalSearch() {}
4750
4751// Model Visitor support.
4752void LocalSearch::Accept(ModelVisitor* const visitor) const {
4753 DCHECK(assignment_ != nullptr);
4754 visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension);
4755 // We collect decision variables from the assignment.
4756 const std::vector<IntVarElement>& elements =
4757 assignment_->IntVarContainer().elements();
4758 if (!elements.empty()) {
4759 std::vector<IntVar*> vars;
4760 for (const IntVarElement& elem : elements) {
4761 vars.push_back(elem.Var());
4762 }
4763 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
4764 vars);
4765 }
4766 const std::vector<IntervalVarElement>& interval_elements =
4767 assignment_->IntervalVarContainer().elements();
4768 if (!interval_elements.empty()) {
4769 std::vector<IntervalVar*> interval_vars;
4770 for (const IntervalVarElement& elem : interval_elements) {
4771 interval_vars.push_back(elem.Var());
4772 }
4773 visitor->VisitIntervalArrayArgument(ModelVisitor::kIntervalsArgument,
4774 interval_vars);
4775 }
4776 visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension);
4777}
4778
4779// This is equivalent to a multi-restart decision builder
4780// TODO(user): abstract this from the local search part
4781// TODO(user): handle the case where the tree depth is not enough to hold
4782// all solutions.
4783
4784Decision* LocalSearch::Next(Solver* const solver) {
4785 CHECK(nullptr != solver);
4786 CHECK_LT(0, nested_decisions_.size());
4787 if (!has_started_) {
4788 nested_decision_index_ = 0;
4789 solver->SaveAndSetValue(&has_started_, true);
4790 } else if (nested_decision_index_ < 0) {
4791 solver->Fail();
4792 }
4793 NestedSolveDecision* decision = nested_decisions_[nested_decision_index_];
4794 const int state = decision->state();
4795 switch (state) {
4796 case NestedSolveDecision::DECISION_FAILED: {
4797 // A local optimum has been reached. The search will continue only if we
4798 // accept up-hill moves (due to metaheuristics). In this case we need to
4799 // reset neighborhood optimal routes.
4800 ls_operator_->Reset();
4801 if (!LocalOptimumReached(solver->ActiveSearch())) {
4802 nested_decision_index_ = -1; // Stop the search
4803 }
4804 solver->Fail();
4805 return nullptr;
4806 }
4807 case NestedSolveDecision::DECISION_PENDING: {
4808 // TODO(user): Find a way to make this balancing invisible to the
4809 // user (no increase in branch or fail counts for instance).
4810 const int32_t kLocalSearchBalancedTreeDepth = 32;
4811 const int depth = solver->SearchDepth();
4812 if (depth < kLocalSearchBalancedTreeDepth) {
4813 return solver->balancing_decision();
4814 }
4815 if (depth > kLocalSearchBalancedTreeDepth) {
4816 solver->Fail();
4817 }
4818 return decision;
4819 }
4820 case NestedSolveDecision::DECISION_FOUND: {
4821 // Next time go to next decision
4822 if (nested_decision_index_ + 1 < nested_decisions_.size()) {
4823 ++nested_decision_index_;
4824 }
4825 return nullptr;
4826 }
4827 default: {
4828 LOG(ERROR) << "Unknown local search state";
4829 return nullptr;
4830 }
4831 }
4832 return nullptr;
4833}
4834
4835void LocalSearch::PushFirstSolutionDecision(DecisionBuilder* first_solution) {
4836 CHECK(first_solution);
4837 Solver* const solver = assignment_->solver();
4838 DecisionBuilder* store = solver->MakeStoreAssignment(assignment_);
4839 DecisionBuilder* first_solution_and_store = solver->Compose(
4840 solver->MakeProfiledDecisionBuilderWrapper(first_solution),
4841 first_solution_sub_decision_builder_, store);
4842 std::vector<SearchMonitor*> monitor;
4843 monitor.push_back(limit_);
4844 nested_decisions_.push_back(solver->RevAlloc(
4845 new NestedSolveDecision(first_solution_and_store, false, monitor)));
4846}
4847
4848void LocalSearch::PushLocalSearchDecision() {
4849 Solver* const solver = assignment_->solver();
4850 DecisionBuilder* find_neighbors = solver->RevAlloc(
4851 new FindOneNeighbor(assignment_, objective_, pool_, ls_operator_,
4852 sub_decision_builder_, limit_, filter_manager_));
4853 nested_decisions_.push_back(
4854 solver->RevAlloc(new NestedSolveDecision(find_neighbors, false)));
4855}
4856
4857class DefaultSolutionPool : public SolutionPool {
4858 public:
4859 DefaultSolutionPool() {}
4860
4861 ~DefaultSolutionPool() override {}
4862
4863 void Initialize(Assignment* const assignment) override {
4864 reference_assignment_ = absl::make_unique<Assignment>(assignment);
4865 }
4866
4867 void RegisterNewSolution(Assignment* const assignment) override {
4868 reference_assignment_->CopyIntersection(assignment);
4869 }
4870
4871 void GetNextSolution(Assignment* const assignment) override {
4872 assignment->CopyIntersection(reference_assignment_.get());
4873 }
4874
4875 bool SyncNeeded(Assignment* const local_assignment) override { return false; }
4876
4877 std::string DebugString() const override { return "DefaultSolutionPool"; }
4878
4879 private:
4880 std::unique_ptr<Assignment> reference_assignment_;
4881};
4882} // namespace
4883
4885 return RevAlloc(new DefaultSolutionPool());
4886}
4887
4890 return RevAlloc(new LocalSearch(
4891 assignment, parameters->objective(), parameters->solution_pool(),
4892 parameters->ls_operator(), parameters->sub_decision_builder(),
4893 parameters->limit(), parameters->filter_manager()));
4894}
4895
4897 const std::vector<IntVar*>& vars, DecisionBuilder* first_solution,
4899 return RevAlloc(new LocalSearch(
4900 vars, parameters->objective(), parameters->solution_pool(),
4901 first_solution, parameters->ls_operator(),
4902 parameters->sub_decision_builder(), parameters->limit(),
4903 parameters->filter_manager()));
4904}
4905
4907 const std::vector<IntVar*>& vars, DecisionBuilder* first_solution,
4908 DecisionBuilder* first_solution_sub_decision_builder,
4910 return RevAlloc(new LocalSearch(
4911 vars, parameters->objective(), parameters->solution_pool(),
4912 first_solution, first_solution_sub_decision_builder,
4913 parameters->ls_operator(), parameters->sub_decision_builder(),
4914 parameters->limit(), parameters->filter_manager()));
4915}
4916
4918 const std::vector<SequenceVar*>& vars, DecisionBuilder* first_solution,
4920 return RevAlloc(new LocalSearch(
4921 vars, parameters->objective(), parameters->solution_pool(),
4922 first_solution, parameters->ls_operator(),
4923 parameters->sub_decision_builder(), parameters->limit(),
4924 parameters->filter_manager()));
4925}
4926} // 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:893
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:892
#define CHECK_LT(val1, val2)
Definition: base/logging.h:706
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
#define CHECK_GE(val1, val2)
Definition: base/logging.h:707
#define CHECK_GT(val1, val2)
Definition: base/logging.h:708
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:895
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:896
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:894
#define LOG(severity)
Definition: base/logging.h:420
#define DCHECK(condition)
Definition: base/logging.h:890
#define CHECK_LE(val1, val2)
Definition: base/logging.h:705
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:891
#define VLOG(verboselevel)
Definition: base/logging.h:984
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)
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
const std::vector< int > & ChangedLoops() const
void ChangePath(int path, const std::vector< ChainBounds > &chains)
void ChangeLoops(const std::vector< int > &new_loops)
NodeRange Nodes(int path) 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
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_
int arc
ABSL_FLAG(int, cp_local_search_sync_frequency, 16, "Frequency of checks for better solutions in the solution pool.")
int head_index
Solver::LocalSearchFilterBound filter_enum_
int index
int64_t synchronized_sum_
int64_t delta_sum_
int64_t *const delta_costs_
int tail_index
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.
int64_t demand
Definition: resource.cc:125
int64_t delta
Definition: resource.cc:1694
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
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.