OR-Tools  9.1
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 && !gtl::ContainsKey(found_bases, 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 (gtl::ContainsKey(breaks_set, node)) {
1536 breaks.push_back(node);
1537 meta_node_costs.push_back(cost);
1538 cost = 0;
1539 } else {
1540 cost = CapAdd(cost, evaluator_(node, next, node_path));
1541 }
1542 node = next;
1543 }
1544 meta_node_costs[0] += cost;
1545 CHECK_EQ(breaks.size(), tsp_size_);
1546 // Setup TSP cost matrix
1547 CHECK_EQ(meta_node_costs.size(), tsp_size_);
1548 for (int i = 0; i < tsp_size_; ++i) {
1549 cost_[i][0] =
1550 CapAdd(meta_node_costs[i],
1551 evaluator_(breaks[i], Next(breaks[tsp_size_ - 1]), node_path));
1552 for (int j = 1; j < tsp_size_; ++j) {
1553 cost_[i][j] =
1554 CapAdd(meta_node_costs[i],
1555 evaluator_(breaks[i], Next(breaks[j - 1]), node_path));
1556 }
1557 cost_[i][i] = 0;
1558 }
1559 // Solve TSP and inject solution in delta (only if it leads to a new solution)
1560 hamiltonian_path_solver_.ChangeCostMatrix(cost_);
1561 std::vector<PathNodeIndex> path;
1562 hamiltonian_path_solver_.TravelingSalesmanPath(&path);
1563 bool nochange = true;
1564 for (int i = 0; i < path.size() - 1; ++i) {
1565 if (path[i] != i) {
1566 nochange = false;
1567 break;
1568 }
1569 }
1570 if (nochange) {
1571 return false;
1572 }
1573 CHECK_EQ(0, path[path.size() - 1]);
1574 for (int i = 0; i < tsp_size_ - 1; ++i) {
1575 SetNext(breaks[path[i]], OldNext(breaks[path[i + 1] - 1]), node_path);
1576 }
1577 SetNext(breaks[path[tsp_size_ - 1]], OldNext(breaks[tsp_size_ - 1]),
1578 node_path);
1579 return true;
1580}
1581
1582// ----- Lin Kernighan -----
1583
1584// For each variable in vars, stores the 'size' pairs(i,j) with the smallest
1585// value according to evaluator, where i is the index of the variable in vars
1586// and j is in the domain of the variable.
1587// Note that the resulting pairs are sorted.
1588// Works in O(size) per variable on average (same approach as qsort)
1589
1591 public:
1593 const PathOperator& path_operator, int size);
1595 void Initialize();
1596 const std::vector<int>& Neighbors(int index) const;
1597
1598 virtual std::string DebugString() const { return "NearestNeighbors"; }
1599
1600 private:
1601 void ComputeNearest(int row);
1602
1603 std::vector<std::vector<int>> neighbors_;
1604 Solver::IndexEvaluator3 evaluator_;
1605 const PathOperator& path_operator_;
1606 const int size_;
1607 bool initialized_;
1608
1609 DISALLOW_COPY_AND_ASSIGN(NearestNeighbors);
1610};
1611
1613 const PathOperator& path_operator, int size)
1614 : evaluator_(std::move(evaluator)),
1615 path_operator_(path_operator),
1616 size_(size),
1617 initialized_(false) {}
1618
1620 // TODO(user): recompute if node changes path ?
1621 if (!initialized_) {
1622 initialized_ = true;
1623 for (int i = 0; i < path_operator_.number_of_nexts(); ++i) {
1624 neighbors_.push_back(std::vector<int>());
1625 ComputeNearest(i);
1626 }
1627 }
1628}
1629
1630const std::vector<int>& NearestNeighbors::Neighbors(int index) const {
1631 return neighbors_[index];
1632}
1633
1634void NearestNeighbors::ComputeNearest(int row) {
1635 // Find size_ nearest neighbors for row of index 'row'.
1636 const int path = path_operator_.Path(row);
1637 const IntVar* var = path_operator_.Var(row);
1638 const int64_t var_min = var->Min();
1639 const int var_size = var->Max() - var_min + 1;
1640 using ValuedIndex = std::pair<int64_t /*value*/, int /*index*/>;
1641 std::vector<ValuedIndex> neighbors(var_size);
1642 for (int i = 0; i < var_size; ++i) {
1643 const int index = i + var_min;
1644 neighbors[i] = std::make_pair(evaluator_(row, index, path), index);
1645 }
1646 if (var_size > size_) {
1647 std::nth_element(neighbors.begin(), neighbors.begin() + size_ - 1,
1648 neighbors.end());
1649 }
1650
1651 // Setup global neighbor matrix for row row_index
1652 for (int i = 0; i < std::min(size_, var_size); ++i) {
1653 neighbors_[row].push_back(neighbors[i].second);
1654 }
1655 std::sort(neighbors_[row].begin(), neighbors_[row].end());
1656}
1657
1659 public:
1660 LinKernighan(const std::vector<IntVar*>& vars,
1661 const std::vector<IntVar*>& secondary_vars,
1662 const Solver::IndexEvaluator3& evaluator, bool topt);
1663 ~LinKernighan() override;
1664 bool MakeNeighbor() override;
1665
1666 std::string DebugString() const override { return "LinKernighan"; }
1667
1668 private:
1669 void OnNodeInitialization() override;
1670
1671 static const int kNeighbors;
1672
1673 bool InFromOut(int64_t in_i, int64_t in_j, int64_t* out, int64_t* gain);
1674
1675 Solver::IndexEvaluator3 const evaluator_;
1676 NearestNeighbors neighbors_;
1677 absl::flat_hash_set<int64_t> marked_;
1678 const bool topt_;
1679};
1680
1681// While the accumulated local gain is positive, perform a 2opt or a 3opt move
1682// followed by a series of 2opt moves. Return a neighbor for which the global
1683// gain is positive.
1684
1685LinKernighan::LinKernighan(const std::vector<IntVar*>& vars,
1686 const std::vector<IntVar*>& secondary_vars,
1687 const Solver::IndexEvaluator3& evaluator, bool topt)
1688 : PathOperator(vars, secondary_vars, 1, true, false, nullptr),
1689 evaluator_(evaluator),
1690 neighbors_(evaluator, *this, kNeighbors),
1691 topt_(topt) {}
1692
1694
1695void LinKernighan::OnNodeInitialization() { neighbors_.Initialize(); }
1696
1698 marked_.clear();
1699 int64_t node = BaseNode(0);
1700 int64_t path = Path(node);
1701 int64_t base = node;
1702 int64_t next = Next(node);
1703 if (IsPathEnd(next)) return false;
1704 int64_t out = -1;
1705 int64_t gain = 0;
1706 marked_.insert(node);
1707 if (topt_) { // Try a 3opt first
1708 if (!InFromOut(node, next, &out, &gain)) return false;
1709 marked_.insert(next);
1710 marked_.insert(out);
1711 const int64_t node1 = out;
1712 if (IsPathEnd(node1)) return false;
1713 const int64_t next1 = Next(node1);
1714 if (IsPathEnd(next1)) return false;
1715 if (!InFromOut(node1, next1, &out, &gain)) return false;
1716 marked_.insert(next1);
1717 marked_.insert(out);
1718 if (!CheckChainValidity(out, node1, node) || !MoveChain(out, node1, node)) {
1719 return false;
1720 }
1721 const int64_t next_out = Next(out);
1722 const int64_t in_cost = evaluator_(node, next_out, path);
1723 const int64_t out_cost = evaluator_(out, next_out, path);
1724 if (CapAdd(CapSub(gain, in_cost), out_cost) > 0) return true;
1725 node = out;
1726 if (IsPathEnd(node)) return false;
1727 next = next_out;
1728 if (IsPathEnd(next)) return false;
1729 }
1730 // Try 2opts
1731 while (InFromOut(node, next, &out, &gain)) {
1732 marked_.insert(next);
1733 marked_.insert(out);
1734 int64_t chain_last;
1735 if (!ReverseChain(node, out, &chain_last)) {
1736 return false;
1737 }
1738 int64_t in_cost = evaluator_(base, chain_last, path);
1739 int64_t out_cost = evaluator_(chain_last, out, path);
1740 if (CapAdd(CapSub(gain, in_cost), out_cost) > 0) {
1741 return true;
1742 }
1743 node = chain_last;
1744 if (IsPathEnd(node)) {
1745 return false;
1746 }
1747 next = out;
1748 if (IsPathEnd(next)) {
1749 return false;
1750 }
1751 }
1752 return false;
1753}
1754
1755const int LinKernighan::kNeighbors = 5 + 1;
1756
1757bool LinKernighan::InFromOut(int64_t in_i, int64_t in_j, int64_t* out,
1758 int64_t* gain) {
1759 const std::vector<int>& nexts = neighbors_.Neighbors(in_j);
1760 int64_t best_gain = std::numeric_limits<int64_t>::min();
1761 int64_t path = Path(in_i);
1762 int64_t out_cost = evaluator_(in_i, in_j, path);
1763 const int64_t current_gain = CapAdd(*gain, out_cost);
1764 for (int k = 0; k < nexts.size(); ++k) {
1765 const int64_t next = nexts[k];
1766 if (next != in_j) {
1767 int64_t in_cost = evaluator_(in_j, next, path);
1768 int64_t new_gain = CapSub(current_gain, in_cost);
1769 if (new_gain > 0 && next != Next(in_j) && marked_.count(in_j) == 0 &&
1770 marked_.count(next) == 0) {
1771 if (best_gain < new_gain) {
1772 *out = next;
1773 best_gain = new_gain;
1774 }
1775 }
1776 }
1777 }
1778 *gain = best_gain;
1779 return (best_gain > std::numeric_limits<int64_t>::min());
1780}
1781
1782// ----- Path-based Large Neighborhood Search -----
1783
1784// Breaks "number_of_chunks" chains of "chunk_size" arcs, and deactivate all
1785// inactive nodes if "unactive_fragments" is true.
1786// As a special case, if chunk_size=0, then we break full paths.
1787
1788class PathLns : public PathOperator {
1789 public:
1790 PathLns(const std::vector<IntVar*>& vars,
1791 const std::vector<IntVar*>& secondary_vars, int number_of_chunks,
1792 int chunk_size, bool unactive_fragments)
1793 : PathOperator(vars, secondary_vars, number_of_chunks, true, true,
1794 nullptr),
1795 number_of_chunks_(number_of_chunks),
1796 chunk_size_(chunk_size),
1797 unactive_fragments_(unactive_fragments) {
1798 CHECK_GE(chunk_size_, 0);
1799 }
1800 ~PathLns() override {}
1801 bool MakeNeighbor() override;
1802
1803 std::string DebugString() const override { return "PathLns"; }
1804 bool HasFragments() const override { return true; }
1805
1806 private:
1807 inline bool ChainsAreFullPaths() const { return chunk_size_ == 0; }
1808 void DeactivateChain(int64_t node);
1809 void DeactivateUnactives();
1810
1811 const int number_of_chunks_;
1812 const int chunk_size_;
1813 const bool unactive_fragments_;
1814};
1815
1817 if (ChainsAreFullPaths()) {
1818 // Reject the current position as a neighbor if any of its base node
1819 // isn't at the start of a path.
1820 // TODO(user): make this more efficient.
1821 for (int i = 0; i < number_of_chunks_; ++i) {
1822 if (BaseNode(i) != StartNode(i)) return false;
1823 }
1824 }
1825 for (int i = 0; i < number_of_chunks_; ++i) {
1826 DeactivateChain(BaseNode(i));
1827 }
1828 DeactivateUnactives();
1829 return true;
1830}
1831
1832void PathLns::DeactivateChain(int64_t node) {
1833 for (int i = 0, current = node;
1834 (ChainsAreFullPaths() || i < chunk_size_) && !IsPathEnd(current);
1835 ++i, current = Next(current)) {
1836 Deactivate(current);
1837 if (!ignore_path_vars_) {
1838 Deactivate(number_of_nexts_ + current);
1839 }
1840 }
1841}
1842
1843void PathLns::DeactivateUnactives() {
1844 if (unactive_fragments_) {
1845 for (int i = 0; i < Size(); ++i) {
1846 if (IsInactive(i)) {
1847 Deactivate(i);
1848 if (!ignore_path_vars_) {
1850 }
1851 }
1852 }
1853 }
1854}
1855
1856// ----- Limit the number of neighborhoods explored -----
1857
1859 public:
1860 NeighborhoodLimit(LocalSearchOperator* const op, int64_t limit)
1861 : operator_(op), limit_(limit), next_neighborhood_calls_(0) {
1862 CHECK(op != nullptr);
1863 CHECK_GT(limit, 0);
1864 }
1865
1866 void Start(const Assignment* assignment) override {
1867 next_neighborhood_calls_ = 0;
1868 operator_->Start(assignment);
1869 }
1870
1871 bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override {
1872 if (next_neighborhood_calls_ >= limit_) {
1873 return false;
1874 }
1875 ++next_neighborhood_calls_;
1876 return operator_->MakeNextNeighbor(delta, deltadelta);
1877 }
1878
1879 bool HoldsDelta() const override { return operator_->HoldsDelta(); }
1880
1881 std::string DebugString() const override { return "NeighborhoodLimit"; }
1882
1883 private:
1884 LocalSearchOperator* const operator_;
1885 const int64_t limit_;
1886 int64_t next_neighborhood_calls_;
1887};
1888
1890 LocalSearchOperator* const op, int64_t limit) {
1891 return RevAlloc(new NeighborhoodLimit(op, limit));
1892}
1893
1894// ----- Concatenation of operators -----
1895
1896namespace {
1897class CompoundOperator : public LocalSearchOperator {
1898 public:
1899 CompoundOperator(std::vector<LocalSearchOperator*> operators,
1900 std::function<int64_t(int, int)> evaluator);
1901 ~CompoundOperator() override {}
1902 void Reset() override;
1903 void Start(const Assignment* assignment) override;
1904 bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override;
1905 bool HasFragments() const override { return has_fragments_; }
1906 bool HoldsDelta() const override { return true; }
1907
1908 std::string DebugString() const override {
1909 return operators_.empty()
1910 ? ""
1911 : operators_[operator_indices_[index_]]->DebugString();
1912 }
1913 const LocalSearchOperator* Self() const override {
1914 return operators_.empty() ? this
1915 : operators_[operator_indices_[index_]]->Self();
1916 }
1917
1918 private:
1919 class OperatorComparator {
1920 public:
1921 OperatorComparator(std::function<int64_t(int, int)> evaluator,
1922 int active_operator)
1923 : evaluator_(std::move(evaluator)), active_operator_(active_operator) {}
1924 bool operator()(int lhs, int rhs) const {
1925 const int64_t lhs_value = Evaluate(lhs);
1926 const int64_t rhs_value = Evaluate(rhs);
1927 return lhs_value < rhs_value || (lhs_value == rhs_value && lhs < rhs);
1928 }
1929
1930 private:
1931 int64_t Evaluate(int operator_index) const {
1932 return evaluator_(active_operator_, operator_index);
1933 }
1934
1935 std::function<int64_t(int, int)> evaluator_;
1936 const int active_operator_;
1937 };
1938
1939 int64_t index_;
1940 std::vector<LocalSearchOperator*> operators_;
1941 std::vector<int> operator_indices_;
1942 std::function<int64_t(int, int)> evaluator_;
1943 Bitset64<> started_;
1944 const Assignment* start_assignment_;
1945 bool has_fragments_;
1946};
1947
1948CompoundOperator::CompoundOperator(std::vector<LocalSearchOperator*> operators,
1949 std::function<int64_t(int, int)> evaluator)
1950 : index_(0),
1951 operators_(std::move(operators)),
1952 evaluator_(std::move(evaluator)),
1953 started_(operators_.size()),
1954 start_assignment_(nullptr),
1955 has_fragments_(false) {
1956 operators_.erase(std::remove(operators_.begin(), operators_.end(), nullptr),
1957 operators_.end());
1958 operator_indices_.resize(operators_.size());
1959 std::iota(operator_indices_.begin(), operator_indices_.end(), 0);
1960 for (LocalSearchOperator* const op : operators_) {
1961 if (op->HasFragments()) {
1962 has_fragments_ = true;
1963 break;
1964 }
1965 }
1966}
1967
1968void CompoundOperator::Reset() {
1969 for (LocalSearchOperator* const op : operators_) {
1970 op->Reset();
1971 }
1972}
1973
1974void CompoundOperator::Start(const Assignment* assignment) {
1975 start_assignment_ = assignment;
1976 started_.ClearAll();
1977 if (!operators_.empty()) {
1978 OperatorComparator comparator(evaluator_, operator_indices_[index_]);
1979 std::sort(operator_indices_.begin(), operator_indices_.end(), comparator);
1980 index_ = 0;
1981 }
1982}
1983
1984bool CompoundOperator::MakeNextNeighbor(Assignment* delta,
1985 Assignment* deltadelta) {
1986 if (!operators_.empty()) {
1987 do {
1988 // TODO(user): keep copy of delta in case MakeNextNeighbor
1989 // pollutes delta on a fail.
1990 const int64_t operator_index = operator_indices_[index_];
1991 if (!started_[operator_index]) {
1992 operators_[operator_index]->Start(start_assignment_);
1993 started_.Set(operator_index);
1994 }
1995 if (!operators_[operator_index]->HoldsDelta()) {
1996 delta->Clear();
1997 }
1998 if (operators_[operator_index]->MakeNextNeighbor(delta, deltadelta)) {
1999 return true;
2000 }
2001 ++index_;
2002 delta->Clear();
2003 if (index_ == operators_.size()) {
2004 index_ = 0;
2005 }
2006 } while (index_ != 0);
2007 }
2008 return false;
2009}
2010
2011int64_t CompoundOperatorNoRestart(int size, int active_index,
2012 int operator_index) {
2013 return (operator_index < active_index) ? size + operator_index - active_index
2014 : operator_index - active_index;
2015}
2016
2017int64_t CompoundOperatorRestart(int active_index, int operator_index) {
2018 return 0;
2019}
2020} // namespace
2021
2023 const std::vector<LocalSearchOperator*>& ops) {
2024 return ConcatenateOperators(ops, false);
2025}
2026
2028 const std::vector<LocalSearchOperator*>& ops, bool restart) {
2029 if (restart) {
2030 std::function<int64_t(int, int)> eval = CompoundOperatorRestart;
2031 return ConcatenateOperators(ops, eval);
2032 }
2033 const int size = ops.size();
2034 return ConcatenateOperators(ops, [size](int i, int j) {
2035 return CompoundOperatorNoRestart(size, i, j);
2036 });
2037}
2038
2040 const std::vector<LocalSearchOperator*>& ops,
2041 std::function<int64_t(int, int)> evaluator) {
2042 return RevAlloc(new CompoundOperator(ops, std::move(evaluator)));
2043}
2044
2045namespace {
2046class RandomCompoundOperator : public LocalSearchOperator {
2047 public:
2048 explicit RandomCompoundOperator(std::vector<LocalSearchOperator*> operators);
2049 RandomCompoundOperator(std::vector<LocalSearchOperator*> operators,
2050 int32_t seed);
2051 ~RandomCompoundOperator() override {}
2052 void Reset() override;
2053 void Start(const Assignment* assignment) override;
2054 bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override;
2055 bool HoldsDelta() const override { return true; }
2056
2057 std::string DebugString() const override { return "RandomCompoundOperator"; }
2058 // TODO(user): define Self method.
2059
2060 private:
2061 std::mt19937 rand_;
2062 const std::vector<LocalSearchOperator*> operators_;
2063 bool has_fragments_;
2064};
2065
2066void RandomCompoundOperator::Start(const Assignment* assignment) {
2067 for (LocalSearchOperator* const op : operators_) {
2068 op->Start(assignment);
2069 }
2070}
2071
2072RandomCompoundOperator::RandomCompoundOperator(
2073 std::vector<LocalSearchOperator*> operators)
2074 : RandomCompoundOperator(std::move(operators), CpRandomSeed()) {}
2075
2076RandomCompoundOperator::RandomCompoundOperator(
2077 std::vector<LocalSearchOperator*> operators, int32_t seed)
2078 : rand_(seed), operators_(std::move(operators)), has_fragments_(false) {
2079 for (LocalSearchOperator* const op : operators_) {
2080 if (op->HasFragments()) {
2081 has_fragments_ = true;
2082 break;
2083 }
2084 }
2085}
2086
2087void RandomCompoundOperator::Reset() {
2088 for (LocalSearchOperator* const op : operators_) {
2089 op->Reset();
2090 }
2091}
2092
2093bool RandomCompoundOperator::MakeNextNeighbor(Assignment* delta,
2094 Assignment* deltadelta) {
2095 const int size = operators_.size();
2096 std::vector<int> indices(size);
2097 std::iota(indices.begin(), indices.end(), 0);
2098 std::shuffle(indices.begin(), indices.end(), rand_);
2099 for (int index : indices) {
2100 if (!operators_[index]->HoldsDelta()) {
2101 delta->Clear();
2102 }
2103 if (operators_[index]->MakeNextNeighbor(delta, deltadelta)) {
2104 return true;
2105 }
2106 delta->Clear();
2107 }
2108 return false;
2109}
2110} // namespace
2111
2113 const std::vector<LocalSearchOperator*>& ops) {
2114 return RevAlloc(new RandomCompoundOperator(ops));
2115}
2116
2118 const std::vector<LocalSearchOperator*>& ops, int32_t seed) {
2119 return RevAlloc(new RandomCompoundOperator(ops, seed));
2120}
2121
2122namespace {
2123class MultiArmedBanditCompoundOperator : public LocalSearchOperator {
2124 public:
2125 explicit MultiArmedBanditCompoundOperator(
2126 std::vector<LocalSearchOperator*> operators, double memory_coefficient,
2127 double exploration_coefficient, bool maximize);
2128 ~MultiArmedBanditCompoundOperator() override {}
2129 void Reset() override;
2130 void Start(const Assignment* assignment) override;
2131 bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override;
2132 bool HoldsDelta() const override { return true; }
2133
2134 std::string DebugString() const override {
2135 return operators_.empty()
2136 ? ""
2137 : operators_[operator_indices_[index_]]->DebugString();
2138 }
2139 const LocalSearchOperator* Self() const override {
2140 return operators_.empty() ? this
2141 : operators_[operator_indices_[index_]]->Self();
2142 }
2143
2144 private:
2145 double Score(int index);
2146 int index_;
2147 std::vector<LocalSearchOperator*> operators_;
2148 Bitset64<> started_;
2149 const Assignment* start_assignment_;
2150 bool has_fragments_;
2151 std::vector<int> operator_indices_;
2152 int64_t last_objective_;
2153 std::vector<double> avg_improvement_;
2154 int num_neighbors_;
2155 std::vector<double> num_neighbors_per_operator_;
2156 const bool maximize_;
2157 // Sets how much the objective improvement of previous accepted neighbors
2158 // influence the current average improvement. The formula is
2159 // avg_improvement +=
2160 // memory_coefficient * (current_improvement - avg_improvement).
2161 const double memory_coefficient_;
2162 // Sets how often we explore rarely used and unsuccessful in the past
2163 // operators. Operators are sorted by
2164 // avg_improvement_[i] + exploration_coefficient_ *
2165 // sqrt(2 * log(1 + num_neighbors_) / (1 + num_neighbors_per_operator_[i]));
2166 const double exploration_coefficient_;
2167};
2168
2169MultiArmedBanditCompoundOperator::MultiArmedBanditCompoundOperator(
2170 std::vector<LocalSearchOperator*> operators, double memory_coefficient,
2171 double exploration_coefficient, bool maximize)
2172 : index_(0),
2173 operators_(std::move(operators)),
2174 started_(operators_.size()),
2175 start_assignment_(nullptr),
2176 has_fragments_(false),
2177 last_objective_(std::numeric_limits<int64_t>::max()),
2178 num_neighbors_(0),
2179 maximize_(maximize),
2180 memory_coefficient_(memory_coefficient),
2181 exploration_coefficient_(exploration_coefficient) {
2182 DCHECK_GE(memory_coefficient_, 0);
2183 DCHECK_LE(memory_coefficient_, 1);
2184 DCHECK_GE(exploration_coefficient_, 0);
2185 operators_.erase(std::remove(operators_.begin(), operators_.end(), nullptr),
2186 operators_.end());
2187 operator_indices_.resize(operators_.size());
2188 std::iota(operator_indices_.begin(), operator_indices_.end(), 0);
2189 num_neighbors_per_operator_.resize(operators_.size(), 0);
2190 avg_improvement_.resize(operators_.size(), 0);
2191 for (LocalSearchOperator* const op : operators_) {
2192 if (op->HasFragments()) {
2193 has_fragments_ = true;
2194 break;
2195 }
2196 }
2197}
2198
2199void MultiArmedBanditCompoundOperator::Reset() {
2200 for (LocalSearchOperator* const op : operators_) {
2201 op->Reset();
2202 }
2203}
2204
2205double MultiArmedBanditCompoundOperator::Score(int index) {
2206 return avg_improvement_[index] +
2207 exploration_coefficient_ *
2208 sqrt(2 * log(1 + num_neighbors_) /
2209 (1 + num_neighbors_per_operator_[index]));
2210}
2211
2212void MultiArmedBanditCompoundOperator::Start(const Assignment* assignment) {
2213 start_assignment_ = assignment;
2214 started_.ClearAll();
2215 if (operators_.empty()) return;
2216
2217 const double objective = assignment->ObjectiveValue();
2218
2219 if (objective == last_objective_) return;
2220 // Skip a neighbor evaluation if last_objective_ hasn't been set yet.
2221 if (last_objective_ == std::numeric_limits<int64_t>::max()) {
2222 last_objective_ = objective;
2223 return;
2224 }
2225
2226 const double improvement =
2227 maximize_ ? objective - last_objective_ : last_objective_ - objective;
2228 if (improvement < 0) {
2229 return;
2230 }
2231 last_objective_ = objective;
2232 avg_improvement_[operator_indices_[index_]] +=
2233 memory_coefficient_ *
2234 (improvement - avg_improvement_[operator_indices_[index_]]);
2235
2236 std::sort(operator_indices_.begin(), operator_indices_.end(),
2237 [this](int lhs, int rhs) {
2238 const double lhs_score = Score(lhs);
2239 const double rhs_score = Score(rhs);
2240 return lhs_score > rhs_score ||
2241 (lhs_score == rhs_score && lhs < rhs);
2242 });
2243
2244 index_ = 0;
2245}
2246
2247bool MultiArmedBanditCompoundOperator::MakeNextNeighbor(
2248 Assignment* delta, Assignment* deltadelta) {
2249 if (operators_.empty()) return false;
2250 do {
2251 const int operator_index = operator_indices_[index_];
2252 if (!started_[operator_index]) {
2253 operators_[operator_index]->Start(start_assignment_);
2254 started_.Set(operator_index);
2255 }
2256 if (!operators_[operator_index]->HoldsDelta()) {
2257 delta->Clear();
2258 }
2259 if (operators_[operator_index]->MakeNextNeighbor(delta, deltadelta)) {
2260 ++num_neighbors_;
2261 ++num_neighbors_per_operator_[operator_index];
2262 return true;
2263 }
2264 ++index_;
2265 delta->Clear();
2266 if (index_ == operators_.size()) {
2267 index_ = 0;
2268 }
2269 } while (index_ != 0);
2270 return false;
2271}
2272} // namespace
2273
2275 const std::vector<LocalSearchOperator*>& ops, double memory_coefficient,
2276 double exploration_coefficient, bool maximize) {
2277 return RevAlloc(new MultiArmedBanditCompoundOperator(
2278 ops, memory_coefficient, exploration_coefficient, maximize));
2279}
2280
2281// ----- Operator factory -----
2282
2283template <class T>
2285 Solver* solver, const std::vector<IntVar*>& vars,
2286 const std::vector<IntVar*>& secondary_vars,
2287 std::function<int(int64_t)> start_empty_path_class) {
2288 return solver->RevAlloc(
2289 new T(vars, secondary_vars, std::move(start_empty_path_class)));
2290}
2291
2292#define MAKE_LOCAL_SEARCH_OPERATOR(OperatorClass) \
2293 template <> \
2294 LocalSearchOperator* MakeLocalSearchOperator<OperatorClass>( \
2295 Solver * solver, const std::vector<IntVar*>& vars, \
2296 const std::vector<IntVar*>& secondary_vars, \
2297 std::function<int(int64_t)> start_empty_path_class) { \
2298 return solver->RevAlloc(new OperatorClass( \
2299 vars, secondary_vars, std::move(start_empty_path_class))); \
2300 }
2301
2306MAKE_LOCAL_SEARCH_OPERATOR(MakeActiveOperator)
2307MAKE_LOCAL_SEARCH_OPERATOR(MakeInactiveOperator)
2308MAKE_LOCAL_SEARCH_OPERATOR(MakeChainInactiveOperator)
2309MAKE_LOCAL_SEARCH_OPERATOR(SwapActiveOperator)
2310MAKE_LOCAL_SEARCH_OPERATOR(ExtendedSwapActiveOperator)
2311MAKE_LOCAL_SEARCH_OPERATOR(MakeActiveAndRelocate)
2312MAKE_LOCAL_SEARCH_OPERATOR(RelocateAndMakeActiveOperator)
2313MAKE_LOCAL_SEARCH_OPERATOR(RelocateAndMakeInactiveOperator)
2314
2315#undef MAKE_LOCAL_SEARCH_OPERATOR
2316
2317LocalSearchOperator* Solver::MakeOperator(const std::vector<IntVar*>& vars,
2319 return MakeOperator(vars, std::vector<IntVar*>(), op);
2320}
2321
2323 const std::vector<IntVar*>& vars,
2324 const std::vector<IntVar*>& secondary_vars,
2326 LocalSearchOperator* result = nullptr;
2327 switch (op) {
2328 case Solver::TWOOPT: {
2329 result = RevAlloc(new TwoOpt(vars, secondary_vars, nullptr));
2330 break;
2331 }
2332 case Solver::OROPT: {
2333 std::vector<LocalSearchOperator*> operators;
2334 for (int i = 1; i < 4; ++i) {
2335 operators.push_back(RevAlloc(
2336 new Relocate(vars, secondary_vars, absl::StrCat("OrOpt<", i, ">"),
2337 nullptr, i, true)));
2338 }
2339 result = ConcatenateOperators(operators);
2340 break;
2341 }
2342 case Solver::RELOCATE: {
2343 result = MakeLocalSearchOperator<Relocate>(this, vars, secondary_vars,
2344 nullptr);
2345 break;
2346 }
2347 case Solver::EXCHANGE: {
2348 result = MakeLocalSearchOperator<Exchange>(this, vars, secondary_vars,
2349 nullptr);
2350 break;
2351 }
2352 case Solver::CROSS: {
2353 result =
2354 MakeLocalSearchOperator<Cross>(this, vars, secondary_vars, nullptr);
2355 break;
2356 }
2357 case Solver::MAKEACTIVE: {
2358 result = MakeLocalSearchOperator<MakeActiveOperator>(
2359 this, vars, secondary_vars, nullptr);
2360 break;
2361 }
2362 case Solver::MAKEINACTIVE: {
2363 result = MakeLocalSearchOperator<MakeInactiveOperator>(
2364 this, vars, secondary_vars, nullptr);
2365 break;
2366 }
2368 result = MakeLocalSearchOperator<MakeChainInactiveOperator>(
2369 this, vars, secondary_vars, nullptr);
2370 break;
2371 }
2372 case Solver::SWAPACTIVE: {
2373 result = MakeLocalSearchOperator<SwapActiveOperator>(
2374 this, vars, secondary_vars, nullptr);
2375 break;
2376 }
2378 result = MakeLocalSearchOperator<ExtendedSwapActiveOperator>(
2379 this, vars, secondary_vars, nullptr);
2380 break;
2381 }
2382 case Solver::PATHLNS: {
2383 result = RevAlloc(new PathLns(vars, secondary_vars, 2, 3, false));
2384 break;
2385 }
2386 case Solver::FULLPATHLNS: {
2387 result = RevAlloc(new PathLns(vars, secondary_vars,
2388 /*number_of_chunks=*/1,
2389 /*chunk_size=*/0,
2390 /*unactive_fragments=*/true));
2391 break;
2392 }
2393 case Solver::UNACTIVELNS: {
2394 result = RevAlloc(new PathLns(vars, secondary_vars, 1, 6, true));
2395 break;
2396 }
2397 case Solver::INCREMENT: {
2398 if (secondary_vars.empty()) {
2399 result = RevAlloc(new IncrementValue(vars));
2400 } else {
2401 LOG(FATAL) << "Operator " << op
2402 << " does not support secondary variables";
2403 }
2404 break;
2405 }
2406 case Solver::DECREMENT: {
2407 if (secondary_vars.empty()) {
2408 result = RevAlloc(new DecrementValue(vars));
2409 } else {
2410 LOG(FATAL) << "Operator " << op
2411 << " does not support secondary variables";
2412 }
2413 break;
2414 }
2415 case Solver::SIMPLELNS: {
2416 if (secondary_vars.empty()) {
2417 result = RevAlloc(new SimpleLns(vars, 1));
2418 } else {
2419 LOG(FATAL) << "Operator " << op
2420 << " does not support secondary variables";
2421 }
2422 break;
2423 }
2424 default:
2425 LOG(FATAL) << "Unknown operator " << op;
2426 }
2427 return result;
2428}
2429
2431 const std::vector<IntVar*>& vars, Solver::IndexEvaluator3 evaluator,
2433 return MakeOperator(vars, std::vector<IntVar*>(), std::move(evaluator), op);
2434}
2435
2437 const std::vector<IntVar*>& vars,
2438 const std::vector<IntVar*>& secondary_vars,
2439 Solver::IndexEvaluator3 evaluator,
2441 LocalSearchOperator* result = nullptr;
2442 switch (op) {
2443 case Solver::LK: {
2444 std::vector<LocalSearchOperator*> operators;
2445 operators.push_back(RevAlloc(
2446 new LinKernighan(vars, secondary_vars, evaluator, /*topt=*/false)));
2447 operators.push_back(RevAlloc(
2448 new LinKernighan(vars, secondary_vars, evaluator, /*topt=*/true)));
2449 result = ConcatenateOperators(operators);
2450 break;
2451 }
2452 case Solver::TSPOPT: {
2453 result = RevAlloc(
2454 new TSPOpt(vars, secondary_vars, evaluator,
2455 absl::GetFlag(FLAGS_cp_local_search_tsp_opt_size)));
2456 break;
2457 }
2458 case Solver::TSPLNS: {
2459 result = RevAlloc(
2460 new TSPLns(vars, secondary_vars, evaluator,
2461 absl::GetFlag(FLAGS_cp_local_search_tsp_lns_size)));
2462 break;
2463 }
2464 default:
2465 LOG(FATAL) << "Unknown operator " << op;
2466 }
2467 return result;
2468}
2469
2470namespace {
2471// Classes for Local Search Operation used in Local Search filters.
2472
2473class SumOperation {
2474 public:
2475 SumOperation() : value_(0) {}
2476 void Init() { value_ = 0; }
2477 void Update(int64_t update) { value_ = CapAdd(value_, update); }
2478 void Remove(int64_t remove) { value_ = CapSub(value_, remove); }
2479 int64_t value() const { return value_; }
2480 void set_value(int64_t new_value) { value_ = new_value; }
2481
2482 private:
2483 int64_t value_;
2484};
2485
2486class ProductOperation {
2487 public:
2488 ProductOperation() : value_(1) {}
2489 void Init() { value_ = 1; }
2490 void Update(int64_t update) { value_ *= update; }
2491 void Remove(int64_t remove) {
2492 if (remove != 0) {
2493 value_ /= remove;
2494 }
2495 }
2496 int64_t value() const { return value_; }
2497 void set_value(int64_t new_value) { value_ = new_value; }
2498
2499 private:
2500 int64_t value_;
2501};
2502
2503class MinOperation {
2504 public:
2505 void Init() { values_set_.clear(); }
2506 void Update(int64_t update) { values_set_.insert(update); }
2507 void Remove(int64_t remove) { values_set_.erase(remove); }
2508 int64_t value() const {
2509 return (!values_set_.empty()) ? *values_set_.begin() : 0;
2510 }
2511 void set_value(int64_t new_value) {}
2512
2513 private:
2514 std::set<int64_t> values_set_;
2515};
2516
2517class MaxOperation {
2518 public:
2519 void Init() { values_set_.clear(); }
2520 void Update(int64_t update) { values_set_.insert(update); }
2521 void Remove(int64_t remove) { values_set_.erase(remove); }
2522 int64_t value() const {
2523 return (!values_set_.empty()) ? *values_set_.rbegin() : 0;
2524 }
2525 void set_value(int64_t new_value) {}
2526
2527 private:
2528 std::set<int64_t> values_set_;
2529};
2530
2531// Always accepts deltas, cost 0.
2532class AcceptFilter : public LocalSearchFilter {
2533 public:
2534 std::string DebugString() const override { return "AcceptFilter"; }
2535 bool Accept(const Assignment* delta, const Assignment* deltadelta,
2536 int64_t obj_min, int64_t obj_max) override {
2537 return true;
2538 }
2539 void Synchronize(const Assignment* assignment,
2540 const Assignment* delta) override {}
2541};
2542} // namespace
2543
2545 return RevAlloc(new AcceptFilter());
2546}
2547
2548namespace {
2549// Never accepts deltas, cost 0.
2550class RejectFilter : public LocalSearchFilter {
2551 public:
2552 std::string DebugString() const override { return "RejectFilter"; }
2553 bool Accept(const Assignment* delta, const Assignment* deltadelta,
2554 int64_t obj_min, int64_t obj_max) override {
2555 return false;
2556 }
2557 void Synchronize(const Assignment* assignment,
2558 const Assignment* delta) override {}
2559};
2560} // namespace
2561
2563 return RevAlloc(new RejectFilter());
2564}
2565
2566PathState::PathState(int num_nodes, std::vector<int> path_start,
2567 std::vector<int> path_end)
2568 : num_nodes_(num_nodes),
2569 num_paths_(path_start.size()),
2570 num_nodes_threshold_(std::max(16, 4 * num_nodes_)) // Arbitrary value.
2571{
2572 DCHECK_EQ(path_start.size(), num_paths_);
2573 DCHECK_EQ(path_end.size(), num_paths_);
2574 for (int p = 0; p < num_paths_; ++p) {
2575 path_start_end_.push_back({path_start[p], path_end[p]});
2576 }
2577 // Initial state is all unperformed: paths go from start to end directly.
2578 committed_index_.assign(num_nodes_, -1);
2579 committed_nodes_.assign(2 * num_paths_, {-1, -1});
2580 chains_.assign(num_paths_ + 1, {-1, -1}); // Reserve 1 more for sentinel.
2581 paths_.assign(num_paths_, {-1, -1});
2582 for (int path = 0; path < num_paths_; ++path) {
2583 const int index = 2 * path;
2584 const PathStartEnd start_end = path_start_end_[path];
2585 committed_index_[start_end.start] = index;
2586 committed_index_[start_end.end] = index + 1;
2587
2588 committed_nodes_[index] = {start_end.start, path};
2589 committed_nodes_[index + 1] = {start_end.end, path};
2590
2591 chains_[path] = {index, index + 2};
2592 paths_[path] = {path, path + 1};
2593 }
2594 chains_[num_paths_] = {0, 0}; // Sentinel.
2595 // Nodes that are not starts or ends are loops.
2596 for (int node = 0; node < num_nodes_; ++node) {
2597 if (committed_index_[node] != -1) continue; // node is start or end.
2598 committed_index_[node] = committed_nodes_.size();
2599 committed_nodes_.push_back({node, -1});
2600 }
2601 path_has_changed_.assign(num_paths_, false);
2602}
2603
2605 const PathBounds bounds = paths_[path];
2606 return PathState::ChainRange(chains_.data() + bounds.begin_index,
2607 chains_.data() + bounds.end_index,
2608 committed_nodes_.data());
2609}
2610
2612 const PathBounds bounds = paths_[path];
2613 return PathState::NodeRange(chains_.data() + bounds.begin_index,
2614 chains_.data() + bounds.end_index,
2615 committed_nodes_.data());
2616}
2617
2618void PathState::MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm() {
2619 int num_visited_changed_arcs = 0;
2620 const int num_changed_arcs = tail_head_indices_.size();
2621 const int num_committed_nodes = committed_nodes_.size();
2622 // For every path, find all its chains.
2623 for (const int path : changed_paths_) {
2624 const int old_chain_size = chains_.size();
2625 const ChainBounds bounds = chains_[paths_[path].begin_index];
2626 const int start_index = bounds.begin_index;
2627 const int end_index = bounds.end_index - 1;
2628 int current_index = start_index;
2629 while (true) {
2630 // Look for smallest non-visited tail_index that is no smaller than
2631 // current_index.
2632 int selected_arc = -1;
2633 int selected_tail_index = num_committed_nodes;
2634 for (int i = num_visited_changed_arcs; i < num_changed_arcs; ++i) {
2635 const int tail_index = tail_head_indices_[i].tail_index;
2636 if (current_index <= tail_index && tail_index < selected_tail_index) {
2637 selected_arc = i;
2638 selected_tail_index = tail_index;
2639 }
2640 }
2641 // If there is no such tail index, or more generally if the next chain
2642 // would be cut by end of path,
2643 // stack {current_index, end_index + 1} in chains_, and go to next path.
2644 // Otherwise, stack {current_index, tail_index+1} in chains_,
2645 // set current_index = head_index, set pair to visited.
2646 if (start_index <= current_index && current_index <= end_index &&
2647 end_index < selected_tail_index) {
2648 chains_.emplace_back(current_index, end_index + 1);
2649 break;
2650 } else {
2651 chains_.emplace_back(current_index, selected_tail_index + 1);
2652 current_index = tail_head_indices_[selected_arc].head_index;
2653 std::swap(tail_head_indices_[num_visited_changed_arcs],
2654 tail_head_indices_[selected_arc]);
2655 ++num_visited_changed_arcs;
2656 }
2657 }
2658 const int new_chain_size = chains_.size();
2659 paths_[path] = {old_chain_size, new_chain_size};
2660 }
2661 chains_.emplace_back(0, 0); // Sentinel.
2662}
2663
2664void PathState::MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm() {
2665 // TRICKY: For each changed path, we want to generate a sequence of chains
2666 // that represents the path in the changed state.
2667 // First, notice that if we add a fake end->start arc for each changed path,
2668 // then all chains will be from the head of an arc to the tail of an arc.
2669 // A way to generate the changed chains and paths would be, for each path,
2670 // to start from a fake arc's head (the path start), go down the path until
2671 // the tail of an arc, and go to the next arc until we return on the fake arc,
2672 // enqueuing the [head, tail] chains as we go.
2673 // In turn, to do that, we need to know which arc to go to.
2674 // If we sort all heads and tails by index in two separate arrays,
2675 // the head_index and tail_index at the same rank are such that
2676 // [head_index, tail_index] is a chain. Moreover, the arc that must be visited
2677 // after head_index's arc is tail_index's arc.
2678
2679 // Add a fake end->start arc for each path.
2680 for (const int path : changed_paths_) {
2681 const PathStartEnd start_end = path_start_end_[path];
2682 tail_head_indices_.push_back(
2683 {committed_index_[start_end.end], committed_index_[start_end.start]});
2684 }
2685
2686 // Generate pairs (tail_index, arc) and (head_index, arc) for all arcs,
2687 // sort those pairs by index.
2688 const int num_arc_indices = tail_head_indices_.size();
2689 arcs_by_tail_index_.resize(num_arc_indices);
2690 arcs_by_head_index_.resize(num_arc_indices);
2691 for (int i = 0; i < num_arc_indices; ++i) {
2692 arcs_by_tail_index_[i] = {tail_head_indices_[i].tail_index, i};
2693 arcs_by_head_index_[i] = {tail_head_indices_[i].head_index, i};
2694 }
2695 std::sort(arcs_by_tail_index_.begin(), arcs_by_tail_index_.end());
2696 std::sort(arcs_by_head_index_.begin(), arcs_by_head_index_.end());
2697 // Generate the map from arc to next arc in path.
2698 next_arc_.resize(num_arc_indices);
2699 for (int i = 0; i < num_arc_indices; ++i) {
2700 next_arc_[arcs_by_head_index_[i].arc] = arcs_by_tail_index_[i].arc;
2701 }
2702
2703 // Generate chains: for every changed path, start from its fake arc,
2704 // jump to next_arc_ until going back to fake arc,
2705 // enqueuing chains as we go.
2706 const int first_fake_arc = num_arc_indices - changed_paths_.size();
2707 for (int fake_arc = first_fake_arc; fake_arc < num_arc_indices; ++fake_arc) {
2708 const int new_path_begin = chains_.size();
2709 int32_t arc = fake_arc;
2710 do {
2711 const int chain_begin = tail_head_indices_[arc].head_index;
2712 arc = next_arc_[arc];
2713 const int chain_end = tail_head_indices_[arc].tail_index + 1;
2714 chains_.emplace_back(chain_begin, chain_end);
2715 } while (arc != fake_arc);
2716 const int path = changed_paths_[fake_arc - first_fake_arc];
2717 const int new_path_end = chains_.size();
2718 paths_[path] = {new_path_begin, new_path_end};
2719 }
2720 chains_.emplace_back(0, 0); // Sentinel.
2721}
2722
2724 if (is_invalid_) return;
2725 // Filter out unchanged arcs from changed_arcs_,
2726 // translate changed arcs to changed arc indices.
2727 // Fill changed_paths_ while we hold node_path.
2728 DCHECK_EQ(chains_.size(), num_paths_ + 1); // One per path + sentinel.
2729 DCHECK(changed_paths_.empty());
2730 tail_head_indices_.clear();
2731 int num_changed_arcs = 0;
2732 for (const auto& arc : changed_arcs_) {
2733 int node, next;
2734 std::tie(node, next) = arc;
2735 const int node_index = committed_index_[node];
2736 const int next_index = committed_index_[next];
2737 const int node_path = committed_nodes_[node_index].path;
2738 if (next != node &&
2739 (next_index != node_index + 1 || node_path == -1)) { // New arc.
2740 tail_head_indices_.push_back({node_index, next_index});
2741 changed_arcs_[num_changed_arcs++] = {node, next};
2742 if (node_path != -1 && !path_has_changed_[node_path]) {
2743 path_has_changed_[node_path] = true;
2744 changed_paths_.push_back(node_path);
2745 }
2746 } else if (node == next && node_path != -1) { // New loop.
2747 changed_arcs_[num_changed_arcs++] = {node, node};
2748 }
2749 }
2750 changed_arcs_.resize(num_changed_arcs);
2751
2752 if (tail_head_indices_.size() + changed_paths_.size() <= 8) {
2753 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
2754 } else {
2755 MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
2756 }
2757}
2758
2760 DCHECK(!IsInvalid());
2761 if (committed_nodes_.size() < num_nodes_threshold_) {
2762 IncrementalCommit();
2763 } else {
2764 FullCommit();
2765 }
2766}
2767
2769 is_invalid_ = false;
2770 chains_.resize(num_paths_ + 1); // One per path + sentinel.
2771 for (const int path : changed_paths_) {
2772 paths_[path] = {path, path + 1};
2773 path_has_changed_[path] = false;
2774 }
2775 changed_paths_.clear();
2776 changed_arcs_.clear();
2777}
2778
2779void PathState::CopyNewPathAtEndOfNodes(int path) {
2780 // Copy path's nodes, chain by chain.
2781 const int new_path_begin_index = committed_nodes_.size();
2782 const PathBounds path_bounds = paths_[path];
2783 for (int i = path_bounds.begin_index; i < path_bounds.end_index; ++i) {
2784 const ChainBounds chain_bounds = chains_[i];
2785 committed_nodes_.insert(committed_nodes_.end(),
2786 committed_nodes_.data() + chain_bounds.begin_index,
2787 committed_nodes_.data() + chain_bounds.end_index);
2788 }
2789 const int new_path_end_index = committed_nodes_.size();
2790 // Set new nodes' path member to path.
2791 for (int i = new_path_begin_index; i < new_path_end_index; ++i) {
2792 committed_nodes_[i].path = path;
2793 }
2794}
2795
2796// TODO(user): Instead of copying paths at the end systematically,
2797// reuse some of the memory when possible.
2798void PathState::IncrementalCommit() {
2799 const int new_nodes_begin = committed_nodes_.size();
2800 for (const int path : ChangedPaths()) {
2801 const int chain_begin = committed_nodes_.size();
2802 CopyNewPathAtEndOfNodes(path);
2803 const int chain_end = committed_nodes_.size();
2804 chains_[path] = {chain_begin, chain_end};
2805 }
2806 // Re-index all copied nodes.
2807 const int new_nodes_end = committed_nodes_.size();
2808 for (int i = new_nodes_begin; i < new_nodes_end; ++i) {
2809 committed_index_[committed_nodes_[i].node] = i;
2810 }
2811 // New loops stay in place: only change their path to -1,
2812 // committed_index_ does not change.
2813 for (const auto& arc : ChangedArcs()) {
2814 int node, next;
2815 std::tie(node, next) = arc;
2816 if (node != next) continue;
2817 const int index = committed_index_[node];
2818 committed_nodes_[index].path = -1;
2819 }
2820 // Committed part of the state is set up, erase incremental changes.
2821 Revert();
2822}
2823
2824void PathState::FullCommit() {
2825 // Copy all paths at the end of committed_nodes_,
2826 // then remove all old committed_nodes_.
2827 const int old_num_nodes = committed_nodes_.size();
2828 for (int path = 0; path < num_paths_; ++path) {
2829 const int new_path_begin = committed_nodes_.size() - old_num_nodes;
2830 CopyNewPathAtEndOfNodes(path);
2831 const int new_path_end = committed_nodes_.size() - old_num_nodes;
2832 chains_[path] = {new_path_begin, new_path_end};
2833 }
2834 committed_nodes_.erase(committed_nodes_.begin(),
2835 committed_nodes_.begin() + old_num_nodes);
2836
2837 // Reindex path nodes, then loop nodes.
2838 constexpr int kUnindexed = -1;
2839 committed_index_.assign(num_nodes_, kUnindexed);
2840 int index = 0;
2841 for (const CommittedNode committed_node : committed_nodes_) {
2842 committed_index_[committed_node.node] = index++;
2843 }
2844 for (int node = 0; node < num_nodes_; ++node) {
2845 if (committed_index_[node] != kUnindexed) continue;
2846 committed_index_[node] = index++;
2847 committed_nodes_.push_back({node, -1});
2848 }
2849 // Committed part of the state is set up, erase incremental changes.
2850 Revert();
2851}
2852
2853namespace {
2854
2855class PathStateFilter : public LocalSearchFilter {
2856 public:
2857 std::string DebugString() const override { return "PathStateFilter"; }
2858 PathStateFilter(std::unique_ptr<PathState> path_state,
2859 const std::vector<IntVar*>& nexts);
2860 void Relax(const Assignment* delta, const Assignment* deltadelta) override;
2861 bool Accept(const Assignment* delta, const Assignment* deltadelta,
2862 int64_t objective_min, int64_t objective_max) override {
2863 return true;
2864 }
2865 void Synchronize(const Assignment* delta,
2866 const Assignment* deltadelta) override{};
2867 void Commit(const Assignment* assignment, const Assignment* delta) override;
2868 void Revert() override;
2869 void Reset() override;
2870
2871 private:
2872 const std::unique_ptr<PathState> path_state_;
2873 // Map IntVar* index to node, offset by the min index in nexts.
2874 std::vector<int> variable_index_to_node_;
2875 int index_offset_;
2876 // Used only in Reset(), this is a member variable to avoid reallocation.
2877 std::vector<bool> node_is_assigned_;
2878};
2879
2880PathStateFilter::PathStateFilter(std::unique_ptr<PathState> path_state,
2881 const std::vector<IntVar*>& nexts)
2882 : path_state_(std::move(path_state)) {
2883 {
2884 int min_index = std::numeric_limits<int>::max();
2885 int max_index = std::numeric_limits<int>::min();
2886 for (const IntVar* next : nexts) {
2887 const int index = next->index();
2888 min_index = std::min<int>(min_index, index);
2889 max_index = std::max<int>(max_index, index);
2890 }
2891 variable_index_to_node_.resize(max_index - min_index + 1, -1);
2892 index_offset_ = min_index;
2893 }
2894
2895 for (int node = 0; node < nexts.size(); ++node) {
2896 const int index = nexts[node]->index() - index_offset_;
2897 variable_index_to_node_[index] = node;
2898 }
2899}
2900
2901void PathStateFilter::Relax(const Assignment* delta,
2902 const Assignment* deltadelta) {
2903 path_state_->Revert();
2904 for (const IntVarElement& var_value : delta->IntVarContainer().elements()) {
2905 if (var_value.Var() == nullptr) continue;
2906 const int index = var_value.Var()->index() - index_offset_;
2907 if (index < 0 || variable_index_to_node_.size() <= index) continue;
2908 const int node = variable_index_to_node_[index];
2909 if (node == -1) continue;
2910 if (var_value.Bound()) {
2911 path_state_->ChangeNext(node, var_value.Value());
2912 } else {
2913 path_state_->Revert();
2914 path_state_->SetInvalid();
2915 break;
2916 }
2917 }
2918 path_state_->CutChains();
2919}
2920
2921void PathStateFilter::Reset() {
2922 path_state_->Revert();
2923 // Set all paths of path state to empty start -> end paths,
2924 // and all nonstart/nonend nodes to node -> node loops.
2925 const int num_nodes = path_state_->NumNodes();
2926 node_is_assigned_.assign(num_nodes, false);
2927 const int num_paths = path_state_->NumPaths();
2928 for (int path = 0; path < num_paths; ++path) {
2929 const int start = path_state_->Start(path);
2930 const int end = path_state_->End(path);
2931 path_state_->ChangeNext(start, end);
2932 node_is_assigned_[start] = true;
2933 node_is_assigned_[end] = true;
2934 }
2935 for (int node = 0; node < num_nodes; ++node) {
2936 if (!node_is_assigned_[node]) path_state_->ChangeNext(node, node);
2937 }
2938 path_state_->CutChains();
2939 path_state_->Commit();
2940}
2941
2942// The solver does not guarantee that a given Commit() corresponds to
2943// the previous Relax() (or that there has been a call to Relax()),
2944// so we replay the full change call sequence.
2945void PathStateFilter::Commit(const Assignment* assignment,
2946 const Assignment* delta) {
2947 path_state_->Revert();
2948 if (delta == nullptr || delta->Empty()) {
2949 Relax(assignment, nullptr);
2950 } else {
2951 Relax(delta, nullptr);
2952 }
2953 path_state_->Commit();
2954}
2955
2956void PathStateFilter::Revert() { path_state_->Revert(); }
2957
2958} // namespace
2959
2961 std::unique_ptr<PathState> path_state,
2962 const std::vector<IntVar*>& nexts) {
2963 PathStateFilter* filter = new PathStateFilter(std::move(path_state), nexts);
2964 return solver->RevAlloc(filter);
2965}
2966
2968 const PathState* path_state, std::vector<Interval> path_capacity,
2969 std::vector<int> path_class, std::vector<std::vector<Interval>> demand,
2970 std::vector<Interval> node_capacity)
2971 : path_state_(path_state),
2972 path_capacity_(std::move(path_capacity)),
2973 path_class_(std::move(path_class)),
2974 demand_(std::move(demand)),
2975 node_capacity_(std::move(node_capacity)),
2976 index_(path_state_->NumNodes(), 0),
2977 maximum_partial_demand_layer_size_(
2978 std::max(16, 4 * path_state_->NumNodes())) // 16 and 4 are arbitrary.
2979{
2980 const int num_nodes = path_state_->NumNodes();
2981 const int num_paths = path_state_->NumPaths();
2982 DCHECK_EQ(num_paths, path_capacity_.size());
2983 DCHECK_EQ(num_paths, path_class_.size());
2984 const int maximum_rmq_exponent = MostSignificantBitPosition32(num_nodes);
2985 partial_demand_sums_rmq_.resize(maximum_rmq_exponent + 1);
2986 previous_nontrivial_index_.reserve(maximum_partial_demand_layer_size_);
2987 FullCommit();
2988}
2989
2991 if (path_state_->IsInvalid()) return true;
2992 for (const int path : path_state_->ChangedPaths()) {
2993 const Interval path_capacity = path_capacity_[path];
2994 if (path_capacity.min == std::numeric_limits<int64_t>::min() &&
2995 path_capacity.max == std::numeric_limits<int64_t>::max()) {
2996 continue;
2997 }
2998 const int path_class = path_class_[path];
2999 // Loop invariant: capacity_used is nonempty and within path_capacity.
3000 Interval capacity_used = node_capacity_[path_state_->Start(path)];
3001 capacity_used = {std::max(capacity_used.min, path_capacity.min),
3002 std::min(capacity_used.max, path_capacity.max)};
3003 if (capacity_used.min > capacity_used.max) return false;
3004
3005 for (const auto chain : path_state_->Chains(path)) {
3006 const int first_node = chain.First();
3007 const int last_node = chain.Last();
3008
3009 const int first_index = index_[first_node];
3010 const int last_index = index_[last_node];
3011
3012 const int chain_path = path_state_->Path(first_node);
3013 const int chain_path_class =
3014 chain_path == -1 ? -1 : path_class_[chain_path];
3015 // Call the RMQ if the chain size is large enough;
3016 // the optimal value was found with the associated benchmark in tests,
3017 // in particular BM_UnaryDimensionChecker<ChangeSparsity::kSparse, *>.
3018 constexpr int kMinRangeSizeForRMQ = 4;
3019 if (last_index - first_index > kMinRangeSizeForRMQ &&
3020 path_class == chain_path_class &&
3021 SubpathOnlyHasTrivialNodes(first_index, last_index)) {
3022 // Compute feasible values of capacity_used that will not violate
3023 // path_capacity. This is done by considering the worst cases
3024 // using a range min/max query.
3025 const Interval min_max =
3026 GetMinMaxPartialDemandSum(first_index, last_index);
3027 const Interval prev_sum = partial_demand_sums_rmq_[0][first_index - 1];
3028 const Interval min_max_delta = {CapSub(min_max.min, prev_sum.min),
3029 CapSub(min_max.max, prev_sum.max)};
3030 capacity_used = {
3031 std::max(capacity_used.min,
3032 CapSub(path_capacity.min, min_max_delta.min)),
3033 std::min(capacity_used.max,
3034 CapSub(path_capacity.max, min_max_delta.max))};
3035 if (capacity_used.min > capacity_used.max) return false;
3036 // Move to last node's state, which is valid since we did not return.
3037 const Interval last_sum = partial_demand_sums_rmq_[0][last_index];
3038 capacity_used = {
3039 CapSub(CapAdd(capacity_used.min, last_sum.min), prev_sum.min),
3040 CapSub(CapAdd(capacity_used.max, last_sum.max), prev_sum.max)};
3041 } else {
3042 for (const int node : chain) {
3043 const Interval node_capacity = node_capacity_[node];
3044 capacity_used = {std::max(capacity_used.min, node_capacity.min),
3045 std::min(capacity_used.max, node_capacity.max)};
3046 if (capacity_used.min > capacity_used.max) return false;
3047 const Interval demand = demand_[path_class][node];
3048 capacity_used = {CapAdd(capacity_used.min, demand.min),
3049 CapAdd(capacity_used.max, demand.max)};
3050 capacity_used = {std::max(capacity_used.min, path_capacity.min),
3051 std::min(capacity_used.max, path_capacity.max)};
3052 }
3053 }
3054 }
3055 if (std::max(capacity_used.min, path_capacity.min) >
3056 std::min(capacity_used.max, path_capacity.max)) {
3057 return false;
3058 }
3059 }
3060 return true;
3061}
3062
3064 const int current_layer_size = partial_demand_sums_rmq_[0].size();
3065 int change_size = path_state_->ChangedPaths().size();
3066 for (const int path : path_state_->ChangedPaths()) {
3067 for (const auto chain : path_state_->Chains(path)) {
3068 change_size += chain.NumNodes();
3069 }
3070 }
3071 if (current_layer_size + change_size <= maximum_partial_demand_layer_size_) {
3072 IncrementalCommit();
3073 } else {
3074 FullCommit();
3075 }
3076}
3077
3078void UnaryDimensionChecker::IncrementalCommit() {
3079 for (const int path : path_state_->ChangedPaths()) {
3080 const int begin_index = partial_demand_sums_rmq_[0].size();
3081 AppendPathDemandsToSums(path);
3082 UpdateRMQStructure(begin_index, partial_demand_sums_rmq_[0].size());
3083 }
3084}
3085
3086void UnaryDimensionChecker::FullCommit() {
3087 // Clear all structures.
3088 previous_nontrivial_index_.clear();
3089 for (auto& sums : partial_demand_sums_rmq_) sums.clear();
3090 // Append all paths.
3091 const int num_paths = path_state_->NumPaths();
3092 for (int path = 0; path < num_paths; ++path) {
3093 const int begin_index = partial_demand_sums_rmq_[0].size();
3094 AppendPathDemandsToSums(path);
3095 UpdateRMQStructure(begin_index, partial_demand_sums_rmq_[0].size());
3096 }
3097}
3098
3099void UnaryDimensionChecker::AppendPathDemandsToSums(int path) {
3100 const int path_class = path_class_[path];
3101 Interval demand_sum = {0, 0};
3102 int previous_nontrivial_index = -1;
3103 int index = partial_demand_sums_rmq_[0].size();
3104 // Value of partial_demand_sums_rmq_ at node_index-1 must be the sum
3105 // of all demands of nodes before node.
3106 partial_demand_sums_rmq_[0].push_back(demand_sum);
3107 previous_nontrivial_index_.push_back(-1);
3108 ++index;
3109
3110 for (const int node : path_state_->Nodes(path)) {
3111 index_[node] = index;
3112 const Interval demand = demand_[path_class][node];
3113 demand_sum = {CapAdd(demand_sum.min, demand.min),
3114 CapAdd(demand_sum.max, demand.max)};
3115 partial_demand_sums_rmq_[0].push_back(demand_sum);
3116
3117 const Interval node_capacity = node_capacity_[node];
3118 if (demand.min != demand.max ||
3119 node_capacity.min != std::numeric_limits<int64_t>::min() ||
3120 node_capacity.max != std::numeric_limits<int64_t>::max()) {
3121 previous_nontrivial_index = index;
3122 }
3123 previous_nontrivial_index_.push_back(previous_nontrivial_index);
3124 ++index;
3125 }
3126}
3127
3128void UnaryDimensionChecker::UpdateRMQStructure(int begin_index, int end_index) {
3129 // The max layer is the one used by
3130 // GetMinMaxPartialDemandSum(begin_index, end_index - 1).
3131 const int maximum_rmq_exponent =
3132 MostSignificantBitPosition32(end_index - 1 - begin_index);
3133 for (int layer = 1, window_size = 1; layer <= maximum_rmq_exponent;
3134 ++layer, window_size *= 2) {
3135 partial_demand_sums_rmq_[layer].resize(end_index);
3136 for (int i = begin_index; i < end_index - window_size; ++i) {
3137 const Interval i1 = partial_demand_sums_rmq_[layer - 1][i];
3138 const Interval i2 = partial_demand_sums_rmq_[layer - 1][i + window_size];
3139 partial_demand_sums_rmq_[layer][i] = {std::min(i1.min, i2.min),
3140 std::max(i1.max, i2.max)};
3141 }
3142 std::copy(
3143 partial_demand_sums_rmq_[layer - 1].begin() + end_index - window_size,
3144 partial_demand_sums_rmq_[layer - 1].begin() + end_index,
3145 partial_demand_sums_rmq_[layer].begin() + end_index - window_size);
3146 }
3147}
3148
3149// TODO(user): since this is called only when
3150// last_node_index - first_node_index is large enough,
3151// the lower layers of partial_demand_sums_rmq_ are never used.
3152// For instance, if this is only called when the range size is > 4
3153// and paths are <= 32 nodes long, then we only need layers 0, 2, 3, and 4.
3154// To compare, on a 512 < #nodes <= 1024 problem, this uses layers in [0, 10].
3155UnaryDimensionChecker::Interval
3156UnaryDimensionChecker::GetMinMaxPartialDemandSum(int first_node_index,
3157 int last_node_index) const {
3158 DCHECK_LE(0, first_node_index);
3159 DCHECK_LT(first_node_index, last_node_index);
3160 DCHECK_LT(last_node_index, partial_demand_sums_rmq_[0].size());
3161 // Find largest window_size = 2^layer such that
3162 // first_node_index < last_node_index - window_size + 1.
3163 const int layer =
3164 MostSignificantBitPosition32(last_node_index - first_node_index);
3165 const int window_size = 1 << layer;
3166 // Classical range min/max query in O(1).
3167 const Interval i1 = partial_demand_sums_rmq_[layer][first_node_index];
3168 const Interval i2 =
3169 partial_demand_sums_rmq_[layer][last_node_index - window_size + 1];
3170 return {std::min(i1.min, i2.min), std::max(i1.max, i2.max)};
3171}
3172
3173bool UnaryDimensionChecker::SubpathOnlyHasTrivialNodes(
3174 int first_node_index, int last_node_index) const {
3175 DCHECK_LE(0, first_node_index);
3176 DCHECK_LT(first_node_index, last_node_index);
3177 DCHECK_LT(last_node_index, previous_nontrivial_index_.size());
3178 return first_node_index > previous_nontrivial_index_[last_node_index];
3179}
3180
3181namespace {
3182
3183class UnaryDimensionFilter : public LocalSearchFilter {
3184 public:
3185 std::string DebugString() const override { return name_; }
3186 UnaryDimensionFilter(std::unique_ptr<UnaryDimensionChecker> checker,
3187 const std::string& dimension_name)
3188 : checker_(std::move(checker)),
3189 name_(absl::StrCat("UnaryDimensionFilter(", dimension_name, ")")) {}
3190
3191 bool Accept(const Assignment* delta, const Assignment* deltadelta,
3192 int64_t objective_min, int64_t objective_max) override {
3193 return checker_->Check();
3194 }
3195
3196 void Synchronize(const Assignment* assignment,
3197 const Assignment* delta) override {
3198 checker_->Commit();
3199 }
3200
3201 private:
3202 std::unique_ptr<UnaryDimensionChecker> checker_;
3203 const std::string name_;
3204};
3205
3206} // namespace
3207
3209 Solver* solver, std::unique_ptr<UnaryDimensionChecker> checker,
3210 const std::string& dimension_name) {
3211 UnaryDimensionFilter* filter =
3212 new UnaryDimensionFilter(std::move(checker), dimension_name);
3213 return solver->RevAlloc(filter);
3214}
3215
3216namespace {
3217// ----- Variable domain filter -----
3218// Rejects assignments to values outside the domain of variables
3219
3220class VariableDomainFilter : public LocalSearchFilter {
3221 public:
3222 VariableDomainFilter() {}
3223 ~VariableDomainFilter() override {}
3224 bool Accept(const Assignment* delta, const Assignment* deltadelta,
3225 int64_t objective_min, int64_t objective_max) override;
3226 void Synchronize(const Assignment* assignment,
3227 const Assignment* delta) override {}
3228
3229 std::string DebugString() const override { return "VariableDomainFilter"; }
3230};
3231
3232bool VariableDomainFilter::Accept(const Assignment* delta,
3233 const Assignment* deltadelta,
3234 int64_t objective_min,
3235 int64_t objective_max) {
3236 const Assignment::IntContainer& container = delta->IntVarContainer();
3237 const int size = container.Size();
3238 for (int i = 0; i < size; ++i) {
3239 const IntVarElement& element = container.Element(i);
3240 if (element.Activated() && !element.Var()->Contains(element.Value())) {
3241 return false;
3242 }
3243 }
3244 return true;
3245}
3246} // namespace
3247
3249 return RevAlloc(new VariableDomainFilter());
3250}
3251
3252// ----- IntVarLocalSearchFilter -----
3253
3254const int IntVarLocalSearchFilter::kUnassigned = -1;
3255
3257 const std::vector<IntVar*>& vars) {
3258 AddVars(vars);
3259}
3260
3261void IntVarLocalSearchFilter::AddVars(const std::vector<IntVar*>& vars) {
3262 if (!vars.empty()) {
3263 for (int i = 0; i < vars.size(); ++i) {
3264 const int index = vars[i]->index();
3265 if (index >= var_index_to_index_.size()) {
3266 var_index_to_index_.resize(index + 1, kUnassigned);
3267 }
3268 var_index_to_index_[index] = i + vars_.size();
3269 }
3270 vars_.insert(vars_.end(), vars.begin(), vars.end());
3271 values_.resize(vars_.size(), /*junk*/ 0);
3272 var_synced_.resize(vars_.size(), false);
3273 }
3274}
3275
3277
3279 const Assignment* delta) {
3280 if (delta == nullptr || delta->Empty()) {
3281 var_synced_.assign(var_synced_.size(), false);
3282 SynchronizeOnAssignment(assignment);
3283 } else {
3285 }
3287}
3288
3290 const Assignment* assignment) {
3291 const Assignment::IntContainer& container = assignment->IntVarContainer();
3292 const int size = container.Size();
3293 for (int i = 0; i < size; ++i) {
3294 const IntVarElement& element = container.Element(i);
3295 IntVar* const var = element.Var();
3296 if (var != nullptr) {
3297 if (i < vars_.size() && vars_[i] == var) {
3298 values_[i] = element.Value();
3299 var_synced_[i] = true;
3300 } else {
3301 const int64_t kUnallocated = -1;
3302 int64_t index = kUnallocated;
3303 if (FindIndex(var, &index)) {
3304 values_[index] = element.Value();
3305 var_synced_[index] = true;
3306 }
3307 }
3308 }
3309 }
3310}
3311
3312// ----- Sum Objective filter ------
3313// Maintains the sum of costs of variables, where the subclass implements
3314// CostOfSynchronizedVariable() and FillCostOfBoundDeltaVariable() to compute
3315// the cost of a variable depending on its value.
3316// An assignment is accepted by this filter if the total cost is allowed
3317// depending on the relation defined by filter_enum:
3318// - Solver::LE -> total_cost <= objective_max.
3319// - Solver::GE -> total_cost >= objective_min.
3320// - Solver::EQ -> the conjunction of LE and GE.
3321namespace {
3322class SumObjectiveFilter : public IntVarLocalSearchFilter {
3323 public:
3324 SumObjectiveFilter(const std::vector<IntVar*>& vars,
3327 primary_vars_size_(vars.size()),
3328 synchronized_costs_(new int64_t[vars.size()]),
3329 delta_costs_(new int64_t[vars.size()]),
3330 filter_enum_(filter_enum),
3331 synchronized_sum_(std::numeric_limits<int64_t>::min()),
3332 delta_sum_(std::numeric_limits<int64_t>::min()),
3333 incremental_(false) {
3334 for (int i = 0; i < vars.size(); ++i) {
3335 synchronized_costs_[i] = 0;
3336 delta_costs_[i] = 0;
3337 }
3338 }
3339 ~SumObjectiveFilter() override {
3340 delete[] synchronized_costs_;
3341 delete[] delta_costs_;
3342 }
3343 bool Accept(const Assignment* delta, const Assignment* deltadelta,
3344 int64_t objective_min, int64_t objective_max) override {
3345 if (delta == nullptr) {
3346 return false;
3347 }
3348 if (deltadelta->Empty()) {
3349 if (incremental_) {
3350 for (int i = 0; i < primary_vars_size_; ++i) {
3352 }
3354 }
3355 incremental_ = false;
3357 CostOfChanges(delta, synchronized_costs_, false));
3358 } else {
3359 if (incremental_) {
3360 delta_sum_ =
3361 CapAdd(delta_sum_, CostOfChanges(deltadelta, delta_costs_, true));
3362 } else {
3364 CostOfChanges(delta, synchronized_costs_, true));
3365 }
3366 incremental_ = true;
3367 }
3368 switch (filter_enum_) {
3369 case Solver::LE: {
3370 return delta_sum_ <= objective_max;
3371 }
3372 case Solver::GE: {
3373 return delta_sum_ >= objective_min;
3374 }
3375 case Solver::EQ: {
3376 return objective_min <= delta_sum_ && delta_sum_ <= objective_max;
3377 }
3378 default: {
3379 LOG(ERROR) << "Unknown local search filter enum value";
3380 return false;
3381 }
3382 }
3383 }
3384 // If the variable is synchronized, returns its associated cost, otherwise
3385 // returns 0.
3386 virtual int64_t CostOfSynchronizedVariable(int64_t index) = 0;
3387 // If the variable is bound, fills new_cost with the cost associated to the
3388 // variable's valuation in container, and returns true. Otherwise, fills
3389 // new_cost with 0, and returns false.
3390 virtual bool FillCostOfBoundDeltaVariable(
3391 const Assignment::IntContainer& container, int index,
3392 int* container_index, int64_t* new_cost) = 0;
3393 bool IsIncremental() const override { return true; }
3394
3395 std::string DebugString() const override { return "SumObjectiveFilter"; }
3396
3397 int64_t GetSynchronizedObjectiveValue() const override {
3398 return synchronized_sum_;
3399 }
3400 int64_t GetAcceptedObjectiveValue() const override { return delta_sum_; }
3401
3402 protected:
3404 int64_t* const synchronized_costs_;
3405 int64_t* const delta_costs_;
3408 int64_t delta_sum_;
3410
3411 private:
3412 void OnSynchronize(const Assignment* delta) override {
3414 for (int i = 0; i < primary_vars_size_; ++i) {
3415 const int64_t cost = CostOfSynchronizedVariable(i);
3417 delta_costs_[i] = cost;
3419 }
3421 incremental_ = false;
3422 }
3423 int64_t CostOfChanges(const Assignment* changes,
3424 const int64_t* const old_costs,
3425 bool cache_delta_values) {
3426 int64_t total_cost = 0;
3427 const Assignment::IntContainer& container = changes->IntVarContainer();
3428 const int size = container.Size();
3429 for (int i = 0; i < size; ++i) {
3430 const IntVarElement& new_element = container.Element(i);
3431 IntVar* const var = new_element.Var();
3432 int64_t index = -1;
3433 if (FindIndex(var, &index) && index < primary_vars_size_) {
3434 total_cost = CapSub(total_cost, old_costs[index]);
3435 int64_t new_cost = 0LL;
3436 if (FillCostOfBoundDeltaVariable(container, index, &i, &new_cost)) {
3437 total_cost = CapAdd(total_cost, new_cost);
3438 }
3439 if (cache_delta_values) {
3440 delta_costs_[index] = new_cost;
3441 }
3442 }
3443 }
3444 return total_cost;
3445 }
3446};
3447
3448class BinaryObjectiveFilter : public SumObjectiveFilter {
3449 public:
3450 BinaryObjectiveFilter(const std::vector<IntVar*>& vars,
3451 Solver::IndexEvaluator2 value_evaluator,
3453 : SumObjectiveFilter(vars, filter_enum),
3454 value_evaluator_(std::move(value_evaluator)) {}
3455 ~BinaryObjectiveFilter() override {}
3456 int64_t CostOfSynchronizedVariable(int64_t index) override {
3458 ? value_evaluator_(index, IntVarLocalSearchFilter::Value(index))
3459 : 0;
3460 }
3461 bool FillCostOfBoundDeltaVariable(const Assignment::IntContainer& container,
3462 int index, int* container_index,
3463 int64_t* new_cost) override {
3464 const IntVarElement& element = container.Element(*container_index);
3465 if (element.Activated()) {
3466 *new_cost = value_evaluator_(index, element.Value());
3467 return true;
3468 }
3469 const IntVar* var = element.Var();
3470 if (var->Bound()) {
3471 *new_cost = value_evaluator_(index, var->Min());
3472 return true;
3473 }
3474 *new_cost = 0;
3475 return false;
3476 }
3477
3478 private:
3479 Solver::IndexEvaluator2 value_evaluator_;
3480};
3481
3482class TernaryObjectiveFilter : public SumObjectiveFilter {
3483 public:
3484 TernaryObjectiveFilter(const std::vector<IntVar*>& vars,
3485 const std::vector<IntVar*>& secondary_vars,
3486 Solver::IndexEvaluator3 value_evaluator,
3488 : SumObjectiveFilter(vars, filter_enum),
3489 secondary_vars_offset_(vars.size()),
3490 value_evaluator_(std::move(value_evaluator)) {
3491 IntVarLocalSearchFilter::AddVars(secondary_vars);
3493 }
3494 ~TernaryObjectiveFilter() override {}
3495 int64_t CostOfSynchronizedVariable(int64_t index) override {
3496 DCHECK_LT(index, secondary_vars_offset_);
3498 ? value_evaluator_(index, IntVarLocalSearchFilter::Value(index),
3500 index + secondary_vars_offset_))
3501 : 0;
3502 }
3503 bool FillCostOfBoundDeltaVariable(const Assignment::IntContainer& container,
3504 int index, int* container_index,
3505 int64_t* new_cost) override {
3506 DCHECK_LT(index, secondary_vars_offset_);
3507 *new_cost = 0LL;
3508 const IntVarElement& element = container.Element(*container_index);
3509 const IntVar* secondary_var =
3510 IntVarLocalSearchFilter::Var(index + secondary_vars_offset_);
3511 if (element.Activated()) {
3512 const int64_t value = element.Value();
3513 int hint_index = *container_index + 1;
3514 if (hint_index < container.Size() &&
3515 secondary_var == container.Element(hint_index).Var()) {
3516 *new_cost = value_evaluator_(index, value,
3517 container.Element(hint_index).Value());
3518 *container_index = hint_index;
3519 } else {
3520 *new_cost = value_evaluator_(index, value,
3521 container.Element(secondary_var).Value());
3522 }
3523 return true;
3524 }
3525 const IntVar* var = element.Var();
3526 if (var->Bound() && secondary_var->Bound()) {
3527 *new_cost = value_evaluator_(index, var->Min(), secondary_var->Min());
3528 return true;
3529 }
3530 *new_cost = 0;
3531 return false;
3532 }
3533
3534 private:
3535 int secondary_vars_offset_;
3536 Solver::IndexEvaluator3 value_evaluator_;
3537};
3538} // namespace
3539
3541 const std::vector<IntVar*>& vars, Solver::IndexEvaluator2 values,
3542 Solver::LocalSearchFilterBound filter_enum) {
3543 return RevAlloc(
3544 new BinaryObjectiveFilter(vars, std::move(values), filter_enum));
3545}
3546
3548 const std::vector<IntVar*>& vars,
3549 const std::vector<IntVar*>& secondary_vars, Solver::IndexEvaluator3 values,
3550 Solver::LocalSearchFilterBound filter_enum) {
3551 return RevAlloc(new TernaryObjectiveFilter(vars, secondary_vars,
3552 std::move(values), filter_enum));
3553}
3554
3556 int64_t initial_max) {
3557 DCHECK(state_is_valid_);
3558 DCHECK_LE(initial_min, initial_max);
3559 initial_variable_bounds_.push_back({initial_min, initial_max});
3560 variable_bounds_.push_back({initial_min, initial_max});
3561 variable_is_relaxed_.push_back(false);
3562
3563 const int variable_index = variable_bounds_.size() - 1;
3564 return {this, variable_index};
3565}
3566
3567void LocalSearchState::RelaxVariableBounds(int variable_index) {
3568 DCHECK(state_is_valid_);
3569 DCHECK(0 <= variable_index && variable_index < variable_is_relaxed_.size());
3570 if (!variable_is_relaxed_[variable_index]) {
3571 variable_is_relaxed_[variable_index] = true;
3572 saved_variable_bounds_trail_.emplace_back(variable_bounds_[variable_index],
3573 variable_index);
3574 variable_bounds_[variable_index] = initial_variable_bounds_[variable_index];
3575 }
3576}
3577
3578int64_t LocalSearchState::VariableMin(int variable_index) const {
3579 DCHECK(state_is_valid_);
3580 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3581 return variable_bounds_[variable_index].min;
3582}
3583
3584int64_t LocalSearchState::VariableMax(int variable_index) const {
3585 DCHECK(state_is_valid_);
3586 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3587 return variable_bounds_[variable_index].max;
3588}
3589
3590bool LocalSearchState::TightenVariableMin(int variable_index,
3591 int64_t min_value) {
3592 DCHECK(state_is_valid_);
3593 DCHECK(variable_is_relaxed_[variable_index]);
3594 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3595 Bounds& bounds = variable_bounds_[variable_index];
3596 if (bounds.max < min_value) {
3597 state_is_valid_ = false;
3598 }
3599 bounds.min = std::max(bounds.min, min_value);
3600 return state_is_valid_;
3601}
3602
3603bool LocalSearchState::TightenVariableMax(int variable_index,
3604 int64_t max_value) {
3605 DCHECK(state_is_valid_);
3606 DCHECK(variable_is_relaxed_[variable_index]);
3607 DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3608 Bounds& bounds = variable_bounds_[variable_index];
3609 if (bounds.min > max_value) {
3610 state_is_valid_ = false;
3611 }
3612 bounds.max = std::min(bounds.max, max_value);
3613 return state_is_valid_;
3614}
3615
3616// TODO(user): When the class has more users, find a threshold ratio of
3617// saved/total variables under which a sparse clear would be more efficient
3618// for both Commit() and Revert().
3620 DCHECK(state_is_valid_);
3621 saved_variable_bounds_trail_.clear();
3622 variable_is_relaxed_.assign(variable_is_relaxed_.size(), false);
3623}
3624
3626 for (const auto& bounds_index : saved_variable_bounds_trail_) {
3627 DCHECK(variable_is_relaxed_[bounds_index.second]);
3628 variable_bounds_[bounds_index.second] = bounds_index.first;
3629 }
3630 saved_variable_bounds_trail_.clear();
3631 variable_is_relaxed_.assign(variable_is_relaxed_.size(), false);
3632 state_is_valid_ = true;
3633}
3634
3635// ----- LocalSearchProfiler -----
3636
3638 public:
3640 std::string DebugString() const override { return "LocalSearchProfiler"; }
3641 void RestartSearch() override {
3642 operator_stats_.clear();
3643 filter_stats_.clear();
3644 }
3645 void ExitSearch() override {
3646 // Update times for current operator when the search ends.
3647 if (solver()->TopLevelSearch() == solver()->ActiveSearch()) {
3648 UpdateTime();
3649 }
3650 }
3652 LocalSearchStatistics statistics_proto;
3653 std::vector<const LocalSearchOperator*> operators;
3654 for (const auto& stat : operator_stats_) {
3655 operators.push_back(stat.first);
3656 }
3657 std::sort(
3658 operators.begin(), operators.end(),
3659 [this](const LocalSearchOperator* op1, const LocalSearchOperator* op2) {
3660 return gtl::FindOrDie(operator_stats_, op1).neighbors >
3661 gtl::FindOrDie(operator_stats_, op2).neighbors;
3662 });
3663 for (const LocalSearchOperator* const op : operators) {
3664 const OperatorStats& stats = gtl::FindOrDie(operator_stats_, op);
3666 local_search_operator_statistics =
3667 statistics_proto.add_local_search_operator_statistics();
3668 local_search_operator_statistics->set_local_search_operator(
3669 op->DebugString());
3670 local_search_operator_statistics->set_num_neighbors(stats.neighbors);
3671 local_search_operator_statistics->set_num_filtered_neighbors(
3672 stats.filtered_neighbors);
3673 local_search_operator_statistics->set_num_accepted_neighbors(
3674 stats.accepted_neighbors);
3675 local_search_operator_statistics->set_duration_seconds(stats.seconds);
3676 }
3677 std::vector<const LocalSearchFilter*> filters;
3678 for (const auto& stat : filter_stats_) {
3679 filters.push_back(stat.first);
3680 }
3681 std::sort(filters.begin(), filters.end(),
3682 [this](const LocalSearchFilter* filter1,
3683 const LocalSearchFilter* filter2) {
3684 return gtl::FindOrDie(filter_stats_, filter1).calls >
3685 gtl::FindOrDie(filter_stats_, filter2).calls;
3686 });
3687 for (const LocalSearchFilter* const filter : filters) {
3688 const FilterStats& stats = gtl::FindOrDie(filter_stats_, filter);
3690 local_search_filter_statistics =
3691 statistics_proto.add_local_search_filter_statistics();
3692 local_search_filter_statistics->set_local_search_filter(
3693 filter->DebugString());
3694 local_search_filter_statistics->set_num_calls(stats.calls);
3695 local_search_filter_statistics->set_num_rejects(stats.rejects);
3696 local_search_filter_statistics->set_duration_seconds(stats.seconds);
3697 }
3698 statistics_proto.set_total_num_neighbors(solver()->neighbors());
3699 statistics_proto.set_total_num_filtered_neighbors(
3700 solver()->filtered_neighbors());
3701 statistics_proto.set_total_num_accepted_neighbors(
3702 solver()->accepted_neighbors());
3703 return statistics_proto;
3704 }
3705 std::string PrintOverview() const {
3706 size_t max_name_size = 0;
3707 std::vector<const LocalSearchOperator*> operators;
3708 for (const auto& stat : operator_stats_) {
3709 operators.push_back(stat.first);
3710 max_name_size =
3711 std::max(max_name_size, stat.first->DebugString().length());
3712 }
3713 std::sort(
3714 operators.begin(), operators.end(),
3715 [this](const LocalSearchOperator* op1, const LocalSearchOperator* op2) {
3716 return gtl::FindOrDie(operator_stats_, op1).neighbors >
3717 gtl::FindOrDie(operator_stats_, op2).neighbors;
3718 });
3719 std::string overview = "Local search operator statistics:\n";
3720 absl::StrAppendFormat(&overview,
3721 "%*s | Neighbors | Filtered | Accepted | Time (s)\n",
3722 max_name_size, "");
3723 OperatorStats total_stats;
3724 for (const LocalSearchOperator* const op : operators) {
3725 const OperatorStats& stats = gtl::FindOrDie(operator_stats_, op);
3726 const std::string& name = op->DebugString();
3727 absl::StrAppendFormat(&overview, "%*s | %9ld | %8ld | %8ld | %7.2g\n",
3728 max_name_size, name, stats.neighbors,
3729 stats.filtered_neighbors, stats.accepted_neighbors,
3730 stats.seconds);
3731 total_stats.neighbors += stats.neighbors;
3732 total_stats.filtered_neighbors += stats.filtered_neighbors;
3733 total_stats.accepted_neighbors += stats.accepted_neighbors;
3734 total_stats.seconds += stats.seconds;
3735 }
3736 absl::StrAppendFormat(&overview, "%*s | %9ld | %8ld | %8ld | %7.2g\n",
3737 max_name_size, "Total", total_stats.neighbors,
3738 total_stats.filtered_neighbors,
3739 total_stats.accepted_neighbors, total_stats.seconds);
3740 max_name_size = 0;
3741 std::vector<const LocalSearchFilter*> filters;
3742 for (const auto& stat : filter_stats_) {
3743 filters.push_back(stat.first);
3744 max_name_size =
3745 std::max(max_name_size, stat.first->DebugString().length());
3746 }
3747 std::sort(filters.begin(), filters.end(),
3748 [this](const LocalSearchFilter* filter1,
3749 const LocalSearchFilter* filter2) {
3750 return gtl::FindOrDie(filter_stats_, filter1).calls >
3751 gtl::FindOrDie(filter_stats_, filter2).calls;
3752 });
3753 absl::StrAppendFormat(&overview,
3754 "Local search filter statistics:\n%*s | Calls | "
3755 " Rejects | Time (s) "
3756 "| Rejects/s\n",
3757 max_name_size, "");
3758 FilterStats total_filter_stats;
3759 for (const LocalSearchFilter* const filter : filters) {
3760 const FilterStats& stats = gtl::FindOrDie(filter_stats_, filter);
3761 const std::string& name = filter->DebugString();
3762 absl::StrAppendFormat(&overview, "%*s | %9ld | %9ld | %7.2g | %7.2g\n",
3763 max_name_size, name, stats.calls, stats.rejects,
3764 stats.seconds, stats.rejects / stats.seconds);
3765 total_filter_stats.calls += stats.calls;
3766 total_filter_stats.rejects += stats.rejects;
3767 total_filter_stats.seconds += stats.seconds;
3768 }
3769 absl::StrAppendFormat(
3770 &overview, "%*s | %9ld | %9ld | %7.2g | %7.2g\n", max_name_size,
3771 "Total", total_filter_stats.calls, total_filter_stats.rejects,
3772 total_filter_stats.seconds,
3773 total_filter_stats.rejects / total_filter_stats.seconds);
3774 return overview;
3775 }
3776 void BeginOperatorStart() override {}
3777 void EndOperatorStart() override {}
3779 if (last_operator_ != op->Self()) {
3780 UpdateTime();
3781 last_operator_ = op->Self();
3782 }
3783 }
3784 void EndMakeNextNeighbor(const LocalSearchOperator* op, bool neighbor_found,
3785 const Assignment* delta,
3786 const Assignment* deltadelta) override {
3787 if (neighbor_found) {
3788 operator_stats_[op->Self()].neighbors++;
3789 }
3790 }
3791 void BeginFilterNeighbor(const LocalSearchOperator* op) override {}
3793 bool neighbor_found) override {
3794 if (neighbor_found) {
3795 operator_stats_[op->Self()].filtered_neighbors++;
3796 }
3797 }
3798 void BeginAcceptNeighbor(const LocalSearchOperator* op) override {}
3800 bool neighbor_found) override {
3801 if (neighbor_found) {
3802 operator_stats_[op->Self()].accepted_neighbors++;
3803 }
3804 }
3805 void BeginFiltering(const LocalSearchFilter* filter) override {
3806 filter_stats_[filter].calls++;
3807 filter_timer_.Start();
3808 }
3809 void EndFiltering(const LocalSearchFilter* filter, bool reject) override {
3810 filter_timer_.Stop();
3811 auto& stats = filter_stats_[filter];
3812 stats.seconds += filter_timer_.Get();
3813 if (reject) {
3814 stats.rejects++;
3815 }
3816 }
3817 void Install() override { SearchMonitor::Install(); }
3818
3819 private:
3820 void UpdateTime() {
3821 if (last_operator_ != nullptr) {
3822 timer_.Stop();
3823 operator_stats_[last_operator_].seconds += timer_.Get();
3824 }
3825 timer_.Start();
3826 }
3827
3828 struct OperatorStats {
3829 int64_t neighbors = 0;
3830 int64_t filtered_neighbors = 0;
3831 int64_t accepted_neighbors = 0;
3832 double seconds = 0;
3833 };
3834
3835 struct FilterStats {
3836 int64_t calls = 0;
3837 int64_t rejects = 0;
3838 double seconds = 0;
3839 };
3840 WallTimer timer_;
3841 WallTimer filter_timer_;
3842 const LocalSearchOperator* last_operator_ = nullptr;
3843 absl::flat_hash_map<const LocalSearchOperator*, OperatorStats>
3844 operator_stats_;
3845 absl::flat_hash_map<const LocalSearchFilter*, FilterStats> filter_stats_;
3846};
3847
3849 monitor->Install();
3850}
3851
3853 if (solver->IsLocalSearchProfilingEnabled()) {
3854 return new LocalSearchProfiler(solver);
3855 }
3856 return nullptr;
3857}
3858
3859void DeleteLocalSearchProfiler(LocalSearchProfiler* monitor) { delete monitor; }
3860
3861std::string Solver::LocalSearchProfile() const {
3862 if (local_search_profiler_ != nullptr) {
3863 return local_search_profiler_->PrintOverview();
3864 }
3865 return "";
3866}
3867
3869 if (local_search_profiler_ != nullptr) {
3870 return local_search_profiler_->ExportToLocalSearchStatistics();
3871 }
3872 return LocalSearchStatistics();
3873}
3874
3875void LocalSearchFilterManager::InitializeForcedEvents() {
3876 const int num_events = filter_events_.size();
3877 int next_forced_event = num_events;
3878 next_forced_events_.resize(num_events);
3879 for (int i = num_events - 1; i >= 0; --i) {
3880 next_forced_events_[i] = next_forced_event;
3881 if (filter_events_[i].filter->IsIncremental() ||
3882 (filter_events_[i].event_type == FilterEventType::kRelax &&
3883 next_forced_event != num_events)) {
3884 next_forced_event = i;
3885 }
3886 }
3887}
3888
3890 std::vector<LocalSearchFilter*> filters)
3891 : synchronized_value_(std::numeric_limits<int64_t>::min()),
3892 accepted_value_(std::numeric_limits<int64_t>::min()) {
3893 filter_events_.reserve(2 * filters.size());
3894 for (LocalSearchFilter* filter : filters) {
3895 filter_events_.push_back({filter, FilterEventType::kRelax});
3896 }
3897 for (LocalSearchFilter* filter : filters) {
3898 filter_events_.push_back({filter, FilterEventType::kAccept});
3899 }
3900 InitializeForcedEvents();
3901}
3902
3904 std::vector<FilterEvent> filter_events)
3905 : filter_events_(std::move(filter_events)),
3906 synchronized_value_(std::numeric_limits<int64_t>::min()),
3907 accepted_value_(std::numeric_limits<int64_t>::min()) {
3908 InitializeForcedEvents();
3909}
3910
3911// Filters' Revert() must be called in the reverse order in which their
3912// Relax() was called.
3914 for (int i = last_event_called_; i >= 0; --i) {
3915 auto [filter, event_type] = filter_events_[i];
3916 if (event_type == FilterEventType::kRelax) filter->Revert();
3917 }
3918 last_event_called_ = -1;
3919}
3920
3921// TODO(user): the behaviour of Accept relies on the initial order of
3922// filters having at most one filter with negative objective values,
3923// this could be fixed by having filters return their general bounds.
3925 const Assignment* delta,
3926 const Assignment* deltadelta,
3927 int64_t objective_min,
3928 int64_t objective_max) {
3929 Revert();
3930 accepted_value_ = 0;
3931 bool ok = true;
3932 const int num_events = filter_events_.size();
3933 for (int i = 0; i < num_events;) {
3934 last_event_called_ = i;
3935 auto [filter, event_type] = filter_events_[last_event_called_];
3936 switch (event_type) {
3937 case FilterEventType::kAccept: {
3938 if (monitor != nullptr) monitor->BeginFiltering(filter);
3939 const bool accept = filter->Accept(
3940 delta, deltadelta, CapSub(objective_min, accepted_value_),
3941 CapSub(objective_max, accepted_value_));
3942 ok &= accept;
3943 if (monitor != nullptr) monitor->EndFiltering(filter, !accept);
3944 if (ok) {
3945 accepted_value_ =
3946 CapAdd(accepted_value_, filter->GetAcceptedObjectiveValue());
3947 // TODO(user): handle objective min.
3948 ok = accepted_value_ <= objective_max;
3949 }
3950 break;
3951 }
3952 case FilterEventType::kRelax: {
3953 filter->Relax(delta, deltadelta);
3954 break;
3955 }
3956 default:
3957 LOG(FATAL) << "Unknown filter event type.";
3958 }
3959 // If the candidate is rejected, forced events must still be called.
3960 if (ok) {
3961 ++i;
3962 } else {
3963 i = next_forced_events_[i];
3964 }
3965 }
3966 return ok;
3967}
3968
3970 const Assignment* delta) {
3971 // If delta is nullptr or empty, then assignment may be a partial solution.
3972 // Send a signal to Relaxing filters to inform them,
3973 // so they can show the partial solution as a change from the empty solution.
3974 const bool reset_to_assignment = delta == nullptr || delta->Empty();
3975 // Relax in the forward direction.
3976 for (auto [filter, event_type] : filter_events_) {
3977 switch (event_type) {
3978 case FilterEventType::kAccept: {
3979 break;
3980 }
3981 case FilterEventType::kRelax: {
3982 if (reset_to_assignment) {
3983 filter->Reset();
3984 filter->Relax(assignment, nullptr);
3985 } else {
3986 filter->Relax(delta, nullptr);
3987 }
3988 break;
3989 }
3990 default:
3991 LOG(FATAL) << "Unknown filter event type.";
3992 }
3993 }
3994 // Synchronize/Commit backwards, so filters can read changes from their
3995 // dependencies before those are synchronized/committed.
3996 synchronized_value_ = 0;
3997 for (auto [filter, event_type] : ::gtl::reversed_view(filter_events_)) {
3998 switch (event_type) {
3999 case FilterEventType::kAccept: {
4000 filter->Synchronize(assignment, delta);
4001 synchronized_value_ = CapAdd(synchronized_value_,
4002 filter->GetSynchronizedObjectiveValue());
4003 break;
4004 }
4005 case FilterEventType::kRelax: {
4006 filter->Commit(assignment, delta);
4007 break;
4008 }
4009 default:
4010 LOG(FATAL) << "Unknown filter event type.";
4011 }
4012 }
4013}
4014
4015// ----- Finds a neighbor of the assignment passed -----
4016
4018 public:
4019 FindOneNeighbor(Assignment* const assignment, IntVar* objective,
4020 SolutionPool* const pool,
4021 LocalSearchOperator* const ls_operator,
4022 DecisionBuilder* const sub_decision_builder,
4023 const RegularLimit* const limit,
4024 LocalSearchFilterManager* filter_manager);
4025 ~FindOneNeighbor() override {}
4026 Decision* Next(Solver* const solver) override;
4027 std::string DebugString() const override { return "FindOneNeighbor"; }
4028
4029 private:
4030 bool FilterAccept(Solver* solver, Assignment* delta, Assignment* deltadelta,
4031 int64_t objective_min, int64_t objective_max);
4032 void SynchronizeAll(Solver* solver);
4033
4034 Assignment* const assignment_;
4035 IntVar* const objective_;
4036 std::unique_ptr<Assignment> reference_assignment_;
4037 SolutionPool* const pool_;
4038 LocalSearchOperator* const ls_operator_;
4039 DecisionBuilder* const sub_decision_builder_;
4040 RegularLimit* limit_;
4041 const RegularLimit* const original_limit_;
4042 bool neighbor_found_;
4043 LocalSearchFilterManager* const filter_manager_;
4044 int64_t solutions_since_last_check_;
4045 int64_t check_period_;
4046 Assignment last_checked_assignment_;
4047 bool has_checked_assignment_ = false;
4048};
4049
4050// reference_assignment_ is used to keep track of the last assignment on which
4051// operators were started, assignment_ corresponding to the last successful
4052// neighbor.
4054 IntVar* objective, SolutionPool* const pool,
4055 LocalSearchOperator* const ls_operator,
4056 DecisionBuilder* const sub_decision_builder,
4057 const RegularLimit* const limit,
4058 LocalSearchFilterManager* filter_manager)
4059 : assignment_(assignment),
4060 objective_(objective),
4061 reference_assignment_(new Assignment(assignment_)),
4062 pool_(pool),
4063 ls_operator_(ls_operator),
4064 sub_decision_builder_(sub_decision_builder),
4065 limit_(nullptr),
4066 original_limit_(limit),
4067 neighbor_found_(false),
4068 filter_manager_(filter_manager),
4069 solutions_since_last_check_(0),
4070 check_period_(
4071 assignment_->solver()->parameters().check_solution_period()),
4072 last_checked_assignment_(assignment) {
4073 CHECK(nullptr != assignment);
4074 CHECK(nullptr != ls_operator);
4075
4076 Solver* const solver = assignment_->solver();
4077 // If limit is nullptr, default limit is 1 solution
4078 if (nullptr == limit) {
4079 limit_ = solver->MakeSolutionsLimit(1);
4080 } else {
4081 limit_ = limit->MakeIdenticalClone();
4082 // TODO(user): Support skipping neighborhood checks for limits accepting
4083 // more than one solution (e.g. best accept). For now re-enabling systematic
4084 // checks.
4085 if (limit_->solutions() != 1) {
4086 VLOG(1) << "Disabling neighbor-check skipping outside of first accept.";
4087 check_period_ = 1;
4088 }
4089 }
4090 // TODO(user): Support skipping neighborhood checks with LNS (at least on
4091 // the non-LNS operators).
4092 if (ls_operator->HasFragments()) {
4093 VLOG(1) << "Disabling neighbor-check skipping for LNS.";
4094 check_period_ = 1;
4095 }
4096
4097 if (!reference_assignment_->HasObjective()) {
4098 reference_assignment_->AddObjective(objective_);
4099 }
4100}
4101
4103 CHECK(nullptr != solver);
4104
4105 if (original_limit_ != nullptr) {
4106 limit_->Copy(original_limit_);
4107 }
4108
4109 if (!last_checked_assignment_.HasObjective()) {
4110 last_checked_assignment_.AddObjective(assignment_->Objective());
4111 }
4112
4113 if (!neighbor_found_) {
4114 // Only called on the first call to Next(), reference_assignment_ has not
4115 // been synced with assignment_ yet
4116
4117 // Keeping the code in case a performance problem forces us to
4118 // use the old code with a zero test on pool_.
4119 // reference_assignment_->CopyIntersection(assignment_);
4120 pool_->Initialize(assignment_);
4121 SynchronizeAll(solver);
4122 }
4123
4124 {
4125 // Another assignment is needed to apply the delta
4126 Assignment* assignment_copy =
4127 solver->MakeAssignment(reference_assignment_.get());
4128 int counter = 0;
4129
4130 DecisionBuilder* restore = solver->MakeRestoreAssignment(assignment_copy);
4131 if (sub_decision_builder_) {
4132 restore = solver->Compose(restore, sub_decision_builder_);
4133 }
4134 Assignment* delta = solver->MakeAssignment();
4135 Assignment* deltadelta = solver->MakeAssignment();
4136 while (true) {
4137 if (!ls_operator_->HoldsDelta()) {
4138 delta->Clear();
4139 }
4140 delta->ClearObjective();
4141 deltadelta->Clear();
4142 solver->TopPeriodicCheck();
4143 if (++counter >= absl::GetFlag(FLAGS_cp_local_search_sync_frequency) &&
4144 pool_->SyncNeeded(reference_assignment_.get())) {
4145 // TODO(user) : SyncNeed(assignment_) ?
4146 counter = 0;
4147 SynchronizeAll(solver);
4148 }
4149
4150 bool has_neighbor = false;
4151 if (!limit_->Check()) {
4152 solver->GetLocalSearchMonitor()->BeginMakeNextNeighbor(ls_operator_);
4153 has_neighbor = ls_operator_->MakeNextNeighbor(delta, deltadelta);
4155 ls_operator_, has_neighbor, delta, deltadelta);
4156 }
4157
4158 if (has_neighbor && !solver->IsUncheckedSolutionLimitReached()) {
4159 solver->neighbors_ += 1;
4160 // All filters must be called for incrementality reasons.
4161 // Empty deltas must also be sent to incremental filters; can be needed
4162 // to resync filters on non-incremental (empty) moves.
4163 // TODO(user): Don't call both if no filter is incremental and one
4164 // of them returned false.
4165 solver->GetLocalSearchMonitor()->BeginFilterNeighbor(ls_operator_);
4166 const bool mh_filter =
4167 AcceptDelta(solver->ParentSearch(), delta, deltadelta);
4168 int64_t objective_min = std::numeric_limits<int64_t>::min();
4169 int64_t objective_max = std::numeric_limits<int64_t>::max();
4170 if (objective_) {
4171 objective_min = objective_->Min();
4172 objective_max = objective_->Max();
4173 }
4174 if (delta->HasObjective() && delta->Objective() == objective_) {
4175 objective_min = std::max(objective_min, delta->ObjectiveMin());
4176 objective_max = std::min(objective_max, delta->ObjectiveMax());
4177 }
4178 const bool move_filter = FilterAccept(solver, delta, deltadelta,
4179 objective_min, objective_max);
4181 ls_operator_, mh_filter && move_filter);
4182 if (!mh_filter || !move_filter) {
4183 if (filter_manager_ != nullptr) filter_manager_->Revert();
4184 continue;
4185 }
4186 solver->filtered_neighbors_ += 1;
4187 if (delta->HasObjective()) {
4188 if (!assignment_copy->HasObjective()) {
4189 assignment_copy->AddObjective(delta->Objective());
4190 }
4191 if (!assignment_->HasObjective()) {
4192 assignment_->AddObjective(delta->Objective());
4193 last_checked_assignment_.AddObjective(delta->Objective());
4194 }
4195 }
4196 assignment_copy->CopyIntersection(reference_assignment_.get());
4197 assignment_copy->CopyIntersection(delta);
4198 solver->GetLocalSearchMonitor()->BeginAcceptNeighbor(ls_operator_);
4199 const bool check_solution = (solutions_since_last_check_ == 0) ||
4200 !solver->UseFastLocalSearch() ||
4201 // LNS deltas need to be restored
4202 !delta->AreAllElementsBound();
4203 if (has_checked_assignment_) solutions_since_last_check_++;
4204 if (solutions_since_last_check_ >= check_period_) {
4205 solutions_since_last_check_ = 0;
4206 }
4207 const bool accept = !check_solution || solver->SolveAndCommit(restore);
4208 solver->GetLocalSearchMonitor()->EndAcceptNeighbor(ls_operator_,
4209 accept);
4210 if (accept) {
4211 solver->accepted_neighbors_ += 1;
4212 if (check_solution) {
4213 solver->SetSearchContext(solver->ParentSearch(),
4214 ls_operator_->DebugString());
4215 assignment_->Store();
4216 last_checked_assignment_.CopyIntersection(assignment_);
4217 neighbor_found_ = true;
4218 has_checked_assignment_ = true;
4219 return nullptr;
4220 }
4221 solver->SetSearchContext(solver->ActiveSearch(),
4222 ls_operator_->DebugString());
4223 assignment_->CopyIntersection(assignment_copy);
4224 assignment_->SetObjectiveValue(
4225 filter_manager_ ? filter_manager_->GetAcceptedObjectiveValue()
4226 : 0);
4227 // Advancing local search to the current solution without
4228 // checking.
4229 // TODO(user): support the case were limit_ accepts more than
4230 // one solution (e.g. best accept).
4231 AcceptUncheckedNeighbor(solver->ParentSearch());
4232 solver->IncrementUncheckedSolutionCounter();
4233 pool_->RegisterNewSolution(assignment_);
4234 SynchronizeAll(solver);
4235 // NOTE: SynchronizeAll() sets neighbor_found_ to false, force it
4236 // back to true when skipping checks.
4237 neighbor_found_ = true;
4238 } else {
4239 if (filter_manager_ != nullptr) filter_manager_->Revert();
4240 if (check_period_ > 1 && has_checked_assignment_) {
4241 // Filtering is not perfect, disabling fast local search and
4242 // resynchronizing with the last checked solution.
4243 // TODO(user): Restore state of local search operators to
4244 // make sure we are exploring neighbors in the same order. This can
4245 // affect the local optimum found.
4246 VLOG(1) << "Imperfect filtering detected, backtracking to last "
4247 "checked solution and checking all solutions.";
4248 check_period_ = 1;
4249 solutions_since_last_check_ = 0;
4250 pool_->RegisterNewSolution(&last_checked_assignment_);
4251 SynchronizeAll(solver);
4252 assignment_->CopyIntersection(&last_checked_assignment_);
4253 }
4254 }
4255 } else {
4256 if (neighbor_found_) {
4257 // In case the last checked assignment isn't the current one, restore
4258 // it to make sure the solver knows about it, especially if this is
4259 // the end of the search.
4260 // TODO(user): Compare assignments in addition to their cost.
4261 if (last_checked_assignment_.ObjectiveValue() !=
4262 assignment_->ObjectiveValue()) {
4263 // If restoring fails this means filtering is not perfect and the
4264 // solver will consider the last checked assignment.
4265 assignment_copy->CopyIntersection(assignment_);
4266 if (!solver->SolveAndCommit(restore)) solver->Fail();
4267 last_checked_assignment_.CopyIntersection(assignment_);
4268 has_checked_assignment_ = true;
4269 return nullptr;
4270 }
4271 AcceptNeighbor(solver->ParentSearch());
4272 // Keeping the code in case a performance problem forces us to
4273 // use the old code with a zero test on pool_.
4274 // reference_assignment_->CopyIntersection(assignment_);
4275 pool_->RegisterNewSolution(assignment_);
4276 SynchronizeAll(solver);
4277 } else {
4278 break;
4279 }
4280 }
4281 }
4282 }
4283 solver->Fail();
4284 return nullptr;
4285}
4286
4287bool FindOneNeighbor::FilterAccept(Solver* solver, Assignment* delta,
4288 Assignment* deltadelta,
4289 int64_t objective_min,
4290 int64_t objective_max) {
4291 if (filter_manager_ == nullptr) return true;
4292 LocalSearchMonitor* const monitor = solver->GetLocalSearchMonitor();
4293 return filter_manager_->Accept(monitor, delta, deltadelta, objective_min,
4294 objective_max);
4295}
4296
4297void FindOneNeighbor::SynchronizeAll(Solver* solver) {
4298 pool_->GetNextSolution(reference_assignment_.get());
4299 neighbor_found_ = false;
4300 limit_->Init();
4301 solver->GetLocalSearchMonitor()->BeginOperatorStart();
4302 ls_operator_->Start(reference_assignment_.get());
4303 if (filter_manager_ != nullptr) {
4304 filter_manager_->Synchronize(reference_assignment_.get(), nullptr);
4305 }
4306 solver->GetLocalSearchMonitor()->EndOperatorStart();
4307}
4308
4309// ---------- Local Search Phase Parameters ----------
4310
4312 public:
4316 RegularLimit* const limit,
4318 : objective_(objective),
4319 solution_pool_(pool),
4320 ls_operator_(ls_operator),
4321 sub_decision_builder_(sub_decision_builder),
4322 limit_(limit),
4323 filter_manager_(filter_manager) {}
4325 std::string DebugString() const override {
4326 return "LocalSearchPhaseParameters";
4327 }
4328
4329 IntVar* objective() const { return objective_; }
4330 SolutionPool* solution_pool() const { return solution_pool_; }
4331 LocalSearchOperator* ls_operator() const { return ls_operator_; }
4333 return sub_decision_builder_;
4334 }
4335 RegularLimit* limit() const { return limit_; }
4337 return filter_manager_;
4338 }
4339
4340 private:
4341 IntVar* const objective_;
4342 SolutionPool* const solution_pool_;
4343 LocalSearchOperator* const ls_operator_;
4344 DecisionBuilder* const sub_decision_builder_;
4345 RegularLimit* const limit_;
4346 LocalSearchFilterManager* const filter_manager_;
4347};
4348
4350 IntVar* objective, LocalSearchOperator* const ls_operator,
4351 DecisionBuilder* const sub_decision_builder) {
4353 ls_operator, sub_decision_builder,
4354 nullptr, nullptr);
4355}
4356
4358 IntVar* objective, LocalSearchOperator* const ls_operator,
4359 DecisionBuilder* const sub_decision_builder, RegularLimit* const limit) {
4361 ls_operator, sub_decision_builder,
4362 limit, nullptr);
4363}
4364
4366 IntVar* objective, LocalSearchOperator* const ls_operator,
4367 DecisionBuilder* const sub_decision_builder, RegularLimit* const limit,
4368 LocalSearchFilterManager* filter_manager) {
4370 ls_operator, sub_decision_builder,
4371 limit, filter_manager);
4372}
4373
4375 IntVar* objective, SolutionPool* const pool,
4376 LocalSearchOperator* const ls_operator,
4377 DecisionBuilder* const sub_decision_builder) {
4378 return MakeLocalSearchPhaseParameters(objective, pool, ls_operator,
4379 sub_decision_builder, nullptr, nullptr);
4380}
4381
4383 IntVar* objective, SolutionPool* const pool,
4384 LocalSearchOperator* const ls_operator,
4385 DecisionBuilder* const sub_decision_builder, RegularLimit* const limit) {
4386 return MakeLocalSearchPhaseParameters(objective, pool, ls_operator,
4387 sub_decision_builder, limit, nullptr);
4388}
4389
4391 IntVar* objective, SolutionPool* const pool,
4392 LocalSearchOperator* const ls_operator,
4393 DecisionBuilder* const sub_decision_builder, RegularLimit* const limit,
4394 LocalSearchFilterManager* filter_manager) {
4395 return RevAlloc(new LocalSearchPhaseParameters(objective, pool, ls_operator,
4396 sub_decision_builder, limit,
4397 filter_manager));
4398}
4399
4400namespace {
4401// ----- NestedSolve decision wrapper -----
4402
4403// This decision calls a nested Solve on the given DecisionBuilder in its
4404// left branch; does nothing in the left branch.
4405// The state of the decision corresponds to the result of the nested Solve:
4406// DECISION_PENDING - Nested Solve not called yet
4407// DECISION_FAILED - Nested Solve failed
4408// DECISION_FOUND - Nested Solve succeeded
4409
4410class NestedSolveDecision : public Decision {
4411 public:
4412 // This enum is used internally to tag states in the local search tree.
4413 enum StateType { DECISION_PENDING, DECISION_FAILED, DECISION_FOUND };
4414
4415 NestedSolveDecision(DecisionBuilder* const db, bool restore,
4416 const std::vector<SearchMonitor*>& monitors);
4417 NestedSolveDecision(DecisionBuilder* const db, bool restore);
4418 ~NestedSolveDecision() override {}
4419 void Apply(Solver* const solver) override;
4420 void Refute(Solver* const solver) override;
4421 std::string DebugString() const override { return "NestedSolveDecision"; }
4422 int state() const { return state_; }
4423
4424 private:
4425 DecisionBuilder* const db_;
4426 bool restore_;
4427 std::vector<SearchMonitor*> monitors_;
4428 int state_;
4429};
4430
4431NestedSolveDecision::NestedSolveDecision(
4432 DecisionBuilder* const db, bool restore,
4433 const std::vector<SearchMonitor*>& monitors)
4434 : db_(db),
4435 restore_(restore),
4436 monitors_(monitors),
4437 state_(DECISION_PENDING) {
4438 CHECK(nullptr != db);
4439}
4440
4441NestedSolveDecision::NestedSolveDecision(DecisionBuilder* const db,
4442 bool restore)
4443 : db_(db), restore_(restore), state_(DECISION_PENDING) {
4444 CHECK(nullptr != db);
4445}
4446
4447void NestedSolveDecision::Apply(Solver* const solver) {
4448 CHECK(nullptr != solver);
4449 if (restore_) {
4450 if (solver->Solve(db_, monitors_)) {
4451 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FOUND));
4452 } else {
4453 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FAILED));
4454 }
4455 } else {
4456 if (solver->SolveAndCommit(db_, monitors_)) {
4457 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FOUND));
4458 } else {
4459 solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FAILED));
4460 }
4461 }
4462}
4463
4464void NestedSolveDecision::Refute(Solver* const solver) {}
4465
4466// ----- Local search decision builder -----
4467
4468// Given a first solution (resulting from either an initial assignment or the
4469// result of a decision builder), it searches for neighbors using a local
4470// search operator. The first solution corresponds to the first leaf of the
4471// search.
4472// The local search applies to the variables contained either in the assignment
4473// or the vector of variables passed.
4474
4475class LocalSearch : public DecisionBuilder {
4476 public:
4477 LocalSearch(Assignment* const assignment, IntVar* objective,
4478 SolutionPool* const pool, LocalSearchOperator* const ls_operator,
4479 DecisionBuilder* const sub_decision_builder,
4480 RegularLimit* const limit,
4481 LocalSearchFilterManager* filter_manager);
4482 // TODO(user): find a way to not have to pass vars here: redundant with
4483 // variables in operators
4484 LocalSearch(const std::vector<IntVar*>& vars, IntVar* objective,
4485 SolutionPool* const pool, DecisionBuilder* const first_solution,
4486 LocalSearchOperator* const ls_operator,
4487 DecisionBuilder* const sub_decision_builder,
4488 RegularLimit* const limit,
4489 LocalSearchFilterManager* filter_manager);
4490 LocalSearch(const std::vector<IntVar*>& vars, IntVar* objective,
4491 SolutionPool* const pool, DecisionBuilder* const first_solution,
4492 DecisionBuilder* const first_solution_sub_decision_builder,
4493 LocalSearchOperator* const ls_operator,
4494 DecisionBuilder* const sub_decision_builder,
4495 RegularLimit* const limit,
4496 LocalSearchFilterManager* filter_manager);
4497 LocalSearch(const std::vector<SequenceVar*>& vars, IntVar* objective,
4498 SolutionPool* const pool, DecisionBuilder* const first_solution,
4499 LocalSearchOperator* const ls_operator,
4500 DecisionBuilder* const sub_decision_builder,
4501 RegularLimit* const limit,
4502 LocalSearchFilterManager* filter_manager);
4503 ~LocalSearch() override;
4504 Decision* Next(Solver* const solver) override;
4505 std::string DebugString() const override { return "LocalSearch"; }
4506 void Accept(ModelVisitor* const visitor) const override;
4507
4508 protected:
4509 void PushFirstSolutionDecision(DecisionBuilder* first_solution);
4510 void PushLocalSearchDecision();
4511
4512 private:
4513 Assignment* assignment_;
4514 IntVar* const objective_ = nullptr;
4515 SolutionPool* const pool_;
4516 LocalSearchOperator* const ls_operator_;
4517 DecisionBuilder* const first_solution_sub_decision_builder_;
4518 DecisionBuilder* const sub_decision_builder_;
4519 std::vector<NestedSolveDecision*> nested_decisions_;
4520 int nested_decision_index_;
4521 RegularLimit* const limit_;
4522 LocalSearchFilterManager* const filter_manager_;
4523 bool has_started_;
4524};
4525
4526LocalSearch::LocalSearch(Assignment* const assignment, IntVar* objective,
4527 SolutionPool* const pool,
4528 LocalSearchOperator* const ls_operator,
4529 DecisionBuilder* const sub_decision_builder,
4530 RegularLimit* const limit,
4531 LocalSearchFilterManager* filter_manager)
4532 : assignment_(nullptr),
4533 objective_(objective),
4534 pool_(pool),
4535 ls_operator_(ls_operator),
4536 first_solution_sub_decision_builder_(sub_decision_builder),
4537 sub_decision_builder_(sub_decision_builder),
4538 nested_decision_index_(0),
4539 limit_(limit),
4540 filter_manager_(filter_manager),
4541 has_started_(false) {
4542 CHECK(nullptr != assignment);
4543 CHECK(nullptr != ls_operator);
4544 Solver* const solver = assignment->solver();
4545 assignment_ = solver->GetOrCreateLocalSearchState();
4546 assignment_->Copy(assignment);
4547 DecisionBuilder* restore = solver->MakeRestoreAssignment(assignment);
4548 PushFirstSolutionDecision(restore);
4549 PushLocalSearchDecision();
4550}
4551
4552LocalSearch::LocalSearch(const std::vector<IntVar*>& vars, IntVar* objective,
4553 SolutionPool* const pool,
4554 DecisionBuilder* const first_solution,
4555 LocalSearchOperator* const ls_operator,
4556 DecisionBuilder* const sub_decision_builder,
4557 RegularLimit* const limit,
4558 LocalSearchFilterManager* filter_manager)
4559 : assignment_(nullptr),
4560 objective_(objective),
4561 pool_(pool),
4562 ls_operator_(ls_operator),
4563 first_solution_sub_decision_builder_(sub_decision_builder),
4564 sub_decision_builder_(sub_decision_builder),
4565 nested_decision_index_(0),
4566 limit_(limit),
4567 filter_manager_(filter_manager),
4568 has_started_(false) {
4569 CHECK(nullptr != first_solution);
4570 CHECK(nullptr != ls_operator);
4571 CHECK(!vars.empty());
4572 Solver* const solver = vars[0]->solver();
4573 assignment_ = solver->GetOrCreateLocalSearchState();
4574 assignment_->Add(vars);
4575 PushFirstSolutionDecision(first_solution);
4576 PushLocalSearchDecision();
4577}
4578
4579LocalSearch::LocalSearch(
4580 const std::vector<IntVar*>& vars, IntVar* objective,
4581 SolutionPool* const pool, DecisionBuilder* const first_solution,
4582 DecisionBuilder* const first_solution_sub_decision_builder,
4583 LocalSearchOperator* const ls_operator,
4584 DecisionBuilder* const sub_decision_builder, RegularLimit* const limit,
4585 LocalSearchFilterManager* filter_manager)
4586 : assignment_(nullptr),
4587 objective_(objective),
4588 pool_(pool),
4589 ls_operator_(ls_operator),
4590 first_solution_sub_decision_builder_(first_solution_sub_decision_builder),
4591 sub_decision_builder_(sub_decision_builder),
4592 nested_decision_index_(0),
4593 limit_(limit),
4594 filter_manager_(filter_manager),
4595 has_started_(false) {
4596 CHECK(nullptr != first_solution);
4597 CHECK(nullptr != ls_operator);
4598 CHECK(!vars.empty());
4599 Solver* const solver = vars[0]->solver();
4600 assignment_ = solver->GetOrCreateLocalSearchState();
4601 assignment_->Add(vars);
4602 PushFirstSolutionDecision(first_solution);
4603 PushLocalSearchDecision();
4604}
4605
4606LocalSearch::LocalSearch(const std::vector<SequenceVar*>& vars,
4607 IntVar* objective, SolutionPool* const pool,
4608 DecisionBuilder* const first_solution,
4609 LocalSearchOperator* const ls_operator,
4610 DecisionBuilder* const sub_decision_builder,
4611 RegularLimit* const limit,
4612 LocalSearchFilterManager* filter_manager)
4613 : assignment_(nullptr),
4614 objective_(objective),
4615 pool_(pool),
4616 ls_operator_(ls_operator),
4617 first_solution_sub_decision_builder_(sub_decision_builder),
4618 sub_decision_builder_(sub_decision_builder),
4619 nested_decision_index_(0),
4620 limit_(limit),
4621 filter_manager_(filter_manager),
4622 has_started_(false) {
4623 CHECK(nullptr != first_solution);
4624 CHECK(nullptr != ls_operator);
4625 CHECK(!vars.empty());
4626 Solver* const solver = vars[0]->solver();
4627 assignment_ = solver->GetOrCreateLocalSearchState();
4628 assignment_->Add(vars);
4629 PushFirstSolutionDecision(first_solution);
4630 PushLocalSearchDecision();
4631}
4632
4633LocalSearch::~LocalSearch() {}
4634
4635// Model Visitor support.
4636void LocalSearch::Accept(ModelVisitor* const visitor) const {
4637 DCHECK(assignment_ != nullptr);
4638 visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension);
4639 // We collect decision variables from the assignment.
4640 const std::vector<IntVarElement>& elements =
4641 assignment_->IntVarContainer().elements();
4642 if (!elements.empty()) {
4643 std::vector<IntVar*> vars;
4644 for (const IntVarElement& elem : elements) {
4645 vars.push_back(elem.Var());
4646 }
4647 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
4648 vars);
4649 }
4650 const std::vector<IntervalVarElement>& interval_elements =
4651 assignment_->IntervalVarContainer().elements();
4652 if (!interval_elements.empty()) {
4653 std::vector<IntervalVar*> interval_vars;
4654 for (const IntervalVarElement& elem : interval_elements) {
4655 interval_vars.push_back(elem.Var());
4656 }
4657 visitor->VisitIntervalArrayArgument(ModelVisitor::kIntervalsArgument,
4658 interval_vars);
4659 }
4660 visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension);
4661}
4662
4663// This is equivalent to a multi-restart decision builder
4664// TODO(user): abstract this from the local search part
4665// TODO(user): handle the case where the tree depth is not enough to hold
4666// all solutions.
4667
4668Decision* LocalSearch::Next(Solver* const solver) {
4669 CHECK(nullptr != solver);
4670 CHECK_LT(0, nested_decisions_.size());
4671 if (!has_started_) {
4672 nested_decision_index_ = 0;
4673 solver->SaveAndSetValue(&has_started_, true);
4674 } else if (nested_decision_index_ < 0) {
4675 solver->Fail();
4676 }
4677 NestedSolveDecision* decision = nested_decisions_[nested_decision_index_];
4678 const int state = decision->state();
4679 switch (state) {
4680 case NestedSolveDecision::DECISION_FAILED: {
4681 // A local optimum has been reached. The search will continue only if we
4682 // accept up-hill moves (due to metaheuristics). In this case we need to
4683 // reset neighborhood optimal routes.
4684 ls_operator_->Reset();
4685 if (!LocalOptimumReached(solver->ActiveSearch())) {
4686 nested_decision_index_ = -1; // Stop the search
4687 }
4688 solver->Fail();
4689 return nullptr;
4690 }
4691 case NestedSolveDecision::DECISION_PENDING: {
4692 // TODO(user): Find a way to make this balancing invisible to the
4693 // user (no increase in branch or fail counts for instance).
4694 const int32_t kLocalSearchBalancedTreeDepth = 32;
4695 const int depth = solver->SearchDepth();
4696 if (depth < kLocalSearchBalancedTreeDepth) {
4697 return solver->balancing_decision();
4698 }
4699 if (depth > kLocalSearchBalancedTreeDepth) {
4700 solver->Fail();
4701 }
4702 return decision;
4703 }
4704 case NestedSolveDecision::DECISION_FOUND: {
4705 // Next time go to next decision
4706 if (nested_decision_index_ + 1 < nested_decisions_.size()) {
4707 ++nested_decision_index_;
4708 }
4709 return nullptr;
4710 }
4711 default: {
4712 LOG(ERROR) << "Unknown local search state";
4713 return nullptr;
4714 }
4715 }
4716 return nullptr;
4717}
4718
4719void LocalSearch::PushFirstSolutionDecision(DecisionBuilder* first_solution) {
4720 CHECK(first_solution);
4721 Solver* const solver = assignment_->solver();
4722 DecisionBuilder* store = solver->MakeStoreAssignment(assignment_);
4723 DecisionBuilder* first_solution_and_store = solver->Compose(
4724 first_solution, first_solution_sub_decision_builder_, store);
4725 std::vector<SearchMonitor*> monitor;
4726 monitor.push_back(limit_);
4727 nested_decisions_.push_back(solver->RevAlloc(
4728 new NestedSolveDecision(first_solution_and_store, false, monitor)));
4729}
4730
4731void LocalSearch::PushLocalSearchDecision() {
4732 Solver* const solver = assignment_->solver();
4733 DecisionBuilder* find_neighbors = solver->RevAlloc(
4734 new FindOneNeighbor(assignment_, objective_, pool_, ls_operator_,
4735 sub_decision_builder_, limit_, filter_manager_));
4736 nested_decisions_.push_back(
4737 solver->RevAlloc(new NestedSolveDecision(find_neighbors, false)));
4738}
4739
4740class DefaultSolutionPool : public SolutionPool {
4741 public:
4742 DefaultSolutionPool() {}
4743
4744 ~DefaultSolutionPool() override {}
4745
4746 void Initialize(Assignment* const assignment) override {
4747 reference_assignment_ = absl::make_unique<Assignment>(assignment);
4748 }
4749
4750 void RegisterNewSolution(Assignment* const assignment) override {
4751 reference_assignment_->CopyIntersection(assignment);
4752 }
4753
4754 void GetNextSolution(Assignment* const assignment) override {
4755 assignment->CopyIntersection(reference_assignment_.get());
4756 }
4757
4758 bool SyncNeeded(Assignment* const local_assignment) override { return false; }
4759
4760 std::string DebugString() const override { return "DefaultSolutionPool"; }
4761
4762 private:
4763 std::unique_ptr<Assignment> reference_assignment_;
4764};
4765} // namespace
4766
4768 return RevAlloc(new DefaultSolutionPool());
4769}
4770
4773 return RevAlloc(new LocalSearch(
4774 assignment, parameters->objective(), parameters->solution_pool(),
4775 parameters->ls_operator(), parameters->sub_decision_builder(),
4776 parameters->limit(), parameters->filter_manager()));
4777}
4778
4780 const std::vector<IntVar*>& vars, DecisionBuilder* first_solution,
4782 return RevAlloc(new LocalSearch(
4783 vars, parameters->objective(), parameters->solution_pool(),
4784 first_solution, parameters->ls_operator(),
4785 parameters->sub_decision_builder(), parameters->limit(),
4786 parameters->filter_manager()));
4787}
4788
4790 const std::vector<IntVar*>& vars, DecisionBuilder* first_solution,
4791 DecisionBuilder* first_solution_sub_decision_builder,
4793 return RevAlloc(new LocalSearch(
4794 vars, parameters->objective(), parameters->solution_pool(),
4795 first_solution, first_solution_sub_decision_builder,
4796 parameters->ls_operator(), parameters->sub_decision_builder(),
4797 parameters->limit(), parameters->filter_manager()));
4798}
4799
4801 const std::vector<SequenceVar*>& vars, DecisionBuilder* first_solution,
4803 return RevAlloc(new LocalSearch(
4804 vars, parameters->objective(), parameters->solution_pool(),
4805 first_solution, parameters->ls_operator(),
4806 parameters->sub_decision_builder(), parameters->limit(),
4807 parameters->filter_manager()));
4808}
4809} // 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:491
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:888
#define CHECK_LT(val1, val2)
Definition: base/logging.h:701
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
#define CHECK_GE(val1, val2)
Definition: base/logging.h:702
#define CHECK_GT(val1, val2)
Definition: base/logging.h:703
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:890
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:891
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:889
#define LOG(severity)
Definition: base/logging.h:416
#define DCHECK(condition)
Definition: base/logging.h:885
#define CHECK_LE(val1, val2)
Definition: base/logging.h:700
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:886
#define VLOG(verboselevel)
Definition: base/logging.h:979
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.
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
LocalSearchVariable AddVariable(int64_t initial_min, int64_t initial_max)
::operations_research::LocalSearchStatistics_LocalSearchOperatorStatistics * add_local_search_operator_statistics()
void set_total_num_filtered_neighbors(::PROTOBUF_NAMESPACE_ID::int64 value)
void set_total_num_neighbors(::PROTOBUF_NAMESPACE_ID::int64 value)
::operations_research::LocalSearchStatistics_LocalSearchFilterStatistics * add_local_search_filter_statistics()
void set_total_num_accepted_neighbors(::PROTOBUF_NAMESPACE_ID::int64 value)
MakeActiveAndRelocate(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::string DebugString() const override
MakeActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::string DebugString() const override
int64_t GetBaseNodeRestartPosition(int base_index) override
Returns the index of the node to which the base node of index base_index must be set to when it reach...
bool OnSamePathAsPreviousBase(int64_t base_index) override
Returns true if a base node has to be on the same path as the "previous" base node (base node of inde...
MakeChainInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
MakeInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::string DebugString() const override
static const char kVariableGroupExtension[]
const std::vector< int > & Neighbors(int index) const
NearestNeighbors(Solver::IndexEvaluator3 evaluator, const PathOperator &path_operator, int size)
virtual std::string DebugString() const
bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta) override
NeighborhoodLimit(LocalSearchOperator *const op, int64_t limit)
void Start(const Assignment *assignment) override
std::string DebugString() const override
PathLns(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, int number_of_chunks, int chunk_size, bool unactive_fragments)
bool HasFragments() const override
std::string DebugString() const override
Base class of the local search operators dedicated to path modifications (a path is a set of nodes li...
int64_t StartNode(int i) const
Returns the start node of the ith base node.
bool IsInactive(int64_t node) const
Returns true if node is inactive.
virtual bool ConsiderAlternatives(int64_t base_index) const
Indicates if alternatives should be considered when iterating over base nodes.
int PathClass(int i) const
Returns the class of the path of the ith base node.
virtual void OnNodeInitialization()
Called by OnStart() after initializing node information.
virtual bool OnSamePathAsPreviousBase(int64_t base_index)
Returns true if a base node has to be on the same path as the "previous" base node (base node of inde...
int number_of_nexts() const
Number of next variables.
bool CheckChainValidity(int64_t before_chain, int64_t chain_end, int64_t exclude) const
Returns true if the chain is a valid path without cycles from before_chain to chain_end and does not ...
virtual bool RestartAtPathStartOnSynchronize()
When the operator is being synchronized with a new solution (when Start() is called),...
bool IsPathEnd(int64_t node) const
Returns true if node is the last node on the path; defined by the fact that node is outside the range...
int64_t Next(int64_t node) const
Returns the node after node in the current delta.
bool MoveChain(int64_t before_chain, int64_t chain_end, int64_t destination)
Moves the chain starting after the node before_chain and ending at the node chain_end after the node ...
bool MakeActive(int64_t node, int64_t destination)
Insert the inactive node after destination.
bool ReverseChain(int64_t before_chain, int64_t after_chain, int64_t *chain_last)
Reverses the chain starting after before_chain and ending before after_chain.
void SetNext(int64_t from, int64_t to, int64_t path)
Sets 'to' to be the node after 'from' on the given path.
int64_t Prev(int64_t node) const
Returns the node before node in the current delta.
int64_t OldNext(int64_t node) const
bool SkipUnchanged(int index) const override
bool SwapActiveAndInactive(int64_t active, int64_t inactive)
Replaces active by inactive in the current path, making active inactive.
void ResetPosition()
Reset the position of the operator to its position when Start() was last called; this can be used to ...
virtual int64_t GetBaseNodeRestartPosition(int base_index)
Returns the index of the node to which the base node of index base_index must be set to when it reach...
int64_t BaseNode(int i) const
Returns the ith base node of the operator.
int GetSiblingAlternativeIndex(int node) const
Returns the index of the alternative set of the sibling of node.
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
int64_t Path(int64_t node) const
Returns the index of the path to which node belongs in the current delta.
virtual bool InitPosition() const
Returns true if the operator needs to restart its initial position at each call to Start()
PathOperator(const std::vector< IntVar * > &next_vars, const std::vector< IntVar * > &path_vars, IterationParameters iteration_parameters)
Builds an instance of PathOperator from next and path variables.
bool MakeChainInactive(int64_t before_chain, int64_t chain_end)
Makes the nodes on the chain starting after before_chain and ending at chain_end inactive.
const std::vector< int > & ChangedPaths() const
NodeRange Nodes(int path) const
const std::vector< std::pair< int, int > > & ChangedArcs() const
ChainRange Chains(int path) const
PathState(int num_nodes, std::vector< int > path_start, std::vector< int > path_end)
Usual limit based on wall_time, number of explored branches and number of failures in the search tree...
bool Check() override
This method is called to check the status of the limit.
Definition: search.cc:4020
void Init() override
This method is called when the search limit is initialized.
Definition: search.cc:4041
void Copy(const SearchLimit *const limit) override
Copy a limit.
Definition: search.cc:4001
RegularLimit * MakeIdenticalClone() const
Definition: search.cc:4014
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.
RegularLimit * MakeSolutionsLimit(int64_t solutions)
Creates a search limit that constrains the number of solutions found during the search.
Definition: search.cc:4143
LocalSearchFilter * MakeVariableDomainFilter()
bool SolveAndCommit(DecisionBuilder *const db, const std::vector< SearchMonitor * > &monitors)
SolveAndCommit using a decision builder and up to three search monitors, usually one for the objectiv...
LocalSearchOperator * MakeMoveTowardTargetOperator(const Assignment &target)
Creates a local search operator that tries to move the assignment of some variables toward a target.
LocalSearchStatistics GetLocalSearchStatistics() const
Returns detailed local search statistics.
ConstraintSolverParameters parameters() const
Stored Parameters.
std::function< int64_t(int64_t, int64_t, int64_t)> IndexEvaluator3
void SetSearchContext(Search *search, const std::string &search_context)
void TopPeriodicCheck()
Performs PeriodicCheck on the top-level search; for instance, can be called from a nested solve to ch...
std::function< int64_t(int64_t, int64_t)> IndexEvaluator2
LocalSearchOperator * ConcatenateOperators(const std::vector< LocalSearchOperator * > &ops)
Creates a local search operator which concatenates a vector of operators.
LocalSearchFilter * MakeRejectFilter()
LocalSearchFilter * MakeAcceptFilter()
Local Search Filters.
LocalSearchOperator * MakeRandomLnsOperator(const std::vector< IntVar * > &vars, int number_of_variables)
Creates a large neighborhood search operator which creates fragments (set of relaxed variables) with ...
LocalSearchOperator * RandomConcatenateOperators(const std::vector< LocalSearchOperator * > &ops)
Randomized version of local search concatenator; calls a random operator at each call to MakeNextNeig...
LocalSearchOperators
This enum is used in Solver::MakeOperator to specify the neighborhood to create.
@ EXCHANGE
Operator which exchanges the positions of two nodes.
@ MAKEINACTIVE
Operator which makes path nodes inactive.
@ RELOCATE
Relocate neighborhood with length of 1 (see OROPT comment).
@ SWAPACTIVE
Operator which replaces an active node by an inactive one.
@ SIMPLELNS
Operator which defines one neighbor per variable.
@ INCREMENT
Operator which defines one neighbor per variable.
@ MAKECHAININACTIVE
Operator which makes a "chain" of path nodes inactive.
@ TWOOPT
Operator which reverses a sub-chain of a path.
@ FULLPATHLNS
Operator which relaxes one entire path and all inactive nodes, thus defining num_paths neighbors.
@ EXTENDEDSWAPACTIVE
Operator which makes an inactive node active and an active one inactive.
@ OROPT
Relocate: OROPT and RELOCATE.
@ PATHLNS
Operator which relaxes two sub-chains of three consecutive arcs each.
@ UNACTIVELNS
Operator which relaxes all inactive nodes and one sub-chain of six consecutive arcs.
@ MAKEACTIVE
Operator which inserts an inactive node into a path.
@ DECREMENT
Operator which defines a neighborhood to decrement values.
@ CROSS
Operator which cross exchanges the starting chains of 2 paths, including exchanging the whole paths.
LocalSearchPhaseParameters * MakeLocalSearchPhaseParameters(IntVar *objective, LocalSearchOperator *const ls_operator, DecisionBuilder *const sub_decision_builder)
Local Search Phase Parameters.
bool IsLocalSearchProfilingEnabled() const
Returns whether we are profiling local search.
IntVarLocalSearchFilter * MakeSumObjectiveFilter(const std::vector< IntVar * > &vars, IndexEvaluator2 values, Solver::LocalSearchFilterBound filter_enum)
Search * ActiveSearch() const
Returns the active search, nullptr outside search.
LocalSearchOperator * MakeNeighborhoodLimit(LocalSearchOperator *const op, int64_t limit)
Creates a local search operator that wraps another local search operator and limits the number of nei...
LocalSearchMonitor * GetLocalSearchMonitor() const
Returns the local search monitor.
SolutionPool * MakeDefaultSolutionPool()
Solution Pool.
bool UseFastLocalSearch() const
Returns true if fast local search is enabled.
LocalSearchOperator * MakeOperator(const std::vector< IntVar * > &vars, LocalSearchOperators op)
Local Search Operators.
std::string LocalSearchProfile() const
Returns local search profiling information in a human readable format.
Solver(const std::string &name)
Solver API.
DecisionBuilder * MakeLocalSearchPhase(Assignment *const assignment, LocalSearchPhaseParameters *const parameters)
Local Search decision builders factories.
LocalSearchOperator * MultiArmedBanditConcatenateOperators(const std::vector< LocalSearchOperator * > &ops, double memory_coefficient, double exploration_coefficient, bool maximize)
Creates a local search operator which concatenates a vector of operators.
Assignment * MakeAssignment()
This method creates an empty assignment.
DecisionBuilder * Compose(DecisionBuilder *const db1, DecisionBuilder *const db2)
Creates a decision builder which sequentially composes decision builders.
Definition: search.cc:556
DecisionBuilder * MakeStoreAssignment(Assignment *assignment)
Returns a DecisionBuilder which stores an Assignment (calls void Assignment::Store())
DecisionBuilder * MakeRestoreAssignment(Assignment *assignment)
Returns a DecisionBuilder which restores an Assignment (calls void Assignment::Restore())
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
T * RevAlloc(T *object)
Registers the given object as being reversible.
EvaluatorLocalSearchOperators
This enum is used in Solver::MakeOperator associated with an evaluator to specify the neighborhood to...
@ TSPOPT
Sliding TSP operator.
@ LK
Lin-Kernighan local search.
LocalSearchFilterBound
This enum is used in Solver::MakeLocalSearchObjectiveFilter.
@ GE
Move is accepted when the current objective value >= objective.Min.
@ LE
Move is accepted when the current objective value <= objective.Max.
@ EQ
Move is accepted when the current objective value is in the interval objective.Min .
SwapActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
std::string DebugString() const override
TSPLns(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int tsp_size)
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
std::string DebugString() const override
TSPOpt(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, Solver::IndexEvaluator3 evaluator, int chain_length)
std::string DebugString() const override
int64_t GetBaseNodeRestartPosition(int base_index) override
Returns the index of the node to which the base node of index base_index must be set to when it reach...
bool MakeNeighbor() override
bool IsIncremental() const override
TwoOpt(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
bool OnSamePathAsPreviousBase(int64_t base_index) override
Returns true if a base node has to be on the same path as the "previous" base node (base node of inde...
std::string DebugString() const override
UnaryDimensionChecker(const PathState *path_state, std::vector< Interval > path_capacity, std::vector< int > path_class, std::vector< std::vector< Interval > > demand, std::vector< Interval > node_capacity)
const int64_t & Value(int64_t index) const
Returns the value in the current assignment of the variable of given index.
IntVar * Var(int64_t index) const
Returns the variable of given index.
Block * next
SatParameters parameters
SharedBoundsManager * bounds
const std::string name
int64_t value
IntVar * var
Definition: expr_array.cc:1874
const int64_t limit_
int64_t *const synchronized_costs_
ABSL_FLAG(int, cp_local_search_sync_frequency, 16, "Frequency of checks for better solutions in the solution pool.")
Solver::LocalSearchFilterBound filter_enum_
int64_t synchronized_sum_
int64_t delta_sum_
int64_t *const delta_costs_
const int primary_vars_size_
#define MAKE_LOCAL_SEARCH_OPERATOR(OperatorClass)
bool incremental_
const int ERROR
Definition: log_severity.h:32
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
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:200
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:263
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
LocalSearchOperator * MakeLocalSearchOperator(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class)
Operator Factories.
void InstallLocalSearchProfiler(LocalSearchProfiler *monitor)
LocalSearchFilter * MakeUnaryDimensionFilter(Solver *solver, std::unique_ptr< UnaryDimensionChecker > checker, const std::string &dimension_name)
int64_t CapSub(int64_t x, int64_t y)
void DeleteLocalSearchProfiler(LocalSearchProfiler *monitor)
bool AcceptDelta(Search *const search, Assignment *delta, Assignment *deltadelta)
void AcceptNeighbor(Search *const search)
int MostSignificantBitPosition32(uint32_t n)
Definition: bitset.h:273
LocalSearchFilter * MakePathStateFilter(Solver *solver, std::unique_ptr< PathState > path_state, const std::vector< IntVar * > &nexts)
bool LocalOptimumReached(Search *const search)
void AcceptUncheckedNeighbor(Search *const search)
LocalSearchProfiler * BuildLocalSearchProfiler(Solver *solver)
STL namespace.
int index
Definition: pack.cc:509
int64_t demand
Definition: resource.cc:125
int64_t delta
Definition: resource.cc:1692
int64_t cost
int nodes
const bool maximize_
Definition: search.cc:2508
IntVar *const objective_
Definition: search.cc:2966
std::function< int64_t(int64_t, int64_t)> evaluator_
Definition: search.cc:1368
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.