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)));
619 LocalSearchOperator* MakeRelocateNeighbors(
620 Solver* solver,
const std::vector<IntVar*>& vars,
621 const std::vector<IntVar*>& secondary_vars,
622 std::function<
int(
int64)> start_empty_path_class,
624 return solver->RevAlloc(
new MakeRelocateNeighborsOperator(
625 vars, secondary_vars, std::move(start_empty_path_class),
626 std::move(arc_evaluator)));
629 LocalSearchOperator* MakePairActive(
630 Solver*
const solver,
const std::vector<IntVar*>& vars,
631 const std::vector<IntVar*>& secondary_vars,
632 std::function<
int(
int64)> start_empty_path_class,
634 return solver->RevAlloc(
new MakePairActiveOperator(
635 vars, secondary_vars, std::move(start_empty_path_class), pairs));
638 LocalSearchOperator* MakePairInactive(
639 Solver*
const solver,
const std::vector<IntVar*>& vars,
640 const std::vector<IntVar*>& secondary_vars,
641 std::function<
int(
int64)> start_empty_path_class,
643 return solver->RevAlloc(
new MakePairInactiveOperator(
644 vars, secondary_vars, std::move(start_empty_path_class), pairs));
647 LocalSearchOperator* MakePairRelocate(
648 Solver*
const solver,
const std::vector<IntVar*>& vars,
649 const std::vector<IntVar*>& secondary_vars,
650 std::function<
int(
int64)> start_empty_path_class,
652 return solver->RevAlloc(
new PairRelocateOperator(
653 vars, secondary_vars, std::move(start_empty_path_class), pairs));
656 LocalSearchOperator* MakeLightPairRelocate(
657 Solver*
const solver,
const std::vector<IntVar*>& vars,
658 const std::vector<IntVar*>& secondary_vars,
659 std::function<
int(
int64)> start_empty_path_class,
661 return solver->RevAlloc(
new LightPairRelocateOperator(
662 vars, secondary_vars, std::move(start_empty_path_class), pairs));
665 LocalSearchOperator* MakePairExchange(
666 Solver*
const solver,
const std::vector<IntVar*>& vars,
667 const std::vector<IntVar*>& secondary_vars,
668 std::function<
int(
int64)> start_empty_path_class,
670 return solver->RevAlloc(
new PairExchangeOperator(
671 vars, secondary_vars, std::move(start_empty_path_class), pairs));
674 LocalSearchOperator* MakePairExchangeRelocate(
675 Solver*
const solver,
const std::vector<IntVar*>& vars,
676 const std::vector<IntVar*>& secondary_vars,
677 std::function<
int(
int64)> start_empty_path_class,
679 return solver->RevAlloc(
new PairExchangeRelocateOperator(
680 vars, secondary_vars, std::move(start_empty_path_class), pairs));
683 LocalSearchOperator* SwapIndexPair(Solver*
const solver,
684 const std::vector<IntVar*>& vars,
685 const std::vector<IntVar*>& secondary_vars,
687 return solver->RevAlloc(
688 new SwapIndexPairOperator(vars, secondary_vars, pairs));
691 LocalSearchOperator* IndexPairSwapActive(
692 Solver*
const solver,
const std::vector<IntVar*>& vars,
693 const std::vector<IntVar*>& secondary_vars,
694 std::function<
int(
int64)> start_empty_path_class,
696 return solver->RevAlloc(
new IndexPairSwapActiveOperator(
697 vars, secondary_vars, std::move(start_empty_path_class), pairs));
700 LocalSearchOperator* PairNodeSwapActive(
701 Solver*
const solver,
const std::vector<IntVar*>& vars,
702 const std::vector<IntVar*>& secondary_vars,
703 std::function<
int(
int64)> start_empty_path_class,
705 return solver->ConcatenateOperators(
706 {solver->RevAlloc(
new PairNodeSwapActiveOperator<true>(
707 vars, secondary_vars, start_empty_path_class, pairs)),
708 solver->RevAlloc(
new PairNodeSwapActiveOperator<false>(
709 vars, secondary_vars, std::move(start_empty_path_class), pairs))});
712 LocalSearchOperator* MakeRelocateSubtrip(
713 Solver*
const solver,
const std::vector<IntVar*>& vars,
714 const std::vector<IntVar*>& secondary_vars,
715 std::function<
int(
int64)> start_empty_path_class,
717 return solver->RevAlloc(
new RelocateSubtrip(
718 vars, secondary_vars, std::move(start_empty_path_class), pairs));
721 LocalSearchOperator* MakeExchangeSubtrip(
722 Solver*
const solver,
const std::vector<IntVar*>& vars,
723 const std::vector<IntVar*>& secondary_vars,
724 std::function<
int(
int64)> start_empty_path_class,
726 return solver->RevAlloc(
new ExchangeSubtrip(
727 vars, secondary_vars, std::move(start_empty_path_class), pairs));
731 template <
class A,
class B>
732 static int64 ReturnZero(A
a, B
b) {
738 for (
int i = 0; i < size1; i++) {
739 for (
int j = 0; j < size2; j++) {
764 : nodes_(index_manager.num_nodes()),
765 vehicles_(index_manager.num_vehicles()),
766 max_active_vehicles_(vehicles_),
767 fixed_cost_of_vehicle_(vehicles_, 0),
769 linear_cost_factor_of_vehicle_(vehicles_, 0),
770 quadratic_cost_factor_of_vehicle_(vehicles_, 0),
771 vehicle_amortized_cost_factors_set_(false),
772 consider_empty_route_costs_(vehicles_, false),
774 costs_are_homogeneous_across_vehicles_(
776 cache_callbacks_(false),
778 vehicle_pickup_delivery_policy_(vehicles_, PICKUP_AND_DELIVERY_NO_ORDER),
779 has_hard_type_incompatibilities_(false),
780 has_temporal_type_incompatibilities_(false),
781 has_same_vehicle_type_requirements_(false),
782 has_temporal_type_requirements_(false),
786 manager_(index_manager) {
788 vehicle_to_transit_cost_.assign(
792 cache_callbacks_ = (nodes_ <=
parameters.max_callback_cache_size());
795 ConstraintSolverParameters solver_parameters =
798 solver_ = absl::make_unique<Solver>(
"Routing", solver_parameters);
804 index_to_pickup_index_pairs_.resize(size);
805 index_to_delivery_index_pairs_.resize(size);
807 index_to_type_policy_.resize(index_manager.
num_indices());
810 for (
int v = 0; v < index_manager.
num_vehicles(); ++v) {
812 index_to_vehicle_[starts_[v]] = v;
814 index_to_vehicle_[ends_[v]] = v;
817 const std::vector<RoutingIndexManager::NodeIndex>& index_to_node =
819 index_to_equivalence_class_.resize(index_manager.
num_indices());
820 for (
int i = 0; i < index_to_node.size(); ++i) {
821 index_to_equivalence_class_[i] = index_to_node[i].value();
823 allowed_vehicles_.resize(
Size() + vehicles_);
826 void RoutingModel::Initialize() {
827 const int size =
Size();
829 solver_->MakeIntVarArray(size, 0, size + vehicles_ - 1,
"Nexts", &nexts_);
830 solver_->AddConstraint(solver_->MakeAllDifferent(nexts_,
false));
831 index_to_disjunctions_.resize(size + vehicles_);
834 solver_->MakeIntVarArray(size + vehicles_, -1, vehicles_ - 1,
"Vehicles",
837 solver_->MakeBoolVarArray(size,
"Active", &active_);
839 solver_->MakeBoolVarArray(vehicles_,
"ActiveVehicle", &vehicle_active_);
841 solver_->MakeBoolVarArray(vehicles_,
"VehicleCostsConsidered",
842 &vehicle_costs_considered_);
844 solver_->MakeBoolVarArray(size + vehicles_,
"IsBoundToEnd",
849 preassignment_ = solver_->MakeAssignment();
856 absl::flat_hash_set<RangeIntToIntFunction*> value_functions_delete;
857 absl::flat_hash_set<RangeMinMaxIndexFunction*> index_functions_delete;
858 for (
const auto& cache_line : state_dependent_transit_evaluators_cache_) {
859 for (
const auto& key_transit : *cache_line) {
860 value_functions_delete.insert(key_transit.second.transit);
861 index_functions_delete.insert(key_transit.second.transit_plus_identity);
869 const int index = unary_transit_evaluators_.size();
870 unary_transit_evaluators_.push_back(std::move(
callback));
872 return unary_transit_evaluators_[
index](i);
878 is_transit_evaluator_positive_.push_back(
true);
879 DCHECK(TransitCallbackPositive(
885 if (cache_callbacks_) {
887 std::vector<int64> cache(size * size, 0);
888 for (
int i = 0; i < size; ++i) {
889 for (
int j = 0; j < size; ++j) {
890 cache[i * size + j] =
callback(i, j);
893 transit_evaluators_.push_back(
894 [cache, size](
int64 i,
int64 j) {
return cache[i * size + j]; });
896 transit_evaluators_.push_back(std::move(
callback));
898 if (transit_evaluators_.size() != unary_transit_evaluators_.size()) {
899 DCHECK_EQ(transit_evaluators_.size(), unary_transit_evaluators_.size() + 1);
900 unary_transit_evaluators_.push_back(
nullptr);
902 if (transit_evaluators_.size() != is_transit_evaluator_positive_.size()) {
904 is_transit_evaluator_positive_.size() + 1);
905 is_transit_evaluator_positive_.push_back(
false);
907 return transit_evaluators_.size() - 1;
911 is_transit_evaluator_positive_.push_back(
true);
919 state_dependent_transit_evaluators_cache_.push_back(
920 absl::make_unique<StateDependentTransitCallbackCache>());
921 StateDependentTransitCallbackCache*
const cache =
922 state_dependent_transit_evaluators_cache_.back().get();
923 state_dependent_transit_evaluators_.push_back(
928 cache->insert({CacheKey(i, j),
value});
931 return state_dependent_transit_evaluators_.size() - 1;
934 void RoutingModel::AddNoCycleConstraintInternal() {
935 if (no_cycle_constraint_ ==
nullptr) {
936 no_cycle_constraint_ = solver_->MakeNoCycle(nexts_, active_);
937 solver_->AddConstraint(no_cycle_constraint_);
943 const std::string&
name) {
944 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
945 std::vector<int64> capacities(vehicles_,
capacity);
946 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
947 std::move(capacities),
948 fix_start_cumul_to_zero,
name);
953 bool fix_start_cumul_to_zero,
const std::string&
name) {
954 std::vector<int64> capacities(vehicles_,
capacity);
955 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
956 std::move(capacities),
957 fix_start_cumul_to_zero,
name);
961 int evaluator_index,
int64 slack_max, std::vector<int64> vehicle_capacities,
962 bool fix_start_cumul_to_zero,
const std::string&
name) {
963 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
964 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
965 std::move(vehicle_capacities),
966 fix_start_cumul_to_zero,
name);
970 const std::vector<int>& evaluator_indices,
int64 slack_max,
971 std::vector<int64> vehicle_capacities,
bool fix_start_cumul_to_zero,
972 const std::string&
name) {
973 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
974 std::move(vehicle_capacities),
975 fix_start_cumul_to_zero,
name);
978 bool RoutingModel::AddDimensionWithCapacityInternal(
979 const std::vector<int>& evaluator_indices,
int64 slack_max,
980 std::vector<int64> vehicle_capacities,
bool fix_start_cumul_to_zero,
981 const std::string&
name) {
982 CHECK_EQ(vehicles_, vehicle_capacities.size());
983 return InitializeDimensionInternal(
984 evaluator_indices, std::vector<int>(), slack_max, fix_start_cumul_to_zero,
988 bool RoutingModel::InitializeDimensionInternal(
989 const std::vector<int>& evaluator_indices,
990 const std::vector<int>& state_dependent_evaluator_indices,
int64 slack_max,
991 bool fix_start_cumul_to_zero, RoutingDimension* dimension) {
992 CHECK(dimension !=
nullptr);
993 CHECK_EQ(vehicles_, evaluator_indices.size());
994 CHECK((dimension->base_dimension_ ==
nullptr &&
995 state_dependent_evaluator_indices.empty()) ||
996 vehicles_ == state_dependent_evaluator_indices.size());
999 dimension_name_to_index_[dimension->name()] = dimension_index;
1000 dimensions_.push_back(dimension);
1001 dimension->Initialize(evaluator_indices, state_dependent_evaluator_indices,
1003 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
1004 nexts_, active_, dimension->cumuls(), dimension->transits()));
1005 if (fix_start_cumul_to_zero) {
1006 for (
int i = 0; i < vehicles_; ++i) {
1007 IntVar*
const start_cumul = dimension->CumulVar(
Start(i));
1009 start_cumul->SetValue(0);
1020 RoutingModel*
model) {
1022 return model->RegisterPositiveTransitCallback(std::move(
callback));
1028 RoutingModel*
model) {
1030 return model->RegisterPositiveUnaryTransitCallback(std::move(
callback));
1032 return model->RegisterUnaryTransitCallback(std::move(
callback));
1038 const std::string& dimension_name) {
1041 slack_max,
capacity, fix_start_cumul_to_zero,
1046 bool fix_start_cumul_to_zero,
1047 const std::string& dimension_name) {
1049 RegisterUnaryCallback(
1050 [
this, values](
int64 i) {
1054 std::all_of(std::begin(values), std::end(values),
1055 [](
int64 transit) {
return transit >= 0; }),
1057 0,
capacity, fix_start_cumul_to_zero, dimension_name);
1062 bool fix_start_cumul_to_zero,
1063 const std::string& dimension_name) {
1064 bool all_transits_positive =
true;
1065 for (
const std::vector<int64>& transit_values : values) {
1066 all_transits_positive =
1067 std::all_of(std::begin(transit_values), std::end(transit_values),
1068 [](
int64 transit) {
return transit >= 0; });
1069 if (!all_transits_positive) {
1078 all_transits_positive,
this),
1079 0,
capacity, fix_start_cumul_to_zero, dimension_name);
1091 CHECK(callback_ !=
nullptr);
1095 int64 Min()
const override {
1097 const int idx_min = index_->Min();
1098 const int idx_max = index_->Max() + 1;
1099 return (idx_min < idx_max) ? callback_->RangeMin(idx_min, idx_max)
1102 void SetMin(
int64 new_min)
override {
1103 const int64 old_min = Min();
1104 const int64 old_max = Max();
1105 if (old_min < new_min && new_min <= old_max) {
1106 const int64 old_idx_min = index_->Min();
1107 const int64 old_idx_max = index_->Max() + 1;
1108 if (old_idx_min < old_idx_max) {
1109 const int64 new_idx_min = callback_->RangeFirstInsideInterval(
1110 old_idx_min, old_idx_max, new_min, old_max + 1);
1111 index_->SetMin(new_idx_min);
1112 if (new_idx_min < old_idx_max) {
1113 const int64 new_idx_max = callback_->RangeLastInsideInterval(
1114 new_idx_min, old_idx_max, new_min, old_max + 1);
1115 index_->SetMax(new_idx_max);
1120 int64 Max()
const override {
1122 const int idx_min = index_->Min();
1123 const int idx_max = index_->Max() + 1;
1124 return (idx_min < idx_max) ? callback_->RangeMax(idx_min, idx_max)
1127 void SetMax(
int64 new_max)
override {
1128 const int64 old_min = Min();
1129 const int64 old_max = Max();
1130 if (old_min <= new_max && new_max < old_max) {
1131 const int64 old_idx_min = index_->Min();
1132 const int64 old_idx_max = index_->Max() + 1;
1133 if (old_idx_min < old_idx_max) {
1134 const int64 new_idx_min = callback_->RangeFirstInsideInterval(
1135 old_idx_min, old_idx_max, old_min, new_max + 1);
1136 index_->SetMin(new_idx_min);
1137 if (new_idx_min < old_idx_max) {
1138 const int64 new_idx_max = callback_->RangeLastInsideInterval(
1139 new_idx_min, old_idx_max, old_min, new_max + 1);
1140 index_->SetMax(new_idx_max);
1145 void WhenRange(Demon* d)
override { index_->WhenRange(d); }
1148 const RangeIntToIntFunction*
const callback_;
1149 IntVar*
const index_;
1152 IntExpr* MakeRangeMakeElementExpr(
const RangeIntToIntFunction*
callback,
1153 IntVar*
index, Solver* s) {
1154 return s->RegisterIntExpr(
1160 const std::vector<int>& dependent_transits,
1162 std::vector<int64> vehicle_capacities,
bool fix_start_cumul_to_zero,
1163 const std::string&
name) {
1164 const std::vector<int> pure_transits(vehicles_, 0);
1166 pure_transits, dependent_transits, base_dimension, slack_max,
1167 std::move(vehicle_capacities), fix_start_cumul_to_zero,
name);
1172 int64 vehicle_capacity,
bool fix_start_cumul_to_zero,
1173 const std::string&
name) {
1175 0, transit, dimension, slack_max, vehicle_capacity,
1176 fix_start_cumul_to_zero,
name);
1179 bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacityInternal(
1180 const std::vector<int>& pure_transits,
1181 const std::vector<int>& dependent_transits,
1183 std::vector<int64> vehicle_capacities,
bool fix_start_cumul_to_zero,
1184 const std::string&
name) {
1185 CHECK_EQ(vehicles_, vehicle_capacities.size());
1187 if (base_dimension ==
nullptr) {
1189 name, RoutingDimension::SelfBased());
1192 name, base_dimension);
1194 return InitializeDimensionInternal(pure_transits, dependent_transits,
1195 slack_max, fix_start_cumul_to_zero,
1200 int pure_transit,
int dependent_transit,
1202 int64 vehicle_capacity,
bool fix_start_cumul_to_zero,
1203 const std::string&
name) {
1204 std::vector<int> pure_transits(vehicles_, pure_transit);
1205 std::vector<int> dependent_transits(vehicles_, dependent_transit);
1206 std::vector<int64> vehicle_capacities(vehicles_, vehicle_capacity);
1207 return AddDimensionDependentDimensionWithVehicleCapacityInternal(
1208 pure_transits, dependent_transits, base_dimension, slack_max,
1209 std::move(vehicle_capacities), fix_start_cumul_to_zero,
name);
1215 const std::function<
int64(
int64)> g = [&f](
int64 x) {
return f(x) + x; };
1223 std::vector<std::string> dimension_names;
1224 for (
const auto& dimension_name_index : dimension_name_to_index_) {
1225 dimension_names.push_back(dimension_name_index.first);
1227 std::sort(dimension_names.begin(), dimension_names.end());
1228 return dimension_names;
1234 if (dim_index < 0 || dim_index >= global_optimizer_index_.
size() ||
1235 global_optimizer_index_[dim_index] < 0) {
1238 const int optimizer_index = global_optimizer_index_[dim_index];
1239 DCHECK_LT(optimizer_index, global_dimension_optimizers_.size());
1240 return global_dimension_optimizers_[optimizer_index].get();
1246 if (dim_index < 0 || dim_index >= local_optimizer_index_.
size() ||
1247 local_optimizer_index_[dim_index] < 0) {
1250 const int optimizer_index = local_optimizer_index_[dim_index];
1251 DCHECK_LT(optimizer_index, local_dimension_optimizers_.size());
1252 return local_dimension_optimizers_[optimizer_index].get();
1258 if (dim_index < 0 || dim_index >= local_optimizer_index_.
size() ||
1259 local_optimizer_index_[dim_index] < 0) {
1262 const int optimizer_index = local_optimizer_index_[dim_index];
1263 DCHECK_LT(optimizer_index, local_dimension_mp_optimizers_.size());
1264 return local_dimension_mp_optimizers_[optimizer_index].get();
1272 const std::string& dimension_name)
const {
1278 const std::string& dimension_name)
const {
1279 return *dimensions_[
gtl::FindOrDie(dimension_name_to_index_, dimension_name)];
1283 const std::string& dimension_name)
const {
1286 return dimensions_[
index];
1293 for (
int i = 0; i < vehicles_; ++i) {
1301 CHECK_LT(evaluator_index, transit_evaluators_.size());
1302 vehicle_to_transit_cost_[vehicle] = evaluator_index;
1306 for (
int i = 0; i < vehicles_; ++i) {
1313 return fixed_cost_of_vehicle_[vehicle];
1319 fixed_cost_of_vehicle_[vehicle] =
cost;
1323 int64 linear_cost_factor,
int64 quadratic_cost_factor) {
1324 for (
int v = 0; v < vehicles_; v++) {
1331 int64 quadratic_cost_factor,
1336 if (linear_cost_factor + quadratic_cost_factor > 0) {
1337 vehicle_amortized_cost_factors_set_ =
true;
1339 linear_cost_factor_of_vehicle_[vehicle] = linear_cost_factor;
1340 quadratic_cost_factor_of_vehicle_[vehicle] = quadratic_cost_factor;
1346 struct CostClassComparator {
1353 struct VehicleClassComparator {
1354 bool operator()(
const RoutingModel::VehicleClass&
a,
1355 const RoutingModel::VehicleClass&
b)
const {
1365 void RoutingModel::ComputeCostClasses(
1368 cost_classes_.reserve(vehicles_);
1369 cost_classes_.clear();
1370 cost_class_index_of_vehicle_.assign(vehicles_,
CostClassIndex(-1));
1371 std::map<CostClass, CostClassIndex, CostClassComparator> cost_class_map;
1374 const CostClass zero_cost_class(0);
1375 cost_classes_.push_back(zero_cost_class);
1376 DCHECK_EQ(cost_classes_[kCostClassIndexOfZeroCost].evaluator_index, 0);
1377 cost_class_map[zero_cost_class] = kCostClassIndexOfZeroCost;
1382 has_vehicle_with_zero_cost_class_ =
false;
1383 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
1384 CostClass cost_class(vehicle_to_transit_cost_[vehicle]);
1388 const int64 coeff = dimension->vehicle_span_cost_coefficients()[vehicle];
1389 if (coeff == 0)
continue;
1390 cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1391 .push_back({dimension->vehicle_to_class(vehicle), coeff, dimension});
1393 std::sort(cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1395 cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1401 if (cost_class_index == kCostClassIndexOfZeroCost) {
1402 has_vehicle_with_zero_cost_class_ =
true;
1403 }
else if (cost_class_index == num_cost_classes) {
1404 cost_classes_.push_back(cost_class);
1406 cost_class_index_of_vehicle_[vehicle] = cost_class_index;
1420 costs_are_homogeneous_across_vehicles_ &= has_vehicle_with_zero_cost_class_
1427 return std::tie(
a.cost_class_index,
a.fixed_cost,
a.start_equivalence_class,
1428 a.end_equivalence_class,
a.unvisitable_nodes_fprint,
1429 a.dimension_start_cumuls_min,
a.dimension_start_cumuls_max,
1430 a.dimension_end_cumuls_min,
a.dimension_end_cumuls_max,
1431 a.dimension_capacities,
a.dimension_evaluator_classes) <
1432 std::tie(
b.cost_class_index,
b.fixed_cost,
b.start_equivalence_class,
1433 b.end_equivalence_class,
b.unvisitable_nodes_fprint,
1434 b.dimension_start_cumuls_min,
b.dimension_start_cumuls_max,
1435 b.dimension_end_cumuls_min,
b.dimension_end_cumuls_max,
1436 b.dimension_capacities,
b.dimension_evaluator_classes);
1439 void RoutingModel::ComputeVehicleClasses() {
1440 vehicle_classes_.reserve(vehicles_);
1441 vehicle_classes_.clear();
1443 std::map<VehicleClass, VehicleClassIndex, VehicleClassComparator>
1445 const int nodes_unvisitability_num_bytes = (vehicle_vars_.size() + 7) / 8;
1446 std::unique_ptr<char[]> nodes_unvisitability_bitmask(
1447 new char[nodes_unvisitability_num_bytes]);
1448 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
1451 vehicle_class.
fixed_cost = fixed_cost_of_vehicle_[vehicle];
1453 index_to_equivalence_class_[
Start(vehicle)];
1455 index_to_equivalence_class_[
End(vehicle)];
1457 IntVar*
const start_cumul_var = dimension->cumuls()[
Start(vehicle)];
1459 start_cumul_var->
Min());
1461 start_cumul_var->
Max());
1462 IntVar*
const end_cumul_var = dimension->cumuls()[
End(vehicle)];
1466 dimension->vehicle_capacities()[vehicle]);
1468 dimension->vehicle_to_class(vehicle));
1470 memset(nodes_unvisitability_bitmask.get(), 0,
1471 nodes_unvisitability_num_bytes);
1475 (!vehicle_var->
Contains(vehicle) ||
1477 nodes_unvisitability_bitmask[
index / CHAR_BIT] |= 1U
1478 << (
index % CHAR_BIT);
1482 nodes_unvisitability_bitmask.get(), nodes_unvisitability_num_bytes);
1485 &vehicle_class_map, vehicle_class, num_vehicle_classes);
1486 if (vehicle_class_index == num_vehicle_classes) {
1487 vehicle_classes_.push_back(vehicle_class);
1489 vehicle_class_index_of_vehicle_[vehicle] = vehicle_class_index;
1493 void RoutingModel::ComputeVehicleTypes() {
1494 const int nodes_squared = nodes_ * nodes_;
1495 std::vector<int>& type_index_of_vehicle =
1497 std::vector<std::set<VehicleTypeContainer::VehicleClassEntry>>&
1498 sorted_vehicle_classes_per_type =
1500 std::vector<std::deque<int>>& vehicles_per_vehicle_class =
1503 type_index_of_vehicle.resize(vehicles_);
1504 sorted_vehicle_classes_per_type.clear();
1505 sorted_vehicle_classes_per_type.reserve(vehicles_);
1506 vehicles_per_vehicle_class.clear();
1509 absl::flat_hash_map<int64, int> type_to_type_index;
1511 for (
int v = 0; v < vehicles_; v++) {
1515 const int64 type = cost_class * nodes_squared + start * nodes_ + end;
1517 const auto& vehicle_type_added = type_to_type_index.insert(
1518 std::make_pair(type, type_to_type_index.size()));
1520 const int index = vehicle_type_added.first->second;
1523 const VehicleTypeContainer::VehicleClassEntry class_entry = {
1526 if (vehicle_type_added.second) {
1529 sorted_vehicle_classes_per_type.push_back({class_entry});
1533 sorted_vehicle_classes_per_type[
index].insert(class_entry);
1535 vehicles_per_vehicle_class[vehicle_class].push_back(v);
1536 type_index_of_vehicle[v] =
index;
1540 void RoutingModel::FinalizeVisitTypes() {
1546 single_nodes_of_type_.clear();
1547 single_nodes_of_type_.resize(num_visit_types_);
1548 pair_indices_of_type_.clear();
1549 pair_indices_of_type_.resize(num_visit_types_);
1550 std::vector<absl::flat_hash_set<int>> pair_indices_added_for_type(
1555 if (visit_type < 0) {
1558 const std::vector<std::pair<int, int>>& pickup_index_pairs =
1559 index_to_pickup_index_pairs_[
index];
1560 const std::vector<std::pair<int, int>>& delivery_index_pairs =
1561 index_to_delivery_index_pairs_[
index];
1562 if (pickup_index_pairs.empty() && delivery_index_pairs.empty()) {
1563 single_nodes_of_type_[visit_type].push_back(
index);
1565 for (
const std::vector<std::pair<int, int>>* index_pairs :
1566 {&pickup_index_pairs, &delivery_index_pairs}) {
1567 for (
const std::pair<int, int>& index_pair : *index_pairs) {
1568 const int pair_index = index_pair.first;
1569 if (pair_indices_added_for_type[visit_type].insert(pair_index).second) {
1570 pair_indices_of_type_[visit_type].push_back(pair_index);
1576 TopologicallySortVisitTypes();
1579 void RoutingModel::TopologicallySortVisitTypes() {
1580 if (!has_same_vehicle_type_requirements_ &&
1581 !has_temporal_type_requirements_) {
1584 std::vector<std::pair<double, double>> type_requirement_tightness(
1585 num_visit_types_, {0, 0});
1586 std::vector<absl::flat_hash_set<int>> type_to_dependent_types(
1588 SparseBitset<> types_in_requirement_graph(num_visit_types_);
1589 std::vector<int> in_degree(num_visit_types_, 0);
1590 for (
int type = 0; type < num_visit_types_; type++) {
1591 int num_alternative_required_types = 0;
1592 int num_required_sets = 0;
1593 for (
const std::vector<absl::flat_hash_set<int>>*
1594 required_type_alternatives :
1595 {&required_type_alternatives_when_adding_type_index_[type],
1596 &required_type_alternatives_when_removing_type_index_[type],
1597 &same_vehicle_required_type_alternatives_per_type_index_[type]}) {
1598 for (
const absl::flat_hash_set<int>& alternatives :
1599 *required_type_alternatives) {
1600 types_in_requirement_graph.Set(type);
1601 num_required_sets++;
1602 for (
int required_type : alternatives) {
1603 type_requirement_tightness[required_type].second +=
1604 1.0 / alternatives.size();
1605 types_in_requirement_graph.Set(required_type);
1606 num_alternative_required_types++;
1607 if (type_to_dependent_types[required_type].insert(type).second) {
1613 if (num_alternative_required_types > 0) {
1614 type_requirement_tightness[type].first += 1.0 * num_required_sets *
1616 num_alternative_required_types;
1621 topologically_sorted_visit_types_.clear();
1622 std::vector<int> current_types_with_zero_indegree;
1623 for (
int type : types_in_requirement_graph.PositionsSetAtLeastOnce()) {
1624 DCHECK(type_requirement_tightness[type].first > 0 ||
1625 type_requirement_tightness[type].second > 0);
1626 if (in_degree[type] == 0) {
1627 current_types_with_zero_indegree.push_back(type);
1631 int num_types_added = 0;
1632 while (!current_types_with_zero_indegree.empty()) {
1635 topologically_sorted_visit_types_.push_back({});
1636 std::vector<int>& topological_group =
1637 topologically_sorted_visit_types_.back();
1638 std::vector<int> next_types_with_zero_indegree;
1639 for (
int type : current_types_with_zero_indegree) {
1640 topological_group.push_back(type);
1642 for (
int dependent_type : type_to_dependent_types[type]) {
1643 DCHECK_GT(in_degree[dependent_type], 0);
1644 if (--in_degree[dependent_type] == 0) {
1645 next_types_with_zero_indegree.push_back(dependent_type);
1656 std::sort(topological_group.begin(), topological_group.end(),
1657 [&type_requirement_tightness](
int type1,
int type2) {
1658 const auto& tightness1 = type_requirement_tightness[type1];
1659 const auto& tightness2 = type_requirement_tightness[type2];
1660 return tightness1 > tightness2 ||
1661 (tightness1 == tightness2 && type1 < type2);
1664 current_types_with_zero_indegree.swap(next_types_with_zero_indegree);
1667 const int num_types_in_requirement_graph =
1668 types_in_requirement_graph.NumberOfSetCallsWithDifferentArguments();
1669 DCHECK_LE(num_types_added, num_types_in_requirement_graph);
1670 if (num_types_added < num_types_in_requirement_graph) {
1672 topologically_sorted_visit_types_.clear();
1677 const std::vector<int64>& indices,
int64 penalty,
int64 max_cardinality) {
1679 for (
int i = 0; i < indices.size(); ++i) {
1684 disjunctions_.
push_back({indices, {penalty, max_cardinality}});
1686 index_to_disjunctions_[
index].push_back(disjunction_index);
1688 return disjunction_index;
1691 std::vector<std::pair<int64, int64>>
1693 std::vector<std::pair<int64, int64>> var_index_pairs;
1694 for (
const Disjunction& disjunction : disjunctions_) {
1695 const std::vector<int64>&
var_indices = disjunction.indices;
1699 if (index_to_disjunctions_[v0].size() == 1 &&
1700 index_to_disjunctions_[v1].size() == 1) {
1705 std::sort(var_index_pairs.begin(), var_index_pairs.end());
1706 return var_index_pairs;
1711 for (Disjunction& disjunction : disjunctions_) {
1712 bool has_one_potentially_active_var =
false;
1713 for (
const int64 var_index : disjunction.indices) {
1715 has_one_potentially_active_var =
true;
1719 if (!has_one_potentially_active_var) {
1720 disjunction.value.max_cardinality = 0;
1726 const std::vector<int64>& indices = disjunctions_[disjunction].indices;
1727 const int indices_size = indices.size();
1728 std::vector<IntVar*> disjunction_vars(indices_size);
1729 for (
int i = 0; i < indices_size; ++i) {
1734 const int64 max_cardinality =
1735 disjunctions_[disjunction].value.max_cardinality;
1736 IntVar* no_active_var = solver_->MakeBoolVar();
1737 IntVar* number_active_vars = solver_->MakeIntVar(0, max_cardinality);
1738 solver_->AddConstraint(
1739 solver_->MakeSumEquality(disjunction_vars, number_active_vars));
1740 solver_->AddConstraint(solver_->MakeIsDifferentCstCt(
1741 number_active_vars, max_cardinality, no_active_var));
1742 const int64 penalty = disjunctions_[disjunction].value.penalty;
1744 no_active_var->SetMax(0);
1747 return solver_->MakeProd(no_active_var, penalty)->Var();
1752 const std::vector<int64>& indices,
int64 cost) {
1753 if (!indices.empty()) {
1754 ValuedNodes<int64> same_vehicle_cost;
1756 same_vehicle_cost.indices.push_back(
index);
1758 same_vehicle_cost.value =
cost;
1759 same_vehicle_costs_.push_back(same_vehicle_cost);
1765 auto& allowed_vehicles = allowed_vehicles_[
index];
1766 allowed_vehicles.clear();
1768 allowed_vehicles.insert(vehicle);
1773 AddPickupAndDeliverySetsInternal({pickup}, {delivery});
1782 pickup_delivery_disjunctions_.push_back(
1783 {pickup_disjunction, delivery_disjunction});
1786 void RoutingModel::AddPickupAndDeliverySetsInternal(
1787 const std::vector<int64>& pickups,
const std::vector<int64>& deliveries) {
1788 if (pickups.empty() || deliveries.empty()) {
1792 const int pair_index = pickup_delivery_pairs_.size();
1793 for (
int pickup_index = 0; pickup_index < pickups.size(); pickup_index++) {
1794 const int64 pickup = pickups[pickup_index];
1796 index_to_pickup_index_pairs_[pickup].emplace_back(pair_index, pickup_index);
1798 for (
int delivery_index = 0; delivery_index < deliveries.size();
1800 const int64 delivery = deliveries[delivery_index];
1802 index_to_delivery_index_pairs_[delivery].emplace_back(pair_index,
1805 pickup_delivery_pairs_.push_back({pickups, deliveries});
1809 int64 node_index)
const {
1810 CHECK_LT(node_index, index_to_pickup_index_pairs_.size());
1811 return index_to_pickup_index_pairs_[node_index];
1815 int64 node_index)
const {
1816 CHECK_LT(node_index, index_to_delivery_index_pairs_.size());
1817 return index_to_delivery_index_pairs_[node_index];
1823 vehicle_pickup_delivery_policy_[vehicle] = policy;
1829 for (
int i = 0; i < vehicles_; ++i) {
1837 return vehicle_pickup_delivery_policy_[vehicle];
1842 for (
int i = 0; i <
Nexts().size(); ++i) {
1852 IntVar* RoutingModel::CreateSameVehicleCost(
int vehicle_index) {
1853 const std::vector<int64>& indices =
1854 same_vehicle_costs_[vehicle_index].indices;
1855 CHECK(!indices.empty());
1856 std::vector<IntVar*> vehicle_counts;
1857 solver_->MakeIntVarArray(vehicle_vars_.size() + 1, 0, indices.size() + 1,
1859 std::vector<int64> vehicle_values(vehicle_vars_.size() + 1);
1860 for (
int i = 0; i < vehicle_vars_.size(); ++i) {
1861 vehicle_values[i] = i;
1863 vehicle_values[vehicle_vars_.size()] = -1;
1864 std::vector<IntVar*> vehicle_vars;
1865 vehicle_vars.reserve(indices.size());
1867 vehicle_vars.push_back(vehicle_vars_[
index]);
1869 solver_->AddConstraint(solver_->MakeDistribute(vehicle_vars, vehicle_counts));
1870 std::vector<IntVar*> vehicle_used;
1871 for (
int i = 0; i < vehicle_vars_.size() + 1; ++i) {
1872 vehicle_used.push_back(
1873 solver_->MakeIsGreaterOrEqualCstVar(vehicle_counts[i], 1));
1875 vehicle_used.push_back(solver_->MakeIntConst(-1));
1877 ->MakeProd(solver_->MakeMax(solver_->MakeSum(vehicle_used), 0),
1878 same_vehicle_costs_[vehicle_index].value)
1883 extra_operators_.push_back(ls_operator);
1890 void RoutingModel::AppendHomogeneousArcCosts(
1891 const RoutingSearchParameters&
parameters,
int node_index,
1892 std::vector<IntVar*>* cost_elements) {
1893 CHECK(cost_elements !=
nullptr);
1894 const auto arc_cost_evaluator = [
this, node_index](
int64 next_index) {
1901 IntVar*
const base_cost_var = solver_->MakeIntVar(0,
kint64max);
1902 solver_->AddConstraint(MakeLightElement(
1903 solver_.get(), base_cost_var, nexts_[node_index], arc_cost_evaluator,
1904 [
this]() { return enable_deep_serialization_; }));
1906 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1907 cost_elements->push_back(
var);
1909 IntExpr*
const expr =
1910 solver_->MakeElement(arc_cost_evaluator, nexts_[node_index]);
1911 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
1912 cost_elements->push_back(
var);
1916 void RoutingModel::AppendArcCosts(
const RoutingSearchParameters&
parameters,
1918 std::vector<IntVar*>* cost_elements) {
1919 CHECK(cost_elements !=
nullptr);
1925 IntVar*
const base_cost_var = solver_->MakeIntVar(0,
kint64max);
1926 solver_->AddConstraint(MakeLightElement2(
1927 solver_.get(), base_cost_var, nexts_[node_index],
1928 vehicle_vars_[node_index],
1929 [
this, node_index](
int64 to,
int64 vehicle) {
1930 return GetArcCostForVehicle(node_index, to, vehicle);
1932 [
this]() { return enable_deep_serialization_; }));
1934 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1935 cost_elements->push_back(
var);
1937 IntVar*
const vehicle_class_var =
1941 return SafeGetCostClassInt64OfVehicle(
index);
1943 vehicle_vars_[node_index])
1945 IntExpr*
const expr = solver_->MakeElement(
1949 nexts_[node_index], vehicle_class_var);
1950 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
1951 cost_elements->push_back(
var);
1955 int RoutingModel::GetVehicleStartClass(
int64 start_index)
const {
1956 const int vehicle = index_to_vehicle_[start_index];
1963 std::string RoutingModel::FindErrorInSearchParametersForModel(
1964 const RoutingSearchParameters& search_parameters)
const {
1966 search_parameters.first_solution_strategy();
1967 if (GetFirstSolutionDecisionBuilder(search_parameters) ==
nullptr) {
1968 return absl::StrCat(
1969 "Undefined first solution strategy: ",
1970 FirstSolutionStrategy::Value_Name(first_solution_strategy),
1971 " (int value: ", first_solution_strategy,
")");
1973 if (search_parameters.first_solution_strategy() ==
1974 FirstSolutionStrategy::SWEEP &&
1976 return "Undefined sweep arranger for ROUTING_SWEEP strategy.";
1981 void RoutingModel::QuietCloseModel() {
1992 same_vehicle_components_.SetNumberOfNodes(
model->Size());
1993 for (
const std::string&
name :
model->GetAllDimensionNames()) {
1995 const std::vector<IntVar*>& cumuls = dimension->
cumuls();
1996 for (
int i = 0; i < cumuls.size(); ++i) {
1997 cumul_to_dim_indices_[cumuls[i]] = {dimension, i};
2000 const std::vector<IntVar*>& vehicle_vars =
model->VehicleVars();
2001 for (
int i = 0; i < vehicle_vars.size(); ++i) {
2002 vehicle_var_to_indices_[vehicle_vars[i]] = i;
2004 RegisterInspectors();
2008 const std::vector<int> node_to_same_vehicle_component_id =
2009 same_vehicle_components_.GetComponentIds();
2010 model_->InitSameVehicleGroups(
2011 same_vehicle_components_.GetNumberOfComponents());
2012 for (
int node = 0; node < model_->Size(); ++node) {
2013 model_->SetSameVehicleGroup(node,
2014 node_to_same_vehicle_component_id[node]);
2020 const Constraint*
const constraint)
override {
2024 IntExpr*
const expr)
override {
2026 [](
const IntExpr* expr) {})(expr);
2029 const std::vector<int64>& values)
override {
2031 [](
const std::vector<int64>& int_array) {})(values);
2035 using ExprInspector = std::function<void(
const IntExpr*)>;
2036 using ArrayInspector = std::function<void(
const std::vector<int64>&)>;
2037 using ConstraintInspector = std::function<void()>;
2039 void RegisterInspectors() {
2040 expr_inspectors_[kExpressionArgument] = [
this](
const IntExpr* expr) {
2043 expr_inspectors_[kLeftArgument] = [
this](
const IntExpr* expr) {
2046 expr_inspectors_[kRightArgument] = [
this](
const IntExpr* expr) {
2049 array_inspectors_[kStartsArgument] =
2050 [
this](
const std::vector<int64>& int_array) {
2051 starts_argument_ = int_array;
2053 array_inspectors_[kEndsArgument] =
2054 [
this](
const std::vector<int64>& int_array) {
2055 ends_argument_ = int_array;
2057 constraint_inspectors_[kNotMember] = [
this]() {
2058 std::pair<RoutingDimension*, int> dim_index;
2061 const int index = dim_index.second;
2062 dimension->forbidden_intervals_[
index].InsertIntervals(starts_argument_,
2064 VLOG(2) << dimension->name() <<
" " <<
index <<
": "
2065 << dimension->forbidden_intervals_[
index].DebugString();
2068 starts_argument_.clear();
2069 ends_argument_.clear();
2071 constraint_inspectors_[kEquality] = [
this]() {
2073 int right_index = 0;
2074 if (
gtl::FindCopy(vehicle_var_to_indices_, left_, &left_index) &&
2075 gtl::FindCopy(vehicle_var_to_indices_, right_, &right_index)) {
2076 VLOG(2) <<
"Vehicle variables for " << left_index <<
" and "
2077 << right_index <<
" are equal.";
2078 same_vehicle_components_.AddEdge(left_index, right_index);
2083 constraint_inspectors_[kLessOrEqual] = [
this]() {
2084 std::pair<RoutingDimension*, int> left_index;
2085 std::pair<RoutingDimension*, int> right_index;
2086 if (
gtl::FindCopy(cumul_to_dim_indices_, left_, &left_index) &&
2087 gtl::FindCopy(cumul_to_dim_indices_, right_, &right_index)) {
2089 if (dimension == right_index.first) {
2090 VLOG(2) <<
"For dimension " << dimension->name() <<
", cumul for "
2091 << left_index.second <<
" is less than " << right_index.second
2093 dimension->path_precedence_graph_.AddArc(left_index.second,
2094 right_index.second);
2104 absl::flat_hash_map<const IntExpr*, std::pair<RoutingDimension*, int>>
2105 cumul_to_dim_indices_;
2106 absl::flat_hash_map<const IntExpr*, int> vehicle_var_to_indices_;
2107 absl::flat_hash_map<std::string, ExprInspector> expr_inspectors_;
2108 absl::flat_hash_map<std::string, ArrayInspector> array_inspectors_;
2109 absl::flat_hash_map<std::string, ConstraintInspector> constraint_inspectors_;
2110 const IntExpr*
expr_ =
nullptr;
2111 const IntExpr* left_ =
nullptr;
2112 const IntExpr* right_ =
nullptr;
2113 std::vector<int64> starts_argument_;
2114 std::vector<int64> ends_argument_;
2117 void RoutingModel::DetectImplicitPickupAndDeliveries() {
2118 std::vector<int> non_pickup_delivery_nodes;
2119 for (
int node = 0; node <
Size(); ++node) {
2122 non_pickup_delivery_nodes.push_back(node);
2126 std::set<std::pair<int64, int64>> implicit_pickup_deliveries;
2128 if (dimension->class_evaluators_.size() != 1) {
2133 if (transit ==
nullptr)
continue;
2134 absl::flat_hash_map<int64, std::vector<int64>> nodes_by_positive_demand;
2135 absl::flat_hash_map<int64, std::vector<int64>> nodes_by_negative_demand;
2136 for (
int node : non_pickup_delivery_nodes) {
2139 nodes_by_positive_demand[
demand].push_back(node);
2141 nodes_by_negative_demand[-
demand].push_back(node);
2144 for (
const auto& [
demand, positive_nodes] : nodes_by_positive_demand) {
2145 const std::vector<int64>*
const negative_nodes =
2147 if (negative_nodes !=
nullptr) {
2148 for (
int64 positive_node : positive_nodes) {
2149 for (
int64 negative_node : *negative_nodes) {
2150 implicit_pickup_deliveries.insert({positive_node, negative_node});
2156 implicit_pickup_delivery_pairs_without_alternatives_.clear();
2157 for (
auto [pickup, delivery] : implicit_pickup_deliveries) {
2158 implicit_pickup_delivery_pairs_without_alternatives_.emplace_back(
2159 std::vector<int64>({pickup}), std::vector<int64>({delivery}));
2166 if (!error.empty()) {
2168 LOG(
ERROR) <<
"Invalid RoutingSearchParameters: " << error;
2178 dimension->CloseModel(UsesLightPropagation(
parameters));
2181 ComputeVehicleClasses();
2182 ComputeVehicleTypes();
2183 FinalizeVisitTypes();
2184 vehicle_start_class_callback_ = [
this](
int64 start) {
2185 return GetVehicleStartClass(start);
2188 AddNoCycleConstraintInternal();
2190 const int size =
Size();
2193 for (
int i = 0; i < vehicles_; ++i) {
2194 const int64 start = starts_[i];
2195 const int64 end = ends_[i];
2196 solver_->AddConstraint(
2197 solver_->MakeEquality(vehicle_vars_[start], solver_->MakeIntConst(i)));
2198 solver_->AddConstraint(
2199 solver_->MakeEquality(vehicle_vars_[end], solver_->MakeIntConst(i)));
2200 solver_->AddConstraint(
2201 solver_->MakeIsDifferentCstCt(nexts_[start], end, vehicle_active_[i]));
2202 if (consider_empty_route_costs_[i]) {
2203 vehicle_costs_considered_[i]->SetMin(1);
2205 solver_->AddConstraint(solver_->MakeEquality(
2206 vehicle_active_[i], vehicle_costs_considered_[i]));
2211 if (vehicles_ > max_active_vehicles_) {
2212 solver_->AddConstraint(
2213 solver_->MakeSumLessOrEqual(vehicle_active_, max_active_vehicles_));
2221 if (vehicles_ > 1) {
2222 std::vector<IntVar*> zero_transit(size, solver_->MakeIntConst(0));
2223 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
2224 nexts_, active_, vehicle_vars_, zero_transit));
2229 for (
int i = 0; i < size; ++i) {
2231 active_[i]->SetValue(1);
2237 const absl::flat_hash_set<VisitTypePolicy>*
const infeasible_policies =
2239 if (infeasible_policies !=
nullptr &&
2241 active_[i]->SetValue(0);
2246 for (
int i = 0; i < allowed_vehicles_.size(); ++i) {
2247 const auto& allowed_vehicles = allowed_vehicles_[i];
2248 if (!allowed_vehicles.empty()) {
2250 vehicles.reserve(allowed_vehicles.size() + 1);
2252 for (
int vehicle : allowed_vehicles) {
2260 for (
int i = 0; i < size; ++i) {
2262 solver_->AddConstraint(solver_->RevAlloc(
2263 new DifferentFromValues(solver_.get(), nexts_[i], starts_)));
2265 solver_->AddConstraint(
2266 solver_->MakeIsDifferentCstCt(nexts_[i], i, active_[i]));
2271 for (
int i = 0; i < size; ++i) {
2272 solver_->AddConstraint(
2273 solver_->MakeIsDifferentCstCt(vehicle_vars_[i], -1, active_[i]));
2277 solver_->AddConstraint(
2282 for (
int i = 0; i < vehicles_; ++i) {
2283 std::vector<int64> forbidden_ends;
2284 forbidden_ends.reserve(vehicles_ - 1);
2285 for (
int j = 0; j < vehicles_; ++j) {
2287 forbidden_ends.push_back(ends_[j]);
2290 solver_->AddConstraint(solver_->RevAlloc(
new DifferentFromValues(
2291 solver_.get(), nexts_[starts_[i]], std::move(forbidden_ends))));
2295 for (
const int64 end : ends_) {
2296 is_bound_to_end_[end]->SetValue(1);
2299 std::vector<IntVar*> cost_elements;
2301 if (vehicles_ > 0) {
2302 for (
int node_index = 0; node_index < size; ++node_index) {
2304 AppendHomogeneousArcCosts(
parameters, node_index, &cost_elements);
2306 AppendArcCosts(
parameters, node_index, &cost_elements);
2309 if (vehicle_amortized_cost_factors_set_) {
2310 std::vector<IntVar*> route_lengths;
2311 solver_->MakeIntVarArray(vehicles_, 0, size, &route_lengths);
2312 solver_->AddConstraint(
2313 solver_->MakeDistribute(vehicle_vars_, route_lengths));
2314 std::vector<IntVar*> vehicle_used;
2315 for (
int i = 0; i < vehicles_; i++) {
2317 vehicle_used.push_back(
2318 solver_->MakeIsGreaterCstVar(route_lengths[i], 2));
2321 ->MakeProd(solver_->MakeOpposite(solver_->MakeSquare(
2322 solver_->MakeSum(route_lengths[i], -2))),
2323 quadratic_cost_factor_of_vehicle_[i])
2325 cost_elements.push_back(
var);
2327 IntVar*
const vehicle_usage_cost =
2328 solver_->MakeScalProd(vehicle_used, linear_cost_factor_of_vehicle_)
2330 cost_elements.push_back(vehicle_usage_cost);
2335 dimension->SetupGlobalSpanCost(&cost_elements);
2336 dimension->SetupSlackAndDependentTransitCosts();
2337 const std::vector<int64>& span_costs =
2338 dimension->vehicle_span_cost_coefficients();
2339 const std::vector<int64>& span_ubs = dimension->vehicle_span_upper_bounds();
2340 const bool has_span_constraint =
2341 std::any_of(span_costs.begin(), span_costs.end(),
2342 [](
int64 coeff) { return coeff != 0; }) ||
2343 std::any_of(span_ubs.begin(), span_ubs.end(),
2344 [](
int64 value) { return value < kint64max; }) ||
2345 dimension->HasSoftSpanUpperBounds() ||
2346 dimension->HasQuadraticCostSoftSpanUpperBounds();
2347 if (has_span_constraint) {
2348 std::vector<IntVar*> spans(
vehicles(),
nullptr);
2349 std::vector<IntVar*> total_slacks(
vehicles(),
nullptr);
2351 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2353 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2355 if (span_costs[vehicle] != 0) {
2356 total_slacks[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2359 if (dimension->HasSoftSpanUpperBounds()) {
2360 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2361 if (spans[vehicle])
continue;
2363 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2364 if (bound_cost.
cost == 0)
continue;
2365 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2368 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2369 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2370 if (spans[vehicle])
continue;
2372 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2373 if (bound_cost.
cost == 0)
continue;
2374 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2377 solver_->AddConstraint(
2381 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2382 if (!spans[vehicle] && !total_slacks[vehicle])
continue;
2383 if (spans[vehicle]) {
2392 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2393 if (span_costs[vehicle] == 0)
continue;
2394 DCHECK(total_slacks[vehicle] !=
nullptr);
2395 IntVar*
const slack_amount =
2397 ->MakeProd(vehicle_costs_considered_[vehicle],
2398 total_slacks[vehicle])
2400 IntVar*
const slack_cost =
2401 solver_->MakeProd(slack_amount, span_costs[vehicle])->Var();
2402 cost_elements.push_back(slack_cost);
2404 span_costs[vehicle]);
2406 if (dimension->HasSoftSpanUpperBounds()) {
2407 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2408 const auto bound_cost =
2409 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2410 if (bound_cost.cost == 0 || bound_cost.bound ==
kint64max)
continue;
2411 DCHECK(spans[vehicle] !=
nullptr);
2414 IntVar*
const span_violation_amount =
2417 vehicle_costs_considered_[vehicle],
2419 solver_->MakeSum(spans[vehicle], -bound_cost.bound),
2422 IntVar*
const span_violation_cost =
2423 solver_->MakeProd(span_violation_amount, bound_cost.cost)->Var();
2424 cost_elements.push_back(span_violation_cost);
2429 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2430 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2431 const auto bound_cost =
2432 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2433 if (bound_cost.cost == 0 || bound_cost.bound ==
kint64max)
continue;
2434 DCHECK(spans[vehicle] !=
nullptr);
2437 IntExpr* max0 = solver_->MakeMax(
2438 solver_->MakeSum(spans[vehicle], -bound_cost.bound), 0);
2439 IntVar*
const squared_span_violation_amount =
2441 ->MakeProd(vehicle_costs_considered_[vehicle],
2442 solver_->MakeSquare(max0))
2444 IntVar*
const span_violation_cost =
2445 solver_->MakeProd(squared_span_violation_amount, bound_cost.cost)
2447 cost_elements.push_back(span_violation_cost);
2456 IntVar* penalty_var = CreateDisjunction(i);
2457 if (penalty_var !=
nullptr) {
2458 cost_elements.push_back(penalty_var);
2463 dimension->SetupCumulVarSoftLowerBoundCosts(&cost_elements);
2464 dimension->SetupCumulVarSoftUpperBoundCosts(&cost_elements);
2465 dimension->SetupCumulVarPiecewiseLinearCosts(&cost_elements);
2468 for (
int i = 0; i < same_vehicle_costs_.size(); ++i) {
2469 cost_elements.push_back(CreateSameVehicleCost(i));
2471 cost_ = solver_->MakeSum(cost_elements)->Var();
2475 std::vector<std::pair<int, int>> pickup_delivery_precedences;
2476 for (
const auto& pair : pickup_delivery_pairs_) {
2477 DCHECK(!pair.first.empty() && !pair.second.empty());
2478 for (
int pickup : pair.first) {
2479 for (
int delivery : pair.second) {
2480 pickup_delivery_precedences.emplace_back(pickup, delivery);
2484 std::vector<int> lifo_vehicles;
2485 std::vector<int> fifo_vehicles;
2486 for (
int i = 0; i < vehicles_; ++i) {
2487 switch (vehicle_pickup_delivery_policy_[i]) {
2491 lifo_vehicles.push_back(
Start(i));
2494 fifo_vehicles.push_back(
Start(i));
2498 solver_->AddConstraint(solver_->MakePathPrecedenceConstraint(
2499 nexts_, pickup_delivery_precedences, lifo_vehicles, fifo_vehicles));
2502 enable_deep_serialization_ =
false;
2503 std::unique_ptr<RoutingModelInspector> inspector(
2505 solver_->Accept(inspector.get());
2506 enable_deep_serialization_ =
true;
2512 dimension->GetPathPrecedenceGraph();
2513 std::vector<std::pair<int, int>> path_precedences;
2515 for (
const auto head : graph[
tail]) {
2516 path_precedences.emplace_back(
tail,
head);
2519 if (!path_precedences.empty()) {
2520 solver_->AddConstraint(solver_->MakePathTransitPrecedenceConstraint(
2521 nexts_, dimension->transits(), path_precedences));
2526 dimension->GetNodePrecedences()) {
2527 const int64 first_node = node_precedence.first_node;
2528 const int64 second_node = node_precedence.second_node;
2529 IntExpr*
const nodes_are_selected =
2530 solver_->MakeMin(active_[first_node], active_[second_node]);
2531 IntExpr*
const cumul_difference = solver_->MakeDifference(
2532 dimension->CumulVar(second_node), dimension->CumulVar(first_node));
2533 IntVar*
const cumul_difference_is_ge_offset =
2534 solver_->MakeIsGreaterOrEqualCstVar(cumul_difference,
2535 node_precedence.offset);
2538 solver_->AddConstraint(solver_->MakeLessOrEqual(
2539 nodes_are_selected->
Var(), cumul_difference_is_ge_offset));
2543 DetectImplicitPickupAndDeliveries();
2552 CreateFirstSolutionDecisionBuilders(
parameters);
2553 error = FindErrorInSearchParametersForModel(
parameters);
2554 if (!error.empty()) {
2556 LOG(
ERROR) <<
"Invalid RoutingSearchParameters for this model: " << error;
2563 Link(std::pair<int, int> link,
double value,
int vehicle_class,
2567 vehicle_class(vehicle_class),
2568 start_depot(start_depot),
2569 end_depot(end_depot) {}
2593 bool check_assignment,
int64 num_indices,
2594 const std::vector<Link>& links_list)
2595 : assignment_(assignment),
2597 check_assignment_(check_assignment),
2598 solver_(model_->
solver()),
2599 num_indices_(num_indices),
2600 links_list_(links_list),
2601 nexts_(model_->
Nexts()),
2602 in_route_(num_indices_, -1),
2604 index_to_chain_index_(num_indices, -1),
2605 index_to_vehicle_class_index_(num_indices, -1) {
2607 const std::vector<std::string> dimension_names =
2608 model_->GetAllDimensionNames();
2609 dimensions_.assign(dimension_names.size(),
nullptr);
2610 for (
int i = 0; i < dimension_names.size(); ++i) {
2611 dimensions_[i] = &model_->GetDimensionOrDie(dimension_names[i]);
2614 cumuls_.resize(dimensions_.size());
2615 for (std::vector<int64>& cumuls :
cumuls_) {
2616 cumuls.resize(num_indices_);
2618 new_possible_cumuls_.resize(dimensions_.size());
2624 model_->solver()->TopPeriodicCheck();
2627 if (!model_->IsStart(
index) && !model_->IsEnd(
index)) {
2628 std::vector<int> route(1,
index);
2629 routes_.push_back(route);
2630 in_route_[
index] = routes_.size() - 1;
2634 for (
const Link& link : links_list_) {
2635 model_->solver()->TopPeriodicCheck();
2636 const int index1 = link.link.first;
2637 const int index2 = link.link.second;
2638 const int vehicle_class = link.vehicle_class;
2639 const int64 start_depot = link.start_depot;
2640 const int64 end_depot = link.end_depot;
2643 if (index_to_vehicle_class_index_[index1] < 0) {
2644 for (
int dimension_index = 0; dimension_index < dimensions_.size();
2645 ++dimension_index) {
2646 cumuls_[dimension_index][index1] =
2647 std::max(dimensions_[dimension_index]->GetTransitValue(
2648 start_depot, index1, 0),
2649 dimensions_[dimension_index]->CumulVar(index1)->Min());
2652 if (index_to_vehicle_class_index_[index2] < 0) {
2653 for (
int dimension_index = 0; dimension_index < dimensions_.size();
2654 ++dimension_index) {
2655 cumuls_[dimension_index][index2] =
2656 std::max(dimensions_[dimension_index]->GetTransitValue(
2657 start_depot, index2, 0),
2658 dimensions_[dimension_index]->CumulVar(index2)->Min());
2662 const int route_index1 = in_route_[index1];
2663 const int route_index2 = in_route_[index2];
2665 route_index1 >= 0 && route_index2 >= 0 &&
2666 FeasibleMerge(routes_[route_index1], routes_[route_index2], index1,
2667 index2, route_index1, route_index2, vehicle_class,
2668 start_depot, end_depot);
2669 if (Merge(merge, route_index1, route_index2)) {
2670 index_to_vehicle_class_index_[index1] = vehicle_class;
2671 index_to_vehicle_class_index_[index2] = vehicle_class;
2675 model_->solver()->TopPeriodicCheck();
2679 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
2681 final_chains_.push_back(chains_[chain_index]);
2684 std::sort(final_chains_.begin(), final_chains_.end(), ChainComparator);
2685 for (
int route_index = 0; route_index < routes_.size(); ++route_index) {
2687 final_routes_.push_back(routes_[route_index]);
2690 std::sort(final_routes_.begin(), final_routes_.end(), RouteComparator);
2692 const int extra_vehicles =
std::max(
2693 0,
static_cast<int>(final_chains_.size()) - model_->vehicles());
2695 int chain_index = 0;
2696 for (chain_index = extra_vehicles; chain_index < final_chains_.size();
2698 if (chain_index - extra_vehicles >= model_->vehicles()) {
2701 const int start = final_chains_[chain_index].head;
2702 const int end = final_chains_[chain_index].tail;
2704 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
2705 assignment_->SetValue(
2706 model_->NextVar(model_->Start(chain_index - extra_vehicles)), start);
2707 assignment_->Add(nexts_[end]);
2708 assignment_->SetValue(nexts_[end],
2709 model_->End(chain_index - extra_vehicles));
2713 for (
int route_index = 0; route_index < final_routes_.size();
2715 if (chain_index - extra_vehicles >= model_->vehicles()) {
2718 DCHECK_LT(route_index, final_routes_.size());
2719 const int head = final_routes_[route_index].front();
2720 const int tail = final_routes_[route_index].back();
2723 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
2724 assignment_->SetValue(
2725 model_->NextVar(model_->Start(chain_index - extra_vehicles)),
head);
2726 assignment_->Add(nexts_[
tail]);
2727 assignment_->SetValue(nexts_[
tail],
2728 model_->End(chain_index - extra_vehicles));
2736 if (!assignment_->Contains(
next)) {
2737 assignment_->Add(
next);
2746 return final_routes_;
2750 enum MergeStatus { FIRST_SECOND, SECOND_FIRST, NO_MERGE };
2753 bool operator()(
const std::vector<int>& route1,
2754 const std::vector<int>& route2)
const {
2755 return (route1.size() < route2.size());
2766 bool operator()(
const Chain& chain1,
const Chain& chain2)
const {
2767 return (chain1.nodes < chain2.nodes);
2771 bool Head(
int node)
const {
2772 return (node == routes_[in_route_[node]].front());
2775 bool Tail(
int node)
const {
2776 return (node == routes_[in_route_[node]].back());
2779 bool FeasibleRoute(
const std::vector<int>& route,
int64 route_cumul,
2780 int dimension_index) {
2782 std::vector<int>::const_iterator it = route.begin();
2783 int64 cumul = route_cumul;
2784 while (it != route.end()) {
2785 const int previous = *it;
2786 const int64 cumul_previous = cumul;
2790 if (it == route.end()) {
2793 const int next = *it;
2794 int64 available_from_previous =
2795 cumul_previous + dimension.GetTransitValue(previous,
next, 0);
2796 int64 available_cumul_next =
2799 const int64 slack = available_cumul_next - available_from_previous;
2800 if (slack > dimension.SlackVar(previous)->Max()) {
2801 available_cumul_next =
2802 available_from_previous + dimension.SlackVar(previous)->Max();
2805 if (available_cumul_next > dimension.CumulVar(
next)->Max()) {
2808 if (available_cumul_next <=
cumuls_[dimension_index][
next]) {
2811 cumul = available_cumul_next;
2816 bool CheckRouteConnection(
const std::vector<int>& route1,
2817 const std::vector<int>& route2,
int dimension_index,
2819 const int tail1 = route1.back();
2820 const int head2 = route2.front();
2821 const int tail2 = route2.back();
2823 int non_depot_node = -1;
2824 for (
int node = 0; node < num_indices_; ++node) {
2825 if (!model_->IsStart(node) && !model_->IsEnd(node)) {
2826 non_depot_node = node;
2831 const int64 depot_threashold =
2832 std::max(dimension.SlackVar(non_depot_node)->Max(),
2833 dimension.CumulVar(non_depot_node)->Max());
2835 int64 available_from_tail1 =
cumuls_[dimension_index][tail1] +
2836 dimension.GetTransitValue(tail1, head2, 0);
2837 int64 new_available_cumul_head2 =
2840 const int64 slack = new_available_cumul_head2 - available_from_tail1;
2841 if (slack > dimension.SlackVar(tail1)->Max()) {
2842 new_available_cumul_head2 =
2843 available_from_tail1 + dimension.SlackVar(tail1)->Max();
2846 bool feasible_route =
true;
2847 if (new_available_cumul_head2 > dimension.CumulVar(head2)->Max()) {
2850 if (new_available_cumul_head2 <=
cumuls_[dimension_index][head2]) {
2855 FeasibleRoute(route2, new_available_cumul_head2, dimension_index);
2856 const int64 new_possible_cumul_tail2 =
2858 ? new_possible_cumuls_[dimension_index][tail2]
2859 :
cumuls_[dimension_index][tail2];
2861 if (!feasible_route || (new_possible_cumul_tail2 +
2862 dimension.GetTransitValue(tail2, end_depot, 0) >
2863 depot_threashold)) {
2869 bool FeasibleMerge(
const std::vector<int>& route1,
2870 const std::vector<int>& route2,
int node1,
int node2,
2871 int route_index1,
int route_index2,
int vehicle_class,
2873 if ((route_index1 == route_index2) || !(Tail(node1) && Head(node2))) {
2878 if (!((index_to_vehicle_class_index_[node1] == -1 &&
2879 index_to_vehicle_class_index_[node2] == -1) ||
2880 (index_to_vehicle_class_index_[node1] == vehicle_class &&
2881 index_to_vehicle_class_index_[node2] == -1) ||
2882 (index_to_vehicle_class_index_[node1] == -1 &&
2883 index_to_vehicle_class_index_[node2] == vehicle_class) ||
2884 (index_to_vehicle_class_index_[node1] == vehicle_class &&
2885 index_to_vehicle_class_index_[node2] == vehicle_class))) {
2891 for (
int dimension_index = 0; dimension_index < dimensions_.size();
2892 ++dimension_index) {
2893 new_possible_cumuls_[dimension_index].clear();
2894 merge = merge && CheckRouteConnection(route1, route2, dimension_index,
2895 start_depot, end_depot);
2903 bool CheckTempAssignment(Assignment*
const temp_assignment,
2904 int new_chain_index,
int old_chain_index,
int head1,
2905 int tail1,
int head2,
int tail2) {
2908 if (new_chain_index >= model_->vehicles())
return false;
2909 const int start = head1;
2910 temp_assignment->Add(model_->NextVar(model_->Start(new_chain_index)));
2911 temp_assignment->SetValue(model_->NextVar(model_->Start(new_chain_index)),
2913 temp_assignment->Add(nexts_[tail1]);
2914 temp_assignment->SetValue(nexts_[tail1], head2);
2915 temp_assignment->Add(nexts_[tail2]);
2916 temp_assignment->SetValue(nexts_[tail2], model_->End(new_chain_index));
2917 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
2918 if ((chain_index != new_chain_index) &&
2919 (chain_index != old_chain_index) &&
2921 const int start = chains_[chain_index].head;
2922 const int end = chains_[chain_index].tail;
2923 temp_assignment->Add(model_->NextVar(model_->Start(chain_index)));
2924 temp_assignment->SetValue(model_->NextVar(model_->Start(chain_index)),
2926 temp_assignment->Add(nexts_[end]);
2927 temp_assignment->SetValue(nexts_[end], model_->End(chain_index));
2930 return solver_->Solve(solver_->MakeRestoreAssignment(temp_assignment));
2933 bool UpdateAssignment(
const std::vector<int>& route1,
2934 const std::vector<int>& route2) {
2935 bool feasible =
true;
2936 const int head1 = route1.front();
2937 const int tail1 = route1.back();
2938 const int head2 = route2.front();
2939 const int tail2 = route2.back();
2940 const int chain_index1 = index_to_chain_index_[head1];
2941 const int chain_index2 = index_to_chain_index_[head2];
2942 if (chain_index1 < 0 && chain_index2 < 0) {
2943 const int chain_index = chains_.size();
2944 if (check_assignment_) {
2945 Assignment*
const temp_assignment =
2946 solver_->MakeAssignment(assignment_);
2947 feasible = CheckTempAssignment(temp_assignment, chain_index, -1, head1,
2948 tail1, head2, tail2);
2955 index_to_chain_index_[head1] = chain_index;
2956 index_to_chain_index_[tail2] = chain_index;
2957 chains_.push_back(chain);
2959 }
else if (chain_index1 >= 0 && chain_index2 < 0) {
2960 if (check_assignment_) {
2961 Assignment*
const temp_assignment =
2962 solver_->MakeAssignment(assignment_);
2964 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
2965 head1, tail1, head2, tail2);
2968 index_to_chain_index_[tail2] = chain_index1;
2969 chains_[chain_index1].head = head1;
2970 chains_[chain_index1].tail = tail2;
2971 ++chains_[chain_index1].nodes;
2973 }
else if (chain_index1 < 0 && chain_index2 >= 0) {
2974 if (check_assignment_) {
2975 Assignment*
const temp_assignment =
2976 solver_->MakeAssignment(assignment_);
2978 CheckTempAssignment(temp_assignment, chain_index2, chain_index1,
2979 head1, tail1, head2, tail2);
2982 index_to_chain_index_[head1] = chain_index2;
2983 chains_[chain_index2].head = head1;
2984 chains_[chain_index2].tail = tail2;
2985 ++chains_[chain_index2].nodes;
2988 if (check_assignment_) {
2989 Assignment*
const temp_assignment =
2990 solver_->MakeAssignment(assignment_);
2992 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
2993 head1, tail1, head2, tail2);
2996 index_to_chain_index_[tail2] = chain_index1;
2997 chains_[chain_index1].head = head1;
2998 chains_[chain_index1].tail = tail2;
2999 chains_[chain_index1].nodes += chains_[chain_index2].nodes;
3000 deleted_chains_.insert(chain_index2);
3004 assignment_->Add(nexts_[tail1]);
3005 assignment_->SetValue(nexts_[tail1], head2);
3010 bool Merge(
bool merge,
int index1,
int index2) {
3012 if (UpdateAssignment(routes_[index1], routes_[index2])) {
3014 for (
const int node : routes_[index2]) {
3015 in_route_[node] = index1;
3016 routes_[index1].push_back(node);
3018 for (
int dimension_index = 0; dimension_index < dimensions_.size();
3019 ++dimension_index) {
3020 for (
const std::pair<int, int64> new_possible_cumul :
3021 new_possible_cumuls_[dimension_index]) {
3022 cumuls_[dimension_index][new_possible_cumul.first] =
3023 new_possible_cumul.second;
3026 deleted_routes_.insert(index2);
3033 Assignment*
const assignment_;
3035 const bool check_assignment_;
3036 Solver*
const solver_;
3037 const int64 num_indices_;
3038 const std::vector<Link> links_list_;
3039 std::vector<IntVar*> nexts_;
3040 std::vector<const RoutingDimension*> dimensions_;
3041 std::vector<std::vector<int64>>
cumuls_;
3042 std::vector<absl::flat_hash_map<int, int64>> new_possible_cumuls_;
3043 std::vector<std::vector<int>> routes_;
3044 std::vector<int> in_route_;
3045 absl::flat_hash_set<int> deleted_routes_;
3046 std::vector<std::vector<int>> final_routes_;
3047 std::vector<Chain> chains_;
3048 absl::flat_hash_set<int> deleted_chains_;
3049 std::vector<Chain> final_chains_;
3050 std::vector<int> index_to_chain_index_;
3051 std::vector<int> index_to_vehicle_class_index_;
3057 :
index(
index), angle(angle), distance(distance) {}
3078 : coordinates_(2 * points.size(), 0), sectors_(1) {
3079 for (
int64 i = 0; i < points.size(); ++i) {
3080 coordinates_[2 * i] = points[i].first;
3081 coordinates_[2 * i + 1] = points[i].second;
3088 const double pi_rad = 3.14159265;
3090 const int x0 = coordinates_[0];
3091 const int y0 = coordinates_[1];
3093 std::vector<SweepIndex> sweep_indices;
3094 for (
int64 index = 0; index < static_cast<int>(coordinates_.size()) / 2;
3096 const int x = coordinates_[2 *
index];
3097 const int y = coordinates_[2 *
index + 1];
3098 const double x_delta = x - x0;
3099 const double y_delta = y - y0;
3100 double square_distance = x_delta * x_delta + y_delta * y_delta;
3101 double angle = square_distance == 0 ? 0 : std::atan2(y_delta, x_delta);
3102 angle = angle >= 0 ? angle : 2 * pi_rad + angle;
3104 sweep_indices.push_back(sweep_index);
3106 std::sort(sweep_indices.begin(), sweep_indices.end(),
3109 const int size =
static_cast<int>(sweep_indices.size()) / sectors_;
3110 for (
int sector = 0; sector < sectors_; ++sector) {
3111 std::vector<SweepIndex> cluster;
3112 std::vector<SweepIndex>::iterator begin =
3113 sweep_indices.begin() + sector * size;
3114 std::vector<SweepIndex>::iterator end =
3115 sector == sectors_ - 1 ? sweep_indices.end()
3116 : sweep_indices.begin() + (sector + 1) * size;
3119 for (
const SweepIndex& sweep_index : sweep_indices) {
3120 indices->push_back(sweep_index.index);
3130 : model_(
model), check_assignment_(check_assignment) {}
3139 route_constructor_ = absl::make_unique<RouteConstructor>(
3140 assignment, model_, check_assignment_, num_indices_, links_);
3142 route_constructor_->Construct();
3143 route_constructor_.reset(
nullptr);
3152 const int depot = model_->
GetDepot();
3154 if (absl::GetFlag(FLAGS_sweep_sectors) > 0 &&
3155 absl::GetFlag(FLAGS_sweep_sectors) < num_indices_) {
3158 std::vector<int64> indices;
3160 for (
int i = 0; i < indices.size() - 1; ++i) {
3161 const int64 first = indices[i];
3162 const int64 second = indices[i + 1];
3163 if ((model_->
IsStart(first) || !model_->
IsEnd(first)) &&
3165 if (first != depot && second != depot) {
3166 Link link(std::make_pair(first, second), 0, 0, depot, depot);
3167 links_.push_back(link);
3173 RoutingModel*
const model_;
3174 std::unique_ptr<RouteConstructor> route_constructor_;
3175 const bool check_assignment_;
3177 std::vector<Link> links_;
3185 class AllUnperformed :
public DecisionBuilder {
3188 explicit AllUnperformed(RoutingModel*
const model) : model_(
model) {}
3189 ~AllUnperformed()
override {}
3190 Decision* Next(Solver*
const solver)
override {
3193 model_->CostVar()->FreezeQueue();
3194 for (
int i = 0; i < model_->Size(); ++i) {
3195 if (!model_->IsStart(i)) {
3196 model_->ActiveVar(i)->SetValue(0);
3199 model_->CostVar()->UnfreezeQueue();
3204 RoutingModel*
const model_;
3209 monitors_.push_back(monitor);
3215 AtSolutionCallbackMonitor(
Solver* solver, std::function<
void()>
callback)
3217 bool AtSolution()
override {
3223 std::function<void()> callback_;
3229 new AtSolutionCallbackMonitor(solver_.get(), std::move(
callback))));
3239 std::vector<const Assignment*>* solutions) {
3244 absl::Duration GetTimeLimit(
const RoutingSearchParameters&
parameters) {
3245 if (!
parameters.has_time_limit())
return absl::InfiniteDuration();
3249 absl::Duration GetLnsTimeLimit(
const RoutingSearchParameters&
parameters) {
3250 if (!
parameters.has_lns_time_limit())
return absl::InfiniteDuration();
3257 void MakeAllUnperformed(
const RoutingModel*
model, Assignment* assignment) {
3258 assignment->Clear();
3259 for (
int i = 0; i <
model->Nexts().size(); ++i) {
3260 if (!
model->IsStart(i)) {
3261 assignment->Add(
model->NextVar(i))->SetValue(i);
3264 for (
int vehicle = 0; vehicle <
model->vehicles(); ++vehicle) {
3265 assignment->Add(
model->NextVar(
model->Start(vehicle)))
3266 ->SetValue(
model->End(vehicle));
3271 bool RoutingModel::AppendAssignmentIfFeasible(
3272 const Assignment& assignment,
3273 std::vector<std::unique_ptr<Assignment>>* assignments) {
3275 solver_->Solve(restore_tmp_assignment_, collect_one_assignment_,
3276 GetOrCreateLimit());
3278 assignments->push_back(absl::make_unique<Assignment>(solver_.get()));
3279 assignments->back()->Copy(collect_one_assignment_->
solution(0));
3285 void RoutingModel::LogSolution(
const RoutingSearchParameters&
parameters,
3286 const std::string& description,
3289 const double cost_scaling_factor =
parameters.log_cost_scaling_factor();
3290 const double cost_offset =
parameters.log_cost_offset();
3291 const std::string cost_string =
3292 cost_scaling_factor == 1.0 && cost_offset == 0.0
3293 ? absl::StrCat(solution_cost)
3295 "%d (%.8lf)", solution_cost,
3296 cost_scaling_factor * (solution_cost + cost_offset));
3298 "%s (%s, time = %d ms, memory used = %s)", description, cost_string,
3299 solver_->wall_time() - start_time_ms, memory_str);
3304 std::vector<const Assignment*>* solutions) {
3305 const int64 start_time_ms = solver_->wall_time();
3308 if (solutions !=
nullptr) solutions->clear();
3322 const int time_limit_shares = 1 + !global_dimension_optimizers_.empty() +
3323 !local_dimension_optimizers_.empty();
3324 const absl::Duration first_solution_lns_time_limit =
3327 first_solution_lns_limit_->
UpdateLimits(first_solution_lns_time_limit,
3330 std::vector<std::unique_ptr<Assignment>> solution_pool;
3332 if (
nullptr == assignment) {
3333 bool solution_found =
false;
3336 AppendAssignmentIfFeasible(matching, &solution_pool)) {
3338 LogSolution(
parameters,
"Min-Cost Flow Solution",
3339 solution_pool.back()->ObjectiveValue(), start_time_ms);
3341 solution_found =
true;
3343 if (!solution_found) {
3347 MakeAllUnperformed(
this, &unperformed);
3348 if (AppendAssignmentIfFeasible(unperformed, &solution_pool) &&
3350 LogSolution(
parameters,
"All Unperformed Solution",
3351 solution_pool.back()->ObjectiveValue(), start_time_ms);
3353 const absl::Duration elapsed_time =
3354 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3355 const absl::Duration time_left =
3357 if (time_left >= absl::ZeroDuration()) {
3361 solver_->Solve(solve_db_, monitors_);
3366 solver_->Solve(improve_db_, monitors_);
3371 const int solution_count = collect_assignments_->
solution_count();
3373 solution_count >= 1 ? collect_assignments_->
solution(solution_count - 1)
3377 AppendAssignmentIfFeasible(sat_solution, &solution_pool) &&
3379 LogSolution(
parameters,
"SAT", solution_pool.back()->ObjectiveValue(),
3384 const absl::Duration elapsed_time =
3385 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3386 const int solution_count = collect_assignments_->
solution_count();
3387 if (solution_count >= 1 || !solution_pool.empty()) {
3389 if (solutions !=
nullptr) {
3390 for (
int i = 0; i < solution_count; ++i) {
3391 solutions->push_back(
3392 solver_->MakeAssignment(collect_assignments_->
solution(i)));
3394 for (
const auto& solution : solution_pool) {
3395 if (solutions->empty() ||
3396 solution->ObjectiveValue() < solutions->back()->ObjectiveValue()) {
3397 solutions->push_back(solver_->MakeAssignment(solution.get()));
3400 return solutions->back();
3403 solution_count >= 1 ? collect_assignments_->
solution(solution_count - 1)
3405 for (
const auto& solution : solution_pool) {
3406 if (best_assignment ==
nullptr ||
3408 best_assignment = solution.get();
3411 return solver_->MakeAssignment(best_assignment);
3413 if (elapsed_time >= GetTimeLimit(
parameters)) {
3425 const int size =
Size();
3431 source_model->
Nexts());
3433 std::vector<IntVar*> source_vars(size + size + vehicles_);
3434 std::vector<IntVar*> target_vars(size + size + vehicles_);
3444 source_assignment, source_vars);
3462 LOG(
WARNING) <<
"Non-closed model not supported.";
3466 LOG(
WARNING) <<
"Non-homogeneous vehicle costs not supported";
3469 if (!disjunctions_.
empty()) {
3471 <<
"Node disjunction constraints or optional nodes not supported.";
3474 const int num_nodes =
Size() + vehicles_;
3481 std::unique_ptr<IntVarIterator> iterator(
3482 nexts_[
tail]->MakeDomainIterator(
false));
3505 return linear_sum_assignment.
GetCost();
3510 bool RoutingModel::RouteCanBeUsedByVehicle(
const Assignment& assignment,
3511 int start_index,
int vehicle)
const {
3513 IsStart(start_index) ?
Next(assignment, start_index) : start_index;
3514 while (!
IsEnd(current_index)) {
3516 if (!vehicle_var->
Contains(vehicle)) {
3519 const int next_index =
Next(assignment, current_index);
3520 CHECK_NE(next_index, current_index) <<
"Inactive node inside a route";
3521 current_index = next_index;
3526 bool RoutingModel::ReplaceUnusedVehicle(
3527 int unused_vehicle,
int active_vehicle,
3528 Assignment*
const compact_assignment)
const {
3529 CHECK(compact_assignment !=
nullptr);
3533 const int unused_vehicle_start =
Start(unused_vehicle);
3534 IntVar*
const unused_vehicle_start_var =
NextVar(unused_vehicle_start);
3535 const int unused_vehicle_end =
End(unused_vehicle);
3536 const int active_vehicle_start =
Start(active_vehicle);
3537 const int active_vehicle_end =
End(active_vehicle);
3538 IntVar*
const active_vehicle_start_var =
NextVar(active_vehicle_start);
3539 const int active_vehicle_next =
3540 compact_assignment->Value(active_vehicle_start_var);
3541 compact_assignment->SetValue(unused_vehicle_start_var, active_vehicle_next);
3542 compact_assignment->SetValue(active_vehicle_start_var,
End(active_vehicle));
3545 int current_index = active_vehicle_next;
3546 while (!
IsEnd(current_index)) {
3547 IntVar*
const vehicle_var =
VehicleVar(current_index);
3548 compact_assignment->SetValue(vehicle_var, unused_vehicle);
3549 const int next_index =
Next(*compact_assignment, current_index);
3550 if (
IsEnd(next_index)) {
3551 IntVar*
const last_next_var =
NextVar(current_index);
3552 compact_assignment->SetValue(last_next_var,
End(unused_vehicle));
3554 current_index = next_index;
3559 const std::vector<IntVar*>& transit_variables = dimension->transits();
3560 IntVar*
const unused_vehicle_transit_var =
3561 transit_variables[unused_vehicle_start];
3562 IntVar*
const active_vehicle_transit_var =
3563 transit_variables[active_vehicle_start];
3564 const bool contains_unused_vehicle_transit_var =
3565 compact_assignment->Contains(unused_vehicle_transit_var);
3566 const bool contains_active_vehicle_transit_var =
3567 compact_assignment->Contains(active_vehicle_transit_var);
3568 if (contains_unused_vehicle_transit_var !=
3569 contains_active_vehicle_transit_var) {
3571 LOG(
INFO) <<
"The assignment contains transit variable for dimension '"
3572 << dimension->name() <<
"' for some vehicles, but not for all";
3575 if (contains_unused_vehicle_transit_var) {
3576 const int64 old_unused_vehicle_transit =
3577 compact_assignment->Value(unused_vehicle_transit_var);
3578 const int64 old_active_vehicle_transit =
3579 compact_assignment->Value(active_vehicle_transit_var);
3580 compact_assignment->SetValue(unused_vehicle_transit_var,
3581 old_active_vehicle_transit);
3582 compact_assignment->SetValue(active_vehicle_transit_var,
3583 old_unused_vehicle_transit);
3587 const std::vector<IntVar*>& cumul_variables = dimension->cumuls();
3588 IntVar*
const unused_vehicle_cumul_var =
3589 cumul_variables[unused_vehicle_end];
3590 IntVar*
const active_vehicle_cumul_var =
3591 cumul_variables[active_vehicle_end];
3592 const int64 old_unused_vehicle_cumul =
3593 compact_assignment->Value(unused_vehicle_cumul_var);
3594 const int64 old_active_vehicle_cumul =
3595 compact_assignment->Value(active_vehicle_cumul_var);
3596 compact_assignment->SetValue(unused_vehicle_cumul_var,
3597 old_active_vehicle_cumul);
3598 compact_assignment->SetValue(active_vehicle_cumul_var,
3599 old_unused_vehicle_cumul);
3606 return CompactAssignmentInternal(assignment,
false);
3611 return CompactAssignmentInternal(assignment,
true);
3614 Assignment* RoutingModel::CompactAssignmentInternal(
3615 const Assignment& assignment,
bool check_compact_assignment)
const {
3619 <<
"The costs are not homogeneous, routes cannot be rearranged";
3623 std::unique_ptr<Assignment> compact_assignment(
new Assignment(&assignment));
3624 for (
int vehicle = 0; vehicle < vehicles_ - 1; ++vehicle) {
3628 const int vehicle_start =
Start(vehicle);
3629 const int vehicle_end =
End(vehicle);
3631 int swap_vehicle = vehicles_ - 1;
3632 bool has_more_vehicles_with_route =
false;
3633 for (; swap_vehicle > vehicle; --swap_vehicle) {
3640 has_more_vehicles_with_route =
true;
3641 const int swap_vehicle_start =
Start(swap_vehicle);
3642 const int swap_vehicle_end =
End(swap_vehicle);
3651 if (RouteCanBeUsedByVehicle(*compact_assignment, swap_vehicle_start,
3657 if (swap_vehicle == vehicle) {
3658 if (has_more_vehicles_with_route) {
3662 LOG(
INFO) <<
"No vehicle that can be swapped with " << vehicle
3669 if (!ReplaceUnusedVehicle(vehicle, swap_vehicle,
3670 compact_assignment.get())) {
3675 if (check_compact_assignment &&
3676 !solver_->CheckAssignment(compact_assignment.get())) {
3678 LOG(
WARNING) <<
"The compacted assignment is not a valid solution";
3681 return compact_assignment.release();
3684 int RoutingModel::FindNextActive(
int index,
3685 const std::vector<int64>& indices)
const {
3688 const int size = indices.size();
3699 preassignment_->
Clear();
3700 IntVar* next_var =
nullptr;
3701 int lock_index = FindNextActive(-1, locks);
3702 const int size = locks.size();
3703 if (lock_index < size) {
3704 next_var =
NextVar(locks[lock_index]);
3705 preassignment_->
Add(next_var);
3706 for (lock_index = FindNextActive(lock_index, locks); lock_index < size;
3707 lock_index = FindNextActive(lock_index, locks)) {
3708 preassignment_->
SetValue(next_var, locks[lock_index]);
3709 next_var =
NextVar(locks[lock_index]);
3710 preassignment_->
Add(next_var);
3717 const std::vector<std::vector<int64>>& locks,
bool close_routes) {
3718 preassignment_->
Clear();
3723 const RoutingSearchParameters&
parameters)
const {
3725 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3731 const RoutingSearchParameters&
parameters)
const {
3733 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3739 if (collect_assignments_->
solution_count() == 1 && assignment_ !=
nullptr) {
3741 return assignment_->
Save(file_name);
3749 CHECK(assignment_ !=
nullptr);
3750 if (assignment_->
Load(file_name)) {
3751 return DoRestoreAssignment();
3758 CHECK(assignment_ !=
nullptr);
3760 return DoRestoreAssignment();
3763 Assignment* RoutingModel::DoRestoreAssignment() {
3767 solver_->Solve(restore_assignment_, monitors_);
3770 return collect_assignments_->
solution(0);
3779 const std::vector<std::vector<int64>>& routes,
bool ignore_inactive_indices,
3780 bool close_routes,
Assignment*
const assignment)
const {
3781 CHECK(assignment !=
nullptr);
3783 LOG(
ERROR) <<
"The model is not closed yet";
3786 const int num_routes = routes.size();
3787 if (num_routes > vehicles_) {
3788 LOG(
ERROR) <<
"The number of vehicles in the assignment (" << routes.size()
3789 <<
") is greater than the number of vehicles in the model ("
3790 << vehicles_ <<
")";
3794 absl::flat_hash_set<int> visited_indices;
3796 for (
int vehicle = 0; vehicle < num_routes; ++vehicle) {
3797 const std::vector<int64>& route = routes[vehicle];
3798 int from_index =
Start(vehicle);
3799 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3800 visited_indices.insert(from_index);
3801 if (!insert_result.second) {
3802 LOG(
ERROR) <<
"Index " << from_index <<
" (start node for vehicle "
3803 << vehicle <<
") was already used";
3807 for (
const int64 to_index : route) {
3808 if (to_index < 0 || to_index >=
Size()) {
3809 LOG(
ERROR) <<
"Invalid index: " << to_index;
3814 if (active_var->
Max() == 0) {
3815 if (ignore_inactive_indices) {
3818 LOG(
ERROR) <<
"Index " << to_index <<
" is not active";
3823 insert_result = visited_indices.insert(to_index);
3824 if (!insert_result.second) {
3825 LOG(
ERROR) <<
"Index " << to_index <<
" is used multiple times";
3830 if (!vehicle_var->
Contains(vehicle)) {
3831 LOG(
ERROR) <<
"Vehicle " << vehicle <<
" is not allowed at index "
3837 if (!assignment->
Contains(from_var)) {
3838 assignment->
Add(from_var);
3840 assignment->
SetValue(from_var, to_index);
3842 from_index = to_index;
3847 if (!assignment->
Contains(last_var)) {
3848 assignment->
Add(last_var);
3855 for (
int vehicle = num_routes; vehicle < vehicles_; ++vehicle) {
3856 const int start_index =
Start(vehicle);
3859 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3860 visited_indices.insert(start_index);
3861 if (!insert_result.second) {
3862 LOG(
ERROR) <<
"Index " << start_index <<
" is used multiple times";
3867 if (!assignment->
Contains(start_var)) {
3868 assignment->
Add(start_var);
3879 if (!assignment->
Contains(next_var)) {
3880 assignment->
Add(next_var);
3891 const std::vector<std::vector<int64>>& routes,
3892 bool ignore_inactive_indices) {
3900 return DoRestoreAssignment();
3905 std::vector<std::vector<int64>>*
const routes)
const {
3907 CHECK(routes !=
nullptr);
3909 const int model_size =
Size();
3910 routes->resize(vehicles_);
3911 for (
int vehicle = 0; vehicle < vehicles_; ++vehicle) {
3912 std::vector<int64>*
const vehicle_route = &routes->at(vehicle);
3913 vehicle_route->clear();
3915 int num_visited_indices = 0;
3916 const int first_index =
Start(vehicle);
3920 int current_index = assignment.
Value(first_var);
3921 while (!
IsEnd(current_index)) {
3922 vehicle_route->push_back(current_index);
3927 current_index = assignment.
Value(next_var);
3929 ++num_visited_indices;
3930 CHECK_LE(num_visited_indices, model_size)
3931 <<
"The assignment contains a cycle";
3939 std::vector<std::vector<int64>> route_indices(
vehicles());
3940 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3942 LOG(DFATAL) <<
"GetRoutesFromAssignment() called on incomplete solution:"
3943 <<
" NextVar(" << vehicle <<
") is unbound.";
3946 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3948 route_indices[vehicle].push_back(
index);
3951 route_indices[vehicle].push_back(
index);
3954 return route_indices;
3958 int64 RoutingModel::GetArcCostForClassInternal(
3959 int64 from_index,
int64 to_index, CostClassIndex cost_class_index)
const {
3962 DCHECK_LT(cost_class_index, cost_classes_.size());
3963 CostCacheElement*
const cache = &cost_cache_[from_index];
3965 if (cache->index ==
static_cast<int>(to_index) &&
3966 cache->cost_class_index == cost_class_index) {
3970 const CostClass& cost_class = cost_classes_[cost_class_index];
3971 const auto& evaluator = transit_evaluators_[cost_class.evaluator_index];
3973 cost =
CapAdd(evaluator(from_index, to_index),
3974 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3975 }
else if (!
IsEnd(to_index)) {
3979 evaluator(from_index, to_index),
3980 CapAdd(GetDimensionTransitCostSum(from_index, to_index, cost_class),
3981 fixed_cost_of_vehicle_[index_to_vehicle_[from_index]]));
3985 if (consider_empty_route_costs_[index_to_vehicle_[from_index]]) {
3987 CapAdd(evaluator(from_index, to_index),
3988 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3993 *cache = {
static_cast<int>(to_index), cost_class_index,
cost};
4002 int vehicle)
const {
4016 return assignment.
Value(next_var);
4020 int64 vehicle)
const {
4021 if (from_index != to_index && vehicle >= 0) {
4022 return GetArcCostForClassInternal(from_index, to_index,
4031 int64 cost_class_index)
const {
4032 if (from_index != to_index) {
4033 return GetArcCostForClassInternal(from_index, to_index,
4041 int64 to_index)
const {
4045 if (!is_bound_to_end_ct_added_.
Switched()) {
4048 std::vector<IntVar*> zero_transit(
Size(), solver_->MakeIntConst(0));
4049 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
4050 nexts_, active_, is_bound_to_end_, zero_transit));
4051 is_bound_to_end_ct_added_.
Switch(solver_.get());
4053 if (is_bound_to_end_[to_index]->Min() == 1)
return kint64max;
4058 int64 RoutingModel::GetDimensionTransitCostSum(
4059 int64 i,
int64 j,
const CostClass& cost_class)
const {
4061 for (
const auto& evaluator_and_coefficient :
4062 cost_class.dimension_transit_evaluator_class_and_cost_coefficient) {
4063 DCHECK_GT(evaluator_and_coefficient.cost_coefficient, 0);
4066 CapProd(evaluator_and_coefficient.cost_coefficient,
4067 evaluator_and_coefficient.dimension->GetTransitValueFromClass(
4068 i, j, evaluator_and_coefficient.transit_evaluator_class)));
4084 const bool mandatory1 = active_[to1]->Min() == 1;
4085 const bool mandatory2 = active_[to2]->Min() == 1;
4087 if (mandatory1 != mandatory2)
return mandatory1;
4094 const int64 src_vehicle = src_vehicle_var->
Max();
4095 if (src_vehicle_var->
Bound()) {
4102 mandatory1 ? to1_vehicle_var->
Bound() : (to1_vehicle_var->
Size() <= 2);
4104 mandatory2 ? to2_vehicle_var->
Bound() : (to2_vehicle_var->
Size() <= 2);
4107 if (bound1 != bound2)
return bound1;
4110 const int64 vehicle1 = to1_vehicle_var->
Max();
4111 const int64 vehicle2 = to2_vehicle_var->
Max();
4114 if ((vehicle1 == src_vehicle) != (vehicle2 == src_vehicle)) {
4115 return vehicle1 == src_vehicle;
4120 if (vehicle1 != src_vehicle)
return to1 < to2;
4131 const std::vector<IntVar*>& cumul_vars =
4133 IntVar*
const dim1 = cumul_vars[to1];
4134 IntVar*
const dim2 = cumul_vars[to2];
4137 if (dim1->
Max() != dim2->
Max())
return dim1->
Max() < dim2->
Max();
4146 const int64 cost_class_index =
4147 SafeGetCostClassInt64OfVehicle(src_vehicle);
4152 if (cost1 != cost2)
return cost1 < cost2;
4159 if (num_vehicles1 != num_vehicles2)
return num_vehicles1 < num_vehicles2;
4168 DCHECK_EQ(index_to_visit_type_.size(), index_to_type_policy_.size());
4169 index_to_visit_type_[
index] = type;
4170 index_to_type_policy_[
index] = policy;
4171 num_visit_types_ =
std::max(num_visit_types_, type + 1);
4176 return index_to_visit_type_[
index];
4180 DCHECK_LT(type, single_nodes_of_type_.size());
4181 return single_nodes_of_type_[type];
4185 DCHECK_LT(type, pair_indices_of_type_.size());
4186 return pair_indices_of_type_[type];
4192 return index_to_type_policy_[
index];
4196 hard_incompatible_types_per_type_index_.resize(num_visit_types_);
4197 temporal_incompatible_types_per_type_index_.resize(num_visit_types_);
4198 same_vehicle_required_type_alternatives_per_type_index_.resize(
4200 required_type_alternatives_when_adding_type_index_.resize(num_visit_types_);
4201 required_type_alternatives_when_removing_type_index_.resize(num_visit_types_);
4206 hard_incompatible_types_per_type_index_.size());
4207 has_hard_type_incompatibilities_ =
true;
4209 hard_incompatible_types_per_type_index_[type1].insert(type2);
4210 hard_incompatible_types_per_type_index_[type2].insert(type1);
4215 temporal_incompatible_types_per_type_index_.size());
4216 has_temporal_type_incompatibilities_ =
true;
4218 temporal_incompatible_types_per_type_index_[type1].insert(type2);
4219 temporal_incompatible_types_per_type_index_[type2].insert(type1);
4222 const absl::flat_hash_set<int>&
4225 DCHECK_LT(type, hard_incompatible_types_per_type_index_.size());
4226 return hard_incompatible_types_per_type_index_[type];
4229 const absl::flat_hash_set<int>&
4232 DCHECK_LT(type, temporal_incompatible_types_per_type_index_.size());
4233 return temporal_incompatible_types_per_type_index_[type];
4239 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4241 same_vehicle_required_type_alternatives_per_type_index_.size());
4243 if (required_type_alternatives.empty()) {
4247 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4248 trivially_infeasible_visit_types_to_policies_[dependent_type];
4255 has_same_vehicle_type_requirements_ =
true;
4256 same_vehicle_required_type_alternatives_per_type_index_[dependent_type]
4257 .push_back(std::move(required_type_alternatives));
4261 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4263 required_type_alternatives_when_adding_type_index_.size());
4265 if (required_type_alternatives.empty()) {
4269 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4270 trivially_infeasible_visit_types_to_policies_[dependent_type];
4276 has_temporal_type_requirements_ =
true;
4277 required_type_alternatives_when_adding_type_index_[dependent_type].push_back(
4278 std::move(required_type_alternatives));
4282 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4284 required_type_alternatives_when_removing_type_index_.size());
4286 if (required_type_alternatives.empty()) {
4290 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4291 trivially_infeasible_visit_types_to_policies_[dependent_type];
4298 has_temporal_type_requirements_ =
true;
4299 required_type_alternatives_when_removing_type_index_[dependent_type]
4300 .push_back(std::move(required_type_alternatives));
4303 const std::vector<absl::flat_hash_set<int>>&
4307 same_vehicle_required_type_alternatives_per_type_index_.size());
4308 return same_vehicle_required_type_alternatives_per_type_index_[type];
4311 const std::vector<absl::flat_hash_set<int>>&
4314 DCHECK_LT(type, required_type_alternatives_when_adding_type_index_.size());
4315 return required_type_alternatives_when_adding_type_index_[type];
4318 const std::vector<absl::flat_hash_set<int>>&
4321 DCHECK_LT(type, required_type_alternatives_when_removing_type_index_.size());
4322 return required_type_alternatives_when_removing_type_index_[type];
4330 int64 var_index)
const {
4331 if (active_[var_index]->Min() == 1)
return kint64max;
4332 const std::vector<DisjunctionIndex>& disjunction_indices =
4334 if (disjunction_indices.size() != 1)
return default_value;
4339 return std::max(
int64{0}, disjunctions_[disjunction_index].value.penalty);
4344 const std::string& dimension_to_print)
const {
4345 for (
int i = 0; i <
Size(); ++i) {
4348 <<
"DebugOutputVehicleSchedules() called on incomplete solution:"
4349 <<
" NextVar(" << i <<
") is unbound.";
4354 absl::flat_hash_set<std::string> dimension_names;
4355 if (dimension_to_print.empty()) {
4357 dimension_names.insert(all_dimension_names.begin(),
4358 all_dimension_names.end());
4360 dimension_names.insert(dimension_to_print);
4362 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4363 int empty_vehicle_range_start = vehicle;
4368 if (empty_vehicle_range_start != vehicle) {
4369 if (empty_vehicle_range_start == vehicle - 1) {
4370 absl::StrAppendFormat(&output,
"Vehicle %d: empty",
4371 empty_vehicle_range_start);
4373 absl::StrAppendFormat(&output,
"Vehicles %d-%d: empty",
4374 empty_vehicle_range_start, vehicle - 1);
4376 output.append(
"\n");
4379 absl::StrAppendFormat(&output,
"Vehicle %d:", vehicle);
4383 absl::StrAppendFormat(&output,
"%d Vehicle(%d) ",
index,
4384 solution_assignment.
Value(vehicle_var));
4388 absl::StrAppendFormat(&output,
"%s(%d..%d) ", dimension->name(),
4389 solution_assignment.
Min(
var),
4390 solution_assignment.
Max(
var));
4395 if (
IsEnd(
index)) output.append(
"Route end ");
4397 output.append(
"\n");
4400 output.append(
"Unperformed nodes: ");
4401 bool has_unperformed =
false;
4402 for (
int i = 0; i <
Size(); ++i) {
4405 absl::StrAppendFormat(&output,
"%d ", i);
4406 has_unperformed =
true;
4409 if (!has_unperformed) output.append(
"None");
4410 output.append(
"\n");
4417 std::vector<std::vector<std::pair<int64, int64>>> cumul_bounds(
vehicles());
4418 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4420 LOG(DFATAL) <<
"GetCumulBounds() called on incomplete solution:"
4421 <<
" NextVar(" << vehicle <<
") is unbound.";
4425 for (
int vehicle_id = 0; vehicle_id <
vehicles(); ++vehicle_id) {
4428 cumul_bounds[vehicle_id].emplace_back(solution_assignment.
Min(dim_var),
4429 solution_assignment.
Max(dim_var));
4433 cumul_bounds[vehicle_id].emplace_back(solution_assignment.
Min(dim_var),
4434 solution_assignment.
Max(dim_var));
4437 return cumul_bounds;
4441 Assignment* RoutingModel::GetOrCreateAssignment() {
4442 if (assignment_ ==
nullptr) {
4443 assignment_ = solver_->MakeAssignment();
4444 assignment_->
Add(nexts_);
4446 assignment_->
Add(vehicle_vars_);
4453 Assignment* RoutingModel::GetOrCreateTmpAssignment() {
4454 if (tmp_assignment_ ==
nullptr) {
4455 tmp_assignment_ = solver_->MakeAssignment();
4456 tmp_assignment_->
Add(nexts_);
4458 return tmp_assignment_;
4461 RegularLimit* RoutingModel::GetOrCreateLimit() {
4462 if (limit_ ==
nullptr) {
4469 RegularLimit* RoutingModel::GetOrCreateLocalSearchLimit() {
4470 if (ls_limit_ ==
nullptr) {
4478 RegularLimit* RoutingModel::GetOrCreateLargeNeighborhoodSearchLimit() {
4479 if (lns_limit_ ==
nullptr) {
4488 RoutingModel::GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit() {
4489 if (first_solution_lns_limit_ ==
nullptr) {
4490 first_solution_lns_limit_ =
4494 return first_solution_lns_limit_;
4497 LocalSearchOperator* RoutingModel::CreateInsertionOperator() {
4498 std::vector<IntVar*> empty;
4499 LocalSearchOperator* insertion_operator =
4500 MakeLocalSearchOperator<MakeActiveOperator>(
4501 solver_.get(), nexts_,
4503 vehicle_start_class_callback_);
4504 if (!pickup_delivery_pairs_.empty()) {
4505 insertion_operator = solver_->ConcatenateOperators(
4507 solver_.get(), nexts_,
4509 vehicle_start_class_callback_, pickup_delivery_pairs_),
4510 insertion_operator});
4512 if (!implicit_pickup_delivery_pairs_without_alternatives_.empty()) {
4513 insertion_operator = solver_->ConcatenateOperators(
4515 solver_.get(), nexts_,
4517 vehicle_start_class_callback_,
4518 implicit_pickup_delivery_pairs_without_alternatives_),
4519 insertion_operator});
4521 return insertion_operator;
4524 LocalSearchOperator* RoutingModel::CreateMakeInactiveOperator() {
4525 std::vector<IntVar*> empty;
4526 LocalSearchOperator* make_inactive_operator =
4527 MakeLocalSearchOperator<MakeInactiveOperator>(
4528 solver_.get(), nexts_,
4530 vehicle_start_class_callback_);
4531 if (!pickup_delivery_pairs_.empty()) {
4532 make_inactive_operator = solver_->ConcatenateOperators(
4534 solver_.get(), nexts_,
4536 vehicle_start_class_callback_, pickup_delivery_pairs_),
4537 make_inactive_operator});
4539 return make_inactive_operator;
4542 #define CP_ROUTING_ADD_OPERATOR(operator_type, cp_operator_type) \
4543 if (CostsAreHomogeneousAcrossVehicles()) { \
4544 local_search_operators_[operator_type] = \
4545 solver_->MakeOperator(nexts_, Solver::cp_operator_type); \
4547 local_search_operators_[operator_type] = solver_->MakeOperator( \
4548 nexts_, vehicle_vars_, Solver::cp_operator_type); \
4551 #define CP_ROUTING_ADD_OPERATOR2(operator_type, cp_operator_class) \
4552 local_search_operators_[operator_type] = \
4553 MakeLocalSearchOperator<cp_operator_class>( \
4554 solver_.get(), nexts_, \
4555 CostsAreHomogeneousAcrossVehicles() ? std::vector<IntVar*>() \
4557 vehicle_start_class_callback_);
4559 #define CP_ROUTING_ADD_CALLBACK_OPERATOR(operator_type, cp_operator_type) \
4560 if (CostsAreHomogeneousAcrossVehicles()) { \
4561 local_search_operators_[operator_type] = solver_->MakeOperator( \
4563 [this](int64 i, int64 j, int64 k) { \
4564 return GetArcCostForVehicle(i, j, k); \
4566 Solver::cp_operator_type); \
4568 local_search_operators_[operator_type] = solver_->MakeOperator( \
4569 nexts_, vehicle_vars_, \
4570 [this](int64 i, int64 j, int64 k) { \
4571 return GetArcCostForVehicle(i, j, k); \
4573 Solver::cp_operator_type); \
4576 void RoutingModel::CreateNeighborhoodOperators(
4578 local_search_operators_.clear();
4579 local_search_operators_.resize(LOCAL_SEARCH_OPERATOR_COUNTER,
nullptr);
4581 std::vector<IntVar*> empty;
4582 local_search_operators_[RELOCATE_PAIR] = MakePairRelocate(
4583 solver_.get(), nexts_,
4585 vehicle_start_class_callback_, pickup_delivery_pairs_);
4586 local_search_operators_[LIGHT_RELOCATE_PAIR] = MakeLightPairRelocate(
4587 solver_.get(), nexts_,
4589 vehicle_start_class_callback_, pickup_delivery_pairs_);
4590 local_search_operators_[EXCHANGE_PAIR] = MakePairExchange(
4591 solver_.get(), nexts_,
4593 vehicle_start_class_callback_, pickup_delivery_pairs_);
4594 local_search_operators_[EXCHANGE_RELOCATE_PAIR] = MakePairExchangeRelocate(
4595 solver_.get(), nexts_,
4597 vehicle_start_class_callback_, pickup_delivery_pairs_);
4598 local_search_operators_[RELOCATE_NEIGHBORS] = MakeRelocateNeighbors(
4599 solver_.get(), nexts_,
4601 vehicle_start_class_callback_,
4603 local_search_operators_[NODE_PAIR_SWAP] = solver_->ConcatenateOperators(
4604 {IndexPairSwapActive(
4605 solver_.get(), nexts_,
4607 vehicle_start_class_callback_, pickup_delivery_pairs_),
4609 solver_.get(), nexts_,
4611 pickup_delivery_pairs_),
4613 solver_.get(), nexts_,
4615 vehicle_start_class_callback_, pickup_delivery_pairs_)});
4616 const auto arc_cost_for_path_start =
4618 const int vehicle = index_to_vehicle_[start_index];
4619 const int64 arc_cost =
4621 return (before_node != start_index ||
IsEnd(after_node))
4625 GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
4626 ls_gci_parameters = {
4629 parameters.cheapest_insertion_ls_operator_neighbors_ratio(),
4631 parameters.cheapest_insertion_add_unperformed_entries()};
4632 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4633 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4634 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4640 GetOrCreateFeasibilityFilterManager(
parameters),
4642 parameters.heuristic_close_nodes_lns_num_nodes()));
4644 local_search_operators_[LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4645 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4646 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4651 GetOrCreateFeasibilityFilterManager(
parameters)),
4652 parameters.heuristic_close_nodes_lns_num_nodes()));
4654 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_PATH_LNS] =
4655 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4656 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4662 GetOrCreateFeasibilityFilterManager(
parameters),
4663 ls_gci_parameters)));
4665 local_search_operators_[LOCAL_CHEAPEST_INSERTION_PATH_LNS] =
4666 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4667 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4672 GetOrCreateFeasibilityFilterManager(
parameters))));
4674 local_search_operators_
4675 [RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED] =
4677 new RelocatePathAndHeuristicInsertUnperformedOperator(
4678 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4686 GetOrCreateFeasibilityFilterManager(
parameters),
4687 ls_gci_parameters)));
4689 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4690 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4691 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4697 GetOrCreateFeasibilityFilterManager(
parameters),
4699 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4700 arc_cost_for_path_start));
4702 local_search_operators_[LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4703 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4704 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4709 GetOrCreateFeasibilityFilterManager(
parameters)),
4710 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4711 arc_cost_for_path_start));
4712 local_search_operators_[RELOCATE_EXPENSIVE_CHAIN] =
4713 solver_->RevAlloc(
new RelocateExpensiveChain(
4715 vehicle_start_class_callback_,
4716 parameters.relocate_expensive_chain_num_arcs_to_consider(),
4717 arc_cost_for_path_start));
4718 local_search_operators_[RELOCATE_SUBTRIP] = MakeRelocateSubtrip(
4719 solver_.get(), nexts_,
4721 vehicle_start_class_callback_, pickup_delivery_pairs_);
4722 local_search_operators_[EXCHANGE_SUBTRIP] = MakeExchangeSubtrip(
4723 solver_.get(), nexts_,
4725 vehicle_start_class_callback_, pickup_delivery_pairs_);
4732 local_search_operators_[MAKE_ACTIVE] = CreateInsertionOperator();
4734 RelocateAndMakeActiveOperator);
4736 local_search_operators_[MAKE_INACTIVE] = CreateMakeInactiveOperator();
4747 #undef CP_ROUTING_ADD_CALLBACK_OPERATOR
4748 #undef CP_ROUTING_ADD_OPERATOR2
4749 #undef CP_ROUTING_ADD_OPERATOR
4751 #define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators) \
4752 if (search_parameters.local_search_operators().use_##operator_method() == \
4754 operators.push_back(local_search_operators_[operator_type]); \
4757 LocalSearchOperator* RoutingModel::ConcatenateOperators(
4758 const RoutingSearchParameters& search_parameters,
4759 const std::vector<LocalSearchOperator*>& operators)
const {
4760 if (search_parameters.use_multi_armed_bandit_concatenate_operators()) {
4761 return solver_->MultiArmedBanditConcatenateOperators(
4764 .multi_armed_bandit_compound_operator_memory_coefficient(),
4766 .multi_armed_bandit_compound_operator_exploration_coefficient(),
4769 return solver_->ConcatenateOperators(operators);
4772 LocalSearchOperator* RoutingModel::GetNeighborhoodOperators(
4773 const RoutingSearchParameters& search_parameters)
const {
4774 std::vector<LocalSearchOperator*> operator_groups;
4775 std::vector<LocalSearchOperator*> operators = extra_operators_;
4776 if (!pickup_delivery_pairs_.empty()) {
4780 if (search_parameters.local_search_operators().use_relocate_pair() ==
4790 if (vehicles_ > 1) {
4801 if (!pickup_delivery_pairs_.empty() ||
4802 search_parameters.local_search_operators().use_relocate_neighbors() ==
4804 operators.push_back(local_search_operators_[RELOCATE_NEIGHBORS]);
4807 search_parameters.local_search_metaheuristic();
4808 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4809 local_search_metaheuristic !=
4810 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4811 local_search_metaheuristic !=
4812 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4819 if (!disjunctions_.
empty()) {
4836 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4845 global_cheapest_insertion_path_lns, operators);
4847 local_cheapest_insertion_path_lns, operators);
4849 RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED,
4850 relocate_path_global_cheapest_insertion_insert_unperformed, operators);
4853 global_cheapest_insertion_expensive_chain_lns,
4856 local_cheapest_insertion_expensive_chain_lns,
4859 global_cheapest_insertion_close_nodes_lns,
4862 local_cheapest_insertion_close_nodes_lns, operators);
4863 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4867 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4868 local_search_metaheuristic !=
4869 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4870 local_search_metaheuristic !=
4871 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4874 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4875 local_search_metaheuristic !=
4876 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4877 local_search_metaheuristic !=
4878 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4883 if (!disjunctions_.
empty()) {
4886 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4888 return solver_->ConcatenateOperators(operator_groups);
4891 #undef CP_ROUTING_PUSH_OPERATOR
4895 if (dimension->GetUnaryTransitEvaluator(0) !=
nullptr)
return true;
4902 void ConvertVectorInt64ToVectorInt(
const std::vector<int64>&
input,
4903 std::vector<int>* output) {
4904 const int n =
input.size();
4906 int* data = output->data();
4907 for (
int i = 0; i < n; ++i) {
4908 const int element =
static_cast<int>(
input[i]);
4916 std::vector<LocalSearchFilterManager::FilterEvent>
4917 RoutingModel::GetOrCreateLocalSearchFilters(
4918 const RoutingSearchParameters&
parameters,
bool filter_cost) {
4919 const auto kAccept = LocalSearchFilterManager::FilterEventType::kAccept;
4920 const auto kRelax = LocalSearchFilterManager::FilterEventType::kRelax;
4930 std::vector<LocalSearchFilterManager::FilterEvent> filters;
4932 if (filter_cost && vehicle_amortized_cost_factors_set_) {
4940 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4943 filters.push_back({sum, kAccept});
4945 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4946 nexts_, vehicle_vars_,
4951 filters.push_back({sum, kAccept});
4955 filters.push_back({solver_->MakeVariableDomainFilter(), kAccept});
4957 if (vehicles_ > max_active_vehicles_) {
4961 if (!disjunctions_.
empty()) {
4965 if (!pickup_delivery_pairs_.empty()) {
4968 vehicle_pickup_delivery_policy_),
4978 const PathState* path_state_reference =
nullptr;
4980 std::vector<int> path_starts;
4981 std::vector<int> path_ends;
4982 ConvertVectorInt64ToVectorInt(starts_, &path_starts);
4983 ConvertVectorInt64ToVectorInt(ends_, &path_ends);
4985 auto path_state = absl::make_unique<PathState>(
4986 Size() +
vehicles(), std::move(path_starts), std::move(path_ends));
4987 path_state_reference = path_state.get();
4999 if (!dimension->HasBreakConstraints())
continue;
5002 filters.insert(filters.end(), extra_filters_.begin(), extra_filters_.end());
5006 LocalSearchFilterManager* RoutingModel::GetOrCreateLocalSearchFilterManager(
5008 if (!local_search_filter_manager_) {
5009 local_search_filter_manager_ =
5010 solver_->RevAlloc(
new LocalSearchFilterManager(
5013 return local_search_filter_manager_;
5016 std::vector<LocalSearchFilterManager::FilterEvent>
5017 RoutingModel::GetOrCreateFeasibilityFilters(
5019 return GetOrCreateLocalSearchFilters(
parameters,
false);
5022 LocalSearchFilterManager* RoutingModel::GetOrCreateFeasibilityFilterManager(
5024 if (!feasibility_filter_manager_) {
5025 feasibility_filter_manager_ =
5026 solver_->RevAlloc(
new LocalSearchFilterManager(
5029 return feasibility_filter_manager_;
5032 LocalSearchFilterManager*
5033 RoutingModel::GetOrCreateStrongFeasibilityFilterManager(
5035 if (!strong_feasibility_filter_manager_) {
5036 std::vector<LocalSearchFilterManager::FilterEvent> filters =
5039 LocalSearchFilterManager::FilterEventType::kAccept});
5040 strong_feasibility_filter_manager_ =
5041 solver_->RevAlloc(
new LocalSearchFilterManager(std::move(filters)));
5043 return strong_feasibility_filter_manager_;
5047 bool AllTransitsPositive(
const RoutingDimension& dimension) {
5048 for (
int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
5049 if (!dimension.AreVehicleTransitsPositive(vehicle)) {
5057 void RoutingModel::StoreDimensionCumulOptimizers(
5059 Assignment* packed_dimensions_collector_assignment =
5060 solver_->MakeAssignment();
5061 packed_dimensions_collector_assignment->AddObjective(
CostVar());
5062 const int num_dimensions = dimensions_.size();
5063 local_optimizer_index_.
resize(num_dimensions, -1);
5064 global_optimizer_index_.
resize(num_dimensions, -1);
5067 if (dimension->global_span_cost_coefficient() > 0 ||
5068 !dimension->GetNodePrecedences().empty()) {
5070 global_optimizer_index_[dim] = global_dimension_optimizers_.size();
5071 global_dimension_optimizers_.push_back(
5072 absl::make_unique<GlobalDimensionCumulOptimizer>(dimension));
5073 packed_dimensions_collector_assignment->Add(dimension->cumuls());
5074 if (!AllTransitsPositive(*dimension)) {
5075 dimension->SetOffsetForGlobalOptimizer(0);
5079 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
5082 std::min(offset, dimension->CumulVar(
Start(vehicle))->Min() - 1);
5084 dimension->SetOffsetForGlobalOptimizer(
std::max(
Zero(), offset));
5086 bool has_span_cost =
false;
5087 bool has_span_limit =
false;
5088 std::vector<int64> vehicle_offsets(
vehicles());
5089 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
5090 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
5091 has_span_cost =
true;
5093 if (dimension->GetSpanUpperBoundForVehicle(vehicle) <
kint64max) {
5094 has_span_limit =
true;
5097 vehicle_offsets[vehicle] =
5098 dimension->AreVehicleTransitsPositive(vehicle)
5100 dimension->CumulVar(
Start(vehicle))->Min() - 1)
5103 bool has_soft_lower_bound =
false;
5104 bool has_soft_upper_bound =
false;
5105 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
5106 if (dimension->HasCumulVarSoftLowerBound(i)) {
5107 has_soft_lower_bound =
true;
5109 if (dimension->HasCumulVarSoftUpperBound(i)) {
5110 has_soft_upper_bound =
true;
5113 int num_linear_constraints = 0;
5114 if (has_span_cost) ++num_linear_constraints;
5115 if (has_span_limit) ++num_linear_constraints;
5116 if (dimension->HasSoftSpanUpperBounds()) ++num_linear_constraints;
5117 if (has_soft_lower_bound) ++num_linear_constraints;
5118 if (has_soft_upper_bound) ++num_linear_constraints;
5119 if (dimension->HasBreakConstraints()) ++num_linear_constraints;
5120 if (num_linear_constraints >= 2) {
5121 dimension->SetVehicleOffsetsForLocalOptimizer(
5122 std::move(vehicle_offsets));
5123 local_optimizer_index_[dim] = local_dimension_optimizers_.size();
5124 local_dimension_optimizers_.push_back(
5125 absl::make_unique<LocalDimensionCumulOptimizer>(
5126 dimension,
parameters.continuous_scheduling_solver()));
5127 bool has_intervals =
false;
5128 for (
const SortedDisjointIntervalList& intervals :
5129 dimension->forbidden_intervals()) {
5132 if (intervals.NumIntervals() > 0) {
5133 has_intervals =
true;
5137 if (dimension->HasBreakConstraints() || has_intervals) {
5138 local_dimension_mp_optimizers_.push_back(
5139 absl::make_unique<LocalDimensionCumulOptimizer>(
5140 dimension,
parameters.mixed_integer_scheduling_solver()));
5142 local_dimension_mp_optimizers_.push_back(
nullptr);
5144 packed_dimensions_collector_assignment->Add(dimension->cumuls());
5147 DCHECK_EQ(local_dimension_mp_optimizers_.size(),
5148 local_dimension_optimizers_.size());
5154 for (IntVar*
const extra_var : extra_vars_) {
5155 packed_dimensions_collector_assignment->Add(extra_var);
5157 for (IntervalVar*
const extra_interval : extra_intervals_) {
5158 packed_dimensions_collector_assignment->Add(extra_interval);
5161 packed_dimensions_assignment_collector_ = solver_->MakeFirstSolutionCollector(
5162 packed_dimensions_collector_assignment);
5167 std::vector<RoutingDimension*> dimensions;
5169 bool has_soft_or_span_cost =
false;
5170 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
5171 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
5172 has_soft_or_span_cost =
true;
5176 if (!has_soft_or_span_cost) {
5177 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
5178 if (dimension->HasCumulVarSoftUpperBound(i) ||
5179 dimension->HasCumulVarSoftLowerBound(i)) {
5180 has_soft_or_span_cost =
true;
5185 if (has_soft_or_span_cost) dimensions.push_back(dimension);
5191 RoutingModel::CreateFinalizerForMinimizedAndMaximizedVariables() {
5192 std::stable_sort(finalizer_variable_cost_pairs_.begin(),
5193 finalizer_variable_cost_pairs_.end(),
5194 [](
const std::pair<IntVar*, int64>& var_cost1,
5195 const std::pair<IntVar*, int64>& var_cost2) {
5196 return var_cost1.second > var_cost2.second;
5198 const int num_variables = finalizer_variable_cost_pairs_.size() +
5199 finalizer_variable_target_pairs_.size();
5200 std::vector<IntVar*> variables;
5201 std::vector<int64> targets;
5202 variables.reserve(num_variables);
5203 targets.reserve(num_variables);
5204 for (
const auto& variable_cost : finalizer_variable_cost_pairs_) {
5205 variables.push_back(variable_cost.first);
5208 for (
const auto& variable_target : finalizer_variable_target_pairs_) {
5209 variables.push_back(variable_target.first);
5210 targets.push_back(variable_target.second);
5213 std::move(targets));
5216 DecisionBuilder* RoutingModel::CreateSolutionFinalizer(SearchLimit* lns_limit) {
5217 std::vector<DecisionBuilder*> decision_builders;
5218 decision_builders.push_back(solver_->MakePhase(
5221 if (!local_dimension_optimizers_.empty()) {
5222 decision_builders.push_back(
5223 solver_->RevAlloc(
new SetCumulsFromLocalDimensionCosts(
5224 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
5227 if (!global_dimension_optimizers_.empty()) {
5228 decision_builders.push_back(
5229 solver_->RevAlloc(
new SetCumulsFromGlobalDimensionCosts(
5230 &global_dimension_optimizers_, lns_limit)));
5232 decision_builders.push_back(
5233 CreateFinalizerForMinimizedAndMaximizedVariables());
5235 return solver_->Compose(decision_builders);
5238 void RoutingModel::CreateFirstSolutionDecisionBuilders(
5239 const RoutingSearchParameters& search_parameters) {
5240 first_solution_decision_builders_.resize(
5242 first_solution_filtered_decision_builders_.resize(
5244 DecisionBuilder*
const finalize_solution =
5245 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit());
5247 first_solution_decision_builders_
5248 [FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE] = finalize_solution;
5250 first_solution_decision_builders_
5251 [FirstSolutionStrategy::GLOBAL_CHEAPEST_ARC] = solver_->MakePhase(
5259 first_solution_decision_builders_[FirstSolutionStrategy::LOCAL_CHEAPEST_ARC] =
5262 first_solution_decision_builders_[FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5264 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5265 first_solution_filtered_decision_builders_
5266 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5267 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5268 absl::make_unique<EvaluatorCheapestAdditionFilteredHeuristic>(
5273 GetOrCreateFeasibilityFilterManager(search_parameters))));
5274 first_solution_decision_builders_
5275 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5276 solver_->Try(first_solution_filtered_decision_builders_
5277 [FirstSolutionStrategy::PATH_CHEAPEST_ARC],
5278 first_solution_decision_builders_
5279 [FirstSolutionStrategy::PATH_CHEAPEST_ARC]);
5286 first_solution_decision_builders_
5287 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5289 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5290 first_solution_filtered_decision_builders_
5291 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5292 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5293 absl::make_unique<ComparatorCheapestAdditionFilteredHeuristic>(
5295 GetOrCreateFeasibilityFilterManager(search_parameters))));
5296 first_solution_decision_builders_
5297 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->Try(
5298 first_solution_filtered_decision_builders_
5299 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC],
5300 first_solution_decision_builders_
5301 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC]);
5304 if (first_solution_evaluator_ !=
nullptr) {
5305 first_solution_decision_builders_
5306 [FirstSolutionStrategy::EVALUATOR_STRATEGY] = solver_->MakePhase(
5309 first_solution_decision_builders_
5310 [FirstSolutionStrategy::EVALUATOR_STRATEGY] =
nullptr;
5313 first_solution_decision_builders_[FirstSolutionStrategy::ALL_UNPERFORMED] =
5314 solver_->RevAlloc(
new AllUnperformed(
this));
5316 RegularLimit*
const ls_limit =
5319 DecisionBuilder*
const finalize = solver_->MakeSolveOnce(
5320 finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit());
5321 LocalSearchPhaseParameters*
const insertion_parameters =
5322 solver_->MakeLocalSearchPhaseParameters(
5323 nullptr, CreateInsertionOperator(), finalize, ls_limit,
5324 GetOrCreateLocalSearchFilterManager(search_parameters));
5325 std::vector<IntVar*> decision_vars = nexts_;
5327 decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(),
5328 vehicle_vars_.end());
5332 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5333 solver_->MakeNestedOptimize(
5334 solver_->MakeLocalSearchPhase(
5335 decision_vars, solver_->RevAlloc(
new AllUnperformed(
this)),
5336 insertion_parameters),
5337 GetOrCreateAssignment(),
false, optimization_step);
5338 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5339 solver_->Compose(first_solution_decision_builders_
5340 [FirstSolutionStrategy::BEST_INSERTION],
5344 GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
5347 search_parameters.cheapest_insertion_farthest_seeds_ratio(),
5348 search_parameters.cheapest_insertion_first_solution_neighbors_ratio(),
5350 search_parameters.cheapest_insertion_add_unperformed_entries()};
5351 for (
bool is_sequential : {
false,
true}) {
5353 is_sequential ? FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION
5354 : FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5355 gci_parameters.is_sequential = is_sequential;
5357 first_solution_filtered_decision_builders_[first_solution_strategy] =
5358 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5359 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5365 GetOrCreateFeasibilityFilterManager(search_parameters),
5367 IntVarFilteredDecisionBuilder*
const strong_gci =
5368 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5369 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5375 GetOrCreateStrongFeasibilityFilterManager(search_parameters),
5377 first_solution_decision_builders_[first_solution_strategy] = solver_->Try(
5378 first_solution_filtered_decision_builders_[first_solution_strategy],
5379 solver_->Try(strong_gci, first_solution_decision_builders_
5380 [FirstSolutionStrategy::BEST_INSERTION]));
5384 first_solution_filtered_decision_builders_
5385 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] =
5386 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5387 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5392 GetOrCreateFeasibilityFilterManager(search_parameters))));
5393 IntVarFilteredDecisionBuilder*
const strong_lci =
5394 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5395 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5400 GetOrCreateStrongFeasibilityFilterManager(search_parameters))));
5401 first_solution_decision_builders_
5402 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = solver_->Try(
5403 first_solution_filtered_decision_builders_
5404 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION],
5405 solver_->Try(strong_lci,
5406 first_solution_decision_builders_
5407 [FirstSolutionStrategy::BEST_INSERTION]));
5409 SavingsFilteredHeuristic::SavingsParameters savings_parameters;
5410 savings_parameters.neighbors_ratio =
5411 search_parameters.savings_neighbors_ratio();
5412 savings_parameters.max_memory_usage_bytes =
5413 search_parameters.savings_max_memory_usage_bytes();
5414 savings_parameters.add_reverse_arcs =
5415 search_parameters.savings_add_reverse_arcs();
5416 savings_parameters.arc_coefficient =
5417 search_parameters.savings_arc_coefficient();
5418 LocalSearchFilterManager* filter_manager =
nullptr;
5419 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5420 filter_manager = GetOrCreateFeasibilityFilterManager(search_parameters);
5423 if (search_parameters.savings_parallel_routes()) {
5424 IntVarFilteredDecisionBuilder* savings_db =
5425 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5426 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5427 this, &manager_, savings_parameters, filter_manager)));
5428 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5429 first_solution_filtered_decision_builders_
5430 [FirstSolutionStrategy::SAVINGS] = savings_db;
5433 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5434 solver_->Try(savings_db,
5435 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5436 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5437 this, &manager_, savings_parameters,
5438 GetOrCreateStrongFeasibilityFilterManager(
5439 search_parameters)))));
5441 IntVarFilteredDecisionBuilder* savings_db =
5442 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5443 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5444 this, &manager_, savings_parameters, filter_manager)));
5445 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5446 first_solution_filtered_decision_builders_
5447 [FirstSolutionStrategy::SAVINGS] = savings_db;
5450 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5451 solver_->Try(savings_db,
5452 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5453 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5454 this, &manager_, savings_parameters,
5455 GetOrCreateStrongFeasibilityFilterManager(
5456 search_parameters)))));
5459 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5460 solver_->RevAlloc(
new SweepBuilder(
this,
true));
5461 DecisionBuilder* sweep_builder =
5462 solver_->RevAlloc(
new SweepBuilder(
this,
false));
5463 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5466 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP]);
5468 first_solution_decision_builders_[FirstSolutionStrategy::CHRISTOFIDES] =
5469 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5470 absl::make_unique<ChristofidesFilteredHeuristic>(
5471 this, GetOrCreateFeasibilityFilterManager(search_parameters),
5472 search_parameters.christofides_use_minimum_matching())));
5475 const bool has_precedences = std::any_of(
5476 dimensions_.begin(), dimensions_.end(),
5478 if (pickup_delivery_pairs_.empty() && !has_precedences) {
5479 automatic_first_solution_strategy_ =
5480 FirstSolutionStrategy::PATH_CHEAPEST_ARC;
5482 automatic_first_solution_strategy_ =
5483 FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5485 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC] =
5486 first_solution_decision_builders_[automatic_first_solution_strategy_];
5487 first_solution_decision_builders_[FirstSolutionStrategy::UNSET] =
5488 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC];
5491 DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder(
5492 const RoutingSearchParameters& search_parameters)
const {
5494 search_parameters.first_solution_strategy();
5495 if (first_solution_strategy < first_solution_decision_builders_.size()) {
5496 return first_solution_decision_builders_[first_solution_strategy];
5502 IntVarFilteredDecisionBuilder*
5503 RoutingModel::GetFilteredFirstSolutionDecisionBuilderOrNull(
5504 const RoutingSearchParameters& search_parameters)
const {
5506 search_parameters.first_solution_strategy();
5507 return first_solution_filtered_decision_builders_[first_solution_strategy];
5510 LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters(
5511 const RoutingSearchParameters& search_parameters) {
5512 SearchLimit* lns_limit = GetOrCreateLargeNeighborhoodSearchLimit();
5513 return solver_->MakeLocalSearchPhaseParameters(
5514 CostVar(), GetNeighborhoodOperators(search_parameters),
5515 solver_->MakeSolveOnce(CreateSolutionFinalizer(lns_limit), lns_limit),
5516 GetOrCreateLocalSearchLimit(),
5517 GetOrCreateLocalSearchFilterManager(search_parameters));
5520 DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder(
5521 const RoutingSearchParameters& search_parameters) {
5522 const int size =
Size();
5523 DecisionBuilder* first_solution =
5524 GetFirstSolutionDecisionBuilder(search_parameters);
5525 LocalSearchPhaseParameters*
const parameters =
5526 CreateLocalSearchParameters(search_parameters);
5527 SearchLimit* first_solution_lns_limit =
5528 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5529 DecisionBuilder*
const first_solution_sub_decision_builder =
5530 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_solution_lns_limit),
5531 first_solution_lns_limit);
5533 return solver_->MakeLocalSearchPhase(nexts_, first_solution,
5534 first_solution_sub_decision_builder,
5537 const int all_size = size + size + vehicles_;
5538 std::vector<IntVar*> all_vars(all_size);
5539 for (
int i = 0; i < size; ++i) {
5540 all_vars[i] = nexts_[i];
5542 for (
int i = size; i < all_size; ++i) {
5543 all_vars[i] = vehicle_vars_[i - size];
5545 return solver_->MakeLocalSearchPhase(all_vars, first_solution,
5546 first_solution_sub_decision_builder,
5551 void RoutingModel::SetupDecisionBuilders(
5552 const RoutingSearchParameters& search_parameters) {
5553 if (search_parameters.use_depth_first_search()) {
5554 SearchLimit* first_lns_limit =
5555 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5556 solve_db_ = solver_->Compose(
5557 GetFirstSolutionDecisionBuilder(search_parameters),
5558 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_lns_limit),
5561 solve_db_ = CreateLocalSearchDecisionBuilder(search_parameters);
5563 CHECK(preassignment_ !=
nullptr);
5564 DecisionBuilder* restore_preassignment =
5565 solver_->MakeRestoreAssignment(preassignment_);
5566 solve_db_ = solver_->Compose(restore_preassignment, solve_db_);
5568 solver_->Compose(restore_preassignment,
5569 solver_->MakeLocalSearchPhase(
5570 GetOrCreateAssignment(),
5571 CreateLocalSearchParameters(search_parameters)));
5572 restore_assignment_ = solver_->Compose(
5573 solver_->MakeRestoreAssignment(GetOrCreateAssignment()),
5574 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5575 restore_tmp_assignment_ = solver_->Compose(
5576 restore_preassignment,
5577 solver_->MakeRestoreAssignment(GetOrCreateTmpAssignment()),
5578 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5581 void RoutingModel::SetupMetaheuristics(
5582 const RoutingSearchParameters& search_parameters) {
5583 SearchMonitor* optimize;
5585 search_parameters.local_search_metaheuristic();
5588 bool limit_too_long = !search_parameters.has_time_limit() &&
5589 search_parameters.solution_limit() ==
kint64max;
5592 switch (metaheuristic) {
5593 case LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH:
5595 optimize = solver_->MakeGuidedLocalSearch(
5598 optimization_step, nexts_,
5599 search_parameters.guided_local_search_lambda_coefficient());
5601 optimize = solver_->MakeGuidedLocalSearch(
5606 optimization_step, nexts_, vehicle_vars_,
5607 search_parameters.guided_local_search_lambda_coefficient());
5610 case LocalSearchMetaheuristic::SIMULATED_ANNEALING:
5612 solver_->MakeSimulatedAnnealing(
false, cost_, optimization_step, 100);
5614 case LocalSearchMetaheuristic::TABU_SEARCH:
5615 optimize = solver_->MakeTabuSearch(
false, cost_, optimization_step,
5616 nexts_, 10, 10, .8);
5618 case LocalSearchMetaheuristic::GENERIC_TABU_SEARCH: {
5619 std::vector<operations_research::IntVar*> tabu_vars;
5620 if (tabu_var_callback_) {
5621 tabu_vars = tabu_var_callback_(
this);
5623 tabu_vars.push_back(cost_);
5625 optimize = solver_->MakeGenericTabuSearch(
false, cost_, optimization_step,
5630 limit_too_long =
false;
5631 optimize = solver_->MakeMinimize(cost_, optimization_step);
5633 if (limit_too_long) {
5634 LOG(
WARNING) << LocalSearchMetaheuristic::Value_Name(metaheuristic)
5635 <<
" specified without sane timeout: solve may run forever.";
5637 monitors_.push_back(optimize);
5641 tabu_var_callback_ = std::move(tabu_var_callback);
5644 void RoutingModel::SetupAssignmentCollector(
5645 const RoutingSearchParameters& search_parameters) {
5646 Assignment* full_assignment = solver_->MakeAssignment();
5648 full_assignment->
Add(dimension->cumuls());
5650 for (IntVar*
const extra_var : extra_vars_) {
5651 full_assignment->
Add(extra_var);
5653 for (IntervalVar*
const extra_interval : extra_intervals_) {
5654 full_assignment->
Add(extra_interval);
5656 full_assignment->
Add(nexts_);
5657 full_assignment->
Add(active_);
5658 full_assignment->
Add(vehicle_vars_);
5661 collect_assignments_ = solver_->MakeNBestValueSolutionCollector(
5662 full_assignment, search_parameters.number_of_solutions_to_collect(),
5664 collect_one_assignment_ =
5665 solver_->MakeFirstSolutionCollector(full_assignment);
5666 monitors_.push_back(collect_assignments_);
5669 void RoutingModel::SetupTrace(
5670 const RoutingSearchParameters& search_parameters) {
5671 if (search_parameters.log_search()) {
5672 Solver::SearchLogParameters search_log_parameters;
5673 search_log_parameters.branch_period = 10000;
5674 search_log_parameters.objective =
nullptr;
5675 search_log_parameters.variable = cost_;
5676 search_log_parameters.scaling_factor =
5677 search_parameters.log_cost_scaling_factor();
5678 search_log_parameters.offset = search_parameters.log_cost_offset();
5679 if (!search_parameters.log_tag().empty()) {
5680 const std::string& tag = search_parameters.log_tag();
5681 search_log_parameters.display_callback = [tag]() {
return tag; };
5683 search_log_parameters.display_callback =
nullptr;
5685 search_log_parameters.display_on_new_solutions_only =
false;
5686 monitors_.push_back(solver_->MakeSearchLog(search_log_parameters));
5690 void RoutingModel::SetupImprovementLimit(
5691 const RoutingSearchParameters& search_parameters) {
5692 if (search_parameters.has_improvement_limit_parameters()) {
5693 monitors_.push_back(solver_->MakeImprovementLimit(
5694 cost_,
false, search_parameters.log_cost_scaling_factor(),
5695 search_parameters.log_cost_offset(),
5696 search_parameters.improvement_limit_parameters()
5697 .improvement_rate_coefficient(),
5698 search_parameters.improvement_limit_parameters()
5699 .improvement_rate_solutions_distance()));
5703 void RoutingModel::SetupSearchMonitors(
5704 const RoutingSearchParameters& search_parameters) {
5705 monitors_.push_back(GetOrCreateLimit());
5706 SetupImprovementLimit(search_parameters);
5707 SetupMetaheuristics(search_parameters);
5708 SetupAssignmentCollector(search_parameters);
5709 SetupTrace(search_parameters);
5712 bool RoutingModel::UsesLightPropagation(
5713 const RoutingSearchParameters& search_parameters)
const {
5714 return !search_parameters.use_full_propagation() &&
5715 !search_parameters.use_depth_first_search() &&
5716 search_parameters.first_solution_strategy() !=
5717 FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE;
5724 finalizer_variable_cost_pairs_.size());
5725 if (
index < finalizer_variable_cost_pairs_.size()) {
5726 const int64 old_cost = finalizer_variable_cost_pairs_[
index].second;
5727 finalizer_variable_cost_pairs_[
index].second =
CapAdd(old_cost,
cost);
5729 finalizer_variable_cost_pairs_.emplace_back(
var,
cost);
5735 if (finalizer_variable_target_set_.contains(
var))
return;
5736 finalizer_variable_target_set_.insert(
var);
5737 finalizer_variable_target_pairs_.emplace_back(
var, target);
5748 void RoutingModel::SetupSearch(
5749 const RoutingSearchParameters& search_parameters) {
5750 SetupDecisionBuilders(search_parameters);
5751 SetupSearchMonitors(search_parameters);
5755 extra_vars_.push_back(
var);
5759 extra_intervals_.push_back(
interval);
5764 class PathSpansAndTotalSlacks :
public Constraint {
5768 std::vector<IntVar*> spans,
5769 std::vector<IntVar*> total_slacks)
5772 dimension_(dimension),
5773 spans_(std::move(spans)),
5774 total_slacks_(std::move(total_slacks)) {
5775 CHECK_EQ(spans_.size(), model_->vehicles());
5776 CHECK_EQ(total_slacks_.size(), model_->vehicles());
5777 vehicle_demons_.resize(model_->vehicles());
5780 std::string DebugString()
const override {
return "PathSpansAndTotalSlacks"; }
5782 void Post()
override {
5783 const int num_nodes = model_->VehicleVars().size();
5784 const int num_transits = model_->Nexts().size();
5785 for (
int node = 0; node < num_nodes; ++node) {
5787 model_->solver(),
this, &PathSpansAndTotalSlacks::PropagateNode,
5788 "PathSpansAndTotalSlacks::PropagateNode", node);
5789 dimension_->CumulVar(node)->WhenRange(demon);
5790 model_->VehicleVar(node)->WhenBound(demon);
5791 if (node < num_transits) {
5792 dimension_->TransitVar(node)->WhenRange(demon);
5793 dimension_->FixedTransitVar(node)->WhenBound(demon);
5794 model_->NextVar(node)->WhenBound(demon);
5797 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5798 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5800 solver(),
this, &PathSpansAndTotalSlacks::PropagateVehicle,
5801 "PathSpansAndTotalSlacks::PropagateVehicle", vehicle);
5802 vehicle_demons_[vehicle] = demon;
5803 if (spans_[vehicle]) spans_[vehicle]->WhenRange(demon);
5804 if (total_slacks_[vehicle]) total_slacks_[vehicle]->WhenRange(demon);
5805 if (dimension_->HasBreakConstraints()) {
5806 for (IntervalVar*
b : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5807 b->WhenAnything(demon);
5814 void InitialPropagate()
override {
5815 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5816 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5817 PropagateVehicle(vehicle);
5825 void PropagateNode(
int node) {
5826 if (!model_->VehicleVar(node)->Bound())
return;
5827 const int vehicle = model_->VehicleVar(node)->Min();
5828 if (vehicle < 0 || vehicle_demons_[vehicle] ==
nullptr)
return;
5829 EnqueueDelayedDemon(vehicle_demons_[vehicle]);
5837 int64 SpanMin(
int vehicle,
int64 sum_fixed_transits) {
5839 const int64 span_min = spans_[vehicle] ? spans_[vehicle]->Min() :
kint64max;
5840 const int64 total_slack_min =
5841 total_slacks_[vehicle] ? total_slacks_[vehicle]->Min() :
kint64max;
5842 return std::min(span_min,
CapAdd(total_slack_min, sum_fixed_transits));
5844 int64 SpanMax(
int vehicle,
int64 sum_fixed_transits) {
5846 const int64 span_max = spans_[vehicle] ? spans_[vehicle]->Max() :
kint64min;
5847 const int64 total_slack_max =
5848 total_slacks_[vehicle] ? total_slacks_[vehicle]->Max() :
kint64min;
5849 return std::max(span_max,
CapAdd(total_slack_max, sum_fixed_transits));
5851 void SetSpanMin(
int vehicle,
int64 min,
int64 sum_fixed_transits) {
5853 if (spans_[vehicle]) {
5854 spans_[vehicle]->SetMin(
min);
5856 if (total_slacks_[vehicle]) {
5857 total_slacks_[vehicle]->SetMin(
CapSub(
min, sum_fixed_transits));
5860 void SetSpanMax(
int vehicle,
int64 max,
int64 sum_fixed_transits) {
5862 if (spans_[vehicle]) {
5863 spans_[vehicle]->SetMax(
max);
5865 if (total_slacks_[vehicle]) {
5866 total_slacks_[vehicle]->SetMax(
CapSub(
max, sum_fixed_transits));
5871 void SynchronizeSpanAndTotalSlack(
int vehicle,
int64 sum_fixed_transits) {
5873 IntVar* span = spans_[vehicle];
5874 IntVar* total_slack = total_slacks_[vehicle];
5875 if (!span || !total_slack)
return;
5876 span->SetMin(
CapAdd(total_slack->Min(), sum_fixed_transits));
5877 span->SetMax(
CapAdd(total_slack->Max(), sum_fixed_transits));
5878 total_slack->SetMin(
CapSub(span->Min(), sum_fixed_transits));
5879 total_slack->SetMax(
CapSub(span->Max(), sum_fixed_transits));
5882 void PropagateVehicle(
int vehicle) {
5883 DCHECK(spans_[vehicle] || total_slacks_[vehicle]);
5884 const int start = model_->Start(vehicle);
5885 const int end = model_->End(vehicle);
5891 int curr_node = start;
5892 while (!model_->IsEnd(curr_node)) {
5893 const IntVar* next_var = model_->NextVar(curr_node);
5894 if (!next_var->Bound())
return;
5895 path_.push_back(curr_node);
5896 curr_node = next_var->Value();
5901 int64 sum_fixed_transits = 0;
5902 for (
const int node : path_) {
5903 const IntVar* fixed_transit_var = dimension_->FixedTransitVar(node);
5904 if (!fixed_transit_var->Bound())
return;
5905 sum_fixed_transits =
5906 CapAdd(sum_fixed_transits, fixed_transit_var->Value());
5909 SynchronizeSpanAndTotalSlack(vehicle, sum_fixed_transits);
5916 if (dimension_->HasBreakConstraints() &&
5917 !dimension_->GetBreakIntervalsOfVehicle(vehicle).empty()) {
5918 const int64 vehicle_start_max = dimension_->CumulVar(start)->Max();
5919 const int64 vehicle_end_min = dimension_->CumulVar(end)->Min();
5921 int64 min_break_duration = 0;
5922 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5923 if (!br->MustBePerformed())
continue;
5924 if (vehicle_start_max < br->EndMin() &&
5925 br->StartMax() < vehicle_end_min) {
5926 min_break_duration =
CapAdd(min_break_duration, br->DurationMin());
5929 SetSpanMin(vehicle,
CapAdd(min_break_duration, sum_fixed_transits),
5930 sum_fixed_transits);
5936 const int64 slack_max =
5937 CapSub(SpanMax(vehicle, sum_fixed_transits), sum_fixed_transits);
5938 const int64 max_additional_slack =
CapSub(slack_max, min_break_duration);
5939 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5940 if (!br->MustBePerformed())
continue;
5942 if (vehicle_start_max >= br->EndMin() &&
5943 br->StartMax() < vehicle_end_min) {
5944 if (br->DurationMin() > max_additional_slack) {
5947 br->SetEndMax(vehicle_start_max);
5948 dimension_->CumulVar(start)->SetMin(br->EndMin());
5953 if (vehicle_start_max < br->EndMin() &&
5954 br->StartMax() >= vehicle_end_min) {
5955 if (br->DurationMin() > max_additional_slack) {
5956 br->SetStartMin(vehicle_end_min);
5957 dimension_->CumulVar(end)->SetMax(br->StartMax());
5965 IntVar* start_cumul = dimension_->CumulVar(start);
5966 IntVar* end_cumul = dimension_->CumulVar(end);
5973 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5975 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5977 const int64 span_min = SpanMin(vehicle, sum_fixed_transits);
5978 const int64 span_max = SpanMax(vehicle, sum_fixed_transits);
5979 const int64 slack_from_lb =
CapSub(span_max, span_lb);
5980 const int64 slack_from_ub =
CapSub(span_ub, span_min);
5996 for (
const int node : path_) {
5997 span_lb =
CapAdd(span_lb, dimension_->TransitVar(node)->Min());
5998 span_ub =
CapAdd(span_ub, dimension_->TransitVar(node)->Max());
6000 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
6001 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
6005 const int64 span_min = SpanMin(vehicle, sum_fixed_transits);
6006 const int64 span_max = SpanMax(vehicle, sum_fixed_transits);
6007 const int64 slack_from_lb =
CapSub(span_max, span_lb);
6008 const int64 slack_from_ub =
6010 for (
const int node : path_) {
6011 IntVar* transit_var = dimension_->TransitVar(node);
6012 const int64 transit_i_min = transit_var->Min();
6013 const int64 transit_i_max = transit_var->Max();
6017 transit_var->SetMax(
CapAdd(transit_i_min, slack_from_lb));
6018 transit_var->SetMin(
CapSub(transit_i_max, slack_from_ub));
6023 path_.push_back(end);
6034 int64 arrival_time = dimension_->CumulVar(start)->Min();
6035 for (
int i = 1; i < path_.size(); ++i) {
6038 dimension_->FixedTransitVar(path_[i - 1])->Min()),
6039 dimension_->CumulVar(path_[i])->Min());
6041 int64 departure_time = arrival_time;
6042 for (
int i = path_.size() - 2; i >= 0; --i) {
6045 dimension_->FixedTransitVar(path_[i])->Min()),
6046 dimension_->CumulVar(path_[i])->Max());
6048 const int64 span_lb =
CapSub(arrival_time, departure_time);
6049 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
6050 const int64 maximum_deviation =
6051 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
6052 const int64 start_lb =
CapSub(departure_time, maximum_deviation);
6053 dimension_->CumulVar(start)->SetMin(start_lb);
6057 int64 departure_time = dimension_->CumulVar(end)->Max();
6058 for (
int i = path_.size() - 2; i >= 0; --i) {
6059 const int curr_node = path_[i];
6062 dimension_->FixedTransitVar(curr_node)->Min()),
6063 dimension_->CumulVar(curr_node)->Max());
6065 int arrival_time = departure_time;
6066 for (
int i = 1; i < path_.size(); ++i) {
6069 dimension_->FixedTransitVar(path_[i - 1])->Min()),
6070 dimension_->CumulVar(path_[i])->Min());
6072 const int64 span_lb =
CapSub(arrival_time, departure_time);
6073 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
6074 const int64 maximum_deviation =
6075 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
6076 dimension_->CumulVar(end)->SetMax(
6077 CapAdd(arrival_time, maximum_deviation));
6081 const RoutingModel*
const model_;
6082 const RoutingDimension*
const dimension_;
6083 std::vector<IntVar*> spans_;
6084 std::vector<IntVar*> total_slacks_;
6085 std::vector<int> path_;
6086 std::vector<Demon*> vehicle_demons_;
6093 std::vector<IntVar*> total_slacks) {
6095 CHECK_EQ(vehicles_, total_slacks.size());
6097 new PathSpansAndTotalSlacks(
this, dimension, spans, total_slacks));
6105 std::vector<int64> vehicle_capacities,
6106 const std::string&
name,
6108 : vehicle_capacities_(std::move(vehicle_capacities)),
6109 base_dimension_(base_dimension),
6110 global_span_cost_coefficient_(0),
6113 global_optimizer_offset_(0) {
6116 vehicle_span_cost_coefficients_.assign(
model->vehicles(), 0);
6119 RoutingDimension::RoutingDimension(RoutingModel*
model,
6120 std::vector<int64> vehicle_capacities,
6121 const std::string&
name, SelfBased)
6122 : RoutingDimension(
model, std::move(vehicle_capacities),
name, this) {}
6125 cumul_var_piecewise_linear_cost_.clear();
6128 void RoutingDimension::Initialize(
6129 const std::vector<int>& transit_evaluators,
6130 const std::vector<int>& state_dependent_transit_evaluators,
6133 InitializeTransits(transit_evaluators, state_dependent_transit_evaluators,
6147 class LightRangeLessOrEqual :
public Constraint {
6149 LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l, IntExpr*
const r);
6150 ~LightRangeLessOrEqual()
override {}
6151 void Post()
override;
6152 void InitialPropagate()
override;
6153 std::string DebugString()
const override;
6154 IntVar* Var()
override {
6155 return solver()->MakeIsLessOrEqualVar(left_, right_);
6158 void Accept(ModelVisitor*
const visitor)
const override {
6169 IntExpr*
const left_;
6170 IntExpr*
const right_;
6174 LightRangeLessOrEqual::LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l,
6176 : Constraint(s), left_(l), right_(r), demon_(nullptr) {}
6178 void LightRangeLessOrEqual::Post() {
6180 solver(),
this, &LightRangeLessOrEqual::CheckRange,
"CheckRange");
6181 left_->WhenRange(demon_);
6182 right_->WhenRange(demon_);
6185 void LightRangeLessOrEqual::InitialPropagate() {
6186 left_->SetMax(right_->Max());
6187 right_->SetMin(left_->Min());
6188 if (left_->Max() <= right_->Min()) {
6193 void LightRangeLessOrEqual::CheckRange() {
6194 if (left_->Min() > right_->Max()) {
6197 if (left_->Max() <= right_->Min()) {
6202 std::string LightRangeLessOrEqual::DebugString()
const {
6203 return left_->DebugString() +
" < " + right_->DebugString();
6208 void RoutingDimension::InitializeCumuls() {
6209 Solver*
const solver = model_->
solver();
6211 const auto capacity_range = std::minmax_element(vehicle_capacities_.begin(),
6212 vehicle_capacities_.end());
6213 const int64 min_capacity = *capacity_range.first;
6215 const int64 max_capacity = *capacity_range.second;
6216 solver->MakeIntVarArray(size, 0, max_capacity, name_, &cumuls_);
6218 for (
int v = 0; v < model_->
vehicles(); v++) {
6219 const int64 vehicle_capacity = vehicle_capacities_[v];
6220 cumuls_[model_->
Start(v)]->SetMax(vehicle_capacity);
6221 cumuls_[model_->
End(v)]->SetMax(vehicle_capacity);
6224 forbidden_intervals_.resize(size);
6225 capacity_vars_.clear();
6226 if (min_capacity != max_capacity) {
6227 solver->MakeIntVarArray(size, 0,
kint64max, &capacity_vars_);
6228 for (
int i = 0; i < size; ++i) {
6229 IntVar*
const capacity_var = capacity_vars_[i];
6230 if (i < model_->Size()) {
6231 IntVar*
const capacity_active = solver->MakeBoolVar();
6232 solver->AddConstraint(
6233 solver->MakeLessOrEqual(model_->
ActiveVar(i), capacity_active));
6234 solver->AddConstraint(solver->MakeIsLessOrEqualCt(
6235 cumuls_[i], capacity_var, capacity_active));
6237 solver->AddConstraint(
6238 solver->MakeLessOrEqual(cumuls_[i], capacity_var));
6245 template <
int64 value>
6250 void ComputeTransitClasses(
const std::vector<int>& evaluator_indices,
6251 std::vector<int>* class_evaluators,
6252 std::vector<int64>* vehicle_to_class) {
6253 CHECK(class_evaluators !=
nullptr);
6254 CHECK(vehicle_to_class !=
nullptr);
6255 class_evaluators->clear();
6256 vehicle_to_class->resize(evaluator_indices.size(), -1);
6257 absl::flat_hash_map<int, int64> evaluator_to_class;
6258 for (
int i = 0; i < evaluator_indices.size(); ++i) {
6259 const int evaluator_index = evaluator_indices[i];
6260 int evaluator_class = -1;
6261 if (!
gtl::FindCopy(evaluator_to_class, evaluator_index, &evaluator_class)) {
6262 evaluator_class = class_evaluators->size();
6263 evaluator_to_class[evaluator_index] = evaluator_class;
6264 class_evaluators->push_back(evaluator_index);
6266 (*vehicle_to_class)[i] = evaluator_class;
6271 void RoutingDimension::InitializeTransitVariables(
int64 slack_max) {
6272 CHECK(!class_evaluators_.empty());
6273 CHECK(base_dimension_ ==
nullptr ||
6274 !state_dependent_class_evaluators_.empty());
6276 Solver*
const solver = model_->
solver();
6277 const int size = model_->
Size();
6280 return (0 <=
index &&
index < state_dependent_vehicle_to_class_.size())
6281 ? state_dependent_vehicle_to_class_[
index]
6282 : state_dependent_class_evaluators_.size();
6284 const std::string slack_name = name_ +
" slack";
6285 const std::string transit_name = name_ +
" fixed transit";
6287 for (
int64 i = 0; i < size; ++i) {
6288 fixed_transits_[i] =
6291 if (base_dimension_ !=
nullptr) {
6292 if (state_dependent_class_evaluators_.size() == 1) {
6293 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
6294 for (
int64 j = 0; j < cumuls_.size(); ++j) {
6295 transition_variables[j] =
6296 MakeRangeMakeElementExpr(
6298 ->StateDependentTransitCallback(
6299 state_dependent_class_evaluators_[0])(i, j)
6301 base_dimension_->
CumulVar(i), solver)
6304 dependent_transits_[i] =
6305 solver->MakeElement(transition_variables, model_->
NextVar(i))
6308 IntVar*
const vehicle_class_var =
6310 ->MakeElement(dependent_vehicle_class_function,
6313 std::vector<IntVar*> transit_for_vehicle;
6314 transit_for_vehicle.reserve(state_dependent_class_evaluators_.size() +
6316 for (
int evaluator : state_dependent_class_evaluators_) {
6317 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
6318 for (
int64 j = 0; j < cumuls_.size(); ++j) {
6319 transition_variables[j] =
6320 MakeRangeMakeElementExpr(
6323 base_dimension_->
CumulVar(i), solver)
6326 transit_for_vehicle.push_back(
6327 solver->MakeElement(transition_variables, model_->
NextVar(i))
6330 transit_for_vehicle.push_back(solver->MakeIntConst(0));
6331 dependent_transits_[i] =
6332 solver->MakeElement(transit_for_vehicle, vehicle_class_var)->Var();
6335 dependent_transits_[i] = solver->MakeIntConst(0);
6339 IntExpr* transit_expr = fixed_transits_[i];
6340 if (dependent_transits_[i]->Min() != 0 ||
6341 dependent_transits_[i]->Max() != 0) {
6342 transit_expr = solver->MakeSum(transit_expr, dependent_transits_[i]);
6345 if (slack_max == 0) {
6346 slacks_[i] = solver->MakeIntConst(0);
6349 solver->MakeIntVar(0, slack_max, absl::StrCat(slack_name, i));
6350 transit_expr = solver->MakeSum(slacks_[i], transit_expr);
6352 transits_[i] = transit_expr->Var();
6356 void RoutingDimension::InitializeTransits(
6357 const std::vector<int>& transit_evaluators,
6358 const std::vector<int>& state_dependent_transit_evaluators,
6361 CHECK(base_dimension_ ==
nullptr ||
6362 model_->
vehicles() == state_dependent_transit_evaluators.size());
6363 const int size = model_->
Size();
6364 transits_.resize(size,
nullptr);
6365 fixed_transits_.resize(size,
nullptr);
6366 slacks_.resize(size,
nullptr);
6367 dependent_transits_.resize(size,
nullptr);
6368 ComputeTransitClasses(transit_evaluators, &class_evaluators_,
6369 &vehicle_to_class_);
6370 if (base_dimension_ !=
nullptr) {
6371 ComputeTransitClasses(state_dependent_transit_evaluators,
6372 &state_dependent_class_evaluators_,
6373 &state_dependent_vehicle_to_class_);
6376 InitializeTransitVariables(slack_max);
6381 std::vector<int64>* values) {
6382 const int num_nodes = path.size();
6383 values->resize(num_nodes - 1);
6384 for (
int i = 0; i < num_nodes - 1; ++i) {
6385 (*values)[i] = evaluator(path[i], path[i + 1]);
6390 : model_(
model), occurrences_of_type_(
model.GetNumberOfVisitTypes()) {}
6393 int vehicle,
const std::function<
int64(
int64)>& next_accessor) {
6400 for (
int pos = 0; pos < current_route_visits_.size(); pos++) {
6401 const int64 current_visit = current_route_visits_[pos];
6408 DCHECK_LT(type, occurrences_of_type_.size());
6409 int& num_type_added = occurrences_of_type_[type].num_type_added_to_vehicle;
6410 int& num_type_removed =
6411 occurrences_of_type_[type].num_type_removed_from_vehicle;
6412 DCHECK_LE(num_type_removed, num_type_added);
6414 num_type_removed == num_type_added) {
6424 if (policy == VisitTypePolicy::TYPE_ADDED_TO_VEHICLE ||
6425 policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED) {
6428 if (policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED ||
6429 policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6437 int vehicle,
const std::function<
int64(
int64)>& next_accessor) {
6440 std::fill(occurrences_of_type_.begin(), occurrences_of_type_.end(),
6446 current_route_visits_.clear();
6448 current = next_accessor(current)) {
6451 VisitTypePolicy::TYPE_ON_VEHICLE_UP_TO_VISIT) {
6452 occurrences_of_type_[type].position_of_last_type_on_vehicle_up_to_visit =
6453 current_route_visits_.size();
6455 current_route_visits_.push_back(current);
6477 check_hard_incompatibilities_(check_hard_incompatibilities) {}
6479 bool TypeIncompatibilityChecker::HasRegulationsToCheck()
const {
6481 (check_hard_incompatibilities_ &&
6490 bool TypeIncompatibilityChecker::CheckTypeRegulations(
int type,
6491 VisitTypePolicy policy,
6493 if (policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6498 for (
int incompatible_type :
6504 if (check_hard_incompatibilities_) {
6505 for (
int incompatible_type :
6515 bool TypeRequirementChecker::HasRegulationsToCheck()
const {
6520 bool TypeRequirementChecker::CheckRequiredTypesCurrentlyOnRoute(
6521 const std::vector<absl::flat_hash_set<int>>& required_type_alternatives,
6523 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6524 required_type_alternatives) {
6525 bool has_one_of_alternatives =
false;
6526 for (
int type_alternative : requirement_alternatives) {
6528 has_one_of_alternatives =
true;
6532 if (!has_one_of_alternatives) {
6539 bool TypeRequirementChecker::CheckTypeRegulations(
int type,
6540 VisitTypePolicy policy,
6544 if (!CheckRequiredTypesCurrentlyOnRoute(
6550 if (!CheckRequiredTypesCurrentlyOnRoute(
6557 types_with_same_vehicle_requirements_on_route_.insert(type);
6562 bool TypeRequirementChecker::FinalizeCheck()
const {
6563 for (
int type : types_with_same_vehicle_requirements_on_route_) {
6564 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6566 bool has_one_of_alternatives =
false;
6567 for (
const int type_alternative : requirement_alternatives) {
6569 has_one_of_alternatives =
true;
6573 if (!has_one_of_alternatives) {
6584 incompatibility_checker_(
model, true),
6585 requirement_checker_(
model),
6586 vehicle_demons_(
model.vehicles()) {}
6588 void TypeRegulationsConstraint::PropagateNodeRegulations(
int node) {
6595 if (vehicle < 0)
return;
6596 DCHECK(vehicle_demons_[vehicle] !=
nullptr);
6600 void TypeRegulationsConstraint::CheckRegulationsOnVehicle(
int vehicle) {
6601 const auto next_accessor = [
this, vehicle](
int64 node) {
6606 return model_.
End(vehicle);
6608 if (!incompatibility_checker_.
CheckVehicle(vehicle, next_accessor) ||
6609 !requirement_checker_.
CheckVehicle(vehicle, next_accessor)) {
6615 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6617 solver(),
this, &TypeRegulationsConstraint::CheckRegulationsOnVehicle,
6618 "CheckRegulationsOnVehicle", vehicle);
6620 for (
int node = 0; node < model_.
Size(); node++) {
6622 solver(),
this, &TypeRegulationsConstraint::PropagateNodeRegulations,
6623 "PropagateNodeRegulations", node);
6630 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6631 CheckRegulationsOnVehicle(vehicle);
6635 void RoutingDimension::CloseModel(
bool use_light_propagation) {
6637 const auto capacity_lambda = [
this](
int64 vehicle) {
6638 return vehicle >= 0 ? vehicle_capacities_[vehicle] :
kint64max;
6640 for (
int i = 0; i < capacity_vars_.size(); ++i) {
6641 IntVar*
const vehicle_var = model_->
VehicleVar(i);
6642 IntVar*
const capacity_var = capacity_vars_[i];
6643 if (use_light_propagation) {
6645 solver, capacity_var, vehicle_var, capacity_lambda,
6646 [
this]() {
return model_->enable_deep_serialization_; }));
6654 return IthElementOrValue<-1>(vehicle_to_class_,
index);
6656 for (
int i = 0; i < fixed_transits_.size(); ++i) {
6657 IntVar*
const next_var = model_->
NextVar(i);
6658 IntVar*
const fixed_transit = fixed_transits_[i];
6659 const auto transit_vehicle_evaluator = [
this, i](
int64 to,
6663 if (use_light_propagation) {
6664 if (class_evaluators_.size() == 1) {
6665 const int class_evaluator_index = class_evaluators_[0];
6666 const auto& unary_callback =
6668 if (unary_callback ==
nullptr) {
6670 solver, fixed_transit, next_var,
6671 [
this, i](
int64 to) {
6674 [
this]() {
return model_->enable_deep_serialization_; }));
6676 fixed_transit->SetValue(unary_callback(i));
6680 solver, fixed_transit, next_var, model_->
VehicleVar(i),
6681 transit_vehicle_evaluator,
6682 [
this]() {
return model_->enable_deep_serialization_; }));
6685 if (class_evaluators_.size() == 1) {
6686 const int class_evaluator_index = class_evaluators_[0];
6687 const auto& unary_callback =
6689 if (unary_callback ==
nullptr) {
6691 fixed_transit, solver
6693 [
this, i](
int64 to) {
6695 class_evaluators_[0])(i, to);
6700 fixed_transit->SetValue(unary_callback(i));
6703 IntVar*
const vehicle_class_var =
6707 fixed_transit, solver
6709 next_var, vehicle_class_var)
6715 GlobalVehicleBreaksConstraint* constraint =
6722 int64 vehicle)
const {
6738 if (next_start >
max)
break;
6739 if (next_start < interval->start) {
6744 if (next_start <=
max) {
6753 CHECK_LT(vehicle, vehicle_span_upper_bounds_.size());
6755 vehicle_span_upper_bounds_[vehicle] = upper_bound;
6761 CHECK_LT(vehicle, vehicle_span_cost_coefficients_.size());
6763 vehicle_span_cost_coefficients_[vehicle] =
coefficient;
6778 if (!
cost.IsNonDecreasing()) {
6779 LOG(
WARNING) <<
"Only non-decreasing cost functions are supported.";
6782 if (
cost.Value(0) < 0) {
6783 LOG(
WARNING) <<
"Only positive cost functions are supported.";
6786 if (
index >= cumul_var_piecewise_linear_cost_.size()) {
6787 cumul_var_piecewise_linear_cost_.resize(
index + 1);
6789 PiecewiseLinearCost& piecewise_linear_cost =
6790 cumul_var_piecewise_linear_cost_[
index];
6791 piecewise_linear_cost.var = cumuls_[
index];
6792 piecewise_linear_cost.cost = absl::make_unique<PiecewiseLinearFunction>(
cost);
6796 return (
index < cumul_var_piecewise_linear_cost_.size() &&
6797 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr);
6802 if (
index < cumul_var_piecewise_linear_cost_.size() &&
6803 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr) {
6804 return cumul_var_piecewise_linear_cost_[
index].cost.get();
6814 const int vehicle =
model->VehicleIndex(
index);
6816 return solver->
MakeProd(expr,
model->VehicleCostsConsideredVar(vehicle))
6823 void RoutingDimension::SetupCumulVarPiecewiseLinearCosts(
6824 std::vector<IntVar*>* cost_elements)
const {
6825 CHECK(cost_elements !=
nullptr);
6826 Solver*
const solver = model_->
solver();
6827 for (
int i = 0; i < cumul_var_piecewise_linear_cost_.size(); ++i) {
6828 const PiecewiseLinearCost& piecewise_linear_cost =
6829 cumul_var_piecewise_linear_cost_[i];
6830 if (piecewise_linear_cost.var !=
nullptr) {
6831 IntExpr*
const expr = solver->MakePiecewiseLinearExpr(
6832 piecewise_linear_cost.var, *piecewise_linear_cost.cost);
6833 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6834 cost_elements->push_back(cost_var);
6844 if (
index >= cumul_var_soft_upper_bound_.size()) {
6845 cumul_var_soft_upper_bound_.resize(
index + 1, {
nullptr, 0, 0});
6847 cumul_var_soft_upper_bound_[
index] = {cumuls_[
index], upper_bound,
6852 return (
index < cumul_var_soft_upper_bound_.size() &&
6853 cumul_var_soft_upper_bound_[
index].var !=
nullptr);
6857 if (
index < cumul_var_soft_upper_bound_.size() &&
6858 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6859 return cumul_var_soft_upper_bound_[
index].bound;
6861 return cumuls_[
index]->Max();
6866 if (
index < cumul_var_soft_upper_bound_.size() &&
6867 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6868 return cumul_var_soft_upper_bound_[
index].coefficient;
6873 void RoutingDimension::SetupCumulVarSoftUpperBoundCosts(
6874 std::vector<IntVar*>* cost_elements)
const {
6875 CHECK(cost_elements !=
nullptr);
6877 for (
int i = 0; i < cumul_var_soft_upper_bound_.size(); ++i) {
6878 const SoftBound& soft_bound = cumul_var_soft_upper_bound_[i];
6879 if (soft_bound.var !=
nullptr) {
6881 solver->
MakeSum(soft_bound.var, -soft_bound.bound), 0,
6882 soft_bound.coefficient);
6883 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6884 cost_elements->push_back(cost_var);
6888 soft_bound.coefficient);
6895 if (
index >= cumul_var_soft_lower_bound_.size()) {
6896 cumul_var_soft_lower_bound_.resize(
index + 1, {
nullptr, 0, 0});
6898 cumul_var_soft_lower_bound_[
index] = {cumuls_[
index], lower_bound,
6903 return (
index < cumul_var_soft_lower_bound_.size() &&
6904 cumul_var_soft_lower_bound_[
index].var !=
nullptr);
6908 if (
index < cumul_var_soft_lower_bound_.size() &&
6909 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6910 return cumul_var_soft_lower_bound_[
index].bound;
6912 return cumuls_[
index]->Min();
6917 if (
index < cumul_var_soft_lower_bound_.size() &&
6918 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6919 return cumul_var_soft_lower_bound_[
index].coefficient;
6924 void RoutingDimension::SetupCumulVarSoftLowerBoundCosts(
6925 std::vector<IntVar*>* cost_elements)
const {
6926 CHECK(cost_elements !=
nullptr);
6928 for (
int i = 0; i < cumul_var_soft_lower_bound_.size(); ++i) {
6929 const SoftBound& soft_bound = cumul_var_soft_lower_bound_[i];
6930 if (soft_bound.var !=
nullptr) {
6933 soft_bound.coefficient);
6934 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6935 cost_elements->push_back(cost_var);
6939 soft_bound.coefficient);
6944 void RoutingDimension::SetupGlobalSpanCost(
6945 std::vector<IntVar*>* cost_elements)
const {
6946 CHECK(cost_elements !=
nullptr);
6947 Solver*
const solver = model_->
solver();
6948 if (global_span_cost_coefficient_ != 0) {
6949 std::vector<IntVar*> end_cumuls;
6950 for (
int i = 0; i < model_->
vehicles(); ++i) {
6951 end_cumuls.push_back(solver
6952 ->MakeProd(model_->vehicle_costs_considered_[i],
6953 cumuls_[model_->
End(i)])
6956 IntVar*
const max_end_cumul = solver->MakeMax(end_cumuls)->Var();
6958 max_end_cumul, global_span_cost_coefficient_);
6959 std::vector<IntVar*> start_cumuls;
6960 for (
int i = 0; i < model_->
vehicles(); ++i) {
6961 IntVar* global_span_cost_start_cumul = solver->MakeIntVar(0,
kint64max);
6962 solver->AddConstraint(solver->MakeIfThenElseCt(
6963 model_->vehicle_costs_considered_[i], cumuls_[model_->
Start(i)],
6964 max_end_cumul, global_span_cost_start_cumul));
6965 start_cumuls.push_back(global_span_cost_start_cumul);
6967 IntVar*
const min_start_cumul = solver->MakeMin(start_cumuls)->Var();
6969 min_start_cumul, global_span_cost_coefficient_);
6974 for (
int var_index = 0; var_index < model_->
Size(); ++var_index) {
6976 slacks_[var_index], global_span_cost_coefficient_);
6977 cost_elements->push_back(
6980 model_->vehicle_costs_considered_[0],
6983 solver->MakeSum(transits_[var_index],
6984 dependent_transits_[var_index]),
6985 global_span_cost_coefficient_),
6990 IntVar*
const end_range =
6991 solver->MakeDifference(max_end_cumul, min_start_cumul)->Var();
6992 end_range->SetMin(0);
6993 cost_elements->push_back(
6994 solver->MakeProd(end_range, global_span_cost_coefficient_)->Var());
7000 std::vector<IntervalVar*> breaks,
int vehicle,
7001 std::vector<int64> node_visit_transits) {
7002 if (breaks.empty())
return;
7004 [node_visit_transits](
int64 from,
int64 to) {
7005 return node_visit_transits[from];
7011 std::vector<IntervalVar*> breaks,
int vehicle,
7012 std::vector<int64> node_visit_transits,
7014 if (breaks.empty())
return;
7016 [node_visit_transits](
int64 from,
int64 to) {
7017 return node_visit_transits[from];
7025 std::vector<IntervalVar*> breaks,
int vehicle,
int pre_travel_evaluator,
7026 int post_travel_evaluator) {
7029 if (breaks.empty())
return;
7031 vehicle_break_intervals_[vehicle] = std::move(breaks);
7032 vehicle_pre_travel_evaluators_[vehicle] = pre_travel_evaluator;
7033 vehicle_post_travel_evaluators_[vehicle] = post_travel_evaluator;
7054 DCHECK(!break_constraints_are_initialized_);
7055 const int num_vehicles = model_->
vehicles();
7056 vehicle_break_intervals_.resize(num_vehicles);
7057 vehicle_pre_travel_evaluators_.resize(num_vehicles, -1);
7058 vehicle_post_travel_evaluators_.resize(num_vehicles, -1);
7059 vehicle_break_distance_duration_.resize(num_vehicles);
7060 break_constraints_are_initialized_ =
true;
7064 return break_constraints_are_initialized_;
7068 int vehicle)
const {
7070 DCHECK_LT(vehicle, vehicle_break_intervals_.size());
7071 return vehicle_break_intervals_[vehicle];
7076 DCHECK_LT(vehicle, vehicle_pre_travel_evaluators_.size());
7077 return vehicle_pre_travel_evaluators_[vehicle];
7082 DCHECK_LT(vehicle, vehicle_post_travel_evaluators_.size());
7083 return vehicle_post_travel_evaluators_[vehicle];
7092 vehicle_break_distance_duration_[vehicle].emplace_back(distance, duration);
7101 const std::vector<std::pair<int64, int64>>&
7104 DCHECK_LT(vehicle, vehicle_break_distance_duration_.size());
7105 return vehicle_break_distance_duration_[vehicle];
7111 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
7112 pickup_to_delivery_limits_per_pair_index_.resize(pair_index + 1);
7114 pickup_to_delivery_limits_per_pair_index_[pair_index] =
7115 std::move(limit_function);
7119 return !pickup_to_delivery_limits_per_pair_index_.empty();
7124 int delivery)
const {
7127 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
7131 pickup_to_delivery_limits_per_pair_index_[pair_index];
7132 if (!pickup_to_delivery_limit_function) {
7138 return pickup_to_delivery_limit_function(pickup, delivery);
7141 void RoutingDimension::SetupSlackAndDependentTransitCosts()
const {
7142 if (model_->
vehicles() == 0)
return;
7144 bool all_vehicle_span_costs_are_equal =
true;
7145 for (
int i = 1; i < model_->
vehicles(); ++i) {
7146 all_vehicle_span_costs_are_equal &= vehicle_span_cost_coefficients_[i] ==
7147 vehicle_span_cost_coefficients_[0];
7150 if (all_vehicle_span_costs_are_equal &&
7151 vehicle_span_cost_coefficients_[0] == 0) {
7163 std::vector<const RoutingDimension*> dimensions_with_relevant_slacks = {
this};
7165 const RoutingDimension*
next =
7166 dimensions_with_relevant_slacks.back()->base_dimension_;
7167 if (
next ==
nullptr ||
next == dimensions_with_relevant_slacks.back()) {
7170 dimensions_with_relevant_slacks.push_back(
next);
7173 for (
auto it = dimensions_with_relevant_slacks.rbegin();
7174 it != dimensions_with_relevant_slacks.rend(); ++it) {
7175 for (
int i = 0; i < model_->
vehicles(); ++i) {
7181 for (IntVar*
const slack : (*it)->slacks_) {