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