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