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