OR-Tools  9.0
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"
36 #include "ortools/base/logging.h"
37 #include "ortools/base/macros.h"
38 #include "ortools/base/map_util.h"
43 
44 ABSL_FLAG(int, cp_local_search_sync_frequency, 16,
45  "Frequency of checks for better solutions in the solution pool.");
46 
47 ABSL_FLAG(int, cp_local_search_tsp_opt_size, 13,
48  "Size of TSPs solved in the TSPOpt operator.");
49 
50 ABSL_FLAG(int, cp_local_search_tsp_lns_size, 10,
51  "Size of TSPs solved in the TSPLns operator.");
52 
53 ABSL_FLAG(bool, cp_use_empty_path_symmetry_breaker, true,
54  "If true, equivalent empty paths are removed from the neighborhood "
55  "of PathOperators");
56 
57 namespace 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.
63 bool 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).
67 bool AcceptDelta(Search* const search, Assignment* delta,
68  Assignment* deltadelta);
69 
70 // Notifies the search that a neighbor has been accepted by local search.
71 void AcceptNeighbor(Search* const search);
72 void 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 
101 BaseLns::BaseLns(const std::vector<IntVar*>& vars)
102  : IntVarLocalSearchOperator(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 
117 void BaseLns::OnStart() { InitFragments(); }
118 
120 
122  if (index >= 0 && index < Size()) {
123  fragment_.push_back(index);
124  }
125 }
126 
127 int BaseLns::FragmentSize() const { return fragment_.size(); }
128 
129 // ----- Simple Large Neighborhood Search operator -----
130 
131 // Frees number_of_variables (contiguous in vars) variables.
132 
133 namespace {
134 class 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 
150 bool 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 
166 class 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 
184 bool 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.
208 namespace {
209 class 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 
295 ChangeValue::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 
311 void ChangeValue::OnStart() { index_ = 0; }
312 
313 // Increments the current value of variables.
314 
315 namespace {
316 class 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 
330 class 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 
345 PathOperator::PathOperator(const std::vector<IntVar*>& next_vars,
346  const std::vector<IntVar*>& path_vars,
347  int number_of_base_nodes,
348  bool skip_locally_optimal_paths,
349  bool accept_path_end_base,
350  std::function<int(int64_t)> start_empty_path_class)
351  : IntVarLocalSearchOperator(next_vars, true),
352  number_of_nexts_(next_vars.size()),
353  ignore_path_vars_(path_vars.empty()),
354  next_base_to_increment_(number_of_base_nodes),
355  base_nodes_(number_of_base_nodes),
356  base_alternatives_(number_of_base_nodes),
357  base_sibling_alternatives_(number_of_base_nodes),
358  end_nodes_(number_of_base_nodes),
359  base_paths_(number_of_base_nodes),
360  just_started_(false),
361  first_start_(true),
362  accept_path_end_base_(accept_path_end_base),
363  start_empty_path_class_(std::move(start_empty_path_class)),
364  skip_locally_optimal_paths_(skip_locally_optimal_paths),
365  optimal_paths_enabled_(false),
366  alternative_index_(next_vars.size(), -1) {
367  DCHECK_GT(number_of_base_nodes, 0);
368  if (!ignore_path_vars_) {
369  AddVars(path_vars);
370  }
371  path_basis_.push_back(0);
372  for (int i = 1; i < base_nodes_.size(); ++i) {
373  if (!OnSamePathAsPreviousBase(i)) path_basis_.push_back(i);
374  }
375  if ((path_basis_.size() > 2) ||
376  (!next_vars.empty() && !next_vars.back()
377  ->solver()
378  ->parameters()
379  .skip_locally_optimal_paths())) {
380  skip_locally_optimal_paths_ = false;
381  }
382 }
383 
384 void PathOperator::Reset() { optimal_paths_.clear(); }
385 
386 void PathOperator::OnStart() {
387  optimal_paths_enabled_ = false;
388  InitializeBaseNodes();
389  InitializeAlternatives();
391 }
392 
394  while (IncrementPosition()) {
395  // Need to revert changes here since MakeNeighbor might have returned false
396  // and have done changes in the previous iteration.
397  RevertChanges(true);
398  if (MakeNeighbor()) {
399  return true;
400  }
401  }
402  return false;
403 }
404 
406  if (ignore_path_vars_) {
407  return true;
408  }
409  if (index < number_of_nexts_) {
410  int path_index = index + number_of_nexts_;
411  return Value(path_index) == OldValue(path_index);
412  }
413  int next_index = index - number_of_nexts_;
414  return Value(next_index) == OldValue(next_index);
415 }
416 
417 bool PathOperator::MoveChain(int64_t before_chain, int64_t chain_end,
418  int64_t destination) {
419  if (destination == before_chain || destination == chain_end) return false;
420  DCHECK(CheckChainValidity(before_chain, chain_end, destination) &&
421  !IsPathEnd(chain_end) && !IsPathEnd(destination));
422  const int64_t destination_path = Path(destination);
423  const int64_t after_chain = Next(chain_end);
424  SetNext(chain_end, Next(destination), destination_path);
425  if (!ignore_path_vars_) {
426  int current = destination;
427  int next = Next(before_chain);
428  while (current != chain_end) {
429  SetNext(current, next, destination_path);
430  current = next;
431  next = Next(next);
432  }
433  } else {
434  SetNext(destination, Next(before_chain), destination_path);
435  }
436  SetNext(before_chain, after_chain, Path(before_chain));
437  return true;
438 }
439 
440 bool PathOperator::ReverseChain(int64_t before_chain, int64_t after_chain,
441  int64_t* chain_last) {
442  if (CheckChainValidity(before_chain, after_chain, -1)) {
443  int64_t path = Path(before_chain);
444  int64_t current = Next(before_chain);
445  if (current == after_chain) {
446  return false;
447  }
448  int64_t current_next = Next(current);
449  SetNext(current, after_chain, path);
450  while (current_next != after_chain) {
451  const int64_t next = Next(current_next);
452  SetNext(current_next, current, path);
453  current = current_next;
454  current_next = next;
455  }
456  SetNext(before_chain, current, path);
457  *chain_last = current;
458  return true;
459  }
460  return false;
461 }
462 
463 bool PathOperator::MakeActive(int64_t node, int64_t destination) {
464  if (!IsPathEnd(destination)) {
465  int64_t destination_path = Path(destination);
466  SetNext(node, Next(destination), destination_path);
467  SetNext(destination, node, destination_path);
468  return true;
469  }
470  return false;
471 }
472 
473 bool PathOperator::MakeChainInactive(int64_t before_chain, int64_t chain_end) {
474  const int64_t kNoPath = -1;
475  if (CheckChainValidity(before_chain, chain_end, -1) &&
476  !IsPathEnd(chain_end)) {
477  const int64_t after_chain = Next(chain_end);
478  int64_t current = Next(before_chain);
479  while (current != after_chain) {
480  const int64_t next = Next(current);
481  SetNext(current, current, kNoPath);
482  current = next;
483  }
484  SetNext(before_chain, after_chain, Path(before_chain));
485  return true;
486  }
487  return false;
488 }
489 
490 bool PathOperator::SwapActiveAndInactive(int64_t active, int64_t inactive) {
491  if (active == inactive) return false;
492  const int64_t prev = Prev(active);
493  return MakeChainInactive(prev, active) && MakeActive(inactive, prev);
494 }
495 
496 bool PathOperator::IncrementPosition() {
497  const int base_node_size = base_nodes_.size();
498 
499  if (!just_started_) {
500  const int number_of_paths = path_starts_.size();
501  // Finding next base node positions.
502  // Increment the position of inner base nodes first (higher index nodes);
503  // if a base node is at the end of a path, reposition it at the start
504  // of the path and increment the position of the preceding base node (this
505  // action is called a restart).
506  int last_restarted = base_node_size;
507  for (int i = base_node_size - 1; i >= 0; --i) {
508  if (base_nodes_[i] < number_of_nexts_ && i <= next_base_to_increment_) {
509  if (ConsiderAlternatives(i)) {
510  // Iterate on sibling alternatives.
511  const int sibling_alternative_index =
512  GetSiblingAlternativeIndex(base_nodes_[i]);
513  if (sibling_alternative_index >= 0) {
514  if (base_sibling_alternatives_[i] <
515  alternative_sets_[sibling_alternative_index].size() - 1) {
516  ++base_sibling_alternatives_[i];
517  break;
518  }
519  base_sibling_alternatives_[i] = 0;
520  }
521  // Iterate on base alternatives.
522  const int alternative_index = alternative_index_[base_nodes_[i]];
523  if (alternative_index >= 0) {
524  if (base_alternatives_[i] <
525  alternative_sets_[alternative_index].size() - 1) {
526  ++base_alternatives_[i];
527  break;
528  }
529  base_alternatives_[i] = 0;
530  base_sibling_alternatives_[i] = 0;
531  }
532  }
533  base_alternatives_[i] = 0;
534  base_sibling_alternatives_[i] = 0;
535  base_nodes_[i] = OldNext(base_nodes_[i]);
536  if (accept_path_end_base_ || !IsPathEnd(base_nodes_[i])) break;
537  }
538  base_alternatives_[i] = 0;
539  base_sibling_alternatives_[i] = 0;
540  base_nodes_[i] = StartNode(i);
541  last_restarted = i;
542  }
543  next_base_to_increment_ = base_node_size;
544  // At the end of the loop, base nodes with indexes in
545  // [last_restarted, base_node_size[ have been restarted.
546  // Restarted base nodes are then repositioned by the virtual
547  // GetBaseNodeRestartPosition to reflect position constraints between
548  // base nodes (by default GetBaseNodeRestartPosition leaves the nodes
549  // at the start of the path).
550  // Base nodes are repositioned in ascending order to ensure that all
551  // base nodes "below" the node being repositioned have their final
552  // position.
553  for (int i = last_restarted; i < base_node_size; ++i) {
554  base_alternatives_[i] = 0;
555  base_sibling_alternatives_[i] = 0;
556  base_nodes_[i] = GetBaseNodeRestartPosition(i);
557  }
558  if (last_restarted > 0) {
559  return CheckEnds();
560  }
561  // If all base nodes have been restarted, base nodes are moved to new paths.
562  // First we mark the current paths as locally optimal if they have been
563  // completely explored.
564  if (optimal_paths_enabled_ && skip_locally_optimal_paths_) {
565  if (path_basis_.size() > 1) {
566  for (int i = 1; i < path_basis_.size(); ++i) {
567  optimal_paths_[num_paths_ *
568  start_to_path_[StartNode(path_basis_[i - 1])] +
569  start_to_path_[StartNode(path_basis_[i])]] = true;
570  }
571  } else {
572  optimal_paths_[num_paths_ * start_to_path_[StartNode(path_basis_[0])] +
573  start_to_path_[StartNode(path_basis_[0])]] = true;
574  }
575  }
576  std::vector<int> current_starts(base_node_size);
577  for (int i = 0; i < base_node_size; ++i) {
578  current_starts[i] = StartNode(i);
579  }
580  // Exploration of next paths can lead to locally optimal paths since we are
581  // exploring them from scratch.
582  optimal_paths_enabled_ = true;
583  while (true) {
584  for (int i = base_node_size - 1; i >= 0; --i) {
585  const int next_path_index = base_paths_[i] + 1;
586  if (next_path_index < number_of_paths) {
587  base_paths_[i] = next_path_index;
588  base_alternatives_[i] = 0;
589  base_sibling_alternatives_[i] = 0;
590  base_nodes_[i] = path_starts_[next_path_index];
591  if (i == 0 || !OnSamePathAsPreviousBase(i)) {
592  break;
593  }
594  } else {
595  base_paths_[i] = 0;
596  base_alternatives_[i] = 0;
597  base_sibling_alternatives_[i] = 0;
598  base_nodes_[i] = path_starts_[0];
599  }
600  }
601  if (!skip_locally_optimal_paths_) return CheckEnds();
602  // If the new paths have already been completely explored, we can
603  // skip them from now on.
604  if (path_basis_.size() > 1) {
605  for (int j = 1; j < path_basis_.size(); ++j) {
606  if (!optimal_paths_[num_paths_ * start_to_path_[StartNode(
607  path_basis_[j - 1])] +
608  start_to_path_[StartNode(path_basis_[j])]]) {
609  return CheckEnds();
610  }
611  }
612  } else {
613  if (!optimal_paths_[num_paths_ *
614  start_to_path_[StartNode(path_basis_[0])] +
615  start_to_path_[StartNode(path_basis_[0])]]) {
616  return CheckEnds();
617  }
618  }
619  // If we are back to paths we just iterated on or have reached the end
620  // of the neighborhood search space, we can stop.
621  if (!CheckEnds()) return false;
622  bool stop = true;
623  for (int i = 0; i < base_node_size; ++i) {
624  if (StartNode(i) != current_starts[i]) {
625  stop = false;
626  break;
627  }
628  }
629  if (stop) return false;
630  }
631  } else {
632  just_started_ = false;
633  return true;
634  }
635  return CheckEnds();
636 }
637 
638 void PathOperator::InitializePathStarts() {
639  // Detect nodes which do not have any possible predecessor in a path; these
640  // nodes are path starts.
641  int max_next = -1;
642  std::vector<bool> has_prevs(number_of_nexts_, false);
643  for (int i = 0; i < number_of_nexts_; ++i) {
644  const int next = OldNext(i);
645  if (next < number_of_nexts_) {
646  has_prevs[next] = true;
647  }
648  max_next = std::max(max_next, next);
649  }
650  // Update locally optimal paths.
651  if (optimal_paths_.empty() && skip_locally_optimal_paths_) {
652  num_paths_ = 0;
653  start_to_path_.clear();
654  start_to_path_.resize(number_of_nexts_, -1);
655  for (int i = 0; i < number_of_nexts_; ++i) {
656  if (!has_prevs[i]) {
658  ++num_paths_;
659  }
660  }
661  optimal_paths_.resize(num_paths_ * num_paths_, false);
662  }
663  if (skip_locally_optimal_paths_) {
664  for (int i = 0; i < number_of_nexts_; ++i) {
665  if (!has_prevs[i]) {
666  int current = i;
667  while (!IsPathEnd(current)) {
668  if ((OldNext(current) != prev_values_[current])) {
669  for (int j = 0; j < num_paths_; ++j) {
670  optimal_paths_[num_paths_ * start_to_path_[i] + j] = false;
671  optimal_paths_[num_paths_ * j + start_to_path_[i]] = false;
672  }
673  break;
674  }
675  current = OldNext(current);
676  }
677  }
678  }
679  }
680  // Create a list of path starts, dropping equivalent path starts of
681  // currently empty paths.
682  std::vector<bool> empty_found(number_of_nexts_, false);
683  std::vector<int64_t> new_path_starts;
684  const bool use_empty_path_symmetry_breaker =
685  absl::GetFlag(FLAGS_cp_use_empty_path_symmetry_breaker);
686  for (int i = 0; i < number_of_nexts_; ++i) {
687  if (!has_prevs[i]) {
688  if (use_empty_path_symmetry_breaker && IsPathEnd(OldNext(i))) {
689  if (start_empty_path_class_ != nullptr) {
690  if (empty_found[start_empty_path_class_(i)]) continue;
691  empty_found[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 < base_nodes_.size(); ++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 < base_nodes_.size(); ++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 
756 void 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 
763 void 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 < base_nodes_.size(); ++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 < base_nodes_.size(); ++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 < base_nodes_.size(); ++i) {
788  if (OnSamePathAsPreviousBase(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 < base_nodes_.size(); ++i) {
797  base_alternatives_[i] = 0;
798  base_sibling_alternatives_[i] = 0;
799  }
800  just_started_ = true;
801 }
802 
803 void 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 
817 bool 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).
839 bool 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
870 class 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 
902  DCHECK_EQ(StartNode(0), StartNode(1));
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 
941 class 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 
1006 class 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 
1042 class 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 
1102 void 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)) {}
1140  ~MakeActiveOperator() override {}
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)) {}
1322  ~SwapActiveOperator() override {}
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 
1381 class 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 
1399 TSPOpt::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 
1449 class 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 
1477 TSPLns::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);
1594  virtual ~NearestNeighbors() {}
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 
1630 const std::vector<int>& NearestNeighbors::Neighbors(int index) const {
1631  return neighbors_[index];
1632 }
1633 
1634 void 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 
1658 class LinKernighan : public PathOperator {
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 
1685 LinKernighan::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 
1695 void 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 
1755 const int LinKernighan::kNeighbors = 5 + 1;
1756 
1757 bool 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 
1788 class 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 
1832 void 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 
1843 void 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 
1896 namespace {
1897 class 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 
1948 CompoundOperator::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 
1968 void CompoundOperator::Reset() {
1969  for (LocalSearchOperator* const op : operators_) {
1970  op->Reset();
1971  }
1972 }
1973 
1974 void 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 
1984 bool 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 
2011 int64_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 
2017 int64_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 
2045 namespace {
2046 class 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 
2066 void RandomCompoundOperator::Start(const Assignment* assignment) {
2067  for (LocalSearchOperator* const op : operators_) {
2068  op->Start(assignment);
2069  }
2070 }
2071 
2072 RandomCompoundOperator::RandomCompoundOperator(
2073  std::vector<LocalSearchOperator*> operators)
2074  : RandomCompoundOperator(std::move(operators), CpRandomSeed()) {}
2075 
2076 RandomCompoundOperator::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 
2087 void RandomCompoundOperator::Reset() {
2088  for (LocalSearchOperator* const op : operators_) {
2089  op->Reset();
2090  }
2091 }
2092 
2093 bool 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 
2122 namespace {
2123 class 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 
2169 MultiArmedBanditCompoundOperator::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 
2199 void MultiArmedBanditCompoundOperator::Reset() {
2200  for (LocalSearchOperator* const op : operators_) {
2201  op->Reset();
2202  }
2203 }
2204 
2205 double 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 
2212 void 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 
2247 bool 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 
2283 template <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 
2306 MAKE_LOCAL_SEARCH_OPERATOR(MakeActiveOperator)
2307 MAKE_LOCAL_SEARCH_OPERATOR(MakeInactiveOperator)
2308 MAKE_LOCAL_SEARCH_OPERATOR(MakeChainInactiveOperator)
2309 MAKE_LOCAL_SEARCH_OPERATOR(SwapActiveOperator)
2310 MAKE_LOCAL_SEARCH_OPERATOR(ExtendedSwapActiveOperator)
2311 MAKE_LOCAL_SEARCH_OPERATOR(MakeActiveAndRelocate)
2312 MAKE_LOCAL_SEARCH_OPERATOR(RelocateAndMakeActiveOperator)
2313 MAKE_LOCAL_SEARCH_OPERATOR(RelocateAndMakeInactiveOperator)
2314 
2315 #undef MAKE_LOCAL_SEARCH_OPERATOR
2316 
2317 LocalSearchOperator* 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 
2470 namespace {
2471 // Classes for Local Search Operation used in Local Search filters.
2472 
2473 class 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 
2486 class 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 
2503 class 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 
2517 class 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.
2532 class 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 
2548 namespace {
2549 // Never accepts deltas, cost 0.
2550 class 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 
2566 PathState::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 
2618 void 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 
2664 void 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 
2779 void 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.
2798 void 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 
2824 void 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 
2853 namespace {
2854 
2855 class 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 
2880 PathStateFilter::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 
2901 void 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 
2921 void 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.
2945 void 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 
2956 void 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 
3078 void 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 
3086 void 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 
3099 void 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 
3128 void 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].
3155 UnaryDimensionChecker::Interval
3156 UnaryDimensionChecker::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 
3173 bool 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 
3181 namespace {
3182 
3183 class UnaryDimensionFilter : public LocalSearchFilter {
3184  public:
3185  std::string DebugString() const override { return "UnaryDimensionFilter"; }
3186  explicit UnaryDimensionFilter(std::unique_ptr<UnaryDimensionChecker> checker)
3187  : checker_(std::move(checker)) {}
3188 
3189  bool Accept(const Assignment* delta, const Assignment* deltadelta,
3190  int64_t objective_min, int64_t objective_max) override {
3191  return checker_->Check();
3192  }
3193 
3194  void Synchronize(const Assignment* assignment,
3195  const Assignment* delta) override {
3196  checker_->Commit();
3197  }
3198 
3199  private:
3200  std::unique_ptr<UnaryDimensionChecker> checker_;
3201 };
3202 
3203 } // namespace
3204 
3206  Solver* solver, std::unique_ptr<UnaryDimensionChecker> checker) {
3207  UnaryDimensionFilter* filter = new UnaryDimensionFilter(std::move(checker));
3208  return solver->RevAlloc(filter);
3209 }
3210 
3211 namespace {
3212 // ----- Variable domain filter -----
3213 // Rejects assignments to values outside the domain of variables
3214 
3215 class VariableDomainFilter : public LocalSearchFilter {
3216  public:
3217  VariableDomainFilter() {}
3218  ~VariableDomainFilter() override {}
3219  bool Accept(const Assignment* delta, const Assignment* deltadelta,
3220  int64_t objective_min, int64_t objective_max) override;
3221  void Synchronize(const Assignment* assignment,
3222  const Assignment* delta) override {}
3223 
3224  std::string DebugString() const override { return "VariableDomainFilter"; }
3225 };
3226 
3227 bool VariableDomainFilter::Accept(const Assignment* delta,
3228  const Assignment* deltadelta,
3229  int64_t objective_min,
3230  int64_t objective_max) {
3231  const Assignment::IntContainer& container = delta->IntVarContainer();
3232  const int size = container.Size();
3233  for (int i = 0; i < size; ++i) {
3234  const IntVarElement& element = container.Element(i);
3235  if (element.Activated() && !element.Var()->Contains(element.Value())) {
3236  return false;
3237  }
3238  }
3239  return true;
3240 }
3241 } // namespace
3242 
3244  return RevAlloc(new VariableDomainFilter());
3245 }
3246 
3247 // ----- IntVarLocalSearchFilter -----
3248 
3249 const int IntVarLocalSearchFilter::kUnassigned = -1;
3250 
3252  const std::vector<IntVar*>& vars) {
3253  AddVars(vars);
3254 }
3255 
3256 void IntVarLocalSearchFilter::AddVars(const std::vector<IntVar*>& vars) {
3257  if (!vars.empty()) {
3258  for (int i = 0; i < vars.size(); ++i) {
3259  const int index = vars[i]->index();
3260  if (index >= var_index_to_index_.size()) {
3261  var_index_to_index_.resize(index + 1, kUnassigned);
3262  }
3263  var_index_to_index_[index] = i + vars_.size();
3264  }
3265  vars_.insert(vars_.end(), vars.begin(), vars.end());
3266  values_.resize(vars_.size(), /*junk*/ 0);
3267  var_synced_.resize(vars_.size(), false);
3268  }
3269 }
3270 
3272 
3274  const Assignment* delta) {
3275  if (delta == nullptr || delta->Empty()) {
3276  var_synced_.assign(var_synced_.size(), false);
3277  SynchronizeOnAssignment(assignment);
3278  } else {
3280  }
3282 }
3283 
3285  const Assignment* assignment) {
3286  const Assignment::IntContainer& container = assignment->IntVarContainer();
3287  const int size = container.Size();
3288  for (int i = 0; i < size; ++i) {
3289  const IntVarElement& element = container.Element(i);
3290  IntVar* const var = element.Var();
3291  if (var != nullptr) {
3292  if (i < vars_.size() && vars_[i] == var) {
3293  values_[i] = element.Value();
3294  var_synced_[i] = true;
3295  } else {
3296  const int64_t kUnallocated = -1;
3297  int64_t index = kUnallocated;
3298  if (FindIndex(var, &index)) {
3299  values_[index] = element.Value();
3300  var_synced_[index] = true;
3301  }
3302  }
3303  }
3304  }
3305 }
3306 
3307 // ----- Sum Objective filter ------
3308 // Maintains the sum of costs of variables, where the subclass implements
3309 // CostOfSynchronizedVariable() and FillCostOfBoundDeltaVariable() to compute
3310 // the cost of a variable depending on its value.
3311 // An assignment is accepted by this filter if the total cost is allowed
3312 // depending on the relation defined by filter_enum:
3313 // - Solver::LE -> total_cost <= objective_max.
3314 // - Solver::GE -> total_cost >= objective_min.
3315 // - Solver::EQ -> the conjunction of LE and GE.
3316 namespace {
3317 class SumObjectiveFilter : public IntVarLocalSearchFilter {
3318  public:
3319  SumObjectiveFilter(const std::vector<IntVar*>& vars,
3320  Solver::LocalSearchFilterBound filter_enum)
3321  : IntVarLocalSearchFilter(vars),
3322  primary_vars_size_(vars.size()),
3323  synchronized_costs_(new int64_t[vars.size()]),
3324  delta_costs_(new int64_t[vars.size()]),
3325  filter_enum_(filter_enum),
3326  synchronized_sum_(std::numeric_limits<int64_t>::min()),
3327  delta_sum_(std::numeric_limits<int64_t>::min()),
3328  incremental_(false) {
3329  for (int i = 0; i < vars.size(); ++i) {
3330  synchronized_costs_[i] = 0;
3331  delta_costs_[i] = 0;
3332  }
3333  }
3334  ~SumObjectiveFilter() override {
3335  delete[] synchronized_costs_;
3336  delete[] delta_costs_;
3337  }
3338  bool Accept(const Assignment* delta, const Assignment* deltadelta,
3339  int64_t objective_min, int64_t objective_max) override {
3340  if (delta == nullptr) {
3341  return false;
3342  }
3343  if (deltadelta->Empty()) {
3344  if (incremental_) {
3345  for (int i = 0; i < primary_vars_size_; ++i) {
3347  }
3349  }
3350  incremental_ = false;
3352  CostOfChanges(delta, synchronized_costs_, false));
3353  } else {
3354  if (incremental_) {
3355  delta_sum_ =
3356  CapAdd(delta_sum_, CostOfChanges(deltadelta, delta_costs_, true));
3357  } else {
3359  CostOfChanges(delta, synchronized_costs_, true));
3360  }
3361  incremental_ = true;
3362  }
3363  switch (filter_enum_) {
3364  case Solver::LE: {
3365  return delta_sum_ <= objective_max;
3366  }
3367  case Solver::GE: {
3368  return delta_sum_ >= objective_min;
3369  }
3370  case Solver::EQ: {
3371  return objective_min <= delta_sum_ && delta_sum_ <= objective_max;
3372  }
3373  default: {
3374  LOG(ERROR) << "Unknown local search filter enum value";
3375  return false;
3376  }
3377  }
3378  }
3379  // If the variable is synchronized, returns its associated cost, otherwise
3380  // returns 0.
3381  virtual int64_t CostOfSynchronizedVariable(int64_t index) = 0;
3382  // If the variable is bound, fills new_cost with the cost associated to the
3383  // variable's valuation in container, and returns true. Otherwise, fills
3384  // new_cost with 0, and returns false.
3385  virtual bool FillCostOfBoundDeltaVariable(
3386  const Assignment::IntContainer& container, int index,
3387  int* container_index, int64_t* new_cost) = 0;
3388  bool IsIncremental() const override { return true; }
3389 
3390  std::string DebugString() const override { return "SumObjectiveFilter"; }
3391 
3392  int64_t GetSynchronizedObjectiveValue() const override {
3393  return synchronized_sum_;
3394  }
3395  int64_t GetAcceptedObjectiveValue() const override { return delta_sum_; }
3396 
3397  protected:
3399  int64_t* const synchronized_costs_;
3400  int64_t* const delta_costs_;
3403  int64_t delta_sum_;
3405 
3406  private:
3407  void OnSynchronize(const Assignment* delta) override {
3408  synchronized_sum_ = 0;
3409  for (int i = 0; i < primary_vars_size_; ++i) {
3410  const int64_t cost = CostOfSynchronizedVariable(i);
3412  delta_costs_[i] = cost;
3414  }
3416  incremental_ = false;
3417  }
3418  int64_t CostOfChanges(const Assignment* changes,
3419  const int64_t* const old_costs,
3420  bool cache_delta_values) {
3421  int64_t total_cost = 0;
3422  const Assignment::IntContainer& container = changes->IntVarContainer();
3423  const int size = container.Size();
3424  for (int i = 0; i < size; ++i) {
3425  const IntVarElement& new_element = container.Element(i);
3426  IntVar* const var = new_element.Var();
3427  int64_t index = -1;
3428  if (FindIndex(var, &index) && index < primary_vars_size_) {
3429  total_cost = CapSub(total_cost, old_costs[index]);
3430  int64_t new_cost = 0LL;
3431  if (FillCostOfBoundDeltaVariable(container, index, &i, &new_cost)) {
3432  total_cost = CapAdd(total_cost, new_cost);
3433  }
3434  if (cache_delta_values) {
3435  delta_costs_[index] = new_cost;
3436  }
3437  }
3438  }
3439  return total_cost;
3440  }
3441 };
3442 
3443 class BinaryObjectiveFilter : public SumObjectiveFilter {
3444  public:
3445  BinaryObjectiveFilter(const std::vector<IntVar*>& vars,
3446  Solver::IndexEvaluator2 value_evaluator,
3447  Solver::LocalSearchFilterBound filter_enum)
3448  : SumObjectiveFilter(vars, filter_enum),
3449  value_evaluator_(std::move(value_evaluator)) {}
3450  ~BinaryObjectiveFilter() override {}
3451  int64_t CostOfSynchronizedVariable(int64_t index) override {
3453  ? value_evaluator_(index, IntVarLocalSearchFilter::Value(index))
3454  : 0;
3455  }
3456  bool FillCostOfBoundDeltaVariable(const Assignment::IntContainer& container,
3457  int index, int* container_index,
3458  int64_t* new_cost) override {
3459  const IntVarElement& element = container.Element(*container_index);
3460  if (element.Activated()) {
3461  *new_cost = value_evaluator_(index, element.Value());
3462  return true;
3463  }
3464  const IntVar* var = element.Var();
3465  if (var->Bound()) {
3466  *new_cost = value_evaluator_(index, var->Min());
3467  return true;
3468  }
3469  *new_cost = 0;
3470  return false;
3471  }
3472 
3473  private:
3474  Solver::IndexEvaluator2 value_evaluator_;
3475 };
3476 
3477 class TernaryObjectiveFilter : public SumObjectiveFilter {
3478  public:
3479  TernaryObjectiveFilter(const std::vector<IntVar*>& vars,
3480  const std::vector<IntVar*>& secondary_vars,
3481  Solver::IndexEvaluator3 value_evaluator,
3482  Solver::LocalSearchFilterBound filter_enum)
3483  : SumObjectiveFilter(vars, filter_enum),
3484  secondary_vars_offset_(vars.size()),
3485  value_evaluator_(std::move(value_evaluator)) {
3486  IntVarLocalSearchFilter::AddVars(secondary_vars);
3488  }
3489  ~TernaryObjectiveFilter() override {}
3490  int64_t CostOfSynchronizedVariable(int64_t index) override {
3491  DCHECK_LT(index, secondary_vars_offset_);
3493  ? value_evaluator_(index, IntVarLocalSearchFilter::Value(index),
3495  index + secondary_vars_offset_))
3496  : 0;
3497  }
3498  bool FillCostOfBoundDeltaVariable(const Assignment::IntContainer& container,
3499  int index, int* container_index,
3500  int64_t* new_cost) override {
3501  DCHECK_LT(index, secondary_vars_offset_);
3502  *new_cost = 0LL;
3503  const IntVarElement& element = container.Element(*container_index);
3504  const IntVar* secondary_var =
3505  IntVarLocalSearchFilter::Var(index + secondary_vars_offset_);
3506  if (element.Activated()) {
3507  const int64_t value = element.Value();
3508  int hint_index = *container_index + 1;
3509  if (hint_index < container.Size() &&
3510  secondary_var == container.Element(hint_index).Var()) {
3511  *new_cost = value_evaluator_(index, value,
3512  container.Element(hint_index).Value());
3513  *container_index = hint_index;
3514  } else {
3515  *new_cost = value_evaluator_(index, value,
3516  container.Element(secondary_var).Value());
3517  }
3518  return true;
3519  }
3520  const IntVar* var = element.Var();
3521  if (var->Bound() && secondary_var->Bound()) {
3522  *new_cost = value_evaluator_(index, var->Min(), secondary_var->Min());
3523  return true;
3524  }
3525  *new_cost = 0;
3526  return false;
3527  }
3528 
3529  private:
3530  int secondary_vars_offset_;
3531  Solver::IndexEvaluator3 value_evaluator_;
3532 };
3533 } // namespace
3534 
3536  const std::vector<IntVar*>& vars, Solver::IndexEvaluator2 values,
3537  Solver::LocalSearchFilterBound filter_enum) {
3538  return RevAlloc(
3539  new BinaryObjectiveFilter(vars, std::move(values), filter_enum));
3540 }
3541 
3543  const std::vector<IntVar*>& vars,
3544  const std::vector<IntVar*>& secondary_vars, Solver::IndexEvaluator3 values,
3545  Solver::LocalSearchFilterBound filter_enum) {
3546  return RevAlloc(new TernaryObjectiveFilter(vars, secondary_vars,
3547  std::move(values), filter_enum));
3548 }
3549 
3551  int64_t initial_max) {
3552  DCHECK(state_is_valid_);
3553  DCHECK_LE(initial_min, initial_max);
3554  initial_variable_bounds_.push_back({initial_min, initial_max});
3555  variable_bounds_.push_back({initial_min, initial_max});
3556  variable_is_relaxed_.push_back(false);
3557 
3558  const int variable_index = variable_bounds_.size() - 1;
3559  return {this, variable_index};
3560 }
3561 
3562 void LocalSearchState::RelaxVariableBounds(int variable_index) {
3563  DCHECK(state_is_valid_);
3564  DCHECK(0 <= variable_index && variable_index < variable_is_relaxed_.size());
3565  if (!variable_is_relaxed_[variable_index]) {
3566  variable_is_relaxed_[variable_index] = true;
3567  saved_variable_bounds_trail_.emplace_back(variable_bounds_[variable_index],
3568  variable_index);
3569  variable_bounds_[variable_index] = initial_variable_bounds_[variable_index];
3570  }
3571 }
3572 
3573 int64_t LocalSearchState::VariableMin(int variable_index) const {
3574  DCHECK(state_is_valid_);
3575  DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3576  return variable_bounds_[variable_index].min;
3577 }
3578 
3579 int64_t LocalSearchState::VariableMax(int variable_index) const {
3580  DCHECK(state_is_valid_);
3581  DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3582  return variable_bounds_[variable_index].max;
3583 }
3584 
3585 bool LocalSearchState::TightenVariableMin(int variable_index,
3586  int64_t min_value) {
3587  DCHECK(state_is_valid_);
3588  DCHECK(variable_is_relaxed_[variable_index]);
3589  DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3590  Bounds& bounds = variable_bounds_[variable_index];
3591  if (bounds.max < min_value) {
3592  state_is_valid_ = false;
3593  }
3594  bounds.min = std::max(bounds.min, min_value);
3595  return state_is_valid_;
3596 }
3597 
3598 bool LocalSearchState::TightenVariableMax(int variable_index,
3599  int64_t max_value) {
3600  DCHECK(state_is_valid_);
3601  DCHECK(variable_is_relaxed_[variable_index]);
3602  DCHECK(0 <= variable_index && variable_index < variable_bounds_.size());
3603  Bounds& bounds = variable_bounds_[variable_index];
3604  if (bounds.min > max_value) {
3605  state_is_valid_ = false;
3606  }
3607  bounds.max = std::min(bounds.max, max_value);
3608  return state_is_valid_;
3609 }
3610 
3611 // TODO(user): When the class has more users, find a threshold ratio of
3612 // saved/total variables under which a sparse clear would be more efficient
3613 // for both Commit() and Revert().
3615  DCHECK(state_is_valid_);
3616  saved_variable_bounds_trail_.clear();
3617  variable_is_relaxed_.assign(variable_is_relaxed_.size(), false);
3618 }
3619 
3621  for (const auto& bounds_index : saved_variable_bounds_trail_) {
3622  DCHECK(variable_is_relaxed_[bounds_index.second]);
3623  variable_bounds_[bounds_index.second] = bounds_index.first;
3624  }
3625  saved_variable_bounds_trail_.clear();
3626  variable_is_relaxed_.assign(variable_is_relaxed_.size(), false);
3627  state_is_valid_ = true;
3628 }
3629 
3630 // ----- LocalSearchProfiler -----
3631 
3633  public:
3635  std::string DebugString() const override { return "LocalSearchProfiler"; }
3636  void RestartSearch() override {
3637  operator_stats_.clear();
3638  filter_stats_.clear();
3639  }
3640  void ExitSearch() override {
3641  // Update times for current operator when the search ends.
3642  if (solver()->TopLevelSearch() == solver()->ActiveSearch()) {
3643  UpdateTime();
3644  }
3645  }
3646  LocalSearchStatistics ExportToLocalSearchStatistics() const {
3647  LocalSearchStatistics statistics_proto;
3648  std::vector<const LocalSearchOperator*> operators;
3649  for (const auto& stat : operator_stats_) {
3650  operators.push_back(stat.first);
3651  }
3652  std::sort(
3653  operators.begin(), operators.end(),
3654  [this](const LocalSearchOperator* op1, const LocalSearchOperator* op2) {
3655  return gtl::FindOrDie(operator_stats_, op1).neighbors >
3656  gtl::FindOrDie(operator_stats_, op2).neighbors;
3657  });
3658  for (const LocalSearchOperator* const op : operators) {
3659  const OperatorStats& stats = gtl::FindOrDie(operator_stats_, op);
3660  LocalSearchStatistics::LocalSearchOperatorStatistics* const
3661  local_search_operator_statistics =
3662  statistics_proto.add_local_search_operator_statistics();
3663  local_search_operator_statistics->set_local_search_operator(
3664  op->DebugString());
3665  local_search_operator_statistics->set_num_neighbors(stats.neighbors);
3666  local_search_operator_statistics->set_num_filtered_neighbors(
3667  stats.filtered_neighbors);
3668  local_search_operator_statistics->set_num_accepted_neighbors(
3669  stats.accepted_neighbors);
3670  local_search_operator_statistics->set_duration_seconds(stats.seconds);
3671  }
3672  std::vector<const LocalSearchFilter*> filters;
3673  for (const auto& stat : filter_stats_) {
3674  filters.push_back(stat.first);
3675  }
3676  std::sort(filters.begin(), filters.end(),
3677  [this](const LocalSearchFilter* filter1,
3678  const LocalSearchFilter* filter2) {
3679  return gtl::FindOrDie(filter_stats_, filter1).calls >
3680  gtl::FindOrDie(filter_stats_, filter2).calls;
3681  });
3682  for (const LocalSearchFilter* const filter : filters) {
3683  const FilterStats& stats = gtl::FindOrDie(filter_stats_, filter);
3684  LocalSearchStatistics::LocalSearchFilterStatistics* const
3685  local_search_filter_statistics =
3686  statistics_proto.add_local_search_filter_statistics();
3687  local_search_filter_statistics->set_local_search_filter(
3688  filter->DebugString());
3689  local_search_filter_statistics->set_num_calls(stats.calls);
3690  local_search_filter_statistics->set_num_rejects(stats.rejects);
3691  local_search_filter_statistics->set_duration_seconds(stats.seconds);
3692  }
3693  statistics_proto.set_total_num_neighbors(solver()->neighbors());
3694  statistics_proto.set_total_num_filtered_neighbors(
3695  solver()->filtered_neighbors());
3696  statistics_proto.set_total_num_accepted_neighbors(
3697  solver()->accepted_neighbors());
3698  return statistics_proto;
3699  }
3700  std::string PrintOverview() const {
3701  size_t max_name_size = 0;
3702  std::vector<const LocalSearchOperator*> operators;
3703  for (const auto& stat : operator_stats_) {
3704  operators.push_back(stat.first);
3705  max_name_size =
3706  std::max(max_name_size, stat.first->DebugString().length());
3707  }
3708  std::sort(
3709  operators.begin(), operators.end(),
3710  [this](const LocalSearchOperator* op1, const LocalSearchOperator* op2) {
3711  return gtl::FindOrDie(operator_stats_, op1).neighbors >
3712  gtl::FindOrDie(operator_stats_, op2).neighbors;
3713  });
3714  std::string overview = "Local search operator statistics:\n";
3715  absl::StrAppendFormat(&overview,
3716  "%*s | Neighbors | Filtered | Accepted | Time (s)\n",
3717  max_name_size, "");
3718  OperatorStats total_stats;
3719  for (const LocalSearchOperator* const op : operators) {
3720  const OperatorStats& stats = gtl::FindOrDie(operator_stats_, op);
3721  const std::string& name = op->DebugString();
3722  absl::StrAppendFormat(&overview, "%*s | %9ld | %8ld | %8ld | %7.2g\n",
3723  max_name_size, name, stats.neighbors,
3724  stats.filtered_neighbors, stats.accepted_neighbors,
3725  stats.seconds);
3726  total_stats.neighbors += stats.neighbors;
3727  total_stats.filtered_neighbors += stats.filtered_neighbors;
3728  total_stats.accepted_neighbors += stats.accepted_neighbors;
3729  total_stats.seconds += stats.seconds;
3730  }
3731  absl::StrAppendFormat(&overview, "%*s | %9ld | %8ld | %8ld | %7.2g\n",
3732  max_name_size, "Total", total_stats.neighbors,
3733  total_stats.filtered_neighbors,
3734  total_stats.accepted_neighbors, total_stats.seconds);
3735  max_name_size = 0;
3736  std::vector<const LocalSearchFilter*> filters;
3737  for (const auto& stat : filter_stats_) {
3738  filters.push_back(stat.first);
3739  max_name_size =
3740  std::max(max_name_size, stat.first->DebugString().length());
3741  }
3742  std::sort(filters.begin(), filters.end(),
3743  [this](const LocalSearchFilter* filter1,
3744  const LocalSearchFilter* filter2) {
3745  return gtl::FindOrDie(filter_stats_, filter1).calls >
3746  gtl::FindOrDie(filter_stats_, filter2).calls;
3747  });
3748  absl::StrAppendFormat(&overview,
3749  "Local search filter statistics:\n%*s | Calls | "
3750  " Rejects | Time (s) "
3751  "| Rejects/s\n",
3752  max_name_size, "");
3753  FilterStats total_filter_stats;
3754  for (const LocalSearchFilter* const filter : filters) {
3755  const FilterStats& stats = gtl::FindOrDie(filter_stats_, filter);
3756  const std::string& name = filter->DebugString();
3757  absl::StrAppendFormat(&overview, "%*s | %9ld | %9ld | %7.2g | %7.2g\n",
3758  max_name_size, name, stats.calls, stats.rejects,
3759  stats.seconds, stats.rejects / stats.seconds);
3760  total_filter_stats.calls += stats.calls;
3761  total_filter_stats.rejects += stats.rejects;
3762  total_filter_stats.seconds += stats.seconds;
3763  }
3764  absl::StrAppendFormat(
3765  &overview, "%*s | %9ld | %9ld | %7.2g | %7.2g\n", max_name_size,
3766  "Total", total_filter_stats.calls, total_filter_stats.rejects,
3767  total_filter_stats.seconds,
3768  total_filter_stats.rejects / total_filter_stats.seconds);
3769  return overview;
3770  }
3771  void BeginOperatorStart() override {}
3772  void EndOperatorStart() override {}
3773  void BeginMakeNextNeighbor(const LocalSearchOperator* op) override {
3774  if (last_operator_ != op->Self()) {
3775  UpdateTime();
3776  last_operator_ = op->Self();
3777  }
3778  }
3779  void EndMakeNextNeighbor(const LocalSearchOperator* op, bool neighbor_found,
3780  const Assignment* delta,
3781  const Assignment* deltadelta) override {
3782  if (neighbor_found) {
3783  operator_stats_[op->Self()].neighbors++;
3784  }
3785  }
3786  void BeginFilterNeighbor(const LocalSearchOperator* op) override {}
3788  bool neighbor_found) override {
3789  if (neighbor_found) {
3790  operator_stats_[op->Self()].filtered_neighbors++;
3791  }
3792  }
3793  void BeginAcceptNeighbor(const LocalSearchOperator* op) override {}
3795  bool neighbor_found) override {
3796  if (neighbor_found) {
3797  operator_stats_[op->Self()].accepted_neighbors++;
3798  }
3799  }
3800  void BeginFiltering(const LocalSearchFilter* filter) override {
3801  filter_stats_[filter].calls++;
3802  filter_timer_.Start();
3803  }
3804  void EndFiltering(const LocalSearchFilter* filter, bool reject) override {
3805  filter_timer_.Stop();
3806  auto& stats = filter_stats_[filter];
3807  stats.seconds += filter_timer_.Get();
3808  if (reject) {
3809  stats.rejects++;
3810  }
3811  }
3812  void Install() override { SearchMonitor::Install(); }
3813 
3814  private:
3815  void UpdateTime() {
3816  if (last_operator_ != nullptr) {
3817  timer_.Stop();
3818  operator_stats_[last_operator_].seconds += timer_.Get();
3819  }
3820  timer_.Start();
3821  }
3822 
3823  struct OperatorStats {
3824  int64_t neighbors = 0;
3825  int64_t filtered_neighbors = 0;
3826  int64_t accepted_neighbors = 0;
3827  double seconds = 0;
3828  };
3829 
3830  struct FilterStats {
3831  int64_t calls = 0;
3832  int64_t rejects = 0;
3833  double seconds = 0;
3834  };
3835  WallTimer timer_;
3836  WallTimer filter_timer_;
3837  const LocalSearchOperator* last_operator_ = nullptr;
3838  absl::flat_hash_map<const LocalSearchOperator*, OperatorStats>
3839  operator_stats_;
3840  absl::flat_hash_map<const LocalSearchFilter*, FilterStats> filter_stats_;
3841 };
3842 
3844  monitor->Install();
3845 }
3846 
3848  if (solver->IsLocalSearchProfilingEnabled()) {
3849  return new LocalSearchProfiler(solver);
3850  }
3851  return nullptr;
3852 }
3853 
3854 void DeleteLocalSearchProfiler(LocalSearchProfiler* monitor) { delete monitor; }
3855 
3856 std::string Solver::LocalSearchProfile() const {
3857  if (local_search_profiler_ != nullptr) {
3858  return local_search_profiler_->PrintOverview();
3859  }
3860  return "";
3861 }
3862 
3863 LocalSearchStatistics Solver::GetLocalSearchStatistics() const {
3864  if (local_search_profiler_ != nullptr) {
3865  return local_search_profiler_->ExportToLocalSearchStatistics();
3866  }
3867  return LocalSearchStatistics();
3868 }
3869 
3870 void LocalSearchFilterManager::InitializeForcedEvents() {
3871  const int num_events = filter_events_.size();
3872  int next_forced_event = num_events;
3873  next_forced_events_.resize(num_events);
3874  for (int i = num_events - 1; i >= 0; --i) {
3875  next_forced_events_[i] = next_forced_event;
3876  if (filter_events_[i].filter->IsIncremental() ||
3877  (filter_events_[i].event_type == FilterEventType::kRelax &&
3878  next_forced_event != num_events)) {
3879  next_forced_event = i;
3880  }
3881  }
3882 }
3883 
3885  std::vector<LocalSearchFilter*> filters)
3886  : synchronized_value_(std::numeric_limits<int64_t>::min()),
3887  accepted_value_(std::numeric_limits<int64_t>::min()) {
3888  filter_events_.reserve(2 * filters.size());
3889  for (LocalSearchFilter* filter : filters) {
3890  filter_events_.push_back({filter, FilterEventType::kRelax});
3891  }
3892  for (LocalSearchFilter* filter : filters) {
3893  filter_events_.push_back({filter, FilterEventType::kAccept});
3894  }
3895  InitializeForcedEvents();
3896 }
3897 
3899  std::vector<FilterEvent> filter_events)
3900  : filter_events_(std::move(filter_events)),
3901  synchronized_value_(std::numeric_limits<int64_t>::min()),
3902  accepted_value_(std::numeric_limits<int64_t>::min()) {
3903  InitializeForcedEvents();
3904 }
3905 
3906 // Filters' Revert() must be called in the reverse order in which their
3907 // Relax() was called.
3909  for (int i = last_event_called_; i >= 0; --i) {
3910  auto [filter, event_type] = filter_events_[i];
3911  if (event_type == FilterEventType::kRelax) filter->Revert();
3912  }
3913  last_event_called_ = -1;
3914 }
3915 
3916 // TODO(user): the behaviour of Accept relies on the initial order of
3917 // filters having at most one filter with negative objective values,
3918 // this could be fixed by having filters return their general bounds.
3920  const Assignment* delta,
3921  const Assignment* deltadelta,
3922  int64_t objective_min,
3923  int64_t objective_max) {
3924  Revert();
3925  accepted_value_ = 0;
3926  bool ok = true;
3927  const int num_events = filter_events_.size();
3928  for (int i = 0; i < num_events;) {
3929  last_event_called_ = i;
3930  auto [filter, event_type] = filter_events_[last_event_called_];
3931  switch (event_type) {
3932  case FilterEventType::kAccept: {
3933  if (monitor != nullptr) monitor->BeginFiltering(filter);
3934  const bool accept = filter->Accept(
3935  delta, deltadelta, CapSub(objective_min, accepted_value_),
3936  CapSub(objective_max, accepted_value_));
3937  ok &= accept;
3938  if (monitor != nullptr) monitor->EndFiltering(filter, !accept);
3939  if (ok) {
3940  accepted_value_ =
3941  CapAdd(accepted_value_, filter->GetAcceptedObjectiveValue());
3942  // TODO(user): handle objective min.
3943  ok = accepted_value_ <= objective_max;
3944  }
3945  break;
3946  }
3947  case FilterEventType::kRelax: {
3948  filter->Relax(delta, deltadelta);
3949  break;
3950  }
3951  default:
3952  LOG(FATAL) << "Unknown filter event type.";
3953  }
3954  // If the candidate is rejected, forced events must still be called.
3955  if (ok) {
3956  ++i;
3957  } else {
3958  i = next_forced_events_[i];
3959  }
3960  }
3961  return ok;
3962 }
3963 
3965  const Assignment* delta) {
3966  // If delta is nullptr or empty, then assignment may be a partial solution.
3967  // Send a signal to Relaxing filters to inform them,
3968  // so they can show the partial solution as a change from the empty solution.
3969  const bool reset_to_assignment = delta == nullptr || delta->Empty();
3970  // Relax in the forward direction.
3971  for (auto [filter, event_type] : filter_events_) {
3972  switch (event_type) {
3973  case FilterEventType::kAccept: {
3974  break;
3975  }
3976  case FilterEventType::kRelax: {
3977  if (reset_to_assignment) {
3978  filter->Reset();
3979  filter->Relax(assignment, nullptr);
3980  } else {
3981  filter->Relax(delta, nullptr);
3982  }
3983  break;
3984  }
3985  default:
3986  LOG(FATAL) << "Unknown filter event type.";
3987  }
3988  }
3989  // Synchronize/Commit backwards, so filters can read changes from their
3990  // dependencies before those are synchronized/committed.
3991  synchronized_value_ = 0;
3992  for (auto [filter, event_type] : ::gtl::reversed_view(filter_events_)) {
3993  switch (event_type) {
3994  case FilterEventType::kAccept: {
3995  filter->Synchronize(assignment, delta);
3996  synchronized_value_ = CapAdd(synchronized_value_,
3997  filter->GetSynchronizedObjectiveValue());
3998  break;
3999  }
4000  case FilterEventType::kRelax: {
4001  filter->Commit(assignment, delta);
4002  break;
4003  }
4004  default:
4005  LOG(FATAL) << "Unknown filter event type.";
4006  }
4007  }
4008 }
4009 
4010 // ----- Finds a neighbor of the assignment passed -----
4011 
4013  public:
4014  FindOneNeighbor(Assignment* const assignment, IntVar* objective,
4015  SolutionPool* const pool,
4016  LocalSearchOperator* const ls_operator,
4017  DecisionBuilder* const sub_decision_builder,
4018  const RegularLimit* const limit,
4019  LocalSearchFilterManager* filter_manager);
4020  ~FindOneNeighbor() override {}
4021  Decision* Next(Solver* const solver) override;
4022  std::string DebugString() const override { return "FindOneNeighbor"; }
4023 
4024  private:
4025  bool FilterAccept(Solver* solver, Assignment* delta, Assignment* deltadelta,
4026  int64_t objective_min, int64_t objective_max);
4027  void SynchronizeAll(Solver* solver);
4028 
4029  Assignment* const assignment_;
4030  IntVar* const objective_;
4031  std::unique_ptr<Assignment> reference_assignment_;
4032  SolutionPool* const pool_;
4033  LocalSearchOperator* const ls_operator_;
4034  DecisionBuilder* const sub_decision_builder_;
4035  RegularLimit* limit_;
4036  const RegularLimit* const original_limit_;
4037  bool neighbor_found_;
4038  LocalSearchFilterManager* const filter_manager_;
4039  int64_t solutions_since_last_check_;
4040  int64_t check_period_;
4041  Assignment last_checked_assignment_;
4042  bool has_checked_assignment_ = false;
4043 };
4044 
4045 // reference_assignment_ is used to keep track of the last assignment on which
4046 // operators were started, assignment_ corresponding to the last successful
4047 // neighbor.
4049  IntVar* objective, SolutionPool* const pool,
4050  LocalSearchOperator* const ls_operator,
4051  DecisionBuilder* const sub_decision_builder,
4052  const RegularLimit* const limit,
4053  LocalSearchFilterManager* filter_manager)
4054  : assignment_(assignment),
4055  objective_(objective),
4056  reference_assignment_(new Assignment(assignment_)),
4057  pool_(pool),
4058  ls_operator_(ls_operator),
4059  sub_decision_builder_(sub_decision_builder),
4060  limit_(nullptr),
4061  original_limit_(limit),
4062  neighbor_found_(false),
4063  filter_manager_(filter_manager),
4064  solutions_since_last_check_(0),
4065  check_period_(
4066  assignment_->solver()->parameters().check_solution_period()),
4067  last_checked_assignment_(assignment) {
4068  CHECK(nullptr != assignment);
4069  CHECK(nullptr != ls_operator);
4070 
4071  Solver* const solver = assignment_->solver();
4072  // If limit is nullptr, default limit is 1 solution
4073  if (nullptr == limit) {
4074  limit_ = solver->MakeSolutionsLimit(1);
4075  } else {
4076  limit_ = limit->MakeIdenticalClone();
4077  // TODO(user): Support skipping neighborhood checks for limits accepting
4078  // more than one solution (e.g. best accept). For now re-enabling systematic
4079  // checks.
4080  if (limit_->solutions() != 1) {
4081  VLOG(1) << "Disabling neighbor-check skipping outside of first accept.";
4082  check_period_ = 1;
4083  }
4084  }
4085  // TODO(user): Support skipping neighborhood checks with LNS (at least on
4086  // the non-LNS operators).
4087  if (ls_operator->HasFragments()) {
4088  VLOG(1) << "Disabling neighbor-check skipping for LNS.";
4089  check_period_ = 1;
4090  }
4091 
4092  if (!reference_assignment_->HasObjective()) {
4093  reference_assignment_->AddObjective(objective_);
4094  }
4095 }
4096 
4098  CHECK(nullptr != solver);
4099 
4100  if (original_limit_ != nullptr) {
4101  limit_->Copy(original_limit_);
4102  }
4103 
4104  if (!last_checked_assignment_.HasObjective()) {
4105  last_checked_assignment_.AddObjective(assignment_->Objective());
4106  }
4107 
4108  if (!neighbor_found_) {
4109  // Only called on the first call to Next(), reference_assignment_ has not
4110  // been synced with assignment_ yet
4111 
4112  // Keeping the code in case a performance problem forces us to
4113  // use the old code with a zero test on pool_.
4114  // reference_assignment_->CopyIntersection(assignment_);
4115  pool_->Initialize(assignment_);
4116  SynchronizeAll(solver);
4117  }
4118 
4119  {
4120  // Another assignment is needed to apply the delta
4121  Assignment* assignment_copy =
4122  solver->MakeAssignment(reference_assignment_.get());
4123  int counter = 0;
4124 
4125  DecisionBuilder* restore = solver->MakeRestoreAssignment(assignment_copy);
4126  if (sub_decision_builder_) {
4127  restore = solver->Compose(restore, sub_decision_builder_);
4128  }
4129  Assignment* delta = solver->MakeAssignment();
4130  Assignment* deltadelta = solver->MakeAssignment();
4131  while (true) {
4132  if (!ls_operator_->HoldsDelta()) {
4133  delta->Clear();
4134  }
4135  delta->ClearObjective();
4136  deltadelta->Clear();
4137  solver->TopPeriodicCheck();
4138  if (++counter >= absl::GetFlag(FLAGS_cp_local_search_sync_frequency) &&
4139  pool_->SyncNeeded(reference_assignment_.get())) {
4140  // TODO(user) : SyncNeed(assignment_) ?
4141  counter = 0;
4142  SynchronizeAll(solver);
4143  }
4144 
4145  bool has_neighbor = false;
4146  if (!limit_->Check()) {
4147  solver->GetLocalSearchMonitor()->BeginMakeNextNeighbor(ls_operator_);
4148  has_neighbor = ls_operator_->MakeNextNeighbor(delta, deltadelta);
4150  ls_operator_, has_neighbor, delta, deltadelta);
4151  }
4152 
4153  if (has_neighbor && !solver->IsUncheckedSolutionLimitReached()) {
4154  solver->neighbors_ += 1;
4155  // All filters must be called for incrementality reasons.
4156  // Empty deltas must also be sent to incremental filters; can be needed
4157  // to resync filters on non-incremental (empty) moves.
4158  // TODO(user): Don't call both if no filter is incremental and one
4159  // of them returned false.
4160  solver->GetLocalSearchMonitor()->BeginFilterNeighbor(ls_operator_);
4161  const bool mh_filter =
4162  AcceptDelta(solver->ParentSearch(), delta, deltadelta);
4163  int64_t objective_min = std::numeric_limits<int64_t>::min();
4164  int64_t objective_max = std::numeric_limits<int64_t>::max();
4165  if (objective_) {
4166  objective_min = objective_->Min();
4167  objective_max = objective_->Max();
4168  }
4169  if (delta->HasObjective() && delta->Objective() == objective_) {
4170  objective_min = std::max(objective_min, delta->ObjectiveMin());
4171  objective_max = std::min(objective_max, delta->ObjectiveMax());
4172  }
4173  const bool move_filter = FilterAccept(solver, delta, deltadelta,
4174  objective_min, objective_max);
4176  ls_operator_, mh_filter && move_filter);
4177  if (!mh_filter || !move_filter) {
4178  if (filter_manager_ != nullptr) filter_manager_->Revert();
4179  continue;
4180  }
4181  solver->filtered_neighbors_ += 1;
4182  if (delta->HasObjective()) {
4183  if (!assignment_copy->HasObjective()) {
4184  assignment_copy->AddObjective(delta->Objective());
4185  }
4186  if (!assignment_->HasObjective()) {
4187  assignment_->AddObjective(delta->Objective());
4188  last_checked_assignment_.AddObjective(delta->Objective());
4189  }
4190  }
4191  assignment_copy->CopyIntersection(reference_assignment_.get());
4192  assignment_copy->CopyIntersection(delta);
4193  solver->GetLocalSearchMonitor()->BeginAcceptNeighbor(ls_operator_);
4194  const bool check_solution = (solutions_since_last_check_ == 0) ||
4195  !solver->UseFastLocalSearch() ||
4196  // LNS deltas need to be restored
4197  !delta->AreAllElementsBound();
4198  if (has_checked_assignment_) solutions_since_last_check_++;
4199  if (solutions_since_last_check_ >= check_period_) {
4200  solutions_since_last_check_ = 0;
4201  }
4202  const bool accept = !check_solution || solver->SolveAndCommit(restore);
4203  solver->GetLocalSearchMonitor()->EndAcceptNeighbor(ls_operator_,
4204  accept);
4205  if (accept) {
4206  solver->accepted_neighbors_ += 1;
4207  if (check_solution) {
4208  solver->SetSearchContext(solver->ParentSearch(),
4209  ls_operator_->DebugString());
4210  assignment_->Store();
4211  last_checked_assignment_.CopyIntersection(assignment_);
4212  neighbor_found_ = true;
4213  has_checked_assignment_ = true;
4214  return nullptr;
4215  }
4216  solver->SetSearchContext(solver->ActiveSearch(),
4217  ls_operator_->DebugString());
4218  assignment_->CopyIntersection(assignment_copy);
4219  assignment_->SetObjectiveValue(
4220  filter_manager_ ? filter_manager_->GetAcceptedObjectiveValue()
4221  : 0);
4222  // Advancing local search to the current solution without
4223  // checking.
4224  // TODO(user): support the case were limit_ accepts more than
4225  // one solution (e.g. best accept).
4226  AcceptUncheckedNeighbor(solver->ParentSearch());
4227  solver->IncrementUncheckedSolutionCounter();
4228  pool_->RegisterNewSolution(assignment_);
4229  SynchronizeAll(solver);
4230  // NOTE: SynchronizeAll() sets neighbor_found_ to false, force it
4231  // back to true when skipping checks.
4232  neighbor_found_ = true;
4233  } else {
4234  if (filter_manager_ != nullptr) filter_manager_->Revert();
4235  if (check_period_ > 1 && has_checked_assignment_) {
4236  // Filtering is not perfect, disabling fast local search and
4237  // resynchronizing with the last checked solution.
4238  // TODO(user): Restore state of local search operators to
4239  // make sure we are exploring neighbors in the same order. This can
4240  // affect the local optimum found.
4241  VLOG(1) << "Imperfect filtering detected, backtracking to last "
4242  "checked solution and checking all solutions.";
4243  check_period_ = 1;
4244  solutions_since_last_check_ = 0;
4245  pool_->RegisterNewSolution(&last_checked_assignment_);
4246  SynchronizeAll(solver);
4247  assignment_->CopyIntersection(&last_checked_assignment_);
4248  }
4249  }
4250  } else {
4251  if (neighbor_found_) {
4252  // In case the last checked assignment isn't the current one, restore
4253  // it to make sure the solver knows about it, especially if this is
4254  // the end of the search.
4255  // TODO(user): Compare assignments in addition to their cost.
4256  if (last_checked_assignment_.ObjectiveValue() !=
4257  assignment_->ObjectiveValue()) {
4258  // If restoring fails this means filtering is not perfect and the
4259  // solver will consider the last checked assignment.
4260  assignment_copy->CopyIntersection(assignment_);
4261  if (!solver->SolveAndCommit(restore)) solver->Fail();
4262  last_checked_assignment_.CopyIntersection(assignment_);
4263  has_checked_assignment_ = true;
4264  return nullptr;
4265  }
4266  AcceptNeighbor(solver->ParentSearch());
4267  // Keeping the code in case a performance problem forces us to
4268  // use the old code with a zero test on pool_.
4269  // reference_assignment_->CopyIntersection(assignment_);
4270  pool_->RegisterNewSolution(assignment_);
4271  SynchronizeAll(solver);
4272  } else {
4273  break;
4274  }
4275  }
4276  }
4277  }
4278  solver->Fail();
4279  return nullptr;
4280 }
4281 
4282 bool FindOneNeighbor::FilterAccept(Solver* solver, Assignment* delta,
4283  Assignment* deltadelta,
4284  int64_t objective_min,
4285  int64_t objective_max) {
4286  if (filter_manager_ == nullptr) return true;
4287  LocalSearchMonitor* const monitor = solver->GetLocalSearchMonitor();
4288  return filter_manager_->Accept(monitor, delta, deltadelta, objective_min,
4289  objective_max);
4290 }
4291 
4292 void FindOneNeighbor::SynchronizeAll(Solver* solver) {
4293  pool_->GetNextSolution(reference_assignment_.get());
4294  neighbor_found_ = false;
4295  limit_->Init();
4296  solver->GetLocalSearchMonitor()->BeginOperatorStart();
4297  ls_operator_->Start(reference_assignment_.get());
4298  if (filter_manager_ != nullptr) {
4299  filter_manager_->Synchronize(reference_assignment_.get(), nullptr);
4300  }
4301  solver->GetLocalSearchMonitor()->EndOperatorStart();
4302 }
4303 
4304 // ---------- Local Search Phase Parameters ----------
4305 
4307  public:
4311  RegularLimit* const limit,
4313  : objective_(objective),
4314  solution_pool_(pool),
4315  ls_operator_(ls_operator),
4316  sub_decision_builder_(sub_decision_builder),
4317  limit_(limit),
4318  filter_manager_(filter_manager) {}
4320  std::string DebugString() const override {
4321  return "LocalSearchPhaseParameters";
4322  }
4323 
4324  IntVar* objective() const { return objective_; }
4325  SolutionPool* solution_pool() const { return solution_pool_; }
4326  LocalSearchOperator* ls_operator() const { return ls_operator_; }
4328  return sub_decision_builder_;
4329  }
4330  RegularLimit* limit() const { return limit_; }
4332  return filter_manager_;
4333  }
4334 
4335  private:
4336  IntVar* const objective_;
4337  SolutionPool* const solution_pool_;
4338  LocalSearchOperator* const ls_operator_;
4339  DecisionBuilder* const sub_decision_builder_;
4340  RegularLimit* const limit_;
4341  LocalSearchFilterManager* const filter_manager_;
4342 };
4343 
4345  IntVar* objective, LocalSearchOperator* const ls_operator,
4346  DecisionBuilder* const sub_decision_builder) {
4348  ls_operator, sub_decision_builder,
4349  nullptr, nullptr);
4350 }
4351 
4353  IntVar* objective, LocalSearchOperator* const ls_operator,
4354  DecisionBuilder* const sub_decision_builder, RegularLimit* const limit) {
4356  ls_operator, sub_decision_builder,
4357  limit, nullptr);
4358 }
4359 
4361  IntVar* objective, LocalSearchOperator* const ls_operator,
4362  DecisionBuilder* const sub_decision_builder, RegularLimit* const limit,
4363  LocalSearchFilterManager* filter_manager) {
4365  ls_operator, sub_decision_builder,
4366  limit, filter_manager);
4367 }
4368 
4370  IntVar* objective, SolutionPool* const pool,
4371  LocalSearchOperator* const ls_operator,
4372  DecisionBuilder* const sub_decision_builder) {
4373  return MakeLocalSearchPhaseParameters(objective, pool, ls_operator,
4374  sub_decision_builder, nullptr, nullptr);
4375 }
4376 
4378  IntVar* objective, SolutionPool* const pool,
4379  LocalSearchOperator* const ls_operator,
4380  DecisionBuilder* const sub_decision_builder, RegularLimit* const limit) {
4381  return MakeLocalSearchPhaseParameters(objective, pool, ls_operator,
4382  sub_decision_builder, limit, nullptr);
4383 }
4384 
4386  IntVar* objective, SolutionPool* const pool,
4387  LocalSearchOperator* const ls_operator,
4388  DecisionBuilder* const sub_decision_builder, RegularLimit* const limit,
4389  LocalSearchFilterManager* filter_manager) {
4390  return RevAlloc(new LocalSearchPhaseParameters(objective, pool, ls_operator,
4391  sub_decision_builder, limit,
4392  filter_manager));
4393 }
4394 
4395 namespace {
4396 // ----- NestedSolve decision wrapper -----
4397 
4398 // This decision calls a nested Solve on the given DecisionBuilder in its
4399 // left branch; does nothing in the left branch.
4400 // The state of the decision corresponds to the result of the nested Solve:
4401 // DECISION_PENDING - Nested Solve not called yet
4402 // DECISION_FAILED - Nested Solve failed
4403 // DECISION_FOUND - Nested Solve succeeded
4404 
4405 class NestedSolveDecision : public Decision {
4406  public:
4407  // This enum is used internally to tag states in the local search tree.
4408  enum StateType { DECISION_PENDING, DECISION_FAILED, DECISION_FOUND };
4409 
4410  NestedSolveDecision(DecisionBuilder* const db, bool restore,
4411  const std::vector<SearchMonitor*>& monitors);
4412  NestedSolveDecision(DecisionBuilder* const db, bool restore);
4413  ~NestedSolveDecision() override {}
4414  void Apply(Solver* const solver) override;
4415  void Refute(Solver* const solver) override;
4416  std::string DebugString() const override { return "NestedSolveDecision"; }
4417  int state() const { return state_; }
4418 
4419  private:
4420  DecisionBuilder* const db_;
4421  bool restore_;
4422  std::vector<SearchMonitor*> monitors_;
4423  int state_;
4424 };
4425 
4426 NestedSolveDecision::NestedSolveDecision(
4427  DecisionBuilder* const db, bool restore,
4428  const std::vector<SearchMonitor*>& monitors)
4429  : db_(db),
4430  restore_(restore),
4431  monitors_(monitors),
4432  state_(DECISION_PENDING) {
4433  CHECK(nullptr != db);
4434 }
4435 
4436 NestedSolveDecision::NestedSolveDecision(DecisionBuilder* const db,
4437  bool restore)
4438  : db_(db), restore_(restore), state_(DECISION_PENDING) {
4439  CHECK(nullptr != db);
4440 }
4441 
4442 void NestedSolveDecision::Apply(Solver* const solver) {
4443  CHECK(nullptr != solver);
4444  if (restore_) {
4445  if (solver->Solve(db_, monitors_)) {
4446  solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FOUND));
4447  } else {
4448  solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FAILED));
4449  }
4450  } else {
4451  if (solver->SolveAndCommit(db_, monitors_)) {
4452  solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FOUND));
4453  } else {
4454  solver->SaveAndSetValue(&state_, static_cast<int>(DECISION_FAILED));
4455  }
4456  }
4457 }
4458 
4459 void NestedSolveDecision::Refute(Solver* const solver) {}
4460 
4461 // ----- Local search decision builder -----
4462 
4463 // Given a first solution (resulting from either an initial assignment or the
4464 // result of a decision builder), it searches for neighbors using a local
4465 // search operator. The first solution corresponds to the first leaf of the
4466 // search.
4467 // The local search applies to the variables contained either in the assignment
4468 // or the vector of variables passed.
4469 
4470 class LocalSearch : public DecisionBuilder {
4471  public:
4472  LocalSearch(Assignment* const assignment, IntVar* objective,
4473  SolutionPool* const pool, LocalSearchOperator* const ls_operator,
4474  DecisionBuilder* const sub_decision_builder,
4475  RegularLimit* const limit,
4476  LocalSearchFilterManager* filter_manager);
4477  // TODO(user): find a way to not have to pass vars here: redundant with
4478  // variables in operators
4479  LocalSearch(const std::vector<IntVar*>& vars, IntVar* objective,
4480  SolutionPool* const pool, DecisionBuilder* const first_solution,
4481  LocalSearchOperator* const ls_operator,
4482  DecisionBuilder* const sub_decision_builder,
4483  RegularLimit* const limit,
4484  LocalSearchFilterManager* filter_manager);
4485  LocalSearch(const std::vector<IntVar*>& vars, IntVar* objective,
4486  SolutionPool* const pool, DecisionBuilder* const first_solution,
4487  DecisionBuilder* const first_solution_sub_decision_builder,
4488  LocalSearchOperator* const ls_operator,
4489  DecisionBuilder* const sub_decision_builder,
4490  RegularLimit* const limit,
4491  LocalSearchFilterManager* filter_manager);
4492  LocalSearch(const std::vector<SequenceVar*>& vars, IntVar* objective,
4493  SolutionPool* const pool, DecisionBuilder* const first_solution,
4494  LocalSearchOperator* const ls_operator,
4495  DecisionBuilder* const sub_decision_builder,
4496  RegularLimit* const limit,
4497  LocalSearchFilterManager* filter_manager);
4498  ~LocalSearch() override;
4499  Decision* Next(Solver* const solver) override;
4500  std::string DebugString() const override { return "LocalSearch"; }
4501  void Accept(ModelVisitor* const visitor) const override;
4502 
4503  protected:
4504  void PushFirstSolutionDecision(DecisionBuilder* first_solution);
4505  void PushLocalSearchDecision();
4506 
4507  private:
4508  Assignment* assignment_;
4509  IntVar* const objective_ = nullptr;
4510  SolutionPool* const pool_;
4511  LocalSearchOperator* const ls_operator_;
4512  DecisionBuilder* const first_solution_sub_decision_builder_;
4513  DecisionBuilder* const sub_decision_builder_;
4514  std::vector<NestedSolveDecision*> nested_decisions_;
4515  int nested_decision_index_;
4516  RegularLimit* const limit_;
4517  LocalSearchFilterManager* const filter_manager_;
4518  bool has_started_;
4519 };
4520 
4521 LocalSearch::LocalSearch(Assignment* const assignment, IntVar* objective,
4522  SolutionPool* const pool,
4523  LocalSearchOperator* const ls_operator,
4524  DecisionBuilder* const sub_decision_builder,
4525  RegularLimit* const limit,
4526  LocalSearchFilterManager* filter_manager)
4527  : assignment_(nullptr),
4528  objective_(objective),
4529  pool_(pool),
4530  ls_operator_(ls_operator),
4531  first_solution_sub_decision_builder_(sub_decision_builder),
4532  sub_decision_builder_(sub_decision_builder),
4533  nested_decision_index_(0),
4534  limit_(limit),
4535  filter_manager_(filter_manager),
4536  has_started_(false) {
4537  CHECK(nullptr != assignment);
4538  CHECK(nullptr != ls_operator);
4539  Solver* const solver = assignment->solver();
4540  assignment_ = solver->GetOrCreateLocalSearchState();
4541  assignment_->Copy(assignment);
4542  DecisionBuilder* restore = solver->MakeRestoreAssignment(assignment);
4543  PushFirstSolutionDecision(restore);
4544  PushLocalSearchDecision();
4545 }
4546 
4547 LocalSearch::LocalSearch(const std::vector<IntVar*>& vars, IntVar* objective,
4548  SolutionPool* const pool,
4549  DecisionBuilder* const first_solution,
4550  LocalSearchOperator* const ls_operator,
4551  DecisionBuilder* const sub_decision_builder,
4552  RegularLimit* const limit,
4553  LocalSearchFilterManager* filter_manager)
4554  : assignment_(nullptr),
4555  objective_(objective),
4556  pool_(pool),
4557  ls_operator_(ls_operator),
4558  first_solution_sub_decision_builder_(sub_decision_builder),
4559  sub_decision_builder_(sub_decision_builder),
4560  nested_decision_index_(0),
4561  limit_(limit),
4562  filter_manager_(filter_manager),
4563  has_started_(false) {
4564  CHECK(nullptr != first_solution);
4565  CHECK(nullptr != ls_operator);
4566  CHECK(!vars.empty());
4567  Solver* const solver = vars[0]->solver();
4568  assignment_ = solver->GetOrCreateLocalSearchState();
4569  assignment_->Add(vars);
4570  PushFirstSolutionDecision(first_solution);
4571  PushLocalSearchDecision();
4572 }
4573 
4574 LocalSearch::LocalSearch(
4575  const std::vector<IntVar*>& vars, IntVar* objective,
4576  SolutionPool* const pool, DecisionBuilder* const first_solution,
4577  DecisionBuilder* const first_solution_sub_decision_builder,
4578  LocalSearchOperator* const ls_operator,
4579  DecisionBuilder* const sub_decision_builder, RegularLimit* const limit,
4580  LocalSearchFilterManager* filter_manager)
4581  : assignment_(nullptr),
4582  objective_(objective),
4583  pool_(pool),
4584  ls_operator_(ls_operator),
4585  first_solution_sub_decision_builder_(first_solution_sub_decision_builder),
4586  sub_decision_builder_(sub_decision_builder),
4587  nested_decision_index_(0),
4588  limit_(limit),
4589  filter_manager_(filter_manager),
4590  has_started_(false) {
4591  CHECK(nullptr != first_solution);
4592  CHECK(nullptr != ls_operator);
4593  CHECK(!vars.empty());
4594  Solver* const solver = vars[0]->solver();
4595  assignment_ = solver->GetOrCreateLocalSearchState();
4596  assignment_->Add(vars);
4597  PushFirstSolutionDecision(first_solution);
4598  PushLocalSearchDecision();
4599 }
4600 
4601 LocalSearch::LocalSearch(const std::vector<SequenceVar*>& vars,
4602  IntVar* objective, SolutionPool* const pool,
4603  DecisionBuilder* const first_solution,
4604  LocalSearchOperator* const ls_operator,
4605  DecisionBuilder* const sub_decision_builder,
4606  RegularLimit* const limit,
4607  LocalSearchFilterManager* filter_manager)
4608  : assignment_(nullptr),
4609  objective_(objective),
4610  pool_(pool),
4611  ls_operator_(ls_operator),
4612  first_solution_sub_decision_builder_(sub_decision_builder),
4613  sub_decision_builder_(sub_decision_builder),
4614  nested_decision_index_(0),
4615  limit_(limit),
4616  filter_manager_(filter_manager),
4617  has_started_(false) {
4618  CHECK(nullptr != first_solution);
4619  CHECK(nullptr != ls_operator);
4620  CHECK(!vars.empty());
4621  Solver* const solver = vars[0]->solver();
4622  assignment_ = solver->GetOrCreateLocalSearchState();
4623  assignment_->Add(vars);
4624  PushFirstSolutionDecision(first_solution);
4625  PushLocalSearchDecision();
4626 }
4627 
4628 LocalSearch::~LocalSearch() {}
4629 
4630 // Model Visitor support.
4631 void LocalSearch::Accept(ModelVisitor* const visitor) const {
4632  DCHECK(assignment_ != nullptr);
4633  visitor->BeginVisitExtension(ModelVisitor::kVariableGroupExtension);
4634  // We collect decision variables from the assignment.
4635  const std::vector<IntVarElement>& elements =
4636  assignment_->IntVarContainer().elements();
4637  if (!elements.empty()) {
4638  std::vector<IntVar*> vars;
4639  for (const IntVarElement& elem : elements) {
4640  vars.push_back(elem.Var());
4641  }
4642  visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
4643  vars);
4644  }
4645  const std::vector<IntervalVarElement>& interval_elements =
4646  assignment_->IntervalVarContainer().elements();
4647  if (!interval_elements.empty()) {
4648  std::vector<IntervalVar*> interval_vars;
4649  for (const IntervalVarElement& elem : interval_elements) {
4650  interval_vars.push_back(elem.Var());
4651  }
4652  visitor->VisitIntervalArrayArgument(ModelVisitor::kIntervalsArgument,
4653  interval_vars);
4654  }
4655  visitor->EndVisitExtension(ModelVisitor::kVariableGroupExtension);
4656 }
4657 
4658 // This is equivalent to a multi-restart decision builder
4659 // TODO(user): abstract this from the local search part
4660 // TODO(user): handle the case where the tree depth is not enough to hold
4661 // all solutions.
4662 
4663 Decision* LocalSearch::Next(Solver* const solver) {
4664  CHECK(nullptr != solver);
4665  CHECK_LT(0, nested_decisions_.size());
4666  if (!has_started_) {
4667  nested_decision_index_ = 0;
4668  solver->SaveAndSetValue(&has_started_, true);
4669  } else if (nested_decision_index_ < 0) {
4670  solver->Fail();
4671  }
4672  NestedSolveDecision* decision = nested_decisions_[nested_decision_index_];
4673  const int state = decision->state();
4674  switch (state) {
4675  case NestedSolveDecision::DECISION_FAILED: {
4676  // A local optimum has been reached. The search will continue only if we
4677  // accept up-hill moves (due to metaheuristics). In this case we need to
4678  // reset neighborhood optimal routes.
4679  ls_operator_->Reset();
4680  if (!LocalOptimumReached(solver->ActiveSearch())) {
4681  nested_decision_index_ = -1; // Stop the search
4682  }
4683  solver->Fail();
4684  return nullptr;
4685  }
4686  case NestedSolveDecision::DECISION_PENDING: {
4687  // TODO(user): Find a way to make this balancing invisible to the
4688  // user (no increase in branch or fail counts for instance).
4689  const int32_t kLocalSearchBalancedTreeDepth = 32;
4690  const int depth = solver->SearchDepth();
4691  if (depth < kLocalSearchBalancedTreeDepth) {
4692  return solver->balancing_decision();
4693  }
4694  if (depth > kLocalSearchBalancedTreeDepth) {
4695  solver->Fail();
4696  }
4697  return decision;
4698  }
4699  case NestedSolveDecision::DECISION_FOUND: {
4700  // Next time go to next decision
4701  if (nested_decision_index_ + 1 < nested_decisions_.size()) {
4702  ++nested_decision_index_;
4703  }
4704  return nullptr;
4705  }
4706  default: {
4707  LOG(ERROR) << "Unknown local search state";
4708  return nullptr;
4709  }
4710  }
4711  return nullptr;
4712 }
4713 
4714 void LocalSearch::PushFirstSolutionDecision(DecisionBuilder* first_solution) {
4715  CHECK(first_solution);
4716  Solver* const solver = assignment_->solver();
4717  DecisionBuilder* store = solver->MakeStoreAssignment(assignment_);
4718  DecisionBuilder* first_solution_and_store = solver->Compose(
4719  first_solution, first_solution_sub_decision_builder_, store);
4720  std::vector<SearchMonitor*> monitor;
4721  monitor.push_back(limit_);
4722  nested_decisions_.push_back(solver->RevAlloc(
4723  new NestedSolveDecision(first_solution_and_store, false, monitor)));
4724 }
4725 
4726 void LocalSearch::PushLocalSearchDecision() {
4727  Solver* const solver = assignment_->solver();
4728  DecisionBuilder* find_neighbors = solver->RevAlloc(
4729  new FindOneNeighbor(assignment_, objective_, pool_, ls_operator_,
4730  sub_decision_builder_, limit_, filter_manager_));
4731  nested_decisions_.push_back(
4732  solver->RevAlloc(new NestedSolveDecision(find_neighbors, false)));
4733 }
4734 
4735 class DefaultSolutionPool : public SolutionPool {
4736  public:
4737  DefaultSolutionPool() {}
4738 
4739  ~DefaultSolutionPool() override {}
4740 
4741  void Initialize(Assignment* const assignment) override {
4742  reference_assignment_ = absl::make_unique<Assignment>(assignment);
4743  }
4744 
4745  void RegisterNewSolution(Assignment* const assignment) override {
4746  reference_assignment_->CopyIntersection(assignment);
4747  }
4748 
4749  void GetNextSolution(Assignment* const assignment) override {
4750  assignment->CopyIntersection(reference_assignment_.get());
4751  }
4752 
4753  bool SyncNeeded(Assignment* const local_assignment) override { return false; }
4754 
4755  std::string DebugString() const override { return "DefaultSolutionPool"; }
4756 
4757  private:
4758  std::unique_ptr<Assignment> reference_assignment_;
4759 };
4760 } // namespace
4761 
4763  return RevAlloc(new DefaultSolutionPool());
4764 }
4765 
4768  return RevAlloc(new LocalSearch(
4769  assignment, parameters->objective(), parameters->solution_pool(),
4770  parameters->ls_operator(), parameters->sub_decision_builder(),
4771  parameters->limit(), parameters->filter_manager()));
4772 }
4773 
4775  const std::vector<IntVar*>& vars, DecisionBuilder* first_solution,
4777  return RevAlloc(new LocalSearch(
4778  vars, parameters->objective(), parameters->solution_pool(),
4779  first_solution, parameters->ls_operator(),
4780  parameters->sub_decision_builder(), parameters->limit(),
4781  parameters->filter_manager()));
4782 }
4783 
4785  const std::vector<IntVar*>& vars, DecisionBuilder* first_solution,
4786  DecisionBuilder* first_solution_sub_decision_builder,
4788  return RevAlloc(new LocalSearch(
4789  vars, parameters->objective(), parameters->solution_pool(),
4790  first_solution, first_solution_sub_decision_builder,
4791  parameters->ls_operator(), parameters->sub_decision_builder(),
4792  parameters->limit(), parameters->filter_manager()));
4793 }
4794 
4796  const std::vector<SequenceVar*>& vars, DecisionBuilder* first_solution,
4798  return RevAlloc(new LocalSearch(
4799  vars, parameters->objective(), parameters->solution_pool(),
4800  first_solution, parameters->ls_operator(),
4801  parameters->sub_decision_builder(), parameters->limit(),
4802  parameters->filter_manager()));
4803 }
4804 } // 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:498
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:895
#define CHECK_LT(val1, val2)
Definition: base/logging.h:708
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:705
#define CHECK_GE(val1, val2)
Definition: base/logging.h:709
#define CHECK_GT(val1, val2)
Definition: base/logging.h:710
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:897
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:898
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:896
#define LOG(severity)
Definition: base/logging.h:423
#define DCHECK(condition)
Definition: base/logging.h:892
#define CHECK_LE(val1, val2)
Definition: base/logging.h:707
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:893
#define VLOG(verboselevel)
Definition: base/logging.h:986
void Start()
Definition: timer.h:31
void Stop()
Definition: timer.h:39
double Get() const
Definition: timer.h:45
const std::vector< E > & elements() const
const E & Element(const V *const var) const
An Assignment is a variable -> domains mapping, used to report solutions to the user.
const IntContainer & IntVarContainer() const
void CopyIntersection(const Assignment *assignment)
Copies the intersection of the two assignments to the current assignment.
AssignmentContainer< IntVar, IntVarElement > IntContainer
const IntervalContainer & IntervalVarContainer() 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)
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.
PathOperator(const std::vector< IntVar * > &next_vars, const std::vector< IntVar * > &path_vars, int number_of_base_nodes, bool skip_locally_optimal_paths, bool accept_path_end_base, std::function< int(int64_t)> start_empty_path_class)
Builds an instance of PathOperator from next and path variables.
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()
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
ChainRange Chains(int path) const
const std::vector< std::pair< int, int > > & ChangedArcs() 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.
T * RevAlloc(T *object)
Registers the given object as being reversible.
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.
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
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:200
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:206
ReverseView< Container > reversed_view(const Container &c)
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)
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)
LocalSearchFilter * MakeUnaryDimensionFilter(Solver *solver, std::unique_ptr< UnaryDimensionChecker > checker)
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)
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