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