21 #include <initializer_list>
28 #include "absl/base/casts.h"
29 #include "absl/container/flat_hash_map.h"
30 #include "absl/container/flat_hash_set.h"
31 #include "absl/memory/memory.h"
32 #include "absl/strings/str_cat.h"
33 #include "absl/strings/str_format.h"
34 #include "absl/time/time.h"
35 #include "google/protobuf/duration.pb.h"
36 #include "google/protobuf/text_format.h"
63 class LocalSearchPhaseParameters;
67 "The number of sectors the space is divided before it is sweeped "
81 class SetValuesFromTargets :
public DecisionBuilder {
83 SetValuesFromTargets(std::vector<IntVar*> variables,
84 std::vector<int64> targets)
85 : variables_(std::move(variables)),
86 targets_(std::move(targets)),
88 steps_(variables_.size(), 0) {
89 DCHECK_EQ(variables_.size(), targets_.size());
91 Decision* Next(Solver*
const solver)
override {
92 int index = index_.Value();
93 while (
index < variables_.size() && variables_[
index]->Bound()) {
96 index_.SetValue(solver,
index);
97 if (
index >= variables_.size())
return nullptr;
98 const int64 variable_min = variables_[
index]->Min();
99 const int64 variable_max = variables_[
index]->Max();
102 if (targets_[
index] <= variable_min) {
103 return solver->MakeAssignVariableValue(variables_[
index], variable_min);
104 }
else if (targets_[
index] >= variable_max) {
105 return solver->MakeAssignVariableValue(variables_[
index], variable_max);
112 if (
value < variable_min || variable_max <
value) {
113 step = GetNextStep(step);
124 steps_.SetValue(solver,
index, GetNextStep(step));
125 return solver->MakeAssignVariableValueOrDoNothing(variables_[
index],
132 return (step > 0) ? -step :
CapSub(1, step);
134 const std::vector<IntVar*> variables_;
135 const std::vector<int64> targets_;
137 RevArray<int64> steps_;
143 std::vector<IntVar*> variables,
144 std::vector<int64> targets) {
146 new SetValuesFromTargets(std::move(variables), std::move(targets)));
151 bool DimensionFixedTransitsEqualTransitEvaluatorForVehicle(
152 const RoutingDimension& dimension,
int vehicle) {
153 const RoutingModel*
const model = dimension.model();
154 int node =
model->Start(vehicle);
155 while (!
model->IsEnd(node)) {
156 if (!
model->NextVar(node)->Bound()) {
159 const int next =
model->NextVar(node)->Value();
160 if (dimension.transit_evaluator(vehicle)(node,
next) !=
161 dimension.FixedTransitVar(node)->Value()) {
169 bool DimensionFixedTransitsEqualTransitEvaluators(
170 const RoutingDimension& dimension) {
171 for (
int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
172 if (!DimensionFixedTransitsEqualTransitEvaluatorForVehicle(dimension,
180 class SetCumulsFromLocalDimensionCosts :
public DecisionBuilder {
182 SetCumulsFromLocalDimensionCosts(
183 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>*
185 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>*
187 SearchMonitor* monitor,
bool optimize_and_pack =
false)
188 : local_optimizers_(*local_optimizers),
189 local_mp_optimizers_(*local_mp_optimizers),
191 optimize_and_pack_(optimize_and_pack) {}
192 Decision* Next(Solver*
const solver)
override {
196 bool should_fail =
false;
197 for (
int i = 0; i < local_optimizers_.size(); ++i) {
198 const auto& local_optimizer = local_optimizers_[i];
199 const RoutingDimension*
const dimension = local_optimizer->dimension();
200 RoutingModel*
const model = dimension->model();
202 const auto compute_cumul_values =
203 [
this, &
next](LocalDimensionCumulOptimizer* optimizer,
int vehicle,
204 std::vector<int64>* cumul_values,
205 std::vector<int64>* break_start_end_values) {
206 if (optimize_and_pack_) {
207 return optimizer->ComputePackedRouteCumuls(
208 vehicle,
next, cumul_values, break_start_end_values);
210 return optimizer->ComputeRouteCumuls(vehicle,
next, cumul_values,
211 break_start_end_values);
214 for (
int vehicle = 0; vehicle <
model->vehicles(); ++vehicle) {
216 DCHECK(DimensionFixedTransitsEqualTransitEvaluatorForVehicle(*dimension,
218 const bool vehicle_has_break_constraint =
219 dimension->HasBreakConstraints() &&
220 !dimension->GetBreakIntervalsOfVehicle(vehicle).empty();
221 LocalDimensionCumulOptimizer*
const optimizer =
222 vehicle_has_break_constraint ? local_mp_optimizers_[i].get()
223 : local_optimizer.get();
224 DCHECK(optimizer !=
nullptr);
225 std::vector<int64> cumul_values;
226 std::vector<int64> break_start_end_values;
228 optimizer, vehicle, &cumul_values, &break_start_end_values);
234 if (status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY) {
235 cumul_values.clear();
236 break_start_end_values.clear();
237 DCHECK(local_mp_optimizers_[i] !=
nullptr);
238 if (compute_cumul_values(local_mp_optimizers_[i].get(), vehicle,
239 &cumul_values, &break_start_end_values) ==
249 std::vector<IntVar*> cp_variables;
250 std::vector<int64> cp_values;
251 std::swap(cp_values, cumul_values);
253 int current =
model->Start(vehicle);
255 cp_variables.push_back(dimension->CumulVar(current));
256 if (!
model->IsEnd(current)) {
257 current =
model->NextVar(current)->Value();
268 std::swap(cp_variables[1], cp_variables.back());
269 std::swap(cp_values[1], cp_values.back());
270 if (dimension->HasBreakConstraints()) {
272 dimension->GetBreakIntervalsOfVehicle(vehicle)) {
273 cp_variables.push_back(
interval->SafeStartExpr(0)->Var());
274 cp_variables.push_back(
interval->SafeEndExpr(0)->Var());
276 cp_values.insert(cp_values.end(), break_start_end_values.begin(),
277 break_start_end_values.end());
280 for (
int i = 0; i < cp_values.size(); ++i) {
282 cp_values[i] = cp_variables[i]->Min();
285 if (!solver->SolveAndCommit(
287 std::move(cp_values)),
301 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>&
303 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>&
304 local_mp_optimizers_;
305 SearchMonitor*
const monitor_;
306 const bool optimize_and_pack_;
309 class SetCumulsFromGlobalDimensionCosts :
public DecisionBuilder {
311 SetCumulsFromGlobalDimensionCosts(
312 const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>*
314 SearchMonitor* monitor,
bool optimize_and_pack =
false)
315 : global_optimizers_(*global_optimizers),
317 optimize_and_pack_(optimize_and_pack) {}
318 Decision* Next(Solver*
const solver)
override {
322 bool should_fail =
false;
323 for (
const auto& global_optimizer : global_optimizers_) {
324 const RoutingDimension* dimension = global_optimizer->dimension();
325 RoutingModel*
const model = dimension->model();
329 DCHECK(DimensionFixedTransitsEqualTransitEvaluators(*dimension));
331 std::vector<int64> cumul_values;
332 std::vector<int64> break_start_end_values;
333 const bool cumuls_optimized =
335 ? global_optimizer->ComputePackedCumuls(
next, &cumul_values,
336 &break_start_end_values)
337 : global_optimizer->ComputeCumuls(
next, &cumul_values,
338 &break_start_end_values);
339 if (!cumuls_optimized) {
345 std::vector<IntVar*> cp_variables = dimension->cumuls();
346 std::vector<int64> cp_values;
347 std::swap(cp_values, cumul_values);
348 if (dimension->HasBreakConstraints()) {
349 const int num_vehicles =
model->vehicles();
350 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
352 dimension->GetBreakIntervalsOfVehicle(vehicle)) {
353 cp_variables.push_back(
interval->SafeStartExpr(0)->Var());
354 cp_variables.push_back(
interval->SafeEndExpr(0)->Var());
357 cp_values.insert(cp_values.end(), break_start_end_values.begin(),
358 break_start_end_values.end());
361 for (
int i = 0; i < cp_values.size(); ++i) {
363 cp_values[i] = cp_variables[i]->Min();
366 if (!solver->SolveAndCommit(
368 std::move(cp_values)),
381 const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>&
383 SearchMonitor*
const monitor_;
384 const bool optimize_and_pack_;
390 const Assignment* original_assignment, absl::Duration duration_limit) {
392 if (original_assignment ==
nullptr)
return nullptr;
393 if (duration_limit <= absl::ZeroDuration())
return original_assignment;
394 if (global_dimension_optimizers_.empty() &&
395 local_dimension_optimizers_.empty()) {
396 DCHECK(local_dimension_mp_optimizers_.empty());
397 return original_assignment;
404 Assignment* packed_assignment = solver_->MakeAssignment();
408 std::vector<DecisionBuilder*> decision_builders;
409 decision_builders.push_back(solver_->MakeRestoreAssignment(preassignment_));
410 decision_builders.push_back(
411 solver_->MakeRestoreAssignment(packed_assignment));
412 decision_builders.push_back(
413 solver_->RevAlloc(
new SetCumulsFromLocalDimensionCosts(
414 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
415 GetOrCreateLargeNeighborhoodSearchLimit(),
417 decision_builders.push_back(
418 solver_->RevAlloc(
new SetCumulsFromGlobalDimensionCosts(
419 &global_dimension_optimizers_,
420 GetOrCreateLargeNeighborhoodSearchLimit(),
422 decision_builders.push_back(
423 CreateFinalizerForMinimizedAndMaximizedVariables());
426 solver_->Compose(decision_builders);
427 solver_->Solve(restore_pack_and_finalize,
428 packed_dimensions_assignment_collector_, limit);
430 if (packed_dimensions_assignment_collector_->
solution_count() != 1) {
431 LOG(
ERROR) <<
"The given assignment is not valid for this model, or cannot "
436 packed_assignment->
Copy(original_assignment);
438 packed_dimensions_assignment_collector_->
solution(0));
440 return packed_assignment;
445 class DifferentFromValues :
public Constraint {
447 DifferentFromValues(
Solver* solver,
IntVar*
var, std::vector<int64> values)
448 :
Constraint(solver), var_(
var), values_(std::move(values)) {}
449 void Post()
override {}
450 void InitialPropagate()
override { var_->RemoveValues(values_); }
451 std::string DebugString()
const override {
return "DifferentFromValues"; }
452 void Accept(ModelVisitor*
const visitor)
const override {
462 const std::vector<int64> values_;
476 template <
typename F>
477 class LightFunctionElementConstraint :
public Constraint {
479 LightFunctionElementConstraint(Solver*
const solver, IntVar*
const var,
480 IntVar*
const index, F values,
481 std::function<
bool()> deep_serialize)
482 : Constraint(solver),
485 values_(std::move(values)),
486 deep_serialize_(std::move(deep_serialize)) {}
487 ~LightFunctionElementConstraint()
override {}
489 void Post()
override {
491 solver(),
this, &LightFunctionElementConstraint::IndexBound,
493 index_->WhenBound(demon);
496 void InitialPropagate()
override {
497 if (index_->Bound()) {
502 std::string DebugString()
const override {
503 return "LightFunctionElementConstraint";
506 void Accept(ModelVisitor*
const visitor)
const override {
513 if (deep_serialize_()) {
514 visitor->VisitInt64ToInt64Extension(values_, index_->Min(),
521 void IndexBound() { var_->SetValue(values_(index_->Min())); }
524 IntVar*
const index_;
526 std::function<bool()> deep_serialize_;
529 template <
typename F>
530 Constraint* MakeLightElement(Solver*
const solver, IntVar*
const var,
531 IntVar*
const index, F values,
532 std::function<
bool()> deep_serialize) {
533 return solver->RevAlloc(
new LightFunctionElementConstraint<F>(
534 solver,
var,
index, std::move(values), std::move(deep_serialize)));
542 template <
typename F>
543 class LightFunctionElement2Constraint :
public Constraint {
545 LightFunctionElement2Constraint(Solver*
const solver, IntVar*
const var,
546 IntVar*
const index1, IntVar*
const index2,
548 std::function<
bool()> deep_serialize)
549 : Constraint(solver),
553 values_(std::move(values)),
554 deep_serialize_(std::move(deep_serialize)) {}
555 ~LightFunctionElement2Constraint()
override {}
556 void Post()
override {
558 solver(),
this, &LightFunctionElement2Constraint::IndexBound,
560 index1_->WhenBound(demon);
561 index2_->WhenBound(demon);
563 void InitialPropagate()
override { IndexBound(); }
565 std::string DebugString()
const override {
566 return "LightFunctionElement2Constraint";
569 void Accept(ModelVisitor*
const visitor)
const override {
578 const int64 index1_min = index1_->Min();
579 const int64 index1_max = index1_->Max();
582 if (deep_serialize_()) {
583 for (
int i = index1_min; i <= index1_max; ++i) {
584 visitor->VisitInt64ToInt64Extension(
585 [
this, i](
int64 j) {
return values_(i, j); }, index2_->Min(),
594 if (index1_->Bound() && index2_->Bound()) {
595 var_->SetValue(values_(index1_->Min(), index2_->Min()));
600 IntVar*
const index1_;
601 IntVar*
const index2_;
603 std::function<bool()> deep_serialize_;
606 template <
typename F>
607 Constraint* MakeLightElement2(Solver*
const solver, IntVar*
const var,
608 IntVar*
const index1, IntVar*
const index2,
609 F values, std::function<
bool()> deep_serialize) {
610 return solver->RevAlloc(
new LightFunctionElement2Constraint<F>(
611 solver,
var, index1, index2, std::move(values),
612 std::move(deep_serialize)));
616 template <
class A,
class B>
617 static int64 ReturnZero(A
a, B
b) {
623 for (
int i = 0; i < size1; i++) {
624 for (
int j = 0; j < size2; j++) {
649 : nodes_(index_manager.num_nodes()),
650 vehicles_(index_manager.num_vehicles()),
651 max_active_vehicles_(vehicles_),
652 fixed_cost_of_vehicle_(vehicles_, 0),
654 linear_cost_factor_of_vehicle_(vehicles_, 0),
655 quadratic_cost_factor_of_vehicle_(vehicles_, 0),
656 vehicle_amortized_cost_factors_set_(false),
657 consider_empty_route_costs_(vehicles_, false),
659 costs_are_homogeneous_across_vehicles_(
661 cache_callbacks_(false),
663 vehicle_pickup_delivery_policy_(vehicles_, PICKUP_AND_DELIVERY_NO_ORDER),
664 has_hard_type_incompatibilities_(false),
665 has_temporal_type_incompatibilities_(false),
666 has_same_vehicle_type_requirements_(false),
667 has_temporal_type_requirements_(false),
671 manager_(index_manager) {
673 vehicle_to_transit_cost_.assign(
677 cache_callbacks_ = (nodes_ <=
parameters.max_callback_cache_size());
680 ConstraintSolverParameters solver_parameters =
683 solver_ = absl::make_unique<Solver>(
"Routing", solver_parameters);
689 index_to_pickup_index_pairs_.resize(size);
690 index_to_delivery_index_pairs_.resize(size);
692 index_to_type_policy_.resize(index_manager.
num_indices());
695 for (
int v = 0; v < index_manager.
num_vehicles(); ++v) {
697 index_to_vehicle_[starts_[v]] = v;
699 index_to_vehicle_[ends_[v]] = v;
702 const std::vector<RoutingIndexManager::NodeIndex>& index_to_node =
704 index_to_equivalence_class_.resize(index_manager.
num_indices());
705 for (
int i = 0; i < index_to_node.size(); ++i) {
706 index_to_equivalence_class_[i] = index_to_node[i].value();
708 allowed_vehicles_.resize(
Size() + vehicles_);
711 void RoutingModel::Initialize() {
712 const int size =
Size();
714 solver_->MakeIntVarArray(size, 0, size + vehicles_ - 1,
"Nexts", &nexts_);
715 solver_->AddConstraint(solver_->MakeAllDifferent(nexts_,
false));
716 index_to_disjunctions_.resize(size + vehicles_);
719 solver_->MakeIntVarArray(size + vehicles_, -1, vehicles_ - 1,
"Vehicles",
722 solver_->MakeBoolVarArray(size,
"Active", &active_);
724 solver_->MakeBoolVarArray(vehicles_,
"ActiveVehicle", &vehicle_active_);
726 solver_->MakeBoolVarArray(vehicles_,
"VehicleCostsConsidered",
727 &vehicle_costs_considered_);
729 solver_->MakeBoolVarArray(size + vehicles_,
"IsBoundToEnd",
734 preassignment_ = solver_->MakeAssignment();
741 absl::flat_hash_set<RangeIntToIntFunction*> value_functions_delete;
742 absl::flat_hash_set<RangeMinMaxIndexFunction*> index_functions_delete;
743 for (
const auto& cache_line : state_dependent_transit_evaluators_cache_) {
744 for (
const auto& key_transit : *cache_line) {
745 value_functions_delete.insert(key_transit.second.transit);
746 index_functions_delete.insert(key_transit.second.transit_plus_identity);
754 const int index = unary_transit_evaluators_.size();
755 unary_transit_evaluators_.push_back(std::move(
callback));
757 return unary_transit_evaluators_[
index](i);
763 is_transit_evaluator_positive_.push_back(
true);
764 DCHECK(TransitCallbackPositive(
770 if (cache_callbacks_) {
772 std::vector<int64> cache(size * size, 0);
773 for (
int i = 0; i < size; ++i) {
774 for (
int j = 0; j < size; ++j) {
775 cache[i * size + j] =
callback(i, j);
778 transit_evaluators_.push_back(
779 [cache, size](
int64 i,
int64 j) {
return cache[i * size + j]; });
781 transit_evaluators_.push_back(std::move(
callback));
783 if (transit_evaluators_.size() != unary_transit_evaluators_.size()) {
784 DCHECK_EQ(transit_evaluators_.size(), unary_transit_evaluators_.size() + 1);
785 unary_transit_evaluators_.push_back(
nullptr);
787 if (transit_evaluators_.size() != is_transit_evaluator_positive_.size()) {
789 is_transit_evaluator_positive_.size() + 1);
790 is_transit_evaluator_positive_.push_back(
false);
792 return transit_evaluators_.size() - 1;
796 is_transit_evaluator_positive_.push_back(
true);
804 state_dependent_transit_evaluators_cache_.push_back(
805 absl::make_unique<StateDependentTransitCallbackCache>());
806 StateDependentTransitCallbackCache*
const cache =
807 state_dependent_transit_evaluators_cache_.back().get();
808 state_dependent_transit_evaluators_.push_back(
813 cache->insert({CacheKey(i, j),
value});
816 return state_dependent_transit_evaluators_.size() - 1;
819 void RoutingModel::AddNoCycleConstraintInternal() {
820 if (no_cycle_constraint_ ==
nullptr) {
821 no_cycle_constraint_ = solver_->MakeNoCycle(nexts_, active_);
822 solver_->AddConstraint(no_cycle_constraint_);
828 const std::string&
name) {
829 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
830 std::vector<int64> capacities(vehicles_,
capacity);
831 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
832 std::move(capacities),
833 fix_start_cumul_to_zero,
name);
838 bool fix_start_cumul_to_zero,
const std::string&
name) {
839 std::vector<int64> capacities(vehicles_,
capacity);
840 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
841 std::move(capacities),
842 fix_start_cumul_to_zero,
name);
846 int evaluator_index,
int64 slack_max, std::vector<int64> vehicle_capacities,
847 bool fix_start_cumul_to_zero,
const std::string&
name) {
848 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
849 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
850 std::move(vehicle_capacities),
851 fix_start_cumul_to_zero,
name);
855 const std::vector<int>& evaluator_indices,
int64 slack_max,
856 std::vector<int64> vehicle_capacities,
bool fix_start_cumul_to_zero,
857 const std::string&
name) {
858 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
859 std::move(vehicle_capacities),
860 fix_start_cumul_to_zero,
name);
863 bool RoutingModel::AddDimensionWithCapacityInternal(
864 const std::vector<int>& evaluator_indices,
int64 slack_max,
865 std::vector<int64> vehicle_capacities,
bool fix_start_cumul_to_zero,
866 const std::string&
name) {
867 CHECK_EQ(vehicles_, vehicle_capacities.size());
868 return InitializeDimensionInternal(
869 evaluator_indices, std::vector<int>(), slack_max, fix_start_cumul_to_zero,
873 bool RoutingModel::InitializeDimensionInternal(
874 const std::vector<int>& evaluator_indices,
875 const std::vector<int>& state_dependent_evaluator_indices,
int64 slack_max,
876 bool fix_start_cumul_to_zero, RoutingDimension* dimension) {
877 CHECK(dimension !=
nullptr);
878 CHECK_EQ(vehicles_, evaluator_indices.size());
879 CHECK((dimension->base_dimension_ ==
nullptr &&
880 state_dependent_evaluator_indices.empty()) ||
881 vehicles_ == state_dependent_evaluator_indices.size());
884 dimension_name_to_index_[dimension->name()] = dimension_index;
885 dimensions_.push_back(dimension);
886 dimension->Initialize(evaluator_indices, state_dependent_evaluator_indices,
888 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
889 nexts_, active_, dimension->cumuls(), dimension->transits()));
890 if (fix_start_cumul_to_zero) {
891 for (
int i = 0; i < vehicles_; ++i) {
892 IntVar*
const start_cumul = dimension->CumulVar(
Start(i));
894 start_cumul->SetValue(0);
905 RoutingModel*
model) {
907 return model->RegisterPositiveTransitCallback(std::move(
callback));
913 RoutingModel*
model) {
915 return model->RegisterPositiveUnaryTransitCallback(std::move(
callback));
917 return model->RegisterUnaryTransitCallback(std::move(
callback));
923 const std::string& dimension_name) {
926 slack_max,
capacity, fix_start_cumul_to_zero,
931 bool fix_start_cumul_to_zero,
932 const std::string& dimension_name) {
934 RegisterUnaryCallback(
935 [
this, values](
int64 i) {
939 std::all_of(std::begin(values), std::end(values),
940 [](
int64 transit) {
return transit >= 0; }),
942 0,
capacity, fix_start_cumul_to_zero, dimension_name);
947 bool fix_start_cumul_to_zero,
948 const std::string& dimension_name) {
949 bool all_transits_positive =
true;
950 for (
const std::vector<int64>& transit_values : values) {
951 all_transits_positive =
952 std::all_of(std::begin(transit_values), std::end(transit_values),
953 [](
int64 transit) {
return transit >= 0; });
954 if (!all_transits_positive) {
963 all_transits_positive,
this),
964 0,
capacity, fix_start_cumul_to_zero, dimension_name);
976 CHECK(callback_ !=
nullptr);
980 int64 Min()
const override {
982 const int idx_min = index_->Min();
983 const int idx_max = index_->Max() + 1;
984 return (idx_min < idx_max) ? callback_->RangeMin(idx_min, idx_max)
987 void SetMin(
int64 new_min)
override {
988 const int64 old_min = Min();
989 const int64 old_max = Max();
990 if (old_min < new_min && new_min <= old_max) {
991 const int64 old_idx_min = index_->Min();
992 const int64 old_idx_max = index_->Max() + 1;
993 if (old_idx_min < old_idx_max) {
994 const int64 new_idx_min = callback_->RangeFirstInsideInterval(
995 old_idx_min, old_idx_max, new_min, old_max + 1);
996 index_->SetMin(new_idx_min);
997 if (new_idx_min < old_idx_max) {
998 const int64 new_idx_max = callback_->RangeLastInsideInterval(
999 new_idx_min, old_idx_max, new_min, old_max + 1);
1000 index_->SetMax(new_idx_max);
1005 int64 Max()
const override {
1007 const int idx_min = index_->Min();
1008 const int idx_max = index_->Max() + 1;
1009 return (idx_min < idx_max) ? callback_->RangeMax(idx_min, idx_max)
1012 void SetMax(
int64 new_max)
override {
1013 const int64 old_min = Min();
1014 const int64 old_max = Max();
1015 if (old_min <= new_max && new_max < old_max) {
1016 const int64 old_idx_min = index_->Min();
1017 const int64 old_idx_max = index_->Max() + 1;
1018 if (old_idx_min < old_idx_max) {
1019 const int64 new_idx_min = callback_->RangeFirstInsideInterval(
1020 old_idx_min, old_idx_max, old_min, new_max + 1);
1021 index_->SetMin(new_idx_min);
1022 if (new_idx_min < old_idx_max) {
1023 const int64 new_idx_max = callback_->RangeLastInsideInterval(
1024 new_idx_min, old_idx_max, old_min, new_max + 1);
1025 index_->SetMax(new_idx_max);
1030 void WhenRange(Demon* d)
override { index_->WhenRange(d); }
1033 const RangeIntToIntFunction*
const callback_;
1034 IntVar*
const index_;
1037 IntExpr* MakeRangeMakeElementExpr(
const RangeIntToIntFunction*
callback,
1038 IntVar*
index, Solver* s) {
1039 return s->RegisterIntExpr(
1045 const std::vector<int>& dependent_transits,
1047 std::vector<int64> vehicle_capacities,
bool fix_start_cumul_to_zero,
1048 const std::string&
name) {
1049 const std::vector<int> pure_transits(vehicles_, 0);
1051 pure_transits, dependent_transits, base_dimension, slack_max,
1052 std::move(vehicle_capacities), fix_start_cumul_to_zero,
name);
1057 int64 vehicle_capacity,
bool fix_start_cumul_to_zero,
1058 const std::string&
name) {
1060 0, transit, dimension, slack_max, vehicle_capacity,
1061 fix_start_cumul_to_zero,
name);
1064 bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacityInternal(
1065 const std::vector<int>& pure_transits,
1066 const std::vector<int>& dependent_transits,
1068 std::vector<int64> vehicle_capacities,
bool fix_start_cumul_to_zero,
1069 const std::string&
name) {
1070 CHECK_EQ(vehicles_, vehicle_capacities.size());
1072 if (base_dimension ==
nullptr) {
1074 name, RoutingDimension::SelfBased());
1077 name, base_dimension);
1079 return InitializeDimensionInternal(pure_transits, dependent_transits,
1080 slack_max, fix_start_cumul_to_zero,
1085 int pure_transit,
int dependent_transit,
1087 int64 vehicle_capacity,
bool fix_start_cumul_to_zero,
1088 const std::string&
name) {
1089 std::vector<int> pure_transits(vehicles_, pure_transit);
1090 std::vector<int> dependent_transits(vehicles_, dependent_transit);
1091 std::vector<int64> vehicle_capacities(vehicles_, vehicle_capacity);
1092 return AddDimensionDependentDimensionWithVehicleCapacityInternal(
1093 pure_transits, dependent_transits, base_dimension, slack_max,
1094 std::move(vehicle_capacities), fix_start_cumul_to_zero,
name);
1100 const std::function<
int64(
int64)> g = [&f](
int64 x) {
return f(x) + x; };
1108 std::vector<std::string> dimension_names;
1109 for (
const auto& dimension_name_index : dimension_name_to_index_) {
1110 dimension_names.push_back(dimension_name_index.first);
1112 std::sort(dimension_names.begin(), dimension_names.end());
1113 return dimension_names;
1119 if (dim_index < 0 || dim_index >= global_optimizer_index_.
size() ||
1120 global_optimizer_index_[dim_index] < 0) {
1123 const int optimizer_index = global_optimizer_index_[dim_index];
1124 DCHECK_LT(optimizer_index, global_dimension_optimizers_.size());
1125 return global_dimension_optimizers_[optimizer_index].get();
1131 if (dim_index < 0 || dim_index >= local_optimizer_index_.
size() ||
1132 local_optimizer_index_[dim_index] < 0) {
1135 const int optimizer_index = local_optimizer_index_[dim_index];
1136 DCHECK_LT(optimizer_index, local_dimension_optimizers_.size());
1137 return local_dimension_optimizers_[optimizer_index].get();
1143 if (dim_index < 0 || dim_index >= local_optimizer_index_.
size() ||
1144 local_optimizer_index_[dim_index] < 0) {
1147 const int optimizer_index = local_optimizer_index_[dim_index];
1148 DCHECK_LT(optimizer_index, local_dimension_mp_optimizers_.size());
1149 return local_dimension_mp_optimizers_[optimizer_index].get();
1157 const std::string& dimension_name)
const {
1163 const std::string& dimension_name)
const {
1164 return *dimensions_[
gtl::FindOrDie(dimension_name_to_index_, dimension_name)];
1168 const std::string& dimension_name)
const {
1171 return dimensions_[
index];
1178 for (
int i = 0; i < vehicles_; ++i) {
1186 CHECK_LT(evaluator_index, transit_evaluators_.size());
1187 vehicle_to_transit_cost_[vehicle] = evaluator_index;
1191 for (
int i = 0; i < vehicles_; ++i) {
1198 return fixed_cost_of_vehicle_[vehicle];
1204 fixed_cost_of_vehicle_[vehicle] =
cost;
1208 int64 linear_cost_factor,
int64 quadratic_cost_factor) {
1209 for (
int v = 0; v < vehicles_; v++) {
1216 int64 quadratic_cost_factor,
1221 if (linear_cost_factor + quadratic_cost_factor > 0) {
1222 vehicle_amortized_cost_factors_set_ =
true;
1224 linear_cost_factor_of_vehicle_[vehicle] = linear_cost_factor;
1225 quadratic_cost_factor_of_vehicle_[vehicle] = quadratic_cost_factor;
1231 struct CostClassComparator {
1238 struct VehicleClassComparator {
1239 bool operator()(
const RoutingModel::VehicleClass&
a,
1240 const RoutingModel::VehicleClass&
b)
const {
1250 void RoutingModel::ComputeCostClasses(
1253 cost_classes_.reserve(vehicles_);
1254 cost_classes_.clear();
1255 cost_class_index_of_vehicle_.assign(vehicles_,
CostClassIndex(-1));
1256 std::map<CostClass, CostClassIndex, CostClassComparator> cost_class_map;
1259 const CostClass zero_cost_class(0);
1260 cost_classes_.push_back(zero_cost_class);
1261 DCHECK_EQ(cost_classes_[kCostClassIndexOfZeroCost].evaluator_index, 0);
1262 cost_class_map[zero_cost_class] = kCostClassIndexOfZeroCost;
1267 has_vehicle_with_zero_cost_class_ =
false;
1268 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
1269 CostClass cost_class(vehicle_to_transit_cost_[vehicle]);
1273 const int64 coeff = dimension->vehicle_span_cost_coefficients()[vehicle];
1274 if (coeff == 0)
continue;
1275 cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1276 .push_back({dimension->vehicle_to_class(vehicle), coeff, dimension});
1278 std::sort(cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1280 cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1286 if (cost_class_index == kCostClassIndexOfZeroCost) {
1287 has_vehicle_with_zero_cost_class_ =
true;
1288 }
else if (cost_class_index == num_cost_classes) {
1289 cost_classes_.push_back(cost_class);
1291 cost_class_index_of_vehicle_[vehicle] = cost_class_index;
1305 costs_are_homogeneous_across_vehicles_ &= has_vehicle_with_zero_cost_class_
1312 return std::tie(
a.cost_class_index,
a.fixed_cost,
a.start_equivalence_class,
1313 a.end_equivalence_class,
a.unvisitable_nodes_fprint,
1314 a.dimension_start_cumuls_min,
a.dimension_start_cumuls_max,
1315 a.dimension_end_cumuls_min,
a.dimension_end_cumuls_max,
1316 a.dimension_capacities,
a.dimension_evaluator_classes) <
1317 std::tie(
b.cost_class_index,
b.fixed_cost,
b.start_equivalence_class,
1318 b.end_equivalence_class,
b.unvisitable_nodes_fprint,
1319 b.dimension_start_cumuls_min,
b.dimension_start_cumuls_max,
1320 b.dimension_end_cumuls_min,
b.dimension_end_cumuls_max,
1321 b.dimension_capacities,
b.dimension_evaluator_classes);
1324 void RoutingModel::ComputeVehicleClasses() {
1325 vehicle_classes_.reserve(vehicles_);
1326 vehicle_classes_.clear();
1328 std::map<VehicleClass, VehicleClassIndex, VehicleClassComparator>
1330 const int nodes_unvisitability_num_bytes = (vehicle_vars_.size() + 7) / 8;
1331 std::unique_ptr<char[]> nodes_unvisitability_bitmask(
1332 new char[nodes_unvisitability_num_bytes]);
1333 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
1336 vehicle_class.
fixed_cost = fixed_cost_of_vehicle_[vehicle];
1338 index_to_equivalence_class_[
Start(vehicle)];
1340 index_to_equivalence_class_[
End(vehicle)];
1342 IntVar*
const start_cumul_var = dimension->cumuls()[
Start(vehicle)];
1344 start_cumul_var->
Min());
1346 start_cumul_var->
Max());
1347 IntVar*
const end_cumul_var = dimension->cumuls()[
End(vehicle)];
1351 dimension->vehicle_capacities()[vehicle]);
1353 dimension->vehicle_to_class(vehicle));
1355 memset(nodes_unvisitability_bitmask.get(), 0,
1356 nodes_unvisitability_num_bytes);
1360 (!vehicle_var->
Contains(vehicle) ||
1362 nodes_unvisitability_bitmask[
index / CHAR_BIT] |= 1U
1363 << (
index % CHAR_BIT);
1367 nodes_unvisitability_bitmask.get(), nodes_unvisitability_num_bytes);
1370 &vehicle_class_map, vehicle_class, num_vehicle_classes);
1371 if (vehicle_class_index == num_vehicle_classes) {
1372 vehicle_classes_.push_back(vehicle_class);
1374 vehicle_class_index_of_vehicle_[vehicle] = vehicle_class_index;
1378 void RoutingModel::ComputeVehicleTypes() {
1379 const int nodes_squared = nodes_ * nodes_;
1380 std::vector<int>& type_index_of_vehicle =
1382 std::vector<std::set<VehicleTypeContainer::VehicleClassEntry>>&
1383 sorted_vehicle_classes_per_type =
1385 std::vector<std::deque<int>>& vehicles_per_vehicle_class =
1388 type_index_of_vehicle.resize(vehicles_);
1389 sorted_vehicle_classes_per_type.clear();
1390 sorted_vehicle_classes_per_type.reserve(vehicles_);
1391 vehicles_per_vehicle_class.clear();
1394 absl::flat_hash_map<int64, int> type_to_type_index;
1396 for (
int v = 0; v < vehicles_; v++) {
1400 const int64 type = cost_class * nodes_squared + start * nodes_ + end;
1402 const auto& vehicle_type_added = type_to_type_index.insert(
1403 std::make_pair(type, type_to_type_index.size()));
1405 const int index = vehicle_type_added.first->second;
1408 const VehicleTypeContainer::VehicleClassEntry class_entry = {
1411 if (vehicle_type_added.second) {
1414 sorted_vehicle_classes_per_type.push_back({class_entry});
1418 sorted_vehicle_classes_per_type[
index].insert(class_entry);
1420 vehicles_per_vehicle_class[vehicle_class].push_back(v);
1421 type_index_of_vehicle[v] =
index;
1425 void RoutingModel::FinalizeVisitTypes() {
1431 single_nodes_of_type_.clear();
1432 single_nodes_of_type_.resize(num_visit_types_);
1433 pair_indices_of_type_.clear();
1434 pair_indices_of_type_.resize(num_visit_types_);
1435 std::vector<absl::flat_hash_set<int>> pair_indices_added_for_type(
1440 if (visit_type < 0) {
1443 const std::vector<std::pair<int, int>>& pickup_index_pairs =
1444 index_to_pickup_index_pairs_[
index];
1445 const std::vector<std::pair<int, int>>& delivery_index_pairs =
1446 index_to_delivery_index_pairs_[
index];
1447 if (pickup_index_pairs.empty() && delivery_index_pairs.empty()) {
1448 single_nodes_of_type_[visit_type].push_back(
index);
1450 for (
const std::vector<std::pair<int, int>>* index_pairs :
1451 {&pickup_index_pairs, &delivery_index_pairs}) {
1452 for (
const std::pair<int, int>& index_pair : *index_pairs) {
1453 const int pair_index = index_pair.first;
1454 if (pair_indices_added_for_type[visit_type].insert(pair_index).second) {
1455 pair_indices_of_type_[visit_type].push_back(pair_index);
1461 TopologicallySortVisitTypes();
1464 void RoutingModel::TopologicallySortVisitTypes() {
1465 if (!has_same_vehicle_type_requirements_ &&
1466 !has_temporal_type_requirements_) {
1469 std::vector<std::pair<double, double>> type_requirement_tightness(
1470 num_visit_types_, {0, 0});
1471 std::vector<absl::flat_hash_set<int>> type_to_dependent_types(
1473 SparseBitset<> types_in_requirement_graph(num_visit_types_);
1474 std::vector<int> in_degree(num_visit_types_, 0);
1475 for (
int type = 0; type < num_visit_types_; type++) {
1476 int num_alternative_required_types = 0;
1477 int num_required_sets = 0;
1478 for (
const std::vector<absl::flat_hash_set<int>>*
1479 required_type_alternatives :
1480 {&required_type_alternatives_when_adding_type_index_[type],
1481 &required_type_alternatives_when_removing_type_index_[type],
1482 &same_vehicle_required_type_alternatives_per_type_index_[type]}) {
1483 for (
const absl::flat_hash_set<int>& alternatives :
1484 *required_type_alternatives) {
1485 types_in_requirement_graph.Set(type);
1486 num_required_sets++;
1487 for (
int required_type : alternatives) {
1488 type_requirement_tightness[required_type].second +=
1489 1.0 / alternatives.size();
1490 types_in_requirement_graph.Set(required_type);
1491 num_alternative_required_types++;
1492 if (type_to_dependent_types[required_type].insert(type).second) {
1498 if (num_alternative_required_types > 0) {
1499 type_requirement_tightness[type].first += 1.0 * num_required_sets *
1501 num_alternative_required_types;
1506 topologically_sorted_visit_types_.clear();
1507 std::vector<int> current_types_with_zero_indegree;
1508 for (
int type : types_in_requirement_graph.PositionsSetAtLeastOnce()) {
1509 DCHECK(type_requirement_tightness[type].first > 0 ||
1510 type_requirement_tightness[type].second > 0);
1511 if (in_degree[type] == 0) {
1512 current_types_with_zero_indegree.push_back(type);
1516 int num_types_added = 0;
1517 while (!current_types_with_zero_indegree.empty()) {
1520 topologically_sorted_visit_types_.push_back({});
1521 std::vector<int>& topological_group =
1522 topologically_sorted_visit_types_.back();
1523 std::vector<int> next_types_with_zero_indegree;
1524 for (
int type : current_types_with_zero_indegree) {
1525 topological_group.push_back(type);
1527 for (
int dependent_type : type_to_dependent_types[type]) {
1528 DCHECK_GT(in_degree[dependent_type], 0);
1529 if (--in_degree[dependent_type] == 0) {
1530 next_types_with_zero_indegree.push_back(dependent_type);
1541 std::sort(topological_group.begin(), topological_group.end(),
1542 [&type_requirement_tightness](
int type1,
int type2) {
1543 const auto& tightness1 = type_requirement_tightness[type1];
1544 const auto& tightness2 = type_requirement_tightness[type2];
1545 return tightness1 > tightness2 ||
1546 (tightness1 == tightness2 && type1 < type2);
1549 current_types_with_zero_indegree.swap(next_types_with_zero_indegree);
1552 const int num_types_in_requirement_graph =
1553 types_in_requirement_graph.NumberOfSetCallsWithDifferentArguments();
1554 DCHECK_LE(num_types_added, num_types_in_requirement_graph);
1555 if (num_types_added < num_types_in_requirement_graph) {
1557 topologically_sorted_visit_types_.clear();
1562 const std::vector<int64>& indices,
int64 penalty,
int64 max_cardinality) {
1564 for (
int i = 0; i < indices.size(); ++i) {
1569 disjunctions_.
push_back({indices, {penalty, max_cardinality}});
1571 index_to_disjunctions_[
index].push_back(disjunction_index);
1573 return disjunction_index;
1576 std::vector<std::pair<int64, int64>>
1578 std::vector<std::pair<int64, int64>> var_index_pairs;
1579 for (
const Disjunction& disjunction : disjunctions_) {
1580 const std::vector<int64>&
var_indices = disjunction.indices;
1584 if (index_to_disjunctions_[v0].size() == 1 &&
1585 index_to_disjunctions_[v1].size() == 1) {
1590 std::sort(var_index_pairs.begin(), var_index_pairs.end());
1591 return var_index_pairs;
1596 for (Disjunction& disjunction : disjunctions_) {
1597 bool has_one_potentially_active_var =
false;
1598 for (
const int64 var_index : disjunction.indices) {
1600 has_one_potentially_active_var =
true;
1604 if (!has_one_potentially_active_var) {
1605 disjunction.value.max_cardinality = 0;
1611 const std::vector<int64>& indices = disjunctions_[disjunction].indices;
1612 const int indices_size = indices.size();
1613 std::vector<IntVar*> disjunction_vars(indices_size);
1614 for (
int i = 0; i < indices_size; ++i) {
1619 const int64 max_cardinality =
1620 disjunctions_[disjunction].value.max_cardinality;
1621 IntVar* no_active_var = solver_->MakeBoolVar();
1622 IntVar* number_active_vars = solver_->MakeIntVar(0, max_cardinality);
1623 solver_->AddConstraint(
1624 solver_->MakeSumEquality(disjunction_vars, number_active_vars));
1625 solver_->AddConstraint(solver_->MakeIsDifferentCstCt(
1626 number_active_vars, max_cardinality, no_active_var));
1627 const int64 penalty = disjunctions_[disjunction].value.penalty;
1629 no_active_var->SetMax(0);
1632 return solver_->MakeProd(no_active_var, penalty)->Var();
1637 const std::vector<int64>& indices,
int64 cost) {
1638 if (!indices.empty()) {
1639 ValuedNodes<int64> same_vehicle_cost;
1641 same_vehicle_cost.indices.push_back(
index);
1643 same_vehicle_cost.value =
cost;
1644 same_vehicle_costs_.push_back(same_vehicle_cost);
1650 auto& allowed_vehicles = allowed_vehicles_[
index];
1651 allowed_vehicles.clear();
1653 allowed_vehicles.insert(vehicle);
1658 AddPickupAndDeliverySetsInternal({pickup}, {delivery});
1667 pickup_delivery_disjunctions_.push_back(
1668 {pickup_disjunction, delivery_disjunction});
1671 void RoutingModel::AddPickupAndDeliverySetsInternal(
1672 const std::vector<int64>& pickups,
const std::vector<int64>& deliveries) {
1673 if (pickups.empty() || deliveries.empty()) {
1677 const int pair_index = pickup_delivery_pairs_.size();
1678 for (
int pickup_index = 0; pickup_index < pickups.size(); pickup_index++) {
1679 const int64 pickup = pickups[pickup_index];
1681 index_to_pickup_index_pairs_[pickup].emplace_back(pair_index, pickup_index);
1683 for (
int delivery_index = 0; delivery_index < deliveries.size();
1685 const int64 delivery = deliveries[delivery_index];
1687 index_to_delivery_index_pairs_[delivery].emplace_back(pair_index,
1690 pickup_delivery_pairs_.push_back({pickups, deliveries});
1694 int64 node_index)
const {
1695 CHECK_LT(node_index, index_to_pickup_index_pairs_.size());
1696 return index_to_pickup_index_pairs_[node_index];
1700 int64 node_index)
const {
1701 CHECK_LT(node_index, index_to_delivery_index_pairs_.size());
1702 return index_to_delivery_index_pairs_[node_index];
1708 vehicle_pickup_delivery_policy_[vehicle] = policy;
1714 for (
int i = 0; i < vehicles_; ++i) {
1722 return vehicle_pickup_delivery_policy_[vehicle];
1727 for (
int i = 0; i <
Nexts().size(); ++i) {
1737 IntVar* RoutingModel::CreateSameVehicleCost(
int vehicle_index) {
1738 const std::vector<int64>& indices =
1739 same_vehicle_costs_[vehicle_index].indices;
1740 CHECK(!indices.empty());
1741 std::vector<IntVar*> vehicle_counts;
1742 solver_->MakeIntVarArray(vehicle_vars_.size() + 1, 0, indices.size() + 1,
1744 std::vector<int64> vehicle_values(vehicle_vars_.size() + 1);
1745 for (
int i = 0; i < vehicle_vars_.size(); ++i) {
1746 vehicle_values[i] = i;
1748 vehicle_values[vehicle_vars_.size()] = -1;
1749 std::vector<IntVar*> vehicle_vars;
1750 vehicle_vars.reserve(indices.size());
1752 vehicle_vars.push_back(vehicle_vars_[
index]);
1754 solver_->AddConstraint(solver_->MakeDistribute(vehicle_vars, vehicle_counts));
1755 std::vector<IntVar*> vehicle_used;
1756 for (
int i = 0; i < vehicle_vars_.size() + 1; ++i) {
1757 vehicle_used.push_back(
1758 solver_->MakeIsGreaterOrEqualCstVar(vehicle_counts[i], 1));
1760 vehicle_used.push_back(solver_->MakeIntConst(-1));
1762 ->MakeProd(solver_->MakeMax(solver_->MakeSum(vehicle_used), 0),
1763 same_vehicle_costs_[vehicle_index].value)
1768 extra_operators_.push_back(ls_operator);
1775 void RoutingModel::AppendHomogeneousArcCosts(
1776 const RoutingSearchParameters&
parameters,
int node_index,
1777 std::vector<IntVar*>* cost_elements) {
1778 CHECK(cost_elements !=
nullptr);
1779 const auto arc_cost_evaluator = [
this, node_index](
int64 next_index) {
1786 IntVar*
const base_cost_var = solver_->MakeIntVar(0,
kint64max);
1787 solver_->AddConstraint(MakeLightElement(
1788 solver_.get(), base_cost_var, nexts_[node_index], arc_cost_evaluator,
1789 [
this]() { return enable_deep_serialization_; }));
1791 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1792 cost_elements->push_back(
var);
1794 IntExpr*
const expr =
1795 solver_->MakeElement(arc_cost_evaluator, nexts_[node_index]);
1796 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
1797 cost_elements->push_back(
var);
1801 void RoutingModel::AppendArcCosts(
const RoutingSearchParameters&
parameters,
1803 std::vector<IntVar*>* cost_elements) {
1804 CHECK(cost_elements !=
nullptr);
1810 IntVar*
const base_cost_var = solver_->MakeIntVar(0,
kint64max);
1811 solver_->AddConstraint(MakeLightElement2(
1812 solver_.get(), base_cost_var, nexts_[node_index],
1813 vehicle_vars_[node_index],
1814 [
this, node_index](
int64 to,
int64 vehicle) {
1815 return GetArcCostForVehicle(node_index, to, vehicle);
1817 [
this]() { return enable_deep_serialization_; }));
1819 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1820 cost_elements->push_back(
var);
1822 IntVar*
const vehicle_class_var =
1826 return SafeGetCostClassInt64OfVehicle(
index);
1828 vehicle_vars_[node_index])
1830 IntExpr*
const expr = solver_->MakeElement(
1834 nexts_[node_index], vehicle_class_var);
1835 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
1836 cost_elements->push_back(
var);
1840 int RoutingModel::GetVehicleStartClass(
int64 start_index)
const {
1841 const int vehicle = index_to_vehicle_[start_index];
1848 std::string RoutingModel::FindErrorInSearchParametersForModel(
1849 const RoutingSearchParameters& search_parameters)
const {
1851 search_parameters.first_solution_strategy();
1852 if (GetFirstSolutionDecisionBuilder(search_parameters) ==
nullptr) {
1853 return absl::StrCat(
1854 "Undefined first solution strategy: ",
1855 FirstSolutionStrategy::Value_Name(first_solution_strategy),
1856 " (int value: ", first_solution_strategy,
")");
1858 if (search_parameters.first_solution_strategy() ==
1859 FirstSolutionStrategy::SWEEP &&
1861 return "Undefined sweep arranger for ROUTING_SWEEP strategy.";
1866 void RoutingModel::QuietCloseModel() {
1877 same_vehicle_components_.SetNumberOfNodes(
model->Size());
1878 for (
const std::string&
name :
model->GetAllDimensionNames()) {
1880 const std::vector<IntVar*>& cumuls = dimension->
cumuls();
1881 for (
int i = 0; i < cumuls.size(); ++i) {
1882 cumul_to_dim_indices_[cumuls[i]] = {dimension, i};
1885 const std::vector<IntVar*>& vehicle_vars =
model->VehicleVars();
1886 for (
int i = 0; i < vehicle_vars.size(); ++i) {
1887 vehicle_var_to_indices_[vehicle_vars[i]] = i;
1889 RegisterInspectors();
1893 const std::vector<int> node_to_same_vehicle_component_id =
1894 same_vehicle_components_.GetComponentIds();
1895 model_->InitSameVehicleGroups(
1896 same_vehicle_components_.GetNumberOfComponents());
1897 for (
int node = 0; node < model_->Size(); ++node) {
1898 model_->SetSameVehicleGroup(node,
1899 node_to_same_vehicle_component_id[node]);
1905 const Constraint*
const constraint)
override {
1909 IntExpr*
const expr)
override {
1911 [](
const IntExpr* expr) {})(expr);
1914 const std::vector<int64>& values)
override {
1916 [](
const std::vector<int64>& int_array) {})(values);
1920 using ExprInspector = std::function<void(
const IntExpr*)>;
1921 using ArrayInspector = std::function<void(
const std::vector<int64>&)>;
1922 using ConstraintInspector = std::function<void()>;
1924 void RegisterInspectors() {
1925 expr_inspectors_[kExpressionArgument] = [
this](
const IntExpr* expr) {
1928 expr_inspectors_[kLeftArgument] = [
this](
const IntExpr* expr) {
1931 expr_inspectors_[kRightArgument] = [
this](
const IntExpr* expr) {
1934 array_inspectors_[kStartsArgument] =
1935 [
this](
const std::vector<int64>& int_array) {
1936 starts_argument_ = int_array;
1938 array_inspectors_[kEndsArgument] =
1939 [
this](
const std::vector<int64>& int_array) {
1940 ends_argument_ = int_array;
1942 constraint_inspectors_[kNotMember] = [
this]() {
1943 std::pair<RoutingDimension*, int> dim_index;
1946 const int index = dim_index.second;
1947 dimension->forbidden_intervals_[
index].InsertIntervals(starts_argument_,
1949 VLOG(2) << dimension->name() <<
" " <<
index <<
": "
1950 << dimension->forbidden_intervals_[
index].DebugString();
1953 starts_argument_.clear();
1954 ends_argument_.clear();
1956 constraint_inspectors_[kEquality] = [
this]() {
1958 int right_index = 0;
1959 if (
gtl::FindCopy(vehicle_var_to_indices_, left_, &left_index) &&
1960 gtl::FindCopy(vehicle_var_to_indices_, right_, &right_index)) {
1961 VLOG(2) <<
"Vehicle variables for " << left_index <<
" and "
1962 << right_index <<
" are equal.";
1963 same_vehicle_components_.AddEdge(left_index, right_index);
1968 constraint_inspectors_[kLessOrEqual] = [
this]() {
1969 std::pair<RoutingDimension*, int> left_index;
1970 std::pair<RoutingDimension*, int> right_index;
1971 if (
gtl::FindCopy(cumul_to_dim_indices_, left_, &left_index) &&
1972 gtl::FindCopy(cumul_to_dim_indices_, right_, &right_index)) {
1974 if (dimension == right_index.first) {
1975 VLOG(2) <<
"For dimension " << dimension->name() <<
", cumul for "
1976 << left_index.second <<
" is less than " << right_index.second
1978 dimension->path_precedence_graph_.AddArc(left_index.second,
1979 right_index.second);
1989 absl::flat_hash_map<const IntExpr*, std::pair<RoutingDimension*, int>>
1990 cumul_to_dim_indices_;
1991 absl::flat_hash_map<const IntExpr*, int> vehicle_var_to_indices_;
1992 absl::flat_hash_map<std::string, ExprInspector> expr_inspectors_;
1993 absl::flat_hash_map<std::string, ArrayInspector> array_inspectors_;
1994 absl::flat_hash_map<std::string, ConstraintInspector> constraint_inspectors_;
1995 const IntExpr*
expr_ =
nullptr;
1996 const IntExpr* left_ =
nullptr;
1997 const IntExpr* right_ =
nullptr;
1998 std::vector<int64> starts_argument_;
1999 std::vector<int64> ends_argument_;
2002 void RoutingModel::DetectImplicitPickupAndDeliveries() {
2003 std::vector<int> non_pickup_delivery_nodes;
2004 for (
int node = 0; node <
Size(); ++node) {
2007 non_pickup_delivery_nodes.push_back(node);
2011 std::set<std::pair<int64, int64>> implicit_pickup_deliveries;
2013 if (dimension->class_evaluators_.size() != 1) {
2018 if (transit ==
nullptr)
continue;
2019 absl::flat_hash_map<int64, std::vector<int64>> nodes_by_positive_demand;
2020 absl::flat_hash_map<int64, std::vector<int64>> nodes_by_negative_demand;
2021 for (
int node : non_pickup_delivery_nodes) {
2024 nodes_by_positive_demand[
demand].push_back(node);
2026 nodes_by_negative_demand[-
demand].push_back(node);
2029 for (
const auto& [
demand, positive_nodes] : nodes_by_positive_demand) {
2030 const std::vector<int64>*
const negative_nodes =
2032 if (negative_nodes !=
nullptr) {
2033 for (
int64 positive_node : positive_nodes) {
2034 for (
int64 negative_node : *negative_nodes) {
2035 implicit_pickup_deliveries.insert({positive_node, negative_node});
2041 implicit_pickup_delivery_pairs_without_alternatives_.clear();
2042 for (
auto [pickup, delivery] : implicit_pickup_deliveries) {
2043 implicit_pickup_delivery_pairs_without_alternatives_.emplace_back(
2044 std::vector<int64>({pickup}), std::vector<int64>({delivery}));
2051 if (!error.empty()) {
2053 LOG(
ERROR) <<
"Invalid RoutingSearchParameters: " << error;
2063 dimension->CloseModel(UsesLightPropagation(
parameters));
2066 ComputeVehicleClasses();
2067 ComputeVehicleTypes();
2068 FinalizeVisitTypes();
2069 vehicle_start_class_callback_ = [
this](
int64 start) {
2070 return GetVehicleStartClass(start);
2073 AddNoCycleConstraintInternal();
2075 const int size =
Size();
2078 for (
int i = 0; i < vehicles_; ++i) {
2079 const int64 start = starts_[i];
2080 const int64 end = ends_[i];
2081 solver_->AddConstraint(
2082 solver_->MakeEquality(vehicle_vars_[start], solver_->MakeIntConst(i)));
2083 solver_->AddConstraint(
2084 solver_->MakeEquality(vehicle_vars_[end], solver_->MakeIntConst(i)));
2085 solver_->AddConstraint(
2086 solver_->MakeIsDifferentCstCt(nexts_[start], end, vehicle_active_[i]));
2087 if (consider_empty_route_costs_[i]) {
2088 vehicle_costs_considered_[i]->SetMin(1);
2090 solver_->AddConstraint(solver_->MakeEquality(
2091 vehicle_active_[i], vehicle_costs_considered_[i]));
2096 if (vehicles_ > max_active_vehicles_) {
2097 solver_->AddConstraint(
2098 solver_->MakeSumLessOrEqual(vehicle_active_, max_active_vehicles_));
2106 if (vehicles_ > 1) {
2107 std::vector<IntVar*> zero_transit(size, solver_->MakeIntConst(0));
2108 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
2109 nexts_, active_, vehicle_vars_, zero_transit));
2114 for (
int i = 0; i < size; ++i) {
2116 active_[i]->SetValue(1);
2122 const absl::flat_hash_set<VisitTypePolicy>*
const infeasible_policies =
2124 if (infeasible_policies !=
nullptr &&
2126 active_[i]->SetValue(0);
2131 for (
int i = 0; i < allowed_vehicles_.size(); ++i) {
2132 const auto& allowed_vehicles = allowed_vehicles_[i];
2133 if (!allowed_vehicles.empty()) {
2135 vehicles.reserve(allowed_vehicles.size() + 1);
2137 for (
int vehicle : allowed_vehicles) {
2145 for (
int i = 0; i < size; ++i) {
2147 solver_->AddConstraint(solver_->RevAlloc(
2148 new DifferentFromValues(solver_.get(), nexts_[i], starts_)));
2150 solver_->AddConstraint(
2151 solver_->MakeIsDifferentCstCt(nexts_[i], i, active_[i]));
2156 for (
int i = 0; i < size; ++i) {
2157 solver_->AddConstraint(
2158 solver_->MakeIsDifferentCstCt(vehicle_vars_[i], -1, active_[i]));
2162 solver_->AddConstraint(
2167 for (
int i = 0; i < vehicles_; ++i) {
2168 std::vector<int64> forbidden_ends;
2169 forbidden_ends.reserve(vehicles_ - 1);
2170 for (
int j = 0; j < vehicles_; ++j) {
2172 forbidden_ends.push_back(ends_[j]);
2175 solver_->AddConstraint(solver_->RevAlloc(
new DifferentFromValues(
2176 solver_.get(), nexts_[starts_[i]], std::move(forbidden_ends))));
2180 for (
const int64 end : ends_) {
2181 is_bound_to_end_[end]->SetValue(1);
2184 std::vector<IntVar*> cost_elements;
2186 if (vehicles_ > 0) {
2187 for (
int node_index = 0; node_index < size; ++node_index) {
2189 AppendHomogeneousArcCosts(
parameters, node_index, &cost_elements);
2191 AppendArcCosts(
parameters, node_index, &cost_elements);
2194 if (vehicle_amortized_cost_factors_set_) {
2195 std::vector<IntVar*> route_lengths;
2196 solver_->MakeIntVarArray(vehicles_, 0, size, &route_lengths);
2197 solver_->AddConstraint(
2198 solver_->MakeDistribute(vehicle_vars_, route_lengths));
2199 std::vector<IntVar*> vehicle_used;
2200 for (
int i = 0; i < vehicles_; i++) {
2202 vehicle_used.push_back(
2203 solver_->MakeIsGreaterCstVar(route_lengths[i], 2));
2206 ->MakeProd(solver_->MakeOpposite(solver_->MakeSquare(
2207 solver_->MakeSum(route_lengths[i], -2))),
2208 quadratic_cost_factor_of_vehicle_[i])
2210 cost_elements.push_back(
var);
2212 IntVar*
const vehicle_usage_cost =
2213 solver_->MakeScalProd(vehicle_used, linear_cost_factor_of_vehicle_)
2215 cost_elements.push_back(vehicle_usage_cost);
2220 dimension->SetupGlobalSpanCost(&cost_elements);
2221 dimension->SetupSlackAndDependentTransitCosts();
2222 const std::vector<int64>& span_costs =
2223 dimension->vehicle_span_cost_coefficients();
2224 const std::vector<int64>& span_ubs = dimension->vehicle_span_upper_bounds();
2225 const bool has_span_constraint =
2226 std::any_of(span_costs.begin(), span_costs.end(),
2227 [](
int64 coeff) { return coeff != 0; }) ||
2228 std::any_of(span_ubs.begin(), span_ubs.end(),
2229 [](
int64 value) { return value < kint64max; }) ||
2230 dimension->HasSoftSpanUpperBounds() ||
2231 dimension->HasQuadraticCostSoftSpanUpperBounds();
2232 if (has_span_constraint) {
2233 std::vector<IntVar*> spans(
vehicles(),
nullptr);
2234 std::vector<IntVar*> total_slacks(
vehicles(),
nullptr);
2236 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2238 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2240 if (span_costs[vehicle] != 0) {
2241 total_slacks[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2244 if (dimension->HasSoftSpanUpperBounds()) {
2245 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2246 if (spans[vehicle])
continue;
2248 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2249 if (bound_cost.
cost == 0)
continue;
2250 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2253 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2254 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2255 if (spans[vehicle])
continue;
2257 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2258 if (bound_cost.
cost == 0)
continue;
2259 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2262 solver_->AddConstraint(
2266 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2267 if (!spans[vehicle] && !total_slacks[vehicle])
continue;
2268 if (spans[vehicle]) {
2277 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2278 if (span_costs[vehicle] == 0)
continue;
2279 DCHECK(total_slacks[vehicle] !=
nullptr);
2280 IntVar*
const slack_amount =
2282 ->MakeProd(vehicle_costs_considered_[vehicle],
2283 total_slacks[vehicle])
2285 IntVar*
const slack_cost =
2286 solver_->MakeProd(slack_amount, span_costs[vehicle])->Var();
2287 cost_elements.push_back(slack_cost);
2289 span_costs[vehicle]);
2291 if (dimension->HasSoftSpanUpperBounds()) {
2292 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2293 const auto bound_cost =
2294 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2295 if (bound_cost.cost == 0 || bound_cost.bound ==
kint64max)
continue;
2296 DCHECK(spans[vehicle] !=
nullptr);
2299 IntVar*
const span_violation_amount =
2302 vehicle_costs_considered_[vehicle],
2304 solver_->MakeSum(spans[vehicle], -bound_cost.bound),
2307 IntVar*
const span_violation_cost =
2308 solver_->MakeProd(span_violation_amount, bound_cost.cost)->Var();
2309 cost_elements.push_back(span_violation_cost);
2314 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2315 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2316 const auto bound_cost =
2317 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2318 if (bound_cost.cost == 0 || bound_cost.bound ==
kint64max)
continue;
2319 DCHECK(spans[vehicle] !=
nullptr);
2322 IntExpr* max0 = solver_->MakeMax(
2323 solver_->MakeSum(spans[vehicle], -bound_cost.bound), 0);
2324 IntVar*
const squared_span_violation_amount =
2326 ->MakeProd(vehicle_costs_considered_[vehicle],
2327 solver_->MakeSquare(max0))
2329 IntVar*
const span_violation_cost =
2330 solver_->MakeProd(squared_span_violation_amount, bound_cost.cost)
2332 cost_elements.push_back(span_violation_cost);
2341 IntVar* penalty_var = CreateDisjunction(i);
2342 if (penalty_var !=
nullptr) {
2343 cost_elements.push_back(penalty_var);
2348 dimension->SetupCumulVarSoftLowerBoundCosts(&cost_elements);
2349 dimension->SetupCumulVarSoftUpperBoundCosts(&cost_elements);
2350 dimension->SetupCumulVarPiecewiseLinearCosts(&cost_elements);
2353 for (
int i = 0; i < same_vehicle_costs_.size(); ++i) {
2354 cost_elements.push_back(CreateSameVehicleCost(i));
2356 cost_ = solver_->MakeSum(cost_elements)->Var();
2360 std::vector<std::pair<int, int>> pickup_delivery_precedences;
2361 for (
const auto& pair : pickup_delivery_pairs_) {
2362 DCHECK(!pair.first.empty() && !pair.second.empty());
2363 for (
int pickup : pair.first) {
2364 for (
int delivery : pair.second) {
2365 pickup_delivery_precedences.emplace_back(pickup, delivery);
2369 std::vector<int> lifo_vehicles;
2370 std::vector<int> fifo_vehicles;
2371 for (
int i = 0; i < vehicles_; ++i) {
2372 switch (vehicle_pickup_delivery_policy_[i]) {
2376 lifo_vehicles.push_back(
Start(i));
2379 fifo_vehicles.push_back(
Start(i));
2383 solver_->AddConstraint(solver_->MakePathPrecedenceConstraint(
2384 nexts_, pickup_delivery_precedences, lifo_vehicles, fifo_vehicles));
2387 enable_deep_serialization_ =
false;
2388 std::unique_ptr<RoutingModelInspector> inspector(
2390 solver_->Accept(inspector.get());
2391 enable_deep_serialization_ =
true;
2397 dimension->GetPathPrecedenceGraph();
2398 std::vector<std::pair<int, int>> path_precedences;
2400 for (
const auto head : graph[
tail]) {
2401 path_precedences.emplace_back(
tail,
head);
2404 if (!path_precedences.empty()) {
2405 solver_->AddConstraint(solver_->MakePathTransitPrecedenceConstraint(
2406 nexts_, dimension->transits(), path_precedences));
2411 dimension->GetNodePrecedences()) {
2412 const int64 first_node = node_precedence.first_node;
2413 const int64 second_node = node_precedence.second_node;
2414 IntExpr*
const nodes_are_selected =
2415 solver_->MakeMin(active_[first_node], active_[second_node]);
2416 IntExpr*
const cumul_difference = solver_->MakeDifference(
2417 dimension->CumulVar(second_node), dimension->CumulVar(first_node));
2418 IntVar*
const cumul_difference_is_ge_offset =
2419 solver_->MakeIsGreaterOrEqualCstVar(cumul_difference,
2420 node_precedence.offset);
2423 solver_->AddConstraint(solver_->MakeLessOrEqual(
2424 nodes_are_selected->
Var(), cumul_difference_is_ge_offset));
2428 DetectImplicitPickupAndDeliveries();
2437 CreateFirstSolutionDecisionBuilders(
parameters);
2438 error = FindErrorInSearchParametersForModel(
parameters);
2439 if (!error.empty()) {
2441 LOG(
ERROR) <<
"Invalid RoutingSearchParameters for this model: " << error;
2448 Link(std::pair<int, int> link,
double value,
int vehicle_class,
2452 vehicle_class(vehicle_class),
2453 start_depot(start_depot),
2454 end_depot(end_depot) {}
2478 bool check_assignment,
int64 num_indices,
2479 const std::vector<Link>& links_list)
2480 : assignment_(assignment),
2482 check_assignment_(check_assignment),
2483 solver_(model_->
solver()),
2484 num_indices_(num_indices),
2485 links_list_(links_list),
2486 nexts_(model_->
Nexts()),
2487 in_route_(num_indices_, -1),
2489 index_to_chain_index_(num_indices, -1),
2490 index_to_vehicle_class_index_(num_indices, -1) {
2492 const std::vector<std::string> dimension_names =
2493 model_->GetAllDimensionNames();
2494 dimensions_.assign(dimension_names.size(),
nullptr);
2495 for (
int i = 0; i < dimension_names.size(); ++i) {
2496 dimensions_[i] = &model_->GetDimensionOrDie(dimension_names[i]);
2499 cumuls_.resize(dimensions_.size());
2500 for (std::vector<int64>& cumuls :
cumuls_) {
2501 cumuls.resize(num_indices_);
2503 new_possible_cumuls_.resize(dimensions_.size());
2509 model_->solver()->TopPeriodicCheck();
2512 if (!model_->IsStart(
index) && !model_->IsEnd(
index)) {
2513 std::vector<int> route(1,
index);
2514 routes_.push_back(route);
2515 in_route_[
index] = routes_.size() - 1;
2519 for (
const Link& link : links_list_) {
2520 model_->solver()->TopPeriodicCheck();
2521 const int index1 = link.link.first;
2522 const int index2 = link.link.second;
2523 const int vehicle_class = link.vehicle_class;
2524 const int64 start_depot = link.start_depot;
2525 const int64 end_depot = link.end_depot;
2528 if (index_to_vehicle_class_index_[index1] < 0) {
2529 for (
int dimension_index = 0; dimension_index < dimensions_.size();
2530 ++dimension_index) {
2531 cumuls_[dimension_index][index1] =
2532 std::max(dimensions_[dimension_index]->GetTransitValue(
2533 start_depot, index1, 0),
2534 dimensions_[dimension_index]->CumulVar(index1)->Min());
2537 if (index_to_vehicle_class_index_[index2] < 0) {
2538 for (
int dimension_index = 0; dimension_index < dimensions_.size();
2539 ++dimension_index) {
2540 cumuls_[dimension_index][index2] =
2541 std::max(dimensions_[dimension_index]->GetTransitValue(
2542 start_depot, index2, 0),
2543 dimensions_[dimension_index]->CumulVar(index2)->Min());
2547 const int route_index1 = in_route_[index1];
2548 const int route_index2 = in_route_[index2];
2550 route_index1 >= 0 && route_index2 >= 0 &&
2551 FeasibleMerge(routes_[route_index1], routes_[route_index2], index1,
2552 index2, route_index1, route_index2, vehicle_class,
2553 start_depot, end_depot);
2554 if (Merge(merge, route_index1, route_index2)) {
2555 index_to_vehicle_class_index_[index1] = vehicle_class;
2556 index_to_vehicle_class_index_[index2] = vehicle_class;
2560 model_->solver()->TopPeriodicCheck();
2564 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
2566 final_chains_.push_back(chains_[chain_index]);
2569 std::sort(final_chains_.begin(), final_chains_.end(), ChainComparator);
2570 for (
int route_index = 0; route_index < routes_.size(); ++route_index) {
2572 final_routes_.push_back(routes_[route_index]);
2575 std::sort(final_routes_.begin(), final_routes_.end(), RouteComparator);
2577 const int extra_vehicles =
std::max(
2578 0,
static_cast<int>(final_chains_.size()) - model_->vehicles());
2580 int chain_index = 0;
2581 for (chain_index = extra_vehicles; chain_index < final_chains_.size();
2583 if (chain_index - extra_vehicles >= model_->vehicles()) {
2586 const int start = final_chains_[chain_index].head;
2587 const int end = final_chains_[chain_index].tail;
2589 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
2590 assignment_->SetValue(
2591 model_->NextVar(model_->Start(chain_index - extra_vehicles)), start);
2592 assignment_->Add(nexts_[end]);
2593 assignment_->SetValue(nexts_[end],
2594 model_->End(chain_index - extra_vehicles));
2598 for (
int route_index = 0; route_index < final_routes_.size();
2600 if (chain_index - extra_vehicles >= model_->vehicles()) {
2603 DCHECK_LT(route_index, final_routes_.size());
2604 const int head = final_routes_[route_index].front();
2605 const int tail = final_routes_[route_index].back();
2608 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
2609 assignment_->SetValue(
2610 model_->NextVar(model_->Start(chain_index - extra_vehicles)),
head);
2611 assignment_->Add(nexts_[
tail]);
2612 assignment_->SetValue(nexts_[
tail],
2613 model_->End(chain_index - extra_vehicles));
2621 if (!assignment_->Contains(
next)) {
2622 assignment_->Add(
next);
2631 return final_routes_;
2635 enum MergeStatus { FIRST_SECOND, SECOND_FIRST, NO_MERGE };
2638 bool operator()(
const std::vector<int>& route1,
2639 const std::vector<int>& route2)
const {
2640 return (route1.size() < route2.size());
2651 bool operator()(
const Chain& chain1,
const Chain& chain2)
const {
2652 return (chain1.nodes < chain2.nodes);
2656 bool Head(
int node)
const {
2657 return (node == routes_[in_route_[node]].front());
2660 bool Tail(
int node)
const {
2661 return (node == routes_[in_route_[node]].back());
2664 bool FeasibleRoute(
const std::vector<int>& route,
int64 route_cumul,
2665 int dimension_index) {
2667 std::vector<int>::const_iterator it = route.begin();
2668 int64 cumul = route_cumul;
2669 while (it != route.end()) {
2670 const int previous = *it;
2671 const int64 cumul_previous = cumul;
2675 if (it == route.end()) {
2678 const int next = *it;
2679 int64 available_from_previous =
2680 cumul_previous + dimension.GetTransitValue(previous,
next, 0);
2681 int64 available_cumul_next =
2684 const int64 slack = available_cumul_next - available_from_previous;
2685 if (slack > dimension.SlackVar(previous)->Max()) {
2686 available_cumul_next =
2687 available_from_previous + dimension.SlackVar(previous)->Max();
2690 if (available_cumul_next > dimension.CumulVar(
next)->Max()) {
2693 if (available_cumul_next <=
cumuls_[dimension_index][
next]) {
2696 cumul = available_cumul_next;
2701 bool CheckRouteConnection(
const std::vector<int>& route1,
2702 const std::vector<int>& route2,
int dimension_index,
2704 const int tail1 = route1.back();
2705 const int head2 = route2.front();
2706 const int tail2 = route2.back();
2708 int non_depot_node = -1;
2709 for (
int node = 0; node < num_indices_; ++node) {
2710 if (!model_->IsStart(node) && !model_->IsEnd(node)) {
2711 non_depot_node = node;
2716 const int64 depot_threashold =
2717 std::max(dimension.SlackVar(non_depot_node)->Max(),
2718 dimension.CumulVar(non_depot_node)->Max());
2720 int64 available_from_tail1 =
cumuls_[dimension_index][tail1] +
2721 dimension.GetTransitValue(tail1, head2, 0);
2722 int64 new_available_cumul_head2 =
2725 const int64 slack = new_available_cumul_head2 - available_from_tail1;
2726 if (slack > dimension.SlackVar(tail1)->Max()) {
2727 new_available_cumul_head2 =
2728 available_from_tail1 + dimension.SlackVar(tail1)->Max();
2731 bool feasible_route =
true;
2732 if (new_available_cumul_head2 > dimension.CumulVar(head2)->Max()) {
2735 if (new_available_cumul_head2 <=
cumuls_[dimension_index][head2]) {
2740 FeasibleRoute(route2, new_available_cumul_head2, dimension_index);
2741 const int64 new_possible_cumul_tail2 =
2743 ? new_possible_cumuls_[dimension_index][tail2]
2744 :
cumuls_[dimension_index][tail2];
2746 if (!feasible_route || (new_possible_cumul_tail2 +
2747 dimension.GetTransitValue(tail2, end_depot, 0) >
2748 depot_threashold)) {
2754 bool FeasibleMerge(
const std::vector<int>& route1,
2755 const std::vector<int>& route2,
int node1,
int node2,
2756 int route_index1,
int route_index2,
int vehicle_class,
2758 if ((route_index1 == route_index2) || !(Tail(node1) && Head(node2))) {
2763 if (!((index_to_vehicle_class_index_[node1] == -1 &&
2764 index_to_vehicle_class_index_[node2] == -1) ||
2765 (index_to_vehicle_class_index_[node1] == vehicle_class &&
2766 index_to_vehicle_class_index_[node2] == -1) ||
2767 (index_to_vehicle_class_index_[node1] == -1 &&
2768 index_to_vehicle_class_index_[node2] == vehicle_class) ||
2769 (index_to_vehicle_class_index_[node1] == vehicle_class &&
2770 index_to_vehicle_class_index_[node2] == vehicle_class))) {
2776 for (
int dimension_index = 0; dimension_index < dimensions_.size();
2777 ++dimension_index) {
2778 new_possible_cumuls_[dimension_index].clear();
2779 merge = merge && CheckRouteConnection(route1, route2, dimension_index,
2780 start_depot, end_depot);
2788 bool CheckTempAssignment(Assignment*
const temp_assignment,
2789 int new_chain_index,
int old_chain_index,
int head1,
2790 int tail1,
int head2,
int tail2) {
2793 if (new_chain_index >= model_->vehicles())
return false;
2794 const int start = head1;
2795 temp_assignment->Add(model_->NextVar(model_->Start(new_chain_index)));
2796 temp_assignment->SetValue(model_->NextVar(model_->Start(new_chain_index)),
2798 temp_assignment->Add(nexts_[tail1]);
2799 temp_assignment->SetValue(nexts_[tail1], head2);
2800 temp_assignment->Add(nexts_[tail2]);
2801 temp_assignment->SetValue(nexts_[tail2], model_->End(new_chain_index));
2802 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
2803 if ((chain_index != new_chain_index) &&
2804 (chain_index != old_chain_index) &&
2806 const int start = chains_[chain_index].head;
2807 const int end = chains_[chain_index].tail;
2808 temp_assignment->Add(model_->NextVar(model_->Start(chain_index)));
2809 temp_assignment->SetValue(model_->NextVar(model_->Start(chain_index)),
2811 temp_assignment->Add(nexts_[end]);
2812 temp_assignment->SetValue(nexts_[end], model_->End(chain_index));
2815 return solver_->Solve(solver_->MakeRestoreAssignment(temp_assignment));
2818 bool UpdateAssignment(
const std::vector<int>& route1,
2819 const std::vector<int>& route2) {
2820 bool feasible =
true;
2821 const int head1 = route1.front();
2822 const int tail1 = route1.back();
2823 const int head2 = route2.front();
2824 const int tail2 = route2.back();
2825 const int chain_index1 = index_to_chain_index_[head1];
2826 const int chain_index2 = index_to_chain_index_[head2];
2827 if (chain_index1 < 0 && chain_index2 < 0) {
2828 const int chain_index = chains_.size();
2829 if (check_assignment_) {
2830 Assignment*
const temp_assignment =
2831 solver_->MakeAssignment(assignment_);
2832 feasible = CheckTempAssignment(temp_assignment, chain_index, -1, head1,
2833 tail1, head2, tail2);
2840 index_to_chain_index_[head1] = chain_index;
2841 index_to_chain_index_[tail2] = chain_index;
2842 chains_.push_back(chain);
2844 }
else if (chain_index1 >= 0 && chain_index2 < 0) {
2845 if (check_assignment_) {
2846 Assignment*
const temp_assignment =
2847 solver_->MakeAssignment(assignment_);
2849 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
2850 head1, tail1, head2, tail2);
2853 index_to_chain_index_[tail2] = chain_index1;
2854 chains_[chain_index1].head = head1;
2855 chains_[chain_index1].tail = tail2;
2856 ++chains_[chain_index1].nodes;
2858 }
else if (chain_index1 < 0 && chain_index2 >= 0) {
2859 if (check_assignment_) {
2860 Assignment*
const temp_assignment =
2861 solver_->MakeAssignment(assignment_);
2863 CheckTempAssignment(temp_assignment, chain_index2, chain_index1,
2864 head1, tail1, head2, tail2);
2867 index_to_chain_index_[head1] = chain_index2;
2868 chains_[chain_index2].head = head1;
2869 chains_[chain_index2].tail = tail2;
2870 ++chains_[chain_index2].nodes;
2873 if (check_assignment_) {
2874 Assignment*
const temp_assignment =
2875 solver_->MakeAssignment(assignment_);
2877 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
2878 head1, tail1, head2, tail2);
2881 index_to_chain_index_[tail2] = chain_index1;
2882 chains_[chain_index1].head = head1;
2883 chains_[chain_index1].tail = tail2;
2884 chains_[chain_index1].nodes += chains_[chain_index2].nodes;
2885 deleted_chains_.insert(chain_index2);
2889 assignment_->Add(nexts_[tail1]);
2890 assignment_->SetValue(nexts_[tail1], head2);
2895 bool Merge(
bool merge,
int index1,
int index2) {
2897 if (UpdateAssignment(routes_[index1], routes_[index2])) {
2899 for (
const int node : routes_[index2]) {
2900 in_route_[node] = index1;
2901 routes_[index1].push_back(node);
2903 for (
int dimension_index = 0; dimension_index < dimensions_.size();
2904 ++dimension_index) {
2905 for (
const std::pair<int, int64> new_possible_cumul :
2906 new_possible_cumuls_[dimension_index]) {
2907 cumuls_[dimension_index][new_possible_cumul.first] =
2908 new_possible_cumul.second;
2911 deleted_routes_.insert(index2);
2918 Assignment*
const assignment_;
2920 const bool check_assignment_;
2921 Solver*
const solver_;
2922 const int64 num_indices_;
2923 const std::vector<Link> links_list_;
2924 std::vector<IntVar*> nexts_;
2925 std::vector<const RoutingDimension*> dimensions_;
2926 std::vector<std::vector<int64>>
cumuls_;
2927 std::vector<absl::flat_hash_map<int, int64>> new_possible_cumuls_;
2928 std::vector<std::vector<int>> routes_;
2929 std::vector<int> in_route_;
2930 absl::flat_hash_set<int> deleted_routes_;
2931 std::vector<std::vector<int>> final_routes_;
2932 std::vector<Chain> chains_;
2933 absl::flat_hash_set<int> deleted_chains_;
2934 std::vector<Chain> final_chains_;
2935 std::vector<int> index_to_chain_index_;
2936 std::vector<int> index_to_vehicle_class_index_;
2942 :
index(
index), angle(angle), distance(distance) {}
2963 : coordinates_(2 * points.size(), 0), sectors_(1) {
2964 for (
int64 i = 0; i < points.size(); ++i) {
2965 coordinates_[2 * i] = points[i].first;
2966 coordinates_[2 * i + 1] = points[i].second;
2973 const double pi_rad = 3.14159265;
2975 const int x0 = coordinates_[0];
2976 const int y0 = coordinates_[1];
2978 std::vector<SweepIndex> sweep_indices;
2979 for (
int64 index = 0; index < static_cast<int>(coordinates_.size()) / 2;
2981 const int x = coordinates_[2 *
index];
2982 const int y = coordinates_[2 *
index + 1];
2983 const double x_delta = x - x0;
2984 const double y_delta = y - y0;
2985 double square_distance = x_delta * x_delta + y_delta * y_delta;
2986 double angle = square_distance == 0 ? 0 : std::atan2(y_delta, x_delta);
2987 angle = angle >= 0 ? angle : 2 * pi_rad + angle;
2989 sweep_indices.push_back(sweep_index);
2991 std::sort(sweep_indices.begin(), sweep_indices.end(),
2994 const int size =
static_cast<int>(sweep_indices.size()) / sectors_;
2995 for (
int sector = 0; sector < sectors_; ++sector) {
2996 std::vector<SweepIndex> cluster;
2997 std::vector<SweepIndex>::iterator begin =
2998 sweep_indices.begin() + sector * size;
2999 std::vector<SweepIndex>::iterator end =
3000 sector == sectors_ - 1 ? sweep_indices.end()
3001 : sweep_indices.begin() + (sector + 1) * size;
3004 for (
const SweepIndex& sweep_index : sweep_indices) {
3005 indices->push_back(sweep_index.index);
3015 : model_(
model), check_assignment_(check_assignment) {}
3024 route_constructor_ = absl::make_unique<RouteConstructor>(
3025 assignment, model_, check_assignment_, num_indices_, links_);
3027 route_constructor_->Construct();
3028 route_constructor_.reset(
nullptr);
3037 const int depot = model_->
GetDepot();
3039 if (absl::GetFlag(FLAGS_sweep_sectors) > 0 &&
3040 absl::GetFlag(FLAGS_sweep_sectors) < num_indices_) {
3043 std::vector<int64> indices;
3045 for (
int i = 0; i < indices.size() - 1; ++i) {
3046 const int64 first = indices[i];
3047 const int64 second = indices[i + 1];
3048 if ((model_->
IsStart(first) || !model_->
IsEnd(first)) &&
3050 if (first != depot && second != depot) {
3051 Link link(std::make_pair(first, second), 0, 0, depot, depot);
3052 links_.push_back(link);
3058 RoutingModel*
const model_;
3059 std::unique_ptr<RouteConstructor> route_constructor_;
3060 const bool check_assignment_;
3062 std::vector<Link> links_;
3070 class AllUnperformed :
public DecisionBuilder {
3073 explicit AllUnperformed(RoutingModel*
const model) : model_(
model) {}
3074 ~AllUnperformed()
override {}
3075 Decision* Next(Solver*
const solver)
override {
3078 model_->CostVar()->FreezeQueue();
3079 for (
int i = 0; i < model_->Size(); ++i) {
3080 if (!model_->IsStart(i)) {
3081 model_->ActiveVar(i)->SetValue(0);
3084 model_->CostVar()->UnfreezeQueue();
3089 RoutingModel*
const model_;
3094 monitors_.push_back(monitor);
3100 AtSolutionCallbackMonitor(
Solver* solver, std::function<
void()>
callback)
3102 bool AtSolution()
override {
3108 std::function<void()> callback_;
3114 new AtSolutionCallbackMonitor(solver_.get(), std::move(
callback))));
3124 std::vector<const Assignment*>* solutions) {
3129 absl::Duration GetTimeLimit(
const RoutingSearchParameters&
parameters) {
3130 if (!
parameters.has_time_limit())
return absl::InfiniteDuration();
3134 absl::Duration GetLnsTimeLimit(
const RoutingSearchParameters&
parameters) {
3135 if (!
parameters.has_lns_time_limit())
return absl::InfiniteDuration();
3142 void MakeAllUnperformed(
const RoutingModel*
model, Assignment* assignment) {
3143 assignment->Clear();
3144 for (
int i = 0; i <
model->Nexts().size(); ++i) {
3145 if (!
model->IsStart(i)) {
3146 assignment->Add(
model->NextVar(i))->SetValue(i);
3149 for (
int vehicle = 0; vehicle <
model->vehicles(); ++vehicle) {
3150 assignment->Add(
model->NextVar(
model->Start(vehicle)))
3151 ->SetValue(
model->End(vehicle));
3156 bool RoutingModel::AppendAssignmentIfFeasible(
3157 const Assignment& assignment,
3158 std::vector<std::unique_ptr<Assignment>>* assignments) {
3160 solver_->Solve(restore_tmp_assignment_, collect_one_assignment_,
3161 GetOrCreateLimit());
3163 assignments->push_back(absl::make_unique<Assignment>(solver_.get()));
3164 assignments->back()->Copy(collect_one_assignment_->
solution(0));
3170 void RoutingModel::LogSolution(
const RoutingSearchParameters&
parameters,
3171 const std::string& description,
3174 const double cost_scaling_factor =
parameters.log_cost_scaling_factor();
3175 const double cost_offset =
parameters.log_cost_offset();
3176 const std::string cost_string =
3177 cost_scaling_factor == 1.0 && cost_offset == 0.0
3178 ? absl::StrCat(solution_cost)
3180 "%d (%.8lf)", solution_cost,
3181 cost_scaling_factor * (solution_cost + cost_offset));
3183 "%s (%s, time = %d ms, memory used = %s)", description, cost_string,
3184 solver_->wall_time() - start_time_ms, memory_str);
3189 std::vector<const Assignment*>* solutions) {
3190 const int64 start_time_ms = solver_->wall_time();
3193 if (solutions !=
nullptr) solutions->clear();
3207 const int time_limit_shares = 1 + !global_dimension_optimizers_.empty() +
3208 !local_dimension_optimizers_.empty();
3209 const absl::Duration first_solution_lns_time_limit =
3212 first_solution_lns_limit_->
UpdateLimits(first_solution_lns_time_limit,
3215 std::vector<std::unique_ptr<Assignment>> solution_pool;
3217 if (
nullptr == assignment) {
3218 bool solution_found =
false;
3221 AppendAssignmentIfFeasible(matching, &solution_pool)) {
3223 LogSolution(
parameters,
"Min-Cost Flow Solution",
3224 solution_pool.back()->ObjectiveValue(), start_time_ms);
3226 solution_found =
true;
3228 if (!solution_found) {
3232 MakeAllUnperformed(
this, &unperformed);
3233 if (AppendAssignmentIfFeasible(unperformed, &solution_pool) &&
3235 LogSolution(
parameters,
"All Unperformed Solution",
3236 solution_pool.back()->ObjectiveValue(), start_time_ms);
3238 const absl::Duration elapsed_time =
3239 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3240 const absl::Duration time_left =
3242 if (time_left >= absl::ZeroDuration()) {
3246 solver_->Solve(solve_db_, monitors_);
3251 solver_->Solve(improve_db_, monitors_);
3256 const int solution_count = collect_assignments_->
solution_count();
3258 solution_count >= 1 ? collect_assignments_->
solution(solution_count - 1)
3262 AppendAssignmentIfFeasible(sat_solution, &solution_pool) &&
3264 LogSolution(
parameters,
"SAT", solution_pool.back()->ObjectiveValue(),
3269 const absl::Duration elapsed_time =
3270 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3271 const int solution_count = collect_assignments_->
solution_count();
3272 if (solution_count >= 1 || !solution_pool.empty()) {
3274 if (solutions !=
nullptr) {
3275 for (
int i = 0; i < solution_count; ++i) {
3276 solutions->push_back(
3277 solver_->MakeAssignment(collect_assignments_->
solution(i)));
3279 for (
const auto& solution : solution_pool) {
3280 if (solutions->empty() ||
3281 solution->ObjectiveValue() < solutions->back()->ObjectiveValue()) {
3282 solutions->push_back(solver_->MakeAssignment(solution.get()));
3285 return solutions->back();
3288 solution_count >= 1 ? collect_assignments_->
solution(solution_count - 1)
3290 for (
const auto& solution : solution_pool) {
3291 if (best_assignment ==
nullptr ||
3293 best_assignment = solution.get();
3296 return solver_->MakeAssignment(best_assignment);
3298 if (elapsed_time >= GetTimeLimit(
parameters)) {
3310 const int size =
Size();
3316 source_model->
Nexts());
3318 std::vector<IntVar*> source_vars(size + size + vehicles_);
3319 std::vector<IntVar*> target_vars(size + size + vehicles_);
3329 source_assignment, source_vars);
3347 LOG(
WARNING) <<
"Non-closed model not supported.";
3351 LOG(
WARNING) <<
"Non-homogeneous vehicle costs not supported";
3354 if (!disjunctions_.
empty()) {
3356 <<
"Node disjunction constraints or optional nodes not supported.";
3359 const int num_nodes =
Size() + vehicles_;
3366 std::unique_ptr<IntVarIterator> iterator(
3367 nexts_[
tail]->MakeDomainIterator(
false));
3390 return linear_sum_assignment.
GetCost();
3395 bool RoutingModel::RouteCanBeUsedByVehicle(
const Assignment& assignment,
3396 int start_index,
int vehicle)
const {
3398 IsStart(start_index) ?
Next(assignment, start_index) : start_index;
3399 while (!
IsEnd(current_index)) {
3401 if (!vehicle_var->
Contains(vehicle)) {
3404 const int next_index =
Next(assignment, current_index);
3405 CHECK_NE(next_index, current_index) <<
"Inactive node inside a route";
3406 current_index = next_index;
3411 bool RoutingModel::ReplaceUnusedVehicle(
3412 int unused_vehicle,
int active_vehicle,
3413 Assignment*
const compact_assignment)
const {
3414 CHECK(compact_assignment !=
nullptr);
3418 const int unused_vehicle_start =
Start(unused_vehicle);
3419 IntVar*
const unused_vehicle_start_var =
NextVar(unused_vehicle_start);
3420 const int unused_vehicle_end =
End(unused_vehicle);
3421 const int active_vehicle_start =
Start(active_vehicle);
3422 const int active_vehicle_end =
End(active_vehicle);
3423 IntVar*
const active_vehicle_start_var =
NextVar(active_vehicle_start);
3424 const int active_vehicle_next =
3425 compact_assignment->Value(active_vehicle_start_var);
3426 compact_assignment->SetValue(unused_vehicle_start_var, active_vehicle_next);
3427 compact_assignment->SetValue(active_vehicle_start_var,
End(active_vehicle));
3430 int current_index = active_vehicle_next;
3431 while (!
IsEnd(current_index)) {
3432 IntVar*
const vehicle_var =
VehicleVar(current_index);
3433 compact_assignment->SetValue(vehicle_var, unused_vehicle);
3434 const int next_index =
Next(*compact_assignment, current_index);
3435 if (
IsEnd(next_index)) {
3436 IntVar*
const last_next_var =
NextVar(current_index);
3437 compact_assignment->SetValue(last_next_var,
End(unused_vehicle));
3439 current_index = next_index;
3444 const std::vector<IntVar*>& transit_variables = dimension->transits();
3445 IntVar*
const unused_vehicle_transit_var =
3446 transit_variables[unused_vehicle_start];
3447 IntVar*
const active_vehicle_transit_var =
3448 transit_variables[active_vehicle_start];
3449 const bool contains_unused_vehicle_transit_var =
3450 compact_assignment->Contains(unused_vehicle_transit_var);
3451 const bool contains_active_vehicle_transit_var =
3452 compact_assignment->Contains(active_vehicle_transit_var);
3453 if (contains_unused_vehicle_transit_var !=
3454 contains_active_vehicle_transit_var) {
3456 LOG(
INFO) <<
"The assignment contains transit variable for dimension '"
3457 << dimension->name() <<
"' for some vehicles, but not for all";
3460 if (contains_unused_vehicle_transit_var) {
3461 const int64 old_unused_vehicle_transit =
3462 compact_assignment->Value(unused_vehicle_transit_var);
3463 const int64 old_active_vehicle_transit =
3464 compact_assignment->Value(active_vehicle_transit_var);
3465 compact_assignment->SetValue(unused_vehicle_transit_var,
3466 old_active_vehicle_transit);
3467 compact_assignment->SetValue(active_vehicle_transit_var,
3468 old_unused_vehicle_transit);
3472 const std::vector<IntVar*>& cumul_variables = dimension->cumuls();
3473 IntVar*
const unused_vehicle_cumul_var =
3474 cumul_variables[unused_vehicle_end];
3475 IntVar*
const active_vehicle_cumul_var =
3476 cumul_variables[active_vehicle_end];
3477 const int64 old_unused_vehicle_cumul =
3478 compact_assignment->Value(unused_vehicle_cumul_var);
3479 const int64 old_active_vehicle_cumul =
3480 compact_assignment->Value(active_vehicle_cumul_var);
3481 compact_assignment->SetValue(unused_vehicle_cumul_var,
3482 old_active_vehicle_cumul);
3483 compact_assignment->SetValue(active_vehicle_cumul_var,
3484 old_unused_vehicle_cumul);
3491 return CompactAssignmentInternal(assignment,
false);
3496 return CompactAssignmentInternal(assignment,
true);
3499 Assignment* RoutingModel::CompactAssignmentInternal(
3500 const Assignment& assignment,
bool check_compact_assignment)
const {
3504 <<
"The costs are not homogeneous, routes cannot be rearranged";
3508 std::unique_ptr<Assignment> compact_assignment(
new Assignment(&assignment));
3509 for (
int vehicle = 0; vehicle < vehicles_ - 1; ++vehicle) {
3513 const int vehicle_start =
Start(vehicle);
3514 const int vehicle_end =
End(vehicle);
3516 int swap_vehicle = vehicles_ - 1;
3517 bool has_more_vehicles_with_route =
false;
3518 for (; swap_vehicle > vehicle; --swap_vehicle) {
3525 has_more_vehicles_with_route =
true;
3526 const int swap_vehicle_start =
Start(swap_vehicle);
3527 const int swap_vehicle_end =
End(swap_vehicle);
3536 if (RouteCanBeUsedByVehicle(*compact_assignment, swap_vehicle_start,
3542 if (swap_vehicle == vehicle) {
3543 if (has_more_vehicles_with_route) {
3547 LOG(
INFO) <<
"No vehicle that can be swapped with " << vehicle
3554 if (!ReplaceUnusedVehicle(vehicle, swap_vehicle,
3555 compact_assignment.get())) {
3560 if (check_compact_assignment &&
3561 !solver_->CheckAssignment(compact_assignment.get())) {
3563 LOG(
WARNING) <<
"The compacted assignment is not a valid solution";
3566 return compact_assignment.release();
3569 int RoutingModel::FindNextActive(
int index,
3570 const std::vector<int64>& indices)
const {
3573 const int size = indices.size();
3584 preassignment_->
Clear();
3585 IntVar* next_var =
nullptr;
3586 int lock_index = FindNextActive(-1, locks);
3587 const int size = locks.size();
3588 if (lock_index < size) {
3589 next_var =
NextVar(locks[lock_index]);
3590 preassignment_->
Add(next_var);
3591 for (lock_index = FindNextActive(lock_index, locks); lock_index < size;
3592 lock_index = FindNextActive(lock_index, locks)) {
3593 preassignment_->
SetValue(next_var, locks[lock_index]);
3594 next_var =
NextVar(locks[lock_index]);
3595 preassignment_->
Add(next_var);
3602 const std::vector<std::vector<int64>>& locks,
bool close_routes) {
3603 preassignment_->
Clear();
3608 const RoutingSearchParameters&
parameters)
const {
3610 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3616 const RoutingSearchParameters&
parameters)
const {
3618 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3624 if (collect_assignments_->
solution_count() == 1 && assignment_ !=
nullptr) {
3626 return assignment_->
Save(file_name);
3634 CHECK(assignment_ !=
nullptr);
3635 if (assignment_->
Load(file_name)) {
3636 return DoRestoreAssignment();
3643 CHECK(assignment_ !=
nullptr);
3645 return DoRestoreAssignment();
3648 Assignment* RoutingModel::DoRestoreAssignment() {
3652 solver_->Solve(restore_assignment_, monitors_);
3655 return collect_assignments_->
solution(0);
3664 const std::vector<std::vector<int64>>& routes,
bool ignore_inactive_indices,
3665 bool close_routes,
Assignment*
const assignment)
const {
3666 CHECK(assignment !=
nullptr);
3668 LOG(
ERROR) <<
"The model is not closed yet";
3671 const int num_routes = routes.size();
3672 if (num_routes > vehicles_) {
3673 LOG(
ERROR) <<
"The number of vehicles in the assignment (" << routes.size()
3674 <<
") is greater than the number of vehicles in the model ("
3675 << vehicles_ <<
")";
3679 absl::flat_hash_set<int> visited_indices;
3681 for (
int vehicle = 0; vehicle < num_routes; ++vehicle) {
3682 const std::vector<int64>& route = routes[vehicle];
3683 int from_index =
Start(vehicle);
3684 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3685 visited_indices.insert(from_index);
3686 if (!insert_result.second) {
3687 LOG(
ERROR) <<
"Index " << from_index <<
" (start node for vehicle "
3688 << vehicle <<
") was already used";
3692 for (
const int64 to_index : route) {
3693 if (to_index < 0 || to_index >=
Size()) {
3694 LOG(
ERROR) <<
"Invalid index: " << to_index;
3699 if (active_var->
Max() == 0) {
3700 if (ignore_inactive_indices) {
3703 LOG(
ERROR) <<
"Index " << to_index <<
" is not active";
3708 insert_result = visited_indices.insert(to_index);
3709 if (!insert_result.second) {
3710 LOG(
ERROR) <<
"Index " << to_index <<
" is used multiple times";
3715 if (!vehicle_var->
Contains(vehicle)) {
3716 LOG(
ERROR) <<
"Vehicle " << vehicle <<
" is not allowed at index "
3722 if (!assignment->
Contains(from_var)) {
3723 assignment->
Add(from_var);
3725 assignment->
SetValue(from_var, to_index);
3727 from_index = to_index;
3732 if (!assignment->
Contains(last_var)) {
3733 assignment->
Add(last_var);
3740 for (
int vehicle = num_routes; vehicle < vehicles_; ++vehicle) {
3741 const int start_index =
Start(vehicle);
3744 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3745 visited_indices.insert(start_index);
3746 if (!insert_result.second) {
3747 LOG(
ERROR) <<
"Index " << start_index <<
" is used multiple times";
3752 if (!assignment->
Contains(start_var)) {
3753 assignment->
Add(start_var);
3764 if (!assignment->
Contains(next_var)) {
3765 assignment->
Add(next_var);
3776 const std::vector<std::vector<int64>>& routes,
3777 bool ignore_inactive_indices) {
3785 return DoRestoreAssignment();
3790 std::vector<std::vector<int64>>*
const routes)
const {
3792 CHECK(routes !=
nullptr);
3794 const int model_size =
Size();
3795 routes->resize(vehicles_);
3796 for (
int vehicle = 0; vehicle < vehicles_; ++vehicle) {
3797 std::vector<int64>*
const vehicle_route = &routes->at(vehicle);
3798 vehicle_route->clear();
3800 int num_visited_indices = 0;
3801 const int first_index =
Start(vehicle);
3805 int current_index = assignment.
Value(first_var);
3806 while (!
IsEnd(current_index)) {
3807 vehicle_route->push_back(current_index);
3812 current_index = assignment.
Value(next_var);
3814 ++num_visited_indices;
3815 CHECK_LE(num_visited_indices, model_size)
3816 <<
"The assignment contains a cycle";
3824 std::vector<std::vector<int64>> route_indices(
vehicles());
3825 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3827 LOG(DFATAL) <<
"GetRoutesFromAssignment() called on incomplete solution:"
3828 <<
" NextVar(" << vehicle <<
") is unbound.";
3831 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3833 route_indices[vehicle].push_back(
index);
3836 route_indices[vehicle].push_back(
index);
3839 return route_indices;
3843 int64 RoutingModel::GetArcCostForClassInternal(
3844 int64 from_index,
int64 to_index, CostClassIndex cost_class_index)
const {
3847 DCHECK_LT(cost_class_index, cost_classes_.size());
3848 CostCacheElement*
const cache = &cost_cache_[from_index];
3850 if (cache->index ==
static_cast<int>(to_index) &&
3851 cache->cost_class_index == cost_class_index) {
3855 const CostClass& cost_class = cost_classes_[cost_class_index];
3856 const auto& evaluator = transit_evaluators_[cost_class.evaluator_index];
3858 cost =
CapAdd(evaluator(from_index, to_index),
3859 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3860 }
else if (!
IsEnd(to_index)) {
3864 evaluator(from_index, to_index),
3865 CapAdd(GetDimensionTransitCostSum(from_index, to_index, cost_class),
3866 fixed_cost_of_vehicle_[index_to_vehicle_[from_index]]));
3870 if (consider_empty_route_costs_[index_to_vehicle_[from_index]]) {
3872 CapAdd(evaluator(from_index, to_index),
3873 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3878 *cache = {
static_cast<int>(to_index), cost_class_index,
cost};
3887 int vehicle)
const {
3901 return assignment.
Value(next_var);
3905 int64 vehicle)
const {
3906 if (from_index != to_index && vehicle >= 0) {
3907 return GetArcCostForClassInternal(from_index, to_index,
3916 int64 cost_class_index)
const {
3917 if (from_index != to_index) {
3918 return GetArcCostForClassInternal(from_index, to_index,
3926 int64 to_index)
const {
3930 if (!is_bound_to_end_ct_added_.
Switched()) {
3933 std::vector<IntVar*> zero_transit(
Size(), solver_->MakeIntConst(0));
3934 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
3935 nexts_, active_, is_bound_to_end_, zero_transit));
3936 is_bound_to_end_ct_added_.
Switch(solver_.get());
3938 if (is_bound_to_end_[to_index]->Min() == 1)
return kint64max;
3943 int64 RoutingModel::GetDimensionTransitCostSum(
3944 int64 i,
int64 j,
const CostClass& cost_class)
const {
3946 for (
const auto& evaluator_and_coefficient :
3947 cost_class.dimension_transit_evaluator_class_and_cost_coefficient) {
3948 DCHECK_GT(evaluator_and_coefficient.cost_coefficient, 0);
3951 CapProd(evaluator_and_coefficient.cost_coefficient,
3952 evaluator_and_coefficient.dimension->GetTransitValueFromClass(
3953 i, j, evaluator_and_coefficient.transit_evaluator_class)));
3969 const bool mandatory1 = active_[to1]->Min() == 1;
3970 const bool mandatory2 = active_[to2]->Min() == 1;
3972 if (mandatory1 != mandatory2)
return mandatory1;
3979 const int64 src_vehicle = src_vehicle_var->
Max();
3980 if (src_vehicle_var->
Bound()) {
3987 mandatory1 ? to1_vehicle_var->
Bound() : (to1_vehicle_var->
Size() <= 2);
3989 mandatory2 ? to2_vehicle_var->
Bound() : (to2_vehicle_var->
Size() <= 2);
3992 if (bound1 != bound2)
return bound1;
3995 const int64 vehicle1 = to1_vehicle_var->
Max();
3996 const int64 vehicle2 = to2_vehicle_var->
Max();
3999 if ((vehicle1 == src_vehicle) != (vehicle2 == src_vehicle)) {
4000 return vehicle1 == src_vehicle;
4005 if (vehicle1 != src_vehicle)
return to1 < to2;
4016 const std::vector<IntVar*>& cumul_vars =
4018 IntVar*
const dim1 = cumul_vars[to1];
4019 IntVar*
const dim2 = cumul_vars[to2];
4022 if (dim1->
Max() != dim2->
Max())
return dim1->
Max() < dim2->
Max();
4031 const int64 cost_class_index =
4032 SafeGetCostClassInt64OfVehicle(src_vehicle);
4037 if (cost1 != cost2)
return cost1 < cost2;
4044 if (num_vehicles1 != num_vehicles2)
return num_vehicles1 < num_vehicles2;
4053 DCHECK_EQ(index_to_visit_type_.size(), index_to_type_policy_.size());
4054 index_to_visit_type_[
index] = type;
4055 index_to_type_policy_[
index] = policy;
4056 num_visit_types_ =
std::max(num_visit_types_, type + 1);
4061 return index_to_visit_type_[
index];
4065 DCHECK_LT(type, single_nodes_of_type_.size());
4066 return single_nodes_of_type_[type];
4070 DCHECK_LT(type, pair_indices_of_type_.size());
4071 return pair_indices_of_type_[type];
4077 return index_to_type_policy_[
index];
4081 hard_incompatible_types_per_type_index_.resize(num_visit_types_);
4082 temporal_incompatible_types_per_type_index_.resize(num_visit_types_);
4083 same_vehicle_required_type_alternatives_per_type_index_.resize(
4085 required_type_alternatives_when_adding_type_index_.resize(num_visit_types_);
4086 required_type_alternatives_when_removing_type_index_.resize(num_visit_types_);
4091 hard_incompatible_types_per_type_index_.size());
4092 has_hard_type_incompatibilities_ =
true;
4094 hard_incompatible_types_per_type_index_[type1].insert(type2);
4095 hard_incompatible_types_per_type_index_[type2].insert(type1);
4100 temporal_incompatible_types_per_type_index_.size());
4101 has_temporal_type_incompatibilities_ =
true;
4103 temporal_incompatible_types_per_type_index_[type1].insert(type2);
4104 temporal_incompatible_types_per_type_index_[type2].insert(type1);
4107 const absl::flat_hash_set<int>&
4110 DCHECK_LT(type, hard_incompatible_types_per_type_index_.size());
4111 return hard_incompatible_types_per_type_index_[type];
4114 const absl::flat_hash_set<int>&
4117 DCHECK_LT(type, temporal_incompatible_types_per_type_index_.size());
4118 return temporal_incompatible_types_per_type_index_[type];
4124 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4126 same_vehicle_required_type_alternatives_per_type_index_.size());
4128 if (required_type_alternatives.empty()) {
4132 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4133 trivially_infeasible_visit_types_to_policies_[dependent_type];
4140 has_same_vehicle_type_requirements_ =
true;
4141 same_vehicle_required_type_alternatives_per_type_index_[dependent_type]
4142 .push_back(std::move(required_type_alternatives));
4146 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4148 required_type_alternatives_when_adding_type_index_.size());
4150 if (required_type_alternatives.empty()) {
4154 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4155 trivially_infeasible_visit_types_to_policies_[dependent_type];
4161 has_temporal_type_requirements_ =
true;
4162 required_type_alternatives_when_adding_type_index_[dependent_type].push_back(
4163 std::move(required_type_alternatives));
4167 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4169 required_type_alternatives_when_removing_type_index_.size());
4171 if (required_type_alternatives.empty()) {
4175 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4176 trivially_infeasible_visit_types_to_policies_[dependent_type];
4183 has_temporal_type_requirements_ =
true;
4184 required_type_alternatives_when_removing_type_index_[dependent_type]
4185 .push_back(std::move(required_type_alternatives));
4188 const std::vector<absl::flat_hash_set<int>>&
4192 same_vehicle_required_type_alternatives_per_type_index_.size());
4193 return same_vehicle_required_type_alternatives_per_type_index_[type];
4196 const std::vector<absl::flat_hash_set<int>>&
4199 DCHECK_LT(type, required_type_alternatives_when_adding_type_index_.size());
4200 return required_type_alternatives_when_adding_type_index_[type];
4203 const std::vector<absl::flat_hash_set<int>>&
4206 DCHECK_LT(type, required_type_alternatives_when_removing_type_index_.size());
4207 return required_type_alternatives_when_removing_type_index_[type];
4215 int64 var_index)
const {
4216 if (active_[var_index]->Min() == 1)
return kint64max;
4217 const std::vector<DisjunctionIndex>& disjunction_indices =
4219 if (disjunction_indices.size() != 1)
return default_value;
4224 return std::max(
int64{0}, disjunctions_[disjunction_index].value.penalty);
4229 const std::string& dimension_to_print)
const {
4230 for (
int i = 0; i <
Size(); ++i) {
4233 <<
"DebugOutputVehicleSchedules() called on incomplete solution:"
4234 <<
" NextVar(" << i <<
") is unbound.";
4239 absl::flat_hash_set<std::string> dimension_names;
4240 if (dimension_to_print.empty()) {
4242 dimension_names.insert(all_dimension_names.begin(),
4243 all_dimension_names.end());
4245 dimension_names.insert(dimension_to_print);
4247 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4248 int empty_vehicle_range_start = vehicle;
4253 if (empty_vehicle_range_start != vehicle) {
4254 if (empty_vehicle_range_start == vehicle - 1) {
4255 absl::StrAppendFormat(&output,
"Vehicle %d: empty",
4256 empty_vehicle_range_start);
4258 absl::StrAppendFormat(&output,
"Vehicles %d-%d: empty",
4259 empty_vehicle_range_start, vehicle - 1);
4261 output.append(
"\n");
4264 absl::StrAppendFormat(&output,
"Vehicle %d:", vehicle);
4268 absl::StrAppendFormat(&output,
"%d Vehicle(%d) ",
index,
4269 solution_assignment.
Value(vehicle_var));
4273 absl::StrAppendFormat(&output,
"%s(%d..%d) ", dimension->name(),
4274 solution_assignment.
Min(
var),
4275 solution_assignment.
Max(
var));
4280 if (
IsEnd(
index)) output.append(
"Route end ");
4282 output.append(
"\n");
4285 output.append(
"Unperformed nodes: ");
4286 bool has_unperformed =
false;
4287 for (
int i = 0; i <
Size(); ++i) {
4290 absl::StrAppendFormat(&output,
"%d ", i);
4291 has_unperformed =
true;
4294 if (!has_unperformed) output.append(
"None");
4295 output.append(
"\n");
4302 std::vector<std::vector<std::pair<int64, int64>>> cumul_bounds(
vehicles());
4303 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4305 LOG(DFATAL) <<
"GetCumulBounds() called on incomplete solution:"
4306 <<
" NextVar(" << vehicle <<
") is unbound.";
4310 for (
int vehicle_id = 0; vehicle_id <
vehicles(); ++vehicle_id) {
4313 cumul_bounds[vehicle_id].emplace_back(solution_assignment.
Min(dim_var),
4314 solution_assignment.
Max(dim_var));
4318 cumul_bounds[vehicle_id].emplace_back(solution_assignment.
Min(dim_var),
4319 solution_assignment.
Max(dim_var));
4322 return cumul_bounds;
4326 Assignment* RoutingModel::GetOrCreateAssignment() {
4327 if (assignment_ ==
nullptr) {
4328 assignment_ = solver_->MakeAssignment();
4329 assignment_->
Add(nexts_);
4331 assignment_->
Add(vehicle_vars_);
4338 Assignment* RoutingModel::GetOrCreateTmpAssignment() {
4339 if (tmp_assignment_ ==
nullptr) {
4340 tmp_assignment_ = solver_->MakeAssignment();
4341 tmp_assignment_->
Add(nexts_);
4343 return tmp_assignment_;
4346 RegularLimit* RoutingModel::GetOrCreateLimit() {
4347 if (limit_ ==
nullptr) {
4354 RegularLimit* RoutingModel::GetOrCreateLocalSearchLimit() {
4355 if (ls_limit_ ==
nullptr) {
4363 RegularLimit* RoutingModel::GetOrCreateLargeNeighborhoodSearchLimit() {
4364 if (lns_limit_ ==
nullptr) {
4373 RoutingModel::GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit() {
4374 if (first_solution_lns_limit_ ==
nullptr) {
4375 first_solution_lns_limit_ =
4379 return first_solution_lns_limit_;
4382 LocalSearchOperator* RoutingModel::CreateInsertionOperator() {
4383 LocalSearchOperator* insertion_operator =
4384 CreateCPOperator<MakeActiveOperator>();
4385 if (!pickup_delivery_pairs_.empty()) {
4386 insertion_operator = solver_->ConcatenateOperators(
4387 {CreatePairOperator<MakePairActiveOperator>(), insertion_operator});
4389 if (!implicit_pickup_delivery_pairs_without_alternatives_.empty()) {
4390 insertion_operator = solver_->ConcatenateOperators(
4391 {CreateOperator<MakePairActiveOperator>(
4392 implicit_pickup_delivery_pairs_without_alternatives_),
4393 insertion_operator});
4395 return insertion_operator;
4398 LocalSearchOperator* RoutingModel::CreateMakeInactiveOperator() {
4399 LocalSearchOperator* make_inactive_operator =
4400 CreateCPOperator<MakeInactiveOperator>();
4401 if (!pickup_delivery_pairs_.empty()) {
4402 make_inactive_operator = solver_->ConcatenateOperators(
4403 {CreatePairOperator<MakePairInactiveOperator>(),
4404 make_inactive_operator});
4406 return make_inactive_operator;
4409 void RoutingModel::CreateNeighborhoodOperators(
4411 local_search_operators_.clear();
4412 local_search_operators_.resize(LOCAL_SEARCH_OPERATOR_COUNTER,
nullptr);
4416 std::pair<RoutingLocalSearchOperator, Solver::LocalSearchOperators>>
4421 for (
const auto [type, op] : operator_by_type) {
4422 local_search_operators_[type] =
4424 ? solver_->MakeOperator(nexts_, op)
4425 : solver_->MakeOperator(nexts_, vehicle_vars_, op);
4430 const std::vector<std::pair<RoutingLocalSearchOperator,
4432 operator_by_type = {{LIN_KERNIGHAN,
Solver::LK},
4435 for (
const auto [type, op] : operator_by_type) {
4438 local_search_operators_[type] =
4440 ? solver_->MakeOperator(nexts_, std::move(arc_cost), op)
4441 : solver_->MakeOperator(nexts_, vehicle_vars_,
4442 std::move(arc_cost), op);
4447 local_search_operators_[RELOCATE] = CreateCPOperator<Relocate>();
4448 local_search_operators_[EXCHANGE] = CreateCPOperator<Exchange>();
4449 local_search_operators_[CROSS] = CreateCPOperator<Cross>();
4450 local_search_operators_[TWO_OPT] = CreateCPOperator<TwoOpt>();
4451 local_search_operators_[RELOCATE_AND_MAKE_ACTIVE] =
4452 CreateCPOperator<RelocateAndMakeActiveOperator>();
4453 local_search_operators_[MAKE_ACTIVE_AND_RELOCATE] =
4454 CreateCPOperator<MakeActiveAndRelocate>();
4455 local_search_operators_[MAKE_CHAIN_INACTIVE] =
4456 CreateCPOperator<MakeChainInactiveOperator>();
4457 local_search_operators_[SWAP_ACTIVE] = CreateCPOperator<SwapActiveOperator>();
4458 local_search_operators_[EXTENDED_SWAP_ACTIVE] =
4459 CreateCPOperator<ExtendedSwapActiveOperator>();
4462 local_search_operators_[MAKE_ACTIVE] = CreateInsertionOperator();
4463 local_search_operators_[MAKE_INACTIVE] = CreateMakeInactiveOperator();
4464 local_search_operators_[RELOCATE_PAIR] =
4465 CreatePairOperator<PairRelocateOperator>();
4466 std::vector<LocalSearchOperator*> light_relocate_pair_operators;
4467 light_relocate_pair_operators.push_back(
4468 CreatePairOperator<LightPairRelocateOperator>());
4469 local_search_operators_[LIGHT_RELOCATE_PAIR] =
4470 solver_->ConcatenateOperators(light_relocate_pair_operators);
4471 local_search_operators_[EXCHANGE_PAIR] =
4472 CreatePairOperator<PairExchangeOperator>();
4473 local_search_operators_[EXCHANGE_RELOCATE_PAIR] =
4474 CreatePairOperator<PairExchangeRelocateOperator>();
4475 local_search_operators_[RELOCATE_NEIGHBORS] =
4476 CreateOperator<MakeRelocateNeighborsOperator>(
4478 local_search_operators_[NODE_PAIR_SWAP] = solver_->ConcatenateOperators(
4479 {CreatePairOperator<IndexPairSwapActiveOperator>(),
4480 CreatePairOperator<SwapIndexPairOperator>(),
4481 CreatePairOperator<PairNodeSwapActiveOperator<true>>(),
4482 CreatePairOperator<PairNodeSwapActiveOperator<false>>()});
4483 local_search_operators_[RELOCATE_SUBTRIP] =
4484 CreatePairOperator<RelocateSubtrip>();
4485 local_search_operators_[EXCHANGE_SUBTRIP] =
4486 CreatePairOperator<ExchangeSubtrip>();
4488 const auto arc_cost_for_path_start =
4490 const int vehicle = index_to_vehicle_[start_index];
4491 const int64 arc_cost =
4493 return (before_node != start_index ||
IsEnd(after_node))
4497 local_search_operators_[RELOCATE_EXPENSIVE_CHAIN] =
4498 solver_->RevAlloc(
new RelocateExpensiveChain(
4502 vehicle_start_class_callback_,
4503 parameters.relocate_expensive_chain_num_arcs_to_consider(),
4504 arc_cost_for_path_start));
4507 const auto make_global_cheapest_insertion_filtered_heuristic =
4509 using Heuristic = GlobalCheapestInsertionFilteredHeuristic;
4510 Heuristic::GlobalCheapestInsertionParameters ls_gci_parameters = {
4513 parameters.cheapest_insertion_ls_operator_neighbors_ratio(),
4515 parameters.cheapest_insertion_add_unperformed_entries()};
4516 return absl::make_unique<Heuristic>(
4519 GetOrCreateFeasibilityFilterManager(
parameters), ls_gci_parameters);
4521 const auto make_local_cheapest_insertion_filtered_heuristic =
4523 return absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4525 GetOrCreateFeasibilityFilterManager(
parameters));
4527 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4528 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4529 make_global_cheapest_insertion_filtered_heuristic(),
4530 parameters.heuristic_close_nodes_lns_num_nodes()));
4532 local_search_operators_[LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4533 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4534 make_local_cheapest_insertion_filtered_heuristic(),
4535 parameters.heuristic_close_nodes_lns_num_nodes()));
4537 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_PATH_LNS] =
4538 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4539 make_global_cheapest_insertion_filtered_heuristic()));
4541 local_search_operators_[LOCAL_CHEAPEST_INSERTION_PATH_LNS] =
4542 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4543 make_local_cheapest_insertion_filtered_heuristic()));
4545 local_search_operators_
4546 [RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED] =
4548 new RelocatePathAndHeuristicInsertUnperformedOperator(
4549 make_global_cheapest_insertion_filtered_heuristic()));
4551 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4552 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4553 make_global_cheapest_insertion_filtered_heuristic(),
4554 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4555 arc_cost_for_path_start));
4557 local_search_operators_[LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4558 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4559 make_local_cheapest_insertion_filtered_heuristic(),
4560 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4561 arc_cost_for_path_start));
4564 #define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators) \
4565 if (search_parameters.local_search_operators().use_##operator_method() == \
4567 operators.push_back(local_search_operators_[operator_type]); \
4570 LocalSearchOperator* RoutingModel::ConcatenateOperators(
4571 const RoutingSearchParameters& search_parameters,
4572 const std::vector<LocalSearchOperator*>& operators)
const {
4573 if (search_parameters.use_multi_armed_bandit_concatenate_operators()) {
4574 return solver_->MultiArmedBanditConcatenateOperators(
4577 .multi_armed_bandit_compound_operator_memory_coefficient(),
4579 .multi_armed_bandit_compound_operator_exploration_coefficient(),
4582 return solver_->ConcatenateOperators(operators);
4585 LocalSearchOperator* RoutingModel::GetNeighborhoodOperators(
4586 const RoutingSearchParameters& search_parameters)
const {
4587 std::vector<LocalSearchOperator*> operator_groups;
4588 std::vector<LocalSearchOperator*> operators = extra_operators_;
4589 if (!pickup_delivery_pairs_.empty()) {
4593 if (search_parameters.local_search_operators().use_relocate_pair() ==
4603 if (vehicles_ > 1) {
4614 if (!pickup_delivery_pairs_.empty() ||
4615 search_parameters.local_search_operators().use_relocate_neighbors() ==
4617 operators.push_back(local_search_operators_[RELOCATE_NEIGHBORS]);
4620 search_parameters.local_search_metaheuristic();
4621 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4622 local_search_metaheuristic !=
4623 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4624 local_search_metaheuristic !=
4625 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4632 if (!disjunctions_.
empty()) {
4649 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4658 global_cheapest_insertion_path_lns, operators);
4660 local_cheapest_insertion_path_lns, operators);
4662 RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED,
4663 relocate_path_global_cheapest_insertion_insert_unperformed, operators);
4666 global_cheapest_insertion_expensive_chain_lns,
4669 local_cheapest_insertion_expensive_chain_lns,
4672 global_cheapest_insertion_close_nodes_lns,
4675 local_cheapest_insertion_close_nodes_lns, operators);
4676 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4680 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4681 local_search_metaheuristic !=
4682 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4683 local_search_metaheuristic !=
4684 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4687 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4688 local_search_metaheuristic !=
4689 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4690 local_search_metaheuristic !=
4691 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4696 if (!disjunctions_.
empty()) {
4699 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4701 return solver_->ConcatenateOperators(operator_groups);
4704 #undef CP_ROUTING_PUSH_OPERATOR
4708 if (dimension->GetUnaryTransitEvaluator(0) !=
nullptr)
return true;
4715 void ConvertVectorInt64ToVectorInt(
const std::vector<int64>&
input,
4716 std::vector<int>* output) {
4717 const int n =
input.size();
4719 int* data = output->data();
4720 for (
int i = 0; i < n; ++i) {
4721 const int element =
static_cast<int>(
input[i]);
4729 std::vector<LocalSearchFilterManager::FilterEvent>
4730 RoutingModel::GetOrCreateLocalSearchFilters(
4731 const RoutingSearchParameters&
parameters,
bool filter_cost) {
4732 const auto kAccept = LocalSearchFilterManager::FilterEventType::kAccept;
4733 const auto kRelax = LocalSearchFilterManager::FilterEventType::kRelax;
4743 std::vector<LocalSearchFilterManager::FilterEvent> filters;
4745 if (filter_cost && vehicle_amortized_cost_factors_set_) {
4753 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4756 filters.push_back({sum, kAccept});
4758 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4759 nexts_, vehicle_vars_,
4764 filters.push_back({sum, kAccept});
4768 filters.push_back({solver_->MakeVariableDomainFilter(), kAccept});
4770 if (vehicles_ > max_active_vehicles_) {
4774 if (!disjunctions_.
empty()) {
4778 if (!pickup_delivery_pairs_.empty()) {
4781 vehicle_pickup_delivery_policy_),
4791 const PathState* path_state_reference =
nullptr;
4793 std::vector<int> path_starts;
4794 std::vector<int> path_ends;
4795 ConvertVectorInt64ToVectorInt(starts_, &path_starts);
4796 ConvertVectorInt64ToVectorInt(ends_, &path_ends);
4798 auto path_state = absl::make_unique<PathState>(
4799 Size() +
vehicles(), std::move(path_starts), std::move(path_ends));
4800 path_state_reference = path_state.get();
4812 if (!dimension->HasBreakConstraints())
continue;
4815 filters.insert(filters.end(), extra_filters_.begin(), extra_filters_.end());
4819 LocalSearchFilterManager* RoutingModel::GetOrCreateLocalSearchFilterManager(
4821 if (!local_search_filter_manager_) {
4822 local_search_filter_manager_ =
4823 solver_->RevAlloc(
new LocalSearchFilterManager(
4826 return local_search_filter_manager_;
4829 std::vector<LocalSearchFilterManager::FilterEvent>
4830 RoutingModel::GetOrCreateFeasibilityFilters(
4832 return GetOrCreateLocalSearchFilters(
parameters,
false);
4835 LocalSearchFilterManager* RoutingModel::GetOrCreateFeasibilityFilterManager(
4837 if (!feasibility_filter_manager_) {
4838 feasibility_filter_manager_ =
4839 solver_->RevAlloc(
new LocalSearchFilterManager(
4842 return feasibility_filter_manager_;
4845 LocalSearchFilterManager*
4846 RoutingModel::GetOrCreateStrongFeasibilityFilterManager(
4848 if (!strong_feasibility_filter_manager_) {
4849 std::vector<LocalSearchFilterManager::FilterEvent> filters =
4852 LocalSearchFilterManager::FilterEventType::kAccept});
4853 strong_feasibility_filter_manager_ =
4854 solver_->RevAlloc(
new LocalSearchFilterManager(std::move(filters)));
4856 return strong_feasibility_filter_manager_;
4860 bool AllTransitsPositive(
const RoutingDimension& dimension) {
4861 for (
int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
4862 if (!dimension.AreVehicleTransitsPositive(vehicle)) {
4870 void RoutingModel::StoreDimensionCumulOptimizers(
4872 Assignment* packed_dimensions_collector_assignment =
4873 solver_->MakeAssignment();
4874 packed_dimensions_collector_assignment->AddObjective(
CostVar());
4875 const int num_dimensions = dimensions_.size();
4876 local_optimizer_index_.
resize(num_dimensions, -1);
4877 global_optimizer_index_.
resize(num_dimensions, -1);
4880 if (dimension->global_span_cost_coefficient() > 0 ||
4881 !dimension->GetNodePrecedences().empty()) {
4883 global_optimizer_index_[dim] = global_dimension_optimizers_.size();
4884 global_dimension_optimizers_.push_back(
4885 absl::make_unique<GlobalDimensionCumulOptimizer>(dimension));
4886 packed_dimensions_collector_assignment->Add(dimension->cumuls());
4887 if (!AllTransitsPositive(*dimension)) {
4888 dimension->SetOffsetForGlobalOptimizer(0);
4892 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4895 std::min(offset, dimension->CumulVar(
Start(vehicle))->Min() - 1);
4897 dimension->SetOffsetForGlobalOptimizer(
std::max(
Zero(), offset));
4899 bool has_span_cost =
false;
4900 bool has_span_limit =
false;
4901 std::vector<int64> vehicle_offsets(
vehicles());
4902 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4903 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4904 has_span_cost =
true;
4906 if (dimension->GetSpanUpperBoundForVehicle(vehicle) <
kint64max) {
4907 has_span_limit =
true;
4910 vehicle_offsets[vehicle] =
4911 dimension->AreVehicleTransitsPositive(vehicle)
4913 dimension->CumulVar(
Start(vehicle))->Min() - 1)
4916 bool has_soft_lower_bound =
false;
4917 bool has_soft_upper_bound =
false;
4918 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
4919 if (dimension->HasCumulVarSoftLowerBound(i)) {
4920 has_soft_lower_bound =
true;
4922 if (dimension->HasCumulVarSoftUpperBound(i)) {
4923 has_soft_upper_bound =
true;
4926 int num_linear_constraints = 0;
4927 if (has_span_cost) ++num_linear_constraints;
4928 if (has_span_limit) ++num_linear_constraints;
4929 if (dimension->HasSoftSpanUpperBounds()) ++num_linear_constraints;
4930 if (has_soft_lower_bound) ++num_linear_constraints;
4931 if (has_soft_upper_bound) ++num_linear_constraints;
4932 if (dimension->HasBreakConstraints()) ++num_linear_constraints;
4933 if (num_linear_constraints >= 2) {
4934 dimension->SetVehicleOffsetsForLocalOptimizer(
4935 std::move(vehicle_offsets));
4936 local_optimizer_index_[dim] = local_dimension_optimizers_.size();
4937 local_dimension_optimizers_.push_back(
4938 absl::make_unique<LocalDimensionCumulOptimizer>(
4939 dimension,
parameters.continuous_scheduling_solver()));
4940 bool has_intervals =
false;
4941 for (
const SortedDisjointIntervalList& intervals :
4942 dimension->forbidden_intervals()) {
4945 if (intervals.NumIntervals() > 0) {
4946 has_intervals =
true;
4950 if (dimension->HasBreakConstraints() || has_intervals) {
4951 local_dimension_mp_optimizers_.push_back(
4952 absl::make_unique<LocalDimensionCumulOptimizer>(
4953 dimension,
parameters.mixed_integer_scheduling_solver()));
4955 local_dimension_mp_optimizers_.push_back(
nullptr);
4957 packed_dimensions_collector_assignment->Add(dimension->cumuls());
4960 DCHECK_EQ(local_dimension_mp_optimizers_.size(),
4961 local_dimension_optimizers_.size());
4967 for (IntVar*
const extra_var : extra_vars_) {
4968 packed_dimensions_collector_assignment->Add(extra_var);
4970 for (IntervalVar*
const extra_interval : extra_intervals_) {
4971 packed_dimensions_collector_assignment->Add(extra_interval);
4974 packed_dimensions_assignment_collector_ = solver_->MakeFirstSolutionCollector(
4975 packed_dimensions_collector_assignment);
4980 std::vector<RoutingDimension*> dimensions;
4982 bool has_soft_or_span_cost =
false;
4983 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4984 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4985 has_soft_or_span_cost =
true;
4989 if (!has_soft_or_span_cost) {
4990 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
4991 if (dimension->HasCumulVarSoftUpperBound(i) ||
4992 dimension->HasCumulVarSoftLowerBound(i)) {
4993 has_soft_or_span_cost =
true;
4998 if (has_soft_or_span_cost) dimensions.push_back(dimension);
5004 RoutingModel::CreateFinalizerForMinimizedAndMaximizedVariables() {
5005 std::stable_sort(finalizer_variable_cost_pairs_.begin(),
5006 finalizer_variable_cost_pairs_.end(),
5007 [](
const std::pair<IntVar*, int64>& var_cost1,
5008 const std::pair<IntVar*, int64>& var_cost2) {
5009 return var_cost1.second > var_cost2.second;
5011 const int num_variables = finalizer_variable_cost_pairs_.size() +
5012 finalizer_variable_target_pairs_.size();
5013 std::vector<IntVar*> variables;
5014 std::vector<int64> targets;
5015 variables.reserve(num_variables);
5016 targets.reserve(num_variables);
5017 for (
const auto& variable_cost : finalizer_variable_cost_pairs_) {
5018 variables.push_back(variable_cost.first);
5021 for (
const auto& variable_target : finalizer_variable_target_pairs_) {
5022 variables.push_back(variable_target.first);
5023 targets.push_back(variable_target.second);
5026 std::move(targets));
5029 DecisionBuilder* RoutingModel::CreateSolutionFinalizer(SearchLimit* lns_limit) {
5030 std::vector<DecisionBuilder*> decision_builders;
5031 decision_builders.push_back(solver_->MakePhase(
5034 if (!local_dimension_optimizers_.empty()) {
5035 decision_builders.push_back(
5036 solver_->RevAlloc(
new SetCumulsFromLocalDimensionCosts(
5037 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
5040 if (!global_dimension_optimizers_.empty()) {
5041 decision_builders.push_back(
5042 solver_->RevAlloc(
new SetCumulsFromGlobalDimensionCosts(
5043 &global_dimension_optimizers_, lns_limit)));
5045 decision_builders.push_back(
5046 CreateFinalizerForMinimizedAndMaximizedVariables());
5048 return solver_->Compose(decision_builders);
5051 void RoutingModel::CreateFirstSolutionDecisionBuilders(
5052 const RoutingSearchParameters& search_parameters) {
5053 first_solution_decision_builders_.resize(
5055 first_solution_filtered_decision_builders_.resize(
5057 DecisionBuilder*
const finalize_solution =
5058 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit());
5060 first_solution_decision_builders_
5061 [FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE] = finalize_solution;
5063 first_solution_decision_builders_
5064 [FirstSolutionStrategy::GLOBAL_CHEAPEST_ARC] = solver_->MakePhase(
5072 first_solution_decision_builders_[FirstSolutionStrategy::LOCAL_CHEAPEST_ARC] =
5075 first_solution_decision_builders_[FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5077 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5078 first_solution_filtered_decision_builders_
5079 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5080 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5081 absl::make_unique<EvaluatorCheapestAdditionFilteredHeuristic>(
5086 GetOrCreateFeasibilityFilterManager(search_parameters))));
5087 first_solution_decision_builders_
5088 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5089 solver_->Try(first_solution_filtered_decision_builders_
5090 [FirstSolutionStrategy::PATH_CHEAPEST_ARC],
5091 first_solution_decision_builders_
5092 [FirstSolutionStrategy::PATH_CHEAPEST_ARC]);
5099 first_solution_decision_builders_
5100 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5102 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5103 first_solution_filtered_decision_builders_
5104 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5105 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5106 absl::make_unique<ComparatorCheapestAdditionFilteredHeuristic>(
5108 GetOrCreateFeasibilityFilterManager(search_parameters))));
5109 first_solution_decision_builders_
5110 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->Try(
5111 first_solution_filtered_decision_builders_
5112 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC],
5113 first_solution_decision_builders_
5114 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC]);
5117 if (first_solution_evaluator_ !=
nullptr) {
5118 first_solution_decision_builders_
5119 [FirstSolutionStrategy::EVALUATOR_STRATEGY] = solver_->MakePhase(
5122 first_solution_decision_builders_
5123 [FirstSolutionStrategy::EVALUATOR_STRATEGY] =
nullptr;
5126 first_solution_decision_builders_[FirstSolutionStrategy::ALL_UNPERFORMED] =
5127 solver_->RevAlloc(
new AllUnperformed(
this));
5129 RegularLimit*
const ls_limit =
5132 DecisionBuilder*
const finalize = solver_->MakeSolveOnce(
5133 finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit());
5134 LocalSearchPhaseParameters*
const insertion_parameters =
5135 solver_->MakeLocalSearchPhaseParameters(
5136 nullptr, CreateInsertionOperator(), finalize, ls_limit,
5137 GetOrCreateLocalSearchFilterManager(search_parameters));
5138 std::vector<IntVar*> decision_vars = nexts_;
5140 decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(),
5141 vehicle_vars_.end());
5145 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5146 solver_->MakeNestedOptimize(
5147 solver_->MakeLocalSearchPhase(
5148 decision_vars, solver_->RevAlloc(
new AllUnperformed(
this)),
5149 insertion_parameters),
5150 GetOrCreateAssignment(),
false, optimization_step);
5151 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5152 solver_->Compose(first_solution_decision_builders_
5153 [FirstSolutionStrategy::BEST_INSERTION],
5157 GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
5160 search_parameters.cheapest_insertion_farthest_seeds_ratio(),
5161 search_parameters.cheapest_insertion_first_solution_neighbors_ratio(),
5163 search_parameters.cheapest_insertion_add_unperformed_entries()};
5164 for (
bool is_sequential : {
false,
true}) {
5166 is_sequential ? FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION
5167 : FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5168 gci_parameters.is_sequential = is_sequential;
5170 first_solution_filtered_decision_builders_[first_solution_strategy] =
5171 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5172 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5178 GetOrCreateFeasibilityFilterManager(search_parameters),
5180 IntVarFilteredDecisionBuilder*
const strong_gci =
5181 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5182 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5188 GetOrCreateStrongFeasibilityFilterManager(search_parameters),
5190 first_solution_decision_builders_[first_solution_strategy] = solver_->Try(
5191 first_solution_filtered_decision_builders_[first_solution_strategy],
5192 solver_->Try(strong_gci, first_solution_decision_builders_
5193 [FirstSolutionStrategy::BEST_INSERTION]));
5197 first_solution_filtered_decision_builders_
5198 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] =
5199 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5200 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5205 GetOrCreateFeasibilityFilterManager(search_parameters))));
5206 IntVarFilteredDecisionBuilder*
const strong_lci =
5207 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5208 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5213 GetOrCreateStrongFeasibilityFilterManager(search_parameters))));
5214 first_solution_decision_builders_
5215 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = solver_->Try(
5216 first_solution_filtered_decision_builders_
5217 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION],
5218 solver_->Try(strong_lci,
5219 first_solution_decision_builders_
5220 [FirstSolutionStrategy::BEST_INSERTION]));
5222 SavingsFilteredHeuristic::SavingsParameters savings_parameters;
5223 savings_parameters.neighbors_ratio =
5224 search_parameters.savings_neighbors_ratio();
5225 savings_parameters.max_memory_usage_bytes =
5226 search_parameters.savings_max_memory_usage_bytes();
5227 savings_parameters.add_reverse_arcs =
5228 search_parameters.savings_add_reverse_arcs();
5229 savings_parameters.arc_coefficient =
5230 search_parameters.savings_arc_coefficient();
5231 LocalSearchFilterManager* filter_manager =
nullptr;
5232 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5233 filter_manager = GetOrCreateFeasibilityFilterManager(search_parameters);
5236 if (search_parameters.savings_parallel_routes()) {
5237 IntVarFilteredDecisionBuilder* savings_db =
5238 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5239 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5240 this, &manager_, savings_parameters, filter_manager)));
5241 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5242 first_solution_filtered_decision_builders_
5243 [FirstSolutionStrategy::SAVINGS] = savings_db;
5246 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5247 solver_->Try(savings_db,
5248 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5249 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5250 this, &manager_, savings_parameters,
5251 GetOrCreateStrongFeasibilityFilterManager(
5252 search_parameters)))));
5254 IntVarFilteredDecisionBuilder* savings_db =
5255 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5256 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5257 this, &manager_, savings_parameters, filter_manager)));
5258 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5259 first_solution_filtered_decision_builders_
5260 [FirstSolutionStrategy::SAVINGS] = savings_db;
5263 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5264 solver_->Try(savings_db,
5265 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5266 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5267 this, &manager_, savings_parameters,
5268 GetOrCreateStrongFeasibilityFilterManager(
5269 search_parameters)))));
5272 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5273 solver_->RevAlloc(
new SweepBuilder(
this,
true));
5274 DecisionBuilder* sweep_builder =
5275 solver_->RevAlloc(
new SweepBuilder(
this,
false));
5276 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5279 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP]);
5281 first_solution_decision_builders_[FirstSolutionStrategy::CHRISTOFIDES] =
5282 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5283 absl::make_unique<ChristofidesFilteredHeuristic>(
5284 this, GetOrCreateFeasibilityFilterManager(search_parameters),
5285 search_parameters.christofides_use_minimum_matching())));
5288 const bool has_precedences = std::any_of(
5289 dimensions_.begin(), dimensions_.end(),
5291 if (pickup_delivery_pairs_.empty() && !has_precedences) {
5292 automatic_first_solution_strategy_ =
5293 FirstSolutionStrategy::PATH_CHEAPEST_ARC;
5295 automatic_first_solution_strategy_ =
5296 FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5298 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC] =
5299 first_solution_decision_builders_[automatic_first_solution_strategy_];
5300 first_solution_decision_builders_[FirstSolutionStrategy::UNSET] =
5301 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC];
5304 DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder(
5305 const RoutingSearchParameters& search_parameters)
const {
5307 search_parameters.first_solution_strategy();
5308 if (first_solution_strategy < first_solution_decision_builders_.size()) {
5309 return first_solution_decision_builders_[first_solution_strategy];
5315 IntVarFilteredDecisionBuilder*
5316 RoutingModel::GetFilteredFirstSolutionDecisionBuilderOrNull(
5317 const RoutingSearchParameters& search_parameters)
const {
5319 search_parameters.first_solution_strategy();
5320 return first_solution_filtered_decision_builders_[first_solution_strategy];
5323 LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters(
5324 const RoutingSearchParameters& search_parameters) {
5325 SearchLimit* lns_limit = GetOrCreateLargeNeighborhoodSearchLimit();
5326 return solver_->MakeLocalSearchPhaseParameters(
5327 CostVar(), GetNeighborhoodOperators(search_parameters),
5328 solver_->MakeSolveOnce(CreateSolutionFinalizer(lns_limit), lns_limit),
5329 GetOrCreateLocalSearchLimit(),
5330 GetOrCreateLocalSearchFilterManager(search_parameters));
5333 DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder(
5334 const RoutingSearchParameters& search_parameters) {
5335 const int size =
Size();
5336 DecisionBuilder* first_solution =
5337 GetFirstSolutionDecisionBuilder(search_parameters);
5338 LocalSearchPhaseParameters*
const parameters =
5339 CreateLocalSearchParameters(search_parameters);
5340 SearchLimit* first_solution_lns_limit =
5341 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5342 DecisionBuilder*
const first_solution_sub_decision_builder =
5343 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_solution_lns_limit),
5344 first_solution_lns_limit);
5346 return solver_->MakeLocalSearchPhase(nexts_, first_solution,
5347 first_solution_sub_decision_builder,
5350 const int all_size = size + size + vehicles_;
5351 std::vector<IntVar*> all_vars(all_size);
5352 for (
int i = 0; i < size; ++i) {
5353 all_vars[i] = nexts_[i];
5355 for (
int i = size; i < all_size; ++i) {
5356 all_vars[i] = vehicle_vars_[i - size];
5358 return solver_->MakeLocalSearchPhase(all_vars, first_solution,
5359 first_solution_sub_decision_builder,
5364 void RoutingModel::SetupDecisionBuilders(
5365 const RoutingSearchParameters& search_parameters) {
5366 if (search_parameters.use_depth_first_search()) {
5367 SearchLimit* first_lns_limit =
5368 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5369 solve_db_ = solver_->Compose(
5370 GetFirstSolutionDecisionBuilder(search_parameters),
5371 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_lns_limit),
5374 solve_db_ = CreateLocalSearchDecisionBuilder(search_parameters);
5376 CHECK(preassignment_ !=
nullptr);
5377 DecisionBuilder* restore_preassignment =
5378 solver_->MakeRestoreAssignment(preassignment_);
5379 solve_db_ = solver_->Compose(restore_preassignment, solve_db_);
5381 solver_->Compose(restore_preassignment,
5382 solver_->MakeLocalSearchPhase(
5383 GetOrCreateAssignment(),
5384 CreateLocalSearchParameters(search_parameters)));
5385 restore_assignment_ = solver_->Compose(
5386 solver_->MakeRestoreAssignment(GetOrCreateAssignment()),
5387 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5388 restore_tmp_assignment_ = solver_->Compose(
5389 restore_preassignment,
5390 solver_->MakeRestoreAssignment(GetOrCreateTmpAssignment()),
5391 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5394 void RoutingModel::SetupMetaheuristics(
5395 const RoutingSearchParameters& search_parameters) {
5396 SearchMonitor* optimize;
5398 search_parameters.local_search_metaheuristic();
5401 bool limit_too_long = !search_parameters.has_time_limit() &&
5402 search_parameters.solution_limit() ==
kint64max;
5405 switch (metaheuristic) {
5406 case LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH:
5408 optimize = solver_->MakeGuidedLocalSearch(
5411 optimization_step, nexts_,
5412 search_parameters.guided_local_search_lambda_coefficient());
5414 optimize = solver_->MakeGuidedLocalSearch(
5419 optimization_step, nexts_, vehicle_vars_,
5420 search_parameters.guided_local_search_lambda_coefficient());
5423 case LocalSearchMetaheuristic::SIMULATED_ANNEALING:
5425 solver_->MakeSimulatedAnnealing(
false, cost_, optimization_step, 100);
5427 case LocalSearchMetaheuristic::TABU_SEARCH:
5428 optimize = solver_->MakeTabuSearch(
false, cost_, optimization_step,
5429 nexts_, 10, 10, .8);
5431 case LocalSearchMetaheuristic::GENERIC_TABU_SEARCH: {
5432 std::vector<operations_research::IntVar*> tabu_vars;
5433 if (tabu_var_callback_) {
5434 tabu_vars = tabu_var_callback_(
this);
5436 tabu_vars.push_back(cost_);
5438 optimize = solver_->MakeGenericTabuSearch(
false, cost_, optimization_step,
5443 limit_too_long =
false;
5444 optimize = solver_->MakeMinimize(cost_, optimization_step);
5446 if (limit_too_long) {
5447 LOG(
WARNING) << LocalSearchMetaheuristic::Value_Name(metaheuristic)
5448 <<
" specified without sane timeout: solve may run forever.";
5450 monitors_.push_back(optimize);
5454 tabu_var_callback_ = std::move(tabu_var_callback);
5457 void RoutingModel::SetupAssignmentCollector(
5458 const RoutingSearchParameters& search_parameters) {
5459 Assignment* full_assignment = solver_->MakeAssignment();
5461 full_assignment->
Add(dimension->cumuls());
5463 for (IntVar*
const extra_var : extra_vars_) {
5464 full_assignment->
Add(extra_var);
5466 for (IntervalVar*
const extra_interval : extra_intervals_) {
5467 full_assignment->
Add(extra_interval);
5469 full_assignment->
Add(nexts_);
5470 full_assignment->
Add(active_);
5471 full_assignment->
Add(vehicle_vars_);
5474 collect_assignments_ = solver_->MakeNBestValueSolutionCollector(
5475 full_assignment, search_parameters.number_of_solutions_to_collect(),
5477 collect_one_assignment_ =
5478 solver_->MakeFirstSolutionCollector(full_assignment);
5479 monitors_.push_back(collect_assignments_);
5482 void RoutingModel::SetupTrace(
5483 const RoutingSearchParameters& search_parameters) {
5484 if (search_parameters.log_search()) {
5485 Solver::SearchLogParameters search_log_parameters;
5486 search_log_parameters.branch_period = 10000;
5487 search_log_parameters.objective =
nullptr;
5488 search_log_parameters.variable = cost_;
5489 search_log_parameters.scaling_factor =
5490 search_parameters.log_cost_scaling_factor();
5491 search_log_parameters.offset = search_parameters.log_cost_offset();
5492 if (!search_parameters.log_tag().empty()) {
5493 const std::string& tag = search_parameters.log_tag();
5494 search_log_parameters.display_callback = [tag]() {
return tag; };
5496 search_log_parameters.display_callback =
nullptr;
5498 search_log_parameters.display_on_new_solutions_only =
false;
5499 monitors_.push_back(solver_->MakeSearchLog(search_log_parameters));
5503 void RoutingModel::SetupImprovementLimit(
5504 const RoutingSearchParameters& search_parameters) {
5505 if (search_parameters.has_improvement_limit_parameters()) {
5506 monitors_.push_back(solver_->MakeImprovementLimit(
5507 cost_,
false, search_parameters.log_cost_scaling_factor(),
5508 search_parameters.log_cost_offset(),
5509 search_parameters.improvement_limit_parameters()
5510 .improvement_rate_coefficient(),
5511 search_parameters.improvement_limit_parameters()
5512 .improvement_rate_solutions_distance()));
5516 void RoutingModel::SetupSearchMonitors(
5517 const RoutingSearchParameters& search_parameters) {
5518 monitors_.push_back(GetOrCreateLimit());
5519 SetupImprovementLimit(search_parameters);
5520 SetupMetaheuristics(search_parameters);
5521 SetupAssignmentCollector(search_parameters);
5522 SetupTrace(search_parameters);
5525 bool RoutingModel::UsesLightPropagation(
5526 const RoutingSearchParameters& search_parameters)
const {
5527 return !search_parameters.use_full_propagation() &&
5528 !search_parameters.use_depth_first_search() &&
5529 search_parameters.first_solution_strategy() !=
5530 FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE;
5537 finalizer_variable_cost_pairs_.size());
5538 if (
index < finalizer_variable_cost_pairs_.size()) {
5539 const int64 old_cost = finalizer_variable_cost_pairs_[
index].second;
5540 finalizer_variable_cost_pairs_[
index].second =
CapAdd(old_cost,
cost);
5542 finalizer_variable_cost_pairs_.emplace_back(
var,
cost);
5548 if (finalizer_variable_target_set_.contains(
var))
return;
5549 finalizer_variable_target_set_.insert(
var);
5550 finalizer_variable_target_pairs_.emplace_back(
var, target);
5561 void RoutingModel::SetupSearch(
5562 const RoutingSearchParameters& search_parameters) {
5563 SetupDecisionBuilders(search_parameters);
5564 SetupSearchMonitors(search_parameters);
5568 extra_vars_.push_back(
var);
5572 extra_intervals_.push_back(
interval);
5577 class PathSpansAndTotalSlacks :
public Constraint {
5581 std::vector<IntVar*> spans,
5582 std::vector<IntVar*> total_slacks)
5585 dimension_(dimension),
5586 spans_(std::move(spans)),
5587 total_slacks_(std::move(total_slacks)) {
5588 CHECK_EQ(spans_.size(), model_->vehicles());
5589 CHECK_EQ(total_slacks_.size(), model_->vehicles());
5590 vehicle_demons_.resize(model_->vehicles());
5593 std::string DebugString()
const override {
return "PathSpansAndTotalSlacks"; }
5595 void Post()
override {
5596 const int num_nodes = model_->VehicleVars().size();
5597 const int num_transits = model_->Nexts().size();
5598 for (
int node = 0; node < num_nodes; ++node) {
5600 model_->solver(),
this, &PathSpansAndTotalSlacks::PropagateNode,
5601 "PathSpansAndTotalSlacks::PropagateNode", node);
5602 dimension_->CumulVar(node)->WhenRange(demon);
5603 model_->VehicleVar(node)->WhenBound(demon);
5604 if (node < num_transits) {
5605 dimension_->TransitVar(node)->WhenRange(demon);
5606 dimension_->FixedTransitVar(node)->WhenBound(demon);
5607 model_->NextVar(node)->WhenBound(demon);
5610 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5611 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5613 solver(),
this, &PathSpansAndTotalSlacks::PropagateVehicle,
5614 "PathSpansAndTotalSlacks::PropagateVehicle", vehicle);
5615 vehicle_demons_[vehicle] = demon;
5616 if (spans_[vehicle]) spans_[vehicle]->WhenRange(demon);
5617 if (total_slacks_[vehicle]) total_slacks_[vehicle]->WhenRange(demon);
5618 if (dimension_->HasBreakConstraints()) {
5619 for (IntervalVar*
b : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5620 b->WhenAnything(demon);
5627 void InitialPropagate()
override {
5628 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5629 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5630 PropagateVehicle(vehicle);
5638 void PropagateNode(
int node) {
5639 if (!model_->VehicleVar(node)->Bound())
return;
5640 const int vehicle = model_->VehicleVar(node)->Min();
5641 if (vehicle < 0 || vehicle_demons_[vehicle] ==
nullptr)
return;
5642 EnqueueDelayedDemon(vehicle_demons_[vehicle]);
5650 int64 SpanMin(
int vehicle,
int64 sum_fixed_transits) {
5652 const int64 span_min = spans_[vehicle] ? spans_[vehicle]->Min() :
kint64max;
5653 const int64 total_slack_min =
5654 total_slacks_[vehicle] ? total_slacks_[vehicle]->Min() :
kint64max;
5655 return std::min(span_min,
CapAdd(total_slack_min, sum_fixed_transits));
5657 int64 SpanMax(
int vehicle,
int64 sum_fixed_transits) {
5659 const int64 span_max = spans_[vehicle] ? spans_[vehicle]->Max() :
kint64min;
5660 const int64 total_slack_max =
5661 total_slacks_[vehicle] ? total_slacks_[vehicle]->Max() :
kint64min;
5662 return std::max(span_max,
CapAdd(total_slack_max, sum_fixed_transits));
5664 void SetSpanMin(
int vehicle,
int64 min,
int64 sum_fixed_transits) {
5666 if (spans_[vehicle]) {
5667 spans_[vehicle]->SetMin(
min);
5669 if (total_slacks_[vehicle]) {
5670 total_slacks_[vehicle]->SetMin(
CapSub(
min, sum_fixed_transits));
5673 void SetSpanMax(
int vehicle,
int64 max,
int64 sum_fixed_transits) {
5675 if (spans_[vehicle]) {
5676 spans_[vehicle]->SetMax(
max);
5678 if (total_slacks_[vehicle]) {
5679 total_slacks_[vehicle]->SetMax(
CapSub(
max, sum_fixed_transits));
5684 void SynchronizeSpanAndTotalSlack(
int vehicle,
int64 sum_fixed_transits) {
5686 IntVar* span = spans_[vehicle];
5687 IntVar* total_slack = total_slacks_[vehicle];
5688 if (!span || !total_slack)
return;
5689 span->SetMin(
CapAdd(total_slack->Min(), sum_fixed_transits));
5690 span->SetMax(
CapAdd(total_slack->Max(), sum_fixed_transits));
5691 total_slack->SetMin(
CapSub(span->Min(), sum_fixed_transits));
5692 total_slack->SetMax(
CapSub(span->Max(), sum_fixed_transits));
5695 void PropagateVehicle(
int vehicle) {
5696 DCHECK(spans_[vehicle] || total_slacks_[vehicle]);
5697 const int start = model_->Start(vehicle);
5698 const int end = model_->End(vehicle);
5704 int curr_node = start;
5705 while (!model_->IsEnd(curr_node)) {
5706 const IntVar* next_var = model_->NextVar(curr_node);
5707 if (!next_var->Bound())
return;
5708 path_.push_back(curr_node);
5709 curr_node = next_var->Value();
5714 int64 sum_fixed_transits = 0;
5715 for (
const int node : path_) {
5716 const IntVar* fixed_transit_var = dimension_->FixedTransitVar(node);
5717 if (!fixed_transit_var->Bound())
return;
5718 sum_fixed_transits =
5719 CapAdd(sum_fixed_transits, fixed_transit_var->Value());
5722 SynchronizeSpanAndTotalSlack(vehicle, sum_fixed_transits);
5729 if (dimension_->HasBreakConstraints() &&
5730 !dimension_->GetBreakIntervalsOfVehicle(vehicle).empty()) {
5731 const int64 vehicle_start_max = dimension_->CumulVar(start)->Max();
5732 const int64 vehicle_end_min = dimension_->CumulVar(end)->Min();
5734 int64 min_break_duration = 0;
5735 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5736 if (!br->MustBePerformed())
continue;
5737 if (vehicle_start_max < br->EndMin() &&
5738 br->StartMax() < vehicle_end_min) {
5739 min_break_duration =
CapAdd(min_break_duration, br->DurationMin());
5742 SetSpanMin(vehicle,
CapAdd(min_break_duration, sum_fixed_transits),
5743 sum_fixed_transits);
5749 const int64 slack_max =
5750 CapSub(SpanMax(vehicle, sum_fixed_transits), sum_fixed_transits);
5751 const int64 max_additional_slack =
CapSub(slack_max, min_break_duration);
5752 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5753 if (!br->MustBePerformed())
continue;
5755 if (vehicle_start_max >= br->EndMin() &&
5756 br->StartMax() < vehicle_end_min) {
5757 if (br->DurationMin() > max_additional_slack) {
5760 br->SetEndMax(vehicle_start_max);
5761 dimension_->CumulVar(start)->SetMin(br->EndMin());
5766 if (vehicle_start_max < br->EndMin() &&
5767 br->StartMax() >= vehicle_end_min) {
5768 if (br->DurationMin() > max_additional_slack) {
5769 br->SetStartMin(vehicle_end_min);
5770 dimension_->CumulVar(end)->SetMax(br->StartMax());
5778 IntVar* start_cumul = dimension_->CumulVar(start);
5779 IntVar* end_cumul = dimension_->CumulVar(end);
5786 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5788 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5790 const int64 span_min = SpanMin(vehicle, sum_fixed_transits);
5791 const int64 span_max = SpanMax(vehicle, sum_fixed_transits);
5792 const int64 slack_from_lb =
CapSub(span_max, span_lb);
5793 const int64 slack_from_ub =
CapSub(span_ub, span_min);
5809 for (
const int node : path_) {
5810 span_lb =
CapAdd(span_lb, dimension_->TransitVar(node)->Min());
5811 span_ub =
CapAdd(span_ub, dimension_->TransitVar(node)->Max());
5813 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5814 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5818 const int64 span_min = SpanMin(vehicle, sum_fixed_transits);
5819 const int64 span_max = SpanMax(vehicle, sum_fixed_transits);
5820 const int64 slack_from_lb =
CapSub(span_max, span_lb);
5821 const int64 slack_from_ub =
5823 for (
const int node : path_) {
5824 IntVar* transit_var = dimension_->TransitVar(node);
5825 const int64 transit_i_min = transit_var->Min();
5826 const int64 transit_i_max = transit_var->Max();
5830 transit_var->SetMax(
CapAdd(transit_i_min, slack_from_lb));
5831 transit_var->SetMin(
CapSub(transit_i_max, slack_from_ub));
5836 path_.push_back(end);
5847 int64 arrival_time = dimension_->CumulVar(start)->Min();
5848 for (
int i = 1; i < path_.size(); ++i) {
5851 dimension_->FixedTransitVar(path_[i - 1])->Min()),
5852 dimension_->CumulVar(path_[i])->Min());
5854 int64 departure_time = arrival_time;
5855 for (
int i = path_.size() - 2; i >= 0; --i) {
5858 dimension_->FixedTransitVar(path_[i])->Min()),
5859 dimension_->CumulVar(path_[i])->Max());
5861 const int64 span_lb =
CapSub(arrival_time, departure_time);
5862 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5863 const int64 maximum_deviation =
5864 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5865 const int64 start_lb =
CapSub(departure_time, maximum_deviation);
5866 dimension_->CumulVar(start)->SetMin(start_lb);
5870 int64 departure_time = dimension_->CumulVar(end)->Max();
5871 for (
int i = path_.size() - 2; i >= 0; --i) {
5872 const int curr_node = path_[i];
5875 dimension_->FixedTransitVar(curr_node)->Min()),
5876 dimension_->CumulVar(curr_node)->Max());
5878 int arrival_time = departure_time;
5879 for (
int i = 1; i < path_.size(); ++i) {
5882 dimension_->FixedTransitVar(path_[i - 1])->Min()),
5883 dimension_->CumulVar(path_[i])->Min());
5885 const int64 span_lb =
CapSub(arrival_time, departure_time);
5886 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5887 const int64 maximum_deviation =
5888 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5889 dimension_->CumulVar(end)->SetMax(
5890 CapAdd(arrival_time, maximum_deviation));
5894 const RoutingModel*
const model_;
5895 const RoutingDimension*
const dimension_;
5896 std::vector<IntVar*> spans_;
5897 std::vector<IntVar*> total_slacks_;
5898 std::vector<int> path_;
5899 std::vector<Demon*> vehicle_demons_;
5906 std::vector<IntVar*> total_slacks) {
5908 CHECK_EQ(vehicles_, total_slacks.size());
5910 new PathSpansAndTotalSlacks(
this, dimension, spans, total_slacks));
5918 std::vector<int64> vehicle_capacities,
5919 const std::string&
name,
5921 : vehicle_capacities_(std::move(vehicle_capacities)),
5922 base_dimension_(base_dimension),
5923 global_span_cost_coefficient_(0),
5926 global_optimizer_offset_(0) {
5929 vehicle_span_cost_coefficients_.assign(
model->vehicles(), 0);
5932 RoutingDimension::RoutingDimension(RoutingModel*
model,
5933 std::vector<int64> vehicle_capacities,
5934 const std::string&
name, SelfBased)
5935 : RoutingDimension(
model, std::move(vehicle_capacities),
name, this) {}
5938 cumul_var_piecewise_linear_cost_.clear();
5941 void RoutingDimension::Initialize(
5942 const std::vector<int>& transit_evaluators,
5943 const std::vector<int>& state_dependent_transit_evaluators,
5946 InitializeTransits(transit_evaluators, state_dependent_transit_evaluators,
5960 class LightRangeLessOrEqual :
public Constraint {
5962 LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l, IntExpr*
const r);
5963 ~LightRangeLessOrEqual()
override {}
5964 void Post()
override;
5965 void InitialPropagate()
override;
5966 std::string DebugString()
const override;
5967 IntVar* Var()
override {
5968 return solver()->MakeIsLessOrEqualVar(left_, right_);
5971 void Accept(ModelVisitor*
const visitor)
const override {
5982 IntExpr*
const left_;
5983 IntExpr*
const right_;
5987 LightRangeLessOrEqual::LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l,
5989 : Constraint(s), left_(l), right_(r), demon_(nullptr) {}
5991 void LightRangeLessOrEqual::Post() {
5993 solver(),
this, &LightRangeLessOrEqual::CheckRange,
"CheckRange");
5994 left_->WhenRange(demon_);
5995 right_->WhenRange(demon_);
5998 void LightRangeLessOrEqual::InitialPropagate() {
5999 left_->SetMax(right_->Max());
6000 right_->SetMin(left_->Min());
6001 if (left_->Max() <= right_->Min()) {
6006 void LightRangeLessOrEqual::CheckRange() {
6007 if (left_->Min() > right_->Max()) {
6010 if (left_->Max() <= right_->Min()) {
6015 std::string LightRangeLessOrEqual::DebugString()
const {
6016 return left_->DebugString() +
" < " + right_->DebugString();
6021 void RoutingDimension::InitializeCumuls() {
6022 Solver*
const solver = model_->
solver();
6024 const auto capacity_range = std::minmax_element(vehicle_capacities_.begin(),
6025 vehicle_capacities_.end());
6026 const int64 min_capacity = *capacity_range.first;
6028 const int64 max_capacity = *capacity_range.second;
6029 solver->MakeIntVarArray(size, 0, max_capacity, name_, &cumuls_);
6031 for (
int v = 0; v < model_->
vehicles(); v++) {
6032 const int64 vehicle_capacity = vehicle_capacities_[v];
6033 cumuls_[model_->
Start(v)]->SetMax(vehicle_capacity);
6034 cumuls_[model_->
End(v)]->SetMax(vehicle_capacity);
6037 forbidden_intervals_.resize(size);
6038 capacity_vars_.clear();
6039 if (min_capacity != max_capacity) {
6040 solver->MakeIntVarArray(size, 0,
kint64max, &capacity_vars_);
6041 for (
int i = 0; i < size; ++i) {
6042 IntVar*
const capacity_var = capacity_vars_[i];
6043 if (i < model_->Size()) {
6044 IntVar*
const capacity_active = solver->MakeBoolVar();
6045 solver->AddConstraint(
6046 solver->MakeLessOrEqual(model_->
ActiveVar(i), capacity_active));
6047 solver->AddConstraint(solver->MakeIsLessOrEqualCt(
6048 cumuls_[i], capacity_var, capacity_active));
6050 solver->AddConstraint(
6051 solver->MakeLessOrEqual(cumuls_[i], capacity_var));
6058 template <
int64 value>
6063 void ComputeTransitClasses(
const std::vector<int>& evaluator_indices,
6064 std::vector<int>* class_evaluators,
6065 std::vector<int64>* vehicle_to_class) {
6066 CHECK(class_evaluators !=
nullptr);
6067 CHECK(vehicle_to_class !=
nullptr);
6068 class_evaluators->clear();
6069 vehicle_to_class->resize(evaluator_indices.size(), -1);
6070 absl::flat_hash_map<int, int64> evaluator_to_class;
6071 for (
int i = 0; i < evaluator_indices.size(); ++i) {
6072 const int evaluator_index = evaluator_indices[i];
6073 int evaluator_class = -1;
6074 if (!
gtl::FindCopy(evaluator_to_class, evaluator_index, &evaluator_class)) {
6075 evaluator_class = class_evaluators->size();
6076 evaluator_to_class[evaluator_index] = evaluator_class;
6077 class_evaluators->push_back(evaluator_index);
6079 (*vehicle_to_class)[i] = evaluator_class;
6084 void RoutingDimension::InitializeTransitVariables(
int64 slack_max) {
6085 CHECK(!class_evaluators_.empty());
6086 CHECK(base_dimension_ ==
nullptr ||
6087 !state_dependent_class_evaluators_.empty());
6089 Solver*
const solver = model_->
solver();
6090 const int size = model_->
Size();
6093 return (0 <=
index &&
index < state_dependent_vehicle_to_class_.size())
6094 ? state_dependent_vehicle_to_class_[
index]
6095 : state_dependent_class_evaluators_.size();
6097 const std::string slack_name = name_ +
" slack";
6098 const std::string transit_name = name_ +
" fixed transit";
6100 for (
int64 i = 0; i < size; ++i) {
6101 fixed_transits_[i] =
6104 if (base_dimension_ !=
nullptr) {
6105 if (state_dependent_class_evaluators_.size() == 1) {
6106 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
6107 for (
int64 j = 0; j < cumuls_.size(); ++j) {
6108 transition_variables[j] =
6109 MakeRangeMakeElementExpr(
6111 ->StateDependentTransitCallback(
6112 state_dependent_class_evaluators_[0])(i, j)
6114 base_dimension_->
CumulVar(i), solver)
6117 dependent_transits_[i] =
6118 solver->MakeElement(transition_variables, model_->
NextVar(i))
6121 IntVar*
const vehicle_class_var =
6123 ->MakeElement(dependent_vehicle_class_function,
6126 std::vector<IntVar*> transit_for_vehicle;
6127 transit_for_vehicle.reserve(state_dependent_class_evaluators_.size() +
6129 for (
int evaluator : state_dependent_class_evaluators_) {
6130 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
6131 for (
int64 j = 0; j < cumuls_.size(); ++j) {
6132 transition_variables[j] =
6133 MakeRangeMakeElementExpr(
6136 base_dimension_->
CumulVar(i), solver)
6139 transit_for_vehicle.push_back(
6140 solver->MakeElement(transition_variables, model_->
NextVar(i))
6143 transit_for_vehicle.push_back(solver->MakeIntConst(0));
6144 dependent_transits_[i] =
6145 solver->MakeElement(transit_for_vehicle, vehicle_class_var)->Var();
6148 dependent_transits_[i] = solver->MakeIntConst(0);
6152 IntExpr* transit_expr = fixed_transits_[i];
6153 if (dependent_transits_[i]->Min() != 0 ||
6154 dependent_transits_[i]->Max() != 0) {
6155 transit_expr = solver->MakeSum(transit_expr, dependent_transits_[i]);
6158 if (slack_max == 0) {
6159 slacks_[i] = solver->MakeIntConst(0);
6162 solver->MakeIntVar(0, slack_max, absl::StrCat(slack_name, i));
6163 transit_expr = solver->MakeSum(slacks_[i], transit_expr);
6165 transits_[i] = transit_expr->Var();
6169 void RoutingDimension::InitializeTransits(
6170 const std::vector<int>& transit_evaluators,
6171 const std::vector<int>& state_dependent_transit_evaluators,
6174 CHECK(base_dimension_ ==
nullptr ||
6175 model_->
vehicles() == state_dependent_transit_evaluators.size());
6176 const int size = model_->
Size();
6177 transits_.resize(size,
nullptr);
6178 fixed_transits_.resize(size,
nullptr);
6179 slacks_.resize(size,
nullptr);
6180 dependent_transits_.resize(size,
nullptr);
6181 ComputeTransitClasses(transit_evaluators, &class_evaluators_,
6182 &vehicle_to_class_);
6183 if (base_dimension_ !=
nullptr) {
6184 ComputeTransitClasses(state_dependent_transit_evaluators,
6185 &state_dependent_class_evaluators_,
6186 &state_dependent_vehicle_to_class_);
6189 InitializeTransitVariables(slack_max);
6194 std::vector<int64>* values) {
6195 const int num_nodes = path.size();
6196 values->resize(num_nodes - 1);
6197 for (
int i = 0; i < num_nodes - 1; ++i) {
6198 (*values)[i] = evaluator(path[i], path[i + 1]);
6203 : model_(
model), occurrences_of_type_(
model.GetNumberOfVisitTypes()) {}
6206 int vehicle,
const std::function<
int64(
int64)>& next_accessor) {
6213 for (
int pos = 0; pos < current_route_visits_.size(); pos++) {
6214 const int64 current_visit = current_route_visits_[pos];
6221 DCHECK_LT(type, occurrences_of_type_.size());
6222 int& num_type_added = occurrences_of_type_[type].num_type_added_to_vehicle;
6223 int& num_type_removed =
6224 occurrences_of_type_[type].num_type_removed_from_vehicle;
6225 DCHECK_LE(num_type_removed, num_type_added);
6227 num_type_removed == num_type_added) {
6237 if (policy == VisitTypePolicy::TYPE_ADDED_TO_VEHICLE ||
6238 policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED) {
6241 if (policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED ||
6242 policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6250 int vehicle,
const std::function<
int64(
int64)>& next_accessor) {
6253 std::fill(occurrences_of_type_.begin(), occurrences_of_type_.end(),
6259 current_route_visits_.clear();
6261 current = next_accessor(current)) {
6264 VisitTypePolicy::TYPE_ON_VEHICLE_UP_TO_VISIT) {
6265 occurrences_of_type_[type].position_of_last_type_on_vehicle_up_to_visit =
6266 current_route_visits_.size();
6268 current_route_visits_.push_back(current);
6290 check_hard_incompatibilities_(check_hard_incompatibilities) {}
6292 bool TypeIncompatibilityChecker::HasRegulationsToCheck()
const {
6294 (check_hard_incompatibilities_ &&
6303 bool TypeIncompatibilityChecker::CheckTypeRegulations(
int type,
6304 VisitTypePolicy policy,
6306 if (policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6311 for (
int incompatible_type :
6317 if (check_hard_incompatibilities_) {
6318 for (
int incompatible_type :
6328 bool TypeRequirementChecker::HasRegulationsToCheck()
const {
6333 bool TypeRequirementChecker::CheckRequiredTypesCurrentlyOnRoute(
6334 const std::vector<absl::flat_hash_set<int>>& required_type_alternatives,
6336 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6337 required_type_alternatives) {
6338 bool has_one_of_alternatives =
false;
6339 for (
int type_alternative : requirement_alternatives) {
6341 has_one_of_alternatives =
true;
6345 if (!has_one_of_alternatives) {
6352 bool TypeRequirementChecker::CheckTypeRegulations(
int type,
6353 VisitTypePolicy policy,
6357 if (!CheckRequiredTypesCurrentlyOnRoute(
6363 if (!CheckRequiredTypesCurrentlyOnRoute(
6370 types_with_same_vehicle_requirements_on_route_.insert(type);
6375 bool TypeRequirementChecker::FinalizeCheck()
const {
6376 for (
int type : types_with_same_vehicle_requirements_on_route_) {
6377 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6379 bool has_one_of_alternatives =
false;
6380 for (
const int type_alternative : requirement_alternatives) {
6382 has_one_of_alternatives =
true;
6386 if (!has_one_of_alternatives) {
6397 incompatibility_checker_(
model, true),
6398 requirement_checker_(
model),
6399 vehicle_demons_(
model.vehicles()) {}
6401 void TypeRegulationsConstraint::PropagateNodeRegulations(
int node) {
6408 if (vehicle < 0)
return;
6409 DCHECK(vehicle_demons_[vehicle] !=
nullptr);
6413 void TypeRegulationsConstraint::CheckRegulationsOnVehicle(
int vehicle) {
6414 const auto next_accessor = [
this, vehicle](
int64 node) {
6419 return model_.
End(vehicle);
6421 if (!incompatibility_checker_.
CheckVehicle(vehicle, next_accessor) ||
6422 !requirement_checker_.
CheckVehicle(vehicle, next_accessor)) {
6428 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6430 solver(),
this, &TypeRegulationsConstraint::CheckRegulationsOnVehicle,
6431 "CheckRegulationsOnVehicle", vehicle);
6433 for (
int node = 0; node < model_.
Size(); node++) {
6435 solver(),
this, &TypeRegulationsConstraint::PropagateNodeRegulations,
6436 "PropagateNodeRegulations", node);
6443 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6444 CheckRegulationsOnVehicle(vehicle);
6448 void RoutingDimension::CloseModel(
bool use_light_propagation) {
6450 const auto capacity_lambda = [
this](
int64 vehicle) {
6451 return vehicle >= 0 ? vehicle_capacities_[vehicle] :
kint64max;
6453 for (
int i = 0; i < capacity_vars_.size(); ++i) {
6454 IntVar*
const vehicle_var = model_->
VehicleVar(i);
6455 IntVar*
const capacity_var = capacity_vars_[i];
6456 if (use_light_propagation) {
6458 solver, capacity_var, vehicle_var, capacity_lambda,
6459 [
this]() {
return model_->enable_deep_serialization_; }));
6467 return IthElementOrValue<-1>(vehicle_to_class_,
index);
6469 for (
int i = 0; i < fixed_transits_.size(); ++i) {
6470 IntVar*
const next_var = model_->
NextVar(i);
6471 IntVar*
const fixed_transit = fixed_transits_[i];
6472 const auto transit_vehicle_evaluator = [
this, i](
int64 to,
6476 if (use_light_propagation) {
6477 if (class_evaluators_.size() == 1) {
6478 const int class_evaluator_index = class_evaluators_[0];
6479 const auto& unary_callback =
6481 if (unary_callback ==
nullptr) {
6483 solver, fixed_transit, next_var,
6484 [
this, i](
int64 to) {
6487 [
this]() {
return model_->enable_deep_serialization_; }));
6489 fixed_transit->SetValue(unary_callback(i));
6493 solver, fixed_transit, next_var, model_->
VehicleVar(i),
6494 transit_vehicle_evaluator,
6495 [
this]() {
return model_->enable_deep_serialization_; }));
6498 if (class_evaluators_.size() == 1) {
6499 const int class_evaluator_index = class_evaluators_[0];
6500 const auto& unary_callback =
6502 if (unary_callback ==
nullptr) {
6504 fixed_transit, solver
6506 [
this, i](
int64 to) {
6508 class_evaluators_[0])(i, to);
6513 fixed_transit->SetValue(unary_callback(i));
6516 IntVar*
const vehicle_class_var =
6520 fixed_transit, solver
6522 next_var, vehicle_class_var)
6528 GlobalVehicleBreaksConstraint* constraint =
6535 int64 vehicle)
const {
6551 if (next_start >
max)
break;
6552 if (next_start < interval->start) {
6557 if (next_start <=
max) {
6566 CHECK_LT(vehicle, vehicle_span_upper_bounds_.size());
6568 vehicle_span_upper_bounds_[vehicle] = upper_bound;
6574 CHECK_LT(vehicle, vehicle_span_cost_coefficients_.size());
6576 vehicle_span_cost_coefficients_[vehicle] =
coefficient;
6591 if (!
cost.IsNonDecreasing()) {
6592 LOG(
WARNING) <<
"Only non-decreasing cost functions are supported.";
6595 if (
cost.Value(0) < 0) {
6596 LOG(
WARNING) <<
"Only positive cost functions are supported.";
6599 if (
index >= cumul_var_piecewise_linear_cost_.size()) {
6600 cumul_var_piecewise_linear_cost_.resize(
index + 1);
6602 PiecewiseLinearCost& piecewise_linear_cost =
6603 cumul_var_piecewise_linear_cost_[
index];
6604 piecewise_linear_cost.var = cumuls_[
index];
6605 piecewise_linear_cost.cost = absl::make_unique<PiecewiseLinearFunction>(
cost);
6609 return (
index < cumul_var_piecewise_linear_cost_.size() &&
6610 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr);
6615 if (
index < cumul_var_piecewise_linear_cost_.size() &&
6616 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr) {
6617 return cumul_var_piecewise_linear_cost_[
index].cost.get();
6627 const int vehicle =
model->VehicleIndex(
index);
6629 return solver->
MakeProd(expr,
model->VehicleCostsConsideredVar(vehicle))
6636 void RoutingDimension::SetupCumulVarPiecewiseLinearCosts(
6637 std::vector<IntVar*>* cost_elements)
const {
6638 CHECK(cost_elements !=
nullptr);
6639 Solver*
const solver = model_->
solver();
6640 for (
int i = 0; i < cumul_var_piecewise_linear_cost_.size(); ++i) {
6641 const PiecewiseLinearCost& piecewise_linear_cost =
6642 cumul_var_piecewise_linear_cost_[i];
6643 if (piecewise_linear_cost.var !=
nullptr) {
6644 IntExpr*
const expr = solver->MakePiecewiseLinearExpr(
6645 piecewise_linear_cost.var, *piecewise_linear_cost.cost);
6646 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6647 cost_elements->push_back(cost_var);
6657 if (
index >= cumul_var_soft_upper_bound_.size()) {
6658 cumul_var_soft_upper_bound_.resize(
index + 1, {
nullptr, 0, 0});
6660 cumul_var_soft_upper_bound_[
index] = {cumuls_[
index], upper_bound,
6665 return (
index < cumul_var_soft_upper_bound_.size() &&
6666 cumul_var_soft_upper_bound_[
index].var !=
nullptr);
6670 if (
index < cumul_var_soft_upper_bound_.size() &&
6671 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6672 return cumul_var_soft_upper_bound_[
index].bound;
6674 return cumuls_[
index]->Max();
6679 if (
index < cumul_var_soft_upper_bound_.size() &&
6680 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6681 return cumul_var_soft_upper_bound_[
index].coefficient;
6686 void RoutingDimension::SetupCumulVarSoftUpperBoundCosts(
6687 std::vector<IntVar*>* cost_elements)
const {
6688 CHECK(cost_elements !=
nullptr);
6690 for (
int i = 0; i < cumul_var_soft_upper_bound_.size(); ++i) {
6691 const SoftBound& soft_bound = cumul_var_soft_upper_bound_[i];
6692 if (soft_bound.var !=
nullptr) {
6694 solver->
MakeSum(soft_bound.var, -soft_bound.bound), 0,
6695 soft_bound.coefficient);
6696 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6697 cost_elements->push_back(cost_var);
6701 soft_bound.coefficient);
6708 if (
index >= cumul_var_soft_lower_bound_.size()) {
6709 cumul_var_soft_lower_bound_.resize(
index + 1, {
nullptr, 0, 0});
6711 cumul_var_soft_lower_bound_[
index] = {cumuls_[
index], lower_bound,
6716 return (
index < cumul_var_soft_lower_bound_.size() &&
6717 cumul_var_soft_lower_bound_[
index].var !=
nullptr);
6721 if (
index < cumul_var_soft_lower_bound_.size() &&
6722 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6723 return cumul_var_soft_lower_bound_[
index].bound;
6725 return cumuls_[
index]->Min();
6730 if (
index < cumul_var_soft_lower_bound_.size() &&
6731 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6732 return cumul_var_soft_lower_bound_[
index].coefficient;
6737 void RoutingDimension::SetupCumulVarSoftLowerBoundCosts(
6738 std::vector<IntVar*>* cost_elements)
const {
6739 CHECK(cost_elements !=
nullptr);
6741 for (
int i = 0; i < cumul_var_soft_lower_bound_.size(); ++i) {
6742 const SoftBound& soft_bound = cumul_var_soft_lower_bound_[i];
6743 if (soft_bound.var !=
nullptr) {
6746 soft_bound.coefficient);
6747 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6748 cost_elements->push_back(cost_var);
6752 soft_bound.coefficient);
6757 void RoutingDimension::SetupGlobalSpanCost(
6758 std::vector<IntVar*>* cost_elements)
const {
6759 CHECK(cost_elements !=
nullptr);
6760 Solver*
const solver = model_->
solver();
6761 if (global_span_cost_coefficient_ != 0) {
6762 std::vector<IntVar*> end_cumuls;
6763 for (
int i = 0; i < model_->
vehicles(); ++i) {
6764 end_cumuls.push_back(solver
6765 ->MakeProd(model_->vehicle_costs_considered_[i],
6766 cumuls_[model_->
End(i)])
6769 IntVar*
const max_end_cumul = solver->MakeMax(end_cumuls)->Var();
6771 max_end_cumul, global_span_cost_coefficient_);
6772 std::vector<IntVar*> start_cumuls;
6773 for (
int i = 0; i < model_->
vehicles(); ++i) {
6774 IntVar* global_span_cost_start_cumul = solver->MakeIntVar(0,
kint64max);
6775 solver->AddConstraint(solver->MakeIfThenElseCt(
6776 model_->vehicle_costs_considered_[i], cumuls_[model_->
Start(i)],
6777 max_end_cumul, global_span_cost_start_cumul));
6778 start_cumuls.push_back(global_span_cost_start_cumul);
6780 IntVar*
const min_start_cumul = solver->MakeMin(start_cumuls)->Var();
6782 min_start_cumul, global_span_cost_coefficient_);
6787 for (
int var_index = 0; var_index < model_->
Size(); ++var_index) {
6789 slacks_[var_index], global_span_cost_coefficient_);
6790 cost_elements->push_back(
6793 model_->vehicle_costs_considered_[0],
6796 solver->MakeSum(transits_[var_index],
6797 dependent_transits_[var_index]),
6798 global_span_cost_coefficient_),
6803 IntVar*
const end_range =
6804 solver->MakeDifference(max_end_cumul, min_start_cumul)->Var();
6805 end_range->SetMin(0);
6806 cost_elements->push_back(
6807 solver->MakeProd(end_range, global_span_cost_coefficient_)->Var());
6813 std::vector<IntervalVar*> breaks,
int vehicle,
6814 std::vector<int64> node_visit_transits) {
6815 if (breaks.empty())
return;
6817 [node_visit_transits](
int64 from,
int64 to) {
6818 return node_visit_transits[from];
6824 std::vector<IntervalVar*> breaks,
int vehicle,
6825 std::vector<int64> node_visit_transits,
6827 if (breaks.empty())
return;
6829 [node_visit_transits](
int64 from,
int64 to) {
6830 return node_visit_transits[from];
6838 std::vector<IntervalVar*> breaks,
int vehicle,
int pre_travel_evaluator,
6839 int post_travel_evaluator) {
6842 if (breaks.empty())
return;
6844 vehicle_break_intervals_[vehicle] = std::move(breaks);
6845 vehicle_pre_travel_evaluators_[vehicle] = pre_travel_evaluator;
6846 vehicle_post_travel_evaluators_[vehicle] = post_travel_evaluator;
6867 DCHECK(!break_constraints_are_initialized_);
6868 const int num_vehicles = model_->
vehicles();
6869 vehicle_break_intervals_.resize(num_vehicles);
6870 vehicle_pre_travel_evaluators_.resize(num_vehicles, -1);
6871 vehicle_post_travel_evaluators_.resize(num_vehicles, -1);
6872 vehicle_break_distance_duration_.resize(num_vehicles);
6873 break_constraints_are_initialized_ =
true;
6877 return break_constraints_are_initialized_;
6881 int vehicle)
const {
6883 DCHECK_LT(vehicle, vehicle_break_intervals_.size());
6884 return vehicle_break_intervals_[vehicle];
6889 DCHECK_LT(vehicle, vehicle_pre_travel_evaluators_.size());
6890 return vehicle_pre_travel_evaluators_[vehicle];
6895 DCHECK_LT(vehicle, vehicle_post_travel_evaluators_.size());
6896 return vehicle_post_travel_evaluators_[vehicle];
6905 vehicle_break_distance_duration_[vehicle].emplace_back(distance, duration);
6914 const std::vector<std::pair<int64, int64>>&
6917 DCHECK_LT(vehicle, vehicle_break_distance_duration_.size());
6918 return vehicle_break_distance_duration_[vehicle];
6924 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6925 pickup_to_delivery_limits_per_pair_index_.resize(pair_index + 1);
6927 pickup_to_delivery_limits_per_pair_index_[pair_index] =
6928 std::move(limit_function);
6932 return !pickup_to_delivery_limits_per_pair_index_.empty();
6937 int delivery)
const {
6940 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6944 pickup_to_delivery_limits_per_pair_index_[pair_index];
6945 if (!pickup_to_delivery_limit_function) {
6951 return pickup_to_delivery_limit_function(pickup, delivery);
6954 void RoutingDimension::SetupSlackAndDependentTransitCosts()
const {
6955 if (model_->
vehicles() == 0)
return;
6957 bool all_vehicle_span_costs_are_equal =
true;
6958 for (
int i = 1; i < model_->
vehicles(); ++i) {
6959 all_vehicle_span_costs_are_equal &= vehicle_span_cost_coefficients_[i] ==
6960 vehicle_span_cost_coefficients_[0];
6963 if (all_vehicle_span_costs_are_equal &&
6964 vehicle_span_cost_coefficients_[0] == 0) {
6976 std::vector<const RoutingDimension*> dimensions_with_relevant_slacks = {
this};
6978 const RoutingDimension*
next =
6979 dimensions_with_relevant_slacks.back()->base_dimension_;
6980 if (
next ==
nullptr ||
next == dimensions_with_relevant_slacks.back()) {
6983 dimensions_with_relevant_slacks.push_back(
next);
6986 for (
auto it = dimensions_with_relevant_slacks.rbegin();
6987 it != dimensions_with_relevant_slacks.rend(); ++it) {
6988 for (
int i = 0; i < model_->
vehicles(); ++i) {
6994 for (IntVar*
const slack : (*it)->slacks_) {