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