OR-Tools  9.6
routing_neighborhoods.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
15 
16 #include <algorithm>
17 #include <cstdint>
18 #include <functional>
19 #include <iterator>
20 #include <memory>
21 #include <queue>
22 #include <tuple>
23 #include <utility>
24 #include <vector>
25 
26 #include "ortools/base/int_type.h"
28 #include "ortools/base/logging.h"
34 #include "ortools/util/bitset.h"
35 
36 namespace operations_research {
37 
39  const std::vector<IntVar*>& vars,
40  const std::vector<IntVar*>& secondary_vars,
41  std::function<int(int64_t)> start_empty_path_class,
42  RoutingTransitCallback2 arc_evaluator)
43  : PathOperator(vars, secondary_vars, 2, true, false,
44  std::move(start_empty_path_class)),
45  arc_evaluator_(std::move(arc_evaluator)) {}
46 
48  const int64_t before_chain = BaseNode(0);
49  int64_t chain_end = Next(before_chain);
50  if (IsPathEnd(chain_end)) return false;
51  const int64_t destination = BaseNode(1);
52  if (chain_end == destination) return false;
53  const int64_t max_arc_value = arc_evaluator_(destination, chain_end);
54  int64_t next = Next(chain_end);
55  while (!IsPathEnd(next) && arc_evaluator_(chain_end, next) <= max_arc_value) {
56  if (next == destination) return false;
57  chain_end = next;
58  next = Next(chain_end);
59  }
60  return MoveChainAndRepair(before_chain, chain_end, destination);
61 }
62 
63 bool MakeRelocateNeighborsOperator::MoveChainAndRepair(int64_t before_chain,
64  int64_t chain_end,
65  int64_t destination) {
66  if (MoveChain(before_chain, chain_end, destination)) {
67  if (!IsPathStart(destination)) {
68  int64_t current = Prev(destination);
69  int64_t last = chain_end;
70  if (current == last) { // chain was just before destination
71  current = before_chain;
72  }
73  while (last >= 0 && !IsPathStart(current) && current != last) {
74  last = Reposition(current, last);
75  current = Prev(current);
76  }
77  }
78  return true;
79  }
80  return false;
81 }
82 
83 int64_t MakeRelocateNeighborsOperator::Reposition(int64_t before_to_move,
84  int64_t up_to) {
85  const int64_t kNoChange = -1;
86  const int64_t to_move = Next(before_to_move);
87  int64_t next = Next(to_move);
88  if (Var(to_move)->Contains(next)) {
89  return kNoChange;
90  }
91  int64_t prev = next;
92  next = Next(next);
93  while (prev != up_to) {
94  if (Var(prev)->Contains(to_move) && Var(to_move)->Contains(next)) {
95  MoveChain(before_to_move, to_move, prev);
96  return up_to;
97  }
98  prev = next;
99  next = Next(next);
100  }
101  if (Var(prev)->Contains(to_move)) {
102  MoveChain(before_to_move, to_move, prev);
103  return to_move;
104  }
105  return kNoChange;
106 }
107 
109  const std::vector<IntVar*>& vars,
110  const std::vector<IntVar*>& secondary_vars,
111  std::function<int(int64_t)> start_empty_path_class,
112  std::vector<std::vector<int64_t>> alternative_sets,
113  RoutingTransitCallback2 arc_evaluator)
114  : PathOperator(vars, secondary_vars, 1, true, false,
115  std::move(start_empty_path_class)),
116  arc_evaluator_(std::move(arc_evaluator)),
117  alternative_sets_(std::move(alternative_sets)),
118  to_alternative_set_(vars.size(), -1),
119  path_predecessor_(vars.size(), -1) {
120  for (int i = 0; i < alternative_sets_.size(); ++i) {
121  for (int j : alternative_sets_[i]) {
122  if (j < to_alternative_set_.size()) to_alternative_set_[j] = i;
123  }
124  }
125 }
126 
128  const int64_t before_chain = BaseNode(0);
129  if (to_alternative_set_[before_chain] != -1) return false;
130  int64_t next = Next(before_chain);
131  std::vector<int> alternatives;
132  while (!IsPathEnd(next) && to_alternative_set_[next] != -1 &&
133  alternative_sets_[to_alternative_set_[next]].size() > 1) {
134  alternatives.push_back(to_alternative_set_[next]);
135  next = Next(next);
136  }
137  if (alternatives.empty()) return false;
138  next = OldNext(before_chain);
139  bool swap_done = false;
140  UpdateShortestPath(before_chain, next, alternatives);
141  for (int64_t node : path_) {
142  if (node != next) {
144  swap_done = true;
145  }
146  next = OldNext(next);
147  }
148  return swap_done;
149 }
150 
151 void SwapActiveToShortestPathOperator::UpdateShortestPath(
152  int source, int sink, const std::vector<int>& alternative_chain) {
153  path_.clear();
154  if (alternative_chain.empty()) return;
155  // Initializing values at the first "layer" after the source (from source to
156  // all alternatives at rank 0).
157  const std::vector<int64_t>& first_alternative_set =
158  alternative_sets_[alternative_chain[0]];
159  std::vector<int64_t> prev_values;
160  prev_values.reserve(first_alternative_set.size());
161  for (int alternative_node : first_alternative_set) {
162  prev_values.push_back(arc_evaluator_(source, alternative_node));
163  }
164  // Updating values "layer" by "layer" (each one is fully connected to the
165  // previous one).
166  std::vector<int64_t> current_values;
167  for (int rank = 1; rank < alternative_chain.size(); ++rank) {
168  const std::vector<int64_t>& current_alternative_set =
169  alternative_sets_[alternative_chain[rank]];
170  current_values.clear();
171  current_values.reserve(current_alternative_set.size());
172  const std::vector<int64_t>& prev_alternative_set =
173  alternative_sets_[alternative_chain[rank - 1]];
174  for (int alternative_node : current_alternative_set) {
175  int64_t min_value = kint64max;
176  int predecessor = -1;
177  for (int prev_alternative = 0;
178  prev_alternative < prev_alternative_set.size(); ++prev_alternative) {
179  const int64_t new_value =
180  CapAdd(prev_values[prev_alternative],
181  arc_evaluator_(prev_alternative_set[prev_alternative],
182  alternative_node));
183  if (new_value <= min_value) {
184  min_value = new_value;
185  predecessor = prev_alternative_set[prev_alternative];
186  }
187  }
188  current_values.push_back(min_value);
189  path_predecessor_[alternative_node] = predecessor;
190  }
191  prev_values.swap(current_values);
192  }
193  // Get the predecessor in the shortest path to sink in the last layer.
194  int64_t min_value = kint64max;
195  int predecessor = -1;
196  const std::vector<int64_t>& last_alternative_set =
197  alternative_sets_[alternative_chain.back()];
198  for (int alternative = 0; alternative < last_alternative_set.size();
199  ++alternative) {
200  const int64_t new_value =
201  CapAdd(prev_values[alternative],
202  arc_evaluator_(last_alternative_set[alternative], sink));
203  if (new_value <= min_value) {
204  min_value = new_value;
205  predecessor = last_alternative_set[alternative];
206  }
207  }
208  if (predecessor == -1) return;
209  // Build the path from predecessors on the shortest path.
210  path_.resize(alternative_chain.size(), predecessor);
211  for (int rank = alternative_chain.size() - 2; rank >= 0; --rank) {
212  path_[rank] = path_predecessor_[path_[rank + 1]];
213  }
214 }
215 
217  const std::vector<IntVar*>& vars,
218  const std::vector<IntVar*>& secondary_vars,
219  std::function<int(int64_t)> start_empty_path_class,
220  const RoutingIndexPairs& pairs)
221  : PathOperator(vars, secondary_vars, 2, false, true,
222  std::move(start_empty_path_class)),
223  inactive_pair_(0),
224  inactive_pair_first_index_(0),
225  inactive_pair_second_index_(0),
226  pairs_(pairs) {}
227 
229  while (inactive_pair_ < pairs_.size()) {
230  if (PathOperator::MakeOneNeighbor()) return true;
231  ResetPosition();
232  if (inactive_pair_first_index_ < pairs_[inactive_pair_].first.size() - 1) {
233  ++inactive_pair_first_index_;
234  } else if (inactive_pair_second_index_ <
235  pairs_[inactive_pair_].second.size() - 1) {
236  inactive_pair_first_index_ = 0;
237  ++inactive_pair_second_index_;
238  } else {
239  inactive_pair_ = FindNextInactivePair(inactive_pair_ + 1);
240  inactive_pair_first_index_ = 0;
241  inactive_pair_second_index_ = 0;
242  }
243  }
244  return false;
245 }
246 
248  DCHECK_EQ(StartNode(0), StartNode(1));
249  // Inserting the second node of the pair before the first one which ensures
250  // that the only solutions where both nodes are next to each other have the
251  // first node before the second (the move is not symmetric and doing it this
252  // way ensures that a potential precedence constraint between the nodes of the
253  // pair is not violated).
254  return MakeActive(pairs_[inactive_pair_].second[inactive_pair_second_index_],
255  BaseNode(1)) &&
256  MakeActive(pairs_[inactive_pair_].first[inactive_pair_first_index_],
257  BaseNode(0));
258 }
259 
261  // Base node 1 must be after base node 0 if they are both on the same path.
262  if (base_index == 0 || StartNode(base_index) != StartNode(base_index - 1)) {
263  return StartNode(base_index);
264  } else {
265  return BaseNode(base_index - 1);
266  }
267 }
268 
269 void MakePairActiveOperator::OnNodeInitialization() {
270  inactive_pair_ = FindNextInactivePair(0);
271  inactive_pair_first_index_ = 0;
272  inactive_pair_second_index_ = 0;
273 }
274 
275 int MakePairActiveOperator::FindNextInactivePair(int pair_index) const {
276  for (int index = pair_index; index < pairs_.size(); ++index) {
277  if (!ContainsActiveNodes(pairs_[index].first) &&
278  !ContainsActiveNodes(pairs_[index].second)) {
279  return index;
280  }
281  }
282  return pairs_.size();
283 }
284 
285 bool MakePairActiveOperator::ContainsActiveNodes(
286  const std::vector<int64_t>& nodes) const {
287  for (int64_t node : nodes) {
288  if (!IsInactive(node)) return true;
289  }
290  return false;
291 }
292 
294  const std::vector<IntVar*>& vars,
295  const std::vector<IntVar*>& secondary_vars,
296  std::function<int(int64_t)> start_empty_path_class,
297  const RoutingIndexPairs& index_pairs)
298  : PathOperator(vars, secondary_vars, 1, true, false,
299  std::move(start_empty_path_class)) {
300  AddPairAlternativeSets(index_pairs);
301 }
302 
304  const int64_t base = BaseNode(0);
305  const int64_t first_index = Next(base);
306  const int64_t second_index = GetActiveAlternativeSibling(first_index);
307  if (second_index < 0) {
308  return false;
309  }
310  return MakeChainInactive(base, first_index) &&
311  MakeChainInactive(Prev(second_index), second_index);
312 }
313 
315  const std::vector<IntVar*>& vars,
316  const std::vector<IntVar*>& secondary_vars,
317  std::function<int(int64_t)> start_empty_path_class,
318  const RoutingIndexPairs& index_pairs)
319  : PathOperator(vars, secondary_vars, 3, true, false,
320  std::move(start_empty_path_class)) {
321  AddPairAlternativeSets(index_pairs);
322 }
323 
325  DCHECK_EQ(StartNode(1), StartNode(2));
326  const int64_t first_pair_node = BaseNode(kPairFirstNode);
327  if (IsPathStart(first_pair_node)) {
328  return false;
329  }
330  int64_t first_prev = Prev(first_pair_node);
331  const int second_pair_node = GetActiveAlternativeSibling(first_pair_node);
332  if (second_pair_node < 0 || IsPathEnd(second_pair_node) ||
333  IsPathStart(second_pair_node)) {
334  return false;
335  }
336  const int64_t second_prev = Prev(second_pair_node);
337 
338  const int64_t first_node_destination = BaseNode(kPairFirstNodeDestination);
339  if (first_node_destination == second_pair_node) {
340  // The second_pair_node -> first_pair_node link is forbidden.
341  return false;
342  }
343 
344  const int64_t second_node_destination = BaseNode(kPairSecondNodeDestination);
345  if (second_prev == first_pair_node && first_node_destination == first_prev &&
346  second_node_destination == first_prev) {
347  // If the current sequence is first_prev -> first_pair_node ->
348  // second_pair_node, and both 1st and 2nd are moved both to prev, the result
349  // of the move will be first_prev -> first_pair_node -> second_pair_node,
350  // which is no move.
351  return false;
352  }
353 
354  // Relocation is successful if both moves are feasible and at least one of the
355  // nodes moves.
356  if (second_pair_node == second_node_destination ||
357  first_pair_node == first_node_destination) {
358  return false;
359  }
360  const bool moved_second_pair_node =
361  MoveChain(second_prev, second_pair_node, second_node_destination);
362  // Explicitly calling Prev as second_pair_node might have been moved before
363  // first_pair_node.
364  const bool moved_first_pair_node =
365  MoveChain(Prev(first_pair_node), first_pair_node, first_node_destination);
366  // Swapping alternatives in.
367  SwapActiveAndInactive(second_pair_node,
368  BaseSiblingAlternativeNode(kPairFirstNode));
369  SwapActiveAndInactive(first_pair_node, BaseAlternativeNode(kPairFirstNode));
370  return moved_first_pair_node || moved_second_pair_node;
371 }
372 
374  // Destination node of the second node of a pair must be after the
375  // destination node of the first node of a pair.
376  if (base_index == kPairSecondNodeDestination) {
377  return BaseNode(kPairFirstNodeDestination);
378  } else {
379  return StartNode(base_index);
380  }
381 }
382 
384  const std::vector<IntVar*>& vars,
385  const std::vector<IntVar*>& secondary_vars,
386  std::function<int(int64_t)> start_empty_path_class,
387  const RoutingIndexPairs& index_pairs)
388  : PathOperator(vars, secondary_vars, 2, true, false,
389  std::move(start_empty_path_class)) {
390  AddPairAlternativeSets(index_pairs);
391 }
392 
394  const int64_t prev1 = BaseNode(0);
395  const int64_t node1 = Next(prev1);
396  if (IsPathEnd(node1)) return false;
397  const int64_t sibling1 = GetActiveAlternativeSibling(node1);
398  if (sibling1 == -1) return false;
399  const int64_t node2 = BaseNode(1);
400  // Skip redundant cases.
401  if (node2 == node1 || node2 == sibling1) return false;
402  const bool ok = MoveChain(prev1, node1, node2);
403  return MoveChain(Prev(sibling1), sibling1, node1) || ok;
404 }
405 
407  const std::vector<IntVar*>& vars,
408  const std::vector<IntVar*>& secondary_vars,
409  std::function<int(int64_t)> start_empty_path_class,
410  const RoutingIndexPairs& index_pairs,
411  std::function<bool(int64_t)> force_lifo)
412  : PathOperator(vars, secondary_vars, 2, true, false,
413  std::move(start_empty_path_class)),
414  force_lifo_(std::move(force_lifo)) {
415  AddPairAlternativeSets(index_pairs);
416 }
417 
419  const int64_t prev1 = BaseNode(0);
420  const int64_t node1 = Next(prev1);
421  if (IsPathEnd(node1)) return false;
422  const int64_t sibling1 = GetActiveAlternativeSibling(node1);
423  if (sibling1 == -1) return false;
424  const int64_t node2 = BaseNode(1);
425  if (node2 == sibling1) return false;
426  const bool path2_is_lifo =
427  (force_lifo_ != nullptr && force_lifo_(StartNode(1)));
428  // Note: MoveChain will return false if it is a no-op (moving the chain to its
429  // current position). However we want to accept the move if at least node1 or
430  // sibling1 gets moved to a new position. Therefore we want to be sure both
431  // MoveChains are called and at least one succeeds.
432 
433  // Special case handling relocating the first node of a pair "before" the
434  // first node of another pair. Limiting this to relocating after the start of
435  // the path as other moves will be mostly equivalent to relocating "after".
436  // TODO(user): extend to relocating before the start of sub-tours (when all
437  // pairs have been matched).
438  if (IsPathStart(node2)) {
439  const bool ok = MoveChain(prev1, node1, node2);
440  const int64_t sibling2 = GetActiveAlternativeSibling(Next(node1));
441  if (sibling2 == -1) {
442  // Not inserting before a pair node: insert sibling1 after node1.
443  return MoveChain(Prev(sibling1), sibling1, node1) || ok;
444  } else {
445  // Depending on the lifo status of the path, insert sibling1 before or
446  // after sibling2 since node1 is being inserted before next2.
447  if (!path2_is_lifo) {
448  if (Prev(sibling2) == sibling1) return ok;
449  return MoveChain(Prev(sibling1), sibling1, Prev(sibling2)) || ok;
450  } else {
451  return MoveChain(Prev(sibling1), sibling1, sibling2) || ok;
452  }
453  }
454  }
455  // Relocating the first node of a pair "after" the first node of another pair.
456  const int64_t sibling2 = GetActiveAlternativeSibling(node2);
457  if (sibling2 == -1) return false;
458  const bool ok = MoveChain(prev1, node1, node2);
459  if (!path2_is_lifo) {
460  return MoveChain(Prev(sibling1), sibling1, sibling2) || ok;
461  } else {
462  if (Prev(sibling2) == sibling1) return ok;
463  return MoveChain(Prev(sibling1), sibling1, Prev(sibling2)) || ok;
464  }
465 }
466 
468  const std::vector<IntVar*>& vars,
469  const std::vector<IntVar*>& secondary_vars,
470  std::function<int(int64_t)> start_empty_path_class,
471  const RoutingIndexPairs& index_pairs)
472  : PathOperator(vars, secondary_vars, 2, true, true,
473  std::move(start_empty_path_class)) {
474  AddPairAlternativeSets(index_pairs);
475 }
476 
478  const int64_t node1 = BaseNode(0);
479  int64_t prev1, sibling1, sibling_prev1 = -1;
480  if (!GetPreviousAndSibling(node1, &prev1, &sibling1, &sibling_prev1)) {
481  return false;
482  }
483  const int64_t node2 = BaseNode(1);
484  int64_t prev2, sibling2, sibling_prev2 = -1;
485  if (!GetPreviousAndSibling(node2, &prev2, &sibling2, &sibling_prev2)) {
486  return false;
487  }
488  bool status = true;
489  // Exchanging node1 and node2.
490  if (node1 == prev2) {
491  status = MoveChain(prev2, node2, prev1);
492  if (sibling_prev1 == node2) sibling_prev1 = node1;
493  if (sibling_prev2 == node2) sibling_prev2 = node1;
494  } else if (node2 == prev1) {
495  status = MoveChain(prev1, node1, prev2);
496  if (sibling_prev1 == node1) sibling_prev1 = node2;
497  if (sibling_prev2 == node1) sibling_prev2 = node2;
498  } else {
499  status = MoveChain(prev1, node1, node2) && MoveChain(prev2, node2, prev1);
500  if (sibling_prev1 == node1) {
501  sibling_prev1 = node2;
502  } else if (sibling_prev1 == node2) {
503  sibling_prev1 = node1;
504  }
505  if (sibling_prev2 == node1) {
506  sibling_prev2 = node2;
507  } else if (sibling_prev2 == node2) {
508  sibling_prev2 = node1;
509  }
510  }
511  if (!status) return false;
512  // Exchanging sibling1 and sibling2.
513  if (sibling1 == sibling_prev2) {
514  status = MoveChain(sibling_prev2, sibling2, sibling_prev1);
515  } else if (sibling2 == sibling_prev1) {
516  status = MoveChain(sibling_prev1, sibling1, sibling_prev2);
517  } else {
518  status = MoveChain(sibling_prev1, sibling1, sibling2) &&
519  MoveChain(sibling_prev2, sibling2, sibling_prev1);
520  }
521  // Swapping alternatives in.
526  return status;
527 }
528 
529 bool PairExchangeOperator::GetPreviousAndSibling(
530  int64_t node, int64_t* previous, int64_t* sibling,
531  int64_t* sibling_previous) const {
532  if (IsPathStart(node)) return false;
533  *previous = Prev(node);
534  *sibling = GetActiveAlternativeSibling(node);
535  *sibling_previous = *sibling >= 0 ? Prev(*sibling) : -1;
536  return *sibling_previous >= 0;
537 }
538 
540  const std::vector<IntVar*>& vars,
541  const std::vector<IntVar*>& secondary_vars,
542  std::function<int(int64_t)> start_empty_path_class,
543  const RoutingIndexPairs& index_pairs)
544  : PathOperator(vars, secondary_vars, 6, true, false,
545  std::move(start_empty_path_class)) {
546  AddPairAlternativeSets(index_pairs);
547 }
548 
550  DCHECK_EQ(StartNode(kSecondPairFirstNodeDestination),
551  StartNode(kSecondPairSecondNodeDestination));
552  DCHECK_EQ(StartNode(kSecondPairFirstNode),
553  StartNode(kFirstPairFirstNodeDestination));
554  DCHECK_EQ(StartNode(kSecondPairFirstNode),
555  StartNode(kFirstPairSecondNodeDestination));
556 
557  if (StartNode(kFirstPairFirstNode) == StartNode(kSecondPairFirstNode)) {
558  SetNextBaseToIncrement(kSecondPairFirstNode);
559  return false;
560  }
561  // Through this method, <base>[X][Y] represent the <base> variable for the
562  // node Y of pair X. <base> is in node, prev, dest.
563  int64_t nodes[2][2];
564  int64_t prev[2][2];
565  int64_t dest[2][2];
566  nodes[0][0] = BaseNode(kFirstPairFirstNode);
567  nodes[1][0] = BaseNode(kSecondPairFirstNode);
568  if (nodes[1][0] <= nodes[0][0]) {
569  // Exchange is symmetric.
570  SetNextBaseToIncrement(kSecondPairFirstNode);
571  return false;
572  }
573  if (!GetPreviousAndSibling(nodes[0][0], &prev[0][0], &nodes[0][1],
574  &prev[0][1])) {
575  SetNextBaseToIncrement(kFirstPairFirstNode);
576  return false;
577  }
578  if (!GetPreviousAndSibling(nodes[1][0], &prev[1][0], &nodes[1][1],
579  &prev[1][1])) {
580  SetNextBaseToIncrement(kSecondPairFirstNode);
581  return false;
582  }
583 
584  if (!LoadAndCheckDest(0, 0, kFirstPairFirstNodeDestination, nodes, dest)) {
585  SetNextBaseToIncrement(kFirstPairFirstNodeDestination);
586  return false;
587  }
588  if (!LoadAndCheckDest(0, 1, kFirstPairSecondNodeDestination, nodes, dest)) {
589  SetNextBaseToIncrement(kFirstPairSecondNodeDestination);
590  return false;
591  }
592  if (StartNode(kSecondPairFirstNodeDestination) !=
593  StartNode(kFirstPairFirstNode) ||
594  !LoadAndCheckDest(1, 0, kSecondPairFirstNodeDestination, nodes, dest)) {
595  SetNextBaseToIncrement(kSecondPairFirstNodeDestination);
596  return false;
597  }
598  if (!LoadAndCheckDest(1, 1, kSecondPairSecondNodeDestination, nodes, dest)) {
599  SetNextBaseToIncrement(kSecondPairSecondNodeDestination);
600  return false;
601  }
602 
603  if (!MoveNode(0, 1, nodes, dest, prev)) {
604  SetNextBaseToIncrement(kFirstPairSecondNodeDestination);
605  return false;
606  }
607  if (!MoveNode(0, 0, nodes, dest, prev)) {
608  SetNextBaseToIncrement(kFirstPairSecondNodeDestination);
609  return false;
610  }
611  if (!MoveNode(1, 1, nodes, dest, prev)) {
612  return false;
613  }
614  if (!MoveNode(1, 0, nodes, dest, prev)) {
615  return false;
616  }
617  return true;
618 }
619 
620 bool PairExchangeRelocateOperator::MoveNode(int pair, int node,
621  int64_t nodes[2][2],
622  int64_t dest[2][2],
623  int64_t prev[2][2]) {
624  if (!MoveChain(prev[pair][node], nodes[pair][node], dest[pair][node])) {
625  return false;
626  }
627  // Update the other pair if needed.
628  if (prev[1 - pair][0] == dest[pair][node]) {
629  prev[1 - pair][0] = nodes[pair][node];
630  }
631  if (prev[1 - pair][1] == dest[pair][node]) {
632  prev[1 - pair][1] = nodes[pair][node];
633  }
634  return true;
635 }
636 
637 bool PairExchangeRelocateOperator::LoadAndCheckDest(int pair, int node,
638  int64_t base_node,
639  int64_t nodes[2][2],
640  int64_t dest[2][2]) const {
641  dest[pair][node] = BaseNode(base_node);
642  // A destination cannot be a node that will be moved.
643  return !(nodes[0][0] == dest[pair][node] || nodes[0][1] == dest[pair][node] ||
644  nodes[1][0] == dest[pair][node] || nodes[1][1] == dest[pair][node]);
645 }
646 
648  int64_t base_index) {
649  // Ensuring the destination of the first pair is on the route of the second.
650  // pair.
651  // Ensuring that destination of both nodes of a pair are on the same route.
652  return base_index == kFirstPairFirstNodeDestination ||
653  base_index == kFirstPairSecondNodeDestination ||
654  base_index == kSecondPairSecondNodeDestination;
655 }
656 
658  int base_index) {
659  if (base_index == kFirstPairSecondNodeDestination ||
660  base_index == kSecondPairSecondNodeDestination) {
661  return BaseNode(base_index - 1);
662  } else {
663  return StartNode(base_index);
664  }
665 }
666 
667 bool PairExchangeRelocateOperator::GetPreviousAndSibling(
668  int64_t node, int64_t* previous, int64_t* sibling,
669  int64_t* sibling_previous) const {
670  if (IsPathStart(node)) return false;
671  *previous = Prev(node);
672  *sibling = GetActiveAlternativeSibling(node);
673  *sibling_previous = *sibling >= 0 ? Prev(*sibling) : -1;
674  return *sibling_previous >= 0;
675 }
676 
678  const std::vector<IntVar*>& vars, const std::vector<IntVar*>& path_vars,
679  std::function<int(int64_t)> start_empty_path_class,
680  const RoutingIndexPairs& index_pairs)
682  index_pairs_(index_pairs),
683  pair_index_(0),
684  first_index_(0),
685  second_index_(0),
686  number_of_nexts_(vars.size()),
687  ignore_path_vars_(path_vars.empty()) {
688  if (!ignore_path_vars_) {
689  AddVars(path_vars);
690  }
691 }
692 
694  Assignment* deltadelta) {
695  const int64_t kNoPath = -1;
696  CHECK(delta != nullptr);
697  while (true) {
698  RevertChanges(true);
699 
700  if (pair_index_ < index_pairs_.size()) {
701  const int64_t path =
702  ignore_path_vars_ ? 0LL : Value(first_active_ + number_of_nexts_);
703  const int64_t prev_first = prevs_[first_active_];
704  const int64_t next_first = Value(first_active_);
705  // Making current active "pickup" unperformed.
706  SetNext(first_active_, first_active_, kNoPath);
707  // Inserting "pickup" alternative at the same position.
708  const int64_t insert_first =
709  index_pairs_[pair_index_].first[first_index_];
710  SetNext(prev_first, insert_first, path);
711  SetNext(insert_first, next_first, path);
712  int64_t prev_second = prevs_[second_active_];
713  if (prev_second == first_active_) {
714  prev_second = insert_first;
715  }
716  DCHECK_EQ(path, ignore_path_vars_
717  ? int64_t{0}
718  : Value(second_active_ + number_of_nexts_));
719  const int64_t next_second = Value(second_active_);
720  // Making current active "delivery" unperformed.
721  SetNext(second_active_, second_active_, kNoPath);
722  // Inserting "delivery" alternative at the same position.
723  const int64_t insert_second =
724  index_pairs_[pair_index_].second[second_index_];
725  SetNext(prev_second, insert_second, path);
726  SetNext(insert_second, next_second, path);
727  // Move to next "pickup/delivery" alternative.
728  ++second_index_;
729  if (second_index_ >= index_pairs_[pair_index_].second.size()) {
730  second_index_ = 0;
731  ++first_index_;
732  if (first_index_ >= index_pairs_[pair_index_].first.size()) {
733  first_index_ = 0;
734  ++pair_index_;
735  UpdateActiveNodes();
736  }
737  }
738  } else {
739  return false;
740  }
741 
742  if (ApplyChanges(delta, deltadelta)) return true;
743  }
744  return false;
745 }
746 
748  prevs_.resize(number_of_nexts_, -1);
749  for (int index = 0; index < number_of_nexts_; ++index) {
750  const int64_t next = Value(index);
751  if (next >= prevs_.size()) prevs_.resize(next + 1, -1);
752  prevs_[next] = index;
753  }
754  pair_index_ = 0;
755  first_index_ = 0;
756  second_index_ = 0;
757  first_active_ = -1;
758  second_active_ = -1;
759  while (true) {
760  if (!UpdateActiveNodes()) break;
761  if (first_active_ != -1 && second_active_ != -1) {
762  break;
763  }
764  ++pair_index_;
765  }
766 }
767 
768 bool SwapIndexPairOperator::UpdateActiveNodes() {
769  if (pair_index_ < index_pairs_.size()) {
770  for (const int64_t first : index_pairs_[pair_index_].first) {
771  if (Value(first) != first) {
772  first_active_ = first;
773  break;
774  }
775  }
776  for (const int64_t second : index_pairs_[pair_index_].second) {
777  if (Value(second) != second) {
778  second_active_ = second;
779  break;
780  }
781  }
782  return true;
783  }
784  return false;
785 }
786 
788  const std::vector<IntVar*>& vars,
789  const std::vector<IntVar*>& secondary_vars,
790  std::function<int(int64_t)> start_empty_path_class,
791  const RoutingIndexPairs& index_pairs)
792  : PathOperator(vars, secondary_vars, 1, true, false,
793  std::move(start_empty_path_class)),
794  inactive_node_(0) {
795  AddPairAlternativeSets(index_pairs);
796 }
797 
799  Assignment* deltadelta) {
800  while (inactive_node_ < Size()) {
801  if (!IsInactive(inactive_node_) ||
802  !PathOperator::MakeNextNeighbor(delta, deltadelta)) {
803  ResetPosition();
804  ++inactive_node_;
805  } else {
806  return true;
807  }
808  }
809  return false;
810 }
811 
813  const int64_t base = BaseNode(0);
814  const int64_t next = Next(base);
815  const int64_t other = GetActiveAlternativeSibling(next);
816  if (other != -1) {
817  return MakeChainInactive(Prev(other), other) &&
818  MakeChainInactive(base, next) && MakeActive(inactive_node_, base);
819  }
820  return false;
821 }
822 
823 void IndexPairSwapActiveOperator::OnNodeInitialization() {
825  for (int i = 0; i < Size(); ++i) {
826  if (IsInactive(i)) {
827  inactive_node_ = i;
828  return;
829  }
830  }
831  inactive_node_ = Size();
832 }
833 
834 // FilteredHeuristicLocalSearchOperator
835 
837  std::unique_ptr<RoutingFilteredHeuristic> heuristic,
838  bool keep_inverse_values)
839  : IntVarLocalSearchOperator(heuristic->model()->Nexts(),
840  keep_inverse_values),
841  model_(heuristic->model()),
842  removed_nodes_(model_->Size()),
843  heuristic_(std::move(heuristic)),
844  consider_vehicle_vars_(!model_->CostsAreHomogeneousAcrossVehicles()) {
845  if (consider_vehicle_vars_) {
847  }
848 }
849 
850 bool FilteredHeuristicLocalSearchOperator::MakeOneNeighbor() {
851  while (IncrementPosition()) {
852  if (model_->CheckLimit()) {
853  // NOTE: Even though the limit is checked in the BuildSolutionFromRoutes()
854  // method of the heuristics, we still check it here to avoid calling
855  // IncrementPosition() and building a solution for every possible position
856  // if the time limit is reached.
857  return false;
858  }
859  // NOTE: No need to call RevertChanges() here as MakeChangeAndInsertNodes()
860  // will always return true if any change was made.
861  if (MakeChangesAndInsertNodes()) {
862  return true;
863  }
864  }
865  return false;
866 }
867 
868 bool FilteredHeuristicLocalSearchOperator::MakeChangesAndInsertNodes() {
870 
871  const std::function<int64_t(int64_t)> next_accessor =
873  if (next_accessor == nullptr) {
874  return false;
875  }
876  const Assignment* const result_assignment =
877  heuristic_->BuildSolutionFromRoutes(next_accessor);
878 
879  if (result_assignment == nullptr) {
880  return false;
881  }
882 
883  bool has_change = false;
884  const std::vector<IntVarElement>& elements =
885  result_assignment->IntVarContainer().elements();
886  for (int vehicle = 0; vehicle < model_->vehicles(); vehicle++) {
887  int64_t node_index = model_->Start(vehicle);
888  while (!model_->IsEnd(node_index)) {
889  // NOTE: When building the solution in the heuristic, Next vars are added
890  // to the assignment at the position corresponding to their index.
891  const IntVarElement& node_element = elements[node_index];
892  DCHECK_EQ(node_element.Var(), model_->NextVar(node_index));
893 
894  const int64_t new_node_value = node_element.Value();
895  DCHECK_NE(new_node_value, node_index);
896 
897  const int64_t vehicle_var_index = VehicleVarIndex(node_index);
898  if (OldValue(node_index) != new_node_value ||
899  (consider_vehicle_vars_ && OldValue(vehicle_var_index) != vehicle)) {
900  has_change = true;
901  SetValue(node_index, new_node_value);
902  if (consider_vehicle_vars_) {
903  SetValue(vehicle_var_index, vehicle);
904  }
905  }
906  node_index = new_node_value;
907  }
908  }
909  // Check for newly unperformed nodes among the ones removed for insertion by
910  // the heuristic.
911  for (int64_t node : removed_nodes_.PositionsSetAtLeastOnce()) {
912  const IntVarElement& node_element = elements[node];
913  DCHECK_EQ(node_element.Var(), model_->NextVar(node));
914  if (node_element.Value() == node) {
915  DCHECK_NE(OldValue(node), node);
916  has_change = true;
917  SetValue(node, node);
918  if (consider_vehicle_vars_) {
919  const int64_t vehicle_var_index = VehicleVarIndex(node);
920  DCHECK_NE(OldValue(vehicle_var_index), -1);
921  SetValue(vehicle_var_index, -1);
922  }
923  }
924  }
925  return has_change;
926 }
927 
928 // FilteredHeuristicPathLNSOperator
929 
931  std::unique_ptr<RoutingFilteredHeuristic> heuristic)
932  : FilteredHeuristicLocalSearchOperator(std::move(heuristic)),
933  current_route_(0),
934  last_route_(0),
935  just_started_(false) {}
936 
937 void FilteredHeuristicPathLNSOperator::OnStart() {
938  // NOTE: We set last_route_ to current_route_ here to make sure all routes
939  // are scanned in IncrementCurrentRouteToNextNonEmpty().
940  last_route_ = current_route_;
941  if (CurrentRouteIsEmpty()) {
942  IncrementCurrentRouteToNextNonEmpty();
943  }
944  just_started_ = true;
945 }
946 
947 bool FilteredHeuristicPathLNSOperator::IncrementPosition() {
948  if (just_started_) {
949  just_started_ = false;
950  return !CurrentRouteIsEmpty();
951  }
952  IncrementCurrentRouteToNextNonEmpty();
953  return current_route_ != last_route_;
954 }
955 
956 bool FilteredHeuristicPathLNSOperator::CurrentRouteIsEmpty() const {
957  return model_->IsEnd(OldValue(model_->Start(current_route_)));
958 }
959 
960 void FilteredHeuristicPathLNSOperator::IncrementCurrentRouteToNextNonEmpty() {
961  const int num_routes = model_->vehicles();
962  do {
963  ++current_route_ %= num_routes;
964  if (current_route_ == last_route_) {
965  // All routes have been scanned.
966  return;
967  }
968  } while (CurrentRouteIsEmpty());
969 }
970 
971 std::function<int64_t(int64_t)>
972 FilteredHeuristicPathLNSOperator::SetupNextAccessorForNeighbor() {
973  const int64_t start_node = model_->Start(current_route_);
974  const int64_t end_node = model_->End(current_route_);
975 
976  int64_t node = Value(start_node);
977  while (node != end_node) {
978  removed_nodes_.Set(node);
979  node = Value(node);
980  }
981 
982  return [this, start_node, end_node](int64_t node) {
983  if (node == start_node) return end_node;
984  return Value(node);
985  };
986 }
987 
988 // RelocatePathAndHeuristicInsertUnperformedOperator
989 
992  std::unique_ptr<RoutingFilteredHeuristic> heuristic)
993  : FilteredHeuristicLocalSearchOperator(std::move(heuristic)),
994  route_to_relocate_index_(0),
995  empty_route_index_(0),
996  just_started_(false) {}
997 
998 void RelocatePathAndHeuristicInsertUnperformedOperator::OnStart() {
999  has_unperformed_nodes_ = false;
1000  last_node_on_route_.resize(model_->vehicles());
1001  routes_to_relocate_.clear();
1002  empty_routes_.clear();
1003  std::vector<bool> empty_vehicle_of_vehicle_class_added(
1004  model_->GetVehicleClassesCount(), false);
1005  for (int64_t node = 0; node < model_->Size(); node++) {
1006  const int64_t next = OldValue(node);
1007  if (next == node) {
1008  has_unperformed_nodes_ = true;
1009  continue;
1010  }
1011  if (model_->IsEnd(next)) {
1012  last_node_on_route_[model_->VehicleIndex(next)] = node;
1013  }
1014  }
1015 
1016  for (int vehicle = 0; vehicle < model_->vehicles(); vehicle++) {
1017  const int64_t next = OldValue(model_->Start(vehicle));
1018  if (!model_->IsEnd(next)) {
1019  routes_to_relocate_.push_back(vehicle);
1020  continue;
1021  }
1022  const int vehicle_class =
1023  model_->GetVehicleClassIndexOfVehicle(vehicle).value();
1024  if (!empty_vehicle_of_vehicle_class_added[vehicle_class]) {
1025  empty_routes_.push_back(vehicle);
1026  empty_vehicle_of_vehicle_class_added[vehicle_class] = true;
1027  }
1028  }
1029 
1030  if (empty_route_index_ >= empty_routes_.size()) {
1031  empty_route_index_ = 0;
1032  }
1033  if (route_to_relocate_index_ >= routes_to_relocate_.size()) {
1034  route_to_relocate_index_ = 0;
1035  }
1036  last_empty_route_index_ = empty_route_index_;
1037  last_route_to_relocate_index_ = route_to_relocate_index_;
1038 
1039  just_started_ = true;
1040 }
1041 
1042 bool RelocatePathAndHeuristicInsertUnperformedOperator::IncrementPosition() {
1043  if (!has_unperformed_nodes_ || empty_routes_.empty() ||
1044  routes_to_relocate_.empty()) {
1045  return false;
1046  }
1047  if (just_started_) {
1048  just_started_ = false;
1049  return true;
1050  }
1051  return IncrementRoutes();
1052 }
1053 
1054 bool RelocatePathAndHeuristicInsertUnperformedOperator::IncrementRoutes() {
1055  ++empty_route_index_ %= empty_routes_.size();
1056  if (empty_route_index_ != last_empty_route_index_) {
1057  return true;
1058  }
1059  ++route_to_relocate_index_ %= routes_to_relocate_.size();
1060  return route_to_relocate_index_ != last_route_to_relocate_index_;
1061 }
1062 
1063 std::function<int64_t(int64_t)>
1064 RelocatePathAndHeuristicInsertUnperformedOperator::
1065  SetupNextAccessorForNeighbor() {
1066  const int empty_route = empty_routes_[empty_route_index_];
1067  const int relocated_route = routes_to_relocate_[route_to_relocate_index_];
1068  if (model_->GetVehicleClassIndexOfVehicle(empty_route) ==
1069  model_->GetVehicleClassIndexOfVehicle(relocated_route)) {
1070  // Don't try to relocate the route to an empty vehicle of the same class.
1071  return nullptr;
1072  }
1073 
1074  const int64_t empty_start_node = model_->Start(empty_route);
1075  const int64_t empty_end_node = model_->End(empty_route);
1076 
1077  const int64_t relocated_route_start = model_->Start(relocated_route);
1078  const int64_t first_relocated_node = OldValue(relocated_route_start);
1079  const int64_t last_relocated_node = last_node_on_route_[relocated_route];
1080  const int64_t relocated_route_end = model_->End(relocated_route);
1081 
1082  return [this, empty_start_node, empty_end_node, first_relocated_node,
1083  last_relocated_node, relocated_route_start,
1084  relocated_route_end](int64_t node) {
1085  if (node == relocated_route_start) return relocated_route_end;
1086  if (node == empty_start_node) return first_relocated_node;
1087  if (node == last_relocated_node) return empty_end_node;
1088  return Value(node);
1089  };
1090 }
1091 
1092 // FilteredHeuristicCloseNodesLNSOperator
1093 
1095  std::unique_ptr<RoutingFilteredHeuristic> heuristic, int num_close_nodes)
1096  : FilteredHeuristicLocalSearchOperator(std::move(heuristic),
1097  /*keep_inverse_values*/ true),
1098  pickup_delivery_pairs_(model_->GetPickupAndDeliveryPairs()),
1099  current_node_(0),
1100  last_node_(0),
1101  just_started_(false),
1102  initialized_(false),
1103  close_nodes_(model_->Size()),
1104  num_close_nodes_(num_close_nodes),
1105  new_nexts_(model_->Size()),
1106  changed_nexts_(model_->Size()),
1107  new_prevs_(model_->Size()),
1108  changed_prevs_(model_->Size()) {}
1109 
1110 void FilteredHeuristicCloseNodesLNSOperator::Initialize() {
1111  if (initialized_) return;
1112  initialized_ = true;
1113  const int64_t size = model_->Size();
1114  const int64_t max_num_neighbors =
1115  std::max<int64_t>(0, size - 1 - model_->vehicles());
1116  const int64_t num_closest_neighbors =
1117  std::min<int64_t>(num_close_nodes_, max_num_neighbors);
1118  DCHECK_GE(num_closest_neighbors, 0);
1119 
1120  if (num_closest_neighbors == 0) return;
1121 
1122  const int64_t num_cost_classes = model_->GetCostClassesCount();
1123 
1124  for (int64_t node = 0; node < size; node++) {
1125  if (model_->IsStart(node) || model_->IsEnd(node)) continue;
1126 
1127  std::vector<std::pair</*cost*/ double, /*node*/ int64_t>>
1128  costed_after_nodes;
1129  costed_after_nodes.reserve(size);
1130  for (int64_t after_node = 0; after_node < size; after_node++) {
1131  if (model_->IsStart(after_node) || model_->IsEnd(after_node) ||
1132  after_node == node) {
1133  continue;
1134  }
1135  double total_cost = 0.0;
1136  // NOTE: We don't consider the 'always-zero' cost class when searching for
1137  // closest neighbors.
1138  for (int cost_class = 1; cost_class < num_cost_classes; cost_class++) {
1139  total_cost += model_->GetArcCostForClass(node, after_node, cost_class);
1140  }
1141  costed_after_nodes.emplace_back(total_cost, after_node);
1142  }
1143 
1144  std::nth_element(costed_after_nodes.begin(),
1145  costed_after_nodes.begin() + num_closest_neighbors - 1,
1146  costed_after_nodes.end());
1147  std::vector<int64_t>& neighbors = close_nodes_[node];
1148  neighbors.reserve(num_closest_neighbors);
1149  for (int index = 0; index < num_closest_neighbors; index++) {
1150  neighbors.push_back(costed_after_nodes[index].second);
1151  }
1152  }
1153 }
1154 
1155 void FilteredHeuristicCloseNodesLNSOperator::OnStart() {
1156  Initialize();
1157  last_node_ = current_node_;
1158  just_started_ = true;
1159 }
1160 
1161 bool FilteredHeuristicCloseNodesLNSOperator::IncrementPosition() {
1162  DCHECK(initialized_);
1163  if (just_started_) {
1164  just_started_ = false;
1165  return true;
1166  }
1167  ++current_node_ %= model_->Size();
1168  return current_node_ != last_node_;
1169 }
1170 
1171 void FilteredHeuristicCloseNodesLNSOperator::RemoveNode(int64_t node) {
1172  DCHECK(!model_->IsEnd(node) && !model_->IsStart(node));
1173  DCHECK_NE(Value(node), node);
1174  DCHECK(IsActive(node));
1175 
1176  removed_nodes_.Set(node);
1177  const int64_t prev = Prev(node);
1178  const int64_t next = Next(node);
1179  changed_nexts_.Set(prev);
1180  new_nexts_[prev] = next;
1181  if (next < model_->Size()) {
1182  changed_prevs_.Set(next);
1183  new_prevs_[next] = prev;
1184  }
1185 }
1186 
1187 void FilteredHeuristicCloseNodesLNSOperator::RemoveNodeAndActiveSibling(
1188  int64_t node) {
1189  if (!IsActive(node)) return;
1190  RemoveNode(node);
1191 
1192  for (int64_t sibling_node : GetActiveSiblings(node)) {
1193  if (!model_->IsStart(sibling_node) && !model_->IsEnd(sibling_node)) {
1194  RemoveNode(sibling_node);
1195  }
1196  }
1197 }
1198 
1199 std::vector<int64_t> FilteredHeuristicCloseNodesLNSOperator::GetActiveSiblings(
1200  int64_t node) const {
1201  // NOTE: In most use-cases, where each node is a pickup or delivery in a
1202  // single index pair, this function is in O(k) where k is the number of
1203  // alternative deliveries or pickups for this index pair.
1204  std::vector<int64_t> active_siblings;
1205  for (std::pair<int64_t, int64_t> index_pair :
1206  model_->GetPickupIndexPairs(node)) {
1207  for (int64_t sibling_delivery :
1208  pickup_delivery_pairs_[index_pair.first].second) {
1209  if (IsActive(sibling_delivery)) {
1210  active_siblings.push_back(sibling_delivery);
1211  break;
1212  }
1213  }
1214  }
1215  for (std::pair<int64_t, int64_t> index_pair :
1216  model_->GetDeliveryIndexPairs(node)) {
1217  for (int64_t sibling_pickup :
1218  pickup_delivery_pairs_[index_pair.first].first) {
1219  if (IsActive(sibling_pickup)) {
1220  active_siblings.push_back(sibling_pickup);
1221  break;
1222  }
1223  }
1224  }
1225  return active_siblings;
1226 }
1227 
1228 std::function<int64_t(int64_t)>
1229 FilteredHeuristicCloseNodesLNSOperator::SetupNextAccessorForNeighbor() {
1230  DCHECK(initialized_);
1231  if (model_->IsStart(current_node_)) {
1232  return nullptr;
1233  }
1234  DCHECK(!model_->IsEnd(current_node_));
1235 
1236  changed_nexts_.SparseClearAll();
1237  changed_prevs_.SparseClearAll();
1238 
1239  RemoveNodeAndActiveSibling(current_node_);
1240 
1241  for (int64_t neighbor : close_nodes_[current_node_]) {
1242  RemoveNodeAndActiveSibling(neighbor);
1243  }
1244 
1245  return [this](int64_t node) { return Next(node); };
1246 }
1247 
1248 // FilteredHeuristicExpensiveChainLNSOperator
1249 
1252  std::unique_ptr<RoutingFilteredHeuristic> heuristic,
1253  int num_arcs_to_consider,
1254  std::function<int64_t(int64_t, int64_t, int64_t)>
1255  arc_cost_for_route_start)
1256  : FilteredHeuristicLocalSearchOperator(std::move(heuristic)),
1257  current_route_(0),
1258  last_route_(0),
1259  num_arcs_to_consider_(num_arcs_to_consider),
1260  current_expensive_arc_indices_({-1, -1}),
1261  arc_cost_for_route_start_(std::move(arc_cost_for_route_start)),
1262  just_started_(false) {
1263  DCHECK_GE(num_arcs_to_consider_, 2);
1264 }
1265 
1266 void FilteredHeuristicExpensiveChainLNSOperator::OnStart() {
1267  last_route_ = current_route_;
1268  just_started_ = true;
1269 }
1270 
1271 bool FilteredHeuristicExpensiveChainLNSOperator::IncrementPosition() {
1272  if (just_started_) {
1273  just_started_ = false;
1274  return FindMostExpensiveChainsOnRemainingRoutes();
1275  }
1276 
1277  if (IncrementCurrentArcIndices()) return true;
1278 
1279  return IncrementRoute() && FindMostExpensiveChainsOnRemainingRoutes();
1280 }
1281 
1282 std::function<int64_t(int64_t)>
1283 FilteredHeuristicExpensiveChainLNSOperator::SetupNextAccessorForNeighbor() {
1284  const int first_arc_index = current_expensive_arc_indices_.first;
1285  const int second_arc_index = current_expensive_arc_indices_.second;
1286  DCHECK_LE(0, first_arc_index);
1287  DCHECK_LT(first_arc_index, second_arc_index);
1288  DCHECK_LT(second_arc_index, most_expensive_arc_starts_and_ranks_.size());
1289 
1290  const std::pair<int, int>& first_start_and_rank =
1291  most_expensive_arc_starts_and_ranks_[first_arc_index];
1292  const std::pair<int, int>& second_start_and_rank =
1293  most_expensive_arc_starts_and_ranks_[second_arc_index];
1294  int64_t before_chain, after_chain;
1295  if (first_start_and_rank.second < second_start_and_rank.second) {
1296  before_chain = first_start_and_rank.first;
1297  after_chain = OldValue(second_start_and_rank.first);
1298  } else {
1299  before_chain = second_start_and_rank.first;
1300  after_chain = OldValue(first_start_and_rank.first);
1301  }
1302 
1303  int node = Value(before_chain);
1304  while (node != after_chain) {
1305  removed_nodes_.Set(node);
1306  node = Value(node);
1307  }
1308 
1309  return [this, before_chain, after_chain](int64_t node) {
1310  if (node == before_chain) return after_chain;
1311  return OldValue(node);
1312  };
1313 }
1314 
1315 bool FilteredHeuristicExpensiveChainLNSOperator::IncrementRoute() {
1316  ++current_route_ %= model_->vehicles();
1317  return current_route_ != last_route_;
1318 }
1319 
1320 bool FilteredHeuristicExpensiveChainLNSOperator::IncrementCurrentArcIndices() {
1321  int& second_index = current_expensive_arc_indices_.second;
1322  if (++second_index < most_expensive_arc_starts_and_ranks_.size()) {
1323  return true;
1324  }
1325  int& first_index = current_expensive_arc_indices_.first;
1326  if (first_index + 2 < most_expensive_arc_starts_and_ranks_.size()) {
1327  first_index++;
1328  second_index = first_index + 1;
1329  return true;
1330  }
1331  return false;
1332 }
1333 
1334 namespace {
1335 
1336 // Returns false if the route starting with 'start' is empty. Otherwise sets
1337 // most_expensive_arc_starts_and_ranks and first_expensive_arc_indices according
1338 // to the most expensive chains on the route, and returns true.
1339 bool FindMostExpensiveArcsOnRoute(
1340  int num_arcs, int64_t start,
1341  const std::function<int64_t(int64_t)>& next_accessor,
1342  const std::function<bool(int64_t)>& is_end,
1343  const std::function<int64_t(int64_t, int64_t, int64_t)>&
1344  arc_cost_for_route_start,
1345  std::vector<std::pair<int64_t, int>>* most_expensive_arc_starts_and_ranks,
1346  std::pair<int, int>* first_expensive_arc_indices) {
1347  if (is_end(next_accessor(start))) {
1348  // Empty route.
1349  *first_expensive_arc_indices = {-1, -1};
1350  return false;
1351  }
1352 
1353  // NOTE: The negative ranks are so that for a given cost, lower ranks are
1354  // given higher priority.
1355  using ArcCostNegativeRankStart = std::tuple<int64_t, int, int64_t>;
1356  std::priority_queue<ArcCostNegativeRankStart,
1357  std::vector<ArcCostNegativeRankStart>,
1358  std::greater<ArcCostNegativeRankStart>>
1359  arc_info_pq;
1360 
1361  int64_t before_node = start;
1362  int rank = 0;
1363  while (!is_end(before_node)) {
1364  const int64_t after_node = next_accessor(before_node);
1365  const int64_t arc_cost =
1366  arc_cost_for_route_start(before_node, after_node, start);
1367  arc_info_pq.emplace(arc_cost, -rank, before_node);
1368 
1369  before_node = after_node;
1370  rank++;
1371 
1372  if (rank > num_arcs) {
1373  arc_info_pq.pop();
1374  }
1375  }
1376 
1377  DCHECK_GE(rank, 2);
1378  DCHECK_EQ(arc_info_pq.size(), std::min(rank, num_arcs));
1379 
1380  most_expensive_arc_starts_and_ranks->resize(arc_info_pq.size());
1381  int arc_index = arc_info_pq.size() - 1;
1382  while (!arc_info_pq.empty()) {
1383  const ArcCostNegativeRankStart& arc_info = arc_info_pq.top();
1384  (*most_expensive_arc_starts_and_ranks)[arc_index] = {
1385  std::get<2>(arc_info), -std::get<1>(arc_info)};
1386  arc_index--;
1387  arc_info_pq.pop();
1388  }
1389 
1390  *first_expensive_arc_indices = {0, 1};
1391  return true;
1392 }
1393 
1394 } // namespace
1395 
1396 bool FilteredHeuristicExpensiveChainLNSOperator::
1397  FindMostExpensiveChainsOnRemainingRoutes() {
1398  do {
1399  if (FindMostExpensiveArcsOnRoute(
1400  num_arcs_to_consider_, model_->Start(current_route_),
1401  [this](int64_t i) { return OldValue(i); },
1402  [this](int64_t node) { return model_->IsEnd(node); },
1403  arc_cost_for_route_start_, &most_expensive_arc_starts_and_ranks_,
1404  &current_expensive_arc_indices_)) {
1405  return true;
1406  }
1407  } while (IncrementRoute());
1408 
1409  return false;
1410 }
1411 
1413  const std::vector<IntVar*>& vars,
1414  const std::vector<IntVar*>& secondary_vars,
1415  std::function<int(int64_t)> start_empty_path_class,
1416  int num_arcs_to_consider,
1417  std::function<int64_t(int64_t, int64_t, int64_t)> arc_cost_for_path_start)
1418  : PathOperator(vars, secondary_vars, 1, false, false,
1419  std::move(start_empty_path_class)),
1420  num_arcs_to_consider_(num_arcs_to_consider),
1421  current_path_(0),
1422  current_expensive_arc_indices_({-1, -1}),
1423  arc_cost_for_path_start_(std::move(arc_cost_for_path_start)),
1424  end_path_(0),
1425  has_non_empty_paths_to_explore_(false) {
1426  DCHECK_GE(num_arcs_to_consider_, 2);
1427 }
1428 
1430  const int first_arc_index = current_expensive_arc_indices_.first;
1431  const int second_arc_index = current_expensive_arc_indices_.second;
1432  DCHECK_LE(0, first_arc_index);
1433  DCHECK_LT(first_arc_index, second_arc_index);
1434  DCHECK_LT(second_arc_index, most_expensive_arc_starts_and_ranks_.size());
1435 
1436  const std::pair<int, int>& first_start_and_rank =
1437  most_expensive_arc_starts_and_ranks_[first_arc_index];
1438  const std::pair<int, int>& second_start_and_rank =
1439  most_expensive_arc_starts_and_ranks_[second_arc_index];
1440  if (first_start_and_rank.second < second_start_and_rank.second) {
1441  return CheckChainValidity(first_start_and_rank.first,
1442  second_start_and_rank.first, BaseNode(0)) &&
1443  MoveChain(first_start_and_rank.first, second_start_and_rank.first,
1444  BaseNode(0));
1445  }
1446  return CheckChainValidity(second_start_and_rank.first,
1447  first_start_and_rank.first, BaseNode(0)) &&
1448  MoveChain(second_start_and_rank.first, first_start_and_rank.first,
1449  BaseNode(0));
1450 }
1451 
1453  while (has_non_empty_paths_to_explore_) {
1455  ResetPosition();
1456  // Move on to the next expensive arcs on the same path.
1457  if (IncrementCurrentArcIndices()) {
1458  continue;
1459  }
1460  // Move on to the next non_empty path.
1461  IncrementCurrentPath();
1462  has_non_empty_paths_to_explore_ =
1463  current_path_ != end_path_ &&
1464  FindMostExpensiveChainsOnRemainingPaths();
1465  } else {
1466  return true;
1467  }
1468  }
1469  return false;
1470 }
1471 
1472 void RelocateExpensiveChain::OnNodeInitialization() {
1473  if (current_path_ >= path_starts().size()) {
1474  // current_path_ was made empty by last move (and it was the last non-empty
1475  // path), restart from 0.
1476  current_path_ = 0;
1477  }
1478  end_path_ = current_path_;
1479  has_non_empty_paths_to_explore_ = FindMostExpensiveChainsOnRemainingPaths();
1480 }
1481 
1482 void RelocateExpensiveChain::IncrementCurrentPath() {
1483  const int num_paths = path_starts().size();
1484  if (++current_path_ == num_paths) {
1485  current_path_ = 0;
1486  }
1487 }
1488 
1489 bool RelocateExpensiveChain::IncrementCurrentArcIndices() {
1490  int& second_index = current_expensive_arc_indices_.second;
1491  if (++second_index < most_expensive_arc_starts_and_ranks_.size()) {
1492  return true;
1493  }
1494  int& first_index = current_expensive_arc_indices_.first;
1495  if (first_index + 2 < most_expensive_arc_starts_and_ranks_.size()) {
1496  first_index++;
1497  second_index = first_index + 1;
1498  return true;
1499  }
1500  return false;
1501 }
1502 
1503 bool RelocateExpensiveChain::FindMostExpensiveChainsOnRemainingPaths() {
1504  do {
1505  if (FindMostExpensiveArcsOnRoute(
1506  num_arcs_to_consider_, path_starts()[current_path_],
1507  [this](int64_t i) { return OldNext(i); },
1508  [this](int64_t node) { return IsPathEnd(node); },
1509  arc_cost_for_path_start_, &most_expensive_arc_starts_and_ranks_,
1510  &current_expensive_arc_indices_)) {
1511  return true;
1512  }
1513  IncrementCurrentPath();
1514  } while (current_path_ != end_path_);
1515  return false;
1516 }
1517 
1519  const std::vector<IntVar*>& vars,
1520  const std::vector<IntVar*>& secondary_vars,
1521  std::function<int(int64_t)> start_empty_path_class,
1522  const RoutingIndexPairs& pairs)
1523  : PathOperator(vars, secondary_vars,
1524  /*number_of_base_nodes*/ 2, true, false,
1525  std::move(start_empty_path_class)) {
1526  is_pickup_node_.resize(number_of_nexts_, false);
1527  is_delivery_node_.resize(number_of_nexts_, false);
1528  pair_of_node_.resize(number_of_nexts_, -1);
1529  for (int pair_index = 0; pair_index < pairs.size(); ++pair_index) {
1530  for (const int node : pairs[pair_index].first) {
1531  is_pickup_node_[node] = true;
1532  pair_of_node_[node] = pair_index;
1533  }
1534  for (const int node : pairs[pair_index].second) {
1535  is_delivery_node_[node] = true;
1536  pair_of_node_[node] = pair_index;
1537  }
1538  }
1539  opened_pairs_bitset_.resize(pairs.size(), false);
1540 }
1541 
1542 bool RelocateSubtrip::RelocateSubTripFromPickup(const int64_t chain_first_node,
1543  const int64_t insertion_node) {
1544  if (IsPathEnd(insertion_node)) return false;
1545  if (Prev(chain_first_node) == insertion_node)
1546  return false; // Skip null move.
1547 
1548  int num_opened_pairs = 0;
1549  // Split chain into subtrip and rejected nodes.
1550  rejected_nodes_ = {Prev(chain_first_node)};
1551  subtrip_nodes_ = {insertion_node};
1552  int current = chain_first_node;
1553  do {
1554  if (current == insertion_node) {
1555  // opened_pairs_bitset_ must be all false when we leave this function.
1556  opened_pairs_bitset_.assign(opened_pairs_bitset_.size(), false);
1557  return false;
1558  }
1559  const int pair = pair_of_node_[current];
1560  if (is_delivery_node_[current] && !opened_pairs_bitset_[pair]) {
1561  rejected_nodes_.push_back(current);
1562  } else {
1563  subtrip_nodes_.push_back(current);
1564  if (is_pickup_node_[current]) {
1565  ++num_opened_pairs;
1566  opened_pairs_bitset_[pair] = true;
1567  } else if (is_delivery_node_[current]) {
1568  --num_opened_pairs;
1569  opened_pairs_bitset_[pair] = false;
1570  }
1571  }
1572  current = Next(current);
1573  } while (num_opened_pairs != 0 && !IsPathEnd(current));
1574  DCHECK_EQ(num_opened_pairs, 0);
1575  rejected_nodes_.push_back(current);
1576  subtrip_nodes_.push_back(Next(insertion_node));
1577 
1578  // Set new paths.
1579  const int64_t rejected_path = Path(chain_first_node);
1580  for (int i = 1; i < rejected_nodes_.size(); ++i) {
1581  SetNext(rejected_nodes_[i - 1], rejected_nodes_[i], rejected_path);
1582  }
1583  const int64_t insertion_path = Path(insertion_node);
1584  for (int i = 1; i < subtrip_nodes_.size(); ++i) {
1585  SetNext(subtrip_nodes_[i - 1], subtrip_nodes_[i], insertion_path);
1586  }
1587  return true;
1588 }
1589 
1590 bool RelocateSubtrip::RelocateSubTripFromDelivery(
1591  const int64_t chain_last_node, const int64_t insertion_node) {
1592  if (IsPathEnd(insertion_node)) return false;
1593 
1594  // opened_pairs_bitset_ should be all false.
1595  DCHECK(std::none_of(opened_pairs_bitset_.begin(), opened_pairs_bitset_.end(),
1596  [](bool value) { return value; }));
1597  int num_opened_pairs = 0;
1598  // Split chain into subtrip and rejected nodes. Store nodes in reverse order.
1599  rejected_nodes_ = {Next(chain_last_node)};
1600  subtrip_nodes_ = {Next(insertion_node)};
1601  int current = chain_last_node;
1602  do {
1603  if (current == insertion_node) {
1604  opened_pairs_bitset_.assign(opened_pairs_bitset_.size(), false);
1605  return false;
1606  }
1607  const int pair = pair_of_node_[current];
1608  if (is_pickup_node_[current] && !opened_pairs_bitset_[pair]) {
1609  rejected_nodes_.push_back(current);
1610  } else {
1611  subtrip_nodes_.push_back(current);
1612  if (is_delivery_node_[current]) {
1613  ++num_opened_pairs;
1614  opened_pairs_bitset_[pair] = true;
1615  } else if (is_pickup_node_[current]) {
1616  --num_opened_pairs;
1617  opened_pairs_bitset_[pair] = false;
1618  }
1619  }
1620  current = Prev(current);
1621  } while (num_opened_pairs != 0 && !IsPathStart(current));
1622  DCHECK_EQ(num_opened_pairs, 0);
1623  if (current == insertion_node) return false; // Skip null move.
1624  rejected_nodes_.push_back(current);
1625  subtrip_nodes_.push_back(insertion_node);
1626 
1627  // TODO(user): either remove those std::reverse() and adapt the loops
1628  // below, or refactor the loops into a function that also DCHECKs the path.
1629  std::reverse(rejected_nodes_.begin(), rejected_nodes_.end());
1630  std::reverse(subtrip_nodes_.begin(), subtrip_nodes_.end());
1631 
1632  // Set new paths.
1633  const int64_t rejected_path = Path(chain_last_node);
1634  for (int i = 1; i < rejected_nodes_.size(); ++i) {
1635  SetNext(rejected_nodes_[i - 1], rejected_nodes_[i], rejected_path);
1636  }
1637  const int64_t insertion_path = Path(insertion_node);
1638  for (int i = 1; i < subtrip_nodes_.size(); ++i) {
1639  SetNext(subtrip_nodes_[i - 1], subtrip_nodes_[i], insertion_path);
1640  }
1641  return true;
1642 }
1643 
1645  if (is_pickup_node_[BaseNode(0)]) {
1646  return RelocateSubTripFromPickup(BaseNode(0), BaseNode(1));
1647  } else if (is_delivery_node_[BaseNode(0)]) {
1648  return RelocateSubTripFromDelivery(BaseNode(0), BaseNode(1));
1649  } else {
1650  return false;
1651  }
1652 }
1653 
1655  const std::vector<IntVar*>& vars,
1656  const std::vector<IntVar*>& secondary_vars,
1657  std::function<int(int64_t)> start_empty_path_class,
1658  const RoutingIndexPairs& pairs)
1659  : PathOperator(vars, secondary_vars, 2, true, false,
1660  std::move(start_empty_path_class)) {
1661  is_pickup_node_.resize(number_of_nexts_, false);
1662  is_delivery_node_.resize(number_of_nexts_, false);
1663  pair_of_node_.resize(number_of_nexts_, -1);
1664  for (int pair_index = 0; pair_index < pairs.size(); ++pair_index) {
1665  for (const int node : pairs[pair_index].first) {
1666  is_pickup_node_[node] = true;
1667  pair_of_node_[node] = pair_index;
1668  }
1669  for (const int node : pairs[pair_index].second) {
1670  is_delivery_node_[node] = true;
1671  pair_of_node_[node] = pair_index;
1672  }
1673  }
1674  opened_pairs_set_.resize(pairs.size(), false);
1675 }
1676 
1677 void ExchangeSubtrip::SetPath(const std::vector<int64_t>& path, int path_id) {
1678  for (int i = 1; i < path.size(); ++i) {
1679  SetNext(path[i - 1], path[i], path_id);
1680  }
1681 }
1682 
1683 namespace {
1684 bool VectorContains(const std::vector<int64_t>& values, int64_t target) {
1685  return std::find(values.begin(), values.end(), target) != values.end();
1686 }
1687 } // namespace
1688 
1690  if (pair_of_node_[BaseNode(0)] == -1) return false;
1691  if (pair_of_node_[BaseNode(1)] == -1) return false;
1692  // Break symmetry: a move generated from (BaseNode(0), BaseNode(1)) is the
1693  // same as from (BaseNode(1), BaseNode(1)): no need to do it twice.
1694  if (BaseNode(0) >= BaseNode(1)) return false;
1695  rejects0_.clear();
1696  subtrip0_.clear();
1697  if (!ExtractChainsAndCheckCanonical(BaseNode(0), &rejects0_, &subtrip0_)) {
1698  return false;
1699  }
1700  rejects1_.clear();
1701  subtrip1_.clear();
1702  if (!ExtractChainsAndCheckCanonical(BaseNode(1), &rejects1_, &subtrip1_)) {
1703  return false;
1704  }
1705 
1706  // If paths intersect, skip the move.
1707  if (Path(BaseNode(0)) == Path(BaseNode(1))) {
1708  if (VectorContains(rejects0_, subtrip1_.front())) return false;
1709  if (VectorContains(rejects1_, subtrip0_.front())) return false;
1710  if (VectorContains(subtrip0_, subtrip1_.front())) return false;
1711  if (VectorContains(subtrip1_, subtrip0_.front())) return false;
1712  }
1713 
1714  // Assemble the new paths.
1715  path0_ = {Prev(subtrip0_.front())};
1716  path1_ = {Prev(subtrip1_.front())};
1717  const int64_t last0 = Next(subtrip0_.back());
1718  const int64_t last1 = Next(subtrip1_.back());
1719  const bool concatenated01 = last0 == subtrip1_.front();
1720  const bool concatenated10 = last1 == subtrip0_.front();
1721 
1722  if (is_delivery_node_[BaseNode(0)]) std::swap(subtrip1_, rejects0_);
1723  path0_.insert(path0_.end(), subtrip1_.begin(), subtrip1_.end());
1724  path0_.insert(path0_.end(), rejects0_.begin(), rejects0_.end());
1725  path0_.push_back(last0);
1726 
1727  if (is_delivery_node_[BaseNode(1)]) std::swap(subtrip0_, rejects1_);
1728  path1_.insert(path1_.end(), subtrip0_.begin(), subtrip0_.end());
1729  path1_.insert(path1_.end(), rejects1_.begin(), rejects1_.end());
1730  path1_.push_back(last1);
1731 
1732  // When the trips are concatenated, bypass the regular extremities.
1733  if (concatenated01) {
1734  path0_.pop_back();
1735  path1_.front() = path0_.back();
1736  } else if (concatenated10) {
1737  path1_.pop_back();
1738  path0_.front() = path1_.back();
1739  }
1740 
1741  // Change the paths. Since SetNext() modifies Path() values,
1742  // record path_id0 and path_id11 before calling SetPath();
1743  const int64_t path0_id = Path(BaseNode(0));
1744  const int64_t path1_id = Path(BaseNode(1));
1745  SetPath(path0_, path0_id);
1746  SetPath(path1_, path1_id);
1747  return true;
1748 }
1749 
1750 bool ExchangeSubtrip::ExtractChainsAndCheckCanonical(
1751  int64_t base_node, std::vector<int64_t>* rejects,
1752  std::vector<int64_t>* subtrip) {
1753  const bool extracted =
1754  is_pickup_node_[base_node]
1755  ? ExtractChainsFromPickup(base_node, rejects, subtrip)
1756  : ExtractChainsFromDelivery(base_node, rejects, subtrip);
1757  if (!extracted) return false;
1758  // Check canonicality.
1759  return !is_delivery_node_[base_node] ||
1760  pair_of_node_[subtrip->front()] != pair_of_node_[subtrip->back()] ||
1761  !rejects->empty();
1762 }
1763 
1764 bool ExchangeSubtrip::ExtractChainsFromPickup(int64_t base_node,
1765  std::vector<int64_t>* rejects,
1766  std::vector<int64_t>* subtrip) {
1767  DCHECK(is_pickup_node_[base_node]);
1768  DCHECK(rejects->empty());
1769  DCHECK(subtrip->empty());
1770  // Iterate from base_node forwards while maintaining the set of opened pairs.
1771  // A pair is opened by a pickup, closed with the corresponding delivery.
1772  opened_pairs_set_.assign(opened_pairs_set_.size(), false);
1773  int num_opened_pairs = 0;
1774  int current = base_node;
1775  do {
1776  const int pair = pair_of_node_[current];
1777  if (is_delivery_node_[current] && !opened_pairs_set_[pair]) {
1778  rejects->push_back(current);
1779  } else {
1780  subtrip->push_back(current);
1781  if (is_pickup_node_[current]) {
1782  ++num_opened_pairs;
1783  opened_pairs_set_[pair] = true;
1784  } else if (is_delivery_node_[current]) {
1785  --num_opened_pairs;
1786  opened_pairs_set_[pair] = false;
1787  }
1788  }
1789  current = Next(current);
1790  } while (num_opened_pairs != 0 && !IsPathEnd(current));
1791  return num_opened_pairs == 0;
1792 }
1793 
1794 bool ExchangeSubtrip::ExtractChainsFromDelivery(int64_t base_node,
1795  std::vector<int64_t>* rejects,
1796  std::vector<int64_t>* subtrip) {
1797  DCHECK(is_delivery_node_[base_node]);
1798  DCHECK(rejects->empty());
1799  DCHECK(subtrip->empty());
1800  // Iterate from base_node backwards while maintaining the set of opened pairs.
1801  // A pair is opened by a delivery, closed with the corresponding pickup.
1802  opened_pairs_set_.assign(opened_pairs_set_.size(), false);
1803  int num_opened_pairs = 0;
1804  int current = base_node;
1805  do {
1806  const int pair = pair_of_node_[current];
1807  if (is_pickup_node_[current] && !opened_pairs_set_[pair]) {
1808  rejects->push_back(current);
1809  } else {
1810  subtrip->push_back(current);
1811  if (is_delivery_node_[current]) {
1812  ++num_opened_pairs;
1813  opened_pairs_set_[pair] = true;
1814  } else if (is_pickup_node_[current]) {
1815  --num_opened_pairs;
1816  opened_pairs_set_[pair] = false;
1817  }
1818  }
1819  current = Prev(current);
1820  } while (num_opened_pairs != 0 && !IsPathStart(current));
1821  if (num_opened_pairs != 0) return false;
1822  std::reverse(rejects->begin(), rejects->end());
1823  std::reverse(subtrip->begin(), subtrip->end());
1824  return true;
1825 }
1826 
1827 } // namespace operations_research
int64_t min
Definition: alldiff_cst.cc:139
An Assignment is a variable -> domains mapping, used to report solutions to the user.
ExchangeSubtrip(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, const RoutingIndexPairs &pairs)
FilteredHeuristicCloseNodesLNSOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic, int num_close_nodes)
FilteredHeuristicExpensiveChainLNSOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic, int num_arcs_to_consider, std::function< int64_t(int64_t, int64_t, int64_t)> arc_cost_for_route_start)
Class of operators using a RoutingFilteredHeuristic to insert unperformed nodes after changes have be...
FilteredHeuristicLocalSearchOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic, bool keep_inverse_values=false)
virtual std::function< int64_t(int64_t)> SetupNextAccessorForNeighbor()=0
Virtual method to return the next_accessor to be passed to the heuristic to build a new solution.
SparseBitset removed_nodes_
Keeps track of removed nodes when making a neighbor.
FilteredHeuristicPathLNSOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic)
GroupPairAndRelocateOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, const RoutingIndexPairs &index_pairs)
IndexPairSwapActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, const RoutingIndexPairs &index_pairs)
bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta) override
OnStart() should really be protected, but then SWIG doesn't see it.
Specialization of LocalSearchOperator built from an array of IntVars which specifies the scope of the...
void SetValue(int64_t index, int64_t value)
bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta) override
OnStart() should really be protected, but then SWIG doesn't see it.
Definition: local_search.cc:79
void RevertChanges(bool change_was_incremental)
bool ApplyChanges(Assignment *delta, Assignment *deltadelta) const
int64_t Value(int64_t index) const
Returns the value in the current assignment of the variable of given index.
IntVar * Var(int64_t index) const
Returns the variable of given index.
void AddVars(const std::vector< IntVar * > &vars)
LightPairRelocateOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, const RoutingIndexPairs &index_pairs, std::function< bool(int64_t)> force_lifo=nullptr)
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...
MakePairActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, const RoutingIndexPairs &pairs)
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
MakePairInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, const RoutingIndexPairs &index_pairs)
MakeRelocateNeighborsOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, RoutingTransitCallback2 arc_evaluator)
PairExchangeOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, const RoutingIndexPairs &index_pairs)
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...
PairExchangeRelocateOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, const RoutingIndexPairs &index_pairs)
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...
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...
PairRelocateOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, const RoutingIndexPairs &index_pairs)
Base class of the local search operators dedicated to path modifications (a path is a set of nodes li...
int64_t StartNode(int i) const
Returns the start node of the ith base node.
bool IsInactive(int64_t node) const
Returns true if node is inactive.
virtual void OnNodeInitialization()
Called by OnStart() after initializing node information.
bool IsPathStart(int64_t node) const
Returns true if node is the first node on the path.
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 ...
bool IsPathEnd(int64_t node) const
Returns true if node is the last node on the path; defined by the fact that node is outside the range...
int64_t Next(int64_t node) const
Returns the node after node in the current delta.
bool MoveChain(int64_t before_chain, int64_t chain_end, int64_t destination)
Moves the chain starting after the node before_chain and ending at the node chain_end after the node ...
bool MakeActive(int64_t node, int64_t destination)
Insert the inactive node after destination.
const std::vector< int64_t > & path_starts() const
Returns the vector of path start nodes.
void SetNext(int64_t from, int64_t to, int64_t path)
Sets 'to' to be the node after 'from' on the given path.
int64_t BaseSiblingAlternativeNode(int i) const
Returns the alternative node for the sibling of the ith base node.
int64_t Prev(int64_t node) const
Returns the node before node in the current delta.
int64_t GetActiveAlternativeSibling(int node) const
Returns the active node in the alternative set of the sibling of the given node.
int64_t OldNext(int64_t node) const
bool SwapActiveAndInactive(int64_t active, int64_t inactive)
Replaces active by inactive in the current path, making active inactive.
void ResetPosition()
Reset the position of the operator to its position when Start() was last called; this can be used to ...
int64_t BaseNode(int i) const
Returns the ith base node of the operator.
int64_t BaseAlternativeNode(int i) const
Returns the alternative node for the ith base node.
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
void AddPairAlternativeSets(const std::vector< std::pair< std::vector< int64_t >, std::vector< int64_t >>> &pair_alternative_sets)
Adds all sets of node alternatives of a vector of alternative pairs.
int64_t Path(int64_t node) const
Returns the index of the path to which node belongs in the current delta.
virtual void SetNextBaseToIncrement(int64_t base_index)
Set the next base to increment on next iteration.
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.
RelocateExpensiveChain(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, int num_arcs_to_consider, std::function< int64_t(int64_t, int64_t, int64_t)> arc_cost_for_path_start)
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
RelocatePathAndHeuristicInsertUnperformedOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic)
RelocateSubtrip(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, const RoutingIndexPairs &pairs)
VehicleClassIndex GetVehicleClassIndexOfVehicle(int64_t vehicle) const
Definition: routing.h:1561
int GetVehicleClassesCount() const
Returns the number of different vehicle classes in the model.
Definition: routing.h:1581
bool IsStart(int64_t index) const
Returns true if 'index' represents the first node of a route.
Definition: routing.h:1454
IntVar * NextVar(int64_t index) const
!defined(SWIGPYTHON)
Definition: routing.h:1485
const std::vector< std::pair< int, int > > & GetDeliveryIndexPairs(int64_t node_index) const
Same as above for deliveries.
Definition: routing.cc:2334
int64_t Size() const
Returns the number of next variables in the model.
Definition: routing.h:1654
bool CheckLimit(absl::Duration offset=absl::ZeroDuration())
Returns true if the search limit has been crossed with the given time offset.
Definition: routing.h:1634
const std::vector< IntVar * > & VehicleVars() const
Returns all vehicle variables of the model, such that VehicleVars(i) is the vehicle variable of the n...
Definition: routing.h:1475
int64_t Start(int vehicle) const
Model inspection.
Definition: routing.h:1450
int vehicles() const
Returns the number of vehicle routes in the model.
Definition: routing.h:1652
int64_t GetArcCostForClass(int64_t from_index, int64_t to_index, int64_t cost_class_index) const
Returns the cost of the segment between two nodes for a given cost class.
Definition: routing.cc:4162
const std::vector< std::pair< int, int > > & GetPickupIndexPairs(int64_t node_index) const
Returns pairs for which the node is a pickup; the first element of each pair is the index in the pick...
Definition: routing.cc:2328
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
Definition: routing.h:1456
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
Definition: routing.h:1556
int VehicleIndex(int64_t index) const
Returns the vehicle of the given start/end index, and -1 if the given index is not a vehicle start/en...
Definition: routing.h:1459
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
Definition: routing.h:1452
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
Definition: bitset.h:806
void Set(IntegerType index)
Definition: bitset.h:792
SwapActiveToShortestPathOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, std::vector< std::vector< int64_t >> alternative_sets, RoutingTransitCallback2 arc_evaluator)
void OnStart() override
Called by Start() after synchronizing the operator with the current assignment.
bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta) override
OnStart() should really be protected, but then SWIG doesn't see it.
SwapIndexPairOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &path_vars, std::function< int(int64_t)> start_empty_path_class, const RoutingIndexPairs &index_pairs)
Block * next
int64_t value
absl::Status status
Definition: g_gurobi.cc:41
GRBmodel * model
static const int64_t kint64max
int index
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:269
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
std::function< int64_t(int64_t, int64_t)> RoutingTransitCallback2
Definition: routing_types.h:43
std::vector< RoutingIndexPair > RoutingIndexPairs
Definition: routing_types.h:46
int64_t delta
Definition: resource.cc:1695
int vehicle_class
int nodes
int64_t start