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