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