OR-Tools  9.1
routing_search.cc
Go to the documentation of this file.
1 // Copyright 2010-2021 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 // Implementation of all classes related to routing and search.
15 // This includes decision builders, local search neighborhood operators
16 // and local search filters.
17 // TODO(user): Move all existing routing search code here.
18 
20 
21 #include <algorithm>
22 #include <cmath>
23 #include <cstdint>
24 #include <deque>
25 #include <functional>
26 #include <iterator>
27 #include <limits>
28 #include <memory>
29 #include <numeric>
30 #include <queue>
31 #include <set>
32 #include <string>
33 #include <tuple>
34 #include <utility>
35 #include <vector>
36 
37 #include "absl/container/flat_hash_map.h"
38 #include "absl/container/flat_hash_set.h"
39 #include "absl/flags/flag.h"
40 #include "absl/memory/memory.h"
41 #include "absl/meta/type_traits.h"
42 #include "absl/strings/str_cat.h"
43 #include "absl/strings/string_view.h"
47 #include "ortools/base/logging.h"
48 #include "ortools/base/macros.h"
49 #include "ortools/base/map_util.h"
50 #include "ortools/base/stl_util.h"
57 #include "ortools/util/bitset.h"
60 
61 namespace operations_research {
62 class LocalSearchPhaseParameters;
63 } // namespace operations_research
64 
65 ABSL_FLAG(bool, routing_shift_insertion_cost_by_penalty, true,
66  "Shift insertion costs by the penalty of the inserted node(s).");
67 
68 ABSL_FLAG(int64_t, sweep_sectors, 1,
69  "The number of sectors the space is divided into before it is sweeped"
70  " by the ray.");
71 
72 namespace operations_research {
73 
74 // --- VehicleTypeCurator ---
75 
76 void VehicleTypeCurator::Reset(const std::function<bool(int)>& store_vehicle) {
77  const std::vector<std::set<VehicleClassEntry>>& all_vehicle_classes_per_type =
78  vehicle_type_container_->sorted_vehicle_classes_per_type;
79  sorted_vehicle_classes_per_type_.resize(all_vehicle_classes_per_type.size());
80  const std::vector<std::deque<int>>& all_vehicles_per_class =
81  vehicle_type_container_->vehicles_per_vehicle_class;
82  vehicles_per_vehicle_class_.resize(all_vehicles_per_class.size());
83 
84  for (int type = 0; type < all_vehicle_classes_per_type.size(); type++) {
85  std::set<VehicleClassEntry>& stored_class_entries =
86  sorted_vehicle_classes_per_type_[type];
87  stored_class_entries.clear();
88  for (VehicleClassEntry class_entry : all_vehicle_classes_per_type[type]) {
89  const int vehicle_class = class_entry.vehicle_class;
90  std::vector<int>& stored_vehicles =
91  vehicles_per_vehicle_class_[vehicle_class];
92  stored_vehicles.clear();
93  for (int vehicle : all_vehicles_per_class[vehicle_class]) {
94  if (store_vehicle(vehicle)) {
95  stored_vehicles.push_back(vehicle);
96  }
97  }
98  if (!stored_vehicles.empty()) {
99  stored_class_entries.insert(class_entry);
100  }
101  }
102  }
103 }
104 
106  const std::function<bool(int)>& remove_vehicle) {
107  for (std::set<VehicleClassEntry>& class_entries :
108  sorted_vehicle_classes_per_type_) {
109  auto class_entry_it = class_entries.begin();
110  while (class_entry_it != class_entries.end()) {
111  const int vehicle_class = class_entry_it->vehicle_class;
112  std::vector<int>& vehicles = vehicles_per_vehicle_class_[vehicle_class];
113  vehicles.erase(std::remove_if(vehicles.begin(), vehicles.end(),
114  [&remove_vehicle](int vehicle) {
115  return remove_vehicle(vehicle);
116  }),
117  vehicles.end());
118  if (vehicles.empty()) {
119  class_entry_it = class_entries.erase(class_entry_it);
120  } else {
121  class_entry_it++;
122  }
123  }
124  }
125 }
126 
128  int type, std::function<bool(int)> vehicle_is_compatible,
129  std::function<bool(int)> stop_and_return_vehicle) {
130  std::set<VehicleTypeCurator::VehicleClassEntry>& sorted_classes =
131  sorted_vehicle_classes_per_type_[type];
132  auto vehicle_class_it = sorted_classes.begin();
133 
134  while (vehicle_class_it != sorted_classes.end()) {
135  const int vehicle_class = vehicle_class_it->vehicle_class;
136  std::vector<int>& vehicles = vehicles_per_vehicle_class_[vehicle_class];
137  DCHECK(!vehicles.empty());
138 
139  for (auto vehicle_it = vehicles.begin(); vehicle_it != vehicles.end();
140  vehicle_it++) {
141  const int vehicle = *vehicle_it;
142  if (vehicle_is_compatible(vehicle)) {
143  vehicles.erase(vehicle_it);
144  if (vehicles.empty()) {
145  sorted_classes.erase(vehicle_class_it);
146  }
147  return {vehicle, -1};
148  }
149  if (stop_and_return_vehicle(vehicle)) {
150  return {-1, vehicle};
151  }
152  }
153  // If no compatible vehicle was found in this class, move on to the next
154  // vehicle class.
155  vehicle_class_it++;
156  }
157  // No compatible vehicle of the given type was found and the stopping
158  // condition wasn't met.
159  return {-1, -1};
160 }
161 
162 // - Models with pickup/deliveries or node precedences are best handled by
163 // PARALLEL_CHEAPEST_INSERTION.
164 // - As of January 2018, models with single nodes and at least one node with
165 // only one allowed vehicle are better solved by PATH_MOST_CONSTRAINED_ARC.
166 // - In all other cases, PATH_CHEAPEST_ARC is used.
167 // TODO(user): Make this smarter.
169  bool has_pickup_deliveries, bool has_node_precedences,
170  bool has_single_vehicle_node) {
171  if (has_pickup_deliveries || has_node_precedences) {
173  }
174  if (has_single_vehicle_node) {
176  }
178 }
179 
180 // --- First solution decision builder ---
181 
182 // IntVarFilteredDecisionBuilder
183 
185  std::unique_ptr<IntVarFilteredHeuristic> heuristic)
186  : heuristic_(std::move(heuristic)) {}
187 
189  Assignment* const assignment = heuristic_->BuildSolution();
190  if (assignment != nullptr) {
191  VLOG(2) << "Number of decisions: " << heuristic_->number_of_decisions();
192  VLOG(2) << "Number of rejected decisions: "
193  << heuristic_->number_of_rejects();
194  assignment->Restore();
195  } else {
196  solver->Fail();
197  }
198  return nullptr;
199 }
200 
202  return heuristic_->number_of_decisions();
203 }
204 
206  return heuristic_->number_of_rejects();
207 }
208 
210  return absl::StrCat("IntVarFilteredDecisionBuilder(",
211  heuristic_->DebugString(), ")");
212 }
213 
214 // --- First solution heuristics ---
215 
216 // IntVarFilteredHeuristic
217 
219  Solver* solver, const std::vector<IntVar*>& vars,
220  LocalSearchFilterManager* filter_manager)
221  : assignment_(solver->MakeAssignment()),
222  solver_(solver),
223  vars_(vars),
224  delta_(solver->MakeAssignment()),
225  is_in_delta_(vars_.size(), false),
226  empty_(solver->MakeAssignment()),
227  filter_manager_(filter_manager),
228  number_of_decisions_(0),
229  number_of_rejects_(0) {
230  assignment_->MutableIntVarContainer()->Resize(vars_.size());
231  delta_indices_.reserve(vars_.size());
232 }
233 
235  number_of_decisions_ = 0;
236  number_of_rejects_ = 0;
237  // Wiping assignment when starting a new search.
239  assignment_->MutableIntVarContainer()->Resize(vars_.size());
240  delta_->MutableIntVarContainer()->Clear();
242 }
243 
245  ResetSolution();
246  if (!InitializeSolution()) {
247  return nullptr;
248  }
250  if (BuildSolutionInternal()) {
251  return assignment_;
252  }
253  return nullptr;
254 }
255 
257  const std::function<int64_t(int64_t)>& next_accessor) {
258  ResetSolution();
260  // NOTE: We don't need to clear or pre-set the two following vectors as the
261  // for loop below will set all elements.
262  start_chain_ends_.resize(model()->vehicles());
263  end_chain_starts_.resize(model()->vehicles());
264 
265  for (int v = 0; v < model_->vehicles(); v++) {
266  int64_t node = model_->Start(v);
267  while (!model_->IsEnd(node)) {
268  const int64_t next = next_accessor(node);
269  DCHECK_NE(next, node);
270  SetValue(node, next);
271  SetVehicleIndex(node, v);
272  node = next;
273  }
274  // We relax all routes from start to end, so routes can now be extended
275  // by inserting nodes between the start and end.
276  start_chain_ends_[v] = model()->Start(v);
277  end_chain_starts_[v] = model()->End(v);
278  }
279  if (!Commit()) {
280  return nullptr;
281  }
283  if (BuildSolutionInternal()) {
284  return assignment_;
285  }
286  return nullptr;
287 }
288 
290  ++number_of_decisions_;
291  const bool accept = FilterAccept();
292  if (accept) {
293  const Assignment::IntContainer& delta_container = delta_->IntVarContainer();
294  const int delta_size = delta_container.Size();
295  Assignment::IntContainer* const container =
297  for (int i = 0; i < delta_size; ++i) {
298  const IntVarElement& delta_element = delta_container.Element(i);
299  IntVar* const var = delta_element.Var();
300  DCHECK_EQ(var, vars_[delta_indices_[i]]);
301  container->AddAtPosition(var, delta_indices_[i])
302  ->SetValue(delta_element.Value());
303  }
305  } else {
306  ++number_of_rejects_;
307  }
308  // Reset is_in_delta to all false.
309  for (const int delta_index : delta_indices_) {
310  is_in_delta_[delta_index] = false;
311  }
312  delta_->Clear();
313  delta_indices_.clear();
314  return accept;
315 }
316 
318  if (filter_manager_) filter_manager_->Synchronize(assignment_, delta_);
319 }
320 
321 bool IntVarFilteredHeuristic::FilterAccept() {
322  if (!filter_manager_) return true;
323  LocalSearchMonitor* const monitor = solver_->GetLocalSearchMonitor();
324  return filter_manager_->Accept(monitor, delta_, empty_,
327 }
328 
329 // RoutingFilteredHeuristic
330 
332  RoutingModel* model, LocalSearchFilterManager* filter_manager)
333  : IntVarFilteredHeuristic(model->solver(), model->Nexts(), filter_manager),
334  model_(model) {}
335 
336 bool RoutingFilteredHeuristic::InitializeSolution() {
337  // Find the chains of nodes (when nodes have their "Next" value bound in the
338  // current solution, it forms a link in a chain). Eventually, starts[end]
339  // will contain the index of the first node of the chain ending at node 'end'
340  // and ends[start] will be the last node of the chain starting at node
341  // 'start'. Values of starts[node] and ends[node] for other nodes is used
342  // for intermediary computations and do not necessarily reflect actual chain
343  // starts and ends.
344 
345  // Start by adding partial start chains to current assignment.
346  start_chain_ends_.clear();
347  start_chain_ends_.resize(model()->vehicles(), -1);
348  end_chain_starts_.clear();
349  end_chain_starts_.resize(model()->vehicles(), -1);
350 
352  for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
353  int64_t node = model()->Start(vehicle);
354  while (!model()->IsEnd(node) && Var(node)->Bound()) {
355  const int64_t next = Var(node)->Min();
356  SetValue(node, next);
357  SetVehicleIndex(node, vehicle);
358  node = next;
359  }
360  start_chain_ends_[vehicle] = node;
361  }
362 
363  std::vector<int64_t> starts(Size() + model()->vehicles(), -1);
364  std::vector<int64_t> ends(Size() + model()->vehicles(), -1);
365  for (int node = 0; node < Size() + model()->vehicles(); ++node) {
366  // Each node starts as a singleton chain.
367  starts[node] = node;
368  ends[node] = node;
369  }
370  std::vector<bool> touched(Size(), false);
371  for (int node = 0; node < Size(); ++node) {
372  int current = node;
373  while (!model()->IsEnd(current) && !touched[current]) {
374  touched[current] = true;
375  IntVar* const next_var = Var(current);
376  if (next_var->Bound()) {
377  current = next_var->Value();
378  }
379  }
380  // Merge the sub-chain starting from 'node' and ending at 'current' with
381  // the existing sub-chain starting at 'current'.
382  starts[ends[current]] = starts[node];
383  ends[starts[node]] = ends[current];
384  }
385 
386  // Set each route to be the concatenation of the chain at its starts and the
387  // chain at its end, without nodes in between.
388  for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
389  end_chain_starts_[vehicle] = starts[model()->End(vehicle)];
390  int64_t node = start_chain_ends_[vehicle];
391  if (!model()->IsEnd(node)) {
392  int64_t next = starts[model()->End(vehicle)];
393  SetValue(node, next);
394  SetVehicleIndex(node, vehicle);
395  node = next;
396  while (!model()->IsEnd(node)) {
397  next = Var(node)->Min();
398  SetValue(node, next);
399  SetVehicleIndex(node, vehicle);
400  node = next;
401  }
402  }
403  }
404 
405  if (!Commit()) {
407  return false;
408  }
409  return true;
410 }
411 
414  node, 1, [this, node](int alternate) {
415  if (node != alternate && !Contains(alternate)) {
416  SetValue(alternate, alternate);
417  }
418  });
419 }
420 
422  for (int index = 0; index < Size(); ++index) {
423  if (!Contains(index)) {
424  SetValue(index, index);
425  }
426  }
427 }
428 
430  std::vector<bool> to_make_unperformed(Size(), false);
431  for (const auto& [pickups, deliveries] :
432  model()->GetPickupAndDeliveryPairs()) {
433  int64_t performed_pickup = -1;
434  for (int64_t pickup : pickups) {
435  if (Contains(pickup) && Value(pickup) != pickup) {
436  performed_pickup = pickup;
437  break;
438  }
439  }
440  int64_t performed_delivery = -1;
441  for (int64_t delivery : deliveries) {
442  if (Contains(delivery) && Value(delivery) != delivery) {
443  performed_delivery = delivery;
444  break;
445  }
446  }
447  if ((performed_pickup == -1) != (performed_delivery == -1)) {
448  if (performed_pickup != -1) {
449  to_make_unperformed[performed_pickup] = true;
450  }
451  if (performed_delivery != -1) {
452  to_make_unperformed[performed_delivery] = true;
453  }
454  }
455  }
456  for (int index = 0; index < Size(); ++index) {
457  if (to_make_unperformed[index] || !Contains(index)) continue;
458  int64_t next = Value(index);
459  while (next < Size() && to_make_unperformed[next]) {
460  const int64_t next_of_next = Value(next);
461  SetValue(index, next_of_next);
462  SetValue(next, next);
463  next = next_of_next;
464  }
465  }
466 }
467 
468 // CheapestInsertionFilteredHeuristic
469 
472  std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
473  std::function<int64_t(int64_t)> penalty_evaluator,
474  LocalSearchFilterManager* filter_manager)
475  : RoutingFilteredHeuristic(model, filter_manager),
476  evaluator_(std::move(evaluator)),
477  penalty_evaluator_(std::move(penalty_evaluator)) {}
478 
479 std::vector<std::vector<CheapestInsertionFilteredHeuristic::StartEndValue>>
481  const std::vector<int>& vehicles) {
482  std::vector<std::vector<StartEndValue>> start_end_distances_per_node(
483  model()->Size());
484 
485  for (int node = 0; node < model()->Size(); node++) {
486  if (Contains(node)) continue;
487  std::vector<StartEndValue>& start_end_distances =
488  start_end_distances_per_node[node];
489 
490  for (const int vehicle : vehicles) {
491  const int64_t start = model()->Start(vehicle);
492  const int64_t end = model()->End(vehicle);
493 
494  // We compute the distance of node to the start/end nodes of the route.
495  const int64_t distance =
496  CapAdd(model()->GetArcCostForVehicle(start, node, vehicle),
497  model()->GetArcCostForVehicle(node, end, vehicle));
498  start_end_distances.push_back({distance, vehicle});
499  }
500  // Sort the distances for the node to all start/ends of available vehicles
501  // in decreasing order.
502  std::sort(start_end_distances.begin(), start_end_distances.end(),
503  [](const StartEndValue& first, const StartEndValue& second) {
504  return second < first;
505  });
506  }
507  return start_end_distances_per_node;
508 }
509 
510 template <class Queue>
512  std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
513  Queue* priority_queue) {
514  const int num_nodes = model()->Size();
515  DCHECK_EQ(start_end_distances_per_node->size(), num_nodes);
516 
517  for (int node = 0; node < num_nodes; node++) {
518  if (Contains(node)) continue;
519  std::vector<StartEndValue>& start_end_distances =
520  (*start_end_distances_per_node)[node];
521  if (start_end_distances.empty()) {
522  continue;
523  }
524  // Put the best StartEndValue for this node in the priority queue.
525  const StartEndValue& start_end_value = start_end_distances.back();
526  priority_queue->push(std::make_pair(start_end_value, node));
527  start_end_distances.pop_back();
528  }
529 }
530 
532  int64_t predecessor,
533  int64_t successor) {
534  SetValue(predecessor, node);
535  SetValue(node, successor);
537 }
538 
540  int64_t node_to_insert, int64_t start, int64_t next_after_start,
541  int64_t vehicle, std::vector<ValuedPosition>* valued_positions) {
542  CHECK(valued_positions != nullptr);
543  int64_t insert_after = start;
544  while (!model()->IsEnd(insert_after)) {
545  const int64_t insert_before =
546  (insert_after == start) ? next_after_start : Value(insert_after);
547  valued_positions->push_back(std::make_pair(
548  GetInsertionCostForNodeAtPosition(node_to_insert, insert_after,
549  insert_before, vehicle),
550  insert_after));
551  insert_after = insert_before;
552  }
553 }
554 
556  int64_t node_to_insert, int64_t insert_after, int64_t insert_before,
557  int vehicle) const {
558  return CapSub(CapAdd(evaluator_(insert_after, node_to_insert, vehicle),
559  evaluator_(node_to_insert, insert_before, vehicle)),
560  evaluator_(insert_after, insert_before, vehicle));
561 }
562 
564  int64_t node_to_insert) const {
565  if (penalty_evaluator_ != nullptr) {
566  return penalty_evaluator_(node_to_insert);
567  }
569 }
570 
571 namespace {
572 template <class T>
573 void SortAndExtractPairSeconds(std::vector<std::pair<int64_t, T>>* pairs,
574  std::vector<T>* sorted_seconds) {
575  CHECK(pairs != nullptr);
576  CHECK(sorted_seconds != nullptr);
577  std::sort(pairs->begin(), pairs->end());
578  sorted_seconds->reserve(pairs->size());
579  for (const std::pair<int64_t, T>& p : *pairs) {
580  sorted_seconds->push_back(p.second);
581  }
582 }
583 } // namespace
584 
585 // Priority queue entries used by global cheapest insertion heuristic.
586 
587 // Entry in priority queue containing the insertion positions of a node pair.
589  public:
592  : heap_index_(-1),
593  value_(std::numeric_limits<int64_t>::max()),
594  pickup_to_insert_(pickup_to_insert),
595  pickup_insert_after_(pickup_insert_after),
596  delivery_to_insert_(delivery_to_insert),
597  delivery_insert_after_(delivery_insert_after),
598  vehicle_(vehicle) {}
599  // Note: for compatibility reasons, comparator follows tie-breaking rules used
600  // in the first version of GlobalCheapestInsertion.
601  bool operator<(const PairEntry& other) const {
602  // We first compare by value, then we favor insertions (vehicle != -1).
603  // The rest of the tie-breaking is done with std::tie.
604  if (value_ != other.value_) {
605  return value_ > other.value_;
606  }
607  if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
608  return vehicle_ == -1;
609  }
610  return std::tie(pickup_insert_after_, pickup_to_insert_,
611  delivery_insert_after_, delivery_to_insert_, vehicle_) >
612  std::tie(other.pickup_insert_after_, other.pickup_to_insert_,
613  other.delivery_insert_after_, other.delivery_to_insert_,
614  other.vehicle_);
615  }
616  void SetHeapIndex(int h) { heap_index_ = h; }
617  int GetHeapIndex() const { return heap_index_; }
618  int64_t value() const { return value_; }
619  void set_value(int64_t value) { value_ = value; }
620  int pickup_to_insert() const { return pickup_to_insert_; }
621  int pickup_insert_after() const { return pickup_insert_after_; }
623  pickup_insert_after_ = pickup_insert_after;
624  }
625  int delivery_to_insert() const { return delivery_to_insert_; }
626  int delivery_insert_after() const { return delivery_insert_after_; }
627  int vehicle() const { return vehicle_; }
628  void set_vehicle(int vehicle) { vehicle_ = vehicle; }
629 
630  private:
631  int heap_index_;
632  int64_t value_;
633  const int pickup_to_insert_;
634  int pickup_insert_after_;
635  const int delivery_to_insert_;
636  const int delivery_insert_after_;
637  int vehicle_;
638 };
639 
640 // Entry in priority queue containing the insertion position of a node.
642  public:
644  : heap_index_(-1),
645  value_(std::numeric_limits<int64_t>::max()),
646  node_to_insert_(node_to_insert),
647  insert_after_(insert_after),
648  vehicle_(vehicle) {}
649  bool operator<(const NodeEntry& other) const {
650  // See PairEntry::operator<(), above. This one is similar.
651  if (value_ != other.value_) {
652  return value_ > other.value_;
653  }
654  if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
655  return vehicle_ == -1;
656  }
657  return std::tie(insert_after_, node_to_insert_, vehicle_) >
658  std::tie(other.insert_after_, other.node_to_insert_, other.vehicle_);
659  }
660  void SetHeapIndex(int h) { heap_index_ = h; }
661  int GetHeapIndex() const { return heap_index_; }
662  int64_t value() const { return value_; }
663  void set_value(int64_t value) { value_ = value; }
664  int node_to_insert() const { return node_to_insert_; }
665  int insert_after() const { return insert_after_; }
666  void set_insert_after(int insert_after) { insert_after_ = insert_after; }
667  int vehicle() const { return vehicle_; }
668  void set_vehicle(int vehicle) { vehicle_ = vehicle; }
669 
670  private:
671  int heap_index_;
672  int64_t value_;
673  const int node_to_insert_;
674  int insert_after_;
675  int vehicle_;
676 };
677 
678 // GlobalCheapestInsertionFilteredHeuristic
679 
683  std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
684  std::function<int64_t(int64_t)> penalty_evaluator,
685  LocalSearchFilterManager* filter_manager,
687  : CheapestInsertionFilteredHeuristic(model, std::move(evaluator),
688  std::move(penalty_evaluator),
689  filter_manager),
690  gci_params_(parameters),
691  node_index_to_vehicle_(model->Size(), -1),
692  empty_vehicle_type_curator_(nullptr) {
693  CHECK_GT(gci_params_.neighbors_ratio, 0);
694  CHECK_LE(gci_params_.neighbors_ratio, 1);
695  CHECK_GE(gci_params_.min_neighbors, 1);
696 
697  if (NumNeighbors() >= NumNonStartEndNodes() - 1) {
698  // All nodes are neighbors, so we set the neighbors_ratio to 1 to avoid
699  // unnecessary computations in the code.
700  gci_params_.neighbors_ratio = 1;
701  }
702 
703  if (gci_params_.neighbors_ratio == 1) {
704  gci_params_.use_neighbors_ratio_for_initialization = false;
705  all_nodes_.resize(model->Size());
706  std::iota(all_nodes_.begin(), all_nodes_.end(), 0);
707  }
708 }
709 
710 void GlobalCheapestInsertionFilteredHeuristic::ComputeNeighborhoods() {
711  if (gci_params_.neighbors_ratio == 1 ||
712  !node_index_to_neighbors_by_cost_class_.empty()) {
713  // Neighborhood computations not needed or already done.
714  return;
715  }
716 
717  // TODO(user): Refactor the neighborhood computations in RoutingModel.
718  const int64_t num_neighbors = NumNeighbors();
719  // If num_neighbors was greater or equal num_non_start_end_nodes - 1,
720  // gci_params_.neighbors_ratio should have been set to 1.
721  DCHECK_LT(num_neighbors, NumNonStartEndNodes() - 1);
722 
723  const RoutingModel& routing_model = *model();
724  const int64_t size = routing_model.Size();
725  node_index_to_neighbors_by_cost_class_.resize(size);
726  const int num_cost_classes = routing_model.GetCostClassesCount();
727  for (int64_t node_index = 0; node_index < size; node_index++) {
728  node_index_to_neighbors_by_cost_class_[node_index].resize(num_cost_classes);
729  for (int cc = 0; cc < num_cost_classes; cc++) {
730  node_index_to_neighbors_by_cost_class_[node_index][cc] =
731  absl::make_unique<SparseBitset<int64_t>>(size);
732  }
733  }
734 
735  for (int64_t node_index = 0; node_index < size; ++node_index) {
736  DCHECK(!routing_model.IsEnd(node_index));
737  if (routing_model.IsStart(node_index)) {
738  // We don't compute neighbors for vehicle starts: all nodes are considered
739  // neighbors for a vehicle start.
740  continue;
741  }
742 
743  // TODO(user): Use the model's IndexNeighborFinder when available.
744  for (int cost_class = 0; cost_class < num_cost_classes; cost_class++) {
745  if (!routing_model.HasVehicleWithCostClassIndex(
746  RoutingCostClassIndex(cost_class))) {
747  // No vehicle with this cost class, avoid unnecessary computations.
748  continue;
749  }
750  std::vector<std::pair</*cost*/ int64_t, /*node*/ int64_t>>
751  costed_after_nodes;
752  costed_after_nodes.reserve(size);
753  for (int after_node = 0; after_node < size; ++after_node) {
754  if (after_node != node_index && !routing_model.IsStart(after_node)) {
755  costed_after_nodes.push_back(
756  std::make_pair(routing_model.GetArcCostForClass(
757  node_index, after_node, cost_class),
758  after_node));
759  }
760  }
761  std::nth_element(costed_after_nodes.begin(),
762  costed_after_nodes.begin() + num_neighbors - 1,
763  costed_after_nodes.end());
764  costed_after_nodes.resize(num_neighbors);
765 
766  for (auto [cost, neighbor] : costed_after_nodes) {
767  node_index_to_neighbors_by_cost_class_[node_index][cost_class]->Set(
768  neighbor);
769 
770  // Add reverse neighborhood.
771  DCHECK(!routing_model.IsEnd(neighbor) &&
772  !routing_model.IsStart(neighbor));
773  node_index_to_neighbors_by_cost_class_[neighbor][cost_class]->Set(
774  node_index);
775  }
776  // Add all vehicle starts as neighbors to this node and vice-versa.
777  for (int vehicle = 0; vehicle < routing_model.vehicles(); vehicle++) {
778  const int64_t vehicle_start = routing_model.Start(vehicle);
779  node_index_to_neighbors_by_cost_class_[node_index][cost_class]->Set(
780  vehicle_start);
781  node_index_to_neighbors_by_cost_class_[vehicle_start][cost_class]->Set(
782  node_index);
783  }
784  }
785  }
786 }
787 
788 bool GlobalCheapestInsertionFilteredHeuristic::IsNeighborForCostClass(
789  int cost_class, int64_t node_index, int64_t neighbor_index) const {
790  return gci_params_.neighbors_ratio == 1 ||
791  (*node_index_to_neighbors_by_cost_class_[node_index]
792  [cost_class])[neighbor_index];
793 }
794 
795 bool GlobalCheapestInsertionFilteredHeuristic::CheckVehicleIndices() const {
796  std::vector<bool> node_is_visited(model()->Size(), -1);
797  for (int v = 0; v < model()->vehicles(); v++) {
798  for (int node = model()->Start(v); !model()->IsEnd(node);
799  node = Value(node)) {
800  if (node_index_to_vehicle_[node] != v) {
801  return false;
802  }
803  node_is_visited[node] = true;
804  }
805  }
806 
807  for (int node = 0; node < model()->Size(); node++) {
808  if (!node_is_visited[node] && node_index_to_vehicle_[node] != -1) {
809  return false;
810  }
811  }
812 
813  return true;
814 }
815 
817  ComputeNeighborhoods();
818  if (empty_vehicle_type_curator_ == nullptr) {
819  empty_vehicle_type_curator_ = absl::make_unique<VehicleTypeCurator>(
820  model()->GetVehicleTypeContainer());
821  }
822  // Store all empty vehicles in the empty_vehicle_type_curator_.
823  empty_vehicle_type_curator_->Reset(
824  [this](int vehicle) { return VehicleIsEmpty(vehicle); });
825  // Insert partially inserted pairs.
826  const RoutingModel::IndexPairs& pickup_delivery_pairs =
828  std::vector<int> pairs_to_insert;
829  absl::flat_hash_map<int, std::vector<int>> vehicle_to_pair_nodes;
830  for (int index = 0; index < pickup_delivery_pairs.size(); index++) {
831  const RoutingModel::IndexPair& index_pair = pickup_delivery_pairs[index];
832  int pickup_vehicle = -1;
833  for (int64_t pickup : index_pair.first) {
834  if (Contains(pickup)) {
835  pickup_vehicle = node_index_to_vehicle_[pickup];
836  break;
837  }
838  }
839  int delivery_vehicle = -1;
840  for (int64_t delivery : index_pair.second) {
841  if (Contains(delivery)) {
842  delivery_vehicle = node_index_to_vehicle_[delivery];
843  break;
844  }
845  }
846  if (pickup_vehicle < 0 && delivery_vehicle < 0) {
847  pairs_to_insert.push_back(index);
848  }
849  if (pickup_vehicle >= 0 && delivery_vehicle < 0) {
850  std::vector<int>& pair_nodes = vehicle_to_pair_nodes[pickup_vehicle];
851  for (int64_t delivery : index_pair.second) {
852  pair_nodes.push_back(delivery);
853  }
854  }
855  if (pickup_vehicle < 0 && delivery_vehicle >= 0) {
856  std::vector<int>& pair_nodes = vehicle_to_pair_nodes[delivery_vehicle];
857  for (int64_t pickup : index_pair.first) {
858  pair_nodes.push_back(pickup);
859  }
860  }
861  }
862  for (const auto& vehicle_and_nodes : vehicle_to_pair_nodes) {
863  InsertNodesOnRoutes(vehicle_and_nodes.second, {vehicle_and_nodes.first});
864  }
865 
866  InsertPairsAndNodesByRequirementTopologicalOrder();
867 
868  // TODO(user): Adapt the pair insertions to also support seed and
869  // sequential insertion.
870  InsertPairs(pairs_to_insert);
871  std::vector<int> nodes;
872  for (int node = 0; node < model()->Size(); ++node) {
873  if (!Contains(node) && model()->GetPickupIndexPairs(node).empty() &&
874  model()->GetDeliveryIndexPairs(node).empty()) {
875  nodes.push_back(node);
876  }
877  }
878  InsertFarthestNodesAsSeeds();
879  if (gci_params_.is_sequential) {
880  SequentialInsertNodes(nodes);
881  } else {
882  InsertNodesOnRoutes(nodes, {});
883  }
885  DCHECK(CheckVehicleIndices());
886  return Commit();
887 }
888 
889 void GlobalCheapestInsertionFilteredHeuristic::
890  InsertPairsAndNodesByRequirementTopologicalOrder() {
891  for (const std::vector<int>& types :
892  model()->GetTopologicallySortedVisitTypes()) {
893  for (int type : types) {
894  InsertPairs(model()->GetPairIndicesOfType(type));
895  InsertNodesOnRoutes(model()->GetSingleNodesOfType(type), {});
896  }
897  }
898 }
899 
900 void GlobalCheapestInsertionFilteredHeuristic::InsertPairs(
901  const std::vector<int>& pair_indices) {
902  AdjustablePriorityQueue<PairEntry> priority_queue;
903  std::vector<PairEntries> pickup_to_entries;
904  std::vector<PairEntries> delivery_to_entries;
905  InitializePairPositions(pair_indices, &priority_queue, &pickup_to_entries,
906  &delivery_to_entries);
907  while (!priority_queue.IsEmpty()) {
908  if (StopSearch()) {
909  for (PairEntry* const entry : *priority_queue.Raw()) {
910  delete entry;
911  }
912  return;
913  }
914  PairEntry* const entry = priority_queue.Top();
915  const int64_t pickup = entry->pickup_to_insert();
916  const int64_t delivery = entry->delivery_to_insert();
917  if (Contains(pickup) || Contains(delivery)) {
918  DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
919  &delivery_to_entries);
920  continue;
921  }
922 
923  const int entry_vehicle = entry->vehicle();
924  if (entry_vehicle == -1) {
925  // Pair is unperformed.
926  SetValue(pickup, pickup);
927  SetValue(delivery, delivery);
928  if (!Commit()) {
929  DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
930  &delivery_to_entries);
931  }
932  continue;
933  }
934 
935  // Pair is performed.
936  if (InsertPairEntryUsingEmptyVehicleTypeCurator(
937  pair_indices, entry, &priority_queue, &pickup_to_entries,
938  &delivery_to_entries)) {
939  // The entry corresponded to an insertion on an empty vehicle, which was
940  // handled by the method.
941  continue;
942  }
943 
944  const int64_t pickup_insert_after = entry->pickup_insert_after();
945  const int64_t pickup_insert_before = Value(pickup_insert_after);
946  InsertBetween(pickup, pickup_insert_after, pickup_insert_before);
947 
948  const int64_t delivery_insert_after = entry->delivery_insert_after();
949  const int64_t delivery_insert_before = (delivery_insert_after == pickup)
950  ? pickup_insert_before
951  : Value(delivery_insert_after);
952  InsertBetween(delivery, delivery_insert_after, delivery_insert_before);
953  if (Commit()) {
954  UpdateAfterPairInsertion(pair_indices, entry_vehicle, pickup,
955  pickup_insert_after, delivery,
956  delivery_insert_after, &priority_queue,
957  &pickup_to_entries, &delivery_to_entries);
958  } else {
959  DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
960  &delivery_to_entries);
961  }
962  }
963 }
964 
965 bool GlobalCheapestInsertionFilteredHeuristic::
966  InsertPairEntryUsingEmptyVehicleTypeCurator(
967  const std::vector<int>& pair_indices,
968  GlobalCheapestInsertionFilteredHeuristic::PairEntry* const pair_entry,
970  GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
971  priority_queue,
972  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
973  pickup_to_entries,
974  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
975  delivery_to_entries) {
976  const int entry_vehicle = pair_entry->vehicle();
977  if (entry_vehicle == -1 || !VehicleIsEmpty(entry_vehicle)) {
978  return false;
979  }
980 
981  // Trying to insert on an empty vehicle.
982  // As we only have one pair_entry per empty vehicle type, we try inserting on
983  // all vehicles of this type with the same fixed cost, as they all have the
984  // same insertion value.
985  const int64_t pickup = pair_entry->pickup_to_insert();
986  const int64_t delivery = pair_entry->delivery_to_insert();
987  const int64_t entry_fixed_cost =
988  model()->GetFixedCostOfVehicle(entry_vehicle);
989  auto vehicle_is_compatible = [this, entry_fixed_cost, pickup,
990  delivery](int vehicle) {
991  if (model()->GetFixedCostOfVehicle(vehicle) != entry_fixed_cost) {
992  return false;
993  }
994  // NOTE: Only empty vehicles should be in the vehicle_curator_.
995  DCHECK(VehicleIsEmpty(vehicle));
996  const int64_t end = model()->End(vehicle);
997  InsertBetween(pickup, model()->Start(vehicle), end);
998  InsertBetween(delivery, pickup, end);
999  return Commit();
1000  };
1001  // Since the vehicles of the same type are sorted by increasing fixed
1002  // cost by the curator, we can stop as soon as a vehicle with a fixed cost
1003  // higher than the entry_fixed_cost is found which is empty, and adapt the
1004  // pair entry with this new vehicle.
1005  auto stop_and_return_vehicle = [this, entry_fixed_cost](int vehicle) {
1006  return model()->GetFixedCostOfVehicle(vehicle) > entry_fixed_cost;
1007  };
1008  const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1009  empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1010  empty_vehicle_type_curator_->Type(entry_vehicle),
1011  vehicle_is_compatible, stop_and_return_vehicle);
1012  if (compatible_vehicle >= 0) {
1013  // The pair was inserted on this vehicle.
1014  const int64_t vehicle_start = model()->Start(compatible_vehicle);
1015  const int num_previous_vehicle_entries =
1016  pickup_to_entries->at(vehicle_start).size() +
1017  delivery_to_entries->at(vehicle_start).size();
1018  UpdateAfterPairInsertion(pair_indices, compatible_vehicle, pickup,
1019  vehicle_start, delivery, pickup, priority_queue,
1020  pickup_to_entries, delivery_to_entries);
1021  if (compatible_vehicle != entry_vehicle) {
1022  // The pair was inserted on another empty vehicle of the same type
1023  // and same fixed cost as entry_vehicle.
1024  // Since this vehicle is empty and has the same fixed cost as the
1025  // entry_vehicle, it shouldn't be the representative of empty vehicles
1026  // for any pickup/delivery in the priority queue.
1027  DCHECK_EQ(num_previous_vehicle_entries, 0);
1028  return true;
1029  }
1030  // The previously unused entry_vehicle is now used, so we use the next
1031  // available vehicle of the same type to compute and store insertions on
1032  // empty vehicles.
1033  const int new_empty_vehicle =
1034  empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1035  empty_vehicle_type_curator_->Type(compatible_vehicle));
1036 
1037  if (new_empty_vehicle >= 0) {
1038  DCHECK(VehicleIsEmpty(new_empty_vehicle));
1039  // Add node entries after this vehicle start for uninserted pairs which
1040  // don't have entries on this empty vehicle.
1041  UpdatePairPositions(pair_indices, new_empty_vehicle,
1042  model()->Start(new_empty_vehicle), priority_queue,
1043  pickup_to_entries, delivery_to_entries);
1044  }
1045  } else if (next_fixed_cost_empty_vehicle >= 0) {
1046  // Could not insert on this vehicle or any other vehicle of the same type
1047  // with the same fixed cost, but found an empty vehicle of this type with
1048  // higher fixed cost.
1049  DCHECK(VehicleIsEmpty(next_fixed_cost_empty_vehicle));
1050  // Update the pair entry to correspond to an insertion on this
1051  // next_fixed_cost_empty_vehicle instead of the previous entry_vehicle.
1052  pair_entry->set_vehicle(next_fixed_cost_empty_vehicle);
1053  pickup_to_entries->at(pair_entry->pickup_insert_after()).erase(pair_entry);
1054  pair_entry->set_pickup_insert_after(
1055  model()->Start(next_fixed_cost_empty_vehicle));
1056  pickup_to_entries->at(pair_entry->pickup_insert_after()).insert(pair_entry);
1057  DCHECK_EQ(pair_entry->delivery_insert_after(), pickup);
1058  UpdatePairEntry(pair_entry, priority_queue);
1059  } else {
1060  DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1061  delivery_to_entries);
1062  }
1063 
1064  return true;
1065 }
1066 
1067 void GlobalCheapestInsertionFilteredHeuristic::InsertNodesOnRoutes(
1068  const std::vector<int>& nodes, const absl::flat_hash_set<int>& vehicles) {
1069  AdjustablePriorityQueue<NodeEntry> priority_queue;
1070  std::vector<NodeEntries> position_to_node_entries;
1071  InitializePositions(nodes, vehicles, &priority_queue,
1072  &position_to_node_entries);
1073  // The following boolean indicates whether or not all vehicles are being
1074  // considered for insertion of the nodes simultaneously.
1075  // In the sequential version of the heuristic, as well as when inserting
1076  // single pickup or deliveries from pickup/delivery pairs, this will be false.
1077  // In the general parallel version of the heuristic, all_vehicles is true.
1078  const bool all_vehicles =
1079  vehicles.empty() || vehicles.size() == model()->vehicles();
1080 
1081  while (!priority_queue.IsEmpty()) {
1082  NodeEntry* const node_entry = priority_queue.Top();
1083  if (StopSearch()) {
1084  for (NodeEntry* const entry : *priority_queue.Raw()) {
1085  delete entry;
1086  }
1087  return;
1088  }
1089  const int64_t node_to_insert = node_entry->node_to_insert();
1090  if (Contains(node_to_insert)) {
1091  DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
1092  continue;
1093  }
1094 
1095  const int entry_vehicle = node_entry->vehicle();
1096  if (entry_vehicle == -1) {
1097  DCHECK(all_vehicles);
1098  // Make node unperformed.
1099  SetValue(node_to_insert, node_to_insert);
1100  if (!Commit()) {
1101  DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
1102  }
1103  continue;
1104  }
1105 
1106  // Make node performed.
1107  if (InsertNodeEntryUsingEmptyVehicleTypeCurator(
1108  nodes, all_vehicles, node_entry, &priority_queue,
1109  &position_to_node_entries)) {
1110  DCHECK(all_vehicles);
1111  continue;
1112  }
1113 
1114  const int64_t insert_after = node_entry->insert_after();
1115  InsertBetween(node_to_insert, insert_after, Value(insert_after));
1116  if (Commit()) {
1117  UpdatePositions(nodes, entry_vehicle, node_to_insert, all_vehicles,
1118  &priority_queue, &position_to_node_entries);
1119  UpdatePositions(nodes, entry_vehicle, insert_after, all_vehicles,
1120  &priority_queue, &position_to_node_entries);
1121  SetVehicleIndex(node_to_insert, entry_vehicle);
1122  } else {
1123  DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
1124  }
1125  }
1126 }
1127 
1128 bool GlobalCheapestInsertionFilteredHeuristic::
1129  InsertNodeEntryUsingEmptyVehicleTypeCurator(
1130  const std::vector<int>& nodes, bool all_vehicles,
1131  GlobalCheapestInsertionFilteredHeuristic::NodeEntry* const node_entry,
1133  GlobalCheapestInsertionFilteredHeuristic::NodeEntry>*
1134  priority_queue,
1135  std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1136  position_to_node_entries) {
1137  const int entry_vehicle = node_entry->vehicle();
1138  if (entry_vehicle == -1 || !all_vehicles || !VehicleIsEmpty(entry_vehicle)) {
1139  return false;
1140  }
1141 
1142  // Trying to insert on an empty vehicle, and all vehicles are being
1143  // considered simultaneously.
1144  // As we only have one node_entry per type, we try inserting on all vehicles
1145  // of this type with the same fixed cost as they all have the same insertion
1146  // value.
1147  const int64_t node_to_insert = node_entry->node_to_insert();
1148  const int64_t entry_fixed_cost =
1149  model()->GetFixedCostOfVehicle(entry_vehicle);
1150  auto vehicle_is_compatible = [this, entry_fixed_cost,
1151  node_to_insert](int vehicle) {
1152  if (model()->GetFixedCostOfVehicle(vehicle) != entry_fixed_cost) {
1153  return false;
1154  }
1155  // NOTE: Only empty vehicles should be in the vehicle_curator_.
1156  DCHECK(VehicleIsEmpty(vehicle));
1157  InsertBetween(node_to_insert, model()->Start(vehicle),
1158  model()->End(vehicle));
1159  return Commit();
1160  };
1161  // Since the vehicles of the same type are sorted by increasing fixed
1162  // cost by the curator, we can stop as soon as an empty vehicle with a fixed
1163  // cost higher than the entry_fixed_cost is found, and add new entries for
1164  // this new vehicle.
1165  auto stop_and_return_vehicle = [this, entry_fixed_cost](int vehicle) {
1166  return model()->GetFixedCostOfVehicle(vehicle) > entry_fixed_cost;
1167  };
1168  const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1169  empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1170  empty_vehicle_type_curator_->Type(entry_vehicle),
1171  vehicle_is_compatible, stop_and_return_vehicle);
1172  if (compatible_vehicle >= 0) {
1173  // The node was inserted on this vehicle.
1174  UpdatePositions(nodes, compatible_vehicle, node_to_insert, all_vehicles,
1175  priority_queue, position_to_node_entries);
1176  const int64_t compatible_start = model()->Start(compatible_vehicle);
1177  const bool no_prior_entries_for_this_vehicle =
1178  position_to_node_entries->at(compatible_start).empty();
1179  UpdatePositions(nodes, compatible_vehicle, compatible_start, all_vehicles,
1180  priority_queue, position_to_node_entries);
1181  SetVehicleIndex(node_to_insert, compatible_vehicle);
1182  if (compatible_vehicle != entry_vehicle) {
1183  // The node was inserted on another empty vehicle of the same type
1184  // and same fixed cost as entry_vehicle.
1185  // Since this vehicle is empty and has the same fixed cost as the
1186  // entry_vehicle, it shouldn't be the representative of empty vehicles
1187  // for any node in the priority queue.
1188  DCHECK(no_prior_entries_for_this_vehicle);
1189  return true;
1190  }
1191  // The previously unused entry_vehicle is now used, so we use the next
1192  // available vehicle of the same type to compute and store insertions on
1193  // empty vehicles.
1194  const int new_empty_vehicle =
1195  empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1196  empty_vehicle_type_curator_->Type(compatible_vehicle));
1197 
1198  if (new_empty_vehicle >= 0) {
1199  DCHECK(VehicleIsEmpty(new_empty_vehicle));
1200  // Add node entries after this vehicle start for uninserted nodes which
1201  // don't have entries on this empty vehicle.
1202  UpdatePositions(nodes, new_empty_vehicle,
1203  model()->Start(new_empty_vehicle), all_vehicles,
1204  priority_queue, position_to_node_entries);
1205  }
1206  } else if (next_fixed_cost_empty_vehicle >= 0) {
1207  // Could not insert on this vehicle or any other vehicle of the same
1208  // type with the same fixed cost, but found an empty vehicle of this type
1209  // with higher fixed cost.
1210  DCHECK(VehicleIsEmpty(next_fixed_cost_empty_vehicle));
1211  // Update the insertion entry to be on next_empty_vehicle instead of the
1212  // previous entry_vehicle.
1213  position_to_node_entries->at(node_entry->insert_after()).erase(node_entry);
1214  node_entry->set_insert_after(model()->Start(next_fixed_cost_empty_vehicle));
1215  position_to_node_entries->at(node_entry->insert_after()).insert(node_entry);
1216  node_entry->set_vehicle(next_fixed_cost_empty_vehicle);
1217  UpdateNodeEntry(node_entry, priority_queue);
1218  } else {
1219  DeleteNodeEntry(node_entry, priority_queue, position_to_node_entries);
1220  }
1221 
1222  return true;
1223 }
1224 
1225 void GlobalCheapestInsertionFilteredHeuristic::SequentialInsertNodes(
1226  const std::vector<int>& nodes) {
1227  std::vector<bool> is_vehicle_used;
1228  absl::flat_hash_set<int> used_vehicles;
1229  std::vector<int> unused_vehicles;
1230 
1231  DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1232  if (!used_vehicles.empty()) {
1233  InsertNodesOnRoutes(nodes, used_vehicles);
1234  }
1235 
1236  std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1237  ComputeStartEndDistanceForVehicles(unused_vehicles);
1238  std::priority_queue<Seed, std::vector<Seed>, std::greater<Seed>>
1239  first_node_queue;
1240  InitializePriorityQueue(&start_end_distances_per_node, &first_node_queue);
1241 
1242  int vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1243  &is_vehicle_used);
1244 
1245  while (vehicle >= 0) {
1246  InsertNodesOnRoutes(nodes, {vehicle});
1247  vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1248  &is_vehicle_used);
1249  }
1250 }
1251 
1252 void GlobalCheapestInsertionFilteredHeuristic::DetectUsedVehicles(
1253  std::vector<bool>* is_vehicle_used, std::vector<int>* unused_vehicles,
1254  absl::flat_hash_set<int>* used_vehicles) {
1255  is_vehicle_used->clear();
1256  is_vehicle_used->resize(model()->vehicles());
1257 
1258  used_vehicles->clear();
1259  used_vehicles->reserve(model()->vehicles());
1260 
1261  unused_vehicles->clear();
1262  unused_vehicles->reserve(model()->vehicles());
1263 
1264  for (int vehicle = 0; vehicle < model()->vehicles(); vehicle++) {
1265  if (!VehicleIsEmpty(vehicle)) {
1266  (*is_vehicle_used)[vehicle] = true;
1267  used_vehicles->insert(vehicle);
1268  } else {
1269  (*is_vehicle_used)[vehicle] = false;
1270  unused_vehicles->push_back(vehicle);
1271  }
1272  }
1273 }
1274 
1275 void GlobalCheapestInsertionFilteredHeuristic::InsertFarthestNodesAsSeeds() {
1276  if (gci_params_.farthest_seeds_ratio <= 0) return;
1277  // Insert at least 1 farthest Seed if the parameter is positive.
1278  const int num_seeds = static_cast<int>(
1279  std::ceil(gci_params_.farthest_seeds_ratio * model()->vehicles()));
1280 
1281  std::vector<bool> is_vehicle_used;
1282  absl::flat_hash_set<int> used_vehicles;
1283  std::vector<int> unused_vehicles;
1284  DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1285  std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1286  ComputeStartEndDistanceForVehicles(unused_vehicles);
1287 
1288  // Priority queue where the Seeds with a larger distance are given higher
1289  // priority.
1290  std::priority_queue<Seed> farthest_node_queue;
1291  InitializePriorityQueue(&start_end_distances_per_node, &farthest_node_queue);
1292 
1293  int inserted_seeds = 0;
1294  while (!farthest_node_queue.empty() && inserted_seeds < num_seeds) {
1295  InsertSeedNode(&start_end_distances_per_node, &farthest_node_queue,
1296  &is_vehicle_used);
1297  inserted_seeds++;
1298  }
1299 
1300  // NOTE: As we don't use the empty_vehicle_type_curator_ when inserting seed
1301  // nodes on routes, some previously empty vehicles may now be used, so we
1302  // update the curator accordingly to ensure it still only stores empty
1303  // vehicles.
1304  DCHECK(empty_vehicle_type_curator_ != nullptr);
1305  empty_vehicle_type_curator_->Update(
1306  [this](int vehicle) { return !VehicleIsEmpty(vehicle); });
1307 }
1308 
1309 template <class Queue>
1310 int GlobalCheapestInsertionFilteredHeuristic::InsertSeedNode(
1311  std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
1312  Queue* priority_queue, std::vector<bool>* is_vehicle_used) {
1313  while (!priority_queue->empty()) {
1314  if (StopSearch()) break;
1315  const Seed& seed = priority_queue->top();
1316 
1317  const int seed_node = seed.second;
1318  const int seed_vehicle = seed.first.vehicle;
1319 
1320  std::vector<StartEndValue>& other_start_end_values =
1321  (*start_end_distances_per_node)[seed_node];
1322 
1323  if (Contains(seed_node)) {
1324  // The node is already inserted, it is therefore no longer considered as
1325  // a potential seed.
1326  priority_queue->pop();
1327  other_start_end_values.clear();
1328  continue;
1329  }
1330  if (!(*is_vehicle_used)[seed_vehicle]) {
1331  // Try to insert this seed_node on this vehicle's route.
1332  const int64_t start = model()->Start(seed_vehicle);
1333  const int64_t end = model()->End(seed_vehicle);
1334  DCHECK_EQ(Value(start), end);
1335  InsertBetween(seed_node, start, end);
1336  if (Commit()) {
1337  priority_queue->pop();
1338  (*is_vehicle_used)[seed_vehicle] = true;
1339  other_start_end_values.clear();
1340  SetVehicleIndex(seed_node, seed_vehicle);
1341  return seed_vehicle;
1342  }
1343  }
1344  // Either the vehicle is already used, or the Commit() wasn't successful.
1345  // In both cases, we remove this Seed from the priority queue, and insert
1346  // the next StartEndValue from start_end_distances_per_node[seed_node]
1347  // in the priority queue.
1348  priority_queue->pop();
1349  if (!other_start_end_values.empty()) {
1350  const StartEndValue& next_seed_value = other_start_end_values.back();
1351  priority_queue->push(std::make_pair(next_seed_value, seed_node));
1352  other_start_end_values.pop_back();
1353  }
1354  }
1355  // No seed node was inserted.
1356  return -1;
1357 }
1358 
1359 void GlobalCheapestInsertionFilteredHeuristic::InitializePairPositions(
1360  const std::vector<int>& pair_indices,
1362  GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1363  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1364  pickup_to_entries,
1365  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1366  delivery_to_entries) {
1367  priority_queue->Clear();
1368  pickup_to_entries->clear();
1369  pickup_to_entries->resize(model()->Size());
1370  delivery_to_entries->clear();
1371  delivery_to_entries->resize(model()->Size());
1372  const RoutingModel::IndexPairs& pickup_delivery_pairs =
1374  for (int index : pair_indices) {
1375  const RoutingModel::IndexPair& index_pair = pickup_delivery_pairs[index];
1376  for (int64_t pickup : index_pair.first) {
1377  if (Contains(pickup)) continue;
1378  for (int64_t delivery : index_pair.second) {
1379  if (Contains(delivery)) continue;
1380  // Add insertion entry making pair unperformed. When the pair is part
1381  // of a disjunction we do not try to make any of its pairs unperformed
1382  // as it requires having an entry with all pairs being unperformed.
1383  // TODO(user): Adapt the code to make pair disjunctions unperformed.
1384  if (gci_params_.add_unperformed_entries &&
1385  index_pair.first.size() == 1 && index_pair.second.size() == 1 &&
1386  GetUnperformedValue(pickup) !=
1388  GetUnperformedValue(delivery) !=
1390  AddPairEntry(pickup, -1, delivery, -1, -1, priority_queue, nullptr,
1391  nullptr);
1392  }
1393  // Add all other insertion entries with pair performed.
1394  InitializeInsertionEntriesPerformingPair(
1395  pickup, delivery, priority_queue, pickup_to_entries,
1396  delivery_to_entries);
1397  }
1398  }
1399  }
1400 }
1401 
1402 void GlobalCheapestInsertionFilteredHeuristic::
1403  InitializeInsertionEntriesPerformingPair(
1404  int64_t pickup, int64_t delivery,
1406  GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
1407  priority_queue,
1408  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1409  pickup_to_entries,
1410  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1411  delivery_to_entries) {
1412  if (!gci_params_.use_neighbors_ratio_for_initialization) {
1413  std::vector<std::pair<std::pair<int64_t, int>, std::pair<int64_t, int64_t>>>
1414  valued_positions;
1415  for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
1416  if (VehicleIsEmpty(vehicle) &&
1417  empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1418  empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1419  // We only consider the least expensive empty vehicle of each type for
1420  // entries.
1421  continue;
1422  }
1423  const int64_t start = model()->Start(vehicle);
1424  std::vector<ValuedPosition> valued_pickup_positions;
1425  AppendEvaluatedPositionsAfter(pickup, start, Value(start), vehicle,
1426  &valued_pickup_positions);
1427  for (const ValuedPosition& valued_pickup_position :
1428  valued_pickup_positions) {
1429  const int64_t pickup_position = valued_pickup_position.second;
1430  CHECK(!model()->IsEnd(pickup_position));
1431  std::vector<ValuedPosition> valued_delivery_positions;
1432  AppendEvaluatedPositionsAfter(delivery, pickup, Value(pickup_position),
1433  vehicle, &valued_delivery_positions);
1434  for (const ValuedPosition& valued_delivery_position :
1435  valued_delivery_positions) {
1436  valued_positions.push_back(std::make_pair(
1437  std::make_pair(CapAdd(valued_pickup_position.first,
1438  valued_delivery_position.first),
1439  vehicle),
1440  std::make_pair(pickup_position,
1441  valued_delivery_position.second)));
1442  }
1443  }
1444  }
1445  for (const auto& [cost_for_vehicle, pair_positions] : valued_positions) {
1446  DCHECK_NE(pair_positions.first, pair_positions.second);
1447  AddPairEntry(pickup, pair_positions.first, delivery,
1448  pair_positions.second, cost_for_vehicle.second,
1449  priority_queue, pickup_to_entries, delivery_to_entries);
1450  }
1451  return;
1452  }
1453 
1454  // We're only considering the closest neighbors as insertion positions for
1455  // the pickup/delivery pair.
1456  for (int cost_class = 0; cost_class < model()->GetCostClassesCount();
1457  cost_class++) {
1458  absl::flat_hash_set<std::pair<int64_t, int64_t>>
1459  existing_insertion_positions;
1460  // Explore the neighborhood of the pickup.
1461  for (const int64_t pickup_insert_after :
1462  GetNeighborsOfNodeForCostClass(cost_class, pickup)) {
1463  if (!Contains(pickup_insert_after)) {
1464  continue;
1465  }
1466  const int vehicle = node_index_to_vehicle_[pickup_insert_after];
1467  if (vehicle < 0 ||
1468  model()->GetCostClassIndexOfVehicle(vehicle).value() != cost_class) {
1469  continue;
1470  }
1471 
1472  if (VehicleIsEmpty(vehicle) &&
1473  empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1474  empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1475  // We only consider the least expensive empty vehicle of each type for
1476  // entries.
1477  continue;
1478  }
1479 
1480  int64_t delivery_insert_after = pickup;
1481  while (!model()->IsEnd(delivery_insert_after)) {
1482  const std::pair<int64_t, int64_t> insertion_position = {
1483  pickup_insert_after, delivery_insert_after};
1484  DCHECK(!gtl::ContainsKey(existing_insertion_positions,
1485  insertion_position));
1486  existing_insertion_positions.insert(insertion_position);
1487 
1488  AddPairEntry(pickup, pickup_insert_after, delivery,
1489  delivery_insert_after, vehicle, priority_queue,
1490  pickup_to_entries, delivery_to_entries);
1491  delivery_insert_after = (delivery_insert_after == pickup)
1492  ? Value(pickup_insert_after)
1493  : Value(delivery_insert_after);
1494  }
1495  }
1496 
1497  // Explore the neighborhood of the delivery.
1498  for (const int64_t delivery_insert_after :
1499  GetNeighborsOfNodeForCostClass(cost_class, delivery)) {
1500  if (!Contains(delivery_insert_after)) {
1501  continue;
1502  }
1503  const int vehicle = node_index_to_vehicle_[delivery_insert_after];
1504  if (vehicle < 0 ||
1505  model()->GetCostClassIndexOfVehicle(vehicle).value() != cost_class) {
1506  continue;
1507  }
1508 
1509  if (VehicleIsEmpty(vehicle)) {
1510  // Vehicle is empty.
1511  DCHECK_EQ(delivery_insert_after, model()->Start(vehicle));
1512  }
1513 
1514  int64_t pickup_insert_after = model()->Start(vehicle);
1515  while (pickup_insert_after != delivery_insert_after) {
1516  if (!gtl::ContainsKey(
1517  existing_insertion_positions,
1518  std::make_pair(pickup_insert_after, delivery_insert_after))) {
1519  AddPairEntry(pickup, pickup_insert_after, delivery,
1520  delivery_insert_after, vehicle, priority_queue,
1521  pickup_to_entries, delivery_to_entries);
1522  }
1523  pickup_insert_after = Value(pickup_insert_after);
1524  }
1525  }
1526  }
1527 }
1528 
1529 void GlobalCheapestInsertionFilteredHeuristic::UpdateAfterPairInsertion(
1530  const std::vector<int>& pair_indices, int vehicle, int64_t pickup,
1531  int64_t pickup_position, int64_t delivery, int64_t delivery_position,
1532  AdjustablePriorityQueue<PairEntry>* priority_queue,
1533  std::vector<PairEntries>* pickup_to_entries,
1534  std::vector<PairEntries>* delivery_to_entries) {
1535  UpdatePairPositions(pair_indices, vehicle, pickup_position, priority_queue,
1536  pickup_to_entries, delivery_to_entries);
1537  UpdatePairPositions(pair_indices, vehicle, pickup, priority_queue,
1538  pickup_to_entries, delivery_to_entries);
1539  UpdatePairPositions(pair_indices, vehicle, delivery, priority_queue,
1540  pickup_to_entries, delivery_to_entries);
1541  if (delivery_position != pickup) {
1542  UpdatePairPositions(pair_indices, vehicle, delivery_position,
1543  priority_queue, pickup_to_entries, delivery_to_entries);
1544  }
1545  SetVehicleIndex(pickup, vehicle);
1546  SetVehicleIndex(delivery, vehicle);
1547 }
1548 
1549 void GlobalCheapestInsertionFilteredHeuristic::UpdatePickupPositions(
1550  const std::vector<int>& pair_indices, int vehicle,
1551  int64_t pickup_insert_after,
1553  GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1554  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1555  pickup_to_entries,
1556  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1557  delivery_to_entries) {
1558  // First, remove entries which have already been inserted and keep track of
1559  // the entries which are being kept and must be updated.
1560  using Pair = std::pair<int64_t, int64_t>;
1561  using Insertion = std::pair<Pair, /*delivery_insert_after*/ int64_t>;
1562  absl::flat_hash_set<Insertion> existing_insertions;
1563  std::vector<PairEntry*> to_remove;
1564  for (PairEntry* const pair_entry :
1565  pickup_to_entries->at(pickup_insert_after)) {
1566  DCHECK(priority_queue->Contains(pair_entry));
1567  DCHECK_EQ(pair_entry->pickup_insert_after(), pickup_insert_after);
1568  if (Contains(pair_entry->pickup_to_insert()) ||
1569  Contains(pair_entry->delivery_to_insert())) {
1570  to_remove.push_back(pair_entry);
1571  } else {
1572  DCHECK(delivery_to_entries->at(pair_entry->delivery_insert_after())
1573  .contains(pair_entry));
1574  UpdatePairEntry(pair_entry, priority_queue);
1575  existing_insertions.insert(
1576  {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
1577  pair_entry->delivery_insert_after()});
1578  }
1579  }
1580  for (PairEntry* const pair_entry : to_remove) {
1581  DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1582  delivery_to_entries);
1583  }
1584  // Create new entries for which the pickup is to be inserted after
1585  // pickup_insert_after.
1586  const int cost_class = model()->GetCostClassIndexOfVehicle(vehicle).value();
1587  const int64_t pickup_insert_before = Value(pickup_insert_after);
1588  const RoutingModel::IndexPairs& pickup_delivery_pairs =
1590  for (int pair_index : pair_indices) {
1591  const RoutingModel::IndexPair& index_pair =
1592  pickup_delivery_pairs[pair_index];
1593  for (int64_t pickup : index_pair.first) {
1594  if (Contains(pickup) ||
1595  !IsNeighborForCostClass(cost_class, pickup_insert_after, pickup)) {
1596  continue;
1597  }
1598  for (int64_t delivery : index_pair.second) {
1599  if (Contains(delivery)) {
1600  continue;
1601  }
1602  int64_t delivery_insert_after = pickup;
1603  while (!model()->IsEnd(delivery_insert_after)) {
1604  const Insertion insertion = {{pickup, delivery},
1605  delivery_insert_after};
1606  if (!gtl::ContainsKey(existing_insertions, insertion)) {
1607  AddPairEntry(pickup, pickup_insert_after, delivery,
1608  delivery_insert_after, vehicle, priority_queue,
1609  pickup_to_entries, delivery_to_entries);
1610  }
1611  if (delivery_insert_after == pickup) {
1612  delivery_insert_after = pickup_insert_before;
1613  } else {
1614  delivery_insert_after = Value(delivery_insert_after);
1615  }
1616  }
1617  }
1618  }
1619  }
1620 }
1621 
1622 void GlobalCheapestInsertionFilteredHeuristic::UpdateDeliveryPositions(
1623  const std::vector<int>& pair_indices, int vehicle,
1624  int64_t delivery_insert_after,
1626  GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1627  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1628  pickup_to_entries,
1629  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1630  delivery_to_entries) {
1631  // First, remove entries which have already been inserted and keep track of
1632  // the entries which are being kept and must be updated.
1633  using Pair = std::pair<int64_t, int64_t>;
1634  using Insertion = std::pair<Pair, /*pickup_insert_after*/ int64_t>;
1635  absl::flat_hash_set<Insertion> existing_insertions;
1636  std::vector<PairEntry*> to_remove;
1637  for (PairEntry* const pair_entry :
1638  delivery_to_entries->at(delivery_insert_after)) {
1639  DCHECK(priority_queue->Contains(pair_entry));
1640  DCHECK_EQ(pair_entry->delivery_insert_after(), delivery_insert_after);
1641  if (Contains(pair_entry->pickup_to_insert()) ||
1642  Contains(pair_entry->delivery_to_insert())) {
1643  to_remove.push_back(pair_entry);
1644  } else {
1645  DCHECK(pickup_to_entries->at(pair_entry->pickup_insert_after())
1646  .contains(pair_entry));
1647  existing_insertions.insert(
1648  {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
1649  pair_entry->pickup_insert_after()});
1650  UpdatePairEntry(pair_entry, priority_queue);
1651  }
1652  }
1653  for (PairEntry* const pair_entry : to_remove) {
1654  DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1655  delivery_to_entries);
1656  }
1657  // Create new entries for which the delivery is to be inserted after
1658  // delivery_insert_after.
1659  const int cost_class = model()->GetCostClassIndexOfVehicle(vehicle).value();
1660  const RoutingModel::IndexPairs& pickup_delivery_pairs =
1662  for (int pair_index : pair_indices) {
1663  const RoutingModel::IndexPair& index_pair =
1664  pickup_delivery_pairs[pair_index];
1665  for (int64_t delivery : index_pair.second) {
1666  if (Contains(delivery) ||
1667  !IsNeighborForCostClass(cost_class, delivery_insert_after,
1668  delivery)) {
1669  continue;
1670  }
1671  for (int64_t pickup : index_pair.first) {
1672  if (Contains(pickup)) {
1673  continue;
1674  }
1675  int64_t pickup_insert_after = model()->Start(vehicle);
1676  while (pickup_insert_after != delivery_insert_after) {
1677  const Insertion insertion = {{pickup, delivery}, pickup_insert_after};
1678  if (!gtl::ContainsKey(existing_insertions, insertion)) {
1679  AddPairEntry(pickup, pickup_insert_after, delivery,
1680  delivery_insert_after, vehicle, priority_queue,
1681  pickup_to_entries, delivery_to_entries);
1682  }
1683  pickup_insert_after = Value(pickup_insert_after);
1684  }
1685  }
1686  }
1687  }
1688 }
1689 
1690 void GlobalCheapestInsertionFilteredHeuristic::DeletePairEntry(
1691  GlobalCheapestInsertionFilteredHeuristic::PairEntry* entry,
1693  GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1694  std::vector<PairEntries>* pickup_to_entries,
1695  std::vector<PairEntries>* delivery_to_entries) {
1696  priority_queue->Remove(entry);
1697  if (entry->pickup_insert_after() != -1) {
1698  pickup_to_entries->at(entry->pickup_insert_after()).erase(entry);
1699  }
1700  if (entry->delivery_insert_after() != -1) {
1701  delivery_to_entries->at(entry->delivery_insert_after()).erase(entry);
1702  }
1703  delete entry;
1704 }
1705 
1706 void GlobalCheapestInsertionFilteredHeuristic::AddPairEntry(
1707  int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
1708  int64_t delivery_insert_after, int vehicle,
1710  GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1711  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1712  pickup_entries,
1713  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1714  delivery_entries) const {
1715  if (pickup_insert_after == -1) {
1716  DCHECK_EQ(delivery_insert_after, -1);
1717  DCHECK_EQ(vehicle, -1);
1718  PairEntry* pair_entry = new PairEntry(pickup, -1, delivery, -1, -1);
1719  pair_entry->set_value(
1720  absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1721  ? 0
1722  : CapAdd(GetUnperformedValue(pickup),
1723  GetUnperformedValue(delivery)));
1724  priority_queue->Add(pair_entry);
1725  return;
1726  }
1727 
1728  PairEntry* const pair_entry = new PairEntry(
1729  pickup, pickup_insert_after, delivery, delivery_insert_after, vehicle);
1730  pair_entry->set_value(GetInsertionValueForPairAtPositions(
1731  pickup, pickup_insert_after, delivery, delivery_insert_after, vehicle));
1732 
1733  // Add entry to priority_queue and pickup_/delivery_entries.
1734  DCHECK(!priority_queue->Contains(pair_entry));
1735  pickup_entries->at(pickup_insert_after).insert(pair_entry);
1736  delivery_entries->at(delivery_insert_after).insert(pair_entry);
1737  priority_queue->Add(pair_entry);
1738 }
1739 
1740 void GlobalCheapestInsertionFilteredHeuristic::UpdatePairEntry(
1741  GlobalCheapestInsertionFilteredHeuristic::PairEntry* const pair_entry,
1743  GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue)
1744  const {
1745  pair_entry->set_value(GetInsertionValueForPairAtPositions(
1746  pair_entry->pickup_to_insert(), pair_entry->pickup_insert_after(),
1747  pair_entry->delivery_to_insert(), pair_entry->delivery_insert_after(),
1748  pair_entry->vehicle()));
1749 
1750  // Update the priority_queue.
1751  DCHECK(priority_queue->Contains(pair_entry));
1752  priority_queue->NoteChangedPriority(pair_entry);
1753 }
1754 
1755 int64_t
1756 GlobalCheapestInsertionFilteredHeuristic::GetInsertionValueForPairAtPositions(
1757  int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
1758  int64_t delivery_insert_after, int vehicle) const {
1759  DCHECK_GE(pickup_insert_after, 0);
1760  const int64_t pickup_insert_before = Value(pickup_insert_after);
1761  const int64_t pickup_value = GetInsertionCostForNodeAtPosition(
1762  pickup, pickup_insert_after, pickup_insert_before, vehicle);
1763 
1764  DCHECK_GE(delivery_insert_after, 0);
1765  const int64_t delivery_insert_before = (delivery_insert_after == pickup)
1766  ? pickup_insert_before
1767  : Value(delivery_insert_after);
1768  const int64_t delivery_value = GetInsertionCostForNodeAtPosition(
1769  delivery, delivery_insert_after, delivery_insert_before, vehicle);
1770 
1771  const int64_t penalty_shift =
1772  absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1773  ? CapAdd(GetUnperformedValue(pickup), GetUnperformedValue(delivery))
1774  : 0;
1775  return CapSub(CapAdd(pickup_value, delivery_value), penalty_shift);
1776 }
1777 
1778 void GlobalCheapestInsertionFilteredHeuristic::InitializePositions(
1779  const std::vector<int>& nodes, const absl::flat_hash_set<int>& vehicles,
1781  GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
1782  std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1783  position_to_node_entries) {
1784  priority_queue->Clear();
1785  position_to_node_entries->clear();
1786  position_to_node_entries->resize(model()->Size());
1787 
1788  const int num_vehicles =
1789  vehicles.empty() ? model()->vehicles() : vehicles.size();
1790  const bool all_vehicles = (num_vehicles == model()->vehicles());
1791 
1792  for (int node : nodes) {
1793  if (Contains(node)) {
1794  continue;
1795  }
1796  // Add insertion entry making node unperformed.
1797  if (gci_params_.add_unperformed_entries &&
1799  AddNodeEntry(node, -1, -1, all_vehicles, priority_queue, nullptr);
1800  }
1801  // Add all insertion entries making node performed.
1802  InitializeInsertionEntriesPerformingNode(node, vehicles, priority_queue,
1803  position_to_node_entries);
1804  }
1805 }
1806 
1807 void GlobalCheapestInsertionFilteredHeuristic::
1808  InitializeInsertionEntriesPerformingNode(
1809  int64_t node, const absl::flat_hash_set<int>& vehicles,
1811  GlobalCheapestInsertionFilteredHeuristic::NodeEntry>*
1812  priority_queue,
1813  std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1814  position_to_node_entries) {
1815  const int num_vehicles =
1816  vehicles.empty() ? model()->vehicles() : vehicles.size();
1817  const bool all_vehicles = (num_vehicles == model()->vehicles());
1818 
1819  if (!gci_params_.use_neighbors_ratio_for_initialization) {
1820  auto vehicles_it = vehicles.begin();
1821  for (int v = 0; v < num_vehicles; v++) {
1822  const int vehicle = vehicles.empty() ? v : *vehicles_it++;
1823 
1824  const int64_t start = model()->Start(vehicle);
1825  if (all_vehicles && VehicleIsEmpty(vehicle) &&
1826  empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1827  empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1828  // We only consider the least expensive empty vehicle of each type for
1829  // entries.
1830  continue;
1831  }
1832  std::vector<ValuedPosition> valued_positions;
1833  AppendEvaluatedPositionsAfter(node, start, Value(start), vehicle,
1834  &valued_positions);
1835  for (const std::pair<int64_t, int64_t>& valued_position :
1836  valued_positions) {
1837  AddNodeEntry(node, valued_position.second, vehicle, all_vehicles,
1838  priority_queue, position_to_node_entries);
1839  }
1840  }
1841  return;
1842  }
1843 
1844  // We're only considering the closest neighbors as insertion positions for
1845  // the node.
1846  const auto insert_on_vehicle_for_cost_class = [this, &vehicles, all_vehicles](
1847  int v, int cost_class) {
1848  return (model()->GetCostClassIndexOfVehicle(v).value() == cost_class) &&
1849  (all_vehicles || vehicles.contains(v));
1850  };
1851  for (int cost_class = 0; cost_class < model()->GetCostClassesCount();
1852  cost_class++) {
1853  for (const int64_t insert_after :
1854  GetNeighborsOfNodeForCostClass(cost_class, node)) {
1855  if (!Contains(insert_after)) {
1856  continue;
1857  }
1858  const int vehicle = node_index_to_vehicle_[insert_after];
1859  if (vehicle == -1 ||
1860  !insert_on_vehicle_for_cost_class(vehicle, cost_class)) {
1861  continue;
1862  }
1863  if (all_vehicles && VehicleIsEmpty(vehicle) &&
1864  empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1865  empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1866  // We only consider the least expensive empty vehicle of each type for
1867  // entries.
1868  continue;
1869  }
1870  AddNodeEntry(node, insert_after, vehicle, all_vehicles, priority_queue,
1871  position_to_node_entries);
1872  }
1873  }
1874 }
1875 
1876 void GlobalCheapestInsertionFilteredHeuristic::UpdatePositions(
1877  const std::vector<int>& nodes, int vehicle, int64_t insert_after,
1878  bool all_vehicles,
1880  GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
1881  std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1882  node_entries) {
1883  // Either create new entries if we are inserting after a newly inserted node
1884  // or remove entries which have already been inserted.
1885  std::vector<NodeEntry*> to_remove;
1886  absl::flat_hash_set<int> existing_insertions;
1887  for (NodeEntry* const node_entry : node_entries->at(insert_after)) {
1888  DCHECK_EQ(node_entry->insert_after(), insert_after);
1889  const int64_t node_to_insert = node_entry->node_to_insert();
1890  if (Contains(node_to_insert)) {
1891  to_remove.push_back(node_entry);
1892  } else {
1893  UpdateNodeEntry(node_entry, priority_queue);
1894  existing_insertions.insert(node_to_insert);
1895  }
1896  }
1897  for (NodeEntry* const node_entry : to_remove) {
1898  DeleteNodeEntry(node_entry, priority_queue, node_entries);
1899  }
1900  const int cost_class = model()->GetCostClassIndexOfVehicle(vehicle).value();
1901  for (int node_to_insert : nodes) {
1902  if (!Contains(node_to_insert) &&
1903  !existing_insertions.contains(node_to_insert) &&
1904  IsNeighborForCostClass(cost_class, insert_after, node_to_insert)) {
1905  AddNodeEntry(node_to_insert, insert_after, vehicle, all_vehicles,
1906  priority_queue, node_entries);
1907  }
1908  }
1909 }
1910 
1911 void GlobalCheapestInsertionFilteredHeuristic::DeleteNodeEntry(
1912  GlobalCheapestInsertionFilteredHeuristic::NodeEntry* entry,
1914  GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
1915  std::vector<NodeEntries>* node_entries) {
1916  priority_queue->Remove(entry);
1917  if (entry->insert_after() != -1) {
1918  node_entries->at(entry->insert_after()).erase(entry);
1919  }
1920  delete entry;
1921 }
1922 
1923 void GlobalCheapestInsertionFilteredHeuristic::AddNodeEntry(
1924  int64_t node, int64_t insert_after, int vehicle, bool all_vehicles,
1925  AdjustablePriorityQueue<NodeEntry>* priority_queue,
1926  std::vector<NodeEntries>* node_entries) const {
1927  const int64_t node_penalty = GetUnperformedValue(node);
1928  const int64_t penalty_shift =
1929  absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1930  ? node_penalty
1931  : 0;
1932  if (insert_after == -1) {
1933  DCHECK_EQ(vehicle, -1);
1934  if (!all_vehicles) {
1935  // NOTE: In the case where we're not considering all routes
1936  // simultaneously, we don't add insertion entries making nodes
1937  // unperformed.
1938  return;
1939  }
1940  NodeEntry* const node_entry = new NodeEntry(node, -1, -1);
1941  node_entry->set_value(CapSub(node_penalty, penalty_shift));
1942  priority_queue->Add(node_entry);
1943  return;
1944  }
1945 
1946  const int64_t insertion_cost = GetInsertionCostForNodeAtPosition(
1947  node, insert_after, Value(insert_after), vehicle);
1948  if (!all_vehicles && insertion_cost > node_penalty) {
1949  // NOTE: When all vehicles aren't considered for insertion, we don't
1950  // add entries making nodes unperformed, so we don't add insertions
1951  // which cost more than the node penalty either.
1952  return;
1953  }
1954 
1955  NodeEntry* const node_entry = new NodeEntry(node, insert_after, vehicle);
1956  node_entry->set_value(CapSub(insertion_cost, penalty_shift));
1957  // Add entry to priority_queue and node_entries.
1958  DCHECK(!priority_queue->Contains(node_entry));
1959  node_entries->at(insert_after).insert(node_entry);
1960  priority_queue->Add(node_entry);
1961 }
1962 
1963 void GlobalCheapestInsertionFilteredHeuristic::UpdateNodeEntry(
1964  NodeEntry* const node_entry,
1965  AdjustablePriorityQueue<NodeEntry>* priority_queue) const {
1966  const int64_t node = node_entry->node_to_insert();
1967  const int64_t insert_after = node_entry->insert_after();
1968  DCHECK_GE(insert_after, 0);
1969  const int64_t insertion_cost = GetInsertionCostForNodeAtPosition(
1970  node, insert_after, Value(insert_after), node_entry->vehicle());
1971  const int64_t penalty_shift =
1972  absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1973  ? GetUnperformedValue(node)
1974  : 0;
1975 
1976  node_entry->set_value(CapSub(insertion_cost, penalty_shift));
1977  // Update the priority_queue.
1978  DCHECK(priority_queue->Contains(node_entry));
1979  priority_queue->NoteChangedPriority(node_entry);
1980 }
1981 
1982 // LocalCheapestInsertionFilteredHeuristic
1983 // TODO(user): Add support for penalty costs.
1987  std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
1988  LocalSearchFilterManager* filter_manager)
1989  : CheapestInsertionFilteredHeuristic(model, std::move(evaluator), nullptr,
1990  filter_manager) {
1991  std::vector<int> all_vehicles(model->vehicles());
1992  std::iota(std::begin(all_vehicles), std::end(all_vehicles), 0);
1993 
1994  start_end_distances_per_node_ =
1995  ComputeStartEndDistanceForVehicles(all_vehicles);
1996 }
1997 
1999  // Marking if we've tried inserting a node.
2000  std::vector<bool> visited(model()->Size(), false);
2001  // Possible positions where the current node can inserted.
2002  std::vector<int64_t> insertion_positions;
2003  // Possible positions where its associated delivery node can inserted (if the
2004  // current node has one).
2005  std::vector<int64_t> delivery_insertion_positions;
2006  // Iterating on pickup and delivery pairs
2007  const RoutingModel::IndexPairs& index_pairs =
2009  for (const auto& index_pair : index_pairs) {
2010  for (int64_t pickup : index_pair.first) {
2011  if (Contains(pickup)) {
2012  continue;
2013  }
2014  for (int64_t delivery : index_pair.second) {
2015  // If either is already in the solution, let it be inserted in the
2016  // standard node insertion loop.
2017  if (Contains(delivery)) {
2018  continue;
2019  }
2020  if (StopSearch()) return false;
2021  visited[pickup] = true;
2022  visited[delivery] = true;
2023  ComputeEvaluatorSortedPositions(pickup, &insertion_positions);
2024  for (const int64_t pickup_insertion : insertion_positions) {
2025  const int pickup_insertion_next = Value(pickup_insertion);
2026  ComputeEvaluatorSortedPositionsOnRouteAfter(
2027  delivery, pickup, pickup_insertion_next,
2028  &delivery_insertion_positions);
2029  bool found = false;
2030  for (const int64_t delivery_insertion :
2031  delivery_insertion_positions) {
2032  InsertBetween(pickup, pickup_insertion, pickup_insertion_next);
2033  const int64_t delivery_insertion_next =
2034  (delivery_insertion == pickup_insertion) ? pickup
2035  : (delivery_insertion == pickup) ? pickup_insertion_next
2036  : Value(delivery_insertion);
2037  InsertBetween(delivery, delivery_insertion,
2038  delivery_insertion_next);
2039  if (Commit()) {
2040  found = true;
2041  break;
2042  }
2043  }
2044  if (found) {
2045  break;
2046  }
2047  }
2048  }
2049  }
2050  }
2051 
2052  std::priority_queue<Seed> node_queue;
2053  InitializePriorityQueue(&start_end_distances_per_node_, &node_queue);
2054 
2055  while (!node_queue.empty()) {
2056  const int node = node_queue.top().second;
2057  node_queue.pop();
2058  if (Contains(node) || visited[node]) continue;
2059  ComputeEvaluatorSortedPositions(node, &insertion_positions);
2060  for (const int64_t insertion : insertion_positions) {
2061  if (StopSearch()) return false;
2062  InsertBetween(node, insertion, Value(insertion));
2063  if (Commit()) {
2064  break;
2065  }
2066  }
2067  }
2069  return Commit();
2070 }
2071 
2072 void LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPositions(
2073  int64_t node, std::vector<int64_t>* sorted_positions) {
2074  CHECK(sorted_positions != nullptr);
2075  CHECK(!Contains(node));
2076  sorted_positions->clear();
2077  const int size = model()->Size();
2078  if (node < size) {
2079  std::vector<std::pair<int64_t, int64_t>> valued_positions;
2080  for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
2081  const int64_t start = model()->Start(vehicle);
2082  AppendEvaluatedPositionsAfter(node, start, Value(start), vehicle,
2083  &valued_positions);
2084  }
2085  SortAndExtractPairSeconds(&valued_positions, sorted_positions);
2086  }
2087 }
2088 
2089 void LocalCheapestInsertionFilteredHeuristic::
2090  ComputeEvaluatorSortedPositionsOnRouteAfter(
2091  int64_t node, int64_t start, int64_t next_after_start,
2092  std::vector<int64_t>* sorted_positions) {
2093  CHECK(sorted_positions != nullptr);
2094  CHECK(!Contains(node));
2095  sorted_positions->clear();
2096  const int size = model()->Size();
2097  if (node < size) {
2098  // TODO(user): Take vehicle into account.
2099  std::vector<std::pair<int64_t, int64_t>> valued_positions;
2100  AppendEvaluatedPositionsAfter(node, start, next_after_start, 0,
2101  &valued_positions);
2102  SortAndExtractPairSeconds(&valued_positions, sorted_positions);
2103  }
2104 }
2105 
2106 // CheapestAdditionFilteredHeuristic
2107 
2109  RoutingModel* model, LocalSearchFilterManager* filter_manager)
2110  : RoutingFilteredHeuristic(model, filter_manager) {}
2111 
2113  const int kUnassigned = -1;
2115  std::vector<std::vector<int64_t>> deliveries(Size());
2116  std::vector<std::vector<int64_t>> pickups(Size());
2117  for (const RoutingModel::IndexPair& pair : pairs) {
2118  for (int first : pair.first) {
2119  for (int second : pair.second) {
2120  deliveries[first].push_back(second);
2121  pickups[second].push_back(first);
2122  }
2123  }
2124  }
2125  // To mimic the behavior of PathSelector (cf. search.cc), iterating on
2126  // routes with partial route at their start first then on routes with largest
2127  // index.
2128  std::vector<int> sorted_vehicles(model()->vehicles(), 0);
2129  for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
2130  sorted_vehicles[vehicle] = vehicle;
2131  }
2132  std::sort(sorted_vehicles.begin(), sorted_vehicles.end(),
2133  PartialRoutesAndLargeVehicleIndicesFirst(*this));
2134  // Neighbors of the node currently being extended.
2135  for (const int vehicle : sorted_vehicles) {
2136  int64_t last_node = GetStartChainEnd(vehicle);
2137  bool extend_route = true;
2138  // Extend the route of the current vehicle while it's possible. We can
2139  // iterate more than once if pickup and delivery pairs have been inserted
2140  // in the last iteration (see comment below); the new iteration will try to
2141  // extend the route after the last delivery on the route.
2142  while (extend_route) {
2143  extend_route = false;
2144  bool found = true;
2145  int64_t index = last_node;
2146  int64_t end = GetEndChainStart(vehicle);
2147  // Extend the route until either the end node of the vehicle is reached
2148  // or no node or node pair can be added. Deliveries in pickup and
2149  // delivery pairs are added at the same time as pickups, at the end of the
2150  // route, in reverse order of the pickups. Deliveries are never added
2151  // alone.
2152  while (found && !model()->IsEnd(index)) {
2153  found = false;
2154  std::vector<int64_t> neighbors;
2155  if (index < model()->Nexts().size()) {
2156  std::unique_ptr<IntVarIterator> it(
2157  model()->Nexts()[index]->MakeDomainIterator(false));
2158  auto next_values = InitAndGetValues(it.get());
2159  neighbors = GetPossibleNextsFromIterator(index, next_values.begin(),
2160  next_values.end());
2161  }
2162  for (int i = 0; !found && i < neighbors.size(); ++i) {
2163  int64_t next = -1;
2164  switch (i) {
2165  case 0:
2166  next = FindTopSuccessor(index, neighbors);
2167  break;
2168  case 1:
2169  SortSuccessors(index, &neighbors);
2170  ABSL_FALLTHROUGH_INTENDED;
2171  default:
2172  next = neighbors[i];
2173  }
2174  if (model()->IsEnd(next) && next != end) {
2175  continue;
2176  }
2177  // Only add a delivery if one of its pickups has been added already.
2178  if (!model()->IsEnd(next) && !pickups[next].empty()) {
2179  bool contains_pickups = false;
2180  for (int64_t pickup : pickups[next]) {
2181  if (Contains(pickup)) {
2182  contains_pickups = true;
2183  break;
2184  }
2185  }
2186  if (!contains_pickups) {
2187  continue;
2188  }
2189  }
2190  std::vector<int64_t> next_deliveries;
2191  if (next < deliveries.size()) {
2192  next_deliveries = GetPossibleNextsFromIterator(
2193  next, deliveries[next].begin(), deliveries[next].end());
2194  }
2195  if (next_deliveries.empty()) next_deliveries = {kUnassigned};
2196  for (int j = 0; !found && j < next_deliveries.size(); ++j) {
2197  if (StopSearch()) return false;
2198  int delivery = -1;
2199  switch (j) {
2200  case 0:
2201  delivery = FindTopSuccessor(next, next_deliveries);
2202  break;
2203  case 1:
2204  SortSuccessors(next, &next_deliveries);
2205  ABSL_FALLTHROUGH_INTENDED;
2206  default:
2207  delivery = next_deliveries[j];
2208  }
2209  // Insert "next" after "index", and before "end" if it is not the
2210  // end already.
2211  SetValue(index, next);
2212  if (!model()->IsEnd(next)) {
2213  SetValue(next, end);
2215  if (delivery != kUnassigned) {
2216  SetValue(next, delivery);
2217  SetValue(delivery, end);
2219  }
2220  }
2221  if (Commit()) {
2222  index = next;
2223  found = true;
2224  if (delivery != kUnassigned) {
2225  if (model()->IsEnd(end) && last_node != delivery) {
2226  last_node = delivery;
2227  extend_route = true;
2228  }
2229  end = delivery;
2230  }
2231  break;
2232  }
2233  }
2234  }
2235  }
2236  }
2237  }
2239  return Commit();
2240 }
2241 
2242 bool CheapestAdditionFilteredHeuristic::
2243  PartialRoutesAndLargeVehicleIndicesFirst::operator()(int vehicle1,
2244  int vehicle2) const {
2245  const bool has_partial_route1 = (builder_.model()->Start(vehicle1) !=
2246  builder_.GetStartChainEnd(vehicle1));
2247  const bool has_partial_route2 = (builder_.model()->Start(vehicle2) !=
2248  builder_.GetStartChainEnd(vehicle2));
2249  if (has_partial_route1 == has_partial_route2) {
2250  return vehicle2 < vehicle1;
2251  } else {
2252  return has_partial_route2 < has_partial_route1;
2253  }
2254 }
2255 
2256 // EvaluatorCheapestAdditionFilteredHeuristic
2257 
2260  RoutingModel* model, std::function<int64_t(int64_t, int64_t)> evaluator,
2261  LocalSearchFilterManager* filter_manager)
2262  : CheapestAdditionFilteredHeuristic(model, filter_manager),
2263  evaluator_(std::move(evaluator)) {}
2264 
2265 int64_t EvaluatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
2266  int64_t node, const std::vector<int64_t>& successors) {
2267  int64_t best_evaluation = std::numeric_limits<int64_t>::max();
2268  int64_t best_successor = -1;
2269  for (int64_t successor : successors) {
2270  const int64_t evaluation = (successor >= 0)
2271  ? evaluator_(node, successor)
2273  if (evaluation < best_evaluation ||
2274  (evaluation == best_evaluation && successor > best_successor)) {
2275  best_evaluation = evaluation;
2276  best_successor = successor;
2277  }
2278  }
2279  return best_successor;
2280 }
2281 
2282 void EvaluatorCheapestAdditionFilteredHeuristic::SortSuccessors(
2283  int64_t node, std::vector<int64_t>* successors) {
2284  std::vector<std::pair<int64_t, int64_t>> values;
2285  values.reserve(successors->size());
2286  for (int64_t successor : *successors) {
2287  // Tie-breaking on largest node index to mimic the behavior of
2288  // CheapestValueSelector (search.cc).
2289  values.push_back({evaluator_(node, successor), -successor});
2290  }
2291  std::sort(values.begin(), values.end());
2292  successors->clear();
2293  for (auto value : values) {
2294  successors->push_back(-value.second);
2295  }
2296 }
2297 
2298 // ComparatorCheapestAdditionFilteredHeuristic
2299 
2303  LocalSearchFilterManager* filter_manager)
2304  : CheapestAdditionFilteredHeuristic(model, filter_manager),
2305  comparator_(std::move(comparator)) {}
2306 
2307 int64_t ComparatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
2308  int64_t node, const std::vector<int64_t>& successors) {
2309  return *std::min_element(successors.begin(), successors.end(),
2310  [this, node](int successor1, int successor2) {
2311  return comparator_(node, successor1, successor2);
2312  });
2313 }
2314 
2315 void ComparatorCheapestAdditionFilteredHeuristic::SortSuccessors(
2316  int64_t node, std::vector<int64_t>* successors) {
2317  std::sort(successors->begin(), successors->end(),
2318  [this, node](int successor1, int successor2) {
2319  return comparator_(node, successor1, successor2);
2320  });
2321 }
2322 
2323 // Class storing and allowing access to the savings according to the number of
2324 // vehicle types.
2325 // The savings are stored and sorted in sorted_savings_per_vehicle_type_.
2326 // Furthermore, when there is more than one vehicle type, the savings for a same
2327 // before-->after arc are sorted in costs_and_savings_per_arc_[arc] by
2328 // increasing cost(s-->before-->after-->e), where s and e are the start and end
2329 // of the route, in order to make sure the arc is served by the route with the
2330 // closest depot (start/end) possible.
2331 // When there is only one vehicle "type" (i.e. all vehicles have the same
2332 // start/end and cost class), each arc has a single saving value associated to
2333 // it, so we ignore this last step to avoid unnecessary computations, and only
2334 // work with sorted_savings_per_vehicle_type_[0].
2335 // In case of multiple vehicle types, the best savings for each arc, i.e. the
2336 // savings corresponding to the closest vehicle type, are inserted and sorted in
2337 // sorted_savings_.
2338 //
2339 // This class also handles skipped Savings:
2340 // The vectors skipped_savings_starting/ending_at_ contain all the Savings that
2341 // weren't added to the model, which we want to consider for later:
2342 // 1) When a Saving before-->after with both nodes uncontained cannot be used to
2343 // start a new route (no more available vehicles or could not commit on any
2344 // of those available).
2345 // 2) When only one of the nodes of the Saving is contained but on a different
2346 // vehicle type.
2347 // In these cases, the Update() method is called with update_best_saving = true,
2348 // which in turn calls SkipSavingForArc() (within
2349 // UpdateNextAndSkippedSavingsForArcWithType()) to mark the Saving for this arc
2350 // (with the correct type in the second case) as "skipped", by storing it in
2351 // skipped_savings_starting_at_[before] and skipped_savings_ending_at_[after].
2352 //
2353 // UpdateNextAndSkippedSavingsForArcWithType() also updates the next_savings_
2354 // vector, which stores the savings to go through once we've iterated through
2355 // all sorted_savings_.
2356 // In the first case above, where neither nodes are contained, we skip the
2357 // current Saving (current_saving_), and add the next best Saving for this arc
2358 // to next_savings_ (in case this skipped Saving is never considered).
2359 // In the second case with a specific type, we search for the Saving with the
2360 // correct type for this arc, and add it to both next_savings_ and the skipped
2361 // Savings.
2362 //
2363 // The skipped Savings are then re-considered when one of their ends gets
2364 // inserted:
2365 // When another Saving other_node-->before (or after-->other_node) gets
2366 // inserted, all skipped Savings in skipped_savings_starting_at_[before] (or
2367 // skipped_savings_ending_at_[after]) are once again considered by calling
2368 // ReinjectSkippedSavingsStartingAt() (or ReinjectSkippedSavingsEndingAt()).
2369 // Then, when calling GetSaving(), we iterate through the reinjected Savings in
2370 // order of insertion in the vectors while there are reinjected savings.
2371 template <typename Saving>
2373  public:
2374  explicit SavingsContainer(const SavingsFilteredHeuristic* savings_db,
2375  int vehicle_types)
2376  : savings_db_(savings_db),
2377  vehicle_types_(vehicle_types),
2378  index_in_sorted_savings_(0),
2379  single_vehicle_type_(vehicle_types == 1),
2380  using_incoming_reinjected_saving_(false),
2381  sorted_(false),
2382  to_update_(true) {}
2383 
2384  void InitializeContainer(int64_t size, int64_t saving_neighbors) {
2385  sorted_savings_per_vehicle_type_.clear();
2386  sorted_savings_per_vehicle_type_.resize(vehicle_types_);
2387  for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
2388  savings.reserve(size * saving_neighbors);
2389  }
2390 
2391  sorted_savings_.clear();
2392  costs_and_savings_per_arc_.clear();
2393  arc_indices_per_before_node_.clear();
2394 
2395  if (!single_vehicle_type_) {
2396  costs_and_savings_per_arc_.reserve(size * saving_neighbors);
2397  arc_indices_per_before_node_.resize(size);
2398  for (int before_node = 0; before_node < size; before_node++) {
2399  arc_indices_per_before_node_[before_node].reserve(saving_neighbors);
2400  }
2401  }
2402  skipped_savings_starting_at_.clear();
2403  skipped_savings_starting_at_.resize(size);
2404  skipped_savings_ending_at_.clear();
2405  skipped_savings_ending_at_.resize(size);
2406  incoming_reinjected_savings_ = nullptr;
2407  outgoing_reinjected_savings_ = nullptr;
2408  incoming_new_reinjected_savings_ = nullptr;
2409  outgoing_new_reinjected_savings_ = nullptr;
2410  }
2411 
2412  void AddNewSaving(const Saving& saving, int64_t total_cost,
2413  int64_t before_node, int64_t after_node, int vehicle_type) {
2414  CHECK(!sorted_savings_per_vehicle_type_.empty())
2415  << "Container not initialized!";
2416  sorted_savings_per_vehicle_type_[vehicle_type].push_back(saving);
2417  UpdateArcIndicesCostsAndSavings(before_node, after_node,
2418  {total_cost, saving});
2419  }
2420 
2421  void Sort() {
2422  CHECK(!sorted_) << "Container already sorted!";
2423 
2424  for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
2425  std::sort(savings.begin(), savings.end());
2426  }
2427 
2428  if (single_vehicle_type_) {
2429  const auto& savings = sorted_savings_per_vehicle_type_[0];
2430  sorted_savings_.resize(savings.size());
2431  std::transform(savings.begin(), savings.end(), sorted_savings_.begin(),
2432  [](const Saving& saving) {
2433  return SavingAndArc({saving, /*arc_index*/ -1});
2434  });
2435  } else {
2436  // For each arc, sort the savings by decreasing total cost
2437  // start-->a-->b-->end.
2438  // The best saving for each arc is therefore the last of its vector.
2439  sorted_savings_.reserve(vehicle_types_ *
2440  costs_and_savings_per_arc_.size());
2441 
2442  for (int arc_index = 0; arc_index < costs_and_savings_per_arc_.size();
2443  arc_index++) {
2444  std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
2445  costs_and_savings_per_arc_[arc_index];
2446  DCHECK(!costs_and_savings.empty());
2447 
2448  std::sort(
2449  costs_and_savings.begin(), costs_and_savings.end(),
2450  [](const std::pair<int64_t, Saving>& cs1,
2451  const std::pair<int64_t, Saving>& cs2) { return cs1 > cs2; });
2452 
2453  // Insert all Savings for this arc with the lowest cost into
2454  // sorted_savings_.
2455  // TODO(user): Also do this when reiterating on next_savings_.
2456  const int64_t cost = costs_and_savings.back().first;
2457  while (!costs_and_savings.empty() &&
2458  costs_and_savings.back().first == cost) {
2459  sorted_savings_.push_back(
2460  {costs_and_savings.back().second, arc_index});
2461  costs_and_savings.pop_back();
2462  }
2463  }
2464  std::sort(sorted_savings_.begin(), sorted_savings_.end());
2465  next_saving_type_and_index_for_arc_.clear();
2466  next_saving_type_and_index_for_arc_.resize(
2467  costs_and_savings_per_arc_.size(), {-1, -1});
2468  }
2469  sorted_ = true;
2470  index_in_sorted_savings_ = 0;
2471  to_update_ = false;
2472  }
2473 
2474  bool HasSaving() {
2475  return index_in_sorted_savings_ < sorted_savings_.size() ||
2476  HasReinjectedSavings();
2477  }
2478 
2480  CHECK(sorted_) << "Calling GetSaving() before Sort() !";
2481  CHECK(!to_update_)
2482  << "Update() should be called between two calls to GetSaving() !";
2483 
2484  to_update_ = true;
2485 
2486  if (HasReinjectedSavings()) {
2487  if (incoming_reinjected_savings_ != nullptr &&
2488  outgoing_reinjected_savings_ != nullptr) {
2489  // Get the best Saving among the two.
2490  SavingAndArc& incoming_saving = incoming_reinjected_savings_->front();
2491  SavingAndArc& outgoing_saving = outgoing_reinjected_savings_->front();
2492  if (incoming_saving < outgoing_saving) {
2493  current_saving_ = incoming_saving;
2494  using_incoming_reinjected_saving_ = true;
2495  } else {
2496  current_saving_ = outgoing_saving;
2497  using_incoming_reinjected_saving_ = false;
2498  }
2499  } else {
2500  if (incoming_reinjected_savings_ != nullptr) {
2501  current_saving_ = incoming_reinjected_savings_->front();
2502  using_incoming_reinjected_saving_ = true;
2503  }
2504  if (outgoing_reinjected_savings_ != nullptr) {
2505  current_saving_ = outgoing_reinjected_savings_->front();
2506  using_incoming_reinjected_saving_ = false;
2507  }
2508  }
2509  } else {
2510  current_saving_ = sorted_savings_[index_in_sorted_savings_];
2511  }
2512  return current_saving_.saving;
2513  }
2514 
2515  void Update(bool update_best_saving, int type = -1) {
2516  CHECK(to_update_) << "Container already up to date!";
2517  if (update_best_saving) {
2518  const int64_t arc_index = current_saving_.arc_index;
2519  UpdateNextAndSkippedSavingsForArcWithType(arc_index, type);
2520  }
2521  if (!HasReinjectedSavings()) {
2522  index_in_sorted_savings_++;
2523 
2524  if (index_in_sorted_savings_ == sorted_savings_.size()) {
2525  sorted_savings_.swap(next_savings_);
2526  gtl::STLClearObject(&next_savings_);
2527  index_in_sorted_savings_ = 0;
2528 
2529  std::sort(sorted_savings_.begin(), sorted_savings_.end());
2530  next_saving_type_and_index_for_arc_.clear();
2531  next_saving_type_and_index_for_arc_.resize(
2532  costs_and_savings_per_arc_.size(), {-1, -1});
2533  }
2534  }
2535  UpdateReinjectedSavings();
2536  to_update_ = false;
2537  }
2538 
2539  void UpdateWithType(int type) {
2540  CHECK(!single_vehicle_type_);
2541  Update(/*update_best_saving*/ true, type);
2542  }
2543 
2544  const std::vector<Saving>& GetSortedSavingsForVehicleType(int type) {
2545  CHECK(sorted_) << "Savings not sorted yet!";
2546  CHECK_LT(type, vehicle_types_);
2547  return sorted_savings_per_vehicle_type_[type];
2548  }
2549 
2551  CHECK(outgoing_new_reinjected_savings_ == nullptr);
2552  outgoing_new_reinjected_savings_ = &(skipped_savings_starting_at_[node]);
2553  }
2554 
2555  void ReinjectSkippedSavingsEndingAt(int64_t node) {
2556  CHECK(incoming_new_reinjected_savings_ == nullptr);
2557  incoming_new_reinjected_savings_ = &(skipped_savings_ending_at_[node]);
2558  }
2559 
2560  private:
2561  struct SavingAndArc {
2562  Saving saving;
2563  int64_t arc_index;
2564 
2565  bool operator<(const SavingAndArc& other) const {
2566  return std::tie(saving, arc_index) <
2567  std::tie(other.saving, other.arc_index);
2568  }
2569  };
2570 
2571  // Skips the Saving for the arc before_node-->after_node, by adding it to the
2572  // skipped_savings_ vector of the nodes, if they're uncontained.
2573  void SkipSavingForArc(const SavingAndArc& saving_and_arc) {
2574  const Saving& saving = saving_and_arc.saving;
2575  const int64_t before_node = savings_db_->GetBeforeNodeFromSaving(saving);
2576  const int64_t after_node = savings_db_->GetAfterNodeFromSaving(saving);
2577  if (!savings_db_->Contains(before_node)) {
2578  skipped_savings_starting_at_[before_node].push_back(saving_and_arc);
2579  }
2580  if (!savings_db_->Contains(after_node)) {
2581  skipped_savings_ending_at_[after_node].push_back(saving_and_arc);
2582  }
2583  }
2584 
2585  // Called within Update() when update_best_saving is true, this method updates
2586  // the next_savings_ and skipped savings vectors for a given arc_index and
2587  // vehicle type.
2588  // When a Saving with the right type has already been added to next_savings_
2589  // for this arc, no action is needed on next_savings_.
2590  // Otherwise, if such a Saving exists, GetNextSavingForArcWithType() will find
2591  // and assign it to next_saving, which is then used to update next_savings_.
2592  // Finally, the right Saving is skipped for this arc: if looking for a
2593  // specific type (i.e. type != -1), next_saving (which has the correct type)
2594  // is skipped, otherwise the current_saving_ is.
2595  void UpdateNextAndSkippedSavingsForArcWithType(int64_t arc_index, int type) {
2596  if (single_vehicle_type_) {
2597  // No next Saving, skip the current Saving.
2598  CHECK_EQ(type, -1);
2599  SkipSavingForArc(current_saving_);
2600  return;
2601  }
2602  CHECK_GE(arc_index, 0);
2603  auto& type_and_index = next_saving_type_and_index_for_arc_[arc_index];
2604  const int previous_index = type_and_index.second;
2605  const int previous_type = type_and_index.first;
2606  bool next_saving_added = false;
2607  Saving next_saving;
2608 
2609  if (previous_index >= 0) {
2610  // Next Saving already added for this arc.
2611  DCHECK_GE(previous_type, 0);
2612  if (type == -1 || previous_type == type) {
2613  // Not looking for a specific type, or correct type already in
2614  // next_savings_.
2615  next_saving_added = true;
2616  next_saving = next_savings_[previous_index].saving;
2617  }
2618  }
2619 
2620  if (!next_saving_added &&
2621  GetNextSavingForArcWithType(arc_index, type, &next_saving)) {
2622  type_and_index.first = savings_db_->GetVehicleTypeFromSaving(next_saving);
2623  if (previous_index >= 0) {
2624  // Update the previous saving.
2625  next_savings_[previous_index] = {next_saving, arc_index};
2626  } else {
2627  // Insert the new next Saving for this arc.
2628  type_and_index.second = next_savings_.size();
2629  next_savings_.push_back({next_saving, arc_index});
2630  }
2631  next_saving_added = true;
2632  }
2633 
2634  // Skip the Saving based on the vehicle type.
2635  if (type == -1) {
2636  // Skip the current Saving.
2637  SkipSavingForArc(current_saving_);
2638  } else {
2639  // Skip the Saving with the correct type, already added to next_savings_
2640  // if it was found.
2641  if (next_saving_added) {
2642  SkipSavingForArc({next_saving, arc_index});
2643  }
2644  }
2645  }
2646 
2647  void UpdateReinjectedSavings() {
2648  UpdateGivenReinjectedSavings(incoming_new_reinjected_savings_,
2649  &incoming_reinjected_savings_,
2650  using_incoming_reinjected_saving_);
2651  UpdateGivenReinjectedSavings(outgoing_new_reinjected_savings_,
2652  &outgoing_reinjected_savings_,
2653  !using_incoming_reinjected_saving_);
2654  incoming_new_reinjected_savings_ = nullptr;
2655  outgoing_new_reinjected_savings_ = nullptr;
2656  }
2657 
2658  void UpdateGivenReinjectedSavings(
2659  std::deque<SavingAndArc>* new_reinjected_savings,
2660  std::deque<SavingAndArc>** reinjected_savings,
2661  bool using_reinjected_savings) {
2662  if (new_reinjected_savings == nullptr) {
2663  // No new reinjected savings, update the previous ones if needed.
2664  if (*reinjected_savings != nullptr && using_reinjected_savings) {
2665  CHECK(!(*reinjected_savings)->empty());
2666  (*reinjected_savings)->pop_front();
2667  if ((*reinjected_savings)->empty()) {
2668  *reinjected_savings = nullptr;
2669  }
2670  }
2671  return;
2672  }
2673 
2674  // New savings reinjected.
2675  // Forget about the previous reinjected savings and add the new ones if
2676  // there are any.
2677  if (*reinjected_savings != nullptr) {
2678  (*reinjected_savings)->clear();
2679  }
2680  *reinjected_savings = nullptr;
2681  if (!new_reinjected_savings->empty()) {
2682  *reinjected_savings = new_reinjected_savings;
2683  }
2684  }
2685 
2686  bool HasReinjectedSavings() {
2687  return outgoing_reinjected_savings_ != nullptr ||
2688  incoming_reinjected_savings_ != nullptr;
2689  }
2690 
2691  void UpdateArcIndicesCostsAndSavings(
2692  int64_t before_node, int64_t after_node,
2693  const std::pair<int64_t, Saving>& cost_and_saving) {
2694  if (single_vehicle_type_) {
2695  return;
2696  }
2697  absl::flat_hash_map<int, int>& arc_indices =
2698  arc_indices_per_before_node_[before_node];
2699  const auto& arc_inserted = arc_indices.insert(
2700  std::make_pair(after_node, costs_and_savings_per_arc_.size()));
2701  const int index = arc_inserted.first->second;
2702  if (arc_inserted.second) {
2703  costs_and_savings_per_arc_.push_back({cost_and_saving});
2704  } else {
2705  DCHECK_LT(index, costs_and_savings_per_arc_.size());
2706  costs_and_savings_per_arc_[index].push_back(cost_and_saving);
2707  }
2708  }
2709 
2710  bool GetNextSavingForArcWithType(int64_t arc_index, int type,
2711  Saving* next_saving) {
2712  std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
2713  costs_and_savings_per_arc_[arc_index];
2714 
2715  bool found_saving = false;
2716  while (!costs_and_savings.empty() && !found_saving) {
2717  const Saving& saving = costs_and_savings.back().second;
2718  if (type == -1 || savings_db_->GetVehicleTypeFromSaving(saving) == type) {
2719  *next_saving = saving;
2720  found_saving = true;
2721  }
2722  costs_and_savings.pop_back();
2723  }
2724  return found_saving;
2725  }
2726 
2727  const SavingsFilteredHeuristic* const savings_db_;
2728  const int vehicle_types_;
2729  int64_t index_in_sorted_savings_;
2730  std::vector<std::vector<Saving>> sorted_savings_per_vehicle_type_;
2731  std::vector<SavingAndArc> sorted_savings_;
2732  std::vector<SavingAndArc> next_savings_;
2733  std::vector<std::pair</*type*/ int, /*index*/ int>>
2734  next_saving_type_and_index_for_arc_;
2735  SavingAndArc current_saving_;
2736  const bool single_vehicle_type_;
2737  std::vector<std::vector<std::pair</*cost*/ int64_t, Saving>>>
2738  costs_and_savings_per_arc_;
2739  std::vector<absl::flat_hash_map</*after_node*/ int, /*arc_index*/ int>>
2740  arc_indices_per_before_node_;
2741  std::vector<std::deque<SavingAndArc>> skipped_savings_starting_at_;
2742  std::vector<std::deque<SavingAndArc>> skipped_savings_ending_at_;
2743  std::deque<SavingAndArc>* outgoing_reinjected_savings_;
2744  std::deque<SavingAndArc>* incoming_reinjected_savings_;
2745  bool using_incoming_reinjected_saving_;
2746  std::deque<SavingAndArc>* outgoing_new_reinjected_savings_;
2747  std::deque<SavingAndArc>* incoming_new_reinjected_savings_;
2748  bool sorted_;
2749  bool to_update_;
2750 };
2751 
2752 // SavingsFilteredHeuristic
2753 
2755  RoutingModel* model, const RoutingIndexManager* manager,
2757  : RoutingFilteredHeuristic(model, filter_manager),
2758  vehicle_type_curator_(nullptr),
2759  manager_(manager),
2760  savings_params_(parameters) {
2761  DCHECK_GT(savings_params_.neighbors_ratio, 0);
2762  DCHECK_LE(savings_params_.neighbors_ratio, 1);
2763  DCHECK_GT(savings_params_.max_memory_usage_bytes, 0);
2764  DCHECK_GT(savings_params_.arc_coefficient, 0);
2765  const int size = model->Size();
2766  size_squared_ = size * size;
2767 }
2768 
2770 
2772  if (vehicle_type_curator_ == nullptr) {
2773  vehicle_type_curator_ = absl::make_unique<VehicleTypeCurator>(
2774  model()->GetVehicleTypeContainer());
2775  }
2776  // Only store empty vehicles in the vehicle_type_curator_.
2777  vehicle_type_curator_->Reset(
2778  [this](int vehicle) { return VehicleIsEmpty(vehicle); });
2779  if (!ComputeSavings()) return false;
2781  // Free all the space used to store the Savings in the container.
2782  savings_container_.reset();
2784  if (!Commit()) return false;
2786  return Commit();
2787 }
2788 
2790  int type, int64_t before_node, int64_t after_node) {
2791  auto vehicle_is_compatible = [this, before_node, after_node](int vehicle) {
2792  if (!model()->VehicleVar(before_node)->Contains(vehicle) ||
2793  !model()->VehicleVar(after_node)->Contains(vehicle)) {
2794  return false;
2795  }
2796  // Try to commit the arc on this vehicle.
2797  DCHECK(VehicleIsEmpty(vehicle));
2798  const int64_t start = model()->Start(vehicle);
2799  const int64_t end = model()->End(vehicle);
2800  SetValue(start, before_node);
2801  SetValue(before_node, after_node);
2802  SetValue(after_node, end);
2803  return Commit();
2804  };
2805 
2806  return vehicle_type_curator_
2807  ->GetCompatibleVehicleOfType(
2808  type, vehicle_is_compatible,
2809  /*stop_and_return_vehicle*/ [](int) { return false; })
2810  .first;
2811 }
2812 
2813 void SavingsFilteredHeuristic::AddSymmetricArcsToAdjacencyLists(
2814  std::vector<std::vector<int64_t>>* adjacency_lists) {
2815  for (int64_t node = 0; node < adjacency_lists->size(); node++) {
2816  for (int64_t neighbor : (*adjacency_lists)[node]) {
2817  if (model()->IsStart(neighbor) || model()->IsEnd(neighbor)) {
2818  continue;
2819  }
2820  (*adjacency_lists)[neighbor].push_back(node);
2821  }
2822  }
2823  std::transform(adjacency_lists->begin(), adjacency_lists->end(),
2824  adjacency_lists->begin(), [](std::vector<int64_t> vec) {
2825  std::sort(vec.begin(), vec.end());
2826  vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
2827  return vec;
2828  });
2829 }
2830 
2831 // Computes the savings related to each pair of non-start and non-end nodes.
2832 // The savings value for an arc a-->b for a vehicle starting at node s and
2833 // ending at node e is:
2834 // saving = cost(s-->a-->e) + cost(s-->b-->e) - cost(s-->a-->b-->e), i.e.
2835 // saving = cost(a-->e) + cost(s-->b) - cost(a-->b)
2836 // The saving value also considers a coefficient for the cost of the arc
2837 // a-->b, which results in:
2838 // saving = cost(a-->e) + cost(s-->b) - [arc_coefficient_ * cost(a-->b)]
2839 // The higher this saving value, the better the arc.
2840 // Here, the value stored for the savings is -saving, which are therefore
2841 // considered in decreasing order.
2842 bool SavingsFilteredHeuristic::ComputeSavings() {
2843  const int num_vehicle_types = vehicle_type_curator_->NumTypes();
2844  const int size = model()->Size();
2845 
2846  std::vector<int64_t> uncontained_non_start_end_nodes;
2847  uncontained_non_start_end_nodes.reserve(size);
2848  for (int node = 0; node < size; node++) {
2849  if (!model()->IsStart(node) && !model()->IsEnd(node) && !Contains(node)) {
2850  uncontained_non_start_end_nodes.push_back(node);
2851  }
2852  }
2853 
2854  const int64_t saving_neighbors =
2855  std::min(MaxNumNeighborsPerNode(num_vehicle_types),
2856  static_cast<int64_t>(uncontained_non_start_end_nodes.size()));
2857 
2859  absl::make_unique<SavingsContainer<Saving>>(this, num_vehicle_types);
2860  savings_container_->InitializeContainer(size, saving_neighbors);
2861  if (StopSearch()) return false;
2862  std::vector<std::vector<int64_t>> adjacency_lists(size);
2863 
2864  for (int type = 0; type < num_vehicle_types; ++type) {
2865  const int vehicle =
2866  vehicle_type_curator_->GetLowestFixedCostVehicleOfType(type);
2867  if (vehicle < 0) {
2868  continue;
2869  }
2870 
2871  const int64_t cost_class =
2872  model()->GetCostClassIndexOfVehicle(vehicle).value();
2873  const int64_t start = model()->Start(vehicle);
2874  const int64_t end = model()->End(vehicle);
2875  const int64_t fixed_cost = model()->GetFixedCostOfVehicle(vehicle);
2876 
2877  // Compute the neighbors for each non-start/end node not already inserted in
2878  // the model.
2879  for (int before_node : uncontained_non_start_end_nodes) {
2880  std::vector<std::pair</*cost*/ int64_t, /*node*/ int64_t>>
2881  costed_after_nodes;
2882  costed_after_nodes.reserve(uncontained_non_start_end_nodes.size());
2883  if (StopSearch()) return false;
2884  for (int after_node : uncontained_non_start_end_nodes) {
2885  if (after_node != before_node) {
2886  costed_after_nodes.push_back(std::make_pair(
2887  model()->GetArcCostForClass(before_node, after_node, cost_class),
2888  after_node));
2889  }
2890  }
2891  if (saving_neighbors < costed_after_nodes.size()) {
2892  std::nth_element(costed_after_nodes.begin(),
2893  costed_after_nodes.begin() + saving_neighbors,
2894  costed_after_nodes.end());
2895  costed_after_nodes.resize(saving_neighbors);
2896  }
2897  adjacency_lists[before_node].resize(costed_after_nodes.size());
2898  std::transform(costed_after_nodes.begin(), costed_after_nodes.end(),
2899  adjacency_lists[before_node].begin(),
2900  [](std::pair<int64_t, int64_t> cost_and_node) {
2901  return cost_and_node.second;
2902  });
2903  }
2904  if (savings_params_.add_reverse_arcs) {
2905  AddSymmetricArcsToAdjacencyLists(&adjacency_lists);
2906  }
2907  if (StopSearch()) return false;
2908 
2909  // Build the savings for this vehicle type given the adjacency_lists.
2910  for (int before_node : uncontained_non_start_end_nodes) {
2911  const int64_t before_to_end_cost =
2912  model()->GetArcCostForClass(before_node, end, cost_class);
2913  const int64_t start_to_before_cost =
2914  CapSub(model()->GetArcCostForClass(start, before_node, cost_class),
2915  fixed_cost);
2916  if (StopSearch()) return false;
2917  for (int64_t after_node : adjacency_lists[before_node]) {
2918  if (model()->IsStart(after_node) || model()->IsEnd(after_node) ||
2919  before_node == after_node || Contains(after_node)) {
2920  continue;
2921  }
2922  const int64_t arc_cost =
2923  model()->GetArcCostForClass(before_node, after_node, cost_class);
2924  const int64_t start_to_after_cost =
2925  CapSub(model()->GetArcCostForClass(start, after_node, cost_class),
2926  fixed_cost);
2927  const int64_t after_to_end_cost =
2928  model()->GetArcCostForClass(after_node, end, cost_class);
2929 
2930  const double weighted_arc_cost_fp =
2931  savings_params_.arc_coefficient * arc_cost;
2932  const int64_t weighted_arc_cost =
2934  ? static_cast<int64_t>(weighted_arc_cost_fp)
2935  : std::numeric_limits<int64_t>::max();
2936  const int64_t saving_value = CapSub(
2937  CapAdd(before_to_end_cost, start_to_after_cost), weighted_arc_cost);
2938 
2939  const Saving saving =
2940  BuildSaving(-saving_value, type, before_node, after_node);
2941 
2942  const int64_t total_cost =
2943  CapAdd(CapAdd(start_to_before_cost, arc_cost), after_to_end_cost);
2944 
2945  savings_container_->AddNewSaving(saving, total_cost, before_node,
2946  after_node, type);
2947  }
2948  }
2949  }
2950  savings_container_->Sort();
2951  return !StopSearch();
2952 }
2953 
2954 int64_t SavingsFilteredHeuristic::MaxNumNeighborsPerNode(
2955  int num_vehicle_types) const {
2956  const int64_t size = model()->Size();
2957 
2958  const int64_t num_neighbors_with_ratio =
2959  std::max(1.0, size * savings_params_.neighbors_ratio);
2960 
2961  // A single Saving takes 2*8 bytes of memory.
2962  // max_memory_usage_in_savings_unit = num_savings * multiplicative_factor,
2963  // Where multiplicative_factor is the memory taken (in Savings unit) for each
2964  // computed Saving.
2965  const double max_memory_usage_in_savings_unit =
2966  savings_params_.max_memory_usage_bytes / 16;
2967 
2968  // In the SavingsContainer, for each Saving, the Savings are stored:
2969  // - Once in "sorted_savings_per_vehicle_type", and (at most) once in
2970  // "sorted_savings_" --> factor 2
2971  // - If num_vehicle_types > 1, they're also stored by arc_index in
2972  // "costs_and_savings_per_arc", along with their int64_t cost --> factor 1.5
2973  //
2974  // On top of that,
2975  // - In the sequential version, the Saving* are also stored by in-coming and
2976  // outgoing node (in in/out_savings_ptr), adding another 2*8 bytes per
2977  // Saving --> factor 1.
2978  // - In the parallel version, skipped Savings are also stored in
2979  // skipped_savings_starting/ending_at_, resulting in a maximum added factor
2980  // of 2 for each Saving.
2981  // These extra factors are given by ExtraSavingsMemoryMultiplicativeFactor().
2982  double multiplicative_factor = 2.0 + ExtraSavingsMemoryMultiplicativeFactor();
2983  if (num_vehicle_types > 1) {
2984  multiplicative_factor += 1.5;
2985  }
2986  const double num_savings =
2987  max_memory_usage_in_savings_unit / multiplicative_factor;
2988  const int64_t num_neighbors_with_memory_restriction =
2989  std::max(1.0, num_savings / (num_vehicle_types * size));
2990 
2991  return std::min(num_neighbors_with_ratio,
2992  num_neighbors_with_memory_restriction);
2993 }
2994 
2995 // SequentialSavingsFilteredHeuristic
2996 
2997 void SequentialSavingsFilteredHeuristic::BuildRoutesFromSavings() {
2998  const int vehicle_types = vehicle_type_curator_->NumTypes();
2999  DCHECK_GT(vehicle_types, 0);
3000  const int size = model()->Size();
3001  // Store savings for each incoming and outgoing node and by vehicle type. This
3002  // is necessary to quickly extend partial chains without scanning all savings.
3003  std::vector<std::vector<const Saving*>> in_savings_ptr(size * vehicle_types);
3004  std::vector<std::vector<const Saving*>> out_savings_ptr(size * vehicle_types);
3005  for (int type = 0; type < vehicle_types; type++) {
3006  const int vehicle_type_offset = type * size;
3007  const std::vector<Saving>& sorted_savings_for_type =
3008  savings_container_->GetSortedSavingsForVehicleType(type);
3009  for (const Saving& saving : sorted_savings_for_type) {
3010  DCHECK_EQ(GetVehicleTypeFromSaving(saving), type);
3011  const int before_node = GetBeforeNodeFromSaving(saving);
3012  in_savings_ptr[vehicle_type_offset + before_node].push_back(&saving);
3013  const int after_node = GetAfterNodeFromSaving(saving);
3014  out_savings_ptr[vehicle_type_offset + after_node].push_back(&saving);
3015  }
3016  }
3017 
3018  // Build routes from savings.
3019  while (savings_container_->HasSaving()) {
3020  if (StopSearch()) return;
3021  // First find the best saving to start a new route.
3022  const Saving saving = savings_container_->GetSaving();
3023  int before_node = GetBeforeNodeFromSaving(saving);
3024  int after_node = GetAfterNodeFromSaving(saving);
3025  const bool nodes_not_contained =
3026  !Contains(before_node) && !Contains(after_node);
3027 
3028  bool committed = false;
3029 
3030  if (nodes_not_contained) {
3031  // Find the right vehicle to start the route with this Saving.
3032  const int type = GetVehicleTypeFromSaving(saving);
3033  const int vehicle =
3034  StartNewRouteWithBestVehicleOfType(type, before_node, after_node);
3035 
3036  if (vehicle >= 0) {
3037  committed = true;
3038  const int64_t start = model()->Start(vehicle);
3039  const int64_t end = model()->End(vehicle);
3040  // Then extend the route from both ends of the partial route.
3041  int in_index = 0;
3042  int out_index = 0;
3043  const int saving_offset = type * size;
3044 
3045  while (in_index < in_savings_ptr[saving_offset + after_node].size() ||
3046  out_index <
3047  out_savings_ptr[saving_offset + before_node].size()) {
3048  if (StopSearch()) return;
3049  // First determine how to extend the route.
3050  int before_before_node = -1;
3051  int after_after_node = -1;
3052  if (in_index < in_savings_ptr[saving_offset + after_node].size()) {
3053  const Saving& in_saving =
3054  *(in_savings_ptr[saving_offset + after_node][in_index]);
3055  if (out_index <
3056  out_savings_ptr[saving_offset + before_node].size()) {
3057  const Saving& out_saving =
3058  *(out_savings_ptr[saving_offset + before_node][out_index]);
3059  if (GetSavingValue(in_saving) < GetSavingValue(out_saving)) {
3060  // Should extend after after_node
3061  after_after_node = GetAfterNodeFromSaving(in_saving);
3062  } else {
3063  // Should extend before before_node
3064  before_before_node = GetBeforeNodeFromSaving(out_saving);
3065  }
3066  } else {
3067  // Should extend after after_node
3068  after_after_node = GetAfterNodeFromSaving(in_saving);
3069  }
3070  } else {
3071  // Should extend before before_node
3072  before_before_node = GetBeforeNodeFromSaving(
3073  *(out_savings_ptr[saving_offset + before_node][out_index]));
3074  }
3075  // Extend the route
3076  if (after_after_node != -1) {
3077  DCHECK_EQ(before_before_node, -1);
3078  // Extending after after_node
3079  if (!Contains(after_after_node)) {
3080  SetValue(after_node, after_after_node);
3081  SetValue(after_after_node, end);
3082  if (Commit()) {
3083  in_index = 0;
3084  after_node = after_after_node;
3085  } else {
3086  ++in_index;
3087  }
3088  } else {
3089  ++in_index;
3090  }
3091  } else {
3092  // Extending before before_node
3093  CHECK_GE(before_before_node, 0);
3094  if (!Contains(before_before_node)) {
3095  SetValue(start, before_before_node);
3096  SetValue(before_before_node, before_node);
3097  if (Commit()) {
3098  out_index = 0;
3099  before_node = before_before_node;
3100  } else {
3101  ++out_index;
3102  }
3103  } else {
3104  ++out_index;
3105  }
3106  }
3107  }
3108  }
3109  }
3110  savings_container_->Update(nodes_not_contained && !committed);
3111  }
3112 }
3113 
3114 // ParallelSavingsFilteredHeuristic
3115 
3116 void ParallelSavingsFilteredHeuristic::BuildRoutesFromSavings() {
3117  // Initialize the vehicles of the first/last non start/end nodes served by
3118  // each route.
3119  const int64_t size = model()->Size();
3120  const int vehicles = model()->vehicles();
3121 
3122  first_node_on_route_.resize(vehicles, -1);
3123  last_node_on_route_.resize(vehicles, -1);
3124  vehicle_of_first_or_last_node_.resize(size, -1);
3125 
3126  for (int vehicle = 0; vehicle < vehicles; vehicle++) {
3127  const int64_t start = model()->Start(vehicle);
3128  const int64_t end = model()->End(vehicle);
3129  if (!Contains(start)) {
3130  continue;
3131  }
3132  int64_t node = Value(start);
3133  if (node != end) {
3134  vehicle_of_first_or_last_node_[node] = vehicle;
3135  first_node_on_route_[vehicle] = node;
3136 
3137  int64_t next = Value(node);
3138  while (next != end) {
3139  node = next;
3140  next = Value(node);
3141  }
3142  vehicle_of_first_or_last_node_[node] = vehicle;
3143  last_node_on_route_[vehicle] = node;
3144  }
3145  }
3146 
3147  while (savings_container_->HasSaving()) {
3148  if (StopSearch()) return;
3149  const Saving saving = savings_container_->GetSaving();
3150  const int64_t before_node = GetBeforeNodeFromSaving(saving);
3151  const int64_t after_node = GetAfterNodeFromSaving(saving);
3152  const int type = GetVehicleTypeFromSaving(saving);
3153 
3154  if (!Contains(before_node) && !Contains(after_node)) {
3155  // Neither nodes are contained, start a new route.
3156  bool committed = false;
3157 
3158  const int vehicle =
3159  StartNewRouteWithBestVehicleOfType(type, before_node, after_node);
3160 
3161  if (vehicle >= 0) {
3162  committed = true;
3163  // Store before_node and after_node as first and last nodes of the route
3164  vehicle_of_first_or_last_node_[before_node] = vehicle;
3165  vehicle_of_first_or_last_node_[after_node] = vehicle;
3166  first_node_on_route_[vehicle] = before_node;
3167  last_node_on_route_[vehicle] = after_node;
3168  savings_container_->ReinjectSkippedSavingsStartingAt(after_node);
3169  savings_container_->ReinjectSkippedSavingsEndingAt(before_node);
3170  }
3171  savings_container_->Update(!committed);
3172  continue;
3173  }
3174 
3175  if (Contains(before_node) && Contains(after_node)) {
3176  // Merge the two routes if before_node is last and after_node first of its
3177  // route, the two nodes aren't already on the same route, and the vehicle
3178  // types are compatible.
3179  const int v1 = vehicle_of_first_or_last_node_[before_node];
3180  const int64_t last_node = v1 == -1 ? -1 : last_node_on_route_[v1];
3181 
3182  const int v2 = vehicle_of_first_or_last_node_[after_node];
3183  const int64_t first_node = v2 == -1 ? -1 : first_node_on_route_[v2];
3184 
3185  if (before_node == last_node && after_node == first_node && v1 != v2 &&
3186  vehicle_type_curator_->Type(v1) == vehicle_type_curator_->Type(v2)) {
3187  CHECK_EQ(Value(before_node), model()->End(v1));
3188  CHECK_EQ(Value(model()->Start(v2)), after_node);
3189 
3190  // We try merging the two routes.
3191  // TODO(user): Try to use skipped savings to start new routes when
3192  // a vehicle becomes available after a merge (not trivial because it can
3193  // result in an infinite loop).
3194  MergeRoutes(v1, v2, before_node, after_node);
3195  }
3196  }
3197 
3198  if (Contains(before_node) && !Contains(after_node)) {
3199  const int vehicle = vehicle_of_first_or_last_node_[before_node];
3200  const int64_t last_node =
3201  vehicle == -1 ? -1 : last_node_on_route_[vehicle];
3202 
3203  if (before_node == last_node) {
3204  const int64_t end = model()->End(vehicle);
3205  CHECK_EQ(Value(before_node), end);
3206 
3207  const int route_type = vehicle_type_curator_->Type(vehicle);
3208  if (type != route_type) {
3209  // The saving doesn't correspond to the type of the vehicle serving
3210  // before_node. We update the container with the correct type.
3211  savings_container_->UpdateWithType(route_type);
3212  continue;
3213  }
3214 
3215  // Try adding after_node on route of before_node.
3216  SetValue(before_node, after_node);
3217  SetValue(after_node, end);
3218  if (Commit()) {
3219  if (first_node_on_route_[vehicle] != before_node) {
3220  // before_node is no longer the start or end of its route
3221  DCHECK_NE(Value(model()->Start(vehicle)), before_node);
3222  vehicle_of_first_or_last_node_[before_node] = -1;
3223  }
3224  vehicle_of_first_or_last_node_[after_node] = vehicle;
3225  last_node_on_route_[vehicle] = after_node;
3226  savings_container_->ReinjectSkippedSavingsStartingAt(after_node);
3227  }
3228  }
3229  }
3230 
3231  if (!Contains(before_node) && Contains(after_node)) {
3232  const int vehicle = vehicle_of_first_or_last_node_[after_node];
3233  const int64_t first_node =
3234  vehicle == -1 ? -1 : first_node_on_route_[vehicle];
3235 
3236  if (after_node == first_node) {
3237  const int64_t start = model()->Start(vehicle);
3238  CHECK_EQ(Value(start), after_node);
3239 
3240  const int route_type = vehicle_type_curator_->Type(vehicle);
3241  if (type != route_type) {
3242  // The saving doesn't correspond to the type of the vehicle serving
3243  // after_node. We update the container with the correct type.
3244  savings_container_->UpdateWithType(route_type);
3245  continue;
3246  }
3247 
3248  // Try adding before_node on route of after_node.
3249  SetValue(before_node, after_node);
3250  SetValue(start, before_node);
3251  if (Commit()) {
3252  if (last_node_on_route_[vehicle] != after_node) {
3253  // after_node is no longer the start or end of its route
3254  DCHECK_NE(Value(after_node), model()->End(vehicle));
3255  vehicle_of_first_or_last_node_[after_node] = -1;
3256  }
3257  vehicle_of_first_or_last_node_[before_node] = vehicle;
3258  first_node_on_route_[vehicle] = before_node;
3259  savings_container_->ReinjectSkippedSavingsEndingAt(before_node);
3260  }
3261  }
3262  }
3263  savings_container_->Update(/*update_best_saving*/ false);
3264  }
3265 }
3266 
3267 void ParallelSavingsFilteredHeuristic::MergeRoutes(int first_vehicle,
3268  int second_vehicle,
3269  int64_t before_node,
3270  int64_t after_node) {
3271  if (StopSearch()) return;
3272  const int64_t new_first_node = first_node_on_route_[first_vehicle];
3273  DCHECK_EQ(vehicle_of_first_or_last_node_[new_first_node], first_vehicle);
3274  CHECK_EQ(Value(model()->Start(first_vehicle)), new_first_node);
3275  const int64_t new_last_node = last_node_on_route_[second_vehicle];
3276  DCHECK_EQ(vehicle_of_first_or_last_node_[new_last_node], second_vehicle);
3277  CHECK_EQ(Value(new_last_node), model()->End(second_vehicle));
3278 
3279  // Select the vehicle with lower fixed cost to merge the routes.
3280  int used_vehicle = first_vehicle;
3281  int unused_vehicle = second_vehicle;
3282  if (model()->GetFixedCostOfVehicle(first_vehicle) >
3283  model()->GetFixedCostOfVehicle(second_vehicle)) {
3284  used_vehicle = second_vehicle;
3285  unused_vehicle = first_vehicle;
3286  }
3287 
3288  SetValue(before_node, after_node);
3289  SetValue(model()->Start(unused_vehicle), model()->End(unused_vehicle));
3290  if (used_vehicle == first_vehicle) {
3291  SetValue(new_last_node, model()->End(used_vehicle));
3292  } else {
3293  SetValue(model()->Start(used_vehicle), new_first_node);
3294  }
3295  bool committed = Commit();
3296  if (!committed &&
3297  model()->GetVehicleClassIndexOfVehicle(first_vehicle).value() !=
3298  model()->GetVehicleClassIndexOfVehicle(second_vehicle).value()) {
3299  // Try committing on other vehicle instead.
3300  std::swap(used_vehicle, unused_vehicle);
3301  SetValue(before_node, after_node);
3302  SetValue(model()->Start(unused_vehicle), model()->End(unused_vehicle));
3303  if (used_vehicle == first_vehicle) {
3304  SetValue(new_last_node, model()->End(used_vehicle));
3305  } else {
3306  SetValue(model()->Start(used_vehicle), new_first_node);
3307  }
3308  committed = Commit();
3309  }
3310  if (committed) {
3311  // Make unused_vehicle available
3312  vehicle_type_curator_->ReinjectVehicleOfClass(
3313  unused_vehicle,
3314  model()->GetVehicleClassIndexOfVehicle(unused_vehicle).value(),
3315  model()->GetFixedCostOfVehicle(unused_vehicle));
3316 
3317  // Update the first and last nodes on vehicles.
3318  first_node_on_route_[unused_vehicle] = -1;
3319  last_node_on_route_[unused_vehicle] = -1;
3320  vehicle_of_first_or_last_node_[before_node] = -1;
3321  vehicle_of_first_or_last_node_[after_node] = -1;
3322  first_node_on_route_[used_vehicle] = new_first_node;
3323  last_node_on_route_[used_vehicle] = new_last_node;
3324  vehicle_of_first_or_last_node_[new_last_node] = used_vehicle;
3325  vehicle_of_first_or_last_node_[new_first_node] = used_vehicle;
3326  }
3327 }
3328 
3329 // ChristofidesFilteredHeuristic
3330 
3332  RoutingModel* model, LocalSearchFilterManager* filter_manager,
3333  bool use_minimum_matching)
3334  : RoutingFilteredHeuristic(model, filter_manager),
3335  use_minimum_matching_(use_minimum_matching) {}
3336 
3337 // TODO(user): Support pickup & delivery.
3339  const int size = model()->Size() - model()->vehicles() + 1;
3340  // Node indices for Christofides solver.
3341  // 0: start/end node
3342  // >0: non start/end nodes
3343  // TODO(user): Add robustness to fixed arcs by collapsing them into meta-
3344  // nodes.
3345  std::vector<int> indices(1, 0);
3346  for (int i = 1; i < size; ++i) {
3347  if (!model()->IsStart(i) && !model()->IsEnd(i)) {
3348  indices.push_back(i);
3349  }
3350  }
3351  const int num_cost_classes = model()->GetCostClassesCount();
3352  std::vector<std::vector<int>> path_per_cost_class(num_cost_classes);
3353  std::vector<bool> class_covered(num_cost_classes, false);
3354  for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
3355  const int64_t cost_class =
3356  model()->GetCostClassIndexOfVehicle(vehicle).value();
3357  if (!class_covered[cost_class]) {
3358  class_covered[cost_class] = true;
3359  const int64_t start = model()->Start(vehicle);
3360  const int64_t end = model()->End(vehicle);
3361  auto cost = [this, &indices, start, end, cost_class](int from, int to) {
3362  DCHECK_LT(from, indices.size());
3363  DCHECK_LT(to, indices.size());
3364  const int from_index = (from == 0) ? start : indices[from];
3365  const int to_index = (to == 0) ? end : indices[to];
3366  const int64_t cost =
3367  model()->GetArcCostForClass(from_index, to_index, cost_class);
3368  // To avoid overflow issues, capping costs at kint64max/2, the maximum
3369  // value supported by MinCostPerfectMatching.
3370  // TODO(user): Investigate if ChristofidesPathSolver should not
3371  // return a status to bail out fast in case of problem.
3373  };
3374  using Cost = decltype(cost);
3376  indices.size(), cost);
3377  if (use_minimum_matching_) {
3378  christofides_solver.SetMatchingAlgorithm(
3380  MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING);
3381  }
3382  if (christofides_solver.Solve()) {
3383  path_per_cost_class[cost_class] =
3384  christofides_solver.TravelingSalesmanPath();
3385  }
3386  }
3387  }
3388  // TODO(user): Investigate if sorting paths per cost improves solutions.
3389  for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
3390  const int64_t cost_class =
3391  model()->GetCostClassIndexOfVehicle(vehicle).value();
3392  const std::vector<int>& path = path_per_cost_class[cost_class];
3393  if (path.empty()) continue;
3394  DCHECK_EQ(0, path[0]);
3395  DCHECK_EQ(0, path.back());
3396  // Extend route from start.
3397  int prev = GetStartChainEnd(vehicle);
3398  const int end = model()->End(vehicle);
3399  for (int i = 1; i < path.size() - 1 && prev != end; ++i) {
3400  if (StopSearch()) return false;
3401  int next = indices[path[i]];
3402  if (!Contains(next)) {
3403  SetValue(prev, next);
3404  SetValue(next, end);
3405  if (Commit()) {
3406  prev = next;
3407  }
3408  }
3409  }
3410  }
3412  return Commit();
3413 }
3414 
3415 // Sweep heuristic
3416 // TODO(user): Clean up to match other first solution strategies.
3417 
3418 namespace {
3419 struct SweepIndex {
3420  SweepIndex(const int64_t index, const double angle, const double distance)
3422  ~SweepIndex() {}
3423 
3424  int64_t index;
3425  double angle;
3426  double distance;
3427 };
3428 
3429 struct SweepIndexSortAngle {
3430  bool operator()(const SweepIndex& node1, const SweepIndex& node2) const {
3431  return (node1.angle < node2.angle);
3432  }
3433 } SweepIndexAngleComparator;
3434 
3435 struct SweepIndexSortDistance {
3436  bool operator()(const SweepIndex& node1, const SweepIndex& node2) const {
3437  return (node1.distance < node2.distance);
3438  }
3439 } SweepIndexDistanceComparator;
3440 } // namespace
3441 
3443  const std::vector<std::pair<int64_t, int64_t>>& points)
3444  : coordinates_(2 * points.size(), 0), sectors_(1) {
3445  for (int64_t i = 0; i < points.size(); ++i) {
3446  coordinates_[2 * i] = points[i].first;
3447  coordinates_[2 * i + 1] = points[i].second;
3448  }
3449 }
3450 
3451 // Splits the space of the indices into sectors and sorts the indices of each
3452 // sector with ascending angle from the depot.
3453 void SweepArranger::ArrangeIndices(std::vector<int64_t>* indices) {
3454  const double pi_rad = 3.14159265;
3455  // Suppose that the center is at x0, y0.
3456  const int x0 = coordinates_[0];
3457  const int y0 = coordinates_[1];
3458 
3459  std::vector<SweepIndex> sweep_indices;
3460  for (int64_t index = 0; index < static_cast<int>(coordinates_.size()) / 2;
3461  ++index) {
3462  const int x = coordinates_[2 * index];
3463  const int y = coordinates_[2 * index + 1];
3464  const double x_delta = x - x0;
3465  const double y_delta = y - y0;
3466  double square_distance = x_delta * x_delta + y_delta * y_delta;
3467  double angle = square_distance == 0 ? 0 : std::atan2(y_delta, x_delta);
3468  angle = angle >= 0 ? angle : 2 * pi_rad + angle;
3469  SweepIndex sweep_index(index, angle, square_distance);
3470  sweep_indices.push_back(sweep_index);
3471  }
3472  std::sort(sweep_indices.begin(), sweep_indices.end(),
3473  SweepIndexDistanceComparator);
3474 
3475  const int size = static_cast<int>(sweep_indices.size()) / sectors_;
3476  for (int sector = 0; sector < sectors_; ++sector) {
3477  std::vector<SweepIndex> cluster;
3478  std::vector<SweepIndex>::iterator begin =
3479  sweep_indices.begin() + sector * size;
3480  std::vector<SweepIndex>::iterator end =
3481  sector == sectors_ - 1 ? sweep_indices.end()
3482  : sweep_indices.begin() + (sector + 1) * size;
3483  std::sort(begin, end, SweepIndexAngleComparator);
3484  }
3485  for (const SweepIndex& sweep_index : sweep_indices) {
3486  indices->push_back(sweep_index.index);
3487  }
3488 }
3489 
3490 namespace {
3491 
3492 struct Link {
3493  Link(std::pair<int, int> link, double value, int vehicle_class,
3494  int64_t start_depot, int64_t end_depot)
3495  : link(link),
3496  value(value),
3499  end_depot(end_depot) {}
3500  ~Link() {}
3501 
3502  std::pair<int, int> link;
3503  int64_t value;
3505  int64_t start_depot;
3506  int64_t end_depot;
3507 };
3508 
3509 // The RouteConstructor creates the routes of a VRP instance subject to its
3510 // constraints by iterating on a list of arcs appearing in descending order
3511 // of priority.
3512 // TODO(user): Use the dimension class in this class.
3513 // TODO(user): Add support for vehicle-dependent dimension transits.
3514 class RouteConstructor {
3515  public:
3516  RouteConstructor(Assignment* const assignment, RoutingModel* const model,
3517  bool check_assignment, int64_t num_indices,
3518  const std::vector<Link>& links_list)
3519  : assignment_(assignment),
3520  model_(model),
3521  check_assignment_(check_assignment),
3522  solver_(model_->solver()),
3523  num_indices_(num_indices),
3524  links_list_(links_list),
3525  nexts_(model_->Nexts()),
3526  in_route_(num_indices_, -1),
3527  final_routes_(),
3528  index_to_chain_index_(num_indices, -1),
3529  index_to_vehicle_class_index_(num_indices, -1) {
3530  {
3531  const std::vector<std::string> dimension_names =
3532  model_->GetAllDimensionNames();
3533  dimensions_.assign(dimension_names.size(), nullptr);
3534  for (int i = 0; i < dimension_names.size(); ++i) {
3535  dimensions_[i] = &model_->GetDimensionOrDie(dimension_names[i]);
3536  }
3537  }
3538  cumuls_.resize(dimensions_.size());
3539  for (std::vector<int64_t>& cumuls : cumuls_) {
3540  cumuls.resize(num_indices_);
3541  }
3542  new_possible_cumuls_.resize(dimensions_.size());
3543  }
3544 
3545  ~RouteConstructor() {}
3546 
3547  void Construct() {
3548  model_->solver()->TopPeriodicCheck();
3549  // Initial State: Each order is served by its own vehicle.
3550  for (int index = 0; index < num_indices_; ++index) {
3551  if (!model_->IsStart(index) && !model_->IsEnd(index)) {
3552  std::vector<int> route(1, index);
3553  routes_.push_back(route);
3554  in_route_[index] = routes_.size() - 1;
3555  }
3556  }
3557 
3558  for (const Link& link : links_list_) {
3559  model_->solver()->TopPeriodicCheck();
3560  const int index1 = link.link.first;
3561  const int index2 = link.link.second;
3562  const int vehicle_class = link.vehicle_class;
3563  const int64_t start_depot = link.start_depot;
3564  const int64_t end_depot = link.end_depot;
3565 
3566  // Initialisation of cumuls_ if the indices are encountered for first time
3567  if (index_to_vehicle_class_index_[index1] < 0) {
3568  for (int dimension_index = 0; dimension_index < dimensions_.size();
3569  ++dimension_index) {
3570  cumuls_[dimension_index][index1] =
3571  std::max(dimensions_[dimension_index]->GetTransitValue(
3572  start_depot, index1, 0),
3573  dimensions_[dimension_index]->CumulVar(index1)->Min());
3574  }
3575  }
3576  if (index_to_vehicle_class_index_[index2] < 0) {
3577  for (int dimension_index = 0; dimension_index < dimensions_.size();
3578  ++dimension_index) {
3579  cumuls_[dimension_index][index2] =
3580  std::max(dimensions_[dimension_index]->GetTransitValue(
3581  start_depot, index2, 0),
3582  dimensions_[dimension_index]->CumulVar(index2)->Min());
3583  }
3584  }
3585 
3586  const int route_index1 = in_route_[index1];
3587  const int route_index2 = in_route_[index2];
3588  const bool merge =
3589  route_index1 >= 0 && route_index2 >= 0 &&
3590  FeasibleMerge(routes_[route_index1], routes_[route_index2], index1,
3591  index2, route_index1, route_index2, vehicle_class,
3593  if (Merge(merge, route_index1, route_index2)) {
3594  index_to_vehicle_class_index_[index1] = vehicle_class;
3595  index_to_vehicle_class_index_[index2] = vehicle_class;
3596  }
3597  }
3598 
3599  model_->solver()->TopPeriodicCheck();
3600  // Beyond this point not checking limits anymore as the rest of the code is
3601  // linear and that given we managed to build a solution would be stupid to
3602  // drop it now.
3603  for (int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
3604  if (!gtl::ContainsKey(deleted_chains_, chain_index)) {
3605  final_chains_.push_back(chains_[chain_index]);
3606  }
3607  }
3608  std::sort(final_chains_.begin(), final_chains_.end(), ChainComparator);
3609  for (int route_index = 0; route_index < routes_.size(); ++route_index) {
3610  if (!gtl::ContainsKey(deleted_routes_, route_index)) {
3611  final_routes_.push_back(routes_[route_index]);
3612  }
3613  }
3614  std::sort(final_routes_.begin(), final_routes_.end(), RouteComparator);
3615 
3616  const int extra_vehicles = std::max(
3617  0, static_cast<int>(final_chains_.size()) - model_->vehicles());
3618  // Bind the Start and End of each chain
3619  int chain_index = 0;
3620  for (chain_index = extra_vehicles; chain_index < final_chains_.size();
3621  ++chain_index) {
3622  if (chain_index - extra_vehicles >= model_->vehicles()) {
3623  break;
3624  }
3625  const int start = final_chains_[chain_index].head;
3626  const int end = final_chains_[chain_index].tail;
3627  assignment_->Add(
3628  model_->NextVar(model_->Start(chain_index - extra_vehicles)));
3629  assignment_->SetValue(
3630  model_->NextVar(model_->Start(chain_index - extra_vehicles)), start);
3631  assignment_->Add(nexts_[end]);
3632  assignment_->SetValue(nexts_[end],
3633  model_->End(chain_index - extra_vehicles));
3634  }
3635 
3636  // Create the single order routes
3637  for (int route_index = 0; route_index < final_routes_.size();
3638  ++route_index) {
3639  if (chain_index - extra_vehicles >= model_->vehicles()) {
3640  break;
3641  }
3642  DCHECK_LT(route_index, final_routes_.size());
3643  const int head = final_routes_[route_index].front();
3644  const int tail = final_routes_[route_index].back();
3645  if (head == tail && head < model_->Size()) {
3646  assignment_->Add(
3647  model_->NextVar(model_->Start(chain_index - extra_vehicles)));
3648  assignment_->SetValue(
3649  model_->NextVar(model_->Start(chain_index - extra_vehicles)), head);
3650  assignment_->Add(nexts_[tail]);
3651  assignment_->SetValue(nexts_[tail],
3652  model_->End(chain_index - extra_vehicles));
3653  ++chain_index;
3654  }
3655  }
3656 
3657  // Unperformed
3658  for (int index = 0; index < model_->Size(); ++index) {
3659  IntVar* const next = nexts_[index];
3660  if (!assignment_->Contains(next)) {
3661  assignment_->Add(next);
3662  if (next->Contains(index)) {
3663  assignment_->SetValue(next, index);
3664  }
3665  }
3666  }
3667  }
3668 
3669  const std::vector<std::vector<int>>& final_routes() const {
3670  return final_routes_;
3671  }
3672 
3673  private:
3674  enum MergeStatus { FIRST_SECOND, SECOND_FIRST, NO_MERGE };
3675 
3676  struct RouteSort {
3677  bool operator()(const std::vector<int>& route1,
3678  const std::vector<int>& route2) const {
3679  return (route1.size() < route2.size());
3680  }
3681  } RouteComparator;
3682 
3683  struct Chain {
3684  int head;
3685  int tail;
3686  int nodes;
3687  };
3688 
3689  struct ChainSort {
3690  bool operator()(const Chain& chain1, const Chain& chain2) const {
3691  return (chain1.nodes < chain2.nodes);
3692  }
3693  } ChainComparator;
3694 
3695  bool Head(int node) const {
3696  return (node == routes_[in_route_[node]].front());
3697  }
3698 
3699  bool Tail(int node) const {
3700  return (node == routes_[in_route_[node]].back());
3701  }
3702 
3703  bool FeasibleRoute(const std::vector<int>& route, int64_t route_cumul,
3704  int dimension_index) {
3705  const RoutingDimension& dimension = *dimensions_[dimension_index];
3706  std::vector<int>::const_iterator it = route.begin();
3707  int64_t cumul = route_cumul;
3708  while (it != route.end()) {
3709  const int previous = *it;
3710  const int64_t cumul_previous = cumul;
3711  gtl::InsertOrDie(&(new_possible_cumuls_[dimension_index]), previous,
3712  cumul_previous);
3713  ++it;
3714  if (it == route.end()) {
3715  return true;
3716  }
3717  const int next = *it;
3718  int64_t available_from_previous =
3719  cumul_previous + dimension.GetTransitValue(previous, next, 0);
3720  int64_t available_cumul_next =
3721  std::max(cumuls_[dimension_index][next], available_from_previous);
3722 
3723  const int64_t slack = available_cumul_next - available_from_previous;
3724  if (slack > dimension.SlackVar(previous)->Max()) {
3725  available_cumul_next =
3726  available_from_previous + dimension.SlackVar(previous)->Max();
3727  }
3728 
3729  if (available_cumul_next > dimension.CumulVar(next)->Max()) {
3730  return false;
3731  }
3732  if (available_cumul_next <= cumuls_[dimension_index][next]) {
3733  return true;
3734  }
3735  cumul = available_cumul_next;
3736  }
3737  return true;
3738  }
3739 
3740  bool CheckRouteConnection(const std::vector<int>& route1,
3741  const std::vector<int>& route2, int dimension_index,
3742  int64_t start_depot, int64_t end_depot) {
3743  const int tail1 = route1.back();
3744  const int head2 = route2.front();
3745  const int tail2 = route2.back();
3746  const RoutingDimension& dimension = *dimensions_[dimension_index];
3747  int non_depot_node = -1;
3748  for (int node = 0; node < num_indices_; ++node) {
3749  if (!model_->IsStart(node) && !model_->IsEnd(node)) {
3750  non_depot_node = node;
3751  break;
3752  }
3753  }
3754  CHECK_GE(non_depot_node, 0);
3755  const int64_t depot_threshold =
3756  std::max(dimension.SlackVar(non_depot_node)->Max(),
3757  dimension.CumulVar(non_depot_node)->Max());
3758 
3759  int64_t available_from_tail1 = cumuls_[dimension_index][tail1] +
3760  dimension.GetTransitValue(tail1, head2, 0);
3761  int64_t new_available_cumul_head2 =
3762  std::max(cumuls_[dimension_index][head2], available_from_tail1);
3763 
3764  const int64_t slack = new_available_cumul_head2 - available_from_tail1;
3765  if (slack > dimension.SlackVar(tail1)->Max()) {
3766  new_available_cumul_head2 =
3767  available_from_tail1 + dimension.SlackVar(tail1)->Max();
3768  }
3769 
3770  bool feasible_route = true;
3771  if (new_available_cumul_head2 > dimension.CumulVar(head2)->Max()) {
3772  return false;
3773  }
3774  if (new_available_cumul_head2 <= cumuls_[dimension_index][head2]) {
3775  return true;
3776  }
3777 
3778  feasible_route =
3779  FeasibleRoute(route2, new_available_cumul_head2, dimension_index);
3780  const int64_t new_possible_cumul_tail2 =
3781  gtl::ContainsKey(new_possible_cumuls_[dimension_index], tail2)
3782  ? new_possible_cumuls_[dimension_index][tail2]
3783  : cumuls_[dimension_index][tail2];
3784 
3785  if (!feasible_route || (new_possible_cumul_tail2 +
3786  dimension.GetTransitValue(tail2, end_depot, 0) >
3787  depot_threshold)) {
3788  return false;
3789  }
3790  return true;
3791  }
3792 
3793  bool FeasibleMerge(const std::vector<int>& route1,
3794  const std::vector<int>& route2, int node1, int node2,
3795  int route_index1, int route_index2, int vehicle_class,
3796  int64_t start_depot, int64_t end_depot) {
3797  if ((route_index1 == route_index2) || !(Tail(node1) && Head(node2))) {
3798  return false;
3799  }
3800 
3801  // Vehicle Class Check
3802  if (!((index_to_vehicle_class_index_[node1] == -1 &&
3803  index_to_vehicle_class_index_[node2] == -1) ||
3804  (index_to_vehicle_class_index_[node1] == vehicle_class &&
3805  index_to_vehicle_class_index_[node2] == -1) ||
3806  (index_to_vehicle_class_index_[node1] == -1 &&
3807  index_to_vehicle_class_index_[node2] == vehicle_class) ||
3808  (index_to_vehicle_class_index_[node1] == vehicle_class &&
3809  index_to_vehicle_class_index_[node2] == vehicle_class))) {
3810  return false;
3811  }
3812 
3813  // Check Route1 -> Route2 connection for every dimension
3814  bool merge = true;
3815  for (int dimension_index = 0; dimension_index < dimensions_.size();
3816  ++dimension_index) {
3817  new_possible_cumuls_[dimension_index].clear();
3818  merge = merge && CheckRouteConnection(route1, route2, dimension_index,
3820  if (!merge) {
3821  return false;
3822  }
3823  }
3824  return true;
3825  }
3826 
3827  bool CheckTempAssignment(Assignment* const temp_assignment,
3828  int new_chain_index, int old_chain_index, int head1,
3829  int tail1, int head2, int tail2) {
3830  // TODO(user): If the chain index is greater than the number of vehicles,
3831  // use another vehicle instead.
3832  if (new_chain_index >= model_->vehicles()) return false;
3833  const int start = head1;
3834  temp_assignment->Add(model_->NextVar(model_->Start(new_chain_index)));
3835  temp_assignment->SetValue(model_->NextVar(model_->Start(new_chain_index)),
3836  start);
3837  temp_assignment->Add(nexts_[tail1]);
3838  temp_assignment->SetValue(nexts_[tail1], head2);
3839  temp_assignment->Add(nexts_[tail2]);
3840  temp_assignment->SetValue(nexts_[tail2], model_->End(new_chain_index));
3841  for (int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
3842  if ((chain_index != new_chain_index) &&
3843  (chain_index != old_chain_index) &&
3844  (!gtl::ContainsKey(deleted_chains_, chain_index))) {
3845  const int start = chains_[chain_index].head;
3846  const int end = chains_[chain_index].tail;
3847  temp_assignment->Add(model_->NextVar(model_->Start(chain_index)));
3848  temp_assignment->SetValue(model_->NextVar(model_->Start(chain_index)),
3849  start);
3850  temp_assignment->Add(nexts_[end]);
3851  temp_assignment->SetValue(nexts_[end], model_->End(chain_index));
3852  }
3853  }
3854  return solver_->Solve(solver_->MakeRestoreAssignment(temp_assignment));
3855  }
3856 
3857  bool UpdateAssignment(const std::vector<int>& route1,
3858  const std::vector<int>& route2) {
3859  bool feasible = true;
3860  const int head1 = route1.front();
3861  const int tail1 = route1.back();
3862  const int head2 = route2.front();
3863  const int tail2 = route2.back();
3864  const int chain_index1 = index_to_chain_index_[head1];
3865  const int chain_index2 = index_to_chain_index_[head2];
3866  if (chain_index1 < 0 && chain_index2 < 0) {
3867  const int chain_index = chains_.size();
3868  if (check_assignment_) {
3869  Assignment* const temp_assignment =
3870  solver_->MakeAssignment(assignment_);
3871  feasible = CheckTempAssignment(temp_assignment, chain_index, -1, head1,
3872  tail1, head2, tail2);
3873  }
3874  if (feasible) {
3875  Chain chain;
3876  chain.head = head1;
3877  chain.tail = tail2;
3878  chain.nodes = 2;
3879  index_to_chain_index_[head1] = chain_index;
3880  index_to_chain_index_[tail2] = chain_index;
3881  chains_.push_back(chain);
3882  }
3883  } else if (chain_index1 >= 0 && chain_index2 < 0) {
3884  if (check_assignment_) {
3885  Assignment* const temp_assignment =
3886  solver_->MakeAssignment(assignment_);
3887  feasible =
3888  CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
3889  head1, tail1, head2, tail2);
3890  }
3891  if (feasible) {
3892  index_to_chain_index_[tail2] = chain_index1;
3893  chains_[chain_index1].head = head1;
3894  chains_[chain_index1].tail = tail2;
3895  ++chains_[chain_index1].nodes;
3896  }
3897  } else if (chain_index1 < 0 && chain_index2 >= 0) {
3898  if (check_assignment_) {
3899  Assignment* const temp_assignment =
3900  solver_->MakeAssignment(assignment_);
3901  feasible =
3902  CheckTempAssignment(temp_assignment, chain_index2, chain_index1,
3903  head1, tail1, head2, tail2);
3904  }
3905  if (feasible) {
3906  index_to_chain_index_[head1] = chain_index2;
3907  chains_[chain_index2].head = head1;
3908  chains_[chain_index2].tail = tail2;
3909  ++chains_[chain_index2].nodes;
3910  }
3911  } else {
3912  if (check_assignment_) {
3913  Assignment* const temp_assignment =
3914  solver_->MakeAssignment(assignment_);
3915  feasible =
3916  CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
3917  head1, tail1, head2, tail2);
3918  }
3919  if (feasible) {
3920  index_to_chain_index_[tail2] = chain_index1;
3921  chains_[chain_index1].head = head1;
3922  chains_[chain_index1].tail = tail2;
3923  chains_[chain_index1].nodes += chains_[chain_index2].nodes;
3924  deleted_chains_.insert(chain_index2);
3925  }
3926  }
3927  if (feasible) {
3928  assignment_->Add(nexts_[tail1]);
3929  assignment_->SetValue(nexts_[tail1], head2);
3930  }
3931  return feasible;
3932  }
3933 
3934  bool Merge(bool merge, int index1, int index2) {
3935  if (merge) {
3936  if (UpdateAssignment(routes_[index1], routes_[index2])) {
3937  // Connection Route1 -> Route2
3938  for (const int node : routes_[index2]) {
3939  in_route_[node] = index1;
3940  routes_[index1].push_back(node);
3941  }
3942  for (int dimension_index = 0; dimension_index < dimensions_.size();
3943  ++dimension_index) {
3944  for (const std::pair<int, int64_t> new_possible_cumul :
3945  new_possible_cumuls_[dimension_index]) {
3946  cumuls_[dimension_index][new_possible_cumul.first] =
3947  new_possible_cumul.second;
3948  }
3949  }
3950  deleted_routes_.insert(index2);
3951  return true;
3952  }
3953  }
3954  return false;
3955  }
3956 
3957  Assignment* const assignment_;
3958  RoutingModel* const model_;
3959  const bool check_assignment_;
3960  Solver* const solver_;
3961  const int64_t num_indices_;
3962  const std::vector<Link> links_list_;
3963  std::vector<IntVar*> nexts_;
3964  std::vector<const RoutingDimension*> dimensions_; // Not owned.
3965  std::vector<std::vector<int64_t>> cumuls_;
3966  std::vector<absl::flat_hash_map<int, int64_t>> new_possible_cumuls_;
3967  std::vector<std::vector<int>> routes_;
3968  std::vector<int> in_route_;
3969  absl::flat_hash_set<int> deleted_routes_;
3970  std::vector<std::vector<int>> final_routes_;
3971  std::vector<Chain> chains_;
3972  absl::flat_hash_set<int> deleted_chains_;
3973  std::vector<Chain> final_chains_;
3974  std::vector<int> index_to_chain_index_;
3975  std::vector<int> index_to_vehicle_class_index_;
3976 };
3977 
3978 // Decision Builder building a first solution based on Sweep heuristic for
3979 // Vehicle Routing Problem.
3980 // Suitable only when distance is considered as the cost.
3981 class SweepBuilder : public DecisionBuilder {
3982  public:
3983  SweepBuilder(RoutingModel* const model, bool check_assignment)
3984  : model_(model), check_assignment_(check_assignment) {}
3985  ~SweepBuilder() override {}
3986 
3987  Decision* Next(Solver* const solver) override {
3988  // Setup the model of the instance for the Sweep Algorithm
3989  ModelSetup();
3990 
3991  // Build the assignment routes for the model
3992  Assignment* const assignment = solver->MakeAssignment();
3993  route_constructor_ = absl::make_unique<RouteConstructor>(
3994  assignment, model_, check_assignment_, num_indices_, links_);
3995  // This call might cause backtracking if the search limit is reached.
3996  route_constructor_->Construct();
3997  route_constructor_.reset(nullptr);
3998  // This call might cause backtracking if the solution is not feasible.
3999  assignment->Restore();
4000 
4001  return nullptr;
4002  }
4003 
4004  private:
4005  void ModelSetup() {
4006  const int depot = model_->GetDepot();
4007  num_indices_ = model_->Size() + model_->vehicles();
4008  if (absl::GetFlag(FLAGS_sweep_sectors) > 0 &&
4009  absl::GetFlag(FLAGS_sweep_sectors) < num_indices_) {
4010  model_->sweep_arranger()->SetSectors(absl::GetFlag(FLAGS_sweep_sectors));
4011  }
4012  std::vector<int64_t> indices;
4013  model_->sweep_arranger()->ArrangeIndices(&indices);
4014  for (int i = 0; i < indices.size() - 1; ++i) {
4015  const int64_t first = indices[i];
4016  const int64_t second = indices[i + 1];
4017  if ((model_->IsStart(first) || !model_->IsEnd(first)) &&
4018  (model_->IsStart(second) || !model_->IsEnd(second))) {
4019  if (first != depot && second != depot) {
4020  Link link(std::make_pair(first, second), 0, 0, depot, depot);
4021  links_.push_back(link);
4022  }
4023  }
4024  }
4025  }
4026 
4027  RoutingModel* const model_;
4028  std::unique_ptr<RouteConstructor> route_constructor_;
4029  const bool check_assignment_;
4030  int64_t num_indices_;
4031  std::vector<Link> links_;
4032 };
4033 } // namespace
4034 
4036  bool check_assignment) {
4037  return model->solver()->RevAlloc(new SweepBuilder(model, check_assignment));
4038 }
4039 
4040 // AllUnperformed
4041 
4042 namespace {
4043 // Decision builder to build a solution with all nodes inactive. It does no
4044 // branching and may fail if some nodes cannot be made inactive.
4045 
4046 class AllUnperformed : public DecisionBuilder {
4047  public:
4048  // Does not take ownership of model.
4049  explicit AllUnperformed(RoutingModel* const model) : model_(model) {}
4050  ~AllUnperformed() override {}
4051  Decision* Next(Solver* const solver) override {
4052  // Solver::(Un)FreezeQueue is private, passing through the public API
4053  // on PropagationBaseObject.
4054  model_->CostVar()->FreezeQueue();
4055  for (int i = 0; i < model_->Size(); ++i) {
4056  if (!model_->IsStart(i)) {
4057  model_->ActiveVar(i)->SetValue(0);
4058  }
4059  }
4060  model_->CostVar()->UnfreezeQueue();
4061  return nullptr;
4062  }
4063 
4064  private:
4065  RoutingModel* const model_;
4066 };
4067 } // namespace
4068 
4070  return model->solver()->RevAlloc(new AllUnperformed(model));
4071 }
4072 
4073 namespace {
4074 // The description is in routing.h:MakeGuidedSlackFinalizer
4075 class GuidedSlackFinalizer : public DecisionBuilder {
4076  public:
4077  GuidedSlackFinalizer(const RoutingDimension* dimension, RoutingModel* model,
4078  std::function<int64_t(int64_t)> initializer);
4079  Decision* Next(Solver* solver) override;
4080 
4081  private:
4082  int64_t SelectValue(int64_t index);
4083  int64_t ChooseVariable();
4084 
4085  const RoutingDimension* const dimension_;
4086  RoutingModel* const model_;
4087  const std::function<int64_t(int64_t)> initializer_;
4088  RevArray<bool> is_initialized_;
4089  std::vector<int64_t> initial_values_;
4090  Rev<int64_t> current_index_;
4091  Rev<int64_t> current_route_;
4092  RevArray<int64_t> last_delta_used_;
4093 
4094  DISALLOW_COPY_AND_ASSIGN(GuidedSlackFinalizer);
4095 };
4096 
4097 GuidedSlackFinalizer::GuidedSlackFinalizer(
4098  const RoutingDimension* dimension, RoutingModel* model,
4099  std::function<int64_t(int64_t)> initializer)
4100  : dimension_(ABSL_DIE_IF_NULL(dimension)),
4101  model_(ABSL_DIE_IF_NULL(model)),
4102  initializer_(std::move(initializer)),
4103  is_initialized_(dimension->slacks().size(), false),
4104  initial_values_(dimension->slacks().size(),
4105  std::numeric_limits<int64_t>::min()),
4106  current_index_(model_->Start(0)),
4107  current_route_(0),
4108  last_delta_used_(dimension->slacks().size(), 0) {}
4109 
4110 Decision* GuidedSlackFinalizer::Next(Solver* solver) {
4111  CHECK_EQ(solver, model_->solver());
4112  const int node_idx = ChooseVariable();
4113  CHECK(node_idx == -1 ||
4114  (node_idx >= 0 && node_idx < dimension_->slacks().size()));
4115  if (node_idx != -1) {
4116  if (!is_initialized_[node_idx]) {
4117  initial_values_[node_idx] = initializer_(node_idx);
4118  is_initialized_.SetValue(solver, node_idx, true);
4119  }
4120  const int64_t value = SelectValue(node_idx);
4121  IntVar* const slack_variable = dimension_->SlackVar(node_idx);
4122  return solver->MakeAssignVariableValue(slack_variable, value);
4123  }
4124  return nullptr;
4125 }
4126 
4127 int64_t GuidedSlackFinalizer::SelectValue(int64_t index) {
4128  const IntVar* const slack_variable = dimension_->SlackVar(index);
4129  const int64_t center = initial_values_[index];
4130  const int64_t max_delta =
4131  std::max(center - slack_variable->Min(), slack_variable->Max() - center) +
4132  1;
4133  int64_t delta = last_delta_used_[index];
4134 
4135  // The sequence of deltas is 0, 1, -1, 2, -2 ...
4136  // Only the values inside the domain of variable are returned.
4137  while (std::abs(delta) < max_delta &&
4138  !slack_variable->Contains(center + delta)) {
4139  if (delta > 0) {
4140  delta = -delta;
4141  } else {
4142  delta = -delta + 1;
4143  }
4144  }
4145  last_delta_used_.SetValue(model_->solver(), index, delta);
4146  return center + delta;
4147 }
4148 
4149 int64_t GuidedSlackFinalizer::ChooseVariable() {
4150  int64_t int_current_node = current_index_.Value();
4151  int64_t int_current_route = current_route_.Value();
4152 
4153  while (int_current_route < model_->vehicles()) {
4154  while (!model_->IsEnd(int_current_node) &&
4155  dimension_->SlackVar(int_current_node)->Bound()) {
4156  int_current_node = model_->NextVar(int_current_node)->Value();
4157  }
4158  if (!model_->IsEnd(int_current_node)) {
4159  break;
4160  }
4161  int_current_route += 1;
4162  if (int_current_route < model_->vehicles()) {
4163  int_current_node = model_->Start(int_current_route);
4164  }
4165  }
4166 
4167  CHECK(int_current_route == model_->vehicles() ||
4168  !dimension_->SlackVar(int_current_node)->Bound());
4169  current_index_.SetValue(model_->solver(), int_current_node);
4170  current_route_.SetValue(model_->solver(), int_current_route);
4171  if (int_current_route < model_->vehicles()) {
4172  return int_current_node;
4173  } else {
4174  return -1;
4175  }
4176 }
4177 } // namespace
4178 
4179 DecisionBuilder* RoutingModel::MakeGuidedSlackFinalizer(
4180  const RoutingDimension* dimension,
4181  std::function<int64_t(int64_t)> initializer) {
4182  return solver_->RevAlloc(
4183  new GuidedSlackFinalizer(dimension, this, std::move(initializer)));
4184 }
4185 
4186 int64_t RoutingDimension::ShortestTransitionSlack(int64_t node) const {
4187  CHECK_EQ(base_dimension_, this);
4188  CHECK(!model_->IsEnd(node));
4189  // Recall that the model is cumul[i+1] = cumul[i] + transit[i] + slack[i]. Our
4190  // aim is to find a value for slack[i] such that cumul[i+1] + transit[i+1] is
4191  // minimized.
4192  const int64_t next = model_->NextVar(node)->Value();
4193  if (model_->IsEnd(next)) {
4194  return SlackVar(node)->Min();
4195  }
4196  const int64_t next_next = model_->NextVar(next)->Value();
4197  const int64_t serving_vehicle = model_->VehicleVar(node)->Value();
4198  CHECK_EQ(serving_vehicle, model_->VehicleVar(next)->Value());
4199  const RoutingModel::StateDependentTransit transit_from_next =
4200  model_->StateDependentTransitCallback(
4201  state_dependent_class_evaluators_
4202  [state_dependent_vehicle_to_class_[serving_vehicle]])(next,
4203  next_next);
4204  // We have that transit[i+1] is a function of cumul[i+1].
4205  const int64_t next_cumul_min = CumulVar(next)->Min();
4206  const int64_t next_cumul_max = CumulVar(next)->Max();
4207  const int64_t optimal_next_cumul =
4208  transit_from_next.transit_plus_identity->RangeMinArgument(
4209  next_cumul_min, next_cumul_max + 1);
4210  // A few checks to make sure we're on the same page.
4211  DCHECK_LE(next_cumul_min, optimal_next_cumul);
4212  DCHECK_LE(optimal_next_cumul, next_cumul_max);
4213  // optimal_next_cumul = cumul + transit + optimal_slack, so
4214  // optimal_slack = optimal_next_cumul - cumul - transit.
4215  // In the current implementation TransitVar(i) = transit[i] + slack[i], so we
4216  // have to find the transit from the evaluators.
4217  const int64_t current_cumul = CumulVar(node)->Value();
4218  const int64_t current_state_independent_transit = model_->TransitCallback(
4219  class_evaluators_[vehicle_to_class_[serving_vehicle]])(node, next);
4220  const int64_t current_state_dependent_transit =
4221  model_
4222  ->StateDependentTransitCallback(
4223  state_dependent_class_evaluators_
4224  [state_dependent_vehicle_to_class_[serving_vehicle]])(node,
4225  next)
4226  .transit->Query(current_cumul);
4227  const int64_t optimal_slack = optimal_next_cumul - current_cumul -
4228  current_state_independent_transit -
4229  current_state_dependent_transit;
4230  CHECK_LE(SlackVar(node)->Min(), optimal_slack);
4231  CHECK_LE(optimal_slack, SlackVar(node)->Max());
4232  return optimal_slack;
4233 }
4234 
4235 namespace {
4236 class GreedyDescentLSOperator : public LocalSearchOperator {
4237  public:
4238  explicit GreedyDescentLSOperator(std::vector<IntVar*> variables);
4239 
4240  bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override;
4241  void Start(const Assignment* assignment) override;
4242 
4243  private:
4244  int64_t FindMaxDistanceToDomain(const Assignment* assignment);
4245 
4246  const std::vector<IntVar*> variables_;
4247  const Assignment* center_;
4248  int64_t current_step_;
4249  // The deltas are returned in this order:
4250  // (current_step_, 0, ... 0), (-current_step_, 0, ... 0),
4251  // (0, current_step_, ... 0), (0, -current_step_, ... 0),
4252  // ...
4253  // (0, ... 0, current_step_), (0, ... 0, -current_step_).
4254  // current_direction_ keeps track what was the last returned delta.
4255  int64_t current_direction_;
4256 
4257  DISALLOW_COPY_AND_ASSIGN(GreedyDescentLSOperator);
4258 };
4259 
4260 GreedyDescentLSOperator::GreedyDescentLSOperator(std::vector<IntVar*> variables)
4261  : variables_(std::move(variables)),
4262  center_(nullptr),
4263  current_step_(0),
4264  current_direction_(0) {}
4265 
4266 bool GreedyDescentLSOperator::MakeNextNeighbor(Assignment* delta,
4267  Assignment* /*deltadelta*/) {
4268  static const int64_t sings[] = {1, -1};
4269  for (; 1 <= current_step_; current_step_ /= 2) {
4270  for (; current_direction_ < 2 * variables_.size();) {
4271  const int64_t variable_idx = current_direction_ / 2;
4272  IntVar* const variable = variables_[variable_idx];
4273  const int64_t sign_index = current_direction_ % 2;
4274  const int64_t sign = sings[sign_index];
4275  const int64_t offset = sign * current_step_;
4276  const int64_t new_value = center_->Value(variable) + offset;
4277  ++current_direction_;
4278  if (variable->Contains(new_value)) {
4279  delta->Add(variable);
4280  delta->SetValue(variable, new_value);
4281  return true;
4282  }
4283  }
4284  current_direction_ = 0;
4285  }
4286  return false;
4287 }
4288 
4289 void GreedyDescentLSOperator::Start(const Assignment* assignment) {
4290  CHECK(assignment != nullptr);
4291  current_step_ = FindMaxDistanceToDomain(assignment);
4292  center_ = assignment;
4293 }
4294 
4295 int64_t GreedyDescentLSOperator::FindMaxDistanceToDomain(
4296  const Assignment* assignment) {
4297  int64_t result = std::numeric_limits<int64_t>::min();
4298  for (const IntVar* const var : variables_) {
4299  result = std::max(result, std::abs(var->Max() - assignment->Value(var)));
4300  result = std::max(result, std::abs(var->Min() - assignment->Value(var)));
4301  }
4302  return result;
4303 }
4304 } // namespace
4305 
4306 std::unique_ptr<LocalSearchOperator> RoutingModel::MakeGreedyDescentLSOperator(
4307  std::vector<IntVar*> variables) {
4308  return std::unique_ptr<LocalSearchOperator>(
4309  new GreedyDescentLSOperator(std::move(variables)));
4310 }
4311 
4313  const RoutingDimension* dimension) {
4314  CHECK(dimension != nullptr);
4315  CHECK(dimension->base_dimension() == dimension);
4316  std::function<int64_t(int64_t)> slack_guide = [dimension](int64_t index) {
4317  return dimension->ShortestTransitionSlack(index);
4318  };
4319  DecisionBuilder* const guided_finalizer =
4320  MakeGuidedSlackFinalizer(dimension, slack_guide);
4321  DecisionBuilder* const slacks_finalizer =
4322  solver_->MakeSolveOnce(guided_finalizer);
4323  std::vector<IntVar*> start_cumuls(vehicles_, nullptr);
4324  for (int64_t vehicle_idx = 0; vehicle_idx < vehicles_; ++vehicle_idx) {
4325  start_cumuls[vehicle_idx] = dimension->CumulVar(starts_[vehicle_idx]);
4326  }
4327  LocalSearchOperator* const hill_climber =
4328  solver_->RevAlloc(new GreedyDescentLSOperator(start_cumuls));
4330  solver_->MakeLocalSearchPhaseParameters(CostVar(), hill_climber,
4331  slacks_finalizer);
4332  Assignment* const first_solution = solver_->MakeAssignment();
4333  first_solution->Add(start_cumuls);
4334  for (IntVar* const cumul : start_cumuls) {
4335  first_solution->SetValue(cumul, cumul->Min());
4336  }
4337  DecisionBuilder* const finalizer =
4338  solver_->MakeLocalSearchPhase(first_solution, parameters);
4339  return finalizer;
4340 }
4341 } // namespace operations_research
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
#define CHECK(condition)
Definition: base/logging.h:491
int64_t start_depot
int64_t GetInsertionCostForNodeAtPosition(int64_t node_to_insert, int64_t insert_after, int64_t insert_before, int vehicle) const
Returns the cost of inserting 'node_to_insert' between 'insert_after' and 'insert_before' on the 'veh...
int64_t CapSub(int64_t x, int64_t y)
std::function< bool(int64_t, int64_t, int64_t)> VariableValueComparator
int64_t Value(int64_t index) const
Returns the value of the variable of index 'index' in the last committed solution.
int64_t Value(const IntVar *const var) const
PairEntry(int pickup_to_insert, int pickup_insert_after, int delivery_to_insert, int delivery_insert_after, int vehicle)
void set_insert_after(int insert_after)
The base class for all local search operators.
int64_t min
Definition: alldiff_cst.cc:139
static std::unique_ptr< LocalSearchOperator > MakeGreedyDescentLSOperator(std::vector< IntVar * > variables)
Perhaps move it to constraint_solver.h.
CheapestAdditionFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager)
int64_t value() const
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
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:3484
Definition: routing.h:355
#define CHECK_GE(val1, val2)
Definition: base/logging.h:702
std::unique_ptr< VehicleTypeCurator > vehicle_type_curator_
std::unique_ptr< SavingsContainer< Saving > > savings_container_
int delivery_to_insert() const
Manager for any NodeIndex <-> variable index conversion.
int64_t ShortestTransitionSlack(int64_t node) const
It makes sense to use the function only for self-dependent dimension.
int64_t GetUnperformedValue(int64_t node_to_insert) const
Returns the cost of unperforming node 'node_to_insert'.
IntVar * VehicleVar(int64_t index) const
Returns the vehicle variable of the node corresponding to index.
Definition: routing.h:1319
bool Contains(int64_t index) const
Returns true if the variable of index 'index' is in the current solution.
void SetValue(const IntVar *const var, int64_t value)
int Size() const
Returns the number of variables the decision builder is trying to instantiate.
#define CHECK_GT(val1, val2)
Definition: base/logging.h:703
void set_value(int64_t value)
#define VLOG(verboselevel)
Definition: base/logging.h:979
Filter-based heuristic dedicated to routing.
IntVar * CostVar() const
Returns the global cost variable which is being minimized.
Definition: routing.h:1321
double farthest_seeds_ratio
The ratio of routes on which to insert farthest nodes as seeds before starting the cheapest insertion...
int head
virtual bool Contains(int64_t v) const =0
This method returns whether the value 'v' is in the domain of the variable.
bool add_reverse_arcs
If add_reverse_arcs is true, the neighborhood relationships are considered symmetrically.
IntVarElement * Add(IntVar *const var)
int64_t GetVehicleTypeFromSaving(const Saving &saving) const
Returns the cost class from a saving.
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:263
virtual double ExtraSavingsMemoryMultiplicativeFactor() const =0
int vehicle() const
IntVarFilteredHeuristic(Solver *solver, const std::vector< IntVar * > &vars, LocalSearchFilterManager *filter_manager)
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
Definition: map_util.h:154
IntVar * Var(int64_t index) const
Returns the variable of index 'index'.
int tail
GRBmodel * model
int pickup_to_insert() const
virtual int64_t Min() const =0
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
Dimensions represent quantities accumulated at nodes along the routes.
Definition: routing.h:2494
E * AddAtPosition(V *var, int position)
Advanced usage: Adds element at a given position; position has to have been allocated with Assignment...
CheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
Definition: routing.h:1279
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:891
int64_t end_depot
int64_t GetBeforeNodeFromSaving(const Saving &saving) const
Returns the "before node" from a saving.
void Reset(const std::function< bool(int)> &store_vehicle)
Resets the vehicles stored, storing only vehicles from the vehicle_type_container_ for which store_ve...
const std::vector< Saving > & GetSortedSavingsForVehicleType(int type)
const E & Element(const V *const var) const
Decision * Next(Solver *solver) override
This is the main method of the decision builder class.
const IndexPairs & GetPickupAndDeliveryPairs() const
Returns pickup and delivery pairs currently in the model.
Definition: routing.h:834
A DecisionBuilder is responsible for creating the search tree.
EvaluatorCheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
int node_to_insert() const
const Assignment * BuildSolutionFromRoutes(const std::function< int64_t(int64_t)> &next_accessor)
Builds a solution starting from the routes formed by the next accessor.
#define CHECK_LT(val1, val2)
Definition: base/logging.h:701
void set_vehicle(int vehicle)
bool StopSearch() override
Returns true if the search must be stopped.
virtual void Start(const Assignment *assignment)=0
ComparatorCheapestAdditionFilteredHeuristic(RoutingModel *model, Solver::VariableValueComparator comparator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
Generic filter-based heuristic applied to IntVars.
IntVar * CumulVar(int64_t index) const
Get the cumul, transit and slack variables for the given node (given as int64_t var index).
Definition: routing.h:2513
int64_t max
Definition: alldiff_cst.cc:140
Block * next
void ResetSolution()
Resets the data members for a new solution.
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:1856
const std::vector< IntVar * > cumuls_
ABSL_FLAG(bool, routing_shift_insertion_cost_by_penalty, true, "Shift insertion costs by the penalty of the inserted node(s).")
void SetMatchingAlgorithm(MatchingAlgorithm matching)
Definition: christofides.h:69
Filter manager: when a move is made, filters are executed to decide whether the solution is feasible ...
void MakeUnassignedNodesUnperformed()
Make all unassigned nodes unperformed.
SavingsContainer(const SavingsFilteredHeuristic *savings_db, int vehicle_types)
int64_t CapAdd(int64_t x, int64_t y)
virtual bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta)=0
int insert_after() const
int GetHeapIndex() const
RoutingFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager)
int vehicle() const
double arc_coefficient
arc_coefficient is a strictly positive parameter indicating the coefficient of the arc being consider...
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:887
int64_t value
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
void set_pickup_insert_after(int pickup_insert_after)
DecisionBuilder * MakeSelfDependentDimensionFinalizer(const RoutingDimension *dimension)
SWIG
const std::vector< std::pair< int, int > > & GetDeliveryIndexPairs(int64_t node_index) const
Same as above for deliveries.
Definition: routing.cc:1862
int pickup_insert_after() const
bool IsStart(int64_t index) const
Returns true if 'index' represents the first node of a route.
Definition: routing.cc:3452
#define CHECK_LE(val1, val2)
Definition: base/logging.h:700
void InitializeContainer(int64_t size, int64_t saving_neighbors)
void SetHeapIndex(int h)
int GetHeapIndex() const
virtual bool BuildSolutionInternal()=0
Virtual method to redefine how to build a solution.
double max_memory_usage_bytes
The number of neighbors considered for each node is also adapted so that the stored Savings don't use...
void InitializePriorityQueue(std::vector< std::vector< StartEndValue > > *start_end_distances_per_node, Queue *priority_queue)
Initializes the priority_queue by inserting the best entry corresponding to each node,...
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:200
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
void InsertBetween(int64_t node, int64_t predecessor, int64_t successor)
Inserts 'node' just after 'predecessor', and just before 'successor', resulting in the following subs...
Filter-based decision builder which builds a solution by using Clarke & Wright's Savings heuristic.
std::function< int64_t(int64_t, int64_t)> evaluator_
Definition: search.cc:1368
std::pair< int, int > link
const RoutingDimension * base_dimension() const
Returns the parent in the dependency tree if any or nullptr otherwise.
Definition: routing.h:2747
void Update(const std::function< bool(int)> &remove_vehicle)
Goes through all the currently stored vehicles and removes vehicles for which remove_vehicle() return...
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
void MakePartiallyPerformedPairsUnperformed()
Make all partially performed pickup and delivery pairs unperformed.
void ForEachNodeInDisjunctionWithMaxCardinalityFromIndex(int64_t index, int64_t max_cardinality, F f) const
Calls f for each variable index of indices in the same disjunctions as the node corresponding to the ...
Definition: routing.h:727
An Assignment is a variable -> domains mapping, used to report solutions to the user.
CostClassIndex GetCostClassIndexOfVehicle(int64_t vehicle) const
Get the cost class index of the given vehicle.
Definition: routing.h:1349
LocalSearchMonitor * GetLocalSearchMonitor() const
Returns the local search monitor.
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
Definition: routing.h:1366
The class IntVar is a subset of IntExpr.
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:890
std::vector< std::deque< int > > vehicles_per_vehicle_class
Definition: routing.h:375
void SetValue(int64_t index, int64_t value)
Modifies the current solution by setting the variable of index 'index' to value 'value'.
void STLClearObject(T *obj)
Definition: stl_util.h:123
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
const std::vector< IntVar * > vars_
Definition: alldiff_cst.cc:44
int64_t delta
Definition: resource.cc:1692
bool Accept(LocalSearchMonitor *const monitor, const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max)
Returns true iff all filters return true, and the sum of their accepted objectives is between objecti...
int64_t cost
void SynchronizeFilters()
Synchronizes filters with an assignment (the current solution).
LocalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
RoutingIndexPairs IndexPairs
Definition: routing.h:244
std::vector< std::set< VehicleClassEntry > > sorted_vehicle_classes_per_type
Definition: routing.h:374
#define DCHECK(condition)
Definition: base/logging.h:885
GlobalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager, GlobalCheapestInsertionParameters parameters)
Takes ownership of evaluators.
int64_t GetSavingValue(const Saving &saving) const
Returns the saving value from a saving.
bool Contains(const T *val) const
ChristofidesFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager, bool use_minimum_matching)
int vehicles() const
Returns the number of vehicle routes in the model.
Definition: routing.h:1458
RoutingIndexPair IndexPair
Definition: routing.h:243
SavingsFilteredHeuristic(RoutingModel *model, const RoutingIndexManager *manager, SavingsParameters parameters, LocalSearchFilterManager *filter_manager)
std::vector< std::vector< StartEndValue > > ComputeStartEndDistanceForVehicles(const std::vector< int > &vehicles)
Computes and returns the distance of each uninserted node to every vehicle in "vehicles" as a std::ve...
void ArrangeIndices(std::vector< int64_t > *indices)
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
static const int kUnassigned
Definition: routing.cc:702
NodeEntry(int node_to_insert, int insert_after, int vehicle)
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:886
void SetHeapIndex(int h)
Filtered-base decision builder based on the addition heuristic, extending a path from its start node ...
bool Commit()
Commits the modifications to the current solution if these modifications are "filter-feasible",...
A Decision represents a choice point in the search tree.
DecisionBuilder * MakeGuidedSlackFinalizer(const RoutingDimension *dimension, std::function< int64_t(int64_t)> initializer)
The next few members are in the public section only for testing purposes.
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:888
int StartNewRouteWithBestVehicleOfType(int type, int64_t before_node, int64_t after_node)
Finds the best available vehicle of type "type" to start a new route to serve the arc before_node-->a...
virtual void SetVehicleIndex(int64_t node, int vehicle)
static constexpr Value PARALLEL_CHEAPEST_INSERTION
bool add_unperformed_entries
If true, entries are created for making the nodes/pairs unperformed, and when the cost of making a no...
bool operator<(const NodeEntry &other) const
Collection of objects used to extend the Constraint Solver library.
std::pair< int, int > GetCompatibleVehicleOfType(int type, std::function< bool(int)> vehicle_is_compatible, std::function< bool(int)> stop_and_return_vehicle)
Searches for the best compatible vehicle of the given type, i.e.
const std::vector< T * > * Raw() const
int delivery_insert_after() const
void set_value(int64_t value)
void Synchronize(const Assignment *assignment, const Assignment *delta)
Synchronizes all filters to assignment.
SatParameters parameters
SweepArranger(const std::vector< std::pair< int64_t, int64_t >> &points)
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
int vehicle_class
int64_t Size() const
Returns the number of next variables in the model.
Definition: routing.h:1460
bool is_sequential
Whether the routes are constructed sequentially or in parallel.
void MakeDisjunctionNodesUnperformed(int64_t node)
Make nodes in the same disjunction as 'node' unperformed.
int64_t GetFixedCostOfVehicle(int vehicle) const
Returns the route fixed cost taken into account if the route of the vehicle is not empty,...
Definition: routing.cc:1356
void set_vehicle(int vehicle)
IntVar * var
Definition: expr_array.cc:1874
int GetEndChainStart(int vehicle) const
Returns the start of the end chain of vehicle,.
virtual bool InitializeSolution()
Virtual method to initialize the solution.
int64_t index
What follows is relevant for models with time/state dependent transits.
Definition: routing.h:260
#define ABSL_DIE_IF_NULL
Definition: base/logging.h:41
std::function< int64_t(int64_t, int64_t, int64_t)> evaluator_
int nodes
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
Definition: routing.h:1283
bool use_neighbors_ratio_for_initialization
If true, only closest neighbors (see neighbors_ratio and min_neighbors) are considered as insertion p...
void AddNewSaving(const Saving &saving, int64_t total_cost, int64_t before_node, int64_t after_node, int vehicle_type)
FirstSolutionStrategy::Value AutomaticFirstSolutionStrategy(bool has_pickup_deliveries, bool has_node_precedences, bool has_single_vehicle_node)
Returns the best value for the automatic first solution strategy, based on the given model parameters...
double angle
void Resize(size_t size)
Advanced usage: Resizes the container, potentially adding elements with null variables.
int GetStartChainEnd(int vehicle) const
Returns the end of the start chain of vehicle,.
IntVarFilteredDecisionBuilder(std::unique_ptr< IntVarFilteredHeuristic > heuristic)
const IntContainer & IntVarContainer() const
Assignment *const BuildSolution()
Builds a solution.
void AppendEvaluatedPositionsAfter(int64_t node_to_insert, int64_t start, int64_t next_after_start, int64_t vehicle, std::vector< ValuedPosition > *valued_positions)
Helper method to the ComputeEvaluatorSortedPositions* methods.
virtual int64_t Max() const =0
double distance
bool operator<(const PairEntry &other) const
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:889
int64_t GetAfterNodeFromSaving(const Saving &saving) const
Returns the "after node" from a saving.
DecisionBuilder * MakeSweepDecisionBuilder(RoutingModel *model, bool check_assignment)
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: macros.h:29
Utility class to encapsulate an IntVarIterator and use it in a range-based loop.
int64_t number_of_decisions() const
Returns statistics from its underlying heuristic.
int64_t Start(int vehicle) const
Model inspection.
Definition: routing.h:1277
int64_t value() const