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