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