27#include "absl/functional/bind_front.h"
28#include "absl/log/check.h"
29#include "absl/time/time.h"
30#include "absl/types/span.h"
31#include "google/protobuf/repeated_ptr_field.h"
36#include "ortools/constraint_solver/routing_ils.pb.h"
37#include "ortools/constraint_solver/routing_parameters.pb.h"
49MakeGlobalCheapestInsertionParameters(
50 const RoutingSearchParameters& search_parameters,
bool is_sequential) {
54 gci_parameters.farthest_seeds_ratio =
55 search_parameters.cheapest_insertion_farthest_seeds_ratio();
56 gci_parameters.neighbors_ratio =
57 search_parameters.cheapest_insertion_first_solution_neighbors_ratio();
58 gci_parameters.min_neighbors =
59 search_parameters.cheapest_insertion_first_solution_min_neighbors();
60 gci_parameters.use_neighbors_ratio_for_initialization =
62 .cheapest_insertion_first_solution_use_neighbors_ratio_for_initialization();
63 gci_parameters.add_unperformed_entries =
64 search_parameters.cheapest_insertion_add_unperformed_entries();
65 return gci_parameters;
71 const RoutingSearchParameters& search_parameters) {
74 search_parameters.savings_neighbors_ratio();
75 savings_parameters.max_memory_usage_bytes =
76 search_parameters.savings_max_memory_usage_bytes();
77 savings_parameters.add_reverse_arcs =
78 search_parameters.savings_add_reverse_arcs();
79 savings_parameters.arc_coefficient =
80 search_parameters.savings_arc_coefficient();
81 return savings_parameters;
85std::unique_ptr<RuinProcedure> MakeRuinProcedure(
86 RoutingModel* model, std::mt19937* rnd, RuinStrategy ruin,
87 int num_neighbors_for_route_selection) {
88 switch (ruin.strategy_case()) {
89 case RuinStrategy::kSpatiallyCloseRoutes:
90 return std::make_unique<CloseRoutesRemovalRuinProcedure>(
91 model, rnd, ruin.spatially_close_routes().num_ruined_routes(),
92 num_neighbors_for_route_selection);
94 case RuinStrategy::kRandomWalk:
95 return std::make_unique<RandomWalkRemovalRuinProcedure>(
96 model, rnd, ruin.random_walk().num_removed_visits(),
97 num_neighbors_for_route_selection);
98 case RuinStrategy::kSisr:
99 return std::make_unique<SISRRuinProcedure>(
100 model, rnd, ruin.sisr().max_removed_sequence_size(),
101 ruin.sisr().avg_num_removed_visits(), ruin.sisr().bypass_factor(),
102 num_neighbors_for_route_selection);
104 LOG(DFATAL) <<
"Unsupported ruin procedure.";
110std::vector<std::unique_ptr<RuinProcedure>> MakeRuinProcedures(
111 RoutingModel* model, std::mt19937* rnd,
112 const google::protobuf::RepeatedPtrField<
113 ::operations_research::RuinStrategy>& ruin_strategies,
114 int num_neighbors_for_route_selection) {
115 std::vector<std::unique_ptr<RuinProcedure>> ruin_procedures;
116 for (
const RuinStrategy& ruin : ruin_strategies) {
117 ruin_procedures.push_back(
118 MakeRuinProcedure(model, rnd, ruin, num_neighbors_for_route_selection));
120 return ruin_procedures;
123class SequentialCompositionStrategy
126 explicit SequentialCompositionStrategy(
127 std::vector<RuinProcedure*> ruin_procedures)
128 : CompositeRuinProcedure::CompositionStrategy(
129 std::move(ruin_procedures)) {}
130 const std::vector<RuinProcedure*>& Select()
override {
return ruins_; }
133class SequentialRandomizedCompositionStrategy
136 SequentialRandomizedCompositionStrategy(
137 std::vector<RuinProcedure*> ruin_procedures, std::mt19937* rnd)
138 : CompositeRuinProcedure::CompositionStrategy(std::move(ruin_procedures)),
140 const std::vector<RuinProcedure*>& Select()
override {
141 std::shuffle(ruins_.begin(), ruins_.end(), rnd_);
149class SingleRandomCompositionStrategy
152 SingleRandomCompositionStrategy(std::vector<RuinProcedure*> ruin_procedures,
154 : CompositeRuinProcedure::CompositionStrategy(std::move(ruin_procedures)),
156 single_ruin_.resize(1);
158 const std::vector<RuinProcedure*>& Select()
override {
159 single_ruin_[0] = ruins_[rnd_() % ruins_.size()];
167 std::vector<RuinProcedure*> single_ruin_;
171std::unique_ptr<CompositeRuinProcedure::CompositionStrategy>
172MakeRuinCompositionStrategy(
173 absl::Span<
const std::unique_ptr<RuinProcedure>> ruins,
174 RuinCompositionStrategy::Value composition_strategy, std::mt19937* rnd) {
175 std::vector<RuinProcedure*> ruin_ptrs;
176 ruin_ptrs.reserve(ruins.size());
177 for (
const auto& ruin : ruins) {
178 ruin_ptrs.push_back(ruin.get());
181 switch (composition_strategy) {
182 case RuinCompositionStrategy::RUN_ALL_SEQUENTIALLY:
183 return std::make_unique<SequentialCompositionStrategy>(
184 std::move(ruin_ptrs));
185 case RuinCompositionStrategy::RUN_ALL_RANDOMLY:
186 return std::make_unique<SequentialRandomizedCompositionStrategy>(
187 std::move(ruin_ptrs), rnd);
188 case RuinCompositionStrategy::RUN_ONE_RANDOMLY:
189 return std::make_unique<SingleRandomCompositionStrategy>(
190 std::move(ruin_ptrs), rnd);
192 LOG(DFATAL) <<
"Unsupported composition strategy.";
198std::unique_ptr<RuinProcedure> MakeRuinProcedure(
199 const RuinRecreateParameters& parameters, RoutingModel* model,
201 const int num_non_start_end_nodes = model->Size() - model->vehicles();
202 const uint32_t preferred_num_neighbors =
203 parameters.route_selection_neighbors_ratio() * num_non_start_end_nodes;
207 const uint32_t num_neighbors_for_route_selection =
208 std::min(parameters.route_selection_max_neighbors(),
209 std::max(parameters.route_selection_min_neighbors(),
210 preferred_num_neighbors));
212 if (parameters.ruin_strategies().size() == 1) {
213 return MakeRuinProcedure(model, rnd, *parameters.ruin_strategies().begin(),
214 num_neighbors_for_route_selection);
217 return std::make_unique<CompositeRuinProcedure>(
219 MakeRuinProcedures(model, rnd, parameters.ruin_strategies(),
220 num_neighbors_for_route_selection),
221 parameters.ruin_composition_strategy(), rnd);
225std::unique_ptr<RoutingFilteredHeuristic> MakeRecreateProcedure(
226 const RoutingSearchParameters& parameters, RoutingModel* model,
227 std::function<
bool()> stop_search,
229 switch (parameters.iterated_local_search_parameters()
230 .ruin_recreate_parameters()
231 .recreate_strategy()) {
232 case FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION:
233 return std::make_unique<LocalCheapestInsertionFilteredHeuristic>(
234 model, std::move(stop_search),
235 absl::bind_front(&RoutingModel::GetArcCostForVehicle, model),
236 parameters.local_cheapest_cost_insertion_pickup_delivery_strategy(),
238 parameters.local_cheapest_insertion_sorting_properties()),
239 filter_manager, model->GetBinCapacities());
240 case FirstSolutionStrategy::LOCAL_CHEAPEST_COST_INSERTION:
241 return std::make_unique<LocalCheapestInsertionFilteredHeuristic>(
242 model, std::move(stop_search),
244 parameters.local_cheapest_cost_insertion_pickup_delivery_strategy(),
246 parameters.local_cheapest_insertion_sorting_properties()),
247 filter_manager, model->GetBinCapacities());
248 case FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION: {
249 GlobalCheapestInsertionFilteredHeuristic::
250 GlobalCheapestInsertionParameters gci_parameters =
251 MakeGlobalCheapestInsertionParameters(parameters,
253 return std::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
254 model, std::move(stop_search),
255 absl::bind_front(&RoutingModel::GetArcCostForVehicle, model),
256 [model](int64_t i) {
return model->UnperformedPenaltyOrValue(0, i); },
257 filter_manager, gci_parameters);
259 case FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION: {
260 GlobalCheapestInsertionFilteredHeuristic::
261 GlobalCheapestInsertionParameters gci_parameters =
262 MakeGlobalCheapestInsertionParameters(parameters,
264 return std::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
265 model, std::move(stop_search),
266 absl::bind_front(&RoutingModel::GetArcCostForVehicle, model),
267 [model](int64_t i) {
return model->UnperformedPenaltyOrValue(0, i); },
268 filter_manager, gci_parameters);
270 case FirstSolutionStrategy::SAVINGS: {
271 return std::make_unique<SequentialSavingsFilteredHeuristic>(
272 model, std::move(stop_search), MakeSavingsParameters(parameters),
275 case FirstSolutionStrategy::PARALLEL_SAVINGS: {
276 return std::make_unique<ParallelSavingsFilteredHeuristic>(
277 model, std::move(stop_search), MakeSavingsParameters(parameters),
281 LOG(DFATAL) <<
"Unsupported recreate procedure.";
292 bool Accept([[maybe_unused]]
const SearchState& search_state,
293 const Assignment* candidate,
294 const Assignment* reference)
override {
295 return candidate->ObjectiveValue() < reference->ObjectiveValue();
300class CoolingSchedule {
302 CoolingSchedule(NeighborAcceptanceCriterion::SearchState final_search_state,
303 double initial_temperature,
double final_temperature)
304 : final_search_state_(std::move(final_search_state)),
305 initial_temperature_(initial_temperature),
306 final_temperature_(final_temperature) {
307 DCHECK_GE(initial_temperature_, final_temperature_);
309 virtual ~CoolingSchedule() =
default;
312 virtual double GetTemperature(
313 const NeighborAcceptanceCriterion::SearchState& search_state)
const = 0;
319 const NeighborAcceptanceCriterion::SearchState& search_state)
const {
320 const double duration_progress =
321 absl::FDivDuration(search_state.duration, final_search_state_.duration);
322 const double solutions_progress =
323 static_cast<double>(search_state.solutions) /
324 final_search_state_.solutions;
325 const double progress = std::max(duration_progress, solutions_progress);
328 return std::min(1.0, progress);
331 const NeighborAcceptanceCriterion::SearchState final_search_state_;
332 const double initial_temperature_;
333 const double final_temperature_;
337class ExponentialCoolingSchedule :
public CoolingSchedule {
339 ExponentialCoolingSchedule(
340 NeighborAcceptanceCriterion::SearchState final_search_state,
341 double initial_temperature,
double final_temperature)
342 : CoolingSchedule(std::move(final_search_state), initial_temperature,
344 temperature_ratio_(final_temperature / initial_temperature) {}
346 double GetTemperature(
const NeighborAcceptanceCriterion::SearchState&
347 search_state)
const override {
348 const double progress = GetProgress(search_state);
350 return initial_temperature_ * std::pow(temperature_ratio_, progress);
354 const double temperature_ratio_;
358class LinearCoolingSchedule :
public CoolingSchedule {
360 LinearCoolingSchedule(
361 NeighborAcceptanceCriterion::SearchState final_search_state,
362 double initial_temperature,
double final_temperature)
363 : CoolingSchedule(std::move(final_search_state), initial_temperature,
364 final_temperature) {}
366 double GetTemperature(
const NeighborAcceptanceCriterion::SearchState&
367 search_state)
const override {
368 const double progress = GetProgress(search_state);
369 return initial_temperature_ -
370 progress * (initial_temperature_ - final_temperature_);
375std::unique_ptr<CoolingSchedule> MakeCoolingSchedule(
376 const RoutingModel& model,
const RoutingSearchParameters& parameters,
378 const absl::Duration final_duration =
379 !parameters.has_time_limit()
380 ? absl::InfiniteDuration()
383 const SimulatedAnnealingParameters& sa_params =
384 parameters.iterated_local_search_parameters()
385 .simulated_annealing_parameters();
388 final_duration, parameters.solution_limit()};
390 const auto [initial_temperature, final_temperature] =
393 switch (sa_params.cooling_schedule_strategy()) {
394 case CoolingScheduleStrategy::EXPONENTIAL:
395 return std::make_unique<ExponentialCoolingSchedule>(
397 parameters.solution_limit()},
398 initial_temperature, final_temperature);
399 case CoolingScheduleStrategy::LINEAR:
400 return std::make_unique<LinearCoolingSchedule>(
401 std::move(final_search_state), initial_temperature,
404 LOG(DFATAL) <<
"Unsupported cooling schedule strategy.";
412class SimulatedAnnealingAcceptanceCriterion
415 explicit SimulatedAnnealingAcceptanceCriterion(
416 std::unique_ptr<CoolingSchedule> cooling_schedule, std::mt19937* rnd)
417 : cooling_schedule_(std::move(cooling_schedule)),
419 probability_distribution_(0.0, 1.0) {}
421 bool Accept(
const SearchState& search_state,
const Assignment* candidate,
422 const Assignment* reference)
override {
423 double temperature = cooling_schedule_->GetTemperature(search_state);
424 return candidate->ObjectiveValue() +
425 temperature * std::log(probability_distribution_(rnd_)) <
426 reference->ObjectiveValue();
430 std::unique_ptr<CoolingSchedule> cooling_schedule_;
432 std::uniform_real_distribution<double> probability_distribution_;
436bool HasPerformedNodes(
const RoutingModel& model,
438 for (
int v = 0; v < model.vehicles(); ++v) {
439 if (model.Next(assignment, model.Start(v)) != model.End(v)) {
447int CountUsedVehicles(
const RoutingModel& model,
const Assignment& assignment) {
449 for (
int vehicle = 0; vehicle < model.vehicles(); ++vehicle) {
450 count += model.Next(assignment, model.Start(vehicle)) != model.End(vehicle);
456double ComputeAverageNonEmptyRouteSize(
const RoutingModel& model,
458 const int num_used_vehicles = CountUsedVehicles(model, assignment);
459 if (num_used_vehicles == 0)
return 0;
461 const double num_visits = model.Size() - model.vehicles();
462 return num_visits / num_used_vehicles;
468int64_t PickRandomPerformedVisit(
469 const RoutingModel& model,
const Assignment& assignment, std::mt19937& rnd,
470 std::uniform_int_distribution<int64_t>& customer_dist) {
471 DCHECK_EQ(customer_dist.min(), 0);
472 DCHECK_EQ(customer_dist.max(), model.Size() - model.vehicles());
474 if (!HasPerformedNodes(model, assignment)) {
480 customer = customer_dist(rnd);
481 }
while (model.IsStart(customer) ||
482 assignment.Value(model.VehicleVar(customer)) == -1);
483 DCHECK(!model.IsEnd(customer));
489 const int all_nodes = model.Size() + model.vehicles();
491 nexts_.resize(all_nodes, -1);
492 prevs_.resize(all_nodes, -1);
493 route_sizes_.resize(model.vehicles(), 0);
497 assignment_ = assignment;
500 nexts_.assign(nexts_.size(), -1);
503 prevs_.assign(prevs_.size(), -1);
504 route_sizes_.assign(model_.vehicles(), -1);
508 const int64_t start = model_.Start(vehicle);
514 const int64_t end = model_.End(vehicle);
517 int64_t curr = start;
520 route_sizes_[vehicle] = -1;
521 while (curr != end) {
522 const int64_t next = assignment_->Value(model_.NextVar(curr));
526 ++route_sizes_[vehicle];
538 DCHECK_EQ(nexts_[node_index] != -1, prevs_[node_index] != -1);
539 return nexts_[node_index] != -1;
545 : assignment_->Value(model_.NextVar(node_index));
550 return prevs_[node_index];
555 return route_sizes_[vehicle];
559 return !model_.IsStart(node_index) && !model_.IsEnd(node_index) &&
566 DCHECK_NE(nexts_[node_index], node_index);
567 DCHECK_NE(prevs_[node_index], node_index);
569 const int64_t next = nexts_[node_index];
570 const int64_t prev = prevs_[node_index];
572 const int vehicle = assignment_->Value(model_.VehicleVar(node_index));
573 --route_sizes_[vehicle];
574 DCHECK_GE(route_sizes_[vehicle], 0);
579 nexts_[node_index] = node_index;
580 prevs_[node_index] = node_index;
584 DCHECK(!model_.IsStart(customer));
585 DCHECK(!model_.IsEnd(customer));
586 if (
const std::optional<int64_t> sibling_node =
587 model_.GetFirstMatchingPickupDeliverySibling(
588 customer, [
this](int64_t node) { return CanBeRemoved(node); });
589 sibling_node.has_value()) {
590 const int sibling_vehicle =
591 assignment_->Value(model_.VehicleVar(sibling_node.value()));
592 DCHECK_NE(sibling_vehicle, -1);
600 int64_t visit, std::mt19937& rnd,
601 std::bernoulli_distribution& boolean_dist)
const {
603 DCHECK(!model_.IsStart(visit));
604 DCHECK(!model_.IsEnd(visit));
608 const int vehicle = assignment_->Value(model_.VehicleVar(visit));
613 const bool move_forward = boolean_dist(rnd);
616 if (model_.IsStart(next_node) || model_.IsEnd(next_node)) {
620 DCHECK(!model_.IsStart(next_node));
621 DCHECK(!model_.IsEnd(next_node));
626 int64_t seed_visit, std::mt19937& rnd,
627 std::bernoulli_distribution& boolean_dist,
int size)
const {
629 DCHECK(!model_.IsStart(seed_visit));
630 DCHECK(!model_.IsEnd(seed_visit));
642 if (model_.IsStart(left) && model_.IsEnd(right)) {
650 if (model_.IsStart(left)) {
652 }
else if (model_.IsEnd(right)) {
655 const bool move_forward = boolean_dist(rnd);
666 std::vector<int64_t> sequence;
668 while (curr != right) {
669 sequence.push_back(curr);
676 std::vector<RuinProcedure*> ruin_procedures)
681 std::vector<std::unique_ptr<RuinProcedure>> ruin_procedures,
682 RuinCompositionStrategy::Value composition_strategy, std::mt19937* rnd)
684 ruin_procedures_(
std::move(ruin_procedures)),
685 composition_strategy_(MakeRuinCompositionStrategy(
686 ruin_procedures_, composition_strategy, rnd)),
687 ruined_assignment_(model_.solver()->MakeAssignment()),
688 next_assignment_(model_.solver()->MakeAssignment()) {}
692 const std::vector<RuinProcedure*>& ruins = composition_strategy_->Select();
694 auto next_accessors = ruins[0]->Ruin(assignment);
695 for (
int i = 1; i < ruins.size(); ++i) {
697 BuildAssignmentFromNextAccessor(next_accessors);
698 ruined_assignment_->Copy(next_assignment);
699 next_accessors = ruins[i]->Ruin(ruined_assignment_);
702 return next_accessors;
705const Assignment* CompositeRuinProcedure::BuildAssignmentFromNextAccessor(
706 const std::function<int64_t(int64_t)>& next_accessors) {
707 next_assignment_->
Clear();
710 for (
int node = 0; node < model_.Size(); node++) {
711 const int64_t next = next_accessors(node);
712 next_assignment_->
Add(model_.NextVar(node))->
SetValue(next);
715 next_assignment_->
Add(model_.VehicleVar(node))->
SetValue(-1);
720 for (
int vehicle = 0; vehicle < model_.vehicles(); ++vehicle) {
721 int64_t node = model_.Start(vehicle);
722 while (!model_.IsEnd(node)) {
723 next_assignment_->Add(model_.VehicleVar(node))->SetValue(vehicle);
724 node = next_accessors(node);
727 next_assignment_->Add(model_.VehicleVar(node))->SetValue(vehicle);
730 return next_assignment_;
734 RoutingModel* model, std::mt19937* rnd,
size_t num_routes,
735 int num_neighbors_for_route_selection)
737 neighbors_manager_(model->GetOrCreateNodeNeighborsByCostClass(
738 {num_neighbors_for_route_selection,
742 num_routes_(num_routes),
744 customer_dist_(0, model->Size() - model->vehicles()),
745 removed_routes_(model->vehicles()) {}
749 if (num_routes_ == 0) {
750 return [
this, assignment](int64_t node) {
751 return assignment->
Value(model_.NextVar(node));
755 const int64_t seed_node =
756 PickRandomPerformedVisit(model_, *assignment, rnd_, customer_dist_);
757 if (seed_node == -1) {
758 return [
this, assignment](int64_t node) {
759 return assignment->
Value(model_.NextVar(node));
763 removed_routes_.SparseClearAll();
765 const int seed_route = assignment->
Value(model_.VehicleVar(seed_node));
766 DCHECK_GE(seed_route, 0);
768 removed_routes_.Set(seed_route);
770 const RoutingCostClassIndex cost_class_index =
771 model_.GetCostClassIndexOfVehicle(seed_route);
773 const std::vector<int>& neighbors =
774 neighbors_manager_->GetOutgoingNeighborsOfNodeForCostClass(
775 cost_class_index.value(), seed_node);
777 for (
int neighbor : neighbors) {
778 if (removed_routes_.NumberOfSetCallsWithDifferentArguments() ==
782 const int64_t route = assignment->
Value(model_.VehicleVar(neighbor));
783 if (route < 0 || removed_routes_[route]) {
786 removed_routes_.Set(route);
789 return [
this, assignment](int64_t node) {
791 if (model_.IsStart(node)) {
792 const int route = assignment->
Value(model_.VehicleVar(node));
793 if (removed_routes_[route]) {
794 return model_.End(route);
797 return assignment->
Value(model_.NextVar(node));
802 RoutingModel* model, std::mt19937* rnd,
int walk_length,
803 int num_neighbors_for_route_selection)
805 routing_solution_(*model),
806 neighbors_manager_(model->GetOrCreateNodeNeighborsByCostClass(
807 {num_neighbors_for_route_selection,
812 walk_length_(walk_length),
813 customer_dist_(0, model->Size() - model->vehicles()) {}
817 if (walk_length_ == 0) {
818 return [
this, assignment](int64_t node) {
819 return assignment->
Value(model_.NextVar(node));
824 PickRandomPerformedVisit(model_, *assignment, rnd_, customer_dist_);
825 if (curr_node == -1) {
826 return [
this, assignment](int64_t node) {
827 return assignment->
Value(model_.NextVar(node));
831 routing_solution_.Reset(assignment);
833 int walk_length = walk_length_;
835 while (walk_length-- > 0) {
839 routing_solution_.RemovePerformedPickupDeliverySibling(curr_node);
841 const int64_t next_node = GetNextNodeToRemove(assignment, curr_node);
843 routing_solution_.RemoveNode(curr_node);
845 if (next_node == -1) {
851 curr_node = next_node;
855 [
this](int64_t node) {
return routing_solution_.GetNextNodeIndex(node); };
858int64_t RandomWalkRemovalRuinProcedure::GetNextNodeToRemove(
860 const int curr_vehicle = assignment->
Value(model_.VehicleVar(node));
863 if (boolean_dist_(rnd_)) {
864 const int64_t next_node =
866 if (next_node != -1) {
873 const RoutingCostClassIndex cost_class_index =
874 model_.GetCostClassIndexOfVehicle(curr_vehicle);
876 const std::vector<int>& neighbors =
877 neighbors_manager_->GetOutgoingNeighborsOfNodeForCostClass(
878 cost_class_index.value(), node);
880 int64_t same_route_closest_neighbor = -1;
882 for (
int neighbor : neighbors) {
883 const int neighbor_vehicle = assignment->
Value(model_.VehicleVar(neighbor));
885 if (!routing_solution_.CanBeRemoved(neighbor)) {
889 if (neighbor_vehicle == curr_vehicle) {
890 if (same_route_closest_neighbor == -1) {
891 same_route_closest_neighbor = neighbor;
903 return same_route_closest_neighbor;
907 int max_removed_sequence_size,
908 int avg_num_removed_visits,
909 double bypass_factor,
int num_neighbors)
912 max_removed_sequence_size_(max_removed_sequence_size),
913 avg_num_removed_visits_(avg_num_removed_visits),
914 bypass_factor_(bypass_factor),
915 neighbors_manager_(model->GetOrCreateNodeNeighborsByCostClass(
920 customer_dist_(0, model->Size() - model->vehicles()),
921 probability_dist_(0.0, 1.0),
922 ruined_routes_(model->vehicles()),
923 routing_solution_(*model) {}
927 const int64_t seed_node =
928 PickRandomPerformedVisit(model_, *assignment, rnd_, customer_dist_);
929 if (seed_node == -1) {
930 return [
this, assignment](int64_t node) {
931 return assignment->
Value(model_.NextVar(node));
935 routing_solution_.Reset(assignment);
936 ruined_routes_.SparseClearAll();
938 const double max_sequence_size =
939 std::min<double>(max_removed_sequence_size_,
940 ComputeAverageNonEmptyRouteSize(model_, *assignment));
942 const double max_num_removed_sequences =
943 (4 * avg_num_removed_visits_) / (1 + max_sequence_size) - 1;
944 DCHECK_GE(max_num_removed_sequences, 1);
946 const int num_sequences_to_remove =
947 std::floor(std::uniform_real_distribution<double>(
948 1.0, max_num_removed_sequences + 1)(rnd_));
951 const int seed_route = RuinRoute(*assignment, seed_node, max_sequence_size);
952 DCHECK_NE(seed_route, -1);
954 const RoutingCostClassIndex cost_class_index =
955 model_.GetCostClassIndexOfVehicle(seed_route);
957 for (
const int neighbor :
958 neighbors_manager_->GetOutgoingNeighborsOfNodeForCostClass(
959 cost_class_index.value(), seed_node)) {
960 if (ruined_routes_.NumberOfSetCallsWithDifferentArguments() ==
961 num_sequences_to_remove) {
965 if (!routing_solution_.CanBeRemoved(neighbor)) {
969 RuinRoute(*assignment, neighbor, max_sequence_size);
973 [
this](int64_t node) {
return routing_solution_.GetNextNodeIndex(node); };
976int SISRRuinProcedure::RuinRoute(
const Assignment& assignment,
978 double global_max_sequence_size) {
979 const int route = assignment.
Value(model_.VehicleVar(seed_visit));
981 if (ruined_routes_[route])
return -1;
984 ruined_routes_.
Set(route);
986 const double max_sequence_size = std::min<double>(
987 routing_solution_.
GetRouteSize(route), global_max_sequence_size);
989 int sequence_size = std::floor(
990 std::uniform_real_distribution<double>(1.0, max_sequence_size + 1)(rnd_));
992 if (sequence_size == 1 || sequence_size == max_sequence_size ||
993 boolean_dist_(rnd_)) {
994 RuinRouteWithSequenceProcedure(seed_visit, sequence_size);
996 RuinRouteWithSplitSequenceProcedure(route, seed_visit, sequence_size);
1002void SISRRuinProcedure::RuinRouteWithSequenceProcedure(int64_t seed_visit,
1003 int sequence_size) {
1004 const std::vector<int64_t> sequence =
1005 routing_solution_.GetRandomSequenceOfVisits(seed_visit, rnd_,
1006 boolean_dist_, sequence_size);
1009 for (
const int64_t visit : sequence) {
1010 routing_solution_.RemoveNode(visit);
1014 for (
const int64_t visit : sequence) {
1015 routing_solution_.RemovePerformedPickupDeliverySibling(visit);
1019void SISRRuinProcedure::RuinRouteWithSplitSequenceProcedure(int64_t route,
1021 int sequence_size) {
1022 const int max_num_bypassed_visits =
1023 routing_solution_.GetRouteSize(route) - sequence_size;
1024 int num_bypassed_visits = 1;
1025 while (num_bypassed_visits < max_num_bypassed_visits &&
1026 probability_dist_(rnd_) >= bypass_factor_ * probability_dist_(rnd_)) {
1027 ++num_bypassed_visits;
1030 const std::vector<int64_t> sequence =
1031 routing_solution_.GetRandomSequenceOfVisits(
1032 seed_visit, rnd_, boolean_dist_, sequence_size + num_bypassed_visits);
1034 const int start_bypassed_visits = rnd_() % (sequence_size + 1);
1035 const int end_bypassed_visits = start_bypassed_visits + num_bypassed_visits;
1038 for (
int i = 0;
i < start_bypassed_visits; ++
i) {
1039 routing_solution_.RemoveNode(sequence[i]);
1041 for (
int i = end_bypassed_visits;
i < sequence.size(); ++
i) {
1042 routing_solution_.RemoveNode(sequence[i]);
1046 for (
int i = 0;
i < start_bypassed_visits; ++
i) {
1047 routing_solution_.RemovePerformedPickupDeliverySibling(sequence[i]);
1049 for (
int i = end_bypassed_visits;
i < sequence.size(); ++
i) {
1050 routing_solution_.RemovePerformedPickupDeliverySibling(sequence[i]);
1057 const Assignment* assignment, std::unique_ptr<RuinProcedure> ruin,
1058 std::unique_ptr<RoutingFilteredHeuristic> recreate)
1059 : assignment_(*assignment),
1060 ruin_(
std::move(ruin)),
1061 recreate_(
std::move(recreate)) {}
1064 Assignment*
const new_assignment = Recreate(ruin_->Ruin(&assignment_));
1065 if (new_assignment) {
1074 Assignment* Recreate(
const std::function<int64_t(int64_t)>& next_accessor) {
1075 return recreate_->BuildSolutionFromRoutes(next_accessor);
1078 const Assignment& assignment_;
1079 std::unique_ptr<RuinProcedure> ruin_;
1080 std::unique_ptr<RoutingFilteredHeuristic> recreate_;
1084 const RoutingSearchParameters& parameters, RoutingModel* model,
1085 std::mt19937* rnd,
const Assignment* assignment,
1086 std::function<
bool()> stop_search,
1088 std::unique_ptr<RuinProcedure> ruin = MakeRuinProcedure(
1089 parameters.iterated_local_search_parameters().ruin_recreate_parameters(),
1092 std::unique_ptr<RoutingFilteredHeuristic> recreate = MakeRecreateProcedure(
1093 parameters, model, std::move(stop_search), filter_manager);
1096 assignment, std::move(ruin), std::move(recreate)));
1100 const RoutingSearchParameters& parameters, RoutingModel* model,
1101 std::mt19937* rnd,
const Assignment* assignment,
1102 std::function<
bool()> stop_search,
1105 parameters.iterated_local_search_parameters().perturbation_strategy()) {
1106 case PerturbationStrategy::RUIN_AND_RECREATE:
1108 parameters, model, rnd, assignment, std::move(stop_search),
1111 LOG(DFATAL) <<
"Unsupported perturbation strategy.";
1117 const RoutingModel& model,
const RoutingSearchParameters& parameters,
1118 std::mt19937* rnd) {
1119 CHECK(parameters.has_iterated_local_search_parameters());
1120 switch (parameters.iterated_local_search_parameters().acceptance_strategy()) {
1121 case AcceptanceStrategy::GREEDY_DESCENT:
1122 return std::make_unique<GreedyDescentAcceptanceCriterion>();
1123 case AcceptanceStrategy::SIMULATED_ANNEALING:
1124 return std::make_unique<SimulatedAnnealingAcceptanceCriterion>(
1125 MakeCoolingSchedule(model, parameters, rnd), rnd);
1127 LOG(DFATAL) <<
"Unsupported acceptance strategy.";
1133 const RoutingModel& model,
const SimulatedAnnealingParameters& sa_params,
1134 std::mt19937* rnd) {
1135 if (!sa_params.automatic_temperatures()) {
1136 return {sa_params.initial_temperature(), sa_params.final_temperature()};
1142 if (model.vehicles() == 0) {
1146 std::vector<int> num_vehicles_of_class(model.GetCostClassesCount(), 0);
1147 for (
int vehicle = 0; vehicle < model.vehicles(); ++vehicle) {
1148 const RoutingCostClassIndex cost_class =
1149 model.GetCostClassIndexOfVehicle(vehicle);
1150 ++num_vehicles_of_class[cost_class.value()];
1153 std::uniform_int_distribution<int64_t> node_dist(0, model.nodes() - 1);
1155 const int sample_size = model.nodes();
1156 DCHECK_GT(sample_size, 0);
1158 std::vector<double> mean_arc_cost_for_class(model.GetCostClassesCount(), 0.0);
1159 for (
int cost_class = 0; cost_class < model.GetCostClassesCount();
1161 if (num_vehicles_of_class[cost_class] == 0) {
1165 for (
int i = 0; i < sample_size; ++i) {
1166 mean_arc_cost_for_class[cost_class] += model.GetArcCostForClass(
1167 node_dist(*rnd), node_dist(*rnd), cost_class);
1169 mean_arc_cost_for_class[cost_class] /= sample_size;
1172 double reference_temperature = 0;
1173 DCHECK_GT(model.vehicles(), 0);
1174 for (
int cost_class = 0; cost_class < model.GetCostClassesCount();
1176 reference_temperature += mean_arc_cost_for_class[cost_class] *
1177 num_vehicles_of_class[cost_class] /
1181 return {reference_temperature * 0.1, reference_temperature * 0.001};
int64_t Value(const IntVar *var) const
IntVarElement * Add(IntVar *var)
CloseRoutesRemovalRuinProcedure(RoutingModel *model, std::mt19937 *rnd, size_t num_routes, int num_neighbors_for_route_selection)
std::function< int64_t(int64_t)> Ruin(const Assignment *assignment) override
Composition strategy interface.
std::vector< RuinProcedure * > ruins_
Contains ptrs to the available ruins.
CompositionStrategy(std::vector< RuinProcedure * > ruin_procedures)
CompositeRuinProcedure(RoutingModel *model, std::vector< std::unique_ptr< RuinProcedure > > ruin_procedures, RuinCompositionStrategy::Value composition_strategy, std::mt19937 *rnd)
std::function< int64_t(int64_t)> Ruin(const Assignment *assignment) override
Returns next accessors describing the ruined solution.
Neighbor acceptance criterion interface.
RandomWalkRemovalRuinProcedure(RoutingModel *model, std::mt19937 *rnd, int walk_length, int num_neighbors_for_route_selection)
std::function< int64_t(int64_t)> Ruin(const Assignment *assignment) override
Returns next accessors describing the ruined solution.
int64_t GetInitializedPrevNodeIndex(int64_t node_index) const
bool CanBeRemoved(int64_t node_index) const
void RemoveNode(int64_t node_index)
void RemovePerformedPickupDeliverySibling(int64_t customer)
Removes the performed sibling pickup or delivery of customer, if any.
void Reset(const Assignment *assignment)
Initializes the routing solution for the given assignment.
int64_t GetRandomAdjacentVisit(int64_t visit, std::mt19937 &rnd, std::bernoulli_distribution &boolean_dist) const
void InitializeRouteInfoIfNeeded(int vehicle)
std::vector< int64_t > GetRandomSequenceOfVisits(int64_t seed_visit, std::mt19937 &rnd, std::bernoulli_distribution &boolean_dist, int size) const
RoutingSolution(const RoutingModel &model)
int64_t GetNextNodeIndex(int64_t node_index) const
Returns the next node index of the given node_index.
int GetRouteSize(int vehicle) const
bool BelongsToInitializedRoute(int64_t node_index) const
Returns whether node_index belongs to a route that has been initialized.
Decision * Next(Solver *const solver) override
RuinAndRecreateDecisionBuilder(const Assignment *assignment, std::unique_ptr< RuinProcedure > ruin, std::unique_ptr< RoutingFilteredHeuristic > recreate)
SISRRuinProcedure(RoutingModel *model, std::mt19937 *rnd, int max_removed_sequence_size, int avg_num_removed_visits, double bypass_factor, int num_neighbors)
std::function< int64_t(int64_t)> Ruin(const Assignment *assignment) override
Returns next accessors describing the ruined solution.
For the time being, Solver is neither MT_SAFE nor MT_HOT.
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
void Set(IntegerType index)
In SWIG mode, we don't want anything besides these top-level includes.
std::vector< RoutingSearchParameters::InsertionSortingProperty > GetLocalCheapestInsertionSortingProperties(absl::Span< const int > lci_insertion_sorting_properties)
DecisionBuilder * MakeRuinAndRecreateDecisionBuilder(const RoutingSearchParameters ¶meters, RoutingModel *model, std::mt19937 *rnd, const Assignment *assignment, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
std::pair< double, double > GetSimulatedAnnealingTemperatures(const RoutingModel &model, const SimulatedAnnealingParameters &sa_params, std::mt19937 *rnd)
DecisionBuilder * MakePerturbationDecisionBuilder(const RoutingSearchParameters ¶meters, RoutingModel *model, std::mt19937 *rnd, const Assignment *assignment, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
std::unique_ptr< NeighborAcceptanceCriterion > MakeNeighborAcceptanceCriterion(const RoutingModel &model, const RoutingSearchParameters ¶meters, std::mt19937 *rnd)
Returns a neighbor acceptance criterion based on the given parameters.
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
bool is_sequential
Whether the routes are constructed sequentially or in parallel.
Representation of the search process state.