36#include "absl/container/flat_hash_map.h"
37#include "absl/container/flat_hash_set.h"
38#include "absl/flags/flag.h"
39#include "absl/functional/bind_front.h"
40#include "absl/memory/memory.h"
41#include "absl/meta/type_traits.h"
42#include "absl/status/statusor.h"
43#include "absl/strings/str_cat.h"
44#include "absl/strings/str_format.h"
45#include "absl/strings/string_view.h"
46#include "absl/time/time.h"
83class ExtendedSwapActiveOperator;
84class LocalSearchPhaseParameters;
85class MakeActiveAndRelocate;
86class MakeActiveOperator;
87class MakeChainInactiveOperator;
88class MakeInactiveOperator;
90class RelocateAndMakeActiveOperator;
91class SwapActiveOperator;
103using ResourceGroup = RoutingModel::ResourceGroup;
108class SetValuesFromTargets :
public DecisionBuilder {
110 SetValuesFromTargets(std::vector<IntVar*> variables,
111 std::vector<int64_t> targets)
112 : variables_(
std::move(variables)),
113 targets_(
std::move(targets)),
115 steps_(variables_.size(), 0) {
116 DCHECK_EQ(variables_.size(), targets_.size());
118 Decision* Next(Solver*
const solver)
override {
119 int index = index_.Value();
120 while (
index < variables_.size() && variables_[
index]->Bound()) {
123 index_.SetValue(solver,
index);
124 if (
index >= variables_.size())
return nullptr;
125 const int64_t variable_min = variables_[
index]->Min();
126 const int64_t variable_max = variables_[
index]->Max();
129 if (targets_[
index] <= variable_min) {
130 return solver->MakeAssignVariableValue(variables_[
index], variable_min);
131 }
else if (targets_[
index] >= variable_max) {
132 return solver->MakeAssignVariableValue(variables_[
index], variable_max);
134 int64_t step = steps_[
index];
139 if (
value < variable_min || variable_max <
value) {
140 step = GetNextStep(step);
151 steps_.SetValue(solver,
index, GetNextStep(step));
152 return solver->MakeAssignVariableValueOrDoNothing(variables_[
index],
158 int64_t GetNextStep(int64_t step)
const {
159 return (step > 0) ? -step :
CapSub(1, step);
161 const std::vector<IntVar*> variables_;
162 const std::vector<int64_t> targets_;
164 RevArray<int64_t> steps_;
170 std::vector<IntVar*> variables,
171 std::vector<int64_t> targets) {
173 new SetValuesFromTargets(std::move(variables), std::move(targets)));
178bool DimensionFixedTransitsEqualTransitEvaluatorForVehicle(
179 const RoutingDimension& dimension,
int vehicle) {
180 const RoutingModel*
const model = dimension.model();
181 int node =
model->Start(vehicle);
182 while (!
model->IsEnd(node)) {
183 if (!
model->NextVar(node)->Bound()) {
186 const int next =
model->NextVar(node)->Value();
187 if (dimension.transit_evaluator(vehicle)(node,
next) !=
188 dimension.FixedTransitVar(node)->Value()) {
196bool DimensionFixedTransitsEqualTransitEvaluators(
197 const RoutingDimension& dimension) {
198 for (
int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
199 if (!DimensionFixedTransitsEqualTransitEvaluatorForVehicle(dimension,
207class SetCumulsFromLocalDimensionCosts :
public DecisionBuilder {
209 SetCumulsFromLocalDimensionCosts(
210 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>*
212 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>*
214 SearchMonitor* monitor,
bool optimize_and_pack =
false)
215 : local_optimizers_(*local_optimizers),
216 local_mp_optimizers_(*local_mp_optimizers),
218 optimize_and_pack_(optimize_and_pack) {}
219 Decision* Next(Solver*
const solver)
override {
223 bool should_fail =
false;
224 for (
int i = 0; i < local_optimizers_.size(); ++i) {
225 const auto& local_optimizer = local_optimizers_[i];
226 const RoutingDimension*
const dimension = local_optimizer->dimension();
227 RoutingModel*
const model = dimension->model();
229 return model->NextVar(i)->Value();
231 const auto compute_cumul_values =
232 [
this, &
next](LocalDimensionCumulOptimizer* optimizer,
int vehicle,
233 std::vector<int64_t>* cumul_values,
234 std::vector<int64_t>* break_start_end_values) {
235 if (optimize_and_pack_) {
236 return optimizer->ComputePackedRouteCumuls(
237 vehicle,
next, cumul_values, break_start_end_values);
239 return optimizer->ComputeRouteCumuls(vehicle,
next, cumul_values,
240 break_start_end_values);
243 for (
int vehicle = 0; vehicle <
model->vehicles(); ++vehicle) {
245 DCHECK(DimensionFixedTransitsEqualTransitEvaluatorForVehicle(*dimension,
247 const bool vehicle_has_break_constraint =
248 dimension->HasBreakConstraints() &&
249 !dimension->GetBreakIntervalsOfVehicle(vehicle).empty();
250 LocalDimensionCumulOptimizer*
const optimizer =
251 vehicle_has_break_constraint ? local_mp_optimizers_[i].get()
252 : local_optimizer.get();
253 DCHECK(optimizer !=
nullptr);
254 std::vector<int64_t> cumul_values;
255 std::vector<int64_t> break_start_end_values;
257 optimizer, vehicle, &cumul_values, &break_start_end_values);
264 cumul_values.clear();
265 break_start_end_values.clear();
266 DCHECK(local_mp_optimizers_[i] !=
nullptr);
267 if (compute_cumul_values(local_mp_optimizers_[i].get(), vehicle,
268 &cumul_values, &break_start_end_values) ==
278 std::vector<IntVar*> cp_variables;
279 std::vector<int64_t> cp_values;
282 int current =
model->Start(vehicle);
284 cp_variables.push_back(dimension->CumulVar(current));
285 if (!
model->IsEnd(current)) {
286 current =
model->NextVar(current)->Value();
297 std::swap(cp_variables[1], cp_variables.back());
298 std::swap(cp_values[1], cp_values.back());
299 if (dimension->HasBreakConstraints()) {
301 dimension->GetBreakIntervalsOfVehicle(vehicle)) {
302 cp_variables.push_back(
interval->SafeStartExpr(0)->Var());
303 cp_variables.push_back(
interval->SafeEndExpr(0)->Var());
305 cp_values.insert(cp_values.end(), break_start_end_values.begin(),
306 break_start_end_values.end());
309 for (
int i = 0; i < cp_values.size(); ++i) {
311 cp_values[i] = cp_variables[i]->Min();
314 if (!solver->SolveAndCommit(
316 std::move(cp_values)),
330 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>&
332 const std::vector<std::unique_ptr<LocalDimensionCumulOptimizer>>&
333 local_mp_optimizers_;
334 SearchMonitor*
const monitor_;
335 const bool optimize_and_pack_;
338class SetCumulsFromGlobalDimensionCosts :
public DecisionBuilder {
340 SetCumulsFromGlobalDimensionCosts(
341 const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>*
343 const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>*
344 global_mp_optimizers,
345 SearchMonitor* monitor,
bool optimize_and_pack =
false)
346 : global_optimizers_(*global_optimizers),
347 global_mp_optimizers_(*global_mp_optimizers),
349 optimize_and_pack_(optimize_and_pack) {}
350 Decision* Next(Solver*
const solver)
override {
354 bool should_fail =
false;
355 for (
int i = 0; i < global_optimizers_.size(); ++i) {
356 const auto& global_optimizer = global_optimizers_[i];
357 const RoutingDimension* dimension = global_optimizer->dimension();
358 RoutingModel*
const model = dimension->model();
361 return model->NextVar(i)->Value();
364 DCHECK(DimensionFixedTransitsEqualTransitEvaluators(*dimension));
366 std::vector<int64_t> cumul_values;
367 std::vector<int64_t> break_start_end_values;
370 ? global_optimizer->ComputePackedCumuls(
next, &cumul_values,
371 &break_start_end_values)
372 : global_optimizer->ComputeCumuls(
next, &cumul_values,
373 &break_start_end_values);
380 cumul_values.clear();
381 break_start_end_values.clear();
382 DCHECK(global_mp_optimizers_[i] !=
nullptr);
385 ? global_mp_optimizers_[i]->ComputePackedCumuls(
386 next, &cumul_values, &break_start_end_values)
387 : global_mp_optimizers_[i]->ComputeCumuls(
388 next, &cumul_values, &break_start_end_values);
398 std::vector<IntVar*> cp_variables = dimension->cumuls();
399 std::vector<int64_t> cp_values;
401 if (dimension->HasBreakConstraints()) {
402 const int num_vehicles =
model->vehicles();
403 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
405 dimension->GetBreakIntervalsOfVehicle(vehicle)) {
406 cp_variables.push_back(
interval->SafeStartExpr(0)->Var());
407 cp_variables.push_back(
interval->SafeEndExpr(0)->Var());
410 cp_values.insert(cp_values.end(), break_start_end_values.begin(),
411 break_start_end_values.end());
414 for (
int i = 0; i < cp_values.size(); ++i) {
416 cp_values[i] = cp_variables[i]->Min();
419 if (!solver->SolveAndCommit(
421 std::move(cp_values)),
434 const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>&
436 const std::vector<std::unique_ptr<GlobalDimensionCumulOptimizer>>&
437 global_mp_optimizers_;
438 SearchMonitor*
const monitor_;
439 const bool optimize_and_pack_;
445 const Assignment* original_assignment, absl::Duration duration_limit) {
447 if (original_assignment ==
nullptr)
return nullptr;
448 if (duration_limit <= absl::ZeroDuration())
return original_assignment;
449 if (global_dimension_optimizers_.empty() &&
450 local_dimension_optimizers_.empty()) {
451 DCHECK(local_dimension_mp_optimizers_.empty());
452 return original_assignment;
461 Assignment* packed_assignment = solver_->MakeAssignment();
465 std::vector<DecisionBuilder*> decision_builders;
466 decision_builders.push_back(solver_->MakeRestoreAssignment(preassignment_));
467 decision_builders.push_back(
468 solver_->MakeRestoreAssignment(packed_assignment));
469 decision_builders.push_back(
470 solver_->RevAlloc(
new SetCumulsFromLocalDimensionCosts(
471 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
472 GetOrCreateLargeNeighborhoodSearchLimit(),
474 decision_builders.push_back(
475 solver_->RevAlloc(
new SetCumulsFromGlobalDimensionCosts(
476 &global_dimension_optimizers_, &global_dimension_mp_optimizers_,
477 GetOrCreateLargeNeighborhoodSearchLimit(),
479 decision_builders.push_back(
480 CreateFinalizerForMinimizedAndMaximizedVariables());
483 solver_->Compose(decision_builders);
484 solver_->Solve(restore_pack_and_finalize,
485 packed_dimensions_assignment_collector_, limit);
487 if (packed_dimensions_assignment_collector_->
solution_count() != 1) {
488 LOG(
ERROR) <<
"The given assignment is not valid for this model, or cannot "
493 packed_assignment->
Copy(original_assignment);
495 packed_dimensions_assignment_collector_->
solution(0));
497 return packed_assignment;
505 return sweep_arranger_.get();
510class DifferentFromValues :
public Constraint {
512 DifferentFromValues(
Solver* solver,
IntVar*
var, std::vector<int64_t> values)
514 void Post()
override {}
515 void InitialPropagate()
override { var_->RemoveValues(values_); }
516 std::string DebugString()
const override {
return "DifferentFromValues"; }
517 void Accept(ModelVisitor*
const visitor)
const override {
527 const std::vector<int64_t> values_;
542class LightFunctionElementConstraint :
public Constraint {
544 LightFunctionElementConstraint(Solver*
const solver, IntVar*
const var,
545 IntVar*
const index, F values,
546 std::function<
bool()> deep_serialize)
547 : Constraint(solver),
550 values_(
std::move(values)),
551 deep_serialize_(
std::move(deep_serialize)) {}
552 ~LightFunctionElementConstraint()
override {}
554 void Post()
override {
556 solver(),
this, &LightFunctionElementConstraint::IndexBound,
558 index_->WhenBound(demon);
561 void InitialPropagate()
override {
562 if (index_->Bound()) {
567 std::string DebugString()
const override {
568 return "LightFunctionElementConstraint";
571 void Accept(ModelVisitor*
const visitor)
const override {
578 if (deep_serialize_()) {
579 visitor->VisitInt64ToInt64Extension(values_, index_->Min(),
586 void IndexBound() { var_->SetValue(values_(index_->Min())); }
589 IntVar*
const index_;
591 std::function<bool()> deep_serialize_;
595Constraint* MakeLightElement(Solver*
const solver, IntVar*
const var,
596 IntVar*
const index, F values,
597 std::function<
bool()> deep_serialize) {
598 return solver->RevAlloc(
new LightFunctionElementConstraint<F>(
599 solver,
var,
index, std::move(values), std::move(deep_serialize)));
608class LightFunctionElement2Constraint :
public Constraint {
610 LightFunctionElement2Constraint(Solver*
const solver, IntVar*
const var,
611 IntVar*
const index1, IntVar*
const index2,
613 std::function<
bool()> deep_serialize)
614 : Constraint(solver),
618 values_(
std::move(values)),
619 deep_serialize_(
std::move(deep_serialize)) {}
620 ~LightFunctionElement2Constraint()
override {}
621 void Post()
override {
623 solver(),
this, &LightFunctionElement2Constraint::IndexBound,
625 index1_->WhenBound(demon);
626 index2_->WhenBound(demon);
628 void InitialPropagate()
override { IndexBound(); }
630 std::string DebugString()
const override {
631 return "LightFunctionElement2Constraint";
634 void Accept(ModelVisitor*
const visitor)
const override {
643 const int64_t index1_min = index1_->Min();
644 const int64_t index1_max = index1_->Max();
647 if (deep_serialize_()) {
648 for (
int i = index1_min; i <= index1_max; ++i) {
649 visitor->VisitInt64ToInt64Extension(
650 [
this, i](int64_t j) {
return values_(i, j); }, index2_->Min(),
659 if (index1_->Bound() && index2_->Bound()) {
660 var_->SetValue(values_(index1_->Min(), index2_->Min()));
665 IntVar*
const index1_;
666 IntVar*
const index2_;
668 std::function<bool()> deep_serialize_;
672Constraint* MakeLightElement2(Solver*
const solver, IntVar*
const var,
673 IntVar*
const index1, IntVar*
const index2,
674 F values, std::function<
bool()> deep_serialize) {
675 return solver->RevAlloc(
new LightFunctionElement2Constraint<F>(
676 solver,
var, index1, index2, std::move(values),
677 std::move(deep_serialize)));
681template <
class A,
class B>
682static int64_t ReturnZero(A
a, B
b) {
688 for (
int i = 0; i < size1; i++) {
689 for (
int j = 0; j < size2; j++) {
714 : nodes_(index_manager.num_nodes()),
715 vehicles_(index_manager.num_vehicles()),
716 max_active_vehicles_(vehicles_),
717 fixed_cost_of_vehicle_(vehicles_, 0),
719 linear_cost_factor_of_vehicle_(vehicles_, 0),
720 quadratic_cost_factor_of_vehicle_(vehicles_, 0),
721 vehicle_amortized_cost_factors_set_(false),
722 consider_empty_route_costs_(vehicles_, false),
724 costs_are_homogeneous_across_vehicles_(
726 cache_callbacks_(false),
728 vehicle_pickup_delivery_policy_(vehicles_, PICKUP_AND_DELIVERY_NO_ORDER),
729 has_hard_type_incompatibilities_(false),
730 has_temporal_type_incompatibilities_(false),
731 has_same_vehicle_type_requirements_(false),
732 has_temporal_type_requirements_(false),
736 manager_(index_manager) {
738 vehicle_to_transit_cost_.assign(
742 cache_callbacks_ = (nodes_ <=
parameters.max_callback_cache_size());
748 solver_ = absl::make_unique<Solver>(
"Routing", solver_parameters);
753 const int64_t size =
Size();
754 index_to_pickup_index_pairs_.resize(size);
755 index_to_delivery_index_pairs_.resize(size);
757 index_to_type_policy_.resize(index_manager.
num_indices());
760 for (
int v = 0; v < index_manager.
num_vehicles(); ++v) {
762 index_to_vehicle_[starts_[v]] = v;
764 index_to_vehicle_[ends_[v]] = v;
767 const std::vector<RoutingIndexManager::NodeIndex>& index_to_node =
769 index_to_equivalence_class_.resize(index_manager.
num_indices());
770 for (
int i = 0; i < index_to_node.size(); ++i) {
771 index_to_equivalence_class_[i] = index_to_node[i].value();
773 allowed_vehicles_.resize(
Size() + vehicles_);
776void RoutingModel::Initialize() {
777 const int size =
Size();
779 solver_->MakeIntVarArray(size, 0, size + vehicles_ - 1,
"Nexts", &nexts_);
780 solver_->AddConstraint(solver_->MakeAllDifferent(nexts_,
false));
781 index_to_disjunctions_.resize(size + vehicles_);
784 solver_->MakeIntVarArray(size + vehicles_, -1, vehicles_ - 1,
"Vehicles",
787 solver_->MakeBoolVarArray(size,
"Active", &active_);
789 solver_->MakeBoolVarArray(vehicles_,
"ActiveVehicle", &vehicle_active_);
791 solver_->MakeBoolVarArray(vehicles_,
"VehicleCostsConsidered",
792 &vehicle_costs_considered_);
794 solver_->MakeBoolVarArray(size + vehicles_,
"IsBoundToEnd",
799 preassignment_ = solver_->MakeAssignment();
806 absl::flat_hash_set<RangeIntToIntFunction*> value_functions_delete;
807 absl::flat_hash_set<RangeMinMaxIndexFunction*> index_functions_delete;
808 for (
const auto& cache_line : state_dependent_transit_evaluators_cache_) {
809 for (
const auto& key_transit : *cache_line) {
810 value_functions_delete.insert(key_transit.second.transit);
811 index_functions_delete.insert(key_transit.second.transit_plus_identity);
822 return model->RegisterPositiveTransitCallback(std::move(
callback));
828 RoutingModel*
model) {
830 return model->RegisterPositiveUnaryTransitCallback(std::move(
callback));
832 return model->RegisterUnaryTransitCallback(std::move(
callback));
837 return RegisterUnaryCallback(
838 [
this, values = std::move(values)](int64_t i) {
842 std::all_of(std::cbegin(values), std::cend(values),
843 [](int64_t transit) {
return transit >= 0; }),
848 const int index = unary_transit_evaluators_.size();
849 unary_transit_evaluators_.push_back(std::move(
callback));
851 return unary_transit_evaluators_[
index](i);
856 std::vector<std::vector<int64_t> > values) {
857 bool all_transits_positive =
true;
858 for (
const std::vector<int64_t>& transit_values : values) {
859 all_transits_positive =
860 std::all_of(std::cbegin(transit_values), std::cend(transit_values),
861 [](int64_t transit) {
return transit >= 0; });
862 if (!all_transits_positive) {
866 return RegisterCallback(
867 [
this, values = std::move(values)](int64_t i, int64_t j) {
871 all_transits_positive,
this);
876 is_transit_evaluator_positive_.push_back(
true);
877 DCHECK(TransitCallbackPositive(
883 if (cache_callbacks_) {
885 std::vector<int64_t> cache(size * size, 0);
886 for (
int i = 0; i < size; ++i) {
887 for (
int j = 0; j < size; ++j) {
888 cache[i * size + j] =
callback(i, j);
891 transit_evaluators_.push_back(
892 [cache, size](int64_t i, int64_t j) {
return cache[i * size + j]; });
894 transit_evaluators_.push_back(std::move(
callback));
896 if (transit_evaluators_.size() != unary_transit_evaluators_.size()) {
897 DCHECK_EQ(transit_evaluators_.size(), unary_transit_evaluators_.size() + 1);
898 unary_transit_evaluators_.push_back(
nullptr);
900 if (transit_evaluators_.size() != is_transit_evaluator_positive_.size()) {
902 is_transit_evaluator_positive_.size() + 1);
903 is_transit_evaluator_positive_.push_back(
false);
905 return transit_evaluators_.size() - 1;
909 is_transit_evaluator_positive_.push_back(
true);
917 state_dependent_transit_evaluators_cache_.push_back(
918 absl::make_unique<StateDependentTransitCallbackCache>());
919 StateDependentTransitCallbackCache*
const cache =
920 state_dependent_transit_evaluators_cache_.back().get();
921 state_dependent_transit_evaluators_.push_back(
922 [cache,
callback](int64_t i, int64_t j) {
926 cache->insert({CacheKey(i, j),
value});
929 return state_dependent_transit_evaluators_.size() - 1;
932void RoutingModel::AddNoCycleConstraintInternal() {
933 if (no_cycle_constraint_ ==
nullptr) {
934 no_cycle_constraint_ = solver_->MakeNoCycle(nexts_, active_);
935 solver_->AddConstraint(no_cycle_constraint_);
940 int64_t
capacity,
bool fix_start_cumul_to_zero,
941 const std::string&
name) {
942 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
943 std::vector<int64_t> capacities(vehicles_,
capacity);
944 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
945 std::move(capacities),
946 fix_start_cumul_to_zero,
name);
950 const std::vector<int>& evaluator_indices, int64_t slack_max,
951 int64_t
capacity,
bool fix_start_cumul_to_zero,
const std::string&
name) {
952 std::vector<int64_t> capacities(vehicles_,
capacity);
953 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
954 std::move(capacities),
955 fix_start_cumul_to_zero,
name);
959 int evaluator_index, int64_t slack_max,
960 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
961 const std::string&
name) {
962 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
963 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
964 std::move(vehicle_capacities),
965 fix_start_cumul_to_zero,
name);
969 const std::vector<int>& evaluator_indices, int64_t slack_max,
970 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
971 const std::string&
name) {
972 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
973 std::move(vehicle_capacities),
974 fix_start_cumul_to_zero,
name);
977bool RoutingModel::AddDimensionWithCapacityInternal(
978 const std::vector<int>& evaluator_indices, int64_t slack_max,
979 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
980 const std::string&
name) {
981 CHECK_EQ(vehicles_, vehicle_capacities.size());
982 return InitializeDimensionInternal(
983 evaluator_indices, std::vector<int>(), slack_max, fix_start_cumul_to_zero,
987bool RoutingModel::InitializeDimensionInternal(
988 const std::vector<int>& evaluator_indices,
989 const std::vector<int>& state_dependent_evaluator_indices,
990 int64_t slack_max,
bool fix_start_cumul_to_zero,
991 RoutingDimension* dimension) {
992 CHECK(dimension !=
nullptr);
993 CHECK_EQ(vehicles_, evaluator_indices.size());
994 CHECK((dimension->base_dimension_ ==
nullptr &&
995 state_dependent_evaluator_indices.empty()) ||
996 vehicles_ == state_dependent_evaluator_indices.size());
999 dimension_name_to_index_[dimension->name()] = dimension_index;
1000 dimensions_.push_back(dimension);
1001 dimension->Initialize(evaluator_indices, state_dependent_evaluator_indices,
1003 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
1004 nexts_, active_, dimension->cumuls(), dimension->transits()));
1005 if (fix_start_cumul_to_zero) {
1006 for (
int i = 0; i < vehicles_; ++i) {
1007 IntVar*
const start_cumul = dimension->CumulVar(
Start(i));
1009 start_cumul->SetValue(0);
1020 bool fix_start_cumul_to_zero,
const std::string& dimension_name) {
1021 const int evaluator_index =
1022 RegisterUnaryCallback([
value](int64_t) {
return value; },
1024 return std::make_pair(evaluator_index,
1026 fix_start_cumul_to_zero, dimension_name));
1030 std::vector<int64_t> values, int64_t
capacity,
bool fix_start_cumul_to_zero,
1031 const std::string& dimension_name) {
1033 return std::make_pair(evaluator_index,
1035 fix_start_cumul_to_zero, dimension_name));
1039 std::vector<std::vector<int64_t>> values, int64_t
capacity,
1040 bool fix_start_cumul_to_zero,
const std::string& dimension_name) {
1042 return std::make_pair(evaluator_index,
1044 fix_start_cumul_to_zero, dimension_name));
1056 CHECK(callback_ !=
nullptr);
1060 int64_t Min()
const override {
1062 const int idx_min = index_->Min();
1063 const int idx_max = index_->Max() + 1;
1064 return (idx_min < idx_max) ? callback_->RangeMin(idx_min, idx_max)
1067 void SetMin(int64_t new_min)
override {
1068 const int64_t old_min = Min();
1069 const int64_t old_max = Max();
1070 if (old_min < new_min && new_min <= old_max) {
1071 const int64_t old_idx_min = index_->Min();
1072 const int64_t old_idx_max = index_->Max() + 1;
1073 if (old_idx_min < old_idx_max) {
1074 const int64_t new_idx_min = callback_->RangeFirstInsideInterval(
1075 old_idx_min, old_idx_max, new_min, old_max + 1);
1076 index_->SetMin(new_idx_min);
1077 if (new_idx_min < old_idx_max) {
1078 const int64_t new_idx_max = callback_->RangeLastInsideInterval(
1079 new_idx_min, old_idx_max, new_min, old_max + 1);
1080 index_->SetMax(new_idx_max);
1085 int64_t Max()
const override {
1087 const int idx_min = index_->Min();
1088 const int idx_max = index_->Max() + 1;
1089 return (idx_min < idx_max) ? callback_->RangeMax(idx_min, idx_max)
1092 void SetMax(int64_t new_max)
override {
1093 const int64_t old_min = Min();
1094 const int64_t old_max = Max();
1095 if (old_min <= new_max && new_max < old_max) {
1096 const int64_t old_idx_min = index_->Min();
1097 const int64_t old_idx_max = index_->Max() + 1;
1098 if (old_idx_min < old_idx_max) {
1099 const int64_t new_idx_min = callback_->RangeFirstInsideInterval(
1100 old_idx_min, old_idx_max, old_min, new_max + 1);
1101 index_->SetMin(new_idx_min);
1102 if (new_idx_min < old_idx_max) {
1103 const int64_t new_idx_max = callback_->RangeLastInsideInterval(
1104 new_idx_min, old_idx_max, old_min, new_max + 1);
1105 index_->SetMax(new_idx_max);
1110 void WhenRange(Demon* d)
override { index_->WhenRange(d); }
1113 const RangeIntToIntFunction*
const callback_;
1114 IntVar*
const index_;
1117IntExpr* MakeRangeMakeElementExpr(
const RangeIntToIntFunction*
callback,
1118 IntVar*
index, Solver* s) {
1119 return s->RegisterIntExpr(
1125 const std::vector<int>& dependent_transits,
1127 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
1128 const std::string&
name) {
1129 const std::vector<int> pure_transits(vehicles_, 0);
1131 pure_transits, dependent_transits, base_dimension, slack_max,
1132 std::move(vehicle_capacities), fix_start_cumul_to_zero,
name);
1137 int64_t vehicle_capacity,
bool fix_start_cumul_to_zero,
1138 const std::string&
name) {
1140 0, transit, dimension, slack_max, vehicle_capacity,
1141 fix_start_cumul_to_zero,
name);
1144bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacityInternal(
1145 const std::vector<int>& pure_transits,
1146 const std::vector<int>& dependent_transits,
1148 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
1149 const std::string&
name) {
1150 CHECK_EQ(vehicles_, vehicle_capacities.size());
1152 if (base_dimension ==
nullptr) {
1154 name, RoutingDimension::SelfBased());
1157 name, base_dimension);
1159 return InitializeDimensionInternal(pure_transits, dependent_transits,
1160 slack_max, fix_start_cumul_to_zero,
1165 int pure_transit,
int dependent_transit,
1167 int64_t vehicle_capacity,
bool fix_start_cumul_to_zero,
1168 const std::string&
name) {
1169 std::vector<int> pure_transits(vehicles_, pure_transit);
1170 std::vector<int> dependent_transits(vehicles_, dependent_transit);
1171 std::vector<int64_t> vehicle_capacities(vehicles_, vehicle_capacity);
1172 return AddDimensionDependentDimensionWithVehicleCapacityInternal(
1173 pure_transits, dependent_transits, base_dimension, slack_max,
1174 std::move(vehicle_capacities), fix_start_cumul_to_zero,
name);
1178 const std::function<int64_t(int64_t)>& f, int64_t domain_start,
1179 int64_t domain_end) {
1180 const std::function<int64_t(int64_t)> g = [&f](int64_t x) {
1190 std::vector<std::string> dimension_names;
1191 for (
const auto& dimension_name_index : dimension_name_to_index_) {
1192 dimension_names.push_back(dimension_name_index.first);
1194 std::sort(dimension_names.begin(), dimension_names.end());
1195 return dimension_names;
1201 if (dim_index < 0 || dim_index >= global_optimizer_index_.size() ||
1202 global_optimizer_index_[dim_index] < 0) {
1205 const int optimizer_index = global_optimizer_index_[dim_index];
1206 DCHECK_LT(optimizer_index, global_dimension_optimizers_.size());
1207 return global_dimension_optimizers_[optimizer_index].get();
1213 if (dim_index < 0 || dim_index >= global_optimizer_index_.size() ||
1214 global_optimizer_index_[dim_index] < 0) {
1217 const int optimizer_index = global_optimizer_index_[dim_index];
1218 DCHECK_LT(optimizer_index, global_dimension_mp_optimizers_.size());
1219 return global_dimension_mp_optimizers_[optimizer_index].get();
1225 if (dim_index < 0 || dim_index >= local_optimizer_index_.size() ||
1226 local_optimizer_index_[dim_index] < 0) {
1229 const int optimizer_index = local_optimizer_index_[dim_index];
1230 DCHECK_LT(optimizer_index, local_dimension_optimizers_.size());
1231 return local_dimension_optimizers_[optimizer_index].get();
1237 if (dim_index < 0 || dim_index >= local_optimizer_index_.size() ||
1238 local_optimizer_index_[dim_index] < 0) {
1241 const int optimizer_index = local_optimizer_index_[dim_index];
1242 DCHECK_LT(optimizer_index, local_dimension_mp_optimizers_.size());
1243 return local_dimension_mp_optimizers_[optimizer_index].get();
1251 const std::string& dimension_name)
const {
1257 const std::string& dimension_name)
const {
1258 return *dimensions_[
gtl::FindOrDie(dimension_name_to_index_, dimension_name)];
1262 const std::string& dimension_name)
const {
1265 return dimensions_[
index];
1271ResourceGroup::Attributes::Attributes()
1272 : start_domain_(
Domain::AllValues()), end_domain_(
Domain::AllValues()) {
1278 : start_domain_(
std::move(start_domain)),
1279 end_domain_(
std::move(end_domain)) {}
1287 GetDefaultAttributes());
1290void ResourceGroup::Resource::SetDimensionAttributes(
1292 DCHECK(dimension_attributes_.empty())
1293 <<
"As of 2021/07, each resource can only constrain a single dimension.";
1296 model_->GetDimensionIndex(dimension->
name());
1298 DCHECK(!dimension_attributes_.contains(dimension_index));
1299 dimension_attributes_[dimension_index] = std::move(attributes);
1304 static const Attributes*
const kAttributes =
new Attributes();
1305 return *kAttributes;
1309 resource_groups_.push_back(absl::make_unique<ResourceGroup>(
this));
1310 return resource_groups_.back().get();
1315 resources_.push_back(
Resource(model_));
1316 resources_.back().SetDimensionAttributes(std::move(attributes), dimension);
1319 model_->GetDimensionIndex(dimension->
name());
1321 affected_dimension_indices_.insert(dimension_index);
1323 DCHECK_EQ(affected_dimension_indices_.size(), 1)
1324 <<
"As of 2021/07, each ResourceGroup can only affect a single "
1325 "RoutingDimension at a time.";
1333 return dimension_resource_group_indices_[dim];
1338 for (
int i = 0; i < vehicles_; ++i) {
1346 CHECK_LT(evaluator_index, transit_evaluators_.size());
1347 vehicle_to_transit_cost_[vehicle] = evaluator_index;
1351 for (
int i = 0; i < vehicles_; ++i) {
1358 return fixed_cost_of_vehicle_[vehicle];
1364 fixed_cost_of_vehicle_[vehicle] =
cost;
1368 int64_t linear_cost_factor, int64_t quadratic_cost_factor) {
1369 for (
int v = 0; v < vehicles_; v++) {
1376 int64_t linear_cost_factor, int64_t quadratic_cost_factor,
int vehicle) {
1380 if (linear_cost_factor + quadratic_cost_factor > 0) {
1381 vehicle_amortized_cost_factors_set_ =
true;
1383 linear_cost_factor_of_vehicle_[vehicle] = linear_cost_factor;
1384 quadratic_cost_factor_of_vehicle_[vehicle] = quadratic_cost_factor;
1390struct CostClassComparator {
1397struct VehicleClassComparator {
1398 bool operator()(
const RoutingModel::VehicleClass&
a,
1399 const RoutingModel::VehicleClass&
b)
const {
1409void RoutingModel::ComputeCostClasses(
1412 cost_classes_.reserve(vehicles_);
1413 cost_classes_.clear();
1414 cost_class_index_of_vehicle_.assign(vehicles_,
CostClassIndex(-1));
1415 std::map<CostClass, CostClassIndex, CostClassComparator> cost_class_map;
1418 const CostClass zero_cost_class(0);
1419 cost_classes_.push_back(zero_cost_class);
1420 DCHECK_EQ(cost_classes_[kCostClassIndexOfZeroCost].evaluator_index, 0);
1421 cost_class_map[zero_cost_class] = kCostClassIndexOfZeroCost;
1426 has_vehicle_with_zero_cost_class_ =
false;
1427 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
1428 CostClass cost_class(vehicle_to_transit_cost_[vehicle]);
1432 const int64_t coeff =
1434 if (coeff == 0)
continue;
1435 cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1438 std::sort(cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1440 cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1446 if (cost_class_index == kCostClassIndexOfZeroCost) {
1447 has_vehicle_with_zero_cost_class_ =
true;
1448 }
else if (cost_class_index == num_cost_classes) {
1449 cost_classes_.push_back(cost_class);
1451 cost_class_index_of_vehicle_[vehicle] = cost_class_index;
1465 costs_are_homogeneous_across_vehicles_ &= has_vehicle_with_zero_cost_class_
1472 return std::tie(
a.cost_class_index,
a.fixed_cost,
a.start_equivalence_class,
1473 a.end_equivalence_class,
a.unvisitable_nodes_fprint,
1474 a.dimension_start_cumuls_min,
a.dimension_start_cumuls_max,
1475 a.dimension_end_cumuls_min,
a.dimension_end_cumuls_max,
1476 a.dimension_capacities,
a.dimension_evaluator_classes) <
1477 std::tie(
b.cost_class_index,
b.fixed_cost,
b.start_equivalence_class,
1478 b.end_equivalence_class,
b.unvisitable_nodes_fprint,
1479 b.dimension_start_cumuls_min,
b.dimension_start_cumuls_max,
1480 b.dimension_end_cumuls_min,
b.dimension_end_cumuls_max,
1481 b.dimension_capacities,
b.dimension_evaluator_classes);
1484void RoutingModel::ComputeVehicleClasses() {
1485 vehicle_classes_.reserve(vehicles_);
1486 vehicle_classes_.clear();
1488 std::map<VehicleClass, VehicleClassIndex, VehicleClassComparator>
1490 const int nodes_unvisitability_num_bytes = (vehicle_vars_.size() + 7) / 8;
1491 std::unique_ptr<char[]> nodes_unvisitability_bitmask(
1492 new char[nodes_unvisitability_num_bytes]);
1493 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
1495 vehicle_class.cost_class_index = cost_class_index_of_vehicle_[vehicle];
1498 index_to_equivalence_class_[
Start(vehicle)];
1500 index_to_equivalence_class_[
End(vehicle)];
1504 start_cumul_var->
Min());
1506 start_cumul_var->
Max());
1515 memset(nodes_unvisitability_bitmask.get(), 0,
1516 nodes_unvisitability_num_bytes);
1520 (!vehicle_var->
Contains(vehicle) ||
1522 nodes_unvisitability_bitmask[
index / CHAR_BIT] |= 1U
1523 << (
index % CHAR_BIT);
1527 nodes_unvisitability_bitmask.get(), nodes_unvisitability_num_bytes);
1531 if (vehicle_class_index == num_vehicle_classes) {
1534 vehicle_class_index_of_vehicle_[vehicle] = vehicle_class_index;
1538void RoutingModel::ComputeVehicleTypes() {
1539 const int nodes_squared = nodes_ * nodes_;
1540 std::vector<int>& type_index_of_vehicle =
1541 vehicle_type_container_.type_index_of_vehicle;
1542 std::vector<std::set<VehicleTypeContainer::VehicleClassEntry>>&
1543 sorted_vehicle_classes_per_type =
1544 vehicle_type_container_.sorted_vehicle_classes_per_type;
1545 std::vector<std::deque<int>>& vehicles_per_vehicle_class =
1546 vehicle_type_container_.vehicles_per_vehicle_class;
1548 type_index_of_vehicle.resize(vehicles_);
1549 sorted_vehicle_classes_per_type.clear();
1550 sorted_vehicle_classes_per_type.reserve(vehicles_);
1551 vehicles_per_vehicle_class.clear();
1554 absl::flat_hash_map<int64_t, int> type_to_type_index;
1556 for (
int v = 0; v < vehicles_; v++) {
1557 const int start = manager_.IndexToNode(
Start(v)).value();
1558 const int end = manager_.IndexToNode(
End(v)).value();
1560 const int64_t type = cost_class * nodes_squared + start * nodes_ + end;
1562 const auto& vehicle_type_added = type_to_type_index.insert(
1563 std::make_pair(type, type_to_type_index.size()));
1565 const int index = vehicle_type_added.first->second;
1568 const VehicleTypeContainer::VehicleClassEntry class_entry = {
1571 if (vehicle_type_added.second) {
1574 sorted_vehicle_classes_per_type.push_back({class_entry});
1578 sorted_vehicle_classes_per_type[
index].insert(class_entry);
1581 type_index_of_vehicle[v] =
index;
1585void RoutingModel::FinalizeVisitTypes() {
1591 single_nodes_of_type_.clear();
1592 single_nodes_of_type_.resize(num_visit_types_);
1593 pair_indices_of_type_.clear();
1594 pair_indices_of_type_.resize(num_visit_types_);
1595 std::vector<absl::flat_hash_set<int>> pair_indices_added_for_type(
1600 if (visit_type < 0) {
1603 const std::vector<std::pair<int, int>>& pickup_index_pairs =
1604 index_to_pickup_index_pairs_[
index];
1605 const std::vector<std::pair<int, int>>& delivery_index_pairs =
1606 index_to_delivery_index_pairs_[
index];
1607 if (pickup_index_pairs.empty() && delivery_index_pairs.empty()) {
1608 single_nodes_of_type_[visit_type].push_back(
index);
1610 for (
const std::vector<std::pair<int, int>>* index_pairs :
1611 {&pickup_index_pairs, &delivery_index_pairs}) {
1612 for (
const std::pair<int, int>& index_pair : *index_pairs) {
1613 const int pair_index = index_pair.first;
1614 if (pair_indices_added_for_type[visit_type].insert(pair_index).second) {
1615 pair_indices_of_type_[visit_type].push_back(pair_index);
1621 TopologicallySortVisitTypes();
1624void RoutingModel::TopologicallySortVisitTypes() {
1625 if (!has_same_vehicle_type_requirements_ &&
1626 !has_temporal_type_requirements_) {
1629 std::vector<std::pair<double, double>> type_requirement_tightness(
1630 num_visit_types_, {0, 0});
1631 std::vector<absl::flat_hash_set<int>> type_to_dependent_types(
1633 SparseBitset<> types_in_requirement_graph(num_visit_types_);
1634 std::vector<int> in_degree(num_visit_types_, 0);
1635 for (
int type = 0; type < num_visit_types_; type++) {
1636 int num_alternative_required_types = 0;
1637 int num_required_sets = 0;
1638 for (
const std::vector<absl::flat_hash_set<int>>*
1639 required_type_alternatives :
1640 {&required_type_alternatives_when_adding_type_index_[type],
1641 &required_type_alternatives_when_removing_type_index_[type],
1642 &same_vehicle_required_type_alternatives_per_type_index_[type]}) {
1643 for (
const absl::flat_hash_set<int>& alternatives :
1644 *required_type_alternatives) {
1645 types_in_requirement_graph.Set(type);
1646 num_required_sets++;
1647 for (
int required_type : alternatives) {
1648 type_requirement_tightness[required_type].second +=
1649 1.0 / alternatives.size();
1650 types_in_requirement_graph.Set(required_type);
1651 num_alternative_required_types++;
1652 if (type_to_dependent_types[required_type].insert(type).second) {
1658 if (num_alternative_required_types > 0) {
1659 type_requirement_tightness[type].first += 1.0 * num_required_sets *
1661 num_alternative_required_types;
1666 topologically_sorted_visit_types_.clear();
1667 std::vector<int> current_types_with_zero_indegree;
1668 for (
int type : types_in_requirement_graph.PositionsSetAtLeastOnce()) {
1669 DCHECK(type_requirement_tightness[type].first > 0 ||
1670 type_requirement_tightness[type].second > 0);
1671 if (in_degree[type] == 0) {
1672 current_types_with_zero_indegree.push_back(type);
1676 int num_types_added = 0;
1677 while (!current_types_with_zero_indegree.empty()) {
1680 topologically_sorted_visit_types_.push_back({});
1681 std::vector<int>& topological_group =
1682 topologically_sorted_visit_types_.back();
1683 std::vector<int> next_types_with_zero_indegree;
1684 for (
int type : current_types_with_zero_indegree) {
1685 topological_group.push_back(type);
1687 for (
int dependent_type : type_to_dependent_types[type]) {
1688 DCHECK_GT(in_degree[dependent_type], 0);
1689 if (--in_degree[dependent_type] == 0) {
1690 next_types_with_zero_indegree.push_back(dependent_type);
1701 std::sort(topological_group.begin(), topological_group.end(),
1702 [&type_requirement_tightness](
int type1,
int type2) {
1703 const auto& tightness1 = type_requirement_tightness[type1];
1704 const auto& tightness2 = type_requirement_tightness[type2];
1705 return tightness1 > tightness2 ||
1706 (tightness1 == tightness2 && type1 < type2);
1709 current_types_with_zero_indegree.swap(next_types_with_zero_indegree);
1712 const int num_types_in_requirement_graph =
1713 types_in_requirement_graph.NumberOfSetCallsWithDifferentArguments();
1714 DCHECK_LE(num_types_added, num_types_in_requirement_graph);
1715 if (num_types_added < num_types_in_requirement_graph) {
1717 topologically_sorted_visit_types_.clear();
1722 const std::vector<int64_t>& indices, int64_t penalty,
1723 int64_t max_cardinality) {
1725 for (
int i = 0; i < indices.size(); ++i) {
1730 disjunctions_.push_back({indices, {penalty, max_cardinality}});
1731 for (
const int64_t
index : indices) {
1732 index_to_disjunctions_[
index].push_back(disjunction_index);
1734 return disjunction_index;
1737std::vector<std::pair<int64_t, int64_t>>
1739 std::vector<std::pair<int64_t, int64_t>> var_index_pairs;
1740 for (
const Disjunction& disjunction : disjunctions_) {
1741 const std::vector<int64_t>&
var_indices = disjunction.indices;
1745 if (index_to_disjunctions_[v0].size() == 1 &&
1746 index_to_disjunctions_[v1].size() == 1) {
1751 std::sort(var_index_pairs.begin(), var_index_pairs.end());
1752 return var_index_pairs;
1757 for (Disjunction& disjunction : disjunctions_) {
1758 bool has_one_potentially_active_var =
false;
1759 for (
const int64_t var_index : disjunction.indices) {
1761 has_one_potentially_active_var =
true;
1765 if (!has_one_potentially_active_var) {
1766 disjunction.value.max_cardinality = 0;
1772 const std::vector<int64_t>& indices = disjunctions_[disjunction].indices;
1773 const int indices_size = indices.size();
1774 std::vector<IntVar*> disjunction_vars(indices_size);
1775 for (
int i = 0; i < indices_size; ++i) {
1776 const int64_t
index = indices[i];
1780 const int64_t max_cardinality =
1781 disjunctions_[disjunction].value.max_cardinality;
1782 IntVar* no_active_var = solver_->MakeBoolVar();
1783 IntVar* number_active_vars = solver_->MakeIntVar(0, max_cardinality);
1784 solver_->AddConstraint(
1785 solver_->MakeSumEquality(disjunction_vars, number_active_vars));
1786 solver_->AddConstraint(solver_->MakeIsDifferentCstCt(
1787 number_active_vars, max_cardinality, no_active_var));
1788 const int64_t penalty = disjunctions_[disjunction].value.penalty;
1790 no_active_var->SetMax(0);
1793 return solver_->MakeProd(no_active_var, penalty)->Var();
1798 const std::vector<int64_t>& indices, int64_t
cost) {
1799 if (!indices.empty()) {
1800 ValuedNodes<int64_t> same_vehicle_cost;
1801 for (
const int64_t
index : indices) {
1802 same_vehicle_cost.indices.push_back(
index);
1804 same_vehicle_cost.value =
cost;
1805 same_vehicle_costs_.push_back(same_vehicle_cost);
1811 auto& allowed_vehicles = allowed_vehicles_[
index];
1812 allowed_vehicles.clear();
1814 allowed_vehicles.insert(vehicle);
1819 AddPickupAndDeliverySetsInternal({pickup}, {delivery});
1826 AddPickupAndDeliverySetsInternal(
1829 pickup_delivery_disjunctions_.push_back(
1830 {pickup_disjunction, delivery_disjunction});
1833void RoutingModel::AddPickupAndDeliverySetsInternal(
1834 const std::vector<int64_t>& pickups,
1835 const std::vector<int64_t>& deliveries) {
1836 if (pickups.empty() || deliveries.empty()) {
1839 const int64_t size =
Size();
1840 const int pair_index = pickup_delivery_pairs_.size();
1841 for (
int pickup_index = 0; pickup_index < pickups.size(); pickup_index++) {
1842 const int64_t pickup = pickups[pickup_index];
1844 index_to_pickup_index_pairs_[pickup].emplace_back(pair_index, pickup_index);
1846 for (
int delivery_index = 0; delivery_index < deliveries.size();
1848 const int64_t delivery = deliveries[delivery_index];
1850 index_to_delivery_index_pairs_[delivery].emplace_back(pair_index,
1853 pickup_delivery_pairs_.push_back({pickups, deliveries});
1857 int64_t node_index)
const {
1858 CHECK_LT(node_index, index_to_pickup_index_pairs_.size());
1859 return index_to_pickup_index_pairs_[node_index];
1863 int64_t node_index)
const {
1864 CHECK_LT(node_index, index_to_delivery_index_pairs_.size());
1865 return index_to_delivery_index_pairs_[node_index];
1871 vehicle_pickup_delivery_policy_[vehicle] = policy;
1877 for (
int i = 0; i < vehicles_; ++i) {
1885 return vehicle_pickup_delivery_policy_[vehicle];
1890 for (
int i = 0; i <
Nexts().size(); ++i) {
1900IntVar* RoutingModel::CreateSameVehicleCost(
int vehicle_index) {
1901 const std::vector<int64_t>& indices =
1902 same_vehicle_costs_[vehicle_index].indices;
1903 CHECK(!indices.empty());
1904 std::vector<IntVar*> vehicle_counts;
1905 solver_->MakeIntVarArray(vehicle_vars_.size() + 1, 0, indices.size() + 1,
1907 std::vector<int64_t> vehicle_values(vehicle_vars_.size() + 1);
1908 for (
int i = 0; i < vehicle_vars_.size(); ++i) {
1909 vehicle_values[i] = i;
1911 vehicle_values[vehicle_vars_.size()] = -1;
1912 std::vector<IntVar*> vehicle_vars;
1913 vehicle_vars.reserve(indices.size());
1914 for (
const int64_t
index : indices) {
1915 vehicle_vars.push_back(vehicle_vars_[
index]);
1917 solver_->AddConstraint(solver_->MakeDistribute(vehicle_vars, vehicle_counts));
1918 std::vector<IntVar*> vehicle_used;
1919 for (
int i = 0; i < vehicle_vars_.size() + 1; ++i) {
1920 vehicle_used.push_back(
1921 solver_->MakeIsGreaterOrEqualCstVar(vehicle_counts[i], 1));
1923 vehicle_used.push_back(solver_->MakeIntConst(-1));
1925 ->MakeProd(solver_->MakeMax(solver_->MakeSum(vehicle_used), 0),
1926 same_vehicle_costs_[vehicle_index].value)
1931 extra_operators_.push_back(ls_operator);
1940void RoutingModel::AppendHomogeneousArcCosts(
1942 std::vector<IntVar*>* cost_elements) {
1943 CHECK(cost_elements !=
nullptr);
1944 const auto arc_cost_evaluator = [
this, node_index](int64_t next_index) {
1951 IntVar*
const base_cost_var =
1953 solver_->AddConstraint(MakeLightElement(
1954 solver_.get(), base_cost_var, nexts_[node_index], arc_cost_evaluator,
1955 [
this]() { return enable_deep_serialization_; }));
1957 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1958 cost_elements->push_back(
var);
1960 IntExpr*
const expr =
1961 solver_->MakeElement(arc_cost_evaluator, nexts_[node_index]);
1962 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
1963 cost_elements->push_back(
var);
1967void RoutingModel::AppendArcCosts(
const RoutingSearchParameters&
parameters,
1969 std::vector<IntVar*>* cost_elements) {
1970 CHECK(cost_elements !=
nullptr);
1976 IntVar*
const base_cost_var =
1978 solver_->AddConstraint(MakeLightElement2(
1979 solver_.get(), base_cost_var, nexts_[node_index],
1980 vehicle_vars_[node_index],
1981 [
this, node_index](int64_t to, int64_t vehicle) {
1982 return GetArcCostForVehicle(node_index, to, vehicle);
1984 [
this]() { return enable_deep_serialization_; }));
1986 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1987 cost_elements->push_back(
var);
1989 IntVar*
const vehicle_class_var =
1992 [
this](int64_t
index) {
1993 return SafeGetCostClassInt64OfVehicle(
index);
1995 vehicle_vars_[node_index])
1997 IntExpr*
const expr = solver_->MakeElement(
2001 nexts_[node_index], vehicle_class_var);
2002 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
2003 cost_elements->push_back(
var);
2007int RoutingModel::GetVehicleStartClass(int64_t start_index)
const {
2008 const int vehicle = index_to_vehicle_[start_index];
2015std::string RoutingModel::FindErrorInSearchParametersForModel(
2016 const RoutingSearchParameters& search_parameters)
const {
2018 search_parameters.first_solution_strategy();
2019 if (GetFirstSolutionDecisionBuilder(search_parameters) ==
nullptr) {
2020 return absl::StrCat(
2021 "Undefined first solution strategy: ",
2023 " (int value: ", first_solution_strategy,
")");
2025 if (search_parameters.first_solution_strategy() ==
2028 return "Undefined sweep arranger for ROUTING_SWEEP strategy.";
2033void RoutingModel::QuietCloseModel() {
2044 same_vehicle_components_.SetNumberOfNodes(
model->Size());
2045 for (
const std::string&
name :
model->GetAllDimensionNames()) {
2047 const std::vector<IntVar*>& cumuls = dimension->
cumuls();
2048 for (
int i = 0; i < cumuls.size(); ++i) {
2049 cumul_to_dim_indices_[cumuls[i]] = {dimension, i};
2052 const std::vector<IntVar*>& vehicle_vars =
model->VehicleVars();
2053 for (
int i = 0; i < vehicle_vars.size(); ++i) {
2054 vehicle_var_to_indices_[vehicle_vars[i]] = i;
2056 RegisterInspectors();
2060 const std::vector<int> node_to_same_vehicle_component_id =
2061 same_vehicle_components_.GetComponentIds();
2062 model_->InitSameVehicleGroups(
2063 same_vehicle_components_.GetNumberOfComponents());
2064 for (
int node = 0; node < model_->Size(); ++node) {
2065 model_->SetSameVehicleGroup(node,
2066 node_to_same_vehicle_component_id[node]);
2072 const Constraint*
const constraint)
override {
2076 IntExpr*
const expr)
override {
2078 [](
const IntExpr* expr) {})(expr);
2081 const std::vector<int64_t>& values)
override {
2083 [](
const std::vector<int64_t>& int_array) {})(values);
2087 using ExprInspector = std::function<void(
const IntExpr*)>;
2088 using ArrayInspector = std::function<void(
const std::vector<int64_t>&)>;
2089 using ConstraintInspector = std::function<void()>;
2091 void RegisterInspectors() {
2092 expr_inspectors_[kExpressionArgument] = [
this](
const IntExpr* expr) {
2095 expr_inspectors_[kLeftArgument] = [
this](
const IntExpr* expr) {
2098 expr_inspectors_[kRightArgument] = [
this](
const IntExpr* expr) {
2101 array_inspectors_[kStartsArgument] =
2102 [
this](
const std::vector<int64_t>& int_array) {
2103 starts_argument_ = int_array;
2105 array_inspectors_[kEndsArgument] =
2106 [
this](
const std::vector<int64_t>& int_array) {
2107 ends_argument_ = int_array;
2109 constraint_inspectors_[kNotMember] = [
this]() {
2110 std::pair<RoutingDimension*, int> dim_index;
2113 const int index = dim_index.second;
2114 dimension->forbidden_intervals_[
index].InsertIntervals(starts_argument_,
2116 VLOG(2) << dimension->name() <<
" " <<
index <<
": "
2117 << dimension->forbidden_intervals_[
index].DebugString();
2120 starts_argument_.clear();
2121 ends_argument_.clear();
2123 constraint_inspectors_[kEquality] = [
this]() {
2125 int right_index = 0;
2126 if (
gtl::FindCopy(vehicle_var_to_indices_, left_, &left_index) &&
2127 gtl::FindCopy(vehicle_var_to_indices_, right_, &right_index)) {
2128 VLOG(2) <<
"Vehicle variables for " << left_index <<
" and "
2129 << right_index <<
" are equal.";
2130 same_vehicle_components_.AddEdge(left_index, right_index);
2135 constraint_inspectors_[kLessOrEqual] = [
this]() {
2136 std::pair<RoutingDimension*, int> left_index;
2137 std::pair<RoutingDimension*, int> right_index;
2138 if (
gtl::FindCopy(cumul_to_dim_indices_, left_, &left_index) &&
2139 gtl::FindCopy(cumul_to_dim_indices_, right_, &right_index)) {
2141 if (dimension == right_index.first) {
2142 VLOG(2) <<
"For dimension " << dimension->name() <<
", cumul for "
2143 << left_index.second <<
" is less than " << right_index.second
2145 dimension->path_precedence_graph_.AddArc(left_index.second,
2146 right_index.second);
2156 absl::flat_hash_map<const IntExpr*, std::pair<RoutingDimension*, int>>
2157 cumul_to_dim_indices_;
2158 absl::flat_hash_map<const IntExpr*, int> vehicle_var_to_indices_;
2159 absl::flat_hash_map<std::string, ExprInspector> expr_inspectors_;
2160 absl::flat_hash_map<std::string, ArrayInspector> array_inspectors_;
2161 absl::flat_hash_map<std::string, ConstraintInspector> constraint_inspectors_;
2162 const IntExpr*
expr_ =
nullptr;
2163 const IntExpr* left_ =
nullptr;
2164 const IntExpr* right_ =
nullptr;
2165 std::vector<int64_t> starts_argument_;
2166 std::vector<int64_t> ends_argument_;
2169void RoutingModel::DetectImplicitPickupAndDeliveries() {
2170 std::vector<int> non_pickup_delivery_nodes;
2171 for (
int node = 0; node <
Size(); ++node) {
2174 non_pickup_delivery_nodes.push_back(node);
2178 std::set<std::pair<int64_t, int64_t>> implicit_pickup_deliveries;
2180 if (dimension->class_evaluators_.size() != 1) {
2185 if (transit ==
nullptr)
continue;
2186 absl::flat_hash_map<int64_t, std::vector<int64_t>> nodes_by_positive_demand;
2187 absl::flat_hash_map<int64_t, std::vector<int64_t>> nodes_by_negative_demand;
2188 for (
int node : non_pickup_delivery_nodes) {
2189 const int64_t
demand = transit(node);
2191 nodes_by_positive_demand[
demand].push_back(node);
2193 nodes_by_negative_demand[-
demand].push_back(node);
2196 for (
const auto& [
demand, positive_nodes] : nodes_by_positive_demand) {
2197 const std::vector<int64_t>*
const negative_nodes =
2199 if (negative_nodes !=
nullptr) {
2200 for (int64_t positive_node : positive_nodes) {
2201 for (int64_t negative_node : *negative_nodes) {
2202 implicit_pickup_deliveries.insert({positive_node, negative_node});
2208 implicit_pickup_delivery_pairs_without_alternatives_.clear();
2209 for (
auto [pickup, delivery] : implicit_pickup_deliveries) {
2210 implicit_pickup_delivery_pairs_without_alternatives_.emplace_back(
2211 std::vector<int64_t>({pickup}), std::vector<int64_t>({delivery}));
2218 if (!error.empty()) {
2220 LOG(
ERROR) <<
"Invalid RoutingSearchParameters: " << error;
2230 dimension->CloseModel(UsesLightPropagation(
parameters));
2233 dimension_resource_group_indices_.resize(dimensions_.size());
2234 for (
int rg_index = 0; rg_index < resource_groups_.size(); rg_index++) {
2237 dimension_resource_group_indices_[dim_index].push_back(rg_index);
2242 ComputeVehicleClasses();
2243 ComputeVehicleTypes();
2244 FinalizeVisitTypes();
2245 vehicle_start_class_callback_ = [
this](int64_t start) {
2246 return GetVehicleStartClass(start);
2249 AddNoCycleConstraintInternal();
2251 const int size =
Size();
2254 for (
int i = 0; i < vehicles_; ++i) {
2255 const int64_t start = starts_[i];
2256 const int64_t end = ends_[i];
2257 solver_->AddConstraint(
2258 solver_->MakeEquality(vehicle_vars_[start], solver_->MakeIntConst(i)));
2259 solver_->AddConstraint(
2260 solver_->MakeEquality(vehicle_vars_[end], solver_->MakeIntConst(i)));
2261 solver_->AddConstraint(
2262 solver_->MakeIsDifferentCstCt(nexts_[start], end, vehicle_active_[i]));
2263 if (consider_empty_route_costs_[i]) {
2264 vehicle_costs_considered_[i]->SetMin(1);
2266 solver_->AddConstraint(solver_->MakeEquality(
2267 vehicle_active_[i], vehicle_costs_considered_[i]));
2272 if (vehicles_ > max_active_vehicles_) {
2273 solver_->AddConstraint(
2274 solver_->MakeSumLessOrEqual(vehicle_active_, max_active_vehicles_));
2282 if (vehicles_ > 1) {
2283 std::vector<IntVar*> zero_transit(size, solver_->MakeIntConst(0));
2284 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
2285 nexts_, active_, vehicle_vars_, zero_transit));
2290 for (
int i = 0; i < size; ++i) {
2292 active_[i]->SetValue(1);
2298 const absl::flat_hash_set<VisitTypePolicy>*
const infeasible_policies =
2300 if (infeasible_policies !=
nullptr &&
2302 active_[i]->SetValue(0);
2307 for (
int i = 0; i < allowed_vehicles_.size(); ++i) {
2308 const auto& allowed_vehicles = allowed_vehicles_[i];
2309 if (!allowed_vehicles.empty()) {
2311 vehicles.reserve(allowed_vehicles.size() + 1);
2313 for (
int vehicle : allowed_vehicles) {
2321 for (
int i = 0; i < size; ++i) {
2323 solver_->AddConstraint(solver_->RevAlloc(
2324 new DifferentFromValues(solver_.get(), nexts_[i], starts_)));
2326 solver_->AddConstraint(
2327 solver_->MakeIsDifferentCstCt(nexts_[i], i, active_[i]));
2332 for (
int i = 0; i < size; ++i) {
2333 solver_->AddConstraint(
2334 solver_->MakeIsDifferentCstCt(vehicle_vars_[i], -1, active_[i]));
2338 solver_->AddConstraint(
2343 for (
int i = 0; i < vehicles_; ++i) {
2344 std::vector<int64_t> forbidden_ends;
2345 forbidden_ends.reserve(vehicles_ - 1);
2346 for (
int j = 0; j < vehicles_; ++j) {
2348 forbidden_ends.push_back(ends_[j]);
2351 solver_->AddConstraint(solver_->RevAlloc(
new DifferentFromValues(
2352 solver_.get(), nexts_[starts_[i]], std::move(forbidden_ends))));
2356 for (
const int64_t end : ends_) {
2357 is_bound_to_end_[end]->SetValue(1);
2360 std::vector<IntVar*> cost_elements;
2362 if (vehicles_ > 0) {
2363 for (
int node_index = 0; node_index < size; ++node_index) {
2365 AppendHomogeneousArcCosts(
parameters, node_index, &cost_elements);
2367 AppendArcCosts(
parameters, node_index, &cost_elements);
2370 if (vehicle_amortized_cost_factors_set_) {
2371 std::vector<IntVar*> route_lengths;
2372 solver_->MakeIntVarArray(vehicles_, 0, size, &route_lengths);
2373 solver_->AddConstraint(
2374 solver_->MakeDistribute(vehicle_vars_, route_lengths));
2375 std::vector<IntVar*> vehicle_used;
2376 for (
int i = 0; i < vehicles_; i++) {
2378 vehicle_used.push_back(
2379 solver_->MakeIsGreaterCstVar(route_lengths[i], 2));
2382 ->MakeProd(solver_->MakeOpposite(solver_->MakeSquare(
2383 solver_->MakeSum(route_lengths[i], -2))),
2384 quadratic_cost_factor_of_vehicle_[i])
2386 cost_elements.push_back(
var);
2388 IntVar*
const vehicle_usage_cost =
2389 solver_->MakeScalProd(vehicle_used, linear_cost_factor_of_vehicle_)
2391 cost_elements.push_back(vehicle_usage_cost);
2396 dimension->SetupGlobalSpanCost(&cost_elements);
2397 dimension->SetupSlackAndDependentTransitCosts();
2398 const std::vector<int64_t>& span_costs =
2399 dimension->vehicle_span_cost_coefficients();
2400 const std::vector<int64_t>& span_ubs =
2401 dimension->vehicle_span_upper_bounds();
2402 const bool has_span_constraint =
2403 std::any_of(span_costs.begin(), span_costs.end(),
2404 [](int64_t coeff) { return coeff != 0; }) ||
2405 std::any_of(span_ubs.begin(), span_ubs.end(),
2407 return value < std::numeric_limits<int64_t>::max();
2409 dimension->HasSoftSpanUpperBounds() ||
2410 dimension->HasQuadraticCostSoftSpanUpperBounds();
2411 if (has_span_constraint) {
2412 std::vector<IntVar*> spans(
vehicles(),
nullptr);
2413 std::vector<IntVar*> total_slacks(
vehicles(),
nullptr);
2415 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2417 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2419 if (span_costs[vehicle] != 0) {
2420 total_slacks[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2423 if (dimension->HasSoftSpanUpperBounds()) {
2424 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2425 if (spans[vehicle])
continue;
2427 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2428 if (bound_cost.
cost == 0)
continue;
2429 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2432 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2433 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2434 if (spans[vehicle])
continue;
2436 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2437 if (bound_cost.
cost == 0)
continue;
2438 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2441 solver_->AddConstraint(
2445 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2446 if (!spans[vehicle] && !total_slacks[vehicle])
continue;
2447 if (spans[vehicle]) {
2457 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2458 if (span_costs[vehicle] == 0)
continue;
2459 DCHECK(total_slacks[vehicle] !=
nullptr);
2460 IntVar*
const slack_amount =
2462 ->MakeProd(vehicle_costs_considered_[vehicle],
2463 total_slacks[vehicle])
2465 IntVar*
const slack_cost =
2466 solver_->MakeProd(slack_amount, span_costs[vehicle])->Var();
2467 cost_elements.push_back(slack_cost);
2469 span_costs[vehicle]);
2471 if (dimension->HasSoftSpanUpperBounds()) {
2472 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2473 const auto bound_cost =
2474 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2475 if (bound_cost.cost == 0 ||
2478 DCHECK(spans[vehicle] !=
nullptr);
2481 IntVar*
const span_violation_amount =
2484 vehicle_costs_considered_[vehicle],
2486 solver_->MakeSum(spans[vehicle], -bound_cost.bound),
2489 IntVar*
const span_violation_cost =
2490 solver_->MakeProd(span_violation_amount, bound_cost.cost)->Var();
2491 cost_elements.push_back(span_violation_cost);
2496 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2497 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
2498 const auto bound_cost =
2499 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2500 if (bound_cost.cost == 0 ||
2503 DCHECK(spans[vehicle] !=
nullptr);
2506 IntExpr* max0 = solver_->MakeMax(
2507 solver_->MakeSum(spans[vehicle], -bound_cost.bound), 0);
2508 IntVar*
const squared_span_violation_amount =
2510 ->MakeProd(vehicle_costs_considered_[vehicle],
2511 solver_->MakeSquare(max0))
2513 IntVar*
const span_violation_cost =
2514 solver_->MakeProd(squared_span_violation_amount, bound_cost.cost)
2516 cost_elements.push_back(span_violation_cost);
2525 IntVar* penalty_var = CreateDisjunction(i);
2526 if (penalty_var !=
nullptr) {
2527 cost_elements.push_back(penalty_var);
2532 dimension->SetupCumulVarSoftLowerBoundCosts(&cost_elements);
2533 dimension->SetupCumulVarSoftUpperBoundCosts(&cost_elements);
2534 dimension->SetupCumulVarPiecewiseLinearCosts(&cost_elements);
2537 for (
int i = 0; i < same_vehicle_costs_.size(); ++i) {
2538 cost_elements.push_back(CreateSameVehicleCost(i));
2540 cost_ = solver_->MakeSum(cost_elements)->Var();
2541 cost_->set_name(
"Cost");
2544 std::vector<std::pair<int, int>> pickup_delivery_precedences;
2545 for (
const auto& pair : pickup_delivery_pairs_) {
2546 DCHECK(!pair.first.empty() && !pair.second.empty());
2547 for (
int pickup : pair.first) {
2548 for (
int delivery : pair.second) {
2549 pickup_delivery_precedences.emplace_back(pickup, delivery);
2553 std::vector<int> lifo_vehicles;
2554 std::vector<int> fifo_vehicles;
2555 for (
int i = 0; i < vehicles_; ++i) {
2556 switch (vehicle_pickup_delivery_policy_[i]) {
2560 lifo_vehicles.push_back(
Start(i));
2563 fifo_vehicles.push_back(
Start(i));
2567 solver_->AddConstraint(solver_->MakePathPrecedenceConstraint(
2568 nexts_, pickup_delivery_precedences, lifo_vehicles, fifo_vehicles));
2571 enable_deep_serialization_ =
false;
2572 std::unique_ptr<RoutingModelInspector> inspector(
2574 solver_->Accept(inspector.get());
2575 enable_deep_serialization_ =
true;
2581 dimension->GetPathPrecedenceGraph();
2582 std::vector<std::pair<int, int>> path_precedences;
2584 for (
const auto head : graph[
tail]) {
2585 path_precedences.emplace_back(
tail,
head);
2588 if (!path_precedences.empty()) {
2589 solver_->AddConstraint(solver_->MakePathTransitPrecedenceConstraint(
2590 nexts_, dimension->transits(), path_precedences));
2595 dimension->GetNodePrecedences()) {
2596 const int64_t first_node = node_precedence.first_node;
2597 const int64_t second_node = node_precedence.second_node;
2598 IntExpr*
const nodes_are_selected =
2599 solver_->MakeMin(active_[first_node], active_[second_node]);
2600 IntExpr*
const cumul_difference = solver_->MakeDifference(
2601 dimension->CumulVar(second_node), dimension->CumulVar(first_node));
2602 IntVar*
const cumul_difference_is_ge_offset =
2603 solver_->MakeIsGreaterOrEqualCstVar(cumul_difference,
2604 node_precedence.offset);
2607 solver_->AddConstraint(solver_->MakeLessOrEqual(
2608 nodes_are_selected->
Var(), cumul_difference_is_ge_offset));
2612 DetectImplicitPickupAndDeliveries();
2621 CreateFirstSolutionDecisionBuilders(
parameters);
2622 error = FindErrorInSearchParametersForModel(
parameters);
2623 if (!error.empty()) {
2625 LOG(
ERROR) <<
"Invalid RoutingSearchParameters for this model: " << error;
2632 monitors_.push_back(monitor);
2640 bool AtSolution()
override {
2646 std::function<void()> callback_;
2652 new AtSolutionCallbackMonitor(solver_.get(), std::move(
callback))));
2662 std::vector<const Assignment*>* solutions) {
2668 if (!
parameters.has_time_limit())
return absl::InfiniteDuration();
2672absl::Duration GetLnsTimeLimit(
const RoutingSearchParameters&
parameters) {
2673 if (!
parameters.has_lns_time_limit())
return absl::InfiniteDuration();
2681 Assignment* assignment) {
2682 assignment->Clear();
2683 for (
int i = 0; i <
model->Nexts().size(); ++i) {
2684 if (!
model->IsStart(i)) {
2685 assignment->Add(
model->NextVar(i))->SetValue(i);
2688 for (
int vehicle = 0; vehicle <
model->vehicles(); ++vehicle) {
2689 assignment->Add(
model->NextVar(
model->Start(vehicle)))
2690 ->SetValue(
model->End(vehicle));
2695bool RoutingModel::AppendAssignmentIfFeasible(
2696 const Assignment& assignment,
2697 std::vector<std::unique_ptr<Assignment>>* assignments) {
2698 tmp_assignment_->CopyIntersection(&assignment);
2699 solver_->Solve(restore_tmp_assignment_, collect_one_assignment_,
2700 GetOrCreateLimit());
2701 if (collect_one_assignment_->solution_count() == 1) {
2702 assignments->push_back(absl::make_unique<Assignment>(solver_.get()));
2703 assignments->back()->Copy(collect_one_assignment_->solution(0));
2709void RoutingModel::LogSolution(
const RoutingSearchParameters&
parameters,
2710 const std::string& description,
2711 int64_t solution_cost, int64_t start_time_ms) {
2713 const double cost_scaling_factor =
parameters.log_cost_scaling_factor();
2714 const double cost_offset =
parameters.log_cost_offset();
2715 const std::string cost_string =
2716 cost_scaling_factor == 1.0 && cost_offset == 0.0
2717 ? absl::StrCat(solution_cost)
2719 "%d (%.8lf)", solution_cost,
2720 cost_scaling_factor * (solution_cost + cost_offset));
2722 "%s (%s, time = %d ms, memory used = %s)", description, cost_string,
2723 solver_->wall_time() - start_time_ms, memory_str);
2728 std::vector<const Assignment*>* solutions) {
2734 const std::vector<const Assignment*>& assignments,
2736 std::vector<const Assignment*>* solutions) {
2737 const int64_t start_time_ms = solver_->wall_time();
2740 if (solutions !=
nullptr) solutions->clear();
2744 const auto update_time_limits = [
this, start_time_ms, &
parameters]() {
2745 const absl::Duration elapsed_time =
2746 absl::Milliseconds(solver_->wall_time() - start_time_ms);
2747 const absl::Duration time_left = GetTimeLimit(
parameters) - elapsed_time;
2748 if (time_left >= absl::ZeroDuration()) {
2758 if (!update_time_limits()) {
2762 lns_limit_->UpdateLimits(
2770 const int time_limit_shares = 1 + !global_dimension_optimizers_.empty() +
2771 !local_dimension_optimizers_.empty();
2772 const absl::Duration first_solution_lns_time_limit =
2775 first_solution_lns_limit_->UpdateLimits(
2779 std::vector<std::unique_ptr<Assignment>> solution_pool;
2780 std::vector<const Assignment*> first_solution_assignments;
2781 for (
const Assignment* assignment : assignments) {
2782 if (assignment !=
nullptr) first_solution_assignments.push_back(assignment);
2785 if (first_solution_assignments.empty()) {
2786 bool solution_found =
false;
2789 AppendAssignmentIfFeasible(matching, &solution_pool)) {
2791 LogSolution(
parameters,
"Min-Cost Flow Solution",
2792 solution_pool.back()->ObjectiveValue(), start_time_ms);
2794 solution_found =
true;
2796 if (!solution_found) {
2800 MakeAllUnperformedInAssignment(
this, &unperformed);
2801 if (AppendAssignmentIfFeasible(unperformed, &solution_pool) &&
2803 LogSolution(
parameters,
"All Unperformed Solution",
2804 solution_pool.back()->ObjectiveValue(), start_time_ms);
2806 if (update_time_limits()) {
2807 solver_->Solve(solve_db_, monitors_);
2811 for (
const Assignment* assignment : first_solution_assignments) {
2812 assignment_->CopyIntersection(assignment);
2813 solver_->Solve(improve_db_, monitors_);
2814 if (collect_assignments_->solution_count() >= 1 ||
2815 !update_time_limits()) {
2824 const int solution_count = collect_assignments_->solution_count();
2826 solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
2830 AppendAssignmentIfFeasible(sat_solution, &solution_pool) &&
2832 LogSolution(
parameters,
"SAT", solution_pool.back()->ObjectiveValue(),
2837 const absl::Duration elapsed_time =
2838 absl::Milliseconds(solver_->wall_time() - start_time_ms);
2839 const int solution_count = collect_assignments_->solution_count();
2840 if (solution_count >= 1 || !solution_pool.empty()) {
2842 if (solutions !=
nullptr) {
2843 for (
int i = 0; i < solution_count; ++i) {
2844 solutions->push_back(
2845 solver_->MakeAssignment(collect_assignments_->solution(i)));
2847 for (
const auto& solution : solution_pool) {
2848 if (solutions->empty() ||
2849 solution->ObjectiveValue() < solutions->back()->ObjectiveValue()) {
2850 solutions->push_back(solver_->MakeAssignment(solution.get()));
2853 return solutions->back();
2856 solution_count >= 1 ? collect_assignments_->solution(solution_count - 1)
2858 for (
const auto& solution : solution_pool) {
2859 if (best_assignment ==
nullptr ||
2861 best_assignment = solution.get();
2864 return solver_->MakeAssignment(best_assignment);
2866 if (elapsed_time >= GetTimeLimit(
parameters)) {
2878 const int size =
Size();
2884 source_model->
Nexts());
2886 std::vector<IntVar*> source_vars(size + size + vehicles_);
2887 std::vector<IntVar*> target_vars(size + size + vehicles_);
2897 source_assignment, source_vars);
2915 LOG(
WARNING) <<
"Non-closed model not supported.";
2919 LOG(
WARNING) <<
"Non-homogeneous vehicle costs not supported";
2922 if (!disjunctions_.empty()) {
2924 <<
"Node disjunction constraints or optional nodes not supported.";
2927 const int num_nodes =
Size() + vehicles_;
2934 std::unique_ptr<IntVarIterator> iterator(
2935 nexts_[
tail]->MakeDomainIterator(
false));
2958 return linear_sum_assignment.
GetCost();
2963bool RoutingModel::RouteCanBeUsedByVehicle(
const Assignment& assignment,
2964 int start_index,
int vehicle)
const {
2966 IsStart(start_index) ?
Next(assignment, start_index) : start_index;
2967 while (!
IsEnd(current_index)) {
2969 if (!vehicle_var->
Contains(vehicle)) {
2972 const int next_index =
Next(assignment, current_index);
2973 CHECK_NE(next_index, current_index) <<
"Inactive node inside a route";
2974 current_index = next_index;
2979bool RoutingModel::ReplaceUnusedVehicle(
2980 int unused_vehicle,
int active_vehicle,
2981 Assignment*
const compact_assignment)
const {
2982 CHECK(compact_assignment !=
nullptr);
2986 const int unused_vehicle_start =
Start(unused_vehicle);
2987 IntVar*
const unused_vehicle_start_var =
NextVar(unused_vehicle_start);
2988 const int unused_vehicle_end =
End(unused_vehicle);
2989 const int active_vehicle_start =
Start(active_vehicle);
2990 const int active_vehicle_end =
End(active_vehicle);
2991 IntVar*
const active_vehicle_start_var =
NextVar(active_vehicle_start);
2992 const int active_vehicle_next =
2993 compact_assignment->Value(active_vehicle_start_var);
2994 compact_assignment->SetValue(unused_vehicle_start_var, active_vehicle_next);
2995 compact_assignment->SetValue(active_vehicle_start_var,
End(active_vehicle));
2998 int current_index = active_vehicle_next;
2999 while (!
IsEnd(current_index)) {
3000 IntVar*
const vehicle_var =
VehicleVar(current_index);
3001 compact_assignment->SetValue(vehicle_var, unused_vehicle);
3002 const int next_index =
Next(*compact_assignment, current_index);
3003 if (
IsEnd(next_index)) {
3004 IntVar*
const last_next_var =
NextVar(current_index);
3005 compact_assignment->SetValue(last_next_var,
End(unused_vehicle));
3007 current_index = next_index;
3012 const std::vector<IntVar*>& transit_variables = dimension->transits();
3013 IntVar*
const unused_vehicle_transit_var =
3014 transit_variables[unused_vehicle_start];
3015 IntVar*
const active_vehicle_transit_var =
3016 transit_variables[active_vehicle_start];
3017 const bool contains_unused_vehicle_transit_var =
3018 compact_assignment->Contains(unused_vehicle_transit_var);
3019 const bool contains_active_vehicle_transit_var =
3020 compact_assignment->Contains(active_vehicle_transit_var);
3021 if (contains_unused_vehicle_transit_var !=
3022 contains_active_vehicle_transit_var) {
3024 LOG(
INFO) <<
"The assignment contains transit variable for dimension '"
3025 << dimension->name() <<
"' for some vehicles, but not for all";
3028 if (contains_unused_vehicle_transit_var) {
3029 const int64_t old_unused_vehicle_transit =
3030 compact_assignment->Value(unused_vehicle_transit_var);
3031 const int64_t old_active_vehicle_transit =
3032 compact_assignment->Value(active_vehicle_transit_var);
3033 compact_assignment->SetValue(unused_vehicle_transit_var,
3034 old_active_vehicle_transit);
3035 compact_assignment->SetValue(active_vehicle_transit_var,
3036 old_unused_vehicle_transit);
3040 const std::vector<IntVar*>& cumul_variables = dimension->cumuls();
3041 IntVar*
const unused_vehicle_cumul_var =
3042 cumul_variables[unused_vehicle_end];
3043 IntVar*
const active_vehicle_cumul_var =
3044 cumul_variables[active_vehicle_end];
3045 const int64_t old_unused_vehicle_cumul =
3046 compact_assignment->Value(unused_vehicle_cumul_var);
3047 const int64_t old_active_vehicle_cumul =
3048 compact_assignment->Value(active_vehicle_cumul_var);
3049 compact_assignment->SetValue(unused_vehicle_cumul_var,
3050 old_active_vehicle_cumul);
3051 compact_assignment->SetValue(active_vehicle_cumul_var,
3052 old_unused_vehicle_cumul);
3059 return CompactAssignmentInternal(assignment,
false);
3064 return CompactAssignmentInternal(assignment,
true);
3067Assignment* RoutingModel::CompactAssignmentInternal(
3068 const Assignment& assignment,
bool check_compact_assignment)
const {
3072 <<
"The costs are not homogeneous, routes cannot be rearranged";
3076 std::unique_ptr<Assignment> compact_assignment(
new Assignment(&assignment));
3077 for (
int vehicle = 0; vehicle < vehicles_ - 1; ++vehicle) {
3081 const int vehicle_start =
Start(vehicle);
3082 const int vehicle_end =
End(vehicle);
3084 int swap_vehicle = vehicles_ - 1;
3085 bool has_more_vehicles_with_route =
false;
3086 for (; swap_vehicle > vehicle; --swap_vehicle) {
3093 has_more_vehicles_with_route =
true;
3094 const int swap_vehicle_start =
Start(swap_vehicle);
3095 const int swap_vehicle_end =
End(swap_vehicle);
3096 if (manager_.IndexToNode(vehicle_start) !=
3097 manager_.IndexToNode(swap_vehicle_start) ||
3098 manager_.IndexToNode(vehicle_end) !=
3099 manager_.IndexToNode(swap_vehicle_end)) {
3104 if (RouteCanBeUsedByVehicle(*compact_assignment, swap_vehicle_start,
3110 if (swap_vehicle == vehicle) {
3111 if (has_more_vehicles_with_route) {
3115 LOG(
INFO) <<
"No vehicle that can be swapped with " << vehicle
3122 if (!ReplaceUnusedVehicle(vehicle, swap_vehicle,
3123 compact_assignment.get())) {
3128 if (check_compact_assignment &&
3129 !solver_->CheckAssignment(compact_assignment.get())) {
3131 LOG(
WARNING) <<
"The compacted assignment is not a valid solution";
3134 return compact_assignment.release();
3137int RoutingModel::FindNextActive(
int index,
3138 const std::vector<int64_t>& indices)
const {
3141 const int size = indices.size();
3152 preassignment_->Clear();
3153 IntVar* next_var =
nullptr;
3154 int lock_index = FindNextActive(-1, locks);
3155 const int size = locks.size();
3156 if (lock_index < size) {
3157 next_var =
NextVar(locks[lock_index]);
3158 preassignment_->Add(next_var);
3159 for (lock_index = FindNextActive(lock_index, locks); lock_index < size;
3160 lock_index = FindNextActive(lock_index, locks)) {
3161 preassignment_->SetValue(next_var, locks[lock_index]);
3162 next_var =
NextVar(locks[lock_index]);
3163 preassignment_->Add(next_var);
3170 const std::vector<std::vector<int64_t>>& locks,
bool close_routes) {
3171 preassignment_->Clear();
3178 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3186 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3192 if (collect_assignments_->solution_count() == 1 && assignment_ !=
nullptr) {
3193 assignment_->CopyIntersection(collect_assignments_->solution(0));
3194 return assignment_->Save(file_name);
3202 CHECK(assignment_ !=
nullptr);
3203 if (assignment_->Load(file_name)) {
3204 return DoRestoreAssignment();
3211 CHECK(assignment_ !=
nullptr);
3212 assignment_->CopyIntersection(&solution);
3213 return DoRestoreAssignment();
3216Assignment* RoutingModel::DoRestoreAssignment() {
3220 solver_->Solve(restore_assignment_, monitors_);
3221 if (collect_assignments_->solution_count() == 1) {
3223 return collect_assignments_->solution(0);
3232 const std::vector<std::vector<int64_t>>& routes,
3233 bool ignore_inactive_indices,
bool close_routes,
3235 CHECK(assignment !=
nullptr);
3237 LOG(
ERROR) <<
"The model is not closed yet";
3240 const int num_routes = routes.size();
3241 if (num_routes > vehicles_) {
3242 LOG(
ERROR) <<
"The number of vehicles in the assignment (" << routes.size()
3243 <<
") is greater than the number of vehicles in the model ("
3244 << vehicles_ <<
")";
3248 absl::flat_hash_set<int> visited_indices;
3250 for (
int vehicle = 0; vehicle < num_routes; ++vehicle) {
3251 const std::vector<int64_t>& route = routes[vehicle];
3252 int from_index =
Start(vehicle);
3253 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3254 visited_indices.insert(from_index);
3255 if (!insert_result.second) {
3256 LOG(
ERROR) <<
"Index " << from_index <<
" (start node for vehicle "
3257 << vehicle <<
") was already used";
3261 for (
const int64_t to_index : route) {
3262 if (to_index < 0 || to_index >=
Size()) {
3263 LOG(
ERROR) <<
"Invalid index: " << to_index;
3268 if (active_var->
Max() == 0) {
3269 if (ignore_inactive_indices) {
3272 LOG(
ERROR) <<
"Index " << to_index <<
" is not active";
3277 insert_result = visited_indices.insert(to_index);
3278 if (!insert_result.second) {
3279 LOG(
ERROR) <<
"Index " << to_index <<
" is used multiple times";
3284 if (!vehicle_var->
Contains(vehicle)) {
3285 LOG(
ERROR) <<
"Vehicle " << vehicle <<
" is not allowed at index "
3291 if (!assignment->
Contains(from_var)) {
3292 assignment->
Add(from_var);
3294 assignment->
SetValue(from_var, to_index);
3296 from_index = to_index;
3301 if (!assignment->
Contains(last_var)) {
3302 assignment->
Add(last_var);
3309 for (
int vehicle = num_routes; vehicle < vehicles_; ++vehicle) {
3310 const int start_index =
Start(vehicle);
3313 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3314 visited_indices.insert(start_index);
3315 if (!insert_result.second) {
3316 LOG(
ERROR) <<
"Index " << start_index <<
" is used multiple times";
3321 if (!assignment->
Contains(start_var)) {
3322 assignment->
Add(start_var);
3333 if (!assignment->
Contains(next_var)) {
3334 assignment->
Add(next_var);
3345 const std::vector<std::vector<int64_t>>& routes,
3346 bool ignore_inactive_indices) {
3354 return DoRestoreAssignment();
3359 std::vector<std::vector<int64_t>>*
const routes)
const {
3361 CHECK(routes !=
nullptr);
3363 const int model_size =
Size();
3364 routes->resize(vehicles_);
3365 for (
int vehicle = 0; vehicle < vehicles_; ++vehicle) {
3366 std::vector<int64_t>*
const vehicle_route = &routes->at(vehicle);
3367 vehicle_route->clear();
3369 int num_visited_indices = 0;
3370 const int first_index =
Start(vehicle);
3374 int current_index = assignment.
Value(first_var);
3375 while (!
IsEnd(current_index)) {
3376 vehicle_route->push_back(current_index);
3381 current_index = assignment.
Value(next_var);
3383 ++num_visited_indices;
3384 CHECK_LE(num_visited_indices, model_size)
3385 <<
"The assignment contains a cycle";
3393 std::vector<std::vector<int64_t>> route_indices(
vehicles());
3394 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3396 LOG(DFATAL) <<
"GetRoutesFromAssignment() called on incomplete solution:"
3397 <<
" NextVar(" << vehicle <<
") is unbound.";
3400 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3402 route_indices[vehicle].push_back(
index);
3405 route_indices[vehicle].push_back(
index);
3408 return route_indices;
3412int64_t RoutingModel::GetArcCostForClassInternal(
3413 int64_t from_index, int64_t to_index,
3417 DCHECK_LT(cost_class_index, cost_classes_.size());
3418 CostCacheElement*
const cache = &cost_cache_[from_index];
3420 if (cache->index ==
static_cast<int>(to_index) &&
3421 cache->cost_class_index == cost_class_index) {
3425 const CostClass& cost_class = cost_classes_[cost_class_index];
3426 const auto& evaluator = transit_evaluators_[cost_class.evaluator_index];
3428 cost =
CapAdd(evaluator(from_index, to_index),
3429 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3430 }
else if (!
IsEnd(to_index)) {
3434 evaluator(from_index, to_index),
3435 CapAdd(GetDimensionTransitCostSum(from_index, to_index, cost_class),
3436 fixed_cost_of_vehicle_[index_to_vehicle_[from_index]]));
3440 if (consider_empty_route_costs_[index_to_vehicle_[from_index]]) {
3442 CapAdd(evaluator(from_index, to_index),
3443 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3448 *cache = {
static_cast<int>(to_index), cost_class_index,
cost};
3457 int vehicle)
const {
3471 return assignment.
Value(next_var);
3475 int64_t vehicle)
const {
3476 if (from_index != to_index && vehicle >= 0) {
3477 return GetArcCostForClassInternal(from_index, to_index,
3485 int64_t from_index, int64_t to_index,
3486 int64_t cost_class_index)
const {
3487 if (from_index != to_index) {
3488 return GetArcCostForClassInternal(from_index, to_index,
3496 int64_t to_index)
const {
3500 if (!is_bound_to_end_ct_added_.Switched()) {
3503 std::vector<IntVar*> zero_transit(
Size(), solver_->MakeIntConst(0));
3504 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
3505 nexts_, active_, is_bound_to_end_, zero_transit));
3506 is_bound_to_end_ct_added_.Switch(solver_.get());
3508 if (is_bound_to_end_[to_index]->Min() == 1)
3514int64_t RoutingModel::GetDimensionTransitCostSum(
3515 int64_t i, int64_t j,
const CostClass& cost_class)
const {
3517 for (
const auto& evaluator_and_coefficient :
3518 cost_class.dimension_transit_evaluator_class_and_cost_coefficient) {
3519 DCHECK_GT(evaluator_and_coefficient.cost_coefficient, 0);
3522 CapProd(evaluator_and_coefficient.cost_coefficient,
3523 evaluator_and_coefficient.dimension->GetTransitValueFromClass(
3524 i, j, evaluator_and_coefficient.transit_evaluator_class)));
3540 const bool mandatory1 = active_[to1]->Min() == 1;
3541 const bool mandatory2 = active_[to2]->Min() == 1;
3543 if (mandatory1 != mandatory2)
return mandatory1;
3550 const int64_t src_vehicle = src_vehicle_var->
Max();
3551 if (src_vehicle_var->
Bound()) {
3558 mandatory1 ? to1_vehicle_var->
Bound() : (to1_vehicle_var->
Size() <= 2);
3560 mandatory2 ? to2_vehicle_var->
Bound() : (to2_vehicle_var->
Size() <= 2);
3563 if (bound1 != bound2)
return bound1;
3566 const int64_t vehicle1 = to1_vehicle_var->
Max();
3567 const int64_t vehicle2 = to2_vehicle_var->
Max();
3570 if ((vehicle1 == src_vehicle) != (vehicle2 == src_vehicle)) {
3571 return vehicle1 == src_vehicle;
3576 if (vehicle1 != src_vehicle)
return to1 < to2;
3587 const std::vector<IntVar*>& cumul_vars =
3589 IntVar*
const dim1 = cumul_vars[to1];
3590 IntVar*
const dim2 = cumul_vars[to2];
3593 if (dim1->
Max() != dim2->
Max())
return dim1->
Max() < dim2->
Max();
3602 const int64_t cost_class_index =
3603 SafeGetCostClassInt64OfVehicle(src_vehicle);
3604 const int64_t cost1 =
3607 const int64_t cost2 =
3610 if (cost1 != cost2)
return cost1 < cost2;
3617 if (num_vehicles1 != num_vehicles2)
return num_vehicles1 < num_vehicles2;
3627 DCHECK_EQ(index_to_visit_type_.size(), index_to_type_policy_.size());
3628 index_to_visit_type_[
index] = type;
3629 index_to_type_policy_[
index] = policy;
3630 num_visit_types_ =
std::max(num_visit_types_, type + 1);
3635 return index_to_visit_type_[
index];
3639 DCHECK_LT(type, single_nodes_of_type_.size());
3640 return single_nodes_of_type_[type];
3644 DCHECK_LT(type, pair_indices_of_type_.size());
3645 return pair_indices_of_type_[type];
3649 int64_t
index)
const {
3651 return index_to_type_policy_[
index];
3655 hard_incompatible_types_per_type_index_.resize(num_visit_types_);
3656 temporal_incompatible_types_per_type_index_.resize(num_visit_types_);
3657 same_vehicle_required_type_alternatives_per_type_index_.resize(
3659 required_type_alternatives_when_adding_type_index_.resize(num_visit_types_);
3660 required_type_alternatives_when_removing_type_index_.resize(num_visit_types_);
3665 hard_incompatible_types_per_type_index_.size());
3666 has_hard_type_incompatibilities_ =
true;
3668 hard_incompatible_types_per_type_index_[type1].insert(type2);
3669 hard_incompatible_types_per_type_index_[type2].insert(type1);
3674 temporal_incompatible_types_per_type_index_.size());
3675 has_temporal_type_incompatibilities_ =
true;
3677 temporal_incompatible_types_per_type_index_[type1].insert(type2);
3678 temporal_incompatible_types_per_type_index_[type2].insert(type1);
3681const absl::flat_hash_set<int>&
3684 DCHECK_LT(type, hard_incompatible_types_per_type_index_.size());
3685 return hard_incompatible_types_per_type_index_[type];
3688const absl::flat_hash_set<int>&
3691 DCHECK_LT(type, temporal_incompatible_types_per_type_index_.size());
3692 return temporal_incompatible_types_per_type_index_[type];
3698 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
3700 same_vehicle_required_type_alternatives_per_type_index_.size());
3702 if (required_type_alternatives.empty()) {
3706 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
3707 trivially_infeasible_visit_types_to_policies_[dependent_type];
3714 has_same_vehicle_type_requirements_ =
true;
3715 same_vehicle_required_type_alternatives_per_type_index_[dependent_type]
3716 .push_back(std::move(required_type_alternatives));
3720 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
3722 required_type_alternatives_when_adding_type_index_.size());
3724 if (required_type_alternatives.empty()) {
3728 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
3729 trivially_infeasible_visit_types_to_policies_[dependent_type];
3735 has_temporal_type_requirements_ =
true;
3736 required_type_alternatives_when_adding_type_index_[dependent_type].push_back(
3737 std::move(required_type_alternatives));
3741 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
3743 required_type_alternatives_when_removing_type_index_.size());
3745 if (required_type_alternatives.empty()) {
3749 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
3750 trivially_infeasible_visit_types_to_policies_[dependent_type];
3757 has_temporal_type_requirements_ =
true;
3758 required_type_alternatives_when_removing_type_index_[dependent_type]
3759 .push_back(std::move(required_type_alternatives));
3762const std::vector<absl::flat_hash_set<int>>&
3766 same_vehicle_required_type_alternatives_per_type_index_.size());
3767 return same_vehicle_required_type_alternatives_per_type_index_[type];
3770const std::vector<absl::flat_hash_set<int>>&
3773 DCHECK_LT(type, required_type_alternatives_when_adding_type_index_.size());
3774 return required_type_alternatives_when_adding_type_index_[type];
3777const std::vector<absl::flat_hash_set<int>>&
3780 DCHECK_LT(type, required_type_alternatives_when_removing_type_index_.size());
3781 return required_type_alternatives_when_removing_type_index_[type];
3789 int64_t var_index)
const {
3790 if (active_[var_index]->Min() == 1)
3792 const std::vector<DisjunctionIndex>& disjunction_indices =
3794 if (disjunction_indices.size() != 1)
return default_value;
3799 return std::max(int64_t{0}, disjunctions_[disjunction_index].value.penalty);
3804 const std::string& dimension_to_print)
const {
3805 for (
int i = 0; i <
Size(); ++i) {
3808 <<
"DebugOutputVehicleSchedules() called on incomplete solution:"
3809 <<
" NextVar(" << i <<
") is unbound.";
3814 absl::flat_hash_set<std::string> dimension_names;
3815 if (dimension_to_print.empty()) {
3817 dimension_names.insert(all_dimension_names.begin(),
3818 all_dimension_names.end());
3820 dimension_names.insert(dimension_to_print);
3822 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3823 int empty_vehicle_range_start = vehicle;
3828 if (empty_vehicle_range_start != vehicle) {
3829 if (empty_vehicle_range_start == vehicle - 1) {
3830 absl::StrAppendFormat(&output,
"Vehicle %d: empty",
3831 empty_vehicle_range_start);
3833 absl::StrAppendFormat(&output,
"Vehicles %d-%d: empty",
3834 empty_vehicle_range_start, vehicle - 1);
3836 output.append(
"\n");
3839 absl::StrAppendFormat(&output,
"Vehicle %d:", vehicle);
3843 absl::StrAppendFormat(&output,
"%d Vehicle(%d) ",
index,
3844 solution_assignment.
Value(vehicle_var));
3848 absl::StrAppendFormat(&output,
"%s(%d..%d) ", dimension->name(),
3849 solution_assignment.
Min(
var),
3850 solution_assignment.
Max(
var));
3855 if (
IsEnd(
index)) output.append(
"Route end ");
3857 output.append(
"\n");
3860 output.append(
"Unperformed nodes: ");
3861 bool has_unperformed =
false;
3862 for (
int i = 0; i <
Size(); ++i) {
3865 absl::StrAppendFormat(&output,
"%d ", i);
3866 has_unperformed =
true;
3869 if (!has_unperformed) output.append(
"None");
3870 output.append(
"\n");
3875std::vector<std::vector<std::pair<int64_t, int64_t>>>
3878 std::vector<std::vector<std::pair<int64_t, int64_t>>> cumul_bounds(
3880 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
3882 LOG(DFATAL) <<
"GetCumulBounds() called on incomplete solution:"
3883 <<
" NextVar(" << vehicle <<
") is unbound.";
3887 for (
int vehicle_id = 0; vehicle_id <
vehicles(); ++vehicle_id) {
3890 cumul_bounds[vehicle_id].emplace_back(solution_assignment.
Min(dim_var),
3891 solution_assignment.
Max(dim_var));
3895 cumul_bounds[vehicle_id].emplace_back(solution_assignment.
Min(dim_var),
3896 solution_assignment.
Max(dim_var));
3899 return cumul_bounds;
3903Assignment* RoutingModel::GetOrCreateAssignment() {
3904 if (assignment_ ==
nullptr) {
3905 assignment_ = solver_->MakeAssignment();
3906 assignment_->Add(nexts_);
3908 assignment_->Add(vehicle_vars_);
3910 assignment_->AddObjective(cost_);
3915Assignment* RoutingModel::GetOrCreateTmpAssignment() {
3916 if (tmp_assignment_ ==
nullptr) {
3917 tmp_assignment_ = solver_->MakeAssignment();
3918 tmp_assignment_->Add(nexts_);
3920 return tmp_assignment_;
3923RegularLimit* RoutingModel::GetOrCreateLimit() {
3924 if (limit_ ==
nullptr) {
3925 limit_ = solver_->MakeLimit(
3933RegularLimit* RoutingModel::GetOrCreateLocalSearchLimit() {
3934 if (ls_limit_ ==
nullptr) {
3935 ls_limit_ = solver_->MakeLimit(absl::InfiniteDuration(),
3943RegularLimit* RoutingModel::GetOrCreateLargeNeighborhoodSearchLimit() {
3944 if (lns_limit_ ==
nullptr) {
3945 lns_limit_ = solver_->MakeLimit(
3954RoutingModel::GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit() {
3955 if (first_solution_lns_limit_ ==
nullptr) {
3956 first_solution_lns_limit_ = solver_->MakeLimit(
3961 return first_solution_lns_limit_;
3964LocalSearchOperator* RoutingModel::CreateInsertionOperator() {
3965 LocalSearchOperator* insertion_operator =
3966 CreateCPOperator<MakeActiveOperator>();
3967 if (!pickup_delivery_pairs_.empty()) {
3968 insertion_operator = solver_->ConcatenateOperators(
3969 {CreatePairOperator<MakePairActiveOperator>(), insertion_operator});
3971 if (!implicit_pickup_delivery_pairs_without_alternatives_.empty()) {
3972 insertion_operator = solver_->ConcatenateOperators(
3973 {CreateOperator<MakePairActiveOperator>(
3974 implicit_pickup_delivery_pairs_without_alternatives_),
3975 insertion_operator});
3977 return insertion_operator;
3980LocalSearchOperator* RoutingModel::CreateMakeInactiveOperator() {
3981 LocalSearchOperator* make_inactive_operator =
3982 CreateCPOperator<MakeInactiveOperator>();
3983 if (!pickup_delivery_pairs_.empty()) {
3984 make_inactive_operator = solver_->ConcatenateOperators(
3985 {CreatePairOperator<MakePairInactiveOperator>(),
3986 make_inactive_operator});
3988 return make_inactive_operator;
3991void RoutingModel::CreateNeighborhoodOperators(
3993 local_search_operators_.clear();
3994 local_search_operators_.resize(LOCAL_SEARCH_OPERATOR_COUNTER,
nullptr);
3998 std::pair<RoutingLocalSearchOperator, Solver::LocalSearchOperators>>
4003 for (
const auto [type, op] : operator_by_type) {
4004 local_search_operators_[type] =
4006 ? solver_->MakeOperator(nexts_, op)
4007 : solver_->MakeOperator(nexts_, vehicle_vars_, op);
4012 const std::vector<std::pair<RoutingLocalSearchOperator,
4014 operator_by_type = {{LIN_KERNIGHAN,
Solver::LK},
4017 for (
const auto [type, op] : operator_by_type) {
4020 local_search_operators_[type] =
4022 ? solver_->MakeOperator(nexts_, std::move(arc_cost), op)
4023 : solver_->MakeOperator(nexts_, vehicle_vars_,
4024 std::move(arc_cost), op);
4029 local_search_operators_[RELOCATE] = CreateCPOperator<Relocate>();
4030 local_search_operators_[EXCHANGE] = CreateCPOperator<Exchange>();
4031 local_search_operators_[CROSS] = CreateCPOperator<Cross>();
4032 local_search_operators_[TWO_OPT] = CreateCPOperator<TwoOpt>();
4033 local_search_operators_[RELOCATE_AND_MAKE_ACTIVE] =
4034 CreateCPOperator<RelocateAndMakeActiveOperator>();
4035 local_search_operators_[MAKE_ACTIVE_AND_RELOCATE] =
4036 CreateCPOperator<MakeActiveAndRelocate>();
4037 local_search_operators_[MAKE_CHAIN_INACTIVE] =
4038 CreateCPOperator<MakeChainInactiveOperator>();
4039 local_search_operators_[SWAP_ACTIVE] = CreateCPOperator<SwapActiveOperator>();
4040 local_search_operators_[EXTENDED_SWAP_ACTIVE] =
4041 CreateCPOperator<ExtendedSwapActiveOperator>();
4044 local_search_operators_[MAKE_ACTIVE] = CreateInsertionOperator();
4045 local_search_operators_[MAKE_INACTIVE] = CreateMakeInactiveOperator();
4046 local_search_operators_[RELOCATE_PAIR] =
4047 CreatePairOperator<PairRelocateOperator>();
4048 std::vector<LocalSearchOperator*> light_relocate_pair_operators;
4049 light_relocate_pair_operators.push_back(
4050 CreatePairOperator<LightPairRelocateOperator>());
4051 local_search_operators_[LIGHT_RELOCATE_PAIR] =
4052 solver_->ConcatenateOperators(light_relocate_pair_operators);
4053 local_search_operators_[EXCHANGE_PAIR] =
4054 CreatePairOperator<PairExchangeOperator>();
4055 local_search_operators_[EXCHANGE_RELOCATE_PAIR] =
4056 CreatePairOperator<PairExchangeRelocateOperator>();
4057 local_search_operators_[RELOCATE_NEIGHBORS] =
4058 CreateOperator<MakeRelocateNeighborsOperator>(
4060 local_search_operators_[NODE_PAIR_SWAP] = solver_->ConcatenateOperators(
4061 {CreatePairOperator<IndexPairSwapActiveOperator>(),
4062 CreatePairOperator<SwapIndexPairOperator>(),
4063 CreatePairOperator<PairNodeSwapActiveOperator<true>>(),
4064 CreatePairOperator<PairNodeSwapActiveOperator<false>>()});
4065 local_search_operators_[RELOCATE_SUBTRIP] =
4066 CreatePairOperator<RelocateSubtrip>();
4067 local_search_operators_[EXCHANGE_SUBTRIP] =
4068 CreatePairOperator<ExchangeSubtrip>();
4070 const auto arc_cost_for_path_start =
4071 [
this](int64_t before_node, int64_t after_node, int64_t start_index) {
4072 const int vehicle = index_to_vehicle_[start_index];
4073 const int64_t arc_cost =
4075 return (before_node != start_index ||
IsEnd(after_node))
4079 local_search_operators_[RELOCATE_EXPENSIVE_CHAIN] =
4080 solver_->RevAlloc(
new RelocateExpensiveChain(
4084 vehicle_start_class_callback_,
4085 parameters.relocate_expensive_chain_num_arcs_to_consider(),
4086 arc_cost_for_path_start));
4089 const auto make_global_cheapest_insertion_filtered_heuristic =
4091 using Heuristic = GlobalCheapestInsertionFilteredHeuristic;
4092 Heuristic::GlobalCheapestInsertionParameters ls_gci_parameters;
4093 ls_gci_parameters.is_sequential =
false;
4094 ls_gci_parameters.farthest_seeds_ratio = 0.0;
4095 ls_gci_parameters.neighbors_ratio =
4096 parameters.cheapest_insertion_ls_operator_neighbors_ratio();
4097 ls_gci_parameters.min_neighbors =
4098 parameters.cheapest_insertion_ls_operator_min_neighbors();
4099 ls_gci_parameters.use_neighbors_ratio_for_initialization =
true;
4100 ls_gci_parameters.add_unperformed_entries =
4101 parameters.cheapest_insertion_add_unperformed_entries();
4102 return absl::make_unique<Heuristic>(
4105 GetOrCreateFeasibilityFilterManager(
parameters), ls_gci_parameters);
4107 const auto make_local_cheapest_insertion_filtered_heuristic =
4109 return absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4111 GetOrCreateFeasibilityFilterManager(
parameters));
4113 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4114 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4115 make_global_cheapest_insertion_filtered_heuristic(),
4116 parameters.heuristic_close_nodes_lns_num_nodes()));
4118 local_search_operators_[LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4119 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4120 make_local_cheapest_insertion_filtered_heuristic(),
4121 parameters.heuristic_close_nodes_lns_num_nodes()));
4123 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_PATH_LNS] =
4124 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4125 make_global_cheapest_insertion_filtered_heuristic()));
4127 local_search_operators_[LOCAL_CHEAPEST_INSERTION_PATH_LNS] =
4128 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4129 make_local_cheapest_insertion_filtered_heuristic()));
4131 local_search_operators_
4132 [RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED] =
4134 new RelocatePathAndHeuristicInsertUnperformedOperator(
4135 make_global_cheapest_insertion_filtered_heuristic()));
4137 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4138 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4139 make_global_cheapest_insertion_filtered_heuristic(),
4140 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4141 arc_cost_for_path_start));
4143 local_search_operators_[LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4144 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4145 make_local_cheapest_insertion_filtered_heuristic(),
4146 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4147 arc_cost_for_path_start));
4150#define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators) \
4151 if (search_parameters.local_search_operators().use_##operator_method() == \
4153 operators.push_back(local_search_operators_[operator_type]); \
4156LocalSearchOperator* RoutingModel::ConcatenateOperators(
4157 const RoutingSearchParameters& search_parameters,
4158 const std::vector<LocalSearchOperator*>& operators)
const {
4159 if (search_parameters.use_multi_armed_bandit_concatenate_operators()) {
4160 return solver_->MultiArmedBanditConcatenateOperators(
4163 .multi_armed_bandit_compound_operator_memory_coefficient(),
4165 .multi_armed_bandit_compound_operator_exploration_coefficient(),
4168 return solver_->ConcatenateOperators(operators);
4171LocalSearchOperator* RoutingModel::GetNeighborhoodOperators(
4172 const RoutingSearchParameters& search_parameters)
const {
4173 std::vector<LocalSearchOperator*> operator_groups;
4174 std::vector<LocalSearchOperator*> operators = extra_operators_;
4175 if (!pickup_delivery_pairs_.empty()) {
4179 if (search_parameters.local_search_operators().use_relocate_pair() ==
4189 if (vehicles_ > 1) {
4200 if (!pickup_delivery_pairs_.empty() ||
4201 search_parameters.local_search_operators().use_relocate_neighbors() ==
4203 operators.push_back(local_search_operators_[RELOCATE_NEIGHBORS]);
4206 search_parameters.local_search_metaheuristic();
4208 local_search_metaheuristic !=
4210 local_search_metaheuristic !=
4218 if (!disjunctions_.empty()) {
4235 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4244 global_cheapest_insertion_path_lns, operators);
4246 local_cheapest_insertion_path_lns, operators);
4248 RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED,
4249 relocate_path_global_cheapest_insertion_insert_unperformed, operators);
4252 global_cheapest_insertion_expensive_chain_lns,
4255 local_cheapest_insertion_expensive_chain_lns,
4258 global_cheapest_insertion_close_nodes_lns,
4261 local_cheapest_insertion_close_nodes_lns, operators);
4262 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4267 local_search_metaheuristic !=
4269 local_search_metaheuristic !=
4274 local_search_metaheuristic !=
4276 local_search_metaheuristic !=
4282 if (!disjunctions_.empty()) {
4285 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4287 return solver_->ConcatenateOperators(operator_groups);
4290#undef CP_ROUTING_PUSH_OPERATOR
4294 if (dimension->GetUnaryTransitEvaluator(0) !=
nullptr)
return true;
4301void ConvertVectorInt64ToVectorInt(
const std::vector<int64_t>&
input,
4302 std::vector<int>* output) {
4303 const int n =
input.size();
4305 int* data = output->data();
4306 for (
int i = 0; i < n; ++i) {
4307 const int element =
static_cast<int>(
input[i]);
4315std::vector<LocalSearchFilterManager::FilterEvent>
4316RoutingModel::GetOrCreateLocalSearchFilters(
4317 const RoutingSearchParameters&
parameters,
bool filter_cost) {
4318 const auto kAccept = LocalSearchFilterManager::FilterEventType::kAccept;
4319 const auto kRelax = LocalSearchFilterManager::FilterEventType::kRelax;
4329 std::vector<LocalSearchFilterManager::FilterEvent> filters;
4331 if (filter_cost && vehicle_amortized_cost_factors_set_) {
4339 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4343 filters.push_back({sum, kAccept});
4345 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4346 nexts_, vehicle_vars_,
4347 [
this](int64_t i, int64_t j, int64_t k) {
4351 filters.push_back({sum, kAccept});
4355 filters.push_back({solver_->MakeVariableDomainFilter(), kAccept});
4357 if (vehicles_ > max_active_vehicles_) {
4361 if (!disjunctions_.empty()) {
4365 if (!pickup_delivery_pairs_.empty()) {
4368 vehicle_pickup_delivery_policy_),
4378 const PathState* path_state_reference =
nullptr;
4380 std::vector<int> path_starts;
4381 std::vector<int> path_ends;
4382 ConvertVectorInt64ToVectorInt(starts_, &path_starts);
4383 ConvertVectorInt64ToVectorInt(ends_, &path_ends);
4385 auto path_state = absl::make_unique<PathState>(
4386 Size() +
vehicles(), std::move(path_starts), std::move(path_ends));
4387 path_state_reference = path_state.get();
4399 if (!dimension->HasBreakConstraints())
continue;
4402 filters.insert(filters.end(), extra_filters_.begin(), extra_filters_.end());
4406LocalSearchFilterManager* RoutingModel::GetOrCreateLocalSearchFilterManager(
4408 if (!local_search_filter_manager_) {
4409 local_search_filter_manager_ =
4410 solver_->RevAlloc(
new LocalSearchFilterManager(
4413 return local_search_filter_manager_;
4416std::vector<LocalSearchFilterManager::FilterEvent>
4417RoutingModel::GetOrCreateFeasibilityFilters(
4419 return GetOrCreateLocalSearchFilters(
parameters,
false);
4422LocalSearchFilterManager* RoutingModel::GetOrCreateFeasibilityFilterManager(
4424 if (!feasibility_filter_manager_) {
4425 feasibility_filter_manager_ =
4426 solver_->RevAlloc(
new LocalSearchFilterManager(
4429 return feasibility_filter_manager_;
4432LocalSearchFilterManager*
4433RoutingModel::GetOrCreateStrongFeasibilityFilterManager(
4435 if (!strong_feasibility_filter_manager_) {
4436 std::vector<LocalSearchFilterManager::FilterEvent> filters =
4439 LocalSearchFilterManager::FilterEventType::kAccept});
4440 strong_feasibility_filter_manager_ =
4441 solver_->RevAlloc(
new LocalSearchFilterManager(std::move(filters)));
4443 return strong_feasibility_filter_manager_;
4448 for (
int vehicle = 0; vehicle < dimension.model()->
vehicles(); vehicle++) {
4449 if (!dimension.AreVehicleTransitsPositive(vehicle)) {
4457void RoutingModel::StoreDimensionCumulOptimizers(
4459 Assignment* packed_dimensions_collector_assignment =
4460 solver_->MakeAssignment();
4461 packed_dimensions_collector_assignment->AddObjective(
CostVar());
4462 const int num_dimensions = dimensions_.size();
4463 local_optimizer_index_.resize(num_dimensions, -1);
4464 global_optimizer_index_.resize(num_dimensions, -1);
4467 if (dimension->global_span_cost_coefficient() > 0 ||
4468 !dimension->GetNodePrecedences().empty()) {
4470 global_optimizer_index_[dim] = global_dimension_optimizers_.size();
4471 global_dimension_optimizers_.push_back(
4472 absl::make_unique<GlobalDimensionCumulOptimizer>(
4473 dimension,
parameters.continuous_scheduling_solver()));
4474 global_dimension_mp_optimizers_.push_back(
4475 absl::make_unique<GlobalDimensionCumulOptimizer>(
4476 dimension,
parameters.mixed_integer_scheduling_solver()));
4477 packed_dimensions_collector_assignment->Add(dimension->cumuls());
4478 if (!AllTransitsPositive(*dimension)) {
4479 dimension->SetOffsetForGlobalOptimizer(0);
4484 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4487 std::min(offset, dimension->CumulVar(
Start(vehicle))->Min() - 1);
4489 dimension->SetOffsetForGlobalOptimizer(
std::max(
Zero(), offset));
4491 bool has_span_cost =
false;
4492 bool has_span_limit =
false;
4493 std::vector<int64_t> vehicle_offsets(
vehicles());
4494 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4495 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4496 has_span_cost =
true;
4498 if (dimension->GetSpanUpperBoundForVehicle(vehicle) <
4500 has_span_limit =
true;
4503 vehicle_offsets[vehicle] =
4504 dimension->AreVehicleTransitsPositive(vehicle)
4506 dimension->CumulVar(
Start(vehicle))->Min() - 1)
4509 bool has_soft_lower_bound =
false;
4510 bool has_soft_upper_bound =
false;
4511 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
4512 if (dimension->HasCumulVarSoftLowerBound(i)) {
4513 has_soft_lower_bound =
true;
4515 if (dimension->HasCumulVarSoftUpperBound(i)) {
4516 has_soft_upper_bound =
true;
4519 int num_linear_constraints = 0;
4520 if (has_span_cost) ++num_linear_constraints;
4521 if (has_span_limit) ++num_linear_constraints;
4522 if (dimension->HasSoftSpanUpperBounds()) ++num_linear_constraints;
4523 if (has_soft_lower_bound) ++num_linear_constraints;
4524 if (has_soft_upper_bound) ++num_linear_constraints;
4525 if (dimension->HasBreakConstraints()) ++num_linear_constraints;
4526 if (num_linear_constraints >= 2) {
4527 dimension->SetVehicleOffsetsForLocalOptimizer(
4528 std::move(vehicle_offsets));
4529 local_optimizer_index_[dim] = local_dimension_optimizers_.size();
4530 local_dimension_optimizers_.push_back(
4531 absl::make_unique<LocalDimensionCumulOptimizer>(
4532 dimension,
parameters.continuous_scheduling_solver()));
4533 bool has_intervals =
false;
4534 for (
const SortedDisjointIntervalList& intervals :
4535 dimension->forbidden_intervals()) {
4538 if (intervals.NumIntervals() > 0) {
4539 has_intervals =
true;
4543 if (dimension->HasBreakConstraints() || has_intervals) {
4544 local_dimension_mp_optimizers_.push_back(
4545 absl::make_unique<LocalDimensionCumulOptimizer>(
4546 dimension,
parameters.mixed_integer_scheduling_solver()));
4548 local_dimension_mp_optimizers_.push_back(
nullptr);
4550 packed_dimensions_collector_assignment->Add(dimension->cumuls());
4553 DCHECK_EQ(local_dimension_mp_optimizers_.size(),
4554 local_dimension_optimizers_.size());
4560 for (IntVar*
const extra_var : extra_vars_) {
4561 packed_dimensions_collector_assignment->Add(extra_var);
4563 for (IntervalVar*
const extra_interval : extra_intervals_) {
4564 packed_dimensions_collector_assignment->Add(extra_interval);
4567 packed_dimensions_assignment_collector_ = solver_->MakeFirstSolutionCollector(
4568 packed_dimensions_collector_assignment);
4575 bool has_soft_or_span_cost =
false;
4576 for (
int vehicle = 0; vehicle <
vehicles(); ++vehicle) {
4577 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4578 has_soft_or_span_cost =
true;
4582 if (!has_soft_or_span_cost) {
4583 for (
int i = 0; i < dimension->cumuls().size(); ++i) {
4584 if (dimension->HasCumulVarSoftUpperBound(i) ||
4585 dimension->HasCumulVarSoftLowerBound(i)) {
4586 has_soft_or_span_cost =
true;
4591 if (has_soft_or_span_cost)
dimensions.push_back(dimension);
4597RoutingModel::CreateFinalizerForMinimizedAndMaximizedVariables() {
4598 std::stable_sort(finalizer_variable_cost_pairs_.begin(),
4599 finalizer_variable_cost_pairs_.end(),
4600 [](
const std::pair<IntVar*, int64_t>& var_cost1,
4601 const std::pair<IntVar*, int64_t>& var_cost2) {
4602 return var_cost1.second > var_cost2.second;
4604 const int num_variables = finalizer_variable_cost_pairs_.size() +
4605 finalizer_variable_target_pairs_.size();
4606 std::vector<IntVar*> variables;
4607 std::vector<int64_t> targets;
4608 variables.reserve(num_variables);
4609 targets.reserve(num_variables);
4610 for (
const auto& variable_cost : finalizer_variable_cost_pairs_) {
4611 variables.push_back(variable_cost.first);
4614 for (
const auto& variable_target : finalizer_variable_target_pairs_) {
4615 variables.push_back(variable_target.first);
4616 targets.push_back(variable_target.second);
4619 std::move(targets));
4622DecisionBuilder* RoutingModel::CreateSolutionFinalizer(SearchLimit* lns_limit) {
4623 std::vector<DecisionBuilder*> decision_builders;
4624 decision_builders.push_back(solver_->MakePhase(
4627 if (!local_dimension_optimizers_.empty()) {
4628 decision_builders.push_back(
4629 solver_->RevAlloc(
new SetCumulsFromLocalDimensionCosts(
4630 &local_dimension_optimizers_, &local_dimension_mp_optimizers_,
4633 if (!global_dimension_optimizers_.empty()) {
4634 decision_builders.push_back(
4635 solver_->RevAlloc(
new SetCumulsFromGlobalDimensionCosts(
4636 &global_dimension_optimizers_, &global_dimension_mp_optimizers_,
4639 decision_builders.push_back(
4640 CreateFinalizerForMinimizedAndMaximizedVariables());
4642 return solver_->Compose(decision_builders);
4645void RoutingModel::CreateFirstSolutionDecisionBuilders(
4646 const RoutingSearchParameters& search_parameters) {
4647 first_solution_decision_builders_.resize(
4649 first_solution_filtered_decision_builders_.resize(
4651 DecisionBuilder*
const finalize_solution =
4652 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit());
4654 first_solution_decision_builders_
4657 first_solution_decision_builders_
4660 [
this](int64_t i, int64_t j) {
4673 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
4674 first_solution_filtered_decision_builders_
4676 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4677 absl::make_unique<EvaluatorCheapestAdditionFilteredHeuristic>(
4679 [
this](int64_t i, int64_t j) {
4682 GetOrCreateFeasibilityFilterManager(search_parameters))));
4683 first_solution_decision_builders_
4685 solver_->Try(first_solution_filtered_decision_builders_
4687 first_solution_decision_builders_
4696 first_solution_decision_builders_
4699 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
4700 first_solution_filtered_decision_builders_
4702 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4703 absl::make_unique<ComparatorCheapestAdditionFilteredHeuristic>(
4705 GetOrCreateFeasibilityFilterManager(search_parameters))));
4706 first_solution_decision_builders_
4708 first_solution_filtered_decision_builders_
4710 first_solution_decision_builders_
4714 if (first_solution_evaluator_ !=
nullptr) {
4715 first_solution_decision_builders_
4719 first_solution_decision_builders_
4726 RegularLimit*
const ls_limit = solver_->MakeLimit(
4730 DecisionBuilder*
const finalize = solver_->MakeSolveOnce(
4731 finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit());
4732 LocalSearchPhaseParameters*
const insertion_parameters =
4733 solver_->MakeLocalSearchPhaseParameters(
4734 nullptr, CreateInsertionOperator(), finalize, ls_limit,
4735 GetOrCreateLocalSearchFilterManager(search_parameters));
4736 std::vector<IntVar*> decision_vars = nexts_;
4738 decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(),
4739 vehicle_vars_.end());
4741 const int64_t optimization_step =
std::max(
4744 solver_->MakeNestedOptimize(
4746 insertion_parameters),
4747 GetOrCreateAssignment(),
false, optimization_step);
4749 solver_->Compose(first_solution_decision_builders_
4754 GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
4756 gci_parameters.is_sequential =
false;
4757 gci_parameters.farthest_seeds_ratio =
4758 search_parameters.cheapest_insertion_farthest_seeds_ratio();
4759 gci_parameters.neighbors_ratio =
4760 search_parameters.cheapest_insertion_first_solution_neighbors_ratio();
4761 gci_parameters.min_neighbors =
4762 search_parameters.cheapest_insertion_first_solution_min_neighbors();
4763 gci_parameters.use_neighbors_ratio_for_initialization =
4765 .cheapest_insertion_first_solution_use_neighbors_ratio_for_initialization();
4766 gci_parameters.add_unperformed_entries =
4767 search_parameters.cheapest_insertion_add_unperformed_entries();
4768 for (
bool is_sequential : {
false,
true}) {
4772 gci_parameters.is_sequential = is_sequential;
4774 first_solution_filtered_decision_builders_[first_solution_strategy] =
4775 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4776 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4778 [
this](int64_t i, int64_t j, int64_t vehicle) {
4782 GetOrCreateFeasibilityFilterManager(search_parameters),
4784 IntVarFilteredDecisionBuilder*
const strong_gci =
4785 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4786 absl::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
4788 [
this](int64_t i, int64_t j, int64_t vehicle) {
4792 GetOrCreateStrongFeasibilityFilterManager(search_parameters),
4794 first_solution_decision_builders_[first_solution_strategy] = solver_->Try(
4795 first_solution_filtered_decision_builders_[first_solution_strategy],
4796 solver_->Try(strong_gci, first_solution_decision_builders_
4801 first_solution_filtered_decision_builders_
4803 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4804 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4806 [
this](int64_t i, int64_t j, int64_t vehicle) {
4809 GetOrCreateFeasibilityFilterManager(search_parameters))));
4810 IntVarFilteredDecisionBuilder*
const strong_lci =
4811 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4812 absl::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4814 [
this](int64_t i, int64_t j, int64_t vehicle) {
4817 GetOrCreateStrongFeasibilityFilterManager(search_parameters))));
4818 first_solution_decision_builders_
4820 first_solution_filtered_decision_builders_
4822 solver_->Try(strong_lci,
4823 first_solution_decision_builders_
4826 SavingsFilteredHeuristic::SavingsParameters savings_parameters;
4827 savings_parameters.neighbors_ratio =
4828 search_parameters.savings_neighbors_ratio();
4829 savings_parameters.max_memory_usage_bytes =
4830 search_parameters.savings_max_memory_usage_bytes();
4831 savings_parameters.add_reverse_arcs =
4832 search_parameters.savings_add_reverse_arcs();
4833 savings_parameters.arc_coefficient =
4834 search_parameters.savings_arc_coefficient();
4835 LocalSearchFilterManager* filter_manager =
nullptr;
4836 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
4837 filter_manager = GetOrCreateFeasibilityFilterManager(search_parameters);
4840 if (search_parameters.savings_parallel_routes()) {
4841 IntVarFilteredDecisionBuilder* savings_db =
4842 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4843 absl::make_unique<ParallelSavingsFilteredHeuristic>(
4844 this, &manager_, savings_parameters, filter_manager)));
4845 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
4846 first_solution_filtered_decision_builders_
4851 solver_->Try(savings_db,
4852 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4853 absl::make_unique<ParallelSavingsFilteredHeuristic>(
4854 this, &manager_, savings_parameters,
4855 GetOrCreateStrongFeasibilityFilterManager(
4856 search_parameters)))));
4858 IntVarFilteredDecisionBuilder* savings_db =
4859 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4860 absl::make_unique<SequentialSavingsFilteredHeuristic>(
4861 this, &manager_, savings_parameters, filter_manager)));
4862 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
4863 first_solution_filtered_decision_builders_
4868 solver_->Try(savings_db,
4869 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4870 absl::make_unique<SequentialSavingsFilteredHeuristic>(
4871 this, &manager_, savings_parameters,
4872 GetOrCreateStrongFeasibilityFilterManager(
4873 search_parameters)))));
4885 solver_->RevAlloc(
new IntVarFilteredDecisionBuilder(
4886 absl::make_unique<ChristofidesFilteredHeuristic>(
4887 this, GetOrCreateFeasibilityFilterManager(search_parameters),
4888 search_parameters.christofides_use_minimum_matching())));
4890 const bool has_precedences = std::any_of(
4891 dimensions_.begin(), dimensions_.end(),
4893 bool has_single_vehicle_node =
false;
4894 for (
int node = 0; node <
Size(); node++) {
4895 if (!
IsStart(node) && !
IsEnd(node) && allowed_vehicles_[node].size() == 1) {
4896 has_single_vehicle_node =
true;
4900 automatic_first_solution_strategy_ =
4902 has_precedences, has_single_vehicle_node);
4904 first_solution_decision_builders_[automatic_first_solution_strategy_];
4909DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder(
4910 const RoutingSearchParameters& search_parameters)
const {
4912 search_parameters.first_solution_strategy();
4913 if (first_solution_strategy < first_solution_decision_builders_.size()) {
4914 return first_solution_decision_builders_[first_solution_strategy];
4920IntVarFilteredDecisionBuilder*
4921RoutingModel::GetFilteredFirstSolutionDecisionBuilderOrNull(
4922 const RoutingSearchParameters& search_parameters)
const {
4924 search_parameters.first_solution_strategy();
4925 return first_solution_filtered_decision_builders_[first_solution_strategy];
4928LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters(
4929 const RoutingSearchParameters& search_parameters) {
4930 SearchLimit* lns_limit = GetOrCreateLargeNeighborhoodSearchLimit();
4931 return solver_->MakeLocalSearchPhaseParameters(
4932 CostVar(), GetNeighborhoodOperators(search_parameters),
4933 solver_->MakeSolveOnce(CreateSolutionFinalizer(lns_limit), lns_limit),
4934 GetOrCreateLocalSearchLimit(),
4935 GetOrCreateLocalSearchFilterManager(search_parameters));
4938DecisionBuilder* RoutingModel::CreateLocalSearchDecisionBuilder(
4939 const RoutingSearchParameters& search_parameters) {
4940 const int size =
Size();
4941 DecisionBuilder* first_solution =
4942 GetFirstSolutionDecisionBuilder(search_parameters);
4943 LocalSearchPhaseParameters*
const parameters =
4944 CreateLocalSearchParameters(search_parameters);
4945 SearchLimit* first_solution_lns_limit =
4946 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
4947 DecisionBuilder*
const first_solution_sub_decision_builder =
4948 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_solution_lns_limit),
4949 first_solution_lns_limit);
4951 return solver_->MakeLocalSearchPhase(nexts_, first_solution,
4952 first_solution_sub_decision_builder,
4955 const int all_size = size + size + vehicles_;
4956 std::vector<IntVar*> all_vars(all_size);
4957 for (
int i = 0; i < size; ++i) {
4958 all_vars[i] = nexts_[i];
4960 for (
int i = size; i < all_size; ++i) {
4961 all_vars[i] = vehicle_vars_[i - size];
4963 return solver_->MakeLocalSearchPhase(all_vars, first_solution,
4964 first_solution_sub_decision_builder,
4969void RoutingModel::SetupDecisionBuilders(
4970 const RoutingSearchParameters& search_parameters) {
4971 if (search_parameters.use_depth_first_search()) {
4972 SearchLimit* first_lns_limit =
4973 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
4974 solve_db_ = solver_->Compose(
4975 GetFirstSolutionDecisionBuilder(search_parameters),
4976 solver_->MakeSolveOnce(CreateSolutionFinalizer(first_lns_limit),
4979 solve_db_ = CreateLocalSearchDecisionBuilder(search_parameters);
4981 CHECK(preassignment_ !=
nullptr);
4982 DecisionBuilder* restore_preassignment =
4983 solver_->MakeRestoreAssignment(preassignment_);
4984 solve_db_ = solver_->Compose(restore_preassignment, solve_db_);
4986 solver_->Compose(restore_preassignment,
4987 solver_->MakeLocalSearchPhase(
4988 GetOrCreateAssignment(),
4989 CreateLocalSearchParameters(search_parameters)));
4990 restore_assignment_ = solver_->Compose(
4991 solver_->MakeRestoreAssignment(GetOrCreateAssignment()),
4992 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
4993 restore_tmp_assignment_ = solver_->Compose(
4994 restore_preassignment,
4995 solver_->MakeRestoreAssignment(GetOrCreateTmpAssignment()),
4996 CreateSolutionFinalizer(GetOrCreateLargeNeighborhoodSearchLimit()));
4999void RoutingModel::SetupMetaheuristics(
5000 const RoutingSearchParameters& search_parameters) {
5001 SearchMonitor* optimize;
5003 search_parameters.local_search_metaheuristic();
5006 bool limit_too_long =
5007 !search_parameters.has_time_limit() &&
5009 const int64_t optimization_step =
std::max(
5011 switch (metaheuristic) {
5014 optimize = solver_->MakeGuidedLocalSearch(
5017 optimization_step, nexts_,
5018 search_parameters.guided_local_search_lambda_coefficient());
5020 optimize = solver_->MakeGuidedLocalSearch(
5022 [
this](int64_t i, int64_t j, int64_t k) {
5025 optimization_step, nexts_, vehicle_vars_,
5026 search_parameters.guided_local_search_lambda_coefficient());
5031 solver_->MakeSimulatedAnnealing(
false, cost_, optimization_step, 100);
5034 optimize = solver_->MakeTabuSearch(
false, cost_, optimization_step,
5035 nexts_, 10, 10, .8);
5038 std::vector<operations_research::IntVar*> tabu_vars;
5039 if (tabu_var_callback_) {
5040 tabu_vars = tabu_var_callback_(
this);
5042 tabu_vars.push_back(cost_);
5044 optimize = solver_->MakeGenericTabuSearch(
false, cost_, optimization_step,
5049 limit_too_long =
false;
5050 optimize = solver_->MakeMinimize(cost_, optimization_step);
5052 if (limit_too_long) {
5054 <<
" specified without sane timeout: solve may run forever.";
5056 monitors_.push_back(optimize);
5060 tabu_var_callback_ = std::move(tabu_var_callback);
5063void RoutingModel::SetupAssignmentCollector(
5065 Assignment* full_assignment = solver_->MakeAssignment();
5067 full_assignment->
Add(dimension->cumuls());
5069 for (IntVar*
const extra_var : extra_vars_) {
5070 full_assignment->
Add(extra_var);
5072 for (IntervalVar*
const extra_interval : extra_intervals_) {
5073 full_assignment->
Add(extra_interval);
5075 full_assignment->
Add(nexts_);
5076 full_assignment->
Add(active_);
5077 full_assignment->
Add(vehicle_vars_);
5080 collect_assignments_ = solver_->MakeNBestValueSolutionCollector(
5083 collect_one_assignment_ =
5084 solver_->MakeFirstSolutionCollector(full_assignment);
5085 monitors_.push_back(collect_assignments_);
5088void RoutingModel::SetupTrace(
5089 const RoutingSearchParameters& search_parameters) {
5090 if (search_parameters.log_search()) {
5091 Solver::SearchLogParameters search_log_parameters;
5092 search_log_parameters.branch_period = 10000;
5093 search_log_parameters.objective =
nullptr;
5094 search_log_parameters.variable = cost_;
5095 search_log_parameters.scaling_factor =
5096 search_parameters.log_cost_scaling_factor();
5097 search_log_parameters.offset = search_parameters.log_cost_offset();
5098 if (!search_parameters.log_tag().empty()) {
5099 const std::string& tag = search_parameters.log_tag();
5100 search_log_parameters.display_callback = [tag]() {
return tag; };
5102 search_log_parameters.display_callback =
nullptr;
5104 search_log_parameters.display_on_new_solutions_only =
false;
5105 monitors_.push_back(solver_->MakeSearchLog(search_log_parameters));
5109void RoutingModel::SetupImprovementLimit(
5110 const RoutingSearchParameters& search_parameters) {
5111 if (search_parameters.has_improvement_limit_parameters()) {
5112 monitors_.push_back(solver_->MakeImprovementLimit(
5113 cost_,
false, search_parameters.log_cost_scaling_factor(),
5114 search_parameters.log_cost_offset(),
5115 search_parameters.improvement_limit_parameters()
5116 .improvement_rate_coefficient(),
5117 search_parameters.improvement_limit_parameters()
5118 .improvement_rate_solutions_distance()));
5122void RoutingModel::SetupSearchMonitors(
5123 const RoutingSearchParameters& search_parameters) {
5124 monitors_.push_back(GetOrCreateLimit());
5125 SetupImprovementLimit(search_parameters);
5126 SetupMetaheuristics(search_parameters);
5127 SetupAssignmentCollector(search_parameters);
5128 SetupTrace(search_parameters);
5131bool RoutingModel::UsesLightPropagation(
5132 const RoutingSearchParameters& search_parameters)
const {
5133 return !search_parameters.use_full_propagation() &&
5134 !search_parameters.use_depth_first_search() &&
5135 search_parameters.first_solution_strategy() !=
5143 finalizer_variable_cost_pairs_.size());
5144 if (
index < finalizer_variable_cost_pairs_.size()) {
5145 const int64_t old_cost = finalizer_variable_cost_pairs_[
index].second;
5146 finalizer_variable_cost_pairs_[
index].second =
CapAdd(old_cost,
cost);
5148 finalizer_variable_cost_pairs_.emplace_back(
var,
cost);
5154 if (finalizer_variable_target_set_.contains(
var))
return;
5155 finalizer_variable_target_set_.insert(
var);
5156 finalizer_variable_target_pairs_.emplace_back(
var, target);
5167void RoutingModel::SetupSearch(
5169 SetupDecisionBuilders(search_parameters);
5170 SetupSearchMonitors(search_parameters);
5174 extra_vars_.push_back(
var);
5178 extra_intervals_.push_back(
interval);
5183class PathSpansAndTotalSlacks :
public Constraint {
5187 std::vector<IntVar*> spans,
5188 std::vector<IntVar*> total_slacks)
5191 dimension_(dimension),
5192 spans_(
std::move(spans)),
5193 total_slacks_(
std::move(total_slacks)) {
5194 CHECK_EQ(spans_.size(), model_->vehicles());
5195 CHECK_EQ(total_slacks_.size(), model_->vehicles());
5196 vehicle_demons_.resize(model_->vehicles());
5199 std::string DebugString()
const override {
return "PathSpansAndTotalSlacks"; }
5201 void Post()
override {
5202 const int num_nodes = model_->VehicleVars().size();
5203 const int num_transits = model_->Nexts().size();
5204 for (
int node = 0; node < num_nodes; ++node) {
5206 model_->solver(),
this, &PathSpansAndTotalSlacks::PropagateNode,
5207 "PathSpansAndTotalSlacks::PropagateNode", node);
5208 dimension_->CumulVar(node)->WhenRange(demon);
5209 model_->VehicleVar(node)->WhenBound(demon);
5210 if (node < num_transits) {
5211 dimension_->TransitVar(node)->WhenRange(demon);
5212 dimension_->FixedTransitVar(node)->WhenBound(demon);
5213 model_->NextVar(node)->WhenBound(demon);
5216 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5217 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5219 solver(),
this, &PathSpansAndTotalSlacks::PropagateVehicle,
5220 "PathSpansAndTotalSlacks::PropagateVehicle", vehicle);
5221 vehicle_demons_[vehicle] = demon;
5222 if (spans_[vehicle]) spans_[vehicle]->WhenRange(demon);
5223 if (total_slacks_[vehicle]) total_slacks_[vehicle]->WhenRange(demon);
5224 if (dimension_->HasBreakConstraints()) {
5225 for (IntervalVar*
b : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5226 b->WhenAnything(demon);
5233 void InitialPropagate()
override {
5234 for (
int vehicle = 0; vehicle < spans_.size(); ++vehicle) {
5235 if (!spans_[vehicle] && !total_slacks_[vehicle])
continue;
5236 PropagateVehicle(vehicle);
5244 void PropagateNode(
int node) {
5245 if (!model_->VehicleVar(node)->Bound())
return;
5246 const int vehicle = model_->VehicleVar(node)->Min();
5247 if (vehicle < 0 || vehicle_demons_[vehicle] ==
nullptr)
return;
5248 EnqueueDelayedDemon(vehicle_demons_[vehicle]);
5256 int64_t SpanMin(
int vehicle, int64_t sum_fixed_transits) {
5258 const int64_t span_min = spans_[vehicle]
5259 ? spans_[vehicle]->Min()
5261 const int64_t total_slack_min = total_slacks_[vehicle]
5262 ? total_slacks_[vehicle]->Min()
5264 return std::min(span_min,
CapAdd(total_slack_min, sum_fixed_transits));
5266 int64_t SpanMax(
int vehicle, int64_t sum_fixed_transits) {
5268 const int64_t span_max = spans_[vehicle]
5269 ? spans_[vehicle]->Max()
5271 const int64_t total_slack_max = total_slacks_[vehicle]
5272 ? total_slacks_[vehicle]->Max()
5274 return std::max(span_max,
CapAdd(total_slack_max, sum_fixed_transits));
5276 void SetSpanMin(
int vehicle, int64_t
min, int64_t sum_fixed_transits) {
5278 if (spans_[vehicle]) {
5279 spans_[vehicle]->SetMin(
min);
5281 if (total_slacks_[vehicle]) {
5282 total_slacks_[vehicle]->SetMin(
CapSub(
min, sum_fixed_transits));
5285 void SetSpanMax(
int vehicle, int64_t
max, int64_t sum_fixed_transits) {
5287 if (spans_[vehicle]) {
5288 spans_[vehicle]->SetMax(
max);
5290 if (total_slacks_[vehicle]) {
5291 total_slacks_[vehicle]->SetMax(
CapSub(
max, sum_fixed_transits));
5296 void SynchronizeSpanAndTotalSlack(
int vehicle, int64_t sum_fixed_transits) {
5298 IntVar* span = spans_[vehicle];
5299 IntVar* total_slack = total_slacks_[vehicle];
5300 if (!span || !total_slack)
return;
5301 span->SetMin(
CapAdd(total_slack->Min(), sum_fixed_transits));
5302 span->SetMax(
CapAdd(total_slack->Max(), sum_fixed_transits));
5303 total_slack->SetMin(
CapSub(span->Min(), sum_fixed_transits));
5304 total_slack->SetMax(
CapSub(span->Max(), sum_fixed_transits));
5307 void PropagateVehicle(
int vehicle) {
5308 DCHECK(spans_[vehicle] || total_slacks_[vehicle]);
5309 const int start = model_->Start(vehicle);
5310 const int end = model_->End(vehicle);
5316 int curr_node = start;
5317 while (!model_->IsEnd(curr_node)) {
5318 const IntVar* next_var = model_->NextVar(curr_node);
5319 if (!next_var->Bound())
return;
5320 path_.push_back(curr_node);
5321 curr_node = next_var->Value();
5326 int64_t sum_fixed_transits = 0;
5327 for (
const int node : path_) {
5328 const IntVar* fixed_transit_var = dimension_->FixedTransitVar(node);
5329 if (!fixed_transit_var->Bound())
return;
5330 sum_fixed_transits =
5331 CapAdd(sum_fixed_transits, fixed_transit_var->Value());
5334 SynchronizeSpanAndTotalSlack(vehicle, sum_fixed_transits);
5341 if (dimension_->HasBreakConstraints() &&
5342 !dimension_->GetBreakIntervalsOfVehicle(vehicle).empty()) {
5343 const int64_t vehicle_start_max = dimension_->CumulVar(start)->Max();
5344 const int64_t vehicle_end_min = dimension_->CumulVar(end)->Min();
5346 int64_t min_break_duration = 0;
5347 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5348 if (!br->MustBePerformed())
continue;
5349 if (vehicle_start_max < br->EndMin() &&
5350 br->StartMax() < vehicle_end_min) {
5351 min_break_duration =
CapAdd(min_break_duration, br->DurationMin());
5354 SetSpanMin(vehicle,
CapAdd(min_break_duration, sum_fixed_transits),
5355 sum_fixed_transits);
5361 const int64_t slack_max =
5362 CapSub(SpanMax(vehicle, sum_fixed_transits), sum_fixed_transits);
5363 const int64_t max_additional_slack =
5364 CapSub(slack_max, min_break_duration);
5365 for (IntervalVar* br : dimension_->GetBreakIntervalsOfVehicle(vehicle)) {
5366 if (!br->MustBePerformed())
continue;
5368 if (vehicle_start_max >= br->EndMin() &&
5369 br->StartMax() < vehicle_end_min) {
5370 if (br->DurationMin() > max_additional_slack) {
5373 br->SetEndMax(vehicle_start_max);
5374 dimension_->CumulVar(start)->SetMin(br->EndMin());
5379 if (vehicle_start_max < br->EndMin() &&
5380 br->StartMax() >= vehicle_end_min) {
5381 if (br->DurationMin() > max_additional_slack) {
5382 br->SetStartMin(vehicle_end_min);
5383 dimension_->CumulVar(end)->SetMax(br->StartMax());
5391 IntVar* start_cumul = dimension_->CumulVar(start);
5392 IntVar* end_cumul = dimension_->CumulVar(end);
5393 const int64_t
start_min = start_cumul->Min();
5394 const int64_t
start_max = start_cumul->Max();
5395 const int64_t
end_min = end_cumul->Min();
5396 const int64_t
end_max = end_cumul->Max();
5399 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5401 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5403 const int64_t span_min = SpanMin(vehicle, sum_fixed_transits);
5404 const int64_t span_max = SpanMax(vehicle, sum_fixed_transits);
5405 const int64_t slack_from_lb =
CapSub(span_max, span_lb);
5406 const int64_t slack_from_ub =
CapSub(span_ub, span_min);
5420 int64_t span_lb = 0;
5421 int64_t span_ub = 0;
5422 for (
const int node : path_) {
5423 span_lb =
CapAdd(span_lb, dimension_->TransitVar(node)->Min());
5424 span_ub =
CapAdd(span_ub, dimension_->TransitVar(node)->Max());
5426 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5427 SetSpanMax(vehicle, span_ub, sum_fixed_transits);
5431 const int64_t span_min = SpanMin(vehicle, sum_fixed_transits);
5432 const int64_t span_max = SpanMax(vehicle, sum_fixed_transits);
5433 const int64_t slack_from_lb =
CapSub(span_max, span_lb);
5434 const int64_t slack_from_ub =
5436 ?
CapSub(span_ub, span_min)
5437 :
std::numeric_limits<int64_t>::
max();
5438 for (
const int node : path_) {
5439 IntVar* transit_var = dimension_->TransitVar(node);
5440 const int64_t transit_i_min = transit_var->Min();
5441 const int64_t transit_i_max = transit_var->Max();
5445 transit_var->SetMax(
CapAdd(transit_i_min, slack_from_lb));
5446 transit_var->SetMin(
CapSub(transit_i_max, slack_from_ub));
5451 path_.push_back(end);
5462 int64_t arrival_time = dimension_->CumulVar(start)->Min();
5463 for (
int i = 1; i < path_.size(); ++i) {
5466 dimension_->FixedTransitVar(path_[i - 1])->Min()),
5467 dimension_->CumulVar(path_[i])->Min());
5469 int64_t departure_time = arrival_time;
5470 for (
int i = path_.size() - 2; i >= 0; --i) {
5473 dimension_->FixedTransitVar(path_[i])->Min()),
5474 dimension_->CumulVar(path_[i])->Max());
5476 const int64_t span_lb =
CapSub(arrival_time, departure_time);
5477 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5478 const int64_t maximum_deviation =
5479 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5480 const int64_t start_lb =
CapSub(departure_time, maximum_deviation);
5481 dimension_->CumulVar(start)->SetMin(start_lb);
5485 int64_t departure_time = dimension_->CumulVar(end)->Max();
5486 for (
int i = path_.size() - 2; i >= 0; --i) {
5487 const int curr_node = path_[i];
5490 dimension_->FixedTransitVar(curr_node)->Min()),
5491 dimension_->CumulVar(curr_node)->Max());
5493 int arrival_time = departure_time;
5494 for (
int i = 1; i < path_.size(); ++i) {
5497 dimension_->FixedTransitVar(path_[i - 1])->Min()),
5498 dimension_->CumulVar(path_[i])->Min());
5500 const int64_t span_lb =
CapSub(arrival_time, departure_time);
5501 SetSpanMin(vehicle, span_lb, sum_fixed_transits);
5502 const int64_t maximum_deviation =
5503 CapSub(SpanMax(vehicle, sum_fixed_transits), span_lb);
5504 dimension_->CumulVar(end)->SetMax(
5505 CapAdd(arrival_time, maximum_deviation));
5511 std::vector<IntVar*> spans_;
5512 std::vector<IntVar*> total_slacks_;
5513 std::vector<int> path_;
5514 std::vector<Demon*> vehicle_demons_;
5521 std::vector<IntVar*> total_slacks) {
5523 CHECK_EQ(vehicles_, total_slacks.size());
5525 new PathSpansAndTotalSlacks(
this, dimension, spans, total_slacks));
5533 std::vector<int64_t> vehicle_capacities,
5534 const std::string&
name,
5536 : vehicle_capacities_(
std::move(vehicle_capacities)),
5537 base_dimension_(base_dimension),
5538 global_span_cost_coefficient_(0),
5541 global_optimizer_offset_(0) {
5543 vehicle_span_upper_bounds_.assign(
model->vehicles(),
5545 vehicle_span_cost_coefficients_.assign(
model->vehicles(), 0);
5548RoutingDimension::RoutingDimension(RoutingModel*
model,
5549 std::vector<int64_t> vehicle_capacities,
5550 const std::string&
name, SelfBased)
5551 : RoutingDimension(
model,
std::move(vehicle_capacities),
name, this) {}
5554 cumul_var_piecewise_linear_cost_.clear();
5557void RoutingDimension::Initialize(
5558 const std::vector<int>& transit_evaluators,
5559 const std::vector<int>& state_dependent_transit_evaluators,
5560 int64_t slack_max) {
5562 InitializeTransits(transit_evaluators, state_dependent_transit_evaluators,
5576class LightRangeLessOrEqual :
public Constraint {
5578 LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l, IntExpr*
const r);
5579 ~LightRangeLessOrEqual()
override {}
5580 void Post()
override;
5581 void InitialPropagate()
override;
5582 std::string DebugString()
const override;
5583 IntVar* Var()
override {
5584 return solver()->MakeIsLessOrEqualVar(left_, right_);
5587 void Accept(ModelVisitor*
const visitor)
const override {
5598 IntExpr*
const left_;
5599 IntExpr*
const right_;
5603LightRangeLessOrEqual::LightRangeLessOrEqual(Solver*
const s, IntExpr*
const l,
5605 : Constraint(s), left_(l), right_(r), demon_(nullptr) {}
5607void LightRangeLessOrEqual::Post() {
5609 solver(),
this, &LightRangeLessOrEqual::CheckRange,
"CheckRange");
5610 left_->WhenRange(demon_);
5611 right_->WhenRange(demon_);
5614void LightRangeLessOrEqual::InitialPropagate() {
5615 left_->SetMax(right_->Max());
5616 right_->SetMin(left_->Min());
5617 if (left_->Max() <= right_->Min()) {
5622void LightRangeLessOrEqual::CheckRange() {
5623 if (left_->Min() > right_->Max()) {
5626 if (left_->Max() <= right_->Min()) {
5631std::string LightRangeLessOrEqual::DebugString()
const {
5632 return left_->DebugString() +
" < " + right_->DebugString();
5637void RoutingDimension::InitializeCumuls() {
5638 Solver*
const solver = model_->
solver();
5640 const auto capacity_range = std::minmax_element(vehicle_capacities_.begin(),
5641 vehicle_capacities_.end());
5642 const int64_t min_capacity = *capacity_range.first;
5644 const int64_t max_capacity = *capacity_range.second;
5645 solver->MakeIntVarArray(size, 0, max_capacity, name_, &cumuls_);
5647 for (
int v = 0; v < model_->
vehicles(); v++) {
5648 const int64_t vehicle_capacity = vehicle_capacities_[v];
5649 cumuls_[model_->
Start(v)]->SetMax(vehicle_capacity);
5650 cumuls_[model_->
End(v)]->SetMax(vehicle_capacity);
5653 forbidden_intervals_.resize(size);
5654 capacity_vars_.clear();
5655 if (min_capacity != max_capacity) {
5658 for (
int i = 0; i < size; ++i) {
5659 IntVar*
const capacity_var = capacity_vars_[i];
5660 if (i < model_->Size()) {
5661 IntVar*
const capacity_active = solver->MakeBoolVar();
5662 solver->AddConstraint(
5663 solver->MakeLessOrEqual(model_->
ActiveVar(i), capacity_active));
5664 solver->AddConstraint(solver->MakeIsLessOrEqualCt(
5665 cumuls_[i], capacity_var, capacity_active));
5667 solver->AddConstraint(
5668 solver->MakeLessOrEqual(cumuls_[i], capacity_var));
5675template <
int64_t value>
5676int64_t IthElementOrValue(
const std::vector<int64_t>& v, int64_t
index) {
5680void ComputeTransitClasses(
const std::vector<int>& evaluator_indices,
5681 std::vector<int>* class_evaluators,
5682 std::vector<int64_t>* vehicle_to_class) {
5683 CHECK(class_evaluators !=
nullptr);
5684 CHECK(vehicle_to_class !=
nullptr);
5685 class_evaluators->clear();
5686 vehicle_to_class->resize(evaluator_indices.size(), -1);
5687 absl::flat_hash_map<int, int64_t> evaluator_to_class;
5688 for (
int i = 0; i < evaluator_indices.size(); ++i) {
5689 const int evaluator_index = evaluator_indices[i];
5690 int evaluator_class = -1;
5691 if (!
gtl::FindCopy(evaluator_to_class, evaluator_index, &evaluator_class)) {
5692 evaluator_class = class_evaluators->size();
5693 evaluator_to_class[evaluator_index] = evaluator_class;
5694 class_evaluators->push_back(evaluator_index);
5696 (*vehicle_to_class)[i] = evaluator_class;
5701void RoutingDimension::InitializeTransitVariables(int64_t slack_max) {
5702 CHECK(!class_evaluators_.empty());
5703 CHECK(base_dimension_ ==
nullptr ||
5704 !state_dependent_class_evaluators_.empty());
5706 Solver*
const solver = model_->
solver();
5707 const int size = model_->
Size();
5710 return (0 <=
index &&
index < state_dependent_vehicle_to_class_.size())
5711 ? state_dependent_vehicle_to_class_[
index]
5712 : state_dependent_class_evaluators_.size();
5714 const std::string slack_name = name_ +
" slack";
5715 const std::string transit_name = name_ +
" fixed transit";
5717 for (int64_t i = 0; i < size; ++i) {
5720 absl::StrCat(transit_name, i));
5722 if (base_dimension_ !=
nullptr) {
5723 if (state_dependent_class_evaluators_.size() == 1) {
5724 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
5725 for (int64_t j = 0; j < cumuls_.size(); ++j) {
5726 transition_variables[j] =
5727 MakeRangeMakeElementExpr(
5729 ->StateDependentTransitCallback(
5730 state_dependent_class_evaluators_[0])(i, j)
5732 base_dimension_->
CumulVar(i), solver)
5735 dependent_transits_[i] =
5736 solver->MakeElement(transition_variables, model_->
NextVar(i))
5739 IntVar*
const vehicle_class_var =
5741 ->MakeElement(dependent_vehicle_class_function,
5744 std::vector<IntVar*> transit_for_vehicle;
5745 transit_for_vehicle.reserve(state_dependent_class_evaluators_.size() +
5747 for (
int evaluator : state_dependent_class_evaluators_) {
5748 std::vector<IntVar*> transition_variables(cumuls_.size(),
nullptr);
5749 for (int64_t j = 0; j < cumuls_.size(); ++j) {
5750 transition_variables[j] =
5751 MakeRangeMakeElementExpr(
5754 base_dimension_->
CumulVar(i), solver)
5757 transit_for_vehicle.push_back(
5758 solver->MakeElement(transition_variables, model_->
NextVar(i))
5761 transit_for_vehicle.push_back(solver->MakeIntConst(0));
5762 dependent_transits_[i] =
5763 solver->MakeElement(transit_for_vehicle, vehicle_class_var)->Var();
5766 dependent_transits_[i] = solver->MakeIntConst(0);
5770 IntExpr* transit_expr = fixed_transits_[i];
5771 if (dependent_transits_[i]->Min() != 0 ||
5772 dependent_transits_[i]->Max() != 0) {
5773 transit_expr = solver->MakeSum(transit_expr, dependent_transits_[i]);
5776 if (slack_max == 0) {
5777 slacks_[i] = solver->MakeIntConst(0);
5780 solver->MakeIntVar(0, slack_max, absl::StrCat(slack_name, i));
5781 transit_expr = solver->MakeSum(slacks_[i], transit_expr);
5783 transits_[i] = transit_expr->Var();
5787void RoutingDimension::InitializeTransits(
5788 const std::vector<int>& transit_evaluators,
5789 const std::vector<int>& state_dependent_transit_evaluators,
5790 int64_t slack_max) {
5792 CHECK(base_dimension_ ==
nullptr ||
5793 model_->
vehicles() == state_dependent_transit_evaluators.size());
5794 const int size = model_->
Size();
5795 transits_.resize(size,
nullptr);
5796 fixed_transits_.resize(size,
nullptr);
5797 slacks_.resize(size,
nullptr);
5798 dependent_transits_.resize(size,
nullptr);
5799 ComputeTransitClasses(transit_evaluators, &class_evaluators_,
5800 &vehicle_to_class_);
5801 if (base_dimension_ !=
nullptr) {
5802 ComputeTransitClasses(state_dependent_transit_evaluators,
5803 &state_dependent_class_evaluators_,
5804 &state_dependent_vehicle_to_class_);
5807 InitializeTransitVariables(slack_max);
5812 std::vector<int64_t>* values) {
5813 const int num_nodes = path.size();
5814 values->resize(num_nodes - 1);
5815 for (
int i = 0; i < num_nodes - 1; ++i) {
5816 (*values)[i] = evaluator(path[i], path[i + 1]);
5821 : model_(
model), occurrences_of_type_(
model.GetNumberOfVisitTypes()) {}
5824 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor) {
5831 for (
int pos = 0; pos < current_route_visits_.size(); pos++) {
5832 const int64_t current_visit = current_route_visits_[pos];
5839 DCHECK_LT(type, occurrences_of_type_.size());
5840 int& num_type_added = occurrences_of_type_[type].num_type_added_to_vehicle;
5841 int& num_type_removed =
5842 occurrences_of_type_[type].num_type_removed_from_vehicle;
5843 DCHECK_LE(num_type_removed, num_type_added);
5845 num_type_removed == num_type_added) {
5855 if (policy == VisitTypePolicy::TYPE_ADDED_TO_VEHICLE ||
5856 policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED) {
5859 if (policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED ||
5860 policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
5868 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor) {
5871 std::fill(occurrences_of_type_.begin(), occurrences_of_type_.end(),
5877 current_route_visits_.clear();
5879 current = next_accessor(current)) {
5882 VisitTypePolicy::TYPE_ON_VEHICLE_UP_TO_VISIT) {
5883 occurrences_of_type_[type].position_of_last_type_on_vehicle_up_to_visit =
5884 current_route_visits_.size();
5886 current_route_visits_.push_back(current);
5908 check_hard_incompatibilities_(check_hard_incompatibilities) {}
5910bool TypeIncompatibilityChecker::HasRegulationsToCheck()
const {
5912 (check_hard_incompatibilities_ &&
5921bool TypeIncompatibilityChecker::CheckTypeRegulations(
int type,
5922 VisitTypePolicy policy,
5924 if (policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
5929 for (
int incompatible_type :
5935 if (check_hard_incompatibilities_) {
5936 for (
int incompatible_type :
5946bool TypeRequirementChecker::HasRegulationsToCheck()
const {
5951bool TypeRequirementChecker::CheckRequiredTypesCurrentlyOnRoute(
5952 const std::vector<absl::flat_hash_set<int>>& required_type_alternatives,
5954 for (
const absl::flat_hash_set<int>& requirement_alternatives :
5955 required_type_alternatives) {
5956 bool has_one_of_alternatives =
false;
5957 for (
int type_alternative : requirement_alternatives) {
5959 has_one_of_alternatives =
true;
5963 if (!has_one_of_alternatives) {
5970bool TypeRequirementChecker::CheckTypeRegulations(
int type,
5971 VisitTypePolicy policy,
5975 if (!CheckRequiredTypesCurrentlyOnRoute(
5981 if (!CheckRequiredTypesCurrentlyOnRoute(
5988 types_with_same_vehicle_requirements_on_route_.insert(type);
5993bool TypeRequirementChecker::FinalizeCheck()
const {
5994 for (
int type : types_with_same_vehicle_requirements_on_route_) {
5995 for (
const absl::flat_hash_set<int>& requirement_alternatives :
5997 bool has_one_of_alternatives =
false;
5998 for (
const int type_alternative : requirement_alternatives) {
6000 has_one_of_alternatives =
true;
6004 if (!has_one_of_alternatives) {
6015 incompatibility_checker_(
model, true),
6016 requirement_checker_(
model),
6017 vehicle_demons_(
model.vehicles()) {}
6019void TypeRegulationsConstraint::PropagateNodeRegulations(
int node) {
6026 if (vehicle < 0)
return;
6027 DCHECK(vehicle_demons_[vehicle] !=
nullptr);
6031void TypeRegulationsConstraint::CheckRegulationsOnVehicle(
int vehicle) {
6032 const auto next_accessor = [
this, vehicle](int64_t node) {
6037 return model_.
End(vehicle);
6039 if (!incompatibility_checker_.
CheckVehicle(vehicle, next_accessor) ||
6040 !requirement_checker_.
CheckVehicle(vehicle, next_accessor)) {
6046 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6048 solver(),
this, &TypeRegulationsConstraint::CheckRegulationsOnVehicle,
6049 "CheckRegulationsOnVehicle", vehicle);
6051 for (
int node = 0; node < model_.
Size(); node++) {
6053 solver(),
this, &TypeRegulationsConstraint::PropagateNodeRegulations,
6054 "PropagateNodeRegulations", node);
6061 for (
int vehicle = 0; vehicle < model_.
vehicles(); vehicle++) {
6062 CheckRegulationsOnVehicle(vehicle);
6066void RoutingDimension::CloseModel(
bool use_light_propagation) {
6068 const auto capacity_lambda = [
this](int64_t vehicle) {
6069 return vehicle >= 0 ? vehicle_capacities_[vehicle]
6072 for (
int i = 0; i < capacity_vars_.size(); ++i) {
6073 IntVar*
const vehicle_var = model_->
VehicleVar(i);
6074 IntVar*
const capacity_var = capacity_vars_[i];
6075 if (use_light_propagation) {
6077 solver, capacity_var, vehicle_var, capacity_lambda,
6078 [
this]() {
return model_->enable_deep_serialization_; }));
6085 for (
int i = 0; i < fixed_transits_.size(); ++i) {
6086 IntVar*
const next_var = model_->
NextVar(i);
6087 IntVar*
const fixed_transit = fixed_transits_[i];
6088 const auto transit_vehicle_evaluator = [
this, i](int64_t to,
6089 int64_t eval_index) {
6092 if (use_light_propagation) {
6093 if (class_evaluators_.size() == 1) {
6094 const int class_evaluator_index = class_evaluators_[0];
6095 const auto& unary_callback =
6097 if (unary_callback ==
nullptr) {
6099 solver, fixed_transit, next_var,
6100 [
this, i](int64_t to) {
6103 [
this]() {
return model_->enable_deep_serialization_; }));
6105 fixed_transit->SetValue(unary_callback(i));
6109 solver, fixed_transit, next_var, model_->
VehicleVar(i),
6110 transit_vehicle_evaluator,
6111 [
this]() {
return model_->enable_deep_serialization_; }));
6114 if (class_evaluators_.size() == 1) {
6115 const int class_evaluator_index = class_evaluators_[0];
6116 const auto& unary_callback =
6118 if (unary_callback ==
nullptr) {
6120 fixed_transit, solver
6122 [
this, i](int64_t to) {
6124 class_evaluators_[0])(i, to);
6129 fixed_transit->SetValue(unary_callback(i));
6133 fixed_transit, solver
6141 GlobalVehicleBreaksConstraint* constraint =
6148 int64_t vehicle)
const {
6154 int64_t
index, int64_t min_value, int64_t max_value)
const {
6160 int64_t next_start =
min;
6164 if (next_start >
max)
break;
6165 if (next_start < interval->start) {
6170 if (next_start <=
max) {
6179 CHECK_LT(vehicle, vehicle_span_upper_bounds_.size());
6181 vehicle_span_upper_bounds_[vehicle] =
upper_bound;
6187 CHECK_LT(vehicle, vehicle_span_cost_coefficients_.size());
6189 vehicle_span_cost_coefficients_[vehicle] =
coefficient;
6205 if (!
cost.IsNonDecreasing()) {
6206 LOG(
WARNING) <<
"Only non-decreasing cost functions are supported.";
6209 if (
cost.Value(0) < 0) {
6210 LOG(
WARNING) <<
"Only positive cost functions are supported.";
6213 if (
index >= cumul_var_piecewise_linear_cost_.size()) {
6214 cumul_var_piecewise_linear_cost_.resize(
index + 1);
6216 PiecewiseLinearCost& piecewise_linear_cost =
6217 cumul_var_piecewise_linear_cost_[
index];
6218 piecewise_linear_cost.var = cumuls_[
index];
6219 piecewise_linear_cost.cost = absl::make_unique<PiecewiseLinearFunction>(
cost);
6223 return (
index < cumul_var_piecewise_linear_cost_.size() &&
6224 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr);
6228 int64_t
index)
const {
6229 if (
index < cumul_var_piecewise_linear_cost_.size() &&
6230 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr) {
6231 return cumul_var_piecewise_linear_cost_[
index].cost.get();
6241 const int vehicle =
model->VehicleIndex(
index);
6243 return solver->
MakeProd(expr,
model->VehicleCostsConsideredVar(vehicle))
6250void RoutingDimension::SetupCumulVarPiecewiseLinearCosts(
6251 std::vector<IntVar*>* cost_elements)
const {
6252 CHECK(cost_elements !=
nullptr);
6253 Solver*
const solver = model_->
solver();
6254 for (
int i = 0; i < cumul_var_piecewise_linear_cost_.size(); ++i) {
6255 const PiecewiseLinearCost& piecewise_linear_cost =
6256 cumul_var_piecewise_linear_cost_[i];
6257 if (piecewise_linear_cost.var !=
nullptr) {
6258 IntExpr*
const expr = solver->MakePiecewiseLinearExpr(
6259 piecewise_linear_cost.var, *piecewise_linear_cost.cost);
6260 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6261 cost_elements->push_back(cost_var);
6272 if (
index >= cumul_var_soft_upper_bound_.size()) {
6273 cumul_var_soft_upper_bound_.resize(
index + 1, {
nullptr, 0, 0});
6280 return (
index < cumul_var_soft_upper_bound_.size() &&
6281 cumul_var_soft_upper_bound_[
index].var !=
nullptr);
6285 if (
index < cumul_var_soft_upper_bound_.size() &&
6286 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6287 return cumul_var_soft_upper_bound_[
index].bound;
6289 return cumuls_[
index]->Max();
6293 int64_t
index)
const {
6294 if (
index < cumul_var_soft_upper_bound_.size() &&
6295 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6296 return cumul_var_soft_upper_bound_[
index].coefficient;
6301void RoutingDimension::SetupCumulVarSoftUpperBoundCosts(
6302 std::vector<IntVar*>* cost_elements)
const {
6303 CHECK(cost_elements !=
nullptr);
6305 for (
int i = 0; i < cumul_var_soft_upper_bound_.size(); ++i) {
6306 const SoftBound& soft_bound = cumul_var_soft_upper_bound_[i];
6307 if (soft_bound.var !=
nullptr) {
6309 solver->
MakeSum(soft_bound.var, -soft_bound.bound), 0,
6310 soft_bound.coefficient);
6311 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6312 cost_elements->push_back(cost_var);
6316 soft_bound.coefficient);
6324 if (
index >= cumul_var_soft_lower_bound_.size()) {
6325 cumul_var_soft_lower_bound_.resize(
index + 1, {
nullptr, 0, 0});
6332 return (
index < cumul_var_soft_lower_bound_.size() &&
6333 cumul_var_soft_lower_bound_[
index].var !=
nullptr);
6337 if (
index < cumul_var_soft_lower_bound_.size() &&
6338 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6339 return cumul_var_soft_lower_bound_[
index].bound;
6341 return cumuls_[
index]->Min();
6345 int64_t
index)
const {
6346 if (
index < cumul_var_soft_lower_bound_.size() &&
6347 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6348 return cumul_var_soft_lower_bound_[
index].coefficient;
6353void RoutingDimension::SetupCumulVarSoftLowerBoundCosts(
6354 std::vector<IntVar*>* cost_elements)
const {
6355 CHECK(cost_elements !=
nullptr);
6357 for (
int i = 0; i < cumul_var_soft_lower_bound_.size(); ++i) {
6358 const SoftBound& soft_bound = cumul_var_soft_lower_bound_[i];
6359 if (soft_bound.var !=
nullptr) {
6362 soft_bound.coefficient);
6363 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6364 cost_elements->push_back(cost_var);
6368 soft_bound.coefficient);
6373void RoutingDimension::SetupGlobalSpanCost(
6374 std::vector<IntVar*>* cost_elements)
const {
6375 CHECK(cost_elements !=
nullptr);
6376 Solver*
const solver = model_->
solver();
6377 if (global_span_cost_coefficient_ != 0) {
6378 std::vector<IntVar*> end_cumuls;
6379 for (
int i = 0; i < model_->
vehicles(); ++i) {
6380 end_cumuls.push_back(solver
6381 ->MakeProd(model_->vehicle_costs_considered_[i],
6382 cumuls_[model_->
End(i)])
6385 IntVar*
const max_end_cumul = solver->
MakeMax(end_cumuls)->
Var();
6387 max_end_cumul, global_span_cost_coefficient_);
6388 std::vector<IntVar*> start_cumuls;
6389 for (
int i = 0; i < model_->
vehicles(); ++i) {
6390 IntVar* global_span_cost_start_cumul =
6392 solver->AddConstraint(solver->MakeIfThenElseCt(
6393 model_->vehicle_costs_considered_[i], cumuls_[model_->
Start(i)],
6394 max_end_cumul, global_span_cost_start_cumul));
6395 start_cumuls.push_back(global_span_cost_start_cumul);
6397 IntVar*
const min_start_cumul = solver->MakeMin(start_cumuls)->Var();
6399 min_start_cumul, global_span_cost_coefficient_);
6404 for (
int var_index = 0; var_index < model_->
Size(); ++var_index) {
6406 slacks_[var_index], global_span_cost_coefficient_);
6407 cost_elements->push_back(
6410 model_->vehicle_costs_considered_[0],
6413 solver->MakeSum(transits_[var_index],
6414 dependent_transits_[var_index]),
6415 global_span_cost_coefficient_),
6420 IntVar*
const end_range =
6421 solver->MakeDifference(max_end_cumul, min_start_cumul)->Var();
6422 end_range->SetMin(0);
6423 cost_elements->push_back(
6424 solver->MakeProd(end_range, global_span_cost_coefficient_)->Var());
6430 std::vector<IntervalVar*> breaks,
int vehicle,
6431 std::vector<int64_t> node_visit_transits) {
6432 if (breaks.empty())
return;
6434 [node_visit_transits](int64_t from, int64_t to) {
6435 return node_visit_transits[from];
6441 std::vector<IntervalVar*> breaks,
int vehicle,
6442 std::vector<int64_t> node_visit_transits,
6443 std::function<int64_t(int64_t, int64_t)> delays) {
6444 if (breaks.empty())
return;
6446 [node_visit_transits](int64_t from, int64_t to) {
6447 return node_visit_transits[from];
6449 const int delay_evaluator =
6456 std::vector<IntervalVar*> breaks,
int vehicle,
int pre_travel_evaluator,
6457 int post_travel_evaluator) {
6460 if (breaks.empty())
return;
6462 vehicle_break_intervals_[vehicle] = std::move(breaks);
6463 vehicle_pre_travel_evaluators_[vehicle] = pre_travel_evaluator;
6464 vehicle_post_travel_evaluators_[vehicle] = post_travel_evaluator;
6485 DCHECK(!break_constraints_are_initialized_);
6486 const int num_vehicles = model_->
vehicles();
6487 vehicle_break_intervals_.resize(num_vehicles);
6488 vehicle_pre_travel_evaluators_.resize(num_vehicles, -1);
6489 vehicle_post_travel_evaluators_.resize(num_vehicles, -1);
6490 vehicle_break_distance_duration_.resize(num_vehicles);
6491 break_constraints_are_initialized_ =
true;
6495 return break_constraints_are_initialized_;
6499 int vehicle)
const {
6501 DCHECK_LT(vehicle, vehicle_break_intervals_.size());
6502 return vehicle_break_intervals_[vehicle];
6507 DCHECK_LT(vehicle, vehicle_pre_travel_evaluators_.size());
6508 return vehicle_pre_travel_evaluators_[vehicle];
6513 DCHECK_LT(vehicle, vehicle_post_travel_evaluators_.size());
6514 return vehicle_post_travel_evaluators_[vehicle];
6523 vehicle_break_distance_duration_[vehicle].emplace_back(
distance, duration);
6532const std::vector<std::pair<int64_t, int64_t>>&
6535 DCHECK_LT(vehicle, vehicle_break_distance_duration_.size());
6536 return vehicle_break_distance_duration_[vehicle];
6542 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6543 pickup_to_delivery_limits_per_pair_index_.resize(pair_index + 1);
6545 pickup_to_delivery_limits_per_pair_index_[pair_index] =
6546 std::move(limit_function);
6550 return !pickup_to_delivery_limits_per_pair_index_.empty();
6555 int delivery)
const {
6558 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6562 pickup_to_delivery_limits_per_pair_index_[pair_index];
6563 if (!pickup_to_delivery_limit_function) {
6569 return pickup_to_delivery_limit_function(pickup, delivery);
6572void RoutingDimension::SetupSlackAndDependentTransitCosts()
const {
6573 if (model_->
vehicles() == 0)
return;
6575 bool all_vehicle_span_costs_are_equal =
true;
6576 for (
int i = 1; i < model_->
vehicles(); ++i) {
6577 all_vehicle_span_costs_are_equal &= vehicle_span_cost_coefficients_[i] ==
6578 vehicle_span_cost_coefficients_[0];
6581 if (all_vehicle_span_costs_are_equal &&
6582 vehicle_span_cost_coefficients_[0] == 0) {
6594 std::vector<const RoutingDimension*> dimensions_with_relevant_slacks = {
this};
6596 const RoutingDimension*
next =
6597 dimensions_with_relevant_slacks.back()->base_dimension_;
6598 if (
next ==
nullptr ||
next == dimensions_with_relevant_slacks.back()) {
6601 dimensions_with_relevant_slacks.push_back(
next);
6604 for (
auto it = dimensions_with_relevant_slacks.rbegin();
6605 it != dimensions_with_relevant_slacks.rend(); ++it) {
6606 for (
int i = 0; i < model_->
vehicles(); ++i) {
6612 for (IntVar*
const slack : (*it)->slacks_) {
std::vector< int > dimensions
#define DCHECK_LE(val1, val2)
#define DCHECK_NE(val1, val2)
#define CHECK_LT(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define DCHECK_GE(val1, val2)
#define CHECK_NE(val1, val2)
#define DCHECK_GT(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define CHECK_LE(val1, val2)
#define DCHECK_EQ(val1, val2)
#define VLOG(verboselevel)
An Assignment is a variable -> domains mapping, used to report solutions to the user.
bool Contains(const IntVar *const var) const
int64_t ObjectiveValue() const
void AddObjective(IntVar *const v)
void SetValue(const IntVar *const var, int64_t value)
int64_t Max(const IntVar *const var) const
int64_t Value(const IntVar *const var) const
void CopyIntersection(const Assignment *assignment)
Copies the intersection of the two assignments to the current assignment.
void Copy(const Assignment *assignment)
Copies 'assignment' to the current assignment, clearing its previous content.
IntVarElement * Add(IntVar *const var)
bool Bound(const IntVar *const var) const
int64_t Min(const IntVar *const var) const
A constraint is the main modeling object.
A DecisionBuilder is responsible for creating the search tree.
A Demon is the base element of a propagation queue.
void inhibit(Solver *const s)
This method inhibits the demon in the search tree below the current position.
We call domain any subset of Int64 = [kint64min, kint64max].
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)
static constexpr Value GLOBAL_CHEAPEST_ARC
static constexpr Value BEST_INSERTION
FirstSolutionStrategy_Value Value
static constexpr Value SAVINGS
static constexpr Value LOCAL_CHEAPEST_ARC
static constexpr Value FIRST_UNBOUND_MIN_VALUE
static constexpr Value ALL_UNPERFORMED
static constexpr Value LOCAL_CHEAPEST_INSERTION
static constexpr Value AUTOMATIC
static constexpr Value PATH_MOST_CONSTRAINED_ARC
static constexpr Value UNSET
static constexpr Value CHRISTOFIDES
static constexpr Value SWEEP
static constexpr Value PARALLEL_CHEAPEST_INSERTION
static const std::string & Value_Name(T enum_t_value)
static constexpr Value EVALUATOR_STRATEGY
static constexpr Value SEQUENTIAL_CHEAPEST_INSERTION
static constexpr Value PATH_CHEAPEST_ARC
Utility class to encapsulate an IntVarIterator and use it in a range-based loop.
The class IntExpr is the base of all integer expressions in constraint programming.
virtual bool Bound() const
Returns true if the min and the max of the expression are equal.
virtual 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.
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 Attributes & GetDimensionAttributes(const RoutingDimension *dimension) const
A ResourceGroup defines a set of available Resources with attributes on one or multiple dimensions.
void AddResource(Attributes attributes, const RoutingDimension *dimension)
Add a Resource with the given attributes for the corresponding dimension.
const absl::flat_hash_set< DimensionIndex > & GetAffectedDimensionIndices() const
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.
int GetVehicleClassesCount() const
Returns the number of different vehicle classes in the model.
int64_t GetFixedCostOfVehicle(int vehicle) const
Returns the route fixed cost taken into account if the route of the vehicle is not empty,...
std::pair< int, bool > AddVectorDimension(std::vector< int64_t > values, int64_t capacity, bool fix_start_cumul_to_zero, const std::string &name)
Creates a dimension where the transit variable is constrained to be equal to 'values[i]' for node i; ...
PickupAndDeliveryPolicy GetPickupAndDeliveryPolicyOfVehicle(int vehicle) const
bool IsStart(int64_t index) const
Returns true if 'index' represents the first node of a route.
void SetPickupAndDeliveryPolicyOfAllVehicles(PickupAndDeliveryPolicy policy)
Sets the Pickup and delivery policy of all vehicles.
void AddSoftSameVehicleConstraint(const std::vector< int64_t > &indices, int64_t cost)
Adds a soft constraint to force a set of variable indices to be on the same vehicle.
Assignment * ReadAssignment(const std::string &file_name)
Reads an assignment from a file and returns the current solution.
Assignment * CompactAssignment(const Assignment &assignment) const
Returns a compacted version of the given assignment, in which all vehicles with id lower or equal to ...
int RegisterStateDependentTransitCallback(VariableIndexEvaluator2 callback)
const std::vector< std::pair< int, int > > & GetDeliveryIndexPairs(int64_t node_index) const
Same as above for deliveries.
void AddToAssignment(IntVar *const var)
Adds an extra variable to the vehicle routing assignment.
IntVar * VehicleVar(int64_t index) const
Returns the vehicle variable of the node corresponding to index.
void AddVariableMinimizedByFinalizer(IntVar *var)
Adds a variable to minimize in the solution finalizer.
VisitTypePolicy
Set the node visit types and incompatibilities/requirements between the types (see below).
@ TYPE_ADDED_TO_VEHICLE
When visited, the number of types 'T' on the vehicle increases by one.
@ ADDED_TYPE_REMOVED_FROM_VEHICLE
When visited, one instance of type 'T' previously added to the route (TYPE_ADDED_TO_VEHICLE),...
@ TYPE_ON_VEHICLE_UP_TO_VISIT
With the following policy, the visit enforces that type 'T' is considered on the route from its start...
@ TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED
The visit doesn't have an impact on the number of types 'T' on the route, as it's (virtually) added a...
GlobalDimensionCumulOptimizer * GetMutableGlobalCumulMPOptimizer(const RoutingDimension &dimension) const
Constraint * MakePathSpansAndTotalSlacks(const RoutingDimension *dimension, std::vector< IntVar * > spans, std::vector< IntVar * > total_slacks)
For every vehicle of the routing model:
friend class RoutingDimension
int64_t GetHomogeneousCost(int64_t from_index, int64_t to_index) const
Returns the cost of the segment between two nodes supposing all vehicle costs are the same (returns t...
LocalDimensionCumulOptimizer * GetMutableLocalCumulOptimizer(const RoutingDimension &dimension) const
int RegisterUnaryTransitVector(std::vector< int64_t > values)
Registers 'callback' and returns its index.
const std::vector< absl::flat_hash_set< int > > & GetSameVehicleRequiredTypeAlternativesOfType(int type) const
Returns the set of same-vehicle requirement alternatives for the given type.
int64_t Size() const
Returns the number of next variables in the model.
RoutingDimension * GetMutableDimension(const std::string &dimension_name) const
Returns a dimension from its name.
int GetVisitType(int64_t index) const
bool RoutesToAssignment(const std::vector< std::vector< int64_t > > &routes, bool ignore_inactive_indices, bool close_routes, Assignment *const assignment) const
Fills an assignment from a specification of the routes of the vehicles.
bool HasTemporalTypeRequirements() const
IntVar * NextVar(int64_t index) const
!defined(SWIGPYTHON)
static const int64_t kNoPenalty
Constant used to express a hard constraint instead of a soft penalty.
void AddPickupAndDeliverySets(DisjunctionIndex pickup_disjunction, DisjunctionIndex delivery_disjunction)
Same as AddPickupAndDelivery but notifying that the performed node from the disjunction of index 'pic...
RoutingTransitCallback2 TransitCallback2
SweepArranger * sweep_arranger() const
Returns the sweep arranger to be used by routing heuristics.
std::vector< std::string > GetAllDimensionNames() const
Outputs the names of all dimensions added to the routing engine.
std::pair< int, bool > AddConstantDimensionWithSlack(int64_t value, int64_t capacity, int64_t slack_max, bool fix_start_cumul_to_zero, const std::string &name)
Creates a dimension where the transit variable is constrained to be equal to 'value'; 'capacity' is t...
Assignment * ReadAssignmentFromRoutes(const std::vector< std::vector< int64_t > > &routes, bool ignore_inactive_indices)
Restores the routes as the current solution.
const Assignment * SolveFromAssignmentWithParameters(const Assignment *assignment, const RoutingSearchParameters &search_parameters, std::vector< const Assignment * > *solutions=nullptr)
Same as above, except that if assignment is not null, it will be used as the initial solution.
@ ROUTING_SUCCESS
Problem solved successfully after calling RoutingModel::Solve().
@ ROUTING_FAIL
No solution found to the problem after calling RoutingModel::Solve().
@ ROUTING_INVALID
Model, model parameters or flags are not valid.
@ ROUTING_FAIL_TIMEOUT
Time limit reached before finding a solution with RoutingModel::Solve().
void SetVisitType(int64_t index, int type, VisitTypePolicy type_policy)
int64_t GetDepot() const
Returns the variable index of the first starting or ending node of all routes.
std::vector< RoutingDimension * > GetDimensionsWithSoftOrSpanCosts() const
Returns dimensions with soft or vehicle span costs.
void SetSweepArranger(SweepArranger *sweep_arranger)
void AddTemporalTypeIncompatibility(int type1, int type2)
const std::vector< int > & GetSingleNodesOfType(int type) const
void SetFixedCostOfVehicle(int64_t cost, int vehicle)
Sets the fixed cost of one vehicle route.
std::vector< std::vector< std::pair< int64_t, int64_t > > > GetCumulBounds(const Assignment &solution_assignment, const RoutingDimension &dimension)
Returns a vector cumul_bounds, for which cumul_bounds[i][j] is a pair containing the minimum and maxi...
const std::vector< absl::flat_hash_set< int > > & GetRequiredTypeAlternativesWhenAddingType(int type) const
Returns the set of requirement alternatives when adding the given type.
bool ArcIsMoreConstrainedThanArc(int64_t from, int64_t to1, int64_t to2)
Returns whether the arc from->to1 is more constrained than from->to2, taking into account,...
void AddHardTypeIncompatibility(int type1, int type2)
Incompatibilities: Two nodes with "hard" incompatible types cannot share the same route at all,...
void AddPickupAndDelivery(int64_t pickup, int64_t delivery)
Notifies that index1 and index2 form a pair of nodes which should belong to the same route.
const TransitCallback1 & UnaryTransitCallbackOrNull(int callback_index) const
int64_t GetArcCostForFirstSolution(int64_t from_index, int64_t to_index) const
Returns the cost of the arc in the context of the first solution strategy.
bool IsVehicleAllowedForIndex(int vehicle, int64_t index)
Returns true if a vehicle is allowed to visit a given node.
int RegisterPositiveUnaryTransitCallback(TransitCallback1 callback)
void SetTabuVarsCallback(GetTabuVarsCallback tabu_var_callback)
IntVar * ApplyLocks(const std::vector< int64_t > &locks)
Applies a lock chain to the next search.
int64_t GetArcCostForVehicle(int64_t from_index, int64_t to_index, int64_t vehicle) const
Returns the cost of the transit arc between two nodes for a given vehicle.
void CloseVisitTypes()
This function should be called once all node visit types have been set and prior to adding any incomp...
void AssignmentToRoutes(const Assignment &assignment, std::vector< std::vector< int64_t > > *const routes) const
Converts the solution in the given assignment to routes for all vehicles.
DisjunctionIndex AddDisjunction(const std::vector< int64_t > &indices, int64_t penalty=kNoPenalty, int64_t max_cardinality=1)
Adds a disjunction constraint on the indices: exactly 'max_cardinality' of the indices are active.
const std::vector< RoutingDimension * > & GetDimensions() const
Returns all dimensions of the model.
void IgnoreDisjunctionsAlreadyForcedToZero()
SPECIAL: Makes the solver ignore all the disjunctions whose active variables are all trivially zero (...
void SetPickupAndDeliveryPolicyOfVehicle(PickupAndDeliveryPolicy policy, int vehicle)
const Assignment * SolveWithParameters(const RoutingSearchParameters &search_parameters, std::vector< const Assignment * > *solutions=nullptr)
Solves the current routing model with the given parameters.
int RegisterTransitCallback(TransitCallback2 callback)
void AddVariableTargetToFinalizer(IntVar *var, int64_t target)
Add a variable to set the closest possible to the target value in the solution finalizer.
const VariableIndexEvaluator2 & StateDependentTransitCallback(int callback_index) const
const Assignment * PackCumulsOfOptimizerDimensionsFromAssignment(const Assignment *original_assignment, absl::Duration duration_limit)
For every dimension in the model with an optimizer in local/global_dimension_optimizers_,...
const std::vector< IntVar * > & Nexts() const
Returns all next variables of the model, such that Nexts(i) is the next variable of the node correspo...
const std::vector< DisjunctionIndex > & GetDisjunctionIndices(int64_t index) const
Returns the indices of the disjunctions to which an index belongs.
int64_t UnperformedPenaltyOrValue(int64_t default_value, int64_t var_index) const
Same as above except that it returns default_value instead of 0 when penalty is not well defined (def...
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
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.
ResourceGroup *const AddResourceGroup()
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.
::PROTOBUF_NAMESPACE_ID::int32 number_of_solutions_to_collect() const
A search monitor is a simple set of callbacks to monitor all search events.
int solution_count() const
Returns how many solutions were stored during the search.
Assignment * solution(int n) const
Returns the nth solution.
IntExpr * MakeSemiContinuousExpr(IntExpr *const expr, int64_t fixed_charge, int64_t step)
Semi continuous Expression (x <= 0 -> f(x) = 0; x > 0 -> f(x) = ax + b) a >= 0 and b >= 0.
Constraint * MakeEquality(IntExpr *const left, IntExpr *const right)
left == right
@ ASSIGN_MIN_VALUE
Selects the min value of the selected variable.
std::function< int64_t(int64_t, int64_t)> IndexEvaluator2
void AddConstraint(Constraint *const c)
Adds the constraint 'c' to the model.
@ FULLPATHLNS
Operator which relaxes one entire path and all inactive nodes, thus defining num_paths neighbors.
@ OROPT
Relocate: OROPT and RELOCATE.
@ PATHLNS
Operator which relaxes two sub-chains of three consecutive arcs each.
@ UNACTIVELNS
Operator which relaxes all inactive nodes and one sub-chain of six consecutive arcs.
IntExpr * MakeElement(const std::vector< int64_t > &values, IntVar *const index)
values[index]
@ CHOOSE_STATIC_GLOBAL_BEST
Pairs are compared at the first call of the selector, and results are cached.
IntExpr * MakeMax(const std::vector< IntVar * > &vars)
std::max(vars)
IntExpr * MakeDifference(IntExpr *const left, IntExpr *const right)
left - right
static ConstraintSolverParameters DefaultSolverParameters()
Create a ConstraintSolverParameters proto with all the default values.
@ CHOOSE_FIRST_UNBOUND
Select the first unbound variable.
@ CHOOSE_PATH
Selects the next unbound variable on a path, the path being defined by the variables: var[i] correspo...
IntExpr * MakeSum(IntExpr *const left, IntExpr *const right)
left + right.
std::function< int64_t(int64_t)> IndexEvaluator1
Callback typedefs.
IntExpr * MakeProd(IntExpr *const left, IntExpr *const right)
left * right
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
std::function< bool(int64_t, int64_t, int64_t)> VariableValueComparator
T * RevAlloc(T *object)
Registers the given object as being reversible.
EvaluatorLocalSearchOperators
This enum is used in Solver::MakeOperator associated with an evaluator to specify the neighborhood to...
@ TSPOPT
Sliding TSP operator.
@ LK
Lin-Kernighan local search.
@ LE
Move is accepted when the current objective value <= objective.Max.
This class represents a sorted list of disjoint, closed intervals.
Iterator InsertInterval(int64_t start, int64_t end)
Adds the interval [start..end] to the list, and merges overlapping or immediately adjacent intervals ...
ConstIterator end() const
Iterator FirstIntervalGreaterOrEqual(int64_t value) const
Returns an iterator to either:
IntervalSet::iterator Iterator
Class to arrange indices by by their distance and their angles from the depot.
TypeIncompatibilityChecker(const RoutingModel &model, bool check_hard_incompatibilities)
virtual bool HasRegulationsToCheck() const =0
virtual bool CheckTypeRegulations(int type, VisitTypePolicy policy, int pos)=0
virtual void OnInitializeCheck()
bool CheckVehicle(int vehicle, const std::function< int64_t(int64_t)> &next_accessor)
TypeRegulationsChecker(const RoutingModel &model)
virtual bool FinalizeCheck() const
void InitializeCheck(int vehicle, const std::function< int64_t(int64_t)> &next_accessor)
bool TypeCurrentlyOnRoute(int type, int pos) const
Returns true iff there's at least one instance of the given type on the route when scanning the route...
const RoutingModel & model_
bool TypeOccursOnRoute(int type) const
Returns true iff any occurrence of the given type was seen on the route, i.e.
The following constraint ensures that incompatibilities and requirements between types are respected.
void Post() override
This method is called when the constraint is processed by the solver.
void InitialPropagate() override
This method performs the initial propagation of the constraint.
TypeRegulationsConstraint(const RoutingModel &model)
IntegerRange< NodeIndex > AllNodes() const
void STLDeleteElements(T *container)
Collection::value_type::second_type & LookupOrInsert(Collection *const collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
bool FindCopy(const Collection &collection, const Key &key, Value *const value)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
const Collection::value_type::second_type * FindOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
bool ContainsKey(const Collection &collection, const Key &key)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Collection of objects used to extend the Constraint Solver library.
bool SolveModelWithSat(const RoutingModel &model, const RoutingSearchParameters &search_parameters, const Assignment *initial_solution, Assignment *solution)
Attempts to solve the model using the cp-sat solver.
void AppendDimensionCumulFilters(const std::vector< RoutingDimension * > &dimensions, const RoutingSearchParameters ¶meters, bool filter_objective_cost, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
int64_t CapAdd(int64_t x, int64_t y)
RangeIntToIntFunction * MakeCachedIntToIntFunction(const std::function< int64_t(int64_t)> &f, int64_t domain_start, int64_t domain_end)
std::function< int64_t(int64_t, int64_t)> RoutingTransitCallback2
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
constexpr int FirstSolutionStrategy_Value_Value_ARRAYSIZE
RoutingModelParameters DefaultRoutingModelParameters()
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 * MakeSetValuesFromTargets(Solver *solver, std::vector< IntVar * > variables, std::vector< int64_t > targets)
A decision builder which tries to assign values to variables as close as possible to target values fi...
FirstSolutionStrategy::Value AutomaticFirstSolutionStrategy(bool has_pickup_deliveries, bool has_node_precedences, bool has_single_vehicle_node)
Returns the best value for the automatic first solution strategy, based on the given model parameters...
uint64_t ThoroughHash(const char *bytes, size_t len)
int64_t One()
This method returns 1.
DimensionSchedulingStatus
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)
Returns a filter ensuring that node disjunction constraints are enforced.
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
static int input(yyscan_t yyscanner)
#define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method, operators)
std::vector< int > var_indices
static bool LessThan(const CostClass &a, const CostClass &b)
Comparator for STL containers and algorithms.
What follows is relevant for models with time/state dependent transits.
static bool LessThan(const VehicleClass &a, const VehicleClass &b)
Comparator for STL containers and algorithms.
int position_of_last_type_on_vehicle_up_to_visit
Position of the last node of policy TYPE_ON_VEHICLE_UP_TO_VISIT visited on the route.
int num_type_added_to_vehicle
Number of TYPE_ADDED_TO_VEHICLE and TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED node type policies seen on ...
int num_type_removed_from_vehicle
Number of ADDED_TYPE_REMOVED_FROM_VEHICLE (effectively removing a type from the route) and TYPE_SIMUL...