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