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