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);
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();
3140 void MakeAllUnperformed(
const RoutingModel*
model, Assignment* assignment) {
3141 assignment->Clear();
3142 for (
int i = 0; i <
model->Nexts().size(); ++i) {
3143 if (!
model->IsStart(i)) {
3144 assignment->Add(
model->NextVar(i))->SetValue(i);
3147 for (
int vehicle = 0; vehicle <
model->vehicles(); ++vehicle) {
3148 assignment->Add(
model->NextVar(
model->Start(vehicle)))
3149 ->SetValue(
model->End(vehicle));
3154 bool RoutingModel::AppendAssignmentIfFeasible(
3155 const Assignment& assignment,
3156 std::vector<std::unique_ptr<Assignment>>* assignments) {
3157 tmp_assignment_->CopyIntersection(&assignment);
3158 solver_->Solve(restore_tmp_assignment_, collect_one_assignment_,
3159 GetOrCreateLimit());
3160 if (collect_one_assignment_->solution_count() == 1) {
3161 assignments->push_back(absl::make_unique<Assignment>(solver_.get()));
3162 assignments->back()->Copy(collect_one_assignment_->solution(0));
3168 void RoutingModel::LogSolution(
const RoutingSearchParameters&
parameters,
3169 const std::string& description,
3172 const double cost_scaling_factor =
parameters.log_cost_scaling_factor();
3173 const double cost_offset =
parameters.log_cost_offset();
3174 const std::string cost_string =
3175 cost_scaling_factor == 1.0 && cost_offset == 0.0
3176 ? absl::StrCat(solution_cost)
3178 "%d (%.8lf)", solution_cost,
3179 cost_scaling_factor * (solution_cost + cost_offset));
3180 LOG(INFO) << absl::StrFormat(
3181 "%s (%s, time = %d ms, memory used = %s)", description, cost_string,
3182 solver_->wall_time() - start_time_ms, memory_str);
3186 const Assignment* assignment,
const RoutingSearchParameters&
parameters,
3187 std::vector<const Assignment*>* solutions) {
3188 const int64 start_time_ms = solver_->wall_time();
3190 VLOG(1) <<
"Search parameters:\n" <<
parameters.DebugString();
3191 if (solutions !=
nullptr) solutions->clear();
3205 const int time_limit_shares = 1 + !global_dimension_optimizers_.empty() +
3206 !local_dimension_optimizers_.empty();
3207 const absl::Duration first_solution_lns_time_limit =
3210 first_solution_lns_limit_->UpdateLimits(first_solution_lns_time_limit,
3213 std::vector<std::unique_ptr<Assignment>> solution_pool;
3215 if (
nullptr == assignment) {
3216 bool solution_found =
false;
3217 Assignment matching(solver_.get());
3219 AppendAssignmentIfFeasible(matching, &solution_pool)) {
3221 LogSolution(
parameters,
"Min-Cost Flow Solution",
3222 solution_pool.back()->ObjectiveValue(), start_time_ms);
3224 solution_found =
true;
3226 if (!solution_found) {
3229 Assignment unperformed(solver_.get());
3230 MakeAllUnperformed(
this, &unperformed);
3231 if (AppendAssignmentIfFeasible(unperformed, &solution_pool) &&
3233 LogSolution(
parameters,
"All Unperformed Solution",
3234 solution_pool.back()->ObjectiveValue(), start_time_ms);
3236 const absl::Duration elapsed_time =
3237 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3238 const absl::Duration time_left =
3240 if (time_left >= absl::ZeroDuration()) {
3244 solver_->Solve(solve_db_, monitors_);
3248 assignment_->CopyIntersection(assignment);
3249 solver_->Solve(improve_db_, monitors_);
3254 const int solution_count = collect_assignments_->solution_count();
3255 Assignment*
const cp_solution =
3256 solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
3258 Assignment sat_solution(solver_.get());
3260 AppendAssignmentIfFeasible(sat_solution, &solution_pool) &&
3262 LogSolution(
parameters,
"SAT", solution_pool.back()->ObjectiveValue(),
3267 const absl::Duration elapsed_time =
3268 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3269 const int solution_count = collect_assignments_->solution_count();
3270 if (solution_count >= 1 || !solution_pool.empty()) {
3272 if (solutions !=
nullptr) {
3273 for (
int i = 0; i < solution_count; ++i) {
3274 solutions->push_back(
3275 solver_->MakeAssignment(collect_assignments_->solution(i)));
3277 for (
const auto& solution : solution_pool) {
3278 if (solutions->empty() ||
3279 solution->ObjectiveValue() < solutions->back()->ObjectiveValue()) {
3280 solutions->push_back(solver_->MakeAssignment(solution.get()));
3283 return solutions->back();
3285 Assignment* best_assignment =
3286 solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
3288 for (
const auto& solution : solution_pool) {
3289 if (best_assignment ==
nullptr ||
3290 solution->ObjectiveValue() < best_assignment->ObjectiveValue()) {
3291 best_assignment = solution.get();
3294 return solver_->MakeAssignment(best_assignment);
3296 if (elapsed_time >= GetTimeLimit(
parameters)) {
3306 Assignment* target_assignment,
const RoutingModel* source_model,
3307 const Assignment* source_assignment) {
3308 const int size =
Size();
3309 DCHECK_EQ(size, source_model->
Size());
3310 CHECK_EQ(target_assignment->solver(), solver_.get());
3314 source_model->
Nexts());
3316 std::vector<IntVar*> source_vars(size + size + vehicles_);
3317 std::vector<IntVar*> target_vars(size + size + vehicles_);
3327 source_assignment, source_vars);
3330 target_assignment->AddObjective(cost_);
3345 LOG(WARNING) <<
"Non-closed model not supported.";
3349 LOG(WARNING) <<
"Non-homogeneous vehicle costs not supported";
3352 if (!disjunctions_.
empty()) {
3354 <<
"Node disjunction constraints or optional nodes not supported.";
3357 const int num_nodes =
Size() + vehicles_;
3364 std::unique_ptr<IntVarIterator> iterator(
3365 nexts_[
tail]->MakeDomainIterator(
false));
3366 for (
const int64 head : InitAndGetValues(iterator.get())) {
3388 return linear_sum_assignment.
GetCost();
3393 bool RoutingModel::RouteCanBeUsedByVehicle(
const Assignment& assignment,
3394 int start_index,
int vehicle)
const {
3396 IsStart(start_index) ?
Next(assignment, start_index) : start_index;
3397 while (!
IsEnd(current_index)) {
3398 const IntVar*
const vehicle_var =
VehicleVar(current_index);
3399 if (!vehicle_var->Contains(vehicle)) {
3402 const int next_index =
Next(assignment, current_index);
3403 CHECK_NE(next_index, current_index) <<
"Inactive node inside a route";
3404 current_index = next_index;
3409 bool RoutingModel::ReplaceUnusedVehicle(
3410 int unused_vehicle,
int active_vehicle,
3411 Assignment*
const compact_assignment)
const {
3412 CHECK(compact_assignment !=
nullptr);
3416 const int unused_vehicle_start =
Start(unused_vehicle);
3417 IntVar*
const unused_vehicle_start_var =
NextVar(unused_vehicle_start);
3418 const int unused_vehicle_end =
End(unused_vehicle);
3419 const int active_vehicle_start =
Start(active_vehicle);
3420 const int active_vehicle_end =
End(active_vehicle);
3421 IntVar*
const active_vehicle_start_var =
NextVar(active_vehicle_start);
3422 const int active_vehicle_next =
3423 compact_assignment->Value(active_vehicle_start_var);
3424 compact_assignment->SetValue(unused_vehicle_start_var, active_vehicle_next);
3425 compact_assignment->SetValue(active_vehicle_start_var,
End(active_vehicle));
3428 int current_index = active_vehicle_next;
3429 while (!
IsEnd(current_index)) {
3430 IntVar*
const vehicle_var =
VehicleVar(current_index);
3431 compact_assignment->SetValue(vehicle_var, unused_vehicle);
3432 const int next_index =
Next(*compact_assignment, current_index);
3433 if (
IsEnd(next_index)) {
3434 IntVar*
const last_next_var =
NextVar(current_index);
3435 compact_assignment->SetValue(last_next_var,
End(unused_vehicle));
3437 current_index = next_index;
3442 const std::vector<IntVar*>& transit_variables = dimension->transits();
3443 IntVar*
const unused_vehicle_transit_var =
3444 transit_variables[unused_vehicle_start];
3445 IntVar*
const active_vehicle_transit_var =
3446 transit_variables[active_vehicle_start];
3447 const bool contains_unused_vehicle_transit_var =
3448 compact_assignment->Contains(unused_vehicle_transit_var);
3449 const bool contains_active_vehicle_transit_var =
3450 compact_assignment->Contains(active_vehicle_transit_var);
3451 if (contains_unused_vehicle_transit_var !=
3452 contains_active_vehicle_transit_var) {
3454 LOG(INFO) <<
"The assignment contains transit variable for dimension '"
3455 << dimension->name() <<
"' for some vehicles, but not for all";
3458 if (contains_unused_vehicle_transit_var) {
3459 const int64 old_unused_vehicle_transit =
3460 compact_assignment->Value(unused_vehicle_transit_var);
3461 const int64 old_active_vehicle_transit =
3462 compact_assignment->Value(active_vehicle_transit_var);
3463 compact_assignment->SetValue(unused_vehicle_transit_var,
3464 old_active_vehicle_transit);
3465 compact_assignment->SetValue(active_vehicle_transit_var,
3466 old_unused_vehicle_transit);
3470 const std::vector<IntVar*>& cumul_variables = dimension->cumuls();
3471 IntVar*
const unused_vehicle_cumul_var =
3472 cumul_variables[unused_vehicle_end];
3473 IntVar*
const active_vehicle_cumul_var =
3474 cumul_variables[active_vehicle_end];
3475 const int64 old_unused_vehicle_cumul =
3476 compact_assignment->Value(unused_vehicle_cumul_var);
3477 const int64 old_active_vehicle_cumul =
3478 compact_assignment->Value(active_vehicle_cumul_var);
3479 compact_assignment->SetValue(unused_vehicle_cumul_var,
3480 old_active_vehicle_cumul);
3481 compact_assignment->SetValue(active_vehicle_cumul_var,
3482 old_unused_vehicle_cumul);
3488 const Assignment& assignment)
const {
3489 return CompactAssignmentInternal(assignment,
false);
3493 const Assignment& assignment)
const {
3494 return CompactAssignmentInternal(assignment,
true);
3497 Assignment* RoutingModel::CompactAssignmentInternal(
3498 const Assignment& assignment,
bool check_compact_assignment)
const {
3499 CHECK_EQ(assignment.solver(), solver_.get());
3502 <<
"The costs are not homogeneous, routes cannot be rearranged";
3506 std::unique_ptr<Assignment> compact_assignment(
new Assignment(&assignment));
3507 for (
int vehicle = 0; vehicle < vehicles_ - 1; ++vehicle) {
3511 const int vehicle_start =
Start(vehicle);
3512 const int vehicle_end =
End(vehicle);
3514 int swap_vehicle = vehicles_ - 1;
3515 bool has_more_vehicles_with_route =
false;
3516 for (; swap_vehicle > vehicle; --swap_vehicle) {
3523 has_more_vehicles_with_route =
true;
3524 const int swap_vehicle_start =
Start(swap_vehicle);
3525 const int swap_vehicle_end =
End(swap_vehicle);
3534 if (RouteCanBeUsedByVehicle(*compact_assignment, swap_vehicle_start,
3540 if (swap_vehicle == vehicle) {
3541 if (has_more_vehicles_with_route) {
3545 LOG(INFO) <<
"No vehicle that can be swapped with " << vehicle
3552 if (!ReplaceUnusedVehicle(vehicle, swap_vehicle,
3553 compact_assignment.get())) {
3558 if (check_compact_assignment &&
3559 !solver_->CheckAssignment(compact_assignment.get())) {
3561 LOG(WARNING) <<
"The compacted assignment is not a valid solution";
3564 return compact_assignment.release();
3567 int RoutingModel::FindNextActive(
int index,
3568 const std::vector<int64>& indices)
const {
3571 const int size = indices.size();
3581 CHECK_EQ(vehicles_, 1);
3582 preassignment_->Clear();
3583 IntVar* next_var =
nullptr;
3584 int lock_index = FindNextActive(-1, locks);
3585 const int size = locks.size();
3586 if (lock_index < size) {
3587 next_var =
NextVar(locks[lock_index]);
3588 preassignment_->Add(next_var);
3589 for (lock_index = FindNextActive(lock_index, locks); lock_index < size;
3590 lock_index = FindNextActive(lock_index, locks)) {
3591 preassignment_->SetValue(next_var, locks[lock_index]);
3592 next_var =
NextVar(locks[lock_index]);
3593 preassignment_->Add(next_var);
3600 const std::vector<std::vector<int64>>& locks,
bool close_routes) {
3601 preassignment_->Clear();
3606 const RoutingSearchParameters&
parameters)
const {
3608 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3614 const RoutingSearchParameters&
parameters)
const {
3616 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3622 if (collect_assignments_->solution_count() == 1 && assignment_ !=
nullptr) {
3623 assignment_->CopyIntersection(collect_assignments_->solution(0));
3624 return assignment_->Save(file_name);
3632 CHECK(assignment_ !=
nullptr);
3633 if (assignment_->Load(file_name)) {
3634 return DoRestoreAssignment();
3641 CHECK(assignment_ !=
nullptr);
3642 assignment_->CopyIntersection(&solution);
3643 return DoRestoreAssignment();
3646 Assignment* RoutingModel::DoRestoreAssignment() {
3650 solver_->Solve(restore_assignment_, monitors_);
3651 if (collect_assignments_->solution_count() == 1) {
3653 return collect_assignments_->solution(0);
3662 const std::vector<std::vector<int64>>& routes,
bool ignore_inactive_indices,
3663 bool close_routes, Assignment*
const assignment)
const {
3664 CHECK(assignment !=
nullptr);
3666 LOG(ERROR) <<
"The model is not closed yet";
3669 const int num_routes = routes.size();
3670 if (num_routes > vehicles_) {
3671 LOG(ERROR) <<
"The number of vehicles in the assignment (" << routes.size()
3672 <<
") is greater than the number of vehicles in the model ("
3673 << vehicles_ <<
")";
3677 absl::flat_hash_set<int> visited_indices;
3679 for (
int vehicle = 0; vehicle < num_routes; ++vehicle) {
3680 const std::vector<int64>& route = routes[vehicle];
3681 int from_index =
Start(vehicle);
3682 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3683 visited_indices.insert(from_index);
3684 if (!insert_result.second) {
3685 LOG(ERROR) <<
"Index " << from_index <<
" (start node for vehicle "
3686 << vehicle <<
") was already used";
3690 for (
const int64 to_index : route) {
3691 if (to_index < 0 || to_index >=
Size()) {
3692 LOG(ERROR) <<
"Invalid index: " << to_index;
3696 IntVar*
const active_var =
ActiveVar(to_index);
3697 if (active_var->Max() == 0) {
3698 if (ignore_inactive_indices) {
3701 LOG(ERROR) <<
"Index " << to_index <<
" is not active";
3706 insert_result = visited_indices.insert(to_index);
3707 if (!insert_result.second) {
3708 LOG(ERROR) <<
"Index " << to_index <<
" is used multiple times";
3712 const IntVar*
const vehicle_var =
VehicleVar(to_index);
3713 if (!vehicle_var->Contains(vehicle)) {
3714 LOG(ERROR) <<
"Vehicle " << vehicle <<
" is not allowed at index "
3719 IntVar*
const from_var =
NextVar(from_index);
3720 if (!assignment->Contains(from_var)) {
3721 assignment->Add(from_var);
3723 assignment->SetValue(from_var, to_index);
3725 from_index = to_index;
3729 IntVar*
const last_var =
NextVar(from_index);
3730 if (!assignment->Contains(last_var)) {
3731 assignment->Add(last_var);
3733 assignment->SetValue(last_var,
End(vehicle));
3738 for (
int vehicle = num_routes; vehicle < vehicles_; ++vehicle) {
3739 const int start_index =
Start(vehicle);
3742 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3743 visited_indices.insert(start_index);
3744 if (!insert_result.second) {
3745 LOG(ERROR) <<
"Index " << start_index <<
" is used multiple times";
3749 IntVar*
const start_var =
NextVar(start_index);
3750 if (!assignment->Contains(start_var)) {
3751 assignment->Add(start_var);
3753 assignment->SetValue(start_var,
End(vehicle));
3762 if (!assignment->Contains(next_var)) {
3763 assignment->Add(next_var);
3765 assignment->SetValue(next_var,
index);
3774 const std::vector<std::vector<int64>>& routes,
3775 bool ignore_inactive_indices) {
3783 return DoRestoreAssignment();
3787 const Assignment& assignment,
3788 std::vector<std::vector<int64>>*
const routes)
const {
3790 CHECK(routes !=
nullptr);
3792 const int model_size =
Size();
3793 routes->resize(vehicles_);
3794 for (
int vehicle = 0; vehicle < vehicles_; ++vehicle) {
3795 std::vector<int64>*
const vehicle_route = &routes->at(vehicle);
3796 vehicle_route->clear();
3798 int num_visited_indices = 0;
3799 const int first_index =
Start(vehicle);
3800 const IntVar*
const first_var =
NextVar(first_index);
3801 CHECK(assignment.Contains(first_var));
3802 CHECK(assignment.Bound(first_var));
3803 int current_index = assignment.Value(first_var);
3804 while (!
IsEnd(current_index)) {
3805 vehicle_route->push_back(current_index);
3807 const IntVar*
const next_var =
NextVar(current_index);
3808 CHECK(assignment.Contains(next_var));
3809 CHECK(assignment.Bound(next_var));
3810 current_index = assignment.Value(next_var);
3812 ++num_visited_indices;
3813 CHECK_LE(num_visited_indices, model_size)
3814 <<
"The assignment contains a cycle";
3821 const Assignment& assignment) {
3822 std::vector<std::vector<int64>> route_indices(
vehicles());
3823 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3824 if (!assignment.Bound(
NextVar(vehicle))) {
3825 LOG(DFATAL) <<
"GetRoutesFromAssignment() called on incomplete solution:"
3826 <<
" NextVar(" << vehicle <<
") is unbound.";
3829 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3831 route_indices[vehicle].push_back(
index);
3834 route_indices[vehicle].push_back(
index);
3837 return route_indices;
3841 int64 RoutingModel::GetArcCostForClassInternal(
3842 int64 from_index,
int64 to_index, CostClassIndex cost_class_index)
const {
3844 DCHECK_GE(cost_class_index, 0);
3845 DCHECK_LT(cost_class_index, cost_classes_.size());
3846 CostCacheElement*
const cache = &cost_cache_[from_index];
3848 if (cache->index ==
static_cast<int>(to_index) &&
3849 cache->cost_class_index == cost_class_index) {
3853 const CostClass& cost_class = cost_classes_[cost_class_index];
3854 const auto& evaluator = transit_evaluators_[cost_class.evaluator_index];
3856 cost =
CapAdd(evaluator(from_index, to_index),
3857 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3858 }
else if (!
IsEnd(to_index)) {
3862 evaluator(from_index, to_index),
3863 CapAdd(GetDimensionTransitCostSum(from_index, to_index, cost_class),
3864 fixed_cost_of_vehicle_[index_to_vehicle_[from_index]]));
3868 if (consider_empty_route_costs_[index_to_vehicle_[from_index]]) {
3870 CapAdd(evaluator(from_index, to_index),
3871 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3876 *cache = {
static_cast<int>(to_index), cost_class_index,
cost};
3885 int vehicle)
const {
3886 CHECK_GE(vehicle, 0);
3887 CHECK_LT(vehicle, vehicles_);
3888 CHECK_EQ(solver_.get(), assignment.solver());
3890 CHECK(assignment.Contains(start_var));
3891 return !
IsEnd(assignment.Value(start_var));
3895 CHECK_EQ(solver_.get(), assignment.solver());
3897 CHECK(assignment.Contains(next_var));
3898 CHECK(assignment.Bound(next_var));
3899 return assignment.Value(next_var);
3903 int64 vehicle)
const {
3904 if (from_index != to_index && vehicle >= 0) {
3905 return GetArcCostForClassInternal(from_index, to_index,
3914 int64 cost_class_index)
const {
3915 if (from_index != to_index) {
3916 return GetArcCostForClassInternal(from_index, to_index,
3924 int64 to_index)
const {
3928 if (!is_bound_to_end_ct_added_.
Switched()) {
3931 std::vector<IntVar*> zero_transit(
Size(), solver_->MakeIntConst(0));
3932 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
3933 nexts_, active_, is_bound_to_end_, zero_transit));
3934 is_bound_to_end_ct_added_.
Switch(solver_.get());
3936 if (is_bound_to_end_[to_index]->Min() == 1)
return kint64max;
3941 int64 RoutingModel::GetDimensionTransitCostSum(
3942 int64 i,
int64 j,
const CostClass& cost_class)
const {
3944 for (
const auto& evaluator_and_coefficient :
3945 cost_class.dimension_transit_evaluator_class_and_cost_coefficient) {
3946 DCHECK_GT(evaluator_and_coefficient.cost_coefficient, 0);
3949 CapProd(evaluator_and_coefficient.cost_coefficient,
3950 evaluator_and_coefficient.dimension->GetTransitValueFromClass(
3951 i, j, evaluator_and_coefficient.transit_evaluator_class)));
3967 const bool mandatory1 = active_[to1]->Min() == 1;
3968 const bool mandatory2 = active_[to2]->Min() == 1;
3970 if (mandatory1 != mandatory2)
return mandatory1;
3973 IntVar*
const src_vehicle_var =
VehicleVar(from);
3977 const int64 src_vehicle = src_vehicle_var->Max();
3978 if (src_vehicle_var->Bound()) {
3979 IntVar*
const to1_vehicle_var =
VehicleVar(to1);
3980 IntVar*
const to2_vehicle_var =
VehicleVar(to2);
3985 mandatory1 ? to1_vehicle_var->Bound() : (to1_vehicle_var->Size() <= 2);
3987 mandatory2 ? to2_vehicle_var->Bound() : (to2_vehicle_var->Size() <= 2);
3990 if (bound1 != bound2)
return bound1;
3993 const int64 vehicle1 = to1_vehicle_var->Max();
3994 const int64 vehicle2 = to2_vehicle_var->Max();
3997 if ((vehicle1 == src_vehicle) != (vehicle2 == src_vehicle)) {
3998 return vehicle1 == src_vehicle;
4003 if (vehicle1 != src_vehicle)
return to1 < to2;
4014 const std::vector<IntVar*>& cumul_vars =
4016 IntVar*
const dim1 = cumul_vars[to1];
4017 IntVar*
const dim2 = cumul_vars[to2];
4020 if (dim1->Max() != dim2->Max())
return dim1->Max() < dim2->Max();
4029 const int64 cost_class_index =
4030 SafeGetCostClassInt64OfVehicle(src_vehicle);
4035 if (cost1 != cost2)
return cost1 < cost2;
4042 if (num_vehicles1 != num_vehicles2)
return num_vehicles1 < num_vehicles2;
4050 CHECK_LT(
index, index_to_visit_type_.size());
4051 DCHECK_EQ(index_to_visit_type_.size(), index_to_type_policy_.size());
4052 index_to_visit_type_[
index] = type;
4053 index_to_type_policy_[
index] = policy;
4054 num_visit_types_ =
std::max(num_visit_types_, type + 1);
4058 CHECK_LT(
index, index_to_visit_type_.size());
4059 return index_to_visit_type_[
index];
4063 DCHECK_LT(type, single_nodes_of_type_.size());
4064 return single_nodes_of_type_[type];
4068 DCHECK_LT(type, pair_indices_of_type_.size());
4069 return pair_indices_of_type_[type];
4074 CHECK_LT(
index, index_to_type_policy_.size());
4075 return index_to_type_policy_[
index];
4079 hard_incompatible_types_per_type_index_.resize(num_visit_types_);
4080 temporal_incompatible_types_per_type_index_.resize(num_visit_types_);
4081 same_vehicle_required_type_alternatives_per_type_index_.resize(
4083 required_type_alternatives_when_adding_type_index_.resize(num_visit_types_);
4084 required_type_alternatives_when_removing_type_index_.resize(num_visit_types_);
4089 hard_incompatible_types_per_type_index_.size());
4090 has_hard_type_incompatibilities_ =
true;
4092 hard_incompatible_types_per_type_index_[type1].insert(type2);
4093 hard_incompatible_types_per_type_index_[type2].insert(type1);
4098 temporal_incompatible_types_per_type_index_.size());
4099 has_temporal_type_incompatibilities_ =
true;
4101 temporal_incompatible_types_per_type_index_[type1].insert(type2);
4102 temporal_incompatible_types_per_type_index_[type2].insert(type1);
4105 const absl::flat_hash_set<int>&
4108 DCHECK_LT(type, hard_incompatible_types_per_type_index_.size());
4109 return hard_incompatible_types_per_type_index_[type];
4112 const absl::flat_hash_set<int>&
4115 DCHECK_LT(type, temporal_incompatible_types_per_type_index_.size());
4116 return temporal_incompatible_types_per_type_index_[type];
4122 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4123 DCHECK_LT(dependent_type,
4124 same_vehicle_required_type_alternatives_per_type_index_.size());
4126 if (required_type_alternatives.empty()) {
4130 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4131 trivially_infeasible_visit_types_to_policies_[dependent_type];
4138 has_same_vehicle_type_requirements_ =
true;
4139 same_vehicle_required_type_alternatives_per_type_index_[dependent_type]
4140 .push_back(std::move(required_type_alternatives));
4144 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4145 DCHECK_LT(dependent_type,
4146 required_type_alternatives_when_adding_type_index_.size());
4148 if (required_type_alternatives.empty()) {
4152 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4153 trivially_infeasible_visit_types_to_policies_[dependent_type];
4159 has_temporal_type_requirements_ =
true;
4160 required_type_alternatives_when_adding_type_index_[dependent_type].push_back(
4161 std::move(required_type_alternatives));
4165 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4166 DCHECK_LT(dependent_type,
4167 required_type_alternatives_when_removing_type_index_.size());
4169 if (required_type_alternatives.empty()) {
4173 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4174 trivially_infeasible_visit_types_to_policies_[dependent_type];
4181 has_temporal_type_requirements_ =
true;
4182 required_type_alternatives_when_removing_type_index_[dependent_type]
4183 .push_back(std::move(required_type_alternatives));
4186 const std::vector<absl::flat_hash_set<int>>&
4190 same_vehicle_required_type_alternatives_per_type_index_.size());
4191 return same_vehicle_required_type_alternatives_per_type_index_[type];
4194 const std::vector<absl::flat_hash_set<int>>&
4197 DCHECK_LT(type, required_type_alternatives_when_adding_type_index_.size());
4198 return required_type_alternatives_when_adding_type_index_[type];
4201 const std::vector<absl::flat_hash_set<int>>&
4204 DCHECK_LT(type, required_type_alternatives_when_removing_type_index_.size());
4205 return required_type_alternatives_when_removing_type_index_[type];
4213 int64 var_index)
const {
4214 if (active_[var_index]->Min() == 1)
return kint64max;
4215 const std::vector<DisjunctionIndex>& disjunction_indices =
4217 if (disjunction_indices.size() != 1)
return default_value;
4222 return std::max(
int64{0}, disjunctions_[disjunction_index].value.penalty);
4226 const Assignment& solution_assignment,
4227 const std::string& dimension_to_print)
const {
4228 for (
int i = 0; i <
Size(); ++i) {
4229 if (!solution_assignment.Bound(
NextVar(i))) {
4231 <<
"DebugOutputVehicleSchedules() called on incomplete solution:"
4232 <<
" NextVar(" << i <<
") is unbound.";
4237 absl::flat_hash_set<std::string> dimension_names;
4238 if (dimension_to_print.empty()) {
4240 dimension_names.insert(all_dimension_names.begin(),
4241 all_dimension_names.end());
4243 dimension_names.insert(dimension_to_print);
4245 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4246 int empty_vehicle_range_start = vehicle;
4251 if (empty_vehicle_range_start != vehicle) {
4252 if (empty_vehicle_range_start == vehicle - 1) {
4253 absl::StrAppendFormat(&output,
"Vehicle %d: empty",
4254 empty_vehicle_range_start);
4256 absl::StrAppendFormat(&output,
"Vehicles %d-%d: empty",
4257 empty_vehicle_range_start, vehicle - 1);
4259 output.append(
"\n");
4262 absl::StrAppendFormat(&output,
"Vehicle %d:", vehicle);
4266 absl::StrAppendFormat(&output,
"%d Vehicle(%d) ",
index,
4267 solution_assignment.Value(vehicle_var));
4270 const IntVar*
const var = dimension->CumulVar(
index);
4271 absl::StrAppendFormat(&output,
"%s(%d..%d) ", dimension->name(),
4272 solution_assignment.Min(
var),
4273 solution_assignment.Max(
var));
4278 if (
IsEnd(
index)) output.append(
"Route end ");
4280 output.append(
"\n");
4283 output.append(
"Unperformed nodes: ");
4284 bool has_unperformed =
false;
4285 for (
int i = 0; i <
Size(); ++i) {
4287 solution_assignment.Value(
NextVar(i)) == i) {
4288 absl::StrAppendFormat(&output,
"%d ", i);
4289 has_unperformed =
true;
4292 if (!has_unperformed) output.append(
"None");
4293 output.append(
"\n");
4299 const Assignment& solution_assignment,
const RoutingDimension& dimension) {
4300 std::vector<std::vector<std::pair<int64, int64>>> cumul_bounds(
vehicles());
4301 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4302 if (!solution_assignment.Bound(
NextVar(vehicle))) {
4303 LOG(DFATAL) <<
"GetCumulBounds() called on incomplete solution:"
4304 <<
" NextVar(" << vehicle <<
") is unbound.";
4308 for (
int vehicle_id = 0; vehicle_id <
vehicles(); ++vehicle_id) {
4311 cumul_bounds[vehicle_id].emplace_back(solution_assignment.Min(dim_var),
4312 solution_assignment.Max(dim_var));
4316 cumul_bounds[vehicle_id].emplace_back(solution_assignment.Min(dim_var),
4317 solution_assignment.Max(dim_var));
4320 return cumul_bounds;
4324 Assignment* RoutingModel::GetOrCreateAssignment() {
4325 if (assignment_ ==
nullptr) {
4326 assignment_ = solver_->MakeAssignment();
4327 assignment_->Add(nexts_);
4329 assignment_->Add(vehicle_vars_);
4331 assignment_->AddObjective(cost_);
4336 Assignment* RoutingModel::GetOrCreateTmpAssignment() {
4337 if (tmp_assignment_ ==
nullptr) {
4338 tmp_assignment_ = solver_->MakeAssignment();
4339 tmp_assignment_->Add(nexts_);
4341 return tmp_assignment_;
4344 RegularLimit* RoutingModel::GetOrCreateLimit() {
4345 if (limit_ ==
nullptr) {
4352 RegularLimit* RoutingModel::GetOrCreateLocalSearchLimit() {
4353 if (ls_limit_ ==
nullptr) {
4361 RegularLimit* RoutingModel::GetOrCreateLargeNeighborhoodSearchLimit() {
4362 if (lns_limit_ ==
nullptr) {
4371 RoutingModel::GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit() {
4372 if (first_solution_lns_limit_ ==
nullptr) {
4373 first_solution_lns_limit_ =
4377 return first_solution_lns_limit_;
4380 LocalSearchOperator* RoutingModel::CreateInsertionOperator() {
4381 std::vector<IntVar*> empty;
4382 LocalSearchOperator* insertion_operator =
4383 MakeLocalSearchOperator<MakeActiveOperator>(
4384 solver_.get(), nexts_,
4386 vehicle_start_class_callback_);
4387 if (!pickup_delivery_pairs_.empty()) {
4388 insertion_operator = solver_->ConcatenateOperators(
4390 solver_.get(), nexts_,
4392 vehicle_start_class_callback_, pickup_delivery_pairs_),
4393 insertion_operator});
4395 return insertion_operator;
4398 LocalSearchOperator* RoutingModel::CreateMakeInactiveOperator() {
4399 std::vector<IntVar*> empty;
4400 LocalSearchOperator* make_inactive_operator =
4401 MakeLocalSearchOperator<MakeInactiveOperator>(
4402 solver_.get(), nexts_,
4404 vehicle_start_class_callback_);
4405 if (!pickup_delivery_pairs_.empty()) {
4406 make_inactive_operator = solver_->ConcatenateOperators(
4408 solver_.get(), nexts_,
4410 vehicle_start_class_callback_, pickup_delivery_pairs_),
4411 make_inactive_operator});
4413 return make_inactive_operator;
4416 #define CP_ROUTING_ADD_OPERATOR(operator_type, cp_operator_type) \
4417 if (CostsAreHomogeneousAcrossVehicles()) { \
4418 local_search_operators_[operator_type] = \
4419 solver_->MakeOperator(nexts_, Solver::cp_operator_type); \
4421 local_search_operators_[operator_type] = solver_->MakeOperator( \
4422 nexts_, vehicle_vars_, Solver::cp_operator_type); \
4425 #define CP_ROUTING_ADD_OPERATOR2(operator_type, cp_operator_class) \
4426 local_search_operators_[operator_type] = \
4427 MakeLocalSearchOperator<cp_operator_class>( \
4428 solver_.get(), nexts_, \
4429 CostsAreHomogeneousAcrossVehicles() ? std::vector<IntVar*>() \
4431 vehicle_start_class_callback_);
4433 #define CP_ROUTING_ADD_CALLBACK_OPERATOR(operator_type, cp_operator_type) \
4434 if (CostsAreHomogeneousAcrossVehicles()) { \
4435 local_search_operators_[operator_type] = solver_->MakeOperator( \
4437 [this](int64 i, int64 j, int64 k) { \
4438 return GetArcCostForVehicle(i, j, k); \
4440 Solver::cp_operator_type); \
4442 local_search_operators_[operator_type] = solver_->MakeOperator( \
4443 nexts_, vehicle_vars_, \
4444 [this](int64 i, int64 j, int64 k) { \
4445 return GetArcCostForVehicle(i, j, k); \
4447 Solver::cp_operator_type); \
4450 void RoutingModel::CreateNeighborhoodOperators(
4452 local_search_operators_.clear();
4453 local_search_operators_.resize(LOCAL_SEARCH_OPERATOR_COUNTER,
nullptr);
4455 std::vector<IntVar*> empty;
4456 local_search_operators_[RELOCATE_PAIR] = MakePairRelocate(
4457 solver_.get(), nexts_,
4459 vehicle_start_class_callback_, pickup_delivery_pairs_);
4460 local_search_operators_[LIGHT_RELOCATE_PAIR] = MakeLightPairRelocate(
4461 solver_.get(), nexts_,
4463 vehicle_start_class_callback_, pickup_delivery_pairs_);
4464 local_search_operators_[EXCHANGE_PAIR] = MakePairExchange(
4465 solver_.get(), nexts_,
4467 vehicle_start_class_callback_, pickup_delivery_pairs_);
4468 local_search_operators_[EXCHANGE_RELOCATE_PAIR] = MakePairExchangeRelocate(
4469 solver_.get(), nexts_,
4471 vehicle_start_class_callback_, pickup_delivery_pairs_);
4472 local_search_operators_[RELOCATE_NEIGHBORS] = MakeRelocateNeighbors(
4473 solver_.get(), nexts_,
4475 vehicle_start_class_callback_,
4477 local_search_operators_[NODE_PAIR_SWAP] = solver_->ConcatenateOperators(
4478 {IndexPairSwapActive(
4479 solver_.get(), nexts_,
4481 vehicle_start_class_callback_, pickup_delivery_pairs_),
4483 solver_.get(), nexts_,
4485 pickup_delivery_pairs_),
4487 solver_.get(), nexts_,
4489 vehicle_start_class_callback_, pickup_delivery_pairs_)});
4490 const auto arc_cost_for_path_start =
4492 const int vehicle = index_to_vehicle_[start_index];
4493 const int64 arc_cost =
4495 return (before_node != start_index ||
IsEnd(after_node))
4499 GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
4500 ls_gci_parameters = {
4503 parameters.cheapest_insertion_ls_operator_neighbors_ratio(),
4505 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4506 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4507 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4513 GetOrCreateFeasibilityFilterManager(
parameters),
4515 parameters.heuristic_close_nodes_lns_num_nodes()));
4517 local_search_operators_[LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4518 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4519 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4524 GetOrCreateFeasibilityFilterManager(
parameters)),
4525 parameters.heuristic_close_nodes_lns_num_nodes()));
4527 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_PATH_LNS] =
4528 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4529 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4535 GetOrCreateFeasibilityFilterManager(
parameters),
4536 ls_gci_parameters)));
4538 local_search_operators_[LOCAL_CHEAPEST_INSERTION_PATH_LNS] =
4539 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4540 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4545 GetOrCreateFeasibilityFilterManager(
parameters))));
4546 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4547 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4548 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4554 GetOrCreateFeasibilityFilterManager(
parameters),
4556 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4557 arc_cost_for_path_start));
4559 local_search_operators_[LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4560 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4561 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4566 GetOrCreateFeasibilityFilterManager(
parameters)),
4567 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4568 arc_cost_for_path_start));
4569 local_search_operators_[RELOCATE_EXPENSIVE_CHAIN] =
4570 solver_->RevAlloc(
new RelocateExpensiveChain(
4572 vehicle_start_class_callback_,
4573 parameters.relocate_expensive_chain_num_arcs_to_consider(),
4574 arc_cost_for_path_start));
4575 local_search_operators_[RELOCATE_SUBTRIP] = MakeRelocateSubtrip(
4576 solver_.get(), nexts_,
4578 vehicle_start_class_callback_, pickup_delivery_pairs_);
4579 local_search_operators_[EXCHANGE_SUBTRIP] = MakeExchangeSubtrip(
4580 solver_.get(), nexts_,
4582 vehicle_start_class_callback_, pickup_delivery_pairs_);
4589 local_search_operators_[MAKE_ACTIVE] = CreateInsertionOperator();
4591 RelocateAndMakeActiveOperator);
4593 local_search_operators_[MAKE_INACTIVE] = CreateMakeInactiveOperator();
4604 #undef CP_ROUTING_ADD_CALLBACK_OPERATOR
4605 #undef CP_ROUTING_ADD_OPERATOR2
4606 #undef CP_ROUTING_ADD_OPERATOR
4608 #define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators) \
4609 if (search_parameters.local_search_operators().use_##operator_method() == \
4611 operators.push_back(local_search_operators_[operator_type]); \
4614 LocalSearchOperator* RoutingModel::GetNeighborhoodOperators(
4615 const RoutingSearchParameters& search_parameters)
const {
4616 std::vector<LocalSearchOperator*> operator_groups;
4617 std::vector<LocalSearchOperator*> operators = extra_operators_;
4618 if (!pickup_delivery_pairs_.empty()) {
4622 if (search_parameters.local_search_operators().use_relocate_pair() ==
4632 if (vehicles_ > 1) {
4643 if (!pickup_delivery_pairs_.empty() ||
4644 search_parameters.local_search_operators().use_relocate_neighbors() ==
4646 operators.push_back(local_search_operators_[RELOCATE_NEIGHBORS]);
4649 search_parameters.local_search_metaheuristic();
4650 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4651 local_search_metaheuristic !=
4652 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4653 local_search_metaheuristic !=
4654 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4661 if (!disjunctions_.
empty()) {
4678 operator_groups.push_back(solver_->ConcatenateOperators(operators));
4687 global_cheapest_insertion_path_lns, operators);
4689 local_cheapest_insertion_path_lns, operators);
4692 global_cheapest_insertion_expensive_chain_lns,
4695 local_cheapest_insertion_expensive_chain_lns,
4698 global_cheapest_insertion_close_nodes_lns,
4701 local_cheapest_insertion_close_nodes_lns, operators);
4702 operator_groups.push_back(solver_->ConcatenateOperators(operators));
4706 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4707 local_search_metaheuristic !=
4708 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4709 local_search_metaheuristic !=
4710 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4713 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4714 local_search_metaheuristic !=
4715 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4716 local_search_metaheuristic !=
4717 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4722 if (!disjunctions_.
empty()) {
4725 operator_groups.push_back(solver_->ConcatenateOperators(operators));
4727 return solver_->ConcatenateOperators(operator_groups);
4730 #undef CP_ROUTING_PUSH_OPERATOR
4732 std::vector<LocalSearchFilter*> RoutingModel::GetOrCreateLocalSearchFilters(
4743 std::vector<LocalSearchFilter*> filters;
4745 if (vehicle_amortized_cost_factors_set_) {
4752 filters.push_back(solver_->MakeSumObjectiveFilter(
4753 nexts_, [
this](
int64 i,
int64 j) { return GetHomogeneousCost(i, j); },
4756 filters.push_back(solver_->MakeSumObjectiveFilter(
4757 nexts_, vehicle_vars_,
4759 return GetArcCostForVehicle(i, j, k);
4764 filters.push_back(solver_->MakeVariableDomainFilter());
4766 if (vehicles_ > max_active_vehicles_) {
4770 if (!disjunctions_.
empty()) {
4774 if (!pickup_delivery_pairs_.empty()) {
4776 *
this, pickup_delivery_pairs_, vehicle_pickup_delivery_policy_));
4789 if (!dimension->HasBreakConstraints())
continue;
4792 filters.insert(filters.end(), extra_filters_.begin(), extra_filters_.end());
4796 LocalSearchFilterManager* RoutingModel::GetOrCreateLocalSearchFilterManager(
4798 if (!local_search_filter_manager_) {
4799 local_search_filter_manager_ = solver_->MakeLocalSearchFilterManager(
4802 return local_search_filter_manager_;
4805 std::vector<LocalSearchFilter*> RoutingModel::GetOrCreateFeasibilityFilters(
4807 std::vector<LocalSearchFilter*> filters;
4808 if (vehicles_ > max_active_vehicles_) {
4811 if (!disjunctions_.
empty()) {
4814 filters.push_back(solver_->MakeVariableDomainFilter());
4815 if (!pickup_delivery_pairs_.empty()) {
4817 *
this, pickup_delivery_pairs_, vehicle_pickup_delivery_policy_));
4828 if (dimension->HasBreakConstraints()) {
4829 IntVarLocalSearchFilter* breaks_filter =
4831 filters.push_back(breaks_filter);
4835 filters.insert(filters.end(), extra_filters_.begin(), extra_filters_.end());
4839 LocalSearchFilterManager* RoutingModel::GetOrCreateFeasibilityFilterManager(
4841 if (!feasibility_filter_manager_) {
4842 feasibility_filter_manager_ = solver_->MakeLocalSearchFilterManager(
4845 return feasibility_filter_manager_;
4848 LocalSearchFilterManager*
4849 RoutingModel::GetOrCreateStrongFeasibilityFilterManager(
4851 if (!strong_feasibility_filter_manager_) {
4852 std::vector<LocalSearchFilter*> filters =
4855 strong_feasibility_filter_manager_ =
4856 solver_->MakeLocalSearchFilterManager(std::move(filters));
4858 return strong_feasibility_filter_manager_;
4862 bool AllTransitsPositive(
const RoutingDimension& dimension) {
4863 for (
int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
4864 if (!dimension.AreVehicleTransitsPositive(vehicle)) {
4872 void RoutingModel::StoreDimensionCumulOptimizers(
4874 Assignment* packed_dimensions_collector_assignment =
4875 solver_->MakeAssignment();
4876 const int num_dimensions = dimensions_.size();
4877 local_optimizer_index_.
resize(num_dimensions, -1);
4878 global_optimizer_index_.
resize(num_dimensions, -1);
4881 if (dimension->global_span_cost_coefficient() > 0 ||
4882 !dimension->GetNodePrecedences().empty()) {
4884 global_optimizer_index_[dim] = global_dimension_optimizers_.size();
4885 global_dimension_optimizers_.push_back(
4886 absl::make_unique<GlobalDimensionCumulOptimizer>(dimension));
4887 packed_dimensions_collector_assignment->Add(dimension->cumuls());
4888 if (!AllTransitsPositive(*dimension)) {
4889 dimension->SetOffsetForGlobalOptimizer(0);
4893 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4894 DCHECK_GE(dimension->CumulVar(
Start(vehicle))->Min(), 0);
4896 std::min(offset, dimension->CumulVar(
Start(vehicle))->Min() - 1);
4898 dimension->SetOffsetForGlobalOptimizer(
std::max(Zero(), offset));
4900 bool has_span_cost =
false;
4901 bool has_span_limit =
false;
4902 std::vector<int64> vehicle_offsets(
vehicles());
4903 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4904 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4905 has_span_cost =
true;
4907 if (dimension->GetSpanUpperBoundForVehicle(vehicle) <
kint64max) {
4908 has_span_limit =
true;
4910 DCHECK_GE(dimension->CumulVar(
Start(vehicle))->Min(), 0);
4911 vehicle_offsets[vehicle] =
4912 dimension->AreVehicleTransitsPositive(vehicle)
4914 dimension->CumulVar(
Start(vehicle))->Min() - 1)
4917 bool has_soft_lower_bound =
false;
4918 bool has_soft_upper_bound =
false;
4919 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
4920 if (dimension->HasCumulVarSoftLowerBound(i)) {
4921 has_soft_lower_bound =
true;
4923 if (dimension->HasCumulVarSoftUpperBound(i)) {
4924 has_soft_upper_bound =
true;
4927 int num_linear_constraints = 0;
4928 if (has_span_cost) ++num_linear_constraints;
4929 if (has_span_limit) ++num_linear_constraints;
4930 if (dimension->HasSoftSpanUpperBounds()) ++num_linear_constraints;
4931 if (has_soft_lower_bound) ++num_linear_constraints;
4932 if (has_soft_upper_bound) ++num_linear_constraints;
4933 if (dimension->HasBreakConstraints()) ++num_linear_constraints;
4934 if (num_linear_constraints >= 2) {
4935 dimension->SetVehicleOffsetsForLocalOptimizer(
4936 std::move(vehicle_offsets));
4937 local_optimizer_index_[dim] = local_dimension_optimizers_.size();
4938 local_dimension_optimizers_.push_back(
4939 absl::make_unique<LocalDimensionCumulOptimizer>(
4940 dimension,
parameters.continuous_scheduling_solver()));
4941 bool has_intervals =
false;
4942 for (
const SortedDisjointIntervalList& intervals :
4943 dimension->forbidden_intervals()) {
4946 if (intervals.NumIntervals() > 0) {
4947 has_intervals =
true;
4951 if (dimension->HasBreakConstraints() || has_intervals) {
4952 local_dimension_mp_optimizers_.push_back(
4953 absl::make_unique<LocalDimensionCumulOptimizer>(
4954 dimension,
parameters.mixed_integer_scheduling_solver()));
4956 local_dimension_mp_optimizers_.push_back(
nullptr);
4958 packed_dimensions_collector_assignment->Add(dimension->cumuls());
4961 DCHECK_EQ(local_dimension_mp_optimizers_.size(),
4962 local_dimension_optimizers_.size());
4968 for (IntVar*
const extra_var : extra_vars_) {
4969 packed_dimensions_collector_assignment->Add(extra_var);
4971 for (IntervalVar*
const extra_interval : extra_intervals_) {
4972 packed_dimensions_collector_assignment->Add(extra_interval);
4975 packed_dimensions_assignment_collector_ = solver_->MakeFirstSolutionCollector(
4976 packed_dimensions_collector_assignment);
4981 std::vector<RoutingDimension*> dimensions;
4983 bool has_soft_or_span_cost =
false;
4984 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4985 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4986 has_soft_or_span_cost =
true;
4990 if (!has_soft_or_span_cost) {
4991 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
4992 if (dimension->HasCumulVarSoftUpperBound(i) ||
4993 dimension->HasCumulVarSoftLowerBound(i)) {
4994 has_soft_or_span_cost =
true;
4999 if (has_soft_or_span_cost) dimensions.push_back(dimension);
5005 RoutingModel::CreateFinalizerForMinimizedAndMaximizedVariables() {
5006 std::stable_sort(finalizer_variable_cost_pairs_.begin(),
5007 finalizer_variable_cost_pairs_.end(),
5008 [](
const std::pair<IntVar*, int64>& var_cost1,
5009 const std::pair<IntVar*, int64>& var_cost2) {
5010 return var_cost1.second > var_cost2.second;
5012 const int num_variables = finalizer_variable_cost_pairs_.size() +
5013 finalizer_variable_target_pairs_.size();
5014 std::vector<IntVar*> variables;
5015 std::vector<int64> targets;
5016 variables.reserve(num_variables);
5017 targets.reserve(num_variables);
5018 for (
const auto& variable_cost : finalizer_variable_cost_pairs_) {
5019 variables.push_back(variable_cost.first);
5022 for (
const auto& variable_target : finalizer_variable_target_pairs_) {
5023 variables.push_back(variable_target.first);
5024 targets.push_back(variable_target.second);
5027 std::move(targets));
5030 DecisionBuilder* RoutingModel::CreateSolutionFinalizer(SearchLimit* lns_limit) {
5031 std::vector<DecisionBuilder*> decision_builders;
5032 decision_builders.push_back(solver_->MakePhase(
5033 nexts_, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE));
5035 if (!local_dimension_optimizers_.empty()) {
5036 decision_builders.push_back(
5037 solver_->RevAlloc(
new SetCumulsFromLocalDimensionCosts(
5038 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
5041 if (!global_dimension_optimizers_.empty()) {
5042 decision_builders.push_back(
5043 solver_->RevAlloc(
new SetCumulsFromGlobalDimensionCosts(
5044 &global_dimension_optimizers_, lns_limit)));
5046 decision_builders.push_back(
5047 CreateFinalizerForMinimizedAndMaximizedVariables());
5049 return solver_->Compose(decision_builders);
5052 void RoutingModel::CreateFirstSolutionDecisionBuilders(
5053 const RoutingSearchParameters& search_parameters) {
5054 first_solution_decision_builders_.resize(
5056 first_solution_filtered_decision_builders_.resize(
5058 DecisionBuilder*
const finalize_solution =
5059 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit());
5061 first_solution_decision_builders_
5062 [FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE] = finalize_solution;
5064 first_solution_decision_builders_
5065 [FirstSolutionStrategy::GLOBAL_CHEAPEST_ARC] = solver_->MakePhase(
5068 Solver::CHOOSE_STATIC_GLOBAL_BEST);
5070 Solver::IndexEvaluator2 eval = [
this](
int64 i,
int64 j) {
5073 first_solution_decision_builders_[FirstSolutionStrategy::LOCAL_CHEAPEST_ARC] =
5074 solver_->MakePhase(nexts_, Solver::CHOOSE_FIRST_UNBOUND, eval);
5076 first_solution_decision_builders_[FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5077 solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, eval);
5078 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5079 first_solution_filtered_decision_builders_
5080 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5081 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5082 absl::make_unique<EvaluatorCheapestAdditionFilteredHeuristic>(
5087 GetOrCreateFeasibilityFilterManager(search_parameters))));
5088 first_solution_decision_builders_
5089 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5090 solver_->Try(first_solution_filtered_decision_builders_
5091 [FirstSolutionStrategy::PATH_CHEAPEST_ARC],
5092 first_solution_decision_builders_
5093 [FirstSolutionStrategy::PATH_CHEAPEST_ARC]);
5096 Solver::VariableValueComparator comp = [
this](
int64 i,
int64 j,
int64 k) {
5100 first_solution_decision_builders_
5101 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5102 solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, comp);
5103 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5104 first_solution_filtered_decision_builders_
5105 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5106 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5107 absl::make_unique<ComparatorCheapestAdditionFilteredHeuristic>(
5109 GetOrCreateFeasibilityFilterManager(search_parameters))));
5110 first_solution_decision_builders_
5111 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->Try(
5112 first_solution_filtered_decision_builders_
5113 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC],
5114 first_solution_decision_builders_
5115 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC]);
5118 if (first_solution_evaluator_ !=
nullptr) {
5119 first_solution_decision_builders_
5120 [FirstSolutionStrategy::EVALUATOR_STRATEGY] = solver_->MakePhase(
5121 nexts_, Solver::CHOOSE_PATH, first_solution_evaluator_);
5123 first_solution_decision_builders_
5124 [FirstSolutionStrategy::EVALUATOR_STRATEGY] =
nullptr;
5127 first_solution_decision_builders_[FirstSolutionStrategy::ALL_UNPERFORMED] =
5128 solver_->RevAlloc(
new AllUnperformed(
this));
5130 RegularLimit*
const ls_limit =
5133 DecisionBuilder*
const finalize = solver_->MakeSolveOnce(
5134 finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit());
5135 LocalSearchPhaseParameters*
const insertion_parameters =
5136 solver_->MakeLocalSearchPhaseParameters(
5137 nullptr, CreateInsertionOperator(), finalize, ls_limit,
5138 GetOrCreateLocalSearchFilterManager(search_parameters));
5139 std::vector<IntVar*> decision_vars = nexts_;
5141 decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(),
5142 vehicle_vars_.end());
5146 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5147 solver_->MakeNestedOptimize(
5148 solver_->MakeLocalSearchPhase(
5149 decision_vars, solver_->RevAlloc(
new AllUnperformed(
this)),
5150 insertion_parameters),
5151 GetOrCreateAssignment(),
false, optimization_step);
5152 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5153 solver_->Compose(first_solution_decision_builders_
5154 [FirstSolutionStrategy::BEST_INSERTION],
5158 GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
5161 search_parameters.cheapest_insertion_farthest_seeds_ratio(),
5162 search_parameters.cheapest_insertion_first_solution_neighbors_ratio(),
5164 for (
bool is_sequential : {
false,
true}) {
5166 is_sequential ? FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION
5167 : FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5168 gci_parameters.is_sequential = is_sequential;
5170 first_solution_filtered_decision_builders_[first_solution_strategy] =
5171 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5172 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5178 GetOrCreateFeasibilityFilterManager(search_parameters),
5180 IntVarFilteredDecisionBuilder*
const strong_gci =
5181 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5182 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5188 GetOrCreateStrongFeasibilityFilterManager(search_parameters),
5190 first_solution_decision_builders_[first_solution_strategy] = solver_->Try(
5191 first_solution_filtered_decision_builders_[first_solution_strategy],
5192 solver_->Try(strong_gci, first_solution_decision_builders_
5193 [FirstSolutionStrategy::BEST_INSERTION]));
5197 first_solution_filtered_decision_builders_
5198 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] =
5199 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5200 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5205 GetOrCreateFeasibilityFilterManager(search_parameters))));
5206 IntVarFilteredDecisionBuilder*
const strong_lci =
5207 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5208 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5213 GetOrCreateStrongFeasibilityFilterManager(search_parameters))));
5214 first_solution_decision_builders_
5215 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = solver_->Try(
5216 first_solution_filtered_decision_builders_
5217 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION],
5218 solver_->Try(strong_lci,
5219 first_solution_decision_builders_
5220 [FirstSolutionStrategy::BEST_INSERTION]));
5222 SavingsFilteredHeuristic::SavingsParameters savings_parameters;
5223 savings_parameters.neighbors_ratio =
5224 search_parameters.savings_neighbors_ratio();
5225 savings_parameters.max_memory_usage_bytes =
5226 search_parameters.savings_max_memory_usage_bytes();
5227 savings_parameters.add_reverse_arcs =
5228 search_parameters.savings_add_reverse_arcs();
5229 savings_parameters.arc_coefficient =
5230 search_parameters.savings_arc_coefficient();
5231 LocalSearchFilterManager* filter_manager =
nullptr;
5232 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5233 filter_manager = GetOrCreateFeasibilityFilterManager(search_parameters);
5236 if (search_parameters.savings_parallel_routes()) {
5237 IntVarFilteredDecisionBuilder* savings_db =
5238 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5239 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5240 this, &manager_, savings_parameters, filter_manager)));
5241 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5242 first_solution_filtered_decision_builders_
5243 [FirstSolutionStrategy::SAVINGS] = savings_db;
5246 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5247 solver_->Try(savings_db,
5248 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5249 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5250 this, &manager_, savings_parameters,
5251 GetOrCreateStrongFeasibilityFilterManager(
5252 search_parameters)))));
5254 IntVarFilteredDecisionBuilder* savings_db =
5255 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5256 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5257 this, &manager_, savings_parameters, filter_manager)));
5258 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5259 first_solution_filtered_decision_builders_
5260 [FirstSolutionStrategy::SAVINGS] = savings_db;
5263 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5264 solver_->Try(savings_db,
5265 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5266 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5267 this, &manager_, savings_parameters,
5268 GetOrCreateStrongFeasibilityFilterManager(
5269 search_parameters)))));
5272 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5273 solver_->RevAlloc(
new SweepBuilder(
this,
true));
5274 DecisionBuilder* sweep_builder =
5275 solver_->RevAlloc(
new SweepBuilder(
this,
false));
5276 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5279 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP]);
5281 first_solution_decision_builders_[FirstSolutionStrategy::CHRISTOFIDES] =
5282 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5283 absl::make_unique<ChristofidesFilteredHeuristic>(
5284 this, GetOrCreateFeasibilityFilterManager(search_parameters),
5285 search_parameters.christofides_use_minimum_matching())));
5288 const bool has_precedences = std::any_of(
5289 dimensions_.begin(), dimensions_.end(),
5291 if (pickup_delivery_pairs_.empty() && !has_precedences) {
5292 automatic_first_solution_strategy_ =
5293 FirstSolutionStrategy::PATH_CHEAPEST_ARC;
5295 automatic_first_solution_strategy_ =
5296 FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5298 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC] =
5299 first_solution_decision_builders_[automatic_first_solution_strategy_];
5300 first_solution_decision_builders_[FirstSolutionStrategy::UNSET] =
5301 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC];
5304 DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder(
5305 const RoutingSearchParameters& search_parameters)
const {
5307 search_parameters.first_solution_strategy();
5308 if (first_solution_strategy < first_solution_decision_builders_.size()) {
5309 return first_solution_decision_builders_[first_solution_strategy];
5315 IntVarFilteredDecisionBuilder*
5316 RoutingModel::GetFilteredFirstSolutionDecisionBuilderOrNull(
5317 const RoutingSearchParameters& search_parameters)
const {
5319 search_parameters.first_solution_strategy();
5320 return first_solution_filtered_decision_builders_[first_solution_strategy];
5323 LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters(
5324 const RoutingSearchParameters& search_parameters) {
5325 SearchLimit* lns_limit = GetOrCreateLargeNeighborhoodSearchLimit();
5326 return solver_->MakeLocalSearchPhaseParameters(
5327 CostVar(), GetNeighborhoodOperators(search_parameters),
5328 solver_->MakeSolveOnce(CreateSolutionFinalizer(lns_limit), lns_limit),
5329 GetOrCreateLocalSearchLimit(),
5330 GetOrCreateLocalSearchFilterManager(search_parameters));
5333 DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder(
5334 const RoutingSearchParameters& search_parameters) {
5335 const int size =
Size();
5336 DecisionBuilder* first_solution =
5337 GetFirstSolutionDecisionBuilder(search_parameters);
5338 LocalSearchPhaseParameters*
const parameters =
5339 CreateLocalSearchParameters(search_parameters);
5340 SearchLimit* first_solution_lns_limit =
5341 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5342 DecisionBuilder*
const first_solution_sub_decision_builder =
5343 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_solution_lns_limit),
5344 first_solution_lns_limit);
5346 return solver_->MakeLocalSearchPhase(nexts_, first_solution,
5347 first_solution_sub_decision_builder,
5350 const int all_size = size + size + vehicles_;
5351 std::vector<IntVar*> all_vars(all_size);
5352 for (
int i = 0; i < size; ++i) {
5353 all_vars[i] = nexts_[i];
5355 for (
int i = size; i < all_size; ++i) {
5356 all_vars[i] = vehicle_vars_[i - size];
5358 return solver_->MakeLocalSearchPhase(all_vars, first_solution,
5359 first_solution_sub_decision_builder,
5364 void RoutingModel::SetupDecisionBuilders(
5365 const RoutingSearchParameters& search_parameters) {
5366 if (search_parameters.use_depth_first_search()) {
5367 SearchLimit* first_lns_limit =
5368 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5369 solve_db_ = solver_->Compose(
5370 GetFirstSolutionDecisionBuilder(search_parameters),
5371 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_lns_limit),
5374 solve_db_ = CreateLocalSearchDecisionBuilder(search_parameters);
5376 CHECK(preassignment_ !=
nullptr);
5377 DecisionBuilder* restore_preassignment =
5378 solver_->MakeRestoreAssignment(preassignment_);
5379 solve_db_ = solver_->Compose(restore_preassignment, solve_db_);
5381 solver_->Compose(restore_preassignment,
5382 solver_->MakeLocalSearchPhase(
5383 GetOrCreateAssignment(),
5384 CreateLocalSearchParameters(search_parameters)));
5385 restore_assignment_ = solver_->Compose(
5386 solver_->MakeRestoreAssignment(GetOrCreateAssignment()),
5387 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5388 restore_tmp_assignment_ = solver_->Compose(
5389 restore_preassignment,
5390 solver_->MakeRestoreAssignment(GetOrCreateTmpAssignment()),
5391 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5394 void RoutingModel::SetupMetaheuristics(
5395 const RoutingSearchParameters& search_parameters) {
5396 SearchMonitor* optimize;
5398 search_parameters.local_search_metaheuristic();
5401 bool limit_too_long = !search_parameters.has_time_limit() &&
5402 search_parameters.solution_limit() ==
kint64max;
5405 switch (metaheuristic) {
5406 case LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH:
5408 optimize = solver_->MakeGuidedLocalSearch(
5411 optimization_step, nexts_,
5412 search_parameters.guided_local_search_lambda_coefficient());
5414 optimize = solver_->MakeGuidedLocalSearch(
5419 optimization_step, nexts_, vehicle_vars_,
5420 search_parameters.guided_local_search_lambda_coefficient());
5423 case LocalSearchMetaheuristic::SIMULATED_ANNEALING:
5425 solver_->MakeSimulatedAnnealing(
false, cost_, optimization_step, 100);
5427 case LocalSearchMetaheuristic::TABU_SEARCH:
5428 optimize = solver_->MakeTabuSearch(
false, cost_, optimization_step,
5429 nexts_, 10, 10, .8);
5431 case LocalSearchMetaheuristic::GENERIC_TABU_SEARCH: {
5432 std::vector<operations_research::IntVar*> tabu_vars;
5433 if (tabu_var_callback_) {
5434 tabu_vars = tabu_var_callback_(
this);
5436 tabu_vars.push_back(cost_);
5438 optimize = solver_->MakeGenericTabuSearch(
false, cost_, optimization_step,
5443 limit_too_long =
false;
5444 optimize = solver_->MakeMinimize(cost_, optimization_step);
5446 if (limit_too_long) {
5447 LOG(WARNING) << LocalSearchMetaheuristic::Value_Name(metaheuristic)
5448 <<
" specified without sane timeout: solve may run forever.";
5450 monitors_.push_back(optimize);
5454 tabu_var_callback_ = std::move(tabu_var_callback);
5457 void RoutingModel::SetupAssignmentCollector(
5458 const RoutingSearchParameters& search_parameters) {
5459 Assignment* full_assignment = solver_->MakeAssignment();
5461 full_assignment->Add(dimension->cumuls());
5463 for (IntVar*
const extra_var : extra_vars_) {
5464 full_assignment->Add(extra_var);
5466 for (IntervalVar*
const extra_interval : extra_intervals_) {
5467 full_assignment->Add(extra_interval);
5469 full_assignment->Add(nexts_);
5470 full_assignment->Add(active_);
5471 full_assignment->Add(vehicle_vars_);
5472 full_assignment->AddObjective(cost_);
5474 collect_assignments_ = solver_->MakeNBestValueSolutionCollector(
5475 full_assignment, search_parameters.number_of_solutions_to_collect(),
5477 collect_one_assignment_ =
5478 solver_->MakeFirstSolutionCollector(full_assignment);
5479 monitors_.push_back(collect_assignments_);
5482 void RoutingModel::SetupTrace(
5483 const RoutingSearchParameters& search_parameters) {
5484 if (search_parameters.log_search()) {
5485 Solver::SearchLogParameters search_log_parameters;
5486 search_log_parameters.branch_period = 10000;
5487 search_log_parameters.objective =
nullptr;
5488 search_log_parameters.variable = cost_;
5489 search_log_parameters.scaling_factor =
5490 search_parameters.log_cost_scaling_factor();
5491 search_log_parameters.offset = search_parameters.log_cost_offset();
5492 if (!search_parameters.log_tag().empty()) {
5493 const std::string tag = search_parameters.log_tag();
5494 search_log_parameters.display_callback = [tag]() {
return tag; };
5496 search_log_parameters.display_callback =
nullptr;
5498 monitors_.push_back(solver_->MakeSearchLog(search_log_parameters));
5502 void RoutingModel::SetupSearchMonitors(
5503 const RoutingSearchParameters& search_parameters) {
5504 monitors_.push_back(GetOrCreateLimit());
5505 SetupMetaheuristics(search_parameters);
5506 SetupAssignmentCollector(search_parameters);
5507 SetupTrace(search_parameters);
5510 bool RoutingModel::UsesLightPropagation(
5511 const RoutingSearchParameters& search_parameters)
const {
5512 return !search_parameters.use_full_propagation() &&
5513 !search_parameters.use_depth_first_search() &&
5514 search_parameters.first_solution_strategy() !=
5515 FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE;
5520 CHECK(
var !=
nullptr);
5522 finalizer_variable_cost_pairs_.size());
5523 if (
index < finalizer_variable_cost_pairs_.size()) {
5524 const int64 old_cost = finalizer_variable_cost_pairs_[
index].second;
5525 finalizer_variable_cost_pairs_[
index].second =
CapAdd(old_cost,
cost);
5527 finalizer_variable_cost_pairs_.emplace_back(
var,
cost);
5532 CHECK(
var !=
nullptr);
5533 if (finalizer_variable_target_set_.contains(
var))
return;
5534 finalizer_variable_target_set_.insert(
var);
5535 finalizer_variable_target_pairs_.emplace_back(
var, target);
5546 void RoutingModel::SetupSearch(
5547 const RoutingSearchParameters& search_parameters) {
5548 SetupDecisionBuilders(search_parameters);
5549 SetupSearchMonitors(search_parameters);
5553 extra_vars_.push_back(
var);
5557 extra_intervals_.push_back(
interval);
5562 class PathSpansAndTotalSlacks :
public Constraint {
5566 std::vector<IntVar*> spans,
5567 std::vector<IntVar*> total_slacks)
5568 : Constraint(
model->solver()),
5570 dimension_(dimension),
5571 spans_(std::move(spans)),
5572 total_slacks_(std::move(total_slacks)) {
5573 CHECK_EQ(spans_.size(), model_->vehicles());
5574 CHECK_EQ(total_slacks_.size(), model_->vehicles());
5575 vehicle_demons_.resize(model_->vehicles());
5578 std::string DebugString()
const override {
return "PathSpansAndTotalSlacks"; }
5580 void Post()
override {
5581 const int num_nodes = model_->VehicleVars().size();
5582 const int num_transits = model_->Nexts().size();
5583 for (
int node = 0; node < num_nodes; ++node) {
5585 model_->solver(),
this, &PathSpansAndTotalSlacks::PropagateNode,
5586 "PathSpansAndTotalSlacks::PropagateNode", node);
5587 dimension_->CumulVar(node)->WhenRange(demon);
5588 model_->VehicleVar(node)->WhenBound(demon);
5589 if (node < num_transits) {
5590 dimension_->TransitVar(node)->WhenRange(demon);
5591 dimension_->FixedTransitVar(node)->WhenBound(demon);
5592 model_->NextVar(node)->WhenBound(demon);
5595 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5596 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5598 solver(),
this, &PathSpansAndTotalSlacks::PropagateVehicle,
5599 "PathSpansAndTotalSlacks::PropagateVehicle", vehicle);
5600 vehicle_demons_[vehicle] = demon;
5601 if (spans_[vehicle]) spans_[vehicle]->WhenRange(demon);
5602 if (total_slacks_[vehicle]) total_slacks_[vehicle]->WhenRange(demon);
5603 if (dimension_->HasBreakConstraints()) {
5604 for (IntervalVar*
b : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5605 b->WhenAnything(demon);
5612 void InitialPropagate()
override {
5613 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5614 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5615 PropagateVehicle(vehicle);
5623 void PropagateNode(
int node) {
5624 if (!model_->VehicleVar(node)->Bound())
return;
5625 const int vehicle = model_->VehicleVar(node)->Min();
5626 if (vehicle < 0 || vehicle_demons_[vehicle] ==
nullptr)
return;
5627 EnqueueDelayedDemon(vehicle_demons_[vehicle]);
5635 int64 SpanMin(
int vehicle,
int64 sum_fixed_transits) {
5636 DCHECK_GE(sum_fixed_transits, 0);
5637 const int64 span_min = spans_[vehicle] ? spans_[vehicle]->Min() :
kint64max;
5638 const int64 total_slack_min =
5639 total_slacks_[vehicle] ? total_slacks_[vehicle]->Min() :
kint64max;
5640 return std::min(span_min,
CapAdd(total_slack_min, sum_fixed_transits));
5642 int64 SpanMax(
int vehicle,
int64 sum_fixed_transits) {
5643 DCHECK_GE(sum_fixed_transits, 0);
5644 const int64 span_max = spans_[vehicle] ? spans_[vehicle]->Max() :
kint64min;
5645 const int64 total_slack_max =
5646 total_slacks_[vehicle] ? total_slacks_[vehicle]->Max() :
kint64min;
5647 return std::max(span_max,
CapAdd(total_slack_max, sum_fixed_transits));
5649 void SetSpanMin(
int vehicle,
int64 min,
int64 sum_fixed_transits) {
5650 DCHECK_GE(sum_fixed_transits, 0);
5651 if (spans_[vehicle]) {
5652 spans_[vehicle]->SetMin(
min);
5654 if (total_slacks_[vehicle]) {
5655 total_slacks_[vehicle]->SetMin(
CapSub(
min, sum_fixed_transits));
5658 void SetSpanMax(
int vehicle,
int64 max,
int64 sum_fixed_transits) {
5659 DCHECK_GE(sum_fixed_transits, 0);
5660 if (spans_[vehicle]) {
5661 spans_[vehicle]->SetMax(
max);
5663 if (total_slacks_[vehicle]) {
5664 total_slacks_[vehicle]->SetMax(
CapSub(
max, sum_fixed_transits));
5669 void SynchronizeSpanAndTotalSlack(
int vehicle,
int64 sum_fixed_transits) {
5670 DCHECK_GE(sum_fixed_transits, 0);
5671 IntVar* span = spans_[vehicle];
5672 IntVar* total_slack = total_slacks_[vehicle];
5673 if (!span || !total_slack)
return;
5674 span->SetMin(
CapAdd(total_slack->Min(), sum_fixed_transits));
5675 span->SetMax(
CapAdd(total_slack->Max(), sum_fixed_transits));
5676 total_slack->SetMin(
CapSub(span->Min(), sum_fixed_transits));
5677 total_slack->SetMax(
CapSub(span->Max(), sum_fixed_transits));
5680 void PropagateVehicle(
int vehicle) {
5681 DCHECK(spans_[vehicle] || total_slacks_[vehicle]);
5682 const int start = model_->Start(vehicle);
5683 const int end = model_->End(vehicle);
5689 int curr_node = start;
5690 while (!model_->IsEnd(curr_node)) {
5691 const IntVar* next_var = model_->NextVar(curr_node);
5692 if (!next_var->Bound())
return;
5693 path_.push_back(curr_node);
5694 curr_node = next_var->Value();
5699 int64 sum_fixed_transits = 0;
5700 for (
const int node : path_) {
5701 const IntVar* fixed_transit_var = dimension_->FixedTransitVar(node);
5702 if (!fixed_transit_var->Bound())
return;
5703 sum_fixed_transits =
5704 CapAdd(sum_fixed_transits, fixed_transit_var->Value());
5707 SynchronizeSpanAndTotalSlack(vehicle, sum_fixed_transits);
5714 if (dimension_->HasBreakConstraints() &&
5715 !dimension_->GetBreakIntervalsOfVehicle(vehicle).empty()) {
5716 const int64 vehicle_start_max = dimension_->CumulVar(start)->Max();
5717 const int64 vehicle_end_min = dimension_->CumulVar(end)->Min();
5719 int64 min_break_duration = 0;
5720 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5721 if (!br->MustBePerformed())
continue;
5722 if (vehicle_start_max < br->EndMin() &&
5723 br->StartMax() < vehicle_end_min) {
5724 min_break_duration =
CapAdd(min_break_duration, br->DurationMin());
5727 SetSpanMin(vehicle,
CapAdd(min_break_duration, sum_fixed_transits),
5728 sum_fixed_transits);
5734 const int64 slack_max =
5735 CapSub(SpanMax(vehicle, sum_fixed_transits), sum_fixed_transits);
5736 const int64 max_additional_slack =
CapSub(slack_max, min_break_duration);
5737 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5738 if (!br->MustBePerformed())
continue;
5740 if (vehicle_start_max >= br->EndMin() &&
5741 br->StartMax() < vehicle_end_min) {
5742 if (br->DurationMin() > max_additional_slack) {
5745 br->SetEndMax(vehicle_start_max);
5746 dimension_->CumulVar(start)->SetMin(br->EndMin());
5751 if (vehicle_start_max < br->EndMin() &&
5752 br->StartMax() >= vehicle_end_min) {
5753 if (br->DurationMin() > max_additional_slack) {
5754 br->SetStartMin(vehicle_end_min);
5755 dimension_->CumulVar(end)->SetMax(br->StartMax());
5763 IntVar* start_cumul = dimension_->CumulVar(start);
5764 IntVar* end_cumul = dimension_->CumulVar(end);
5771 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5773 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5775 const int64 span_min = SpanMin(vehicle, sum_fixed_transits);
5776 const int64 span_max = SpanMax(vehicle, sum_fixed_transits);
5777 const int64 slack_from_lb =
CapSub(span_max, span_lb);
5778 const int64 slack_from_ub =
CapSub(span_ub, span_min);
5794 for (
const int node : path_) {
5795 span_lb =
CapAdd(span_lb, dimension_->TransitVar(node)->Min());
5796 span_ub =
CapAdd(span_ub, dimension_->TransitVar(node)->Max());
5798 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5799 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5803 const int64 span_min = SpanMin(vehicle, sum_fixed_transits);
5804 const int64 span_max = SpanMax(vehicle, sum_fixed_transits);
5805 const int64 slack_from_lb =
CapSub(span_max, span_lb);
5806 const int64 slack_from_ub =
5808 for (
const int node : path_) {
5809 IntVar* transit_var = dimension_->TransitVar(node);
5810 const int64 transit_i_min = transit_var->Min();
5811 const int64 transit_i_max = transit_var->Max();
5815 transit_var->SetMax(
CapAdd(transit_i_min, slack_from_lb));
5816 transit_var->SetMin(
CapSub(transit_i_max, slack_from_ub));
5821 path_.push_back(end);
5832 int64 arrival_time = dimension_->CumulVar(start)->Min();
5833 for (
int i = 1; i < path_.size(); ++i) {
5836 dimension_->FixedTransitVar(path_[i - 1])->Min()),
5837 dimension_->CumulVar(path_[i])->Min());
5839 int64 departure_time = arrival_time;
5840 for (
int i = path_.size() - 2; i >= 0; --i) {
5843 dimension_->FixedTransitVar(path_[i])->Min()),
5844 dimension_->CumulVar(path_[i])->Max());
5846 const int64 span_lb =
CapSub(arrival_time, departure_time);
5847 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5848 const int64 maximum_deviation =
5849 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5850 const int64 start_lb =
CapSub(departure_time, maximum_deviation);
5851 dimension_->CumulVar(start)->SetMin(start_lb);
5855 int64 departure_time = dimension_->CumulVar(end)->Max();
5856 for (
int i = path_.size() - 2; i >= 0; --i) {
5857 const int curr_node = path_[i];
5860 dimension_->FixedTransitVar(curr_node)->Min()),
5861 dimension_->CumulVar(curr_node)->Max());
5863 int arrival_time = departure_time;
5864 for (
int i = 1; i < path_.size(); ++i) {
5867 dimension_->FixedTransitVar(path_[i - 1])->Min()),
5868 dimension_->CumulVar(path_[i])->Min());
5870 const int64 span_lb =
CapSub(arrival_time, departure_time);
5871 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5872 const int64 maximum_deviation =
5873 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5874 dimension_->CumulVar(end)->SetMax(
5875 CapAdd(arrival_time, maximum_deviation));
5879 const RoutingModel*
const model_;
5880 const RoutingDimension*
const dimension_;
5881 std::vector<IntVar*> spans_;
5882 std::vector<IntVar*> total_slacks_;
5883 std::vector<int> path_;
5884 std::vector<Demon*> vehicle_demons_;
5891 std::vector<IntVar*> total_slacks) {
5892 CHECK_EQ(vehicles_, spans.size());
5893 CHECK_EQ(vehicles_, total_slacks.size());
5894 return solver()->RevAlloc(
5895 new PathSpansAndTotalSlacks(
this, dimension, spans, total_slacks));
5903 std::vector<int64> vehicle_capacities,
5904 const std::string&
name,
5906 : vehicle_capacities_(std::move(vehicle_capacities)),
5907 base_dimension_(base_dimension),
5908 global_span_cost_coefficient_(0),
5911 global_optimizer_offset_(0) {
5912 CHECK(
model !=
nullptr);
5914 vehicle_span_cost_coefficients_.assign(
model->vehicles(), 0);
5917 RoutingDimension::RoutingDimension(RoutingModel*
model,
5918 std::vector<int64> vehicle_capacities,
5919 const std::string&
name, SelfBased)
5920 : RoutingDimension(
model, std::move(vehicle_capacities),
name, this) {}
5923 cumul_var_piecewise_linear_cost_.clear();
5926 void RoutingDimension::Initialize(
5927 const std::vector<int>& transit_evaluators,
5928 const std::vector<int>& state_dependent_transit_evaluators,
5931 InitializeTransits(transit_evaluators, state_dependent_transit_evaluators,
5945 class LightRangeLessOrEqual :
public Constraint {
5947 LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l, IntExpr*
const r);
5948 ~LightRangeLessOrEqual()
override {}
5949 void Post()
override;
5950 void InitialPropagate()
override;
5951 std::string DebugString()
const override;
5952 IntVar* Var()
override {
5953 return solver()->MakeIsLessOrEqualVar(left_, right_);
5956 void Accept(ModelVisitor*
const visitor)
const override {
5957 visitor->BeginVisitConstraint(ModelVisitor::kLessOrEqual,
this);
5958 visitor->VisitIntegerExpressionArgument(ModelVisitor::kLeftArgument, left_);
5959 visitor->VisitIntegerExpressionArgument(ModelVisitor::kRightArgument,
5961 visitor->EndVisitConstraint(ModelVisitor::kLessOrEqual,
this);
5967 IntExpr*
const left_;
5968 IntExpr*
const right_;
5972 LightRangeLessOrEqual::LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l,
5974 : Constraint(s), left_(l), right_(r), demon_(nullptr) {}
5976 void LightRangeLessOrEqual::Post() {
5978 solver(),
this, &LightRangeLessOrEqual::CheckRange,
"CheckRange");
5979 left_->WhenRange(demon_);
5980 right_->WhenRange(demon_);
5983 void LightRangeLessOrEqual::InitialPropagate() {
5984 left_->SetMax(right_->Max());
5985 right_->SetMin(left_->Min());
5986 if (left_->Max() <= right_->Min()) {
5987 demon_->inhibit(solver());
5991 void LightRangeLessOrEqual::CheckRange() {
5992 if (left_->Min() > right_->Max()) {
5995 if (left_->Max() <= right_->Min()) {
5996 demon_->inhibit(solver());
6000 std::string LightRangeLessOrEqual::DebugString()
const {
6001 return left_->DebugString() +
" < " + right_->DebugString();
6006 void RoutingDimension::InitializeCumuls() {
6007 Solver*
const solver = model_->
solver();
6009 const auto capacity_range = std::minmax_element(vehicle_capacities_.begin(),
6010 vehicle_capacities_.end());
6011 const int64 min_capacity = *capacity_range.first;
6012 CHECK_GE(min_capacity, 0);
6013 const int64 max_capacity = *capacity_range.second;
6014 solver->MakeIntVarArray(size, 0, max_capacity, name_, &cumuls_);
6016 for (
int v = 0; v < model_->
vehicles(); v++) {
6017 const int64 vehicle_capacity = vehicle_capacities_[v];
6018 cumuls_[model_->
Start(v)]->SetMax(vehicle_capacity);
6019 cumuls_[model_->
End(v)]->SetMax(vehicle_capacity);
6022 forbidden_intervals_.resize(size);
6023 capacity_vars_.clear();
6024 if (min_capacity != max_capacity) {
6025 solver->MakeIntVarArray(size, 0,
kint64max, &capacity_vars_);
6026 for (
int i = 0; i < size; ++i) {
6027 IntVar*
const capacity_var = capacity_vars_[i];
6028 if (i < model_->Size()) {
6029 IntVar*
const capacity_active = solver->MakeBoolVar();
6030 solver->AddConstraint(
6031 solver->MakeLessOrEqual(model_->
ActiveVar(i), capacity_active));
6032 solver->AddConstraint(solver->MakeIsLessOrEqualCt(
6033 cumuls_[i], capacity_var, capacity_active));
6035 solver->AddConstraint(
6036 solver->MakeLessOrEqual(cumuls_[i], capacity_var));
6043 template <
int64 value>
6048 void ComputeTransitClasses(
const std::vector<int>& evaluator_indices,
6049 std::vector<int>* class_evaluators,
6050 std::vector<int64>* vehicle_to_class) {
6051 CHECK(class_evaluators !=
nullptr);
6052 CHECK(vehicle_to_class !=
nullptr);
6053 class_evaluators->clear();
6054 vehicle_to_class->resize(evaluator_indices.size(), -1);
6055 absl::flat_hash_map<int, int64> evaluator_to_class;
6056 for (
int i = 0; i < evaluator_indices.size(); ++i) {
6057 const int evaluator_index = evaluator_indices[i];
6058 int evaluator_class = -1;
6059 if (!
gtl::FindCopy(evaluator_to_class, evaluator_index, &evaluator_class)) {
6060 evaluator_class = class_evaluators->size();
6061 evaluator_to_class[evaluator_index] = evaluator_class;
6062 class_evaluators->push_back(evaluator_index);
6064 (*vehicle_to_class)[i] = evaluator_class;
6069 void RoutingDimension::InitializeTransitVariables(
int64 slack_max) {
6070 CHECK(!class_evaluators_.empty());
6071 CHECK(base_dimension_ ==
nullptr ||
6072 !state_dependent_class_evaluators_.empty());
6074 Solver*
const solver = model_->
solver();
6075 const int size = model_->
Size();
6076 const Solver::IndexEvaluator1 dependent_vehicle_class_function =
6078 return (0 <=
index &&
index < state_dependent_vehicle_to_class_.size())
6079 ? state_dependent_vehicle_to_class_[
index]
6080 : state_dependent_class_evaluators_.size();
6082 const std::string slack_name = name_ +
" slack";
6083 const std::string transit_name = name_ +
" fixed transit";
6085 for (
int64 i = 0; i < size; ++i) {
6086 fixed_transits_[i] =
6089 if (base_dimension_ !=
nullptr) {
6090 if (state_dependent_class_evaluators_.size() == 1) {
6091 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
6092 for (
int64 j = 0; j < cumuls_.size(); ++j) {
6093 transition_variables[j] =
6094 MakeRangeMakeElementExpr(
6096 ->StateDependentTransitCallback(
6097 state_dependent_class_evaluators_[0])(i, j)
6099 base_dimension_->
CumulVar(i), solver)
6102 dependent_transits_[i] =
6103 solver->MakeElement(transition_variables, model_->
NextVar(i))
6106 IntVar*
const vehicle_class_var =
6108 ->MakeElement(dependent_vehicle_class_function,
6111 std::vector<IntVar*> transit_for_vehicle;
6112 transit_for_vehicle.reserve(state_dependent_class_evaluators_.size() +
6114 for (
int evaluator : state_dependent_class_evaluators_) {
6115 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
6116 for (
int64 j = 0; j < cumuls_.size(); ++j) {
6117 transition_variables[j] =
6118 MakeRangeMakeElementExpr(
6121 base_dimension_->
CumulVar(i), solver)
6124 transit_for_vehicle.push_back(
6125 solver->MakeElement(transition_variables, model_->
NextVar(i))
6128 transit_for_vehicle.push_back(solver->MakeIntConst(0));
6129 dependent_transits_[i] =
6130 solver->MakeElement(transit_for_vehicle, vehicle_class_var)->Var();
6133 dependent_transits_[i] = solver->MakeIntConst(0);
6137 IntExpr* transit_expr = fixed_transits_[i];
6138 if (dependent_transits_[i]->Min() != 0 ||
6139 dependent_transits_[i]->Max() != 0) {
6140 transit_expr = solver->MakeSum(transit_expr, dependent_transits_[i]);
6143 if (slack_max == 0) {
6144 slacks_[i] = solver->MakeIntConst(0);
6147 solver->MakeIntVar(0, slack_max, absl::StrCat(slack_name, i));
6148 transit_expr = solver->MakeSum(slacks_[i], transit_expr);
6150 transits_[i] = transit_expr->Var();
6154 void RoutingDimension::InitializeTransits(
6155 const std::vector<int>& transit_evaluators,
6156 const std::vector<int>& state_dependent_transit_evaluators,
6158 CHECK_EQ(model_->
vehicles(), transit_evaluators.size());
6159 CHECK(base_dimension_ ==
nullptr ||
6160 model_->
vehicles() == state_dependent_transit_evaluators.size());
6161 const int size = model_->
Size();
6162 transits_.resize(size,
nullptr);
6163 fixed_transits_.resize(size,
nullptr);
6164 slacks_.resize(size,
nullptr);
6165 dependent_transits_.resize(size,
nullptr);
6166 ComputeTransitClasses(transit_evaluators, &class_evaluators_,
6167 &vehicle_to_class_);
6168 if (base_dimension_ !=
nullptr) {
6169 ComputeTransitClasses(state_dependent_transit_evaluators,
6170 &state_dependent_class_evaluators_,
6171 &state_dependent_vehicle_to_class_);
6174 InitializeTransitVariables(slack_max);
6179 std::vector<int64>* values) {
6180 const int num_nodes = path.size();
6181 values->resize(num_nodes - 1);
6182 for (
int i = 0; i < num_nodes - 1; ++i) {
6183 (*values)[i] = evaluator(path[i], path[i + 1]);
6188 : model_(
model), occurrences_of_type_(
model.GetNumberOfVisitTypes()) {}
6191 int vehicle,
const std::function<
int64(
int64)>& next_accessor) {
6198 for (
int pos = 0; pos < current_route_visits_.size(); pos++) {
6199 const int64 current_visit = current_route_visits_[pos];
6206 DCHECK_LT(type, occurrences_of_type_.size());
6207 int& num_type_added = occurrences_of_type_[type].num_type_added_to_vehicle;
6208 int& num_type_removed =
6209 occurrences_of_type_[type].num_type_removed_from_vehicle;
6210 DCHECK_LE(num_type_removed, num_type_added);
6212 num_type_removed == num_type_added) {
6222 if (policy == VisitTypePolicy::TYPE_ADDED_TO_VEHICLE ||
6223 policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED) {
6226 if (policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED ||
6227 policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6235 int vehicle,
const std::function<
int64(
int64)>& next_accessor) {
6238 std::fill(occurrences_of_type_.begin(), occurrences_of_type_.end(),
6244 current_route_visits_.clear();
6246 current = next_accessor(current)) {
6249 VisitTypePolicy::TYPE_ON_VEHICLE_UP_TO_VISIT) {
6250 occurrences_of_type_[type].position_of_last_type_on_vehicle_up_to_visit =
6251 current_route_visits_.size();
6253 current_route_visits_.push_back(current);
6275 check_hard_incompatibilities_(check_hard_incompatibilities) {}
6277 bool TypeIncompatibilityChecker::HasRegulationsToCheck()
const {
6279 (check_hard_incompatibilities_ &&
6288 bool TypeIncompatibilityChecker::CheckTypeRegulations(
int type,
6289 VisitTypePolicy policy,
6291 if (policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6296 for (
int incompatible_type :
6302 if (check_hard_incompatibilities_) {
6303 for (
int incompatible_type :
6313 bool TypeRequirementChecker::HasRegulationsToCheck()
const {
6318 bool TypeRequirementChecker::CheckRequiredTypesCurrentlyOnRoute(
6319 const std::vector<absl::flat_hash_set<int>>& required_type_alternatives,
6321 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6322 required_type_alternatives) {
6323 bool has_one_of_alternatives =
false;
6324 for (
int type_alternative : requirement_alternatives) {
6326 has_one_of_alternatives =
true;
6330 if (!has_one_of_alternatives) {
6337 bool TypeRequirementChecker::CheckTypeRegulations(
int type,
6338 VisitTypePolicy policy,
6342 if (!CheckRequiredTypesCurrentlyOnRoute(
6348 if (!CheckRequiredTypesCurrentlyOnRoute(
6355 types_with_same_vehicle_requirements_on_route_.insert(type);
6360 bool TypeRequirementChecker::FinalizeCheck()
const {
6361 for (
int type : types_with_same_vehicle_requirements_on_route_) {
6362 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6364 bool has_one_of_alternatives =
false;
6365 for (
const int type_alternative : requirement_alternatives) {
6367 has_one_of_alternatives =
true;
6371 if (!has_one_of_alternatives) {
6380 : Constraint(
model.solver()),
6382 incompatibility_checker_(
model, true),
6383 requirement_checker_(
model),
6384 vehicle_demons_(
model.vehicles()) {}
6386 void TypeRegulationsConstraint::PropagateNodeRegulations(
int node) {
6387 DCHECK_LT(node, model_.
Size());
6392 const int vehicle = model_.
VehicleVar(node)->Min();
6393 if (vehicle < 0)
return;
6394 DCHECK(vehicle_demons_[vehicle] !=
nullptr);
6395 EnqueueDelayedDemon(vehicle_demons_[vehicle]);
6398 void TypeRegulationsConstraint::CheckRegulationsOnVehicle(
int vehicle) {
6399 const auto next_accessor = [
this, vehicle](
int64 node) {
6400 if (model_.
NextVar(node)->Bound()) {
6401 return model_.
NextVar(node)->Value();
6404 return model_.
End(vehicle);
6406 if (!incompatibility_checker_.
CheckVehicle(vehicle, next_accessor) ||
6407 !requirement_checker_.
CheckVehicle(vehicle, next_accessor)) {
6413 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6415 solver(),
this, &TypeRegulationsConstraint::CheckRegulationsOnVehicle,
6416 "CheckRegulationsOnVehicle", vehicle);
6418 for (
int node = 0; node < model_.
Size(); node++) {
6420 solver(),
this, &TypeRegulationsConstraint::PropagateNodeRegulations,
6421 "PropagateNodeRegulations", node);
6422 model_.
NextVar(node)->WhenBound(node_demon);
6423 model_.
VehicleVar(node)->WhenBound(node_demon);
6428 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6429 CheckRegulationsOnVehicle(vehicle);
6433 void RoutingDimension::CloseModel(
bool use_light_propagation) {
6434 Solver*
const solver = model_->
solver();
6435 const auto capacity_lambda = [
this](
int64 vehicle) {
6436 return vehicle >= 0 ? vehicle_capacities_[vehicle] :
kint64max;
6438 for (
int i = 0; i < capacity_vars_.size(); ++i) {
6439 IntVar*
const vehicle_var = model_->
VehicleVar(i);
6440 IntVar*
const capacity_var = capacity_vars_[i];
6441 if (use_light_propagation) {
6442 solver->AddConstraint(MakeLightElement(
6443 solver, capacity_var, vehicle_var, capacity_lambda,
6444 [
this]() {
return model_->enable_deep_serialization_; }));
6446 solver->AddConstraint(solver->MakeEquality(
6448 solver->MakeElement(capacity_lambda, vehicle_var)->Var()));
6451 const Solver::IndexEvaluator1 vehicle_class_function = [
this](
int index) {
6452 return IthElementOrValue<-1>(vehicle_to_class_,
index);
6454 for (
int i = 0; i < fixed_transits_.size(); ++i) {
6455 IntVar*
const next_var = model_->
NextVar(i);
6456 IntVar*
const fixed_transit = fixed_transits_[i];
6457 const auto transit_vehicle_evaluator = [
this, i](
int64 to,
6461 if (use_light_propagation) {
6462 if (class_evaluators_.size() == 1) {
6463 const int class_evaluator_index = class_evaluators_[0];
6464 const auto& unary_callback =
6466 if (unary_callback ==
nullptr) {
6467 solver->AddConstraint(MakeLightElement(
6468 solver, fixed_transit, next_var,
6469 [
this, i](
int64 to) {
6472 [
this]() {
return model_->enable_deep_serialization_; }));
6474 fixed_transit->SetValue(unary_callback(i));
6477 solver->AddConstraint(MakeLightElement2(
6478 solver, fixed_transit, next_var, model_->
VehicleVar(i),
6479 transit_vehicle_evaluator,
6480 [
this]() {
return model_->enable_deep_serialization_; }));
6483 if (class_evaluators_.size() == 1) {
6484 const int class_evaluator_index = class_evaluators_[0];
6485 const auto& unary_callback =
6487 if (unary_callback ==
nullptr) {
6488 solver->AddConstraint(solver->MakeEquality(
6489 fixed_transit, solver
6491 [
this, i](
int64 to) {
6493 class_evaluators_[0])(i, to);
6498 fixed_transit->SetValue(unary_callback(i));
6501 IntVar*
const vehicle_class_var =
6502 solver->MakeElement(vehicle_class_function, model_->
VehicleVar(i))
6504 solver->AddConstraint(solver->MakeEquality(
6505 fixed_transit, solver
6506 ->MakeElement(transit_vehicle_evaluator,
6507 next_var, vehicle_class_var)
6513 GlobalVehicleBreaksConstraint* constraint =
6514 model()->
solver()->RevAlloc(
new GlobalVehicleBreaksConstraint(
this));
6515 solver->AddConstraint(constraint);
6520 int64 vehicle)
const {
6529 IntVar*
const cumul_var = cumuls_[
index];
6536 if (next_start >
max)
break;
6537 if (next_start < interval->start) {
6542 if (next_start <=
max) {
6550 CHECK_GE(vehicle, 0);
6551 CHECK_LT(vehicle, vehicle_span_upper_bounds_.size());
6552 CHECK_GE(upper_bound, 0);
6553 vehicle_span_upper_bounds_[vehicle] = upper_bound;
6558 CHECK_GE(vehicle, 0);
6559 CHECK_LT(vehicle, vehicle_span_cost_coefficients_.size());
6561 vehicle_span_cost_coefficients_[vehicle] =
coefficient;
6576 if (!
cost.IsNonDecreasing()) {
6577 LOG(WARNING) <<
"Only non-decreasing cost functions are supported.";
6580 if (
cost.Value(0) < 0) {
6581 LOG(WARNING) <<
"Only positive cost functions are supported.";
6584 if (
index >= cumul_var_piecewise_linear_cost_.size()) {
6585 cumul_var_piecewise_linear_cost_.resize(
index + 1);
6587 PiecewiseLinearCost& piecewise_linear_cost =
6588 cumul_var_piecewise_linear_cost_[
index];
6589 piecewise_linear_cost.var = cumuls_[
index];
6590 piecewise_linear_cost.cost = absl::make_unique<PiecewiseLinearFunction>(
cost);
6594 return (
index < cumul_var_piecewise_linear_cost_.size() &&
6595 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr);
6600 if (
index < cumul_var_piecewise_linear_cost_.size() &&
6601 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr) {
6602 return cumul_var_piecewise_linear_cost_[
index].cost.get();
6609 IntExpr* expr,
int index) {
6610 Solver*
const solver =
model->solver();
6612 const int vehicle =
model->VehicleIndex(
index);
6613 DCHECK_GE(vehicle, 0);
6614 return solver->MakeProd(expr,
model->VehicleCostsConsideredVar(vehicle))
6617 return solver->MakeProd(expr,
model->ActiveVar(
index))->Var();
6621 void RoutingDimension::SetupCumulVarPiecewiseLinearCosts(
6622 std::vector<IntVar*>* cost_elements)
const {
6623 CHECK(cost_elements !=
nullptr);
6624 Solver*
const solver = model_->
solver();
6625 for (
int i = 0; i < cumul_var_piecewise_linear_cost_.size(); ++i) {
6626 const PiecewiseLinearCost& piecewise_linear_cost =
6627 cumul_var_piecewise_linear_cost_[i];
6628 if (piecewise_linear_cost.var !=
nullptr) {
6629 IntExpr*
const expr = solver->MakePiecewiseLinearExpr(
6630 piecewise_linear_cost.var, *piecewise_linear_cost.cost);
6631 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6632 cost_elements->push_back(cost_var);
6642 if (
index >= cumul_var_soft_upper_bound_.size()) {
6643 cumul_var_soft_upper_bound_.resize(
index + 1, {
nullptr, 0, 0});
6645 cumul_var_soft_upper_bound_[
index] = {cumuls_[
index], upper_bound,
6650 return (
index < cumul_var_soft_upper_bound_.size() &&
6651 cumul_var_soft_upper_bound_[
index].var !=
nullptr);
6655 if (
index < cumul_var_soft_upper_bound_.size() &&
6656 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6657 return cumul_var_soft_upper_bound_[
index].bound;
6659 return cumuls_[
index]->Max();
6664 if (
index < cumul_var_soft_upper_bound_.size() &&
6665 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6666 return cumul_var_soft_upper_bound_[
index].coefficient;
6671 void RoutingDimension::SetupCumulVarSoftUpperBoundCosts(
6672 std::vector<IntVar*>* cost_elements)
const {
6673 CHECK(cost_elements !=
nullptr);
6674 Solver*
const solver = model_->
solver();
6675 for (
int i = 0; i < cumul_var_soft_upper_bound_.size(); ++i) {
6676 const SoftBound& soft_bound = cumul_var_soft_upper_bound_[i];
6677 if (soft_bound.var !=
nullptr) {
6678 IntExpr*
const expr = solver->MakeSemiContinuousExpr(
6679 solver->MakeSum(soft_bound.var, -soft_bound.bound), 0,
6680 soft_bound.coefficient);
6681 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6682 cost_elements->push_back(cost_var);
6686 soft_bound.coefficient);
6693 if (
index >= cumul_var_soft_lower_bound_.size()) {
6694 cumul_var_soft_lower_bound_.resize(
index + 1, {
nullptr, 0, 0});
6696 cumul_var_soft_lower_bound_[
index] = {cumuls_[
index], lower_bound,
6701 return (
index < cumul_var_soft_lower_bound_.size() &&
6702 cumul_var_soft_lower_bound_[
index].var !=
nullptr);
6706 if (
index < cumul_var_soft_lower_bound_.size() &&
6707 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6708 return cumul_var_soft_lower_bound_[
index].bound;
6710 return cumuls_[
index]->Min();
6715 if (
index < cumul_var_soft_lower_bound_.size() &&
6716 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6717 return cumul_var_soft_lower_bound_[
index].coefficient;
6722 void RoutingDimension::SetupCumulVarSoftLowerBoundCosts(
6723 std::vector<IntVar*>* cost_elements)
const {
6724 CHECK(cost_elements !=
nullptr);
6725 Solver*
const solver = model_->
solver();
6726 for (
int i = 0; i < cumul_var_soft_lower_bound_.size(); ++i) {
6727 const SoftBound& soft_bound = cumul_var_soft_lower_bound_[i];
6728 if (soft_bound.var !=
nullptr) {
6729 IntExpr*
const expr = solver->MakeSemiContinuousExpr(
6730 solver->MakeDifference(soft_bound.bound, soft_bound.var), 0,
6731 soft_bound.coefficient);
6732 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6733 cost_elements->push_back(cost_var);
6737 soft_bound.coefficient);
6742 void RoutingDimension::SetupGlobalSpanCost(
6743 std::vector<IntVar*>* cost_elements)
const {
6744 CHECK(cost_elements !=
nullptr);
6745 Solver*
const solver = model_->
solver();
6746 if (global_span_cost_coefficient_ != 0) {
6747 std::vector<IntVar*> end_cumuls;
6748 for (
int i = 0; i < model_->
vehicles(); ++i) {
6749 end_cumuls.push_back(solver
6750 ->MakeProd(model_->vehicle_costs_considered_[i],
6751 cumuls_[model_->
End(i)])
6754 IntVar*
const max_end_cumul = solver->MakeMax(end_cumuls)->Var();
6756 max_end_cumul, global_span_cost_coefficient_);
6757 std::vector<IntVar*> start_cumuls;
6758 for (
int i = 0; i < model_->
vehicles(); ++i) {
6759 IntVar* global_span_cost_start_cumul = solver->MakeIntVar(0,
kint64max);
6760 solver->AddConstraint(solver->MakeIfThenElseCt(
6761 model_->vehicle_costs_considered_[i], cumuls_[model_->
Start(i)],
6762 max_end_cumul, global_span_cost_start_cumul));
6763 start_cumuls.push_back(global_span_cost_start_cumul);
6765 IntVar*
const min_start_cumul = solver->MakeMin(start_cumuls)->Var();
6767 min_start_cumul, global_span_cost_coefficient_);
6772 for (
int var_index = 0; var_index < model_->
Size(); ++var_index) {
6774 slacks_[var_index], global_span_cost_coefficient_);
6775 cost_elements->push_back(
6778 model_->vehicle_costs_considered_[0],
6781 solver->MakeSum(transits_[var_index],
6782 dependent_transits_[var_index]),
6783 global_span_cost_coefficient_),
6788 IntVar*
const end_range =
6789 solver->MakeDifference(max_end_cumul, min_start_cumul)->Var();
6790 end_range->SetMin(0);
6791 cost_elements->push_back(
6792 solver->MakeProd(end_range, global_span_cost_coefficient_)->Var());
6798 std::vector<IntervalVar*> breaks,
int vehicle,
6799 std::vector<int64> node_visit_transits) {
6800 if (breaks.empty())
return;
6802 [node_visit_transits](
int64 from,
int64 to) {
6803 return node_visit_transits[from];
6809 std::vector<IntervalVar*> breaks,
int vehicle,
6810 std::vector<int64> node_visit_transits,
6812 if (breaks.empty())
return;
6814 [node_visit_transits](
int64 from,
int64 to) {
6815 return node_visit_transits[from];
6823 std::vector<IntervalVar*> breaks,
int vehicle,
int pre_travel_evaluator,
6824 int post_travel_evaluator) {
6825 DCHECK_LE(0, vehicle);
6826 DCHECK_LT(vehicle, model_->
vehicles());
6827 if (breaks.empty())
return;
6829 vehicle_break_intervals_[vehicle] = std::move(breaks);
6830 vehicle_pre_travel_evaluators_[vehicle] = pre_travel_evaluator;
6831 vehicle_post_travel_evaluators_[vehicle] = post_travel_evaluator;
6833 for (IntervalVar*
const interval : vehicle_break_intervals_[vehicle]) {
6852 DCHECK(!break_constraints_are_initialized_);
6853 const int num_vehicles = model_->
vehicles();
6854 vehicle_break_intervals_.resize(num_vehicles);
6855 vehicle_pre_travel_evaluators_.resize(num_vehicles, -1);
6856 vehicle_post_travel_evaluators_.resize(num_vehicles, -1);
6857 vehicle_break_distance_duration_.resize(num_vehicles);
6858 break_constraints_are_initialized_ =
true;
6862 return break_constraints_are_initialized_;
6866 int vehicle)
const {
6867 DCHECK_LE(0, vehicle);
6868 DCHECK_LT(vehicle, vehicle_break_intervals_.size());
6869 return vehicle_break_intervals_[vehicle];
6873 DCHECK_LE(0, vehicle);
6874 DCHECK_LT(vehicle, vehicle_pre_travel_evaluators_.size());
6875 return vehicle_pre_travel_evaluators_[vehicle];
6879 DCHECK_LE(0, vehicle);
6880 DCHECK_LT(vehicle, vehicle_post_travel_evaluators_.size());
6881 return vehicle_post_travel_evaluators_[vehicle];
6887 DCHECK_LE(0, vehicle);
6888 DCHECK_LT(vehicle, model_->
vehicles());
6890 vehicle_break_distance_duration_[vehicle].emplace_back(distance, duration);
6899 const std::vector<std::pair<int64, int64>>&
6901 DCHECK_LE(0, vehicle);
6902 DCHECK_LT(vehicle, vehicle_break_distance_duration_.size());
6903 return vehicle_break_distance_duration_[vehicle];
6908 CHECK_GE(pair_index, 0);
6909 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6910 pickup_to_delivery_limits_per_pair_index_.resize(pair_index + 1);
6912 pickup_to_delivery_limits_per_pair_index_[pair_index] =
6913 std::move(limit_function);
6917 return !pickup_to_delivery_limits_per_pair_index_.empty();
6922 int delivery)
const {
6923 DCHECK_GE(pair_index, 0);
6925 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6929 pickup_to_delivery_limits_per_pair_index_[pair_index];
6930 if (!pickup_to_delivery_limit_function) {
6934 DCHECK_GE(pickup, 0);
6935 DCHECK_GE(delivery, 0);
6936 return pickup_to_delivery_limit_function(pickup, delivery);
6939 void RoutingDimension::SetupSlackAndDependentTransitCosts()
const {
6940 if (model_->
vehicles() == 0)
return;
6942 bool all_vehicle_span_costs_are_equal =
true;
6943 for (
int i = 1; i < model_->
vehicles(); ++i) {
6944 all_vehicle_span_costs_are_equal &= vehicle_span_cost_coefficients_[i] ==
6945 vehicle_span_cost_coefficients_[0];
6948 if (all_vehicle_span_costs_are_equal &&
6949 vehicle_span_cost_coefficients_[0] == 0) {
6961 std::vector<const RoutingDimension*> dimensions_with_relevant_slacks = {
this};
6963 const RoutingDimension*
next =
6964 dimensions_with_relevant_slacks.back()->base_dimension_;
6965 if (
next ==
nullptr ||
next == dimensions_with_relevant_slacks.back()) {
6968 dimensions_with_relevant_slacks.push_back(
next);
6971 for (
auto it = dimensions_with_relevant_slacks.rbegin();
6972 it != dimensions_with_relevant_slacks.rend(); ++it) {
6973 for (
int i = 0; i < model_->
vehicles(); ++i) {
6979 for (IntVar*
const slack : (*it)->slacks_) {