1 // Copyright 2010-2021 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 #include "ortools/constraint_solver/routing.h"
15 
16 #include <limits.h>
17 
18 #include <algorithm>
19 #include <cmath>
20 #include <cstddef>
21 #include <cstdint>
22 #include <cstring>
23 #include <deque>
24 #include <functional>
25 #include <iterator>
26 #include <limits>
27 #include <map>
28 #include <memory>
29 #include <set>
30 #include <string>
31 #include <tuple>
32 #include <type_traits>
33 #include <utility>
34 #include <vector>
35 
36 #include "absl/container/flat_hash_map.h"
37 #include "absl/container/flat_hash_set.h"
38 #include "absl/flags/flag.h"
39 #include "absl/functional/bind_front.h"
40 #include "absl/memory/memory.h"
41 #include "absl/meta/type_traits.h"
42 #include "absl/status/statusor.h"
43 #include "absl/strings/str_cat.h"
44 #include "absl/strings/str_format.h"
45 #include "absl/strings/str_join.h"
46 #include "absl/strings/string_view.h"
47 #include "absl/time/time.h"
48 #include "ortools/base/int_type.h"
49 #include "ortools/base/integral_types.h"
50 #include "ortools/base/logging.h"
51 #include "ortools/base/map_util.h"
52 #include "ortools/base/mathutil.h"
53 #include "ortools/base/protoutil.h"
54 #include "ortools/base/stl_util.h"
55 #include "ortools/base/strong_vector.h"
56 #include "ortools/base/thorough_hash.h"
57 #include "ortools/constraint_solver/constraint_solver.h"
58 #include "ortools/constraint_solver/constraint_solveri.h"
59 #include "ortools/constraint_solver/routing_enums.pb.h"
60 #include "ortools/constraint_solver/routing_filters.h"
61 #include "ortools/constraint_solver/routing_index_manager.h"
62 #include "ortools/constraint_solver/routing_lp_scheduling.h"
63 #include "ortools/constraint_solver/routing_neighborhoods.h"
64 #include "ortools/constraint_solver/routing_parameters.h"
65 #include "ortools/constraint_solver/routing_parameters.pb.h"
66 #include "ortools/constraint_solver/routing_search.h"
67 #include "ortools/constraint_solver/routing_types.h"
68 #include "ortools/constraint_solver/solver_parameters.pb.h"
69 #include "ortools/graph/connected_components.h"
70 #include "ortools/graph/ebert_graph.h"
71 #include "ortools/graph/graph.h"
72 #include "ortools/graph/linear_assignment.h"
73 #include "ortools/util/bitset.h"
74 #include "ortools/util/optional_boolean.pb.h"
75 #include "ortools/util/piecewise_linear_function.h"
76 #include "ortools/util/range_query_function.h"
77 #include "ortools/util/saturated_arithmetic.h"
78 #include "ortools/util/sorted_interval_list.h"
79 #include "ortools/util/stats.h"
80 
81 namespace operations_research {
82 class Cross;
83 class Exchange;
84 class ExtendedSwapActiveOperator;
85 class LocalSearchPhaseParameters;
86 class MakeActiveAndRelocate;
87 class MakeActiveOperator;
88 class MakeChainInactiveOperator;
89 class MakeInactiveOperator;
90 class Relocate;
91 class RelocateAndMakeActiveOperator;
92 class SwapActiveOperator;
93 class TwoOpt;
94 }  // namespace operations_research
95 
96 // Trace settings
97 
98 // TODO(user): Move most of the following settings to a model parameter
99 // proto.
100 
101 namespace operations_research {
102 
103 namespace {
104 using ResourceGroup = RoutingModel::ResourceGroup;
105 
106 // A decision builder which tries to assign values to variables as close as
107 // possible to target values first.
108 // TODO(user): Move to CP solver.
109 class SetValuesFromTargets : public DecisionBuilder {
110  public:
SetValuesFromTargets(std::vector<IntVar * > variables,std::vector<int64_t> targets)111   SetValuesFromTargets(std::vector<IntVar*> variables,
112                        std::vector<int64_t> targets)
113       : variables_(std::move(variables)),
114         targets_(std::move(targets)),
115         index_(0),
116         steps_(variables_.size(), 0) {
117     DCHECK_EQ(variables_.size(), targets_.size());
118   }
Next(Solver * const solver)119   Decision* Next(Solver* const solver) override {
120     int index = index_.Value();
121     while (index < variables_.size() && variables_[index]->Bound()) {
122       ++index;
123     }
124     index_.SetValue(solver, index);
125     if (index >= variables_.size()) return nullptr;
126     const int64_t variable_min = variables_[index]->Min();
127     const int64_t variable_max = variables_[index]->Max();
128     // Target can be before, inside, or after the variable range.
129     // We do a trichotomy on this for clarity.
130     if (targets_[index] <= variable_min) {
131       return solver->MakeAssignVariableValue(variables_[index], variable_min);
132     } else if (targets_[index] >= variable_max) {
133       return solver->MakeAssignVariableValue(variables_[index], variable_max);
134     } else {
135       int64_t step = steps_[index];
136       int64_t value = CapAdd(targets_[index], step);
137       // If value is out of variable's range, we can remove the interval of
138       // values already explored (which can make the solver fail) and
139       // recall Next() to get back into the trichotomy above.
140       if (value < variable_min || variable_max < value) {
141         step = GetNextStep(step);
142         value = CapAdd(targets_[index], step);
143         if (step > 0) {
144           // Values in [variable_min, value) were already explored.
145           variables_[index]->SetMin(value);
146         } else {
147           // Values in (value, variable_max] were already explored.
148           variables_[index]->SetMax(value);
149         }
150         return Next(solver);
151       }
152       steps_.SetValue(solver, index, GetNextStep(step));
153       return solver->MakeAssignVariableValueOrDoNothing(variables_[index],
154                                                         value);
155     }
156   }
157 
158  private:
GetNextStep(int64_t step) const159   int64_t GetNextStep(int64_t step) const {
160     return (step > 0) ? -step : CapSub(1, step);
161   }
162   const std::vector<IntVar*> variables_;
163   const std::vector<int64_t> targets_;
164   Rev<int> index_;
165   RevArray<int64_t> steps_;
166 };
167 
168 }  // namespace
169 
MakeSetValuesFromTargets(Solver * solver,std::vector<IntVar * > variables,std::vector<int64_t> targets)170 DecisionBuilder* MakeSetValuesFromTargets(Solver* solver,
171                                           std::vector<IntVar*> variables,
172                                           std::vector<int64_t> targets) {
173   return solver->RevAlloc(
174       new SetValuesFromTargets(std::move(variables), std::move(targets)));
175 }
176 
177 namespace {
178 
DimensionFixedTransitsEqualTransitEvaluatorForVehicle(const RoutingDimension & dimension,int vehicle)179 bool DimensionFixedTransitsEqualTransitEvaluatorForVehicle(
180     const RoutingDimension& dimension, int vehicle) {
181   const RoutingModel* const model = dimension.model();
182   int node = model->Start(vehicle);
183   while (!model->IsEnd(node)) {
184     if (!model->NextVar(node)->Bound()) {
185       return false;
186     }
187     const int next = model->NextVar(node)->Value();
188     if (dimension.transit_evaluator(vehicle)(node, next) !=
189         dimension.FixedTransitVar(node)->Value()) {
190       return false;
191     }
192     node = next;
193   }
194   return true;
195 }
196 
DimensionFixedTransitsEqualTransitEvaluators(const RoutingDimension & dimension)197 bool DimensionFixedTransitsEqualTransitEvaluators(
198     const RoutingDimension& dimension) {
199   for (int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
200     if (!DimensionFixedTransitsEqualTransitEvaluatorForVehicle(dimension,
201                                                                vehicle)) {
202       return false;
203     }
204   }
205   return true;
206 }
207 
208 // Concatenates cumul_values and break_values into 'values', and generates the
209 // corresponding 'variables' vector.
ConcatenateRouteCumulAndBreakVarAndValues(const RoutingDimension & dimension,int vehicle,const std::vector<int64_t> & cumul_values,const std::vector<int64_t> & break_values,std::vector<IntVar * > * variables,std::vector<int64_t> * values)210 void ConcatenateRouteCumulAndBreakVarAndValues(
211     const RoutingDimension& dimension, int vehicle,
212     const std::vector<int64_t>& cumul_values,
213     const std::vector<int64_t>& break_values, std::vector<IntVar*>* variables,
214     std::vector<int64_t>* values) {
215   *values = cumul_values;
216   variables->clear();
217   const RoutingModel& model = *dimension.model();
218   {
219     int current = model.Start(vehicle);
220     while (true) {
221       variables->push_back(dimension.CumulVar(current));
222       if (!model.IsEnd(current)) {
223         current = model.NextVar(current)->Value();
224       } else {
225         break;
226       }
227     }
228   }
229   // Setting the cumuls of path start/end first is more efficient than
230   // setting the cumuls in order of path appearance, because setting start
231   // and end cumuls gives an opportunity to fix all cumuls with two
232   // decisions instead of |path| decisions.
233   // To this effect, we put end cumul just after the start cumul.
234   std::swap(variables->at(1), variables->back());
235   std::swap(values->at(1), values->back());
236   if (dimension.HasBreakConstraints()) {
237     for (IntervalVar* interval :
238          dimension.GetBreakIntervalsOfVehicle(vehicle)) {
239       variables->push_back(interval->SafeStartExpr(0)->Var());
240       variables->push_back(interval->SafeEndExpr(0)->Var());
241     }
242     values->insert(values->end(), break_values.begin(), break_values.end());
243   }
244   // Value kint64min signals an unoptimized variable, set to min instead.
245   for (int j = 0; j < values->size(); ++j) {
246     if (values->at(j) == std::numeric_limits<int64_t>::min()) {
247       values->at(j) = variables->at(j)->Min();
248     }
249   }
250   DCHECK_EQ(variables->size(), values->size());
251 }
252 
253 class SetCumulsFromLocalDimensionCosts : public DecisionBuilder {
254  public:
SetCumulsFromLocalDimensionCosts(const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>> * local_optimizers,const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>> * local_mp_optimizers,const std::function<bool (const RoutingDimension *)> & set_dimension_cumuls,SearchMonitor * monitor,bool optimize_and_pack=false)255   SetCumulsFromLocalDimensionCosts(
256       const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>*
257           local_optimizers,
258       const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>*
259           local_mp_optimizers,
260       const std::function<bool(const RoutingDimension*)>& set_dimension_cumuls,
261       SearchMonitor* monitor, bool optimize_and_pack = false)
262       : monitor_(monitor), optimize_and_pack_(optimize_and_pack) {
263     for (int i = 0; i < local_optimizers->size(); i++) {
264       if (!set_dimension_cumuls(local_optimizers->at(i)->dimension())) {
265         continue;
266       }
267       const auto& optimizer = local_optimizers->at(i);
268       local_optimizers_.push_back(optimizer.get());
269       local_mp_optimizers_.push_back(local_mp_optimizers->at(i).get());
270 
271       const RoutingDimension* const dimension = optimizer->dimension();
272       const std::vector<int>& resource_groups =
273           dimension->model()->GetDimensionResourceGroupIndices(dimension);
274       DCHECK_LE(resource_groups.size(), optimize_and_pack ? 1 : 0);
275       resource_group_index_.push_back(
276           resource_groups.empty() ? -1 : resource_groups[0]);
277     }
278   }
Next(Solver * const solver)279   Decision* Next(Solver* const solver) override {
280     // The following boolean variable indicates if the solver should fail, in
281     // order to postpone the Fail() call until after the internal for loop, so
282     // there are no memory leaks related to the cumul_values vector.
283     bool should_fail = false;
284     for (int i = 0; i < local_optimizers_.size(); ++i) {
285       LocalDimensionCumulOptimizer* const local_optimizer =
286           local_optimizers_[i];
287       const RoutingDimension& dimension = *local_optimizer->dimension();
288       RoutingModel* const model = dimension.model();
289       const auto next = [model](int64_t n) {
290         return model->NextVar(n)->Value();
291       };
292       const int rg_index = resource_group_index_[i];
293       const auto compute_cumul_values =
294           [this, &model, &next, rg_index](
295               LocalDimensionCumulOptimizer* optimizer, int vehicle,
296               std::vector<int64_t>* cumul_values,
297               std::vector<int64_t>* break_start_end_values) {
298             if (optimize_and_pack_) {
299               const int resource_index =
300                   rg_index < 0 ? -1
301                                : model->ResourceVar(vehicle, rg_index)->Value();
302               const Resource* const resource =
303                   resource_index < 0
304                       ? nullptr
305                       : &model->GetResourceGroup(rg_index)->GetResource(
306                             resource_index);
307               return optimizer->ComputePackedRouteCumuls(
308                   vehicle, next, resource, cumul_values,
309                   break_start_end_values);
310             } else {
311               return optimizer->ComputeRouteCumuls(vehicle, next, cumul_values,
312                                                    break_start_end_values);
313             }
314           };
315       for (int vehicle = 0; vehicle < model->vehicles(); ++vehicle) {
316         solver->TopPeriodicCheck();
317         // TODO(user): Investigate if we should skip unused vehicles.
318         DCHECK(DimensionFixedTransitsEqualTransitEvaluatorForVehicle(dimension,
319                                                                      vehicle));
320         const bool vehicle_has_break_constraint =
321             dimension.HasBreakConstraints() &&
322             !dimension.GetBreakIntervalsOfVehicle(vehicle).empty();
323         LocalDimensionCumulOptimizer* const optimizer =
324             vehicle_has_break_constraint ? local_mp_optimizers_[i]
325                                          : local_optimizer;
326         DCHECK(optimizer != nullptr);
327         std::vector<int64_t> cumul_values;
328         std::vector<int64_t> break_start_end_values;
329         const DimensionSchedulingStatus status = compute_cumul_values(
330             optimizer, vehicle, &cumul_values, &break_start_end_values);
331         if (status == DimensionSchedulingStatus::INFEASIBLE) {
332           should_fail = true;
333           break;
334         }
335         // If relaxation is not feasible, try the MILP optimizer.
336         if (status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY) {
337           cumul_values.clear();
338           break_start_end_values.clear();
339           DCHECK(local_mp_optimizers_[i] != nullptr);
340           if (compute_cumul_values(local_mp_optimizers_[i], vehicle,
341                                    &cumul_values, &break_start_end_values) ==
342               DimensionSchedulingStatus::INFEASIBLE) {
343             should_fail = true;
344             break;
345           }
346         } else {
347           DCHECK(status == DimensionSchedulingStatus::OPTIMAL);
348         }
349         // Concatenate cumul_values and break_start_end_values into cp_values,
350         // generate corresponding cp_variables vector.
351         std::vector<IntVar*> cp_variables;
352         std::vector<int64_t> cp_values;
353         ConcatenateRouteCumulAndBreakVarAndValues(
354             dimension, vehicle, cumul_values, break_start_end_values,
355             &cp_variables, &cp_values);
356         if (!solver->SolveAndCommit(
357                 MakeSetValuesFromTargets(solver, std::move(cp_variables),
358                                          std::move(cp_values)),
359                 monitor_)) {
360           should_fail = true;
361           break;
362         }
363       }
364       if (should_fail) {
365         break;
366       }
367     }
368     if (should_fail) {
369       solver->Fail();
370     }
371     return nullptr;
372   }
373 
374  private:
375   using Resource = RoutingModel::ResourceGroup::Resource;
376 
377   std::vector<LocalDimensionCumulOptimizer*> local_optimizers_;
378   std::vector<LocalDimensionCumulOptimizer*> local_mp_optimizers_;
379   // Stores the resource group index of dimensions in local_[mp_]optimizers_.
380   std::vector<int> resource_group_index_;
381   SearchMonitor* const monitor_;
382   const bool optimize_and_pack_;
383 };
384 
385 class SetCumulsFromGlobalDimensionCosts : public DecisionBuilder {
386  public:
SetCumulsFromGlobalDimensionCosts(const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>> * global_optimizers,const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>> * global_mp_optimizers,SearchMonitor * monitor,bool optimize_and_pack=false)387   SetCumulsFromGlobalDimensionCosts(
388       const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>*
389           global_optimizers,
390       const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>*
391           global_mp_optimizers,
392       SearchMonitor* monitor, bool optimize_and_pack = false)
393       : monitor_(monitor), optimize_and_pack_(optimize_and_pack) {
394     for (int i = 0; i < global_optimizers->size(); i++) {
395       global_optimizers_.push_back(global_optimizers->at(i).get());
396       global_mp_optimizers_.push_back(global_mp_optimizers->at(i).get());
397     }
398   }
Next(Solver * const solver)399   Decision* Next(Solver* const solver) override {
400     // The following boolean variable indicates if the solver should fail, in
401     // order to postpone the Fail() call until after the for loop, so there are
402     // no memory leaks related to the cumul_values vector.
403     bool should_fail = false;
404     for (int i = 0; i < global_optimizers_.size(); ++i) {
405       GlobalDimensionCumulOptimizer* const global_optimizer =
406           global_optimizers_[i];
407       const RoutingDimension* dimension = global_optimizer->dimension();
408       RoutingModel* const model = dimension->model();
409 
410       const auto next = [model](int64_t n) {
411         return model->NextVar(n)->Value();
412       };
413 
414       DCHECK(DimensionFixedTransitsEqualTransitEvaluators(*dimension));
415 
416       std::vector<int64_t> cumul_values;
417       std::vector<int64_t> break_start_end_values;
418       std::vector<std::vector<int>> resource_indices_per_group;
419       const DimensionSchedulingStatus status =
420           !model->GetDimensionResourceGroupIndices(dimension).empty()
421               ? DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY
422           : optimize_and_pack_
423               ? global_optimizer->ComputePackedCumuls(
424                     next, &cumul_values, &break_start_end_values,
425                     &resource_indices_per_group)
426               : global_optimizer->ComputeCumuls(next, &cumul_values,
427                                                 &break_start_end_values,
428                                                 &resource_indices_per_group);
429 
430       if (status == DimensionSchedulingStatus::INFEASIBLE) {
431         should_fail = true;
432         break;
433       }
434       // If relaxation is not feasible, try the MILP optimizer.
435       if (status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY) {
436         cumul_values.clear();
437         break_start_end_values.clear();
438         DCHECK(global_mp_optimizers_[i] != nullptr);
439         const DimensionSchedulingStatus mp_status =
440             optimize_and_pack_
441                 ? global_mp_optimizers_[i]->ComputePackedCumuls(
442                       next, &cumul_values, &break_start_end_values,
443                       &resource_indices_per_group)
444                 : global_mp_optimizers_[i]->ComputeCumuls(
445                       next, &cumul_values, &break_start_end_values,
446                       &resource_indices_per_group);
447         if (mp_status != DimensionSchedulingStatus::OPTIMAL) {
448           should_fail = true;
449           break;
450         }
451       } else {
452         DCHECK(status == DimensionSchedulingStatus::OPTIMAL);
453       }
454       // Concatenate cumul_values and break_start_end_values into cp_values,
455       // generate corresponding cp_variables vector.
456       std::vector<IntVar*> cp_variables = dimension->cumuls();
457       std::vector<int64_t> cp_values;
458       std::swap(cp_values, cumul_values);
459       if (dimension->HasBreakConstraints()) {
460         const int num_vehicles = model->vehicles();
461         for (int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
462           for (IntervalVar* interval :
463                dimension->GetBreakIntervalsOfVehicle(vehicle)) {
464             cp_variables.push_back(interval->SafeStartExpr(0)->Var());
465             cp_variables.push_back(interval->SafeEndExpr(0)->Var());
466           }
467         }
468         cp_values.insert(cp_values.end(), break_start_end_values.begin(),
469                          break_start_end_values.end());
470       }
471       for (int rg_index : model->GetDimensionResourceGroupIndices(dimension)) {
472         const std::vector<int>& resource_values =
473             resource_indices_per_group[rg_index];
474         DCHECK(!resource_values.empty());
475         cp_values.insert(cp_values.end(), resource_values.begin(),
476                          resource_values.end());
477         const std::vector<IntVar*>& resource_vars =
478             model->ResourceVars(rg_index);
479         DCHECK_EQ(resource_vars.size(), resource_values.size());
480         cp_variables.insert(cp_variables.end(), resource_vars.begin(),
481                             resource_vars.end());
482       }
483       // Value kint64min signals an unoptimized variable, set to min instead.
484       for (int j = 0; j < cp_values.size(); ++j) {
485         if (cp_values[j] == std::numeric_limits<int64_t>::min()) {
486           cp_values[j] = cp_variables[j]->Min();
487         }
488       }
489       if (!solver->SolveAndCommit(
490               MakeSetValuesFromTargets(solver, std::move(cp_variables),
491                                        std::move(cp_values)),
492               monitor_)) {
493         should_fail = true;
494         break;
495       }
496     }
497     if (should_fail) {
498       solver->Fail();
499     }
500     return nullptr;
501   }
502 
503  private:
504   std::vector<GlobalDimensionCumulOptimizer*> global_optimizers_;
505   std::vector<GlobalDimensionCumulOptimizer*> global_mp_optimizers_;
506   SearchMonitor* const monitor_;
507   const bool optimize_and_pack_;
508 };
509 
510 class SetCumulsFromResourceAssignmentCosts : public DecisionBuilder {
511  public:
SetCumulsFromResourceAssignmentCosts(LocalDimensionCumulOptimizer * optimizer,LocalDimensionCumulOptimizer * mp_optimizer,SearchMonitor * monitor)512   SetCumulsFromResourceAssignmentCosts(
513       LocalDimensionCumulOptimizer* optimizer,
514       LocalDimensionCumulOptimizer* mp_optimizer, SearchMonitor* monitor)
515       : model_(*optimizer->dimension()->model()),
516         dimension_(*optimizer->dimension()),
517         rg_index_(model_.GetDimensionResourceGroupIndex(&dimension_)),
518         resource_group_(*model_.GetResourceGroup(rg_index_)),
519         resource_assignment_optimizer_(&resource_group_, optimizer,
520                                        mp_optimizer),
521         monitor_(monitor) {}
522 
Next(Solver * const solver)523   Decision* Next(Solver* const solver) override {
524     bool should_fail = false;
525     {
526       const int num_vehicles = model_.vehicles();
527       std::vector<std::vector<int64_t>> assignment_costs(num_vehicles);
528       std::vector<std::vector<std::vector<int64_t>>> cumul_values(num_vehicles);
529       std::vector<std::vector<std::vector<int64_t>>> break_values(num_vehicles);
530 
531       const auto next = [&model = model_](int64_t n) {
532         return model.NextVar(n)->Value();
533       };
534       DCHECK(DimensionFixedTransitsEqualTransitEvaluators(dimension_));
535 
536       for (int v : resource_group_.GetVehiclesRequiringAResource()) {
537         if (!resource_assignment_optimizer_.ComputeAssignmentCostsForVehicle(
538                 v, next, /*optimize_vehicle_costs*/ true, &assignment_costs[v],
539                 &cumul_values[v], &break_values[v])) {
540           should_fail = true;
541           break;
542         }
543       }
544 
545       std::vector<int> resource_indices;
546       should_fail = should_fail ||
547                     resource_assignment_optimizer_.ComputeBestAssignmentCost(
548                         assignment_costs, assignment_costs,
549                         [](int) { return true; }, &resource_indices) < 0;
550 
551       if (!should_fail) {
552         DCHECK_EQ(resource_indices.size(), num_vehicles);
553         const int num_resources = resource_group_.Size();
554         for (int v : resource_group_.GetVehiclesRequiringAResource()) {
555           if (next(model_.Start(v)) == model_.End(v) &&
556               !model_.IsVehicleUsedWhenEmpty(v)) {
557             continue;
558           }
559           const int resource_index = resource_indices[v];
560           DCHECK_GE(resource_index, 0);
561           DCHECK_EQ(cumul_values[v].size(), num_resources);
562           DCHECK_EQ(break_values[v].size(), num_resources);
563           const std::vector<int64_t>& optimal_cumul_values =
564               cumul_values[v][resource_index];
565           const std::vector<int64_t>& optimal_break_values =
566               break_values[v][resource_index];
567           std::vector<IntVar*> cp_variables;
568           std::vector<int64_t> cp_values;
569           ConcatenateRouteCumulAndBreakVarAndValues(
570               dimension_, v, optimal_cumul_values, optimal_break_values,
571               &cp_variables, &cp_values);
572 
573           const std::vector<IntVar*>& resource_vars =
574               model_.ResourceVars(rg_index_);
575           DCHECK_EQ(resource_vars.size(), resource_indices.size());
576           cp_variables.insert(cp_variables.end(), resource_vars.begin(),
577                               resource_vars.end());
578           cp_values.insert(cp_values.end(), resource_indices.begin(),
579                            resource_indices.end());
580           if (!solver->SolveAndCommit(
581                   MakeSetValuesFromTargets(solver, std::move(cp_variables),
582                                            std::move(cp_values)),
583                   monitor_)) {
584             should_fail = true;
585             break;
586           }
587         }
588       }
589     }
590     if (should_fail) {
591       solver->Fail();
592     }
593     return nullptr;
594   }
595 
596  private:
597   const RoutingModel& model_;
598   const RoutingDimension& dimension_;
599   const int rg_index_;
600   const ResourceGroup& resource_group_;
601   ResourceAssignmentOptimizer resource_assignment_optimizer_;
602   SearchMonitor* const monitor_;
603 };
604 
605 }  // namespace
606 
PackCumulsOfOptimizerDimensionsFromAssignment(const Assignment * original_assignment,absl::Duration duration_limit)607 const Assignment* RoutingModel::PackCumulsOfOptimizerDimensionsFromAssignment(
608     const Assignment* original_assignment, absl::Duration duration_limit) {
609   CHECK(closed_);
610   if (original_assignment == nullptr) return nullptr;
611   if (duration_limit <= absl::ZeroDuration()) return original_assignment;
612   if (global_dimension_optimizers_.empty() &&
613       local_dimension_optimizers_.empty()) {
614     DCHECK(local_dimension_mp_optimizers_.empty());
615     return original_assignment;
616   }
617   RegularLimit* const limit = GetOrCreateLimit();
618   limit->UpdateLimits(duration_limit, std::numeric_limits<int64_t>::max(),
619                       std::numeric_limits<int64_t>::max(),
620                       std::numeric_limits<int64_t>::max());
621 
622   // Initialize the packed_assignment with the Next values in the
623   // original_assignment.
624   Assignment* packed_assignment = solver_->MakeAssignment();
625   packed_assignment->Add(Nexts());
626   // Also keep the Resource values for dimensions with a single resource group.
627   for (const RoutingDimension* const dimension : dimensions_) {
628     const std::vector<int>& resource_groups =
629         GetDimensionResourceGroupIndices(dimension);
630     if (resource_groups.size() == 1) {
631       DCHECK_NE(GetMutableLocalCumulOptimizer(*dimension), nullptr);
632       packed_assignment->Add(resource_vars_[resource_groups[0]]);
633     }
634   }
635   packed_assignment->CopyIntersection(original_assignment);
636 
637   std::vector<DecisionBuilder*> decision_builders;
638   decision_builders.push_back(solver_->MakeRestoreAssignment(preassignment_));
639   decision_builders.push_back(
640       solver_->MakeRestoreAssignment(packed_assignment));
641   decision_builders.push_back(
642       solver_->RevAlloc(new SetCumulsFromLocalDimensionCosts(
643           &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
644           [this](const RoutingDimension* dim) {
645             // Don't set cumuls of dimensions with a global optimizer.
646             return GetMutableGlobalCumulOptimizer(*dim) == nullptr;
647           },
648           GetOrCreateLargeNeighborhoodSearchLimit(),
649           /*optimize_and_pack=*/true)));
650   decision_builders.push_back(
651       solver_->RevAlloc(new SetCumulsFromGlobalDimensionCosts(
652           &global_dimension_optimizers_, &global_dimension_mp_optimizers_,
653           GetOrCreateLargeNeighborhoodSearchLimit(),
654           /*optimize_and_pack=*/true)));
655   decision_builders.push_back(
656       CreateFinalizerForMinimizedAndMaximizedVariables());
657 
658   DecisionBuilder* restore_pack_and_finalize =
659       solver_->Compose(decision_builders);
660   solver_->Solve(restore_pack_and_finalize,
661                  packed_dimensions_assignment_collector_, limit);
662 
663   if (packed_dimensions_assignment_collector_->solution_count() != 1) {
664     LOG(ERROR) << "The given assignment is not valid for this model, or cannot "
665                   "be packed.";
666     return nullptr;
667   }
668 
669   packed_assignment->Copy(original_assignment);
670   packed_assignment->CopyIntersection(
671       packed_dimensions_assignment_collector_->solution(0));
672 
673   return packed_assignment;
674 }
675 
SetSweepArranger(SweepArranger * sweep_arranger)676 void RoutingModel::SetSweepArranger(SweepArranger* sweep_arranger) {
677   sweep_arranger_.reset(sweep_arranger);
678 }
679 
sweep_arranger() const680 SweepArranger* RoutingModel::sweep_arranger() const {
681   return sweep_arranger_.get();
682 }
683 
684 namespace {
685 // Constraint which ensures that var != values.
686 class DifferentFromValues : public Constraint {
687  public:
DifferentFromValues(Solver * solver,IntVar * var,std::vector<int64_t> values)688   DifferentFromValues(Solver* solver, IntVar* var, std::vector<int64_t> values)
689       : Constraint(solver), var_(var), values_(std::move(values)) {}
Post()690   void Post() override {}
InitialPropagate()691   void InitialPropagate() override { var_->RemoveValues(values_); }
DebugString() const692   std::string DebugString() const override { return "DifferentFromValues"; }
Accept(ModelVisitor * const visitor) const693   void Accept(ModelVisitor* const visitor) const override {
694     visitor->BeginVisitConstraint(RoutingModelVisitor::kRemoveValues, this);
695     visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
696                                                {var_});
697     visitor->VisitIntegerArrayArgument(ModelVisitor::kValuesArgument, values_);
698     visitor->EndVisitConstraint(RoutingModelVisitor::kRemoveValues, this);
699   }
700 
701  private:
702   IntVar* const var_;
703   const std::vector<int64_t> values_;
704 };
705 
706 // Set of "light" constraints, well-suited for use within Local Search.
707 // These constraints are "checking" constraints, only triggered on WhenBound
708 // events. The provide very little (or no) domain filtering.
709 // TODO(user): Move to core constraintsolver library.
710 
711 // Light one-dimension function-based element constraint ensuring:
712 // var == values(index).
713 // Doesn't perform bound reduction of the resulting variable until the index
714 // variable is bound.
715 // If deep_serialize returns false, the model visitor will not extract all
716 // possible values from the values function.
717 template <typename F>
718 class LightFunctionElementConstraint : public Constraint {
719  public:
LightFunctionElementConstraint(Solver * const solver,IntVar * const var,IntVar * const index,F values,std::function<bool ()> deep_serialize)720   LightFunctionElementConstraint(Solver* const solver, IntVar* const var,
721                                  IntVar* const index, F values,
722                                  std::function<bool()> deep_serialize)
723       : Constraint(solver),
724         var_(var),
725         index_(index),
726         values_(std::move(values)),
727         deep_serialize_(std::move(deep_serialize)) {}
~LightFunctionElementConstraint()728   ~LightFunctionElementConstraint() override {}
729 
Post()730   void Post() override {
731     Demon* demon = MakeConstraintDemon0(
732         solver(), this, &LightFunctionElementConstraint::IndexBound,
733         "IndexBound");
734     index_->WhenBound(demon);
735   }
736 
InitialPropagate()737   void InitialPropagate() override {
738     if (index_->Bound()) {
739       IndexBound();
740     }
741   }
742 
DebugString() const743   std::string DebugString() const override {
744     return "LightFunctionElementConstraint";
745   }
746 
Accept(ModelVisitor * const visitor) const747   void Accept(ModelVisitor* const visitor) const override {
748     visitor->BeginVisitConstraint(RoutingModelVisitor::kLightElement, this);
749     visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
750                                             var_);
751     visitor->VisitIntegerExpressionArgument(ModelVisitor::kIndexArgument,
752                                             index_);
753     // Warning: This will expand all values into a vector.
754     if (deep_serialize_()) {
755       visitor->VisitInt64ToInt64Extension(values_, index_->Min(),
756                                           index_->Max());
757     }
758     visitor->EndVisitConstraint(RoutingModelVisitor::kLightElement, this);
759   }
760 
761  private:
IndexBound()762   void IndexBound() { var_->SetValue(values_(index_->Min())); }
763 
764   IntVar* const var_;
765   IntVar* const index_;
766   F values_;
767   std::function<bool()> deep_serialize_;
768 };
769 
770 template <typename F>
MakeLightElement(Solver * const solver,IntVar * const var,IntVar * const index,F values,std::function<bool ()> deep_serialize)771 Constraint* MakeLightElement(Solver* const solver, IntVar* const var,
772                              IntVar* const index, F values,
773                              std::function<bool()> deep_serialize) {
774   return solver->RevAlloc(new LightFunctionElementConstraint<F>(
775       solver, var, index, std::move(values), std::move(deep_serialize)));
776 }
777 
778 // Light two-dimension function-based element constraint ensuring:
779 // var == values(index1, index2).
780 // Doesn't perform bound reduction of the resulting variable until the index
781 // variables are bound.
782 // Ownership of the 'values' callback is taken by the constraint.
783 template <typename F>
784 class LightFunctionElement2Constraint : public Constraint {
785  public:
LightFunctionElement2Constraint(Solver * const solver,IntVar * const var,IntVar * const index1,IntVar * const index2,F values,std::function<bool ()> deep_serialize)786   LightFunctionElement2Constraint(Solver* const solver, IntVar* const var,
787                                   IntVar* const index1, IntVar* const index2,
788                                   F values,
789                                   std::function<bool()> deep_serialize)
790       : Constraint(solver),
791         var_(var),
792         index1_(index1),
793         index2_(index2),
794         values_(std::move(values)),
795         deep_serialize_(std::move(deep_serialize)) {}
~LightFunctionElement2Constraint()796   ~LightFunctionElement2Constraint() override {}
Post()797   void Post() override {
798     Demon* demon = MakeConstraintDemon0(
799         solver(), this, &LightFunctionElement2Constraint::IndexBound,
800         "IndexBound");
801     index1_->WhenBound(demon);
802     index2_->WhenBound(demon);
803   }
InitialPropagate()804   void InitialPropagate() override { IndexBound(); }
805 
DebugString() const806   std::string DebugString() const override {
807     return "LightFunctionElement2Constraint";
808   }
809 
Accept(ModelVisitor * const visitor) const810   void Accept(ModelVisitor* const visitor) const override {
811     visitor->BeginVisitConstraint(RoutingModelVisitor::kLightElement2, this);
812     visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
813                                             var_);
814     visitor->VisitIntegerExpressionArgument(ModelVisitor::kIndexArgument,
815                                             index1_);
816     visitor->VisitIntegerExpressionArgument(ModelVisitor::kIndex2Argument,
817                                             index2_);
818     // Warning: This will expand all values into a vector.
819     const int64_t index1_min = index1_->Min();
820     const int64_t index1_max = index1_->Max();
821     visitor->VisitIntegerArgument(ModelVisitor::kMinArgument, index1_min);
822     visitor->VisitIntegerArgument(ModelVisitor::kMaxArgument, index1_max);
823     if (deep_serialize_()) {
824       for (int i = index1_min; i <= index1_max; ++i) {
825         visitor->VisitInt64ToInt64Extension(
826             [this, i](int64_t j) { return values_(i, j); }, index2_->Min(),
827             index2_->Max());
828       }
829     }
830     visitor->EndVisitConstraint(RoutingModelVisitor::kLightElement2, this);
831   }
832 
833  private:
IndexBound()834   void IndexBound() {
835     if (index1_->Bound() && index2_->Bound()) {
836       var_->SetValue(values_(index1_->Min(), index2_->Min()));
837     }
838   }
839 
840   IntVar* const var_;
841   IntVar* const index1_;
842   IntVar* const index2_;
843   Solver::IndexEvaluator2 values_;
844   std::function<bool()> deep_serialize_;
845 };
846 
847 template <typename F>
MakeLightElement2(Solver * const solver,IntVar * const var,IntVar * const index1,IntVar * const index2,F values,std::function<bool ()> deep_serialize)848 Constraint* MakeLightElement2(Solver* const solver, IntVar* const var,
849                               IntVar* const index1, IntVar* const index2,
850                               F values, std::function<bool()> deep_serialize) {
851   return solver->RevAlloc(new LightFunctionElement2Constraint<F>(
852       solver, var, index1, index2, std::move(values),
853       std::move(deep_serialize)));
854 }
855 
856 // Evaluators
857 template <class A, class B>
ReturnZero(A a,B b)858 static int64_t ReturnZero(A a, B b) {
859   return 0;
860 }
861 
TransitCallbackPositive(const RoutingTransitCallback2 & callback,int size1,int size2)862 bool TransitCallbackPositive(const RoutingTransitCallback2& callback, int size1,
863                              int size2) {
864   for (int i = 0; i < size1; i++) {
865     for (int j = 0; j < size2; j++) {
866       if (callback(i, j) < 0) {
867         return false;
868       }
869     }
870   }
871   return true;
872 }
873 
874 }  // namespace
875 
876 // ----- Routing model -----
877 
878 static const int kUnassigned = -1;
879 const int64_t RoutingModel::kNoPenalty = -1;
880 
881 const RoutingModel::DisjunctionIndex RoutingModel::kNoDisjunction(-1);
882 
883 const RoutingModel::DimensionIndex RoutingModel::kNoDimension(-1);
884 
RoutingModel(const RoutingIndexManager & index_manager)885 RoutingModel::RoutingModel(const RoutingIndexManager& index_manager)
886     : RoutingModel(index_manager, DefaultRoutingModelParameters()) {}
887 
RoutingModel(const RoutingIndexManager & index_manager,const RoutingModelParameters & parameters)888 RoutingModel::RoutingModel(const RoutingIndexManager& index_manager,
889                            const RoutingModelParameters& parameters)
890     : nodes_(index_manager.num_nodes()),
891       vehicles_(index_manager.num_vehicles()),
892       max_active_vehicles_(vehicles_),
893       fixed_cost_of_vehicle_(vehicles_, 0),
894       cost_class_index_of_vehicle_(vehicles_, CostClassIndex(-1)),
895       linear_cost_factor_of_vehicle_(vehicles_, 0),
896       quadratic_cost_factor_of_vehicle_(vehicles_, 0),
897       vehicle_amortized_cost_factors_set_(false),
898       vehicle_used_when_empty_(vehicles_, false),
899       cost_classes_(),
900       costs_are_homogeneous_across_vehicles_(
901           parameters.reduce_vehicle_cost_model()),
902       cache_callbacks_(false),
903       vehicle_class_index_of_vehicle_(vehicles_, VehicleClassIndex(-1)),
904       vehicle_pickup_delivery_policy_(vehicles_, PICKUP_AND_DELIVERY_NO_ORDER),
905       has_hard_type_incompatibilities_(false),
906       has_temporal_type_incompatibilities_(false),
907       has_same_vehicle_type_requirements_(false),
908       has_temporal_type_requirements_(false),
909       num_visit_types_(0),
910       starts_(vehicles_),
911       ends_(vehicles_),
912       manager_(index_manager) {
913   // Initialize vehicle costs to the zero evaluator.
914   vehicle_to_transit_cost_.assign(
915       vehicles_, RegisterTransitCallback(ReturnZero<int64_t, int64_t>));
916   // Active caching after initializing vehicle_to_transit_cost_ to avoid
917   // uselessly caching ReturnZero.
918   cache_callbacks_ = (nodes_ <= parameters.max_callback_cache_size());
919 
920   VLOG(1) << "Model parameters:\n" << parameters.DebugString();
921   ConstraintSolverParameters solver_parameters =
922       parameters.has_solver_parameters() ? parameters.solver_parameters()
923                                          : Solver::DefaultSolverParameters();
924   solver_ = absl::make_unique<Solver>("Routing", solver_parameters);
925   // TODO(user): Remove when removal of NodeIndex is complete.
926   start_end_count_ = index_manager.num_unique_depots();
927   Initialize();
928 
929   const int64_t size = Size();
930   index_to_pickup_index_pairs_.resize(size);
931   index_to_delivery_index_pairs_.resize(size);
932   index_to_visit_type_.resize(index_manager.num_indices(), kUnassigned);
933   index_to_type_policy_.resize(index_manager.num_indices());
934 
935   index_to_vehicle_.resize(index_manager.num_indices(), kUnassigned);
936   for (int v = 0; v < index_manager.num_vehicles(); ++v) {
937     starts_[v] = index_manager.GetStartIndex(v);
938     index_to_vehicle_[starts_[v]] = v;
939     ends_[v] = index_manager.GetEndIndex(v);
940     index_to_vehicle_[ends_[v]] = v;
941   }
942 
943   const std::vector<RoutingIndexManager::NodeIndex>& index_to_node =
944       index_manager.GetIndexToNodeMap();
945   index_to_equivalence_class_.resize(index_manager.num_indices());
946   for (int i = 0; i < index_to_node.size(); ++i) {
947     index_to_equivalence_class_[i] = index_to_node[i].value();
948   }
949   allowed_vehicles_.resize(Size() + vehicles_);
950 }
951 
Initialize()952 void RoutingModel::Initialize() {
953   const int size = Size();
954   // Next variables
955   solver_->MakeIntVarArray(size, 0, size + vehicles_ - 1, "Nexts", &nexts_);
956   solver_->AddConstraint(solver_->MakeAllDifferent(nexts_, false));
957   index_to_disjunctions_.resize(size + vehicles_);
958   // Vehicle variables. In case that node i is not active, vehicle_vars_[i] is
959   // bound to -1.
960   solver_->MakeIntVarArray(size + vehicles_, -1, vehicles_ - 1, "Vehicles",
961                            &vehicle_vars_);
962   // Active variables
963   solver_->MakeBoolVarArray(size, "Active", &active_);
964   // Active vehicle variables
965   solver_->MakeBoolVarArray(vehicles_, "ActiveVehicle", &vehicle_active_);
966   // Variables representing vehicles contributing to cost.
967   solver_->MakeBoolVarArray(vehicles_, "VehicleCostsConsidered",
968                             &vehicle_route_considered_);
969   // Is-bound-to-end variables.
970   solver_->MakeBoolVarArray(size + vehicles_, "IsBoundToEnd",
971                             &is_bound_to_end_);
972   // Cost cache
973   cost_cache_.clear();
974   cost_cache_.resize(size + vehicles_, {kUnassigned, CostClassIndex(-1), 0});
975   preassignment_ = solver_->MakeAssignment();
976 }
977 
~RoutingModel()978 RoutingModel::~RoutingModel() {
979   gtl::STLDeleteElements(&dimensions_);
980 
981   // State dependent transit callbacks.
982   absl::flat_hash_set<RangeIntToIntFunction*> value_functions_delete;
983   absl::flat_hash_set<RangeMinMaxIndexFunction*> index_functions_delete;
984   for (const auto& cache_line : state_dependent_transit_evaluators_cache_) {
985     for (const auto& key_transit : *cache_line) {
986       value_functions_delete.insert(key_transit.second.transit);
987       index_functions_delete.insert(key_transit.second.transit_plus_identity);
988     }
989   }
990   gtl::STLDeleteElements(&value_functions_delete);
991   gtl::STLDeleteElements(&index_functions_delete);
992 }
993 
994 namespace {
RegisterCallback(RoutingTransitCallback2 callback,bool is_positive,RoutingModel * model)995 int RegisterCallback(RoutingTransitCallback2 callback, bool is_positive,
996                      RoutingModel* model) {
997   if (is_positive) {
998     return model->RegisterPositiveTransitCallback(std::move(callback));
999   }
1000   return model->RegisterTransitCallback(std::move(callback));
1001 }
1002 
RegisterUnaryCallback(RoutingTransitCallback1 callback,bool is_positive,RoutingModel * model)1003 int RegisterUnaryCallback(RoutingTransitCallback1 callback, bool is_positive,
1004                           RoutingModel* model) {
1005   if (is_positive) {
1006     return model->RegisterPositiveUnaryTransitCallback(std::move(callback));
1007   }
1008   return model->RegisterUnaryTransitCallback(std::move(callback));
1009 }
1010 }  // namespace
1011 
RegisterUnaryTransitVector(std::vector<int64_t> values)1012 int RoutingModel::RegisterUnaryTransitVector(std::vector<int64_t> values) {
1013   return RegisterUnaryCallback(
1014       [this, values = std::move(values)](int64_t i) {
1015         return values[manager_.IndexToNode(i).value()];
1016       },
1017       /*is_positive=*/
1018       std::all_of(std::cbegin(values), std::cend(values),
1019                   [](int64_t transit) { return transit >= 0; }),
1020       this);
1021 }
1022 
RegisterUnaryTransitCallback(TransitCallback1 callback)1023 int RoutingModel::RegisterUnaryTransitCallback(TransitCallback1 callback) {
1024   const int index = unary_transit_evaluators_.size();
1025   unary_transit_evaluators_.push_back(std::move(callback));
1026   return RegisterTransitCallback([this, index](int i, int j) {
1027     return unary_transit_evaluators_[index](i);
1028   });
1029 }
1030 
RegisterTransitMatrix(std::vector<std::vector<int64_t>> values)1031 int RoutingModel::RegisterTransitMatrix(
1032     std::vector<std::vector<int64_t> /*needed_for_swig*/> values) {
1033   bool all_transits_positive = true;
1034   for (const std::vector<int64_t>& transit_values : values) {
1035     all_transits_positive =
1036         std::all_of(std::cbegin(transit_values), std::cend(transit_values),
1037                     [](int64_t transit) { return transit >= 0; });
1038     if (!all_transits_positive) {
1039       break;
1040     }
1041   }
1042   return RegisterCallback(
1043       [this, values = std::move(values)](int64_t i, int64_t j) {
1044         return values[manager_.IndexToNode(i).value()]
1045                      [manager_.IndexToNode(j).value()];
1046       },
1047       all_transits_positive, this);
1048 }
1049 
RegisterPositiveUnaryTransitCallback(TransitCallback1 callback)1050 int RoutingModel::RegisterPositiveUnaryTransitCallback(
1051     TransitCallback1 callback) {
1052   is_transit_evaluator_positive_.push_back(true);
1053   DCHECK(TransitCallbackPositive(
1054       [&callback](int i, int) { return callback(i); }, Size() + vehicles(), 1));
1055   return RegisterUnaryTransitCallback(std::move(callback));
1056 }
1057 
RegisterTransitCallback(TransitCallback2 callback)1058 int RoutingModel::RegisterTransitCallback(TransitCallback2 callback) {
1059   if (cache_callbacks_) {
1060     const int size = Size() + vehicles();
1061     std::vector<int64_t> cache(size * size, 0);
1062     for (int i = 0; i < size; ++i) {
1063       for (int j = 0; j < size; ++j) {
1064         cache[i * size + j] = callback(i, j);
1065       }
1066     }
1067     transit_evaluators_.push_back(
1068         [cache, size](int64_t i, int64_t j) { return cache[i * size + j]; });
1069   } else {
1070     transit_evaluators_.push_back(std::move(callback));
1071   }
1072   if (transit_evaluators_.size() != unary_transit_evaluators_.size()) {
1073     DCHECK_EQ(transit_evaluators_.size(), unary_transit_evaluators_.size() + 1);
1074     unary_transit_evaluators_.push_back(nullptr);
1075   }
1076   if (transit_evaluators_.size() != is_transit_evaluator_positive_.size()) {
1077     DCHECK_EQ(transit_evaluators_.size(),
1078               is_transit_evaluator_positive_.size() + 1);
1079     is_transit_evaluator_positive_.push_back(false);
1080   }
1081   return transit_evaluators_.size() - 1;
1082 }
1083 
RegisterPositiveTransitCallback(TransitCallback2 callback)1084 int RoutingModel::RegisterPositiveTransitCallback(TransitCallback2 callback) {
1085   is_transit_evaluator_positive_.push_back(true);
1086   DCHECK(TransitCallbackPositive(callback, Size() + vehicles(),
1087                                  Size() + vehicles()));
1088   return RegisterTransitCallback(std::move(callback));
1089 }
1090 
RegisterStateDependentTransitCallback(VariableIndexEvaluator2 callback)1091 int RoutingModel::RegisterStateDependentTransitCallback(
1092     VariableIndexEvaluator2 callback) {
1093   state_dependent_transit_evaluators_cache_.push_back(
1094       absl::make_unique<StateDependentTransitCallbackCache>());
1095   StateDependentTransitCallbackCache* const cache =
1096       state_dependent_transit_evaluators_cache_.back().get();
1097   state_dependent_transit_evaluators_.push_back(
1098       [cache, callback](int64_t i, int64_t j) {
1099         StateDependentTransit value;
1100         if (gtl::FindCopy(*cache, CacheKey(i, j), &value)) return value;
1101         value = callback(i, j);
1102         cache->insert({CacheKey(i, j), value});
1103         return value;
1104       });
1105   return state_dependent_transit_evaluators_.size() - 1;
1106 }
1107 
AddNoCycleConstraintInternal()1108 void RoutingModel::AddNoCycleConstraintInternal() {
1109   if (no_cycle_constraint_ == nullptr) {
1110     no_cycle_constraint_ = solver_->MakeNoCycle(nexts_, active_);
1111     solver_->AddConstraint(no_cycle_constraint_);
1112   }
1113 }
1114 
AddDimension(int evaluator_index,int64_t slack_max,int64_t capacity,bool fix_start_cumul_to_zero,const std::string & name)1115 bool RoutingModel::AddDimension(int evaluator_index, int64_t slack_max,
1116                                 int64_t capacity, bool fix_start_cumul_to_zero,
1117                                 const std::string& name) {
1118   const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
1119   std::vector<int64_t> capacities(vehicles_, capacity);
1120   return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
1121                                           std::move(capacities),
1122                                           fix_start_cumul_to_zero, name);
1123 }
1124 
AddDimensionWithVehicleTransits(const std::vector<int> & evaluator_indices,int64_t slack_max,int64_t capacity,bool fix_start_cumul_to_zero,const std::string & name)1125 bool RoutingModel::AddDimensionWithVehicleTransits(
1126     const std::vector<int>& evaluator_indices, int64_t slack_max,
1127     int64_t capacity, bool fix_start_cumul_to_zero, const std::string& name) {
1128   std::vector<int64_t> capacities(vehicles_, capacity);
1129   return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
1130                                           std::move(capacities),
1131                                           fix_start_cumul_to_zero, name);
1132 }
1133 
AddDimensionWithVehicleCapacity(int evaluator_index,int64_t slack_max,std::vector<int64_t> vehicle_capacities,bool fix_start_cumul_to_zero,const std::string & name)1134 bool RoutingModel::AddDimensionWithVehicleCapacity(
1135     int evaluator_index, int64_t slack_max,
1136     std::vector<int64_t> vehicle_capacities, bool fix_start_cumul_to_zero,
1137     const std::string& name) {
1138   const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
1139   return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
1140                                           std::move(vehicle_capacities),
1141                                           fix_start_cumul_to_zero, name);
1142 }
1143 
AddDimensionWithVehicleTransitAndCapacity(const std::vector<int> & evaluator_indices,int64_t slack_max,std::vector<int64_t> vehicle_capacities,bool fix_start_cumul_to_zero,const std::string & name)1144 bool RoutingModel::AddDimensionWithVehicleTransitAndCapacity(
1145     const std::vector<int>& evaluator_indices, int64_t slack_max,
1146     std::vector<int64_t> vehicle_capacities, bool fix_start_cumul_to_zero,
1147     const std::string& name) {
1148   return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
1149                                           std::move(vehicle_capacities),
1150                                           fix_start_cumul_to_zero, name);
1151 }
1152 
AddDimensionWithCapacityInternal(const std::vector<int> & evaluator_indices,int64_t slack_max,std::vector<int64_t> vehicle_capacities,bool fix_start_cumul_to_zero,const std::string & name)1153 bool RoutingModel::AddDimensionWithCapacityInternal(
1154     const std::vector<int>& evaluator_indices, int64_t slack_max,
1155     std::vector<int64_t> vehicle_capacities, bool fix_start_cumul_to_zero,
1156     const std::string& name) {
1157   CHECK_EQ(vehicles_, vehicle_capacities.size());
1158   return InitializeDimensionInternal(
1159       evaluator_indices, std::vector<int>(), slack_max, fix_start_cumul_to_zero,
1160       new RoutingDimension(this, std::move(vehicle_capacities), name, nullptr));
1161 }
1162 
InitializeDimensionInternal(const std::vector<int> & evaluator_indices,const std::vector<int> & state_dependent_evaluator_indices,int64_t slack_max,bool fix_start_cumul_to_zero,RoutingDimension * dimension)1163 bool RoutingModel::InitializeDimensionInternal(
1164     const std::vector<int>& evaluator_indices,
1165     const std::vector<int>& state_dependent_evaluator_indices,
1166     int64_t slack_max, bool fix_start_cumul_to_zero,
1167     RoutingDimension* dimension) {
1168   CHECK(dimension != nullptr);
1169   CHECK_EQ(vehicles_, evaluator_indices.size());
1170   CHECK((dimension->base_dimension_ == nullptr &&
1171          state_dependent_evaluator_indices.empty()) ||
1172         vehicles_ == state_dependent_evaluator_indices.size());
1173   if (!HasDimension(dimension->name())) {
1174     const DimensionIndex dimension_index(dimensions_.size());
1175     dimension_name_to_index_[dimension->name()] = dimension_index;
1176     dimensions_.push_back(dimension);
1177     dimension->Initialize(evaluator_indices, state_dependent_evaluator_indices,
1178                           slack_max);
1179     solver_->AddConstraint(solver_->MakeDelayedPathCumul(
1180         nexts_, active_, dimension->cumuls(), dimension->transits()));
1181     if (fix_start_cumul_to_zero) {
1182       for (int i = 0; i < vehicles_; ++i) {
1183         IntVar* const start_cumul = dimension->CumulVar(Start(i));
1184         CHECK_EQ(0, start_cumul->Min());
1185         start_cumul->SetValue(0);
1186       }
1187     }
1188     return true;
1189   }
1190   delete dimension;
1191   return false;
1192 }
1193 
AddConstantDimensionWithSlack(int64_t value,int64_t capacity,int64_t slack_max,bool fix_start_cumul_to_zero,const std::string & dimension_name)1194 std::pair<int, bool> RoutingModel::AddConstantDimensionWithSlack(
1195     int64_t value, int64_t capacity, int64_t slack_max,
1196     bool fix_start_cumul_to_zero, const std::string& dimension_name) {
1197   const int evaluator_index =
1198       RegisterUnaryCallback([value](int64_t) { return value; },
1199                             /*is_positive=*/value >= 0, this);
1200   return std::make_pair(evaluator_index,
1201                         AddDimension(evaluator_index, slack_max, capacity,
1202                                      fix_start_cumul_to_zero, dimension_name));
1203 }
1204 
AddVectorDimension(std::vector<int64_t> values,int64_t capacity,bool fix_start_cumul_to_zero,const std::string & dimension_name)1205 std::pair<int, bool> RoutingModel::AddVectorDimension(
1206     std::vector<int64_t> values, int64_t capacity, bool fix_start_cumul_to_zero,
1207     const std::string& dimension_name) {
1208   const int evaluator_index = RegisterUnaryTransitVector(std::move(values));
1209   return std::make_pair(evaluator_index,
1210                         AddDimension(evaluator_index, 0, capacity,
1211                                      fix_start_cumul_to_zero, dimension_name));
1212 }
1213 
AddMatrixDimension(std::vector<std::vector<int64_t>> values,int64_t capacity,bool fix_start_cumul_to_zero,const std::string & dimension_name)1214 std::pair<int, bool> RoutingModel::AddMatrixDimension(
1215     std::vector<std::vector<int64_t>> values, int64_t capacity,
1216     bool fix_start_cumul_to_zero, const std::string& dimension_name) {
1217   const int evaluator_index = RegisterTransitMatrix(std::move(values));
1218   return std::make_pair(evaluator_index,
1219                         AddDimension(evaluator_index, 0, capacity,
1220                                      fix_start_cumul_to_zero, dimension_name));
1221 }
1222 
1223 namespace {
1224 // RangeMakeElementExpr is an IntExpr that corresponds to a
1225 // RangeIntToIntFunction indexed by an IntVar.
1226 // Do not create this class dicretly, but rather use MakeRangeMakeElementExpr.
1227 class RangeMakeElementExpr : public BaseIntExpr {
1228  public:
RangeMakeElementExpr(const RangeIntToIntFunction * callback,IntVar * index,Solver * s)1229   RangeMakeElementExpr(const RangeIntToIntFunction* callback, IntVar* index,
1230                        Solver* s)
1231       : BaseIntExpr(s), callback_(ABSL_DIE_IF_NULL(callback)), index_(index) {
1232     CHECK(callback_ != nullptr);
1233     CHECK(index != nullptr);
1234   }
1235 
Min() const1236   int64_t Min() const override {
1237     // Converting [index_->Min(), index_->Max()] to [idx_min, idx_max).
1238     const int idx_min = index_->Min();
1239     const int idx_max = index_->Max() + 1;
1240     return (idx_min < idx_max) ? callback_->RangeMin(idx_min, idx_max)
1241                                : std::numeric_limits<int64_t>::max();
1242   }
SetMin(int64_t new_min)1243   void SetMin(int64_t new_min) override {
1244     const int64_t old_min = Min();
1245     const int64_t old_max = Max();
1246     if (old_min < new_min && new_min <= old_max) {
1247       const int64_t old_idx_min = index_->Min();
1248       const int64_t old_idx_max = index_->Max() + 1;
1249       if (old_idx_min < old_idx_max) {
1250         const int64_t new_idx_min = callback_->RangeFirstInsideInterval(
1251             old_idx_min, old_idx_max, new_min, old_max + 1);
1252         index_->SetMin(new_idx_min);
1253         if (new_idx_min < old_idx_max) {
1254           const int64_t new_idx_max = callback_->RangeLastInsideInterval(
1255               new_idx_min, old_idx_max, new_min, old_max + 1);
1256           index_->SetMax(new_idx_max);
1257         }
1258       }
1259     }
1260   }
Max() const1261   int64_t Max() const override {
1262     // Converting [index_->Min(), index_->Max()] to [idx_min, idx_max).
1263     const int idx_min = index_->Min();
1264     const int idx_max = index_->Max() + 1;
1265     return (idx_min < idx_max) ? callback_->RangeMax(idx_min, idx_max)
1266                                : std::numeric_limits<int64_t>::min();
1267   }
SetMax(int64_t new_max)1268   void SetMax(int64_t new_max) override {
1269     const int64_t old_min = Min();
1270     const int64_t old_max = Max();
1271     if (old_min <= new_max && new_max < old_max) {
1272       const int64_t old_idx_min = index_->Min();
1273       const int64_t old_idx_max = index_->Max() + 1;
1274       if (old_idx_min < old_idx_max) {
1275         const int64_t new_idx_min = callback_->RangeFirstInsideInterval(
1276             old_idx_min, old_idx_max, old_min, new_max + 1);
1277         index_->SetMin(new_idx_min);
1278         if (new_idx_min < old_idx_max) {
1279           const int64_t new_idx_max = callback_->RangeLastInsideInterval(
1280               new_idx_min, old_idx_max, old_min, new_max + 1);
1281           index_->SetMax(new_idx_max);
1282         }
1283       }
1284     }
1285   }
WhenRange(Demon * d)1286   void WhenRange(Demon* d) override { index_->WhenRange(d); }
1287 
1288  private:
1289   const RangeIntToIntFunction* const callback_;
1290   IntVar* const index_;
1291 };
1292 
MakeRangeMakeElementExpr(const RangeIntToIntFunction * callback,IntVar * index,Solver * s)1293 IntExpr* MakeRangeMakeElementExpr(const RangeIntToIntFunction* callback,
1294                                   IntVar* index, Solver* s) {
1295   return s->RegisterIntExpr(
1296       s->RevAlloc(new RangeMakeElementExpr(callback, index, s)));
1297 }
1298 }  // namespace
1299 
AddDimensionDependentDimensionWithVehicleCapacity(const std::vector<int> & dependent_transits,const RoutingDimension * base_dimension,int64_t slack_max,std::vector<int64_t> vehicle_capacities,bool fix_start_cumul_to_zero,const std::string & name)1300 bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity(
1301     const std::vector<int>& dependent_transits,
1302     const RoutingDimension* base_dimension, int64_t slack_max,
1303     std::vector<int64_t> vehicle_capacities, bool fix_start_cumul_to_zero,
1304     const std::string& name) {
1305   const std::vector<int> pure_transits(vehicles_, /*zero_evaluator*/ 0);
1306   return AddDimensionDependentDimensionWithVehicleCapacity(
1307       pure_transits, dependent_transits, base_dimension, slack_max,
1308       std::move(vehicle_capacities), fix_start_cumul_to_zero, name);
1309 }
1310 
AddDimensionDependentDimensionWithVehicleCapacity(int transit,const RoutingDimension * dimension,int64_t slack_max,int64_t vehicle_capacity,bool fix_start_cumul_to_zero,const std::string & name)1311 bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity(
1312     int transit, const RoutingDimension* dimension, int64_t slack_max,
1313     int64_t vehicle_capacity, bool fix_start_cumul_to_zero,
1314     const std::string& name) {
1315   return AddDimensionDependentDimensionWithVehicleCapacity(
1316       /*zero_evaluator*/ 0, transit, dimension, slack_max, vehicle_capacity,
1317       fix_start_cumul_to_zero, name);
1318 }
1319 
AddDimensionDependentDimensionWithVehicleCapacityInternal(const std::vector<int> & pure_transits,const std::vector<int> & dependent_transits,const RoutingDimension * base_dimension,int64_t slack_max,std::vector<int64_t> vehicle_capacities,bool fix_start_cumul_to_zero,const std::string & name)1320 bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacityInternal(
1321     const std::vector<int>& pure_transits,
1322     const std::vector<int>& dependent_transits,
1323     const RoutingDimension* base_dimension, int64_t slack_max,
1324     std::vector<int64_t> vehicle_capacities, bool fix_start_cumul_to_zero,
1325     const std::string& name) {
1326   CHECK_EQ(vehicles_, vehicle_capacities.size());
1327   RoutingDimension* new_dimension = nullptr;
1328   if (base_dimension == nullptr) {
1329     new_dimension = new RoutingDimension(this, std::move(vehicle_capacities),
1330                                          name, RoutingDimension::SelfBased());
1331   } else {
1332     new_dimension = new RoutingDimension(this, std::move(vehicle_capacities),
1333                                          name, base_dimension);
1334   }
1335   return InitializeDimensionInternal(pure_transits, dependent_transits,
1336                                      slack_max, fix_start_cumul_to_zero,
1337                                      new_dimension);
1338 }
1339 
AddDimensionDependentDimensionWithVehicleCapacity(int pure_transit,int dependent_transit,const RoutingDimension * base_dimension,int64_t slack_max,int64_t vehicle_capacity,bool fix_start_cumul_to_zero,const std::string & name)1340 bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity(
1341     int pure_transit, int dependent_transit,
1342     const RoutingDimension* base_dimension, int64_t slack_max,
1343     int64_t vehicle_capacity, bool fix_start_cumul_to_zero,
1344     const std::string& name) {
1345   std::vector<int> pure_transits(vehicles_, pure_transit);
1346   std::vector<int> dependent_transits(vehicles_, dependent_transit);
1347   std::vector<int64_t> vehicle_capacities(vehicles_, vehicle_capacity);
1348   return AddDimensionDependentDimensionWithVehicleCapacityInternal(
1349       pure_transits, dependent_transits, base_dimension, slack_max,
1350       std::move(vehicle_capacities), fix_start_cumul_to_zero, name);
1351 }
1352 
MakeStateDependentTransit(const std::function<int64_t (int64_t)> & f,int64_t domain_start,int64_t domain_end)1353 RoutingModel::StateDependentTransit RoutingModel::MakeStateDependentTransit(
1354     const std::function<int64_t(int64_t)>& f, int64_t domain_start,
1355     int64_t domain_end) {
1356   const std::function<int64_t(int64_t)> g = [&f](int64_t x) {
1357     return f(x) + x;
1358   };
1359   // The next line is safe, because MakeCachedIntToIntFunction does not count
1360   // on keeping the closure of its first argument alive.
1361   return {MakeCachedIntToIntFunction(f, domain_start, domain_end),
1362           MakeCachedRangeMinMaxIndexFunction(g, domain_start, domain_end)};
1363 }
1364 
GetAllDimensionNames() const1365 std::vector<std::string> RoutingModel::GetAllDimensionNames() const {
1366   std::vector<std::string> dimension_names;
1367   for (const auto& dimension_name_index : dimension_name_to_index_) {
1368     dimension_names.push_back(dimension_name_index.first);
1369   }
1370   std::sort(dimension_names.begin(), dimension_names.end());
1371   return dimension_names;
1372 }
1373 
GetMutableGlobalCumulOptimizer(const RoutingDimension & dimension) const1374 GlobalDimensionCumulOptimizer* RoutingModel::GetMutableGlobalCumulOptimizer(
1375     const RoutingDimension& dimension) const {
1376   const DimensionIndex dim_index = GetDimensionIndex(dimension.name());
1377   if (dim_index < 0 || dim_index >= global_optimizer_index_.size() ||
1378       global_optimizer_index_[dim_index] < 0) {
1379     return nullptr;
1380   }
1381   const int optimizer_index = global_optimizer_index_[dim_index];
1382   DCHECK_LT(optimizer_index, global_dimension_optimizers_.size());
1383   return global_dimension_optimizers_[optimizer_index].get();
1384 }
1385 
GetMutableGlobalCumulMPOptimizer(const RoutingDimension & dimension) const1386 GlobalDimensionCumulOptimizer* RoutingModel::GetMutableGlobalCumulMPOptimizer(
1387     const RoutingDimension& dimension) const {
1388   const DimensionIndex dim_index = GetDimensionIndex(dimension.name());
1389   if (dim_index < 0 || dim_index >= global_optimizer_index_.size() ||
1390       global_optimizer_index_[dim_index] < 0) {
1391     return nullptr;
1392   }
1393   const int optimizer_index = global_optimizer_index_[dim_index];
1394   DCHECK_LT(optimizer_index, global_dimension_mp_optimizers_.size());
1395   return global_dimension_mp_optimizers_[optimizer_index].get();
1396 }
1397 
GetMutableLocalCumulOptimizer(const RoutingDimension & dimension) const1398 LocalDimensionCumulOptimizer* RoutingModel::GetMutableLocalCumulOptimizer(
1399     const RoutingDimension& dimension) const {
1400   const DimensionIndex dim_index = GetDimensionIndex(dimension.name());
1401   if (dim_index < 0 || dim_index >= local_optimizer_index_.size() ||
1402       local_optimizer_index_[dim_index] < 0) {
1403     return nullptr;
1404   }
1405   const int optimizer_index = local_optimizer_index_[dim_index];
1406   DCHECK_LT(optimizer_index, local_dimension_optimizers_.size());
1407   return local_dimension_optimizers_[optimizer_index].get();
1408 }
1409 
GetMutableLocalCumulMPOptimizer(const RoutingDimension & dimension) const1410 LocalDimensionCumulOptimizer* RoutingModel::GetMutableLocalCumulMPOptimizer(
1411     const RoutingDimension& dimension) const {
1412   const DimensionIndex dim_index = GetDimensionIndex(dimension.name());
1413   if (dim_index < 0 || dim_index >= local_optimizer_index_.size() ||
1414       local_optimizer_index_[dim_index] < 0) {
1415     return nullptr;
1416   }
1417   const int optimizer_index = local_optimizer_index_[dim_index];
1418   DCHECK_LT(optimizer_index, local_dimension_mp_optimizers_.size());
1419   return local_dimension_mp_optimizers_[optimizer_index].get();
1420 }
1421 
HasDimension(const std::string & dimension_name) const1422 bool RoutingModel::HasDimension(const std::string& dimension_name) const {
1423   return gtl::ContainsKey(dimension_name_to_index_, dimension_name);
1424 }
1425 
GetDimensionIndex(const std::string & dimension_name) const1426 RoutingModel::DimensionIndex RoutingModel::GetDimensionIndex(
1427     const std::string& dimension_name) const {
1428   return gtl::FindWithDefault(dimension_name_to_index_, dimension_name,
1429                               kNoDimension);
1430 }
1431 
GetDimensionOrDie(const std::string & dimension_name) const1432 const RoutingDimension& RoutingModel::GetDimensionOrDie(
1433     const std::string& dimension_name) const {
1434   return *dimensions_[gtl::FindOrDie(dimension_name_to_index_, dimension_name)];
1435 }
1436 
GetMutableDimension(const std::string & dimension_name) const1437 RoutingDimension* RoutingModel::GetMutableDimension(
1438     const std::string& dimension_name) const {
1439   const DimensionIndex index = GetDimensionIndex(dimension_name);
1440   if (index != kNoDimension) {
1441     return dimensions_[index];
1442   }
1443   return nullptr;
1444 }
1445 
1446 // ResourceGroup
Attributes()1447 ResourceGroup::Attributes::Attributes()
1448     : start_domain_(Domain::AllValues()), end_domain_(Domain::AllValues()) {
1449   /// The default attributes have unconstrained start/end domains.
1450 }
1451 
Attributes(Domain start_domain,Domain end_domain)1452 RoutingModel::ResourceGroup::Attributes::Attributes(Domain start_domain,
1453                                                     Domain end_domain)
1454     : start_domain_(std::move(start_domain)),
1455       end_domain_(std::move(end_domain)) {}
1456 
1457 const ResourceGroup::Attributes&
GetDimensionAttributes(const RoutingDimension * dimension) const1458 ResourceGroup::Resource::GetDimensionAttributes(
1459     const RoutingDimension* dimension) const {
1460   DimensionIndex dimension_index = model_->GetDimensionIndex(dimension->name());
1461   DCHECK_NE(dimension_index, kNoDimension);
1462   return gtl::FindWithDefault(dimension_attributes_, dimension_index,
1463                               GetDefaultAttributes());
1464 }
1465 
SetDimensionAttributes(Attributes attributes,const RoutingDimension * dimension)1466 void ResourceGroup::Resource::SetDimensionAttributes(
1467     Attributes attributes, const RoutingDimension* dimension) {
1468   DCHECK(dimension_attributes_.empty())
1469       << "As of 2021/07, each resource can only constrain a single dimension.";
1470 
1471   const DimensionIndex dimension_index =
1472       model_->GetDimensionIndex(dimension->name());
1473   DCHECK_NE(dimension_index, kNoDimension);
1474   DCHECK(!dimension_attributes_.contains(dimension_index));
1475   dimension_attributes_[dimension_index] = std::move(attributes);
1476 }
1477 
GetDefaultAttributes() const1478 const ResourceGroup::Attributes& ResourceGroup::Resource::GetDefaultAttributes()
1479     const {
1480   static const Attributes* const kAttributes = new Attributes();
1481   return *kAttributes;
1482 }
1483 
AddResourceGroup()1484 int RoutingModel::AddResourceGroup() {
1485   resource_groups_.push_back(absl::make_unique<ResourceGroup>(this));
1486   return resource_groups_.size() - 1;
1487 }
1488 
AddResource(Attributes attributes,const RoutingDimension * dimension)1489 int RoutingModel::ResourceGroup::AddResource(
1490     Attributes attributes, const RoutingDimension* dimension) {
1491   resources_.push_back(Resource(model_));
1492   resources_.back().SetDimensionAttributes(std::move(attributes), dimension);
1493 
1494   const DimensionIndex dimension_index =
1495       model_->GetDimensionIndex(dimension->name());
1496   DCHECK_NE(dimension_index, kNoDimension);
1497   affected_dimension_indices_.insert(dimension_index);
1498 
1499   DCHECK_EQ(affected_dimension_indices_.size(), 1)
1500       << "As of 2021/07, each ResourceGroup can only affect a single "
1501          "RoutingDimension at a time.";
1502 
1503   return resources_.size() - 1;
1504 }
1505 
NotifyVehicleRequiresAResource(int vehicle)1506 void RoutingModel::ResourceGroup::NotifyVehicleRequiresAResource(int vehicle) {
1507   DCHECK_LT(vehicle, vehicle_requires_resource_.size());
1508   if (vehicle_requires_resource_[vehicle]) return;
1509   vehicle_requires_resource_[vehicle] = true;
1510   vehicles_requiring_resource_.push_back(vehicle);
1511 }
1512 
GetDimensionResourceGroupIndices(const RoutingDimension * dimension) const1513 const std::vector<int>& RoutingModel::GetDimensionResourceGroupIndices(
1514     const RoutingDimension* dimension) const {
1515   DCHECK(closed_);
1516   const DimensionIndex dim = GetDimensionIndex(dimension->name());
1517   DCHECK_NE(dim, kNoDimension);
1518   return dimension_resource_group_indices_[dim];
1519 }
1520 
SetArcCostEvaluatorOfAllVehicles(int evaluator_index)1521 void RoutingModel::SetArcCostEvaluatorOfAllVehicles(int evaluator_index) {
1522   CHECK_LT(0, vehicles_);
1523   for (int i = 0; i < vehicles_; ++i) {
1524     SetArcCostEvaluatorOfVehicle(evaluator_index, i);
1525   }
1526 }
1527 
SetArcCostEvaluatorOfVehicle(int evaluator_index,int vehicle)1528 void RoutingModel::SetArcCostEvaluatorOfVehicle(int evaluator_index,
1529                                                 int vehicle) {
1530   CHECK_LT(vehicle, vehicles_);
1531   CHECK_LT(evaluator_index, transit_evaluators_.size());
1532   vehicle_to_transit_cost_[vehicle] = evaluator_index;
1533 }
1534 
SetFixedCostOfAllVehicles(int64_t cost)1535 void RoutingModel::SetFixedCostOfAllVehicles(int64_t cost) {
1536   for (int i = 0; i < vehicles_; ++i) {
1537     SetFixedCostOfVehicle(cost, i);
1538   }
1539 }
1540 
GetFixedCostOfVehicle(int vehicle) const1541 int64_t RoutingModel::GetFixedCostOfVehicle(int vehicle) const {
1542   CHECK_LT(vehicle, vehicles_);
1543   return fixed_cost_of_vehicle_[vehicle];
1544 }
1545 
SetFixedCostOfVehicle(int64_t cost,int vehicle)1546 void RoutingModel::SetFixedCostOfVehicle(int64_t cost, int vehicle) {
1547   CHECK_LT(vehicle, vehicles_);
1548   DCHECK_GE(cost, 0);
1549   fixed_cost_of_vehicle_[vehicle] = cost;
1550 }
1551 
SetAmortizedCostFactorsOfAllVehicles(int64_t linear_cost_factor,int64_t quadratic_cost_factor)1552 void RoutingModel::SetAmortizedCostFactorsOfAllVehicles(
1553     int64_t linear_cost_factor, int64_t quadratic_cost_factor) {
1554   for (int v = 0; v < vehicles_; v++) {
1555     SetAmortizedCostFactorsOfVehicle(linear_cost_factor, quadratic_cost_factor,
1556                                      v);
1557   }
1558 }
1559 
SetAmortizedCostFactorsOfVehicle(int64_t linear_cost_factor,int64_t quadratic_cost_factor,int vehicle)1560 void RoutingModel::SetAmortizedCostFactorsOfVehicle(
1561     int64_t linear_cost_factor, int64_t quadratic_cost_factor, int vehicle) {
1562   CHECK_LT(vehicle, vehicles_);
1563   DCHECK_GE(linear_cost_factor, 0);
1564   DCHECK_GE(quadratic_cost_factor, 0);
1565   if (linear_cost_factor + quadratic_cost_factor > 0) {
1566     vehicle_amortized_cost_factors_set_ = true;
1567   }
1568   linear_cost_factor_of_vehicle_[vehicle] = linear_cost_factor;
1569   quadratic_cost_factor_of_vehicle_[vehicle] = quadratic_cost_factor;
1570 }
1571 
1572 namespace {
1573 // Some C++ versions used in the open-source export don't support comparison
1574 // functors for STL containers; so we need a comparator class instead.
1575 struct CostClassComparator {
operator ()operations_research::__anon1b0943261711::CostClassComparator1576   bool operator()(const RoutingModel::CostClass& a,
1577                   const RoutingModel::CostClass& b) const {
1578     return RoutingModel::CostClass::LessThan(a, b);
1579   }
1580 };
1581 
1582 struct VehicleClassComparator {
operator ()operations_research::__anon1b0943261711::VehicleClassComparator1583   bool operator()(const RoutingModel::VehicleClass& a,
1584                   const RoutingModel::VehicleClass& b) const {
1585     return RoutingModel::VehicleClass::LessThan(a, b);
1586   }
1587 };
1588 }  // namespace
1589 
1590 // static
1591 const RoutingModel::CostClassIndex RoutingModel::kCostClassIndexOfZeroCost =
1592     CostClassIndex(0);
1593 
ComputeCostClasses(const RoutingSearchParameters & parameters)1594 void RoutingModel::ComputeCostClasses(
1595     const RoutingSearchParameters& parameters) {
1596   // Create and reduce the cost classes.
1597   cost_classes_.reserve(vehicles_);
1598   cost_classes_.clear();
1599   cost_class_index_of_vehicle_.assign(vehicles_, CostClassIndex(-1));
1600   std::map<CostClass, CostClassIndex, CostClassComparator> cost_class_map;
1601 
1602   // Pre-insert the built-in cost class 'zero cost' with index 0.
1603   const CostClass zero_cost_class(0);
1604   cost_classes_.push_back(zero_cost_class);
1605   DCHECK_EQ(cost_classes_[kCostClassIndexOfZeroCost].evaluator_index, 0);
1606   cost_class_map[zero_cost_class] = kCostClassIndexOfZeroCost;
1607 
1608   // Determine the canonicalized cost class for each vehicle, and insert it as
1609   // a new cost class if it doesn't exist already. Building cached evaluators
1610   // on the way.
1611   has_vehicle_with_zero_cost_class_ = false;
1612   for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
1613     CostClass cost_class(vehicle_to_transit_cost_[vehicle]);
1614 
1615     // Insert the dimension data in a canonical way.
1616     for (const RoutingDimension* const dimension : dimensions_) {
1617       const int64_t coeff =
1618           dimension->vehicle_span_cost_coefficients()[vehicle];
1619       if (coeff == 0) continue;
1620       cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1621           .push_back({dimension->vehicle_to_class(vehicle), coeff, dimension});
1622     }
1623     std::sort(cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1624                   .begin(),
1625               cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1626                   .end());
1627     // Try inserting the CostClass, if it's not already present.
1628     const CostClassIndex num_cost_classes(cost_classes_.size());
1629     const CostClassIndex cost_class_index =
1630         gtl::LookupOrInsert(&cost_class_map, cost_class, num_cost_classes);
1631     if (cost_class_index == kCostClassIndexOfZeroCost) {
1632       has_vehicle_with_zero_cost_class_ = true;
1633     } else if (cost_class_index == num_cost_classes) {  // New cost class.
1634       cost_classes_.push_back(cost_class);
1635     }
1636     cost_class_index_of_vehicle_[vehicle] = cost_class_index;
1637   }
1638 
1639   // TRICKY:
1640   // If some vehicle had the "zero" cost class, then we'll have homogeneous
1641   // vehicles iff they all have that cost class (i.e. cost class count = 1).
1642   // If none of them have it, then we have homogeneous costs iff there are two
1643   // cost classes: the unused "zero" cost class and the one used by all
1644   // vehicles.
1645   // Note that we always need the zero cost class, even if no vehicle uses it,
1646   // because we use it in the vehicle_var = -1 scenario (i.e. unperformed).
1647   //
1648   // Fixed costs are simply ignored for computing these cost classes. They are
1649   // attached to start nodes directly.
1650   costs_are_homogeneous_across_vehicles_ &= has_vehicle_with_zero_cost_class_
1651                                                 ? GetCostClassesCount() == 1
1652                                                 : GetCostClassesCount() <= 2;
1653 }
1654 
LessThan(const VehicleClass & a,const VehicleClass & b)1655 bool RoutingModel::VehicleClass::LessThan(const VehicleClass& a,
1656                                           const VehicleClass& b) {
1657   return std::tie(a.cost_class_index, a.fixed_cost, a.used_when_empty,
1658                   a.start_equivalence_class, a.end_equivalence_class,
1659                   a.unvisitable_nodes_fprint, a.dimension_start_cumuls_min,
1660                   a.dimension_start_cumuls_max, a.dimension_end_cumuls_min,
1661                   a.dimension_end_cumuls_max, a.dimension_capacities,
1662                   a.dimension_evaluator_classes,
1663                   a.required_resource_group_indices) <
1664          std::tie(b.cost_class_index, b.fixed_cost, b.used_when_empty,
1665                   b.start_equivalence_class, b.end_equivalence_class,
1666                   b.unvisitable_nodes_fprint, b.dimension_start_cumuls_min,
1667                   b.dimension_start_cumuls_max, b.dimension_end_cumuls_min,
1668                   b.dimension_end_cumuls_max, b.dimension_capacities,
1669                   b.dimension_evaluator_classes,
1670                   b.required_resource_group_indices);
1671 }
1672 
ComputeVehicleClasses()1673 void RoutingModel::ComputeVehicleClasses() {
1674   vehicle_classes_.reserve(vehicles_);
1675   vehicle_classes_.clear();
1676   vehicle_class_index_of_vehicle_.assign(vehicles_, VehicleClassIndex(-1));
1677   std::map<VehicleClass, VehicleClassIndex, VehicleClassComparator>
1678       vehicle_class_map;
1679   const int nodes_unvisitability_num_bytes = (vehicle_vars_.size() + 7) / 8;
1680   std::unique_ptr<char[]> nodes_unvisitability_bitmask(
1681       new char[nodes_unvisitability_num_bytes]);
1682   for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
1683     VehicleClass vehicle_class;
1684     vehicle_class.cost_class_index = cost_class_index_of_vehicle_[vehicle];
1685     vehicle_class.fixed_cost = fixed_cost_of_vehicle_[vehicle];
1686     vehicle_class.used_when_empty = vehicle_used_when_empty_[vehicle];
1687     vehicle_class.start_equivalence_class =
1688         index_to_equivalence_class_[Start(vehicle)];
1689     vehicle_class.end_equivalence_class =
1690         index_to_equivalence_class_[End(vehicle)];
1691     for (const RoutingDimension* const dimension : dimensions_) {
1692       IntVar* const start_cumul_var = dimension->cumuls()[Start(vehicle)];
1693       vehicle_class.dimension_start_cumuls_min.push_back(
1694           start_cumul_var->Min());
1695       vehicle_class.dimension_start_cumuls_max.push_back(
1696           start_cumul_var->Max());
1697       IntVar* const end_cumul_var = dimension->cumuls()[End(vehicle)];
1698       vehicle_class.dimension_end_cumuls_min.push_back(end_cumul_var->Min());
1699       vehicle_class.dimension_end_cumuls_max.push_back(end_cumul_var->Max());
1700       vehicle_class.dimension_capacities.push_back(
1701           dimension->vehicle_capacities()[vehicle]);
1702       vehicle_class.dimension_evaluator_classes.push_back(
1703           dimension->vehicle_to_class(vehicle));
1704     }
1705     memset(nodes_unvisitability_bitmask.get(), 0,
1706            nodes_unvisitability_num_bytes);
1707     for (int index = 0; index < vehicle_vars_.size(); ++index) {
1708       IntVar* const vehicle_var = vehicle_vars_[index];
1709       if (!IsStart(index) && !IsEnd(index) &&
1710           (!vehicle_var->Contains(vehicle) ||
1711            !IsVehicleAllowedForIndex(vehicle, index))) {
1712         nodes_unvisitability_bitmask[index / CHAR_BIT] |= 1U
1713                                                           << (index % CHAR_BIT);
1714       }
1715     }
1716     vehicle_class.unvisitable_nodes_fprint = ThoroughHash(
1717         nodes_unvisitability_bitmask.get(), nodes_unvisitability_num_bytes);
1718     for (int rg_index = 0; rg_index < resource_groups_.size(); rg_index++) {
1719       if (resource_groups_[rg_index]->VehicleRequiresAResource(vehicle)) {
1720         vehicle_class.required_resource_group_indices.push_back(rg_index);
1721       }
1722     }
1723 
1724     const VehicleClassIndex num_vehicle_classes(vehicle_classes_.size());
1725     const VehicleClassIndex vehicle_class_index = gtl::LookupOrInsert(
1726         &vehicle_class_map, vehicle_class, num_vehicle_classes);
1727     if (vehicle_class_index == num_vehicle_classes) {  // New vehicle class
1728       vehicle_classes_.push_back(vehicle_class);
1729     }
1730     vehicle_class_index_of_vehicle_[vehicle] = vehicle_class_index;
1731   }
1732 }
1733 
ComputeVehicleTypes()1734 void RoutingModel::ComputeVehicleTypes() {
1735   const int nodes_squared = nodes_ * nodes_;
1736   std::vector<int>& type_index_of_vehicle =
1737       vehicle_type_container_.type_index_of_vehicle;
1738   std::vector<std::set<VehicleTypeContainer::VehicleClassEntry>>&
1739       sorted_vehicle_classes_per_type =
1740           vehicle_type_container_.sorted_vehicle_classes_per_type;
1741   std::vector<std::deque<int>>& vehicles_per_vehicle_class =
1742       vehicle_type_container_.vehicles_per_vehicle_class;
1743 
1744   type_index_of_vehicle.resize(vehicles_);
1745   sorted_vehicle_classes_per_type.clear();
1746   sorted_vehicle_classes_per_type.reserve(vehicles_);
1747   vehicles_per_vehicle_class.clear();
1748   vehicles_per_vehicle_class.resize(GetVehicleClassesCount());
1749 
1750   absl::flat_hash_map<int64_t, int> type_to_type_index;
1751 
1752   for (int v = 0; v < vehicles_; v++) {
1753     const int start = manager_.IndexToNode(Start(v)).value();
1754     const int end = manager_.IndexToNode(End(v)).value();
1755     const int cost_class = GetCostClassIndexOfVehicle(v).value();
1756     const int64_t type = cost_class * nodes_squared + start * nodes_ + end;
1757 
1758     const auto& vehicle_type_added = type_to_type_index.insert(
1759         std::make_pair(type, type_to_type_index.size()));
1760 
1761     const int index = vehicle_type_added.first->second;
1762 
1763     const int vehicle_class = GetVehicleClassIndexOfVehicle(v).value();
1764     const VehicleTypeContainer::VehicleClassEntry class_entry = {
1765         vehicle_class, GetFixedCostOfVehicle(v)};
1766 
1767     if (vehicle_type_added.second) {
1768       // Type was not indexed yet.
1769       DCHECK_EQ(sorted_vehicle_classes_per_type.size(), index);
1770       sorted_vehicle_classes_per_type.push_back({class_entry});
1771     } else {
1772       // Type already indexed.
1773       DCHECK_LT(index, sorted_vehicle_classes_per_type.size());
1774       sorted_vehicle_classes_per_type[index].insert(class_entry);
1775     }
1776     vehicles_per_vehicle_class[vehicle_class].push_back(v);
1777     type_index_of_vehicle[v] = index;
1778   }
1779 }
1780 
FinalizeVisitTypes()1781 void RoutingModel::FinalizeVisitTypes() {
1782   // NOTE(user): This is necessary if CloseVisitTypes() was not called
1783   // explicitly before. This will be removed when the TODO regarding this logic
1784   // is addressed.
1785   CloseVisitTypes();
1786 
1787   single_nodes_of_type_.clear();
1788   single_nodes_of_type_.resize(num_visit_types_);
1789   pair_indices_of_type_.clear();
1790   pair_indices_of_type_.resize(num_visit_types_);
1791   std::vector<absl::flat_hash_set<int>> pair_indices_added_for_type(
1792       num_visit_types_);
1793 
1794   for (int index = 0; index < index_to_visit_type_.size(); index++) {
1795     const int visit_type = GetVisitType(index);
1796     if (visit_type < 0) {
1797       continue;
1798     }
1799     const std::vector<std::pair<int, int>>& pickup_index_pairs =
1800         index_to_pickup_index_pairs_[index];
1801     const std::vector<std::pair<int, int>>& delivery_index_pairs =
1802         index_to_delivery_index_pairs_[index];
1803     if (pickup_index_pairs.empty() && delivery_index_pairs.empty()) {
1804       single_nodes_of_type_[visit_type].push_back(index);
1805     }
1806     for (const std::vector<std::pair<int, int>>* index_pairs :
1807          {&pickup_index_pairs, &delivery_index_pairs}) {
1808       for (const std::pair<int, int>& index_pair : *index_pairs) {
1809         const int pair_index = index_pair.first;
1810         if (pair_indices_added_for_type[visit_type].insert(pair_index).second) {
1811           pair_indices_of_type_[visit_type].push_back(pair_index);
1812         }
1813       }
1814     }
1815   }
1816 
1817   TopologicallySortVisitTypes();
1818 }
1819 
TopologicallySortVisitTypes()1820 void RoutingModel::TopologicallySortVisitTypes() {
1821   if (!has_same_vehicle_type_requirements_ &&
1822       !has_temporal_type_requirements_) {
1823     return;
1824   }
1825   std::vector<std::pair<double, double>> type_requirement_tightness(
1826       num_visit_types_, {0, 0});
1827   std::vector<absl::flat_hash_set<int>> type_to_dependent_types(
1828       num_visit_types_);
1829   SparseBitset<> types_in_requirement_graph(num_visit_types_);
1830   std::vector<int> in_degree(num_visit_types_, 0);
1831   for (int type = 0; type < num_visit_types_; type++) {
1832     int num_alternative_required_types = 0;
1833     int num_required_sets = 0;
1834     for (const std::vector<absl::flat_hash_set<int>>*
1835              required_type_alternatives :
1836          {&required_type_alternatives_when_adding_type_index_[type],
1837           &required_type_alternatives_when_removing_type_index_[type],
1838           &same_vehicle_required_type_alternatives_per_type_index_[type]}) {
1839       for (const absl::flat_hash_set<int>& alternatives :
1840            *required_type_alternatives) {
1841         types_in_requirement_graph.Set(type);
1842         num_required_sets++;
1843         for (int required_type : alternatives) {
1844           type_requirement_tightness[required_type].second +=
1845               1.0 / alternatives.size();
1846           types_in_requirement_graph.Set(required_type);
1847           num_alternative_required_types++;
1848           if (type_to_dependent_types[required_type].insert(type).second) {
1849             in_degree[type]++;
1850           }
1851         }
1852       }
1853     }
1854     if (num_alternative_required_types > 0) {
1855       type_requirement_tightness[type].first += 1.0 * num_required_sets *
1856                                                 num_required_sets /
1857                                                 num_alternative_required_types;
1858     }
1859   }
1860 
1861   // Compute topological order of visit types.
1862   topologically_sorted_visit_types_.clear();
1863   std::vector<int> current_types_with_zero_indegree;
1864   for (int type : types_in_requirement_graph.PositionsSetAtLeastOnce()) {
1865     DCHECK(type_requirement_tightness[type].first > 0 ||
1866            type_requirement_tightness[type].second > 0);
1867     if (in_degree[type] == 0) {
1868       current_types_with_zero_indegree.push_back(type);
1869     }
1870   }
1871 
1872   int num_types_added = 0;
1873   while (!current_types_with_zero_indegree.empty()) {
1874     // Add all zero-degree nodes to the same topological order group, while
1875     // also marking their dependent types that become part of the next group.
1876     topologically_sorted_visit_types_.push_back({});
1877     std::vector<int>& topological_group =
1878         topologically_sorted_visit_types_.back();
1879     std::vector<int> next_types_with_zero_indegree;
1880     for (int type : current_types_with_zero_indegree) {
1881       topological_group.push_back(type);
1882       num_types_added++;
1883       for (int dependent_type : type_to_dependent_types[type]) {
1884         DCHECK_GT(in_degree[dependent_type], 0);
1885         if (--in_degree[dependent_type] == 0) {
1886           next_types_with_zero_indegree.push_back(dependent_type);
1887         }
1888       }
1889     }
1890     // Sort the types in the current topological group based on their
1891     // requirement tightness.
1892     // NOTE: For a deterministic order, types with equal tightness are sorted by
1893     // increasing type.
1894     // TODO(user): Put types of the same topological order and same
1895     // requirement tightness in a single group (so that they all get inserted
1896     // simultaneously by the GlobalCheapestInsertion heuristic, for instance).
1897     std::sort(topological_group.begin(), topological_group.end(),
1898               [&type_requirement_tightness](int type1, int type2) {
1899                 const auto& tightness1 = type_requirement_tightness[type1];
1900                 const auto& tightness2 = type_requirement_tightness[type2];
1901                 return tightness1 > tightness2 ||
1902                        (tightness1 == tightness2 && type1 < type2);
1903               });
1904     // Swap the current types with zero in-degree with the next ones.
1905     current_types_with_zero_indegree.swap(next_types_with_zero_indegree);
1906   }
1907 
1908   const int num_types_in_requirement_graph =
1909       types_in_requirement_graph.NumberOfSetCallsWithDifferentArguments();
1910   DCHECK_LE(num_types_added, num_types_in_requirement_graph);
1911   if (num_types_added < num_types_in_requirement_graph) {
1912     // Requirement graph is cyclic, no topological order.
1913     topologically_sorted_visit_types_.clear();
1914   }
1915 }
1916 
AddDisjunction(const std::vector<int64_t> & indices,int64_t penalty,int64_t max_cardinality)1917 RoutingModel::DisjunctionIndex RoutingModel::AddDisjunction(
1918     const std::vector<int64_t>& indices, int64_t penalty,
1919     int64_t max_cardinality) {
1920   CHECK_GE(max_cardinality, 1);
1921   for (int i = 0; i < indices.size(); ++i) {
1922     CHECK_NE(kUnassigned, indices[i]);
1923   }
1924 
1925   const DisjunctionIndex disjunction_index(disjunctions_.size());
1926   disjunctions_.push_back({indices, {penalty, max_cardinality}});
1927   for (const int64_t index : indices) {
1928     index_to_disjunctions_[index].push_back(disjunction_index);
1929   }
1930   return disjunction_index;
1931 }
1932 
HasMandatoryDisjunctions() const1933 bool RoutingModel::HasMandatoryDisjunctions() const {
1934   for (const auto& [indices, value] : disjunctions_) {
1935     if (value.penalty == kNoPenalty) return true;
1936   }
1937   return false;
1938 }
1939 
HasMaxCardinalityConstrainedDisjunctions() const1940 bool RoutingModel::HasMaxCardinalityConstrainedDisjunctions() const {
1941   for (const auto& [indices, value] : disjunctions_) {
1942     if (indices.size() > value.max_cardinality) return true;
1943   }
1944   return false;
1945 }
1946 
1947 std::vector<std::pair<int64_t, int64_t>>
GetPerfectBinaryDisjunctions() const1948 RoutingModel::GetPerfectBinaryDisjunctions() const {
1949   std::vector<std::pair<int64_t, int64_t>> var_index_pairs;
1950   for (const Disjunction& disjunction : disjunctions_) {
1951     const std::vector<int64_t>& var_indices = disjunction.indices;
1952     if (var_indices.size() != 2) continue;
1953     const int64_t v0 = var_indices[0];
1954     const int64_t v1 = var_indices[1];
1955     if (index_to_disjunctions_[v0].size() == 1 &&
1956         index_to_disjunctions_[v1].size() == 1) {
1957       // We output sorted pairs.
1958       var_index_pairs.push_back({std::min(v0, v1), std::max(v0, v1)});
1959     }
1960   }
1961   std::sort(var_index_pairs.begin(), var_index_pairs.end());
1962   return var_index_pairs;
1963 }
1964 
IgnoreDisjunctionsAlreadyForcedToZero()1965 void RoutingModel::IgnoreDisjunctionsAlreadyForcedToZero() {
1966   CHECK(!closed_);
1967   for (Disjunction& disjunction : disjunctions_) {
1968     bool has_one_potentially_active_var = false;
1969     for (const int64_t var_index : disjunction.indices) {
1970       if (ActiveVar(var_index)->Max() > 0) {
1971         has_one_potentially_active_var = true;
1972         break;
1973       }
1974     }
1975     if (!has_one_potentially_active_var) {
1976       disjunction.value.max_cardinality = 0;
1977     }
1978   }
1979 }
1980 
CreateDisjunction(DisjunctionIndex disjunction)1981 IntVar* RoutingModel::CreateDisjunction(DisjunctionIndex disjunction) {
1982   const std::vector<int64_t>& indices = disjunctions_[disjunction].indices;
1983   const int indices_size = indices.size();
1984   std::vector<IntVar*> disjunction_vars(indices_size);
1985   for (int i = 0; i < indices_size; ++i) {
1986     const int64_t index = indices[i];
1987     CHECK_LT(index, Size());
1988     disjunction_vars[i] = ActiveVar(index);
1989   }
1990   const int64_t max_cardinality =
1991       disjunctions_[disjunction].value.max_cardinality;
1992   IntVar* no_active_var = solver_->MakeBoolVar();
1993   IntVar* number_active_vars = solver_->MakeIntVar(0, max_cardinality);
1994   solver_->AddConstraint(
1995       solver_->MakeSumEquality(disjunction_vars, number_active_vars));
1996   solver_->AddConstraint(solver_->MakeIsDifferentCstCt(
1997       number_active_vars, max_cardinality, no_active_var));
1998   const int64_t penalty = disjunctions_[disjunction].value.penalty;
1999   if (penalty < 0) {
2000     no_active_var->SetMax(0);
2001     return nullptr;
2002   } else {
2003     return solver_->MakeProd(no_active_var, penalty)->Var();
2004   }
2005 }
2006 
AddSoftSameVehicleConstraint(const std::vector<int64_t> & indices,int64_t cost)2007 void RoutingModel::AddSoftSameVehicleConstraint(
2008     const std::vector<int64_t>& indices, int64_t cost) {
2009   if (!indices.empty()) {
2010     ValuedNodes<int64_t> same_vehicle_cost;
2011     for (const int64_t index : indices) {
2012       same_vehicle_cost.indices.push_back(index);
2013     }
2014     same_vehicle_cost.value = cost;
2015     same_vehicle_costs_.push_back(same_vehicle_cost);
2016   }
2017 }
2018 
SetAllowedVehiclesForIndex(const std::vector<int> & vehicles,int64_t index)2019 void RoutingModel::SetAllowedVehiclesForIndex(const std::vector<int>& vehicles,
2020                                               int64_t index) {
2021   auto& allowed_vehicles = allowed_vehicles_[index];
2022   allowed_vehicles.clear();
2023   for (int vehicle : vehicles) {
2024     allowed_vehicles.insert(vehicle);
2025   }
2026 }
2027 
AddPickupAndDelivery(int64_t pickup,int64_t delivery)2028 void RoutingModel::AddPickupAndDelivery(int64_t pickup, int64_t delivery) {
2029   AddPickupAndDeliverySetsInternal({pickup}, {delivery});
2030   pickup_delivery_disjunctions_.push_back({kNoDisjunction, kNoDisjunction});
2031 }
2032 
AddPickupAndDeliverySets(DisjunctionIndex pickup_disjunction,DisjunctionIndex delivery_disjunction)2033 void RoutingModel::AddPickupAndDeliverySets(
2034     DisjunctionIndex pickup_disjunction,
2035     DisjunctionIndex delivery_disjunction) {
2036   AddPickupAndDeliverySetsInternal(
2037       GetDisjunctionNodeIndices(pickup_disjunction),
2038       GetDisjunctionNodeIndices(delivery_disjunction));
2039   pickup_delivery_disjunctions_.push_back(
2040       {pickup_disjunction, delivery_disjunction});
2041 }
2042 
AddPickupAndDeliverySetsInternal(const std::vector<int64_t> & pickups,const std::vector<int64_t> & deliveries)2043 void RoutingModel::AddPickupAndDeliverySetsInternal(
2044     const std::vector<int64_t>& pickups,
2045     const std::vector<int64_t>& deliveries) {
2046   if (pickups.empty() || deliveries.empty()) {
2047     return;
2048   }
2049   const int64_t size = Size();
2050   const int pair_index = pickup_delivery_pairs_.size();
2051   for (int pickup_index = 0; pickup_index < pickups.size(); pickup_index++) {
2052     const int64_t pickup = pickups[pickup_index];
2053     CHECK_LT(pickup, size);
2054     index_to_pickup_index_pairs_[pickup].emplace_back(pair_index, pickup_index);
2055   }
2056   for (int delivery_index = 0; delivery_index < deliveries.size();
2057        delivery_index++) {
2058     const int64_t delivery = deliveries[delivery_index];
2059     CHECK_LT(delivery, size);
2060     index_to_delivery_index_pairs_[delivery].emplace_back(pair_index,
2061                                                           delivery_index);
2062   }
2063   pickup_delivery_pairs_.push_back({pickups, deliveries});
2064 }
2065 
GetPickupIndexPairs(int64_t node_index) const2066 const std::vector<std::pair<int, int>>& RoutingModel::GetPickupIndexPairs(
2067     int64_t node_index) const {
2068   CHECK_LT(node_index, index_to_pickup_index_pairs_.size());
2069   return index_to_pickup_index_pairs_[node_index];
2070 }
2071 
GetDeliveryIndexPairs(int64_t node_index) const2072 const std::vector<std::pair<int, int>>& RoutingModel::GetDeliveryIndexPairs(
2073     int64_t node_index) const {
2074   CHECK_LT(node_index, index_to_delivery_index_pairs_.size());
2075   return index_to_delivery_index_pairs_[node_index];
2076 }
2077 
SetPickupAndDeliveryPolicyOfVehicle(PickupAndDeliveryPolicy policy,int vehicle)2078 void RoutingModel::SetPickupAndDeliveryPolicyOfVehicle(
2079     PickupAndDeliveryPolicy policy, int vehicle) {
2080   CHECK_LT(vehicle, vehicles_);
2081   vehicle_pickup_delivery_policy_[vehicle] = policy;
2082 }
2083 
SetPickupAndDeliveryPolicyOfAllVehicles(PickupAndDeliveryPolicy policy)2084 void RoutingModel::SetPickupAndDeliveryPolicyOfAllVehicles(
2085     PickupAndDeliveryPolicy policy) {
2086   CHECK_LT(0, vehicles_);
2087   for (int i = 0; i < vehicles_; ++i) {
2088     SetPickupAndDeliveryPolicyOfVehicle(policy, i);
2089   }
2090 }
2091 
2092 RoutingModel::PickupAndDeliveryPolicy
GetPickupAndDeliveryPolicyOfVehicle(int vehicle) const2093 RoutingModel::GetPickupAndDeliveryPolicyOfVehicle(int vehicle) const {
2094   CHECK_LT(vehicle, vehicles_);
2095   return vehicle_pickup_delivery_policy_[vehicle];
2096 }
2097 
GetNumOfSingletonNodes() const2098 int RoutingModel::GetNumOfSingletonNodes() const {
2099   int count = 0;
2100   for (int i = 0; i < Nexts().size(); ++i) {
2101     // End nodes have no next variables.
2102     if (!IsStart(i) && GetPickupIndexPairs(i).empty() &&
2103         GetDeliveryIndexPairs(i).empty()) {
2104       ++count;
2105     }
2106   }
2107   return count;
2108 }
2109 
CreateSameVehicleCost(int vehicle_index)2110 IntVar* RoutingModel::CreateSameVehicleCost(int vehicle_index) {
2111   const std::vector<int64_t>& indices =
2112       same_vehicle_costs_[vehicle_index].indices;
2113   CHECK(!indices.empty());
2114   std::vector<IntVar*> vehicle_counts;
2115   solver_->MakeIntVarArray(vehicle_vars_.size() + 1, 0, indices.size() + 1,
2116                            &vehicle_counts);
2117   std::vector<int64_t> vehicle_values(vehicle_vars_.size() + 1);
2118   for (int i = 0; i < vehicle_vars_.size(); ++i) {
2119     vehicle_values[i] = i;
2120   }
2121   vehicle_values[vehicle_vars_.size()] = -1;
2122   std::vector<IntVar*> vehicle_vars;
2123   vehicle_vars.reserve(indices.size());
2124   for (const int64_t index : indices) {
2125     vehicle_vars.push_back(vehicle_vars_[index]);
2126   }
2127   solver_->AddConstraint(solver_->MakeDistribute(vehicle_vars, vehicle_counts));
2128   std::vector<IntVar*> vehicle_used;
2129   for (int i = 0; i < vehicle_vars_.size() + 1; ++i) {
2130     vehicle_used.push_back(
2131         solver_->MakeIsGreaterOrEqualCstVar(vehicle_counts[i], 1));
2132   }
2133   vehicle_used.push_back(solver_->MakeIntConst(-1));
2134   return solver_
2135       ->MakeProd(solver_->MakeMax(solver_->MakeSum(vehicle_used), 0),
2136                  same_vehicle_costs_[vehicle_index].value)
2137       ->Var();
2138 }
2139 
AddLocalSearchOperator(LocalSearchOperator * ls_operator)2140 void RoutingModel::AddLocalSearchOperator(LocalSearchOperator* ls_operator) {
2141   extra_operators_.push_back(ls_operator);
2142 }
2143 
GetDepot() const2144 int64_t RoutingModel::GetDepot() const {
2145   return vehicles() > 0 ? Start(0) : -1;
2146 }
2147 
2148 // TODO(user): Remove the need for the homogeneous version once the
2149 // vehicle var to cost class element constraint is fast enough.
AppendHomogeneousArcCosts(const RoutingSearchParameters & parameters,int node_index,std::vector<IntVar * > * cost_elements)2150 void RoutingModel::AppendHomogeneousArcCosts(
2151     const RoutingSearchParameters& parameters, int node_index,
2152     std::vector<IntVar*>* cost_elements) {
2153   CHECK(cost_elements != nullptr);
2154   const auto arc_cost_evaluator = [this, node_index](int64_t next_index) {
2155     return GetHomogeneousCost(node_index, next_index);
2156   };
2157   if (UsesLightPropagation(parameters)) {
2158     // Only supporting positive costs.
2159     // TODO(user): Detect why changing lower bound to kint64min stalls
2160     // the search in GLS in some cases (Solomon instances for instance).
2161     IntVar* const base_cost_var =
2162         solver_->MakeIntVar(0, std::numeric_limits<int64_t>::max());
2163     solver_->AddConstraint(MakeLightElement(
2164         solver_.get(), base_cost_var, nexts_[node_index], arc_cost_evaluator,
2165         [this]() { return enable_deep_serialization_; }));
2166     IntVar* const var =
2167         solver_->MakeProd(base_cost_var, active_[node_index])->Var();
2168     cost_elements->push_back(var);
2169   } else {
2170     IntExpr* const expr =
2171         solver_->MakeElement(arc_cost_evaluator, nexts_[node_index]);
2172     IntVar* const var = solver_->MakeProd(expr, active_[node_index])->Var();
2173     cost_elements->push_back(var);
2174   }
2175 }
2176 
AppendArcCosts(const RoutingSearchParameters & parameters,int node_index,std::vector<IntVar * > * cost_elements)2177 void RoutingModel::AppendArcCosts(const RoutingSearchParameters& parameters,
2178                                   int node_index,
2179                                   std::vector<IntVar*>* cost_elements) {
2180   CHECK(cost_elements != nullptr);
2181   DCHECK_GT(vehicles_, 0);
2182   if (UsesLightPropagation(parameters)) {
2183     // Only supporting positive costs.
2184     // TODO(user): Detect why changing lower bound to kint64min stalls
2185     // the search in GLS in some cases (Solomon instances for instance).
2186     IntVar* const base_cost_var =
2187         solver_->MakeIntVar(0, std::numeric_limits<int64_t>::max());
2188     solver_->AddConstraint(MakeLightElement2(
2189         solver_.get(), base_cost_var, nexts_[node_index],
2190         vehicle_vars_[node_index],
2191         [this, node_index](int64_t to, int64_t vehicle) {
2192           return GetArcCostForVehicle(node_index, to, vehicle);
2193         },
2194         [this]() { return enable_deep_serialization_; }));
2195     IntVar* const var =
2196         solver_->MakeProd(base_cost_var, active_[node_index])->Var();
2197     cost_elements->push_back(var);
2198   } else {
2199     IntVar* const vehicle_class_var =
2200         solver_
2201             ->MakeElement(
2202                 [this](int64_t index) {
2203                   return SafeGetCostClassInt64OfVehicle(index);
2204                 },
2205                 vehicle_vars_[node_index])
2206             ->Var();
2207     IntExpr* const expr = solver_->MakeElement(
2208         [this, node_index](int64_t next, int64_t vehicle_class) {
2209           return GetArcCostForClass(node_index, next, vehicle_class);
2210         },
2211         nexts_[node_index], vehicle_class_var);
2212     IntVar* const var = solver_->MakeProd(expr, active_[node_index])->Var();
2213     cost_elements->push_back(var);
2214   }
2215 }
2216 
GetVehicleStartClass(int64_t start_index) const2217 int RoutingModel::GetVehicleStartClass(int64_t start_index) const {
2218   const int vehicle = index_to_vehicle_[start_index];
2219   if (vehicle != kUnassigned) {
2220     return GetVehicleClassIndexOfVehicle(vehicle).value();
2221   }
2222   return kUnassigned;
2223 }
2224 
FindErrorInSearchParametersForModel(const RoutingSearchParameters & search_parameters) const2225 std::string RoutingModel::FindErrorInSearchParametersForModel(
2226     const RoutingSearchParameters& search_parameters) const {
2227   const FirstSolutionStrategy::Value first_solution_strategy =
2228       search_parameters.first_solution_strategy();
2229   if (GetFirstSolutionDecisionBuilder(search_parameters) == nullptr) {
2230     return absl::StrCat(
2231         "Undefined first solution strategy: ",
2232         FirstSolutionStrategy::Value_Name(first_solution_strategy),
2233         " (int value: ", first_solution_strategy, ")");
2234   }
2235   if (search_parameters.first_solution_strategy() ==
2236           FirstSolutionStrategy::SWEEP &&
2237       sweep_arranger() == nullptr) {
2238     return "Undefined sweep arranger for ROUTING_SWEEP strategy.";
2239   }
2240   return "";
2241 }
2242 
QuietCloseModel()2243 void RoutingModel::QuietCloseModel() {
2244   QuietCloseModelWithParameters(DefaultRoutingSearchParameters());
2245 }
2246 
CloseModel()2247 void RoutingModel::CloseModel() {
2248   CloseModelWithParameters(DefaultRoutingSearchParameters());
2249 }
2250 
2251 class RoutingModelInspector : public ModelVisitor {
2252  public:
RoutingModelInspector(RoutingModel * model)2253   explicit RoutingModelInspector(RoutingModel* model) : model_(model) {
2254     same_vehicle_components_.SetNumberOfNodes(model->Size());
2255     for (const std::string& name : model->GetAllDimensionNames()) {
2256       RoutingDimension* const dimension = model->GetMutableDimension(name);
2257       const std::vector<IntVar*>& cumuls = dimension->cumuls();
2258       for (int i = 0; i < cumuls.size(); ++i) {
2259         cumul_to_dim_indices_[cumuls[i]] = {dimension, i};
2260       }
2261     }
2262     const std::vector<IntVar*>& vehicle_vars = model->VehicleVars();
2263     for (int i = 0; i < vehicle_vars.size(); ++i) {
2264       vehicle_var_to_indices_[vehicle_vars[i]] = i;
2265     }
2266     RegisterInspectors();
2267   }
~RoutingModelInspector()2268   ~RoutingModelInspector() override {}
EndVisitModel(const std::string & solver_name)2269   void EndVisitModel(const std::string& solver_name) override {
2270     const std::vector<int> node_to_same_vehicle_component_id =
2271         same_vehicle_components_.GetComponentIds();
2272     model_->InitSameVehicleGroups(
2273         same_vehicle_components_.GetNumberOfComponents());
2274     for (int node = 0; node < model_->Size(); ++node) {
2275       model_->SetSameVehicleGroup(node,
2276                                   node_to_same_vehicle_component_id[node]);
2277     }
2278     // TODO(user): Perform transitive closure of dimension precedence graphs.
2279     // TODO(user): Have a single annotated precedence graph.
2280   }
EndVisitConstraint(const std::string & type_name,const Constraint * const constraint)2281   void EndVisitConstraint(const std::string& type_name,
2282                           const Constraint* const constraint) override {
2283     gtl::FindWithDefault(constraint_inspectors_, type_name, []() {})();
2284   }
VisitIntegerExpressionArgument(const std::string & type_name,IntExpr * const expr)2285   void VisitIntegerExpressionArgument(const std::string& type_name,
2286                                       IntExpr* const expr) override {
2287     gtl::FindWithDefault(expr_inspectors_, type_name,
2288                          [](const IntExpr* expr) {})(expr);
2289   }
VisitIntegerArrayArgument(const std::string & arg_name,const std::vector<int64_t> & values)2290   void VisitIntegerArrayArgument(const std::string& arg_name,
2291                                  const std::vector<int64_t>& values) override {
2292     gtl::FindWithDefault(array_inspectors_, arg_name,
2293                          [](const std::vector<int64_t>& int_array) {})(values);
2294   }
2295 
2296  private:
2297   using ExprInspector = std::function<void(const IntExpr*)>;
2298   using ArrayInspector = std::function<void(const std::vector<int64_t>&)>;
2299   using ConstraintInspector = std::function<void()>;
2300 
RegisterInspectors()2301   void RegisterInspectors() {
2302     expr_inspectors_[kExpressionArgument] = [this](const IntExpr* expr) {
2303       expr_ = expr;
2304     };
2305     expr_inspectors_[kLeftArgument] = [this](const IntExpr* expr) {
2306       left_ = expr;
2307     };
2308     expr_inspectors_[kRightArgument] = [this](const IntExpr* expr) {
2309       right_ = expr;
2310     };
2311     array_inspectors_[kStartsArgument] =
2312         [this](const std::vector<int64_t>& int_array) {
2313           starts_argument_ = int_array;
2314         };
2315     array_inspectors_[kEndsArgument] =
2316         [this](const std::vector<int64_t>& int_array) {
2317           ends_argument_ = int_array;
2318         };
2319     constraint_inspectors_[kNotMember] = [this]() {
2320       std::pair<RoutingDimension*, int> dim_index;
2321       if (gtl::FindCopy(cumul_to_dim_indices_, expr_, &dim_index)) {
2322         RoutingDimension* const dimension = dim_index.first;
2323         const int index = dim_index.second;
2324         dimension->forbidden_intervals_[index].InsertIntervals(starts_argument_,
2325                                                                ends_argument_);
2326         VLOG(2) << dimension->name() << " " << index << ": "
2327                 << dimension->forbidden_intervals_[index].DebugString();
2328       }
2329       expr_ = nullptr;
2330       starts_argument_.clear();
2331       ends_argument_.clear();
2332     };
2333     constraint_inspectors_[kEquality] = [this]() {
2334       int left_index = 0;
2335       int right_index = 0;
2336       if (gtl::FindCopy(vehicle_var_to_indices_, left_, &left_index) &&
2337           gtl::FindCopy(vehicle_var_to_indices_, right_, &right_index)) {
2338         VLOG(2) << "Vehicle variables for " << left_index << " and "
2339                 << right_index << " are equal.";
2340         same_vehicle_components_.AddEdge(left_index, right_index);
2341       }
2342       left_ = nullptr;
2343       right_ = nullptr;
2344     };
2345     constraint_inspectors_[kLessOrEqual] = [this]() {
2346       std::pair<RoutingDimension*, int> left_index;
2347       std::pair<RoutingDimension*, int> right_index;
2348       if (gtl::FindCopy(cumul_to_dim_indices_, left_, &left_index) &&
2349           gtl::FindCopy(cumul_to_dim_indices_, right_, &right_index)) {
2350         RoutingDimension* const dimension = left_index.first;
2351         if (dimension == right_index.first) {
2352           VLOG(2) << "For dimension " << dimension->name() << ", cumul for "
2353                   << left_index.second << " is less than " << right_index.second
2354                   << ".";
2355           dimension->path_precedence_graph_.AddArc(left_index.second,
2356                                                    right_index.second);
2357         }
2358       }
2359       left_ = nullptr;
2360       right_ = nullptr;
2361     };
2362   }
2363 
2364   RoutingModel* const model_;
2365   DenseConnectedComponentsFinder same_vehicle_components_;
2366   absl::flat_hash_map<const IntExpr*, std::pair<RoutingDimension*, int>>
2367       cumul_to_dim_indices_;
2368   absl::flat_hash_map<const IntExpr*, int> vehicle_var_to_indices_;
2369   absl::flat_hash_map<std::string, ExprInspector> expr_inspectors_;
2370   absl::flat_hash_map<std::string, ArrayInspector> array_inspectors_;
2371   absl::flat_hash_map<std::string, ConstraintInspector> constraint_inspectors_;
2372   const IntExpr* expr_ = nullptr;
2373   const IntExpr* left_ = nullptr;
2374   const IntExpr* right_ = nullptr;
2375   std::vector<int64_t> starts_argument_;
2376   std::vector<int64_t> ends_argument_;
2377 };
2378 
DetectImplicitPickupAndDeliveries()2379 void RoutingModel::DetectImplicitPickupAndDeliveries() {
2380   std::vector<int> non_pickup_delivery_nodes;
2381   for (int node = 0; node < Size(); ++node) {
2382     if (!IsStart(node) && GetPickupIndexPairs(node).empty() &&
2383         GetDeliveryIndexPairs(node).empty()) {
2384       non_pickup_delivery_nodes.push_back(node);
2385     }
2386   }
2387   // Needs to be sorted for stability.
2388   std::set<std::pair<int64_t, int64_t>> implicit_pickup_deliveries;
2389   for (const RoutingDimension* const dimension : dimensions_) {
2390     if (dimension->class_evaluators_.size() != 1) {
2391       continue;
2392     }
2393     const TransitCallback1& transit =
2394         UnaryTransitCallbackOrNull(dimension->class_evaluators_[0]);
2395     if (transit == nullptr) continue;
2396     absl::flat_hash_map<int64_t, std::vector<int64_t>> nodes_by_positive_demand;
2397     absl::flat_hash_map<int64_t, std::vector<int64_t>> nodes_by_negative_demand;
2398     for (int node : non_pickup_delivery_nodes) {
2399       const int64_t demand = transit(node);
2400       if (demand > 0) {
2401         nodes_by_positive_demand[demand].push_back(node);
2402       } else if (demand < 0) {
2403         nodes_by_negative_demand[-demand].push_back(node);
2404       }
2405     }
2406     for (const auto& [demand, positive_nodes] : nodes_by_positive_demand) {
2407       const std::vector<int64_t>* const negative_nodes =
2408           gtl::FindOrNull(nodes_by_negative_demand, demand);
2409       if (negative_nodes != nullptr) {
2410         for (int64_t positive_node : positive_nodes) {
2411           for (int64_t negative_node : *negative_nodes) {
2412             implicit_pickup_deliveries.insert({positive_node, negative_node});
2413           }
2414         }
2415       }
2416     }
2417   }
2418   implicit_pickup_delivery_pairs_without_alternatives_.clear();
2419   for (auto [pickup, delivery] : implicit_pickup_deliveries) {
2420     implicit_pickup_delivery_pairs_without_alternatives_.emplace_back(
2421         std::vector<int64_t>({pickup}), std::vector<int64_t>({delivery}));
2422   }
2423 }
2424 
CloseModelWithParameters(const RoutingSearchParameters & parameters)2425 void RoutingModel::CloseModelWithParameters(
2426     const RoutingSearchParameters& parameters) {
2427   std::string error = FindErrorInRoutingSearchParameters(parameters);
2428   if (!error.empty()) {
2429     status_ = ROUTING_INVALID;
2430     LOG(ERROR) << "Invalid RoutingSearchParameters: " << error;
2431     return;
2432   }
2433   if (closed_) {
2434     LOG(WARNING) << "Model already closed";
2435     return;
2436   }
2437   closed_ = true;
2438 
2439   for (RoutingDimension* const dimension : dimensions_) {
2440     dimension->CloseModel(UsesLightPropagation(parameters));
2441   }
2442 
2443   dimension_resource_group_indices_.resize(dimensions_.size());
2444   for (int rg_index = 0; rg_index < resource_groups_.size(); rg_index++) {
2445     const ResourceGroup& resource_group = *resource_groups_[rg_index];
2446     if (resource_group.GetVehiclesRequiringAResource().empty()) continue;
2447     for (DimensionIndex dim_index :
2448          resource_group.GetAffectedDimensionIndices()) {
2449       dimension_resource_group_indices_[dim_index].push_back(rg_index);
2450     }
2451   }
2452 
2453   ComputeCostClasses(parameters);
2454   ComputeVehicleClasses();
2455   ComputeVehicleTypes();
2456   FinalizeVisitTypes();
2457   vehicle_start_class_callback_ = [this](int64_t start) {
2458     return GetVehicleStartClass(start);
2459   };
2460 
2461   AddNoCycleConstraintInternal();
2462 
2463   const int size = Size();
2464 
2465   // Vehicle variable constraints
2466   for (int i = 0; i < vehicles_; ++i) {
2467     const int64_t start = starts_[i];
2468     const int64_t end = ends_[i];
2469     solver_->AddConstraint(
2470         solver_->MakeEquality(vehicle_vars_[start], solver_->MakeIntConst(i)));
2471     solver_->AddConstraint(
2472         solver_->MakeEquality(vehicle_vars_[end], solver_->MakeIntConst(i)));
2473     solver_->AddConstraint(
2474         solver_->MakeIsDifferentCstCt(nexts_[start], end, vehicle_active_[i]));
2475     if (vehicle_used_when_empty_[i]) {
2476       vehicle_route_considered_[i]->SetMin(1);
2477     } else {
2478       solver_->AddConstraint(solver_->MakeEquality(
2479           vehicle_active_[i], vehicle_route_considered_[i]));
2480     }
2481   }
2482 
2483   // Limit the number of vehicles with non-empty routes.
2484   if (vehicles_ > max_active_vehicles_) {
2485     solver_->AddConstraint(
2486         solver_->MakeSumLessOrEqual(vehicle_active_, max_active_vehicles_));
2487   }
2488 
2489   // If there is only one vehicle in the model the vehicle variables will have
2490   // a maximum domain of [-1, 0]. If a node is performed/active then its vehicle
2491   // variable will be reduced to [0] making the path-cumul constraint below
2492   // useless. If the node is unperformed/unactive then its vehicle variable will
2493   // be reduced to [-1] in any case.
2494   if (vehicles_ > 1) {
2495     std::vector<IntVar*> zero_transit(size, solver_->MakeIntConst(0));
2496     solver_->AddConstraint(solver_->MakeDelayedPathCumul(
2497         nexts_, active_, vehicle_vars_, zero_transit));
2498   }
2499 
2500   // Nodes which are not in a disjunction are mandatory, and those with a
2501   // trivially infeasible type are necessarily unperformed
2502   for (int i = 0; i < size; ++i) {
2503     if (GetDisjunctionIndices(i).empty() && active_[i]->Max() != 0) {
2504       active_[i]->SetValue(1);
2505     }
2506     const int type = GetVisitType(i);
2507     if (type == kUnassigned) {
2508       continue;
2509     }
2510     const absl::flat_hash_set<VisitTypePolicy>* const infeasible_policies =
2511         gtl::FindOrNull(trivially_infeasible_visit_types_to_policies_, type);
2512     if (infeasible_policies != nullptr &&
2513         gtl::ContainsKey(*infeasible_policies, index_to_type_policy_[i])) {
2514       active_[i]->SetValue(0);
2515     }
2516   }
2517 
2518   // Reduce domains of vehicle variables
2519   for (int i = 0; i < allowed_vehicles_.size(); ++i) {
2520     const auto& allowed_vehicles = allowed_vehicles_[i];
2521     if (!allowed_vehicles.empty()) {
2522       std::vector<int64_t> vehicles;
2523       vehicles.reserve(allowed_vehicles.size() + 1);
2524       vehicles.push_back(-1);
2525       for (int vehicle : allowed_vehicles) {
2526         vehicles.push_back(vehicle);
2527       }
2528       solver_->AddConstraint(solver_->MakeMemberCt(VehicleVar(i), vehicles));
2529     }
2530   }
2531 
2532   // Reduce domain of next variables.
2533   for (int i = 0; i < size; ++i) {
2534     // No variable can point back to a start.
2535     solver_->AddConstraint(solver_->RevAlloc(
2536         new DifferentFromValues(solver_.get(), nexts_[i], starts_)));
2537     // Extra constraint to state an active node can't point to itself.
2538     solver_->AddConstraint(
2539         solver_->MakeIsDifferentCstCt(nexts_[i], i, active_[i]));
2540   }
2541 
2542   // Add constraints to bind vehicle_vars_[i] to -1 in case that node i is not
2543   // active.
2544   for (int i = 0; i < size; ++i) {
2545     solver_->AddConstraint(
2546         solver_->MakeIsDifferentCstCt(vehicle_vars_[i], -1, active_[i]));
2547   }
2548 
2549   if (HasTypeRegulations()) {
2550     solver_->AddConstraint(
2551         solver_->RevAlloc(new TypeRegulationsConstraint(*this)));
2552   }
2553 
2554   // Associate first and "logical" last nodes
2555   for (int i = 0; i < vehicles_; ++i) {
2556     std::vector<int64_t> forbidden_ends;
2557     forbidden_ends.reserve(vehicles_ - 1);
2558     for (int j = 0; j < vehicles_; ++j) {
2559       if (i != j) {
2560         forbidden_ends.push_back(ends_[j]);
2561       }
2562     }
2563     solver_->AddConstraint(solver_->RevAlloc(new DifferentFromValues(
2564         solver_.get(), nexts_[starts_[i]], std::move(forbidden_ends))));
2565   }
2566 
2567   // Constraining is_bound_to_end_ variables.
2568   for (const int64_t end : ends_) {
2569     is_bound_to_end_[end]->SetValue(1);
2570   }
2571 
2572   std::vector<IntVar*> cost_elements;
2573   // Arc and dimension costs.
2574   if (vehicles_ > 0) {
2575     for (int node_index = 0; node_index < size; ++node_index) {
2576       if (CostsAreHomogeneousAcrossVehicles()) {
2577         AppendHomogeneousArcCosts(parameters, node_index, &cost_elements);
2578       } else {
2579         AppendArcCosts(parameters, node_index, &cost_elements);
2580       }
2581     }
2582     if (vehicle_amortized_cost_factors_set_) {
2583       std::vector<IntVar*> route_lengths;
2584       solver_->MakeIntVarArray(vehicles_, 0, size, &route_lengths);
2585       solver_->AddConstraint(
2586           solver_->MakeDistribute(vehicle_vars_, route_lengths));
2587       std::vector<IntVar*> vehicle_used;
2588       for (int i = 0; i < vehicles_; i++) {
2589         // The start/end of the vehicle are always on the route.
2590         vehicle_used.push_back(
2591             solver_->MakeIsGreaterCstVar(route_lengths[i], 2));
2592         IntVar* const var =
2593             solver_
2594                 ->MakeProd(solver_->MakeOpposite(solver_->MakeSquare(
2595                                solver_->MakeSum(route_lengths[i], -2))),
2596                            quadratic_cost_factor_of_vehicle_[i])
2597                 ->Var();
2598         cost_elements.push_back(var);
2599       }
2600       IntVar* const vehicle_usage_cost =
2601           solver_->MakeScalProd(vehicle_used, linear_cost_factor_of_vehicle_)
2602               ->Var();
2603       cost_elements.push_back(vehicle_usage_cost);
2604     }
2605   }
2606   // Dimension span constraints: cost and limits.
2607   for (const RoutingDimension* dimension : dimensions_) {
2608     dimension->SetupGlobalSpanCost(&cost_elements);
2609     dimension->SetupSlackAndDependentTransitCosts();
2610     const std::vector<int64_t>& span_costs =
2611         dimension->vehicle_span_cost_coefficients();
2612     const std::vector<int64_t>& span_ubs =
2613         dimension->vehicle_span_upper_bounds();
2614     const bool has_span_constraint =
2615         std::any_of(span_costs.begin(), span_costs.end(),
2616                     [](int64_t coeff) { return coeff != 0; }) ||
2617         std::any_of(span_ubs.begin(), span_ubs.end(),
2618                     [](int64_t value) {
2619                       return value < std::numeric_limits<int64_t>::max();
2620                     }) ||
2621         dimension->HasSoftSpanUpperBounds() ||
2622         dimension->HasQuadraticCostSoftSpanUpperBounds();
2623     if (has_span_constraint) {
2624       std::vector<IntVar*> spans(vehicles(), nullptr);
2625       std::vector<IntVar*> total_slacks(vehicles(), nullptr);
2626       // Generate variables only where needed.
2627       for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2628         if (span_ubs[vehicle] < std::numeric_limits<int64_t>::max()) {
2629           spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle], "");
2630         }
2631         if (span_costs[vehicle] != 0) {
2632           total_slacks[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle], "");
2633         }
2634       }
2635       if (dimension->HasSoftSpanUpperBounds()) {
2636         for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2637           if (spans[vehicle]) continue;
2638           const SimpleBoundCosts::BoundCost bound_cost =
2639               dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2640           if (bound_cost.cost == 0) continue;
2641           spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2642         }
2643       }
2644       if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2645         for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2646           if (spans[vehicle]) continue;
2647           const SimpleBoundCosts::BoundCost bound_cost =
2648               dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2649           if (bound_cost.cost == 0) continue;
2650           spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2651         }
2652       }
2653       solver_->AddConstraint(
2654           MakePathSpansAndTotalSlacks(dimension, spans, total_slacks));
2655       // If a vehicle's span is constrained, its start/end cumuls must be
2656       // instantiated.
2657       for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2658         if (!spans[vehicle] && !total_slacks[vehicle]) continue;
2659         if (spans[vehicle]) {
2660           AddVariableTargetToFinalizer(spans[vehicle],
2661                                        std::numeric_limits<int64_t>::min());
2662         }
2663         AddVariableTargetToFinalizer(dimension->CumulVar(End(vehicle)),
2664                                      std::numeric_limits<int64_t>::min());
2665         AddVariableTargetToFinalizer(dimension->CumulVar(Start(vehicle)),
2666                                      std::numeric_limits<int64_t>::max());
2667       }
2668       // Add costs of variables.
2669       for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2670         if (span_costs[vehicle] == 0) continue;
2671         DCHECK(total_slacks[vehicle] != nullptr);
2672         IntVar* const slack_amount =
2673             solver_
2674                 ->MakeProd(vehicle_route_considered_[vehicle],
2675                            total_slacks[vehicle])
2676                 ->Var();
2677         IntVar* const slack_cost =
2678             solver_->MakeProd(slack_amount, span_costs[vehicle])->Var();
2679         cost_elements.push_back(slack_cost);
2680         AddWeightedVariableMinimizedByFinalizer(slack_amount,
2681                                                 span_costs[vehicle]);
2682       }
2683       if (dimension->HasSoftSpanUpperBounds()) {
2684         for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2685           const auto bound_cost =
2686               dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2687           if (bound_cost.cost == 0 ||
2688               bound_cost.bound == std::numeric_limits<int64_t>::max())
2689             continue;
2690           DCHECK(spans[vehicle] != nullptr);
2691           // Additional cost is vehicle_cost_considered_[vehicle] *
2692           // max(0, spans[vehicle] - bound_cost.bound) * bound_cost.cost.
2693           IntVar* const span_violation_amount =
2694               solver_
2695                   ->MakeProd(
2696                       vehicle_route_considered_[vehicle],
2697                       solver_->MakeMax(
2698                           solver_->MakeSum(spans[vehicle], -bound_cost.bound),
2699                           0))
2700                   ->Var();
2701           IntVar* const span_violation_cost =
2702               solver_->MakeProd(span_violation_amount, bound_cost.cost)->Var();
2703           cost_elements.push_back(span_violation_cost);
2704           AddWeightedVariableMinimizedByFinalizer(span_violation_amount,
2705                                                   bound_cost.cost);
2706         }
2707       }
2708       if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2709         for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2710           const auto bound_cost =
2711               dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2712           if (bound_cost.cost == 0 ||
2713               bound_cost.bound == std::numeric_limits<int64_t>::max())
2714             continue;
2715           DCHECK(spans[vehicle] != nullptr);
2716           // Additional cost is vehicle_cost_considered_[vehicle] *
2717           // max(0, spans[vehicle] - bound_cost.bound)^2 * bound_cost.cost.
2718           IntExpr* max0 = solver_->MakeMax(
2719               solver_->MakeSum(spans[vehicle], -bound_cost.bound), 0);
2720           IntVar* const squared_span_violation_amount =
2721               solver_
2722                   ->MakeProd(vehicle_route_considered_[vehicle],
2723                              solver_->MakeSquare(max0))
2724                   ->Var();
2725           IntVar* const span_violation_cost =
2726               solver_->MakeProd(squared_span_violation_amount, bound_cost.cost)
2727                   ->Var();
2728           cost_elements.push_back(span_violation_cost);
2729           AddWeightedVariableMinimizedByFinalizer(squared_span_violation_amount,
2730                                                   bound_cost.cost);
2731         }
2732       }
2733     }
2734   }
2735   // Penalty costs
2736   for (DisjunctionIndex i(0); i < disjunctions_.size(); ++i) {
2737     IntVar* penalty_var = CreateDisjunction(i);
2738     if (penalty_var != nullptr) {
2739       cost_elements.push_back(penalty_var);
2740     }
2741   }
2742   // Soft cumul lower/upper bound costs
2743   for (const RoutingDimension* dimension : dimensions_) {
2744     dimension->SetupCumulVarSoftLowerBoundCosts(&cost_elements);
2745     dimension->SetupCumulVarSoftUpperBoundCosts(&cost_elements);
2746     dimension->SetupCumulVarPiecewiseLinearCosts(&cost_elements);
2747   }
2748   // Same vehicle costs
2749   for (int i = 0; i < same_vehicle_costs_.size(); ++i) {
2750     cost_elements.push_back(CreateSameVehicleCost(i));
2751   }
2752   cost_ = solver_->MakeSum(cost_elements)->Var();
2753   cost_->set_name("Cost");
2754 
2755   // Pickup-delivery precedences
2756   std::vector<std::pair<int, int>> pickup_delivery_precedences;
2757   for (const auto& pair : pickup_delivery_pairs_) {
2758     DCHECK(!pair.first.empty() && !pair.second.empty());
2759     for (int pickup : pair.first) {
2760       for (int delivery : pair.second) {
2761         pickup_delivery_precedences.emplace_back(pickup, delivery);
2762       }
2763     }
2764   }
2765   std::vector<int> lifo_vehicles;
2766   std::vector<int> fifo_vehicles;
2767   for (int i = 0; i < vehicles_; ++i) {
2768     switch (vehicle_pickup_delivery_policy_[i]) {
2769       case PICKUP_AND_DELIVERY_NO_ORDER:
2770         break;
2771       case PICKUP_AND_DELIVERY_LIFO:
2772         lifo_vehicles.push_back(Start(i));
2773         break;
2774       case PICKUP_AND_DELIVERY_FIFO:
2775         fifo_vehicles.push_back(Start(i));
2776         break;
2777     }
2778   }
2779   solver_->AddConstraint(solver_->MakePathPrecedenceConstraint(
2780       nexts_, pickup_delivery_precedences, lifo_vehicles, fifo_vehicles));
2781 
2782   // Detect constraints
2783   enable_deep_serialization_ = false;
2784   std::unique_ptr<RoutingModelInspector> inspector(
2785       new RoutingModelInspector(this));
2786   solver_->Accept(inspector.get());
2787   enable_deep_serialization_ = true;
2788 
2789   for (const RoutingDimension* const dimension : dimensions_) {
2790     // Dimension path precedences, discovered by model inspection (which must be
2791     // performed before adding path transit precedences).
2792     const ReverseArcListGraph<int, int>& graph =
2793         dimension->GetPathPrecedenceGraph();
2794     std::vector<std::pair<int, int>> path_precedences;
2795     for (const auto tail : graph.AllNodes()) {
2796       for (const auto head : graph[tail]) {
2797         path_precedences.emplace_back(tail, head);
2798       }
2799     }
2800     if (!path_precedences.empty()) {
2801       solver_->AddConstraint(solver_->MakePathTransitPrecedenceConstraint(
2802           nexts_, dimension->transits(), path_precedences));
2803     }
2804 
2805     // Dimension node precedences.
2806     for (const RoutingDimension::NodePrecedence& node_precedence :
2807          dimension->GetNodePrecedences()) {
2808       const int64_t first_node = node_precedence.first_node;
2809       const int64_t second_node = node_precedence.second_node;
2810       IntExpr* const nodes_are_selected =
2811           solver_->MakeMin(active_[first_node], active_[second_node]);
2812       IntExpr* const cumul_difference = solver_->MakeDifference(
2813           dimension->CumulVar(second_node), dimension->CumulVar(first_node));
2814       IntVar* const cumul_difference_is_ge_offset =
2815           solver_->MakeIsGreaterOrEqualCstVar(cumul_difference,
2816                                               node_precedence.offset);
2817       // Forces the implication: both nodes are active => cumul difference
2818       // constraint is active.
2819       solver_->AddConstraint(solver_->MakeLessOrEqual(
2820           nodes_are_selected->Var(), cumul_difference_is_ge_offset));
2821     }
2822   }
2823 
2824   // Setup resource vars and constraints.
2825   resource_vars_.resize(resource_groups_.size());
2826   for (int rg = 0; rg < resource_groups_.size(); rg++) {
2827     const ResourceGroup* const resource_group = resource_groups_[rg].get();
2828 
2829     const int num_resources = resource_group->Size();
2830     std::vector<IntVar*>& resource_vars = resource_vars_[rg];
2831     solver_->MakeIntVarArray(vehicles_, -1, num_resources - 1,
2832                              absl::StrCat("Resources[", rg, "]"),
2833                              &resource_vars);
2834     // Resources cannot be shared, so assigned resources must all be different
2835     // (note that resource_var == -1 means no resource assigned).
2836     solver_->AddConstraint(solver_->MakeAllDifferentExcept(resource_vars, -1));
2837     for (int v = 0; v < vehicles_; v++) {
2838       IntVar* const resource_var = resource_vars[v];
2839       AddToAssignment(resource_var);
2840       if (!resource_group->VehicleRequiresAResource(v)) {
2841         resource_var->SetValue(-1);
2842         continue;
2843       }
2844       // vehicle_route_considered_[v] <--> resource_vars[v] != -1.
2845       solver_->AddConstraint(solver_->MakeEquality(
2846           vehicle_route_considered_[v],
2847           solver_->MakeIsDifferentCstVar(resource_var, -1)));
2848 
2849       // Add dimension cumul constraints.
2850       for (const DimensionIndex d :
2851            resource_group->GetAffectedDimensionIndices()) {
2852         const RoutingDimension* const dim = dimensions_[d];
2853         // resource_start_lb_var <= cumul[start(v)] <= resource_start_ub_var
2854         IntVar* const start_cumul_var = dim->CumulVar(Start(v));
2855         IntVar* const resource_start_lb_var =
2856             solver_->MakeIntVar(std::numeric_limits<int64_t>::min(),
2857                                 std::numeric_limits<int64_t>::max());
2858         solver_->AddConstraint(MakeLightElement(
2859             solver_.get(), resource_start_lb_var, resource_var,
2860             [dim, resource_group](int r) {
2861               if (r < 0) return std::numeric_limits<int64_t>::min();
2862               return resource_group->GetResources()[r]
2863                   .GetDimensionAttributes(dim)
2864                   .start_domain()
2865                   .Min();
2866             },
2867             [this]() { return enable_deep_serialization_; }));
2868         solver_->AddConstraint(solver_->MakeGreaterOrEqual(
2869             start_cumul_var, resource_start_lb_var));
2870 
2871         IntVar* const resource_start_ub_var =
2872             solver_->MakeIntVar(std::numeric_limits<int64_t>::min(),
2873                                 std::numeric_limits<int64_t>::max());
2874         solver_->AddConstraint(MakeLightElement(
2875             solver_.get(), resource_start_ub_var, resource_var,
2876             [dim, resource_group](int r) {
2877               if (r < 0) return std::numeric_limits<int64_t>::max();
2878               return resource_group->GetResources()[r]
2879                   .GetDimensionAttributes(dim)
2880                   .start_domain()
2881                   .Max();
2882             },
2883             [this]() { return enable_deep_serialization_; }));
2884         solver_->AddConstraint(
2885             solver_->MakeLessOrEqual(start_cumul_var, resource_start_ub_var));
2886 
2887         // resource_end_lb_var <= cumul[end(v)] <= resource_end_ub_var
2888         IntVar* const end_cumul_var = dim->CumulVar(End(v));
2889         IntVar* const resource_end_lb_var =
2890             solver_->MakeIntVar(std::numeric_limits<int64_t>::min(),
2891                                 std::numeric_limits<int64_t>::max());
2892         solver_->AddConstraint(MakeLightElement(
2893             solver_.get(), resource_end_lb_var, resource_var,
2894             [dim, resource_group](int r) {
2895               if (r < 0) return std::numeric_limits<int64_t>::min();
2896               return resource_group->GetResources()[r]
2897                   .GetDimensionAttributes(dim)
2898                   .end_domain()
2899                   .Min();
2900             },
2901             [this]() { return enable_deep_serialization_; }));
2902         solver_->AddConstraint(
2903             solver_->MakeGreaterOrEqual(end_cumul_var, resource_end_lb_var));
2904 
2905         IntVar* const resource_end_ub_var =
2906             solver_->MakeIntVar(std::numeric_limits<int64_t>::min(),
2907                                 std::numeric_limits<int64_t>::max());
2908         solver_->AddConstraint(MakeLightElement(
2909             solver_.get(), resource_end_ub_var, resource_var,
2910             [dim, resource_group](int r) {
2911               if (r < 0) return std::numeric_limits<int64_t>::max();
2912               return resource_group->GetResources()[r]
2913                   .GetDimensionAttributes(dim)
2914                   .end_domain()
2915                   .Max();
2916             },
2917             [this]() { return enable_deep_serialization_; }));
2918         solver_->AddConstraint(
2919             solver_->MakeLessOrEqual(end_cumul_var, resource_end_ub_var));
2920       }
2921     }
2922   }
2923 
2924   DetectImplicitPickupAndDeliveries();
2925 
2926   // Store the local/global cumul optimizers, along with their offsets.
2927   StoreDimensionCumulOptimizers(parameters);
2928 
2929   // Keep this out of SetupSearch as this contains static search objects.
2930   // This will allow calling SetupSearch multiple times with different search
2931   // parameters.
2932   CreateNeighborhoodOperators(parameters);
2933   CreateFirstSolutionDecisionBuilders(parameters);
2934   error = FindErrorInSearchParametersForModel(parameters);
2935   if (!error.empty()) {
2936     status_ = ROUTING_INVALID;
2937     LOG(ERROR) << "Invalid RoutingSearchParameters for this model: " << error;
2938     return;
2939   }
2940   SetupSearch(parameters);
2941 }
2942 
2943 namespace {
2944 // A decision builder that tries to set variables to their value in the last
2945 // solution, if their corresponding vehicle path has not changed.
2946 // This tries to constrain all such variables in one shot in order to speed up
2947 // instantiation.
2948 // TODO(user): try to use Assignment instead of MakeAssignment(),
2949 //   try to record and restore the min/max instead of a single value.
2950 class RestoreDimensionValuesForUnchangedRoutes : public DecisionBuilder {
2951  public:
RestoreDimensionValuesForUnchangedRoutes(RoutingModel * model)2952   explicit RestoreDimensionValuesForUnchangedRoutes(RoutingModel* model)
2953       : model_(model) {
2954     model_->AddAtSolutionCallback([this]() { AtSolution(); });
2955     next_last_value_.resize(model_->Nexts().size(), -1);
2956   }
2957 
2958   // In a given branch of a search tree, this decision builder only returns
2959   // a Decision once, the first time it is called in that branch.
Next(Solver * const s)2960   Decision* Next(Solver* const s) override {
2961     if (!must_return_decision_) return nullptr;
2962     s->SaveAndSetValue(&must_return_decision_, false);
2963     return MakeDecision(s);
2964   }
2965 
2966  private:
2967   // Initialize() is lazy to make sure all dimensions have been instantiated
2968   // when initialization is done.
Initialize()2969   void Initialize() {
2970     is_initialized_ = true;
2971     const int num_nodes = model_->VehicleVars().size();
2972     node_to_integer_variable_indices_.resize(num_nodes);
2973     node_to_interval_variable_indices_.resize(num_nodes);
2974     // Search for dimension variables that correspond to input variables.
2975     for (const std::string& dimension_name : model_->GetAllDimensionNames()) {
2976       const RoutingDimension& dimension =
2977           model_->GetDimensionOrDie(dimension_name);
2978       // Search among cumuls and slacks, and attach them to corresponding nodes.
2979       for (const std::vector<IntVar*>& dimension_variables :
2980            {dimension.cumuls(), dimension.slacks()}) {
2981         const int num_dimension_variables = dimension_variables.size();
2982         DCHECK_LE(num_dimension_variables, num_nodes);
2983         for (int node = 0; node < num_dimension_variables; ++node) {
2984           node_to_integer_variable_indices_[node].push_back(
2985               integer_variables_.size());
2986           integer_variables_.push_back(dimension_variables[node]);
2987         }
2988       }
2989       // Search for break start/end variables, attach them to vehicle starts.
2990       for (int vehicle = 0; vehicle < model_->vehicles(); ++vehicle) {
2991         if (!dimension.HasBreakConstraints()) continue;
2992         const int vehicle_start = model_->Start(vehicle);
2993         for (IntervalVar* interval :
2994              dimension.GetBreakIntervalsOfVehicle(vehicle)) {
2995           node_to_interval_variable_indices_[vehicle_start].push_back(
2996               interval_variables_.size());
2997           interval_variables_.push_back(interval);
2998         }
2999       }
3000     }
3001     integer_variables_last_min_.resize(integer_variables_.size());
3002     interval_variables_last_start_min_.resize(interval_variables_.size());
3003     interval_variables_last_end_max_.resize(interval_variables_.size());
3004   }
3005 
MakeDecision(Solver * const s)3006   Decision* MakeDecision(Solver* const s) {
3007     if (!is_initialized_) return nullptr;
3008     // Collect vehicles that have not changed.
3009     std::vector<int> unchanged_vehicles;
3010     const int num_vehicles = model_->vehicles();
3011     for (int v = 0; v < num_vehicles; ++v) {
3012       bool unchanged = true;
3013       for (int current = model_->Start(v); !model_->IsEnd(current);
3014            current = next_last_value_[current]) {
3015         if (!model_->NextVar(current)->Bound() ||
3016             next_last_value_[current] != model_->NextVar(current)->Value()) {
3017           unchanged = false;
3018           break;
3019         }
3020       }
3021       if (unchanged) unchanged_vehicles.push_back(v);
3022     }
3023     // If all routes are unchanged, the solver might be trying to do a full
3024     // reschedule. Do nothing.
3025     if (unchanged_vehicles.size() == num_vehicles) return nullptr;
3026 
3027     // Collect cumuls and slacks of unchanged routes to be assigned a value.
3028     std::vector<IntVar*> vars;
3029     std::vector<int64_t> values;
3030     for (const int vehicle : unchanged_vehicles) {
3031       for (int current = model_->Start(vehicle); true;
3032            current = next_last_value_[current]) {
3033         for (const int index : node_to_integer_variable_indices_[current]) {
3034           vars.push_back(integer_variables_[index]);
3035           values.push_back(integer_variables_last_min_[index]);
3036         }
3037         for (const int index : node_to_interval_variable_indices_[current]) {
3038           const int64_t start_min = interval_variables_last_start_min_[index];
3039           const int64_t end_max = interval_variables_last_end_max_[index];
3040           if (start_min < end_max) {
3041             vars.push_back(interval_variables_[index]->SafeStartExpr(0)->Var());
3042             values.push_back(interval_variables_last_start_min_[index]);
3043             vars.push_back(interval_variables_[index]->SafeEndExpr(0)->Var());
3044             values.push_back(interval_variables_last_end_max_[index]);
3045           } else {
3046             vars.push_back(interval_variables_[index]->PerformedExpr()->Var());
3047             values.push_back(0);
3048           }
3049         }
3050         if (model_->IsEnd(current)) break;
3051       }
3052     }
3053     return s->MakeAssignVariablesValuesOrDoNothing(vars, values);
3054   }
3055 
AtSolution()3056   void AtSolution() {
3057     if (!is_initialized_) Initialize();
3058     const int num_integers = integer_variables_.size();
3059     // Variables may not be fixed at solution time,
3060     // the decision builder is fine with the Min() of the unfixed variables.
3061     for (int i = 0; i < num_integers; ++i) {
3062       integer_variables_last_min_[i] = integer_variables_[i]->Min();
3063     }
3064     const int num_intervals = interval_variables_.size();
3065     for (int i = 0; i < num_intervals; ++i) {
3066       const bool is_performed = interval_variables_[i]->MustBePerformed();
3067       interval_variables_last_start_min_[i] =
3068           is_performed ? interval_variables_[i]->StartMin() : 0;
3069       interval_variables_last_end_max_[i] =
3070           is_performed ? interval_variables_[i]->EndMax() : -1;
3071     }
3072     const int num_nodes = next_last_value_.size();
3073     for (int node = 0; node < num_nodes; ++node) {
3074       if (model_->IsEnd(node)) continue;
3075       next_last_value_[node] = model_->NextVar(node)->Value();
3076     }
3077   }
3078 
3079   // Input data.
3080   RoutingModel* const model_;
3081 
3082   // The valuation of the last solution.
3083   std::vector<int> next_last_value_;
3084   // For every node, the indices of integer_variables_ and interval_variables_
3085   // that correspond to that node.
3086   std::vector<std::vector<int>> node_to_integer_variable_indices_;
3087   std::vector<std::vector<int>> node_to_interval_variable_indices_;
3088   // Variables and the value they had in the previous solution.
3089   std::vector<IntVar*> integer_variables_;
3090   std::vector<int64_t> integer_variables_last_min_;
3091   std::vector<IntervalVar*> interval_variables_;
3092   std::vector<int64_t> interval_variables_last_start_min_;
3093   std::vector<int64_t> interval_variables_last_end_max_;
3094 
3095   bool is_initialized_ = false;
3096   bool must_return_decision_ = true;
3097 };
3098 }  // namespace
3099 
MakeRestoreDimensionValuesForUnchangedRoutes(RoutingModel * model)3100 DecisionBuilder* MakeRestoreDimensionValuesForUnchangedRoutes(
3101     RoutingModel* model) {
3102   return model->solver()->RevAlloc(
3103       new RestoreDimensionValuesForUnchangedRoutes(model));
3104 }
3105 
AddSearchMonitor(SearchMonitor * const monitor)3106 void RoutingModel::AddSearchMonitor(SearchMonitor* const monitor) {
3107   monitors_.push_back(monitor);
3108 }
3109 
3110 namespace {
3111 class AtSolutionCallbackMonitor : public SearchMonitor {
3112  public:
AtSolutionCallbackMonitor(Solver * solver,std::function<void ()> callback)3113   AtSolutionCallbackMonitor(Solver* solver, std::function<void()> callback)
3114       : SearchMonitor(solver), callback_(std::move(callback)) {}
AtSolution()3115   bool AtSolution() override {
3116     callback_();
3117     return false;
3118   }
3119 
3120  private:
3121   std::function<void()> callback_;
3122 };
3123 }  // namespace
3124 
AddAtSolutionCallback(std::function<void ()> callback)3125 void RoutingModel::AddAtSolutionCallback(std::function<void()> callback) {
3126   AddSearchMonitor(solver_->RevAlloc(
3127       new AtSolutionCallbackMonitor(solver_.get(), std::move(callback))));
3128 }
3129 
Solve(const Assignment * assignment)3130 const Assignment* RoutingModel::Solve(const Assignment* assignment) {
3131   return SolveFromAssignmentWithParameters(assignment,
3132                                            DefaultRoutingSearchParameters());
3133 }
3134 
SolveWithParameters(const RoutingSearchParameters & parameters,std::vector<const Assignment * > * solutions)3135 const Assignment* RoutingModel::SolveWithParameters(
3136     const RoutingSearchParameters& parameters,
3137     std::vector<const Assignment*>* solutions) {
3138   return SolveFromAssignmentWithParameters(nullptr, parameters, solutions);
3139 }
3140 
3141 namespace {
GetTimeLimit(const RoutingSearchParameters & parameters)3142 absl::Duration GetTimeLimit(const RoutingSearchParameters& parameters) {
3143   if (!parameters.has_time_limit()) return absl::InfiniteDuration();
3144   return util_time::DecodeGoogleApiProto(parameters.time_limit()).value();
3145 }
3146 
GetLnsTimeLimit(const RoutingSearchParameters & parameters)3147 absl::Duration GetLnsTimeLimit(const RoutingSearchParameters& parameters) {
3148   if (!parameters.has_lns_time_limit()) return absl::InfiniteDuration();
3149   return util_time::DecodeGoogleApiProto(parameters.lns_time_limit()).value();
3150 }
3151 
3152 }  // namespace
3153 
3154 namespace {
MakeAllUnperformedInAssignment(const RoutingModel * model,Assignment * assignment)3155 void MakeAllUnperformedInAssignment(const RoutingModel* model,
3156                                     Assignment* assignment) {
3157   assignment->Clear();
3158   for (int i = 0; i < model->Nexts().size(); ++i) {
3159     if (!model->IsStart(i)) {
3160       assignment->Add(model->NextVar(i))->SetValue(i);
3161     }
3162   }
3163   for (int vehicle = 0; vehicle < model->vehicles(); ++vehicle) {
3164     assignment->Add(model->NextVar(model->Start(vehicle)))
3165         ->SetValue(model->End(vehicle));
3166   }
3167 }
3168 }  //  namespace
3169 
AppendAssignmentIfFeasible(const Assignment & assignment,std::vector<std::unique_ptr<Assignment>> * assignments)3170 bool RoutingModel::AppendAssignmentIfFeasible(
3171     const Assignment& assignment,
3172     std::vector<std::unique_ptr<Assignment>>* assignments) {
3173   tmp_assignment_->CopyIntersection(&assignment);
3174   solver_->Solve(restore_tmp_assignment_, collect_one_assignment_,
3175                  GetOrCreateLimit());
3176   if (collect_one_assignment_->solution_count() == 1) {
3177     assignments->push_back(absl::make_unique<Assignment>(solver_.get()));
3178     assignments->back()->Copy(collect_one_assignment_->solution(0));
3179     return true;
3180   }
3181   return false;
3182 }
3183 
LogSolution(const RoutingSearchParameters & parameters,const std::string & description,int64_t solution_cost,int64_t start_time_ms)3184 void RoutingModel::LogSolution(const RoutingSearchParameters& parameters,
3185                                const std::string& description,
3186                                int64_t solution_cost, int64_t start_time_ms) {
3187   const std::string memory_str = MemoryUsage();
3188   const double cost_scaling_factor = parameters.log_cost_scaling_factor();
3189   const double cost_offset = parameters.log_cost_offset();
3190   const std::string cost_string =
3191       cost_scaling_factor == 1.0 && cost_offset == 0.0
3192           ? absl::StrCat(solution_cost)
3193           : absl::StrFormat(
3194                 "%d (%.8lf)", solution_cost,
3195                 cost_scaling_factor * (solution_cost + cost_offset));
3196   LOG(INFO) << absl::StrFormat(
3197       "%s (%s, time = %d ms, memory used = %s)", description, cost_string,
3198       solver_->wall_time() - start_time_ms, memory_str);
3199 }
3200 
SolveFromAssignmentWithParameters(const Assignment * assignment,const RoutingSearchParameters & parameters,std::vector<const Assignment * > * solutions)3201 const Assignment* RoutingModel::SolveFromAssignmentWithParameters(
3202     const Assignment* assignment, const RoutingSearchParameters& parameters,
3203     std::vector<const Assignment*>* solutions) {
3204   return SolveFromAssignmentsWithParameters({assignment}, parameters,
3205                                             solutions);
3206 }
3207 
SolveFromAssignmentsWithParameters(const std::vector<const Assignment * > & assignments,const RoutingSearchParameters & parameters,std::vector<const Assignment * > * solutions)3208 const Assignment* RoutingModel::SolveFromAssignmentsWithParameters(
3209     const std::vector<const Assignment*>& assignments,
3210     const RoutingSearchParameters& parameters,
3211     std::vector<const Assignment*>* solutions) {
3212   const int64_t start_time_ms = solver_->wall_time();
3213   QuietCloseModelWithParameters(parameters);
3214   VLOG(1) << "Search parameters:\n" << parameters.DebugString();
3215   if (solutions != nullptr) solutions->clear();
3216   if (status_ == ROUTING_INVALID) {
3217     return nullptr;
3218   }
3219   const auto update_time_limits = [this, start_time_ms, &parameters]() {
3220     const absl::Duration elapsed_time =
3221         absl::Milliseconds(solver_->wall_time() - start_time_ms);
3222     const absl::Duration time_left = GetTimeLimit(parameters) - elapsed_time;
3223     if (time_left >= absl::ZeroDuration()) {
3224       limit_->UpdateLimits(time_left, std::numeric_limits<int64_t>::max(),
3225                            std::numeric_limits<int64_t>::max(),
3226                            parameters.solution_limit());
3227       ls_limit_->UpdateLimits(time_left, std::numeric_limits<int64_t>::max(),
3228                               std::numeric_limits<int64_t>::max(), 1);
3229       return true;
3230     }
3231     return false;
3232   };
3233   if (!update_time_limits()) {
3234     status_ = ROUTING_FAIL_TIMEOUT;
3235     return nullptr;
3236   }
3237   lns_limit_->UpdateLimits(
3238       GetLnsTimeLimit(parameters), std::numeric_limits<int64_t>::max(),
3239       std::numeric_limits<int64_t>::max(), std::numeric_limits<int64_t>::max());
3240   // NOTE: Allow more time for the first solution's scheduling, since if it
3241   // fails, we won't have anything to build upon.
3242   // We set this time limit based on whether local/global dimension optimizers
3243   // are used in the finalizer to avoid going over the general time limit.
3244   // TODO(user): Adapt this when absolute timeouts are given to the model.
3245   const int time_limit_shares = 1 + !global_dimension_optimizers_.empty() +
3246                                 !local_dimension_optimizers_.empty();
3247   const absl::Duration first_solution_lns_time_limit =
3248       std::max(GetTimeLimit(parameters) / time_limit_shares,
3249                GetLnsTimeLimit(parameters));
3250   first_solution_lns_limit_->UpdateLimits(
3251       first_solution_lns_time_limit, std::numeric_limits<int64_t>::max(),
3252       std::numeric_limits<int64_t>::max(), std::numeric_limits<int64_t>::max());
3253 
3254   std::vector<std::unique_ptr<Assignment>> solution_pool;
3255   std::vector<const Assignment*> first_solution_assignments;
3256   for (const Assignment* assignment : assignments) {
3257     if (assignment != nullptr) first_solution_assignments.push_back(assignment);
3258   }
3259   if (parameters.use_cp() == BOOL_TRUE) {
3260     if (first_solution_assignments.empty()) {
3261       bool solution_found = false;
3262       Assignment matching(solver_.get());
3263       if (IsMatchingModel() && SolveMatchingModel(&matching, parameters) &&
3264           AppendAssignmentIfFeasible(matching, &solution_pool)) {
3265         if (parameters.log_search()) {
3266           LogSolution(parameters, "Min-Cost Flow Solution",
3267                       solution_pool.back()->ObjectiveValue(), start_time_ms);
3268         }
3269         solution_found = true;
3270       }
3271       if (!solution_found) {
3272         // Build trivial solutions to which we can come back too in case the
3273         // solver does not manage to build something better.
3274         Assignment unperformed(solver_.get());
3275         MakeAllUnperformedInAssignment(this, &unperformed);
3276         if (AppendAssignmentIfFeasible(unperformed, &solution_pool) &&
3277             parameters.log_search()) {
3278           LogSolution(parameters, "All Unperformed Solution",
3279                       solution_pool.back()->ObjectiveValue(), start_time_ms);
3280         }
3281         if (update_time_limits()) {
3282           solver_->Solve(solve_db_, monitors_);
3283         }
3284       }
3285     } else {
3286       for (const Assignment* assignment : first_solution_assignments) {
3287         assignment_->CopyIntersection(assignment);
3288         solver_->Solve(improve_db_, monitors_);
3289         if (collect_assignments_->solution_count() >= 1 ||
3290             !update_time_limits()) {
3291           break;
3292         }
3293       }
3294     }
3295   }
3296 
3297   if (parameters.use_cp_sat() == BOOL_TRUE ||
3298       parameters.use_generalized_cp_sat() == BOOL_TRUE) {
3299     const int solution_count = collect_assignments_->solution_count();
3300     Assignment* const cp_solution =
3301         solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
3302                             : nullptr;
3303     Assignment sat_solution(solver_.get());
3304     if (SolveModelWithSat(*this, parameters, cp_solution, &sat_solution) &&
3305         AppendAssignmentIfFeasible(sat_solution, &solution_pool) &&
3306         parameters.log_search()) {
3307       LogSolution(parameters, "SAT", solution_pool.back()->ObjectiveValue(),
3308                   start_time_ms);
3309     }
3310   }
3311 
3312   const absl::Duration elapsed_time =
3313       absl::Milliseconds(solver_->wall_time() - start_time_ms);
3314   const int solution_count = collect_assignments_->solution_count();
3315   if (solution_count >= 1 || !solution_pool.empty()) {
3316     status_ = ROUTING_SUCCESS;
3317     if (solutions != nullptr) {
3318       for (int i = 0; i < solution_count; ++i) {
3319         solutions->push_back(
3320             solver_->MakeAssignment(collect_assignments_->solution(i)));
3321       }
3322       for (const auto& solution : solution_pool) {
3323         if (solutions->empty() ||
3324             solution->ObjectiveValue() < solutions->back()->ObjectiveValue()) {
3325           solutions->push_back(solver_->MakeAssignment(solution.get()));
3326         }
3327       }
3328       return solutions->back();
3329     }
3330     Assignment* best_assignment =
3331         solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
3332                             : nullptr;
3333     for (const auto& solution : solution_pool) {
3334       if (best_assignment == nullptr ||
3335           solution->ObjectiveValue() < best_assignment->ObjectiveValue()) {
3336         best_assignment = solution.get();
3337       }
3338     }
3339     return solver_->MakeAssignment(best_assignment);
3340   } else {
3341     if (elapsed_time >= GetTimeLimit(parameters)) {
3342       status_ = ROUTING_FAIL_TIMEOUT;
3343     } else {
3344       status_ = ROUTING_FAIL;
3345     }
3346     return nullptr;
3347   }
3348 }
3349 
SetAssignmentFromOtherModelAssignment(Assignment * target_assignment,const RoutingModel * source_model,const Assignment * source_assignment)3350 void RoutingModel::SetAssignmentFromOtherModelAssignment(
3351     Assignment* target_assignment, const RoutingModel* source_model,
3352     const Assignment* source_assignment) {
3353   const int size = Size();
3354   DCHECK_EQ(size, source_model->Size());
3355   CHECK_EQ(target_assignment->solver(), solver_.get());
3356 
3357   if (CostsAreHomogeneousAcrossVehicles()) {
3358     SetAssignmentFromAssignment(target_assignment, Nexts(), source_assignment,
3359                                 source_model->Nexts());
3360   } else {
3361     std::vector<IntVar*> source_vars(size + size + vehicles_);
3362     std::vector<IntVar*> target_vars(size + size + vehicles_);
3363     for (int index = 0; index < size; index++) {
3364       source_vars[index] = source_model->NextVar(index);
3365       target_vars[index] = NextVar(index);
3366     }
3367     for (int index = 0; index < size + vehicles_; index++) {
3368       source_vars[size + index] = source_model->VehicleVar(index);
3369       target_vars[size + index] = VehicleVar(index);
3370     }
3371     SetAssignmentFromAssignment(target_assignment, target_vars,
3372                                 source_assignment, source_vars);
3373   }
3374 
3375   target_assignment->AddObjective(cost_);
3376 }
3377 
3378 // Computing a lower bound to the cost of a vehicle routing problem solving a
3379 // a linear assignment problem (minimum-cost perfect bipartite matching).
3380 // A bipartite graph is created with left nodes representing the nodes of the
3381 // routing problem and right nodes representing possible node successors; an
3382 // arc between a left node l and a right node r is created if r can be the
3383 // node folowing l in a route (Next(l) = r); the cost of the arc is the transit
3384 // cost between l and r in the routing problem.
3385 // This is a lower bound given the solution to assignment problem does not
3386 // necessarily produce a (set of) closed route(s) from a starting node to an
3387 // ending node.
ComputeLowerBound()3388 int64_t RoutingModel::ComputeLowerBound() {
3389   if (!closed_) {
3390     LOG(WARNING) << "Non-closed model not supported.";
3391     return 0;
3392   }
3393   if (!CostsAreHomogeneousAcrossVehicles()) {
3394     LOG(WARNING) << "Non-homogeneous vehicle costs not supported";
3395     return 0;
3396   }
3397   if (!disjunctions_.empty()) {
3398     LOG(WARNING)
3399         << "Node disjunction constraints or optional nodes not supported.";
3400     return 0;
3401   }
3402   const int num_nodes = Size() + vehicles_;
3403   ForwardStarGraph graph(2 * num_nodes, num_nodes * num_nodes);
3404   LinearSumAssignment<ForwardStarGraph> linear_sum_assignment(graph, num_nodes);
3405   // Adding arcs for non-end nodes, based on possible values of next variables.
3406   // Left nodes in the bipartite are indexed from 0 to num_nodes - 1; right
3407   // nodes are indexed from num_nodes to 2 * num_nodes - 1.
3408   for (int tail = 0; tail < Size(); ++tail) {
3409     std::unique_ptr<IntVarIterator> iterator(
3410         nexts_[tail]->MakeDomainIterator(false));
3411     for (const int64_t head : InitAndGetValues(iterator.get())) {
3412       // Given there are no disjunction constraints, a node cannot point to
3413       // itself. Doing this explicitly given that outside the search,
3414       // propagation hasn't removed this value from next variables yet.
3415       if (head == tail) {
3416         continue;
3417       }
3418       // The index of a right node in the bipartite graph is the index
3419       // of the successor offset by the number of nodes.
3420       const ArcIndex arc = graph.AddArc(tail, num_nodes + head);
3421       const CostValue cost = GetHomogeneousCost(tail, head);
3422       linear_sum_assignment.SetArcCost(arc, cost);
3423     }
3424   }
3425   // The linear assignment library requires having as many left and right nodes.
3426   // Therefore we are creating fake assignments for end nodes, forced to point
3427   // to the equivalent start node with a cost of 0.
3428   for (int tail = Size(); tail < num_nodes; ++tail) {
3429     const ArcIndex arc = graph.AddArc(tail, num_nodes + starts_[tail - Size()]);
3430     linear_sum_assignment.SetArcCost(arc, 0);
3431   }
3432   if (linear_sum_assignment.ComputeAssignment()) {
3433     return linear_sum_assignment.GetCost();
3434   }
3435   return 0;
3436 }
3437 
RouteCanBeUsedByVehicle(const Assignment & assignment,int start_index,int vehicle) const3438 bool RoutingModel::RouteCanBeUsedByVehicle(const Assignment& assignment,
3439                                            int start_index, int vehicle) const {
3440   int current_index =
3441       IsStart(start_index) ? Next(assignment, start_index) : start_index;
3442   while (!IsEnd(current_index)) {
3443     const IntVar* const vehicle_var = VehicleVar(current_index);
3444     if (!vehicle_var->Contains(vehicle)) {
3445       return false;
3446     }
3447     const int next_index = Next(assignment, current_index);
3448     CHECK_NE(next_index, current_index) << "Inactive node inside a route";
3449     current_index = next_index;
3450   }
3451   return true;
3452 }
3453 
ReplaceUnusedVehicle(int unused_vehicle,int active_vehicle,Assignment * const compact_assignment) const3454 bool RoutingModel::ReplaceUnusedVehicle(
3455     int unused_vehicle, int active_vehicle,
3456     Assignment* const compact_assignment) const {
3457   CHECK(compact_assignment != nullptr);
3458   CHECK(!IsVehicleUsed(*compact_assignment, unused_vehicle));
3459   CHECK(IsVehicleUsed(*compact_assignment, active_vehicle));
3460   // Swap NextVars at start nodes.
3461   const int unused_vehicle_start = Start(unused_vehicle);
3462   IntVar* const unused_vehicle_start_var = NextVar(unused_vehicle_start);
3463   const int unused_vehicle_end = End(unused_vehicle);
3464   const int active_vehicle_start = Start(active_vehicle);
3465   const int active_vehicle_end = End(active_vehicle);
3466   IntVar* const active_vehicle_start_var = NextVar(active_vehicle_start);
3467   const int active_vehicle_next =
3468       compact_assignment->Value(active_vehicle_start_var);
3469   compact_assignment->SetValue(unused_vehicle_start_var, active_vehicle_next);
3470   compact_assignment->SetValue(active_vehicle_start_var, End(active_vehicle));
3471 
3472   // Update VehicleVars along the route, update the last NextVar.
3473   int current_index = active_vehicle_next;
3474   while (!IsEnd(current_index)) {
3475     IntVar* const vehicle_var = VehicleVar(current_index);
3476     compact_assignment->SetValue(vehicle_var, unused_vehicle);
3477     const int next_index = Next(*compact_assignment, current_index);
3478     if (IsEnd(next_index)) {
3479       IntVar* const last_next_var = NextVar(current_index);
3480       compact_assignment->SetValue(last_next_var, End(unused_vehicle));
3481     }
3482     current_index = next_index;
3483   }
3484 
3485   // Update dimensions: update transits at the start.
3486   for (const RoutingDimension* const dimension : dimensions_) {
3487     const std::vector<IntVar*>& transit_variables = dimension->transits();
3488     IntVar* const unused_vehicle_transit_var =
3489         transit_variables[unused_vehicle_start];
3490     IntVar* const active_vehicle_transit_var =
3491         transit_variables[active_vehicle_start];
3492     const bool contains_unused_vehicle_transit_var =
3493         compact_assignment->Contains(unused_vehicle_transit_var);
3494     const bool contains_active_vehicle_transit_var =
3495         compact_assignment->Contains(active_vehicle_transit_var);
3496     if (contains_unused_vehicle_transit_var !=
3497         contains_active_vehicle_transit_var) {
3498       // TODO(user): clarify the expected trigger rate of this LOG.
3499       LOG(INFO) << "The assignment contains transit variable for dimension '"
3500                 << dimension->name() << "' for some vehicles, but not for all";
3501       return false;
3502     }
3503     if (contains_unused_vehicle_transit_var) {
3504       const int64_t old_unused_vehicle_transit =
3505           compact_assignment->Value(unused_vehicle_transit_var);
3506       const int64_t old_active_vehicle_transit =
3507           compact_assignment->Value(active_vehicle_transit_var);
3508       compact_assignment->SetValue(unused_vehicle_transit_var,
3509                                    old_active_vehicle_transit);
3510       compact_assignment->SetValue(active_vehicle_transit_var,
3511                                    old_unused_vehicle_transit);
3512     }
3513 
3514     // Update dimensions: update cumuls at the end.
3515     const std::vector<IntVar*>& cumul_variables = dimension->cumuls();
3516     IntVar* const unused_vehicle_cumul_var =
3517         cumul_variables[unused_vehicle_end];
3518     IntVar* const active_vehicle_cumul_var =
3519         cumul_variables[active_vehicle_end];
3520     const int64_t old_unused_vehicle_cumul =
3521         compact_assignment->Value(unused_vehicle_cumul_var);
3522     const int64_t old_active_vehicle_cumul =
3523         compact_assignment->Value(active_vehicle_cumul_var);
3524     compact_assignment->SetValue(unused_vehicle_cumul_var,
3525                                  old_active_vehicle_cumul);
3526     compact_assignment->SetValue(active_vehicle_cumul_var,
3527                                  old_unused_vehicle_cumul);
3528   }
3529   return true;
3530 }
3531 
CompactAssignment(const Assignment & assignment) const3532 Assignment* RoutingModel::CompactAssignment(
3533     const Assignment& assignment) const {
3534   return CompactAssignmentInternal(assignment, false);
3535 }
3536 
CompactAndCheckAssignment(const Assignment & assignment) const3537 Assignment* RoutingModel::CompactAndCheckAssignment(
3538     const Assignment& assignment) const {
3539   return CompactAssignmentInternal(assignment, true);
3540 }
3541 
CompactAssignmentInternal(const Assignment & assignment,bool check_compact_assignment) const3542 Assignment* RoutingModel::CompactAssignmentInternal(
3543     const Assignment& assignment, bool check_compact_assignment) const {
3544   CHECK_EQ(assignment.solver(), solver_.get());
3545   if (!CostsAreHomogeneousAcrossVehicles()) {
3546     LOG(WARNING)
3547         << "The costs are not homogeneous, routes cannot be rearranged";
3548     return nullptr;
3549   }
3550 
3551   std::unique_ptr<Assignment> compact_assignment(new Assignment(&assignment));
3552   for (int vehicle = 0; vehicle < vehicles_ - 1; ++vehicle) {
3553     if (IsVehicleUsed(*compact_assignment, vehicle)) {
3554       continue;
3555     }
3556     const int vehicle_start = Start(vehicle);
3557     const int vehicle_end = End(vehicle);
3558     // Find the last vehicle, that can swap routes with this one.
3559     int swap_vehicle = vehicles_ - 1;
3560     bool has_more_vehicles_with_route = false;
3561     for (; swap_vehicle > vehicle; --swap_vehicle) {
3562       // If a vehicle was already swapped, it will appear in compact_assignment
3563       // as unused.
3564       if (!IsVehicleUsed(*compact_assignment, swap_vehicle) ||
3565           !IsVehicleUsed(*compact_assignment, swap_vehicle)) {
3566         continue;
3567       }
3568       has_more_vehicles_with_route = true;
3569       const int swap_vehicle_start = Start(swap_vehicle);
3570       const int swap_vehicle_end = End(swap_vehicle);
3571       if (manager_.IndexToNode(vehicle_start) !=
3572               manager_.IndexToNode(swap_vehicle_start) ||
3573           manager_.IndexToNode(vehicle_end) !=
3574               manager_.IndexToNode(swap_vehicle_end)) {
3575         continue;
3576       }
3577 
3578       // Check that updating VehicleVars is OK.
3579       if (RouteCanBeUsedByVehicle(*compact_assignment, swap_vehicle_start,
3580                                   vehicle)) {
3581         break;
3582       }
3583     }
3584 
3585     if (swap_vehicle == vehicle) {
3586       if (has_more_vehicles_with_route) {
3587         // No route can be assigned to this vehicle, but there are more vehicles
3588         // with a route left. This would leave a gap in the indices.
3589         // TODO(user): clarify the expected trigger rate of this LOG.
3590         LOG(INFO) << "No vehicle that can be swapped with " << vehicle
3591                   << " was found";
3592         return nullptr;
3593       } else {
3594         break;
3595       }
3596     } else {
3597       if (!ReplaceUnusedVehicle(vehicle, swap_vehicle,
3598                                 compact_assignment.get())) {
3599         return nullptr;
3600       }
3601     }
3602   }
3603   if (check_compact_assignment &&
3604       !solver_->CheckAssignment(compact_assignment.get())) {
3605     // TODO(user): clarify the expected trigger rate of this LOG.
3606     LOG(WARNING) << "The compacted assignment is not a valid solution";
3607     return nullptr;
3608   }
3609   return compact_assignment.release();
3610 }
3611 
FindNextActive(int index,const std::vector<int64_t> & indices) const3612 int RoutingModel::FindNextActive(int index,
3613                                  const std::vector<int64_t>& indices) const {
3614   ++index;
3615   CHECK_LE(0, index);
3616   const int size = indices.size();
3617   while (index < size && ActiveVar(indices[index])->Max() == 0) {
3618     ++index;
3619   }
3620   return index;
3621 }
3622 
ApplyLocks(const std::vector<int64_t> & locks)3623 IntVar* RoutingModel::ApplyLocks(const std::vector<int64_t>& locks) {
3624   // TODO(user): Replace calls to this method with calls to
3625   // ApplyLocksToAllVehicles and remove this method?
3626   CHECK_EQ(vehicles_, 1);
3627   preassignment_->Clear();
3628   IntVar* next_var = nullptr;
3629   int lock_index = FindNextActive(-1, locks);
3630   const int size = locks.size();
3631   if (lock_index < size) {
3632     next_var = NextVar(locks[lock_index]);
3633     preassignment_->Add(next_var);
3634     for (lock_index = FindNextActive(lock_index, locks); lock_index < size;
3635          lock_index = FindNextActive(lock_index, locks)) {
3636       preassignment_->SetValue(next_var, locks[lock_index]);
3637       next_var = NextVar(locks[lock_index]);
3638       preassignment_->Add(next_var);
3639     }
3640   }
3641   return next_var;
3642 }
3643 
ApplyLocksToAllVehicles(const std::vector<std::vector<int64_t>> & locks,bool close_routes)3644 bool RoutingModel::ApplyLocksToAllVehicles(
3645     const std::vector<std::vector<int64_t>>& locks, bool close_routes) {
3646   preassignment_->Clear();
3647   return RoutesToAssignment(locks, true, close_routes, preassignment_);
3648 }
3649 
GetNumberOfDecisionsInFirstSolution(const RoutingSearchParameters & parameters) const3650 int64_t RoutingModel::GetNumberOfDecisionsInFirstSolution(
3651     const RoutingSearchParameters& parameters) const {
3652   IntVarFilteredDecisionBuilder* const decision_builder =
3653       GetFilteredFirstSolutionDecisionBuilderOrNull(parameters);
3654   return decision_builder != nullptr ? decision_builder->number_of_decisions()
3655                                      : 0;
3656 }
3657 
GetNumberOfRejectsInFirstSolution(const RoutingSearchParameters & parameters) const3658 int64_t RoutingModel::GetNumberOfRejectsInFirstSolution(
3659     const RoutingSearchParameters& parameters) const {
3660   IntVarFilteredDecisionBuilder* const decision_builder =
3661       GetFilteredFirstSolutionDecisionBuilderOrNull(parameters);
3662   return decision_builder != nullptr ? decision_builder->number_of_rejects()
3663                                      : 0;
3664 }
3665 
WriteAssignment(const std::string & file_name) const3666 bool RoutingModel::WriteAssignment(const std::string& file_name) const {
3667   if (collect_assignments_->solution_count() == 1 && assignment_ != nullptr) {
3668     assignment_->CopyIntersection(collect_assignments_->solution(0));
3669     return assignment_->Save(file_name);
3670   } else {
3671     return false;
3672   }
3673 }
3674 
ReadAssignment(const std::string & file_name)3675 Assignment* RoutingModel::ReadAssignment(const std::string& file_name) {
3676   QuietCloseModel();
3677   CHECK(assignment_ != nullptr);
3678   if (assignment_->Load(file_name)) {
3679     return DoRestoreAssignment();
3680   }
3681   return nullptr;
3682 }
3683 
RestoreAssignment(const Assignment & solution)3684 Assignment* RoutingModel::RestoreAssignment(const Assignment& solution) {
3685   QuietCloseModel();
3686   CHECK(assignment_ != nullptr);
3687   assignment_->CopyIntersection(&solution);
3688   return DoRestoreAssignment();
3689 }
3690 
DoRestoreAssignment()3691 Assignment* RoutingModel::DoRestoreAssignment() {
3692   if (status_ == ROUTING_INVALID) {
3693     return nullptr;
3694   }
3695   solver_->Solve(restore_assignment_, monitors_);
3696   if (collect_assignments_->solution_count() == 1) {
3697     status_ = ROUTING_SUCCESS;
3698     return collect_assignments_->solution(0);
3699   } else {
3700     status_ = ROUTING_FAIL;
3701     return nullptr;
3702   }
3703   return nullptr;
3704 }
3705 
RoutesToAssignment(const std::vector<std::vector<int64_t>> & routes,bool ignore_inactive_indices,bool close_routes,Assignment * const assignment) const3706 bool RoutingModel::RoutesToAssignment(
3707     const std::vector<std::vector<int64_t>>& routes,
3708     bool ignore_inactive_indices, bool close_routes,
3709     Assignment* const assignment) const {
3710   CHECK(assignment != nullptr);
3711   if (!closed_) {
3712     LOG(ERROR) << "The model is not closed yet";
3713     return false;
3714   }
3715   const int num_routes = routes.size();
3716   if (num_routes > vehicles_) {
3717     LOG(ERROR) << "The number of vehicles in the assignment (" << routes.size()
3718                << ") is greater than the number of vehicles in the model ("
3719                << vehicles_ << ")";
3720     return false;
3721   }
3722 
3723   absl::flat_hash_set<int> visited_indices;
3724   // Set value to NextVars based on the routes.
3725   for (int vehicle = 0; vehicle < num_routes; ++vehicle) {
3726     const std::vector<int64_t>& route = routes[vehicle];
3727     int from_index = Start(vehicle);
3728     std::pair<absl::flat_hash_set<int>::iterator, bool> insert_result =
3729         visited_indices.insert(from_index);
3730     if (!insert_result.second) {
3731       LOG(ERROR) << "Index " << from_index << " (start node for vehicle "
3732                  << vehicle << ") was already used";
3733       return false;
3734     }
3735 
3736     for (const int64_t to_index : route) {
3737       if (to_index < 0 || to_index >= Size()) {
3738         LOG(ERROR) << "Invalid index: " << to_index;
3739         return false;
3740       }
3741 
3742       IntVar* const active_var = ActiveVar(to_index);
3743       if (active_var->Max() == 0) {
3744         if (ignore_inactive_indices) {
3745           continue;
3746         } else {
3747           LOG(ERROR) << "Index " << to_index << " is not active";
3748           return false;
3749         }
3750       }
3751 
3752       insert_result = visited_indices.insert(to_index);
3753       if (!insert_result.second) {
3754         LOG(ERROR) << "Index " << to_index << " is used multiple times";
3755         return false;
3756       }
3757 
3758       const IntVar* const vehicle_var = VehicleVar(to_index);
3759       if (!vehicle_var->Contains(vehicle)) {
3760         LOG(ERROR) << "Vehicle " << vehicle << " is not allowed at index "
3761                    << to_index;
3762         return false;
3763       }
3764 
3765       IntVar* const from_var = NextVar(from_index);
3766       if (!assignment->Contains(from_var)) {
3767         assignment->Add(from_var);
3768       }
3769       assignment->SetValue(from_var, to_index);
3770 
3771       from_index = to_index;
3772     }
3773 
3774     if (close_routes) {
3775       IntVar* const last_var = NextVar(from_index);
3776       if (!assignment->Contains(last_var)) {
3777         assignment->Add(last_var);
3778       }
3779       assignment->SetValue(last_var, End(vehicle));
3780     }
3781   }
3782 
3783   // Do not use the remaining vehicles.
3784   for (int vehicle = num_routes; vehicle < vehicles_; ++vehicle) {
3785     const int start_index = Start(vehicle);
3786     // Even if close_routes is false, we still need to add the start index to
3787     // visited_indices so that deactivating other nodes works correctly.
3788     std::pair<absl::flat_hash_set<int>::iterator, bool> insert_result =
3789         visited_indices.insert(start_index);
3790     if (!insert_result.second) {
3791       LOG(ERROR) << "Index " << start_index << " is used multiple times";
3792       return false;
3793     }
3794     if (close_routes) {
3795       IntVar* const start_var = NextVar(start_index);
3796       if (!assignment->Contains(start_var)) {
3797         assignment->Add(start_var);
3798       }
3799       assignment->SetValue(start_var, End(vehicle));
3800     }
3801   }
3802 
3803   // Deactivate other nodes (by pointing them to themselves).
3804   if (close_routes) {
3805     for (int index = 0; index < Size(); ++index) {
3806       if (!gtl::ContainsKey(visited_indices, index)) {
3807         IntVar* const next_var = NextVar(index);
3808         if (!assignment->Contains(next_var)) {
3809           assignment->Add(next_var);
3810         }
3811         assignment->SetValue(next_var, index);
3812       }
3813     }
3814   }
3815 
3816   return true;
3817 }
3818 
ReadAssignmentFromRoutes(const std::vector<std::vector<int64_t>> & routes,bool ignore_inactive_indices)3819 Assignment* RoutingModel::ReadAssignmentFromRoutes(
3820     const std::vector<std::vector<int64_t>>& routes,
3821     bool ignore_inactive_indices) {
3822   QuietCloseModel();
3823   if (!RoutesToAssignment(routes, ignore_inactive_indices, true, assignment_)) {
3824     return nullptr;
3825   }
3826   // DoRestoreAssignment() might still fail when checking constraints (most
3827   // constraints are not verified by RoutesToAssignment) or when filling in
3828   // dimension variables.
3829   return DoRestoreAssignment();
3830 }
3831 
AssignmentToRoutes(const Assignment & assignment,std::vector<std::vector<int64_t>> * const routes) const3832 void RoutingModel::AssignmentToRoutes(
3833     const Assignment& assignment,
3834     std::vector<std::vector<int64_t>>* const routes) const {
3835   CHECK(closed_);
3836   CHECK(routes != nullptr);
3837 
3838   const int model_size = Size();
3839   routes->resize(vehicles_);
3840   for (int vehicle = 0; vehicle < vehicles_; ++vehicle) {
3841     std::vector<int64_t>* const vehicle_route = &routes->at(vehicle);
3842     vehicle_route->clear();
3843 
3844     int num_visited_indices = 0;
3845     const int first_index = Start(vehicle);
3846     const IntVar* const first_var = NextVar(first_index);
3847     CHECK(assignment.Contains(first_var));
3848     CHECK(assignment.Bound(first_var));
3849     int current_index = assignment.Value(first_var);
3850     while (!IsEnd(current_index)) {
3851       vehicle_route->push_back(current_index);
3852 
3853       const IntVar* const next_var = NextVar(current_index);
3854       CHECK(assignment.Contains(next_var));
3855       CHECK(assignment.Bound(next_var));
3856       current_index = assignment.Value(next_var);
3857 
3858       ++num_visited_indices;
3859       CHECK_LE(num_visited_indices, model_size)
3860           << "The assignment contains a cycle";
3861     }
3862   }
3863 }
3864 
3865 #ifndef SWIG
GetRoutesFromAssignment(const Assignment & assignment)3866 std::vector<std::vector<int64_t>> RoutingModel::GetRoutesFromAssignment(
3867     const Assignment& assignment) {
3868   std::vector<std::vector<int64_t>> route_indices(vehicles());
3869   for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
3870     if (!assignment.Bound(NextVar(vehicle))) {
3871       LOG(DFATAL) << "GetRoutesFromAssignment() called on incomplete solution:"
3872                   << " NextVar(" << vehicle << ") is unbound.";
3873     }
3874   }
3875   for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
3876     int64_t index = Start(vehicle);
3877     route_indices[vehicle].push_back(index);
3878     while (!IsEnd(index)) {
3879       index = assignment.Value(NextVar(index));
3880       route_indices[vehicle].push_back(index);
3881     }
3882   }
3883   return route_indices;
3884 }
3885 #endif
3886 
GetArcCostForClassInternal(int64_t from_index,int64_t to_index,CostClassIndex cost_class_index) const3887 int64_t RoutingModel::GetArcCostForClassInternal(
3888     int64_t from_index, int64_t to_index,
3889     CostClassIndex cost_class_index) const {
3890   DCHECK(closed_);
3891   DCHECK_GE(cost_class_index, 0);
3892   DCHECK_LT(cost_class_index, cost_classes_.size());
3893   CostCacheElement* const cache = &cost_cache_[from_index];
3894   // See the comment in CostCacheElement in the .h for the int64_t->int cast.
3895   if (cache->index == static_cast<int>(to_index) &&
3896       cache->cost_class_index == cost_class_index) {
3897     return cache->cost;
3898   }
3899   int64_t cost = 0;
3900   const CostClass& cost_class = cost_classes_[cost_class_index];
3901   const auto& evaluator = transit_evaluators_[cost_class.evaluator_index];
3902   if (!IsStart(from_index)) {
3903     cost = CapAdd(evaluator(from_index, to_index),
3904                   GetDimensionTransitCostSum(from_index, to_index, cost_class));
3905   } else if (!IsEnd(to_index)) {
3906     // Apply route fixed cost on first non-first/last node, in other words on
3907     // the arc from the first node to its next node if it's not the last node.
3908     cost = CapAdd(
3909         evaluator(from_index, to_index),
3910         CapAdd(GetDimensionTransitCostSum(from_index, to_index, cost_class),
3911                fixed_cost_of_vehicle_[index_to_vehicle_[from_index]]));
3912   } else {
3913     // If there's only the first and last nodes on the route, it is considered
3914     // as an empty route.
3915     if (vehicle_used_when_empty_[index_to_vehicle_[from_index]]) {
3916       cost =
3917           CapAdd(evaluator(from_index, to_index),
3918                  GetDimensionTransitCostSum(from_index, to_index, cost_class));
3919     } else {
3920       cost = 0;
3921     }
3922   }
3923   *cache = {static_cast<int>(to_index), cost_class_index, cost};
3924   return cost;
3925 }
3926 
IsStart(int64_t index) const3927 bool RoutingModel::IsStart(int64_t index) const {
3928   return !IsEnd(index) && index_to_vehicle_[index] != kUnassigned;
3929 }
3930 
IsVehicleUsed(const Assignment & assignment,int vehicle) const3931 bool RoutingModel::IsVehicleUsed(const Assignment& assignment,
3932                                  int vehicle) const {
3933   CHECK_GE(vehicle, 0);
3934   CHECK_LT(vehicle, vehicles_);
3935   CHECK_EQ(solver_.get(), assignment.solver());
3936   IntVar* const start_var = NextVar(Start(vehicle));
3937   CHECK(assignment.Contains(start_var));
3938   return !IsEnd(assignment.Value(start_var));
3939 }
3940 
Next(const Assignment & assignment,int64_t index) const3941 int64_t RoutingModel::Next(const Assignment& assignment, int64_t index) const {
3942   CHECK_EQ(solver_.get(), assignment.solver());
3943   IntVar* const next_var = NextVar(index);
3944   CHECK(assignment.Contains(next_var));
3945   CHECK(assignment.Bound(next_var));
3946   return assignment.Value(next_var);
3947 }
3948 
GetArcCostForVehicle(int64_t from_index,int64_t to_index,int64_t vehicle) const3949 int64_t RoutingModel::GetArcCostForVehicle(int64_t from_index, int64_t to_index,
3950                                            int64_t vehicle) const {
3951   if (from_index != to_index && vehicle >= 0) {
3952     return GetArcCostForClassInternal(from_index, to_index,
3953                                       GetCostClassIndexOfVehicle(vehicle));
3954   } else {
3955     return 0;
3956   }
3957 }
3958 
GetArcCostForClass(int64_t from_index,int64_t to_index,int64_t cost_class_index) const3959 int64_t RoutingModel::GetArcCostForClass(
3960     int64_t from_index, int64_t to_index,
3961     int64_t /*CostClassIndex*/ cost_class_index) const {
3962   if (from_index != to_index) {
3963     return GetArcCostForClassInternal(from_index, to_index,
3964                                       CostClassIndex(cost_class_index));
3965   } else {
3966     return 0;
3967   }
3968 }
3969 
GetArcCostForFirstSolution(int64_t from_index,int64_t to_index) const3970 int64_t RoutingModel::GetArcCostForFirstSolution(int64_t from_index,
3971                                                  int64_t to_index) const {
3972   // Return high cost if connecting to an end (or bound-to-end) node;
3973   // this is used in the cost-based first solution strategies to avoid closing
3974   // routes too soon.
3975   if (!is_bound_to_end_ct_added_.Switched()) {
3976     // Lazily adding path-cumul constraint propagating connection to route end,
3977     // as it can be pretty costly in the general case.
3978     std::vector<IntVar*> zero_transit(Size(), solver_->MakeIntConst(0));
3979     solver_->AddConstraint(solver_->MakeDelayedPathCumul(
3980         nexts_, active_, is_bound_to_end_, zero_transit));
3981     is_bound_to_end_ct_added_.Switch(solver_.get());
3982   }
3983   if (is_bound_to_end_[to_index]->Min() == 1)
3984     return std::numeric_limits<int64_t>::max();
3985   // TODO(user): Take vehicle into account.
3986   return GetHomogeneousCost(from_index, to_index);
3987 }
3988 
GetDimensionTransitCostSum(int64_t i,int64_t j,const CostClass & cost_class) const3989 int64_t RoutingModel::GetDimensionTransitCostSum(
3990     int64_t i, int64_t j, const CostClass& cost_class) const {
3991   int64_t cost = 0;
3992   for (const auto& evaluator_and_coefficient :
3993        cost_class.dimension_transit_evaluator_class_and_cost_coefficient) {
3994     DCHECK_GT(evaluator_and_coefficient.cost_coefficient, 0);
3995     cost = CapAdd(
3996         cost,
3997         CapProd(evaluator_and_coefficient.cost_coefficient,
3998                 evaluator_and_coefficient.dimension->GetTransitValueFromClass(
3999                     i, j, evaluator_and_coefficient.transit_evaluator_class)));
4000   }
4001   return cost;
4002 }
4003 
ArcIsMoreConstrainedThanArc(int64_t from,int64_t to1,int64_t to2)4004 bool RoutingModel::ArcIsMoreConstrainedThanArc(int64_t from, int64_t to1,
4005                                                int64_t to2) {
4006   // Deal with end nodes: never pick an end node over a non-end node.
4007   if (IsEnd(to1) || IsEnd(to2)) {
4008     if (IsEnd(to1) != IsEnd(to2)) return IsEnd(to2);
4009     // If both are end nodes, we don't care; the right end node will be picked
4010     // by constraint propagation. Break the tie by index.
4011     return to1 < to2;
4012   }
4013 
4014   // Look whether they are mandatory (must be performed) or optional.
4015   const bool mandatory1 = active_[to1]->Min() == 1;
4016   const bool mandatory2 = active_[to2]->Min() == 1;
4017   // Always pick a mandatory node over a non-mandatory one.
4018   if (mandatory1 != mandatory2) return mandatory1;
4019 
4020   // Look at the vehicle variables.
4021   IntVar* const src_vehicle_var = VehicleVar(from);
4022   // In case the source vehicle is bound, "src_vehicle" will be it.
4023   // Otherwise, it'll be set to some possible source vehicle that
4024   // isn't -1 (if possible).
4025   const int64_t src_vehicle = src_vehicle_var->Max();
4026   if (src_vehicle_var->Bound()) {
4027     IntVar* const to1_vehicle_var = VehicleVar(to1);
4028     IntVar* const to2_vehicle_var = VehicleVar(to2);
4029     // Subtle: non-mandatory node have kNoVehicle as possible value for
4030     // their vehicle variable. So they're effectively "bound" when their domain
4031     // size is 2.
4032     const bool bound1 =
4033         mandatory1 ? to1_vehicle_var->Bound() : (to1_vehicle_var->Size() <= 2);
4034     const bool bound2 =
4035         mandatory2 ? to2_vehicle_var->Bound() : (to2_vehicle_var->Size() <= 2);
4036     // Prefer a destination bound to a given vehicle, even if it's not
4037     // bound to the right one (the propagation will quickly rule it out).
4038     if (bound1 != bound2) return bound1;
4039     if (bound1) {  // same as bound1 && bound2.
4040       // Min() will return kNoVehicle for optional nodes. Thus we use Max().
4041       const int64_t vehicle1 = to1_vehicle_var->Max();
4042       const int64_t vehicle2 = to2_vehicle_var->Max();
4043       // Prefer a destination bound to the right vehicle.
4044       // TODO(user): cover this clause in a unit test.
4045       if ((vehicle1 == src_vehicle) != (vehicle2 == src_vehicle)) {
4046         return vehicle1 == src_vehicle;
4047       }
4048       // If no destination is bound to the right vehicle, whatever we
4049       // return doesn't matter: both are infeasible. To be consistent, we
4050       // just break the tie.
4051       if (vehicle1 != src_vehicle) return to1 < to2;
4052     }
4053   }
4054   // At this point, either both destinations are bound to the source vehicle,
4055   // or none of them is bound, or the source vehicle isn't bound.
4056   // We don't bother inspecting the domains of the vehicle variables further.
4057 
4058   // Inspect the primary constrained dimension, if any.
4059   // TODO(user): try looking at all the dimensions, not just the primary one,
4060   // and reconsider the need for a "primary" dimension.
4061   if (!GetPrimaryConstrainedDimension().empty()) {
4062     const std::vector<IntVar*>& cumul_vars =
4063         GetDimensionOrDie(GetPrimaryConstrainedDimension()).cumuls();
4064     IntVar* const dim1 = cumul_vars[to1];
4065     IntVar* const dim2 = cumul_vars[to2];
4066     // Prefer the destination that has a lower upper bound for the constrained
4067     // dimension.
4068     if (dim1->Max() != dim2->Max()) return dim1->Max() < dim2->Max();
4069     // TODO(user): evaluate the *actual* Min() of each cumul variable in the
4070     // scenario where the corresponding arc from->to is performed, and pick
4071     // the destination with the lowest value.
4072   }
4073 
4074   // Break ties on equally constrained nodes with the (cost - unperformed
4075   // penalty).
4076   {
4077     const /*CostClassIndex*/ int64_t cost_class_index =
4078         SafeGetCostClassInt64OfVehicle(src_vehicle);
4079     const int64_t cost1 =
4080         CapSub(GetArcCostForClass(from, to1, cost_class_index),
4081                UnperformedPenalty(to1));
4082     const int64_t cost2 =
4083         CapSub(GetArcCostForClass(from, to2, cost_class_index),
4084                UnperformedPenalty(to2));
4085     if (cost1 != cost2) return cost1 < cost2;
4086   }
4087 
4088   // Further break ties by looking at the size of the VehicleVar.
4089   {
4090     const int64_t num_vehicles1 = VehicleVar(to1)->Size();
4091     const int64_t num_vehicles2 = VehicleVar(to2)->Size();
4092     if (num_vehicles1 != num_vehicles2) return num_vehicles1 < num_vehicles2;
4093   }
4094 
4095   // Break perfect ties by value.
4096   return to1 < to2;
4097 }
4098 
SetVisitType(int64_t index,int type,VisitTypePolicy policy)4099 void RoutingModel::SetVisitType(int64_t index, int type,
4100                                 VisitTypePolicy policy) {
4101   CHECK_LT(index, index_to_visit_type_.size());
4102   DCHECK_EQ(index_to_visit_type_.size(), index_to_type_policy_.size());
4103   index_to_visit_type_[index] = type;
4104   index_to_type_policy_[index] = policy;
4105   num_visit_types_ = std::max(num_visit_types_, type + 1);
4106 }
4107 
GetVisitType(int64_t index) const4108 int RoutingModel::GetVisitType(int64_t index) const {
4109   CHECK_LT(index, index_to_visit_type_.size());
4110   return index_to_visit_type_[index];
4111 }
4112 
GetSingleNodesOfType(int type) const4113 const std::vector<int>& RoutingModel::GetSingleNodesOfType(int type) const {
4114   DCHECK_LT(type, single_nodes_of_type_.size());
4115   return single_nodes_of_type_[type];
4116 }
4117 
GetPairIndicesOfType(int type) const4118 const std::vector<int>& RoutingModel::GetPairIndicesOfType(int type) const {
4119   DCHECK_LT(type, pair_indices_of_type_.size());
4120   return pair_indices_of_type_[type];
4121 }
4122 
GetVisitTypePolicy(int64_t index) const4123 RoutingModel::VisitTypePolicy RoutingModel::GetVisitTypePolicy(
4124     int64_t index) const {
4125   CHECK_LT(index, index_to_type_policy_.size());
4126   return index_to_type_policy_[index];
4127 }
4128 
CloseVisitTypes()4129 void RoutingModel::CloseVisitTypes() {
4130   hard_incompatible_types_per_type_index_.resize(num_visit_types_);
4131   temporal_incompatible_types_per_type_index_.resize(num_visit_types_);
4132   same_vehicle_required_type_alternatives_per_type_index_.resize(
4133       num_visit_types_);
4134   required_type_alternatives_when_adding_type_index_.resize(num_visit_types_);
4135   required_type_alternatives_when_removing_type_index_.resize(num_visit_types_);
4136 }
4137 
AddHardTypeIncompatibility(int type1,int type2)4138 void RoutingModel::AddHardTypeIncompatibility(int type1, int type2) {
4139   DCHECK_LT(std::max(type1, type2),
4140             hard_incompatible_types_per_type_index_.size());
4141   has_hard_type_incompatibilities_ = true;
4142 
4143   hard_incompatible_types_per_type_index_[type1].insert(type2);
4144   hard_incompatible_types_per_type_index_[type2].insert(type1);
4145 }
4146 
AddTemporalTypeIncompatibility(int type1,int type2)4147 void RoutingModel::AddTemporalTypeIncompatibility(int type1, int type2) {
4148   DCHECK_LT(std::max(type1, type2),
4149             temporal_incompatible_types_per_type_index_.size());
4150   has_temporal_type_incompatibilities_ = true;
4151 
4152   temporal_incompatible_types_per_type_index_[type1].insert(type2);
4153   temporal_incompatible_types_per_type_index_[type2].insert(type1);
4154 }
4155 
4156 const absl::flat_hash_set<int>&
GetHardTypeIncompatibilitiesOfType(int type) const4157 RoutingModel::GetHardTypeIncompatibilitiesOfType(int type) const {
4158   DCHECK_GE(type, 0);
4159   DCHECK_LT(type, hard_incompatible_types_per_type_index_.size());
4160   return hard_incompatible_types_per_type_index_[type];
4161 }
4162 
4163 const absl::flat_hash_set<int>&
GetTemporalTypeIncompatibilitiesOfType(int type) const4164 RoutingModel::GetTemporalTypeIncompatibilitiesOfType(int type) const {
4165   DCHECK_GE(type, 0);
4166   DCHECK_LT(type, temporal_incompatible_types_per_type_index_.size());
4167   return temporal_incompatible_types_per_type_index_[type];
4168 }
4169 
4170 // TODO(user): Consider if an empty "required_type_alternatives" should mean
4171 // trivially feasible requirement, as there are no required type alternatives?
AddSameVehicleRequiredTypeAlternatives(int dependent_type,absl::flat_hash_set<int> required_type_alternatives)4172 void RoutingModel::AddSameVehicleRequiredTypeAlternatives(
4173     int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4174   DCHECK_LT(dependent_type,
4175             same_vehicle_required_type_alternatives_per_type_index_.size());
4176 
4177   if (required_type_alternatives.empty()) {
4178     // The dependent_type requires an infeasible (empty) set of types.
4179     // Nodes of this type and all policies except
4180     // ADDED_TYPE_REMOVED_FROM_VEHICLE are trivially infeasible.
4181     absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4182         trivially_infeasible_visit_types_to_policies_[dependent_type];
4183     infeasible_policies.insert(TYPE_ADDED_TO_VEHICLE);
4184     infeasible_policies.insert(TYPE_ON_VEHICLE_UP_TO_VISIT);
4185     infeasible_policies.insert(TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED);
4186     return;
4187   }
4188 
4189   has_same_vehicle_type_requirements_ = true;
4190   same_vehicle_required_type_alternatives_per_type_index_[dependent_type]
4191       .push_back(std::move(required_type_alternatives));
4192 }
4193 
AddRequiredTypeAlternativesWhenAddingType(int dependent_type,absl::flat_hash_set<int> required_type_alternatives)4194 void RoutingModel::AddRequiredTypeAlternativesWhenAddingType(
4195     int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4196   DCHECK_LT(dependent_type,
4197             required_type_alternatives_when_adding_type_index_.size());
4198 
4199   if (required_type_alternatives.empty()) {
4200     // The dependent_type requires an infeasible (empty) set of types.
4201     // Nodes of this type and policy TYPE_ADDED_TO_VEHICLE or
4202     // TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED are trivially infeasible.
4203     absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4204         trivially_infeasible_visit_types_to_policies_[dependent_type];
4205     infeasible_policies.insert(TYPE_ADDED_TO_VEHICLE);
4206     infeasible_policies.insert(TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED);
4207     return;
4208   }
4209 
4210   has_temporal_type_requirements_ = true;
4211   required_type_alternatives_when_adding_type_index_[dependent_type].push_back(
4212       std::move(required_type_alternatives));
4213 }
4214 
AddRequiredTypeAlternativesWhenRemovingType(int dependent_type,absl::flat_hash_set<int> required_type_alternatives)4215 void RoutingModel::AddRequiredTypeAlternativesWhenRemovingType(
4216     int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4217   DCHECK_LT(dependent_type,
4218             required_type_alternatives_when_removing_type_index_.size());
4219 
4220   if (required_type_alternatives.empty()) {
4221     // The dependent_type requires an infeasible (empty) set of types.
4222     // Nodes of this type and all policies except TYPE_ADDED_TO_VEHICLE are
4223     // trivially infeasible.
4224     absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4225         trivially_infeasible_visit_types_to_policies_[dependent_type];
4226     infeasible_policies.insert(ADDED_TYPE_REMOVED_FROM_VEHICLE);
4227     infeasible_policies.insert(TYPE_ON_VEHICLE_UP_TO_VISIT);
4228     infeasible_policies.insert(TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED);
4229     return;
4230   }
4231 
4232   has_temporal_type_requirements_ = true;
4233   required_type_alternatives_when_removing_type_index_[dependent_type]
4234       .push_back(std::move(required_type_alternatives));
4235 }
4236 
4237 const std::vector<absl::flat_hash_set<int>>&
GetSameVehicleRequiredTypeAlternativesOfType(int type) const4238 RoutingModel::GetSameVehicleRequiredTypeAlternativesOfType(int type) const {
4239   DCHECK_GE(type, 0);
4240   DCHECK_LT(type,
4241             same_vehicle_required_type_alternatives_per_type_index_.size());
4242   return same_vehicle_required_type_alternatives_per_type_index_[type];
4243 }
4244 
4245 const std::vector<absl::flat_hash_set<int>>&
GetRequiredTypeAlternativesWhenAddingType(int type) const4246 RoutingModel::GetRequiredTypeAlternativesWhenAddingType(int type) const {
4247   DCHECK_GE(type, 0);
4248   DCHECK_LT(type, required_type_alternatives_when_adding_type_index_.size());
4249   return required_type_alternatives_when_adding_type_index_[type];
4250 }
4251 
4252 const std::vector<absl::flat_hash_set<int>>&
GetRequiredTypeAlternativesWhenRemovingType(int type) const4253 RoutingModel::GetRequiredTypeAlternativesWhenRemovingType(int type) const {
4254   DCHECK_GE(type, 0);
4255   DCHECK_LT(type, required_type_alternatives_when_removing_type_index_.size());
4256   return required_type_alternatives_when_removing_type_index_[type];
4257 }
4258 
UnperformedPenalty(int64_t var_index) const4259 int64_t RoutingModel::UnperformedPenalty(int64_t var_index) const {
4260   return UnperformedPenaltyOrValue(0, var_index);
4261 }
4262 
UnperformedPenaltyOrValue(int64_t default_value,int64_t var_index) const4263 int64_t RoutingModel::UnperformedPenaltyOrValue(int64_t default_value,
4264                                                 int64_t var_index) const {
4265   if (active_[var_index]->Min() == 1)
4266     return std::numeric_limits<int64_t>::max();  // Forced active.
4267   const std::vector<DisjunctionIndex>& disjunction_indices =
4268       GetDisjunctionIndices(var_index);
4269   if (disjunction_indices.size() != 1) return default_value;
4270   const DisjunctionIndex disjunction_index = disjunction_indices[0];
4271   // The disjunction penalty can be kNoPenalty iff there is more than one node
4272   // in the disjunction; otherwise we would have caught it earlier (the node
4273   // would be forced active).
4274   return std::max(int64_t{0}, disjunctions_[disjunction_index].value.penalty);
4275 }
4276 
DebugOutputAssignment(const Assignment & solution_assignment,const std::string & dimension_to_print) const4277 std::string RoutingModel::DebugOutputAssignment(
4278     const Assignment& solution_assignment,
4279     const std::string& dimension_to_print) const {
4280   for (int i = 0; i < Size(); ++i) {
4281     if (!solution_assignment.Bound(NextVar(i))) {
4282       LOG(DFATAL)
4283           << "DebugOutputVehicleSchedules() called on incomplete solution:"
4284           << " NextVar(" << i << ") is unbound.";
4285       return "";
4286     }
4287   }
4288   std::string output;
4289   absl::flat_hash_set<std::string> dimension_names;
4290   if (dimension_to_print.empty()) {
4291     const std::vector<std::string> all_dimension_names = GetAllDimensionNames();
4292     dimension_names.insert(all_dimension_names.begin(),
4293                            all_dimension_names.end());
4294   } else {
4295     dimension_names.insert(dimension_to_print);
4296   }
4297   for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4298     int empty_vehicle_range_start = vehicle;
4299     while (vehicle < vehicles() &&
4300            IsEnd(solution_assignment.Value(NextVar(Start(vehicle))))) {
4301       vehicle++;
4302     }
4303     if (empty_vehicle_range_start != vehicle) {
4304       if (empty_vehicle_range_start == vehicle - 1) {
4305         absl::StrAppendFormat(&output, "Vehicle %d: empty",
4306                               empty_vehicle_range_start);
4307       } else {
4308         absl::StrAppendFormat(&output, "Vehicles %d-%d: empty",
4309                               empty_vehicle_range_start, vehicle - 1);
4310       }
4311       output.append("\n");
4312     }
4313     if (vehicle < vehicles()) {
4314       absl::StrAppendFormat(&output, "Vehicle %d:", vehicle);
4315       int64_t index = Start(vehicle);
4316       for (;;) {
4317         const IntVar* vehicle_var = VehicleVar(index);
4318         absl::StrAppendFormat(&output, "%d Vehicle(%d) ", index,
4319                               solution_assignment.Value(vehicle_var));
4320         for (const RoutingDimension* const dimension : dimensions_) {
4321           if (gtl::ContainsKey(dimension_names, dimension->name())) {
4322             const IntVar* const var = dimension->CumulVar(index);
4323             absl::StrAppendFormat(&output, "%s(%d..%d) ", dimension->name(),
4324                                   solution_assignment.Min(var),
4325                                   solution_assignment.Max(var));
4326           }
4327         }
4328         if (IsEnd(index)) break;
4329         index = solution_assignment.Value(NextVar(index));
4330         if (IsEnd(index)) output.append("Route end ");
4331       }
4332       output.append("\n");
4333     }
4334   }
4335   output.append("Unperformed nodes: ");
4336   bool has_unperformed = false;
4337   for (int i = 0; i < Size(); ++i) {
4338     if (!IsEnd(i) && !IsStart(i) &&
4339         solution_assignment.Value(NextVar(i)) == i) {
4340       absl::StrAppendFormat(&output, "%d ", i);
4341       has_unperformed = true;
4342     }
4343   }
4344   if (!has_unperformed) output.append("None");
4345   output.append("\n");
4346   return output;
4347 }
4348 
4349 #ifndef SWIG
4350 std::vector<std::vector<std::pair<int64_t, int64_t>>>
GetCumulBounds(const Assignment & solution_assignment,const RoutingDimension & dimension)4351 RoutingModel::GetCumulBounds(const Assignment& solution_assignment,
4352                              const RoutingDimension& dimension) {
4353   std::vector<std::vector<std::pair<int64_t, int64_t>>> cumul_bounds(
4354       vehicles());
4355   for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4356     if (!solution_assignment.Bound(NextVar(vehicle))) {
4357       LOG(DFATAL) << "GetCumulBounds() called on incomplete solution:"
4358                   << " NextVar(" << vehicle << ") is unbound.";
4359     }
4360   }
4361 
4362   for (int vehicle_id = 0; vehicle_id < vehicles(); ++vehicle_id) {
4363     int64_t index = Start(vehicle_id);
4364     IntVar* dim_var = dimension.CumulVar(index);
4365     cumul_bounds[vehicle_id].emplace_back(solution_assignment.Min(dim_var),
4366                                           solution_assignment.Max(dim_var));
4367     while (!IsEnd(index)) {
4368       index = solution_assignment.Value(NextVar(index));
4369       IntVar* dim_var = dimension.CumulVar(index);
4370       cumul_bounds[vehicle_id].emplace_back(solution_assignment.Min(dim_var),
4371                                             solution_assignment.Max(dim_var));
4372     }
4373   }
4374   return cumul_bounds;
4375 }
4376 #endif
4377 
GetOrCreateAssignment()4378 Assignment* RoutingModel::GetOrCreateAssignment() {
4379   if (assignment_ == nullptr) {
4380     assignment_ = solver_->MakeAssignment();
4381     assignment_->Add(nexts_);
4382     if (!CostsAreHomogeneousAcrossVehicles()) {
4383       assignment_->Add(vehicle_vars_);
4384     }
4385     assignment_->AddObjective(cost_);
4386   }
4387   return assignment_;
4388 }
4389 
GetOrCreateTmpAssignment()4390 Assignment* RoutingModel::GetOrCreateTmpAssignment() {
4391   if (tmp_assignment_ == nullptr) {
4392     tmp_assignment_ = solver_->MakeAssignment();
4393     tmp_assignment_->Add(nexts_);
4394   }
4395   return tmp_assignment_;
4396 }
4397 
GetOrCreateLimit()4398 RegularLimit* RoutingModel::GetOrCreateLimit() {
4399   if (limit_ == nullptr) {
4400     limit_ = solver_->MakeLimit(
4401         absl::InfiniteDuration(), std::numeric_limits<int64_t>::max(),
4402         std::numeric_limits<int64_t>::max(),
4403         std::numeric_limits<int64_t>::max(), /*smart_time_check=*/true);
4404   }
4405   return limit_;
4406 }
4407 
GetOrCreateLocalSearchLimit()4408 RegularLimit* RoutingModel::GetOrCreateLocalSearchLimit() {
4409   if (ls_limit_ == nullptr) {
4410     ls_limit_ = solver_->MakeLimit(absl::InfiniteDuration(),
4411                                    std::numeric_limits<int64_t>::max(),
4412                                    std::numeric_limits<int64_t>::max(),
4413                                    /*solutions=*/1, /*smart_time_check=*/true);
4414   }
4415   return ls_limit_;
4416 }
4417 
GetOrCreateLargeNeighborhoodSearchLimit()4418 RegularLimit* RoutingModel::GetOrCreateLargeNeighborhoodSearchLimit() {
4419   if (lns_limit_ == nullptr) {
4420     lns_limit_ = solver_->MakeLimit(
4421         absl::InfiniteDuration(), std::numeric_limits<int64_t>::max(),
4422         std::numeric_limits<int64_t>::max(),
4423         std::numeric_limits<int64_t>::max(), /*smart_time_check=*/false);
4424   }
4425   return lns_limit_;
4426 }
4427 
4428 RegularLimit*
GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit()4429 RoutingModel::GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit() {
4430   if (first_solution_lns_limit_ == nullptr) {
4431     first_solution_lns_limit_ = solver_->MakeLimit(
4432         absl::InfiniteDuration(), std::numeric_limits<int64_t>::max(),
4433         std::numeric_limits<int64_t>::max(),
4434         std::numeric_limits<int64_t>::max(), /*smart_time_check=*/false);
4435   }
4436   return first_solution_lns_limit_;
4437 }
4438 
CreateInsertionOperator()4439 LocalSearchOperator* RoutingModel::CreateInsertionOperator() {
4440   LocalSearchOperator* insertion_operator =
4441       CreateCPOperator<MakeActiveOperator>();
4442   if (!pickup_delivery_pairs_.empty()) {
4443     insertion_operator = solver_->ConcatenateOperators(
4444         {CreatePairOperator<MakePairActiveOperator>(), insertion_operator});
4445   }
4446   if (!implicit_pickup_delivery_pairs_without_alternatives_.empty()) {
4447     insertion_operator = solver_->ConcatenateOperators(
4448         {CreateOperator<MakePairActiveOperator>(
4449              implicit_pickup_delivery_pairs_without_alternatives_),
4450          insertion_operator});
4451   }
4452   return insertion_operator;
4453 }
4454 
CreateMakeInactiveOperator()4455 LocalSearchOperator* RoutingModel::CreateMakeInactiveOperator() {
4456   LocalSearchOperator* make_inactive_operator =
4457       CreateCPOperator<MakeInactiveOperator>();
4458   if (!pickup_delivery_pairs_.empty()) {
4459     make_inactive_operator = solver_->ConcatenateOperators(
4460         {CreatePairOperator<MakePairInactiveOperator>(),
4461          make_inactive_operator});
4462   }
4463   return make_inactive_operator;
4464 }
4465 
CreateNeighborhoodOperators(const RoutingSearchParameters & parameters)4466 void RoutingModel::CreateNeighborhoodOperators(
4467     const RoutingSearchParameters& parameters) {
4468   local_search_operators_.clear();
4469   local_search_operators_.resize(LOCAL_SEARCH_OPERATOR_COUNTER, nullptr);
4470   {
4471     // Operators defined by Solver::LocalSearchOperators.
4472     const std::vector<
4473         std::pair<RoutingLocalSearchOperator, Solver::LocalSearchOperators>>
4474         operator_by_type = {{OR_OPT, Solver::OROPT},
4475                             {PATH_LNS, Solver::PATHLNS},
4476                             {FULL_PATH_LNS, Solver::FULLPATHLNS},
4477                             {INACTIVE_LNS, Solver::UNACTIVELNS}};
4478     for (const auto [type, op] : operator_by_type) {
4479       local_search_operators_[type] =
4480           CostsAreHomogeneousAcrossVehicles()
4481               ? solver_->MakeOperator(nexts_, op)
4482               : solver_->MakeOperator(nexts_, vehicle_vars_, op);
4483     }
4484   }
4485   {
4486     // Operators defined by Solver::EvaluatorLocalSearchOperators.
4487     const std::vector<std::pair<RoutingLocalSearchOperator,
4488                                 Solver::EvaluatorLocalSearchOperators>>
4489         operator_by_type = {{LIN_KERNIGHAN, Solver::LK},
4490                             {TSP_OPT, Solver::TSPOPT},
4491                             {TSP_LNS, Solver::TSPLNS}};
4492     for (const auto [type, op] : operator_by_type) {
4493       auto arc_cost =
4494           absl::bind_front(&RoutingModel::GetArcCostForVehicle, this);
4495       local_search_operators_[type] =
4496           CostsAreHomogeneousAcrossVehicles()
4497               ? solver_->MakeOperator(nexts_, std::move(arc_cost), op)
4498               : solver_->MakeOperator(nexts_, vehicle_vars_,
4499                                       std::move(arc_cost), op);
4500     }
4501   }
4502 
4503   // Other operators defined in the CP solver.
4504   local_search_operators_[RELOCATE] = CreateCPOperator<Relocate>();
4505   local_search_operators_[EXCHANGE] = CreateCPOperator<Exchange>();
4506   local_search_operators_[CROSS] = CreateCPOperator<Cross>();
4507   local_search_operators_[TWO_OPT] = CreateCPOperator<TwoOpt>();
4508   local_search_operators_[RELOCATE_AND_MAKE_ACTIVE] =
4509       CreateCPOperator<RelocateAndMakeActiveOperator>();
4510   local_search_operators_[MAKE_ACTIVE_AND_RELOCATE] =
4511       CreateCPOperator<MakeActiveAndRelocate>();
4512   local_search_operators_[MAKE_CHAIN_INACTIVE] =
4513       CreateCPOperator<MakeChainInactiveOperator>();
4514   local_search_operators_[SWAP_ACTIVE] = CreateCPOperator<SwapActiveOperator>();
4515   local_search_operators_[EXTENDED_SWAP_ACTIVE] =
4516       CreateCPOperator<ExtendedSwapActiveOperator>();
4517 
4518   // Routing-specific operators.
4519   local_search_operators_[MAKE_ACTIVE] = CreateInsertionOperator();
4520   local_search_operators_[MAKE_INACTIVE] = CreateMakeInactiveOperator();
4521   local_search_operators_[RELOCATE_PAIR] =
4522       CreatePairOperator<PairRelocateOperator>();
4523   std::vector<LocalSearchOperator*> light_relocate_pair_operators;
4524   light_relocate_pair_operators.push_back(
4525       CreatePairOperator<LightPairRelocateOperator>());
4526   local_search_operators_[LIGHT_RELOCATE_PAIR] =
4527       solver_->ConcatenateOperators(light_relocate_pair_operators);
4528   local_search_operators_[EXCHANGE_PAIR] =
4529       CreatePairOperator<PairExchangeOperator>();
4530   local_search_operators_[EXCHANGE_RELOCATE_PAIR] =
4531       CreatePairOperator<PairExchangeRelocateOperator>();
4532   local_search_operators_[RELOCATE_NEIGHBORS] =
4533       CreateOperator<MakeRelocateNeighborsOperator>(
4534           absl::bind_front(&RoutingModel::GetHomogeneousCost, this));
4535   local_search_operators_[NODE_PAIR_SWAP] = solver_->ConcatenateOperators(
4536       {CreatePairOperator<IndexPairSwapActiveOperator>(),
4537        CreatePairOperator<SwapIndexPairOperator>(),
4538        CreatePairOperator<PairNodeSwapActiveOperator<true>>(),
4539        CreatePairOperator<PairNodeSwapActiveOperator<false>>()});
4540   local_search_operators_[RELOCATE_SUBTRIP] =
4541       CreatePairOperator<RelocateSubtrip>();
4542   local_search_operators_[EXCHANGE_SUBTRIP] =
4543       CreatePairOperator<ExchangeSubtrip>();
4544 
4545   const auto arc_cost_for_path_start =
4546       [this](int64_t before_node, int64_t after_node, int64_t start_index) {
4547         const int vehicle = index_to_vehicle_[start_index];
4548         const int64_t arc_cost =
4549             GetArcCostForVehicle(before_node, after_node, vehicle);
4550         return (before_node != start_index || IsEnd(after_node))
4551                    ? arc_cost
4552                    : CapSub(arc_cost, GetFixedCostOfVehicle(vehicle));
4553       };
4554   local_search_operators_[RELOCATE_EXPENSIVE_CHAIN] =
4555       solver_->RevAlloc(new RelocateExpensiveChain(
4556           nexts_,
4557           CostsAreHomogeneousAcrossVehicles() ? std::vector<IntVar*>()
4558                                               : vehicle_vars_,
4559           vehicle_start_class_callback_,
4560           parameters.relocate_expensive_chain_num_arcs_to_consider(),
4561           arc_cost_for_path_start));
4562 
4563   // Insertion-based LNS neighborhoods.
4564   const auto make_global_cheapest_insertion_filtered_heuristic =
4565       [this, &parameters]() {
4566         using Heuristic = GlobalCheapestInsertionFilteredHeuristic;
4567         Heuristic::GlobalCheapestInsertionParameters ls_gci_parameters;
4568         ls_gci_parameters.is_sequential = false;
4569         ls_gci_parameters.farthest_seeds_ratio = 0.0;
4570         ls_gci_parameters.neighbors_ratio =
4571             parameters.cheapest_insertion_ls_operator_neighbors_ratio();
4572         ls_gci_parameters.min_neighbors =
4573             parameters.cheapest_insertion_ls_operator_min_neighbors();
4574         ls_gci_parameters.use_neighbors_ratio_for_initialization = true;
4575         ls_gci_parameters.add_unperformed_entries =
4576             parameters.cheapest_insertion_add_unperformed_entries();
4577         return absl::make_unique<Heuristic>(
4578             this, absl::bind_front(&RoutingModel::GetArcCostForVehicle, this),
4579             absl::bind_front(&RoutingModel::UnperformedPenaltyOrValue, this, 0),
4580             GetOrCreateFeasibilityFilterManager(parameters), ls_gci_parameters);
4581       };
4582   const auto make_local_cheapest_insertion_filtered_heuristic =
4583       [this, &parameters]() {
4584         return absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4585             this, absl::bind_front(&RoutingModel::GetArcCostForVehicle, this),
4586             GetOrCreateFeasibilityFilterManager(parameters));
4587       };
4588   local_search_operators_[GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4589       solver_->RevAlloc(new FilteredHeuristicCloseNodesLNSOperator(
4590           make_global_cheapest_insertion_filtered_heuristic(),
4591           parameters.heuristic_close_nodes_lns_num_nodes()));
4592 
4593   local_search_operators_[LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4594       solver_->RevAlloc(new FilteredHeuristicCloseNodesLNSOperator(
4595           make_local_cheapest_insertion_filtered_heuristic(),
4596           parameters.heuristic_close_nodes_lns_num_nodes()));
4597 
4598   local_search_operators_[GLOBAL_CHEAPEST_INSERTION_PATH_LNS] =
4599       solver_->RevAlloc(new FilteredHeuristicPathLNSOperator(
4600           make_global_cheapest_insertion_filtered_heuristic()));
4601 
4602   local_search_operators_[LOCAL_CHEAPEST_INSERTION_PATH_LNS] =
4603       solver_->RevAlloc(new FilteredHeuristicPathLNSOperator(
4604           make_local_cheapest_insertion_filtered_heuristic()));
4605 
4606   local_search_operators_
4607       [RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED] =
4608           solver_->RevAlloc(
4609               new RelocatePathAndHeuristicInsertUnperformedOperator(
4610                   make_global_cheapest_insertion_filtered_heuristic()));
4611 
4612   local_search_operators_[GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4613       solver_->RevAlloc(new FilteredHeuristicExpensiveChainLNSOperator(
4614           make_global_cheapest_insertion_filtered_heuristic(),
4615           parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4616           arc_cost_for_path_start));
4617 
4618   local_search_operators_[LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4619       solver_->RevAlloc(new FilteredHeuristicExpensiveChainLNSOperator(
4620           make_local_cheapest_insertion_filtered_heuristic(),
4621           parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4622           arc_cost_for_path_start));
4623 }
4624 
4625 #define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators) \
4626   if (search_parameters.local_search_operators().use_##operator_method() == \
4627       BOOL_TRUE) {                                                          \
4628     operators.push_back(local_search_operators_[operator_type]);            \
4629   }
4630 
ConcatenateOperators(const RoutingSearchParameters & search_parameters,const std::vector<LocalSearchOperator * > & operators) const4631 LocalSearchOperator* RoutingModel::ConcatenateOperators(
4632     const RoutingSearchParameters& search_parameters,
4633     const std::vector<LocalSearchOperator*>& operators) const {
4634   if (search_parameters.use_multi_armed_bandit_concatenate_operators()) {
4635     return solver_->MultiArmedBanditConcatenateOperators(
4636         operators,
4637         search_parameters
4638             .multi_armed_bandit_compound_operator_memory_coefficient(),
4639         search_parameters
4640             .multi_armed_bandit_compound_operator_exploration_coefficient(),
4641         /*maximize=*/false);
4642   }
4643   return solver_->ConcatenateOperators(operators);
4644 }
4645 
GetNeighborhoodOperators(const RoutingSearchParameters & search_parameters) const4646 LocalSearchOperator* RoutingModel::GetNeighborhoodOperators(
4647     const RoutingSearchParameters& search_parameters) const {
4648   std::vector<LocalSearchOperator*> operator_groups;
4649   std::vector<LocalSearchOperator*> operators = extra_operators_;
4650   if (!pickup_delivery_pairs_.empty()) {
4651     CP_ROUTING_PUSH_OPERATOR(RELOCATE_PAIR, relocate_pair, operators);
4652     // Only add the light version of relocate pair if the normal version has not
4653     // already been added as it covers a subset of its neighborhood.
4654     if (search_parameters.local_search_operators().use_relocate_pair() ==
4655         BOOL_FALSE) {
4656       CP_ROUTING_PUSH_OPERATOR(LIGHT_RELOCATE_PAIR, light_relocate_pair,
4657                                operators);
4658     }
4659     CP_ROUTING_PUSH_OPERATOR(EXCHANGE_PAIR, exchange_pair, operators);
4660     CP_ROUTING_PUSH_OPERATOR(NODE_PAIR_SWAP, node_pair_swap_active, operators);
4661     CP_ROUTING_PUSH_OPERATOR(RELOCATE_SUBTRIP, relocate_subtrip, operators);
4662     CP_ROUTING_PUSH_OPERATOR(EXCHANGE_SUBTRIP, exchange_subtrip, operators);
4663   }
4664   if (vehicles_ > 1) {
4665     if (GetNumOfSingletonNodes() > 0) {
4666       // If there are only pairs in the model the only case where Relocate will
4667       // work is for intra-route moves, already covered by OrOpt.
4668       // We are not disabling Exchange and Cross because there are no
4669       // intra-route equivalents.
4670       CP_ROUTING_PUSH_OPERATOR(RELOCATE, relocate, operators);
4671     }
4672     CP_ROUTING_PUSH_OPERATOR(EXCHANGE, exchange, operators);
4673     CP_ROUTING_PUSH_OPERATOR(CROSS, cross, operators);
4674   }
4675   if (!pickup_delivery_pairs_.empty() ||
4676       search_parameters.local_search_operators().use_relocate_neighbors() ==
4677           BOOL_TRUE) {
4678     operators.push_back(local_search_operators_[RELOCATE_NEIGHBORS]);
4679   }
4680   const LocalSearchMetaheuristic::Value local_search_metaheuristic =
4681       search_parameters.local_search_metaheuristic();
4682   if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4683       local_search_metaheuristic !=
4684           LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4685       local_search_metaheuristic !=
4686           LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4687     CP_ROUTING_PUSH_OPERATOR(LIN_KERNIGHAN, lin_kernighan, operators);
4688   }
4689   CP_ROUTING_PUSH_OPERATOR(TWO_OPT, two_opt, operators);
4690   CP_ROUTING_PUSH_OPERATOR(OR_OPT, or_opt, operators);
4691   CP_ROUTING_PUSH_OPERATOR(RELOCATE_EXPENSIVE_CHAIN, relocate_expensive_chain,
4692                            operators);
4693   if (!disjunctions_.empty()) {
4694     CP_ROUTING_PUSH_OPERATOR(MAKE_INACTIVE, make_inactive, operators);
4695     CP_ROUTING_PUSH_OPERATOR(MAKE_CHAIN_INACTIVE, make_chain_inactive,
4696                              operators);
4697     CP_ROUTING_PUSH_OPERATOR(MAKE_ACTIVE, make_active, operators);
4698 
4699     // The relocate_and_make_active parameter activates all neighborhoods
4700     // relocating a node together with making another active.
4701     CP_ROUTING_PUSH_OPERATOR(RELOCATE_AND_MAKE_ACTIVE, relocate_and_make_active,
4702                              operators);
4703     CP_ROUTING_PUSH_OPERATOR(MAKE_ACTIVE_AND_RELOCATE, relocate_and_make_active,
4704                              operators);
4705 
4706     CP_ROUTING_PUSH_OPERATOR(SWAP_ACTIVE, swap_active, operators);
4707     CP_ROUTING_PUSH_OPERATOR(EXTENDED_SWAP_ACTIVE, extended_swap_active,
4708                              operators);
4709   }
4710   operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4711 
4712   // Second local search loop: LNS-like operators.
4713   operators.clear();
4714   if (vehicles() > 1) {
4715     // NOTE: The following heuristic path LNS with a single vehicle are
4716     // equivalent to using the heuristic as first solution strategy, so we only
4717     // add these moves if we have at least 2 vehicles in the model.
4718     CP_ROUTING_PUSH_OPERATOR(GLOBAL_CHEAPEST_INSERTION_PATH_LNS,
4719                              global_cheapest_insertion_path_lns, operators);
4720     CP_ROUTING_PUSH_OPERATOR(LOCAL_CHEAPEST_INSERTION_PATH_LNS,
4721                              local_cheapest_insertion_path_lns, operators);
4722     CP_ROUTING_PUSH_OPERATOR(
4723         RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED,
4724         relocate_path_global_cheapest_insertion_insert_unperformed, operators);
4725   }
4726   CP_ROUTING_PUSH_OPERATOR(GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS,
4727                            global_cheapest_insertion_expensive_chain_lns,
4728                            operators);
4729   CP_ROUTING_PUSH_OPERATOR(LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS,
4730                            local_cheapest_insertion_expensive_chain_lns,
4731                            operators);
4732   CP_ROUTING_PUSH_OPERATOR(GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS,
4733                            global_cheapest_insertion_close_nodes_lns,
4734                            operators);
4735   CP_ROUTING_PUSH_OPERATOR(LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS,
4736                            local_cheapest_insertion_close_nodes_lns, operators);
4737   operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4738 
4739   // Third local search loop: Expensive LNS operators.
4740   operators.clear();
4741   if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4742       local_search_metaheuristic !=
4743           LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4744       local_search_metaheuristic !=
4745           LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4746     CP_ROUTING_PUSH_OPERATOR(TSP_OPT, tsp_opt, operators);
4747   }
4748   if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4749       local_search_metaheuristic !=
4750           LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4751       local_search_metaheuristic !=
4752           LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4753     CP_ROUTING_PUSH_OPERATOR(TSP_LNS, tsp_lns, operators);
4754   }
4755   CP_ROUTING_PUSH_OPERATOR(FULL_PATH_LNS, full_path_lns, operators);
4756   CP_ROUTING_PUSH_OPERATOR(PATH_LNS, path_lns, operators);
4757   if (!disjunctions_.empty()) {
4758     CP_ROUTING_PUSH_OPERATOR(INACTIVE_LNS, inactive_lns, operators);
4759   }
4760   operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4761 
4762   return solver_->ConcatenateOperators(operator_groups);
4763 }
4764 
4765 #undef CP_ROUTING_PUSH_OPERATOR
4766 
HasUnaryDimension(const std::vector<RoutingDimension * > & dimensions)4767 bool HasUnaryDimension(const std::vector<RoutingDimension*>& dimensions) {
4768   for (const RoutingDimension* dimension : dimensions) {
4769     if (dimension->GetUnaryTransitEvaluator(0) != nullptr) return true;
4770   }
4771   return false;
4772 }
4773 
4774 namespace {
4775 
ConvertVectorInt64ToVectorInt(const std::vector<int64_t> & input,std::vector<int> * output)4776 void ConvertVectorInt64ToVectorInt(const std::vector<int64_t>& input,
4777                                    std::vector<int>* output) {
4778   const int n = input.size();
4779   output->resize(n);
4780   int* data = output->data();
4781   for (int i = 0; i < n; ++i) {
4782     const int element = static_cast<int>(input[i]);
4783     DCHECK_EQ(input[i], static_cast<int64_t>(element));
4784     data[i] = element;
4785   }
4786 }
4787 
4788 }  // namespace
4789 
4790 std::vector<LocalSearchFilterManager::FilterEvent>
GetOrCreateLocalSearchFilters(const RoutingSearchParameters & parameters,bool filter_cost)4791 RoutingModel::GetOrCreateLocalSearchFilters(
4792     const RoutingSearchParameters& parameters, bool filter_cost) {
4793   const auto kAccept = LocalSearchFilterManager::FilterEventType::kAccept;
4794   const auto kRelax = LocalSearchFilterManager::FilterEventType::kRelax;
4795   // As of 2013/01, three filters evaluate sub-parts of the objective
4796   // function:
4797   // - NodeDisjunctionFilter: takes disjunction penalty costs into account,
4798   // - PathCumulFilter: takes dimension span costs into account,
4799   // - ObjectiveFilter:
4800   //     - VehicleAmortizedCostFilter, which considers the part of the cost
4801   //       related to amortized linear and quadratic vehicle cost factors.
4802   //     - LocalSearchObjectiveFilter, which takes dimension "arc" costs into
4803   //       account.
4804   std::vector<LocalSearchFilterManager::FilterEvent> filters;
4805   // VehicleAmortizedCostFilter can have a negative value, so it must be first.
4806   if (filter_cost && vehicle_amortized_cost_factors_set_) {
4807     filters.push_back({MakeVehicleAmortizedCostFilter(*this), kAccept});
4808   }
4809 
4810   // The SumObjectiveFilter has the best reject/second ratio in practice,
4811   // so it is the earliest.
4812   if (filter_cost) {
4813     if (CostsAreHomogeneousAcrossVehicles()) {
4814       LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4815           nexts_,
4816           [this](int64_t i, int64_t j) { return GetHomogeneousCost(i, j); },
4817           Solver::LE);
4818       filters.push_back({sum, kAccept});
4819     } else {
4820       LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4821           nexts_, vehicle_vars_,
4822           [this](int64_t i, int64_t j, int64_t k) {
4823             return GetArcCostForVehicle(i, j, k);
4824           },
4825           Solver::LE);
4826       filters.push_back({sum, kAccept});
4827     }
4828   }
4829 
4830   filters.push_back({solver_->MakeVariableDomainFilter(), kAccept});
4831 
4832   if (vehicles_ > max_active_vehicles_) {
4833     filters.push_back({MakeMaxActiveVehiclesFilter(*this), kAccept});
4834   }
4835 
4836   if (!disjunctions_.empty()) {
4837     if (filter_cost || HasMandatoryDisjunctions() ||
4838         HasMaxCardinalityConstrainedDisjunctions()) {
4839       filters.push_back(
4840           {MakeNodeDisjunctionFilter(*this, filter_cost), kAccept});
4841     }
4842   }
4843 
4844   // If vehicle costs are not homogeneous, vehicle variables will be added to
4845   // local search deltas and their domain will be checked by
4846   // VariableDomainFilter.
4847   if (CostsAreHomogeneousAcrossVehicles()) {
4848     filters.push_back({MakeVehicleVarFilter(*this), kAccept});
4849   }
4850 
4851   const PathState* path_state_reference = nullptr;
4852   if (HasUnaryDimension(GetDimensions())) {
4853     std::vector<int> path_starts;
4854     std::vector<int> path_ends;
4855     ConvertVectorInt64ToVectorInt(starts_, &path_starts);
4856     ConvertVectorInt64ToVectorInt(ends_, &path_ends);
4857 
4858     auto path_state = absl::make_unique<PathState>(
4859         Size() + vehicles(), std::move(path_starts), std::move(path_ends));
4860     path_state_reference = path_state.get();
4861     filters.push_back(
4862         {MakePathStateFilter(solver_.get(), std::move(path_state), Nexts()),
4863          kRelax});
4864     AppendLightWeightDimensionFilters(path_state_reference, GetDimensions(),
4865                                       &filters);
4866   }
4867 
4868   // As of 10/2021, TypeRegulationsFilter assumes pickup and delivery
4869   // constraints are enforced, therefore PickupDeliveryFilter must be
4870   // called first.
4871   if (!pickup_delivery_pairs_.empty()) {
4872     filters.push_back(
4873         {MakePickupDeliveryFilter(*this, pickup_delivery_pairs_,
4874                                   vehicle_pickup_delivery_policy_),
4875          kAccept});
4876   }
4877 
4878   if (HasTypeRegulations()) {
4879     filters.push_back({MakeTypeRegulationsFilter(*this), kAccept});
4880   }
4881 
4882   AppendDimensionCumulFilters(GetDimensions(), parameters, filter_cost,
4883                               /* filter_light_weight_unary_dimensions */ false,
4884                               &filters);
4885 
4886   for (const RoutingDimension* dimension : dimensions_) {
4887     if (!dimension->HasBreakConstraints()) continue;
4888     filters.push_back({MakeVehicleBreaksFilter(*this, *dimension), kAccept});
4889   }
4890   filters.insert(filters.end(), extra_filters_.begin(), extra_filters_.end());
4891   return filters;
4892 }
4893 
GetOrCreateLocalSearchFilterManager(const RoutingSearchParameters & parameters)4894 LocalSearchFilterManager* RoutingModel::GetOrCreateLocalSearchFilterManager(
4895     const RoutingSearchParameters& parameters) {
4896   if (!local_search_filter_manager_) {
4897     local_search_filter_manager_ =
4898         solver_->RevAlloc(new LocalSearchFilterManager(
4899             GetOrCreateLocalSearchFilters(parameters)));
4900   }
4901   return local_search_filter_manager_;
4902 }
4903 
4904 std::vector<LocalSearchFilterManager::FilterEvent>
GetOrCreateFeasibilityFilters(const RoutingSearchParameters & parameters)4905 RoutingModel::GetOrCreateFeasibilityFilters(
4906     const RoutingSearchParameters& parameters) {
4907   return GetOrCreateLocalSearchFilters(parameters, false);
4908 }
4909 
GetOrCreateFeasibilityFilterManager(const RoutingSearchParameters & parameters)4910 LocalSearchFilterManager* RoutingModel::GetOrCreateFeasibilityFilterManager(
4911     const RoutingSearchParameters& parameters) {
4912   if (!feasibility_filter_manager_) {
4913     feasibility_filter_manager_ =
4914         solver_->RevAlloc(new LocalSearchFilterManager(
4915             GetOrCreateFeasibilityFilters(parameters)));
4916   }
4917   return feasibility_filter_manager_;
4918 }
4919 
4920 LocalSearchFilterManager*
GetOrCreateStrongFeasibilityFilterManager(const RoutingSearchParameters & parameters)4921 RoutingModel::GetOrCreateStrongFeasibilityFilterManager(
4922     const RoutingSearchParameters& parameters) {
4923   if (!strong_feasibility_filter_manager_) {
4924     std::vector<LocalSearchFilterManager::FilterEvent> filters =
4925         GetOrCreateFeasibilityFilters(parameters);
4926     filters.push_back({MakeCPFeasibilityFilter(this),
4927                        LocalSearchFilterManager::FilterEventType::kAccept});
4928     strong_feasibility_filter_manager_ =
4929         solver_->RevAlloc(new LocalSearchFilterManager(std::move(filters)));
4930   }
4931   return strong_feasibility_filter_manager_;
4932 }
4933 
4934 namespace {
AllTransitsPositive(const RoutingDimension & dimension)4935 bool AllTransitsPositive(const RoutingDimension& dimension) {
4936   for (int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
4937     if (!dimension.AreVehicleTransitsPositive(vehicle)) {
4938       return false;
4939     }
4940   }
4941   return true;
4942 }
4943 }  // namespace
4944 
StoreDimensionCumulOptimizers(const RoutingSearchParameters & parameters)4945 void RoutingModel::StoreDimensionCumulOptimizers(
4946     const RoutingSearchParameters& parameters) {
4947   Assignment* packed_dimensions_collector_assignment =
4948       solver_->MakeAssignment();
4949   packed_dimensions_collector_assignment->AddObjective(CostVar());
4950   const int num_dimensions = dimensions_.size();
4951   local_optimizer_index_.resize(num_dimensions, -1);
4952   global_optimizer_index_.resize(num_dimensions, -1);
4953   for (DimensionIndex dim = DimensionIndex(0); dim < num_dimensions; dim++) {
4954     RoutingDimension* dimension = dimensions_[dim];
4955     DCHECK_EQ(dimension->model(), this);
4956     const int num_resource_groups =
4957         GetDimensionResourceGroupIndices(dimension).size();
4958     bool needs_optimizer = false;
4959     if (dimension->global_span_cost_coefficient() > 0 ||
4960         !dimension->GetNodePrecedences().empty() || num_resource_groups > 1) {
4961       // Use global optimizer.
4962       needs_optimizer = true;
4963       global_optimizer_index_[dim] = global_dimension_optimizers_.size();
4964       global_dimension_optimizers_.push_back(
4965           absl::make_unique<GlobalDimensionCumulOptimizer>(
4966               dimension, parameters.continuous_scheduling_solver()));
4967       global_dimension_mp_optimizers_.push_back(
4968           absl::make_unique<GlobalDimensionCumulOptimizer>(
4969               dimension, parameters.mixed_integer_scheduling_solver()));
4970       if (!AllTransitsPositive(*dimension)) {
4971         dimension->SetOffsetForGlobalOptimizer(0);
4972       } else {
4973         int64_t offset =
4974             vehicles() == 0 ? 0 : std::numeric_limits<int64_t>::max();
4975         for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4976           DCHECK_GE(dimension->CumulVar(Start(vehicle))->Min(), 0);
4977           offset =
4978               std::min(offset, dimension->CumulVar(Start(vehicle))->Min() - 1);
4979         }
4980         dimension->SetOffsetForGlobalOptimizer(std::max(Zero(), offset));
4981       }
4982     }
4983     // Check if we need the local optimizer.
4984     bool has_span_cost = false;
4985     bool has_span_limit = false;
4986     std::vector<int64_t> vehicle_offsets(vehicles());
4987     for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4988       if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4989         has_span_cost = true;
4990       }
4991       if (dimension->GetSpanUpperBoundForVehicle(vehicle) <
4992           std::numeric_limits<int64_t>::max()) {
4993         has_span_limit = true;
4994       }
4995       DCHECK_GE(dimension->CumulVar(Start(vehicle))->Min(), 0);
4996       vehicle_offsets[vehicle] =
4997           dimension->AreVehicleTransitsPositive(vehicle)
4998               ? std::max(Zero(), dimension->CumulVar(Start(vehicle))->Min() - 1)
4999               : 0;
5000     }
5001     bool has_soft_lower_bound = false;
5002     bool has_soft_upper_bound = false;
5003     for (int i = 0; i < dimension->cumuls().size(); ++i) {
5004       if (dimension->HasCumulVarSoftLowerBound(i)) {
5005         has_soft_lower_bound = true;
5006       }
5007       if (dimension->HasCumulVarSoftUpperBound(i)) {
5008         has_soft_upper_bound = true;
5009       }
5010     }
5011     int num_linear_constraints = 0;
5012     if (has_span_cost) ++num_linear_constraints;
5013     if (has_span_limit) ++num_linear_constraints;
5014     if (dimension->HasSoftSpanUpperBounds()) ++num_linear_constraints;
5015     if (has_soft_lower_bound) ++num_linear_constraints;
5016     if (has_soft_upper_bound) ++num_linear_constraints;
5017     if (dimension->HasBreakConstraints()) ++num_linear_constraints;
5018     if (num_resource_groups > 0 || num_linear_constraints >= 2) {
5019       needs_optimizer = true;
5020       dimension->SetVehicleOffsetsForLocalOptimizer(std::move(vehicle_offsets));
5021       local_optimizer_index_[dim] = local_dimension_optimizers_.size();
5022       local_dimension_optimizers_.push_back(
5023           absl::make_unique<LocalDimensionCumulOptimizer>(
5024               dimension, parameters.continuous_scheduling_solver()));
5025       local_dimension_mp_optimizers_.push_back(
5026           absl::make_unique<LocalDimensionCumulOptimizer>(
5027               dimension, parameters.mixed_integer_scheduling_solver()));
5028     }
5029     if (needs_optimizer) {
5030       packed_dimensions_collector_assignment->Add(dimension->cumuls());
5031     }
5032   }
5033 
5034   // NOTE(b/129252839): We also add all other extra variables to the
5035   // packed_dimensions_collector_assignment to make sure the necessary
5036   // propagations on these variables after packing are correctly stored.
5037   for (IntVar* const extra_var : extra_vars_) {
5038     packed_dimensions_collector_assignment->Add(extra_var);
5039   }
5040   for (IntervalVar* const extra_interval : extra_intervals_) {
5041     packed_dimensions_collector_assignment->Add(extra_interval);
5042   }
5043 
5044   packed_dimensions_assignment_collector_ = solver_->MakeFirstSolutionCollector(
5045       packed_dimensions_collector_assignment);
5046 }
5047 
GetDimensionsWithSoftOrSpanCosts() const5048 std::vector<RoutingDimension*> RoutingModel::GetDimensionsWithSoftOrSpanCosts()
5049     const {
5050   std::vector<RoutingDimension*> dimensions;
5051   for (RoutingDimension* dimension : dimensions_) {
5052     bool has_soft_or_span_cost = false;
5053     for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
5054       if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
5055         has_soft_or_span_cost = true;
5056         break;
5057       }
5058     }
5059     if (!has_soft_or_span_cost) {
5060       for (int i = 0; i < dimension->cumuls().size(); ++i) {
5061         if (dimension->HasCumulVarSoftUpperBound(i) ||
5062             dimension->HasCumulVarSoftLowerBound(i)) {
5063           has_soft_or_span_cost = true;
5064           break;
5065         }
5066       }
5067     }
5068     if (has_soft_or_span_cost) dimensions.push_back(dimension);
5069   }
5070   return dimensions;
5071 }
5072 
5073 DecisionBuilder*
CreateFinalizerForMinimizedAndMaximizedVariables()5074 RoutingModel::CreateFinalizerForMinimizedAndMaximizedVariables() {
5075   std::stable_sort(finalizer_variable_cost_pairs_.begin(),
5076                    finalizer_variable_cost_pairs_.end(),
5077                    [](const std::pair<IntVar*, int64_t>& var_cost1,
5078                       const std::pair<IntVar*, int64_t>& var_cost2) {
5079                      return var_cost1.second > var_cost2.second;
5080                    });
5081   const int num_variables = finalizer_variable_cost_pairs_.size() +
5082                             finalizer_variable_target_pairs_.size();
5083   std::vector<IntVar*> variables;
5084   std::vector<int64_t> targets;
5085   variables.reserve(num_variables);
5086   targets.reserve(num_variables);
5087   for (const auto& variable_cost : finalizer_variable_cost_pairs_) {
5088     variables.push_back(variable_cost.first);
5089     targets.push_back(std::numeric_limits<int64_t>::min());
5090   }
5091   for (const auto& variable_target : finalizer_variable_target_pairs_) {
5092     variables.push_back(variable_target.first);
5093     targets.push_back(variable_target.second);
5094   }
5095   return MakeSetValuesFromTargets(solver(), std::move(variables),
5096                                   std::move(targets));
5097 }
5098 
CreateSolutionFinalizer(SearchLimit * lns_limit)5099 DecisionBuilder* RoutingModel::CreateSolutionFinalizer(SearchLimit* lns_limit) {
5100   std::vector<DecisionBuilder*> decision_builders;
5101   decision_builders.push_back(solver_->MakePhase(
5102       nexts_, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE));
5103   bool routes_are_interdependent = false;
5104   for (RoutingDimension* const dim : dimensions_) {
5105     if (!GetDimensionResourceGroupIndices(dim).empty() ||
5106         GetMutableGlobalCumulOptimizer(*dim) != nullptr) {
5107       routes_are_interdependent = true;
5108       break;
5109     }
5110   }
5111   if (!routes_are_interdependent) {
5112     // When routes are interdependent, optimal dimension values of unchanged
5113     // routes might be affected by changes on other routes, so we only add the
5114     // RestoreDimensionValuesForUnchangedRoutes decision builder when routes
5115     // aren't interdependent.
5116     decision_builders.push_back(
5117         MakeRestoreDimensionValuesForUnchangedRoutes(this));
5118   }
5119   if (!local_dimension_optimizers_.empty()) {
5120     decision_builders.push_back(
5121         solver_->RevAlloc(new SetCumulsFromLocalDimensionCosts(
5122             &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
5123             [this](const RoutingDimension* dim) {
5124               // Don't set cumuls of dimensions with resources or having a
5125               // global optimizer.
5126               return GetDimensionResourceGroupIndices(dim).empty() &&
5127                      GetMutableGlobalCumulOptimizer(*dim) == nullptr;
5128             },
5129             lns_limit)));
5130   }
5131   // Add a specific DB for setting cumuls of dimensions with a single resource
5132   // and no global optimizer.
5133   for (const RoutingDimension* const dim : dimensions_) {
5134     if (GetMutableGlobalCumulOptimizer(*dim) != nullptr) continue;
5135     DCHECK_LE(GetDimensionResourceGroupIndices(dim).size(), 1);
5136     if (GetDimensionResourceGroupIndices(dim).size() != 1) continue;
5137 
5138     LocalDimensionCumulOptimizer* const optimizer =
5139         GetMutableLocalCumulOptimizer(*dim);
5140     DCHECK_NE(optimizer, nullptr);
5141     LocalDimensionCumulOptimizer* const mp_optimizer =
5142         GetMutableLocalCumulMPOptimizer(*dim);
5143     DCHECK_NE(mp_optimizer, nullptr);
5144     decision_builders.push_back(
5145         solver_->RevAlloc(new SetCumulsFromResourceAssignmentCosts(
5146             optimizer, mp_optimizer, lns_limit)));
5147   }
5148 
5149   if (!global_dimension_optimizers_.empty()) {
5150     decision_builders.push_back(
5151         solver_->RevAlloc(new SetCumulsFromGlobalDimensionCosts(
5152             &global_dimension_optimizers_, &global_dimension_mp_optimizers_,
5153             lns_limit)));
5154   }
5155   decision_builders.push_back(
5156       CreateFinalizerForMinimizedAndMaximizedVariables());
5157 
5158   return solver_->Compose(decision_builders);
5159 }
5160 
CreateFirstSolutionDecisionBuilders(const RoutingSearchParameters & search_parameters)5161 void RoutingModel::CreateFirstSolutionDecisionBuilders(
5162     const RoutingSearchParameters& search_parameters) {
5163   first_solution_decision_builders_.resize(
5164       FirstSolutionStrategy_Value_Value_ARRAYSIZE, nullptr);
5165   first_solution_filtered_decision_builders_.resize(
5166       FirstSolutionStrategy_Value_Value_ARRAYSIZE, nullptr);
5167   DecisionBuilder* const finalize_solution =
5168       CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit());
5169   // Default heuristic
5170   first_solution_decision_builders_
5171       [FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE] = finalize_solution;
5172   // Global cheapest addition heuristic.
5173   first_solution_decision_builders_
5174       [FirstSolutionStrategy::GLOBAL_CHEAPEST_ARC] = solver_->MakePhase(
5175           nexts_,
5176           [this](int64_t i, int64_t j) {
5177             return GetArcCostForFirstSolution(i, j);
5178           },
5179           Solver::CHOOSE_STATIC_GLOBAL_BEST);
5180   // Cheapest addition heuristic.
5181   Solver::IndexEvaluator2 eval = [this](int64_t i, int64_t j) {
5182     return GetArcCostForFirstSolution(i, j);
5183   };
5184   first_solution_decision_builders_[FirstSolutionStrategy::LOCAL_CHEAPEST_ARC] =
5185       solver_->MakePhase(nexts_, Solver::CHOOSE_FIRST_UNBOUND, eval);
5186   // Path-based cheapest addition heuristic.
5187   first_solution_decision_builders_[FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5188       solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, eval);
5189   if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5190     first_solution_filtered_decision_builders_
5191         [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5192             solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5193                 absl::make_unique<EvaluatorCheapestAdditionFilteredHeuristic>(
5194                     this,
5195                     [this](int64_t i, int64_t j) {
5196                       return GetArcCostForFirstSolution(i, j);
5197                     },
5198                     GetOrCreateFeasibilityFilterManager(search_parameters))));
5199     first_solution_decision_builders_
5200         [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5201             solver_->Try(first_solution_filtered_decision_builders_
5202                              [FirstSolutionStrategy::PATH_CHEAPEST_ARC],
5203                          first_solution_decision_builders_
5204                              [FirstSolutionStrategy::PATH_CHEAPEST_ARC]);
5205   }
5206   // Path-based most constrained arc addition heuristic.
5207   Solver::VariableValueComparator comp = [this](int64_t i, int64_t j,
5208                                                 int64_t k) {
5209     return ArcIsMoreConstrainedThanArc(i, j, k);
5210   };
5211 
5212   first_solution_decision_builders_
5213       [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5214           solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, comp);
5215   if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5216     first_solution_filtered_decision_builders_
5217         [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5218             solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5219                 absl::make_unique<ComparatorCheapestAdditionFilteredHeuristic>(
5220                     this, comp,
5221                     GetOrCreateFeasibilityFilterManager(search_parameters))));
5222     first_solution_decision_builders_
5223         [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->Try(
5224             first_solution_filtered_decision_builders_
5225                 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC],
5226             first_solution_decision_builders_
5227                 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC]);
5228   }
5229   // Evaluator-based path heuristic.
5230   if (first_solution_evaluator_ != nullptr) {
5231     first_solution_decision_builders_
5232         [FirstSolutionStrategy::EVALUATOR_STRATEGY] = solver_->MakePhase(
5233             nexts_, Solver::CHOOSE_PATH, first_solution_evaluator_);
5234   } else {
5235     first_solution_decision_builders_
5236         [FirstSolutionStrategy::EVALUATOR_STRATEGY] = nullptr;
5237   }
5238   // All unperformed heuristic.
5239   first_solution_decision_builders_[FirstSolutionStrategy::ALL_UNPERFORMED] =
5240       MakeAllUnperformed(this);
5241   // Best insertion heuristic.
5242   RegularLimit* const ls_limit = solver_->MakeLimit(
5243       GetTimeLimit(search_parameters), std::numeric_limits<int64_t>::max(),
5244       std::numeric_limits<int64_t>::max(), std::numeric_limits<int64_t>::max(),
5245       /*smart_time_check=*/true);
5246   DecisionBuilder* const finalize = solver_->MakeSolveOnce(
5247       finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit());
5248   LocalSearchPhaseParameters* const insertion_parameters =
5249       solver_->MakeLocalSearchPhaseParameters(
5250           nullptr, CreateInsertionOperator(), finalize, ls_limit,
5251           GetOrCreateLocalSearchFilterManager(search_parameters));
5252   std::vector<IntVar*> decision_vars = nexts_;
5253   if (!CostsAreHomogeneousAcrossVehicles()) {
5254     decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(),
5255                          vehicle_vars_.end());
5256   }
5257   const int64_t optimization_step = std::max(
5258       MathUtil::FastInt64Round(search_parameters.optimization_step()), One());
5259   first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5260       solver_->MakeNestedOptimize(
5261           solver_->MakeLocalSearchPhase(decision_vars, MakeAllUnperformed(this),
5262                                         insertion_parameters),
5263           GetOrCreateAssignment(), false, optimization_step);
5264   first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5265       solver_->Compose(first_solution_decision_builders_
5266                            [FirstSolutionStrategy::BEST_INSERTION],
5267                        finalize);
5268 
5269   // Parallel/Sequential Global cheapest insertion
5270   GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
5271       gci_parameters;
5272   gci_parameters.is_sequential = false;
5273   gci_parameters.farthest_seeds_ratio =
5274       search_parameters.cheapest_insertion_farthest_seeds_ratio();
5275   gci_parameters.neighbors_ratio =
5276       search_parameters.cheapest_insertion_first_solution_neighbors_ratio();
5277   gci_parameters.min_neighbors =
5278       search_parameters.cheapest_insertion_first_solution_min_neighbors();
5279   gci_parameters.use_neighbors_ratio_for_initialization =
5280       search_parameters
5281           .cheapest_insertion_first_solution_use_neighbors_ratio_for_initialization();  // NOLINT
5282   gci_parameters.add_unperformed_entries =
5283       search_parameters.cheapest_insertion_add_unperformed_entries();
5284   for (bool is_sequential : {false, true}) {
5285     FirstSolutionStrategy::Value first_solution_strategy =
5286         is_sequential ? FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION
5287                       : FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5288     gci_parameters.is_sequential = is_sequential;
5289 
5290     first_solution_filtered_decision_builders_[first_solution_strategy] =
5291         solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5292             absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5293                 this,
5294                 [this](int64_t i, int64_t j, int64_t vehicle) {
5295                   return GetArcCostForVehicle(i, j, vehicle);
5296                 },
5297                 [this](int64_t i) { return UnperformedPenaltyOrValue(0, i); },
5298                 GetOrCreateFeasibilityFilterManager(search_parameters),
5299                 gci_parameters)));
5300     IntVarFilteredDecisionBuilder* const strong_gci =
5301         solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5302             absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5303                 this,
5304                 [this](int64_t i, int64_t j, int64_t vehicle) {
5305                   return GetArcCostForVehicle(i, j, vehicle);
5306                 },
5307                 [this](int64_t i) { return UnperformedPenaltyOrValue(0, i); },
5308                 GetOrCreateStrongFeasibilityFilterManager(search_parameters),
5309                 gci_parameters)));
5310     first_solution_decision_builders_[first_solution_strategy] = solver_->Try(
5311         first_solution_filtered_decision_builders_[first_solution_strategy],
5312         solver_->Try(strong_gci, first_solution_decision_builders_
5313                                      [FirstSolutionStrategy::BEST_INSERTION]));
5314   }
5315 
5316   // Local cheapest insertion
5317   first_solution_filtered_decision_builders_
5318       [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] =
5319           solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5320               absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5321                   this,
5322                   [this](int64_t i, int64_t j, int64_t vehicle) {
5323                     return GetArcCostForVehicle(i, j, vehicle);
5324                   },
5325                   GetOrCreateFeasibilityFilterManager(search_parameters))));
5326   IntVarFilteredDecisionBuilder* const strong_lci =
5327       solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5328           absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5329               this,
5330               [this](int64_t i, int64_t j, int64_t vehicle) {
5331                 return GetArcCostForVehicle(i, j, vehicle);
5332               },
5333               GetOrCreateStrongFeasibilityFilterManager(search_parameters))));
5334   first_solution_decision_builders_
5335       [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = solver_->Try(
5336           first_solution_filtered_decision_builders_
5337               [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION],
5338           solver_->Try(strong_lci,
5339                        first_solution_decision_builders_
5340                            [FirstSolutionStrategy::BEST_INSERTION]));
5341   // Savings
5342   SavingsFilteredHeuristic::SavingsParameters savings_parameters;
5343   savings_parameters.neighbors_ratio =
5344       search_parameters.savings_neighbors_ratio();
5345   savings_parameters.max_memory_usage_bytes =
5346       search_parameters.savings_max_memory_usage_bytes();
5347   savings_parameters.add_reverse_arcs =
5348       search_parameters.savings_add_reverse_arcs();
5349   savings_parameters.arc_coefficient =
5350       search_parameters.savings_arc_coefficient();
5351   LocalSearchFilterManager* filter_manager = nullptr;
5352   if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5353     filter_manager = GetOrCreateFeasibilityFilterManager(search_parameters);
5354   }
5355 
5356   if (search_parameters.savings_parallel_routes()) {
5357     IntVarFilteredDecisionBuilder* savings_db =
5358         solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5359             absl::make_unique<ParallelSavingsFilteredHeuristic>(
5360                 this, savings_parameters, filter_manager)));
5361     if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5362       first_solution_filtered_decision_builders_
5363           [FirstSolutionStrategy::SAVINGS] = savings_db;
5364     }
5365 
5366     first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5367         solver_->Try(savings_db,
5368                      solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5369                          absl::make_unique<ParallelSavingsFilteredHeuristic>(
5370                              this, savings_parameters,
5371                              GetOrCreateStrongFeasibilityFilterManager(
5372                                  search_parameters)))));
5373   } else {
5374     IntVarFilteredDecisionBuilder* savings_db =
5375         solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5376             absl::make_unique<SequentialSavingsFilteredHeuristic>(
5377                 this, savings_parameters, filter_manager)));
5378     if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5379       first_solution_filtered_decision_builders_
5380           [FirstSolutionStrategy::SAVINGS] = savings_db;
5381     }
5382 
5383     first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5384         solver_->Try(savings_db,
5385                      solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5386                          absl::make_unique<SequentialSavingsFilteredHeuristic>(
5387                              this, savings_parameters,
5388                              GetOrCreateStrongFeasibilityFilterManager(
5389                                  search_parameters)))));
5390   }
5391   // Sweep
5392   first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5393       MakeSweepDecisionBuilder(this, true);
5394   DecisionBuilder* sweep_builder = MakeSweepDecisionBuilder(this, false);
5395   first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5396       solver_->Try(
5397           sweep_builder,
5398           first_solution_decision_builders_[FirstSolutionStrategy::SWEEP]);
5399   // Christofides
5400   first_solution_decision_builders_[FirstSolutionStrategy::CHRISTOFIDES] =
5401       solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5402           absl::make_unique<ChristofidesFilteredHeuristic>(
5403               this, GetOrCreateFeasibilityFilterManager(search_parameters),
5404               search_parameters.christofides_use_minimum_matching())));
5405   // Automatic
5406   const bool has_precedences = std::any_of(
5407       dimensions_.begin(), dimensions_.end(),
5408       [](RoutingDimension* dim) { return !dim->GetNodePrecedences().empty(); });
5409   bool has_single_vehicle_node = false;
5410   for (int node = 0; node < Size(); node++) {
5411     if (!IsStart(node) && !IsEnd(node) && allowed_vehicles_[node].size() == 1) {
5412       has_single_vehicle_node = true;
5413       break;
5414     }
5415   }
5416   automatic_first_solution_strategy_ =
5417       AutomaticFirstSolutionStrategy(!pickup_delivery_pairs_.empty(),
5418                                      has_precedences, has_single_vehicle_node);
5419   first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC] =
5420       first_solution_decision_builders_[automatic_first_solution_strategy_];
5421   first_solution_decision_builders_[FirstSolutionStrategy::UNSET] =
5422       first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC];
5423 
5424   // Naming decision builders to clarify profiling.
5425   for (FirstSolutionStrategy_Value strategy =
5426            FirstSolutionStrategy_Value_Value_MIN;
5427        strategy <= FirstSolutionStrategy_Value_Value_MAX;
5428        strategy = FirstSolutionStrategy_Value(strategy + 1)) {
5429     if (first_solution_decision_builders_[strategy] == nullptr ||
5430         strategy == FirstSolutionStrategy::AUTOMATIC) {
5431       continue;
5432     }
5433     const std::string strategy_name =
5434         FirstSolutionStrategy_Value_Name(strategy);
5435     const std::string& log_tag = search_parameters.log_tag();
5436     if (!log_tag.empty() && log_tag != strategy_name) {
5437       first_solution_decision_builders_[strategy]->set_name(absl::StrFormat(
5438           "%s / %s", strategy_name, search_parameters.log_tag()));
5439     } else {
5440       first_solution_decision_builders_[strategy]->set_name(strategy_name);
5441     }
5442   }
5443 }
5444 
GetFirstSolutionDecisionBuilder(const RoutingSearchParameters & search_parameters) const5445 DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder(
5446     const RoutingSearchParameters& search_parameters) const {
5447   const FirstSolutionStrategy::Value first_solution_strategy =
5448       search_parameters.first_solution_strategy();
5449   if (first_solution_strategy < first_solution_decision_builders_.size()) {
5450     return first_solution_decision_builders_[first_solution_strategy];
5451   } else {
5452     return nullptr;
5453   }
5454 }
5455 
5456 IntVarFilteredDecisionBuilder*
GetFilteredFirstSolutionDecisionBuilderOrNull(const RoutingSearchParameters & search_parameters) const5457 RoutingModel::GetFilteredFirstSolutionDecisionBuilderOrNull(
5458     const RoutingSearchParameters& search_parameters) const {
5459   const FirstSolutionStrategy::Value first_solution_strategy =
5460       search_parameters.first_solution_strategy();
5461   return first_solution_filtered_decision_builders_[first_solution_strategy];
5462 }
5463 
CreateLocalSearchParameters(const RoutingSearchParameters & search_parameters)5464 LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters(
5465     const RoutingSearchParameters& search_parameters) {
5466   SearchLimit* lns_limit = GetOrCreateLargeNeighborhoodSearchLimit();
5467   return solver_->MakeLocalSearchPhaseParameters(
5468       CostVar(), GetNeighborhoodOperators(search_parameters),
5469       solver_->MakeSolveOnce(CreateSolutionFinalizer(lns_limit), lns_limit),
5470       GetOrCreateLocalSearchLimit(),
5471       GetOrCreateLocalSearchFilterManager(search_parameters));
5472 }
5473 
CreateLocalSearchDecisionBuilder(const RoutingSearchParameters & search_parameters)5474 DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder(
5475     const RoutingSearchParameters& search_parameters) {
5476   const int size = Size();
5477   DecisionBuilder* first_solution =
5478       GetFirstSolutionDecisionBuilder(search_parameters);
5479   LocalSearchPhaseParameters* const parameters =
5480       CreateLocalSearchParameters(search_parameters);
5481   SearchLimit* first_solution_lns_limit =
5482       GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5483   DecisionBuilder* const first_solution_sub_decision_builder =
5484       solver_->MakeSolveOnce(CreateSolutionFinalizer(first_solution_lns_limit),
5485                              first_solution_lns_limit);
5486   if (CostsAreHomogeneousAcrossVehicles()) {
5487     return solver_->MakeLocalSearchPhase(nexts_, first_solution,
5488                                          first_solution_sub_decision_builder,
5489                                          parameters);
5490   } else {
5491     const int all_size = size + size + vehicles_;
5492     std::vector<IntVar*> all_vars(all_size);
5493     for (int i = 0; i < size; ++i) {
5494       all_vars[i] = nexts_[i];
5495     }
5496     for (int i = size; i < all_size; ++i) {
5497       all_vars[i] = vehicle_vars_[i - size];
5498     }
5499     return solver_->MakeLocalSearchPhase(all_vars, first_solution,
5500                                          first_solution_sub_decision_builder,
5501                                          parameters);
5502   }
5503 }
5504 
SetupDecisionBuilders(const RoutingSearchParameters & search_parameters)5505 void RoutingModel::SetupDecisionBuilders(
5506     const RoutingSearchParameters& search_parameters) {
5507   if (search_parameters.use_depth_first_search()) {
5508     SearchLimit* first_lns_limit =
5509         GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5510     solve_db_ = solver_->Compose(
5511         GetFirstSolutionDecisionBuilder(search_parameters),
5512         solver_->MakeSolveOnce(CreateSolutionFinalizer(first_lns_limit),
5513                                first_lns_limit));
5514   } else {
5515     solve_db_ = CreateLocalSearchDecisionBuilder(search_parameters);
5516   }
5517   CHECK(preassignment_ != nullptr);
5518   DecisionBuilder* restore_preassignment =
5519       solver_->MakeRestoreAssignment(preassignment_);
5520   solve_db_ = solver_->Compose(restore_preassignment, solve_db_);
5521   improve_db_ =
5522       solver_->Compose(restore_preassignment,
5523                        solver_->MakeLocalSearchPhase(
5524                            GetOrCreateAssignment(),
5525                            CreateLocalSearchParameters(search_parameters)));
5526   restore_assignment_ = solver_->Compose(
5527       solver_->MakeRestoreAssignment(GetOrCreateAssignment()),
5528       CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5529   restore_tmp_assignment_ = solver_->Compose(
5530       restore_preassignment,
5531       solver_->MakeRestoreAssignment(GetOrCreateTmpAssignment()),
5532       CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5533 }
5534 
SetupMetaheuristics(const RoutingSearchParameters & search_parameters)5535 void RoutingModel::SetupMetaheuristics(
5536     const RoutingSearchParameters& search_parameters) {
5537   SearchMonitor* optimize;
5538   const LocalSearchMetaheuristic::Value metaheuristic =
5539       search_parameters.local_search_metaheuristic();
5540   // Some metaheuristics will effectively never terminate; warn
5541   // user if they fail to set a time limit.
5542   bool limit_too_long =
5543       !search_parameters.has_time_limit() &&
5544       search_parameters.solution_limit() == std::numeric_limits<int64_t>::max();
5545   const int64_t optimization_step = std::max(
5546       MathUtil::FastInt64Round(search_parameters.optimization_step()), One());
5547   switch (metaheuristic) {
5548     case LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH:
5549       if (CostsAreHomogeneousAcrossVehicles()) {
5550         optimize = solver_->MakeGuidedLocalSearch(
5551             false, cost_,
5552             [this](int64_t i, int64_t j) { return GetHomogeneousCost(i, j); },
5553             optimization_step, nexts_,
5554             search_parameters.guided_local_search_lambda_coefficient());
5555       } else {
5556         optimize = solver_->MakeGuidedLocalSearch(
5557             false, cost_,
5558             [this](int64_t i, int64_t j, int64_t k) {
5559               return GetArcCostForVehicle(i, j, k);
5560             },
5561             optimization_step, nexts_, vehicle_vars_,
5562             search_parameters.guided_local_search_lambda_coefficient());
5563       }
5564       break;
5565     case LocalSearchMetaheuristic::SIMULATED_ANNEALING:
5566       optimize =
5567           solver_->MakeSimulatedAnnealing(false, cost_, optimization_step, 100);
5568       break;
5569     case LocalSearchMetaheuristic::TABU_SEARCH:
5570       optimize = solver_->MakeTabuSearch(false, cost_, optimization_step,
5571                                          nexts_, 10, 10, .8);
5572       break;
5573     case LocalSearchMetaheuristic::GENERIC_TABU_SEARCH: {
5574       std::vector<operations_research::IntVar*> tabu_vars;
5575       if (tabu_var_callback_) {
5576         tabu_vars = tabu_var_callback_(this);
5577       } else {
5578         tabu_vars.push_back(cost_);
5579       }
5580       optimize = solver_->MakeGenericTabuSearch(false, cost_, optimization_step,
5581                                                 tabu_vars, 100);
5582       break;
5583     }
5584     default:
5585       limit_too_long = false;
5586       optimize = solver_->MakeMinimize(cost_, optimization_step);
5587   }
5588   if (limit_too_long) {
5589     LOG(WARNING) << LocalSearchMetaheuristic::Value_Name(metaheuristic)
5590                  << " specified without sane timeout: solve may run forever.";
5591   }
5592   monitors_.push_back(optimize);
5593 }
5594 
SetTabuVarsCallback(GetTabuVarsCallback tabu_var_callback)5595 void RoutingModel::SetTabuVarsCallback(GetTabuVarsCallback tabu_var_callback) {
5596   tabu_var_callback_ = std::move(tabu_var_callback);
5597 }
5598 
SetupAssignmentCollector(const RoutingSearchParameters & search_parameters)5599 void RoutingModel::SetupAssignmentCollector(
5600     const RoutingSearchParameters& search_parameters) {
5601   Assignment* full_assignment = solver_->MakeAssignment();
5602   for (const RoutingDimension* const dimension : dimensions_) {
5603     full_assignment->Add(dimension->cumuls());
5604   }
5605   for (IntVar* const extra_var : extra_vars_) {
5606     full_assignment->Add(extra_var);
5607   }
5608   for (IntervalVar* const extra_interval : extra_intervals_) {
5609     full_assignment->Add(extra_interval);
5610   }
5611   full_assignment->Add(nexts_);
5612   full_assignment->Add(active_);
5613   full_assignment->Add(vehicle_vars_);
5614   full_assignment->AddObjective(cost_);
5615 
5616   collect_assignments_ = solver_->MakeNBestValueSolutionCollector(
5617       full_assignment, search_parameters.number_of_solutions_to_collect(),
5618       false);
5619   collect_one_assignment_ =
5620       solver_->MakeFirstSolutionCollector(full_assignment);
5621   monitors_.push_back(collect_assignments_);
5622 }
5623 
SetupTrace(const RoutingSearchParameters & search_parameters)5624 void RoutingModel::SetupTrace(
5625     const RoutingSearchParameters& search_parameters) {
5626   if (search_parameters.log_search()) {
5627     Solver::SearchLogParameters search_log_parameters;
5628     search_log_parameters.branch_period = 10000;
5629     search_log_parameters.objective = nullptr;
5630     search_log_parameters.variable = cost_;
5631     search_log_parameters.scaling_factor =
5632         search_parameters.log_cost_scaling_factor();
5633     search_log_parameters.offset = search_parameters.log_cost_offset();
5634     if (!search_parameters.log_tag().empty()) {
5635       const std::string& tag = search_parameters.log_tag();
5636       search_log_parameters.display_callback = [tag]() { return tag; };
5637     } else {
5638       search_log_parameters.display_callback = nullptr;
5639     }
5640     search_log_parameters.display_on_new_solutions_only = false;
5641     monitors_.push_back(solver_->MakeSearchLog(search_log_parameters));
5642   }
5643 }
5644 
SetupImprovementLimit(const RoutingSearchParameters & search_parameters)5645 void RoutingModel::SetupImprovementLimit(
5646     const RoutingSearchParameters& search_parameters) {
5647   if (search_parameters.has_improvement_limit_parameters()) {
5648     monitors_.push_back(solver_->MakeImprovementLimit(
5649         cost_, /*maximize=*/false, search_parameters.log_cost_scaling_factor(),
5650         search_parameters.log_cost_offset(),
5651         search_parameters.improvement_limit_parameters()
5652             .improvement_rate_coefficient(),
5653         search_parameters.improvement_limit_parameters()
5654             .improvement_rate_solutions_distance()));
5655   }
5656 }
5657 
SetupSearchMonitors(const RoutingSearchParameters & search_parameters)5658 void RoutingModel::SetupSearchMonitors(
5659     const RoutingSearchParameters& search_parameters) {
5660   monitors_.push_back(GetOrCreateLimit());
5661   SetupImprovementLimit(search_parameters);
5662   SetupMetaheuristics(search_parameters);
5663   SetupAssignmentCollector(search_parameters);
5664   SetupTrace(search_parameters);
5665 }
5666 
UsesLightPropagation(const RoutingSearchParameters & search_parameters) const5667 bool RoutingModel::UsesLightPropagation(
5668     const RoutingSearchParameters& search_parameters) const {
5669   return !search_parameters.use_full_propagation() &&
5670          !search_parameters.use_depth_first_search() &&
5671          search_parameters.first_solution_strategy() !=
5672              FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE;
5673 }
5674 
AddWeightedVariableMinimizedByFinalizer(IntVar * var,int64_t cost)5675 void RoutingModel::AddWeightedVariableMinimizedByFinalizer(IntVar* var,
5676                                                            int64_t cost) {
5677   CHECK(var != nullptr);
5678   const int index = gtl::LookupOrInsert(&finalizer_variable_cost_index_, var,
5679                                         finalizer_variable_cost_pairs_.size());
5680   if (index < finalizer_variable_cost_pairs_.size()) {
5681     const int64_t old_cost = finalizer_variable_cost_pairs_[index].second;
5682     finalizer_variable_cost_pairs_[index].second = CapAdd(old_cost, cost);
5683   } else {
5684     finalizer_variable_cost_pairs_.emplace_back(var, cost);
5685   }
5686 }
5687 
AddVariableTargetToFinalizer(IntVar * var,int64_t target)5688 void RoutingModel::AddVariableTargetToFinalizer(IntVar* var, int64_t target) {
5689   CHECK(var != nullptr);
5690   if (finalizer_variable_target_set_.contains(var)) return;
5691   finalizer_variable_target_set_.insert(var);
5692   finalizer_variable_target_pairs_.emplace_back(var, target);
5693 }
5694 
AddVariableMaximizedByFinalizer(IntVar * var)5695 void RoutingModel::AddVariableMaximizedByFinalizer(IntVar* var) {
5696   AddVariableTargetToFinalizer(var, std::numeric_limits<int64_t>::max());
5697 }
5698 
AddVariableMinimizedByFinalizer(IntVar * var)5699 void RoutingModel::AddVariableMinimizedByFinalizer(IntVar* var) {
5700   AddVariableTargetToFinalizer(var, std::numeric_limits<int64_t>::min());
5701 }
5702 
SetupSearch(const RoutingSearchParameters & search_parameters)5703 void RoutingModel::SetupSearch(
5704     const RoutingSearchParameters& search_parameters) {
5705   SetupDecisionBuilders(search_parameters);
5706   SetupSearchMonitors(search_parameters);
5707 }
5708 
AddToAssignment(IntVar * const var)5709 void RoutingModel::AddToAssignment(IntVar* const var) {
5710   extra_vars_.push_back(var);
5711 }
5712 
AddIntervalToAssignment(IntervalVar * const interval)5713 void RoutingModel::AddIntervalToAssignment(IntervalVar* const interval) {
5714   extra_intervals_.push_back(interval);
5715 }
5716 
5717 namespace {
5718 
5719 class PathSpansAndTotalSlacks : public Constraint {
5720  public:
PathSpansAndTotalSlacks(const RoutingModel * model,const RoutingDimension * dimension,std::vector<IntVar * > spans,std::vector<IntVar * > total_slacks)5721   PathSpansAndTotalSlacks(const RoutingModel* model,
5722                           const RoutingDimension* dimension,
5723                           std::vector<IntVar*> spans,
5724                           std::vector<IntVar*> total_slacks)
5725       : Constraint(model->solver()),
5726         model_(model),
5727         dimension_(dimension),
5728         spans_(std::move(spans)),
5729         total_slacks_(std::move(total_slacks)) {
5730     CHECK_EQ(spans_.size(), model_->vehicles());
5731     CHECK_EQ(total_slacks_.size(), model_->vehicles());
5732     vehicle_demons_.resize(model_->vehicles());
5733   }
5734 
DebugString() const5735   std::string DebugString() const override { return "PathSpansAndTotalSlacks"; }
5736 
Post()5737   void Post() override {
5738     const int num_nodes = model_->VehicleVars().size();
5739     const int num_transits = model_->Nexts().size();
5740     for (int node = 0; node < num_nodes; ++node) {
5741       auto* demon = MakeConstraintDemon1(
5742           model_->solver(), this, &PathSpansAndTotalSlacks::PropagateNode,
5743           "PathSpansAndTotalSlacks::PropagateNode", node);
5744       dimension_->CumulVar(node)->WhenRange(demon);
5745       model_->VehicleVar(node)->WhenBound(demon);
5746       if (node < num_transits) {
5747         dimension_->TransitVar(node)->WhenRange(demon);
5748         dimension_->FixedTransitVar(node)->WhenBound(demon);
5749         model_->NextVar(node)->WhenBound(demon);
5750       }
5751     }
5752     for (int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5753       if (!spans_[vehicle] && !total_slacks_[vehicle]) continue;
5754       auto* demon = MakeDelayedConstraintDemon1(
5755           solver(), this, &PathSpansAndTotalSlacks::PropagateVehicle,
5756           "PathSpansAndTotalSlacks::PropagateVehicle", vehicle);
5757       vehicle_demons_[vehicle] = demon;
5758       if (spans_[vehicle]) spans_[vehicle]->WhenRange(demon);
5759       if (total_slacks_[vehicle]) total_slacks_[vehicle]->WhenRange(demon);
5760       if (dimension_->HasBreakConstraints()) {
5761         for (IntervalVar* b : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5762           b->WhenAnything(demon);
5763         }
5764       }
5765     }
5766   }
5767 
5768   // Call propagator on all vehicles.
InitialPropagate()5769   void InitialPropagate() override {
5770     for (int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5771       if (!spans_[vehicle] && !total_slacks_[vehicle]) continue;
5772       PropagateVehicle(vehicle);
5773     }
5774   }
5775 
5776  private:
5777   // Called when a path/dimension variables of the node changes,
5778   // this delays propagator calls until path variables (Next and VehicleVar)
5779   // are instantiated, which saves fruitless and multiple identical calls.
PropagateNode(int node)5780   void PropagateNode(int node) {
5781     if (!model_->VehicleVar(node)->Bound()) return;
5782     const int vehicle = model_->VehicleVar(node)->Min();
5783     if (vehicle < 0 || vehicle_demons_[vehicle] == nullptr) return;
5784     EnqueueDelayedDemon(vehicle_demons_[vehicle]);
5785   }
5786 
5787   // In order to make reasoning on span and total_slack of a vehicle uniform,
5788   // we rely on the fact that span == sum_fixed_transits + total_slack
5789   // to present both span and total_slack in terms of span and fixed transit.
5790   // This allows to use the same code whether there actually are variables
5791   // for span and total_slack or not.
SpanMin(int vehicle,int64_t sum_fixed_transits)5792   int64_t SpanMin(int vehicle, int64_t sum_fixed_transits) {
5793     DCHECK_GE(sum_fixed_transits, 0);
5794     const int64_t span_min = spans_[vehicle]
5795                                  ? spans_[vehicle]->Min()
5796                                  : std::numeric_limits<int64_t>::max();
5797     const int64_t total_slack_min = total_slacks_[vehicle]
5798                                         ? total_slacks_[vehicle]->Min()
5799                                         : std::numeric_limits<int64_t>::max();
5800     return std::min(span_min, CapAdd(total_slack_min, sum_fixed_transits));
5801   }
SpanMax(int vehicle,int64_t sum_fixed_transits)5802   int64_t SpanMax(int vehicle, int64_t sum_fixed_transits) {
5803     DCHECK_GE(sum_fixed_transits, 0);
5804     const int64_t span_max = spans_[vehicle]
5805                                  ? spans_[vehicle]->Max()
5806                                  : std::numeric_limits<int64_t>::min();
5807     const int64_t total_slack_max = total_slacks_[vehicle]
5808                                         ? total_slacks_[vehicle]->Max()
5809                                         : std::numeric_limits<int64_t>::min();
5810     return std::max(span_max, CapAdd(total_slack_max, sum_fixed_transits));
5811   }
SetSpanMin(int vehicle,int64_t min,int64_t sum_fixed_transits)5812   void SetSpanMin(int vehicle, int64_t min, int64_t sum_fixed_transits) {
5813     DCHECK_GE(sum_fixed_transits, 0);
5814     if (spans_[vehicle]) {
5815       spans_[vehicle]->SetMin(min);
5816     }
5817     if (total_slacks_[vehicle]) {
5818       total_slacks_[vehicle]->SetMin(CapSub(min, sum_fixed_transits));
5819     }
5820   }
SetSpanMax(int vehicle,int64_t max,int64_t sum_fixed_transits)5821   void SetSpanMax(int vehicle, int64_t max, int64_t sum_fixed_transits) {
5822     DCHECK_GE(sum_fixed_transits, 0);
5823     if (spans_[vehicle]) {
5824       spans_[vehicle]->SetMax(max);
5825     }
5826     if (total_slacks_[vehicle]) {
5827       total_slacks_[vehicle]->SetMax(CapSub(max, sum_fixed_transits));
5828     }
5829   }
5830   // Propagates span == sum_fixed_transits + total_slack.
5831   // This should be called at least once during PropagateVehicle().
SynchronizeSpanAndTotalSlack(int vehicle,int64_t sum_fixed_transits)5832   void SynchronizeSpanAndTotalSlack(int vehicle, int64_t sum_fixed_transits) {
5833     DCHECK_GE(sum_fixed_transits, 0);
5834     IntVar* span = spans_[vehicle];
5835     IntVar* total_slack = total_slacks_[vehicle];
5836     if (!span || !total_slack) return;
5837     span->SetMin(CapAdd(total_slack->Min(), sum_fixed_transits));
5838     span->SetMax(CapAdd(total_slack->Max(), sum_fixed_transits));
5839     total_slack->SetMin(CapSub(span->Min(), sum_fixed_transits));
5840     total_slack->SetMax(CapSub(span->Max(), sum_fixed_transits));
5841   }
5842 
PropagateVehicle(int vehicle)5843   void PropagateVehicle(int vehicle) {
5844     DCHECK(spans_[vehicle] || total_slacks_[vehicle]);
5845     const int start = model_->Start(vehicle);
5846     const int end = model_->End(vehicle);
5847     // Record path, if it is not fixed from start to end, stop here.
5848     // TRICKY: do not put end node yet, we look only at transits in the next
5849     // reasonings, we will append the end when we look at cumuls.
5850     {
5851       path_.clear();
5852       int curr_node = start;
5853       while (!model_->IsEnd(curr_node)) {
5854         const IntVar* next_var = model_->NextVar(curr_node);
5855         if (!next_var->Bound()) return;
5856         path_.push_back(curr_node);
5857         curr_node = next_var->Value();
5858       }
5859     }
5860     // Compute the sum of fixed transits. Fixed transit variables should all be
5861     // fixed, otherwise we wait to get called later when propagation does it.
5862     int64_t sum_fixed_transits = 0;
5863     for (const int node : path_) {
5864       const IntVar* fixed_transit_var = dimension_->FixedTransitVar(node);
5865       if (!fixed_transit_var->Bound()) return;
5866       sum_fixed_transits =
5867           CapAdd(sum_fixed_transits, fixed_transit_var->Value());
5868     }
5869 
5870     SynchronizeSpanAndTotalSlack(vehicle, sum_fixed_transits);
5871 
5872     // The amount of break time that must occur during the route must be smaller
5873     // than span max - sum_fixed_transits. A break must occur on the route if it
5874     // must be after the route's start and before the route's end.
5875     // Propagate lower bound on span, then filter out values
5876     // that would force more breaks in route than possible.
5877     if (dimension_->HasBreakConstraints() &&
5878         !dimension_->GetBreakIntervalsOfVehicle(vehicle).empty()) {
5879       const int64_t vehicle_start_max = dimension_->CumulVar(start)->Max();
5880       const int64_t vehicle_end_min = dimension_->CumulVar(end)->Min();
5881       // Compute and propagate lower bound.
5882       int64_t min_break_duration = 0;
5883       for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5884         if (!br->MustBePerformed()) continue;
5885         if (vehicle_start_max < br->EndMin() &&
5886             br->StartMax() < vehicle_end_min) {
5887           min_break_duration = CapAdd(min_break_duration, br->DurationMin());
5888         }
5889       }
5890       SetSpanMin(vehicle, CapAdd(min_break_duration, sum_fixed_transits),
5891                  sum_fixed_transits);
5892       // If a break that is not inside the route may violate slack_max,
5893       // we can propagate in some cases: when the break must be before or
5894       // must be after the route.
5895       // In the other cases, we cannot deduce a better bound on a CumulVar or
5896       // on a break, so we do nothing.
5897       const int64_t slack_max =
5898           CapSub(SpanMax(vehicle, sum_fixed_transits), sum_fixed_transits);
5899       const int64_t max_additional_slack =
5900           CapSub(slack_max, min_break_duration);
5901       for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5902         if (!br->MustBePerformed()) continue;
5903         // Break must be before end, detect whether it must be before start.
5904         if (vehicle_start_max >= br->EndMin() &&
5905             br->StartMax() < vehicle_end_min) {
5906           if (br->DurationMin() > max_additional_slack) {
5907             // Having the break inside would violate max_additional_slack..
5908             // Thus, it must be outside the route, in this case, before.
5909             br->SetEndMax(vehicle_start_max);
5910             dimension_->CumulVar(start)->SetMin(br->EndMin());
5911           }
5912         }
5913         // Break must be after start, detect whether it must be after end.
5914         // Same reasoning, in the case where the break is after.
5915         if (vehicle_start_max < br->EndMin() &&
5916             br->StartMax() >= vehicle_end_min) {
5917           if (br->DurationMin() > max_additional_slack) {
5918             br->SetStartMin(vehicle_end_min);
5919             dimension_->CumulVar(end)->SetMax(br->StartMax());
5920           }
5921         }
5922       }
5923     }
5924 
5925     // Propagate span == cumul(end) - cumul(start).
5926     {
5927       IntVar* start_cumul = dimension_->CumulVar(start);
5928       IntVar* end_cumul = dimension_->CumulVar(end);
5929       const int64_t start_min = start_cumul->Min();
5930       const int64_t start_max = start_cumul->Max();
5931       const int64_t end_min = end_cumul->Min();
5932       const int64_t end_max = end_cumul->Max();
5933       // Propagate from cumuls to span.
5934       const int64_t span_lb = CapSub(end_min, start_max);
5935       SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5936       const int64_t span_ub = CapSub(end_max, start_min);
5937       SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5938       // Propagate from span to cumuls.
5939       const int64_t span_min = SpanMin(vehicle, sum_fixed_transits);
5940       const int64_t span_max = SpanMax(vehicle, sum_fixed_transits);
5941       const int64_t slack_from_lb = CapSub(span_max, span_lb);
5942       const int64_t slack_from_ub = CapSub(span_ub, span_min);
5943       // start >= start_max - (span_max - span_lb).
5944       start_cumul->SetMin(CapSub(start_max, slack_from_lb));
5945       // end <= end_min + (span_max - span_lb).
5946       end_cumul->SetMax(CapAdd(end_min, slack_from_lb));
5947       // // start <= start_min + (span_ub - span_min)
5948       start_cumul->SetMax(CapAdd(start_min, slack_from_ub));
5949       // // end >= end_max - (span_ub - span_min)
5950       end_cumul->SetMin(CapSub(end_max, slack_from_ub));
5951     }
5952 
5953     // Propagate sum transits == span.
5954     {
5955       // Propagate from transits to span.
5956       int64_t span_lb = 0;
5957       int64_t span_ub = 0;
5958       for (const int node : path_) {
5959         span_lb = CapAdd(span_lb, dimension_->TransitVar(node)->Min());
5960         span_ub = CapAdd(span_ub, dimension_->TransitVar(node)->Max());
5961       }
5962       SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5963       SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5964       // Propagate from span to transits.
5965       // transit[i] <= transit_i_min + (span_max - span_lb)
5966       // transit[i] >= transit_i_max - (span_ub - span_min)
5967       const int64_t span_min = SpanMin(vehicle, sum_fixed_transits);
5968       const int64_t span_max = SpanMax(vehicle, sum_fixed_transits);
5969       const int64_t slack_from_lb = CapSub(span_max, span_lb);
5970       const int64_t slack_from_ub =
5971           span_ub < std::numeric_limits<int64_t>::max()
5972               ? CapSub(span_ub, span_min)
5973               : std::numeric_limits<int64_t>::max();
5974       for (const int node : path_) {
5975         IntVar* transit_var = dimension_->TransitVar(node);
5976         const int64_t transit_i_min = transit_var->Min();
5977         const int64_t transit_i_max = transit_var->Max();
5978         // TRICKY: the first propagation might change transit_var->Max(),
5979         // but we must use the same value of transit_i_max in the computation
5980         // of transit[i]'s lower bound that was used for span_ub.
5981         transit_var->SetMax(CapAdd(transit_i_min, slack_from_lb));
5982         transit_var->SetMin(CapSub(transit_i_max, slack_from_ub));
5983       }
5984     }
5985 
5986     // TRICKY: add end node now, we will look at cumuls.
5987     path_.push_back(end);
5988 
5989     // A stronger bound: from start min of the route, go to node i+1 with time
5990     // max(cumul[i] + fixed_transit, cumul[i+1].Min()).
5991     // Record arrival time (should be the same as end cumul min).
5992     // Then do the reverse route, going to time
5993     // min(cumul[i+1] - fixed_transit, cumul[i].Max())
5994     // Record final time as departure time.
5995     // Then arrival time - departure time is a valid lower bound of span.
5996     // First reasoning: start - end - start
5997     {
5998       int64_t arrival_time = dimension_->CumulVar(start)->Min();
5999       for (int i = 1; i < path_.size(); ++i) {
6000         arrival_time =
6001             std::max(CapAdd(arrival_time,
6002                             dimension_->FixedTransitVar(path_[i - 1])->Min()),
6003                      dimension_->CumulVar(path_[i])->Min());
6004       }
6005       int64_t departure_time = arrival_time;
6006       for (int i = path_.size() - 2; i >= 0; --i) {
6007         departure_time =
6008             std::min(CapSub(departure_time,
6009                             dimension_->FixedTransitVar(path_[i])->Min()),
6010                      dimension_->CumulVar(path_[i])->Max());
6011       }
6012       const int64_t span_lb = CapSub(arrival_time, departure_time);
6013       SetSpanMin(vehicle, span_lb, sum_fixed_transits);
6014       const int64_t maximum_deviation =
6015           CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
6016       const int64_t start_lb = CapSub(departure_time, maximum_deviation);
6017       dimension_->CumulVar(start)->SetMin(start_lb);
6018     }
6019     // Second reasoning: end - start - end
6020     {
6021       int64_t departure_time = dimension_->CumulVar(end)->Max();
6022       for (int i = path_.size() - 2; i >= 0; --i) {
6023         const int curr_node = path_[i];
6024         departure_time =
6025             std::min(CapSub(departure_time,
6026                             dimension_->FixedTransitVar(curr_node)->Min()),
6027                      dimension_->CumulVar(curr_node)->Max());
6028       }
6029       int arrival_time = departure_time;
6030       for (int i = 1; i < path_.size(); ++i) {
6031         arrival_time =
6032             std::max(CapAdd(arrival_time,
6033                             dimension_->FixedTransitVar(path_[i - 1])->Min()),
6034                      dimension_->CumulVar(path_[i])->Min());
6035       }
6036       const int64_t span_lb = CapSub(arrival_time, departure_time);
6037       SetSpanMin(vehicle, span_lb, sum_fixed_transits);
6038       const int64_t maximum_deviation =
6039           CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
6040       dimension_->CumulVar(end)->SetMax(
6041           CapAdd(arrival_time, maximum_deviation));
6042     }
6043   }
6044 
6045   const RoutingModel* const model_;
6046   const RoutingDimension* const dimension_;
6047   std::vector<IntVar*> spans_;
6048   std::vector<IntVar*> total_slacks_;
6049   std::vector<int> path_;
6050   std::vector<Demon*> vehicle_demons_;
6051 };
6052 
6053 }  // namespace
6054 
MakePathSpansAndTotalSlacks(const RoutingDimension * dimension,std::vector<IntVar * > spans,std::vector<IntVar * > total_slacks)6055 Constraint* RoutingModel::MakePathSpansAndTotalSlacks(
6056     const RoutingDimension* dimension, std::vector<IntVar*> spans,
6057     std::vector<IntVar*> total_slacks) {
6058   CHECK_EQ(vehicles_, spans.size());
6059   CHECK_EQ(vehicles_, total_slacks.size());
6060   return solver()->RevAlloc(
6061       new PathSpansAndTotalSlacks(this, dimension, spans, total_slacks));
6062 }
6063 
6064 const char RoutingModelVisitor::kLightElement[] = "LightElement";
6065 const char RoutingModelVisitor::kLightElement2[] = "LightElement2";
6066 const char RoutingModelVisitor::kRemoveValues[] = "RemoveValues";
6067 
RoutingDimension(RoutingModel * model,std::vector<int64_t> vehicle_capacities,const std::string & name,const RoutingDimension * base_dimension)6068 RoutingDimension::RoutingDimension(RoutingModel* model,
6069                                    std::vector<int64_t> vehicle_capacities,
6070                                    const std::string& name,
6071                                    const RoutingDimension* base_dimension)
6072     : vehicle_capacities_(std::move(vehicle_capacities)),
6073       base_dimension_(base_dimension),
6074       global_span_cost_coefficient_(0),
6075       model_(model),
6076       name_(name),
6077       global_optimizer_offset_(0) {
6078   CHECK(model != nullptr);
6079   vehicle_span_upper_bounds_.assign(model->vehicles(),
6080                                     std::numeric_limits<int64_t>::max());
6081   vehicle_span_cost_coefficients_.assign(model->vehicles(), 0);
6082 }
6083 
RoutingDimension(RoutingModel * model,std::vector<int64_t> vehicle_capacities,const std::string & name,SelfBased)6084 RoutingDimension::RoutingDimension(RoutingModel* model,
6085                                    std::vector<int64_t> vehicle_capacities,
6086                                    const std::string& name, SelfBased)
6087     : RoutingDimension(model, std::move(vehicle_capacities), name, this) {}
6088 
~RoutingDimension()6089 RoutingDimension::~RoutingDimension() {
6090   cumul_var_piecewise_linear_cost_.clear();
6091 }
6092 
Initialize(const std::vector<int> & transit_evaluators,const std::vector<int> & state_dependent_transit_evaluators,int64_t slack_max)6093 void RoutingDimension::Initialize(
6094     const std::vector<int>& transit_evaluators,
6095     const std::vector<int>& state_dependent_transit_evaluators,
6096     int64_t slack_max) {
6097   InitializeCumuls();
6098   InitializeTransits(transit_evaluators, state_dependent_transit_evaluators,
6099                      slack_max);
6100 }
6101 
6102 namespace {
6103 // Very light version of the RangeLessOrEqual constraint (see ./range_cst.cc).
6104 // Only performs initial propagation and then checks the compatibility of the
6105 // variable domains without domain pruning.
6106 // This is useful when to avoid ping-pong effects with costly constraints
6107 // such as the PathCumul constraint.
6108 // This constraint has not been added to the cp library (in range_cst.cc) given
6109 // it only does checking and no propagation (except the initial propagation)
6110 // and is only fit for local search, in particular in the context of vehicle
6111 // routing.
6112 class LightRangeLessOrEqual : public Constraint {
6113  public:
6114   LightRangeLessOrEqual(Solver* const s, IntExpr* const l, IntExpr* const r);
~LightRangeLessOrEqual()6115   ~LightRangeLessOrEqual() override {}
6116   void Post() override;
6117   void InitialPropagate() override;
6118   std::string DebugString() const override;
Var()6119   IntVar* Var() override {
6120     return solver()->MakeIsLessOrEqualVar(left_, right_);
6121   }
6122   // TODO(user): introduce a kLightLessOrEqual tag.
Accept(ModelVisitor * const visitor) const6123   void Accept(ModelVisitor* const visitor) const override {
6124     visitor->BeginVisitConstraint(ModelVisitor::kLessOrEqual, this);
6125     visitor->VisitIntegerExpressionArgument(ModelVisitor::kLeftArgument, left_);
6126     visitor->VisitIntegerExpressionArgument(ModelVisitor::kRightArgument,
6127                                             right_);
6128     visitor->EndVisitConstraint(ModelVisitor::kLessOrEqual, this);
6129   }
6130 
6131  private:
6132   void CheckRange();
6133 
6134   IntExpr* const left_;
6135   IntExpr* const right_;
6136   Demon* demon_;
6137 };
6138 
LightRangeLessOrEqual(Solver * const s,IntExpr * const l,IntExpr * const r)6139 LightRangeLessOrEqual::LightRangeLessOrEqual(Solver* const s, IntExpr* const l,
6140                                              IntExpr* const r)
6141     : Constraint(s), left_(l), right_(r), demon_(nullptr) {}
6142 
Post()6143 void LightRangeLessOrEqual::Post() {
6144   demon_ = MakeConstraintDemon0(
6145       solver(), this, &LightRangeLessOrEqual::CheckRange, "CheckRange");
6146   left_->WhenRange(demon_);
6147   right_->WhenRange(demon_);
6148 }
6149 
InitialPropagate()6150 void LightRangeLessOrEqual::InitialPropagate() {
6151   left_->SetMax(right_->Max());
6152   right_->SetMin(left_->Min());
6153   if (left_->Max() <= right_->Min()) {
6154     demon_->inhibit(solver());
6155   }
6156 }
6157 
CheckRange()6158 void LightRangeLessOrEqual::CheckRange() {
6159   if (left_->Min() > right_->Max()) {
6160     solver()->Fail();
6161   }
6162   if (left_->Max() <= right_->Min()) {
6163     demon_->inhibit(solver());
6164   }
6165 }
6166 
DebugString() const6167 std::string LightRangeLessOrEqual::DebugString() const {
6168   return left_->DebugString() + " < " + right_->DebugString();
6169 }
6170 
6171 }  // namespace
6172 
InitializeCumuls()6173 void RoutingDimension::InitializeCumuls() {
6174   Solver* const solver = model_->solver();
6175   const int size = model_->Size() + model_->vehicles();
6176   const auto capacity_range = std::minmax_element(vehicle_capacities_.begin(),
6177                                                   vehicle_capacities_.end());
6178   const int64_t min_capacity = *capacity_range.first;
6179   CHECK_GE(min_capacity, 0);
6180   const int64_t max_capacity = *capacity_range.second;
6181   solver->MakeIntVarArray(size, 0, max_capacity, name_, &cumuls_);
6182   // Refine the min/max for vehicle start/ends based on vehicle capacities.
6183   for (int v = 0; v < model_->vehicles(); v++) {
6184     const int64_t vehicle_capacity = vehicle_capacities_[v];
6185     cumuls_[model_->Start(v)]->SetMax(vehicle_capacity);
6186     cumuls_[model_->End(v)]->SetMax(vehicle_capacity);
6187   }
6188 
6189   forbidden_intervals_.resize(size);
6190   capacity_vars_.clear();
6191   if (min_capacity != max_capacity) {
6192     solver->MakeIntVarArray(size, 0, std::numeric_limits<int64_t>::max(),
6193                             &capacity_vars_);
6194     for (int i = 0; i < size; ++i) {
6195       IntVar* const capacity_var = capacity_vars_[i];
6196       if (i < model_->Size()) {
6197         IntVar* const capacity_active = solver->MakeBoolVar();
6198         solver->AddConstraint(
6199             solver->MakeLessOrEqual(model_->ActiveVar(i), capacity_active));
6200         solver->AddConstraint(solver->MakeIsLessOrEqualCt(
6201             cumuls_[i], capacity_var, capacity_active));
6202       } else {
6203         solver->AddConstraint(
6204             solver->MakeLessOrEqual(cumuls_[i], capacity_var));
6205       }
6206     }
6207   }
6208 }
6209 
6210 namespace {
6211 template <int64_t value>
IthElementOrValue(const std::vector<int64_t> & v,int64_t index)6212 int64_t IthElementOrValue(const std::vector<int64_t>& v, int64_t index) {
6213   return index >= 0 ? v[index] : value;
6214 }
6215 
ComputeTransitClasses(const std::vector<int> & evaluator_indices,std::vector<int> * class_evaluators,std::vector<int64_t> * vehicle_to_class)6216 void ComputeTransitClasses(const std::vector<int>& evaluator_indices,
6217                            std::vector<int>* class_evaluators,
6218                            std::vector<int64_t>* vehicle_to_class) {
6219   CHECK(class_evaluators != nullptr);
6220   CHECK(vehicle_to_class != nullptr);
6221   class_evaluators->clear();
6222   vehicle_to_class->resize(evaluator_indices.size(), -1);
6223   absl::flat_hash_map<int, int64_t> evaluator_to_class;
6224   for (int i = 0; i < evaluator_indices.size(); ++i) {
6225     const int evaluator_index = evaluator_indices[i];
6226     int evaluator_class = -1;
6227     if (!gtl::FindCopy(evaluator_to_class, evaluator_index, &evaluator_class)) {
6228       evaluator_class = class_evaluators->size();
6229       evaluator_to_class[evaluator_index] = evaluator_class;
6230       class_evaluators->push_back(evaluator_index);
6231     }
6232     (*vehicle_to_class)[i] = evaluator_class;
6233   }
6234 }
6235 }  // namespace
6236 
InitializeTransitVariables(int64_t slack_max)6237 void RoutingDimension::InitializeTransitVariables(int64_t slack_max) {
6238   CHECK(!class_evaluators_.empty());
6239   CHECK(base_dimension_ == nullptr ||
6240         !state_dependent_class_evaluators_.empty());
6241 
6242   Solver* const solver = model_->solver();
6243   const int size = model_->Size();
6244   const Solver::IndexEvaluator1 dependent_vehicle_class_function =
6245       [this](int index) {
6246         return (0 <= index && index < state_dependent_vehicle_to_class_.size())
6247                    ? state_dependent_vehicle_to_class_[index]
6248                    : state_dependent_class_evaluators_.size();
6249       };
6250   const std::string slack_name = name_ + " slack";
6251   const std::string transit_name = name_ + " fixed transit";
6252 
6253   for (int64_t i = 0; i < size; ++i) {
6254     fixed_transits_[i] = solver->MakeIntVar(std::numeric_limits<int64_t>::min(),
6255                                             std::numeric_limits<int64_t>::max(),
6256                                             absl::StrCat(transit_name, i));
6257     // Setting dependent_transits_[i].
6258     if (base_dimension_ != nullptr) {
6259       if (state_dependent_class_evaluators_.size() == 1) {
6260         std::vector<IntVar*> transition_variables(cumuls_.size(), nullptr);
6261         for (int64_t j = 0; j < cumuls_.size(); ++j) {
6262           transition_variables[j] =
6263               MakeRangeMakeElementExpr(
6264                   model_
6265                       ->StateDependentTransitCallback(
6266                           state_dependent_class_evaluators_[0])(i, j)
6267                       .transit,
6268                   base_dimension_->CumulVar(i), solver)
6269                   ->Var();
6270         }
6271         dependent_transits_[i] =
6272             solver->MakeElement(transition_variables, model_->NextVar(i))
6273                 ->Var();
6274       } else {
6275         IntVar* const vehicle_class_var =
6276             solver
6277                 ->MakeElement(dependent_vehicle_class_function,
6278                               model_->VehicleVar(i))
6279                 ->Var();
6280         std::vector<IntVar*> transit_for_vehicle;
6281         transit_for_vehicle.reserve(state_dependent_class_evaluators_.size() +
6282                                     1);
6283         for (int evaluator : state_dependent_class_evaluators_) {
6284           std::vector<IntVar*> transition_variables(cumuls_.size(), nullptr);
6285           for (int64_t j = 0; j < cumuls_.size(); ++j) {
6286             transition_variables[j] =
6287                 MakeRangeMakeElementExpr(
6288                     model_->StateDependentTransitCallback(evaluator)(i, j)
6289                         .transit,
6290                     base_dimension_->CumulVar(i), solver)
6291                     ->Var();
6292           }
6293           transit_for_vehicle.push_back(
6294               solver->MakeElement(transition_variables, model_->NextVar(i))
6295                   ->Var());
6296         }
6297         transit_for_vehicle.push_back(solver->MakeIntConst(0));
6298         dependent_transits_[i] =
6299             solver->MakeElement(transit_for_vehicle, vehicle_class_var)->Var();
6300       }
6301     } else {
6302       dependent_transits_[i] = solver->MakeIntConst(0);
6303     }
6304 
6305     // Summing fixed transits, dependent transits and the slack.
6306     IntExpr* transit_expr = fixed_transits_[i];
6307     if (dependent_transits_[i]->Min() != 0 ||
6308         dependent_transits_[i]->Max() != 0) {
6309       transit_expr = solver->MakeSum(transit_expr, dependent_transits_[i]);
6310     }
6311 
6312     if (slack_max == 0) {
6313       slacks_[i] = solver->MakeIntConst(0);
6314     } else {
6315       slacks_[i] =
6316           solver->MakeIntVar(0, slack_max, absl::StrCat(slack_name, i));
6317       transit_expr = solver->MakeSum(slacks_[i], transit_expr);
6318     }
6319     transits_[i] = transit_expr->Var();
6320   }
6321 }
6322 
InitializeTransits(const std::vector<int> & transit_evaluators,const std::vector<int> & state_dependent_transit_evaluators,int64_t slack_max)6323 void RoutingDimension::InitializeTransits(
6324     const std::vector<int>& transit_evaluators,
6325     const std::vector<int>& state_dependent_transit_evaluators,
6326     int64_t slack_max) {
6327   CHECK_EQ(model_->vehicles(), transit_evaluators.size());
6328   CHECK(base_dimension_ == nullptr ||
6329         model_->vehicles() == state_dependent_transit_evaluators.size());
6330   const int size = model_->Size();
6331   transits_.resize(size, nullptr);
6332   fixed_transits_.resize(size, nullptr);
6333   slacks_.resize(size, nullptr);
6334   dependent_transits_.resize(size, nullptr);
6335   ComputeTransitClasses(transit_evaluators, &class_evaluators_,
6336                         &vehicle_to_class_);
6337   if (base_dimension_ != nullptr) {
6338     ComputeTransitClasses(state_dependent_transit_evaluators,
6339                           &state_dependent_class_evaluators_,
6340                           &state_dependent_vehicle_to_class_);
6341   }
6342 
6343   InitializeTransitVariables(slack_max);
6344 }
6345 
FillPathEvaluation(const std::vector<int64_t> & path,const RoutingModel::TransitCallback2 & evaluator,std::vector<int64_t> * values)6346 void FillPathEvaluation(const std::vector<int64_t>& path,
6347                         const RoutingModel::TransitCallback2& evaluator,
6348                         std::vector<int64_t>* values) {
6349   const int num_nodes = path.size();
6350   values->resize(num_nodes - 1);
6351   for (int i = 0; i < num_nodes - 1; ++i) {
6352     (*values)[i] = evaluator(path[i], path[i + 1]);
6353   }
6354 }
6355 
TypeRegulationsChecker(const RoutingModel & model)6356 TypeRegulationsChecker::TypeRegulationsChecker(const RoutingModel& model)
6357     : model_(model), occurrences_of_type_(model.GetNumberOfVisitTypes()) {}
6358 
CheckVehicle(int vehicle,const std::function<int64_t (int64_t)> & next_accessor)6359 bool TypeRegulationsChecker::CheckVehicle(
6360     int vehicle, const std::function<int64_t(int64_t)>& next_accessor) {
6361   if (!HasRegulationsToCheck()) {
6362     return true;
6363   }
6364 
6365   InitializeCheck(vehicle, next_accessor);
6366 
6367   for (int pos = 0; pos < current_route_visits_.size(); pos++) {
6368     const int64_t current_visit = current_route_visits_[pos];
6369     const int type = model_.GetVisitType(current_visit);
6370     if (type < 0) {
6371       continue;
6372     }
6373     const VisitTypePolicy policy = model_.GetVisitTypePolicy(current_visit);
6374 
6375     DCHECK_LT(type, occurrences_of_type_.size());
6376     int& num_type_added = occurrences_of_type_[type].num_type_added_to_vehicle;
6377     int& num_type_removed =
6378         occurrences_of_type_[type].num_type_removed_from_vehicle;
6379     DCHECK_LE(num_type_removed, num_type_added);
6380     if (policy == RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE &&
6381         num_type_removed == num_type_added) {
6382       // The type is not actually being removed as all added types have already
6383       // been removed.
6384       continue;
6385     }
6386 
6387     if (!CheckTypeRegulations(type, policy, pos)) {
6388       return false;
6389     }
6390     // Update count of type based on the visit policy.
6391     if (policy == VisitTypePolicy::TYPE_ADDED_TO_VEHICLE ||
6392         policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED) {
6393       num_type_added++;
6394     }
6395     if (policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED ||
6396         policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6397       num_type_removed++;
6398     }
6399   }
6400   return FinalizeCheck();
6401 }
6402 
InitializeCheck(int vehicle,const std::function<int64_t (int64_t)> & next_accessor)6403 void TypeRegulationsChecker::InitializeCheck(
6404     int vehicle, const std::function<int64_t(int64_t)>& next_accessor) {
6405   // Accumulates the count of types before the current node.
6406   // {0, 0, -1} does not compile on or-tools.
6407   std::fill(occurrences_of_type_.begin(), occurrences_of_type_.end(),
6408             TypeRegulationsChecker::TypePolicyOccurrence());
6409 
6410   // TODO(user): Optimize the filter to avoid scanning the route an extra
6411   // time when there are no TYPE_ON_VEHICLE_UP_TO_VISIT policies on the route,
6412   // by passing a boolean to CheckVehicle() passed to InitializeCheck().
6413   current_route_visits_.clear();
6414   for (int64_t current = model_.Start(vehicle); !model_.IsEnd(current);
6415        current = next_accessor(current)) {
6416     const int type = model_.GetVisitType(current);
6417     if (type >= 0 && model_.GetVisitTypePolicy(current) ==
6418                          VisitTypePolicy::TYPE_ON_VEHICLE_UP_TO_VISIT) {
6419       occurrences_of_type_[type].position_of_last_type_on_vehicle_up_to_visit =
6420           current_route_visits_.size();
6421     }
6422     current_route_visits_.push_back(current);
6423   }
6424 
6425   OnInitializeCheck();
6426 }
6427 
TypeOccursOnRoute(int type) const6428 bool TypeRegulationsChecker::TypeOccursOnRoute(int type) const {
6429   const TypePolicyOccurrence& occurrences = occurrences_of_type_[type];
6430   return occurrences.num_type_added_to_vehicle > 0 ||
6431          occurrences.position_of_last_type_on_vehicle_up_to_visit >= 0;
6432 }
6433 
TypeCurrentlyOnRoute(int type,int pos) const6434 bool TypeRegulationsChecker::TypeCurrentlyOnRoute(int type, int pos) const {
6435   const TypePolicyOccurrence& occurrences = occurrences_of_type_[type];
6436   return occurrences.num_type_removed_from_vehicle <
6437              occurrences.num_type_added_to_vehicle ||
6438          occurrences.position_of_last_type_on_vehicle_up_to_visit >= pos;
6439 }
6440 
TypeIncompatibilityChecker(const RoutingModel & model,bool check_hard_incompatibilities)6441 TypeIncompatibilityChecker::TypeIncompatibilityChecker(
6442     const RoutingModel& model, bool check_hard_incompatibilities)
6443     : TypeRegulationsChecker(model),
6444       check_hard_incompatibilities_(check_hard_incompatibilities) {}
6445 
HasRegulationsToCheck() const6446 bool TypeIncompatibilityChecker::HasRegulationsToCheck() const {
6447   return model_.HasTemporalTypeIncompatibilities() ||
6448          (check_hard_incompatibilities_ &&
6449           model_.HasHardTypeIncompatibilities());
6450 }
6451 
6452 // TODO(user): Remove the check_hard_incompatibilities_ boolean and always
6453 // check both incompatibilities to simplify the code?
6454 // TODO(user): Improve algorithm by only checking a given type if necessary?
6455 // - For temporal incompatibilities, only check if NonDeliveredType(count) == 1.
6456 // - For hard incompatibilities, only if NonDeliveryType(type) == 1.
CheckTypeRegulations(int type,VisitTypePolicy policy,int pos)6457 bool TypeIncompatibilityChecker::CheckTypeRegulations(int type,
6458                                                       VisitTypePolicy policy,
6459                                                       int pos) {
6460   if (policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6461     // NOTE: We don't need to check incompatibilities when the type is being
6462     // removed from the route.
6463     return true;
6464   }
6465   for (int incompatible_type :
6466        model_.GetTemporalTypeIncompatibilitiesOfType(type)) {
6467     if (TypeCurrentlyOnRoute(incompatible_type, pos)) {
6468       return false;
6469     }
6470   }
6471   if (check_hard_incompatibilities_) {
6472     for (int incompatible_type :
6473          model_.GetHardTypeIncompatibilitiesOfType(type)) {
6474       if (TypeOccursOnRoute(incompatible_type)) {
6475         return false;
6476       }
6477     }
6478   }
6479   return true;
6480 }
6481 
HasRegulationsToCheck() const6482 bool TypeRequirementChecker::HasRegulationsToCheck() const {
6483   return model_.HasTemporalTypeRequirements() ||
6484          model_.HasSameVehicleTypeRequirements();
6485 }
6486 
CheckRequiredTypesCurrentlyOnRoute(const std::vector<absl::flat_hash_set<int>> & required_type_alternatives,int pos)6487 bool TypeRequirementChecker::CheckRequiredTypesCurrentlyOnRoute(
6488     const std::vector<absl::flat_hash_set<int>>& required_type_alternatives,
6489     int pos) {
6490   for (const absl::flat_hash_set<int>& requirement_alternatives :
6491        required_type_alternatives) {
6492     bool has_one_of_alternatives = false;
6493     for (int type_alternative : requirement_alternatives) {
6494       if (TypeCurrentlyOnRoute(type_alternative, pos)) {
6495         has_one_of_alternatives = true;
6496         break;
6497       }
6498     }
6499     if (!has_one_of_alternatives) {
6500       return false;
6501     }
6502   }
6503   return true;
6504 }
6505 
CheckTypeRegulations(int type,VisitTypePolicy policy,int pos)6506 bool TypeRequirementChecker::CheckTypeRegulations(int type,
6507                                                   VisitTypePolicy policy,
6508                                                   int pos) {
6509   if (policy == RoutingModel::TYPE_ADDED_TO_VEHICLE ||
6510       policy == RoutingModel::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED) {
6511     if (!CheckRequiredTypesCurrentlyOnRoute(
6512             model_.GetRequiredTypeAlternativesWhenAddingType(type), pos)) {
6513       return false;
6514     }
6515   }
6516   if (policy != RoutingModel::TYPE_ADDED_TO_VEHICLE) {
6517     if (!CheckRequiredTypesCurrentlyOnRoute(
6518             model_.GetRequiredTypeAlternativesWhenRemovingType(type), pos)) {
6519       return false;
6520     }
6521   }
6522   if (policy != RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE &&
6523       !model_.GetSameVehicleRequiredTypeAlternativesOfType(type).empty()) {
6524     types_with_same_vehicle_requirements_on_route_.insert(type);
6525   }
6526   return true;
6527 }
6528 
FinalizeCheck() const6529 bool TypeRequirementChecker::FinalizeCheck() const {
6530   for (int type : types_with_same_vehicle_requirements_on_route_) {
6531     for (const absl::flat_hash_set<int>& requirement_alternatives :
6532          model_.GetSameVehicleRequiredTypeAlternativesOfType(type)) {
6533       bool has_one_of_alternatives = false;
6534       for (const int type_alternative : requirement_alternatives) {
6535         if (TypeOccursOnRoute(type_alternative)) {
6536           has_one_of_alternatives = true;
6537           break;
6538         }
6539       }
6540       if (!has_one_of_alternatives) {
6541         return false;
6542       }
6543     }
6544   }
6545   return true;
6546 }
6547 
TypeRegulationsConstraint(const RoutingModel & model)6548 TypeRegulationsConstraint::TypeRegulationsConstraint(const RoutingModel& model)
6549     : Constraint(model.solver()),
6550       model_(model),
6551       incompatibility_checker_(model, /*check_hard_incompatibilities*/ true),
6552       requirement_checker_(model),
6553       vehicle_demons_(model.vehicles()) {}
6554 
PropagateNodeRegulations(int node)6555 void TypeRegulationsConstraint::PropagateNodeRegulations(int node) {
6556   DCHECK_LT(node, model_.Size());
6557   if (!model_.VehicleVar(node)->Bound() || !model_.NextVar(node)->Bound()) {
6558     // Vehicle var or Next var not bound.
6559     return;
6560   }
6561   const int vehicle = model_.VehicleVar(node)->Min();
6562   if (vehicle < 0) return;
6563   DCHECK(vehicle_demons_[vehicle] != nullptr);
6564   EnqueueDelayedDemon(vehicle_demons_[vehicle]);
6565 }
6566 
CheckRegulationsOnVehicle(int vehicle)6567 void TypeRegulationsConstraint::CheckRegulationsOnVehicle(int vehicle) {
6568   const auto next_accessor = [this, vehicle](int64_t node) {
6569     if (model_.NextVar(node)->Bound()) {
6570       return model_.NextVar(node)->Value();
6571     }
6572     // Node not bound, skip to the end of the vehicle.
6573     return model_.End(vehicle);
6574   };
6575   if (!incompatibility_checker_.CheckVehicle(vehicle, next_accessor) ||
6576       !requirement_checker_.CheckVehicle(vehicle, next_accessor)) {
6577     model_.solver()->Fail();
6578   }
6579 }
6580 
Post()6581 void TypeRegulationsConstraint::Post() {
6582   for (int vehicle = 0; vehicle < model_.vehicles(); vehicle++) {
6583     vehicle_demons_[vehicle] = MakeDelayedConstraintDemon1(
6584         solver(), this, &TypeRegulationsConstraint::CheckRegulationsOnVehicle,
6585         "CheckRegulationsOnVehicle", vehicle);
6586   }
6587   for (int node = 0; node < model_.Size(); node++) {
6588     Demon* node_demon = MakeConstraintDemon1(
6589         solver(), this, &TypeRegulationsConstraint::PropagateNodeRegulations,
6590         "PropagateNodeRegulations", node);
6591     model_.NextVar(node)->WhenBound(node_demon);
6592     model_.VehicleVar(node)->WhenBound(node_demon);
6593   }
6594 }
6595 
InitialPropagate()6596 void TypeRegulationsConstraint::InitialPropagate() {
6597   for (int vehicle = 0; vehicle < model_.vehicles(); vehicle++) {
6598     CheckRegulationsOnVehicle(vehicle);
6599   }
6600 }
6601 
CloseModel(bool use_light_propagation)6602 void RoutingDimension::CloseModel(bool use_light_propagation) {
6603   Solver* const solver = model_->solver();
6604   const auto capacity_lambda = [this](int64_t vehicle) {
6605     return vehicle >= 0 ? vehicle_capacities_[vehicle]
6606                         : std::numeric_limits<int64_t>::max();
6607   };
6608   for (int i = 0; i < capacity_vars_.size(); ++i) {
6609     IntVar* const vehicle_var = model_->VehicleVar(i);
6610     IntVar* const capacity_var = capacity_vars_[i];
6611     if (use_light_propagation) {
6612       solver->AddConstraint(MakeLightElement(
6613           solver, capacity_var, vehicle_var, capacity_lambda,
6614           [this]() { return model_->enable_deep_serialization_; }));
6615     } else {
6616       solver->AddConstraint(solver->MakeEquality(
6617           capacity_var,
6618           solver->MakeElement(capacity_lambda, vehicle_var)->Var()));
6619     }
6620   }
6621   for (int i = 0; i < fixed_transits_.size(); ++i) {
6622     IntVar* const next_var = model_->NextVar(i);
6623     IntVar* const fixed_transit = fixed_transits_[i];
6624     const auto transit_vehicle_evaluator = [this, i](int64_t to,
6625                                                      int64_t eval_index) {
6626       return eval_index >= 0 ? transit_evaluator(eval_index)(i, to) : 0;
6627     };
6628     if (use_light_propagation) {
6629       if (class_evaluators_.size() == 1) {
6630         const int class_evaluator_index = class_evaluators_[0];
6631         const auto& unary_callback =
6632             model_->UnaryTransitCallbackOrNull(class_evaluator_index);
6633         if (unary_callback == nullptr) {
6634           solver->AddConstraint(MakeLightElement(
6635               solver, fixed_transit, next_var,
6636               [this, i](int64_t to) {
6637                 return model_->TransitCallback(class_evaluators_[0])(i, to);
6638               },
6639               [this]() { return model_->enable_deep_serialization_; }));
6640         } else {
6641           fixed_transit->SetValue(unary_callback(i));
6642         }
6643       } else {
6644         solver->AddConstraint(MakeLightElement2(
6645             solver, fixed_transit, next_var, model_->VehicleVar(i),
6646             transit_vehicle_evaluator,
6647             [this]() { return model_->enable_deep_serialization_; }));
6648       }
6649     } else {
6650       if (class_evaluators_.size() == 1) {
6651         const int class_evaluator_index = class_evaluators_[0];
6652         const auto& unary_callback =
6653             model_->UnaryTransitCallbackOrNull(class_evaluator_index);
6654         if (unary_callback == nullptr) {
6655           solver->AddConstraint(solver->MakeEquality(
6656               fixed_transit, solver
6657                                  ->MakeElement(
6658                                      [this, i](int64_t to) {
6659                                        return model_->TransitCallback(
6660                                            class_evaluators_[0])(i, to);
6661                                      },
6662                                      model_->NextVar(i))
6663                                  ->Var()));
6664         } else {
6665           fixed_transit->SetValue(unary_callback(i));
6666         }
6667       } else {
6668         solver->AddConstraint(solver->MakeEquality(
6669             fixed_transit, solver
6670                                ->MakeElement(transit_vehicle_evaluator,
6671                                              next_var, model_->VehicleVar(i))
6672                                ->Var()));
6673       }
6674     }
6675   }
6676   if (HasBreakConstraints()) {
6677     GlobalVehicleBreaksConstraint* constraint =
6678         model()->solver()->RevAlloc(new GlobalVehicleBreaksConstraint(this));
6679     solver->AddConstraint(constraint);
6680   }
6681 }
6682 
GetTransitValue(int64_t from_index,int64_t to_index,int64_t vehicle) const6683 int64_t RoutingDimension::GetTransitValue(int64_t from_index, int64_t to_index,
6684                                           int64_t vehicle) const {
6685   DCHECK(transit_evaluator(vehicle) != nullptr);
6686   return transit_evaluator(vehicle)(from_index, to_index);
6687 }
6688 
GetAllowedIntervalsInRange(int64_t index,int64_t min_value,int64_t max_value) const6689 SortedDisjointIntervalList RoutingDimension::GetAllowedIntervalsInRange(
6690     int64_t index, int64_t min_value, int64_t max_value) const {
6691   SortedDisjointIntervalList allowed;
6692   const SortedDisjointIntervalList& forbidden = forbidden_intervals_[index];
6693   IntVar* const cumul_var = cumuls_[index];
6694   const int64_t min = std::max(min_value, cumul_var->Min());
6695   const int64_t max = std::min(max_value, cumul_var->Max());
6696   int64_t next_start = min;
6697   for (SortedDisjointIntervalList::Iterator interval =
6698            forbidden.FirstIntervalGreaterOrEqual(min);
6699        interval != forbidden.end(); ++interval) {
6700     if (next_start > max) break;
6701     if (next_start < interval->start) {
6702       allowed.InsertInterval(next_start, CapSub(interval->start, 1));
6703     }
6704     next_start = CapAdd(interval->end, 1);
6705   }
6706   if (next_start <= max) {
6707     allowed.InsertInterval(next_start, max);
6708   }
6709   return allowed;
6710 }
6711 
SetSpanUpperBoundForVehicle(int64_t upper_bound,int vehicle)6712 void RoutingDimension::SetSpanUpperBoundForVehicle(int64_t upper_bound,
6713                                                    int vehicle) {
6714   CHECK_GE(vehicle, 0);
6715   CHECK_LT(vehicle, vehicle_span_upper_bounds_.size());
6716   CHECK_GE(upper_bound, 0);
6717   vehicle_span_upper_bounds_[vehicle] = upper_bound;
6718 }
6719 
SetSpanCostCoefficientForVehicle(int64_t coefficient,int vehicle)6720 void RoutingDimension::SetSpanCostCoefficientForVehicle(int64_t coefficient,
6721                                                         int vehicle) {
6722   CHECK_GE(vehicle, 0);
6723   CHECK_LT(vehicle, vehicle_span_cost_coefficients_.size());
6724   CHECK_GE(coefficient, 0);
6725   vehicle_span_cost_coefficients_[vehicle] = coefficient;
6726 }
6727 
SetSpanCostCoefficientForAllVehicles(int64_t coefficient)6728 void RoutingDimension::SetSpanCostCoefficientForAllVehicles(
6729     int64_t coefficient) {
6730   CHECK_GE(coefficient, 0);
6731   vehicle_span_cost_coefficients_.assign(model_->vehicles(), coefficient);
6732 }
6733 
SetGlobalSpanCostCoefficient(int64_t coefficient)6734 void RoutingDimension::SetGlobalSpanCostCoefficient(int64_t coefficient) {
6735   CHECK_GE(coefficient, 0);
6736   global_span_cost_coefficient_ = coefficient;
6737 }
6738 
SetCumulVarPiecewiseLinearCost(int64_t index,const PiecewiseLinearFunction & cost)6739 void RoutingDimension::SetCumulVarPiecewiseLinearCost(
6740     int64_t index, const PiecewiseLinearFunction& cost) {
6741   if (!cost.IsNonDecreasing()) {
6742     LOG(WARNING) << "Only non-decreasing cost functions are supported.";
6743     return;
6744   }
6745   if (cost.Value(0) < 0) {
6746     LOG(WARNING) << "Only positive cost functions are supported.";
6747     return;
6748   }
6749   if (index >= cumul_var_piecewise_linear_cost_.size()) {
6750     cumul_var_piecewise_linear_cost_.resize(index + 1);
6751   }
6752   PiecewiseLinearCost& piecewise_linear_cost =
6753       cumul_var_piecewise_linear_cost_[index];
6754   piecewise_linear_cost.var = cumuls_[index];
6755   piecewise_linear_cost.cost = absl::make_unique<PiecewiseLinearFunction>(cost);
6756 }
6757 
HasCumulVarPiecewiseLinearCost(int64_t index) const6758 bool RoutingDimension::HasCumulVarPiecewiseLinearCost(int64_t index) const {
6759   return (index < cumul_var_piecewise_linear_cost_.size() &&
6760           cumul_var_piecewise_linear_cost_[index].var != nullptr);
6761 }
6762 
GetCumulVarPiecewiseLinearCost(int64_t index) const6763 const PiecewiseLinearFunction* RoutingDimension::GetCumulVarPiecewiseLinearCost(
6764     int64_t index) const {
6765   if (index < cumul_var_piecewise_linear_cost_.size() &&
6766       cumul_var_piecewise_linear_cost_[index].var != nullptr) {
6767     return cumul_var_piecewise_linear_cost_[index].cost.get();
6768   }
6769   return nullptr;
6770 }
6771 
6772 namespace {
BuildVarFromExprAndIndexActiveState(const RoutingModel * model,IntExpr * expr,int index)6773 IntVar* BuildVarFromExprAndIndexActiveState(const RoutingModel* model,
6774                                             IntExpr* expr, int index) {
6775   Solver* const solver = model->solver();
6776   if (model->IsStart(index) || model->IsEnd(index)) {
6777     const int vehicle = model->VehicleIndex(index);
6778     DCHECK_GE(vehicle, 0);
6779     return solver->MakeProd(expr, model->VehicleRouteConsideredVar(vehicle))
6780         ->Var();
6781   }
6782   return solver->MakeProd(expr, model->ActiveVar(index))->Var();
6783 }
6784 }  // namespace
6785 
SetupCumulVarPiecewiseLinearCosts(std::vector<IntVar * > * cost_elements) const6786 void RoutingDimension::SetupCumulVarPiecewiseLinearCosts(
6787     std::vector<IntVar*>* cost_elements) const {
6788   CHECK(cost_elements != nullptr);
6789   Solver* const solver = model_->solver();
6790   for (int i = 0; i < cumul_var_piecewise_linear_cost_.size(); ++i) {
6791     const PiecewiseLinearCost& piecewise_linear_cost =
6792         cumul_var_piecewise_linear_cost_[i];
6793     if (piecewise_linear_cost.var != nullptr) {
6794       IntExpr* const expr = solver->MakePiecewiseLinearExpr(
6795           piecewise_linear_cost.var, *piecewise_linear_cost.cost);
6796       IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6797       cost_elements->push_back(cost_var);
6798       // TODO(user): Check if it wouldn't be better to minimize
6799       // piecewise_linear_cost.var here.
6800       model_->AddWeightedVariableMinimizedByFinalizer(cost_var, 0);
6801     }
6802   }
6803 }
6804 
SetCumulVarSoftUpperBound(int64_t index,int64_t upper_bound,int64_t coefficient)6805 void RoutingDimension::SetCumulVarSoftUpperBound(int64_t index,
6806                                                  int64_t upper_bound,
6807                                                  int64_t coefficient) {
6808   if (index >= cumul_var_soft_upper_bound_.size()) {
6809     cumul_var_soft_upper_bound_.resize(index + 1, {nullptr, 0, 0});
6810   }
6811   cumul_var_soft_upper_bound_[index] = {cumuls_[index], upper_bound,
6812                                         coefficient};
6813 }
6814 
HasCumulVarSoftUpperBound(int64_t index) const6815 bool RoutingDimension::HasCumulVarSoftUpperBound(int64_t index) const {
6816   return (index < cumul_var_soft_upper_bound_.size() &&
6817           cumul_var_soft_upper_bound_[index].var != nullptr);
6818 }
6819 
GetCumulVarSoftUpperBound(int64_t index) const6820 int64_t RoutingDimension::GetCumulVarSoftUpperBound(int64_t index) const {
6821   if (index < cumul_var_soft_upper_bound_.size() &&
6822       cumul_var_soft_upper_bound_[index].var != nullptr) {
6823     return cumul_var_soft_upper_bound_[index].bound;
6824   }
6825   return cumuls_[index]->Max();
6826 }
6827 
GetCumulVarSoftUpperBoundCoefficient(int64_t index) const6828 int64_t RoutingDimension::GetCumulVarSoftUpperBoundCoefficient(
6829     int64_t index) const {
6830   if (index < cumul_var_soft_upper_bound_.size() &&
6831       cumul_var_soft_upper_bound_[index].var != nullptr) {
6832     return cumul_var_soft_upper_bound_[index].coefficient;
6833   }
6834   return 0;
6835 }
6836 
SetupCumulVarSoftUpperBoundCosts(std::vector<IntVar * > * cost_elements) const6837 void RoutingDimension::SetupCumulVarSoftUpperBoundCosts(
6838     std::vector<IntVar*>* cost_elements) const {
6839   CHECK(cost_elements != nullptr);
6840   Solver* const solver = model_->solver();
6841   for (int i = 0; i < cumul_var_soft_upper_bound_.size(); ++i) {
6842     const SoftBound& soft_bound = cumul_var_soft_upper_bound_[i];
6843     if (soft_bound.var != nullptr) {
6844       IntExpr* const expr = solver->MakeSemiContinuousExpr(
6845           solver->MakeSum(soft_bound.var, -soft_bound.bound), 0,
6846           soft_bound.coefficient);
6847       IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6848       cost_elements->push_back(cost_var);
6849       // NOTE: We minimize the cost here instead of minimizing the cumul
6850       // variable, to avoid setting the cumul to earlier than necessary.
6851       model_->AddWeightedVariableMinimizedByFinalizer(cost_var,
6852                                                       soft_bound.coefficient);
6853     }
6854   }
6855 }
6856 
SetCumulVarSoftLowerBound(int64_t index,int64_t lower_bound,int64_t coefficient)6857 void RoutingDimension::SetCumulVarSoftLowerBound(int64_t index,
6858                                                  int64_t lower_bound,
6859                                                  int64_t coefficient) {
6860   if (index >= cumul_var_soft_lower_bound_.size()) {
6861     cumul_var_soft_lower_bound_.resize(index + 1, {nullptr, 0, 0});
6862   }
6863   cumul_var_soft_lower_bound_[index] = {cumuls_[index], lower_bound,
6864                                         coefficient};
6865 }
6866 
HasCumulVarSoftLowerBound(int64_t index) const6867 bool RoutingDimension::HasCumulVarSoftLowerBound(int64_t index) const {
6868   return (index < cumul_var_soft_lower_bound_.size() &&
6869           cumul_var_soft_lower_bound_[index].var != nullptr);
6870 }
6871 
GetCumulVarSoftLowerBound(int64_t index) const6872 int64_t RoutingDimension::GetCumulVarSoftLowerBound(int64_t index) const {
6873   if (index < cumul_var_soft_lower_bound_.size() &&
6874       cumul_var_soft_lower_bound_[index].var != nullptr) {
6875     return cumul_var_soft_lower_bound_[index].bound;
6876   }
6877   return cumuls_[index]->Min();
6878 }
6879 
GetCumulVarSoftLowerBoundCoefficient(int64_t index) const6880 int64_t RoutingDimension::GetCumulVarSoftLowerBoundCoefficient(
6881     int64_t index) const {
6882   if (index < cumul_var_soft_lower_bound_.size() &&
6883       cumul_var_soft_lower_bound_[index].var != nullptr) {
6884     return cumul_var_soft_lower_bound_[index].coefficient;
6885   }
6886   return 0;
6887 }
6888 
SetupCumulVarSoftLowerBoundCosts(std::vector<IntVar * > * cost_elements) const6889 void RoutingDimension::SetupCumulVarSoftLowerBoundCosts(
6890     std::vector<IntVar*>* cost_elements) const {
6891   CHECK(cost_elements != nullptr);
6892   Solver* const solver = model_->solver();
6893   for (int i = 0; i < cumul_var_soft_lower_bound_.size(); ++i) {
6894     const SoftBound& soft_bound = cumul_var_soft_lower_bound_[i];
6895     if (soft_bound.var != nullptr) {
6896       IntExpr* const expr = solver->MakeSemiContinuousExpr(
6897           solver->MakeDifference(soft_bound.bound, soft_bound.var), 0,
6898           soft_bound.coefficient);
6899       IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6900       cost_elements->push_back(cost_var);
6901       // NOTE: We minimize the cost here instead of maximizing the cumul
6902       // variable, to avoid setting the cumul to later than necessary.
6903       model_->AddWeightedVariableMinimizedByFinalizer(cost_var,
6904                                                       soft_bound.coefficient);
6905     }
6906   }
6907 }
6908 
SetupGlobalSpanCost(std::vector<IntVar * > * cost_elements) const6909 void RoutingDimension::SetupGlobalSpanCost(
6910     std::vector<IntVar*>* cost_elements) const {
6911   CHECK(cost_elements != nullptr);
6912   Solver* const solver = model_->solver();
6913   if (global_span_cost_coefficient_ != 0) {
6914     std::vector<IntVar*> end_cumuls;
6915     for (int i = 0; i < model_->vehicles(); ++i) {
6916       end_cumuls.push_back(solver
6917                                ->MakeProd(model_->vehicle_route_considered_[i],
6918                                           cumuls_[model_->End(i)])
6919                                ->Var());
6920     }
6921     IntVar* const max_end_cumul = solver->MakeMax(end_cumuls)->Var();
6922     model_->AddWeightedVariableMinimizedByFinalizer(
6923         max_end_cumul, global_span_cost_coefficient_);
6924     std::vector<IntVar*> start_cumuls;
6925     for (int i = 0; i < model_->vehicles(); ++i) {
6926       IntVar* global_span_cost_start_cumul =
6927           solver->MakeIntVar(0, std::numeric_limits<int64_t>::max());
6928       solver->AddConstraint(solver->MakeIfThenElseCt(
6929           model_->vehicle_route_considered_[i], cumuls_[model_->Start(i)],
6930           max_end_cumul, global_span_cost_start_cumul));
6931       start_cumuls.push_back(global_span_cost_start_cumul);
6932     }
6933     IntVar* const min_start_cumul = solver->MakeMin(start_cumuls)->Var();
6934     model_->AddWeightedVariableMinimizedByFinalizer(
6935         min_start_cumul, global_span_cost_coefficient_);
6936     // If there is a single vehicle, model the cost as the sum of its transits
6937     // to avoid slow (infinite) propagation loops.
6938     // TODO(user): Avoid slow propagation in the path constraints.
6939     if (model_->vehicles() == 1) {
6940       for (int var_index = 0; var_index < model_->Size(); ++var_index) {
6941         model_->AddWeightedVariableMinimizedByFinalizer(
6942             slacks_[var_index], global_span_cost_coefficient_);
6943         cost_elements->push_back(
6944             solver
6945                 ->MakeProd(
6946                     model_->vehicle_route_considered_[0],
6947                     solver->MakeProd(
6948                         solver->MakeProd(
6949                             solver->MakeSum(transits_[var_index],
6950                                             dependent_transits_[var_index]),
6951                             global_span_cost_coefficient_),
6952                         model_->ActiveVar(var_index)))
6953                 ->Var());
6954       }
6955     } else {
6956       IntVar* const end_range =
6957           solver->MakeDifference(max_end_cumul, min_start_cumul)->Var();
6958       end_range->SetMin(0);
6959       cost_elements->push_back(
6960           solver->MakeProd(end_range, global_span_cost_coefficient_)->Var());
6961     }
6962   }
6963 }
6964 
SetBreakIntervalsOfVehicle(std::vector<IntervalVar * > breaks,int vehicle,std::vector<int64_t> node_visit_transits)6965 void RoutingDimension::SetBreakIntervalsOfVehicle(
6966     std::vector<IntervalVar*> breaks, int vehicle,
6967     std::vector<int64_t> node_visit_transits) {
6968   if (breaks.empty()) return;
6969   const int visit_evaluator = model()->RegisterTransitCallback(
6970       [node_visit_transits](int64_t from, int64_t to) {
6971         return node_visit_transits[from];
6972       });
6973   SetBreakIntervalsOfVehicle(std::move(breaks), vehicle, visit_evaluator, -1);
6974 }
6975 
SetBreakIntervalsOfVehicle(std::vector<IntervalVar * > breaks,int vehicle,std::vector<int64_t> node_visit_transits,std::function<int64_t (int64_t,int64_t)> delays)6976 void RoutingDimension::SetBreakIntervalsOfVehicle(
6977     std::vector<IntervalVar*> breaks, int vehicle,
6978     std::vector<int64_t> node_visit_transits,
6979     std::function<int64_t(int64_t, int64_t)> delays) {
6980   if (breaks.empty()) return;
6981   const int visit_evaluator = model()->RegisterTransitCallback(
6982       [node_visit_transits](int64_t from, int64_t to) {
6983         return node_visit_transits[from];
6984       });
6985   const int delay_evaluator =
6986       model()->RegisterTransitCallback(std::move(delays));
6987   SetBreakIntervalsOfVehicle(std::move(breaks), vehicle, visit_evaluator,
6988                              delay_evaluator);
6989 }
6990 
SetBreakIntervalsOfVehicle(std::vector<IntervalVar * > breaks,int vehicle,int pre_travel_evaluator,int post_travel_evaluator)6991 void RoutingDimension::SetBreakIntervalsOfVehicle(
6992     std::vector<IntervalVar*> breaks, int vehicle, int pre_travel_evaluator,
6993     int post_travel_evaluator) {
6994   DCHECK_LE(0, vehicle);
6995   DCHECK_LT(vehicle, model_->vehicles());
6996   if (breaks.empty()) return;
6997   if (!break_constraints_are_initialized_) InitializeBreaks();
6998   vehicle_break_intervals_[vehicle] = std::move(breaks);
6999   vehicle_pre_travel_evaluators_[vehicle] = pre_travel_evaluator;
7000   vehicle_post_travel_evaluators_[vehicle] = post_travel_evaluator;
7001   // Breaks intervals must be fixed by search.
7002   for (IntervalVar* const interval : vehicle_break_intervals_[vehicle]) {
7003     model_->AddIntervalToAssignment(interval);
7004     if (interval->MayBePerformed() && !interval->MustBePerformed()) {
7005       model_->AddVariableTargetToFinalizer(interval->PerformedExpr()->Var(), 0);
7006     }
7007     model_->AddVariableTargetToFinalizer(interval->SafeStartExpr(0)->Var(),
7008                                          std::numeric_limits<int64_t>::min());
7009     model_->AddVariableTargetToFinalizer(interval->SafeDurationExpr(0)->Var(),
7010                                          std::numeric_limits<int64_t>::min());
7011   }
7012   // When a vehicle has breaks, if its start and end are fixed,
7013   // then propagation keeps the cumuls min and max on its path feasible.
7014   model_->AddVariableTargetToFinalizer(CumulVar(model_->End(vehicle)),
7015                                        std::numeric_limits<int64_t>::min());
7016   model_->AddVariableTargetToFinalizer(CumulVar(model_->Start(vehicle)),
7017                                        std::numeric_limits<int64_t>::max());
7018 }
7019 
InitializeBreaks()7020 void RoutingDimension::InitializeBreaks() {
7021   DCHECK(!break_constraints_are_initialized_);
7022   const int num_vehicles = model_->vehicles();
7023   vehicle_break_intervals_.resize(num_vehicles);
7024   vehicle_pre_travel_evaluators_.resize(num_vehicles, -1);
7025   vehicle_post_travel_evaluators_.resize(num_vehicles, -1);
7026   vehicle_break_distance_duration_.resize(num_vehicles);
7027   break_constraints_are_initialized_ = true;
7028 }
7029 
HasBreakConstraints() const7030 bool RoutingDimension::HasBreakConstraints() const {
7031   return break_constraints_are_initialized_;
7032 }
7033 
GetBreakIntervalsOfVehicle(int vehicle) const7034 const std::vector<IntervalVar*>& RoutingDimension::GetBreakIntervalsOfVehicle(
7035     int vehicle) const {
7036   DCHECK_LE(0, vehicle);
7037   DCHECK_LT(vehicle, vehicle_break_intervals_.size());
7038   return vehicle_break_intervals_[vehicle];
7039 }
7040 
GetPreTravelEvaluatorOfVehicle(int vehicle) const7041 int RoutingDimension::GetPreTravelEvaluatorOfVehicle(int vehicle) const {
7042   DCHECK_LE(0, vehicle);
7043   DCHECK_LT(vehicle, vehicle_pre_travel_evaluators_.size());
7044   return vehicle_pre_travel_evaluators_[vehicle];
7045 }
7046 
GetPostTravelEvaluatorOfVehicle(int vehicle) const7047 int RoutingDimension::GetPostTravelEvaluatorOfVehicle(int vehicle) const {
7048   DCHECK_LE(0, vehicle);
7049   DCHECK_LT(vehicle, vehicle_post_travel_evaluators_.size());
7050   return vehicle_post_travel_evaluators_[vehicle];
7051 }
7052 
SetBreakDistanceDurationOfVehicle(int64_t distance,int64_t duration,int vehicle)7053 void RoutingDimension::SetBreakDistanceDurationOfVehicle(int64_t distance,
7054                                                          int64_t duration,
7055                                                          int vehicle) {
7056   DCHECK_LE(0, vehicle);
7057   DCHECK_LT(vehicle, model_->vehicles());
7058   if (!break_constraints_are_initialized_) InitializeBreaks();
7059   vehicle_break_distance_duration_[vehicle].emplace_back(distance, duration);
7060   // When a vehicle has breaks, if its start and end are fixed,
7061   // then propagation keeps the cumuls min and max on its path feasible.
7062   model_->AddVariableTargetToFinalizer(CumulVar(model_->End(vehicle)),
7063                                        std::numeric_limits<int64_t>::min());
7064   model_->AddVariableTargetToFinalizer(CumulVar(model_->Start(vehicle)),
7065                                        std::numeric_limits<int64_t>::max());
7066 }
7067 
7068 const std::vector<std::pair<int64_t, int64_t>>&
GetBreakDistanceDurationOfVehicle(int vehicle) const7069 RoutingDimension::GetBreakDistanceDurationOfVehicle(int vehicle) const {
7070   DCHECK_LE(0, vehicle);
7071   DCHECK_LT(vehicle, vehicle_break_distance_duration_.size());
7072   return vehicle_break_distance_duration_[vehicle];
7073 }
7074 
SetPickupToDeliveryLimitFunctionForPair(PickupToDeliveryLimitFunction limit_function,int pair_index)7075 void RoutingDimension::SetPickupToDeliveryLimitFunctionForPair(
7076     PickupToDeliveryLimitFunction limit_function, int pair_index) {
7077   CHECK_GE(pair_index, 0);
7078   if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
7079     pickup_to_delivery_limits_per_pair_index_.resize(pair_index + 1);
7080   }
7081   pickup_to_delivery_limits_per_pair_index_[pair_index] =
7082       std::move(limit_function);
7083 }
7084 
HasPickupToDeliveryLimits() const7085 bool RoutingDimension::HasPickupToDeliveryLimits() const {
7086   return !pickup_to_delivery_limits_per_pair_index_.empty();
7087 }
7088 
GetPickupToDeliveryLimitForPair(int pair_index,int pickup,int delivery) const7089 int64_t RoutingDimension::GetPickupToDeliveryLimitForPair(int pair_index,
7090                                                           int pickup,
7091                                                           int delivery) const {
7092   DCHECK_GE(pair_index, 0);
7093 
7094   if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
7095     return std::numeric_limits<int64_t>::max();
7096   }
7097   const PickupToDeliveryLimitFunction& pickup_to_delivery_limit_function =
7098       pickup_to_delivery_limits_per_pair_index_[pair_index];
7099   if (!pickup_to_delivery_limit_function) {
7100     // No limit function set for this pair.
7101     return std::numeric_limits<int64_t>::max();
7102   }
7103   DCHECK_GE(pickup, 0);
7104   DCHECK_GE(delivery, 0);
7105   return pickup_to_delivery_limit_function(pickup, delivery);
7106 }
7107 
SetupSlackAndDependentTransitCosts() const7108 void RoutingDimension::SetupSlackAndDependentTransitCosts() const {
7109   if (model_->vehicles() == 0) return;
7110   // Figure out whether all vehicles have the same span cost coefficient or not.
7111   bool all_vehicle_span_costs_are_equal = true;
7112   for (int i = 1; i < model_->vehicles(); ++i) {
7113     all_vehicle_span_costs_are_equal &= vehicle_span_cost_coefficients_[i] ==
7114                                         vehicle_span_cost_coefficients_[0];
7115   }
7116 
7117   if (all_vehicle_span_costs_are_equal &&
7118       vehicle_span_cost_coefficients_[0] == 0) {
7119     return;  // No vehicle span cost.
7120   }
7121 
7122   // Make sure that the vehicle's start cumul will be maximized in the end;
7123   // and that the vehicle's end cumul and the node's slacks will be minimized.
7124   // Note that we don't do that if there was no span cost (see the return
7125   // clause above), because in that case we want the dimension cumul to
7126   // remain unconstrained. Since transitions depend on base dimensions, we
7127   // have to make sure the slacks of base dimensions are taken care of.
7128   // Also, it makes more sense to make decisions from the root of the tree
7129   // towards to leaves, and hence the slacks are pushed in reverse order.
7130   std::vector<const RoutingDimension*> dimensions_with_relevant_slacks = {this};
7131   while (true) {
7132     const RoutingDimension* next =
7133         dimensions_with_relevant_slacks.back()->base_dimension_;
7134     if (next == nullptr || next == dimensions_with_relevant_slacks.back()) {
7135       break;
7136     }
7137     dimensions_with_relevant_slacks.push_back(next);
7138   }
7139 
7140   for (auto it = dimensions_with_relevant_slacks.rbegin();
7141        it != dimensions_with_relevant_slacks.rend(); ++it) {
7142     for (int i = 0; i < model_->vehicles(); ++i) {
7143       model_->AddVariableTargetToFinalizer((*it)->cumuls_[model_->End(i)],
7144                                            std::numeric_limits<int64_t>::min());
7145       model_->AddVariableTargetToFinalizer((*it)->cumuls_[model_->Start(i)],
7146                                            std::numeric_limits<int64_t>::max());
7147     }
7148     for (IntVar* const slack : (*it)->slacks_) {
7149       model_->AddVariableTargetToFinalizer(slack,
7150                                            std::numeric_limits<int64_t>::min());
7151     }
7152   }
7153 }
7154 }  // namespace operations_research
7155