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.
234  if (status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY) {
235  cumul_values.clear();
236  break_start_end_values.clear();
237  DCHECK(local_mp_optimizers_[i] != nullptr);
238  if (compute_cumul_values(local_mp_optimizers_[i].get(), vehicle,
239  &cumul_values, &break_start_end_values) ==
241  should_fail = true;
242  break;
243  }
244  } else {
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()).value();
3134 }
3135 
3136 } // namespace
3137 
3138 namespace {
3139 void MakeAllUnperformed(const RoutingModel* model, Assignment* assignment) {
3140  assignment->Clear();
3141  for (int i = 0; i < model->Nexts().size(); ++i) {
3142  if (!model->IsStart(i)) {
3143  assignment->Add(model->NextVar(i))->SetValue(i);
3144  }
3145  }
3146  for (int vehicle = 0; vehicle < model->vehicles(); ++vehicle) {
3147  assignment->Add(model->NextVar(model->Start(vehicle)))
3148  ->SetValue(model->End(vehicle));
3149  }
3150 }
3151 } // namespace
3152 
3153 bool RoutingModel::AppendAssignmentIfFeasible(
3154  const Assignment& assignment,
3155  std::vector<std::unique_ptr<Assignment>>* assignments) {
3156  tmp_assignment_->CopyIntersection(&assignment);
3157  solver_->Solve(restore_tmp_assignment_, collect_one_assignment_,
3158  GetOrCreateLimit());
3159  if (collect_one_assignment_->solution_count() == 1) {
3160  assignments->push_back(absl::make_unique<Assignment>(solver_.get()));
3161  assignments->back()->Copy(collect_one_assignment_->solution(0));
3162  return true;
3163  }
3164  return false;
3165 }
3166 
3167 void RoutingModel::LogSolution(const RoutingSearchParameters& parameters,
3168  const std::string& description,
3169  int64 solution_cost, int64 start_time_ms) {
3170  const std::string memory_str = MemoryUsage();
3171  const double cost_scaling_factor = parameters.log_cost_scaling_factor();
3172  const double cost_offset = parameters.log_cost_offset();
3173  const std::string cost_string =
3174  cost_scaling_factor == 1.0 && cost_offset == 0.0
3175  ? absl::StrCat(solution_cost)
3176  : absl::StrFormat(
3177  "%d (%.8lf)", solution_cost,
3178  cost_scaling_factor * (solution_cost + cost_offset));
3179  LOG(INFO) << absl::StrFormat(
3180  "%s (%s, time = %d ms, memory used = %s)", description, cost_string,
3181  solver_->wall_time() - start_time_ms, memory_str);
3182 }
3183 
3185  const Assignment* assignment, const RoutingSearchParameters& parameters,
3186  std::vector<const Assignment*>* solutions) {
3187  const int64 start_time_ms = solver_->wall_time();
3188  QuietCloseModelWithParameters(parameters);
3189  VLOG(1) << "Search parameters:\n" << parameters.DebugString();
3190  if (solutions != nullptr) solutions->clear();
3191  if (status_ == ROUTING_INVALID) {
3192  return nullptr;
3193  }
3194  limit_->UpdateLimits(GetTimeLimit(parameters), kint64max, kint64max,
3195  parameters.solution_limit());
3196  ls_limit_->UpdateLimits(GetTimeLimit(parameters), kint64max, kint64max, 1);
3197  lns_limit_->UpdateLimits(GetLnsTimeLimit(parameters), kint64max, kint64max,
3198  kint64max);
3199  // NOTE: Allow more time for the first solution's scheduling, since if it
3200  // fails, we won't have anything to build upon.
3201  // We set this time limit based on whether local/global dimension optimizers
3202  // are used in the finalizer to avoid going over the general time limit.
3203  // TODO(user): Adapt this when absolute timeouts are given to the model.
3204  const int time_limit_shares = 1 + !global_dimension_optimizers_.empty() +
3205  !local_dimension_optimizers_.empty();
3206  const absl::Duration first_solution_lns_time_limit =
3207  std::max(GetTimeLimit(parameters) / time_limit_shares,
3208  GetLnsTimeLimit(parameters));
3209  first_solution_lns_limit_->UpdateLimits(first_solution_lns_time_limit,
3211 
3212  std::vector<std::unique_ptr<Assignment>> solution_pool;
3213  if (parameters.use_cp() == BOOL_TRUE) {
3214  if (nullptr == assignment) {
3215  bool solution_found = false;
3216  Assignment matching(solver_.get());
3217  if (IsMatchingModel() && SolveMatchingModel(&matching, parameters) &&
3218  AppendAssignmentIfFeasible(matching, &solution_pool)) {
3219  if (parameters.log_search()) {
3220  LogSolution(parameters, "Min-Cost Flow Solution",
3221  solution_pool.back()->ObjectiveValue(), start_time_ms);
3222  }
3223  solution_found = true;
3224  }
3225  if (!solution_found) {
3226  // Build trivial solutions to which we can come back too in case the
3227  // solver does not manage to build something better.
3228  Assignment unperformed(solver_.get());
3229  MakeAllUnperformed(this, &unperformed);
3230  if (AppendAssignmentIfFeasible(unperformed, &solution_pool) &&
3231  parameters.log_search()) {
3232  LogSolution(parameters, "All Unperformed Solution",
3233  solution_pool.back()->ObjectiveValue(), start_time_ms);
3234  }
3235  const absl::Duration elapsed_time =
3236  absl::Milliseconds(solver_->wall_time() - start_time_ms);
3237  const absl::Duration time_left =
3238  GetTimeLimit(parameters) - elapsed_time;
3239  if (time_left >= absl::ZeroDuration()) {
3240  limit_->UpdateLimits(time_left, kint64max, kint64max,
3241  parameters.solution_limit());
3242  ls_limit_->UpdateLimits(time_left, kint64max, kint64max, 1);
3243  solver_->Solve(solve_db_, monitors_);
3244  }
3245  }
3246  } else {
3247  assignment_->CopyIntersection(assignment);
3248  solver_->Solve(improve_db_, monitors_);
3249  }
3250  }
3251 
3252  if (parameters.use_cp_sat() == BOOL_TRUE) {
3253  const int solution_count = collect_assignments_->solution_count();
3254  Assignment* const cp_solution =
3255  solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
3256  : nullptr;
3257  Assignment sat_solution(solver_.get());
3258  if (SolveModelWithSat(*this, parameters, cp_solution, &sat_solution) &&
3259  AppendAssignmentIfFeasible(sat_solution, &solution_pool) &&
3260  parameters.log_search()) {
3261  LogSolution(parameters, "SAT", solution_pool.back()->ObjectiveValue(),
3262  start_time_ms);
3263  }
3264  }
3265 
3266  const absl::Duration elapsed_time =
3267  absl::Milliseconds(solver_->wall_time() - start_time_ms);
3268  const int solution_count = collect_assignments_->solution_count();
3269  if (solution_count >= 1 || !solution_pool.empty()) {
3270  status_ = ROUTING_SUCCESS;
3271  if (solutions != nullptr) {
3272  for (int i = 0; i < solution_count; ++i) {
3273  solutions->push_back(
3274  solver_->MakeAssignment(collect_assignments_->solution(i)));
3275  }
3276  for (const auto& solution : solution_pool) {
3277  if (solutions->empty() ||
3278  solution->ObjectiveValue() < solutions->back()->ObjectiveValue()) {
3279  solutions->push_back(solver_->MakeAssignment(solution.get()));
3280  }
3281  }
3282  return solutions->back();
3283  }
3284  Assignment* best_assignment =
3285  solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
3286  : nullptr;
3287  for (const auto& solution : solution_pool) {
3288  if (best_assignment == nullptr ||
3289  solution->ObjectiveValue() < best_assignment->ObjectiveValue()) {
3290  best_assignment = solution.get();
3291  }
3292  }
3293  return solver_->MakeAssignment(best_assignment);
3294  } else {
3295  if (elapsed_time >= GetTimeLimit(parameters)) {
3296  status_ = ROUTING_FAIL_TIMEOUT;
3297  } else {
3298  status_ = ROUTING_FAIL;
3299  }
3300  return nullptr;
3301  }
3302 }
3303 
3305  Assignment* target_assignment, const RoutingModel* source_model,
3306  const Assignment* source_assignment) {
3307  const int size = Size();
3308  DCHECK_EQ(size, source_model->Size());
3309  CHECK_EQ(target_assignment->solver(), solver_.get());
3310 
3312  SetAssignmentFromAssignment(target_assignment, Nexts(), source_assignment,
3313  source_model->Nexts());
3314  } else {
3315  std::vector<IntVar*> source_vars(size + size + vehicles_);
3316  std::vector<IntVar*> target_vars(size + size + vehicles_);
3317  for (int index = 0; index < size; index++) {
3318  source_vars[index] = source_model->NextVar(index);
3319  target_vars[index] = NextVar(index);
3320  }
3321  for (int index = 0; index < size + vehicles_; index++) {
3322  source_vars[size + index] = source_model->VehicleVar(index);
3323  target_vars[size + index] = VehicleVar(index);
3324  }
3325  SetAssignmentFromAssignment(target_assignment, target_vars,
3326  source_assignment, source_vars);
3327  }
3328 
3329  target_assignment->AddObjective(cost_);
3330 }
3331 
3332 // Computing a lower bound to the cost of a vehicle routing problem solving a
3333 // a linear assignment problem (minimum-cost perfect bipartite matching).
3334 // A bipartite graph is created with left nodes representing the nodes of the
3335 // routing problem and right nodes representing possible node successors; an
3336 // arc between a left node l and a right node r is created if r can be the
3337 // node folowing l in a route (Next(l) = r); the cost of the arc is the transit
3338 // cost between l and r in the routing problem.
3339 // This is a lower bound given the solution to assignment problem does not
3340 // necessarily produce a (set of) closed route(s) from a starting node to an
3341 // ending node.
3343  if (!closed_) {
3344  LOG(WARNING) << "Non-closed model not supported.";
3345  return 0;
3346  }
3348  LOG(WARNING) << "Non-homogeneous vehicle costs not supported";
3349  return 0;
3350  }
3351  if (!disjunctions_.empty()) {
3352  LOG(WARNING)
3353  << "Node disjunction constraints or optional nodes not supported.";
3354  return 0;
3355  }
3356  const int num_nodes = Size() + vehicles_;
3357  ForwardStarGraph graph(2 * num_nodes, num_nodes * num_nodes);
3358  LinearSumAssignment<ForwardStarGraph> linear_sum_assignment(graph, num_nodes);
3359  // Adding arcs for non-end nodes, based on possible values of next variables.
3360  // Left nodes in the bipartite are indexed from 0 to num_nodes - 1; right
3361  // nodes are indexed from num_nodes to 2 * num_nodes - 1.
3362  for (int tail = 0; tail < Size(); ++tail) {
3363  std::unique_ptr<IntVarIterator> iterator(
3364  nexts_[tail]->MakeDomainIterator(false));
3365  for (const int64 head : InitAndGetValues(iterator.get())) {
3366  // Given there are no disjunction constraints, a node cannot point to
3367  // itself. Doing this explicitly given that outside the search,
3368  // propagation hasn't removed this value from next variables yet.
3369  if (head == tail) {
3370  continue;
3371  }
3372  // The index of a right node in the bipartite graph is the index
3373  // of the successor offset by the number of nodes.
3374  const ArcIndex arc = graph.AddArc(tail, num_nodes + head);
3376  linear_sum_assignment.SetArcCost(arc, cost);
3377  }
3378  }
3379  // The linear assignment library requires having as many left and right nodes.
3380  // Therefore we are creating fake assignments for end nodes, forced to point
3381  // to the equivalent start node with a cost of 0.
3382  for (int tail = Size(); tail < num_nodes; ++tail) {
3383  const ArcIndex arc = graph.AddArc(tail, num_nodes + starts_[tail - Size()]);
3384  linear_sum_assignment.SetArcCost(arc, 0);
3385  }
3386  if (linear_sum_assignment.ComputeAssignment()) {
3387  return linear_sum_assignment.GetCost();
3388  }
3389  return 0;
3390 }
3391 
3392 bool RoutingModel::RouteCanBeUsedByVehicle(const Assignment& assignment,
3393  int start_index, int vehicle) const {
3394  int current_index =
3395  IsStart(start_index) ? Next(assignment, start_index) : start_index;
3396  while (!IsEnd(current_index)) {
3397  const IntVar* const vehicle_var = VehicleVar(current_index);
3398  if (!vehicle_var->Contains(vehicle)) {
3399  return false;
3400  }
3401  const int next_index = Next(assignment, current_index);
3402  CHECK_NE(next_index, current_index) << "Inactive node inside a route";
3403  current_index = next_index;
3404  }
3405  return true;
3406 }
3407 
3408 bool RoutingModel::ReplaceUnusedVehicle(
3409  int unused_vehicle, int active_vehicle,
3410  Assignment* const compact_assignment) const {
3411  CHECK(compact_assignment != nullptr);
3412  CHECK(!IsVehicleUsed(*compact_assignment, unused_vehicle));
3413  CHECK(IsVehicleUsed(*compact_assignment, active_vehicle));
3414  // Swap NextVars at start nodes.
3415  const int unused_vehicle_start = Start(unused_vehicle);
3416  IntVar* const unused_vehicle_start_var = NextVar(unused_vehicle_start);
3417  const int unused_vehicle_end = End(unused_vehicle);
3418  const int active_vehicle_start = Start(active_vehicle);
3419  const int active_vehicle_end = End(active_vehicle);
3420  IntVar* const active_vehicle_start_var = NextVar(active_vehicle_start);
3421  const int active_vehicle_next =
3422  compact_assignment->Value(active_vehicle_start_var);
3423  compact_assignment->SetValue(unused_vehicle_start_var, active_vehicle_next);
3424  compact_assignment->SetValue(active_vehicle_start_var, End(active_vehicle));
3425 
3426  // Update VehicleVars along the route, update the last NextVar.
3427  int current_index = active_vehicle_next;
3428  while (!IsEnd(current_index)) {
3429  IntVar* const vehicle_var = VehicleVar(current_index);
3430  compact_assignment->SetValue(vehicle_var, unused_vehicle);
3431  const int next_index = Next(*compact_assignment, current_index);
3432  if (IsEnd(next_index)) {
3433  IntVar* const last_next_var = NextVar(current_index);
3434  compact_assignment->SetValue(last_next_var, End(unused_vehicle));
3435  }
3436  current_index = next_index;
3437  }
3438 
3439  // Update dimensions: update transits at the start.
3440  for (const RoutingDimension* const dimension : dimensions_) {
3441  const std::vector<IntVar*>& transit_variables = dimension->transits();
3442  IntVar* const unused_vehicle_transit_var =
3443  transit_variables[unused_vehicle_start];
3444  IntVar* const active_vehicle_transit_var =
3445  transit_variables[active_vehicle_start];
3446  const bool contains_unused_vehicle_transit_var =
3447  compact_assignment->Contains(unused_vehicle_transit_var);
3448  const bool contains_active_vehicle_transit_var =
3449  compact_assignment->Contains(active_vehicle_transit_var);
3450  if (contains_unused_vehicle_transit_var !=
3451  contains_active_vehicle_transit_var) {
3452  // TODO(user): clarify the expected trigger rate of this LOG.
3453  LOG(INFO) << "The assignment contains transit variable for dimension '"
3454  << dimension->name() << "' for some vehicles, but not for all";
3455  return false;
3456  }
3457  if (contains_unused_vehicle_transit_var) {
3458  const int64 old_unused_vehicle_transit =
3459  compact_assignment->Value(unused_vehicle_transit_var);
3460  const int64 old_active_vehicle_transit =
3461  compact_assignment->Value(active_vehicle_transit_var);
3462  compact_assignment->SetValue(unused_vehicle_transit_var,
3463  old_active_vehicle_transit);
3464  compact_assignment->SetValue(active_vehicle_transit_var,
3465  old_unused_vehicle_transit);
3466  }
3467 
3468  // Update dimensions: update cumuls at the end.
3469  const std::vector<IntVar*>& cumul_variables = dimension->cumuls();
3470  IntVar* const unused_vehicle_cumul_var =
3471  cumul_variables[unused_vehicle_end];
3472  IntVar* const active_vehicle_cumul_var =
3473  cumul_variables[active_vehicle_end];
3474  const int64 old_unused_vehicle_cumul =
3475  compact_assignment->Value(unused_vehicle_cumul_var);
3476  const int64 old_active_vehicle_cumul =
3477  compact_assignment->Value(active_vehicle_cumul_var);
3478  compact_assignment->SetValue(unused_vehicle_cumul_var,
3479  old_active_vehicle_cumul);
3480  compact_assignment->SetValue(active_vehicle_cumul_var,
3481  old_unused_vehicle_cumul);
3482  }
3483  return true;
3484 }
3485 
3487  const Assignment& assignment) const {
3488  return CompactAssignmentInternal(assignment, false);
3489 }
3490 
3492  const Assignment& assignment) const {
3493  return CompactAssignmentInternal(assignment, true);
3494 }
3495 
3496 Assignment* RoutingModel::CompactAssignmentInternal(
3497  const Assignment& assignment, bool check_compact_assignment) const {
3498  CHECK_EQ(assignment.solver(), solver_.get());
3500  LOG(WARNING)
3501  << "The costs are not homogeneous, routes cannot be rearranged";
3502  return nullptr;
3503  }
3504 
3505  std::unique_ptr<Assignment> compact_assignment(new Assignment(&assignment));
3506  for (int vehicle = 0; vehicle < vehicles_ - 1; ++vehicle) {
3507  if (IsVehicleUsed(*compact_assignment, vehicle)) {
3508  continue;
3509  }
3510  const int vehicle_start = Start(vehicle);
3511  const int vehicle_end = End(vehicle);
3512  // Find the last vehicle, that can swap routes with this one.
3513  int swap_vehicle = vehicles_ - 1;
3514  bool has_more_vehicles_with_route = false;
3515  for (; swap_vehicle > vehicle; --swap_vehicle) {
3516  // If a vehicle was already swapped, it will appear in compact_assignment
3517  // as unused.
3518  if (!IsVehicleUsed(*compact_assignment, swap_vehicle) ||
3519  !IsVehicleUsed(*compact_assignment, swap_vehicle)) {
3520  continue;
3521  }
3522  has_more_vehicles_with_route = true;
3523  const int swap_vehicle_start = Start(swap_vehicle);
3524  const int swap_vehicle_end = End(swap_vehicle);
3525  if (manager_.IndexToNode(vehicle_start) !=
3526  manager_.IndexToNode(swap_vehicle_start) ||
3527  manager_.IndexToNode(vehicle_end) !=
3528  manager_.IndexToNode(swap_vehicle_end)) {
3529  continue;
3530  }
3531 
3532  // Check that updating VehicleVars is OK.
3533  if (RouteCanBeUsedByVehicle(*compact_assignment, swap_vehicle_start,
3534  vehicle)) {
3535  break;
3536  }
3537  }
3538 
3539  if (swap_vehicle == vehicle) {
3540  if (has_more_vehicles_with_route) {
3541  // No route can be assigned to this vehicle, but there are more vehicles
3542  // with a route left. This would leave a gap in the indices.
3543  // TODO(user): clarify the expected trigger rate of this LOG.
3544  LOG(INFO) << "No vehicle that can be swapped with " << vehicle
3545  << " was found";
3546  return nullptr;
3547  } else {
3548  break;
3549  }
3550  } else {
3551  if (!ReplaceUnusedVehicle(vehicle, swap_vehicle,
3552  compact_assignment.get())) {
3553  return nullptr;
3554  }
3555  }
3556  }
3557  if (check_compact_assignment &&
3558  !solver_->CheckAssignment(compact_assignment.get())) {
3559  // TODO(user): clarify the expected trigger rate of this LOG.
3560  LOG(WARNING) << "The compacted assignment is not a valid solution";
3561  return nullptr;
3562  }
3563  return compact_assignment.release();
3564 }
3565 
3566 int RoutingModel::FindNextActive(int index,
3567  const std::vector<int64>& indices) const {
3568  ++index;
3569  CHECK_LE(0, index);
3570  const int size = indices.size();
3571  while (index < size && ActiveVar(indices[index])->Max() == 0) {
3572  ++index;
3573  }
3574  return index;
3575 }
3576 
3577 IntVar* RoutingModel::ApplyLocks(const std::vector<int64>& locks) {
3578  // TODO(user): Replace calls to this method with calls to
3579  // ApplyLocksToAllVehicles and remove this method?
3580  CHECK_EQ(vehicles_, 1);
3581  preassignment_->Clear();
3582  IntVar* next_var = nullptr;
3583  int lock_index = FindNextActive(-1, locks);
3584  const int size = locks.size();
3585  if (lock_index < size) {
3586  next_var = NextVar(locks[lock_index]);
3587  preassignment_->Add(next_var);
3588  for (lock_index = FindNextActive(lock_index, locks); lock_index < size;
3589  lock_index = FindNextActive(lock_index, locks)) {
3590  preassignment_->SetValue(next_var, locks[lock_index]);
3591  next_var = NextVar(locks[lock_index]);
3592  preassignment_->Add(next_var);
3593  }
3594  }
3595  return next_var;
3596 }
3597 
3599  const std::vector<std::vector<int64>>& locks, bool close_routes) {
3600  preassignment_->Clear();
3601  return RoutesToAssignment(locks, true, close_routes, preassignment_);
3602 }
3603 
3605  const RoutingSearchParameters& parameters) const {
3606  IntVarFilteredDecisionBuilder* const decision_builder =
3607  GetFilteredFirstSolutionDecisionBuilderOrNull(parameters);
3608  return decision_builder != nullptr ? decision_builder->number_of_decisions()
3609  : 0;
3610 }
3611 
3613  const RoutingSearchParameters& parameters) const {
3614  IntVarFilteredDecisionBuilder* const decision_builder =
3615  GetFilteredFirstSolutionDecisionBuilderOrNull(parameters);
3616  return decision_builder != nullptr ? decision_builder->number_of_rejects()
3617  : 0;
3618 }
3619 
3620 bool RoutingModel::WriteAssignment(const std::string& file_name) const {
3621  if (collect_assignments_->solution_count() == 1 && assignment_ != nullptr) {
3622  assignment_->CopyIntersection(collect_assignments_->solution(0));
3623  return assignment_->Save(file_name);
3624  } else {
3625  return false;
3626  }
3627 }
3628 
3629 Assignment* RoutingModel::ReadAssignment(const std::string& file_name) {
3630  QuietCloseModel();
3631  CHECK(assignment_ != nullptr);
3632  if (assignment_->Load(file_name)) {
3633  return DoRestoreAssignment();
3634  }
3635  return nullptr;
3636 }
3637 
3638 Assignment* RoutingModel::RestoreAssignment(const Assignment& solution) {
3639  QuietCloseModel();
3640  CHECK(assignment_ != nullptr);
3641  assignment_->CopyIntersection(&solution);
3642  return DoRestoreAssignment();
3643 }
3644 
3645 Assignment* RoutingModel::DoRestoreAssignment() {
3646  if (status_ == ROUTING_INVALID) {
3647  return nullptr;
3648  }
3649  solver_->Solve(restore_assignment_, monitors_);
3650  if (collect_assignments_->solution_count() == 1) {
3651  status_ = ROUTING_SUCCESS;
3652  return collect_assignments_->solution(0);
3653  } else {
3654  status_ = ROUTING_FAIL;
3655  return nullptr;
3656  }
3657  return nullptr;
3658 }
3659 
3661  const std::vector<std::vector<int64>>& routes, bool ignore_inactive_indices,
3662  bool close_routes, Assignment* const assignment) const {
3663  CHECK(assignment != nullptr);
3664  if (!closed_) {
3665  LOG(ERROR) << "The model is not closed yet";
3666  return false;
3667  }
3668  const int num_routes = routes.size();
3669  if (num_routes > vehicles_) {
3670  LOG(ERROR) << "The number of vehicles in the assignment (" << routes.size()
3671  << ") is greater than the number of vehicles in the model ("
3672  << vehicles_ << ")";
3673  return false;
3674  }
3675 
3676  absl::flat_hash_set<int> visited_indices;
3677  // Set value to NextVars based on the routes.
3678  for (int vehicle = 0; vehicle < num_routes; ++vehicle) {
3679  const std::vector<int64>& route = routes[vehicle];
3680  int from_index = Start(vehicle);
3681  std::pair<absl::flat_hash_set<int>::iterator, bool> insert_result =
3682  visited_indices.insert(from_index);
3683  if (!insert_result.second) {
3684  LOG(ERROR) << "Index " << from_index << " (start node for vehicle "
3685  << vehicle << ") was already used";
3686  return false;
3687  }
3688 
3689  for (const int64 to_index : route) {
3690  if (to_index < 0 || to_index >= Size()) {
3691  LOG(ERROR) << "Invalid index: " << to_index;
3692  return false;
3693  }
3694 
3695  IntVar* const active_var = ActiveVar(to_index);
3696  if (active_var->Max() == 0) {
3697  if (ignore_inactive_indices) {
3698  continue;
3699  } else {
3700  LOG(ERROR) << "Index " << to_index << " is not active";
3701  return false;
3702  }
3703  }
3704 
3705  insert_result = visited_indices.insert(to_index);
3706  if (!insert_result.second) {
3707  LOG(ERROR) << "Index " << to_index << " is used multiple times";
3708  return false;
3709  }
3710 
3711  const IntVar* const vehicle_var = VehicleVar(to_index);
3712  if (!vehicle_var->Contains(vehicle)) {
3713  LOG(ERROR) << "Vehicle " << vehicle << " is not allowed at index "
3714  << to_index;
3715  return false;
3716  }
3717 
3718  IntVar* const from_var = NextVar(from_index);
3719  if (!assignment->Contains(from_var)) {
3720  assignment->Add(from_var);
3721  }
3722  assignment->SetValue(from_var, to_index);
3723 
3724  from_index = to_index;
3725  }
3726 
3727  if (close_routes) {
3728  IntVar* const last_var = NextVar(from_index);
3729  if (!assignment->Contains(last_var)) {
3730  assignment->Add(last_var);
3731  }
3732  assignment->SetValue(last_var, End(vehicle));
3733  }
3734  }
3735 
3736  // Do not use the remaining vehicles.
3737  for (int vehicle = num_routes; vehicle < vehicles_; ++vehicle) {
3738  const int start_index = Start(vehicle);
3739  // Even if close_routes is false, we still need to add the start index to
3740  // visited_indices so that deactivating other nodes works correctly.
3741  std::pair<absl::flat_hash_set<int>::iterator, bool> insert_result =
3742  visited_indices.insert(start_index);
3743  if (!insert_result.second) {
3744  LOG(ERROR) << "Index " << start_index << " is used multiple times";
3745  return false;
3746  }
3747  if (close_routes) {
3748  IntVar* const start_var = NextVar(start_index);
3749  if (!assignment->Contains(start_var)) {
3750  assignment->Add(start_var);
3751  }
3752  assignment->SetValue(start_var, End(vehicle));
3753  }
3754  }
3755 
3756  // Deactivate other nodes (by pointing them to themselves).
3757  if (close_routes) {
3758  for (int index = 0; index < Size(); ++index) {
3759  if (!gtl::ContainsKey(visited_indices, index)) {
3760  IntVar* const next_var = NextVar(index);
3761  if (!assignment->Contains(next_var)) {
3762  assignment->Add(next_var);
3763  }
3764  assignment->SetValue(next_var, index);
3765  }
3766  }
3767  }
3768 
3769  return true;
3770 }
3771 
3773  const std::vector<std::vector<int64>>& routes,
3774  bool ignore_inactive_indices) {
3775  QuietCloseModel();
3776  if (!RoutesToAssignment(routes, ignore_inactive_indices, true, assignment_)) {
3777  return nullptr;
3778  }
3779  // DoRestoreAssignment() might still fail when checking constraints (most
3780  // constraints are not verified by RoutesToAssignment) or when filling in
3781  // dimension variables.
3782  return DoRestoreAssignment();
3783 }
3784 
3786  const Assignment& assignment,
3787  std::vector<std::vector<int64>>* const routes) const {
3788  CHECK(closed_);
3789  CHECK(routes != nullptr);
3790 
3791  const int model_size = Size();
3792  routes->resize(vehicles_);
3793  for (int vehicle = 0; vehicle < vehicles_; ++vehicle) {
3794  std::vector<int64>* const vehicle_route = &routes->at(vehicle);
3795  vehicle_route->clear();
3796 
3797  int num_visited_indices = 0;
3798  const int first_index = Start(vehicle);
3799  const IntVar* const first_var = NextVar(first_index);
3800  CHECK(assignment.Contains(first_var));
3801  CHECK(assignment.Bound(first_var));
3802  int current_index = assignment.Value(first_var);
3803  while (!IsEnd(current_index)) {
3804  vehicle_route->push_back(current_index);
3805 
3806  const IntVar* const next_var = NextVar(current_index);
3807  CHECK(assignment.Contains(next_var));
3808  CHECK(assignment.Bound(next_var));
3809  current_index = assignment.Value(next_var);
3810 
3811  ++num_visited_indices;
3812  CHECK_LE(num_visited_indices, model_size)
3813  << "The assignment contains a cycle";
3814  }
3815  }
3816 }
3817 
3818 #ifndef SWIG
3819 std::vector<std::vector<int64>> RoutingModel::GetRoutesFromAssignment(
3820  const Assignment& assignment) {
3821  std::vector<std::vector<int64>> route_indices(vehicles());
3822  for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
3823  if (!assignment.Bound(NextVar(vehicle))) {
3824  LOG(DFATAL) << "GetRoutesFromAssignment() called on incomplete solution:"
3825  << " NextVar(" << vehicle << ") is unbound.";
3826  }
3827  }
3828  for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
3829  int64 index = Start(vehicle);
3830  route_indices[vehicle].push_back(index);
3831  while (!IsEnd(index)) {
3832  index = assignment.Value(NextVar(index));
3833  route_indices[vehicle].push_back(index);
3834  }
3835  }
3836  return route_indices;
3837 }
3838 #endif
3839 
3840 int64 RoutingModel::GetArcCostForClassInternal(
3841  int64 from_index, int64 to_index, CostClassIndex cost_class_index) const {
3842  DCHECK(closed_);
3843  DCHECK_GE(cost_class_index, 0);
3844  DCHECK_LT(cost_class_index, cost_classes_.size());
3845  CostCacheElement* const cache = &cost_cache_[from_index];
3846  // See the comment in CostCacheElement in the .h for the int64->int cast.
3847  if (cache->index == static_cast<int>(to_index) &&
3848  cache->cost_class_index == cost_class_index) {
3849  return cache->cost;
3850  }
3851  int64 cost = 0;
3852  const CostClass& cost_class = cost_classes_[cost_class_index];
3853  const auto& evaluator = transit_evaluators_[cost_class.evaluator_index];
3854  if (!IsStart(from_index)) {
3855  cost = CapAdd(evaluator(from_index, to_index),
3856  GetDimensionTransitCostSum(from_index, to_index, cost_class));
3857  } else if (!IsEnd(to_index)) {
3858  // Apply route fixed cost on first non-first/last node, in other words on
3859  // the arc from the first node to its next node if it's not the last node.
3860  cost = CapAdd(
3861  evaluator(from_index, to_index),
3862  CapAdd(GetDimensionTransitCostSum(from_index, to_index, cost_class),
3863  fixed_cost_of_vehicle_[index_to_vehicle_[from_index]]));
3864  } else {
3865  // If there's only the first and last nodes on the route, it is considered
3866  // as an empty route.
3867  if (consider_empty_route_costs_[index_to_vehicle_[from_index]]) {
3868  cost =
3869  CapAdd(evaluator(from_index, to_index),
3870  GetDimensionTransitCostSum(from_index, to_index, cost_class));
3871  } else {
3872  cost = 0;
3873  }
3874  }
3875  *cache = {static_cast<int>(to_index), cost_class_index, cost};
3876  return cost;
3877 }
3878 
3880  return !IsEnd(index) && index_to_vehicle_[index] != kUnassigned;
3881 }
3882 
3883 bool RoutingModel::IsVehicleUsed(const Assignment& assignment,
3884  int vehicle) const {
3885  CHECK_GE(vehicle, 0);
3886  CHECK_LT(vehicle, vehicles_);
3887  CHECK_EQ(solver_.get(), assignment.solver());
3888  IntVar* const start_var = NextVar(Start(vehicle));
3889  CHECK(assignment.Contains(start_var));
3890  return !IsEnd(assignment.Value(start_var));
3891 }
3892 
3893 int64 RoutingModel::Next(const Assignment& assignment, int64 index) const {
3894  CHECK_EQ(solver_.get(), assignment.solver());
3895  IntVar* const next_var = NextVar(index);
3896  CHECK(assignment.Contains(next_var));
3897  CHECK(assignment.Bound(next_var));
3898  return assignment.Value(next_var);
3899 }
3900 
3902  int64 vehicle) const {
3903  if (from_index != to_index && vehicle >= 0) {
3904  return GetArcCostForClassInternal(from_index, to_index,
3905  GetCostClassIndexOfVehicle(vehicle));
3906  } else {
3907  return 0;
3908  }
3909 }
3910 
3912  int64 from_index, int64 to_index,
3913  int64 /*CostClassIndex*/ cost_class_index) const {
3914  if (from_index != to_index) {
3915  return GetArcCostForClassInternal(from_index, to_index,
3916  CostClassIndex(cost_class_index));
3917  } else {
3918  return 0;
3919  }
3920 }
3921 
3923  int64 to_index) const {
3924  // Return high cost if connecting to an end (or bound-to-end) node;
3925  // this is used in the cost-based first solution strategies to avoid closing
3926  // routes too soon.
3927  if (!is_bound_to_end_ct_added_.Switched()) {
3928  // Lazily adding path-cumul constraint propagating connection to route end,
3929  // as it can be pretty costly in the general case.
3930  std::vector<IntVar*> zero_transit(Size(), solver_->MakeIntConst(0));
3931  solver_->AddConstraint(solver_->MakeDelayedPathCumul(
3932  nexts_, active_, is_bound_to_end_, zero_transit));
3933  is_bound_to_end_ct_added_.Switch(solver_.get());
3934  }
3935  if (is_bound_to_end_[to_index]->Min() == 1) return kint64max;
3936  // TODO(user): Take vehicle into account.
3937  return GetHomogeneousCost(from_index, to_index);
3938 }
3939 
3940 int64 RoutingModel::GetDimensionTransitCostSum(
3941  int64 i, int64 j, const CostClass& cost_class) const {
3942  int64 cost = 0;
3943  for (const auto& evaluator_and_coefficient :
3944  cost_class.dimension_transit_evaluator_class_and_cost_coefficient) {
3945  DCHECK_GT(evaluator_and_coefficient.cost_coefficient, 0);
3946  cost = CapAdd(
3947  cost,
3948  CapProd(evaluator_and_coefficient.cost_coefficient,
3949  evaluator_and_coefficient.dimension->GetTransitValueFromClass(
3950  i, j, evaluator_and_coefficient.transit_evaluator_class)));
3951  }
3952  return cost;
3953 }
3954 
3956  int64 to2) {
3957  // Deal with end nodes: never pick an end node over a non-end node.
3958  if (IsEnd(to1) || IsEnd(to2)) {
3959  if (IsEnd(to1) != IsEnd(to2)) return IsEnd(to2);
3960  // If both are end nodes, we don't care; the right end node will be picked
3961  // by constraint propagation. Break the tie by index.
3962  return to1 < to2;
3963  }
3964 
3965  // Look whether they are mandatory (must be performed) or optional.
3966  const bool mandatory1 = active_[to1]->Min() == 1;
3967  const bool mandatory2 = active_[to2]->Min() == 1;
3968  // Always pick a mandatory node over a non-mandatory one.
3969  if (mandatory1 != mandatory2) return mandatory1;
3970 
3971  // Look at the vehicle variables.
3972  IntVar* const src_vehicle_var = VehicleVar(from);
3973  // In case the source vehicle is bound, "src_vehicle" will be it.
3974  // Otherwise, it'll be set to some possible source vehicle that
3975  // isn't -1 (if possible).
3976  const int64 src_vehicle = src_vehicle_var->Max();
3977  if (src_vehicle_var->Bound()) {
3978  IntVar* const to1_vehicle_var = VehicleVar(to1);
3979  IntVar* const to2_vehicle_var = VehicleVar(to2);
3980  // Subtle: non-mandatory node have kNoVehicle as possible value for
3981  // their vehicle variable. So they're effectively "bound" when their domain
3982  // size is 2.
3983  const bool bound1 =
3984  mandatory1 ? to1_vehicle_var->Bound() : (to1_vehicle_var->Size() <= 2);
3985  const bool bound2 =
3986  mandatory2 ? to2_vehicle_var->Bound() : (to2_vehicle_var->Size() <= 2);
3987  // Prefer a destination bound to a given vehicle, even if it's not
3988  // bound to the right one (the propagation will quickly rule it out).
3989  if (bound1 != bound2) return bound1;
3990  if (bound1) { // same as bound1 && bound2.
3991  // Min() will return kNoVehicle for optional nodes. Thus we use Max().
3992  const int64 vehicle1 = to1_vehicle_var->Max();
3993  const int64 vehicle2 = to2_vehicle_var->Max();
3994  // Prefer a destination bound to the right vehicle.
3995  // TODO(user): cover this clause in a unit test.
3996  if ((vehicle1 == src_vehicle) != (vehicle2 == src_vehicle)) {
3997  return vehicle1 == src_vehicle;
3998  }
3999  // If no destination is bound to the right vehicle, whatever we
4000  // return doesn't matter: both are infeasible. To be consistent, we
4001  // just break the tie.
4002  if (vehicle1 != src_vehicle) return to1 < to2;
4003  }
4004  }
4005  // At this point, either both destinations are bound to the source vehicle,
4006  // or none of them is bound, or the source vehicle isn't bound.
4007  // We don't bother inspecting the domains of the vehicle variables further.
4008 
4009  // Inspect the primary constrained dimension, if any.
4010  // TODO(user): try looking at all the dimensions, not just the primary one,
4011  // and reconsider the need for a "primary" dimension.
4012  if (!GetPrimaryConstrainedDimension().empty()) {
4013  const std::vector<IntVar*>& cumul_vars =
4015  IntVar* const dim1 = cumul_vars[to1];
4016  IntVar* const dim2 = cumul_vars[to2];
4017  // Prefer the destination that has a lower upper bound for the constrained
4018  // dimension.
4019  if (dim1->Max() != dim2->Max()) return dim1->Max() < dim2->Max();
4020  // TODO(user): evaluate the *actual* Min() of each cumul variable in the
4021  // scenario where the corresponding arc from->to is performed, and pick
4022  // the destination with the lowest value.
4023  }
4024 
4025  // Break ties on equally constrained nodes with the (cost - unperformed
4026  // penalty).
4027  {
4028  const /*CostClassIndex*/ int64 cost_class_index =
4029  SafeGetCostClassInt64OfVehicle(src_vehicle);
4030  const int64 cost1 = CapSub(GetArcCostForClass(from, to1, cost_class_index),
4031  UnperformedPenalty(to1));
4032  const int64 cost2 = CapSub(GetArcCostForClass(from, to2, cost_class_index),
4033  UnperformedPenalty(to2));
4034  if (cost1 != cost2) return cost1 < cost2;
4035  }
4036 
4037  // Further break ties by looking at the size of the VehicleVar.
4038  {
4039  const int64 num_vehicles1 = VehicleVar(to1)->Size();
4040  const int64 num_vehicles2 = VehicleVar(to2)->Size();
4041  if (num_vehicles1 != num_vehicles2) return num_vehicles1 < num_vehicles2;
4042  }
4043 
4044  // Break perfect ties by value.
4045  return to1 < to2;
4046 }
4047 
4049  CHECK_LT(index, index_to_visit_type_.size());
4050  DCHECK_EQ(index_to_visit_type_.size(), index_to_type_policy_.size());
4051  index_to_visit_type_[index] = type;
4052  index_to_type_policy_[index] = policy;
4053  num_visit_types_ = std::max(num_visit_types_, type + 1);
4054 }
4055 
4057  CHECK_LT(index, index_to_visit_type_.size());
4058  return index_to_visit_type_[index];
4059 }
4060 
4061 const std::vector<int>& RoutingModel::GetSingleNodesOfType(int type) const {
4062  DCHECK_LT(type, single_nodes_of_type_.size());
4063  return single_nodes_of_type_[type];
4064 }
4065 
4066 const std::vector<int>& RoutingModel::GetPairIndicesOfType(int type) const {
4067  DCHECK_LT(type, pair_indices_of_type_.size());
4068  return pair_indices_of_type_[type];
4069 }
4070 
4072  int64 index) const {
4073  CHECK_LT(index, index_to_type_policy_.size());
4074  return index_to_type_policy_[index];
4075 }
4076 
4078  hard_incompatible_types_per_type_index_.resize(num_visit_types_);
4079  temporal_incompatible_types_per_type_index_.resize(num_visit_types_);
4080  same_vehicle_required_type_alternatives_per_type_index_.resize(
4081  num_visit_types_);
4082  required_type_alternatives_when_adding_type_index_.resize(num_visit_types_);
4083  required_type_alternatives_when_removing_type_index_.resize(num_visit_types_);
4084 }
4085 
4086 void RoutingModel::AddHardTypeIncompatibility(int type1, int type2) {
4087  DCHECK_LT(std::max(type1, type2),
4088  hard_incompatible_types_per_type_index_.size());
4089  has_hard_type_incompatibilities_ = true;
4090 
4091  hard_incompatible_types_per_type_index_[type1].insert(type2);
4092  hard_incompatible_types_per_type_index_[type2].insert(type1);
4093 }
4094 
4096  DCHECK_LT(std::max(type1, type2),
4097  temporal_incompatible_types_per_type_index_.size());
4098  has_temporal_type_incompatibilities_ = true;
4099 
4100  temporal_incompatible_types_per_type_index_[type1].insert(type2);
4101  temporal_incompatible_types_per_type_index_[type2].insert(type1);
4102 }
4103 
4104 const absl::flat_hash_set<int>&
4106  DCHECK_GE(type, 0);
4107  DCHECK_LT(type, hard_incompatible_types_per_type_index_.size());
4108  return hard_incompatible_types_per_type_index_[type];
4109 }
4110 
4111 const absl::flat_hash_set<int>&
4113  DCHECK_GE(type, 0);
4114  DCHECK_LT(type, temporal_incompatible_types_per_type_index_.size());
4115  return temporal_incompatible_types_per_type_index_[type];
4116 }
4117 
4118 // TODO(user): Consider if an empty "required_type_alternatives" should mean
4119 // trivially feasible requirement, as there are no required type alternatives?
4121  int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4122  DCHECK_LT(dependent_type,
4123  same_vehicle_required_type_alternatives_per_type_index_.size());
4124 
4125  if (required_type_alternatives.empty()) {
4126  // The dependent_type requires an infeasible (empty) set of types.
4127  // Nodes of this type and all policies except
4128  // ADDED_TYPE_REMOVED_FROM_VEHICLE are trivially infeasible.
4129  absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4130  trivially_infeasible_visit_types_to_policies_[dependent_type];
4131  infeasible_policies.insert(TYPE_ADDED_TO_VEHICLE);
4132  infeasible_policies.insert(TYPE_ON_VEHICLE_UP_TO_VISIT);
4133  infeasible_policies.insert(TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED);
4134  return;
4135  }
4136 
4137  has_same_vehicle_type_requirements_ = true;
4138  same_vehicle_required_type_alternatives_per_type_index_[dependent_type]
4139  .push_back(std::move(required_type_alternatives));
4140 }
4141 
4143  int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4144  DCHECK_LT(dependent_type,
4145  required_type_alternatives_when_adding_type_index_.size());
4146 
4147  if (required_type_alternatives.empty()) {
4148  // The dependent_type requires an infeasible (empty) set of types.
4149  // Nodes of this type and policy TYPE_ADDED_TO_VEHICLE or
4150  // TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED are trivially infeasible.
4151  absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4152  trivially_infeasible_visit_types_to_policies_[dependent_type];
4153  infeasible_policies.insert(TYPE_ADDED_TO_VEHICLE);
4154  infeasible_policies.insert(TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED);
4155  return;
4156  }
4157 
4158  has_temporal_type_requirements_ = true;
4159  required_type_alternatives_when_adding_type_index_[dependent_type].push_back(
4160  std::move(required_type_alternatives));
4161 }
4162 
4164  int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4165  DCHECK_LT(dependent_type,
4166  required_type_alternatives_when_removing_type_index_.size());
4167 
4168  if (required_type_alternatives.empty()) {
4169  // The dependent_type requires an infeasible (empty) set of types.
4170  // Nodes of this type and all policies except TYPE_ADDED_TO_VEHICLE are
4171  // trivially infeasible.
4172  absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4173  trivially_infeasible_visit_types_to_policies_[dependent_type];
4174  infeasible_policies.insert(ADDED_TYPE_REMOVED_FROM_VEHICLE);
4175  infeasible_policies.insert(TYPE_ON_VEHICLE_UP_TO_VISIT);
4176  infeasible_policies.insert(TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED);
4177  return;
4178  }
4179 
4180  has_temporal_type_requirements_ = true;
4181  required_type_alternatives_when_removing_type_index_[dependent_type]
4182  .push_back(std::move(required_type_alternatives));
4183 }
4184 
4185 const std::vector<absl::flat_hash_set<int>>&
4187  DCHECK_GE(type, 0);
4188  DCHECK_LT(type,
4189  same_vehicle_required_type_alternatives_per_type_index_.size());
4190  return same_vehicle_required_type_alternatives_per_type_index_[type];
4191 }
4192 
4193 const std::vector<absl::flat_hash_set<int>>&
4195  DCHECK_GE(type, 0);
4196  DCHECK_LT(type, required_type_alternatives_when_adding_type_index_.size());
4197  return required_type_alternatives_when_adding_type_index_[type];
4198 }
4199 
4200 const std::vector<absl::flat_hash_set<int>>&
4202  DCHECK_GE(type, 0);
4203  DCHECK_LT(type, required_type_alternatives_when_removing_type_index_.size());
4204  return required_type_alternatives_when_removing_type_index_[type];
4205 }
4206 
4208  return UnperformedPenaltyOrValue(0, var_index);
4209 }
4210 
4212  int64 var_index) const {
4213  if (active_[var_index]->Min() == 1) return kint64max; // Forced active.
4214  const std::vector<DisjunctionIndex>& disjunction_indices =
4215  GetDisjunctionIndices(var_index);
4216  if (disjunction_indices.size() != 1) return default_value;
4217  const DisjunctionIndex disjunction_index = disjunction_indices[0];
4218  // The disjunction penalty can be kNoPenalty iff there is more than one node
4219  // in the disjunction; otherwise we would have caught it earlier (the node
4220  // would be forced active).
4221  return std::max(int64{0}, disjunctions_[disjunction_index].value.penalty);
4222 }
4223 
4225  const Assignment& solution_assignment,
4226  const std::string& dimension_to_print) const {
4227  for (int i = 0; i < Size(); ++i) {
4228  if (!solution_assignment.Bound(NextVar(i))) {
4229  LOG(DFATAL)
4230  << "DebugOutputVehicleSchedules() called on incomplete solution:"
4231  << " NextVar(" << i << ") is unbound.";
4232  return "";
4233  }
4234  }
4235  std::string output;
4236  absl::flat_hash_set<std::string> dimension_names;
4237  if (dimension_to_print.empty()) {
4238  const std::vector<std::string> all_dimension_names = GetAllDimensionNames();
4239  dimension_names.insert(all_dimension_names.begin(),
4240  all_dimension_names.end());
4241  } else {
4242  dimension_names.insert(dimension_to_print);
4243  }
4244  for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4245  int empty_vehicle_range_start = vehicle;
4246  while (vehicle < vehicles() &&
4247  IsEnd(solution_assignment.Value(NextVar(Start(vehicle))))) {
4248  vehicle++;
4249  }
4250  if (empty_vehicle_range_start != vehicle) {
4251  if (empty_vehicle_range_start == vehicle - 1) {
4252  absl::StrAppendFormat(&output, "Vehicle %d: empty",
4253  empty_vehicle_range_start);
4254  } else {
4255  absl::StrAppendFormat(&output, "Vehicles %d-%d: empty",
4256  empty_vehicle_range_start, vehicle - 1);
4257  }
4258  output.append("\n");
4259  }
4260  if (vehicle < vehicles()) {
4261  absl::StrAppendFormat(&output, "Vehicle %d:", vehicle);
4262  int64 index = Start(vehicle);
4263  for (;;) {
4264  const IntVar* vehicle_var = VehicleVar(index);
4265  absl::StrAppendFormat(&output, "%d Vehicle(%d) ", index,
4266  solution_assignment.Value(vehicle_var));
4267  for (const RoutingDimension* const dimension : dimensions_) {
4268  if (gtl::ContainsKey(dimension_names, dimension->name())) {
4269  const IntVar* const var = dimension->CumulVar(index);
4270  absl::StrAppendFormat(&output, "%s(%d..%d) ", dimension->name(),
4271  solution_assignment.Min(var),
4272  solution_assignment.Max(var));
4273  }
4274  }
4275  if (IsEnd(index)) break;
4276  index = solution_assignment.Value(NextVar(index));
4277  if (IsEnd(index)) output.append("Route end ");
4278  }
4279  output.append("\n");
4280  }
4281  }
4282  output.append("Unperformed nodes: ");
4283  bool has_unperformed = false;
4284  for (int i = 0; i < Size(); ++i) {
4285  if (!IsEnd(i) && !IsStart(i) &&
4286  solution_assignment.Value(NextVar(i)) == i) {
4287  absl::StrAppendFormat(&output, "%d ", i);
4288  has_unperformed = true;
4289  }
4290  }
4291  if (!has_unperformed) output.append("None");
4292  output.append("\n");
4293  return output;
4294 }
4295 
4296 #ifndef SWIG
4297 std::vector<std::vector<std::pair<int64, int64>>> RoutingModel::GetCumulBounds(
4298  const Assignment& solution_assignment, const RoutingDimension& dimension) {
4299  std::vector<std::vector<std::pair<int64, int64>>> cumul_bounds(vehicles());
4300  for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4301  if (!solution_assignment.Bound(NextVar(vehicle))) {
4302  LOG(DFATAL) << "GetCumulBounds() called on incomplete solution:"
4303  << " NextVar(" << vehicle << ") is unbound.";
4304  }
4305  }
4306 
4307  for (int vehicle_id = 0; vehicle_id < vehicles(); ++vehicle_id) {
4308  int64 index = Start(vehicle_id);
4309  IntVar* dim_var = dimension.CumulVar(index);
4310  cumul_bounds[vehicle_id].emplace_back(solution_assignment.Min(dim_var),
4311  solution_assignment.Max(dim_var));
4312  while (!IsEnd(index)) {
4313  index = solution_assignment.Value(NextVar(index));
4314  IntVar* dim_var = dimension.CumulVar(index);
4315  cumul_bounds[vehicle_id].emplace_back(solution_assignment.Min(dim_var),
4316  solution_assignment.Max(dim_var));
4317  }
4318  }
4319  return cumul_bounds;
4320 }
4321 #endif
4322 
4323 Assignment* RoutingModel::GetOrCreateAssignment() {
4324  if (assignment_ == nullptr) {
4325  assignment_ = solver_->MakeAssignment();
4326  assignment_->Add(nexts_);
4328  assignment_->Add(vehicle_vars_);
4329  }
4330  assignment_->AddObjective(cost_);
4331  }
4332  return assignment_;
4333 }
4334 
4335 Assignment* RoutingModel::GetOrCreateTmpAssignment() {
4336  if (tmp_assignment_ == nullptr) {
4337  tmp_assignment_ = solver_->MakeAssignment();
4338  tmp_assignment_->Add(nexts_);
4339  }
4340  return tmp_assignment_;
4341 }
4342 
4343 RegularLimit* RoutingModel::GetOrCreateLimit() {
4344  if (limit_ == nullptr) {
4345  limit_ = solver_->MakeLimit(absl::InfiniteDuration(), kint64max, kint64max,
4346  kint64max, /*smart_time_check=*/true);
4347  }
4348  return limit_;
4349 }
4350 
4351 RegularLimit* RoutingModel::GetOrCreateLocalSearchLimit() {
4352  if (ls_limit_ == nullptr) {
4353  ls_limit_ =
4354  solver_->MakeLimit(absl::InfiniteDuration(), kint64max, kint64max,
4355  /*solutions=*/1, /*smart_time_check=*/true);
4356  }
4357  return ls_limit_;
4358 }
4359 
4360 RegularLimit* RoutingModel::GetOrCreateLargeNeighborhoodSearchLimit() {
4361  if (lns_limit_ == nullptr) {
4362  lns_limit_ =
4363  solver_->MakeLimit(absl::InfiniteDuration(), kint64max, kint64max,
4364  kint64max, /*smart_time_check=*/false);
4365  }
4366  return lns_limit_;
4367 }
4368 
4369 RegularLimit*
4370 RoutingModel::GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit() {
4371  if (first_solution_lns_limit_ == nullptr) {
4372  first_solution_lns_limit_ =
4373  solver_->MakeLimit(absl::InfiniteDuration(), kint64max, kint64max,
4374  kint64max, /*smart_time_check=*/false);
4375  }
4376  return first_solution_lns_limit_;
4377 }
4378 
4379 LocalSearchOperator* RoutingModel::CreateInsertionOperator() {
4380  std::vector<IntVar*> empty;
4381  LocalSearchOperator* insertion_operator =
4382  MakeLocalSearchOperator<MakeActiveOperator>(
4383  solver_.get(), nexts_,
4384  CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_,
4385  vehicle_start_class_callback_);
4386  if (!pickup_delivery_pairs_.empty()) {
4387  insertion_operator = solver_->ConcatenateOperators(
4388  {MakePairActive(
4389  solver_.get(), nexts_,
4390  CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_,
4391  vehicle_start_class_callback_, pickup_delivery_pairs_),
4392  insertion_operator});
4393  }
4394  return insertion_operator;
4395 }
4396 
4397 LocalSearchOperator* RoutingModel::CreateMakeInactiveOperator() {
4398  std::vector<IntVar*> empty;
4399  LocalSearchOperator* make_inactive_operator =
4400  MakeLocalSearchOperator<MakeInactiveOperator>(
4401  solver_.get(), nexts_,
4402  CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_,
4403  vehicle_start_class_callback_);
4404  if (!pickup_delivery_pairs_.empty()) {
4405  make_inactive_operator = solver_->ConcatenateOperators(
4406  {MakePairInactive(
4407  solver_.get(), nexts_,
4408  CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_,
4409  vehicle_start_class_callback_, pickup_delivery_pairs_),
4410  make_inactive_operator});
4411  }
4412  return make_inactive_operator;
4413 }
4414 
4415 #define CP_ROUTING_ADD_OPERATOR(operator_type, cp_operator_type) \
4416  if (CostsAreHomogeneousAcrossVehicles()) { \
4417  local_search_operators_[operator_type] = \
4418  solver_->MakeOperator(nexts_, Solver::cp_operator_type); \
4419  } else { \
4420  local_search_operators_[operator_type] = solver_->MakeOperator( \
4421  nexts_, vehicle_vars_, Solver::cp_operator_type); \
4422  }
4423 
4424 #define CP_ROUTING_ADD_OPERATOR2(operator_type, cp_operator_class) \
4425  local_search_operators_[operator_type] = \
4426  MakeLocalSearchOperator<cp_operator_class>( \
4427  solver_.get(), nexts_, \
4428  CostsAreHomogeneousAcrossVehicles() ? std::vector<IntVar*>() \
4429  : vehicle_vars_, \
4430  vehicle_start_class_callback_);
4431 
4432 #define CP_ROUTING_ADD_CALLBACK_OPERATOR(operator_type, cp_operator_type) \
4433  if (CostsAreHomogeneousAcrossVehicles()) { \
4434  local_search_operators_[operator_type] = solver_->MakeOperator( \
4435  nexts_, \
4436  [this](int64 i, int64 j, int64 k) { \
4437  return GetArcCostForVehicle(i, j, k); \
4438  }, \
4439  Solver::cp_operator_type); \
4440  } else { \
4441  local_search_operators_[operator_type] = solver_->MakeOperator( \
4442  nexts_, vehicle_vars_, \
4443  [this](int64 i, int64 j, int64 k) { \
4444  return GetArcCostForVehicle(i, j, k); \
4445  }, \
4446  Solver::cp_operator_type); \
4447  }
4448 
4449 void RoutingModel::CreateNeighborhoodOperators(
4450  const RoutingSearchParameters& parameters) {
4451  local_search_operators_.clear();
4452  local_search_operators_.resize(LOCAL_SEARCH_OPERATOR_COUNTER, nullptr);
4453  CP_ROUTING_ADD_OPERATOR2(RELOCATE, Relocate);
4454  std::vector<IntVar*> empty;
4455  local_search_operators_[RELOCATE_PAIR] = MakePairRelocate(
4456  solver_.get(), nexts_,
4457  CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_,
4458  vehicle_start_class_callback_, pickup_delivery_pairs_);
4459  local_search_operators_[LIGHT_RELOCATE_PAIR] = MakeLightPairRelocate(
4460  solver_.get(), nexts_,
4461  CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_,
4462  vehicle_start_class_callback_, pickup_delivery_pairs_);
4463  local_search_operators_[EXCHANGE_PAIR] = MakePairExchange(
4464  solver_.get(), nexts_,
4465  CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_,
4466  vehicle_start_class_callback_, pickup_delivery_pairs_);
4467  local_search_operators_[EXCHANGE_RELOCATE_PAIR] = MakePairExchangeRelocate(
4468  solver_.get(), nexts_,
4469  CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_,
4470  vehicle_start_class_callback_, pickup_delivery_pairs_);
4471  local_search_operators_[RELOCATE_NEIGHBORS] = MakeRelocateNeighbors(
4472  solver_.get(), nexts_,
4473  CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_,
4474  vehicle_start_class_callback_,
4475  [this](int64 from, int64 to) { return GetHomogeneousCost(from, to); });
4476  local_search_operators_[NODE_PAIR_SWAP] = solver_->ConcatenateOperators(
4477  {IndexPairSwapActive(
4478  solver_.get(), nexts_,
4479  CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_,
4480  vehicle_start_class_callback_, pickup_delivery_pairs_),
4481  SwapIndexPair(
4482  solver_.get(), nexts_,
4483  CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_,
4484  pickup_delivery_pairs_),
4485  PairNodeSwapActive(
4486  solver_.get(), nexts_,
4487  CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_,
4488  vehicle_start_class_callback_, pickup_delivery_pairs_)});
4489  const auto arc_cost_for_path_start =
4490  [this](int64 before_node, int64 after_node, int64 start_index) {
4491  const int vehicle = index_to_vehicle_[start_index];
4492  const int64 arc_cost =
4493  GetArcCostForVehicle(before_node, after_node, vehicle);
4494  return (before_node != start_index || IsEnd(after_node))
4495  ? arc_cost
4496  : CapSub(arc_cost, GetFixedCostOfVehicle(vehicle));
4497  };
4498  GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
4499  ls_gci_parameters = {
4500  /* is_sequential */ false,
4501  /* farthest_seeds_ratio */ 0.0,
4502  parameters.cheapest_insertion_ls_operator_neighbors_ratio(),
4503  /* use_neighbors_ratio_for_initialization */ true};
4504  local_search_operators_[GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4505  solver_->RevAlloc(new FilteredHeuristicCloseNodesLNSOperator(
4506  absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4507  this,
4508  [this](int64 i, int64 j, int64 vehicle) {
4509  return GetArcCostForVehicle(i, j, vehicle);
4510  },
4511  [this](int64 i) { return UnperformedPenaltyOrValue(0, i); },
4512  GetOrCreateFeasibilityFilterManager(parameters),
4513  ls_gci_parameters),
4514  parameters.heuristic_close_nodes_lns_num_nodes()));
4515 
4516  local_search_operators_[LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4517  solver_->RevAlloc(new FilteredHeuristicCloseNodesLNSOperator(
4518  absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4519  this,
4520  [this](int64 i, int64 j, int64 vehicle) {
4521  return GetArcCostForVehicle(i, j, vehicle);
4522  },
4523  GetOrCreateFeasibilityFilterManager(parameters)),
4524  parameters.heuristic_close_nodes_lns_num_nodes()));
4525 
4526  local_search_operators_[GLOBAL_CHEAPEST_INSERTION_PATH_LNS] =
4527  solver_->RevAlloc(new FilteredHeuristicPathLNSOperator(
4528  absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4529  this,
4530  [this](int64 i, int64 j, int64 vehicle) {
4531  return GetArcCostForVehicle(i, j, vehicle);
4532  },
4533  [this](int64 i) { return UnperformedPenaltyOrValue(0, i); },
4534  GetOrCreateFeasibilityFilterManager(parameters),
4535  ls_gci_parameters)));
4536 
4537  local_search_operators_[LOCAL_CHEAPEST_INSERTION_PATH_LNS] =
4538  solver_->RevAlloc(new FilteredHeuristicPathLNSOperator(
4539  absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4540  this,
4541  [this](int64 i, int64 j, int64 vehicle) {
4542  return GetArcCostForVehicle(i, j, vehicle);
4543  },
4544  GetOrCreateFeasibilityFilterManager(parameters))));
4545  local_search_operators_[GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4546  solver_->RevAlloc(new FilteredHeuristicExpensiveChainLNSOperator(
4547  absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4548  this,
4549  [this](int64 i, int64 j, int64 vehicle) {
4550  return GetArcCostForVehicle(i, j, vehicle);
4551  },
4552  [this](int64 i) { return UnperformedPenaltyOrValue(0, i); },
4553  GetOrCreateFeasibilityFilterManager(parameters),
4554  ls_gci_parameters),
4555  parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4556  arc_cost_for_path_start));
4557 
4558  local_search_operators_[LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4559  solver_->RevAlloc(new FilteredHeuristicExpensiveChainLNSOperator(
4560  absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4561  this,
4562  [this](int64 i, int64 j, int64 vehicle) {
4563  return GetArcCostForVehicle(i, j, vehicle);
4564  },
4565  GetOrCreateFeasibilityFilterManager(parameters)),
4566  parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4567  arc_cost_for_path_start));
4568  local_search_operators_[RELOCATE_EXPENSIVE_CHAIN] =
4569  solver_->RevAlloc(new RelocateExpensiveChain(
4570  nexts_, CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_,
4571  vehicle_start_class_callback_,
4572  parameters.relocate_expensive_chain_num_arcs_to_consider(),
4573  arc_cost_for_path_start));
4574  local_search_operators_[RELOCATE_SUBTRIP] = MakeRelocateSubtrip(
4575  solver_.get(), nexts_,
4576  CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_,
4577  vehicle_start_class_callback_, pickup_delivery_pairs_);
4578  local_search_operators_[EXCHANGE_SUBTRIP] = MakeExchangeSubtrip(
4579  solver_.get(), nexts_,
4580  CostsAreHomogeneousAcrossVehicles() ? empty : vehicle_vars_,
4581  vehicle_start_class_callback_, pickup_delivery_pairs_);
4582 
4583  CP_ROUTING_ADD_OPERATOR2(EXCHANGE, Exchange);
4584  CP_ROUTING_ADD_OPERATOR2(CROSS, Cross);
4585  CP_ROUTING_ADD_OPERATOR2(TWO_OPT, TwoOpt);
4586  CP_ROUTING_ADD_OPERATOR(OR_OPT, OROPT);
4587  CP_ROUTING_ADD_CALLBACK_OPERATOR(LIN_KERNIGHAN, LK);
4588  local_search_operators_[MAKE_ACTIVE] = CreateInsertionOperator();
4589  CP_ROUTING_ADD_OPERATOR2(RELOCATE_AND_MAKE_ACTIVE,
4590  RelocateAndMakeActiveOperator);
4591  CP_ROUTING_ADD_OPERATOR2(MAKE_ACTIVE_AND_RELOCATE, MakeActiveAndRelocate);
4592  local_search_operators_[MAKE_INACTIVE] = CreateMakeInactiveOperator();
4593  CP_ROUTING_ADD_OPERATOR2(MAKE_CHAIN_INACTIVE, MakeChainInactiveOperator);
4594  CP_ROUTING_ADD_OPERATOR2(SWAP_ACTIVE, SwapActiveOperator);
4595  CP_ROUTING_ADD_OPERATOR2(EXTENDED_SWAP_ACTIVE, ExtendedSwapActiveOperator);
4596  CP_ROUTING_ADD_CALLBACK_OPERATOR(TSP_OPT, TSPOPT);
4597  CP_ROUTING_ADD_CALLBACK_OPERATOR(TSP_LNS, TSPLNS);
4598  CP_ROUTING_ADD_OPERATOR(PATH_LNS, PATHLNS);
4599  CP_ROUTING_ADD_OPERATOR(FULL_PATH_LNS, FULLPATHLNS);
4600  CP_ROUTING_ADD_OPERATOR(INACTIVE_LNS, UNACTIVELNS);
4601 }
4602 
4603 #undef CP_ROUTING_ADD_CALLBACK_OPERATOR
4604 #undef CP_ROUTING_ADD_OPERATOR2
4605 #undef CP_ROUTING_ADD_OPERATOR
4606 
4607 #define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators) \
4608  if (search_parameters.local_search_operators().use_##operator_method() == \
4609  BOOL_TRUE) { \
4610  operators.push_back(local_search_operators_[operator_type]); \
4611  }
4612 
4613 LocalSearchOperator* RoutingModel::GetNeighborhoodOperators(
4614  const RoutingSearchParameters& search_parameters) const {
4615  std::vector<LocalSearchOperator*> operator_groups;
4616  std::vector<LocalSearchOperator*> operators = extra_operators_;
4617  if (!pickup_delivery_pairs_.empty()) {
4618  CP_ROUTING_PUSH_OPERATOR(RELOCATE_PAIR, relocate_pair, operators);
4619  // Only add the light version of relocate pair if the normal version has not
4620  // already been added as it covers a subset of its neighborhood.
4621  if (search_parameters.local_search_operators().use_relocate_pair() ==
4622  BOOL_FALSE) {
4623  CP_ROUTING_PUSH_OPERATOR(LIGHT_RELOCATE_PAIR, light_relocate_pair,
4624  operators);
4625  }
4626  CP_ROUTING_PUSH_OPERATOR(EXCHANGE_PAIR, exchange_pair, operators);
4627  CP_ROUTING_PUSH_OPERATOR(NODE_PAIR_SWAP, node_pair_swap_active, operators);
4628  CP_ROUTING_PUSH_OPERATOR(RELOCATE_SUBTRIP, relocate_subtrip, operators);
4629  CP_ROUTING_PUSH_OPERATOR(EXCHANGE_SUBTRIP, exchange_subtrip, operators);
4630  }
4631  if (vehicles_ > 1) {
4632  if (GetNumOfSingletonNodes() > 0) {
4633  // If there are only pairs in the model the only case where Relocate will
4634  // work is for intra-route moves, already covered by OrOpt.
4635  // We are not disabling Exchange and Cross because there are no
4636  // intra-route equivalents.
4637  CP_ROUTING_PUSH_OPERATOR(RELOCATE, relocate, operators);
4638  }
4639  CP_ROUTING_PUSH_OPERATOR(EXCHANGE, exchange, operators);
4640  CP_ROUTING_PUSH_OPERATOR(CROSS, cross, operators);
4641  }
4642  if (!pickup_delivery_pairs_.empty() ||
4643  search_parameters.local_search_operators().use_relocate_neighbors() ==
4644  BOOL_TRUE) {
4645  operators.push_back(local_search_operators_[RELOCATE_NEIGHBORS]);
4646  }
4647  const LocalSearchMetaheuristic::Value local_search_metaheuristic =
4648  search_parameters.local_search_metaheuristic();
4649  if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4650  local_search_metaheuristic !=
4651  LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4652  local_search_metaheuristic !=
4653  LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4654  CP_ROUTING_PUSH_OPERATOR(LIN_KERNIGHAN, lin_kernighan, operators);
4655  }
4656  CP_ROUTING_PUSH_OPERATOR(TWO_OPT, two_opt, operators);
4657  CP_ROUTING_PUSH_OPERATOR(OR_OPT, or_opt, operators);
4658  CP_ROUTING_PUSH_OPERATOR(RELOCATE_EXPENSIVE_CHAIN, relocate_expensive_chain,
4659  operators);
4660  if (!disjunctions_.empty()) {
4661  CP_ROUTING_PUSH_OPERATOR(MAKE_INACTIVE, make_inactive, operators);
4662  CP_ROUTING_PUSH_OPERATOR(MAKE_CHAIN_INACTIVE, make_chain_inactive,
4663  operators);
4664  CP_ROUTING_PUSH_OPERATOR(MAKE_ACTIVE, make_active, operators);
4665 
4666  // The relocate_and_make_active parameter activates all neighborhoods
4667  // relocating a node together with making another active.
4668  CP_ROUTING_PUSH_OPERATOR(RELOCATE_AND_MAKE_ACTIVE, relocate_and_make_active,
4669  operators);
4670  CP_ROUTING_PUSH_OPERATOR(MAKE_ACTIVE_AND_RELOCATE, relocate_and_make_active,
4671  operators);
4672 
4673  CP_ROUTING_PUSH_OPERATOR(SWAP_ACTIVE, swap_active, operators);
4674  CP_ROUTING_PUSH_OPERATOR(EXTENDED_SWAP_ACTIVE, extended_swap_active,
4675  operators);
4676  }
4677  operator_groups.push_back(solver_->ConcatenateOperators(operators));
4678 
4679  // Second local search loop: LNS-like operators.
4680  operators.clear();
4681  if (vehicles() > 1) {
4682  // NOTE: The following heuristic path LNS with a single vehicle are
4683  // equivalent to using the heuristic as first solution strategy, so we only
4684  // add these moves if we have at least 2 vehicles in the model.
4685  CP_ROUTING_PUSH_OPERATOR(GLOBAL_CHEAPEST_INSERTION_PATH_LNS,
4686  global_cheapest_insertion_path_lns, operators);
4687  CP_ROUTING_PUSH_OPERATOR(LOCAL_CHEAPEST_INSERTION_PATH_LNS,
4688  local_cheapest_insertion_path_lns, operators);
4689  }
4690  CP_ROUTING_PUSH_OPERATOR(GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS,
4691  global_cheapest_insertion_expensive_chain_lns,
4692  operators);
4693  CP_ROUTING_PUSH_OPERATOR(LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS,
4694  local_cheapest_insertion_expensive_chain_lns,
4695  operators);
4696  CP_ROUTING_PUSH_OPERATOR(GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS,
4697  global_cheapest_insertion_close_nodes_lns,
4698  operators);
4699  CP_ROUTING_PUSH_OPERATOR(LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS,
4700  local_cheapest_insertion_close_nodes_lns, operators);
4701  operator_groups.push_back(solver_->ConcatenateOperators(operators));
4702 
4703  // Third local search loop: Expensive LNS operators.
4704  operators.clear();
4705  if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4706  local_search_metaheuristic !=
4707  LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4708  local_search_metaheuristic !=
4709  LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4710  CP_ROUTING_PUSH_OPERATOR(TSP_OPT, tsp_opt, operators);
4711  }
4712  if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4713  local_search_metaheuristic !=
4714  LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4715  local_search_metaheuristic !=
4716  LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4717  CP_ROUTING_PUSH_OPERATOR(TSP_LNS, tsp_lns, operators);
4718  }
4719  CP_ROUTING_PUSH_OPERATOR(FULL_PATH_LNS, full_path_lns, operators);
4720  CP_ROUTING_PUSH_OPERATOR(PATH_LNS, path_lns, operators);
4721  if (!disjunctions_.empty()) {
4722  CP_ROUTING_PUSH_OPERATOR(INACTIVE_LNS, inactive_lns, operators);
4723  }
4724  operator_groups.push_back(solver_->ConcatenateOperators(operators));
4725 
4726  return solver_->ConcatenateOperators(operator_groups);
4727 }
4728 
4729 #undef CP_ROUTING_PUSH_OPERATOR
4730 
4731 std::vector<LocalSearchFilter*> RoutingModel::GetOrCreateLocalSearchFilters(
4732  const RoutingSearchParameters& parameters) {
4733  // As of 2013/01, three filters evaluate sub-parts of the objective
4734  // function:
4735  // - NodeDisjunctionFilter: takes disjunction penalty costs into account,
4736  // - PathCumulFilter: takes dimension span costs into account,
4737  // - ObjectiveFilter:
4738  // - VehicleAmortizedCostFilter, which considers the part of the cost
4739  // related to amortized linear and quadratic vehicle cost factors.
4740  // - LocalSearchObjectiveFilter, which takes dimension "arc" costs into
4741  // account.
4742  std::vector<LocalSearchFilter*> filters;
4743  // VehicleAmortizedCostFilter can have a negative value, so it must be first.
4744  if (vehicle_amortized_cost_factors_set_) {
4745  filters.push_back(MakeVehicleAmortizedCostFilter(*this));
4746  }
4747 
4748  // The SumObjectiveFilter has the best reject/second ratio in practice,
4749  // so it is the earliest.
4751  filters.push_back(solver_->MakeSumObjectiveFilter(
4752  nexts_, [this](int64 i, int64 j) { return GetHomogeneousCost(i, j); },
4753  Solver::LE));
4754  } else {
4755  filters.push_back(solver_->MakeSumObjectiveFilter(
4756  nexts_, vehicle_vars_,
4757  [this](int64 i, int64 j, int64 k) {
4758  return GetArcCostForVehicle(i, j, k);
4759  },
4760  Solver::LE));
4761  }
4762 
4763  filters.push_back(solver_->MakeVariableDomainFilter());
4764 
4765  if (vehicles_ > max_active_vehicles_) {
4766  filters.push_back(MakeMaxActiveVehiclesFilter(*this));
4767  }
4768 
4769  if (!disjunctions_.empty()) {
4770  filters.push_back(MakeNodeDisjunctionFilter(*this));
4771  }
4772 
4773  if (!pickup_delivery_pairs_.empty()) {
4774  filters.push_back(MakePickupDeliveryFilter(
4775  *this, pickup_delivery_pairs_, vehicle_pickup_delivery_policy_));
4776  }
4777 
4778  if (HasTypeRegulations()) {
4779  filters.push_back(MakeTypeRegulationsFilter(*this));
4780  }
4781 
4782  filters.push_back(MakeVehicleVarFilter(*this));
4783 
4785  /*filter_objective_cost*/ true, &filters);
4786 
4787  for (const RoutingDimension* dimension : dimensions_) {
4788  if (!dimension->HasBreakConstraints()) continue;
4789  filters.push_back(MakeVehicleBreaksFilter(*this, *dimension));
4790  }
4791  filters.insert(filters.end(), extra_filters_.begin(), extra_filters_.end());
4792  return filters;
4793 }
4794 
4795 LocalSearchFilterManager* RoutingModel::GetOrCreateLocalSearchFilterManager(
4796  const RoutingSearchParameters& parameters) {
4797  if (!local_search_filter_manager_) {
4798  local_search_filter_manager_ = solver_->MakeLocalSearchFilterManager(
4799  GetOrCreateLocalSearchFilters(parameters));
4800  }
4801  return local_search_filter_manager_;
4802 }
4803 
4804 std::vector<LocalSearchFilter*> RoutingModel::GetOrCreateFeasibilityFilters(
4805  const RoutingSearchParameters& parameters) {
4806  std::vector<LocalSearchFilter*> filters;
4807  if (vehicles_ > max_active_vehicles_) {
4808  filters.push_back(MakeMaxActiveVehiclesFilter(*this));
4809  }
4810  if (!disjunctions_.empty()) {
4811  filters.push_back(MakeNodeDisjunctionFilter(*this));
4812  }
4813  filters.push_back(solver_->MakeVariableDomainFilter());
4814  if (!pickup_delivery_pairs_.empty()) {
4815  filters.push_back(MakePickupDeliveryFilter(
4816  *this, pickup_delivery_pairs_, vehicle_pickup_delivery_policy_));
4817  }
4818  if (HasTypeRegulations()) {
4819  filters.push_back(MakeTypeRegulationsFilter(*this));
4820  }
4821  filters.push_back(MakeVehicleVarFilter(*this));
4822 
4824  /*filter_objective_cost*/ false, &filters);
4825 
4826  for (const RoutingDimension* dimension : dimensions_) {
4827  if (dimension->HasBreakConstraints()) {
4828  IntVarLocalSearchFilter* breaks_filter =
4829  MakeVehicleBreaksFilter(*this, *dimension);
4830  filters.push_back(breaks_filter);
4831  }
4832  }
4833 
4834  filters.insert(filters.end(), extra_filters_.begin(), extra_filters_.end());
4835  return filters;
4836 }
4837 
4838 LocalSearchFilterManager* RoutingModel::GetOrCreateFeasibilityFilterManager(
4839  const RoutingSearchParameters& parameters) {
4840  if (!feasibility_filter_manager_) {
4841  feasibility_filter_manager_ = solver_->MakeLocalSearchFilterManager(
4842  GetOrCreateFeasibilityFilters(parameters));
4843  }
4844  return feasibility_filter_manager_;
4845 }
4846 
4847 LocalSearchFilterManager*
4848 RoutingModel::GetOrCreateStrongFeasibilityFilterManager(
4849  const RoutingSearchParameters& parameters) {
4850  if (!strong_feasibility_filter_manager_) {
4851  std::vector<LocalSearchFilter*> filters =
4852  GetOrCreateFeasibilityFilters(parameters);
4853  filters.push_back(MakeCPFeasibilityFilter(this));
4854  strong_feasibility_filter_manager_ =
4855  solver_->MakeLocalSearchFilterManager(std::move(filters));
4856  }
4857  return strong_feasibility_filter_manager_;
4858 }
4859 
4860 namespace {
4861 bool AllTransitsPositive(const RoutingDimension& dimension) {
4862  for (int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
4863  if (!dimension.AreVehicleTransitsPositive(vehicle)) {
4864  return false;
4865  }
4866  }
4867  return true;
4868 }
4869 } // namespace
4870 
4871 void RoutingModel::StoreDimensionCumulOptimizers(
4872  const RoutingSearchParameters& parameters) {
4873  Assignment* packed_dimensions_collector_assignment =
4874  solver_->MakeAssignment();
4875  const int num_dimensions = dimensions_.size();
4876  local_optimizer_index_.resize(num_dimensions, -1);
4877  global_optimizer_index_.resize(num_dimensions, -1);
4878  for (DimensionIndex dim = DimensionIndex(0); dim < num_dimensions; dim++) {
4879  RoutingDimension* dimension = dimensions_[dim];
4880  if (dimension->global_span_cost_coefficient() > 0 ||
4881  !dimension->GetNodePrecedences().empty()) {
4882  // Use global optimizer.
4883  global_optimizer_index_[dim] = global_dimension_optimizers_.size();
4884  global_dimension_optimizers_.push_back(
4885  absl::make_unique<GlobalDimensionCumulOptimizer>(dimension));
4886  packed_dimensions_collector_assignment->Add(dimension->cumuls());
4887  if (!AllTransitsPositive(*dimension)) {
4888  dimension->SetOffsetForGlobalOptimizer(0);
4889  continue;
4890  }
4891  int64 offset = vehicles() == 0 ? 0 : kint64max;
4892  for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4893  DCHECK_GE(dimension->CumulVar(Start(vehicle))->Min(), 0);
4894  offset =
4895  std::min(offset, dimension->CumulVar(Start(vehicle))->Min() - 1);
4896  }
4897  dimension->SetOffsetForGlobalOptimizer(std::max(Zero(), offset));
4898  } else {
4899  bool has_span_cost = false;
4900  bool has_span_limit = false;
4901  std::vector<int64> vehicle_offsets(vehicles());
4902  for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4903  if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4904  has_span_cost = true;
4905  }
4906  if (dimension->GetSpanUpperBoundForVehicle(vehicle) < kint64max) {
4907  has_span_limit = true;
4908  }
4909  DCHECK_GE(dimension->CumulVar(Start(vehicle))->Min(), 0);
4910  vehicle_offsets[vehicle] =
4911  dimension->AreVehicleTransitsPositive(vehicle)
4912  ? std::max(Zero(),
4913  dimension->CumulVar(Start(vehicle))->Min() - 1)
4914  : 0;
4915  }
4916  bool has_soft_lower_bound = false;
4917  bool has_soft_upper_bound = false;
4918  for (int i = 0; i < dimension->cumuls().size(); ++i) {
4919  if (dimension->HasCumulVarSoftLowerBound(i)) {
4920  has_soft_lower_bound = true;
4921  }
4922  if (dimension->HasCumulVarSoftUpperBound(i)) {
4923  has_soft_upper_bound = true;
4924  }
4925  }
4926  int num_linear_constraints = 0;
4927  if (has_span_cost) ++num_linear_constraints;
4928  if (has_span_limit) ++num_linear_constraints;
4929  if (dimension->HasSoftSpanUpperBounds()) ++num_linear_constraints;
4930  if (has_soft_lower_bound) ++num_linear_constraints;
4931  if (has_soft_upper_bound) ++num_linear_constraints;
4932  if (dimension->HasBreakConstraints()) ++num_linear_constraints;
4933  if (num_linear_constraints >= 2) {
4934  dimension->SetVehicleOffsetsForLocalOptimizer(
4935  std::move(vehicle_offsets));
4936  local_optimizer_index_[dim] = local_dimension_optimizers_.size();
4937  local_dimension_optimizers_.push_back(
4938  absl::make_unique<LocalDimensionCumulOptimizer>(
4939  dimension, parameters.continuous_scheduling_solver()));
4940  bool has_intervals = false;
4941  for (const SortedDisjointIntervalList& intervals :
4942  dimension->forbidden_intervals()) {
4943  // TODO(user): Change the following test to check intervals within
4944  // the domain of the corresponding variables.
4945  if (intervals.NumIntervals() > 0) {
4946  has_intervals = true;
4947  break;
4948  }
4949  }
4950  if (dimension->HasBreakConstraints() || has_intervals) {
4951  local_dimension_mp_optimizers_.push_back(
4952  absl::make_unique<LocalDimensionCumulOptimizer>(
4953  dimension, parameters.mixed_integer_scheduling_solver()));
4954  } else {
4955  local_dimension_mp_optimizers_.push_back(nullptr);
4956  }
4957  packed_dimensions_collector_assignment->Add(dimension->cumuls());
4958  }
4959  }
4960  DCHECK_EQ(local_dimension_mp_optimizers_.size(),
4961  local_dimension_optimizers_.size());
4962  }
4963 
4964  // NOTE(b/129252839): We also add all other extra variables to the
4965  // packed_dimensions_collector_assignment to make sure the necessary
4966  // propagations on these variables after packing are correctly stored.
4967  for (IntVar* const extra_var : extra_vars_) {
4968  packed_dimensions_collector_assignment->Add(extra_var);
4969  }
4970  for (IntervalVar* const extra_interval : extra_intervals_) {
4971  packed_dimensions_collector_assignment->Add(extra_interval);
4972  }
4973 
4974  packed_dimensions_assignment_collector_ = solver_->MakeFirstSolutionCollector(
4975  packed_dimensions_collector_assignment);
4976 }
4977 
4978 std::vector<RoutingDimension*> RoutingModel::GetDimensionsWithSoftOrSpanCosts()
4979  const {
4980  std::vector<RoutingDimension*> dimensions;
4981  for (RoutingDimension* dimension : dimensions_) {
4982  bool has_soft_or_span_cost = false;
4983  for (int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4984  if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4985  has_soft_or_span_cost = true;
4986  break;
4987  }
4988  }
4989  if (!has_soft_or_span_cost) {
4990  for (int i = 0; i < dimension->cumuls().size(); ++i) {
4991  if (dimension->HasCumulVarSoftUpperBound(i) ||
4992  dimension->HasCumulVarSoftLowerBound(i)) {
4993  has_soft_or_span_cost = true;
4994  break;
4995  }
4996  }
4997  }
4998  if (has_soft_or_span_cost) dimensions.push_back(dimension);
4999  }
5000  return dimensions;
5001 }
5002 
5003 DecisionBuilder*
5004 RoutingModel::CreateFinalizerForMinimizedAndMaximizedVariables() {
5005  std::stable_sort(finalizer_variable_cost_pairs_.begin(),
5006  finalizer_variable_cost_pairs_.end(),
5007  [](const std::pair<IntVar*, int64>& var_cost1,
5008  const std::pair<IntVar*, int64>& var_cost2) {
5009  return var_cost1.second > var_cost2.second;
5010  });
5011  const int num_variables = finalizer_variable_cost_pairs_.size() +
5012  finalizer_variable_target_pairs_.size();
5013  std::vector<IntVar*> variables;
5014  std::vector<int64> targets;
5015  variables.reserve(num_variables);
5016  targets.reserve(num_variables);
5017  for (const auto& variable_cost : finalizer_variable_cost_pairs_) {
5018  variables.push_back(variable_cost.first);
5019  targets.push_back(kint64min);
5020  }
5021  for (const auto& variable_target : finalizer_variable_target_pairs_) {
5022  variables.push_back(variable_target.first);
5023  targets.push_back(variable_target.second);
5024  }
5025  return MakeSetValuesFromTargets(solver(), std::move(variables),
5026  std::move(targets));
5027 }
5028 
5029 DecisionBuilder* RoutingModel::CreateSolutionFinalizer(SearchLimit* lns_limit) {
5030  std::vector<DecisionBuilder*> decision_builders;
5031  decision_builders.push_back(solver_->MakePhase(
5032  nexts_, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE));
5033 
5034  if (!local_dimension_optimizers_.empty()) {
5035  decision_builders.push_back(
5036  solver_->RevAlloc(new SetCumulsFromLocalDimensionCosts(
5037  &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
5038  lns_limit)));
5039  }
5040  if (!global_dimension_optimizers_.empty()) {
5041  decision_builders.push_back(
5042  solver_->RevAlloc(new SetCumulsFromGlobalDimensionCosts(
5043  &global_dimension_optimizers_, lns_limit)));
5044  }
5045  decision_builders.push_back(
5046  CreateFinalizerForMinimizedAndMaximizedVariables());
5047 
5048  return solver_->Compose(decision_builders);
5049 }
5050 
5051 void RoutingModel::CreateFirstSolutionDecisionBuilders(
5052  const RoutingSearchParameters& search_parameters) {
5053  first_solution_decision_builders_.resize(
5055  first_solution_filtered_decision_builders_.resize(
5057  DecisionBuilder* const finalize_solution =
5058  CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit());
5059  // Default heuristic
5060  first_solution_decision_builders_
5061  [FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE] = finalize_solution;
5062  // Global cheapest addition heuristic.
5063  first_solution_decision_builders_
5064  [FirstSolutionStrategy::GLOBAL_CHEAPEST_ARC] = solver_->MakePhase(
5065  nexts_,
5066  [this](int64 i, int64 j) { return GetArcCostForFirstSolution(i, j); },
5067  Solver::CHOOSE_STATIC_GLOBAL_BEST);
5068  // Cheapest addition heuristic.
5069  Solver::IndexEvaluator2 eval = [this](int64 i, int64 j) {
5070  return GetArcCostForFirstSolution(i, j);
5071  };
5072  first_solution_decision_builders_[FirstSolutionStrategy::LOCAL_CHEAPEST_ARC] =
5073  solver_->MakePhase(nexts_, Solver::CHOOSE_FIRST_UNBOUND, eval);
5074  // Path-based cheapest addition heuristic.
5075  first_solution_decision_builders_[FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5076  solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, eval);
5077  if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5078  first_solution_filtered_decision_builders_
5079  [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5080  solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5081  absl::make_unique<EvaluatorCheapestAdditionFilteredHeuristic>(
5082  this,
5083  [this](int64 i, int64 j) {
5084  return GetArcCostForFirstSolution(i, j);
5085  },
5086  GetOrCreateFeasibilityFilterManager(search_parameters))));
5087  first_solution_decision_builders_
5088  [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5089  solver_->Try(first_solution_filtered_decision_builders_
5090  [FirstSolutionStrategy::PATH_CHEAPEST_ARC],
5091  first_solution_decision_builders_
5092  [FirstSolutionStrategy::PATH_CHEAPEST_ARC]);
5093  }
5094  // Path-based most constrained arc addition heuristic.
5095  Solver::VariableValueComparator comp = [this](int64 i, int64 j, int64 k) {
5096  return ArcIsMoreConstrainedThanArc(i, j, k);
5097  };
5098 
5099  first_solution_decision_builders_
5100  [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5101  solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, comp);
5102  if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5103  first_solution_filtered_decision_builders_
5104  [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5105  solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5106  absl::make_unique<ComparatorCheapestAdditionFilteredHeuristic>(
5107  this, comp,
5108  GetOrCreateFeasibilityFilterManager(search_parameters))));
5109  first_solution_decision_builders_
5110  [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->Try(
5111  first_solution_filtered_decision_builders_
5112  [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC],
5113  first_solution_decision_builders_
5114  [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC]);
5115  }
5116  // Evaluator-based path heuristic.
5117  if (first_solution_evaluator_ != nullptr) {
5118  first_solution_decision_builders_
5119  [FirstSolutionStrategy::EVALUATOR_STRATEGY] = solver_->MakePhase(
5120  nexts_, Solver::CHOOSE_PATH, first_solution_evaluator_);
5121  } else {
5122  first_solution_decision_builders_
5123  [FirstSolutionStrategy::EVALUATOR_STRATEGY] = nullptr;
5124  }
5125  // All unperformed heuristic.
5126  first_solution_decision_builders_[FirstSolutionStrategy::ALL_UNPERFORMED] =
5127  solver_->RevAlloc(new AllUnperformed(this));
5128  // Best insertion heuristic.
5129  RegularLimit* const ls_limit =
5130  solver_->MakeLimit(GetTimeLimit(search_parameters), kint64max, kint64max,
5131  kint64max, /*smart_time_check=*/true);
5132  DecisionBuilder* const finalize = solver_->MakeSolveOnce(
5133  finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit());
5134  LocalSearchPhaseParameters* const insertion_parameters =
5135  solver_->MakeLocalSearchPhaseParameters(
5136  nullptr, CreateInsertionOperator(), finalize, ls_limit,
5137  GetOrCreateLocalSearchFilterManager(search_parameters));
5138  std::vector<IntVar*> decision_vars = nexts_;
5140  decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(),
5141  vehicle_vars_.end());
5142  }
5143  const int64 optimization_step = std::max(
5144  MathUtil::FastInt64Round(search_parameters.optimization_step()), One());
5145  first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5146  solver_->MakeNestedOptimize(
5147  solver_->MakeLocalSearchPhase(
5148  decision_vars, solver_->RevAlloc(new AllUnperformed(this)),
5149  insertion_parameters),
5150  GetOrCreateAssignment(), false, optimization_step);
5151  first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5152  solver_->Compose(first_solution_decision_builders_
5153  [FirstSolutionStrategy::BEST_INSERTION],
5154  finalize);
5155 
5156  // Parallel/Sequential Global cheapest insertion
5157  GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
5158  gci_parameters = {
5159  /* is_sequential */ false,
5160  search_parameters.cheapest_insertion_farthest_seeds_ratio(),
5161  search_parameters.cheapest_insertion_first_solution_neighbors_ratio(),
5162  /* use_neighbors_ratio_for_initialization */ false};
5163  for (bool is_sequential : {false, true}) {
5164  FirstSolutionStrategy::Value first_solution_strategy =
5165  is_sequential ? FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION
5166  : FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5167  gci_parameters.is_sequential = is_sequential;
5168 
5169  first_solution_filtered_decision_builders_[first_solution_strategy] =
5170  solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5171  absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5172  this,
5173  [this](int64 i, int64 j, int64 vehicle) {
5174  return GetArcCostForVehicle(i, j, vehicle);
5175  },
5176  [this](int64 i) { return UnperformedPenaltyOrValue(0, i); },
5177  GetOrCreateFeasibilityFilterManager(search_parameters),
5178  gci_parameters)));
5179  IntVarFilteredDecisionBuilder* const strong_gci =
5180  solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5181  absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5182  this,
5183  [this](int64 i, int64 j, int64 vehicle) {
5184  return GetArcCostForVehicle(i, j, vehicle);
5185  },
5186  [this](int64 i) { return UnperformedPenaltyOrValue(0, i); },
5187  GetOrCreateStrongFeasibilityFilterManager(search_parameters),
5188  gci_parameters)));
5189  first_solution_decision_builders_[first_solution_strategy] = solver_->Try(
5190  first_solution_filtered_decision_builders_[first_solution_strategy],
5191  solver_->Try(strong_gci, first_solution_decision_builders_
5192  [FirstSolutionStrategy::BEST_INSERTION]));
5193  }
5194 
5195  // Local cheapest insertion
5196  first_solution_filtered_decision_builders_
5197  [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] =
5198  solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5199  absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5200  this,
5201  [this](int64 i, int64 j, int64 vehicle) {
5202  return GetArcCostForVehicle(i, j, vehicle);
5203  },
5204  GetOrCreateFeasibilityFilterManager(search_parameters))));
5205  IntVarFilteredDecisionBuilder* const strong_lci =
5206  solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5207  absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5208  this,
5209  [this](int64 i, int64 j, int64 vehicle) {
5210  return GetArcCostForVehicle(i, j, vehicle);
5211  },
5212  GetOrCreateStrongFeasibilityFilterManager(search_parameters))));
5213  first_solution_decision_builders_
5214  [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = solver_->Try(
5215  first_solution_filtered_decision_builders_
5216  [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION],
5217  solver_->Try(strong_lci,
5218  first_solution_decision_builders_
5219  [FirstSolutionStrategy::BEST_INSERTION]));
5220  // Savings
5221  SavingsFilteredHeuristic::SavingsParameters savings_parameters;
5222  savings_parameters.neighbors_ratio =
5223  search_parameters.savings_neighbors_ratio();
5224  savings_parameters.max_memory_usage_bytes =
5225  search_parameters.savings_max_memory_usage_bytes();
5226  savings_parameters.add_reverse_arcs =
5227  search_parameters.savings_add_reverse_arcs();
5228  savings_parameters.arc_coefficient =
5229  search_parameters.savings_arc_coefficient();
5230  LocalSearchFilterManager* filter_manager = nullptr;
5231  if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5232  filter_manager = GetOrCreateFeasibilityFilterManager(search_parameters);
5233  }
5234 
5235  if (search_parameters.savings_parallel_routes()) {
5236  IntVarFilteredDecisionBuilder* savings_db =
5237  solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5238  absl::make_unique<ParallelSavingsFilteredHeuristic>(
5239  this, &manager_, savings_parameters, filter_manager)));
5240  if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5241  first_solution_filtered_decision_builders_
5242  [FirstSolutionStrategy::SAVINGS] = savings_db;
5243  }
5244 
5245  first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5246  solver_->Try(savings_db,
5247  solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5248  absl::make_unique<ParallelSavingsFilteredHeuristic>(
5249  this, &manager_, savings_parameters,
5250  GetOrCreateStrongFeasibilityFilterManager(
5251  search_parameters)))));
5252  } else {
5253  IntVarFilteredDecisionBuilder* savings_db =
5254  solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5255  absl::make_unique<SequentialSavingsFilteredHeuristic>(
5256  this, &manager_, savings_parameters, filter_manager)));
5257  if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5258  first_solution_filtered_decision_builders_
5259  [FirstSolutionStrategy::SAVINGS] = savings_db;
5260  }
5261 
5262  first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5263  solver_->Try(savings_db,
5264  solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5265  absl::make_unique<SequentialSavingsFilteredHeuristic>(
5266  this, &manager_, savings_parameters,
5267  GetOrCreateStrongFeasibilityFilterManager(
5268  search_parameters)))));
5269  }
5270  // Sweep
5271  first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5272  solver_->RevAlloc(new SweepBuilder(this, true));
5273  DecisionBuilder* sweep_builder =
5274  solver_->RevAlloc(new SweepBuilder(this, false));
5275  first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5276  solver_->Try(
5277  sweep_builder,
5278  first_solution_decision_builders_[FirstSolutionStrategy::SWEEP]);
5279  // Christofides
5280  first_solution_decision_builders_[FirstSolutionStrategy::CHRISTOFIDES] =
5281  solver_->RevAlloc(new IntVarFilteredDecisionBuilder(
5282  absl::make_unique<ChristofidesFilteredHeuristic>(
5283  this, GetOrCreateFeasibilityFilterManager(search_parameters),
5284  search_parameters.christofides_use_minimum_matching())));
5285  // Automatic
5286  // TODO(user): make this smarter.
5287  const bool has_precedences = std::any_of(
5288  dimensions_.begin(), dimensions_.end(),
5289  [](RoutingDimension* dim) { return !dim->GetNodePrecedences().empty(); });
5290  if (pickup_delivery_pairs_.empty() && !has_precedences) {
5291  automatic_first_solution_strategy_ =
5292  FirstSolutionStrategy::PATH_CHEAPEST_ARC;
5293  } else {
5294  automatic_first_solution_strategy_ =
5295  FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5296  }
5297  first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC] =
5298  first_solution_decision_builders_[automatic_first_solution_strategy_];
5299  first_solution_decision_builders_[FirstSolutionStrategy::UNSET] =
5300  first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC];
5301 }
5302 
5303 DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder(
5304  const RoutingSearchParameters& search_parameters) const {
5305  const FirstSolutionStrategy::Value first_solution_strategy =
5306  search_parameters.first_solution_strategy();
5307  if (first_solution_strategy < first_solution_decision_builders_.size()) {
5308  return first_solution_decision_builders_[first_solution_strategy];
5309  } else {
5310  return nullptr;
5311  }
5312 }
5313 
5314 IntVarFilteredDecisionBuilder*
5315 RoutingModel::GetFilteredFirstSolutionDecisionBuilderOrNull(
5316  const RoutingSearchParameters& search_parameters) const {
5317  const FirstSolutionStrategy::Value first_solution_strategy =
5318  search_parameters.first_solution_strategy();
5319  return first_solution_filtered_decision_builders_[first_solution_strategy];
5320 }
5321 
5322 LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters(
5323  const RoutingSearchParameters& search_parameters) {
5324  SearchLimit* lns_limit = GetOrCreateLargeNeighborhoodSearchLimit();
5325  return solver_->MakeLocalSearchPhaseParameters(
5326  CostVar(), GetNeighborhoodOperators(search_parameters),
5327  solver_->MakeSolveOnce(CreateSolutionFinalizer(lns_limit), lns_limit),
5328  GetOrCreateLocalSearchLimit(),
5329  GetOrCreateLocalSearchFilterManager(search_parameters));
5330 }
5331 
5332 DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder(
5333  const RoutingSearchParameters& search_parameters) {
5334  const int size = Size();
5335  DecisionBuilder* first_solution =
5336  GetFirstSolutionDecisionBuilder(search_parameters);
5337  LocalSearchPhaseParameters* const parameters =
5338  CreateLocalSearchParameters(search_parameters);
5339  SearchLimit* first_solution_lns_limit =
5340  GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5341  DecisionBuilder* const first_solution_sub_decision_builder =
5342  solver_->MakeSolveOnce(CreateSolutionFinalizer(first_solution_lns_limit),
5343  first_solution_lns_limit);
5345  return solver_->MakeLocalSearchPhase(nexts_, first_solution,
5346  first_solution_sub_decision_builder,
5347  parameters);
5348  } else {
5349  const int all_size = size + size + vehicles_;
5350  std::vector<IntVar*> all_vars(all_size);
5351  for (int i = 0; i < size; ++i) {
5352  all_vars[i] = nexts_[i];
5353  }
5354  for (int i = size; i < all_size; ++i) {
5355  all_vars[i] = vehicle_vars_[i - size];
5356  }
5357  return solver_->MakeLocalSearchPhase(all_vars, first_solution,
5358  first_solution_sub_decision_builder,
5359  parameters);
5360  }
5361 }
5362 
5363 void RoutingModel::SetupDecisionBuilders(
5364  const RoutingSearchParameters& search_parameters) {
5365  if (search_parameters.use_depth_first_search()) {
5366  SearchLimit* first_lns_limit =
5367  GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5368  solve_db_ = solver_->Compose(
5369  GetFirstSolutionDecisionBuilder(search_parameters),
5370  solver_->MakeSolveOnce(CreateSolutionFinalizer(first_lns_limit),
5371  first_lns_limit));
5372  } else {
5373  solve_db_ = CreateLocalSearchDecisionBuilder(search_parameters);
5374  }
5375  CHECK(preassignment_ != nullptr);
5376  DecisionBuilder* restore_preassignment =
5377  solver_->MakeRestoreAssignment(preassignment_);
5378  solve_db_ = solver_->Compose(restore_preassignment, solve_db_);
5379  improve_db_ =
5380  solver_->Compose(restore_preassignment,
5381  solver_->MakeLocalSearchPhase(
5382  GetOrCreateAssignment(),
5383  CreateLocalSearchParameters(search_parameters)));
5384  restore_assignment_ = solver_->Compose(
5385  solver_->MakeRestoreAssignment(GetOrCreateAssignment()),
5386  CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5387  restore_tmp_assignment_ = solver_->Compose(
5388  restore_preassignment,
5389  solver_->MakeRestoreAssignment(GetOrCreateTmpAssignment()),
5390  CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5391 }
5392 
5393 void RoutingModel::SetupMetaheuristics(
5394  const RoutingSearchParameters& search_parameters) {
5395  SearchMonitor* optimize;
5396  const LocalSearchMetaheuristic::Value metaheuristic =
5397  search_parameters.local_search_metaheuristic();
5398  // Some metaheuristics will effectively never terminate; warn
5399  // user if they fail to set a time limit.
5400  bool limit_too_long = !search_parameters.has_time_limit() &&
5401  search_parameters.solution_limit() == kint64max;
5402  const int64 optimization_step = std::max(
5403  MathUtil::FastInt64Round(search_parameters.optimization_step()), One());
5404  switch (metaheuristic) {
5405  case LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH:
5407  optimize = solver_->MakeGuidedLocalSearch(
5408  false, cost_,
5409  [this](int64 i, int64 j) { return GetHomogeneousCost(i, j); },
5410  optimization_step, nexts_,
5411  search_parameters.guided_local_search_lambda_coefficient());
5412  } else {
5413  optimize = solver_->MakeGuidedLocalSearch(
5414  false, cost_,
5415  [this](int64 i, int64 j, int64 k) {
5416  return GetArcCostForVehicle(i, j, k);
5417  },
5418  optimization_step, nexts_, vehicle_vars_,
5419  search_parameters.guided_local_search_lambda_coefficient());
5420  }
5421  break;
5422  case LocalSearchMetaheuristic::SIMULATED_ANNEALING:
5423  optimize =
5424  solver_->MakeSimulatedAnnealing(false, cost_, optimization_step, 100);
5425  break;
5426  case LocalSearchMetaheuristic::TABU_SEARCH:
5427  optimize = solver_->MakeTabuSearch(false, cost_, optimization_step,
5428  nexts_, 10, 10, .8);
5429  break;
5430  case LocalSearchMetaheuristic::GENERIC_TABU_SEARCH: {
5431  std::vector<operations_research::IntVar*> tabu_vars;
5432  if (tabu_var_callback_) {
5433  tabu_vars = tabu_var_callback_(this);
5434  } else {
5435  tabu_vars.push_back(cost_);
5436  }
5437  optimize = solver_->MakeGenericTabuSearch(false, cost_, optimization_step,
5438  tabu_vars, 100);
5439  break;
5440  }
5441  default:
5442  limit_too_long = false;
5443  optimize = solver_->MakeMinimize(cost_, optimization_step);
5444  }
5445  if (limit_too_long) {
5446  LOG(WARNING) << LocalSearchMetaheuristic::Value_Name(metaheuristic)
5447  << " specified without sane timeout: solve may run forever.";
5448  }
5449  monitors_.push_back(optimize);
5450 }
5451 
5453  tabu_var_callback_ = std::move(tabu_var_callback);
5454 }
5455 
5456 void RoutingModel::SetupAssignmentCollector(
5457  const RoutingSearchParameters& search_parameters) {
5458  Assignment* full_assignment = solver_->MakeAssignment();
5459  for (const RoutingDimension* const dimension : dimensions_) {
5460  full_assignment->Add(dimension->cumuls());
5461  }
5462  for (IntVar* const extra_var : extra_vars_) {
5463  full_assignment->Add(extra_var);
5464  }
5465  for (IntervalVar* const extra_interval : extra_intervals_) {
5466  full_assignment->Add(extra_interval);
5467  }
5468  full_assignment->Add(nexts_);
5469  full_assignment->Add(active_);
5470  full_assignment->Add(vehicle_vars_);
5471  full_assignment->AddObjective(cost_);
5472 
5473  collect_assignments_ = solver_->MakeNBestValueSolutionCollector(
5474  full_assignment, search_parameters.number_of_solutions_to_collect(),
5475  false);
5476  collect_one_assignment_ =
5477  solver_->MakeFirstSolutionCollector(full_assignment);
5478  monitors_.push_back(collect_assignments_);
5479 }
5480 
5481 void RoutingModel::SetupTrace(
5482  const RoutingSearchParameters& search_parameters) {
5483  if (search_parameters.log_search()) {
5484  Solver::SearchLogParameters search_log_parameters;
5485  search_log_parameters.branch_period = 10000;
5486  search_log_parameters.objective = nullptr;
5487  search_log_parameters.variable = cost_;
5488  search_log_parameters.scaling_factor =
5489  search_parameters.log_cost_scaling_factor();
5490  search_log_parameters.offset = search_parameters.log_cost_offset();
5491  if (!search_parameters.log_tag().empty()) {
5492  const std::string tag = search_parameters.log_tag();
5493  search_log_parameters.display_callback = [tag]() { return tag; };
5494  } else {
5495  search_log_parameters.display_callback = nullptr;
5496  }
5497  monitors_.push_back(solver_->MakeSearchLog(search_log_parameters));
5498  }
5499 }
5500 
5501 void RoutingModel::SetupSearchMonitors(
5502  const RoutingSearchParameters& search_parameters) {
5503  monitors_.push_back(GetOrCreateLimit());
5504  SetupMetaheuristics(search_parameters);
5505  SetupAssignmentCollector(search_parameters);
5506  SetupTrace(search_parameters);
5507 }
5508 
5509 bool RoutingModel::UsesLightPropagation(
5510  const RoutingSearchParameters& search_parameters) const {
5511  return !search_parameters.use_full_propagation() &&
5512  !search_parameters.use_depth_first_search() &&
5513  search_parameters.first_solution_strategy() !=
5514  FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE;
5515 }
5516 
5518  int64 cost) {
5519  CHECK(var != nullptr);
5520  const int index = gtl::LookupOrInsert(&finalizer_variable_cost_index_, var,
5521  finalizer_variable_cost_pairs_.size());
5522  if (index < finalizer_variable_cost_pairs_.size()) {
5523  const int64 old_cost = finalizer_variable_cost_pairs_[index].second;
5524  finalizer_variable_cost_pairs_[index].second = CapAdd(old_cost, cost);
5525  } else {
5526  finalizer_variable_cost_pairs_.emplace_back(var, cost);
5527  }
5528 }
5529 
5531  CHECK(var != nullptr);
5532  if (finalizer_variable_target_set_.contains(var)) return;
5533  finalizer_variable_target_set_.insert(var);
5534  finalizer_variable_target_pairs_.emplace_back(var, target);
5535 }
5536 
5539 }
5540 
5543 }
5544 
5545 void RoutingModel::SetupSearch(
5546  const RoutingSearchParameters& search_parameters) {
5547  SetupDecisionBuilders(search_parameters);
5548  SetupSearchMonitors(search_parameters);
5549 }
5550 
5551 void RoutingModel::AddToAssignment(IntVar* const var) {
5552  extra_vars_.push_back(var);
5553 }
5554 
5556  extra_intervals_.push_back(interval);
5557 }
5558 
5559 namespace {
5560 
5561 class PathSpansAndTotalSlacks : public Constraint {
5562  public:
5563  PathSpansAndTotalSlacks(const RoutingModel* model,
5564  const RoutingDimension* dimension,
5565  std::vector<IntVar*> spans,
5566  std::vector<IntVar*> total_slacks)
5567  : Constraint(model->solver()),
5568  model_(model),
5569  dimension_(dimension),
5570  spans_(std::move(spans)),
5571  total_slacks_(std::move(total_slacks)) {
5572  CHECK_EQ(spans_.size(), model_->vehicles());
5573  CHECK_EQ(total_slacks_.size(), model_->vehicles());
5574  vehicle_demons_.resize(model_->vehicles());
5575  }
5576 
5577  std::string DebugString() const override { return "PathSpansAndTotalSlacks"; }
5578 
5579  void Post() override {
5580  const int num_nodes = model_->VehicleVars().size();
5581  const int num_transits = model_->Nexts().size();
5582  for (int node = 0; node < num_nodes; ++node) {
5583  auto* demon = MakeConstraintDemon1(
5584  model_->solver(), this, &PathSpansAndTotalSlacks::PropagateNode,
5585  "PathSpansAndTotalSlacks::PropagateNode", node);
5586  dimension_->CumulVar(node)->WhenRange(demon);
5587  model_->VehicleVar(node)->WhenBound(demon);
5588  if (node < num_transits) {
5589  dimension_->TransitVar(node)->WhenRange(demon);
5590  dimension_->FixedTransitVar(node)->WhenBound(demon);
5591  model_->NextVar(node)->WhenBound(demon);
5592  }
5593  }
5594  for (int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5595  if (!spans_[vehicle] && !total_slacks_[vehicle]) continue;
5596  auto* demon = MakeDelayedConstraintDemon1(
5597  solver(), this, &PathSpansAndTotalSlacks::PropagateVehicle,
5598  "PathSpansAndTotalSlacks::PropagateVehicle", vehicle);
5599  vehicle_demons_[vehicle] = demon;
5600  if (spans_[vehicle]) spans_[vehicle]->WhenRange(demon);
5601  if (total_slacks_[vehicle]) total_slacks_[vehicle]->WhenRange(demon);
5602  if (dimension_->HasBreakConstraints()) {
5603  for (IntervalVar* b : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5604  b->WhenAnything(demon);
5605  }
5606  }
5607  }
5608  }
5609 
5610  // Call propagator on all vehicles.
5611  void InitialPropagate() override {
5612  for (int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5613  if (!spans_[vehicle] && !total_slacks_[vehicle]) continue;
5614  PropagateVehicle(vehicle);
5615  }
5616  }
5617 
5618  private:
5619  // Called when a path/dimension variables of the node changes,
5620  // this delays propagator calls until path variables (Next and VehicleVar)
5621  // are instantiated, which saves fruitless and multiple identical calls.
5622  void PropagateNode(int node) {
5623  if (!model_->VehicleVar(node)->Bound()) return;
5624  const int vehicle = model_->VehicleVar(node)->Min();
5625  if (vehicle < 0 || vehicle_demons_[vehicle] == nullptr) return;
5626  EnqueueDelayedDemon(vehicle_demons_[vehicle]);
5627  }
5628 
5629  // In order to make reasoning on span and total_slack of a vehicle uniform,
5630  // we rely on the fact that span == sum_fixed_transits + total_slack
5631  // to present both span and total_slack in terms of span and fixed transit.
5632  // This allows to use the same code whether there actually are variables
5633  // for span and total_slack or not.
5634  int64 SpanMin(int vehicle, int64 sum_fixed_transits) {
5635  DCHECK_GE(sum_fixed_transits, 0);
5636  const int64 span_min = spans_[vehicle] ? spans_[vehicle]->Min() : kint64max;
5637  const int64 total_slack_min =
5638  total_slacks_[vehicle] ? total_slacks_[vehicle]->Min() : kint64max;
5639  return std::min(span_min, CapAdd(total_slack_min, sum_fixed_transits));
5640  }
5641  int64 SpanMax(int vehicle, int64 sum_fixed_transits) {
5642  DCHECK_GE(sum_fixed_transits, 0);
5643  const int64 span_max = spans_[vehicle] ? spans_[vehicle]->Max() : kint64min;
5644  const int64 total_slack_max =
5645  total_slacks_[vehicle] ? total_slacks_[vehicle]->Max() : kint64min;
5646  return std::max(span_max, CapAdd(total_slack_max, sum_fixed_transits));
5647  }
5648  void SetSpanMin(int vehicle, int64 min, int64 sum_fixed_transits) {
5649  DCHECK_GE(sum_fixed_transits, 0);
5650  if (spans_[vehicle]) {
5651  spans_[vehicle]->SetMin(min);
5652  }
5653  if (total_slacks_[vehicle]) {
5654  total_slacks_[vehicle]->SetMin(CapSub(min, sum_fixed_transits));
5655  }
5656  }
5657  void SetSpanMax(int vehicle, int64 max, int64 sum_fixed_transits) {
5658  DCHECK_GE(sum_fixed_transits, 0);
5659  if (spans_[vehicle]) {
5660  spans_[vehicle]->SetMax(max);
5661  }
5662  if (total_slacks_[vehicle]) {
5663  total_slacks_[vehicle]->SetMax(CapSub(max, sum_fixed_transits));
5664  }
5665  }
5666  // Propagates span == sum_fixed_transits + total_slack.
5667  // This should be called at least once during PropagateVehicle().
5668  void SynchronizeSpanAndTotalSlack(int vehicle, int64 sum_fixed_transits) {
5669  DCHECK_GE(sum_fixed_transits, 0);
5670  IntVar* span = spans_[vehicle];
5671  IntVar* total_slack = total_slacks_[vehicle];
5672  if (!span || !total_slack) return;
5673  span->SetMin(CapAdd(total_slack->Min(), sum_fixed_transits));
5674  span->SetMax(CapAdd(total_slack->Max(), sum_fixed_transits));
5675  total_slack->SetMin(CapSub(span->Min(), sum_fixed_transits));
5676  total_slack->SetMax(CapSub(span->Max(), sum_fixed_transits));
5677  }
5678 
5679  void PropagateVehicle(int vehicle) {
5680  DCHECK(spans_[vehicle] || total_slacks_[vehicle]);
5681  const int start = model_->Start(vehicle);
5682  const int end = model_->End(vehicle);
5683  // Record path, if it is not fixed from start to end, stop here.
5684  // TRICKY: do not put end node yet, we look only at transits in the next
5685  // reasonings, we will append the end when we look at cumuls.
5686  {
5687  path_.clear();
5688  int curr_node = start;
5689  while (!model_->IsEnd(curr_node)) {
5690  const IntVar* next_var = model_->NextVar(curr_node);
5691  if (!next_var->Bound()) return;
5692  path_.push_back(curr_node);
5693  curr_node = next_var->Value();
5694  }
5695  }
5696  // Compute the sum of fixed transits. Fixed transit variables should all be
5697  // fixed, otherwise we wait to get called later when propagation does it.
5698  int64 sum_fixed_transits = 0;
5699  for (const int node : path_) {
5700  const IntVar* fixed_transit_var = dimension_->FixedTransitVar(node);
5701  if (!fixed_transit_var->Bound()) return;
5702  sum_fixed_transits =
5703  CapAdd(sum_fixed_transits, fixed_transit_var->Value());
5704  }
5705 
5706  SynchronizeSpanAndTotalSlack(vehicle, sum_fixed_transits);
5707 
5708  // The amount of break time that must occur during the route must be smaller
5709  // than span max - sum_fixed_transits. A break must occur on the route if it
5710  // must be after the route's start and before the route's end.
5711  // Propagate lower bound on span, then filter out values
5712  // that would force more breaks in route than possible.
5713  if (dimension_->HasBreakConstraints() &&
5714  !dimension_->GetBreakIntervalsOfVehicle(vehicle).empty()) {
5715  const int64 vehicle_start_max = dimension_->CumulVar(start)->Max();
5716  const int64 vehicle_end_min = dimension_->CumulVar(end)->Min();
5717  // Compute and propagate lower bound.
5718  int64 min_break_duration = 0;
5719  for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5720  if (!br->MustBePerformed()) continue;
5721  if (vehicle_start_max < br->EndMin() &&
5722  br->StartMax() < vehicle_end_min) {
5723  min_break_duration = CapAdd(min_break_duration, br->DurationMin());
5724  }
5725  }
5726  SetSpanMin(vehicle, CapAdd(min_break_duration, sum_fixed_transits),
5727  sum_fixed_transits);
5728  // If a break that is not inside the route may violate slack_max,
5729  // we can propagate in some cases: when the break must be before or
5730  // must be after the route.
5731  // In the other cases, we cannot deduce a better bound on a CumulVar or
5732  // on a break, so we do nothing.
5733  const int64 slack_max =
5734  CapSub(SpanMax(vehicle, sum_fixed_transits), sum_fixed_transits);
5735  const int64 max_additional_slack = CapSub(slack_max, min_break_duration);
5736  for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5737  if (!br->MustBePerformed()) continue;
5738  // Break must be before end, detect whether it must be before start.
5739  if (vehicle_start_max >= br->EndMin() &&
5740  br->StartMax() < vehicle_end_min) {
5741  if (br->DurationMin() > max_additional_slack) {
5742  // Having the break inside would violate max_additional_slack..
5743  // Thus, it must be outside the route, in this case, before.
5744  br->SetEndMax(vehicle_start_max);
5745  dimension_->CumulVar(start)->SetMin(br->EndMin());
5746  }
5747  }
5748  // Break must be after start, detect whether it must be after end.
5749  // Same reasoning, in the case where the break is after.
5750  if (vehicle_start_max < br->EndMin() &&
5751  br->StartMax() >= vehicle_end_min) {
5752  if (br->DurationMin() > max_additional_slack) {
5753  br->SetStartMin(vehicle_end_min);
5754  dimension_->CumulVar(end)->SetMax(br->StartMax());
5755  }
5756  }
5757  }
5758  }
5759 
5760  // Propagate span == cumul(end) - cumul(start).
5761  {
5762  IntVar* start_cumul = dimension_->CumulVar(start);
5763  IntVar* end_cumul = dimension_->CumulVar(end);
5764  const int64 start_min = start_cumul->Min();
5765  const int64 start_max = start_cumul->Max();
5766  const int64 end_min = end_cumul->Min();
5767  const int64 end_max = end_cumul->Max();
5768  // Propagate from cumuls to span.
5769  const int64 span_lb = CapSub(end_min, start_max);
5770  SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5771  const int64 span_ub = CapSub(end_max, start_min);
5772  SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5773  // Propagate from span to cumuls.
5774  const int64 span_min = SpanMin(vehicle, sum_fixed_transits);
5775  const int64 span_max = SpanMax(vehicle, sum_fixed_transits);
5776  const int64 slack_from_lb = CapSub(span_max, span_lb);
5777  const int64 slack_from_ub = CapSub(span_ub, span_min);
5778  // start >= start_max - (span_max - span_lb).
5779  start_cumul->SetMin(CapSub(start_max, slack_from_lb));
5780  // end <= end_min + (span_max - span_lb).
5781  end_cumul->SetMax(CapAdd(end_min, slack_from_lb));
5782  // // start <= start_min + (span_ub - span_min)
5783  start_cumul->SetMax(CapAdd(start_min, slack_from_ub));
5784  // // end >= end_max - (span_ub - span_min)
5785  end_cumul->SetMin(CapSub(end_max, slack_from_ub));
5786  }
5787 
5788  // Propagate sum transits == span.
5789  {
5790  // Propagate from transits to span.
5791  int64 span_lb = 0;
5792  int64 span_ub = 0;
5793  for (const int node : path_) {
5794  span_lb = CapAdd(span_lb, dimension_->TransitVar(node)->Min());
5795  span_ub = CapAdd(span_ub, dimension_->TransitVar(node)->Max());
5796  }
5797  SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5798  SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5799  // Propagate from span to transits.
5800  // transit[i] <= transit_i_min + (span_max - span_lb)
5801  // transit[i] >= transit_i_max - (span_ub - span_min)
5802  const int64 span_min = SpanMin(vehicle, sum_fixed_transits);
5803  const int64 span_max = SpanMax(vehicle, sum_fixed_transits);
5804  const int64 slack_from_lb = CapSub(span_max, span_lb);
5805  const int64 slack_from_ub =
5806  span_ub < kint64max ? CapSub(span_ub, span_min) : kint64max;
5807  for (const int node : path_) {
5808  IntVar* transit_var = dimension_->TransitVar(node);
5809  const int64 transit_i_min = transit_var->Min();
5810  const int64 transit_i_max = transit_var->Max();
5811  // TRICKY: the first propagation might change transit_var->Max(),
5812  // but we must use the same value of transit_i_max in the computation
5813  // of transit[i]'s lower bound that was used for span_ub.
5814  transit_var->SetMax(CapAdd(transit_i_min, slack_from_lb));
5815  transit_var->SetMin(CapSub(transit_i_max, slack_from_ub));
5816  }
5817  }
5818 
5819  // TRICKY: add end node now, we will look at cumuls.
5820  path_.push_back(end);
5821 
5822  // A stronger bound: from start min of the route, go to node i+1 with time
5823  // max(cumul[i] + fixed_transit, cumul[i+1].Min()).
5824  // Record arrival time (should be the same as end cumul min).
5825  // Then do the reverse route, going to time
5826  // min(cumul[i+1] - fixed_transit, cumul[i].Max())
5827  // Record final time as departure time.
5828  // Then arrival time - departure time is a valid lower bound of span.
5829  // First reasoning: start - end - start
5830  {
5831  int64 arrival_time = dimension_->CumulVar(start)->Min();
5832  for (int i = 1; i < path_.size(); ++i) {
5833  arrival_time =
5834  std::max(CapAdd(arrival_time,
5835  dimension_->FixedTransitVar(path_[i - 1])->Min()),
5836  dimension_->CumulVar(path_[i])->Min());
5837  }
5838  int64 departure_time = arrival_time;
5839  for (int i = path_.size() - 2; i >= 0; --i) {
5840  departure_time =
5841  std::min(CapSub(departure_time,
5842  dimension_->FixedTransitVar(path_[i])->Min()),
5843  dimension_->CumulVar(path_[i])->Max());
5844  }
5845  const int64 span_lb = CapSub(arrival_time, departure_time);
5846  SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5847  const int64 maximum_deviation =
5848  CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5849  const int64 start_lb = CapSub(departure_time, maximum_deviation);
5850  dimension_->CumulVar(start)->SetMin(start_lb);
5851  }
5852  // Second reasoning: end - start - end
5853  {
5854  int64 departure_time = dimension_->CumulVar(end)->Max();
5855  for (int i = path_.size() - 2; i >= 0; --i) {
5856  const int curr_node = path_[i];
5857  departure_time =
5858  std::min(CapSub(departure_time,
5859  dimension_->FixedTransitVar(curr_node)->Min()),
5860  dimension_->CumulVar(curr_node)->Max());
5861  }
5862  int arrival_time = departure_time;
5863  for (int i = 1; i < path_.size(); ++i) {
5864  arrival_time =
5865  std::max(CapAdd(arrival_time,
5866  dimension_->FixedTransitVar(path_[i - 1])->Min()),
5867  dimension_->CumulVar(path_[i])->Min());
5868  }
5869  const int64 span_lb = CapSub(arrival_time, departure_time);
5870  SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5871  const int64 maximum_deviation =
5872  CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5873  dimension_->CumulVar(end)->SetMax(
5874  CapAdd(arrival_time, maximum_deviation));
5875  }
5876  }
5877 
5878  const RoutingModel* const model_;
5879  const RoutingDimension* const dimension_;
5880  std::vector<IntVar*> spans_;
5881  std::vector<IntVar*> total_slacks_;
5882  std::vector<int> path_;
5883  std::vector<Demon*> vehicle_demons_;
5884 };
5885 
5886 } // namespace
5887 
5889  const RoutingDimension* dimension, std::vector<IntVar*> spans,
5890  std::vector<IntVar*> total_slacks) {
5891  CHECK_EQ(vehicles_, spans.size());
5892  CHECK_EQ(vehicles_, total_slacks.size());
5893  return solver()->RevAlloc(
5894  new PathSpansAndTotalSlacks(this, dimension, spans, total_slacks));
5895 }
5896 
5897 const char RoutingModelVisitor::kLightElement[] = "LightElement";
5898 const char RoutingModelVisitor::kLightElement2[] = "LightElement2";
5899 const char RoutingModelVisitor::kRemoveValues[] = "RemoveValues";
5900 
5901 RoutingDimension::RoutingDimension(RoutingModel* model,
5902  std::vector<int64> vehicle_capacities,
5903  const std::string& name,
5904  const RoutingDimension* base_dimension)
5905  : vehicle_capacities_(std::move(vehicle_capacities)),
5906  base_dimension_(base_dimension),
5907  global_span_cost_coefficient_(0),
5908  model_(model),
5909  name_(name),
5910  global_optimizer_offset_(0) {
5911  CHECK(model != nullptr);
5912  vehicle_span_upper_bounds_.assign(model->vehicles(), kint64max);
5913  vehicle_span_cost_coefficients_.assign(model->vehicles(), 0);
5914 }
5915 
5916 RoutingDimension::RoutingDimension(RoutingModel* model,
5917  std::vector<int64> vehicle_capacities,
5918  const std::string& name, SelfBased)
5919  : RoutingDimension(model, std::move(vehicle_capacities), name, this) {}
5920 
5922  cumul_var_piecewise_linear_cost_.clear();
5923 }
5924 
5925 void RoutingDimension::Initialize(
5926  const std::vector<int>& transit_evaluators,
5927  const std::vector<int>& state_dependent_transit_evaluators,
5928  int64 slack_max) {
5929  InitializeCumuls();
5930  InitializeTransits(transit_evaluators, state_dependent_transit_evaluators,
5931  slack_max);
5932 }
5933 
5934 namespace {
5935 // Very light version of the RangeLessOrEqual constraint (see ./range_cst.cc).
5936 // Only performs initial propagation and then checks the compatibility of the
5937 // variable domains without domain pruning.
5938 // This is useful when to avoid ping-pong effects with costly constraints
5939 // such as the PathCumul constraint.
5940 // This constraint has not been added to the cp library (in range_cst.cc) given
5941 // it only does checking and no propagation (except the initial propagation)
5942 // and is only fit for local search, in particular in the context of vehicle
5943 // routing.
5944 class LightRangeLessOrEqual : public Constraint {
5945  public:
5946  LightRangeLessOrEqual(Solver* const s, IntExpr* const l, IntExpr* const r);
5947  ~LightRangeLessOrEqual() override {}
5948  void Post() override;
5949  void InitialPropagate() override;
5950  std::string DebugString() const override;
5951  IntVar* Var() override {
5952  return solver()->MakeIsLessOrEqualVar(left_, right_);
5953  }
5954  // TODO(user): introduce a kLightLessOrEqual tag.
5955  void Accept(ModelVisitor* const visitor) const override {
5956  visitor->BeginVisitConstraint(ModelVisitor::kLessOrEqual, this);
5957  visitor->VisitIntegerExpressionArgument(ModelVisitor::kLeftArgument, left_);
5958  visitor->VisitIntegerExpressionArgument(ModelVisitor::kRightArgument,
5959  right_);
5960  visitor->EndVisitConstraint(ModelVisitor::kLessOrEqual, this);
5961  }
5962 
5963  private:
5964  void CheckRange();
5965 
5966  IntExpr* const left_;
5967  IntExpr* const right_;
5968  Demon* demon_;
5969 };
5970 
5971 LightRangeLessOrEqual::LightRangeLessOrEqual(Solver* const s, IntExpr* const l,
5972  IntExpr* const r)
5973  : Constraint(s), left_(l), right_(r), demon_(nullptr) {}
5974 
5975 void LightRangeLessOrEqual::Post() {
5976  demon_ = MakeConstraintDemon0(
5977  solver(), this, &LightRangeLessOrEqual::CheckRange, "CheckRange");
5978  left_->WhenRange(demon_);
5979  right_->WhenRange(demon_);
5980 }
5981 
5982 void LightRangeLessOrEqual::InitialPropagate() {
5983  left_->SetMax(right_->Max());
5984  right_->SetMin(left_->Min());
5985  if (left_->Max() <= right_->Min()) {
5986  demon_->inhibit(solver());
5987  }
5988 }
5989 
5990 void LightRangeLessOrEqual::CheckRange() {
5991  if (left_->Min() > right_->Max()) {
5992  solver()->Fail();
5993  }
5994  if (left_->Max() <= right_->Min()) {
5995  demon_->inhibit(solver());
5996  }
5997 }
5998 
5999 std::string LightRangeLessOrEqual::DebugString() const {
6000  return left_->DebugString() + " < " + right_->DebugString();
6001 }
6002 
6003 } // namespace
6004 
6005 void RoutingDimension::InitializeCumuls() {
6006  Solver* const solver = model_->solver();
6007  const int size = model_->Size() + model_->vehicles();
6008  const auto capacity_range = std::minmax_element(vehicle_capacities_.begin(),
6009  vehicle_capacities_.end());
6010  const int64 min_capacity = *capacity_range.first;
6011  CHECK_GE(min_capacity, 0);
6012  const int64 max_capacity = *capacity_range.second;
6013  solver->MakeIntVarArray(size, 0, max_capacity, name_, &cumuls_);
6014  // Refine the min/max for vehicle start/ends based on vehicle capacities.
6015  for (int v = 0; v < model_->vehicles(); v++) {
6016  const int64 vehicle_capacity = vehicle_capacities_[v];
6017  cumuls_[model_->Start(v)]->SetMax(vehicle_capacity);
6018  cumuls_[model_->End(v)]->SetMax(vehicle_capacity);
6019  }
6020 
6021  forbidden_intervals_.resize(size);
6022  capacity_vars_.clear();
6023  if (min_capacity != max_capacity) {
6024  solver->MakeIntVarArray(size, 0, kint64max, &capacity_vars_);
6025  for (int i = 0; i < size; ++i) {
6026  IntVar* const capacity_var = capacity_vars_[i];
6027  if (i < model_->Size()) {
6028  IntVar* const capacity_active = solver->MakeBoolVar();
6029  solver->AddConstraint(
6030  solver->MakeLessOrEqual(model_->ActiveVar(i), capacity_active));
6031  solver->AddConstraint(solver->MakeIsLessOrEqualCt(
6032  cumuls_[i], capacity_var, capacity_active));
6033  } else {
6034  solver->AddConstraint(
6035  solver->MakeLessOrEqual(cumuls_[i], capacity_var));
6036  }
6037  }
6038  }
6039 }
6040 
6041 namespace {
6042 template <int64 value>
6043 int64 IthElementOrValue(const std::vector<int64>& v, int64 index) {
6044  return index >= 0 ? v[index] : value;
6045 }
6046 
6047 void ComputeTransitClasses(const std::vector<int>& evaluator_indices,
6048  std::vector<int>* class_evaluators,
6049  std::vector<int64>* vehicle_to_class) {
6050  CHECK(class_evaluators != nullptr);
6051  CHECK(vehicle_to_class != nullptr);
6052  class_evaluators->clear();
6053  vehicle_to_class->resize(evaluator_indices.size(), -1);
6054  absl::flat_hash_map<int, int64> evaluator_to_class;
6055  for (int i = 0; i < evaluator_indices.size(); ++i) {
6056  const int evaluator_index = evaluator_indices[i];
6057  int evaluator_class = -1;
6058  if (!gtl::FindCopy(evaluator_to_class, evaluator_index, &evaluator_class)) {
6059  evaluator_class = class_evaluators->size();
6060  evaluator_to_class[evaluator_index] = evaluator_class;
6061  class_evaluators->push_back(evaluator_index);
6062  }
6063  (*vehicle_to_class)[i] = evaluator_class;
6064  }
6065 }
6066 } // namespace
6067 
6068 void RoutingDimension::InitializeTransitVariables(int64 slack_max) {
6069  CHECK(!class_evaluators_.empty());
6070  CHECK(base_dimension_ == nullptr ||
6071  !state_dependent_class_evaluators_.empty());
6072 
6073  Solver* const solver = model_->solver();
6074  const int size = model_->Size();
6075  const Solver::IndexEvaluator1 dependent_vehicle_class_function =
6076  [this](int index) {
6077  return (0 <= index && index < state_dependent_vehicle_to_class_.size())
6078  ? state_dependent_vehicle_to_class_[index]
6079  : state_dependent_class_evaluators_.size();
6080  };
6081  const std::string slack_name = name_ + " slack";
6082  const std::string transit_name = name_ + " fixed transit";
6083 
6084  for (int64 i = 0; i < size; ++i) {
6085  fixed_transits_[i] =
6086  solver->MakeIntVar(kint64min, kint64max, absl::StrCat(transit_name, i));
6087  // Setting dependent_transits_[i].
6088  if (base_dimension_ != nullptr) {
6089  if (state_dependent_class_evaluators_.size() == 1) {
6090  std::vector<IntVar*> transition_variables(cumuls_.size(), nullptr);
6091  for (int64 j = 0; j < cumuls_.size(); ++j) {
6092  transition_variables[j] =
6093  MakeRangeMakeElementExpr(
6094  model_
6095  ->StateDependentTransitCallback(
6096  state_dependent_class_evaluators_[0])(i, j)
6097  .transit,
6098  base_dimension_->CumulVar(i), solver)
6099  ->Var();
6100  }
6101  dependent_transits_[i] =
6102  solver->MakeElement(transition_variables, model_->NextVar(i))
6103  ->Var();
6104  } else {
6105  IntVar* const vehicle_class_var =
6106  solver
6107  ->MakeElement(dependent_vehicle_class_function,
6108  model_->VehicleVar(i))
6109  ->Var();
6110  std::vector<IntVar*> transit_for_vehicle;
6111  transit_for_vehicle.reserve(state_dependent_class_evaluators_.size() +
6112  1);
6113  for (int evaluator : state_dependent_class_evaluators_) {
6114  std::vector<IntVar*> transition_variables(cumuls_.size(), nullptr);
6115  for (int64 j = 0; j < cumuls_.size(); ++j) {
6116  transition_variables[j] =
6117  MakeRangeMakeElementExpr(
6118  model_->StateDependentTransitCallback(evaluator)(i, j)
6119  .transit,
6120  base_dimension_->CumulVar(i), solver)
6121  ->Var();
6122  }
6123  transit_for_vehicle.push_back(
6124  solver->MakeElement(transition_variables, model_->NextVar(i))
6125  ->Var());
6126  }
6127  transit_for_vehicle.push_back(solver->MakeIntConst(0));
6128  dependent_transits_[i] =
6129  solver->MakeElement(transit_for_vehicle, vehicle_class_var)->Var();
6130  }
6131  } else {
6132  dependent_transits_[i] = solver->MakeIntConst(0);
6133  }
6134 
6135  // Summing fixed transits, dependent transits and the slack.
6136  IntExpr* transit_expr = fixed_transits_[i];
6137  if (dependent_transits_[i]->Min() != 0 ||
6138  dependent_transits_[i]->Max() != 0) {
6139  transit_expr = solver->MakeSum(transit_expr, dependent_transits_[i]);
6140  }
6141 
6142  if (slack_max == 0) {
6143  slacks_[i] = solver->MakeIntConst(0);
6144  } else {
6145  slacks_[i] =
6146  solver->MakeIntVar(0, slack_max, absl::StrCat(slack_name, i));
6147  transit_expr = solver->MakeSum(slacks_[i], transit_expr);
6148  }
6149  transits_[i] = transit_expr->Var();
6150  }
6151 }
6152 
6153 void RoutingDimension::InitializeTransits(
6154  const std::vector<int>& transit_evaluators,
6155  const std::vector<int>& state_dependent_transit_evaluators,
6156  int64 slack_max) {
6157  CHECK_EQ(model_->vehicles(), transit_evaluators.size());
6158  CHECK(base_dimension_ == nullptr ||
6159  model_->vehicles() == state_dependent_transit_evaluators.size());
6160  const int size = model_->Size();
6161  transits_.resize(size, nullptr);
6162  fixed_transits_.resize(size, nullptr);
6163  slacks_.resize(size, nullptr);
6164  dependent_transits_.resize(size, nullptr);
6165  ComputeTransitClasses(transit_evaluators, &class_evaluators_,
6166  &vehicle_to_class_);
6167  if (base_dimension_ != nullptr) {
6168  ComputeTransitClasses(state_dependent_transit_evaluators,
6169  &state_dependent_class_evaluators_,
6170  &state_dependent_vehicle_to_class_);
6171  }
6172 
6173  InitializeTransitVariables(slack_max);
6174 }
6175 
6176 void FillPathEvaluation(const std::vector<int64>& path,
6177  const RoutingModel::TransitCallback2& evaluator,
6178  std::vector<int64>* values) {
6179  const int num_nodes = path.size();
6180  values->resize(num_nodes - 1);
6181  for (int i = 0; i < num_nodes - 1; ++i) {
6182  (*values)[i] = evaluator(path[i], path[i + 1]);
6183  }
6184 }
6185 
6187  : model_(model), occurrences_of_type_(model.GetNumberOfVisitTypes()) {}
6188 
6190  int vehicle, const std::function<int64(int64)>& next_accessor) {
6191  if (!HasRegulationsToCheck()) {
6192  return true;
6193  }
6194 
6195  InitializeCheck(vehicle, next_accessor);
6196 
6197  for (int pos = 0; pos < current_route_visits_.size(); pos++) {
6198  const int64 current_visit = current_route_visits_[pos];
6199  const int type = model_.GetVisitType(current_visit);
6200  if (type < 0) {
6201  continue;
6202  }
6203  const VisitTypePolicy policy = model_.GetVisitTypePolicy(current_visit);
6204 
6205  DCHECK_LT(type, occurrences_of_type_.size());
6206  int& num_type_added = occurrences_of_type_[type].num_type_added_to_vehicle;
6207  int& num_type_removed =
6208  occurrences_of_type_[type].num_type_removed_from_vehicle;
6209  DCHECK_LE(num_type_removed, num_type_added);
6211  num_type_removed == num_type_added) {
6212  // The type is not actually being removed as all added types have already
6213  // been removed.
6214  continue;
6215  }
6216 
6217  if (!CheckTypeRegulations(type, policy, pos)) {
6218  return false;
6219  }
6220  // Update count of type based on the visit policy.
6221  if (policy == VisitTypePolicy::TYPE_ADDED_TO_VEHICLE ||
6222  policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED) {
6223  num_type_added++;
6224  }
6225  if (policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED ||
6226  policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6227  num_type_removed++;
6228  }
6229  }
6230  return FinalizeCheck();
6231 }
6232 
6234  int vehicle, const std::function<int64(int64)>& next_accessor) {
6235  // Accumulates the count of types before the current node.
6236  // {0, 0, -1} does not compile on or-tools.
6237  std::fill(occurrences_of_type_.begin(), occurrences_of_type_.end(),
6239 
6240  // TODO(user): Optimize the filter to avoid scanning the route an extra
6241  // time when there are no TYPE_ON_VEHICLE_UP_TO_VISIT policies on the route,
6242  // by passing a boolean to CheckVehicle() passed to InitializeCheck().
6243  current_route_visits_.clear();
6244  for (int64 current = model_.Start(vehicle); !model_.IsEnd(current);
6245  current = next_accessor(current)) {
6246  const int type = model_.GetVisitType(current);
6247  if (type >= 0 && model_.GetVisitTypePolicy(current) ==
6248  VisitTypePolicy::TYPE_ON_VEHICLE_UP_TO_VISIT) {
6249  occurrences_of_type_[type].position_of_last_type_on_vehicle_up_to_visit =
6250  current_route_visits_.size();
6251  }
6252  current_route_visits_.push_back(current);
6253  }
6254 
6256 }
6257 
6259  const TypePolicyOccurrence& occurrences = occurrences_of_type_[type];
6260  return occurrences.num_type_added_to_vehicle > 0 ||
6262 }
6263 
6264 bool TypeRegulationsChecker::TypeCurrentlyOnRoute(int type, int pos) const {
6265  const TypePolicyOccurrence& occurrences = occurrences_of_type_[type];
6266  return occurrences.num_type_removed_from_vehicle <
6267  occurrences.num_type_added_to_vehicle ||
6269 }
6270 
6272  const RoutingModel& model, bool check_hard_incompatibilities)
6274  check_hard_incompatibilities_(check_hard_incompatibilities) {}
6275 
6276 bool TypeIncompatibilityChecker::HasRegulationsToCheck() const {
6278  (check_hard_incompatibilities_ &&
6280 }
6281 
6282 // TODO(user): Remove the check_hard_incompatibilities_ boolean and always
6283 // check both incompatibilities to simplify the code?
6284 // TODO(user): Improve algorithm by only checking a given type if necessary?
6285 // - For temporal incompatibilities, only check if NonDeliveredType(count) == 1.
6286 // - For hard incompatibilities, only if NonDeliveryType(type) == 1.
6287 bool TypeIncompatibilityChecker::CheckTypeRegulations(int type,
6288  VisitTypePolicy policy,
6289  int pos) {
6290  if (policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6291  // NOTE: We don't need to check incompatibilities when the type is being
6292  // removed from the route.
6293  return true;
6294  }
6295  for (int incompatible_type :
6297  if (TypeCurrentlyOnRoute(incompatible_type, pos)) {
6298  return false;
6299  }
6300  }
6301  if (check_hard_incompatibilities_) {
6302  for (int incompatible_type :
6304  if (TypeOccursOnRoute(incompatible_type)) {
6305  return false;
6306  }
6307  }
6308  }
6309  return true;
6310 }
6311 
6312 bool TypeRequirementChecker::HasRegulationsToCheck() const {
6315 }
6316 
6317 bool TypeRequirementChecker::CheckRequiredTypesCurrentlyOnRoute(
6318  const std::vector<absl::flat_hash_set<int>>& required_type_alternatives,
6319  int pos) {
6320  for (const absl::flat_hash_set<int>& requirement_alternatives :
6321  required_type_alternatives) {
6322  bool has_one_of_alternatives = false;
6323  for (int type_alternative : requirement_alternatives) {
6324  if (TypeCurrentlyOnRoute(type_alternative, pos)) {
6325  has_one_of_alternatives = true;
6326  break;
6327  }
6328  }
6329  if (!has_one_of_alternatives) {
6330  return false;
6331  }
6332  }
6333  return true;
6334 }
6335 
6336 bool TypeRequirementChecker::CheckTypeRegulations(int type,
6337  VisitTypePolicy policy,
6338  int pos) {
6339  if (policy == RoutingModel::TYPE_ADDED_TO_VEHICLE ||
6341  if (!CheckRequiredTypesCurrentlyOnRoute(
6343  return false;
6344  }
6345  }
6346  if (policy != RoutingModel::TYPE_ADDED_TO_VEHICLE) {
6347  if (!CheckRequiredTypesCurrentlyOnRoute(
6349  return false;
6350  }
6351  }
6354  types_with_same_vehicle_requirements_on_route_.insert(type);
6355  }
6356  return true;
6357 }
6358 
6359 bool TypeRequirementChecker::FinalizeCheck() const {
6360  for (int type : types_with_same_vehicle_requirements_on_route_) {
6361  for (const absl::flat_hash_set<int>& requirement_alternatives :
6363  bool has_one_of_alternatives = false;
6364  for (const int type_alternative : requirement_alternatives) {
6365  if (TypeOccursOnRoute(type_alternative)) {
6366  has_one_of_alternatives = true;
6367  break;
6368  }
6369  }
6370  if (!has_one_of_alternatives) {
6371  return false;
6372  }
6373  }
6374  }
6375  return true;
6376 }
6377 
6379  : Constraint(model.solver()),
6380  model_(model),
6381  incompatibility_checker_(model, /*check_hard_incompatibilities*/ true),
6382  requirement_checker_(model),
6383  vehicle_demons_(model.vehicles()) {}
6384 
6385 void TypeRegulationsConstraint::PropagateNodeRegulations(int node) {
6386  DCHECK_LT(node, model_.Size());
6387  if (!model_.VehicleVar(node)->Bound() || !model_.NextVar(node)->Bound()) {
6388  // Vehicle var or Next var not bound.
6389  return;
6390  }
6391  const int vehicle = model_.VehicleVar(node)->Min();
6392  if (vehicle < 0) return;
6393  DCHECK(vehicle_demons_[vehicle] != nullptr);
6394  EnqueueDelayedDemon(vehicle_demons_[vehicle]);
6395 }
6396 
6397 void TypeRegulationsConstraint::CheckRegulationsOnVehicle(int vehicle) {
6398  const auto next_accessor = [this, vehicle](int64 node) {
6399  if (model_.NextVar(node)->Bound()) {
6400  return model_.NextVar(node)->Value();
6401  }
6402  // Node not bound, skip to the end of the vehicle.
6403  return model_.End(vehicle);
6404  };
6405  if (!incompatibility_checker_.CheckVehicle(vehicle, next_accessor) ||
6406  !requirement_checker_.CheckVehicle(vehicle, next_accessor)) {
6407  model_.solver()->Fail();
6408  }
6409 }
6410 
6412  for (int vehicle = 0; vehicle < model_.vehicles(); vehicle++) {
6413  vehicle_demons_[vehicle] = MakeDelayedConstraintDemon1(
6414  solver(), this, &TypeRegulationsConstraint::CheckRegulationsOnVehicle,
6415  "CheckRegulationsOnVehicle", vehicle);
6416  }
6417  for (int node = 0; node < model_.Size(); node++) {
6418  Demon* node_demon = MakeConstraintDemon1(
6419  solver(), this, &TypeRegulationsConstraint::PropagateNodeRegulations,
6420  "PropagateNodeRegulations", node);
6421  model_.NextVar(node)->WhenBound(node_demon);
6422  model_.VehicleVar(node)->WhenBound(node_demon);
6423  }
6424 }
6425 
6427  for (int vehicle = 0; vehicle < model_.vehicles(); vehicle++) {
6428  CheckRegulationsOnVehicle(vehicle);
6429  }
6430 }
6431 
6432 void RoutingDimension::CloseModel(bool use_light_propagation) {
6433  Solver* const solver = model_->solver();
6434  const auto capacity_lambda = [this](int64 vehicle) {
6435  return vehicle >= 0 ? vehicle_capacities_[vehicle] : kint64max;
6436  };
6437  for (int i = 0; i < capacity_vars_.size(); ++i) {
6438  IntVar* const vehicle_var = model_->VehicleVar(i);
6439  IntVar* const capacity_var = capacity_vars_[i];
6440  if (use_light_propagation) {
6441  solver->AddConstraint(MakeLightElement(
6442  solver, capacity_var, vehicle_var, capacity_lambda,
6443  [this]() { return model_->enable_deep_serialization_; }));
6444  } else {
6445  solver->AddConstraint(solver->MakeEquality(
6446  capacity_var,
6447  solver->MakeElement(capacity_lambda, vehicle_var)->Var()));
6448  }
6449  }
6450  const Solver::IndexEvaluator1 vehicle_class_function = [this](int index) {
6451  return IthElementOrValue<-1>(vehicle_to_class_, index);
6452  };
6453  for (int i = 0; i < fixed_transits_.size(); ++i) {
6454  IntVar* const next_var = model_->NextVar(i);
6455  IntVar* const fixed_transit = fixed_transits_[i];
6456  const auto transit_vehicle_evaluator = [this, i](int64 to,
6457  int64 eval_index) {
6458  return eval_index >= 0 ? transit_evaluator(eval_index)(i, to) : 0;
6459  };
6460  if (use_light_propagation) {
6461  if (class_evaluators_.size() == 1) {
6462  const int class_evaluator_index = class_evaluators_[0];
6463  const auto& unary_callback =
6464  model_->UnaryTransitCallbackOrNull(class_evaluator_index);
6465  if (unary_callback == nullptr) {
6466  solver->AddConstraint(MakeLightElement(
6467  solver, fixed_transit, next_var,
6468  [this, i](int64 to) {
6469  return model_->TransitCallback(class_evaluators_[0])(i, to);
6470  },
6471  [this]() { return model_->enable_deep_serialization_; }));
6472  } else {
6473  fixed_transit->SetValue(unary_callback(i));
6474  }
6475  } else {
6476  solver->AddConstraint(MakeLightElement2(
6477  solver, fixed_transit, next_var, model_->VehicleVar(i),
6478  transit_vehicle_evaluator,
6479  [this]() { return model_->enable_deep_serialization_; }));
6480  }
6481  } else {
6482  if (class_evaluators_.size() == 1) {
6483  const int class_evaluator_index = class_evaluators_[0];
6484  const auto& unary_callback =
6485  model_->UnaryTransitCallbackOrNull(class_evaluator_index);
6486  if (unary_callback == nullptr) {
6487  solver->AddConstraint(solver->MakeEquality(
6488  fixed_transit, solver
6489  ->MakeElement(
6490  [this, i](int64 to) {
6491  return model_->TransitCallback(
6492  class_evaluators_[0])(i, to);
6493  },
6494  model_->NextVar(i))
6495  ->Var()));
6496  } else {
6497  fixed_transit->SetValue(unary_callback(i));
6498  }
6499  } else {
6500  IntVar* const vehicle_class_var =
6501  solver->MakeElement(vehicle_class_function, model_->VehicleVar(i))
6502  ->Var();
6503  solver->AddConstraint(solver->MakeEquality(
6504  fixed_transit, solver
6505  ->MakeElement(transit_vehicle_evaluator,
6506  next_var, vehicle_class_var)
6507  ->Var()));
6508  }
6509  }
6510  }
6511  if (HasBreakConstraints()) {
6512  GlobalVehicleBreaksConstraint* constraint =
6513  model()->solver()->RevAlloc(new GlobalVehicleBreaksConstraint(this));
6514  solver->AddConstraint(constraint);
6515  }
6516 }
6517 
6519  int64 vehicle) const {
6520  DCHECK(transit_evaluator(vehicle) != nullptr);
6521  return transit_evaluator(vehicle)(from_index, to_index);
6522 }
6523 
6525  int64 index, int64 min_value, int64 max_value) const {
6527  const SortedDisjointIntervalList& forbidden = forbidden_intervals_[index];
6528  IntVar* const cumul_var = cumuls_[index];
6529  const int64 min = std::max(min_value, cumul_var->Min());
6530  const int64 max = std::min(max_value, cumul_var->Max());
6531  int64 next_start = min;
6533  forbidden.FirstIntervalGreaterOrEqual(min);
6534  interval != forbidden.end(); ++interval) {
6535  if (next_start > max) break;
6536  if (next_start < interval->start) {
6537  allowed.InsertInterval(next_start, CapSub(interval->start, 1));
6538  }
6539  next_start = CapAdd(interval->end, 1);
6540  }
6541  if (next_start <= max) {
6542  allowed.InsertInterval(next_start, max);
6543  }
6544  return allowed;
6545 }
6546 
6548  int vehicle) {
6549  CHECK_GE(vehicle, 0);
6550  CHECK_LT(vehicle, vehicle_span_upper_bounds_.size());
6551  CHECK_GE(upper_bound, 0);
6552  vehicle_span_upper_bounds_[vehicle] = upper_bound;
6553 }
6554 
6556  int vehicle) {
6557  CHECK_GE(vehicle, 0);
6558  CHECK_LT(vehicle, vehicle_span_cost_coefficients_.size());
6559  CHECK_GE(coefficient, 0);
6560  vehicle_span_cost_coefficients_[vehicle] = coefficient;
6561 }
6562 
6564  CHECK_GE(coefficient, 0);
6565  vehicle_span_cost_coefficients_.assign(model_->vehicles(), coefficient);
6566 }
6567 
6569  CHECK_GE(coefficient, 0);
6570  global_span_cost_coefficient_ = coefficient;
6571 }
6572 
6575  if (!cost.IsNonDecreasing()) {
6576  LOG(WARNING) << "Only non-decreasing cost functions are supported.";
6577  return;
6578  }
6579  if (cost.Value(0) < 0) {
6580  LOG(WARNING) << "Only positive cost functions are supported.";
6581  return;
6582  }
6583  if (index >= cumul_var_piecewise_linear_cost_.size()) {
6584  cumul_var_piecewise_linear_cost_.resize(index + 1);
6585  }
6586  PiecewiseLinearCost& piecewise_linear_cost =
6587  cumul_var_piecewise_linear_cost_[index];
6588  piecewise_linear_cost.var = cumuls_[index];
6589  piecewise_linear_cost.cost = absl::make_unique<PiecewiseLinearFunction>(cost);
6590 }
6591 
6593  return (index < cumul_var_piecewise_linear_cost_.size() &&
6594  cumul_var_piecewise_linear_cost_[index].var != nullptr);
6595 }
6596 
6598  int64 index) const {
6599  if (index < cumul_var_piecewise_linear_cost_.size() &&
6600  cumul_var_piecewise_linear_cost_[index].var != nullptr) {
6601  return cumul_var_piecewise_linear_cost_[index].cost.get();
6602  }
6603  return nullptr;
6604 }
6605 
6606 namespace {
6607 IntVar* BuildVarFromExprAndIndexActiveState(const RoutingModel* model,
6608  IntExpr* expr, int index) {
6609  Solver* const solver = model->solver();
6610  if (model->IsStart(index) || model->IsEnd(index)) {
6611  const int vehicle = model->VehicleIndex(index);
6612  DCHECK_GE(vehicle, 0);
6613  return solver->MakeProd(expr, model->VehicleCostsConsideredVar(vehicle))
6614  ->Var();
6615  }
6616  return solver->MakeProd(expr, model->ActiveVar(index))->Var();
6617 }
6618 } // namespace
6619 
6620 void RoutingDimension::SetupCumulVarPiecewiseLinearCosts(
6621  std::vector<IntVar*>* cost_elements) const {
6622  CHECK(cost_elements != nullptr);
6623  Solver* const solver = model_->solver();
6624  for (int i = 0; i < cumul_var_piecewise_linear_cost_.size(); ++i) {
6625  const PiecewiseLinearCost& piecewise_linear_cost =
6626  cumul_var_piecewise_linear_cost_[i];
6627  if (piecewise_linear_cost.var != nullptr) {
6628  IntExpr* const expr = solver->MakePiecewiseLinearExpr(
6629  piecewise_linear_cost.var, *piecewise_linear_cost.cost);
6630  IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6631  cost_elements->push_back(cost_var);
6632  // TODO(user): Check if it wouldn't be better to minimize
6633  // piecewise_linear_cost.var here.
6634  model_->AddWeightedVariableMinimizedByFinalizer(cost_var, 0);
6635  }
6636  }
6637 }
6638 
6640  int64 coefficient) {
6641  if (index >= cumul_var_soft_upper_bound_.size()) {
6642  cumul_var_soft_upper_bound_.resize(index + 1, {nullptr, 0, 0});
6643  }
6644  cumul_var_soft_upper_bound_[index] = {cumuls_[index], upper_bound,
6645  coefficient};
6646 }
6647 
6649  return (index < cumul_var_soft_upper_bound_.size() &&
6650  cumul_var_soft_upper_bound_[index].var != nullptr);
6651 }
6652 
6654  if (index < cumul_var_soft_upper_bound_.size() &&
6655  cumul_var_soft_upper_bound_[index].var != nullptr) {
6656  return cumul_var_soft_upper_bound_[index].bound;
6657  }
6658  return cumuls_[index]->Max();
6659 }
6660 
6662  int64 index) const {
6663  if (index < cumul_var_soft_upper_bound_.size() &&
6664  cumul_var_soft_upper_bound_[index].var != nullptr) {
6665  return cumul_var_soft_upper_bound_[index].coefficient;
6666  }
6667  return 0;
6668 }
6669 
6670 void RoutingDimension::SetupCumulVarSoftUpperBoundCosts(
6671  std::vector<IntVar*>* cost_elements) const {
6672  CHECK(cost_elements != nullptr);
6673  Solver* const solver = model_->solver();
6674  for (int i = 0; i < cumul_var_soft_upper_bound_.size(); ++i) {
6675  const SoftBound& soft_bound = cumul_var_soft_upper_bound_[i];
6676  if (soft_bound.var != nullptr) {
6677  IntExpr* const expr = solver->MakeSemiContinuousExpr(
6678  solver->MakeSum(soft_bound.var, -soft_bound.bound), 0,
6679  soft_bound.coefficient);
6680  IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6681  cost_elements->push_back(cost_var);
6682  // NOTE: We minimize the cost here instead of minimizing the cumul
6683  // variable, to avoid setting the cumul to earlier than necessary.
6684  model_->AddWeightedVariableMinimizedByFinalizer(cost_var,
6685  soft_bound.coefficient);
6686  }
6687  }
6688 }
6689 
6691  int64 coefficient) {
6692  if (index >= cumul_var_soft_lower_bound_.size()) {
6693  cumul_var_soft_lower_bound_.resize(index + 1, {nullptr, 0, 0});
6694  }
6695  cumul_var_soft_lower_bound_[index] = {cumuls_[index], lower_bound,
6696  coefficient};
6697 }
6698 
6700  return (index < cumul_var_soft_lower_bound_.size() &&
6701  cumul_var_soft_lower_bound_[index].var != nullptr);
6702 }
6703 
6705  if (index < cumul_var_soft_lower_bound_.size() &&
6706  cumul_var_soft_lower_bound_[index].var != nullptr) {
6707  return cumul_var_soft_lower_bound_[index].bound;
6708  }
6709  return cumuls_[index]->Min();
6710 }
6711 
6713  int64 index) const {
6714  if (index < cumul_var_soft_lower_bound_.size() &&
6715  cumul_var_soft_lower_bound_[index].var != nullptr) {
6716  return cumul_var_soft_lower_bound_[index].coefficient;
6717  }
6718  return 0;
6719 }
6720 
6721 void RoutingDimension::SetupCumulVarSoftLowerBoundCosts(
6722  std::vector<IntVar*>* cost_elements) const {
6723  CHECK(cost_elements != nullptr);
6724  Solver* const solver = model_->solver();
6725  for (int i = 0; i < cumul_var_soft_lower_bound_.size(); ++i) {
6726  const SoftBound& soft_bound = cumul_var_soft_lower_bound_[i];
6727  if (soft_bound.var != nullptr) {
6728  IntExpr* const expr = solver->MakeSemiContinuousExpr(
6729  solver->MakeDifference(soft_bound.bound, soft_bound.var), 0,
6730  soft_bound.coefficient);
6731  IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6732  cost_elements->push_back(cost_var);
6733  // NOTE: We minimize the cost here instead of maximizing the cumul
6734  // variable, to avoid setting the cumul to later than necessary.
6735  model_->AddWeightedVariableMinimizedByFinalizer(cost_var,
6736  soft_bound.coefficient);
6737  }
6738  }
6739 }
6740 
6741 void RoutingDimension::SetupGlobalSpanCost(
6742  std::vector<IntVar*>* cost_elements) const {
6743  CHECK(cost_elements != nullptr);
6744  Solver* const solver = model_->solver();
6745  if (global_span_cost_coefficient_ != 0) {
6746  std::vector<IntVar*> end_cumuls;
6747  for (int i = 0; i < model_->vehicles(); ++i) {
6748  end_cumuls.push_back(solver
6749  ->MakeProd(model_->vehicle_costs_considered_[i],
6750  cumuls_[model_->End(i)])
6751  ->Var());
6752  }
6753  IntVar* const max_end_cumul = solver->MakeMax(end_cumuls)->Var();
6755  max_end_cumul, global_span_cost_coefficient_);
6756  std::vector<IntVar*> start_cumuls;
6757  for (int i = 0; i < model_->vehicles(); ++i) {
6758  IntVar* global_span_cost_start_cumul = solver->MakeIntVar(0, kint64max);
6759  solver->AddConstraint(solver->MakeIfThenElseCt(
6760  model_->vehicle_costs_considered_[i], cumuls_[model_->Start(i)],
6761  max_end_cumul, global_span_cost_start_cumul));
6762  start_cumuls.push_back(global_span_cost_start_cumul);
6763  }
6764  IntVar* const min_start_cumul = solver->MakeMin(start_cumuls)->Var();
6766  min_start_cumul, global_span_cost_coefficient_);
6767  // If there is a single vehicle, model the cost as the sum of its transits
6768  // to avoid slow (infinite) propagation loops.
6769  // TODO(user): Avoid slow propagation in the path constraints.
6770  if (model_->vehicles() == 1) {
6771  for (int var_index = 0; var_index < model_->Size(); ++var_index) {
6773  slacks_[var_index], global_span_cost_coefficient_);
6774  cost_elements->push_back(
6775  solver
6776  ->MakeProd(
6777  model_->vehicle_costs_considered_[0],
6778  solver->MakeProd(
6779  solver->MakeProd(
6780  solver->MakeSum(transits_[var_index],
6781  dependent_transits_[var_index]),
6782  global_span_cost_coefficient_),
6783  model_->ActiveVar(var_index)))
6784  ->Var());
6785  }
6786  } else {
6787  IntVar* const end_range =
6788  solver->MakeDifference(max_end_cumul, min_start_cumul)->Var();
6789  end_range->SetMin(0);
6790  cost_elements->push_back(
6791  solver->MakeProd(end_range, global_span_cost_coefficient_)->Var());
6792  }
6793  }
6794 }
6795 
6797  std::vector<IntervalVar*> breaks, int vehicle,
6798  std::vector<int64> node_visit_transits) {
6799  if (breaks.empty()) return;
6800  const int visit_evaluator = model()->RegisterTransitCallback(
6801  [node_visit_transits](int64 from, int64 to) {
6802  return node_visit_transits[from];
6803  });
6804  SetBreakIntervalsOfVehicle(std::move(breaks), vehicle, visit_evaluator, -1);
6805 }
6806 
6808  std::vector<IntervalVar*> breaks, int vehicle,
6809  std::vector<int64> node_visit_transits,
6810  std::function<int64(int64, int64)> group_delays) {
6811  if (breaks.empty()) return;
6812  const int visit_evaluator = model()->RegisterTransitCallback(
6813  [node_visit_transits](int64 from, int64 to) {
6814  return node_visit_transits[from];
6815  });
6816  const int delay_evaluator = model()->RegisterTransitCallback(group_delays);
6817  SetBreakIntervalsOfVehicle(std::move(breaks), vehicle, visit_evaluator,
6818  delay_evaluator);
6819 }
6820 
6822  std::vector<IntervalVar*> breaks, int vehicle, int pre_travel_evaluator,
6823  int post_travel_evaluator) {
6824  DCHECK_LE(0, vehicle);
6825  DCHECK_LT(vehicle, model_->vehicles());
6826  if (breaks.empty()) return;
6827  if (!break_constraints_are_initialized_) InitializeBreaks();
6828  vehicle_break_intervals_[vehicle] = std::move(breaks);
6829  vehicle_pre_travel_evaluators_[vehicle] = pre_travel_evaluator;
6830  vehicle_post_travel_evaluators_[vehicle] = post_travel_evaluator;
6831  // Breaks intervals must be fixed by search.
6832  for (IntervalVar* const interval : vehicle_break_intervals_[vehicle]) {
6834  if (interval->MayBePerformed() && !interval->MustBePerformed()) {
6835  model_->AddVariableTargetToFinalizer(interval->PerformedExpr()->Var(), 0);
6836  }
6837  model_->AddVariableTargetToFinalizer(interval->SafeStartExpr(0)->Var(),
6838  kint64min);
6839  model_->AddVariableTargetToFinalizer(interval->SafeDurationExpr(0)->Var(),
6840  kint64min);
6841  }
6842  // When a vehicle has breaks, if its start and end are fixed,
6843  // then propagation keeps the cumuls min and max on its path feasible.
6844  model_->AddVariableTargetToFinalizer(CumulVar(model_->End(vehicle)),
6845  kint64min);
6846  model_->AddVariableTargetToFinalizer(CumulVar(model_->Start(vehicle)),
6847  kint64max);
6848 }
6849 
6851  DCHECK(!break_constraints_are_initialized_);
6852  const int num_vehicles = model_->vehicles();
6853  vehicle_break_intervals_.resize(num_vehicles);
6854  vehicle_pre_travel_evaluators_.resize(num_vehicles, -1);
6855  vehicle_post_travel_evaluators_.resize(num_vehicles, -1);
6856  vehicle_break_distance_duration_.resize(num_vehicles);
6857  break_constraints_are_initialized_ = true;
6858 }
6859 
6861  return break_constraints_are_initialized_;
6862 }
6863 
6864 const std::vector<IntervalVar*>& RoutingDimension::GetBreakIntervalsOfVehicle(
6865  int vehicle) const {
6866  DCHECK_LE(0, vehicle);
6867  DCHECK_LT(vehicle, vehicle_break_intervals_.size());
6868  return vehicle_break_intervals_[vehicle];
6869 }
6870 
6872  DCHECK_LE(0, vehicle);
6873  DCHECK_LT(vehicle, vehicle_pre_travel_evaluators_.size());
6874  return vehicle_pre_travel_evaluators_[vehicle];
6875 }
6876 
6878  DCHECK_LE(0, vehicle);
6879  DCHECK_LT(vehicle, vehicle_post_travel_evaluators_.size());
6880  return vehicle_post_travel_evaluators_[vehicle];
6881 }
6882 
6884  int64 duration,
6885  int vehicle) {
6886  DCHECK_LE(0, vehicle);
6887  DCHECK_LT(vehicle, model_->vehicles());
6888  if (!break_constraints_are_initialized_) InitializeBreaks();
6889  vehicle_break_distance_duration_[vehicle].emplace_back(distance, duration);
6890  // When a vehicle has breaks, if its start and end are fixed,
6891  // then propagation keeps the cumuls min and max on its path feasible.
6892  model_->AddVariableTargetToFinalizer(CumulVar(model_->End(vehicle)),
6893  kint64min);
6894  model_->AddVariableTargetToFinalizer(CumulVar(model_->Start(vehicle)),
6895  kint64max);
6896 }
6897 
6898 const std::vector<std::pair<int64, int64>>&
6900  DCHECK_LE(0, vehicle);
6901  DCHECK_LT(vehicle, vehicle_break_distance_duration_.size());
6902  return vehicle_break_distance_duration_[vehicle];
6903 }
6904 
6906  PickupToDeliveryLimitFunction limit_function, int pair_index) {
6907  CHECK_GE(pair_index, 0);
6908  if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6909  pickup_to_delivery_limits_per_pair_index_.resize(pair_index + 1);
6910  }
6911  pickup_to_delivery_limits_per_pair_index_[pair_index] =
6912  std::move(limit_function);
6913 }
6914 
6916  return !pickup_to_delivery_limits_per_pair_index_.empty();
6917 }
6918 
6920  int pickup,
6921  int delivery) const {
6922  DCHECK_GE(pair_index, 0);
6923 
6924  if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6925  return kint64max;
6926  }
6927  const PickupToDeliveryLimitFunction& pickup_to_delivery_limit_function =
6928  pickup_to_delivery_limits_per_pair_index_[pair_index];
6929  if (!pickup_to_delivery_limit_function) {
6930  // No limit function set for this pair.
6931  return kint64max;
6932  }
6933  DCHECK_GE(pickup, 0);
6934  DCHECK_GE(delivery, 0);
6935  return pickup_to_delivery_limit_function(pickup, delivery);
6936 }
6937 
6938 void RoutingDimension::SetupSlackAndDependentTransitCosts() const {
6939  if (model_->vehicles() == 0) return;
6940  // Figure out whether all vehicles have the same span cost coefficient or not.
6941  bool all_vehicle_span_costs_are_equal = true;
6942  for (int i = 1; i < model_->vehicles(); ++i) {
6943  all_vehicle_span_costs_are_equal &= vehicle_span_cost_coefficients_[i] ==
6944  vehicle_span_cost_coefficients_[0];
6945  }
6946 
6947  if (all_vehicle_span_costs_are_equal &&
6948  vehicle_span_cost_coefficients_[0] == 0) {
6949  return; // No vehicle span cost.
6950  }
6951 
6952  // Make sure that the vehicle's start cumul will be maximized in the end;
6953  // and that the vehicle's end cumul and the node's slacks will be minimized.
6954  // Note that we don't do that if there was no span cost (see the return
6955  // clause above), because in that case we want the dimension cumul to
6956  // remain unconstrained. Since transitions depend on base dimensions, we
6957  // have to make sure the slacks of base dimensions are taken care of.
6958  // Also, it makes more sense to make decisions from the root of the tree
6959  // towards to leaves, and hence the slacks are pushed in reverse order.
6960  std::vector<const RoutingDimension*> dimensions_with_relevant_slacks = {this};
6961  while (true) {
6962  const RoutingDimension* next =
6963  dimensions_with_relevant_slacks.back()->base_dimension_;
6964  if (next == nullptr || next == dimensions_with_relevant_slacks.back()) {
6965  break;
6966  }
6967  dimensions_with_relevant_slacks.push_back(next);
6968  }
6969 
6970  for (auto it = dimensions_with_relevant_slacks.rbegin();
6971  it != dimensions_with_relevant_slacks.rend(); ++it) {
6972  for (int i = 0; i < model_->vehicles(); ++i) {
6973  model_->AddVariableTargetToFinalizer((*it)->cumuls_[model_->End(i)],
6974  kint64min);
6975  model_->AddVariableTargetToFinalizer((*it)->cumuls_[model_->Start(i)],
6976  kint64max);
6977  }
6978  for (IntVar* const slack : (*it)->slacks_) {
6979  model_->AddVariableTargetToFinalizer(slack, kint64min);
6980  }
6981  }
6982 }
6983 } // namespace operations_research
operations_research::RoutingModel::VariableIndexEvaluator2
std::function< StateDependentTransit(int64, int64)> VariableIndexEvaluator2
Definition: routing.h:267
operations_research::TypeRegulationsConstraint::TypeRegulationsConstraint
TypeRegulationsConstraint(const RoutingModel &model)
Definition: routing.cc:6378
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:4105
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::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:4086
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::VehicleClassIndex
RoutingVehicleClassIndex VehicleClassIndex
Definition: routing.h:239
gtl::LookupOrInsert
Collection::value_type::second_type & LookupOrInsert(Collection *const collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:207
operations_research::RoutingIndexManager::GetStartIndex
int64 GetStartIndex(int vehicle) const
Definition: routing_index_manager.h:73
operations_research::RoutingDimension::GetPreTravelEvaluatorOfVehicle
int GetPreTravelEvaluatorOfVehicle(int vehicle) const
!defined(SWIGPYTHON)
Definition: routing.cc:6871
operations_research::RoutingModel::GetDimensionsWithSoftOrSpanCosts
std::vector< RoutingDimension * > GetDimensionsWithSoftOrSpanCosts() const
Returns dimensions with soft or vehicle span costs.
Definition: routing.cc:4978
var
IntVar * var
Definition: expr_array.cc:1858
operations_research::RoutingModel::TransitCallback1
RoutingTransitCallback1 TransitCallback1
Definition: routing.h:240
operations_research::TypeRegulationsConstraint
The following constraint ensures that incompatibilities and requirements between types are respected.
Definition: routing.h:2220
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:3660
tail
int64 tail
Definition: routing_flow.cc:127
operations_research::RoutingModel::IgnoreDisjunctionsAlreadyForcedToZero
void IgnoreDisjunctionsAlreadyForcedToZero()
SPECIAL: Makes the solver ignore all the disjunctions whose active variables are all trivially zero (...
Definition: routing.cc:1629
operations_research::TypeRegulationsChecker
Definition: routing.h:2080
operations_research::RoutingModel::PICKUP_AND_DELIVERY_NO_ORDER
@ PICKUP_AND_DELIVERY_NO_ORDER
Any precedence is accepted.
Definition: routing.h:230
operations_research::RoutingModel::VehicleTypeContainer::vehicles_per_vehicle_class
std::vector< std::deque< int > > vehicles_per_vehicle_class
Definition: routing.h:377
CP_ROUTING_ADD_OPERATOR
#define CP_ROUTING_ADD_OPERATOR(operator_type, cp_operator_type)
Definition: routing.cc:4415
operations_research::SimpleBoundCosts::BoundCost
Definition: routing.h:2251
operations_research::RoutingModel::RegisterUnaryTransitCallback
int RegisterUnaryTransitCallback(TransitCallback1 callback)
Registers 'callback' and returns its index.
Definition: routing.cc:868
operations_research::RoutingModel::vehicles
int vehicles() const
Returns the number of vehicle routes in the model.
Definition: routing.h:1317
operations_research::SolveModelWithSat
bool SolveModelWithSat(const RoutingModel &model, const RoutingSearchParameters &search_parameters, const Assignment *initial_solution, Assignment *solution)
Attempts to solve the model using the cp-sat solver.
Definition: routing_sat.cc:495
operations_research::ForwardEbertGraph
Definition: ebert_graph.h:1564
min
int64 min
Definition: alldiff_cst.cc:138
operations_research::RoutingModel::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED
@ TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED
The visit doesn't have an impact on the number of types 'T' on the route, as it's (virtually) added a...
Definition: routing.h:767
operations_research::RoutingModel::VehicleClass
Definition: routing.h:322
operations_research::RouteConstructor
Definition: routing.cc:2473
integral_types.h
map_util.h
CP_ROUTING_ADD_OPERATOR2
#define CP_ROUTING_ADD_OPERATOR2(operator_type, cp_operator_class)
Definition: routing.cc:4424
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::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
operations_research::CapSub
int64 CapSub(int64 x, int64 y)
Definition: saturated_arithmetic.h:154
operations_research::RoutingModel::MakePathSpansAndTotalSlacks
Constraint * MakePathSpansAndTotalSlacks(const RoutingDimension *dimension, std::vector< IntVar * > spans, std::vector< IntVar * > total_slacks)
For every vehicle of the routing model:
Definition: routing.cc:5888
max
int64 max
Definition: alldiff_cst.cc:139
operations_research::RoutingModel::GetVehicleClassesCount
int GetVehicleClassesCount() const
Returns the number of different vehicle classes in the model.
Definition: routing.h:1250
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:6712
operations_research::SweepBuilder::Next
Decision * Next(Solver *const solver) override
Definition: routing.cc:3016
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::RoutingModel::sweep_arranger
SweepArranger * sweep_arranger() const
Returns the sweep arranger to be used by routing heuristics.
Definition: routing.h:1138
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::SweepArranger::ArrangeIndices
void ArrangeIndices(std::vector< int64 > *indices)
Definition: routing.cc:2970
operations_research::RoutingModel::CloseModelWithParameters
void CloseModelWithParameters(const RoutingSearchParameters &search_parameters)
Same as above taking search parameters (as of 10/2015 some the parameters have to be set when closing...
Definition: routing.cc:2048
operations_research::RoutingModel::TransitCallback2
RoutingTransitCallback2 TransitCallback2
Definition: routing.h:241
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::LinearSumAssignment::SetArcCost
void SetArcCost(ArcIndex arc, CostValue cost)
Definition: linear_assignment.h:1009
operations_research::RoutingModel::SetAmortizedCostFactorsOfVehicle
void SetAmortizedCostFactorsOfVehicle(int64 linear_cost_factor, int64 quadratic_cost_factor, int vehicle)
Sets the linear and quadratic cost factor of the given vehicle.
Definition: routing.cc:1330
operations_research::RoutingModel::GetVisitType
int GetVisitType(int64 index) const
Definition: routing.cc:4056
operations_research::RoutingTransitCallback1
std::function< int64(int64)> RoutingTransitCallback1
Definition: routing_types.h:41
operations_research::DefaultRoutingModelParameters
RoutingModelParameters DefaultRoutingModelParameters()
Definition: routing_parameters.cc:32
operations_research::CapProd
int64 CapProd(int64 x, int64 y)
Definition: saturated_arithmetic.h:231
lp_data.h
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::SortedDisjointIntervalList::end
ConstIterator end() const
Definition: sorted_interval_list.h:481
operations_research::TypeRegulationsConstraint::InitialPropagate
void InitialPropagate() override
Definition: routing.cc:6426
operations_research::RoutingModel::CostClass
Definition: routing.h:271
operations_research::LinearSumAssignment
Definition: linear_assignment.h:226
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::RegisterPositiveTransitCallback
int RegisterPositiveTransitCallback(TransitCallback2 callback)
Definition: routing.cc:910
operations_research::RoutingModelInspector::EndVisitConstraint
void EndVisitConstraint(const std::string &type_name, const Constraint *const constraint) override
Definition: routing.cc:1950
operations_research::RoutingDimension::HasBreakConstraints
bool HasBreakConstraints() const
Returns true if any break interval or break distance was defined.
Definition: routing.cc:6860
operations_research::FillPathEvaluation
void FillPathEvaluation(const std::vector< int64 > &path, const RoutingModel::TransitCallback2 &evaluator, std::vector< int64 > *values)
Definition: routing.cc:6176
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::SweepArranger::SweepArranger
SweepArranger(const std::vector< std::pair< int64, int64 >> &points)
Definition: routing.cc:2960
operations_research::RoutingModelVisitor::kRemoveValues
static const char kRemoveValues[]
Definition: routing.h:1868
operations_research::RoutingModel::VehicleClass::fixed_cost
int64 fixed_cost
Contrarily to CostClass, here we need strict equivalence.
Definition: routing.h:326
operations_research::RoutingModel::GetSingleNodesOfType
const std::vector< int > & GetSingleNodesOfType(int type) const
Definition: routing.cc:4061
operations_research::RoutingDimension::NodePrecedence
Definition: routing.h:2571
operations_research::RoutingModel::GetNumberOfRejectsInFirstSolution
int64 GetNumberOfRejectsInFirstSolution(const RoutingSearchParameters &search_parameters) const
Definition: routing.cc:3612
operations_research::MakeTypeRegulationsFilter
IntVarLocalSearchFilter * MakeTypeRegulationsFilter(const RoutingModel &routing_model)
Definition: routing_search.cc:818
operations_research::RoutingModel::GetVisitTypePolicy
VisitTypePolicy GetVisitTypePolicy(int64 index) const
Definition: routing.cc:4071
start_min
Rev< int64 > start_min
Definition: sched_constraints.cc:241
operations_research::RoutingModel::AddDimensionWithVehicleTransitAndCapacity
bool AddDimensionWithVehicleTransitAndCapacity(const std::vector< int > &evaluator_indices, int64 slack_max, std::vector< int64 > vehicle_capacities, bool fix_start_cumul_to_zero, const std::string &name)
Definition: routing.cc:969
operations_research::RoutingModel::GetPickupAndDeliveryPolicyOfVehicle
PickupAndDeliveryPolicy GetPickupAndDeliveryPolicyOfVehicle(int vehicle) const
Definition: routing.cc:1755
logging.h
routing.h
expr_
IntVar *const expr_
Definition: element.cc:85
operations_research::RoutingDimension::GetCumulVarPiecewiseLinearCost
const PiecewiseLinearFunction * GetCumulVarPiecewiseLinearCost(int64 index) const
Returns the piecewise linear cost of a cumul variable for a given variable index.
Definition: routing.cc:6597
gtl::ITIVector::push_back
void push_back(const value_type &x)
Definition: int_type_indexed_vector.h:157
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::RoutingModel::PICKUP_AND_DELIVERY_LIFO
@ PICKUP_AND_DELIVERY_LIFO
Deliveries must be performed in reverse order of pickups.
Definition: routing.h:232
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::GetMutableLocalCumulMPOptimizer
LocalDimensionCumulOptimizer * GetMutableLocalCumulMPOptimizer(const RoutingDimension &dimension) const
Definition: routing.cc:1255
operations_research::RoutingModel
Definition: routing.h:211
operations_research::RoutingIndexManager::GetEndIndex
int64 GetEndIndex(int vehicle) const
Definition: routing_index_manager.h:74
operations_research::RoutingModel::RoutingDimension
friend class RoutingDimension
Definition: routing.h:1856
operations_research::RoutingModelInspector::RoutingModelInspector
RoutingModelInspector(RoutingModel *model)
Definition: routing.cc:1911
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:6899
var_indices
std::vector< int > var_indices
Definition: sat/lp_utils.cc:441
operations_research::RoutingModel::StateDependentTransitCallback
const VariableIndexEvaluator2 & StateDependentTransitCallback(int callback_index) const
Definition: routing.h:414
operations_research::RoutingModel::RoutingModel
RoutingModel(const RoutingIndexManager &index_manager)
Constructor taking an index manager.
Definition: routing.cc:759
value
int64 value
Definition: demon_profiler.cc:43
operations_research::RoutingModel::IsStart
bool IsStart(int64 index) const
Returns true if 'index' represents the first node of a route.
Definition: routing.cc:3879
operations_research::RoutingIndexManager::IndexToNode
NodeIndex IndexToNode(int64 index) const
Definition: routing_index_manager.h:88
operations_research::RoutingModel::ROUTING_FAIL
@ ROUTING_FAIL
No solution found to the problem after calling RoutingModel::Solve().
Definition: routing.h:220
operations_research::SweepIndexSortDistance
Definition: routing.cc:2954
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:6653
gtl::FindWithDefault
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:26
operations_research::RoutingModel::AddPickupAndDeliverySets
void AddPickupAndDeliverySets(DisjunctionIndex pickup_disjunction, DisjunctionIndex delivery_disjunction)
Same as AddPickupAndDelivery but notifying that the performed node from the disjunction of index 'pic...
Definition: routing.cc:1697
saturated_arithmetic.h
operations_research
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
Definition: dense_doubly_linked_list.h:21
operations_research::RoutingDimension::transit_evaluator
const RoutingModel::TransitCallback2 & transit_evaluator(int vehicle) const
Returns the callback evaluating the transit value between two node indices for a given vehicle.
Definition: routing.h:2369
operations_research::DimensionSchedulingStatus
DimensionSchedulingStatus
Definition: routing_lp_scheduling.h:126
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
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
operations_research::MemoryUsage
std::string MemoryUsage()
Definition: stats.cc:25
operations_research::TypeRegulationsChecker::TypePolicyOccurrence::position_of_last_type_on_vehicle_up_to_visit
int position_of_last_type_on_vehicle_up_to_visit
Position of the last node of policy TYPE_ON_VEHICLE_UP_TO_VISIT visited on the route.
Definition: routing.h:2108
protoutil.h
connectivity.h
operations_research::RoutingModel::PICKUP_AND_DELIVERY_FIFO
@ PICKUP_AND_DELIVERY_FIFO
Deliveries must be performed in the same order as pickups.
Definition: routing.h:234
kint64min
static const int64 kint64min
Definition: integral_types.h:60
operations_research::RoutingModel::AddDimensionWithVehicleTransits
bool AddDimensionWithVehicleTransits(const std::vector< int > &evaluator_indices, int64 slack_max, int64 capacity, bool fix_start_cumul_to_zero, const std::string &name)
Definition: routing.cc:951
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:3911
operations_research::RangeIntToIntFunction
Definition: range_query_function.h:28
operations_research::RoutingModel::IsVehicleUsed
bool IsVehicleUsed(const Assignment &assignment, int vehicle) const
Returns true if the route of 'vehicle' is non empty in 'assignment'.
Definition: routing.cc:3883
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:6264
operations_research::RoutingModel::VehicleClass::cost_class_index
CostClassIndex cost_class_index
The cost class of the vehicle.
Definition: routing.h:324
operations_research::MakeMaxActiveVehiclesFilter
IntVarLocalSearchFilter * MakeMaxActiveVehiclesFilter(const RoutingModel &routing_model)
Definition: routing_search.cc:109
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::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:6258
operations_research::SweepIndex::distance
double distance
Definition: routing.cc:2945
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:3486
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:3629
operations_research::RoutingDimension::SetPickupToDeliveryLimitFunctionForPair
void SetPickupToDeliveryLimitFunctionForPair(PickupToDeliveryLimitFunction limit_function, int pair_index)
Definition: routing.cc:6905
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:6518
operations_research::RoutingModel::GetMutableDimension
RoutingDimension * GetMutableDimension(const std::string &dimension_name) const
Returns a dimension from its name.
Definition: routing.cc:1282
int64
int64_t int64
Definition: integral_types.h:34
operations_research::BaseIntExpr
This is the base class for all expressions that are not variables.
Definition: constraint_solveri.h:109
routing_enums.pb.h
operations_research::RoutingModel::CostsAreHomogeneousAcrossVehicles
bool CostsAreHomogeneousAcrossVehicles() const
Whether costs are homogeneous across all vehicles.
Definition: routing.h:1206
operations_research::SweepIndexDistanceComparator
struct operations_research::SweepIndexSortDistance SweepIndexDistanceComparator
operations_research::RoutingDimension::~RoutingDimension
~RoutingDimension()
Definition: routing.cc:5921
operations_research::MakeCPFeasibilityFilter
IntVarLocalSearchFilter * MakeCPFeasibilityFilter(const RoutingModel *routing_model)
Definition: routing_search.cc:2661
operations_research::MathUtil::FastInt64Round
static int64 FastInt64Round(double x)
Definition: mathutil.h:138
operations_research::DefaultRoutingSearchParameters
RoutingSearchParameters DefaultRoutingSearchParameters()
Definition: routing_parameters.cc:45
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:3819
operations_research::RoutingModel::HasHardTypeIncompatibilities
bool HasHardTypeIncompatibilities() const
Returns true iff any hard (resp.
Definition: routing.h:798
operations_research::RoutingModel::AddTemporalTypeIncompatibility
void AddTemporalTypeIncompatibility(int type1, int type2)
Definition: routing.cc:4095
operations_research::RoutingModel::GetVehicleClassIndexOfVehicle
VehicleClassIndex GetVehicleClassIndexOfVehicle(int64 vehicle) const
Definition: routing.h:1245
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::~RoutingModel
~RoutingModel()
Definition: routing.cc:852
index
int index
Definition: pack.cc:508
operations_research::RoutingDimension::name
const std::string & name() const
Returns the name of the dimension.
Definition: routing.h:2543
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::SweepIndexSortAngle::operator()
bool operator()(const SweepIndex &node1, const SweepIndex &node2) const
Definition: routing.cc:2949
operations_research::RevSwitch::Switch
void Switch(Solver *const solver)
Definition: constraint_solveri.h:403
operations_research::RoutingModel::SetPickupAndDeliveryPolicyOfAllVehicles
void SetPickupAndDeliveryPolicyOfAllVehicles(PickupAndDeliveryPolicy policy)
Sets the Pickup and delivery policy of all vehicles.
Definition: routing.cc:1746
operations_research::RoutingModel::AddToAssignment
void AddToAssignment(IntVar *const var)
Adds an extra variable to the vehicle routing assignment.
Definition: routing.cc:5551
operations_research::RouteConstructor::final_routes
const std::vector< std::vector< int > > & final_routes() const
Definition: routing.cc:2628
operations_research::RoutingModel::GetDimensions
const std::vector< RoutingDimension * > & GetDimensions() const
Returns all dimensions of the model.
Definition: routing.h:546
gtl::ITIVector::empty
bool empty() const
Definition: int_type_indexed_vector.h:155
operations_research::MakeVehicleAmortizedCostFilter
IntVarLocalSearchFilter * MakeVehicleAmortizedCostFilter(const RoutingModel &routing_model)
Definition: routing_search.cc:669
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::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
gtl::FindOrDie
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:176
operations_research::IntVarFilteredDecisionBuilder
Decision builder building a solution using heuristics with local search filters to evaluate its feasi...
Definition: routing.h:2897
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:6883
operations_research::RouteConstructor::~RouteConstructor
~RouteConstructor()
Definition: routing.cc:2504
operations_research::RoutingIndexManager
Manager for any NodeIndex <-> variable index conversion.
Definition: routing_index_manager.h:48
operations_research::RoutingModel::GetCostClassesCount
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
Definition: routing.h:1240
operations_research::RoutingModel::VehicleClass::dimension_capacities
gtl::ITIVector< DimensionIndex, int64 > dimension_capacities
Definition: routing.h:341
operations_research::RoutingModel::Size
int64 Size() const
Returns the number of next variables in the model.
Definition: routing.h:1319
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
operations_research::SweepIndexAngleComparator
struct operations_research::SweepIndexSortAngle SweepIndexAngleComparator
cost
int64 cost
Definition: routing_flow.cc:130
stats.h
operations_research::BOOL_FALSE
@ BOOL_FALSE
Definition: optional_boolean.pb.h:62
a
int64 a
Definition: constraint_solver/table.cc:42
operations_research::GlobalDimensionCumulOptimizer
Definition: routing_lp_scheduling.h:679
routing_lp_scheduling.h
operations_research::RoutingModel::GetAllDimensionNames
std::vector< std::string > GetAllDimensionNames() const
Outputs the names of all dimensions added to the routing engine.
Definition: routing.cc:1222
operations_research::LinkComparator
struct operations_research::LinkSort LinkComparator
operations_research::RoutingModel::VehicleClass::unvisitable_nodes_fprint
uint64 unvisitable_nodes_fprint
Fingerprint of unvisitable non-start/end nodes.
Definition: routing.h:346
min_cost_flow.h
constraint_solver.h
util_time::DecodeGoogleApiProto
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
Definition: protoutil.h:40
operations_research::RoutingModel::AddDimension
bool AddDimension(int evaluator_index, int64 slack_max, int64 capacity, bool fix_start_cumul_to_zero, const std::string &name)
Model creation.
Definition: routing.cc:941
operations_research::SweepIndex
Definition: routing.cc:2938
operations_research::SortedDisjointIntervalList::Iterator
IntervalSet::iterator Iterator
Definition: sorted_interval_list.h:395
operations_research::SweepIndex::index
int64 index
Definition: routing.cc:2943
cumuls_
const std::vector< IntVar * > cumuls_
Definition: graph_constraints.cc:670
operations_research::SortedDisjointIntervalList
This class represents a sorted list of disjoint, closed intervals.
Definition: sorted_interval_list.h:387
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:6573
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:5530
operations_research::SweepIndex::~SweepIndex
~SweepIndex()
Definition: routing.cc:2941
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::TypeRegulationsChecker::InitializeCheck
void InitializeCheck(int vehicle, const std::function< int64(int64)> &next_accessor)
Definition: routing.cc:6233
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:4186
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:3772
operations_research::RoutingModel::PickupAndDeliveryPolicy
PickupAndDeliveryPolicy
Types of precedence policy applied to pickup and delivery pairs.
Definition: routing.h:228
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::RoutingModel::DisjunctionIndex
RoutingDisjunctionIndex DisjunctionIndex
Definition: routing.h:238
operations_research::DimensionSchedulingStatus::INFEASIBLE
@ INFEASIBLE
operations_research::RoutingModel::GetHomogeneousCost
int64 GetHomogeneousCost(int64 from_index, int64 to_index) const
Returns the cost of the segment between two nodes supposing all vehicle costs are the same (returns t...
Definition: routing.h:1211
operations_research::RoutingModelInspector::VisitIntegerExpressionArgument
void VisitIntegerExpressionArgument(const std::string &type_name, IntExpr *const expr) override
Definition: routing.cc:1954
gtl::FindOrNull
const Collection::value_type::second_type * FindOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:41
operations_research::CapAdd
int64 CapAdd(int64 x, int64 y)
Definition: saturated_arithmetic.h:124
operations_research::RoutingModel::kNoDimension
static const DimensionIndex kNoDimension
Constant used to express the "no dimension" index, returned when a dimension name does not correspond...
Definition: routing.h:390
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:3922
operations_research::DimensionSchedulingStatus::OPTIMAL
@ OPTIMAL
operations_research::TypeIncompatibilityChecker::TypeIncompatibilityChecker
TypeIncompatibilityChecker(const RoutingModel &model, bool check_hard_incompatibilities)
Definition: routing.cc:6271
thorough_hash.h
operations_research::PiecewiseLinearFunction
Definition: piecewise_linear_function.h:101
operations_research::RoutingModel::SetFixedCostOfAllVehicles
void SetFixedCostOfAllVehicles(int64 cost)
Sets the fixed cost of all vehicle routes.
Definition: routing.cc:1305
operations_research::RoutingIndexManager::GetIndexToNodeMap
std::vector< NodeIndex > GetIndexToNodeMap() const
Definition: routing_index_manager.h:100
operations_research::RoutingIndexManager::num_vehicles
int num_vehicles() const
Definition: routing_index_manager.h:69
operations_research::CostValue
int64 CostValue
Definition: ebert_graph.h:203
operations_research::RoutingModel::ArcIsMoreConstrainedThanArc
bool ArcIsMoreConstrainedThanArc(int64 from, int64 to1, int64 to2)
Returns whether the arc from->to1 is more constrained than from->to2, taking into account,...
Definition: routing.cc:3955
operations_research::RoutingModel::TransitCallback
const TransitCallback2 & TransitCallback(int callback_index) const
Definition: routing.h:406
operations_research::RoutingModel::UnaryTransitCallbackOrNull
const TransitCallback1 & UnaryTransitCallbackOrNull(int callback_index) const
Definition: routing.h:410
operations_research::RoutingModel::CostVar
IntVar * CostVar() const
Returns the global cost variable which is being minimized.
Definition: routing.h:1199
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:3785
operations_research::RoutingDimension::InitializeBreaks
void InitializeBreaks()
Sets up vehicle_break_intervals_, vehicle_break_distance_duration_, pre_travel_evaluators and post_tr...
Definition: routing.cc:6850
operations_research::SweepBuilder::~SweepBuilder
~SweepBuilder() override
Definition: routing.cc:3014
operations_research::RoutingModel::ComputeLowerBound
int64 ComputeLowerBound()
Computes a lower bound to the routing problem solving a linear assignment problem.
Definition: routing.cc:3342
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:3620
operations_research::RoutingModel::CostClassIndex
RoutingCostClassIndex CostClassIndex
Definition: routing.h:236
operations_research::TypeRegulationsChecker::TypeRegulationsChecker
TypeRegulationsChecker(const RoutingModel &model)
Definition: routing.cc:6186
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:4211
mathutil.h
operations_research::RoutingModel::GetPairIndicesOfType
const std::vector< int > & GetPairIndicesOfType(int type) const
Definition: routing.cc:4066
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::RoutingModel::VehicleClass::dimension_end_cumuls_max
gtl::ITIVector< DimensionIndex, int64 > dimension_end_cumuls_max
Definition: routing.h:340
operations_research::RoutingModel::UnperformedPenalty
int64 UnperformedPenalty(int64 var_index) const
Get the "unperformed" penalty of a node.
Definition: routing.cc:4207
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::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::MakeConstraintDemon1
Demon * MakeConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
Definition: constraint_solveri.h:574
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::TypeRegulationsChecker::model_
const RoutingModel & model_
Definition: routing.h:2132
operations_research::LocalDimensionCumulOptimizer
Definition: routing_lp_scheduling.h:634
operations_research::LinearSumAssignment::GetCost
CostValue GetCost() const
Definition: linear_assignment.h:1473
operations_research::RoutingModel::VehicleClass::start_equivalence_class
int start_equivalence_class
Vehicle start and end equivalence classes.
Definition: routing.h:333
operations_research::RouteConstructor::Construct
void Construct()
Definition: routing.cc:2506
operations_research::MakeVehicleVarFilter
IntVarLocalSearchFilter * MakeVehicleVarFilter(const RoutingModel &routing_model)
Definition: routing_search.cc:2442
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::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::RoutingModel::IndexPairs
RoutingIndexPairs IndexPairs
Definition: routing.h:246
operations_research::RoutingDimension::model
RoutingModel * model() const
Returns the model on which the dimension was created.
Definition: routing.h:2292
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::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:4224
util::ReverseArcListGraph
Definition: graph.h:460
operations_research::RoutingModel::HasSameVehicleTypeRequirements
bool HasSameVehicleTypeRequirements() const
Returns true iff any same-route (resp.
Definition: routing.h:843
start_max
Rev< int64 > start_max
Definition: sched_constraints.cc:242
operations_research::RoutingModel::NextVar
IntVar * NextVar(int64 index) const
!defined(SWIGPYTHON)
Definition: routing.h:1182
operations_research::TypeRegulationsChecker::FinalizeCheck
virtual bool FinalizeCheck() const
Definition: routing.h:2130
operations_research::sat::Value
std::function< int64(const Model &)> Value(IntegerVariable v)
Definition: integer.h:1406
DEFINE_int64
DEFINE_int64(sweep_sectors, 1, "The number of sectors the space is divided before it is sweeped " "by the ray.")
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:3598
operations_research::RoutingModel::VisitTypePolicy
VisitTypePolicy
Set the node visit types and incompatibilities/requirements between the types (see below).
Definition: routing.h:752
operations_research::RoutingModelInspector::EndVisitModel
void EndVisitModel(const std::string &solver_name) override
Definition: routing.cc:1927
operations_research::ArcIndex
int32 ArcIndex
Definition: ebert_graph.h:201
operations_research::RoutingModel::ROUTING_SUCCESS
@ ROUTING_SUCCESS
Problem solved successfully after calling RoutingModel::Solve().
Definition: routing.h:218
operations_research::RoutingModel::SetVisitType
void SetVisitType(int64 index, int type, VisitTypePolicy type_policy)
Definition: routing.cc:4048
operations_research::SweepBuilder::SweepBuilder
SweepBuilder(RoutingModel *const model, bool check_assignment)
Definition: routing.cc:3012
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::RoutingModelVisitor::kLightElement
static const char kLightElement[]
Constraint types.
Definition: routing.h:1866
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::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:6555
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::RoutingModel::IsMatchingModel
bool IsMatchingModel() const
Returns true if a vehicle/node matching problem is detected.
Definition: routing_flow.cc:34
operations_research::RoutingDimension::GetBreakIntervalsOfVehicle
const std::vector< IntervalVar * > & GetBreakIntervalsOfVehicle(int vehicle) const
Returns the break intervals set by SetBreakIntervalsOfVehicle().
Definition: routing.cc:6864
operations_research::RoutingDimension::HasPickupToDeliveryLimits
bool HasPickupToDeliveryLimits() const
Definition: routing.cc:6915
operations_research::RoutingModel::RegisterTransitCallback
int RegisterTransitCallback(TransitCallback2 callback)
Definition: routing.cc:884
operations_research::RoutingModel::RegisterPositiveUnaryTransitCallback
int RegisterPositiveUnaryTransitCallback(TransitCallback1 callback)
Definition: routing.cc:876
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
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:6821
operations_research::TypeRegulationsChecker::OnInitializeCheck
virtual void OnInitializeCheck()
Definition: routing.h:2126
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::VehicleClass::end_equivalence_class
int end_equivalence_class
Definition: routing.h:334
gtl::ITIVector::size
size_type size() const
Definition: int_type_indexed_vector.h:146
callback
MPCallback * callback
Definition: gurobi_interface.cc:440
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::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::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::VehicleTypeContainer::type_index_of_vehicle
std::vector< int > type_index_of_vehicle
Definition: routing.h:374
routing_neighborhoods.h
operations_research::RoutingDimension::SetSpanCostCoefficientForAllVehicles
void SetSpanCostCoefficientForAllVehicles(int64 coefficient)
Definition: routing.cc:6563
model
GRBmodel * model
Definition: gurobi_interface.cc:195
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:6639
operations_research::TypeRegulationsChecker::HasRegulationsToCheck
virtual bool HasRegulationsToCheck() const =0
operations_research::RoutingModel::HasTemporalTypeIncompatibilities
bool HasTemporalTypeIncompatibilities() const
Definition: routing.h:801
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:4201
operations_research::MakeVehicleBreaksFilter
IntVarLocalSearchFilter * MakeVehicleBreaksFilter(const RoutingModel &routing_model, const RoutingDimension &dimension)
Definition: routing_breaks.cc:1060
operations_research::SweepIndex::SweepIndex
SweepIndex(const int64 index, const double angle, const double distance)
Definition: routing.cc:2939
routing_parameters.h
operations_research::SweepIndexSortAngle
Definition: routing.cc:2948
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:3604
operations_research::RoutingModel::VehicleClass::dimension_end_cumuls_min
gtl::ITIVector< DimensionIndex, int64 > dimension_end_cumuls_min
Definition: routing.h:339
coefficient
int64 coefficient
Definition: routing_search.cc:973
operations_research::RoutingModelInspector::VisitIntegerArrayArgument
void VisitIntegerArrayArgument(const std::string &arg_name, const std::vector< int64 > &values) override
Definition: routing.cc:1959
gtl::InsertIfNotPresent
bool InsertIfNotPresent(Collection *const collection, const typename Collection::value_type &value)
Definition: map_util.h:103
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
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::SetSpanUpperBoundForVehicle
void SetSpanUpperBoundForVehicle(int64 upper_bound, int vehicle)
!defined(SWIGCSHARP) && !defined(SWIGJAVA) !defined(SWIGPYTHON)
Definition: routing.cc:6547
ABSL_DIE_IF_NULL
#define ABSL_DIE_IF_NULL
Definition: base/logging.h:28
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:4297
operations_research::RoutingModel::AddVariableMinimizedByFinalizer
void AddVariableMinimizedByFinalizer(IntVar *var)
Adds a variable to minimize in the solution finalizer.
Definition: routing.cc:5541
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:6648
operations_research::RoutingIndexManager::num_unique_depots
int num_unique_depots() const
complete.
Definition: routing_index_manager.h:99
CP_ROUTING_ADD_CALLBACK_OPERATOR
#define CP_ROUTING_ADD_CALLBACK_OPERATOR(operator_type, cp_operator_type)
Definition: routing.cc:4432
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:4077
operations_research::RoutingModel::GetCostClassIndexOfVehicle
CostClassIndex GetCostClassIndexOfVehicle(int64 vehicle) const
Get the cost class index of the given vehicle.
Definition: routing.h:1226
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:5517
operations_research::RoutingIndexManager::num_indices
int num_indices() const
Definition: routing_index_manager.h:71
operations_research::RoutingModel::GetPerfectBinaryDisjunctions
std::vector< std::pair< int64, int64 > > GetPerfectBinaryDisjunctions() const
Returns the list of all perfect binary disjunctions, as pairs of variable indices: a disjunction is "...
Definition: routing.cc:1612
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::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:3893
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:4163
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::SweepBuilder
Definition: routing.cc:3010
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::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::RoutingModel::Start
int64 Start(int vehicle) const
Model inspection.
Definition: routing.h:1155
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::RoutingModel::AddIntervalToAssignment
void AddIntervalToAssignment(IntervalVar *const interval)
Definition: routing.cc:5555
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:4120
optional_boolean.pb.h
operations_research::RoutingModelInspector
Definition: routing.cc:1909
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::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
hash.h
operations_research::RoutingModel::SolveFromAssignmentWithParameters
const Assignment * SolveFromAssignmentWithParameters(const Assignment *assignment, const RoutingSearchParameters &search_parameters, std::vector< const Assignment * > *solutions=nullptr)
Definition: routing.cc:3184
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:6661
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::ApplyLocks
IntVar * ApplyLocks(const std::vector< int64 > &locks)
Applies a lock chain to the next search.
Definition: routing.cc:3577
operations_research::RoutingDimension::GetPickupToDeliveryLimitForPair
int64 GetPickupToDeliveryLimitForPair(int pair_index, int pickup, int delivery) const
Definition: routing.cc:6919
b
int64 b
Definition: constraint_solver/table.cc:43
operations_research::SweepIndexSortDistance::operator()
bool operator()(const SweepIndex &node1, const SweepIndex &node2) const
Definition: routing.cc:2955
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::RoutingModel::ROUTING_INVALID
@ ROUTING_INVALID
Model, model parameters or flags are not valid.
Definition: routing.h:224
operations_research::IntVarFilteredDecisionBuilder::number_of_rejects
int64 number_of_rejects() const
Definition: routing_search.cc:2727
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
gtl::ITIVector::resize
void resize(size_type new_size)
Definition: int_type_indexed_vector.h:149
operations_research::IntVarFilteredDecisionBuilder::number_of_decisions
int64 number_of_decisions() const
Returns statistics from its underlying heuristic.
Definition: routing_search.cc:2723
operations_research::RevSwitch::Switched
bool Switched() const
Definition: constraint_solveri.h:401
operations_research::RoutingModelInspector::~RoutingModelInspector
~RoutingModelInspector() override
Definition: routing.cc:1926
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::solver
Solver * solver() const
Returns the underlying constraint solver.
Definition: routing.h:1299
util::BaseGraph< int32, int32, true >::AllNodes
IntegerRange< NodeIndex > AllNodes() const
Definition: graph.h:929
operations_research::RoutingDimension
Dimensions represent quantities accumulated at nodes along the routes.
Definition: routing.h:2288
operations_research::RoutingModel::Solve
const Assignment * Solve(const Assignment *assignment=nullptr)
Solves the current routing model; closes the current model.
Definition: routing.cc:3114
capacity
int64 capacity
Definition: routing_flow.cc:129
operations_research::TypeRegulationsChecker::CheckVehicle
bool CheckVehicle(int vehicle, const std::function< int64(int64)> &next_accessor)
Definition: routing.cc:6189
interval
IntervalVar * interval
Definition: resource.cc:98
operations_research::LocalSearchOperator
The base class for all local search operators.
Definition: constraint_solveri.h:806
topologicalsorter.h
next
Block * next
Definition: constraint_solver.cc:667
operations_research::kUnassigned
static const int kUnassigned
Definition: routing.cc:752
operations_research::SweepIndex::angle
double angle
Definition: routing.cc:2944
absl
Definition: cleanup.h:22
gtl::STLDeleteElements
void STLDeleteElements(T *container)
Definition: stl_util.h:372
operations_research::TypeRegulationsConstraint::Post
void Post() override
Definition: routing.cc:6411
operations_research::BOOL_TRUE
@ BOOL_TRUE
Definition: optional_boolean.pb.h:63
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:6592
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::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::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:4194
operations_research::TypeRegulationsChecker::TypePolicyOccurrence
Definition: routing.h:2093
operations_research::RoutingModel::GetTemporalTypeIncompatibilitiesOfType
const absl::flat_hash_set< int > & GetTemporalTypeIncompatibilitiesOfType(int type) const
Definition: routing.cc:4112
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:6568
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::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:4142
operations_research::ThoroughHash
uint64 ThoroughHash(const char *bytes, size_t len)
Definition: thorough_hash.h:33
lp_types.h
operations_research::RoutingModel::SetPickupAndDeliveryPolicyOfVehicle
void SetPickupAndDeliveryPolicyOfVehicle(PickupAndDeliveryPolicy policy, int vehicle)
Definition: routing.cc:1740
head
int64 head
Definition: routing_flow.cc:128
linear_assignment.h
operations_research::MakeConstraintDemon0
Demon * MakeConstraintDemon0(Solver *const s, T *const ct, void(T::*method)(), const std::string &name)
Definition: constraint_solveri.h:533
operations_research::RoutingModel::SetTabuVarsCallback
void SetTabuVarsCallback(GetTabuVarsCallback tabu_var_callback)
Definition: routing.cc:5452
operations_research::FirstSolutionStrategy_Value_Value_ARRAYSIZE
constexpr int FirstSolutionStrategy_Value_Value_ARRAYSIZE
Definition: routing_enums.pb.h:95
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:6690
operations_research::RoutingModel::SetFixedCostOfVehicle
void SetFixedCostOfVehicle(int64 cost, int vehicle)
Sets the fixed cost of one vehicle route.
Definition: routing.cc:1316
operations_research::RoutingModel::RoutingModelInspector
friend class RoutingModelInspector
Definition: routing.h:1857
gtl::FindCopy
bool FindCopy(const Collection &collection, const Key &key, Value *const value)
Definition: map_util.h:155
operations_research::SimpleBoundCosts::BoundCost::cost
int64 cost
Definition: routing.h:2253
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:6704
CP_ROUTING_PUSH_OPERATOR
#define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators)
Definition: routing.cc:4607
operations_research::RoutingModel::DimensionIndex
RoutingDimensionIndex DimensionIndex
Definition: routing.h:237
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:3638
operations_research::RoutingModel::VehicleClass::dimension_start_cumuls_max
gtl::ITIVector< DimensionIndex, int64 > dimension_start_cumuls_max
Definition: routing.h:338
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:6524
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::RoutingModel::StateDependentTransit
What follows is relevant for models with time/state dependent transits.
Definition: routing.h:262
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::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::RoutingModel::RegisterStateDependentTransitCallback
int RegisterStateDependentTransitCallback(VariableIndexEvaluator2 callback)
Definition: routing.cc:917
commandlineflags.h
operations_research::RoutingModelVisitor::kLightElement2
static const char kLightElement2[]
Definition: routing.h:1867
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::SweepArranger::SetSectors
void SetSectors(int sectors)
Definition: routing.h:2794
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::LinearSumAssignment::ComputeAssignment
bool ComputeAssignment()
Definition: linear_assignment.h:1448
operations_research::MakeNodeDisjunctionFilter
IntVarLocalSearchFilter * MakeNodeDisjunctionFilter(const RoutingModel &routing_model)
Definition: routing_search.cc:280
operations_research::RoutingModel::kNoPenalty
static const int64 kNoPenalty
Constant used to express a hard constraint instead of a soft penalty.
Definition: routing.h:382
name
const std::string name
Definition: default_search.cc:807
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:3901
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::nodes
int nodes() const
Sizes and indices Returns the number of nodes in the model.
Definition: routing.h:1315
operations_research::RoutingModel::HasTemporalTypeRequirements
bool HasTemporalTypeRequirements() const
Definition: routing.h:846
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:3304
bitset.h
operations_research::RoutingDimension::HasCumulVarSoftLowerBound
bool HasCumulVarSoftLowerBound(int64 index) const
Returns true if a soft lower bound has been set for a given variable index.
Definition: routing.cc:6699
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:732
operations_research::TypeRegulationsChecker::CheckTypeRegulations
virtual bool CheckTypeRegulations(int type, VisitTypePolicy policy, int pos)=0
operations_research::SortedDisjointIntervalList::FirstIntervalGreaterOrEqual
Iterator FirstIntervalGreaterOrEqual(int64 value) const
Returns an iterator to either:
Definition: sorted_interval_list.cc:716
kint64max
static const int64 kint64max
Definition: integral_types.h:62
operations_research::RoutingModel::GetNumOfSingletonNodes
int GetNumOfSingletonNodes() const
Returns the number of non-start/end nodes which do not appear in a pickup/delivery pair.
Definition: routing.cc:1760
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:3491
operations_research::RoutingModel::GetMutableLocalCumulOptimizer
LocalDimensionCumulOptimizer * GetMutableLocalCumulOptimizer(const RoutingDimension &dimension) const
Definition: routing.cc:1243
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:5537
gtl::ContainsKey
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:170
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::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::RoutingDimension::GetPostTravelEvaluatorOfVehicle
int GetPostTravelEvaluatorOfVehicle(int vehicle) const
Definition: routing.cc:6877