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