36#include "absl/algorithm/container.h"
37#include "absl/container/flat_hash_map.h"
38#include "absl/container/flat_hash_set.h"
39#include "absl/flags/flag.h"
40#include "absl/functional/bind_front.h"
41#include "absl/hash/hash.h"
42#include "absl/log/check.h"
43#include "absl/log/die_if_null.h"
44#include "absl/memory/memory.h"
45#include "absl/status/statusor.h"
46#include "absl/strings/str_cat.h"
47#include "absl/strings/str_format.h"
48#include "absl/strings/string_view.h"
49#include "absl/time/time.h"
50#include "absl/types/span.h"
64#include "ortools/constraint_solver/routing_enums.pb.h"
67#include "ortools/constraint_solver/routing_ils.pb.h"
73#include "ortools/constraint_solver/routing_parameters.pb.h"
77#include "ortools/constraint_solver/solver_parameters.pb.h"
82#include "ortools/util/optional_boolean.pb.h"
92class ExtendedSwapActiveOperator;
93class LocalSearchPhaseParameters;
94class MakeActiveAndRelocate;
95class MakeActiveOperator;
96class MakeChainInactiveOperator;
97class MakeInactiveOperator;
99class RelocateAndMakeActiveOperator;
100class SwapActiveOperator;
108std::string RoutingModel::RouteDimensionTravelInfo::DebugString(
109 std::string line_prefix)
const {
110 std::string s = absl::StrFormat(
"%stravel_cost_coefficient: %ld", line_prefix,
111 travel_cost_coefficient);
112 for (
int i = 0;
i < transition_info.size(); ++
i) {
113 absl::StrAppendFormat(&s,
"\ntransition[%d] {\n%s\n}\n", i,
114 transition_info[i].DebugString(line_prefix +
"\t"));
119std::string RoutingModel::RouteDimensionTravelInfo::TransitionInfo::DebugString(
120 std::string line_prefix)
const {
121 return absl::StrFormat(
130 line_prefix, pre_travel_transit_value, line_prefix,
131 post_travel_transit_value, line_prefix,
132 compressed_travel_value_lower_bound, line_prefix,
133 travel_value_upper_bound, line_prefix,
134 travel_start_dependent_travel.DebugString(line_prefix + "\t"),
135 line_prefix, travel_compression_cost.DebugString(line_prefix +
"\t"));
138std::string RoutingModel::RouteDimensionTravelInfo::TransitionInfo::
139 PiecewiseLinearFormulation::DebugString(std::string line_prefix)
const {
140 if (x_anchors.size() <= 10) {
141 return "{ " +
DUMP_VARS(x_anchors, y_anchors).str() +
"}";
143 return absl::StrFormat(
"{\n%s%s\n%s%s\n}", line_prefix,
148const Assignment* RoutingModel::PackCumulsOfOptimizerDimensionsFromAssignment(
149 const Assignment* original_assignment, absl::Duration duration_limit,
150 bool* time_limit_was_reached) {
152 if (original_assignment ==
nullptr)
return nullptr;
153 if (duration_limit <= absl::ZeroDuration()) {
154 if (time_limit_was_reached) *time_limit_was_reached =
true;
155 return original_assignment;
157 if (global_dimension_optimizers_.empty() &&
158 local_dimension_optimizers_.empty()) {
159 return original_assignment;
161 RegularLimit*
const limit = GetOrCreateLimit();
162 limit->UpdateLimits(duration_limit, std::numeric_limits<int64_t>::max(),
163 std::numeric_limits<int64_t>::max(),
164 std::numeric_limits<int64_t>::max());
166 RegularLimit*
const cumulative_limit = GetOrCreateCumulativeLimit();
167 cumulative_limit->UpdateLimits(
168 duration_limit, std::numeric_limits<int64_t>::max(),
169 std::numeric_limits<int64_t>::max(), std::numeric_limits<int64_t>::max());
174 packed_assignment->
Add(Nexts());
176 for (
const RoutingDimension*
const dimension : dimensions_) {
177 for (
int rg_index : GetDimensionResourceGroupIndices(dimension)) {
178 DCHECK(HasLocalCumulOptimizer(*dimension));
179 packed_assignment->Add(resource_vars_[rg_index]);
182 packed_assignment->CopyIntersection(original_assignment);
184 std::vector<DecisionBuilder*> decision_builders;
186 decision_builders.push_back(
188 for (
auto& [lp_optimizer, mp_optimizer] : local_dimension_optimizers_) {
189 if (HasGlobalCumulOptimizer(*lp_optimizer->dimension())) {
194 solver_.get(), lp_optimizer.get(), mp_optimizer.get(),
197 for (
auto& [lp_optimizer, mp_optimizer] : global_dimension_optimizers_) {
199 solver_.get(), lp_optimizer.get(), mp_optimizer.get(), cumulative_limit,
202 decision_builders.push_back(finalizer_variables_->CreateFinalizer());
204 DecisionBuilder* restore_pack_and_finalize =
205 solver_->
Compose(decision_builders);
206 solver_->
Solve(restore_pack_and_finalize,
207 optimized_dimensions_assignment_collector_, limit);
208 const bool limit_was_reached = limit->Check();
209 if (time_limit_was_reached) *time_limit_was_reached = limit_was_reached;
210 if (optimized_dimensions_assignment_collector_->solution_count() != 1) {
211 if (limit_was_reached) {
212 VLOG(1) <<
"The packing reached the time limit.";
216 LOG(ERROR) <<
"The given assignment is not valid for this model, or"
217 " cannot be packed.";
222 packed_assignment->Copy(original_assignment);
223 packed_assignment->CopyIntersection(
224 optimized_dimensions_assignment_collector_->solution(0));
226 return packed_assignment;
229void RoutingModel::SetSweepArranger(SweepArranger* sweep_arranger) {
230 sweep_arranger_.reset(sweep_arranger);
233SweepArranger* RoutingModel::sweep_arranger()
const {
234 return sweep_arranger_.get();
237void RoutingModel::NodeNeighborsByCostClass::ComputeNeighbors(
238 const RoutingModel& routing_model,
int num_neighbors,
239 bool add_vehicle_starts_to_neighbors) {
241 const int size = routing_model.Size();
242 const int size_with_vehicle_nodes =
size + routing_model.vehicles();
243 node_index_to_neighbors_by_cost_class_.clear();
244 if (num_neighbors >=
size) {
245 all_nodes_.resize(
size);
246 std::iota(all_nodes_.begin(), all_nodes_.end(), 0);
249 node_index_to_neighbors_by_cost_class_.resize(size_with_vehicle_nodes);
251 const int num_cost_classes = routing_model.GetCostClassesCount();
252 for (
int node_index = 0; node_index < size_with_vehicle_nodes; node_index++) {
253 node_index_to_neighbors_by_cost_class_[node_index].resize(num_cost_classes);
254 for (
int cc = 0; cc < num_cost_classes; cc++) {
255 node_index_to_neighbors_by_cost_class_[node_index][cc] =
256 std::make_unique<SparseBitset<int>>(
size);
260 std::vector<std::pair< int64_t,
int>> cost_nodes;
261 cost_nodes.reserve(
size);
262 for (
int node_index = 0; node_index < size_with_vehicle_nodes; ++node_index) {
263 if (routing_model.IsStart(node_index) || routing_model.IsEnd(node_index)) {
269 for (
int cost_class = 0; cost_class < num_cost_classes; cost_class++) {
270 if (!routing_model.HasVehicleWithCostClassIndex(
271 RoutingCostClassIndex(cost_class))) {
276 for (
int after_node = 0; after_node <
size; ++after_node) {
277 if (after_node != node_index && !routing_model.IsStart(after_node)) {
278 cost_nodes.push_back(
279 std::make_pair(routing_model.GetArcCostForClass(
280 node_index, after_node, cost_class),
284 std::nth_element(cost_nodes.begin(),
285 cost_nodes.begin() + num_neighbors - 1,
287 cost_nodes.resize(num_neighbors);
289 std::sort(cost_nodes.begin(), cost_nodes.end());
291 auto& node_neighbors =
292 node_index_to_neighbors_by_cost_class_[node_index][cost_class];
293 for (
const auto& costed_node : cost_nodes) {
294 const int neighbor = costed_node.second;
295 node_neighbors->Set(neighbor);
298 DCHECK(!routing_model.IsEnd(neighbor) &&
299 !routing_model.IsStart(neighbor));
300 node_index_to_neighbors_by_cost_class_[neighbor][cost_class]->Set(
306 for (
int vehicle = 0; vehicle < routing_model.vehicles(); vehicle++) {
307 const int vehicle_start = routing_model.Start(vehicle);
308 if (add_vehicle_starts_to_neighbors) node_neighbors->Set(vehicle_start);
309 node_index_to_neighbors_by_cost_class_[vehicle_start][cost_class]->Set(
311 node_index_to_neighbors_by_cost_class_[routing_model.End(vehicle)]
319const RoutingModel::NodeNeighborsByCostClass*
320RoutingModel::GetOrCreateNodeNeighborsByCostClass(
321 double neighbors_ratio, int64_t min_neighbors,
double& neighbors_ratio_used,
322 bool add_vehicle_starts_to_neighbors) {
323 const int64_t num_non_start_end_nodes = Size() - vehicles();
324 neighbors_ratio_used = neighbors_ratio;
325 int num_neighbors = std::max(
328 if (neighbors_ratio == 1 || num_neighbors >= num_non_start_end_nodes - 1) {
329 neighbors_ratio_used = 1;
330 num_neighbors = Size();
332 return GetOrCreateNodeNeighborsByCostClass(num_neighbors,
333 add_vehicle_starts_to_neighbors);
336const RoutingModel::NodeNeighborsByCostClass*
337RoutingModel::GetOrCreateNodeNeighborsByCostClass(
338 int num_neighbors,
bool add_vehicle_starts_to_neighbors) {
339 const NodeNeighborsParameters params = {num_neighbors,
340 add_vehicle_starts_to_neighbors};
341 std::unique_ptr<NodeNeighborsByCostClass>* node_neighbors_by_cost_class_ptr =
343 if (node_neighbors_by_cost_class_ptr !=
nullptr) {
344 return node_neighbors_by_cost_class_ptr->get();
346 std::unique_ptr<NodeNeighborsByCostClass>& node_neighbors_by_cost_class =
347 node_neighbors_by_cost_class_per_size_
348 .insert(std::make_pair(params,
349 std::make_unique<NodeNeighborsByCostClass>()))
351 node_neighbors_by_cost_class->ComputeNeighbors(
352 *
this, num_neighbors, add_vehicle_starts_to_neighbors);
353 return node_neighbors_by_cost_class.get();
359template <
class A,
class B>
360static int64_t ReturnZero(A, B) {
369const int64_t RoutingModel::kNoPenalty = -1;
371const RoutingModel::DisjunctionIndex RoutingModel::kNoDisjunction(-1);
373const RoutingModel::DimensionIndex RoutingModel::kNoDimension(-1);
375RoutingModel::RoutingModel(
const RoutingIndexManager& index_manager)
379std::unique_ptr<Solver> CreateSolverFromParameters(
381 VLOG(1) <<
"Model parameters:\n" <<
parameters;
382 ConstraintSolverParameters solver_parameters =
384 : Solver::DefaultSolverParameters();
385 return std::make_unique<Solver>(
"Routing", solver_parameters);
389RoutingModel::RoutingModel(
const RoutingIndexManager& index_manager,
391 : solver_(CreateSolverFromParameters(
parameters)),
392 nodes_(index_manager.num_nodes()),
393 vehicles_(index_manager.num_vehicles()),
394 max_active_vehicles_(vehicles_),
395 fixed_cost_of_vehicle_(vehicles_, 0),
396 cost_class_index_of_vehicle_(vehicles_, CostClassIndex(-1)),
397 linear_cost_factor_of_vehicle_(vehicles_, 0),
398 quadratic_cost_factor_of_vehicle_(vehicles_, 0),
399 vehicle_amortized_cost_factors_set_(
false),
400 vehicle_used_when_empty_(vehicles_,
false),
402 costs_are_homogeneous_across_vehicles_(
404 cache_callbacks_(
false),
405 vehicle_class_index_of_vehicle_(vehicles_, VehicleClassIndex(-1)),
406 vehicle_pickup_delivery_policy_(vehicles_, PICKUP_AND_DELIVERY_NO_ORDER),
407 has_hard_type_incompatibilities_(
false),
408 has_temporal_type_incompatibilities_(
false),
409 has_same_vehicle_type_requirements_(
false),
410 has_temporal_type_requirements_(
false),
412 paths_metadata_(index_manager),
413 manager_(index_manager),
414 finalizer_variables_(
std::make_unique<FinalizerVariables>(solver_.get())),
415 interrupt_cp_sat_(
false),
416 interrupt_cp_(
false) {
418 vehicle_to_transit_cost_.assign(
419 vehicles_, RegisterTransitCallback(
420 ReturnZero<int64_t, int64_t>,
421 RoutingModel::kTransitEvaluatorSignPositiveOrZero));
424 cache_callbacks_ = (nodes_ <=
parameters.max_callback_cache_size());
427 start_end_count_ = index_manager.num_unique_depots();
430 const int64_t
size = Size();
431 index_to_pickup_positions_.resize(
size);
432 index_to_delivery_positions_.resize(
size);
433 index_to_visit_type_.resize(index_manager.num_indices(),
kUnassigned);
434 index_to_type_policy_.resize(index_manager.num_indices());
436 const std::vector<RoutingIndexManager::NodeIndex>& index_to_node =
437 index_manager.GetIndexToNodeMap();
438 index_to_equivalence_class_.resize(index_manager.num_indices());
439 for (
int i = 0;
i < index_to_node.size(); ++
i) {
440 index_to_equivalence_class_[
i] = index_to_node[
i].value();
442 allowed_vehicles_.resize(Size() + vehicles_);
445void RoutingModel::Initialize() {
446 const int size = Size();
448 solver_->MakeIntVarArray(
size, 0,
size + vehicles_ - 1,
"Nexts", &nexts_);
449 solver_->AddConstraint(solver_->MakeAllDifferent(nexts_,
false));
450 index_to_disjunctions_.resize(
size + vehicles_);
453 solver_->MakeIntVarArray(
size + vehicles_, -1, vehicles_ - 1,
"Vehicles",
456 solver_->MakeBoolVarArray(
size,
"Active", &active_);
458 solver_->MakeBoolVarArray(vehicles_,
"ActiveVehicle", &vehicle_active_);
460 solver_->MakeBoolVarArray(vehicles_,
"VehicleCostsConsidered",
461 &vehicle_route_considered_);
463 solver_->MakeBoolVarArray(
size + vehicles_,
"IsBoundToEnd",
467 cost_cache_.resize(
size + vehicles_, {
kUnassigned, CostClassIndex(-1), 0});
468 preassignment_ = solver_->MakeAssignment();
471RoutingModel::~RoutingModel() {
475 absl::flat_hash_set<RangeIntToIntFunction*> value_functions_delete;
476 absl::flat_hash_set<RangeMinMaxIndexFunction*> index_functions_delete;
477 for (
const auto& cache_line : state_dependent_transit_evaluators_cache_) {
478 for (
const auto& key_transit : *cache_line) {
479 value_functions_delete.insert(key_transit.second.transit);
480 index_functions_delete.insert(key_transit.second.transit_plus_identity);
487int RoutingModel::RegisterUnaryTransitVector(std::vector<int64_t> values) {
488 TransitEvaluatorSign sign = kTransitEvaluatorSignUnknown;
489 if (std::all_of(std::cbegin(values), std::cend(values),
490 [](int64_t transit) {
return transit >= 0; })) {
491 sign = kTransitEvaluatorSignPositiveOrZero;
492 }
else if (std::all_of(std::cbegin(values), std::cend(values),
493 [](int64_t transit) {
return transit <= 0; })) {
494 sign = kTransitEvaluatorSignNegativeOrZero;
496 return RegisterUnaryTransitCallback(
497 [
this, values = std::move(values)](int64_t i) {
498 return values[manager_.IndexToNode(i).value()];
503int RoutingModel::RegisterUnaryTransitCallback(TransitCallback1
callback,
504 TransitEvaluatorSign sign) {
505 const int index = unary_transit_evaluators_.size();
506 unary_transit_evaluators_.push_back(std::move(
callback));
507 return RegisterTransitCallback(
508 [
this,
index](
int i,
int ) {
509 return unary_transit_evaluators_[
index](
i);
514int RoutingModel::RegisterTransitMatrix(
515 std::vector<std::vector<int64_t> > values) {
518 bool all_transits_geq_zero =
true;
519 bool all_transits_leq_zero =
true;
520 for (
const std::vector<int64_t>& transit_values : values) {
521 for (
const int64_t
value : transit_values) {
522 all_transits_leq_zero &=
value <= 0;
523 all_transits_geq_zero &=
value >= 0;
525 if (!all_transits_geq_zero && !all_transits_leq_zero)
break;
527 const TransitEvaluatorSign sign =
528 all_transits_geq_zero
529 ? kTransitEvaluatorSignPositiveOrZero
530 : (all_transits_leq_zero ? kTransitEvaluatorSignNegativeOrZero
531 : kTransitEvaluatorSignUnknown);
532 return RegisterTransitCallback(
533 [
this, values = std::move(values)](int64_t i, int64_t j) {
534 return values[manager_.IndexToNode(i).value()]
535 [manager_.IndexToNode(j).value()];
540int RoutingModel::RegisterTransitCallback(TransitCallback2
callback,
541 TransitEvaluatorSign sign) {
542 if (cache_callbacks_) {
543 TransitEvaluatorSign actual_sign = sign;
544 const int size = Size() + vehicles();
545 std::vector<int64_t> cache(
size *
size, 0);
546 bool all_transits_geq_zero =
true;
547 bool all_transits_leq_zero =
true;
548 for (
int i = 0;
i <
size; ++
i) {
549 for (
int j = 0; j <
size; ++j) {
552 all_transits_geq_zero &=
value >= 0;
553 all_transits_leq_zero &=
value <= 0;
557 all_transits_geq_zero
558 ? kTransitEvaluatorSignPositiveOrZero
559 : (all_transits_leq_zero ? kTransitEvaluatorSignNegativeOrZero
560 : kTransitEvaluatorSignUnknown);
561 transit_evaluators_.push_back(
562 [cache = std::move(cache),
size](int64_t i, int64_t j) {
563 return cache[
i *
size + j];
565 DCHECK(sign == kTransitEvaluatorSignUnknown || actual_sign == sign);
567 transit_evaluators_.push_back(std::move(
callback));
569 if (transit_evaluators_.size() != unary_transit_evaluators_.size()) {
570 DCHECK_EQ(transit_evaluators_.size(), unary_transit_evaluators_.size() + 1);
571 unary_transit_evaluators_.push_back(
nullptr);
573 transit_evaluator_sign_.push_back(sign);
574 return transit_evaluators_.size() - 1;
577int RoutingModel::RegisterStateDependentTransitCallback(
579 state_dependent_transit_evaluators_cache_.push_back(
580 std::make_unique<StateDependentTransitCallbackCache>());
581 StateDependentTransitCallbackCache*
const cache =
582 state_dependent_transit_evaluators_cache_.back().get();
583 state_dependent_transit_evaluators_.push_back(
584 [cache,
callback](int64_t i, int64_t j) {
585 StateDependentTransit
value;
588 cache->insert({CacheKey(i, j),
value});
591 return state_dependent_transit_evaluators_.size() - 1;
594void RoutingModel::AddNoCycleConstraintInternal() {
595 if (no_cycle_constraint_ ==
nullptr) {
596 no_cycle_constraint_ = solver_->MakeNoCycle(nexts_, active_);
597 solver_->AddConstraint(no_cycle_constraint_);
601bool RoutingModel::AddDimension(
int evaluator_index, int64_t slack_max,
602 int64_t capacity,
bool fix_start_cumul_to_zero,
603 const std::string&
name) {
604 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
605 std::vector<int64_t> capacities(vehicles_, capacity);
606 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
607 std::move(capacities),
608 fix_start_cumul_to_zero,
name);
611bool RoutingModel::AddDimensionWithVehicleTransits(
612 const std::vector<int>& evaluator_indices, int64_t slack_max,
613 int64_t capacity,
bool fix_start_cumul_to_zero,
const std::string&
name) {
614 std::vector<int64_t> capacities(vehicles_, capacity);
615 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
616 std::move(capacities),
617 fix_start_cumul_to_zero,
name);
620bool RoutingModel::AddDimensionWithVehicleCapacity(
621 int evaluator_index, int64_t slack_max,
622 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
623 const std::string&
name) {
624 const std::vector<int> evaluator_indices(vehicles_, evaluator_index);
625 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
626 std::move(vehicle_capacities),
627 fix_start_cumul_to_zero,
name);
630bool RoutingModel::AddDimensionWithVehicleTransitAndCapacity(
631 const std::vector<int>& evaluator_indices, int64_t slack_max,
632 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
633 const std::string&
name) {
634 return AddDimensionWithCapacityInternal(evaluator_indices, slack_max,
635 std::move(vehicle_capacities),
636 fix_start_cumul_to_zero,
name);
639bool RoutingModel::AddDimensionWithCapacityInternal(
640 const std::vector<int>& evaluator_indices, int64_t slack_max,
641 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
642 const std::string&
name) {
643 CHECK_EQ(vehicles_, vehicle_capacities.size());
644 return InitializeDimensionInternal(
645 evaluator_indices, std::vector<int>(), slack_max, fix_start_cumul_to_zero,
646 new RoutingDimension(
this, std::move(vehicle_capacities),
name,
nullptr));
649bool RoutingModel::InitializeDimensionInternal(
650 const std::vector<int>& evaluator_indices,
651 const std::vector<int>& state_dependent_evaluator_indices,
652 int64_t slack_max,
bool fix_start_cumul_to_zero,
653 RoutingDimension* dimension) {
654 CHECK(dimension !=
nullptr);
655 CHECK_EQ(vehicles_, evaluator_indices.size());
656 CHECK((dimension->base_dimension_ ==
nullptr &&
657 state_dependent_evaluator_indices.empty()) ||
658 vehicles_ == state_dependent_evaluator_indices.size());
659 if (!HasDimension(dimension->name())) {
660 const DimensionIndex dimension_index(dimensions_.size());
661 dimension_name_to_index_[dimension->name()] = dimension_index;
662 dimensions_.push_back(dimension);
663 dimension->Initialize(evaluator_indices, state_dependent_evaluator_indices,
665 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
666 nexts_, active_, dimension->cumuls(), dimension->transits()));
667 if (fix_start_cumul_to_zero) {
668 for (
int i = 0;
i < vehicles_; ++
i) {
669 IntVar*
const start_cumul = dimension->CumulVar(Start(i));
670 CHECK_EQ(0, start_cumul->Min());
671 start_cumul->SetValue(0);
680std::pair<int, bool> RoutingModel::AddConstantDimensionWithSlack(
681 int64_t
value, int64_t capacity, int64_t slack_max,
682 bool fix_start_cumul_to_zero,
const std::string& dimension_name) {
683 const TransitEvaluatorSign sign =
value < 0
684 ? kTransitEvaluatorSignNegativeOrZero
685 : kTransitEvaluatorSignPositiveOrZero;
686 const int evaluator_index =
687 RegisterUnaryTransitCallback([
value](int64_t) {
return value; }, sign);
688 return std::make_pair(evaluator_index,
689 AddDimension(evaluator_index, slack_max, capacity,
690 fix_start_cumul_to_zero, dimension_name));
693std::pair<int, bool> RoutingModel::AddVectorDimension(
694 std::vector<int64_t> values, int64_t capacity,
bool fix_start_cumul_to_zero,
695 const std::string& dimension_name) {
696 const int evaluator_index = RegisterUnaryTransitVector(std::move(values));
697 return std::make_pair(evaluator_index,
698 AddDimension(evaluator_index, 0, capacity,
699 fix_start_cumul_to_zero, dimension_name));
702std::pair<int, bool> RoutingModel::AddMatrixDimension(
703 std::vector<std::vector<int64_t>> values, int64_t capacity,
704 bool fix_start_cumul_to_zero,
const std::string& dimension_name) {
705 const int evaluator_index = RegisterTransitMatrix(std::move(values));
706 return std::make_pair(evaluator_index,
707 AddDimension(evaluator_index, 0, capacity,
708 fix_start_cumul_to_zero, dimension_name));
715class RangeMakeElementExpr :
public BaseIntExpr {
717 RangeMakeElementExpr(
const RangeIntToIntFunction*
callback, IntVar*
index,
719 : BaseIntExpr(s), callback_(ABSL_DIE_IF_NULL(
callback)), index_(
index) {
720 CHECK(callback_ !=
nullptr);
721 CHECK(
index !=
nullptr);
724 int64_t Min()
const override {
726 const int idx_min = index_->Min();
727 const int idx_max = index_->Max() + 1;
728 return (idx_min < idx_max) ? callback_->RangeMin(idx_min, idx_max)
729 : std::numeric_limits<int64_t>::max();
731 void SetMin(int64_t new_min)
override {
732 const int64_t old_min = Min();
733 const int64_t old_max = Max();
734 if (old_min < new_min && new_min <= old_max) {
735 const int64_t old_idx_min = index_->Min();
736 const int64_t old_idx_max = index_->Max() + 1;
737 if (old_idx_min < old_idx_max) {
738 const int64_t new_idx_min = callback_->RangeFirstInsideInterval(
739 old_idx_min, old_idx_max, new_min, old_max + 1);
740 index_->SetMin(new_idx_min);
741 if (new_idx_min < old_idx_max) {
742 const int64_t new_idx_max = callback_->RangeLastInsideInterval(
743 new_idx_min, old_idx_max, new_min, old_max + 1);
744 index_->SetMax(new_idx_max);
749 int64_t Max()
const override {
751 const int idx_min = index_->Min();
752 const int idx_max = index_->Max() + 1;
753 return (idx_min < idx_max) ? callback_->RangeMax(idx_min, idx_max)
754 : std::numeric_limits<int64_t>::min();
756 void SetMax(int64_t new_max)
override {
757 const int64_t old_min = Min();
758 const int64_t old_max = Max();
759 if (old_min <= new_max && new_max < old_max) {
760 const int64_t old_idx_min = index_->Min();
761 const int64_t old_idx_max = index_->Max() + 1;
762 if (old_idx_min < old_idx_max) {
763 const int64_t new_idx_min = callback_->RangeFirstInsideInterval(
764 old_idx_min, old_idx_max, old_min, new_max + 1);
765 index_->SetMin(new_idx_min);
766 if (new_idx_min < old_idx_max) {
767 const int64_t new_idx_max = callback_->RangeLastInsideInterval(
768 new_idx_min, old_idx_max, old_min, new_max + 1);
769 index_->SetMax(new_idx_max);
774 void WhenRange(Demon* d)
override { index_->WhenRange(d); }
777 const RangeIntToIntFunction*
const callback_;
778 IntVar*
const index_;
781IntExpr* MakeRangeMakeElementExpr(
const RangeIntToIntFunction*
callback,
782 IntVar*
index, Solver* s) {
783 return s->RegisterIntExpr(
788bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity(
789 const std::vector<int>& dependent_transits,
790 const RoutingDimension* base_dimension, int64_t slack_max,
791 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
792 const std::string&
name) {
793 const std::vector<int> pure_transits(vehicles_, 0);
794 return AddDimensionDependentDimensionWithVehicleCapacity(
795 pure_transits, dependent_transits, base_dimension, slack_max,
796 std::move(vehicle_capacities), fix_start_cumul_to_zero,
name);
799bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity(
800 int transit,
const RoutingDimension* dimension, int64_t slack_max,
801 int64_t vehicle_capacity,
bool fix_start_cumul_to_zero,
802 const std::string&
name) {
803 return AddDimensionDependentDimensionWithVehicleCapacity(
804 0, transit, dimension, slack_max, vehicle_capacity,
805 fix_start_cumul_to_zero,
name);
808bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacityInternal(
809 const std::vector<int>& pure_transits,
810 const std::vector<int>& dependent_transits,
811 const RoutingDimension* base_dimension, int64_t slack_max,
812 std::vector<int64_t> vehicle_capacities,
bool fix_start_cumul_to_zero,
813 const std::string&
name) {
814 CHECK_EQ(vehicles_, vehicle_capacities.size());
815 RoutingDimension* new_dimension =
nullptr;
816 if (base_dimension ==
nullptr) {
817 new_dimension =
new RoutingDimension(
this, std::move(vehicle_capacities),
818 name, RoutingDimension::SelfBased());
820 new_dimension =
new RoutingDimension(
this, std::move(vehicle_capacities),
821 name, base_dimension);
823 return InitializeDimensionInternal(pure_transits, dependent_transits,
824 slack_max, fix_start_cumul_to_zero,
828bool RoutingModel::AddDimensionDependentDimensionWithVehicleCapacity(
829 int pure_transit,
int dependent_transit,
830 const RoutingDimension* base_dimension, int64_t slack_max,
831 int64_t vehicle_capacity,
bool fix_start_cumul_to_zero,
832 const std::string&
name) {
833 std::vector<int> pure_transits(vehicles_, pure_transit);
834 std::vector<int> dependent_transits(vehicles_, dependent_transit);
835 std::vector<int64_t> vehicle_capacities(vehicles_, vehicle_capacity);
836 return AddDimensionDependentDimensionWithVehicleCapacityInternal(
837 pure_transits, dependent_transits, base_dimension, slack_max,
838 std::move(vehicle_capacities), fix_start_cumul_to_zero,
name);
841RoutingModel::StateDependentTransit RoutingModel::MakeStateDependentTransit(
842 const std::function<int64_t(int64_t)>& f, int64_t domain_start,
843 int64_t domain_end) {
844 const std::function<int64_t(int64_t)> g = [&f](int64_t
x) {
853std::vector<std::string> RoutingModel::GetAllDimensionNames()
const {
854 std::vector<std::string> dimension_names;
855 for (
const auto& dimension_name_index : dimension_name_to_index_) {
856 dimension_names.push_back(dimension_name_index.first);
858 std::sort(dimension_names.begin(), dimension_names.end());
859 return dimension_names;
862GlobalDimensionCumulOptimizer* RoutingModel::GetMutableGlobalCumulLPOptimizer(
863 const RoutingDimension& dimension)
const {
864 const int optimizer_index = GetGlobalCumulOptimizerIndex(dimension);
865 return optimizer_index < 0
867 : global_dimension_optimizers_[optimizer_index].lp_optimizer.get();
870GlobalDimensionCumulOptimizer* RoutingModel::GetMutableGlobalCumulMPOptimizer(
871 const RoutingDimension& dimension)
const {
872 const int optimizer_index = GetGlobalCumulOptimizerIndex(dimension);
873 return optimizer_index < 0
875 : global_dimension_optimizers_[optimizer_index].mp_optimizer.get();
878int RoutingModel::GetGlobalCumulOptimizerIndex(
879 const RoutingDimension& dimension)
const {
881 const DimensionIndex dim_index = GetDimensionIndex(dimension.name());
882 if (dim_index < 0 || dim_index >= global_optimizer_index_.size() ||
883 global_optimizer_index_[dim_index] < 0) {
886 const int optimizer_index = global_optimizer_index_[dim_index];
887 DCHECK_LT(optimizer_index, global_dimension_optimizers_.size());
888 return optimizer_index;
891LocalDimensionCumulOptimizer* RoutingModel::GetMutableLocalCumulLPOptimizer(
892 const RoutingDimension& dimension)
const {
893 const int optimizer_index = GetLocalCumulOptimizerIndex(dimension);
894 return optimizer_index < 0
896 : local_dimension_optimizers_[optimizer_index].lp_optimizer.get();
899LocalDimensionCumulOptimizer* RoutingModel::GetMutableLocalCumulMPOptimizer(
900 const RoutingDimension& dimension)
const {
901 const int optimizer_index = GetLocalCumulOptimizerIndex(dimension);
902 return optimizer_index < 0
904 : local_dimension_optimizers_[optimizer_index].mp_optimizer.get();
907int RoutingModel::GetLocalCumulOptimizerIndex(
908 const RoutingDimension& dimension)
const {
910 const DimensionIndex dim_index = GetDimensionIndex(dimension.name());
911 if (dim_index < 0 || dim_index >= local_optimizer_index_.size() ||
912 local_optimizer_index_[dim_index] < 0) {
915 const int optimizer_index = local_optimizer_index_[dim_index];
916 DCHECK_LT(optimizer_index, local_dimension_optimizers_.size());
917 return optimizer_index;
920bool RoutingModel::HasDimension(absl::string_view dimension_name)
const {
921 return dimension_name_to_index_.contains(dimension_name);
924RoutingModel::DimensionIndex RoutingModel::GetDimensionIndex(
925 const std::string& dimension_name)
const {
930const RoutingDimension& RoutingModel::GetDimensionOrDie(
931 const std::string& dimension_name)
const {
932 return *dimensions_[
gtl::FindOrDie(dimension_name_to_index_, dimension_name)];
935RoutingDimension* RoutingModel::GetMutableDimension(
936 const std::string& dimension_name)
const {
937 const DimensionIndex
index = GetDimensionIndex(dimension_name);
938 if (
index != kNoDimension) {
939 return dimensions_[
index];
946using ResourceGroup = RoutingModel::ResourceGroup;
949ResourceGroup::Attributes::Attributes()
950 : start_domain_(Domain::AllValues()), end_domain_(Domain::AllValues()) {
954ResourceGroup::Attributes::Attributes(Domain start_domain, Domain end_domain)
955 : start_domain_(
std::move(start_domain)),
956 end_domain_(
std::move(end_domain)) {}
958const ResourceGroup::Attributes&
959ResourceGroup::Resource::GetDimensionAttributes(
960 const RoutingDimension* dimension)
const {
961 DimensionIndex dimension_index = model_->GetDimensionIndex(dimension->name());
962 DCHECK_NE(dimension_index, kNoDimension);
964 GetDefaultAttributes());
967void ResourceGroup::Resource::SetDimensionAttributes(
968 Attributes attributes,
const RoutingDimension* dimension) {
969 DCHECK(dimension_attributes_.empty())
970 <<
"As of 2021/07, each resource can only constrain a single dimension.";
972 const DimensionIndex dimension_index =
973 model_->GetDimensionIndex(dimension->name());
974 DCHECK_NE(dimension_index, kNoDimension);
975 DCHECK(!dimension_attributes_.contains(dimension_index));
976 dimension_attributes_[dimension_index] = std::move(attributes);
979const ResourceGroup::Attributes& ResourceGroup::Resource::GetDefaultAttributes()
981 static const Attributes*
const kAttributes =
new Attributes();
985ResourceGroup* RoutingModel::AddResourceGroup() {
986 DCHECK_EQ(resource_groups_.size(), resource_vars_.size());
989 resource_groups_.push_back(absl::WrapUnique(
new ResourceGroup(
this)));
990 const int rg_index = resource_groups_.back()->Index();
991 DCHECK_EQ(rg_index, resource_groups_.size() - 1);
995 resource_vars_.push_back({});
996 solver_->MakeIntVarArray(vehicles(), -1, std::numeric_limits<int64_t>::max(),
997 absl::StrCat(
"Resources[", rg_index,
"]"),
998 &resource_vars_.back());
1000 return resource_groups_[rg_index].get();
1003int ResourceGroup::AddResource(Attributes attributes,
1004 const RoutingDimension* dimension) {
1005 resources_.push_back(Resource(model_));
1006 resources_.back().SetDimensionAttributes(std::move(attributes), dimension);
1008 const DimensionIndex dimension_index =
1009 model_->GetDimensionIndex(dimension->name());
1010 DCHECK_NE(dimension_index, kNoDimension);
1011 affected_dimension_indices_.insert(dimension_index);
1013 DCHECK_EQ(affected_dimension_indices_.size(), 1)
1014 <<
"As of 2021/07, each ResourceGroup can only affect a single "
1015 "RoutingDimension at a time.";
1017 return resources_.size() - 1;
1020void ResourceGroup::NotifyVehicleRequiresAResource(
int vehicle) {
1021 DCHECK_LT(vehicle, vehicle_requires_resource_.size());
1022 if (vehicle_requires_resource_[vehicle])
return;
1023 vehicle_requires_resource_[vehicle] =
true;
1024 vehicles_requiring_resource_.push_back(vehicle);
1028struct ResourceClass {
1029 using DimensionIndex = RoutingModel::DimensionIndex;
1037 friend bool operator==(
const ResourceClass& c1,
const ResourceClass& c2) {
1038 return c1.dimension_attributes == c2.dimension_attributes &&
1039 c1.assignable_to_vehicle == c2.assignable_to_vehicle;
1041 template <
typename H>
1042 friend H AbslHashValue(H h,
const ResourceClass& c) {
1043 return H::combine(std::move(h), c.dimension_attributes,
1044 c.assignable_to_vehicle);
1049void ResourceGroup::ComputeResourceClasses() {
1050 resource_class_indices_.assign(resources_.size(), ResourceClassIndex(-1));
1051 resource_indices_per_class_.clear();
1053 absl::flat_hash_map<ResourceClass, ResourceClassIndex> resource_class_map;
1054 for (
int r = 0; r < resources_.size(); ++r) {
1055 ResourceClass resource_class;
1058 resource_class.dimension_attributes;
1059 dim_attributes.
resize(model_->dimensions_.size(), Attributes());
1060 for (
const auto& [dim_index, attributes] :
1061 resources_[r].dimension_attributes_) {
1062 dim_attributes[dim_index] = attributes;
1065 std::vector<bool>& assignable_to_v = resource_class.assignable_to_vehicle;
1066 assignable_to_v.resize(model_->vehicles_,
false);
1067 for (
int v : vehicles_requiring_resource_) {
1068 assignable_to_v[v] = IsResourceAllowedForVehicle(r, v);
1071 DCHECK_EQ(resource_indices_per_class_.size(), resource_class_map.size());
1072 const ResourceClassIndex num_resource_classes(resource_class_map.size());
1073 ResourceClassIndex& resource_class_index = resource_class_indices_[r];
1075 &resource_class_map, resource_class, num_resource_classes);
1076 if (resource_class_index == num_resource_classes) {
1078 resource_indices_per_class_.push_back({});
1080 resource_indices_per_class_[resource_class_index].push_back(r);
1084const std::vector<int>& RoutingModel::GetDimensionResourceGroupIndices(
1085 const RoutingDimension* dimension)
const {
1087 const DimensionIndex dim = GetDimensionIndex(dimension->name());
1088 DCHECK_NE(dim, kNoDimension);
1089 return dimension_resource_group_indices_[dim];
1092RoutingModel::SecondaryOptimizer::SecondaryOptimizer(
1093 RoutingModel*
model, RoutingSearchParameters* search_parameters,
1094 int64_t solve_period)
1096 search_parameters_(search_parameters),
1097 solve_period_(solve_period) {
1098 DCHECK(model_ !=
nullptr);
1099 state_ = model_->solver()->MakeAssignment();
1100 Assignment::IntContainer* container = state_->MutableIntVarContainer();
1101 const std::vector<IntVar*> nexts = model_->Nexts();
1102 container->Resize(nexts.size());
1103 for (
int i = 0;
i < nexts.size(); ++
i) {
1104 IntVar* next_var = nexts[
i];
1105 container->AddAtPosition(next_var, i)->SetValue(i);
1106 var_to_index_[next_var] =
i;
1108 IntVar* cost = (model_->CostVar() !=
nullptr)
1110 : model_->solver()->MakeIntConst(0);
1111 state_->AddObjective(cost);
1114bool RoutingModel::SecondaryOptimizer::Solve(
1115 const std::vector<RoutingModel::VariableValuePair>& in_state,
1116 std::vector<RoutingModel::VariableValuePair>* out_state) {
1117 if (solve_period_ <= 0)
return false;
1118 if (call_count_ == solve_period_) {
1124 Assignment::IntContainer* container = state_->MutableIntVarContainer();
1125 for (
const auto [
var,
value] : in_state) {
1126 container->MutableElement(
var)->SetValue(
value);
1128 if (call_count_ != 0)
return false;
1129 absl::flat_hash_set<IntVar*> touched;
1130 const Assignment*
solution = model_->FastSolveFromAssignmentWithParameters(
1131 state_, *search_parameters_,
1133 if (
solution ==
nullptr || touched.empty())
return false;
1134 for (IntVar*
var : touched) {
1135 const int index = var_to_index_[
var];
1138 container->MutableElement(
index)->SetValue(
value);
1143void RoutingModel::SetArcCostEvaluatorOfAllVehicles(
int evaluator_index) {
1144 CHECK_LT(0, vehicles_);
1145 for (
int i = 0;
i < vehicles_; ++
i) {
1146 SetArcCostEvaluatorOfVehicle(evaluator_index, i);
1150void RoutingModel::SetArcCostEvaluatorOfVehicle(
int evaluator_index,
1152 CHECK_LT(vehicle, vehicles_);
1153 CHECK_LT(evaluator_index, transit_evaluators_.size());
1154 vehicle_to_transit_cost_[vehicle] = evaluator_index;
1157void RoutingModel::SetFixedCostOfAllVehicles(int64_t cost) {
1158 for (
int i = 0;
i < vehicles_; ++
i) {
1159 SetFixedCostOfVehicle(cost, i);
1163int64_t RoutingModel::GetFixedCostOfVehicle(
int vehicle)
const {
1164 CHECK_LT(vehicle, vehicles_);
1165 return fixed_cost_of_vehicle_[vehicle];
1168void RoutingModel::SetFixedCostOfVehicle(int64_t cost,
int vehicle) {
1169 CHECK_LT(vehicle, vehicles_);
1171 fixed_cost_of_vehicle_[vehicle] = cost;
1174void RoutingModel::SetPathEnergyCostOfVehicle(
const std::string& force,
1176 int64_t unit_cost,
int vehicle) {
1177 DCHECK_LE(0, vehicle);
1178 DCHECK_LT(vehicle, vehicles_);
1179 DCHECK_LE(0, unit_cost);
1182 if (unit_cost == 0)
return;
1183 std::vector<int64_t>& vehicle_unit_costs =
1184 force_distance_to_vehicle_unit_costs_[std::make_pair(force,
distance)];
1185 if (vehicle_unit_costs.size() < vehicles_) {
1186 vehicle_unit_costs.resize(vehicles_, 0);
1188 vehicle_unit_costs[vehicle] = unit_cost;
1191void RoutingModel::SetAmortizedCostFactorsOfAllVehicles(
1192 int64_t linear_cost_factor, int64_t quadratic_cost_factor) {
1193 for (
int v = 0; v < vehicles_; v++) {
1194 SetAmortizedCostFactorsOfVehicle(linear_cost_factor, quadratic_cost_factor,
1199void RoutingModel::SetAmortizedCostFactorsOfVehicle(
1200 int64_t linear_cost_factor, int64_t quadratic_cost_factor,
int vehicle) {
1201 CHECK_LT(vehicle, vehicles_);
1202 DCHECK_GE(linear_cost_factor, 0);
1203 DCHECK_GE(quadratic_cost_factor, 0);
1204 if (linear_cost_factor + quadratic_cost_factor > 0) {
1205 vehicle_amortized_cost_factors_set_ =
true;
1207 linear_cost_factor_of_vehicle_[vehicle] = linear_cost_factor;
1208 quadratic_cost_factor_of_vehicle_[vehicle] = quadratic_cost_factor;
1211void RoutingModel::FinalizeAllowedVehicles() {
1212 const std::vector<RoutingDimension*> unary_dimensions = GetUnaryDimensions();
1216 std::vector<int64_t> dimension_max_node_transit(unary_dimensions.size(), 0);
1217 for (
int i = 0;
i < unary_dimensions.size(); ++
i) {
1218 int64_t& max_node_transit = dimension_max_node_transit[
i];
1219 const RoutingDimension* dimension = unary_dimensions[
i];
1220 for (
int node = 0; node < Size(); ++node) {
1221 if (IsStart(node))
continue;
1222 for (
int callback_index : dimension->class_evaluators_) {
1223 max_node_transit = std::max(
1225 std::abs(UnaryTransitCallbackOrNull(callback_index)(node)));
1230 for (
int vehicle = 0; vehicle < vehicles_; ++vehicle) {
1234 for (
int i = 0;
i < unary_dimensions.size(); ++
i) {
1235 const RoutingDimension*
const dim = unary_dimensions[
i];
1236 const TransitCallback1& transit_evaluator =
1237 dim->GetUnaryTransitEvaluator(vehicle);
1238 DCHECK(transit_evaluator !=
nullptr);
1239 const int64_t capacity = dim->vehicle_capacities()[vehicle];
1240 if (capacity >= dimension_max_node_transit[i])
continue;
1242 for (
int node = 0; node < Size(); ++node) {
1243 if (IsStart(node))
continue;
1244 absl::flat_hash_set<int>& allowed_vehicles = allowed_vehicles_[node];
1245 if (!allowed_vehicles.empty() && !allowed_vehicles.contains(vehicle)) {
1249 if (std::abs(transit_evaluator(node)) <= capacity)
continue;
1253 if (allowed_vehicles.empty()) {
1258 for (
int v = 0; v < vehicles_; ++v) allowed_vehicles.insert(v);
1260 allowed_vehicles.erase(vehicle);
1261 if (allowed_vehicles.empty()) {
1266 allowed_vehicles.insert(-1);
1274const RoutingModel::CostClassIndex RoutingModel::kCostClassIndexOfZeroCost =
1277void RoutingModel::ComputeCostClasses(
1278 const RoutingSearchParameters& ) {
1280 cost_classes_.reserve(vehicles_);
1281 cost_classes_.clear();
1282 cost_class_index_of_vehicle_.assign(vehicles_, CostClassIndex(-1));
1283 absl::flat_hash_map<CostClass, CostClassIndex> cost_class_map;
1285 const CostClass zero_cost_class(0);
1286 cost_classes_.push_back(zero_cost_class);
1287 DCHECK_EQ(cost_classes_[kCostClassIndexOfZeroCost].evaluator_index, 0);
1288 cost_class_map[zero_cost_class] = kCostClassIndexOfZeroCost;
1293 has_vehicle_with_zero_cost_class_ =
false;
1294 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
1295 CostClass cost_class(vehicle_to_transit_cost_[vehicle]);
1298 for (
const RoutingDimension*
const dimension : dimensions_) {
1299 const int64_t span_coeff =
1300 dimension->vehicle_span_cost_coefficients()[vehicle];
1301 const int64_t slack_coeff =
1302 dimension->vehicle_slack_cost_coefficients()[vehicle];
1303 if (span_coeff == 0 && slack_coeff == 0)
continue;
1304 cost_class.dimension_transit_evaluator_class_and_cost_coefficient
1305 .push_back({dimension->vehicle_to_class(vehicle), span_coeff,
1306 slack_coeff, dimension});
1309 cost_class.dimension_transit_evaluator_class_and_cost_coefficient);
1311 const CostClassIndex num_cost_classes(cost_classes_.size());
1315 has_vehicle_with_zero_cost_class_ =
true;
1317 cost_classes_.push_back(cost_class);
1333 costs_are_homogeneous_across_vehicles_ &= has_vehicle_with_zero_cost_class_
1334 ? GetCostClassesCount() == 1
1335 : GetCostClassesCount() <= 2;
1340struct VehicleClass {
1341 using DimensionIndex = RoutingModel::DimensionIndex;
1372 friend bool operator==(
const VehicleClass& c1,
const VehicleClass& c2) {
1373 return c1.cost_class_index == c2.cost_class_index &&
1374 c1.fixed_cost == c2.fixed_cost &&
1375 c1.used_when_empty == c2.used_when_empty &&
1376 c1.start_equivalence_class == c2.start_equivalence_class &&
1377 c1.end_equivalence_class == c2.end_equivalence_class &&
1378 c1.dimension_start_cumuls_min == c2.dimension_start_cumuls_min &&
1379 c1.dimension_start_cumuls_max == c2.dimension_start_cumuls_max &&
1380 c1.dimension_end_cumuls_min == c2.dimension_end_cumuls_min &&
1381 c1.dimension_end_cumuls_max == c2.dimension_end_cumuls_max &&
1382 c1.dimension_capacities == c2.dimension_capacities &&
1383 c1.dimension_evaluator_classes == c2.dimension_evaluator_classes &&
1384 c1.visitable_nodes_hash == c2.visitable_nodes_hash &&
1385 c1.group_allowed_resources_hash == c2.group_allowed_resources_hash;
1387 template <
typename H>
1389 return H::combine(std::move(h),
c.cost_class_index,
c.fixed_cost,
1390 c.used_when_empty,
c.start_equivalence_class,
1391 c.end_equivalence_class,
c.dimension_start_cumuls_min,
1392 c.dimension_start_cumuls_max,
c.dimension_end_cumuls_min,
1393 c.dimension_end_cumuls_max,
c.dimension_capacities,
1394 c.dimension_evaluator_classes,
c.visitable_nodes_hash,
1395 c.group_allowed_resources_hash);
1401void RoutingModel::ComputeVehicleClasses() {
1402 vehicle_class_index_of_vehicle_.assign(vehicles_, VehicleClassIndex(-1));
1403 absl::flat_hash_map<VehicleClass, VehicleClassIndex> vehicle_class_map;
1404 std::vector<bool> node_is_visitable(Size(),
true);
1405 const auto bool_vec_hash = absl::Hash<std::vector<bool>>();
1406 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
1408 vehicle_class.cost_class_index = cost_class_index_of_vehicle_[vehicle];
1410 vehicle_class.used_when_empty = vehicle_used_when_empty_[vehicle];
1412 index_to_equivalence_class_[Start(vehicle)];
1414 index_to_equivalence_class_[End(vehicle)];
1415 for (
const RoutingDimension*
const dimension : dimensions_) {
1416 IntVar*
const start_cumul_var = dimension->cumuls()[Start(vehicle)];
1418 start_cumul_var->Min());
1420 start_cumul_var->Max());
1421 IntVar*
const end_cumul_var = dimension->cumuls()[End(vehicle)];
1422 vehicle_class.dimension_end_cumuls_min.push_back(end_cumul_var->Min());
1423 vehicle_class.dimension_end_cumuls_max.push_back(end_cumul_var->Max());
1425 dimension->vehicle_capacities()[vehicle]);
1427 dimension->vehicle_to_class(vehicle));
1429 node_is_visitable.assign(Size(),
true);
1431 DCHECK(!IsEnd(
index));
1432 if (IsStart(
index))
continue;
1433 if (!vehicle_vars_[
index]->Contains(vehicle) ||
1434 !IsVehicleAllowedForIndex(vehicle,
index)) {
1435 node_is_visitable[
index] =
false;
1438 vehicle_class.visitable_nodes_hash = bool_vec_hash(node_is_visitable);
1440 std::vector<int64_t>& allowed_resources_hash =
1442 allowed_resources_hash.reserve(resource_groups_.size());
1443 for (
int rg_index = 0; rg_index < resource_groups_.size(); rg_index++) {
1444 const ResourceGroup& resource_group = *resource_groups_[rg_index];
1445 if (!resource_group.VehicleRequiresAResource(vehicle)) {
1446 allowed_resources_hash.push_back(-1);
1449 const std::vector<IntVar*>& resource_vars = resource_vars_[rg_index];
1450 std::vector<bool> resource_allowed_for_vehicle(resource_group.Size(),
1452 for (
int resource = 0; resource < resource_group.Size(); resource++) {
1453 if (!resource_vars[vehicle]->Contains(resource) ||
1454 !resource_group.IsResourceAllowedForVehicle(resource, vehicle)) {
1455 resource_allowed_for_vehicle[resource] =
false;
1458 allowed_resources_hash.push_back(
1459 bool_vec_hash(resource_allowed_for_vehicle));
1461 DCHECK_EQ(allowed_resources_hash.size(), resource_groups_.size());
1463 const VehicleClassIndex num_vehicle_classes(vehicle_class_map.size());
1467 num_vehicle_classes_ = vehicle_class_map.size();
1470void RoutingModel::ComputeVehicleTypes() {
1471 const int nodes_squared = nodes_ * nodes_;
1472 std::vector<int>& type_index_of_vehicle =
1473 vehicle_type_container_.type_index_of_vehicle;
1474 std::vector<std::set<VehicleTypeContainer::VehicleClassEntry>>&
1475 sorted_vehicle_classes_per_type =
1476 vehicle_type_container_.sorted_vehicle_classes_per_type;
1477 std::vector<std::deque<int>>& vehicles_per_vehicle_class =
1478 vehicle_type_container_.vehicles_per_vehicle_class;
1480 type_index_of_vehicle.resize(vehicles_);
1481 sorted_vehicle_classes_per_type.clear();
1482 sorted_vehicle_classes_per_type.reserve(vehicles_);
1483 vehicles_per_vehicle_class.clear();
1484 vehicles_per_vehicle_class.resize(GetVehicleClassesCount());
1486 absl::flat_hash_map<int64_t, int> type_to_type_index;
1488 for (
int v = 0; v < vehicles_; v++) {
1489 const int start = manager_.IndexToNode(Start(v)).value();
1490 const int end = manager_.IndexToNode(End(v)).value();
1491 const int cost_class = GetCostClassIndexOfVehicle(v).value();
1492 const int64_t type = cost_class * nodes_squared +
start * nodes_ +
end;
1494 const auto& vehicle_type_added = type_to_type_index.insert(
1495 std::make_pair(type, type_to_type_index.size()));
1497 const int index = vehicle_type_added.first->second;
1499 const int vehicle_class = GetVehicleClassIndexOfVehicle(v).value();
1500 const VehicleTypeContainer::VehicleClassEntry class_entry = {
1503 if (vehicle_type_added.second) {
1505 DCHECK_EQ(sorted_vehicle_classes_per_type.size(),
index);
1506 sorted_vehicle_classes_per_type.push_back({class_entry});
1509 DCHECK_LT(
index, sorted_vehicle_classes_per_type.size());
1510 sorted_vehicle_classes_per_type[
index].insert(class_entry);
1513 type_index_of_vehicle[v] =
index;
1517void RoutingModel::ComputeResourceClasses() {
1518 for (
auto& resource_group : resource_groups_) {
1519 resource_group->ComputeResourceClasses();
1523void RoutingModel::FinalizeVisitTypes() {
1529 single_nodes_of_type_.clear();
1530 single_nodes_of_type_.resize(num_visit_types_);
1531 pair_indices_of_type_.clear();
1532 pair_indices_of_type_.resize(num_visit_types_);
1533 std::vector<absl::flat_hash_set<int>> pair_indices_added_for_type(
1537 const int visit_type = GetVisitType(
index);
1538 if (visit_type < 0) {
1541 const std::vector<PickupDeliveryPosition>& pickup_positions =
1542 index_to_pickup_positions_[
index];
1543 const std::vector<PickupDeliveryPosition>& delivery_positions =
1544 index_to_delivery_positions_[
index];
1545 if (pickup_positions.empty() && delivery_positions.empty()) {
1546 single_nodes_of_type_[visit_type].push_back(
index);
1548 for (
const std::vector<RoutingModel::PickupDeliveryPosition>* positions :
1549 {&pickup_positions, &delivery_positions}) {
1550 for (
const auto& [pair_index, unused] : *positions) {
1551 if (pair_indices_added_for_type[visit_type].insert(pair_index).second) {
1552 pair_indices_of_type_[visit_type].push_back(pair_index);
1558 TopologicallySortVisitTypes();
1561void RoutingModel::TopologicallySortVisitTypes() {
1562 if (!has_same_vehicle_type_requirements_ &&
1563 !has_temporal_type_requirements_) {
1566 std::vector<std::pair<double, double>> type_requirement_tightness(
1567 num_visit_types_, {0, 0});
1568 std::vector<absl::flat_hash_set<int>> type_to_dependent_types(
1570 SparseBitset<> types_in_requirement_graph(num_visit_types_);
1571 std::vector<int> in_degree(num_visit_types_, 0);
1572 for (
int type = 0; type < num_visit_types_; type++) {
1573 int num_alternative_required_types = 0;
1574 int num_required_sets = 0;
1575 for (
const std::vector<absl::flat_hash_set<int>>*
1576 required_type_alternatives :
1577 {&required_type_alternatives_when_adding_type_index_[type],
1578 &required_type_alternatives_when_removing_type_index_[type],
1579 &same_vehicle_required_type_alternatives_per_type_index_[type]}) {
1580 for (
const absl::flat_hash_set<int>& alternatives :
1581 *required_type_alternatives) {
1582 types_in_requirement_graph.Set(type);
1583 num_required_sets++;
1584 for (
int required_type : alternatives) {
1585 type_requirement_tightness[required_type].second +=
1586 1.0 / alternatives.size();
1587 types_in_requirement_graph.Set(required_type);
1588 num_alternative_required_types++;
1589 if (type_to_dependent_types[required_type].insert(type).second) {
1595 if (num_alternative_required_types > 0) {
1596 type_requirement_tightness[type].first += 1.0 * num_required_sets *
1598 num_alternative_required_types;
1603 topologically_sorted_visit_types_.clear();
1604 std::vector<int> current_types_with_zero_indegree;
1605 for (
int type : types_in_requirement_graph.PositionsSetAtLeastOnce()) {
1606 DCHECK(type_requirement_tightness[type].first > 0 ||
1607 type_requirement_tightness[type].second > 0);
1608 if (in_degree[type] == 0) {
1609 current_types_with_zero_indegree.push_back(type);
1613 int num_types_added = 0;
1614 while (!current_types_with_zero_indegree.empty()) {
1617 topologically_sorted_visit_types_.push_back({});
1618 std::vector<int>& topological_group =
1619 topologically_sorted_visit_types_.back();
1620 std::vector<int> next_types_with_zero_indegree;
1621 for (
int type : current_types_with_zero_indegree) {
1622 topological_group.push_back(type);
1624 for (
int dependent_type : type_to_dependent_types[type]) {
1625 DCHECK_GT(in_degree[dependent_type], 0);
1626 if (--in_degree[dependent_type] == 0) {
1627 next_types_with_zero_indegree.push_back(dependent_type);
1638 std::sort(topological_group.begin(), topological_group.end(),
1639 [&type_requirement_tightness](
int type1,
int type2) {
1640 const auto& tightness1 = type_requirement_tightness[type1];
1641 const auto& tightness2 = type_requirement_tightness[type2];
1642 return tightness1 > tightness2 ||
1643 (tightness1 == tightness2 && type1 < type2);
1646 current_types_with_zero_indegree.swap(next_types_with_zero_indegree);
1649 const int num_types_in_requirement_graph =
1650 types_in_requirement_graph.NumberOfSetCallsWithDifferentArguments();
1651 DCHECK_LE(num_types_added, num_types_in_requirement_graph);
1652 if (num_types_added < num_types_in_requirement_graph) {
1654 topologically_sorted_visit_types_.clear();
1658RoutingModel::DisjunctionIndex RoutingModel::AddDisjunction(
1659 const std::vector<int64_t>& indices, int64_t penalty,
1660 int64_t max_cardinality) {
1661 CHECK_GE(max_cardinality, 1);
1662 for (
int i = 0;
i < indices.size(); ++
i) {
1663 CHECK_NE(kUnassigned, indices[i]);
1666 const DisjunctionIndex disjunction_index(disjunctions_.size());
1667 disjunctions_.push_back({indices, {penalty, max_cardinality}});
1668 for (
const int64_t
index : indices) {
1669 index_to_disjunctions_[
index].push_back(disjunction_index);
1671 return disjunction_index;
1674bool RoutingModel::HasMandatoryDisjunctions()
const {
1675 for (
const auto& [indices,
value] : disjunctions_) {
1676 if (
value.penalty == kNoPenalty)
return true;
1681bool RoutingModel::HasMaxCardinalityConstrainedDisjunctions()
const {
1682 for (
const auto& [indices,
value] : disjunctions_) {
1683 if (indices.size() >
value.max_cardinality)
return true;
1688std::vector<std::pair<int64_t, int64_t>>
1689RoutingModel::GetPerfectBinaryDisjunctions()
const {
1690 std::vector<std::pair<int64_t, int64_t>> var_index_pairs;
1691 for (
const Disjunction& disjunction : disjunctions_) {
1692 const std::vector<int64_t>&
var_indices = disjunction.indices;
1696 if (index_to_disjunctions_[v0].
size() == 1 &&
1697 index_to_disjunctions_[v1].
size() == 1) {
1699 var_index_pairs.push_back({std::min(v0, v1), std::max(v0, v1)});
1702 std::sort(var_index_pairs.begin(), var_index_pairs.end());
1703 return var_index_pairs;
1706void RoutingModel::IgnoreDisjunctionsAlreadyForcedToZero() {
1708 for (Disjunction& disjunction : disjunctions_) {
1709 bool has_one_potentially_active_var =
false;
1710 for (
const int64_t
var_index : disjunction.indices) {
1712 has_one_potentially_active_var =
true;
1716 if (!has_one_potentially_active_var) {
1717 disjunction.value.max_cardinality = 0;
1722IntVar* RoutingModel::CreateDisjunction(DisjunctionIndex disjunction) {
1723 const std::vector<int64_t>& indices = disjunctions_[disjunction].indices;
1724 const int indices_size = indices.size();
1725 std::vector<IntVar*> disjunction_vars(indices_size);
1726 for (
int i = 0;
i < indices_size; ++
i) {
1727 const int64_t
index = indices[
i];
1728 CHECK_LT(
index, Size());
1729 disjunction_vars[
i] = ActiveVar(
index);
1731 const int64_t max_cardinality =
1732 disjunctions_[disjunction].value.max_cardinality;
1733 IntVar* no_active_var = solver_->MakeBoolVar();
1734 IntVar* number_active_vars = solver_->MakeIntVar(0, max_cardinality);
1735 solver_->AddConstraint(
1736 solver_->MakeSumEquality(disjunction_vars, number_active_vars));
1737 solver_->AddConstraint(solver_->MakeIsDifferentCstCt(
1738 number_active_vars, max_cardinality, no_active_var));
1739 const int64_t penalty = disjunctions_[disjunction].value.penalty;
1741 no_active_var->SetMax(0);
1744 return solver_->MakeProd(no_active_var, penalty)->Var();
1748void RoutingModel::AddSoftSameVehicleConstraint(
1749 const std::vector<int64_t>& indices, int64_t cost) {
1750 if (!indices.empty()) {
1751 ValuedNodes<int64_t> same_vehicle_cost;
1752 for (
const int64_t
index : indices) {
1753 same_vehicle_cost.indices.push_back(
index);
1755 same_vehicle_cost.value = cost;
1756 same_vehicle_costs_.push_back(same_vehicle_cost);
1760void RoutingModel::SetAllowedVehiclesForIndex(
const std::vector<int>& vehicles,
1763 auto& allowed_vehicles = allowed_vehicles_[
index];
1764 allowed_vehicles.clear();
1765 for (
int vehicle : vehicles) {
1766 allowed_vehicles.insert(vehicle);
1770void RoutingModel::AddPickupAndDelivery(int64_t pickup, int64_t delivery) {
1771 AddPickupAndDeliverySetsInternal({pickup}, {delivery});
1772 pickup_delivery_disjunctions_.push_back({kNoDisjunction, kNoDisjunction});
1775void RoutingModel::AddPickupAndDeliverySets(
1776 DisjunctionIndex pickup_disjunction,
1777 DisjunctionIndex delivery_disjunction) {
1778 AddPickupAndDeliverySetsInternal(
1779 GetDisjunctionNodeIndices(pickup_disjunction),
1780 GetDisjunctionNodeIndices(delivery_disjunction));
1781 pickup_delivery_disjunctions_.push_back(
1782 {pickup_disjunction, delivery_disjunction});
1785void RoutingModel::AddPickupAndDeliverySetsInternal(
1786 const std::vector<int64_t>& pickups,
1787 const std::vector<int64_t>& deliveries) {
1788 if (pickups.empty() || deliveries.empty()) {
1791 const int64_t
size = Size();
1792 const int pair_index = pickup_delivery_pairs_.size();
1793 for (
int pickup_index = 0; pickup_index < pickups.size(); pickup_index++) {
1794 const int64_t pickup = pickups[pickup_index];
1795 CHECK_LT(pickup,
size);
1796 index_to_pickup_positions_[pickup].push_back({pair_index, pickup_index});
1798 for (
int delivery_index = 0; delivery_index < deliveries.size();
1800 const int64_t delivery = deliveries[delivery_index];
1801 CHECK_LT(delivery,
size);
1802 index_to_delivery_positions_[delivery].push_back(
1803 {pair_index, delivery_index});
1805 pickup_delivery_pairs_.push_back({pickups, deliveries});
1808const std::vector<RoutingModel::PickupDeliveryPosition>&
1809RoutingModel::GetPickupPositions(int64_t node_index)
const {
1810 CHECK_LT(node_index, index_to_pickup_positions_.size());
1811 return index_to_pickup_positions_[node_index];
1814const std::vector<RoutingModel::PickupDeliveryPosition>&
1815RoutingModel::GetDeliveryPositions(int64_t node_index)
const {
1816 CHECK_LT(node_index, index_to_delivery_positions_.size());
1817 return index_to_delivery_positions_[node_index];
1820void RoutingModel::SetPickupAndDeliveryPolicyOfVehicle(
1821 PickupAndDeliveryPolicy policy,
int vehicle) {
1822 CHECK_LT(vehicle, vehicles_);
1823 vehicle_pickup_delivery_policy_[vehicle] = policy;
1826void RoutingModel::SetPickupAndDeliveryPolicyOfAllVehicles(
1827 PickupAndDeliveryPolicy policy) {
1828 CHECK_LT(0, vehicles_);
1829 for (
int i = 0;
i < vehicles_; ++
i) {
1830 SetPickupAndDeliveryPolicyOfVehicle(policy, i);
1834RoutingModel::PickupAndDeliveryPolicy
1835RoutingModel::GetPickupAndDeliveryPolicyOfVehicle(
int vehicle)
const {
1836 CHECK_LT(vehicle, vehicles_);
1837 return vehicle_pickup_delivery_policy_[vehicle];
1840int RoutingModel::GetNumOfSingletonNodes()
const {
1842 for (
int i = 0;
i < Nexts().size(); ++
i) {
1844 if (!IsStart(i) && !IsPickup(i) && !IsDelivery(i)) {
1851IntVar* RoutingModel::CreateSameVehicleCost(
int vehicle_index) {
1852 const std::vector<int64_t>& indices =
1853 same_vehicle_costs_[vehicle_index].indices;
1854 CHECK(!indices.empty());
1855 std::vector<IntVar*> vehicle_counts;
1856 solver_->MakeIntVarArray(vehicle_vars_.size() + 1, 0, indices.size() + 1,
1858 std::vector<int64_t> vehicle_values(vehicle_vars_.size() + 1);
1859 for (
int i = 0;
i < vehicle_vars_.size(); ++
i) {
1860 vehicle_values[
i] =
i;
1862 vehicle_values[vehicle_vars_.size()] = -1;
1863 std::vector<IntVar*> vehicle_vars;
1864 vehicle_vars.reserve(indices.size());
1865 for (
const int64_t
index : indices) {
1866 vehicle_vars.push_back(vehicle_vars_[
index]);
1868 solver_->AddConstraint(solver_->MakeDistribute(vehicle_vars, vehicle_counts));
1869 std::vector<IntVar*> vehicle_used;
1870 for (
int i = 0;
i < vehicle_vars_.size() + 1; ++
i) {
1871 vehicle_used.push_back(
1872 solver_->MakeIsGreaterOrEqualCstVar(vehicle_counts[i], 1));
1874 vehicle_used.push_back(solver_->MakeIntConst(-1));
1876 ->MakeProd(solver_->MakeMax(solver_->MakeSum(vehicle_used), 0),
1877 same_vehicle_costs_[vehicle_index].value)
1881void RoutingModel::AddLocalSearchOperator(LocalSearchOperator* ls_operator) {
1882 extra_operators_.push_back(ls_operator);
1885int64_t RoutingModel::GetDepot()
const {
1886 return vehicles() > 0 ? Start(0) : -1;
1891void RoutingModel::AppendHomogeneousArcCosts(
1892 const RoutingSearchParameters&
parameters,
int node_index,
1893 std::vector<IntVar*>* cost_elements) {
1894 CHECK(cost_elements !=
nullptr);
1895 const auto arc_cost_evaluator = [
this, node_index](int64_t next_index) {
1896 return GetHomogeneousCost(node_index, next_index);
1902 IntVar*
const base_cost_var =
1903 solver_->MakeIntVar(0, std::numeric_limits<int64_t>::max());
1904 solver_->AddConstraint(solver_->MakeLightElement(
1905 arc_cost_evaluator, base_cost_var, nexts_[node_index],
1906 [
this]() { return enable_deep_serialization_; }));
1908 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1909 cost_elements->push_back(
var);
1911 IntExpr*
const expr =
1912 solver_->MakeElement(arc_cost_evaluator, nexts_[node_index]);
1913 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
1914 cost_elements->push_back(
var);
1918void RoutingModel::AppendArcCosts(
const RoutingSearchParameters&
parameters,
1920 std::vector<IntVar*>* cost_elements) {
1921 CHECK(cost_elements !=
nullptr);
1922 DCHECK_GT(vehicles_, 0);
1927 IntVar*
const base_cost_var =
1928 solver_->MakeIntVar(0, std::numeric_limits<int64_t>::max());
1929 solver_->AddConstraint(solver_->MakeLightElement(
1930 [
this, node_index](int64_t
to, int64_t vehicle) {
1931 return GetArcCostForVehicle(node_index, to, vehicle);
1933 base_cost_var, nexts_[node_index], vehicle_vars_[node_index],
1934 [
this]() { return enable_deep_serialization_; }));
1936 solver_->MakeProd(base_cost_var, active_[node_index])->Var();
1937 cost_elements->push_back(
var);
1939 IntVar*
const vehicle_class_var =
1942 [
this](int64_t
index) {
1943 return SafeGetCostClassInt64OfVehicle(
index);
1945 vehicle_vars_[node_index])
1947 IntExpr*
const expr = solver_->MakeElement(
1951 nexts_[node_index], vehicle_class_var);
1952 IntVar*
const var = solver_->MakeProd(expr, active_[node_index])->Var();
1953 cost_elements->push_back(
var);
1957int RoutingModel::GetVehicleStartClass(int64_t start_index)
const {
1958 const int vehicle = VehicleIndex(start_index);
1959 if (vehicle != kUnassigned) {
1960 return GetVehicleClassIndexOfVehicle(vehicle).value();
1965std::string RoutingModel::FindErrorInSearchParametersForModel(
1966 const RoutingSearchParameters& search_parameters)
const {
1967 const FirstSolutionStrategy::Value first_solution_strategy =
1968 search_parameters.first_solution_strategy();
1969 if (GetFirstSolutionDecisionBuilder(search_parameters) ==
nullptr) {
1970 return absl::StrCat(
1971 "Undefined first solution strategy: ",
1972 FirstSolutionStrategy::Value_Name(first_solution_strategy),
1973 " (int value: ", first_solution_strategy,
")");
1975 if (search_parameters.first_solution_strategy() ==
1976 FirstSolutionStrategy::SWEEP &&
1977 sweep_arranger() ==
nullptr) {
1978 return "Undefined sweep arranger for ROUTING_SWEEP strategy.";
1983void RoutingModel::QuietCloseModel() {
1987void RoutingModel::CloseModel() {
1994 same_vehicle_components_.SetNumberOfNodes(
model->Size());
1995 for (
const std::string&
name :
model->GetAllDimensionNames()) {
1996 RoutingDimension* const dimension = model->GetMutableDimension(name);
1997 const std::vector<IntVar*>& cumuls = dimension->cumuls();
1998 for (int i = 0; i < cumuls.size(); ++i) {
1999 cumul_to_dim_indices_[cumuls[i]] = {dimension, i};
2002 const std::vector<IntVar*>& vehicle_vars =
model->VehicleVars();
2003 for (
int i = 0; i < vehicle_vars.size(); ++i) {
2004 vehicle_var_to_indices_[vehicle_vars[i]] = i;
2006 RegisterInspectors();
2008 ~RoutingModelInspector()
override {}
2009 void EndVisitModel(
const std::string& )
override {
2010 const std::vector<int> node_to_same_vehicle_component_id =
2011 same_vehicle_components_.GetComponentIds();
2012 model_->InitSameVehicleGroups(
2013 same_vehicle_components_.GetNumberOfComponents());
2014 for (
int node = 0; node < model_->Size(); ++node) {
2015 model_->SetSameVehicleGroup(node,
2016 node_to_same_vehicle_component_id[node]);
2021 void EndVisitConstraint(
const std::string& type_name,
2025 void VisitIntegerExpressionArgument(
const std::string& type_name,
2026 IntExpr*
const expr)
override {
2028 [](
const IntExpr*) {})(expr);
2030 void VisitIntegerArrayArgument(
const std::string& arg_name,
2031 const std::vector<int64_t>& values)
override {
2033 [](
const std::vector<int64_t>&) {})(values);
2037 using ExprInspector = std::function<void(
const IntExpr*)>;
2038 using ArrayInspector = std::function<void(
const std::vector<int64_t>&)>;
2039 using ConstraintInspector = std::function<void()>;
2041 void RegisterInspectors() {
2042 expr_inspectors_[kExpressionArgument] = [
this](
const IntExpr* expr) {
2045 expr_inspectors_[kLeftArgument] = [
this](
const IntExpr* expr) {
2048 expr_inspectors_[kRightArgument] = [
this](
const IntExpr* expr) {
2051 array_inspectors_[kStartsArgument] =
2052 [
this](
const std::vector<int64_t>& int_array) {
2053 starts_argument_ = int_array;
2055 array_inspectors_[kEndsArgument] =
2056 [
this](
const std::vector<int64_t>& int_array) {
2057 ends_argument_ = int_array;
2059 constraint_inspectors_[kNotMember] = [
this]() {
2060 std::pair<RoutingDimension*, int> dim_index;
2062 RoutingDimension*
const dimension = dim_index.first;
2063 const int index = dim_index.second;
2064 dimension->forbidden_intervals_[
index].InsertIntervals(starts_argument_,
2066 VLOG(2) << dimension->name() <<
" " <<
index <<
": "
2067 << dimension->forbidden_intervals_[
index].DebugString();
2070 starts_argument_.clear();
2071 ends_argument_.clear();
2073 constraint_inspectors_[kEquality] = [
this]() {
2075 int right_index = 0;
2076 if (
gtl::FindCopy(vehicle_var_to_indices_, left_, &left_index) &&
2077 gtl::FindCopy(vehicle_var_to_indices_, right_, &right_index)) {
2078 VLOG(2) <<
"Vehicle variables for " << left_index <<
" and "
2079 << right_index <<
" are equal.";
2080 same_vehicle_components_.AddEdge(left_index, right_index);
2086 std::pair<RoutingDimension*, int> left_index;
2087 std::pair<RoutingDimension*, int> right_index;
2088 if (
gtl::FindCopy(cumul_to_dim_indices_, left_, &left_index) &&
2089 gtl::FindCopy(cumul_to_dim_indices_, right_, &right_index)) {
2090 RoutingDimension*
const dimension = left_index.first;
2091 if (dimension == right_index.first) {
2092 VLOG(2) <<
"For dimension " << dimension->name() <<
", cumul for "
2093 << left_index.second <<
" is less than " << right_index.second
2095 dimension->path_precedence_graph_.AddArc(left_index.second,
2096 right_index.second);
2104 RoutingModel*
const model_;
2106 absl::flat_hash_map<const IntExpr*, std::pair<RoutingDimension*, int>>
2107 cumul_to_dim_indices_;
2108 absl::flat_hash_map<const IntExpr*, int> vehicle_var_to_indices_;
2109 absl::flat_hash_map<std::string, ExprInspector> expr_inspectors_;
2110 absl::flat_hash_map<std::string, ArrayInspector> array_inspectors_;
2111 absl::flat_hash_map<std::string, ConstraintInspector> constraint_inspectors_;
2112 const IntExpr*
expr_ =
nullptr;
2113 const IntExpr* left_ =
nullptr;
2114 const IntExpr* right_ =
nullptr;
2115 std::vector<int64_t> starts_argument_;
2116 std::vector<int64_t> ends_argument_;
2119void RoutingModel::DetectImplicitPickupAndDeliveries() {
2120 std::vector<int> non_pickup_delivery_nodes;
2121 for (
int node = 0; node < Size(); ++node) {
2122 if (!IsStart(node) && !IsPickup(node) && !IsDelivery(node)) {
2123 non_pickup_delivery_nodes.push_back(node);
2127 std::set<std::pair<int64_t, int64_t>> implicit_pickup_deliveries;
2128 for (
const RoutingDimension*
const dimension : dimensions_) {
2129 if (dimension->class_evaluators_.size() != 1) {
2132 const TransitCallback1& transit =
2133 UnaryTransitCallbackOrNull(dimension->class_evaluators_[0]);
2134 if (transit ==
nullptr)
continue;
2135 absl::flat_hash_map<int64_t, std::vector<int64_t>> nodes_by_positive_demand;
2136 absl::flat_hash_map<int64_t, std::vector<int64_t>> nodes_by_negative_demand;
2137 for (
int node : non_pickup_delivery_nodes) {
2138 const int64_t
demand = transit(node);
2140 nodes_by_positive_demand[
demand].push_back(node);
2142 nodes_by_negative_demand[-
demand].push_back(node);
2145 for (
const auto& [
demand, positive_nodes] : nodes_by_positive_demand) {
2146 const std::vector<int64_t>*
const negative_nodes =
2148 if (negative_nodes !=
nullptr) {
2149 for (int64_t positive_node : positive_nodes) {
2150 for (int64_t negative_node : *negative_nodes) {
2151 implicit_pickup_deliveries.insert({positive_node, negative_node});
2157 implicit_pickup_delivery_pairs_without_alternatives_.clear();
2158 for (
auto [pickup, delivery] : implicit_pickup_deliveries) {
2159 implicit_pickup_delivery_pairs_without_alternatives_.push_back(
2160 {{pickup}, {delivery}});
2165absl::Duration GetTimeLimit(
const RoutingSearchParameters&
parameters) {
2166 if (!
parameters.has_time_limit())
return absl::InfiniteDuration();
2171void RoutingModel::CloseModelWithParameters(
2174 if (!error.empty()) {
2175 status_ = ROUTING_INVALID;
2176 LOG(ERROR) <<
"Invalid RoutingSearchParameters: " << error;
2180 LOG(WARNING) <<
"Model already closed";
2186 GetOrCreateLimit()->UpdateLimits(
2187 GetTimeLimit(
parameters), std::numeric_limits<int64_t>::max(),
2188 std::numeric_limits<int64_t>::max(),
parameters.solution_limit());
2190 for (RoutingDimension*
const dimension : dimensions_) {
2191 dimension->CloseModel(UsesLightPropagation(
parameters));
2194 dimension_resource_group_indices_.resize(dimensions_.size());
2195 for (
int rg_index = 0; rg_index < resource_groups_.size(); rg_index++) {
2196 const ResourceGroup& resource_group = *resource_groups_[rg_index];
2197 if (resource_group.GetVehiclesRequiringAResource().empty())
continue;
2198 for (DimensionIndex dim_index :
2199 resource_group.GetAffectedDimensionIndices()) {
2200 dimension_resource_group_indices_[dim_index].push_back(rg_index);
2206 FinalizeAllowedVehicles();
2208 ComputeVehicleClasses();
2209 ComputeVehicleTypes();
2210 ComputeResourceClasses();
2211 FinalizeVisitTypes();
2212 vehicle_start_class_callback_ = [
this](int64_t
start) {
2213 return GetVehicleStartClass(
start);
2216 AddNoCycleConstraintInternal();
2218 const int size = Size();
2221 for (
int i = 0;
i < vehicles_; ++
i) {
2222 const int64_t
start = Start(i);
2223 const int64_t
end = End(i);
2224 solver_->AddConstraint(
2225 solver_->MakeEquality(vehicle_vars_[
start], solver_->MakeIntConst(i)));
2226 solver_->AddConstraint(
2227 solver_->MakeEquality(vehicle_vars_[
end], solver_->MakeIntConst(i)));
2228 solver_->AddConstraint(
2229 solver_->MakeIsDifferentCstCt(nexts_[
start],
end, vehicle_active_[i]));
2230 if (vehicle_used_when_empty_[i]) {
2231 vehicle_route_considered_[
i]->SetMin(1);
2233 solver_->AddConstraint(solver_->MakeEquality(
2234 vehicle_active_[i], vehicle_route_considered_[i]));
2238 for (
int i = 0;
i < allowed_vehicles_.size(); ++
i) {
2239 const auto& allowed_vehicles = allowed_vehicles_[
i];
2240 if (!allowed_vehicles.empty()) {
2241 std::vector<int64_t> vehicles;
2242 vehicles.reserve(allowed_vehicles.size() + 1);
2243 vehicles.push_back(-1);
2244 for (
int vehicle : allowed_vehicles) {
2245 vehicles.push_back(vehicle);
2247 solver_->AddConstraint(solver_->MakeMemberCt(VehicleVar(i), vehicles));
2252 if (vehicles_ > max_active_vehicles_) {
2253 solver_->AddConstraint(
2254 solver_->MakeSumLessOrEqual(vehicle_active_, max_active_vehicles_));
2262 if (vehicles_ > 1) {
2263 std::vector<IntVar*> zero_transit(
size, solver_->MakeIntConst(0));
2264 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
2265 nexts_, active_, vehicle_vars_, zero_transit));
2270 for (
int i = 0;
i <
size; ++
i) {
2271 if (GetDisjunctionIndices(i).empty() && active_[i]->Max() != 0) {
2272 active_[
i]->SetValue(1);
2274 const int type = GetVisitType(i);
2275 if (type == kUnassigned) {
2278 const absl::flat_hash_set<VisitTypePolicy>*
const infeasible_policies =
2280 if (infeasible_policies !=
nullptr &&
2281 infeasible_policies->contains(index_to_type_policy_[i])) {
2282 active_[
i]->SetValue(0);
2287 for (
int i = 0;
i <
size; ++
i) {
2290 paths_metadata_.Starts()));
2292 solver_->AddConstraint(
2293 solver_->MakeIsDifferentCstCt(nexts_[i], i, active_[i]));
2298 for (
int i = 0;
i <
size; ++
i) {
2299 solver_->AddConstraint(
2300 solver_->MakeIsDifferentCstCt(vehicle_vars_[i], -1, active_[i]));
2303 if (HasTypeRegulations()) {
2304 solver_->AddConstraint(
2305 solver_->RevAlloc(
new TypeRegulationsConstraint(*
this)));
2309 for (
int i = 0;
i < vehicles_; ++
i) {
2310 std::vector<int64_t> forbidden_ends;
2311 forbidden_ends.reserve(vehicles_ - 1);
2312 for (
int j = 0; j < vehicles_; ++j) {
2314 forbidden_ends.push_back(End(j));
2318 solver_.get(), nexts_[Start(i)], std::move(forbidden_ends)));
2322 for (
const int64_t
end : paths_metadata_.Ends()) {
2323 is_bound_to_end_[
end]->SetValue(1);
2326 std::vector<IntVar*> cost_elements;
2328 if (vehicles_ > 0) {
2329 for (
int node_index = 0; node_index <
size; ++node_index) {
2330 if (CostsAreHomogeneousAcrossVehicles()) {
2331 AppendHomogeneousArcCosts(
parameters, node_index, &cost_elements);
2333 AppendArcCosts(
parameters, node_index, &cost_elements);
2336 if (vehicle_amortized_cost_factors_set_) {
2337 std::vector<IntVar*> route_lengths;
2338 solver_->MakeIntVarArray(vehicles_, 0,
size, &route_lengths);
2339 solver_->AddConstraint(
2340 solver_->MakeDistribute(vehicle_vars_, route_lengths));
2341 std::vector<IntVar*> vehicle_used;
2342 for (
int i = 0;
i < vehicles_;
i++) {
2344 vehicle_used.push_back(
2345 solver_->MakeIsGreaterCstVar(route_lengths[i], 2));
2348 ->MakeProd(solver_->MakeOpposite(solver_->MakeSquare(
2349 solver_->MakeSum(route_lengths[i], -2))),
2350 quadratic_cost_factor_of_vehicle_[i])
2352 cost_elements.push_back(
var);
2354 IntVar*
const vehicle_usage_cost =
2355 solver_->MakeScalProd(vehicle_used, linear_cost_factor_of_vehicle_)
2357 cost_elements.push_back(vehicle_usage_cost);
2361 for (
const RoutingDimension* dimension : dimensions_) {
2362 dimension->SetupGlobalSpanCost(&cost_elements);
2363 dimension->SetupSlackAndDependentTransitCosts();
2364 const std::vector<int64_t>& span_costs =
2365 dimension->vehicle_span_cost_coefficients();
2366 const std::vector<int64_t>& slack_costs =
2367 dimension->vehicle_slack_cost_coefficients();
2368 const std::vector<int64_t>& span_ubs =
2369 dimension->vehicle_span_upper_bounds();
2370 const bool has_span_constraint =
2371 std::any_of(span_costs.begin(), span_costs.end(),
2372 [](int64_t coeff) { return coeff != 0; }) ||
2373 std::any_of(slack_costs.begin(), slack_costs.end(),
2374 [](int64_t coeff) { return coeff != 0; }) ||
2375 std::any_of(span_ubs.begin(), span_ubs.end(),
2377 return value < std::numeric_limits<int64_t>::max();
2379 dimension->HasSoftSpanUpperBounds() ||
2380 dimension->HasQuadraticCostSoftSpanUpperBounds();
2381 if (has_span_constraint) {
2382 std::vector<IntVar*> spans(vehicles(),
nullptr);
2383 std::vector<IntVar*> total_slacks(vehicles(),
nullptr);
2385 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2386 if (span_ubs[vehicle] < std::numeric_limits<int64_t>::max()) {
2387 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2389 if (span_costs[vehicle] != 0 || slack_costs[vehicle] != 0) {
2390 total_slacks[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle],
"");
2393 if (dimension->HasSoftSpanUpperBounds()) {
2394 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2395 if (spans[vehicle])
continue;
2396 const BoundCost bound_cost =
2397 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2398 if (bound_cost.cost == 0)
continue;
2399 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2402 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2403 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2404 if (spans[vehicle])
continue;
2405 const BoundCost bound_cost =
2406 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2407 if (bound_cost.cost == 0)
continue;
2408 spans[vehicle] = solver_->MakeIntVar(0, span_ubs[vehicle]);
2411 solver_->AddConstraint(
2415 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2416 if (!spans[vehicle] && !total_slacks[vehicle])
continue;
2417 if (spans[vehicle]) {
2418 AddVariableTargetToFinalizer(spans[vehicle],
2419 std::numeric_limits<int64_t>::min());
2421 AddVariableTargetToFinalizer(dimension->CumulVar(End(vehicle)),
2422 std::numeric_limits<int64_t>::min());
2423 AddVariableTargetToFinalizer(dimension->CumulVar(Start(vehicle)),
2424 std::numeric_limits<int64_t>::max());
2427 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2428 if (span_costs[vehicle] == 0 && slack_costs[vehicle] == 0)
continue;
2429 DCHECK(total_slacks[vehicle] !=
nullptr);
2430 IntVar*
const slack_amount =
2432 ->MakeProd(vehicle_route_considered_[vehicle],
2433 total_slacks[vehicle])
2435 const int64_t slack_cost_coefficient =
2436 CapAdd(slack_costs[vehicle], span_costs[vehicle]);
2437 IntVar*
const slack_cost =
2438 solver_->MakeProd(slack_amount, slack_cost_coefficient)->Var();
2439 cost_elements.push_back(slack_cost);
2440 AddWeightedVariableMinimizedByFinalizer(slack_amount,
2441 slack_cost_coefficient);
2443 if (dimension->HasSoftSpanUpperBounds()) {
2444 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2445 const auto bound_cost =
2446 dimension->GetSoftSpanUpperBoundForVehicle(vehicle);
2447 if (bound_cost.cost == 0 ||
2448 bound_cost.bound == std::numeric_limits<int64_t>::max())
2450 DCHECK(spans[vehicle] !=
nullptr);
2453 IntVar*
const span_violation_amount =
2456 vehicle_route_considered_[vehicle],
2458 solver_->MakeSum(spans[vehicle], -bound_cost.bound),
2461 IntVar*
const span_violation_cost =
2462 solver_->MakeProd(span_violation_amount, bound_cost.cost)->Var();
2463 cost_elements.push_back(span_violation_cost);
2464 AddWeightedVariableMinimizedByFinalizer(span_violation_amount,
2468 if (dimension->HasQuadraticCostSoftSpanUpperBounds()) {
2469 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
2470 const auto bound_cost =
2471 dimension->GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
2472 if (bound_cost.cost == 0 ||
2473 bound_cost.bound == std::numeric_limits<int64_t>::max())
2475 DCHECK(spans[vehicle] !=
nullptr);
2478 IntExpr* max0 = solver_->MakeMax(
2479 solver_->MakeSum(spans[vehicle], -bound_cost.bound), 0);
2480 IntVar*
const squared_span_violation_amount =
2482 ->MakeProd(vehicle_route_considered_[vehicle],
2483 solver_->MakeSquare(max0))
2485 IntVar*
const span_violation_cost =
2486 solver_->MakeProd(squared_span_violation_amount, bound_cost.cost)
2488 cost_elements.push_back(span_violation_cost);
2489 AddWeightedVariableMinimizedByFinalizer(squared_span_violation_amount,
2496 for (DisjunctionIndex
i(0);
i < disjunctions_.size(); ++
i) {
2497 IntVar* penalty_var = CreateDisjunction(i);
2498 if (penalty_var !=
nullptr) {
2499 cost_elements.push_back(penalty_var);
2503 for (
const RoutingDimension* dimension : dimensions_) {
2504 dimension->SetupCumulVarSoftLowerBoundCosts(&cost_elements);
2505 dimension->SetupCumulVarSoftUpperBoundCosts(&cost_elements);
2506 dimension->SetupCumulVarPiecewiseLinearCosts(&cost_elements);
2509 for (
int i = 0;
i < same_vehicle_costs_.size(); ++
i) {
2510 cost_elements.push_back(CreateSameVehicleCost(i));
2514 for (
const auto& [force_distance, vehicle_unit_costs] :
2515 force_distance_to_vehicle_unit_costs_) {
2516 std::vector<IntVar*> vehicle_costs(vehicles_,
nullptr);
2518 for (
int vehicle = 0; vehicle < vehicles_; ++vehicle) {
2519 const int64_t cost_ub =
2520 vehicle_unit_costs[vehicle] == 0 ? 0 :
kint64max;
2521 vehicle_costs[vehicle] = solver_->MakeIntVar(0, cost_ub);
2522 cost_elements.push_back(vehicle_costs[vehicle]);
2523 AddWeightedVariableMinimizedByFinalizer(vehicle_costs[vehicle],
2524 vehicle_unit_costs[vehicle]);
2527 const RoutingDimension* force_dimension =
2528 GetMutableDimension(force_distance.first);
2529 DCHECK_NE(force_dimension,
nullptr);
2530 const RoutingDimension* distance_dimension =
2531 GetMutableDimension(force_distance.second);
2532 DCHECK_NE(distance_dimension,
nullptr);
2533 if (force_dimension ==
nullptr || distance_dimension ==
nullptr)
continue;
2535 Solver::PathEnergyCostConstraintSpecification specification{
2537 .paths = VehicleVars(),
2538 .forces = force_dimension->cumuls(),
2539 .distances = distance_dimension->transits(),
2540 .path_unit_costs = vehicle_unit_costs,
2541 .path_used_when_empty = vehicle_used_when_empty_,
2542 .path_starts = paths_metadata_.Starts(),
2543 .path_ends = paths_metadata_.Ends(),
2544 .costs = vehicle_costs,
2547 solver_->AddConstraint(
2548 solver_->MakePathEnergyCostConstraint(specification));
2552 cost_ = solver_->MakeSum(cost_elements)->Var();
2553 cost_->set_name(
"Cost");
2556 std::vector<std::pair<int, int>> pickup_delivery_precedences;
2557 for (
const auto& [pickups, deliveries] : pickup_delivery_pairs_) {
2558 DCHECK(!pickups.empty() && !deliveries.empty());
2559 for (
int pickup : pickups) {
2560 for (
int delivery : deliveries) {
2561 pickup_delivery_precedences.emplace_back(pickup, delivery);
2565 std::vector<int> lifo_vehicles;
2566 std::vector<int> fifo_vehicles;
2567 for (
int i = 0;
i < vehicles_; ++
i) {
2568 switch (vehicle_pickup_delivery_policy_[i]) {
2569 case PICKUP_AND_DELIVERY_NO_ORDER:
2571 case PICKUP_AND_DELIVERY_LIFO:
2572 lifo_vehicles.push_back(Start(i));
2574 case PICKUP_AND_DELIVERY_FIFO:
2575 fifo_vehicles.push_back(Start(i));
2579 solver_->AddConstraint(solver_->MakePathPrecedenceConstraint(
2580 nexts_, pickup_delivery_precedences, lifo_vehicles, fifo_vehicles));
2583 enable_deep_serialization_ =
false;
2584 std::unique_ptr<RoutingModelInspector> inspector(
2585 new RoutingModelInspector(
this));
2586 solver_->Accept(inspector.get());
2587 enable_deep_serialization_ =
true;
2589 for (
const RoutingDimension*
const dimension : dimensions_) {
2592 const ReverseArcListGraph<int, int>&
graph =
2593 dimension->GetPathPrecedenceGraph();
2594 std::vector<std::pair<int, int>> path_precedences;
2595 for (
const auto tail :
graph.AllNodes()) {
2597 path_precedences.emplace_back(
tail,
head);
2600 if (!path_precedences.empty()) {
2601 solver_->AddConstraint(solver_->MakePathTransitPrecedenceConstraint(
2602 nexts_, dimension->transits(), path_precedences));
2606 for (
const RoutingDimension::NodePrecedence& node_precedence :
2607 dimension->GetNodePrecedences()) {
2608 const int64_t first_node = node_precedence.first_node;
2609 const int64_t second_node = node_precedence.second_node;
2610 IntExpr*
const nodes_are_selected =
2611 solver_->MakeMin(active_[first_node], active_[second_node]);
2612 IntExpr*
const cumul_difference = solver_->MakeDifference(
2613 dimension->CumulVar(second_node), dimension->CumulVar(first_node));
2614 IntVar*
const cumul_difference_is_ge_offset =
2615 solver_->MakeIsGreaterOrEqualCstVar(cumul_difference,
2616 node_precedence.offset);
2619 solver_->AddConstraint(solver_->MakeLessOrEqual(
2620 nodes_are_selected->Var(), cumul_difference_is_ge_offset));
2624 if (!resource_groups_.empty()) {
2625 DCHECK_EQ(resource_vars_.size(), resource_groups_.size());
2626 for (
int rg = 0; rg < resource_groups_.size(); ++rg) {
2627 const auto& resource_group = resource_groups_[rg];
2628 const int max_resource_index = resource_group->Size() - 1;
2629 std::vector<IntVar*>& vehicle_res_vars = resource_vars_[rg];
2630 for (IntVar* res_var : vehicle_res_vars) {
2631 res_var->SetMax(max_resource_index);
2634 &vehicle_res_vars,
this));
2638 DetectImplicitPickupAndDeliveries();
2647 CreateFirstSolutionDecisionBuilders(
parameters);
2648 error = FindErrorInSearchParametersForModel(
parameters);
2649 if (!error.empty()) {
2650 status_ = ROUTING_INVALID;
2651 LOG(ERROR) <<
"Invalid RoutingSearchParameters for this model: " << error;
2657void RoutingModel::AddSearchMonitor(SearchMonitor*
const monitor) {
2658 monitors_.push_back(monitor);
2659 secondary_ls_monitors_.push_back(monitor);
2663class AtSolutionCallbackMonitor :
public SearchMonitor {
2665 AtSolutionCallbackMonitor(Solver* solver, std::function<
void()>
callback,
2666 bool track_unchecked_neighbors)
2667 : SearchMonitor(solver),
2669 track_unchecked_neighbors_(track_unchecked_neighbors) {}
2670 bool AtSolution()
override {
2675 void Install()
override {
2676 ListenToEvent(Solver::MonitorEvent::kAtSolution);
2677 if (track_unchecked_neighbors_) {
2678 ListenToEvent(Solver::MonitorEvent::kAcceptUncheckedNeighbor);
2683 std::function<void()> callback_;
2684 const bool track_unchecked_neighbors_;
2688void RoutingModel::AddAtSolutionCallback(std::function<
void()>
callback,
2689 bool track_unchecked_neighbors) {
2690 AtSolutionCallbackMonitor*
const monitor =
2691 solver_->RevAlloc(
new AtSolutionCallbackMonitor(
2692 solver_.get(), std::move(
callback), track_unchecked_neighbors));
2693 at_solution_monitors_.push_back(monitor);
2694 AddSearchMonitor(monitor);
2697const Assignment* RoutingModel::Solve(
const Assignment* assignment) {
2698 return SolveFromAssignmentWithParameters(assignment,
2702const Assignment* RoutingModel::SolveWithParameters(
2704 std::vector<const Assignment*>* solutions) {
2705 return SolveFromAssignmentWithParameters(
nullptr,
parameters, solutions);
2709absl::Duration GetLnsTimeLimit(
const RoutingSearchParameters&
parameters) {
2710 if (!
parameters.has_lns_time_limit())
return absl::InfiniteDuration();
2716void MakeAllUnperformedInAssignment(
const RoutingModel*
model,
2717 Assignment* assignment) {
2718 assignment->Clear();
2720 if (!
model->IsStart(i)) {
2721 assignment->Add(
model->NextVar(i))->SetValue(i);
2724 for (
int vehicle = 0; vehicle <
model->vehicles(); ++vehicle) {
2725 assignment->Add(
model->NextVar(
model->Start(vehicle)))
2726 ->SetValue(
model->End(vehicle));
2731bool RoutingModel::CheckIfAssignmentIsFeasible(
const Assignment& assignment,
2732 bool call_at_solution_monitors) {
2733 tmp_assignment_->CopyIntersection(&assignment);
2734 std::vector<SearchMonitor*> monitors = call_at_solution_monitors
2735 ? at_solution_monitors_
2736 : std::vector<SearchMonitor*>();
2737 monitors.push_back(collect_one_assignment_);
2738 monitors.push_back(GetOrCreateLimit());
2739 solver_->Solve(restore_tmp_assignment_, monitors);
2740 return collect_one_assignment_->solution_count() == 1;
2743bool RoutingModel::AppendAssignmentIfFeasible(
2744 const Assignment& assignment,
2745 std::vector<std::unique_ptr<Assignment>>* assignments,
2746 bool call_at_solution_monitors) {
2747 if (CheckIfAssignmentIsFeasible(assignment, call_at_solution_monitors)) {
2748 assignments->push_back(std::make_unique<Assignment>(solver_.get()));
2749 assignments->back()->Copy(collect_one_assignment_->solution(0));
2755void RoutingModel::LogSolution(
const RoutingSearchParameters&
parameters,
2756 absl::string_view description,
2757 int64_t solution_cost, int64_t start_time_ms) {
2759 const double cost_scaling_factor =
parameters.log_cost_scaling_factor();
2760 const double cost_offset =
parameters.log_cost_offset();
2761 const std::string cost_string =
2762 cost_scaling_factor == 1.0 && cost_offset == 0.0
2763 ? absl::StrCat(solution_cost)
2765 "%d (%.8lf)", solution_cost,
2766 cost_scaling_factor * (solution_cost + cost_offset));
2767 LOG(INFO) << absl::StrFormat(
2768 "%s (%s, time = %d ms, memory used = %s)", description, cost_string,
2769 solver_->wall_time() - start_time_ms, memory_str);
2772const Assignment* RoutingModel::SolveFromAssignmentWithParameters(
2773 const Assignment* assignment,
const RoutingSearchParameters&
parameters,
2774 std::vector<const Assignment*>* solutions) {
2775 return SolveFromAssignmentsWithParameters({assignment},
parameters,
2779const Assignment* RoutingModel::FastSolveFromAssignmentWithParameters(
2780 const Assignment* assignment,
2781 const RoutingSearchParameters& search_parameters,
bool check_solution_in_cp,
2782 absl::flat_hash_set<IntVar*>* touched) {
2783 if (search_parameters.local_search_metaheuristic() !=
2784 LocalSearchMetaheuristic::GREEDY_DESCENT &&
2785 search_parameters.local_search_metaheuristic() !=
2786 LocalSearchMetaheuristic::AUTOMATIC) {
2787 LOG(DFATAL) <<
"local_search_metaheuristic value unsupported: "
2788 << search_parameters.local_search_metaheuristic();
2791 const int64_t start_time_ms = solver_->wall_time();
2792 QuietCloseModelWithParameters(search_parameters);
2793 if (status_ == ROUTING_INVALID)
return nullptr;
2794 status_ = ROUTING_NOT_SOLVED;
2795 if (assignment ==
nullptr)
return nullptr;
2797 GetTimeLimit(search_parameters), std::numeric_limits<int64_t>::max(),
2798 std::numeric_limits<int64_t>::max(), search_parameters.solution_limit());
2799 std::vector<SearchMonitor*> monitors = {metaheuristic_};
2800 if (search_log_ !=
nullptr) monitors.push_back(search_log_);
2801 Assignment*
solution = solver()->RunUncheckedLocalSearch(
2803 GetOrCreateLocalSearchFilterManager(search_parameters,
2806 primary_ls_operator_, monitors,
limit_, touched);
2807 const absl::Duration elapsed_time =
2808 absl::Milliseconds(solver_->wall_time() - start_time_ms);
2810 if (!check_solution_in_cp ||
2811 CheckIfAssignmentIsFeasible(*
solution,
2813 status_ = ROUTING_SUCCESS;
2816 if (status_ != ROUTING_SUCCESS) {
2817 if (elapsed_time >= GetTimeLimit(search_parameters)) {
2818 status_ = ROUTING_FAIL_TIMEOUT;
2820 status_ = ROUTING_FAIL;
2826const Assignment* RoutingModel::SolveFromAssignmentsWithParameters(
2827 const std::vector<const Assignment*>& assignments,
2829 std::vector<const Assignment*>* solutions) {
2830 const int64_t start_time_ms = solver_->wall_time();
2832 VLOG(1) <<
"Search parameters:\n" <<
parameters;
2833 if (solutions !=
nullptr) solutions->clear();
2834 if (status_ == ROUTING_INVALID) {
2837 status_ = ROUTING_NOT_SOLVED;
2840 if (!solver_->CheckConstraint(solver_->MakeTrueConstraint())) {
2841 status_ = ROUTING_INFEASIBLE;
2845 const bool perform_secondary_ls =
2846 GetTimeLimit(
parameters) != absl::InfiniteDuration() &&
2847 parameters.secondary_ls_time_limit_ratio() > 0;
2848 const auto update_time_limits =
2849 [
this, start_time_ms, perform_secondary_ls,
2850 &
parameters](
bool leave_secondary_solve_buffer =
true) {
2851 const absl::Duration elapsed_time =
2852 absl::Milliseconds(solver_->wall_time() - start_time_ms);
2853 const absl::Duration time_left =
2856 if (time_left < absl::ZeroDuration())
return false;
2858 const absl::Duration secondary_solve_buffer =
2859 !leave_secondary_solve_buffer || !perform_secondary_ls
2860 ? absl::ZeroDuration()
2861 :
parameters.secondary_ls_time_limit_ratio() * time_left;
2862 const absl::Duration
time_limit = time_left - secondary_solve_buffer;
2864 std::numeric_limits<int64_t>::max(),
2866 DCHECK_NE(ls_limit_,
nullptr);
2867 ls_limit_->UpdateLimits(
time_limit, std::numeric_limits<int64_t>::max(),
2868 std::numeric_limits<int64_t>::max(), 1);
2871 time_buffer_ = std::min(absl::Seconds(1),
time_limit * 0.05);
2874 if (!update_time_limits()) {
2875 status_ = ROUTING_FAIL_TIMEOUT;
2878 lns_limit_->UpdateLimits(
2879 GetLnsTimeLimit(
parameters), std::numeric_limits<int64_t>::max(),
2880 std::numeric_limits<int64_t>::max(), std::numeric_limits<int64_t>::max());
2886 const int time_limit_shares = 1 + !global_dimension_optimizers_.empty() +
2887 !local_dimension_optimizers_.empty();
2888 const absl::Duration first_solution_lns_time_limit =
2889 std::max(GetTimeLimit(
parameters) / time_limit_shares,
2891 first_solution_lns_limit_->UpdateLimits(
2892 first_solution_lns_time_limit, std::numeric_limits<int64_t>::max(),
2893 std::numeric_limits<int64_t>::max(), std::numeric_limits<int64_t>::max());
2895 std::vector<std::unique_ptr<Assignment>> solution_pool;
2896 std::vector<const Assignment*> first_solution_assignments;
2897 for (
const Assignment* assignment : assignments) {
2898 if (assignment !=
nullptr) first_solution_assignments.push_back(assignment);
2900 local_optimum_reached_ =
false;
2903 const auto run_secondary_ls = [
this, perform_secondary_ls,
2904 &update_time_limits]() {
2905 if (collect_assignments_->has_solution() && perform_secondary_ls &&
2906 update_time_limits(
false)) {
2907 assignment_->CopyIntersection(
2908 collect_assignments_->last_solution_or_null());
2909 solver_->Solve(secondary_ls_db_, secondary_ls_monitors_);
2912 if (first_solution_assignments.empty()) {
2913 bool solution_found =
false;
2914 if (IsMatchingModel()) {
2915 Assignment matching(solver_.get());
2917 if (SolveMatchingModel(&matching,
parameters) &&
2918 update_time_limits(
false) &&
2919 AppendAssignmentIfFeasible(matching, &solution_pool)) {
2921 LogSolution(
parameters,
"Min-Cost Flow Solution",
2922 solution_pool.back()->ObjectiveValue(), start_time_ms);
2924 solution_found =
true;
2925 local_optimum_reached_ =
true;
2928 if (!solution_found) {
2931 Assignment unperformed(solver_.get());
2932 MakeAllUnperformedInAssignment(
this, &unperformed);
2933 if (update_time_limits(
false) &&
2934 AppendAssignmentIfFeasible(unperformed, &solution_pool,
false) &&
2936 LogSolution(
parameters,
"All Unperformed Solution",
2937 solution_pool.back()->ObjectiveValue(), start_time_ms);
2939 local_optimum_reached_ =
false;
2940 if (update_time_limits()) {
2941 solver_->Solve(solve_db_, monitors_);
2946 for (
const Assignment* assignment : first_solution_assignments) {
2947 assignment_->CopyIntersection(assignment);
2948 solver_->Solve(improve_db_, monitors_);
2950 if (collect_assignments_->solution_count() >= 1 ||
2951 !update_time_limits()) {
2958 const SolutionCollector*
const solution_collector =
2959 collect_secondary_ls_assignments_->has_solution()
2960 ? collect_secondary_ls_assignments_
2961 : collect_assignments_;
2963 if (update_time_limits(
false) &&
2965 parameters.use_generalized_cp_sat() == BOOL_TRUE ||
2966 (
parameters.fallback_to_cp_sat_size_threshold() >= Size() &&
2967 !solution_collector->has_solution() && solution_pool.empty()))) {
2968 VLOG(1) <<
"Solving with CP-SAT";
2969 Assignment*
const cp_solution = solution_collector->last_solution_or_null();
2970 Assignment sat_solution(solver_.get());
2972 update_time_limits(
false) &&
2973 AppendAssignmentIfFeasible(sat_solution, &solution_pool)) {
2975 LogSolution(
parameters,
"SAT", solution_pool.back()->ObjectiveValue(),
2978 local_optimum_reached_ =
true;
2979 if (sat_solution.HasObjective()) {
2980 objective_lower_bound_ =
2981 std::max(objective_lower_bound_, sat_solution.ObjectiveValue());
2985 VLOG(1) <<
"Objective lower bound: " << objective_lower_bound_;
2986 const absl::Duration elapsed_time =
2987 absl::Milliseconds(solver_->wall_time() - start_time_ms);
2989 if (solution_collector->has_solution() || !solution_pool.empty()) {
2990 status_ = local_optimum_reached_
2992 : ROUTING_PARTIAL_SUCCESS_LOCAL_OPTIMUM_NOT_REACHED;
2993 if (solutions !=
nullptr) {
2994 std::vector<Assignment*> temp_solutions;
2995 for (
int i = 0;
i < solution_collector->solution_count(); ++
i) {
2996 temp_solutions.push_back(
2997 solver_->MakeAssignment(solution_collector->solution(i)));
2999 for (
const auto&
solution : solution_pool) {
3000 if (temp_solutions.empty() ||
3002 temp_solutions.back()->ObjectiveValue()) {
3003 temp_solutions.push_back(solver_->MakeAssignment(
solution.get()));
3008 DCHECK(!temp_solutions.empty());
3009 const int64_t min_objective_value =
3010 temp_solutions.back()->ObjectiveValue();
3012 if (temp_solutions.size() <
parameters.number_of_solutions_to_collect() &&
3013 solution_collector != collect_assignments_ &&
3014 collect_assignments_->has_solution()) {
3018 DCHECK_EQ(*collect_assignments_->last_solution_or_null(),
3019 *temp_solutions[0]);
3021 const size_t num_solutions = collect_assignments_->solution_count();
3022 const int num_solutions_to_add = std::min(
3023 parameters.number_of_solutions_to_collect() - solutions->size(),
3025 for (
int i = num_solutions_to_add;
i > 0; --
i) {
3026 solutions->push_back(solver_->MakeAssignment(
3027 collect_assignments_->solution(num_solutions - 1 - i)));
3028 DCHECK_GE(solutions->back()->ObjectiveValue(), min_objective_value);
3033 solutions->insert(solutions->end(), temp_solutions.begin(),
3034 temp_solutions.end());
3035 if (min_objective_value <= objective_lower_bound_) {
3036 status_ = ROUTING_OPTIMAL;
3038 return solutions->back();
3040 Assignment* best_assignment = solution_collector->last_solution_or_null();
3041 for (
const auto&
solution : solution_pool) {
3042 if (best_assignment ==
nullptr ||
3043 solution->ObjectiveValue() < best_assignment->ObjectiveValue()) {
3047 if (best_assignment->ObjectiveValue() <= objective_lower_bound_) {
3048 status_ = ROUTING_OPTIMAL;
3050 return solver_->MakeAssignment(best_assignment);
3052 if (elapsed_time >= GetTimeLimit(
parameters)) {
3053 status_ = ROUTING_FAIL_TIMEOUT;
3055 status_ = ROUTING_FAIL;
3061const Assignment* RoutingModel::SolveWithIteratedLocalSearch(
3063 const int64_t start_time_ms = solver_->wall_time();
3067 solver_->Solve(solve_db_, monitors_);
3068 uint64_t explored_solutions = solver_->solutions();
3070 Assignment* best_solution = collect_assignments_->last_solution_or_null();
3071 if (!best_solution) {
3075 LocalSearchFilterManager* filter_manager =
3076 GetOrCreateLocalSearchFilterManager(
parameters,
3082 [
this]() {
return CheckLimit(time_buffer_); }, filter_manager);
3086 const auto update_time_limits = [
this, start_time_ms, &
parameters]() {
3087 const absl::Duration elapsed_time =
3088 absl::Milliseconds(solver_->wall_time() - start_time_ms);
3089 const absl::Duration time_left = GetTimeLimit(
parameters) - elapsed_time;
3090 if (time_left < absl::ZeroDuration()) {
3093 limit_->UpdateLimits(time_left, std::numeric_limits<int64_t>::max(),
3094 std::numeric_limits<int64_t>::max(),
3096 DCHECK_NE(ls_limit_,
nullptr);
3097 ls_limit_->UpdateLimits(time_left, std::numeric_limits<int64_t>::max(),
3098 std::numeric_limits<int64_t>::max(), 1);
3101 time_buffer_ = std::min(absl::Seconds(1), time_left * 0.05);
3105 std::unique_ptr<NeighborAcceptanceCriterion> acceptance_criterion =
3108 const bool improve_perturbed_solution =
3109 parameters.iterated_local_search_parameters()
3110 .improve_perturbed_solution();
3112 while (update_time_limits() &&
3113 explored_solutions <
parameters.solution_limit()) {
3114 solver_->Solve(perturbation_db, monitors_);
3115 explored_solutions += solver_->solutions();
3117 Assignment* neighbor = collect_assignments_->last_solution_or_null();
3122 if (improve_perturbed_solution) {
3123 assignment_->CopyIntersection(neighbor);
3125 solver_->Solve(improve_db_, monitors_);
3127 neighbor = collect_assignments_->last_solution_or_null();
3133 if (acceptance_criterion->Accept(best_solution, neighbor)) {
3137 best_solution->CopyIntersection(neighbor);
3141 return best_solution;
3144void RoutingModel::SetAssignmentFromOtherModelAssignment(
3145 Assignment* target_assignment,
const RoutingModel* source_model,
3146 const Assignment* source_assignment) {
3147 const int size = Size();
3148 DCHECK_EQ(
size, source_model->Size());
3149 CHECK_EQ(target_assignment->solver(), solver_.get());
3151 if (CostsAreHomogeneousAcrossVehicles()) {
3153 source_model->Nexts());
3155 std::vector<IntVar*> source_vars(
size +
size + vehicles_);
3156 std::vector<IntVar*> target_vars(
size +
size + vehicles_);
3158 source_vars[
index] = source_model->NextVar(
index);
3166 source_assignment, source_vars);
3169 target_assignment->AddObjective(cost_);
3182int64_t RoutingModel::ComputeLowerBound() {
3184 LOG(WARNING) <<
"Non-closed model not supported.";
3187 if (!CostsAreHomogeneousAcrossVehicles()) {
3188 LOG(WARNING) <<
"Non-homogeneous vehicle costs not supported";
3191 if (!disjunctions_.empty()) {
3193 <<
"Node disjunction constraints or optional nodes not supported.";
3196 const int num_nodes = Size() + vehicles_;
3198 LinearSumAssignment<ForwardStarGraph> linear_sum_assignment(
graph, num_nodes);
3203 std::unique_ptr<IntVarIterator> iterator(
3204 nexts_[
tail]->MakeDomainIterator(
false));
3205 for (
const int64_t
head : InitAndGetValues(iterator.get())) {
3216 linear_sum_assignment.SetArcCost(
arc, cost);
3224 linear_sum_assignment.SetArcCost(
arc, 0);
3226 if (linear_sum_assignment.ComputeAssignment()) {
3227 return linear_sum_assignment.GetCost();
3232bool RoutingModel::RouteCanBeUsedByVehicle(
const Assignment& assignment,
3233 int start_index,
int vehicle)
const {
3235 IsStart(start_index) ?
Next(assignment, start_index) : start_index;
3236 while (!IsEnd(current_index)) {
3237 const IntVar*
const vehicle_var = VehicleVar(current_index);
3238 if (!vehicle_var->Contains(vehicle)) {
3241 const int next_index =
Next(assignment, current_index);
3242 CHECK_NE(next_index, current_index) <<
"Inactive node inside a route";
3243 current_index = next_index;
3248bool RoutingModel::ReplaceUnusedVehicle(
3249 int unused_vehicle,
int active_vehicle,
3250 Assignment*
const compact_assignment)
const {
3251 CHECK(compact_assignment !=
nullptr);
3252 CHECK(!IsVehicleUsed(*compact_assignment, unused_vehicle));
3253 CHECK(IsVehicleUsed(*compact_assignment, active_vehicle));
3255 const int unused_vehicle_start = Start(unused_vehicle);
3256 IntVar*
const unused_vehicle_start_var = NextVar(unused_vehicle_start);
3257 const int unused_vehicle_end = End(unused_vehicle);
3258 const int active_vehicle_start = Start(active_vehicle);
3259 const int active_vehicle_end = End(active_vehicle);
3260 IntVar*
const active_vehicle_start_var = NextVar(active_vehicle_start);
3261 const int active_vehicle_next =
3262 compact_assignment->Value(active_vehicle_start_var);
3263 compact_assignment->SetValue(unused_vehicle_start_var, active_vehicle_next);
3264 compact_assignment->SetValue(active_vehicle_start_var, End(active_vehicle));
3267 int current_index = active_vehicle_next;
3268 while (!IsEnd(current_index)) {
3269 IntVar*
const vehicle_var = VehicleVar(current_index);
3270 compact_assignment->SetValue(vehicle_var, unused_vehicle);
3271 const int next_index =
Next(*compact_assignment, current_index);
3272 if (IsEnd(next_index)) {
3273 IntVar*
const last_next_var = NextVar(current_index);
3274 compact_assignment->SetValue(last_next_var, End(unused_vehicle));
3276 current_index = next_index;
3280 for (
const RoutingDimension*
const dimension : dimensions_) {
3281 const std::vector<IntVar*>& transit_variables = dimension->transits();
3282 IntVar*
const unused_vehicle_transit_var =
3283 transit_variables[unused_vehicle_start];
3284 IntVar*
const active_vehicle_transit_var =
3285 transit_variables[active_vehicle_start];
3286 const bool contains_unused_vehicle_transit_var =
3287 compact_assignment->Contains(unused_vehicle_transit_var);
3288 const bool contains_active_vehicle_transit_var =
3289 compact_assignment->Contains(active_vehicle_transit_var);
3290 if (contains_unused_vehicle_transit_var !=
3291 contains_active_vehicle_transit_var) {
3293 LOG(INFO) <<
"The assignment contains transit variable for dimension '"
3294 << dimension->name() <<
"' for some vehicles, but not for all";
3297 if (contains_unused_vehicle_transit_var) {
3298 const int64_t old_unused_vehicle_transit =
3299 compact_assignment->Value(unused_vehicle_transit_var);
3300 const int64_t old_active_vehicle_transit =
3301 compact_assignment->Value(active_vehicle_transit_var);
3302 compact_assignment->SetValue(unused_vehicle_transit_var,
3303 old_active_vehicle_transit);
3304 compact_assignment->SetValue(active_vehicle_transit_var,
3305 old_unused_vehicle_transit);
3309 const std::vector<IntVar*>& cumul_variables = dimension->cumuls();
3310 IntVar*
const unused_vehicle_cumul_var =
3311 cumul_variables[unused_vehicle_end];
3312 IntVar*
const active_vehicle_cumul_var =
3313 cumul_variables[active_vehicle_end];
3314 const int64_t old_unused_vehicle_cumul =
3315 compact_assignment->Value(unused_vehicle_cumul_var);
3316 const int64_t old_active_vehicle_cumul =
3317 compact_assignment->Value(active_vehicle_cumul_var);
3318 compact_assignment->SetValue(unused_vehicle_cumul_var,
3319 old_active_vehicle_cumul);
3320 compact_assignment->SetValue(active_vehicle_cumul_var,
3321 old_unused_vehicle_cumul);
3326Assignment* RoutingModel::CompactAssignment(
3327 const Assignment& assignment)
const {
3328 return CompactAssignmentInternal(assignment,
false);
3331Assignment* RoutingModel::CompactAndCheckAssignment(
3332 const Assignment& assignment)
const {
3333 return CompactAssignmentInternal(assignment,
true);
3336Assignment* RoutingModel::CompactAssignmentInternal(
3337 const Assignment& assignment,
bool check_compact_assignment)
const {
3338 CHECK_EQ(assignment.solver(), solver_.get());
3339 if (!CostsAreHomogeneousAcrossVehicles()) {
3341 <<
"The costs are not homogeneous, routes cannot be rearranged";
3345 std::unique_ptr<Assignment> compact_assignment(
new Assignment(&assignment));
3346 for (
int vehicle = 0; vehicle < vehicles_ - 1; ++vehicle) {
3347 if (IsVehicleUsed(*compact_assignment, vehicle)) {
3350 const int vehicle_start = Start(vehicle);
3351 const int vehicle_end = End(vehicle);
3353 int swap_vehicle = vehicles_ - 1;
3354 bool has_more_vehicles_with_route =
false;
3355 for (; swap_vehicle > vehicle; --swap_vehicle) {
3358 if (!IsVehicleUsed(*compact_assignment, swap_vehicle) ||
3359 !IsVehicleUsed(*compact_assignment, swap_vehicle)) {
3362 has_more_vehicles_with_route =
true;
3363 const int swap_vehicle_start = Start(swap_vehicle);
3364 const int swap_vehicle_end = End(swap_vehicle);
3365 if (manager_.IndexToNode(vehicle_start) !=
3366 manager_.IndexToNode(swap_vehicle_start) ||
3367 manager_.IndexToNode(vehicle_end) !=
3368 manager_.IndexToNode(swap_vehicle_end)) {
3373 if (RouteCanBeUsedByVehicle(*compact_assignment, swap_vehicle_start,
3379 if (swap_vehicle == vehicle) {
3380 if (has_more_vehicles_with_route) {
3384 LOG(INFO) <<
"No vehicle that can be swapped with " << vehicle
3391 if (!ReplaceUnusedVehicle(vehicle, swap_vehicle,
3392 compact_assignment.get())) {
3397 if (check_compact_assignment &&
3398 !solver_->CheckAssignment(compact_assignment.get())) {
3400 LOG(WARNING) <<
"The compacted assignment is not a valid solution";
3403 return compact_assignment.release();
3406int RoutingModel::FindNextActive(
int index,
3407 const std::vector<int64_t>& indices)
const {
3410 const int size = indices.size();
3417IntVar* RoutingModel::ApplyLocks(
const std::vector<int64_t>& locks) {
3420 CHECK_EQ(vehicles_, 1);
3421 preassignment_->Clear();
3422 IntVar* next_var =
nullptr;
3423 int lock_index = FindNextActive(-1, locks);
3424 const int size = locks.size();
3425 if (lock_index <
size) {
3426 next_var = NextVar(locks[lock_index]);
3427 preassignment_->Add(next_var);
3428 for (lock_index = FindNextActive(lock_index, locks); lock_index <
size;
3429 lock_index = FindNextActive(lock_index, locks)) {
3430 preassignment_->SetValue(next_var, locks[lock_index]);
3431 next_var = NextVar(locks[lock_index]);
3432 preassignment_->Add(next_var);
3438bool RoutingModel::ApplyLocksToAllVehicles(
3439 const std::vector<std::vector<int64_t>>& locks,
bool close_routes) {
3440 preassignment_->Clear();
3441 return RoutesToAssignment(locks,
true, close_routes, preassignment_);
3444int64_t RoutingModel::GetNumberOfDecisionsInFirstSolution(
3445 const RoutingSearchParameters&
parameters)
const {
3446 IntVarFilteredDecisionBuilder*
const decision_builder =
3447 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3448 return decision_builder !=
nullptr ? decision_builder->number_of_decisions()
3452int64_t RoutingModel::GetNumberOfRejectsInFirstSolution(
3453 const RoutingSearchParameters&
parameters)
const {
3454 IntVarFilteredDecisionBuilder*
const decision_builder =
3455 GetFilteredFirstSolutionDecisionBuilderOrNull(
parameters);
3456 return decision_builder !=
nullptr ? decision_builder->number_of_rejects()
3460bool RoutingModel::WriteAssignment(
const std::string& file_name)
const {
3461 if (collect_assignments_->solution_count() == 1 && assignment_ !=
nullptr) {
3462 assignment_->CopyIntersection(collect_assignments_->solution(0));
3463 return assignment_->Save(file_name);
3469Assignment* RoutingModel::ReadAssignment(
const std::string& file_name) {
3471 CHECK(assignment_ !=
nullptr);
3472 if (assignment_->Load(file_name)) {
3473 return DoRestoreAssignment();
3478Assignment* RoutingModel::RestoreAssignment(
const Assignment&
solution) {
3480 CHECK(assignment_ !=
nullptr);
3481 assignment_->CopyIntersection(&
solution);
3482 return DoRestoreAssignment();
3485Assignment* RoutingModel::DoRestoreAssignment() {
3486 if (status_ == ROUTING_INVALID) {
3489 solver_->Solve(restore_assignment_, monitors_);
3490 if (collect_assignments_->solution_count() == 1) {
3491 status_ = ROUTING_SUCCESS;
3492 return collect_assignments_->solution(0);
3494 status_ = ROUTING_FAIL;
3500bool RoutingModel::RoutesToAssignment(
3501 const std::vector<std::vector<int64_t>>& routes,
3502 bool ignore_inactive_indices,
bool close_routes,
3503 Assignment*
const assignment)
const {
3504 CHECK(assignment !=
nullptr);
3506 LOG(ERROR) <<
"The model is not closed yet";
3509 const int num_routes = routes.size();
3510 if (num_routes > vehicles_) {
3511 LOG(ERROR) <<
"The number of vehicles in the assignment (" << routes.size()
3512 <<
") is greater than the number of vehicles in the model ("
3513 << vehicles_ <<
")";
3517 absl::flat_hash_set<int> visited_indices;
3519 for (
int vehicle = 0; vehicle < num_routes; ++vehicle) {
3520 const std::vector<int64_t>& route = routes[vehicle];
3521 int from_index = Start(vehicle);
3522 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3523 visited_indices.insert(from_index);
3524 if (!insert_result.second) {
3525 LOG(ERROR) <<
"Index " << from_index <<
" (start node for vehicle "
3526 << vehicle <<
") was already used";
3530 for (
const int64_t to_index : route) {
3531 if (to_index < 0 || to_index >= Size()) {
3532 LOG(ERROR) <<
"Invalid index: " << to_index;
3536 IntVar*
const active_var = ActiveVar(to_index);
3537 if (active_var->Max() == 0) {
3538 if (ignore_inactive_indices) {
3541 LOG(ERROR) <<
"Index " << to_index <<
" is not active";
3546 insert_result = visited_indices.insert(to_index);
3547 if (!insert_result.second) {
3548 LOG(ERROR) <<
"Index " << to_index <<
" is used multiple times";
3552 const IntVar*
const vehicle_var = VehicleVar(to_index);
3553 if (!vehicle_var->Contains(vehicle)) {
3554 LOG(ERROR) <<
"Vehicle " << vehicle <<
" is not allowed at index "
3559 IntVar*
const from_var = NextVar(from_index);
3560 if (!assignment->Contains(from_var)) {
3561 assignment->Add(from_var);
3563 assignment->SetValue(from_var, to_index);
3565 from_index = to_index;
3569 IntVar*
const last_var = NextVar(from_index);
3570 if (!assignment->Contains(last_var)) {
3571 assignment->Add(last_var);
3573 assignment->SetValue(last_var, End(vehicle));
3578 for (
int vehicle = num_routes; vehicle < vehicles_; ++vehicle) {
3579 const int start_index = Start(vehicle);
3582 std::pair<absl::flat_hash_set<int>::iterator,
bool> insert_result =
3583 visited_indices.insert(start_index);
3584 if (!insert_result.second) {
3585 LOG(ERROR) <<
"Index " << start_index <<
" is used multiple times";
3589 IntVar*
const start_var = NextVar(start_index);
3590 if (!assignment->Contains(start_var)) {
3591 assignment->Add(start_var);
3593 assignment->SetValue(start_var, End(vehicle));
3600 if (!visited_indices.contains(
index)) {
3601 IntVar*
const next_var = NextVar(
index);
3602 if (!assignment->Contains(next_var)) {
3603 assignment->Add(next_var);
3605 assignment->SetValue(next_var,
index);
3613Assignment* RoutingModel::ReadAssignmentFromRoutes(
3614 const std::vector<std::vector<int64_t>>& routes,
3615 bool ignore_inactive_indices) {
3617 if (!RoutesToAssignment(routes, ignore_inactive_indices,
true, assignment_)) {
3623 return DoRestoreAssignment();
3626void RoutingModel::AssignmentToRoutes(
3627 const Assignment& assignment,
3628 std::vector<std::vector<int64_t>>*
const routes)
const {
3630 CHECK(routes !=
nullptr);
3632 const int model_size = Size();
3633 routes->resize(vehicles_);
3634 for (
int vehicle = 0; vehicle < vehicles_; ++vehicle) {
3635 std::vector<int64_t>*
const vehicle_route = &routes->at(vehicle);
3636 vehicle_route->clear();
3638 int num_visited_indices = 0;
3639 const int first_index = Start(vehicle);
3640 const IntVar*
const first_var = NextVar(first_index);
3641 CHECK(assignment.Contains(first_var));
3642 CHECK(assignment.Bound(first_var));
3643 int current_index = assignment.Value(first_var);
3644 while (!IsEnd(current_index)) {
3645 vehicle_route->push_back(current_index);
3647 const IntVar*
const next_var = NextVar(current_index);
3648 CHECK(assignment.Contains(next_var));
3649 CHECK(assignment.Bound(next_var));
3650 current_index = assignment.Value(next_var);
3652 ++num_visited_indices;
3653 CHECK_LE(num_visited_indices, model_size)
3654 <<
"The assignment contains a cycle";
3660std::vector<std::vector<int64_t>> RoutingModel::GetRoutesFromAssignment(
3661 const Assignment& assignment) {
3662 std::vector<std::vector<int64_t>> route_indices(vehicles());
3663 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
3664 if (!assignment.Bound(NextVar(vehicle))) {
3665 LOG(DFATAL) <<
"GetRoutesFromAssignment() called on incomplete solution:"
3666 <<
" NextVar(" << vehicle <<
") is unbound.";
3669 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
3670 int64_t
index = Start(vehicle);
3671 route_indices[vehicle].push_back(
index);
3672 while (!IsEnd(
index)) {
3674 route_indices[vehicle].push_back(
index);
3677 return route_indices;
3681int64_t RoutingModel::GetArcCostForClassInternal(
3682 int64_t from_index, int64_t to_index,
3687 CostCacheElement*
const cache = &cost_cache_[from_index];
3689 if (cache->index ==
static_cast<int>(to_index) &&
3695 const auto& evaluator = transit_evaluators_[cost_class.evaluator_index];
3696 if (!IsStart(from_index)) {
3697 cost =
CapAdd(evaluator(from_index, to_index),
3698 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3699 }
else if (!IsEnd(to_index)) {
3703 evaluator(from_index, to_index),
3704 CapAdd(GetDimensionTransitCostSum(from_index, to_index, cost_class),
3705 fixed_cost_of_vehicle_[VehicleIndex(from_index)]));
3709 if (vehicle_used_when_empty_[VehicleIndex(from_index)]) {
3711 CapAdd(evaluator(from_index, to_index),
3712 GetDimensionTransitCostSum(from_index, to_index, cost_class));
3721bool RoutingModel::IsVehicleUsed(
const Assignment& assignment,
3722 int vehicle)
const {
3723 CHECK_GE(vehicle, 0);
3724 CHECK_LT(vehicle, vehicles_);
3725 CHECK_EQ(solver_.get(), assignment.solver());
3726 IntVar*
const start_var = NextVar(Start(vehicle));
3727 CHECK(assignment.Contains(start_var));
3728 return !IsEnd(assignment.Value(start_var));
3731int64_t RoutingModel::Next(
const Assignment& assignment, int64_t
index)
const {
3732 CHECK_EQ(solver_.get(), assignment.solver());
3733 IntVar*
const next_var = NextVar(
index);
3734 CHECK(assignment.Contains(next_var));
3735 CHECK(assignment.Bound(next_var));
3736 return assignment.Value(next_var);
3739int64_t RoutingModel::GetArcCostForVehicle(int64_t from_index, int64_t to_index,
3740 int64_t vehicle)
const {
3741 if (from_index != to_index && vehicle >= 0) {
3742 return GetArcCostForClassInternal(from_index, to_index,
3743 GetCostClassIndexOfVehicle(vehicle));
3749int64_t RoutingModel::GetArcCostForClass(
3750 int64_t from_index, int64_t to_index,
3752 if (from_index != to_index) {
3753 return GetArcCostForClassInternal(from_index, to_index,
3760int64_t RoutingModel::GetArcCostForFirstSolution(int64_t from_index,
3761 int64_t to_index)
const {
3765 if (!is_bound_to_end_ct_added_.Switched()) {
3768 std::vector<IntVar*> zero_transit(Size(), solver_->MakeIntConst(0));
3769 solver_->AddConstraint(solver_->MakeDelayedPathCumul(
3770 nexts_, active_, is_bound_to_end_, zero_transit));
3771 is_bound_to_end_ct_added_.Switch(solver_.get());
3773 if (is_bound_to_end_[to_index]->Min() == 1)
3774 return std::numeric_limits<int64_t>::max();
3776 return GetHomogeneousCost(from_index, to_index);
3779int64_t RoutingModel::GetDimensionTransitCostSum(
3780 int64_t i, int64_t j,
const CostClass& cost_class)
const {
3782 for ([[maybe_unused]]
const auto [transit_evaluator_class,
3783 span_cost_coefficient,
3784 unused_slack_cost_coefficient, dimension] :
3785 cost_class.dimension_transit_evaluator_class_and_cost_coefficient) {
3786 DCHECK_GE(span_cost_coefficient, 0);
3787 if (span_cost_coefficient == 0)
continue;
3789 dimension->GetTransitValueFromClass(
3790 i, j, transit_evaluator_class)));
3795bool RoutingModel::ArcIsMoreConstrainedThanArc(int64_t from, int64_t to1,
3798 if (IsEnd(to1) || IsEnd(to2)) {
3799 if (IsEnd(to1) != IsEnd(to2))
return IsEnd(to2);
3806 const bool mandatory1 = active_[to1]->Min() == 1;
3807 const bool mandatory2 = active_[to2]->Min() == 1;
3809 if (mandatory1 != mandatory2)
return mandatory1;
3812 IntVar*
const src_vehicle_var = VehicleVar(from);
3816 const int64_t src_vehicle = src_vehicle_var->Max();
3817 if (src_vehicle_var->Bound()) {
3818 IntVar*
const to1_vehicle_var = VehicleVar(to1);
3819 IntVar*
const to2_vehicle_var = VehicleVar(to2);
3824 mandatory1 ? to1_vehicle_var->Bound() : (to1_vehicle_var->Size() <= 2);
3826 mandatory2 ? to2_vehicle_var->Bound() : (to2_vehicle_var->Size() <= 2);
3829 if (bound1 != bound2)
return bound1;
3832 const int64_t vehicle1 = to1_vehicle_var->Max();
3833 const int64_t vehicle2 = to2_vehicle_var->Max();
3836 if ((vehicle1 == src_vehicle) != (vehicle2 == src_vehicle)) {
3837 return vehicle1 == src_vehicle;
3842 if (vehicle1 != src_vehicle)
return to1 < to2;
3852 if (!GetPrimaryConstrainedDimension().empty()) {
3853 const std::vector<IntVar*>& cumul_vars =
3854 GetDimensionOrDie(GetPrimaryConstrainedDimension()).cumuls();
3855 IntVar*
const dim1 = cumul_vars[to1];
3856 IntVar*
const dim2 = cumul_vars[to2];
3859 if (dim1->Max() != dim2->Max())
return dim1->Max() < dim2->Max();
3869 SafeGetCostClassInt64OfVehicle(src_vehicle);
3870 const int64_t cost1 =
3872 UnperformedPenalty(to1));
3873 const int64_t cost2 =
3875 UnperformedPenalty(to2));
3876 if (cost1 != cost2)
return cost1 < cost2;
3881 const int64_t num_vehicles1 = VehicleVar(to1)->Size();
3882 const int64_t num_vehicles2 = VehicleVar(to2)->Size();
3883 if (num_vehicles1 != num_vehicles2)
return num_vehicles1 < num_vehicles2;
3890void RoutingModel::SetVisitType(int64_t
index,
int type,
3891 VisitTypePolicy policy) {
3892 CHECK_LT(
index, index_to_visit_type_.size());
3893 DCHECK_EQ(index_to_visit_type_.size(), index_to_type_policy_.size());
3894 index_to_visit_type_[
index] = type;
3895 index_to_type_policy_[
index] = policy;
3896 num_visit_types_ = std::max(num_visit_types_, type + 1);
3899int RoutingModel::GetVisitType(int64_t
index)
const {
3900 CHECK_LT(
index, index_to_visit_type_.size());
3901 return index_to_visit_type_[
index];
3904const std::vector<int>& RoutingModel::GetSingleNodesOfType(
int type)
const {
3905 DCHECK_LT(type, single_nodes_of_type_.size());
3906 return single_nodes_of_type_[type];
3909const std::vector<int>& RoutingModel::GetPairIndicesOfType(
int type)
const {
3910 DCHECK_LT(type, pair_indices_of_type_.size());
3911 return pair_indices_of_type_[type];
3914RoutingModel::VisitTypePolicy RoutingModel::GetVisitTypePolicy(
3915 int64_t
index)
const {
3916 CHECK_LT(
index, index_to_type_policy_.size());
3917 return index_to_type_policy_[
index];
3920void RoutingModel::CloseVisitTypes() {
3921 hard_incompatible_types_per_type_index_.resize(num_visit_types_);
3922 temporal_incompatible_types_per_type_index_.resize(num_visit_types_);
3923 same_vehicle_required_type_alternatives_per_type_index_.resize(
3925 required_type_alternatives_when_adding_type_index_.resize(num_visit_types_);
3926 required_type_alternatives_when_removing_type_index_.resize(num_visit_types_);
3929void RoutingModel::AddHardTypeIncompatibility(
int type1,
int type2) {
3930 DCHECK_LT(std::max(type1, type2),
3931 hard_incompatible_types_per_type_index_.size());
3932 has_hard_type_incompatibilities_ =
true;
3934 hard_incompatible_types_per_type_index_[type1].insert(type2);
3935 hard_incompatible_types_per_type_index_[type2].insert(type1);
3938void RoutingModel::AddTemporalTypeIncompatibility(
int type1,
int type2) {
3939 DCHECK_LT(std::max(type1, type2),
3940 temporal_incompatible_types_per_type_index_.size());
3941 has_temporal_type_incompatibilities_ =
true;
3943 temporal_incompatible_types_per_type_index_[type1].insert(type2);
3944 temporal_incompatible_types_per_type_index_[type2].insert(type1);
3947const absl::flat_hash_set<int>&
3948RoutingModel::GetHardTypeIncompatibilitiesOfType(
int type)
const {
3950 DCHECK_LT(type, hard_incompatible_types_per_type_index_.size());
3951 return hard_incompatible_types_per_type_index_[type];
3954const absl::flat_hash_set<int>&
3955RoutingModel::GetTemporalTypeIncompatibilitiesOfType(
int type)
const {
3957 DCHECK_LT(type, temporal_incompatible_types_per_type_index_.size());
3958 return temporal_incompatible_types_per_type_index_[type];
3963void RoutingModel::AddSameVehicleRequiredTypeAlternatives(
3964 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
3965 DCHECK_LT(dependent_type,
3966 same_vehicle_required_type_alternatives_per_type_index_.size());
3968 if (required_type_alternatives.empty()) {
3972 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
3973 trivially_infeasible_visit_types_to_policies_[dependent_type];
3974 infeasible_policies.insert(TYPE_ADDED_TO_VEHICLE);
3975 infeasible_policies.insert(TYPE_ON_VEHICLE_UP_TO_VISIT);
3976 infeasible_policies.insert(TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED);
3980 has_same_vehicle_type_requirements_ =
true;
3981 same_vehicle_required_type_alternatives_per_type_index_[dependent_type]
3982 .push_back(std::move(required_type_alternatives));
3985void RoutingModel::AddRequiredTypeAlternativesWhenAddingType(
3986 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
3987 DCHECK_LT(dependent_type,
3988 required_type_alternatives_when_adding_type_index_.size());
3990 if (required_type_alternatives.empty()) {
3994 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
3995 trivially_infeasible_visit_types_to_policies_[dependent_type];
3996 infeasible_policies.insert(TYPE_ADDED_TO_VEHICLE);
3997 infeasible_policies.insert(TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED);
4001 has_temporal_type_requirements_ =
true;
4002 required_type_alternatives_when_adding_type_index_[dependent_type].push_back(
4003 std::move(required_type_alternatives));
4006void RoutingModel::AddRequiredTypeAlternativesWhenRemovingType(
4007 int dependent_type, absl::flat_hash_set<int> required_type_alternatives) {
4008 DCHECK_LT(dependent_type,
4009 required_type_alternatives_when_removing_type_index_.size());
4011 if (required_type_alternatives.empty()) {
4015 absl::flat_hash_set<VisitTypePolicy>& infeasible_policies =
4016 trivially_infeasible_visit_types_to_policies_[dependent_type];
4017 infeasible_policies.insert(ADDED_TYPE_REMOVED_FROM_VEHICLE);
4018 infeasible_policies.insert(TYPE_ON_VEHICLE_UP_TO_VISIT);
4019 infeasible_policies.insert(TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED);
4023 has_temporal_type_requirements_ =
true;
4024 required_type_alternatives_when_removing_type_index_[dependent_type]
4025 .push_back(std::move(required_type_alternatives));
4028const std::vector<absl::flat_hash_set<int>>&
4029RoutingModel::GetSameVehicleRequiredTypeAlternativesOfType(
int type)
const {
4032 same_vehicle_required_type_alternatives_per_type_index_.size());
4033 return same_vehicle_required_type_alternatives_per_type_index_[type];
4036const std::vector<absl::flat_hash_set<int>>&
4037RoutingModel::GetRequiredTypeAlternativesWhenAddingType(
int type)
const {
4039 DCHECK_LT(type, required_type_alternatives_when_adding_type_index_.size());
4040 return required_type_alternatives_when_adding_type_index_[type];
4043const std::vector<absl::flat_hash_set<int>>&
4044RoutingModel::GetRequiredTypeAlternativesWhenRemovingType(
int type)
const {
4046 DCHECK_LT(type, required_type_alternatives_when_removing_type_index_.size());
4047 return required_type_alternatives_when_removing_type_index_[type];
4050int64_t RoutingModel::UnperformedPenalty(int64_t
var_index)
const {
4051 return UnperformedPenaltyOrValue(0,
var_index);
4054int64_t RoutingModel::UnperformedPenaltyOrValue(int64_t default_value,
4057 return std::numeric_limits<int64_t>::max();
4058 const std::vector<DisjunctionIndex>& disjunction_indices =
4060 if (disjunction_indices.size() != 1)
return default_value;
4061 const DisjunctionIndex disjunction_index = disjunction_indices[0];
4065 return std::max(int64_t{0}, disjunctions_[disjunction_index].value.penalty);
4068std::string RoutingModel::DebugOutputAssignment(
4069 const Assignment& solution_assignment,
4070 const std::string& dimension_to_print)
const {
4071 for (
int i = 0;
i < Size(); ++
i) {
4072 if (!solution_assignment.Bound(NextVar(i))) {
4074 <<
"DebugOutputVehicleSchedules() called on incomplete solution:"
4075 <<
" NextVar(" <<
i <<
") is unbound.";
4080 absl::flat_hash_set<std::string> dimension_names;
4081 if (dimension_to_print.empty()) {
4082 const std::vector<std::string> all_dimension_names = GetAllDimensionNames();
4083 dimension_names.insert(all_dimension_names.begin(),
4084 all_dimension_names.end());
4086 dimension_names.insert(dimension_to_print);
4088 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4089 int empty_vehicle_range_start = vehicle;
4090 while (vehicle < vehicles() &&
4091 IsEnd(solution_assignment.Value(NextVar(Start(vehicle))))) {
4094 if (empty_vehicle_range_start != vehicle) {
4095 if (empty_vehicle_range_start == vehicle - 1) {
4096 absl::StrAppendFormat(&output,
"Vehicle %d: empty",
4097 empty_vehicle_range_start);
4099 absl::StrAppendFormat(&output,
"Vehicles %d-%d: empty",
4100 empty_vehicle_range_start, vehicle - 1);
4102 output.append(
"\n");
4104 if (vehicle < vehicles()) {
4105 absl::StrAppendFormat(&output,
"Vehicle %d:", vehicle);
4106 int64_t
index = Start(vehicle);
4108 const IntVar* vehicle_var = VehicleVar(
index);
4109 absl::StrAppendFormat(&output,
"%d Vehicle(%d) ",
index,
4110 solution_assignment.Value(vehicle_var));
4111 for (
const RoutingDimension*
const dimension : dimensions_) {
4112 if (dimension_names.contains(dimension->name())) {
4113 const IntVar*
const var = dimension->CumulVar(
index);
4114 absl::StrAppendFormat(&output,
"%s(%d..%d) ", dimension->name(),
4115 solution_assignment.Min(
var),
4116 solution_assignment.Max(
var));
4119 if (IsEnd(
index))
break;
4120 index = solution_assignment.Value(NextVar(
index));
4121 if (IsEnd(
index)) output.append(
"Route end ");
4123 output.append(
"\n");
4126 output.append(
"Unperformed nodes: ");
4127 bool has_unperformed =
false;
4128 for (
int i = 0;
i < Size(); ++
i) {
4129 if (!IsEnd(i) && !IsStart(i) &&
4130 solution_assignment.Value(NextVar(i)) == i) {
4131 absl::StrAppendFormat(&output,
"%d ", i);
4132 has_unperformed =
true;
4135 if (!has_unperformed) output.append(
"None");
4136 output.append(
"\n");
4141std::vector<std::vector<std::pair<int64_t, int64_t>>>
4142RoutingModel::GetCumulBounds(
const Assignment& solution_assignment,
4143 const RoutingDimension& dimension) {
4144 std::vector<std::vector<std::pair<int64_t, int64_t>>> cumul_bounds(
4146 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4147 if (!solution_assignment.Bound(NextVar(vehicle))) {
4148 LOG(DFATAL) <<
"GetCumulBounds() called on incomplete solution:"
4149 <<
" NextVar(" << vehicle <<
") is unbound.";
4153 for (
int vehicle_id = 0; vehicle_id < vehicles(); ++vehicle_id) {
4154 int64_t
index = Start(vehicle_id);
4155 IntVar* dim_var = dimension.CumulVar(
index);
4156 cumul_bounds[vehicle_id].emplace_back(solution_assignment.Min(dim_var),
4157 solution_assignment.Max(dim_var));
4158 while (!IsEnd(
index)) {
4159 index = solution_assignment.Value(NextVar(
index));
4160 IntVar* dim_var = dimension.CumulVar(
index);
4161 cumul_bounds[vehicle_id].emplace_back(solution_assignment.Min(dim_var),
4162 solution_assignment.Max(dim_var));
4165 return cumul_bounds;
4169Assignment* RoutingModel::GetOrCreateAssignment() {
4170 if (assignment_ ==
nullptr) {
4171 assignment_ = solver_->MakeAssignment();
4172 assignment_->Add(nexts_);
4173 if (!CostsAreHomogeneousAcrossVehicles()) {
4174 assignment_->Add(vehicle_vars_);
4176 assignment_->AddObjective(cost_);
4181Assignment* RoutingModel::GetOrCreateTmpAssignment() {
4182 if (tmp_assignment_ ==
nullptr) {
4183 tmp_assignment_ = solver_->MakeAssignment();
4184 tmp_assignment_->Add(nexts_);
4186 return tmp_assignment_;
4189RegularLimit* RoutingModel::GetOrCreateLimit() {
4191 limit_ = solver_->MakeLimit(
4192 absl::InfiniteDuration(), std::numeric_limits<int64_t>::max(),
4193 std::numeric_limits<int64_t>::max(),
4194 std::numeric_limits<int64_t>::max(),
true);
4199RegularLimit* RoutingModel::GetOrCreateCumulativeLimit() {
4200 if (cumulative_limit_ ==
nullptr) {
4201 cumulative_limit_ = solver_->MakeLimit(
4202 absl::InfiniteDuration(), std::numeric_limits<int64_t>::max(),
4203 std::numeric_limits<int64_t>::max(),
4204 std::numeric_limits<int64_t>::max(),
true,
4207 return cumulative_limit_;
4210RegularLimit* RoutingModel::GetOrCreateLocalSearchLimit() {
4211 if (ls_limit_ ==
nullptr) {
4212 ls_limit_ = solver_->MakeLimit(absl::InfiniteDuration(),
4213 std::numeric_limits<int64_t>::max(),
4214 std::numeric_limits<int64_t>::max(),
4220RegularLimit* RoutingModel::GetOrCreateLargeNeighborhoodSearchLimit() {
4221 if (lns_limit_ ==
nullptr) {
4222 lns_limit_ = solver_->MakeLimit(
4223 absl::InfiniteDuration(), std::numeric_limits<int64_t>::max(),
4224 std::numeric_limits<int64_t>::max(),
4225 std::numeric_limits<int64_t>::max(),
false);
4231RoutingModel::GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit() {
4232 if (first_solution_lns_limit_ ==
nullptr) {
4233 first_solution_lns_limit_ = solver_->MakeLimit(
4234 absl::InfiniteDuration(), std::numeric_limits<int64_t>::max(),
4235 std::numeric_limits<int64_t>::max(),
4236 std::numeric_limits<int64_t>::max(),
false);
4238 return first_solution_lns_limit_;
4241LocalSearchOperator* RoutingModel::CreateInsertionOperator() {
4242 LocalSearchOperator* insertion_operator =
4243 CreateCPOperator<MakeActiveOperator>();
4244 if (!pickup_delivery_pairs_.empty()) {
4245 insertion_operator = solver_->ConcatenateOperators(
4246 {CreatePairOperator<MakePairActiveOperator>(), insertion_operator});
4248 if (!implicit_pickup_delivery_pairs_without_alternatives_.empty()) {
4249 insertion_operator = solver_->ConcatenateOperators(
4250 {CreateOperator<MakePairActiveOperator>(
4251 implicit_pickup_delivery_pairs_without_alternatives_),
4252 insertion_operator});
4254 return insertion_operator;
4257LocalSearchOperator* RoutingModel::CreateMakeInactiveOperator() {
4258 LocalSearchOperator* make_inactive_operator =
4259 CreateCPOperator<MakeInactiveOperator>();
4260 if (!pickup_delivery_pairs_.empty()) {
4261 make_inactive_operator = solver_->ConcatenateOperators(
4262 {CreatePairOperator<MakePairInactiveOperator>(),
4263 make_inactive_operator});
4265 return make_inactive_operator;
4268void RoutingModel::CreateNeighborhoodOperators(
4270 double neighbors_ratio_used = 1;
4271 const NodeNeighborsByCostClass* neighbors_by_cost_class =
4272 GetOrCreateNodeNeighborsByCostClass(
4274 parameters.ls_operator_min_neighbors(), neighbors_ratio_used,
4276 const auto get_neighbors = [neighbors_by_cost_class,
this](
4278 int64_t
start) ->
const std::vector<int>& {
4279 return neighbors_by_cost_class->GetNeighborsOfNodeForCostClass(
4280 GetCostClassIndexOfVehicle(VehicleIndex(
start)).
value(), node);
4283 local_search_operators_.clear();
4284 local_search_operators_.resize(LOCAL_SEARCH_OPERATOR_COUNTER,
nullptr);
4288 std::pair<RoutingLocalSearchOperator, Solver::LocalSearchOperators>>
4289 operator_by_type = {{OR_OPT, Solver::OROPT},
4290 {PATH_LNS, Solver::PATHLNS},
4291 {FULL_PATH_LNS, Solver::FULLPATHLNS},
4292 {INACTIVE_LNS, Solver::UNACTIVELNS}};
4293 for (
const auto [type, op] : operator_by_type) {
4294 local_search_operators_[type] =
4295 CostsAreHomogeneousAcrossVehicles()
4296 ? solver_->MakeOperator(nexts_, op)
4297 : solver_->MakeOperator(nexts_, vehicle_vars_, op);
4302 const std::vector<std::pair<RoutingLocalSearchOperator,
4303 Solver::EvaluatorLocalSearchOperators>>
4304 operator_by_type = {{LIN_KERNIGHAN, Solver::LK},
4305 {TSP_OPT, Solver::TSPOPT},
4306 {TSP_LNS, Solver::TSPLNS}};
4307 for (
const auto [type, op] : operator_by_type) {
4309 absl::bind_front(&RoutingModel::GetArcCostForVehicle,
this);
4310 local_search_operators_[type] =
4311 CostsAreHomogeneousAcrossVehicles()
4312 ? solver_->MakeOperator(nexts_, std::move(arc_cost), op)
4313 : solver_->MakeOperator(nexts_, vehicle_vars_,
4314 std::move(arc_cost), op);
4319 local_search_operators_[RELOCATE] =
4320 CreateOperatorWithNeighborsRatio<Relocate>(neighbors_ratio_used,
4322 local_search_operators_[EXCHANGE] =
4323 CreateOperatorWithNeighborsRatio<Exchange>(neighbors_ratio_used,
4325 local_search_operators_[CROSS] = CreateOperatorWithNeighborsRatio<Cross>(
4326 neighbors_ratio_used, get_neighbors);
4327 local_search_operators_[TWO_OPT] = CreateOperatorWithNeighborsRatio<TwoOpt>(
4328 neighbors_ratio_used, get_neighbors);
4329 local_search_operators_[RELOCATE_AND_MAKE_ACTIVE] =
4330 CreateCPOperator<RelocateAndMakeActiveOperator>();
4331 local_search_operators_[MAKE_ACTIVE_AND_RELOCATE] =
4332 CreateCPOperator<MakeActiveAndRelocate>();
4333 local_search_operators_[MAKE_CHAIN_INACTIVE] =
4334 CreateCPOperator<MakeChainInactiveOperator>();
4335 local_search_operators_[SWAP_ACTIVE] = CreateCPOperator<SwapActiveOperator>();
4336 local_search_operators_[EXTENDED_SWAP_ACTIVE] =
4337 CreateCPOperator<ExtendedSwapActiveOperator>();
4338 std::vector<std::vector<int64_t>> alternative_sets(disjunctions_.size());
4339 for (
const RoutingModel::Disjunction& disjunction : disjunctions_) {
4342 if (disjunction.value.max_cardinality == 1) {
4343 alternative_sets.push_back(disjunction.indices);
4346 local_search_operators_[SHORTEST_PATH_SWAP_ACTIVE] =
4347 CreateOperator<SwapActiveToShortestPathOperator>(
4348 std::move(alternative_sets),
4349 absl::bind_front(&RoutingModel::GetHomogeneousCost,
this));
4352 local_search_operators_[MAKE_ACTIVE] = CreateInsertionOperator();
4353 local_search_operators_[MAKE_INACTIVE] = CreateMakeInactiveOperator();
4354 local_search_operators_[RELOCATE_PAIR] =
4355 CreatePairOperator<PairRelocateOperator>();
4356 std::vector<LocalSearchOperator*> light_relocate_pair_operators;
4357 light_relocate_pair_operators.push_back(
4358 CreateOperatorWithNeighborsRatio<LightPairRelocateOperator>(
4359 neighbors_ratio_used, get_neighbors, pickup_delivery_pairs_,
4360 [
this](int64_t
start) {
4361 return vehicle_pickup_delivery_policy_[VehicleIndex(
start)] ==
4362 RoutingModel::PICKUP_AND_DELIVERY_LIFO;
4364 light_relocate_pair_operators.push_back(
4365 CreatePairOperator<GroupPairAndRelocateOperator>(neighbors_ratio_used,
4367 local_search_operators_[LIGHT_RELOCATE_PAIR] =
4368 solver_->ConcatenateOperators(light_relocate_pair_operators);
4369 local_search_operators_[EXCHANGE_PAIR] = solver_->ConcatenateOperators(
4370 {CreatePairOperator<PairExchangeOperator>(neighbors_ratio_used,
4372 solver_->RevAlloc(
new SwapIndexPairOperator(
4374 CostsAreHomogeneousAcrossVehicles() ? std::vector<IntVar*>()
4376 pickup_delivery_pairs_))});
4377 local_search_operators_[EXCHANGE_RELOCATE_PAIR] =
4378 CreatePairOperator<PairExchangeRelocateOperator>();
4379 local_search_operators_[RELOCATE_NEIGHBORS] =
4380 CreateOperatorWithNeighborsRatio<MakeRelocateNeighborsOperator>(
4381 neighbors_ratio_used, get_neighbors,
4382 absl::bind_front(&RoutingModel::GetHomogeneousCost,
this));
4383 local_search_operators_[NODE_PAIR_SWAP] = solver_->ConcatenateOperators(
4384 {CreatePairOperator<IndexPairSwapActiveOperator>(),
4385 CreatePairOperator<PairNodeSwapActiveOperator<true>>(),
4386 CreatePairOperator<PairNodeSwapActiveOperator<false>>()});
4387 local_search_operators_[RELOCATE_SUBTRIP] =
4388 CreatePairOperator<RelocateSubtrip>(neighbors_ratio_used, get_neighbors);
4389 local_search_operators_[EXCHANGE_SUBTRIP] =
4390 CreatePairOperator<ExchangeSubtrip>(neighbors_ratio_used, get_neighbors);
4392 const auto arc_cost_for_path_start =
4393 [
this](int64_t before_node, int64_t after_node, int64_t start_index) {
4394 const int vehicle = VehicleIndex(start_index);
4395 const int64_t arc_cost =
4396 GetArcCostForVehicle(before_node, after_node, vehicle);
4397 return (before_node != start_index || IsEnd(after_node))
4399 :
CapSub(arc_cost, GetFixedCostOfVehicle(vehicle));
4401 local_search_operators_[RELOCATE_EXPENSIVE_CHAIN] =
4402 solver_->RevAlloc(
new RelocateExpensiveChain(
4404 CostsAreHomogeneousAcrossVehicles() ? std::vector<IntVar*>()
4406 vehicle_start_class_callback_,
4407 parameters.relocate_expensive_chain_num_arcs_to_consider(),
4408 arc_cost_for_path_start));
4411 const auto make_global_cheapest_insertion_filtered_heuristic =
4413 using Heuristic = GlobalCheapestInsertionFilteredHeuristic;
4414 Heuristic::GlobalCheapestInsertionParameters ls_gci_parameters;
4415 ls_gci_parameters.is_sequential =
false;
4416 ls_gci_parameters.farthest_seeds_ratio = 0.0;
4417 ls_gci_parameters.neighbors_ratio =
4418 parameters.cheapest_insertion_ls_operator_neighbors_ratio();
4419 ls_gci_parameters.min_neighbors =
4420 parameters.cheapest_insertion_ls_operator_min_neighbors();
4421 ls_gci_parameters.use_neighbors_ratio_for_initialization =
true;
4422 ls_gci_parameters.add_unperformed_entries =
4423 parameters.cheapest_insertion_add_unperformed_entries();
4424 return std::make_unique<Heuristic>(
4425 this, [
this]() {
return CheckLimit(time_buffer_); },
4426 absl::bind_front(&RoutingModel::GetArcCostForVehicle,
this),
4427 absl::bind_front(&RoutingModel::UnperformedPenaltyOrValue,
this, 0),
4428 GetOrCreateLocalSearchFilterManager(
4433 const auto make_local_cheapest_insertion_filtered_heuristic =
4435 return std::make_unique<LocalCheapestInsertionFilteredHeuristic>(
4436 this, [
this]() {
return CheckLimit(time_buffer_); },
4437 absl::bind_front(&RoutingModel::GetArcCostForVehicle,
this),
4438 parameters.local_cheapest_insertion_pickup_delivery_strategy(),
4439 GetOrCreateLocalSearchFilterManager(
4442 bin_capacities_.get());
4444 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4445 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4446 make_global_cheapest_insertion_filtered_heuristic(),
4447 parameters.heuristic_close_nodes_lns_num_nodes()));
4449 local_search_operators_[LOCAL_CHEAPEST_INSERTION_CLOSE_NODES_LNS] =
4450 solver_->RevAlloc(
new FilteredHeuristicCloseNodesLNSOperator(
4451 make_local_cheapest_insertion_filtered_heuristic(),
4452 parameters.heuristic_close_nodes_lns_num_nodes()));
4454 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_PATH_LNS] =
4455 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4456 make_global_cheapest_insertion_filtered_heuristic()));
4458 local_search_operators_[LOCAL_CHEAPEST_INSERTION_PATH_LNS] =
4459 solver_->RevAlloc(
new FilteredHeuristicPathLNSOperator(
4460 make_local_cheapest_insertion_filtered_heuristic()));
4462 local_search_operators_
4463 [RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED] =
4465 new RelocatePathAndHeuristicInsertUnperformedOperator(
4466 make_global_cheapest_insertion_filtered_heuristic()));
4468 local_search_operators_[GLOBAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4469 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4470 make_global_cheapest_insertion_filtered_heuristic(),
4471 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4472 arc_cost_for_path_start));
4474 local_search_operators_[LOCAL_CHEAPEST_INSERTION_EXPENSIVE_CHAIN_LNS] =
4475 solver_->RevAlloc(
new FilteredHeuristicExpensiveChainLNSOperator(
4476 make_local_cheapest_insertion_filtered_heuristic(),
4477 parameters.heuristic_expensive_chain_lns_num_arcs_to_consider(),
4478 arc_cost_for_path_start));
4481#define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method) \
4482 if (operators_to_consider.contains(operator_type) && \
4483 search_parameters.local_search_operators().use_##operator_method() == \
4485 operators.push_back(local_search_operators_[operator_type]); \
4488LocalSearchOperator* RoutingModel::ConcatenateOperators(
4489 const RoutingSearchParameters& search_parameters,
4490 const std::vector<LocalSearchOperator*>& operators)
const {
4491 if (search_parameters.use_multi_armed_bandit_concatenate_operators()) {
4492 return solver_->MultiArmedBanditConcatenateOperators(
4495 .multi_armed_bandit_compound_operator_memory_coefficient(),
4497 .multi_armed_bandit_compound_operator_exploration_coefficient(),
4500 return solver_->ConcatenateOperators(operators);
4503LocalSearchOperator* RoutingModel::GetNeighborhoodOperators(
4504 const RoutingSearchParameters& search_parameters,
4505 const absl::flat_hash_set<RoutingLocalSearchOperator>&
4506 operators_to_consider)
const {
4507 std::vector<LocalSearchOperator*> operator_groups;
4508 std::vector<LocalSearchOperator*> operators = extra_operators_;
4509 if (!pickup_delivery_pairs_.empty()) {
4513 if (search_parameters.local_search_operators().use_relocate_pair() ==
4522 if (vehicles_ > 1) {
4523 if (GetNumOfSingletonNodes() > 0) {
4533 if (!pickup_delivery_pairs_.empty() ||
4534 search_parameters.local_search_operators().use_relocate_neighbors() ==
4536 operators.push_back(local_search_operators_[RELOCATE_NEIGHBORS]);
4538 const LocalSearchMetaheuristic::Value local_search_metaheuristic =
4539 search_parameters.local_search_metaheuristic();
4540 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4541 local_search_metaheuristic !=
4542 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4543 local_search_metaheuristic !=
4544 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4550 if (!disjunctions_.empty()) {
4558 relocate_and_make_active);
4560 relocate_and_make_active);
4565 shortest_path_swap_active);
4567 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4571 if (vehicles() > 1) {
4576 global_cheapest_insertion_path_lns);
4578 local_cheapest_insertion_path_lns);
4580 RELOCATE_PATH_GLOBAL_CHEAPEST_INSERTION_INSERT_UNPERFORMED,
4581 relocate_path_global_cheapest_insertion_insert_unperformed);
4584 global_cheapest_insertion_expensive_chain_lns);
4586 local_cheapest_insertion_expensive_chain_lns);
4588 global_cheapest_insertion_close_nodes_lns);
4590 local_cheapest_insertion_close_nodes_lns);
4591 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4595 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4596 local_search_metaheuristic !=
4597 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4598 local_search_metaheuristic !=
4599 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4602 if (local_search_metaheuristic != LocalSearchMetaheuristic::TABU_SEARCH &&
4603 local_search_metaheuristic !=
4604 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH &&
4605 local_search_metaheuristic !=
4606 LocalSearchMetaheuristic::SIMULATED_ANNEALING) {
4611 if (!disjunctions_.empty()) {
4614 operator_groups.push_back(ConcatenateOperators(search_parameters, operators));
4616 return solver_->ConcatenateOperators(operator_groups);
4619#undef CP_ROUTING_PUSH_OPERATOR
4623void ConvertVectorInt64ToVectorInt(
const std::vector<int64_t>&
input,
4624 std::vector<int>* output) {
4625 const int n =
input.size();
4627 int* data = output->data();
4628 for (
int i = 0;
i < n; ++
i) {
4629 const int element =
static_cast<int>(
input[
i]);
4630 DCHECK_EQ(
input[i],
static_cast<int64_t
>(element));
4637std::vector<LocalSearchFilterManager::FilterEvent>
4638RoutingModel::CreateLocalSearchFilters(
4639 const RoutingSearchParameters&
parameters,
const FilterOptions& options) {
4640 const auto kAccept = LocalSearchFilterManager::FilterEventType::kAccept;
4641 const auto kRelax = LocalSearchFilterManager::FilterEventType::kRelax;
4651 std::vector<LocalSearchFilterManager::FilterEvent> filter_events;
4655 if (options.filter_objective && vehicle_amortized_cost_factors_set_) {
4656 filter_events.push_back(
4663 if (options.filter_objective) {
4664 if (CostsAreHomogeneousAcrossVehicles()) {
4665 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4667 [
this](int64_t i, int64_t j) {
return GetHomogeneousCost(i, j); },
4669 filter_events.push_back({sum, kAccept, priority});
4671 LocalSearchFilter* sum = solver_->MakeSumObjectiveFilter(
4672 nexts_, vehicle_vars_,
4673 [
this](int64_t i, int64_t j, int64_t k) {
4674 return GetArcCostForVehicle(i, j, k);
4677 filter_events.push_back({sum, kAccept, priority});
4680 const PathState* path_state_reference =
nullptr;
4682 std::vector<int> path_starts;
4683 std::vector<int> path_ends;
4684 ConvertVectorInt64ToVectorInt(paths_metadata_.Starts(), &path_starts);
4685 ConvertVectorInt64ToVectorInt(paths_metadata_.Ends(), &path_ends);
4686 auto path_state = std::make_unique<PathState>(
4687 Size() + vehicles(), std::move(path_starts), std::move(path_ends));
4688 path_state_reference = path_state.get();
4689 filter_events.push_back(
4696 filter_events.push_back(
4697 {solver_->MakeVariableDomainFilter(), kAccept, priority});
4699 if (vehicles_ > max_active_vehicles_) {
4700 filter_events.push_back(
4704 if (!disjunctions_.empty()) {
4705 if (options.filter_objective || HasMandatoryDisjunctions() ||
4706 HasMaxCardinalityConstrainedDisjunctions()) {
4707 filter_events.push_back(
4709 kAccept, priority});
4716 if (CostsAreHomogeneousAcrossVehicles()) {
4723 const int first_lightweight_index = filter_events.size();
4726 for (
int e = first_lightweight_index; e < filter_events.size(); ++e) {
4727 filter_events[e].priority = priority;
4735 if (!pickup_delivery_pairs_.empty()) {
4736 filter_events.push_back(
4738 vehicle_pickup_delivery_policy_),
4739 kAccept, priority});
4741 if (options.filter_objective) {
4742 const int num_vehicles = vehicles();
4743 for (
const auto& [force_distance, unit_costs] :
4744 force_distance_to_vehicle_unit_costs_) {
4745 const auto& [force,
distance] = force_distance;
4746 const RoutingDimension* force_dimension = GetMutableDimension(force);
4747 DCHECK_NE(force_dimension,
nullptr);
4748 if (force_dimension ==
nullptr)
continue;
4749 std::vector<int> force_class(num_vehicles);
4750 std::vector<
const std::function<int64_t(int64_t)>*> force_evaluators;
4751 for (
int v = 0; v < num_vehicles; ++v) {
4752 const int c = force_dimension->vehicle_to_class(v);
4754 if (c >= force_evaluators.size()) {
4755 force_evaluators.resize(c + 1,
nullptr);
4757 if (force_evaluators[c] ==
nullptr) {
4758 force_evaluators[
c] = &(force_dimension->GetUnaryTransitEvaluator(v));
4759 DCHECK(force_evaluators[c] !=
nullptr);
4760 if (force_evaluators[c] ==
nullptr)
continue;
4763 const RoutingDimension* distance_dimension =
4765 DCHECK_NE(distance_dimension,
nullptr);
4766 if (distance_dimension ==
nullptr)
continue;
4767 std::vector<int> distance_class(num_vehicles);
4768 std::vector<
const std::function<int64_t(int64_t, int64_t)>*>
4769 distance_evaluators;
4770 for (
int v = 0; v < num_vehicles; ++v) {
4771 const int c = distance_dimension->vehicle_to_class(v);
4772 distance_class[v] =
c;
4773 if (c >= distance_evaluators.size())
4774 distance_evaluators.resize(c + 1,
nullptr);
4775 if (distance_evaluators[c] ==
nullptr) {
4776 distance_evaluators[
c] =
4777 &(distance_dimension->GetBinaryTransitEvaluator(v));
4780 auto checker = std::make_unique<PathEnergyCostChecker>(
4781 path_state_reference, std::move(force_class),
4782 std::move(force_evaluators), std::move(distance_class),
4783 std::move(distance_evaluators), unit_costs, vehicle_used_when_empty_);
4784 filter_events.push_back(
4786 absl::StrCat(force_dimension->name(),
4787 distance_dimension->name())),
4788 kAccept, priority});
4792 if (HasTypeRegulations()) {
4794 filter_events.push_back(
4800 const int first_dimension_filter_index = filter_events.size();
4802 GetDimensions(),
parameters, options.filter_objective,
4803 false, &filter_events);
4804 int max_priority = priority;
4805 for (
int e = first_dimension_filter_index; e < filter_events.size(); ++e) {
4806 filter_events[e].priority += priority;
4807 max_priority = std::max(max_priority, filter_events[e].priority);
4809 priority = max_priority;
4814 for (
const RoutingDimension* dimension : dimensions_) {
4815 if (!dimension->HasBreakConstraints())
continue;
4816 filter_events.push_back(
4821 if (!extra_filters_.empty()) {
4823 for (
const auto& event : extra_filters_) {
4824 filter_events.push_back({
event.filter,
event.event_type, priority});
4828 if (options.filter_with_cp_solver) {
4832 return filter_events;
4835LocalSearchFilterManager* RoutingModel::GetOrCreateLocalSearchFilterManager(
4836 const RoutingSearchParameters&
parameters,
const FilterOptions& options) {
4837 LocalSearchFilterManager* local_search_filter_manager =
4839 if (local_search_filter_manager ==
nullptr) {
4840 local_search_filter_manager =
4842 CreateLocalSearchFilters(
parameters, options)));
4843 local_search_filter_managers_[options] = local_search_filter_manager;
4845 return local_search_filter_manager;
4849 const std::vector<RoutingDimension*>&
dimensions,
4850 const PathsMetadata& paths_metadata) {
4851 const int num_vehicles = paths_metadata.NumPaths();
4852 auto bin_capacities = std::make_unique<BinCapacities>(num_vehicles);
4853 std::vector<BinCapacities::LoadLimit> load_limits;
4854 for (
const RoutingDimension* dimension :
dimensions) {
4856 if (dimension->GetUnaryTransitEvaluator(0) ==
nullptr)
continue;
4858 if (dimension->AllTransitEvaluatorSignsAreUnknown())
continue;
4861 load_limits.assign(num_vehicles, {.max_load =
kint64max,
4863 .cost_above_soft_max_load = 0});
4864 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
4865 const RoutingModel::TransitEvaluatorSign sign =
4866 dimension->GetTransitEvaluatorSign(vehicle);
4867 if (sign == RoutingModel::kTransitEvaluatorSignUnknown)
continue;
4874 int64_t min_node = paths_metadata.Starts()[vehicle];
4875 int64_t max_node = paths_metadata.Ends()[vehicle];
4876 if (sign == RoutingModel::kTransitEvaluatorSignNegativeOrZero) {
4877 std::swap(min_node, max_node);
4879 const int64_t load_min =
4880 std::max<int64_t>(0, dimension->CumulVar(min_node)->Min());
4881 const int64_t load_max =
4882 std::min(dimension->vehicle_capacities()[vehicle],
4883 dimension->CumulVar(max_node)->Max());
4884 load_limits[vehicle].max_load =
CapSub(load_max, load_min);
4885 if (dimension->HasCumulVarSoftUpperBound(max_node)) {
4886 load_limits[vehicle].soft_max_load =
4887 CapSub(dimension->GetCumulVarSoftUpperBound(max_node), load_min);
4888 load_limits[vehicle].cost_above_soft_max_load =
4889 dimension->GetCumulVarSoftUpperBoundCoefficient(max_node);
4892 bin_capacities->AddDimension(
4893 [dimension](
int node,
int vehicle) {
4894 return CapAbs(dimension->GetUnaryTransitEvaluator(vehicle)(node));
4898 if (bin_capacities->NumDimensions() == 0) bin_capacities.reset(
nullptr);
4899 return bin_capacities;
4903bool AllTransitsPositive(
const RoutingDimension& dimension) {
4904 for (
int vehicle = 0; vehicle < dimension.model()->vehicles(); vehicle++) {
4905 if (!dimension.AreVehicleTransitsPositive(vehicle)) {
4913void RoutingModel::StoreDimensionCumulOptimizers(
4915 Assignment* optimized_dimensions_collector_assignment =
4916 solver_->MakeAssignment();
4917 optimized_dimensions_collector_assignment->AddObjective(CostVar());
4918 const int num_dimensions = dimensions_.size();
4919 local_optimizer_index_.resize(num_dimensions, -1);
4920 global_optimizer_index_.resize(num_dimensions, -1);
4921 if (
parameters.disable_scheduling_beware_this_may_degrade_performance()) {
4924 for (DimensionIndex dim = DimensionIndex(0); dim < num_dimensions; dim++) {
4925 RoutingDimension* dimension = dimensions_[dim];
4926 DCHECK_EQ(dimension->model(),
this);
4927 const int num_resource_groups =
4928 GetDimensionResourceGroupIndices(dimension).size();
4929 bool needs_optimizer =
false;
4930 if (dimension->global_span_cost_coefficient() > 0 ||
4931 !dimension->GetNodePrecedences().empty() || num_resource_groups > 1) {
4933 needs_optimizer =
true;
4934 global_optimizer_index_[dim] = global_dimension_optimizers_.size();
4935 global_dimension_optimizers_.push_back(
4936 {std::make_unique<GlobalDimensionCumulOptimizer>(
4937 dimension,
parameters.continuous_scheduling_solver()),
4938 std::make_unique<GlobalDimensionCumulOptimizer>(
4939 dimension,
parameters.mixed_integer_scheduling_solver())});
4940 if (!AllTransitsPositive(*dimension)) {
4941 dimension->SetOffsetForGlobalOptimizer(0);
4944 vehicles() == 0 ? 0 : std::numeric_limits<int64_t>::max();
4945 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4946 DCHECK_GE(dimension->CumulVar(Start(vehicle))->Min(), 0);
4948 std::min(offset, dimension->CumulVar(Start(vehicle))->Min() - 1);
4950 if (dimension->HasBreakConstraints()) {
4951 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4952 for (
const IntervalVar* br :
4953 dimension->GetBreakIntervalsOfVehicle(vehicle)) {
4954 offset = std::min(offset,
CapSub(br->StartMin(), 1));
4959 dimension->SetOffsetForGlobalOptimizer(std::max(
Zero(), offset));
4963 bool has_span_cost =
false;
4964 bool has_span_limit =
false;
4965 std::vector<int64_t> vehicle_offsets(vehicles());
4966 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
4967 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
4968 has_span_cost =
true;
4970 if (dimension->GetSpanUpperBoundForVehicle(vehicle) <
4971 std::numeric_limits<int64_t>::max()) {
4972 has_span_limit =
true;
4974 DCHECK_GE(dimension->CumulVar(Start(vehicle))->Min(), 0);
4976 if (dimension->AreVehicleTransitsPositive(vehicle)) {
4977 offset =
CapSub(dimension->CumulVar(Start(vehicle))->Min(), 1);
4978 if (dimension->HasBreakConstraints()) {
4979 for (
const IntervalVar* br :
4980 dimension->GetBreakIntervalsOfVehicle(vehicle)) {
4981 offset = std::min(offset,
CapSub(br->StartMin(), 1));
4985 vehicle_offsets[vehicle] = std::max<int64_t>(0, offset);
4987 bool has_soft_lower_bound =
false;
4988 bool has_soft_upper_bound =
false;
4989 for (
int i = 0;
i < dimension->cumuls().
size(); ++
i) {
4990 if (dimension->HasCumulVarSoftLowerBound(i)) {
4991 has_soft_lower_bound =
true;
4993 if (dimension->HasCumulVarSoftUpperBound(i)) {
4994 has_soft_upper_bound =
true;
4997 int num_linear_constraints = 0;
4998 if (has_span_cost) ++num_linear_constraints;
4999 if (has_span_limit) ++num_linear_constraints;
5000 if (dimension->HasSoftSpanUpperBounds()) ++num_linear_constraints;
5001 if (has_soft_lower_bound) ++num_linear_constraints;
5002 if (has_soft_upper_bound) ++num_linear_constraints;
5003 if (dimension->HasBreakConstraints()) ++num_linear_constraints;
5004 if (num_resource_groups > 0 || num_linear_constraints >= 2) {
5005 needs_optimizer =
true;
5006 dimension->SetVehicleOffsetsForLocalOptimizer(std::move(vehicle_offsets));
5007 local_optimizer_index_[dim] = local_dimension_optimizers_.size();
5008 local_dimension_optimizers_.push_back(
5009 {std::make_unique<LocalDimensionCumulOptimizer>(
5010 dimension,
parameters.continuous_scheduling_solver()),
5011 std::make_unique<LocalDimensionCumulOptimizer>(
5012 dimension,
parameters.mixed_integer_scheduling_solver())});
5014 if (needs_optimizer) {
5015 optimized_dimensions_collector_assignment->Add(dimension->cumuls());
5023 for (IntVar*
const extra_var : extra_vars_) {
5024 optimized_dimensions_collector_assignment->Add(extra_var);
5026 for (IntervalVar*
const extra_interval : extra_intervals_) {
5027 optimized_dimensions_collector_assignment->Add(extra_interval);
5030 optimized_dimensions_assignment_collector_ =
5031 solver_->MakeFirstSolutionCollector(
5032 optimized_dimensions_collector_assignment);
5035std::vector<RoutingDimension*> RoutingModel::GetDimensionsWithSoftOrSpanCosts()
5038 for (RoutingDimension* dimension : dimensions_) {
5039 bool has_soft_or_span_cost =
false;
5040 for (
int vehicle = 0; vehicle < vehicles(); ++vehicle) {
5041 if (dimension->GetSpanCostCoefficientForVehicle(vehicle) > 0) {
5042 has_soft_or_span_cost =
true;
5046 if (!has_soft_or_span_cost) {
5047 for (
int i = 0;
i < dimension->cumuls().
size(); ++
i) {
5048 if (dimension->HasCumulVarSoftUpperBound(i) ||
5049 dimension->HasCumulVarSoftLowerBound(i)) {
5050 has_soft_or_span_cost =
true;
5055 if (has_soft_or_span_cost)
dimensions.push_back(dimension);
5060std::vector<RoutingDimension*> RoutingModel::GetUnaryDimensions()
const {
5061 std::vector<RoutingDimension*> unary_dimensions;
5062 for (RoutingDimension* dim : dimensions_) {
5063 if (dim->IsUnary()) {
5064 unary_dimensions.push_back(dim);
5067 return unary_dimensions;
5070std::vector<const RoutingDimension*>
5071RoutingModel::GetDimensionsWithGlobalCumulOptimizers()
const {
5073 std::vector<const RoutingDimension*> global_optimizer_dimensions;
5074 for (
auto& [lp_optimizer, mp_optimizer] : global_dimension_optimizers_) {
5075 DCHECK_NE(lp_optimizer.get(),
nullptr);
5076 DCHECK_NE(mp_optimizer.get(),
nullptr);
5077 global_optimizer_dimensions.push_back(lp_optimizer->dimension());
5079 return global_optimizer_dimensions;
5082std::vector<const RoutingDimension*>
5083RoutingModel::GetDimensionsWithLocalCumulOptimizers()
const {
5085 std::vector<const RoutingDimension*> local_optimizer_dimensions;
5086 for (
auto& [lp_optimizer, mp_optimizer] : local_dimension_optimizers_) {
5087 DCHECK_NE(lp_optimizer.get(),
nullptr);
5088 DCHECK_NE(mp_optimizer.get(),
nullptr);
5089 local_optimizer_dimensions.push_back(lp_optimizer->dimension());
5091 return local_optimizer_dimensions;
5094bool RoutingModel::AreRoutesInterdependent(
5095 const RoutingSearchParameters&
parameters)
const {
5101 if (
parameters.local_search_metaheuristic() ==
5102 LocalSearchMetaheuristic::GENERIC_TABU_SEARCH ||
5104 LocalSearchMetaheuristic::TABU_SEARCH) {
5107 for (RoutingDimension*
const dim : dimensions_) {
5108 if (!GetDimensionResourceGroupIndices(dim).empty() ||
5109 HasGlobalCumulOptimizer(*dim)) {
5116DecisionBuilder* RoutingModel::CreateSolutionFinalizer(
5117 const RoutingSearchParameters&
parameters, SearchLimit* lns_limit) {
5118 std::vector<DecisionBuilder*> decision_builders;
5119 decision_builders.push_back(solver_->MakePhase(
5120 nexts_, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE));
5126 decision_builders.push_back(
5129 const bool can_use_dimension_cumul_optimizers =
5130 !
parameters.disable_scheduling_beware_this_may_degrade_performance();
5131 DCHECK(local_dimension_optimizers_.empty() ||
5132 can_use_dimension_cumul_optimizers);
5133 for (
auto& [lp_optimizer, mp_optimizer] : local_dimension_optimizers_) {
5134 const RoutingDimension*
const dim = lp_optimizer->dimension();
5135 if (HasGlobalCumulOptimizer(*dim)) {
5139 DCHECK_LE(GetDimensionResourceGroupIndices(dim).
size(), 1);
5141 solver_.get(), lp_optimizer.get(), mp_optimizer.get()));
5144 DCHECK(global_dimension_optimizers_.empty() ||
5145 can_use_dimension_cumul_optimizers);
5146 for (
auto& [lp_optimizer, mp_optimizer] : global_dimension_optimizers_) {
5148 solver_.get(), lp_optimizer.get(), mp_optimizer.get(), lns_limit));
5150 decision_builders.push_back(finalizer_variables_->CreateFinalizer());
5152 return solver_->Compose(decision_builders);
5155void RoutingModel::CreateFirstSolutionDecisionBuilders(
5156 const RoutingSearchParameters& search_parameters) {
5157 first_solution_decision_builders_.resize(
5158 FirstSolutionStrategy_Value_Value_ARRAYSIZE,
nullptr);
5159 first_solution_filtered_decision_builders_.resize(
5160 FirstSolutionStrategy_Value_Value_ARRAYSIZE,
nullptr);
5161 DecisionBuilder*
const finalize_solution = CreateSolutionFinalizer(
5162 search_parameters, GetOrCreateLargeNeighborhoodSearchLimit());
5164 first_solution_decision_builders_
5165 [FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE] = finalize_solution;
5167 first_solution_decision_builders_
5168 [FirstSolutionStrategy::GLOBAL_CHEAPEST_ARC] = solver_->MakePhase(
5170 [
this](int64_t i, int64_t j) {
5171 return GetArcCostForFirstSolution(i, j);
5173 Solver::CHOOSE_STATIC_GLOBAL_BEST);
5175 Solver::IndexEvaluator2 eval = [
this](int64_t
i, int64_t j) {
5176 return GetArcCostForFirstSolution(i, j);
5178 first_solution_decision_builders_[FirstSolutionStrategy::LOCAL_CHEAPEST_ARC] =
5179 solver_->MakePhase(nexts_, Solver::CHOOSE_FIRST_UNBOUND, eval);
5181 first_solution_decision_builders_[FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5182 solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, eval);
5183 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5184 first_solution_filtered_decision_builders_
5185 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5186 CreateIntVarFilteredDecisionBuilder<
5187 EvaluatorCheapestAdditionFilteredHeuristic>(
5188 [
this](int64_t
i, int64_t j) {
5189 return GetArcCostForFirstSolution(i, j);
5191 GetOrCreateLocalSearchFilterManager(
5192 search_parameters, {
false,
5194 first_solution_decision_builders_
5195 [FirstSolutionStrategy::PATH_CHEAPEST_ARC] =
5196 solver_->Try(first_solution_filtered_decision_builders_
5197 [FirstSolutionStrategy::PATH_CHEAPEST_ARC],
5198 first_solution_decision_builders_
5199 [FirstSolutionStrategy::PATH_CHEAPEST_ARC]);
5202 Solver::VariableValueComparator comp = [
this](int64_t
i, int64_t j,
5204 return ArcIsMoreConstrainedThanArc(i, j, k);
5207 first_solution_decision_builders_
5208 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5209 solver_->MakePhase(nexts_, Solver::CHOOSE_PATH, comp);
5210 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5211 first_solution_filtered_decision_builders_
5212 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] =
5213 CreateIntVarFilteredDecisionBuilder<
5214 ComparatorCheapestAdditionFilteredHeuristic>(
5216 GetOrCreateLocalSearchFilterManager(
5217 search_parameters, {
false,
5219 first_solution_decision_builders_
5220 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC] = solver_->Try(
5221 first_solution_filtered_decision_builders_
5222 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC],
5223 first_solution_decision_builders_
5224 [FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC]);
5227 if (first_solution_evaluator_ !=
nullptr) {
5228 first_solution_decision_builders_
5229 [FirstSolutionStrategy::EVALUATOR_STRATEGY] = solver_->MakePhase(
5230 nexts_, Solver::CHOOSE_PATH, first_solution_evaluator_);
5232 first_solution_decision_builders_
5233 [FirstSolutionStrategy::EVALUATOR_STRATEGY] =
nullptr;
5236 first_solution_decision_builders_[FirstSolutionStrategy::ALL_UNPERFORMED] =
5239 RegularLimit*
const ls_limit = solver_->MakeLimit(
5240 GetTimeLimit(search_parameters), std::numeric_limits<int64_t>::max(),
5241 std::numeric_limits<int64_t>::max(), std::numeric_limits<int64_t>::max(),
5243 DecisionBuilder*
const finalize = solver_->MakeSolveOnce(
5244 finalize_solution, GetOrCreateLargeNeighborhoodSearchLimit());
5245 LocalSearchPhaseParameters*
const insertion_parameters =
5246 solver_->MakeLocalSearchPhaseParameters(
5247 nullptr, CreateInsertionOperator(), finalize, ls_limit,
5248 GetOrCreateLocalSearchFilterManager(
5251 std::vector<IntVar*> decision_vars = nexts_;
5252 if (!CostsAreHomogeneousAcrossVehicles()) {
5253 decision_vars.insert(decision_vars.end(), vehicle_vars_.begin(),
5254 vehicle_vars_.end());
5256 const int64_t optimization_step = std::max(
5257 MathUtil::FastInt64Round(search_parameters.optimization_step()),
One());
5258 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5259 solver_->MakeNestedOptimize(
5261 insertion_parameters),
5262 GetOrCreateAssignment(),
false, optimization_step);
5263 first_solution_decision_builders_[FirstSolutionStrategy::BEST_INSERTION] =
5264 solver_->Compose(first_solution_decision_builders_
5265 [FirstSolutionStrategy::BEST_INSERTION],
5269 GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
5271 gci_parameters.is_sequential =
false;
5272 gci_parameters.farthest_seeds_ratio =
5273 search_parameters.cheapest_insertion_farthest_seeds_ratio();
5274 gci_parameters.neighbors_ratio =
5275 search_parameters.cheapest_insertion_first_solution_neighbors_ratio();
5276 gci_parameters.min_neighbors =
5277 search_parameters.cheapest_insertion_first_solution_min_neighbors();
5278 gci_parameters.use_neighbors_ratio_for_initialization =
5280 .cheapest_insertion_first_solution_use_neighbors_ratio_for_initialization();
5281 gci_parameters.add_unperformed_entries =
5282 search_parameters.cheapest_insertion_add_unperformed_entries();
5283 for (
bool is_sequential : {
false,
true}) {
5284 FirstSolutionStrategy::Value first_solution_strategy =
5285 is_sequential ? FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION
5286 : FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
5287 gci_parameters.is_sequential = is_sequential;
5289 first_solution_filtered_decision_builders_[first_solution_strategy] =
5290 CreateIntVarFilteredDecisionBuilder<
5291 GlobalCheapestInsertionFilteredHeuristic>(
5292 [
this](int64_t
i, int64_t j, int64_t vehicle) {
5293 return GetArcCostForVehicle(i, j, vehicle);
5295 [
this](int64_t
i) {
return UnperformedPenaltyOrValue(0, i); },
5296 GetOrCreateLocalSearchFilterManager(
5297 search_parameters, {
false,
5300 IntVarFilteredDecisionBuilder*
const strong_gci =
5301 CreateIntVarFilteredDecisionBuilder<
5302 GlobalCheapestInsertionFilteredHeuristic>(
5303 [
this](int64_t
i, int64_t j, int64_t vehicle) {
5304 return GetArcCostForVehicle(i, j, vehicle);
5306 [
this](int64_t
i) {
return UnperformedPenaltyOrValue(0, i); },
5307 GetOrCreateLocalSearchFilterManager(
5308 search_parameters, {
false,
5311 first_solution_decision_builders_[first_solution_strategy] = solver_->Try(
5312 first_solution_filtered_decision_builders_[first_solution_strategy],
5313 solver_->Try(strong_gci, first_solution_decision_builders_
5314 [FirstSolutionStrategy::BEST_INSERTION]));
5318 std::function<bool(
const std::vector<VariableValuePair>&,
5319 std::vector<VariableValuePair>*)>
5320 optimize_on_insertion;
5321 if (secondary_model_ !=
nullptr) {
5322 secondary_model_->QuietCloseModelWithParameters(secondary_parameters_);
5323 secondary_optimizer_ = std::make_unique<SecondaryOptimizer>(
5324 secondary_model_, &secondary_parameters_,
5325 search_parameters.first_solution_optimization_period());
5326 optimize_on_insertion = absl::bind_front(&SecondaryOptimizer::Solve,
5327 secondary_optimizer_.get());
5329 const RoutingSearchParameters::PairInsertionStrategy lci_pair_strategy =
5330 search_parameters.local_cheapest_insertion_pickup_delivery_strategy();
5331 first_solution_filtered_decision_builders_
5332 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] =
5333 CreateIntVarFilteredDecisionBuilder<
5334 LocalCheapestInsertionFilteredHeuristic>(
5335 [
this](int64_t
i, int64_t j, int64_t vehicle) {
5336 return GetArcCostForVehicle(i, j, vehicle);
5339 GetOrCreateLocalSearchFilterManager(
5340 search_parameters, {
false,
5342 bin_capacities_.get(), optimize_on_insertion);
5343 IntVarFilteredDecisionBuilder*
const strong_lci =
5344 CreateIntVarFilteredDecisionBuilder<
5345 LocalCheapestInsertionFilteredHeuristic>(
5346 [
this](int64_t
i, int64_t j, int64_t vehicle) {
5347 return GetArcCostForVehicle(i, j, vehicle);
5350 GetOrCreateLocalSearchFilterManager(search_parameters,
5353 bin_capacities_.get(), optimize_on_insertion);
5354 first_solution_decision_builders_
5355 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = solver_->Try(
5356 first_solution_filtered_decision_builders_
5357 [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION],
5358 solver_->Try(strong_lci,
5359 first_solution_decision_builders_
5360 [FirstSolutionStrategy::BEST_INSERTION]));
5363 const RoutingSearchParameters::PairInsertionStrategy lcci_pair_strategy =
5365 .local_cheapest_cost_insertion_pickup_delivery_strategy();
5366 first_solution_filtered_decision_builders_
5367 [FirstSolutionStrategy::LOCAL_CHEAPEST_COST_INSERTION] =
5368 CreateIntVarFilteredDecisionBuilder<
5369 LocalCheapestInsertionFilteredHeuristic>(
5370 nullptr, lcci_pair_strategy,
5371 GetOrCreateLocalSearchFilterManager(
5372 search_parameters, {
true,
5374 bin_capacities_.get(), optimize_on_insertion);
5375 IntVarFilteredDecisionBuilder*
const strong_lcci =
5376 CreateIntVarFilteredDecisionBuilder<
5377 LocalCheapestInsertionFilteredHeuristic>(
5378 nullptr, lcci_pair_strategy,
5379 GetOrCreateLocalSearchFilterManager(search_parameters,
5382 bin_capacities_.get(), optimize_on_insertion);
5383 first_solution_decision_builders_
5384 [FirstSolutionStrategy::LOCAL_CHEAPEST_COST_INSERTION] = solver_->Try(
5385 first_solution_filtered_decision_builders_
5386 [FirstSolutionStrategy::LOCAL_CHEAPEST_COST_INSERTION],
5387 solver_->Try(strong_lcci,
5388 first_solution_decision_builders_
5389 [FirstSolutionStrategy::BEST_INSERTION]));
5392 SavingsFilteredHeuristic::SavingsParameters savings_parameters;
5393 savings_parameters.neighbors_ratio =
5394 search_parameters.savings_neighbors_ratio();
5395 savings_parameters.max_memory_usage_bytes =
5396 search_parameters.savings_max_memory_usage_bytes();
5397 savings_parameters.add_reverse_arcs =
5398 search_parameters.savings_add_reverse_arcs();
5399 savings_parameters.arc_coefficient =
5400 search_parameters.savings_arc_coefficient();
5401 LocalSearchFilterManager* filter_manager =
nullptr;
5402 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5403 filter_manager = GetOrCreateLocalSearchFilterManager(
5408 if (search_parameters.savings_parallel_routes()) {
5409 IntVarFilteredDecisionBuilder* savings_db =
5410 CreateIntVarFilteredDecisionBuilder<ParallelSavingsFilteredHeuristic>(
5411 savings_parameters, filter_manager);
5412 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5413 first_solution_filtered_decision_builders_
5414 [FirstSolutionStrategy::SAVINGS] = savings_db;
5417 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5418 solver_->Try(savings_db, CreateIntVarFilteredDecisionBuilder<
5419 ParallelSavingsFilteredHeuristic>(
5421 GetOrCreateLocalSearchFilterManager(
5426 IntVarFilteredDecisionBuilder* savings_db =
5427 CreateIntVarFilteredDecisionBuilder<SequentialSavingsFilteredHeuristic>(
5428 savings_parameters, filter_manager);
5429 if (!search_parameters.use_unfiltered_first_solution_strategy()) {
5430 first_solution_filtered_decision_builders_
5431 [FirstSolutionStrategy::SAVINGS] = savings_db;
5434 first_solution_decision_builders_[FirstSolutionStrategy::SAVINGS] =
5435 solver_->Try(savings_db, CreateIntVarFilteredDecisionBuilder<
5436 SequentialSavingsFilteredHeuristic>(
5438 GetOrCreateLocalSearchFilterManager(
5444 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5447 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP] =
5450 first_solution_decision_builders_[FirstSolutionStrategy::SWEEP]);
5452 first_solution_decision_builders_[FirstSolutionStrategy::CHRISTOFIDES] =
5453 CreateIntVarFilteredDecisionBuilder<ChristofidesFilteredHeuristic>(
5454 GetOrCreateLocalSearchFilterManager(
5455 search_parameters, {
false,
5457 search_parameters.christofides_use_minimum_matching());
5459 const bool has_precedences = std::any_of(
5460 dimensions_.begin(), dimensions_.end(),
5461 [](RoutingDimension* dim) { return !dim->GetNodePrecedences().empty(); });
5462 bool has_single_vehicle_node =
false;
5463 for (
int node = 0; node < Size(); node++) {
5464 if (!IsStart(node) && !IsEnd(node) && allowed_vehicles_[node].
size() == 1) {
5465 has_single_vehicle_node =
true;
5469 automatic_first_solution_strategy_ =
5471 has_precedences, has_single_vehicle_node);
5472 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC] =
5473 first_solution_decision_builders_[automatic_first_solution_strategy_];
5474 first_solution_decision_builders_[FirstSolutionStrategy::UNSET] =
5475 first_solution_decision_builders_[FirstSolutionStrategy::AUTOMATIC];
5478 for (FirstSolutionStrategy_Value strategy =
5479 FirstSolutionStrategy_Value_Value_MIN;
5480 strategy <= FirstSolutionStrategy_Value_Value_MAX;
5481 strategy = FirstSolutionStrategy_Value(strategy + 1)) {
5482 if (first_solution_decision_builders_[strategy] ==
nullptr ||
5483 strategy == FirstSolutionStrategy::AUTOMATIC) {
5486 const std::string strategy_name =
5487 FirstSolutionStrategy_Value_Name(strategy);
5488 const std::string& log_tag = search_parameters.log_tag();
5489 if (!log_tag.empty() && log_tag != strategy_name) {
5490 first_solution_decision_builders_[strategy]->set_name(absl::StrFormat(
5491 "%s / %s", strategy_name, search_parameters.log_tag()));
5493 first_solution_decision_builders_[strategy]->set_name(strategy_name);
5498DecisionBuilder* RoutingModel::GetFirstSolutionDecisionBuilder(
5499 const RoutingSearchParameters& search_parameters)
const {
5500 const FirstSolutionStrategy::Value first_solution_strategy =
5501 search_parameters.first_solution_strategy();
5502 if (first_solution_strategy < first_solution_decision_builders_.size()) {
5503 return first_solution_decision_builders_[first_solution_strategy];
5509IntVarFilteredDecisionBuilder*
5510RoutingModel::GetFilteredFirstSolutionDecisionBuilderOrNull(
5511 const RoutingSearchParameters& search_parameters)
const {
5512 const FirstSolutionStrategy::Value first_solution_strategy =
5513 search_parameters.first_solution_strategy();
5514 return first_solution_filtered_decision_builders_[first_solution_strategy];
5517template <
typename Heuristic,
typename... Args>
5518IntVarFilteredDecisionBuilder*
5519RoutingModel::CreateIntVarFilteredDecisionBuilder(
const Args&... args) {
5520 return solver_->RevAlloc(
5521 new IntVarFilteredDecisionBuilder(std::make_unique<Heuristic>(
5522 this, [
this]() {
return CheckLimit(time_buffer_); }, args...)));
5525LocalSearchPhaseParameters* RoutingModel::CreateLocalSearchParameters(
5526 const RoutingSearchParameters& search_parameters,
bool secondary_ls) {
5527 SearchLimit* lns_limit = GetOrCreateLargeNeighborhoodSearchLimit();
5528 absl::flat_hash_set<RoutingLocalSearchOperator> operators_to_consider;
5529 LocalSearchOperator* ls_operator =
nullptr;
5531 if (secondary_ls_operator_ ==
nullptr) {
5532 operators_to_consider = {TWO_OPT,
5536 MAKE_CHAIN_INACTIVE,
5537 SHORTEST_PATH_SWAP_ACTIVE};
5538 secondary_ls_operator_ =
5539 GetNeighborhoodOperators(search_parameters, operators_to_consider);
5541 ls_operator = secondary_ls_operator_;
5543 if (primary_ls_operator_ ==
nullptr) {
5545 for (
int op = 0; op < LOCAL_SEARCH_OPERATOR_COUNTER; ++op) {
5546 operators_to_consider.insert(RoutingLocalSearchOperator(op));
5548 primary_ls_operator_ =
5549 GetNeighborhoodOperators(search_parameters, operators_to_consider);
5551 ls_operator = primary_ls_operator_;
5553 return solver_->MakeLocalSearchPhaseParameters(
5554 CostVar(), ls_operator,
5555 solver_->MakeSolveOnce(
5556 CreateSolutionFinalizer(search_parameters, lns_limit), lns_limit),
5557 GetOrCreateLocalSearchLimit(),
5558 GetOrCreateLocalSearchFilterManager(
5563DecisionBuilder* RoutingModel::CreatePrimaryLocalSearchDecisionBuilder(
5564 const RoutingSearchParameters& search_parameters) {
5565 const int size = Size();
5566 DecisionBuilder* first_solution =
5567 GetFirstSolutionDecisionBuilder(search_parameters);
5568 LocalSearchPhaseParameters*
const parameters =
5569 CreateLocalSearchParameters(search_parameters,
false);
5570 SearchLimit* first_solution_lns_limit =
5571 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5572 DecisionBuilder*
const first_solution_sub_decision_builder =
5573 solver_->MakeSolveOnce(
5574 CreateSolutionFinalizer(search_parameters, first_solution_lns_limit),
5575 first_solution_lns_limit);
5576 if (CostsAreHomogeneousAcrossVehicles()) {
5577 return solver_->MakeLocalSearchPhase(nexts_, first_solution,
5578 first_solution_sub_decision_builder,
5581 const int all_size =
size +
size + vehicles_;
5582 std::vector<IntVar*> all_vars(all_size);
5583 for (
int i = 0;
i <
size; ++
i) {
5584 all_vars[
i] = nexts_[
i];
5586 for (
int i =
size;
i < all_size; ++
i) {
5587 all_vars[
i] = vehicle_vars_[
i -
size];
5589 return solver_->MakeLocalSearchPhase(all_vars, first_solution,
5590 first_solution_sub_decision_builder,
5594void RoutingModel::SetupDecisionBuilders(
5595 const RoutingSearchParameters& search_parameters) {
5596 if (search_parameters.use_depth_first_search()) {
5597 SearchLimit* first_lns_limit =
5598 GetOrCreateFirstSolutionLargeNeighborhoodSearchLimit();
5599 solve_db_ = solver_->Compose(
5600 GetFirstSolutionDecisionBuilder(search_parameters),
5601 solver_->MakeSolveOnce(
5602 CreateSolutionFinalizer(search_parameters, first_lns_limit),
5605 solve_db_ = CreatePrimaryLocalSearchDecisionBuilder(search_parameters);
5607 CHECK(preassignment_ !=
nullptr);
5608 DecisionBuilder* restore_preassignment =
5609 solver_->MakeRestoreAssignment(preassignment_);
5610 solve_db_ = solver_->Compose(restore_preassignment, solve_db_);
5613 solver_->Compose(restore_preassignment,
5614 solver_->MakeLocalSearchPhase(
5615 GetOrCreateAssignment(),
5616 CreateLocalSearchParameters(
5617 search_parameters,
false)));
5619 secondary_ls_db_ = solver_->MakeLocalSearchPhase(
5620 GetOrCreateAssignment(),
5621 CreateLocalSearchParameters(search_parameters,
true));
5622 secondary_ls_db_ = solver_->Compose(restore_preassignment, secondary_ls_db_);
5624 restore_assignment_ = solver_->Compose(
5625 solver_->MakeRestoreAssignment(GetOrCreateAssignment()),
5626 CreateSolutionFinalizer(search_parameters,
5627 GetOrCreateLargeNeighborhoodSearchLimit()));
5628 restore_tmp_assignment_ = solver_->Compose(
5629 restore_preassignment,
5630 solver_->MakeRestoreAssignment(GetOrCreateTmpAssignment()),
5631 CreateSolutionFinalizer(search_parameters,
5632 GetOrCreateLargeNeighborhoodSearchLimit()));
5635void RoutingModel::SetupMetaheuristics(
5636 const RoutingSearchParameters& search_parameters) {
5637 SearchMonitor* optimize =
nullptr;
5638 const LocalSearchMetaheuristic::Value metaheuristic =
5639 search_parameters.local_search_metaheuristic();
5642 bool limit_too_long =
5643 !search_parameters.has_time_limit() &&
5644 search_parameters.solution_limit() == std::numeric_limits<int64_t>::max();
5645 const int64_t optimization_step = std::max(
5646 MathUtil::FastInt64Round(search_parameters.optimization_step()),
One());
5647 switch (metaheuristic) {
5648 case LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH:
5649 if (CostsAreHomogeneousAcrossVehicles()) {
5650 optimize = solver_->MakeGuidedLocalSearch(
5652 [
this](int64_t i, int64_t j) {
return GetHomogeneousCost(i, j); },
5653 optimization_step, nexts_,
5654 search_parameters.guided_local_search_lambda_coefficient(),
5656 .guided_local_search_reset_penalties_on_new_best_solution());
5658 optimize = solver_->MakeGuidedLocalSearch(
5660 [
this](int64_t i, int64_t j, int64_t k) {
5661 return GetArcCostForVehicle(i, j, k);
5663 optimization_step, nexts_, vehicle_vars_,
5664 search_parameters.guided_local_search_lambda_coefficient(),
5666 .guided_local_search_reset_penalties_on_new_best_solution());
5669 case LocalSearchMetaheuristic::SIMULATED_ANNEALING:
5671 solver_->MakeSimulatedAnnealing(
false, cost_, optimization_step, 100);
5673 case LocalSearchMetaheuristic::TABU_SEARCH:
5674 optimize = solver_->MakeTabuSearch(
false, cost_, optimization_step,
5675 nexts_, 10, 10, .8);
5677 case LocalSearchMetaheuristic::GENERIC_TABU_SEARCH: {
5678 std::vector<operations_research::IntVar*> tabu_vars;
5679 if (tabu_var_callback_) {
5680 tabu_vars = tabu_var_callback_(
this);
5682 tabu_vars.push_back(cost_);
5684 optimize = solver_->MakeGenericTabuSearch(
false, cost_, optimization_step,
5689 limit_too_long =
false;
5690 optimize = solver_->MakeMinimize(cost_, optimization_step);
5692 if (limit_too_long) {
5693 LOG(WARNING) << LocalSearchMetaheuristic::Value_Name(metaheuristic)
5694 <<
" specified without sane timeout: solve may run forever.";
5696 metaheuristic_ = optimize;
5697 monitors_.push_back(optimize);
5698 secondary_ls_monitors_.push_back(optimize);
5701void RoutingModel::SetTabuVarsCallback(GetTabuVarsCallback tabu_var_callback) {
5702 tabu_var_callback_ = std::move(tabu_var_callback);
5705void RoutingModel::SetupAssignmentCollector(
5706 const RoutingSearchParameters& search_parameters) {
5707 Assignment* full_assignment = solver_->MakeAssignment();
5708 for (
const RoutingDimension*
const dimension : dimensions_) {
5709 full_assignment->Add(dimension->cumuls());
5711 for (IntVar*
const extra_var : extra_vars_) {
5712 full_assignment->Add(extra_var);
5714 for (IntervalVar*
const extra_interval : extra_intervals_) {
5715 full_assignment->Add(extra_interval);
5717 full_assignment->Add(nexts_);
5718 full_assignment->Add(active_);
5719 full_assignment->Add(vehicle_vars_);
5720 full_assignment->AddObjective(cost_);
5722 collect_assignments_ = solver_->MakeNBestValueSolutionCollector(
5723 full_assignment, search_parameters.number_of_solutions_to_collect(),
5725 collect_secondary_ls_assignments_ = solver_->MakeNBestValueSolutionCollector(
5726 full_assignment, search_parameters.number_of_solutions_to_collect(),
5728 collect_one_assignment_ =
5729 solver_->MakeFirstSolutionCollector(full_assignment);
5730 monitors_.push_back(collect_assignments_);
5731 secondary_ls_monitors_.push_back(collect_secondary_ls_assignments_);
5734void RoutingModel::SetupTrace(
5735 const RoutingSearchParameters& search_parameters) {
5736 if (search_parameters.log_search()) {
5737 Solver::SearchLogParameters search_log_parameters;
5738 search_log_parameters.branch_period = 10000;
5739 search_log_parameters.objective =
nullptr;
5740 search_log_parameters.variables = {cost_};
5741 search_log_parameters.scaling_factors = {
5742 search_parameters.log_cost_scaling_factor()};
5743 search_log_parameters.offsets = {search_parameters.log_cost_offset()};
5744 if (!search_parameters.log_tag().empty()) {
5745 const std::string& tag = search_parameters.log_tag();
5746 search_log_parameters.display_callback = [tag]() {
return tag; };
5748 search_log_parameters.display_callback =
nullptr;
5750 search_log_parameters.display_on_new_solutions_only =
false;
5751 search_log_ = solver_->MakeSearchLog(search_log_parameters);
5752 monitors_.push_back(search_log_);
5753 secondary_ls_monitors_.push_back(
5754 solver_->MakeSearchLog(search_log_parameters));
5758void RoutingModel::SetupImprovementLimit(
5759 const RoutingSearchParameters& search_parameters) {
5760 if (!search_parameters.has_improvement_limit_parameters())
return;
5762 SearchMonitor*
const improvement_limit = solver_->MakeImprovementLimit(
5763 cost_,
false, search_parameters.log_cost_scaling_factor(),
5764 search_parameters.log_cost_offset(),
5765 search_parameters.improvement_limit_parameters()
5766 .improvement_rate_coefficient(),
5767 search_parameters.improvement_limit_parameters()
5768 .improvement_rate_solutions_distance());
5769 monitors_.push_back(improvement_limit);
5770 secondary_ls_monitors_.push_back(improvement_limit);
5775template <
typename EndInitialPropagationCallback,
typename LocalOptimumCallback>
5776class LocalOptimumWatcher :
public SearchMonitor {
5778 LocalOptimumWatcher(
5780 EndInitialPropagationCallback end_initial_propagation_callback,
5781 LocalOptimumCallback local_optimum_callback)
5782 : SearchMonitor(solver),
5783 end_initial_propagation_callback_(
5784 std::move(end_initial_propagation_callback)),
5785 local_optimum_callback_(
std::move(local_optimum_callback)) {}
5786 void Install()
override {
5787 ListenToEvent(Solver::MonitorEvent::kEndInitialPropagation);
5788 ListenToEvent(Solver::MonitorEvent::kLocalOptimum);
5790 void EndInitialPropagation()
override { end_initial_propagation_callback_(); }
5791 bool LocalOptimum()
override {
5792 local_optimum_callback_();
5793 return SearchMonitor::LocalOptimum();
5797 EndInitialPropagationCallback end_initial_propagation_callback_;
5798 LocalOptimumCallback local_optimum_callback_;
5801template <
typename EndInitialPropagationCallback,
typename LocalOptimumCallback>
5802SearchMonitor* MakeLocalOptimumWatcher(
5804 EndInitialPropagationCallback end_initial_propagation_callback,
5805 LocalOptimumCallback local_optimum_callback) {
5806 return solver->RevAlloc(
new LocalOptimumWatcher<EndInitialPropagationCallback,
5807 LocalOptimumCallback>(
5808 solver, std::move(end_initial_propagation_callback),
5809 std::move(local_optimum_callback)));
5814void RoutingModel::SetupSearchMonitors(
5815 const RoutingSearchParameters& search_parameters) {
5816 monitors_.push_back(GetOrCreateLimit());
5817 monitors_.push_back(MakeLocalOptimumWatcher(
5820 objective_lower_bound_ =
5821 std::max(objective_lower_bound_, CostVar()->Min());
5823 [
this]() { local_optimum_reached_ =
true; }));
5824 monitors_.push_back(
5825 solver_->MakeCustomLimit([
this]() ->
bool { return interrupt_cp_; }));
5827 secondary_ls_monitors_ = monitors_;
5829 SetupImprovementLimit(search_parameters);
5830 SetupMetaheuristics(search_parameters);
5831 SetupAssignmentCollector(search_parameters);
5832 SetupTrace(search_parameters);
5835bool RoutingModel::UsesLightPropagation(
5836 const RoutingSearchParameters& search_parameters)
const {
5837 return !search_parameters.use_full_propagation() &&
5838 !search_parameters.use_depth_first_search() &&
5839 search_parameters.first_solution_strategy() !=
5840 FirstSolutionStrategy::FIRST_UNBOUND_MIN_VALUE;
5843void RoutingModel::AddWeightedVariableTargetToFinalizer(IntVar*
var,
5846 finalizer_variables_->AddWeightedVariableTarget(
var, target, cost);
5849void RoutingModel::AddWeightedVariableMinimizedByFinalizer(IntVar*
var,
5851 finalizer_variables_->AddWeightedVariableToMinimize(
var, cost);
5854void RoutingModel::AddWeightedVariableMaximizedByFinalizer(IntVar*
var,
5856 finalizer_variables_->AddWeightedVariableToMaximize(
var, cost);
5859void RoutingModel::AddVariableTargetToFinalizer(IntVar*
var, int64_t target) {
5860 finalizer_variables_->AddVariableTarget(
var, target);
5863void RoutingModel::AddVariableMaximizedByFinalizer(IntVar*
var) {
5864 finalizer_variables_->AddVariableToMaximize(
var);
5867void RoutingModel::AddVariableMinimizedByFinalizer(IntVar*
var) {
5868 finalizer_variables_->AddVariableToMinimize(
var);
5871void RoutingModel::SetupSearch(
5872 const RoutingSearchParameters& search_parameters) {
5873 SetupDecisionBuilders(search_parameters);
5874 SetupSearchMonitors(search_parameters);
5877void RoutingModel::AddToAssignment(IntVar*
const var) {
5878 extra_vars_.push_back(
var);
5881void RoutingModel::AddIntervalToAssignment(IntervalVar*
const interval) {
5882 extra_intervals_.push_back(
interval);
5885const char RoutingModelVisitor::kLightElement[] =
"LightElement";
5886const char RoutingModelVisitor::kLightElement2[] =
"LightElement2";
5887const char RoutingModelVisitor::kRemoveValues[] =
"RemoveValues";
5889RoutingDimension::RoutingDimension(RoutingModel*
model,
5890 std::vector<int64_t> vehicle_capacities,
5891 const std::string&
name,
5892 const RoutingDimension* base_dimension)
5893 : vehicle_capacities_(
std::move(vehicle_capacities)),
5894 base_dimension_(base_dimension),
5895 global_span_cost_coefficient_(0),
5898 global_optimizer_offset_(0) {
5899 CHECK(
model !=
nullptr);
5900 vehicle_span_upper_bounds_.assign(
model->vehicles(),
5901 std::numeric_limits<int64_t>::max());
5902 vehicle_span_cost_coefficients_.assign(
model->vehicles(), 0);
5903 vehicle_slack_cost_coefficients_.assign(
model->vehicles(), 0);
5906RoutingDimension::RoutingDimension(RoutingModel*
model,
5907 std::vector<int64_t> vehicle_capacities,
5908 const std::string&
name, SelfBased)
5909 : RoutingDimension(
model,
std::move(vehicle_capacities),
name, this) {}
5911RoutingDimension::~RoutingDimension() {
5912 cumul_var_piecewise_linear_cost_.clear();
5915void RoutingDimension::Initialize(
5916 const std::vector<int>& transit_evaluators,
5917 const std::vector<int>& state_dependent_transit_evaluators,
5918 int64_t slack_max) {
5920 InitializeTransits(transit_evaluators, state_dependent_transit_evaluators,
5924void RoutingDimension::InitializeCumuls() {
5925 Solver*
const solver = model_->solver();
5926 const int size = model_->Size() + model_->vehicles();
5927 const auto capacity_range = std::minmax_element(vehicle_capacities_.begin(),
5928 vehicle_capacities_.end());
5929 const int64_t min_capacity = *capacity_range.first;
5930 CHECK_GE(min_capacity, 0);
5931 const int64_t max_capacity = *capacity_range.second;
5932 solver->MakeIntVarArray(
size, 0, max_capacity, name_, &
cumuls_);
5934 for (
int v = 0; v < model_->vehicles(); v++) {
5935 const int64_t vehicle_capacity = vehicle_capacities_[v];
5936 cumuls_[model_->Start(v)]->SetMax(vehicle_capacity);
5937 cumuls_[model_->End(v)]->SetMax(vehicle_capacity);
5940 forbidden_intervals_.resize(
size);
5941 capacity_vars_.clear();
5942 if (min_capacity != max_capacity) {
5943 solver->MakeIntVarArray(
size, 0, std::numeric_limits<int64_t>::max(),
5945 for (
int i = 0;
i <
size; ++
i) {
5946 IntVar*
const capacity_var = capacity_vars_[
i];
5947 if (i < model_->Size()) {
5948 IntVar*
const capacity_active = solver->MakeBoolVar();
5949 solver->AddConstraint(
5950 solver->MakeLessOrEqual(model_->ActiveVar(i), capacity_active));
5951 solver->AddConstraint(solver->MakeIsLessOrEqualCt(
5952 cumuls_[i], capacity_var, capacity_active));
5954 solver->AddConstraint(
5955 solver->MakeLessOrEqual(
cumuls_[i], capacity_var));
5962void ComputeTransitClasses(absl::Span<const int> evaluator_indices,
5963 std::vector<int>* class_evaluators,
5964 std::vector<int64_t>* vehicle_to_class) {
5965 CHECK(class_evaluators !=
nullptr);
5966 CHECK(vehicle_to_class !=
nullptr);
5967 class_evaluators->clear();
5968 vehicle_to_class->resize(evaluator_indices.size(), -1);
5969 absl::flat_hash_map<int, int64_t> evaluator_to_class;
5970 for (
int i = 0;
i < evaluator_indices.size(); ++
i) {
5971 const int evaluator_index = evaluator_indices[
i];
5972 int evaluator_class = -1;
5973 if (!
gtl::FindCopy(evaluator_to_class, evaluator_index, &evaluator_class)) {
5974 evaluator_class = class_evaluators->size();
5975 evaluator_to_class[evaluator_index] = evaluator_class;
5976 class_evaluators->push_back(evaluator_index);
5978 (*vehicle_to_class)[
i] = evaluator_class;
5983void RoutingDimension::InitializeTransitVariables(int64_t slack_max) {
5984 CHECK(!class_evaluators_.empty());
5985 CHECK(base_dimension_ ==
nullptr ||
5986 !state_dependent_class_evaluators_.empty());
5988 Solver*
const solver = model_->solver();
5989 const int size = model_->Size();
5990 const Solver::IndexEvaluator1 dependent_vehicle_class_function =
5992 return (0 <=
index &&
index < state_dependent_vehicle_to_class_.size())
5993 ? state_dependent_vehicle_to_class_[
index]
5994 : state_dependent_class_evaluators_.size();
5996 const std::string slack_name = name_ +
" slack";
5997 const std::string transit_name = name_ +
" fixed transit";
5999 bool are_all_evaluators_positive =
true;
6000 for (
int class_evaluator : class_evaluators_) {
6001 if (
model()->transit_evaluator_sign_[class_evaluator] !=
6002 RoutingModel::kTransitEvaluatorSignPositiveOrZero) {
6003 are_all_evaluators_positive =
false;
6007 for (int64_t i = 0;
i <
size; ++
i) {
6008 fixed_transits_[
i] = solver->MakeIntVar(
6009 are_all_evaluators_positive ? int64_t{0}
6010 : std::numeric_limits<int64_t>::min(),
6011 std::numeric_limits<int64_t>::max(), absl::StrCat(transit_name, i));
6013 if (base_dimension_ !=
nullptr) {
6014 if (state_dependent_class_evaluators_.size() == 1) {
6015 std::vector<IntVar*> transition_variables(
cumuls_.size(),
nullptr);
6016 for (int64_t j = 0; j <
cumuls_.size(); ++j) {
6017 transition_variables[j] =
6018 MakeRangeMakeElementExpr(
6020 ->StateDependentTransitCallback(
6021 state_dependent_class_evaluators_[0])(i, j)
6023 base_dimension_->CumulVar(i), solver)
6026 dependent_transits_[
i] =
6027 solver->MakeElement(transition_variables, model_->NextVar(i))
6030 IntVar*
const vehicle_class_var =
6032 ->MakeElement(dependent_vehicle_class_function,
6033 model_->VehicleVar(i))
6035 std::vector<IntVar*> transit_for_vehicle;
6036 transit_for_vehicle.reserve(state_dependent_class_evaluators_.size() +
6038 for (
int evaluator : state_dependent_class_evaluators_) {
6039 std::vector<IntVar*> transition_variables(
cumuls_.size(),
nullptr);
6040 for (int64_t j = 0; j <
cumuls_.size(); ++j) {
6041 transition_variables[j] =
6042 MakeRangeMakeElementExpr(
6043 model_->StateDependentTransitCallback(evaluator)(i, j)
6045 base_dimension_->CumulVar(i), solver)
6048 transit_for_vehicle.push_back(
6049 solver->MakeElement(transition_variables, model_->NextVar(i))
6052 transit_for_vehicle.push_back(solver->MakeIntConst(0));
6053 dependent_transits_[
i] =
6054 solver->MakeElement(transit_for_vehicle, vehicle_class_var)->Var();
6057 dependent_transits_[
i] = solver->MakeIntConst(0);
6061 IntExpr* transit_expr = fixed_transits_[
i];
6062 if (dependent_transits_[i]->Min() != 0 ||
6063 dependent_transits_[i]->Max() != 0) {
6064 transit_expr = solver->MakeSum(transit_expr, dependent_transits_[i]);
6067 if (slack_max == 0) {
6068 slacks_[
i] = solver->MakeIntConst(0);
6071 solver->MakeIntVar(0, slack_max, absl::StrCat(slack_name, i));
6072 transit_expr = solver->MakeSum(slacks_[i], transit_expr);
6074 transits_[
i] = transit_expr->Var();
6078void RoutingDimension::InitializeTransits(
6079 const std::vector<int>& transit_evaluators,
6080 const std::vector<int>& state_dependent_transit_evaluators,
6081 int64_t slack_max) {
6082 CHECK_EQ(model_->vehicles(), transit_evaluators.size());
6083 CHECK(base_dimension_ ==
nullptr ||
6084 model_->vehicles() == state_dependent_transit_evaluators.size());
6085 const int size = model_->Size();
6086 transits_.resize(
size,
nullptr);
6087 fixed_transits_.resize(
size,
nullptr);
6088 slacks_.resize(
size,
nullptr);
6089 dependent_transits_.resize(
size,
nullptr);
6090 ComputeTransitClasses(transit_evaluators, &class_evaluators_,
6091 &vehicle_to_class_);
6092 if (base_dimension_ !=
nullptr) {
6093 ComputeTransitClasses(state_dependent_transit_evaluators,
6094 &state_dependent_class_evaluators_,
6095 &state_dependent_vehicle_to_class_);
6098 InitializeTransitVariables(slack_max);
6103 const RoutingModel::TransitCallback2& evaluator,
6104 std::vector<int64_t>* values) {
6105 const int num_nodes = path.size();
6106 values->resize(num_nodes - 1);
6107 for (
int i = 0;
i < num_nodes - 1; ++
i) {
6108 (*values)[
i] = evaluator(path[i], path[i + 1]);
6112TypeRegulationsChecker::TypeRegulationsChecker(
const RoutingModel&
model)
6113 : model_(
model), occurrences_of_type_(
model.GetNumberOfVisitTypes()) {}
6115bool TypeRegulationsChecker::CheckVehicle(
6116 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor) {
6117 if (!HasRegulationsToCheck()) {
6121 InitializeCheck(vehicle, next_accessor);
6123 for (
int pos = 0; pos < current_route_visits_.size(); pos++) {
6124 const int64_t current_visit = current_route_visits_[pos];
6125 const int type = model_.GetVisitType(current_visit);
6129 const VisitTypePolicy policy = model_.GetVisitTypePolicy(current_visit);
6131 DCHECK_LT(type, occurrences_of_type_.size());
6132 int& num_type_added = occurrences_of_type_[type].num_type_added_to_vehicle;
6133 int& num_type_removed =
6134 occurrences_of_type_[type].num_type_removed_from_vehicle;
6135 DCHECK_LE(num_type_removed, num_type_added);
6136 if (policy == RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE &&
6137 num_type_removed == num_type_added) {
6143 if (!CheckTypeRegulations(type, policy, pos)) {
6147 if (policy == VisitTypePolicy::TYPE_ADDED_TO_VEHICLE ||
6148 policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED) {
6151 if (policy == VisitTypePolicy::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED ||
6152 policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6156 return FinalizeCheck();
6159void TypeRegulationsChecker::InitializeCheck(
6160 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor) {
6163 std::fill(occurrences_of_type_.begin(), occurrences_of_type_.end(),
6164 TypeRegulationsChecker::TypePolicyOccurrence());
6169 current_route_visits_.clear();
6170 for (int64_t current = model_.Start(vehicle); !model_.IsEnd(current);
6171 current = next_accessor(current)) {
6172 const int type = model_.GetVisitType(current);
6173 if (type >= 0 && model_.GetVisitTypePolicy(current) ==
6174 VisitTypePolicy::TYPE_ON_VEHICLE_UP_TO_VISIT) {
6175 occurrences_of_type_[type].position_of_last_type_on_vehicle_up_to_visit =
6176 current_route_visits_.size();
6178 current_route_visits_.push_back(current);
6181 OnInitializeCheck();
6184bool TypeRegulationsChecker::TypeOccursOnRoute(
int type)
const {
6185 const TypePolicyOccurrence& occurrences = occurrences_of_type_[type];
6186 return occurrences.num_type_added_to_vehicle > 0 ||
6187 occurrences.position_of_last_type_on_vehicle_up_to_visit >= 0;
6190bool TypeRegulationsChecker::TypeCurrentlyOnRoute(
int type,
int pos)
const {
6191 const TypePolicyOccurrence& occurrences = occurrences_of_type_[type];
6192 return occurrences.num_type_removed_from_vehicle <
6193 occurrences.num_type_added_to_vehicle ||
6194 occurrences.position_of_last_type_on_vehicle_up_to_visit >= pos;
6197TypeIncompatibilityChecker::TypeIncompatibilityChecker(
6198 const RoutingModel&
model,
bool check_hard_incompatibilities)
6199 : TypeRegulationsChecker(
model),
6200 check_hard_incompatibilities_(check_hard_incompatibilities) {}
6202bool TypeIncompatibilityChecker::HasRegulationsToCheck()
const {
6203 return model_.HasTemporalTypeIncompatibilities() ||
6204 (check_hard_incompatibilities_ &&
6205 model_.HasHardTypeIncompatibilities());
6213bool TypeIncompatibilityChecker::CheckTypeRegulations(
int type,
6214 VisitTypePolicy policy,
6216 if (policy == VisitTypePolicy::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
6221 for (
int incompatible_type :
6222 model_.GetTemporalTypeIncompatibilitiesOfType(type)) {
6223 if (TypeCurrentlyOnRoute(incompatible_type, pos)) {
6227 if (check_hard_incompatibilities_) {
6228 for (
int incompatible_type :
6229 model_.GetHardTypeIncompatibilitiesOfType(type)) {
6230 if (TypeOccursOnRoute(incompatible_type)) {
6238bool TypeRequirementChecker::HasRegulationsToCheck()
const {
6239 return model_.HasTemporalTypeRequirements() ||
6240 model_.HasSameVehicleTypeRequirements();
6243bool TypeRequirementChecker::CheckRequiredTypesCurrentlyOnRoute(
6244 const std::vector<absl::flat_hash_set<int>>& required_type_alternatives,
6246 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6247 required_type_alternatives) {
6248 bool has_one_of_alternatives =
false;
6249 for (
int type_alternative : requirement_alternatives) {
6250 if (TypeCurrentlyOnRoute(type_alternative, pos)) {
6251 has_one_of_alternatives =
true;
6255 if (!has_one_of_alternatives) {
6262bool TypeRequirementChecker::CheckTypeRegulations(
int type,
6263 VisitTypePolicy policy,
6265 if (policy == RoutingModel::TYPE_ADDED_TO_VEHICLE ||
6266 policy == RoutingModel::TYPE_SIMULTANEOUSLY_ADDED_AND_REMOVED) {
6267 if (!CheckRequiredTypesCurrentlyOnRoute(
6268 model_.GetRequiredTypeAlternativesWhenAddingType(type), pos)) {
6272 if (policy != RoutingModel::TYPE_ADDED_TO_VEHICLE) {
6273 if (!CheckRequiredTypesCurrentlyOnRoute(
6274 model_.GetRequiredTypeAlternativesWhenRemovingType(type), pos)) {
6278 if (policy != RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE &&
6279 !model_.GetSameVehicleRequiredTypeAlternativesOfType(type).empty()) {
6280 types_with_same_vehicle_requirements_on_route_.insert(type);
6285bool TypeRequirementChecker::FinalizeCheck()
const {
6286 for (
int type : types_with_same_vehicle_requirements_on_route_) {
6287 for (
const absl::flat_hash_set<int>& requirement_alternatives :
6288 model_.GetSameVehicleRequiredTypeAlternativesOfType(type)) {
6289 bool has_one_of_alternatives =
false;
6290 for (
const int type_alternative : requirement_alternatives) {
6291 if (TypeOccursOnRoute(type_alternative)) {
6292 has_one_of_alternatives =
true;
6296 if (!has_one_of_alternatives) {
6304TypeRegulationsConstraint::TypeRegulationsConstraint(
const RoutingModel&
model)
6305 : Constraint(
model.solver()),
6308 requirement_checker_(
model),
6309 vehicle_demons_(
model.vehicles()) {}
6311void TypeRegulationsConstraint::PropagateNodeRegulations(
int node) {
6312 DCHECK_LT(node, model_.Size());
6313 if (!model_.VehicleVar(node)->Bound() || !model_.NextVar(node)->Bound()) {
6317 const int vehicle = model_.VehicleVar(node)->Min();
6318 if (vehicle < 0)
return;
6319 DCHECK(vehicle_demons_[vehicle] !=
nullptr);
6320 EnqueueDelayedDemon(vehicle_demons_[vehicle]);
6323void TypeRegulationsConstraint::CheckRegulationsOnVehicle(
int vehicle) {
6324 const auto next_accessor = [
this, vehicle](int64_t node) {
6325 if (model_.NextVar(node)->Bound()) {
6326 return model_.NextVar(node)->Value();
6329 return model_.End(vehicle);
6331 if (!incompatibility_checker_.CheckVehicle(vehicle, next_accessor) ||
6332 !requirement_checker_.CheckVehicle(vehicle, next_accessor)) {
6333 model_.solver()->Fail();
6337void TypeRegulationsConstraint::Post() {
6338 for (
int vehicle = 0; vehicle < model_.vehicles(); vehicle++) {
6340 solver(),
this, &TypeRegulationsConstraint::CheckRegulationsOnVehicle,
6341 "CheckRegulationsOnVehicle", vehicle);
6343 for (
int node = 0; node < model_.Size(); node++) {
6345 solver(),
this, &TypeRegulationsConstraint::PropagateNodeRegulations,
6346 "PropagateNodeRegulations", node);
6347 model_.NextVar(node)->WhenBound(node_demon);
6348 model_.VehicleVar(node)->WhenBound(node_demon);
6352void TypeRegulationsConstraint::InitialPropagate() {
6353 for (
int vehicle = 0; vehicle < model_.vehicles(); vehicle++) {
6354 CheckRegulationsOnVehicle(vehicle);
6358void RoutingDimension::CloseModel(
bool use_light_propagation) {
6359 Solver*
const solver = model_->solver();
6360 const auto capacity_lambda = [
this](int64_t vehicle) {
6361 return vehicle >= 0 ? vehicle_capacities_[vehicle]
6362 : std::numeric_limits<int64_t>::max();
6364 for (
int i = 0;
i < capacity_vars_.size(); ++
i) {
6365 IntVar*
const vehicle_var = model_->VehicleVar(i);
6366 IntVar*
const capacity_var = capacity_vars_[
i];
6367 if (use_light_propagation) {
6368 solver->AddConstraint(solver->MakeLightElement(
6369 capacity_lambda, capacity_var, vehicle_var,
6370 [
this]() { return model_->enable_deep_serialization_; }));
6372 solver->AddConstraint(solver->MakeEquality(
6374 solver->MakeElement(capacity_lambda, vehicle_var)->Var()));
6377 for (
int i = 0;
i < fixed_transits_.size(); ++
i) {
6378 IntVar*
const next_var = model_->NextVar(i);
6379 IntVar*
const fixed_transit = fixed_transits_[
i];
6380 const auto transit_vehicle_evaluator = [
this,
i](int64_t
to,
6381 int64_t eval_index) {
6382 return eval_index >= 0 ? transit_evaluator(eval_index)(
i,
to) : 0;
6384 if (use_light_propagation) {
6385 if (class_evaluators_.size() == 1) {
6386 const int class_evaluator_index = class_evaluators_[0];
6387 const auto& unary_callback =
6388 model_->UnaryTransitCallbackOrNull(class_evaluator_index);
6389 if (unary_callback ==
nullptr) {
6390 solver->AddConstraint(solver->MakeLightElement(
6391 [
this, i](int64_t
to) {
6392 return model_->TransitCallback(class_evaluators_[0])(i, to);
6394 fixed_transit, next_var,
6395 [
this]() { return model_->enable_deep_serialization_; }));
6397 fixed_transit->SetValue(unary_callback(i));
6400 solver->AddConstraint(solver->MakeLightElement(
6401 transit_vehicle_evaluator, fixed_transit, next_var,
6402 model_->VehicleVar(i),
6403 [
this]() { return model_->enable_deep_serialization_; }));
6406 if (class_evaluators_.size() == 1) {
6407 const int class_evaluator_index = class_evaluators_[0];
6408 const auto& unary_callback =
6409 model_->UnaryTransitCallbackOrNull(class_evaluator_index);
6410 if (unary_callback ==
nullptr) {
6411 solver->AddConstraint(solver->MakeEquality(
6412 fixed_transit, solver
6414 [
this, i](int64_t
to) {
6415 return model_->TransitCallback(
6416 class_evaluators_[0])(i, to);
6421 fixed_transit->SetValue(unary_callback(i));
6424 solver->AddConstraint(solver->MakeEquality(
6425 fixed_transit, solver
6426 ->MakeElement(transit_vehicle_evaluator,
6427 next_var, model_->VehicleVar(i))
6432 if (HasBreakConstraints()) {
6433 GlobalVehicleBreaksConstraint* constraint =
6434 model()->solver()->RevAlloc(
new GlobalVehicleBreaksConstraint(
this));
6435 solver->AddConstraint(constraint);
6439int64_t RoutingDimension::GetTransitValue(int64_t from_index, int64_t to_index,
6440 int64_t vehicle)
const {
6441 DCHECK(transit_evaluator(vehicle) !=
nullptr);
6442 return transit_evaluator(vehicle)(from_index, to_index);
6445bool RoutingDimension::AllTransitEvaluatorSignsAreUnknown()
const {
6446 for (
const int evaluator_index : class_evaluators_) {
6447 if (
model()->transit_evaluator_sign_[evaluator_index] !=
6448 RoutingModel::kTransitEvaluatorSignUnknown) {
6455SortedDisjointIntervalList RoutingDimension::GetAllowedIntervalsInRange(
6456 int64_t
index, int64_t min_value, int64_t max_value)
const {
6457 SortedDisjointIntervalList allowed;
6458 const SortedDisjointIntervalList& forbidden = forbidden_intervals_[
index];
6460 const int64_t
min = std::max(min_value, cumul_var->Min());
6461 const int64_t
max = std::min(max_value, cumul_var->Max());
6462 int64_t next_start =
min;
6463 for (SortedDisjointIntervalList::Iterator
interval =
6464 forbidden.FirstIntervalGreaterOrEqual(
min);
6466 if (next_start >
max)
break;
6467 if (next_start < interval->
start) {
6472 if (next_start <=
max) {
6473 allowed.InsertInterval(next_start,
max);
6478void RoutingDimension::SetSpanUpperBoundForVehicle(int64_t
upper_bound,
6480 CHECK_GE(vehicle, 0);
6481 CHECK_LT(vehicle, vehicle_span_upper_bounds_.size());
6483 vehicle_span_upper_bounds_[vehicle] =
upper_bound;
6486void RoutingDimension::SetSpanCostCoefficientForVehicle(int64_t
coefficient,
6488 CHECK_GE(vehicle, 0);
6489 CHECK_LT(vehicle, vehicle_span_cost_coefficients_.size());
6491 vehicle_span_cost_coefficients_[vehicle] =
coefficient;
6494void RoutingDimension::SetSpanCostCoefficientForAllVehicles(
6497 vehicle_span_cost_coefficients_.assign(model_->vehicles(),
coefficient);
6500void RoutingDimension::SetGlobalSpanCostCoefficient(int64_t
coefficient) {
6505void RoutingDimension::SetSlackCostCoefficientForVehicle(int64_t
coefficient,
6507 CHECK_GE(vehicle, 0);
6508 CHECK_LT(vehicle, vehicle_slack_cost_coefficients_.size());
6510 vehicle_slack_cost_coefficients_[vehicle] =
coefficient;
6512void RoutingDimension::SetSlackCostCoefficientForAllVehicles(
6515 vehicle_slack_cost_coefficients_.assign(model_->vehicles(),
coefficient);
6518void RoutingDimension::SetCumulVarPiecewiseLinearCost(
6519 int64_t
index,
const PiecewiseLinearFunction& cost) {
6520 if (!cost.IsNonDecreasing()) {
6521 LOG(WARNING) <<
"Only non-decreasing cost functions are supported.";
6524 if (cost.Value(0) < 0) {
6525 LOG(WARNING) <<
"Only positive cost functions are supported.";
6528 if (
index >= cumul_var_piecewise_linear_cost_.size()) {
6529 cumul_var_piecewise_linear_cost_.resize(
index + 1);
6531 PiecewiseLinearCost& piecewise_linear_cost =
6532 cumul_var_piecewise_linear_cost_[
index];
6534 piecewise_linear_cost.cost = std::make_unique<PiecewiseLinearFunction>(cost);
6537bool RoutingDimension::HasCumulVarPiecewiseLinearCost(int64_t
index)
const {
6538 return (
index < cumul_var_piecewise_linear_cost_.size() &&
6539 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr);
6542const PiecewiseLinearFunction* RoutingDimension::GetCumulVarPiecewiseLinearCost(
6543 int64_t
index)
const {
6544 if (
index < cumul_var_piecewise_linear_cost_.size() &&
6545 cumul_var_piecewise_linear_cost_[
index].var !=
nullptr) {
6546 return cumul_var_piecewise_linear_cost_[
index].cost.get();
6552IntVar* BuildVarFromExprAndIndexActiveState(
const RoutingModel*
model,
6553 IntExpr* expr,
int index) {
6554 Solver*
const solver =
model->solver();
6556 const int vehicle =
model->VehicleIndex(
index);
6557 DCHECK_GE(vehicle, 0);
6558 return solver->MakeProd(expr,
model->VehicleRouteConsideredVar(vehicle))
6561 return solver->MakeProd(expr,
model->ActiveVar(
index))->Var();
6565void RoutingDimension::SetupCumulVarPiecewiseLinearCosts(
6566 std::vector<IntVar*>* cost_elements)
const {
6567 CHECK(cost_elements !=
nullptr);
6568 Solver*
const solver = model_->solver();
6569 for (
int i = 0;
i < cumul_var_piecewise_linear_cost_.size(); ++
i) {
6570 const PiecewiseLinearCost& piecewise_linear_cost =
6571 cumul_var_piecewise_linear_cost_[
i];
6572 if (piecewise_linear_cost.var !=
nullptr) {
6573 IntExpr*
const expr = solver->MakePiecewiseLinearExpr(
6574 piecewise_linear_cost.var, *piecewise_linear_cost.cost);
6575 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6576 cost_elements->push_back(cost_var);
6579 model_->AddWeightedVariableMinimizedByFinalizer(cost_var, 0);
6584void RoutingDimension::SetCumulVarSoftUpperBound(int64_t
index,
6587 if (
index >= cumul_var_soft_upper_bound_.size()) {
6588 cumul_var_soft_upper_bound_.resize(
index + 1, {
nullptr, 0, 0});
6594bool RoutingDimension::HasCumulVarSoftUpperBound(int64_t
index)
const {
6595 return (
index < cumul_var_soft_upper_bound_.size() &&
6596 cumul_var_soft_upper_bound_[
index].var !=
nullptr);
6599int64_t RoutingDimension::GetCumulVarSoftUpperBound(int64_t
index)
const {
6600 if (
index < cumul_var_soft_upper_bound_.size() &&
6601 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6602 return cumul_var_soft_upper_bound_[
index].bound;
6607int64_t RoutingDimension::GetCumulVarSoftUpperBoundCoefficient(
6608 int64_t
index)
const {
6609 if (
index < cumul_var_soft_upper_bound_.size() &&
6610 cumul_var_soft_upper_bound_[
index].var !=
nullptr) {
6611 return cumul_var_soft_upper_bound_[
index].coefficient;
6616void RoutingDimension::SetupCumulVarSoftUpperBoundCosts(
6617 std::vector<IntVar*>* cost_elements)
const {
6618 CHECK(cost_elements !=
nullptr);
6619 Solver*
const solver = model_->solver();
6620 for (
int i = 0;
i < cumul_var_soft_upper_bound_.size(); ++
i) {
6621 const SoftBound& soft_bound = cumul_var_soft_upper_bound_[
i];
6622 if (soft_bound.var !=
nullptr) {
6623 IntExpr*
const expr = solver->MakeSemiContinuousExpr(
6624 solver->MakeSum(soft_bound.var, -soft_bound.bound), 0,
6625 soft_bound.coefficient);
6626 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6627 cost_elements->push_back(cost_var);
6630 model_->AddWeightedVariableMinimizedByFinalizer(cost_var,
6631 soft_bound.coefficient);
6636void RoutingDimension::SetCumulVarSoftLowerBound(int64_t
index,
6639 if (
index >= cumul_var_soft_lower_bound_.size()) {
6640 cumul_var_soft_lower_bound_.resize(
index + 1, {
nullptr, 0, 0});
6646bool RoutingDimension::HasCumulVarSoftLowerBound(int64_t
index)
const {
6647 return (
index < cumul_var_soft_lower_bound_.size() &&
6648 cumul_var_soft_lower_bound_[
index].var !=
nullptr);
6651int64_t RoutingDimension::GetCumulVarSoftLowerBound(int64_t
index)
const {
6652 if (
index < cumul_var_soft_lower_bound_.size() &&
6653 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6654 return cumul_var_soft_lower_bound_[
index].bound;
6659int64_t RoutingDimension::GetCumulVarSoftLowerBoundCoefficient(
6660 int64_t
index)
const {
6661 if (
index < cumul_var_soft_lower_bound_.size() &&
6662 cumul_var_soft_lower_bound_[
index].var !=
nullptr) {
6663 return cumul_var_soft_lower_bound_[
index].coefficient;
6668void RoutingDimension::SetupCumulVarSoftLowerBoundCosts(
6669 std::vector<IntVar*>* cost_elements)
const {
6670 CHECK(cost_elements !=
nullptr);
6671 Solver*
const solver = model_->solver();
6672 for (
int i = 0;
i < cumul_var_soft_lower_bound_.size(); ++
i) {
6673 const SoftBound& soft_bound = cumul_var_soft_lower_bound_[
i];
6674 if (soft_bound.var !=
nullptr) {
6675 IntExpr*
const expr = solver->MakeSemiContinuousExpr(
6676 solver->MakeDifference(soft_bound.bound, soft_bound.var), 0,
6677 soft_bound.coefficient);
6678 IntVar* cost_var = BuildVarFromExprAndIndexActiveState(model_, expr, i);
6679 cost_elements->push_back(cost_var);
6682 model_->AddWeightedVariableMinimizedByFinalizer(cost_var,
6683 soft_bound.coefficient);
6688void RoutingDimension::SetupGlobalSpanCost(
6689 std::vector<IntVar*>* cost_elements)
const {
6690 CHECK(cost_elements !=
nullptr);
6691 Solver*
const solver = model_->solver();
6692 if (global_span_cost_coefficient_ != 0) {
6693 std::vector<IntVar*> end_cumuls;
6694 for (
int i = 0;
i < model_->vehicles(); ++
i) {
6695 end_cumuls.push_back(solver
6696 ->MakeProd(model_->vehicle_route_considered_[i],
6700 IntVar*
const max_end_cumul = solver->MakeMax(end_cumuls)->Var();
6701 model_->AddWeightedVariableMinimizedByFinalizer(
6702 max_end_cumul, global_span_cost_coefficient_);
6703 std::vector<IntVar*> start_cumuls;
6704 for (
int i = 0;
i < model_->vehicles(); ++
i) {
6705 IntVar* global_span_cost_start_cumul =
6706 solver->MakeIntVar(0, std::numeric_limits<int64_t>::max());
6707 solver->AddConstraint(solver->MakeIfThenElseCt(
6708 model_->vehicle_route_considered_[i],
cumuls_[model_->Start(i)],
6709 max_end_cumul, global_span_cost_start_cumul));
6710 start_cumuls.push_back(global_span_cost_start_cumul);
6712 IntVar*
const min_start_cumul = solver->MakeMin(start_cumuls)->Var();
6713 model_->AddWeightedVariableMaximizedByFinalizer(
6714 min_start_cumul, global_span_cost_coefficient_);
6718 if (model_->vehicles() == 1) {
6720 model_->AddWeightedVariableMinimizedByFinalizer(
6721 slacks_[
var_index], global_span_cost_coefficient_);
6722 cost_elements->push_back(
6725 model_->vehicle_route_considered_[0],
6730 global_span_cost_coefficient_),
6735 IntVar*
const end_range =
6736 solver->MakeDifference(max_end_cumul, min_start_cumul)->Var();
6737 end_range->SetMin(0);
6738 cost_elements->push_back(
6739 solver->MakeProd(end_range, global_span_cost_coefficient_)->Var());
6744void RoutingDimension::SetBreakIntervalsOfVehicle(
6745 std::vector<IntervalVar*> breaks,
int vehicle,
6746 std::vector<int64_t> node_visit_transits) {
6747 if (breaks.empty())
return;
6748 const int visit_evaluator =
model()->RegisterTransitCallback(
6749 [node_visit_transits](int64_t from, int64_t ) {
6750 return node_visit_transits[from];
6752 RoutingModel::kTransitEvaluatorSignPositiveOrZero);
6753 SetBreakIntervalsOfVehicle(std::move(breaks), vehicle, visit_evaluator, -1);
6756void RoutingDimension::SetBreakIntervalsOfVehicle(
6757 std::vector<IntervalVar*> breaks,
int vehicle,
6758 std::vector<int64_t> node_visit_transits,
6759 std::function<int64_t(int64_t, int64_t)> delays) {
6760 if (breaks.empty())
return;
6761 const int visit_evaluator =
model()->RegisterTransitCallback(
6762 [node_visit_transits](int64_t from, int64_t ) {
6763 return node_visit_transits[from];
6765 RoutingModel::kTransitEvaluatorSignPositiveOrZero);
6766 const int delay_evaluator =
model()->RegisterTransitCallback(
6767 std::move(delays), RoutingModel::kTransitEvaluatorSignPositiveOrZero);
6768 SetBreakIntervalsOfVehicle(std::move(breaks), vehicle, visit_evaluator,
6772void RoutingDimension::SetBreakIntervalsOfVehicle(
6773 std::vector<IntervalVar*> breaks,
int vehicle,
int pre_travel_evaluator,
6774 int post_travel_evaluator) {
6775 DCHECK_LE(0, vehicle);
6776 DCHECK_LT(vehicle, model_->vehicles());
6777 if (breaks.empty())
return;
6778 if (!break_constraints_are_initialized_) InitializeBreaks();
6779 vehicle_break_intervals_[vehicle] = std::move(breaks);
6780 vehicle_pre_travel_evaluators_[vehicle] = pre_travel_evaluator;
6781 vehicle_post_travel_evaluators_[vehicle] = post_travel_evaluator;
6783 for (IntervalVar*
const interval : vehicle_break_intervals_[vehicle]) {
6784 model_->AddIntervalToAssignment(
interval);
6786 model_->AddVariableTargetToFinalizer(
interval->PerformedExpr()->Var(), 0);
6788 model_->AddVariableTargetToFinalizer(
interval->SafeStartExpr(0)->Var(),
6789 std::numeric_limits<int64_t>::min());
6790 model_->AddVariableTargetToFinalizer(
interval->SafeDurationExpr(0)->Var(),
6791 std::numeric_limits<int64_t>::min());
6795 model_->AddVariableTargetToFinalizer(CumulVar(model_->End(vehicle)),
6796 std::numeric_limits<int64_t>::min());
6797 model_->AddVariableTargetToFinalizer(CumulVar(model_->Start(vehicle)),
6798 std::numeric_limits<int64_t>::max());
6801void RoutingDimension::InitializeBreaks() {
6802 DCHECK(!break_constraints_are_initialized_);
6803 const int num_vehicles = model_->vehicles();
6804 vehicle_break_intervals_.resize(num_vehicles);
6805 vehicle_pre_travel_evaluators_.resize(num_vehicles, -1);
6806 vehicle_post_travel_evaluators_.resize(num_vehicles, -1);
6807 vehicle_break_distance_duration_.resize(num_vehicles);
6808 break_constraints_are_initialized_ =
true;
6811bool RoutingDimension::HasBreakConstraints()
const {
6812 return break_constraints_are_initialized_;
6815const std::vector<IntervalVar*>& RoutingDimension::GetBreakIntervalsOfVehicle(
6816 int vehicle)
const {
6817 DCHECK_LE(0, vehicle);
6818 DCHECK_LT(vehicle, vehicle_break_intervals_.size());
6819 return vehicle_break_intervals_[vehicle];
6822int RoutingDimension::GetPreTravelEvaluatorOfVehicle(
int vehicle)
const {
6823 DCHECK_LE(0, vehicle);
6824 DCHECK_LT(vehicle, vehicle_pre_travel_evaluators_.size());
6825 return vehicle_pre_travel_evaluators_[vehicle];
6828int RoutingDimension::GetPostTravelEvaluatorOfVehicle(
int vehicle)
const {
6829 DCHECK_LE(0, vehicle);
6830 DCHECK_LT(vehicle, vehicle_post_travel_evaluators_.size());
6831 return vehicle_post_travel_evaluators_[vehicle];
6834void RoutingDimension::SetBreakDistanceDurationOfVehicle(int64_t
distance,
6837 DCHECK_LE(0, vehicle);
6838 DCHECK_LT(vehicle, model_->vehicles());
6839 if (!break_constraints_are_initialized_) InitializeBreaks();
6840 vehicle_break_distance_duration_[vehicle].emplace_back(
distance, duration);
6843 model_->AddVariableTargetToFinalizer(CumulVar(model_->End(vehicle)),
6844 std::numeric_limits<int64_t>::min());
6845 model_->AddVariableTargetToFinalizer(CumulVar(model_->Start(vehicle)),
6846 std::numeric_limits<int64_t>::max());
6849const std::vector<std::pair<int64_t, int64_t>>&
6850RoutingDimension::GetBreakDistanceDurationOfVehicle(
int vehicle)
const {
6851 DCHECK_LE(0, vehicle);
6852 DCHECK_LT(vehicle, vehicle_break_distance_duration_.size());
6853 return vehicle_break_distance_duration_[vehicle];
6856void RoutingDimension::SetPickupToDeliveryLimitFunctionForPair(
6857 PickupToDeliveryLimitFunction limit_function,
int pair_index) {
6858 CHECK_GE(pair_index, 0);
6859 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6860 pickup_to_delivery_limits_per_pair_index_.resize(pair_index + 1);
6862 pickup_to_delivery_limits_per_pair_index_[pair_index] =
6863 std::move(limit_function);
6866bool RoutingDimension::HasPickupToDeliveryLimits()
const {
6867 return !pickup_to_delivery_limits_per_pair_index_.empty();
6870int64_t RoutingDimension::GetPickupToDeliveryLimitForPair(
6871 int pair_index,
int pickup_alternative_index,
6872 int delivery_alternative_index)
const {
6873 DCHECK_GE(pair_index, 0);
6875 if (pair_index >= pickup_to_delivery_limits_per_pair_index_.size()) {
6876 return std::numeric_limits<int64_t>::max();
6878 const PickupToDeliveryLimitFunction& pickup_to_delivery_limit_function =
6879 pickup_to_delivery_limits_per_pair_index_[pair_index];
6880 if (!pickup_to_delivery_limit_function) {
6882 return std::numeric_limits<int64_t>::max();
6884 DCHECK_GE(pickup_alternative_index, 0);
6885 DCHECK_GE(delivery_alternative_index, 0);
6886 return pickup_to_delivery_limit_function(pickup_alternative_index,
6887 delivery_alternative_index);
6890void RoutingDimension::SetupSlackAndDependentTransitCosts()
const {
6891 if (model_->vehicles() == 0)
return;
6893 if (absl::c_all_of(vehicle_span_cost_coefficients_,
6894 [](int64_t c) {
return c == 0; }) &&
6895 absl::c_all_of(vehicle_slack_cost_coefficients_,
6896 [](int64_t c) {
return c == 0; })) {
6908 std::vector<const RoutingDimension*> dimensions_with_relevant_slacks = {
this};
6910 const RoutingDimension*
next =
6911 dimensions_with_relevant_slacks.back()->base_dimension_;
6912 if (
next ==
nullptr ||
next == dimensions_with_relevant_slacks.back()) {
6915 dimensions_with_relevant_slacks.push_back(
next);
6918 for (
auto it = dimensions_with_relevant_slacks.rbegin();
6919 it != dimensions_with_relevant_slacks.rend(); ++it) {
6920 for (
int i = 0;
i < model_->vehicles(); ++
i) {
6921 model_->AddVariableTargetToFinalizer((*it)->cumuls_[model_->End(i)],
6922 std::numeric_limits<int64_t>::min());
6923 model_->AddVariableTargetToFinalizer((*it)->cumuls_[model_->Start(i)],
6924 std::numeric_limits<int64_t>::max());
6926 for (IntVar*
const slack : (*it)->slacks_) {
6927 model_->AddVariableTargetToFinalizer(slack,
6928 std::numeric_limits<int64_t>::min());
std::vector< int > dimensions
int64_t MemoryUsage(int unused)
A connected components finder that only works on dense ints.
IntVarElement * Add(IntVar *var)
static int64_t FastInt64Round(double x)
DecisionBuilder * MakeRestoreAssignment(Assignment *assignment)
DecisionBuilder * Compose(DecisionBuilder *db1, DecisionBuilder *db2)
Assignment * MakeAssignment()
This method creates an empty assignment.
bool Solve(DecisionBuilder *db, const std::vector< SearchMonitor * > &monitors)
void resize(size_type new_size)
const std::string name
A name for logging purposes.
const std::vector< IntVar * > cumuls_
const Collection::value_type::second_type FindPtrOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
void STLDeleteElements(T *container)
Collection::value_type::second_type & LookupOrInsert(Collection *const collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
bool FindCopy(const Collection &collection, const Key &key, Value *const value)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
const Collection::value_type::second_type * FindOrNull(const Collection &collection, const typename Collection::value_type::first_type &key)
const MapUtilMappedT< Collection > & FindWithDefault(const Collection &collection, const KeyType &key, const MapUtilMappedT< Collection > &value)
For infeasible and unbounded see Not checked if options check_solutions_if_inf_or_unbounded and the If options first_solution_only is false
problem is infeasible or unbounded (default).
H AbslHashValue(H h, const IndicatorConstraint &constraint)
In SWIG mode, we don't want anything besides these top-level includes.
std::unique_ptr< NeighborAcceptanceCriterion > MakeNeighborAcceptanceCriterion(const RoutingSearchParameters ¶meters)
Returns a neighbor acceptance criterion based on the given parameters.
IntVarLocalSearchFilter * MakeVehicleAmortizedCostFilter(const RoutingModel &routing_model)
Returns a filter computing vehicle amortized costs.
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
Returns a DecisionBuilder making all nodes unperformed.
int64_t CapAdd(int64_t x, int64_t y)
FirstSolutionStrategy::Value AutomaticFirstSolutionStrategy(bool has_pickup_deliveries, bool has_node_precedences, bool has_single_vehicle_node)
IntVarLocalSearchFilter * MakeMaxActiveVehiclesFilter(const RoutingModel &routing_model)
Returns a filter ensuring that max active vehicles constraints are enforced.
void AppendLightWeightDimensionFilters(const PathState *path_state, const std::vector< RoutingDimension * > &dimensions, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
RangeIntToIntFunction * MakeCachedIntToIntFunction(const std::function< int64_t(int64_t)> &f, int64_t domain_start, int64_t domain_end)
IntVarLocalSearchFilter * MakeNodeDisjunctionFilter(const RoutingModel &routing_model, bool filter_cost)
Returns a filter ensuring that node disjunction constraints are enforced.
void AcceptUncheckedNeighbor(Search *search)
int64_t CapSub(int64_t x, int64_t y)
IntVarLocalSearchFilter * MakeVehicleBreaksFilter(const RoutingModel &routing_model, const RoutingDimension &dimension)
LocalSearchFilter * MakePathEnergyCostFilter(Solver *solver, std::unique_ptr< PathEnergyCostChecker > checker, absl::string_view dimension_name)
DecisionBuilder * MakeRestoreDimensionValuesForUnchangedRoutes(RoutingModel *model)
ForwardEbertGraph< NodeIndex, ArcIndex > ForwardStarGraph
LocalSearchFilter * MakePathStateFilter(Solver *solver, std::unique_ptr< PathState > path_state, const std::vector< IntVar * > &nexts)
IntVarLocalSearchFilter * MakeCPFeasibilityFilter(RoutingModel *routing_model)
Returns a filter checking the current solution using CP propagation.
Constraint * MakePathSpansAndTotalSlacks(const RoutingDimension *dimension, std::vector< IntVar * > spans, std::vector< IntVar * > total_slacks)
IntVarLocalSearchFilter * MakePickupDeliveryFilter(const RoutingModel &routing_model, const std::vector< PickupDeliveryPair > &pairs, const std::vector< RoutingModel::PickupAndDeliveryPolicy > &vehicle_policies)
int64_t One()
This method returns 1.
RoutingModelParameters DefaultRoutingModelParameters()
DecisionBuilder * MakeSetCumulsFromLocalDimensionCosts(Solver *solver, LocalDimensionCumulOptimizer *lp_optimizer, LocalDimensionCumulOptimizer *mp_optimizer, bool optimize_and_pack, std::vector< RoutingModel::RouteDimensionTravelInfo > dimension_travel_info_per_route)
int64_t CapProd(int64_t x, int64_t y)
Constraint * MakeResourceConstraint(const RoutingModel::ResourceGroup *resource_group, const std::vector< IntVar * > *vehicle_resource_vars, RoutingModel *model)
static const int kUnassigned
--— Routing model --—
void FillPathEvaluation(const std::vector< int64_t > &path, const RoutingModel::TransitCallback2 &evaluator, std::vector< int64_t > *values)
RangeMinMaxIndexFunction * MakeCachedRangeMinMaxIndexFunction(const std::function< int64_t(int64_t)> &f, int64_t domain_start, int64_t domain_end)
void AppendDimensionCumulFilters(const std::vector< RoutingDimension * > &dimensions, const RoutingSearchParameters ¶meters, bool filter_objective_cost, bool use_chain_cumul_filter, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
int64_t CapAbs(int64_t v)
DecisionBuilder * MakeSweepDecisionBuilder(RoutingModel *model, bool check_assignment)
Demon * MakeDelayedConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
DecisionBuilder * MakeSetCumulsFromGlobalDimensionCosts(Solver *solver, GlobalDimensionCumulOptimizer *global_optimizer, GlobalDimensionCumulOptimizer *global_mp_optimizer, SearchMonitor *monitor, bool optimize_and_pack, std::vector< RoutingModel::RouteDimensionTravelInfo > dimension_travel_info_per_route)
Variant based on global optimizers, handling all routes together.
std::unique_ptr< BinCapacities > MakeBinCapacities(const std::vector< RoutingDimension * > &dimensions, const PathsMetadata &paths_metadata)
IntVarLocalSearchFilter * MakeVehicleVarFilter(const RoutingModel &routing_model)
Returns a filter checking that vehicle variable domains are respected.
IntVarLocalSearchFilter * MakeTypeRegulationsFilter(const RoutingModel &routing_model)
Returns a filter ensuring type regulation constraints are enforced.
RoutingSearchParameters DefaultRoutingSearchParameters()
static
Demon * MakeConstraintDemon1(Solver *const s, T *const ct, void(T::*method)(P), const std::string &name, P param1)
bool SolveModelWithSat(RoutingModel *model, const RoutingSearchParameters &search_parameters, const Assignment *initial_solution, Assignment *solution)
std::string FindErrorInRoutingSearchParameters(const RoutingSearchParameters &search_parameters)
Constraint * MakeDifferentFromValues(Solver *solver, IntVar *var, std::vector< int64_t > values)
void SetAssignmentFromAssignment(Assignment *target_assignment, const std::vector< IntVar * > &target_vars, const Assignment *source_assignment, const std::vector< IntVar * > &source_vars)
NOLINT.
DecisionBuilder * MakePerturbationDecisionBuilder(const RoutingSearchParameters ¶meters, RoutingModel *model, const Assignment *assignment, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
trees with all degrees equal to
static int input(yyscan_t yyscanner)
int64_t fixed_cost
Contrarily to CostClass, here we need strict equivalence.
std::vector< int64_t > group_allowed_resources_hash
#define CP_ROUTING_PUSH_OPERATOR(operator_type, operator_method)
util_intops::StrongVector< DimensionIndex, int64_t > dimension_end_cumuls_max
util_intops::StrongVector< DimensionIndex, int64_t > dimension_capacities
util_intops::StrongVector< DimensionIndex, int64_t > dimension_start_cumuls_min
uint64_t visitable_nodes_hash
Hash of the visitability of (non-start/end) nodes.
RoutingModel::CostClassIndex cost_class_index
The cost class of the vehicle.
std::vector< bool > assignable_to_vehicle
Assignability of vehicles.
bool used_when_empty
Whether or not the vehicle is used when empty.
int start_equivalence_class
util_intops::StrongVector< DimensionIndex, int64_t > dimension_start_cumuls_max
int end_equivalence_class
util_intops::StrongVector< DimensionIndex, int64_t > dimension_evaluator_classes
util_intops::StrongVector< DimensionIndex, ResourceGroup::Attributes > dimension_attributes
The attributes for each dimension.
util_intops::StrongVector< DimensionIndex, int64_t > dimension_end_cumuls_min
std::vector< int > var_indices
std::optional< int64_t > end
static const int64_t kint64max
static const int64_t kint64min