32 #include <type_traits>
36 #include "absl/container/flat_hash_map.h"
37 #include "absl/container/flat_hash_set.h"
38 #include "absl/flags/flag.h"
39 #include "absl/functional/bind_front.h"
40 #include "absl/memory/memory.h"
41 #include "absl/meta/type_traits.h"
42 #include "absl/status/statusor.h"
43 #include "absl/strings/str_cat.h"
44 #include "absl/strings/str_format.h"
45 #include "absl/strings/string_view.h"
46 #include "absl/time/time.h"
83 class ExtendedSwapActiveOperator;
84 class LocalSearchPhaseParameters;
85 class MakeActiveAndRelocate;
86 class MakeActiveOperator;
87 class MakeChainInactiveOperator;
88 class MakeInactiveOperator;
90 class RelocateAndMakeActiveOperator;
91 class SwapActiveOperator;
106 class SetValuesFromTargets :
public DecisionBuilder {
108 SetValuesFromTargets(std::vector<IntVar*> variables,
109 std::vector<int64_t> targets)
110 : variables_(std::move(variables)),
111 targets_(std::move(targets)),
113 steps_(variables_.size(), 0) {
114 DCHECK_EQ(variables_.size(), targets_.size());
116 Decision* Next(Solver*
const solver)
override {
117 int index = index_.Value();
118 while (
index < variables_.size() && variables_[
index]->Bound()) {
121 index_.SetValue(solver,
index);
122 if (
index >= variables_.size())
return nullptr;
123 const int64_t variable_min = variables_[
index]->Min();
124 const int64_t variable_max = variables_[
index]->Max();
127 if (targets_[
index] <= variable_min) {
128 return solver->MakeAssignVariableValue(variables_[
index], variable_min);
129 }
else if (targets_[
index] >= variable_max) {
130 return solver->MakeAssignVariableValue(variables_[
index], variable_max);
132 int64_t step = steps_[
index];
137 if (
value < variable_min || variable_max <
value) {
138 step = GetNextStep(step);
149 steps_.SetValue(solver,
index, GetNextStep(step));
150 return solver->MakeAssignVariableValueOrDoNothing(variables_[
index],
156 int64_t GetNextStep(int64_t step)
const {
157 return (step > 0) ? -step :
CapSub(1, step);
159 const std::vector<IntVar*> variables_;
160 const std::vector<int64_t> targets_;
162 RevArray<int64_t> steps_;
168 std::vector<IntVar*> variables,
169 std::vector<int64_t> targets) {
171 new SetValuesFromTargets(std::move(variables), std::move(targets)));
176 bool DimensionFixedTransitsEqualTransitEvaluatorForVehicle(
177 const RoutingDimension& dimension,
int vehicle) {
178 const RoutingModel*
const model = dimension.model();
179 int node =
model->Start(vehicle);
180 while (!
model->IsEnd(node)) {
181 if (!
model->NextVar(node)->Bound()) {
184 const int next =
model->NextVar(node)->Value();
185 if (dimension.transit_evaluator(vehicle)(node,
next) !=
186 dimension.FixedTransitVar(node)->Value()) {
194 bool DimensionFixedTransitsEqualTransitEvaluators(
195 const RoutingDimension& dimension) {
196 for (
int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
197 if (!DimensionFixedTransitsEqualTransitEvaluatorForVehicle(dimension,
205 class SetCumulsFromLocalDimensionCosts :
public DecisionBuilder {
207 SetCumulsFromLocalDimensionCosts(
208 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>*
210 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>*
212 SearchMonitor* monitor,
bool optimize_and_pack =
false)
213 : local_optimizers_(*local_optimizers),
214 local_mp_optimizers_(*local_mp_optimizers),
216 optimize_and_pack_(optimize_and_pack) {}
217 Decision* Next(Solver*
const solver)
override {
221 bool should_fail =
false;
222 for (
int i = 0; i < local_optimizers_.size(); ++i) {
223 const auto& local_optimizer = local_optimizers_[i];
224 const RoutingDimension*
const dimension = local_optimizer->dimension();
225 RoutingModel*
const model = dimension->model();
227 return model->NextVar(i)->Value();
229 const auto compute_cumul_values =
230 [
this, &
next](LocalDimensionCumulOptimizer* optimizer,
int vehicle,
231 std::vector<int64_t>* cumul_values,
232 std::vector<int64_t>* break_start_end_values) {
233 if (optimize_and_pack_) {
234 return optimizer->ComputePackedRouteCumuls(
235 vehicle,
next, cumul_values, break_start_end_values);
237 return optimizer->ComputeRouteCumuls(vehicle,
next, cumul_values,
238 break_start_end_values);
241 for (
int vehicle = 0; vehicle <
model->vehicles(); ++vehicle) {
243 DCHECK(DimensionFixedTransitsEqualTransitEvaluatorForVehicle(*dimension,
245 const bool vehicle_has_break_constraint =
246 dimension->HasBreakConstraints() &&
247 !dimension->GetBreakIntervalsOfVehicle(vehicle).empty();
248 LocalDimensionCumulOptimizer*
const optimizer =
249 vehicle_has_break_constraint ? local_mp_optimizers_[i].get()
250 : local_optimizer.get();
251 DCHECK(optimizer !=
nullptr);
252 std::vector<int64_t> cumul_values;
253 std::vector<int64_t> break_start_end_values;
255 optimizer, vehicle, &cumul_values, &break_start_end_values);
262 cumul_values.clear();
263 break_start_end_values.clear();
264 DCHECK(local_mp_optimizers_[i] !=
nullptr);
265 if (compute_cumul_values(local_mp_optimizers_[i].get(), vehicle,
266 &cumul_values, &break_start_end_values) ==
276 std::vector<IntVar*> cp_variables;
277 std::vector<int64_t> cp_values;
280 int current =
model->Start(vehicle);
282 cp_variables.push_back(dimension->CumulVar(current));
283 if (!
model->IsEnd(current)) {
284 current =
model->NextVar(current)->Value();
295 std::swap(cp_variables[1], cp_variables.back());
296 std::swap(cp_values[1], cp_values.back());
297 if (dimension->HasBreakConstraints()) {
299 dimension->GetBreakIntervalsOfVehicle(vehicle)) {
300 cp_variables.push_back(
interval->SafeStartExpr(0)->Var());
301 cp_variables.push_back(
interval->SafeEndExpr(0)->Var());
303 cp_values.insert(cp_values.end(), break_start_end_values.begin(),
304 break_start_end_values.end());
307 for (
int i = 0; i < cp_values.size(); ++i) {
309 cp_values[i] = cp_variables[i]->Min();
312 if (!solver->SolveAndCommit(
314 std::move(cp_values)),
328 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>&
330 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>&
331 local_mp_optimizers_;
332 SearchMonitor*
const monitor_;
333 const bool optimize_and_pack_;
336 class SetCumulsFromGlobalDimensionCosts :
public DecisionBuilder {
338 SetCumulsFromGlobalDimensionCosts(
339 const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>*
341 SearchMonitor* monitor,
bool optimize_and_pack =
false)
342 : global_optimizers_(*global_optimizers),
344 optimize_and_pack_(optimize_and_pack) {}
345 Decision* Next(Solver*
const solver)
override {
349 bool should_fail =
false;
350 for (
const auto& global_optimizer : global_optimizers_) {
351 const RoutingDimension* dimension = global_optimizer->dimension();
352 RoutingModel*
const model = dimension->model();
355 return model->NextVar(i)->Value();
358 DCHECK(DimensionFixedTransitsEqualTransitEvaluators(*dimension));
360 std::vector<int64_t> cumul_values;
361 std::vector<int64_t> break_start_end_values;
362 const bool cumuls_optimized =
364 ? global_optimizer->ComputePackedCumuls(
next, &cumul_values,
365 &break_start_end_values)
366 : global_optimizer->ComputeCumuls(
next, &cumul_values,
367 &break_start_end_values);
368 if (!cumuls_optimized) {
374 std::vector<IntVar*> cp_variables = dimension->cumuls();
375 std::vector<int64_t> cp_values;
377 if (dimension->HasBreakConstraints()) {
378 const int num_vehicles =
model->vehicles();
379 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
381 dimension->GetBreakIntervalsOfVehicle(vehicle)) {
382 cp_variables.push_back(
interval->SafeStartExpr(0)->Var());
383 cp_variables.push_back(
interval->SafeEndExpr(0)->Var());
386 cp_values.insert(cp_values.end(), break_start_end_values.begin(),
387 break_start_end_values.end());
390 for (
int i = 0; i < cp_values.size(); ++i) {
392 cp_values[i] = cp_variables[i]->Min();
395 if (!solver->SolveAndCommit(
397 std::move(cp_values)),
410 const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>&
412 SearchMonitor*
const monitor_;
413 const bool optimize_and_pack_;
419 const Assignment* original_assignment, absl::Duration duration_limit) {
421 if (original_assignment ==
nullptr)
return nullptr;
422 if (duration_limit <= absl::ZeroDuration())
return original_assignment;
423 if (global_dimension_optimizers_.empty() &&
424 local_dimension_optimizers_.empty()) {
425 DCHECK(local_dimension_mp_optimizers_.empty());
426 return original_assignment;
435 Assignment* packed_assignment = solver_->MakeAssignment();
439 std::vector<DecisionBuilder*> decision_builders;
440 decision_builders.push_back(solver_->MakeRestoreAssignment(preassignment_));
441 decision_builders.push_back(
442 solver_->MakeRestoreAssignment(packed_assignment));
443 decision_builders.push_back(
444 solver_->RevAlloc(
new SetCumulsFromLocalDimensionCosts(
445 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
446 GetOrCreateLargeNeighborhoodSearchLimit(),
448 decision_builders.push_back(
449 solver_->RevAlloc(
new SetCumulsFromGlobalDimensionCosts(
450 &global_dimension_optimizers_,
451 GetOrCreateLargeNeighborhoodSearchLimit(),
453 decision_builders.push_back(
454 CreateFinalizerForMinimizedAndMaximizedVariables());
457 solver_->Compose(decision_builders);
458 solver_->Solve(restore_pack_and_finalize,
459 packed_dimensions_assignment_collector_, limit);
461 if (packed_dimensions_assignment_collector_->
solution_count() != 1) {
462 LOG(
ERROR) <<
"The given assignment is not valid for this model, or cannot "
467 packed_assignment->
Copy(original_assignment);
469 packed_dimensions_assignment_collector_->
solution(0));
471 return packed_assignment;
479 return sweep_arranger_.get();
484 class DifferentFromValues :
public Constraint {
486 DifferentFromValues(
Solver* solver,
IntVar*
var, std::vector<int64_t> values)
487 :
Constraint(solver), var_(
var), values_(std::move(values)) {}
488 void Post()
override {}
489 void InitialPropagate()
override { var_->RemoveValues(values_); }
490 std::string DebugString()
const override {
return "DifferentFromValues"; }
491 void Accept(ModelVisitor*
const visitor)
const override {
501 const std::vector<int64_t> values_;
515 template <
typename F>
516 class LightFunctionElementConstraint :
public Constraint {
518 LightFunctionElementConstraint(Solver*
const solver, IntVar*
const var,
519 IntVar*
const index, F values,
520 std::function<
bool()> deep_serialize)
521 : Constraint(solver),
524 values_(std::move(values)),
525 deep_serialize_(std::move(deep_serialize)) {}
526 ~LightFunctionElementConstraint()
override {}
528 void Post()
override {
530 solver(),
this, &LightFunctionElementConstraint::IndexBound,
532 index_->WhenBound(demon);
535 void InitialPropagate()
override {
536 if (index_->Bound()) {
541 std::string DebugString()
const override {
542 return "LightFunctionElementConstraint";
545 void Accept(ModelVisitor*
const visitor)
const override {
552 if (deep_serialize_()) {
553 visitor->VisitInt64ToInt64Extension(values_, index_->Min(),
560 void IndexBound() { var_->SetValue(values_(index_->Min())); }
563 IntVar*
const index_;
565 std::function<bool()> deep_serialize_;
568 template <
typename F>
569 Constraint* MakeLightElement(Solver*
const solver, IntVar*
const var,
570 IntVar*
const index, F values,
571 std::function<
bool()> deep_serialize) {
572 return solver->RevAlloc(
new LightFunctionElementConstraint<F>(
573 solver,
var,
index, std::move(values), std::move(deep_serialize)));
581 template <
typename F>
582 class LightFunctionElement2Constraint :
public Constraint {
584 LightFunctionElement2Constraint(Solver*
const solver, IntVar*
const var,
585 IntVar*
const index1, IntVar*
const index2,
587 std::function<
bool()> deep_serialize)
588 : Constraint(solver),
592 values_(std::move(values)),
593 deep_serialize_(std::move(deep_serialize)) {}
594 ~LightFunctionElement2Constraint()
override {}
595 void Post()
override {
597 solver(),
this, &LightFunctionElement2Constraint::IndexBound,
599 index1_->WhenBound(demon);
600 index2_->WhenBound(demon);
602 void InitialPropagate()
override { IndexBound(); }
604 std::string DebugString()
const override {
605 return "LightFunctionElement2Constraint";
608 void Accept(ModelVisitor*
const visitor)
const override {
617 const int64_t index1_min = index1_->Min();
618 const int64_t index1_max = index1_->Max();
621 if (deep_serialize_()) {
622 for (
int i = index1_min; i <= index1_max; ++i) {
623 visitor->VisitInt64ToInt64Extension(
624 [
this, i](int64_t j) {
return values_(i, j); }, index2_->Min(),
633 if (index1_->Bound() && index2_->Bound()) {
634 var_->SetValue(values_(index1_->Min(), index2_->Min()));
639 IntVar*
const index1_;
640 IntVar*
const index2_;
642 std::function<bool()> deep_serialize_;
645 template <
typename F>
646 Constraint* MakeLightElement2(Solver*
const solver, IntVar*
const var,
647 IntVar*
const index1, IntVar*
const index2,
648 F values, std::function<
bool()> deep_serialize) {
649 return solver->RevAlloc(
new LightFunctionElement2Constraint<F>(
650 solver,
var, index1, index2, std::move(values),
651 std::move(deep_serialize)));
655 template <
class A,
class B>
656 static int64_t ReturnZero(A
a, B
b) {
662 for (
int i = 0; i < size1; i++) {
663 for (
int j = 0; j < size2; j++) {
688 : nodes_(index_manager.num_nodes()),
689 vehicles_(index_manager.num_vehicles()),
690 max_active_vehicles_(vehicles_),
691 fixed_cost_of_vehicle_(vehicles_, 0),
693 linear_cost_factor_of_vehicle_(vehicles_, 0),
694 quadratic_cost_factor_of_vehicle_(vehicles_, 0),
695 vehicle_amortized_cost_factors_set_(false),
696 consider_empty_route_costs_(vehicles_, false),
698 costs_are_homogeneous_across_vehicles_(
700 cache_callbacks_(false),
702 vehicle_pickup_delivery_policy_(vehicles_, PICKUP_AND_DELIVERY_NO_ORDER),
703 has_hard_type_incompatibilities_(false),
704 has_temporal_type_incompatibilities_(false),
705 has_same_vehicle_type_requirements_(false),
706 has_temporal_type_requirements_(false),
710 manager_(index_manager) {
712 vehicle_to_transit_cost_.assign(
716 cache_callbacks_ = (nodes_ <=
parameters.max_callback_cache_size());
719 ConstraintSolverParameters solver_parameters =
722 solver_ = absl::make_unique<Solver>(
"Routing", solver_parameters);
727 const int64_t size =
Size();
728 index_to_pickup_index_pairs_.resize(size);
729 index_to_delivery_index_pairs_.resize(size);
731 index_to_type_policy_.resize(index_manager.
num_indices());
734 for (
int v = 0; v < index_manager.
num_vehicles(); ++v) {
736 index_to_vehicle_[starts_[v]] = v;
738 index_to_vehicle_[ends_[v]] = v;
741 const std::vector<RoutingIndexManager::NodeIndex>& index_to_node =
743 index_to_equivalence_class_.resize(index_manager.
num_indices());
744 for (
int i = 0; i < index_to_node.size(); ++i) {
745 index_to_equivalence_class_[i] = index_to_node[i].value();
747 allowed_vehicles_.resize(
Size() + vehicles_);
750 void RoutingModel::Initialize() {
751 const int size =
Size();
753 solver_->MakeIntVarArray(size, 0, size + vehicles_ - 1,
"Nexts", &nexts_);
754 solver_->AddConstraint(solver_->MakeAllDifferent(nexts_,
false));
755 index_to_disjunctions_.resize(size + vehicles_);
758 solver_->MakeIntVarArray(size + vehicles_, -1, vehicles_ - 1,
"Vehicles",
761 solver_->MakeBoolVarArray(size,
"Active", &active_);
763 solver_->MakeBoolVarArray(vehicles_,
"ActiveVehicle", &vehicle_active_);
765 solver_->MakeBoolVarArray(vehicles_,
"VehicleCostsConsidered",
766 &vehicle_costs_considered_);
768 solver_->MakeBoolVarArray(size + vehicles_,
"IsBoundToEnd",
773 preassignment_ = solver_->MakeAssignment();
780 absl::flat_hash_set<RangeIntToIntFunction*> value_functions_delete;
781 absl::flat_hash_set<RangeMinMaxIndexFunction*> index_functions_delete;
782 for (
const auto& cache_line : state_dependent_transit_evaluators_cache_) {
783 for (
const auto& key_transit : *cache_line) {
784 value_functions_delete.insert(key_transit.second.transit);
785 index_functions_delete.insert(key_transit.second.transit_plus_identity);
796 return model->RegisterPositiveTransitCallback(std::move(
callback));
802 RoutingModel*
model) {
804 return model->RegisterPositiveUnaryTransitCallback(std::move(
callback));
806 return model->RegisterUnaryTransitCallback(std::move(
callback));
811 return RegisterUnaryCallback(
812 [
this, values = std::move(values)](int64_t i) {
816 std::all_of(std::cbegin(values), std::cend(values),
817 [](int64_t transit) {
return transit >= 0; }),
822 const int index = unary_transit_evaluators_.size();
823 unary_transit_evaluators_.push_back(std::move(
callback));
825 return unary_transit_evaluators_[
index](i);
830 std::vector<std::vector<int64_t> > values) {
831 bool all_transits_positive =
true;
832 for (
const std::vector<int64_t>& transit_values : values) {
833 all_transits_positive =
834 std::all_of(std::cbegin(transit_values), std::cend(transit_values),
835 [](int64_t transit) {
return transit >= 0; });
836 if (!all_transits_positive) {
840 return RegisterCallback(
841 [
this, values = std::move(values)](int64_t i, int64_t j) {
845 all_transits_positive,
this);
850 is_transit_evaluator_positive_.push_back(
true);
851 DCHECK(TransitCallbackPositive(
857 if (cache_callbacks_) {
859 std::vector<int64_t> cache(size * size, 0);
860 for (
int i = 0; i < size; ++i) {
861 for (
int j = 0; j < size; ++j) {
862 cache[i * size + j] =
callback(i, j);
865 transit_evaluators_.push_back(
866 [cache, size](int64_t i, int64_t j) {
return cache[i * size + j]; });
868 transit_evaluators_.push_back(std::move(
callback));
870 if (transit_evaluators_.size() != unary_transit_evaluators_.size()) {
871 DCHECK_EQ(transit_evaluators_.size(), unary_transit_evaluators_.size() + 1);
872 unary_transit_evaluators_.push_back(
nullptr);
874 if (transit_evaluators_.size() != is_transit_evaluator_positive_.size()) {
876 is_transit_evaluator_positive_.size() + 1);
877 is_transit_evaluator_positive_.push_back(
false);
879 return transit_evaluators_.size() - 1;
883 is_transit_evaluator_positive_.push_back(
true);
891 state_dependent_transit_evaluators_cache_.push_back(
892 absl::make_unique<StateDependentTransitCallbackCache>());
893 StateDependentTransitCallbackCache*
const cache =
894 state_dependent_transit_evaluators_cache_.back().get();
895 state_dependent_transit_evaluators_.push_back(
896 [cache,
callback](int64_t i, int64_t j) {
900 cache->insert({CacheKey(i, j),
value});
903 return state_dependent_transit_evaluators_.size() - 1;
906 void RoutingModel::AddNoCycleConstraintInternal() {
907 if (no_cycle_constraint_ ==
nullptr) {
908 no_cycle_constraint_ = solver_->MakeNoCycle(nexts_, active_);
909 solver_->AddConstraint(no_cycle_constraint_);
914 int64_t
capacity,
bool fix_start_cumul_to_zero,
915 const std::string&
name) {
916 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
917 std::vector<int64_t> capacities(vehicles_,
capacity);
918 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
919 std::move(capacities),
920 fix_start_cumul_to_zero,
name);
924 const std::vector<int>& evaluator_indices, int64_t slack_max,
925 int64_t
capacity,
bool fix_start_cumul_to_zero,
const std::string&
name) {
926 std::vector<int64_t> capacities(vehicles_,
capacity);
927 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
928 std::move(capacities),
929 fix_start_cumul_to_zero,
name);
933 int evaluator_index, int64_t slack_max,
934 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
935 const std::string&
name) {
936 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
937 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
938 std::move(vehicle_capacities),
939 fix_start_cumul_to_zero,
name);
943 const std::vector<int>& evaluator_indices, int64_t slack_max,
944 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
945 const std::string&
name) {
946 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
947 std::move(vehicle_capacities),
948 fix_start_cumul_to_zero,
name);
951 bool RoutingModel::AddDimensionWithCapacityInternal(
952 const std::vector<int>& evaluator_indices, int64_t slack_max,
953 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
954 const std::string&
name) {
955 CHECK_EQ(vehicles_, vehicle_capacities.size());
956 return InitializeDimensionInternal(
957 evaluator_indices, std::vector<int>(), slack_max, fix_start_cumul_to_zero,
961 bool RoutingModel::InitializeDimensionInternal(
962 const std::vector<int>& evaluator_indices,
963 const std::vector<int>& state_dependent_evaluator_indices,
964 int64_t slack_max,
bool fix_start_cumul_to_zero,
965 RoutingDimension* dimension) {
966 CHECK(dimension !=
nullptr);
967 CHECK_EQ(vehicles_, evaluator_indices.size());
968 CHECK((dimension->base_dimension_ ==
nullptr &&
969 state_dependent_evaluator_indices.empty()) ||
970 vehicles_ == state_dependent_evaluator_indices.size());
973 dimension_name_to_index_[dimension->name()] = dimension_index;
974 dimensions_.push_back(dimension);
975 dimension->Initialize(evaluator_indices, state_dependent_evaluator_indices,
977 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
978 nexts_, active_, dimension->cumuls(), dimension->transits()));
979 if (fix_start_cumul_to_zero) {
980 for (
int i = 0; i < vehicles_; ++i) {
981 IntVar*
const start_cumul = dimension->CumulVar(
Start(i));
983 start_cumul->SetValue(0);
994 bool fix_start_cumul_to_zero,
const std::string& dimension_name) {
995 const int evaluator_index =
996 RegisterUnaryCallback([
value](int64_t) {
return value; },
998 return std::make_pair(evaluator_index,
1000 fix_start_cumul_to_zero, dimension_name));
1004 std::vector<int64_t> values, int64_t
capacity,
bool fix_start_cumul_to_zero,
1005 const std::string& dimension_name) {
1007 return std::make_pair(evaluator_index,
1009 fix_start_cumul_to_zero, dimension_name));
1013 std::vector<std::vector<int64_t>> values, int64_t
capacity,
1014 bool fix_start_cumul_to_zero,
const std::string& dimension_name) {
1016 return std::make_pair(evaluator_index,
1018 fix_start_cumul_to_zero, dimension_name));
1030 CHECK(callback_ !=
nullptr);
1034 int64_t Min()
const override {
1036 const int idx_min = index_->Min();
1037 const int idx_max = index_->Max() + 1;
1038 return (idx_min < idx_max) ? callback_->RangeMin(idx_min, idx_max)
1041 void SetMin(int64_t new_min)
override {
1042 const int64_t old_min = Min();
1043 const int64_t old_max = Max();
1044 if (old_min < new_min && new_min <= old_max) {
1045 const int64_t old_idx_min = index_->Min();
1046 const int64_t old_idx_max = index_->Max() + 1;
1047 if (old_idx_min < old_idx_max) {
1048 const int64_t new_idx_min = callback_->RangeFirstInsideInterval(
1049 old_idx_min, old_idx_max, new_min, old_max + 1);
1050 index_->SetMin(new_idx_min);
1051 if (new_idx_min < old_idx_max) {
1052 const int64_t new_idx_max = callback_->RangeLastInsideInterval(
1053 new_idx_min, old_idx_max, new_min, old_max + 1);
1054 index_->SetMax(new_idx_max);
1059 int64_t Max()
const override {
1061 const int idx_min = index_->Min();
1062 const int idx_max = index_->Max() + 1;
1063 return (idx_min < idx_max) ? callback_->RangeMax(idx_min, idx_max)
1066 void SetMax(int64_t new_max)
override {
1067 const int64_t old_min = Min();
1068 const int64_t old_max = Max();
1069 if (old_min <= new_max && new_max < old_max) {
1070 const int64_t old_idx_min = index_->Min();
1071 const int64_t old_idx_max = index_->Max() + 1;
1072 if (old_idx_min < old_idx_max) {
1073 const int64_t new_idx_min = callback_->RangeFirstInsideInterval(
1074 old_idx_min, old_idx_max, old_min, new_max + 1);
1075 index_->SetMin(new_idx_min);
1076 if (new_idx_min < old_idx_max) {
1077 const int64_t new_idx_max = callback_->RangeLastInsideInterval(
1078 new_idx_min, old_idx_max, old_min, new_max + 1);
1079 index_->SetMax(new_idx_max);
1084 void WhenRange(Demon* d)
override { index_->WhenRange(d); }
1087 const RangeIntToIntFunction*
const callback_;
1088 IntVar*
const index_;
1091 IntExpr* MakeRangeMakeElementExpr(
const RangeIntToIntFunction*
callback,
1092 IntVar*
index, Solver* s) {
1093 return s->RegisterIntExpr(
1099 const std::vector<int>& dependent_transits,
1101 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
1102 const std::string&
name) {
1103 const std::vector<int> pure_transits(vehicles_, 0);
1105 pure_transits, dependent_transits, base_dimension, slack_max,
1106 std::move(vehicle_capacities), fix_start_cumul_to_zero,
name);
1111 int64_t vehicle_capacity,
bool fix_start_cumul_to_zero,
1112 const std::string&
name) {
1114 0, transit, dimension, slack_max, vehicle_capacity,
1115 fix_start_cumul_to_zero,
name);
1118 bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacityInternal(
1119 const std::vector<int>& pure_transits,
1120 const std::vector<int>& dependent_transits,
1122 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
1123 const std::string&
name) {
1124 CHECK_EQ(vehicles_, vehicle_capacities.size());
1126 if (base_dimension ==
nullptr) {
1128 name, RoutingDimension::SelfBased());
1131 name, base_dimension);
1133 return InitializeDimensionInternal(pure_transits, dependent_transits,
1134 slack_max, fix_start_cumul_to_zero,
1139 int pure_transit,
int dependent_transit,
1141 int64_t vehicle_capacity,
bool fix_start_cumul_to_zero,
1142 const std::string&
name) {
1143 std::vector<int> pure_transits(vehicles_, pure_transit);
1144 std::vector<int> dependent_transits(vehicles_, dependent_transit);
1145 std::vector<int64_t> vehicle_capacities(vehicles_, vehicle_capacity);
1146 return AddDimensionDependentDimensionWithVehicleCapacityInternal(
1147 pure_transits, dependent_transits, base_dimension, slack_max,
1148 std::move(vehicle_capacities), fix_start_cumul_to_zero,
name);
1152 const std::function<int64_t(int64_t)>& f, int64_t domain_start,
1153 int64_t domain_end) {
1154 const std::function<int64_t(int64_t)> g = [&f](int64_t x) {
1164 std::vector<std::string> dimension_names;
1165 for (
const auto& dimension_name_index : dimension_name_to_index_) {
1166 dimension_names.push_back(dimension_name_index.first);
1168 std::sort(dimension_names.begin(), dimension_names.end());
1169 return dimension_names;
1175 if (dim_index < 0 || dim_index >= global_optimizer_index_.
size() ||
1176 global_optimizer_index_[dim_index] < 0) {
1179 const int optimizer_index = global_optimizer_index_[dim_index];
1180 DCHECK_LT(optimizer_index, global_dimension_optimizers_.size());
1181 return global_dimension_optimizers_[optimizer_index].get();
1187 if (dim_index < 0 || dim_index >= local_optimizer_index_.
size() ||
1188 local_optimizer_index_[dim_index] < 0) {
1191 const int optimizer_index = local_optimizer_index_[dim_index];
1192 DCHECK_LT(optimizer_index, local_dimension_optimizers_.size());
1193 return local_dimension_optimizers_[optimizer_index].get();
1199 if (dim_index < 0 || dim_index >= local_optimizer_index_.
size() ||
1200 local_optimizer_index_[dim_index] < 0) {
1203 const int optimizer_index = local_optimizer_index_[dim_index];
1204 DCHECK_LT(optimizer_index, local_dimension_mp_optimizers_.size());
1205 return local_dimension_mp_optimizers_[optimizer_index].get();
1213 const std::string& dimension_name)
const {
1219 const std::string& dimension_name)
const {
1220 return *dimensions_[
gtl::FindOrDie(dimension_name_to_index_, dimension_name)];
1224 const std::string& dimension_name)
const {
1227 return dimensions_[
index];
1234 for (
int i = 0; i < vehicles_; ++i) {
1242 CHECK_LT(evaluator_index, transit_evaluators_.size());
1243 vehicle_to_transit_cost_[vehicle] = evaluator_index;
1247 for (
int i = 0; i < vehicles_; ++i) {
1254 return fixed_cost_of_vehicle_[vehicle];
1260 fixed_cost_of_vehicle_[vehicle] =
cost;
1264 int64_t linear_cost_factor, int64_t quadratic_cost_factor) {
1265 for (
int v = 0; v < vehicles_; v++) {
1272 int64_t linear_cost_factor, int64_t quadratic_cost_factor,
int vehicle) {
1276 if (linear_cost_factor + quadratic_cost_factor > 0) {
1277 vehicle_amortized_cost_factors_set_ =
true;
1279 linear_cost_factor_of_vehicle_[vehicle] = linear_cost_factor;
1280 quadratic_cost_factor_of_vehicle_[vehicle] = quadratic_cost_factor;
1286 struct CostClassComparator {
1293 struct VehicleClassComparator {
1294 bool operator()(
const RoutingModel::VehicleClass&
a,
1295 const RoutingModel::VehicleClass&
b)
const {
1305 void RoutingModel::ComputeCostClasses(
1308 cost_classes_.reserve(vehicles_);
1309 cost_classes_.clear();
1310 cost_class_index_of_vehicle_.assign(vehicles_,
CostClassIndex(-1));
1311 std::map<CostClass, CostClassIndex, CostClassComparator> cost_class_map;
1314 const CostClass zero_cost_class(0);
1315 cost_classes_.push_back(zero_cost_class);
1316 DCHECK_EQ(cost_classes_[kCostClassIndexOfZeroCost].evaluator_index, 0);
1317 cost_class_map[zero_cost_class] = kCostClassIndexOfZeroCost;
1322 has_vehicle_with_zero_cost_class_ =
false;
1323 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
1324 CostClass cost_class(vehicle_to_transit_cost_[vehicle]);
1328 const int64_t coeff =
1329 dimension->vehicle_span_cost_coefficients()[vehicle];
1330 if (coeff == 0)
continue;
1331 cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1332 .push_back({dimension->vehicle_to_class(vehicle), coeff, dimension});
1334 std::sort(cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1336 cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1342 if (cost_class_index == kCostClassIndexOfZeroCost) {
1343 has_vehicle_with_zero_cost_class_ =
true;
1344 }
else if (cost_class_index == num_cost_classes) {
1345 cost_classes_.push_back(cost_class);
1347 cost_class_index_of_vehicle_[vehicle] = cost_class_index;
1361 costs_are_homogeneous_across_vehicles_ &= has_vehicle_with_zero_cost_class_
1368 return std::tie(
a.cost_class_index,
a.fixed_cost,
a.start_equivalence_class,
1369 a.end_equivalence_class,
a.unvisitable_nodes_fprint,
1370 a.dimension_start_cumuls_min,
a.dimension_start_cumuls_max,
1371 a.dimension_end_cumuls_min,
a.dimension_end_cumuls_max,
1372 a.dimension_capacities,
a.dimension_evaluator_classes) <
1373 std::tie(
b.cost_class_index,
b.fixed_cost,
b.start_equivalence_class,
1374 b.end_equivalence_class,
b.unvisitable_nodes_fprint,
1375 b.dimension_start_cumuls_min,
b.dimension_start_cumuls_max,
1376 b.dimension_end_cumuls_min,
b.dimension_end_cumuls_max,
1377 b.dimension_capacities,
b.dimension_evaluator_classes);
1380 void RoutingModel::ComputeVehicleClasses() {
1381 vehicle_classes_.reserve(vehicles_);
1382 vehicle_classes_.clear();
1384 std::map<VehicleClass, VehicleClassIndex, VehicleClassComparator>
1386 const int nodes_unvisitability_num_bytes = (vehicle_vars_.size() + 7) / 8;
1387 std::unique_ptr<char[]> nodes_unvisitability_bitmask(
1388 new char[nodes_unvisitability_num_bytes]);
1389 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
1391 vehicle_class.cost_class_index = cost_class_index_of_vehicle_[vehicle];
1394 index_to_equivalence_class_[
Start(vehicle)];
1396 index_to_equivalence_class_[
End(vehicle)];
1398 IntVar*
const start_cumul_var = dimension->cumuls()[
Start(vehicle)];
1400 start_cumul_var->
Min());
1402 start_cumul_var->
Max());
1403 IntVar*
const end_cumul_var = dimension->cumuls()[
End(vehicle)];
1407 dimension->vehicle_capacities()[vehicle]);
1409 dimension->vehicle_to_class(vehicle));
1411 memset(nodes_unvisitability_bitmask.get(), 0,
1412 nodes_unvisitability_num_bytes);
1416 (!vehicle_var->
Contains(vehicle) ||
1418 nodes_unvisitability_bitmask[
index / CHAR_BIT] |= 1U
1419 << (
index % CHAR_BIT);
1423 nodes_unvisitability_bitmask.get(), nodes_unvisitability_num_bytes);
1427 if (vehicle_class_index == num_vehicle_classes) {
1430 vehicle_class_index_of_vehicle_[vehicle] = vehicle_class_index;
1434 void RoutingModel::ComputeVehicleTypes() {
1435 const int nodes_squared = nodes_ * nodes_;
1436 std::vector<int>& type_index_of_vehicle =
1438 std::vector<std::set<VehicleTypeContainer::VehicleClassEntry>>&
1439 sorted_vehicle_classes_per_type =
1441 std::vector<std::deque<int>>& vehicles_per_vehicle_class =
1444 type_index_of_vehicle.resize(vehicles_);
1445 sorted_vehicle_classes_per_type.clear();
1446 sorted_vehicle_classes_per_type.reserve(vehicles_);
1447 vehicles_per_vehicle_class.clear();
1450 absl::flat_hash_map<int64_t, int> type_to_type_index;
1452 for (
int v = 0; v < vehicles_; v++) {
1456 const int64_t type = cost_class * nodes_squared + start * nodes_ + end;
1458 const auto& vehicle_type_added = type_to_type_index.insert(
1459 std::make_pair(type, type_to_type_index.size()));
1461 const int index = vehicle_type_added.first->second;
1464 const VehicleTypeContainer::VehicleClassEntry class_entry = {
1467 if (vehicle_type_added.second) {
1470 sorted_vehicle_classes_per_type.push_back({class_entry});
1474 sorted_vehicle_classes_per_type[
index].insert(class_entry);
1477 type_index_of_vehicle[v] =
index;
1481 void RoutingModel::FinalizeVisitTypes() {
1487 single_nodes_of_type_.clear();
1488 single_nodes_of_type_.resize(num_visit_types_);
1489 pair_indices_of_type_.clear();
1490 pair_indices_of_type_.resize(num_visit_types_);
1491 std::vector<absl::flat_hash_set<int>> pair_indices_added_for_type(
1496 if (visit_type < 0) {
1499 const std::vector<std::pair<int, int>>& pickup_index_pairs =
1500 index_to_pickup_index_pairs_[
index];
1501 const std::vector<std::pair<int, int>>& delivery_index_pairs =
1502 index_to_delivery_index_pairs_[
index];
1503 if (pickup_index_pairs.empty() && delivery_index_pairs.empty()) {
1504 single_nodes_of_type_[visit_type].push_back(
index);
1506 for (
const std::vector<std::pair<int, int>>* index_pairs :
1507 {&pickup_index_pairs, &delivery_index_pairs}) {
1508 for (
const std::pair<int, int>& index_pair : *index_pairs) {
1509 const int pair_index = index_pair.first;
1510 if (pair_indices_added_for_type[visit_type].insert(pair_index).second) {
1511 pair_indices_of_type_[visit_type].push_back(pair_index);
1517 TopologicallySortVisitTypes();
1520 void RoutingModel::TopologicallySortVisitTypes() {
1521 if (!has_same_vehicle_type_requirements_ &&
1522 !has_temporal_type_requirements_) {
1525 std::vector<std::pair<double, double>> type_requirement_tightness(
1526 num_visit_types_, {0, 0});
1527 std::vector<absl::flat_hash_set<int>> type_to_dependent_types(
1529 SparseBitset<> types_in_requirement_graph(num_visit_types_);
1530 std::vector<int> in_degree(num_visit_types_, 0);
1531 for (
int type = 0; type < num_visit_types_; type++) {
1532 int num_alternative_required_types = 0;
1533 int num_required_sets = 0;
1534 for (
const std::vector<absl::flat_hash_set<int>>*
1535 required_type_alternatives :
1536 {&required_type_alternatives_when_adding_type_index_[type],
1537 &required_type_alternatives_when_removing_type_index_[type],
1538 &same_vehicle_required_type_alternatives_per_type_index_[type]}) {
1539 for (
const absl::flat_hash_set<int>& alternatives :
1540 *required_type_alternatives) {
1541 types_in_requirement_graph.Set(type);
1542 num_required_sets++;
1543 for (
int required_type : alternatives) {
1544 type_requirement_tightness[required_type].second +=
1545 1.0 / alternatives.size();
1546 types_in_requirement_graph.Set(required_type);
1547 num_alternative_required_types++;
1548 if (type_to_dependent_types[required_type].insert(type).second) {
1554 if (num_alternative_required_types > 0) {
1555 type_requirement_tightness[type].first += 1.0 * num_required_sets *
1557 num_alternative_required_types;
1562 topologically_sorted_visit_types_.clear();
1563 std::vector<int> current_types_with_zero_indegree;
1564 for (
int type : types_in_requirement_graph.PositionsSetAtLeastOnce()) {
1565 DCHECK(type_requirement_tightness[type].first > 0 ||
1566 type_requirement_tightness[type].second > 0);
1567 if (in_degree[type] == 0) {
1568 current_types_with_zero_indegree.push_back(type);
1572 int num_types_added = 0;
1573 while (!current_types_with_zero_indegree.empty()) {
1576 topologically_sorted_visit_types_.push_back({});
1577 std::vector<int>& topological_group =
1578 topologically_sorted_visit_types_.back();
1579 std::vector<int> next_types_with_zero_indegree;
1580 for (
int type : current_types_with_zero_indegree) {
1581 topological_group.push_back(type);
1583 for (
int dependent_type : type_to_dependent_types[type]) {
1584 DCHECK_GT(in_degree[dependent_type], 0);
1585 if (--in_degree[dependent_type] == 0) {
1586 next_types_with_zero_indegree.push_back(dependent_type);
1597 std::sort(topological_group.begin(), topological_group.end(),
1598 [&type_requirement_tightness](
int type1,
int type2) {
1599 const auto& tightness1 = type_requirement_tightness[type1];
1600 const auto& tightness2 = type_requirement_tightness[type2];
1601 return tightness1 > tightness2 ||
1602 (tightness1 == tightness2 && type1 < type2);
1605 current_types_with_zero_indegree.swap(next_types_with_zero_indegree);
1608 const int num_types_in_requirement_graph =
1609 types_in_requirement_graph.NumberOfSetCallsWithDifferentArguments();
1610 DCHECK_LE(num_types_added, num_types_in_requirement_graph);
1611 if (num_types_added < num_types_in_requirement_graph) {
1613 topologically_sorted_visit_types_.clear();
1618 const std::vector<int64_t>& indices, int64_t penalty,
1619 int64_t max_cardinality) {
1621 for (
int i = 0; i < indices.size(); ++i) {
1626 disjunctions_.
push_back({indices, {penalty, max_cardinality}});
1627 for (
const int64_t
index : indices) {
1628 index_to_disjunctions_[
index].push_back(disjunction_index);
1630 return disjunction_index;
1633 std::vector<std::pair<int64_t, int64_t>>
1635 std::vector<std::pair<int64_t, int64_t>> var_index_pairs;
1636 for (
const Disjunction& disjunction : disjunctions_) {
1637 const std::vector<int64_t>&
var_indices = disjunction.indices;
1641 if (index_to_disjunctions_[v0].size() == 1 &&
1642 index_to_disjunctions_[v1].size() == 1) {
1647 std::sort(var_index_pairs.begin(), var_index_pairs.end());
1648 return var_index_pairs;
1653 for (Disjunction& disjunction : disjunctions_) {
1654 bool has_one_potentially_active_var =
false;
1655 for (
const int64_t var_index : disjunction.indices) {
1657 has_one_potentially_active_var =
true;
1661 if (!has_one_potentially_active_var) {
1662 disjunction.value.max_cardinality = 0;
1668 const std::vector<int64_t>& indices = disjunctions_[disjunction].indices;
1669 const int indices_size = indices.
size();
1670 std::vector<IntVar*> disjunction_vars(indices_size);
1671 for (
int i = 0; i < indices_size; ++i) {
1672 const int64_t
index = indices[i];
1676 const int64_t max_cardinality =
1677 disjunctions_[disjunction].value.max_cardinality;
1678 IntVar* no_active_var = solver_->MakeBoolVar();
1679 IntVar* number_active_vars = solver_->MakeIntVar(0, max_cardinality);
1680 solver_->AddConstraint(
1681 solver_->MakeSumEquality(disjunction_vars, number_active_vars));
1682 solver_->AddConstraint(solver_->MakeIsDifferentCstCt(
1683 number_active_vars, max_cardinality, no_active_var));
1684 const int64_t penalty = disjunctions_[disjunction].value.penalty;
1686 no_active_var->SetMax(0);
1689 return solver_->MakeProd(no_active_var, penalty)->Var();
1694 const std::vector<int64_t>& indices, int64_t
cost) {
1695 if (!indices.empty()) {
1696 ValuedNodes<int64_t> same_vehicle_cost;
1697 for (
const int64_t
index : indices) {
1698 same_vehicle_cost.indices.push_back(
index);
1700 same_vehicle_cost.value =
cost;
1701 same_vehicle_costs_.push_back(same_vehicle_cost);
1707 auto& allowed_vehicles = allowed_vehicles_[
index];
1708 allowed_vehicles.clear();
1710 allowed_vehicles.insert(vehicle);
1715 AddPickupAndDeliverySetsInternal({pickup}, {delivery});
1724 pickup_delivery_disjunctions_.push_back(
1725 {pickup_disjunction, delivery_disjunction});
1728 void RoutingModel::AddPickupAndDeliverySetsInternal(
1729 const std::vector<int64_t>& pickups,
1730 const std::vector<int64_t>& deliveries) {
1731 if (pickups.empty() || deliveries.empty()) {
1734 const int64_t size =
Size();
1735 const int pair_index = pickup_delivery_pairs_.size();
1736 for (
int pickup_index = 0; pickup_index < pickups.size(); pickup_index++) {
1737 const int64_t pickup = pickups[pickup_index];
1739 index_to_pickup_index_pairs_[pickup].emplace_back(pair_index, pickup_index);
1741 for (
int delivery_index = 0; delivery_index < deliveries.size();
1743 const int64_t delivery = deliveries[delivery_index];
1745 index_to_delivery_index_pairs_[delivery].emplace_back(pair_index,
1748 pickup_delivery_pairs_.push_back({pickups, deliveries});
1752 int64_t node_index)
const {
1753 CHECK_LT(node_index, index_to_pickup_index_pairs_.size());
1754 return index_to_pickup_index_pairs_[node_index];
1758 int64_t node_index)
const {
1759 CHECK_LT(node_index, index_to_delivery_index_pairs_.size());
1760 return index_to_delivery_index_pairs_[node_index];
1766 vehicle_pickup_delivery_policy_[vehicle] = policy;
1772 for (
int i = 0; i < vehicles_; ++i) {
1780 return vehicle_pickup_delivery_policy_[vehicle];
1785 for (
int i = 0; i <
Nexts().size(); ++i) {
1795 IntVar* RoutingModel::CreateSameVehicleCost(
int vehicle_index) {
1796 const std::vector<int64_t>& indices =
1797 same_vehicle_costs_[vehicle_index].indices;
1798 CHECK(!indices.empty());
1799 std::vector<IntVar*> vehicle_counts;
1800 solver_->MakeIntVarArray(vehicle_vars_.size() + 1, 0, indices.size() + 1,
1802 std::vector<int64_t> vehicle_values(vehicle_vars_.size() + 1);
1803 for (
int i = 0; i < vehicle_vars_.size(); ++i) {
1804 vehicle_values[i] = i;
1806 vehicle_values[vehicle_vars_.size()] = -1;
1807 std::vector<IntVar*> vehicle_vars;
1808 vehicle_vars.reserve(indices.size());
1809 for (
const int64_t
index : indices) {
1810 vehicle_vars.push_back(vehicle_vars_[
index]);
1812 solver_->AddConstraint(solver_->MakeDistribute(vehicle_vars, vehicle_counts));
1813 std::vector<IntVar*> vehicle_used;
1814 for (
int i = 0; i < vehicle_vars_.size() + 1; ++i) {
1815 vehicle_used.push_back(
1816 solver_->MakeIsGreaterOrEqualCstVar(vehicle_counts[i], 1));
1818 vehicle_used.push_back(solver_->MakeIntConst(-1));
1820 ->MakeProd(solver_->MakeMax(solver_->MakeSum(vehicle_used), 0),
1821 same_vehicle_costs_[vehicle_index].value)
1826 extra_operators_.push_back(ls_operator);
1835 void RoutingModel::AppendHomogeneousArcCosts(
1836 const RoutingSearchParameters&
parameters,
int node_index,
1837 std::vector<IntVar*>* cost_elements) {
1838 CHECK(cost_elements !=
nullptr);
1839 const auto arc_cost_evaluator = [
this, node_index](int64_t next_index) {
1846 IntVar*
const base_cost_var =
1848 solver_->AddConstraint(MakeLightElement(
1849 solver_.get(), base_cost_var, nexts_[node_index], arc_cost_evaluator,
1850 [
this]() { return enable_deep_serialization_; }));
1852 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1853 cost_elements->push_back(
var);
1855 IntExpr*
const expr =
1856 solver_->MakeElement(arc_cost_evaluator, nexts_[node_index]);
1857 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
1858 cost_elements->push_back(
var);
1862 void RoutingModel::AppendArcCosts(
const RoutingSearchParameters&
parameters,
1864 std::vector<IntVar*>* cost_elements) {
1865 CHECK(cost_elements !=
nullptr);
1871 IntVar*
const base_cost_var =
1873 solver_->AddConstraint(MakeLightElement2(
1874 solver_.get(), base_cost_var, nexts_[node_index],
1875 vehicle_vars_[node_index],
1876 [
this, node_index](int64_t to, int64_t vehicle) {
1877 return GetArcCostForVehicle(node_index, to, vehicle);
1879 [
this]() { return enable_deep_serialization_; }));
1881 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1882 cost_elements->push_back(
var);
1884 IntVar*
const vehicle_class_var =
1887 [
this](int64_t
index) {
1888 return SafeGetCostClassInt64OfVehicle(
index);
1890 vehicle_vars_[node_index])
1892 IntExpr*
const expr = solver_->MakeElement(
1896 nexts_[node_index], vehicle_class_var);
1897 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
1898 cost_elements->push_back(
var);
1902 int RoutingModel::GetVehicleStartClass(int64_t start_index)
const {
1903 const int vehicle = index_to_vehicle_[start_index];
1910 std::string RoutingModel::FindErrorInSearchParametersForModel(
1911 const RoutingSearchParameters& search_parameters)
const {
1913 search_parameters.first_solution_strategy();
1914 if (GetFirstSolutionDecisionBuilder(search_parameters) ==
nullptr) {
1915 return absl::StrCat(
1916 "Undefined first solution strategy: ",
1917 FirstSolutionStrategy::Value_Name(first_solution_strategy),
1918 " (int value: ", first_solution_strategy,
")");
1920 if (search_parameters.first_solution_strategy() ==
1921 FirstSolutionStrategy::SWEEP &&
1923 return "Undefined sweep arranger for ROUTING_SWEEP strategy.";
1928 void RoutingModel::QuietCloseModel() {
1939 same_vehicle_components_.SetNumberOfNodes(
model->Size());
1940 for (
const std::string&
name :
model->GetAllDimensionNames()) {
1942 const std::vector<IntVar*>& cumuls = dimension->
cumuls();
1943 for (
int i = 0; i < cumuls.size(); ++i) {
1944 cumul_to_dim_indices_[cumuls[i]] = {dimension, i};
1947 const std::vector<IntVar*>& vehicle_vars =
model->VehicleVars();
1948 for (
int i = 0; i < vehicle_vars.size(); ++i) {
1949 vehicle_var_to_indices_[vehicle_vars[i]] = i;
1951 RegisterInspectors();
1955 const std::vector<int> node_to_same_vehicle_component_id =
1956 same_vehicle_components_.GetComponentIds();
1957 model_->InitSameVehicleGroups(
1958 same_vehicle_components_.GetNumberOfComponents());
1959 for (
int node = 0; node < model_->Size(); ++node) {
1960 model_->SetSameVehicleGroup(node,
1961 node_to_same_vehicle_component_id[node]);
1967 const Constraint*
const constraint)
override {
1971 IntExpr*
const expr)
override {
1973 [](
const IntExpr* expr) {})(expr);
1976 const std::vector<int64_t>& values)
override {
1978 [](
const std::vector<int64_t>& int_array) {})(values);
1982 using ExprInspector = std::function<void(
const IntExpr*)>;
1983 using ArrayInspector = std::function<void(
const std::vector<int64_t>&)>;
1984 using ConstraintInspector = std::function<void()>;
1986 void RegisterInspectors() {
1987 expr_inspectors_[kExpressionArgument] = [
this](
const IntExpr* expr) {
1990 expr_inspectors_[kLeftArgument] = [
this](
const IntExpr* expr) {
1993 expr_inspectors_[kRightArgument] = [
this](
const IntExpr* expr) {
1996 array_inspectors_[kStartsArgument] =
1997 [
this](
const std::vector<int64_t>& int_array) {
1998 starts_argument_ = int_array;
2000 array_inspectors_[kEndsArgument] =
2001 [
this](
const std::vector<int64_t>& int_array) {
2002 ends_argument_ = int_array;
2004 constraint_inspectors_[kNotMember] = [
this]() {
2005 std::pair<RoutingDimension*, int> dim_index;
2008 const int index = dim_index.second;
2009 dimension->forbidden_intervals_[
index].InsertIntervals(starts_argument_,
2011 VLOG(2) << dimension->name() <<
" " <<
index <<
": "
2012 << dimension->forbidden_intervals_[
index].DebugString();
2015 starts_argument_.clear();
2016 ends_argument_.clear();
2018 constraint_inspectors_[kEquality] = [
this]() {
2020 int right_index = 0;
2021 if (
gtl::FindCopy(vehicle_var_to_indices_, left_, &left_index) &&
2022 gtl::FindCopy(vehicle_var_to_indices_, right_, &right_index)) {
2023 VLOG(2) <<
"Vehicle variables for " << left_index <<
" and "
2024 << right_index <<
" are equal.";
2025 same_vehicle_components_.AddEdge(left_index, right_index);
2030 constraint_inspectors_[kLessOrEqual] = [
this]() {
2031 std::pair<RoutingDimension*, int> left_index;
2032 std::pair<RoutingDimension*, int> right_index;
2033 if (
gtl::FindCopy(cumul_to_dim_indices_, left_, &left_index) &&
2034 gtl::FindCopy(cumul_to_dim_indices_, right_, &right_index)) {
2036 if (dimension == right_index.first) {
2037 VLOG(2) <<
"For dimension " << dimension->name() <<
", cumul for "
2038 << left_index.second <<
" is less than " << right_index.second
2040 dimension->path_precedence_graph_.AddArc(left_index.second,
2041 right_index.second);
2051 absl::flat_hash_map<const IntExpr*, std::pair<RoutingDimension*, int>>
2052 cumul_to_dim_indices_;
2053 absl::flat_hash_map<const IntExpr*, int> vehicle_var_to_indices_;
2054 absl::flat_hash_map<std::string, ExprInspector> expr_inspectors_;
2055 absl::flat_hash_map<std::string, ArrayInspector> array_inspectors_;
2056 absl::flat_hash_map<std::string, ConstraintInspector> constraint_inspectors_;
2057 const IntExpr*
expr_ =
nullptr;
2058 const IntExpr* left_ =
nullptr;
2059 const IntExpr* right_ =
nullptr;
2060 std::vector<int64_t> starts_argument_;
2061 std::vector<int64_t> ends_argument_;
2064 void RoutingModel::DetectImplicitPickupAndDeliveries() {
2065 std::vector<int> non_pickup_delivery_nodes;
2066 for (
int node = 0; node <
Size(); ++node) {
2069 non_pickup_delivery_nodes.push_back(node);
2073 std::set<std::pair<int64_t, int64_t>> implicit_pickup_deliveries;
2075 if (dimension->class_evaluators_.size() != 1) {
2080 if (transit ==
nullptr)
continue;
2081 absl::flat_hash_map<int64_t, std::vector<int64_t>> nodes_by_positive_demand;
2082 absl::flat_hash_map<int64_t, std::vector<int64_t>> nodes_by_negative_demand;
2083 for (
int node : non_pickup_delivery_nodes) {
2084 const int64_t
demand = transit(node);
2086 nodes_by_positive_demand[
demand].push_back(node);
2088 nodes_by_negative_demand[-
demand].push_back(node);
2091 for (
const auto& [
demand, positive_nodes] : nodes_by_positive_demand) {
2092 const std::vector<int64_t>*
const negative_nodes =
2094 if (negative_nodes !=
nullptr) {
2095 for (int64_t positive_node : positive_nodes) {
2096 for (int64_t negative_node : *negative_nodes) {
2097 implicit_pickup_deliveries.insert({positive_node, negative_node});
2103 implicit_pickup_delivery_pairs_without_alternatives_.clear();
2104 for (
auto [pickup, delivery] : implicit_pickup_deliveries) {
2105 implicit_pickup_delivery_pairs_without_alternatives_.emplace_back(
2106 std::vector<int64_t>({pickup}), std::vector<int64_t>({delivery}));
2113 if (!error.empty()) {
2115 LOG(
ERROR) <<
"Invalid RoutingSearchParameters: " << error;
2125 dimension->CloseModel(UsesLightPropagation(
parameters));
2128 ComputeVehicleClasses();
2129 ComputeVehicleTypes();
2130 FinalizeVisitTypes();
2131 vehicle_start_class_callback_ = [
this](int64_t start) {
2132 return GetVehicleStartClass(start);
2135 AddNoCycleConstraintInternal();
2137 const int size =
Size();
2140 for (
int i = 0; i < vehicles_; ++i) {
2141 const int64_t start = starts_[i];
2142 const int64_t end = ends_[i];
2143 solver_->AddConstraint(
2144 solver_->MakeEquality(vehicle_vars_[start], solver_->MakeIntConst(i)));
2145 solver_->AddConstraint(
2146 solver_->MakeEquality(vehicle_vars_[end], solver_->MakeIntConst(i)));
2147 solver_->AddConstraint(
2148 solver_->MakeIsDifferentCstCt(nexts_[start], end, vehicle_active_[i]));
2149 if (consider_empty_route_costs_[i]) {
2150 vehicle_costs_considered_[i]->SetMin(1);
2152 solver_->AddConstraint(solver_->MakeEquality(
2153 vehicle_active_[i], vehicle_costs_considered_[i]));
2158 if (vehicles_ > max_active_vehicles_) {
2159 solver_->AddConstraint(
2160 solver_->MakeSumLessOrEqual(vehicle_active_, max_active_vehicles_));
2168 if (vehicles_ > 1) {
2169 std::vector<IntVar*> zero_transit(size, solver_->MakeIntConst(0));
2170 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
2171 nexts_, active_, vehicle_vars_, zero_transit));
2176 for (
int i = 0; i < size; ++i) {
2178 active_[i]->SetValue(1);
2184 const absl::flat_hash_set<VisitTypePolicy>*
const infeasible_policies =
2186 if (infeasible_policies !=
nullptr &&
2188 active_[i]->SetValue(0);
2193 for (
int i = 0; i < allowed_vehicles_.size(); ++i) {
2194 const auto& allowed_vehicles = allowed_vehicles_[i];
2195 if (!allowed_vehicles.empty()) {
2197 vehicles.reserve(allowed_vehicles.size() + 1);
2199 for (
int vehicle : allowed_vehicles) {
2207 for (
int i = 0; i < size; ++i) {
2209 solver_->AddConstraint(solver_->RevAlloc(
2210 new DifferentFromValues(solver_.get(), nexts_[i], starts_)));
2212 solver_->AddConstraint(
2213 solver_->MakeIsDifferentCstCt(nexts_[i], i, active_[i]));
2218 for (
int i = 0; i < size; ++i) {
2219 solver_->AddConstraint(
2220 solver_->MakeIsDifferentCstCt(vehicle_vars_[i], -1, active_[i]));
2224 solver_->AddConstraint(
2229 for (
int i = 0; i < vehicles_; ++i) {
2230 std::vector<int64_t> forbidden_ends;
2231 forbidden_ends.reserve(vehicles_ - 1);
2232 for (
int j = 0; j < vehicles_; ++j) {
2234 forbidden_ends.push_back(ends_[j]);
2237 solver_->AddConstraint(solver_->RevAlloc(
new DifferentFromValues(
2238 solver_.get(), nexts_[starts_[i]], std::move(forbidden_ends))));
2242 for (
const int64_t end : ends_) {
2243 is_bound_to_end_[end]->SetValue(1);
2246 std::vector<IntVar*> cost_elements;
2248 if (vehicles_ > 0) {
2249 for (
int node_index = 0; node_index < size; ++node_index) {
2251 AppendHomogeneousArcCosts(
parameters, node_index, &cost_elements);
2253 AppendArcCosts(
parameters, node_index, &cost_elements);
2256 if (vehicle_amortized_cost_factors_set_) {
2257 std::vector<IntVar*> route_lengths;
2258 solver_->MakeIntVarArray(vehicles_, 0, size, &route_lengths);
2259 solver_->AddConstraint(
2260 solver_->MakeDistribute(vehicle_vars_, route_lengths));
2261 std::vector<IntVar*> vehicle_used;
2262 for (
int i = 0; i < vehicles_; i++) {
2264 vehicle_used.push_back(
2265 solver_->MakeIsGreaterCstVar(route_lengths[i], 2));
2268 ->MakeProd(solver_->MakeOpposite(solver_->MakeSquare(
2269 solver_->MakeSum(route_lengths[i], -2))),
2270 quadratic_cost_factor_of_vehicle_[i])
2272 cost_elements.push_back(
var);
2274 IntVar*
const vehicle_usage_cost =
2275 solver_->MakeScalProd(vehicle_used, linear_cost_factor_of_vehicle_)
2277 cost_elements.push_back(vehicle_usage_cost);
2282 dimension->SetupGlobalSpanCost(&cost_elements);
2283 dimension->SetupSlackAndDependentTransitCosts();
2284 const std::vector<int64_t>& span_costs =
2285 dimension->vehicle_span_cost_coefficients();
2286 const std::vector<int64_t>& span_ubs =
2287 dimension->vehicle_span_upper_bounds();
2288 const bool has_span_constraint =
2289 std::any_of(span_costs.begin(), span_costs.end(),
2290 [](int64_t coeff) { return coeff != 0; }) ||
2291 std::any_of(span_ubs.begin(), span_ubs.end(),
2293 return value < std::numeric_limits<int64_t>::max();
2295 dimension->HasSoftSpanUpperBounds() ||
2296 dimension->HasQuadraticCostSoftSpanUpperBounds();
2297 if (has_span_constraint) {
2298 std::vector<IntVar*> spans(
vehicles(),
nullptr);
2299 std::vector<IntVar*> total_slacks(
vehicles(),
nullptr);
2301 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2303 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2305 if (span_costs[vehicle] != 0) {
2306 total_slacks[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2309 if (dimension->HasSoftSpanUpperBounds()) {
2310 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2311 if (spans[vehicle])
continue;
2313 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2314 if (bound_cost.
cost == 0)
continue;
2315 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2318 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2319 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2320 if (spans[vehicle])
continue;
2322 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2323 if (bound_cost.
cost == 0)
continue;
2324 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2327 solver_->AddConstraint(
2331 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2332 if (!spans[vehicle] && !total_slacks[vehicle])
continue;
2333 if (spans[vehicle]) {
2343 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2344 if (span_costs[vehicle] == 0)
continue;
2345 DCHECK(total_slacks[vehicle] !=
nullptr);
2346 IntVar*
const slack_amount =
2348 ->MakeProd(vehicle_costs_considered_[vehicle],
2349 total_slacks[vehicle])
2351 IntVar*
const slack_cost =
2352 solver_->MakeProd(slack_amount, span_costs[vehicle])->Var();
2353 cost_elements.push_back(slack_cost);
2355 span_costs[vehicle]);
2357 if (dimension->HasSoftSpanUpperBounds()) {
2358 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2359 const auto bound_cost =
2360 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2361 if (bound_cost.cost == 0 ||
2364 DCHECK(spans[vehicle] !=
nullptr);
2367 IntVar*
const span_violation_amount =
2370 vehicle_costs_considered_[vehicle],
2372 solver_->MakeSum(spans[vehicle], -bound_cost.bound),
2375 IntVar*
const span_violation_cost =
2376 solver_->MakeProd(span_violation_amount, bound_cost.cost)->Var();
2377 cost_elements.push_back(span_violation_cost);
2382 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2383 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2384 const auto bound_cost =
2385 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2386 if (bound_cost.cost == 0 ||
2389 DCHECK(spans[vehicle] !=
nullptr);
2392 IntExpr* max0 = solver_->MakeMax(
2393 solver_->MakeSum(spans[vehicle], -bound_cost.bound), 0);
2394 IntVar*
const squared_span_violation_amount =
2396 ->MakeProd(vehicle_costs_considered_[vehicle],
2397 solver_->MakeSquare(max0))
2399 IntVar*
const span_violation_cost =
2400 solver_->MakeProd(squared_span_violation_amount, bound_cost.cost)
2402 cost_elements.push_back(span_violation_cost);
2411 IntVar* penalty_var = CreateDisjunction(i);
2412 if (penalty_var !=
nullptr) {
2413 cost_elements.push_back(penalty_var);
2418 dimension->SetupCumulVarSoftLowerBoundCosts(&cost_elements);
2419 dimension->SetupCumulVarSoftUpperBoundCosts(&cost_elements);
2420 dimension->SetupCumulVarPiecewiseLinearCosts(&cost_elements);
2423 for (
int i = 0; i < same_vehicle_costs_.size(); ++i) {
2424 cost_elements.push_back(CreateSameVehicleCost(i));
2426 cost_ = solver_->MakeSum(cost_elements)->Var();
2430 std::vector<std::pair<int, int>> pickup_delivery_precedences;
2431 for (
const auto& pair : pickup_delivery_pairs_) {
2432 DCHECK(!pair.first.empty() && !pair.second.empty());
2433 for (
int pickup : pair.first) {
2434 for (
int delivery : pair.second) {
2435 pickup_delivery_precedences.emplace_back(pickup, delivery);
2439 std::vector<int> lifo_vehicles;
2440 std::vector<int> fifo_vehicles;
2441 for (
int i = 0; i < vehicles_; ++i) {
2442 switch (vehicle_pickup_delivery_policy_[i]) {
2446 lifo_vehicles.push_back(
Start(i));
2449 fifo_vehicles.push_back(
Start(i));
2453 solver_->AddConstraint(solver_->MakePathPrecedenceConstraint(
2454 nexts_, pickup_delivery_precedences, lifo_vehicles, fifo_vehicles));
2457 enable_deep_serialization_ =
false;
2458 std::unique_ptr<RoutingModelInspector> inspector(
2460 solver_->Accept(inspector.get());
2461 enable_deep_serialization_ =
true;
2467 dimension->GetPathPrecedenceGraph();
2468 std::vector<std::pair<int, int>> path_precedences;
2470 for (
const auto head : graph[
tail]) {
2471 path_precedences.emplace_back(
tail,
head);
2474 if (!path_precedences.empty()) {
2475 solver_->AddConstraint(solver_->MakePathTransitPrecedenceConstraint(
2476 nexts_, dimension->transits(), path_precedences));
2481 dimension->GetNodePrecedences()) {
2482 const int64_t first_node = node_precedence.first_node;
2483 const int64_t second_node = node_precedence.second_node;
2484 IntExpr*
const nodes_are_selected =
2485 solver_->MakeMin(active_[first_node], active_[second_node]);
2486 IntExpr*
const cumul_difference = solver_->MakeDifference(
2487 dimension->CumulVar(second_node), dimension->CumulVar(first_node));
2488 IntVar*
const cumul_difference_is_ge_offset =
2489 solver_->MakeIsGreaterOrEqualCstVar(cumul_difference,
2490 node_precedence.offset);
2493 solver_->AddConstraint(solver_->MakeLessOrEqual(
2494 nodes_are_selected->
Var(), cumul_difference_is_ge_offset));
2498 DetectImplicitPickupAndDeliveries();
2507 CreateFirstSolutionDecisionBuilders(
parameters);
2508 error = FindErrorInSearchParametersForModel(
parameters);
2509 if (!error.empty()) {
2511 LOG(
ERROR) <<
"Invalid RoutingSearchParameters for this model: " << error;
2518 monitors_.push_back(monitor);
2526 bool AtSolution()
override {
2532 std::function<void()> callback_;
2538 new AtSolutionCallbackMonitor(solver_.get(), std::move(
callback))));
2548 std::vector<const Assignment*>* solutions) {
2553 absl::Duration GetTimeLimit(
const RoutingSearchParameters&
parameters) {
2554 if (!
parameters.has_time_limit())
return absl::InfiniteDuration();
2558 absl::Duration GetLnsTimeLimit(
const RoutingSearchParameters&
parameters) {
2559 if (!
parameters.has_lns_time_limit())
return absl::InfiniteDuration();
2567 Assignment* assignment) {
2568 assignment->Clear();
2569 for (
int i = 0; i <
model->Nexts().size(); ++i) {
2570 if (!
model->IsStart(i)) {
2571 assignment->Add(
model->NextVar(i))->SetValue(i);
2574 for (
int vehicle = 0; vehicle <
model->vehicles(); ++vehicle) {
2575 assignment->Add(
model->NextVar(
model->Start(vehicle)))
2576 ->SetValue(
model->End(vehicle));
2581 bool RoutingModel::AppendAssignmentIfFeasible(
2582 const Assignment& assignment,
2583 std::vector<std::unique_ptr<Assignment>>* assignments) {
2585 solver_->Solve(restore_tmp_assignment_, collect_one_assignment_,
2586 GetOrCreateLimit());
2588 assignments->push_back(absl::make_unique<Assignment>(solver_.get()));
2589 assignments->back()->Copy(collect_one_assignment_->
solution(0));
2595 void RoutingModel::LogSolution(
const RoutingSearchParameters&
parameters,
2596 const std::string& description,
2597 int64_t solution_cost, int64_t start_time_ms) {
2599 const double cost_scaling_factor =
parameters.log_cost_scaling_factor();
2600 const double cost_offset =
parameters.log_cost_offset();
2601 const std::string cost_string =
2602 cost_scaling_factor == 1.0 && cost_offset == 0.0
2603 ? absl::StrCat(solution_cost)
2605 "%d (%.8lf)", solution_cost,
2606 cost_scaling_factor * (solution_cost + cost_offset));
2608 "%s (%s, time = %d ms, memory used = %s)", description, cost_string,
2609 solver_->wall_time() - start_time_ms, memory_str);
2614 std::vector<const Assignment*>* solutions) {
2620 const std::vector<const Assignment*>& assignments,
2622 std::vector<const Assignment*>* solutions) {
2623 const int64_t start_time_ms = solver_->wall_time();
2626 if (solutions !=
nullptr) solutions->clear();
2630 const auto update_time_limits = [
this, start_time_ms, &
parameters]() {
2631 const absl::Duration elapsed_time =
2632 absl::Milliseconds(solver_->wall_time() - start_time_ms);
2633 const absl::Duration time_left = GetTimeLimit(
parameters) - elapsed_time;
2634 if (time_left >= absl::ZeroDuration()) {
2644 if (!update_time_limits()) {
2656 const int time_limit_shares = 1 + !global_dimension_optimizers_.empty() +
2657 !local_dimension_optimizers_.empty();
2658 const absl::Duration first_solution_lns_time_limit =
2665 std::vector<std::unique_ptr<Assignment>> solution_pool;
2666 std::vector<const Assignment*> first_solution_assignments;
2667 for (
const Assignment* assignment : assignments) {
2668 if (assignment !=
nullptr) first_solution_assignments.push_back(assignment);
2671 if (first_solution_assignments.empty()) {
2672 bool solution_found =
false;
2675 AppendAssignmentIfFeasible(matching, &solution_pool)) {
2677 LogSolution(
parameters,
"Min-Cost Flow Solution",
2678 solution_pool.back()->ObjectiveValue(), start_time_ms);
2680 solution_found =
true;
2682 if (!solution_found) {
2686 MakeAllUnperformedInAssignment(
this, &unperformed);
2687 if (AppendAssignmentIfFeasible(unperformed, &solution_pool) &&
2689 LogSolution(
parameters,
"All Unperformed Solution",
2690 solution_pool.back()->ObjectiveValue(), start_time_ms);
2692 if (update_time_limits()) {
2693 solver_->Solve(solve_db_, monitors_);
2697 for (
const Assignment* assignment : first_solution_assignments) {
2699 solver_->Solve(improve_db_, monitors_);
2701 !update_time_limits()) {
2709 const int solution_count = collect_assignments_->
solution_count();
2711 solution_count >= 1 ? collect_assignments_->
solution(solution_count - 1)
2715 AppendAssignmentIfFeasible(sat_solution, &solution_pool) &&
2717 LogSolution(
parameters,
"SAT", solution_pool.back()->ObjectiveValue(),
2722 const absl::Duration elapsed_time =
2723 absl::Milliseconds(solver_->wall_time() - start_time_ms);
2724 const int solution_count = collect_assignments_->
solution_count();
2725 if (solution_count >= 1 || !solution_pool.empty()) {
2727 if (solutions !=
nullptr) {
2728 for (
int i = 0; i < solution_count; ++i) {
2729 solutions->push_back(
2730 solver_->MakeAssignment(collect_assignments_->
solution(i)));
2732 for (
const auto& solution : solution_pool) {
2733 if (solutions->empty() ||
2734 solution->ObjectiveValue() < solutions->back()->ObjectiveValue()) {
2735 solutions->push_back(solver_->MakeAssignment(solution.get()));
2738 return solutions->back();
2741 solution_count >= 1 ? collect_assignments_->
solution(solution_count - 1)
2743 for (
const auto& solution : solution_pool) {
2744 if (best_assignment ==
nullptr ||
2746 best_assignment = solution.get();
2749 return solver_->MakeAssignment(best_assignment);
2751 if (elapsed_time >= GetTimeLimit(
parameters)) {
2763 const int size =
Size();
2769 source_model->
Nexts());
2771 std::vector<IntVar*> source_vars(size + size + vehicles_);
2772 std::vector<IntVar*> target_vars(size + size + vehicles_);
2782 source_assignment, source_vars);
2800 LOG(
WARNING) <<
"Non-closed model not supported.";
2804 LOG(
WARNING) <<
"Non-homogeneous vehicle costs not supported";
2807 if (!disjunctions_.
empty()) {
2809 <<
"Node disjunction constraints or optional nodes not supported.";
2812 const int num_nodes =
Size() + vehicles_;
2819 std::unique_ptr<IntVarIterator> iterator(
2820 nexts_[
tail]->MakeDomainIterator(
false));
2843 return linear_sum_assignment.
GetCost();
2848 bool RoutingModel::RouteCanBeUsedByVehicle(
const Assignment& assignment,
2849 int start_index,
int vehicle)
const {
2851 IsStart(start_index) ?
Next(assignment, start_index) : start_index;
2852 while (!
IsEnd(current_index)) {
2854 if (!vehicle_var->
Contains(vehicle)) {
2857 const int next_index =
Next(assignment, current_index);
2858 CHECK_NE(next_index, current_index) <<
"Inactive node inside a route";
2859 current_index = next_index;
2864 bool RoutingModel::ReplaceUnusedVehicle(
2865 int unused_vehicle,
int active_vehicle,
2866 Assignment*
const compact_assignment)
const {
2867 CHECK(compact_assignment !=
nullptr);
2871 const int unused_vehicle_start =
Start(unused_vehicle);
2872 IntVar*
const unused_vehicle_start_var =
NextVar(unused_vehicle_start);
2873 const int unused_vehicle_end =
End(unused_vehicle);
2874 const int active_vehicle_start =
Start(active_vehicle);
2875 const int active_vehicle_end =
End(active_vehicle);
2876 IntVar*
const active_vehicle_start_var =
NextVar(active_vehicle_start);
2877 const int active_vehicle_next =
2878 compact_assignment->Value(active_vehicle_start_var);
2879 compact_assignment->SetValue(unused_vehicle_start_var, active_vehicle_next);
2880 compact_assignment->SetValue(active_vehicle_start_var,
End(active_vehicle));
2883 int current_index = active_vehicle_next;
2884 while (!
IsEnd(current_index)) {
2885 IntVar*
const vehicle_var =
VehicleVar(current_index);
2886 compact_assignment->SetValue(vehicle_var, unused_vehicle);
2887 const int next_index =
Next(*compact_assignment, current_index);
2888 if (
IsEnd(next_index)) {
2889 IntVar*
const last_next_var =
NextVar(current_index);
2890 compact_assignment->SetValue(last_next_var,
End(unused_vehicle));
2892 current_index = next_index;
2897 const std::vector<IntVar*>& transit_variables = dimension->transits();
2898 IntVar*
const unused_vehicle_transit_var =
2899 transit_variables[unused_vehicle_start];
2900 IntVar*
const active_vehicle_transit_var =
2901 transit_variables[active_vehicle_start];
2902 const bool contains_unused_vehicle_transit_var =
2903 compact_assignment->Contains(unused_vehicle_transit_var);
2904 const bool contains_active_vehicle_transit_var =
2905 compact_assignment->Contains(active_vehicle_transit_var);
2906 if (contains_unused_vehicle_transit_var !=
2907 contains_active_vehicle_transit_var) {
2909 LOG(
INFO) <<
"The assignment contains transit variable for dimension '"
2910 << dimension->name() <<
"' for some vehicles, but not for all";
2913 if (contains_unused_vehicle_transit_var) {
2914 const int64_t old_unused_vehicle_transit =
2915 compact_assignment->Value(unused_vehicle_transit_var);
2916 const int64_t old_active_vehicle_transit =
2917 compact_assignment->Value(active_vehicle_transit_var);
2918 compact_assignment->SetValue(unused_vehicle_transit_var,
2919 old_active_vehicle_transit);
2920 compact_assignment->SetValue(active_vehicle_transit_var,
2921 old_unused_vehicle_transit);
2925 const std::vector<IntVar*>& cumul_variables = dimension->cumuls();
2926 IntVar*
const unused_vehicle_cumul_var =
2927 cumul_variables[unused_vehicle_end];
2928 IntVar*
const active_vehicle_cumul_var =
2929 cumul_variables[active_vehicle_end];
2930 const int64_t old_unused_vehicle_cumul =
2931 compact_assignment->Value(unused_vehicle_cumul_var);
2932 const int64_t old_active_vehicle_cumul =
2933 compact_assignment->Value(active_vehicle_cumul_var);
2934 compact_assignment->SetValue(unused_vehicle_cumul_var,
2935 old_active_vehicle_cumul);
2936 compact_assignment->SetValue(active_vehicle_cumul_var,
2937 old_unused_vehicle_cumul);
2944 return CompactAssignmentInternal(assignment,
false);
2949 return CompactAssignmentInternal(assignment,
true);
2952 Assignment* RoutingModel::CompactAssignmentInternal(
2953 const Assignment& assignment,
bool check_compact_assignment)
const {
2957 <<
"The costs are not homogeneous, routes cannot be rearranged";
2961 std::unique_ptr<Assignment> compact_assignment(
new Assignment(&assignment));
2962 for (
int vehicle = 0; vehicle < vehicles_ - 1; ++vehicle) {
2966 const int vehicle_start =
Start(vehicle);
2967 const int vehicle_end =
End(vehicle);
2969 int swap_vehicle = vehicles_ - 1;
2970 bool has_more_vehicles_with_route =
false;
2971 for (; swap_vehicle > vehicle; --swap_vehicle) {
2978 has_more_vehicles_with_route =
true;
2979 const int swap_vehicle_start =
Start(swap_vehicle);
2980 const int swap_vehicle_end =
End(swap_vehicle);
2989 if (RouteCanBeUsedByVehicle(*compact_assignment, swap_vehicle_start,
2995 if (swap_vehicle == vehicle) {
2996 if (has_more_vehicles_with_route) {
3000 LOG(
INFO) <<
"No vehicle that can be swapped with " << vehicle
3007 if (!ReplaceUnusedVehicle(vehicle, swap_vehicle,
3008 compact_assignment.get())) {
3013 if (check_compact_assignment &&
3014 !solver_->CheckAssignment(compact_assignment.get())) {
3016 LOG(
WARNING) <<
"The compacted assignment is not a valid solution";
3019 return compact_assignment.release();
3022 int RoutingModel::FindNextActive(
int index,
3023 const std::vector<int64_t>& indices)
const {
3026 const int size = indices.size();
3037 preassignment_->
Clear();
3038 IntVar* next_var =
nullptr;
3039 int lock_index = FindNextActive(-1, locks);
3040 const int size = locks.size();
3041 if (lock_index < size) {
3042 next_var =
NextVar(locks[lock_index]);
3043 preassignment_->
Add(next_var);
3044 for (lock_index = FindNextActive(lock_index, locks); lock_index < size;
3045 lock_index = FindNextActive(lock_index, locks)) {
3046 preassignment_->
SetValue(next_var, locks[lock_index]);
3047 next_var =
NextVar(locks[lock_index]);
3048 preassignment_->
Add(next_var);
3055 const std::vector<std::vector<int64_t>>& locks,
bool close_routes) {
3056 preassignment_->
Clear();
3061 const RoutingSearchParameters&
parameters)
const {
3063 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3069 const RoutingSearchParameters&
parameters)
const {
3071 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3077 if (collect_assignments_->
solution_count() == 1 && assignment_ !=
nullptr) {
3079 return assignment_->
Save(file_name);
3087 CHECK(assignment_ !=
nullptr);
3088 if (assignment_->
Load(file_name)) {
3089 return DoRestoreAssignment();
3096 CHECK(assignment_ !=
nullptr);
3098 return DoRestoreAssignment();
3101 Assignment* RoutingModel::DoRestoreAssignment() {
3105 solver_->Solve(restore_assignment_, monitors_);
3108 return collect_assignments_->
solution(0);
3117 const std::vector<std::vector<int64_t>>& routes,
3118 bool ignore_inactive_indices,
bool close_routes,
3120 CHECK(assignment !=
nullptr);
3122 LOG(
ERROR) <<
"The model is not closed yet";
3125 const int num_routes = routes.size();
3126 if (num_routes > vehicles_) {
3127 LOG(
ERROR) <<
"The number of vehicles in the assignment (" << routes.size()
3128 <<
") is greater than the number of vehicles in the model ("
3129 << vehicles_ <<
")";
3133 absl::flat_hash_set<int> visited_indices;
3135 for (
int vehicle = 0; vehicle < num_routes; ++vehicle) {
3136 const std::vector<int64_t>& route = routes[vehicle];
3137 int from_index =
Start(vehicle);
3138 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3139 visited_indices.insert(from_index);
3140 if (!insert_result.second) {
3141 LOG(
ERROR) <<
"Index " << from_index <<
" (start node for vehicle "
3142 << vehicle <<
") was already used";
3146 for (
const int64_t to_index : route) {
3147 if (to_index < 0 || to_index >=
Size()) {
3148 LOG(
ERROR) <<
"Invalid index: " << to_index;
3153 if (active_var->
Max() == 0) {
3154 if (ignore_inactive_indices) {
3157 LOG(
ERROR) <<
"Index " << to_index <<
" is not active";
3162 insert_result = visited_indices.insert(to_index);
3163 if (!insert_result.second) {
3164 LOG(
ERROR) <<
"Index " << to_index <<
" is used multiple times";
3169 if (!vehicle_var->
Contains(vehicle)) {
3170 LOG(
ERROR) <<
"Vehicle " << vehicle <<
" is not allowed at index "
3176 if (!assignment->
Contains(from_var)) {
3177 assignment->
Add(from_var);
3179 assignment->
SetValue(from_var, to_index);
3181 from_index = to_index;
3186 if (!assignment->
Contains(last_var)) {
3187 assignment->
Add(last_var);
3194 for (
int vehicle = num_routes; vehicle < vehicles_; ++vehicle) {
3195 const int start_index =
Start(vehicle);
3198 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3199 visited_indices.insert(start_index);
3200 if (!insert_result.second) {
3201 LOG(
ERROR) <<
"Index " << start_index <<
" is used multiple times";
3206 if (!assignment->
Contains(start_var)) {
3207 assignment->
Add(start_var);
3218 if (!assignment->
Contains(next_var)) {
3219 assignment->
Add(next_var);
3230 const std::vector<std::vector<int64_t>>& routes,
3231 bool ignore_inactive_indices) {
3239 return DoRestoreAssignment();
3244 std::vector<std::vector<int64_t>>*
const routes)
const {
3246 CHECK(routes !=
nullptr);
3248 const int model_size =
Size();
3249 routes->resize(vehicles_);
3250 for (
int vehicle = 0; vehicle < vehicles_; ++vehicle) {
3251 std::vector<int64_t>*
const vehicle_route = &routes->at(vehicle);
3252 vehicle_route->clear();
3254 int num_visited_indices = 0;
3255 const int first_index =
Start(vehicle);
3259 int current_index = assignment.
Value(first_var);
3260 while (!
IsEnd(current_index)) {
3261 vehicle_route->push_back(current_index);
3266 current_index = assignment.
Value(next_var);
3268 ++num_visited_indices;
3269 CHECK_LE(num_visited_indices, model_size)
3270 <<
"The assignment contains a cycle";
3278 std::vector<std::vector<int64_t>> route_indices(
vehicles());
3279 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3281 LOG(DFATAL) <<
"GetRoutesFromAssignment() called on incomplete solution:"
3282 <<
" NextVar(" << vehicle <<
") is unbound.";
3285 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3287 route_indices[vehicle].push_back(
index);
3290 route_indices[vehicle].push_back(
index);
3293 return route_indices;
3297 int64_t RoutingModel::GetArcCostForClassInternal(
3298 int64_t from_index, int64_t to_index,
3302 DCHECK_LT(cost_class_index, cost_classes_.size());
3303 CostCacheElement*
const cache = &cost_cache_[from_index];
3305 if (cache->index ==
static_cast<int>(to_index) &&
3306 cache->cost_class_index == cost_class_index) {
3310 const CostClass& cost_class = cost_classes_[cost_class_index];
3311 const auto& evaluator = transit_evaluators_[cost_class.evaluator_index];
3313 cost =
CapAdd(evaluator(from_index, to_index),
3314 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3315 }
else if (!
IsEnd(to_index)) {
3319 evaluator(from_index, to_index),
3320 CapAdd(GetDimensionTransitCostSum(from_index, to_index, cost_class),
3321 fixed_cost_of_vehicle_[index_to_vehicle_[from_index]]));
3325 if (consider_empty_route_costs_[index_to_vehicle_[from_index]]) {
3327 CapAdd(evaluator(from_index, to_index),
3328 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3333 *cache = {
static_cast<int>(to_index), cost_class_index,
cost};
3342 int vehicle)
const {
3356 return assignment.
Value(next_var);
3360 int64_t vehicle)
const {
3361 if (from_index != to_index && vehicle >= 0) {
3362 return GetArcCostForClassInternal(from_index, to_index,
3370 int64_t from_index, int64_t to_index,
3371 int64_t cost_class_index)
const {
3372 if (from_index != to_index) {
3373 return GetArcCostForClassInternal(from_index, to_index,
3381 int64_t to_index)
const {
3385 if (!is_bound_to_end_ct_added_.
Switched()) {
3388 std::vector<IntVar*> zero_transit(
Size(), solver_->MakeIntConst(0));
3389 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
3390 nexts_, active_, is_bound_to_end_, zero_transit));
3391 is_bound_to_end_ct_added_.
Switch(solver_.get());
3393 if (is_bound_to_end_[to_index]->Min() == 1)
3399 int64_t RoutingModel::GetDimensionTransitCostSum(
3400 int64_t i, int64_t j,
const CostClass& cost_class)
const {
3402 for (
const auto& evaluator_and_coefficient :
3403 cost_class.dimension_transit_evaluator_class_and_cost_coefficient) {
3404 DCHECK_GT(evaluator_and_coefficient.cost_coefficient, 0);
3407 CapProd(evaluator_and_coefficient.cost_coefficient,
3408 evaluator_and_coefficient.dimension->GetTransitValueFromClass(
3409 i, j, evaluator_and_coefficient.transit_evaluator_class)));
3425 const bool mandatory1 = active_[to1]->Min() == 1;
3426 const bool mandatory2 = active_[to2]->Min() == 1;
3428 if (mandatory1 != mandatory2)
return mandatory1;
3435 const int64_t src_vehicle = src_vehicle_var->
Max();
3436 if (src_vehicle_var->
Bound()) {
3443 mandatory1 ? to1_vehicle_var->
Bound() : (to1_vehicle_var->
Size() <= 2);
3445 mandatory2 ? to2_vehicle_var->
Bound() : (to2_vehicle_var->
Size() <= 2);
3448 if (bound1 != bound2)
return bound1;
3451 const int64_t vehicle1 = to1_vehicle_var->
Max();
3452 const int64_t vehicle2 = to2_vehicle_var->
Max();
3455 if ((vehicle1 == src_vehicle) != (vehicle2 == src_vehicle)) {
3456 return vehicle1 == src_vehicle;
3461 if (vehicle1 != src_vehicle)
return to1 < to2;
3472 const std::vector<IntVar*>& cumul_vars =
3474 IntVar*
const dim1 = cumul_vars[to1];
3475 IntVar*
const dim2 = cumul_vars[to2];
3478 if (dim1->
Max() != dim2->
Max())
return dim1->
Max() < dim2->
Max();
3487 const int64_t cost_class_index =
3488 SafeGetCostClassInt64OfVehicle(src_vehicle);
3489 const int64_t cost1 =
3492 const int64_t cost2 =
3495 if (cost1 != cost2)
return cost1 < cost2;
3502 if (num_vehicles1 != num_vehicles2)
return num_vehicles1 < num_vehicles2;
3512 DCHECK_EQ(index_to_visit_type_.size(), index_to_type_policy_.size());
3513 index_to_visit_type_[
index] = type;
3514 index_to_type_policy_[
index] = policy;
3515 num_visit_types_ =
std::max(num_visit_types_, type + 1);
3520 return index_to_visit_type_[
index];
3524 DCHECK_LT(type, single_nodes_of_type_.size());
3525 return single_nodes_of_type_[type];
3529 DCHECK_LT(type, pair_indices_of_type_.size());
3530 return pair_indices_of_type_[type];
3534 int64_t
index)
const {
3536 return index_to_type_policy_[
index];
3540 hard_incompatible_types_per_type_index_.resize(num_visit_types_);
3541 temporal_incompatible_types_per_type_index_.resize(num_visit_types_);
3542 same_vehicle_required_type_alternatives_per_type_index_.resize(
3544 required_type_alternatives_when_adding_type_index_.resize(num_visit_types_);
3545 required_type_alternatives_when_removing_type_index_.resize(num_visit_types_);
3550 hard_incompatible_types_per_type_index_.size());
3551 has_hard_type_incompatibilities_ =
true;
3553 hard_incompatible_types_per_type_index_[type1].insert(type2);
3554 hard_incompatible_types_per_type_index_[type2].insert(type1);
3559 temporal_incompatible_types_per_type_index_.size());
3560 has_temporal_type_incompatibilities_ =
true;
3562 temporal_incompatible_types_per_type_index_[type1].insert(type2);
3563 temporal_incompatible_types_per_type_index_[type2].insert(type1);
3566 const absl::flat_hash_set<int>&
3569 DCHECK_LT(type, hard_incompatible_types_per_type_index_.size());
3570 return hard_incompatible_types_per_type_index_[type];
3573 const absl::flat_hash_set<int>&
3576 DCHECK_LT(type, temporal_incompatible_types_per_type_index_.size());
3577 return temporal_incompatible_types_per_type_index_[type];
3583 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
3585 same_vehicle_required_type_alternatives_per_type_index_.size());
3587 if (required_type_alternatives.empty()) {
3591 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
3592 trivially_infeasible_visit_types_to_policies_[dependent_type];
3599 has_same_vehicle_type_requirements_ =
true;
3600 same_vehicle_required_type_alternatives_per_type_index_[dependent_type]
3601 .push_back(std::move(required_type_alternatives));
3605 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
3607 required_type_alternatives_when_adding_type_index_.size());
3609 if (required_type_alternatives.empty()) {
3613 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
3614 trivially_infeasible_visit_types_to_policies_[dependent_type];
3620 has_temporal_type_requirements_ =
true;
3621 required_type_alternatives_when_adding_type_index_[dependent_type].push_back(
3622 std::move(required_type_alternatives));
3626 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
3628 required_type_alternatives_when_removing_type_index_.size());
3630 if (required_type_alternatives.empty()) {
3634 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
3635 trivially_infeasible_visit_types_to_policies_[dependent_type];
3642 has_temporal_type_requirements_ =
true;
3643 required_type_alternatives_when_removing_type_index_[dependent_type]
3644 .push_back(std::move(required_type_alternatives));
3647 const std::vector<absl::flat_hash_set<int>>&
3651 same_vehicle_required_type_alternatives_per_type_index_.size());
3652 return same_vehicle_required_type_alternatives_per_type_index_[type];
3655 const std::vector<absl::flat_hash_set<int>>&
3658 DCHECK_LT(type, required_type_alternatives_when_adding_type_index_.size());
3659 return required_type_alternatives_when_adding_type_index_[type];
3662 const std::vector<absl::flat_hash_set<int>>&
3665 DCHECK_LT(type, required_type_alternatives_when_removing_type_index_.size());
3666 return required_type_alternatives_when_removing_type_index_[type];
3674 int64_t var_index)
const {
3675 if (active_[var_index]->Min() == 1)
3677 const std::vector<DisjunctionIndex>& disjunction_indices =
3679 if (disjunction_indices.size() != 1)
return default_value;
3684 return std::max(int64_t{0}, disjunctions_[disjunction_index].value.penalty);
3689 const std::string& dimension_to_print)
const {
3690 for (
int i = 0; i <
Size(); ++i) {
3693 <<
"DebugOutputVehicleSchedules() called on incomplete solution:"
3694 <<
" NextVar(" << i <<
") is unbound.";
3699 absl::flat_hash_set<std::string> dimension_names;
3700 if (dimension_to_print.empty()) {
3702 dimension_names.insert(all_dimension_names.begin(),
3703 all_dimension_names.end());
3705 dimension_names.insert(dimension_to_print);
3707 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3708 int empty_vehicle_range_start = vehicle;
3713 if (empty_vehicle_range_start != vehicle) {
3714 if (empty_vehicle_range_start == vehicle - 1) {
3715 absl::StrAppendFormat(&output,
"Vehicle %d: empty",
3716 empty_vehicle_range_start);
3718 absl::StrAppendFormat(&output,
"Vehicles %d-%d: empty",
3719 empty_vehicle_range_start, vehicle - 1);
3721 output.append(
"\n");
3724 absl::StrAppendFormat(&output,
"Vehicle %d:", vehicle);
3728 absl::StrAppendFormat(&output,
"%d Vehicle(%d) ",
index,
3729 solution_assignment.
Value(vehicle_var));
3733 absl::StrAppendFormat(&output,
"%s(%d..%d) ", dimension->name(),
3734 solution_assignment.
Min(
var),
3735 solution_assignment.
Max(
var));
3740 if (
IsEnd(
index)) output.append(
"Route end ");
3742 output.append(
"\n");
3745 output.append(
"Unperformed nodes: ");
3746 bool has_unperformed =
false;
3747 for (
int i = 0; i <
Size(); ++i) {
3750 absl::StrAppendFormat(&output,
"%d ", i);
3751 has_unperformed =
true;
3754 if (!has_unperformed) output.append(
"None");
3755 output.append(
"\n");
3760 std::vector<std::vector<std::pair<int64_t, int64_t>>>
3763 std::vector<std::vector<std::pair<int64_t, int64_t>>> cumul_bounds(
3765 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3767 LOG(DFATAL) <<
"GetCumulBounds() called on incomplete solution:"
3768 <<
" NextVar(" << vehicle <<
") is unbound.";
3772 for (
int vehicle_id = 0; vehicle_id <
vehicles(); ++vehicle_id) {
3775 cumul_bounds[vehicle_id].emplace_back(solution_assignment.
Min(dim_var),
3776 solution_assignment.
Max(dim_var));
3780 cumul_bounds[vehicle_id].emplace_back(solution_assignment.
Min(dim_var),
3781 solution_assignment.
Max(dim_var));
3784 return cumul_bounds;
3788 Assignment* RoutingModel::GetOrCreateAssignment() {
3789 if (assignment_ ==
nullptr) {
3790 assignment_ = solver_->MakeAssignment();
3791 assignment_->
Add(nexts_);
3793 assignment_->
Add(vehicle_vars_);
3800 Assignment* RoutingModel::GetOrCreateTmpAssignment() {
3801 if (tmp_assignment_ ==
nullptr) {
3802 tmp_assignment_ = solver_->MakeAssignment();
3803 tmp_assignment_->
Add(nexts_);
3805 return tmp_assignment_;
3808 RegularLimit* RoutingModel::GetOrCreateLimit() {
3809 if (limit_ ==
nullptr) {
3810 limit_ = solver_->MakeLimit(
3818 RegularLimit* RoutingModel::GetOrCreateLocalSearchLimit() {
3819 if (ls_limit_ ==
nullptr) {
3820 ls_limit_ = solver_->MakeLimit(absl::InfiniteDuration(),
3828 RegularLimit* RoutingModel::GetOrCreateLargeNeighborhoodSearchLimit() {
3829 if (lns_limit_ ==
nullptr) {
3830 lns_limit_ = solver_->MakeLimit(
3839 RoutingModel::GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit() {
3840 if (first_solution_lns_limit_ ==
nullptr) {
3841 first_solution_lns_limit_ = solver_->MakeLimit(
3846 return first_solution_lns_limit_;
3849 LocalSearchOperator* RoutingModel::CreateInsertionOperator() {
3850 LocalSearchOperator* insertion_operator =
3851 CreateCPOperator<MakeActiveOperator>();
3852 if (!pickup_delivery_pairs_.empty()) {
3853 insertion_operator = solver_->ConcatenateOperators(
3854 {CreatePairOperator<MakePairActiveOperator>(), insertion_operator});
3856 if (!implicit_pickup_delivery_pairs_without_alternatives_.empty()) {
3857 insertion_operator = solver_->ConcatenateOperators(
3858 {CreateOperator<MakePairActiveOperator>(
3859 implicit_pickup_delivery_pairs_without_alternatives_),
3860 insertion_operator});
3862 return insertion_operator;
3865 LocalSearchOperator* RoutingModel::CreateMakeInactiveOperator() {
3866 LocalSearchOperator* make_inactive_operator =
3867 CreateCPOperator<MakeInactiveOperator>();
3868 if (!pickup_delivery_pairs_.empty()) {
3869 make_inactive_operator = solver_->ConcatenateOperators(
3870 {CreatePairOperator<MakePairInactiveOperator>(),
3871 make_inactive_operator});
3873 return make_inactive_operator;
3876 void RoutingModel::CreateNeighborhoodOperators(
3878 local_search_operators_.clear();
3879 local_search_operators_.resize(LOCAL_SEARCH_OPERATOR_COUNTER,
nullptr);
3883 std::pair<RoutingLocalSearchOperator, Solver::LocalSearchOperators>>
3888 for (
const auto [type, op] : operator_by_type) {
3889 local_search_operators_[type] =
3891 ? solver_->MakeOperator(nexts_, op)
3892 : solver_->MakeOperator(nexts_, vehicle_vars_, op);
3897 const std::vector<std::pair<RoutingLocalSearchOperator,
3899 operator_by_type = {{LIN_KERNIGHAN,
Solver::LK},
3902 for (
const auto [type, op] : operator_by_type) {
3905 local_search_operators_[type] =
3907 ? solver_->MakeOperator(nexts_, std::move(arc_cost), op)
3908 : solver_->MakeOperator(nexts_, vehicle_vars_,
3909 std::move(arc_cost), op);
3914 local_search_operators_[RELOCATE] = CreateCPOperator<Relocate>();
3915 local_search_operators_[EXCHANGE] = CreateCPOperator<Exchange>();
3916 local_search_operators_[CROSS] = CreateCPOperator<Cross>();
3917 local_search_operators_[TWO_OPT] = CreateCPOperator<TwoOpt>();
3918 local_search_operators_[RELOCATE_AND_MAKE_ACTIVE] =
3919 CreateCPOperator<RelocateAndMakeActiveOperator>();
3920 local_search_operators_[MAKE_ACTIVE_AND_RELOCATE] =
3921 CreateCPOperator<MakeActiveAndRelocate>();
3922 local_search_operators_[MAKE_CHAIN_INACTIVE] =
3923 CreateCPOperator<MakeChainInactiveOperator>();
3924 local_search_operators_[SWAP_ACTIVE] = CreateCPOperator<SwapActiveOperator>();
3925 local_search_operators_[EXTENDED_SWAP_ACTIVE] =
3926 CreateCPOperator<ExtendedSwapActiveOperator>();
3929 local_search_operators_[MAKE_ACTIVE] = CreateInsertionOperator();
3930 local_search_operators_[MAKE_INACTIVE] = CreateMakeInactiveOperator();
3931 local_search_operators_[RELOCATE_PAIR] =
3932 CreatePairOperator<PairRelocateOperator>();
3933 std::vector<LocalSearchOperator*> light_relocate_pair_operators;
3934 light_relocate_pair_operators.push_back(
3935 CreatePairOperator<LightPairRelocateOperator>());
3936 local_search_operators_[LIGHT_RELOCATE_PAIR] =
3937 solver_->ConcatenateOperators(light_relocate_pair_operators);
3938 local_search_operators_[EXCHANGE_PAIR] =
3939 CreatePairOperator<PairExchangeOperator>();
3940 local_search_operators_[EXCHANGE_RELOCATE_PAIR] =
3941 CreatePairOperator<PairExchangeRelocateOperator>();
3942 local_search_operators_[RELOCATE_NEIGHBORS] =
3943 CreateOperator<MakeRelocateNeighborsOperator>(
3945 local_search_operators_[NODE_PAIR_SWAP] = solver_->ConcatenateOperators(
3946 {CreatePairOperator<IndexPairSwapActiveOperator>(),
3947 CreatePairOperator<SwapIndexPairOperator>(),
3948 CreatePairOperator<PairNodeSwapActiveOperator<true>>(),
3949 CreatePairOperator<PairNodeSwapActiveOperator<false>>()});
3950 local_search_operators_[RELOCATE_SUBTRIP] =
3951 CreatePairOperator<RelocateSubtrip>();
3952 local_search_operators_[EXCHANGE_SUBTRIP] =
3953 CreatePairOperator<ExchangeSubtrip>();
3955 const auto arc_cost_for_path_start =
3956 [
this](int64_t before_node, int64_t after_node, int64_t start_index) {
3957 const int vehicle = index_to_vehicle_[start_index];
3958 const int64_t arc_cost =
3960 return (before_node != start_index ||
IsEnd(after_node))
3964 local_search_operators_[RELOCATE_EXPENSIVE_CHAIN] =
3965 solver_->RevAlloc(
new RelocateExpensiveChain(
3969 vehicle_start_class_callback_,
3970 parameters.relocate_expensive_chain_num_arcs_to_consider(),
3971 arc_cost_for_path_start));
3974 const auto make_global_cheapest_insertion_filtered_heuristic =
3976 using Heuristic = GlobalCheapestInsertionFilteredHeuristic;
3977 Heuristic::GlobalCheapestInsertionParameters ls_gci_parameters;
3978 ls_gci_parameters.is_sequential =
false;
3979 ls_gci_parameters.farthest_seeds_ratio = 0.0;
3980 ls_gci_parameters.neighbors_ratio =
3981 parameters.cheapest_insertion_ls_operator_neighbors_ratio();
3982 ls_gci_parameters.min_neighbors =
3983 parameters.cheapest_insertion_ls_operator_min_neighbors();
3984 ls_gci_parameters.use_neighbors_ratio_for_initialization =
true;
3985 ls_gci_parameters.add_unperformed_entries =
3986 parameters.cheapest_insertion_add_unperformed_entries();
3987 return absl::make_unique<Heuristic>(
3990 GetOrCreateFeasibilityFilterManager(
parameters), ls_gci_parameters);
3992 const auto make_local_cheapest_insertion_filtered_heuristic =
3994 return absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
3996 GetOrCreateFeasibilityFilterManager(
parameters));
3998 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
3999 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4000 make_global_cheapest_insertion_filtered_heuristic(),
4001 parameters.heuristic_close_nodes_lns_num_nodes()));
4003 local_search_operators_[LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4004 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4005 make_local_cheapest_insertion_filtered_heuristic(),
4006 parameters.heuristic_close_nodes_lns_num_nodes()));
4008 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_PATH_LNS] =
4009 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4010 make_global_cheapest_insertion_filtered_heuristic()));
4012 local_search_operators_[LOCAL_CHEAPEST_INSERTION_PATH_LNS] =
4013 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4014 make_local_cheapest_insertion_filtered_heuristic()));
4016 local_search_operators_
4017 [RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED] =
4019 new RelocatePathAndHeuristicInsertUnperformedOperator(
4020 make_global_cheapest_insertion_filtered_heuristic()));
4022 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4023 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4024 make_global_cheapest_insertion_filtered_heuristic(),
4025 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4026 arc_cost_for_path_start));
4028 local_search_operators_[LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4029 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4030 make_local_cheapest_insertion_filtered_heuristic(),
4031 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4032 arc_cost_for_path_start));
4035 #define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators) \
4036 if (search_parameters.local_search_operators().use_##operator_method() == \
4038 operators.push_back(local_search_operators_[operator_type]); \
4041 LocalSearchOperator* RoutingModel::ConcatenateOperators(
4042 const RoutingSearchParameters& search_parameters,
4043 const std::vector<LocalSearchOperator*>& operators)
const {
4044 if (search_parameters.use_multi_armed_bandit_concatenate_operators()) {
4045 return solver_->MultiArmedBanditConcatenateOperators(
4048 .multi_armed_bandit_compound_operator_memory_coefficient(),
4050 .multi_armed_bandit_compound_operator_exploration_coefficient(),
4053 return solver_->ConcatenateOperators(operators);
4056 LocalSearchOperator* RoutingModel::GetNeighborhoodOperators(
4057 const RoutingSearchParameters& search_parameters)
const {
4058 std::vector<LocalSearchOperator*> operator_groups;
4059 std::vector<LocalSearchOperator*> operators = extra_operators_;
4060 if (!pickup_delivery_pairs_.empty()) {
4064 if (search_parameters.local_search_operators().use_relocate_pair() ==
4074 if (vehicles_ > 1) {
4085 if (!pickup_delivery_pairs_.empty() ||
4086 search_parameters.local_search_operators().use_relocate_neighbors() ==
4088 operators.push_back(local_search_operators_[RELOCATE_NEIGHBORS]);
4091 search_parameters.local_search_metaheuristic();
4092 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4093 local_search_metaheuristic !=
4094 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4095 local_search_metaheuristic !=
4096 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4103 if (!disjunctions_.
empty()) {
4120 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4129 global_cheapest_insertion_path_lns, operators);
4131 local_cheapest_insertion_path_lns, operators);
4133 RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED,
4134 relocate_path_global_cheapest_insertion_insert_unperformed, operators);
4137 global_cheapest_insertion_expensive_chain_lns,
4140 local_cheapest_insertion_expensive_chain_lns,
4143 global_cheapest_insertion_close_nodes_lns,
4146 local_cheapest_insertion_close_nodes_lns, operators);
4147 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4151 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4152 local_search_metaheuristic !=
4153 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4154 local_search_metaheuristic !=
4155 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4158 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4159 local_search_metaheuristic !=
4160 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4161 local_search_metaheuristic !=
4162 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4167 if (!disjunctions_.
empty()) {
4170 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4172 return solver_->ConcatenateOperators(operator_groups);
4175 #undef CP_ROUTING_PUSH_OPERATOR
4179 if (dimension->GetUnaryTransitEvaluator(0) !=
nullptr)
return true;
4186 void ConvertVectorInt64ToVectorInt(
const std::vector<int64_t>&
input,
4187 std::vector<int>* output) {
4188 const int n =
input.size();
4190 int* data = output->data();
4191 for (
int i = 0; i < n; ++i) {
4192 const int element =
static_cast<int>(
input[i]);
4200 std::vector<LocalSearchFilterManager::FilterEvent>
4201 RoutingModel::GetOrCreateLocalSearchFilters(
4202 const RoutingSearchParameters&
parameters,
bool filter_cost) {
4203 const auto kAccept = LocalSearchFilterManager::FilterEventType::kAccept;
4204 const auto kRelax = LocalSearchFilterManager::FilterEventType::kRelax;
4214 std::vector<LocalSearchFilterManager::FilterEvent> filters;
4216 if (filter_cost && vehicle_amortized_cost_factors_set_) {
4224 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4228 filters.push_back({sum, kAccept});
4230 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4231 nexts_, vehicle_vars_,
4232 [
this](int64_t i, int64_t j, int64_t k) {
4236 filters.push_back({sum, kAccept});
4240 filters.push_back({solver_->MakeVariableDomainFilter(), kAccept});
4242 if (vehicles_ > max_active_vehicles_) {
4246 if (!disjunctions_.
empty()) {
4250 if (!pickup_delivery_pairs_.empty()) {
4253 vehicle_pickup_delivery_policy_),
4263 const PathState* path_state_reference =
nullptr;
4265 std::vector<int> path_starts;
4266 std::vector<int> path_ends;
4267 ConvertVectorInt64ToVectorInt(starts_, &path_starts);
4268 ConvertVectorInt64ToVectorInt(ends_, &path_ends);
4270 auto path_state = absl::make_unique<PathState>(
4271 Size() +
vehicles(), std::move(path_starts), std::move(path_ends));
4272 path_state_reference = path_state.get();
4284 if (!dimension->HasBreakConstraints())
continue;
4287 filters.insert(filters.end(), extra_filters_.begin(), extra_filters_.end());
4291 LocalSearchFilterManager* RoutingModel::GetOrCreateLocalSearchFilterManager(
4293 if (!local_search_filter_manager_) {
4294 local_search_filter_manager_ =
4295 solver_->RevAlloc(
new LocalSearchFilterManager(
4298 return local_search_filter_manager_;
4301 std::vector<LocalSearchFilterManager::FilterEvent>
4302 RoutingModel::GetOrCreateFeasibilityFilters(
4304 return GetOrCreateLocalSearchFilters(
parameters,
false);
4307 LocalSearchFilterManager* RoutingModel::GetOrCreateFeasibilityFilterManager(
4309 if (!feasibility_filter_manager_) {
4310 feasibility_filter_manager_ =
4311 solver_->RevAlloc(
new LocalSearchFilterManager(
4314 return feasibility_filter_manager_;
4317 LocalSearchFilterManager*
4318 RoutingModel::GetOrCreateStrongFeasibilityFilterManager(
4320 if (!strong_feasibility_filter_manager_) {
4321 std::vector<LocalSearchFilterManager::FilterEvent> filters =
4324 LocalSearchFilterManager::FilterEventType::kAccept});
4325 strong_feasibility_filter_manager_ =
4326 solver_->RevAlloc(
new LocalSearchFilterManager(std::move(filters)));
4328 return strong_feasibility_filter_manager_;
4333 for (
int vehicle = 0; vehicle < dimension.model()->
vehicles(); vehicle++) {
4334 if (!dimension.AreVehicleTransitsPositive(vehicle)) {
4342 void RoutingModel::StoreDimensionCumulOptimizers(
4344 Assignment* packed_dimensions_collector_assignment =
4345 solver_->MakeAssignment();
4346 packed_dimensions_collector_assignment->AddObjective(
CostVar());
4347 const int num_dimensions = dimensions_.size();
4348 local_optimizer_index_.
resize(num_dimensions, -1);
4349 global_optimizer_index_.
resize(num_dimensions, -1);
4352 if (dimension->global_span_cost_coefficient() > 0 ||
4353 !dimension->GetNodePrecedences().empty()) {
4355 global_optimizer_index_[dim] = global_dimension_optimizers_.size();
4356 global_dimension_optimizers_.push_back(
4357 absl::make_unique<GlobalDimensionCumulOptimizer>(dimension));
4358 packed_dimensions_collector_assignment->Add(dimension->cumuls());
4359 if (!AllTransitsPositive(*dimension)) {
4360 dimension->SetOffsetForGlobalOptimizer(0);
4365 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4368 std::min(offset, dimension->CumulVar(
Start(vehicle))->Min() - 1);
4370 dimension->SetOffsetForGlobalOptimizer(
std::max(
Zero(), offset));
4372 bool has_span_cost =
false;
4373 bool has_span_limit =
false;
4374 std::vector<int64_t> vehicle_offsets(
vehicles());
4375 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4376 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4377 has_span_cost =
true;
4379 if (dimension->GetSpanUpperBoundForVehicle(vehicle) <
4381 has_span_limit =
true;
4384 vehicle_offsets[vehicle] =
4385 dimension->AreVehicleTransitsPositive(vehicle)
4387 dimension->CumulVar(
Start(vehicle))->Min() - 1)
4390 bool has_soft_lower_bound =
false;
4391 bool has_soft_upper_bound =
false;
4392 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
4393 if (dimension->HasCumulVarSoftLowerBound(i)) {
4394 has_soft_lower_bound =
true;
4396 if (dimension->HasCumulVarSoftUpperBound(i)) {
4397 has_soft_upper_bound =
true;
4400 int num_linear_constraints = 0;
4401 if (has_span_cost) ++num_linear_constraints;
4402 if (has_span_limit) ++num_linear_constraints;
4403 if (dimension->HasSoftSpanUpperBounds()) ++num_linear_constraints;
4404 if (has_soft_lower_bound) ++num_linear_constraints;
4405 if (has_soft_upper_bound) ++num_linear_constraints;
4406 if (dimension->HasBreakConstraints()) ++num_linear_constraints;
4407 if (num_linear_constraints >= 2) {
4408 dimension->SetVehicleOffsetsForLocalOptimizer(
4409 std::move(vehicle_offsets));
4410 local_optimizer_index_[dim] = local_dimension_optimizers_.size();
4411 local_dimension_optimizers_.push_back(
4412 absl::make_unique<LocalDimensionCumulOptimizer>(
4413 dimension,
parameters.continuous_scheduling_solver()));
4414 bool has_intervals =
false;
4415 for (
const SortedDisjointIntervalList& intervals :
4416 dimension->forbidden_intervals()) {
4419 if (intervals.NumIntervals() > 0) {
4420 has_intervals =
true;
4424 if (dimension->HasBreakConstraints() || has_intervals) {
4425 local_dimension_mp_optimizers_.push_back(
4426 absl::make_unique<LocalDimensionCumulOptimizer>(
4427 dimension,
parameters.mixed_integer_scheduling_solver()));
4429 local_dimension_mp_optimizers_.push_back(
nullptr);
4431 packed_dimensions_collector_assignment->Add(dimension->cumuls());
4434 DCHECK_EQ(local_dimension_mp_optimizers_.size(),
4435 local_dimension_optimizers_.size());
4441 for (IntVar*
const extra_var : extra_vars_) {
4442 packed_dimensions_collector_assignment->Add(extra_var);
4444 for (IntervalVar*
const extra_interval : extra_intervals_) {
4445 packed_dimensions_collector_assignment->Add(extra_interval);
4448 packed_dimensions_assignment_collector_ = solver_->MakeFirstSolutionCollector(
4449 packed_dimensions_collector_assignment);
4454 std::vector<RoutingDimension*> dimensions;
4456 bool has_soft_or_span_cost =
false;
4457 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4458 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4459 has_soft_or_span_cost =
true;
4463 if (!has_soft_or_span_cost) {
4464 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
4465 if (dimension->HasCumulVarSoftUpperBound(i) ||
4466 dimension->HasCumulVarSoftLowerBound(i)) {
4467 has_soft_or_span_cost =
true;
4472 if (has_soft_or_span_cost) dimensions.push_back(dimension);
4478 RoutingModel::CreateFinalizerForMinimizedAndMaximizedVariables() {
4479 std::stable_sort(finalizer_variable_cost_pairs_.begin(),
4480 finalizer_variable_cost_pairs_.end(),
4481 [](
const std::pair<IntVar*, int64_t>& var_cost1,
4482 const std::pair<IntVar*, int64_t>& var_cost2) {
4483 return var_cost1.second > var_cost2.second;
4485 const int num_variables = finalizer_variable_cost_pairs_.size() +
4486 finalizer_variable_target_pairs_.size();
4487 std::vector<IntVar*> variables;
4488 std::vector<int64_t> targets;
4489 variables.reserve(num_variables);
4490 targets.reserve(num_variables);
4491 for (
const auto& variable_cost : finalizer_variable_cost_pairs_) {
4492 variables.push_back(variable_cost.first);
4495 for (
const auto& variable_target : finalizer_variable_target_pairs_) {
4496 variables.push_back(variable_target.first);
4497 targets.push_back(variable_target.second);
4500 std::move(targets));
4503 DecisionBuilder* RoutingModel::CreateSolutionFinalizer(SearchLimit* lns_limit) {
4504 std::vector<DecisionBuilder*> decision_builders;
4505 decision_builders.push_back(solver_->MakePhase(
4508 if (!local_dimension_optimizers_.empty()) {
4509 decision_builders.push_back(
4510 solver_->RevAlloc(
new SetCumulsFromLocalDimensionCosts(
4511 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
4514 if (!global_dimension_optimizers_.empty()) {
4515 decision_builders.push_back(
4516 solver_->RevAlloc(
new SetCumulsFromGlobalDimensionCosts(
4517 &global_dimension_optimizers_, lns_limit)));
4519 decision_builders.push_back(
4520 CreateFinalizerForMinimizedAndMaximizedVariables());
4522 return solver_->Compose(decision_builders);
4525 void RoutingModel::CreateFirstSolutionDecisionBuilders(
4526 const RoutingSearchParameters& search_parameters) {
4527 first_solution_decision_builders_.resize(
4529 first_solution_filtered_decision_builders_.resize(
4531 DecisionBuilder*
const finalize_solution =
4532 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit());
4534 first_solution_decision_builders_
4535 [FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE] = finalize_solution;
4537 first_solution_decision_builders_
4538 [FirstSolutionStrategy::GLOBAL_CHEAPEST_ARC] = solver_->MakePhase(
4540 [
this](int64_t i, int64_t j) {
4548 first_solution_decision_builders_[FirstSolutionStrategy::LOCAL_CHEAPEST_ARC] =
4551 first_solution_decision_builders_[FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
4553 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
4554 first_solution_filtered_decision_builders_
4555 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
4556 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4557 absl::make_unique<EvaluatorCheapestAdditionFilteredHeuristic>(
4559 [
this](int64_t i, int64_t j) {
4562 GetOrCreateFeasibilityFilterManager(search_parameters))));
4563 first_solution_decision_builders_
4564 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
4565 solver_->Try(first_solution_filtered_decision_builders_
4566 [FirstSolutionStrategy::PATH_CHEAPEST_ARC],
4567 first_solution_decision_builders_
4568 [FirstSolutionStrategy::PATH_CHEAPEST_ARC]);
4576 first_solution_decision_builders_
4577 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
4579 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
4580 first_solution_filtered_decision_builders_
4581 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
4582 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4583 absl::make_unique<ComparatorCheapestAdditionFilteredHeuristic>(
4585 GetOrCreateFeasibilityFilterManager(search_parameters))));
4586 first_solution_decision_builders_
4587 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->Try(
4588 first_solution_filtered_decision_builders_
4589 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC],
4590 first_solution_decision_builders_
4591 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC]);
4594 if (first_solution_evaluator_ !=
nullptr) {
4595 first_solution_decision_builders_
4596 [FirstSolutionStrategy::EVALUATOR_STRATEGY] = solver_->MakePhase(
4599 first_solution_decision_builders_
4600 [FirstSolutionStrategy::EVALUATOR_STRATEGY] =
nullptr;
4603 first_solution_decision_builders_[FirstSolutionStrategy::ALL_UNPERFORMED] =
4606 RegularLimit*
const ls_limit = solver_->MakeLimit(
4610 DecisionBuilder*
const finalize = solver_->MakeSolveOnce(
4611 finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit());
4612 LocalSearchPhaseParameters*
const insertion_parameters =
4613 solver_->MakeLocalSearchPhaseParameters(
4614 nullptr, CreateInsertionOperator(), finalize, ls_limit,
4615 GetOrCreateLocalSearchFilterManager(search_parameters));
4616 std::vector<IntVar*> decision_vars = nexts_;
4618 decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(),
4619 vehicle_vars_.end());
4621 const int64_t optimization_step =
std::max(
4623 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
4624 solver_->MakeNestedOptimize(
4626 insertion_parameters),
4627 GetOrCreateAssignment(),
false, optimization_step);
4628 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
4629 solver_->Compose(first_solution_decision_builders_
4630 [FirstSolutionStrategy::BEST_INSERTION],
4634 GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
4636 gci_parameters.is_sequential =
false;
4637 gci_parameters.farthest_seeds_ratio =
4638 search_parameters.cheapest_insertion_farthest_seeds_ratio();
4639 gci_parameters.neighbors_ratio =
4640 search_parameters.cheapest_insertion_first_solution_neighbors_ratio();
4641 gci_parameters.min_neighbors =
4642 search_parameters.cheapest_insertion_first_solution_min_neighbors();
4643 gci_parameters.use_neighbors_ratio_for_initialization =
4645 .cheapest_insertion_first_solution_use_neighbors_ratio_for_initialization();
4646 gci_parameters.add_unperformed_entries =
4647 search_parameters.cheapest_insertion_add_unperformed_entries();
4648 for (
bool is_sequential : {
false,
true}) {
4650 is_sequential ? FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION
4651 : FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
4652 gci_parameters.is_sequential = is_sequential;
4654 first_solution_filtered_decision_builders_[first_solution_strategy] =
4655 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4656 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4658 [
this](int64_t i, int64_t j, int64_t vehicle) {
4662 GetOrCreateFeasibilityFilterManager(search_parameters),
4664 IntVarFilteredDecisionBuilder*
const strong_gci =
4665 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4666 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4668 [
this](int64_t i, int64_t j, int64_t vehicle) {
4672 GetOrCreateStrongFeasibilityFilterManager(search_parameters),
4674 first_solution_decision_builders_[first_solution_strategy] = solver_->Try(
4675 first_solution_filtered_decision_builders_[first_solution_strategy],
4676 solver_->Try(strong_gci, first_solution_decision_builders_
4677 [FirstSolutionStrategy::BEST_INSERTION]));
4681 first_solution_filtered_decision_builders_
4682 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] =
4683 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4684 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4686 [
this](int64_t i, int64_t j, int64_t vehicle) {
4689 GetOrCreateFeasibilityFilterManager(search_parameters))));
4690 IntVarFilteredDecisionBuilder*
const strong_lci =
4691 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4692 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4694 [
this](int64_t i, int64_t j, int64_t vehicle) {
4697 GetOrCreateStrongFeasibilityFilterManager(search_parameters))));
4698 first_solution_decision_builders_
4699 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = solver_->Try(
4700 first_solution_filtered_decision_builders_
4701 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION],
4702 solver_->Try(strong_lci,
4703 first_solution_decision_builders_
4704 [FirstSolutionStrategy::BEST_INSERTION]));
4706 SavingsFilteredHeuristic::SavingsParameters savings_parameters;
4707 savings_parameters.neighbors_ratio =
4708 search_parameters.savings_neighbors_ratio();
4709 savings_parameters.max_memory_usage_bytes =
4710 search_parameters.savings_max_memory_usage_bytes();
4711 savings_parameters.add_reverse_arcs =
4712 search_parameters.savings_add_reverse_arcs();
4713 savings_parameters.arc_coefficient =
4714 search_parameters.savings_arc_coefficient();
4715 LocalSearchFilterManager* filter_manager =
nullptr;
4716 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
4717 filter_manager = GetOrCreateFeasibilityFilterManager(search_parameters);
4720 if (search_parameters.savings_parallel_routes()) {
4721 IntVarFilteredDecisionBuilder* savings_db =
4722 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4723 absl::make_unique<ParallelSavingsFilteredHeuristic>(
4724 this, &manager_, savings_parameters, filter_manager)));
4725 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
4726 first_solution_filtered_decision_builders_
4727 [FirstSolutionStrategy::SAVINGS] = savings_db;
4730 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
4731 solver_->Try(savings_db,
4732 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4733 absl::make_unique<ParallelSavingsFilteredHeuristic>(
4734 this, &manager_, savings_parameters,
4735 GetOrCreateStrongFeasibilityFilterManager(
4736 search_parameters)))));
4738 IntVarFilteredDecisionBuilder* savings_db =
4739 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4740 absl::make_unique<SequentialSavingsFilteredHeuristic>(
4741 this, &manager_, savings_parameters, filter_manager)));
4742 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
4743 first_solution_filtered_decision_builders_
4744 [FirstSolutionStrategy::SAVINGS] = savings_db;
4747 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
4748 solver_->Try(savings_db,
4749 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4750 absl::make_unique<SequentialSavingsFilteredHeuristic>(
4751 this, &manager_, savings_parameters,
4752 GetOrCreateStrongFeasibilityFilterManager(
4753 search_parameters)))));
4756 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
4759 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
4762 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP]);
4764 first_solution_decision_builders_[FirstSolutionStrategy::CHRISTOFIDES] =
4765 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4766 absl::make_unique<ChristofidesFilteredHeuristic>(
4767 this, GetOrCreateFeasibilityFilterManager(search_parameters),
4768 search_parameters.christofides_use_minimum_matching())));
4770 const bool has_precedences = std::any_of(
4771 dimensions_.begin(), dimensions_.end(),
4773 bool has_single_vehicle_node =
false;
4774 for (
int node = 0; node <
Size(); node++) {
4775 if (!
IsStart(node) && !
IsEnd(node) && allowed_vehicles_[node].size() == 1) {
4776 has_single_vehicle_node =
true;
4780 automatic_first_solution_strategy_ =
4782 has_precedences, has_single_vehicle_node);
4783 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC] =
4784 first_solution_decision_builders_[automatic_first_solution_strategy_];
4785 first_solution_decision_builders_[FirstSolutionStrategy::UNSET] =
4786 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC];
4789 DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder(
4790 const RoutingSearchParameters& search_parameters)
const {
4792 search_parameters.first_solution_strategy();
4793 if (first_solution_strategy < first_solution_decision_builders_.size()) {
4794 return first_solution_decision_builders_[first_solution_strategy];
4800 IntVarFilteredDecisionBuilder*
4801 RoutingModel::GetFilteredFirstSolutionDecisionBuilderOrNull(
4802 const RoutingSearchParameters& search_parameters)
const {
4804 search_parameters.first_solution_strategy();
4805 return first_solution_filtered_decision_builders_[first_solution_strategy];
4808 LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters(
4809 const RoutingSearchParameters& search_parameters) {
4810 SearchLimit* lns_limit = GetOrCreateLargeNeighborhoodSearchLimit();
4811 return solver_->MakeLocalSearchPhaseParameters(
4812 CostVar(), GetNeighborhoodOperators(search_parameters),
4813 solver_->MakeSolveOnce(CreateSolutionFinalizer(lns_limit), lns_limit),
4814 GetOrCreateLocalSearchLimit(),
4815 GetOrCreateLocalSearchFilterManager(search_parameters));
4818 DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder(
4819 const RoutingSearchParameters& search_parameters) {
4820 const int size =
Size();
4821 DecisionBuilder* first_solution =
4822 GetFirstSolutionDecisionBuilder(search_parameters);
4823 LocalSearchPhaseParameters*
const parameters =
4824 CreateLocalSearchParameters(search_parameters);
4825 SearchLimit* first_solution_lns_limit =
4826 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
4827 DecisionBuilder*
const first_solution_sub_decision_builder =
4828 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_solution_lns_limit),
4829 first_solution_lns_limit);
4831 return solver_->MakeLocalSearchPhase(nexts_, first_solution,
4832 first_solution_sub_decision_builder,
4835 const int all_size = size + size + vehicles_;
4836 std::vector<IntVar*> all_vars(all_size);
4837 for (
int i = 0; i < size; ++i) {
4838 all_vars[i] = nexts_[i];
4840 for (
int i = size; i < all_size; ++i) {
4841 all_vars[i] = vehicle_vars_[i - size];
4843 return solver_->MakeLocalSearchPhase(all_vars, first_solution,
4844 first_solution_sub_decision_builder,
4849 void RoutingModel::SetupDecisionBuilders(
4850 const RoutingSearchParameters& search_parameters) {
4851 if (search_parameters.use_depth_first_search()) {
4852 SearchLimit* first_lns_limit =
4853 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
4854 solve_db_ = solver_->Compose(
4855 GetFirstSolutionDecisionBuilder(search_parameters),
4856 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_lns_limit),
4859 solve_db_ = CreateLocalSearchDecisionBuilder(search_parameters);
4861 CHECK(preassignment_ !=
nullptr);
4862 DecisionBuilder* restore_preassignment =
4863 solver_->MakeRestoreAssignment(preassignment_);
4864 solve_db_ = solver_->Compose(restore_preassignment, solve_db_);
4866 solver_->Compose(restore_preassignment,
4867 solver_->MakeLocalSearchPhase(
4868 GetOrCreateAssignment(),
4869 CreateLocalSearchParameters(search_parameters)));
4870 restore_assignment_ = solver_->Compose(
4871 solver_->MakeRestoreAssignment(GetOrCreateAssignment()),
4872 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
4873 restore_tmp_assignment_ = solver_->Compose(
4874 restore_preassignment,
4875 solver_->MakeRestoreAssignment(GetOrCreateTmpAssignment()),
4876 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
4879 void RoutingModel::SetupMetaheuristics(
4880 const RoutingSearchParameters& search_parameters) {
4881 SearchMonitor* optimize;
4883 search_parameters.local_search_metaheuristic();
4886 bool limit_too_long =
4887 !search_parameters.has_time_limit() &&
4889 const int64_t optimization_step =
std::max(
4891 switch (metaheuristic) {
4892 case LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH:
4894 optimize = solver_->MakeGuidedLocalSearch(
4897 optimization_step, nexts_,
4898 search_parameters.guided_local_search_lambda_coefficient());
4900 optimize = solver_->MakeGuidedLocalSearch(
4902 [
this](int64_t i, int64_t j, int64_t k) {
4905 optimization_step, nexts_, vehicle_vars_,
4906 search_parameters.guided_local_search_lambda_coefficient());
4909 case LocalSearchMetaheuristic::SIMULATED_ANNEALING:
4911 solver_->MakeSimulatedAnnealing(
false, cost_, optimization_step, 100);
4913 case LocalSearchMetaheuristic::TABU_SEARCH:
4914 optimize = solver_->MakeTabuSearch(
false, cost_, optimization_step,
4915 nexts_, 10, 10, .8);
4917 case LocalSearchMetaheuristic::GENERIC_TABU_SEARCH: {
4918 std::vector<operations_research::IntVar*> tabu_vars;
4919 if (tabu_var_callback_) {
4920 tabu_vars = tabu_var_callback_(
this);
4922 tabu_vars.push_back(cost_);
4924 optimize = solver_->MakeGenericTabuSearch(
false, cost_, optimization_step,
4929 limit_too_long =
false;
4930 optimize = solver_->MakeMinimize(cost_, optimization_step);
4932 if (limit_too_long) {
4933 LOG(
WARNING) << LocalSearchMetaheuristic::Value_Name(metaheuristic)
4934 <<
" specified without sane timeout: solve may run forever.";
4936 monitors_.push_back(optimize);
4940 tabu_var_callback_ = std::move(tabu_var_callback);
4943 void RoutingModel::SetupAssignmentCollector(
4944 const RoutingSearchParameters& search_parameters) {
4945 Assignment* full_assignment = solver_->MakeAssignment();
4947 full_assignment->
Add(dimension->cumuls());
4949 for (IntVar*
const extra_var : extra_vars_) {
4950 full_assignment->
Add(extra_var);
4952 for (IntervalVar*
const extra_interval : extra_intervals_) {
4953 full_assignment->
Add(extra_interval);
4955 full_assignment->
Add(nexts_);
4956 full_assignment->
Add(active_);
4957 full_assignment->
Add(vehicle_vars_);
4960 collect_assignments_ = solver_->MakeNBestValueSolutionCollector(
4961 full_assignment, search_parameters.number_of_solutions_to_collect(),
4963 collect_one_assignment_ =
4964 solver_->MakeFirstSolutionCollector(full_assignment);
4965 monitors_.push_back(collect_assignments_);
4968 void RoutingModel::SetupTrace(
4969 const RoutingSearchParameters& search_parameters) {
4970 if (search_parameters.log_search()) {
4971 Solver::SearchLogParameters search_log_parameters;
4972 search_log_parameters.branch_period = 10000;
4973 search_log_parameters.objective =
nullptr;
4974 search_log_parameters.variable = cost_;
4975 search_log_parameters.scaling_factor =
4976 search_parameters.log_cost_scaling_factor();
4977 search_log_parameters.offset = search_parameters.log_cost_offset();
4978 if (!search_parameters.log_tag().empty()) {
4979 const std::string& tag = search_parameters.log_tag();
4980 search_log_parameters.display_callback = [tag]() {
return tag; };
4982 search_log_parameters.display_callback =
nullptr;
4984 search_log_parameters.display_on_new_solutions_only =
false;
4985 monitors_.push_back(solver_->MakeSearchLog(search_log_parameters));
4989 void RoutingModel::SetupImprovementLimit(
4990 const RoutingSearchParameters& search_parameters) {
4991 if (search_parameters.has_improvement_limit_parameters()) {
4992 monitors_.push_back(solver_->MakeImprovementLimit(
4993 cost_,
false, search_parameters.log_cost_scaling_factor(),
4994 search_parameters.log_cost_offset(),
4995 search_parameters.improvement_limit_parameters()
4996 .improvement_rate_coefficient(),
4997 search_parameters.improvement_limit_parameters()
4998 .improvement_rate_solutions_distance()));
5002 void RoutingModel::SetupSearchMonitors(
5003 const RoutingSearchParameters& search_parameters) {
5004 monitors_.push_back(GetOrCreateLimit());
5005 SetupImprovementLimit(search_parameters);
5006 SetupMetaheuristics(search_parameters);
5007 SetupAssignmentCollector(search_parameters);
5008 SetupTrace(search_parameters);
5011 bool RoutingModel::UsesLightPropagation(
5012 const RoutingSearchParameters& search_parameters)
const {
5013 return !search_parameters.use_full_propagation() &&
5014 !search_parameters.use_depth_first_search() &&
5015 search_parameters.first_solution_strategy() !=
5016 FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE;
5023 finalizer_variable_cost_pairs_.size());
5024 if (
index < finalizer_variable_cost_pairs_.size()) {
5025 const int64_t old_cost = finalizer_variable_cost_pairs_[
index].second;
5026 finalizer_variable_cost_pairs_[
index].second =
CapAdd(old_cost,
cost);
5028 finalizer_variable_cost_pairs_.emplace_back(
var,
cost);
5034 if (finalizer_variable_target_set_.contains(
var))
return;
5035 finalizer_variable_target_set_.insert(
var);
5036 finalizer_variable_target_pairs_.emplace_back(
var, target);
5047 void RoutingModel::SetupSearch(
5048 const RoutingSearchParameters& search_parameters) {
5049 SetupDecisionBuilders(search_parameters);
5050 SetupSearchMonitors(search_parameters);
5054 extra_vars_.push_back(
var);
5058 extra_intervals_.push_back(
interval);
5063 class PathSpansAndTotalSlacks :
public Constraint {
5067 std::vector<IntVar*> spans,
5068 std::vector<IntVar*> total_slacks)
5071 dimension_(dimension),
5072 spans_(std::move(spans)),
5073 total_slacks_(std::move(total_slacks)) {
5074 CHECK_EQ(spans_.size(), model_->vehicles());
5075 CHECK_EQ(total_slacks_.size(), model_->vehicles());
5076 vehicle_demons_.resize(model_->vehicles());
5079 std::string DebugString()
const override {
return "PathSpansAndTotalSlacks"; }
5081 void Post()
override {
5082 const int num_nodes = model_->VehicleVars().size();
5083 const int num_transits = model_->Nexts().size();
5084 for (
int node = 0; node < num_nodes; ++node) {
5086 model_->solver(),
this, &PathSpansAndTotalSlacks::PropagateNode,
5087 "PathSpansAndTotalSlacks::PropagateNode", node);
5088 dimension_->CumulVar(node)->WhenRange(demon);
5089 model_->VehicleVar(node)->WhenBound(demon);
5090 if (node < num_transits) {
5091 dimension_->TransitVar(node)->WhenRange(demon);
5092 dimension_->FixedTransitVar(node)->WhenBound(demon);
5093 model_->NextVar(node)->WhenBound(demon);
5096 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5097 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5099 solver(),
this, &PathSpansAndTotalSlacks::PropagateVehicle,
5100 "PathSpansAndTotalSlacks::PropagateVehicle", vehicle);
5101 vehicle_demons_[vehicle] = demon;
5102 if (spans_[vehicle]) spans_[vehicle]->WhenRange(demon);
5103 if (total_slacks_[vehicle]) total_slacks_[vehicle]->WhenRange(demon);
5104 if (dimension_->HasBreakConstraints()) {
5105 for (IntervalVar*
b : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5106 b->WhenAnything(demon);
5113 void InitialPropagate()
override {
5114 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5115 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5116 PropagateVehicle(vehicle);
5124 void PropagateNode(
int node) {
5125 if (!model_->VehicleVar(node)->Bound())
return;
5126 const int vehicle = model_->VehicleVar(node)->Min();
5127 if (vehicle < 0 || vehicle_demons_[vehicle] ==
nullptr)
return;
5128 EnqueueDelayedDemon(vehicle_demons_[vehicle]);
5136 int64_t SpanMin(
int vehicle, int64_t sum_fixed_transits) {
5138 const int64_t span_min = spans_[vehicle]
5139 ? spans_[vehicle]->Min()
5141 const int64_t total_slack_min = total_slacks_[vehicle]
5142 ? total_slacks_[vehicle]->Min()
5144 return std::min(span_min,
CapAdd(total_slack_min, sum_fixed_transits));
5146 int64_t SpanMax(
int vehicle, int64_t sum_fixed_transits) {
5148 const int64_t span_max = spans_[vehicle]
5149 ? spans_[vehicle]->Max()
5151 const int64_t total_slack_max = total_slacks_[vehicle]
5152 ? total_slacks_[vehicle]->Max()
5154 return std::max(span_max,
CapAdd(total_slack_max, sum_fixed_transits));
5156 void SetSpanMin(
int vehicle, int64_t
min, int64_t sum_fixed_transits) {
5158 if (spans_[vehicle]) {
5159 spans_[vehicle]->SetMin(
min);
5161 if (total_slacks_[vehicle]) {
5162 total_slacks_[vehicle]->SetMin(
CapSub(
min, sum_fixed_transits));
5165 void SetSpanMax(
int vehicle, int64_t
max, int64_t sum_fixed_transits) {
5167 if (spans_[vehicle]) {
5168 spans_[vehicle]->SetMax(
max);
5170 if (total_slacks_[vehicle]) {
5171 total_slacks_[vehicle]->SetMax(
CapSub(
max, sum_fixed_transits));
5176 void SynchronizeSpanAndTotalSlack(
int vehicle, int64_t sum_fixed_transits) {
5178 IntVar* span = spans_[vehicle];
5179 IntVar* total_slack = total_slacks_[vehicle];
5180 if (!span || !total_slack)
return;
5181 span->SetMin(
CapAdd(total_slack->Min(), sum_fixed_transits));
5182 span->SetMax(
CapAdd(total_slack->Max(), sum_fixed_transits));
5183 total_slack->SetMin(
CapSub(span->Min(), sum_fixed_transits));
5184 total_slack->SetMax(
CapSub(span->Max(), sum_fixed_transits));
5187 void PropagateVehicle(
int vehicle) {
5188 DCHECK(spans_[vehicle] || total_slacks_[vehicle]);
5189 const int start = model_->Start(vehicle);
5190 const int end = model_->End(vehicle);
5196 int curr_node = start;
5197 while (!model_->IsEnd(curr_node)) {
5198 const IntVar* next_var = model_->NextVar(curr_node);
5199 if (!next_var->Bound())
return;
5200 path_.push_back(curr_node);
5201 curr_node = next_var->Value();
5206 int64_t sum_fixed_transits = 0;
5207 for (
const int node : path_) {
5208 const IntVar* fixed_transit_var = dimension_->FixedTransitVar(node);
5209 if (!fixed_transit_var->Bound())
return;
5210 sum_fixed_transits =
5211 CapAdd(sum_fixed_transits, fixed_transit_var->Value());
5214 SynchronizeSpanAndTotalSlack(vehicle, sum_fixed_transits);
5221 if (dimension_->HasBreakConstraints() &&
5222 !dimension_->GetBreakIntervalsOfVehicle(vehicle).empty()) {
5223 const int64_t vehicle_start_max = dimension_->CumulVar(start)->Max();
5224 const int64_t vehicle_end_min = dimension_->CumulVar(end)->Min();
5226 int64_t min_break_duration = 0;
5227 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5228 if (!br->MustBePerformed())
continue;
5229 if (vehicle_start_max < br->EndMin() &&
5230 br->StartMax() < vehicle_end_min) {
5231 min_break_duration =
CapAdd(min_break_duration, br->DurationMin());
5234 SetSpanMin(vehicle,
CapAdd(min_break_duration, sum_fixed_transits),
5235 sum_fixed_transits);
5241 const int64_t slack_max =
5242 CapSub(SpanMax(vehicle, sum_fixed_transits), sum_fixed_transits);
5243 const int64_t max_additional_slack =
5244 CapSub(slack_max, min_break_duration);
5245 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5246 if (!br->MustBePerformed())
continue;
5248 if (vehicle_start_max >= br->EndMin() &&
5249 br->StartMax() < vehicle_end_min) {
5250 if (br->DurationMin() > max_additional_slack) {
5253 br->SetEndMax(vehicle_start_max);
5254 dimension_->CumulVar(start)->SetMin(br->EndMin());
5259 if (vehicle_start_max < br->EndMin() &&
5260 br->StartMax() >= vehicle_end_min) {
5261 if (br->DurationMin() > max_additional_slack) {
5262 br->SetStartMin(vehicle_end_min);
5263 dimension_->CumulVar(end)->SetMax(br->StartMax());
5271 IntVar* start_cumul = dimension_->CumulVar(start);
5272 IntVar* end_cumul = dimension_->CumulVar(end);
5273 const int64_t
start_min = start_cumul->Min();
5274 const int64_t
start_max = start_cumul->Max();
5275 const int64_t
end_min = end_cumul->Min();
5276 const int64_t
end_max = end_cumul->Max();
5279 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5281 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5283 const int64_t span_min = SpanMin(vehicle, sum_fixed_transits);
5284 const int64_t span_max = SpanMax(vehicle, sum_fixed_transits);
5285 const int64_t slack_from_lb =
CapSub(span_max, span_lb);
5286 const int64_t slack_from_ub =
CapSub(span_ub, span_min);
5300 int64_t span_lb = 0;
5301 int64_t span_ub = 0;
5302 for (
const int node : path_) {
5303 span_lb =
CapAdd(span_lb, dimension_->TransitVar(node)->Min());
5304 span_ub =
CapAdd(span_ub, dimension_->TransitVar(node)->Max());
5306 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5307 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5311 const int64_t span_min = SpanMin(vehicle, sum_fixed_transits);
5312 const int64_t span_max = SpanMax(vehicle, sum_fixed_transits);
5313 const int64_t slack_from_lb =
CapSub(span_max, span_lb);
5314 const int64_t slack_from_ub =
5316 ?
CapSub(span_ub, span_min)
5317 : std::numeric_limits<int64_t>::
max();
5318 for (
const int node : path_) {
5319 IntVar* transit_var = dimension_->TransitVar(node);
5320 const int64_t transit_i_min = transit_var->Min();
5321 const int64_t transit_i_max = transit_var->Max();
5325 transit_var->SetMax(
CapAdd(transit_i_min, slack_from_lb));
5326 transit_var->SetMin(
CapSub(transit_i_max, slack_from_ub));
5331 path_.push_back(end);
5342 int64_t arrival_time = dimension_->CumulVar(start)->Min();
5343 for (
int i = 1; i < path_.size(); ++i) {
5346 dimension_->FixedTransitVar(path_[i - 1])->Min()),
5347 dimension_->CumulVar(path_[i])->Min());
5349 int64_t departure_time = arrival_time;
5350 for (
int i = path_.size() - 2; i >= 0; --i) {
5353 dimension_->FixedTransitVar(path_[i])->Min()),
5354 dimension_->CumulVar(path_[i])->Max());
5356 const int64_t span_lb =
CapSub(arrival_time, departure_time);
5357 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5358 const int64_t maximum_deviation =
5359 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5360 const int64_t start_lb =
CapSub(departure_time, maximum_deviation);
5361 dimension_->CumulVar(start)->SetMin(start_lb);
5365 int64_t departure_time = dimension_->CumulVar(end)->Max();
5366 for (
int i = path_.size() - 2; i >= 0; --i) {
5367 const int curr_node = path_[i];
5370 dimension_->FixedTransitVar(curr_node)->Min()),
5371 dimension_->CumulVar(curr_node)->Max());
5373 int arrival_time = departure_time;
5374 for (
int i = 1; i < path_.size(); ++i) {
5377 dimension_->FixedTransitVar(path_[i - 1])->Min()),
5378 dimension_->CumulVar(path_[i])->Min());
5380 const int64_t span_lb =
CapSub(arrival_time, departure_time);
5381 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5382 const int64_t maximum_deviation =
5383 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5384 dimension_->CumulVar(end)->SetMax(
5385 CapAdd(arrival_time, maximum_deviation));
5391 std::vector<IntVar*> spans_;
5392 std::vector<IntVar*> total_slacks_;
5393 std::vector<int> path_;
5394 std::vector<Demon*> vehicle_demons_;
5401 std::vector<IntVar*> total_slacks) {
5403 CHECK_EQ(vehicles_, total_slacks.size());
5405 new PathSpansAndTotalSlacks(
this, dimension, spans, total_slacks));
5413 std::vector<int64_t> vehicle_capacities,
5414 const std::string&
name,
5416 : vehicle_capacities_(std::move(vehicle_capacities)),
5417 base_dimension_(base_dimension),
5418 global_span_cost_coefficient_(0),
5421 global_optimizer_offset_(0) {
5423 vehicle_span_upper_bounds_.assign(
model->vehicles(),
5425 vehicle_span_cost_coefficients_.assign(
model->vehicles(), 0);
5428 RoutingDimension::RoutingDimension(RoutingModel*
model,
5429 std::vector<int64_t> vehicle_capacities,
5430 const std::string&
name, SelfBased)
5431 : RoutingDimension(
model, std::move(vehicle_capacities),
name, this) {}
5434 cumul_var_piecewise_linear_cost_.clear();
5437 void RoutingDimension::Initialize(
5438 const std::vector<int>& transit_evaluators,
5439 const std::vector<int>& state_dependent_transit_evaluators,
5440 int64_t slack_max) {
5442 InitializeTransits(transit_evaluators, state_dependent_transit_evaluators,
5456 class LightRangeLessOrEqual :
public Constraint {
5458 LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l, IntExpr*
const r);
5459 ~LightRangeLessOrEqual()
override {}
5460 void Post()
override;
5461 void InitialPropagate()
override;
5462 std::string DebugString()
const override;
5463 IntVar* Var()
override {
5464 return solver()->MakeIsLessOrEqualVar(left_, right_);
5467 void Accept(ModelVisitor*
const visitor)
const override {
5478 IntExpr*
const left_;
5479 IntExpr*
const right_;
5483 LightRangeLessOrEqual::LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l,
5485 : Constraint(s), left_(l), right_(r), demon_(nullptr) {}
5487 void LightRangeLessOrEqual::Post() {
5489 solver(),
this, &LightRangeLessOrEqual::CheckRange,
"CheckRange");
5490 left_->WhenRange(demon_);
5491 right_->WhenRange(demon_);
5494 void LightRangeLessOrEqual::InitialPropagate() {
5495 left_->SetMax(right_->Max());
5496 right_->SetMin(left_->Min());
5497 if (left_->Max() <= right_->Min()) {
5502 void LightRangeLessOrEqual::CheckRange() {
5503 if (left_->Min() > right_->Max()) {
5506 if (left_->Max() <= right_->Min()) {
5511 std::string LightRangeLessOrEqual::DebugString()
const {
5512 return left_->DebugString() +
" < " + right_->DebugString();
5517 void RoutingDimension::InitializeCumuls() {
5518 Solver*
const solver = model_->
solver();
5520 const auto capacity_range = std::minmax_element(vehicle_capacities_.begin(),
5521 vehicle_capacities_.end());
5522 const int64_t min_capacity = *capacity_range.first;
5524 const int64_t max_capacity = *capacity_range.second;
5525 solver->MakeIntVarArray(size, 0, max_capacity, name_, &cumuls_);
5527 for (
int v = 0; v < model_->
vehicles(); v++) {
5528 const int64_t vehicle_capacity = vehicle_capacities_[v];
5529 cumuls_[model_->
Start(v)]->SetMax(vehicle_capacity);
5530 cumuls_[model_->
End(v)]->SetMax(vehicle_capacity);
5533 forbidden_intervals_.resize(size);
5534 capacity_vars_.clear();
5535 if (min_capacity != max_capacity) {
5538 for (
int i = 0; i < size; ++i) {
5539 IntVar*
const capacity_var = capacity_vars_[i];
5540 if (i < model_->Size()) {
5541 IntVar*
const capacity_active = solver->MakeBoolVar();
5542 solver->AddConstraint(
5543 solver->MakeLessOrEqual(model_->
ActiveVar(i), capacity_active));
5544 solver->AddConstraint(solver->MakeIsLessOrEqualCt(
5545 cumuls_[i], capacity_var, capacity_active));
5547 solver->AddConstraint(
5548 solver->MakeLessOrEqual(cumuls_[i], capacity_var));
5555 template <
int64_t value>
5556 int64_t IthElementOrValue(
const std::vector<int64_t>& v, int64_t
index) {
5560 void ComputeTransitClasses(
const std::vector<int>& evaluator_indices,
5561 std::vector<int>* class_evaluators,
5562 std::vector<int64_t>* vehicle_to_class) {
5563 CHECK(class_evaluators !=
nullptr);
5564 CHECK(vehicle_to_class !=
nullptr);
5565 class_evaluators->clear();
5566 vehicle_to_class->resize(evaluator_indices.size(), -1);
5567 absl::flat_hash_map<int, int64_t> evaluator_to_class;
5568 for (
int i = 0; i < evaluator_indices.size(); ++i) {
5569 const int evaluator_index = evaluator_indices[i];
5570 int evaluator_class = -1;
5571 if (!
gtl::FindCopy(evaluator_to_class, evaluator_index, &evaluator_class)) {
5572 evaluator_class = class_evaluators->size();
5573 evaluator_to_class[evaluator_index] = evaluator_class;
5574 class_evaluators->push_back(evaluator_index);
5576 (*vehicle_to_class)[i] = evaluator_class;
5581 void RoutingDimension::InitializeTransitVariables(int64_t slack_max) {
5582 CHECK(!class_evaluators_.empty());
5583 CHECK(base_dimension_ ==
nullptr ||
5584 !state_dependent_class_evaluators_.empty());
5586 Solver*
const solver = model_->
solver();
5587 const int size = model_->
Size();
5590 return (0 <=
index &&
index < state_dependent_vehicle_to_class_.size())
5591 ? state_dependent_vehicle_to_class_[
index]
5592 : state_dependent_class_evaluators_.size();
5594 const std::string slack_name = name_ +
" slack";
5595 const std::string transit_name = name_ +
" fixed transit";
5597 for (int64_t i = 0; i < size; ++i) {
5600 absl::StrCat(transit_name, i));
5602 if (base_dimension_ !=
nullptr) {
5603 if (state_dependent_class_evaluators_.size() == 1) {
5604 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
5605 for (int64_t j = 0; j < cumuls_.size(); ++j) {
5606 transition_variables[j] =
5607 MakeRangeMakeElementExpr(
5609 ->StateDependentTransitCallback(
5610 state_dependent_class_evaluators_[0])(i, j)
5612 base_dimension_->
CumulVar(i), solver)
5615 dependent_transits_[i] =
5616 solver->MakeElement(transition_variables, model_->
NextVar(i))
5619 IntVar*
const vehicle_class_var =
5621 ->MakeElement(dependent_vehicle_class_function,
5624 std::vector<IntVar*> transit_for_vehicle;
5625 transit_for_vehicle.reserve(state_dependent_class_evaluators_.size() +
5627 for (
int evaluator : state_dependent_class_evaluators_) {
5628 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
5629 for (int64_t j = 0; j < cumuls_.size(); ++j) {
5630 transition_variables[j] =
5631 MakeRangeMakeElementExpr(
5634 base_dimension_->
CumulVar(i), solver)
5637 transit_for_vehicle.push_back(
5638 solver->MakeElement(transition_variables, model_->
NextVar(i))
5641 transit_for_vehicle.push_back(solver->MakeIntConst(0));
5642 dependent_transits_[i] =
5643 solver->MakeElement(transit_for_vehicle, vehicle_class_var)->Var();
5646 dependent_transits_[i] = solver->MakeIntConst(0);
5650 IntExpr* transit_expr = fixed_transits_[i];
5651 if (dependent_transits_[i]->Min() != 0 ||
5652 dependent_transits_[i]->Max() != 0) {
5653 transit_expr = solver->MakeSum(transit_expr, dependent_transits_[i]);
5656 if (slack_max == 0) {
5657 slacks_[i] = solver->MakeIntConst(0);
5660 solver->MakeIntVar(0, slack_max, absl::StrCat(slack_name, i));
5661 transit_expr = solver->MakeSum(slacks_[i], transit_expr);
5663 transits_[i] = transit_expr->Var();
5667 void RoutingDimension::InitializeTransits(
5668 const std::vector<int>& transit_evaluators,
5669 const std::vector<int>& state_dependent_transit_evaluators,
5670 int64_t slack_max) {
5672 CHECK(base_dimension_ ==
nullptr ||
5673 model_->
vehicles() == state_dependent_transit_evaluators.size());
5674 const int size = model_->
Size();
5675 transits_.resize(size,
nullptr);
5676 fixed_transits_.resize(size,
nullptr);
5677 slacks_.resize(size,
nullptr);
5678 dependent_transits_.resize(size,
nullptr);
5679 ComputeTransitClasses(transit_evaluators, &class_evaluators_,
5680 &vehicle_to_class_);
5681 if (base_dimension_ !=
nullptr) {
5682 ComputeTransitClasses(state_dependent_transit_evaluators,
5683 &state_dependent_class_evaluators_,
5684 &state_dependent_vehicle_to_class_);
5687 InitializeTransitVariables(slack_max);
5692 std::vector<int64_t>* values) {
5693 const int num_nodes = path.size();
5694 values->resize(num_nodes - 1);
5695 for (
int i = 0; i < num_nodes - 1; ++i) {
5696 (*values)[i] = evaluator(path[i], path[i + 1]);
5701 : model_(
model), occurrences_of_type_(
model.GetNumberOfVisitTypes()) {}
5704 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor) {
5711 for (
int pos = 0; pos < current_route_visits_.size(); pos++) {
5712 const int64_t current_visit = current_route_visits_[pos];
5719 DCHECK_LT(type, occurrences_of_type_.size());
5720 int& num_type_added = occurrences_of_type_[type].num_type_added_to_vehicle;
5721 int& num_type_removed =
5722 occurrences_of_type_[type].num_type_removed_from_vehicle;
5723 DCHECK_LE(num_type_removed, num_type_added);
5725 num_type_removed == num_type_added) {
5735 if (policy == VisitTypePolicy::TYPE_ADDED_TO_VEHICLE ||
5736 policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED) {
5739 if (policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED ||
5740 policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
5748 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor) {
5751 std::fill(occurrences_of_type_.begin(), occurrences_of_type_.end(),
5757 current_route_visits_.clear();
5759 current = next_accessor(current)) {
5762 VisitTypePolicy::TYPE_ON_VEHICLE_UP_TO_VISIT) {
5763 occurrences_of_type_[type].position_of_last_type_on_vehicle_up_to_visit =
5764 current_route_visits_.size();
5766 current_route_visits_.push_back(current);
5788 check_hard_incompatibilities_(check_hard_incompatibilities) {}
5790 bool TypeIncompatibilityChecker::HasRegulationsToCheck()
const {
5792 (check_hard_incompatibilities_ &&
5801 bool TypeIncompatibilityChecker::CheckTypeRegulations(
int type,
5802 VisitTypePolicy policy,
5804 if (policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
5809 for (
int incompatible_type :
5815 if (check_hard_incompatibilities_) {
5816 for (
int incompatible_type :
5826 bool TypeRequirementChecker::HasRegulationsToCheck()
const {
5831 bool TypeRequirementChecker::CheckRequiredTypesCurrentlyOnRoute(
5832 const std::vector<absl::flat_hash_set<int>>& required_type_alternatives,
5834 for (
const absl::flat_hash_set<int>& requirement_alternatives :
5835 required_type_alternatives) {
5836 bool has_one_of_alternatives =
false;
5837 for (
int type_alternative : requirement_alternatives) {
5839 has_one_of_alternatives =
true;
5843 if (!has_one_of_alternatives) {
5850 bool TypeRequirementChecker::CheckTypeRegulations(
int type,
5851 VisitTypePolicy policy,
5855 if (!CheckRequiredTypesCurrentlyOnRoute(
5861 if (!CheckRequiredTypesCurrentlyOnRoute(
5868 types_with_same_vehicle_requirements_on_route_.insert(type);
5873 bool TypeRequirementChecker::FinalizeCheck()
const {
5874 for (
int type : types_with_same_vehicle_requirements_on_route_) {
5875 for (
const absl::flat_hash_set<int>& requirement_alternatives :
5877 bool has_one_of_alternatives =
false;
5878 for (
const int type_alternative : requirement_alternatives) {
5880 has_one_of_alternatives =
true;
5884 if (!has_one_of_alternatives) {
5895 incompatibility_checker_(
model, true),
5896 requirement_checker_(
model),
5897 vehicle_demons_(
model.vehicles()) {}
5899 void TypeRegulationsConstraint::PropagateNodeRegulations(
int node) {
5906 if (vehicle < 0)
return;
5907 DCHECK(vehicle_demons_[vehicle] !=
nullptr);
5911 void TypeRegulationsConstraint::CheckRegulationsOnVehicle(
int vehicle) {
5912 const auto next_accessor = [
this, vehicle](int64_t node) {
5917 return model_.
End(vehicle);
5919 if (!incompatibility_checker_.
CheckVehicle(vehicle, next_accessor) ||
5920 !requirement_checker_.
CheckVehicle(vehicle, next_accessor)) {
5926 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
5928 solver(),
this, &TypeRegulationsConstraint::CheckRegulationsOnVehicle,
5929 "CheckRegulationsOnVehicle", vehicle);
5931 for (
int node = 0; node < model_.
Size(); node++) {
5933 solver(),
this, &TypeRegulationsConstraint::PropagateNodeRegulations,
5934 "PropagateNodeRegulations", node);
5941 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
5942 CheckRegulationsOnVehicle(vehicle);
5946 void RoutingDimension::CloseModel(
bool use_light_propagation) {
5948 const auto capacity_lambda = [
this](int64_t vehicle) {
5949 return vehicle >= 0 ? vehicle_capacities_[vehicle]
5952 for (
int i = 0; i < capacity_vars_.size(); ++i) {
5953 IntVar*
const vehicle_var = model_->
VehicleVar(i);
5954 IntVar*
const capacity_var = capacity_vars_[i];
5955 if (use_light_propagation) {
5957 solver, capacity_var, vehicle_var, capacity_lambda,
5958 [
this]() {
return model_->enable_deep_serialization_; }));
5965 for (
int i = 0; i < fixed_transits_.size(); ++i) {
5966 IntVar*
const next_var = model_->
NextVar(i);
5967 IntVar*
const fixed_transit = fixed_transits_[i];
5968 const auto transit_vehicle_evaluator = [
this, i](int64_t to,
5969 int64_t eval_index) {
5972 if (use_light_propagation) {
5973 if (class_evaluators_.size() == 1) {
5974 const int class_evaluator_index = class_evaluators_[0];
5975 const auto& unary_callback =
5977 if (unary_callback ==
nullptr) {
5979 solver, fixed_transit, next_var,
5980 [
this, i](int64_t to) {
5983 [
this]() {
return model_->enable_deep_serialization_; }));
5985 fixed_transit->SetValue(unary_callback(i));
5989 solver, fixed_transit, next_var, model_->
VehicleVar(i),
5990 transit_vehicle_evaluator,
5991 [
this]() {
return model_->enable_deep_serialization_; }));
5994 if (class_evaluators_.size() == 1) {
5995 const int class_evaluator_index = class_evaluators_[0];
5996 const auto& unary_callback =
5998 if (unary_callback ==
nullptr) {
6000 fixed_transit, solver
6002 [
this, i](int64_t to) {
6004 class_evaluators_[0])(i, to);
6009 fixed_transit->SetValue(unary_callback(i));
6013 fixed_transit, solver
6021 GlobalVehicleBreaksConstraint* constraint =
6028 int64_t vehicle)
const {
6034 int64_t
index, int64_t min_value, int64_t max_value)
const {
6040 int64_t next_start =
min;
6044 if (next_start >
max)
break;
6045 if (next_start < interval->start) {
6050 if (next_start <=
max) {
6059 CHECK_LT(vehicle, vehicle_span_upper_bounds_.size());
6061 vehicle_span_upper_bounds_[vehicle] =
upper_bound;
6067 CHECK_LT(vehicle, vehicle_span_cost_coefficients_.size());
6069 vehicle_span_cost_coefficients_[vehicle] =
coefficient;
6085 if (!
cost.IsNonDecreasing()) {
6086 LOG(
WARNING) <<
"Only non-decreasing cost functions are supported.";
6089 if (
cost.Value(0) < 0) {
6090 LOG(
WARNING) <<
"Only positive cost functions are supported.";
6093 if (
index >= cumul_var_piecewise_linear_cost_.size()) {
6094 cumul_var_piecewise_linear_cost_.resize(
index + 1);
6096 PiecewiseLinearCost& piecewise_linear_cost =
6097 cumul_var_piecewise_linear_cost_[
index];
6098 piecewise_linear_cost.var = cumuls_[
index];
6099 piecewise_linear_cost.cost = absl::make_unique<PiecewiseLinearFunction>(
cost);
6103 return (
index < cumul_var_piecewise_linear_cost_.size() &&
6104 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr);
6108 int64_t
index)
const {
6109 if (
index < cumul_var_piecewise_linear_cost_.size() &&
6110 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr) {
6111 return cumul_var_piecewise_linear_cost_[
index].cost.get();
6121 const int vehicle =
model->VehicleIndex(
index);
6123 return solver->
MakeProd(expr,
model->VehicleCostsConsideredVar(vehicle))
6130 void RoutingDimension::SetupCumulVarPiecewiseLinearCosts(
6131 std::vector<IntVar*>* cost_elements)
const {
6132 CHECK(cost_elements !=
nullptr);
6133 Solver*
const solver = model_->
solver();
6134 for (
int i = 0; i < cumul_var_piecewise_linear_cost_.size(); ++i) {
6135 const PiecewiseLinearCost& piecewise_linear_cost =
6136 cumul_var_piecewise_linear_cost_[i];
6137 if (piecewise_linear_cost.var !=
nullptr) {
6138 IntExpr*
const expr = solver->MakePiecewiseLinearExpr(
6139 piecewise_linear_cost.var, *piecewise_linear_cost.cost);
6140 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6141 cost_elements->push_back(cost_var);
6152 if (
index >= cumul_var_soft_upper_bound_.size()) {
6153 cumul_var_soft_upper_bound_.resize(
index + 1, {
nullptr, 0, 0});
6160 return (
index < cumul_var_soft_upper_bound_.size() &&
6161 cumul_var_soft_upper_bound_[
index].var !=
nullptr);
6165 if (
index < cumul_var_soft_upper_bound_.size() &&
6166 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6167 return cumul_var_soft_upper_bound_[
index].bound;
6169 return cumuls_[
index]->Max();
6173 int64_t
index)
const {
6174 if (
index < cumul_var_soft_upper_bound_.size() &&
6175 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6176 return cumul_var_soft_upper_bound_[
index].coefficient;
6181 void RoutingDimension::SetupCumulVarSoftUpperBoundCosts(
6182 std::vector<IntVar*>* cost_elements)
const {
6183 CHECK(cost_elements !=
nullptr);
6185 for (
int i = 0; i < cumul_var_soft_upper_bound_.size(); ++i) {
6186 const SoftBound& soft_bound = cumul_var_soft_upper_bound_[i];
6187 if (soft_bound.var !=
nullptr) {
6189 solver->
MakeSum(soft_bound.var, -soft_bound.bound), 0,
6190 soft_bound.coefficient);
6191 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6192 cost_elements->push_back(cost_var);
6196 soft_bound.coefficient);
6204 if (
index >= cumul_var_soft_lower_bound_.size()) {
6205 cumul_var_soft_lower_bound_.resize(
index + 1, {
nullptr, 0, 0});
6212 return (
index < cumul_var_soft_lower_bound_.size() &&
6213 cumul_var_soft_lower_bound_[
index].var !=
nullptr);
6217 if (
index < cumul_var_soft_lower_bound_.size() &&
6218 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6219 return cumul_var_soft_lower_bound_[
index].bound;
6221 return cumuls_[
index]->Min();
6225 int64_t
index)
const {
6226 if (
index < cumul_var_soft_lower_bound_.size() &&
6227 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6228 return cumul_var_soft_lower_bound_[
index].coefficient;
6233 void RoutingDimension::SetupCumulVarSoftLowerBoundCosts(
6234 std::vector<IntVar*>* cost_elements)
const {
6235 CHECK(cost_elements !=
nullptr);
6237 for (
int i = 0; i < cumul_var_soft_lower_bound_.size(); ++i) {
6238 const SoftBound& soft_bound = cumul_var_soft_lower_bound_[i];
6239 if (soft_bound.var !=
nullptr) {
6242 soft_bound.coefficient);
6243 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6244 cost_elements->push_back(cost_var);
6248 soft_bound.coefficient);
6253 void RoutingDimension::SetupGlobalSpanCost(
6254 std::vector<IntVar*>* cost_elements)
const {
6255 CHECK(cost_elements !=
nullptr);
6256 Solver*
const solver = model_->
solver();
6257 if (global_span_cost_coefficient_ != 0) {
6258 std::vector<IntVar*> end_cumuls;
6259 for (
int i = 0; i < model_->
vehicles(); ++i) {
6260 end_cumuls.push_back(solver
6261 ->MakeProd(model_->vehicle_costs_considered_[i],
6262 cumuls_[model_->
End(i)])
6265 IntVar*
const max_end_cumul = solver->
MakeMax(end_cumuls)->
Var();
6267 max_end_cumul, global_span_cost_coefficient_);
6268 std::vector<IntVar*> start_cumuls;
6269 for (
int i = 0; i < model_->
vehicles(); ++i) {
6270 IntVar* global_span_cost_start_cumul =
6272 solver->AddConstraint(solver->MakeIfThenElseCt(
6273 model_->vehicle_costs_considered_[i], cumuls_[model_->
Start(i)],
6274 max_end_cumul, global_span_cost_start_cumul));
6275 start_cumuls.push_back(global_span_cost_start_cumul);
6277 IntVar*
const min_start_cumul = solver->MakeMin(start_cumuls)->Var();
6279 min_start_cumul, global_span_cost_coefficient_);
6284 for (
int var_index = 0; var_index < model_->
Size(); ++var_index) {
6286 slacks_[var_index], global_span_cost_coefficient_);
6287 cost_elements->push_back(
6290 model_->vehicle_costs_considered_[0],
6293 solver->MakeSum(transits_[var_index],
6294 dependent_transits_[var_index]),
6295 global_span_cost_coefficient_),
6300 IntVar*
const end_range =
6301 solver->MakeDifference(max_end_cumul, min_start_cumul)->Var();
6302 end_range->SetMin(0);
6303 cost_elements->push_back(
6304 solver->MakeProd(end_range, global_span_cost_coefficient_)->Var());
6310 std::vector<IntervalVar*> breaks,
int vehicle,
6311 std::vector<int64_t> node_visit_transits) {
6312 if (breaks.empty())
return;
6314 [node_visit_transits](int64_t from, int64_t to) {
6315 return node_visit_transits[from];
6321 std::vector<IntervalVar*> breaks,
int vehicle,
6322 std::vector<int64_t> node_visit_transits,
6323 std::function<int64_t(int64_t, int64_t)> delays) {
6324 if (breaks.empty())
return;
6326 [node_visit_transits](int64_t from, int64_t to) {
6327 return node_visit_transits[from];
6329 const int delay_evaluator =
6336 std::vector<IntervalVar*> breaks,
int vehicle,
int pre_travel_evaluator,
6337 int post_travel_evaluator) {
6340 if (breaks.empty())
return;
6342 vehicle_break_intervals_[vehicle] = std::move(breaks);
6343 vehicle_pre_travel_evaluators_[vehicle] = pre_travel_evaluator;
6344 vehicle_post_travel_evaluators_[vehicle] = post_travel_evaluator;
6365 DCHECK(!break_constraints_are_initialized_);
6366 const int num_vehicles = model_->
vehicles();
6367 vehicle_break_intervals_.resize(num_vehicles);
6368 vehicle_pre_travel_evaluators_.resize(num_vehicles, -1);
6369 vehicle_post_travel_evaluators_.resize(num_vehicles, -1);
6370 vehicle_break_distance_duration_.resize(num_vehicles);
6371 break_constraints_are_initialized_ =
true;
6375 return break_constraints_are_initialized_;
6379 int vehicle)
const {
6381 DCHECK_LT(vehicle, vehicle_break_intervals_.size());
6382 return vehicle_break_intervals_[vehicle];
6387 DCHECK_LT(vehicle, vehicle_pre_travel_evaluators_.size());
6388 return vehicle_pre_travel_evaluators_[vehicle];
6393 DCHECK_LT(vehicle, vehicle_post_travel_evaluators_.size());
6394 return vehicle_post_travel_evaluators_[vehicle];
6403 vehicle_break_distance_duration_[vehicle].emplace_back(
distance, duration);
6412 const std::vector<std::pair<int64_t, int64_t>>&
6415 DCHECK_LT(vehicle, vehicle_break_distance_duration_.size());
6416 return vehicle_break_distance_duration_[vehicle];
6422 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6423 pickup_to_delivery_limits_per_pair_index_.resize(pair_index + 1);
6425 pickup_to_delivery_limits_per_pair_index_[pair_index] =
6426 std::move(limit_function);
6430 return !pickup_to_delivery_limits_per_pair_index_.empty();
6435 int delivery)
const {
6438 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6442 pickup_to_delivery_limits_per_pair_index_[pair_index];
6443 if (!pickup_to_delivery_limit_function) {
6449 return pickup_to_delivery_limit_function(pickup, delivery);
6452 void RoutingDimension::SetupSlackAndDependentTransitCosts()
const {
6453 if (model_->
vehicles() == 0)
return;
6455 bool all_vehicle_span_costs_are_equal =
true;
6456 for (
int i = 1; i < model_->
vehicles(); ++i) {
6457 all_vehicle_span_costs_are_equal &= vehicle_span_cost_coefficients_[i] ==
6458 vehicle_span_cost_coefficients_[0];
6461 if (all_vehicle_span_costs_are_equal &&
6462 vehicle_span_cost_coefficients_[0] == 0) {
6474 std::vector<const RoutingDimension*> dimensions_with_relevant_slacks = {
this};
6476 const RoutingDimension*
next =
6477 dimensions_with_relevant_slacks.back()->base_dimension_;
6478 if (
next ==
nullptr ||
next == dimensions_with_relevant_slacks.back()) {
6481 dimensions_with_relevant_slacks.push_back(
next);
6484 for (
auto it = dimensions_with_relevant_slacks.rbegin();
6485 it != dimensions_with_relevant_slacks.rend(); ++it) {
6486 for (
int i = 0; i < model_->
vehicles(); ++i) {
6492 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.
bool Load(const std::string &filename)
Loads an assignment from a file; does not add variables to the assignment (only the variables contain...
bool Contains(const IntVar *const var) const
int64_t ObjectiveValue() const
bool Save(const std::string &filename) const
Saves the assignment to a file.
void AddObjective(IntVar *const v)
void SetValue(const IntVar *const var, int64_t value)
int64_t Max(const IntVar *const var) const
int64_t Value(const IntVar *const var) const
void CopyIntersection(const Assignment *assignment)
Copies the intersection of the two assignments to the current assignment.
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
int64_t Min(const IntVar *const var) const
A constraint is the main modeling object.
A DecisionBuilder is responsible for creating 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_t Min() const =0
virtual int64_t Max() const =0
Decision builder building a solution using heuristics with local search filters to evaluate its feasi...
int64_t number_of_decisions() const
Returns statistics from its underlying heuristic.
int64_t number_of_rejects() const
The class IntVar is a subset of IntExpr.
virtual bool Contains(int64_t v) const =0
This method returns whether the value 'v' is in the domain of the variable.
virtual void WhenBound(Demon *d)=0
This method attaches a demon that will be awakened when the variable is bound.
virtual int64_t Value() const =0
This method returns the value of the variable.
virtual uint64_t 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_t 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_t branches, int64_t failures, int64_t solutions)
void Switch(Solver *const solver)
Dimensions represent quantities accumulated at nodes along the routes.
void SetSpanCostCoefficientForAllVehicles(int64_t coefficient)
void SetCumulVarPiecewiseLinearCost(int64_t 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...
bool HasCumulVarPiecewiseLinearCost(int64_t index) const
Returns true if a piecewise linear cost has been set for a given variable index.
int64_t GetCumulVarSoftUpperBoundCoefficient(int64_t index) const
Returns the cost coefficient of the soft upper bound of a cumul variable for a given variable index.
RoutingModel * model() const
Returns the model on which the dimension was created.
bool HasPickupToDeliveryLimits() const
int64_t GetPickupToDeliveryLimitForPair(int pair_index, int pickup, int delivery) const
bool HasCumulVarSoftLowerBound(int64_t index) const
Returns true if a soft lower bound has been set for a given variable index.
void SetBreakDistanceDurationOfVehicle(int64_t distance, int64_t 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.
SortedDisjointIntervalList GetAllowedIntervalsInRange(int64_t index, int64_t min_value, int64_t max_value) const
Returns allowed intervals for a given node in a given interval.
void InitializeBreaks()
Sets up vehicle_break_intervals_, vehicle_break_distance_duration_, pre_travel_evaluators and post_tr...
std::function< int64_t(int, int)> PickupToDeliveryLimitFunction
Limits, in terms of maximum difference between the cumul variables, between the pickup and delivery a...
int GetPreTravelEvaluatorOfVehicle(int vehicle) const
!defined(SWIGPYTHON)
bool HasCumulVarSoftUpperBound(int64_t index) const
Returns true if a soft upper bound has been set for a given variable index.
const std::vector< IntervalVar * > & GetBreakIntervalsOfVehicle(int vehicle) const
Returns the break intervals set by SetBreakIntervalsOfVehicle().
void SetPickupToDeliveryLimitFunctionForPair(PickupToDeliveryLimitFunction limit_function, int pair_index)
const PiecewiseLinearFunction * GetCumulVarPiecewiseLinearCost(int64_t index) const
Returns the piecewise linear cost of a cumul variable for a given variable index.
int64_t GetTransitValue(int64_t from_index, int64_t to_index, int64_t vehicle) const
Returns the transition value for a given pair of nodes (as var index); this value is the one taken by...
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_t index, int64_t upper_bound, int64_t coefficient)
Sets a soft upper bound to the cumul variable of a given variable index.
const std::string & name() const
Returns the name of the dimension.
IntVar * CumulVar(int64_t index) const
Get the cumul, transit and slack variables for the given node (given as int64_t var index).
void SetBreakIntervalsOfVehicle(std::vector< IntervalVar * > breaks, int vehicle, int pre_travel_evaluator, int post_travel_evaluator)
Sets the breaks for a given vehicle.
int64_t GetCumulVarSoftUpperBound(int64_t index) const
Returns the soft upper bound of a cumul variable for a given variable index.
const std::vector< std::pair< int64_t, int64_t > > & GetBreakDistanceDurationOfVehicle(int vehicle) const
Returns the pairs (distance, duration) specified by break distance constraints.
void SetSpanUpperBoundForVehicle(int64_t upper_bound, int vehicle)
!defined(SWIGCSHARP) && !defined(SWIGJAVA) !defined(SWIGPYTHON)
void SetGlobalSpanCostCoefficient(int64_t coefficient)
Sets a cost proportional to the global dimension span, that is the difference between the largest val...
int64_t GetCumulVarSoftLowerBoundCoefficient(int64_t index) const
Returns the cost coefficient of the soft lower bound of a cumul variable for a given variable index.
int GetPostTravelEvaluatorOfVehicle(int vehicle) const
void SetSpanCostCoefficientForVehicle(int64_t coefficient, int vehicle)
Sets a cost proportional to the dimension span on a given vehicle, or on all vehicles at once.
void SetCumulVarSoftLowerBound(int64_t index, int64_t lower_bound, int64_t coefficient)
Sets a soft lower bound to the cumul variable of a given variable index.
int64_t GetCumulVarSoftLowerBound(int64_t index) const
Returns the soft lower bound of a cumul variable for a given variable index.
Manager for any NodeIndex <-> variable index conversion.
int64_t GetEndIndex(int vehicle) const
std::vector< NodeIndex > GetIndexToNodeMap() const
int64_t GetStartIndex(int vehicle) const
int num_unique_depots() const
complete.
NodeIndex IndexToNode(int64_t index) const
friend class RoutingModelInspector
Assignment * ReadAssignmentFromRoutes(const std::vector< std::vector< int64_t >> &routes, bool ignore_inactive_indices)
Restores the routes as the current solution.
int64_t ComputeLowerBound()
Computes a lower bound to the routing problem solving a linear assignment problem.
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.
const Assignment * SolveFromAssignmentsWithParameters(const std::vector< const Assignment * > &assignments, const RoutingSearchParameters &search_parameters, std::vector< const Assignment * > *solutions=nullptr)
Same as above but will try all assignments in order as first solutions until one succeeds.
Assignment * RestoreAssignment(const Assignment &solution)
Restores an assignment as a solution in the routing model and returns the new solution.
bool RoutesToAssignment(const std::vector< std::vector< int64_t >> &routes, bool ignore_inactive_indices, bool close_routes, Assignment *const assignment) const
Fills an assignment from a specification of the routes of the vehicles.
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.
bool AddDimensionDependentDimensionWithVehicleCapacity(const std::vector< int > &pure_transits, const std::vector< int > &dependent_transits, const RoutingDimension *base_dimension, int64_t slack_max, std::vector< int64_t > vehicle_capacities, bool fix_start_cumul_to_zero, const std::string &name)
Creates a dimension with transits depending on the cumuls of another dimension.
VehicleClassIndex GetVehicleClassIndexOfVehicle(int64_t vehicle) const
void AddLocalSearchOperator(LocalSearchOperator *ls_operator)
Adds a local search operator to the set of operators used to solve the vehicle routing problem.
std::pair< int, bool > AddMatrixDimension(std::vector< std::vector< int64_t > > values, int64_t 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...
const std::vector< int > & GetPairIndicesOfType(int type) const
RoutingTransitCallback1 TransitCallback1
const absl::flat_hash_set< int > & GetTemporalTypeIncompatibilitiesOfType(int type) const
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.
int GetVehicleClassesCount() const
Returns the number of different vehicle classes in the model.
int64_t GetFixedCostOfVehicle(int vehicle) const
Returns the route fixed cost taken into account if the route of the vehicle is not empty,...
std::pair< int, bool > AddVectorDimension(std::vector< int64_t > values, int64_t 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; ...
PickupAndDeliveryPolicy GetPickupAndDeliveryPolicyOfVehicle(int vehicle) const
bool IsStart(int64_t index) const
Returns true if 'index' represents the first node of a route.
void SetPickupAndDeliveryPolicyOfAllVehicles(PickupAndDeliveryPolicy policy)
Sets the Pickup and delivery policy of all vehicles.
void AddSoftSameVehicleConstraint(const std::vector< int64_t > &indices, int64_t cost)
Adds a soft constraint to force a set of variable indices to be on the same vehicle.
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 ...
IntVar * ActiveVar(int64_t index) const
Returns the active variable of the node corresponding to index.
const std::vector< DisjunctionIndex > & GetDisjunctionIndices(int64_t index) const
Returns the indices of the disjunctions to which an index belongs.
IntVar * NextVar(int64_t index) const
!defined(SWIGPYTHON)
int RegisterStateDependentTransitCallback(VariableIndexEvaluator2 callback)
const std::vector< std::pair< int, int > > & GetDeliveryIndexPairs(int64_t node_index) const
Same as above for deliveries.
void AddToAssignment(IntVar *const var)
Adds an extra variable to the vehicle routing assignment.
const TransitCallback1 & UnaryTransitCallbackOrNull(int callback_index) const
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...
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_t GetHomogeneousCost(int64_t from_index, int64_t to_index) const
Returns the cost of the segment between two nodes supposing all vehicle costs are the same (returns t...
IntVar * VehicleVar(int64_t index) const
Returns the vehicle variable of the node corresponding to index.
LocalDimensionCumulOptimizer * GetMutableLocalCumulOptimizer(const RoutingDimension &dimension) const
int RegisterUnaryTransitVector(std::vector< int64_t > values)
Registers 'callback' and returns its index.
const std::vector< absl::flat_hash_set< int > > & GetSameVehicleRequiredTypeAlternativesOfType(int type) const
Returns the set of same-vehicle requirement alternatives for the given type.
int64_t Size() const
Returns the number of next variables in the model.
RoutingDimension * GetMutableDimension(const std::string &dimension_name) const
Returns a dimension from its name.
int GetVisitType(int64_t index) const
bool HasTemporalTypeRequirements() const
Solver * solver() const
Returns the underlying constraint solver.
static const int64_t kNoPenalty
Constant used to express a hard constraint instead of a soft penalty.
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
bool ApplyLocksToAllVehicles(const std::vector< std::vector< int64_t >> &locks, bool close_routes)
Applies lock chains to all vehicles to the next search, such that locks[p] is the lock chain for rout...
const std::vector< RoutingDimension * > & GetDimensions() const
Returns all dimensions of the model.
SweepArranger * sweep_arranger() const
Returns the sweep arranger to be used by routing heuristics.
std::vector< std::string > GetAllDimensionNames() const
Outputs the names of all dimensions added to the routing engine.
std::pair< int, bool > AddConstantDimensionWithSlack(int64_t value, int64_t capacity, int64_t 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...
const Assignment * SolveFromAssignmentWithParameters(const Assignment *assignment, const RoutingSearchParameters &search_parameters, std::vector< const Assignment * > *solutions=nullptr)
Same as above, except that if assignment is not null, it will be used as the initial solution.
@ 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().
void SetVisitType(int64_t index, int type, VisitTypePolicy type_policy)
int64_t GetDepot() const
Returns the variable index of the first starting or ending node of all routes.
std::vector< RoutingDimension * > GetDimensionsWithSoftOrSpanCosts() const
Returns dimensions with soft or vehicle span costs.
void SetSweepArranger(SweepArranger *sweep_arranger)
void AddTemporalTypeIncompatibility(int type1, int type2)
const std::vector< int > & GetSingleNodesOfType(int type) const
void SetFixedCostOfVehicle(int64_t cost, int vehicle)
Sets the fixed cost of one vehicle route.
std::vector< std::vector< std::pair< int64_t, int64_t > > > 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...
const std::vector< absl::flat_hash_set< int > > & GetRequiredTypeAlternativesWhenAddingType(int type) const
Returns the set of requirement alternatives when adding the given type.
bool ArcIsMoreConstrainedThanArc(int64_t from, int64_t to1, int64_t to2)
Returns whether the arc from->to1 is more constrained than from->to2, taking into account,...
void AddHardTypeIncompatibility(int type1, int type2)
Incompatibilities: Two nodes with "hard" incompatible types cannot share the same route at all,...
void AddPickupAndDelivery(int64_t pickup, int64_t delivery)
Notifies that index1 and index2 form a pair of nodes which should belong to the same route.
int64_t GetArcCostForFirstSolution(int64_t from_index, int64_t to_index) const
Returns the cost of the arc in the context of the first solution strategy.
bool IsVehicleAllowedForIndex(int vehicle, int64_t index)
Returns true if a vehicle is allowed to visit a given node.
int RegisterPositiveUnaryTransitCallback(TransitCallback1 callback)
void SetTabuVarsCallback(GetTabuVarsCallback tabu_var_callback)
IntVar * ApplyLocks(const std::vector< int64_t > &locks)
Applies a lock chain to the next search.
int64_t GetArcCostForVehicle(int64_t from_index, int64_t to_index, int64_t vehicle) const
Returns the cost of the transit arc between two nodes for a given vehicle.
void CloseVisitTypes()
This function should be called once all node visit types have been set and prior to adding any incomp...
DisjunctionIndex AddDisjunction(const std::vector< int64_t > &indices, int64_t penalty=kNoPenalty, int64_t max_cardinality=1)
Adds a disjunction constraint on the indices: exactly 'max_cardinality' of the indices are active.
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)
void AddVariableTargetToFinalizer(IntVar *var, int64_t target)
Add a variable to set the closest possible to the target value in the solution finalizer.
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_t UnperformedPenaltyOrValue(int64_t default_value, int64_t var_index) const
Same as above except that it returns default_value instead of 0 when penalty is not well defined (def...
RoutingDimensionIndex DimensionIndex
Assignment * CompactAndCheckAssignment(const Assignment &assignment) const
Same as CompactAssignment() but also checks the validity of the final compact solution; if it is not ...
LocalDimensionCumulOptimizer * GetMutableLocalCumulMPOptimizer(const RoutingDimension &dimension) const
bool HasHardTypeIncompatibilities() const
Returns true iff any hard (resp.
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< int64_t > > GetRoutesFromAssignment(const Assignment &assignment)
Converts the solution in the given assignment to routes for all vehicles.
int64_t Next(const Assignment &assignment, int64_t index) const
Assignment inspection Returns the variable index of the node directly after the node corresponding to...
void SetAmortizedCostFactorsOfAllVehicles(int64_t linear_cost_factor, int64_t quadratic_cost_factor)
The following methods set the linear and quadratic cost factors of vehicles (must be positive values)...
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.
int64_t Start(int vehicle) const
Model inspection.
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 SetAllowedVehiclesForIndex(const std::vector< int > &vehicles, int64_t index)
Sets the vehicles which can visit a given node.
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...
int64_t UnperformedPenalty(int64_t var_index) const
Get the "unperformed" penalty of a node.
void SetAmortizedCostFactorsOfVehicle(int64_t linear_cost_factor, int64_t quadratic_cost_factor, int vehicle)
Sets the linear and quadratic cost factor of the given vehicle.
int64_t GetNumberOfDecisionsInFirstSolution(const RoutingSearchParameters &search_parameters) const
Returns statistics on first solution search, number of decisions sent to filters, number of decisions...
bool HasTypeRegulations() const
Returns true iff the model has any incompatibilities or requirements set on node types.
RoutingVehicleClassIndex VehicleClassIndex
void AddWeightedVariableMinimizedByFinalizer(IntVar *var, int64_t cost)
Adds a variable to minimize in the solution finalizer, with a weighted priority: the higher the more ...
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...
std::vector< std::pair< int64_t, int64_t > > GetPerfectBinaryDisjunctions() const
Returns the list of all perfect binary disjunctions, as pairs of variable indices: a disjunction is "...
std::function< StateDependentTransit(int64_t, int64_t)> VariableIndexEvaluator2
GlobalDimensionCumulOptimizer * GetMutableGlobalCumulOptimizer(const RoutingDimension &dimension) const
Returns the global/local dimension cumul optimizer for a given dimension, or nullptr if there is none...
bool AddDimensionWithVehicleCapacity(int evaluator_index, int64_t slack_max, std::vector< int64_t > vehicle_capacities, bool fix_start_cumul_to_zero, const std::string &name)
bool HasSameVehicleTypeRequirements() const
Returns true iff any same-route (resp.
IntVar * CostVar() const
Returns the global cost variable which is being minimized.
int64_t GetArcCostForClass(int64_t from_index, int64_t to_index, int64_t cost_class_index) const
Returns the cost of the segment between two nodes for a given cost class.
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.
const std::vector< std::pair< int, int > > & GetPickupIndexPairs(int64_t node_index) const
Returns pairs for which the node is a pickup; the first element of each pair is the index in the pick...
bool IsMatchingModel() const
Returns true if a vehicle/node matching problem is detected.
static RoutingModel::StateDependentTransit MakeStateDependentTransit(const std::function< int64_t(int64_t)> &f, int64_t domain_start, int64_t domain_end)
Creates a cached StateDependentTransit from an std::function.
int RegisterUnaryTransitCallback(TransitCallback1 callback)
int64_t GetNumberOfRejectsInFirstSolution(const RoutingSearchParameters &search_parameters) const
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
bool AddDimensionWithVehicleTransits(const std::vector< int > &evaluator_indices, int64_t slack_max, int64_t capacity, bool fix_start_cumul_to_zero, const std::string &name)
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 AssignmentToRoutes(const Assignment &assignment, std::vector< std::vector< int64_t >> *const routes) const
Converts the solution in the given assignment to routes for all vehicles.
int RegisterTransitMatrix(std::vector< std::vector< int64_t > > values)
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.
CostClassIndex GetCostClassIndexOfVehicle(int64_t vehicle) const
Get the cost class index of the given vehicle.
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 SetFixedCostOfAllVehicles(int64_t cost)
Sets the fixed cost of all vehicle routes.
void SetArcCostEvaluatorOfVehicle(int evaluator_index, int vehicle)
Sets the cost function for a given vehicle route.
bool HasDimension(const std::string &dimension_name) const
Returns true if a dimension exists for a given dimension name.
VisitTypePolicy GetVisitTypePolicy(int64_t index) const
bool IsVehicleUsed(const Assignment &assignment, int vehicle) const
Returns true if the route of 'vehicle' is non empty in 'assignment'.
RoutingModel(const RoutingIndexManager &index_manager)
Constructor taking an index manager.
bool AddDimensionWithVehicleTransitAndCapacity(const std::vector< int > &evaluator_indices, int64_t slack_max, std::vector< int64_t > vehicle_capacities, bool fix_start_cumul_to_zero, const std::string &name)
RoutingDisjunctionIndex DisjunctionIndex
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
bool AddDimension(int evaluator_index, int64_t slack_max, int64_t capacity, bool fix_start_cumul_to_zero, const std::string &name)
Model creation.
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 VisitIntegerArrayArgument(const std::string &arg_name, const std::vector< int64_t > &values) override
void EndVisitConstraint(const std::string &type_name, const Constraint *const constraint) override
void EndVisitModel(const std::string &solver_name) override
~RoutingModelInspector() 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 * MakeSemiContinuousExpr(IntExpr *const expr, int64_t fixed_charge, int64_t step)
Semi continuous Expression (x <= 0 -> f(x) = 0; x > 0 -> f(x) = ax + b) a >= 0 and b >= 0.
Constraint * MakeEquality(IntExpr *const left, IntExpr *const right)
left == right
@ ASSIGN_MIN_VALUE
Selects the min value of the selected variable.
std::function< int64_t(int64_t, int64_t)> IndexEvaluator2
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.
IntExpr * MakeElement(const std::vector< int64_t > &values, IntVar *const index)
values[index]
@ 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
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...
IntExpr * MakeSum(IntExpr *const left, IntExpr *const right)
left + right.
std::function< int64_t(int64_t)> IndexEvaluator1
Callback typedefs.
IntExpr * MakeProd(IntExpr *const left, IntExpr *const right)
left * right
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
std::function< bool(int64_t, int64_t, int64_t)> VariableValueComparator
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_t start, int64_t end)
Adds the interval [start..end] to the list, and merges overlapping or immediately adjacent intervals ...
ConstIterator end() const
Iterator FirstIntervalGreaterOrEqual(int64_t value) const
Returns an iterator to either:
IntervalSet::iterator Iterator
Class to arrange indices by by their distance and their angles from the depot.
TypeIncompatibilityChecker(const RoutingModel &model, bool check_hard_incompatibilities)
virtual bool HasRegulationsToCheck() const =0
virtual bool CheckTypeRegulations(int type, VisitTypePolicy policy, int pos)=0
virtual void OnInitializeCheck()
bool CheckVehicle(int vehicle, const std::function< int64_t(int64_t)> &next_accessor)
TypeRegulationsChecker(const RoutingModel &model)
virtual bool FinalizeCheck() const
void InitializeCheck(int vehicle, const std::function< int64_t(int64_t)> &next_accessor)
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_
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
void STLDeleteElements(T *container)
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)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
Collection of objects used to extend the Constraint Solver library.
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)
int64_t CapAdd(int64_t x, int64_t y)
Demon * MakeDelayedConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
RangeIntToIntFunction * MakeCachedIntToIntFunction(const std::function< int64_t(int64_t)> &f, int64_t domain_start, int64_t domain_end)
std::function< int64_t(int64_t, int64_t)> RoutingTransitCallback2
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
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,...
int64_t CapSub(int64_t x, int64_t y)
IntVarLocalSearchFilter * MakeVehicleAmortizedCostFilter(const RoutingModel &routing_model)
Returns a filter computing vehicle amortized costs.
Demon * MakeConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
void SetAssignmentFromAssignment(Assignment *target_assignment, const std::vector< IntVar * > &target_vars, const Assignment *source_assignment, const std::vector< IntVar * > &source_vars)
NOLINT.
RangeMinMaxIndexFunction * MakeCachedRangeMinMaxIndexFunction(const std::function< int64_t(int64_t)> &f, int64_t domain_start, int64_t domain_end)
IntVarLocalSearchFilter * MakeCPFeasibilityFilter(RoutingModel *routing_model)
Returns a filter checking the current solution using CP propagation.
DecisionBuilder * MakeSetValuesFromTargets(Solver *solver, std::vector< IntVar * > variables, std::vector< int64_t > targets)
A decision builder which tries to assign values to variables as close as possible to target values fi...
FirstSolutionStrategy::Value AutomaticFirstSolutionStrategy(bool has_pickup_deliveries, bool has_node_precedences, bool has_single_vehicle_node)
Returns the best value for the automatic first solution strategy, based on the given model parameters...
uint64_t ThoroughHash(const char *bytes, size_t len)
int64_t One()
This method returns 1.
DimensionSchedulingStatus
IntVarLocalSearchFilter * MakeMaxActiveVehiclesFilter(const RoutingModel &routing_model)
Returns a filter ensuring that max active vehicles constraints are enforced.
int64_t CapProd(int64_t x, int64_t y)
std::function< int64_t(int64_t)> RoutingTransitCallback1
void FillPathEvaluation(const std::vector< int64_t > &path, const RoutingModel::TransitCallback2 &evaluator, std::vector< int64_t > *values)
RoutingSearchParameters DefaultRoutingSearchParameters()
DecisionBuilder * MakeSweepDecisionBuilder(RoutingModel *model, bool check_assignment)
IntVarLocalSearchFilter * MakeVehicleVarFilter(const RoutingModel &routing_model)
Returns a filter checking that vehicle variable domains are respected.
std::string MemoryUsage()
IntVarLocalSearchFilter * MakePickupDeliveryFilter(const RoutingModel &routing_model, const RoutingModel::IndexPairs &pairs, const std::vector< RoutingModel::PickupAndDeliveryPolicy > &vehicle_policies)
Returns a filter enforcing pickup and delivery constraints for the given pair of nodes and given poli...
IntVarLocalSearchFilter * MakeTypeRegulationsFilter(const RoutingModel &routing_model)
Returns a filter ensuring type regulation constraints are enforced.
bool HasUnaryDimension(const std::vector< RoutingDimension * > &dimensions)
static const int kUnassigned
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)
Appends dimension-based filters to the given list of filters using a path state.
IntVarLocalSearchFilter * MakeNodeDisjunctionFilter(const RoutingModel &routing_model)
Returns a filter ensuring that node disjunction constraints are enforced.
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
static int input(yyscan_t yyscanner)
#define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators)
std::vector< int > var_indices
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.
static bool LessThan(const VehicleClass &a, const VehicleClass &b)
Comparator for STL containers and algorithms.
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
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...