OR-Tools  8.0
routing_search.cc
Go to the documentation of this file.
1 // Copyright 2010-2018 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 
19 #include <algorithm>
20 #include <cstdlib>
21 #include <map>
22 #include <numeric>
23 #include <set>
24 #include <utility>
25 
26 #include "absl/container/flat_hash_map.h"
27 #include "absl/container/flat_hash_set.h"
30 #include "ortools/base/map_util.h"
31 #include "ortools/base/small_map.h"
33 #include "ortools/base/stl_util.h"
38 #include "ortools/util/bitset.h"
40 
41 DEFINE_bool(routing_strong_debug_checks, false,
42  "Run stronger checks in debug; these stronger tests might change "
43  "the complexity of the code in particular.");
44 DEFINE_bool(routing_shift_insertion_cost_by_penalty, true,
45  "Shift insertion costs by the penalty of the inserted node(s).");
46 
47 namespace operations_research {
48 
49 namespace {
50 
51 // Max active vehicles filter.
52 
53 class MaxActiveVehiclesFilter : public IntVarLocalSearchFilter {
54  public:
55  explicit MaxActiveVehiclesFilter(const RoutingModel& routing_model)
56  : IntVarLocalSearchFilter(routing_model.Nexts()),
57  routing_model_(routing_model),
58  is_active_(routing_model.vehicles(), false),
59  active_vehicles_(0) {}
60  bool Accept(const Assignment* delta, const Assignment* deltadelta,
61  int64 objective_min, int64 objective_max) override {
62  const int64 kUnassigned = -1;
63  const Assignment::IntContainer& container = delta->IntVarContainer();
64  const int delta_size = container.Size();
65  int current_active_vehicles = active_vehicles_;
66  for (int i = 0; i < delta_size; ++i) {
67  const IntVarElement& new_element = container.Element(i);
68  IntVar* const var = new_element.Var();
70  if (FindIndex(var, &index) && routing_model_.IsStart(index)) {
71  if (new_element.Min() != new_element.Max()) {
72  // LNS detected.
73  return true;
74  }
75  const int vehicle = routing_model_.VehicleIndex(index);
76  const bool is_active =
77  (new_element.Min() != routing_model_.End(vehicle));
78  if (is_active && !is_active_[vehicle]) {
79  ++current_active_vehicles;
80  } else if (!is_active && is_active_[vehicle]) {
81  --current_active_vehicles;
82  }
83  }
84  }
85  return current_active_vehicles <=
86  routing_model_.GetMaximumNumberOfActiveVehicles();
87  }
88 
89  private:
90  void OnSynchronize(const Assignment* delta) override {
91  active_vehicles_ = 0;
92  for (int i = 0; i < routing_model_.vehicles(); ++i) {
93  const int index = routing_model_.Start(i);
94  if (IsVarSynced(index) && Value(index) != routing_model_.End(i)) {
95  is_active_[i] = true;
96  ++active_vehicles_;
97  } else {
98  is_active_[i] = false;
99  }
100  }
101  }
102 
103  const RoutingModel& routing_model_;
104  std::vector<bool> is_active_;
105  int active_vehicles_;
106 };
107 } // namespace
108 
110  const RoutingModel& routing_model) {
111  return routing_model.solver()->RevAlloc(
112  new MaxActiveVehiclesFilter(routing_model));
113 }
114 
115 namespace {
116 
117 // Node disjunction filter class.
118 
119 class NodeDisjunctionFilter : public IntVarLocalSearchFilter {
120  public:
121  explicit NodeDisjunctionFilter(const RoutingModel& routing_model)
122  : IntVarLocalSearchFilter(routing_model.Nexts()),
123  routing_model_(routing_model),
124  active_per_disjunction_(routing_model.GetNumberOfDisjunctions(), 0),
125  inactive_per_disjunction_(routing_model.GetNumberOfDisjunctions(), 0),
126  synchronized_objective_value_(kint64min),
127  accepted_objective_value_(kint64min) {}
128 
129  bool Accept(const Assignment* delta, const Assignment* deltadelta,
130  int64 objective_min, int64 objective_max) override {
131  const int64 kUnassigned = -1;
132  const Assignment::IntContainer& container = delta->IntVarContainer();
133  const int delta_size = container.Size();
135  disjunction_active_deltas;
137  disjunction_inactive_deltas;
138  bool lns_detected = false;
139  // Update active/inactive count per disjunction for each element of delta.
140  for (int i = 0; i < delta_size; ++i) {
141  const IntVarElement& new_element = container.Element(i);
142  IntVar* const var = new_element.Var();
144  if (FindIndex(var, &index)) {
145  const bool is_inactive =
146  (new_element.Min() <= index && new_element.Max() >= index);
147  if (new_element.Min() != new_element.Max()) {
148  lns_detected = true;
149  }
150  for (const RoutingModel::DisjunctionIndex disjunction_index :
151  routing_model_.GetDisjunctionIndices(index)) {
152  const bool active_state_changed =
153  !IsVarSynced(index) || (Value(index) == index) != is_inactive;
154  if (active_state_changed) {
155  if (!is_inactive) {
156  ++gtl::LookupOrInsert(&disjunction_active_deltas,
157  disjunction_index, 0);
158  if (IsVarSynced(index)) {
159  --gtl::LookupOrInsert(&disjunction_inactive_deltas,
160  disjunction_index, 0);
161  }
162  } else {
163  ++gtl::LookupOrInsert(&disjunction_inactive_deltas,
164  disjunction_index, 0);
165  if (IsVarSynced(index)) {
166  --gtl::LookupOrInsert(&disjunction_active_deltas,
167  disjunction_index, 0);
168  }
169  }
170  }
171  }
172  }
173  }
174  // Check if any disjunction has too many active nodes.
175  for (const std::pair<RoutingModel::DisjunctionIndex, int>
176  disjunction_active_delta : disjunction_active_deltas) {
177  const int current_active_nodes =
178  active_per_disjunction_[disjunction_active_delta.first];
179  const int active_nodes =
180  current_active_nodes + disjunction_active_delta.second;
181  const int max_cardinality = routing_model_.GetDisjunctionMaxCardinality(
182  disjunction_active_delta.first);
183  // Too many active nodes.
184  if (active_nodes > max_cardinality) {
185  return false;
186  }
187  }
188  // Update penalty costs for disjunctions.
189  accepted_objective_value_ = synchronized_objective_value_;
190  for (const std::pair<RoutingModel::DisjunctionIndex, int>
191  disjunction_inactive_delta : disjunction_inactive_deltas) {
192  const int64 penalty = routing_model_.GetDisjunctionPenalty(
193  disjunction_inactive_delta.first);
194  if (penalty != 0 && !lns_detected) {
195  const RoutingModel::DisjunctionIndex disjunction_index =
196  disjunction_inactive_delta.first;
197  const int current_inactive_nodes =
198  inactive_per_disjunction_[disjunction_index];
199  const int inactive_nodes =
200  current_inactive_nodes + disjunction_inactive_delta.second;
201  const int max_inactive_cardinality =
202  routing_model_.GetDisjunctionIndices(disjunction_index).size() -
203  routing_model_.GetDisjunctionMaxCardinality(disjunction_index);
204  // Too many inactive nodes.
205  if (inactive_nodes > max_inactive_cardinality) {
206  if (penalty < 0) {
207  // Nodes are mandatory, i.e. exactly max_cardinality nodes must be
208  // performed, so the move is not acceptable.
209  return false;
210  } else if (current_inactive_nodes <= max_inactive_cardinality) {
211  // Add penalty if there were not too many inactive nodes before the
212  // move.
213  accepted_objective_value_ =
214  CapAdd(accepted_objective_value_, penalty);
215  }
216  } else if (current_inactive_nodes > max_inactive_cardinality) {
217  // Remove penalty if there were too many inactive nodes before the
218  // move and there are not too many after the move.
219  accepted_objective_value_ =
220  CapSub(accepted_objective_value_, penalty);
221  }
222  }
223  }
224  if (lns_detected) {
225  accepted_objective_value_ = 0;
226  return true;
227  } else {
228  // Only compare to max as a cost lower bound is computed.
229  return accepted_objective_value_ <= objective_max;
230  }
231  }
232  std::string DebugString() const override { return "NodeDisjunctionFilter"; }
233  int64 GetSynchronizedObjectiveValue() const override {
234  return synchronized_objective_value_;
235  }
236  int64 GetAcceptedObjectiveValue() const override {
237  return accepted_objective_value_;
238  }
239 
240  private:
241  void OnSynchronize(const Assignment* delta) override {
242  synchronized_objective_value_ = 0;
244  i < active_per_disjunction_.size(); ++i) {
245  active_per_disjunction_[i] = 0;
246  inactive_per_disjunction_[i] = 0;
247  const std::vector<int64>& disjunction_indices =
248  routing_model_.GetDisjunctionIndices(i);
249  for (const int64 index : disjunction_indices) {
250  const bool index_synced = IsVarSynced(index);
251  if (index_synced) {
252  if (Value(index) != index) {
253  ++active_per_disjunction_[i];
254  } else {
255  ++inactive_per_disjunction_[i];
256  }
257  }
258  }
259  const int64 penalty = routing_model_.GetDisjunctionPenalty(i);
260  const int max_cardinality =
261  routing_model_.GetDisjunctionMaxCardinality(i);
262  if (inactive_per_disjunction_[i] >
263  disjunction_indices.size() - max_cardinality &&
264  penalty > 0) {
265  synchronized_objective_value_ =
266  CapAdd(synchronized_objective_value_, penalty);
267  }
268  }
269  }
270 
271  const RoutingModel& routing_model_;
272 
274  gtl::ITIVector<RoutingModel::DisjunctionIndex, int> inactive_per_disjunction_;
275  int64 synchronized_objective_value_;
276  int64 accepted_objective_value_;
277 };
278 } // namespace
279 
281  const RoutingModel& routing_model) {
282  return routing_model.solver()->RevAlloc(
283  new NodeDisjunctionFilter(routing_model));
284 }
285 
287 
288 BasePathFilter::BasePathFilter(const std::vector<IntVar*>& nexts,
289  int next_domain_size)
290  : IntVarLocalSearchFilter(nexts),
291  node_path_starts_(next_domain_size, kUnassigned),
292  paths_(nexts.size(), -1),
293  new_synchronized_unperformed_nodes_(nexts.size()),
294  new_nexts_(nexts.size(), kUnassigned),
295  touched_paths_(nexts.size()),
296  touched_path_nodes_(next_domain_size),
297  ranks_(next_domain_size, -1),
298  status_(BasePathFilter::UNKNOWN) {}
299 
300 bool BasePathFilter::Accept(const Assignment* delta,
301  const Assignment* deltadelta, int64 objective_min,
302  int64 objective_max) {
303  if (IsDisabled()) return true;
304  for (const int touched : delta_touched_) {
305  new_nexts_[touched] = kUnassigned;
306  }
307  delta_touched_.clear();
308  const Assignment::IntContainer& container = delta->IntVarContainer();
309  const int delta_size = container.Size();
310  delta_touched_.reserve(delta_size);
311  // Determining touched paths and touched nodes (a node is touched if it
312  // corresponds to an element of delta or that an element of delta points to
313  // it.
314  touched_paths_.SparseClearAll();
315  touched_path_nodes_.SparseClearAll();
316  for (int i = 0; i < delta_size; ++i) {
317  const IntVarElement& new_element = container.Element(i);
318  IntVar* const var = new_element.Var();
320  if (FindIndex(var, &index)) {
321  if (!new_element.Bound()) {
322  // LNS detected
323  return true;
324  }
325  new_nexts_[index] = new_element.Value();
326  delta_touched_.push_back(index);
327  const int64 start = node_path_starts_[index];
328  touched_path_nodes_.Set(index);
329  touched_path_nodes_.Set(new_nexts_[index]);
330  if (start != kUnassigned) {
331  touched_paths_.Set(start);
332  }
333  }
334  }
335  // Checking feasibility of touched paths.
336  InitializeAcceptPath();
337  bool accept = true;
338  // Finding touched subchains from ranks of touched nodes in paths; the first
339  // and last node of a subchain will have remained on the same path and will
340  // correspond to the min and max ranks of touched nodes in the current
341  // assignment.
342  for (const int64 touched_start : touched_paths_.PositionsSetAtLeastOnce()) {
343  int min_rank = kint32max;
344  int64 start = kUnassigned;
345  int max_rank = kint32min;
346  int64 end = kUnassigned;
347  // Linear search on touched nodes is ok since there shouldn't be many of
348  // them.
349  // TODO(user): Remove the linear loop.
350  for (const int64 touched_path_node :
351  touched_path_nodes_.PositionsSetAtLeastOnce()) {
352  if (node_path_starts_[touched_path_node] == touched_start) {
353  const int rank = ranks_[touched_path_node];
354  if (rank < min_rank) {
355  min_rank = rank;
356  start = touched_path_node;
357  }
358  if (rank > max_rank) {
359  max_rank = rank;
360  end = touched_path_node;
361  }
362  }
363  }
364  if (!AcceptPath(touched_start, start, end)) {
365  accept = false;
366  break;
367  }
368  }
369  // NOTE: FinalizeAcceptPath() is only called if all paths are accepted.
370  return accept && FinalizeAcceptPath(delta, objective_min, objective_max);
371 }
372 
373 void BasePathFilter::ComputePathStarts(std::vector<int64>* path_starts,
374  std::vector<int>* index_to_path) {
375  path_starts->clear();
376  const int nexts_size = Size();
377  index_to_path->assign(nexts_size, kUnassigned);
378  Bitset64<> has_prevs(nexts_size);
379  for (int i = 0; i < nexts_size; ++i) {
380  if (!IsVarSynced(i)) {
381  has_prevs.Set(i);
382  } else {
383  const int next = Value(i);
384  if (next < nexts_size) {
385  has_prevs.Set(next);
386  }
387  }
388  }
389  for (int i = 0; i < nexts_size; ++i) {
390  if (!has_prevs[i]) {
391  (*index_to_path)[i] = path_starts->size();
392  path_starts->push_back(i);
393  }
394  }
395 }
396 
397 bool BasePathFilter::HavePathsChanged() {
398  std::vector<int64> path_starts;
399  std::vector<int> index_to_path(Size(), kUnassigned);
400  ComputePathStarts(&path_starts, &index_to_path);
401  if (path_starts.size() != starts_.size()) {
402  return true;
403  }
404  for (int i = 0; i < path_starts.size(); ++i) {
405  if (path_starts[i] != starts_[i]) {
406  return true;
407  }
408  }
409  for (int i = 0; i < Size(); ++i) {
410  if (index_to_path[i] != paths_[i]) {
411  return true;
412  }
413  }
414  return false;
415 }
416 
417 void BasePathFilter::SynchronizeFullAssignment() {
418  // Subclasses of BasePathFilter might not propagate injected objective values
419  // so making sure it is done here (can be done again by the subclass if
420  // needed).
421  ComputePathStarts(&starts_, &paths_);
422  for (int64 index = 0; index < Size(); index++) {
423  if (IsVarSynced(index) && Value(index) == index &&
424  node_path_starts_[index] != kUnassigned) {
425  // index was performed before and is now unperformed.
426  new_synchronized_unperformed_nodes_.Set(index);
427  }
428  }
429  // Marking unactive nodes (which are not on a path).
430  node_path_starts_.assign(node_path_starts_.size(), kUnassigned);
431  // Marking nodes on a path and storing next values.
432  const int nexts_size = Size();
433  for (const int64 start : starts_) {
434  int node = start;
435  node_path_starts_[node] = start;
436  DCHECK(IsVarSynced(node));
437  int next = Value(node);
438  while (next < nexts_size) {
439  node = next;
440  node_path_starts_[node] = start;
441  DCHECK(IsVarSynced(node));
442  next = Value(node);
443  }
444  node_path_starts_[next] = start;
445  }
446  OnBeforeSynchronizePaths();
447  UpdateAllRanks();
448  OnAfterSynchronizePaths();
449 }
450 
451 void BasePathFilter::OnSynchronize(const Assignment* delta) {
452  if (status_ == BasePathFilter::UNKNOWN) {
453  status_ =
454  DisableFiltering() ? BasePathFilter::DISABLED : BasePathFilter::ENABLED;
455  }
456  if (IsDisabled()) return;
457  new_synchronized_unperformed_nodes_.ClearAll();
458  if (delta == nullptr || delta->Empty() || starts_.empty()) {
459  SynchronizeFullAssignment();
460  return;
461  }
462  // Subclasses of BasePathFilter might not propagate injected objective values
463  // so making sure it is done here (can be done again by the subclass if
464  // needed).
465  // This code supposes that path starts didn't change.
466  DCHECK(!FLAGS_routing_strong_debug_checks || !HavePathsChanged());
467  const Assignment::IntContainer& container = delta->IntVarContainer();
468  touched_paths_.SparseClearAll();
469  for (int i = 0; i < container.Size(); ++i) {
470  const IntVarElement& new_element = container.Element(i);
472  if (FindIndex(new_element.Var(), &index)) {
473  const int64 start = node_path_starts_[index];
474  if (start != kUnassigned) {
475  touched_paths_.Set(start);
476  if (Value(index) == index) {
477  // New unperformed node (its previous start isn't unassigned).
478  DCHECK_LT(index, new_nexts_.size());
479  new_synchronized_unperformed_nodes_.Set(index);
480  node_path_starts_[index] = kUnassigned;
481  }
482  }
483  }
484  }
485  OnBeforeSynchronizePaths();
486  for (const int64 touched_start : touched_paths_.PositionsSetAtLeastOnce()) {
487  int64 node = touched_start;
488  while (node < Size()) {
489  node_path_starts_[node] = touched_start;
490  node = Value(node);
491  }
492  node_path_starts_[node] = touched_start;
493  UpdatePathRanksFromStart(touched_start);
494  OnSynchronizePathFromStart(touched_start);
495  }
496  OnAfterSynchronizePaths();
497 }
498 
499 void BasePathFilter::UpdateAllRanks() {
500  for (int i = 0; i < ranks_.size(); ++i) {
501  ranks_[i] = kUnassigned;
502  }
503  for (int r = 0; r < NumPaths(); ++r) {
504  UpdatePathRanksFromStart(Start(r));
505  OnSynchronizePathFromStart(Start(r));
506  }
507 }
508 
509 void BasePathFilter::UpdatePathRanksFromStart(int start) {
510  int rank = 0;
511  int64 node = start;
512  while (node < Size()) {
513  ranks_[node] = rank;
514  rank++;
515  node = Value(node);
516  }
517  ranks_[node] = rank;
518 }
519 
520 namespace {
521 
522 class VehicleAmortizedCostFilter : public BasePathFilter {
523  public:
524  explicit VehicleAmortizedCostFilter(const RoutingModel& routing_model);
525  ~VehicleAmortizedCostFilter() override {}
526  std::string DebugString() const override {
527  return "VehicleAmortizedCostFilter";
528  }
529  int64 GetSynchronizedObjectiveValue() const override {
530  return current_vehicle_cost_;
531  }
532  int64 GetAcceptedObjectiveValue() const override {
533  return delta_vehicle_cost_;
534  }
535 
536  private:
537  void OnSynchronizePathFromStart(int64 start) override;
538  void OnAfterSynchronizePaths() override;
539  void InitializeAcceptPath() override;
540  bool AcceptPath(int64 path_start, int64 chain_start,
541  int64 chain_end) override;
542  bool FinalizeAcceptPath(const Assignment* delta, int64 objective_min,
543  int64 objective_max) override;
544 
545  int64 current_vehicle_cost_;
546  int64 delta_vehicle_cost_;
547  std::vector<int> current_route_lengths_;
548  std::vector<int64> start_to_end_;
549  std::vector<int> start_to_vehicle_;
550  std::vector<int64> vehicle_to_start_;
551  const std::vector<int64>& linear_cost_factor_of_vehicle_;
552  const std::vector<int64>& quadratic_cost_factor_of_vehicle_;
553 };
554 
555 VehicleAmortizedCostFilter::VehicleAmortizedCostFilter(
556  const RoutingModel& routing_model)
557  : BasePathFilter(routing_model.Nexts(),
558  routing_model.Size() + routing_model.vehicles()),
559  current_vehicle_cost_(0),
560  delta_vehicle_cost_(0),
561  current_route_lengths_(Size(), -1),
562  linear_cost_factor_of_vehicle_(
563  routing_model.GetAmortizedLinearCostFactorOfVehicles()),
564  quadratic_cost_factor_of_vehicle_(
565  routing_model.GetAmortizedQuadraticCostFactorOfVehicles()) {
566  start_to_end_.resize(Size(), -1);
567  start_to_vehicle_.resize(Size(), -1);
568  vehicle_to_start_.resize(routing_model.vehicles());
569  for (int v = 0; v < routing_model.vehicles(); v++) {
570  const int64 start = routing_model.Start(v);
571  start_to_vehicle_[start] = v;
572  start_to_end_[start] = routing_model.End(v);
573  vehicle_to_start_[v] = start;
574  }
575 }
576 
577 void VehicleAmortizedCostFilter::OnSynchronizePathFromStart(int64 start) {
578  const int64 end = start_to_end_[start];
579  CHECK_GE(end, 0);
580  const int route_length = Rank(end) - 1;
581  CHECK_GE(route_length, 0);
582  current_route_lengths_[start] = route_length;
583 }
584 
585 void VehicleAmortizedCostFilter::OnAfterSynchronizePaths() {
586  current_vehicle_cost_ = 0;
587  for (int vehicle = 0; vehicle < vehicle_to_start_.size(); vehicle++) {
588  const int64 start = vehicle_to_start_[vehicle];
589  DCHECK_EQ(vehicle, start_to_vehicle_[start]);
590 
591  const int route_length = current_route_lengths_[start];
592  DCHECK_GE(route_length, 0);
593 
594  if (route_length == 0) {
595  // The path is empty.
596  continue;
597  }
598 
599  const int64 linear_cost_factor = linear_cost_factor_of_vehicle_[vehicle];
600  const int64 route_length_cost =
601  CapProd(quadratic_cost_factor_of_vehicle_[vehicle],
602  route_length * route_length);
603 
604  current_vehicle_cost_ = CapAdd(
605  current_vehicle_cost_, CapSub(linear_cost_factor, route_length_cost));
606  }
607 }
608 
609 void VehicleAmortizedCostFilter::InitializeAcceptPath() {
610  delta_vehicle_cost_ = current_vehicle_cost_;
611 }
612 
613 bool VehicleAmortizedCostFilter::AcceptPath(int64 path_start, int64 chain_start,
614  int64 chain_end) {
615  // Number of nodes previously between chain_start and chain_end
616  const int previous_chain_nodes = Rank(chain_end) - 1 - Rank(chain_start);
617  CHECK_GE(previous_chain_nodes, 0);
618  int new_chain_nodes = 0;
619  int64 node = GetNext(chain_start);
620  while (node != chain_end) {
621  new_chain_nodes++;
622  node = GetNext(node);
623  }
624 
625  const int previous_route_length = current_route_lengths_[path_start];
626  CHECK_GE(previous_route_length, 0);
627  const int new_route_length =
628  previous_route_length - previous_chain_nodes + new_chain_nodes;
629 
630  const int vehicle = start_to_vehicle_[path_start];
631  CHECK_GE(vehicle, 0);
632  DCHECK_EQ(path_start, vehicle_to_start_[vehicle]);
633 
634  // Update the cost related to used vehicles.
635  // TODO(user): Handle possible overflows.
636  if (previous_route_length == 0) {
637  // The route was empty before, it is no longer the case (changed path).
638  CHECK_GT(new_route_length, 0);
639  delta_vehicle_cost_ =
640  CapAdd(delta_vehicle_cost_, linear_cost_factor_of_vehicle_[vehicle]);
641  } else if (new_route_length == 0) {
642  // The route is now empty.
643  delta_vehicle_cost_ =
644  CapSub(delta_vehicle_cost_, linear_cost_factor_of_vehicle_[vehicle]);
645  }
646 
647  // Update the cost related to the sum of the squares of the route lengths.
648  const int64 quadratic_cost_factor =
649  quadratic_cost_factor_of_vehicle_[vehicle];
650  delta_vehicle_cost_ =
651  CapAdd(delta_vehicle_cost_,
652  CapProd(quadratic_cost_factor,
653  previous_route_length * previous_route_length));
654  delta_vehicle_cost_ = CapSub(
655  delta_vehicle_cost_,
656  CapProd(quadratic_cost_factor, new_route_length * new_route_length));
657 
658  return true;
659 }
660 
661 bool VehicleAmortizedCostFilter::FinalizeAcceptPath(const Assignment* delta,
662  int64 objective_min,
663  int64 objective_max) {
664  return delta_vehicle_cost_ <= objective_max;
665 }
666 
667 } // namespace
668 
670  const RoutingModel& routing_model) {
671  return routing_model.solver()->RevAlloc(
672  new VehicleAmortizedCostFilter(routing_model));
673 }
674 
675 namespace {
676 
677 class TypeRegulationsFilter : public BasePathFilter {
678  public:
679  explicit TypeRegulationsFilter(const RoutingModel& model);
680  ~TypeRegulationsFilter() override {}
681  std::string DebugString() const override { return "TypeRegulationsFilter"; }
682 
683  private:
684  void OnSynchronizePathFromStart(int64 start) override;
685  bool AcceptPath(int64 path_start, int64 chain_start,
686  int64 chain_end) override;
687 
688  bool HardIncompatibilitiesRespected(int vehicle, int64 chain_start,
689  int64 chain_end);
690 
691  const RoutingModel& routing_model_;
692  std::vector<int> start_to_vehicle_;
693  // The following vector is used to keep track of the type counts for hard
694  // incompatibilities.
695  std::vector<std::vector<int>> hard_incompatibility_type_counts_per_vehicle_;
696  // Used to verify the temporal incompatibilities and requirements.
697  TypeIncompatibilityChecker temporal_incompatibility_checker_;
698  TypeRequirementChecker requirement_checker_;
699 };
700 
701 TypeRegulationsFilter::TypeRegulationsFilter(const RoutingModel& model)
702  : BasePathFilter(model.Nexts(), model.Size() + model.vehicles()),
703  routing_model_(model),
704  start_to_vehicle_(model.Size(), -1),
705  temporal_incompatibility_checker_(model,
706  /*check_hard_incompatibilities*/ false),
707  requirement_checker_(model) {
708  const int num_vehicles = model.vehicles();
709  const bool has_hard_type_incompatibilities =
710  model.HasHardTypeIncompatibilities();
711  if (has_hard_type_incompatibilities) {
712  hard_incompatibility_type_counts_per_vehicle_.resize(num_vehicles);
713  }
714  const int num_visit_types = model.GetNumberOfVisitTypes();
715  for (int vehicle = 0; vehicle < num_vehicles; vehicle++) {
716  const int64 start = model.Start(vehicle);
717  start_to_vehicle_[start] = vehicle;
718  if (has_hard_type_incompatibilities) {
719  hard_incompatibility_type_counts_per_vehicle_[vehicle].resize(
720  num_visit_types, 0);
721  }
722  }
723 }
724 
725 void TypeRegulationsFilter::OnSynchronizePathFromStart(int64 start) {
726  if (!routing_model_.HasHardTypeIncompatibilities()) return;
727 
728  const int vehicle = start_to_vehicle_[start];
729  CHECK_GE(vehicle, 0);
730  std::vector<int>& type_counts =
731  hard_incompatibility_type_counts_per_vehicle_[vehicle];
732  std::fill(type_counts.begin(), type_counts.end(), 0);
733  const int num_types = type_counts.size();
734 
735  int64 node = start;
736  while (node < Size()) {
737  DCHECK(IsVarSynced(node));
738  const int type = routing_model_.GetVisitType(node);
739  if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
740  RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
741  CHECK_LT(type, num_types);
742  type_counts[type]++;
743  }
744  node = Value(node);
745  }
746 }
747 
748 bool TypeRegulationsFilter::HardIncompatibilitiesRespected(int vehicle,
749  int64 chain_start,
750  int64 chain_end) {
751  if (!routing_model_.HasHardTypeIncompatibilities()) return true;
752 
753  const std::vector<int>& previous_type_counts =
754  hard_incompatibility_type_counts_per_vehicle_[vehicle];
755 
756  absl::flat_hash_map</*type*/ int, /*new_count*/ int> new_type_counts;
757  absl::flat_hash_set<int> types_to_check;
758 
759  // Go through the new nodes on the path and increment their type counts.
760  int64 node = GetNext(chain_start);
761  while (node != chain_end) {
762  const int type = routing_model_.GetVisitType(node);
763  if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
764  RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
765  DCHECK_LT(type, previous_type_counts.size());
766  int& type_count = gtl::LookupOrInsert(&new_type_counts, type,
767  previous_type_counts[type]);
768  if (type_count++ == 0) {
769  // New type on the route, mark to check its incompatibilities.
770  types_to_check.insert(type);
771  }
772  }
773  node = GetNext(node);
774  }
775 
776  // Update new_type_counts by decrementing the occurrence of the types of the
777  // nodes no longer on the route.
778  node = Value(chain_start);
779  while (node != chain_end) {
780  const int type = routing_model_.GetVisitType(node);
781  if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
782  RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
783  DCHECK_LT(type, previous_type_counts.size());
784  int& type_count = gtl::LookupOrInsert(&new_type_counts, type,
785  previous_type_counts[type]);
786  CHECK_GE(type_count, 1);
787  type_count--;
788  }
789  node = Value(node);
790  }
791 
792  // Check the incompatibilities for types in types_to_check.
793  for (int type : types_to_check) {
794  for (int incompatible_type :
795  routing_model_.GetHardTypeIncompatibilitiesOfType(type)) {
796  if (gtl::FindWithDefault(new_type_counts, incompatible_type,
797  previous_type_counts[incompatible_type]) > 0) {
798  return false;
799  }
800  }
801  }
802  return true;
803 }
804 
805 bool TypeRegulationsFilter::AcceptPath(int64 path_start, int64 chain_start,
806  int64 chain_end) {
807  const int vehicle = start_to_vehicle_[path_start];
808  CHECK_GE(vehicle, 0);
809  const auto next_accessor = [this](int64 node) { return GetNext(node); };
810  return HardIncompatibilitiesRespected(vehicle, chain_start, chain_end) &&
811  temporal_incompatibility_checker_.CheckVehicle(vehicle,
812  next_accessor) &&
813  requirement_checker_.CheckVehicle(vehicle, next_accessor);
814 }
815 
816 } // namespace
817 
819  const RoutingModel& routing_model) {
820  return routing_model.solver()->RevAlloc(
821  new TypeRegulationsFilter(routing_model));
822 }
823 
824 namespace {
825 
826 // ChainCumul filter. Version of dimension path filter which is O(delta) rather
827 // than O(length of touched paths). Currently only supports dimensions without
828 // costs (global and local span cost, soft bounds) and with unconstrained
829 // cumul variables except overall capacity and cumul variables of path ends.
830 
831 class ChainCumulFilter : public BasePathFilter {
832  public:
833  ChainCumulFilter(const RoutingModel& routing_model,
834  const RoutingDimension& dimension);
835  ~ChainCumulFilter() override {}
836  std::string DebugString() const override {
837  return "ChainCumulFilter(" + name_ + ")";
838  }
839 
840  private:
841  void OnSynchronizePathFromStart(int64 start) override;
842  bool AcceptPath(int64 path_start, int64 chain_start,
843  int64 chain_end) override;
844 
845  const std::vector<IntVar*> cumuls_;
846  std::vector<int64> start_to_vehicle_;
847  std::vector<int64> start_to_end_;
848  std::vector<const RoutingModel::TransitCallback2*> evaluators_;
849  const std::vector<int64> vehicle_capacities_;
850  std::vector<int64> current_path_cumul_mins_;
851  std::vector<int64> current_max_of_path_end_cumul_mins_;
852  std::vector<int64> old_nexts_;
853  std::vector<int> old_vehicles_;
854  std::vector<int64> current_transits_;
855  const std::string name_;
856 };
857 
858 ChainCumulFilter::ChainCumulFilter(const RoutingModel& routing_model,
859  const RoutingDimension& dimension)
860  : BasePathFilter(routing_model.Nexts(), dimension.cumuls().size()),
861  cumuls_(dimension.cumuls()),
862  evaluators_(routing_model.vehicles(), nullptr),
863  vehicle_capacities_(dimension.vehicle_capacities()),
864  current_path_cumul_mins_(dimension.cumuls().size(), 0),
865  current_max_of_path_end_cumul_mins_(dimension.cumuls().size(), 0),
866  old_nexts_(routing_model.Size(), kUnassigned),
867  old_vehicles_(routing_model.Size(), kUnassigned),
868  current_transits_(routing_model.Size(), 0),
869  name_(dimension.name()) {
870  start_to_vehicle_.resize(Size(), -1);
871  start_to_end_.resize(Size(), -1);
872  for (int i = 0; i < routing_model.vehicles(); ++i) {
873  start_to_vehicle_[routing_model.Start(i)] = i;
874  start_to_end_[routing_model.Start(i)] = routing_model.End(i);
875  evaluators_[i] = &dimension.transit_evaluator(i);
876  }
877 }
878 
879 // On synchronization, maintain "propagated" cumul mins and max level of cumul
880 // from each node to the end of the path; to be used by AcceptPath to
881 // incrementally check feasibility.
882 void ChainCumulFilter::OnSynchronizePathFromStart(int64 start) {
883  const int vehicle = start_to_vehicle_[start];
884  std::vector<int64> path_nodes;
885  int64 node = start;
886  int64 cumul = cumuls_[node]->Min();
887  while (node < Size()) {
888  path_nodes.push_back(node);
889  current_path_cumul_mins_[node] = cumul;
890  const int64 next = Value(node);
891  if (next != old_nexts_[node] || vehicle != old_vehicles_[node]) {
892  old_nexts_[node] = next;
893  old_vehicles_[node] = vehicle;
894  current_transits_[node] = (*evaluators_[vehicle])(node, next);
895  }
896  cumul = CapAdd(cumul, current_transits_[node]);
897  cumul = std::max(cumuls_[next]->Min(), cumul);
898  node = next;
899  }
900  path_nodes.push_back(node);
901  current_path_cumul_mins_[node] = cumul;
902  int64 max_cumuls = cumul;
903  for (int i = path_nodes.size() - 1; i >= 0; --i) {
904  const int64 node = path_nodes[i];
905  max_cumuls = std::max(max_cumuls, current_path_cumul_mins_[node]);
906  current_max_of_path_end_cumul_mins_[node] = max_cumuls;
907  }
908 }
909 
910 // The complexity of the method is O(size of chain (chain_start...chain_end).
911 bool ChainCumulFilter::AcceptPath(int64 path_start, int64 chain_start,
912  int64 chain_end) {
913  const int vehicle = start_to_vehicle_[path_start];
914  const int64 capacity = vehicle_capacities_[vehicle];
915  int64 node = chain_start;
916  int64 cumul = current_path_cumul_mins_[node];
917  while (node != chain_end) {
918  const int64 next = GetNext(node);
919  if (IsVarSynced(node) && next == Value(node) &&
920  vehicle == old_vehicles_[node]) {
921  cumul = CapAdd(cumul, current_transits_[node]);
922  } else {
923  cumul = CapAdd(cumul, (*evaluators_[vehicle])(node, next));
924  }
925  cumul = std::max(cumuls_[next]->Min(), cumul);
926  if (cumul > capacity) return false;
927  node = next;
928  }
929  const int64 end = start_to_end_[path_start];
930  const int64 end_cumul_delta =
931  CapSub(current_path_cumul_mins_[end], current_path_cumul_mins_[node]);
932  const int64 after_chain_cumul_delta =
933  CapSub(current_max_of_path_end_cumul_mins_[node],
934  current_path_cumul_mins_[node]);
935  return CapAdd(cumul, after_chain_cumul_delta) <= capacity &&
936  CapAdd(cumul, end_cumul_delta) <= cumuls_[end]->Max();
937 }
938 
939 // PathCumul filter.
940 
941 class PathCumulFilter : public BasePathFilter {
942  public:
943  PathCumulFilter(const RoutingModel& routing_model,
944  const RoutingDimension& dimension,
945  const RoutingSearchParameters& parameters,
946  bool propagate_own_objective_value,
947  bool filter_objective_cost, bool can_use_lp);
948  ~PathCumulFilter() override {}
949  std::string DebugString() const override {
950  return "PathCumulFilter(" + name_ + ")";
951  }
952  int64 GetSynchronizedObjectiveValue() const override {
953  return propagate_own_objective_value_ ? synchronized_objective_value_ : 0;
954  }
955  int64 GetAcceptedObjectiveValue() const override {
956  return propagate_own_objective_value_ ? accepted_objective_value_ : 0;
957  }
958 
959  private:
960  // This structure stores the "best" path cumul value for a solution, the path
961  // supporting this value, and the corresponding path cumul values for all
962  // paths.
963  struct SupportedPathCumul {
964  SupportedPathCumul() : cumul_value(0), cumul_value_support(0) {}
967  std::vector<int64> path_values;
968  };
969 
970  struct SoftBound {
971  SoftBound() : bound(-1), coefficient(0) {}
974  };
975 
976  // This class caches transit values between nodes of paths. Transit and path
977  // nodes are to be added in the order in which they appear on a path.
978  class PathTransits {
979  public:
980  void Clear() {
981  paths_.clear();
982  transits_.clear();
983  }
984  void ClearPath(int path) {
985  paths_[path].clear();
986  transits_[path].clear();
987  }
988  int AddPaths(int num_paths) {
989  const int first_path = paths_.size();
990  paths_.resize(first_path + num_paths);
991  transits_.resize(first_path + num_paths);
992  return first_path;
993  }
994  void ReserveTransits(int path, int number_of_route_arcs) {
995  transits_[path].reserve(number_of_route_arcs);
996  paths_[path].reserve(number_of_route_arcs + 1);
997  }
998  // Stores the transit between node and next on path. For a given non-empty
999  // path, node must correspond to next in the previous call to PushTransit.
1000  void PushTransit(int path, int node, int next, int64 transit) {
1001  transits_[path].push_back(transit);
1002  if (paths_[path].empty()) {
1003  paths_[path].push_back(node);
1004  }
1005  DCHECK_EQ(paths_[path].back(), node);
1006  paths_[path].push_back(next);
1007  }
1008  int NumPaths() const { return paths_.size(); }
1009  int PathSize(int path) const { return paths_[path].size(); }
1010  int Node(int path, int position) const { return paths_[path][position]; }
1011  int64 Transit(int path, int position) const {
1012  return transits_[path][position];
1013  }
1014 
1015  private:
1016  // paths_[r][i] is the ith node on path r.
1017  std::vector<std::vector<int64>> paths_;
1018  // transits_[r][i] is the transit value between nodes path_[i] and
1019  // path_[i+1] on path r.
1020  std::vector<std::vector<int64>> transits_;
1021  };
1022 
1023  void InitializeAcceptPath() override {
1024  cumul_cost_delta_ = total_current_cumul_cost_value_;
1025  node_with_precedence_to_delta_min_max_cumuls_.clear();
1026  // Cleaning up for the new delta.
1027  delta_max_end_cumul_ = kint64min;
1028  delta_paths_.clear();
1029  delta_path_transits_.Clear();
1030  lns_detected_ = false;
1031  delta_nodes_with_precedences_and_changed_cumul_.ClearAll();
1032  }
1033  bool AcceptPath(int64 path_start, int64 chain_start,
1034  int64 chain_end) override;
1035  bool FinalizeAcceptPath(const Assignment* delta, int64 objective_min,
1036  int64 objective_max) override;
1037  void OnBeforeSynchronizePaths() override;
1038 
1039  bool FilterSpanCost() const { return global_span_cost_coefficient_ != 0; }
1040 
1041  bool FilterSlackCost() const {
1042  return has_nonzero_vehicle_span_cost_coefficients_ ||
1043  has_vehicle_span_upper_bounds_;
1044  }
1045 
1046  bool FilterBreakCost(int vehicle) const {
1047  return dimension_.HasBreakConstraints() &&
1048  !dimension_.GetBreakIntervalsOfVehicle(vehicle).empty();
1049  }
1050 
1051  bool FilterCumulSoftBounds() const { return !cumul_soft_bounds_.empty(); }
1052 
1053  int64 GetCumulSoftCost(int64 node, int64 cumul_value) const;
1054 
1055  bool FilterCumulPiecewiseLinearCosts() const {
1056  return !cumul_piecewise_linear_costs_.empty();
1057  }
1058 
1059  bool FilterWithDimensionCumulOptimizerForVehicle(int vehicle) const {
1060  if (!can_use_lp_ || FilterCumulPiecewiseLinearCosts()) {
1061  return false;
1062  }
1063 
1064  int num_linear_constraints = 0;
1065  if (dimension_.GetSpanCostCoefficientForVehicle(vehicle) > 0)
1066  ++num_linear_constraints;
1067  if (FilterSoftSpanCost(vehicle)) ++num_linear_constraints;
1068  if (FilterCumulSoftLowerBounds()) ++num_linear_constraints;
1069  if (FilterCumulSoftBounds()) ++num_linear_constraints;
1070  if (vehicle_span_upper_bounds_[vehicle] < kint64max)
1071  ++num_linear_constraints;
1072  const bool has_breaks = FilterBreakCost(vehicle);
1073  if (has_breaks) ++num_linear_constraints;
1074 
1075  // The DimensionCumulOptimizer is used to compute a more precise value of
1076  // the cost related to the cumul values (soft bounds and span costs).
1077  // It is also used to garantee feasibility with complex mixes of constraints
1078  // and in particular in the presence of break requests along other
1079  // constraints.
1080  // Therefore, without breaks, we only use the optimizer when the costs are
1081  // actually used to filter the solutions, i.e. when filter_objective_cost_
1082  // is true.
1083  return num_linear_constraints >= 2 &&
1084  (has_breaks || filter_objective_cost_);
1085  }
1086 
1087  bool FilterDimensionForbiddenIntervals() const {
1088  for (const SortedDisjointIntervalList& intervals :
1089  dimension_.forbidden_intervals()) {
1090  // TODO(user): Change the following test to check intervals within
1091  // the domain of the corresponding variables.
1092  if (intervals.NumIntervals() > 0) {
1093  return true;
1094  }
1095  }
1096  return false;
1097  }
1098 
1099  int64 GetCumulPiecewiseLinearCost(int64 node, int64 cumul_value) const;
1100 
1101  bool FilterCumulSoftLowerBounds() const {
1102  return !cumul_soft_lower_bounds_.empty();
1103  }
1104 
1105  bool FilterPrecedences() const { return !node_index_to_precedences_.empty(); }
1106 
1107  bool FilterSoftSpanCost() const {
1108  return dimension_.HasSoftSpanUpperBounds();
1109  }
1110  bool FilterSoftSpanCost(int vehicle) const {
1111  return dimension_.HasSoftSpanUpperBounds() &&
1112  dimension_.GetSoftSpanUpperBoundForVehicle(vehicle).cost > 0;
1113  }
1114  bool FilterSoftSpanQuadraticCost() const {
1115  return dimension_.HasQuadraticCostSoftSpanUpperBounds();
1116  }
1117  bool FilterSoftSpanQuadraticCost(int vehicle) const {
1118  return dimension_.HasQuadraticCostSoftSpanUpperBounds() &&
1119  dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle)
1120  .cost > 0;
1121  }
1122 
1123  int64 GetCumulSoftLowerBoundCost(int64 node, int64 cumul_value) const;
1124 
1125  int64 GetPathCumulSoftLowerBoundCost(const PathTransits& path_transits,
1126  int path) const;
1127 
1128  void InitializeSupportedPathCumul(SupportedPathCumul* supported_cumul,
1129  int64 default_value);
1130 
1131  // Given the vector of minimum cumuls on the path, determines if the pickup to
1132  // delivery limits for this dimension (if there are any) can be respected by
1133  // this path.
1134  // Returns true if for every pickup/delivery nodes visited on this path,
1135  // min_cumul_value(delivery) - max_cumul_value(pickup) is less than the limit
1136  // set for this pickup to delivery.
1137  // TODO(user): Verify if we should filter the pickup/delivery limits using
1138  // the LP, for a perfect filtering.
1139  bool PickupToDeliveryLimitsRespected(
1140  const PathTransits& path_transits, int path,
1141  const std::vector<int64>& min_path_cumuls) const;
1142 
1143  // Computes the maximum cumul value of nodes along the path using
1144  // [current|delta]_path_transits_, and stores the min/max cumul
1145  // related to each node in the corresponding vector
1146  // [current|delta]_[min|max]_node_cumuls_.
1147  // The boolean is_delta indicates if the computations should take place on the
1148  // "delta" or "current" members. When true, the nodes for which the min/max
1149  // cumul has changed from the current value are marked in
1150  // delta_nodes_with_precedences_and_changed_cumul_.
1151  void StoreMinMaxCumulOfNodesOnPath(int path,
1152  const std::vector<int64>& min_path_cumuls,
1153  bool is_delta);
1154 
1155  // Compute the max start cumul value for a given path and a given minimal end
1156  // cumul value.
1157  // NOTE: Since this function is used to compute a lower bound on the span of
1158  // the routes, we don't "jump" over the forbidden intervals with this min end
1159  // cumul value. We do however concurrently compute the max possible start
1160  // given the max end cumul, for which we can "jump" over forbidden intervals,
1161  // and return the minimum of the two.
1162  int64 ComputePathMaxStartFromEndCumul(const PathTransits& path_transits,
1163  int path, int64 path_start,
1164  int64 min_end_cumul) const;
1165 
1166  const RoutingModel& routing_model_;
1167  const RoutingDimension& dimension_;
1168  const std::vector<IntVar*> cumuls_;
1169  const std::vector<IntVar*> slacks_;
1170  std::vector<int64> start_to_vehicle_;
1171  std::vector<const RoutingModel::TransitCallback2*> evaluators_;
1172  std::vector<int64> vehicle_span_upper_bounds_;
1173  bool has_vehicle_span_upper_bounds_;
1174  int64 total_current_cumul_cost_value_;
1175  int64 synchronized_objective_value_;
1176  int64 accepted_objective_value_;
1177  // Map between paths and path soft cumul bound costs. The paths are indexed
1178  // by the index of the start node of the path.
1179  absl::flat_hash_map<int64, int64> current_cumul_cost_values_;
1180  int64 cumul_cost_delta_;
1181  // Cumul cost values for paths in delta, indexed by vehicle.
1182  std::vector<int64> delta_path_cumul_cost_values_;
1183  const int64 global_span_cost_coefficient_;
1184  std::vector<SoftBound> cumul_soft_bounds_;
1185  std::vector<SoftBound> cumul_soft_lower_bounds_;
1186  std::vector<const PiecewiseLinearFunction*> cumul_piecewise_linear_costs_;
1187  std::vector<int64> vehicle_span_cost_coefficients_;
1188  bool has_nonzero_vehicle_span_cost_coefficients_;
1189  const std::vector<int64> vehicle_capacities_;
1190  // node_index_to_precedences_[node_index] contains all NodePrecedence elements
1191  // with node_index as either "first_node" or "second_node".
1192  // This vector is empty if there are no precedences on the dimension_.
1193  std::vector<std::vector<RoutingDimension::NodePrecedence>>
1194  node_index_to_precedences_;
1195  // Data reflecting information on paths and cumul variables for the solution
1196  // to which the filter was synchronized.
1197  SupportedPathCumul current_min_start_;
1198  SupportedPathCumul current_max_end_;
1199  PathTransits current_path_transits_;
1200  // Current min/max cumul values, indexed by node.
1201  std::vector<std::pair<int64, int64>> current_min_max_node_cumuls_;
1202  // Data reflecting information on paths and cumul variables for the "delta"
1203  // solution (aka neighbor solution) being examined.
1204  PathTransits delta_path_transits_;
1205  int64 delta_max_end_cumul_;
1206  SparseBitset<int64> delta_nodes_with_precedences_and_changed_cumul_;
1207  absl::flat_hash_map<int64, std::pair<int64, int64>>
1208  node_with_precedence_to_delta_min_max_cumuls_;
1209  // Note: small_ordered_set only support non-hash sets.
1211  const std::string name_;
1212 
1213  LocalDimensionCumulOptimizer* optimizer_;
1214  std::unique_ptr<LocalDimensionCumulOptimizer> internal_optimizer_;
1215  LocalDimensionCumulOptimizer* mp_optimizer_;
1216  std::unique_ptr<LocalDimensionCumulOptimizer> internal_mp_optimizer_;
1217  const bool filter_objective_cost_;
1218  // This boolean indicates if the LP optimizer can be used if necessary to
1219  // optimize the dimension cumuls, and is only used for testing purposes.
1220  const bool can_use_lp_;
1221  const bool propagate_own_objective_value_;
1222 
1223  // Used to do span lower bounding in presence of vehicle breaks.
1224  DisjunctivePropagator disjunctive_propagator_;
1225  DisjunctivePropagator::Tasks tasks_;
1226  TravelBounds travel_bounds_;
1227  std::vector<int64> current_path_;
1228 
1229  bool lns_detected_;
1230 };
1231 
1232 PathCumulFilter::PathCumulFilter(const RoutingModel& routing_model,
1233  const RoutingDimension& dimension,
1234  const RoutingSearchParameters& parameters,
1235  bool propagate_own_objective_value,
1236  bool filter_objective_cost, bool can_use_lp)
1237  : BasePathFilter(routing_model.Nexts(), dimension.cumuls().size()),
1238  routing_model_(routing_model),
1239  dimension_(dimension),
1240  cumuls_(dimension.cumuls()),
1241  slacks_(dimension.slacks()),
1242  evaluators_(routing_model.vehicles(), nullptr),
1243  vehicle_span_upper_bounds_(dimension.vehicle_span_upper_bounds()),
1244  has_vehicle_span_upper_bounds_(false),
1245  total_current_cumul_cost_value_(0),
1246  synchronized_objective_value_(0),
1247  accepted_objective_value_(0),
1248  current_cumul_cost_values_(),
1249  cumul_cost_delta_(0),
1250  delta_path_cumul_cost_values_(routing_model.vehicles(), kint64min),
1251  global_span_cost_coefficient_(dimension.global_span_cost_coefficient()),
1252  vehicle_span_cost_coefficients_(
1253  dimension.vehicle_span_cost_coefficients()),
1254  has_nonzero_vehicle_span_cost_coefficients_(false),
1255  vehicle_capacities_(dimension.vehicle_capacities()),
1256  delta_max_end_cumul_(0),
1257  delta_nodes_with_precedences_and_changed_cumul_(routing_model.Size()),
1258  name_(dimension.name()),
1259  optimizer_(routing_model.GetMutableLocalCumulOptimizer(dimension)),
1260  mp_optimizer_(routing_model.GetMutableLocalCumulMPOptimizer(dimension)),
1261  filter_objective_cost_(filter_objective_cost),
1262  can_use_lp_(can_use_lp),
1263  propagate_own_objective_value_(propagate_own_objective_value),
1264  lns_detected_(false) {
1265  for (const int64 upper_bound : vehicle_span_upper_bounds_) {
1266  if (upper_bound != kint64max) {
1267  has_vehicle_span_upper_bounds_ = true;
1268  break;
1269  }
1270  }
1271  for (const int64 coefficient : vehicle_span_cost_coefficients_) {
1272  if (coefficient != 0) {
1273  has_nonzero_vehicle_span_cost_coefficients_ = true;
1274  break;
1275  }
1276  }
1277  cumul_soft_bounds_.resize(cumuls_.size());
1278  cumul_soft_lower_bounds_.resize(cumuls_.size());
1279  cumul_piecewise_linear_costs_.resize(cumuls_.size());
1280  bool has_cumul_soft_bounds = false;
1281  bool has_cumul_soft_lower_bounds = false;
1282  bool has_cumul_piecewise_linear_costs = false;
1283  bool has_cumul_hard_bounds = false;
1284  for (const IntVar* const slack : slacks_) {
1285  if (slack->Min() > 0) {
1286  has_cumul_hard_bounds = true;
1287  break;
1288  }
1289  }
1290  for (int i = 0; i < cumuls_.size(); ++i) {
1291  if (dimension.HasCumulVarSoftUpperBound(i)) {
1292  has_cumul_soft_bounds = true;
1293  cumul_soft_bounds_[i].bound = dimension.GetCumulVarSoftUpperBound(i);
1294  cumul_soft_bounds_[i].coefficient =
1295  dimension.GetCumulVarSoftUpperBoundCoefficient(i);
1296  }
1297  if (dimension.HasCumulVarSoftLowerBound(i)) {
1298  has_cumul_soft_lower_bounds = true;
1299  cumul_soft_lower_bounds_[i].bound =
1300  dimension.GetCumulVarSoftLowerBound(i);
1301  cumul_soft_lower_bounds_[i].coefficient =
1302  dimension.GetCumulVarSoftLowerBoundCoefficient(i);
1303  }
1304  if (dimension.HasCumulVarPiecewiseLinearCost(i)) {
1305  has_cumul_piecewise_linear_costs = true;
1306  cumul_piecewise_linear_costs_[i] =
1307  dimension.GetCumulVarPiecewiseLinearCost(i);
1308  }
1309  IntVar* const cumul_var = cumuls_[i];
1310  if (cumul_var->Min() > 0 || cumul_var->Max() < kint64max) {
1311  has_cumul_hard_bounds = true;
1312  }
1313  }
1314  if (!has_cumul_soft_bounds) {
1315  cumul_soft_bounds_.clear();
1316  }
1317  if (!has_cumul_soft_lower_bounds) {
1318  cumul_soft_lower_bounds_.clear();
1319  }
1320  if (!has_cumul_piecewise_linear_costs) {
1321  cumul_piecewise_linear_costs_.clear();
1322  }
1323  if (!has_cumul_hard_bounds) {
1324  // Slacks don't need to be constrained if the cumuls don't have hard bounds;
1325  // therefore we can ignore the vehicle span cost coefficient (note that the
1326  // transit part is already handled by the arc cost filters).
1327  // This doesn't concern the global span filter though.
1328  vehicle_span_cost_coefficients_.assign(routing_model.vehicles(), 0);
1329  has_nonzero_vehicle_span_cost_coefficients_ = false;
1330  }
1331  start_to_vehicle_.resize(Size(), -1);
1332  for (int i = 0; i < routing_model.vehicles(); ++i) {
1333  start_to_vehicle_[routing_model.Start(i)] = i;
1334  evaluators_[i] = &dimension.transit_evaluator(i);
1335  }
1336 
1337  const std::vector<RoutingDimension::NodePrecedence>& node_precedences =
1338  dimension.GetNodePrecedences();
1339  if (!node_precedences.empty()) {
1340  current_min_max_node_cumuls_.resize(cumuls_.size(), {-1, -1});
1341  node_index_to_precedences_.resize(cumuls_.size());
1342  for (const auto& node_precedence : node_precedences) {
1343  node_index_to_precedences_[node_precedence.first_node].push_back(
1344  node_precedence);
1345  node_index_to_precedences_[node_precedence.second_node].push_back(
1346  node_precedence);
1347  }
1348  }
1349  // NOTE(user): The model's local optimizer for this dimension could be
1350  // null because the finalizer is using a global optimizer, so we create a
1351  // separate optimizer for the PathCumulFilter if we need it.
1352  if (can_use_lp_ && optimizer_ == nullptr) {
1353  //DCHECK_EQ(mp_optimizer_, nullptr);
1354  for (int vehicle = 0; vehicle < routing_model.vehicles(); vehicle++) {
1355  if (!FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
1356  continue;
1357  }
1358  if (optimizer_ == nullptr) {
1359  // NOTE: The optimizer_ might have already been set in the for loop,
1360  // since we continue scanning vehicles in case one of them needs the
1361  // the mp_optimizer_ for break constraints.
1362  internal_optimizer_ = absl::make_unique<LocalDimensionCumulOptimizer>(
1363  &dimension, parameters.continuous_scheduling_solver());
1364  optimizer_ = internal_optimizer_.get();
1365  }
1366  if (FilterBreakCost(vehicle) || FilterDimensionForbiddenIntervals()) {
1367  internal_mp_optimizer_ =
1368  absl::make_unique<LocalDimensionCumulOptimizer>(
1369  &dimension, parameters.mixed_integer_scheduling_solver());
1370  mp_optimizer_ = internal_mp_optimizer_.get();
1371  break;
1372  }
1373  }
1374  }
1375 }
1376 
1377 int64 PathCumulFilter::GetCumulSoftCost(int64 node, int64 cumul_value) const {
1378  if (node < cumul_soft_bounds_.size()) {
1379  const int64 bound = cumul_soft_bounds_[node].bound;
1380  const int64 coefficient = cumul_soft_bounds_[node].coefficient;
1381  if (coefficient > 0 && bound < cumul_value) {
1383  }
1384  }
1385  return 0;
1386 }
1387 
1388 int64 PathCumulFilter::GetCumulPiecewiseLinearCost(int64 node,
1389  int64 cumul_value) const {
1390  if (node < cumul_piecewise_linear_costs_.size()) {
1391  const PiecewiseLinearFunction* cost = cumul_piecewise_linear_costs_[node];
1392  if (cost != nullptr) {
1393  return cost->Value(cumul_value);
1394  }
1395  }
1396  return 0;
1397 }
1398 
1399 int64 PathCumulFilter::GetCumulSoftLowerBoundCost(int64 node,
1400  int64 cumul_value) const {
1401  if (node < cumul_soft_lower_bounds_.size()) {
1402  const int64 bound = cumul_soft_lower_bounds_[node].bound;
1403  const int64 coefficient = cumul_soft_lower_bounds_[node].coefficient;
1404  if (coefficient > 0 && bound > cumul_value) {
1406  }
1407  }
1408  return 0;
1409 }
1410 
1411 int64 PathCumulFilter::GetPathCumulSoftLowerBoundCost(
1412  const PathTransits& path_transits, int path) const {
1413  int64 node = path_transits.Node(path, path_transits.PathSize(path) - 1);
1414  int64 cumul = cumuls_[node]->Max();
1415  int64 current_cumul_cost_value = GetCumulSoftLowerBoundCost(node, cumul);
1416  for (int i = path_transits.PathSize(path) - 2; i >= 0; --i) {
1417  node = path_transits.Node(path, i);
1418  cumul = CapSub(cumul, path_transits.Transit(path, i));
1419  cumul = std::min(cumuls_[node]->Max(), cumul);
1420  current_cumul_cost_value = CapAdd(current_cumul_cost_value,
1421  GetCumulSoftLowerBoundCost(node, cumul));
1422  }
1423  return current_cumul_cost_value;
1424 }
1425 
1426 void PathCumulFilter::OnBeforeSynchronizePaths() {
1427  total_current_cumul_cost_value_ = 0;
1428  cumul_cost_delta_ = 0;
1429  current_cumul_cost_values_.clear();
1430  if (NumPaths() > 0 &&
1431  (FilterSpanCost() || FilterCumulSoftBounds() || FilterSlackCost() ||
1432  FilterCumulSoftLowerBounds() || FilterCumulPiecewiseLinearCosts() ||
1433  FilterPrecedences() || FilterSoftSpanCost() ||
1434  FilterSoftSpanQuadraticCost())) {
1435  InitializeSupportedPathCumul(&current_min_start_, kint64max);
1436  InitializeSupportedPathCumul(&current_max_end_, kint64min);
1437  current_path_transits_.Clear();
1438  current_path_transits_.AddPaths(NumPaths());
1439  // For each path, compute the minimum end cumul and store the max of these.
1440  for (int r = 0; r < NumPaths(); ++r) {
1441  int64 node = Start(r);
1442  const int vehicle = start_to_vehicle_[Start(r)];
1443  // First pass: evaluating route length to reserve memory to store route
1444  // information.
1445  int number_of_route_arcs = 0;
1446  while (node < Size()) {
1447  ++number_of_route_arcs;
1448  node = Value(node);
1449  }
1450  current_path_transits_.ReserveTransits(r, number_of_route_arcs);
1451  // Second pass: update cumul, transit and cost values.
1452  node = Start(r);
1453  int64 cumul = cumuls_[node]->Min();
1454  std::vector<int64> min_path_cumuls;
1455  min_path_cumuls.reserve(number_of_route_arcs + 1);
1456  min_path_cumuls.push_back(cumul);
1457 
1458  int64 current_cumul_cost_value = GetCumulSoftCost(node, cumul);
1459  current_cumul_cost_value = CapAdd(
1460  current_cumul_cost_value, GetCumulPiecewiseLinearCost(node, cumul));
1461 
1462  int64 total_transit = 0;
1463  while (node < Size()) {
1464  const int64 next = Value(node);
1465  const int64 transit = (*evaluators_[vehicle])(node, next);
1466  total_transit = CapAdd(total_transit, transit);
1467  const int64 transit_slack = CapAdd(transit, slacks_[node]->Min());
1468  current_path_transits_.PushTransit(r, node, next, transit_slack);
1469  cumul = CapAdd(cumul, transit_slack);
1470  cumul =
1471  dimension_.GetFirstPossibleGreaterOrEqualValueForNode(next, cumul);
1472  cumul = std::max(cumuls_[next]->Min(), cumul);
1473  min_path_cumuls.push_back(cumul);
1474  node = next;
1475  current_cumul_cost_value =
1476  CapAdd(current_cumul_cost_value, GetCumulSoftCost(node, cumul));
1477  current_cumul_cost_value = CapAdd(
1478  current_cumul_cost_value, GetCumulPiecewiseLinearCost(node, cumul));
1479  }
1480  if (FilterPrecedences()) {
1481  StoreMinMaxCumulOfNodesOnPath(/*path=*/r, min_path_cumuls,
1482  /*is_delta=*/false);
1483  }
1484  if (number_of_route_arcs == 1 &&
1485  !routing_model_.AreEmptyRouteCostsConsideredForVehicle(vehicle)) {
1486  // This is an empty route (single start->end arc) which we don't take
1487  // into account for costs.
1488  current_cumul_cost_values_[Start(r)] = 0;
1489  current_path_transits_.ClearPath(r);
1490  continue;
1491  }
1492  if (FilterSlackCost() || FilterSoftSpanCost() ||
1493  FilterSoftSpanQuadraticCost()) {
1494  const int64 start = ComputePathMaxStartFromEndCumul(
1495  current_path_transits_, r, Start(r), cumul);
1496  const int64 span_lower_bound = CapSub(cumul, start);
1497  if (FilterSlackCost()) {
1498  current_cumul_cost_value =
1499  CapAdd(current_cumul_cost_value,
1500  CapProd(vehicle_span_cost_coefficients_[vehicle],
1501  CapSub(span_lower_bound, total_transit)));
1502  }
1503  if (FilterSoftSpanCost()) {
1504  const SimpleBoundCosts::BoundCost bound_cost =
1505  dimension_.GetSoftSpanUpperBoundForVehicle(vehicle);
1506  if (bound_cost.bound < span_lower_bound) {
1507  const int64 violation = CapSub(span_lower_bound, bound_cost.bound);
1508  current_cumul_cost_value = CapAdd(
1509  current_cumul_cost_value, CapProd(bound_cost.cost, violation));
1510  }
1511  }
1512  if (FilterSoftSpanQuadraticCost()) {
1513  const SimpleBoundCosts::BoundCost bound_cost =
1514  dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
1515  if (bound_cost.bound < span_lower_bound) {
1516  const int64 violation = CapSub(span_lower_bound, bound_cost.bound);
1517  current_cumul_cost_value =
1518  CapAdd(current_cumul_cost_value,
1519  CapProd(bound_cost.cost, CapProd(violation, violation)));
1520  }
1521  }
1522  }
1523  if (FilterCumulSoftLowerBounds()) {
1524  current_cumul_cost_value =
1525  CapAdd(current_cumul_cost_value,
1526  GetPathCumulSoftLowerBoundCost(current_path_transits_, r));
1527  }
1528  if (FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
1529  // TODO(user): Return a status from the optimizer to detect failures
1530  // The only admissible failures here are because of LP timeout.
1531  int64 lp_cumul_cost_value = 0;
1532  LocalDimensionCumulOptimizer* const optimizer =
1533  FilterBreakCost(vehicle) ? mp_optimizer_ : optimizer_;
1534  //DCHECK_NE(optimizer, nullptr);
1535  const DimensionSchedulingStatus status =
1536  optimizer->ComputeRouteCumulCostWithoutFixedTransits(
1537  vehicle, [this](int64 node) { return Value(node); },
1538  &lp_cumul_cost_value);
1539  switch (status) {
1541  lp_cumul_cost_value = 0;
1542  break;
1543  case DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY:
1544  //DCHECK_NE(mp_optimizer_, nullptr);
1545  if (mp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
1546  vehicle, [this](int64 node) { return Value(node); },
1547  &lp_cumul_cost_value) ==
1549  lp_cumul_cost_value = 0;
1550  }
1551  break;
1552  default:
1553  DCHECK(status == DimensionSchedulingStatus::OPTIMAL);
1554  }
1555  current_cumul_cost_value =
1556  std::max(current_cumul_cost_value, lp_cumul_cost_value);
1557  }
1558  current_cumul_cost_values_[Start(r)] = current_cumul_cost_value;
1559  current_max_end_.path_values[r] = cumul;
1560  if (current_max_end_.cumul_value < cumul) {
1561  current_max_end_.cumul_value = cumul;
1562  current_max_end_.cumul_value_support = r;
1563  }
1564  total_current_cumul_cost_value_ =
1565  CapAdd(total_current_cumul_cost_value_, current_cumul_cost_value);
1566  }
1567  if (FilterPrecedences()) {
1568  // Update the min/max node cumuls of new unperformed nodes.
1569  for (int64 node : GetNewSynchronizedUnperformedNodes()) {
1570  current_min_max_node_cumuls_[node] = {-1, -1};
1571  }
1572  }
1573  // Use the max of the path end cumul mins to compute the corresponding
1574  // maximum start cumul of each path; store the minimum of these.
1575  for (int r = 0; r < NumPaths(); ++r) {
1576  const int64 start = ComputePathMaxStartFromEndCumul(
1577  current_path_transits_, r, Start(r), current_max_end_.cumul_value);
1578  current_min_start_.path_values[r] = start;
1579  if (current_min_start_.cumul_value > start) {
1580  current_min_start_.cumul_value = start;
1581  current_min_start_.cumul_value_support = r;
1582  }
1583  }
1584  }
1585  // Initialize this before considering any deltas (neighbor).
1586  delta_max_end_cumul_ = kint64min;
1587  lns_detected_ = false;
1588 
1589  DCHECK_GE(current_max_end_.cumul_value, current_min_start_.cumul_value);
1590  synchronized_objective_value_ =
1591  CapAdd(total_current_cumul_cost_value_,
1592  CapProd(global_span_cost_coefficient_,
1593  CapSub(current_max_end_.cumul_value,
1594  current_min_start_.cumul_value)));
1595 }
1596 
1597 bool PathCumulFilter::AcceptPath(int64 path_start, int64 chain_start,
1598  int64 chain_end) {
1599  int64 node = path_start;
1600  int64 cumul = cumuls_[node]->Min();
1601  int64 cumul_cost_delta = 0;
1602  int64 total_transit = 0;
1603  const int path = delta_path_transits_.AddPaths(1);
1604  const int vehicle = start_to_vehicle_[path_start];
1605  const int64 capacity = vehicle_capacities_[vehicle];
1606  const bool filter_vehicle_costs =
1607  !routing_model_.IsEnd(GetNext(node)) ||
1608  routing_model_.AreEmptyRouteCostsConsideredForVehicle(vehicle);
1609  if (filter_vehicle_costs) {
1610  cumul_cost_delta = CapAdd(GetCumulSoftCost(node, cumul),
1611  GetCumulPiecewiseLinearCost(node, cumul));
1612  }
1613  // Evaluating route length to reserve memory to store transit information.
1614  int number_of_route_arcs = 0;
1615  while (node < Size()) {
1616  const int64 next = GetNext(node);
1617  // TODO(user): This shouldn't be needed anymore as such deltas should
1618  // have been filtered already.
1619  if (next == kUnassigned) {
1620  // LNS detected, return true since other paths were ok up to now.
1621  lns_detected_ = true;
1622  return true;
1623  }
1624  ++number_of_route_arcs;
1625  node = next;
1626  }
1627  delta_path_transits_.ReserveTransits(path, number_of_route_arcs);
1628  std::vector<int64> min_path_cumuls;
1629  min_path_cumuls.reserve(number_of_route_arcs + 1);
1630  min_path_cumuls.push_back(cumul);
1631  // Check that the path is feasible with regards to cumul bounds, scanning
1632  // the paths from start to end (caching path node sequences and transits
1633  // for further span cost filtering).
1634  node = path_start;
1635  while (node < Size()) {
1636  const int64 next = GetNext(node);
1637  const int64 transit = (*evaluators_[vehicle])(node, next);
1638  total_transit = CapAdd(total_transit, transit);
1639  const int64 transit_slack = CapAdd(transit, slacks_[node]->Min());
1640  delta_path_transits_.PushTransit(path, node, next, transit_slack);
1641  cumul = CapAdd(cumul, transit_slack);
1642  cumul = dimension_.GetFirstPossibleGreaterOrEqualValueForNode(next, cumul);
1643  if (cumul > std::min(capacity, cumuls_[next]->Max())) {
1644  return false;
1645  }
1646  cumul = std::max(cumuls_[next]->Min(), cumul);
1647  min_path_cumuls.push_back(cumul);
1648  node = next;
1649  if (filter_vehicle_costs) {
1650  cumul_cost_delta =
1651  CapAdd(cumul_cost_delta, GetCumulSoftCost(node, cumul));
1652  cumul_cost_delta =
1653  CapAdd(cumul_cost_delta, GetCumulPiecewiseLinearCost(node, cumul));
1654  }
1655  }
1656  const int64 min_end = cumul;
1657 
1658  if (!PickupToDeliveryLimitsRespected(delta_path_transits_, path,
1659  min_path_cumuls)) {
1660  return false;
1661  }
1662  if (FilterSlackCost() || FilterBreakCost(vehicle) ||
1663  FilterSoftSpanCost(vehicle) || FilterSoftSpanQuadraticCost(vehicle)) {
1664  const int64 max_start_from_min_end = ComputePathMaxStartFromEndCumul(
1665  delta_path_transits_, path, path_start, min_end);
1666  int64 min_total_slack =
1667  CapSub(CapSub(min_end, max_start_from_min_end), total_transit);
1668  if (FilterBreakCost(vehicle)) {
1669  // Copy path of vehicle.
1670  {
1671  current_path_.clear();
1672  int64 node = path_start;
1673  while (node < Size()) {
1674  current_path_.push_back(node);
1675  node = GetNext(node);
1676  }
1677  current_path_.push_back(node);
1678  }
1679  FillTravelBoundsOfVehicle(vehicle, current_path_, dimension_,
1680  &travel_bounds_);
1681  tasks_.Clear();
1682  AppendTasksFromPath(current_path_, travel_bounds_, dimension_, &tasks_);
1683  tasks_.num_chain_tasks = tasks_.start_min.size();
1684  AppendTasksFromIntervals(dimension_.GetBreakIntervalsOfVehicle(vehicle),
1685  &tasks_);
1686  tasks_.distance_duration =
1687  dimension_.GetBreakDistanceDurationOfVehicle(vehicle);
1688  if (!disjunctive_propagator_.Precedences(&tasks_) ||
1689  !disjunctive_propagator_.ChainSpanMin(&tasks_)) {
1690  return false;
1691  }
1692  min_total_slack =
1693  std::max(min_total_slack, CapSub(tasks_.span_min, total_transit));
1694  }
1695  if (filter_vehicle_costs) {
1696  cumul_cost_delta = CapAdd(
1697  cumul_cost_delta,
1698  CapProd(vehicle_span_cost_coefficients_[vehicle], min_total_slack));
1699  const int64 span_lower_bound = CapAdd(total_transit, min_total_slack);
1700  if (FilterSoftSpanCost()) {
1701  const SimpleBoundCosts::BoundCost bound_cost =
1702  dimension_.GetSoftSpanUpperBoundForVehicle(vehicle);
1703  if (bound_cost.bound < span_lower_bound) {
1704  const int64 violation = CapSub(span_lower_bound, bound_cost.bound);
1705  cumul_cost_delta =
1706  CapAdd(cumul_cost_delta, CapProd(bound_cost.cost, violation));
1707  }
1708  }
1709  if (FilterSoftSpanQuadraticCost()) {
1710  const SimpleBoundCosts::BoundCost bound_cost =
1711  dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
1712  if (bound_cost.bound < span_lower_bound) {
1713  const int64 violation = CapSub(span_lower_bound, bound_cost.bound);
1714  cumul_cost_delta =
1715  CapAdd(cumul_cost_delta,
1716  CapProd(bound_cost.cost, CapProd(violation, violation)));
1717  }
1718  }
1719  }
1720  if (CapAdd(total_transit, min_total_slack) >
1721  vehicle_span_upper_bounds_[vehicle]) {
1722  return false;
1723  }
1724  }
1725  if (FilterCumulSoftLowerBounds() && filter_vehicle_costs) {
1726  cumul_cost_delta =
1727  CapAdd(cumul_cost_delta,
1728  GetPathCumulSoftLowerBoundCost(delta_path_transits_, path));
1729  }
1730  if (FilterPrecedences()) {
1731  StoreMinMaxCumulOfNodesOnPath(path, min_path_cumuls, /*is_delta=*/true);
1732  }
1733  if (!filter_vehicle_costs) {
1734  // If this route's costs should't be taken into account, reset the
1735  // cumul_cost_delta and delta_path_transits_ for this path.
1736  cumul_cost_delta = 0;
1737  delta_path_transits_.ClearPath(path);
1738  }
1739  if (FilterSpanCost() || FilterCumulSoftBounds() || FilterSlackCost() ||
1740  FilterCumulSoftLowerBounds() || FilterCumulPiecewiseLinearCosts() ||
1741  FilterSoftSpanCost(vehicle) || FilterSoftSpanQuadraticCost(vehicle)) {
1742  delta_paths_.insert(GetPath(path_start));
1743  delta_path_cumul_cost_values_[vehicle] = cumul_cost_delta;
1744  cumul_cost_delta =
1745  CapSub(cumul_cost_delta, current_cumul_cost_values_[path_start]);
1746  if (filter_vehicle_costs) {
1747  delta_max_end_cumul_ = std::max(delta_max_end_cumul_, min_end);
1748  }
1749  }
1750  cumul_cost_delta_ = CapAdd(cumul_cost_delta_, cumul_cost_delta);
1751  return true;
1752 }
1753 
1754 bool PathCumulFilter::FinalizeAcceptPath(const Assignment* delta,
1755  int64 objective_min,
1756  int64 objective_max) {
1757  if ((!FilterSpanCost() && !FilterCumulSoftBounds() && !FilterSlackCost() &&
1758  !FilterCumulSoftLowerBounds() && !FilterCumulPiecewiseLinearCosts() &&
1759  !FilterPrecedences() && !FilterSoftSpanCost() &&
1760  !FilterSoftSpanQuadraticCost()) ||
1761  lns_detected_) {
1762  return true;
1763  }
1764  if (FilterPrecedences()) {
1765  for (int64 node : delta_nodes_with_precedences_and_changed_cumul_
1766  .PositionsSetAtLeastOnce()) {
1767  const std::pair<int64, int64> node_min_max_cumul_in_delta =
1768  gtl::FindWithDefault(node_with_precedence_to_delta_min_max_cumuls_,
1769  node, {-1, -1});
1770  // NOTE: This node was seen in delta, so its delta min/max cumul should be
1771  // stored in the map.
1772  DCHECK(node_min_max_cumul_in_delta.first >= 0 &&
1773  node_min_max_cumul_in_delta.second >= 0);
1774  for (const RoutingDimension::NodePrecedence& precedence :
1775  node_index_to_precedences_[node]) {
1776  const bool node_is_first = (precedence.first_node == node);
1777  const int64 other_node =
1778  node_is_first ? precedence.second_node : precedence.first_node;
1779  if (GetNext(other_node) == kUnassigned ||
1780  GetNext(other_node) == other_node) {
1781  // The other node is unperformed, so the precedence constraint is
1782  // inactive.
1783  continue;
1784  }
1785  // max_cumul[second_node] should be greater or equal than
1786  // min_cumul[first_node] + offset.
1787  const std::pair<int64, int64>& other_min_max_cumul_in_delta =
1788  gtl::FindWithDefault(node_with_precedence_to_delta_min_max_cumuls_,
1789  other_node,
1790  current_min_max_node_cumuls_[other_node]);
1791 
1792  const int64 first_min_cumul = node_is_first
1793  ? node_min_max_cumul_in_delta.first
1794  : other_min_max_cumul_in_delta.first;
1795  const int64 second_max_cumul = node_is_first
1796  ? other_min_max_cumul_in_delta.second
1797  : node_min_max_cumul_in_delta.second;
1798 
1799  if (second_max_cumul < first_min_cumul + precedence.offset) {
1800  return false;
1801  }
1802  }
1803  }
1804  }
1805  int64 new_max_end = delta_max_end_cumul_;
1806  int64 new_min_start = kint64max;
1807  if (FilterSpanCost()) {
1808  if (new_max_end < current_max_end_.cumul_value) {
1809  // Delta max end is lower than the current solution one.
1810  // If the path supporting the current max end has been modified, we need
1811  // to check all paths to find the largest max end.
1812  if (!gtl::ContainsKey(delta_paths_,
1813  current_max_end_.cumul_value_support)) {
1814  new_max_end = current_max_end_.cumul_value;
1815  } else {
1816  for (int i = 0; i < current_max_end_.path_values.size(); ++i) {
1817  if (current_max_end_.path_values[i] > new_max_end &&
1818  !gtl::ContainsKey(delta_paths_, i)) {
1819  new_max_end = current_max_end_.path_values[i];
1820  }
1821  }
1822  }
1823  }
1824  // Now that the max end cumul has been found, compute the corresponding
1825  // min start cumul, first from the delta, then if the max end cumul has
1826  // changed, from the unchanged paths as well.
1827  for (int r = 0; r < delta_path_transits_.NumPaths(); ++r) {
1828  new_min_start =
1829  std::min(ComputePathMaxStartFromEndCumul(delta_path_transits_, r,
1830  Start(r), new_max_end),
1831  new_min_start);
1832  }
1833  if (new_max_end != current_max_end_.cumul_value) {
1834  for (int r = 0; r < NumPaths(); ++r) {
1835  if (gtl::ContainsKey(delta_paths_, r)) {
1836  continue;
1837  }
1838  new_min_start = std::min(new_min_start, ComputePathMaxStartFromEndCumul(
1839  current_path_transits_, r,
1840  Start(r), new_max_end));
1841  }
1842  } else if (new_min_start > current_min_start_.cumul_value) {
1843  // Delta min start is greater than the current solution one.
1844  // If the path supporting the current min start has been modified, we need
1845  // to check all paths to find the smallest min start.
1846  if (!gtl::ContainsKey(delta_paths_,
1847  current_min_start_.cumul_value_support)) {
1848  new_min_start = current_min_start_.cumul_value;
1849  } else {
1850  for (int i = 0; i < current_min_start_.path_values.size(); ++i) {
1851  if (current_min_start_.path_values[i] < new_min_start &&
1852  !gtl::ContainsKey(delta_paths_, i)) {
1853  new_min_start = current_min_start_.path_values[i];
1854  }
1855  }
1856  }
1857  }
1858  }
1859 
1860  // Filtering on objective value, calling LPs and MIPs if needed..
1861  accepted_objective_value_ =
1862  CapAdd(cumul_cost_delta_, CapProd(global_span_cost_coefficient_,
1863  CapSub(new_max_end, new_min_start)));
1864 
1865  if (can_use_lp_ && optimizer_ != nullptr &&
1866  accepted_objective_value_ <= objective_max) {
1867  const size_t num_touched_paths = GetTouchedPathStarts().size();
1868  std::vector<int64> path_delta_cost_values(num_touched_paths, 0);
1869  std::vector<bool> requires_mp(num_touched_paths, false);
1870  for (int i = 0; i < num_touched_paths; ++i) {
1871  const int64 start = GetTouchedPathStarts()[i];
1872  const int vehicle = start_to_vehicle_[start];
1873  if (!FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
1874  continue;
1875  }
1876  int64 path_delta_cost_with_lp = 0;
1877  const DimensionSchedulingStatus status =
1878  optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
1879  vehicle, [this](int64 node) { return GetNext(node); },
1880  &path_delta_cost_with_lp);
1881  if (status == DimensionSchedulingStatus::INFEASIBLE) {
1882  return false;
1883  }
1884  DCHECK(gtl::ContainsKey(delta_paths_, GetPath(start)));
1885  const int64 path_cost_diff_with_lp = CapSub(
1886  path_delta_cost_with_lp, delta_path_cumul_cost_values_[vehicle]);
1887  if (path_cost_diff_with_lp > 0) {
1888  path_delta_cost_values[i] = path_delta_cost_with_lp;
1889  accepted_objective_value_ =
1890  CapAdd(accepted_objective_value_, path_cost_diff_with_lp);
1891  if (accepted_objective_value_ > objective_max) {
1892  return false;
1893  }
1894  } else {
1895  path_delta_cost_values[i] = delta_path_cumul_cost_values_[vehicle];
1896  }
1897  if (mp_optimizer_ != nullptr) {
1898  requires_mp[i] =
1899  FilterBreakCost(vehicle) ||
1900  (status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY);
1901  }
1902  }
1903  if (mp_optimizer_ == nullptr) {
1904  return accepted_objective_value_ <= objective_max;
1905  }
1906  for (int i = 0; i < num_touched_paths; ++i) {
1907  if (!requires_mp[i]) {
1908  continue;
1909  }
1910  const int64 start = GetTouchedPathStarts()[i];
1911  const int vehicle = start_to_vehicle_[start];
1912  int64 path_delta_cost_with_mp = 0;
1913  if (mp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
1914  vehicle, [this](int64 node) { return GetNext(node); },
1915  &path_delta_cost_with_mp) ==
1917  return false;
1918  }
1919  DCHECK(gtl::ContainsKey(delta_paths_, GetPath(start)));
1920  const int64 path_cost_diff_with_mp =
1921  CapSub(path_delta_cost_with_mp, path_delta_cost_values[i]);
1922  if (path_cost_diff_with_mp > 0) {
1923  accepted_objective_value_ =
1924  CapAdd(accepted_objective_value_, path_cost_diff_with_mp);
1925  if (accepted_objective_value_ > objective_max) {
1926  return false;
1927  }
1928  }
1929  }
1930  }
1931 
1932  return accepted_objective_value_ <= objective_max;
1933 }
1934 
1935 void PathCumulFilter::InitializeSupportedPathCumul(
1936  SupportedPathCumul* supported_cumul, int64 default_value) {
1937  supported_cumul->cumul_value = default_value;
1938  supported_cumul->cumul_value_support = -1;
1939  supported_cumul->path_values.resize(NumPaths(), default_value);
1940 }
1941 
1942 bool PathCumulFilter::PickupToDeliveryLimitsRespected(
1943  const PathTransits& path_transits, int path,
1944  const std::vector<int64>& min_path_cumuls) const {
1945  if (!dimension_.HasPickupToDeliveryLimits()) {
1946  return true;
1947  }
1948  const int num_pairs = routing_model_.GetPickupAndDeliveryPairs().size();
1949  DCHECK_GT(num_pairs, 0);
1950  std::vector<std::pair<int, int64>> visited_delivery_and_min_cumul_per_pair(
1951  num_pairs, {-1, -1});
1952 
1953  const int path_size = path_transits.PathSize(path);
1954  CHECK_EQ(min_path_cumuls.size(), path_size);
1955 
1956  int64 max_cumul = min_path_cumuls.back();
1957  for (int i = path_transits.PathSize(path) - 2; i >= 0; i--) {
1958  const int node_index = path_transits.Node(path, i);
1959  max_cumul = CapSub(max_cumul, path_transits.Transit(path, i));
1960  max_cumul = std::min(cumuls_[node_index]->Max(), max_cumul);
1961 
1962  const std::vector<std::pair<int, int>>& pickup_index_pairs =
1963  routing_model_.GetPickupIndexPairs(node_index);
1964  const std::vector<std::pair<int, int>>& delivery_index_pairs =
1965  routing_model_.GetDeliveryIndexPairs(node_index);
1966  if (!pickup_index_pairs.empty()) {
1967  // The node is a pickup. Check that it is not a delivery and that it
1968  // appears in a single pickup/delivery pair (as required when limits are
1969  // set on dimension cumuls for pickup and deliveries).
1970  DCHECK(delivery_index_pairs.empty());
1971  DCHECK_EQ(pickup_index_pairs.size(), 1);
1972  const int pair_index = pickup_index_pairs[0].first;
1973  // Get the delivery visited for this pair.
1974  const int delivery_index =
1975  visited_delivery_and_min_cumul_per_pair[pair_index].first;
1976  if (delivery_index < 0) {
1977  // No delivery visited after this pickup for this pickup/delivery pair.
1978  continue;
1979  }
1980  const int64 cumul_diff_limit = dimension_.GetPickupToDeliveryLimitForPair(
1981  pair_index, pickup_index_pairs[0].second, delivery_index);
1982  if (CapSub(visited_delivery_and_min_cumul_per_pair[pair_index].second,
1983  max_cumul) > cumul_diff_limit) {
1984  return false;
1985  }
1986  }
1987  if (!delivery_index_pairs.empty()) {
1988  // The node is a delivery. Check that it's not a pickup and it belongs to
1989  // a single pair.
1990  DCHECK(pickup_index_pairs.empty());
1991  DCHECK_EQ(delivery_index_pairs.size(), 1);
1992  const int pair_index = delivery_index_pairs[0].first;
1993  std::pair<int, int64>& delivery_index_and_cumul =
1994  visited_delivery_and_min_cumul_per_pair[pair_index];
1995  int& delivery_index = delivery_index_and_cumul.first;
1996  DCHECK_EQ(delivery_index, -1);
1997  delivery_index = delivery_index_pairs[0].second;
1998  delivery_index_and_cumul.second = min_path_cumuls[i];
1999  }
2000  }
2001  return true;
2002 }
2003 
2004 void PathCumulFilter::StoreMinMaxCumulOfNodesOnPath(
2005  int path, const std::vector<int64>& min_path_cumuls, bool is_delta) {
2006  const PathTransits& path_transits =
2007  is_delta ? delta_path_transits_ : current_path_transits_;
2008 
2009  const int path_size = path_transits.PathSize(path);
2010  DCHECK_EQ(min_path_cumuls.size(), path_size);
2011 
2012  int64 max_cumul = cumuls_[path_transits.Node(path, path_size - 1)]->Max();
2013  for (int i = path_size - 1; i >= 0; i--) {
2014  const int node_index = path_transits.Node(path, i);
2015 
2016  if (i < path_size - 1) {
2017  max_cumul = CapSub(max_cumul, path_transits.Transit(path, i));
2018  max_cumul = std::min(cumuls_[node_index]->Max(), max_cumul);
2019  }
2020 
2021  if (is_delta && node_index_to_precedences_[node_index].empty()) {
2022  // No need to update the delta cumul map for nodes without precedences.
2023  continue;
2024  }
2025 
2026  std::pair<int64, int64>& min_max_cumuls =
2027  is_delta ? node_with_precedence_to_delta_min_max_cumuls_[node_index]
2028  : current_min_max_node_cumuls_[node_index];
2029  min_max_cumuls.first = min_path_cumuls[i];
2030  min_max_cumuls.second = max_cumul;
2031 
2032  if (is_delta && !routing_model_.IsEnd(node_index) &&
2033  (min_max_cumuls.first !=
2034  current_min_max_node_cumuls_[node_index].first ||
2035  max_cumul != current_min_max_node_cumuls_[node_index].second)) {
2036  delta_nodes_with_precedences_and_changed_cumul_.Set(node_index);
2037  }
2038  }
2039 }
2040 
2041 int64 PathCumulFilter::ComputePathMaxStartFromEndCumul(
2042  const PathTransits& path_transits, int path, int64 path_start,
2043  int64 min_end_cumul) const {
2044  int64 cumul_from_min_end = min_end_cumul;
2045  int64 cumul_from_max_end =
2046  cumuls_[routing_model_.End(start_to_vehicle_[path_start])]->Max();
2047  for (int i = path_transits.PathSize(path) - 2; i >= 0; --i) {
2048  const int64 transit = path_transits.Transit(path, i);
2049  const int64 node = path_transits.Node(path, i);
2050  cumul_from_min_end =
2051  std::min(cumuls_[node]->Max(), CapSub(cumul_from_min_end, transit));
2052  cumul_from_max_end = dimension_.GetLastPossibleLessOrEqualValueForNode(
2053  node, CapSub(cumul_from_max_end, transit));
2054  }
2055  return std::min(cumul_from_min_end, cumul_from_max_end);
2056 }
2057 
2058 } // namespace
2059 
2061  const RoutingDimension& dimension,
2062  const RoutingSearchParameters& parameters,
2063  bool propagate_own_objective_value, bool filter_objective_cost,
2064  bool can_use_lp) {
2065  RoutingModel& model = *dimension.model();
2066  return model.solver()->RevAlloc(new PathCumulFilter(
2067  model, dimension, parameters, propagate_own_objective_value,
2068  filter_objective_cost, can_use_lp));
2069 }
2070 
2071 namespace {
2072 
2073 bool DimensionHasCumulCost(const RoutingDimension& dimension) {
2074  if (dimension.global_span_cost_coefficient() != 0) return true;
2075  if (dimension.HasSoftSpanUpperBounds()) return true;
2076  if (dimension.HasQuadraticCostSoftSpanUpperBounds()) return true;
2077  for (const int64 coefficient : dimension.vehicle_span_cost_coefficients()) {
2078  if (coefficient != 0) return true;
2079  }
2080  for (int i = 0; i < dimension.cumuls().size(); ++i) {
2081  if (dimension.HasCumulVarSoftUpperBound(i)) return true;
2082  if (dimension.HasCumulVarSoftLowerBound(i)) return true;
2083  if (dimension.HasCumulVarPiecewiseLinearCost(i)) return true;
2084  }
2085  return false;
2086 }
2087 
2088 bool DimensionHasCumulConstraint(const RoutingDimension& dimension) {
2089  if (dimension.HasBreakConstraints()) return true;
2090  if (dimension.HasPickupToDeliveryLimits()) return true;
2091  if (!dimension.GetNodePrecedences().empty()) return true;
2092  for (const int64 upper_bound : dimension.vehicle_span_upper_bounds()) {
2093  if (upper_bound != kint64max) return true;
2094  }
2095  for (const IntVar* const slack : dimension.slacks()) {
2096  if (slack->Min() > 0) return true;
2097  }
2098  const std::vector<IntVar*>& cumuls = dimension.cumuls();
2099  for (int i = 0; i < cumuls.size(); ++i) {
2100  IntVar* const cumul_var = cumuls[i];
2101  if (cumul_var->Min() > 0 && cumul_var->Max() < kint64max &&
2102  !dimension.model()->IsEnd(i)) {
2103  return true;
2104  }
2105  if (dimension.forbidden_intervals()[i].NumIntervals() > 0) return true;
2106  }
2107  return false;
2108 }
2109 
2110 } // namespace
2111 
2113  const std::vector<RoutingDimension*>& dimensions,
2114  const RoutingSearchParameters& parameters, bool filter_objective_cost,
2115  std::vector<LocalSearchFilter*>* filters) {
2116  // NOTE: We first sort the dimensions by increasing complexity of filtering:
2117  // - Dimensions without any cumul-related costs or constraints will have a
2118  // ChainCumulFilter.
2119  // - Dimensions with cumul costs or constraints, but no global span cost
2120  // and/or precedences will have a PathCumulFilter.
2121  // - Dimensions with a global span cost coefficient and/or precedences will
2122  // have a global LP filter.
2123  const int num_dimensions = dimensions.size();
2124 
2125  std::vector<bool> use_path_cumul_filter(num_dimensions);
2126  std::vector<bool> use_cumul_bounds_propagator_filter(num_dimensions);
2127  std::vector<bool> use_global_lp_filter(num_dimensions);
2128  std::vector<int> filtering_difficulty(num_dimensions);
2129  for (int d = 0; d < num_dimensions; d++) {
2130  const RoutingDimension& dimension = *dimensions[d];
2131  const bool has_cumul_cost = DimensionHasCumulCost(dimension);
2132  use_path_cumul_filter[d] =
2133  has_cumul_cost || DimensionHasCumulConstraint(dimension);
2134 
2135  const bool can_use_cumul_bounds_propagator_filter =
2136  !dimension.HasBreakConstraints() &&
2137  (!filter_objective_cost || !has_cumul_cost);
2138  const bool has_precedences = !dimension.GetNodePrecedences().empty();
2139  use_global_lp_filter[d] =
2140  (has_precedences && !can_use_cumul_bounds_propagator_filter) ||
2141  (filter_objective_cost && dimension.global_span_cost_coefficient() > 0);
2142 
2143  use_cumul_bounds_propagator_filter[d] =
2144  has_precedences && !use_global_lp_filter[d];
2145 
2146  filtering_difficulty[d] = 4 * use_global_lp_filter[d] +
2147  2 * use_cumul_bounds_propagator_filter[d] +
2148  use_path_cumul_filter[d];
2149  }
2150 
2151  std::vector<int> sorted_dimension_indices(num_dimensions);
2152  std::iota(sorted_dimension_indices.begin(), sorted_dimension_indices.end(),
2153  0);
2154  std::sort(sorted_dimension_indices.begin(), sorted_dimension_indices.end(),
2155  [&filtering_difficulty](int d1, int d2) {
2156  return filtering_difficulty[d1] < filtering_difficulty[d2];
2157  });
2158 
2159  for (const int d : sorted_dimension_indices) {
2160  const RoutingDimension& dimension = *dimensions[d];
2161  const RoutingModel& model = *dimension.model();
2162  // NOTE: We always add the [Chain|Path]CumulFilter to filter each route's
2163  // feasibility separately to try and cut bad decisions earlier in the
2164  // search, but we don't propagate the computed cost if the LPCumulFilter is
2165  // already doing it.
2166  const bool use_global_lp = use_global_lp_filter[d];
2167  if (use_path_cumul_filter[d]) {
2168  filters->push_back(MakePathCumulFilter(
2169  dimension, parameters, !use_global_lp, filter_objective_cost));
2170  } else {
2171  filters->push_back(
2172  model.solver()->RevAlloc(new ChainCumulFilter(model, dimension)));
2173  }
2174 
2175  if (use_global_lp) {
2176  DCHECK(model.GetMutableGlobalCumulOptimizer(dimension) != nullptr);
2177  filters->push_back(MakeGlobalLPCumulFilter(
2178  model.GetMutableGlobalCumulOptimizer(dimension),
2179  filter_objective_cost));
2180  } else if (use_cumul_bounds_propagator_filter[d]) {
2181  filters->push_back(MakeCumulBoundsPropagatorFilter(dimension));
2182  }
2183  }
2184 }
2185 
2186 namespace {
2187 
2188 // Filter for pickup/delivery precedences.
2189 class PickupDeliveryFilter : public BasePathFilter {
2190  public:
2191  PickupDeliveryFilter(const std::vector<IntVar*>& nexts, int next_domain_size,
2192  const RoutingModel::IndexPairs& pairs,
2193  const std::vector<RoutingModel::PickupAndDeliveryPolicy>&
2194  vehicle_policies);
2195  ~PickupDeliveryFilter() override {}
2196  bool AcceptPath(int64 path_start, int64 chain_start,
2197  int64 chain_end) override;
2198  std::string DebugString() const override { return "PickupDeliveryFilter"; }
2199 
2200  private:
2201  bool AcceptPathDefault(int64 path_start);
2202  template <bool lifo>
2203  bool AcceptPathOrdered(int64 path_start);
2204 
2205  std::vector<int> pair_firsts_;
2206  std::vector<int> pair_seconds_;
2207  const RoutingModel::IndexPairs pairs_;
2208  SparseBitset<> visited_;
2209  std::deque<int> visited_deque_;
2210  const std::vector<RoutingModel::PickupAndDeliveryPolicy> vehicle_policies_;
2211 };
2212 
2213 PickupDeliveryFilter::PickupDeliveryFilter(
2214  const std::vector<IntVar*>& nexts, int next_domain_size,
2215  const RoutingModel::IndexPairs& pairs,
2216  const std::vector<RoutingModel::PickupAndDeliveryPolicy>& vehicle_policies)
2217  : BasePathFilter(nexts, next_domain_size),
2218  pair_firsts_(next_domain_size, kUnassigned),
2219  pair_seconds_(next_domain_size, kUnassigned),
2220  pairs_(pairs),
2221  visited_(Size()),
2222  vehicle_policies_(vehicle_policies) {
2223  for (int i = 0; i < pairs.size(); ++i) {
2224  const auto& index_pair = pairs[i];
2225  for (int first : index_pair.first) {
2226  pair_firsts_[first] = i;
2227  }
2228  for (int second : index_pair.second) {
2229  pair_seconds_[second] = i;
2230  }
2231  }
2232 }
2233 
2234 bool PickupDeliveryFilter::AcceptPath(int64 path_start, int64 chain_start,
2235  int64 chain_end) {
2236  switch (vehicle_policies_[GetPath(path_start)]) {
2237  case RoutingModel::PICKUP_AND_DELIVERY_NO_ORDER:
2238  return AcceptPathDefault(path_start);
2239  case RoutingModel::PICKUP_AND_DELIVERY_LIFO:
2240  return AcceptPathOrdered<true>(path_start);
2241  case RoutingModel::PICKUP_AND_DELIVERY_FIFO:
2242  return AcceptPathOrdered<false>(path_start);
2243  default:
2244  return true;
2245  }
2246 }
2247 
2248 bool PickupDeliveryFilter::AcceptPathDefault(int64 path_start) {
2249  visited_.ClearAll();
2250  int64 node = path_start;
2251  int64 path_length = 1;
2252  while (node < Size()) {
2253  // Detect sub-cycles (path is longer than longest possible path).
2254  if (path_length > Size()) {
2255  return false;
2256  }
2257  if (pair_firsts_[node] != kUnassigned) {
2258  // Checking on pair firsts is not actually necessary (inconsistencies
2259  // will get caught when checking pair seconds); doing it anyway to
2260  // cut checks early.
2261  for (int second : pairs_[pair_firsts_[node]].second) {
2262  if (visited_[second]) {
2263  return false;
2264  }
2265  }
2266  }
2267  if (pair_seconds_[node] != kUnassigned) {
2268  bool found_first = false;
2269  bool some_synced = false;
2270  for (int first : pairs_[pair_seconds_[node]].first) {
2271  if (visited_[first]) {
2272  found_first = true;
2273  break;
2274  }
2275  if (IsVarSynced(first)) {
2276  some_synced = true;
2277  }
2278  }
2279  if (!found_first && some_synced) {
2280  return false;
2281  }
2282  }
2283  visited_.Set(node);
2284  const int64 next = GetNext(node);
2285  if (next == kUnassigned) {
2286  // LNS detected, return true since path was ok up to now.
2287  return true;
2288  }
2289  node = next;
2290  ++path_length;
2291  }
2292  for (const int64 node : visited_.PositionsSetAtLeastOnce()) {
2293  if (pair_firsts_[node] != kUnassigned) {
2294  bool found_second = false;
2295  bool some_synced = false;
2296  for (int second : pairs_[pair_firsts_[node]].second) {
2297  if (visited_[second]) {
2298  found_second = true;
2299  break;
2300  }
2301  if (IsVarSynced(second)) {
2302  some_synced = true;
2303  }
2304  }
2305  if (!found_second && some_synced) {
2306  return false;
2307  }
2308  }
2309  }
2310  return true;
2311 }
2312 
2313 template <bool lifo>
2314 bool PickupDeliveryFilter::AcceptPathOrdered(int64 path_start) {
2315  visited_deque_.clear();
2316  int64 node = path_start;
2317  int64 path_length = 1;
2318  while (node < Size()) {
2319  // Detect sub-cycles (path is longer than longest possible path).
2320  if (path_length > Size()) {
2321  return false;
2322  }
2323  if (pair_firsts_[node] != kUnassigned) {
2324  if (lifo) {
2325  visited_deque_.push_back(node);
2326  } else {
2327  visited_deque_.push_front(node);
2328  }
2329  }
2330  if (pair_seconds_[node] != kUnassigned) {
2331  bool found_first = false;
2332  bool some_synced = false;
2333  for (int first : pairs_[pair_seconds_[node]].first) {
2334  if (!visited_deque_.empty() && visited_deque_.back() == first) {
2335  found_first = true;
2336  break;
2337  }
2338  if (IsVarSynced(first)) {
2339  some_synced = true;
2340  }
2341  }
2342  if (!found_first && some_synced) {
2343  return false;
2344  } else if (!visited_deque_.empty()) {
2345  visited_deque_.pop_back();
2346  }
2347  }
2348  const int64 next = GetNext(node);
2349  if (next == kUnassigned) {
2350  // LNS detected, return true since path was ok up to now.
2351  return true;
2352  }
2353  node = next;
2354  ++path_length;
2355  }
2356  while (!visited_deque_.empty()) {
2357  for (int second : pairs_[pair_firsts_[visited_deque_.back()]].second) {
2358  if (IsVarSynced(second)) {
2359  return false;
2360  }
2361  }
2362  visited_deque_.pop_back();
2363  }
2364  return true;
2365 }
2366 
2367 } // namespace
2368 
2370  const RoutingModel& routing_model, const RoutingModel::IndexPairs& pairs,
2371  const std::vector<RoutingModel::PickupAndDeliveryPolicy>&
2372  vehicle_policies) {
2373  return routing_model.solver()->RevAlloc(new PickupDeliveryFilter(
2374  routing_model.Nexts(), routing_model.Size() + routing_model.vehicles(),
2375  pairs, vehicle_policies));
2376 }
2377 
2378 namespace {
2379 
2380 // Vehicle variable filter
2381 class VehicleVarFilter : public BasePathFilter {
2382  public:
2383  explicit VehicleVarFilter(const RoutingModel& routing_model);
2384  ~VehicleVarFilter() override {}
2385  bool AcceptPath(int64 path_start, int64 chain_start,
2386  int64 chain_end) override;
2387  std::string DebugString() const override { return "VehicleVariableFilter"; }
2388 
2389  private:
2390  bool DisableFiltering() const override;
2391  bool IsVehicleVariableConstrained(int index) const;
2392 
2393  std::vector<int64> start_to_vehicle_;
2394  std::vector<IntVar*> vehicle_vars_;
2395  const int64 unconstrained_vehicle_var_domain_size_;
2396 };
2397 
2398 VehicleVarFilter::VehicleVarFilter(const RoutingModel& routing_model)
2399  : BasePathFilter(routing_model.Nexts(),
2400  routing_model.Size() + routing_model.vehicles()),
2401  vehicle_vars_(routing_model.VehicleVars()),
2402  unconstrained_vehicle_var_domain_size_(routing_model.vehicles()) {
2403  start_to_vehicle_.resize(Size(), -1);
2404  for (int i = 0; i < routing_model.vehicles(); ++i) {
2405  start_to_vehicle_[routing_model.Start(i)] = i;
2406  }
2407 }
2408 
2409 bool VehicleVarFilter::AcceptPath(int64 path_start, int64 chain_start,
2410  int64 chain_end) {
2411  const int64 vehicle = start_to_vehicle_[path_start];
2412  int64 node = chain_start;
2413  while (node != chain_end) {
2414  if (!vehicle_vars_[node]->Contains(vehicle)) {
2415  return false;
2416  }
2417  node = GetNext(node);
2418  }
2419  return vehicle_vars_[node]->Contains(vehicle);
2420 }
2421 
2422 bool VehicleVarFilter::DisableFiltering() const {
2423  for (int i = 0; i < vehicle_vars_.size(); ++i) {
2424  if (IsVehicleVariableConstrained(i)) return false;
2425  }
2426  return true;
2427 }
2428 
2429 bool VehicleVarFilter::IsVehicleVariableConstrained(int index) const {
2430  const IntVar* const vehicle_var = vehicle_vars_[index];
2431  // If vehicle variable contains -1 (optional node), then we need to
2432  // add it to the "unconstrained" domain. Impact we don't filter mandatory
2433  // nodes made inactive here, but it is covered by other filters.
2434  const int adjusted_unconstrained_vehicle_var_domain_size =
2435  vehicle_var->Min() >= 0 ? unconstrained_vehicle_var_domain_size_
2436  : unconstrained_vehicle_var_domain_size_ + 1;
2437  return vehicle_var->Size() != adjusted_unconstrained_vehicle_var_domain_size;
2438 }
2439 
2440 } // namespace
2441 
2443  const RoutingModel& routing_model) {
2444  return routing_model.solver()->RevAlloc(new VehicleVarFilter(routing_model));
2445 }
2446 
2447 namespace {
2448 
2449 class CumulBoundsPropagatorFilter : public IntVarLocalSearchFilter {
2450  public:
2451  explicit CumulBoundsPropagatorFilter(const RoutingDimension& dimension);
2452  bool Accept(const Assignment* delta, const Assignment* deltadelta,
2453  int64 objective_min, int64 objective_max) override;
2454  std::string DebugString() const override {
2455  return "CumulBoundsPropagatorFilter(" + propagator_.dimension().name() +
2456  ")";
2457  }
2458 
2459  private:
2460  CumulBoundsPropagator propagator_;
2461  const int64 cumul_offset_;
2462  SparseBitset<int64> delta_touched_;
2463  std::vector<int64> delta_nexts_;
2464 };
2465 
2466 CumulBoundsPropagatorFilter::CumulBoundsPropagatorFilter(
2467  const RoutingDimension& dimension)
2468  : IntVarLocalSearchFilter(dimension.model()->Nexts()),
2469  propagator_(&dimension),
2470  cumul_offset_(dimension.GetGlobalOptimizerOffset()),
2471  delta_touched_(Size()),
2472  delta_nexts_(Size()) {}
2473 
2474 bool CumulBoundsPropagatorFilter::Accept(const Assignment* delta,
2475  const Assignment* deltadelta,
2476  int64 objective_min,
2477  int64 objective_max) {
2478  delta_touched_.ClearAll();
2479  for (const IntVarElement& delta_element :
2480  delta->IntVarContainer().elements()) {
2481  int64 index = -1;
2482  if (FindIndex(delta_element.Var(), &index)) {
2483  if (!delta_element.Bound()) {
2484  // LNS detected
2485  return true;
2486  }
2487  delta_touched_.Set(index);
2488  delta_nexts_[index] = delta_element.Value();
2489  }
2490  }
2491  const auto& next_accessor = [this](int64 index) {
2492  return delta_touched_[index] ? delta_nexts_[index] : Value(index);
2493  };
2494 
2495  return propagator_.PropagateCumulBounds(next_accessor, cumul_offset_);
2496 }
2497 
2498 } // namespace
2499 
2501  const RoutingDimension& dimension) {
2502  return dimension.model()->solver()->RevAlloc(
2503  new CumulBoundsPropagatorFilter(dimension));
2504 }
2505 
2506 namespace {
2507 
2508 class LPCumulFilter : public IntVarLocalSearchFilter {
2509  public:
2510  LPCumulFilter(const std::vector<IntVar*>& nexts,
2511  GlobalDimensionCumulOptimizer* optimizer,
2512  bool filter_objective_cost);
2513  bool Accept(const Assignment* delta, const Assignment* deltadelta,
2514  int64 objective_min, int64 objective_max) override;
2515  int64 GetAcceptedObjectiveValue() const override;
2516  void OnSynchronize(const Assignment* delta) override;
2517  int64 GetSynchronizedObjectiveValue() const override;
2518  std::string DebugString() const override {
2519  return "LPCumulFilter(" + optimizer_.dimension()->name() + ")";
2520  }
2521 
2522  private:
2523  GlobalDimensionCumulOptimizer& optimizer_;
2524  const bool filter_objective_cost_;
2525  int64 synchronized_cost_without_transit_;
2526  int64 delta_cost_without_transit_;
2527  SparseBitset<int64> delta_touched_;
2528  std::vector<int64> delta_nexts_;
2529 };
2530 
2531 LPCumulFilter::LPCumulFilter(const std::vector<IntVar*>& nexts,
2532  GlobalDimensionCumulOptimizer* optimizer,
2533  bool filter_objective_cost)
2534  : IntVarLocalSearchFilter(nexts),
2535  optimizer_(*optimizer),
2536  filter_objective_cost_(filter_objective_cost),
2537  synchronized_cost_without_transit_(-1),
2538  delta_cost_without_transit_(-1),
2539  delta_touched_(Size()),
2540  delta_nexts_(Size()) {}
2541 
2542 bool LPCumulFilter::Accept(const Assignment* delta,
2543  const Assignment* deltadelta, int64 objective_min,
2544  int64 objective_max) {
2545  delta_touched_.ClearAll();
2546  for (const IntVarElement& delta_element :
2547  delta->IntVarContainer().elements()) {
2548  int64 index = -1;
2549  if (FindIndex(delta_element.Var(), &index)) {
2550  if (!delta_element.Bound()) {
2551  // LNS detected
2552  return true;
2553  }
2554  delta_touched_.Set(index);
2555  delta_nexts_[index] = delta_element.Value();
2556  }
2557  }
2558  const auto& next_accessor = [this](int64 index) {
2559  return delta_touched_[index] ? delta_nexts_[index] : Value(index);
2560  };
2561 
2562  if (!filter_objective_cost_) {
2563  // No need to compute the cost of the LP, only verify its feasibility.
2564  delta_cost_without_transit_ = 0;
2565  return optimizer_.IsFeasible(next_accessor);
2566  }
2567 
2568  if (!optimizer_.ComputeCumulCostWithoutFixedTransits(
2569  next_accessor, &delta_cost_without_transit_)) {
2570  // Infeasible.
2571  delta_cost_without_transit_ = kint64max;
2572  return false;
2573  }
2574  return delta_cost_without_transit_ <= objective_max;
2575 }
2576 
2577 int64 LPCumulFilter::GetAcceptedObjectiveValue() const {
2578  return delta_cost_without_transit_;
2579 }
2580 
2581 void LPCumulFilter::OnSynchronize(const Assignment* delta) {
2582  // TODO(user): Try to optimize this so the LP is not called when the last
2583  // computed delta cost corresponds to the solution being synchronized.
2584  if (!optimizer_.ComputeCumulCostWithoutFixedTransits(
2585  [this](int64 index) { return Value(index); },
2586  &synchronized_cost_without_transit_)) {
2587  // TODO(user): This should only happen if the LP solver times out.
2588  // DCHECK the fail wasn't due to an infeasible model.
2589  synchronized_cost_without_transit_ = 0;
2590  }
2591 }
2592 
2593 int64 LPCumulFilter::GetSynchronizedObjectiveValue() const {
2594  return synchronized_cost_without_transit_;
2595 }
2596 
2597 } // namespace
2598 
2600  GlobalDimensionCumulOptimizer* optimizer, bool filter_objective_cost) {
2601  const RoutingModel& model = *optimizer->dimension()->model();
2602  return model.solver()->RevAlloc(
2603  new LPCumulFilter(model.Nexts(), optimizer, filter_objective_cost));
2604 }
2605 
2607 
2608 CPFeasibilityFilter::CPFeasibilityFilter(const RoutingModel* routing_model)
2609  : IntVarLocalSearchFilter(routing_model->Nexts()),
2610  model_(routing_model),
2611  solver_(routing_model->solver()),
2612  assignment_(solver_->MakeAssignment()),
2613  temp_assignment_(solver_->MakeAssignment()),
2614  restore_(solver_->MakeRestoreAssignment(temp_assignment_)) {
2615  assignment_->Add(routing_model->Nexts());
2616 }
2617 
2618 bool CPFeasibilityFilter::Accept(const Assignment* delta,
2619  const Assignment* deltadelta,
2620  int64 objective_min, int64 objective_max) {
2621  temp_assignment_->Copy(assignment_);
2622  AddDeltaToAssignment(delta, temp_assignment_);
2623  return solver_->Solve(restore_);
2624 }
2625 
2627  AddDeltaToAssignment(delta, assignment_);
2628 }
2629 
2630 void CPFeasibilityFilter::AddDeltaToAssignment(const Assignment* delta,
2631  Assignment* assignment) {
2632  if (delta == nullptr) {
2633  return;
2634  }
2635  Assignment::IntContainer* const container =
2636  assignment->MutableIntVarContainer();
2637  const Assignment::IntContainer& delta_container = delta->IntVarContainer();
2638  const int delta_size = delta_container.Size();
2639 
2640  for (int i = 0; i < delta_size; i++) {
2641  const IntVarElement& delta_element = delta_container.Element(i);
2642  IntVar* const var = delta_element.Var();
2643  int64 index = kUnassigned;
2644  CHECK(FindIndex(var, &index));
2645  DCHECK_EQ(var, Var(index));
2646  const int64 value = delta_element.Value();
2647 
2648  container->AddAtPosition(var, index)->SetValue(value);
2649  if (model_->IsStart(index)) {
2650  if (model_->IsEnd(value)) {
2651  // Do not restore unused routes.
2652  container->MutableElement(index)->Deactivate();
2653  } else {
2654  // Re-activate the route's start in case it was deactivated before.
2655  container->MutableElement(index)->Activate();
2656  }
2657  }
2658  }
2659 }
2660 
2662  const RoutingModel* routing_model) {
2663  return routing_model->solver()->RevAlloc(
2664  new CPFeasibilityFilter(routing_model));
2665 }
2666 
2667 // TODO(user): Implement same-vehicle filter. Could be merged with node
2668 // precedence filter.
2669 
2670 // --- VehicleTypeCurator ---
2671 
2673  int type, std::function<bool(int)> vehicle_is_compatible) {
2674  std::set<VehicleTypeCurator::VehicleClassEntry>& sorted_classes =
2675  sorted_vehicle_classes_per_type_[type];
2676  auto vehicle_class_it = sorted_classes.begin();
2677 
2678  while (vehicle_class_it != sorted_classes.end()) {
2679  const int vehicle_class = vehicle_class_it->vehicle_class;
2680  std::vector<int>& vehicles = vehicles_per_vehicle_class_[vehicle_class];
2681  DCHECK(!vehicles.empty());
2682 
2683  for (auto vehicle_it = vehicles.begin(); vehicle_it != vehicles.end();
2684  vehicle_it++) {
2685  const int vehicle = *vehicle_it;
2686  if (vehicle_is_compatible(vehicle)) {
2687  vehicles.erase(vehicle_it);
2688  if (vehicles.empty()) {
2689  sorted_classes.erase(vehicle_class_it);
2690  }
2691  return vehicle;
2692  }
2693  }
2694  // If no compatible vehicle was found in this class, move on to the next
2695  // vehicle class.
2696  vehicle_class_it++;
2697  }
2698  // No compatible vehicle of the given type was found.
2699  return -1;
2700 }
2701 
2702 // --- First solution decision builder ---
2703 
2704 // IntVarFilteredDecisionBuilder
2705 
2707  std::unique_ptr<IntVarFilteredHeuristic> heuristic)
2708  : heuristic_(std::move(heuristic)) {}
2709 
2710 Decision* IntVarFilteredDecisionBuilder::Next(Solver* solver) {
2711  Assignment* const assignment = heuristic_->BuildSolution();
2712  if (assignment != nullptr) {
2713  VLOG(2) << "Number of decisions: " << heuristic_->number_of_decisions();
2714  VLOG(2) << "Number of rejected decisions: "
2715  << heuristic_->number_of_rejects();
2716  assignment->Restore();
2717  } else {
2718  solver->Fail();
2719  }
2720  return nullptr;
2721 }
2722 
2724  return heuristic_->number_of_decisions();
2725 }
2726 
2728  return heuristic_->number_of_rejects();
2729 }
2730 
2732  return absl::StrCat("IntVarFilteredDecisionBuilder(",
2733  heuristic_->DebugString(), ")");
2734 }
2735 
2736 // --- First solution heuristics ---
2737 
2738 // IntVarFilteredHeuristic
2739 
2741  Solver* solver, const std::vector<IntVar*>& vars,
2742  LocalSearchFilterManager* filter_manager)
2743  : assignment_(solver->MakeAssignment()),
2744  solver_(solver),
2745  vars_(vars),
2746  delta_(solver->MakeAssignment()),
2747  is_in_delta_(vars_.size(), false),
2748  empty_(solver->MakeAssignment()),
2749  filter_manager_(filter_manager),
2750  number_of_decisions_(0),
2751  number_of_rejects_(0) {
2752  assignment_->MutableIntVarContainer()->Resize(vars_.size());
2753  delta_indices_.reserve(vars_.size());
2754 }
2755 
2757  number_of_decisions_ = 0;
2758  number_of_rejects_ = 0;
2759  // Wiping assignment when starting a new search.
2760  assignment_->MutableIntVarContainer()->Clear();
2761  assignment_->MutableIntVarContainer()->Resize(vars_.size());
2762  delta_->MutableIntVarContainer()->Clear();
2764 }
2765 
2767  ResetSolution();
2768  if (!InitializeSolution()) {
2769  return nullptr;
2770  }
2772  if (BuildSolutionInternal()) {
2773  return assignment_;
2774  }
2775  return nullptr;
2776 }
2777 
2779  const std::function<int64(int64)>& next_accessor) {
2780  ResetSolution();
2782  // NOTE: We don't need to clear or pre-set the two following vectors as the
2783  // for loop below will set all elements.
2784  start_chain_ends_.resize(model()->vehicles());
2785  end_chain_starts_.resize(model()->vehicles());
2786 
2787  for (int v = 0; v < model_->vehicles(); v++) {
2788  int64 node = model_->Start(v);
2789  while (!model_->IsEnd(node)) {
2790  const int64 next = next_accessor(node);
2791  DCHECK_NE(next, node);
2792  SetValue(node, next);
2793  SetVehicleIndex(node, v);
2794  node = next;
2795  }
2796  // All vehicles have full routes from start to end here.
2797  start_chain_ends_[v] = model()->End(v);
2798  end_chain_starts_[v] = model()->Start(v);
2799  }
2800  if (!Commit()) {
2801  return nullptr;
2802  }
2804  if (BuildSolutionInternal()) {
2805  return assignment_;
2806  }
2807  return nullptr;
2808 }
2809 
2811  ++number_of_decisions_;
2812  const bool accept = FilterAccept();
2813  if (accept) {
2814  const Assignment::IntContainer& delta_container = delta_->IntVarContainer();
2815  const int delta_size = delta_container.Size();
2816  Assignment::IntContainer* const container =
2817  assignment_->MutableIntVarContainer();
2818  for (int i = 0; i < delta_size; ++i) {
2819  const IntVarElement& delta_element = delta_container.Element(i);
2820  IntVar* const var = delta_element.Var();
2821  DCHECK_EQ(var, vars_[delta_indices_[i]]);
2822  container->AddAtPosition(var, delta_indices_[i])
2823  ->SetValue(delta_element.Value());
2824  }
2826  } else {
2827  ++number_of_rejects_;
2828  }
2829  // Reset is_in_delta to all false.
2830  for (const int delta_index : delta_indices_) {
2831  is_in_delta_[delta_index] = false;
2832  }
2833  delta_->Clear();
2834  delta_indices_.clear();
2835  return accept;
2836 }
2837 
2839  if (filter_manager_) filter_manager_->Synchronize(assignment_, delta_);
2840 }
2841 
2842 bool IntVarFilteredHeuristic::FilterAccept() {
2843  if (!filter_manager_) return true;
2844  LocalSearchMonitor* const monitor = solver_->GetLocalSearchMonitor();
2845  return filter_manager_->Accept(monitor, delta_, empty_, kint64min, kint64max);
2846 }
2847 
2848 // RoutingFilteredHeuristic
2849 
2851  RoutingModel* model, LocalSearchFilterManager* filter_manager)
2852  : IntVarFilteredHeuristic(model->solver(), model->Nexts(), filter_manager),
2853  model_(model) {}
2854 
2855 bool RoutingFilteredHeuristic::InitializeSolution() {
2856  // Find the chains of nodes (when nodes have their "Next" value bound in the
2857  // current solution, it forms a link in a chain). Eventually, starts[end]
2858  // will contain the index of the first node of the chain ending at node 'end'
2859  // and ends[start] will be the last node of the chain starting at node
2860  // 'start'. Values of starts[node] and ends[node] for other nodes is used
2861  // for intermediary computations and do not necessarily reflect actual chain
2862  // starts and ends.
2863 
2864  // Start by adding partial start chains to current assignment.
2865  start_chain_ends_.clear();
2866  start_chain_ends_.resize(model()->vehicles(), -1);
2867  end_chain_starts_.clear();
2868  end_chain_starts_.resize(model()->vehicles(), -1);
2869 
2871  for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
2872  int64 node = model()->Start(vehicle);
2873  while (!model()->IsEnd(node) && Var(node)->Bound()) {
2874  const int64 next = Var(node)->Min();
2875  SetValue(node, next);
2876  SetVehicleIndex(node, vehicle);
2877  node = next;
2878  }
2879  start_chain_ends_[vehicle] = node;
2880  }
2881 
2882  std::vector<int64> starts(Size() + model()->vehicles(), -1);
2883  std::vector<int64> ends(Size() + model()->vehicles(), -1);
2884  for (int node = 0; node < Size() + model()->vehicles(); ++node) {
2885  // Each node starts as a singleton chain.
2886  starts[node] = node;
2887  ends[node] = node;
2888  }
2889  std::vector<bool> touched(Size(), false);
2890  for (int node = 0; node < Size(); ++node) {
2891  int current = node;
2892  while (!model()->IsEnd(current) && !touched[current]) {
2893  touched[current] = true;
2894  IntVar* const next_var = Var(current);
2895  if (next_var->Bound()) {
2896  current = next_var->Value();
2897  }
2898  }
2899  // Merge the sub-chain starting from 'node' and ending at 'current' with
2900  // the existing sub-chain starting at 'current'.
2901  starts[ends[current]] = starts[node];
2902  ends[starts[node]] = ends[current];
2903  }
2904 
2905  // Set each route to be the concatenation of the chain at its starts and the
2906  // chain at its end, without nodes in between.
2907  for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
2908  end_chain_starts_[vehicle] = starts[model()->End(vehicle)];
2909  int64 node = start_chain_ends_[vehicle];
2910  if (!model()->IsEnd(node)) {
2911  int64 next = starts[model()->End(vehicle)];
2912  SetValue(node, next);
2913  SetVehicleIndex(node, vehicle);
2914  node = next;
2915  while (!model()->IsEnd(node)) {
2916  next = Var(node)->Min();
2917  SetValue(node, next);
2918  SetVehicleIndex(node, vehicle);
2919  node = next;
2920  }
2921  }
2922  }
2923 
2924  if (!Commit()) {
2926  return false;
2927  }
2928  return true;
2929 }
2930 
2933  node, 1, [this, node](int alternate) {
2934  if (node != alternate && !Contains(alternate)) {
2935  SetValue(alternate, alternate);
2936  }
2937  });
2938 }
2939 
2941  for (int index = 0; index < Size(); ++index) {
2942  if (!Contains(index)) {
2943  SetValue(index, index);
2944  }
2945  }
2946 }
2947 
2948 // CheapestInsertionFilteredHeuristic
2949 
2951  RoutingModel* model, std::function<int64(int64, int64, int64)> evaluator,
2952  std::function<int64(int64)> penalty_evaluator,
2953  LocalSearchFilterManager* filter_manager)
2954  : RoutingFilteredHeuristic(model, filter_manager),
2955  evaluator_(std::move(evaluator)),
2956  penalty_evaluator_(std::move(penalty_evaluator)) {}
2957 
2958 std::vector<std::vector<CheapestInsertionFilteredHeuristic::StartEndValue>>
2960  const std::vector<int>& vehicles) {
2961  std::vector<std::vector<StartEndValue>> start_end_distances_per_node(
2962  model()->Size());
2963 
2964  for (int node = 0; node < model()->Size(); node++) {
2965  if (Contains(node)) continue;
2966  std::vector<StartEndValue>& start_end_distances =
2967  start_end_distances_per_node[node];
2968 
2969  for (const int vehicle : vehicles) {
2970  const int64 start = model()->Start(vehicle);
2971  const int64 end = model()->End(vehicle);
2972 
2973  // We compute the distance of node to the start/end nodes of the route.
2974  const int64 distance =
2975  CapAdd(model()->GetArcCostForVehicle(start, node, vehicle),
2976  model()->GetArcCostForVehicle(node, end, vehicle));
2977  start_end_distances.push_back({distance, vehicle});
2978  }
2979  // Sort the distances for the node to all start/ends of available vehicles
2980  // in decreasing order.
2981  std::sort(start_end_distances.begin(), start_end_distances.end(),
2982  [](const StartEndValue& first, const StartEndValue& second) {
2983  return second < first;
2984  });
2985  }
2986  return start_end_distances_per_node;
2987 }
2988 
2989 template <class Queue>
2991  std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
2992  Queue* priority_queue) {
2993  const int num_nodes = model()->Size();
2994  DCHECK_EQ(start_end_distances_per_node->size(), num_nodes);
2995 
2996  for (int node = 0; node < num_nodes; node++) {
2997  if (Contains(node)) continue;
2998  std::vector<StartEndValue>& start_end_distances =
2999  (*start_end_distances_per_node)[node];
3000  if (start_end_distances.empty()) {
3001  continue;
3002  }
3003  // Put the best StartEndValue for this node in the priority queue.
3004  const StartEndValue& start_end_value = start_end_distances.back();
3005  priority_queue->push(std::make_pair(start_end_value, node));
3006  start_end_distances.pop_back();
3007  }
3008 }
3009 
3011  int64 predecessor,
3012  int64 successor) {
3013  SetValue(predecessor, node);
3014  SetValue(node, successor);
3016 }
3017 
3019  int64 node_to_insert, int64 start, int64 next_after_start, int64 vehicle,
3020  std::vector<ValuedPosition>* valued_positions) {
3021  CHECK(valued_positions != nullptr);
3022  int64 insert_after = start;
3023  while (!model()->IsEnd(insert_after)) {
3024  const int64 insert_before =
3025  (insert_after == start) ? next_after_start : Value(insert_after);
3026  valued_positions->push_back(std::make_pair(
3027  GetInsertionCostForNodeAtPosition(node_to_insert, insert_after,
3028  insert_before, vehicle),
3029  insert_after));
3030  insert_after = insert_before;
3031  }
3032 }
3033 
3035  int64 node_to_insert, int64 insert_after, int64 insert_before,
3036  int vehicle) const {
3037  return CapSub(CapAdd(evaluator_(insert_after, node_to_insert, vehicle),
3038  evaluator_(node_to_insert, insert_before, vehicle)),
3039  evaluator_(insert_after, insert_before, vehicle));
3040 }
3041 
3043  int64 node_to_insert) const {
3044  if (penalty_evaluator_ != nullptr) {
3045  return penalty_evaluator_(node_to_insert);
3046  }
3047  return kint64max;
3048 }
3049 
3050 namespace {
3051 template <class T>
3052 void SortAndExtractPairSeconds(std::vector<std::pair<int64, T>>* pairs,
3053  std::vector<T>* sorted_seconds) {
3054  CHECK(pairs != nullptr);
3055  CHECK(sorted_seconds != nullptr);
3056  std::sort(pairs->begin(), pairs->end());
3057  sorted_seconds->reserve(pairs->size());
3058  for (const std::pair<int64, T>& p : *pairs) {
3059  sorted_seconds->push_back(p.second);
3060  }
3061 }
3062 } // namespace
3063 
3064 // Priority queue entries used by global cheapest insertion heuristic.
3065 
3066 // Entry in priority queue containing the insertion positions of a node pair.
3068  public:
3071  : heap_index_(-1),
3072  value_(kint64max),
3073  pickup_to_insert_(pickup_to_insert),
3074  pickup_insert_after_(pickup_insert_after),
3075  delivery_to_insert_(delivery_to_insert),
3076  delivery_insert_after_(delivery_insert_after),
3077  vehicle_(vehicle) {}
3078  // Note: for compatibility reasons, comparator follows tie-breaking rules used
3079  // in the first version of GlobalCheapestInsertion.
3080  bool operator<(const PairEntry& other) const {
3081  // We first compare by value, then we favor insertions (vehicle != -1).
3082  // The rest of the tie-breaking is done with std::tie.
3083  if (value_ != other.value_) {
3084  return value_ > other.value_;
3085  }
3086  if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
3087  return vehicle_ == -1;
3088  }
3089  return std::tie(pickup_insert_after_, pickup_to_insert_,
3090  delivery_insert_after_, delivery_to_insert_, vehicle_) >
3091  std::tie(other.pickup_insert_after_, other.pickup_to_insert_,
3092  other.delivery_insert_after_, other.delivery_to_insert_,
3093  other.vehicle_);
3094  }
3095  void SetHeapIndex(int h) { heap_index_ = h; }
3096  int GetHeapIndex() const { return heap_index_; }
3097  int64 value() const { return value_; }
3098  void set_value(int64 value) { value_ = value; }
3099  int pickup_to_insert() const { return pickup_to_insert_; }
3100  int pickup_insert_after() const { return pickup_insert_after_; }
3101  int delivery_to_insert() const { return delivery_to_insert_; }
3102  int delivery_insert_after() const { return delivery_insert_after_; }
3103  int vehicle() const { return vehicle_; }
3104 
3105  private:
3106  int heap_index_;
3107  int64 value_;
3108  const int pickup_to_insert_;
3109  const int pickup_insert_after_;
3110  const int delivery_to_insert_;
3111  const int delivery_insert_after_;
3112  const int vehicle_;
3113 };
3114 
3115 // Entry in priority queue containing the insertion position of a node.
3117  public:
3119  : heap_index_(-1),
3120  value_(kint64max),
3121  node_to_insert_(node_to_insert),
3122  insert_after_(insert_after),
3123  vehicle_(vehicle) {}
3124  bool operator<(const NodeEntry& other) const {
3125  // See PairEntry::operator<(), above. This one is similar.
3126  if (value_ != other.value_) {
3127  return value_ > other.value_;
3128  }
3129  if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
3130  return vehicle_ == -1;
3131  }
3132  return std::tie(insert_after_, node_to_insert_, vehicle_) >
3133  std::tie(other.insert_after_, other.node_to_insert_, other.vehicle_);
3134  }
3135  void SetHeapIndex(int h) { heap_index_ = h; }
3136  int GetHeapIndex() const { return heap_index_; }
3137  int64 value() const { return value_; }
3138  void set_value(int64 value) { value_ = value; }
3139  int node_to_insert() const { return node_to_insert_; }
3140  int insert_after() const { return insert_after_; }
3141  int vehicle() const { return vehicle_; }
3142 
3143  private:
3144  int heap_index_;
3145  int64 value_;
3146  const int node_to_insert_;
3147  const int insert_after_;
3148  const int vehicle_;
3149 };
3150 
3151 // GlobalCheapestInsertionFilteredHeuristic
3152 
3156  std::function<int64(int64, int64, int64)> evaluator,
3157  std::function<int64(int64)> penalty_evaluator,
3158  LocalSearchFilterManager* filter_manager,
3160  : CheapestInsertionFilteredHeuristic(model, std::move(evaluator),
3161  std::move(penalty_evaluator),
3162  filter_manager),
3163  gci_params_(parameters),
3164  node_index_to_vehicle_(model->Size(), -1) {
3165  CHECK_GT(gci_params_.neighbors_ratio, 0);
3166  CHECK_LE(gci_params_.neighbors_ratio, 1);
3167 
3168  const int64 size = model->Size();
3169  const int64 num_neighbors = std::max(1.0, gci_params_.neighbors_ratio * size);
3170 
3171  if (num_neighbors >= size - 1) {
3172  // All nodes are neighbors, so we set the neighbors_ratio to 1 to avoid
3173  // unnecessary computations in the code.
3174  gci_params_.neighbors_ratio = 1;
3175  }
3176 
3177  if (gci_params_.neighbors_ratio == 1) {
3178  gci_params_.use_neighbors_ratio_for_initialization = false;
3179  for (int64 node = 0; node < size; node++) {
3180  if (!model->GetPickupIndexPairs(node).empty()) {
3181  pickup_nodes_.push_back(node);
3182  } else if (!model->GetDeliveryIndexPairs(node).empty()) {
3183  delivery_nodes_.push_back(node);
3184  } else {
3185  single_nodes_.push_back(node);
3186  }
3187  }
3188  }
3189 }
3190 
3191 void GlobalCheapestInsertionFilteredHeuristic::ComputeNeighborhoods() {
3192  if (gci_params_.neighbors_ratio == 1) {
3193  // Neighborhood computations not needed.
3194  return;
3195  }
3196  if (!node_index_to_single_neighbors_by_cost_class_.empty()) {
3197  // Neigborhoods already computed.
3198  DCHECK(!node_index_to_pickup_neighbors_by_cost_class_.empty());
3199  DCHECK(!node_index_to_delivery_neighbors_by_cost_class_.empty());
3200  return;
3201  }
3202 
3203  // TODO(user): Refactor the neighborhood computations in RoutingModel.
3204  const RoutingModel& routing_model = *model();
3205  const int64 size = routing_model.Size();
3206  const int64 num_neighbors = std::max(1.0, gci_params_.neighbors_ratio * size);
3207  // If num_neighbors was greater or equal size - 1, gci_params_.neighbors_ratio
3208  // should have been set to 1.
3209  DCHECK_LT(num_neighbors, size - 1);
3210 
3211  node_index_to_single_neighbors_by_cost_class_.resize(size);
3212  node_index_to_pickup_neighbors_by_cost_class_.resize(size);
3213  node_index_to_delivery_neighbors_by_cost_class_.resize(size);
3214  const int num_cost_classes = routing_model.GetCostClassesCount();
3215  for (int64 node_index = 0; node_index < size; node_index++) {
3216  node_index_to_single_neighbors_by_cost_class_[node_index].resize(
3217  num_cost_classes);
3218  node_index_to_pickup_neighbors_by_cost_class_[node_index].resize(
3219  num_cost_classes);
3220  node_index_to_delivery_neighbors_by_cost_class_[node_index].resize(
3221  num_cost_classes);
3222  for (int cc = 0; cc < num_cost_classes; cc++) {
3223  node_index_to_single_neighbors_by_cost_class_[node_index][cc] =
3224  absl::make_unique<SparseBitset<int64>>(size);
3225  node_index_to_pickup_neighbors_by_cost_class_[node_index][cc] =
3226  absl::make_unique<SparseBitset<int64>>(size);
3227  node_index_to_delivery_neighbors_by_cost_class_[node_index][cc] =
3228  absl::make_unique<SparseBitset<int64>>(size);
3229  }
3230  }
3231 
3232  for (int64 node_index = 0; node_index < size; ++node_index) {
3233  DCHECK(!routing_model.IsEnd(node_index));
3234  const bool node_is_pickup =
3235  !routing_model.GetPickupIndexPairs(node_index).empty();
3236  const bool node_is_delivery =
3237  !routing_model.GetDeliveryIndexPairs(node_index).empty();
3238 
3239  // TODO(user): Use the model's IndexNeighborFinder when available.
3240  for (int cost_class = 0; cost_class < num_cost_classes; cost_class++) {
3241  if (!routing_model.HasVehicleWithCostClassIndex(
3242  RoutingCostClassIndex(cost_class))) {
3243  // No vehicle with this cost class, avoid unnecessary computations.
3244  continue;
3245  }
3246  std::vector<std::pair</*cost*/ int64, /*node*/ int64>> costed_after_nodes;
3247  costed_after_nodes.reserve(size);
3248  for (int after_node = 0; after_node < size; ++after_node) {
3249  if (after_node != node_index) {
3250  costed_after_nodes.push_back(
3251  std::make_pair(routing_model.GetArcCostForClass(
3252  node_index, after_node, cost_class),
3253  after_node));
3254  }
3255  }
3256  std::nth_element(costed_after_nodes.begin(),
3257  costed_after_nodes.begin() + num_neighbors - 1,
3258  costed_after_nodes.end());
3259  costed_after_nodes.resize(num_neighbors);
3260 
3261  for (const auto& costed_neighbor : costed_after_nodes) {
3262  const int64 neighbor = costed_neighbor.second;
3263  AddNeighborForCostClass(
3264  cost_class, node_index, neighbor,
3265  !routing_model.GetPickupIndexPairs(neighbor).empty(),
3266  !routing_model.GetDeliveryIndexPairs(neighbor).empty());
3267 
3268  // Add reverse neighborhood.
3269  DCHECK(!routing_model.IsEnd(neighbor));
3270  AddNeighborForCostClass(cost_class, neighbor, node_index,
3271  node_is_pickup, node_is_delivery);
3272  }
3273  }
3274  }
3275 }
3276 
3277 void GlobalCheapestInsertionFilteredHeuristic::AddNeighborForCostClass(
3278  int cost_class, int64 node_index, int64 neighbor_index,
3279  bool neighbor_is_pickup, bool neighbor_is_delivery) {
3280  if (neighbor_is_pickup) {
3281  node_index_to_pickup_neighbors_by_cost_class_[node_index][cost_class]->Set(
3282  neighbor_index);
3283  }
3284  if (neighbor_is_delivery) {
3285  node_index_to_delivery_neighbors_by_cost_class_[node_index][cost_class]
3286  ->Set(neighbor_index);
3287  }
3288  if (!neighbor_is_pickup && !neighbor_is_delivery) {
3289  node_index_to_single_neighbors_by_cost_class_[node_index][cost_class]->Set(
3290  neighbor_index);
3291  }
3292 }
3293 
3294 bool GlobalCheapestInsertionFilteredHeuristic::IsNeighborForCostClass(
3295  int cost_class, int64 node_index, int64 neighbor_index) const {
3296  if (gci_params_.neighbors_ratio == 1) {
3297  return true;
3298  }
3299  const SparseBitset<int64>* neighbors;
3300  if (!model()->GetPickupIndexPairs(neighbor_index).empty()) {
3301  neighbors =
3302  node_index_to_pickup_neighbors_by_cost_class_[node_index][cost_class]
3303  .get();
3304  } else if (!model()->GetDeliveryIndexPairs(neighbor_index).empty()) {
3305  neighbors =
3306  node_index_to_delivery_neighbors_by_cost_class_[node_index][cost_class]
3307  .get();
3308  } else {
3309  neighbors =
3310  node_index_to_single_neighbors_by_cost_class_[node_index][cost_class]
3311  .get();
3312  }
3313  return (*neighbors)[neighbor_index];
3314 }
3315 
3316 bool GlobalCheapestInsertionFilteredHeuristic::CheckVehicleIndices() const {
3317  std::vector<bool> node_is_visited(model()->Size(), -1);
3318  for (int v = 0; v < model()->vehicles(); v++) {
3319  for (int node = model()->Start(v); !model()->IsEnd(node);
3320  node = Value(node)) {
3321  if (node_index_to_vehicle_[node] != v) {
3322  return false;
3323  }
3324  node_is_visited[node] = true;
3325  }
3326  }
3327 
3328  for (int node = 0; node < model()->Size(); node++) {
3329  if (!node_is_visited[node] && node_index_to_vehicle_[node] != -1) {
3330  return false;
3331  }
3332  }
3333 
3334  return true;
3335 }
3336 
3338  ComputeNeighborhoods();
3339  // Insert partially inserted pairs.
3340  absl::flat_hash_map<int, std::vector<int>> vehicle_to_pair_nodes;
3341  for (const RoutingModel::IndexPair& index_pair :
3342  model()->GetPickupAndDeliveryPairs()) {
3343  int pickup_vehicle = -1;
3344  for (int64 pickup : index_pair.first) {
3345  if (Contains(pickup)) {
3346  pickup_vehicle = node_index_to_vehicle_[pickup];
3347  break;
3348  }
3349  }
3350  int delivery_vehicle = -1;
3351  for (int64 delivery : index_pair.second) {
3352  if (Contains(delivery)) {
3353  delivery_vehicle = node_index_to_vehicle_[delivery];
3354  break;
3355  }
3356  }
3357  if (pickup_vehicle >= 0 && delivery_vehicle < 0) {
3358  std::vector<int>& pair_nodes = vehicle_to_pair_nodes[pickup_vehicle];
3359  for (int64 delivery : index_pair.second) {
3360  pair_nodes.push_back(delivery);
3361  }
3362  }
3363  if (pickup_vehicle < 0 && delivery_vehicle >= 0) {
3364  std::vector<int>& pair_nodes = vehicle_to_pair_nodes[delivery_vehicle];
3365  for (int64 pickup : index_pair.first) {
3366  pair_nodes.push_back(pickup);
3367  }
3368  }
3369  }
3370  for (const auto& vehicle_and_nodes : vehicle_to_pair_nodes) {
3371  InsertNodesOnRoutes(vehicle_and_nodes.second, {vehicle_and_nodes.first});
3372  }
3373 
3374  InsertNodesByRequirementTopologicalOrder();
3375 
3376  // TODO(user): Adapt the pair insertions to also support seed and
3377  // sequential insertion.
3378  InsertPairs();
3379  std::vector<int> nodes;
3380  for (int node = 0; node < model()->Size(); ++node) {
3381  if (!Contains(node)) {
3382  nodes.push_back(node);
3383  }
3384  }
3385  InsertFarthestNodesAsSeeds();
3386  if (gci_params_.is_sequential) {
3387  SequentialInsertNodes(nodes);
3388  } else {
3389  InsertNodesOnRoutes(nodes, {});
3390  }
3392  DCHECK(CheckVehicleIndices());
3393  return Commit();
3394 }
3395 
3396 void GlobalCheapestInsertionFilteredHeuristic::
3397  InsertNodesByRequirementTopologicalOrder() {
3398  for (int type : model()->GetTopologicallySortedVisitTypes()) {
3399  InsertNodesOnRoutes(model()->GetSingleNodesOfType(type), {});
3400  }
3401 }
3402 
3403 void GlobalCheapestInsertionFilteredHeuristic::InsertPairs() {
3404  AdjustablePriorityQueue<PairEntry> priority_queue;
3405  std::vector<PairEntries> pickup_to_entries;
3406  std::vector<PairEntries> delivery_to_entries;
3407  InitializePairPositions(&priority_queue, &pickup_to_entries,
3408  &delivery_to_entries);
3409  while (!priority_queue.IsEmpty()) {
3410  if (StopSearch()) {
3411  for (PairEntry* const entry : *priority_queue.Raw()) {
3412  delete entry;
3413  }
3414  return;
3415  }
3416  PairEntry* const entry = priority_queue.Top();
3417  if (Contains(entry->pickup_to_insert()) ||
3418  Contains(entry->delivery_to_insert())) {
3419  DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
3420  &delivery_to_entries);
3421  continue;
3422  }
3423 
3424  if (entry->vehicle() == -1) {
3425  // Pair is unperformed.
3426  SetValue(entry->pickup_to_insert(), entry->pickup_to_insert());
3427  SetValue(entry->delivery_to_insert(), entry->delivery_to_insert());
3428  if (!Commit()) {
3429  DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
3430  &delivery_to_entries);
3431  }
3432  continue;
3433  }
3434 
3435  // Pair is performed.
3436  const int64 pickup_insert_before = Value(entry->pickup_insert_after());
3437  InsertBetween(entry->pickup_to_insert(), entry->pickup_insert_after(),
3438  pickup_insert_before);
3439  const int64 delivery_insert_before =
3440  (entry->pickup_to_insert() == entry->delivery_insert_after())
3441  ? pickup_insert_before
3442  : Value(entry->delivery_insert_after());
3443  InsertBetween(entry->delivery_to_insert(), entry->delivery_insert_after(),
3444  delivery_insert_before);
3445  if (Commit()) {
3446  const int64 pickup_after = entry->pickup_insert_after();
3447  const int64 pickup = entry->pickup_to_insert();
3448  const int64 delivery_after = entry->delivery_insert_after();
3449  const int64 delivery = entry->delivery_to_insert();
3450  const int vehicle = entry->vehicle();
3451  UpdatePairPositions(vehicle, pickup_after, &priority_queue,
3452  &pickup_to_entries, &delivery_to_entries);
3453  UpdatePairPositions(vehicle, pickup, &priority_queue, &pickup_to_entries,
3454  &delivery_to_entries);
3455  UpdatePairPositions(vehicle, delivery, &priority_queue,
3456  &pickup_to_entries, &delivery_to_entries);
3457  if (pickup != delivery_after) {
3458  UpdatePairPositions(vehicle, delivery_after, &priority_queue,
3459  &pickup_to_entries, &delivery_to_entries);
3460  }
3461  SetVehicleIndex(pickup, vehicle);
3462  SetVehicleIndex(delivery, vehicle);
3463  } else {
3464  DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
3465  &delivery_to_entries);
3466  }
3467  }
3468 }
3469 
3470 void GlobalCheapestInsertionFilteredHeuristic::InsertNodesOnRoutes(
3471  const std::vector<int>& nodes, const absl::flat_hash_set<int>& vehicles) {
3472  AdjustablePriorityQueue<NodeEntry> priority_queue;
3473  std::vector<NodeEntries> position_to_node_entries;
3474  InitializePositions(nodes, &priority_queue, &position_to_node_entries,
3475  vehicles);
3476  const bool all_routes =
3477  vehicles.empty() || vehicles.size() == model()->vehicles();
3478 
3479  while (!priority_queue.IsEmpty()) {
3480  NodeEntry* const node_entry = priority_queue.Top();
3481  if (StopSearch()) {
3482  for (NodeEntry* const entry : *priority_queue.Raw()) {
3483  delete entry;
3484  }
3485  return;
3486  }
3487  const int64 node_to_insert = node_entry->node_to_insert();
3488  if (Contains(node_to_insert)) {
3489  DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
3490  continue;
3491  }
3492 
3493  if (node_entry->vehicle() == -1) {
3494  // Node is unperformed.
3495  if (all_routes) {
3496  // Make node unperformed.
3497  SetValue(node_to_insert, node_to_insert);
3498  if (!Commit()) {
3499  DeleteNodeEntry(node_entry, &priority_queue,
3500  &position_to_node_entries);
3501  }
3502  } else {
3503  DCHECK_EQ(node_entry->value(), 0);
3504  // In this case, all routes are not being considered simultaneously,
3505  // so we do not make nodes unperformed (they might be better performed
3506  // on some other route later).
3507  // Furthermore, since in this case the node penalty is necessarily
3508  // taken into account in the NodeEntry, the values "cost - penalty"
3509  // for all nodes are now positive for all remaining entries in the
3510  // priority queue, so we can empty the priority queue.
3511  DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
3512  while (!priority_queue.IsEmpty()) {
3513  NodeEntry* const to_delete = priority_queue.Top();
3514  DeleteNodeEntry(to_delete, &priority_queue,
3515  &position_to_node_entries);
3516  }
3517  }
3518  continue;
3519  }
3520 
3521  // Make node performed.
3522  const int64 insert_after = node_entry->insert_after();
3523  InsertBetween(node_to_insert, insert_after, Value(insert_after));
3524  if (Commit()) {
3525  const int vehicle = node_entry->vehicle();
3526  UpdatePositions(nodes, vehicle, node_to_insert, &priority_queue,
3527  &position_to_node_entries);
3528  UpdatePositions(nodes, vehicle, insert_after, &priority_queue,
3529  &position_to_node_entries);
3530  SetVehicleIndex(node_to_insert, vehicle);
3531  } else {
3532  DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
3533  }
3534  }
3535 }
3536 
3537 void GlobalCheapestInsertionFilteredHeuristic::SequentialInsertNodes(
3538  const std::vector<int>& nodes) {
3539  std::vector<bool> is_vehicle_used;
3540  absl::flat_hash_set<int> used_vehicles;
3541  std::vector<int> unused_vehicles;
3542 
3543  DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
3544  if (!used_vehicles.empty()) {
3545  InsertNodesOnRoutes(nodes, used_vehicles);
3546  }
3547 
3548  std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
3549  ComputeStartEndDistanceForVehicles(unused_vehicles);
3550  std::priority_queue<Seed, std::vector<Seed>, std::greater<Seed>>
3551  first_node_queue;
3552  InitializePriorityQueue(&start_end_distances_per_node, &first_node_queue);
3553 
3554  int vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
3555  &is_vehicle_used);
3556 
3557  while (vehicle >= 0) {
3558  InsertNodesOnRoutes(nodes, {vehicle});
3559  vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
3560  &is_vehicle_used);
3561  }
3562 }
3563 
3564 void GlobalCheapestInsertionFilteredHeuristic::DetectUsedVehicles(
3565  std::vector<bool>* is_vehicle_used, std::vector<int>* unused_vehicles,
3566  absl::flat_hash_set<int>* used_vehicles) {
3567  is_vehicle_used->clear();
3568  is_vehicle_used->resize(model()->vehicles());
3569 
3570  used_vehicles->clear();
3571  used_vehicles->reserve(model()->vehicles());
3572 
3573  unused_vehicles->clear();
3574  unused_vehicles->reserve(model()->vehicles());
3575 
3576  for (int vehicle = 0; vehicle < model()->vehicles(); vehicle++) {
3577  if (Value(model()->Start(vehicle)) != model()->End(vehicle)) {
3578  (*is_vehicle_used)[vehicle] = true;
3579  used_vehicles->insert(vehicle);
3580  } else {
3581  (*is_vehicle_used)[vehicle] = false;
3582  unused_vehicles->push_back(vehicle);
3583  }
3584  }
3585 }
3586 
3587 void GlobalCheapestInsertionFilteredHeuristic::InsertFarthestNodesAsSeeds() {
3588  if (gci_params_.farthest_seeds_ratio <= 0) return;
3589  // Insert at least 1 farthest Seed if the parameter is positive.
3590  const int num_seeds = static_cast<int>(
3591  std::ceil(gci_params_.farthest_seeds_ratio * model()->vehicles()));
3592 
3593  std::vector<bool> is_vehicle_used;
3594  absl::flat_hash_set<int> used_vehicles;
3595  std::vector<int> unused_vehicles;
3596  DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
3597  std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
3598  ComputeStartEndDistanceForVehicles(unused_vehicles);
3599 
3600  // Priority queue where the Seeds with a larger distance are given higher
3601  // priority.
3602  std::priority_queue<Seed> farthest_node_queue;
3603  InitializePriorityQueue(&start_end_distances_per_node, &farthest_node_queue);
3604 
3605  int inserted_seeds = 0;
3606  while (!farthest_node_queue.empty() && inserted_seeds < num_seeds) {
3607  InsertSeedNode(&start_end_distances_per_node, &farthest_node_queue,
3608  &is_vehicle_used);
3609  inserted_seeds++;
3610  }
3611 }
3612 
3613 template <class Queue>
3614 int GlobalCheapestInsertionFilteredHeuristic::InsertSeedNode(
3615  std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
3616  Queue* priority_queue, std::vector<bool>* is_vehicle_used) {
3617  while (!priority_queue->empty()) {
3618  if (StopSearch()) break;
3619  const Seed& seed = priority_queue->top();
3620 
3621  const int seed_node = seed.second;
3622  const int seed_vehicle = seed.first.vehicle;
3623 
3624  std::vector<StartEndValue>& other_start_end_values =
3625  (*start_end_distances_per_node)[seed_node];
3626 
3627  if (Contains(seed_node)) {
3628  // The node is already inserted, it is therefore no longer considered as
3629  // a potential seed.
3630  priority_queue->pop();
3631  other_start_end_values.clear();
3632  continue;
3633  }
3634  if (!(*is_vehicle_used)[seed_vehicle]) {
3635  // Try to insert this seed_node on this vehicle's route.
3636  const int64 start = model()->Start(seed_vehicle);
3637  const int64 end = model()->End(seed_vehicle);
3638  DCHECK_EQ(Value(start), end);
3639  InsertBetween(seed_node, start, end);
3640  if (Commit()) {
3641  priority_queue->pop();
3642  (*is_vehicle_used)[seed_vehicle] = true;
3643  other_start_end_values.clear();
3644  SetVehicleIndex(seed_node, seed_vehicle);
3645  return seed_vehicle;
3646  }
3647  }
3648  // Either the vehicle is already used, or the Commit() wasn't successful.
3649  // In both cases, we remove this Seed from the priority queue, and insert
3650  // the next StartEndValue from start_end_distances_per_node[seed_node]
3651  // in the priority queue.
3652  priority_queue->pop();
3653  if (!other_start_end_values.empty()) {
3654  const StartEndValue& next_seed_value = other_start_end_values.back();
3655  priority_queue->push(std::make_pair(next_seed_value, seed_node));
3656  other_start_end_values.pop_back();
3657  }
3658  }
3659  // No seed node was inserted.
3660  return -1;
3661 }
3662 
3663 void GlobalCheapestInsertionFilteredHeuristic::InitializePairPositions(
3665  GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
3666  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3667  pickup_to_entries,
3668  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3669  delivery_to_entries) {
3670  priority_queue->Clear();
3671  pickup_to_entries->clear();
3672  pickup_to_entries->resize(model()->Size());
3673  delivery_to_entries->clear();
3674  delivery_to_entries->resize(model()->Size());
3675  for (const RoutingModel::IndexPair& index_pair :
3676  model()->GetPickupAndDeliveryPairs()) {
3677  for (int64 pickup : index_pair.first) {
3678  for (int64 delivery : index_pair.second) {
3679  if (Contains(pickup) || Contains(delivery)) {
3680  continue;
3681  }
3682  int64 penalty =
3683  FLAGS_routing_shift_insertion_cost_by_penalty ? kint64max : 0;
3684  // Add insertion entry making pair unperformed. When the pair is part
3685  // of a disjunction we do not try to make any of its pairs unperformed
3686  // as it requires having an entry with all pairs being unperformed.
3687  // TODO(user): Adapt the code to make pair disjunctions unperformed.
3688  if (index_pair.first.size() == 1 && index_pair.second.size() == 1) {
3689  const int64 pickup_penalty = GetUnperformedValue(pickup);
3690  const int64 delivery_penalty = GetUnperformedValue(delivery);
3691  if (pickup_penalty != kint64max && delivery_penalty != kint64max) {
3692  PairEntry* const entry =
3693  new PairEntry(pickup, -1, delivery, -1, -1);
3694  if (FLAGS_routing_shift_insertion_cost_by_penalty) {
3695  entry->set_value(0);
3696  penalty = CapAdd(pickup_penalty, delivery_penalty);
3697  } else {
3698  entry->set_value(CapAdd(pickup_penalty, delivery_penalty));
3699  penalty = 0;
3700  }
3701  priority_queue->Add(entry);
3702  }
3703  }
3704  // Add all other insertion entries with pair performed.
3705  InitializeInsertionEntriesPerformingPair(
3706  pickup, delivery, penalty, priority_queue, pickup_to_entries,
3707  delivery_to_entries);
3708  }
3709  }
3710  }
3711 }
3712 
3713 void GlobalCheapestInsertionFilteredHeuristic::
3714  InitializeInsertionEntriesPerformingPair(
3715  int64 pickup, int64 delivery, int64 penalty,
3717  GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
3718  priority_queue,
3719  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3720  pickup_to_entries,
3721  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3722  delivery_to_entries) {
3723  if (!gci_params_.use_neighbors_ratio_for_initialization) {
3724  std::vector<std::pair<std::pair<int64, int>, std::pair<int64, int64>>>
3725  valued_positions;
3726  for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
3727  std::vector<ValuedPosition> valued_pickup_positions;
3728  const int64 start = model()->Start(vehicle);
3729  AppendEvaluatedPositionsAfter(pickup, start, Value(start), vehicle,
3730  &valued_pickup_positions);
3731  for (const ValuedPosition& valued_pickup_position :
3732  valued_pickup_positions) {
3733  const int64 pickup_position = valued_pickup_position.second;
3734  CHECK(!model()->IsEnd(pickup_position));
3735  std::vector<ValuedPosition> valued_delivery_positions;
3736  AppendEvaluatedPositionsAfter(delivery, pickup, Value(pickup_position),
3737  vehicle, &valued_delivery_positions);
3738  for (const ValuedPosition& valued_delivery_position :
3739  valued_delivery_positions) {
3740  valued_positions.push_back(std::make_pair(
3741  std::make_pair(CapAdd(valued_pickup_position.first,
3742  valued_delivery_position.first),
3743  vehicle),
3744  std::make_pair(pickup_position,
3745  valued_delivery_position.second)));
3746  }
3747  }
3748  }
3749  for (const std::pair<std::pair<int64, int>, std::pair<int64, int64>>&
3750  valued_position : valued_positions) {
3751  PairEntry* const entry = new PairEntry(
3752  pickup, valued_position.second.first, delivery,
3753  valued_position.second.second, valued_position.first.second);
3754  entry->set_value(CapSub(valued_position.first.first, penalty));
3755  pickup_to_entries->at(valued_position.second.first).insert(entry);
3756  DCHECK_NE(valued_position.second.first, valued_position.second.second);
3757  delivery_to_entries->at(valued_position.second.second).insert(entry);
3758  priority_queue->Add(entry);
3759  }
3760  return;
3761  }
3762 
3763  // We're only considering the closest neighbors as insertion positions for
3764  // the pickup/delivery pair.
3765  for (int cost_class = 0; cost_class < model()->GetCostClassesCount();
3766  cost_class++) {
3767  absl::flat_hash_set<std::pair<int64, int64>> existing_insertion_positions;
3768  // Explore the neighborhood of the pickup.
3769  for (const std::vector<int64>* const neighbors :
3770  GetNeighborsOfNodeForCostClass(cost_class, pickup)) {
3771  for (const int64 pickup_insert_after : *neighbors) {
3772  if (!Contains(pickup_insert_after)) {
3773  continue;
3774  }
3775  const int vehicle = node_index_to_vehicle_[pickup_insert_after];
3776  if (model()->GetCostClassIndexOfVehicle(vehicle).value() !=
3777  cost_class) {
3778  continue;
3779  }
3780 
3781  const int64 pickup_value = GetInsertionCostForNodeAtPosition(
3782  pickup, pickup_insert_after, Value(pickup_insert_after), vehicle);
3783  int64 delivery_insert_after = pickup;
3784  while (!model()->IsEnd(delivery_insert_after)) {
3785  const std::pair<int64, int64> insertion_position = {
3786  pickup_insert_after, delivery_insert_after};
3787  DCHECK(!gtl::ContainsKey(existing_insertion_positions,
3788  insertion_position));
3789  existing_insertion_positions.insert(insertion_position);
3790  PairEntry* const entry =
3791  new PairEntry(pickup, pickup_insert_after, delivery,
3792  delivery_insert_after, vehicle);
3793  pickup_to_entries->at(pickup_insert_after).insert(entry);
3794  delivery_to_entries->at(delivery_insert_after).insert(entry);
3795 
3796  const int64 delivery_insert_before =
3797  (delivery_insert_after == pickup) ? Value(pickup_insert_after)
3798  : Value(delivery_insert_after);
3799  const int64 delivery_value = GetInsertionCostForNodeAtPosition(
3800  delivery, delivery_insert_after, delivery_insert_before, vehicle);
3801  entry->set_value(
3802  CapSub(CapAdd(pickup_value, delivery_value), penalty));
3803  priority_queue->Add(entry);
3804  delivery_insert_after = delivery_insert_before;
3805  }
3806  }
3807  }
3808 
3809  // Explore the neighborhood of the delivery.
3810  for (const std::vector<int64>* const neighbors :
3811  GetNeighborsOfNodeForCostClass(cost_class, delivery)) {
3812  for (const int64 delivery_insert_after : *neighbors) {
3813  if (!Contains(delivery_insert_after)) {
3814  continue;
3815  }
3816  const int vehicle = node_index_to_vehicle_[delivery_insert_after];
3817  if (model()->GetCostClassIndexOfVehicle(vehicle).value() !=
3818  cost_class) {
3819  continue;
3820  }
3821 
3822  const int64 delivery_value = GetInsertionCostForNodeAtPosition(
3823  delivery, delivery_insert_after, Value(delivery_insert_after),
3824  vehicle);
3825  int64 pickup_insert_after = model()->Start(vehicle);
3826  while (pickup_insert_after != delivery_insert_after) {
3827  const int64 pickup_insert_before = Value(pickup_insert_after);
3828  if (gtl::ContainsKey(
3829  existing_insertion_positions,
3830  std::make_pair(pickup_insert_after, delivery_insert_after))) {
3831  pickup_insert_after = pickup_insert_before;
3832  continue;
3833  }
3834  PairEntry* const entry =
3835  new PairEntry(pickup, pickup_insert_after, delivery,
3836  delivery_insert_after, vehicle);
3837  pickup_to_entries->at(pickup_insert_after).insert(entry);
3838  delivery_to_entries->at(delivery_insert_after).insert(entry);
3839  const int64 pickup_value = GetInsertionCostForNodeAtPosition(
3840  pickup, pickup_insert_after, pickup_insert_before, vehicle);
3841  entry->set_value(
3842  CapSub(CapAdd(pickup_value, delivery_value), penalty));
3843  priority_queue->Add(entry);
3844  pickup_insert_after = pickup_insert_before;
3845  }
3846  }
3847  }
3848  }
3849 }
3850 
3851 void GlobalCheapestInsertionFilteredHeuristic::UpdatePickupPositions(
3852  int vehicle, int64 pickup_insert_after,
3854  GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
3855  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3856  pickup_to_entries,
3857  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3858  delivery_to_entries) {
3859  // First, remove entries which have already been inserted and keep track of
3860  // the entries which are being kept and must be updated.
3861  using Pair = std::pair<int64, int64>;
3862  using Insertion = std::pair<Pair, /*delivery_insert_after*/ int64>;
3863  absl::flat_hash_set<Insertion> existing_insertions;
3864  std::vector<PairEntry*> to_remove;
3865  for (PairEntry* const pair_entry :
3866  pickup_to_entries->at(pickup_insert_after)) {
3867  DCHECK(priority_queue->Contains(pair_entry));
3868  DCHECK_EQ(pair_entry->pickup_insert_after(), pickup_insert_after);
3869  if (Contains(pair_entry->pickup_to_insert()) ||
3870  Contains(pair_entry->delivery_to_insert())) {
3871  to_remove.push_back(pair_entry);
3872  } else {
3873  existing_insertions.insert(
3874  {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
3875  pair_entry->delivery_insert_after()});
3876  }
3877  }
3878  for (PairEntry* const pair_entry : to_remove) {
3879  DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
3880  delivery_to_entries);
3881  }
3882  // Create new entries for which the pickup is to be inserted after
3883  // pickup_insert_after.
3884  const int cost_class = model()->GetCostClassIndexOfVehicle(vehicle).value();
3885  const int64 pickup_insert_before = Value(pickup_insert_after);
3886  for (int64 pickup :
3887  GetPickupNeighborsOfNodeForCostClass(cost_class, pickup_insert_after)) {
3888  if (Contains(pickup)) {
3889  continue;
3890  }
3891  for (const auto& pickup_index_pair : model()->GetPickupIndexPairs(pickup)) {
3892  const RoutingModel::IndexPair& index_pair =
3893  model()->GetPickupAndDeliveryPairs()[pickup_index_pair.first];
3894  for (int64 delivery : index_pair.second) {
3895  if (Contains(delivery)) {
3896  continue;
3897  }
3898  int64 delivery_insert_after = pickup;
3899  while (!model()->IsEnd(delivery_insert_after)) {
3900  const std::pair<Pair, int64> insertion = {{pickup, delivery},
3901  delivery_insert_after};
3902  if (!gtl::ContainsKey(existing_insertions, insertion)) {
3903  PairEntry* const entry =
3904  new PairEntry(pickup, pickup_insert_after, delivery,
3905  delivery_insert_after, vehicle);
3906  pickup_to_entries->at(pickup_insert_after).insert(entry);
3907  delivery_to_entries->at(delivery_insert_after).insert(entry);
3908  }
3909  if (delivery_insert_after == pickup) {
3910  delivery_insert_after = pickup_insert_before;
3911  } else {
3912  delivery_insert_after = Value(delivery_insert_after);
3913  }
3914  }
3915  }
3916  }
3917  }
3918  // Compute new value of entries and either update the priority queue
3919  // accordingly if the entry already existed or add it to the queue if it's
3920  // new.
3921  for (PairEntry* const pair_entry :
3922  pickup_to_entries->at(pickup_insert_after)) {
3923  const int64 pickup = pair_entry->pickup_to_insert();
3924  const int64 delivery = pair_entry->delivery_to_insert();
3925  DCHECK_EQ(pickup_insert_after, pair_entry->pickup_insert_after());
3926  const int64 pickup_value = GetInsertionCostForNodeAtPosition(
3927  pickup, pickup_insert_after, pickup_insert_before, vehicle);
3928  const int64 delivery_insert_after = pair_entry->delivery_insert_after();
3929  const int64 delivery_insert_before = (delivery_insert_after == pickup)
3930  ? pickup_insert_before
3931  : Value(delivery_insert_after);
3932  const int64 delivery_value = GetInsertionCostForNodeAtPosition(
3933  delivery, delivery_insert_after, delivery_insert_before, vehicle);
3934  const int64 penalty =
3935  FLAGS_routing_shift_insertion_cost_by_penalty
3936  ? CapAdd(GetUnperformedValue(pickup), GetUnperformedValue(delivery))
3937  : 0;
3938  pair_entry->set_value(
3939  CapSub(CapAdd(pickup_value, delivery_value), penalty));
3940  if (priority_queue->Contains(pair_entry)) {
3941  priority_queue->NoteChangedPriority(pair_entry);
3942  } else {
3943  priority_queue->Add(pair_entry);
3944  }
3945  }
3946 }
3947 
3948 void GlobalCheapestInsertionFilteredHeuristic::UpdateDeliveryPositions(
3949  int vehicle, int64 delivery_insert_after,
3951  GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
3952  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3953  pickup_to_entries,
3954  std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3955  delivery_to_entries) {
3956  // First, remove entries which have already been inserted and keep track of
3957  // the entries which are being kept and must be updated.
3958  using Pair = std::pair<int64, int64>;
3959  using Insertion = std::pair<Pair, /*pickup_insert_after*/ int64>;
3960  absl::flat_hash_set<Insertion> existing_insertions;
3961  std::vector<PairEntry*> to_remove;
3962  for (PairEntry* const pair_entry :
3963  delivery_to_entries->at(delivery_insert_after)) {
3964  DCHECK(priority_queue->Contains(pair_entry));
3965  DCHECK_EQ(pair_entry->delivery_insert_after(), delivery_insert_after);
3966  if (Contains(pair_entry->pickup_to_insert()) ||
3967  Contains(pair_entry->delivery_to_insert())) {
3968  to_remove.push_back(pair_entry);
3969  } else {
3970  existing_insertions.insert(
3971  {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
3972  pair_entry->pickup_insert_after()});
3973  }
3974  }
3975  for (PairEntry* const pair_entry : to_remove) {
3976  DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
3977  delivery_to_entries);
3978  }
3979  // Create new entries for which the delivery is to be inserted after
3980  // delivery_insert_after.
3981  const int cost_class = model()->GetCostClassIndexOfVehicle(vehicle).value();
3982  const int64 delivery_insert_before = Value(delivery_insert_after);
3983  for (int64 delivery : GetDeliveryNeighborsOfNodeForCostClass(
3984  cost_class, delivery_insert_after)) {
3985  if (Contains(delivery)) {
3986  continue;
3987  }
3988  for (const auto& delivery_index_pair :
3989  model()->GetDeliveryIndexPairs(delivery)) {
3990  const RoutingModel::IndexPair& index_pair =
3991  model()->GetPickupAndDeliveryPairs()[delivery_index_pair.first];
3992  for (int64 pickup : index_pair.first) {
3993  if (Contains(pickup)) {
3994  continue;
3995  }
3996  int64 pickup_insert_after = model()->Start(vehicle);
3997  while (pickup_insert_after != delivery_insert_after) {
3998  std::pair<Pair, int64> insertion = {{pickup, delivery},
3999  pickup_insert_after};
4000  if (!gtl::ContainsKey(existing_insertions, insertion)) {
4001  PairEntry* const entry =
4002  new PairEntry(pickup, pickup_insert_after, delivery,
4003  delivery_insert_after, vehicle);
4004  pickup_to_entries->at(pickup_insert_after).insert(entry);
4005  delivery_to_entries->at(delivery_insert_after).insert(entry);
4006  }
4007  pickup_insert_after = Value(pickup_insert_after);
4008  }
4009  }
4010  }
4011  }
4012  // Compute new value of entries and either update the priority queue
4013  // accordingly if the entry already existed or add it to the queue if it's
4014  // new.
4015  for (PairEntry* const pair_entry :
4016  delivery_to_entries->at(delivery_insert_after)) {
4017  const int64 pickup = pair_entry->pickup_to_insert();
4018  const int64 delivery = pair_entry->delivery_to_insert();
4019  DCHECK_EQ(delivery_insert_after, pair_entry->delivery_insert_after());
4020  const int64 pickup_insert_after = pair_entry->pickup_insert_after();
4021  const int64 pickup_value = GetInsertionCostForNodeAtPosition(
4022  pickup, pickup_insert_after, Value(pickup_insert_after), vehicle);
4023  const int64 delivery_value = GetInsertionCostForNodeAtPosition(
4024  delivery, delivery_insert_after, delivery_insert_before, vehicle);
4025  const int64 penalty =
4026  FLAGS_routing_shift_insertion_cost_by_penalty
4027  ? CapAdd(GetUnperformedValue(pickup), GetUnperformedValue(delivery))
4028  : 0;
4029  pair_entry->set_value(
4030  CapSub(CapAdd(pickup_value, delivery_value), penalty));
4031  if (priority_queue->Contains(pair_entry)) {
4032  priority_queue->NoteChangedPriority(pair_entry);
4033  } else {
4034  priority_queue->Add(pair_entry);
4035  }
4036  }
4037 }
4038 
4039 void GlobalCheapestInsertionFilteredHeuristic::DeletePairEntry(
4040  GlobalCheapestInsertionFilteredHeuristic::PairEntry* entry,
4042  GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
4043  std::vector<PairEntries>* pickup_to_entries,
4044  std::vector<PairEntries>* delivery_to_entries) {
4045  priority_queue->Remove(entry);
4046  if (entry->pickup_insert_after() != -1) {
4047  pickup_to_entries->at(entry->pickup_insert_after()).erase(entry);
4048  }
4049  if (entry->delivery_insert_after() != -1) {
4050  delivery_to_entries->at(entry->delivery_insert_after()).erase(entry);
4051  }
4052  delete entry;
4053 }
4054 
4055 void GlobalCheapestInsertionFilteredHeuristic::InitializePositions(
4056  const std::vector<int>& nodes,
4058  GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
4059  std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
4060  position_to_node_entries,
4061  const absl::flat_hash_set<int>& vehicles) {
4062  priority_queue->Clear();
4063  position_to_node_entries->clear();
4064  position_to_node_entries->resize(model()->Size());
4065 
4066  const int num_vehicles =
4067  vehicles.empty() ? model()->vehicles() : vehicles.size();
4068 
4069  for (int node : nodes) {
4070  if (Contains(node)) {
4071  continue;
4072  }
4073  const int64 node_penalty = GetUnperformedValue(node);
4074  int64 penalty =
4075  FLAGS_routing_shift_insertion_cost_by_penalty ? kint64max : 0;
4076  // Add insertion entry making node unperformed.
4077  if (node_penalty != kint64max) {
4078  NodeEntry* const node_entry = new NodeEntry(node, -1, -1);
4079  if (FLAGS_routing_shift_insertion_cost_by_penalty ||
4080  num_vehicles < model()->vehicles()) {
4081  // In the case where we're not considering all routes simultaneously,
4082  // always shift insertion costs by penalty.
4083  node_entry->set_value(0);
4084  penalty = node_penalty;
4085  } else {
4086  node_entry->set_value(node_penalty);
4087  penalty = 0;
4088  }
4089  priority_queue->Add(node_entry);
4090  }
4091  // Add all insertion entries making node performed.
4092  InitializeInsertionEntriesPerformingNode(
4093  node, penalty, vehicles, priority_queue, position_to_node_entries);
4094  }
4095 }
4096 
4097 void GlobalCheapestInsertionFilteredHeuristic::
4098  InitializeInsertionEntriesPerformingNode(
4099  int64 node, int64 penalty, const absl::flat_hash_set<int>& vehicles,
4101  GlobalCheapestInsertionFilteredHeuristic::NodeEntry>*
4102  priority_queue,
4103  std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
4104  position_to_node_entries) {
4105  const int num_vehicles =
4106  vehicles.empty() ? model()->vehicles() : vehicles.size();
4107  if (!gci_params_.use_neighbors_ratio_for_initialization) {
4108  auto vehicles_it = vehicles.begin();
4109  for (int v = 0; v < num_vehicles; v++) {
4110  const int vehicle = vehicles.empty() ? v : *vehicles_it++;
4111 
4112  std::vector<ValuedPosition> valued_positions;
4113  const int64 start = model()->Start(vehicle);
4114  AppendEvaluatedPositionsAfter(node, start, Value(start), vehicle,
4115  &valued_positions);
4116  for (const std::pair<int64, int64>& valued_position : valued_positions) {
4117  NodeEntry* const node_entry =
4118  new NodeEntry(node, valued_position.second, vehicle);
4119  node_entry->set_value(CapSub(valued_position.first, penalty));
4120  position_to_node_entries->at(valued_position.second).insert(node_entry);
4121  priority_queue->Add(node_entry);
4122  }
4123  }
4124  return;
4125  }
4126 
4127  // We're only considering the closest neighbors as insertion positions for
4128  // the node.
4129  absl::flat_hash_set<int> vehicles_to_consider;
4130  const bool all_vehicles = (num_vehicles == model()->vehicles());
4131  const auto insert_on_vehicle_for_cost_class = [this, &vehicles, all_vehicles](
4132  int v, int cost_class) {
4133  return (model()->GetCostClassIndexOfVehicle(v).value() == cost_class) &&
4134  (all_vehicles || vehicles.contains(v));
4135  };
4136  for (int cost_class = 0; cost_class < model()->GetCostClassesCount();
4137  cost_class++) {
4138  for (const std::vector<int64>* const neighbors :
4139  GetNeighborsOfNodeForCostClass(cost_class, node)) {
4140  for (const int64 insert_after : *neighbors) {
4141  if (!Contains(insert_after)) {
4142  continue;
4143  }
4144  const int vehicle = node_index_to_vehicle_[insert_after];
4145  if (!insert_on_vehicle_for_cost_class(vehicle, cost_class)) {
4146  continue;
4147  }
4148  NodeEntry* const node_entry =
4149  new NodeEntry(node, insert_after, vehicle);
4150  node_entry->set_value(
4152  node, insert_after, Value(insert_after), vehicle),
4153  penalty));
4154  position_to_node_entries->at(insert_after).insert(node_entry);
4155  priority_queue->Add(node_entry);
4156  }
4157  }
4158  }
4159 }
4160 
4161 void GlobalCheapestInsertionFilteredHeuristic::UpdatePositions(
4162  const std::vector<int>& nodes, int vehicle, int64 insert_after,
4164  GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
4165  std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
4166  node_entries) {
4167  // Either create new entries if we are inserting after a newly inserted node
4168  // or remove entries which have already been inserted.
4169  bool update = true;
4170  if (node_entries->at(insert_after).empty()) {
4171  update = false;
4172  const int cost_class = model()->GetCostClassIndexOfVehicle(vehicle).value();
4173  for (int node_to_insert : nodes) {
4174  if (!Contains(node_to_insert) &&
4175  IsNeighborForCostClass(cost_class, insert_after, node_to_insert)) {
4176  NodeEntry* const node_entry =
4177  new NodeEntry(node_to_insert, insert_after, vehicle);
4178  node_entries->at(insert_after).insert(node_entry);
4179  }
4180  }
4181  } else {
4182  std::vector<NodeEntry*> to_remove;
4183  for (NodeEntry* const node_entry : node_entries->at(insert_after)) {
4184  if (priority_queue->Contains(node_entry)) {
4185  DCHECK_EQ(node_entry->insert_after(), insert_after);
4186  if (Contains(node_entry->node_to_insert())) {
4187  to_remove.push_back(node_entry);
4188  }
4189  }
4190  }
4191  for (NodeEntry* const node_entry : to_remove) {
4192  DeleteNodeEntry(node_entry, priority_queue, node_entries);
4193  }
4194  }
4195  // Compute new value of entries and either update the priority queue
4196  // accordingly if the entry already existed or add it to the queue if it's
4197  // new.
4198  DCHECK_GE(model()->Size(), node_entries->at(insert_after).size());
4199  const int64 insert_before = Value(insert_after);
4200  for (NodeEntry* const node_entry : node_entries->at(insert_after)) {
4201  DCHECK_EQ(node_entry->insert_after(), insert_after);
4203  node_entry->node_to_insert(), insert_after, insert_before, vehicle);
4204  const int64 penalty =
4205  FLAGS_routing_shift_insertion_cost_by_penalty
4206  ? GetUnperformedValue(node_entry->node_to_insert())
4207  : 0;
4208  node_entry->set_value(CapSub(value, penalty));
4209  if (update) {
4210  priority_queue->NoteChangedPriority(node_entry);
4211  } else {
4212  priority_queue->Add(node_entry);
4213  }
4214  }
4215 }
4216 
4217 void GlobalCheapestInsertionFilteredHeuristic::DeleteNodeEntry(
4218  GlobalCheapestInsertionFilteredHeuristic::NodeEntry* entry,
4220  GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
4221  std::vector<NodeEntries>* node_entries) {
4222  priority_queue->Remove(entry);
4223  if (entry->insert_after() != -1) {
4224  node_entries->at(entry->insert_after()).erase(entry);
4225  }
4226  delete entry;
4227 }
4228 
4229 // LocalCheapestInsertionFilteredHeuristic
4230 // TODO(user): Add support for penalty costs.
4234  std::function<int64(int64, int64, int64)> evaluator,
4235  LocalSearchFilterManager* filter_manager)
4236  : CheapestInsertionFilteredHeuristic(model, std::move(evaluator), nullptr,
4237  filter_manager) {
4238  std::vector<int> all_vehicles(model->vehicles());
4239  std::iota(std::begin(all_vehicles), std::end(all_vehicles), 0);
4240 
4241  start_end_distances_per_node_ =
4242  ComputeStartEndDistanceForVehicles(all_vehicles);
4243 }
4244 
4246  // Marking if we've tried inserting a node.
4247  std::vector<bool> visited(model()->Size(), false);
4248  // Possible positions where the current node can inserted.
4249  std::vector<int64> insertion_positions;
4250  // Possible positions where its associated delivery node can inserted (if the
4251  // current node has one).
4252  std::vector<int64> delivery_insertion_positions;
4253  // Iterating on pickup and delivery pairs
4254  const RoutingModel::IndexPairs& index_pairs =
4256  for (const auto& index_pair : index_pairs) {
4257  for (int64 pickup : index_pair.first) {
4258  if (Contains(pickup)) {
4259  continue;
4260  }
4261  for (int64 delivery : index_pair.second) {
4262  // If either is already in the solution, let it be inserted in the
4263  // standard node insertion loop.
4264  if (Contains(delivery)) {
4265  continue;
4266  }
4267  if (StopSearch()) return false;
4268  visited[pickup] = true;
4269  visited[delivery] = true;
4270  ComputeEvaluatorSortedPositions(pickup, &insertion_positions);
4271  for (const int64 pickup_insertion : insertion_positions) {
4272  const int pickup_insertion_next = Value(pickup_insertion);
4273  ComputeEvaluatorSortedPositionsOnRouteAfter(
4274  delivery, pickup, pickup_insertion_next,
4275  &delivery_insertion_positions);
4276  bool found = false;
4277  for (const int64 delivery_insertion : delivery_insertion_positions) {
4278  InsertBetween(pickup, pickup_insertion, pickup_insertion_next);
4279  const int64 delivery_insertion_next =
4280  (delivery_insertion == pickup_insertion) ? pickup
4281  : (delivery_insertion == pickup) ? pickup_insertion_next
4282  : Value(delivery_insertion);
4283  InsertBetween(delivery, delivery_insertion,
4284  delivery_insertion_next);
4285  if (Commit()) {
4286  found = true;
4287  break;
4288  }
4289  }
4290  if (found) {
4291  break;
4292  }
4293  }
4294  }
4295  }
4296  }
4297 
4298  std::priority_queue<Seed> node_queue;
4299  InitializePriorityQueue(&start_end_distances_per_node_, &node_queue);
4300 
4301  while (!node_queue.empty()) {
4302  const int node = node_queue.top().second;
4303  node_queue.pop();
4304  if (Contains(node) || visited[node]) continue;
4305  ComputeEvaluatorSortedPositions(node, &insertion_positions);
4306  for (const int64 insertion : insertion_positions) {
4307  if (StopSearch()) return false;
4308  InsertBetween(node, insertion, Value(insertion));
4309  if (Commit()) {
4310  break;
4311  }
4312  }
4313  }
4315  return Commit();
4316 }
4317 
4318 void LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPositions(
4319  int64 node, std::vector<int64>* sorted_positions) {
4320  CHECK(sorted_positions != nullptr);
4321  CHECK(!Contains(node));
4322  sorted_positions->clear();
4323  const int size = model()->Size();
4324  if (node < size) {
4325  std::vector<std::pair<int64, int64>> valued_positions;
4326  for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
4327  const int64 start = model()->Start(vehicle);
4328  AppendEvaluatedPositionsAfter(node, start, Value(start), vehicle,
4329  &valued_positions);
4330  }
4331  SortAndExtractPairSeconds(&valued_positions, sorted_positions);
4332  }
4333 }
4334 
4335 void LocalCheapestInsertionFilteredHeuristic::
4336  ComputeEvaluatorSortedPositionsOnRouteAfter(
4337  int64 node, int64 start, int64 next_after_start,
4338  std::vector<int64>* sorted_positions) {
4339  CHECK(sorted_positions != nullptr);
4340  CHECK(!Contains(node));
4341  sorted_positions->clear();
4342  const int size = model()->Size();
4343  if (node < size) {
4344  // TODO(user): Take vehicle into account.
4345  std::vector<std::pair<int64, int64>> valued_positions;
4346  AppendEvaluatedPositionsAfter(node, start, next_after_start, 0,
4347  &valued_positions);
4348  SortAndExtractPairSeconds(&valued_positions, sorted_positions);
4349  }
4350 }
4351 
4352 // CheapestAdditionFilteredHeuristic
4353 
4355  RoutingModel* model, LocalSearchFilterManager* filter_manager)
4356  : RoutingFilteredHeuristic(model, filter_manager) {}
4357 
4359  const int kUnassigned = -1;
4361  std::vector<std::vector<int64>> deliveries(Size());
4362  std::vector<std::vector<int64>> pickups(Size());
4363  for (const RoutingModel::IndexPair& pair : pairs) {
4364  for (int first : pair.first) {
4365  for (int second : pair.second) {
4366  deliveries[first].push_back(second);
4367  pickups[second].push_back(first);
4368  }
4369  }
4370  }
4371  // To mimic the behavior of PathSelector (cf. search.cc), iterating on
4372  // routes with partial route at their start first then on routes with largest
4373  // index.
4374  std::vector<int> sorted_vehicles(model()->vehicles(), 0);
4375  for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
4376  sorted_vehicles[vehicle] = vehicle;
4377  }
4378  std::sort(sorted_vehicles.begin(), sorted_vehicles.end(),
4379  PartialRoutesAndLargeVehicleIndicesFirst(*this));
4380  // Neighbors of the node currently being extended.
4381  for (const int vehicle : sorted_vehicles) {
4382  int64 last_node = GetStartChainEnd(vehicle);
4383  bool extend_route = true;
4384  // Extend the route of the current vehicle while it's possible. We can
4385  // iterate more than once if pickup and delivery pairs have been inserted
4386  // in the last iteration (see comment below); the new iteration will try to
4387  // extend the route after the last delivery on the route.
4388  while (extend_route) {
4389  extend_route = false;
4390  bool found = true;
4391  int64 index = last_node;
4392  int64 end = GetEndChainStart(vehicle);
4393  // Extend the route until either the end node of the vehicle is reached
4394  // or no node or node pair can be added. Deliveries in pickup and
4395  // delivery pairs are added at the same time as pickups, at the end of the
4396  // route, in reverse order of the pickups. Deliveries are never added
4397  // alone.
4398  while (found && !model()->IsEnd(index)) {
4399  found = false;
4400  std::vector<int64> neighbors;
4401  if (index < model()->Nexts().size()) {
4402  std::unique_ptr<IntVarIterator> it(
4403  model()->Nexts()[index]->MakeDomainIterator(false));
4404  auto next_values = InitAndGetValues(it.get());
4405  neighbors = GetPossibleNextsFromIterator(index, next_values.begin(),
4406  next_values.end());
4407  }
4408  for (int i = 0; !found && i < neighbors.size(); ++i) {
4409  int64 next = -1;
4410  switch (i) {
4411  case 0:
4412  next = FindTopSuccessor(index, neighbors);
4413  break;
4414  case 1:
4415  SortSuccessors(index, &neighbors);
4416  ABSL_FALLTHROUGH_INTENDED;
4417  default:
4418  next = neighbors[i];
4419  }
4420  if (model()->IsEnd(next) && next != end) {
4421  continue;
4422  }
4423  // Only add a delivery if one of its pickups has been added already.
4424  if (!model()->IsEnd(next) && !pickups[next].empty()) {
4425  bool contains_pickups = false;
4426  for (int64 pickup : pickups[next]) {
4427  if (Contains(pickup)) {
4428  contains_pickups = true;
4429  break;
4430  }
4431  }
4432  if (!contains_pickups) {
4433  continue;
4434  }
4435  }
4436  std::vector<int64> next_deliveries;
4437  if (next < deliveries.size()) {
4438  next_deliveries = GetPossibleNextsFromIterator(
4439  next, deliveries[next].begin(), deliveries[next].end());
4440  }
4441  if (next_deliveries.empty()) next_deliveries = {kUnassigned};
4442  for (int j = 0; !found && j < next_deliveries.size(); ++j) {
4443  if (StopSearch()) return false;
4444  int delivery = -1;
4445  switch (j) {
4446  case 0:
4447  delivery = FindTopSuccessor(next, next_deliveries);
4448  break;
4449  case 1:
4450  SortSuccessors(next, &next_deliveries);
4451  ABSL_FALLTHROUGH_INTENDED;
4452  default:
4453  delivery = next_deliveries[j];
4454  }
4455  // Insert "next" after "index", and before "end" if it is not the
4456  // end already.
4457  SetValue(index, next);
4458  if (!model()->IsEnd(next)) {
4459  SetValue(next, end);
4461  if (delivery != kUnassigned) {
4462  SetValue(next, delivery);
4463  SetValue(delivery, end);
4465  }
4466  }
4467  if (Commit()) {
4468  index = next;
4469  found = true;
4470  if (delivery != kUnassigned) {
4471  if (model()->IsEnd(end) && last_node != delivery) {
4472  last_node = delivery;
4473  extend_route = true;
4474  }
4475  end = delivery;
4476  }
4477  break;
4478  }
4479  }
4480  }
4481  }
4482  }
4483  }
4485  return Commit();
4486 }
4487 
4488 bool CheapestAdditionFilteredHeuristic::
4489  PartialRoutesAndLargeVehicleIndicesFirst::operator()(int vehicle1,
4490  int vehicle2) const {
4491  const bool has_partial_route1 = (builder_.model()->Start(vehicle1) !=
4492  builder_.GetStartChainEnd(vehicle1));
4493  const bool has_partial_route2 = (builder_.model()->Start(vehicle2) !=
4494  builder_.GetStartChainEnd(vehicle2));
4495  if (has_partial_route1 == has_partial_route2) {
4496  return vehicle2 < vehicle1;
4497  } else {
4498  return has_partial_route2 < has_partial_route1;
4499  }
4500 }
4501 
4502 // EvaluatorCheapestAdditionFilteredHeuristic
4503 
4506  RoutingModel* model, std::function<int64(int64, int64)> evaluator,
4507  LocalSearchFilterManager* filter_manager)
4508  : CheapestAdditionFilteredHeuristic(model, filter_manager),
4509  evaluator_(std::move(evaluator)) {}
4510 
4511 int64 EvaluatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
4512  int64 node, const std::vector<int64>& successors) {
4513  int64 best_evaluation = kint64max;
4514  int64 best_successor = -1;
4515  for (int64 successor : successors) {
4516  const int64 evaluation =
4517  (successor >= 0) ? evaluator_(node, successor) : kint64max;
4518  if (evaluation < best_evaluation ||
4519  (evaluation == best_evaluation && successor > best_successor)) {
4520  best_evaluation = evaluation;
4521  best_successor = successor;
4522  }
4523  }
4524  return best_successor;
4525 }
4526 
4527 void EvaluatorCheapestAdditionFilteredHeuristic::SortSuccessors(
4528  int64 node, std::vector<int64>* successors) {
4529  std::vector<std::pair<int64, int64>> values;
4530  values.reserve(successors->size());
4531  for (int64 successor : *successors) {
4532  // Tie-breaking on largest node index to mimic the behavior of
4533  // CheapestValueSelector (search.cc).
4534  values.push_back({evaluator_(node, successor), -successor});
4535  }
4536  std::sort(values.begin(), values.end());
4537  successors->clear();
4538  for (auto value : values) {
4539  successors->push_back(-value.second);
4540  }
4541 }
4542 
4543 // ComparatorCheapestAdditionFilteredHeuristic
4544 
4547  RoutingModel* model, Solver::VariableValueComparator comparator,
4548  LocalSearchFilterManager* filter_manager)
4549  : CheapestAdditionFilteredHeuristic(model, filter_manager),
4550  comparator_(std::move(comparator)) {}
4551 
4552 int64 ComparatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
4553  int64 node, const std::vector<int64>& successors) {
4554  return *std::min_element(successors.begin(), successors.end(),
4555  [this, node](int successor1, int successor2) {
4556  return comparator_(node, successor1, successor2);
4557  });
4558 }
4559 
4560 void ComparatorCheapestAdditionFilteredHeuristic::SortSuccessors(
4561  int64 node, std::vector<int64>* successors) {
4562  std::sort(successors->begin(), successors->end(),
4563  [this, node](int successor1, int successor2) {
4564  return comparator_(node, successor1, successor2);
4565  });
4566 }
4567 
4568 // Class storing and allowing access to the savings according to the number of
4569 // vehicle types.
4570 // The savings are stored and sorted in sorted_savings_per_vehicle_type_.
4571 // Furthermore, when there is more than one vehicle type, the savings for a same
4572 // before-->after arc are sorted in costs_and_savings_per_arc_[arc] by
4573 // increasing cost(s-->before-->after-->e), where s and e are the start and end
4574 // of the route, in order to make sure the arc is served by the route with the
4575 // closest depot (start/end) possible.
4576 // When there is only one vehicle "type" (i.e. all vehicles have the same
4577 // start/end and cost class), each arc has a single saving value associated to
4578 // it, so we ignore this last step to avoid unnecessary computations, and only
4579 // work with sorted_savings_per_vehicle_type_[0].
4580 // In case of multiple vehicle types, the best savings for each arc, i.e. the
4581 // savings corresponding to the closest vehicle type, are inserted and sorted in
4582 // sorted_savings_.
4583 //
4584 // This class also handles skipped Savings:
4585 // The vectors skipped_savings_starting/ending_at_ contain all the Savings that
4586 // weren't added to the model, which we want to consider for later:
4587 // 1) When a Saving before-->after with both nodes uncontained cannot be used to
4588 // start a new route (no more available vehicles or could not commit on any
4589 // of those available).
4590 // 2) When only one of the nodes of the Saving is contained but on a different
4591 // vehicle type.
4592 // In these cases, the Update() method is called with update_best_saving = true,
4593 // which in turn calls SkipSavingForArc() (within
4594 // UpdateNextAndSkippedSavingsForArcWithType()) to mark the Saving for this arc
4595 // (with the correct type in the second case) as "skipped", by storing it in
4596 // skipped_savings_starting_at_[before] and skipped_savings_ending_at_[after].
4597 //
4598 // UpdateNextAndSkippedSavingsForArcWithType() also updates the next_savings_
4599 // vector, which stores the savings to go through once we've iterated through
4600 // all sorted_savings_.
4601 // In the first case above, where neither nodes are contained, we skip the
4602 // current Saving (current_saving_), and add the next best Saving for this arc
4603 // to next_savings_ (in case this skipped Saving is never considered).
4604 // In the second case with a specific type, we search for the Saving with the
4605 // correct type for this arc, and add it to both next_savings_ and the skipped
4606 // Savings.
4607 //
4608 // The skipped Savings are then re-considered when one of their ends gets
4609 // inserted:
4610 // When another Saving other_node-->before (or after-->other_node) gets
4611 // inserted, all skipped Savings in skipped_savings_starting_at_[before] (or
4612 // skipped_savings_ending_at_[after]) are once again considered by calling
4613 // ReinjectSkippedSavingsStartingAt() (or ReinjectSkippedSavingsEndingAt()).
4614 // Then, when calling GetSaving(), we iterate through the reinjected Savings in
4615 // order of insertion in the vectors while there are reinjected savings.
4616 template <typename Saving>
4617 class SavingsFilteredHeuristic::SavingsContainer {
4618  public:
4619  explicit SavingsContainer(const SavingsFilteredHeuristic* savings_db,
4620  int vehicle_types)
4621  : savings_db_(savings_db),
4622  vehicle_types_(vehicle_types),
4623  index_in_sorted_savings_(0),
4624  single_vehicle_type_(vehicle_types == 1),
4625  using_incoming_reinjected_saving_(false),
4626  sorted_(false),
4627  to_update_(true) {}
4628 
4629  void InitializeContainer(int64 size, int64 saving_neighbors) {
4630  sorted_savings_per_vehicle_type_.clear();
4631  sorted_savings_per_vehicle_type_.resize(vehicle_types_);
4632  for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
4633  savings.reserve(size * saving_neighbors);
4634  }
4635 
4636  sorted_savings_.clear();
4637  costs_and_savings_per_arc_.clear();
4638  arc_indices_per_before_node_.clear();
4639 
4640  if (!single_vehicle_type_) {
4641  costs_and_savings_per_arc_.reserve(size * saving_neighbors);
4642  arc_indices_per_before_node_.resize(size);
4643  for (int before_node = 0; before_node < size; before_node++) {
4644  arc_indices_per_before_node_[before_node].reserve(saving_neighbors);
4645  }
4646  }
4647  skipped_savings_starting_at_.clear();
4648  skipped_savings_starting_at_.resize(size);
4649  skipped_savings_ending_at_.clear();
4650  skipped_savings_ending_at_.resize(size);
4651  incoming_reinjected_savings_ = nullptr;
4652  outgoing_reinjected_savings_ = nullptr;
4653  incoming_new_reinjected_savings_ = nullptr;
4654  outgoing_new_reinjected_savings_ = nullptr;
4655  }
4656 
4657  void AddNewSaving(const Saving& saving, int64 total_cost, int64 before_node,
4658  int64 after_node, int vehicle_type) {
4659  CHECK(!sorted_savings_per_vehicle_type_.empty())
4660  << "Container not initialized!";
4661  sorted_savings_per_vehicle_type_[vehicle_type].push_back(saving);
4662  UpdateArcIndicesCostsAndSavings(before_node, after_node,
4663  {total_cost, saving});
4664  }
4665 
4666  void Sort() {
4667  CHECK(!sorted_) << "Container already sorted!";
4668 
4669  for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
4670  std::sort(savings.begin(), savings.end());
4671  }
4672 
4673  if (single_vehicle_type_) {
4674  const auto& savings = sorted_savings_per_vehicle_type_[0];
4675  sorted_savings_.resize(savings.size());
4676  std::transform(savings.begin(), savings.end(), sorted_savings_.begin(),
4677  [](const Saving& saving) {
4678  return SavingAndArc({saving, /*arc_index*/ -1});
4679  });
4680  } else {
4681  // For each arc, sort the savings by decreasing total cost
4682  // start-->a-->b-->end.
4683  // The best saving for each arc is therefore the last of its vector.
4684  sorted_savings_.reserve(vehicle_types_ *
4685  costs_and_savings_per_arc_.size());
4686 
4687  for (int arc_index = 0; arc_index < costs_and_savings_per_arc_.size();
4688  arc_index++) {
4689  std::vector<std::pair<int64, Saving>>& costs_and_savings =
4690  costs_and_savings_per_arc_[arc_index];
4691  DCHECK(!costs_and_savings.empty());
4692 
4693  std::sort(
4694  costs_and_savings.begin(), costs_and_savings.end(),
4695  [](const std::pair<int64, Saving>& cs1,
4696  const std::pair<int64, Saving>& cs2) { return cs1 > cs2; });
4697 
4698  // Insert all Savings for this arc with the lowest cost into
4699  // sorted_savings_.
4700  // TODO(user): Also do this when reiterating on next_savings_.
4701  const int64 cost = costs_and_savings.back().first;
4702  while (!costs_and_savings.empty() &&
4703  costs_and_savings.back().first == cost) {
4704  sorted_savings_.push_back(
4705  {costs_and_savings.back().second, arc_index});
4706  costs_and_savings.pop_back();
4707  }
4708  }
4709  std::sort(sorted_savings_.begin(), sorted_savings_.end());
4710  next_saving_type_and_index_for_arc_.clear();
4711  next_saving_type_and_index_for_arc_.resize(
4712  costs_and_savings_per_arc_.size(), {-1, -1});
4713  }
4714  sorted_ = true;
4715  index_in_sorted_savings_ = 0;
4716  to_update_ = false;
4717  }
4718 
4719  bool HasSaving() {
4720  return index_in_sorted_savings_ < sorted_savings_.size() ||
4721  HasReinjectedSavings();
4722  }
4723 
4725  CHECK(sorted_) << "Calling GetSaving() before Sort() !";
4726  CHECK(!to_update_)
4727  << "Update() should be called between two calls to GetSaving() !";
4728 
4729  to_update_ = true;
4730 
4731  if (HasReinjectedSavings()) {
4732  if (incoming_reinjected_savings_ != nullptr &&
4733  outgoing_reinjected_savings_ != nullptr) {
4734  // Get the best Saving among the two.
4735  SavingAndArc& incoming_saving = incoming_reinjected_savings_->front();
4736  SavingAndArc& outgoing_saving = outgoing_reinjected_savings_->front();
4737  if (incoming_saving < outgoing_saving) {
4738  current_saving_ = incoming_saving;
4739  using_incoming_reinjected_saving_ = true;
4740  } else {
4741  current_saving_ = outgoing_saving;
4742  using_incoming_reinjected_saving_ = false;
4743  }
4744  } else {
4745  if (incoming_reinjected_savings_ != nullptr) {
4746  current_saving_ = incoming_reinjected_savings_->front();
4747  using_incoming_reinjected_saving_ = true;
4748  }
4749  if (outgoing_reinjected_savings_ != nullptr) {
4750  current_saving_ = outgoing_reinjected_savings_->front();
4751  using_incoming_reinjected_saving_ = false;
4752  }
4753  }
4754  } else {
4755  current_saving_ = sorted_savings_[index_in_sorted_savings_];
4756  }
4757  return current_saving_.saving;
4758  }
4759 
4760  void Update(bool update_best_saving, int type = -1) {
4761  CHECK(to_update_) << "Container already up to date!";
4762  if (update_best_saving) {
4763  const int64 arc_index = current_saving_.arc_index;
4764  UpdateNextAndSkippedSavingsForArcWithType(arc_index, type);
4765  }
4766  if (!HasReinjectedSavings()) {
4767  index_in_sorted_savings_++;
4768 
4769  if (index_in_sorted_savings_ == sorted_savings_.size()) {
4770  sorted_savings_.swap(next_savings_);
4771  gtl::STLClearObject(&next_savings_);
4772  index_in_sorted_savings_ = 0;
4773 
4774  std::sort(sorted_savings_.begin(), sorted_savings_.end());
4775  next_saving_type_and_index_for_arc_.clear();
4776  next_saving_type_and_index_for_arc_.resize(
4777  costs_and_savings_per_arc_.size(), {-1, -1});
4778  }
4779  }
4780  UpdateReinjectedSavings();
4781  to_update_ = false;
4782  }
4783 
4784  void UpdateWithType(int type) {
4785  CHECK(!single_vehicle_type_);
4786  Update(/*update_best_saving*/ true, type);
4787  }
4788 
4789  const std::vector<Saving>& GetSortedSavingsForVehicleType(int type) {
4790  CHECK(sorted_) << "Savings not sorted yet!";
4791  CHECK_LT(type, vehicle_types_);
4792  return sorted_savings_per_vehicle_type_[type];
4793  }
4794 
4796  CHECK(outgoing_new_reinjected_savings_ == nullptr);
4797  outgoing_new_reinjected_savings_ = &(skipped_savings_starting_at_[node]);
4798  }
4799 
4801  CHECK(incoming_new_reinjected_savings_ == nullptr);
4802  incoming_new_reinjected_savings_ = &(skipped_savings_ending_at_[node]);
4803  }
4804 
4805  private:
4806  struct SavingAndArc {
4807  Saving saving;
4808  int64 arc_index;
4809 
4810  bool operator<(const SavingAndArc& other) const {
4811  return std::tie(saving, arc_index) <
4812  std::tie(other.saving, other.arc_index);
4813  }
4814  };
4815 
4816  // Skips the Saving for the arc before_node-->after_node, by adding it to the
4817  // skipped_savings_ vector of the nodes, if they're uncontained.
4818  void SkipSavingForArc(const SavingAndArc& saving_and_arc) {
4819  const Saving& saving = saving_and_arc.saving;
4820  const int64 before_node = savings_db_->GetBeforeNodeFromSaving(saving);
4821  const int64 after_node = savings_db_->GetAfterNodeFromSaving(saving);
4822  if (!savings_db_->Contains(before_node)) {
4823  skipped_savings_starting_at_[before_node].push_back(saving_and_arc);
4824  }
4825  if (!savings_db_->Contains(after_node)) {
4826  skipped_savings_ending_at_[after_node].push_back(saving_and_arc);
4827  }
4828  }
4829 
4830  // Called within Update() when update_best_saving is true, this method updates
4831  // the next_savings_ and skipped savings vectors for a given arc_index and
4832  // vehicle type.
4833  // When a Saving with the right type has already been added to next_savings_
4834  // for this arc, no action is needed on next_savings_.
4835  // Otherwise, if such a Saving exists, GetNextSavingForArcWithType() will find
4836  // and assign it to next_saving, which is then used to update next_savings_.
4837  // Finally, the right Saving is skipped for this arc: if looking for a
4838  // specific type (i.e. type != -1), next_saving (which has the correct type)
4839  // is skipped, otherwise the current_saving_ is.
4840  void UpdateNextAndSkippedSavingsForArcWithType(int64 arc_index, int type) {
4841  if (single_vehicle_type_) {
4842  // No next Saving, skip the current Saving.
4843  CHECK_EQ(type, -1);
4844  SkipSavingForArc(current_saving_);
4845  return;
4846  }
4847  CHECK_GE(arc_index, 0);
4848  auto& type_and_index = next_saving_type_and_index_for_arc_[arc_index];
4849  const int previous_index = type_and_index.second;
4850  const int previous_type = type_and_index.first;
4851  bool next_saving_added = false;
4852  Saving next_saving;
4853 
4854  if (previous_index >= 0) {
4855  // Next Saving already added for this arc.
4856  DCHECK_GE(previous_type, 0);
4857  if (type == -1 || previous_type == type) {
4858  // Not looking for a specific type, or correct type already in
4859  // next_savings_.
4860  next_saving_added = true;
4861  next_saving = next_savings_[previous_index].saving;
4862  }
4863  }
4864 
4865  if (!next_saving_added &&
4866  GetNextSavingForArcWithType(arc_index, type, &next_saving)) {
4867  type_and_index.first = savings_db_->GetVehicleTypeFromSaving(next_saving);
4868  if (previous_index >= 0) {
4869  // Update the previous saving.
4870  next_savings_[previous_index] = {next_saving, arc_index};
4871  } else {
4872  // Insert the new next Saving for this arc.
4873  type_and_index.second = next_savings_.size();
4874  next_savings_.push_back({next_saving, arc_index});
4875  }
4876  next_saving_added = true;
4877  }
4878 
4879  // Skip the Saving based on the vehicle type.
4880  if (type == -1) {
4881  // Skip the current Saving.
4882  SkipSavingForArc(current_saving_);
4883  } else {
4884  // Skip the Saving with the correct type, already added to next_savings_
4885  // if it was found.
4886  if (next_saving_added) {
4887  SkipSavingForArc({next_saving, arc_index});
4888  }
4889  }
4890  }
4891 
4892  void UpdateReinjectedSavings() {
4893  UpdateGivenReinjectedSavings(incoming_new_reinjected_savings_,
4894  &incoming_reinjected_savings_,
4895  using_incoming_reinjected_saving_);
4896  UpdateGivenReinjectedSavings(outgoing_new_reinjected_savings_,
4897  &outgoing_reinjected_savings_,
4898  !using_incoming_reinjected_saving_);
4899  incoming_new_reinjected_savings_ = nullptr;
4900  outgoing_new_reinjected_savings_ = nullptr;
4901  }
4902 
4903  void UpdateGivenReinjectedSavings(
4904  std::deque<SavingAndArc>* new_reinjected_savings,
4905  std::deque<SavingAndArc>** reinjected_savings,
4906  bool using_reinjected_savings) {
4907  if (new_reinjected_savings == nullptr) {
4908  // No new reinjected savings, update the previous ones if needed.
4909  if (*reinjected_savings != nullptr && using_reinjected_savings) {
4910  CHECK(!(*reinjected_savings)->empty());
4911  (*reinjected_savings)->pop_front();
4912  if ((*reinjected_savings)->empty()) {
4913  *reinjected_savings = nullptr;
4914  }
4915  }
4916  return;
4917  }
4918 
4919  // New savings reinjected.
4920  // Forget about the previous reinjected savings and add the new ones if
4921  // there are any.
4922  if (*reinjected_savings != nullptr) {
4923  (*reinjected_savings)->clear();
4924  }
4925  *reinjected_savings = nullptr;
4926  if (!new_reinjected_savings->empty()) {
4927  *reinjected_savings = new_reinjected_savings;
4928  }
4929  }
4930 
4931  bool HasReinjectedSavings() {
4932  return outgoing_reinjected_savings_ != nullptr ||
4933  incoming_reinjected_savings_ != nullptr;
4934  }
4935 
4936  void UpdateArcIndicesCostsAndSavings(
4937  int64 before_node, int64 after_node,
4938  const std::pair<int64, Saving>& cost_and_saving) {
4939  if (single_vehicle_type_) {
4940  return;
4941  }
4942  absl::flat_hash_map<int, int>& arc_indices =
4943  arc_indices_per_before_node_[before_node];
4944  const auto& arc_inserted = arc_indices.insert(
4945  std::make_pair(after_node, costs_and_savings_per_arc_.size()));
4946  const int index = arc_inserted.first->second;
4947  if (arc_inserted.second) {
4948  costs_and_savings_per_arc_.push_back({cost_and_saving});
4949  } else {
4950  DCHECK_LT(index, costs_and_savings_per_arc_.size());
4951  costs_and_savings_per_arc_[index].push_back(cost_and_saving);
4952  }
4953  }
4954 
4955  bool GetNextSavingForArcWithType(int64 arc_index, int type,
4956  Saving* next_saving) {
4957  std::vector<std::pair<int64, Saving>>& costs_and_savings =
4958  costs_and_savings_per_arc_[arc_index];
4959 
4960  bool found_saving = false;
4961  while (!costs_and_savings.empty() && !found_saving) {
4962  const Saving& saving = costs_and_savings.back().second;
4963  if (type == -1 || savings_db_->GetVehicleTypeFromSaving(saving) == type) {
4964  *next_saving = saving;
4965  found_saving = true;
4966  }
4967  costs_and_savings.pop_back();
4968  }
4969  return found_saving;
4970  }
4971 
4972  const SavingsFilteredHeuristic* const savings_db_;
4973  const int vehicle_types_;
4974  int64 index_in_sorted_savings_;
4975  std::vector<std::vector<Saving>> sorted_savings_per_vehicle_type_;
4976  std::vector<SavingAndArc> sorted_savings_;
4977  std::vector<SavingAndArc> next_savings_;
4978  std::vector<std::pair</*type*/ int, /*index*/ int>>
4979  next_saving_type_and_index_for_arc_;
4980  SavingAndArc current_saving_;
4981  const bool single_vehicle_type_;
4982  std::vector<std::vector<std::pair</*cost*/ int64, Saving>>>
4983  costs_and_savings_per_arc_;
4984  std::vector<absl::flat_hash_map</*after_node*/ int, /*arc_index*/ int>>
4985  arc_indices_per_before_node_;
4986  std::vector<std::deque<SavingAndArc>> skipped_savings_starting_at_;
4987  std::vector<std::deque<SavingAndArc>> skipped_savings_ending_at_;
4988  std::deque<SavingAndArc>* outgoing_reinjected_savings_;
4989  std::deque<SavingAndArc>* incoming_reinjected_savings_;
4990  bool using_incoming_reinjected_saving_;
4991  std::deque<SavingAndArc>* outgoing_new_reinjected_savings_;
4992  std::deque<SavingAndArc>* incoming_new_reinjected_savings_;
4993  bool sorted_;
4994  bool to_update_;
4995 };
4996 
4997 // SavingsFilteredHeuristic
4998 
4999 SavingsFilteredHeuristic::SavingsFilteredHeuristic(
5000  RoutingModel* model, const RoutingIndexManager* manager,
5002  : RoutingFilteredHeuristic(model, filter_manager),
5003  vehicle_type_curator_(nullptr),
5004  manager_(manager),
5005  savings_params_(parameters) {
5006  DCHECK_GT(savings_params_.neighbors_ratio, 0);
5007  DCHECK_LE(savings_params_.neighbors_ratio, 1);
5008  DCHECK_GT(savings_params_.max_memory_usage_bytes, 0);
5009  DCHECK_GT(savings_params_.arc_coefficient, 0);
5010  const int size = model->Size();
5011  size_squared_ = size * size;
5012 }
5013 
5015 
5017  if (vehicle_type_curator_ == nullptr) {
5018  vehicle_type_curator_ = absl::make_unique<VehicleTypeCurator>(
5019  model()->GetVehicleTypeContainer());
5020  }
5021  vehicle_type_curator_->Reset();
5022  ComputeSavings();
5024  // Free all the space used to store the Savings in the container.
5025  savings_container_.reset();
5027  return Commit();
5028 }
5029 
5031  int type, int64 before_node, int64 after_node) {
5032  auto vehicle_is_compatible = [this, before_node, after_node](int vehicle) {
5033  if (!model()->VehicleVar(before_node)->Contains(vehicle) ||
5034  !model()->VehicleVar(after_node)->Contains(vehicle)) {
5035  return false;
5036  }
5037  // Try to commit the arc on this vehicle.
5038  const int64 start = model()->Start(vehicle);
5039  const int64 end = model()->End(vehicle);
5040  SetValue(start, before_node);
5041  SetValue(before_node, after_node);
5042  SetValue(after_node, end);
5043  return Commit();
5044  };
5045 
5046  const int vehicle = vehicle_type_curator_->GetCompatibleVehicleOfType(
5047  type, vehicle_is_compatible);
5048  return vehicle;
5049 }
5050 
5051 void SavingsFilteredHeuristic::AddSymmetricArcsToAdjacencyLists(
5052  std::vector<std::vector<int64>>* adjacency_lists) {
5053  for (int64 node = 0; node < adjacency_lists->size(); node++) {
5054  for (int64 neighbor : (*adjacency_lists)[node]) {
5055  if (model()->IsStart(neighbor) || model()->IsEnd(neighbor)) {
5056  continue;
5057  }
5058  (*adjacency_lists)[neighbor].push_back(node);
5059  }
5060  }
5061  std::transform(adjacency_lists->begin(), adjacency_lists->end(),
5062  adjacency_lists->begin(), [](std::vector<int64> vec) {
5063  std::sort(vec.begin(), vec.end());
5064  vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
5065  return vec;
5066  });
5067 }
5068 
5069 // Computes the savings related to each pair of non-start and non-end nodes.
5070 // The savings value for an arc a-->b for a vehicle starting at node s and
5071 // ending at node e is:
5072 // saving = cost(s-->a-->e) + cost(s-->b-->e) - cost(s-->a-->b-->e), i.e.
5073 // saving = cost(a-->e) + cost(s-->b) - cost(a-->b)
5074 // The saving value also considers a coefficient for the cost of the arc
5075 // a-->b, which results in:
5076 // saving = cost(a-->e) + cost(s-->b) - [arc_coefficient_ * cost(a-->b)]
5077 // The higher this saving value, the better the arc.
5078 // Here, the value stored for the savings is -saving, which are therefore
5079 // considered in decreasing order.
5080 void SavingsFilteredHeuristic::ComputeSavings() {
5081  const int num_vehicle_types = vehicle_type_curator_->NumTypes();
5082  const int size = model()->Size();
5083 
5084  std::vector<int64> uncontained_non_start_end_nodes;
5085  uncontained_non_start_end_nodes.reserve(size);
5086  for (int node = 0; node < size; node++) {
5087  if (!model()->IsStart(node) && !model()->IsEnd(node) && !Contains(node)) {
5088  uncontained_non_start_end_nodes.push_back(node);
5089  }
5090  }
5091 
5092  const int64 saving_neighbors =
5093  std::min(MaxNumNeighborsPerNode(num_vehicle_types),
5094  static_cast<int64>(uncontained_non_start_end_nodes.size()));
5095 
5097  absl::make_unique<SavingsContainer<Saving>>(this, num_vehicle_types);
5098  savings_container_->InitializeContainer(size, saving_neighbors);
5099 
5100  std::vector<std::vector<int64>> adjacency_lists(size);
5101 
5102  for (int type = 0; type < num_vehicle_types; ++type) {
5103  const int vehicle = vehicle_type_curator_->GetVehicleOfType(type);
5104  if (vehicle < 0) {
5105  continue;
5106  }
5107 
5108  const int64 cost_class =
5109  model()->GetCostClassIndexOfVehicle(vehicle).value();
5110  const int64 start = model()->Start(vehicle);
5111  const int64 end = model()->End(vehicle);
5112  const int64 fixed_cost = model()->GetFixedCostOfVehicle(vehicle);
5113 
5114  // Compute the neighbors for each non-start/end node not already inserted in
5115  // the model.
5116  for (int before_node : uncontained_non_start_end_nodes) {
5117  std::vector<std::pair</*cost*/ int64, /*node*/ int64>> costed_after_nodes;
5118  costed_after_nodes.reserve(uncontained_non_start_end_nodes.size());
5119  for (int after_node : uncontained_non_start_end_nodes) {
5120  if (after_node != before_node) {
5121  costed_after_nodes.push_back(std::make_pair(
5122  model()->GetArcCostForClass(before_node, after_node, cost_class),
5123  after_node));
5124  }
5125  }
5126  if (saving_neighbors < costed_after_nodes.size()) {
5127  std::nth_element(costed_after_nodes.begin(),
5128  costed_after_nodes.begin() + saving_neighbors,
5129  costed_after_nodes.end());
5130  costed_after_nodes.resize(saving_neighbors);
5131  }
5132  adjacency_lists[before_node].resize(costed_after_nodes.size());
5133  std::transform(costed_after_nodes.begin(), costed_after_nodes.end(),
5134  adjacency_lists[before_node].begin(),
5135  [](std::pair<int64, int64> cost_and_node) {
5136  return cost_and_node.second;
5137  });
5138  }
5139  if (savings_params_.add_reverse_arcs) {
5140  AddSymmetricArcsToAdjacencyLists(&adjacency_lists);
5141  }
5142 
5143  // Build the savings for this vehicle type given the adjacency_lists.
5144  for (int before_node : uncontained_non_start_end_nodes) {
5145  const int64 before_to_end_cost =
5146  model()->GetArcCostForClass(before_node, end, cost_class);
5147  const int64 start_to_before_cost =
5148  CapSub(model()->GetArcCostForClass(start, before_node, cost_class),
5149  fixed_cost);
5150  for (int64 after_node : adjacency_lists[before_node]) {
5151  if (model()->IsStart(after_node) || model()->IsEnd(after_node) ||
5152  before_node == after_node || Contains(after_node)) {
5153  continue;
5154  }
5155  const int64 arc_cost =
5156  model()->GetArcCostForClass(before_node, after_node, cost_class);
5157  const int64 start_to_after_cost =
5158  CapSub(model()->GetArcCostForClass(start, after_node, cost_class),
5159  fixed_cost);
5160  const int64 after_to_end_cost =
5161  model()->GetArcCostForClass(after_node, end, cost_class);
5162 
5163  const double weighted_arc_cost_fp =
5164  savings_params_.arc_coefficient * arc_cost;
5165  const int64 weighted_arc_cost =
5166  weighted_arc_cost_fp < kint64max
5167  ? static_cast<int64>(weighted_arc_cost_fp)
5168  : kint64max;
5169  const int64 saving_value = CapSub(
5170  CapAdd(before_to_end_cost, start_to_after_cost), weighted_arc_cost);
5171 
5172  const Saving saving =
5173  BuildSaving(-saving_value, type, before_node, after_node);
5174 
5175  const int64 total_cost =
5176  CapAdd(CapAdd(start_to_before_cost, arc_cost), after_to_end_cost);
5177 
5178  savings_container_->AddNewSaving(saving, total_cost, before_node,
5179  after_node, type);
5180  }
5181  }
5182  }
5183  savings_container_->Sort();
5184 }
5185 
5186 int64 SavingsFilteredHeuristic::MaxNumNeighborsPerNode(
5187  int num_vehicle_types) const {
5188  const int64 size = model()->Size();
5189 
5190  const int64 num_neighbors_with_ratio =
5191  std::max(1.0, size * savings_params_.neighbors_ratio);
5192 
5193  // A single Saving takes 2*8 bytes of memory.
5194  // max_memory_usage_in_savings_unit = num_savings * multiplicative_factor,
5195  // Where multiplicative_factor is the memory taken (in Savings unit) for each
5196  // computed Saving.
5197  const double max_memory_usage_in_savings_unit =
5198  savings_params_.max_memory_usage_bytes / 16;
5199 
5200  // In the SavingsContainer, for each Saving, the Savings are stored:
5201  // - Once in "sorted_savings_per_vehicle_type", and (at most) once in
5202  // "sorted_savings_" --> factor 2
5203  // - If num_vehicle_types > 1, they're also stored by arc_index in
5204  // "costs_and_savings_per_arc", along with their int64 cost --> factor 1.5
5205  //
5206  // On top of that,
5207  // - In the sequential version, the Saving* are also stored by in-coming and
5208  // outgoing node (in in/out_savings_ptr), adding another 2*8 bytes per
5209  // Saving --> factor 1.
5210  // - In the parallel version, skipped Savings are also stored in
5211  // skipped_savings_starting/ending_at_, resulting in a maximum added factor
5212  // of 2 for each Saving.
5213  // These extra factors are given by ExtraSavingsMemoryMultiplicativeFactor().
5214  double multiplicative_factor = 2.0 + ExtraSavingsMemoryMultiplicativeFactor();
5215  if (num_vehicle_types > 1) {
5216  multiplicative_factor += 1.5;
5217  }
5218  const double num_savings =
5219  max_memory_usage_in_savings_unit / multiplicative_factor;
5220  const int64 num_neighbors_with_memory_restriction =
5221  std::max(1.0, num_savings / (num_vehicle_types * size));
5222 
5223  return std::min(num_neighbors_with_ratio,
5224  num_neighbors_with_memory_restriction);
5225 }
5226 
5227 // SequentialSavingsFilteredHeuristic
5228 
5229 void SequentialSavingsFilteredHeuristic::BuildRoutesFromSavings() {
5230  const int vehicle_types = vehicle_type_curator_->NumTypes();
5231  DCHECK_GT(vehicle_types, 0);
5232  const int size = model()->Size();
5233  // Store savings for each incoming and outgoing node and by vehicle type. This
5234  // is necessary to quickly extend partial chains without scanning all savings.
5235  std::vector<std::vector<const Saving*>> in_savings_ptr(size * vehicle_types);
5236  std::vector<std::vector<const Saving*>> out_savings_ptr(size * vehicle_types);
5237  for (int type = 0; type < vehicle_types; type++) {
5238  const int vehicle_type_offset = type * size;
5239  const std::vector<Saving>& sorted_savings_for_type =
5240  savings_container_->GetSortedSavingsForVehicleType(type);
5241  for (const Saving& saving : sorted_savings_for_type) {
5242  DCHECK_EQ(GetVehicleTypeFromSaving(saving), type);
5243  const int before_node = GetBeforeNodeFromSaving(saving);
5244  in_savings_ptr[vehicle_type_offset + before_node].push_back(&saving);
5245  const int after_node = GetAfterNodeFromSaving(saving);
5246  out_savings_ptr[vehicle_type_offset + after_node].push_back(&saving);
5247  }
5248  }
5249 
5250  // Build routes from savings.
5251  while (savings_container_->HasSaving()) {
5252  // First find the best saving to start a new route.
5253  const Saving saving = savings_container_->GetSaving();
5254  int before_node = GetBeforeNodeFromSaving(saving);
5255  int after_node = GetAfterNodeFromSaving(saving);
5256  const bool nodes_not_contained =
5257  !Contains(before_node) && !Contains(after_node);
5258 
5259  bool committed = false;
5260 
5261  if (nodes_not_contained) {
5262  // Find the right vehicle to start the route with this Saving.
5263  const int type = GetVehicleTypeFromSaving(saving);
5264  const int vehicle =
5265  StartNewRouteWithBestVehicleOfType(type, before_node, after_node);
5266 
5267  if (vehicle >= 0) {
5268  committed = true;
5269  const int64 start = model()->Start(vehicle);
5270  const int64 end = model()->End(vehicle);
5271  // Then extend the route from both ends of the partial route.
5272  int in_index = 0;
5273  int out_index = 0;
5274  const int saving_offset = type * size;
5275 
5276  while (in_index < in_savings_ptr[saving_offset + after_node].size() ||
5277  out_index <
5278  out_savings_ptr[saving_offset + before_node].size()) {
5279  if (StopSearch()) return;
5280  // First determine how to extend the route.
5281  int before_before_node = -1;
5282  int after_after_node = -1;
5283  if (in_index < in_savings_ptr[saving_offset + after_node].size()) {
5284  const Saving& in_saving =
5285  *(in_savings_ptr[saving_offset + after_node][in_index]);
5286  if (out_index <
5287  out_savings_ptr[saving_offset + before_node].size()) {
5288  const Saving& out_saving =
5289  *(out_savings_ptr[saving_offset + before_node][out_index]);
5290  if (GetSavingValue(in_saving) < GetSavingValue(out_saving)) {
5291  // Should extend after after_node
5292  after_after_node = GetAfterNodeFromSaving(in_saving);
5293  } else {
5294  // Should extend before before_node
5295  before_before_node = GetBeforeNodeFromSaving(out_saving);
5296  }
5297  } else {
5298  // Should extend after after_node
5299  after_after_node = GetAfterNodeFromSaving(in_saving);
5300  }
5301  } else {
5302  // Should extend before before_node
5303  before_before_node = GetBeforeNodeFromSaving(
5304  *(out_savings_ptr[saving_offset + before_node][out_index]));
5305  }
5306  // Extend the route
5307  if (after_after_node != -1) {
5308  DCHECK_EQ(before_before_node, -1);
5309  // Extending after after_node
5310  if (!Contains(after_after_node)) {
5311  SetValue(after_node, after_after_node);
5312  SetValue(after_after_node, end);
5313  if (Commit()) {
5314  in_index = 0;
5315  after_node = after_after_node;
5316  } else {
5317  ++in_index;
5318  }
5319  } else {
5320  ++in_index;
5321  }
5322  } else {
5323  // Extending before before_node
5324  CHECK_GE(before_before_node, 0);
5325  if (!Contains(before_before_node)) {
5326  SetValue(start, before_before_node);
5327  SetValue(before_before_node, before_node);
5328  if (Commit()) {
5329  out_index = 0;
5330  before_node = before_before_node;
5331  } else {
5332  ++out_index;
5333  }
5334  } else {
5335  ++out_index;
5336  }
5337  }
5338  }
5339  }
5340  }
5341  savings_container_->Update(nodes_not_contained && !committed);
5342  }
5343 }
5344 
5345 // ParallelSavingsFilteredHeuristic
5346 
5347 void ParallelSavingsFilteredHeuristic::BuildRoutesFromSavings() {
5348  // Initialize the vehicles of the first/last non start/end nodes served by
5349  // each route.
5350  const int64 size = model()->Size();
5351  const int vehicles = model()->vehicles();
5352 
5353  first_node_on_route_.resize(vehicles, -1);
5354  last_node_on_route_.resize(vehicles, -1);
5355  vehicle_of_first_or_last_node_.resize(size, -1);
5356 
5357  for (int vehicle = 0; vehicle < vehicles; vehicle++) {
5358  const int64 start = model()->Start(vehicle);
5359  const int64 end = model()->End(vehicle);
5360  if (!Contains(start)) {
5361  continue;
5362  }
5363  int64 node = Value(start);
5364  if (node != end) {
5365  vehicle_of_first_or_last_node_[node] = vehicle;
5366  first_node_on_route_[vehicle] = node;
5367 
5368  int64 next = Value(node);
5369  while (next != end) {
5370  node = next;
5371  next = Value(node);
5372  }
5373  vehicle_of_first_or_last_node_[node] = vehicle;
5374  last_node_on_route_[vehicle] = node;
5375  }
5376  }
5377 
5378  while (savings_container_->HasSaving()) {
5379  if (StopSearch()) return;
5380  const Saving saving = savings_container_->GetSaving();
5381  const int64 before_node = GetBeforeNodeFromSaving(saving);
5382  const int64 after_node = GetAfterNodeFromSaving(saving);
5383  const int type = GetVehicleTypeFromSaving(saving);
5384 
5385  if (!Contains(before_node) && !Contains(after_node)) {
5386  // Neither nodes are contained, start a new route.
5387  bool committed = false;
5388 
5389  const int vehicle =
5390  StartNewRouteWithBestVehicleOfType(type, before_node, after_node);
5391 
5392  if (vehicle >= 0) {
5393  committed = true;
5394  // Store before_node and after_node as first and last nodes of the route
5395  vehicle_of_first_or_last_node_[before_node] = vehicle;
5396  vehicle_of_first_or_last_node_[after_node] = vehicle;
5397  first_node_on_route_[vehicle] = before_node;
5398  last_node_on_route_[vehicle] = after_node;
5399  savings_container_->ReinjectSkippedSavingsStartingAt(after_node);
5400  savings_container_->ReinjectSkippedSavingsEndingAt(before_node);
5401  }
5402  savings_container_->Update(!committed);
5403  continue;
5404  }
5405 
5406  if (Contains(before_node) && Contains(after_node)) {
5407  // Merge the two routes if before_node is last and after_node first of its
5408  // route, the two nodes aren't already on the same route, and the vehicle
5409  // types are compatible.
5410  const int v1 = vehicle_of_first_or_last_node_[before_node];
5411  const int64 last_node = v1 == -1 ? -1 : last_node_on_route_[v1];
5412 
5413  const int v2 = vehicle_of_first_or_last_node_[after_node];
5414  const int64 first_node = v2 == -1 ? -1 : first_node_on_route_[v2];
5415 
5416  if (before_node == last_node && after_node == first_node && v1 != v2 &&
5417  vehicle_type_curator_->Type(v1) == vehicle_type_curator_->Type(v2)) {
5418  CHECK_EQ(Value(before_node), model()->End(v1));
5419  CHECK_EQ(Value(model()->Start(v2)), after_node);
5420 
5421  // We try merging the two routes.
5422  // TODO(user): Try to use skipped savings to start new routes when
5423  // a vehicle becomes available after a merge (not trivial because it can
5424  // result in an infinite loop).
5425  MergeRoutes(v1, v2, before_node, after_node);
5426  }
5427  }
5428 
5429  if (Contains(before_node) && !Contains(after_node)) {
5430  const int vehicle = vehicle_of_first_or_last_node_[before_node];
5431  const int64 last_node = vehicle == -1 ? -1 : last_node_on_route_[vehicle];
5432 
5433  if (before_node == last_node) {
5434  const int64 end = model()->End(vehicle);
5435  CHECK_EQ(Value(before_node), end);
5436 
5437  const int route_type = vehicle_type_curator_->Type(vehicle);
5438  if (type != route_type) {
5439  // The saving doesn't correspond to the type of the vehicle serving
5440  // before_node. We update the container with the correct type.
5441  savings_container_->UpdateWithType(route_type);
5442  continue;
5443  }
5444 
5445  // Try adding after_node on route of before_node.
5446  SetValue(before_node, after_node);
5447  SetValue(after_node, end);
5448  if (Commit()) {
5449  if (first_node_on_route_[vehicle] != before_node) {
5450  // before_node is no longer the start or end of its route
5451  DCHECK_NE(Value(model()->Start(vehicle)), before_node);
5452  vehicle_of_first_or_last_node_[before_node] = -1;
5453  }
5454  vehicle_of_first_or_last_node_[after_node] = vehicle;
5455  last_node_on_route_[vehicle] = after_node;
5456  savings_container_->ReinjectSkippedSavingsStartingAt(after_node);
5457  }
5458  }
5459  }
5460 
5461  if (!Contains(before_node) && Contains(after_node)) {
5462  const int vehicle = vehicle_of_first_or_last_node_[after_node];
5463  const int64 first_node =
5464  vehicle == -1 ? -1 : first_node_on_route_[vehicle];
5465 
5466  if (after_node == first_node) {
5467  const int64 start = model()->Start(vehicle);
5468  CHECK_EQ(Value(start), after_node);
5469 
5470  const int route_type = vehicle_type_curator_->Type(vehicle);
5471  if (type != route_type) {
5472  // The saving doesn't correspond to the type of the vehicle serving
5473  // after_node. We update the container with the correct type.
5474  savings_container_->UpdateWithType(route_type);
5475  continue;
5476  }
5477 
5478  // Try adding before_node on route of after_node.
5479  SetValue(before_node, after_node);
5480  SetValue(start, before_node);
5481  if (Commit()) {
5482  if (last_node_on_route_[vehicle] != after_node) {
5483  // after_node is no longer the start or end of its route
5484  DCHECK_NE(Value(after_node), model()->End(vehicle));
5485  vehicle_of_first_or_last_node_[after_node] = -1;
5486  }
5487  vehicle_of_first_or_last_node_[before_node] = vehicle;
5488  first_node_on_route_[vehicle] = before_node;
5489  savings_container_->ReinjectSkippedSavingsEndingAt(before_node);
5490  }
5491  }
5492  }
5493  savings_container_->Update(/*update_best_saving*/ false);
5494  }
5495 }
5496 
5497 void ParallelSavingsFilteredHeuristic::MergeRoutes(int first_vehicle,
5498  int second_vehicle,
5499  int64 before_node,
5500  int64 after_node) {
5501  if (StopSearch()) return;
5502  const int64 new_first_node = first_node_on_route_[first_vehicle];
5503  DCHECK_EQ(vehicle_of_first_or_last_node_[new_first_node], first_vehicle);
5504  CHECK_EQ(Value(model()->Start(first_vehicle)), new_first_node);
5505  const int64 new_last_node = last_node_on_route_[second_vehicle];
5506  DCHECK_EQ(vehicle_of_first_or_last_node_[new_last_node], second_vehicle);
5507  CHECK_EQ(Value(new_last_node), model()->End(second_vehicle));
5508 
5509  // Select the vehicle with lower fixed cost to merge the routes.
5510  int used_vehicle = first_vehicle;
5511  int unused_vehicle = second_vehicle;
5512  if (model()->GetFixedCostOfVehicle(first_vehicle) >
5513  model()->GetFixedCostOfVehicle(second_vehicle)) {
5514  used_vehicle = second_vehicle;
5515  unused_vehicle = first_vehicle;
5516  }
5517 
5518  SetValue(before_node, after_node);
5519  SetValue(model()->Start(unused_vehicle), model()->End(unused_vehicle));
5520  if (used_vehicle == first_vehicle) {
5521  SetValue(new_last_node, model()->End(used_vehicle));
5522  } else {
5523  SetValue(model()->Start(used_vehicle), new_first_node);
5524  }
5525  bool committed = Commit();
5526  if (!committed &&
5527  model()->GetVehicleClassIndexOfVehicle(first_vehicle).value() !=
5528  model()->GetVehicleClassIndexOfVehicle(second_vehicle).value()) {
5529  // Try committing on other vehicle instead.
5530  std::swap(used_vehicle, unused_vehicle);
5531  SetValue(before_node, after_node);
5532  SetValue(model()->Start(unused_vehicle), model()->End(unused_vehicle));
5533  if (used_vehicle == first_vehicle) {
5534  SetValue(new_last_node, model()->End(used_vehicle));
5535  } else {
5536  SetValue(model()->Start(used_vehicle), new_first_node);
5537  }
5538  committed = Commit();
5539  }
5540  if (committed) {
5541  // Make unused_vehicle available
5542  vehicle_type_curator_->ReinjectVehicleOfClass(
5543  unused_vehicle,
5544  model()->GetVehicleClassIndexOfVehicle(unused_vehicle).value(),
5545  model()->GetFixedCostOfVehicle(unused_vehicle));
5546 
5547  // Update the first and last nodes on vehicles.
5548  first_node_on_route_[unused_vehicle] = -1;
5549  last_node_on_route_[unused_vehicle] = -1;
5550  vehicle_of_first_or_last_node_[before_node] = -1;
5551  vehicle_of_first_or_last_node_[after_node] = -1;
5552  first_node_on_route_[used_vehicle] = new_first_node;
5553  last_node_on_route_[used_vehicle] = new_last_node;
5554  vehicle_of_first_or_last_node_[new_last_node] = used_vehicle;
5555  vehicle_of_first_or_last_node_[new_first_node] = used_vehicle;
5556  }
5557 }
5558 
5559 // ChristofidesFilteredHeuristic
5561  RoutingModel* model, LocalSearchFilterManager* filter_manager,
5562  bool use_minimum_matching)
5563  : RoutingFilteredHeuristic(model, filter_manager),
5564  use_minimum_matching_(use_minimum_matching) {}
5565 
5566 // TODO(user): Support pickup & delivery.
5568  const int size = model()->Size() - model()->vehicles() + 1;
5569  // Node indices for Christofides solver.
5570  // 0: start/end node
5571  // >0: non start/end nodes
5572  // TODO(user): Add robustness to fixed arcs by collapsing them into meta-
5573  // nodes.
5574  std::vector<int> indices(1, 0);
5575  for (int i = 1; i < size; ++i) {
5576  if (!model()->IsStart(i) && !model()->IsEnd(i)) {
5577  indices.push_back(i);
5578  }
5579  }
5580  const int num_cost_classes = model()->GetCostClassesCount();
5581  std::vector<std::vector<int>> path_per_cost_class(num_cost_classes);
5582  std::vector<bool> class_covered(num_cost_classes, false);
5583  for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
5584  const int64 cost_class =
5585  model()->GetCostClassIndexOfVehicle(vehicle).value();
5586  if (!class_covered[cost_class]) {
5587  class_covered[cost_class] = true;
5588  const int64 start = model()->Start(vehicle);
5589  const int64 end = model()->End(vehicle);
5590  auto cost = [this, &indices, start, end, cost_class](int from, int to) {
5591  DCHECK_LT(from, indices.size());
5592  DCHECK_LT(to, indices.size());
5593  const int from_index = (from == 0) ? start : indices[from];
5594  const int to_index = (to == 0) ? end : indices[to];
5595  const int64 cost =
5596  model()->GetArcCostForClass(from_index, to_index, cost_class);
5597  // To avoid overflow issues, capping costs at kint64max/2, the maximum
5598  // value supported by MinCostPerfectMatching.
5599  // TODO(user): Investigate if ChristofidesPathSolver should not
5600  // return a status to bail out fast in case of problem.
5601  return std::min(cost, kint64max / 2);
5602  };
5603  using Cost = decltype(cost);
5605  indices.size(), cost);
5606  if (use_minimum_matching_) {
5607  christofides_solver.SetMatchingAlgorithm(
5609  MINIMUM_WEIGHT_MATCHING);
5610  }
5611  path_per_cost_class[cost_class] =
5612  christofides_solver.TravelingSalesmanPath();
5613  }
5614  }
5615  // TODO(user): Investigate if sorting paths per cost improves solutions.
5616  for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
5617  const int64 cost_class =
5618  model()->GetCostClassIndexOfVehicle(vehicle).value();
5619  const std::vector<int>& path = path_per_cost_class[cost_class];
5620  DCHECK_EQ(0, path[0]);
5621  DCHECK_EQ(0, path.back());
5622  // Extend route from start.
5623  int prev = GetStartChainEnd(vehicle);
5624  const int end = model()->End(vehicle);
5625  for (int i = 1; i < path.size() - 1 && prev != end; ++i) {
5626  if (StopSearch()) return false;
5627  int next = indices[path[i]];
5628  if (!Contains(next)) {
5629  SetValue(prev, next);
5630  SetValue(next, end);
5631  if (Commit()) {
5632  prev = next;
5633  }
5634  }
5635  }
5636  }
5638  return Commit();
5639 }
5640 
5641 namespace {
5642 // The description is in routing.h:MakeGuidedSlackFinalizer
5643 class GuidedSlackFinalizer : public DecisionBuilder {
5644  public:
5645  GuidedSlackFinalizer(const RoutingDimension* dimension, RoutingModel* model,
5646  std::function<int64(int64)> initializer);
5647  Decision* Next(Solver* solver) override;
5648 
5649  private:
5650  int64 SelectValue(int64 index);
5651  int64 ChooseVariable();
5652 
5653  const RoutingDimension* const dimension_;
5654  RoutingModel* const model_;
5655  const std::function<int64(int64)> initializer_;
5656  RevArray<bool> is_initialized_;
5657  std::vector<int64> initial_values_;
5658  Rev<int64> current_index_;
5659  Rev<int64> current_route_;
5660  RevArray<int64> last_delta_used_;
5661 
5662  DISALLOW_COPY_AND_ASSIGN(GuidedSlackFinalizer);
5663 };
5664 
5665 GuidedSlackFinalizer::GuidedSlackFinalizer(
5666  const RoutingDimension* dimension, RoutingModel* model,
5667  std::function<int64(int64)> initializer)
5668  : dimension_(ABSL_DIE_IF_NULL(dimension)),
5669  model_(ABSL_DIE_IF_NULL(model)),
5670  initializer_(std::move(initializer)),
5671  is_initialized_(dimension->slacks().size(), false),
5672  initial_values_(dimension->slacks().size(), kint64min),
5673  current_index_(model_->Start(0)),
5674  current_route_(0),
5675  last_delta_used_(dimension->slacks().size(), 0) {}
5676 
5677 Decision* GuidedSlackFinalizer::Next(Solver* solver) {
5678  CHECK_EQ(solver, model_->solver());
5679  const int node_idx = ChooseVariable();
5680  CHECK(node_idx == -1 ||
5681  (node_idx >= 0 && node_idx < dimension_->slacks().size()));
5682  if (node_idx != -1) {
5683  if (!is_initialized_[node_idx]) {
5684  initial_values_[node_idx] = initializer_(node_idx);
5685  is_initialized_.SetValue(solver, node_idx, true);
5686  }
5687  const int64 value = SelectValue(node_idx);
5688  IntVar* const slack_variable = dimension_->SlackVar(node_idx);
5689  return solver->MakeAssignVariableValue(slack_variable, value);
5690  }
5691  return nullptr;
5692 }
5693 
5694 int64 GuidedSlackFinalizer::SelectValue(int64 index) {
5695  const IntVar* const slack_variable = dimension_->SlackVar(index);
5696  const int64 center = initial_values_[index];
5697  const int64 max_delta =
5698  std::max(center - slack_variable->Min(), slack_variable->Max() - center) +
5699  1;
5700  int64 delta = last_delta_used_[index];
5701 
5702  // The sequence of deltas is 0, 1, -1, 2, -2 ...
5703  // Only the values inside the domain of variable are returned.
5704  while (std::abs(delta) < max_delta &&
5705  !slack_variable->Contains(center + delta)) {
5706  if (delta > 0) {
5707  delta = -delta;
5708  } else {
5709  delta = -delta + 1;
5710  }
5711  }
5712  last_delta_used_.SetValue(model_->solver(), index, delta);
5713  return center + delta;
5714 }
5715 
5716 int64 GuidedSlackFinalizer::ChooseVariable() {
5717  int64 int_current_node = current_index_.Value();
5718  int64 int_current_route = current_route_.Value();
5719 
5720  while (int_current_route < model_->vehicles()) {
5721  while (!model_->IsEnd(int_current_node) &&
5722  dimension_->SlackVar(int_current_node)->Bound()) {
5723  int_current_node = model_->NextVar(int_current_node)->Value();
5724  }
5725  if (!model_->IsEnd(int_current_node)) {
5726  break;
5727  }
5728  int_current_route += 1;
5729  if (int_current_route < model_->vehicles()) {
5730  int_current_node = model_->Start(int_current_route);
5731  }
5732  }
5733 
5734  CHECK(int_current_route == model_->vehicles() ||
5735  !dimension_->SlackVar(int_current_node)->Bound());
5736  current_index_.SetValue(model_->solver(), int_current_node);
5737  current_route_.SetValue(model_->solver(), int_current_route);
5738  if (int_current_route < model_->vehicles()) {
5739  return int_current_node;
5740  } else {
5741  return -1;
5742  }
5743 }
5744 } // namespace
5745 
5747  const RoutingDimension* dimension,
5748  std::function<int64(int64)> initializer) {
5749  return solver_->RevAlloc(
5750  new GuidedSlackFinalizer(dimension, this, std::move(initializer)));
5751 }
5752 
5754  CHECK_EQ(base_dimension_, this);
5755  CHECK(!model_->IsEnd(node));
5756  // Recall that the model is cumul[i+1] = cumul[i] + transit[i] + slack[i]. Our
5757  // aim is to find a value for slack[i] such that cumul[i+1] + transit[i+1] is
5758  // minimized.
5759  const int64 next = model_->NextVar(node)->Value();
5760  if (model_->IsEnd(next)) {
5761  return SlackVar(node)->Min();
5762  }
5763  const int64 next_next = model_->NextVar(next)->Value();
5764  const int64 serving_vehicle = model_->VehicleVar(node)->Value();
5765  CHECK_EQ(serving_vehicle, model_->VehicleVar(next)->Value());
5766  const RoutingModel::StateDependentTransit transit_from_next =
5768  state_dependent_class_evaluators_
5769  [state_dependent_vehicle_to_class_[serving_vehicle]])(next,
5770  next_next);
5771  // We have that transit[i+1] is a function of cumul[i+1].
5772  const int64 next_cumul_min = CumulVar(next)->Min();
5773  const int64 next_cumul_max = CumulVar(next)->Max();
5774  const int64 optimal_next_cumul =
5775  transit_from_next.transit_plus_identity->RangeMinArgument(
5776  next_cumul_min, next_cumul_max + 1);
5777  // A few checks to make sure we're on the same page.
5778  DCHECK_LE(next_cumul_min, optimal_next_cumul);
5779  DCHECK_LE(optimal_next_cumul, next_cumul_max);
5780  // optimal_next_cumul = cumul + transit + optimal_slack, so
5781  // optimal_slack = optimal_next_cumul - cumul - transit.
5782  // In the current implementation TransitVar(i) = transit[i] + slack[i], so we
5783  // have to find the transit from the evaluators.
5784  const int64 current_cumul = CumulVar(node)->Value();
5785  const int64 current_state_independent_transit = model_->TransitCallback(
5786  class_evaluators_[vehicle_to_class_[serving_vehicle]])(node, next);
5787  const int64 current_state_dependent_transit =
5788  model_
5790  state_dependent_class_evaluators_
5791  [state_dependent_vehicle_to_class_[serving_vehicle]])(node,
5792  next)
5793  .transit->Query(current_cumul);
5794  const int64 optimal_slack = optimal_next_cumul - current_cumul -
5795  current_state_independent_transit -
5796  current_state_dependent_transit;
5797  CHECK_LE(SlackVar(node)->Min(), optimal_slack);
5798  CHECK_LE(optimal_slack, SlackVar(node)->Max());
5799  return optimal_slack;
5800 }
5801 
5802 namespace {
5803 class GreedyDescentLSOperator : public LocalSearchOperator {
5804  public:
5805  explicit GreedyDescentLSOperator(std::vector<IntVar*> variables);
5806 
5807  bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override;
5808  void Start(const Assignment* assignment) override;
5809 
5810  private:
5811  int64 FindMaxDistanceToDomain(const Assignment* assignment);
5812 
5813  const std::vector<IntVar*> variables_;
5814  const Assignment* center_;
5815  int64 current_step_;
5816  // The deltas are returned in this order:
5817  // (current_step_, 0, ... 0), (-current_step_, 0, ... 0),
5818  // (0, current_step_, ... 0), (0, -current_step_, ... 0),
5819  // ...
5820  // (0, ... 0, current_step_), (0, ... 0, -current_step_).
5821  // current_direction_ keeps track what was the last returned delta.
5822  int64 current_direction_;
5823 
5824  DISALLOW_COPY_AND_ASSIGN(GreedyDescentLSOperator);
5825 };
5826 
5827 GreedyDescentLSOperator::GreedyDescentLSOperator(std::vector<IntVar*> variables)
5828  : variables_(std::move(variables)),
5829  center_(nullptr),
5830  current_step_(0),
5831  current_direction_(0) {}
5832 
5833 bool GreedyDescentLSOperator::MakeNextNeighbor(Assignment* delta,
5834  Assignment* /*deltadelta*/) {
5835  static const int64 sings[] = {1, -1};
5836  for (; 1 <= current_step_; current_step_ /= 2) {
5837  for (; current_direction_ < 2 * variables_.size();) {
5838  const int64 variable_idx = current_direction_ / 2;
5839  IntVar* const variable = variables_[variable_idx];
5840  const int64 sign_index = current_direction_ % 2;
5841  const int64 sign = sings[sign_index];
5842  const int64 offset = sign * current_step_;
5843  const int64 new_value = center_->Value(variable) + offset;
5844  ++current_direction_;
5845  if (variable->Contains(new_value)) {
5846  delta->Add(variable);
5847  delta->SetValue(variable, new_value);
5848  return true;
5849  }
5850  }
5851  current_direction_ = 0;
5852  }
5853  return false;
5854 }
5855 
5856 void GreedyDescentLSOperator::Start(const Assignment* assignment) {
5857  CHECK(assignment != nullptr);
5858  current_step_ = FindMaxDistanceToDomain(assignment);
5859  center_ = assignment;
5860 }
5861 
5862 int64 GreedyDescentLSOperator::FindMaxDistanceToDomain(
5863  const Assignment* assignment) {
5864  int64 result = kint64min;
5865  for (const IntVar* const var : variables_) {
5866  result = std::max(result, std::abs(var->Max() - assignment->Value(var)));
5867  result = std::max(result, std::abs(var->Min() - assignment->Value(var)));
5868  }
5869  return result;
5870 }
5871 } // namespace
5872 
5873 std::unique_ptr<LocalSearchOperator> RoutingModel::MakeGreedyDescentLSOperator(
5874  std::vector<IntVar*> variables) {
5875  return std::unique_ptr<LocalSearchOperator>(
5876  new GreedyDescentLSOperator(std::move(variables)));
5877 }
5878 
5880  const RoutingDimension* dimension) {
5881  CHECK(dimension != nullptr);
5882  CHECK(dimension->base_dimension() == dimension);
5883  std::function<int64(int64)> slack_guide = [dimension](int64 index) {
5884  return dimension->ShortestTransitionSlack(index);
5885  };
5886  DecisionBuilder* const guided_finalizer =
5887  MakeGuidedSlackFinalizer(dimension, slack_guide);
5888  DecisionBuilder* const slacks_finalizer =
5889  solver_->MakeSolveOnce(guided_finalizer);
5890  std::vector<IntVar*> start_cumuls(vehicles_, nullptr);
5891  for (int64 vehicle_idx = 0; vehicle_idx < vehicles_; ++vehicle_idx) {
5892  start_cumuls[vehicle_idx] = dimension->CumulVar(starts_[vehicle_idx]);
5893  }
5894  LocalSearchOperator* const hill_climber =
5895  solver_->RevAlloc(new GreedyDescentLSOperator(start_cumuls));
5897  solver_->MakeLocalSearchPhaseParameters(CostVar(), hill_climber,
5898  slacks_finalizer);
5899  Assignment* const first_solution = solver_->MakeAssignment();
5900  first_solution->Add(start_cumuls);
5901  for (IntVar* const cumul : start_cumuls) {
5902  first_solution->SetValue(cumul, cumul->Min());
5903  }
5904  DecisionBuilder* const finalizer =
5905  solver_->MakeLocalSearchPhase(first_solution, parameters);
5906  return finalizer;
5907 }
5908 } // namespace operations_research
operations_research::SavingsFilteredHeuristic::vehicle_type_curator_
std::unique_ptr< VehicleTypeCurator > vehicle_type_curator_
Definition: routing.h:3559
operations_research::RoutingModel::IndexPair
RoutingIndexPair IndexPair
Definition: routing.h:245
operations_research::IntVarFilteredHeuristic::InitializeSolution
virtual bool InitializeSolution()
Virtual method to initialize the solution.
Definition: routing.h:2939
gtl::LookupOrInsert
Collection::value_type::second_type & LookupOrInsert(Collection *const collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:207
operations_research::IntVarFilteredHeuristic
Generic filter-based heuristic applied to IntVars.
Definition: routing.h:2917
var
IntVar * var
Definition: expr_array.cc:1858
operations_research::RoutingModel::GetCostClassIndexOfVehicle
CostClassIndex GetCostClassIndexOfVehicle(int64 vehicle) const
Get the cost class index of the given vehicle.
Definition: routing.h:1226
operations_research::RoutingModel::ForEachNodeInDisjunctionWithMaxCardinalityFromIndex
void ForEachNodeInDisjunctionWithMaxCardinalityFromIndex(int64 index, int64 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:626
min
int64 min
Definition: alldiff_cst.cc:138
operations_research::RoutingModel::solver
Solver * solver() const
Returns the underlying constraint solver.
Definition: routing.h:1299
operations_research::SavingsFilteredHeuristic::SavingsContainer::ReinjectSkippedSavingsEndingAt
void ReinjectSkippedSavingsEndingAt(int64 node)
Definition: routing_search.cc:4800
integral_types.h
map_util.h
operations_research::GlobalCheapestInsertionFilteredHeuristic::NodeEntry::NodeEntry
NodeEntry(int node_to_insert, int insert_after, int vehicle)
Definition: routing_search.cc:3118
operations_research::LocalSearchFilterManager::Accept
bool Accept(LocalSearchMonitor *const monitor, const Assignment *delta, const Assignment *deltadelta, int64 objective_min, int64 objective_max)
Returns true iff all filters return true, and the sum of their accepted objectives is between objecti...
Definition: local_search.cc:3633
operations_research::IntVarLocalSearchFilter::Value
int64 Value(int index) const
Definition: constraint_solveri.h:1835
operations_research::CapSub
int64 CapSub(int64 x, int64 y)
Definition: saturated_arithmetic.h:154
operations_research::RoutingModel::MakeGreedyDescentLSOperator
static std::unique_ptr< LocalSearchOperator > MakeGreedyDescentLSOperator(std::vector< IntVar * > variables)
Perhaps move it to constraint_solver.h.
Definition: routing_search.cc:5873
operations_research::RoutingDimension::model
RoutingModel * model() const
Returns the model on which the dimension was created.
Definition: routing.h:2292
operations_research::RoutingModel::StateDependentTransit::transit_plus_identity
RangeMinMaxIndexFunction * transit_plus_identity
f(x)
Definition: routing.h:264
operations_research::CheapestInsertionFilteredHeuristic::ValuedPosition
std::pair< int64, int64 > ValuedPosition
Definition: routing.h:3039
max
int64 max
Definition: alldiff_cst.cc:139
small_ordered_set.h
bound
int64 bound
Definition: routing_search.cc:972
operations_research::BasePathFilter::BasePathFilter
BasePathFilter(const std::vector< IntVar * > &nexts, int next_domain_size)
Definition: routing_search.cc:288
operations_research::ComparatorCheapestAdditionFilteredHeuristic::ComparatorCheapestAdditionFilteredHeuristic
ComparatorCheapestAdditionFilteredHeuristic(RoutingModel *model, Solver::VariableValueComparator comparator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
Definition: routing_search.cc:4546
operations_research::RoutingFilteredHeuristic::GetStartChainEnd
int GetStartChainEnd(int vehicle) const
Returns the end of the start chain of vehicle,.
Definition: routing.h:3006
operations_research::CapProd
int64 CapProd(int64 x, int64 y)
Definition: saturated_arithmetic.h:231
operations_research::RoutingModel
Definition: routing.h:211
operations_research::RoutingModel::End
int64 End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
Definition: routing.h:1157
operations_research::SparseBitset::Set
void Set(IntegerType index)
Definition: bitset.h:805
operations_research::VehicleTypeCurator::GetCompatibleVehicleOfType
int GetCompatibleVehicleOfType(int type, std::function< bool(int)> vehicle_is_compatible)
Definition: routing_search.cc:2672
operations_research::IntVarFilteredHeuristic::Commit
bool Commit()
Commits the modifications to the current solution if these modifications are "filter-feasible",...
Definition: routing_search.cc:2810
cumul_value
int64 cumul_value
Definition: routing_search.cc:965
operations_research::IntVarFilteredDecisionBuilder::number_of_decisions
int64 number_of_decisions() const
Returns statistics from its underlying heuristic.
Definition: routing_search.cc:2723
operations_research::GlobalCheapestInsertionFilteredHeuristic::BuildSolutionInternal
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
Definition: routing_search.cc:3337
operations_research::RoutingModel::GetDeliveryIndexPairs
const std::vector< std::pair< int, int > > & GetDeliveryIndexPairs(int64 node_index) const
Same as above for deliveries.
Definition: routing.cc:1734
operations_research::RoutingFilteredHeuristic
Filter-based heuristic dedicated to routing.
Definition: routing.h:2996
operations_research::SavingsFilteredHeuristic::SavingsContainer::UpdateWithType
void UpdateWithType(int type)
Definition: routing_search.cc:4784
operations_research::GlobalDimensionCumulOptimizer
Definition: routing_lp_scheduling.h:679
operations_research::MakeTypeRegulationsFilter
IntVarLocalSearchFilter * MakeTypeRegulationsFilter(const RoutingModel &routing_model)
Definition: routing_search.cc:818
operations_research::CheapestInsertionFilteredHeuristic::penalty_evaluator_
std::function< int64(int64)> penalty_evaluator_
Definition: routing.h:3095
operations_research::RoutingModel::DisjunctionIndex
RoutingDisjunctionIndex DisjunctionIndex
Definition: routing.h:238
operations_research::ChristofidesPathSolver::SetMatchingAlgorithm
void SetMatchingAlgorithm(MatchingAlgorithm matching)
Definition: christofides.h:59
routing.h
operations_research::SavingsFilteredHeuristic::BuildRoutesFromSavings
virtual void BuildRoutesFromSavings()=0
operations_research::CheapestInsertionFilteredHeuristic::CheapestInsertionFilteredHeuristic
CheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64(int64, int64, int64)> evaluator, std::function< int64(int64)> penalty_evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
Definition: routing_search.cc:2950
gtl::STLClearObject
void STLClearObject(T *obj)
Definition: stl_util.h:123
operations_research::IntVarLocalSearchFilter::IsVarSynced
bool IsVarSynced(int index) const
Definition: constraint_solveri.h:1839
operations_research::SavingsFilteredHeuristic::SavingsContainer::SavingsContainer
SavingsContainer(const SavingsFilteredHeuristic *savings_db, int vehicle_types)
Definition: routing_search.cc:4619
operations_research::RoutingDimension::SlackVar
IntVar * SlackVar(int64 index) const
Definition: routing.h:2309
operations_research::BasePathFilter::kUnassigned
static const int64 kUnassigned
Definition: routing.h:3701
operations_research::SavingsFilteredHeuristic::SavingsParameters::max_memory_usage_bytes
double max_memory_usage_bytes
The number of neighbors considered for each node is also adapted so that the stored Savings don't use...
Definition: routing.h:3503
path_values
std::vector< int64 > path_values
Definition: routing_search.cc:967
value
int64 value
Definition: demon_profiler.cc:43
operations_research::RoutingIndexManager
Manager for any NodeIndex <-> variable index conversion.
Definition: routing_index_manager.h:48
operations_research::RoutingModel::StateDependentTransit
What follows is relevant for models with time/state dependent transits.
Definition: routing.h:262
operations_research::RoutingModel::IsEnd
bool IsEnd(int64 index) const
Returns true if 'index' represents the last node of a route.
Definition: routing.h:1161
operations_research::GlobalCheapestInsertionFilteredHeuristic::PairEntry::pickup_insert_after
int pickup_insert_after() const
Definition: routing_search.cc:3100
operations_research::LocalSearchFilterManager::Synchronize
void Synchronize(const Assignment *assignment, const Assignment *delta)
Synchronizes all filters to assignment.
Definition: local_search.cc:3678
operations_research::CheapestInsertionFilteredHeuristic::GetInsertionCostForNodeAtPosition
int64 GetInsertionCostForNodeAtPosition(int64 node_to_insert, int64 insert_after, int64 insert_before, int vehicle) const
Returns the cost of inserting 'node_to_insert' between 'insert_after' and 'insert_before' on the 'veh...
Definition: routing_search.cc:3034
operations_research::RoutingDimension::HasBreakConstraints
bool HasBreakConstraints() const
Returns true if any break interval or break distance was defined.
Definition: routing.cc:6861
small_map.h
operations_research::GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters::farthest_seeds_ratio
double farthest_seeds_ratio
The ratio of routes on which to insert farthest nodes as seeds before starting the cheapest insertion...
Definition: routing.h:3113
gtl::FindWithDefault
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:26
operations_research::SavingsFilteredHeuristic::ExtraSavingsMemoryMultiplicativeFactor
virtual double ExtraSavingsMemoryMultiplicativeFactor() const =0
operations_research::GlobalCheapestInsertionFilteredHeuristic::PairEntry::value
int64 value() const
Definition: routing_search.cc:3097
saturated_arithmetic.h
operations_research
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
Definition: dense_doubly_linked_list.h:21
AdjustablePriorityQueue::Top
T * Top()
Definition: adjustable_priority_queue.h:87
operations_research::GlobalDimensionCumulOptimizer::dimension
const RoutingDimension * dimension() const
Definition: routing_lp_scheduling.h:710
operations_research::RoutingModel::GetCostClassesCount
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
Definition: routing.h:1240
min_rank
int min_rank
Definition: alldiff_cst.cc:140
operations_research::GlobalCheapestInsertionFilteredHeuristic::PairEntry::delivery_to_insert
int delivery_to_insert() const
Definition: routing_search.cc:3101
operations_research::DimensionSchedulingStatus
DimensionSchedulingStatus
Definition: routing_lp_scheduling.h:126
operations_research::RoutingFilteredHeuristic::StopSearch
bool StopSearch() override
Returns true if the search must be stopped.
Definition: routing.h:3016
operations_research::IntVarFilteredDecisionBuilder::DebugString
std::string DebugString() const override
Definition: routing_search.cc:2731
operations_research::SavingsFilteredHeuristic::SavingsParameters::add_reverse_arcs
bool add_reverse_arcs
If add_reverse_arcs is true, the neighborhood relationships are considered symmetrically.
Definition: routing.h:3506
kint64min
static const int64 kint64min
Definition: integral_types.h:60
operations_research::SavingsFilteredHeuristic::SavingsContainer::InitializeContainer
void InitializeContainer(int64 size, int64 saving_neighbors)
Definition: routing_search.cc:4629
christofides.h
operations_research::sat::UNKNOWN
@ UNKNOWN
Definition: cp_model.pb.h:228
operations_research::SavingsFilteredHeuristic::Saving
std::pair< int64, int64 > Saving
Definition: routing.h:3520
operations_research::MakeMaxActiveVehiclesFilter
IntVarLocalSearchFilter * MakeMaxActiveVehiclesFilter(const RoutingModel &routing_model)
Definition: routing_search.cc:109
operations_research::CheapestInsertionFilteredHeuristic::GetUnperformedValue
int64 GetUnperformedValue(int64 node_to_insert) const
Returns the cost of unperforming node 'node_to_insert'.
Definition: routing_search.cc:3042
operations_research::GlobalCheapestInsertionFilteredHeuristic::PairEntry
Definition: routing_search.cc:3067
operations_research::RoutingDimension::CumulVar
IntVar * CumulVar(int64 index) const
Get the cumul, transit and slack variables for the given node (given as int64 var index).
Definition: routing.h:2306
operations_research::GlobalCheapestInsertionFilteredHeuristic::PairEntry::operator<
bool operator<(const PairEntry &other) const
Definition: routing_search.cc:3080
operations_research::SavingsFilteredHeuristic::savings_container_
std::unique_ptr< SavingsContainer< Saving > > savings_container_
Definition: routing.h:3557
operations_research::GlobalCheapestInsertionFilteredHeuristic::NodeEntry::SetHeapIndex
void SetHeapIndex(int h)
Definition: routing_search.cc:3135
operations_research::GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters::neighbors_ratio
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
Definition: routing.h:3116
int64
int64_t int64
Definition: integral_types.h:34
constraint_solveri.h
operations_research::IntVarFilteredHeuristic::SetValue
void SetValue(int64 index, int64 value)
Modifies the current solution by setting the variable of index 'index' to value 'value'.
Definition: routing.h:2950
operations_research::SavingsFilteredHeuristic::SavingsContainer::ReinjectSkippedSavingsStartingAt
void ReinjectSkippedSavingsStartingAt(int64 node)
Definition: routing_search.cc:4795
operations_research::SavingsFilteredHeuristic::GetSavingValue
int64 GetSavingValue(const Saving &saving) const
Returns the saving value from a saving.
Definition: routing.h:3542
operations_research::RoutingDimension::ShortestTransitionSlack
int64 ShortestTransitionSlack(int64 node) const
It makes sense to use the function only for self-dependent dimension.
Definition: routing_search.cc:5753
operations_research::MakeCPFeasibilityFilter
IntVarLocalSearchFilter * MakeCPFeasibilityFilter(const RoutingModel *routing_model)
Definition: routing_search.cc:2661
operations_research::RoutingFilteredHeuristic::ResetVehicleIndices
virtual void ResetVehicleIndices()
Definition: routing.h:3018
operations_research::RoutingModel::vehicles
int vehicles() const
Returns the number of vehicle routes in the model.
Definition: routing.h:1317
operations_research::GlobalCheapestInsertionFilteredHeuristic::PairEntry::pickup_to_insert
int pickup_to_insert() const
Definition: routing_search.cc:3099
operations_research::BasePathFilter::NumPaths
int NumPaths() const
Definition: routing.h:3708
operations_research::FillTravelBoundsOfVehicle
void FillTravelBoundsOfVehicle(int vehicle, const std::vector< int64 > &path, const RoutingDimension &dimension, TravelBounds *travel_bounds)
Definition: routing_breaks.cc:645
index
int index
Definition: pack.cc:508
operations_research::LocalSearchFilterManager
Filter manager: when a move is made, filters are executed to decide whether the solution is feasible ...
Definition: constraint_solveri.h:1765
operations_research::RoutingModel::Nexts
const std::vector< IntVar * > & Nexts() const
Returns all next variables of the model, such that Nexts(i) is the next variable of the node correspo...
Definition: routing.h:1175
operations_research::LocalCheapestInsertionFilteredHeuristic::BuildSolutionInternal
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
Definition: routing_search.cc:4245
operations_research::IntVarFilteredHeuristic::Value
int64 Value(int64 index) const
Returns the value of the variable of index 'index' in the last committed solution.
Definition: routing.h:2961
operations_research::LocalSearchPhaseParameters
Definition: local_search.cc:3996
operations_research::MakeVehicleAmortizedCostFilter
IntVarLocalSearchFilter * MakeVehicleAmortizedCostFilter(const RoutingModel &routing_model)
Definition: routing_search.cc:669
DEFINE_bool
DEFINE_bool(routing_strong_debug_checks, false, "Run stronger checks in debug; these stronger tests might change " "the complexity of the code in particular.")
evaluator_
std::function< int64(int64, int64)> evaluator_
Definition: search.cc:1355
operations_research::SavingsFilteredHeuristic::GetBeforeNodeFromSaving
int64 GetBeforeNodeFromSaving(const Saving &saving) const
Returns the "before node" from a saving.
Definition: routing.h:3534
operations_research::IntVarFilteredDecisionBuilder::Next
Decision * Next(Solver *solver) override
Definition: routing_search.cc:2710
operations_research::CheapestAdditionFilteredHeuristic
Filtered-base decision builder based on the addition heuristic, extending a path from its start node ...
Definition: routing.h:3403
operations_research::GlobalCheapestInsertionFilteredHeuristic::NodeEntry::set_value
void set_value(int64 value)
Definition: routing_search.cc:3138
cost
int64 cost
Definition: routing_flow.cc:130
operations_research::GlobalCheapestInsertionFilteredHeuristic::PairEntry::PairEntry
PairEntry(int pickup_to_insert, int pickup_insert_after, int delivery_to_insert, int delivery_insert_after, int vehicle)
Definition: routing_search.cc:3069
operations_research::CheapestInsertionFilteredHeuristic::evaluator_
std::function< int64(int64, int64, int64)> evaluator_
Definition: routing.h:3094
operations_research::IntVarFilteredDecisionBuilder::IntVarFilteredDecisionBuilder
IntVarFilteredDecisionBuilder(std::unique_ptr< IntVarFilteredHeuristic > heuristic)
Definition: routing_search.cc:2706
routing_lp_scheduling.h
operations_research::LocalSearchOperator
The base class for all local search operators.
Definition: constraint_solveri.h:805
operations_research::sat::INFEASIBLE
@ INFEASIBLE
Definition: cp_model.pb.h:231
operations_research::SavingsFilteredHeuristic::StartNewRouteWithBestVehicleOfType
int StartNewRouteWithBestVehicleOfType(int type, int64 before_node, int64 after_node)
Finds the best available vehicle of type "type" to start a new route to serve the arc before_node-->a...
Definition: routing_search.cc:5030
operations_research::GlobalCheapestInsertionFilteredHeuristic::NodeEntry::operator<
bool operator<(const NodeEntry &other) const
Definition: routing_search.cc:3124
operations_research::Bitset64
Definition: bitset.h:412
operations_research::ChristofidesPathSolver
Definition: christofides.h:40
operations_research::BasePathFilter::IsDisabled
bool IsDisabled() const
Definition: routing.h:3712
operations_research::MakeCumulBoundsPropagatorFilter
IntVarLocalSearchFilter * MakeCumulBoundsPropagatorFilter(const RoutingDimension &dimension)
Definition: routing_search.cc:2500
operations_research::EvaluatorCheapestAdditionFilteredHeuristic::EvaluatorCheapestAdditionFilteredHeuristic
EvaluatorCheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< int64(int64, int64)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
Definition: routing_search.cc:4505
cumuls_
const std::vector< IntVar * > cumuls_
Definition: graph_constraints.cc:670
operations_research::GlobalCheapestInsertionFilteredHeuristic::NodeEntry::GetHeapIndex
int GetHeapIndex() const
Definition: routing_search.cc:3136
cumul_value_support
int cumul_value_support
Definition: routing_search.cc:966
operations_research::RoutingModel::NextVar
IntVar * NextVar(int64 index) const
!defined(SWIGPYTHON)
Definition: routing.h:1182
operations_research::AppendDimensionCumulFilters
void AppendDimensionCumulFilters(const std::vector< RoutingDimension * > &dimensions, const RoutingSearchParameters &parameters, bool filter_objective_cost, std::vector< LocalSearchFilter * > *filters)
Definition: routing_search.cc:2112
operations_research::CPFeasibilityFilter::Accept
bool Accept(const Assignment *delta, const Assignment *deltadelta, int64 objective_min, int64 objective_max) override
Accepts a "delta" given the assignment with which the filter has been synchronized; the delta holds t...
Definition: routing_search.cc:2618
kint32max
static const int32 kint32max
Definition: integral_types.h:59
operations_research::GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters::use_neighbors_ratio_for_initialization
bool use_neighbors_ratio_for_initialization
If true, only closest neighbors (see neighbors_ratio) are considered as insertion positions during in...
Definition: routing.h:3120
operations_research::BasePathFilter::OnSynchronize
void OnSynchronize(const Assignment *delta) override
Definition: routing_search.cc:451
operations_research::SavingsFilteredHeuristic::SavingsContainer::AddNewSaving
void AddNewSaving(const Saving &saving, int64 total_cost, int64 before_node, int64 after_node, int vehicle_type)
Definition: routing_search.cc:4657
operations_research::GlobalCheapestInsertionFilteredHeuristic::NodeEntry::insert_after
int insert_after() const
Definition: routing_search.cc:3140
operations_research::RoutingModel::TransitCallback
const TransitCallback2 & TransitCallback(int callback_index) const
Definition: routing.h:406
operations_research::SavingsFilteredHeuristic::SavingsParameters
Definition: routing.h:3497
operations_research::CapAdd
int64 CapAdd(int64 x, int64 y)
Definition: saturated_arithmetic.h:124
operations_research::GlobalCheapestInsertionFilteredHeuristic::PairEntry::SetHeapIndex
void SetHeapIndex(int h)
Definition: routing_search.cc:3095
operations_research::SavingsFilteredHeuristic::SavingsContainer::Sort
void Sort()
Definition: routing_search.cc:4666
operations_research::ChristofidesFilteredHeuristic::ChristofidesFilteredHeuristic
ChristofidesFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager, bool use_minimum_matching)
Definition: routing_search.cc:5560
operations_research::RoutingModel::Size
int64 Size() const
Returns the number of next variables in the model.
Definition: routing.h:1319
operations_research::SavingsFilteredHeuristic::GetVehicleTypeFromSaving
int64 GetVehicleTypeFromSaving(const Saving &saving) const
Returns the cost class from a saving.
Definition: routing.h:3530
operations_research::GlobalCheapestInsertionFilteredHeuristic::PairEntry::set_value
void set_value(int64 value)
Definition: routing_search.cc:3098
operations_research::CPFeasibilityFilter::OnSynchronize
void OnSynchronize(const Assignment *delta) override
Definition: routing_search.cc:2626
operations_research::RoutingFilteredHeuristic::GetEndChainStart
int GetEndChainStart(int vehicle) const
Returns the start of the end chain of vehicle,.
Definition: routing.h:3008
operations_research::RoutingModel::MakeGuidedSlackFinalizer
DecisionBuilder * MakeGuidedSlackFinalizer(const RoutingDimension *dimension, std::function< int64(int64)> initializer)
The next few members are in the public section only for testing purposes.
Definition: routing_search.cc:5746
operations_research::RoutingModel::IsStart
bool IsStart(int64 index) const
Returns true if 'index' represents the first node of a route.
Definition: routing.cc:3880
operations_research::CheapestInsertionFilteredHeuristic::AppendEvaluatedPositionsAfter
void AppendEvaluatedPositionsAfter(int64 node_to_insert, int64 start, int64 next_after_start, int64 vehicle, std::vector< ValuedPosition > *valued_positions)
Helper method to the ComputeEvaluatorSortedPositions* methods.
Definition: routing_search.cc:3018
operations_research::RoutingFilteredHeuristic::MakeDisjunctionNodesUnperformed
void MakeDisjunctionNodesUnperformed(int64 node)
Make nodes in the same disjunction as 'node' unperformed.
Definition: routing_search.cc:2931
operations_research::RoutingModel::GetFixedCostOfVehicle
int64 GetFixedCostOfVehicle(int vehicle) const
Returns the route fixed cost taken into account if the route of the vehicle is not empty,...
Definition: routing.cc:1311
operations_research::IntVarFilteredHeuristic::BuildSolutionInternal
virtual bool BuildSolutionInternal()=0
Virtual method to redefine how to build a solution.
operations_research::RoutingDimension
Dimensions represent quantities accumulated at nodes along the routes.
Definition: routing.h:2288
operations_research::SavingsFilteredHeuristic::GetAfterNodeFromSaving
int64 GetAfterNodeFromSaving(const Saving &saving) const
Returns the "after node" from a saving.
Definition: routing.h:3538
operations_research::MakeVehicleVarFilter
IntVarLocalSearchFilter * MakeVehicleVarFilter(const RoutingModel &routing_model)
Definition: routing_search.cc:2442
operations_research::RoutingModel::VehicleVar
IntVar * VehicleVar(int64 index) const
Returns the vehicle variable of the node corresponding to index.
Definition: routing.h:1197
operations_research::SparseBitset::PositionsSetAtLeastOnce
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
Definition: bitset.h:815
operations_research::sat::OPTIMAL
@ OPTIMAL
Definition: cp_model.pb.h:232
operations_research::AppendTasksFromIntervals
void AppendTasksFromIntervals(const std::vector< IntervalVar * > &intervals, DisjunctivePropagator::Tasks *tasks)
Definition: routing_breaks.cc:673
operations_research::IntVarLocalSearchFilter::Size
int Size() const
Definition: constraint_solveri.h:1833
operations_research::RangeMinMaxIndexFunction::RangeMinArgument
virtual int64 RangeMinArgument(int64 from, int64 to) const =0
operations_research::MakeGlobalLPCumulFilter
IntVarLocalSearchFilter * MakeGlobalLPCumulFilter(GlobalDimensionCumulOptimizer *optimizer, bool filter_objective_cost)
Definition: routing_search.cc:2599
operations_research::LocalSearchMonitor
Definition: constraint_solveri.h:1917
operations_research::sat::Value
std::function< int64(const Model &)> Value(IntegerVariable v)
Definition: integer.h:1396
operations_research::SavingsFilteredHeuristic
Filter-based decision builder which builds a solution by using Clarke & Wright's Savings heuristic.
Definition: routing.h:3495
operations_research::CheapestInsertionFilteredHeuristic::InsertBetween
void InsertBetween(int64 node, int64 predecessor, int64 successor)
Inserts 'node' just after 'predecessor', and just before 'successor', resulting in the following subs...
Definition: routing_search.cc:3010
operations_research::GlobalCheapestInsertionFilteredHeuristic::NodeEntry::node_to_insert
int node_to_insert() const
Definition: routing_search.cc:3139
operations_research::RoutingFilteredHeuristic::RoutingFilteredHeuristic
RoutingFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager)
Definition: routing_search.cc:2850
max_rank
int max_rank
Definition: alldiff_cst.cc:141
operations_research::SavingsFilteredHeuristic::SavingsParameters::arc_coefficient
double arc_coefficient
arc_coefficient is a strictly positive parameter indicating the coefficient of the arc being consider...
Definition: routing.h:3509
operations_research::BasePathFilter
Generic path-based filter class.
Definition: routing.h:3692
AdjustablePriorityQueue::IsEmpty
bool IsEmpty() const
Definition: adjustable_priority_queue.h:127
operations_research::Queue
Definition: constraint_solver.cc:208
kint32min
static const int32 kint32min
Definition: integral_types.h:58
operations_research::RoutingDimension::global_span_cost_coefficient
int64 global_span_cost_coefficient() const
Definition: routing.h:2605
operations_research::IntVarFilteredHeuristic::ResetSolution
void ResetSolution()
Resets the data members for a new solution.
Definition: routing_search.cc:2756
operations_research::IntVarFilteredHeuristic::Var
IntVar * Var(int64 index) const
Returns the variable of index 'index'.
Definition: routing.h:2972
operations_research::IntVarLocalSearchFilter::Var
IntVar * Var(int index) const
Definition: constraint_solveri.h:1834
operations_research::CPFeasibilityFilter
This filter accepts deltas for which the assignment satisfies the constraints of the Solver.
Definition: routing.h:3766
operations_research::GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
Definition: routing.h:3108
operations_research::GlobalCheapestInsertionFilteredHeuristic::PairEntry::GetHeapIndex
int GetHeapIndex() const
Definition: routing_search.cc:3096
operations_research::RoutingFilteredHeuristic::SetVehicleIndex
virtual void SetVehicleIndex(int64 node, int vehicle)
Definition: routing.h:3017
model
GRBmodel * model
Definition: gurobi_interface.cc:195
operations_research::SavingsFilteredHeuristic::SavingsContainer::GetSaving
Saving GetSaving()
Definition: routing_search.cc:4724
operations_research::MakePathCumulFilter
IntVarLocalSearchFilter * MakePathCumulFilter(const RoutingDimension &dimension, const RoutingSearchParameters &parameters, bool propagate_own_objective_value, bool filter_objective_cost, bool can_use_lp=true)
Definition: routing_search.cc:2060
operations_research::ChristofidesPathSolver::TravelingSalesmanPath
std::vector< NodeIndex > TravelingSalesmanPath()
Definition: christofides.h:233
operations_research::AppendTasksFromPath
void AppendTasksFromPath(const std::vector< int64 > &path, const TravelBounds &travel_bounds, const RoutingDimension &dimension, DisjunctivePropagator::Tasks *tasks)
Definition: routing_breaks.cc:590
operations_research::RoutingModel::GetArcCostForClass
int64 GetArcCostForClass(int64 from_index, int64 to_index, int64 cost_class_index) const
Returns the cost of the segment between two nodes for a given cost class.
Definition: routing.cc:3912
operations_research::SparseBitset::ClearAll
void ClearAll()
Definition: bitset.h:776
operations_research::RoutingFilteredHeuristic::MakeUnassignedNodesUnperformed
void MakeUnassignedNodesUnperformed()
Make all unassigned nodes unperformed.
Definition: routing_search.cc:2940
operations_research::IntVarLocalSearchFilter
Definition: constraint_solveri.h:1813
operations_research::CheapestAdditionFilteredHeuristic::BuildSolutionInternal
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
Definition: routing_search.cc:4358
operations_research::ChristofidesPathSolver::MatchingAlgorithm
MatchingAlgorithm
Definition: christofides.h:42
operations_research::RoutingDimension::base_dimension
const RoutingDimension * base_dimension() const
Returns the parent in the dependency tree if any or nullptr otherwise.
Definition: routing.h:2532
operations_research::IntVarFilteredHeuristic::Size
int Size() const
Returns the number of variables the decision builder is trying to instantiate.
Definition: routing.h:2970
operations_research::RoutingFilteredHeuristic::model
RoutingModel * model() const
Definition: routing.h:3004
AdjustablePriorityQueue
Definition: adjustable_priority_queue.h:38
coefficient
int64 coefficient
Definition: routing_search.cc:973
operations_research::RoutingModel::StateDependentTransitCallback
const VariableIndexEvaluator2 & StateDependentTransitCallback(int callback_index) const
Definition: routing.h:414
stl_util.h
operations_research::RoutingModel::GetPickupAndDeliveryPairs
const IndexPairs & GetPickupAndDeliveryPairs() const
Returns pickup and delivery pairs currently in the model.
Definition: routing.h:733
ABSL_DIE_IF_NULL
#define ABSL_DIE_IF_NULL
Definition: base/logging.h:28
operations_research::SavingsFilteredHeuristic::SavingsParameters::neighbors_ratio
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
Definition: routing.h:3500
operations_research::BasePathFilter::Start
int64 Start(int i) const
Definition: routing.h:3709
operations_research::CheapestInsertionFilteredHeuristic::ComputeStartEndDistanceForVehicles
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...
Definition: routing_search.cc:2959
operations_research::CheapestInsertionFilteredHeuristic::InitializePriorityQueue
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,...
iterator_adaptors.h
operations_research::BasePathFilter::Accept
bool Accept(const Assignment *delta, const Assignment *deltadelta, int64 objective_min, int64 objective_max) override
Accepts a "delta" given the assignment with which the filter has been synchronized; the delta holds t...
Definition: routing_search.cc:300
operations_research::MakePickupDeliveryFilter
IntVarLocalSearchFilter * MakePickupDeliveryFilter(const RoutingModel &routing_model, const RoutingModel::IndexPairs &pairs, const std::vector< RoutingModel::PickupAndDeliveryPolicy > &vehicle_policies)
Definition: routing_search.cc:2369
AdjustablePriorityQueue::Raw
const std::vector< T * > * Raw() const
Definition: adjustable_priority_queue.h:151
operations_research::RoutingFilteredHeuristic::BuildSolutionFromRoutes
const Assignment * BuildSolutionFromRoutes(const std::function< int64(int64)> &next_accessor)
Builds a solution starting from the routes formed by the next accessor.
Definition: routing_search.cc:2778
operations_research::GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionFilteredHeuristic
GlobalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64(int64, int64, int64)> evaluator, std::function< int64(int64)> penalty_evaluator, LocalSearchFilterManager *filter_manager, GlobalCheapestInsertionParameters parameters)
Takes ownership of evaluators.
Definition: routing_search.cc:3154
operations_research::SavingsFilteredHeuristic::SavingsContainer::HasSaving
bool HasSaving()
Definition: routing_search.cc:4719
vars_
const std::vector< IntVar * > vars_
Definition: alldiff_cst.cc:43
operations_research::RoutingDimension::GetNodePrecedences
const std::vector< NodePrecedence > & GetNodePrecedences() const
Definition: routing.h:2580
operations_research::RoutingModel::IndexPairs
RoutingIndexPairs IndexPairs
Definition: routing.h:246
operations_research::CheapestInsertionFilteredHeuristic::StartEndValue
Definition: routing.h:3040
delta
int64 delta
Definition: resource.cc:1684
operations_research::GlobalCheapestInsertionFilteredHeuristic::NodeEntry::value
int64 value() const
Definition: routing_search.cc:3137
DISALLOW_COPY_AND_ASSIGN
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: macros.h:29
operations_research::RoutingModel::CostVar
IntVar * CostVar() const
Returns the global cost variable which is being minimized.
Definition: routing.h:1199
operations_research::GlobalCheapestInsertionFilteredHeuristic::PairEntry::vehicle
int vehicle() const
Definition: routing_search.cc:3103
operations_research::IntVarFilteredHeuristic::Contains
bool Contains(int64 index) const
Returns true if the variable of index 'index' is in the current solution.
Definition: routing.h:2965
operations_research::GlobalCheapestInsertionFilteredHeuristic::NodeEntry
Definition: routing_search.cc:3116
operations_research::GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters::is_sequential
bool is_sequential
Whether the routes are constructed sequentially or in parallel.
Definition: routing.h:3110
gtl::small_map
Definition: small_map.h:19
operations_research::RoutingModel::MakeSelfDependentDimensionFinalizer
DecisionBuilder * MakeSelfDependentDimensionFinalizer(const RoutingDimension *dimension)
SWIG
Definition: routing_search.cc:5879
gtl::ITIVector
Definition: int_type_indexed_vector.h:76
capacity
int64 capacity
Definition: routing_flow.cc:129
next
Block * next
Definition: constraint_solver.cc:667
operations_research::GlobalCheapestInsertionFilteredHeuristic::NodeEntry::vehicle
int vehicle() const
Definition: routing_search.cc:3141
operations_research::kUnassigned
static const int kUnassigned
Definition: routing.cc:752
operations_research::IntVarFilteredHeuristic::assignment_
Assignment *const assignment_
Definition: routing.h:2976
operations_research::IntVarFilteredHeuristic::IntVarFilteredHeuristic
IntVarFilteredHeuristic(Solver *solver, const std::vector< IntVar * > &vars, LocalSearchFilterManager *filter_manager)
Definition: routing_search.cc:2740
operations_research::IntVarFilteredDecisionBuilder::number_of_rejects
int64 number_of_rejects() const
Definition: routing_search.cc:2727
operations_research::SavingsFilteredHeuristic::SavingsContainer::GetSortedSavingsForVehicleType
const std::vector< Saving > & GetSortedSavingsForVehicleType(int type)
Definition: routing_search.cc:4789
operations_research::SavingsFilteredHeuristic::BuildSolutionInternal
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
Definition: routing_search.cc:5016
operations_research::SavingsFilteredHeuristic::~SavingsFilteredHeuristic
~SavingsFilteredHeuristic() override
Definition: routing_search.cc:5014
operations_research::RoutingModel::Start
int64 Start(int vehicle) const
Model inspection.
Definition: routing.h:1155
operations_research::CheapestInsertionFilteredHeuristic::Seed
std::pair< StartEndValue, int > Seed
Definition: routing.h:3049
gtl::small_ordered_set
Definition: small_ordered_set.h:19
operations_research::ChristofidesFilteredHeuristic::BuildSolutionInternal
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
Definition: routing_search.cc:5567
operations_research::CheapestInsertionFilteredHeuristic
Definition: routing.h:3029
operations_research::SparseBitset::SparseClearAll
void SparseClearAll()
Definition: bitset.h:772
operations_research::SavingsFilteredHeuristic::SavingsContainer::Update
void Update(bool update_best_saving, int type=-1)
Definition: routing_search.cc:4760
parameters
SatParameters parameters
Definition: cp_model_fz_solver.cc:107
operations_research::IntVarFilteredHeuristic::BuildSolution
Assignment *const BuildSolution()
Builds a solution.
Definition: routing_search.cc:2766
operations_research::MakeNodeDisjunctionFilter
IntVarLocalSearchFilter * MakeNodeDisjunctionFilter(const RoutingModel &routing_model)
Definition: routing_search.cc:280
name
const std::string name
Definition: default_search.cc:807
operations_research::IntVarLocalSearchFilter::FindIndex
bool FindIndex(IntVar *const var, int64 *index) const
Definition: constraint_solveri.h:1822
operations_research::GlobalCheapestInsertionFilteredHeuristic::PairEntry::delivery_insert_after
int delivery_insert_after() const
Definition: routing_search.cc:3102
operations_research::CheapestAdditionFilteredHeuristic::CheapestAdditionFilteredHeuristic
CheapestAdditionFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager)
Definition: routing_search.cc:4354
bitset.h
kint64max
static const int64 kint64max
Definition: integral_types.h:62
operations_research::RoutingModel::GetPickupIndexPairs
const std::vector< std::pair< int, int > > & GetPickupIndexPairs(int64 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:1728
operations_research::LocalCheapestInsertionFilteredHeuristic::LocalCheapestInsertionFilteredHeuristic
LocalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64(int64, int64, int64)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
Definition: routing_search.cc:4232
gtl::ContainsKey
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:170
operations_research::IntVarFilteredHeuristic::SynchronizeFilters
void SynchronizeFilters()
Synchronizes filters with an assignment (the current solution).
Definition: routing_search.cc:2838