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