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) {
145 return solver->RevAlloc(
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();
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;
399 RegularLimit*
const limit = GetOrCreateLimit();
404 Assignment* packed_assignment = solver_->MakeAssignment();
405 packed_assignment->Add(
Nexts());
406 packed_assignment->CopyIntersection(original_assignment);
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());
425 DecisionBuilder* restore_pack_and_finalize =
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);
437 packed_assignment->CopyIntersection(
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 {
454 visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
456 visitor->VisitIntegerArrayArgument(ModelVisitor::kValuesArgument, values_);
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 {
508 visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
510 visitor->VisitIntegerExpressionArgument(ModelVisitor::kIndexArgument,
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 {
571 visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
573 visitor->VisitIntegerExpressionArgument(ModelVisitor::kIndexArgument,
575 visitor->VisitIntegerExpressionArgument(ModelVisitor::kIndex2Argument,
578 const int64 index1_min = index1_->Min();
579 const int64 index1_max = index1_->Max();
580 visitor->VisitIntegerArgument(ModelVisitor::kMinArgument, index1_min);
581 visitor->VisitIntegerArgument(ModelVisitor::kMaxArgument, 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_;
602 Solver::IndexEvaluator2 values_;
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());
794 VLOG(1) <<
"Model parameters:\n" <<
parameters.DebugString();
795 ConstraintSolverParameters solver_parameters =
797 : Solver::DefaultSolverParameters();
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()) {
903 DCHECK_EQ(transit_evaluators_.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));
1008 CHECK_EQ(0, start_cumul->Min());
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);
1092 CHECK(
index !=
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];
1292 CHECK_LT(0, vehicles_);
1293 for (
int i = 0; i < vehicles_; ++i) {
1300 CHECK_LT(vehicle, vehicles_);
1301 CHECK_LT(evaluator_index, transit_evaluators_.size());
1302 vehicle_to_transit_cost_[vehicle] = evaluator_index;
1306 for (
int i = 0; i < vehicles_; ++i) {
1312 CHECK_LT(vehicle, vehicles_);
1313 return fixed_cost_of_vehicle_[vehicle];
1317 CHECK_LT(vehicle, vehicles_);
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,
1333 CHECK_LT(vehicle, vehicles_);
1334 DCHECK_GE(linear_cost_factor, 0);
1335 DCHECK_GE(quadratic_cost_factor, 0);
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);
1473 IntVar*
const vehicle_var = vehicle_vars_[
index];
1475 nodes_unvisitability_bitmask[
index / CHAR_BIT] |= 1U
1476 << (
index % CHAR_BIT);
1480 nodes_unvisitability_bitmask.get(), nodes_unvisitability_num_bytes);
1483 &vehicle_class_map, vehicle_class, num_vehicle_classes);
1484 if (vehicle_class_index == num_vehicle_classes) {
1485 vehicle_classes_.push_back(vehicle_class);
1487 vehicle_class_index_of_vehicle_[vehicle] = vehicle_class_index;
1491 void RoutingModel::ComputeVehicleTypes() {
1492 const int nodes_squared = nodes_ * nodes_;
1493 std::vector<int>& type_index_of_vehicle =
1495 std::vector<std::set<VehicleTypeContainer::VehicleClassEntry>>&
1496 sorted_vehicle_classes_per_type =
1498 std::vector<std::deque<int>>& vehicles_per_vehicle_class =
1501 type_index_of_vehicle.resize(vehicles_);
1502 sorted_vehicle_classes_per_type.clear();
1503 sorted_vehicle_classes_per_type.reserve(vehicles_);
1504 vehicles_per_vehicle_class.clear();
1507 absl::flat_hash_map<int64, int> type_to_type_index;
1509 for (
int v = 0; v < vehicles_; v++) {
1513 const int64 type = cost_class * nodes_squared + start * nodes_ + end;
1515 const auto& vehicle_type_added = type_to_type_index.insert(
1516 std::make_pair(type, type_to_type_index.size()));
1518 const int index = vehicle_type_added.first->second;
1521 const VehicleTypeContainer::VehicleClassEntry class_entry = {
1524 if (vehicle_type_added.second) {
1526 DCHECK_EQ(sorted_vehicle_classes_per_type.size(),
index);
1527 sorted_vehicle_classes_per_type.push_back({class_entry});
1530 DCHECK_LT(
index, sorted_vehicle_classes_per_type.size());
1531 sorted_vehicle_classes_per_type[
index].insert(class_entry);
1533 vehicles_per_vehicle_class[vehicle_class].push_back(v);
1534 type_index_of_vehicle[v] =
index;
1538 void RoutingModel::FinalizeVisitTypes() {
1544 single_nodes_of_type_.clear();
1545 single_nodes_of_type_.resize(num_visit_types_);
1546 pair_indices_of_type_.clear();
1547 pair_indices_of_type_.resize(num_visit_types_);
1548 std::vector<absl::flat_hash_set<int>> pair_indices_added_for_type(
1553 if (visit_type < 0) {
1556 const std::vector<std::pair<int, int>>& pickup_index_pairs =
1557 index_to_pickup_index_pairs_[
index];
1558 const std::vector<std::pair<int, int>>& delivery_index_pairs =
1559 index_to_delivery_index_pairs_[
index];
1560 if (pickup_index_pairs.empty() && delivery_index_pairs.empty()) {
1561 single_nodes_of_type_[visit_type].push_back(
index);
1563 for (
const std::vector<std::pair<int, int>>* index_pairs :
1564 {&pickup_index_pairs, &delivery_index_pairs}) {
1565 for (
const std::pair<int, int>& index_pair : *index_pairs) {
1566 const int pair_index = index_pair.first;
1567 if (pair_indices_added_for_type[visit_type].insert(pair_index).second) {
1568 pair_indices_of_type_[visit_type].push_back(pair_index);
1574 std::vector<std::pair<int, int>> requirement_arcs;
1575 for (
int type = 0; type < num_visit_types_; type++) {
1576 for (
const std::vector<absl::flat_hash_set<int>>*
1577 required_type_alternatives :
1578 {&required_type_alternatives_when_adding_type_index_[type],
1579 &required_type_alternatives_when_removing_type_index_[type],
1580 &same_vehicle_required_type_alternatives_per_type_index_[type]}) {
1581 for (
const absl::flat_hash_set<int>& alternatives :
1582 *required_type_alternatives) {
1583 for (
int required_type : alternatives) {
1584 requirement_arcs.emplace_back(required_type, type);
1589 if (requirement_arcs.empty())
return;
1591 &topologically_sorted_visit_types_)) {
1592 topologically_sorted_visit_types_.clear();
1597 const std::vector<int64>& indices,
int64 penalty,
int64 max_cardinality) {
1598 CHECK_GE(max_cardinality, 1);
1599 for (
int i = 0; i < indices.size(); ++i) {
1604 disjunctions_.
push_back({indices, {penalty, max_cardinality}});
1606 index_to_disjunctions_[
index].push_back(disjunction_index);
1608 return disjunction_index;
1611 std::vector<std::pair<int64, int64>>
1613 std::vector<std::pair<int64, int64>> var_index_pairs;
1614 for (
const Disjunction& disjunction : disjunctions_) {
1615 const std::vector<int64>&
var_indices = disjunction.indices;
1619 if (index_to_disjunctions_[v0].size() == 1 &&
1620 index_to_disjunctions_[v1].size() == 1) {
1625 std::sort(var_index_pairs.begin(), var_index_pairs.end());
1626 return var_index_pairs;
1631 for (Disjunction& disjunction : disjunctions_) {
1632 bool has_one_potentially_active_var =
false;
1633 for (
const int64 var_index : disjunction.indices) {
1635 has_one_potentially_active_var =
true;
1639 if (!has_one_potentially_active_var) {
1640 disjunction.value.max_cardinality = 0;
1646 const std::vector<int64>& indices = disjunctions_[disjunction].indices;
1647 const int indices_size = indices.size();
1648 std::vector<IntVar*> disjunction_vars(indices_size);
1649 for (
int i = 0; i < indices_size; ++i) {
1654 const int64 max_cardinality =
1655 disjunctions_[disjunction].value.max_cardinality;
1656 IntVar* no_active_var = solver_->MakeBoolVar();
1657 IntVar* number_active_vars = solver_->MakeIntVar(0, max_cardinality);
1658 solver_->AddConstraint(
1659 solver_->MakeSumEquality(disjunction_vars, number_active_vars));
1660 solver_->AddConstraint(solver_->MakeIsDifferentCstCt(
1661 number_active_vars, max_cardinality, no_active_var));
1662 const int64 penalty = disjunctions_[disjunction].value.penalty;
1664 no_active_var->SetMax(0);
1667 return solver_->MakeProd(no_active_var, penalty)->Var();
1672 const std::vector<int64>& indices,
int64 cost) {
1673 if (!indices.empty()) {
1674 ValuedNodes<int64> same_vehicle_cost;
1676 same_vehicle_cost.indices.push_back(
index);
1678 same_vehicle_cost.value =
cost;
1679 same_vehicle_costs_.push_back(same_vehicle_cost);
1685 auto& allowed_vehicles = allowed_vehicles_[
index];
1686 allowed_vehicles.clear();
1688 allowed_vehicles.insert(vehicle);
1693 AddPickupAndDeliverySetsInternal({pickup}, {delivery});
1702 pickup_delivery_disjunctions_.push_back(
1703 {pickup_disjunction, delivery_disjunction});
1706 void RoutingModel::AddPickupAndDeliverySetsInternal(
1707 const std::vector<int64>& pickups,
const std::vector<int64>& deliveries) {
1708 if (pickups.empty() || deliveries.empty()) {
1712 const int pair_index = pickup_delivery_pairs_.size();
1713 for (
int pickup_index = 0; pickup_index < pickups.size(); pickup_index++) {
1714 const int64 pickup = pickups[pickup_index];
1715 CHECK_LT(pickup, size);
1716 index_to_pickup_index_pairs_[pickup].emplace_back(pair_index, pickup_index);
1718 for (
int delivery_index = 0; delivery_index < deliveries.size();
1720 const int64 delivery = deliveries[delivery_index];
1721 CHECK_LT(delivery, size);
1722 index_to_delivery_index_pairs_[delivery].emplace_back(pair_index,
1725 pickup_delivery_pairs_.push_back({pickups, deliveries});
1729 int64 node_index)
const {
1730 CHECK_LT(node_index, index_to_pickup_index_pairs_.size());
1731 return index_to_pickup_index_pairs_[node_index];
1735 int64 node_index)
const {
1736 CHECK_LT(node_index, index_to_delivery_index_pairs_.size());
1737 return index_to_delivery_index_pairs_[node_index];
1742 CHECK_LT(vehicle, vehicles_);
1743 vehicle_pickup_delivery_policy_[vehicle] = policy;
1748 CHECK_LT(0, vehicles_);
1749 for (
int i = 0; i < vehicles_; ++i) {
1756 CHECK_LT(vehicle, vehicles_);
1757 return vehicle_pickup_delivery_policy_[vehicle];
1762 for (
int i = 0; i <
Nexts().size(); ++i) {
1772 IntVar* RoutingModel::CreateSameVehicleCost(
int vehicle_index) {
1773 const std::vector<int64>& indices =
1774 same_vehicle_costs_[vehicle_index].indices;
1775 CHECK(!indices.empty());
1776 std::vector<IntVar*> vehicle_counts;
1777 solver_->MakeIntVarArray(vehicle_vars_.size() + 1, 0, indices.size() + 1,
1779 std::vector<int64> vehicle_values(vehicle_vars_.size() + 1);
1780 for (
int i = 0; i < vehicle_vars_.size(); ++i) {
1781 vehicle_values[i] = i;
1783 vehicle_values[vehicle_vars_.size()] = -1;
1784 std::vector<IntVar*> vehicle_vars;
1785 vehicle_vars.reserve(indices.size());
1787 vehicle_vars.push_back(vehicle_vars_[
index]);
1789 solver_->AddConstraint(solver_->MakeDistribute(vehicle_vars, vehicle_counts));
1790 std::vector<IntVar*> vehicle_used;
1791 for (
int i = 0; i < vehicle_vars_.size() + 1; ++i) {
1792 vehicle_used.push_back(
1793 solver_->MakeIsGreaterOrEqualCstVar(vehicle_counts[i], 1));
1795 vehicle_used.push_back(solver_->MakeIntConst(-1));
1797 ->MakeProd(solver_->MakeMax(solver_->MakeSum(vehicle_used), 0),
1798 same_vehicle_costs_[vehicle_index].value)
1803 extra_operators_.push_back(ls_operator);
1810 void RoutingModel::AppendHomogeneousArcCosts(
1811 const RoutingSearchParameters&
parameters,
int node_index,
1812 std::vector<IntVar*>* cost_elements) {
1813 CHECK(cost_elements !=
nullptr);
1814 const auto arc_cost_evaluator = [
this, node_index](
int64 next_index) {
1821 IntVar*
const base_cost_var = solver_->MakeIntVar(0,
kint64max);
1822 solver_->AddConstraint(MakeLightElement(
1823 solver_.get(), base_cost_var, nexts_[node_index], arc_cost_evaluator,
1824 [
this]() { return enable_deep_serialization_; }));
1826 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1827 cost_elements->push_back(
var);
1829 IntExpr*
const expr =
1830 solver_->MakeElement(arc_cost_evaluator, nexts_[node_index]);
1831 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
1832 cost_elements->push_back(
var);
1836 void RoutingModel::AppendArcCosts(
const RoutingSearchParameters&
parameters,
1838 std::vector<IntVar*>* cost_elements) {
1839 CHECK(cost_elements !=
nullptr);
1840 DCHECK_GT(vehicles_, 0);
1845 IntVar*
const base_cost_var = solver_->MakeIntVar(0,
kint64max);
1846 solver_->AddConstraint(MakeLightElement2(
1847 solver_.get(), base_cost_var, nexts_[node_index],
1848 vehicle_vars_[node_index],
1849 [
this, node_index](
int64 to,
int64 vehicle) {
1850 return GetArcCostForVehicle(node_index, to, vehicle);
1852 [
this]() { return enable_deep_serialization_; }));
1854 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1855 cost_elements->push_back(
var);
1857 IntVar*
const vehicle_class_var =
1861 return SafeGetCostClassInt64OfVehicle(
index);
1863 vehicle_vars_[node_index])
1865 IntExpr*
const expr = solver_->MakeElement(
1869 nexts_[node_index], vehicle_class_var);
1870 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
1871 cost_elements->push_back(
var);
1875 int RoutingModel::GetVehicleStartClass(
int64 start_index)
const {
1876 const int vehicle = index_to_vehicle_[start_index];
1883 std::string RoutingModel::FindErrorInSearchParametersForModel(
1884 const RoutingSearchParameters& search_parameters)
const {
1886 search_parameters.first_solution_strategy();
1887 if (GetFirstSolutionDecisionBuilder(search_parameters) ==
nullptr) {
1888 return absl::StrCat(
1889 "Undefined first solution strategy: ",
1890 FirstSolutionStrategy::Value_Name(first_solution_strategy),
1891 " (int value: ", first_solution_strategy,
")");
1893 if (search_parameters.first_solution_strategy() ==
1894 FirstSolutionStrategy::SWEEP &&
1896 return "Undefined sweep arranger for ROUTING_SWEEP strategy.";
1901 void RoutingModel::QuietCloseModel() {
1912 same_vehicle_components_.Init(
model->Size());
1913 for (
const std::string&
name :
model->GetAllDimensionNames()) {
1915 const std::vector<IntVar*>& cumuls = dimension->
cumuls();
1916 for (
int i = 0; i < cumuls.size(); ++i) {
1917 cumul_to_dim_indices_[cumuls[i]] = {dimension, i};
1920 const std::vector<IntVar*>& vehicle_vars =
model->VehicleVars();
1921 for (
int i = 0; i < vehicle_vars.size(); ++i) {
1922 vehicle_var_to_indices_[vehicle_vars[i]] = i;
1924 RegisterInspectors();
1929 absl::flat_hash_map<int, int> component_indices;
1930 int component_index = 0;
1931 for (
int node = 0; node < model_->Size(); ++node) {
1932 const int component =
1933 same_vehicle_components_.GetClassRepresentative(node);
1939 model_->InitSameVehicleGroups(component_indices.size());
1940 for (
int node = 0; node < model_->Size(); ++node) {
1941 const int component =
1942 same_vehicle_components_.GetClassRepresentative(node);
1944 model_->SetSameVehicleGroup(
1951 const Constraint*
const constraint)
override {
1955 IntExpr*
const expr)
override {
1957 [](
const IntExpr* expr) {})(expr);
1960 const std::vector<int64>& values)
override {
1962 [](
const std::vector<int64>& int_array) {})(values);
1966 using ExprInspector = std::function<void(
const IntExpr*)>;
1967 using ArrayInspector = std::function<void(
const std::vector<int64>&)>;
1968 using ConstraintInspector = std::function<void()>;
1970 void RegisterInspectors() {
1971 expr_inspectors_[kExpressionArgument] = [
this](
const IntExpr* expr) {
1974 expr_inspectors_[kLeftArgument] = [
this](
const IntExpr* expr) {
1977 expr_inspectors_[kRightArgument] = [
this](
const IntExpr* expr) {
1980 array_inspectors_[kStartsArgument] =
1981 [
this](
const std::vector<int64>& int_array) {
1982 starts_argument_ = int_array;
1984 array_inspectors_[kEndsArgument] =
1985 [
this](
const std::vector<int64>& int_array) {
1986 ends_argument_ = int_array;
1988 constraint_inspectors_[kNotMember] = [
this]() {
1989 std::pair<RoutingDimension*, int> dim_index;
1992 const int index = dim_index.second;
1993 dimension->forbidden_intervals_[
index].InsertIntervals(starts_argument_,
1995 VLOG(2) << dimension->name() <<
" " <<
index <<
": "
1996 << dimension->forbidden_intervals_[
index].DebugString();
1999 starts_argument_.clear();
2000 ends_argument_.clear();
2002 constraint_inspectors_[kEquality] = [
this]() {
2004 int right_index = 0;
2005 if (
gtl::FindCopy(vehicle_var_to_indices_, left_, &left_index) &&
2006 gtl::FindCopy(vehicle_var_to_indices_, right_, &right_index)) {
2007 VLOG(2) <<
"Vehicle variables for " << left_index <<
" and "
2008 << right_index <<
" are equal.";
2009 same_vehicle_components_.AddArc(left_index, right_index);
2014 constraint_inspectors_[kLessOrEqual] = [
this]() {
2015 std::pair<RoutingDimension*, int> left_index;
2016 std::pair<RoutingDimension*, int> right_index;
2017 if (
gtl::FindCopy(cumul_to_dim_indices_, left_, &left_index) &&
2018 gtl::FindCopy(cumul_to_dim_indices_, right_, &right_index)) {
2020 if (dimension == right_index.first) {
2021 VLOG(2) <<
"For dimension " << dimension->name() <<
", cumul for "
2022 << left_index.second <<
" is less than " << right_index.second
2024 dimension->path_precedence_graph_.AddArc(left_index.second,
2025 right_index.second);
2034 ConnectedComponents<int, int> same_vehicle_components_;
2035 absl::flat_hash_map<const IntExpr*, std::pair<RoutingDimension*, int>>
2036 cumul_to_dim_indices_;
2037 absl::flat_hash_map<const IntExpr*, int> vehicle_var_to_indices_;
2038 absl::flat_hash_map<std::string, ExprInspector> expr_inspectors_;
2039 absl::flat_hash_map<std::string, ArrayInspector> array_inspectors_;
2040 absl::flat_hash_map<std::string, ConstraintInspector> constraint_inspectors_;
2041 const IntExpr*
expr_ =
nullptr;
2042 const IntExpr* left_ =
nullptr;
2043 const IntExpr* right_ =
nullptr;
2044 std::vector<int64> starts_argument_;
2045 std::vector<int64> ends_argument_;
2051 if (!error.empty()) {
2053 LOG(ERROR) <<
"Invalid RoutingSearchParameters: " << error;
2057 LOG(WARNING) <<
"Model already closed";
2063 dimension->CloseModel(UsesLightPropagation(
parameters));
2066 ComputeVehicleClasses();
2067 ComputeVehicleTypes();
2068 FinalizeVisitTypes();
2069 vehicle_start_class_callback_ = [
this](
int64 start) {
2070 return GetVehicleStartClass(start);
2073 AddNoCycleConstraintInternal();
2075 const int size =
Size();
2078 for (
int i = 0; i < vehicles_; ++i) {
2079 const int64 start = starts_[i];
2080 const int64 end = ends_[i];
2081 solver_->AddConstraint(
2082 solver_->MakeEquality(vehicle_vars_[start], solver_->MakeIntConst(i)));
2083 solver_->AddConstraint(
2084 solver_->MakeEquality(vehicle_vars_[end], solver_->MakeIntConst(i)));
2085 solver_->AddConstraint(
2086 solver_->MakeIsDifferentCstCt(nexts_[start], end, vehicle_active_[i]));
2087 if (consider_empty_route_costs_[i]) {
2088 vehicle_costs_considered_[i]->SetMin(1);
2090 solver_->AddConstraint(solver_->MakeEquality(
2091 vehicle_active_[i], vehicle_costs_considered_[i]));
2096 if (vehicles_ > max_active_vehicles_) {
2097 solver_->AddConstraint(
2098 solver_->MakeSumLessOrEqual(vehicle_active_, max_active_vehicles_));
2106 if (vehicles_ > 1) {
2107 std::vector<IntVar*> zero_transit(size, solver_->MakeIntConst(0));
2108 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
2109 nexts_, active_, vehicle_vars_, zero_transit));
2114 for (
int i = 0; i < size; ++i) {
2116 active_[i]->SetValue(1);
2122 const absl::flat_hash_set<VisitTypePolicy>*
const infeasible_policies =
2124 if (infeasible_policies !=
nullptr &&
2126 active_[i]->SetValue(0);
2131 for (
int i = 0; i < allowed_vehicles_.size(); ++i) {
2132 const auto& allowed_vehicles = allowed_vehicles_[i];
2133 if (!allowed_vehicles.empty()) {
2135 vehicles.reserve(allowed_vehicles.size() + 1);
2137 for (
int vehicle : allowed_vehicles) {
2145 for (
int i = 0; i < size; ++i) {
2147 solver_->AddConstraint(solver_->RevAlloc(
2148 new DifferentFromValues(solver_.get(), nexts_[i], starts_)));
2150 solver_->AddConstraint(
2151 solver_->MakeIsDifferentCstCt(nexts_[i], i, active_[i]));
2156 for (
int i = 0; i < size; ++i) {
2157 solver_->AddConstraint(
2158 solver_->MakeIsDifferentCstCt(vehicle_vars_[i], -1, active_[i]));
2162 solver_->AddConstraint(
2167 for (
int i = 0; i < vehicles_; ++i) {
2168 std::vector<int64> forbidden_ends;
2169 forbidden_ends.reserve(vehicles_ - 1);
2170 for (
int j = 0; j < vehicles_; ++j) {
2172 forbidden_ends.push_back(ends_[j]);
2175 solver_->AddConstraint(solver_->RevAlloc(
new DifferentFromValues(
2176 solver_.get(), nexts_[starts_[i]], std::move(forbidden_ends))));
2180 for (
const int64 end : ends_) {
2181 is_bound_to_end_[end]->SetValue(1);
2184 std::vector<IntVar*> cost_elements;
2186 if (vehicles_ > 0) {
2187 for (
int node_index = 0; node_index < size; ++node_index) {
2189 AppendHomogeneousArcCosts(
parameters, node_index, &cost_elements);
2191 AppendArcCosts(
parameters, node_index, &cost_elements);
2194 if (vehicle_amortized_cost_factors_set_) {
2195 std::vector<IntVar*> route_lengths;
2196 solver_->MakeIntVarArray(vehicles_, 0, size, &route_lengths);
2197 solver_->AddConstraint(
2198 solver_->MakeDistribute(vehicle_vars_, route_lengths));
2199 std::vector<IntVar*> vehicle_used;
2200 for (
int i = 0; i < vehicles_; i++) {
2202 vehicle_used.push_back(
2203 solver_->MakeIsGreaterCstVar(route_lengths[i], 2));
2206 ->MakeProd(solver_->MakeOpposite(solver_->MakeSquare(
2207 solver_->MakeSum(route_lengths[i], -2))),
2208 quadratic_cost_factor_of_vehicle_[i])
2210 cost_elements.push_back(
var);
2212 IntVar*
const vehicle_usage_cost =
2213 solver_->MakeScalProd(vehicle_used, linear_cost_factor_of_vehicle_)
2215 cost_elements.push_back(vehicle_usage_cost);
2220 dimension->SetupGlobalSpanCost(&cost_elements);
2221 dimension->SetupSlackAndDependentTransitCosts();
2222 const std::vector<int64>& span_costs =
2223 dimension->vehicle_span_cost_coefficients();
2224 const std::vector<int64>& span_ubs = dimension->vehicle_span_upper_bounds();
2225 const bool has_span_constraint =
2226 std::any_of(span_costs.begin(), span_costs.end(),
2227 [](
int64 coeff) { return coeff != 0; }) ||
2228 std::any_of(span_ubs.begin(), span_ubs.end(),
2229 [](
int64 value) { return value < kint64max; }) ||
2230 dimension->HasSoftSpanUpperBounds() ||
2231 dimension->HasQuadraticCostSoftSpanUpperBounds();
2232 if (has_span_constraint) {
2233 std::vector<IntVar*> spans(
vehicles(),
nullptr);
2234 std::vector<IntVar*> total_slacks(
vehicles(),
nullptr);
2236 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2238 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2240 if (span_costs[vehicle] != 0) {
2241 total_slacks[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2244 if (dimension->HasSoftSpanUpperBounds()) {
2245 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2246 if (spans[vehicle])
continue;
2248 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2249 if (bound_cost.
cost == 0)
continue;
2250 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2253 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2254 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2255 if (spans[vehicle])
continue;
2257 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2258 if (bound_cost.
cost == 0)
continue;
2259 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2262 solver_->AddConstraint(
2266 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2267 if (!spans[vehicle] && !total_slacks[vehicle])
continue;
2268 if (spans[vehicle]) {
2277 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2278 if (span_costs[vehicle] == 0)
continue;
2279 DCHECK(total_slacks[vehicle] !=
nullptr);
2280 IntVar*
const slack_amount =
2282 ->MakeProd(vehicle_costs_considered_[vehicle],
2283 total_slacks[vehicle])
2285 IntVar*
const slack_cost =
2286 solver_->MakeProd(slack_amount, span_costs[vehicle])->Var();
2287 cost_elements.push_back(slack_cost);
2289 span_costs[vehicle]);
2291 if (dimension->HasSoftSpanUpperBounds()) {
2292 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2293 const auto bound_cost =
2294 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2295 if (bound_cost.cost == 0 || bound_cost.bound ==
kint64max)
continue;
2296 DCHECK(spans[vehicle] !=
nullptr);
2299 IntVar*
const span_violation_amount =
2302 vehicle_costs_considered_[vehicle],
2304 solver_->MakeSum(spans[vehicle], -bound_cost.bound),
2307 IntVar*
const span_violation_cost =
2308 solver_->MakeProd(span_violation_amount, bound_cost.cost)->Var();
2309 cost_elements.push_back(span_violation_cost);
2314 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2315 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2316 const auto bound_cost =
2317 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2318 if (bound_cost.cost == 0 || bound_cost.bound ==
kint64max)
continue;
2319 DCHECK(spans[vehicle] !=
nullptr);
2322 IntExpr* max0 = solver_->MakeMax(
2323 solver_->MakeSum(spans[vehicle], -bound_cost.bound), 0);
2324 IntVar*
const squared_span_violation_amount =
2326 ->MakeProd(vehicle_costs_considered_[vehicle],
2327 solver_->MakeSquare(max0))
2329 IntVar*
const span_violation_cost =
2330 solver_->MakeProd(squared_span_violation_amount, bound_cost.cost)
2332 cost_elements.push_back(span_violation_cost);
2341 IntVar* penalty_var = CreateDisjunction(i);
2342 if (penalty_var !=
nullptr) {
2343 cost_elements.push_back(penalty_var);
2348 dimension->SetupCumulVarSoftLowerBoundCosts(&cost_elements);
2349 dimension->SetupCumulVarSoftUpperBoundCosts(&cost_elements);
2350 dimension->SetupCumulVarPiecewiseLinearCosts(&cost_elements);
2353 for (
int i = 0; i < same_vehicle_costs_.size(); ++i) {
2354 cost_elements.push_back(CreateSameVehicleCost(i));
2356 cost_ = solver_->MakeSum(cost_elements)->Var();
2357 cost_->set_name(
"Cost");
2360 std::vector<std::pair<int, int>> pickup_delivery_precedences;
2361 for (
const auto& pair : pickup_delivery_pairs_) {
2362 DCHECK(!pair.first.empty() && !pair.second.empty());
2363 for (
int pickup : pair.first) {
2364 for (
int delivery : pair.second) {
2365 pickup_delivery_precedences.emplace_back(pickup, delivery);
2369 std::vector<int> lifo_vehicles;
2370 std::vector<int> fifo_vehicles;
2371 for (
int i = 0; i < vehicles_; ++i) {
2372 switch (vehicle_pickup_delivery_policy_[i]) {
2376 lifo_vehicles.push_back(
Start(i));
2379 fifo_vehicles.push_back(
Start(i));
2383 solver_->AddConstraint(solver_->MakePathPrecedenceConstraint(
2384 nexts_, pickup_delivery_precedences, lifo_vehicles, fifo_vehicles));
2387 enable_deep_serialization_ =
false;
2388 std::unique_ptr<RoutingModelInspector> inspector(
2390 solver_->Accept(inspector.get());
2391 enable_deep_serialization_ =
true;
2397 dimension->GetPathPrecedenceGraph();
2398 std::vector<std::pair<int, int>> path_precedences;
2400 for (
const auto head : graph[
tail]) {
2401 path_precedences.emplace_back(
tail,
head);
2404 if (!path_precedences.empty()) {
2405 solver_->AddConstraint(solver_->MakePathTransitPrecedenceConstraint(
2406 nexts_, dimension->transits(), path_precedences));
2411 dimension->GetNodePrecedences()) {
2412 const int64 first_node = node_precedence.first_node;
2413 const int64 second_node = node_precedence.second_node;
2414 IntExpr*
const nodes_are_selected =
2415 solver_->MakeMin(active_[first_node], active_[second_node]);
2416 IntExpr*
const cumul_difference = solver_->MakeDifference(
2417 dimension->CumulVar(second_node), dimension->CumulVar(first_node));
2418 IntVar*
const cumul_difference_is_ge_offset =
2419 solver_->MakeIsGreaterOrEqualCstVar(cumul_difference,
2420 node_precedence.offset);
2423 solver_->AddConstraint(solver_->MakeLessOrEqual(
2424 nodes_are_selected->Var(), cumul_difference_is_ge_offset));
2435 CreateFirstSolutionDecisionBuilders(
parameters);
2436 error = FindErrorInSearchParametersForModel(
parameters);
2437 if (!error.empty()) {
2439 LOG(ERROR) <<
"Invalid RoutingSearchParameters for this model: " << error;
2446 Link(std::pair<int, int> link,
double value,
int vehicle_class,
2450 vehicle_class(vehicle_class),
2451 start_depot(start_depot),
2452 end_depot(end_depot) {}
2476 bool check_assignment,
int64 num_indices,
2477 const std::vector<Link>& links_list)
2478 : assignment_(assignment),
2480 check_assignment_(check_assignment),
2481 solver_(model_->
solver()),
2482 num_indices_(num_indices),
2483 links_list_(links_list),
2484 nexts_(model_->
Nexts()),
2485 in_route_(num_indices_, -1),
2487 index_to_chain_index_(num_indices, -1),
2488 index_to_vehicle_class_index_(num_indices, -1) {
2490 const std::vector<std::string> dimension_names =
2491 model_->GetAllDimensionNames();
2492 dimensions_.assign(dimension_names.size(),
nullptr);
2493 for (
int i = 0; i < dimension_names.size(); ++i) {
2494 dimensions_[i] = &model_->GetDimensionOrDie(dimension_names[i]);
2497 cumuls_.resize(dimensions_.size());
2498 for (std::vector<int64>& cumuls :
cumuls_) {
2499 cumuls.resize(num_indices_);
2501 new_possible_cumuls_.resize(dimensions_.size());
2507 model_->solver()->TopPeriodicCheck();
2510 if (!model_->IsStart(
index) && !model_->IsEnd(
index)) {
2511 std::vector<int> route(1,
index);
2512 routes_.push_back(route);
2513 in_route_[
index] = routes_.size() - 1;
2517 for (
const Link& link : links_list_) {
2518 model_->solver()->TopPeriodicCheck();
2519 const int index1 = link.link.first;
2520 const int index2 = link.link.second;
2521 const int vehicle_class = link.vehicle_class;
2522 const int64 start_depot = link.start_depot;
2523 const int64 end_depot = link.end_depot;
2526 if (index_to_vehicle_class_index_[index1] < 0) {
2527 for (
int dimension_index = 0; dimension_index < dimensions_.size();
2528 ++dimension_index) {
2529 cumuls_[dimension_index][index1] =
2530 std::max(dimensions_[dimension_index]->GetTransitValue(
2531 start_depot, index1, 0),
2532 dimensions_[dimension_index]->CumulVar(index1)->Min());
2535 if (index_to_vehicle_class_index_[index2] < 0) {
2536 for (
int dimension_index = 0; dimension_index < dimensions_.size();
2537 ++dimension_index) {
2538 cumuls_[dimension_index][index2] =
2539 std::max(dimensions_[dimension_index]->GetTransitValue(
2540 start_depot, index2, 0),
2541 dimensions_[dimension_index]->CumulVar(index2)->Min());
2545 const int route_index1 = in_route_[index1];
2546 const int route_index2 = in_route_[index2];
2548 route_index1 >= 0 && route_index2 >= 0 &&
2549 FeasibleMerge(routes_[route_index1], routes_[route_index2], index1,
2550 index2, route_index1, route_index2, vehicle_class,
2551 start_depot, end_depot);
2552 if (Merge(merge, route_index1, route_index2)) {
2553 index_to_vehicle_class_index_[index1] = vehicle_class;
2554 index_to_vehicle_class_index_[index2] = vehicle_class;
2558 model_->solver()->TopPeriodicCheck();
2562 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
2564 final_chains_.push_back(chains_[chain_index]);
2567 std::sort(final_chains_.begin(), final_chains_.end(), ChainComparator);
2568 for (
int route_index = 0; route_index < routes_.size(); ++route_index) {
2570 final_routes_.push_back(routes_[route_index]);
2573 std::sort(final_routes_.begin(), final_routes_.end(), RouteComparator);
2575 const int extra_vehicles =
std::max(
2576 0,
static_cast<int>(final_chains_.size()) - model_->vehicles());
2578 int chain_index = 0;
2579 for (chain_index = extra_vehicles; chain_index < final_chains_.size();
2581 if (chain_index - extra_vehicles >= model_->vehicles()) {
2584 const int start = final_chains_[chain_index].head;
2585 const int end = final_chains_[chain_index].tail;
2587 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
2588 assignment_->SetValue(
2589 model_->NextVar(model_->Start(chain_index - extra_vehicles)), start);
2590 assignment_->Add(nexts_[end]);
2591 assignment_->SetValue(nexts_[end],
2592 model_->End(chain_index - extra_vehicles));
2596 for (
int route_index = 0; route_index < final_routes_.size();
2598 if (chain_index - extra_vehicles >= model_->vehicles()) {
2601 DCHECK_LT(route_index, final_routes_.size());
2602 const int head = final_routes_[route_index].front();
2603 const int tail = final_routes_[route_index].back();
2606 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
2607 assignment_->SetValue(
2608 model_->NextVar(model_->Start(chain_index - extra_vehicles)),
head);
2609 assignment_->Add(nexts_[
tail]);
2610 assignment_->SetValue(nexts_[
tail],
2611 model_->End(chain_index - extra_vehicles));
2619 if (!assignment_->Contains(
next)) {
2620 assignment_->Add(
next);
2629 return final_routes_;
2633 enum MergeStatus { FIRST_SECOND, SECOND_FIRST, NO_MERGE };
2636 bool operator()(
const std::vector<int>& route1,
2637 const std::vector<int>& route2)
const {
2638 return (route1.size() < route2.size());
2649 bool operator()(
const Chain& chain1,
const Chain& chain2)
const {
2650 return (chain1.nodes < chain2.nodes);
2654 bool Head(
int node)
const {
2655 return (node == routes_[in_route_[node]].front());
2658 bool Tail(
int node)
const {
2659 return (node == routes_[in_route_[node]].back());
2662 bool FeasibleRoute(
const std::vector<int>& route,
int64 route_cumul,
2663 int dimension_index) {
2665 std::vector<int>::const_iterator it = route.begin();
2666 int64 cumul = route_cumul;
2667 while (it != route.end()) {
2668 const int previous = *it;
2669 const int64 cumul_previous = cumul;
2673 if (it == route.end()) {
2676 const int next = *it;
2677 int64 available_from_previous =
2678 cumul_previous + dimension.GetTransitValue(previous,
next, 0);
2679 int64 available_cumul_next =
2682 const int64 slack = available_cumul_next - available_from_previous;
2683 if (slack > dimension.SlackVar(previous)->Max()) {
2684 available_cumul_next =
2685 available_from_previous + dimension.SlackVar(previous)->Max();
2688 if (available_cumul_next > dimension.CumulVar(
next)->Max()) {
2691 if (available_cumul_next <=
cumuls_[dimension_index][
next]) {
2694 cumul = available_cumul_next;
2699 bool CheckRouteConnection(
const std::vector<int>& route1,
2700 const std::vector<int>& route2,
int dimension_index,
2702 const int tail1 = route1.back();
2703 const int head2 = route2.front();
2704 const int tail2 = route2.back();
2706 int non_depot_node = -1;
2707 for (
int node = 0; node < num_indices_; ++node) {
2708 if (!model_->IsStart(node) && !model_->IsEnd(node)) {
2709 non_depot_node = node;
2713 CHECK_GE(non_depot_node, 0);
2714 const int64 depot_threashold =
2715 std::max(dimension.SlackVar(non_depot_node)->Max(),
2716 dimension.CumulVar(non_depot_node)->Max());
2718 int64 available_from_tail1 =
cumuls_[dimension_index][tail1] +
2719 dimension.GetTransitValue(tail1, head2, 0);
2720 int64 new_available_cumul_head2 =
2723 const int64 slack = new_available_cumul_head2 - available_from_tail1;
2724 if (slack > dimension.SlackVar(tail1)->Max()) {
2725 new_available_cumul_head2 =
2726 available_from_tail1 + dimension.SlackVar(tail1)->Max();
2729 bool feasible_route =
true;
2730 if (new_available_cumul_head2 > dimension.CumulVar(head2)->Max()) {
2733 if (new_available_cumul_head2 <=
cumuls_[dimension_index][head2]) {
2738 FeasibleRoute(route2, new_available_cumul_head2, dimension_index);
2739 const int64 new_possible_cumul_tail2 =
2741 ? new_possible_cumuls_[dimension_index][tail2]
2742 :
cumuls_[dimension_index][tail2];
2744 if (!feasible_route || (new_possible_cumul_tail2 +
2745 dimension.GetTransitValue(tail2, end_depot, 0) >
2746 depot_threashold)) {
2752 bool FeasibleMerge(
const std::vector<int>& route1,
2753 const std::vector<int>& route2,
int node1,
int node2,
2754 int route_index1,
int route_index2,
int vehicle_class,
2756 if ((route_index1 == route_index2) || !(Tail(node1) && Head(node2))) {
2761 if (!((index_to_vehicle_class_index_[node1] == -1 &&
2762 index_to_vehicle_class_index_[node2] == -1) ||
2763 (index_to_vehicle_class_index_[node1] == vehicle_class &&
2764 index_to_vehicle_class_index_[node2] == -1) ||
2765 (index_to_vehicle_class_index_[node1] == -1 &&
2766 index_to_vehicle_class_index_[node2] == vehicle_class) ||
2767 (index_to_vehicle_class_index_[node1] == vehicle_class &&
2768 index_to_vehicle_class_index_[node2] == vehicle_class))) {
2774 for (
int dimension_index = 0; dimension_index < dimensions_.size();
2775 ++dimension_index) {
2776 new_possible_cumuls_[dimension_index].clear();
2777 merge = merge && CheckRouteConnection(route1, route2, dimension_index,
2778 start_depot, end_depot);
2786 bool CheckTempAssignment(Assignment*
const temp_assignment,
2787 int new_chain_index,
int old_chain_index,
int head1,
2788 int tail1,
int head2,
int tail2) {
2791 if (new_chain_index >= model_->vehicles())
return false;
2792 const int start = head1;
2793 temp_assignment->Add(model_->NextVar(model_->Start(new_chain_index)));
2794 temp_assignment->SetValue(model_->NextVar(model_->Start(new_chain_index)),
2796 temp_assignment->Add(nexts_[tail1]);
2797 temp_assignment->SetValue(nexts_[tail1], head2);
2798 temp_assignment->Add(nexts_[tail2]);
2799 temp_assignment->SetValue(nexts_[tail2], model_->End(new_chain_index));
2800 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
2801 if ((chain_index != new_chain_index) &&
2802 (chain_index != old_chain_index) &&
2804 const int start = chains_[chain_index].head;
2805 const int end = chains_[chain_index].tail;
2806 temp_assignment->Add(model_->NextVar(model_->Start(chain_index)));
2807 temp_assignment->SetValue(model_->NextVar(model_->Start(chain_index)),
2809 temp_assignment->Add(nexts_[end]);
2810 temp_assignment->SetValue(nexts_[end], model_->End(chain_index));
2813 return solver_->Solve(solver_->MakeRestoreAssignment(temp_assignment));
2816 bool UpdateAssignment(
const std::vector<int>& route1,
2817 const std::vector<int>& route2) {
2818 bool feasible =
true;
2819 const int head1 = route1.front();
2820 const int tail1 = route1.back();
2821 const int head2 = route2.front();
2822 const int tail2 = route2.back();
2823 const int chain_index1 = index_to_chain_index_[head1];
2824 const int chain_index2 = index_to_chain_index_[head2];
2825 if (chain_index1 < 0 && chain_index2 < 0) {
2826 const int chain_index = chains_.size();
2827 if (check_assignment_) {
2828 Assignment*
const temp_assignment =
2829 solver_->MakeAssignment(assignment_);
2830 feasible = CheckTempAssignment(temp_assignment, chain_index, -1, head1,
2831 tail1, head2, tail2);
2838 index_to_chain_index_[head1] = chain_index;
2839 index_to_chain_index_[tail2] = chain_index;
2840 chains_.push_back(chain);
2842 }
else if (chain_index1 >= 0 && chain_index2 < 0) {
2843 if (check_assignment_) {
2844 Assignment*
const temp_assignment =
2845 solver_->MakeAssignment(assignment_);
2847 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
2848 head1, tail1, head2, tail2);
2851 index_to_chain_index_[tail2] = chain_index1;
2852 chains_[chain_index1].head = head1;
2853 chains_[chain_index1].tail = tail2;
2854 ++chains_[chain_index1].nodes;
2856 }
else if (chain_index1 < 0 && chain_index2 >= 0) {
2857 if (check_assignment_) {
2858 Assignment*
const temp_assignment =
2859 solver_->MakeAssignment(assignment_);
2861 CheckTempAssignment(temp_assignment, chain_index2, chain_index1,
2862 head1, tail1, head2, tail2);
2865 index_to_chain_index_[head1] = chain_index2;
2866 chains_[chain_index2].head = head1;
2867 chains_[chain_index2].tail = tail2;
2868 ++chains_[chain_index2].nodes;
2871 if (check_assignment_) {
2872 Assignment*
const temp_assignment =
2873 solver_->MakeAssignment(assignment_);
2875 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
2876 head1, tail1, head2, tail2);
2879 index_to_chain_index_[tail2] = chain_index1;
2880 chains_[chain_index1].head = head1;
2881 chains_[chain_index1].tail = tail2;
2882 chains_[chain_index1].nodes += chains_[chain_index2].nodes;
2883 deleted_chains_.insert(chain_index2);
2887 assignment_->Add(nexts_[tail1]);
2888 assignment_->SetValue(nexts_[tail1], head2);
2893 bool Merge(
bool merge,
int index1,
int index2) {
2895 if (UpdateAssignment(routes_[index1], routes_[index2])) {
2897 for (
const int node : routes_[index2]) {
2898 in_route_[node] = index1;
2899 routes_[index1].push_back(node);
2901 for (
int dimension_index = 0; dimension_index < dimensions_.size();
2902 ++dimension_index) {
2903 for (
const std::pair<int, int64> new_possible_cumul :
2904 new_possible_cumuls_[dimension_index]) {
2905 cumuls_[dimension_index][new_possible_cumul.first] =
2906 new_possible_cumul.second;
2909 deleted_routes_.insert(index2);
2916 Assignment*
const assignment_;
2918 const bool check_assignment_;
2919 Solver*
const solver_;
2920 const int64 num_indices_;
2921 const std::vector<Link> links_list_;
2922 std::vector<IntVar*> nexts_;
2923 std::vector<const RoutingDimension*> dimensions_;
2924 std::vector<std::vector<int64>>
cumuls_;
2925 std::vector<absl::flat_hash_map<int, int64>> new_possible_cumuls_;
2926 std::vector<std::vector<int>> routes_;
2927 std::vector<int> in_route_;
2928 absl::flat_hash_set<int> deleted_routes_;
2929 std::vector<std::vector<int>> final_routes_;
2930 std::vector<Chain> chains_;
2931 absl::flat_hash_set<int> deleted_chains_;
2932 std::vector<Chain> final_chains_;
2933 std::vector<int> index_to_chain_index_;
2934 std::vector<int> index_to_vehicle_class_index_;
2940 :
index(
index), angle(angle), distance(distance) {}
2961 : coordinates_(2 * points.size(), 0), sectors_(1) {
2962 for (
int64 i = 0; i < points.size(); ++i) {
2963 coordinates_[2 * i] = points[i].first;
2964 coordinates_[2 * i + 1] = points[i].second;
2971 const double pi_rad = 3.14159265;
2973 const int x0 = coordinates_[0];
2974 const int y0 = coordinates_[1];
2976 std::vector<SweepIndex> sweep_indices;
2977 for (
int64 index = 0; index < static_cast<int>(coordinates_.size()) / 2;
2979 const int x = coordinates_[2 *
index];
2980 const int y = coordinates_[2 *
index + 1];
2981 const double x_delta = x - x0;
2982 const double y_delta = y - y0;
2983 double square_distance = x_delta * x_delta + y_delta * y_delta;
2984 double angle = square_distance == 0 ? 0 : std::atan2(y_delta, x_delta);
2985 angle = angle >= 0 ? angle : 2 * pi_rad + angle;
2987 sweep_indices.push_back(sweep_index);
2989 std::sort(sweep_indices.begin(), sweep_indices.end(),
2992 const int size =
static_cast<int>(sweep_indices.size()) / sectors_;
2993 for (
int sector = 0; sector < sectors_; ++sector) {
2994 std::vector<SweepIndex> cluster;
2995 std::vector<SweepIndex>::iterator begin =
2996 sweep_indices.begin() + sector * size;
2997 std::vector<SweepIndex>::iterator end =
2998 sector == sectors_ - 1 ? sweep_indices.end()
2999 : sweep_indices.begin() + (sector + 1) * size;
3002 for (
const SweepIndex& sweep_index : sweep_indices) {
3003 indices->push_back(sweep_index.index);
3013 : model_(
model), check_assignment_(check_assignment) {}
3016 Decision*
Next(Solver*
const solver)
override {
3021 Assignment*
const assignment = solver->MakeAssignment();
3022 route_constructor_ = absl::make_unique<RouteConstructor>(
3023 assignment, model_, check_assignment_, num_indices_, links_);
3025 route_constructor_->Construct();
3026 route_constructor_.reset(
nullptr);
3028 assignment->Restore();
3035 const int depot = model_->
GetDepot();
3037 if (FLAGS_sweep_sectors > 0 && FLAGS_sweep_sectors < num_indices_) {
3040 std::vector<int64> indices;
3042 for (
int i = 0; i < indices.size() - 1; ++i) {
3043 const int64 first = indices[i];
3044 const int64 second = indices[i + 1];
3045 if ((model_->
IsStart(first) || !model_->
IsEnd(first)) &&
3047 if (first != depot && second != depot) {
3048 Link link(std::make_pair(first, second), 0, 0, depot, depot);
3049 links_.push_back(link);
3055 RoutingModel*
const model_;
3056 std::unique_ptr<RouteConstructor> route_constructor_;
3057 const bool check_assignment_;
3059 std::vector<Link> links_;
3067 class AllUnperformed :
public DecisionBuilder {
3070 explicit AllUnperformed(RoutingModel*
const model) : model_(
model) {}
3071 ~AllUnperformed()
override {}
3072 Decision* Next(Solver*
const solver)
override {
3075 model_->CostVar()->FreezeQueue();
3076 for (
int i = 0; i < model_->Size(); ++i) {
3077 if (!model_->IsStart(i)) {
3078 model_->ActiveVar(i)->SetValue(0);
3081 model_->CostVar()->UnfreezeQueue();
3086 RoutingModel*
const model_;
3091 monitors_.push_back(monitor);
3095 class AtSolutionCallbackMonitor :
public SearchMonitor {
3097 AtSolutionCallbackMonitor(Solver* solver, std::function<
void()>
callback)
3098 : SearchMonitor(solver), callback_(std::move(
callback)) {}
3099 bool AtSolution()
override {
3105 std::function<void()> callback_;
3111 new AtSolutionCallbackMonitor(solver_.get(), std::move(
callback))));
3121 std::vector<const Assignment*>* solutions) {
3126 absl::Duration GetTimeLimit(
const RoutingSearchParameters&
parameters) {
3127 if (!
parameters.has_time_limit())
return absl::InfiniteDuration();
3131 absl::Duration GetLnsTimeLimit(
const RoutingSearchParameters&
parameters) {
3132 if (!
parameters.has_lns_time_limit())
return absl::InfiniteDuration();
3139 void MakeAllUnperformed(
const RoutingModel*
model, Assignment* assignment) {
3140 assignment->Clear();
3141 for (
int i = 0; i <
model->Nexts().size(); ++i) {
3142 if (!
model->IsStart(i)) {
3143 assignment->Add(
model->NextVar(i))->SetValue(i);
3146 for (
int vehicle = 0; vehicle <
model->vehicles(); ++vehicle) {
3147 assignment->Add(
model->NextVar(
model->Start(vehicle)))
3148 ->SetValue(
model->End(vehicle));
3153 bool RoutingModel::AppendAssignmentIfFeasible(
3154 const Assignment& assignment,
3155 std::vector<std::unique_ptr<Assignment>>* assignments) {
3156 tmp_assignment_->CopyIntersection(&assignment);
3157 solver_->Solve(restore_tmp_assignment_, collect_one_assignment_,
3158 GetOrCreateLimit());
3159 if (collect_one_assignment_->solution_count() == 1) {
3160 assignments->push_back(absl::make_unique<Assignment>(solver_.get()));
3161 assignments->back()->Copy(collect_one_assignment_->solution(0));
3167 void RoutingModel::LogSolution(
const RoutingSearchParameters&
parameters,
3168 const std::string& description,
3171 const double cost_scaling_factor =
parameters.log_cost_scaling_factor();
3172 const double cost_offset =
parameters.log_cost_offset();
3173 const std::string cost_string =
3174 cost_scaling_factor == 1.0 && cost_offset == 0.0
3175 ? absl::StrCat(solution_cost)
3177 "%d (%.8lf)", solution_cost,
3178 cost_scaling_factor * (solution_cost + cost_offset));
3179 LOG(INFO) << absl::StrFormat(
3180 "%s (%s, time = %d ms, memory used = %s)", description, cost_string,
3181 solver_->wall_time() - start_time_ms, memory_str);
3185 const Assignment* assignment,
const RoutingSearchParameters&
parameters,
3186 std::vector<const Assignment*>* solutions) {
3187 const int64 start_time_ms = solver_->wall_time();
3189 VLOG(1) <<
"Search parameters:\n" <<
parameters.DebugString();
3190 if (solutions !=
nullptr) solutions->clear();
3204 const int time_limit_shares = 1 + !global_dimension_optimizers_.empty() +
3205 !local_dimension_optimizers_.empty();
3206 const absl::Duration first_solution_lns_time_limit =
3209 first_solution_lns_limit_->UpdateLimits(first_solution_lns_time_limit,
3212 std::vector<std::unique_ptr<Assignment>> solution_pool;
3214 if (
nullptr == assignment) {
3215 bool solution_found =
false;
3216 Assignment matching(solver_.get());
3218 AppendAssignmentIfFeasible(matching, &solution_pool)) {
3220 LogSolution(
parameters,
"Min-Cost Flow Solution",
3221 solution_pool.back()->ObjectiveValue(), start_time_ms);
3223 solution_found =
true;
3225 if (!solution_found) {
3228 Assignment unperformed(solver_.get());
3229 MakeAllUnperformed(
this, &unperformed);
3230 if (AppendAssignmentIfFeasible(unperformed, &solution_pool) &&
3232 LogSolution(
parameters,
"All Unperformed Solution",
3233 solution_pool.back()->ObjectiveValue(), start_time_ms);
3235 const absl::Duration elapsed_time =
3236 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3237 const absl::Duration time_left =
3239 if (time_left >= absl::ZeroDuration()) {
3243 solver_->Solve(solve_db_, monitors_);
3247 assignment_->CopyIntersection(assignment);
3248 solver_->Solve(improve_db_, monitors_);
3253 const int solution_count = collect_assignments_->solution_count();
3254 Assignment*
const cp_solution =
3255 solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
3257 Assignment sat_solution(solver_.get());
3259 AppendAssignmentIfFeasible(sat_solution, &solution_pool) &&
3261 LogSolution(
parameters,
"SAT", solution_pool.back()->ObjectiveValue(),
3266 const absl::Duration elapsed_time =
3267 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3268 const int solution_count = collect_assignments_->solution_count();
3269 if (solution_count >= 1 || !solution_pool.empty()) {
3271 if (solutions !=
nullptr) {
3272 for (
int i = 0; i < solution_count; ++i) {
3273 solutions->push_back(
3274 solver_->MakeAssignment(collect_assignments_->solution(i)));
3276 for (
const auto& solution : solution_pool) {
3277 if (solutions->empty() ||
3278 solution->ObjectiveValue() < solutions->back()->ObjectiveValue()) {
3279 solutions->push_back(solver_->MakeAssignment(solution.get()));
3282 return solutions->back();
3284 Assignment* best_assignment =
3285 solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
3287 for (
const auto& solution : solution_pool) {
3288 if (best_assignment ==
nullptr ||
3289 solution->ObjectiveValue() < best_assignment->ObjectiveValue()) {
3290 best_assignment = solution.get();
3293 return solver_->MakeAssignment(best_assignment);
3295 if (elapsed_time >= GetTimeLimit(
parameters)) {
3305 Assignment* target_assignment,
const RoutingModel* source_model,
3306 const Assignment* source_assignment) {
3307 const int size =
Size();
3308 DCHECK_EQ(size, source_model->
Size());
3309 CHECK_EQ(target_assignment->solver(), solver_.get());
3313 source_model->
Nexts());
3315 std::vector<IntVar*> source_vars(size + size + vehicles_);
3316 std::vector<IntVar*> target_vars(size + size + vehicles_);
3326 source_assignment, source_vars);
3329 target_assignment->AddObjective(cost_);
3344 LOG(WARNING) <<
"Non-closed model not supported.";
3348 LOG(WARNING) <<
"Non-homogeneous vehicle costs not supported";
3351 if (!disjunctions_.
empty()) {
3353 <<
"Node disjunction constraints or optional nodes not supported.";
3356 const int num_nodes =
Size() + vehicles_;
3363 std::unique_ptr<IntVarIterator> iterator(
3364 nexts_[
tail]->MakeDomainIterator(
false));
3365 for (
const int64 head : InitAndGetValues(iterator.get())) {
3387 return linear_sum_assignment.
GetCost();
3392 bool RoutingModel::RouteCanBeUsedByVehicle(
const Assignment& assignment,
3393 int start_index,
int vehicle)
const {
3395 IsStart(start_index) ?
Next(assignment, start_index) : start_index;
3396 while (!
IsEnd(current_index)) {
3397 const IntVar*
const vehicle_var =
VehicleVar(current_index);
3398 if (!vehicle_var->Contains(vehicle)) {
3401 const int next_index =
Next(assignment, current_index);
3402 CHECK_NE(next_index, current_index) <<
"Inactive node inside a route";
3403 current_index = next_index;
3408 bool RoutingModel::ReplaceUnusedVehicle(
3409 int unused_vehicle,
int active_vehicle,
3410 Assignment*
const compact_assignment)
const {
3411 CHECK(compact_assignment !=
nullptr);
3415 const int unused_vehicle_start =
Start(unused_vehicle);
3416 IntVar*
const unused_vehicle_start_var =
NextVar(unused_vehicle_start);
3417 const int unused_vehicle_end =
End(unused_vehicle);
3418 const int active_vehicle_start =
Start(active_vehicle);
3419 const int active_vehicle_end =
End(active_vehicle);
3420 IntVar*
const active_vehicle_start_var =
NextVar(active_vehicle_start);
3421 const int active_vehicle_next =
3422 compact_assignment->Value(active_vehicle_start_var);
3423 compact_assignment->SetValue(unused_vehicle_start_var, active_vehicle_next);
3424 compact_assignment->SetValue(active_vehicle_start_var,
End(active_vehicle));
3427 int current_index = active_vehicle_next;
3428 while (!
IsEnd(current_index)) {
3429 IntVar*
const vehicle_var =
VehicleVar(current_index);
3430 compact_assignment->SetValue(vehicle_var, unused_vehicle);
3431 const int next_index =
Next(*compact_assignment, current_index);
3432 if (
IsEnd(next_index)) {
3433 IntVar*
const last_next_var =
NextVar(current_index);
3434 compact_assignment->SetValue(last_next_var,
End(unused_vehicle));
3436 current_index = next_index;
3441 const std::vector<IntVar*>& transit_variables = dimension->transits();
3442 IntVar*
const unused_vehicle_transit_var =
3443 transit_variables[unused_vehicle_start];
3444 IntVar*
const active_vehicle_transit_var =
3445 transit_variables[active_vehicle_start];
3446 const bool contains_unused_vehicle_transit_var =
3447 compact_assignment->Contains(unused_vehicle_transit_var);
3448 const bool contains_active_vehicle_transit_var =
3449 compact_assignment->Contains(active_vehicle_transit_var);
3450 if (contains_unused_vehicle_transit_var !=
3451 contains_active_vehicle_transit_var) {
3453 LOG(INFO) <<
"The assignment contains transit variable for dimension '"
3454 << dimension->name() <<
"' for some vehicles, but not for all";
3457 if (contains_unused_vehicle_transit_var) {
3458 const int64 old_unused_vehicle_transit =
3459 compact_assignment->Value(unused_vehicle_transit_var);
3460 const int64 old_active_vehicle_transit =
3461 compact_assignment->Value(active_vehicle_transit_var);
3462 compact_assignment->SetValue(unused_vehicle_transit_var,
3463 old_active_vehicle_transit);
3464 compact_assignment->SetValue(active_vehicle_transit_var,
3465 old_unused_vehicle_transit);
3469 const std::vector<IntVar*>& cumul_variables = dimension->cumuls();
3470 IntVar*
const unused_vehicle_cumul_var =
3471 cumul_variables[unused_vehicle_end];
3472 IntVar*
const active_vehicle_cumul_var =
3473 cumul_variables[active_vehicle_end];
3474 const int64 old_unused_vehicle_cumul =
3475 compact_assignment->Value(unused_vehicle_cumul_var);
3476 const int64 old_active_vehicle_cumul =
3477 compact_assignment->Value(active_vehicle_cumul_var);
3478 compact_assignment->SetValue(unused_vehicle_cumul_var,
3479 old_active_vehicle_cumul);
3480 compact_assignment->SetValue(active_vehicle_cumul_var,
3481 old_unused_vehicle_cumul);
3487 const Assignment& assignment)
const {
3488 return CompactAssignmentInternal(assignment,
false);
3492 const Assignment& assignment)
const {
3493 return CompactAssignmentInternal(assignment,
true);
3496 Assignment* RoutingModel::CompactAssignmentInternal(
3497 const Assignment& assignment,
bool check_compact_assignment)
const {
3498 CHECK_EQ(assignment.solver(), solver_.get());
3501 <<
"The costs are not homogeneous, routes cannot be rearranged";
3505 std::unique_ptr<Assignment> compact_assignment(
new Assignment(&assignment));
3506 for (
int vehicle = 0; vehicle < vehicles_ - 1; ++vehicle) {
3510 const int vehicle_start =
Start(vehicle);
3511 const int vehicle_end =
End(vehicle);
3513 int swap_vehicle = vehicles_ - 1;
3514 bool has_more_vehicles_with_route =
false;
3515 for (; swap_vehicle > vehicle; --swap_vehicle) {
3522 has_more_vehicles_with_route =
true;
3523 const int swap_vehicle_start =
Start(swap_vehicle);
3524 const int swap_vehicle_end =
End(swap_vehicle);
3533 if (RouteCanBeUsedByVehicle(*compact_assignment, swap_vehicle_start,
3539 if (swap_vehicle == vehicle) {
3540 if (has_more_vehicles_with_route) {
3544 LOG(INFO) <<
"No vehicle that can be swapped with " << vehicle
3551 if (!ReplaceUnusedVehicle(vehicle, swap_vehicle,
3552 compact_assignment.get())) {
3557 if (check_compact_assignment &&
3558 !solver_->CheckAssignment(compact_assignment.get())) {
3560 LOG(WARNING) <<
"The compacted assignment is not a valid solution";
3563 return compact_assignment.release();
3566 int RoutingModel::FindNextActive(
int index,
3567 const std::vector<int64>& indices)
const {
3570 const int size = indices.size();
3580 CHECK_EQ(vehicles_, 1);
3581 preassignment_->Clear();
3582 IntVar* next_var =
nullptr;
3583 int lock_index = FindNextActive(-1, locks);
3584 const int size = locks.size();
3585 if (lock_index < size) {
3586 next_var =
NextVar(locks[lock_index]);
3587 preassignment_->Add(next_var);
3588 for (lock_index = FindNextActive(lock_index, locks); lock_index < size;
3589 lock_index = FindNextActive(lock_index, locks)) {
3590 preassignment_->SetValue(next_var, locks[lock_index]);
3591 next_var =
NextVar(locks[lock_index]);
3592 preassignment_->Add(next_var);
3599 const std::vector<std::vector<int64>>& locks,
bool close_routes) {
3600 preassignment_->Clear();
3605 const RoutingSearchParameters&
parameters)
const {
3607 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3613 const RoutingSearchParameters&
parameters)
const {
3615 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3621 if (collect_assignments_->solution_count() == 1 && assignment_ !=
nullptr) {
3622 assignment_->CopyIntersection(collect_assignments_->solution(0));
3623 return assignment_->Save(file_name);
3631 CHECK(assignment_ !=
nullptr);
3632 if (assignment_->Load(file_name)) {
3633 return DoRestoreAssignment();
3640 CHECK(assignment_ !=
nullptr);
3641 assignment_->CopyIntersection(&solution);
3642 return DoRestoreAssignment();
3645 Assignment* RoutingModel::DoRestoreAssignment() {
3649 solver_->Solve(restore_assignment_, monitors_);
3650 if (collect_assignments_->solution_count() == 1) {
3652 return collect_assignments_->solution(0);
3661 const std::vector<std::vector<int64>>& routes,
bool ignore_inactive_indices,
3662 bool close_routes, Assignment*
const assignment)
const {
3663 CHECK(assignment !=
nullptr);
3665 LOG(ERROR) <<
"The model is not closed yet";
3668 const int num_routes = routes.size();
3669 if (num_routes > vehicles_) {
3670 LOG(ERROR) <<
"The number of vehicles in the assignment (" << routes.size()
3671 <<
") is greater than the number of vehicles in the model ("
3672 << vehicles_ <<
")";
3676 absl::flat_hash_set<int> visited_indices;
3678 for (
int vehicle = 0; vehicle < num_routes; ++vehicle) {
3679 const std::vector<int64>& route = routes[vehicle];
3680 int from_index =
Start(vehicle);
3681 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3682 visited_indices.insert(from_index);
3683 if (!insert_result.second) {
3684 LOG(ERROR) <<
"Index " << from_index <<
" (start node for vehicle "
3685 << vehicle <<
") was already used";
3689 for (
const int64 to_index : route) {
3690 if (to_index < 0 || to_index >=
Size()) {
3691 LOG(ERROR) <<
"Invalid index: " << to_index;
3695 IntVar*
const active_var =
ActiveVar(to_index);
3696 if (active_var->Max() == 0) {
3697 if (ignore_inactive_indices) {
3700 LOG(ERROR) <<
"Index " << to_index <<
" is not active";
3705 insert_result = visited_indices.insert(to_index);
3706 if (!insert_result.second) {
3707 LOG(ERROR) <<
"Index " << to_index <<
" is used multiple times";
3711 const IntVar*
const vehicle_var =
VehicleVar(to_index);
3712 if (!vehicle_var->Contains(vehicle)) {
3713 LOG(ERROR) <<
"Vehicle " << vehicle <<
" is not allowed at index "
3718 IntVar*
const from_var =
NextVar(from_index);
3719 if (!assignment->Contains(from_var)) {
3720 assignment->Add(from_var);
3722 assignment->SetValue(from_var, to_index);
3724 from_index = to_index;
3728 IntVar*
const last_var =
NextVar(from_index);
3729 if (!assignment->Contains(last_var)) {
3730 assignment->Add(last_var);
3732 assignment->SetValue(last_var,
End(vehicle));
3737 for (
int vehicle = num_routes; vehicle < vehicles_; ++vehicle) {
3738 const int start_index =
Start(vehicle);
3741 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3742 visited_indices.insert(start_index);
3743 if (!insert_result.second) {
3744 LOG(ERROR) <<
"Index " << start_index <<
" is used multiple times";
3748 IntVar*
const start_var =
NextVar(start_index);
3749 if (!assignment->Contains(start_var)) {
3750 assignment->Add(start_var);
3752 assignment->SetValue(start_var,
End(vehicle));
3761 if (!assignment->Contains(next_var)) {
3762 assignment->Add(next_var);
3764 assignment->SetValue(next_var,
index);
3773 const std::vector<std::vector<int64>>& routes,
3774 bool ignore_inactive_indices) {
3782 return DoRestoreAssignment();
3786 const Assignment& assignment,
3787 std::vector<std::vector<int64>>*
const routes)
const {
3789 CHECK(routes !=
nullptr);
3791 const int model_size =
Size();
3792 routes->resize(vehicles_);
3793 for (
int vehicle = 0; vehicle < vehicles_; ++vehicle) {
3794 std::vector<int64>*
const vehicle_route = &routes->at(vehicle);
3795 vehicle_route->clear();
3797 int num_visited_indices = 0;
3798 const int first_index =
Start(vehicle);
3799 const IntVar*
const first_var =
NextVar(first_index);
3800 CHECK(assignment.Contains(first_var));
3801 CHECK(assignment.Bound(first_var));
3802 int current_index = assignment.Value(first_var);
3803 while (!
IsEnd(current_index)) {
3804 vehicle_route->push_back(current_index);
3806 const IntVar*
const next_var =
NextVar(current_index);
3807 CHECK(assignment.Contains(next_var));
3808 CHECK(assignment.Bound(next_var));
3809 current_index = assignment.Value(next_var);
3811 ++num_visited_indices;
3812 CHECK_LE(num_visited_indices, model_size)
3813 <<
"The assignment contains a cycle";
3820 const Assignment& assignment) {
3821 std::vector<std::vector<int64>> route_indices(
vehicles());
3822 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3823 if (!assignment.Bound(
NextVar(vehicle))) {
3824 LOG(DFATAL) <<
"GetRoutesFromAssignment() called on incomplete solution:"
3825 <<
" NextVar(" << vehicle <<
") is unbound.";
3828 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3830 route_indices[vehicle].push_back(
index);
3833 route_indices[vehicle].push_back(
index);
3836 return route_indices;
3840 int64 RoutingModel::GetArcCostForClassInternal(
3841 int64 from_index,
int64 to_index, CostClassIndex cost_class_index)
const {
3843 DCHECK_GE(cost_class_index, 0);
3844 DCHECK_LT(cost_class_index, cost_classes_.size());
3845 CostCacheElement*
const cache = &cost_cache_[from_index];
3847 if (cache->index ==
static_cast<int>(to_index) &&
3848 cache->cost_class_index == cost_class_index) {
3852 const CostClass& cost_class = cost_classes_[cost_class_index];
3853 const auto& evaluator = transit_evaluators_[cost_class.evaluator_index];
3855 cost =
CapAdd(evaluator(from_index, to_index),
3856 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3857 }
else if (!
IsEnd(to_index)) {
3861 evaluator(from_index, to_index),
3862 CapAdd(GetDimensionTransitCostSum(from_index, to_index, cost_class),
3863 fixed_cost_of_vehicle_[index_to_vehicle_[from_index]]));
3867 if (consider_empty_route_costs_[index_to_vehicle_[from_index]]) {
3869 CapAdd(evaluator(from_index, to_index),
3870 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3875 *cache = {
static_cast<int>(to_index), cost_class_index,
cost};
3884 int vehicle)
const {
3885 CHECK_GE(vehicle, 0);
3886 CHECK_LT(vehicle, vehicles_);
3887 CHECK_EQ(solver_.get(), assignment.solver());
3889 CHECK(assignment.Contains(start_var));
3890 return !
IsEnd(assignment.Value(start_var));
3894 CHECK_EQ(solver_.get(), assignment.solver());
3896 CHECK(assignment.Contains(next_var));
3897 CHECK(assignment.Bound(next_var));
3898 return assignment.Value(next_var);
3902 int64 vehicle)
const {
3903 if (from_index != to_index && vehicle >= 0) {
3904 return GetArcCostForClassInternal(from_index, to_index,
3913 int64 cost_class_index)
const {
3914 if (from_index != to_index) {
3915 return GetArcCostForClassInternal(from_index, to_index,
3923 int64 to_index)
const {
3927 if (!is_bound_to_end_ct_added_.
Switched()) {
3930 std::vector<IntVar*> zero_transit(
Size(), solver_->MakeIntConst(0));
3931 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
3932 nexts_, active_, is_bound_to_end_, zero_transit));
3933 is_bound_to_end_ct_added_.
Switch(solver_.get());
3935 if (is_bound_to_end_[to_index]->Min() == 1)
return kint64max;
3940 int64 RoutingModel::GetDimensionTransitCostSum(
3941 int64 i,
int64 j,
const CostClass& cost_class)
const {
3943 for (
const auto& evaluator_and_coefficient :
3944 cost_class.dimension_transit_evaluator_class_and_cost_coefficient) {
3945 DCHECK_GT(evaluator_and_coefficient.cost_coefficient, 0);
3948 CapProd(evaluator_and_coefficient.cost_coefficient,
3949 evaluator_and_coefficient.dimension->GetTransitValueFromClass(
3950 i, j, evaluator_and_coefficient.transit_evaluator_class)));
3966 const bool mandatory1 = active_[to1]->Min() == 1;
3967 const bool mandatory2 = active_[to2]->Min() == 1;
3969 if (mandatory1 != mandatory2)
return mandatory1;
3972 IntVar*
const src_vehicle_var =
VehicleVar(from);
3976 const int64 src_vehicle = src_vehicle_var->Max();
3977 if (src_vehicle_var->Bound()) {
3978 IntVar*
const to1_vehicle_var =
VehicleVar(to1);
3979 IntVar*
const to2_vehicle_var =
VehicleVar(to2);
3984 mandatory1 ? to1_vehicle_var->Bound() : (to1_vehicle_var->Size() <= 2);
3986 mandatory2 ? to2_vehicle_var->Bound() : (to2_vehicle_var->Size() <= 2);
3989 if (bound1 != bound2)
return bound1;
3992 const int64 vehicle1 = to1_vehicle_var->Max();
3993 const int64 vehicle2 = to2_vehicle_var->Max();
3996 if ((vehicle1 == src_vehicle) != (vehicle2 == src_vehicle)) {
3997 return vehicle1 == src_vehicle;
4002 if (vehicle1 != src_vehicle)
return to1 < to2;
4013 const std::vector<IntVar*>& cumul_vars =
4015 IntVar*
const dim1 = cumul_vars[to1];
4016 IntVar*
const dim2 = cumul_vars[to2];
4019 if (dim1->Max() != dim2->Max())
return dim1->Max() < dim2->Max();
4028 const int64 cost_class_index =
4029 SafeGetCostClassInt64OfVehicle(src_vehicle);
4034 if (cost1 != cost2)
return cost1 < cost2;
4041 if (num_vehicles1 != num_vehicles2)
return num_vehicles1 < num_vehicles2;
4049 CHECK_LT(
index, index_to_visit_type_.size());
4050 DCHECK_EQ(index_to_visit_type_.size(), index_to_type_policy_.size());
4051 index_to_visit_type_[
index] = type;
4052 index_to_type_policy_[
index] = policy;
4053 num_visit_types_ =
std::max(num_visit_types_, type + 1);
4057 CHECK_LT(
index, index_to_visit_type_.size());
4058 return index_to_visit_type_[
index];
4062 DCHECK_LT(type, single_nodes_of_type_.size());
4063 return single_nodes_of_type_[type];
4067 DCHECK_LT(type, pair_indices_of_type_.size());
4068 return pair_indices_of_type_[type];
4073 CHECK_LT(
index, index_to_type_policy_.size());
4074 return index_to_type_policy_[
index];
4078 hard_incompatible_types_per_type_index_.resize(num_visit_types_);
4079 temporal_incompatible_types_per_type_index_.resize(num_visit_types_);
4080 same_vehicle_required_type_alternatives_per_type_index_.resize(
4082 required_type_alternatives_when_adding_type_index_.resize(num_visit_types_);
4083 required_type_alternatives_when_removing_type_index_.resize(num_visit_types_);
4088 hard_incompatible_types_per_type_index_.size());
4089 has_hard_type_incompatibilities_ =
true;
4091 hard_incompatible_types_per_type_index_[type1].insert(type2);
4092 hard_incompatible_types_per_type_index_[type2].insert(type1);
4097 temporal_incompatible_types_per_type_index_.size());
4098 has_temporal_type_incompatibilities_ =
true;
4100 temporal_incompatible_types_per_type_index_[type1].insert(type2);
4101 temporal_incompatible_types_per_type_index_[type2].insert(type1);
4104 const absl::flat_hash_set<int>&
4107 DCHECK_LT(type, hard_incompatible_types_per_type_index_.size());
4108 return hard_incompatible_types_per_type_index_[type];
4111 const absl::flat_hash_set<int>&
4114 DCHECK_LT(type, temporal_incompatible_types_per_type_index_.size());
4115 return temporal_incompatible_types_per_type_index_[type];
4121 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4122 DCHECK_LT(dependent_type,
4123 same_vehicle_required_type_alternatives_per_type_index_.size());
4125 if (required_type_alternatives.empty()) {
4129 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4130 trivially_infeasible_visit_types_to_policies_[dependent_type];
4137 has_same_vehicle_type_requirements_ =
true;
4138 same_vehicle_required_type_alternatives_per_type_index_[dependent_type]
4139 .push_back(std::move(required_type_alternatives));
4143 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4144 DCHECK_LT(dependent_type,
4145 required_type_alternatives_when_adding_type_index_.size());
4147 if (required_type_alternatives.empty()) {
4151 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4152 trivially_infeasible_visit_types_to_policies_[dependent_type];
4158 has_temporal_type_requirements_ =
true;
4159 required_type_alternatives_when_adding_type_index_[dependent_type].push_back(
4160 std::move(required_type_alternatives));
4164 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4165 DCHECK_LT(dependent_type,
4166 required_type_alternatives_when_removing_type_index_.size());
4168 if (required_type_alternatives.empty()) {
4172 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4173 trivially_infeasible_visit_types_to_policies_[dependent_type];
4180 has_temporal_type_requirements_ =
true;
4181 required_type_alternatives_when_removing_type_index_[dependent_type]
4182 .push_back(std::move(required_type_alternatives));
4185 const std::vector<absl::flat_hash_set<int>>&
4189 same_vehicle_required_type_alternatives_per_type_index_.size());
4190 return same_vehicle_required_type_alternatives_per_type_index_[type];
4193 const std::vector<absl::flat_hash_set<int>>&
4196 DCHECK_LT(type, required_type_alternatives_when_adding_type_index_.size());
4197 return required_type_alternatives_when_adding_type_index_[type];
4200 const std::vector<absl::flat_hash_set<int>>&
4203 DCHECK_LT(type, required_type_alternatives_when_removing_type_index_.size());
4204 return required_type_alternatives_when_removing_type_index_[type];
4212 int64 var_index)
const {
4213 if (active_[var_index]->Min() == 1)
return kint64max;
4214 const std::vector<DisjunctionIndex>& disjunction_indices =
4216 if (disjunction_indices.size() != 1)
return default_value;
4221 return std::max(
int64{0}, disjunctions_[disjunction_index].value.penalty);
4225 const Assignment& solution_assignment,
4226 const std::string& dimension_to_print)
const {
4227 for (
int i = 0; i <
Size(); ++i) {
4228 if (!solution_assignment.Bound(
NextVar(i))) {
4230 <<
"DebugOutputVehicleSchedules() called on incomplete solution:"
4231 <<
" NextVar(" << i <<
") is unbound.";
4236 absl::flat_hash_set<std::string> dimension_names;
4237 if (dimension_to_print.empty()) {
4239 dimension_names.insert(all_dimension_names.begin(),
4240 all_dimension_names.end());
4242 dimension_names.insert(dimension_to_print);
4244 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4245 int empty_vehicle_range_start = vehicle;
4250 if (empty_vehicle_range_start != vehicle) {
4251 if (empty_vehicle_range_start == vehicle - 1) {
4252 absl::StrAppendFormat(&output,
"Vehicle %d: empty",
4253 empty_vehicle_range_start);
4255 absl::StrAppendFormat(&output,
"Vehicles %d-%d: empty",
4256 empty_vehicle_range_start, vehicle - 1);
4258 output.append(
"\n");
4261 absl::StrAppendFormat(&output,
"Vehicle %d:", vehicle);
4265 absl::StrAppendFormat(&output,
"%d Vehicle(%d) ",
index,
4266 solution_assignment.Value(vehicle_var));
4269 const IntVar*
const var = dimension->CumulVar(
index);
4270 absl::StrAppendFormat(&output,
"%s(%d..%d) ", dimension->name(),
4271 solution_assignment.Min(
var),
4272 solution_assignment.Max(
var));
4277 if (
IsEnd(
index)) output.append(
"Route end ");
4279 output.append(
"\n");
4282 output.append(
"Unperformed nodes: ");
4283 bool has_unperformed =
false;
4284 for (
int i = 0; i <
Size(); ++i) {
4286 solution_assignment.Value(
NextVar(i)) == i) {
4287 absl::StrAppendFormat(&output,
"%d ", i);
4288 has_unperformed =
true;
4291 if (!has_unperformed) output.append(
"None");
4292 output.append(
"\n");
4298 const Assignment& solution_assignment,
const RoutingDimension& dimension) {
4299 std::vector<std::vector<std::pair<int64, int64>>> cumul_bounds(
vehicles());
4300 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4301 if (!solution_assignment.Bound(
NextVar(vehicle))) {
4302 LOG(DFATAL) <<
"GetCumulBounds() called on incomplete solution:"
4303 <<
" NextVar(" << vehicle <<
") is unbound.";
4307 for (
int vehicle_id = 0; vehicle_id <
vehicles(); ++vehicle_id) {
4310 cumul_bounds[vehicle_id].emplace_back(solution_assignment.Min(dim_var),
4311 solution_assignment.Max(dim_var));
4315 cumul_bounds[vehicle_id].emplace_back(solution_assignment.Min(dim_var),
4316 solution_assignment.Max(dim_var));
4319 return cumul_bounds;
4323 Assignment* RoutingModel::GetOrCreateAssignment() {
4324 if (assignment_ ==
nullptr) {
4325 assignment_ = solver_->MakeAssignment();
4326 assignment_->Add(nexts_);
4328 assignment_->Add(vehicle_vars_);
4330 assignment_->AddObjective(cost_);
4335 Assignment* RoutingModel::GetOrCreateTmpAssignment() {
4336 if (tmp_assignment_ ==
nullptr) {
4337 tmp_assignment_ = solver_->MakeAssignment();
4338 tmp_assignment_->Add(nexts_);
4340 return tmp_assignment_;
4343 RegularLimit* RoutingModel::GetOrCreateLimit() {
4344 if (limit_ ==
nullptr) {
4351 RegularLimit* RoutingModel::GetOrCreateLocalSearchLimit() {
4352 if (ls_limit_ ==
nullptr) {
4360 RegularLimit* RoutingModel::GetOrCreateLargeNeighborhoodSearchLimit() {
4361 if (lns_limit_ ==
nullptr) {
4370 RoutingModel::GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit() {
4371 if (first_solution_lns_limit_ ==
nullptr) {
4372 first_solution_lns_limit_ =
4376 return first_solution_lns_limit_;
4379 LocalSearchOperator* RoutingModel::CreateInsertionOperator() {
4380 std::vector<IntVar*> empty;
4381 LocalSearchOperator* insertion_operator =
4382 MakeLocalSearchOperator<MakeActiveOperator>(
4383 solver_.get(), nexts_,
4385 vehicle_start_class_callback_);
4386 if (!pickup_delivery_pairs_.empty()) {
4387 insertion_operator = solver_->ConcatenateOperators(
4389 solver_.get(), nexts_,
4391 vehicle_start_class_callback_, pickup_delivery_pairs_),
4392 insertion_operator});
4394 return insertion_operator;
4397 LocalSearchOperator* RoutingModel::CreateMakeInactiveOperator() {
4398 std::vector<IntVar*> empty;
4399 LocalSearchOperator* make_inactive_operator =
4400 MakeLocalSearchOperator<MakeInactiveOperator>(
4401 solver_.get(), nexts_,
4403 vehicle_start_class_callback_);
4404 if (!pickup_delivery_pairs_.empty()) {
4405 make_inactive_operator = solver_->ConcatenateOperators(
4407 solver_.get(), nexts_,
4409 vehicle_start_class_callback_, pickup_delivery_pairs_),
4410 make_inactive_operator});
4412 return make_inactive_operator;
4415 #define CP_ROUTING_ADD_OPERATOR(operator_type, cp_operator_type) \
4416 if (CostsAreHomogeneousAcrossVehicles()) { \
4417 local_search_operators_[operator_type] = \
4418 solver_->MakeOperator(nexts_, Solver::cp_operator_type); \
4420 local_search_operators_[operator_type] = solver_->MakeOperator( \
4421 nexts_, vehicle_vars_, Solver::cp_operator_type); \
4424 #define CP_ROUTING_ADD_OPERATOR2(operator_type, cp_operator_class) \
4425 local_search_operators_[operator_type] = \
4426 MakeLocalSearchOperator<cp_operator_class>( \
4427 solver_.get(), nexts_, \
4428 CostsAreHomogeneousAcrossVehicles() ? std::vector<IntVar*>() \
4430 vehicle_start_class_callback_);
4432 #define CP_ROUTING_ADD_CALLBACK_OPERATOR(operator_type, cp_operator_type) \
4433 if (CostsAreHomogeneousAcrossVehicles()) { \
4434 local_search_operators_[operator_type] = solver_->MakeOperator( \
4436 [this](int64 i, int64 j, int64 k) { \
4437 return GetArcCostForVehicle(i, j, k); \
4439 Solver::cp_operator_type); \
4441 local_search_operators_[operator_type] = solver_->MakeOperator( \
4442 nexts_, vehicle_vars_, \
4443 [this](int64 i, int64 j, int64 k) { \
4444 return GetArcCostForVehicle(i, j, k); \
4446 Solver::cp_operator_type); \
4449 void RoutingModel::CreateNeighborhoodOperators(
4451 local_search_operators_.clear();
4452 local_search_operators_.resize(LOCAL_SEARCH_OPERATOR_COUNTER,
nullptr);
4454 std::vector<IntVar*> empty;
4455 local_search_operators_[RELOCATE_PAIR] = MakePairRelocate(
4456 solver_.get(), nexts_,
4458 vehicle_start_class_callback_, pickup_delivery_pairs_);
4459 local_search_operators_[LIGHT_RELOCATE_PAIR] = MakeLightPairRelocate(
4460 solver_.get(), nexts_,
4462 vehicle_start_class_callback_, pickup_delivery_pairs_);
4463 local_search_operators_[EXCHANGE_PAIR] = MakePairExchange(
4464 solver_.get(), nexts_,
4466 vehicle_start_class_callback_, pickup_delivery_pairs_);
4467 local_search_operators_[EXCHANGE_RELOCATE_PAIR] = MakePairExchangeRelocate(
4468 solver_.get(), nexts_,
4470 vehicle_start_class_callback_, pickup_delivery_pairs_);
4471 local_search_operators_[RELOCATE_NEIGHBORS] = MakeRelocateNeighbors(
4472 solver_.get(), nexts_,
4474 vehicle_start_class_callback_,
4476 local_search_operators_[NODE_PAIR_SWAP] = solver_->ConcatenateOperators(
4477 {IndexPairSwapActive(
4478 solver_.get(), nexts_,
4480 vehicle_start_class_callback_, pickup_delivery_pairs_),
4482 solver_.get(), nexts_,
4484 pickup_delivery_pairs_),
4486 solver_.get(), nexts_,
4488 vehicle_start_class_callback_, pickup_delivery_pairs_)});
4489 const auto arc_cost_for_path_start =
4491 const int vehicle = index_to_vehicle_[start_index];
4492 const int64 arc_cost =
4494 return (before_node != start_index ||
IsEnd(after_node))
4498 GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
4499 ls_gci_parameters = {
4502 parameters.cheapest_insertion_ls_operator_neighbors_ratio(),
4504 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4505 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4506 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4512 GetOrCreateFeasibilityFilterManager(
parameters),
4514 parameters.heuristic_close_nodes_lns_num_nodes()));
4516 local_search_operators_[LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4517 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4518 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4523 GetOrCreateFeasibilityFilterManager(
parameters)),
4524 parameters.heuristic_close_nodes_lns_num_nodes()));
4526 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_PATH_LNS] =
4527 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4528 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4534 GetOrCreateFeasibilityFilterManager(
parameters),
4535 ls_gci_parameters)));
4537 local_search_operators_[LOCAL_CHEAPEST_INSERTION_PATH_LNS] =
4538 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4539 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4544 GetOrCreateFeasibilityFilterManager(
parameters))));
4545 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4546 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4547 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4553 GetOrCreateFeasibilityFilterManager(
parameters),
4555 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4556 arc_cost_for_path_start));
4558 local_search_operators_[LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4559 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4560 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4565 GetOrCreateFeasibilityFilterManager(
parameters)),
4566 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4567 arc_cost_for_path_start));
4568 local_search_operators_[RELOCATE_EXPENSIVE_CHAIN] =
4569 solver_->RevAlloc(
new RelocateExpensiveChain(
4571 vehicle_start_class_callback_,
4572 parameters.relocate_expensive_chain_num_arcs_to_consider(),
4573 arc_cost_for_path_start));
4574 local_search_operators_[RELOCATE_SUBTRIP] = MakeRelocateSubtrip(
4575 solver_.get(), nexts_,
4577 vehicle_start_class_callback_, pickup_delivery_pairs_);
4578 local_search_operators_[EXCHANGE_SUBTRIP] = MakeExchangeSubtrip(
4579 solver_.get(), nexts_,
4581 vehicle_start_class_callback_, pickup_delivery_pairs_);
4588 local_search_operators_[MAKE_ACTIVE] = CreateInsertionOperator();
4590 RelocateAndMakeActiveOperator);
4592 local_search_operators_[MAKE_INACTIVE] = CreateMakeInactiveOperator();
4603 #undef CP_ROUTING_ADD_CALLBACK_OPERATOR
4604 #undef CP_ROUTING_ADD_OPERATOR2
4605 #undef CP_ROUTING_ADD_OPERATOR
4607 #define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators) \
4608 if (search_parameters.local_search_operators().use_##operator_method() == \
4610 operators.push_back(local_search_operators_[operator_type]); \
4613 LocalSearchOperator* RoutingModel::GetNeighborhoodOperators(
4614 const RoutingSearchParameters& search_parameters)
const {
4615 std::vector<LocalSearchOperator*> operator_groups;
4616 std::vector<LocalSearchOperator*> operators = extra_operators_;
4617 if (!pickup_delivery_pairs_.empty()) {
4621 if (search_parameters.local_search_operators().use_relocate_pair() ==
4631 if (vehicles_ > 1) {
4642 if (!pickup_delivery_pairs_.empty() ||
4643 search_parameters.local_search_operators().use_relocate_neighbors() ==
4645 operators.push_back(local_search_operators_[RELOCATE_NEIGHBORS]);
4648 search_parameters.local_search_metaheuristic();
4649 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4650 local_search_metaheuristic !=
4651 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4652 local_search_metaheuristic !=
4653 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4660 if (!disjunctions_.
empty()) {
4677 operator_groups.push_back(solver_->ConcatenateOperators(operators));
4686 global_cheapest_insertion_path_lns, operators);
4688 local_cheapest_insertion_path_lns, operators);
4691 global_cheapest_insertion_expensive_chain_lns,
4694 local_cheapest_insertion_expensive_chain_lns,
4697 global_cheapest_insertion_close_nodes_lns,
4700 local_cheapest_insertion_close_nodes_lns, operators);
4701 operator_groups.push_back(solver_->ConcatenateOperators(operators));
4705 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4706 local_search_metaheuristic !=
4707 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4708 local_search_metaheuristic !=
4709 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4712 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4713 local_search_metaheuristic !=
4714 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4715 local_search_metaheuristic !=
4716 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4721 if (!disjunctions_.
empty()) {
4724 operator_groups.push_back(solver_->ConcatenateOperators(operators));
4726 return solver_->ConcatenateOperators(operator_groups);
4729 #undef CP_ROUTING_PUSH_OPERATOR
4731 std::vector<LocalSearchFilter*> RoutingModel::GetOrCreateLocalSearchFilters(
4742 std::vector<LocalSearchFilter*> filters;
4744 if (vehicle_amortized_cost_factors_set_) {
4751 filters.push_back(solver_->MakeSumObjectiveFilter(
4752 nexts_, [
this](
int64 i,
int64 j) { return GetHomogeneousCost(i, j); },
4755 filters.push_back(solver_->MakeSumObjectiveFilter(
4756 nexts_, vehicle_vars_,
4758 return GetArcCostForVehicle(i, j, k);
4763 filters.push_back(solver_->MakeVariableDomainFilter());
4765 if (vehicles_ > max_active_vehicles_) {
4769 if (!disjunctions_.
empty()) {
4773 if (!pickup_delivery_pairs_.empty()) {
4775 *
this, pickup_delivery_pairs_, vehicle_pickup_delivery_policy_));
4788 if (!dimension->HasBreakConstraints())
continue;
4791 filters.insert(filters.end(), extra_filters_.begin(), extra_filters_.end());
4795 LocalSearchFilterManager* RoutingModel::GetOrCreateLocalSearchFilterManager(
4797 if (!local_search_filter_manager_) {
4798 local_search_filter_manager_ = solver_->MakeLocalSearchFilterManager(
4801 return local_search_filter_manager_;
4804 std::vector<LocalSearchFilter*> RoutingModel::GetOrCreateFeasibilityFilters(
4806 std::vector<LocalSearchFilter*> filters;
4807 if (vehicles_ > max_active_vehicles_) {
4810 if (!disjunctions_.
empty()) {
4813 filters.push_back(solver_->MakeVariableDomainFilter());
4814 if (!pickup_delivery_pairs_.empty()) {
4816 *
this, pickup_delivery_pairs_, vehicle_pickup_delivery_policy_));
4827 if (dimension->HasBreakConstraints()) {
4828 IntVarLocalSearchFilter* breaks_filter =
4830 filters.push_back(breaks_filter);
4834 filters.insert(filters.end(), extra_filters_.begin(), extra_filters_.end());
4838 LocalSearchFilterManager* RoutingModel::GetOrCreateFeasibilityFilterManager(
4840 if (!feasibility_filter_manager_) {
4841 feasibility_filter_manager_ = solver_->MakeLocalSearchFilterManager(
4844 return feasibility_filter_manager_;
4847 LocalSearchFilterManager*
4848 RoutingModel::GetOrCreateStrongFeasibilityFilterManager(
4850 if (!strong_feasibility_filter_manager_) {
4851 std::vector<LocalSearchFilter*> filters =
4854 strong_feasibility_filter_manager_ =
4855 solver_->MakeLocalSearchFilterManager(std::move(filters));
4857 return strong_feasibility_filter_manager_;
4861 bool AllTransitsPositive(
const RoutingDimension& dimension) {
4862 for (
int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
4863 if (!dimension.AreVehicleTransitsPositive(vehicle)) {
4871 void RoutingModel::StoreDimensionCumulOptimizers(
4873 Assignment* packed_dimensions_collector_assignment =
4874 solver_->MakeAssignment();
4875 const int num_dimensions = dimensions_.size();
4876 local_optimizer_index_.
resize(num_dimensions, -1);
4877 global_optimizer_index_.
resize(num_dimensions, -1);
4880 if (dimension->global_span_cost_coefficient() > 0 ||
4881 !dimension->GetNodePrecedences().empty()) {
4883 global_optimizer_index_[dim] = global_dimension_optimizers_.size();
4884 global_dimension_optimizers_.push_back(
4885 absl::make_unique<GlobalDimensionCumulOptimizer>(dimension));
4886 packed_dimensions_collector_assignment->Add(dimension->cumuls());
4887 if (!AllTransitsPositive(*dimension)) {
4888 dimension->SetOffsetForGlobalOptimizer(0);
4892 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4893 DCHECK_GE(dimension->CumulVar(
Start(vehicle))->Min(), 0);
4895 std::min(offset, dimension->CumulVar(
Start(vehicle))->Min() - 1);
4897 dimension->SetOffsetForGlobalOptimizer(
std::max(Zero(), offset));
4899 bool has_span_cost =
false;
4900 bool has_span_limit =
false;
4901 std::vector<int64> vehicle_offsets(
vehicles());
4902 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4903 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4904 has_span_cost =
true;
4906 if (dimension->GetSpanUpperBoundForVehicle(vehicle) <
kint64max) {
4907 has_span_limit =
true;
4909 DCHECK_GE(dimension->CumulVar(
Start(vehicle))->Min(), 0);
4910 vehicle_offsets[vehicle] =
4911 dimension->AreVehicleTransitsPositive(vehicle)
4913 dimension->CumulVar(
Start(vehicle))->Min() - 1)
4916 bool has_soft_lower_bound =
false;
4917 bool has_soft_upper_bound =
false;
4918 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
4919 if (dimension->HasCumulVarSoftLowerBound(i)) {
4920 has_soft_lower_bound =
true;
4922 if (dimension->HasCumulVarSoftUpperBound(i)) {
4923 has_soft_upper_bound =
true;
4926 int num_linear_constraints = 0;
4927 if (has_span_cost) ++num_linear_constraints;
4928 if (has_span_limit) ++num_linear_constraints;
4929 if (dimension->HasSoftSpanUpperBounds()) ++num_linear_constraints;
4930 if (has_soft_lower_bound) ++num_linear_constraints;
4931 if (has_soft_upper_bound) ++num_linear_constraints;
4932 if (dimension->HasBreakConstraints()) ++num_linear_constraints;
4933 if (num_linear_constraints >= 2) {
4934 dimension->SetVehicleOffsetsForLocalOptimizer(
4935 std::move(vehicle_offsets));
4936 local_optimizer_index_[dim] = local_dimension_optimizers_.size();
4937 local_dimension_optimizers_.push_back(
4938 absl::make_unique<LocalDimensionCumulOptimizer>(
4939 dimension,
parameters.continuous_scheduling_solver()));
4940 bool has_intervals =
false;
4941 for (
const SortedDisjointIntervalList& intervals :
4942 dimension->forbidden_intervals()) {
4945 if (intervals.NumIntervals() > 0) {
4946 has_intervals =
true;
4950 if (dimension->HasBreakConstraints() || has_intervals) {
4951 local_dimension_mp_optimizers_.push_back(
4952 absl::make_unique<LocalDimensionCumulOptimizer>(
4953 dimension,
parameters.mixed_integer_scheduling_solver()));
4955 local_dimension_mp_optimizers_.push_back(
nullptr);
4957 packed_dimensions_collector_assignment->Add(dimension->cumuls());
4960 DCHECK_EQ(local_dimension_mp_optimizers_.size(),
4961 local_dimension_optimizers_.size());
4967 for (IntVar*
const extra_var : extra_vars_) {
4968 packed_dimensions_collector_assignment->Add(extra_var);
4970 for (IntervalVar*
const extra_interval : extra_intervals_) {
4971 packed_dimensions_collector_assignment->Add(extra_interval);
4974 packed_dimensions_assignment_collector_ = solver_->MakeFirstSolutionCollector(
4975 packed_dimensions_collector_assignment);
4980 std::vector<RoutingDimension*> dimensions;
4982 bool has_soft_or_span_cost =
false;
4983 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4984 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4985 has_soft_or_span_cost =
true;
4989 if (!has_soft_or_span_cost) {
4990 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
4991 if (dimension->HasCumulVarSoftUpperBound(i) ||
4992 dimension->HasCumulVarSoftLowerBound(i)) {
4993 has_soft_or_span_cost =
true;
4998 if (has_soft_or_span_cost) dimensions.push_back(dimension);
5004 RoutingModel::CreateFinalizerForMinimizedAndMaximizedVariables() {
5005 std::stable_sort(finalizer_variable_cost_pairs_.begin(),
5006 finalizer_variable_cost_pairs_.end(),
5007 [](
const std::pair<IntVar*, int64>& var_cost1,
5008 const std::pair<IntVar*, int64>& var_cost2) {
5009 return var_cost1.second > var_cost2.second;
5011 const int num_variables = finalizer_variable_cost_pairs_.size() +
5012 finalizer_variable_target_pairs_.size();
5013 std::vector<IntVar*> variables;
5014 std::vector<int64> targets;
5015 variables.reserve(num_variables);
5016 targets.reserve(num_variables);
5017 for (
const auto& variable_cost : finalizer_variable_cost_pairs_) {
5018 variables.push_back(variable_cost.first);
5021 for (
const auto& variable_target : finalizer_variable_target_pairs_) {
5022 variables.push_back(variable_target.first);
5023 targets.push_back(variable_target.second);
5026 std::move(targets));
5029 DecisionBuilder* RoutingModel::CreateSolutionFinalizer(SearchLimit* lns_limit) {
5030 std::vector<DecisionBuilder*> decision_builders;
5031 decision_builders.push_back(solver_->MakePhase(
5032 nexts_, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE));
5034 if (!local_dimension_optimizers_.empty()) {
5035 decision_builders.push_back(
5036 solver_->RevAlloc(
new SetCumulsFromLocalDimensionCosts(
5037 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
5040 if (!global_dimension_optimizers_.empty()) {
5041 decision_builders.push_back(
5042 solver_->RevAlloc(
new SetCumulsFromGlobalDimensionCosts(
5043 &global_dimension_optimizers_, lns_limit)));
5045 decision_builders.push_back(
5046 CreateFinalizerForMinimizedAndMaximizedVariables());
5048 return solver_->Compose(decision_builders);
5051 void RoutingModel::CreateFirstSolutionDecisionBuilders(
5052 const RoutingSearchParameters& search_parameters) {
5053 first_solution_decision_builders_.resize(
5055 first_solution_filtered_decision_builders_.resize(
5057 DecisionBuilder*
const finalize_solution =
5058 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit());
5060 first_solution_decision_builders_
5061 [FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE] = finalize_solution;
5063 first_solution_decision_builders_
5064 [FirstSolutionStrategy::GLOBAL_CHEAPEST_ARC] = solver_->MakePhase(
5067 Solver::CHOOSE_STATIC_GLOBAL_BEST);
5069 Solver::IndexEvaluator2 eval = [
this](
int64 i,
int64 j) {
5072 first_solution_decision_builders_[FirstSolutionStrategy::LOCAL_CHEAPEST_ARC] =
5073 solver_->MakePhase(nexts_, Solver::CHOOSE_FIRST_UNBOUND, eval);
5075 first_solution_decision_builders_[FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5076 solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, eval);
5077 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5078 first_solution_filtered_decision_builders_
5079 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5080 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5081 absl::make_unique<EvaluatorCheapestAdditionFilteredHeuristic>(
5086 GetOrCreateFeasibilityFilterManager(search_parameters))));
5087 first_solution_decision_builders_
5088 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5089 solver_->Try(first_solution_filtered_decision_builders_
5090 [FirstSolutionStrategy::PATH_CHEAPEST_ARC],
5091 first_solution_decision_builders_
5092 [FirstSolutionStrategy::PATH_CHEAPEST_ARC]);
5095 Solver::VariableValueComparator comp = [
this](
int64 i,
int64 j,
int64 k) {
5099 first_solution_decision_builders_
5100 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5101 solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, comp);
5102 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5103 first_solution_filtered_decision_builders_
5104 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5105 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5106 absl::make_unique<ComparatorCheapestAdditionFilteredHeuristic>(
5108 GetOrCreateFeasibilityFilterManager(search_parameters))));
5109 first_solution_decision_builders_
5110 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->Try(
5111 first_solution_filtered_decision_builders_
5112 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC],
5113 first_solution_decision_builders_
5114 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC]);
5117 if (first_solution_evaluator_ !=
nullptr) {
5118 first_solution_decision_builders_
5119 [FirstSolutionStrategy::EVALUATOR_STRATEGY] = solver_->MakePhase(
5120 nexts_, Solver::CHOOSE_PATH, first_solution_evaluator_);
5122 first_solution_decision_builders_
5123 [FirstSolutionStrategy::EVALUATOR_STRATEGY] =
nullptr;
5126 first_solution_decision_builders_[FirstSolutionStrategy::ALL_UNPERFORMED] =
5127 solver_->RevAlloc(
new AllUnperformed(
this));
5129 RegularLimit*
const ls_limit =
5132 DecisionBuilder*
const finalize = solver_->MakeSolveOnce(
5133 finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit());
5134 LocalSearchPhaseParameters*
const insertion_parameters =
5135 solver_->MakeLocalSearchPhaseParameters(
5136 nullptr, CreateInsertionOperator(), finalize, ls_limit,
5137 GetOrCreateLocalSearchFilterManager(search_parameters));
5138 std::vector<IntVar*> decision_vars = nexts_;
5140 decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(),
5141 vehicle_vars_.end());
5145 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5146 solver_->MakeNestedOptimize(
5147 solver_->MakeLocalSearchPhase(
5148 decision_vars, solver_->RevAlloc(
new AllUnperformed(
this)),
5149 insertion_parameters),
5150 GetOrCreateAssignment(),
false, optimization_step);
5151 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5152 solver_->Compose(first_solution_decision_builders_
5153 [FirstSolutionStrategy::BEST_INSERTION],
5157 GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
5160 search_parameters.cheapest_insertion_farthest_seeds_ratio(),
5161 search_parameters.cheapest_insertion_first_solution_neighbors_ratio(),
5163 for (
bool is_sequential : {
false,
true}) {
5165 is_sequential ? FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION
5166 : FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5167 gci_parameters.is_sequential = is_sequential;
5169 first_solution_filtered_decision_builders_[first_solution_strategy] =
5170 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5171 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5177 GetOrCreateFeasibilityFilterManager(search_parameters),
5179 IntVarFilteredDecisionBuilder*
const strong_gci =
5180 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5181 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5187 GetOrCreateStrongFeasibilityFilterManager(search_parameters),
5189 first_solution_decision_builders_[first_solution_strategy] = solver_->Try(
5190 first_solution_filtered_decision_builders_[first_solution_strategy],
5191 solver_->Try(strong_gci, first_solution_decision_builders_
5192 [FirstSolutionStrategy::BEST_INSERTION]));
5196 first_solution_filtered_decision_builders_
5197 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] =
5198 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5199 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5204 GetOrCreateFeasibilityFilterManager(search_parameters))));
5205 IntVarFilteredDecisionBuilder*
const strong_lci =
5206 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5207 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5212 GetOrCreateStrongFeasibilityFilterManager(search_parameters))));
5213 first_solution_decision_builders_
5214 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = solver_->Try(
5215 first_solution_filtered_decision_builders_
5216 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION],
5217 solver_->Try(strong_lci,
5218 first_solution_decision_builders_
5219 [FirstSolutionStrategy::BEST_INSERTION]));
5221 SavingsFilteredHeuristic::SavingsParameters savings_parameters;
5222 savings_parameters.neighbors_ratio =
5223 search_parameters.savings_neighbors_ratio();
5224 savings_parameters.max_memory_usage_bytes =
5225 search_parameters.savings_max_memory_usage_bytes();
5226 savings_parameters.add_reverse_arcs =
5227 search_parameters.savings_add_reverse_arcs();
5228 savings_parameters.arc_coefficient =
5229 search_parameters.savings_arc_coefficient();
5230 LocalSearchFilterManager* filter_manager =
nullptr;
5231 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5232 filter_manager = GetOrCreateFeasibilityFilterManager(search_parameters);
5235 if (search_parameters.savings_parallel_routes()) {
5236 IntVarFilteredDecisionBuilder* savings_db =
5237 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5238 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5239 this, &manager_, savings_parameters, filter_manager)));
5240 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5241 first_solution_filtered_decision_builders_
5242 [FirstSolutionStrategy::SAVINGS] = savings_db;
5245 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5246 solver_->Try(savings_db,
5247 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5248 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5249 this, &manager_, savings_parameters,
5250 GetOrCreateStrongFeasibilityFilterManager(
5251 search_parameters)))));
5253 IntVarFilteredDecisionBuilder* savings_db =
5254 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5255 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5256 this, &manager_, savings_parameters, filter_manager)));
5257 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5258 first_solution_filtered_decision_builders_
5259 [FirstSolutionStrategy::SAVINGS] = savings_db;
5262 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5263 solver_->Try(savings_db,
5264 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5265 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5266 this, &manager_, savings_parameters,
5267 GetOrCreateStrongFeasibilityFilterManager(
5268 search_parameters)))));
5271 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5272 solver_->RevAlloc(
new SweepBuilder(
this,
true));
5273 DecisionBuilder* sweep_builder =
5274 solver_->RevAlloc(
new SweepBuilder(
this,
false));
5275 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5278 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP]);
5280 first_solution_decision_builders_[FirstSolutionStrategy::CHRISTOFIDES] =
5281 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5282 absl::make_unique<ChristofidesFilteredHeuristic>(
5283 this, GetOrCreateFeasibilityFilterManager(search_parameters),
5284 search_parameters.christofides_use_minimum_matching())));
5287 const bool has_precedences = std::any_of(
5288 dimensions_.begin(), dimensions_.end(),
5290 if (pickup_delivery_pairs_.empty() && !has_precedences) {
5291 automatic_first_solution_strategy_ =
5292 FirstSolutionStrategy::PATH_CHEAPEST_ARC;
5294 automatic_first_solution_strategy_ =
5295 FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5297 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC] =
5298 first_solution_decision_builders_[automatic_first_solution_strategy_];
5299 first_solution_decision_builders_[FirstSolutionStrategy::UNSET] =
5300 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC];
5303 DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder(
5304 const RoutingSearchParameters& search_parameters)
const {
5306 search_parameters.first_solution_strategy();
5307 if (first_solution_strategy < first_solution_decision_builders_.size()) {
5308 return first_solution_decision_builders_[first_solution_strategy];
5314 IntVarFilteredDecisionBuilder*
5315 RoutingModel::GetFilteredFirstSolutionDecisionBuilderOrNull(
5316 const RoutingSearchParameters& search_parameters)
const {
5318 search_parameters.first_solution_strategy();
5319 return first_solution_filtered_decision_builders_[first_solution_strategy];
5322 LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters(
5323 const RoutingSearchParameters& search_parameters) {
5324 SearchLimit* lns_limit = GetOrCreateLargeNeighborhoodSearchLimit();
5325 return solver_->MakeLocalSearchPhaseParameters(
5326 CostVar(), GetNeighborhoodOperators(search_parameters),
5327 solver_->MakeSolveOnce(CreateSolutionFinalizer(lns_limit), lns_limit),
5328 GetOrCreateLocalSearchLimit(),
5329 GetOrCreateLocalSearchFilterManager(search_parameters));
5332 DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder(
5333 const RoutingSearchParameters& search_parameters) {
5334 const int size =
Size();
5335 DecisionBuilder* first_solution =
5336 GetFirstSolutionDecisionBuilder(search_parameters);
5337 LocalSearchPhaseParameters*
const parameters =
5338 CreateLocalSearchParameters(search_parameters);
5339 SearchLimit* first_solution_lns_limit =
5340 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5341 DecisionBuilder*
const first_solution_sub_decision_builder =
5342 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_solution_lns_limit),
5343 first_solution_lns_limit);
5345 return solver_->MakeLocalSearchPhase(nexts_, first_solution,
5346 first_solution_sub_decision_builder,
5349 const int all_size = size + size + vehicles_;
5350 std::vector<IntVar*> all_vars(all_size);
5351 for (
int i = 0; i < size; ++i) {
5352 all_vars[i] = nexts_[i];
5354 for (
int i = size; i < all_size; ++i) {
5355 all_vars[i] = vehicle_vars_[i - size];
5357 return solver_->MakeLocalSearchPhase(all_vars, first_solution,
5358 first_solution_sub_decision_builder,
5363 void RoutingModel::SetupDecisionBuilders(
5364 const RoutingSearchParameters& search_parameters) {
5365 if (search_parameters.use_depth_first_search()) {
5366 SearchLimit* first_lns_limit =
5367 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5368 solve_db_ = solver_->Compose(
5369 GetFirstSolutionDecisionBuilder(search_parameters),
5370 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_lns_limit),
5373 solve_db_ = CreateLocalSearchDecisionBuilder(search_parameters);
5375 CHECK(preassignment_ !=
nullptr);
5376 DecisionBuilder* restore_preassignment =
5377 solver_->MakeRestoreAssignment(preassignment_);
5378 solve_db_ = solver_->Compose(restore_preassignment, solve_db_);
5380 solver_->Compose(restore_preassignment,
5381 solver_->MakeLocalSearchPhase(
5382 GetOrCreateAssignment(),
5383 CreateLocalSearchParameters(search_parameters)));
5384 restore_assignment_ = solver_->Compose(
5385 solver_->MakeRestoreAssignment(GetOrCreateAssignment()),
5386 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5387 restore_tmp_assignment_ = solver_->Compose(
5388 restore_preassignment,
5389 solver_->MakeRestoreAssignment(GetOrCreateTmpAssignment()),
5390 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5393 void RoutingModel::SetupMetaheuristics(
5394 const RoutingSearchParameters& search_parameters) {
5395 SearchMonitor* optimize;
5397 search_parameters.local_search_metaheuristic();
5400 bool limit_too_long = !search_parameters.has_time_limit() &&
5401 search_parameters.solution_limit() ==
kint64max;
5404 switch (metaheuristic) {
5405 case LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH:
5407 optimize = solver_->MakeGuidedLocalSearch(
5410 optimization_step, nexts_,
5411 search_parameters.guided_local_search_lambda_coefficient());
5413 optimize = solver_->MakeGuidedLocalSearch(
5418 optimization_step, nexts_, vehicle_vars_,
5419 search_parameters.guided_local_search_lambda_coefficient());
5422 case LocalSearchMetaheuristic::SIMULATED_ANNEALING:
5424 solver_->MakeSimulatedAnnealing(
false, cost_, optimization_step, 100);
5426 case LocalSearchMetaheuristic::TABU_SEARCH:
5427 optimize = solver_->MakeTabuSearch(
false, cost_, optimization_step,
5428 nexts_, 10, 10, .8);
5430 case LocalSearchMetaheuristic::GENERIC_TABU_SEARCH: {
5431 std::vector<operations_research::IntVar*> tabu_vars;
5432 if (tabu_var_callback_) {
5433 tabu_vars = tabu_var_callback_(
this);
5435 tabu_vars.push_back(cost_);
5437 optimize = solver_->MakeGenericTabuSearch(
false, cost_, optimization_step,
5442 limit_too_long =
false;
5443 optimize = solver_->MakeMinimize(cost_, optimization_step);
5445 if (limit_too_long) {
5446 LOG(WARNING) << LocalSearchMetaheuristic::Value_Name(metaheuristic)
5447 <<
" specified without sane timeout: solve may run forever.";
5449 monitors_.push_back(optimize);
5453 tabu_var_callback_ = std::move(tabu_var_callback);
5456 void RoutingModel::SetupAssignmentCollector(
5457 const RoutingSearchParameters& search_parameters) {
5458 Assignment* full_assignment = solver_->MakeAssignment();
5460 full_assignment->Add(dimension->cumuls());
5462 for (IntVar*
const extra_var : extra_vars_) {
5463 full_assignment->Add(extra_var);
5465 for (IntervalVar*
const extra_interval : extra_intervals_) {
5466 full_assignment->Add(extra_interval);
5468 full_assignment->Add(nexts_);
5469 full_assignment->Add(active_);
5470 full_assignment->Add(vehicle_vars_);
5471 full_assignment->AddObjective(cost_);
5473 collect_assignments_ = solver_->MakeNBestValueSolutionCollector(
5474 full_assignment, search_parameters.number_of_solutions_to_collect(),
5476 collect_one_assignment_ =
5477 solver_->MakeFirstSolutionCollector(full_assignment);
5478 monitors_.push_back(collect_assignments_);
5481 void RoutingModel::SetupTrace(
5482 const RoutingSearchParameters& search_parameters) {
5483 if (search_parameters.log_search()) {
5484 Solver::SearchLogParameters search_log_parameters;
5485 search_log_parameters.branch_period = 10000;
5486 search_log_parameters.objective =
nullptr;
5487 search_log_parameters.variable = cost_;
5488 search_log_parameters.scaling_factor =
5489 search_parameters.log_cost_scaling_factor();
5490 search_log_parameters.offset = search_parameters.log_cost_offset();
5491 if (!search_parameters.log_tag().empty()) {
5492 const std::string tag = search_parameters.log_tag();
5493 search_log_parameters.display_callback = [tag]() {
return tag; };
5495 search_log_parameters.display_callback =
nullptr;
5497 monitors_.push_back(solver_->MakeSearchLog(search_log_parameters));
5501 void RoutingModel::SetupSearchMonitors(
5502 const RoutingSearchParameters& search_parameters) {
5503 monitors_.push_back(GetOrCreateLimit());
5504 SetupMetaheuristics(search_parameters);
5505 SetupAssignmentCollector(search_parameters);
5506 SetupTrace(search_parameters);
5509 bool RoutingModel::UsesLightPropagation(
5510 const RoutingSearchParameters& search_parameters)
const {
5511 return !search_parameters.use_full_propagation() &&
5512 !search_parameters.use_depth_first_search() &&
5513 search_parameters.first_solution_strategy() !=
5514 FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE;
5519 CHECK(
var !=
nullptr);
5521 finalizer_variable_cost_pairs_.size());
5522 if (
index < finalizer_variable_cost_pairs_.size()) {
5523 const int64 old_cost = finalizer_variable_cost_pairs_[
index].second;
5524 finalizer_variable_cost_pairs_[
index].second =
CapAdd(old_cost,
cost);
5526 finalizer_variable_cost_pairs_.emplace_back(
var,
cost);
5531 CHECK(
var !=
nullptr);
5532 if (finalizer_variable_target_set_.contains(
var))
return;
5533 finalizer_variable_target_set_.insert(
var);
5534 finalizer_variable_target_pairs_.emplace_back(
var, target);
5545 void RoutingModel::SetupSearch(
5546 const RoutingSearchParameters& search_parameters) {
5547 SetupDecisionBuilders(search_parameters);
5548 SetupSearchMonitors(search_parameters);
5552 extra_vars_.push_back(
var);
5556 extra_intervals_.push_back(
interval);
5561 class PathSpansAndTotalSlacks :
public Constraint {
5565 std::vector<IntVar*> spans,
5566 std::vector<IntVar*> total_slacks)
5567 : Constraint(
model->solver()),
5569 dimension_(dimension),
5570 spans_(std::move(spans)),
5571 total_slacks_(std::move(total_slacks)) {
5572 CHECK_EQ(spans_.size(), model_->vehicles());
5573 CHECK_EQ(total_slacks_.size(), model_->vehicles());
5574 vehicle_demons_.resize(model_->vehicles());
5577 std::string DebugString()
const override {
return "PathSpansAndTotalSlacks"; }
5579 void Post()
override {
5580 const int num_nodes = model_->VehicleVars().size();
5581 const int num_transits = model_->Nexts().size();
5582 for (
int node = 0; node < num_nodes; ++node) {
5584 model_->solver(),
this, &PathSpansAndTotalSlacks::PropagateNode,
5585 "PathSpansAndTotalSlacks::PropagateNode", node);
5586 dimension_->CumulVar(node)->WhenRange(demon);
5587 model_->VehicleVar(node)->WhenBound(demon);
5588 if (node < num_transits) {
5589 dimension_->TransitVar(node)->WhenRange(demon);
5590 dimension_->FixedTransitVar(node)->WhenBound(demon);
5591 model_->NextVar(node)->WhenBound(demon);
5594 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5595 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5597 solver(),
this, &PathSpansAndTotalSlacks::PropagateVehicle,
5598 "PathSpansAndTotalSlacks::PropagateVehicle", vehicle);
5599 vehicle_demons_[vehicle] = demon;
5600 if (spans_[vehicle]) spans_[vehicle]->WhenRange(demon);
5601 if (total_slacks_[vehicle]) total_slacks_[vehicle]->WhenRange(demon);
5602 if (dimension_->HasBreakConstraints()) {
5603 for (IntervalVar*
b : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5604 b->WhenAnything(demon);
5611 void InitialPropagate()
override {
5612 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5613 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5614 PropagateVehicle(vehicle);
5622 void PropagateNode(
int node) {
5623 if (!model_->VehicleVar(node)->Bound())
return;
5624 const int vehicle = model_->VehicleVar(node)->Min();
5625 if (vehicle < 0 || vehicle_demons_[vehicle] ==
nullptr)
return;
5626 EnqueueDelayedDemon(vehicle_demons_[vehicle]);
5634 int64 SpanMin(
int vehicle,
int64 sum_fixed_transits) {
5635 DCHECK_GE(sum_fixed_transits, 0);
5636 const int64 span_min = spans_[vehicle] ? spans_[vehicle]->Min() :
kint64max;
5637 const int64 total_slack_min =
5638 total_slacks_[vehicle] ? total_slacks_[vehicle]->Min() :
kint64max;
5639 return std::min(span_min,
CapAdd(total_slack_min, sum_fixed_transits));
5641 int64 SpanMax(
int vehicle,
int64 sum_fixed_transits) {
5642 DCHECK_GE(sum_fixed_transits, 0);
5643 const int64 span_max = spans_[vehicle] ? spans_[vehicle]->Max() :
kint64min;
5644 const int64 total_slack_max =
5645 total_slacks_[vehicle] ? total_slacks_[vehicle]->Max() :
kint64min;
5646 return std::max(span_max,
CapAdd(total_slack_max, sum_fixed_transits));
5648 void SetSpanMin(
int vehicle,
int64 min,
int64 sum_fixed_transits) {
5649 DCHECK_GE(sum_fixed_transits, 0);
5650 if (spans_[vehicle]) {
5651 spans_[vehicle]->SetMin(
min);
5653 if (total_slacks_[vehicle]) {
5654 total_slacks_[vehicle]->SetMin(
CapSub(
min, sum_fixed_transits));
5657 void SetSpanMax(
int vehicle,
int64 max,
int64 sum_fixed_transits) {
5658 DCHECK_GE(sum_fixed_transits, 0);
5659 if (spans_[vehicle]) {
5660 spans_[vehicle]->SetMax(
max);
5662 if (total_slacks_[vehicle]) {
5663 total_slacks_[vehicle]->SetMax(
CapSub(
max, sum_fixed_transits));
5668 void SynchronizeSpanAndTotalSlack(
int vehicle,
int64 sum_fixed_transits) {
5669 DCHECK_GE(sum_fixed_transits, 0);
5670 IntVar* span = spans_[vehicle];
5671 IntVar* total_slack = total_slacks_[vehicle];
5672 if (!span || !total_slack)
return;
5673 span->SetMin(
CapAdd(total_slack->Min(), sum_fixed_transits));
5674 span->SetMax(
CapAdd(total_slack->Max(), sum_fixed_transits));
5675 total_slack->SetMin(
CapSub(span->Min(), sum_fixed_transits));
5676 total_slack->SetMax(
CapSub(span->Max(), sum_fixed_transits));
5679 void PropagateVehicle(
int vehicle) {
5680 DCHECK(spans_[vehicle] || total_slacks_[vehicle]);
5681 const int start = model_->Start(vehicle);
5682 const int end = model_->End(vehicle);
5688 int curr_node = start;
5689 while (!model_->IsEnd(curr_node)) {
5690 const IntVar* next_var = model_->NextVar(curr_node);
5691 if (!next_var->Bound())
return;
5692 path_.push_back(curr_node);
5693 curr_node = next_var->Value();
5698 int64 sum_fixed_transits = 0;
5699 for (
const int node : path_) {
5700 const IntVar* fixed_transit_var = dimension_->FixedTransitVar(node);
5701 if (!fixed_transit_var->Bound())
return;
5702 sum_fixed_transits =
5703 CapAdd(sum_fixed_transits, fixed_transit_var->Value());
5706 SynchronizeSpanAndTotalSlack(vehicle, sum_fixed_transits);
5713 if (dimension_->HasBreakConstraints() &&
5714 !dimension_->GetBreakIntervalsOfVehicle(vehicle).empty()) {
5715 const int64 vehicle_start_max = dimension_->CumulVar(start)->Max();
5716 const int64 vehicle_end_min = dimension_->CumulVar(end)->Min();
5718 int64 min_break_duration = 0;
5719 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5720 if (!br->MustBePerformed())
continue;
5721 if (vehicle_start_max < br->EndMin() &&
5722 br->StartMax() < vehicle_end_min) {
5723 min_break_duration =
CapAdd(min_break_duration, br->DurationMin());
5726 SetSpanMin(vehicle,
CapAdd(min_break_duration, sum_fixed_transits),
5727 sum_fixed_transits);
5733 const int64 slack_max =
5734 CapSub(SpanMax(vehicle, sum_fixed_transits), sum_fixed_transits);
5735 const int64 max_additional_slack =
CapSub(slack_max, min_break_duration);
5736 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5737 if (!br->MustBePerformed())
continue;
5739 if (vehicle_start_max >= br->EndMin() &&
5740 br->StartMax() < vehicle_end_min) {
5741 if (br->DurationMin() > max_additional_slack) {
5744 br->SetEndMax(vehicle_start_max);
5745 dimension_->CumulVar(start)->SetMin(br->EndMin());
5750 if (vehicle_start_max < br->EndMin() &&
5751 br->StartMax() >= vehicle_end_min) {
5752 if (br->DurationMin() > max_additional_slack) {
5753 br->SetStartMin(vehicle_end_min);
5754 dimension_->CumulVar(end)->SetMax(br->StartMax());
5762 IntVar* start_cumul = dimension_->CumulVar(start);
5763 IntVar* end_cumul = dimension_->CumulVar(end);
5770 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5772 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5774 const int64 span_min = SpanMin(vehicle, sum_fixed_transits);
5775 const int64 span_max = SpanMax(vehicle, sum_fixed_transits);
5776 const int64 slack_from_lb =
CapSub(span_max, span_lb);
5777 const int64 slack_from_ub =
CapSub(span_ub, span_min);
5793 for (
const int node : path_) {
5794 span_lb =
CapAdd(span_lb, dimension_->TransitVar(node)->Min());
5795 span_ub =
CapAdd(span_ub, dimension_->TransitVar(node)->Max());
5797 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5798 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5802 const int64 span_min = SpanMin(vehicle, sum_fixed_transits);
5803 const int64 span_max = SpanMax(vehicle, sum_fixed_transits);
5804 const int64 slack_from_lb =
CapSub(span_max, span_lb);
5805 const int64 slack_from_ub =
5807 for (
const int node : path_) {
5808 IntVar* transit_var = dimension_->TransitVar(node);
5809 const int64 transit_i_min = transit_var->Min();
5810 const int64 transit_i_max = transit_var->Max();
5814 transit_var->SetMax(
CapAdd(transit_i_min, slack_from_lb));
5815 transit_var->SetMin(
CapSub(transit_i_max, slack_from_ub));
5820 path_.push_back(end);
5831 int64 arrival_time = dimension_->CumulVar(start)->Min();
5832 for (
int i = 1; i < path_.size(); ++i) {
5835 dimension_->FixedTransitVar(path_[i - 1])->Min()),
5836 dimension_->CumulVar(path_[i])->Min());
5838 int64 departure_time = arrival_time;
5839 for (
int i = path_.size() - 2; i >= 0; --i) {
5842 dimension_->FixedTransitVar(path_[i])->Min()),
5843 dimension_->CumulVar(path_[i])->Max());
5845 const int64 span_lb =
CapSub(arrival_time, departure_time);
5846 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5847 const int64 maximum_deviation =
5848 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5849 const int64 start_lb =
CapSub(departure_time, maximum_deviation);
5850 dimension_->CumulVar(start)->SetMin(start_lb);
5854 int64 departure_time = dimension_->CumulVar(end)->Max();
5855 for (
int i = path_.size() - 2; i >= 0; --i) {
5856 const int curr_node = path_[i];
5859 dimension_->FixedTransitVar(curr_node)->Min()),
5860 dimension_->CumulVar(curr_node)->Max());
5862 int arrival_time = departure_time;
5863 for (
int i = 1; i < path_.size(); ++i) {
5866 dimension_->FixedTransitVar(path_[i - 1])->Min()),
5867 dimension_->CumulVar(path_[i])->Min());
5869 const int64 span_lb =
CapSub(arrival_time, departure_time);
5870 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5871 const int64 maximum_deviation =
5872 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5873 dimension_->CumulVar(end)->SetMax(
5874 CapAdd(arrival_time, maximum_deviation));
5878 const RoutingModel*
const model_;
5879 const RoutingDimension*
const dimension_;
5880 std::vector<IntVar*> spans_;
5881 std::vector<IntVar*> total_slacks_;
5882 std::vector<int> path_;
5883 std::vector<Demon*> vehicle_demons_;
5890 std::vector<IntVar*> total_slacks) {
5891 CHECK_EQ(vehicles_, spans.size());
5892 CHECK_EQ(vehicles_, total_slacks.size());
5893 return solver()->RevAlloc(
5894 new PathSpansAndTotalSlacks(
this, dimension, spans, total_slacks));
5902 std::vector<int64> vehicle_capacities,
5903 const std::string&
name,
5905 : vehicle_capacities_(std::move(vehicle_capacities)),
5906 base_dimension_(base_dimension),
5907 global_span_cost_coefficient_(0),
5910 global_optimizer_offset_(0) {
5911 CHECK(
model !=
nullptr);
5913 vehicle_span_cost_coefficients_.assign(
model->vehicles(), 0);
5916 RoutingDimension::RoutingDimension(RoutingModel*
model,
5917 std::vector<int64> vehicle_capacities,
5918 const std::string&
name, SelfBased)
5919 : RoutingDimension(
model, std::move(vehicle_capacities),
name, this) {}
5922 cumul_var_piecewise_linear_cost_.clear();
5925 void RoutingDimension::Initialize(
5926 const std::vector<int>& transit_evaluators,
5927 const std::vector<int>& state_dependent_transit_evaluators,
5930 InitializeTransits(transit_evaluators, state_dependent_transit_evaluators,
5944 class LightRangeLessOrEqual :
public Constraint {
5946 LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l, IntExpr*
const r);
5947 ~LightRangeLessOrEqual()
override {}
5948 void Post()
override;
5949 void InitialPropagate()
override;
5950 std::string DebugString()
const override;
5951 IntVar* Var()
override {
5952 return solver()->MakeIsLessOrEqualVar(left_, right_);
5955 void Accept(ModelVisitor*
const visitor)
const override {
5956 visitor->BeginVisitConstraint(ModelVisitor::kLessOrEqual,
this);
5957 visitor->VisitIntegerExpressionArgument(ModelVisitor::kLeftArgument, left_);
5958 visitor->VisitIntegerExpressionArgument(ModelVisitor::kRightArgument,
5960 visitor->EndVisitConstraint(ModelVisitor::kLessOrEqual,
this);
5966 IntExpr*
const left_;
5967 IntExpr*
const right_;
5971 LightRangeLessOrEqual::LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l,
5973 : Constraint(s), left_(l), right_(r), demon_(nullptr) {}
5975 void LightRangeLessOrEqual::Post() {
5977 solver(),
this, &LightRangeLessOrEqual::CheckRange,
"CheckRange");
5978 left_->WhenRange(demon_);
5979 right_->WhenRange(demon_);
5982 void LightRangeLessOrEqual::InitialPropagate() {
5983 left_->SetMax(right_->Max());
5984 right_->SetMin(left_->Min());
5985 if (left_->Max() <= right_->Min()) {
5986 demon_->inhibit(solver());
5990 void LightRangeLessOrEqual::CheckRange() {
5991 if (left_->Min() > right_->Max()) {
5994 if (left_->Max() <= right_->Min()) {
5995 demon_->inhibit(solver());
5999 std::string LightRangeLessOrEqual::DebugString()
const {
6000 return left_->DebugString() +
" < " + right_->DebugString();
6005 void RoutingDimension::InitializeCumuls() {
6006 Solver*
const solver = model_->
solver();
6008 const auto capacity_range = std::minmax_element(vehicle_capacities_.begin(),
6009 vehicle_capacities_.end());
6010 const int64 min_capacity = *capacity_range.first;
6011 CHECK_GE(min_capacity, 0);
6012 const int64 max_capacity = *capacity_range.second;
6013 solver->MakeIntVarArray(size, 0, max_capacity, name_, &cumuls_);
6015 for (
int v = 0; v < model_->
vehicles(); v++) {
6016 const int64 vehicle_capacity = vehicle_capacities_[v];
6017 cumuls_[model_->
Start(v)]->SetMax(vehicle_capacity);
6018 cumuls_[model_->
End(v)]->SetMax(vehicle_capacity);
6021 forbidden_intervals_.resize(size);
6022 capacity_vars_.clear();
6023 if (min_capacity != max_capacity) {
6024 solver->MakeIntVarArray(size, 0,
kint64max, &capacity_vars_);
6025 for (
int i = 0; i < size; ++i) {
6026 IntVar*
const capacity_var = capacity_vars_[i];
6027 if (i < model_->Size()) {
6028 IntVar*
const capacity_active = solver->MakeBoolVar();
6029 solver->AddConstraint(
6030 solver->MakeLessOrEqual(model_->
ActiveVar(i), capacity_active));
6031 solver->AddConstraint(solver->MakeIsLessOrEqualCt(
6032 cumuls_[i], capacity_var, capacity_active));
6034 solver->AddConstraint(
6035 solver->MakeLessOrEqual(cumuls_[i], capacity_var));
6042 template <
int64 value>
6047 void ComputeTransitClasses(
const std::vector<int>& evaluator_indices,
6048 std::vector<int>* class_evaluators,
6049 std::vector<int64>* vehicle_to_class) {
6050 CHECK(class_evaluators !=
nullptr);
6051 CHECK(vehicle_to_class !=
nullptr);
6052 class_evaluators->clear();
6053 vehicle_to_class->resize(evaluator_indices.size(), -1);
6054 absl::flat_hash_map<int, int64> evaluator_to_class;
6055 for (
int i = 0; i < evaluator_indices.size(); ++i) {
6056 const int evaluator_index = evaluator_indices[i];
6057 int evaluator_class = -1;
6058 if (!
gtl::FindCopy(evaluator_to_class, evaluator_index, &evaluator_class)) {
6059 evaluator_class = class_evaluators->size();
6060 evaluator_to_class[evaluator_index] = evaluator_class;
6061 class_evaluators->push_back(evaluator_index);
6063 (*vehicle_to_class)[i] = evaluator_class;
6068 void RoutingDimension::InitializeTransitVariables(
int64 slack_max) {
6069 CHECK(!class_evaluators_.empty());
6070 CHECK(base_dimension_ ==
nullptr ||
6071 !state_dependent_class_evaluators_.empty());
6073 Solver*
const solver = model_->
solver();
6074 const int size = model_->
Size();
6075 const Solver::IndexEvaluator1 dependent_vehicle_class_function =
6077 return (0 <=
index &&
index < state_dependent_vehicle_to_class_.size())
6078 ? state_dependent_vehicle_to_class_[
index]
6079 : state_dependent_class_evaluators_.size();
6081 const std::string slack_name = name_ +
" slack";
6082 const std::string transit_name = name_ +
" fixed transit";
6084 for (
int64 i = 0; i < size; ++i) {
6085 fixed_transits_[i] =
6088 if (base_dimension_ !=
nullptr) {
6089 if (state_dependent_class_evaluators_.size() == 1) {
6090 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
6091 for (
int64 j = 0; j < cumuls_.size(); ++j) {
6092 transition_variables[j] =
6093 MakeRangeMakeElementExpr(
6095 ->StateDependentTransitCallback(
6096 state_dependent_class_evaluators_[0])(i, j)
6098 base_dimension_->
CumulVar(i), solver)
6101 dependent_transits_[i] =
6102 solver->MakeElement(transition_variables, model_->
NextVar(i))
6105 IntVar*
const vehicle_class_var =
6107 ->MakeElement(dependent_vehicle_class_function,
6110 std::vector<IntVar*> transit_for_vehicle;
6111 transit_for_vehicle.reserve(state_dependent_class_evaluators_.size() +
6113 for (
int evaluator : state_dependent_class_evaluators_) {
6114 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
6115 for (
int64 j = 0; j < cumuls_.size(); ++j) {
6116 transition_variables[j] =
6117 MakeRangeMakeElementExpr(
6120 base_dimension_->
CumulVar(i), solver)
6123 transit_for_vehicle.push_back(
6124 solver->MakeElement(transition_variables, model_->
NextVar(i))
6127 transit_for_vehicle.push_back(solver->MakeIntConst(0));
6128 dependent_transits_[i] =
6129 solver->MakeElement(transit_for_vehicle, vehicle_class_var)->Var();
6132 dependent_transits_[i] = solver->MakeIntConst(0);
6136 IntExpr* transit_expr = fixed_transits_[i];
6137 if (dependent_transits_[i]->Min() != 0 ||
6138 dependent_transits_[i]->Max() != 0) {
6139 transit_expr = solver->MakeSum(transit_expr, dependent_transits_[i]);
6142 if (slack_max == 0) {
6143 slacks_[i] = solver->MakeIntConst(0);
6146 solver->MakeIntVar(0, slack_max, absl::StrCat(slack_name, i));
6147 transit_expr = solver->MakeSum(slacks_[i], transit_expr);
6149 transits_[i] = transit_expr->Var();
6153 void RoutingDimension::InitializeTransits(
6154 const std::vector<int>& transit_evaluators,
6155 const std::vector<int>& state_dependent_transit_evaluators,
6157 CHECK_EQ(model_->
vehicles(), transit_evaluators.size());
6158 CHECK(base_dimension_ ==
nullptr ||
6159 model_->
vehicles() == state_dependent_transit_evaluators.size());
6160 const int size = model_->
Size();
6161 transits_.resize(size,
nullptr);
6162 fixed_transits_.resize(size,
nullptr);
6163 slacks_.resize(size,
nullptr);
6164 dependent_transits_.resize(size,
nullptr);
6165 ComputeTransitClasses(transit_evaluators, &class_evaluators_,
6166 &vehicle_to_class_);
6167 if (base_dimension_ !=
nullptr) {
6168 ComputeTransitClasses(state_dependent_transit_evaluators,
6169 &state_dependent_class_evaluators_,
6170 &state_dependent_vehicle_to_class_);
6173 InitializeTransitVariables(slack_max);
6178 std::vector<int64>* values) {
6179 const int num_nodes = path.size();
6180 values->resize(num_nodes - 1);
6181 for (
int i = 0; i < num_nodes - 1; ++i) {
6182 (*values)[i] = evaluator(path[i], path[i + 1]);
6187 : model_(
model), occurrences_of_type_(
model.GetNumberOfVisitTypes()) {}
6190 int vehicle,
const std::function<
int64(
int64)>& next_accessor) {
6197 for (
int pos = 0; pos < current_route_visits_.size(); pos++) {
6198 const int64 current_visit = current_route_visits_[pos];
6205 DCHECK_LT(type, occurrences_of_type_.size());
6206 int& num_type_added = occurrences_of_type_[type].num_type_added_to_vehicle;
6207 int& num_type_removed =
6208 occurrences_of_type_[type].num_type_removed_from_vehicle;
6209 DCHECK_LE(num_type_removed, num_type_added);
6211 num_type_removed == num_type_added) {
6221 if (policy == VisitTypePolicy::TYPE_ADDED_TO_VEHICLE ||
6222 policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED) {
6225 if (policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED ||
6226 policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6234 int vehicle,
const std::function<
int64(
int64)>& next_accessor) {
6237 std::fill(occurrences_of_type_.begin(), occurrences_of_type_.end(),
6243 current_route_visits_.clear();
6245 current = next_accessor(current)) {
6248 VisitTypePolicy::TYPE_ON_VEHICLE_UP_TO_VISIT) {
6249 occurrences_of_type_[type].position_of_last_type_on_vehicle_up_to_visit =
6250 current_route_visits_.size();
6252 current_route_visits_.push_back(current);
6274 check_hard_incompatibilities_(check_hard_incompatibilities) {}
6276 bool TypeIncompatibilityChecker::HasRegulationsToCheck()
const {
6278 (check_hard_incompatibilities_ &&
6287 bool TypeIncompatibilityChecker::CheckTypeRegulations(
int type,
6288 VisitTypePolicy policy,
6290 if (policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6295 for (
int incompatible_type :
6301 if (check_hard_incompatibilities_) {
6302 for (
int incompatible_type :
6312 bool TypeRequirementChecker::HasRegulationsToCheck()
const {
6317 bool TypeRequirementChecker::CheckRequiredTypesCurrentlyOnRoute(
6318 const std::vector<absl::flat_hash_set<int>>& required_type_alternatives,
6320 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6321 required_type_alternatives) {
6322 bool has_one_of_alternatives =
false;
6323 for (
int type_alternative : requirement_alternatives) {
6325 has_one_of_alternatives =
true;
6329 if (!has_one_of_alternatives) {
6336 bool TypeRequirementChecker::CheckTypeRegulations(
int type,
6337 VisitTypePolicy policy,
6341 if (!CheckRequiredTypesCurrentlyOnRoute(
6347 if (!CheckRequiredTypesCurrentlyOnRoute(
6354 types_with_same_vehicle_requirements_on_route_.insert(type);
6359 bool TypeRequirementChecker::FinalizeCheck()
const {
6360 for (
int type : types_with_same_vehicle_requirements_on_route_) {
6361 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6363 bool has_one_of_alternatives =
false;
6364 for (
const int type_alternative : requirement_alternatives) {
6366 has_one_of_alternatives =
true;
6370 if (!has_one_of_alternatives) {
6379 : Constraint(
model.solver()),
6381 incompatibility_checker_(
model, true),
6382 requirement_checker_(
model),
6383 vehicle_demons_(
model.vehicles()) {}
6385 void TypeRegulationsConstraint::PropagateNodeRegulations(
int node) {
6386 DCHECK_LT(node, model_.
Size());
6391 const int vehicle = model_.
VehicleVar(node)->Min();
6392 if (vehicle < 0)
return;
6393 DCHECK(vehicle_demons_[vehicle] !=
nullptr);
6394 EnqueueDelayedDemon(vehicle_demons_[vehicle]);
6397 void TypeRegulationsConstraint::CheckRegulationsOnVehicle(
int vehicle) {
6398 const auto next_accessor = [
this, vehicle](
int64 node) {
6399 if (model_.
NextVar(node)->Bound()) {
6400 return model_.
NextVar(node)->Value();
6403 return model_.
End(vehicle);
6405 if (!incompatibility_checker_.
CheckVehicle(vehicle, next_accessor) ||
6406 !requirement_checker_.
CheckVehicle(vehicle, next_accessor)) {
6412 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6414 solver(),
this, &TypeRegulationsConstraint::CheckRegulationsOnVehicle,
6415 "CheckRegulationsOnVehicle", vehicle);
6417 for (
int node = 0; node < model_.
Size(); node++) {
6419 solver(),
this, &TypeRegulationsConstraint::PropagateNodeRegulations,
6420 "PropagateNodeRegulations", node);
6421 model_.
NextVar(node)->WhenBound(node_demon);
6422 model_.
VehicleVar(node)->WhenBound(node_demon);
6427 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6428 CheckRegulationsOnVehicle(vehicle);
6432 void RoutingDimension::CloseModel(
bool use_light_propagation) {
6433 Solver*
const solver = model_->
solver();
6434 const auto capacity_lambda = [
this](
int64 vehicle) {
6435 return vehicle >= 0 ? vehicle_capacities_[vehicle] :
kint64max;
6437 for (
int i = 0; i < capacity_vars_.size(); ++i) {
6438 IntVar*
const vehicle_var = model_->
VehicleVar(i);
6439 IntVar*
const capacity_var = capacity_vars_[i];
6440 if (use_light_propagation) {
6441 solver->AddConstraint(MakeLightElement(
6442 solver, capacity_var, vehicle_var, capacity_lambda,
6443 [
this]() {
return model_->enable_deep_serialization_; }));
6445 solver->AddConstraint(solver->MakeEquality(
6447 solver->MakeElement(capacity_lambda, vehicle_var)->Var()));
6450 const Solver::IndexEvaluator1 vehicle_class_function = [
this](
int index) {
6451 return IthElementOrValue<-1>(vehicle_to_class_,
index);
6453 for (
int i = 0; i < fixed_transits_.size(); ++i) {
6454 IntVar*
const next_var = model_->
NextVar(i);
6455 IntVar*
const fixed_transit = fixed_transits_[i];
6456 const auto transit_vehicle_evaluator = [
this, i](
int64 to,
6460 if (use_light_propagation) {
6461 if (class_evaluators_.size() == 1) {
6462 const int class_evaluator_index = class_evaluators_[0];
6463 const auto& unary_callback =
6465 if (unary_callback ==
nullptr) {
6466 solver->AddConstraint(MakeLightElement(
6467 solver, fixed_transit, next_var,
6468 [
this, i](
int64 to) {
6471 [
this]() {
return model_->enable_deep_serialization_; }));
6473 fixed_transit->SetValue(unary_callback(i));
6476 solver->AddConstraint(MakeLightElement2(
6477 solver, fixed_transit, next_var, model_->
VehicleVar(i),
6478 transit_vehicle_evaluator,
6479 [
this]() {
return model_->enable_deep_serialization_; }));
6482 if (class_evaluators_.size() == 1) {
6483 const int class_evaluator_index = class_evaluators_[0];
6484 const auto& unary_callback =
6486 if (unary_callback ==
nullptr) {
6487 solver->AddConstraint(solver->MakeEquality(
6488 fixed_transit, solver
6490 [
this, i](
int64 to) {
6492 class_evaluators_[0])(i, to);
6497 fixed_transit->SetValue(unary_callback(i));
6500 IntVar*
const vehicle_class_var =
6501 solver->MakeElement(vehicle_class_function, model_->
VehicleVar(i))
6503 solver->AddConstraint(solver->MakeEquality(
6504 fixed_transit, solver
6505 ->MakeElement(transit_vehicle_evaluator,
6506 next_var, vehicle_class_var)
6512 GlobalVehicleBreaksConstraint* constraint =
6513 model()->
solver()->RevAlloc(
new GlobalVehicleBreaksConstraint(
this));
6514 solver->AddConstraint(constraint);
6519 int64 vehicle)
const {
6528 IntVar*
const cumul_var = cumuls_[
index];
6535 if (next_start >
max)
break;
6536 if (next_start < interval->start) {
6541 if (next_start <=
max) {
6549 CHECK_GE(vehicle, 0);
6550 CHECK_LT(vehicle, vehicle_span_upper_bounds_.size());
6551 CHECK_GE(upper_bound, 0);
6552 vehicle_span_upper_bounds_[vehicle] = upper_bound;
6557 CHECK_GE(vehicle, 0);
6558 CHECK_LT(vehicle, vehicle_span_cost_coefficients_.size());
6560 vehicle_span_cost_coefficients_[vehicle] =
coefficient;
6575 if (!
cost.IsNonDecreasing()) {
6576 LOG(WARNING) <<
"Only non-decreasing cost functions are supported.";
6579 if (
cost.Value(0) < 0) {
6580 LOG(WARNING) <<
"Only positive cost functions are supported.";
6583 if (
index >= cumul_var_piecewise_linear_cost_.size()) {
6584 cumul_var_piecewise_linear_cost_.resize(
index + 1);
6586 PiecewiseLinearCost& piecewise_linear_cost =
6587 cumul_var_piecewise_linear_cost_[
index];
6588 piecewise_linear_cost.var = cumuls_[
index];
6589 piecewise_linear_cost.cost = absl::make_unique<PiecewiseLinearFunction>(
cost);
6593 return (
index < cumul_var_piecewise_linear_cost_.size() &&
6594 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr);
6599 if (
index < cumul_var_piecewise_linear_cost_.size() &&
6600 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr) {
6601 return cumul_var_piecewise_linear_cost_[
index].cost.get();
6608 IntExpr* expr,
int index) {
6609 Solver*
const solver =
model->solver();
6611 const int vehicle =
model->VehicleIndex(
index);
6612 DCHECK_GE(vehicle, 0);
6613 return solver->MakeProd(expr,
model->VehicleCostsConsideredVar(vehicle))
6616 return solver->MakeProd(expr,
model->ActiveVar(
index))->Var();
6620 void RoutingDimension::SetupCumulVarPiecewiseLinearCosts(
6621 std::vector<IntVar*>* cost_elements)
const {
6622 CHECK(cost_elements !=
nullptr);
6623 Solver*
const solver = model_->
solver();
6624 for (
int i = 0; i < cumul_var_piecewise_linear_cost_.size(); ++i) {
6625 const PiecewiseLinearCost& piecewise_linear_cost =
6626 cumul_var_piecewise_linear_cost_[i];
6627 if (piecewise_linear_cost.var !=
nullptr) {
6628 IntExpr*
const expr = solver->MakePiecewiseLinearExpr(
6629 piecewise_linear_cost.var, *piecewise_linear_cost.cost);
6630 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6631 cost_elements->push_back(cost_var);
6641 if (
index >= cumul_var_soft_upper_bound_.size()) {
6642 cumul_var_soft_upper_bound_.resize(
index + 1, {
nullptr, 0, 0});
6644 cumul_var_soft_upper_bound_[
index] = {cumuls_[
index], upper_bound,
6649 return (
index < cumul_var_soft_upper_bound_.size() &&
6650 cumul_var_soft_upper_bound_[
index].var !=
nullptr);
6654 if (
index < cumul_var_soft_upper_bound_.size() &&
6655 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6656 return cumul_var_soft_upper_bound_[
index].bound;
6658 return cumuls_[
index]->Max();
6663 if (
index < cumul_var_soft_upper_bound_.size() &&
6664 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6665 return cumul_var_soft_upper_bound_[
index].coefficient;
6670 void RoutingDimension::SetupCumulVarSoftUpperBoundCosts(
6671 std::vector<IntVar*>* cost_elements)
const {
6672 CHECK(cost_elements !=
nullptr);
6673 Solver*
const solver = model_->
solver();
6674 for (
int i = 0; i < cumul_var_soft_upper_bound_.size(); ++i) {
6675 const SoftBound& soft_bound = cumul_var_soft_upper_bound_[i];
6676 if (soft_bound.var !=
nullptr) {
6677 IntExpr*
const expr = solver->MakeSemiContinuousExpr(
6678 solver->MakeSum(soft_bound.var, -soft_bound.bound), 0,
6679 soft_bound.coefficient);
6680 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6681 cost_elements->push_back(cost_var);
6685 soft_bound.coefficient);
6692 if (
index >= cumul_var_soft_lower_bound_.size()) {
6693 cumul_var_soft_lower_bound_.resize(
index + 1, {
nullptr, 0, 0});
6695 cumul_var_soft_lower_bound_[
index] = {cumuls_[
index], lower_bound,
6700 return (
index < cumul_var_soft_lower_bound_.size() &&
6701 cumul_var_soft_lower_bound_[
index].var !=
nullptr);
6705 if (
index < cumul_var_soft_lower_bound_.size() &&
6706 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6707 return cumul_var_soft_lower_bound_[
index].bound;
6709 return cumuls_[
index]->Min();
6714 if (
index < cumul_var_soft_lower_bound_.size() &&
6715 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6716 return cumul_var_soft_lower_bound_[
index].coefficient;
6721 void RoutingDimension::SetupCumulVarSoftLowerBoundCosts(
6722 std::vector<IntVar*>* cost_elements)
const {
6723 CHECK(cost_elements !=
nullptr);
6724 Solver*
const solver = model_->
solver();
6725 for (
int i = 0; i < cumul_var_soft_lower_bound_.size(); ++i) {
6726 const SoftBound& soft_bound = cumul_var_soft_lower_bound_[i];
6727 if (soft_bound.var !=
nullptr) {
6728 IntExpr*
const expr = solver->MakeSemiContinuousExpr(
6729 solver->MakeDifference(soft_bound.bound, soft_bound.var), 0,
6730 soft_bound.coefficient);
6731 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6732 cost_elements->push_back(cost_var);
6736 soft_bound.coefficient);
6741 void RoutingDimension::SetupGlobalSpanCost(
6742 std::vector<IntVar*>* cost_elements)
const {
6743 CHECK(cost_elements !=
nullptr);
6744 Solver*
const solver = model_->
solver();
6745 if (global_span_cost_coefficient_ != 0) {
6746 std::vector<IntVar*> end_cumuls;
6747 for (
int i = 0; i < model_->
vehicles(); ++i) {
6748 end_cumuls.push_back(solver
6749 ->MakeProd(model_->vehicle_costs_considered_[i],
6750 cumuls_[model_->
End(i)])
6753 IntVar*
const max_end_cumul = solver->MakeMax(end_cumuls)->Var();
6755 max_end_cumul, global_span_cost_coefficient_);
6756 std::vector<IntVar*> start_cumuls;
6757 for (
int i = 0; i < model_->
vehicles(); ++i) {
6758 IntVar* global_span_cost_start_cumul = solver->MakeIntVar(0,
kint64max);
6759 solver->AddConstraint(solver->MakeIfThenElseCt(
6760 model_->vehicle_costs_considered_[i], cumuls_[model_->
Start(i)],
6761 max_end_cumul, global_span_cost_start_cumul));
6762 start_cumuls.push_back(global_span_cost_start_cumul);
6764 IntVar*
const min_start_cumul = solver->MakeMin(start_cumuls)->Var();
6766 min_start_cumul, global_span_cost_coefficient_);
6771 for (
int var_index = 0; var_index < model_->
Size(); ++var_index) {
6773 slacks_[var_index], global_span_cost_coefficient_);
6774 cost_elements->push_back(
6777 model_->vehicle_costs_considered_[0],
6780 solver->MakeSum(transits_[var_index],
6781 dependent_transits_[var_index]),
6782 global_span_cost_coefficient_),
6787 IntVar*
const end_range =
6788 solver->MakeDifference(max_end_cumul, min_start_cumul)->Var();
6789 end_range->SetMin(0);
6790 cost_elements->push_back(
6791 solver->MakeProd(end_range, global_span_cost_coefficient_)->Var());
6797 std::vector<IntervalVar*> breaks,
int vehicle,
6798 std::vector<int64> node_visit_transits) {
6799 if (breaks.empty())
return;
6801 [node_visit_transits](
int64 from,
int64 to) {
6802 return node_visit_transits[from];
6808 std::vector<IntervalVar*> breaks,
int vehicle,
6809 std::vector<int64> node_visit_transits,
6811 if (breaks.empty())
return;
6813 [node_visit_transits](
int64 from,
int64 to) {
6814 return node_visit_transits[from];
6822 std::vector<IntervalVar*> breaks,
int vehicle,
int pre_travel_evaluator,
6823 int post_travel_evaluator) {
6824 DCHECK_LE(0, vehicle);
6825 DCHECK_LT(vehicle, model_->
vehicles());
6826 if (breaks.empty())
return;
6828 vehicle_break_intervals_[vehicle] = std::move(breaks);
6829 vehicle_pre_travel_evaluators_[vehicle] = pre_travel_evaluator;
6830 vehicle_post_travel_evaluators_[vehicle] = post_travel_evaluator;
6832 for (IntervalVar*
const interval : vehicle_break_intervals_[vehicle]) {
6851 DCHECK(!break_constraints_are_initialized_);
6852 const int num_vehicles = model_->
vehicles();
6853 vehicle_break_intervals_.resize(num_vehicles);
6854 vehicle_pre_travel_evaluators_.resize(num_vehicles, -1);
6855 vehicle_post_travel_evaluators_.resize(num_vehicles, -1);
6856 vehicle_break_distance_duration_.resize(num_vehicles);
6857 break_constraints_are_initialized_ =
true;
6861 return break_constraints_are_initialized_;
6865 int vehicle)
const {
6866 DCHECK_LE(0, vehicle);
6867 DCHECK_LT(vehicle, vehicle_break_intervals_.size());
6868 return vehicle_break_intervals_[vehicle];
6872 DCHECK_LE(0, vehicle);
6873 DCHECK_LT(vehicle, vehicle_pre_travel_evaluators_.size());
6874 return vehicle_pre_travel_evaluators_[vehicle];
6878 DCHECK_LE(0, vehicle);
6879 DCHECK_LT(vehicle, vehicle_post_travel_evaluators_.size());
6880 return vehicle_post_travel_evaluators_[vehicle];
6886 DCHECK_LE(0, vehicle);
6887 DCHECK_LT(vehicle, model_->
vehicles());
6889 vehicle_break_distance_duration_[vehicle].emplace_back(distance, duration);
6898 const std::vector<std::pair<int64, int64>>&
6900 DCHECK_LE(0, vehicle);
6901 DCHECK_LT(vehicle, vehicle_break_distance_duration_.size());
6902 return vehicle_break_distance_duration_[vehicle];
6907 CHECK_GE(pair_index, 0);
6908 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6909 pickup_to_delivery_limits_per_pair_index_.resize(pair_index + 1);
6911 pickup_to_delivery_limits_per_pair_index_[pair_index] =
6912 std::move(limit_function);
6916 return !pickup_to_delivery_limits_per_pair_index_.empty();
6921 int delivery)
const {
6922 DCHECK_GE(pair_index, 0);
6924 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6928 pickup_to_delivery_limits_per_pair_index_[pair_index];
6929 if (!pickup_to_delivery_limit_function) {
6933 DCHECK_GE(pickup, 0);
6934 DCHECK_GE(delivery, 0);
6935 return pickup_to_delivery_limit_function(pickup, delivery);
6938 void RoutingDimension::SetupSlackAndDependentTransitCosts()
const {
6939 if (model_->
vehicles() == 0)
return;
6941 bool all_vehicle_span_costs_are_equal =
true;
6942 for (
int i = 1; i < model_->
vehicles(); ++i) {
6943 all_vehicle_span_costs_are_equal &= vehicle_span_cost_coefficients_[i] ==
6944 vehicle_span_cost_coefficients_[0];
6947 if (all_vehicle_span_costs_are_equal &&
6948 vehicle_span_cost_coefficients_[0] == 0) {
6960 std::vector<const RoutingDimension*> dimensions_with_relevant_slacks = {
this};
6962 const RoutingDimension*
next =
6963 dimensions_with_relevant_slacks.back()->base_dimension_;
6964 if (
next ==
nullptr ||
next == dimensions_with_relevant_slacks.back()) {
6967 dimensions_with_relevant_slacks.push_back(
next);
6970 for (
auto it = dimensions_with_relevant_slacks.rbegin();
6971 it != dimensions_with_relevant_slacks.rend(); ++it) {
6972 for (
int i = 0; i < model_->
vehicles(); ++i) {
6978 for (IntVar*
const slack : (*it)->slacks_) {