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"
64 class LocalSearchPhaseParameters;
68 "The number of sectors the space is divided before it is sweeped "
82 class SetValuesFromTargets :
public DecisionBuilder {
84 SetValuesFromTargets(std::vector<IntVar*> variables,
85 std::vector<int64> targets)
86 : variables_(std::move(variables)),
87 targets_(std::move(targets)),
89 steps_(variables_.size(), 0) {
90 DCHECK_EQ(variables_.size(), targets_.size());
92 Decision* Next(Solver*
const solver)
override {
93 int index = index_.Value();
94 while (
index < variables_.size() && variables_[
index]->Bound()) {
97 index_.SetValue(solver,
index);
98 if (
index >= variables_.size())
return nullptr;
99 const int64 variable_min = variables_[
index]->Min();
100 const int64 variable_max = variables_[
index]->Max();
103 if (targets_[
index] <= variable_min) {
104 return solver->MakeAssignVariableValue(variables_[
index], variable_min);
105 }
else if (targets_[
index] >= variable_max) {
106 return solver->MakeAssignVariableValue(variables_[
index], variable_max);
113 if (
value < variable_min || variable_max <
value) {
114 step = GetNextStep(step);
125 steps_.SetValue(solver,
index, GetNextStep(step));
126 return solver->MakeAssignVariableValueOrDoNothing(variables_[
index],
133 return (step > 0) ? -step :
CapSub(1, step);
135 const std::vector<IntVar*> variables_;
136 const std::vector<int64> targets_;
138 RevArray<int64> steps_;
144 std::vector<IntVar*> variables,
145 std::vector<int64> targets) {
147 new SetValuesFromTargets(std::move(variables), std::move(targets)));
152 bool DimensionFixedTransitsEqualTransitEvaluatorForVehicle(
153 const RoutingDimension& dimension,
int vehicle) {
154 const RoutingModel*
const model = dimension.model();
155 int node =
model->Start(vehicle);
156 while (!
model->IsEnd(node)) {
157 if (!
model->NextVar(node)->Bound()) {
160 const int next =
model->NextVar(node)->Value();
161 if (dimension.transit_evaluator(vehicle)(node,
next) !=
162 dimension.FixedTransitVar(node)->Value()) {
170 bool DimensionFixedTransitsEqualTransitEvaluators(
171 const RoutingDimension& dimension) {
172 for (
int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
173 if (!DimensionFixedTransitsEqualTransitEvaluatorForVehicle(dimension,
181 class SetCumulsFromLocalDimensionCosts :
public DecisionBuilder {
183 SetCumulsFromLocalDimensionCosts(
184 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>*
186 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>*
188 SearchMonitor* monitor,
bool optimize_and_pack =
false)
189 : local_optimizers_(*local_optimizers),
190 local_mp_optimizers_(*local_mp_optimizers),
192 optimize_and_pack_(optimize_and_pack) {}
193 Decision* Next(Solver*
const solver)
override {
197 bool should_fail =
false;
198 for (
int i = 0; i < local_optimizers_.size(); ++i) {
199 const auto& local_optimizer = local_optimizers_[i];
200 const RoutingDimension*
const dimension = local_optimizer->dimension();
201 RoutingModel*
const model = dimension->model();
203 const auto compute_cumul_values =
204 [
this, &
next](LocalDimensionCumulOptimizer* optimizer,
int vehicle,
205 std::vector<int64>* cumul_values,
206 std::vector<int64>* break_start_end_values) {
207 if (optimize_and_pack_) {
208 return optimizer->ComputePackedRouteCumuls(
209 vehicle,
next, cumul_values, break_start_end_values);
211 return optimizer->ComputeRouteCumuls(vehicle,
next, cumul_values,
212 break_start_end_values);
215 for (
int vehicle = 0; vehicle <
model->vehicles(); ++vehicle) {
217 DCHECK(DimensionFixedTransitsEqualTransitEvaluatorForVehicle(*dimension,
219 const bool vehicle_has_break_constraint =
220 dimension->HasBreakConstraints() &&
221 !dimension->GetBreakIntervalsOfVehicle(vehicle).empty();
222 LocalDimensionCumulOptimizer*
const optimizer =
223 vehicle_has_break_constraint ? local_mp_optimizers_[i].get()
224 : local_optimizer.get();
225 DCHECK(optimizer !=
nullptr);
226 std::vector<int64> cumul_values;
227 std::vector<int64> break_start_end_values;
229 optimizer, vehicle, &cumul_values, &break_start_end_values);
236 cumul_values.clear();
237 break_start_end_values.clear();
238 DCHECK(local_mp_optimizers_[i] !=
nullptr);
239 if (compute_cumul_values(local_mp_optimizers_[i].get(), vehicle,
240 &cumul_values, &break_start_end_values) ==
250 std::vector<IntVar*> cp_variables;
251 std::vector<int64> cp_values;
252 std::swap(cp_values, cumul_values);
254 int current =
model->Start(vehicle);
256 cp_variables.push_back(dimension->CumulVar(current));
257 if (!
model->IsEnd(current)) {
258 current =
model->NextVar(current)->Value();
269 std::swap(cp_variables[1], cp_variables.back());
270 std::swap(cp_values[1], cp_values.back());
271 if (dimension->HasBreakConstraints()) {
273 dimension->GetBreakIntervalsOfVehicle(vehicle)) {
274 cp_variables.push_back(
interval->SafeStartExpr(0)->Var());
275 cp_variables.push_back(
interval->SafeEndExpr(0)->Var());
277 cp_values.insert(cp_values.end(), break_start_end_values.begin(),
278 break_start_end_values.end());
281 for (
int i = 0; i < cp_values.size(); ++i) {
283 cp_values[i] = cp_variables[i]->Min();
286 if (!solver->SolveAndCommit(
288 std::move(cp_values)),
302 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>&
304 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>&
305 local_mp_optimizers_;
306 SearchMonitor*
const monitor_;
307 const bool optimize_and_pack_;
310 class SetCumulsFromGlobalDimensionCosts :
public DecisionBuilder {
312 SetCumulsFromGlobalDimensionCosts(
313 const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>*
315 SearchMonitor* monitor,
bool optimize_and_pack =
false)
316 : global_optimizers_(*global_optimizers),
318 optimize_and_pack_(optimize_and_pack) {}
319 Decision* Next(Solver*
const solver)
override {
323 bool should_fail =
false;
324 for (
const auto& global_optimizer : global_optimizers_) {
325 const RoutingDimension* dimension = global_optimizer->dimension();
326 RoutingModel*
const model = dimension->model();
330 DCHECK(DimensionFixedTransitsEqualTransitEvaluators(*dimension));
332 std::vector<int64> cumul_values;
333 std::vector<int64> break_start_end_values;
334 const bool cumuls_optimized =
336 ? global_optimizer->ComputePackedCumuls(
next, &cumul_values,
337 &break_start_end_values)
338 : global_optimizer->ComputeCumuls(
next, &cumul_values,
339 &break_start_end_values);
340 if (!cumuls_optimized) {
346 std::vector<IntVar*> cp_variables = dimension->cumuls();
347 std::vector<int64> cp_values;
348 std::swap(cp_values, cumul_values);
349 if (dimension->HasBreakConstraints()) {
350 const int num_vehicles =
model->vehicles();
351 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
353 dimension->GetBreakIntervalsOfVehicle(vehicle)) {
354 cp_variables.push_back(
interval->SafeStartExpr(0)->Var());
355 cp_variables.push_back(
interval->SafeEndExpr(0)->Var());
358 cp_values.insert(cp_values.end(), break_start_end_values.begin(),
359 break_start_end_values.end());
362 for (
int i = 0; i < cp_values.size(); ++i) {
364 cp_values[i] = cp_variables[i]->Min();
367 if (!solver->SolveAndCommit(
369 std::move(cp_values)),
382 const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>&
384 SearchMonitor*
const monitor_;
385 const bool optimize_and_pack_;
391 const Assignment* original_assignment, absl::Duration duration_limit) {
393 if (original_assignment ==
nullptr)
return nullptr;
394 if (duration_limit <= absl::ZeroDuration())
return original_assignment;
395 if (global_dimension_optimizers_.empty() &&
396 local_dimension_optimizers_.empty()) {
397 DCHECK(local_dimension_mp_optimizers_.empty());
398 return original_assignment;
405 Assignment* packed_assignment = solver_->MakeAssignment();
409 std::vector<DecisionBuilder*> decision_builders;
410 decision_builders.push_back(solver_->MakeRestoreAssignment(preassignment_));
411 decision_builders.push_back(
412 solver_->MakeRestoreAssignment(packed_assignment));
413 decision_builders.push_back(
414 solver_->RevAlloc(
new SetCumulsFromLocalDimensionCosts(
415 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
416 GetOrCreateLargeNeighborhoodSearchLimit(),
418 decision_builders.push_back(
419 solver_->RevAlloc(
new SetCumulsFromGlobalDimensionCosts(
420 &global_dimension_optimizers_,
421 GetOrCreateLargeNeighborhoodSearchLimit(),
423 decision_builders.push_back(
424 CreateFinalizerForMinimizedAndMaximizedVariables());
427 solver_->Compose(decision_builders);
428 solver_->Solve(restore_pack_and_finalize,
429 packed_dimensions_assignment_collector_, limit);
431 if (packed_dimensions_assignment_collector_->
solution_count() != 1) {
432 LOG(
ERROR) <<
"The given assignment is not valid for this model, or cannot "
437 packed_assignment->
Copy(original_assignment);
439 packed_dimensions_assignment_collector_->
solution(0));
441 return packed_assignment;
446 class DifferentFromValues :
public Constraint {
448 DifferentFromValues(
Solver* solver,
IntVar*
var, std::vector<int64> values)
449 :
Constraint(solver), var_(
var), values_(std::move(values)) {}
450 void Post()
override {}
451 void InitialPropagate()
override { var_->RemoveValues(values_); }
452 std::string DebugString()
const override {
return "DifferentFromValues"; }
453 void Accept(ModelVisitor*
const visitor)
const override {
463 const std::vector<int64> values_;
477 template <
typename F>
478 class LightFunctionElementConstraint :
public Constraint {
480 LightFunctionElementConstraint(Solver*
const solver, IntVar*
const var,
481 IntVar*
const index, F values,
482 std::function<
bool()> deep_serialize)
483 : Constraint(solver),
486 values_(std::move(values)),
487 deep_serialize_(std::move(deep_serialize)) {}
488 ~LightFunctionElementConstraint()
override {}
490 void Post()
override {
492 solver(),
this, &LightFunctionElementConstraint::IndexBound,
494 index_->WhenBound(demon);
497 void InitialPropagate()
override {
498 if (index_->Bound()) {
503 std::string DebugString()
const override {
504 return "LightFunctionElementConstraint";
507 void Accept(ModelVisitor*
const visitor)
const override {
514 if (deep_serialize_()) {
515 visitor->VisitInt64ToInt64Extension(values_, index_->Min(),
522 void IndexBound() { var_->SetValue(values_(index_->Min())); }
525 IntVar*
const index_;
527 std::function<bool()> deep_serialize_;
530 template <
typename F>
531 Constraint* MakeLightElement(Solver*
const solver, IntVar*
const var,
532 IntVar*
const index, F values,
533 std::function<
bool()> deep_serialize) {
534 return solver->RevAlloc(
new LightFunctionElementConstraint<F>(
535 solver,
var,
index, std::move(values), std::move(deep_serialize)));
543 template <
typename F>
544 class LightFunctionElement2Constraint :
public Constraint {
546 LightFunctionElement2Constraint(Solver*
const solver, IntVar*
const var,
547 IntVar*
const index1, IntVar*
const index2,
549 std::function<
bool()> deep_serialize)
550 : Constraint(solver),
554 values_(std::move(values)),
555 deep_serialize_(std::move(deep_serialize)) {}
556 ~LightFunctionElement2Constraint()
override {}
557 void Post()
override {
559 solver(),
this, &LightFunctionElement2Constraint::IndexBound,
561 index1_->WhenBound(demon);
562 index2_->WhenBound(demon);
564 void InitialPropagate()
override { IndexBound(); }
566 std::string DebugString()
const override {
567 return "LightFunctionElement2Constraint";
570 void Accept(ModelVisitor*
const visitor)
const override {
579 const int64 index1_min = index1_->Min();
580 const int64 index1_max = index1_->Max();
583 if (deep_serialize_()) {
584 for (
int i = index1_min; i <= index1_max; ++i) {
585 visitor->VisitInt64ToInt64Extension(
586 [
this, i](
int64 j) {
return values_(i, j); }, index2_->Min(),
595 if (index1_->Bound() && index2_->Bound()) {
596 var_->SetValue(values_(index1_->Min(), index2_->Min()));
601 IntVar*
const index1_;
602 IntVar*
const index2_;
604 std::function<bool()> deep_serialize_;
607 template <
typename F>
608 Constraint* MakeLightElement2(Solver*
const solver, IntVar*
const var,
609 IntVar*
const index1, IntVar*
const index2,
610 F values, std::function<
bool()> deep_serialize) {
611 return solver->RevAlloc(
new LightFunctionElement2Constraint<F>(
612 solver,
var, index1, index2, std::move(values),
613 std::move(deep_serialize)));
617 template <
class A,
class B>
618 static int64 ReturnZero(A
a, B
b) {
624 for (
int i = 0; i < size1; i++) {
625 for (
int j = 0; j < size2; j++) {
650 : nodes_(index_manager.num_nodes()),
651 vehicles_(index_manager.num_vehicles()),
652 max_active_vehicles_(vehicles_),
653 fixed_cost_of_vehicle_(vehicles_, 0),
655 linear_cost_factor_of_vehicle_(vehicles_, 0),
656 quadratic_cost_factor_of_vehicle_(vehicles_, 0),
657 vehicle_amortized_cost_factors_set_(false),
658 consider_empty_route_costs_(vehicles_, false),
660 costs_are_homogeneous_across_vehicles_(
662 cache_callbacks_(false),
664 vehicle_pickup_delivery_policy_(vehicles_, PICKUP_AND_DELIVERY_NO_ORDER),
665 has_hard_type_incompatibilities_(false),
666 has_temporal_type_incompatibilities_(false),
667 has_same_vehicle_type_requirements_(false),
668 has_temporal_type_requirements_(false),
672 manager_(index_manager) {
674 vehicle_to_transit_cost_.assign(
678 cache_callbacks_ = (nodes_ <=
parameters.max_callback_cache_size());
681 ConstraintSolverParameters solver_parameters =
684 solver_ = absl::make_unique<Solver>(
"Routing", solver_parameters);
690 index_to_pickup_index_pairs_.resize(size);
691 index_to_delivery_index_pairs_.resize(size);
693 index_to_type_policy_.resize(index_manager.
num_indices());
696 for (
int v = 0; v < index_manager.
num_vehicles(); ++v) {
698 index_to_vehicle_[starts_[v]] = v;
700 index_to_vehicle_[ends_[v]] = v;
703 const std::vector<RoutingIndexManager::NodeIndex>& index_to_node =
705 index_to_equivalence_class_.resize(index_manager.
num_indices());
706 for (
int i = 0; i < index_to_node.size(); ++i) {
707 index_to_equivalence_class_[i] = index_to_node[i].value();
709 allowed_vehicles_.resize(
Size() + vehicles_);
712 void RoutingModel::Initialize() {
713 const int size =
Size();
715 solver_->MakeIntVarArray(size, 0, size + vehicles_ - 1,
"Nexts", &nexts_);
716 solver_->AddConstraint(solver_->MakeAllDifferent(nexts_,
false));
717 index_to_disjunctions_.resize(size + vehicles_);
720 solver_->MakeIntVarArray(size + vehicles_, -1, vehicles_ - 1,
"Vehicles",
723 solver_->MakeBoolVarArray(size,
"Active", &active_);
725 solver_->MakeBoolVarArray(vehicles_,
"ActiveVehicle", &vehicle_active_);
727 solver_->MakeBoolVarArray(vehicles_,
"VehicleCostsConsidered",
728 &vehicle_costs_considered_);
730 solver_->MakeBoolVarArray(size + vehicles_,
"IsBoundToEnd",
735 preassignment_ = solver_->MakeAssignment();
742 absl::flat_hash_set<RangeIntToIntFunction*> value_functions_delete;
743 absl::flat_hash_set<RangeMinMaxIndexFunction*> index_functions_delete;
744 for (
const auto& cache_line : state_dependent_transit_evaluators_cache_) {
745 for (
const auto& key_transit : *cache_line) {
746 value_functions_delete.insert(key_transit.second.transit);
747 index_functions_delete.insert(key_transit.second.transit_plus_identity);
755 const int index = unary_transit_evaluators_.size();
756 unary_transit_evaluators_.push_back(std::move(
callback));
758 return unary_transit_evaluators_[
index](i);
764 is_transit_evaluator_positive_.push_back(
true);
765 DCHECK(TransitCallbackPositive(
771 if (cache_callbacks_) {
773 std::vector<int64> cache(size * size, 0);
774 for (
int i = 0; i < size; ++i) {
775 for (
int j = 0; j < size; ++j) {
776 cache[i * size + j] =
callback(i, j);
779 transit_evaluators_.push_back(
780 [cache, size](
int64 i,
int64 j) {
return cache[i * size + j]; });
782 transit_evaluators_.push_back(std::move(
callback));
784 if (transit_evaluators_.size() != unary_transit_evaluators_.size()) {
785 DCHECK_EQ(transit_evaluators_.size(), unary_transit_evaluators_.size() + 1);
786 unary_transit_evaluators_.push_back(
nullptr);
788 if (transit_evaluators_.size() != is_transit_evaluator_positive_.size()) {
790 is_transit_evaluator_positive_.size() + 1);
791 is_transit_evaluator_positive_.push_back(
false);
793 return transit_evaluators_.size() - 1;
797 is_transit_evaluator_positive_.push_back(
true);
805 state_dependent_transit_evaluators_cache_.push_back(
806 absl::make_unique<StateDependentTransitCallbackCache>());
807 StateDependentTransitCallbackCache*
const cache =
808 state_dependent_transit_evaluators_cache_.back().get();
809 state_dependent_transit_evaluators_.push_back(
814 cache->insert({CacheKey(i, j),
value});
817 return state_dependent_transit_evaluators_.size() - 1;
820 void RoutingModel::AddNoCycleConstraintInternal() {
821 if (no_cycle_constraint_ ==
nullptr) {
822 no_cycle_constraint_ = solver_->MakeNoCycle(nexts_, active_);
823 solver_->AddConstraint(no_cycle_constraint_);
829 const std::string&
name) {
830 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
831 std::vector<int64> capacities(vehicles_,
capacity);
832 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
833 std::move(capacities),
834 fix_start_cumul_to_zero,
name);
839 bool fix_start_cumul_to_zero,
const std::string&
name) {
840 std::vector<int64> capacities(vehicles_,
capacity);
841 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
842 std::move(capacities),
843 fix_start_cumul_to_zero,
name);
847 int evaluator_index,
int64 slack_max, std::vector<int64> vehicle_capacities,
848 bool fix_start_cumul_to_zero,
const std::string&
name) {
849 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
850 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
851 std::move(vehicle_capacities),
852 fix_start_cumul_to_zero,
name);
856 const std::vector<int>& evaluator_indices,
int64 slack_max,
857 std::vector<int64> vehicle_capacities,
bool fix_start_cumul_to_zero,
858 const std::string&
name) {
859 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
860 std::move(vehicle_capacities),
861 fix_start_cumul_to_zero,
name);
864 bool RoutingModel::AddDimensionWithCapacityInternal(
865 const std::vector<int>& evaluator_indices,
int64 slack_max,
866 std::vector<int64> vehicle_capacities,
bool fix_start_cumul_to_zero,
867 const std::string&
name) {
868 CHECK_EQ(vehicles_, vehicle_capacities.size());
869 return InitializeDimensionInternal(
870 evaluator_indices, std::vector<int>(), slack_max, fix_start_cumul_to_zero,
874 bool RoutingModel::InitializeDimensionInternal(
875 const std::vector<int>& evaluator_indices,
876 const std::vector<int>& state_dependent_evaluator_indices,
int64 slack_max,
877 bool fix_start_cumul_to_zero, RoutingDimension* dimension) {
878 CHECK(dimension !=
nullptr);
879 CHECK_EQ(vehicles_, evaluator_indices.size());
880 CHECK((dimension->base_dimension_ ==
nullptr &&
881 state_dependent_evaluator_indices.empty()) ||
882 vehicles_ == state_dependent_evaluator_indices.size());
885 dimension_name_to_index_[dimension->name()] = dimension_index;
886 dimensions_.push_back(dimension);
887 dimension->Initialize(evaluator_indices, state_dependent_evaluator_indices,
889 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
890 nexts_, active_, dimension->cumuls(), dimension->transits()));
891 if (fix_start_cumul_to_zero) {
892 for (
int i = 0; i < vehicles_; ++i) {
893 IntVar*
const start_cumul = dimension->CumulVar(
Start(i));
895 start_cumul->SetValue(0);
906 RoutingModel*
model) {
908 return model->RegisterPositiveTransitCallback(std::move(
callback));
914 RoutingModel*
model) {
916 return model->RegisterPositiveUnaryTransitCallback(std::move(
callback));
918 return model->RegisterUnaryTransitCallback(std::move(
callback));
924 const std::string& dimension_name) {
927 slack_max,
capacity, fix_start_cumul_to_zero,
932 bool fix_start_cumul_to_zero,
933 const std::string& dimension_name) {
935 RegisterUnaryCallback(
936 [
this, values](
int64 i) {
940 std::all_of(std::begin(values), std::end(values),
941 [](
int64 transit) {
return transit >= 0; }),
943 0,
capacity, fix_start_cumul_to_zero, dimension_name);
948 bool fix_start_cumul_to_zero,
949 const std::string& dimension_name) {
950 bool all_transits_positive =
true;
951 for (
const std::vector<int64>& transit_values : values) {
952 all_transits_positive =
953 std::all_of(std::begin(transit_values), std::end(transit_values),
954 [](
int64 transit) {
return transit >= 0; });
955 if (!all_transits_positive) {
964 all_transits_positive,
this),
965 0,
capacity, fix_start_cumul_to_zero, dimension_name);
977 CHECK(callback_ !=
nullptr);
981 int64 Min()
const override {
983 const int idx_min = index_->Min();
984 const int idx_max = index_->Max() + 1;
985 return (idx_min < idx_max) ? callback_->RangeMin(idx_min, idx_max)
988 void SetMin(
int64 new_min)
override {
989 const int64 old_min = Min();
990 const int64 old_max = Max();
991 if (old_min < new_min && new_min <= old_max) {
992 const int64 old_idx_min = index_->Min();
993 const int64 old_idx_max = index_->Max() + 1;
994 if (old_idx_min < old_idx_max) {
995 const int64 new_idx_min = callback_->RangeFirstInsideInterval(
996 old_idx_min, old_idx_max, new_min, old_max + 1);
997 index_->SetMin(new_idx_min);
998 if (new_idx_min < old_idx_max) {
999 const int64 new_idx_max = callback_->RangeLastInsideInterval(
1000 new_idx_min, old_idx_max, new_min, old_max + 1);
1001 index_->SetMax(new_idx_max);
1006 int64 Max()
const override {
1008 const int idx_min = index_->Min();
1009 const int idx_max = index_->Max() + 1;
1010 return (idx_min < idx_max) ? callback_->RangeMax(idx_min, idx_max)
1013 void SetMax(
int64 new_max)
override {
1014 const int64 old_min = Min();
1015 const int64 old_max = Max();
1016 if (old_min <= new_max && new_max < old_max) {
1017 const int64 old_idx_min = index_->Min();
1018 const int64 old_idx_max = index_->Max() + 1;
1019 if (old_idx_min < old_idx_max) {
1020 const int64 new_idx_min = callback_->RangeFirstInsideInterval(
1021 old_idx_min, old_idx_max, old_min, new_max + 1);
1022 index_->SetMin(new_idx_min);
1023 if (new_idx_min < old_idx_max) {
1024 const int64 new_idx_max = callback_->RangeLastInsideInterval(
1025 new_idx_min, old_idx_max, old_min, new_max + 1);
1026 index_->SetMax(new_idx_max);
1031 void WhenRange(Demon* d)
override { index_->WhenRange(d); }
1034 const RangeIntToIntFunction*
const callback_;
1035 IntVar*
const index_;
1038 IntExpr* MakeRangeMakeElementExpr(
const RangeIntToIntFunction*
callback,
1039 IntVar*
index, Solver* s) {
1040 return s->RegisterIntExpr(
1046 const std::vector<int>& dependent_transits,
1048 std::vector<int64> vehicle_capacities,
bool fix_start_cumul_to_zero,
1049 const std::string&
name) {
1050 const std::vector<int> pure_transits(vehicles_, 0);
1052 pure_transits, dependent_transits, base_dimension, slack_max,
1053 std::move(vehicle_capacities), fix_start_cumul_to_zero,
name);
1058 int64 vehicle_capacity,
bool fix_start_cumul_to_zero,
1059 const std::string&
name) {
1061 0, transit, dimension, slack_max, vehicle_capacity,
1062 fix_start_cumul_to_zero,
name);
1065 bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacityInternal(
1066 const std::vector<int>& pure_transits,
1067 const std::vector<int>& dependent_transits,
1069 std::vector<int64> vehicle_capacities,
bool fix_start_cumul_to_zero,
1070 const std::string&
name) {
1071 CHECK_EQ(vehicles_, vehicle_capacities.size());
1073 if (base_dimension ==
nullptr) {
1075 name, RoutingDimension::SelfBased());
1078 name, base_dimension);
1080 return InitializeDimensionInternal(pure_transits, dependent_transits,
1081 slack_max, fix_start_cumul_to_zero,
1086 int pure_transit,
int dependent_transit,
1088 int64 vehicle_capacity,
bool fix_start_cumul_to_zero,
1089 const std::string&
name) {
1090 std::vector<int> pure_transits(vehicles_, pure_transit);
1091 std::vector<int> dependent_transits(vehicles_, dependent_transit);
1092 std::vector<int64> vehicle_capacities(vehicles_, vehicle_capacity);
1093 return AddDimensionDependentDimensionWithVehicleCapacityInternal(
1094 pure_transits, dependent_transits, base_dimension, slack_max,
1095 std::move(vehicle_capacities), fix_start_cumul_to_zero,
name);
1101 const std::function<
int64(
int64)> g = [&f](
int64 x) {
return f(x) + x; };
1109 std::vector<std::string> dimension_names;
1110 for (
const auto& dimension_name_index : dimension_name_to_index_) {
1111 dimension_names.push_back(dimension_name_index.first);
1113 std::sort(dimension_names.begin(), dimension_names.end());
1114 return dimension_names;
1120 if (dim_index < 0 || dim_index >= global_optimizer_index_.
size() ||
1121 global_optimizer_index_[dim_index] < 0) {
1124 const int optimizer_index = global_optimizer_index_[dim_index];
1125 DCHECK_LT(optimizer_index, global_dimension_optimizers_.size());
1126 return global_dimension_optimizers_[optimizer_index].get();
1132 if (dim_index < 0 || dim_index >= local_optimizer_index_.
size() ||
1133 local_optimizer_index_[dim_index] < 0) {
1136 const int optimizer_index = local_optimizer_index_[dim_index];
1137 DCHECK_LT(optimizer_index, local_dimension_optimizers_.size());
1138 return local_dimension_optimizers_[optimizer_index].get();
1144 if (dim_index < 0 || dim_index >= local_optimizer_index_.
size() ||
1145 local_optimizer_index_[dim_index] < 0) {
1148 const int optimizer_index = local_optimizer_index_[dim_index];
1149 DCHECK_LT(optimizer_index, local_dimension_mp_optimizers_.size());
1150 return local_dimension_mp_optimizers_[optimizer_index].get();
1158 const std::string& dimension_name)
const {
1164 const std::string& dimension_name)
const {
1165 return *dimensions_[
gtl::FindOrDie(dimension_name_to_index_, dimension_name)];
1169 const std::string& dimension_name)
const {
1172 return dimensions_[
index];
1179 for (
int i = 0; i < vehicles_; ++i) {
1187 CHECK_LT(evaluator_index, transit_evaluators_.size());
1188 vehicle_to_transit_cost_[vehicle] = evaluator_index;
1192 for (
int i = 0; i < vehicles_; ++i) {
1199 return fixed_cost_of_vehicle_[vehicle];
1205 fixed_cost_of_vehicle_[vehicle] =
cost;
1209 int64 linear_cost_factor,
int64 quadratic_cost_factor) {
1210 for (
int v = 0; v < vehicles_; v++) {
1217 int64 quadratic_cost_factor,
1222 if (linear_cost_factor + quadratic_cost_factor > 0) {
1223 vehicle_amortized_cost_factors_set_ =
true;
1225 linear_cost_factor_of_vehicle_[vehicle] = linear_cost_factor;
1226 quadratic_cost_factor_of_vehicle_[vehicle] = quadratic_cost_factor;
1232 struct CostClassComparator {
1239 struct VehicleClassComparator {
1240 bool operator()(
const RoutingModel::VehicleClass&
a,
1241 const RoutingModel::VehicleClass&
b)
const {
1251 void RoutingModel::ComputeCostClasses(
1254 cost_classes_.reserve(vehicles_);
1255 cost_classes_.clear();
1256 cost_class_index_of_vehicle_.assign(vehicles_,
CostClassIndex(-1));
1257 std::map<CostClass, CostClassIndex, CostClassComparator> cost_class_map;
1260 const CostClass zero_cost_class(0);
1261 cost_classes_.push_back(zero_cost_class);
1262 DCHECK_EQ(cost_classes_[kCostClassIndexOfZeroCost].evaluator_index, 0);
1263 cost_class_map[zero_cost_class] = kCostClassIndexOfZeroCost;
1268 has_vehicle_with_zero_cost_class_ =
false;
1269 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
1270 CostClass cost_class(vehicle_to_transit_cost_[vehicle]);
1274 const int64 coeff = dimension->vehicle_span_cost_coefficients()[vehicle];
1275 if (coeff == 0)
continue;
1276 cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1277 .push_back({dimension->vehicle_to_class(vehicle), coeff, dimension});
1279 std::sort(cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1281 cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1287 if (cost_class_index == kCostClassIndexOfZeroCost) {
1288 has_vehicle_with_zero_cost_class_ =
true;
1289 }
else if (cost_class_index == num_cost_classes) {
1290 cost_classes_.push_back(cost_class);
1292 cost_class_index_of_vehicle_[vehicle] = cost_class_index;
1306 costs_are_homogeneous_across_vehicles_ &= has_vehicle_with_zero_cost_class_
1313 return std::tie(
a.cost_class_index,
a.fixed_cost,
a.start_equivalence_class,
1314 a.end_equivalence_class,
a.unvisitable_nodes_fprint,
1315 a.dimension_start_cumuls_min,
a.dimension_start_cumuls_max,
1316 a.dimension_end_cumuls_min,
a.dimension_end_cumuls_max,
1317 a.dimension_capacities,
a.dimension_evaluator_classes) <
1318 std::tie(
b.cost_class_index,
b.fixed_cost,
b.start_equivalence_class,
1319 b.end_equivalence_class,
b.unvisitable_nodes_fprint,
1320 b.dimension_start_cumuls_min,
b.dimension_start_cumuls_max,
1321 b.dimension_end_cumuls_min,
b.dimension_end_cumuls_max,
1322 b.dimension_capacities,
b.dimension_evaluator_classes);
1325 void RoutingModel::ComputeVehicleClasses() {
1326 vehicle_classes_.reserve(vehicles_);
1327 vehicle_classes_.clear();
1329 std::map<VehicleClass, VehicleClassIndex, VehicleClassComparator>
1331 const int nodes_unvisitability_num_bytes = (vehicle_vars_.size() + 7) / 8;
1332 std::unique_ptr<char[]> nodes_unvisitability_bitmask(
1333 new char[nodes_unvisitability_num_bytes]);
1334 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
1337 vehicle_class.
fixed_cost = fixed_cost_of_vehicle_[vehicle];
1339 index_to_equivalence_class_[
Start(vehicle)];
1341 index_to_equivalence_class_[
End(vehicle)];
1343 IntVar*
const start_cumul_var = dimension->cumuls()[
Start(vehicle)];
1345 start_cumul_var->
Min());
1347 start_cumul_var->
Max());
1348 IntVar*
const end_cumul_var = dimension->cumuls()[
End(vehicle)];
1352 dimension->vehicle_capacities()[vehicle]);
1354 dimension->vehicle_to_class(vehicle));
1356 memset(nodes_unvisitability_bitmask.get(), 0,
1357 nodes_unvisitability_num_bytes);
1361 (!vehicle_var->
Contains(vehicle) ||
1363 nodes_unvisitability_bitmask[
index / CHAR_BIT] |= 1U
1364 << (
index % CHAR_BIT);
1368 nodes_unvisitability_bitmask.get(), nodes_unvisitability_num_bytes);
1371 &vehicle_class_map, vehicle_class, num_vehicle_classes);
1372 if (vehicle_class_index == num_vehicle_classes) {
1373 vehicle_classes_.push_back(vehicle_class);
1375 vehicle_class_index_of_vehicle_[vehicle] = vehicle_class_index;
1379 void RoutingModel::ComputeVehicleTypes() {
1380 const int nodes_squared = nodes_ * nodes_;
1381 std::vector<int>& type_index_of_vehicle =
1383 std::vector<std::set<VehicleTypeContainer::VehicleClassEntry>>&
1384 sorted_vehicle_classes_per_type =
1386 std::vector<std::deque<int>>& vehicles_per_vehicle_class =
1389 type_index_of_vehicle.resize(vehicles_);
1390 sorted_vehicle_classes_per_type.clear();
1391 sorted_vehicle_classes_per_type.reserve(vehicles_);
1392 vehicles_per_vehicle_class.clear();
1395 absl::flat_hash_map<int64, int> type_to_type_index;
1397 for (
int v = 0; v < vehicles_; v++) {
1401 const int64 type = cost_class * nodes_squared + start * nodes_ + end;
1403 const auto& vehicle_type_added = type_to_type_index.insert(
1404 std::make_pair(type, type_to_type_index.size()));
1406 const int index = vehicle_type_added.first->second;
1409 const VehicleTypeContainer::VehicleClassEntry class_entry = {
1412 if (vehicle_type_added.second) {
1415 sorted_vehicle_classes_per_type.push_back({class_entry});
1419 sorted_vehicle_classes_per_type[
index].insert(class_entry);
1421 vehicles_per_vehicle_class[vehicle_class].push_back(v);
1422 type_index_of_vehicle[v] =
index;
1426 void RoutingModel::FinalizeVisitTypes() {
1432 single_nodes_of_type_.clear();
1433 single_nodes_of_type_.resize(num_visit_types_);
1434 pair_indices_of_type_.clear();
1435 pair_indices_of_type_.resize(num_visit_types_);
1436 std::vector<absl::flat_hash_set<int>> pair_indices_added_for_type(
1441 if (visit_type < 0) {
1444 const std::vector<std::pair<int, int>>& pickup_index_pairs =
1445 index_to_pickup_index_pairs_[
index];
1446 const std::vector<std::pair<int, int>>& delivery_index_pairs =
1447 index_to_delivery_index_pairs_[
index];
1448 if (pickup_index_pairs.empty() && delivery_index_pairs.empty()) {
1449 single_nodes_of_type_[visit_type].push_back(
index);
1451 for (
const std::vector<std::pair<int, int>>* index_pairs :
1452 {&pickup_index_pairs, &delivery_index_pairs}) {
1453 for (
const std::pair<int, int>& index_pair : *index_pairs) {
1454 const int pair_index = index_pair.first;
1455 if (pair_indices_added_for_type[visit_type].insert(pair_index).second) {
1456 pair_indices_of_type_[visit_type].push_back(pair_index);
1462 TopologicallySortVisitTypes();
1465 void RoutingModel::TopologicallySortVisitTypes() {
1466 if (!has_same_vehicle_type_requirements_ &&
1467 !has_temporal_type_requirements_) {
1470 std::vector<std::pair<double, double>> type_requirement_tightness(
1471 num_visit_types_, {0, 0});
1472 std::vector<absl::flat_hash_set<int>> type_to_dependent_types(
1474 SparseBitset<> types_in_requirement_graph(num_visit_types_);
1475 std::vector<int> in_degree(num_visit_types_, 0);
1476 for (
int type = 0; type < num_visit_types_; type++) {
1477 int num_alternative_required_types = 0;
1478 int num_required_sets = 0;
1479 for (
const std::vector<absl::flat_hash_set<int>>*
1480 required_type_alternatives :
1481 {&required_type_alternatives_when_adding_type_index_[type],
1482 &required_type_alternatives_when_removing_type_index_[type],
1483 &same_vehicle_required_type_alternatives_per_type_index_[type]}) {
1484 for (
const absl::flat_hash_set<int>& alternatives :
1485 *required_type_alternatives) {
1486 types_in_requirement_graph.Set(type);
1487 num_required_sets++;
1488 for (
int required_type : alternatives) {
1489 type_requirement_tightness[required_type].second +=
1490 1.0 / alternatives.size();
1491 types_in_requirement_graph.Set(required_type);
1492 num_alternative_required_types++;
1493 if (type_to_dependent_types[required_type].insert(type).second) {
1499 if (num_alternative_required_types > 0) {
1500 type_requirement_tightness[type].first += 1.0 * num_required_sets *
1502 num_alternative_required_types;
1507 topologically_sorted_visit_types_.clear();
1508 std::vector<int> current_types_with_zero_indegree;
1509 for (
int type : types_in_requirement_graph.PositionsSetAtLeastOnce()) {
1510 DCHECK(type_requirement_tightness[type].first > 0 ||
1511 type_requirement_tightness[type].second > 0);
1512 if (in_degree[type] == 0) {
1513 current_types_with_zero_indegree.push_back(type);
1517 int num_types_added = 0;
1518 while (!current_types_with_zero_indegree.empty()) {
1521 topologically_sorted_visit_types_.push_back({});
1522 std::vector<int>& topological_group =
1523 topologically_sorted_visit_types_.back();
1524 std::vector<int> next_types_with_zero_indegree;
1525 for (
int type : current_types_with_zero_indegree) {
1526 topological_group.push_back(type);
1528 for (
int dependent_type : type_to_dependent_types[type]) {
1529 DCHECK_GT(in_degree[dependent_type], 0);
1530 if (--in_degree[dependent_type] == 0) {
1531 next_types_with_zero_indegree.push_back(dependent_type);
1542 std::sort(topological_group.begin(), topological_group.end(),
1543 [&type_requirement_tightness](
int type1,
int type2) {
1544 const auto& tightness1 = type_requirement_tightness[type1];
1545 const auto& tightness2 = type_requirement_tightness[type2];
1546 return tightness1 > tightness2 ||
1547 (tightness1 == tightness2 && type1 < type2);
1550 current_types_with_zero_indegree.swap(next_types_with_zero_indegree);
1553 const int num_types_in_requirement_graph =
1554 types_in_requirement_graph.NumberOfSetCallsWithDifferentArguments();
1555 DCHECK_LE(num_types_added, num_types_in_requirement_graph);
1556 if (num_types_added < num_types_in_requirement_graph) {
1558 topologically_sorted_visit_types_.clear();
1563 const std::vector<int64>& indices,
int64 penalty,
int64 max_cardinality) {
1565 for (
int i = 0; i < indices.size(); ++i) {
1570 disjunctions_.
push_back({indices, {penalty, max_cardinality}});
1572 index_to_disjunctions_[
index].push_back(disjunction_index);
1574 return disjunction_index;
1577 std::vector<std::pair<int64, int64>>
1579 std::vector<std::pair<int64, int64>> var_index_pairs;
1580 for (
const Disjunction& disjunction : disjunctions_) {
1581 const std::vector<int64>&
var_indices = disjunction.indices;
1585 if (index_to_disjunctions_[v0].size() == 1 &&
1586 index_to_disjunctions_[v1].size() == 1) {
1591 std::sort(var_index_pairs.begin(), var_index_pairs.end());
1592 return var_index_pairs;
1597 for (Disjunction& disjunction : disjunctions_) {
1598 bool has_one_potentially_active_var =
false;
1599 for (
const int64 var_index : disjunction.indices) {
1601 has_one_potentially_active_var =
true;
1605 if (!has_one_potentially_active_var) {
1606 disjunction.value.max_cardinality = 0;
1612 const std::vector<int64>& indices = disjunctions_[disjunction].indices;
1613 const int indices_size = indices.
size();
1614 std::vector<IntVar*> disjunction_vars(indices_size);
1615 for (
int i = 0; i < indices_size; ++i) {
1620 const int64 max_cardinality =
1621 disjunctions_[disjunction].value.max_cardinality;
1622 IntVar* no_active_var = solver_->MakeBoolVar();
1623 IntVar* number_active_vars = solver_->MakeIntVar(0, max_cardinality);
1624 solver_->AddConstraint(
1625 solver_->MakeSumEquality(disjunction_vars, number_active_vars));
1626 solver_->AddConstraint(solver_->MakeIsDifferentCstCt(
1627 number_active_vars, max_cardinality, no_active_var));
1628 const int64 penalty = disjunctions_[disjunction].value.penalty;
1630 no_active_var->SetMax(0);
1633 return solver_->MakeProd(no_active_var, penalty)->Var();
1638 const std::vector<int64>& indices,
int64 cost) {
1639 if (!indices.empty()) {
1640 ValuedNodes<int64> same_vehicle_cost;
1642 same_vehicle_cost.indices.push_back(
index);
1644 same_vehicle_cost.value =
cost;
1645 same_vehicle_costs_.push_back(same_vehicle_cost);
1651 auto& allowed_vehicles = allowed_vehicles_[
index];
1652 allowed_vehicles.clear();
1654 allowed_vehicles.insert(vehicle);
1659 AddPickupAndDeliverySetsInternal({pickup}, {delivery});
1668 pickup_delivery_disjunctions_.push_back(
1669 {pickup_disjunction, delivery_disjunction});
1672 void RoutingModel::AddPickupAndDeliverySetsInternal(
1673 const std::vector<int64>& pickups,
const std::vector<int64>& deliveries) {
1674 if (pickups.empty() || deliveries.empty()) {
1678 const int pair_index = pickup_delivery_pairs_.size();
1679 for (
int pickup_index = 0; pickup_index < pickups.size(); pickup_index++) {
1680 const int64 pickup = pickups[pickup_index];
1682 index_to_pickup_index_pairs_[pickup].emplace_back(pair_index, pickup_index);
1684 for (
int delivery_index = 0; delivery_index < deliveries.size();
1686 const int64 delivery = deliveries[delivery_index];
1688 index_to_delivery_index_pairs_[delivery].emplace_back(pair_index,
1691 pickup_delivery_pairs_.push_back({pickups, deliveries});
1695 int64 node_index)
const {
1696 CHECK_LT(node_index, index_to_pickup_index_pairs_.size());
1697 return index_to_pickup_index_pairs_[node_index];
1701 int64 node_index)
const {
1702 CHECK_LT(node_index, index_to_delivery_index_pairs_.size());
1703 return index_to_delivery_index_pairs_[node_index];
1709 vehicle_pickup_delivery_policy_[vehicle] = policy;
1715 for (
int i = 0; i < vehicles_; ++i) {
1723 return vehicle_pickup_delivery_policy_[vehicle];
1728 for (
int i = 0; i <
Nexts().size(); ++i) {
1738 IntVar* RoutingModel::CreateSameVehicleCost(
int vehicle_index) {
1739 const std::vector<int64>& indices =
1740 same_vehicle_costs_[vehicle_index].indices;
1741 CHECK(!indices.empty());
1742 std::vector<IntVar*> vehicle_counts;
1743 solver_->MakeIntVarArray(vehicle_vars_.size() + 1, 0, indices.size() + 1,
1745 std::vector<int64> vehicle_values(vehicle_vars_.size() + 1);
1746 for (
int i = 0; i < vehicle_vars_.size(); ++i) {
1747 vehicle_values[i] = i;
1749 vehicle_values[vehicle_vars_.size()] = -1;
1750 std::vector<IntVar*> vehicle_vars;
1751 vehicle_vars.reserve(indices.size());
1753 vehicle_vars.push_back(vehicle_vars_[
index]);
1755 solver_->AddConstraint(solver_->MakeDistribute(vehicle_vars, vehicle_counts));
1756 std::vector<IntVar*> vehicle_used;
1757 for (
int i = 0; i < vehicle_vars_.size() + 1; ++i) {
1758 vehicle_used.push_back(
1759 solver_->MakeIsGreaterOrEqualCstVar(vehicle_counts[i], 1));
1761 vehicle_used.push_back(solver_->MakeIntConst(-1));
1763 ->MakeProd(solver_->MakeMax(solver_->MakeSum(vehicle_used), 0),
1764 same_vehicle_costs_[vehicle_index].value)
1769 extra_operators_.push_back(ls_operator);
1776 void RoutingModel::AppendHomogeneousArcCosts(
1777 const RoutingSearchParameters&
parameters,
int node_index,
1778 std::vector<IntVar*>* cost_elements) {
1779 CHECK(cost_elements !=
nullptr);
1780 const auto arc_cost_evaluator = [
this, node_index](
int64 next_index) {
1787 IntVar*
const base_cost_var = solver_->MakeIntVar(0,
kint64max);
1788 solver_->AddConstraint(MakeLightElement(
1789 solver_.get(), base_cost_var, nexts_[node_index], arc_cost_evaluator,
1790 [
this]() { return enable_deep_serialization_; }));
1792 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1793 cost_elements->push_back(
var);
1795 IntExpr*
const expr =
1796 solver_->MakeElement(arc_cost_evaluator, nexts_[node_index]);
1797 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
1798 cost_elements->push_back(
var);
1802 void RoutingModel::AppendArcCosts(
const RoutingSearchParameters&
parameters,
1804 std::vector<IntVar*>* cost_elements) {
1805 CHECK(cost_elements !=
nullptr);
1811 IntVar*
const base_cost_var = solver_->MakeIntVar(0,
kint64max);
1812 solver_->AddConstraint(MakeLightElement2(
1813 solver_.get(), base_cost_var, nexts_[node_index],
1814 vehicle_vars_[node_index],
1815 [
this, node_index](
int64 to,
int64 vehicle) {
1816 return GetArcCostForVehicle(node_index, to, vehicle);
1818 [
this]() { return enable_deep_serialization_; }));
1820 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1821 cost_elements->push_back(
var);
1823 IntVar*
const vehicle_class_var =
1827 return SafeGetCostClassInt64OfVehicle(
index);
1829 vehicle_vars_[node_index])
1831 IntExpr*
const expr = solver_->MakeElement(
1835 nexts_[node_index], vehicle_class_var);
1836 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
1837 cost_elements->push_back(
var);
1841 int RoutingModel::GetVehicleStartClass(
int64 start_index)
const {
1842 const int vehicle = index_to_vehicle_[start_index];
1849 std::string RoutingModel::FindErrorInSearchParametersForModel(
1850 const RoutingSearchParameters& search_parameters)
const {
1852 search_parameters.first_solution_strategy();
1853 if (GetFirstSolutionDecisionBuilder(search_parameters) ==
nullptr) {
1854 return absl::StrCat(
1855 "Undefined first solution strategy: ",
1856 FirstSolutionStrategy::Value_Name(first_solution_strategy),
1857 " (int value: ", first_solution_strategy,
")");
1859 if (search_parameters.first_solution_strategy() ==
1860 FirstSolutionStrategy::SWEEP &&
1862 return "Undefined sweep arranger for ROUTING_SWEEP strategy.";
1867 void RoutingModel::QuietCloseModel() {
1878 same_vehicle_components_.SetNumberOfNodes(
model->Size());
1879 for (
const std::string&
name :
model->GetAllDimensionNames()) {
1881 const std::vector<IntVar*>& cumuls = dimension->
cumuls();
1882 for (
int i = 0; i < cumuls.size(); ++i) {
1883 cumul_to_dim_indices_[cumuls[i]] = {dimension, i};
1886 const std::vector<IntVar*>& vehicle_vars =
model->VehicleVars();
1887 for (
int i = 0; i < vehicle_vars.size(); ++i) {
1888 vehicle_var_to_indices_[vehicle_vars[i]] = i;
1890 RegisterInspectors();
1894 const std::vector<int> node_to_same_vehicle_component_id =
1895 same_vehicle_components_.GetComponentIds();
1896 model_->InitSameVehicleGroups(
1897 same_vehicle_components_.GetNumberOfComponents());
1898 for (
int node = 0; node < model_->Size(); ++node) {
1899 model_->SetSameVehicleGroup(node,
1900 node_to_same_vehicle_component_id[node]);
1906 const Constraint*
const constraint)
override {
1910 IntExpr*
const expr)
override {
1912 [](
const IntExpr* expr) {})(expr);
1915 const std::vector<int64>& values)
override {
1917 [](
const std::vector<int64>& int_array) {})(values);
1921 using ExprInspector = std::function<void(
const IntExpr*)>;
1922 using ArrayInspector = std::function<void(
const std::vector<int64>&)>;
1923 using ConstraintInspector = std::function<void()>;
1925 void RegisterInspectors() {
1926 expr_inspectors_[kExpressionArgument] = [
this](
const IntExpr* expr) {
1929 expr_inspectors_[kLeftArgument] = [
this](
const IntExpr* expr) {
1932 expr_inspectors_[kRightArgument] = [
this](
const IntExpr* expr) {
1935 array_inspectors_[kStartsArgument] =
1936 [
this](
const std::vector<int64>& int_array) {
1937 starts_argument_ = int_array;
1939 array_inspectors_[kEndsArgument] =
1940 [
this](
const std::vector<int64>& int_array) {
1941 ends_argument_ = int_array;
1943 constraint_inspectors_[kNotMember] = [
this]() {
1944 std::pair<RoutingDimension*, int> dim_index;
1947 const int index = dim_index.second;
1948 dimension->forbidden_intervals_[
index].InsertIntervals(starts_argument_,
1950 VLOG(2) << dimension->name() <<
" " <<
index <<
": "
1951 << dimension->forbidden_intervals_[
index].DebugString();
1954 starts_argument_.clear();
1955 ends_argument_.clear();
1957 constraint_inspectors_[kEquality] = [
this]() {
1959 int right_index = 0;
1960 if (
gtl::FindCopy(vehicle_var_to_indices_, left_, &left_index) &&
1961 gtl::FindCopy(vehicle_var_to_indices_, right_, &right_index)) {
1962 VLOG(2) <<
"Vehicle variables for " << left_index <<
" and "
1963 << right_index <<
" are equal.";
1964 same_vehicle_components_.AddEdge(left_index, right_index);
1969 constraint_inspectors_[kLessOrEqual] = [
this]() {
1970 std::pair<RoutingDimension*, int> left_index;
1971 std::pair<RoutingDimension*, int> right_index;
1972 if (
gtl::FindCopy(cumul_to_dim_indices_, left_, &left_index) &&
1973 gtl::FindCopy(cumul_to_dim_indices_, right_, &right_index)) {
1975 if (dimension == right_index.first) {
1976 VLOG(2) <<
"For dimension " << dimension->name() <<
", cumul for "
1977 << left_index.second <<
" is less than " << right_index.second
1979 dimension->path_precedence_graph_.AddArc(left_index.second,
1980 right_index.second);
1990 absl::flat_hash_map<const IntExpr*, std::pair<RoutingDimension*, int>>
1991 cumul_to_dim_indices_;
1992 absl::flat_hash_map<const IntExpr*, int> vehicle_var_to_indices_;
1993 absl::flat_hash_map<std::string, ExprInspector> expr_inspectors_;
1994 absl::flat_hash_map<std::string, ArrayInspector> array_inspectors_;
1995 absl::flat_hash_map<std::string, ConstraintInspector> constraint_inspectors_;
1996 const IntExpr*
expr_ =
nullptr;
1997 const IntExpr* left_ =
nullptr;
1998 const IntExpr* right_ =
nullptr;
1999 std::vector<int64> starts_argument_;
2000 std::vector<int64> ends_argument_;
2003 void RoutingModel::DetectImplicitPickupAndDeliveries() {
2004 std::vector<int> non_pickup_delivery_nodes;
2005 for (
int node = 0; node <
Size(); ++node) {
2008 non_pickup_delivery_nodes.push_back(node);
2012 std::set<std::pair<int64, int64>> implicit_pickup_deliveries;
2014 if (dimension->class_evaluators_.size() != 1) {
2019 if (transit ==
nullptr)
continue;
2020 absl::flat_hash_map<int64, std::vector<int64>> nodes_by_positive_demand;
2021 absl::flat_hash_map<int64, std::vector<int64>> nodes_by_negative_demand;
2022 for (
int node : non_pickup_delivery_nodes) {
2025 nodes_by_positive_demand[
demand].push_back(node);
2027 nodes_by_negative_demand[-
demand].push_back(node);
2030 for (
const auto& [
demand, positive_nodes] : nodes_by_positive_demand) {
2031 const std::vector<int64>*
const negative_nodes =
2033 if (negative_nodes !=
nullptr) {
2034 for (
int64 positive_node : positive_nodes) {
2035 for (
int64 negative_node : *negative_nodes) {
2036 implicit_pickup_deliveries.insert({positive_node, negative_node});
2042 implicit_pickup_delivery_pairs_without_alternatives_.clear();
2043 for (
auto [pickup, delivery] : implicit_pickup_deliveries) {
2044 implicit_pickup_delivery_pairs_without_alternatives_.emplace_back(
2045 std::vector<int64>({pickup}), std::vector<int64>({delivery}));
2052 if (!error.empty()) {
2054 LOG(
ERROR) <<
"Invalid RoutingSearchParameters: " << error;
2064 dimension->CloseModel(UsesLightPropagation(
parameters));
2067 ComputeVehicleClasses();
2068 ComputeVehicleTypes();
2069 FinalizeVisitTypes();
2070 vehicle_start_class_callback_ = [
this](
int64 start) {
2071 return GetVehicleStartClass(start);
2074 AddNoCycleConstraintInternal();
2076 const int size =
Size();
2079 for (
int i = 0; i < vehicles_; ++i) {
2080 const int64 start = starts_[i];
2081 const int64 end = ends_[i];
2082 solver_->AddConstraint(
2083 solver_->MakeEquality(vehicle_vars_[start], solver_->MakeIntConst(i)));
2084 solver_->AddConstraint(
2085 solver_->MakeEquality(vehicle_vars_[end], solver_->MakeIntConst(i)));
2086 solver_->AddConstraint(
2087 solver_->MakeIsDifferentCstCt(nexts_[start], end, vehicle_active_[i]));
2088 if (consider_empty_route_costs_[i]) {
2089 vehicle_costs_considered_[i]->SetMin(1);
2091 solver_->AddConstraint(solver_->MakeEquality(
2092 vehicle_active_[i], vehicle_costs_considered_[i]));
2097 if (vehicles_ > max_active_vehicles_) {
2098 solver_->AddConstraint(
2099 solver_->MakeSumLessOrEqual(vehicle_active_, max_active_vehicles_));
2107 if (vehicles_ > 1) {
2108 std::vector<IntVar*> zero_transit(size, solver_->MakeIntConst(0));
2109 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
2110 nexts_, active_, vehicle_vars_, zero_transit));
2115 for (
int i = 0; i < size; ++i) {
2117 active_[i]->SetValue(1);
2123 const absl::flat_hash_set<VisitTypePolicy>*
const infeasible_policies =
2125 if (infeasible_policies !=
nullptr &&
2127 active_[i]->SetValue(0);
2132 for (
int i = 0; i < allowed_vehicles_.size(); ++i) {
2133 const auto& allowed_vehicles = allowed_vehicles_[i];
2134 if (!allowed_vehicles.empty()) {
2136 vehicles.reserve(allowed_vehicles.size() + 1);
2138 for (
int vehicle : allowed_vehicles) {
2146 for (
int i = 0; i < size; ++i) {
2148 solver_->AddConstraint(solver_->RevAlloc(
2149 new DifferentFromValues(solver_.get(), nexts_[i], starts_)));
2151 solver_->AddConstraint(
2152 solver_->MakeIsDifferentCstCt(nexts_[i], i, active_[i]));
2157 for (
int i = 0; i < size; ++i) {
2158 solver_->AddConstraint(
2159 solver_->MakeIsDifferentCstCt(vehicle_vars_[i], -1, active_[i]));
2163 solver_->AddConstraint(
2168 for (
int i = 0; i < vehicles_; ++i) {
2169 std::vector<int64> forbidden_ends;
2170 forbidden_ends.reserve(vehicles_ - 1);
2171 for (
int j = 0; j < vehicles_; ++j) {
2173 forbidden_ends.push_back(ends_[j]);
2176 solver_->AddConstraint(solver_->RevAlloc(
new DifferentFromValues(
2177 solver_.get(), nexts_[starts_[i]], std::move(forbidden_ends))));
2181 for (
const int64 end : ends_) {
2182 is_bound_to_end_[end]->SetValue(1);
2185 std::vector<IntVar*> cost_elements;
2187 if (vehicles_ > 0) {
2188 for (
int node_index = 0; node_index < size; ++node_index) {
2190 AppendHomogeneousArcCosts(
parameters, node_index, &cost_elements);
2192 AppendArcCosts(
parameters, node_index, &cost_elements);
2195 if (vehicle_amortized_cost_factors_set_) {
2196 std::vector<IntVar*> route_lengths;
2197 solver_->MakeIntVarArray(vehicles_, 0, size, &route_lengths);
2198 solver_->AddConstraint(
2199 solver_->MakeDistribute(vehicle_vars_, route_lengths));
2200 std::vector<IntVar*> vehicle_used;
2201 for (
int i = 0; i < vehicles_; i++) {
2203 vehicle_used.push_back(
2204 solver_->MakeIsGreaterCstVar(route_lengths[i], 2));
2207 ->MakeProd(solver_->MakeOpposite(solver_->MakeSquare(
2208 solver_->MakeSum(route_lengths[i], -2))),
2209 quadratic_cost_factor_of_vehicle_[i])
2211 cost_elements.push_back(
var);
2213 IntVar*
const vehicle_usage_cost =
2214 solver_->MakeScalProd(vehicle_used, linear_cost_factor_of_vehicle_)
2216 cost_elements.push_back(vehicle_usage_cost);
2221 dimension->SetupGlobalSpanCost(&cost_elements);
2222 dimension->SetupSlackAndDependentTransitCosts();
2223 const std::vector<int64>& span_costs =
2224 dimension->vehicle_span_cost_coefficients();
2225 const std::vector<int64>& span_ubs = dimension->vehicle_span_upper_bounds();
2226 const bool has_span_constraint =
2227 std::any_of(span_costs.begin(), span_costs.end(),
2228 [](
int64 coeff) { return coeff != 0; }) ||
2229 std::any_of(span_ubs.begin(), span_ubs.end(),
2230 [](
int64 value) { return value < kint64max; }) ||
2231 dimension->HasSoftSpanUpperBounds() ||
2232 dimension->HasQuadraticCostSoftSpanUpperBounds();
2233 if (has_span_constraint) {
2234 std::vector<IntVar*> spans(
vehicles(),
nullptr);
2235 std::vector<IntVar*> total_slacks(
vehicles(),
nullptr);
2237 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2239 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2241 if (span_costs[vehicle] != 0) {
2242 total_slacks[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2245 if (dimension->HasSoftSpanUpperBounds()) {
2246 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2247 if (spans[vehicle])
continue;
2249 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2250 if (bound_cost.
cost == 0)
continue;
2251 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2254 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2255 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2256 if (spans[vehicle])
continue;
2258 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2259 if (bound_cost.
cost == 0)
continue;
2260 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2263 solver_->AddConstraint(
2267 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2268 if (!spans[vehicle] && !total_slacks[vehicle])
continue;
2269 if (spans[vehicle]) {
2278 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2279 if (span_costs[vehicle] == 0)
continue;
2280 DCHECK(total_slacks[vehicle] !=
nullptr);
2281 IntVar*
const slack_amount =
2283 ->MakeProd(vehicle_costs_considered_[vehicle],
2284 total_slacks[vehicle])
2286 IntVar*
const slack_cost =
2287 solver_->MakeProd(slack_amount, span_costs[vehicle])->Var();
2288 cost_elements.push_back(slack_cost);
2290 span_costs[vehicle]);
2292 if (dimension->HasSoftSpanUpperBounds()) {
2293 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2294 const auto bound_cost =
2295 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2296 if (bound_cost.cost == 0 || bound_cost.bound ==
kint64max)
continue;
2297 DCHECK(spans[vehicle] !=
nullptr);
2300 IntVar*
const span_violation_amount =
2303 vehicle_costs_considered_[vehicle],
2305 solver_->MakeSum(spans[vehicle], -bound_cost.bound),
2308 IntVar*
const span_violation_cost =
2309 solver_->MakeProd(span_violation_amount, bound_cost.cost)->Var();
2310 cost_elements.push_back(span_violation_cost);
2315 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2316 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2317 const auto bound_cost =
2318 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2319 if (bound_cost.cost == 0 || bound_cost.bound ==
kint64max)
continue;
2320 DCHECK(spans[vehicle] !=
nullptr);
2323 IntExpr* max0 = solver_->MakeMax(
2324 solver_->MakeSum(spans[vehicle], -bound_cost.bound), 0);
2325 IntVar*
const squared_span_violation_amount =
2327 ->MakeProd(vehicle_costs_considered_[vehicle],
2328 solver_->MakeSquare(max0))
2330 IntVar*
const span_violation_cost =
2331 solver_->MakeProd(squared_span_violation_amount, bound_cost.cost)
2333 cost_elements.push_back(span_violation_cost);
2342 IntVar* penalty_var = CreateDisjunction(i);
2343 if (penalty_var !=
nullptr) {
2344 cost_elements.push_back(penalty_var);
2349 dimension->SetupCumulVarSoftLowerBoundCosts(&cost_elements);
2350 dimension->SetupCumulVarSoftUpperBoundCosts(&cost_elements);
2351 dimension->SetupCumulVarPiecewiseLinearCosts(&cost_elements);
2354 for (
int i = 0; i < same_vehicle_costs_.size(); ++i) {
2355 cost_elements.push_back(CreateSameVehicleCost(i));
2357 cost_ = solver_->MakeSum(cost_elements)->Var();
2361 std::vector<std::pair<int, int>> pickup_delivery_precedences;
2362 for (
const auto& pair : pickup_delivery_pairs_) {
2363 DCHECK(!pair.first.empty() && !pair.second.empty());
2364 for (
int pickup : pair.first) {
2365 for (
int delivery : pair.second) {
2366 pickup_delivery_precedences.emplace_back(pickup, delivery);
2370 std::vector<int> lifo_vehicles;
2371 std::vector<int> fifo_vehicles;
2372 for (
int i = 0; i < vehicles_; ++i) {
2373 switch (vehicle_pickup_delivery_policy_[i]) {
2377 lifo_vehicles.push_back(
Start(i));
2380 fifo_vehicles.push_back(
Start(i));
2384 solver_->AddConstraint(solver_->MakePathPrecedenceConstraint(
2385 nexts_, pickup_delivery_precedences, lifo_vehicles, fifo_vehicles));
2388 enable_deep_serialization_ =
false;
2389 std::unique_ptr<RoutingModelInspector> inspector(
2391 solver_->Accept(inspector.get());
2392 enable_deep_serialization_ =
true;
2398 dimension->GetPathPrecedenceGraph();
2399 std::vector<std::pair<int, int>> path_precedences;
2401 for (
const auto head : graph[
tail]) {
2402 path_precedences.emplace_back(
tail,
head);
2405 if (!path_precedences.empty()) {
2406 solver_->AddConstraint(solver_->MakePathTransitPrecedenceConstraint(
2407 nexts_, dimension->transits(), path_precedences));
2412 dimension->GetNodePrecedences()) {
2413 const int64 first_node = node_precedence.first_node;
2414 const int64 second_node = node_precedence.second_node;
2415 IntExpr*
const nodes_are_selected =
2416 solver_->MakeMin(active_[first_node], active_[second_node]);
2417 IntExpr*
const cumul_difference = solver_->MakeDifference(
2418 dimension->CumulVar(second_node), dimension->CumulVar(first_node));
2419 IntVar*
const cumul_difference_is_ge_offset =
2420 solver_->MakeIsGreaterOrEqualCstVar(cumul_difference,
2421 node_precedence.offset);
2424 solver_->AddConstraint(solver_->MakeLessOrEqual(
2425 nodes_are_selected->
Var(), cumul_difference_is_ge_offset));
2429 DetectImplicitPickupAndDeliveries();
2438 CreateFirstSolutionDecisionBuilders(
parameters);
2439 error = FindErrorInSearchParametersForModel(
parameters);
2440 if (!error.empty()) {
2442 LOG(
ERROR) <<
"Invalid RoutingSearchParameters for this model: " << error;
2449 Link(std::pair<int, int> link,
double value,
int vehicle_class,
2453 vehicle_class(vehicle_class),
2454 start_depot(start_depot),
2455 end_depot(end_depot) {}
2479 bool check_assignment,
int64 num_indices,
2480 const std::vector<Link>& links_list)
2481 : assignment_(assignment),
2483 check_assignment_(check_assignment),
2484 solver_(model_->
solver()),
2485 num_indices_(num_indices),
2486 links_list_(links_list),
2487 nexts_(model_->
Nexts()),
2488 in_route_(num_indices_, -1),
2490 index_to_chain_index_(num_indices, -1),
2491 index_to_vehicle_class_index_(num_indices, -1) {
2493 const std::vector<std::string> dimension_names =
2494 model_->GetAllDimensionNames();
2495 dimensions_.assign(dimension_names.size(),
nullptr);
2496 for (
int i = 0; i < dimension_names.size(); ++i) {
2497 dimensions_[i] = &model_->GetDimensionOrDie(dimension_names[i]);
2500 cumuls_.resize(dimensions_.size());
2501 for (std::vector<int64>& cumuls :
cumuls_) {
2502 cumuls.resize(num_indices_);
2504 new_possible_cumuls_.resize(dimensions_.size());
2510 model_->solver()->TopPeriodicCheck();
2513 if (!model_->IsStart(
index) && !model_->IsEnd(
index)) {
2514 std::vector<int> route(1,
index);
2515 routes_.push_back(route);
2516 in_route_[
index] = routes_.size() - 1;
2520 for (
const Link& link : links_list_) {
2521 model_->solver()->TopPeriodicCheck();
2522 const int index1 = link.link.first;
2523 const int index2 = link.link.second;
2524 const int vehicle_class = link.vehicle_class;
2525 const int64 start_depot = link.start_depot;
2526 const int64 end_depot = link.end_depot;
2529 if (index_to_vehicle_class_index_[index1] < 0) {
2530 for (
int dimension_index = 0; dimension_index < dimensions_.size();
2531 ++dimension_index) {
2532 cumuls_[dimension_index][index1] =
2533 std::max(dimensions_[dimension_index]->GetTransitValue(
2534 start_depot, index1, 0),
2535 dimensions_[dimension_index]->CumulVar(index1)->Min());
2538 if (index_to_vehicle_class_index_[index2] < 0) {
2539 for (
int dimension_index = 0; dimension_index < dimensions_.size();
2540 ++dimension_index) {
2541 cumuls_[dimension_index][index2] =
2542 std::max(dimensions_[dimension_index]->GetTransitValue(
2543 start_depot, index2, 0),
2544 dimensions_[dimension_index]->CumulVar(index2)->Min());
2548 const int route_index1 = in_route_[index1];
2549 const int route_index2 = in_route_[index2];
2551 route_index1 >= 0 && route_index2 >= 0 &&
2552 FeasibleMerge(routes_[route_index1], routes_[route_index2], index1,
2553 index2, route_index1, route_index2, vehicle_class,
2554 start_depot, end_depot);
2555 if (Merge(merge, route_index1, route_index2)) {
2556 index_to_vehicle_class_index_[index1] = vehicle_class;
2557 index_to_vehicle_class_index_[index2] = vehicle_class;
2561 model_->solver()->TopPeriodicCheck();
2565 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
2567 final_chains_.push_back(chains_[chain_index]);
2570 std::sort(final_chains_.begin(), final_chains_.end(), ChainComparator);
2571 for (
int route_index = 0; route_index < routes_.size(); ++route_index) {
2573 final_routes_.push_back(routes_[route_index]);
2576 std::sort(final_routes_.begin(), final_routes_.end(), RouteComparator);
2578 const int extra_vehicles =
std::max(
2579 0,
static_cast<int>(final_chains_.size()) - model_->vehicles());
2581 int chain_index = 0;
2582 for (chain_index = extra_vehicles; chain_index < final_chains_.size();
2584 if (chain_index - extra_vehicles >= model_->vehicles()) {
2587 const int start = final_chains_[chain_index].head;
2588 const int end = final_chains_[chain_index].tail;
2590 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
2591 assignment_->SetValue(
2592 model_->NextVar(model_->Start(chain_index - extra_vehicles)), start);
2593 assignment_->Add(nexts_[end]);
2594 assignment_->SetValue(nexts_[end],
2595 model_->End(chain_index - extra_vehicles));
2599 for (
int route_index = 0; route_index < final_routes_.size();
2601 if (chain_index - extra_vehicles >= model_->vehicles()) {
2604 DCHECK_LT(route_index, final_routes_.size());
2605 const int head = final_routes_[route_index].front();
2606 const int tail = final_routes_[route_index].back();
2609 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
2610 assignment_->SetValue(
2611 model_->NextVar(model_->Start(chain_index - extra_vehicles)),
head);
2612 assignment_->Add(nexts_[
tail]);
2613 assignment_->SetValue(nexts_[
tail],
2614 model_->End(chain_index - extra_vehicles));
2622 if (!assignment_->Contains(
next)) {
2623 assignment_->Add(
next);
2632 return final_routes_;
2636 enum MergeStatus { FIRST_SECOND, SECOND_FIRST, NO_MERGE };
2639 bool operator()(
const std::vector<int>& route1,
2640 const std::vector<int>& route2)
const {
2641 return (route1.size() < route2.size());
2652 bool operator()(
const Chain& chain1,
const Chain& chain2)
const {
2653 return (chain1.nodes < chain2.nodes);
2657 bool Head(
int node)
const {
2658 return (node == routes_[in_route_[node]].front());
2661 bool Tail(
int node)
const {
2662 return (node == routes_[in_route_[node]].back());
2665 bool FeasibleRoute(
const std::vector<int>& route,
int64 route_cumul,
2666 int dimension_index) {
2668 std::vector<int>::const_iterator it = route.begin();
2669 int64 cumul = route_cumul;
2670 while (it != route.end()) {
2671 const int previous = *it;
2672 const int64 cumul_previous = cumul;
2676 if (it == route.end()) {
2679 const int next = *it;
2680 int64 available_from_previous =
2681 cumul_previous + dimension.GetTransitValue(previous,
next, 0);
2682 int64 available_cumul_next =
2685 const int64 slack = available_cumul_next - available_from_previous;
2686 if (slack > dimension.SlackVar(previous)->Max()) {
2687 available_cumul_next =
2688 available_from_previous + dimension.SlackVar(previous)->Max();
2691 if (available_cumul_next > dimension.CumulVar(
next)->Max()) {
2694 if (available_cumul_next <=
cumuls_[dimension_index][
next]) {
2697 cumul = available_cumul_next;
2702 bool CheckRouteConnection(
const std::vector<int>& route1,
2703 const std::vector<int>& route2,
int dimension_index,
2705 const int tail1 = route1.back();
2706 const int head2 = route2.front();
2707 const int tail2 = route2.back();
2709 int non_depot_node = -1;
2710 for (
int node = 0; node < num_indices_; ++node) {
2711 if (!model_->IsStart(node) && !model_->IsEnd(node)) {
2712 non_depot_node = node;
2717 const int64 depot_threashold =
2718 std::max(dimension.SlackVar(non_depot_node)->Max(),
2719 dimension.CumulVar(non_depot_node)->Max());
2721 int64 available_from_tail1 =
cumuls_[dimension_index][tail1] +
2722 dimension.GetTransitValue(tail1, head2, 0);
2723 int64 new_available_cumul_head2 =
2726 const int64 slack = new_available_cumul_head2 - available_from_tail1;
2727 if (slack > dimension.SlackVar(tail1)->Max()) {
2728 new_available_cumul_head2 =
2729 available_from_tail1 + dimension.SlackVar(tail1)->Max();
2732 bool feasible_route =
true;
2733 if (new_available_cumul_head2 > dimension.CumulVar(head2)->Max()) {
2736 if (new_available_cumul_head2 <=
cumuls_[dimension_index][head2]) {
2741 FeasibleRoute(route2, new_available_cumul_head2, dimension_index);
2742 const int64 new_possible_cumul_tail2 =
2744 ? new_possible_cumuls_[dimension_index][tail2]
2745 :
cumuls_[dimension_index][tail2];
2747 if (!feasible_route || (new_possible_cumul_tail2 +
2748 dimension.GetTransitValue(tail2, end_depot, 0) >
2749 depot_threashold)) {
2755 bool FeasibleMerge(
const std::vector<int>& route1,
2756 const std::vector<int>& route2,
int node1,
int node2,
2757 int route_index1,
int route_index2,
int vehicle_class,
2759 if ((route_index1 == route_index2) || !(Tail(node1) && Head(node2))) {
2764 if (!((index_to_vehicle_class_index_[node1] == -1 &&
2765 index_to_vehicle_class_index_[node2] == -1) ||
2766 (index_to_vehicle_class_index_[node1] == vehicle_class &&
2767 index_to_vehicle_class_index_[node2] == -1) ||
2768 (index_to_vehicle_class_index_[node1] == -1 &&
2769 index_to_vehicle_class_index_[node2] == vehicle_class) ||
2770 (index_to_vehicle_class_index_[node1] == vehicle_class &&
2771 index_to_vehicle_class_index_[node2] == vehicle_class))) {
2777 for (
int dimension_index = 0; dimension_index < dimensions_.size();
2778 ++dimension_index) {
2779 new_possible_cumuls_[dimension_index].clear();
2780 merge = merge && CheckRouteConnection(route1, route2, dimension_index,
2781 start_depot, end_depot);
2789 bool CheckTempAssignment(Assignment*
const temp_assignment,
2790 int new_chain_index,
int old_chain_index,
int head1,
2791 int tail1,
int head2,
int tail2) {
2794 if (new_chain_index >= model_->vehicles())
return false;
2795 const int start = head1;
2796 temp_assignment->Add(model_->NextVar(model_->Start(new_chain_index)));
2797 temp_assignment->SetValue(model_->NextVar(model_->Start(new_chain_index)),
2799 temp_assignment->Add(nexts_[tail1]);
2800 temp_assignment->SetValue(nexts_[tail1], head2);
2801 temp_assignment->Add(nexts_[tail2]);
2802 temp_assignment->SetValue(nexts_[tail2], model_->End(new_chain_index));
2803 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
2804 if ((chain_index != new_chain_index) &&
2805 (chain_index != old_chain_index) &&
2807 const int start = chains_[chain_index].head;
2808 const int end = chains_[chain_index].tail;
2809 temp_assignment->Add(model_->NextVar(model_->Start(chain_index)));
2810 temp_assignment->SetValue(model_->NextVar(model_->Start(chain_index)),
2812 temp_assignment->Add(nexts_[end]);
2813 temp_assignment->SetValue(nexts_[end], model_->End(chain_index));
2816 return solver_->Solve(solver_->MakeRestoreAssignment(temp_assignment));
2819 bool UpdateAssignment(
const std::vector<int>& route1,
2820 const std::vector<int>& route2) {
2821 bool feasible =
true;
2822 const int head1 = route1.front();
2823 const int tail1 = route1.back();
2824 const int head2 = route2.front();
2825 const int tail2 = route2.back();
2826 const int chain_index1 = index_to_chain_index_[head1];
2827 const int chain_index2 = index_to_chain_index_[head2];
2828 if (chain_index1 < 0 && chain_index2 < 0) {
2829 const int chain_index = chains_.size();
2830 if (check_assignment_) {
2831 Assignment*
const temp_assignment =
2832 solver_->MakeAssignment(assignment_);
2833 feasible = CheckTempAssignment(temp_assignment, chain_index, -1, head1,
2834 tail1, head2, tail2);
2841 index_to_chain_index_[head1] = chain_index;
2842 index_to_chain_index_[tail2] = chain_index;
2843 chains_.push_back(chain);
2845 }
else if (chain_index1 >= 0 && chain_index2 < 0) {
2846 if (check_assignment_) {
2847 Assignment*
const temp_assignment =
2848 solver_->MakeAssignment(assignment_);
2850 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
2851 head1, tail1, head2, tail2);
2854 index_to_chain_index_[tail2] = chain_index1;
2855 chains_[chain_index1].head = head1;
2856 chains_[chain_index1].tail = tail2;
2857 ++chains_[chain_index1].nodes;
2859 }
else if (chain_index1 < 0 && chain_index2 >= 0) {
2860 if (check_assignment_) {
2861 Assignment*
const temp_assignment =
2862 solver_->MakeAssignment(assignment_);
2864 CheckTempAssignment(temp_assignment, chain_index2, chain_index1,
2865 head1, tail1, head2, tail2);
2868 index_to_chain_index_[head1] = chain_index2;
2869 chains_[chain_index2].head = head1;
2870 chains_[chain_index2].tail = tail2;
2871 ++chains_[chain_index2].nodes;
2874 if (check_assignment_) {
2875 Assignment*
const temp_assignment =
2876 solver_->MakeAssignment(assignment_);
2878 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
2879 head1, tail1, head2, tail2);
2882 index_to_chain_index_[tail2] = chain_index1;
2883 chains_[chain_index1].head = head1;
2884 chains_[chain_index1].tail = tail2;
2885 chains_[chain_index1].nodes += chains_[chain_index2].nodes;
2886 deleted_chains_.insert(chain_index2);
2890 assignment_->Add(nexts_[tail1]);
2891 assignment_->SetValue(nexts_[tail1], head2);
2896 bool Merge(
bool merge,
int index1,
int index2) {
2898 if (UpdateAssignment(routes_[index1], routes_[index2])) {
2900 for (
const int node : routes_[index2]) {
2901 in_route_[node] = index1;
2902 routes_[index1].push_back(node);
2904 for (
int dimension_index = 0; dimension_index < dimensions_.size();
2905 ++dimension_index) {
2906 for (
const std::pair<int, int64> new_possible_cumul :
2907 new_possible_cumuls_[dimension_index]) {
2908 cumuls_[dimension_index][new_possible_cumul.first] =
2909 new_possible_cumul.second;
2912 deleted_routes_.insert(index2);
2919 Assignment*
const assignment_;
2921 const bool check_assignment_;
2922 Solver*
const solver_;
2923 const int64 num_indices_;
2924 const std::vector<Link> links_list_;
2925 std::vector<IntVar*> nexts_;
2926 std::vector<const RoutingDimension*> dimensions_;
2927 std::vector<std::vector<int64>>
cumuls_;
2928 std::vector<absl::flat_hash_map<int, int64>> new_possible_cumuls_;
2929 std::vector<std::vector<int>> routes_;
2930 std::vector<int> in_route_;
2931 absl::flat_hash_set<int> deleted_routes_;
2932 std::vector<std::vector<int>> final_routes_;
2933 std::vector<Chain> chains_;
2934 absl::flat_hash_set<int> deleted_chains_;
2935 std::vector<Chain> final_chains_;
2936 std::vector<int> index_to_chain_index_;
2937 std::vector<int> index_to_vehicle_class_index_;
2943 :
index(
index), angle(angle), distance(distance) {}
2964 : coordinates_(2 * points.size(), 0), sectors_(1) {
2965 for (
int64 i = 0; i < points.size(); ++i) {
2966 coordinates_[2 * i] = points[i].first;
2967 coordinates_[2 * i + 1] = points[i].second;
2974 const double pi_rad = 3.14159265;
2976 const int x0 = coordinates_[0];
2977 const int y0 = coordinates_[1];
2979 std::vector<SweepIndex> sweep_indices;
2980 for (
int64 index = 0; index < static_cast<int>(coordinates_.size()) / 2;
2982 const int x = coordinates_[2 *
index];
2983 const int y = coordinates_[2 *
index + 1];
2984 const double x_delta = x - x0;
2985 const double y_delta = y - y0;
2986 double square_distance = x_delta * x_delta + y_delta * y_delta;
2987 double angle = square_distance == 0 ? 0 : std::atan2(y_delta, x_delta);
2988 angle = angle >= 0 ? angle : 2 * pi_rad + angle;
2990 sweep_indices.push_back(sweep_index);
2992 std::sort(sweep_indices.begin(), sweep_indices.end(),
2995 const int size =
static_cast<int>(sweep_indices.size()) / sectors_;
2996 for (
int sector = 0; sector < sectors_; ++sector) {
2997 std::vector<SweepIndex> cluster;
2998 std::vector<SweepIndex>::iterator begin =
2999 sweep_indices.begin() + sector * size;
3000 std::vector<SweepIndex>::iterator end =
3001 sector == sectors_ - 1 ? sweep_indices.end()
3002 : sweep_indices.begin() + (sector + 1) * size;
3005 for (
const SweepIndex& sweep_index : sweep_indices) {
3006 indices->push_back(sweep_index.index);
3016 : model_(
model), check_assignment_(check_assignment) {}
3025 route_constructor_ = absl::make_unique<RouteConstructor>(
3026 assignment, model_, check_assignment_, num_indices_, links_);
3028 route_constructor_->Construct();
3029 route_constructor_.reset(
nullptr);
3038 const int depot = model_->
GetDepot();
3040 if (absl::GetFlag(FLAGS_sweep_sectors) > 0 &&
3041 absl::GetFlag(FLAGS_sweep_sectors) < num_indices_) {
3044 std::vector<int64> indices;
3046 for (
int i = 0; i < indices.size() - 1; ++i) {
3047 const int64 first = indices[i];
3048 const int64 second = indices[i + 1];
3049 if ((model_->
IsStart(first) || !model_->
IsEnd(first)) &&
3051 if (first != depot && second != depot) {
3052 Link link(std::make_pair(first, second), 0, 0, depot, depot);
3053 links_.push_back(link);
3059 RoutingModel*
const model_;
3060 std::unique_ptr<RouteConstructor> route_constructor_;
3061 const bool check_assignment_;
3063 std::vector<Link> links_;
3071 class AllUnperformed :
public DecisionBuilder {
3074 explicit AllUnperformed(RoutingModel*
const model) : model_(
model) {}
3075 ~AllUnperformed()
override {}
3076 Decision* Next(Solver*
const solver)
override {
3079 model_->CostVar()->FreezeQueue();
3080 for (
int i = 0; i < model_->Size(); ++i) {
3081 if (!model_->IsStart(i)) {
3082 model_->ActiveVar(i)->SetValue(0);
3085 model_->CostVar()->UnfreezeQueue();
3090 RoutingModel*
const model_;
3095 monitors_.push_back(monitor);
3101 AtSolutionCallbackMonitor(
Solver* solver, std::function<
void()>
callback)
3103 bool AtSolution()
override {
3109 std::function<void()> callback_;
3115 new AtSolutionCallbackMonitor(solver_.get(), std::move(
callback))));
3125 std::vector<const Assignment*>* solutions) {
3130 absl::Duration GetTimeLimit(
const RoutingSearchParameters&
parameters) {
3131 if (!
parameters.has_time_limit())
return absl::InfiniteDuration();
3135 absl::Duration GetLnsTimeLimit(
const RoutingSearchParameters&
parameters) {
3136 if (!
parameters.has_lns_time_limit())
return absl::InfiniteDuration();
3143 void MakeAllUnperformed(
const RoutingModel*
model, Assignment* assignment) {
3144 assignment->Clear();
3145 for (
int i = 0; i <
model->Nexts().size(); ++i) {
3146 if (!
model->IsStart(i)) {
3147 assignment->Add(
model->NextVar(i))->SetValue(i);
3150 for (
int vehicle = 0; vehicle <
model->vehicles(); ++vehicle) {
3151 assignment->Add(
model->NextVar(
model->Start(vehicle)))
3152 ->SetValue(
model->End(vehicle));
3157 bool RoutingModel::AppendAssignmentIfFeasible(
3158 const Assignment& assignment,
3159 std::vector<std::unique_ptr<Assignment>>* assignments) {
3161 solver_->Solve(restore_tmp_assignment_, collect_one_assignment_,
3162 GetOrCreateLimit());
3164 assignments->push_back(absl::make_unique<Assignment>(solver_.get()));
3165 assignments->back()->Copy(collect_one_assignment_->
solution(0));
3171 void RoutingModel::LogSolution(
const RoutingSearchParameters&
parameters,
3172 const std::string& description,
3175 const double cost_scaling_factor =
parameters.log_cost_scaling_factor();
3176 const double cost_offset =
parameters.log_cost_offset();
3177 const std::string cost_string =
3178 cost_scaling_factor == 1.0 && cost_offset == 0.0
3179 ? absl::StrCat(solution_cost)
3181 "%d (%.8lf)", solution_cost,
3182 cost_scaling_factor * (solution_cost + cost_offset));
3184 "%s (%s, time = %d ms, memory used = %s)", description, cost_string,
3185 solver_->wall_time() - start_time_ms, memory_str);
3190 std::vector<const Assignment*>* solutions) {
3191 const int64 start_time_ms = solver_->wall_time();
3194 if (solutions !=
nullptr) solutions->clear();
3208 const int time_limit_shares = 1 + !global_dimension_optimizers_.empty() +
3209 !local_dimension_optimizers_.empty();
3210 const absl::Duration first_solution_lns_time_limit =
3213 first_solution_lns_limit_->
UpdateLimits(first_solution_lns_time_limit,
3216 std::vector<std::unique_ptr<Assignment>> solution_pool;
3218 if (
nullptr == assignment) {
3219 bool solution_found =
false;
3222 AppendAssignmentIfFeasible(matching, &solution_pool)) {
3224 LogSolution(
parameters,
"Min-Cost Flow Solution",
3225 solution_pool.back()->ObjectiveValue(), start_time_ms);
3227 solution_found =
true;
3229 if (!solution_found) {
3233 MakeAllUnperformed(
this, &unperformed);
3234 if (AppendAssignmentIfFeasible(unperformed, &solution_pool) &&
3236 LogSolution(
parameters,
"All Unperformed Solution",
3237 solution_pool.back()->ObjectiveValue(), start_time_ms);
3239 const absl::Duration elapsed_time =
3240 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3241 const absl::Duration time_left =
3243 if (time_left >= absl::ZeroDuration()) {
3247 solver_->Solve(solve_db_, monitors_);
3252 solver_->Solve(improve_db_, monitors_);
3257 const int solution_count = collect_assignments_->
solution_count();
3259 solution_count >= 1 ? collect_assignments_->
solution(solution_count - 1)
3263 AppendAssignmentIfFeasible(sat_solution, &solution_pool) &&
3265 LogSolution(
parameters,
"SAT", solution_pool.back()->ObjectiveValue(),
3270 const absl::Duration elapsed_time =
3271 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3272 const int solution_count = collect_assignments_->
solution_count();
3273 if (solution_count >= 1 || !solution_pool.empty()) {
3275 if (solutions !=
nullptr) {
3276 for (
int i = 0; i < solution_count; ++i) {
3277 solutions->push_back(
3278 solver_->MakeAssignment(collect_assignments_->
solution(i)));
3280 for (
const auto& solution : solution_pool) {
3281 if (solutions->empty() ||
3282 solution->ObjectiveValue() < solutions->back()->ObjectiveValue()) {
3283 solutions->push_back(solver_->MakeAssignment(solution.get()));
3286 return solutions->back();
3289 solution_count >= 1 ? collect_assignments_->
solution(solution_count - 1)
3291 for (
const auto& solution : solution_pool) {
3292 if (best_assignment ==
nullptr ||
3294 best_assignment = solution.get();
3297 return solver_->MakeAssignment(best_assignment);
3299 if (elapsed_time >= GetTimeLimit(
parameters)) {
3311 const int size =
Size();
3317 source_model->
Nexts());
3319 std::vector<IntVar*> source_vars(size + size + vehicles_);
3320 std::vector<IntVar*> target_vars(size + size + vehicles_);
3330 source_assignment, source_vars);
3348 LOG(
WARNING) <<
"Non-closed model not supported.";
3352 LOG(
WARNING) <<
"Non-homogeneous vehicle costs not supported";
3355 if (!disjunctions_.
empty()) {
3357 <<
"Node disjunction constraints or optional nodes not supported.";
3360 const int num_nodes =
Size() + vehicles_;
3367 std::unique_ptr<IntVarIterator> iterator(
3368 nexts_[
tail]->MakeDomainIterator(
false));
3391 return linear_sum_assignment.
GetCost();
3396 bool RoutingModel::RouteCanBeUsedByVehicle(
const Assignment& assignment,
3397 int start_index,
int vehicle)
const {
3399 IsStart(start_index) ?
Next(assignment, start_index) : start_index;
3400 while (!
IsEnd(current_index)) {
3402 if (!vehicle_var->
Contains(vehicle)) {
3405 const int next_index =
Next(assignment, current_index);
3406 CHECK_NE(next_index, current_index) <<
"Inactive node inside a route";
3407 current_index = next_index;
3412 bool RoutingModel::ReplaceUnusedVehicle(
3413 int unused_vehicle,
int active_vehicle,
3414 Assignment*
const compact_assignment)
const {
3415 CHECK(compact_assignment !=
nullptr);
3419 const int unused_vehicle_start =
Start(unused_vehicle);
3420 IntVar*
const unused_vehicle_start_var =
NextVar(unused_vehicle_start);
3421 const int unused_vehicle_end =
End(unused_vehicle);
3422 const int active_vehicle_start =
Start(active_vehicle);
3423 const int active_vehicle_end =
End(active_vehicle);
3424 IntVar*
const active_vehicle_start_var =
NextVar(active_vehicle_start);
3425 const int active_vehicle_next =
3426 compact_assignment->Value(active_vehicle_start_var);
3427 compact_assignment->SetValue(unused_vehicle_start_var, active_vehicle_next);
3428 compact_assignment->SetValue(active_vehicle_start_var,
End(active_vehicle));
3431 int current_index = active_vehicle_next;
3432 while (!
IsEnd(current_index)) {
3433 IntVar*
const vehicle_var =
VehicleVar(current_index);
3434 compact_assignment->SetValue(vehicle_var, unused_vehicle);
3435 const int next_index =
Next(*compact_assignment, current_index);
3436 if (
IsEnd(next_index)) {
3437 IntVar*
const last_next_var =
NextVar(current_index);
3438 compact_assignment->SetValue(last_next_var,
End(unused_vehicle));
3440 current_index = next_index;
3445 const std::vector<IntVar*>& transit_variables = dimension->transits();
3446 IntVar*
const unused_vehicle_transit_var =
3447 transit_variables[unused_vehicle_start];
3448 IntVar*
const active_vehicle_transit_var =
3449 transit_variables[active_vehicle_start];
3450 const bool contains_unused_vehicle_transit_var =
3451 compact_assignment->Contains(unused_vehicle_transit_var);
3452 const bool contains_active_vehicle_transit_var =
3453 compact_assignment->Contains(active_vehicle_transit_var);
3454 if (contains_unused_vehicle_transit_var !=
3455 contains_active_vehicle_transit_var) {
3457 LOG(
INFO) <<
"The assignment contains transit variable for dimension '"
3458 << dimension->name() <<
"' for some vehicles, but not for all";
3461 if (contains_unused_vehicle_transit_var) {
3462 const int64 old_unused_vehicle_transit =
3463 compact_assignment->Value(unused_vehicle_transit_var);
3464 const int64 old_active_vehicle_transit =
3465 compact_assignment->Value(active_vehicle_transit_var);
3466 compact_assignment->SetValue(unused_vehicle_transit_var,
3467 old_active_vehicle_transit);
3468 compact_assignment->SetValue(active_vehicle_transit_var,
3469 old_unused_vehicle_transit);
3473 const std::vector<IntVar*>& cumul_variables = dimension->cumuls();
3474 IntVar*
const unused_vehicle_cumul_var =
3475 cumul_variables[unused_vehicle_end];
3476 IntVar*
const active_vehicle_cumul_var =
3477 cumul_variables[active_vehicle_end];
3478 const int64 old_unused_vehicle_cumul =
3479 compact_assignment->Value(unused_vehicle_cumul_var);
3480 const int64 old_active_vehicle_cumul =
3481 compact_assignment->Value(active_vehicle_cumul_var);
3482 compact_assignment->SetValue(unused_vehicle_cumul_var,
3483 old_active_vehicle_cumul);
3484 compact_assignment->SetValue(active_vehicle_cumul_var,
3485 old_unused_vehicle_cumul);
3492 return CompactAssignmentInternal(assignment,
false);
3497 return CompactAssignmentInternal(assignment,
true);
3500 Assignment* RoutingModel::CompactAssignmentInternal(
3501 const Assignment& assignment,
bool check_compact_assignment)
const {
3505 <<
"The costs are not homogeneous, routes cannot be rearranged";
3509 std::unique_ptr<Assignment> compact_assignment(
new Assignment(&assignment));
3510 for (
int vehicle = 0; vehicle < vehicles_ - 1; ++vehicle) {
3514 const int vehicle_start =
Start(vehicle);
3515 const int vehicle_end =
End(vehicle);
3517 int swap_vehicle = vehicles_ - 1;
3518 bool has_more_vehicles_with_route =
false;
3519 for (; swap_vehicle > vehicle; --swap_vehicle) {
3526 has_more_vehicles_with_route =
true;
3527 const int swap_vehicle_start =
Start(swap_vehicle);
3528 const int swap_vehicle_end =
End(swap_vehicle);
3537 if (RouteCanBeUsedByVehicle(*compact_assignment, swap_vehicle_start,
3543 if (swap_vehicle == vehicle) {
3544 if (has_more_vehicles_with_route) {
3548 LOG(
INFO) <<
"No vehicle that can be swapped with " << vehicle
3555 if (!ReplaceUnusedVehicle(vehicle, swap_vehicle,
3556 compact_assignment.get())) {
3561 if (check_compact_assignment &&
3562 !solver_->CheckAssignment(compact_assignment.get())) {
3564 LOG(
WARNING) <<
"The compacted assignment is not a valid solution";
3567 return compact_assignment.release();
3570 int RoutingModel::FindNextActive(
int index,
3571 const std::vector<int64>& indices)
const {
3574 const int size = indices.size();
3585 preassignment_->
Clear();
3586 IntVar* next_var =
nullptr;
3587 int lock_index = FindNextActive(-1, locks);
3588 const int size = locks.size();
3589 if (lock_index < size) {
3590 next_var =
NextVar(locks[lock_index]);
3591 preassignment_->
Add(next_var);
3592 for (lock_index = FindNextActive(lock_index, locks); lock_index < size;
3593 lock_index = FindNextActive(lock_index, locks)) {
3594 preassignment_->
SetValue(next_var, locks[lock_index]);
3595 next_var =
NextVar(locks[lock_index]);
3596 preassignment_->
Add(next_var);
3603 const std::vector<std::vector<int64>>& locks,
bool close_routes) {
3604 preassignment_->
Clear();
3609 const RoutingSearchParameters&
parameters)
const {
3611 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3617 const RoutingSearchParameters&
parameters)
const {
3619 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3625 if (collect_assignments_->
solution_count() == 1 && assignment_ !=
nullptr) {
3627 return assignment_->
Save(file_name);
3635 CHECK(assignment_ !=
nullptr);
3636 if (assignment_->
Load(file_name)) {
3637 return DoRestoreAssignment();
3644 CHECK(assignment_ !=
nullptr);
3646 return DoRestoreAssignment();
3649 Assignment* RoutingModel::DoRestoreAssignment() {
3653 solver_->Solve(restore_assignment_, monitors_);
3656 return collect_assignments_->
solution(0);
3665 const std::vector<std::vector<int64>>& routes,
bool ignore_inactive_indices,
3666 bool close_routes,
Assignment*
const assignment)
const {
3667 CHECK(assignment !=
nullptr);
3669 LOG(
ERROR) <<
"The model is not closed yet";
3672 const int num_routes = routes.size();
3673 if (num_routes > vehicles_) {
3674 LOG(
ERROR) <<
"The number of vehicles in the assignment (" << routes.size()
3675 <<
") is greater than the number of vehicles in the model ("
3676 << vehicles_ <<
")";
3680 absl::flat_hash_set<int> visited_indices;
3682 for (
int vehicle = 0; vehicle < num_routes; ++vehicle) {
3683 const std::vector<int64>& route = routes[vehicle];
3684 int from_index =
Start(vehicle);
3685 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3686 visited_indices.insert(from_index);
3687 if (!insert_result.second) {
3688 LOG(
ERROR) <<
"Index " << from_index <<
" (start node for vehicle "
3689 << vehicle <<
") was already used";
3693 for (
const int64 to_index : route) {
3694 if (to_index < 0 || to_index >=
Size()) {
3695 LOG(
ERROR) <<
"Invalid index: " << to_index;
3700 if (active_var->
Max() == 0) {
3701 if (ignore_inactive_indices) {
3704 LOG(
ERROR) <<
"Index " << to_index <<
" is not active";
3709 insert_result = visited_indices.insert(to_index);
3710 if (!insert_result.second) {
3711 LOG(
ERROR) <<
"Index " << to_index <<
" is used multiple times";
3716 if (!vehicle_var->
Contains(vehicle)) {
3717 LOG(
ERROR) <<
"Vehicle " << vehicle <<
" is not allowed at index "
3723 if (!assignment->
Contains(from_var)) {
3724 assignment->
Add(from_var);
3726 assignment->
SetValue(from_var, to_index);
3728 from_index = to_index;
3733 if (!assignment->
Contains(last_var)) {
3734 assignment->
Add(last_var);
3741 for (
int vehicle = num_routes; vehicle < vehicles_; ++vehicle) {
3742 const int start_index =
Start(vehicle);
3745 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3746 visited_indices.insert(start_index);
3747 if (!insert_result.second) {
3748 LOG(
ERROR) <<
"Index " << start_index <<
" is used multiple times";
3753 if (!assignment->
Contains(start_var)) {
3754 assignment->
Add(start_var);
3765 if (!assignment->
Contains(next_var)) {
3766 assignment->
Add(next_var);
3777 const std::vector<std::vector<int64>>& routes,
3778 bool ignore_inactive_indices) {
3786 return DoRestoreAssignment();
3791 std::vector<std::vector<int64>>*
const routes)
const {
3793 CHECK(routes !=
nullptr);
3795 const int model_size =
Size();
3796 routes->resize(vehicles_);
3797 for (
int vehicle = 0; vehicle < vehicles_; ++vehicle) {
3798 std::vector<int64>*
const vehicle_route = &routes->at(vehicle);
3799 vehicle_route->clear();
3801 int num_visited_indices = 0;
3802 const int first_index =
Start(vehicle);
3806 int current_index = assignment.
Value(first_var);
3807 while (!
IsEnd(current_index)) {
3808 vehicle_route->push_back(current_index);
3813 current_index = assignment.
Value(next_var);
3815 ++num_visited_indices;
3816 CHECK_LE(num_visited_indices, model_size)
3817 <<
"The assignment contains a cycle";
3825 std::vector<std::vector<int64>> route_indices(
vehicles());
3826 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3828 LOG(DFATAL) <<
"GetRoutesFromAssignment() called on incomplete solution:"
3829 <<
" NextVar(" << vehicle <<
") is unbound.";
3832 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3834 route_indices[vehicle].push_back(
index);
3837 route_indices[vehicle].push_back(
index);
3840 return route_indices;
3844 int64 RoutingModel::GetArcCostForClassInternal(
3845 int64 from_index,
int64 to_index, CostClassIndex cost_class_index)
const {
3848 DCHECK_LT(cost_class_index, cost_classes_.size());
3849 CostCacheElement*
const cache = &cost_cache_[from_index];
3851 if (cache->index ==
static_cast<int>(to_index) &&
3852 cache->cost_class_index == cost_class_index) {
3856 const CostClass& cost_class = cost_classes_[cost_class_index];
3857 const auto& evaluator = transit_evaluators_[cost_class.evaluator_index];
3859 cost =
CapAdd(evaluator(from_index, to_index),
3860 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3861 }
else if (!
IsEnd(to_index)) {
3865 evaluator(from_index, to_index),
3866 CapAdd(GetDimensionTransitCostSum(from_index, to_index, cost_class),
3867 fixed_cost_of_vehicle_[index_to_vehicle_[from_index]]));
3871 if (consider_empty_route_costs_[index_to_vehicle_[from_index]]) {
3873 CapAdd(evaluator(from_index, to_index),
3874 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3879 *cache = {
static_cast<int>(to_index), cost_class_index,
cost};
3888 int vehicle)
const {
3902 return assignment.
Value(next_var);
3906 int64 vehicle)
const {
3907 if (from_index != to_index && vehicle >= 0) {
3908 return GetArcCostForClassInternal(from_index, to_index,
3917 int64 cost_class_index)
const {
3918 if (from_index != to_index) {
3919 return GetArcCostForClassInternal(from_index, to_index,
3927 int64 to_index)
const {
3931 if (!is_bound_to_end_ct_added_.
Switched()) {
3934 std::vector<IntVar*> zero_transit(
Size(), solver_->MakeIntConst(0));
3935 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
3936 nexts_, active_, is_bound_to_end_, zero_transit));
3937 is_bound_to_end_ct_added_.
Switch(solver_.get());
3939 if (is_bound_to_end_[to_index]->Min() == 1)
return kint64max;
3944 int64 RoutingModel::GetDimensionTransitCostSum(
3945 int64 i,
int64 j,
const CostClass& cost_class)
const {
3947 for (
const auto& evaluator_and_coefficient :
3948 cost_class.dimension_transit_evaluator_class_and_cost_coefficient) {
3949 DCHECK_GT(evaluator_and_coefficient.cost_coefficient, 0);
3952 CapProd(evaluator_and_coefficient.cost_coefficient,
3953 evaluator_and_coefficient.dimension->GetTransitValueFromClass(
3954 i, j, evaluator_and_coefficient.transit_evaluator_class)));
3970 const bool mandatory1 = active_[to1]->Min() == 1;
3971 const bool mandatory2 = active_[to2]->Min() == 1;
3973 if (mandatory1 != mandatory2)
return mandatory1;
3980 const int64 src_vehicle = src_vehicle_var->
Max();
3981 if (src_vehicle_var->
Bound()) {
3988 mandatory1 ? to1_vehicle_var->
Bound() : (to1_vehicle_var->
Size() <= 2);
3990 mandatory2 ? to2_vehicle_var->
Bound() : (to2_vehicle_var->
Size() <= 2);
3993 if (bound1 != bound2)
return bound1;
3996 const int64 vehicle1 = to1_vehicle_var->
Max();
3997 const int64 vehicle2 = to2_vehicle_var->
Max();
4000 if ((vehicle1 == src_vehicle) != (vehicle2 == src_vehicle)) {
4001 return vehicle1 == src_vehicle;
4006 if (vehicle1 != src_vehicle)
return to1 < to2;
4017 const std::vector<IntVar*>& cumul_vars =
4019 IntVar*
const dim1 = cumul_vars[to1];
4020 IntVar*
const dim2 = cumul_vars[to2];
4023 if (dim1->
Max() != dim2->
Max())
return dim1->
Max() < dim2->
Max();
4032 const int64 cost_class_index =
4033 SafeGetCostClassInt64OfVehicle(src_vehicle);
4038 if (cost1 != cost2)
return cost1 < cost2;
4045 if (num_vehicles1 != num_vehicles2)
return num_vehicles1 < num_vehicles2;
4054 DCHECK_EQ(index_to_visit_type_.size(), index_to_type_policy_.size());
4055 index_to_visit_type_[
index] = type;
4056 index_to_type_policy_[
index] = policy;
4057 num_visit_types_ =
std::max(num_visit_types_, type + 1);
4062 return index_to_visit_type_[
index];
4066 DCHECK_LT(type, single_nodes_of_type_.size());
4067 return single_nodes_of_type_[type];
4071 DCHECK_LT(type, pair_indices_of_type_.size());
4072 return pair_indices_of_type_[type];
4078 return index_to_type_policy_[
index];
4082 hard_incompatible_types_per_type_index_.resize(num_visit_types_);
4083 temporal_incompatible_types_per_type_index_.resize(num_visit_types_);
4084 same_vehicle_required_type_alternatives_per_type_index_.resize(
4086 required_type_alternatives_when_adding_type_index_.resize(num_visit_types_);
4087 required_type_alternatives_when_removing_type_index_.resize(num_visit_types_);
4092 hard_incompatible_types_per_type_index_.size());
4093 has_hard_type_incompatibilities_ =
true;
4095 hard_incompatible_types_per_type_index_[type1].insert(type2);
4096 hard_incompatible_types_per_type_index_[type2].insert(type1);
4101 temporal_incompatible_types_per_type_index_.size());
4102 has_temporal_type_incompatibilities_ =
true;
4104 temporal_incompatible_types_per_type_index_[type1].insert(type2);
4105 temporal_incompatible_types_per_type_index_[type2].insert(type1);
4108 const absl::flat_hash_set<int>&
4111 DCHECK_LT(type, hard_incompatible_types_per_type_index_.size());
4112 return hard_incompatible_types_per_type_index_[type];
4115 const absl::flat_hash_set<int>&
4118 DCHECK_LT(type, temporal_incompatible_types_per_type_index_.size());
4119 return temporal_incompatible_types_per_type_index_[type];
4125 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4127 same_vehicle_required_type_alternatives_per_type_index_.size());
4129 if (required_type_alternatives.empty()) {
4133 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4134 trivially_infeasible_visit_types_to_policies_[dependent_type];
4141 has_same_vehicle_type_requirements_ =
true;
4142 same_vehicle_required_type_alternatives_per_type_index_[dependent_type]
4143 .push_back(std::move(required_type_alternatives));
4147 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4149 required_type_alternatives_when_adding_type_index_.size());
4151 if (required_type_alternatives.empty()) {
4155 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4156 trivially_infeasible_visit_types_to_policies_[dependent_type];
4162 has_temporal_type_requirements_ =
true;
4163 required_type_alternatives_when_adding_type_index_[dependent_type].push_back(
4164 std::move(required_type_alternatives));
4168 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4170 required_type_alternatives_when_removing_type_index_.size());
4172 if (required_type_alternatives.empty()) {
4176 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4177 trivially_infeasible_visit_types_to_policies_[dependent_type];
4184 has_temporal_type_requirements_ =
true;
4185 required_type_alternatives_when_removing_type_index_[dependent_type]
4186 .push_back(std::move(required_type_alternatives));
4189 const std::vector<absl::flat_hash_set<int>>&
4193 same_vehicle_required_type_alternatives_per_type_index_.size());
4194 return same_vehicle_required_type_alternatives_per_type_index_[type];
4197 const std::vector<absl::flat_hash_set<int>>&
4200 DCHECK_LT(type, required_type_alternatives_when_adding_type_index_.size());
4201 return required_type_alternatives_when_adding_type_index_[type];
4204 const std::vector<absl::flat_hash_set<int>>&
4207 DCHECK_LT(type, required_type_alternatives_when_removing_type_index_.size());
4208 return required_type_alternatives_when_removing_type_index_[type];
4216 int64 var_index)
const {
4217 if (active_[var_index]->Min() == 1)
return kint64max;
4218 const std::vector<DisjunctionIndex>& disjunction_indices =
4220 if (disjunction_indices.size() != 1)
return default_value;
4225 return std::max(
int64{0}, disjunctions_[disjunction_index].value.penalty);
4230 const std::string& dimension_to_print)
const {
4231 for (
int i = 0; i <
Size(); ++i) {
4234 <<
"DebugOutputVehicleSchedules() called on incomplete solution:"
4235 <<
" NextVar(" << i <<
") is unbound.";
4240 absl::flat_hash_set<std::string> dimension_names;
4241 if (dimension_to_print.empty()) {
4243 dimension_names.insert(all_dimension_names.begin(),
4244 all_dimension_names.end());
4246 dimension_names.insert(dimension_to_print);
4248 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4249 int empty_vehicle_range_start = vehicle;
4254 if (empty_vehicle_range_start != vehicle) {
4255 if (empty_vehicle_range_start == vehicle - 1) {
4256 absl::StrAppendFormat(&output,
"Vehicle %d: empty",
4257 empty_vehicle_range_start);
4259 absl::StrAppendFormat(&output,
"Vehicles %d-%d: empty",
4260 empty_vehicle_range_start, vehicle - 1);
4262 output.append(
"\n");
4265 absl::StrAppendFormat(&output,
"Vehicle %d:", vehicle);
4269 absl::StrAppendFormat(&output,
"%d Vehicle(%d) ",
index,
4270 solution_assignment.
Value(vehicle_var));
4274 absl::StrAppendFormat(&output,
"%s(%d..%d) ", dimension->name(),
4275 solution_assignment.
Min(
var),
4276 solution_assignment.
Max(
var));
4281 if (
IsEnd(
index)) output.append(
"Route end ");
4283 output.append(
"\n");
4286 output.append(
"Unperformed nodes: ");
4287 bool has_unperformed =
false;
4288 for (
int i = 0; i <
Size(); ++i) {
4291 absl::StrAppendFormat(&output,
"%d ", i);
4292 has_unperformed =
true;
4295 if (!has_unperformed) output.append(
"None");
4296 output.append(
"\n");
4303 std::vector<std::vector<std::pair<int64, int64>>> cumul_bounds(
vehicles());
4304 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4306 LOG(DFATAL) <<
"GetCumulBounds() called on incomplete solution:"
4307 <<
" NextVar(" << vehicle <<
") is unbound.";
4311 for (
int vehicle_id = 0; vehicle_id <
vehicles(); ++vehicle_id) {
4314 cumul_bounds[vehicle_id].emplace_back(solution_assignment.
Min(dim_var),
4315 solution_assignment.
Max(dim_var));
4319 cumul_bounds[vehicle_id].emplace_back(solution_assignment.
Min(dim_var),
4320 solution_assignment.
Max(dim_var));
4323 return cumul_bounds;
4327 Assignment* RoutingModel::GetOrCreateAssignment() {
4328 if (assignment_ ==
nullptr) {
4329 assignment_ = solver_->MakeAssignment();
4330 assignment_->
Add(nexts_);
4332 assignment_->
Add(vehicle_vars_);
4339 Assignment* RoutingModel::GetOrCreateTmpAssignment() {
4340 if (tmp_assignment_ ==
nullptr) {
4341 tmp_assignment_ = solver_->MakeAssignment();
4342 tmp_assignment_->
Add(nexts_);
4344 return tmp_assignment_;
4347 RegularLimit* RoutingModel::GetOrCreateLimit() {
4348 if (limit_ ==
nullptr) {
4355 RegularLimit* RoutingModel::GetOrCreateLocalSearchLimit() {
4356 if (ls_limit_ ==
nullptr) {
4364 RegularLimit* RoutingModel::GetOrCreateLargeNeighborhoodSearchLimit() {
4365 if (lns_limit_ ==
nullptr) {
4374 RoutingModel::GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit() {
4375 if (first_solution_lns_limit_ ==
nullptr) {
4376 first_solution_lns_limit_ =
4380 return first_solution_lns_limit_;
4383 LocalSearchOperator* RoutingModel::CreateInsertionOperator() {
4384 LocalSearchOperator* insertion_operator =
4385 CreateCPOperator<MakeActiveOperator>();
4386 if (!pickup_delivery_pairs_.empty()) {
4387 insertion_operator = solver_->ConcatenateOperators(
4388 {CreatePairOperator<MakePairActiveOperator>(), insertion_operator});
4390 if (!implicit_pickup_delivery_pairs_without_alternatives_.empty()) {
4391 insertion_operator = solver_->ConcatenateOperators(
4392 {CreateOperator<MakePairActiveOperator>(
4393 implicit_pickup_delivery_pairs_without_alternatives_),
4394 insertion_operator});
4396 return insertion_operator;
4399 LocalSearchOperator* RoutingModel::CreateMakeInactiveOperator() {
4400 LocalSearchOperator* make_inactive_operator =
4401 CreateCPOperator<MakeInactiveOperator>();
4402 if (!pickup_delivery_pairs_.empty()) {
4403 make_inactive_operator = solver_->ConcatenateOperators(
4404 {CreatePairOperator<MakePairInactiveOperator>(),
4405 make_inactive_operator});
4407 return make_inactive_operator;
4410 void RoutingModel::CreateNeighborhoodOperators(
4412 local_search_operators_.clear();
4413 local_search_operators_.resize(LOCAL_SEARCH_OPERATOR_COUNTER,
nullptr);
4417 std::pair<RoutingLocalSearchOperator, Solver::LocalSearchOperators>>
4422 for (
const auto [type, op] : operator_by_type) {
4423 local_search_operators_[type] =
4425 ? solver_->MakeOperator(nexts_, op)
4426 : solver_->MakeOperator(nexts_, vehicle_vars_, op);
4431 const std::vector<std::pair<RoutingLocalSearchOperator,
4433 operator_by_type = {{LIN_KERNIGHAN,
Solver::LK},
4436 for (
const auto [type, op] : operator_by_type) {
4439 local_search_operators_[type] =
4441 ? solver_->MakeOperator(nexts_, std::move(arc_cost), op)
4442 : solver_->MakeOperator(nexts_, vehicle_vars_,
4443 std::move(arc_cost), op);
4448 local_search_operators_[RELOCATE] = CreateCPOperator<Relocate>();
4449 local_search_operators_[EXCHANGE] = CreateCPOperator<Exchange>();
4450 local_search_operators_[CROSS] = CreateCPOperator<Cross>();
4451 local_search_operators_[TWO_OPT] = CreateCPOperator<TwoOpt>();
4452 local_search_operators_[RELOCATE_AND_MAKE_ACTIVE] =
4453 CreateCPOperator<RelocateAndMakeActiveOperator>();
4454 local_search_operators_[MAKE_ACTIVE_AND_RELOCATE] =
4455 CreateCPOperator<MakeActiveAndRelocate>();
4456 local_search_operators_[MAKE_CHAIN_INACTIVE] =
4457 CreateCPOperator<MakeChainInactiveOperator>();
4458 local_search_operators_[SWAP_ACTIVE] = CreateCPOperator<SwapActiveOperator>();
4459 local_search_operators_[EXTENDED_SWAP_ACTIVE] =
4460 CreateCPOperator<ExtendedSwapActiveOperator>();
4463 local_search_operators_[MAKE_ACTIVE] = CreateInsertionOperator();
4464 local_search_operators_[MAKE_INACTIVE] = CreateMakeInactiveOperator();
4465 local_search_operators_[RELOCATE_PAIR] =
4466 CreatePairOperator<PairRelocateOperator>();
4467 std::vector<LocalSearchOperator*> light_relocate_pair_operators;
4468 light_relocate_pair_operators.push_back(
4469 CreatePairOperator<LightPairRelocateOperator>());
4470 local_search_operators_[LIGHT_RELOCATE_PAIR] =
4471 solver_->ConcatenateOperators(light_relocate_pair_operators);
4472 local_search_operators_[EXCHANGE_PAIR] =
4473 CreatePairOperator<PairExchangeOperator>();
4474 local_search_operators_[EXCHANGE_RELOCATE_PAIR] =
4475 CreatePairOperator<PairExchangeRelocateOperator>();
4476 local_search_operators_[RELOCATE_NEIGHBORS] =
4477 CreateOperator<MakeRelocateNeighborsOperator>(
4479 local_search_operators_[NODE_PAIR_SWAP] = solver_->ConcatenateOperators(
4480 {CreatePairOperator<IndexPairSwapActiveOperator>(),
4481 CreatePairOperator<SwapIndexPairOperator>(),
4482 CreatePairOperator<PairNodeSwapActiveOperator<true>>(),
4483 CreatePairOperator<PairNodeSwapActiveOperator<false>>()});
4484 local_search_operators_[RELOCATE_SUBTRIP] =
4485 CreatePairOperator<RelocateSubtrip>();
4486 local_search_operators_[EXCHANGE_SUBTRIP] =
4487 CreatePairOperator<ExchangeSubtrip>();
4489 const auto arc_cost_for_path_start =
4491 const int vehicle = index_to_vehicle_[start_index];
4492 const int64 arc_cost =
4494 return (before_node != start_index ||
IsEnd(after_node))
4498 local_search_operators_[RELOCATE_EXPENSIVE_CHAIN] =
4499 solver_->RevAlloc(
new RelocateExpensiveChain(
4503 vehicle_start_class_callback_,
4504 parameters.relocate_expensive_chain_num_arcs_to_consider(),
4505 arc_cost_for_path_start));
4508 const auto make_global_cheapest_insertion_filtered_heuristic =
4510 using Heuristic = GlobalCheapestInsertionFilteredHeuristic;
4511 Heuristic::GlobalCheapestInsertionParameters ls_gci_parameters;
4512 ls_gci_parameters.is_sequential =
false;
4513 ls_gci_parameters.farthest_seeds_ratio = 0.0;
4514 ls_gci_parameters.neighbors_ratio =
4515 parameters.cheapest_insertion_ls_operator_neighbors_ratio();
4516 ls_gci_parameters.min_neighbors =
4517 parameters.cheapest_insertion_ls_operator_min_neighbors();
4518 ls_gci_parameters.use_neighbors_ratio_for_initialization =
true;
4519 ls_gci_parameters.add_unperformed_entries =
4520 parameters.cheapest_insertion_add_unperformed_entries();
4521 return absl::make_unique<Heuristic>(
4524 GetOrCreateFeasibilityFilterManager(
parameters), ls_gci_parameters);
4526 const auto make_local_cheapest_insertion_filtered_heuristic =
4528 return absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4530 GetOrCreateFeasibilityFilterManager(
parameters));
4532 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4533 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4534 make_global_cheapest_insertion_filtered_heuristic(),
4535 parameters.heuristic_close_nodes_lns_num_nodes()));
4537 local_search_operators_[LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4538 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4539 make_local_cheapest_insertion_filtered_heuristic(),
4540 parameters.heuristic_close_nodes_lns_num_nodes()));
4542 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_PATH_LNS] =
4543 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4544 make_global_cheapest_insertion_filtered_heuristic()));
4546 local_search_operators_[LOCAL_CHEAPEST_INSERTION_PATH_LNS] =
4547 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4548 make_local_cheapest_insertion_filtered_heuristic()));
4550 local_search_operators_
4551 [RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED] =
4553 new RelocatePathAndHeuristicInsertUnperformedOperator(
4554 make_global_cheapest_insertion_filtered_heuristic()));
4556 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4557 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4558 make_global_cheapest_insertion_filtered_heuristic(),
4559 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4560 arc_cost_for_path_start));
4562 local_search_operators_[LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4563 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4564 make_local_cheapest_insertion_filtered_heuristic(),
4565 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4566 arc_cost_for_path_start));
4569 #define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators) \
4570 if (search_parameters.local_search_operators().use_##operator_method() == \
4572 operators.push_back(local_search_operators_[operator_type]); \
4575 LocalSearchOperator* RoutingModel::ConcatenateOperators(
4576 const RoutingSearchParameters& search_parameters,
4577 const std::vector<LocalSearchOperator*>& operators)
const {
4578 if (search_parameters.use_multi_armed_bandit_concatenate_operators()) {
4579 return solver_->MultiArmedBanditConcatenateOperators(
4582 .multi_armed_bandit_compound_operator_memory_coefficient(),
4584 .multi_armed_bandit_compound_operator_exploration_coefficient(),
4587 return solver_->ConcatenateOperators(operators);
4590 LocalSearchOperator* RoutingModel::GetNeighborhoodOperators(
4591 const RoutingSearchParameters& search_parameters)
const {
4592 std::vector<LocalSearchOperator*> operator_groups;
4593 std::vector<LocalSearchOperator*> operators = extra_operators_;
4594 if (!pickup_delivery_pairs_.empty()) {
4598 if (search_parameters.local_search_operators().use_relocate_pair() ==
4608 if (vehicles_ > 1) {
4619 if (!pickup_delivery_pairs_.empty() ||
4620 search_parameters.local_search_operators().use_relocate_neighbors() ==
4622 operators.push_back(local_search_operators_[RELOCATE_NEIGHBORS]);
4625 search_parameters.local_search_metaheuristic();
4626 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4627 local_search_metaheuristic !=
4628 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4629 local_search_metaheuristic !=
4630 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4637 if (!disjunctions_.
empty()) {
4654 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4663 global_cheapest_insertion_path_lns, operators);
4665 local_cheapest_insertion_path_lns, operators);
4667 RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED,
4668 relocate_path_global_cheapest_insertion_insert_unperformed, operators);
4671 global_cheapest_insertion_expensive_chain_lns,
4674 local_cheapest_insertion_expensive_chain_lns,
4677 global_cheapest_insertion_close_nodes_lns,
4680 local_cheapest_insertion_close_nodes_lns, operators);
4681 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4685 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4686 local_search_metaheuristic !=
4687 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4688 local_search_metaheuristic !=
4689 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4692 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4693 local_search_metaheuristic !=
4694 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4695 local_search_metaheuristic !=
4696 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4701 if (!disjunctions_.
empty()) {
4704 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4706 return solver_->ConcatenateOperators(operator_groups);
4709 #undef CP_ROUTING_PUSH_OPERATOR
4713 if (dimension->GetUnaryTransitEvaluator(0) !=
nullptr)
return true;
4720 void ConvertVectorInt64ToVectorInt(
const std::vector<int64>&
input,
4721 std::vector<int>* output) {
4722 const int n =
input.size();
4724 int* data = output->data();
4725 for (
int i = 0; i < n; ++i) {
4726 const int element =
static_cast<int>(
input[i]);
4734 std::vector<LocalSearchFilterManager::FilterEvent>
4735 RoutingModel::GetOrCreateLocalSearchFilters(
4736 const RoutingSearchParameters&
parameters,
bool filter_cost) {
4737 const auto kAccept = LocalSearchFilterManager::FilterEventType::kAccept;
4738 const auto kRelax = LocalSearchFilterManager::FilterEventType::kRelax;
4748 std::vector<LocalSearchFilterManager::FilterEvent> filters;
4750 if (filter_cost && vehicle_amortized_cost_factors_set_) {
4758 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4761 filters.push_back({sum, kAccept});
4763 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4764 nexts_, vehicle_vars_,
4769 filters.push_back({sum, kAccept});
4773 filters.push_back({solver_->MakeVariableDomainFilter(), kAccept});
4775 if (vehicles_ > max_active_vehicles_) {
4779 if (!disjunctions_.
empty()) {
4783 if (!pickup_delivery_pairs_.empty()) {
4786 vehicle_pickup_delivery_policy_),
4796 const PathState* path_state_reference =
nullptr;
4798 std::vector<int> path_starts;
4799 std::vector<int> path_ends;
4800 ConvertVectorInt64ToVectorInt(starts_, &path_starts);
4801 ConvertVectorInt64ToVectorInt(ends_, &path_ends);
4803 auto path_state = absl::make_unique<PathState>(
4804 Size() +
vehicles(), std::move(path_starts), std::move(path_ends));
4805 path_state_reference = path_state.get();
4817 if (!dimension->HasBreakConstraints())
continue;
4820 filters.insert(filters.end(), extra_filters_.begin(), extra_filters_.end());
4824 LocalSearchFilterManager* RoutingModel::GetOrCreateLocalSearchFilterManager(
4826 if (!local_search_filter_manager_) {
4827 local_search_filter_manager_ =
4828 solver_->RevAlloc(
new LocalSearchFilterManager(
4831 return local_search_filter_manager_;
4834 std::vector<LocalSearchFilterManager::FilterEvent>
4835 RoutingModel::GetOrCreateFeasibilityFilters(
4837 return GetOrCreateLocalSearchFilters(
parameters,
false);
4840 LocalSearchFilterManager* RoutingModel::GetOrCreateFeasibilityFilterManager(
4842 if (!feasibility_filter_manager_) {
4843 feasibility_filter_manager_ =
4844 solver_->RevAlloc(
new LocalSearchFilterManager(
4847 return feasibility_filter_manager_;
4850 LocalSearchFilterManager*
4851 RoutingModel::GetOrCreateStrongFeasibilityFilterManager(
4853 if (!strong_feasibility_filter_manager_) {
4854 std::vector<LocalSearchFilterManager::FilterEvent> filters =
4857 LocalSearchFilterManager::FilterEventType::kAccept});
4858 strong_feasibility_filter_manager_ =
4859 solver_->RevAlloc(
new LocalSearchFilterManager(std::move(filters)));
4861 return strong_feasibility_filter_manager_;
4865 bool AllTransitsPositive(
const RoutingDimension& dimension) {
4866 for (
int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
4867 if (!dimension.AreVehicleTransitsPositive(vehicle)) {
4875 void RoutingModel::StoreDimensionCumulOptimizers(
4877 Assignment* packed_dimensions_collector_assignment =
4878 solver_->MakeAssignment();
4879 packed_dimensions_collector_assignment->AddObjective(
CostVar());
4880 const int num_dimensions = dimensions_.size();
4881 local_optimizer_index_.
resize(num_dimensions, -1);
4882 global_optimizer_index_.
resize(num_dimensions, -1);
4885 if (dimension->global_span_cost_coefficient() > 0 ||
4886 !dimension->GetNodePrecedences().empty()) {
4888 global_optimizer_index_[dim] = global_dimension_optimizers_.size();
4889 global_dimension_optimizers_.push_back(
4890 absl::make_unique<GlobalDimensionCumulOptimizer>(dimension));
4891 packed_dimensions_collector_assignment->Add(dimension->cumuls());
4892 if (!AllTransitsPositive(*dimension)) {
4893 dimension->SetOffsetForGlobalOptimizer(0);
4897 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4900 std::min(offset, dimension->CumulVar(
Start(vehicle))->Min() - 1);
4902 dimension->SetOffsetForGlobalOptimizer(
std::max(
Zero(), offset));
4904 bool has_span_cost =
false;
4905 bool has_span_limit =
false;
4906 std::vector<int64> vehicle_offsets(
vehicles());
4907 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4908 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4909 has_span_cost =
true;
4911 if (dimension->GetSpanUpperBoundForVehicle(vehicle) <
kint64max) {
4912 has_span_limit =
true;
4915 vehicle_offsets[vehicle] =
4916 dimension->AreVehicleTransitsPositive(vehicle)
4918 dimension->CumulVar(
Start(vehicle))->Min() - 1)
4921 bool has_soft_lower_bound =
false;
4922 bool has_soft_upper_bound =
false;
4923 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
4924 if (dimension->HasCumulVarSoftLowerBound(i)) {
4925 has_soft_lower_bound =
true;
4927 if (dimension->HasCumulVarSoftUpperBound(i)) {
4928 has_soft_upper_bound =
true;
4931 int num_linear_constraints = 0;
4932 if (has_span_cost) ++num_linear_constraints;
4933 if (has_span_limit) ++num_linear_constraints;
4934 if (dimension->HasSoftSpanUpperBounds()) ++num_linear_constraints;
4935 if (has_soft_lower_bound) ++num_linear_constraints;
4936 if (has_soft_upper_bound) ++num_linear_constraints;
4937 if (dimension->HasBreakConstraints()) ++num_linear_constraints;
4938 if (num_linear_constraints >= 2) {
4939 dimension->SetVehicleOffsetsForLocalOptimizer(
4940 std::move(vehicle_offsets));
4941 local_optimizer_index_[dim] = local_dimension_optimizers_.size();
4942 local_dimension_optimizers_.push_back(
4943 absl::make_unique<LocalDimensionCumulOptimizer>(
4944 dimension,
parameters.continuous_scheduling_solver()));
4945 bool has_intervals =
false;
4946 for (
const SortedDisjointIntervalList& intervals :
4947 dimension->forbidden_intervals()) {
4950 if (intervals.NumIntervals() > 0) {
4951 has_intervals =
true;
4955 if (dimension->HasBreakConstraints() || has_intervals) {
4956 local_dimension_mp_optimizers_.push_back(
4957 absl::make_unique<LocalDimensionCumulOptimizer>(
4958 dimension,
parameters.mixed_integer_scheduling_solver()));
4960 local_dimension_mp_optimizers_.push_back(
nullptr);
4962 packed_dimensions_collector_assignment->Add(dimension->cumuls());
4965 DCHECK_EQ(local_dimension_mp_optimizers_.size(),
4966 local_dimension_optimizers_.size());
4972 for (IntVar*
const extra_var : extra_vars_) {
4973 packed_dimensions_collector_assignment->Add(extra_var);
4975 for (IntervalVar*
const extra_interval : extra_intervals_) {
4976 packed_dimensions_collector_assignment->Add(extra_interval);
4979 packed_dimensions_assignment_collector_ = solver_->MakeFirstSolutionCollector(
4980 packed_dimensions_collector_assignment);
4985 std::vector<RoutingDimension*> dimensions;
4987 bool has_soft_or_span_cost =
false;
4988 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4989 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4990 has_soft_or_span_cost =
true;
4994 if (!has_soft_or_span_cost) {
4995 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
4996 if (dimension->HasCumulVarSoftUpperBound(i) ||
4997 dimension->HasCumulVarSoftLowerBound(i)) {
4998 has_soft_or_span_cost =
true;
5003 if (has_soft_or_span_cost) dimensions.push_back(dimension);
5009 RoutingModel::CreateFinalizerForMinimizedAndMaximizedVariables() {
5010 std::stable_sort(finalizer_variable_cost_pairs_.begin(),
5011 finalizer_variable_cost_pairs_.end(),
5012 [](
const std::pair<IntVar*, int64>& var_cost1,
5013 const std::pair<IntVar*, int64>& var_cost2) {
5014 return var_cost1.second > var_cost2.second;
5016 const int num_variables = finalizer_variable_cost_pairs_.size() +
5017 finalizer_variable_target_pairs_.size();
5018 std::vector<IntVar*> variables;
5019 std::vector<int64> targets;
5020 variables.reserve(num_variables);
5021 targets.reserve(num_variables);
5022 for (
const auto& variable_cost : finalizer_variable_cost_pairs_) {
5023 variables.push_back(variable_cost.first);
5026 for (
const auto& variable_target : finalizer_variable_target_pairs_) {
5027 variables.push_back(variable_target.first);
5028 targets.push_back(variable_target.second);
5031 std::move(targets));
5034 DecisionBuilder* RoutingModel::CreateSolutionFinalizer(SearchLimit* lns_limit) {
5035 std::vector<DecisionBuilder*> decision_builders;
5036 decision_builders.push_back(solver_->MakePhase(
5039 if (!local_dimension_optimizers_.empty()) {
5040 decision_builders.push_back(
5041 solver_->RevAlloc(
new SetCumulsFromLocalDimensionCosts(
5042 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
5045 if (!global_dimension_optimizers_.empty()) {
5046 decision_builders.push_back(
5047 solver_->RevAlloc(
new SetCumulsFromGlobalDimensionCosts(
5048 &global_dimension_optimizers_, lns_limit)));
5050 decision_builders.push_back(
5051 CreateFinalizerForMinimizedAndMaximizedVariables());
5053 return solver_->Compose(decision_builders);
5056 void RoutingModel::CreateFirstSolutionDecisionBuilders(
5057 const RoutingSearchParameters& search_parameters) {
5058 first_solution_decision_builders_.resize(
5060 first_solution_filtered_decision_builders_.resize(
5062 DecisionBuilder*
const finalize_solution =
5063 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit());
5065 first_solution_decision_builders_
5066 [FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE] = finalize_solution;
5068 first_solution_decision_builders_
5069 [FirstSolutionStrategy::GLOBAL_CHEAPEST_ARC] = solver_->MakePhase(
5077 first_solution_decision_builders_[FirstSolutionStrategy::LOCAL_CHEAPEST_ARC] =
5080 first_solution_decision_builders_[FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5082 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5083 first_solution_filtered_decision_builders_
5084 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5085 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5086 absl::make_unique<EvaluatorCheapestAdditionFilteredHeuristic>(
5091 GetOrCreateFeasibilityFilterManager(search_parameters))));
5092 first_solution_decision_builders_
5093 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5094 solver_->Try(first_solution_filtered_decision_builders_
5095 [FirstSolutionStrategy::PATH_CHEAPEST_ARC],
5096 first_solution_decision_builders_
5097 [FirstSolutionStrategy::PATH_CHEAPEST_ARC]);
5104 first_solution_decision_builders_
5105 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5107 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5108 first_solution_filtered_decision_builders_
5109 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5110 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5111 absl::make_unique<ComparatorCheapestAdditionFilteredHeuristic>(
5113 GetOrCreateFeasibilityFilterManager(search_parameters))));
5114 first_solution_decision_builders_
5115 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->Try(
5116 first_solution_filtered_decision_builders_
5117 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC],
5118 first_solution_decision_builders_
5119 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC]);
5122 if (first_solution_evaluator_ !=
nullptr) {
5123 first_solution_decision_builders_
5124 [FirstSolutionStrategy::EVALUATOR_STRATEGY] = solver_->MakePhase(
5127 first_solution_decision_builders_
5128 [FirstSolutionStrategy::EVALUATOR_STRATEGY] =
nullptr;
5131 first_solution_decision_builders_[FirstSolutionStrategy::ALL_UNPERFORMED] =
5132 solver_->RevAlloc(
new AllUnperformed(
this));
5134 RegularLimit*
const ls_limit =
5137 DecisionBuilder*
const finalize = solver_->MakeSolveOnce(
5138 finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit());
5139 LocalSearchPhaseParameters*
const insertion_parameters =
5140 solver_->MakeLocalSearchPhaseParameters(
5141 nullptr, CreateInsertionOperator(), finalize, ls_limit,
5142 GetOrCreateLocalSearchFilterManager(search_parameters));
5143 std::vector<IntVar*> decision_vars = nexts_;
5145 decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(),
5146 vehicle_vars_.end());
5150 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5151 solver_->MakeNestedOptimize(
5152 solver_->MakeLocalSearchPhase(
5153 decision_vars, solver_->RevAlloc(
new AllUnperformed(
this)),
5154 insertion_parameters),
5155 GetOrCreateAssignment(),
false, optimization_step);
5156 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5157 solver_->Compose(first_solution_decision_builders_
5158 [FirstSolutionStrategy::BEST_INSERTION],
5162 GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
5164 gci_parameters.is_sequential =
false;
5165 gci_parameters.farthest_seeds_ratio =
5166 search_parameters.cheapest_insertion_farthest_seeds_ratio();
5167 gci_parameters.neighbors_ratio =
5168 search_parameters.cheapest_insertion_first_solution_neighbors_ratio();
5169 gci_parameters.min_neighbors =
5170 search_parameters.cheapest_insertion_first_solution_min_neighbors();
5171 gci_parameters.use_neighbors_ratio_for_initialization =
false;
5172 gci_parameters.add_unperformed_entries =
5173 search_parameters.cheapest_insertion_add_unperformed_entries();
5174 for (
bool is_sequential : {
false,
true}) {
5176 is_sequential ? FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION
5177 : FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5178 gci_parameters.is_sequential = is_sequential;
5180 first_solution_filtered_decision_builders_[first_solution_strategy] =
5181 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5182 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5188 GetOrCreateFeasibilityFilterManager(search_parameters),
5190 IntVarFilteredDecisionBuilder*
const strong_gci =
5191 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5192 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5198 GetOrCreateStrongFeasibilityFilterManager(search_parameters),
5200 first_solution_decision_builders_[first_solution_strategy] = solver_->Try(
5201 first_solution_filtered_decision_builders_[first_solution_strategy],
5202 solver_->Try(strong_gci, first_solution_decision_builders_
5203 [FirstSolutionStrategy::BEST_INSERTION]));
5207 first_solution_filtered_decision_builders_
5208 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] =
5209 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5210 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5215 GetOrCreateFeasibilityFilterManager(search_parameters))));
5216 IntVarFilteredDecisionBuilder*
const strong_lci =
5217 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5218 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5223 GetOrCreateStrongFeasibilityFilterManager(search_parameters))));
5224 first_solution_decision_builders_
5225 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = solver_->Try(
5226 first_solution_filtered_decision_builders_
5227 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION],
5228 solver_->Try(strong_lci,
5229 first_solution_decision_builders_
5230 [FirstSolutionStrategy::BEST_INSERTION]));
5232 SavingsFilteredHeuristic::SavingsParameters savings_parameters;
5233 savings_parameters.neighbors_ratio =
5234 search_parameters.savings_neighbors_ratio();
5235 savings_parameters.max_memory_usage_bytes =
5236 search_parameters.savings_max_memory_usage_bytes();
5237 savings_parameters.add_reverse_arcs =
5238 search_parameters.savings_add_reverse_arcs();
5239 savings_parameters.arc_coefficient =
5240 search_parameters.savings_arc_coefficient();
5241 LocalSearchFilterManager* filter_manager =
nullptr;
5242 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5243 filter_manager = GetOrCreateFeasibilityFilterManager(search_parameters);
5246 if (search_parameters.savings_parallel_routes()) {
5247 IntVarFilteredDecisionBuilder* savings_db =
5248 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5249 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5250 this, &manager_, savings_parameters, filter_manager)));
5251 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5252 first_solution_filtered_decision_builders_
5253 [FirstSolutionStrategy::SAVINGS] = savings_db;
5256 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5257 solver_->Try(savings_db,
5258 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5259 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5260 this, &manager_, savings_parameters,
5261 GetOrCreateStrongFeasibilityFilterManager(
5262 search_parameters)))));
5264 IntVarFilteredDecisionBuilder* savings_db =
5265 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5266 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5267 this, &manager_, savings_parameters, filter_manager)));
5268 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5269 first_solution_filtered_decision_builders_
5270 [FirstSolutionStrategy::SAVINGS] = savings_db;
5273 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5274 solver_->Try(savings_db,
5275 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5276 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5277 this, &manager_, savings_parameters,
5278 GetOrCreateStrongFeasibilityFilterManager(
5279 search_parameters)))));
5282 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5283 solver_->RevAlloc(
new SweepBuilder(
this,
true));
5284 DecisionBuilder* sweep_builder =
5285 solver_->RevAlloc(
new SweepBuilder(
this,
false));
5286 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5289 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP]);
5291 first_solution_decision_builders_[FirstSolutionStrategy::CHRISTOFIDES] =
5292 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5293 absl::make_unique<ChristofidesFilteredHeuristic>(
5294 this, GetOrCreateFeasibilityFilterManager(search_parameters),
5295 search_parameters.christofides_use_minimum_matching())));
5298 const bool has_precedences = std::any_of(
5299 dimensions_.begin(), dimensions_.end(),
5301 if (pickup_delivery_pairs_.empty() && !has_precedences) {
5302 automatic_first_solution_strategy_ =
5303 FirstSolutionStrategy::PATH_CHEAPEST_ARC;
5305 automatic_first_solution_strategy_ =
5306 FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5308 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC] =
5309 first_solution_decision_builders_[automatic_first_solution_strategy_];
5310 first_solution_decision_builders_[FirstSolutionStrategy::UNSET] =
5311 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC];
5314 DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder(
5315 const RoutingSearchParameters& search_parameters)
const {
5317 search_parameters.first_solution_strategy();
5318 if (first_solution_strategy < first_solution_decision_builders_.size()) {
5319 return first_solution_decision_builders_[first_solution_strategy];
5325 IntVarFilteredDecisionBuilder*
5326 RoutingModel::GetFilteredFirstSolutionDecisionBuilderOrNull(
5327 const RoutingSearchParameters& search_parameters)
const {
5329 search_parameters.first_solution_strategy();
5330 return first_solution_filtered_decision_builders_[first_solution_strategy];
5333 LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters(
5334 const RoutingSearchParameters& search_parameters) {
5335 SearchLimit* lns_limit = GetOrCreateLargeNeighborhoodSearchLimit();
5336 return solver_->MakeLocalSearchPhaseParameters(
5337 CostVar(), GetNeighborhoodOperators(search_parameters),
5338 solver_->MakeSolveOnce(CreateSolutionFinalizer(lns_limit), lns_limit),
5339 GetOrCreateLocalSearchLimit(),
5340 GetOrCreateLocalSearchFilterManager(search_parameters));
5343 DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder(
5344 const RoutingSearchParameters& search_parameters) {
5345 const int size =
Size();
5346 DecisionBuilder* first_solution =
5347 GetFirstSolutionDecisionBuilder(search_parameters);
5348 LocalSearchPhaseParameters*
const parameters =
5349 CreateLocalSearchParameters(search_parameters);
5350 SearchLimit* first_solution_lns_limit =
5351 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5352 DecisionBuilder*
const first_solution_sub_decision_builder =
5353 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_solution_lns_limit),
5354 first_solution_lns_limit);
5356 return solver_->MakeLocalSearchPhase(nexts_, first_solution,
5357 first_solution_sub_decision_builder,
5360 const int all_size = size + size + vehicles_;
5361 std::vector<IntVar*> all_vars(all_size);
5362 for (
int i = 0; i < size; ++i) {
5363 all_vars[i] = nexts_[i];
5365 for (
int i = size; i < all_size; ++i) {
5366 all_vars[i] = vehicle_vars_[i - size];
5368 return solver_->MakeLocalSearchPhase(all_vars, first_solution,
5369 first_solution_sub_decision_builder,
5374 void RoutingModel::SetupDecisionBuilders(
5375 const RoutingSearchParameters& search_parameters) {
5376 if (search_parameters.use_depth_first_search()) {
5377 SearchLimit* first_lns_limit =
5378 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5379 solve_db_ = solver_->Compose(
5380 GetFirstSolutionDecisionBuilder(search_parameters),
5381 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_lns_limit),
5384 solve_db_ = CreateLocalSearchDecisionBuilder(search_parameters);
5386 CHECK(preassignment_ !=
nullptr);
5387 DecisionBuilder* restore_preassignment =
5388 solver_->MakeRestoreAssignment(preassignment_);
5389 solve_db_ = solver_->Compose(restore_preassignment, solve_db_);
5391 solver_->Compose(restore_preassignment,
5392 solver_->MakeLocalSearchPhase(
5393 GetOrCreateAssignment(),
5394 CreateLocalSearchParameters(search_parameters)));
5395 restore_assignment_ = solver_->Compose(
5396 solver_->MakeRestoreAssignment(GetOrCreateAssignment()),
5397 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5398 restore_tmp_assignment_ = solver_->Compose(
5399 restore_preassignment,
5400 solver_->MakeRestoreAssignment(GetOrCreateTmpAssignment()),
5401 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5404 void RoutingModel::SetupMetaheuristics(
5405 const RoutingSearchParameters& search_parameters) {
5406 SearchMonitor* optimize;
5408 search_parameters.local_search_metaheuristic();
5411 bool limit_too_long = !search_parameters.has_time_limit() &&
5412 search_parameters.solution_limit() ==
kint64max;
5415 switch (metaheuristic) {
5416 case LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH:
5418 optimize = solver_->MakeGuidedLocalSearch(
5421 optimization_step, nexts_,
5422 search_parameters.guided_local_search_lambda_coefficient());
5424 optimize = solver_->MakeGuidedLocalSearch(
5429 optimization_step, nexts_, vehicle_vars_,
5430 search_parameters.guided_local_search_lambda_coefficient());
5433 case LocalSearchMetaheuristic::SIMULATED_ANNEALING:
5435 solver_->MakeSimulatedAnnealing(
false, cost_, optimization_step, 100);
5437 case LocalSearchMetaheuristic::TABU_SEARCH:
5438 optimize = solver_->MakeTabuSearch(
false, cost_, optimization_step,
5439 nexts_, 10, 10, .8);
5441 case LocalSearchMetaheuristic::GENERIC_TABU_SEARCH: {
5442 std::vector<operations_research::IntVar*> tabu_vars;
5443 if (tabu_var_callback_) {
5444 tabu_vars = tabu_var_callback_(
this);
5446 tabu_vars.push_back(cost_);
5448 optimize = solver_->MakeGenericTabuSearch(
false, cost_, optimization_step,
5453 limit_too_long =
false;
5454 optimize = solver_->MakeMinimize(cost_, optimization_step);
5456 if (limit_too_long) {
5457 LOG(
WARNING) << LocalSearchMetaheuristic::Value_Name(metaheuristic)
5458 <<
" specified without sane timeout: solve may run forever.";
5460 monitors_.push_back(optimize);
5464 tabu_var_callback_ = std::move(tabu_var_callback);
5467 void RoutingModel::SetupAssignmentCollector(
5468 const RoutingSearchParameters& search_parameters) {
5469 Assignment* full_assignment = solver_->MakeAssignment();
5471 full_assignment->
Add(dimension->cumuls());
5473 for (IntVar*
const extra_var : extra_vars_) {
5474 full_assignment->
Add(extra_var);
5476 for (IntervalVar*
const extra_interval : extra_intervals_) {
5477 full_assignment->
Add(extra_interval);
5479 full_assignment->
Add(nexts_);
5480 full_assignment->
Add(active_);
5481 full_assignment->
Add(vehicle_vars_);
5484 collect_assignments_ = solver_->MakeNBestValueSolutionCollector(
5485 full_assignment, search_parameters.number_of_solutions_to_collect(),
5487 collect_one_assignment_ =
5488 solver_->MakeFirstSolutionCollector(full_assignment);
5489 monitors_.push_back(collect_assignments_);
5492 void RoutingModel::SetupTrace(
5493 const RoutingSearchParameters& search_parameters) {
5494 if (search_parameters.log_search()) {
5495 Solver::SearchLogParameters search_log_parameters;
5496 search_log_parameters.branch_period = 10000;
5497 search_log_parameters.objective =
nullptr;
5498 search_log_parameters.variable = cost_;
5499 search_log_parameters.scaling_factor =
5500 search_parameters.log_cost_scaling_factor();
5501 search_log_parameters.offset = search_parameters.log_cost_offset();
5502 if (!search_parameters.log_tag().empty()) {
5503 const std::string& tag = search_parameters.log_tag();
5504 search_log_parameters.display_callback = [tag]() {
return tag; };
5506 search_log_parameters.display_callback =
nullptr;
5508 search_log_parameters.display_on_new_solutions_only =
false;
5509 monitors_.push_back(solver_->MakeSearchLog(search_log_parameters));
5513 void RoutingModel::SetupImprovementLimit(
5514 const RoutingSearchParameters& search_parameters) {
5515 if (search_parameters.has_improvement_limit_parameters()) {
5516 monitors_.push_back(solver_->MakeImprovementLimit(
5517 cost_,
false, search_parameters.log_cost_scaling_factor(),
5518 search_parameters.log_cost_offset(),
5519 search_parameters.improvement_limit_parameters()
5520 .improvement_rate_coefficient(),
5521 search_parameters.improvement_limit_parameters()
5522 .improvement_rate_solutions_distance()));
5526 void RoutingModel::SetupSearchMonitors(
5527 const RoutingSearchParameters& search_parameters) {
5528 monitors_.push_back(GetOrCreateLimit());
5529 SetupImprovementLimit(search_parameters);
5530 SetupMetaheuristics(search_parameters);
5531 SetupAssignmentCollector(search_parameters);
5532 SetupTrace(search_parameters);
5535 bool RoutingModel::UsesLightPropagation(
5536 const RoutingSearchParameters& search_parameters)
const {
5537 return !search_parameters.use_full_propagation() &&
5538 !search_parameters.use_depth_first_search() &&
5539 search_parameters.first_solution_strategy() !=
5540 FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE;
5547 finalizer_variable_cost_pairs_.size());
5548 if (
index < finalizer_variable_cost_pairs_.size()) {
5549 const int64 old_cost = finalizer_variable_cost_pairs_[
index].second;
5550 finalizer_variable_cost_pairs_[
index].second =
CapAdd(old_cost,
cost);
5552 finalizer_variable_cost_pairs_.emplace_back(
var,
cost);
5558 if (finalizer_variable_target_set_.contains(
var))
return;
5559 finalizer_variable_target_set_.insert(
var);
5560 finalizer_variable_target_pairs_.emplace_back(
var, target);
5571 void RoutingModel::SetupSearch(
5572 const RoutingSearchParameters& search_parameters) {
5573 SetupDecisionBuilders(search_parameters);
5574 SetupSearchMonitors(search_parameters);
5578 extra_vars_.push_back(
var);
5582 extra_intervals_.push_back(
interval);
5587 class PathSpansAndTotalSlacks :
public Constraint {
5591 std::vector<IntVar*> spans,
5592 std::vector<IntVar*> total_slacks)
5595 dimension_(dimension),
5596 spans_(std::move(spans)),
5597 total_slacks_(std::move(total_slacks)) {
5598 CHECK_EQ(spans_.size(), model_->vehicles());
5599 CHECK_EQ(total_slacks_.size(), model_->vehicles());
5600 vehicle_demons_.resize(model_->vehicles());
5603 std::string DebugString()
const override {
return "PathSpansAndTotalSlacks"; }
5605 void Post()
override {
5606 const int num_nodes = model_->VehicleVars().size();
5607 const int num_transits = model_->Nexts().size();
5608 for (
int node = 0; node < num_nodes; ++node) {
5610 model_->solver(),
this, &PathSpansAndTotalSlacks::PropagateNode,
5611 "PathSpansAndTotalSlacks::PropagateNode", node);
5612 dimension_->CumulVar(node)->WhenRange(demon);
5613 model_->VehicleVar(node)->WhenBound(demon);
5614 if (node < num_transits) {
5615 dimension_->TransitVar(node)->WhenRange(demon);
5616 dimension_->FixedTransitVar(node)->WhenBound(demon);
5617 model_->NextVar(node)->WhenBound(demon);
5620 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5621 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5623 solver(),
this, &PathSpansAndTotalSlacks::PropagateVehicle,
5624 "PathSpansAndTotalSlacks::PropagateVehicle", vehicle);
5625 vehicle_demons_[vehicle] = demon;
5626 if (spans_[vehicle]) spans_[vehicle]->WhenRange(demon);
5627 if (total_slacks_[vehicle]) total_slacks_[vehicle]->WhenRange(demon);
5628 if (dimension_->HasBreakConstraints()) {
5629 for (IntervalVar*
b : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5630 b->WhenAnything(demon);
5637 void InitialPropagate()
override {
5638 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5639 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5640 PropagateVehicle(vehicle);
5648 void PropagateNode(
int node) {
5649 if (!model_->VehicleVar(node)->Bound())
return;
5650 const int vehicle = model_->VehicleVar(node)->Min();
5651 if (vehicle < 0 || vehicle_demons_[vehicle] ==
nullptr)
return;
5652 EnqueueDelayedDemon(vehicle_demons_[vehicle]);
5660 int64 SpanMin(
int vehicle,
int64 sum_fixed_transits) {
5662 const int64 span_min = spans_[vehicle] ? spans_[vehicle]->Min() :
kint64max;
5663 const int64 total_slack_min =
5664 total_slacks_[vehicle] ? total_slacks_[vehicle]->Min() :
kint64max;
5665 return std::min(span_min,
CapAdd(total_slack_min, sum_fixed_transits));
5667 int64 SpanMax(
int vehicle,
int64 sum_fixed_transits) {
5669 const int64 span_max = spans_[vehicle] ? spans_[vehicle]->Max() :
kint64min;
5670 const int64 total_slack_max =
5671 total_slacks_[vehicle] ? total_slacks_[vehicle]->Max() :
kint64min;
5672 return std::max(span_max,
CapAdd(total_slack_max, sum_fixed_transits));
5674 void SetSpanMin(
int vehicle,
int64 min,
int64 sum_fixed_transits) {
5676 if (spans_[vehicle]) {
5677 spans_[vehicle]->SetMin(
min);
5679 if (total_slacks_[vehicle]) {
5680 total_slacks_[vehicle]->SetMin(
CapSub(
min, sum_fixed_transits));
5683 void SetSpanMax(
int vehicle,
int64 max,
int64 sum_fixed_transits) {
5685 if (spans_[vehicle]) {
5686 spans_[vehicle]->SetMax(
max);
5688 if (total_slacks_[vehicle]) {
5689 total_slacks_[vehicle]->SetMax(
CapSub(
max, sum_fixed_transits));
5694 void SynchronizeSpanAndTotalSlack(
int vehicle,
int64 sum_fixed_transits) {
5696 IntVar* span = spans_[vehicle];
5697 IntVar* total_slack = total_slacks_[vehicle];
5698 if (!span || !total_slack)
return;
5699 span->SetMin(
CapAdd(total_slack->Min(), sum_fixed_transits));
5700 span->SetMax(
CapAdd(total_slack->Max(), sum_fixed_transits));
5701 total_slack->SetMin(
CapSub(span->Min(), sum_fixed_transits));
5702 total_slack->SetMax(
CapSub(span->Max(), sum_fixed_transits));
5705 void PropagateVehicle(
int vehicle) {
5706 DCHECK(spans_[vehicle] || total_slacks_[vehicle]);
5707 const int start = model_->Start(vehicle);
5708 const int end = model_->End(vehicle);
5714 int curr_node = start;
5715 while (!model_->IsEnd(curr_node)) {
5716 const IntVar* next_var = model_->NextVar(curr_node);
5717 if (!next_var->Bound())
return;
5718 path_.push_back(curr_node);
5719 curr_node = next_var->Value();
5724 int64 sum_fixed_transits = 0;
5725 for (
const int node : path_) {
5726 const IntVar* fixed_transit_var = dimension_->FixedTransitVar(node);
5727 if (!fixed_transit_var->Bound())
return;
5728 sum_fixed_transits =
5729 CapAdd(sum_fixed_transits, fixed_transit_var->Value());
5732 SynchronizeSpanAndTotalSlack(vehicle, sum_fixed_transits);
5739 if (dimension_->HasBreakConstraints() &&
5740 !dimension_->GetBreakIntervalsOfVehicle(vehicle).empty()) {
5741 const int64 vehicle_start_max = dimension_->CumulVar(start)->Max();
5742 const int64 vehicle_end_min = dimension_->CumulVar(end)->Min();
5744 int64 min_break_duration = 0;
5745 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5746 if (!br->MustBePerformed())
continue;
5747 if (vehicle_start_max < br->EndMin() &&
5748 br->StartMax() < vehicle_end_min) {
5749 min_break_duration =
CapAdd(min_break_duration, br->DurationMin());
5752 SetSpanMin(vehicle,
CapAdd(min_break_duration, sum_fixed_transits),
5753 sum_fixed_transits);
5759 const int64 slack_max =
5760 CapSub(SpanMax(vehicle, sum_fixed_transits), sum_fixed_transits);
5761 const int64 max_additional_slack =
CapSub(slack_max, min_break_duration);
5762 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5763 if (!br->MustBePerformed())
continue;
5765 if (vehicle_start_max >= br->EndMin() &&
5766 br->StartMax() < vehicle_end_min) {
5767 if (br->DurationMin() > max_additional_slack) {
5770 br->SetEndMax(vehicle_start_max);
5771 dimension_->CumulVar(start)->SetMin(br->EndMin());
5776 if (vehicle_start_max < br->EndMin() &&
5777 br->StartMax() >= vehicle_end_min) {
5778 if (br->DurationMin() > max_additional_slack) {
5779 br->SetStartMin(vehicle_end_min);
5780 dimension_->CumulVar(end)->SetMax(br->StartMax());
5788 IntVar* start_cumul = dimension_->CumulVar(start);
5789 IntVar* end_cumul = dimension_->CumulVar(end);
5796 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5798 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5800 const int64 span_min = SpanMin(vehicle, sum_fixed_transits);
5801 const int64 span_max = SpanMax(vehicle, sum_fixed_transits);
5802 const int64 slack_from_lb =
CapSub(span_max, span_lb);
5803 const int64 slack_from_ub =
CapSub(span_ub, span_min);
5819 for (
const int node : path_) {
5820 span_lb =
CapAdd(span_lb, dimension_->TransitVar(node)->Min());
5821 span_ub =
CapAdd(span_ub, dimension_->TransitVar(node)->Max());
5823 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5824 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5828 const int64 span_min = SpanMin(vehicle, sum_fixed_transits);
5829 const int64 span_max = SpanMax(vehicle, sum_fixed_transits);
5830 const int64 slack_from_lb =
CapSub(span_max, span_lb);
5831 const int64 slack_from_ub =
5833 for (
const int node : path_) {
5834 IntVar* transit_var = dimension_->TransitVar(node);
5835 const int64 transit_i_min = transit_var->Min();
5836 const int64 transit_i_max = transit_var->Max();
5840 transit_var->SetMax(
CapAdd(transit_i_min, slack_from_lb));
5841 transit_var->SetMin(
CapSub(transit_i_max, slack_from_ub));
5846 path_.push_back(end);
5857 int64 arrival_time = dimension_->CumulVar(start)->Min();
5858 for (
int i = 1; i < path_.size(); ++i) {
5861 dimension_->FixedTransitVar(path_[i - 1])->Min()),
5862 dimension_->CumulVar(path_[i])->Min());
5864 int64 departure_time = arrival_time;
5865 for (
int i = path_.size() - 2; i >= 0; --i) {
5868 dimension_->FixedTransitVar(path_[i])->Min()),
5869 dimension_->CumulVar(path_[i])->Max());
5871 const int64 span_lb =
CapSub(arrival_time, departure_time);
5872 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5873 const int64 maximum_deviation =
5874 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5875 const int64 start_lb =
CapSub(departure_time, maximum_deviation);
5876 dimension_->CumulVar(start)->SetMin(start_lb);
5880 int64 departure_time = dimension_->CumulVar(end)->Max();
5881 for (
int i = path_.size() - 2; i >= 0; --i) {
5882 const int curr_node = path_[i];
5885 dimension_->FixedTransitVar(curr_node)->Min()),
5886 dimension_->CumulVar(curr_node)->Max());
5888 int arrival_time = departure_time;
5889 for (
int i = 1; i < path_.size(); ++i) {
5892 dimension_->FixedTransitVar(path_[i - 1])->Min()),
5893 dimension_->CumulVar(path_[i])->Min());
5895 const int64 span_lb =
CapSub(arrival_time, departure_time);
5896 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5897 const int64 maximum_deviation =
5898 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5899 dimension_->CumulVar(end)->SetMax(
5900 CapAdd(arrival_time, maximum_deviation));
5904 const RoutingModel*
const model_;
5905 const RoutingDimension*
const dimension_;
5906 std::vector<IntVar*> spans_;
5907 std::vector<IntVar*> total_slacks_;
5908 std::vector<int> path_;
5909 std::vector<Demon*> vehicle_demons_;
5916 std::vector<IntVar*> total_slacks) {
5918 CHECK_EQ(vehicles_, total_slacks.size());
5920 new PathSpansAndTotalSlacks(
this, dimension, spans, total_slacks));
5928 std::vector<int64> vehicle_capacities,
5929 const std::string&
name,
5931 : vehicle_capacities_(std::move(vehicle_capacities)),
5932 base_dimension_(base_dimension),
5933 global_span_cost_coefficient_(0),
5936 global_optimizer_offset_(0) {
5939 vehicle_span_cost_coefficients_.assign(
model->vehicles(), 0);
5942 RoutingDimension::RoutingDimension(RoutingModel*
model,
5943 std::vector<int64> vehicle_capacities,
5944 const std::string&
name, SelfBased)
5945 : RoutingDimension(
model, std::move(vehicle_capacities),
name, this) {}
5948 cumul_var_piecewise_linear_cost_.clear();
5951 void RoutingDimension::Initialize(
5952 const std::vector<int>& transit_evaluators,
5953 const std::vector<int>& state_dependent_transit_evaluators,
5956 InitializeTransits(transit_evaluators, state_dependent_transit_evaluators,
5970 class LightRangeLessOrEqual :
public Constraint {
5972 LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l, IntExpr*
const r);
5973 ~LightRangeLessOrEqual()
override {}
5974 void Post()
override;
5975 void InitialPropagate()
override;
5976 std::string DebugString()
const override;
5977 IntVar* Var()
override {
5978 return solver()->MakeIsLessOrEqualVar(left_, right_);
5981 void Accept(ModelVisitor*
const visitor)
const override {
5992 IntExpr*
const left_;
5993 IntExpr*
const right_;
5997 LightRangeLessOrEqual::LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l,
5999 : Constraint(s), left_(l), right_(r), demon_(nullptr) {}
6001 void LightRangeLessOrEqual::Post() {
6003 solver(),
this, &LightRangeLessOrEqual::CheckRange,
"CheckRange");
6004 left_->WhenRange(demon_);
6005 right_->WhenRange(demon_);
6008 void LightRangeLessOrEqual::InitialPropagate() {
6009 left_->SetMax(right_->Max());
6010 right_->SetMin(left_->Min());
6011 if (left_->Max() <= right_->Min()) {
6016 void LightRangeLessOrEqual::CheckRange() {
6017 if (left_->Min() > right_->Max()) {
6020 if (left_->Max() <= right_->Min()) {
6025 std::string LightRangeLessOrEqual::DebugString()
const {
6026 return left_->DebugString() +
" < " + right_->DebugString();
6031 void RoutingDimension::InitializeCumuls() {
6032 Solver*
const solver = model_->
solver();
6034 const auto capacity_range = std::minmax_element(vehicle_capacities_.begin(),
6035 vehicle_capacities_.end());
6036 const int64 min_capacity = *capacity_range.first;
6038 const int64 max_capacity = *capacity_range.second;
6039 solver->MakeIntVarArray(size, 0, max_capacity, name_, &cumuls_);
6041 for (
int v = 0; v < model_->
vehicles(); v++) {
6042 const int64 vehicle_capacity = vehicle_capacities_[v];
6043 cumuls_[model_->
Start(v)]->SetMax(vehicle_capacity);
6044 cumuls_[model_->
End(v)]->SetMax(vehicle_capacity);
6047 forbidden_intervals_.resize(size);
6048 capacity_vars_.clear();
6049 if (min_capacity != max_capacity) {
6050 solver->MakeIntVarArray(size, 0,
kint64max, &capacity_vars_);
6051 for (
int i = 0; i < size; ++i) {
6052 IntVar*
const capacity_var = capacity_vars_[i];
6053 if (i < model_->Size()) {
6054 IntVar*
const capacity_active = solver->MakeBoolVar();
6055 solver->AddConstraint(
6056 solver->MakeLessOrEqual(model_->
ActiveVar(i), capacity_active));
6057 solver->AddConstraint(solver->MakeIsLessOrEqualCt(
6058 cumuls_[i], capacity_var, capacity_active));
6060 solver->AddConstraint(
6061 solver->MakeLessOrEqual(cumuls_[i], capacity_var));
6068 template <
int64 value>
6073 void ComputeTransitClasses(
const std::vector<int>& evaluator_indices,
6074 std::vector<int>* class_evaluators,
6075 std::vector<int64>* vehicle_to_class) {
6076 CHECK(class_evaluators !=
nullptr);
6077 CHECK(vehicle_to_class !=
nullptr);
6078 class_evaluators->clear();
6079 vehicle_to_class->resize(evaluator_indices.size(), -1);
6080 absl::flat_hash_map<int, int64> evaluator_to_class;
6081 for (
int i = 0; i < evaluator_indices.size(); ++i) {
6082 const int evaluator_index = evaluator_indices[i];
6083 int evaluator_class = -1;
6084 if (!
gtl::FindCopy(evaluator_to_class, evaluator_index, &evaluator_class)) {
6085 evaluator_class = class_evaluators->size();
6086 evaluator_to_class[evaluator_index] = evaluator_class;
6087 class_evaluators->push_back(evaluator_index);
6089 (*vehicle_to_class)[i] = evaluator_class;
6094 void RoutingDimension::InitializeTransitVariables(
int64 slack_max) {
6095 CHECK(!class_evaluators_.empty());
6096 CHECK(base_dimension_ ==
nullptr ||
6097 !state_dependent_class_evaluators_.empty());
6099 Solver*
const solver = model_->
solver();
6100 const int size = model_->
Size();
6103 return (0 <=
index &&
index < state_dependent_vehicle_to_class_.size())
6104 ? state_dependent_vehicle_to_class_[
index]
6105 : state_dependent_class_evaluators_.size();
6107 const std::string slack_name = name_ +
" slack";
6108 const std::string transit_name = name_ +
" fixed transit";
6110 for (
int64 i = 0; i < size; ++i) {
6111 fixed_transits_[i] =
6114 if (base_dimension_ !=
nullptr) {
6115 if (state_dependent_class_evaluators_.size() == 1) {
6116 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
6117 for (
int64 j = 0; j < cumuls_.size(); ++j) {
6118 transition_variables[j] =
6119 MakeRangeMakeElementExpr(
6121 ->StateDependentTransitCallback(
6122 state_dependent_class_evaluators_[0])(i, j)
6124 base_dimension_->
CumulVar(i), solver)
6127 dependent_transits_[i] =
6128 solver->MakeElement(transition_variables, model_->
NextVar(i))
6131 IntVar*
const vehicle_class_var =
6133 ->MakeElement(dependent_vehicle_class_function,
6136 std::vector<IntVar*> transit_for_vehicle;
6137 transit_for_vehicle.reserve(state_dependent_class_evaluators_.size() +
6139 for (
int evaluator : state_dependent_class_evaluators_) {
6140 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
6141 for (
int64 j = 0; j < cumuls_.size(); ++j) {
6142 transition_variables[j] =
6143 MakeRangeMakeElementExpr(
6146 base_dimension_->
CumulVar(i), solver)
6149 transit_for_vehicle.push_back(
6150 solver->MakeElement(transition_variables, model_->
NextVar(i))
6153 transit_for_vehicle.push_back(solver->MakeIntConst(0));
6154 dependent_transits_[i] =
6155 solver->MakeElement(transit_for_vehicle, vehicle_class_var)->Var();
6158 dependent_transits_[i] = solver->MakeIntConst(0);
6162 IntExpr* transit_expr = fixed_transits_[i];
6163 if (dependent_transits_[i]->Min() != 0 ||
6164 dependent_transits_[i]->Max() != 0) {
6165 transit_expr = solver->MakeSum(transit_expr, dependent_transits_[i]);
6168 if (slack_max == 0) {
6169 slacks_[i] = solver->MakeIntConst(0);
6172 solver->MakeIntVar(0, slack_max, absl::StrCat(slack_name, i));
6173 transit_expr = solver->MakeSum(slacks_[i], transit_expr);
6175 transits_[i] = transit_expr->Var();
6179 void RoutingDimension::InitializeTransits(
6180 const std::vector<int>& transit_evaluators,
6181 const std::vector<int>& state_dependent_transit_evaluators,
6184 CHECK(base_dimension_ ==
nullptr ||
6185 model_->
vehicles() == state_dependent_transit_evaluators.size());
6186 const int size = model_->
Size();
6187 transits_.resize(size,
nullptr);
6188 fixed_transits_.resize(size,
nullptr);
6189 slacks_.resize(size,
nullptr);
6190 dependent_transits_.resize(size,
nullptr);
6191 ComputeTransitClasses(transit_evaluators, &class_evaluators_,
6192 &vehicle_to_class_);
6193 if (base_dimension_ !=
nullptr) {
6194 ComputeTransitClasses(state_dependent_transit_evaluators,
6195 &state_dependent_class_evaluators_,
6196 &state_dependent_vehicle_to_class_);
6199 InitializeTransitVariables(slack_max);
6204 std::vector<int64>* values) {
6205 const int num_nodes = path.size();
6206 values->resize(num_nodes - 1);
6207 for (
int i = 0; i < num_nodes - 1; ++i) {
6208 (*values)[i] = evaluator(path[i], path[i + 1]);
6213 : model_(
model), occurrences_of_type_(
model.GetNumberOfVisitTypes()) {}
6216 int vehicle,
const std::function<
int64(
int64)>& next_accessor) {
6223 for (
int pos = 0; pos < current_route_visits_.size(); pos++) {
6224 const int64 current_visit = current_route_visits_[pos];
6231 DCHECK_LT(type, occurrences_of_type_.size());
6232 int& num_type_added = occurrences_of_type_[type].num_type_added_to_vehicle;
6233 int& num_type_removed =
6234 occurrences_of_type_[type].num_type_removed_from_vehicle;
6235 DCHECK_LE(num_type_removed, num_type_added);
6237 num_type_removed == num_type_added) {
6247 if (policy == VisitTypePolicy::TYPE_ADDED_TO_VEHICLE ||
6248 policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED) {
6251 if (policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED ||
6252 policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6260 int vehicle,
const std::function<
int64(
int64)>& next_accessor) {
6263 std::fill(occurrences_of_type_.begin(), occurrences_of_type_.end(),
6269 current_route_visits_.clear();
6271 current = next_accessor(current)) {
6274 VisitTypePolicy::TYPE_ON_VEHICLE_UP_TO_VISIT) {
6275 occurrences_of_type_[type].position_of_last_type_on_vehicle_up_to_visit =
6276 current_route_visits_.size();
6278 current_route_visits_.push_back(current);
6300 check_hard_incompatibilities_(check_hard_incompatibilities) {}
6302 bool TypeIncompatibilityChecker::HasRegulationsToCheck()
const {
6304 (check_hard_incompatibilities_ &&
6313 bool TypeIncompatibilityChecker::CheckTypeRegulations(
int type,
6314 VisitTypePolicy policy,
6316 if (policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6321 for (
int incompatible_type :
6327 if (check_hard_incompatibilities_) {
6328 for (
int incompatible_type :
6338 bool TypeRequirementChecker::HasRegulationsToCheck()
const {
6343 bool TypeRequirementChecker::CheckRequiredTypesCurrentlyOnRoute(
6344 const std::vector<absl::flat_hash_set<int>>& required_type_alternatives,
6346 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6347 required_type_alternatives) {
6348 bool has_one_of_alternatives =
false;
6349 for (
int type_alternative : requirement_alternatives) {
6351 has_one_of_alternatives =
true;
6355 if (!has_one_of_alternatives) {
6362 bool TypeRequirementChecker::CheckTypeRegulations(
int type,
6363 VisitTypePolicy policy,
6367 if (!CheckRequiredTypesCurrentlyOnRoute(
6373 if (!CheckRequiredTypesCurrentlyOnRoute(
6380 types_with_same_vehicle_requirements_on_route_.insert(type);
6385 bool TypeRequirementChecker::FinalizeCheck()
const {
6386 for (
int type : types_with_same_vehicle_requirements_on_route_) {
6387 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6389 bool has_one_of_alternatives =
false;
6390 for (
const int type_alternative : requirement_alternatives) {
6392 has_one_of_alternatives =
true;
6396 if (!has_one_of_alternatives) {
6407 incompatibility_checker_(
model, true),
6408 requirement_checker_(
model),
6409 vehicle_demons_(
model.vehicles()) {}
6411 void TypeRegulationsConstraint::PropagateNodeRegulations(
int node) {
6418 if (vehicle < 0)
return;
6419 DCHECK(vehicle_demons_[vehicle] !=
nullptr);
6423 void TypeRegulationsConstraint::CheckRegulationsOnVehicle(
int vehicle) {
6424 const auto next_accessor = [
this, vehicle](
int64 node) {
6429 return model_.
End(vehicle);
6431 if (!incompatibility_checker_.
CheckVehicle(vehicle, next_accessor) ||
6432 !requirement_checker_.
CheckVehicle(vehicle, next_accessor)) {
6438 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6440 solver(),
this, &TypeRegulationsConstraint::CheckRegulationsOnVehicle,
6441 "CheckRegulationsOnVehicle", vehicle);
6443 for (
int node = 0; node < model_.
Size(); node++) {
6445 solver(),
this, &TypeRegulationsConstraint::PropagateNodeRegulations,
6446 "PropagateNodeRegulations", node);
6453 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6454 CheckRegulationsOnVehicle(vehicle);
6458 void RoutingDimension::CloseModel(
bool use_light_propagation) {
6460 const auto capacity_lambda = [
this](
int64 vehicle) {
6461 return vehicle >= 0 ? vehicle_capacities_[vehicle] :
kint64max;
6463 for (
int i = 0; i < capacity_vars_.size(); ++i) {
6464 IntVar*
const vehicle_var = model_->
VehicleVar(i);
6465 IntVar*
const capacity_var = capacity_vars_[i];
6466 if (use_light_propagation) {
6468 solver, capacity_var, vehicle_var, capacity_lambda,
6469 [
this]() {
return model_->enable_deep_serialization_; }));
6477 return IthElementOrValue<-1>(vehicle_to_class_,
index);
6479 for (
int i = 0; i < fixed_transits_.size(); ++i) {
6480 IntVar*
const next_var = model_->
NextVar(i);
6481 IntVar*
const fixed_transit = fixed_transits_[i];
6482 const auto transit_vehicle_evaluator = [
this, i](
int64 to,
6486 if (use_light_propagation) {
6487 if (class_evaluators_.size() == 1) {
6488 const int class_evaluator_index = class_evaluators_[0];
6489 const auto& unary_callback =
6491 if (unary_callback ==
nullptr) {
6493 solver, fixed_transit, next_var,
6494 [
this, i](
int64 to) {
6497 [
this]() {
return model_->enable_deep_serialization_; }));
6499 fixed_transit->SetValue(unary_callback(i));
6503 solver, fixed_transit, next_var, model_->
VehicleVar(i),
6504 transit_vehicle_evaluator,
6505 [
this]() {
return model_->enable_deep_serialization_; }));
6508 if (class_evaluators_.size() == 1) {
6509 const int class_evaluator_index = class_evaluators_[0];
6510 const auto& unary_callback =
6512 if (unary_callback ==
nullptr) {
6514 fixed_transit, solver
6516 [
this, i](
int64 to) {
6518 class_evaluators_[0])(i, to);
6523 fixed_transit->SetValue(unary_callback(i));
6526 IntVar*
const vehicle_class_var =
6530 fixed_transit, solver
6532 next_var, vehicle_class_var)
6538 GlobalVehicleBreaksConstraint* constraint =
6545 int64 vehicle)
const {
6561 if (next_start >
max)
break;
6562 if (next_start < interval->start) {
6567 if (next_start <=
max) {
6576 CHECK_LT(vehicle, vehicle_span_upper_bounds_.size());
6578 vehicle_span_upper_bounds_[vehicle] = upper_bound;
6584 CHECK_LT(vehicle, vehicle_span_cost_coefficients_.size());
6586 vehicle_span_cost_coefficients_[vehicle] =
coefficient;
6601 if (!
cost.IsNonDecreasing()) {
6602 LOG(
WARNING) <<
"Only non-decreasing cost functions are supported.";
6605 if (
cost.Value(0) < 0) {
6606 LOG(
WARNING) <<
"Only positive cost functions are supported.";
6609 if (
index >= cumul_var_piecewise_linear_cost_.size()) {
6610 cumul_var_piecewise_linear_cost_.resize(
index + 1);
6612 PiecewiseLinearCost& piecewise_linear_cost =
6613 cumul_var_piecewise_linear_cost_[
index];
6614 piecewise_linear_cost.var = cumuls_[
index];
6615 piecewise_linear_cost.cost = absl::make_unique<PiecewiseLinearFunction>(
cost);
6619 return (
index < cumul_var_piecewise_linear_cost_.size() &&
6620 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr);
6625 if (
index < cumul_var_piecewise_linear_cost_.size() &&
6626 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr) {
6627 return cumul_var_piecewise_linear_cost_[
index].cost.get();
6637 const int vehicle =
model->VehicleIndex(
index);
6639 return solver->
MakeProd(expr,
model->VehicleCostsConsideredVar(vehicle))
6646 void RoutingDimension::SetupCumulVarPiecewiseLinearCosts(
6647 std::vector<IntVar*>* cost_elements)
const {
6648 CHECK(cost_elements !=
nullptr);
6649 Solver*
const solver = model_->
solver();
6650 for (
int i = 0; i < cumul_var_piecewise_linear_cost_.size(); ++i) {
6651 const PiecewiseLinearCost& piecewise_linear_cost =
6652 cumul_var_piecewise_linear_cost_[i];
6653 if (piecewise_linear_cost.var !=
nullptr) {
6654 IntExpr*
const expr = solver->MakePiecewiseLinearExpr(
6655 piecewise_linear_cost.var, *piecewise_linear_cost.cost);
6656 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6657 cost_elements->push_back(cost_var);
6667 if (
index >= cumul_var_soft_upper_bound_.size()) {
6668 cumul_var_soft_upper_bound_.resize(
index + 1, {
nullptr, 0, 0});
6670 cumul_var_soft_upper_bound_[
index] = {cumuls_[
index], upper_bound,
6675 return (
index < cumul_var_soft_upper_bound_.size() &&
6676 cumul_var_soft_upper_bound_[
index].var !=
nullptr);
6680 if (
index < cumul_var_soft_upper_bound_.size() &&
6681 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6682 return cumul_var_soft_upper_bound_[
index].bound;
6684 return cumuls_[
index]->Max();
6689 if (
index < cumul_var_soft_upper_bound_.size() &&
6690 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6691 return cumul_var_soft_upper_bound_[
index].coefficient;
6696 void RoutingDimension::SetupCumulVarSoftUpperBoundCosts(
6697 std::vector<IntVar*>* cost_elements)
const {
6698 CHECK(cost_elements !=
nullptr);
6700 for (
int i = 0; i < cumul_var_soft_upper_bound_.size(); ++i) {
6701 const SoftBound& soft_bound = cumul_var_soft_upper_bound_[i];
6702 if (soft_bound.var !=
nullptr) {
6704 solver->
MakeSum(soft_bound.var, -soft_bound.bound), 0,
6705 soft_bound.coefficient);
6706 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6707 cost_elements->push_back(cost_var);
6711 soft_bound.coefficient);
6718 if (
index >= cumul_var_soft_lower_bound_.size()) {
6719 cumul_var_soft_lower_bound_.resize(
index + 1, {
nullptr, 0, 0});
6721 cumul_var_soft_lower_bound_[
index] = {cumuls_[
index], lower_bound,
6726 return (
index < cumul_var_soft_lower_bound_.size() &&
6727 cumul_var_soft_lower_bound_[
index].var !=
nullptr);
6731 if (
index < cumul_var_soft_lower_bound_.size() &&
6732 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6733 return cumul_var_soft_lower_bound_[
index].bound;
6735 return cumuls_[
index]->Min();
6740 if (
index < cumul_var_soft_lower_bound_.size() &&
6741 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6742 return cumul_var_soft_lower_bound_[
index].coefficient;
6747 void RoutingDimension::SetupCumulVarSoftLowerBoundCosts(
6748 std::vector<IntVar*>* cost_elements)
const {
6749 CHECK(cost_elements !=
nullptr);
6751 for (
int i = 0; i < cumul_var_soft_lower_bound_.size(); ++i) {
6752 const SoftBound& soft_bound = cumul_var_soft_lower_bound_[i];
6753 if (soft_bound.var !=
nullptr) {
6756 soft_bound.coefficient);
6757 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6758 cost_elements->push_back(cost_var);
6762 soft_bound.coefficient);
6767 void RoutingDimension::SetupGlobalSpanCost(
6768 std::vector<IntVar*>* cost_elements)
const {
6769 CHECK(cost_elements !=
nullptr);
6770 Solver*
const solver = model_->
solver();
6771 if (global_span_cost_coefficient_ != 0) {
6772 std::vector<IntVar*> end_cumuls;
6773 for (
int i = 0; i < model_->
vehicles(); ++i) {
6774 end_cumuls.push_back(solver
6775 ->MakeProd(model_->vehicle_costs_considered_[i],
6776 cumuls_[model_->
End(i)])
6779 IntVar*
const max_end_cumul = solver->
MakeMax(end_cumuls)->
Var();
6781 max_end_cumul, global_span_cost_coefficient_);
6782 std::vector<IntVar*> start_cumuls;
6783 for (
int i = 0; i < model_->
vehicles(); ++i) {
6784 IntVar* global_span_cost_start_cumul = solver->MakeIntVar(0,
kint64max);
6785 solver->AddConstraint(solver->MakeIfThenElseCt(
6786 model_->vehicle_costs_considered_[i], cumuls_[model_->
Start(i)],
6787 max_end_cumul, global_span_cost_start_cumul));
6788 start_cumuls.push_back(global_span_cost_start_cumul);
6790 IntVar*
const min_start_cumul = solver->MakeMin(start_cumuls)->Var();
6792 min_start_cumul, global_span_cost_coefficient_);
6797 for (
int var_index = 0; var_index < model_->
Size(); ++var_index) {
6799 slacks_[var_index], global_span_cost_coefficient_);
6800 cost_elements->push_back(
6803 model_->vehicle_costs_considered_[0],
6806 solver->MakeSum(transits_[var_index],
6807 dependent_transits_[var_index]),
6808 global_span_cost_coefficient_),
6813 IntVar*
const end_range =
6814 solver->MakeDifference(max_end_cumul, min_start_cumul)->Var();
6815 end_range->SetMin(0);
6816 cost_elements->push_back(
6817 solver->MakeProd(end_range, global_span_cost_coefficient_)->Var());
6823 std::vector<IntervalVar*> breaks,
int vehicle,
6824 std::vector<int64> node_visit_transits) {
6825 if (breaks.empty())
return;
6827 [node_visit_transits](
int64 from,
int64 to) {
6828 return node_visit_transits[from];
6834 std::vector<IntervalVar*> breaks,
int vehicle,
6835 std::vector<int64> node_visit_transits,
6837 if (breaks.empty())
return;
6839 [node_visit_transits](
int64 from,
int64 to) {
6840 return node_visit_transits[from];
6848 std::vector<IntervalVar*> breaks,
int vehicle,
int pre_travel_evaluator,
6849 int post_travel_evaluator) {
6852 if (breaks.empty())
return;
6854 vehicle_break_intervals_[vehicle] = std::move(breaks);
6855 vehicle_pre_travel_evaluators_[vehicle] = pre_travel_evaluator;
6856 vehicle_post_travel_evaluators_[vehicle] = post_travel_evaluator;
6877 DCHECK(!break_constraints_are_initialized_);
6878 const int num_vehicles = model_->
vehicles();
6879 vehicle_break_intervals_.resize(num_vehicles);
6880 vehicle_pre_travel_evaluators_.resize(num_vehicles, -1);
6881 vehicle_post_travel_evaluators_.resize(num_vehicles, -1);
6882 vehicle_break_distance_duration_.resize(num_vehicles);
6883 break_constraints_are_initialized_ =
true;
6887 return break_constraints_are_initialized_;
6891 int vehicle)
const {
6893 DCHECK_LT(vehicle, vehicle_break_intervals_.size());
6894 return vehicle_break_intervals_[vehicle];
6899 DCHECK_LT(vehicle, vehicle_pre_travel_evaluators_.size());
6900 return vehicle_pre_travel_evaluators_[vehicle];
6905 DCHECK_LT(vehicle, vehicle_post_travel_evaluators_.size());
6906 return vehicle_post_travel_evaluators_[vehicle];
6915 vehicle_break_distance_duration_[vehicle].emplace_back(distance, duration);
6924 const std::vector<std::pair<int64, int64>>&
6927 DCHECK_LT(vehicle, vehicle_break_distance_duration_.size());
6928 return vehicle_break_distance_duration_[vehicle];
6934 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6935 pickup_to_delivery_limits_per_pair_index_.resize(pair_index + 1);
6937 pickup_to_delivery_limits_per_pair_index_[pair_index] =
6938 std::move(limit_function);
6942 return !pickup_to_delivery_limits_per_pair_index_.empty();
6947 int delivery)
const {
6950 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6954 pickup_to_delivery_limits_per_pair_index_[pair_index];
6955 if (!pickup_to_delivery_limit_function) {
6961 return pickup_to_delivery_limit_function(pickup, delivery);
6964 void RoutingDimension::SetupSlackAndDependentTransitCosts()
const {
6965 if (model_->
vehicles() == 0)
return;
6967 bool all_vehicle_span_costs_are_equal =
true;
6968 for (
int i = 1; i < model_->
vehicles(); ++i) {
6969 all_vehicle_span_costs_are_equal &= vehicle_span_cost_coefficients_[i] ==
6970 vehicle_span_cost_coefficients_[0];
6973 if (all_vehicle_span_costs_are_equal &&
6974 vehicle_span_cost_coefficients_[0] == 0) {
6986 std::vector<const RoutingDimension*> dimensions_with_relevant_slacks = {
this};
6988 const RoutingDimension*
next =
6989 dimensions_with_relevant_slacks.back()->base_dimension_;
6990 if (
next ==
nullptr ||
next == dimensions_with_relevant_slacks.back()) {
6993 dimensions_with_relevant_slacks.push_back(
next);
6996 for (
auto it = dimensions_with_relevant_slacks.rbegin();
6997 it != dimensions_with_relevant_slacks.rend(); ++it) {
6998 for (
int i = 0; i < model_->
vehicles(); ++i) {
7004 for (IntVar*
const slack : (*it)->slacks_) {
#define DCHECK_LE(val1, val2)
#define CHECK_LT(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define DCHECK_GE(val1, val2)
#define CHECK_NE(val1, val2)
#define DCHECK_GT(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define CHECK_LE(val1, val2)
#define DCHECK_EQ(val1, val2)
#define VLOG(verboselevel)
void resize(size_type new_size)
void push_back(const value_type &x)
An Assignment is a variable -> domains mapping, used to report solutions to the user.
int64 Value(const IntVar *const var) const
bool Load(const std::string &filename)
Loads an assignment from a file; does not add variables to the assignment (only the variables contain...
int64 Min(const IntVar *const var) const
bool Contains(const IntVar *const var) const
bool Save(const std::string &filename) const
Saves the assignment to a file.
void AddObjective(IntVar *const v)
int64 ObjectiveValue() const
int64 Max(const IntVar *const var) const
void CopyIntersection(const Assignment *assignment)
Copies the intersection of the two assignments to the current assignment.
void SetValue(const IntVar *const var, int64 value)
void Copy(const Assignment *assignment)
Copies 'assignment' to the current assignment, clearing its previous content.
IntVarElement * Add(IntVar *const var)
bool Bound(const IntVar *const var) const
This is the base class for all expressions that are not variables.
A constraint is the main modeling object.
A DecisionBuilder is responsible for creating the search tree.
A Decision represents a choice point in the search tree.
A Demon is the base element of a propagation queue.
void inhibit(Solver *const s)
This method inhibits the demon in the search tree below the current position.
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)
Utility class to encapsulate an IntVarIterator and use it in a range-based loop.
The class IntExpr is the base of all integer expressions in constraint programming.
virtual IntVar * Var()=0
Creates a variable from the expression.
virtual bool Bound() const
Returns true if the min and the max of the expression are equal.
virtual int64 Max() const =0
virtual int64 Min() const =0
Decision builder building a solution using heuristics with local search filters to evaluate its feasi...
int64 number_of_rejects() const
int64 number_of_decisions() const
Returns statistics from its underlying heuristic.
The class IntVar is a subset of IntExpr.
virtual void WhenBound(Demon *d)=0
This method attaches a demon that will be awakened when the variable is bound.
virtual bool Contains(int64 v) const =0
This method returns whether the value 'v' is in the domain of the variable.
virtual int64 Value() const =0
This method returns the value of the variable.
virtual uint64 Size() const =0
This method returns the number of values in the domain of the variable.
Interval variables are often used in scheduling.
CostValue GetCost() const
void SetArcCost(ArcIndex arc, CostValue cost)
The base class for all local search operators.
static int64 FastInt64Round(double x)
static const char kIndex2Argument[]
static const char kMinArgument[]
static const char kTargetArgument[]
static const char kMaxArgument[]
static const char kLessOrEqual[]
static const char kLeftArgument[]
static const char kVarsArgument[]
static const char kRightArgument[]
static const char kValuesArgument[]
static const char kIndexArgument[]
void EnqueueDelayedDemon(Demon *const d)
This method pushes the demon onto the propagation queue.
void set_name(const std::string &name)
Usual limit based on wall_time, number of explored branches and number of failures in the search tree...
void UpdateLimits(absl::Duration time, int64 branches, int64 failures, int64 solutions)
void Switch(Solver *const solver)
RouteConstructor(Assignment *const assignment, RoutingModel *const model, bool check_assignment, int64 num_indices, const std::vector< Link > &links_list)
const std::vector< std::vector< int > > & final_routes() const
Dimensions represent quantities accumulated at nodes along the routes.
void SetCumulVarPiecewiseLinearCost(int64 index, const PiecewiseLinearFunction &cost)
Sets a piecewise linear cost on the cumul variable of a given variable index.
const std::vector< IntVar * > & cumuls() const
Like CumulVar(), TransitVar(), SlackVar() but return the whole variable vectors instead (indexed by i...
const std::vector< std::pair< int64, int64 > > & GetBreakDistanceDurationOfVehicle(int vehicle) const
Returns the pairs (distance, duration) specified by break distance constraints.
RoutingModel * model() const
Returns the model on which the dimension was created.
void SetSpanUpperBoundForVehicle(int64 upper_bound, int vehicle)
!defined(SWIGCSHARP) && !defined(SWIGJAVA) !defined(SWIGPYTHON)
bool HasPickupToDeliveryLimits() const
IntVar * CumulVar(int64 index) const
Get the cumul, transit and slack variables for the given node (given as int64 var index).
int64 GetCumulVarSoftLowerBoundCoefficient(int64 index) const
Returns the cost coefficient of the soft lower bound of a cumul variable for a given variable index.
bool HasCumulVarSoftLowerBound(int64 index) const
Returns true if a soft lower bound has been set for a given variable index.
void SetSpanCostCoefficientForAllVehicles(int64 coefficient)
int64 GetCumulVarSoftLowerBound(int64 index) const
Returns the soft lower bound of a cumul variable for a given variable index.
std::function< int64(int, int)> PickupToDeliveryLimitFunction
Limits, in terms of maximum difference between the cumul variables, between the pickup and delivery a...
void SetBreakDistanceDurationOfVehicle(int64 distance, int64 duration, int vehicle)
With breaks supposed to be consecutive, this forces the distance between breaks of size at least mini...
bool HasBreakConstraints() const
Returns true if any break interval or break distance was defined.
void InitializeBreaks()
Sets up vehicle_break_intervals_, vehicle_break_distance_duration_, pre_travel_evaluators and post_tr...
int64 GetTransitValue(int64 from_index, int64 to_index, int64 vehicle) const
Returns the transition value for a given pair of nodes (as var index); this value is the one taken by...
int GetPreTravelEvaluatorOfVehicle(int vehicle) const
!defined(SWIGPYTHON)
const std::vector< IntervalVar * > & GetBreakIntervalsOfVehicle(int vehicle) const
Returns the break intervals set by SetBreakIntervalsOfVehicle().
void SetPickupToDeliveryLimitFunctionForPair(PickupToDeliveryLimitFunction limit_function, int pair_index)
int64 GetPickupToDeliveryLimitForPair(int pair_index, int pickup, int delivery) const
void SetSpanCostCoefficientForVehicle(int64 coefficient, int vehicle)
Sets a cost proportional to the dimension span on a given vehicle, or on all vehicles at once.
int64 GetCumulVarSoftUpperBound(int64 index) const
Returns the soft upper bound of a cumul variable for a given variable index.
const RoutingModel::TransitCallback2 & transit_evaluator(int vehicle) const
Returns the callback evaluating the transit value between two node indices for a given vehicle.
void SetCumulVarSoftUpperBound(int64 index, int64 upper_bound, int64 coefficient)
Sets a soft upper bound to the cumul variable of a given variable index.
const PiecewiseLinearFunction * GetCumulVarPiecewiseLinearCost(int64 index) const
Returns the piecewise linear cost of a cumul variable for a given variable index.
SortedDisjointIntervalList GetAllowedIntervalsInRange(int64 index, int64 min_value, int64 max_value) const
Returns allowed intervals for a given node in a given interval.
const std::string & name() const
Returns the name of the dimension.
void SetCumulVarSoftLowerBound(int64 index, int64 lower_bound, int64 coefficient)
Sets a soft lower bound to the cumul variable of a given variable index.
void SetBreakIntervalsOfVehicle(std::vector< IntervalVar * > breaks, int vehicle, int pre_travel_evaluator, int post_travel_evaluator)
Sets the breaks for a given vehicle.
bool HasCumulVarPiecewiseLinearCost(int64 index) const
Returns true if a piecewise linear cost has been set for a given variable index.
void SetGlobalSpanCostCoefficient(int64 coefficient)
Sets a cost proportional to the global dimension span, that is the difference between the largest val...
bool HasCumulVarSoftUpperBound(int64 index) const
Returns true if a soft upper bound has been set for a given variable index.
int64 GetCumulVarSoftUpperBoundCoefficient(int64 index) const
Returns the cost coefficient of the soft upper bound of a cumul variable for a given variable index.
int GetPostTravelEvaluatorOfVehicle(int vehicle) const
Manager for any NodeIndex <-> variable index conversion.
std::vector< NodeIndex > GetIndexToNodeMap() const
int64 GetEndIndex(int vehicle) const
int num_unique_depots() const
complete.
NodeIndex IndexToNode(int64 index) const
int64 GetStartIndex(int vehicle) const
friend class RoutingModelInspector
bool AddDimensionWithVehicleTransitAndCapacity(const std::vector< int > &evaluator_indices, int64 slack_max, std::vector< int64 > vehicle_capacities, bool fix_start_cumul_to_zero, const std::string &name)
int64 GetNumberOfDecisionsInFirstSolution(const RoutingSearchParameters &search_parameters) const
Returns statistics on first solution search, number of decisions sent to filters, number of decisions...
void SetFixedCostOfAllVehicles(int64 cost)
Sets the fixed cost of all vehicle routes.
void AddAtSolutionCallback(std::function< void()> callback)
Adds a callback called each time a solution is found during the search.
const std::string & GetPrimaryConstrainedDimension() const
Get the primary constrained dimension, or an empty string if it is unset.
Assignment * RestoreAssignment(const Assignment &solution)
Restores an assignment as a solution in the routing model and returns the new solution.
std::function< std::vector< operations_research::IntVar * >(RoutingModel *)> GetTabuVarsCallback
Sets the callback returning the variable to use for the Tabu Search metaheuristic.
void AddSearchMonitor(SearchMonitor *const monitor)
Adds a search monitor to the search used to solve the routing model.
int nodes() const
Sizes and indices Returns the number of nodes in the model.
bool ArcIsMoreConstrainedThanArc(int64 from, int64 to1, int64 to2)
Returns whether the arc from->to1 is more constrained than from->to2, taking into account,...
void AddLocalSearchOperator(LocalSearchOperator *ls_operator)
Adds a local search operator to the set of operators used to solve the vehicle routing problem.
Assignment * ReadAssignmentFromRoutes(const std::vector< std::vector< int64 >> &routes, bool ignore_inactive_indices)
Restores the routes as the current solution.
void AddVariableTargetToFinalizer(IntVar *var, int64 target)
Add a variable to set the closest possible to the target value in the solution finalizer.
int64 End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
const std::vector< int > & GetPairIndicesOfType(int type) const
RoutingTransitCallback1 TransitCallback1
const absl::flat_hash_set< int > & GetTemporalTypeIncompatibilitiesOfType(int type) const
void AddPickupAndDelivery(int64 pickup, int64 delivery)
Notifies that index1 and index2 form a pair of nodes which should belong to the same route.
std::string DebugOutputAssignment(const Assignment &solution_assignment, const std::string &dimension_to_print) const
Print some debugging information about an assignment, including the feasible intervals of the CumulVa...
const std::vector< absl::flat_hash_set< int > > & GetRequiredTypeAlternativesWhenRemovingType(int type) const
Returns the set of requirement alternatives when removing the given type.
CostClassIndex GetCostClassIndexOfVehicle(int64 vehicle) const
Get the cost class index of the given vehicle.
int GetVehicleClassesCount() const
Returns the number of different vehicle classes in the model.
PickupAndDeliveryPolicy GetPickupAndDeliveryPolicyOfVehicle(int vehicle) const
int64 Size() const
Returns the number of next variables in the model.
int64 UnperformedPenalty(int64 var_index) const
Get the "unperformed" penalty of a node.
void SetPickupAndDeliveryPolicyOfAllVehicles(PickupAndDeliveryPolicy policy)
Sets the Pickup and delivery policy of all vehicles.
Assignment * ReadAssignment(const std::string &file_name)
Reads an assignment from a file and returns the current solution.
Assignment * CompactAssignment(const Assignment &assignment) const
Returns a compacted version of the given assignment, in which all vehicles with id lower or equal to ...
bool ApplyLocksToAllVehicles(const std::vector< std::vector< int64 >> &locks, bool close_routes)
Applies lock chains to all vehicles to the next search, such that locks[p] is the lock chain for rout...
int RegisterStateDependentTransitCallback(VariableIndexEvaluator2 callback)
void AddToAssignment(IntVar *const var)
Adds an extra variable to the vehicle routing assignment.
bool IsVehicleAllowedForIndex(int vehicle, int64 index)
Returns true if a vehicle is allowed to visit a given node.
const TransitCallback1 & UnaryTransitCallbackOrNull(int callback_index) const
int64 Next(const Assignment &assignment, int64 index) const
Assignment inspection Returns the variable index of the node directly after the node corresponding to...
void AddVariableMinimizedByFinalizer(IntVar *var)
Adds a variable to minimize in the solution finalizer.
VisitTypePolicy
Set the node visit types and incompatibilities/requirements between the types (see below).
@ TYPE_ADDED_TO_VEHICLE
When visited, the number of types 'T' on the vehicle increases by one.
@ ADDED_TYPE_REMOVED_FROM_VEHICLE
When visited, one instance of type 'T' previously added to the route (TYPE_ADDED_TO_VEHICLE),...
@ TYPE_ON_VEHICLE_UP_TO_VISIT
With the following policy, the visit enforces that type 'T' is considered on the route from its start...
@ TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED
The visit doesn't have an impact on the number of types 'T' on the route, as it's (virtually) added a...
bool AddDimensionDependentDimensionWithVehicleCapacity(const std::vector< int > &pure_transits, const std::vector< int > &dependent_transits, const RoutingDimension *base_dimension, int64 slack_max, std::vector< int64 > vehicle_capacities, bool fix_start_cumul_to_zero, const std::string &name)
Creates a dimension with transits depending on the cumuls of another dimension.
static RoutingModel::StateDependentTransit MakeStateDependentTransit(const std::function< int64(int64)> &f, int64 domain_start, int64 domain_end)
Creates a cached StateDependentTransit from an std::function.
int64 GetFixedCostOfVehicle(int vehicle) const
Returns the route fixed cost taken into account if the route of the vehicle is not empty,...
void SetFixedCostOfVehicle(int64 cost, int vehicle)
Sets the fixed cost of one vehicle route.
Constraint * MakePathSpansAndTotalSlacks(const RoutingDimension *dimension, std::vector< IntVar * > spans, std::vector< IntVar * > total_slacks)
For every vehicle of the routing model:
friend class RoutingDimension
int64 GetArcCostForVehicle(int64 from_index, int64 to_index, int64 vehicle) const
Returns the cost of the transit arc between two nodes for a given vehicle.
LocalDimensionCumulOptimizer * GetMutableLocalCumulOptimizer(const RoutingDimension &dimension) const
const std::vector< absl::flat_hash_set< int > > & GetSameVehicleRequiredTypeAlternativesOfType(int type) const
Returns the set of same-vehicle requirement alternatives for the given type.
RoutingDimension * GetMutableDimension(const std::string &dimension_name) const
Returns a dimension from its name.
std::vector< std::vector< int64 > > GetRoutesFromAssignment(const Assignment &assignment)
Converts the solution in the given assignment to routes for all vehicles.
bool HasTemporalTypeRequirements() const
Solver * solver() const
Returns the underlying constraint solver.
void AddPickupAndDeliverySets(DisjunctionIndex pickup_disjunction, DisjunctionIndex delivery_disjunction)
Same as AddPickupAndDelivery but notifying that the performed node from the disjunction of index 'pic...
RoutingTransitCallback2 TransitCallback2
const std::vector< RoutingDimension * > & GetDimensions() const
Returns all dimensions of the model.
std::vector< std::string > GetAllDimensionNames() const
Outputs the names of all dimensions added to the routing engine.
int64 GetArcCostForFirstSolution(int64 from_index, int64 to_index) const
Returns the cost of the arc in the context of the first solution strategy.
bool AddDimensionWithVehicleTransits(const std::vector< int > &evaluator_indices, int64 slack_max, int64 capacity, bool fix_start_cumul_to_zero, const std::string &name)
IntVar * NextVar(int64 index) const
!defined(SWIGPYTHON)
std::function< StateDependentTransit(int64, int64)> VariableIndexEvaluator2
const Assignment * SolveFromAssignmentWithParameters(const Assignment *assignment, const RoutingSearchParameters &search_parameters, std::vector< const Assignment * > *solutions=nullptr)
@ ROUTING_SUCCESS
Problem solved successfully after calling RoutingModel::Solve().
@ ROUTING_FAIL
No solution found to the problem after calling RoutingModel::Solve().
@ ROUTING_INVALID
Model, model parameters or flags are not valid.
@ ROUTING_FAIL_TIMEOUT
Time limit reached before finding a solution with RoutingModel::Solve().
std::vector< RoutingDimension * > GetDimensionsWithSoftOrSpanCosts() const
Returns dimensions with soft or vehicle span costs.
void AddTemporalTypeIncompatibility(int type1, int type2)
std::vector< std::pair< int64, int64 > > GetPerfectBinaryDisjunctions() const
Returns the list of all perfect binary disjunctions, as pairs of variable indices: a disjunction is "...
SweepArranger * sweep_arranger() const
Returns the sweep arranger to be used by routing heuristics.
const std::vector< int > & GetSingleNodesOfType(int type) const
void SetVisitType(int64 index, int type, VisitTypePolicy type_policy)
const std::vector< absl::flat_hash_set< int > > & GetRequiredTypeAlternativesWhenAddingType(int type) const
Returns the set of requirement alternatives when adding the given type.
bool RoutesToAssignment(const std::vector< std::vector< int64 >> &routes, bool ignore_inactive_indices, bool close_routes, Assignment *const assignment) const
Fills an assignment from a specification of the routes of the vehicles.
void AddHardTypeIncompatibility(int type1, int type2)
Incompatibilities: Two nodes with "hard" incompatible types cannot share the same route at all,...
VehicleClassIndex GetVehicleClassIndexOfVehicle(int64 vehicle) const
int RegisterPositiveUnaryTransitCallback(TransitCallback1 callback)
void SetTabuVarsCallback(GetTabuVarsCallback tabu_var_callback)
void CloseVisitTypes()
This function should be called once all node visit types have been set and prior to adding any incomp...
const std::vector< DisjunctionIndex > & GetDisjunctionIndices(int64 index) const
Returns the indices of the disjunctions to which an index belongs.
static const int64 kNoPenalty
Constant used to express a hard constraint instead of a soft penalty.
void IgnoreDisjunctionsAlreadyForcedToZero()
SPECIAL: Makes the solver ignore all the disjunctions whose active variables are all trivially zero (...
void SetPickupAndDeliveryPolicyOfVehicle(PickupAndDeliveryPolicy policy, int vehicle)
const Assignment * SolveWithParameters(const RoutingSearchParameters &search_parameters, std::vector< const Assignment * > *solutions=nullptr)
Solves the current routing model with the given parameters.
int RegisterTransitCallback(TransitCallback2 callback)
IntVar * VehicleVar(int64 index) const
Returns the vehicle variable of the node corresponding to index.
const Assignment * PackCumulsOfOptimizerDimensionsFromAssignment(const Assignment *original_assignment, absl::Duration duration_limit)
For every dimension in the model with an optimizer in local/global_dimension_optimizers_,...
int64 GetArcCostForClass(int64 from_index, int64 to_index, int64 cost_class_index) const
Returns the cost of the segment between two nodes for a given cost class.
void AddWeightedVariableMinimizedByFinalizer(IntVar *var, int64 cost)
Adds a variable to minimize in the solution finalizer, with a weighted priority: the higher the more ...
int GetVisitType(int64 index) const
bool AddConstantDimensionWithSlack(int64 value, int64 capacity, int64 slack_max, bool fix_start_cumul_to_zero, const std::string &name)
Creates a dimension where the transit variable is constrained to be equal to 'value'; 'capacity' is t...
RoutingDimensionIndex DimensionIndex
void AssignmentToRoutes(const Assignment &assignment, std::vector< std::vector< int64 >> *const routes) const
Converts the solution in the given assignment to routes for all vehicles.
IntVar * ApplyLocks(const std::vector< int64 > &locks)
Applies a lock chain to the next search.
DisjunctionIndex AddDisjunction(const std::vector< int64 > &indices, int64 penalty=kNoPenalty, int64 max_cardinality=1)
Adds a disjunction constraint on the indices: exactly 'max_cardinality' of the indices are active.
Assignment * CompactAndCheckAssignment(const Assignment &assignment) const
Same as CompactAssignment() but also checks the validity of the final compact solution; if it is not ...
void AddSoftSameVehicleConstraint(const std::vector< int64 > &indices, int64 cost)
Adds a soft contraint to force a set of variable indices to be on the same vehicle.
LocalDimensionCumulOptimizer * GetMutableLocalCumulMPOptimizer(const RoutingDimension &dimension) const
bool HasHardTypeIncompatibilities() const
Returns true iff any hard (resp.
bool AddMatrixDimension(std::vector< std::vector< int64 > > values, int64 capacity, bool fix_start_cumul_to_zero, const std::string &name)
Creates a dimension where the transit variable is constrained to be equal to 'values[i][next(i)]' for...
void AddRequiredTypeAlternativesWhenRemovingType(int dependent_type, absl::flat_hash_set< int > required_type_alternatives)
The following requirements apply when visiting dependent nodes that remove their type from the route,...
std::vector< std::vector< std::pair< int64, int64 > > > GetCumulBounds(const Assignment &solution_assignment, const RoutingDimension &dimension)
Returns a vector cumul_bounds, for which cumul_bounds[i][j] is a pair containing the minimum and maxi...
int64 GetHomogeneousCost(int64 from_index, int64 to_index) const
Returns the cost of the segment between two nodes supposing all vehicle costs are the same (returns t...
int RegisterPositiveTransitCallback(TransitCallback2 callback)
PickupAndDeliveryPolicy
Types of precedence policy applied to pickup and delivery pairs.
@ PICKUP_AND_DELIVERY_LIFO
Deliveries must be performed in reverse order of pickups.
@ PICKUP_AND_DELIVERY_NO_ORDER
Any precedence is accepted.
@ PICKUP_AND_DELIVERY_FIFO
Deliveries must be performed in the same order as pickups.
void CloseModelWithParameters(const RoutingSearchParameters &search_parameters)
Same as above taking search parameters (as of 10/2015 some the parameters have to be set when closing...
int vehicles() const
Returns the number of vehicle routes in the model.
void AddVariableMaximizedByFinalizer(IntVar *var)
Adds a variable to maximize in the solution finalizer (see above for information on the solution fina...
const std::vector< IntVar * > & Nexts() const
Returns all next variables of the model, such that Nexts(i) is the next variable of the node correspo...
bool HasTypeRegulations() const
Returns true iff the model has any incompatibilities or requirements set on node types.
RoutingVehicleClassIndex VehicleClassIndex
bool AddDimension(int evaluator_index, int64 slack_max, int64 capacity, bool fix_start_cumul_to_zero, const std::string &name)
Model creation.
void AddIntervalToAssignment(IntervalVar *const interval)
void SetArcCostEvaluatorOfAllVehicles(int evaluator_index)
Sets the cost function of the model such that the cost of a segment of a route between node 'from' an...
void SetAmortizedCostFactorsOfAllVehicles(int64 linear_cost_factor, int64 quadratic_cost_factor)
The following methods set the linear and quadratic cost factors of vehicles (must be positive values)...
GlobalDimensionCumulOptimizer * GetMutableGlobalCumulOptimizer(const RoutingDimension &dimension) const
Returns the global/local dimension cumul optimizer for a given dimension, or nullptr if there is none...
bool HasSameVehicleTypeRequirements() const
Returns true iff any same-route (resp.
IntVar * CostVar() const
Returns the global cost variable which is being minimized.
const VariableIndexEvaluator2 & StateDependentTransitCallback(int callback_index) const
void SetAssignmentFromOtherModelAssignment(Assignment *target_assignment, const RoutingModel *source_model, const Assignment *source_assignment)
Given a "source_model" and its "source_assignment", resets "target_assignment" with the IntVar variab...
void AddSameVehicleRequiredTypeAlternatives(int dependent_type, absl::flat_hash_set< int > required_type_alternatives)
Requirements: NOTE: As of 2019-04, cycles in the requirement graph are not supported,...
const absl::flat_hash_set< int > & GetHardTypeIncompatibilitiesOfType(int type) const
Returns visit types incompatible with a given type.
bool IsMatchingModel() const
Returns true if a vehicle/node matching problem is detected.
bool AddDimensionWithVehicleCapacity(int evaluator_index, int64 slack_max, std::vector< int64 > vehicle_capacities, bool fix_start_cumul_to_zero, const std::string &name)
int RegisterUnaryTransitCallback(TransitCallback1 callback)
Registers 'callback' and returns its index.
int64 Start(int vehicle) const
Model inspection.
int64 GetDepot() const
Returns the variable index of the first starting or ending node of all routes.
bool WriteAssignment(const std::string &file_name) const
Writes the current solution to a file containing an AssignmentProto.
RoutingCostClassIndex CostClassIndex
bool HasTemporalTypeIncompatibilities() const
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
const TransitCallback2 & TransitCallback(int callback_index) const
int GetNumOfSingletonNodes() const
Returns the number of non-start/end nodes which do not appear in a pickup/delivery pair.
void AddRequiredTypeAlternativesWhenAddingType(int dependent_type, absl::flat_hash_set< int > required_type_alternatives)
If type_D depends on type_R when adding type_D, any node_D of type_D and VisitTypePolicy TYPE_ADDED_T...
void CloseModel()
Closes the current routing model; after this method is called, no modification to the model can be do...
static const DimensionIndex kNoDimension
Constant used to express the "no dimension" index, returned when a dimension name does not correspond...
bool CostsAreHomogeneousAcrossVehicles() const
Whether costs are homogeneous across all vehicles.
void SetAmortizedCostFactorsOfVehicle(int64 linear_cost_factor, int64 quadratic_cost_factor, int vehicle)
Sets the linear and quadratic cost factor of the given vehicle.
bool IsStart(int64 index) const
Returns true if 'index' represents the first node of a route.
bool IsEnd(int64 index) const
Returns true if 'index' represents the last node of a route.
const Assignment * Solve(const Assignment *assignment=nullptr)
Solves the current routing model; closes the current model.
static const DisjunctionIndex kNoDisjunction
Constant used to express the "no disjunction" index, returned when a node does not appear in any disj...
void SetArcCostEvaluatorOfVehicle(int evaluator_index, int vehicle)
Sets the cost function for a given vehicle route.
const std::vector< std::pair< int, int > > & GetPickupIndexPairs(int64 node_index) const
Returns pairs for which the node is a pickup; the first element of each pair is the index in the pick...
int64 UnperformedPenaltyOrValue(int64 default_value, int64 var_index) const
Same as above except that it returns default_value instead of 0 when penalty is not well defined (def...
int64 ComputeLowerBound()
Computes a lower bound to the routing problem solving a linear assignment problem.
bool HasDimension(const std::string &dimension_name) const
Returns true if a dimension exists for a given dimension name.
bool IsVehicleUsed(const Assignment &assignment, int vehicle) const
Returns true if the route of 'vehicle' is non empty in 'assignment'.
bool AddVectorDimension(std::vector< int64 > values, int64 capacity, bool fix_start_cumul_to_zero, const std::string &name)
Creates a dimension where the transit variable is constrained to be equal to 'values[i]' for node i; ...
int64 GetNumberOfRejectsInFirstSolution(const RoutingSearchParameters &search_parameters) const
RoutingModel(const RoutingIndexManager &index_manager)
Constructor taking an index manager.
RoutingDisjunctionIndex DisjunctionIndex
const std::vector< std::pair< int, int > > & GetDeliveryIndexPairs(int64 node_index) const
Same as above for deliveries.
IntVar * ActiveVar(int64 index) const
Returns the active variable of the node corresponding to index.
VisitTypePolicy GetVisitTypePolicy(int64 index) const
void SetAllowedVehiclesForIndex(const std::vector< int > &vehicles, int64 index)
Sets the vehicles which can visit a given node.
const RoutingDimension & GetDimensionOrDie(const std::string &dimension_name) const
Returns a dimension from its name. Dies if the dimension does not exist.
RoutingModelInspector(RoutingModel *model)
void EndVisitConstraint(const std::string &type_name, const Constraint *const constraint) override
void EndVisitModel(const std::string &solver_name) override
~RoutingModelInspector() override
void VisitIntegerArrayArgument(const std::string &arg_name, const std::vector< int64 > &values) override
void VisitIntegerExpressionArgument(const std::string &type_name, IntExpr *const expr) override
Visit integer expression argument.
static const char kLightElement2[]
static const char kRemoveValues[]
static const char kLightElement[]
Constraint types.
A search monitor is a simple set of callbacks to monitor all search events.
int solution_count() const
Returns how many solutions were stored during the search.
Assignment * solution(int n) const
Returns the nth solution.
IntExpr * MakeElement(const std::vector< int64 > &values, IntVar *const index)
values[index]
Constraint * MakeEquality(IntExpr *const left, IntExpr *const right)
left == right
std::function< bool(int64, int64, int64)> VariableValueComparator
@ ASSIGN_MIN_VALUE
Selects the min value of the selected variable.
void AddConstraint(Constraint *const c)
Adds the constraint 'c' to the model.
@ FULLPATHLNS
Operator which relaxes one entire path and all inactive nodes, thus defining num_paths neighbors.
@ OROPT
Relocate: OROPT and RELOCATE.
@ PATHLNS
Operator which relaxes two sub-chains of three consecutive arcs each.
@ UNACTIVELNS
Operator which relaxes all inactive nodes and one sub-chain of six consecutive arcs.
@ CHOOSE_STATIC_GLOBAL_BEST
Pairs are compared at the first call of the selector, and results are cached.
IntExpr * MakeMax(const std::vector< IntVar * > &vars)
std::max(vars)
IntExpr * MakeDifference(IntExpr *const left, IntExpr *const right)
left - right
std::function< int64(int64)> IndexEvaluator1
Callback typedefs.
static ConstraintSolverParameters DefaultSolverParameters()
Create a ConstraintSolverParameters proto with all the default values.
T * RevAlloc(T *object)
Registers the given object as being reversible.
@ CHOOSE_FIRST_UNBOUND
Select the first unbound variable.
@ CHOOSE_PATH
Selects the next unbound variable on a path, the path being defined by the variables: var[i] correspo...
std::function< int64(int64, int64)> IndexEvaluator2
IntExpr * MakeSemiContinuousExpr(IntExpr *const expr, int64 fixed_charge, int64 step)
Semi continuous Expression (x <= 0 -> f(x) = 0; x > 0 -> f(x) = ax + b) a >= 0 and b >= 0.
IntExpr * MakeSum(IntExpr *const left, IntExpr *const right)
left + right.
Assignment * MakeAssignment()
This method creates an empty assignment.
IntExpr * MakeProd(IntExpr *const left, IntExpr *const right)
left * right
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
EvaluatorLocalSearchOperators
This enum is used in Solver::MakeOperator associated with an evaluator to specify the neighborhood to...
@ TSPOPT
Sliding TSP operator.
@ LK
Lin-Kernighan local search.
@ LE
Move is accepted when the current objective value <= objective.Max.
This class represents a sorted list of disjoint, closed intervals.
Iterator InsertInterval(int64 start, int64 end)
Adds the interval [start..end] to the list, and merges overlapping or immediately adjacent intervals ...
ConstIterator end() const
IntervalSet::iterator Iterator
Iterator FirstIntervalGreaterOrEqual(int64 value) const
Returns an iterator to either:
void ArrangeIndices(std::vector< int64 > *indices)
SweepArranger(const std::vector< std::pair< int64, int64 >> &points)
void SetSectors(int sectors)
Decision * Next(Solver *const solver) override
This is the main method of the decision builder class.
SweepBuilder(RoutingModel *const model, bool check_assignment)
TypeIncompatibilityChecker(const RoutingModel &model, bool check_hard_incompatibilities)
virtual bool HasRegulationsToCheck() const =0
virtual bool CheckTypeRegulations(int type, VisitTypePolicy policy, int pos)=0
bool CheckVehicle(int vehicle, const std::function< int64(int64)> &next_accessor)
virtual void OnInitializeCheck()
TypeRegulationsChecker(const RoutingModel &model)
virtual bool FinalizeCheck() const
bool TypeCurrentlyOnRoute(int type, int pos) const
Returns true iff there's at least one instance of the given type on the route when scanning the route...
const RoutingModel & model_
void InitializeCheck(int vehicle, const std::function< int64(int64)> &next_accessor)
bool TypeOccursOnRoute(int type) const
Returns true iff any occurrence of the given type was seen on the route, i.e.
The following constraint ensures that incompatibilities and requirements between types are respected.
void Post() override
This method is called when the constraint is processed by the solver.
void InitialPropagate() override
This method performs the initial propagation of the constraint.
TypeRegulationsConstraint(const RoutingModel &model)
IntegerRange< NodeIndex > AllNodes() const
const std::vector< IntVar * > cumuls_
static const int64 kint64max
static const int64 kint64min
void STLDeleteElements(T *container)
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
bool FindCopy(const Collection &collection, const Key &key, Value *const value)
Collection::value_type::second_type & LookupOrInsert(Collection *const collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
bool ContainsKey(const Collection &collection, const Key &key)
const Collection::value_type::second_type * FindOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
std::function< int64(const Model &)> Value(IntegerVariable v)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
bool SolveModelWithSat(const RoutingModel &model, const RoutingSearchParameters &search_parameters, const Assignment *initial_solution, Assignment *solution)
Attempts to solve the model using the cp-sat solver.
void AppendDimensionCumulFilters(const std::vector< RoutingDimension * > &dimensions, const RoutingSearchParameters ¶meters, bool filter_objective_cost, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
Demon * MakeDelayedConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
int64 CapSub(int64 x, int64 y)
constexpr int FirstSolutionStrategy_Value_Value_ARRAYSIZE
RoutingModelParameters DefaultRoutingModelParameters()
Demon * MakeConstraintDemon0(Solver *const s, T *const ct, void(T::*method)(), const std::string &name)
IntVarLocalSearchFilter * MakeVehicleBreaksFilter(const RoutingModel &routing_model, const RoutingDimension &dimension)
std::string FindErrorInRoutingSearchParameters(const RoutingSearchParameters &search_parameters)
Returns an empty std::string if the routing search parameters are valid, and a non-empty,...
IntVarLocalSearchFilter * MakeVehicleAmortizedCostFilter(const RoutingModel &routing_model)
Demon * MakeConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
struct operations_research::SweepIndexSortAngle SweepIndexAngleComparator
void SetAssignmentFromAssignment(Assignment *target_assignment, const std::vector< IntVar * > &target_vars, const Assignment *source_assignment, const std::vector< IntVar * > &source_vars)
NOLINT.
IntVarLocalSearchFilter * MakeCPFeasibilityFilter(RoutingModel *routing_model)
uint64 ThoroughHash(const char *bytes, size_t len)
int64 CapAdd(int64 x, int64 y)
DimensionSchedulingStatus
struct operations_research::SweepIndexSortDistance SweepIndexDistanceComparator
IntVarLocalSearchFilter * MakeMaxActiveVehiclesFilter(const RoutingModel &routing_model)
int64 One()
This method returns 1.
struct operations_research::LinkSort LinkComparator
int64 CapProd(int64 x, int64 y)
std::function< int64(int64, int64)> RoutingTransitCallback2
RoutingSearchParameters DefaultRoutingSearchParameters()
IntVarLocalSearchFilter * MakeVehicleVarFilter(const RoutingModel &routing_model)
std::function< int64(int64)> RoutingTransitCallback1
RangeMinMaxIndexFunction * MakeCachedRangeMinMaxIndexFunction(const std::function< int64(int64)> &f, int64 domain_start, int64 domain_end)
std::string MemoryUsage()
IntVarLocalSearchFilter * MakePickupDeliveryFilter(const RoutingModel &routing_model, const RoutingModel::IndexPairs &pairs, const std::vector< RoutingModel::PickupAndDeliveryPolicy > &vehicle_policies)
void FillPathEvaluation(const std::vector< int64 > &path, const RoutingModel::TransitCallback2 &evaluator, std::vector< int64 > *values)
IntVarLocalSearchFilter * MakeTypeRegulationsFilter(const RoutingModel &routing_model)
bool HasUnaryDimension(const std::vector< RoutingDimension * > &dimensions)
static const int kUnassigned
RangeIntToIntFunction * MakeCachedIntToIntFunction(const std::function< int64(int64)> &f, int64 domain_start, int64 domain_end)
LocalSearchFilter * MakePathStateFilter(Solver *solver, std::unique_ptr< PathState > path_state, const std::vector< IntVar * > &nexts)
void AppendLightWeightDimensionFilters(const PathState *path_state, const std::vector< RoutingDimension * > &dimensions, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
IntVarLocalSearchFilter * MakeNodeDisjunctionFilter(const RoutingModel &routing_model)
DecisionBuilder * MakeSetValuesFromTargets(Solver *solver, std::vector< IntVar * > variables, std::vector< int64 > targets)
A decision builder which tries to assign values to variables as close as possible to target values fi...
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
static int input(yyscan_t yyscanner)
ABSL_FLAG(int64, sweep_sectors, 1, "The number of sectors the space is divided before it is sweeped " "by the ray.")
#define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators)
std::vector< int > var_indices
Link(std::pair< int, int > link, double value, int vehicle_class, int64 start_depot, int64 end_depot)
std::pair< int, int > link
bool operator()(const Link &link1, const Link &link2) const
static bool LessThan(const CostClass &a, const CostClass &b)
Comparator for STL containers and algorithms.
What follows is relevant for models with time/state dependent transits.
int64 fixed_cost
Contrarily to CostClass, here we need strict equivalence.
absl::StrongVector< DimensionIndex, int64 > dimension_capacities
absl::StrongVector< DimensionIndex, int64 > dimension_evaluator_classes
dimension_evaluators[d]->Run(from, to) is the transit value of arc from->to for a dimension d.
int start_equivalence_class
Vehicle start and end equivalence classes.
absl::StrongVector< DimensionIndex, int64 > dimension_start_cumuls_min
Bounds of cumul variables at start and end vehicle nodes.
absl::StrongVector< DimensionIndex, int64 > dimension_end_cumuls_min
absl::StrongVector< DimensionIndex, int64 > dimension_end_cumuls_max
int end_equivalence_class
uint64 unvisitable_nodes_fprint
Fingerprint of unvisitable non-start/end nodes.
static bool LessThan(const VehicleClass &a, const VehicleClass &b)
Comparator for STL containers and algorithms.
absl::StrongVector< DimensionIndex, int64 > dimension_start_cumuls_max
CostClassIndex cost_class_index
The cost class of the vehicle.
std::vector< int > type_index_of_vehicle
std::vector< std::set< VehicleClassEntry > > sorted_vehicle_classes_per_type
std::vector< std::deque< int > > vehicles_per_vehicle_class
SweepIndex(const int64 index, const double angle, const double distance)
bool operator()(const SweepIndex &node1, const SweepIndex &node2) const
bool operator()(const SweepIndex &node1, const SweepIndex &node2) const
int position_of_last_type_on_vehicle_up_to_visit
Position of the last node of policy TYPE_ON_VEHICLE_UP_TO_VISIT visited on the route.
int num_type_added_to_vehicle
Number of TYPE_ADDED_TO_VEHICLE and TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED node type policies seen on ...
int num_type_removed_from_vehicle
Number of ADDED_TYPE_REMOVED_FROM_VEHICLE (effectively removing a type from the route) and TYPE_SIMUL...