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/str_join.h"
46#include "absl/strings/string_view.h"
47#include "absl/time/time.h"
84class ExtendedSwapActiveOperator;
85class LocalSearchPhaseParameters;
86class MakeActiveAndRelocate;
87class MakeActiveOperator;
88class MakeChainInactiveOperator;
89class MakeInactiveOperator;
91class RelocateAndMakeActiveOperator;
92class SwapActiveOperator;
104using ResourceGroup = RoutingModel::ResourceGroup;
109class SetValuesFromTargets :
public DecisionBuilder {
111 SetValuesFromTargets(std::vector<IntVar*> variables,
112 std::vector<int64_t> targets)
113 : variables_(
std::move(variables)),
114 targets_(
std::move(targets)),
116 steps_(variables_.size(), 0) {
117 DCHECK_EQ(variables_.size(), targets_.size());
119 Decision* Next(Solver*
const solver)
override {
120 int index = index_.Value();
121 while (
index < variables_.size() && variables_[
index]->Bound()) {
124 index_.SetValue(solver,
index);
125 if (
index >= variables_.size())
return nullptr;
126 const int64_t variable_min = variables_[
index]->Min();
127 const int64_t variable_max = variables_[
index]->Max();
130 if (targets_[
index] <= variable_min) {
131 return solver->MakeAssignVariableValue(variables_[
index], variable_min);
132 }
else if (targets_[
index] >= variable_max) {
133 return solver->MakeAssignVariableValue(variables_[
index], variable_max);
135 int64_t step = steps_[
index];
140 if (
value < variable_min || variable_max <
value) {
141 step = GetNextStep(step);
152 steps_.SetValue(solver,
index, GetNextStep(step));
153 return solver->MakeAssignVariableValueOrDoNothing(variables_[
index],
159 int64_t GetNextStep(int64_t step)
const {
160 return (step > 0) ? -step :
CapSub(1, step);
162 const std::vector<IntVar*> variables_;
163 const std::vector<int64_t> targets_;
165 RevArray<int64_t> steps_;
171 std::vector<IntVar*> variables,
172 std::vector<int64_t> targets) {
174 new SetValuesFromTargets(std::move(variables), std::move(targets)));
179bool DimensionFixedTransitsEqualTransitEvaluatorForVehicle(
180 const RoutingDimension& dimension,
int vehicle) {
181 const RoutingModel*
const model = dimension.model();
182 int node =
model->Start(vehicle);
183 while (!
model->IsEnd(node)) {
184 if (!
model->NextVar(node)->Bound()) {
187 const int next =
model->NextVar(node)->Value();
188 if (dimension.transit_evaluator(vehicle)(node,
next) !=
189 dimension.FixedTransitVar(node)->Value()) {
197bool DimensionFixedTransitsEqualTransitEvaluators(
198 const RoutingDimension& dimension) {
199 for (
int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
200 if (!DimensionFixedTransitsEqualTransitEvaluatorForVehicle(dimension,
210void ConcatenateRouteCumulAndBreakVarAndValues(
211 const RoutingDimension& dimension,
int vehicle,
212 const std::vector<int64_t>& cumul_values,
213 const std::vector<int64_t>& break_values, std::vector<IntVar*>* variables,
214 std::vector<int64_t>* values) {
215 *values = cumul_values;
217 const RoutingModel&
model = *dimension.model();
219 int current =
model.Start(vehicle);
221 variables->push_back(dimension.CumulVar(current));
222 if (!
model.IsEnd(current)) {
223 current =
model.NextVar(current)->Value();
234 std::swap(variables->at(1), variables->back());
235 std::swap(values->at(1), values->back());
236 if (dimension.HasBreakConstraints()) {
238 dimension.GetBreakIntervalsOfVehicle(vehicle)) {
242 values->insert(values->end(), break_values.begin(), break_values.end());
245 for (
int j = 0; j < values->size(); ++j) {
247 values->at(j) = variables->at(j)->Min();
250 DCHECK_EQ(variables->size(), values->size());
253class SetCumulsFromLocalDimensionCosts :
public DecisionBuilder {
255 SetCumulsFromLocalDimensionCosts(
256 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>*
258 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>*
260 const std::function<
bool(
const RoutingDimension*)>& set_dimension_cumuls,
261 SearchMonitor* monitor,
bool optimize_and_pack =
false)
262 : monitor_(monitor), optimize_and_pack_(optimize_and_pack) {
263 for (
int i = 0; i < local_optimizers->size(); i++) {
264 if (!set_dimension_cumuls(local_optimizers->at(i)->dimension())) {
267 const auto& optimizer = local_optimizers->at(i);
268 local_optimizers_.push_back(optimizer.get());
269 local_mp_optimizers_.push_back(local_mp_optimizers->at(i).get());
271 const RoutingDimension*
const dimension = optimizer->dimension();
272 const std::vector<int>& resource_groups =
273 dimension->model()->GetDimensionResourceGroupIndices(dimension);
274 DCHECK_LE(resource_groups.size(), optimize_and_pack ? 1 : 0);
275 resource_group_index_.push_back(
276 resource_groups.empty() ? -1 : resource_groups[0]);
279 Decision* Next(Solver*
const solver)
override {
283 bool should_fail =
false;
284 for (
int i = 0; i < local_optimizers_.size(); ++i) {
285 LocalDimensionCumulOptimizer*
const local_optimizer =
286 local_optimizers_[i];
287 const RoutingDimension& dimension = *local_optimizer->dimension();
288 RoutingModel*
const model = dimension.model();
290 return model->NextVar(n)->Value();
292 const int rg_index = resource_group_index_[i];
293 const auto compute_cumul_values =
295 LocalDimensionCumulOptimizer* optimizer,
int vehicle,
296 std::vector<int64_t>* cumul_values,
297 std::vector<int64_t>* break_start_end_values) {
298 if (optimize_and_pack_) {
299 const int resource_index =
301 :
model->ResourceVar(vehicle, rg_index)->Value();
302 const Resource*
const resource =
305 : &
model->GetResourceGroup(rg_index)->GetResource(
307 return optimizer->ComputePackedRouteCumuls(
308 vehicle,
next, resource, cumul_values,
309 break_start_end_values);
311 return optimizer->ComputeRouteCumuls(vehicle,
next, cumul_values,
312 break_start_end_values);
315 for (
int vehicle = 0; vehicle <
model->vehicles(); ++vehicle) {
316 solver->TopPeriodicCheck();
318 DCHECK(DimensionFixedTransitsEqualTransitEvaluatorForVehicle(dimension,
320 const bool vehicle_has_break_constraint =
321 dimension.HasBreakConstraints() &&
322 !dimension.GetBreakIntervalsOfVehicle(vehicle).empty();
323 LocalDimensionCumulOptimizer*
const optimizer =
324 vehicle_has_break_constraint ? local_mp_optimizers_[i]
326 DCHECK(optimizer !=
nullptr);
327 std::vector<int64_t> cumul_values;
328 std::vector<int64_t> break_start_end_values;
330 optimizer, vehicle, &cumul_values, &break_start_end_values);
337 cumul_values.clear();
338 break_start_end_values.clear();
339 DCHECK(local_mp_optimizers_[i] !=
nullptr);
340 if (compute_cumul_values(local_mp_optimizers_[i], vehicle,
341 &cumul_values, &break_start_end_values) ==
351 std::vector<IntVar*> cp_variables;
352 std::vector<int64_t> cp_values;
353 ConcatenateRouteCumulAndBreakVarAndValues(
354 dimension, vehicle, cumul_values, break_start_end_values,
355 &cp_variables, &cp_values);
356 if (!solver->SolveAndCommit(
358 std::move(cp_values)),
375 using Resource = RoutingModel::ResourceGroup::Resource;
377 std::vector<LocalDimensionCumulOptimizer*> local_optimizers_;
378 std::vector<LocalDimensionCumulOptimizer*> local_mp_optimizers_;
380 std::vector<int> resource_group_index_;
381 SearchMonitor*
const monitor_;
382 const bool optimize_and_pack_;
385class SetCumulsFromGlobalDimensionCosts :
public DecisionBuilder {
387 SetCumulsFromGlobalDimensionCosts(
388 const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>*
390 const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>*
391 global_mp_optimizers,
392 SearchMonitor* monitor,
bool optimize_and_pack =
false)
393 : monitor_(monitor), optimize_and_pack_(optimize_and_pack) {
394 for (
int i = 0; i < global_optimizers->size(); i++) {
395 global_optimizers_.push_back(global_optimizers->at(i).get());
396 global_mp_optimizers_.push_back(global_mp_optimizers->at(i).get());
399 Decision* Next(Solver*
const solver)
override {
403 bool should_fail =
false;
404 for (
int i = 0; i < global_optimizers_.size(); ++i) {
405 GlobalDimensionCumulOptimizer*
const global_optimizer =
406 global_optimizers_[i];
407 const RoutingDimension* dimension = global_optimizer->dimension();
408 RoutingModel*
const model = dimension->model();
411 return model->NextVar(n)->Value();
414 DCHECK(DimensionFixedTransitsEqualTransitEvaluators(*dimension));
416 std::vector<int64_t> cumul_values;
417 std::vector<int64_t> break_start_end_values;
418 std::vector<std::vector<int>> resource_indices_per_group;
420 !
model->GetDimensionResourceGroupIndices(dimension).empty()
423 ? global_optimizer->ComputePackedCumuls(
424 next, &cumul_values, &break_start_end_values,
425 &resource_indices_per_group)
426 : global_optimizer->ComputeCumuls(
next, &cumul_values,
427 &break_start_end_values,
428 &resource_indices_per_group);
436 cumul_values.clear();
437 break_start_end_values.clear();
438 DCHECK(global_mp_optimizers_[i] !=
nullptr);
441 ? global_mp_optimizers_[i]->ComputePackedCumuls(
442 next, &cumul_values, &break_start_end_values,
443 &resource_indices_per_group)
444 : global_mp_optimizers_[i]->ComputeCumuls(
445 next, &cumul_values, &break_start_end_values,
446 &resource_indices_per_group);
456 std::vector<IntVar*> cp_variables = dimension->cumuls();
457 std::vector<int64_t> cp_values;
459 if (dimension->HasBreakConstraints()) {
460 const int num_vehicles =
model->vehicles();
461 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
463 dimension->GetBreakIntervalsOfVehicle(vehicle)) {
464 cp_variables.push_back(
interval->SafeStartExpr(0)->Var());
465 cp_variables.push_back(
interval->SafeEndExpr(0)->Var());
468 cp_values.insert(cp_values.end(), break_start_end_values.begin(),
469 break_start_end_values.end());
471 for (
int rg_index :
model->GetDimensionResourceGroupIndices(dimension)) {
472 const std::vector<int>& resource_values =
473 resource_indices_per_group[rg_index];
474 DCHECK(!resource_values.empty());
475 cp_values.insert(cp_values.end(), resource_values.begin(),
476 resource_values.end());
477 const std::vector<IntVar*>& resource_vars =
478 model->ResourceVars(rg_index);
479 DCHECK_EQ(resource_vars.size(), resource_values.size());
480 cp_variables.insert(cp_variables.end(), resource_vars.begin(),
481 resource_vars.end());
484 for (
int j = 0; j < cp_values.size(); ++j) {
486 cp_values[j] = cp_variables[j]->Min();
489 if (!solver->SolveAndCommit(
491 std::move(cp_values)),
504 std::vector<GlobalDimensionCumulOptimizer*> global_optimizers_;
505 std::vector<GlobalDimensionCumulOptimizer*> global_mp_optimizers_;
506 SearchMonitor*
const monitor_;
507 const bool optimize_and_pack_;
510class SetCumulsFromResourceAssignmentCosts :
public DecisionBuilder {
512 SetCumulsFromResourceAssignmentCosts(
513 LocalDimensionCumulOptimizer* optimizer,
514 LocalDimensionCumulOptimizer* mp_optimizer, SearchMonitor* monitor)
515 : model_(*optimizer->dimension()->
model()),
516 dimension_(*optimizer->dimension()),
517 rg_index_(model_.GetDimensionResourceGroupIndex(&dimension_)),
518 resource_group_(*model_.GetResourceGroup(rg_index_)),
519 resource_assignment_optimizer_(&resource_group_, optimizer,
523 Decision* Next(Solver*
const solver)
override {
524 bool should_fail =
false;
526 const int num_vehicles = model_.vehicles();
527 std::vector<std::vector<int64_t>> assignment_costs(num_vehicles);
528 std::vector<std::vector<std::vector<int64_t>>> cumul_values(num_vehicles);
529 std::vector<std::vector<std::vector<int64_t>>> break_values(num_vehicles);
531 const auto next = [&
model = model_](int64_t n) {
532 return model.NextVar(n)->Value();
534 DCHECK(DimensionFixedTransitsEqualTransitEvaluators(dimension_));
536 for (
int v : resource_group_.GetVehiclesRequiringAResource()) {
537 if (!resource_assignment_optimizer_.ComputeAssignmentCostsForVehicle(
538 v,
next,
true, &assignment_costs[v],
539 &cumul_values[v], &break_values[v])) {
545 std::vector<int> resource_indices;
546 should_fail = should_fail ||
547 resource_assignment_optimizer_.ComputeBestAssignmentCost(
548 assignment_costs, assignment_costs,
549 [](
int) {
return true; }, &resource_indices) < 0;
552 DCHECK_EQ(resource_indices.size(), num_vehicles);
553 const int num_resources = resource_group_.Size();
554 for (
int v : resource_group_.GetVehiclesRequiringAResource()) {
555 if (
next(model_.Start(v)) == model_.End(v) &&
556 !model_.IsVehicleUsedWhenEmpty(v)) {
559 const int resource_index = resource_indices[v];
561 DCHECK_EQ(cumul_values[v].size(), num_resources);
562 DCHECK_EQ(break_values[v].size(), num_resources);
563 const std::vector<int64_t>& optimal_cumul_values =
564 cumul_values[v][resource_index];
565 const std::vector<int64_t>& optimal_break_values =
566 break_values[v][resource_index];
567 std::vector<IntVar*> cp_variables;
568 std::vector<int64_t> cp_values;
569 ConcatenateRouteCumulAndBreakVarAndValues(
570 dimension_, v, optimal_cumul_values, optimal_break_values,
571 &cp_variables, &cp_values);
573 const std::vector<IntVar*>& resource_vars =
574 model_.ResourceVars(rg_index_);
575 DCHECK_EQ(resource_vars.size(), resource_indices.size());
576 cp_variables.insert(cp_variables.end(), resource_vars.begin(),
577 resource_vars.end());
578 cp_values.insert(cp_values.end(), resource_indices.begin(),
579 resource_indices.end());
580 if (!solver->SolveAndCommit(
582 std::move(cp_values)),
597 const RoutingModel& model_;
598 const RoutingDimension& dimension_;
600 const ResourceGroup& resource_group_;
601 ResourceAssignmentOptimizer resource_assignment_optimizer_;
602 SearchMonitor*
const monitor_;
608 const Assignment* original_assignment, absl::Duration duration_limit) {
610 if (original_assignment ==
nullptr)
return nullptr;
611 if (duration_limit <= absl::ZeroDuration())
return original_assignment;
612 if (global_dimension_optimizers_.empty() &&
613 local_dimension_optimizers_.empty()) {
614 DCHECK(local_dimension_mp_optimizers_.empty());
615 return original_assignment;
624 Assignment* packed_assignment = solver_->MakeAssignment();
628 const std::vector<int>& resource_groups =
630 if (resource_groups.size() == 1) {
632 packed_assignment->
Add(resource_vars_[resource_groups[0]]);
637 std::vector<DecisionBuilder*> decision_builders;
638 decision_builders.push_back(solver_->MakeRestoreAssignment(preassignment_));
639 decision_builders.push_back(
640 solver_->MakeRestoreAssignment(packed_assignment));
641 decision_builders.push_back(
642 solver_->RevAlloc(
new SetCumulsFromLocalDimensionCosts(
643 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
648 GetOrCreateLargeNeighborhoodSearchLimit(),
650 decision_builders.push_back(
651 solver_->RevAlloc(
new SetCumulsFromGlobalDimensionCosts(
652 &global_dimension_optimizers_, &global_dimension_mp_optimizers_,
653 GetOrCreateLargeNeighborhoodSearchLimit(),
655 decision_builders.push_back(
656 CreateFinalizerForMinimizedAndMaximizedVariables());
659 solver_->Compose(decision_builders);
660 solver_->Solve(restore_pack_and_finalize,
661 packed_dimensions_assignment_collector_, limit);
663 if (packed_dimensions_assignment_collector_->
solution_count() != 1) {
664 LOG(
ERROR) <<
"The given assignment is not valid for this model, or cannot "
669 packed_assignment->
Copy(original_assignment);
671 packed_dimensions_assignment_collector_->
solution(0));
673 return packed_assignment;
681 return sweep_arranger_.get();
686class DifferentFromValues :
public Constraint {
688 DifferentFromValues(
Solver* solver,
IntVar*
var, std::vector<int64_t> values)
690 void Post()
override {}
691 void InitialPropagate()
override { var_->RemoveValues(values_); }
692 std::string DebugString()
const override {
return "DifferentFromValues"; }
693 void Accept(ModelVisitor*
const visitor)
const override {
703 const std::vector<int64_t> values_;
718class LightFunctionElementConstraint :
public Constraint {
720 LightFunctionElementConstraint(Solver*
const solver, IntVar*
const var,
721 IntVar*
const index, F values,
722 std::function<
bool()> deep_serialize)
723 : Constraint(solver),
726 values_(
std::move(values)),
727 deep_serialize_(
std::move(deep_serialize)) {}
728 ~LightFunctionElementConstraint()
override {}
730 void Post()
override {
732 solver(),
this, &LightFunctionElementConstraint::IndexBound,
734 index_->WhenBound(demon);
737 void InitialPropagate()
override {
738 if (index_->Bound()) {
743 std::string DebugString()
const override {
744 return "LightFunctionElementConstraint";
747 void Accept(ModelVisitor*
const visitor)
const override {
754 if (deep_serialize_()) {
755 visitor->VisitInt64ToInt64Extension(values_, index_->Min(),
762 void IndexBound() { var_->SetValue(values_(index_->Min())); }
765 IntVar*
const index_;
767 std::function<bool()> deep_serialize_;
771Constraint* MakeLightElement(Solver*
const solver, IntVar*
const var,
772 IntVar*
const index, F values,
773 std::function<
bool()> deep_serialize) {
774 return solver->RevAlloc(
new LightFunctionElementConstraint<F>(
775 solver,
var,
index, std::move(values), std::move(deep_serialize)));
784class LightFunctionElement2Constraint :
public Constraint {
786 LightFunctionElement2Constraint(Solver*
const solver, IntVar*
const var,
787 IntVar*
const index1, IntVar*
const index2,
789 std::function<
bool()> deep_serialize)
790 : Constraint(solver),
794 values_(
std::move(values)),
795 deep_serialize_(
std::move(deep_serialize)) {}
796 ~LightFunctionElement2Constraint()
override {}
797 void Post()
override {
799 solver(),
this, &LightFunctionElement2Constraint::IndexBound,
801 index1_->WhenBound(demon);
802 index2_->WhenBound(demon);
804 void InitialPropagate()
override { IndexBound(); }
806 std::string DebugString()
const override {
807 return "LightFunctionElement2Constraint";
810 void Accept(ModelVisitor*
const visitor)
const override {
819 const int64_t index1_min = index1_->Min();
820 const int64_t index1_max = index1_->Max();
823 if (deep_serialize_()) {
824 for (
int i = index1_min; i <= index1_max; ++i) {
825 visitor->VisitInt64ToInt64Extension(
826 [
this, i](int64_t j) {
return values_(i, j); }, index2_->Min(),
835 if (index1_->Bound() && index2_->Bound()) {
836 var_->SetValue(values_(index1_->Min(), index2_->Min()));
841 IntVar*
const index1_;
842 IntVar*
const index2_;
844 std::function<bool()> deep_serialize_;
848Constraint* MakeLightElement2(Solver*
const solver, IntVar*
const var,
849 IntVar*
const index1, IntVar*
const index2,
850 F values, std::function<
bool()> deep_serialize) {
851 return solver->RevAlloc(
new LightFunctionElement2Constraint<F>(
852 solver,
var, index1, index2, std::move(values),
853 std::move(deep_serialize)));
857template <
class A,
class B>
858static int64_t ReturnZero(A
a, B
b) {
864 for (
int i = 0; i < size1; i++) {
865 for (
int j = 0; j < size2; j++) {
890 : nodes_(index_manager.num_nodes()),
891 vehicles_(index_manager.num_vehicles()),
892 max_active_vehicles_(vehicles_),
893 fixed_cost_of_vehicle_(vehicles_, 0),
895 linear_cost_factor_of_vehicle_(vehicles_, 0),
896 quadratic_cost_factor_of_vehicle_(vehicles_, 0),
897 vehicle_amortized_cost_factors_set_(false),
898 vehicle_used_when_empty_(vehicles_, false),
900 costs_are_homogeneous_across_vehicles_(
902 cache_callbacks_(false),
904 vehicle_pickup_delivery_policy_(vehicles_, PICKUP_AND_DELIVERY_NO_ORDER),
905 has_hard_type_incompatibilities_(false),
906 has_temporal_type_incompatibilities_(false),
907 has_same_vehicle_type_requirements_(false),
908 has_temporal_type_requirements_(false),
912 manager_(index_manager) {
914 vehicle_to_transit_cost_.assign(
918 cache_callbacks_ = (nodes_ <=
parameters.max_callback_cache_size());
924 solver_ = absl::make_unique<Solver>(
"Routing", solver_parameters);
929 const int64_t size =
Size();
930 index_to_pickup_index_pairs_.resize(size);
931 index_to_delivery_index_pairs_.resize(size);
933 index_to_type_policy_.resize(index_manager.
num_indices());
936 for (
int v = 0; v < index_manager.
num_vehicles(); ++v) {
938 index_to_vehicle_[starts_[v]] = v;
940 index_to_vehicle_[ends_[v]] = v;
943 const std::vector<RoutingIndexManager::NodeIndex>& index_to_node =
945 index_to_equivalence_class_.resize(index_manager.
num_indices());
946 for (
int i = 0; i < index_to_node.size(); ++i) {
947 index_to_equivalence_class_[i] = index_to_node[i].value();
949 allowed_vehicles_.resize(
Size() + vehicles_);
952void RoutingModel::Initialize() {
953 const int size =
Size();
955 solver_->MakeIntVarArray(size, 0, size + vehicles_ - 1,
"Nexts", &nexts_);
956 solver_->AddConstraint(solver_->MakeAllDifferent(nexts_,
false));
957 index_to_disjunctions_.resize(size + vehicles_);
960 solver_->MakeIntVarArray(size + vehicles_, -1, vehicles_ - 1,
"Vehicles",
963 solver_->MakeBoolVarArray(size,
"Active", &active_);
965 solver_->MakeBoolVarArray(vehicles_,
"ActiveVehicle", &vehicle_active_);
967 solver_->MakeBoolVarArray(vehicles_,
"VehicleCostsConsidered",
968 &vehicle_route_considered_);
970 solver_->MakeBoolVarArray(size + vehicles_,
"IsBoundToEnd",
975 preassignment_ = solver_->MakeAssignment();
982 absl::flat_hash_set<RangeIntToIntFunction*> value_functions_delete;
983 absl::flat_hash_set<RangeMinMaxIndexFunction*> index_functions_delete;
984 for (
const auto& cache_line : state_dependent_transit_evaluators_cache_) {
985 for (
const auto& key_transit : *cache_line) {
986 value_functions_delete.insert(key_transit.second.transit);
987 index_functions_delete.insert(key_transit.second.transit_plus_identity);
998 return model->RegisterPositiveTransitCallback(std::move(
callback));
1004 RoutingModel*
model) {
1006 return model->RegisterPositiveUnaryTransitCallback(std::move(
callback));
1008 return model->RegisterUnaryTransitCallback(std::move(
callback));
1013 return RegisterUnaryCallback(
1014 [
this, values = std::move(values)](int64_t i) {
1018 std::all_of(std::cbegin(values), std::cend(values),
1019 [](int64_t transit) {
return transit >= 0; }),
1024 const int index = unary_transit_evaluators_.size();
1025 unary_transit_evaluators_.push_back(std::move(
callback));
1027 return unary_transit_evaluators_[
index](i);
1032 std::vector<std::vector<int64_t> > values) {
1033 bool all_transits_positive =
true;
1034 for (
const std::vector<int64_t>& transit_values : values) {
1035 all_transits_positive =
1036 std::all_of(std::cbegin(transit_values), std::cend(transit_values),
1037 [](int64_t transit) {
return transit >= 0; });
1038 if (!all_transits_positive) {
1042 return RegisterCallback(
1043 [
this, values = std::move(values)](int64_t i, int64_t j) {
1047 all_transits_positive,
this);
1052 is_transit_evaluator_positive_.push_back(
true);
1053 DCHECK(TransitCallbackPositive(
1059 if (cache_callbacks_) {
1061 std::vector<int64_t> cache(size * size, 0);
1062 for (
int i = 0; i < size; ++i) {
1063 for (
int j = 0; j < size; ++j) {
1064 cache[i * size + j] =
callback(i, j);
1067 transit_evaluators_.push_back(
1068 [cache, size](int64_t i, int64_t j) {
return cache[i * size + j]; });
1070 transit_evaluators_.push_back(std::move(
callback));
1072 if (transit_evaluators_.size() != unary_transit_evaluators_.size()) {
1073 DCHECK_EQ(transit_evaluators_.size(), unary_transit_evaluators_.size() + 1);
1074 unary_transit_evaluators_.push_back(
nullptr);
1076 if (transit_evaluators_.size() != is_transit_evaluator_positive_.size()) {
1078 is_transit_evaluator_positive_.size() + 1);
1079 is_transit_evaluator_positive_.push_back(
false);
1081 return transit_evaluators_.size() - 1;
1085 is_transit_evaluator_positive_.push_back(
true);
1093 state_dependent_transit_evaluators_cache_.push_back(
1094 absl::make_unique<StateDependentTransitCallbackCache>());
1095 StateDependentTransitCallbackCache*
const cache =
1096 state_dependent_transit_evaluators_cache_.back().get();
1097 state_dependent_transit_evaluators_.push_back(
1098 [cache,
callback](int64_t i, int64_t j) {
1102 cache->insert({CacheKey(i, j),
value});
1105 return state_dependent_transit_evaluators_.size() - 1;
1108void RoutingModel::AddNoCycleConstraintInternal() {
1109 if (no_cycle_constraint_ ==
nullptr) {
1110 no_cycle_constraint_ = solver_->MakeNoCycle(nexts_, active_);
1111 solver_->AddConstraint(no_cycle_constraint_);
1116 int64_t
capacity,
bool fix_start_cumul_to_zero,
1117 const std::string&
name) {
1118 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
1119 std::vector<int64_t> capacities(vehicles_,
capacity);
1120 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
1121 std::move(capacities),
1122 fix_start_cumul_to_zero,
name);
1126 const std::vector<int>& evaluator_indices, int64_t slack_max,
1127 int64_t
capacity,
bool fix_start_cumul_to_zero,
const std::string&
name) {
1128 std::vector<int64_t> capacities(vehicles_,
capacity);
1129 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
1130 std::move(capacities),
1131 fix_start_cumul_to_zero,
name);
1135 int evaluator_index, int64_t slack_max,
1136 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
1137 const std::string&
name) {
1138 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
1139 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
1140 std::move(vehicle_capacities),
1141 fix_start_cumul_to_zero,
name);
1145 const std::vector<int>& evaluator_indices, int64_t slack_max,
1146 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
1147 const std::string&
name) {
1148 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
1149 std::move(vehicle_capacities),
1150 fix_start_cumul_to_zero,
name);
1153bool RoutingModel::AddDimensionWithCapacityInternal(
1154 const std::vector<int>& evaluator_indices, int64_t slack_max,
1155 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
1156 const std::string&
name) {
1157 CHECK_EQ(vehicles_, vehicle_capacities.size());
1158 return InitializeDimensionInternal(
1159 evaluator_indices, std::vector<int>(), slack_max, fix_start_cumul_to_zero,
1163bool RoutingModel::InitializeDimensionInternal(
1164 const std::vector<int>& evaluator_indices,
1165 const std::vector<int>& state_dependent_evaluator_indices,
1166 int64_t slack_max,
bool fix_start_cumul_to_zero,
1167 RoutingDimension* dimension) {
1168 CHECK(dimension !=
nullptr);
1169 CHECK_EQ(vehicles_, evaluator_indices.size());
1170 CHECK((dimension->base_dimension_ ==
nullptr &&
1171 state_dependent_evaluator_indices.empty()) ||
1172 vehicles_ == state_dependent_evaluator_indices.size());
1175 dimension_name_to_index_[dimension->name()] = dimension_index;
1176 dimensions_.push_back(dimension);
1177 dimension->Initialize(evaluator_indices, state_dependent_evaluator_indices,
1179 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
1180 nexts_, active_, dimension->cumuls(), dimension->transits()));
1181 if (fix_start_cumul_to_zero) {
1182 for (
int i = 0; i < vehicles_; ++i) {
1183 IntVar*
const start_cumul = dimension->CumulVar(
Start(i));
1185 start_cumul->SetValue(0);
1196 bool fix_start_cumul_to_zero,
const std::string& dimension_name) {
1197 const int evaluator_index =
1198 RegisterUnaryCallback([
value](int64_t) {
return value; },
1200 return std::make_pair(evaluator_index,
1202 fix_start_cumul_to_zero, dimension_name));
1206 std::vector<int64_t> values, int64_t
capacity,
bool fix_start_cumul_to_zero,
1207 const std::string& dimension_name) {
1209 return std::make_pair(evaluator_index,
1211 fix_start_cumul_to_zero, dimension_name));
1215 std::vector<std::vector<int64_t>> values, int64_t
capacity,
1216 bool fix_start_cumul_to_zero,
const std::string& dimension_name) {
1218 return std::make_pair(evaluator_index,
1220 fix_start_cumul_to_zero, dimension_name));
1232 CHECK(callback_ !=
nullptr);
1236 int64_t Min()
const override {
1238 const int idx_min = index_->Min();
1239 const int idx_max = index_->Max() + 1;
1240 return (idx_min < idx_max) ? callback_->RangeMin(idx_min, idx_max)
1243 void SetMin(int64_t new_min)
override {
1244 const int64_t old_min = Min();
1245 const int64_t old_max = Max();
1246 if (old_min < new_min && new_min <= old_max) {
1247 const int64_t old_idx_min = index_->Min();
1248 const int64_t old_idx_max = index_->Max() + 1;
1249 if (old_idx_min < old_idx_max) {
1250 const int64_t new_idx_min = callback_->RangeFirstInsideInterval(
1251 old_idx_min, old_idx_max, new_min, old_max + 1);
1252 index_->SetMin(new_idx_min);
1253 if (new_idx_min < old_idx_max) {
1254 const int64_t new_idx_max = callback_->RangeLastInsideInterval(
1255 new_idx_min, old_idx_max, new_min, old_max + 1);
1256 index_->SetMax(new_idx_max);
1261 int64_t Max()
const override {
1263 const int idx_min = index_->Min();
1264 const int idx_max = index_->Max() + 1;
1265 return (idx_min < idx_max) ? callback_->RangeMax(idx_min, idx_max)
1268 void SetMax(int64_t new_max)
override {
1269 const int64_t old_min = Min();
1270 const int64_t old_max = Max();
1271 if (old_min <= new_max && new_max < old_max) {
1272 const int64_t old_idx_min = index_->Min();
1273 const int64_t old_idx_max = index_->Max() + 1;
1274 if (old_idx_min < old_idx_max) {
1275 const int64_t new_idx_min = callback_->RangeFirstInsideInterval(
1276 old_idx_min, old_idx_max, old_min, new_max + 1);
1277 index_->SetMin(new_idx_min);
1278 if (new_idx_min < old_idx_max) {
1279 const int64_t new_idx_max = callback_->RangeLastInsideInterval(
1280 new_idx_min, old_idx_max, old_min, new_max + 1);
1281 index_->SetMax(new_idx_max);
1286 void WhenRange(Demon* d)
override { index_->WhenRange(d); }
1289 const RangeIntToIntFunction*
const callback_;
1290 IntVar*
const index_;
1293IntExpr* MakeRangeMakeElementExpr(
const RangeIntToIntFunction*
callback,
1294 IntVar*
index, Solver* s) {
1295 return s->RegisterIntExpr(
1301 const std::vector<int>& dependent_transits,
1303 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
1304 const std::string&
name) {
1305 const std::vector<int> pure_transits(vehicles_, 0);
1307 pure_transits, dependent_transits, base_dimension, slack_max,
1308 std::move(vehicle_capacities), fix_start_cumul_to_zero,
name);
1313 int64_t vehicle_capacity,
bool fix_start_cumul_to_zero,
1314 const std::string&
name) {
1316 0, transit, dimension, slack_max, vehicle_capacity,
1317 fix_start_cumul_to_zero,
name);
1320bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacityInternal(
1321 const std::vector<int>& pure_transits,
1322 const std::vector<int>& dependent_transits,
1324 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
1325 const std::string&
name) {
1326 CHECK_EQ(vehicles_, vehicle_capacities.size());
1328 if (base_dimension ==
nullptr) {
1330 name, RoutingDimension::SelfBased());
1333 name, base_dimension);
1335 return InitializeDimensionInternal(pure_transits, dependent_transits,
1336 slack_max, fix_start_cumul_to_zero,
1341 int pure_transit,
int dependent_transit,
1343 int64_t vehicle_capacity,
bool fix_start_cumul_to_zero,
1344 const std::string&
name) {
1345 std::vector<int> pure_transits(vehicles_, pure_transit);
1346 std::vector<int> dependent_transits(vehicles_, dependent_transit);
1347 std::vector<int64_t> vehicle_capacities(vehicles_, vehicle_capacity);
1348 return AddDimensionDependentDimensionWithVehicleCapacityInternal(
1349 pure_transits, dependent_transits, base_dimension, slack_max,
1350 std::move(vehicle_capacities), fix_start_cumul_to_zero,
name);
1354 const std::function<int64_t(int64_t)>& f, int64_t domain_start,
1355 int64_t domain_end) {
1356 const std::function<int64_t(int64_t)> g = [&f](int64_t x) {
1366 std::vector<std::string> dimension_names;
1367 for (
const auto& dimension_name_index : dimension_name_to_index_) {
1368 dimension_names.push_back(dimension_name_index.first);
1370 std::sort(dimension_names.begin(), dimension_names.end());
1371 return dimension_names;
1377 if (dim_index < 0 || dim_index >= global_optimizer_index_.size() ||
1378 global_optimizer_index_[dim_index] < 0) {
1381 const int optimizer_index = global_optimizer_index_[dim_index];
1382 DCHECK_LT(optimizer_index, global_dimension_optimizers_.size());
1383 return global_dimension_optimizers_[optimizer_index].get();
1389 if (dim_index < 0 || dim_index >= global_optimizer_index_.size() ||
1390 global_optimizer_index_[dim_index] < 0) {
1393 const int optimizer_index = global_optimizer_index_[dim_index];
1394 DCHECK_LT(optimizer_index, global_dimension_mp_optimizers_.size());
1395 return global_dimension_mp_optimizers_[optimizer_index].get();
1401 if (dim_index < 0 || dim_index >= local_optimizer_index_.size() ||
1402 local_optimizer_index_[dim_index] < 0) {
1405 const int optimizer_index = local_optimizer_index_[dim_index];
1406 DCHECK_LT(optimizer_index, local_dimension_optimizers_.size());
1407 return local_dimension_optimizers_[optimizer_index].get();
1413 if (dim_index < 0 || dim_index >= local_optimizer_index_.size() ||
1414 local_optimizer_index_[dim_index] < 0) {
1417 const int optimizer_index = local_optimizer_index_[dim_index];
1418 DCHECK_LT(optimizer_index, local_dimension_mp_optimizers_.size());
1419 return local_dimension_mp_optimizers_[optimizer_index].get();
1427 const std::string& dimension_name)
const {
1433 const std::string& dimension_name)
const {
1434 return *dimensions_[
gtl::FindOrDie(dimension_name_to_index_, dimension_name)];
1438 const std::string& dimension_name)
const {
1441 return dimensions_[
index];
1447ResourceGroup::Attributes::Attributes()
1448 : start_domain_(
Domain::AllValues()), end_domain_(
Domain::AllValues()) {
1454 : start_domain_(
std::move(start_domain)),
1455 end_domain_(
std::move(end_domain)) {}
1463 GetDefaultAttributes());
1466void ResourceGroup::Resource::SetDimensionAttributes(
1468 DCHECK(dimension_attributes_.empty())
1469 <<
"As of 2021/07, each resource can only constrain a single dimension.";
1472 model_->GetDimensionIndex(dimension->
name());
1474 DCHECK(!dimension_attributes_.contains(dimension_index));
1475 dimension_attributes_[dimension_index] = std::move(attributes);
1480 static const Attributes*
const kAttributes =
new Attributes();
1481 return *kAttributes;
1485 resource_groups_.push_back(absl::make_unique<ResourceGroup>(
this));
1486 return resource_groups_.size() - 1;
1491 resources_.push_back(
Resource(model_));
1492 resources_.back().SetDimensionAttributes(std::move(attributes), dimension);
1495 model_->GetDimensionIndex(dimension->
name());
1497 affected_dimension_indices_.insert(dimension_index);
1499 DCHECK_EQ(affected_dimension_indices_.size(), 1)
1500 <<
"As of 2021/07, each ResourceGroup can only affect a single "
1501 "RoutingDimension at a time.";
1503 return resources_.size() - 1;
1507 DCHECK_LT(vehicle, vehicle_requires_resource_.size());
1508 if (vehicle_requires_resource_[vehicle])
return;
1509 vehicle_requires_resource_[vehicle] =
true;
1510 vehicles_requiring_resource_.push_back(vehicle);
1518 return dimension_resource_group_indices_[dim];
1523 for (
int i = 0; i < vehicles_; ++i) {
1531 CHECK_LT(evaluator_index, transit_evaluators_.size());
1532 vehicle_to_transit_cost_[vehicle] = evaluator_index;
1536 for (
int i = 0; i < vehicles_; ++i) {
1543 return fixed_cost_of_vehicle_[vehicle];
1549 fixed_cost_of_vehicle_[vehicle] =
cost;
1553 int64_t linear_cost_factor, int64_t quadratic_cost_factor) {
1554 for (
int v = 0; v < vehicles_; v++) {
1561 int64_t linear_cost_factor, int64_t quadratic_cost_factor,
int vehicle) {
1565 if (linear_cost_factor + quadratic_cost_factor > 0) {
1566 vehicle_amortized_cost_factors_set_ =
true;
1568 linear_cost_factor_of_vehicle_[vehicle] = linear_cost_factor;
1569 quadratic_cost_factor_of_vehicle_[vehicle] = quadratic_cost_factor;
1575struct CostClassComparator {
1582struct VehicleClassComparator {
1583 bool operator()(
const RoutingModel::VehicleClass&
a,
1584 const RoutingModel::VehicleClass&
b)
const {
1594void RoutingModel::ComputeCostClasses(
1597 cost_classes_.reserve(vehicles_);
1598 cost_classes_.clear();
1599 cost_class_index_of_vehicle_.assign(vehicles_,
CostClassIndex(-1));
1600 std::map<CostClass, CostClassIndex, CostClassComparator> cost_class_map;
1603 const CostClass zero_cost_class(0);
1604 cost_classes_.push_back(zero_cost_class);
1605 DCHECK_EQ(cost_classes_[kCostClassIndexOfZeroCost].evaluator_index, 0);
1606 cost_class_map[zero_cost_class] = kCostClassIndexOfZeroCost;
1611 has_vehicle_with_zero_cost_class_ =
false;
1612 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
1613 CostClass cost_class(vehicle_to_transit_cost_[vehicle]);
1617 const int64_t coeff =
1619 if (coeff == 0)
continue;
1620 cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1623 std::sort(cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1625 cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1631 if (cost_class_index == kCostClassIndexOfZeroCost) {
1632 has_vehicle_with_zero_cost_class_ =
true;
1633 }
else if (cost_class_index == num_cost_classes) {
1634 cost_classes_.push_back(cost_class);
1636 cost_class_index_of_vehicle_[vehicle] = cost_class_index;
1650 costs_are_homogeneous_across_vehicles_ &= has_vehicle_with_zero_cost_class_
1657 return std::tie(
a.cost_class_index,
a.fixed_cost,
a.used_when_empty,
1658 a.start_equivalence_class,
a.end_equivalence_class,
1659 a.unvisitable_nodes_fprint,
a.dimension_start_cumuls_min,
1660 a.dimension_start_cumuls_max,
a.dimension_end_cumuls_min,
1661 a.dimension_end_cumuls_max,
a.dimension_capacities,
1662 a.dimension_evaluator_classes,
1663 a.required_resource_group_indices) <
1664 std::tie(
b.cost_class_index,
b.fixed_cost,
b.used_when_empty,
1665 b.start_equivalence_class,
b.end_equivalence_class,
1666 b.unvisitable_nodes_fprint,
b.dimension_start_cumuls_min,
1667 b.dimension_start_cumuls_max,
b.dimension_end_cumuls_min,
1668 b.dimension_end_cumuls_max,
b.dimension_capacities,
1669 b.dimension_evaluator_classes,
1670 b.required_resource_group_indices);
1673void RoutingModel::ComputeVehicleClasses() {
1674 vehicle_classes_.reserve(vehicles_);
1675 vehicle_classes_.clear();
1677 std::map<VehicleClass, VehicleClassIndex, VehicleClassComparator>
1679 const int nodes_unvisitability_num_bytes = (vehicle_vars_.size() + 7) / 8;
1680 std::unique_ptr<char[]> nodes_unvisitability_bitmask(
1681 new char[nodes_unvisitability_num_bytes]);
1682 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
1684 vehicle_class.cost_class_index = cost_class_index_of_vehicle_[vehicle];
1686 vehicle_class.used_when_empty = vehicle_used_when_empty_[vehicle];
1688 index_to_equivalence_class_[
Start(vehicle)];
1690 index_to_equivalence_class_[
End(vehicle)];
1694 start_cumul_var->
Min());
1696 start_cumul_var->
Max());
1705 memset(nodes_unvisitability_bitmask.get(), 0,
1706 nodes_unvisitability_num_bytes);
1710 (!vehicle_var->
Contains(vehicle) ||
1712 nodes_unvisitability_bitmask[
index / CHAR_BIT] |= 1U
1713 << (
index % CHAR_BIT);
1717 nodes_unvisitability_bitmask.get(), nodes_unvisitability_num_bytes);
1718 for (
int rg_index = 0; rg_index < resource_groups_.size(); rg_index++) {
1720 vehicle_class.required_resource_group_indices.push_back(rg_index);
1727 if (vehicle_class_index == num_vehicle_classes) {
1730 vehicle_class_index_of_vehicle_[vehicle] = vehicle_class_index;
1734void RoutingModel::ComputeVehicleTypes() {
1735 const int nodes_squared = nodes_ * nodes_;
1736 std::vector<int>& type_index_of_vehicle =
1737 vehicle_type_container_.type_index_of_vehicle;
1738 std::vector<std::set<VehicleTypeContainer::VehicleClassEntry>>&
1739 sorted_vehicle_classes_per_type =
1740 vehicle_type_container_.sorted_vehicle_classes_per_type;
1741 std::vector<std::deque<int>>& vehicles_per_vehicle_class =
1742 vehicle_type_container_.vehicles_per_vehicle_class;
1744 type_index_of_vehicle.resize(vehicles_);
1745 sorted_vehicle_classes_per_type.clear();
1746 sorted_vehicle_classes_per_type.reserve(vehicles_);
1747 vehicles_per_vehicle_class.clear();
1750 absl::flat_hash_map<int64_t, int> type_to_type_index;
1752 for (
int v = 0; v < vehicles_; v++) {
1753 const int start = manager_.IndexToNode(
Start(v)).value();
1754 const int end = manager_.IndexToNode(
End(v)).value();
1756 const int64_t type = cost_class * nodes_squared + start * nodes_ + end;
1758 const auto& vehicle_type_added = type_to_type_index.insert(
1759 std::make_pair(type, type_to_type_index.size()));
1761 const int index = vehicle_type_added.first->second;
1764 const VehicleTypeContainer::VehicleClassEntry class_entry = {
1767 if (vehicle_type_added.second) {
1770 sorted_vehicle_classes_per_type.push_back({class_entry});
1774 sorted_vehicle_classes_per_type[
index].insert(class_entry);
1777 type_index_of_vehicle[v] =
index;
1781void RoutingModel::FinalizeVisitTypes() {
1787 single_nodes_of_type_.clear();
1788 single_nodes_of_type_.resize(num_visit_types_);
1789 pair_indices_of_type_.clear();
1790 pair_indices_of_type_.resize(num_visit_types_);
1791 std::vector<absl::flat_hash_set<int>> pair_indices_added_for_type(
1796 if (visit_type < 0) {
1799 const std::vector<std::pair<int, int>>& pickup_index_pairs =
1800 index_to_pickup_index_pairs_[
index];
1801 const std::vector<std::pair<int, int>>& delivery_index_pairs =
1802 index_to_delivery_index_pairs_[
index];
1803 if (pickup_index_pairs.empty() && delivery_index_pairs.empty()) {
1804 single_nodes_of_type_[visit_type].push_back(
index);
1806 for (
const std::vector<std::pair<int, int>>* index_pairs :
1807 {&pickup_index_pairs, &delivery_index_pairs}) {
1808 for (
const std::pair<int, int>& index_pair : *index_pairs) {
1809 const int pair_index = index_pair.first;
1810 if (pair_indices_added_for_type[visit_type].insert(pair_index).second) {
1811 pair_indices_of_type_[visit_type].push_back(pair_index);
1817 TopologicallySortVisitTypes();
1820void RoutingModel::TopologicallySortVisitTypes() {
1821 if (!has_same_vehicle_type_requirements_ &&
1822 !has_temporal_type_requirements_) {
1825 std::vector<std::pair<double, double>> type_requirement_tightness(
1826 num_visit_types_, {0, 0});
1827 std::vector<absl::flat_hash_set<int>> type_to_dependent_types(
1829 SparseBitset<> types_in_requirement_graph(num_visit_types_);
1830 std::vector<int> in_degree(num_visit_types_, 0);
1831 for (
int type = 0; type < num_visit_types_; type++) {
1832 int num_alternative_required_types = 0;
1833 int num_required_sets = 0;
1834 for (
const std::vector<absl::flat_hash_set<int>>*
1835 required_type_alternatives :
1836 {&required_type_alternatives_when_adding_type_index_[type],
1837 &required_type_alternatives_when_removing_type_index_[type],
1838 &same_vehicle_required_type_alternatives_per_type_index_[type]}) {
1839 for (
const absl::flat_hash_set<int>& alternatives :
1840 *required_type_alternatives) {
1841 types_in_requirement_graph.Set(type);
1842 num_required_sets++;
1843 for (
int required_type : alternatives) {
1844 type_requirement_tightness[required_type].second +=
1845 1.0 / alternatives.size();
1846 types_in_requirement_graph.Set(required_type);
1847 num_alternative_required_types++;
1848 if (type_to_dependent_types[required_type].insert(type).second) {
1854 if (num_alternative_required_types > 0) {
1855 type_requirement_tightness[type].first += 1.0 * num_required_sets *
1857 num_alternative_required_types;
1862 topologically_sorted_visit_types_.clear();
1863 std::vector<int> current_types_with_zero_indegree;
1864 for (
int type : types_in_requirement_graph.PositionsSetAtLeastOnce()) {
1865 DCHECK(type_requirement_tightness[type].first > 0 ||
1866 type_requirement_tightness[type].second > 0);
1867 if (in_degree[type] == 0) {
1868 current_types_with_zero_indegree.push_back(type);
1872 int num_types_added = 0;
1873 while (!current_types_with_zero_indegree.empty()) {
1876 topologically_sorted_visit_types_.push_back({});
1877 std::vector<int>& topological_group =
1878 topologically_sorted_visit_types_.back();
1879 std::vector<int> next_types_with_zero_indegree;
1880 for (
int type : current_types_with_zero_indegree) {
1881 topological_group.push_back(type);
1883 for (
int dependent_type : type_to_dependent_types[type]) {
1884 DCHECK_GT(in_degree[dependent_type], 0);
1885 if (--in_degree[dependent_type] == 0) {
1886 next_types_with_zero_indegree.push_back(dependent_type);
1897 std::sort(topological_group.begin(), topological_group.end(),
1898 [&type_requirement_tightness](
int type1,
int type2) {
1899 const auto& tightness1 = type_requirement_tightness[type1];
1900 const auto& tightness2 = type_requirement_tightness[type2];
1901 return tightness1 > tightness2 ||
1902 (tightness1 == tightness2 && type1 < type2);
1905 current_types_with_zero_indegree.swap(next_types_with_zero_indegree);
1908 const int num_types_in_requirement_graph =
1909 types_in_requirement_graph.NumberOfSetCallsWithDifferentArguments();
1910 DCHECK_LE(num_types_added, num_types_in_requirement_graph);
1911 if (num_types_added < num_types_in_requirement_graph) {
1913 topologically_sorted_visit_types_.clear();
1918 const std::vector<int64_t>& indices, int64_t penalty,
1919 int64_t max_cardinality) {
1921 for (
int i = 0; i < indices.size(); ++i) {
1926 disjunctions_.push_back({indices, {penalty, max_cardinality}});
1927 for (
const int64_t
index : indices) {
1928 index_to_disjunctions_[
index].push_back(disjunction_index);
1930 return disjunction_index;
1934 for (
const auto& [indices,
value] : disjunctions_) {
1941 for (
const auto& [indices,
value] : disjunctions_) {
1942 if (indices.size() >
value.max_cardinality)
return true;
1947std::vector<std::pair<int64_t, int64_t>>
1949 std::vector<std::pair<int64_t, int64_t>> var_index_pairs;
1950 for (
const Disjunction& disjunction : disjunctions_) {
1951 const std::vector<int64_t>&
var_indices = disjunction.indices;
1955 if (index_to_disjunctions_[v0].size() == 1 &&
1956 index_to_disjunctions_[v1].size() == 1) {
1961 std::sort(var_index_pairs.begin(), var_index_pairs.end());
1962 return var_index_pairs;
1967 for (Disjunction& disjunction : disjunctions_) {
1968 bool has_one_potentially_active_var =
false;
1969 for (
const int64_t var_index : disjunction.indices) {
1971 has_one_potentially_active_var =
true;
1975 if (!has_one_potentially_active_var) {
1976 disjunction.value.max_cardinality = 0;
1982 const std::vector<int64_t>& indices = disjunctions_[disjunction].indices;
1983 const int indices_size = indices.size();
1984 std::vector<IntVar*> disjunction_vars(indices_size);
1985 for (
int i = 0; i < indices_size; ++i) {
1986 const int64_t
index = indices[i];
1990 const int64_t max_cardinality =
1991 disjunctions_[disjunction].value.max_cardinality;
1992 IntVar* no_active_var = solver_->MakeBoolVar();
1993 IntVar* number_active_vars = solver_->MakeIntVar(0, max_cardinality);
1994 solver_->AddConstraint(
1995 solver_->MakeSumEquality(disjunction_vars, number_active_vars));
1996 solver_->AddConstraint(solver_->MakeIsDifferentCstCt(
1997 number_active_vars, max_cardinality, no_active_var));
1998 const int64_t penalty = disjunctions_[disjunction].value.penalty;
2000 no_active_var->SetMax(0);
2003 return solver_->MakeProd(no_active_var, penalty)->Var();
2008 const std::vector<int64_t>& indices, int64_t
cost) {
2009 if (!indices.empty()) {
2010 ValuedNodes<int64_t> same_vehicle_cost;
2011 for (
const int64_t
index : indices) {
2012 same_vehicle_cost.indices.push_back(
index);
2014 same_vehicle_cost.value =
cost;
2015 same_vehicle_costs_.push_back(same_vehicle_cost);
2021 auto& allowed_vehicles = allowed_vehicles_[
index];
2022 allowed_vehicles.clear();
2024 allowed_vehicles.insert(vehicle);
2029 AddPickupAndDeliverySetsInternal({pickup}, {delivery});
2036 AddPickupAndDeliverySetsInternal(
2039 pickup_delivery_disjunctions_.push_back(
2040 {pickup_disjunction, delivery_disjunction});
2043void RoutingModel::AddPickupAndDeliverySetsInternal(
2044 const std::vector<int64_t>& pickups,
2045 const std::vector<int64_t>& deliveries) {
2046 if (pickups.empty() || deliveries.empty()) {
2049 const int64_t size =
Size();
2050 const int pair_index = pickup_delivery_pairs_.size();
2051 for (
int pickup_index = 0; pickup_index < pickups.size(); pickup_index++) {
2052 const int64_t pickup = pickups[pickup_index];
2054 index_to_pickup_index_pairs_[pickup].emplace_back(pair_index, pickup_index);
2056 for (
int delivery_index = 0; delivery_index < deliveries.size();
2058 const int64_t delivery = deliveries[delivery_index];
2060 index_to_delivery_index_pairs_[delivery].emplace_back(pair_index,
2063 pickup_delivery_pairs_.push_back({pickups, deliveries});
2067 int64_t node_index)
const {
2068 CHECK_LT(node_index, index_to_pickup_index_pairs_.size());
2069 return index_to_pickup_index_pairs_[node_index];
2073 int64_t node_index)
const {
2074 CHECK_LT(node_index, index_to_delivery_index_pairs_.size());
2075 return index_to_delivery_index_pairs_[node_index];
2081 vehicle_pickup_delivery_policy_[vehicle] = policy;
2087 for (
int i = 0; i < vehicles_; ++i) {
2095 return vehicle_pickup_delivery_policy_[vehicle];
2100 for (
int i = 0; i <
Nexts().size(); ++i) {
2110IntVar* RoutingModel::CreateSameVehicleCost(
int vehicle_index) {
2111 const std::vector<int64_t>& indices =
2112 same_vehicle_costs_[vehicle_index].indices;
2113 CHECK(!indices.empty());
2114 std::vector<IntVar*> vehicle_counts;
2115 solver_->MakeIntVarArray(vehicle_vars_.size() + 1, 0, indices.size() + 1,
2117 std::vector<int64_t> vehicle_values(vehicle_vars_.size() + 1);
2118 for (
int i = 0; i < vehicle_vars_.size(); ++i) {
2119 vehicle_values[i] = i;
2121 vehicle_values[vehicle_vars_.size()] = -1;
2122 std::vector<IntVar*> vehicle_vars;
2123 vehicle_vars.reserve(indices.size());
2124 for (
const int64_t
index : indices) {
2125 vehicle_vars.push_back(vehicle_vars_[
index]);
2127 solver_->AddConstraint(solver_->MakeDistribute(vehicle_vars, vehicle_counts));
2128 std::vector<IntVar*> vehicle_used;
2129 for (
int i = 0; i < vehicle_vars_.size() + 1; ++i) {
2130 vehicle_used.push_back(
2131 solver_->MakeIsGreaterOrEqualCstVar(vehicle_counts[i], 1));
2133 vehicle_used.push_back(solver_->MakeIntConst(-1));
2135 ->MakeProd(solver_->MakeMax(solver_->MakeSum(vehicle_used), 0),
2136 same_vehicle_costs_[vehicle_index].value)
2141 extra_operators_.push_back(ls_operator);
2150void RoutingModel::AppendHomogeneousArcCosts(
2152 std::vector<IntVar*>* cost_elements) {
2153 CHECK(cost_elements !=
nullptr);
2154 const auto arc_cost_evaluator = [
this, node_index](int64_t next_index) {
2161 IntVar*
const base_cost_var =
2163 solver_->AddConstraint(MakeLightElement(
2164 solver_.get(), base_cost_var, nexts_[node_index], arc_cost_evaluator,
2165 [
this]() { return enable_deep_serialization_; }));
2167 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
2168 cost_elements->push_back(
var);
2170 IntExpr*
const expr =
2171 solver_->MakeElement(arc_cost_evaluator, nexts_[node_index]);
2172 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
2173 cost_elements->push_back(
var);
2177void RoutingModel::AppendArcCosts(
const RoutingSearchParameters&
parameters,
2179 std::vector<IntVar*>* cost_elements) {
2180 CHECK(cost_elements !=
nullptr);
2186 IntVar*
const base_cost_var =
2188 solver_->AddConstraint(MakeLightElement2(
2189 solver_.get(), base_cost_var, nexts_[node_index],
2190 vehicle_vars_[node_index],
2191 [
this, node_index](int64_t to, int64_t vehicle) {
2192 return GetArcCostForVehicle(node_index, to, vehicle);
2194 [
this]() { return enable_deep_serialization_; }));
2196 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
2197 cost_elements->push_back(
var);
2199 IntVar*
const vehicle_class_var =
2202 [
this](int64_t
index) {
2203 return SafeGetCostClassInt64OfVehicle(
index);
2205 vehicle_vars_[node_index])
2207 IntExpr*
const expr = solver_->MakeElement(
2211 nexts_[node_index], vehicle_class_var);
2212 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
2213 cost_elements->push_back(
var);
2217int RoutingModel::GetVehicleStartClass(int64_t start_index)
const {
2218 const int vehicle = index_to_vehicle_[start_index];
2225std::string RoutingModel::FindErrorInSearchParametersForModel(
2226 const RoutingSearchParameters& search_parameters)
const {
2228 search_parameters.first_solution_strategy();
2229 if (GetFirstSolutionDecisionBuilder(search_parameters) ==
nullptr) {
2230 return absl::StrCat(
2231 "Undefined first solution strategy: ",
2233 " (int value: ", first_solution_strategy,
")");
2235 if (search_parameters.first_solution_strategy() ==
2238 return "Undefined sweep arranger for ROUTING_SWEEP strategy.";
2243void RoutingModel::QuietCloseModel() {
2254 same_vehicle_components_.SetNumberOfNodes(
model->Size());
2255 for (
const std::string&
name :
model->GetAllDimensionNames()) {
2257 const std::vector<IntVar*>& cumuls = dimension->
cumuls();
2258 for (
int i = 0; i < cumuls.size(); ++i) {
2259 cumul_to_dim_indices_[cumuls[i]] = {dimension, i};
2262 const std::vector<IntVar*>& vehicle_vars =
model->VehicleVars();
2263 for (
int i = 0; i < vehicle_vars.size(); ++i) {
2264 vehicle_var_to_indices_[vehicle_vars[i]] = i;
2266 RegisterInspectors();
2270 const std::vector<int> node_to_same_vehicle_component_id =
2271 same_vehicle_components_.GetComponentIds();
2272 model_->InitSameVehicleGroups(
2273 same_vehicle_components_.GetNumberOfComponents());
2274 for (
int node = 0; node < model_->Size(); ++node) {
2275 model_->SetSameVehicleGroup(node,
2276 node_to_same_vehicle_component_id[node]);
2282 const Constraint*
const constraint)
override {
2286 IntExpr*
const expr)
override {
2288 [](
const IntExpr* expr) {})(expr);
2291 const std::vector<int64_t>& values)
override {
2293 [](
const std::vector<int64_t>& int_array) {})(values);
2297 using ExprInspector = std::function<void(
const IntExpr*)>;
2298 using ArrayInspector = std::function<void(
const std::vector<int64_t>&)>;
2299 using ConstraintInspector = std::function<void()>;
2301 void RegisterInspectors() {
2302 expr_inspectors_[kExpressionArgument] = [
this](
const IntExpr* expr) {
2305 expr_inspectors_[kLeftArgument] = [
this](
const IntExpr* expr) {
2308 expr_inspectors_[kRightArgument] = [
this](
const IntExpr* expr) {
2311 array_inspectors_[kStartsArgument] =
2312 [
this](
const std::vector<int64_t>& int_array) {
2313 starts_argument_ = int_array;
2315 array_inspectors_[kEndsArgument] =
2316 [
this](
const std::vector<int64_t>& int_array) {
2317 ends_argument_ = int_array;
2319 constraint_inspectors_[kNotMember] = [
this]() {
2320 std::pair<RoutingDimension*, int> dim_index;
2323 const int index = dim_index.second;
2324 dimension->forbidden_intervals_[
index].InsertIntervals(starts_argument_,
2326 VLOG(2) << dimension->name() <<
" " <<
index <<
": "
2327 << dimension->forbidden_intervals_[
index].DebugString();
2330 starts_argument_.clear();
2331 ends_argument_.clear();
2333 constraint_inspectors_[kEquality] = [
this]() {
2335 int right_index = 0;
2336 if (
gtl::FindCopy(vehicle_var_to_indices_, left_, &left_index) &&
2337 gtl::FindCopy(vehicle_var_to_indices_, right_, &right_index)) {
2338 VLOG(2) <<
"Vehicle variables for " << left_index <<
" and "
2339 << right_index <<
" are equal.";
2340 same_vehicle_components_.AddEdge(left_index, right_index);
2345 constraint_inspectors_[kLessOrEqual] = [
this]() {
2346 std::pair<RoutingDimension*, int> left_index;
2347 std::pair<RoutingDimension*, int> right_index;
2348 if (
gtl::FindCopy(cumul_to_dim_indices_, left_, &left_index) &&
2349 gtl::FindCopy(cumul_to_dim_indices_, right_, &right_index)) {
2351 if (dimension == right_index.first) {
2352 VLOG(2) <<
"For dimension " << dimension->name() <<
", cumul for "
2353 << left_index.second <<
" is less than " << right_index.second
2355 dimension->path_precedence_graph_.AddArc(left_index.second,
2356 right_index.second);
2366 absl::flat_hash_map<const IntExpr*, std::pair<RoutingDimension*, int>>
2367 cumul_to_dim_indices_;
2368 absl::flat_hash_map<const IntExpr*, int> vehicle_var_to_indices_;
2369 absl::flat_hash_map<std::string, ExprInspector> expr_inspectors_;
2370 absl::flat_hash_map<std::string, ArrayInspector> array_inspectors_;
2371 absl::flat_hash_map<std::string, ConstraintInspector> constraint_inspectors_;
2372 const IntExpr*
expr_ =
nullptr;
2373 const IntExpr* left_ =
nullptr;
2374 const IntExpr* right_ =
nullptr;
2375 std::vector<int64_t> starts_argument_;
2376 std::vector<int64_t> ends_argument_;
2379void RoutingModel::DetectImplicitPickupAndDeliveries() {
2380 std::vector<int> non_pickup_delivery_nodes;
2381 for (
int node = 0; node <
Size(); ++node) {
2384 non_pickup_delivery_nodes.push_back(node);
2388 std::set<std::pair<int64_t, int64_t>> implicit_pickup_deliveries;
2390 if (dimension->class_evaluators_.size() != 1) {
2395 if (transit ==
nullptr)
continue;
2396 absl::flat_hash_map<int64_t, std::vector<int64_t>> nodes_by_positive_demand;
2397 absl::flat_hash_map<int64_t, std::vector<int64_t>> nodes_by_negative_demand;
2398 for (
int node : non_pickup_delivery_nodes) {
2399 const int64_t
demand = transit(node);
2401 nodes_by_positive_demand[
demand].push_back(node);
2403 nodes_by_negative_demand[-
demand].push_back(node);
2406 for (
const auto& [
demand, positive_nodes] : nodes_by_positive_demand) {
2407 const std::vector<int64_t>*
const negative_nodes =
2409 if (negative_nodes !=
nullptr) {
2410 for (int64_t positive_node : positive_nodes) {
2411 for (int64_t negative_node : *negative_nodes) {
2412 implicit_pickup_deliveries.insert({positive_node, negative_node});
2418 implicit_pickup_delivery_pairs_without_alternatives_.clear();
2419 for (
auto [pickup, delivery] : implicit_pickup_deliveries) {
2420 implicit_pickup_delivery_pairs_without_alternatives_.emplace_back(
2421 std::vector<int64_t>({pickup}), std::vector<int64_t>({delivery}));
2428 if (!error.empty()) {
2430 LOG(
ERROR) <<
"Invalid RoutingSearchParameters: " << error;
2440 dimension->CloseModel(UsesLightPropagation(
parameters));
2443 dimension_resource_group_indices_.resize(dimensions_.size());
2444 for (
int rg_index = 0; rg_index < resource_groups_.size(); rg_index++) {
2445 const ResourceGroup& resource_group = *resource_groups_[rg_index];
2449 dimension_resource_group_indices_[dim_index].push_back(rg_index);
2454 ComputeVehicleClasses();
2455 ComputeVehicleTypes();
2456 FinalizeVisitTypes();
2457 vehicle_start_class_callback_ = [
this](int64_t start) {
2458 return GetVehicleStartClass(start);
2461 AddNoCycleConstraintInternal();
2463 const int size =
Size();
2466 for (
int i = 0; i < vehicles_; ++i) {
2467 const int64_t start = starts_[i];
2468 const int64_t end = ends_[i];
2469 solver_->AddConstraint(
2470 solver_->MakeEquality(vehicle_vars_[start], solver_->MakeIntConst(i)));
2471 solver_->AddConstraint(
2472 solver_->MakeEquality(vehicle_vars_[end], solver_->MakeIntConst(i)));
2473 solver_->AddConstraint(
2474 solver_->MakeIsDifferentCstCt(nexts_[start], end, vehicle_active_[i]));
2475 if (vehicle_used_when_empty_[i]) {
2476 vehicle_route_considered_[i]->SetMin(1);
2478 solver_->AddConstraint(solver_->MakeEquality(
2479 vehicle_active_[i], vehicle_route_considered_[i]));
2484 if (vehicles_ > max_active_vehicles_) {
2485 solver_->AddConstraint(
2486 solver_->MakeSumLessOrEqual(vehicle_active_, max_active_vehicles_));
2494 if (vehicles_ > 1) {
2495 std::vector<IntVar*> zero_transit(size, solver_->MakeIntConst(0));
2496 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
2497 nexts_, active_, vehicle_vars_, zero_transit));
2502 for (
int i = 0; i < size; ++i) {
2504 active_[i]->SetValue(1);
2510 const absl::flat_hash_set<VisitTypePolicy>*
const infeasible_policies =
2512 if (infeasible_policies !=
nullptr &&
2514 active_[i]->SetValue(0);
2519 for (
int i = 0; i < allowed_vehicles_.size(); ++i) {
2520 const auto& allowed_vehicles = allowed_vehicles_[i];
2521 if (!allowed_vehicles.empty()) {
2523 vehicles.reserve(allowed_vehicles.size() + 1);
2525 for (
int vehicle : allowed_vehicles) {
2533 for (
int i = 0; i < size; ++i) {
2535 solver_->AddConstraint(solver_->RevAlloc(
2536 new DifferentFromValues(solver_.get(), nexts_[i], starts_)));
2538 solver_->AddConstraint(
2539 solver_->MakeIsDifferentCstCt(nexts_[i], i, active_[i]));
2544 for (
int i = 0; i < size; ++i) {
2545 solver_->AddConstraint(
2546 solver_->MakeIsDifferentCstCt(vehicle_vars_[i], -1, active_[i]));
2550 solver_->AddConstraint(
2555 for (
int i = 0; i < vehicles_; ++i) {
2556 std::vector<int64_t> forbidden_ends;
2557 forbidden_ends.reserve(vehicles_ - 1);
2558 for (
int j = 0; j < vehicles_; ++j) {
2560 forbidden_ends.push_back(ends_[j]);
2563 solver_->AddConstraint(solver_->RevAlloc(
new DifferentFromValues(
2564 solver_.get(), nexts_[starts_[i]], std::move(forbidden_ends))));
2568 for (
const int64_t end : ends_) {
2569 is_bound_to_end_[end]->SetValue(1);
2572 std::vector<IntVar*> cost_elements;
2574 if (vehicles_ > 0) {
2575 for (
int node_index = 0; node_index < size; ++node_index) {
2577 AppendHomogeneousArcCosts(
parameters, node_index, &cost_elements);
2579 AppendArcCosts(
parameters, node_index, &cost_elements);
2582 if (vehicle_amortized_cost_factors_set_) {
2583 std::vector<IntVar*> route_lengths;
2584 solver_->MakeIntVarArray(vehicles_, 0, size, &route_lengths);
2585 solver_->AddConstraint(
2586 solver_->MakeDistribute(vehicle_vars_, route_lengths));
2587 std::vector<IntVar*> vehicle_used;
2588 for (
int i = 0; i < vehicles_; i++) {
2590 vehicle_used.push_back(
2591 solver_->MakeIsGreaterCstVar(route_lengths[i], 2));
2594 ->MakeProd(solver_->MakeOpposite(solver_->MakeSquare(
2595 solver_->MakeSum(route_lengths[i], -2))),
2596 quadratic_cost_factor_of_vehicle_[i])
2598 cost_elements.push_back(
var);
2600 IntVar*
const vehicle_usage_cost =
2601 solver_->MakeScalProd(vehicle_used, linear_cost_factor_of_vehicle_)
2603 cost_elements.push_back(vehicle_usage_cost);
2608 dimension->SetupGlobalSpanCost(&cost_elements);
2609 dimension->SetupSlackAndDependentTransitCosts();
2610 const std::vector<int64_t>& span_costs =
2611 dimension->vehicle_span_cost_coefficients();
2612 const std::vector<int64_t>& span_ubs =
2613 dimension->vehicle_span_upper_bounds();
2614 const bool has_span_constraint =
2615 std::any_of(span_costs.begin(), span_costs.end(),
2616 [](int64_t coeff) { return coeff != 0; }) ||
2617 std::any_of(span_ubs.begin(), span_ubs.end(),
2619 return value < std::numeric_limits<int64_t>::max();
2621 dimension->HasSoftSpanUpperBounds() ||
2622 dimension->HasQuadraticCostSoftSpanUpperBounds();
2623 if (has_span_constraint) {
2624 std::vector<IntVar*> spans(
vehicles(),
nullptr);
2625 std::vector<IntVar*> total_slacks(
vehicles(),
nullptr);
2627 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2629 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2631 if (span_costs[vehicle] != 0) {
2632 total_slacks[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2635 if (dimension->HasSoftSpanUpperBounds()) {
2636 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2637 if (spans[vehicle])
continue;
2639 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2640 if (bound_cost.
cost == 0)
continue;
2641 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2644 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2645 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2646 if (spans[vehicle])
continue;
2648 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2649 if (bound_cost.
cost == 0)
continue;
2650 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2653 solver_->AddConstraint(
2657 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2658 if (!spans[vehicle] && !total_slacks[vehicle])
continue;
2659 if (spans[vehicle]) {
2669 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2670 if (span_costs[vehicle] == 0)
continue;
2671 DCHECK(total_slacks[vehicle] !=
nullptr);
2672 IntVar*
const slack_amount =
2674 ->MakeProd(vehicle_route_considered_[vehicle],
2675 total_slacks[vehicle])
2677 IntVar*
const slack_cost =
2678 solver_->MakeProd(slack_amount, span_costs[vehicle])->Var();
2679 cost_elements.push_back(slack_cost);
2681 span_costs[vehicle]);
2683 if (dimension->HasSoftSpanUpperBounds()) {
2684 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2685 const auto bound_cost =
2686 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2687 if (bound_cost.cost == 0 ||
2690 DCHECK(spans[vehicle] !=
nullptr);
2693 IntVar*
const span_violation_amount =
2696 vehicle_route_considered_[vehicle],
2698 solver_->MakeSum(spans[vehicle], -bound_cost.bound),
2701 IntVar*
const span_violation_cost =
2702 solver_->MakeProd(span_violation_amount, bound_cost.cost)->Var();
2703 cost_elements.push_back(span_violation_cost);
2708 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2709 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2710 const auto bound_cost =
2711 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2712 if (bound_cost.cost == 0 ||
2715 DCHECK(spans[vehicle] !=
nullptr);
2718 IntExpr* max0 = solver_->MakeMax(
2719 solver_->MakeSum(spans[vehicle], -bound_cost.bound), 0);
2720 IntVar*
const squared_span_violation_amount =
2722 ->MakeProd(vehicle_route_considered_[vehicle],
2723 solver_->MakeSquare(max0))
2725 IntVar*
const span_violation_cost =
2726 solver_->MakeProd(squared_span_violation_amount, bound_cost.cost)
2728 cost_elements.push_back(span_violation_cost);
2737 IntVar* penalty_var = CreateDisjunction(i);
2738 if (penalty_var !=
nullptr) {
2739 cost_elements.push_back(penalty_var);
2744 dimension->SetupCumulVarSoftLowerBoundCosts(&cost_elements);
2745 dimension->SetupCumulVarSoftUpperBoundCosts(&cost_elements);
2746 dimension->SetupCumulVarPiecewiseLinearCosts(&cost_elements);
2749 for (
int i = 0; i < same_vehicle_costs_.size(); ++i) {
2750 cost_elements.push_back(CreateSameVehicleCost(i));
2752 cost_ = solver_->MakeSum(cost_elements)->Var();
2753 cost_->set_name(
"Cost");
2756 std::vector<std::pair<int, int>> pickup_delivery_precedences;
2757 for (
const auto& pair : pickup_delivery_pairs_) {
2758 DCHECK(!pair.first.empty() && !pair.second.empty());
2759 for (
int pickup : pair.first) {
2760 for (
int delivery : pair.second) {
2761 pickup_delivery_precedences.emplace_back(pickup, delivery);
2765 std::vector<int> lifo_vehicles;
2766 std::vector<int> fifo_vehicles;
2767 for (
int i = 0; i < vehicles_; ++i) {
2768 switch (vehicle_pickup_delivery_policy_[i]) {
2772 lifo_vehicles.push_back(
Start(i));
2775 fifo_vehicles.push_back(
Start(i));
2779 solver_->AddConstraint(solver_->MakePathPrecedenceConstraint(
2780 nexts_, pickup_delivery_precedences, lifo_vehicles, fifo_vehicles));
2783 enable_deep_serialization_ =
false;
2784 std::unique_ptr<RoutingModelInspector> inspector(
2786 solver_->Accept(inspector.get());
2787 enable_deep_serialization_ =
true;
2793 dimension->GetPathPrecedenceGraph();
2794 std::vector<std::pair<int, int>> path_precedences;
2796 for (
const auto head : graph[
tail]) {
2797 path_precedences.emplace_back(
tail,
head);
2800 if (!path_precedences.empty()) {
2801 solver_->AddConstraint(solver_->MakePathTransitPrecedenceConstraint(
2802 nexts_, dimension->transits(), path_precedences));
2807 dimension->GetNodePrecedences()) {
2808 const int64_t first_node = node_precedence.first_node;
2809 const int64_t second_node = node_precedence.second_node;
2810 IntExpr*
const nodes_are_selected =
2811 solver_->MakeMin(active_[first_node], active_[second_node]);
2812 IntExpr*
const cumul_difference = solver_->MakeDifference(
2813 dimension->CumulVar(second_node), dimension->CumulVar(first_node));
2814 IntVar*
const cumul_difference_is_ge_offset =
2815 solver_->MakeIsGreaterOrEqualCstVar(cumul_difference,
2816 node_precedence.offset);
2819 solver_->AddConstraint(solver_->MakeLessOrEqual(
2820 nodes_are_selected->
Var(), cumul_difference_is_ge_offset));
2825 resource_vars_.resize(resource_groups_.size());
2826 for (
int rg = 0; rg < resource_groups_.size(); rg++) {
2827 const ResourceGroup*
const resource_group = resource_groups_[rg].get();
2829 const int num_resources = resource_group->
Size();
2830 std::vector<IntVar*>& resource_vars = resource_vars_[rg];
2831 solver_->MakeIntVarArray(vehicles_, -1, num_resources - 1,
2832 absl::StrCat(
"Resources[", rg,
"]"),
2836 solver_->AddConstraint(solver_->MakeAllDifferentExcept(resource_vars, -1));
2837 for (
int v = 0; v < vehicles_; v++) {
2838 IntVar*
const resource_var = resource_vars[v];
2845 solver_->AddConstraint(solver_->MakeEquality(
2846 vehicle_route_considered_[v],
2847 solver_->MakeIsDifferentCstVar(resource_var, -1)));
2855 IntVar*
const resource_start_lb_var =
2858 solver_->AddConstraint(MakeLightElement(
2859 solver_.get(), resource_start_lb_var, resource_var,
2860 [dim, resource_group](
int r) {
2861 if (r < 0) return std::numeric_limits<int64_t>::min();
2862 return resource_group->GetResources()[r]
2863 .GetDimensionAttributes(dim)
2867 [
this]() { return enable_deep_serialization_; }));
2868 solver_->AddConstraint(solver_->MakeGreaterOrEqual(
2869 start_cumul_var, resource_start_lb_var));
2871 IntVar*
const resource_start_ub_var =
2874 solver_->AddConstraint(MakeLightElement(
2875 solver_.get(), resource_start_ub_var, resource_var,
2876 [dim, resource_group](
int r) {
2877 if (r < 0) return std::numeric_limits<int64_t>::max();
2878 return resource_group->GetResources()[r]
2879 .GetDimensionAttributes(dim)
2883 [
this]() { return enable_deep_serialization_; }));
2884 solver_->AddConstraint(
2885 solver_->MakeLessOrEqual(start_cumul_var, resource_start_ub_var));
2889 IntVar*
const resource_end_lb_var =
2892 solver_->AddConstraint(MakeLightElement(
2893 solver_.get(), resource_end_lb_var, resource_var,
2894 [dim, resource_group](
int r) {
2895 if (r < 0) return std::numeric_limits<int64_t>::min();
2896 return resource_group->GetResources()[r]
2897 .GetDimensionAttributes(dim)
2901 [
this]() { return enable_deep_serialization_; }));
2902 solver_->AddConstraint(
2903 solver_->MakeGreaterOrEqual(end_cumul_var, resource_end_lb_var));
2905 IntVar*
const resource_end_ub_var =
2908 solver_->AddConstraint(MakeLightElement(
2909 solver_.get(), resource_end_ub_var, resource_var,
2910 [dim, resource_group](
int r) {
2911 if (r < 0) return std::numeric_limits<int64_t>::max();
2912 return resource_group->GetResources()[r]
2913 .GetDimensionAttributes(dim)
2917 [
this]() { return enable_deep_serialization_; }));
2918 solver_->AddConstraint(
2919 solver_->MakeLessOrEqual(end_cumul_var, resource_end_ub_var));
2924 DetectImplicitPickupAndDeliveries();
2933 CreateFirstSolutionDecisionBuilders(
parameters);
2934 error = FindErrorInSearchParametersForModel(
parameters);
2935 if (!error.empty()) {
2937 LOG(
ERROR) <<
"Invalid RoutingSearchParameters for this model: " << error;
2950class RestoreDimensionValuesForUnchangedRoutes :
public DecisionBuilder {
2954 model_->AddAtSolutionCallback([
this]() { AtSolution(); });
2955 next_last_value_.resize(model_->Nexts().size(), -1);
2960 Decision*
Next(Solver*
const s)
override {
2961 if (!must_return_decision_)
return nullptr;
2962 s->SaveAndSetValue(&must_return_decision_,
false);
2963 return MakeDecision(s);
2970 is_initialized_ =
true;
2971 const int num_nodes = model_->VehicleVars().size();
2972 node_to_integer_variable_indices_.resize(num_nodes);
2973 node_to_interval_variable_indices_.resize(num_nodes);
2975 for (
const std::string& dimension_name : model_->GetAllDimensionNames()) {
2977 model_->GetDimensionOrDie(dimension_name);
2979 for (
const std::vector<IntVar*>& dimension_variables :
2980 {dimension.cumuls(), dimension.slacks()}) {
2981 const int num_dimension_variables = dimension_variables.size();
2982 DCHECK_LE(num_dimension_variables, num_nodes);
2983 for (
int node = 0; node < num_dimension_variables; ++node) {
2984 node_to_integer_variable_indices_[node].push_back(
2985 integer_variables_.size());
2986 integer_variables_.push_back(dimension_variables[node]);
2990 for (
int vehicle = 0; vehicle < model_->vehicles(); ++vehicle) {
2991 if (!dimension.HasBreakConstraints())
continue;
2992 const int vehicle_start = model_->Start(vehicle);
2994 dimension.GetBreakIntervalsOfVehicle(vehicle)) {
2995 node_to_interval_variable_indices_[vehicle_start].push_back(
2996 interval_variables_.size());
2997 interval_variables_.push_back(
interval);
3001 integer_variables_last_min_.resize(integer_variables_.size());
3002 interval_variables_last_start_min_.resize(interval_variables_.size());
3003 interval_variables_last_end_max_.resize(interval_variables_.size());
3006 Decision* MakeDecision(Solver*
const s) {
3007 if (!is_initialized_)
return nullptr;
3009 std::vector<int> unchanged_vehicles;
3010 const int num_vehicles = model_->vehicles();
3011 for (
int v = 0; v < num_vehicles; ++v) {
3012 bool unchanged =
true;
3013 for (
int current = model_->Start(v); !model_->IsEnd(current);
3014 current = next_last_value_[current]) {
3015 if (!model_->NextVar(current)->Bound() ||
3016 next_last_value_[current] != model_->NextVar(current)->Value()) {
3021 if (unchanged) unchanged_vehicles.push_back(v);
3025 if (unchanged_vehicles.size() == num_vehicles)
return nullptr;
3028 std::vector<IntVar*> vars;
3029 std::vector<int64_t> values;
3030 for (
const int vehicle : unchanged_vehicles) {
3031 for (
int current = model_->Start(vehicle);
true;
3032 current = next_last_value_[current]) {
3033 for (
const int index : node_to_integer_variable_indices_[current]) {
3034 vars.push_back(integer_variables_[
index]);
3035 values.push_back(integer_variables_last_min_[
index]);
3037 for (
const int index : node_to_interval_variable_indices_[current]) {
3038 const int64_t
start_min = interval_variables_last_start_min_[
index];
3039 const int64_t
end_max = interval_variables_last_end_max_[
index];
3041 vars.push_back(interval_variables_[
index]->SafeStartExpr(0)->Var());
3042 values.push_back(interval_variables_last_start_min_[
index]);
3043 vars.push_back(interval_variables_[
index]->SafeEndExpr(0)->Var());
3044 values.push_back(interval_variables_last_end_max_[
index]);
3046 vars.push_back(interval_variables_[
index]->PerformedExpr()->Var());
3047 values.push_back(0);
3050 if (model_->IsEnd(current))
break;
3053 return s->MakeAssignVariablesValuesOrDoNothing(vars, values);
3057 if (!is_initialized_) Initialize();
3058 const int num_integers = integer_variables_.size();
3061 for (
int i = 0; i < num_integers; ++i) {
3062 integer_variables_last_min_[i] = integer_variables_[i]->Min();
3064 const int num_intervals = interval_variables_.size();
3065 for (
int i = 0; i < num_intervals; ++i) {
3066 const bool is_performed = interval_variables_[i]->MustBePerformed();
3067 interval_variables_last_start_min_[i] =
3068 is_performed ? interval_variables_[i]->StartMin() : 0;
3069 interval_variables_last_end_max_[i] =
3070 is_performed ? interval_variables_[i]->EndMax() : -1;
3072 const int num_nodes = next_last_value_.size();
3073 for (
int node = 0; node < num_nodes; ++node) {
3074 if (model_->IsEnd(node))
continue;
3075 next_last_value_[node] = model_->NextVar(node)->Value();
3083 std::vector<int> next_last_value_;
3086 std::vector<std::vector<int>> node_to_integer_variable_indices_;
3087 std::vector<std::vector<int>> node_to_interval_variable_indices_;
3089 std::vector<IntVar*> integer_variables_;
3090 std::vector<int64_t> integer_variables_last_min_;
3091 std::vector<IntervalVar*> interval_variables_;
3092 std::vector<int64_t> interval_variables_last_start_min_;
3093 std::vector<int64_t> interval_variables_last_end_max_;
3095 bool is_initialized_ =
false;
3096 bool must_return_decision_ =
true;
3102 return model->solver()->RevAlloc(
3103 new RestoreDimensionValuesForUnchangedRoutes(
model));
3107 monitors_.push_back(monitor);
3115 bool AtSolution()
override {
3121 std::function<void()> callback_;
3127 new AtSolutionCallbackMonitor(solver_.get(), std::move(
callback))));
3137 std::vector<const Assignment*>* solutions) {
3143 if (!
parameters.has_time_limit())
return absl::InfiniteDuration();
3147absl::Duration GetLnsTimeLimit(
const RoutingSearchParameters&
parameters) {
3148 if (!
parameters.has_lns_time_limit())
return absl::InfiniteDuration();
3156 Assignment* assignment) {
3157 assignment->Clear();
3158 for (
int i = 0; i <
model->Nexts().size(); ++i) {
3159 if (!
model->IsStart(i)) {
3160 assignment->Add(
model->NextVar(i))->SetValue(i);
3163 for (
int vehicle = 0; vehicle <
model->vehicles(); ++vehicle) {
3164 assignment->Add(
model->NextVar(
model->Start(vehicle)))
3165 ->SetValue(
model->End(vehicle));
3170bool RoutingModel::AppendAssignmentIfFeasible(
3171 const Assignment& assignment,
3172 std::vector<std::unique_ptr<Assignment>>* assignments) {
3173 tmp_assignment_->CopyIntersection(&assignment);
3174 solver_->Solve(restore_tmp_assignment_, collect_one_assignment_,
3175 GetOrCreateLimit());
3176 if (collect_one_assignment_->solution_count() == 1) {
3177 assignments->push_back(absl::make_unique<Assignment>(solver_.get()));
3178 assignments->back()->Copy(collect_one_assignment_->solution(0));
3184void RoutingModel::LogSolution(
const RoutingSearchParameters&
parameters,
3185 const std::string& description,
3186 int64_t solution_cost, int64_t start_time_ms) {
3188 const double cost_scaling_factor =
parameters.log_cost_scaling_factor();
3189 const double cost_offset =
parameters.log_cost_offset();
3190 const std::string cost_string =
3191 cost_scaling_factor == 1.0 && cost_offset == 0.0
3192 ? absl::StrCat(solution_cost)
3194 "%d (%.8lf)", solution_cost,
3195 cost_scaling_factor * (solution_cost + cost_offset));
3197 "%s (%s, time = %d ms, memory used = %s)", description, cost_string,
3198 solver_->wall_time() - start_time_ms, memory_str);
3203 std::vector<const Assignment*>* solutions) {
3209 const std::vector<const Assignment*>& assignments,
3211 std::vector<const Assignment*>* solutions) {
3212 const int64_t start_time_ms = solver_->wall_time();
3215 if (solutions !=
nullptr) solutions->clear();
3219 const auto update_time_limits = [
this, start_time_ms, &
parameters]() {
3220 const absl::Duration elapsed_time =
3221 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3222 const absl::Duration time_left = GetTimeLimit(
parameters) - elapsed_time;
3223 if (time_left >= absl::ZeroDuration()) {
3233 if (!update_time_limits()) {
3237 lns_limit_->UpdateLimits(
3245 const int time_limit_shares = 1 + !global_dimension_optimizers_.empty() +
3246 !local_dimension_optimizers_.empty();
3247 const absl::Duration first_solution_lns_time_limit =
3250 first_solution_lns_limit_->UpdateLimits(
3254 std::vector<std::unique_ptr<Assignment>> solution_pool;
3255 std::vector<const Assignment*> first_solution_assignments;
3256 for (
const Assignment* assignment : assignments) {
3257 if (assignment !=
nullptr) first_solution_assignments.push_back(assignment);
3260 if (first_solution_assignments.empty()) {
3261 bool solution_found =
false;
3264 AppendAssignmentIfFeasible(matching, &solution_pool)) {
3266 LogSolution(
parameters,
"Min-Cost Flow Solution",
3267 solution_pool.back()->ObjectiveValue(), start_time_ms);
3269 solution_found =
true;
3271 if (!solution_found) {
3275 MakeAllUnperformedInAssignment(
this, &unperformed);
3276 if (AppendAssignmentIfFeasible(unperformed, &solution_pool) &&
3278 LogSolution(
parameters,
"All Unperformed Solution",
3279 solution_pool.back()->ObjectiveValue(), start_time_ms);
3281 if (update_time_limits()) {
3282 solver_->Solve(solve_db_, monitors_);
3286 for (
const Assignment* assignment : first_solution_assignments) {
3287 assignment_->CopyIntersection(assignment);
3288 solver_->Solve(improve_db_, monitors_);
3289 if (collect_assignments_->solution_count() >= 1 ||
3290 !update_time_limits()) {
3299 const int solution_count = collect_assignments_->solution_count();
3301 solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
3305 AppendAssignmentIfFeasible(sat_solution, &solution_pool) &&
3307 LogSolution(
parameters,
"SAT", solution_pool.back()->ObjectiveValue(),
3312 const absl::Duration elapsed_time =
3313 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3314 const int solution_count = collect_assignments_->solution_count();
3315 if (solution_count >= 1 || !solution_pool.empty()) {
3317 if (solutions !=
nullptr) {
3318 for (
int i = 0; i < solution_count; ++i) {
3319 solutions->push_back(
3320 solver_->MakeAssignment(collect_assignments_->solution(i)));
3322 for (
const auto& solution : solution_pool) {
3323 if (solutions->empty() ||
3324 solution->ObjectiveValue() < solutions->back()->ObjectiveValue()) {
3325 solutions->push_back(solver_->MakeAssignment(solution.get()));
3328 return solutions->back();
3331 solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
3333 for (
const auto& solution : solution_pool) {
3334 if (best_assignment ==
nullptr ||
3336 best_assignment = solution.get();
3339 return solver_->MakeAssignment(best_assignment);
3341 if (elapsed_time >= GetTimeLimit(
parameters)) {
3353 const int size =
Size();
3359 source_model->
Nexts());
3361 std::vector<IntVar*> source_vars(size + size + vehicles_);
3362 std::vector<IntVar*> target_vars(size + size + vehicles_);
3372 source_assignment, source_vars);
3390 LOG(
WARNING) <<
"Non-closed model not supported.";
3394 LOG(
WARNING) <<
"Non-homogeneous vehicle costs not supported";
3397 if (!disjunctions_.empty()) {
3399 <<
"Node disjunction constraints or optional nodes not supported.";
3402 const int num_nodes =
Size() + vehicles_;
3409 std::unique_ptr<IntVarIterator> iterator(
3410 nexts_[
tail]->MakeDomainIterator(
false));
3433 return linear_sum_assignment.
GetCost();
3438bool RoutingModel::RouteCanBeUsedByVehicle(
const Assignment& assignment,
3439 int start_index,
int vehicle)
const {
3441 IsStart(start_index) ?
Next(assignment, start_index) : start_index;
3442 while (!
IsEnd(current_index)) {
3444 if (!vehicle_var->
Contains(vehicle)) {
3447 const int next_index =
Next(assignment, current_index);
3448 CHECK_NE(next_index, current_index) <<
"Inactive node inside a route";
3449 current_index = next_index;
3454bool RoutingModel::ReplaceUnusedVehicle(
3455 int unused_vehicle,
int active_vehicle,
3456 Assignment*
const compact_assignment)
const {
3457 CHECK(compact_assignment !=
nullptr);
3461 const int unused_vehicle_start =
Start(unused_vehicle);
3462 IntVar*
const unused_vehicle_start_var =
NextVar(unused_vehicle_start);
3463 const int unused_vehicle_end =
End(unused_vehicle);
3464 const int active_vehicle_start =
Start(active_vehicle);
3465 const int active_vehicle_end =
End(active_vehicle);
3466 IntVar*
const active_vehicle_start_var =
NextVar(active_vehicle_start);
3467 const int active_vehicle_next =
3468 compact_assignment->Value(active_vehicle_start_var);
3469 compact_assignment->SetValue(unused_vehicle_start_var, active_vehicle_next);
3470 compact_assignment->SetValue(active_vehicle_start_var,
End(active_vehicle));
3473 int current_index = active_vehicle_next;
3474 while (!
IsEnd(current_index)) {
3475 IntVar*
const vehicle_var =
VehicleVar(current_index);
3476 compact_assignment->SetValue(vehicle_var, unused_vehicle);
3477 const int next_index =
Next(*compact_assignment, current_index);
3478 if (
IsEnd(next_index)) {
3479 IntVar*
const last_next_var =
NextVar(current_index);
3480 compact_assignment->SetValue(last_next_var,
End(unused_vehicle));
3482 current_index = next_index;
3487 const std::vector<IntVar*>& transit_variables = dimension->transits();
3488 IntVar*
const unused_vehicle_transit_var =
3489 transit_variables[unused_vehicle_start];
3490 IntVar*
const active_vehicle_transit_var =
3491 transit_variables[active_vehicle_start];
3492 const bool contains_unused_vehicle_transit_var =
3493 compact_assignment->Contains(unused_vehicle_transit_var);
3494 const bool contains_active_vehicle_transit_var =
3495 compact_assignment->Contains(active_vehicle_transit_var);
3496 if (contains_unused_vehicle_transit_var !=
3497 contains_active_vehicle_transit_var) {
3499 LOG(
INFO) <<
"The assignment contains transit variable for dimension '"
3500 << dimension->name() <<
"' for some vehicles, but not for all";
3503 if (contains_unused_vehicle_transit_var) {
3504 const int64_t old_unused_vehicle_transit =
3505 compact_assignment->Value(unused_vehicle_transit_var);
3506 const int64_t old_active_vehicle_transit =
3507 compact_assignment->Value(active_vehicle_transit_var);
3508 compact_assignment->SetValue(unused_vehicle_transit_var,
3509 old_active_vehicle_transit);
3510 compact_assignment->SetValue(active_vehicle_transit_var,
3511 old_unused_vehicle_transit);
3515 const std::vector<IntVar*>& cumul_variables = dimension->cumuls();
3516 IntVar*
const unused_vehicle_cumul_var =
3517 cumul_variables[unused_vehicle_end];
3518 IntVar*
const active_vehicle_cumul_var =
3519 cumul_variables[active_vehicle_end];
3520 const int64_t old_unused_vehicle_cumul =
3521 compact_assignment->Value(unused_vehicle_cumul_var);
3522 const int64_t old_active_vehicle_cumul =
3523 compact_assignment->Value(active_vehicle_cumul_var);
3524 compact_assignment->SetValue(unused_vehicle_cumul_var,
3525 old_active_vehicle_cumul);
3526 compact_assignment->SetValue(active_vehicle_cumul_var,
3527 old_unused_vehicle_cumul);
3534 return CompactAssignmentInternal(assignment,
false);
3539 return CompactAssignmentInternal(assignment,
true);
3542Assignment* RoutingModel::CompactAssignmentInternal(
3543 const Assignment& assignment,
bool check_compact_assignment)
const {
3547 <<
"The costs are not homogeneous, routes cannot be rearranged";
3551 std::unique_ptr<Assignment> compact_assignment(
new Assignment(&assignment));
3552 for (
int vehicle = 0; vehicle < vehicles_ - 1; ++vehicle) {
3556 const int vehicle_start =
Start(vehicle);
3557 const int vehicle_end =
End(vehicle);
3559 int swap_vehicle = vehicles_ - 1;
3560 bool has_more_vehicles_with_route =
false;
3561 for (; swap_vehicle > vehicle; --swap_vehicle) {
3568 has_more_vehicles_with_route =
true;
3569 const int swap_vehicle_start =
Start(swap_vehicle);
3570 const int swap_vehicle_end =
End(swap_vehicle);
3571 if (manager_.IndexToNode(vehicle_start) !=
3572 manager_.IndexToNode(swap_vehicle_start) ||
3573 manager_.IndexToNode(vehicle_end) !=
3574 manager_.IndexToNode(swap_vehicle_end)) {
3579 if (RouteCanBeUsedByVehicle(*compact_assignment, swap_vehicle_start,
3585 if (swap_vehicle == vehicle) {
3586 if (has_more_vehicles_with_route) {
3590 LOG(
INFO) <<
"No vehicle that can be swapped with " << vehicle
3597 if (!ReplaceUnusedVehicle(vehicle, swap_vehicle,
3598 compact_assignment.get())) {
3603 if (check_compact_assignment &&
3604 !solver_->CheckAssignment(compact_assignment.get())) {
3606 LOG(
WARNING) <<
"The compacted assignment is not a valid solution";
3609 return compact_assignment.release();
3612int RoutingModel::FindNextActive(
int index,
3613 const std::vector<int64_t>& indices)
const {
3616 const int size = indices.size();
3627 preassignment_->Clear();
3628 IntVar* next_var =
nullptr;
3629 int lock_index = FindNextActive(-1, locks);
3630 const int size = locks.size();
3631 if (lock_index < size) {
3632 next_var =
NextVar(locks[lock_index]);
3633 preassignment_->Add(next_var);
3634 for (lock_index = FindNextActive(lock_index, locks); lock_index < size;
3635 lock_index = FindNextActive(lock_index, locks)) {
3636 preassignment_->SetValue(next_var, locks[lock_index]);
3637 next_var =
NextVar(locks[lock_index]);
3638 preassignment_->Add(next_var);
3645 const std::vector<std::vector<int64_t>>& locks,
bool close_routes) {
3646 preassignment_->Clear();
3653 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3661 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3667 if (collect_assignments_->solution_count() == 1 && assignment_ !=
nullptr) {
3668 assignment_->CopyIntersection(collect_assignments_->solution(0));
3669 return assignment_->Save(file_name);
3677 CHECK(assignment_ !=
nullptr);
3678 if (assignment_->Load(file_name)) {
3679 return DoRestoreAssignment();
3686 CHECK(assignment_ !=
nullptr);
3687 assignment_->CopyIntersection(&solution);
3688 return DoRestoreAssignment();
3691Assignment* RoutingModel::DoRestoreAssignment() {
3695 solver_->Solve(restore_assignment_, monitors_);
3696 if (collect_assignments_->solution_count() == 1) {
3698 return collect_assignments_->solution(0);
3707 const std::vector<std::vector<int64_t>>& routes,
3708 bool ignore_inactive_indices,
bool close_routes,
3710 CHECK(assignment !=
nullptr);
3712 LOG(
ERROR) <<
"The model is not closed yet";
3715 const int num_routes = routes.size();
3716 if (num_routes > vehicles_) {
3717 LOG(
ERROR) <<
"The number of vehicles in the assignment (" << routes.size()
3718 <<
") is greater than the number of vehicles in the model ("
3719 << vehicles_ <<
")";
3723 absl::flat_hash_set<int> visited_indices;
3725 for (
int vehicle = 0; vehicle < num_routes; ++vehicle) {
3726 const std::vector<int64_t>& route = routes[vehicle];
3727 int from_index =
Start(vehicle);
3728 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3729 visited_indices.insert(from_index);
3730 if (!insert_result.second) {
3731 LOG(
ERROR) <<
"Index " << from_index <<
" (start node for vehicle "
3732 << vehicle <<
") was already used";
3736 for (
const int64_t to_index : route) {
3737 if (to_index < 0 || to_index >=
Size()) {
3738 LOG(
ERROR) <<
"Invalid index: " << to_index;
3743 if (active_var->
Max() == 0) {
3744 if (ignore_inactive_indices) {
3747 LOG(
ERROR) <<
"Index " << to_index <<
" is not active";
3752 insert_result = visited_indices.insert(to_index);
3753 if (!insert_result.second) {
3754 LOG(
ERROR) <<
"Index " << to_index <<
" is used multiple times";
3759 if (!vehicle_var->
Contains(vehicle)) {
3760 LOG(
ERROR) <<
"Vehicle " << vehicle <<
" is not allowed at index "
3766 if (!assignment->
Contains(from_var)) {
3767 assignment->
Add(from_var);
3769 assignment->
SetValue(from_var, to_index);
3771 from_index = to_index;
3776 if (!assignment->
Contains(last_var)) {
3777 assignment->
Add(last_var);
3784 for (
int vehicle = num_routes; vehicle < vehicles_; ++vehicle) {
3785 const int start_index =
Start(vehicle);
3788 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3789 visited_indices.insert(start_index);
3790 if (!insert_result.second) {
3791 LOG(
ERROR) <<
"Index " << start_index <<
" is used multiple times";
3796 if (!assignment->
Contains(start_var)) {
3797 assignment->
Add(start_var);
3808 if (!assignment->
Contains(next_var)) {
3809 assignment->
Add(next_var);
3820 const std::vector<std::vector<int64_t>>& routes,
3821 bool ignore_inactive_indices) {
3829 return DoRestoreAssignment();
3834 std::vector<std::vector<int64_t>>*
const routes)
const {
3836 CHECK(routes !=
nullptr);
3838 const int model_size =
Size();
3839 routes->resize(vehicles_);
3840 for (
int vehicle = 0; vehicle < vehicles_; ++vehicle) {
3841 std::vector<int64_t>*
const vehicle_route = &routes->at(vehicle);
3842 vehicle_route->clear();
3844 int num_visited_indices = 0;
3845 const int first_index =
Start(vehicle);
3849 int current_index = assignment.
Value(first_var);
3850 while (!
IsEnd(current_index)) {
3851 vehicle_route->push_back(current_index);
3856 current_index = assignment.
Value(next_var);
3858 ++num_visited_indices;
3859 CHECK_LE(num_visited_indices, model_size)
3860 <<
"The assignment contains a cycle";
3868 std::vector<std::vector<int64_t>> route_indices(
vehicles());
3869 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3871 LOG(DFATAL) <<
"GetRoutesFromAssignment() called on incomplete solution:"
3872 <<
" NextVar(" << vehicle <<
") is unbound.";
3875 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3877 route_indices[vehicle].push_back(
index);
3880 route_indices[vehicle].push_back(
index);
3883 return route_indices;
3887int64_t RoutingModel::GetArcCostForClassInternal(
3888 int64_t from_index, int64_t to_index,
3892 DCHECK_LT(cost_class_index, cost_classes_.size());
3893 CostCacheElement*
const cache = &cost_cache_[from_index];
3895 if (cache->index ==
static_cast<int>(to_index) &&
3896 cache->cost_class_index == cost_class_index) {
3900 const CostClass& cost_class = cost_classes_[cost_class_index];
3901 const auto& evaluator = transit_evaluators_[cost_class.evaluator_index];
3903 cost =
CapAdd(evaluator(from_index, to_index),
3904 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3905 }
else if (!
IsEnd(to_index)) {
3909 evaluator(from_index, to_index),
3910 CapAdd(GetDimensionTransitCostSum(from_index, to_index, cost_class),
3911 fixed_cost_of_vehicle_[index_to_vehicle_[from_index]]));
3915 if (vehicle_used_when_empty_[index_to_vehicle_[from_index]]) {
3917 CapAdd(evaluator(from_index, to_index),
3918 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3923 *cache = {
static_cast<int>(to_index), cost_class_index,
cost};
3932 int vehicle)
const {
3946 return assignment.
Value(next_var);
3950 int64_t vehicle)
const {
3951 if (from_index != to_index && vehicle >= 0) {
3952 return GetArcCostForClassInternal(from_index, to_index,
3960 int64_t from_index, int64_t to_index,
3961 int64_t cost_class_index)
const {
3962 if (from_index != to_index) {
3963 return GetArcCostForClassInternal(from_index, to_index,
3971 int64_t to_index)
const {
3975 if (!is_bound_to_end_ct_added_.Switched()) {
3978 std::vector<IntVar*> zero_transit(
Size(), solver_->MakeIntConst(0));
3979 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
3980 nexts_, active_, is_bound_to_end_, zero_transit));
3981 is_bound_to_end_ct_added_.Switch(solver_.get());
3983 if (is_bound_to_end_[to_index]->Min() == 1)
3989int64_t RoutingModel::GetDimensionTransitCostSum(
3990 int64_t i, int64_t j,
const CostClass& cost_class)
const {
3992 for (
const auto& evaluator_and_coefficient :
3993 cost_class.dimension_transit_evaluator_class_and_cost_coefficient) {
3994 DCHECK_GT(evaluator_and_coefficient.cost_coefficient, 0);
3997 CapProd(evaluator_and_coefficient.cost_coefficient,
3998 evaluator_and_coefficient.dimension->GetTransitValueFromClass(
3999 i, j, evaluator_and_coefficient.transit_evaluator_class)));
4015 const bool mandatory1 = active_[to1]->Min() == 1;
4016 const bool mandatory2 = active_[to2]->Min() == 1;
4018 if (mandatory1 != mandatory2)
return mandatory1;
4025 const int64_t src_vehicle = src_vehicle_var->
Max();
4026 if (src_vehicle_var->
Bound()) {
4033 mandatory1 ? to1_vehicle_var->
Bound() : (to1_vehicle_var->
Size() <= 2);
4035 mandatory2 ? to2_vehicle_var->
Bound() : (to2_vehicle_var->
Size() <= 2);
4038 if (bound1 != bound2)
return bound1;
4041 const int64_t vehicle1 = to1_vehicle_var->
Max();
4042 const int64_t vehicle2 = to2_vehicle_var->
Max();
4045 if ((vehicle1 == src_vehicle) != (vehicle2 == src_vehicle)) {
4046 return vehicle1 == src_vehicle;
4051 if (vehicle1 != src_vehicle)
return to1 < to2;
4062 const std::vector<IntVar*>& cumul_vars =
4064 IntVar*
const dim1 = cumul_vars[to1];
4065 IntVar*
const dim2 = cumul_vars[to2];
4068 if (dim1->
Max() != dim2->
Max())
return dim1->
Max() < dim2->
Max();
4077 const int64_t cost_class_index =
4078 SafeGetCostClassInt64OfVehicle(src_vehicle);
4079 const int64_t cost1 =
4082 const int64_t cost2 =
4085 if (cost1 != cost2)
return cost1 < cost2;
4092 if (num_vehicles1 != num_vehicles2)
return num_vehicles1 < num_vehicles2;
4102 DCHECK_EQ(index_to_visit_type_.size(), index_to_type_policy_.size());
4103 index_to_visit_type_[
index] = type;
4104 index_to_type_policy_[
index] = policy;
4105 num_visit_types_ =
std::max(num_visit_types_, type + 1);
4110 return index_to_visit_type_[
index];
4114 DCHECK_LT(type, single_nodes_of_type_.size());
4115 return single_nodes_of_type_[type];
4119 DCHECK_LT(type, pair_indices_of_type_.size());
4120 return pair_indices_of_type_[type];
4124 int64_t
index)
const {
4126 return index_to_type_policy_[
index];
4130 hard_incompatible_types_per_type_index_.resize(num_visit_types_);
4131 temporal_incompatible_types_per_type_index_.resize(num_visit_types_);
4132 same_vehicle_required_type_alternatives_per_type_index_.resize(
4134 required_type_alternatives_when_adding_type_index_.resize(num_visit_types_);
4135 required_type_alternatives_when_removing_type_index_.resize(num_visit_types_);
4140 hard_incompatible_types_per_type_index_.size());
4141 has_hard_type_incompatibilities_ =
true;
4143 hard_incompatible_types_per_type_index_[type1].insert(type2);
4144 hard_incompatible_types_per_type_index_[type2].insert(type1);
4149 temporal_incompatible_types_per_type_index_.size());
4150 has_temporal_type_incompatibilities_ =
true;
4152 temporal_incompatible_types_per_type_index_[type1].insert(type2);
4153 temporal_incompatible_types_per_type_index_[type2].insert(type1);
4156const absl::flat_hash_set<int>&
4159 DCHECK_LT(type, hard_incompatible_types_per_type_index_.size());
4160 return hard_incompatible_types_per_type_index_[type];
4163const absl::flat_hash_set<int>&
4166 DCHECK_LT(type, temporal_incompatible_types_per_type_index_.size());
4167 return temporal_incompatible_types_per_type_index_[type];
4173 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4175 same_vehicle_required_type_alternatives_per_type_index_.size());
4177 if (required_type_alternatives.empty()) {
4181 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4182 trivially_infeasible_visit_types_to_policies_[dependent_type];
4189 has_same_vehicle_type_requirements_ =
true;
4190 same_vehicle_required_type_alternatives_per_type_index_[dependent_type]
4191 .push_back(std::move(required_type_alternatives));
4195 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4197 required_type_alternatives_when_adding_type_index_.size());
4199 if (required_type_alternatives.empty()) {
4203 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4204 trivially_infeasible_visit_types_to_policies_[dependent_type];
4210 has_temporal_type_requirements_ =
true;
4211 required_type_alternatives_when_adding_type_index_[dependent_type].push_back(
4212 std::move(required_type_alternatives));
4216 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4218 required_type_alternatives_when_removing_type_index_.size());
4220 if (required_type_alternatives.empty()) {
4224 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4225 trivially_infeasible_visit_types_to_policies_[dependent_type];
4232 has_temporal_type_requirements_ =
true;
4233 required_type_alternatives_when_removing_type_index_[dependent_type]
4234 .push_back(std::move(required_type_alternatives));
4237const std::vector<absl::flat_hash_set<int>>&
4241 same_vehicle_required_type_alternatives_per_type_index_.size());
4242 return same_vehicle_required_type_alternatives_per_type_index_[type];
4245const std::vector<absl::flat_hash_set<int>>&
4248 DCHECK_LT(type, required_type_alternatives_when_adding_type_index_.size());
4249 return required_type_alternatives_when_adding_type_index_[type];
4252const std::vector<absl::flat_hash_set<int>>&
4255 DCHECK_LT(type, required_type_alternatives_when_removing_type_index_.size());
4256 return required_type_alternatives_when_removing_type_index_[type];
4264 int64_t var_index)
const {
4265 if (active_[var_index]->Min() == 1)
4267 const std::vector<DisjunctionIndex>& disjunction_indices =
4269 if (disjunction_indices.size() != 1)
return default_value;
4274 return std::max(int64_t{0}, disjunctions_[disjunction_index].value.penalty);
4279 const std::string& dimension_to_print)
const {
4280 for (
int i = 0; i <
Size(); ++i) {
4283 <<
"DebugOutputVehicleSchedules() called on incomplete solution:"
4284 <<
" NextVar(" << i <<
") is unbound.";
4289 absl::flat_hash_set<std::string> dimension_names;
4290 if (dimension_to_print.empty()) {
4292 dimension_names.insert(all_dimension_names.begin(),
4293 all_dimension_names.end());
4295 dimension_names.insert(dimension_to_print);
4297 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4298 int empty_vehicle_range_start = vehicle;
4303 if (empty_vehicle_range_start != vehicle) {
4304 if (empty_vehicle_range_start == vehicle - 1) {
4305 absl::StrAppendFormat(&output,
"Vehicle %d: empty",
4306 empty_vehicle_range_start);
4308 absl::StrAppendFormat(&output,
"Vehicles %d-%d: empty",
4309 empty_vehicle_range_start, vehicle - 1);
4311 output.append(
"\n");
4314 absl::StrAppendFormat(&output,
"Vehicle %d:", vehicle);
4318 absl::StrAppendFormat(&output,
"%d Vehicle(%d) ",
index,
4319 solution_assignment.
Value(vehicle_var));
4323 absl::StrAppendFormat(&output,
"%s(%d..%d) ", dimension->name(),
4324 solution_assignment.
Min(
var),
4325 solution_assignment.
Max(
var));
4330 if (
IsEnd(
index)) output.append(
"Route end ");
4332 output.append(
"\n");
4335 output.append(
"Unperformed nodes: ");
4336 bool has_unperformed =
false;
4337 for (
int i = 0; i <
Size(); ++i) {
4340 absl::StrAppendFormat(&output,
"%d ", i);
4341 has_unperformed =
true;
4344 if (!has_unperformed) output.append(
"None");
4345 output.append(
"\n");
4350std::vector<std::vector<std::pair<int64_t, int64_t>>>
4353 std::vector<std::vector<std::pair<int64_t, int64_t>>> cumul_bounds(
4355 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4357 LOG(DFATAL) <<
"GetCumulBounds() called on incomplete solution:"
4358 <<
" NextVar(" << vehicle <<
") is unbound.";
4362 for (
int vehicle_id = 0; vehicle_id <
vehicles(); ++vehicle_id) {
4365 cumul_bounds[vehicle_id].emplace_back(solution_assignment.
Min(dim_var),
4366 solution_assignment.
Max(dim_var));
4370 cumul_bounds[vehicle_id].emplace_back(solution_assignment.
Min(dim_var),
4371 solution_assignment.
Max(dim_var));
4374 return cumul_bounds;
4378Assignment* RoutingModel::GetOrCreateAssignment() {
4379 if (assignment_ ==
nullptr) {
4380 assignment_ = solver_->MakeAssignment();
4381 assignment_->Add(nexts_);
4383 assignment_->Add(vehicle_vars_);
4385 assignment_->AddObjective(cost_);
4390Assignment* RoutingModel::GetOrCreateTmpAssignment() {
4391 if (tmp_assignment_ ==
nullptr) {
4392 tmp_assignment_ = solver_->MakeAssignment();
4393 tmp_assignment_->Add(nexts_);
4395 return tmp_assignment_;
4398RegularLimit* RoutingModel::GetOrCreateLimit() {
4399 if (limit_ ==
nullptr) {
4400 limit_ = solver_->MakeLimit(
4408RegularLimit* RoutingModel::GetOrCreateLocalSearchLimit() {
4409 if (ls_limit_ ==
nullptr) {
4410 ls_limit_ = solver_->MakeLimit(absl::InfiniteDuration(),
4418RegularLimit* RoutingModel::GetOrCreateLargeNeighborhoodSearchLimit() {
4419 if (lns_limit_ ==
nullptr) {
4420 lns_limit_ = solver_->MakeLimit(
4429RoutingModel::GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit() {
4430 if (first_solution_lns_limit_ ==
nullptr) {
4431 first_solution_lns_limit_ = solver_->MakeLimit(
4436 return first_solution_lns_limit_;
4439LocalSearchOperator* RoutingModel::CreateInsertionOperator() {
4440 LocalSearchOperator* insertion_operator =
4441 CreateCPOperator<MakeActiveOperator>();
4442 if (!pickup_delivery_pairs_.empty()) {
4443 insertion_operator = solver_->ConcatenateOperators(
4444 {CreatePairOperator<MakePairActiveOperator>(), insertion_operator});
4446 if (!implicit_pickup_delivery_pairs_without_alternatives_.empty()) {
4447 insertion_operator = solver_->ConcatenateOperators(
4448 {CreateOperator<MakePairActiveOperator>(
4449 implicit_pickup_delivery_pairs_without_alternatives_),
4450 insertion_operator});
4452 return insertion_operator;
4455LocalSearchOperator* RoutingModel::CreateMakeInactiveOperator() {
4456 LocalSearchOperator* make_inactive_operator =
4457 CreateCPOperator<MakeInactiveOperator>();
4458 if (!pickup_delivery_pairs_.empty()) {
4459 make_inactive_operator = solver_->ConcatenateOperators(
4460 {CreatePairOperator<MakePairInactiveOperator>(),
4461 make_inactive_operator});
4463 return make_inactive_operator;
4466void RoutingModel::CreateNeighborhoodOperators(
4468 local_search_operators_.clear();
4469 local_search_operators_.resize(LOCAL_SEARCH_OPERATOR_COUNTER,
nullptr);
4473 std::pair<RoutingLocalSearchOperator, Solver::LocalSearchOperators>>
4478 for (
const auto [type, op] : operator_by_type) {
4479 local_search_operators_[type] =
4481 ? solver_->MakeOperator(nexts_, op)
4482 : solver_->MakeOperator(nexts_, vehicle_vars_, op);
4487 const std::vector<std::pair<RoutingLocalSearchOperator,
4489 operator_by_type = {{LIN_KERNIGHAN,
Solver::LK},
4492 for (
const auto [type, op] : operator_by_type) {
4495 local_search_operators_[type] =
4497 ? solver_->MakeOperator(nexts_, std::move(arc_cost), op)
4498 : solver_->MakeOperator(nexts_, vehicle_vars_,
4499 std::move(arc_cost), op);
4504 local_search_operators_[RELOCATE] = CreateCPOperator<Relocate>();
4505 local_search_operators_[EXCHANGE] = CreateCPOperator<Exchange>();
4506 local_search_operators_[CROSS] = CreateCPOperator<Cross>();
4507 local_search_operators_[TWO_OPT] = CreateCPOperator<TwoOpt>();
4508 local_search_operators_[RELOCATE_AND_MAKE_ACTIVE] =
4509 CreateCPOperator<RelocateAndMakeActiveOperator>();
4510 local_search_operators_[MAKE_ACTIVE_AND_RELOCATE] =
4511 CreateCPOperator<MakeActiveAndRelocate>();
4512 local_search_operators_[MAKE_CHAIN_INACTIVE] =
4513 CreateCPOperator<MakeChainInactiveOperator>();
4514 local_search_operators_[SWAP_ACTIVE] = CreateCPOperator<SwapActiveOperator>();
4515 local_search_operators_[EXTENDED_SWAP_ACTIVE] =
4516 CreateCPOperator<ExtendedSwapActiveOperator>();
4519 local_search_operators_[MAKE_ACTIVE] = CreateInsertionOperator();
4520 local_search_operators_[MAKE_INACTIVE] = CreateMakeInactiveOperator();
4521 local_search_operators_[RELOCATE_PAIR] =
4522 CreatePairOperator<PairRelocateOperator>();
4523 std::vector<LocalSearchOperator*> light_relocate_pair_operators;
4524 light_relocate_pair_operators.push_back(
4525 CreatePairOperator<LightPairRelocateOperator>());
4526 local_search_operators_[LIGHT_RELOCATE_PAIR] =
4527 solver_->ConcatenateOperators(light_relocate_pair_operators);
4528 local_search_operators_[EXCHANGE_PAIR] =
4529 CreatePairOperator<PairExchangeOperator>();
4530 local_search_operators_[EXCHANGE_RELOCATE_PAIR] =
4531 CreatePairOperator<PairExchangeRelocateOperator>();
4532 local_search_operators_[RELOCATE_NEIGHBORS] =
4533 CreateOperator<MakeRelocateNeighborsOperator>(
4535 local_search_operators_[NODE_PAIR_SWAP] = solver_->ConcatenateOperators(
4536 {CreatePairOperator<IndexPairSwapActiveOperator>(),
4537 CreatePairOperator<SwapIndexPairOperator>(),
4538 CreatePairOperator<PairNodeSwapActiveOperator<true>>(),
4539 CreatePairOperator<PairNodeSwapActiveOperator<false>>()});
4540 local_search_operators_[RELOCATE_SUBTRIP] =
4541 CreatePairOperator<RelocateSubtrip>();
4542 local_search_operators_[EXCHANGE_SUBTRIP] =
4543 CreatePairOperator<ExchangeSubtrip>();
4545 const auto arc_cost_for_path_start =
4546 [
this](int64_t before_node, int64_t after_node, int64_t start_index) {
4547 const int vehicle = index_to_vehicle_[start_index];
4548 const int64_t arc_cost =
4550 return (before_node != start_index ||
IsEnd(after_node))
4554 local_search_operators_[RELOCATE_EXPENSIVE_CHAIN] =
4555 solver_->RevAlloc(
new RelocateExpensiveChain(
4559 vehicle_start_class_callback_,
4560 parameters.relocate_expensive_chain_num_arcs_to_consider(),
4561 arc_cost_for_path_start));
4564 const auto make_global_cheapest_insertion_filtered_heuristic =
4566 using Heuristic = GlobalCheapestInsertionFilteredHeuristic;
4567 Heuristic::GlobalCheapestInsertionParameters ls_gci_parameters;
4568 ls_gci_parameters.is_sequential =
false;
4569 ls_gci_parameters.farthest_seeds_ratio = 0.0;
4570 ls_gci_parameters.neighbors_ratio =
4571 parameters.cheapest_insertion_ls_operator_neighbors_ratio();
4572 ls_gci_parameters.min_neighbors =
4573 parameters.cheapest_insertion_ls_operator_min_neighbors();
4574 ls_gci_parameters.use_neighbors_ratio_for_initialization =
true;
4575 ls_gci_parameters.add_unperformed_entries =
4576 parameters.cheapest_insertion_add_unperformed_entries();
4577 return absl::make_unique<Heuristic>(
4580 GetOrCreateFeasibilityFilterManager(
parameters), ls_gci_parameters);
4582 const auto make_local_cheapest_insertion_filtered_heuristic =
4584 return absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4586 GetOrCreateFeasibilityFilterManager(
parameters));
4588 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4589 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4590 make_global_cheapest_insertion_filtered_heuristic(),
4591 parameters.heuristic_close_nodes_lns_num_nodes()));
4593 local_search_operators_[LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4594 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4595 make_local_cheapest_insertion_filtered_heuristic(),
4596 parameters.heuristic_close_nodes_lns_num_nodes()));
4598 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_PATH_LNS] =
4599 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4600 make_global_cheapest_insertion_filtered_heuristic()));
4602 local_search_operators_[LOCAL_CHEAPEST_INSERTION_PATH_LNS] =
4603 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4604 make_local_cheapest_insertion_filtered_heuristic()));
4606 local_search_operators_
4607 [RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED] =
4609 new RelocatePathAndHeuristicInsertUnperformedOperator(
4610 make_global_cheapest_insertion_filtered_heuristic()));
4612 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4613 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4614 make_global_cheapest_insertion_filtered_heuristic(),
4615 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4616 arc_cost_for_path_start));
4618 local_search_operators_[LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4619 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4620 make_local_cheapest_insertion_filtered_heuristic(),
4621 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4622 arc_cost_for_path_start));
4625#define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators) \
4626 if (search_parameters.local_search_operators().use_##operator_method() == \
4628 operators.push_back(local_search_operators_[operator_type]); \
4631LocalSearchOperator* RoutingModel::ConcatenateOperators(
4632 const RoutingSearchParameters& search_parameters,
4633 const std::vector<LocalSearchOperator*>& operators)
const {
4634 if (search_parameters.use_multi_armed_bandit_concatenate_operators()) {
4635 return solver_->MultiArmedBanditConcatenateOperators(
4638 .multi_armed_bandit_compound_operator_memory_coefficient(),
4640 .multi_armed_bandit_compound_operator_exploration_coefficient(),
4643 return solver_->ConcatenateOperators(operators);
4646LocalSearchOperator* RoutingModel::GetNeighborhoodOperators(
4647 const RoutingSearchParameters& search_parameters)
const {
4648 std::vector<LocalSearchOperator*> operator_groups;
4649 std::vector<LocalSearchOperator*> operators = extra_operators_;
4650 if (!pickup_delivery_pairs_.empty()) {
4654 if (search_parameters.local_search_operators().use_relocate_pair() ==
4664 if (vehicles_ > 1) {
4675 if (!pickup_delivery_pairs_.empty() ||
4676 search_parameters.local_search_operators().use_relocate_neighbors() ==
4678 operators.push_back(local_search_operators_[RELOCATE_NEIGHBORS]);
4681 search_parameters.local_search_metaheuristic();
4683 local_search_metaheuristic !=
4685 local_search_metaheuristic !=
4693 if (!disjunctions_.empty()) {
4710 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4719 global_cheapest_insertion_path_lns, operators);
4721 local_cheapest_insertion_path_lns, operators);
4723 RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED,
4724 relocate_path_global_cheapest_insertion_insert_unperformed, operators);
4727 global_cheapest_insertion_expensive_chain_lns,
4730 local_cheapest_insertion_expensive_chain_lns,
4733 global_cheapest_insertion_close_nodes_lns,
4736 local_cheapest_insertion_close_nodes_lns, operators);
4737 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4742 local_search_metaheuristic !=
4744 local_search_metaheuristic !=
4749 local_search_metaheuristic !=
4751 local_search_metaheuristic !=
4757 if (!disjunctions_.empty()) {
4760 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4762 return solver_->ConcatenateOperators(operator_groups);
4765#undef CP_ROUTING_PUSH_OPERATOR
4769 if (dimension->GetUnaryTransitEvaluator(0) !=
nullptr)
return true;
4776void ConvertVectorInt64ToVectorInt(
const std::vector<int64_t>&
input,
4777 std::vector<int>* output) {
4778 const int n =
input.size();
4780 int* data = output->data();
4781 for (
int i = 0; i < n; ++i) {
4782 const int element =
static_cast<int>(
input[i]);
4790std::vector<LocalSearchFilterManager::FilterEvent>
4791RoutingModel::GetOrCreateLocalSearchFilters(
4792 const RoutingSearchParameters&
parameters,
bool filter_cost) {
4793 const auto kAccept = LocalSearchFilterManager::FilterEventType::kAccept;
4794 const auto kRelax = LocalSearchFilterManager::FilterEventType::kRelax;
4804 std::vector<LocalSearchFilterManager::FilterEvent> filters;
4806 if (filter_cost && vehicle_amortized_cost_factors_set_) {
4814 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4818 filters.push_back({sum, kAccept});
4820 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4821 nexts_, vehicle_vars_,
4822 [
this](int64_t i, int64_t j, int64_t k) {
4826 filters.push_back({sum, kAccept});
4830 filters.push_back({solver_->MakeVariableDomainFilter(), kAccept});
4832 if (vehicles_ > max_active_vehicles_) {
4836 if (!disjunctions_.empty()) {
4851 const PathState* path_state_reference =
nullptr;
4853 std::vector<int> path_starts;
4854 std::vector<int> path_ends;
4855 ConvertVectorInt64ToVectorInt(starts_, &path_starts);
4856 ConvertVectorInt64ToVectorInt(ends_, &path_ends);
4858 auto path_state = absl::make_unique<PathState>(
4859 Size() +
vehicles(), std::move(path_starts), std::move(path_ends));
4860 path_state_reference = path_state.get();
4871 if (!pickup_delivery_pairs_.empty()) {
4874 vehicle_pickup_delivery_policy_),
4887 if (!dimension->HasBreakConstraints())
continue;
4890 filters.insert(filters.end(), extra_filters_.begin(), extra_filters_.end());
4894LocalSearchFilterManager* RoutingModel::GetOrCreateLocalSearchFilterManager(
4896 if (!local_search_filter_manager_) {
4897 local_search_filter_manager_ =
4898 solver_->RevAlloc(
new LocalSearchFilterManager(
4901 return local_search_filter_manager_;
4904std::vector<LocalSearchFilterManager::FilterEvent>
4905RoutingModel::GetOrCreateFeasibilityFilters(
4907 return GetOrCreateLocalSearchFilters(
parameters,
false);
4910LocalSearchFilterManager* RoutingModel::GetOrCreateFeasibilityFilterManager(
4912 if (!feasibility_filter_manager_) {
4913 feasibility_filter_manager_ =
4914 solver_->RevAlloc(
new LocalSearchFilterManager(
4917 return feasibility_filter_manager_;
4920LocalSearchFilterManager*
4921RoutingModel::GetOrCreateStrongFeasibilityFilterManager(
4923 if (!strong_feasibility_filter_manager_) {
4924 std::vector<LocalSearchFilterManager::FilterEvent> filters =
4927 LocalSearchFilterManager::FilterEventType::kAccept});
4928 strong_feasibility_filter_manager_ =
4929 solver_->RevAlloc(
new LocalSearchFilterManager(std::move(filters)));
4931 return strong_feasibility_filter_manager_;
4936 for (
int vehicle = 0; vehicle < dimension.model()->
vehicles(); vehicle++) {
4937 if (!dimension.AreVehicleTransitsPositive(vehicle)) {
4945void RoutingModel::StoreDimensionCumulOptimizers(
4947 Assignment* packed_dimensions_collector_assignment =
4948 solver_->MakeAssignment();
4949 packed_dimensions_collector_assignment->AddObjective(
CostVar());
4950 const int num_dimensions = dimensions_.size();
4951 local_optimizer_index_.resize(num_dimensions, -1);
4952 global_optimizer_index_.resize(num_dimensions, -1);
4956 const int num_resource_groups =
4958 bool needs_optimizer =
false;
4959 if (dimension->global_span_cost_coefficient() > 0 ||
4960 !dimension->GetNodePrecedences().empty() || num_resource_groups > 1) {
4962 needs_optimizer =
true;
4963 global_optimizer_index_[dim] = global_dimension_optimizers_.size();
4964 global_dimension_optimizers_.push_back(
4965 absl::make_unique<GlobalDimensionCumulOptimizer>(
4966 dimension,
parameters.continuous_scheduling_solver()));
4967 global_dimension_mp_optimizers_.push_back(
4968 absl::make_unique<GlobalDimensionCumulOptimizer>(
4969 dimension,
parameters.mixed_integer_scheduling_solver()));
4970 if (!AllTransitsPositive(*dimension)) {
4971 dimension->SetOffsetForGlobalOptimizer(0);
4975 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4978 std::min(offset, dimension->CumulVar(
Start(vehicle))->Min() - 1);
4980 dimension->SetOffsetForGlobalOptimizer(
std::max(
Zero(), offset));
4984 bool has_span_cost =
false;
4985 bool has_span_limit =
false;
4986 std::vector<int64_t> vehicle_offsets(
vehicles());
4987 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4988 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4989 has_span_cost =
true;
4991 if (dimension->GetSpanUpperBoundForVehicle(vehicle) <
4993 has_span_limit =
true;
4996 vehicle_offsets[vehicle] =
4997 dimension->AreVehicleTransitsPositive(vehicle)
5001 bool has_soft_lower_bound =
false;
5002 bool has_soft_upper_bound =
false;
5003 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
5004 if (dimension->HasCumulVarSoftLowerBound(i)) {
5005 has_soft_lower_bound =
true;
5007 if (dimension->HasCumulVarSoftUpperBound(i)) {
5008 has_soft_upper_bound =
true;
5011 int num_linear_constraints = 0;
5012 if (has_span_cost) ++num_linear_constraints;
5013 if (has_span_limit) ++num_linear_constraints;
5014 if (dimension->HasSoftSpanUpperBounds()) ++num_linear_constraints;
5015 if (has_soft_lower_bound) ++num_linear_constraints;
5016 if (has_soft_upper_bound) ++num_linear_constraints;
5017 if (dimension->HasBreakConstraints()) ++num_linear_constraints;
5018 if (num_resource_groups > 0 || num_linear_constraints >= 2) {
5019 needs_optimizer =
true;
5020 dimension->SetVehicleOffsetsForLocalOptimizer(std::move(vehicle_offsets));
5021 local_optimizer_index_[dim] = local_dimension_optimizers_.size();
5022 local_dimension_optimizers_.push_back(
5023 absl::make_unique<LocalDimensionCumulOptimizer>(
5024 dimension,
parameters.continuous_scheduling_solver()));
5025 local_dimension_mp_optimizers_.push_back(
5026 absl::make_unique<LocalDimensionCumulOptimizer>(
5027 dimension,
parameters.mixed_integer_scheduling_solver()));
5029 if (needs_optimizer) {
5030 packed_dimensions_collector_assignment->Add(dimension->cumuls());
5037 for (IntVar*
const extra_var : extra_vars_) {
5038 packed_dimensions_collector_assignment->Add(extra_var);
5040 for (IntervalVar*
const extra_interval : extra_intervals_) {
5041 packed_dimensions_collector_assignment->Add(extra_interval);
5044 packed_dimensions_assignment_collector_ = solver_->MakeFirstSolutionCollector(
5045 packed_dimensions_collector_assignment);
5052 bool has_soft_or_span_cost =
false;
5053 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
5054 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
5055 has_soft_or_span_cost =
true;
5059 if (!has_soft_or_span_cost) {
5060 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
5061 if (dimension->HasCumulVarSoftUpperBound(i) ||
5062 dimension->HasCumulVarSoftLowerBound(i)) {
5063 has_soft_or_span_cost =
true;
5068 if (has_soft_or_span_cost)
dimensions.push_back(dimension);
5074RoutingModel::CreateFinalizerForMinimizedAndMaximizedVariables() {
5075 std::stable_sort(finalizer_variable_cost_pairs_.begin(),
5076 finalizer_variable_cost_pairs_.end(),
5077 [](
const std::pair<IntVar*, int64_t>& var_cost1,
5078 const std::pair<IntVar*, int64_t>& var_cost2) {
5079 return var_cost1.second > var_cost2.second;
5081 const int num_variables = finalizer_variable_cost_pairs_.size() +
5082 finalizer_variable_target_pairs_.size();
5083 std::vector<IntVar*> variables;
5084 std::vector<int64_t> targets;
5085 variables.reserve(num_variables);
5086 targets.reserve(num_variables);
5087 for (
const auto& variable_cost : finalizer_variable_cost_pairs_) {
5088 variables.push_back(variable_cost.first);
5091 for (
const auto& variable_target : finalizer_variable_target_pairs_) {
5092 variables.push_back(variable_target.first);
5093 targets.push_back(variable_target.second);
5096 std::move(targets));
5099DecisionBuilder* RoutingModel::CreateSolutionFinalizer(SearchLimit* lns_limit) {
5100 std::vector<DecisionBuilder*> decision_builders;
5101 decision_builders.push_back(solver_->MakePhase(
5103 bool routes_are_interdependent =
false;
5107 routes_are_interdependent =
true;
5111 if (!routes_are_interdependent) {
5116 decision_builders.push_back(
5119 if (!local_dimension_optimizers_.empty()) {
5120 decision_builders.push_back(
5121 solver_->RevAlloc(
new SetCumulsFromLocalDimensionCosts(
5122 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
5138 LocalDimensionCumulOptimizer*
const optimizer =
5141 LocalDimensionCumulOptimizer*
const mp_optimizer =
5144 decision_builders.push_back(
5145 solver_->RevAlloc(
new SetCumulsFromResourceAssignmentCosts(
5146 optimizer, mp_optimizer, lns_limit)));
5149 if (!global_dimension_optimizers_.empty()) {
5150 decision_builders.push_back(
5151 solver_->RevAlloc(
new SetCumulsFromGlobalDimensionCosts(
5152 &global_dimension_optimizers_, &global_dimension_mp_optimizers_,
5155 decision_builders.push_back(
5156 CreateFinalizerForMinimizedAndMaximizedVariables());
5158 return solver_->Compose(decision_builders);
5161void RoutingModel::CreateFirstSolutionDecisionBuilders(
5162 const RoutingSearchParameters& search_parameters) {
5163 first_solution_decision_builders_.resize(
5165 first_solution_filtered_decision_builders_.resize(
5167 DecisionBuilder*
const finalize_solution =
5168 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit());
5170 first_solution_decision_builders_
5173 first_solution_decision_builders_
5176 [
this](int64_t i, int64_t j) {
5189 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5190 first_solution_filtered_decision_builders_
5192 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5193 absl::make_unique<EvaluatorCheapestAdditionFilteredHeuristic>(
5195 [
this](int64_t i, int64_t j) {
5198 GetOrCreateFeasibilityFilterManager(search_parameters))));
5199 first_solution_decision_builders_
5201 solver_->Try(first_solution_filtered_decision_builders_
5203 first_solution_decision_builders_
5212 first_solution_decision_builders_
5215 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5216 first_solution_filtered_decision_builders_
5218 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5219 absl::make_unique<ComparatorCheapestAdditionFilteredHeuristic>(
5221 GetOrCreateFeasibilityFilterManager(search_parameters))));
5222 first_solution_decision_builders_
5224 first_solution_filtered_decision_builders_
5226 first_solution_decision_builders_
5230 if (first_solution_evaluator_ !=
nullptr) {
5231 first_solution_decision_builders_
5235 first_solution_decision_builders_
5242 RegularLimit*
const ls_limit = solver_->MakeLimit(
5246 DecisionBuilder*
const finalize = solver_->MakeSolveOnce(
5247 finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit());
5248 LocalSearchPhaseParameters*
const insertion_parameters =
5249 solver_->MakeLocalSearchPhaseParameters(
5250 nullptr, CreateInsertionOperator(), finalize, ls_limit,
5251 GetOrCreateLocalSearchFilterManager(search_parameters));
5252 std::vector<IntVar*> decision_vars = nexts_;
5254 decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(),
5255 vehicle_vars_.end());
5257 const int64_t optimization_step =
std::max(
5260 solver_->MakeNestedOptimize(
5262 insertion_parameters),
5263 GetOrCreateAssignment(),
false, optimization_step);
5265 solver_->Compose(first_solution_decision_builders_
5270 GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
5272 gci_parameters.is_sequential =
false;
5273 gci_parameters.farthest_seeds_ratio =
5274 search_parameters.cheapest_insertion_farthest_seeds_ratio();
5275 gci_parameters.neighbors_ratio =
5276 search_parameters.cheapest_insertion_first_solution_neighbors_ratio();
5277 gci_parameters.min_neighbors =
5278 search_parameters.cheapest_insertion_first_solution_min_neighbors();
5279 gci_parameters.use_neighbors_ratio_for_initialization =
5281 .cheapest_insertion_first_solution_use_neighbors_ratio_for_initialization();
5282 gci_parameters.add_unperformed_entries =
5283 search_parameters.cheapest_insertion_add_unperformed_entries();
5284 for (
bool is_sequential : {
false,
true}) {
5288 gci_parameters.is_sequential = is_sequential;
5290 first_solution_filtered_decision_builders_[first_solution_strategy] =
5291 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5292 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5294 [
this](int64_t i, int64_t j, int64_t vehicle) {
5298 GetOrCreateFeasibilityFilterManager(search_parameters),
5300 IntVarFilteredDecisionBuilder*
const strong_gci =
5301 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5302 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5304 [
this](int64_t i, int64_t j, int64_t vehicle) {
5308 GetOrCreateStrongFeasibilityFilterManager(search_parameters),
5310 first_solution_decision_builders_[first_solution_strategy] = solver_->Try(
5311 first_solution_filtered_decision_builders_[first_solution_strategy],
5312 solver_->Try(strong_gci, first_solution_decision_builders_
5317 first_solution_filtered_decision_builders_
5319 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5320 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5322 [
this](int64_t i, int64_t j, int64_t vehicle) {
5325 GetOrCreateFeasibilityFilterManager(search_parameters))));
5326 IntVarFilteredDecisionBuilder*
const strong_lci =
5327 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5328 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5330 [
this](int64_t i, int64_t j, int64_t vehicle) {
5333 GetOrCreateStrongFeasibilityFilterManager(search_parameters))));
5334 first_solution_decision_builders_
5336 first_solution_filtered_decision_builders_
5338 solver_->Try(strong_lci,
5339 first_solution_decision_builders_
5342 SavingsFilteredHeuristic::SavingsParameters savings_parameters;
5343 savings_parameters.neighbors_ratio =
5344 search_parameters.savings_neighbors_ratio();
5345 savings_parameters.max_memory_usage_bytes =
5346 search_parameters.savings_max_memory_usage_bytes();
5347 savings_parameters.add_reverse_arcs =
5348 search_parameters.savings_add_reverse_arcs();
5349 savings_parameters.arc_coefficient =
5350 search_parameters.savings_arc_coefficient();
5351 LocalSearchFilterManager* filter_manager =
nullptr;
5352 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5353 filter_manager = GetOrCreateFeasibilityFilterManager(search_parameters);
5356 if (search_parameters.savings_parallel_routes()) {
5357 IntVarFilteredDecisionBuilder* savings_db =
5358 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5359 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5360 this, savings_parameters, filter_manager)));
5361 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5362 first_solution_filtered_decision_builders_
5367 solver_->Try(savings_db,
5368 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5369 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5370 this, savings_parameters,
5371 GetOrCreateStrongFeasibilityFilterManager(
5372 search_parameters)))));
5374 IntVarFilteredDecisionBuilder* savings_db =
5375 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5376 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5377 this, savings_parameters, filter_manager)));
5378 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5379 first_solution_filtered_decision_builders_
5384 solver_->Try(savings_db,
5385 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5386 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5387 this, savings_parameters,
5388 GetOrCreateStrongFeasibilityFilterManager(
5389 search_parameters)))));
5401 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5402 absl::make_unique<ChristofidesFilteredHeuristic>(
5403 this, GetOrCreateFeasibilityFilterManager(search_parameters),
5404 search_parameters.christofides_use_minimum_matching())));
5406 const bool has_precedences = std::any_of(
5407 dimensions_.begin(), dimensions_.end(),
5409 bool has_single_vehicle_node =
false;
5410 for (
int node = 0; node <
Size(); node++) {
5411 if (!
IsStart(node) && !
IsEnd(node) && allowed_vehicles_[node].size() == 1) {
5412 has_single_vehicle_node =
true;
5416 automatic_first_solution_strategy_ =
5418 has_precedences, has_single_vehicle_node);
5420 first_solution_decision_builders_[automatic_first_solution_strategy_];
5429 if (first_solution_decision_builders_[strategy] ==
nullptr ||
5433 const std::string strategy_name =
5435 const std::string& log_tag = search_parameters.log_tag();
5436 if (!log_tag.empty() && log_tag != strategy_name) {
5437 first_solution_decision_builders_[strategy]->set_name(absl::StrFormat(
5438 "%s / %s", strategy_name, search_parameters.log_tag()));
5440 first_solution_decision_builders_[strategy]->set_name(strategy_name);
5445DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder(
5446 const RoutingSearchParameters& search_parameters)
const {
5448 search_parameters.first_solution_strategy();
5449 if (first_solution_strategy < first_solution_decision_builders_.size()) {
5450 return first_solution_decision_builders_[first_solution_strategy];
5456IntVarFilteredDecisionBuilder*
5457RoutingModel::GetFilteredFirstSolutionDecisionBuilderOrNull(
5458 const RoutingSearchParameters& search_parameters)
const {
5460 search_parameters.first_solution_strategy();
5461 return first_solution_filtered_decision_builders_[first_solution_strategy];
5464LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters(
5465 const RoutingSearchParameters& search_parameters) {
5466 SearchLimit* lns_limit = GetOrCreateLargeNeighborhoodSearchLimit();
5467 return solver_->MakeLocalSearchPhaseParameters(
5468 CostVar(), GetNeighborhoodOperators(search_parameters),
5469 solver_->MakeSolveOnce(CreateSolutionFinalizer(lns_limit), lns_limit),
5470 GetOrCreateLocalSearchLimit(),
5471 GetOrCreateLocalSearchFilterManager(search_parameters));
5474DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder(
5475 const RoutingSearchParameters& search_parameters) {
5476 const int size =
Size();
5477 DecisionBuilder* first_solution =
5478 GetFirstSolutionDecisionBuilder(search_parameters);
5479 LocalSearchPhaseParameters*
const parameters =
5480 CreateLocalSearchParameters(search_parameters);
5481 SearchLimit* first_solution_lns_limit =
5482 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5483 DecisionBuilder*
const first_solution_sub_decision_builder =
5484 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_solution_lns_limit),
5485 first_solution_lns_limit);
5487 return solver_->MakeLocalSearchPhase(nexts_, first_solution,
5488 first_solution_sub_decision_builder,
5491 const int all_size = size + size + vehicles_;
5492 std::vector<IntVar*> all_vars(all_size);
5493 for (
int i = 0; i < size; ++i) {
5494 all_vars[i] = nexts_[i];
5496 for (
int i = size; i < all_size; ++i) {
5497 all_vars[i] = vehicle_vars_[i - size];
5499 return solver_->MakeLocalSearchPhase(all_vars, first_solution,
5500 first_solution_sub_decision_builder,
5505void RoutingModel::SetupDecisionBuilders(
5506 const RoutingSearchParameters& search_parameters) {
5507 if (search_parameters.use_depth_first_search()) {
5508 SearchLimit* first_lns_limit =
5509 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5510 solve_db_ = solver_->Compose(
5511 GetFirstSolutionDecisionBuilder(search_parameters),
5512 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_lns_limit),
5515 solve_db_ = CreateLocalSearchDecisionBuilder(search_parameters);
5517 CHECK(preassignment_ !=
nullptr);
5518 DecisionBuilder* restore_preassignment =
5519 solver_->MakeRestoreAssignment(preassignment_);
5520 solve_db_ = solver_->Compose(restore_preassignment, solve_db_);
5522 solver_->Compose(restore_preassignment,
5523 solver_->MakeLocalSearchPhase(
5524 GetOrCreateAssignment(),
5525 CreateLocalSearchParameters(search_parameters)));
5526 restore_assignment_ = solver_->Compose(
5527 solver_->MakeRestoreAssignment(GetOrCreateAssignment()),
5528 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5529 restore_tmp_assignment_ = solver_->Compose(
5530 restore_preassignment,
5531 solver_->MakeRestoreAssignment(GetOrCreateTmpAssignment()),
5532 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
5535void RoutingModel::SetupMetaheuristics(
5536 const RoutingSearchParameters& search_parameters) {
5537 SearchMonitor* optimize;
5539 search_parameters.local_search_metaheuristic();
5542 bool limit_too_long =
5543 !search_parameters.has_time_limit() &&
5545 const int64_t optimization_step =
std::max(
5547 switch (metaheuristic) {
5550 optimize = solver_->MakeGuidedLocalSearch(
5553 optimization_step, nexts_,
5554 search_parameters.guided_local_search_lambda_coefficient());
5556 optimize = solver_->MakeGuidedLocalSearch(
5558 [
this](int64_t i, int64_t j, int64_t k) {
5561 optimization_step, nexts_, vehicle_vars_,
5562 search_parameters.guided_local_search_lambda_coefficient());
5567 solver_->MakeSimulatedAnnealing(
false, cost_, optimization_step, 100);
5570 optimize = solver_->MakeTabuSearch(
false, cost_, optimization_step,
5571 nexts_, 10, 10, .8);
5574 std::vector<operations_research::IntVar*> tabu_vars;
5575 if (tabu_var_callback_) {
5576 tabu_vars = tabu_var_callback_(
this);
5578 tabu_vars.push_back(cost_);
5580 optimize = solver_->MakeGenericTabuSearch(
false, cost_, optimization_step,
5585 limit_too_long =
false;
5586 optimize = solver_->MakeMinimize(cost_, optimization_step);
5588 if (limit_too_long) {
5590 <<
" specified without sane timeout: solve may run forever.";
5592 monitors_.push_back(optimize);
5596 tabu_var_callback_ = std::move(tabu_var_callback);
5599void RoutingModel::SetupAssignmentCollector(
5601 Assignment* full_assignment = solver_->MakeAssignment();
5603 full_assignment->
Add(dimension->cumuls());
5605 for (IntVar*
const extra_var : extra_vars_) {
5606 full_assignment->
Add(extra_var);
5608 for (IntervalVar*
const extra_interval : extra_intervals_) {
5609 full_assignment->
Add(extra_interval);
5611 full_assignment->
Add(nexts_);
5612 full_assignment->
Add(active_);
5613 full_assignment->
Add(vehicle_vars_);
5616 collect_assignments_ = solver_->MakeNBestValueSolutionCollector(
5619 collect_one_assignment_ =
5620 solver_->MakeFirstSolutionCollector(full_assignment);
5621 monitors_.push_back(collect_assignments_);
5624void RoutingModel::SetupTrace(
5625 const RoutingSearchParameters& search_parameters) {
5626 if (search_parameters.log_search()) {
5627 Solver::SearchLogParameters search_log_parameters;
5628 search_log_parameters.branch_period = 10000;
5629 search_log_parameters.objective =
nullptr;
5630 search_log_parameters.variable = cost_;
5631 search_log_parameters.scaling_factor =
5632 search_parameters.log_cost_scaling_factor();
5633 search_log_parameters.offset = search_parameters.log_cost_offset();
5634 if (!search_parameters.log_tag().empty()) {
5635 const std::string& tag = search_parameters.log_tag();
5636 search_log_parameters.display_callback = [tag]() {
return tag; };
5638 search_log_parameters.display_callback =
nullptr;
5640 search_log_parameters.display_on_new_solutions_only =
false;
5641 monitors_.push_back(solver_->MakeSearchLog(search_log_parameters));
5645void RoutingModel::SetupImprovementLimit(
5646 const RoutingSearchParameters& search_parameters) {
5647 if (search_parameters.has_improvement_limit_parameters()) {
5648 monitors_.push_back(solver_->MakeImprovementLimit(
5649 cost_,
false, search_parameters.log_cost_scaling_factor(),
5650 search_parameters.log_cost_offset(),
5651 search_parameters.improvement_limit_parameters()
5652 .improvement_rate_coefficient(),
5653 search_parameters.improvement_limit_parameters()
5654 .improvement_rate_solutions_distance()));
5658void RoutingModel::SetupSearchMonitors(
5659 const RoutingSearchParameters& search_parameters) {
5660 monitors_.push_back(GetOrCreateLimit());
5661 SetupImprovementLimit(search_parameters);
5662 SetupMetaheuristics(search_parameters);
5663 SetupAssignmentCollector(search_parameters);
5664 SetupTrace(search_parameters);
5667bool RoutingModel::UsesLightPropagation(
5668 const RoutingSearchParameters& search_parameters)
const {
5669 return !search_parameters.use_full_propagation() &&
5670 !search_parameters.use_depth_first_search() &&
5671 search_parameters.first_solution_strategy() !=
5679 finalizer_variable_cost_pairs_.size());
5680 if (
index < finalizer_variable_cost_pairs_.size()) {
5681 const int64_t old_cost = finalizer_variable_cost_pairs_[
index].second;
5682 finalizer_variable_cost_pairs_[
index].second =
CapAdd(old_cost,
cost);
5684 finalizer_variable_cost_pairs_.emplace_back(
var,
cost);
5690 if (finalizer_variable_target_set_.contains(
var))
return;
5691 finalizer_variable_target_set_.insert(
var);
5692 finalizer_variable_target_pairs_.emplace_back(
var, target);
5703void RoutingModel::SetupSearch(
5705 SetupDecisionBuilders(search_parameters);
5706 SetupSearchMonitors(search_parameters);
5710 extra_vars_.push_back(
var);
5714 extra_intervals_.push_back(
interval);
5719class PathSpansAndTotalSlacks :
public Constraint {
5723 std::vector<IntVar*> spans,
5724 std::vector<IntVar*> total_slacks)
5727 dimension_(dimension),
5728 spans_(
std::move(spans)),
5729 total_slacks_(
std::move(total_slacks)) {
5730 CHECK_EQ(spans_.size(), model_->vehicles());
5731 CHECK_EQ(total_slacks_.size(), model_->vehicles());
5732 vehicle_demons_.resize(model_->vehicles());
5735 std::string DebugString()
const override {
return "PathSpansAndTotalSlacks"; }
5737 void Post()
override {
5738 const int num_nodes = model_->VehicleVars().size();
5739 const int num_transits = model_->Nexts().size();
5740 for (
int node = 0; node < num_nodes; ++node) {
5742 model_->solver(),
this, &PathSpansAndTotalSlacks::PropagateNode,
5743 "PathSpansAndTotalSlacks::PropagateNode", node);
5744 dimension_->CumulVar(node)->WhenRange(demon);
5745 model_->VehicleVar(node)->WhenBound(demon);
5746 if (node < num_transits) {
5747 dimension_->TransitVar(node)->WhenRange(demon);
5748 dimension_->FixedTransitVar(node)->WhenBound(demon);
5749 model_->NextVar(node)->WhenBound(demon);
5752 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5753 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5755 solver(),
this, &PathSpansAndTotalSlacks::PropagateVehicle,
5756 "PathSpansAndTotalSlacks::PropagateVehicle", vehicle);
5757 vehicle_demons_[vehicle] = demon;
5758 if (spans_[vehicle]) spans_[vehicle]->WhenRange(demon);
5759 if (total_slacks_[vehicle]) total_slacks_[vehicle]->WhenRange(demon);
5760 if (dimension_->HasBreakConstraints()) {
5761 for (IntervalVar*
b : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5762 b->WhenAnything(demon);
5769 void InitialPropagate()
override {
5770 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5771 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5772 PropagateVehicle(vehicle);
5780 void PropagateNode(
int node) {
5781 if (!model_->VehicleVar(node)->Bound())
return;
5782 const int vehicle = model_->VehicleVar(node)->Min();
5783 if (vehicle < 0 || vehicle_demons_[vehicle] ==
nullptr)
return;
5784 EnqueueDelayedDemon(vehicle_demons_[vehicle]);
5792 int64_t SpanMin(
int vehicle, int64_t sum_fixed_transits) {
5794 const int64_t span_min = spans_[vehicle]
5795 ? spans_[vehicle]->Min()
5797 const int64_t total_slack_min = total_slacks_[vehicle]
5798 ? total_slacks_[vehicle]->Min()
5800 return std::min(span_min,
CapAdd(total_slack_min, sum_fixed_transits));
5802 int64_t SpanMax(
int vehicle, int64_t sum_fixed_transits) {
5804 const int64_t span_max = spans_[vehicle]
5805 ? spans_[vehicle]->Max()
5807 const int64_t total_slack_max = total_slacks_[vehicle]
5808 ? total_slacks_[vehicle]->Max()
5810 return std::max(span_max,
CapAdd(total_slack_max, sum_fixed_transits));
5812 void SetSpanMin(
int vehicle, int64_t
min, int64_t sum_fixed_transits) {
5814 if (spans_[vehicle]) {
5815 spans_[vehicle]->SetMin(
min);
5817 if (total_slacks_[vehicle]) {
5818 total_slacks_[vehicle]->SetMin(
CapSub(
min, sum_fixed_transits));
5821 void SetSpanMax(
int vehicle, int64_t
max, int64_t sum_fixed_transits) {
5823 if (spans_[vehicle]) {
5824 spans_[vehicle]->SetMax(
max);
5826 if (total_slacks_[vehicle]) {
5827 total_slacks_[vehicle]->SetMax(
CapSub(
max, sum_fixed_transits));
5832 void SynchronizeSpanAndTotalSlack(
int vehicle, int64_t sum_fixed_transits) {
5834 IntVar* span = spans_[vehicle];
5835 IntVar* total_slack = total_slacks_[vehicle];
5836 if (!span || !total_slack)
return;
5837 span->SetMin(
CapAdd(total_slack->Min(), sum_fixed_transits));
5838 span->SetMax(
CapAdd(total_slack->Max(), sum_fixed_transits));
5839 total_slack->SetMin(
CapSub(span->Min(), sum_fixed_transits));
5840 total_slack->SetMax(
CapSub(span->Max(), sum_fixed_transits));
5843 void PropagateVehicle(
int vehicle) {
5844 DCHECK(spans_[vehicle] || total_slacks_[vehicle]);
5845 const int start = model_->Start(vehicle);
5846 const int end = model_->End(vehicle);
5852 int curr_node = start;
5853 while (!model_->IsEnd(curr_node)) {
5854 const IntVar* next_var = model_->NextVar(curr_node);
5855 if (!next_var->Bound())
return;
5856 path_.push_back(curr_node);
5857 curr_node = next_var->Value();
5862 int64_t sum_fixed_transits = 0;
5863 for (
const int node : path_) {
5864 const IntVar* fixed_transit_var = dimension_->FixedTransitVar(node);
5865 if (!fixed_transit_var->Bound())
return;
5866 sum_fixed_transits =
5867 CapAdd(sum_fixed_transits, fixed_transit_var->Value());
5870 SynchronizeSpanAndTotalSlack(vehicle, sum_fixed_transits);
5877 if (dimension_->HasBreakConstraints() &&
5878 !dimension_->GetBreakIntervalsOfVehicle(vehicle).empty()) {
5879 const int64_t vehicle_start_max = dimension_->CumulVar(start)->Max();
5880 const int64_t vehicle_end_min = dimension_->CumulVar(end)->Min();
5882 int64_t min_break_duration = 0;
5883 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5884 if (!br->MustBePerformed())
continue;
5885 if (vehicle_start_max < br->EndMin() &&
5886 br->StartMax() < vehicle_end_min) {
5887 min_break_duration =
CapAdd(min_break_duration, br->DurationMin());
5890 SetSpanMin(vehicle,
CapAdd(min_break_duration, sum_fixed_transits),
5891 sum_fixed_transits);
5897 const int64_t slack_max =
5898 CapSub(SpanMax(vehicle, sum_fixed_transits), sum_fixed_transits);
5899 const int64_t max_additional_slack =
5900 CapSub(slack_max, min_break_duration);
5901 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5902 if (!br->MustBePerformed())
continue;
5904 if (vehicle_start_max >= br->EndMin() &&
5905 br->StartMax() < vehicle_end_min) {
5906 if (br->DurationMin() > max_additional_slack) {
5909 br->SetEndMax(vehicle_start_max);
5910 dimension_->CumulVar(start)->SetMin(br->EndMin());
5915 if (vehicle_start_max < br->EndMin() &&
5916 br->StartMax() >= vehicle_end_min) {
5917 if (br->DurationMin() > max_additional_slack) {
5918 br->SetStartMin(vehicle_end_min);
5919 dimension_->CumulVar(end)->SetMax(br->StartMax());
5927 IntVar* start_cumul = dimension_->CumulVar(start);
5928 IntVar* end_cumul = dimension_->CumulVar(end);
5929 const int64_t
start_min = start_cumul->Min();
5930 const int64_t
start_max = start_cumul->Max();
5931 const int64_t
end_min = end_cumul->Min();
5932 const int64_t
end_max = end_cumul->Max();
5935 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5937 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5939 const int64_t span_min = SpanMin(vehicle, sum_fixed_transits);
5940 const int64_t span_max = SpanMax(vehicle, sum_fixed_transits);
5941 const int64_t slack_from_lb =
CapSub(span_max, span_lb);
5942 const int64_t slack_from_ub =
CapSub(span_ub, span_min);
5956 int64_t span_lb = 0;
5957 int64_t span_ub = 0;
5958 for (
const int node : path_) {
5959 span_lb =
CapAdd(span_lb, dimension_->TransitVar(node)->Min());
5960 span_ub =
CapAdd(span_ub, dimension_->TransitVar(node)->Max());
5962 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5963 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5967 const int64_t span_min = SpanMin(vehicle, sum_fixed_transits);
5968 const int64_t span_max = SpanMax(vehicle, sum_fixed_transits);
5969 const int64_t slack_from_lb =
CapSub(span_max, span_lb);
5970 const int64_t slack_from_ub =
5972 ?
CapSub(span_ub, span_min)
5973 :
std::numeric_limits<int64_t>::
max();
5974 for (
const int node : path_) {
5975 IntVar* transit_var = dimension_->TransitVar(node);
5976 const int64_t transit_i_min = transit_var->Min();
5977 const int64_t transit_i_max = transit_var->Max();
5981 transit_var->SetMax(
CapAdd(transit_i_min, slack_from_lb));
5982 transit_var->SetMin(
CapSub(transit_i_max, slack_from_ub));
5987 path_.push_back(end);
5998 int64_t arrival_time = dimension_->CumulVar(start)->Min();
5999 for (
int i = 1; i < path_.size(); ++i) {
6002 dimension_->FixedTransitVar(path_[i - 1])->Min()),
6003 dimension_->CumulVar(path_[i])->Min());
6005 int64_t departure_time = arrival_time;
6006 for (
int i = path_.size() - 2; i >= 0; --i) {
6009 dimension_->FixedTransitVar(path_[i])->Min()),
6010 dimension_->CumulVar(path_[i])->Max());
6012 const int64_t span_lb =
CapSub(arrival_time, departure_time);
6013 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
6014 const int64_t maximum_deviation =
6015 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
6016 const int64_t start_lb =
CapSub(departure_time, maximum_deviation);
6017 dimension_->CumulVar(start)->SetMin(start_lb);
6021 int64_t departure_time = dimension_->CumulVar(end)->Max();
6022 for (
int i = path_.size() - 2; i >= 0; --i) {
6023 const int curr_node = path_[i];
6026 dimension_->FixedTransitVar(curr_node)->Min()),
6027 dimension_->CumulVar(curr_node)->Max());
6029 int arrival_time = departure_time;
6030 for (
int i = 1; i < path_.size(); ++i) {
6033 dimension_->FixedTransitVar(path_[i - 1])->Min()),
6034 dimension_->CumulVar(path_[i])->Min());
6036 const int64_t span_lb =
CapSub(arrival_time, departure_time);
6037 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
6038 const int64_t maximum_deviation =
6039 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
6040 dimension_->CumulVar(end)->SetMax(
6041 CapAdd(arrival_time, maximum_deviation));
6047 std::vector<IntVar*> spans_;
6048 std::vector<IntVar*> total_slacks_;
6049 std::vector<int> path_;
6050 std::vector<Demon*> vehicle_demons_;
6057 std::vector<IntVar*> total_slacks) {
6059 CHECK_EQ(vehicles_, total_slacks.size());
6061 new PathSpansAndTotalSlacks(
this, dimension, spans, total_slacks));
6069 std::vector<int64_t> vehicle_capacities,
6070 const std::string&
name,
6072 : vehicle_capacities_(
std::move(vehicle_capacities)),
6073 base_dimension_(base_dimension),
6074 global_span_cost_coefficient_(0),
6077 global_optimizer_offset_(0) {
6079 vehicle_span_upper_bounds_.assign(
model->vehicles(),
6081 vehicle_span_cost_coefficients_.assign(
model->vehicles(), 0);
6084RoutingDimension::RoutingDimension(RoutingModel*
model,
6085 std::vector<int64_t> vehicle_capacities,
6086 const std::string&
name, SelfBased)
6087 : RoutingDimension(
model,
std::move(vehicle_capacities),
name, this) {}
6090 cumul_var_piecewise_linear_cost_.clear();
6093void RoutingDimension::Initialize(
6094 const std::vector<int>& transit_evaluators,
6095 const std::vector<int>& state_dependent_transit_evaluators,
6096 int64_t slack_max) {
6098 InitializeTransits(transit_evaluators, state_dependent_transit_evaluators,
6112class LightRangeLessOrEqual :
public Constraint {
6114 LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l, IntExpr*
const r);
6115 ~LightRangeLessOrEqual()
override {}
6116 void Post()
override;
6117 void InitialPropagate()
override;
6118 std::string DebugString()
const override;
6119 IntVar* Var()
override {
6120 return solver()->MakeIsLessOrEqualVar(left_, right_);
6123 void Accept(ModelVisitor*
const visitor)
const override {
6134 IntExpr*
const left_;
6135 IntExpr*
const right_;
6139LightRangeLessOrEqual::LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l,
6141 : Constraint(s), left_(l), right_(r), demon_(nullptr) {}
6143void LightRangeLessOrEqual::Post() {
6145 solver(),
this, &LightRangeLessOrEqual::CheckRange,
"CheckRange");
6146 left_->WhenRange(demon_);
6147 right_->WhenRange(demon_);
6150void LightRangeLessOrEqual::InitialPropagate() {
6151 left_->SetMax(right_->Max());
6152 right_->SetMin(left_->Min());
6153 if (left_->Max() <= right_->Min()) {
6158void LightRangeLessOrEqual::CheckRange() {
6159 if (left_->Min() > right_->Max()) {
6162 if (left_->Max() <= right_->Min()) {
6167std::string LightRangeLessOrEqual::DebugString()
const {
6168 return left_->DebugString() +
" < " + right_->DebugString();
6173void RoutingDimension::InitializeCumuls() {
6174 Solver*
const solver = model_->
solver();
6176 const auto capacity_range = std::minmax_element(vehicle_capacities_.begin(),
6177 vehicle_capacities_.end());
6178 const int64_t min_capacity = *capacity_range.first;
6180 const int64_t max_capacity = *capacity_range.second;
6181 solver->MakeIntVarArray(size, 0, max_capacity, name_, &cumuls_);
6183 for (
int v = 0; v < model_->
vehicles(); v++) {
6184 const int64_t vehicle_capacity = vehicle_capacities_[v];
6185 cumuls_[model_->
Start(v)]->SetMax(vehicle_capacity);
6186 cumuls_[model_->
End(v)]->SetMax(vehicle_capacity);
6189 forbidden_intervals_.resize(size);
6190 capacity_vars_.clear();
6191 if (min_capacity != max_capacity) {
6194 for (
int i = 0; i < size; ++i) {
6195 IntVar*
const capacity_var = capacity_vars_[i];
6196 if (i < model_->Size()) {
6197 IntVar*
const capacity_active = solver->MakeBoolVar();
6198 solver->AddConstraint(
6199 solver->MakeLessOrEqual(model_->
ActiveVar(i), capacity_active));
6200 solver->AddConstraint(solver->MakeIsLessOrEqualCt(
6201 cumuls_[i], capacity_var, capacity_active));
6203 solver->AddConstraint(
6204 solver->MakeLessOrEqual(cumuls_[i], capacity_var));
6211template <
int64_t value>
6212int64_t IthElementOrValue(
const std::vector<int64_t>& v, int64_t
index) {
6216void ComputeTransitClasses(
const std::vector<int>& evaluator_indices,
6217 std::vector<int>* class_evaluators,
6218 std::vector<int64_t>* vehicle_to_class) {
6219 CHECK(class_evaluators !=
nullptr);
6220 CHECK(vehicle_to_class !=
nullptr);
6221 class_evaluators->clear();
6222 vehicle_to_class->resize(evaluator_indices.size(), -1);
6223 absl::flat_hash_map<int, int64_t> evaluator_to_class;
6224 for (
int i = 0; i < evaluator_indices.size(); ++i) {
6225 const int evaluator_index = evaluator_indices[i];
6226 int evaluator_class = -1;
6227 if (!
gtl::FindCopy(evaluator_to_class, evaluator_index, &evaluator_class)) {
6228 evaluator_class = class_evaluators->size();
6229 evaluator_to_class[evaluator_index] = evaluator_class;
6230 class_evaluators->push_back(evaluator_index);
6232 (*vehicle_to_class)[i] = evaluator_class;
6237void RoutingDimension::InitializeTransitVariables(int64_t slack_max) {
6238 CHECK(!class_evaluators_.empty());
6239 CHECK(base_dimension_ ==
nullptr ||
6240 !state_dependent_class_evaluators_.empty());
6242 Solver*
const solver = model_->
solver();
6243 const int size = model_->
Size();
6246 return (0 <=
index &&
index < state_dependent_vehicle_to_class_.size())
6247 ? state_dependent_vehicle_to_class_[
index]
6248 : state_dependent_class_evaluators_.size();
6250 const std::string slack_name = name_ +
" slack";
6251 const std::string transit_name = name_ +
" fixed transit";
6253 for (int64_t i = 0; i < size; ++i) {
6256 absl::StrCat(transit_name, i));
6258 if (base_dimension_ !=
nullptr) {
6259 if (state_dependent_class_evaluators_.size() == 1) {
6260 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
6261 for (int64_t j = 0; j < cumuls_.size(); ++j) {
6262 transition_variables[j] =
6263 MakeRangeMakeElementExpr(
6265 ->StateDependentTransitCallback(
6266 state_dependent_class_evaluators_[0])(i, j)
6268 base_dimension_->
CumulVar(i), solver)
6271 dependent_transits_[i] =
6272 solver->MakeElement(transition_variables, model_->
NextVar(i))
6275 IntVar*
const vehicle_class_var =
6277 ->MakeElement(dependent_vehicle_class_function,
6280 std::vector<IntVar*> transit_for_vehicle;
6281 transit_for_vehicle.reserve(state_dependent_class_evaluators_.size() +
6283 for (
int evaluator : state_dependent_class_evaluators_) {
6284 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
6285 for (int64_t j = 0; j < cumuls_.size(); ++j) {
6286 transition_variables[j] =
6287 MakeRangeMakeElementExpr(
6290 base_dimension_->
CumulVar(i), solver)
6293 transit_for_vehicle.push_back(
6294 solver->MakeElement(transition_variables, model_->
NextVar(i))
6297 transit_for_vehicle.push_back(solver->MakeIntConst(0));
6298 dependent_transits_[i] =
6299 solver->MakeElement(transit_for_vehicle, vehicle_class_var)->Var();
6302 dependent_transits_[i] = solver->MakeIntConst(0);
6306 IntExpr* transit_expr = fixed_transits_[i];
6307 if (dependent_transits_[i]->Min() != 0 ||
6308 dependent_transits_[i]->Max() != 0) {
6309 transit_expr = solver->MakeSum(transit_expr, dependent_transits_[i]);
6312 if (slack_max == 0) {
6313 slacks_[i] = solver->MakeIntConst(0);
6316 solver->MakeIntVar(0, slack_max, absl::StrCat(slack_name, i));
6317 transit_expr = solver->MakeSum(slacks_[i], transit_expr);
6319 transits_[i] = transit_expr->Var();
6323void RoutingDimension::InitializeTransits(
6324 const std::vector<int>& transit_evaluators,
6325 const std::vector<int>& state_dependent_transit_evaluators,
6326 int64_t slack_max) {
6328 CHECK(base_dimension_ ==
nullptr ||
6329 model_->
vehicles() == state_dependent_transit_evaluators.size());
6330 const int size = model_->
Size();
6331 transits_.resize(size,
nullptr);
6332 fixed_transits_.resize(size,
nullptr);
6333 slacks_.resize(size,
nullptr);
6334 dependent_transits_.resize(size,
nullptr);
6335 ComputeTransitClasses(transit_evaluators, &class_evaluators_,
6336 &vehicle_to_class_);
6337 if (base_dimension_ !=
nullptr) {
6338 ComputeTransitClasses(state_dependent_transit_evaluators,
6339 &state_dependent_class_evaluators_,
6340 &state_dependent_vehicle_to_class_);
6343 InitializeTransitVariables(slack_max);
6348 std::vector<int64_t>* values) {
6349 const int num_nodes = path.size();
6350 values->resize(num_nodes - 1);
6351 for (
int i = 0; i < num_nodes - 1; ++i) {
6352 (*values)[i] = evaluator(path[i], path[i + 1]);
6357 : model_(
model), occurrences_of_type_(
model.GetNumberOfVisitTypes()) {}
6360 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor) {
6367 for (
int pos = 0; pos < current_route_visits_.size(); pos++) {
6368 const int64_t current_visit = current_route_visits_[pos];
6375 DCHECK_LT(type, occurrences_of_type_.size());
6376 int& num_type_added = occurrences_of_type_[type].num_type_added_to_vehicle;
6377 int& num_type_removed =
6378 occurrences_of_type_[type].num_type_removed_from_vehicle;
6379 DCHECK_LE(num_type_removed, num_type_added);
6381 num_type_removed == num_type_added) {
6391 if (policy == VisitTypePolicy::TYPE_ADDED_TO_VEHICLE ||
6392 policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED) {
6395 if (policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED ||
6396 policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6404 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor) {
6407 std::fill(occurrences_of_type_.begin(), occurrences_of_type_.end(),
6413 current_route_visits_.clear();
6415 current = next_accessor(current)) {
6418 VisitTypePolicy::TYPE_ON_VEHICLE_UP_TO_VISIT) {
6419 occurrences_of_type_[type].position_of_last_type_on_vehicle_up_to_visit =
6420 current_route_visits_.size();
6422 current_route_visits_.push_back(current);
6444 check_hard_incompatibilities_(check_hard_incompatibilities) {}
6446bool TypeIncompatibilityChecker::HasRegulationsToCheck()
const {
6448 (check_hard_incompatibilities_ &&
6457bool TypeIncompatibilityChecker::CheckTypeRegulations(
int type,
6458 VisitTypePolicy policy,
6460 if (policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6465 for (
int incompatible_type :
6471 if (check_hard_incompatibilities_) {
6472 for (
int incompatible_type :
6482bool TypeRequirementChecker::HasRegulationsToCheck()
const {
6487bool TypeRequirementChecker::CheckRequiredTypesCurrentlyOnRoute(
6488 const std::vector<absl::flat_hash_set<int>>& required_type_alternatives,
6490 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6491 required_type_alternatives) {
6492 bool has_one_of_alternatives =
false;
6493 for (
int type_alternative : requirement_alternatives) {
6495 has_one_of_alternatives =
true;
6499 if (!has_one_of_alternatives) {
6506bool TypeRequirementChecker::CheckTypeRegulations(
int type,
6507 VisitTypePolicy policy,
6511 if (!CheckRequiredTypesCurrentlyOnRoute(
6517 if (!CheckRequiredTypesCurrentlyOnRoute(
6524 types_with_same_vehicle_requirements_on_route_.insert(type);
6529bool TypeRequirementChecker::FinalizeCheck()
const {
6530 for (
int type : types_with_same_vehicle_requirements_on_route_) {
6531 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6533 bool has_one_of_alternatives =
false;
6534 for (
const int type_alternative : requirement_alternatives) {
6536 has_one_of_alternatives =
true;
6540 if (!has_one_of_alternatives) {
6551 incompatibility_checker_(
model, true),
6552 requirement_checker_(
model),
6553 vehicle_demons_(
model.vehicles()) {}
6555void TypeRegulationsConstraint::PropagateNodeRegulations(
int node) {
6562 if (vehicle < 0)
return;
6563 DCHECK(vehicle_demons_[vehicle] !=
nullptr);
6567void TypeRegulationsConstraint::CheckRegulationsOnVehicle(
int vehicle) {
6568 const auto next_accessor = [
this, vehicle](int64_t node) {
6573 return model_.
End(vehicle);
6575 if (!incompatibility_checker_.
CheckVehicle(vehicle, next_accessor) ||
6576 !requirement_checker_.
CheckVehicle(vehicle, next_accessor)) {
6582 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6584 solver(),
this, &TypeRegulationsConstraint::CheckRegulationsOnVehicle,
6585 "CheckRegulationsOnVehicle", vehicle);
6587 for (
int node = 0; node < model_.
Size(); node++) {
6589 solver(),
this, &TypeRegulationsConstraint::PropagateNodeRegulations,
6590 "PropagateNodeRegulations", node);
6597 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6598 CheckRegulationsOnVehicle(vehicle);
6602void RoutingDimension::CloseModel(
bool use_light_propagation) {
6604 const auto capacity_lambda = [
this](int64_t vehicle) {
6605 return vehicle >= 0 ? vehicle_capacities_[vehicle]
6608 for (
int i = 0; i < capacity_vars_.size(); ++i) {
6609 IntVar*
const vehicle_var = model_->
VehicleVar(i);
6610 IntVar*
const capacity_var = capacity_vars_[i];
6611 if (use_light_propagation) {
6613 solver, capacity_var, vehicle_var, capacity_lambda,
6614 [
this]() {
return model_->enable_deep_serialization_; }));
6621 for (
int i = 0; i < fixed_transits_.size(); ++i) {
6622 IntVar*
const next_var = model_->
NextVar(i);
6623 IntVar*
const fixed_transit = fixed_transits_[i];
6624 const auto transit_vehicle_evaluator = [
this, i](int64_t to,
6625 int64_t eval_index) {
6628 if (use_light_propagation) {
6629 if (class_evaluators_.size() == 1) {
6630 const int class_evaluator_index = class_evaluators_[0];
6631 const auto& unary_callback =
6633 if (unary_callback ==
nullptr) {
6635 solver, fixed_transit, next_var,
6636 [
this, i](int64_t to) {
6639 [
this]() {
return model_->enable_deep_serialization_; }));
6641 fixed_transit->SetValue(unary_callback(i));
6645 solver, fixed_transit, next_var, model_->
VehicleVar(i),
6646 transit_vehicle_evaluator,
6647 [
this]() {
return model_->enable_deep_serialization_; }));
6650 if (class_evaluators_.size() == 1) {
6651 const int class_evaluator_index = class_evaluators_[0];
6652 const auto& unary_callback =
6654 if (unary_callback ==
nullptr) {
6656 fixed_transit, solver
6658 [
this, i](int64_t to) {
6660 class_evaluators_[0])(i, to);
6665 fixed_transit->SetValue(unary_callback(i));
6669 fixed_transit, solver
6677 GlobalVehicleBreaksConstraint* constraint =
6684 int64_t vehicle)
const {
6690 int64_t
index, int64_t min_value, int64_t max_value)
const {
6696 int64_t next_start =
min;
6700 if (next_start >
max)
break;
6701 if (next_start < interval->start) {
6706 if (next_start <=
max) {
6715 CHECK_LT(vehicle, vehicle_span_upper_bounds_.size());
6717 vehicle_span_upper_bounds_[vehicle] =
upper_bound;
6723 CHECK_LT(vehicle, vehicle_span_cost_coefficients_.size());
6725 vehicle_span_cost_coefficients_[vehicle] =
coefficient;
6741 if (!
cost.IsNonDecreasing()) {
6742 LOG(
WARNING) <<
"Only non-decreasing cost functions are supported.";
6745 if (
cost.Value(0) < 0) {
6746 LOG(
WARNING) <<
"Only positive cost functions are supported.";
6749 if (
index >= cumul_var_piecewise_linear_cost_.size()) {
6750 cumul_var_piecewise_linear_cost_.resize(
index + 1);
6752 PiecewiseLinearCost& piecewise_linear_cost =
6753 cumul_var_piecewise_linear_cost_[
index];
6754 piecewise_linear_cost.var = cumuls_[
index];
6755 piecewise_linear_cost.cost = absl::make_unique<PiecewiseLinearFunction>(
cost);
6759 return (
index < cumul_var_piecewise_linear_cost_.size() &&
6760 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr);
6764 int64_t
index)
const {
6765 if (
index < cumul_var_piecewise_linear_cost_.size() &&
6766 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr) {
6767 return cumul_var_piecewise_linear_cost_[
index].cost.get();
6777 const int vehicle =
model->VehicleIndex(
index);
6779 return solver->
MakeProd(expr,
model->VehicleRouteConsideredVar(vehicle))
6786void RoutingDimension::SetupCumulVarPiecewiseLinearCosts(
6787 std::vector<IntVar*>* cost_elements)
const {
6788 CHECK(cost_elements !=
nullptr);
6789 Solver*
const solver = model_->
solver();
6790 for (
int i = 0; i < cumul_var_piecewise_linear_cost_.size(); ++i) {
6791 const PiecewiseLinearCost& piecewise_linear_cost =
6792 cumul_var_piecewise_linear_cost_[i];
6793 if (piecewise_linear_cost.var !=
nullptr) {
6794 IntExpr*
const expr = solver->MakePiecewiseLinearExpr(
6795 piecewise_linear_cost.var, *piecewise_linear_cost.cost);
6796 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6797 cost_elements->push_back(cost_var);
6808 if (
index >= cumul_var_soft_upper_bound_.size()) {
6809 cumul_var_soft_upper_bound_.resize(
index + 1, {
nullptr, 0, 0});
6816 return (
index < cumul_var_soft_upper_bound_.size() &&
6817 cumul_var_soft_upper_bound_[
index].var !=
nullptr);
6821 if (
index < cumul_var_soft_upper_bound_.size() &&
6822 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6823 return cumul_var_soft_upper_bound_[
index].bound;
6825 return cumuls_[
index]->Max();
6829 int64_t
index)
const {
6830 if (
index < cumul_var_soft_upper_bound_.size() &&
6831 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6832 return cumul_var_soft_upper_bound_[
index].coefficient;
6837void RoutingDimension::SetupCumulVarSoftUpperBoundCosts(
6838 std::vector<IntVar*>* cost_elements)
const {
6839 CHECK(cost_elements !=
nullptr);
6841 for (
int i = 0; i < cumul_var_soft_upper_bound_.size(); ++i) {
6842 const SoftBound& soft_bound = cumul_var_soft_upper_bound_[i];
6843 if (soft_bound.var !=
nullptr) {
6845 solver->
MakeSum(soft_bound.var, -soft_bound.bound), 0,
6846 soft_bound.coefficient);
6847 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6848 cost_elements->push_back(cost_var);
6852 soft_bound.coefficient);
6860 if (
index >= cumul_var_soft_lower_bound_.size()) {
6861 cumul_var_soft_lower_bound_.resize(
index + 1, {
nullptr, 0, 0});
6868 return (
index < cumul_var_soft_lower_bound_.size() &&
6869 cumul_var_soft_lower_bound_[
index].var !=
nullptr);
6873 if (
index < cumul_var_soft_lower_bound_.size() &&
6874 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6875 return cumul_var_soft_lower_bound_[
index].bound;
6877 return cumuls_[
index]->Min();
6881 int64_t
index)
const {
6882 if (
index < cumul_var_soft_lower_bound_.size() &&
6883 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6884 return cumul_var_soft_lower_bound_[
index].coefficient;
6889void RoutingDimension::SetupCumulVarSoftLowerBoundCosts(
6890 std::vector<IntVar*>* cost_elements)
const {
6891 CHECK(cost_elements !=
nullptr);
6893 for (
int i = 0; i < cumul_var_soft_lower_bound_.size(); ++i) {
6894 const SoftBound& soft_bound = cumul_var_soft_lower_bound_[i];
6895 if (soft_bound.var !=
nullptr) {
6898 soft_bound.coefficient);
6899 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6900 cost_elements->push_back(cost_var);
6904 soft_bound.coefficient);
6909void RoutingDimension::SetupGlobalSpanCost(
6910 std::vector<IntVar*>* cost_elements)
const {
6911 CHECK(cost_elements !=
nullptr);
6912 Solver*
const solver = model_->
solver();
6913 if (global_span_cost_coefficient_ != 0) {
6914 std::vector<IntVar*> end_cumuls;
6915 for (
int i = 0; i < model_->
vehicles(); ++i) {
6916 end_cumuls.push_back(solver
6917 ->MakeProd(model_->vehicle_route_considered_[i],
6918 cumuls_[model_->
End(i)])
6921 IntVar*
const max_end_cumul = solver->
MakeMax(end_cumuls)->
Var();
6923 max_end_cumul, global_span_cost_coefficient_);
6924 std::vector<IntVar*> start_cumuls;
6925 for (
int i = 0; i < model_->
vehicles(); ++i) {
6926 IntVar* global_span_cost_start_cumul =
6928 solver->AddConstraint(solver->MakeIfThenElseCt(
6929 model_->vehicle_route_considered_[i], cumuls_[model_->
Start(i)],
6930 max_end_cumul, global_span_cost_start_cumul));
6931 start_cumuls.push_back(global_span_cost_start_cumul);
6933 IntVar*
const min_start_cumul = solver->MakeMin(start_cumuls)->Var();
6935 min_start_cumul, global_span_cost_coefficient_);
6940 for (
int var_index = 0; var_index < model_->
Size(); ++var_index) {
6942 slacks_[var_index], global_span_cost_coefficient_);
6943 cost_elements->push_back(
6946 model_->vehicle_route_considered_[0],
6949 solver->MakeSum(transits_[var_index],
6950 dependent_transits_[var_index]),
6951 global_span_cost_coefficient_),
6956 IntVar*
const end_range =
6957 solver->MakeDifference(max_end_cumul, min_start_cumul)->Var();
6958 end_range->SetMin(0);
6959 cost_elements->push_back(
6960 solver->MakeProd(end_range, global_span_cost_coefficient_)->Var());
6966 std::vector<IntervalVar*> breaks,
int vehicle,
6967 std::vector<int64_t> node_visit_transits) {
6968 if (breaks.empty())
return;
6970 [node_visit_transits](int64_t from, int64_t to) {
6971 return node_visit_transits[from];
6977 std::vector<IntervalVar*> breaks,
int vehicle,
6978 std::vector<int64_t> node_visit_transits,
6979 std::function<int64_t(int64_t, int64_t)> delays) {
6980 if (breaks.empty())
return;
6982 [node_visit_transits](int64_t from, int64_t to) {
6983 return node_visit_transits[from];
6985 const int delay_evaluator =
6992 std::vector<IntervalVar*> breaks,
int vehicle,
int pre_travel_evaluator,
6993 int post_travel_evaluator) {
6996 if (breaks.empty())
return;
6998 vehicle_break_intervals_[vehicle] = std::move(breaks);
6999 vehicle_pre_travel_evaluators_[vehicle] = pre_travel_evaluator;
7000 vehicle_post_travel_evaluators_[vehicle] = post_travel_evaluator;
7021 DCHECK(!break_constraints_are_initialized_);
7022 const int num_vehicles = model_->
vehicles();
7023 vehicle_break_intervals_.resize(num_vehicles);
7024 vehicle_pre_travel_evaluators_.resize(num_vehicles, -1);
7025 vehicle_post_travel_evaluators_.resize(num_vehicles, -1);
7026 vehicle_break_distance_duration_.resize(num_vehicles);
7027 break_constraints_are_initialized_ =
true;
7031 return break_constraints_are_initialized_;
7035 int vehicle)
const {
7037 DCHECK_LT(vehicle, vehicle_break_intervals_.size());
7038 return vehicle_break_intervals_[vehicle];
7043 DCHECK_LT(vehicle, vehicle_pre_travel_evaluators_.size());
7044 return vehicle_pre_travel_evaluators_[vehicle];
7049 DCHECK_LT(vehicle, vehicle_post_travel_evaluators_.size());
7050 return vehicle_post_travel_evaluators_[vehicle];
7059 vehicle_break_distance_duration_[vehicle].emplace_back(
distance, duration);
7068const std::vector<std::pair<int64_t, int64_t>>&
7071 DCHECK_LT(vehicle, vehicle_break_distance_duration_.size());
7072 return vehicle_break_distance_duration_[vehicle];
7078 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
7079 pickup_to_delivery_limits_per_pair_index_.resize(pair_index + 1);
7081 pickup_to_delivery_limits_per_pair_index_[pair_index] =
7082 std::move(limit_function);
7086 return !pickup_to_delivery_limits_per_pair_index_.empty();
7091 int delivery)
const {
7094 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
7098 pickup_to_delivery_limits_per_pair_index_[pair_index];
7099 if (!pickup_to_delivery_limit_function) {
7105 return pickup_to_delivery_limit_function(pickup, delivery);
7108void RoutingDimension::SetupSlackAndDependentTransitCosts()
const {
7109 if (model_->
vehicles() == 0)
return;
7111 bool all_vehicle_span_costs_are_equal =
true;
7112 for (
int i = 1; i < model_->
vehicles(); ++i) {
7113 all_vehicle_span_costs_are_equal &= vehicle_span_cost_coefficients_[i] ==
7114 vehicle_span_cost_coefficients_[0];
7117 if (all_vehicle_span_costs_are_equal &&
7118 vehicle_span_cost_coefficients_[0] == 0) {
7130 std::vector<const RoutingDimension*> dimensions_with_relevant_slacks = {
this};
7132 const RoutingDimension*
next =
7133 dimensions_with_relevant_slacks.back()->base_dimension_;
7134 if (
next ==
nullptr ||
next == dimensions_with_relevant_slacks.back()) {
7137 dimensions_with_relevant_slacks.push_back(
next);
7140 for (
auto it = dimensions_with_relevant_slacks.rbegin();
7141 it != dimensions_with_relevant_slacks.rend(); ++it) {
7142 for (
int i = 0; i < model_->
vehicles(); ++i) {
7148 for (IntVar*
const slack : (*it)->slacks_) {
std::vector< int > dimensions
#define DCHECK_LE(val1, val2)
#define DCHECK_NE(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)
An Assignment is a variable -> domains mapping, used to report solutions to the user.
bool Contains(const IntVar *const var) const
int64_t ObjectiveValue() const
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.
We call domain any subset of Int64 = [kint64min, kint64max].
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)
static constexpr Value GLOBAL_CHEAPEST_ARC
static constexpr Value BEST_INSERTION
FirstSolutionStrategy_Value Value
static constexpr Value SAVINGS
static constexpr Value LOCAL_CHEAPEST_ARC
static constexpr Value FIRST_UNBOUND_MIN_VALUE
static constexpr Value ALL_UNPERFORMED
static constexpr Value LOCAL_CHEAPEST_INSERTION
static constexpr Value AUTOMATIC
static constexpr Value PATH_MOST_CONSTRAINED_ARC
static constexpr Value UNSET
static constexpr Value CHRISTOFIDES
static constexpr Value SWEEP
static constexpr Value PARALLEL_CHEAPEST_INSERTION
static const std::string & Value_Name(T enum_t_value)
static constexpr Value EVALUATOR_STRATEGY
static constexpr Value SEQUENTIAL_CHEAPEST_INSERTION
static constexpr Value PATH_CHEAPEST_ARC
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 bool Bound() const
Returns true if the min and the max of the expression are equal.
virtual void SetValue(int64_t v)
This method sets the value of the expression.
virtual int64_t Min() const =0
virtual IntVar * Var()=0
Creates a variable from the expression.
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.
virtual IntExpr * SafeStartExpr(int64_t unperformed_value)=0
These methods create expressions encapsulating the start, end and duration of the interval var.
virtual IntExpr * SafeEndExpr(int64_t unperformed_value)=0
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.
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)
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::string & name() const
Returns the name of the dimension.
const std::vector< IntVar * > & cumuls() const
Like CumulVar(), TransitVar(), SlackVar() but return the whole variable vectors instead (indexed by i...
const std::vector< int64_t > & vehicle_capacities() const
Returns the capacities for all vehicles.
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.
bool HasPickupToDeliveryLimits() const
IntVar * CumulVar(int64_t index) const
Get the cumul, transit and slack variables for the given node (given as int64_t var index).
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...
const RoutingModel::TransitCallback2 & transit_evaluator(int vehicle) const
Returns the callback evaluating the transit value between two node indices for a given vehicle.
const std::vector< int64_t > & vehicle_span_cost_coefficients() const
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.
RoutingModel * model() const
Returns the model on which the dimension was created.
const std::vector< IntervalVar * > & GetBreakIntervalsOfVehicle(int vehicle) const
Returns the break intervals set by SetBreakIntervalsOfVehicle().
void SetPickupToDeliveryLimitFunctionForPair(PickupToDeliveryLimitFunction limit_function, int pair_index)
int vehicle_to_class(int vehicle) const
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...
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.
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
int64_t GetStartIndex(int vehicle) const
int num_unique_depots() const
complete.
NodeIndex IndexToNode(int64_t index) const
std::vector< NodeIndex > GetIndexToNodeMap() const
Attributes for a dimension.
A Resource sets attributes (costs/constraints) for a set of dimensions.
const ResourceGroup::Attributes & GetDimensionAttributes(const RoutingDimension *dimension) const
A ResourceGroup defines a set of available Resources with attributes on one or multiple dimensions.
const std::vector< int > & GetVehiclesRequiringAResource() const
bool VehicleRequiresAResource(int vehicle) const
int AddResource(Attributes attributes, const RoutingDimension *dimension)
Adds a Resource with the given attributes for the corresponding dimension.
const absl::flat_hash_set< DimensionIndex > & GetAffectedDimensionIndices() const
void NotifyVehicleRequiresAResource(int vehicle)
Notifies that the given vehicle index requires a resource from this group if the vehicle is used (i....
friend class RoutingModelInspector
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 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.
Solver * solver() const
Returns the underlying constraint solver.
const TransitCallback2 & TransitCallback(int callback_index) const
Assignment * RestoreAssignment(const Assignment &solution)
Restores an assignment as a solution in the routing model and returns the new solution.
std::function< std::vector< operations_research::IntVar * >(RoutingModel *)> GetTabuVarsCallback
Sets the callback returning the variable to use for the Tabu Search metaheuristic.
void AddSearchMonitor(SearchMonitor *const monitor)
Adds a search monitor to the search used to solve the routing model.
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
const std::vector< int > & GetDimensionResourceGroupIndices(const RoutingDimension *dimension) const
Returns the indices of resource groups for this dimension.
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.
bool HasMandatoryDisjunctions() const
Returns true if the model contains mandatory disjunctions (ones with kNoPenalty as penalty).
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 ...
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.
IntVar * VehicleVar(int64_t index) const
Returns the vehicle variable of the node corresponding to index.
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...
GlobalDimensionCumulOptimizer * GetMutableGlobalCumulMPOptimizer(const RoutingDimension &dimension) const
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...
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 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.
bool HasTemporalTypeRequirements() const
IntVar * NextVar(int64_t index) const
!defined(SWIGPYTHON)
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
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...
Assignment * ReadAssignmentFromRoutes(const std::vector< std::vector< int64_t > > &routes, bool ignore_inactive_indices)
Restores the routes as the current solution.
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.
const TransitCallback1 & UnaryTransitCallbackOrNull(int callback_index) const
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...
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.
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.
const std::vector< RoutingDimension * > & GetDimensions() const
Returns all dimensions of the model.
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 VariableIndexEvaluator2 & StateDependentTransitCallback(int callback_index) const
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_,...
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...
const std::vector< DisjunctionIndex > & GetDisjunctionIndices(int64_t index) const
Returns the indices of the disjunctions to which an index belongs.
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...
int AddResourceGroup()
Adds a resource group to the routing model.
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...
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.
IntVar * ActiveVar(int64_t index) const
Returns the active variable of the node corresponding to index.
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...
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...
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)
IntVar * CostVar() const
Returns the global cost variable which is being minimized.
bool HasSameVehicleTypeRequirements() const
Returns true iff any same-route (resp.
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.
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
bool HasMaxCardinalityConstrainedDisjunctions() const
Returns true if the model contains at least one disjunction which is constrained by its max_cardinali...
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
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...
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'.
const std::string & GetPrimaryConstrainedDimension() const
Get the primary constrained dimension, or an empty string if it is unset.
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.
const std::vector< int64_t > & GetDisjunctionNodeIndices(DisjunctionIndex index) const
Returns the variable indices of the nodes in the disjunction of index 'index'.
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.
int32_t number_of_solutions_to_collect() const
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.
@ 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
T * RevAlloc(T *object)
Registers the given object as being reversible.
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)
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 FindCopy(const Collection &collection, const Key &key, Value *const value)
const Collection::value_type::second_type & FindOrDie(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 * FindOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
bool ContainsKey(const Collection &collection, const Key &key)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
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.
int64_t CapAdd(int64_t x, int64_t y)
RangeIntToIntFunction * MakeCachedIntToIntFunction(const std::function< int64_t(int64_t)> &f, int64_t domain_start, int64_t domain_end)
const std::string & FirstSolutionStrategy_Value_Name(T enum_t_value)
std::function< int64_t(int64_t, int64_t)> RoutingTransitCallback2
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
constexpr int FirstSolutionStrategy_Value_Value_ARRAYSIZE
RoutingModelParameters DefaultRoutingModelParameters()
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,...
FirstSolutionStrategy_Value
int64_t CapSub(int64_t x, int64_t y)
IntVarLocalSearchFilter * MakeVehicleAmortizedCostFilter(const RoutingModel &routing_model)
Returns a filter computing vehicle amortized costs.
void SetAssignmentFromAssignment(Assignment *target_assignment, const std::vector< IntVar * > &target_vars, const Assignment *source_assignment, const std::vector< IntVar * > &source_vars)
NOLINT.
constexpr FirstSolutionStrategy_Value FirstSolutionStrategy_Value_Value_MIN
RangeMinMaxIndexFunction * MakeCachedRangeMinMaxIndexFunction(const std::function< int64_t(int64_t)> &f, int64_t domain_start, int64_t domain_end)
constexpr FirstSolutionStrategy_Value FirstSolutionStrategy_Value_Value_MAX
IntVarLocalSearchFilter * MakeCPFeasibilityFilter(RoutingModel *routing_model)
Returns a filter checking the current solution using CP propagation.
DecisionBuilder * MakeRestoreDimensionValuesForUnchangedRoutes(RoutingModel *model)
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...
void AppendDimensionCumulFilters(const std::vector< RoutingDimension * > &dimensions, const RoutingSearchParameters ¶meters, bool filter_objective_cost, bool filter_light_weight_unary_dimensions, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
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
Demon * MakeConstraintDemon0(Solver *const s, T *const ct, void(T::*method)(), const std::string &name)
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.
Demon * MakeDelayedConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
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
Demon * MakeConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
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, bool filter_cost)
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.
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...