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"
59#include "ortools/constraint_solver/routing_enums.pb.h"
65#include "ortools/constraint_solver/routing_parameters.pb.h"
68#include "ortools/constraint_solver/solver_parameters.pb.h"
74#include "ortools/util/optional_boolean.pb.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());
921 ConstraintSolverParameters solver_parameters =
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();
1423 return dimension_name_to_index_.contains(dimension_name);
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(
2151 const RoutingSearchParameters&
parameters,
int node_index,
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: ",
2232 FirstSolutionStrategy::Value_Name(first_solution_strategy),
2233 " (int value: ", first_solution_strategy,
")");
2235 if (search_parameters.first_solution_strategy() ==
2236 FirstSolutionStrategy::SWEEP &&
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 &&
2513 infeasible_policies->contains(index_to_type_policy_[i])) {
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];
2847 solver_->AddConstraint(solver_->MakeEquality(
2848 vehicle_route_considered_[v],
2849 solver_->MakeIsDifferentCstVar(resource_var, -1)));
2860 IntVar*
const resource_start_lb_var =
2863 solver_->AddConstraint(MakeLightElement(
2864 solver_.get(), resource_start_lb_var, resource_var,
2865 [dim, resource_group](
int r) {
2866 if (r < 0) return std::numeric_limits<int64_t>::min();
2867 return resource_group->GetResources()[r]
2868 .GetDimensionAttributes(dim)
2872 [
this]() { return enable_deep_serialization_; }));
2873 solver_->AddConstraint(solver_->MakeGreaterOrEqual(
2874 start_cumul_var, resource_start_lb_var));
2876 IntVar*
const resource_start_ub_var =
2879 solver_->AddConstraint(MakeLightElement(
2880 solver_.get(), resource_start_ub_var, resource_var,
2881 [dim, resource_group](
int r) {
2882 if (r < 0) return std::numeric_limits<int64_t>::max();
2883 return resource_group->GetResources()[r]
2884 .GetDimensionAttributes(dim)
2888 [
this]() { return enable_deep_serialization_; }));
2889 solver_->AddConstraint(
2890 solver_->MakeLessOrEqual(start_cumul_var, resource_start_ub_var));
2894 IntVar*
const resource_end_lb_var =
2897 solver_->AddConstraint(MakeLightElement(
2898 solver_.get(), resource_end_lb_var, resource_var,
2899 [dim, resource_group](
int r) {
2900 if (r < 0) return std::numeric_limits<int64_t>::min();
2901 return resource_group->GetResources()[r]
2902 .GetDimensionAttributes(dim)
2906 [
this]() { return enable_deep_serialization_; }));
2907 solver_->AddConstraint(
2908 solver_->MakeGreaterOrEqual(end_cumul_var, resource_end_lb_var));
2910 IntVar*
const resource_end_ub_var =
2913 solver_->AddConstraint(MakeLightElement(
2914 solver_.get(), resource_end_ub_var, resource_var,
2915 [dim, resource_group](
int r) {
2916 if (r < 0) return std::numeric_limits<int64_t>::max();
2917 return resource_group->GetResources()[r]
2918 .GetDimensionAttributes(dim)
2922 [
this]() { return enable_deep_serialization_; }));
2923 solver_->AddConstraint(
2924 solver_->MakeLessOrEqual(end_cumul_var, resource_end_ub_var));
2929 DetectImplicitPickupAndDeliveries();
2938 CreateFirstSolutionDecisionBuilders(
parameters);
2939 error = FindErrorInSearchParametersForModel(
parameters);
2940 if (!error.empty()) {
2942 LOG(
ERROR) <<
"Invalid RoutingSearchParameters for this model: " << error;
2955class RestoreDimensionValuesForUnchangedRoutes :
public DecisionBuilder {
2959 model_->AddAtSolutionCallback([
this]() { AtSolution(); });
2960 next_last_value_.resize(model_->Nexts().size(), -1);
2965 Decision*
Next(Solver*
const s)
override {
2966 if (!must_return_decision_)
return nullptr;
2967 s->SaveAndSetValue(&must_return_decision_,
false);
2968 return MakeDecision(s);
2975 is_initialized_ =
true;
2976 const int num_nodes = model_->VehicleVars().size();
2977 node_to_integer_variable_indices_.resize(num_nodes);
2978 node_to_interval_variable_indices_.resize(num_nodes);
2980 for (
const std::string& dimension_name : model_->GetAllDimensionNames()) {
2982 model_->GetDimensionOrDie(dimension_name);
2984 for (
const std::vector<IntVar*>& dimension_variables :
2985 {dimension.cumuls(), dimension.slacks()}) {
2986 const int num_dimension_variables = dimension_variables.size();
2987 DCHECK_LE(num_dimension_variables, num_nodes);
2988 for (
int node = 0; node < num_dimension_variables; ++node) {
2989 node_to_integer_variable_indices_[node].push_back(
2990 integer_variables_.size());
2991 integer_variables_.push_back(dimension_variables[node]);
2995 for (
int vehicle = 0; vehicle < model_->vehicles(); ++vehicle) {
2996 if (!dimension.HasBreakConstraints())
continue;
2997 const int vehicle_start = model_->Start(vehicle);
2999 dimension.GetBreakIntervalsOfVehicle(vehicle)) {
3000 node_to_interval_variable_indices_[vehicle_start].push_back(
3001 interval_variables_.size());
3002 interval_variables_.push_back(
interval);
3006 integer_variables_last_min_.resize(integer_variables_.size());
3007 interval_variables_last_start_min_.resize(interval_variables_.size());
3008 interval_variables_last_end_max_.resize(interval_variables_.size());
3011 Decision* MakeDecision(Solver*
const s) {
3012 if (!is_initialized_)
return nullptr;
3014 std::vector<int> unchanged_vehicles;
3015 const int num_vehicles = model_->vehicles();
3016 for (
int v = 0; v < num_vehicles; ++v) {
3017 bool unchanged =
true;
3018 for (
int current = model_->Start(v); !model_->IsEnd(current);
3019 current = next_last_value_[current]) {
3020 if (!model_->NextVar(current)->Bound() ||
3021 next_last_value_[current] != model_->NextVar(current)->Value()) {
3026 if (unchanged) unchanged_vehicles.push_back(v);
3030 if (unchanged_vehicles.size() == num_vehicles)
return nullptr;
3033 std::vector<IntVar*> vars;
3034 std::vector<int64_t> values;
3035 for (
const int vehicle : unchanged_vehicles) {
3036 for (
int current = model_->Start(vehicle);
true;
3037 current = next_last_value_[current]) {
3038 for (
const int index : node_to_integer_variable_indices_[current]) {
3039 vars.push_back(integer_variables_[
index]);
3040 values.push_back(integer_variables_last_min_[
index]);
3042 for (
const int index : node_to_interval_variable_indices_[current]) {
3043 const int64_t
start_min = interval_variables_last_start_min_[
index];
3044 const int64_t
end_max = interval_variables_last_end_max_[
index];
3046 vars.push_back(interval_variables_[
index]->SafeStartExpr(0)->Var());
3047 values.push_back(interval_variables_last_start_min_[
index]);
3048 vars.push_back(interval_variables_[
index]->SafeEndExpr(0)->Var());
3049 values.push_back(interval_variables_last_end_max_[
index]);
3051 vars.push_back(interval_variables_[
index]->PerformedExpr()->Var());
3052 values.push_back(0);
3055 if (model_->IsEnd(current))
break;
3058 return s->MakeAssignVariablesValuesOrDoNothing(vars, values);
3062 if (!is_initialized_) Initialize();
3063 const int num_integers = integer_variables_.size();
3066 for (
int i = 0; i < num_integers; ++i) {
3067 integer_variables_last_min_[i] = integer_variables_[i]->Min();
3069 const int num_intervals = interval_variables_.size();
3070 for (
int i = 0; i < num_intervals; ++i) {
3071 const bool is_performed = interval_variables_[i]->MustBePerformed();
3072 interval_variables_last_start_min_[i] =
3073 is_performed ? interval_variables_[i]->StartMin() : 0;
3074 interval_variables_last_end_max_[i] =
3075 is_performed ? interval_variables_[i]->EndMax() : -1;
3077 const int num_nodes = next_last_value_.size();
3078 for (
int node = 0; node < num_nodes; ++node) {
3079 if (model_->IsEnd(node))
continue;
3080 next_last_value_[node] = model_->NextVar(node)->Value();
3088 std::vector<int> next_last_value_;
3091 std::vector<std::vector<int>> node_to_integer_variable_indices_;
3092 std::vector<std::vector<int>> node_to_interval_variable_indices_;
3094 std::vector<IntVar*> integer_variables_;
3095 std::vector<int64_t> integer_variables_last_min_;
3096 std::vector<IntervalVar*> interval_variables_;
3097 std::vector<int64_t> interval_variables_last_start_min_;
3098 std::vector<int64_t> interval_variables_last_end_max_;
3100 bool is_initialized_ =
false;
3101 bool must_return_decision_ =
true;
3107 return model->solver()->RevAlloc(
3108 new RestoreDimensionValuesForUnchangedRoutes(
model));
3112 monitors_.push_back(monitor);
3120 bool AtSolution()
override {
3126 std::function<void()> callback_;
3132 new AtSolutionCallbackMonitor(solver_.get(), std::move(
callback))));
3142 std::vector<const Assignment*>* solutions) {
3147absl::Duration GetTimeLimit(
const RoutingSearchParameters&
parameters) {
3148 if (!
parameters.has_time_limit())
return absl::InfiniteDuration();
3152absl::Duration GetLnsTimeLimit(
const RoutingSearchParameters&
parameters) {
3153 if (!
parameters.has_lns_time_limit())
return absl::InfiniteDuration();
3161 Assignment* assignment) {
3162 assignment->Clear();
3163 for (
int i = 0; i <
model->Nexts().size(); ++i) {
3164 if (!
model->IsStart(i)) {
3165 assignment->Add(
model->NextVar(i))->SetValue(i);
3168 for (
int vehicle = 0; vehicle <
model->vehicles(); ++vehicle) {
3169 assignment->Add(
model->NextVar(
model->Start(vehicle)))
3170 ->SetValue(
model->End(vehicle));
3175bool RoutingModel::AppendAssignmentIfFeasible(
3176 const Assignment& assignment,
3177 std::vector<std::unique_ptr<Assignment>>* assignments) {
3178 tmp_assignment_->CopyIntersection(&assignment);
3179 solver_->Solve(restore_tmp_assignment_, collect_one_assignment_,
3180 GetOrCreateLimit());
3181 if (collect_one_assignment_->solution_count() == 1) {
3182 assignments->push_back(absl::make_unique<Assignment>(solver_.get()));
3183 assignments->back()->Copy(collect_one_assignment_->solution(0));
3189void RoutingModel::LogSolution(
const RoutingSearchParameters&
parameters,
3190 const std::string& description,
3191 int64_t solution_cost, int64_t start_time_ms) {
3193 const double cost_scaling_factor =
parameters.log_cost_scaling_factor();
3194 const double cost_offset =
parameters.log_cost_offset();
3195 const std::string cost_string =
3196 cost_scaling_factor == 1.0 && cost_offset == 0.0
3197 ? absl::StrCat(solution_cost)
3199 "%d (%.8lf)", solution_cost,
3200 cost_scaling_factor * (solution_cost + cost_offset));
3202 "%s (%s, time = %d ms, memory used = %s)", description, cost_string,
3203 solver_->wall_time() - start_time_ms, memory_str);
3208 std::vector<const Assignment*>* solutions) {
3214 const std::vector<const Assignment*>& assignments,
3216 std::vector<const Assignment*>* solutions) {
3217 const int64_t start_time_ms = solver_->wall_time();
3220 if (solutions !=
nullptr) solutions->clear();
3226 if (!solver_->CheckConstraint(solver_->MakeTrueConstraint())) {
3231 const auto update_time_limits = [
this, start_time_ms, &
parameters]() {
3232 const absl::Duration elapsed_time =
3233 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3234 const absl::Duration time_left = GetTimeLimit(
parameters) - elapsed_time;
3235 if (time_left >= absl::ZeroDuration()) {
3245 if (!update_time_limits()) {
3249 lns_limit_->UpdateLimits(
3257 const int time_limit_shares = 1 + !global_dimension_optimizers_.empty() +
3258 !local_dimension_optimizers_.empty();
3259 const absl::Duration first_solution_lns_time_limit =
3262 first_solution_lns_limit_->UpdateLimits(
3266 std::vector<std::unique_ptr<Assignment>> solution_pool;
3267 std::vector<const Assignment*> first_solution_assignments;
3268 for (
const Assignment* assignment : assignments) {
3269 if (assignment !=
nullptr) first_solution_assignments.push_back(assignment);
3272 if (first_solution_assignments.empty()) {
3273 bool solution_found =
false;
3276 AppendAssignmentIfFeasible(matching, &solution_pool)) {
3278 LogSolution(
parameters,
"Min-Cost Flow Solution",
3279 solution_pool.back()->ObjectiveValue(), start_time_ms);
3281 solution_found =
true;
3283 if (!solution_found) {
3287 MakeAllUnperformedInAssignment(
this, &unperformed);
3288 if (AppendAssignmentIfFeasible(unperformed, &solution_pool) &&
3290 LogSolution(
parameters,
"All Unperformed Solution",
3291 solution_pool.back()->ObjectiveValue(), start_time_ms);
3293 if (update_time_limits()) {
3294 solver_->Solve(solve_db_, monitors_);
3298 for (
const Assignment* assignment : first_solution_assignments) {
3299 assignment_->CopyIntersection(assignment);
3300 solver_->Solve(improve_db_, monitors_);
3301 if (collect_assignments_->solution_count() >= 1 ||
3302 !update_time_limits()) {
3310 parameters.use_generalized_cp_sat() == BOOL_TRUE) {
3311 const int solution_count = collect_assignments_->solution_count();
3313 solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
3317 AppendAssignmentIfFeasible(sat_solution, &solution_pool) &&
3319 LogSolution(
parameters,
"SAT", solution_pool.back()->ObjectiveValue(),
3324 const absl::Duration elapsed_time =
3325 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3326 const int solution_count = collect_assignments_->solution_count();
3327 if (solution_count >= 1 || !solution_pool.empty()) {
3329 if (solutions !=
nullptr) {
3330 for (
int i = 0; i < solution_count; ++i) {
3331 solutions->push_back(
3332 solver_->MakeAssignment(collect_assignments_->solution(i)));
3334 for (
const auto& solution : solution_pool) {
3335 if (solutions->empty() ||
3336 solution->ObjectiveValue() < solutions->back()->ObjectiveValue()) {
3337 solutions->push_back(solver_->MakeAssignment(solution.get()));
3340 return solutions->back();
3343 solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
3345 for (
const auto& solution : solution_pool) {
3346 if (best_assignment ==
nullptr ||
3348 best_assignment = solution.get();
3351 return solver_->MakeAssignment(best_assignment);
3353 if (elapsed_time >= GetTimeLimit(
parameters)) {
3365 const int size =
Size();
3371 source_model->
Nexts());
3373 std::vector<IntVar*> source_vars(size + size + vehicles_);
3374 std::vector<IntVar*> target_vars(size + size + vehicles_);
3384 source_assignment, source_vars);
3402 LOG(
WARNING) <<
"Non-closed model not supported.";
3406 LOG(
WARNING) <<
"Non-homogeneous vehicle costs not supported";
3409 if (!disjunctions_.empty()) {
3411 <<
"Node disjunction constraints or optional nodes not supported.";
3414 const int num_nodes =
Size() + vehicles_;
3421 std::unique_ptr<IntVarIterator> iterator(
3422 nexts_[
tail]->MakeDomainIterator(
false));
3445 return linear_sum_assignment.
GetCost();
3450bool RoutingModel::RouteCanBeUsedByVehicle(
const Assignment& assignment,
3451 int start_index,
int vehicle)
const {
3453 IsStart(start_index) ?
Next(assignment, start_index) : start_index;
3454 while (!
IsEnd(current_index)) {
3456 if (!vehicle_var->
Contains(vehicle)) {
3459 const int next_index =
Next(assignment, current_index);
3460 CHECK_NE(next_index, current_index) <<
"Inactive node inside a route";
3461 current_index = next_index;
3466bool RoutingModel::ReplaceUnusedVehicle(
3467 int unused_vehicle,
int active_vehicle,
3468 Assignment*
const compact_assignment)
const {
3469 CHECK(compact_assignment !=
nullptr);
3473 const int unused_vehicle_start =
Start(unused_vehicle);
3474 IntVar*
const unused_vehicle_start_var =
NextVar(unused_vehicle_start);
3475 const int unused_vehicle_end =
End(unused_vehicle);
3476 const int active_vehicle_start =
Start(active_vehicle);
3477 const int active_vehicle_end =
End(active_vehicle);
3478 IntVar*
const active_vehicle_start_var =
NextVar(active_vehicle_start);
3479 const int active_vehicle_next =
3480 compact_assignment->Value(active_vehicle_start_var);
3481 compact_assignment->SetValue(unused_vehicle_start_var, active_vehicle_next);
3482 compact_assignment->SetValue(active_vehicle_start_var,
End(active_vehicle));
3485 int current_index = active_vehicle_next;
3486 while (!
IsEnd(current_index)) {
3487 IntVar*
const vehicle_var =
VehicleVar(current_index);
3488 compact_assignment->SetValue(vehicle_var, unused_vehicle);
3489 const int next_index =
Next(*compact_assignment, current_index);
3490 if (
IsEnd(next_index)) {
3491 IntVar*
const last_next_var =
NextVar(current_index);
3492 compact_assignment->SetValue(last_next_var,
End(unused_vehicle));
3494 current_index = next_index;
3499 const std::vector<IntVar*>& transit_variables = dimension->transits();
3500 IntVar*
const unused_vehicle_transit_var =
3501 transit_variables[unused_vehicle_start];
3502 IntVar*
const active_vehicle_transit_var =
3503 transit_variables[active_vehicle_start];
3504 const bool contains_unused_vehicle_transit_var =
3505 compact_assignment->Contains(unused_vehicle_transit_var);
3506 const bool contains_active_vehicle_transit_var =
3507 compact_assignment->Contains(active_vehicle_transit_var);
3508 if (contains_unused_vehicle_transit_var !=
3509 contains_active_vehicle_transit_var) {
3511 LOG(
INFO) <<
"The assignment contains transit variable for dimension '"
3512 << dimension->name() <<
"' for some vehicles, but not for all";
3515 if (contains_unused_vehicle_transit_var) {
3516 const int64_t old_unused_vehicle_transit =
3517 compact_assignment->Value(unused_vehicle_transit_var);
3518 const int64_t old_active_vehicle_transit =
3519 compact_assignment->Value(active_vehicle_transit_var);
3520 compact_assignment->SetValue(unused_vehicle_transit_var,
3521 old_active_vehicle_transit);
3522 compact_assignment->SetValue(active_vehicle_transit_var,
3523 old_unused_vehicle_transit);
3527 const std::vector<IntVar*>& cumul_variables = dimension->cumuls();
3528 IntVar*
const unused_vehicle_cumul_var =
3529 cumul_variables[unused_vehicle_end];
3530 IntVar*
const active_vehicle_cumul_var =
3531 cumul_variables[active_vehicle_end];
3532 const int64_t old_unused_vehicle_cumul =
3533 compact_assignment->Value(unused_vehicle_cumul_var);
3534 const int64_t old_active_vehicle_cumul =
3535 compact_assignment->Value(active_vehicle_cumul_var);
3536 compact_assignment->SetValue(unused_vehicle_cumul_var,
3537 old_active_vehicle_cumul);
3538 compact_assignment->SetValue(active_vehicle_cumul_var,
3539 old_unused_vehicle_cumul);
3546 return CompactAssignmentInternal(assignment,
false);
3551 return CompactAssignmentInternal(assignment,
true);
3554Assignment* RoutingModel::CompactAssignmentInternal(
3555 const Assignment& assignment,
bool check_compact_assignment)
const {
3559 <<
"The costs are not homogeneous, routes cannot be rearranged";
3563 std::unique_ptr<Assignment> compact_assignment(
new Assignment(&assignment));
3564 for (
int vehicle = 0; vehicle < vehicles_ - 1; ++vehicle) {
3568 const int vehicle_start =
Start(vehicle);
3569 const int vehicle_end =
End(vehicle);
3571 int swap_vehicle = vehicles_ - 1;
3572 bool has_more_vehicles_with_route =
false;
3573 for (; swap_vehicle > vehicle; --swap_vehicle) {
3580 has_more_vehicles_with_route =
true;
3581 const int swap_vehicle_start =
Start(swap_vehicle);
3582 const int swap_vehicle_end =
End(swap_vehicle);
3583 if (manager_.IndexToNode(vehicle_start) !=
3584 manager_.IndexToNode(swap_vehicle_start) ||
3585 manager_.IndexToNode(vehicle_end) !=
3586 manager_.IndexToNode(swap_vehicle_end)) {
3591 if (RouteCanBeUsedByVehicle(*compact_assignment, swap_vehicle_start,
3597 if (swap_vehicle == vehicle) {
3598 if (has_more_vehicles_with_route) {
3602 LOG(
INFO) <<
"No vehicle that can be swapped with " << vehicle
3609 if (!ReplaceUnusedVehicle(vehicle, swap_vehicle,
3610 compact_assignment.get())) {
3615 if (check_compact_assignment &&
3616 !solver_->CheckAssignment(compact_assignment.get())) {
3618 LOG(
WARNING) <<
"The compacted assignment is not a valid solution";
3621 return compact_assignment.release();
3624int RoutingModel::FindNextActive(
int index,
3625 const std::vector<int64_t>& indices)
const {
3628 const int size = indices.size();
3639 preassignment_->Clear();
3640 IntVar* next_var =
nullptr;
3641 int lock_index = FindNextActive(-1, locks);
3642 const int size = locks.size();
3643 if (lock_index < size) {
3644 next_var =
NextVar(locks[lock_index]);
3645 preassignment_->Add(next_var);
3646 for (lock_index = FindNextActive(lock_index, locks); lock_index < size;
3647 lock_index = FindNextActive(lock_index, locks)) {
3648 preassignment_->SetValue(next_var, locks[lock_index]);
3649 next_var =
NextVar(locks[lock_index]);
3650 preassignment_->Add(next_var);
3657 const std::vector<std::vector<int64_t>>& locks,
bool close_routes) {
3658 preassignment_->Clear();
3663 const RoutingSearchParameters&
parameters)
const {
3665 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3671 const RoutingSearchParameters&
parameters)
const {
3673 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3679 if (collect_assignments_->solution_count() == 1 && assignment_ !=
nullptr) {
3680 assignment_->CopyIntersection(collect_assignments_->solution(0));
3681 return assignment_->Save(file_name);
3689 CHECK(assignment_ !=
nullptr);
3690 if (assignment_->Load(file_name)) {
3691 return DoRestoreAssignment();
3698 CHECK(assignment_ !=
nullptr);
3699 assignment_->CopyIntersection(&solution);
3700 return DoRestoreAssignment();
3703Assignment* RoutingModel::DoRestoreAssignment() {
3707 solver_->Solve(restore_assignment_, monitors_);
3708 if (collect_assignments_->solution_count() == 1) {
3710 return collect_assignments_->solution(0);
3719 const std::vector<std::vector<int64_t>>& routes,
3720 bool ignore_inactive_indices,
bool close_routes,
3722 CHECK(assignment !=
nullptr);
3724 LOG(
ERROR) <<
"The model is not closed yet";
3727 const int num_routes = routes.size();
3728 if (num_routes > vehicles_) {
3729 LOG(
ERROR) <<
"The number of vehicles in the assignment (" << routes.size()
3730 <<
") is greater than the number of vehicles in the model ("
3731 << vehicles_ <<
")";
3735 absl::flat_hash_set<int> visited_indices;
3737 for (
int vehicle = 0; vehicle < num_routes; ++vehicle) {
3738 const std::vector<int64_t>& route = routes[vehicle];
3739 int from_index =
Start(vehicle);
3740 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3741 visited_indices.insert(from_index);
3742 if (!insert_result.second) {
3743 LOG(
ERROR) <<
"Index " << from_index <<
" (start node for vehicle "
3744 << vehicle <<
") was already used";
3748 for (
const int64_t to_index : route) {
3749 if (to_index < 0 || to_index >=
Size()) {
3750 LOG(
ERROR) <<
"Invalid index: " << to_index;
3755 if (active_var->
Max() == 0) {
3756 if (ignore_inactive_indices) {
3759 LOG(
ERROR) <<
"Index " << to_index <<
" is not active";
3764 insert_result = visited_indices.insert(to_index);
3765 if (!insert_result.second) {
3766 LOG(
ERROR) <<
"Index " << to_index <<
" is used multiple times";
3771 if (!vehicle_var->
Contains(vehicle)) {
3772 LOG(
ERROR) <<
"Vehicle " << vehicle <<
" is not allowed at index "
3778 if (!assignment->
Contains(from_var)) {
3779 assignment->
Add(from_var);
3781 assignment->
SetValue(from_var, to_index);
3783 from_index = to_index;
3788 if (!assignment->
Contains(last_var)) {
3789 assignment->
Add(last_var);
3796 for (
int vehicle = num_routes; vehicle < vehicles_; ++vehicle) {
3797 const int start_index =
Start(vehicle);
3800 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3801 visited_indices.insert(start_index);
3802 if (!insert_result.second) {
3803 LOG(
ERROR) <<
"Index " << start_index <<
" is used multiple times";
3808 if (!assignment->
Contains(start_var)) {
3809 assignment->
Add(start_var);
3818 if (!visited_indices.contains(
index)) {
3820 if (!assignment->
Contains(next_var)) {
3821 assignment->
Add(next_var);
3832 const std::vector<std::vector<int64_t>>& routes,
3833 bool ignore_inactive_indices) {
3841 return DoRestoreAssignment();
3846 std::vector<std::vector<int64_t>>*
const routes)
const {
3848 CHECK(routes !=
nullptr);
3850 const int model_size =
Size();
3851 routes->resize(vehicles_);
3852 for (
int vehicle = 0; vehicle < vehicles_; ++vehicle) {
3853 std::vector<int64_t>*
const vehicle_route = &routes->at(vehicle);
3854 vehicle_route->clear();
3856 int num_visited_indices = 0;
3857 const int first_index =
Start(vehicle);
3861 int current_index = assignment.
Value(first_var);
3862 while (!
IsEnd(current_index)) {
3863 vehicle_route->push_back(current_index);
3868 current_index = assignment.
Value(next_var);
3870 ++num_visited_indices;
3871 CHECK_LE(num_visited_indices, model_size)
3872 <<
"The assignment contains a cycle";
3880 std::vector<std::vector<int64_t>> route_indices(
vehicles());
3881 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3883 LOG(DFATAL) <<
"GetRoutesFromAssignment() called on incomplete solution:"
3884 <<
" NextVar(" << vehicle <<
") is unbound.";
3887 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3889 route_indices[vehicle].push_back(
index);
3892 route_indices[vehicle].push_back(
index);
3895 return route_indices;
3899int64_t RoutingModel::GetArcCostForClassInternal(
3900 int64_t from_index, int64_t to_index,
3904 DCHECK_LT(cost_class_index, cost_classes_.size());
3905 CostCacheElement*
const cache = &cost_cache_[from_index];
3907 if (cache->index ==
static_cast<int>(to_index) &&
3908 cache->cost_class_index == cost_class_index) {
3912 const CostClass& cost_class = cost_classes_[cost_class_index];
3913 const auto& evaluator = transit_evaluators_[cost_class.evaluator_index];
3915 cost =
CapAdd(evaluator(from_index, to_index),
3916 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3917 }
else if (!
IsEnd(to_index)) {
3921 evaluator(from_index, to_index),
3922 CapAdd(GetDimensionTransitCostSum(from_index, to_index, cost_class),
3923 fixed_cost_of_vehicle_[index_to_vehicle_[from_index]]));
3927 if (vehicle_used_when_empty_[index_to_vehicle_[from_index]]) {
3929 CapAdd(evaluator(from_index, to_index),
3930 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3935 *cache = {
static_cast<int>(to_index), cost_class_index,
cost};
3944 int vehicle)
const {
3958 return assignment.
Value(next_var);
3962 int64_t vehicle)
const {
3963 if (from_index != to_index && vehicle >= 0) {
3964 return GetArcCostForClassInternal(from_index, to_index,
3972 int64_t from_index, int64_t to_index,
3973 int64_t cost_class_index)
const {
3974 if (from_index != to_index) {
3975 return GetArcCostForClassInternal(from_index, to_index,
3983 int64_t to_index)
const {
3987 if (!is_bound_to_end_ct_added_.Switched()) {
3990 std::vector<IntVar*> zero_transit(
Size(), solver_->MakeIntConst(0));
3991 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
3992 nexts_, active_, is_bound_to_end_, zero_transit));
3993 is_bound_to_end_ct_added_.Switch(solver_.get());
3995 if (is_bound_to_end_[to_index]->Min() == 1)
4001int64_t RoutingModel::GetDimensionTransitCostSum(
4002 int64_t i, int64_t j,
const CostClass& cost_class)
const {
4004 for (
const auto& evaluator_and_coefficient :
4005 cost_class.dimension_transit_evaluator_class_and_cost_coefficient) {
4006 DCHECK_GT(evaluator_and_coefficient.cost_coefficient, 0);
4009 CapProd(evaluator_and_coefficient.cost_coefficient,
4010 evaluator_and_coefficient.dimension->GetTransitValueFromClass(
4011 i, j, evaluator_and_coefficient.transit_evaluator_class)));
4027 const bool mandatory1 = active_[to1]->Min() == 1;
4028 const bool mandatory2 = active_[to2]->Min() == 1;
4030 if (mandatory1 != mandatory2)
return mandatory1;
4037 const int64_t src_vehicle = src_vehicle_var->
Max();
4038 if (src_vehicle_var->
Bound()) {
4045 mandatory1 ? to1_vehicle_var->
Bound() : (to1_vehicle_var->
Size() <= 2);
4047 mandatory2 ? to2_vehicle_var->
Bound() : (to2_vehicle_var->
Size() <= 2);
4050 if (bound1 != bound2)
return bound1;
4053 const int64_t vehicle1 = to1_vehicle_var->
Max();
4054 const int64_t vehicle2 = to2_vehicle_var->
Max();
4057 if ((vehicle1 == src_vehicle) != (vehicle2 == src_vehicle)) {
4058 return vehicle1 == src_vehicle;
4063 if (vehicle1 != src_vehicle)
return to1 < to2;
4074 const std::vector<IntVar*>& cumul_vars =
4076 IntVar*
const dim1 = cumul_vars[to1];
4077 IntVar*
const dim2 = cumul_vars[to2];
4080 if (dim1->
Max() != dim2->
Max())
return dim1->
Max() < dim2->
Max();
4089 const int64_t cost_class_index =
4090 SafeGetCostClassInt64OfVehicle(src_vehicle);
4091 const int64_t cost1 =
4094 const int64_t cost2 =
4097 if (cost1 != cost2)
return cost1 < cost2;
4104 if (num_vehicles1 != num_vehicles2)
return num_vehicles1 < num_vehicles2;
4114 DCHECK_EQ(index_to_visit_type_.size(), index_to_type_policy_.size());
4115 index_to_visit_type_[
index] = type;
4116 index_to_type_policy_[
index] = policy;
4117 num_visit_types_ =
std::max(num_visit_types_, type + 1);
4122 return index_to_visit_type_[
index];
4126 DCHECK_LT(type, single_nodes_of_type_.size());
4127 return single_nodes_of_type_[type];
4131 DCHECK_LT(type, pair_indices_of_type_.size());
4132 return pair_indices_of_type_[type];
4136 int64_t
index)
const {
4138 return index_to_type_policy_[
index];
4142 hard_incompatible_types_per_type_index_.resize(num_visit_types_);
4143 temporal_incompatible_types_per_type_index_.resize(num_visit_types_);
4144 same_vehicle_required_type_alternatives_per_type_index_.resize(
4146 required_type_alternatives_when_adding_type_index_.resize(num_visit_types_);
4147 required_type_alternatives_when_removing_type_index_.resize(num_visit_types_);
4152 hard_incompatible_types_per_type_index_.size());
4153 has_hard_type_incompatibilities_ =
true;
4155 hard_incompatible_types_per_type_index_[type1].insert(type2);
4156 hard_incompatible_types_per_type_index_[type2].insert(type1);
4161 temporal_incompatible_types_per_type_index_.size());
4162 has_temporal_type_incompatibilities_ =
true;
4164 temporal_incompatible_types_per_type_index_[type1].insert(type2);
4165 temporal_incompatible_types_per_type_index_[type2].insert(type1);
4168const absl::flat_hash_set<int>&
4171 DCHECK_LT(type, hard_incompatible_types_per_type_index_.size());
4172 return hard_incompatible_types_per_type_index_[type];
4175const absl::flat_hash_set<int>&
4178 DCHECK_LT(type, temporal_incompatible_types_per_type_index_.size());
4179 return temporal_incompatible_types_per_type_index_[type];
4185 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4187 same_vehicle_required_type_alternatives_per_type_index_.size());
4189 if (required_type_alternatives.empty()) {
4193 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4194 trivially_infeasible_visit_types_to_policies_[dependent_type];
4201 has_same_vehicle_type_requirements_ =
true;
4202 same_vehicle_required_type_alternatives_per_type_index_[dependent_type]
4203 .push_back(std::move(required_type_alternatives));
4207 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4209 required_type_alternatives_when_adding_type_index_.size());
4211 if (required_type_alternatives.empty()) {
4215 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4216 trivially_infeasible_visit_types_to_policies_[dependent_type];
4222 has_temporal_type_requirements_ =
true;
4223 required_type_alternatives_when_adding_type_index_[dependent_type].push_back(
4224 std::move(required_type_alternatives));
4228 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4230 required_type_alternatives_when_removing_type_index_.size());
4232 if (required_type_alternatives.empty()) {
4236 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4237 trivially_infeasible_visit_types_to_policies_[dependent_type];
4244 has_temporal_type_requirements_ =
true;
4245 required_type_alternatives_when_removing_type_index_[dependent_type]
4246 .push_back(std::move(required_type_alternatives));
4249const std::vector<absl::flat_hash_set<int>>&
4253 same_vehicle_required_type_alternatives_per_type_index_.size());
4254 return same_vehicle_required_type_alternatives_per_type_index_[type];
4257const std::vector<absl::flat_hash_set<int>>&
4260 DCHECK_LT(type, required_type_alternatives_when_adding_type_index_.size());
4261 return required_type_alternatives_when_adding_type_index_[type];
4264const std::vector<absl::flat_hash_set<int>>&
4267 DCHECK_LT(type, required_type_alternatives_when_removing_type_index_.size());
4268 return required_type_alternatives_when_removing_type_index_[type];
4276 int64_t var_index)
const {
4277 if (active_[var_index]->Min() == 1)
4279 const std::vector<DisjunctionIndex>& disjunction_indices =
4281 if (disjunction_indices.size() != 1)
return default_value;
4286 return std::max(int64_t{0}, disjunctions_[disjunction_index].value.penalty);
4291 const std::string& dimension_to_print)
const {
4292 for (
int i = 0; i <
Size(); ++i) {
4295 <<
"DebugOutputVehicleSchedules() called on incomplete solution:"
4296 <<
" NextVar(" << i <<
") is unbound.";
4301 absl::flat_hash_set<std::string> dimension_names;
4302 if (dimension_to_print.empty()) {
4304 dimension_names.insert(all_dimension_names.begin(),
4305 all_dimension_names.end());
4307 dimension_names.insert(dimension_to_print);
4309 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4310 int empty_vehicle_range_start = vehicle;
4315 if (empty_vehicle_range_start != vehicle) {
4316 if (empty_vehicle_range_start == vehicle - 1) {
4317 absl::StrAppendFormat(&output,
"Vehicle %d: empty",
4318 empty_vehicle_range_start);
4320 absl::StrAppendFormat(&output,
"Vehicles %d-%d: empty",
4321 empty_vehicle_range_start, vehicle - 1);
4323 output.append(
"\n");
4326 absl::StrAppendFormat(&output,
"Vehicle %d:", vehicle);
4330 absl::StrAppendFormat(&output,
"%d Vehicle(%d) ",
index,
4331 solution_assignment.
Value(vehicle_var));
4333 if (dimension_names.contains(dimension->name())) {
4335 absl::StrAppendFormat(&output,
"%s(%d..%d) ", dimension->name(),
4336 solution_assignment.
Min(
var),
4337 solution_assignment.
Max(
var));
4342 if (
IsEnd(
index)) output.append(
"Route end ");
4344 output.append(
"\n");
4347 output.append(
"Unperformed nodes: ");
4348 bool has_unperformed =
false;
4349 for (
int i = 0; i <
Size(); ++i) {
4352 absl::StrAppendFormat(&output,
"%d ", i);
4353 has_unperformed =
true;
4356 if (!has_unperformed) output.append(
"None");
4357 output.append(
"\n");
4362std::vector<std::vector<std::pair<int64_t, int64_t>>>
4365 std::vector<std::vector<std::pair<int64_t, int64_t>>> cumul_bounds(
4367 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4369 LOG(DFATAL) <<
"GetCumulBounds() called on incomplete solution:"
4370 <<
" NextVar(" << vehicle <<
") is unbound.";
4374 for (
int vehicle_id = 0; vehicle_id <
vehicles(); ++vehicle_id) {
4377 cumul_bounds[vehicle_id].emplace_back(solution_assignment.
Min(dim_var),
4378 solution_assignment.
Max(dim_var));
4382 cumul_bounds[vehicle_id].emplace_back(solution_assignment.
Min(dim_var),
4383 solution_assignment.
Max(dim_var));
4386 return cumul_bounds;
4390Assignment* RoutingModel::GetOrCreateAssignment() {
4391 if (assignment_ ==
nullptr) {
4392 assignment_ = solver_->MakeAssignment();
4393 assignment_->Add(nexts_);
4395 assignment_->Add(vehicle_vars_);
4397 assignment_->AddObjective(cost_);
4402Assignment* RoutingModel::GetOrCreateTmpAssignment() {
4403 if (tmp_assignment_ ==
nullptr) {
4404 tmp_assignment_ = solver_->MakeAssignment();
4405 tmp_assignment_->Add(nexts_);
4407 return tmp_assignment_;
4410RegularLimit* RoutingModel::GetOrCreateLimit() {
4411 if (limit_ ==
nullptr) {
4412 limit_ = solver_->MakeLimit(
4420RegularLimit* RoutingModel::GetOrCreateLocalSearchLimit() {
4421 if (ls_limit_ ==
nullptr) {
4422 ls_limit_ = solver_->MakeLimit(absl::InfiniteDuration(),
4430RegularLimit* RoutingModel::GetOrCreateLargeNeighborhoodSearchLimit() {
4431 if (lns_limit_ ==
nullptr) {
4432 lns_limit_ = solver_->MakeLimit(
4441RoutingModel::GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit() {
4442 if (first_solution_lns_limit_ ==
nullptr) {
4443 first_solution_lns_limit_ = solver_->MakeLimit(
4448 return first_solution_lns_limit_;
4451LocalSearchOperator* RoutingModel::CreateInsertionOperator() {
4452 LocalSearchOperator* insertion_operator =
4453 CreateCPOperator<MakeActiveOperator>();
4454 if (!pickup_delivery_pairs_.empty()) {
4455 insertion_operator = solver_->ConcatenateOperators(
4456 {CreatePairOperator<MakePairActiveOperator>(), insertion_operator});
4458 if (!implicit_pickup_delivery_pairs_without_alternatives_.empty()) {
4459 insertion_operator = solver_->ConcatenateOperators(
4460 {CreateOperator<MakePairActiveOperator>(
4461 implicit_pickup_delivery_pairs_without_alternatives_),
4462 insertion_operator});
4464 return insertion_operator;
4467LocalSearchOperator* RoutingModel::CreateMakeInactiveOperator() {
4468 LocalSearchOperator* make_inactive_operator =
4469 CreateCPOperator<MakeInactiveOperator>();
4470 if (!pickup_delivery_pairs_.empty()) {
4471 make_inactive_operator = solver_->ConcatenateOperators(
4472 {CreatePairOperator<MakePairInactiveOperator>(),
4473 make_inactive_operator});
4475 return make_inactive_operator;
4478void RoutingModel::CreateNeighborhoodOperators(
4480 local_search_operators_.clear();
4481 local_search_operators_.resize(LOCAL_SEARCH_OPERATOR_COUNTER,
nullptr);
4485 std::pair<RoutingLocalSearchOperator, Solver::LocalSearchOperators>>
4490 for (
const auto [type, op] : operator_by_type) {
4491 local_search_operators_[type] =
4493 ? solver_->MakeOperator(nexts_, op)
4494 : solver_->MakeOperator(nexts_, vehicle_vars_, op);
4499 const std::vector<std::pair<RoutingLocalSearchOperator,
4501 operator_by_type = {{LIN_KERNIGHAN,
Solver::LK},
4504 for (
const auto [type, op] : operator_by_type) {
4507 local_search_operators_[type] =
4509 ? solver_->MakeOperator(nexts_, std::move(arc_cost), op)
4510 : solver_->MakeOperator(nexts_, vehicle_vars_,
4511 std::move(arc_cost), op);
4516 local_search_operators_[RELOCATE] = CreateCPOperator<Relocate>();
4517 local_search_operators_[EXCHANGE] = CreateCPOperator<Exchange>();
4518 local_search_operators_[CROSS] = CreateCPOperator<Cross>();
4519 local_search_operators_[TWO_OPT] = CreateCPOperator<TwoOpt>();
4520 local_search_operators_[RELOCATE_AND_MAKE_ACTIVE] =
4521 CreateCPOperator<RelocateAndMakeActiveOperator>();
4522 local_search_operators_[MAKE_ACTIVE_AND_RELOCATE] =
4523 CreateCPOperator<MakeActiveAndRelocate>();
4524 local_search_operators_[MAKE_CHAIN_INACTIVE] =
4525 CreateCPOperator<MakeChainInactiveOperator>();
4526 local_search_operators_[SWAP_ACTIVE] = CreateCPOperator<SwapActiveOperator>();
4527 local_search_operators_[EXTENDED_SWAP_ACTIVE] =
4528 CreateCPOperator<ExtendedSwapActiveOperator>();
4531 local_search_operators_[MAKE_ACTIVE] = CreateInsertionOperator();
4532 local_search_operators_[MAKE_INACTIVE] = CreateMakeInactiveOperator();
4533 local_search_operators_[RELOCATE_PAIR] =
4534 CreatePairOperator<PairRelocateOperator>();
4535 std::vector<LocalSearchOperator*> light_relocate_pair_operators;
4536 light_relocate_pair_operators.push_back(
4537 CreatePairOperator<LightPairRelocateOperator>());
4538 local_search_operators_[LIGHT_RELOCATE_PAIR] =
4539 solver_->ConcatenateOperators(light_relocate_pair_operators);
4540 local_search_operators_[EXCHANGE_PAIR] =
4541 CreatePairOperator<PairExchangeOperator>();
4542 local_search_operators_[EXCHANGE_RELOCATE_PAIR] =
4543 CreatePairOperator<PairExchangeRelocateOperator>();
4544 local_search_operators_[RELOCATE_NEIGHBORS] =
4545 CreateOperator<MakeRelocateNeighborsOperator>(
4547 local_search_operators_[NODE_PAIR_SWAP] = solver_->ConcatenateOperators(
4548 {CreatePairOperator<IndexPairSwapActiveOperator>(),
4549 CreatePairOperator<SwapIndexPairOperator>(),
4550 CreatePairOperator<PairNodeSwapActiveOperator<true>>(),
4551 CreatePairOperator<PairNodeSwapActiveOperator<false>>()});
4552 local_search_operators_[RELOCATE_SUBTRIP] =
4553 CreatePairOperator<RelocateSubtrip>();
4554 local_search_operators_[EXCHANGE_SUBTRIP] =
4555 CreatePairOperator<ExchangeSubtrip>();
4557 const auto arc_cost_for_path_start =
4558 [
this](int64_t before_node, int64_t after_node, int64_t start_index) {
4559 const int vehicle = index_to_vehicle_[start_index];
4560 const int64_t arc_cost =
4562 return (before_node != start_index ||
IsEnd(after_node))
4566 local_search_operators_[RELOCATE_EXPENSIVE_CHAIN] =
4567 solver_->RevAlloc(
new RelocateExpensiveChain(
4571 vehicle_start_class_callback_,
4572 parameters.relocate_expensive_chain_num_arcs_to_consider(),
4573 arc_cost_for_path_start));
4576 const auto make_global_cheapest_insertion_filtered_heuristic =
4578 using Heuristic = GlobalCheapestInsertionFilteredHeuristic;
4579 Heuristic::GlobalCheapestInsertionParameters ls_gci_parameters;
4580 ls_gci_parameters.is_sequential =
false;
4581 ls_gci_parameters.farthest_seeds_ratio = 0.0;
4582 ls_gci_parameters.neighbors_ratio =
4583 parameters.cheapest_insertion_ls_operator_neighbors_ratio();
4584 ls_gci_parameters.min_neighbors =
4585 parameters.cheapest_insertion_ls_operator_min_neighbors();
4586 ls_gci_parameters.use_neighbors_ratio_for_initialization =
true;
4587 ls_gci_parameters.add_unperformed_entries =
4588 parameters.cheapest_insertion_add_unperformed_entries();
4589 return absl::make_unique<Heuristic>(
4592 GetOrCreateLocalSearchFilterManager(
4597 const auto make_local_cheapest_insertion_filtered_heuristic =
4599 return absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4602 GetOrCreateLocalSearchFilterManager(
4606 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4607 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4608 make_global_cheapest_insertion_filtered_heuristic(),
4609 parameters.heuristic_close_nodes_lns_num_nodes()));
4611 local_search_operators_[LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4612 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4613 make_local_cheapest_insertion_filtered_heuristic(),
4614 parameters.heuristic_close_nodes_lns_num_nodes()));
4616 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_PATH_LNS] =
4617 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4618 make_global_cheapest_insertion_filtered_heuristic()));
4620 local_search_operators_[LOCAL_CHEAPEST_INSERTION_PATH_LNS] =
4621 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4622 make_local_cheapest_insertion_filtered_heuristic()));
4624 local_search_operators_
4625 [RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED] =
4627 new RelocatePathAndHeuristicInsertUnperformedOperator(
4628 make_global_cheapest_insertion_filtered_heuristic()));
4630 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4631 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4632 make_global_cheapest_insertion_filtered_heuristic(),
4633 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4634 arc_cost_for_path_start));
4636 local_search_operators_[LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4637 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4638 make_local_cheapest_insertion_filtered_heuristic(),
4639 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4640 arc_cost_for_path_start));
4643#define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators) \
4644 if (search_parameters.local_search_operators().use_##operator_method() == \
4646 operators.push_back(local_search_operators_[operator_type]); \
4649LocalSearchOperator* RoutingModel::ConcatenateOperators(
4650 const RoutingSearchParameters& search_parameters,
4651 const std::vector<LocalSearchOperator*>& operators)
const {
4652 if (search_parameters.use_multi_armed_bandit_concatenate_operators()) {
4653 return solver_->MultiArmedBanditConcatenateOperators(
4656 .multi_armed_bandit_compound_operator_memory_coefficient(),
4658 .multi_armed_bandit_compound_operator_exploration_coefficient(),
4661 return solver_->ConcatenateOperators(operators);
4664LocalSearchOperator* RoutingModel::GetNeighborhoodOperators(
4665 const RoutingSearchParameters& search_parameters)
const {
4666 std::vector<LocalSearchOperator*> operator_groups;
4667 std::vector<LocalSearchOperator*> operators = extra_operators_;
4668 if (!pickup_delivery_pairs_.empty()) {
4672 if (search_parameters.local_search_operators().use_relocate_pair() ==
4682 if (vehicles_ > 1) {
4693 if (!pickup_delivery_pairs_.empty() ||
4694 search_parameters.local_search_operators().use_relocate_neighbors() ==
4696 operators.push_back(local_search_operators_[RELOCATE_NEIGHBORS]);
4699 search_parameters.local_search_metaheuristic();
4700 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4701 local_search_metaheuristic !=
4702 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4703 local_search_metaheuristic !=
4704 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4711 if (!disjunctions_.empty()) {
4728 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4737 global_cheapest_insertion_path_lns, operators);
4739 local_cheapest_insertion_path_lns, operators);
4741 RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED,
4742 relocate_path_global_cheapest_insertion_insert_unperformed, operators);
4745 global_cheapest_insertion_expensive_chain_lns,
4748 local_cheapest_insertion_expensive_chain_lns,
4751 global_cheapest_insertion_close_nodes_lns,
4754 local_cheapest_insertion_close_nodes_lns, operators);
4755 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4759 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4760 local_search_metaheuristic !=
4761 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4762 local_search_metaheuristic !=
4763 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4766 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4767 local_search_metaheuristic !=
4768 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4769 local_search_metaheuristic !=
4770 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4775 if (!disjunctions_.empty()) {
4778 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4780 return solver_->ConcatenateOperators(operator_groups);
4783#undef CP_ROUTING_PUSH_OPERATOR
4787 if (dimension->GetUnaryTransitEvaluator(0) !=
nullptr)
return true;
4794void ConvertVectorInt64ToVectorInt(
const std::vector<int64_t>&
input,
4795 std::vector<int>* output) {
4796 const int n =
input.size();
4798 int* data = output->data();
4799 for (
int i = 0; i < n; ++i) {
4800 const int element =
static_cast<int>(
input[i]);
4808std::vector<LocalSearchFilterManager::FilterEvent>
4809RoutingModel::CreateLocalSearchFilters(
4810 const RoutingSearchParameters&
parameters,
const FilterOptions& options) {
4811 const auto kAccept = LocalSearchFilterManager::FilterEventType::kAccept;
4812 const auto kRelax = LocalSearchFilterManager::FilterEventType::kRelax;
4822 std::vector<LocalSearchFilterManager::FilterEvent> filters;
4824 if (options.filter_objective && vehicle_amortized_cost_factors_set_) {
4830 if (options.filter_objective) {
4832 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4836 filters.push_back({sum, kAccept});
4838 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4839 nexts_, vehicle_vars_,
4840 [
this](int64_t i, int64_t j, int64_t k) {
4844 filters.push_back({sum, kAccept});
4848 filters.push_back({solver_->MakeVariableDomainFilter(), kAccept});
4850 if (vehicles_ > max_active_vehicles_) {
4854 if (!disjunctions_.empty()) {
4870 const PathState* path_state_reference =
nullptr;
4872 std::vector<int> path_starts;
4873 std::vector<int> path_ends;
4874 ConvertVectorInt64ToVectorInt(starts_, &path_starts);
4875 ConvertVectorInt64ToVectorInt(ends_, &path_ends);
4877 auto path_state = absl::make_unique<PathState>(
4878 Size() +
vehicles(), std::move(path_starts), std::move(path_ends));
4879 path_state_reference = path_state.get();
4890 if (!pickup_delivery_pairs_.empty()) {
4893 vehicle_pickup_delivery_policy_),
4906 if (!dimension->HasBreakConstraints())
continue;
4909 filters.insert(filters.end(), extra_filters_.begin(), extra_filters_.end());
4911 if (options.filter_with_cp_solver) {
4917LocalSearchFilterManager* RoutingModel::GetOrCreateLocalSearchFilterManager(
4918 const RoutingSearchParameters&
parameters,
const FilterOptions& options) {
4919 LocalSearchFilterManager* local_search_filter_manager =
4921 if (local_search_filter_manager ==
nullptr) {
4922 local_search_filter_manager =
4923 solver_->RevAlloc(
new LocalSearchFilterManager(
4924 CreateLocalSearchFilters(
parameters, options)));
4925 local_search_filter_managers_[options] = local_search_filter_manager;
4927 return local_search_filter_manager;
4932 for (
int vehicle = 0; vehicle < dimension.model()->
vehicles(); vehicle++) {
4933 if (!dimension.AreVehicleTransitsPositive(vehicle)) {
4941void RoutingModel::StoreDimensionCumulOptimizers(
4943 Assignment* packed_dimensions_collector_assignment =
4944 solver_->MakeAssignment();
4945 packed_dimensions_collector_assignment->AddObjective(
CostVar());
4946 const int num_dimensions = dimensions_.size();
4947 local_optimizer_index_.resize(num_dimensions, -1);
4948 global_optimizer_index_.resize(num_dimensions, -1);
4949 if (
parameters.disable_scheduling_beware_this_may_degrade_performance()) {
4955 const int num_resource_groups =
4957 bool needs_optimizer =
false;
4958 if (dimension->global_span_cost_coefficient() > 0 ||
4959 !dimension->GetNodePrecedences().empty() || num_resource_groups > 1) {
4961 needs_optimizer =
true;
4962 global_optimizer_index_[dim] = global_dimension_optimizers_.size();
4963 global_dimension_optimizers_.push_back(
4964 absl::make_unique<GlobalDimensionCumulOptimizer>(
4965 dimension,
parameters.continuous_scheduling_solver()));
4966 global_dimension_mp_optimizers_.push_back(
4967 absl::make_unique<GlobalDimensionCumulOptimizer>(
4968 dimension,
parameters.mixed_integer_scheduling_solver()));
4969 if (!AllTransitsPositive(*dimension)) {
4970 dimension->SetOffsetForGlobalOptimizer(0);
4974 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4977 std::min(offset, dimension->CumulVar(
Start(vehicle))->Min() - 1);
4979 dimension->SetOffsetForGlobalOptimizer(
std::max(
Zero(), offset));
4983 bool has_span_cost =
false;
4984 bool has_span_limit =
false;
4985 std::vector<int64_t> vehicle_offsets(
vehicles());
4986 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4987 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4988 has_span_cost =
true;
4990 if (dimension->GetSpanUpperBoundForVehicle(vehicle) <
4992 has_span_limit =
true;
4995 vehicle_offsets[vehicle] =
4996 dimension->AreVehicleTransitsPositive(vehicle)
5000 bool has_soft_lower_bound =
false;
5001 bool has_soft_upper_bound =
false;
5002 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
5003 if (dimension->HasCumulVarSoftLowerBound(i)) {
5004 has_soft_lower_bound =
true;
5006 if (dimension->HasCumulVarSoftUpperBound(i)) {
5007 has_soft_upper_bound =
true;
5010 int num_linear_constraints = 0;
5011 if (has_span_cost) ++num_linear_constraints;
5012 if (has_span_limit) ++num_linear_constraints;
5013 if (dimension->HasSoftSpanUpperBounds()) ++num_linear_constraints;
5014 if (has_soft_lower_bound) ++num_linear_constraints;
5015 if (has_soft_upper_bound) ++num_linear_constraints;
5016 if (dimension->HasBreakConstraints()) ++num_linear_constraints;
5017 if (num_resource_groups > 0 || num_linear_constraints >= 2) {
5018 needs_optimizer =
true;
5019 dimension->SetVehicleOffsetsForLocalOptimizer(std::move(vehicle_offsets));
5020 local_optimizer_index_[dim] = local_dimension_optimizers_.size();
5021 local_dimension_optimizers_.push_back(
5022 absl::make_unique<LocalDimensionCumulOptimizer>(
5023 dimension,
parameters.continuous_scheduling_solver()));
5024 local_dimension_mp_optimizers_.push_back(
5025 absl::make_unique<LocalDimensionCumulOptimizer>(
5026 dimension,
parameters.mixed_integer_scheduling_solver()));
5028 if (needs_optimizer) {
5029 packed_dimensions_collector_assignment->Add(dimension->cumuls());
5036 for (IntVar*
const extra_var : extra_vars_) {
5037 packed_dimensions_collector_assignment->Add(extra_var);
5039 for (IntervalVar*
const extra_interval : extra_intervals_) {
5040 packed_dimensions_collector_assignment->Add(extra_interval);
5043 packed_dimensions_assignment_collector_ = solver_->MakeFirstSolutionCollector(
5044 packed_dimensions_collector_assignment);
5051 bool has_soft_or_span_cost =
false;
5052 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
5053 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
5054 has_soft_or_span_cost =
true;
5058 if (!has_soft_or_span_cost) {
5059 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
5060 if (dimension->HasCumulVarSoftUpperBound(i) ||
5061 dimension->HasCumulVarSoftLowerBound(i)) {
5062 has_soft_or_span_cost =
true;
5067 if (has_soft_or_span_cost)
dimensions.push_back(dimension);
5073RoutingModel::CreateFinalizerForMinimizedAndMaximizedVariables() {
5074 std::stable_sort(weighted_finalizer_variable_targets_.begin(),
5075 weighted_finalizer_variable_targets_.end(),
5076 [](
const std::pair<VarTarget, int64_t>& var_cost1,
5077 const std::pair<VarTarget, int64_t>& var_cost2) {
5078 return var_cost1.second > var_cost2.second;
5080 const int num_variables = weighted_finalizer_variable_targets_.size() +
5081 finalizer_variable_targets_.size();
5082 std::vector<IntVar*> variables;
5083 std::vector<int64_t> targets;
5084 variables.reserve(num_variables);
5085 targets.reserve(num_variables);
5086 for (
const auto& [var_target,
cost] : weighted_finalizer_variable_targets_) {
5087 variables.push_back(var_target.var);
5088 targets.push_back(var_target.target);
5090 for (
const auto& [
var, target] : finalizer_variable_targets_) {
5091 variables.push_back(
var);
5092 targets.push_back(target);
5095 std::move(targets));
5098DecisionBuilder* RoutingModel::CreateSolutionFinalizer(
5099 const RoutingSearchParameters&
parameters, 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 const bool can_use_dimension_cumul_optimizers =
5120 !
parameters.disable_scheduling_beware_this_may_degrade_performance();
5121 if (!local_dimension_optimizers_.empty()) {
5122 DCHECK(can_use_dimension_cumul_optimizers);
5123 decision_builders.push_back(
5124 solver_->RevAlloc(
new SetCumulsFromLocalDimensionCosts(
5125 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
5136 if (can_use_dimension_cumul_optimizers) {
5142 LocalDimensionCumulOptimizer*
const optimizer =
5145 LocalDimensionCumulOptimizer*
const mp_optimizer =
5148 decision_builders.push_back(
5149 solver_->RevAlloc(
new SetCumulsFromResourceAssignmentCosts(
5150 optimizer, mp_optimizer, lns_limit)));
5154 if (!global_dimension_optimizers_.empty()) {
5155 DCHECK(can_use_dimension_cumul_optimizers);
5156 decision_builders.push_back(
5157 solver_->RevAlloc(
new SetCumulsFromGlobalDimensionCosts(
5158 &global_dimension_optimizers_, &global_dimension_mp_optimizers_,
5161 decision_builders.push_back(
5162 CreateFinalizerForMinimizedAndMaximizedVariables());
5164 return solver_->Compose(decision_builders);
5167void RoutingModel::CreateFirstSolutionDecisionBuilders(
5168 const RoutingSearchParameters& search_parameters) {
5169 first_solution_decision_builders_.resize(
5170 FirstSolutionStrategy_Value_Value_ARRAYSIZE,
nullptr);
5171 first_solution_filtered_decision_builders_.resize(
5172 FirstSolutionStrategy_Value_Value_ARRAYSIZE,
nullptr);
5173 DecisionBuilder*
const finalize_solution = CreateSolutionFinalizer(
5174 search_parameters, GetOrCreateLargeNeighborhoodSearchLimit());
5176 first_solution_decision_builders_
5177 [FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE] = finalize_solution;
5179 first_solution_decision_builders_
5180 [FirstSolutionStrategy::GLOBAL_CHEAPEST_ARC] = solver_->MakePhase(
5182 [
this](int64_t i, int64_t j) {
5190 first_solution_decision_builders_[FirstSolutionStrategy::LOCAL_CHEAPEST_ARC] =
5193 first_solution_decision_builders_[FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5195 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5196 first_solution_filtered_decision_builders_
5197 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5198 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5199 absl::make_unique<EvaluatorCheapestAdditionFilteredHeuristic>(
5201 [
this](int64_t i, int64_t j) {
5204 GetOrCreateLocalSearchFilterManager(
5208 first_solution_decision_builders_
5209 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5210 solver_->Try(first_solution_filtered_decision_builders_
5211 [FirstSolutionStrategy::PATH_CHEAPEST_ARC],
5212 first_solution_decision_builders_
5213 [FirstSolutionStrategy::PATH_CHEAPEST_ARC]);
5221 first_solution_decision_builders_
5222 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5224 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5225 first_solution_filtered_decision_builders_
5226 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5227 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5228 absl::make_unique<ComparatorCheapestAdditionFilteredHeuristic>(
5230 GetOrCreateLocalSearchFilterManager(
5234 first_solution_decision_builders_
5235 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->Try(
5236 first_solution_filtered_decision_builders_
5237 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC],
5238 first_solution_decision_builders_
5239 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC]);
5242 if (first_solution_evaluator_ !=
nullptr) {
5243 first_solution_decision_builders_
5244 [FirstSolutionStrategy::EVALUATOR_STRATEGY] = solver_->MakePhase(
5247 first_solution_decision_builders_
5248 [FirstSolutionStrategy::EVALUATOR_STRATEGY] =
nullptr;
5251 first_solution_decision_builders_[FirstSolutionStrategy::ALL_UNPERFORMED] =
5254 RegularLimit*
const ls_limit = solver_->MakeLimit(
5258 DecisionBuilder*
const finalize = solver_->MakeSolveOnce(
5259 finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit());
5260 LocalSearchPhaseParameters*
const insertion_parameters =
5261 solver_->MakeLocalSearchPhaseParameters(
5262 nullptr, CreateInsertionOperator(), finalize, ls_limit,
5263 GetOrCreateLocalSearchFilterManager(
5266 std::vector<IntVar*> decision_vars = nexts_;
5268 decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(),
5269 vehicle_vars_.end());
5271 const int64_t optimization_step =
std::max(
5273 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5274 solver_->MakeNestedOptimize(
5276 insertion_parameters),
5277 GetOrCreateAssignment(),
false, optimization_step);
5278 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5279 solver_->Compose(first_solution_decision_builders_
5280 [FirstSolutionStrategy::BEST_INSERTION],
5284 GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
5286 gci_parameters.is_sequential =
false;
5287 gci_parameters.farthest_seeds_ratio =
5288 search_parameters.cheapest_insertion_farthest_seeds_ratio();
5289 gci_parameters.neighbors_ratio =
5290 search_parameters.cheapest_insertion_first_solution_neighbors_ratio();
5291 gci_parameters.min_neighbors =
5292 search_parameters.cheapest_insertion_first_solution_min_neighbors();
5293 gci_parameters.use_neighbors_ratio_for_initialization =
5295 .cheapest_insertion_first_solution_use_neighbors_ratio_for_initialization();
5296 gci_parameters.add_unperformed_entries =
5297 search_parameters.cheapest_insertion_add_unperformed_entries();
5298 for (
bool is_sequential : {
false,
true}) {
5300 is_sequential ? FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION
5301 : FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5302 gci_parameters.is_sequential = is_sequential;
5304 first_solution_filtered_decision_builders_[first_solution_strategy] =
5305 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5306 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5308 [
this](int64_t i, int64_t j, int64_t vehicle) {
5312 GetOrCreateLocalSearchFilterManager(
5313 search_parameters, {
false,
5316 IntVarFilteredDecisionBuilder*
const strong_gci =
5317 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5318 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
5320 [
this](int64_t i, int64_t j, int64_t vehicle) {
5324 GetOrCreateLocalSearchFilterManager(
5325 search_parameters, {
false,
5328 first_solution_decision_builders_[first_solution_strategy] = solver_->Try(
5329 first_solution_filtered_decision_builders_[first_solution_strategy],
5330 solver_->Try(strong_gci, first_solution_decision_builders_
5331 [FirstSolutionStrategy::BEST_INSERTION]));
5335 const bool evaluate_pickup_delivery_costs_independently =
5337 .local_cheapest_insertion_evaluate_pickup_delivery_costs_independently();
5338 first_solution_filtered_decision_builders_
5339 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] =
5340 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5341 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5343 [
this](int64_t i, int64_t j, int64_t vehicle) {
5346 evaluate_pickup_delivery_costs_independently,
5347 GetOrCreateLocalSearchFilterManager(
5348 search_parameters, {
false,
5350 IntVarFilteredDecisionBuilder*
const strong_lci =
5351 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5352 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5354 [
this](int64_t i, int64_t j, int64_t vehicle) {
5357 evaluate_pickup_delivery_costs_independently,
5358 GetOrCreateLocalSearchFilterManager(
5359 search_parameters, {
false,
5361 first_solution_decision_builders_
5362 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = solver_->Try(
5363 first_solution_filtered_decision_builders_
5364 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION],
5365 solver_->Try(strong_lci,
5366 first_solution_decision_builders_
5367 [FirstSolutionStrategy::BEST_INSERTION]));
5370 first_solution_filtered_decision_builders_
5371 [FirstSolutionStrategy::LOCAL_CHEAPEST_COST_INSERTION] =
5372 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5373 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5376 GetOrCreateLocalSearchFilterManager(
5377 search_parameters, {
true,
5379 IntVarFilteredDecisionBuilder*
const strong_lcci =
5380 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5381 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
5384 GetOrCreateLocalSearchFilterManager(
5385 search_parameters, {
true,
5387 first_solution_decision_builders_
5388 [FirstSolutionStrategy::LOCAL_CHEAPEST_COST_INSERTION] = solver_->Try(
5389 first_solution_filtered_decision_builders_
5390 [FirstSolutionStrategy::LOCAL_CHEAPEST_COST_INSERTION],
5391 solver_->Try(strong_lcci,
5392 first_solution_decision_builders_
5393 [FirstSolutionStrategy::BEST_INSERTION]));
5396 SavingsFilteredHeuristic::SavingsParameters savings_parameters;
5397 savings_parameters.neighbors_ratio =
5398 search_parameters.savings_neighbors_ratio();
5399 savings_parameters.max_memory_usage_bytes =
5400 search_parameters.savings_max_memory_usage_bytes();
5401 savings_parameters.add_reverse_arcs =
5402 search_parameters.savings_add_reverse_arcs();
5403 savings_parameters.arc_coefficient =
5404 search_parameters.savings_arc_coefficient();
5405 LocalSearchFilterManager* filter_manager =
nullptr;
5406 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5407 filter_manager = GetOrCreateLocalSearchFilterManager(
5412 if (search_parameters.savings_parallel_routes()) {
5413 IntVarFilteredDecisionBuilder* savings_db =
5414 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5415 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5416 this, savings_parameters, filter_manager)));
5417 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5418 first_solution_filtered_decision_builders_
5419 [FirstSolutionStrategy::SAVINGS] = savings_db;
5422 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5423 solver_->Try(savings_db,
5424 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5425 absl::make_unique<ParallelSavingsFilteredHeuristic>(
5426 this, savings_parameters,
5427 GetOrCreateLocalSearchFilterManager(
5432 IntVarFilteredDecisionBuilder* savings_db =
5433 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5434 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5435 this, savings_parameters, filter_manager)));
5436 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5437 first_solution_filtered_decision_builders_
5438 [FirstSolutionStrategy::SAVINGS] = savings_db;
5441 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5442 solver_->Try(savings_db,
5443 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5444 absl::make_unique<SequentialSavingsFilteredHeuristic>(
5445 this, savings_parameters,
5446 GetOrCreateLocalSearchFilterManager(
5452 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5455 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5458 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP]);
5460 first_solution_decision_builders_[FirstSolutionStrategy::CHRISTOFIDES] =
5461 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
5462 absl::make_unique<ChristofidesFilteredHeuristic>(
5464 GetOrCreateLocalSearchFilterManager(
5465 search_parameters, {
false,
5467 search_parameters.christofides_use_minimum_matching())));
5469 const bool has_precedences = std::any_of(
5470 dimensions_.begin(), dimensions_.end(),
5472 bool has_single_vehicle_node =
false;
5473 for (
int node = 0; node <
Size(); node++) {
5474 if (!
IsStart(node) && !
IsEnd(node) && allowed_vehicles_[node].size() == 1) {
5475 has_single_vehicle_node =
true;
5479 automatic_first_solution_strategy_ =
5481 has_precedences, has_single_vehicle_node);
5482 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC] =
5483 first_solution_decision_builders_[automatic_first_solution_strategy_];
5484 first_solution_decision_builders_[FirstSolutionStrategy::UNSET] =
5485 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC];
5488 for (FirstSolutionStrategy_Value strategy =
5489 FirstSolutionStrategy_Value_Value_MIN;
5490 strategy <= FirstSolutionStrategy_Value_Value_MAX;
5491 strategy = FirstSolutionStrategy_Value(strategy + 1)) {
5492 if (first_solution_decision_builders_[strategy] ==
nullptr ||
5493 strategy == FirstSolutionStrategy::AUTOMATIC) {
5496 const std::string strategy_name =
5497 FirstSolutionStrategy_Value_Name(strategy);
5498 const std::string& log_tag = search_parameters.log_tag();
5499 if (!log_tag.empty() && log_tag != strategy_name) {
5500 first_solution_decision_builders_[strategy]->set_name(absl::StrFormat(
5501 "%s / %s", strategy_name, search_parameters.log_tag()));
5503 first_solution_decision_builders_[strategy]->set_name(strategy_name);
5508DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder(
5509 const RoutingSearchParameters& search_parameters)
const {
5511 search_parameters.first_solution_strategy();
5512 if (first_solution_strategy < first_solution_decision_builders_.size()) {
5513 return first_solution_decision_builders_[first_solution_strategy];
5519IntVarFilteredDecisionBuilder*
5520RoutingModel::GetFilteredFirstSolutionDecisionBuilderOrNull(
5521 const RoutingSearchParameters& search_parameters)
const {
5523 search_parameters.first_solution_strategy();
5524 return first_solution_filtered_decision_builders_[first_solution_strategy];
5527LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters(
5528 const RoutingSearchParameters& search_parameters) {
5529 SearchLimit* lns_limit = GetOrCreateLargeNeighborhoodSearchLimit();
5530 return solver_->MakeLocalSearchPhaseParameters(
5531 CostVar(), GetNeighborhoodOperators(search_parameters),
5532 solver_->MakeSolveOnce(
5533 CreateSolutionFinalizer(search_parameters, lns_limit), lns_limit),
5534 GetOrCreateLocalSearchLimit(),
5535 GetOrCreateLocalSearchFilterManager(
5540DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder(
5541 const RoutingSearchParameters& search_parameters) {
5542 const int size =
Size();
5543 DecisionBuilder* first_solution =
5544 GetFirstSolutionDecisionBuilder(search_parameters);
5545 LocalSearchPhaseParameters*
const parameters =
5546 CreateLocalSearchParameters(search_parameters);
5547 SearchLimit* first_solution_lns_limit =
5548 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5549 DecisionBuilder*
const first_solution_sub_decision_builder =
5550 solver_->MakeSolveOnce(
5551 CreateSolutionFinalizer(search_parameters, first_solution_lns_limit),
5552 first_solution_lns_limit);
5554 return solver_->MakeLocalSearchPhase(nexts_, first_solution,
5555 first_solution_sub_decision_builder,
5558 const int all_size = size + size + vehicles_;
5559 std::vector<IntVar*> all_vars(all_size);
5560 for (
int i = 0; i < size; ++i) {
5561 all_vars[i] = nexts_[i];
5563 for (
int i = size; i < all_size; ++i) {
5564 all_vars[i] = vehicle_vars_[i - size];
5566 return solver_->MakeLocalSearchPhase(all_vars, first_solution,
5567 first_solution_sub_decision_builder,
5572void RoutingModel::SetupDecisionBuilders(
5573 const RoutingSearchParameters& search_parameters) {
5574 if (search_parameters.use_depth_first_search()) {
5575 SearchLimit* first_lns_limit =
5576 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5577 solve_db_ = solver_->Compose(
5578 GetFirstSolutionDecisionBuilder(search_parameters),
5579 solver_->MakeSolveOnce(
5580 CreateSolutionFinalizer(search_parameters, first_lns_limit),
5583 solve_db_ = CreateLocalSearchDecisionBuilder(search_parameters);
5585 CHECK(preassignment_ !=
nullptr);
5586 DecisionBuilder* restore_preassignment =
5587 solver_->MakeRestoreAssignment(preassignment_);
5588 solve_db_ = solver_->Compose(restore_preassignment, solve_db_);
5590 solver_->Compose(restore_preassignment,
5591 solver_->MakeLocalSearchPhase(
5592 GetOrCreateAssignment(),
5593 CreateLocalSearchParameters(search_parameters)));
5594 restore_assignment_ = solver_->Compose(
5595 solver_->MakeRestoreAssignment(GetOrCreateAssignment()),
5596 CreateSolutionFinalizer(search_parameters,
5597 GetOrCreateLargeNeighborhoodSearchLimit()));
5598 restore_tmp_assignment_ = solver_->Compose(
5599 restore_preassignment,
5600 solver_->MakeRestoreAssignment(GetOrCreateTmpAssignment()),
5601 CreateSolutionFinalizer(search_parameters,
5602 GetOrCreateLargeNeighborhoodSearchLimit()));
5605void RoutingModel::SetupMetaheuristics(
5606 const RoutingSearchParameters& search_parameters) {
5607 SearchMonitor* optimize;
5609 search_parameters.local_search_metaheuristic();
5612 bool limit_too_long =
5613 !search_parameters.has_time_limit() &&
5615 const int64_t optimization_step =
std::max(
5617 switch (metaheuristic) {
5618 case LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH:
5620 optimize = solver_->MakeGuidedLocalSearch(
5623 optimization_step, nexts_,
5624 search_parameters.guided_local_search_lambda_coefficient());
5626 optimize = solver_->MakeGuidedLocalSearch(
5628 [
this](int64_t i, int64_t j, int64_t k) {
5631 optimization_step, nexts_, vehicle_vars_,
5632 search_parameters.guided_local_search_lambda_coefficient());
5635 case LocalSearchMetaheuristic::SIMULATED_ANNEALING:
5637 solver_->MakeSimulatedAnnealing(
false, cost_, optimization_step, 100);
5639 case LocalSearchMetaheuristic::TABU_SEARCH:
5640 optimize = solver_->MakeTabuSearch(
false, cost_, optimization_step,
5641 nexts_, 10, 10, .8);
5643 case LocalSearchMetaheuristic::GENERIC_TABU_SEARCH: {
5644 std::vector<operations_research::IntVar*> tabu_vars;
5645 if (tabu_var_callback_) {
5646 tabu_vars = tabu_var_callback_(
this);
5648 tabu_vars.push_back(cost_);
5650 optimize = solver_->MakeGenericTabuSearch(
false, cost_, optimization_step,
5655 limit_too_long =
false;
5656 optimize = solver_->MakeMinimize(cost_, optimization_step);
5658 if (limit_too_long) {
5659 LOG(
WARNING) << LocalSearchMetaheuristic::Value_Name(metaheuristic)
5660 <<
" specified without sane timeout: solve may run forever.";
5662 monitors_.push_back(optimize);
5666 tabu_var_callback_ = std::move(tabu_var_callback);
5669void RoutingModel::SetupAssignmentCollector(
5670 const RoutingSearchParameters& search_parameters) {
5671 Assignment* full_assignment = solver_->MakeAssignment();
5673 full_assignment->
Add(dimension->cumuls());
5675 for (IntVar*
const extra_var : extra_vars_) {
5676 full_assignment->
Add(extra_var);
5678 for (IntervalVar*
const extra_interval : extra_intervals_) {
5679 full_assignment->
Add(extra_interval);
5681 full_assignment->
Add(nexts_);
5682 full_assignment->
Add(active_);
5683 full_assignment->
Add(vehicle_vars_);
5686 collect_assignments_ = solver_->MakeNBestValueSolutionCollector(
5687 full_assignment, search_parameters.number_of_solutions_to_collect(),
5689 collect_one_assignment_ =
5690 solver_->MakeFirstSolutionCollector(full_assignment);
5691 monitors_.push_back(collect_assignments_);
5694void RoutingModel::SetupTrace(
5695 const RoutingSearchParameters& search_parameters) {
5696 if (search_parameters.log_search()) {
5697 Solver::SearchLogParameters search_log_parameters;
5698 search_log_parameters.branch_period = 10000;
5699 search_log_parameters.objective =
nullptr;
5700 search_log_parameters.variable = cost_;
5701 search_log_parameters.scaling_factor =
5702 search_parameters.log_cost_scaling_factor();
5703 search_log_parameters.offset = search_parameters.log_cost_offset();
5704 if (!search_parameters.log_tag().empty()) {
5705 const std::string& tag = search_parameters.log_tag();
5706 search_log_parameters.display_callback = [tag]() {
return tag; };
5708 search_log_parameters.display_callback =
nullptr;
5710 search_log_parameters.display_on_new_solutions_only =
false;
5711 monitors_.push_back(solver_->MakeSearchLog(search_log_parameters));
5715void RoutingModel::SetupImprovementLimit(
5716 const RoutingSearchParameters& search_parameters) {
5717 if (search_parameters.has_improvement_limit_parameters()) {
5718 monitors_.push_back(solver_->MakeImprovementLimit(
5719 cost_,
false, search_parameters.log_cost_scaling_factor(),
5720 search_parameters.log_cost_offset(),
5721 search_parameters.improvement_limit_parameters()
5722 .improvement_rate_coefficient(),
5723 search_parameters.improvement_limit_parameters()
5724 .improvement_rate_solutions_distance()));
5728void RoutingModel::SetupSearchMonitors(
5729 const RoutingSearchParameters& search_parameters) {
5730 monitors_.push_back(GetOrCreateLimit());
5731 SetupImprovementLimit(search_parameters);
5732 SetupMetaheuristics(search_parameters);
5733 SetupAssignmentCollector(search_parameters);
5734 SetupTrace(search_parameters);
5737bool RoutingModel::UsesLightPropagation(
5738 const RoutingSearchParameters& search_parameters)
const {
5739 return !search_parameters.use_full_propagation() &&
5740 !search_parameters.use_depth_first_search() &&
5741 search_parameters.first_solution_strategy() !=
5742 FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE;
5751 weighted_finalizer_variable_targets_.size());
5752 if (
index < weighted_finalizer_variable_targets_.size()) {
5753 auto& [var_target, total_cost] =
5754 weighted_finalizer_variable_targets_[
index];
5760 weighted_finalizer_variable_targets_.emplace_back(VarTarget(
var, target),
5779 if (finalizer_variable_target_set_.contains(
var))
return;
5780 finalizer_variable_target_set_.insert(
var);
5781 finalizer_variable_targets_.emplace_back(
var, target);
5792void RoutingModel::SetupSearch(
5793 const RoutingSearchParameters& search_parameters) {
5794 SetupDecisionBuilders(search_parameters);
5795 SetupSearchMonitors(search_parameters);
5799 extra_vars_.push_back(
var);
5803 extra_intervals_.push_back(
interval);
5808class PathSpansAndTotalSlacks :
public Constraint {
5812 std::vector<IntVar*> spans,
5813 std::vector<IntVar*> total_slacks)
5816 dimension_(dimension),
5817 spans_(
std::move(spans)),
5818 total_slacks_(
std::move(total_slacks)) {
5819 CHECK_EQ(spans_.size(), model_->vehicles());
5820 CHECK_EQ(total_slacks_.size(), model_->vehicles());
5821 vehicle_demons_.resize(model_->vehicles());
5824 std::string DebugString()
const override {
return "PathSpansAndTotalSlacks"; }
5826 void Post()
override {
5827 const int num_nodes = model_->VehicleVars().size();
5828 const int num_transits = model_->Nexts().size();
5829 for (
int node = 0; node < num_nodes; ++node) {
5831 model_->solver(),
this, &PathSpansAndTotalSlacks::PropagateNode,
5832 "PathSpansAndTotalSlacks::PropagateNode", node);
5833 dimension_->CumulVar(node)->WhenRange(demon);
5834 model_->VehicleVar(node)->WhenBound(demon);
5835 if (node < num_transits) {
5836 dimension_->TransitVar(node)->WhenRange(demon);
5837 dimension_->FixedTransitVar(node)->WhenBound(demon);
5838 model_->NextVar(node)->WhenBound(demon);
5841 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5842 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5844 solver(),
this, &PathSpansAndTotalSlacks::PropagateVehicle,
5845 "PathSpansAndTotalSlacks::PropagateVehicle", vehicle);
5846 vehicle_demons_[vehicle] = demon;
5847 if (spans_[vehicle]) spans_[vehicle]->WhenRange(demon);
5848 if (total_slacks_[vehicle]) total_slacks_[vehicle]->WhenRange(demon);
5849 if (dimension_->HasBreakConstraints()) {
5850 for (IntervalVar*
b : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5851 b->WhenAnything(demon);
5858 void InitialPropagate()
override {
5859 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5860 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5861 PropagateVehicle(vehicle);
5869 void PropagateNode(
int node) {
5870 if (!model_->VehicleVar(node)->Bound())
return;
5871 const int vehicle = model_->VehicleVar(node)->Min();
5872 if (vehicle < 0 || vehicle_demons_[vehicle] ==
nullptr)
return;
5873 EnqueueDelayedDemon(vehicle_demons_[vehicle]);
5881 int64_t SpanMin(
int vehicle, int64_t sum_fixed_transits) {
5883 const int64_t span_min = spans_[vehicle]
5884 ? spans_[vehicle]->Min()
5886 const int64_t total_slack_min = total_slacks_[vehicle]
5887 ? total_slacks_[vehicle]->Min()
5889 return std::min(span_min,
CapAdd(total_slack_min, sum_fixed_transits));
5891 int64_t SpanMax(
int vehicle, int64_t sum_fixed_transits) {
5893 const int64_t span_max = spans_[vehicle]
5894 ? spans_[vehicle]->Max()
5896 const int64_t total_slack_max = total_slacks_[vehicle]
5897 ? total_slacks_[vehicle]->Max()
5899 return std::max(span_max,
CapAdd(total_slack_max, sum_fixed_transits));
5901 void SetSpanMin(
int vehicle, int64_t
min, int64_t sum_fixed_transits) {
5903 if (spans_[vehicle]) {
5904 spans_[vehicle]->SetMin(
min);
5906 if (total_slacks_[vehicle]) {
5907 total_slacks_[vehicle]->SetMin(
CapSub(
min, sum_fixed_transits));
5910 void SetSpanMax(
int vehicle, int64_t
max, int64_t sum_fixed_transits) {
5912 if (spans_[vehicle]) {
5913 spans_[vehicle]->SetMax(
max);
5915 if (total_slacks_[vehicle]) {
5916 total_slacks_[vehicle]->SetMax(
CapSub(
max, sum_fixed_transits));
5921 void SynchronizeSpanAndTotalSlack(
int vehicle, int64_t sum_fixed_transits) {
5923 IntVar* span = spans_[vehicle];
5924 IntVar* total_slack = total_slacks_[vehicle];
5925 if (!span || !total_slack)
return;
5926 span->SetMin(
CapAdd(total_slack->Min(), sum_fixed_transits));
5927 span->SetMax(
CapAdd(total_slack->Max(), sum_fixed_transits));
5928 total_slack->SetMin(
CapSub(span->Min(), sum_fixed_transits));
5929 total_slack->SetMax(
CapSub(span->Max(), sum_fixed_transits));
5932 void PropagateVehicle(
int vehicle) {
5933 DCHECK(spans_[vehicle] || total_slacks_[vehicle]);
5934 const int start = model_->Start(vehicle);
5935 const int end = model_->End(vehicle);
5938 if (spans_[vehicle] !=
nullptr &&
5939 dimension_->AreVehicleTransitsPositive(vehicle)) {
5940 spans_[vehicle]->SetRange(
CapSub(dimension_->CumulVar(
end)->Min(),
5941 dimension_->CumulVar(
start)->Max()),
5942 CapSub(dimension_->CumulVar(
end)->Max(),
5943 dimension_->CumulVar(
start)->Min()));
5950 int curr_node =
start;
5951 while (!model_->IsEnd(curr_node)) {
5952 const IntVar* next_var = model_->NextVar(curr_node);
5953 if (!next_var->Bound())
return;
5954 path_.push_back(curr_node);
5955 curr_node = next_var->Value();
5960 int64_t sum_fixed_transits = 0;
5961 for (
const int node : path_) {
5962 const IntVar* fixed_transit_var = dimension_->FixedTransitVar(node);
5963 if (!fixed_transit_var->Bound())
return;
5964 sum_fixed_transits =
5965 CapAdd(sum_fixed_transits, fixed_transit_var->Value());
5968 SynchronizeSpanAndTotalSlack(vehicle, sum_fixed_transits);
5975 if (dimension_->HasBreakConstraints() &&
5976 !dimension_->GetBreakIntervalsOfVehicle(vehicle).empty()) {
5977 const int64_t vehicle_start_max = dimension_->CumulVar(
start)->Max();
5978 const int64_t vehicle_end_min = dimension_->CumulVar(
end)->Min();
5980 int64_t min_break_duration = 0;
5981 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5982 if (!br->MustBePerformed())
continue;
5983 if (vehicle_start_max < br->EndMin() &&
5984 br->StartMax() < vehicle_end_min) {
5985 min_break_duration =
CapAdd(min_break_duration, br->DurationMin());
5988 SetSpanMin(vehicle,
CapAdd(min_break_duration, sum_fixed_transits),
5989 sum_fixed_transits);
5995 const int64_t slack_max =
5996 CapSub(SpanMax(vehicle, sum_fixed_transits), sum_fixed_transits);
5997 const int64_t max_additional_slack =
5998 CapSub(slack_max, min_break_duration);
5999 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
6000 if (!br->MustBePerformed())
continue;
6002 if (vehicle_start_max >= br->EndMin() &&
6003 br->StartMax() < vehicle_end_min) {
6004 if (br->DurationMin() > max_additional_slack) {
6007 br->SetEndMax(vehicle_start_max);
6008 dimension_->CumulVar(
start)->SetMin(br->EndMin());
6013 if (vehicle_start_max < br->EndMin() &&
6014 br->StartMax() >= vehicle_end_min) {
6015 if (br->DurationMin() > max_additional_slack) {
6016 br->SetStartMin(vehicle_end_min);
6017 dimension_->CumulVar(
end)->SetMax(br->StartMax());
6025 IntVar* start_cumul = dimension_->CumulVar(
start);
6026 IntVar* end_cumul = dimension_->CumulVar(
end);
6027 const int64_t
start_min = start_cumul->Min();
6028 const int64_t
start_max = start_cumul->Max();
6029 const int64_t
end_min = end_cumul->Min();
6030 const int64_t
end_max = end_cumul->Max();
6033 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
6035 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
6037 const int64_t span_min = SpanMin(vehicle, sum_fixed_transits);
6038 const int64_t span_max = SpanMax(vehicle, sum_fixed_transits);
6039 const int64_t slack_from_lb =
CapSub(span_max, span_lb);
6040 const int64_t slack_from_ub =
CapSub(span_ub, span_min);
6054 int64_t span_lb = 0;
6055 int64_t span_ub = 0;
6056 for (
const int node : path_) {
6057 span_lb =
CapAdd(span_lb, dimension_->TransitVar(node)->Min());
6058 span_ub =
CapAdd(span_ub, dimension_->TransitVar(node)->Max());
6060 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
6061 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
6065 const int64_t span_min = SpanMin(vehicle, sum_fixed_transits);
6066 const int64_t span_max = SpanMax(vehicle, sum_fixed_transits);
6067 const int64_t slack_from_lb =
CapSub(span_max, span_lb);
6068 const int64_t slack_from_ub =
6070 ?
CapSub(span_ub, span_min)
6071 :
std::numeric_limits<int64_t>::
max();
6072 for (
const int node : path_) {
6073 IntVar* transit_var = dimension_->TransitVar(node);
6074 const int64_t transit_i_min = transit_var->Min();
6075 const int64_t transit_i_max = transit_var->Max();
6079 transit_var->SetMax(
CapAdd(transit_i_min, slack_from_lb));
6080 transit_var->SetMin(
CapSub(transit_i_max, slack_from_ub));
6085 path_.push_back(
end);
6096 int64_t arrival_time = dimension_->CumulVar(
start)->Min();
6097 for (
int i = 1; i < path_.size(); ++i) {
6100 dimension_->FixedTransitVar(path_[i - 1])->Min()),
6101 dimension_->CumulVar(path_[i])->Min());
6103 int64_t departure_time = arrival_time;
6104 for (
int i = path_.size() - 2; i >= 0; --i) {
6107 dimension_->FixedTransitVar(path_[i])->Min()),
6108 dimension_->CumulVar(path_[i])->Max());
6110 const int64_t span_lb =
CapSub(arrival_time, departure_time);
6111 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
6112 const int64_t maximum_deviation =
6113 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
6114 const int64_t start_lb =
CapSub(departure_time, maximum_deviation);
6115 dimension_->CumulVar(
start)->SetMin(start_lb);
6119 int64_t departure_time = dimension_->CumulVar(
end)->Max();
6120 for (
int i = path_.size() - 2; i >= 0; --i) {
6121 const int curr_node = path_[i];
6124 dimension_->FixedTransitVar(curr_node)->Min()),
6125 dimension_->CumulVar(curr_node)->Max());
6127 int arrival_time = departure_time;
6128 for (
int i = 1; i < path_.size(); ++i) {
6131 dimension_->FixedTransitVar(path_[i - 1])->Min()),
6132 dimension_->CumulVar(path_[i])->Min());
6134 const int64_t span_lb =
CapSub(arrival_time, departure_time);
6135 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
6136 const int64_t maximum_deviation =
6137 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
6138 dimension_->CumulVar(
end)->SetMax(
6139 CapAdd(arrival_time, maximum_deviation));
6145 std::vector<IntVar*> spans_;
6146 std::vector<IntVar*> total_slacks_;
6147 std::vector<int> path_;
6148 std::vector<Demon*> vehicle_demons_;
6155 std::vector<IntVar*> total_slacks) {
6157 CHECK_EQ(vehicles_, total_slacks.size());
6159 new PathSpansAndTotalSlacks(
this, dimension, spans, total_slacks));
6167 std::vector<int64_t> vehicle_capacities,
6168 const std::string&
name,
6170 : vehicle_capacities_(
std::move(vehicle_capacities)),
6171 base_dimension_(base_dimension),
6172 global_span_cost_coefficient_(0),
6175 global_optimizer_offset_(0) {
6177 vehicle_span_upper_bounds_.assign(
model->vehicles(),
6179 vehicle_span_cost_coefficients_.assign(
model->vehicles(), 0);
6182RoutingDimension::RoutingDimension(RoutingModel*
model,
6183 std::vector<int64_t> vehicle_capacities,
6184 const std::string&
name, SelfBased)
6185 : RoutingDimension(
model,
std::move(vehicle_capacities),
name, this) {}
6188 cumul_var_piecewise_linear_cost_.clear();
6191void RoutingDimension::Initialize(
6192 const std::vector<int>& transit_evaluators,
6193 const std::vector<int>& state_dependent_transit_evaluators,
6194 int64_t slack_max) {
6196 InitializeTransits(transit_evaluators, state_dependent_transit_evaluators,
6210class LightRangeLessOrEqual :
public Constraint {
6212 LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l, IntExpr*
const r);
6213 ~LightRangeLessOrEqual()
override {}
6214 void Post()
override;
6215 void InitialPropagate()
override;
6216 std::string DebugString()
const override;
6217 IntVar* Var()
override {
6218 return solver()->MakeIsLessOrEqualVar(left_, right_);
6221 void Accept(ModelVisitor*
const visitor)
const override {
6232 IntExpr*
const left_;
6233 IntExpr*
const right_;
6237LightRangeLessOrEqual::LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l,
6239 : Constraint(s), left_(l), right_(r), demon_(nullptr) {}
6241void LightRangeLessOrEqual::Post() {
6243 solver(),
this, &LightRangeLessOrEqual::CheckRange,
"CheckRange");
6244 left_->WhenRange(demon_);
6245 right_->WhenRange(demon_);
6248void LightRangeLessOrEqual::InitialPropagate() {
6249 left_->SetMax(right_->Max());
6250 right_->SetMin(left_->Min());
6251 if (left_->Max() <= right_->Min()) {
6256void LightRangeLessOrEqual::CheckRange() {
6257 if (left_->Min() > right_->Max()) {
6260 if (left_->Max() <= right_->Min()) {
6265std::string LightRangeLessOrEqual::DebugString()
const {
6266 return left_->DebugString() +
" < " + right_->DebugString();
6271void RoutingDimension::InitializeCumuls() {
6272 Solver*
const solver = model_->
solver();
6274 const auto capacity_range = std::minmax_element(vehicle_capacities_.begin(),
6275 vehicle_capacities_.end());
6276 const int64_t min_capacity = *capacity_range.first;
6278 const int64_t max_capacity = *capacity_range.second;
6279 solver->MakeIntVarArray(size, 0, max_capacity, name_, &cumuls_);
6281 for (
int v = 0; v < model_->
vehicles(); v++) {
6282 const int64_t vehicle_capacity = vehicle_capacities_[v];
6283 cumuls_[model_->
Start(v)]->SetMax(vehicle_capacity);
6284 cumuls_[model_->
End(v)]->SetMax(vehicle_capacity);
6287 forbidden_intervals_.resize(size);
6288 capacity_vars_.clear();
6289 if (min_capacity != max_capacity) {
6292 for (
int i = 0; i < size; ++i) {
6293 IntVar*
const capacity_var = capacity_vars_[i];
6294 if (i < model_->Size()) {
6295 IntVar*
const capacity_active = solver->MakeBoolVar();
6296 solver->AddConstraint(
6297 solver->MakeLessOrEqual(model_->
ActiveVar(i), capacity_active));
6298 solver->AddConstraint(solver->MakeIsLessOrEqualCt(
6299 cumuls_[i], capacity_var, capacity_active));
6301 solver->AddConstraint(
6302 solver->MakeLessOrEqual(cumuls_[i], capacity_var));
6309template <
int64_t value>
6310int64_t IthElementOrValue(
const std::vector<int64_t>& v, int64_t
index) {
6314void ComputeTransitClasses(
const std::vector<int>& evaluator_indices,
6315 std::vector<int>* class_evaluators,
6316 std::vector<int64_t>* vehicle_to_class) {
6317 CHECK(class_evaluators !=
nullptr);
6318 CHECK(vehicle_to_class !=
nullptr);
6319 class_evaluators->clear();
6320 vehicle_to_class->resize(evaluator_indices.size(), -1);
6321 absl::flat_hash_map<int, int64_t> evaluator_to_class;
6322 for (
int i = 0; i < evaluator_indices.size(); ++i) {
6323 const int evaluator_index = evaluator_indices[i];
6324 int evaluator_class = -1;
6325 if (!
gtl::FindCopy(evaluator_to_class, evaluator_index, &evaluator_class)) {
6326 evaluator_class = class_evaluators->size();
6327 evaluator_to_class[evaluator_index] = evaluator_class;
6328 class_evaluators->push_back(evaluator_index);
6330 (*vehicle_to_class)[i] = evaluator_class;
6335void RoutingDimension::InitializeTransitVariables(int64_t slack_max) {
6336 CHECK(!class_evaluators_.empty());
6337 CHECK(base_dimension_ ==
nullptr ||
6338 !state_dependent_class_evaluators_.empty());
6340 Solver*
const solver = model_->
solver();
6341 const int size = model_->
Size();
6344 return (0 <=
index &&
index < state_dependent_vehicle_to_class_.size())
6345 ? state_dependent_vehicle_to_class_[
index]
6346 : state_dependent_class_evaluators_.size();
6348 const std::string slack_name = name_ +
" slack";
6349 const std::string transit_name = name_ +
" fixed transit";
6351 bool are_all_evaluators_positive =
true;
6352 for (
int class_evaluator : class_evaluators_) {
6353 if (!
model()->is_transit_evaluator_positive_[class_evaluator]) {
6354 are_all_evaluators_positive =
false;
6358 for (int64_t i = 0; i < size; ++i) {
6359 fixed_transits_[i] = solver->MakeIntVar(
6360 are_all_evaluators_positive ? int64_t{0}
6364 if (base_dimension_ !=
nullptr) {
6365 if (state_dependent_class_evaluators_.size() == 1) {
6366 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
6367 for (int64_t j = 0; j < cumuls_.size(); ++j) {
6368 transition_variables[j] =
6369 MakeRangeMakeElementExpr(
6371 ->StateDependentTransitCallback(
6372 state_dependent_class_evaluators_[0])(i, j)
6374 base_dimension_->
CumulVar(i), solver)
6377 dependent_transits_[i] =
6378 solver->MakeElement(transition_variables, model_->
NextVar(i))
6381 IntVar*
const vehicle_class_var =
6383 ->MakeElement(dependent_vehicle_class_function,
6386 std::vector<IntVar*> transit_for_vehicle;
6387 transit_for_vehicle.reserve(state_dependent_class_evaluators_.size() +
6389 for (
int evaluator : state_dependent_class_evaluators_) {
6390 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
6391 for (int64_t j = 0; j < cumuls_.size(); ++j) {
6392 transition_variables[j] =
6393 MakeRangeMakeElementExpr(
6396 base_dimension_->
CumulVar(i), solver)
6399 transit_for_vehicle.push_back(
6400 solver->MakeElement(transition_variables, model_->
NextVar(i))
6403 transit_for_vehicle.push_back(solver->MakeIntConst(0));
6404 dependent_transits_[i] =
6405 solver->MakeElement(transit_for_vehicle, vehicle_class_var)->Var();
6408 dependent_transits_[i] = solver->MakeIntConst(0);
6412 IntExpr* transit_expr = fixed_transits_[i];
6413 if (dependent_transits_[i]->Min() != 0 ||
6414 dependent_transits_[i]->Max() != 0) {
6415 transit_expr = solver->MakeSum(transit_expr, dependent_transits_[i]);
6418 if (slack_max == 0) {
6419 slacks_[i] = solver->MakeIntConst(0);
6422 solver->MakeIntVar(0, slack_max, absl::StrCat(slack_name, i));
6423 transit_expr = solver->MakeSum(slacks_[i], transit_expr);
6425 transits_[i] = transit_expr->Var();
6429void RoutingDimension::InitializeTransits(
6430 const std::vector<int>& transit_evaluators,
6431 const std::vector<int>& state_dependent_transit_evaluators,
6432 int64_t slack_max) {
6434 CHECK(base_dimension_ ==
nullptr ||
6435 model_->
vehicles() == state_dependent_transit_evaluators.size());
6436 const int size = model_->
Size();
6437 transits_.resize(size,
nullptr);
6438 fixed_transits_.resize(size,
nullptr);
6439 slacks_.resize(size,
nullptr);
6440 dependent_transits_.resize(size,
nullptr);
6441 ComputeTransitClasses(transit_evaluators, &class_evaluators_,
6442 &vehicle_to_class_);
6443 if (base_dimension_ !=
nullptr) {
6444 ComputeTransitClasses(state_dependent_transit_evaluators,
6445 &state_dependent_class_evaluators_,
6446 &state_dependent_vehicle_to_class_);
6449 InitializeTransitVariables(slack_max);
6454 std::vector<int64_t>* values) {
6455 const int num_nodes = path.size();
6456 values->resize(num_nodes - 1);
6457 for (
int i = 0; i < num_nodes - 1; ++i) {
6458 (*values)[i] = evaluator(path[i], path[i + 1]);
6463 : model_(
model), occurrences_of_type_(
model.GetNumberOfVisitTypes()) {}
6466 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor) {
6473 for (
int pos = 0; pos < current_route_visits_.size(); pos++) {
6474 const int64_t current_visit = current_route_visits_[pos];
6481 DCHECK_LT(type, occurrences_of_type_.size());
6482 int& num_type_added = occurrences_of_type_[type].num_type_added_to_vehicle;
6483 int& num_type_removed =
6484 occurrences_of_type_[type].num_type_removed_from_vehicle;
6485 DCHECK_LE(num_type_removed, num_type_added);
6487 num_type_removed == num_type_added) {
6497 if (policy == VisitTypePolicy::TYPE_ADDED_TO_VEHICLE ||
6498 policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED) {
6501 if (policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED ||
6502 policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6510 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor) {
6513 std::fill(occurrences_of_type_.begin(), occurrences_of_type_.end(),
6519 current_route_visits_.clear();
6521 current = next_accessor(current)) {
6524 VisitTypePolicy::TYPE_ON_VEHICLE_UP_TO_VISIT) {
6525 occurrences_of_type_[type].position_of_last_type_on_vehicle_up_to_visit =
6526 current_route_visits_.size();
6528 current_route_visits_.push_back(current);
6550 check_hard_incompatibilities_(check_hard_incompatibilities) {}
6552bool TypeIncompatibilityChecker::HasRegulationsToCheck()
const {
6554 (check_hard_incompatibilities_ &&
6563bool TypeIncompatibilityChecker::CheckTypeRegulations(
int type,
6564 VisitTypePolicy policy,
6566 if (policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6571 for (
int incompatible_type :
6577 if (check_hard_incompatibilities_) {
6578 for (
int incompatible_type :
6588bool TypeRequirementChecker::HasRegulationsToCheck()
const {
6593bool TypeRequirementChecker::CheckRequiredTypesCurrentlyOnRoute(
6594 const std::vector<absl::flat_hash_set<int>>& required_type_alternatives,
6596 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6597 required_type_alternatives) {
6598 bool has_one_of_alternatives =
false;
6599 for (
int type_alternative : requirement_alternatives) {
6601 has_one_of_alternatives =
true;
6605 if (!has_one_of_alternatives) {
6612bool TypeRequirementChecker::CheckTypeRegulations(
int type,
6613 VisitTypePolicy policy,
6617 if (!CheckRequiredTypesCurrentlyOnRoute(
6623 if (!CheckRequiredTypesCurrentlyOnRoute(
6630 types_with_same_vehicle_requirements_on_route_.insert(type);
6635bool TypeRequirementChecker::FinalizeCheck()
const {
6636 for (
int type : types_with_same_vehicle_requirements_on_route_) {
6637 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6639 bool has_one_of_alternatives =
false;
6640 for (
const int type_alternative : requirement_alternatives) {
6642 has_one_of_alternatives =
true;
6646 if (!has_one_of_alternatives) {
6657 incompatibility_checker_(
model, true),
6658 requirement_checker_(
model),
6659 vehicle_demons_(
model.vehicles()) {}
6661void TypeRegulationsConstraint::PropagateNodeRegulations(
int node) {
6668 if (vehicle < 0)
return;
6669 DCHECK(vehicle_demons_[vehicle] !=
nullptr);
6673void TypeRegulationsConstraint::CheckRegulationsOnVehicle(
int vehicle) {
6674 const auto next_accessor = [
this, vehicle](int64_t node) {
6679 return model_.
End(vehicle);
6681 if (!incompatibility_checker_.
CheckVehicle(vehicle, next_accessor) ||
6682 !requirement_checker_.
CheckVehicle(vehicle, next_accessor)) {
6688 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6690 solver(),
this, &TypeRegulationsConstraint::CheckRegulationsOnVehicle,
6691 "CheckRegulationsOnVehicle", vehicle);
6693 for (
int node = 0; node < model_.
Size(); node++) {
6695 solver(),
this, &TypeRegulationsConstraint::PropagateNodeRegulations,
6696 "PropagateNodeRegulations", node);
6703 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6704 CheckRegulationsOnVehicle(vehicle);
6708void RoutingDimension::CloseModel(
bool use_light_propagation) {
6710 const auto capacity_lambda = [
this](int64_t vehicle) {
6711 return vehicle >= 0 ? vehicle_capacities_[vehicle]
6714 for (
int i = 0; i < capacity_vars_.size(); ++i) {
6715 IntVar*
const vehicle_var = model_->
VehicleVar(i);
6716 IntVar*
const capacity_var = capacity_vars_[i];
6717 if (use_light_propagation) {
6719 solver, capacity_var, vehicle_var, capacity_lambda,
6720 [
this]() {
return model_->enable_deep_serialization_; }));
6727 for (
int i = 0; i < fixed_transits_.size(); ++i) {
6728 IntVar*
const next_var = model_->
NextVar(i);
6729 IntVar*
const fixed_transit = fixed_transits_[i];
6730 const auto transit_vehicle_evaluator = [
this, i](int64_t to,
6731 int64_t eval_index) {
6734 if (use_light_propagation) {
6735 if (class_evaluators_.size() == 1) {
6736 const int class_evaluator_index = class_evaluators_[0];
6737 const auto& unary_callback =
6739 if (unary_callback ==
nullptr) {
6741 solver, fixed_transit, next_var,
6742 [
this, i](int64_t to) {
6745 [
this]() {
return model_->enable_deep_serialization_; }));
6747 fixed_transit->SetValue(unary_callback(i));
6751 solver, fixed_transit, next_var, model_->
VehicleVar(i),
6752 transit_vehicle_evaluator,
6753 [
this]() {
return model_->enable_deep_serialization_; }));
6756 if (class_evaluators_.size() == 1) {
6757 const int class_evaluator_index = class_evaluators_[0];
6758 const auto& unary_callback =
6760 if (unary_callback ==
nullptr) {
6762 fixed_transit, solver
6764 [
this, i](int64_t to) {
6766 class_evaluators_[0])(i, to);
6771 fixed_transit->SetValue(unary_callback(i));
6775 fixed_transit, solver
6783 GlobalVehicleBreaksConstraint* constraint =
6790 int64_t vehicle)
const {
6796 int64_t
index, int64_t min_value, int64_t max_value)
const {
6802 int64_t next_start =
min;
6806 if (next_start >
max)
break;
6807 if (next_start < interval->
start) {
6812 if (next_start <=
max) {
6821 CHECK_LT(vehicle, vehicle_span_upper_bounds_.size());
6823 vehicle_span_upper_bounds_[vehicle] =
upper_bound;
6829 CHECK_LT(vehicle, vehicle_span_cost_coefficients_.size());
6831 vehicle_span_cost_coefficients_[vehicle] =
coefficient;
6847 if (!
cost.IsNonDecreasing()) {
6848 LOG(
WARNING) <<
"Only non-decreasing cost functions are supported.";
6851 if (
cost.Value(0) < 0) {
6852 LOG(
WARNING) <<
"Only positive cost functions are supported.";
6855 if (
index >= cumul_var_piecewise_linear_cost_.size()) {
6856 cumul_var_piecewise_linear_cost_.resize(
index + 1);
6858 PiecewiseLinearCost& piecewise_linear_cost =
6859 cumul_var_piecewise_linear_cost_[
index];
6860 piecewise_linear_cost.var = cumuls_[
index];
6861 piecewise_linear_cost.cost = absl::make_unique<PiecewiseLinearFunction>(
cost);
6865 return (
index < cumul_var_piecewise_linear_cost_.size() &&
6866 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr);
6870 int64_t
index)
const {
6871 if (
index < cumul_var_piecewise_linear_cost_.size() &&
6872 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr) {
6873 return cumul_var_piecewise_linear_cost_[
index].cost.get();
6883 const int vehicle =
model->VehicleIndex(
index);
6885 return solver->
MakeProd(expr,
model->VehicleRouteConsideredVar(vehicle))
6892void RoutingDimension::SetupCumulVarPiecewiseLinearCosts(
6893 std::vector<IntVar*>* cost_elements)
const {
6894 CHECK(cost_elements !=
nullptr);
6895 Solver*
const solver = model_->
solver();
6896 for (
int i = 0; i < cumul_var_piecewise_linear_cost_.size(); ++i) {
6897 const PiecewiseLinearCost& piecewise_linear_cost =
6898 cumul_var_piecewise_linear_cost_[i];
6899 if (piecewise_linear_cost.var !=
nullptr) {
6900 IntExpr*
const expr = solver->MakePiecewiseLinearExpr(
6901 piecewise_linear_cost.var, *piecewise_linear_cost.cost);
6902 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6903 cost_elements->push_back(cost_var);
6914 if (
index >= cumul_var_soft_upper_bound_.size()) {
6915 cumul_var_soft_upper_bound_.resize(
index + 1, {
nullptr, 0, 0});
6922 return (
index < cumul_var_soft_upper_bound_.size() &&
6923 cumul_var_soft_upper_bound_[
index].var !=
nullptr);
6927 if (
index < cumul_var_soft_upper_bound_.size() &&
6928 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6929 return cumul_var_soft_upper_bound_[
index].bound;
6931 return cumuls_[
index]->Max();
6935 int64_t
index)
const {
6936 if (
index < cumul_var_soft_upper_bound_.size() &&
6937 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6938 return cumul_var_soft_upper_bound_[
index].coefficient;
6943void RoutingDimension::SetupCumulVarSoftUpperBoundCosts(
6944 std::vector<IntVar*>* cost_elements)
const {
6945 CHECK(cost_elements !=
nullptr);
6947 for (
int i = 0; i < cumul_var_soft_upper_bound_.size(); ++i) {
6948 const SoftBound& soft_bound = cumul_var_soft_upper_bound_[i];
6949 if (soft_bound.var !=
nullptr) {
6951 solver->
MakeSum(soft_bound.var, -soft_bound.bound), 0,
6952 soft_bound.coefficient);
6953 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6954 cost_elements->push_back(cost_var);
6958 soft_bound.coefficient);
6966 if (
index >= cumul_var_soft_lower_bound_.size()) {
6967 cumul_var_soft_lower_bound_.resize(
index + 1, {
nullptr, 0, 0});
6974 return (
index < cumul_var_soft_lower_bound_.size() &&
6975 cumul_var_soft_lower_bound_[
index].var !=
nullptr);
6979 if (
index < cumul_var_soft_lower_bound_.size() &&
6980 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6981 return cumul_var_soft_lower_bound_[
index].bound;
6983 return cumuls_[
index]->Min();
6987 int64_t
index)
const {
6988 if (
index < cumul_var_soft_lower_bound_.size() &&
6989 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6990 return cumul_var_soft_lower_bound_[
index].coefficient;
6995void RoutingDimension::SetupCumulVarSoftLowerBoundCosts(
6996 std::vector<IntVar*>* cost_elements)
const {
6997 CHECK(cost_elements !=
nullptr);
6999 for (
int i = 0; i < cumul_var_soft_lower_bound_.size(); ++i) {
7000 const SoftBound& soft_bound = cumul_var_soft_lower_bound_[i];
7001 if (soft_bound.var !=
nullptr) {
7004 soft_bound.coefficient);
7005 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
7006 cost_elements->push_back(cost_var);
7010 soft_bound.coefficient);
7015void RoutingDimension::SetupGlobalSpanCost(
7016 std::vector<IntVar*>* cost_elements)
const {
7017 CHECK(cost_elements !=
nullptr);
7018 Solver*
const solver = model_->
solver();
7019 if (global_span_cost_coefficient_ != 0) {
7020 std::vector<IntVar*> end_cumuls;
7021 for (
int i = 0; i < model_->
vehicles(); ++i) {
7022 end_cumuls.push_back(solver
7023 ->MakeProd(model_->vehicle_route_considered_[i],
7024 cumuls_[model_->
End(i)])
7027 IntVar*
const max_end_cumul = solver->
MakeMax(end_cumuls)->
Var();
7029 max_end_cumul, global_span_cost_coefficient_);
7030 std::vector<IntVar*> start_cumuls;
7031 for (
int i = 0; i < model_->
vehicles(); ++i) {
7032 IntVar* global_span_cost_start_cumul =
7034 solver->AddConstraint(solver->MakeIfThenElseCt(
7035 model_->vehicle_route_considered_[i], cumuls_[model_->
Start(i)],
7036 max_end_cumul, global_span_cost_start_cumul));
7037 start_cumuls.push_back(global_span_cost_start_cumul);
7039 IntVar*
const min_start_cumul = solver->MakeMin(start_cumuls)->Var();
7041 min_start_cumul, global_span_cost_coefficient_);
7046 for (
int var_index = 0; var_index < model_->
Size(); ++var_index) {
7048 slacks_[var_index], global_span_cost_coefficient_);
7049 cost_elements->push_back(
7052 model_->vehicle_route_considered_[0],
7055 solver->MakeSum(transits_[var_index],
7056 dependent_transits_[var_index]),
7057 global_span_cost_coefficient_),
7062 IntVar*
const end_range =
7063 solver->MakeDifference(max_end_cumul, min_start_cumul)->Var();
7064 end_range->SetMin(0);
7065 cost_elements->push_back(
7066 solver->MakeProd(end_range, global_span_cost_coefficient_)->Var());
7072 std::vector<IntervalVar*> breaks,
int vehicle,
7073 std::vector<int64_t> node_visit_transits) {
7074 if (breaks.empty())
return;
7076 [node_visit_transits](int64_t from, int64_t to) {
7077 return node_visit_transits[from];
7083 std::vector<IntervalVar*> breaks,
int vehicle,
7084 std::vector<int64_t> node_visit_transits,
7085 std::function<int64_t(int64_t, int64_t)> delays) {
7086 if (breaks.empty())
return;
7088 [node_visit_transits](int64_t from, int64_t to) {
7089 return node_visit_transits[from];
7091 const int delay_evaluator =
7098 std::vector<IntervalVar*> breaks,
int vehicle,
int pre_travel_evaluator,
7099 int post_travel_evaluator) {
7102 if (breaks.empty())
return;
7104 vehicle_break_intervals_[vehicle] = std::move(breaks);
7105 vehicle_pre_travel_evaluators_[vehicle] = pre_travel_evaluator;
7106 vehicle_post_travel_evaluators_[vehicle] = post_travel_evaluator;
7127 DCHECK(!break_constraints_are_initialized_);
7128 const int num_vehicles = model_->
vehicles();
7129 vehicle_break_intervals_.resize(num_vehicles);
7130 vehicle_pre_travel_evaluators_.resize(num_vehicles, -1);
7131 vehicle_post_travel_evaluators_.resize(num_vehicles, -1);
7132 vehicle_break_distance_duration_.resize(num_vehicles);
7133 break_constraints_are_initialized_ =
true;
7137 return break_constraints_are_initialized_;
7141 int vehicle)
const {
7143 DCHECK_LT(vehicle, vehicle_break_intervals_.size());
7144 return vehicle_break_intervals_[vehicle];
7149 DCHECK_LT(vehicle, vehicle_pre_travel_evaluators_.size());
7150 return vehicle_pre_travel_evaluators_[vehicle];
7155 DCHECK_LT(vehicle, vehicle_post_travel_evaluators_.size());
7156 return vehicle_post_travel_evaluators_[vehicle];
7165 vehicle_break_distance_duration_[vehicle].emplace_back(
distance, duration);
7174const std::vector<std::pair<int64_t, int64_t>>&
7177 DCHECK_LT(vehicle, vehicle_break_distance_duration_.size());
7178 return vehicle_break_distance_duration_[vehicle];
7184 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
7185 pickup_to_delivery_limits_per_pair_index_.resize(pair_index + 1);
7187 pickup_to_delivery_limits_per_pair_index_[pair_index] =
7188 std::move(limit_function);
7192 return !pickup_to_delivery_limits_per_pair_index_.empty();
7197 int delivery)
const {
7200 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
7204 pickup_to_delivery_limits_per_pair_index_[pair_index];
7205 if (!pickup_to_delivery_limit_function) {
7211 return pickup_to_delivery_limit_function(pickup, delivery);
7214void RoutingDimension::SetupSlackAndDependentTransitCosts()
const {
7215 if (model_->
vehicles() == 0)
return;
7217 bool all_vehicle_span_costs_are_equal =
true;
7218 for (
int i = 1; i < model_->
vehicles(); ++i) {
7219 all_vehicle_span_costs_are_equal &= vehicle_span_cost_coefficients_[i] ==
7220 vehicle_span_cost_coefficients_[0];
7223 if (all_vehicle_span_costs_are_equal &&
7224 vehicle_span_cost_coefficients_[0] == 0) {
7236 std::vector<const RoutingDimension*> dimensions_with_relevant_slacks = {
this};
7238 const RoutingDimension*
next =
7239 dimensions_with_relevant_slacks.back()->base_dimension_;
7240 if (
next ==
nullptr ||
next == dimensions_with_relevant_slacks.back()) {
7243 dimensions_with_relevant_slacks.push_back(
next);
7246 for (
auto it = dimensions_with_relevant_slacks.rbegin();
7247 it != dimensions_with_relevant_slacks.rend(); ++it) {
7248 for (
int i = 0; i < model_->
vehicles(); ++i) {
7254 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)
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
void AddWeightedVariableTargetToFinalizer(IntVar *var, int64_t target, int64_t cost)
Same as above with a weighted priority: the higher the cost, the more priority it has to be set close...
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_INFEASIBLE
Problem proven to be infeasible.
@ 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 AddWeightedVariableMaximizedByFinalizer(IntVar *var, int64_t cost)
Adds a variable to maximize in the solution finalizer, with a weighted priority: the higher the more ...
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.
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 their distance and their angle 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
const Collection::value_type::second_type FindPtrOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
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)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
Collection of objects used to extend the Constraint Solver library.
bool SolveModelWithSat(const RoutingModel &model, const RoutingSearchParameters &search_parameters, const Assignment *initial_solution, Assignment *solution)
Attempts to solve the model using the cp-sat solver.
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)
std::function< int64_t(int64_t, int64_t)> RoutingTransitCallback2
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
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,...
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.
RangeMinMaxIndexFunction * MakeCachedRangeMinMaxIndexFunction(const std::function< int64_t(int64_t)> &f, int64_t domain_start, int64_t domain_end)
IntVarLocalSearchFilter * MakeCPFeasibilityFilter(RoutingModel *routing_model)
Returns a filter checking the current solution using CP propagation.
DecisionBuilder * 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
std::optional< int64_t > end
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...