OR-Tools  9.0
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) {
172  return FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
173  }
174  if (has_single_vehicle_node) {
175  return FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC;
176  }
177  return FirstSolutionStrategy::PATH_CHEAPEST_ARC;
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 
2754 SavingsFilteredHeuristic::SavingsFilteredHeuristic(
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
const std::vector< IntVar * > vars_
Definition: alldiff_cst.cc:44
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK(condition)
Definition: base/logging.h:498
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:895
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:894
#define CHECK_LT(val1, val2)
Definition: base/logging.h:708
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:705
#define CHECK_GE(val1, val2)
Definition: base/logging.h:709
#define CHECK_GT(val1, val2)
Definition: base/logging.h:710
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:897
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:898
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:896
#define DCHECK(condition)
Definition: base/logging.h:892
#define CHECK_LE(val1, val2)
Definition: base/logging.h:707
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:893
#define ABSL_DIE_IF_NULL
Definition: base/logging.h:41
#define VLOG(verboselevel)
Definition: base/logging.h:986
const std::vector< T * > * Raw() const
bool Contains(const T *val) const
E * AddAtPosition(V *var, int position)
Advanced usage: Adds element at a given position; position has to have been allocated with Assignment...
const E & Element(const V *const var) const
void Resize(size_t size)
Advanced usage: Resizes the container, potentially adding elements with null variables.
An Assignment is a variable -> domains mapping, used to report solutions to the user.
const IntContainer & IntVarContainer() const
void SetValue(const IntVar *const var, int64_t value)
int64_t Value(const IntVar *const var) const
IntVarElement * Add(IntVar *const var)
Filtered-base decision builder based on the addition heuristic, extending a path from its start node ...
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
CheapestAdditionFilteredHeuristic(RoutingModel *model, 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 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.
std::function< int64_t(int64_t, int64_t, int64_t)> evaluator_
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,...
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 GetUnperformedValue(int64_t node_to_insert) const
Returns the cost of unperforming node 'node_to_insert'.
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.
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...
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
ChristofidesFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager, bool use_minimum_matching)
std::vector< NodeIndex > TravelingSalesmanPath()
Definition: christofides.h:241
void SetMatchingAlgorithm(MatchingAlgorithm matching)
Definition: christofides.h:61
ComparatorCheapestAdditionFilteredHeuristic(RoutingModel *model, Solver::VariableValueComparator comparator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
A DecisionBuilder is responsible for creating the search tree.
A Decision represents a choice point in the search tree.
EvaluatorCheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
bool operator<(const NodeEntry &other) const
int64_t value() const
int vehicle() const
void set_vehicle(int vehicle)
int insert_after() const
int GetHeapIndex() const
void set_insert_after(int insert_after)
NodeEntry(int node_to_insert, int insert_after, int vehicle)
void SetHeapIndex(int h)
void set_value(int64_t value)
int node_to_insert() const
bool operator<(const PairEntry &other) const
int64_t value() const
int vehicle() const
void set_vehicle(int vehicle)
int GetHeapIndex() const
PairEntry(int pickup_to_insert, int pickup_insert_after, int delivery_to_insert, int delivery_insert_after, int vehicle)
int pickup_insert_after() const
void set_pickup_insert_after(int pickup_insert_after)
int delivery_insert_after() const
void SetHeapIndex(int h)
int delivery_to_insert() const
void set_value(int64_t value)
int pickup_to_insert() const
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
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.
Utility class to encapsulate an IntVarIterator and use it in a range-based loop.
virtual int64_t Min() const =0
virtual int64_t Max() const =0
Decision * Next(Solver *solver) override
This is the main method of the decision builder class.
int64_t number_of_decisions() const
Returns statistics from its underlying heuristic.
IntVarFilteredDecisionBuilder(std::unique_ptr< IntVarFilteredHeuristic > heuristic)
Generic filter-based heuristic applied to IntVars.
void SetValue(int64_t index, int64_t value)
Modifies the current solution by setting the variable of index 'index' to value 'value'.
virtual bool BuildSolutionInternal()=0
Virtual method to redefine how to build a solution.
int Size() const
Returns the number of variables the decision builder is trying to instantiate.
bool Contains(int64_t index) const
Returns true if the variable of index 'index' is in the current solution.
bool Commit()
Commits the modifications to the current solution if these modifications are "filter-feasible",...
void ResetSolution()
Resets the data members for a new solution.
void SynchronizeFilters()
Synchronizes filters with an assignment (the current solution).
virtual bool InitializeSolution()
Virtual method to initialize the solution.
int64_t Value(int64_t index) const
Returns the value of the variable of index 'index' in the last committed solution.
IntVar * Var(int64_t index) const
Returns the variable of index 'index'.
IntVarFilteredHeuristic(Solver *solver, const std::vector< IntVar * > &vars, LocalSearchFilterManager *filter_manager)
Assignment *const BuildSolution()
Builds a solution.
The class IntVar is a subset of IntExpr.
virtual bool Contains(int64_t v) const =0
This method returns whether the value 'v' is in the domain of the variable.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
LocalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
Filter manager: when a move is made, filters are executed to decide whether the solution is feasible ...
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...
void Synchronize(const Assignment *assignment, const Assignment *delta)
Synchronizes all filters to assignment.
The base class for all local search operators.
virtual bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta)=0
virtual void Start(const Assignment *assignment)=0
virtual int64_t RangeMinArgument(int64_t from, int64_t to) const =0
Dimensions represent quantities accumulated at nodes along the routes.
Definition: routing.h:2374
const RoutingDimension * base_dimension() const
Returns the parent in the dependency tree if any or nullptr otherwise.
Definition: routing.h:2617
int64_t ShortestTransitionSlack(int64_t node) const
It makes sense to use the function only for self-dependent dimension.
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:2393
Filter-based heuristic dedicated to routing.
int GetStartChainEnd(int vehicle) const
Returns the end of the start chain of vehicle,.
virtual void SetVehicleIndex(int64_t node, int vehicle)
int GetEndChainStart(int vehicle) const
Returns the start of the end chain of vehicle,.
void MakeDisjunctionNodesUnperformed(int64_t node)
Make nodes in the same disjunction as 'node' unperformed.
bool StopSearch() override
Returns true if the search must be stopped.
void MakeUnassignedNodesUnperformed()
Make all unassigned nodes unperformed.
void MakePartiallyPerformedPairsUnperformed()
Make all partially performed pickup and delivery pairs unperformed.
const Assignment * BuildSolutionFromRoutes(const std::function< int64_t(int64_t)> &next_accessor)
Builds a solution starting from the routes formed by the next accessor.
RoutingFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager)
Manager for any NodeIndex <-> variable index conversion.
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:634
RoutingIndexPair IndexPair
Definition: routing.h:242
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:1252
const std::vector< std::pair< int, int > > & GetDeliveryIndexPairs(int64_t node_index) const
Same as above for deliveries.
Definition: routing.cc:1757
static std::unique_ptr< LocalSearchOperator > MakeGreedyDescentLSOperator(std::vector< IntVar * > variables)
Perhaps move it to constraint_solver.h.
IntVar * VehicleVar(int64_t index) const
Returns the vehicle variable of the node corresponding to index.
Definition: routing.h:1226
int64_t Size() const
Returns the number of next variables in the model.
Definition: routing.h:1352
RoutingIndexPairs IndexPairs
Definition: routing.h:243
const IndexPairs & GetPickupAndDeliveryPairs() const
Returns pickup and delivery pairs currently in the model.
Definition: routing.h:741
int64_t Start(int vehicle) const
Model inspection.
Definition: routing.h:1184
int vehicles() const
Returns the number of vehicle routes in the model.
Definition: routing.h:1350
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.
IntVar * CostVar() const
Returns the global cost variable which is being minimized.
Definition: routing.h:1228
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:3369
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:1751
DecisionBuilder * MakeSelfDependentDimensionFinalizer(const RoutingDimension *dimension)
SWIG
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
Definition: routing.h:1190
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
Definition: routing.h:1273
CostClassIndex GetCostClassIndexOfVehicle(int64_t vehicle) const
Get the cost class index of the given vehicle.
Definition: routing.h:1256
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
Definition: routing.h:1186
SavingsContainer(const SavingsFilteredHeuristic *savings_db, int vehicle_types)
void InitializeContainer(int64_t size, int64_t saving_neighbors)
const std::vector< Saving > & GetSortedSavingsForVehicleType(int type)
void AddNewSaving(const Saving &saving, int64_t total_cost, int64_t before_node, int64_t after_node, int vehicle_type)
Filter-based decision builder which builds a solution by using Clarke & Wright's Savings heuristic.
int64_t GetVehicleTypeFromSaving(const Saving &saving) const
Returns the cost class from a saving.
std::unique_ptr< VehicleTypeCurator > vehicle_type_curator_
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
int64_t GetAfterNodeFromSaving(const Saving &saving) const
Returns the "after node" from a saving.
int64_t GetSavingValue(const Saving &saving) const
Returns the saving value from a saving.
std::unique_ptr< SavingsContainer< Saving > > savings_container_
virtual double ExtraSavingsMemoryMultiplicativeFactor() const =0
int64_t GetBeforeNodeFromSaving(const Saving &saving) const
Returns the "before node" from a saving.
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...
LocalSearchMonitor * GetLocalSearchMonitor() const
Returns the local search monitor.
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
std::function< bool(int64_t, int64_t, int64_t)> VariableValueComparator
void ArrangeIndices(std::vector< int64_t > *indices)
SweepArranger(const std::vector< std::pair< int64_t, int64_t >> &points)
void Update(const std::function< bool(int)> &remove_vehicle)
Goes through all the currently stored vehicles and removes vehicles for which remove_vehicle() return...
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.
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...
Block * next
SatParameters parameters
IntVar * var
Definition: expr_array.cc:1874
const std::vector< IntVar * > cumuls_
GRBmodel * model
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: macros.h:29
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
Definition: map_util.h:154
void STLClearObject(T *obj)
Definition: stl_util.h:123
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:200
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:263
std::function< int64_t(const Model &)> Value(IntegerVariable v)
Definition: integer.h:1492
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
int64_t CapSub(int64_t x, int64_t y)
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...
DecisionBuilder * MakeSweepDecisionBuilder(RoutingModel *model, bool check_assignment)
static const int kUnassigned
Definition: routing.cc:676
int64_t delta
Definition: resource.cc:1692
int64_t cost
int vehicle_class
int head
std::pair< int, int > link
int nodes
double distance
double angle
int64_t start_depot
int64_t index
int64_t value
ABSL_FLAG(bool, routing_shift_insertion_cost_by_penalty, true, "Shift insertion costs by the penalty of the inserted node(s).")
int64_t end_depot
int tail
std::function< int64_t(int64_t, int64_t)> evaluator_
Definition: search.cc:1368
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
bool is_sequential
Whether the routes are constructed sequentially or in parallel.
double farthest_seeds_ratio
The ratio of routes on which to insert farthest nodes as seeds before starting the cheapest insertion...
bool use_neighbors_ratio_for_initialization
If true, only closest neighbors (see neighbors_ratio and min_neighbors) are considered as insertion p...
bool add_unperformed_entries
If true, entries are created for making the nodes/pairs unperformed, and when the cost of making a no...
What follows is relevant for models with time/state dependent transits.
Definition: routing.h:259
RangeMinMaxIndexFunction * transit_plus_identity
f(x)
Definition: routing.h:261
Definition: routing.h:354
std::vector< std::set< VehicleClassEntry > > sorted_vehicle_classes_per_type
Definition: routing.h:373
std::vector< std::deque< int > > vehicles_per_vehicle_class
Definition: routing.h:374
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
double arc_coefficient
arc_coefficient is a strictly positive parameter indicating the coefficient of the arc being consider...
double max_memory_usage_bytes
The number of neighbors considered for each node is also adapted so that the stored Savings don't use...
bool add_reverse_arcs
If add_reverse_arcs is true, the neighborhood relationships are considered symmetrically.