Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
routing_neighborhoods.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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 <utility>
20#include <vector>
21
22#include "absl/log/check.h"
23#include "absl/types/span.h"
24#include "ortools/base/types.h"
30
31namespace operations_research {
32
34 const std::vector<IntVar*>& vars,
35 const std::vector<IntVar*>& secondary_vars,
36 std::function<int(int64_t)> start_empty_path_class,
37 std::function<const std::vector<int>&(int, int)> get_neighbors,
38 RoutingTransitCallback2 arc_evaluator)
39 : PathOperator(vars, secondary_vars,
40 /*number_of_base_nodes=*/get_neighbors == nullptr ? 2 : 1,
41 /*skip_locally_optimal_paths=*/true,
42 /*accept_path_end_base=*/false,
43 std::move(start_empty_path_class), std::move(get_neighbors)),
44 arc_evaluator_(std::move(arc_evaluator)) {}
45
47 const auto do_move = [this](int64_t before_chain, int64_t destination) {
48 int64_t chain_end = Next(before_chain);
49 if (IsPathEnd(chain_end)) return false;
50 if (chain_end == destination) return false;
51 const int64_t max_arc_value = arc_evaluator_(destination, chain_end);
52 int64_t next = Next(chain_end);
53 while (!IsPathEnd(next) &&
54 arc_evaluator_(chain_end, next) <= max_arc_value) {
55 // We return false here to avoid symmetric moves. The rationale is that
56 // if destination is part of the same group as the chain, we probably want
57 // to extend the chain to contain it, which means finding another
58 // destination further down the path.
59 // TODO(user): Add a parameter to either return false or break here,
60 // depending if we want to permutate nodes within the same chain.
61 if (next == destination) return false;
62 chain_end = next;
63 next = Next(chain_end);
64 }
65 return MoveChainAndRepair(before_chain, chain_end, destination);
66 };
67 if (HasNeighbors()) {
68 const int64_t node = GetNeighborForBaseNode(0);
69 if (IsInactive(node)) return false;
70 return do_move(/*before_chain=*/Prev(node),
71 /*destination=*/BaseNode(0));
72 } else {
73 return do_move(/*before_chain=*/BaseNode(0),
74 /*destination=*/BaseNode(1));
75 }
76}
77
78bool MakeRelocateNeighborsOperator::MoveChainAndRepair(int64_t before_chain,
79 int64_t chain_end,
80 int64_t destination) {
81 if (MoveChain(before_chain, chain_end, destination)) {
82 if (!IsPathStart(destination)) {
83 int64_t current = Prev(destination);
84 int64_t last = chain_end;
85 if (current == last) { // chain was just before destination
86 current = before_chain;
87 }
88 while (last >= 0 && !IsPathStart(current) && current != last) {
89 last = Reposition(current, last);
90 current = Prev(current);
91 }
92 }
93 return true;
94 }
95 return false;
96}
97
98int64_t MakeRelocateNeighborsOperator::Reposition(int64_t before_to_move,
99 int64_t up_to) {
100 const int64_t kNoChange = -1;
101 const int64_t to_move = Next(before_to_move);
102 int64_t next = Next(to_move);
103 if (Var(to_move)->Contains(next)) {
104 return kNoChange;
105 }
106 int64_t prev = next;
107 next = Next(next);
108 while (prev != up_to) {
109 if (Var(prev)->Contains(to_move) && Var(to_move)->Contains(next)) {
110 MoveChain(before_to_move, to_move, prev);
111 return up_to;
112 }
113 prev = next;
114 next = Next(next);
115 }
116 if (Var(prev)->Contains(to_move)) {
117 MoveChain(before_to_move, to_move, prev);
118 return to_move;
119 }
120 return kNoChange;
121}
122
124 const std::vector<IntVar*>& vars,
125 const std::vector<IntVar*>& secondary_vars,
126 std::function<int(int64_t)> start_empty_path_class,
127 std::vector<std::vector<int64_t>> alternative_sets,
128 RoutingTransitCallback2 arc_evaluator)
129 : PathOperator(vars, secondary_vars, 1, true, false,
130 std::move(start_empty_path_class), nullptr),
131 arc_evaluator_(std::move(arc_evaluator)),
132 alternative_sets_(std::move(alternative_sets)),
133 to_alternative_set_(vars.size(), -1),
134 path_predecessor_(vars.size(), -1),
135 touched_(vars.size()) {
136 for (int i = 0; i < alternative_sets_.size(); ++i) {
137 for (int j : alternative_sets_[i]) {
138 if (j < to_alternative_set_.size()) to_alternative_set_[j] = i;
139 }
140 }
141}
142
144 const int64_t before_chain = BaseNode(0);
145 if (to_alternative_set_[before_chain] != -1) return false;
146 int64_t next = Next(before_chain);
147 std::vector<int> alternatives;
148 while (!IsPathEnd(next) && to_alternative_set_[next] != -1 &&
149 alternative_sets_[to_alternative_set_[next]].size() > 1) {
150 alternatives.push_back(to_alternative_set_[next]);
151 next = Next(next);
152 }
153 if (alternatives.empty()) return false;
154 const int sink = next;
155 next = OldNext(before_chain);
156 bool swap_done = false;
157 for (int64_t node : GetShortestPath(before_chain, sink, alternatives)) {
158 if (node != next) {
160 swap_done = true;
161 }
162 next = OldNext(next);
163 }
164 return swap_done;
165}
166
167const std::vector<int64_t>& SwapActiveToShortestPathOperator::GetShortestPath(
168 int source, int sink, const std::vector<int>& alternative_chain) {
169 path_.clear();
170 if (alternative_chain.empty()) return path_;
171 // Initializing values at the first "layer" after the source (from source to
172 // all alternatives at rank 0).
173 const std::vector<int64_t>& first_alternative_set =
174 alternative_sets_[alternative_chain[0]];
175 std::vector<int64_t> prev_values;
176 prev_values.reserve(first_alternative_set.size());
177 for (int alternative_node : first_alternative_set) {
178 prev_values.push_back(arc_evaluator_(source, alternative_node));
179 }
180 // Updating values "layer" by "layer" (each one is fully connected to the
181 // previous one).
182 std::vector<int64_t> current_values;
183 for (int rank = 1; rank < alternative_chain.size(); ++rank) {
184 const std::vector<int64_t>& current_alternative_set =
185 alternative_sets_[alternative_chain[rank]];
186 current_values.clear();
187 current_values.reserve(current_alternative_set.size());
188 const std::vector<int64_t>& prev_alternative_set =
189 alternative_sets_[alternative_chain[rank - 1]];
190 for (int alternative_node : current_alternative_set) {
191 int64_t min_value = kint64max;
192 int predecessor = -1;
193 for (int prev_alternative = 0;
194 prev_alternative < prev_alternative_set.size(); ++prev_alternative) {
195 const int64_t new_value =
196 CapAdd(prev_values[prev_alternative],
197 arc_evaluator_(prev_alternative_set[prev_alternative],
198 alternative_node));
199 if (new_value <= min_value) {
200 min_value = new_value;
201 predecessor = prev_alternative_set[prev_alternative];
202 }
203 }
204 current_values.push_back(min_value);
205 path_predecessor_[alternative_node] = predecessor;
206 }
207 prev_values.swap(current_values);
208 }
209 // Get the predecessor in the shortest path to sink in the last layer.
210 int64_t min_value = kint64max;
211 int predecessor = -1;
212 const std::vector<int64_t>& last_alternative_set =
213 alternative_sets_[alternative_chain.back()];
214 for (int alternative = 0; alternative < last_alternative_set.size();
215 ++alternative) {
216 const int64_t new_value =
217 CapAdd(prev_values[alternative],
218 arc_evaluator_(last_alternative_set[alternative], sink));
219 if (new_value <= min_value) {
220 min_value = new_value;
221 predecessor = last_alternative_set[alternative];
222 }
223 }
224 if (predecessor == -1) return path_;
225 // Build the path from predecessors on the shortest path.
226 path_.resize(alternative_chain.size(), predecessor);
227 touched_.SparseClearAll();
228 touched_.Set(predecessor);
229 for (int rank = alternative_chain.size() - 2; rank >= 0; --rank) {
230 path_[rank] = path_predecessor_[path_[rank + 1]];
231 if (touched_[path_[rank]]) {
232 path_.clear();
233 return path_;
234 }
235 touched_.Set(path_[rank]);
236 }
237 return path_;
238}
239
241 const std::vector<IntVar*>& vars,
242 const std::vector<IntVar*>& secondary_vars,
243 std::function<int(int64_t)> start_empty_path_class,
244 const std::vector<PickupDeliveryPair>& pairs)
245 : PathOperator(vars, secondary_vars, 2, false, true,
246 std::move(start_empty_path_class), nullptr),
247 inactive_pair_(0),
248 inactive_pair_first_index_(0),
249 inactive_pair_second_index_(0),
250 pairs_(pairs) {}
251
253 while (inactive_pair_ < pairs_.size()) {
254 if (PathOperator::MakeOneNeighbor()) return true;
256 const auto& [pickup_alternatives, delivery_alternatives] =
257 pairs_[inactive_pair_];
258 if (inactive_pair_first_index_ < pickup_alternatives.size() - 1) {
259 ++inactive_pair_first_index_;
260 } else if (inactive_pair_second_index_ < delivery_alternatives.size() - 1) {
261 inactive_pair_first_index_ = 0;
262 ++inactive_pair_second_index_;
263 } else {
264 inactive_pair_ = FindNextInactivePair(inactive_pair_ + 1);
265 inactive_pair_first_index_ = 0;
266 inactive_pair_second_index_ = 0;
267 }
268 }
269 return false;
270}
271
273 DCHECK_EQ(StartNode(0), StartNode(1));
274 // Inserting the second node of the pair before the first one which ensures
275 // that the only solutions where both nodes are next to each other have the
276 // first node before the second (the move is not symmetric and doing it this
277 // way ensures that a potential precedence constraint between the nodes of the
278 // pair is not violated).
279 const auto& [pickup_alternatives, delivery_alternatives] =
280 pairs_[inactive_pair_];
281 return MakeActive(delivery_alternatives[inactive_pair_second_index_],
282 BaseNode(1)) &&
283 MakeActive(pickup_alternatives[inactive_pair_first_index_],
284 BaseNode(0));
285}
286
288 // Base node 1 must be after base node 0 if they are both on the same path.
289 if (base_index == 0 || StartNode(base_index) != StartNode(base_index - 1)) {
290 return StartNode(base_index);
291 } else {
292 return BaseNode(base_index - 1);
293 }
294}
295
296void MakePairActiveOperator::OnNodeInitialization() {
297 inactive_pair_ = FindNextInactivePair(0);
298 inactive_pair_first_index_ = 0;
299 inactive_pair_second_index_ = 0;
300}
301
302int MakePairActiveOperator::FindNextInactivePair(int pair_index) const {
303 for (int index = pair_index; index < pairs_.size(); ++index) {
304 if (!ContainsActiveNodes(pairs_[index].pickup_alternatives) &&
305 !ContainsActiveNodes(pairs_[index].delivery_alternatives)) {
306 return index;
307 }
308 }
309 return pairs_.size();
310}
311
312bool MakePairActiveOperator::ContainsActiveNodes(
313 const std::vector<int64_t>& nodes) const {
314 for (int64_t node : nodes) {
315 if (!IsInactive(node)) return true;
316 }
317 return false;
318}
319
321 const std::vector<IntVar*>& vars,
322 const std::vector<IntVar*>& secondary_vars,
323 std::function<int(int64_t)> start_empty_path_class,
324 const std::vector<PickupDeliveryPair>& pairs)
325 : PathOperator(vars, secondary_vars, 1, true, false,
326 std::move(start_empty_path_class), nullptr) {
328}
329
331 const int64_t base = BaseNode(0);
332 const int64_t first_index = Next(base);
333 const int64_t second_index = GetActiveAlternativeSibling(first_index);
334 if (second_index < 0) {
335 return false;
336 }
337 return MakeChainInactive(base, first_index) &&
338 MakeChainInactive(Prev(second_index), second_index);
339}
340
342 const std::vector<IntVar*>& vars,
343 const std::vector<IntVar*>& secondary_vars,
344 std::function<int(int64_t)> start_empty_path_class,
345 const std::vector<PickupDeliveryPair>& pairs)
346 : PathOperator(vars, secondary_vars, 3, true, false,
347 std::move(start_empty_path_class), nullptr) {
349}
350
352 DCHECK_EQ(StartNode(1), StartNode(2));
353 const int64_t first_pair_node = BaseNode(kPairFirstNode);
354 if (IsPathStart(first_pair_node)) {
355 return false;
356 }
357 int64_t first_prev = Prev(first_pair_node);
358 const int second_pair_node = GetActiveAlternativeSibling(first_pair_node);
359 if (second_pair_node < 0 || IsPathEnd(second_pair_node) ||
360 IsPathStart(second_pair_node)) {
361 return false;
362 }
363 const int64_t second_prev = Prev(second_pair_node);
364
365 const int64_t first_node_destination = BaseNode(kPairFirstNodeDestination);
366 if (first_node_destination == second_pair_node) {
367 // The second_pair_node -> first_pair_node link is forbidden.
368 return false;
369 }
370
371 const int64_t second_node_destination = BaseNode(kPairSecondNodeDestination);
372 if (second_prev == first_pair_node && first_node_destination == first_prev &&
373 second_node_destination == first_prev) {
374 // If the current sequence is first_prev -> first_pair_node ->
375 // second_pair_node, and both 1st and 2nd are moved both to prev, the result
376 // of the move will be first_prev -> first_pair_node -> second_pair_node,
377 // which is no move.
378 return false;
379 }
380
381 // Relocation is successful if both moves are feasible and at least one of the
382 // nodes moves.
383 if (second_pair_node == second_node_destination ||
384 first_pair_node == first_node_destination) {
385 return false;
386 }
387 const bool moved_second_pair_node =
388 MoveChain(second_prev, second_pair_node, second_node_destination);
389 // Explicitly calling Prev as second_pair_node might have been moved before
390 // first_pair_node.
391 const bool moved_first_pair_node =
392 MoveChain(Prev(first_pair_node), first_pair_node, first_node_destination);
393 // Swapping alternatives in.
394 SwapActiveAndInactive(second_pair_node,
395 BaseSiblingAlternativeNode(kPairFirstNode));
396 SwapActiveAndInactive(first_pair_node, BaseAlternativeNode(kPairFirstNode));
397 return moved_first_pair_node || moved_second_pair_node;
398}
399
401 // Destination node of the second node of a pair must be after the
402 // destination node of the first node of a pair.
403 if (base_index == kPairSecondNodeDestination) {
404 return BaseNode(kPairFirstNodeDestination);
405 } else {
406 return StartNode(base_index);
407 }
408}
409
411 const std::vector<IntVar*>& vars,
412 const std::vector<IntVar*>& secondary_vars,
413 std::function<int(int64_t)> start_empty_path_class,
414 std::function<const std::vector<int>&(int, int)> get_neighbors,
415 const std::vector<PickupDeliveryPair>& pairs)
416 : PathOperator(vars, secondary_vars,
417 /*number_of_base_nodes=*/get_neighbors == nullptr ? 2 : 1,
418 /*skip_locally_optimal_paths=*/true,
419 /*accept_path_end_base=*/false,
420 std::move(start_empty_path_class),
421 std::move(get_neighbors)) {
423}
424
426 const auto do_move = [this](int64_t node, int64_t destination) {
427 if (IsPathEnd(node) || IsInactive(node)) return false;
428 const int64_t sibling = GetActiveAlternativeSibling(node);
429 if (sibling == -1) return false;
430 // Skip redundant cases.
431 if (destination == node || destination == sibling) return false;
432 const bool ok = MoveChain(Prev(node), node, destination);
433 return MoveChain(Prev(sibling), sibling, node) || ok;
434 };
435 return HasNeighbors()
436 ? do_move(/*node=*/GetNeighborForBaseNode(0),
437 /*destination=*/BaseNode(0))
438 : do_move(/*node=*/Next(BaseNode(0)), /*destination=*/BaseNode(1));
439}
440
442 const std::vector<IntVar*>& vars,
443 const std::vector<IntVar*>& secondary_vars,
444 std::function<int(int64_t)> start_empty_path_class,
445 std::function<const std::vector<int>&(int, int)> get_neighbors,
446 const std::vector<PickupDeliveryPair>& pairs,
447 std::function<bool(int64_t)> force_lifo)
448 : PathOperator(vars, secondary_vars,
449 /*number_of_base_nodes=*/get_neighbors == nullptr ? 2 : 1,
450 /*skip_locally_optimal_paths=*/true,
451 /*accept_path_end_base=*/false,
452 std::move(start_empty_path_class), std::move(get_neighbors)),
453 force_lifo_(std::move(force_lifo)) {
455}
456
458 const std::vector<IntVar*>& vars,
459 const std::vector<IntVar*>& secondary_vars,
460 std::function<int(int64_t)> start_empty_path_class,
461 const std::vector<PickupDeliveryPair>& pairs,
462 std::function<bool(int64_t)> force_lifo)
463 : LightPairRelocateOperator(vars, secondary_vars, start_empty_path_class,
464 nullptr, pairs, std::move(force_lifo)) {}
465
467 const auto do_move = [this](int64_t node, int64_t destination,
468 bool destination_is_lifo) {
469 if (IsPathStart(node) || IsPathEnd(node) || IsInactive(node)) return false;
470 const int64_t prev = Prev(node);
471 if (IsPathEnd(node)) return false;
472 const int64_t sibling = GetActiveAlternativeSibling(node);
473 if (sibling == -1 || destination == sibling) return false;
474
475 // Note: MoveChain will return false if it is a no-op (moving the chain to
476 // its current position). However we want to accept the move if at least
477 // node or sibling gets moved to a new position. Therefore we want to be
478 // sure both MoveChains are called and at least one succeeds.
479
480 // Special case handling relocating the first node of a pair "before" the
481 // first node of another pair. Limiting this to relocating after the start
482 // of the path as other moves will be mostly equivalent to relocating
483 // "after".
484 // TODO(user): extend to relocating before the start of sub-tours (when
485 // all pairs have been matched).
486 if (IsPathStart(destination)) {
487 const bool ok = MoveChain(prev, node, destination);
488 const int64_t destination_sibling =
490 if (destination_sibling == -1) {
491 // Not inserting before a pair node: insert sibling after node.
492 return MoveChain(Prev(sibling), sibling, node) || ok;
493 } else {
494 // Depending on the lifo status of the path, insert sibling before or
495 // after destination_sibling since node is being inserted before
496 // next(destination).
497 if (!destination_is_lifo) {
498 if (Prev(destination_sibling) == sibling) return ok;
499 return MoveChain(Prev(sibling), sibling, Prev(destination_sibling)) ||
500 ok;
501 } else {
502 return MoveChain(Prev(sibling), sibling, destination_sibling) || ok;
503 }
504 }
505 }
506 // Relocating the first node of a pair "after" the first node of another
507 // pair.
508 const int64_t destination_sibling =
509 GetActiveAlternativeSibling(destination);
510 if (destination_sibling == -1) return false;
511 const bool ok = MoveChain(prev, node, destination);
512 if (!destination_is_lifo) {
513 return MoveChain(Prev(sibling), sibling, destination_sibling) || ok;
514 } else {
515 if (Prev(destination_sibling) == sibling) return ok;
516 return MoveChain(Prev(sibling), sibling, Prev(destination_sibling)) || ok;
517 }
518 };
519 // TODO(user): Add support for lifo for neighbor-based move.
520 return HasNeighbors()
521 ? do_move(/*node=*/GetNeighborForBaseNode(0),
522 /*destination=*/BaseNode(0),
523 /*destination_is_lifo=*/false)
524 : do_move(/*node=*/Next(BaseNode(0)), /*destination=*/BaseNode(1),
525 force_lifo_ != nullptr && force_lifo_(StartNode(1)));
526}
527
529 const std::vector<IntVar*>& vars,
530 const std::vector<IntVar*>& secondary_vars,
531 std::function<int(int64_t)> start_empty_path_class,
532 std::function<const std::vector<int>&(int, int)> get_neighbors,
533 const std::vector<PickupDeliveryPair>& pairs)
534 : PathOperator(vars, secondary_vars,
535 /*number_of_base_nodes=*/get_neighbors == nullptr ? 2 : 1,
536 /*skip_locally_optimal_paths=*/true,
537 /*accept_path_end_base=*/true,
538 std::move(start_empty_path_class),
539 std::move(get_neighbors)) {
541}
542
544 const int64_t node1 = BaseNode(0);
545 int64_t prev1, sibling1, sibling_prev1 = -1;
546 if (!GetPreviousAndSibling(node1, &prev1, &sibling1, &sibling_prev1)) {
547 return false;
548 }
549 int64_t node2 = -1;
550 if (!HasNeighbors()) {
551 node2 = BaseNode(1);
552 } else {
553 const int64_t neighbor = GetNeighborForBaseNode(0);
554 if (IsInactive(neighbor) || IsPathStart(neighbor)) return false;
555 node2 = Prev(neighbor);
556 }
557 int64_t prev2, sibling2, sibling_prev2 = -1;
558 if (!GetPreviousAndSibling(node2, &prev2, &sibling2, &sibling_prev2)) {
559 return false;
560 }
561 bool status = true;
562 // Exchanging node1 and node2.
563 if (node1 == prev2) {
564 status = MoveChain(prev2, node2, prev1);
565 if (sibling_prev1 == node2) sibling_prev1 = node1;
566 if (sibling_prev2 == node2) sibling_prev2 = node1;
567 } else if (node2 == prev1) {
568 status = MoveChain(prev1, node1, prev2);
569 if (sibling_prev1 == node1) sibling_prev1 = node2;
570 if (sibling_prev2 == node1) sibling_prev2 = node2;
571 } else {
572 status = MoveChain(prev1, node1, node2) && MoveChain(prev2, node2, prev1);
573 if (sibling_prev1 == node1) {
574 sibling_prev1 = node2;
575 } else if (sibling_prev1 == node2) {
576 sibling_prev1 = node1;
577 }
578 if (sibling_prev2 == node1) {
579 sibling_prev2 = node2;
580 } else if (sibling_prev2 == node2) {
581 sibling_prev2 = node1;
582 }
583 }
584 if (!status) return false;
585 // Exchanging sibling1 and sibling2.
586 if (sibling1 == sibling_prev2) {
587 status = MoveChain(sibling_prev2, sibling2, sibling_prev1);
588 } else if (sibling2 == sibling_prev1) {
589 status = MoveChain(sibling_prev1, sibling1, sibling_prev2);
590 } else {
591 status = MoveChain(sibling_prev1, sibling1, sibling2) &&
592 MoveChain(sibling_prev2, sibling2, sibling_prev1);
593 }
594 // Swapping alternatives in.
597 if (!HasNeighbors()) {
598 // TODO(user): Support alternatives with neighbors.
601 }
602 return status;
603}
604
605bool PairExchangeOperator::GetPreviousAndSibling(
606 int64_t node, int64_t* previous, int64_t* sibling,
607 int64_t* sibling_previous) const {
608 if (IsPathStart(node)) return false;
609 *previous = Prev(node);
610 *sibling = GetActiveAlternativeSibling(node);
611 *sibling_previous = *sibling >= 0 ? Prev(*sibling) : -1;
612 return *sibling_previous >= 0;
613}
614
616 const std::vector<IntVar*>& vars,
617 const std::vector<IntVar*>& secondary_vars,
618 std::function<int(int64_t)> start_empty_path_class,
619 const std::vector<PickupDeliveryPair>& pairs)
620 : PathOperator(vars, secondary_vars, 6, true, false,
621 std::move(start_empty_path_class), nullptr) {
623}
624
626 DCHECK_EQ(StartNode(kSecondPairFirstNodeDestination),
627 StartNode(kSecondPairSecondNodeDestination));
628 DCHECK_EQ(StartNode(kSecondPairFirstNode),
629 StartNode(kFirstPairFirstNodeDestination));
630 DCHECK_EQ(StartNode(kSecondPairFirstNode),
631 StartNode(kFirstPairSecondNodeDestination));
632
633 if (StartNode(kFirstPairFirstNode) == StartNode(kSecondPairFirstNode)) {
634 SetNextBaseToIncrement(kSecondPairFirstNode);
635 return false;
636 }
637 // Through this method, <base>[X][Y] represent the <base> variable for the
638 // node Y of pair X. <base> is in node, prev, dest.
639 int64_t nodes[2][2];
640 int64_t prev[2][2];
641 int64_t dest[2][2];
642 nodes[0][0] = BaseNode(kFirstPairFirstNode);
643 nodes[1][0] = BaseNode(kSecondPairFirstNode);
644 if (nodes[1][0] <= nodes[0][0]) {
645 // Exchange is symmetric.
646 SetNextBaseToIncrement(kSecondPairFirstNode);
647 return false;
648 }
649 if (!GetPreviousAndSibling(nodes[0][0], &prev[0][0], &nodes[0][1],
650 &prev[0][1])) {
651 SetNextBaseToIncrement(kFirstPairFirstNode);
652 return false;
653 }
654 if (!GetPreviousAndSibling(nodes[1][0], &prev[1][0], &nodes[1][1],
655 &prev[1][1])) {
656 SetNextBaseToIncrement(kSecondPairFirstNode);
657 return false;
658 }
659
660 if (!LoadAndCheckDest(0, 0, kFirstPairFirstNodeDestination, nodes, dest)) {
661 SetNextBaseToIncrement(kFirstPairFirstNodeDestination);
662 return false;
663 }
664 if (!LoadAndCheckDest(0, 1, kFirstPairSecondNodeDestination, nodes, dest)) {
665 SetNextBaseToIncrement(kFirstPairSecondNodeDestination);
666 return false;
667 }
668 if (StartNode(kSecondPairFirstNodeDestination) !=
669 StartNode(kFirstPairFirstNode) ||
670 !LoadAndCheckDest(1, 0, kSecondPairFirstNodeDestination, nodes, dest)) {
671 SetNextBaseToIncrement(kSecondPairFirstNodeDestination);
672 return false;
673 }
674 if (!LoadAndCheckDest(1, 1, kSecondPairSecondNodeDestination, nodes, dest)) {
675 SetNextBaseToIncrement(kSecondPairSecondNodeDestination);
676 return false;
677 }
678
679 if (!MoveNode(0, 1, nodes, dest, prev)) {
680 SetNextBaseToIncrement(kFirstPairSecondNodeDestination);
681 return false;
682 }
683 if (!MoveNode(0, 0, nodes, dest, prev)) {
684 SetNextBaseToIncrement(kFirstPairSecondNodeDestination);
685 return false;
686 }
687 if (!MoveNode(1, 1, nodes, dest, prev)) {
688 return false;
689 }
690 if (!MoveNode(1, 0, nodes, dest, prev)) {
691 return false;
692 }
693 return true;
694}
695
696bool PairExchangeRelocateOperator::MoveNode(int pair, int node,
697 int64_t nodes[2][2],
698 int64_t dest[2][2],
699 int64_t prev[2][2]) {
700 if (!MoveChain(prev[pair][node], nodes[pair][node], dest[pair][node])) {
701 return false;
702 }
703 // Update the other pair if needed.
704 if (prev[1 - pair][0] == dest[pair][node]) {
705 prev[1 - pair][0] = nodes[pair][node];
706 }
707 if (prev[1 - pair][1] == dest[pair][node]) {
708 prev[1 - pair][1] = nodes[pair][node];
709 }
710 return true;
711}
712
713bool PairExchangeRelocateOperator::LoadAndCheckDest(int pair, int node,
714 int64_t base_node,
715 int64_t nodes[2][2],
716 int64_t dest[2][2]) const {
717 dest[pair][node] = BaseNode(base_node);
718 // A destination cannot be a node that will be moved.
719 return !(nodes[0][0] == dest[pair][node] || nodes[0][1] == dest[pair][node] ||
720 nodes[1][0] == dest[pair][node] || nodes[1][1] == dest[pair][node]);
721}
722
724 int64_t base_index) {
725 // Ensuring the destination of the first pair is on the route of the second.
726 // pair.
727 // Ensuring that destination of both nodes of a pair are on the same route.
728 return base_index == kFirstPairFirstNodeDestination ||
729 base_index == kFirstPairSecondNodeDestination ||
730 base_index == kSecondPairSecondNodeDestination;
731}
732
734 int base_index) {
735 if (base_index == kFirstPairSecondNodeDestination ||
736 base_index == kSecondPairSecondNodeDestination) {
737 return BaseNode(base_index - 1);
738 } else {
739 return StartNode(base_index);
740 }
741}
742
743bool PairExchangeRelocateOperator::GetPreviousAndSibling(
744 int64_t node, int64_t* previous, int64_t* sibling,
745 int64_t* sibling_previous) const {
746 if (IsPathStart(node)) return false;
747 *previous = Prev(node);
748 *sibling = GetActiveAlternativeSibling(node);
749 *sibling_previous = *sibling >= 0 ? Prev(*sibling) : -1;
750 return *sibling_previous >= 0;
751}
752
754 const std::vector<IntVar*>& vars, const std::vector<IntVar*>& path_vars,
755 const std::vector<PickupDeliveryPair>& pairs)
757 pairs_(pairs),
758 pair_index_(0),
759 first_index_(0),
760 second_index_(0),
761 number_of_nexts_(vars.size()),
762 ignore_path_vars_(path_vars.empty()) {
763 if (!ignore_path_vars_) {
764 AddVars(path_vars);
765 }
766}
767
769 Assignment* deltadelta) {
770 const int64_t kNoPath = -1;
771 CHECK(delta != nullptr);
772 while (true) {
773 RevertChanges(true);
774
775 if (pair_index_ >= pairs_.size()) return false;
776 const int64_t path =
777 ignore_path_vars_ ? 0LL : Value(first_active_ + number_of_nexts_);
778 const int64_t prev_first = prevs_[first_active_];
779 const int64_t next_first = Value(first_active_);
780 // Making current active "pickup" unperformed.
781 SetNext(first_active_, first_active_, kNoPath);
782 // Inserting "pickup" alternative at the same position.
783 const auto& [pickup_alternatives, delivery_alternatives] =
784 pairs_[pair_index_];
785 const int64_t insert_first = pickup_alternatives[first_index_];
786 SetNext(prev_first, insert_first, path);
787 SetNext(insert_first, next_first, path);
788 int64_t prev_second = prevs_[second_active_];
789 if (prev_second == first_active_) {
790 prev_second = insert_first;
791 }
792 DCHECK_EQ(path, ignore_path_vars_
793 ? int64_t{0}
794 : Value(second_active_ + number_of_nexts_));
795 const int64_t next_second = Value(second_active_);
796 // Making current active "delivery" unperformed.
797 SetNext(second_active_, second_active_, kNoPath);
798 // Inserting "delivery" alternative at the same position.
799 const int64_t insert_second = delivery_alternatives[second_index_];
800 SetNext(prev_second, insert_second, path);
801 SetNext(insert_second, next_second, path);
802 // Move to next "pickup/delivery" alternative.
803 ++second_index_;
804 if (second_index_ >= delivery_alternatives.size()) {
805 second_index_ = 0;
806 ++first_index_;
807 if (first_index_ >= pickup_alternatives.size()) {
808 first_index_ = 0;
809 while (true) {
810 ++pair_index_;
811 if (!UpdateActiveNodes()) break;
812 if (first_active_ != -1 && second_active_ != -1) {
813 break;
814 }
815 }
816 }
817 }
818
819 if (ApplyChanges(delta, deltadelta)) return true;
820 }
821 return false;
822}
823
825 prevs_.resize(number_of_nexts_, -1);
826 for (int index = 0; index < number_of_nexts_; ++index) {
827 const int64_t next = Value(index);
828 if (next >= prevs_.size()) prevs_.resize(next + 1, -1);
829 prevs_[next] = index;
830 }
831 pair_index_ = 0;
832 first_index_ = 0;
833 second_index_ = 0;
834 first_active_ = -1;
835 second_active_ = -1;
836 while (true) {
837 if (!UpdateActiveNodes()) break;
838 if (first_active_ != -1 && second_active_ != -1) {
839 break;
840 }
841 ++pair_index_;
842 }
843}
844
845bool SwapIndexPairOperator::UpdateActiveNodes() {
846 if (pair_index_ < pairs_.size()) {
847 const auto& [pickup_alternatives, delivery_alternatives] =
848 pairs_[pair_index_];
849 first_active_ = -1;
850 second_active_ = -1;
851 if (pickup_alternatives.size() == 1 && delivery_alternatives.size() == 1) {
852 // When there are no alternatives, the pair should be ignored whether
853 // there are active nodes or not.
854 return true;
855 }
856 for (const int64_t first : pickup_alternatives) {
857 if (Value(first) != first) {
858 first_active_ = first;
859 break;
860 }
861 }
862 for (const int64_t second : delivery_alternatives) {
863 if (Value(second) != second) {
864 second_active_ = second;
865 break;
866 }
867 }
868 return true;
869 }
870 return false;
871}
872
874 const std::vector<IntVar*>& vars,
875 const std::vector<IntVar*>& secondary_vars,
876 std::function<int(int64_t)> start_empty_path_class,
877 const std::vector<PickupDeliveryPair>& pairs)
878 : PathOperator(vars, secondary_vars, 1, true, false,
879 std::move(start_empty_path_class), nullptr),
880 inactive_node_(0) {
882}
883
885 Assignment* deltadelta) {
886 while (inactive_node_ < Size()) {
887 if (!IsInactive(inactive_node_) ||
890 ++inactive_node_;
891 } else {
892 return true;
893 }
894 }
895 return false;
896}
897
899 const int64_t base = BaseNode(0);
900 const int64_t next = Next(base);
901 const int64_t other = GetActiveAlternativeSibling(next);
902 if (other != -1) {
903 return MakeChainInactive(Prev(other), other) &&
904 MakeChainInactive(base, next) && MakeActive(inactive_node_, base);
905 }
906 return false;
907}
908
909void IndexPairSwapActiveOperator::OnNodeInitialization() {
911 for (int i = 0; i < Size(); ++i) {
912 if (IsInactive(i)) {
913 inactive_node_ = i;
914 return;
915 }
916 }
917 inactive_node_ = Size();
918}
919
921 const std::vector<IntVar*>& vars,
922 const std::vector<IntVar*>& secondary_vars,
923 std::function<int(int64_t)> start_empty_path_class,
924 int num_arcs_to_consider,
925 std::function<int64_t(int64_t, int64_t, int64_t)> arc_cost_for_path_start)
926 : PathOperator(vars, secondary_vars, 1, false, false,
927 std::move(start_empty_path_class), nullptr),
928 num_arcs_to_consider_(num_arcs_to_consider),
929 current_path_(0),
930 current_expensive_arc_indices_({-1, -1}),
931 arc_cost_for_path_start_(std::move(arc_cost_for_path_start)),
932 end_path_(0),
933 has_non_empty_paths_to_explore_(false) {
934 DCHECK_GE(num_arcs_to_consider_, 2);
935}
936
938 const int first_arc_index = current_expensive_arc_indices_.first;
939 const int second_arc_index = current_expensive_arc_indices_.second;
940 DCHECK_LE(0, first_arc_index);
941 DCHECK_LT(first_arc_index, second_arc_index);
942 DCHECK_LT(second_arc_index, most_expensive_arc_starts_and_ranks_.size());
943
944 const std::pair<int, int>& first_start_and_rank =
945 most_expensive_arc_starts_and_ranks_[first_arc_index];
946 const std::pair<int, int>& second_start_and_rank =
947 most_expensive_arc_starts_and_ranks_[second_arc_index];
948 if (first_start_and_rank.second < second_start_and_rank.second) {
949 return CheckChainValidity(first_start_and_rank.first,
950 second_start_and_rank.first, BaseNode(0)) &&
951 MoveChain(first_start_and_rank.first, second_start_and_rank.first,
952 BaseNode(0));
953 }
954 return CheckChainValidity(second_start_and_rank.first,
955 first_start_and_rank.first, BaseNode(0)) &&
956 MoveChain(second_start_and_rank.first, first_start_and_rank.first,
957 BaseNode(0));
958}
960 while (has_non_empty_paths_to_explore_) {
963 // Move on to the next expensive arcs on the same path.
964 if (IncrementCurrentArcIndices()) {
965 continue;
966 }
967 // Move on to the next non_empty path.
968 IncrementCurrentPath();
969 has_non_empty_paths_to_explore_ =
970 current_path_ != end_path_ &&
971 FindMostExpensiveChainsOnRemainingPaths();
972 } else {
973 return true;
974 }
975 }
976 return false;
977}
978
979void RelocateExpensiveChain::OnNodeInitialization() {
980 if (current_path_ >= path_starts().size()) {
981 // current_path_ was made empty by last move (and it was the last non-empty
982 // path), restart from 0.
983 current_path_ = 0;
984 }
985 end_path_ = current_path_;
986 has_non_empty_paths_to_explore_ = FindMostExpensiveChainsOnRemainingPaths();
987}
988
989void RelocateExpensiveChain::IncrementCurrentPath() {
990 const int num_paths = path_starts().size();
991 if (++current_path_ == num_paths) {
992 current_path_ = 0;
993 }
994}
995
996bool RelocateExpensiveChain::IncrementCurrentArcIndices() {
997 int& second_index = current_expensive_arc_indices_.second;
998 if (++second_index < most_expensive_arc_starts_and_ranks_.size()) {
999 return true;
1000 }
1001 int& first_index = current_expensive_arc_indices_.first;
1002 if (first_index + 2 < most_expensive_arc_starts_and_ranks_.size()) {
1003 first_index++;
1004 second_index = first_index + 1;
1005 return true;
1006 }
1007 return false;
1008}
1009
1010bool RelocateExpensiveChain::FindMostExpensiveChainsOnRemainingPaths() {
1011 do {
1013 num_arcs_to_consider_, path_starts()[current_path_],
1014 [this](int64_t i) { return OldNext(i); },
1015 [this](int64_t node) { return IsPathEnd(node); },
1016 arc_cost_for_path_start_, &most_expensive_arc_starts_and_ranks_,
1017 &current_expensive_arc_indices_)) {
1018 return true;
1019 }
1020 IncrementCurrentPath();
1021 } while (current_path_ != end_path_);
1022 return false;
1023}
1024
1026 int num_nodes, absl::Span<const PickupDeliveryPair> pairs)
1027 : is_pickup_node_(num_nodes, false),
1028 is_delivery_node_(num_nodes, false),
1029 pair_of_node_(num_nodes, -1) {
1030 for (int pair_index = 0; pair_index < pairs.size(); ++pair_index) {
1031 for (const int node : pairs[pair_index].pickup_alternatives) {
1032 is_pickup_node_[node] = true;
1033 pair_of_node_[node] = pair_index;
1034 }
1035 for (const int node : pairs[pair_index].delivery_alternatives) {
1036 is_delivery_node_[node] = true;
1037 pair_of_node_[node] = pair_index;
1038 }
1039 }
1040}
1041
1043 const std::vector<IntVar*>& vars,
1044 const std::vector<IntVar*>& secondary_vars,
1045 std::function<int(int64_t)> start_empty_path_class,
1046 std::function<const std::vector<int>&(int, int)> get_neighbors,
1047 const std::vector<PickupDeliveryPair>& pairs)
1048 : PathOperator(vars, secondary_vars,
1049 /*number_of_base_nodes=*/get_neighbors == nullptr ? 2 : 1,
1050 /*skip_locally_optimal_paths=*/true,
1051 /*accept_path_end_base=*/false,
1052 std::move(start_empty_path_class), std::move(get_neighbors)),
1053 pd_data_(number_of_nexts_, pairs) {
1054 opened_pairs_set_.resize(pairs.size(), false);
1055}
1056
1057void RelocateSubtrip::SetPath(absl::Span<const int64_t> path, int path_id) {
1058 for (int i = 1; i < path.size(); ++i) {
1059 SetNext(path[i - 1], path[i], path_id);
1060 }
1061}
1062
1063bool RelocateSubtrip::RelocateSubTripFromPickup(const int64_t chain_first_node,
1064 const int64_t insertion_node) {
1065 if (IsPathEnd(insertion_node)) return false;
1066 if (Prev(chain_first_node) == insertion_node)
1067 return false; // Skip null move.
1068
1069 int num_opened_pairs = 0;
1070 // Split chain into subtrip and rejected nodes.
1071 rejected_nodes_ = {Prev(chain_first_node)};
1072 subtrip_nodes_ = {insertion_node};
1073 int current = chain_first_node;
1074 do {
1075 if (current == insertion_node) {
1076 // opened_pairs_set_ must be all false when we leave this function.
1077 opened_pairs_set_.assign(opened_pairs_set_.size(), false);
1078 return false;
1079 }
1080 const int pair = pd_data_.GetPairOfNode(current);
1081 if (pd_data_.IsDeliveryNode(current) && !opened_pairs_set_[pair]) {
1082 rejected_nodes_.push_back(current);
1083 } else {
1084 subtrip_nodes_.push_back(current);
1085 if (pd_data_.IsPickupNode(current)) {
1086 ++num_opened_pairs;
1087 opened_pairs_set_[pair] = true;
1088 } else if (pd_data_.IsDeliveryNode(current)) {
1089 --num_opened_pairs;
1090 opened_pairs_set_[pair] = false;
1091 }
1092 }
1093 current = Next(current);
1094 } while (num_opened_pairs != 0 && !IsPathEnd(current));
1095 DCHECK_EQ(num_opened_pairs, 0);
1096 rejected_nodes_.push_back(current);
1097 subtrip_nodes_.push_back(Next(insertion_node));
1098
1099 // Set new paths.
1100 SetPath(rejected_nodes_, Path(chain_first_node));
1101 SetPath(subtrip_nodes_, Path(insertion_node));
1102 return true;
1103}
1104
1105bool RelocateSubtrip::RelocateSubTripFromDelivery(
1106 const int64_t chain_last_node, const int64_t insertion_node) {
1107 if (IsPathEnd(insertion_node)) return false;
1108
1109 // opened_pairs_set_ should be all false.
1110 DCHECK(std::none_of(opened_pairs_set_.begin(), opened_pairs_set_.end(),
1111 [](bool value) { return value; }));
1112 int num_opened_pairs = 0;
1113 // Split chain into subtrip and rejected nodes. Store nodes in reverse order.
1114 rejected_nodes_ = {Next(chain_last_node)};
1115 subtrip_nodes_ = {Next(insertion_node)};
1116 int current = chain_last_node;
1117 do {
1118 if (current == insertion_node) {
1119 opened_pairs_set_.assign(opened_pairs_set_.size(), false);
1120 return false;
1121 }
1122 const int pair = pd_data_.GetPairOfNode(current);
1123 if (pd_data_.IsPickupNode(current) && !opened_pairs_set_[pair]) {
1124 rejected_nodes_.push_back(current);
1125 } else {
1126 subtrip_nodes_.push_back(current);
1127 if (pd_data_.IsDeliveryNode(current)) {
1128 ++num_opened_pairs;
1129 opened_pairs_set_[pair] = true;
1130 } else if (pd_data_.IsPickupNode(current)) {
1131 --num_opened_pairs;
1132 opened_pairs_set_[pair] = false;
1133 }
1134 }
1135 current = Prev(current);
1136 } while (num_opened_pairs != 0 && !IsPathStart(current));
1137 DCHECK_EQ(num_opened_pairs, 0);
1138 if (current == insertion_node) return false; // Skip null move.
1139 rejected_nodes_.push_back(current);
1140 subtrip_nodes_.push_back(insertion_node);
1141
1142 // TODO(user): either remove those std::reverse() and adapt the loops
1143 // below, or refactor the loops into a function that also DCHECKs the path.
1144 std::reverse(rejected_nodes_.begin(), rejected_nodes_.end());
1145 std::reverse(subtrip_nodes_.begin(), subtrip_nodes_.end());
1146
1147 // Set new paths.
1148 SetPath(rejected_nodes_, Path(chain_last_node));
1149 SetPath(subtrip_nodes_, Path(insertion_node));
1150 return true;
1151}
1152
1154 const auto do_move = [this](int64_t node, int64_t insertion_node) {
1155 if (IsInactive(node)) return false;
1156 if (pd_data_.IsPickupNode(node)) {
1157 return RelocateSubTripFromPickup(node, insertion_node);
1158 } else if (pd_data_.IsDeliveryNode(node)) {
1159 return RelocateSubTripFromDelivery(node, insertion_node);
1160 } else {
1161 return false;
1162 }
1163 };
1164 return HasNeighbors()
1165 ? do_move(/*node=*/GetNeighborForBaseNode(0),
1166 /*insertion_node=*/BaseNode(0))
1167 : do_move(/*node=*/BaseNode(0), /*insertion_node=*/BaseNode(1));
1168}
1169
1171 const std::vector<IntVar*>& vars,
1172 const std::vector<IntVar*>& secondary_vars,
1173 std::function<int(int64_t)> start_empty_path_class,
1174 std::function<const std::vector<int>&(int, int)> get_neighbors,
1175 const std::vector<PickupDeliveryPair>& pairs)
1176 : PathOperator(vars, secondary_vars,
1177 /*number_of_base_nodes=*/get_neighbors == nullptr ? 2 : 1,
1178 /*skip_locally_optimal_paths=*/true,
1179 /*accept_path_end_base=*/false,
1180 std::move(start_empty_path_class), std::move(get_neighbors)),
1181 pd_data_(number_of_nexts_, pairs) {
1182 opened_pairs_set_.resize(pairs.size(), false);
1183}
1184
1185void ExchangeSubtrip::SetPath(const std::vector<int64_t>& path, int path_id) {
1186 for (int i = 1; i < path.size(); ++i) {
1187 SetNext(path[i - 1], path[i], path_id);
1188 }
1189}
1190
1191namespace {
1192bool VectorContains(absl::Span<const int64_t> values, int64_t target) {
1193 return std::find(values.begin(), values.end(), target) != values.end();
1194}
1195} // namespace
1196
1198 int64_t node0 = -1;
1199 int64_t node1 = -1;
1200 if (HasNeighbors()) {
1201 const int64_t node = BaseNode(0);
1202 const int64_t neighbor = GetNeighborForBaseNode(0);
1203 if (IsInactive(neighbor)) return false;
1204 if (pd_data_.IsDeliveryNode(node) &&
1205 pd_data_.IsDeliveryNode(Prev(neighbor))) {
1206 node0 = node;
1207 node1 = Prev(neighbor);
1208 } else if (pd_data_.IsPickupNode(neighbor) && !IsPathEnd(Next(node)) &&
1209 pd_data_.IsPickupNode(Next(node))) {
1210 node0 = Next(node);
1211 node1 = neighbor;
1212 } else {
1213 return false;
1214 }
1215 } else {
1216 node0 = BaseNode(0);
1217 node1 = BaseNode(1);
1218 }
1219
1220 if (pd_data_.GetPairOfNode(node0) == -1) return false;
1221 if (pd_data_.GetPairOfNode(node1) == -1) return false;
1222 // Break symmetry: a move generated from (node0, node1) is the
1223 // same as from (node1, node0): no need to do it twice.
1224 if (node0 >= node1) return false;
1225 rejects0_.clear();
1226 subtrip0_.clear();
1227 if (!ExtractChainsAndCheckCanonical(node0, &rejects0_, &subtrip0_)) {
1228 return false;
1229 }
1230 rejects1_.clear();
1231 subtrip1_.clear();
1232 if (!ExtractChainsAndCheckCanonical(node1, &rejects1_, &subtrip1_)) {
1233 return false;
1234 }
1235
1236 // If paths intersect, skip the move.
1237 if (HasNeighbors() || StartNode(0) == StartNode(1)) {
1238 if (VectorContains(rejects0_, subtrip1_.front())) return false;
1239 if (VectorContains(rejects1_, subtrip0_.front())) return false;
1240 if (VectorContains(subtrip0_, subtrip1_.front())) return false;
1241 if (VectorContains(subtrip1_, subtrip0_.front())) return false;
1242 }
1243
1244 // Assemble the new paths.
1245 path0_ = {Prev(subtrip0_.front())};
1246 path1_ = {Prev(subtrip1_.front())};
1247 const int64_t last0 = Next(subtrip0_.back());
1248 const int64_t last1 = Next(subtrip1_.back());
1249 const bool concatenated01 = last0 == subtrip1_.front();
1250 const bool concatenated10 = last1 == subtrip0_.front();
1251
1252 if (pd_data_.IsDeliveryNode(node0)) std::swap(subtrip1_, rejects0_);
1253 path0_.insert(path0_.end(), subtrip1_.begin(), subtrip1_.end());
1254 path0_.insert(path0_.end(), rejects0_.begin(), rejects0_.end());
1255 path0_.push_back(last0);
1256
1257 if (pd_data_.IsDeliveryNode(node1)) std::swap(subtrip0_, rejects1_);
1258 path1_.insert(path1_.end(), subtrip0_.begin(), subtrip0_.end());
1259 path1_.insert(path1_.end(), rejects1_.begin(), rejects1_.end());
1260 path1_.push_back(last1);
1261
1262 // When the trips are concatenated, bypass the regular extremities.
1263 if (concatenated01) {
1264 path0_.pop_back();
1265 path1_.front() = path0_.back();
1266 } else if (concatenated10) {
1267 path1_.pop_back();
1268 path0_.front() = path1_.back();
1269 }
1270
1271 // Change the paths. Since SetNext() modifies Path() values,
1272 // record path_id0 and path_id11 before calling SetPath();
1273 const int64_t path0_id = Path(node0);
1274 const int64_t path1_id = Path(node1);
1275 SetPath(path0_, path0_id);
1276 SetPath(path1_, path1_id);
1277 return true;
1278}
1279
1280bool ExchangeSubtrip::ExtractChainsAndCheckCanonical(
1281 int64_t base_node, std::vector<int64_t>* rejects,
1282 std::vector<int64_t>* subtrip) {
1283 const bool extracted =
1284 pd_data_.IsPickupNode(base_node)
1285 ? ExtractChainsFromPickup(base_node, rejects, subtrip)
1286 : ExtractChainsFromDelivery(base_node, rejects, subtrip);
1287 if (!extracted) return false;
1288 // Check canonicality.
1289 return !pd_data_.IsDeliveryNode(base_node) ||
1290 pd_data_.GetPairOfNode(subtrip->front()) !=
1291 pd_data_.GetPairOfNode(subtrip->back()) ||
1292 !rejects->empty();
1293}
1294
1295bool ExchangeSubtrip::ExtractChainsFromPickup(int64_t base_node,
1296 std::vector<int64_t>* rejects,
1297 std::vector<int64_t>* subtrip) {
1298 DCHECK(pd_data_.IsPickupNode(base_node));
1299 DCHECK(rejects->empty());
1300 DCHECK(subtrip->empty());
1301 // Iterate from base_node forwards while maintaining the set of opened pairs.
1302 // A pair is opened by a pickup, closed with the corresponding delivery.
1303 opened_pairs_set_.assign(opened_pairs_set_.size(), false);
1304 int num_opened_pairs = 0;
1305 int current = base_node;
1306 do {
1307 const int pair = pd_data_.GetPairOfNode(current);
1308 if (pd_data_.IsDeliveryNode(current) && !opened_pairs_set_[pair]) {
1309 rejects->push_back(current);
1310 } else {
1311 subtrip->push_back(current);
1312 if (pd_data_.IsPickupNode(current)) {
1313 ++num_opened_pairs;
1314 opened_pairs_set_[pair] = true;
1315 } else if (pd_data_.IsDeliveryNode(current)) {
1316 --num_opened_pairs;
1317 opened_pairs_set_[pair] = false;
1318 }
1319 }
1320 current = Next(current);
1321 } while (num_opened_pairs != 0 && !IsPathEnd(current));
1322 return num_opened_pairs == 0;
1323}
1324
1325bool ExchangeSubtrip::ExtractChainsFromDelivery(int64_t base_node,
1326 std::vector<int64_t>* rejects,
1327 std::vector<int64_t>* subtrip) {
1328 DCHECK(pd_data_.IsDeliveryNode(base_node));
1329 DCHECK(rejects->empty());
1330 DCHECK(subtrip->empty());
1331 // Iterate from base_node backwards while maintaining the set of opened pairs.
1332 // A pair is opened by a delivery, closed with the corresponding pickup.
1333 opened_pairs_set_.assign(opened_pairs_set_.size(), false);
1334 int num_opened_pairs = 0;
1335 int current = base_node;
1336 do {
1337 const int pair = pd_data_.GetPairOfNode(current);
1338 if (pd_data_.IsPickupNode(current) && !opened_pairs_set_[pair]) {
1339 rejects->push_back(current);
1340 } else {
1341 subtrip->push_back(current);
1342 if (pd_data_.IsDeliveryNode(current)) {
1343 ++num_opened_pairs;
1344 opened_pairs_set_[pair] = true;
1345 } else if (pd_data_.IsPickupNode(current)) {
1346 --num_opened_pairs;
1347 opened_pairs_set_[pair] = false;
1348 }
1349 }
1350 current = Prev(current);
1351 } while (num_opened_pairs != 0 && !IsPathStart(current));
1352 if (num_opened_pairs != 0) return false;
1353 std::reverse(rejects->begin(), rejects->end());
1354 std::reverse(subtrip->begin(), subtrip->end());
1355 return true;
1356}
1357
1358} // namespace operations_research
IntegerValue size
ExchangeSubtrip(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, std::function< const std::vector< int > &(int, int)> get_neighbors, const std::vector< PickupDeliveryPair > &pairs)
GroupPairAndRelocateOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, std::function< const std::vector< int > &(int, int)> get_neighbors, const std::vector< PickupDeliveryPair > &pairs)
bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta) override
IndexPairSwapActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, const std::vector< PickupDeliveryPair > &pairs)
void RevertChanges(bool change_was_incremental)
void AddVars(const std::vector< IntVar * > &vars)
IntVar * Var(int64_t index) const
Returns the variable of given index.
bool ApplyChanges(Assignment *delta, Assignment *deltadelta) const
LightPairRelocateOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, std::function< const std::vector< int > &(int, int)> get_neighbors, const std::vector< PickupDeliveryPair > &pairs, std::function< bool(int64_t)> force_lifo=nullptr)
virtual bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta)=0
int64_t GetBaseNodeRestartPosition(int base_index) override
MakePairActiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, const std::vector< PickupDeliveryPair > &pairs)
MakePairInactiveOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, const std::vector< PickupDeliveryPair > &pairs)
MakeRelocateNeighborsOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, std::function< const std::vector< int > &(int, int)> get_neighbors, RoutingTransitCallback2 arc_evaluator)
PairExchangeOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, std::function< const std::vector< int > &(int, int)> get_neighbors, const std::vector< PickupDeliveryPair > &pairs)
int64_t GetBaseNodeRestartPosition(int base_index) override
bool OnSamePathAsPreviousBase(int64_t base_index) override
PairExchangeRelocateOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, const std::vector< PickupDeliveryPair > &pairs)
int64_t GetBaseNodeRestartPosition(int base_index) override
PairRelocateOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, const std::vector< PickupDeliveryPair > &pairs)
int64_t Prev(int64_t node) const
Returns the node before node in the current delta.
bool MakeActive(int64_t node, int64_t destination)
Insert the inactive node after destination.
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
int GetNeighborForBaseNode(int64_t base_index) const
int64_t OldNext(int64_t node) const
bool MakeChainInactive(int64_t before_chain, int64_t chain_end)
bool IsInactive(int64_t node) const
Returns true if node is inactive.
bool SwapActiveAndInactive(int64_t active, int64_t inactive)
Replaces active by inactive in the current path, making active inactive.
void AddPairAlternativeSets(const std::vector< PairType > &pair_alternative_sets)
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
int64_t BaseSiblingAlternativeNode(int i) const
Returns the alternative node for the sibling of the ith base node.
int64_t BaseAlternativeNode(int i) const
Returns the alternative node for the ith base node.
const std::vector< int64_t > & path_starts() const
Returns the vector of path start nodes.
int64_t Path(int64_t node) const
int64_t GetActiveAlternativeSibling(int node) const
bool MoveChain(int64_t before_chain, int64_t chain_end, int64_t destination)
int64_t StartNode(int i) const
Returns the start node of the ith base node.
virtual void SetNextBaseToIncrement(int64_t base_index)
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 BaseNode(int i) const
Returns the ith base node of the operator.
PickupAndDeliveryData(int num_nodes, absl::Span< const PickupDeliveryPair > pairs)
bool MakeOneNeighbor() override
This method should not be overridden. Override MakeNeighbor() instead.
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)
RelocateSubtrip(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, std::function< int(int64_t)> start_empty_path_class, std::function< const std::vector< int > &(int, int)> get_neighbors, const std::vector< PickupDeliveryPair > &pairs)
void Set(IntegerType index)
Definition bitset.h:864
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)
SwapIndexPairOperator(const std::vector< IntVar * > &vars, const std::vector< IntVar * > &path_vars, const std::vector< PickupDeliveryPair > &pairs)
bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta) override
--— Base operator class for operators manipulating IntVars --—
Block * next
int64_t value
absl::Status status
Definition g_gurobi.cc:44
int index
In SWIG mode, we don't want anything besides these top-level includes.
int64_t CapAdd(int64_t x, int64_t y)
bool FindMostExpensiveArcsOnRoute(int num_arcs, int64_t start, const std::function< int64_t(int64_t)> &next_accessor, const std::function< bool(int64_t)> &is_end, const std::function< int64_t(int64_t, int64_t, int64_t)> &arc_cost_for_route_start, std::vector< std::pair< int64_t, int > > *most_expensive_arc_starts_and_ranks, std::pair< int, int > *first_expensive_arc_indices)
std::function< int64_t(int64_t, int64_t)> RoutingTransitCallback2
STL namespace.
bool Next()
int64_t delta
Definition resource.cc:1709
int nodes
static const int64_t kint64max
Definition types.h:32