Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
routing_insertion_lns.cc
Go to the documentation of this file.
1// Copyright 2010-2025 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 <memory>
20#include <optional>
21#include <utility>
22#include <vector>
23
24#include "absl/container/flat_hash_set.h"
25#include "absl/log/check.h"
32#include "ortools/util/bitset.h"
33
34namespace operations_research {
35
36// FilteredHeuristicLocalSearchOperator
37
39 std::unique_ptr<RoutingFilteredHeuristic> heuristic,
40 bool keep_inverse_values)
41 : IntVarLocalSearchOperator(heuristic->model()->Nexts(),
42 keep_inverse_values),
43 model_(heuristic->model()),
45 heuristic_(std::move(heuristic)),
46 consider_vehicle_vars_(!model_->CostsAreHomogeneousAcrossVehicles()) {
47 if (consider_vehicle_vars_) {
48 AddVars(model_->VehicleVars());
49 }
50}
51
53 while (IncrementPosition()) {
54 if (model_->CheckLimit()) {
55 // NOTE: Even though the limit is checked in the BuildSolutionFromRoutes()
56 // method of the heuristics, we still check it here to avoid calling
57 // IncrementPosition() and building a solution for every possible position
58 // if the time limit is reached.
59 return false;
60 }
61 // NOTE: No need to call RevertChanges() here as MakeChangeAndInsertNodes()
62 // will always return true if any change was made.
63 if (MakeChangesAndInsertNodes()) {
64 return true;
65 }
66 }
67 return false;
68}
69
70bool FilteredHeuristicLocalSearchOperator::MakeChangesAndInsertNodes() {
71 removed_nodes_.ResetAllToFalse();
72
73 // Note: next_accessor might be depending on the current state of the
74 // neighborhood. Do not make this class modify it without checking its
75 // derived classes.
76 const std::function<int64_t(int64_t)> next_accessor =
78 if (next_accessor == nullptr) {
79 return false;
80 }
81 model_->solver()->set_context(DebugString());
82 const Assignment* const result_assignment =
83 heuristic_->BuildSolutionFromRoutes(next_accessor);
84 model_->solver()->set_context("");
85
86 if (result_assignment == nullptr) {
87 return false;
88 }
89
90 bool has_change = false;
91 const std::vector<IntVarElement>& elements =
92 result_assignment->IntVarContainer().elements();
93 for (int vehicle = 0; vehicle < model_->vehicles(); vehicle++) {
94 int64_t node_index = model_->Start(vehicle);
95 while (!model_->IsEnd(node_index)) {
96 // NOTE: When building the solution in the heuristic, Next vars are added
97 // to the assignment at the position corresponding to their index.
98 const IntVarElement& node_element = elements[node_index];
99 DCHECK_EQ(node_element.Var(), model_->NextVar(node_index));
100
101 const int64_t new_node_value = node_element.Value();
102 DCHECK_NE(new_node_value, node_index);
103
104 const int64_t vehicle_var_index = VehicleVarIndex(node_index);
105 if (OldValue(node_index) != new_node_value ||
106 (consider_vehicle_vars_ && OldValue(vehicle_var_index) != vehicle)) {
107 has_change = true;
108 SetValue(node_index, new_node_value);
109 if (consider_vehicle_vars_) {
110 SetValue(vehicle_var_index, vehicle);
111 }
112 }
113 node_index = new_node_value;
114 }
115 }
116 // Check for newly unperformed nodes among the ones removed for insertion by
117 // the heuristic.
118 for (int64_t node : removed_nodes_.PositionsSetAtLeastOnce()) {
119 const IntVarElement& node_element = elements[node];
120 DCHECK_EQ(node_element.Var(), model_->NextVar(node));
121 if (node_element.Value() == node) {
122 DCHECK_NE(OldValue(node), node);
123 has_change = true;
124 SetValue(node, node);
125 if (consider_vehicle_vars_) {
126 const int64_t vehicle_var_index = VehicleVarIndex(node);
127 DCHECK_NE(OldValue(vehicle_var_index), -1);
128 SetValue(vehicle_var_index, -1);
129 }
130 }
131 }
132 return has_change;
133}
134
135// FilteredHeuristicPathLNSOperator
136
138 std::unique_ptr<RoutingFilteredHeuristic> heuristic)
139 : FilteredHeuristicLocalSearchOperator(std::move(heuristic)),
140 num_routes_(model_->vehicles()),
141 current_route_(0),
142 last_route_(0),
143 just_started_(false) {}
144
146 // NOTE: We set last_route_ to current_route_ here to make sure all routes
147 // are scanned in GetFirstNonEmptyRouteAfterCurrentRoute().
148 last_route_ = current_route_;
149 if (RouteIsEmpty(current_route_)) {
150 current_route_ = GetFirstNonEmptyRouteAfterCurrentRoute();
151 }
152 just_started_ = true;
153}
154
156 if (just_started_) {
157 just_started_ = false;
158 // If current_route_ is empty or is the only non-empty route, then we don't
159 // create a new neighbor with this operator as it would result in running
160 // a first solution heuristic with all the nodes.
161 return !RouteIsEmpty(current_route_) &&
162 GetFirstNonEmptyRouteAfterCurrentRoute() != last_route_;
163 }
164 current_route_ = GetFirstNonEmptyRouteAfterCurrentRoute();
165 return current_route_ != last_route_;
166}
167
168bool FilteredHeuristicPathLNSOperator::RouteIsEmpty(int route) const {
169 return model_->IsEnd(OldValue(model_->Start(route)));
170}
171
172int FilteredHeuristicPathLNSOperator::GetFirstNonEmptyRouteAfterCurrentRoute()
173 const {
174 int route = GetNextRoute(current_route_);
175 while (route != last_route_ && RouteIsEmpty(route)) {
176 route = GetNextRoute(route);
177 }
178 return route;
179}
180
181std::function<int64_t(int64_t)>
183 const int64_t start_node = model_->Start(current_route_);
184 const int64_t end_node = model_->End(current_route_);
185
186 int64_t node = Value(start_node);
187 while (node != end_node) {
188 removed_nodes_.Set(node);
189 node = Value(node);
190 }
191
192 return [this, start_node, end_node](int64_t node) {
193 if (node == start_node) return end_node;
194 return Value(node);
195 };
196}
197
198// RelocatePathAndHeuristicInsertUnperformedOperator
199
202 std::unique_ptr<RoutingFilteredHeuristic> heuristic)
203 : FilteredHeuristicLocalSearchOperator(std::move(heuristic)),
204 route_to_relocate_index_(0),
205 empty_route_index_(0),
206 just_started_(false) {}
207
209 has_unperformed_nodes_ = false;
210 last_node_on_route_.resize(model_->vehicles());
211 routes_to_relocate_.clear();
212 empty_routes_.clear();
213 std::vector<bool> empty_vehicle_of_vehicle_class_added(
215 for (int64_t node = 0; node < model_->Size(); node++) {
216 const int64_t next = OldValue(node);
217 if (next == node) {
218 has_unperformed_nodes_ = true;
219 continue;
220 }
221 if (model_->IsEnd(next)) {
222 last_node_on_route_[model_->VehicleIndex(next)] = node;
223 }
224 }
225
226 for (int vehicle = 0; vehicle < model_->vehicles(); vehicle++) {
227 const int64_t next = OldValue(model_->Start(vehicle));
228 if (!model_->IsEnd(next)) {
229 routes_to_relocate_.push_back(vehicle);
230 continue;
231 }
232 const int vehicle_class =
233 model_->GetVehicleClassIndexOfVehicle(vehicle).value();
234 if (!empty_vehicle_of_vehicle_class_added[vehicle_class]) {
235 empty_routes_.push_back(vehicle);
236 empty_vehicle_of_vehicle_class_added[vehicle_class] = true;
237 }
238 }
239
240 if (empty_route_index_ >= empty_routes_.size()) {
241 empty_route_index_ = 0;
242 }
243 if (route_to_relocate_index_ >= routes_to_relocate_.size()) {
244 route_to_relocate_index_ = 0;
245 }
246 last_empty_route_index_ = empty_route_index_;
247 last_route_to_relocate_index_ = route_to_relocate_index_;
248
249 just_started_ = true;
250}
251
253 if (!has_unperformed_nodes_ || empty_routes_.empty() ||
254 routes_to_relocate_.empty()) {
255 return false;
256 }
257 if (just_started_) {
258 just_started_ = false;
259 return true;
260 }
261 return IncrementRoutes();
262}
263
264bool RelocatePathAndHeuristicInsertUnperformedOperator::IncrementRoutes() {
265 ++empty_route_index_ %= empty_routes_.size();
266 if (empty_route_index_ != last_empty_route_index_) {
267 return true;
268 }
269 ++route_to_relocate_index_ %= routes_to_relocate_.size();
270 return route_to_relocate_index_ != last_route_to_relocate_index_;
271}
272
273std::function<int64_t(int64_t)>
276 const int empty_route = empty_routes_[empty_route_index_];
277 const int relocated_route = routes_to_relocate_[route_to_relocate_index_];
278 if (model_->GetVehicleClassIndexOfVehicle(empty_route) ==
279 model_->GetVehicleClassIndexOfVehicle(relocated_route)) {
280 // Don't try to relocate the route to an empty vehicle of the same class.
281 return nullptr;
282 }
283
284 const int64_t empty_start_node = model_->Start(empty_route);
285 const int64_t empty_end_node = model_->End(empty_route);
286
287 const int64_t relocated_route_start = model_->Start(relocated_route);
288 const int64_t first_relocated_node = OldValue(relocated_route_start);
289 const int64_t last_relocated_node = last_node_on_route_[relocated_route];
290 const int64_t relocated_route_end = model_->End(relocated_route);
291
292 return [this, empty_start_node, empty_end_node, first_relocated_node,
293 last_relocated_node, relocated_route_start,
294 relocated_route_end](int64_t node) {
295 if (node == relocated_route_start) return relocated_route_end;
296 if (node == empty_start_node) return first_relocated_node;
297 if (node == last_relocated_node) return empty_end_node;
298 return Value(node);
299 };
300}
301
302// FilteredHeuristicCloseNodesLNSOperator
303
305 std::unique_ptr<RoutingFilteredHeuristic> heuristic, int num_close_nodes)
306 : FilteredHeuristicLocalSearchOperator(std::move(heuristic),
307 /*keep_inverse_values*/ true),
308 pickup_delivery_pairs_(model_->GetPickupAndDeliveryPairs()),
309 current_node_(0),
310 last_node_(0),
311 just_started_(false),
312 initialized_(false),
313 close_nodes_(model_->Size()),
314 num_close_nodes_(num_close_nodes),
315 new_nexts_(model_->Size()),
316 changed_nexts_(model_->Size()),
317 new_prevs_(model_->Size()),
318 changed_prevs_(model_->Size()) {}
319
320void FilteredHeuristicCloseNodesLNSOperator::Initialize() {
321 if (initialized_) return;
322 initialized_ = true;
323 const int64_t size = model_->Size();
324 const int64_t max_num_neighbors =
325 std::max<int64_t>(0, size - 1 - model_->vehicles());
326 const int64_t num_closest_neighbors =
327 std::min<int64_t>(num_close_nodes_, max_num_neighbors);
328 DCHECK_GE(num_closest_neighbors, 0);
329
330 if (num_closest_neighbors == 0) return;
331
332 const int64_t num_cost_classes = model_->GetCostClassesCount();
333
334 for (int64_t node = 0; node < size; node++) {
335 if (model_->IsStart(node) || model_->IsEnd(node)) continue;
336
337 std::vector<std::pair</*cost*/ double, /*node*/ int64_t>>
338 costed_after_nodes;
339 costed_after_nodes.reserve(size);
340 for (int64_t after_node = 0; after_node < size; after_node++) {
341 if (model_->IsStart(after_node) || model_->IsEnd(after_node) ||
342 after_node == node) {
343 continue;
344 }
345 double total_cost = 0.0;
346 // NOTE: We don't consider the 'always-zero' cost class when searching for
347 // closest neighbors.
348 for (int cost_class = 1; cost_class < num_cost_classes; cost_class++) {
349 total_cost += model_->GetArcCostForClass(node, after_node, cost_class);
350 }
351 costed_after_nodes.emplace_back(total_cost, after_node);
352 }
353
354 std::nth_element(costed_after_nodes.begin(),
355 costed_after_nodes.begin() + num_closest_neighbors - 1,
356 costed_after_nodes.end());
357 std::vector<int64_t>& neighbors = close_nodes_[node];
358 neighbors.reserve(num_closest_neighbors);
359 for (int index = 0; index < num_closest_neighbors; index++) {
360 neighbors.push_back(costed_after_nodes[index].second);
361 }
362 }
363}
364
366 Initialize();
367 last_node_ = current_node_;
368 just_started_ = true;
369}
370
372 DCHECK(initialized_);
373 if (just_started_) {
374 just_started_ = false;
375 return true;
376 }
377 ++current_node_ %= model_->Size();
378 return current_node_ != last_node_;
379}
380
381void FilteredHeuristicCloseNodesLNSOperator::RemoveNode(int64_t node) {
382 DCHECK(!model_->IsEnd(node) && !model_->IsStart(node));
383 DCHECK_NE(Value(node), node);
384 DCHECK(IsActive(node));
385
386 removed_nodes_.Set(node);
387 const int64_t prev = Prev(node);
388 const int64_t next = Next(node);
389 changed_nexts_.Set(prev);
390 new_nexts_[prev] = next;
391 if (next < model_->Size()) {
392 changed_prevs_.Set(next);
393 new_prevs_[next] = prev;
394 }
395}
396
397void FilteredHeuristicCloseNodesLNSOperator::RemoveNodeAndActiveSibling(
398 int64_t node) {
399 if (!IsActive(node)) return;
400 RemoveNode(node);
401
402 if (const std::optional<int64_t> sibling_node =
403 model_->GetFirstMatchingPickupDeliverySibling(
404 node,
405 [this](int64_t node) {
406 return IsActive(node) && !model_->IsStart(node) &&
407 !model_->IsEnd(node);
408 });
409 sibling_node.has_value()) {
410 RemoveNode(sibling_node.value());
411 }
412}
413
414std::function<int64_t(int64_t)>
416 DCHECK(initialized_);
417 if (model_->IsStart(current_node_)) {
418 return nullptr;
419 }
420 DCHECK(!model_->IsEnd(current_node_));
421
422 changed_nexts_.ResetAllToFalse();
423 changed_prevs_.ResetAllToFalse();
424
425 RemoveNodeAndActiveSibling(current_node_);
426
427 for (int64_t neighbor : close_nodes_[current_node_]) {
428 RemoveNodeAndActiveSibling(neighbor);
429 }
430
431 return [this](int64_t node) { return Next(node); };
432}
433
434// FilteredHeuristicExpensiveChainLNSOperator
435
438 std::unique_ptr<RoutingFilteredHeuristic> heuristic,
439 int num_arcs_to_consider,
440 std::function<int64_t(int64_t, int64_t, int64_t)>
441 arc_cost_for_route_start)
442 : FilteredHeuristicLocalSearchOperator(std::move(heuristic)),
443 current_route_(0),
444 last_route_(0),
445 num_arcs_to_consider_(num_arcs_to_consider),
446 current_expensive_arc_indices_({-1, -1}),
447 arc_cost_for_route_start_(std::move(arc_cost_for_route_start)),
448 just_started_(false) {
449 DCHECK_GE(num_arcs_to_consider_, 2);
450}
451
453 last_route_ = current_route_;
454 just_started_ = true;
455}
456
458 if (just_started_) {
459 just_started_ = false;
460 return FindMostExpensiveChainsOnRemainingRoutes();
461 }
462
463 if (IncrementCurrentArcIndices()) return true;
464
465 return IncrementRoute() && FindMostExpensiveChainsOnRemainingRoutes();
466}
467
468std::function<int64_t(int64_t)>
470 const int first_arc_index = current_expensive_arc_indices_.first;
471 const int second_arc_index = current_expensive_arc_indices_.second;
472 DCHECK_LE(0, first_arc_index);
473 DCHECK_LT(first_arc_index, second_arc_index);
474 DCHECK_LT(second_arc_index, most_expensive_arc_starts_and_ranks_.size());
475
476 const std::pair<int, int>& first_start_and_rank =
477 most_expensive_arc_starts_and_ranks_[first_arc_index];
478 const std::pair<int, int>& second_start_and_rank =
479 most_expensive_arc_starts_and_ranks_[second_arc_index];
480 int64_t before_chain, after_chain;
481 if (first_start_and_rank.second < second_start_and_rank.second) {
482 before_chain = first_start_and_rank.first;
483 after_chain = OldValue(second_start_and_rank.first);
484 } else {
485 before_chain = second_start_and_rank.first;
486 after_chain = OldValue(first_start_and_rank.first);
487 }
488
489 int node = Value(before_chain);
490 while (node != after_chain) {
491 removed_nodes_.Set(node);
492 node = Value(node);
493 }
494
495 return [this, before_chain, after_chain](int64_t node) {
496 if (node == before_chain) return after_chain;
497 return OldValue(node);
498 };
499}
500
501bool FilteredHeuristicExpensiveChainLNSOperator::IncrementRoute() {
502 ++current_route_ %= model_->vehicles();
503 return current_route_ != last_route_;
504}
505
506bool FilteredHeuristicExpensiveChainLNSOperator::IncrementCurrentArcIndices() {
507 int& second_index = current_expensive_arc_indices_.second;
508 if (++second_index < most_expensive_arc_starts_and_ranks_.size()) {
509 return true;
510 }
511 int& first_index = current_expensive_arc_indices_.first;
512 if (first_index + 2 < most_expensive_arc_starts_and_ranks_.size()) {
513 first_index++;
514 second_index = first_index + 1;
515 return true;
516 }
517 return false;
518}
519
520bool FilteredHeuristicExpensiveChainLNSOperator::
521 FindMostExpensiveChainsOnRemainingRoutes() {
522 do {
524 num_arcs_to_consider_, model_->Start(current_route_),
525 [this](int64_t i) { return OldValue(i); },
526 [this](int64_t node) { return model_->IsEnd(node); },
527 arc_cost_for_route_start_, &most_expensive_arc_starts_and_ranks_,
528 &current_expensive_arc_indices_)) {
529 return true;
530 }
531 } while (IncrementRoute());
532
533 return false;
534}
535// RelocateVisitTypeOperator
536
538 std::unique_ptr<RoutingFilteredHeuristic> heuristic)
539 : FilteredHeuristicLocalSearchOperator(std::move(heuristic),
540 /*keep_inverse_values*/ true),
541 current_visit_type_component_index_(0),
542 last_visit_type_component_index_(0),
543 empty_route_index_(0),
544 new_nexts_(model_->Size()),
545 changed_nexts_(model_->Size()),
546 new_prevs_(model_->Size()),
547 changed_prevs_(model_->Size()),
548 just_started_(false) {}
549
551 const std::vector<std::vector<int>>& visit_type_components =
553 while (current_visit_type_component_index_ < visit_type_components.size() &&
554 visit_type_components[current_visit_type_component_index_].empty()) {
555 ++current_visit_type_component_index_;
556 }
557 last_visit_type_component_index_ = current_visit_type_component_index_;
558 empty_routes_.clear();
559 std::vector<bool> empty_vehicle_of_vehicle_class_added(
561 for (int vehicle = 0; vehicle < model_->vehicles(); vehicle++) {
562 if (!model_->IsEnd(OldValue(model_->Start(vehicle)))) {
563 continue;
564 }
565 const int vehicle_class =
566 model_->GetVehicleClassIndexOfVehicle(vehicle).value();
567 if (!empty_vehicle_of_vehicle_class_added[vehicle_class]) {
568 empty_routes_.push_back(vehicle);
569 empty_vehicle_of_vehicle_class_added[vehicle_class] = true;
570 }
571 }
572 if (empty_route_index_ >= empty_routes_.size()) {
573 empty_route_index_ = 0;
574 }
575 last_empty_route_index_ = empty_route_index_;
576 just_started_ = true;
577}
578
580 const std::vector<std::vector<int>>& visit_type_components =
581 model_->GetVisitTypeComponents();
582 if (visit_type_components.empty() || empty_routes_.empty()) return false;
583 if (just_started_) {
584 just_started_ = false;
585 return true;
586 }
587 ++empty_route_index_ %= empty_routes_.size();
588 if (empty_route_index_ != last_empty_route_index_) return true;
589 do {
590 ++current_visit_type_component_index_ %= visit_type_components.size();
591 } while (current_visit_type_component_index_ !=
592 last_visit_type_component_index_ &&
593 visit_type_components[current_visit_type_component_index_].empty());
594 return current_visit_type_component_index_ !=
595 last_visit_type_component_index_;
596}
597
598void RelocateVisitTypeOperator::RemoveNode(int64_t node) {
599 DCHECK(!model_->IsEnd(node) && !model_->IsStart(node));
600 if (Value(node) == node || removed_nodes_[node]) return;
601 removed_nodes_.Set(node);
602 const int64_t prev =
603 changed_prevs_[node] ? new_prevs_[node] : InverseValue(node);
604 const int64_t next = changed_nexts_[node] ? new_nexts_[node] : Value(node);
605 changed_nexts_.Set(prev);
606 new_nexts_[prev] = next;
607 if (next < model_->Size()) {
608 changed_prevs_.Set(next);
609 new_prevs_[next] = prev;
610 }
611}
612
613std::function<int64_t(int64_t)>
615 changed_nexts_.ResetAllToFalse();
616 changed_prevs_.ResetAllToFalse();
617 const std::vector<std::vector<int>>& visit_type_components =
618 model_->GetVisitTypeComponents();
619 if (visit_type_components.empty() ||
620 visit_type_components[current_visit_type_component_index_].empty()) {
621 return nullptr;
622 }
623
624 absl::flat_hash_set<int64_t> visited_types;
625 const std::vector<PickupDeliveryPair>& pairs =
626 model_->GetPickupAndDeliveryPairs();
627 for (const int type :
628 visit_type_components[current_visit_type_component_index_]) {
629 visited_types.insert(type);
630 for (int64_t pair_index : model_->GetPairIndicesOfType(type)) {
631 const PickupDeliveryPair& pair = pairs[pair_index];
632 for (int64_t pickup : pair.pickup_alternatives) RemoveNode(pickup);
633 for (int64_t delivery : pair.delivery_alternatives) RemoveNode(delivery);
634 }
635 for (int64_t node : model_->GetSingleNodesOfType(type)) RemoveNode(node);
636 }
637 // Initiate a new route on an empty vehicle. Picking a "root" type node/pair
638 // to do so. The rationale is to incentivize insertion algorithms to use this
639 // new vehicle, especially in the case where vehicles have fixed costs. By
640 // taking a root node/pair which does not depend on other nodes, the
641 // likelihood of having an initial route which is feasible is higher.
642 const int empty_route = empty_routes_[empty_route_index_];
643 const int64_t empty_start_node = model_->Start(empty_route);
644 const int64_t empty_end_node = model_->End(empty_route);
645 for (const std::vector<int>& sorted_types :
646 model_->GetTopologicallySortedVisitTypes()) {
647 for (const int type : sorted_types) {
648 if (!visited_types.contains(type)) continue;
649 const std::vector<int>& pair_indices = model_->GetPairIndicesOfType(type);
650 if (!pair_indices.empty()) {
651 const int pair_index = pair_indices.front();
652 // TODO(user): Add support for multiple pickup/delivery
653 // alternatives.
654 const int64_t pickup = pairs[pair_index].pickup_alternatives[0];
655 const int64_t delivery = pairs[pair_index].delivery_alternatives[0];
656 return [this, empty_start_node, empty_end_node, pickup,
657 delivery](int64_t node) {
658 if (node == empty_start_node) return pickup;
659 if (node == pickup) return delivery;
660 if (node == delivery) return empty_end_node;
661 return changed_nexts_[node] ? new_nexts_[node] : OldValue(node);
662 };
663 }
664 const std::vector<int>& single_nodes = model_->GetSingleNodesOfType(type);
665 if (!single_nodes.empty()) {
666 const int64_t single_node = single_nodes.front();
667 return [this, empty_start_node, empty_end_node,
668 single_node](int64_t node) {
669 if (node == empty_start_node) return single_node;
670 if (node == single_node) return empty_end_node;
671 return changed_nexts_[node] ? new_nexts_[node] : OldValue(node);
672 };
673 }
674 }
675 }
676 return nullptr;
677}
678
679} // namespace operations_research
virtual std::string DebugString() const
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)
SparseBitset removed_nodes_
Keeps track of removed nodes when making a neighbor.
virtual std::function< int64_t(int64_t)> SetupNextAccessorForNeighbor()=0
FilteredHeuristicLocalSearchOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic, bool keep_inverse_values=false)
FilteredHeuristicPathLNSOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic)
void SetValue(int64_t index, int64_t value)
void AddVars(const std::vector< IntVar * > &vars)
IntVarLocalSearchOperator(const std::vector< IntVar * > &vars, bool keep_inverse_values=false)
virtual bool MakeOneNeighbor()
MakeNextNeighbor() in a subclass of IntVarLocalSearchOperator.
RelocatePathAndHeuristicInsertUnperformedOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic)
RelocateVisitTypeOperator(std::unique_ptr< RoutingFilteredHeuristic > heuristic)
int64_t Size() const
Returns the number of next variables in the model.
Definition routing.h:2104
int64_t Start(int vehicle) const
Definition routing.h:1817
int64_t GetArcCostForClass(int64_t from_index, int64_t to_index, int64_t cost_class_index) const
Definition routing.cc:4308
int GetVehicleClassesCount() const
Returns the number of different vehicle classes in the model.
Definition routing.h:1962
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
Definition routing.h:1937
bool IsStart(int64_t index) const
Returns true if 'index' represents the first node of a route.
Definition routing.h:1821
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
Definition routing.h:1823
int vehicles() const
Returns the number of vehicle routes in the model.
Definition routing.h:2102
bool CheckLimit(absl::Duration offset=absl::ZeroDuration())
Definition routing.h:2066
int VehicleIndex(int64_t index) const
Definition routing.h:1826
const std::vector< std::vector< int > > & GetVisitTypeComponents() const
Definition routing.h:1134
OR-Tools root namespace.
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)
STL namespace.
bool Next()
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...