28#include "absl/functional/bind_front.h"
29#include "absl/log/check.h"
30#include "absl/log/log.h"
31#include "absl/time/time.h"
32#include "absl/types/span.h"
33#include "google/protobuf/repeated_ptr_field.h"
47GetGlobalCheapestInsertionParametersForRecreateStrategy(
50 return recreate_strategy.has_parameters() &&
51 recreate_strategy.parameters().has_global_cheapest_insertion()
52 ? recreate_strategy.parameters().global_cheapest_insertion()
59GetLocalCheapestInsertionParametersForRecreateStrategy(
62 return recreate_strategy.has_parameters() &&
63 recreate_strategy.parameters().has_local_cheapest_insertion()
64 ? recreate_strategy.parameters().local_cheapest_insertion()
71 return recreate_strategy.has_parameters() &&
72 recreate_strategy.parameters().has_savings()
73 ? recreate_strategy.parameters().savings()
78std::unique_ptr<RuinProcedure> MakeRuinProcedure(
80 int num_neighbors_for_route_selection) {
81 switch (ruin.strategy_case()) {
83 return std::make_unique<CloseRoutesRemovalRuinProcedure>(
84 model, rnd, ruin.spatially_close_routes().num_ruined_routes(),
85 num_neighbors_for_route_selection);
88 return std::make_unique<RandomWalkRemovalRuinProcedure>(
89 model, rnd, ruin.random_walk().num_removed_visits(),
90 num_neighbors_for_route_selection);
92 return std::make_unique<SISRRuinProcedure>(
93 model, rnd, ruin.sisr().max_removed_sequence_size(),
94 ruin.sisr().avg_num_removed_visits(), ruin.sisr().bypass_factor(),
95 num_neighbors_for_route_selection);
97 LOG(DFATAL) <<
"Unsupported ruin procedure.";
103std::vector<std::unique_ptr<RuinProcedure>> MakeRuinProcedures(
105 const google::protobuf::RepeatedPtrField<
106 ::operations_research::RuinStrategy>& ruin_strategies,
107 int num_neighbors_for_route_selection) {
108 std::vector<std::unique_ptr<RuinProcedure>> ruin_procedures;
110 ruin_procedures.push_back(
111 MakeRuinProcedure(model, rnd, ruin, num_neighbors_for_route_selection));
113 return ruin_procedures;
116class SequentialCompositionStrategy
119 explicit SequentialCompositionStrategy(
120 std::vector<RuinProcedure*> ruin_procedures)
121 : CompositeRuinProcedure::CompositionStrategy(
122 std::move(ruin_procedures)) {}
123 const std::vector<RuinProcedure*>& Select()
override {
return ruins_; }
126class SequentialRandomizedCompositionStrategy
129 SequentialRandomizedCompositionStrategy(
130 std::vector<RuinProcedure*> ruin_procedures, std::mt19937* rnd)
131 : CompositeRuinProcedure::CompositionStrategy(std::move(ruin_procedures)),
133 const std::vector<RuinProcedure*>& Select()
override {
134 std::shuffle(ruins_.begin(), ruins_.end(), rnd_);
142class SingleRandomCompositionStrategy
145 SingleRandomCompositionStrategy(std::vector<RuinProcedure*> ruin_procedures,
147 : CompositeRuinProcedure::CompositionStrategy(std::move(ruin_procedures)),
149 single_ruin_.resize(1);
151 const std::vector<RuinProcedure*>& Select()
override {
152 single_ruin_[0] = ruins_[rnd_() % ruins_.size()];
160 std::vector<RuinProcedure*> single_ruin_;
164std::unique_ptr<CompositeRuinProcedure::CompositionStrategy>
165MakeRuinCompositionStrategy(
166 absl::Span<
const std::unique_ptr<RuinProcedure>> ruins,
168 std::vector<RuinProcedure*> ruin_ptrs;
169 ruin_ptrs.reserve(ruins.size());
170 for (
const auto& ruin : ruins) {
171 ruin_ptrs.push_back(ruin.get());
174 switch (composition_strategy) {
176 return std::make_unique<SequentialCompositionStrategy>(
177 std::move(ruin_ptrs));
179 return std::make_unique<SequentialRandomizedCompositionStrategy>(
180 std::move(ruin_ptrs), rnd);
182 return std::make_unique<SingleRandomCompositionStrategy>(
183 std::move(ruin_ptrs), rnd);
185 LOG(DFATAL) <<
"Unsupported composition strategy.";
191std::unique_ptr<RuinProcedure> MakeRuinProcedure(
194 const int num_non_start_end_nodes = model->Size() - model->vehicles();
195 const uint32_t preferred_num_neighbors =
196 parameters.route_selection_neighbors_ratio() * num_non_start_end_nodes;
200 const uint32_t num_neighbors_for_route_selection =
201 std::min(parameters.route_selection_max_neighbors(),
202 std::max(parameters.route_selection_min_neighbors(),
203 preferred_num_neighbors));
205 if (parameters.ruin_strategies().size() == 1) {
206 return MakeRuinProcedure(model, rnd, *parameters.ruin_strategies().begin(),
207 num_neighbors_for_route_selection);
210 return std::make_unique<CompositeRuinProcedure>(
212 MakeRuinProcedures(model, rnd, parameters.ruin_strategies(),
213 num_neighbors_for_route_selection),
214 parameters.ruin_composition_strategy(), rnd);
218std::unique_ptr<RoutingFilteredHeuristic> MakeRecreateProcedure(
220 std::function<
bool()> stop_search,
223 parameters.iterated_local_search_parameters()
224 .ruin_recreate_parameters()
225 .recreate_strategy();
226 switch (recreate_strategy.heuristic()) {
228 return std::make_unique<LocalCheapestInsertionFilteredHeuristic>(
229 model, std::move(stop_search),
231 GetLocalCheapestInsertionParametersForRecreateStrategy(
233 parameters.local_cheapest_insertion_parameters()),
234 filter_manager, model->GetBinCapacities());
236 return std::make_unique<LocalCheapestInsertionFilteredHeuristic>(
237 model, std::move(stop_search),
239 GetLocalCheapestInsertionParametersForRecreateStrategy(
241 parameters.local_cheapest_cost_insertion_parameters()),
242 filter_manager, model->GetBinCapacities());
244 return std::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
245 model, std::move(stop_search),
247 [model](int64_t i) {
return model->UnperformedPenaltyOrValue(0, i); },
249 GetGlobalCheapestInsertionParametersForRecreateStrategy(
251 parameters.global_cheapest_insertion_first_solution_parameters()),
254 return std::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
255 model, std::move(stop_search),
257 [model](int64_t i) {
return model->UnperformedPenaltyOrValue(0, i); },
259 GetGlobalCheapestInsertionParametersForRecreateStrategy(
261 parameters.global_cheapest_insertion_first_solution_parameters()),
264 return std::make_unique<SequentialSavingsFilteredHeuristic>(
265 model, std::move(stop_search),
266 GetSavingsParametersForRecreateStrategy(
267 recreate_strategy, parameters.savings_parameters()),
270 return std::make_unique<ParallelSavingsFilteredHeuristic>(
271 model, std::move(stop_search),
272 GetSavingsParametersForRecreateStrategy(
273 recreate_strategy, parameters.savings_parameters()),
276 LOG(DFATAL) <<
"Unsupported recreate procedure.";
287 bool Accept([[maybe_unused]]
const SearchState& search_state,
288 const Assignment* candidate,
289 const Assignment* reference)
override {
290 return candidate->ObjectiveValue() < reference->ObjectiveValue();
295class CoolingSchedule {
297 CoolingSchedule(NeighborAcceptanceCriterion::SearchState final_search_state,
298 double initial_temperature,
double final_temperature)
299 : final_search_state_(std::move(final_search_state)),
300 initial_temperature_(initial_temperature),
301 final_temperature_(final_temperature) {
302 DCHECK_GE(initial_temperature_, final_temperature_);
304 virtual ~CoolingSchedule() =
default;
307 virtual double GetTemperature(
308 const NeighborAcceptanceCriterion::SearchState& search_state)
const = 0;
314 const NeighborAcceptanceCriterion::SearchState& search_state)
const {
315 const double duration_progress =
316 absl::FDivDuration(search_state.duration, final_search_state_.duration);
317 const double solutions_progress =
318 static_cast<double>(search_state.solutions) /
319 final_search_state_.solutions;
320 const double progress = std::max(duration_progress, solutions_progress);
323 return std::min(1.0, progress);
326 const NeighborAcceptanceCriterion::SearchState final_search_state_;
327 const double initial_temperature_;
328 const double final_temperature_;
332class ExponentialCoolingSchedule :
public CoolingSchedule {
334 ExponentialCoolingSchedule(
335 NeighborAcceptanceCriterion::SearchState final_search_state,
336 double initial_temperature,
double final_temperature)
337 : CoolingSchedule(std::move(final_search_state), initial_temperature,
339 temperature_ratio_(final_temperature / initial_temperature) {}
341 double GetTemperature(
const NeighborAcceptanceCriterion::SearchState&
342 search_state)
const override {
343 const double progress = GetProgress(search_state);
345 return initial_temperature_ * std::pow(temperature_ratio_, progress);
349 const double temperature_ratio_;
353class LinearCoolingSchedule :
public CoolingSchedule {
355 LinearCoolingSchedule(
356 NeighborAcceptanceCriterion::SearchState final_search_state,
357 double initial_temperature,
double final_temperature)
358 : CoolingSchedule(std::move(final_search_state), initial_temperature,
359 final_temperature) {}
361 double GetTemperature(
const NeighborAcceptanceCriterion::SearchState&
362 search_state)
const override {
363 const double progress = GetProgress(search_state);
364 return initial_temperature_ -
365 progress * (initial_temperature_ - final_temperature_);
370std::unique_ptr<CoolingSchedule> MakeCoolingSchedule(
375 const auto [initial_temperature, final_temperature] =
378 switch (sa_params.cooling_schedule_strategy()) {
380 return std::make_unique<ExponentialCoolingSchedule>(
381 final_search_state, initial_temperature, final_temperature);
383 return std::make_unique<LinearCoolingSchedule>(
384 final_search_state, initial_temperature, final_temperature);
386 LOG(DFATAL) <<
"Unsupported cooling schedule strategy.";
394class SimulatedAnnealingAcceptanceCriterion
397 explicit SimulatedAnnealingAcceptanceCriterion(
398 std::unique_ptr<CoolingSchedule> cooling_schedule, std::mt19937* rnd)
399 : cooling_schedule_(std::move(cooling_schedule)),
401 probability_distribution_(0.0, 1.0) {}
403 bool Accept(
const SearchState& search_state,
const Assignment* candidate,
404 const Assignment* reference)
override {
405 double temperature = cooling_schedule_->GetTemperature(search_state);
406 return candidate->ObjectiveValue() +
407 temperature * std::log(probability_distribution_(rnd_)) <
408 reference->ObjectiveValue();
412 std::unique_ptr<CoolingSchedule> cooling_schedule_;
414 std::uniform_real_distribution<double> probability_distribution_;
419class AllNodesPerformedAcceptanceCriterion
422 explicit AllNodesPerformedAcceptanceCriterion(
const RoutingModel& model)
425 bool Accept([[maybe_unused]]
const SearchState& search_state,
426 const Assignment* candidate,
427 [[maybe_unused]]
const Assignment* reference)
override {
429 d < model_.GetNumberOfDisjunctions(); ++d) {
431 int num_possible_actives = model_.GetDisjunctionNodeIndices(d).size();
432 for (
const int64_t node : model_.GetDisjunctionNodeIndices(d)) {
433 if (candidate->Value(model_.NextVar(node)) == node) {
434 --num_possible_actives;
437 if (num_possible_actives < model_.GetDisjunctionMaxCardinality(d)) {
442 for (
int node = 0; node < model_.Size(); ++node) {
443 if (model_.IsStart(node) || model_.IsEnd(node))
continue;
444 if (!model_.GetDisjunctionIndices(node).empty())
continue;
445 if (candidate->Value(model_.NextVar(node)) == node)
return false;
451 const RoutingModel& model_;
458 for (
int v = 0; v < model.vehicles(); ++v) {
459 int64_t current_node_index = model.Start(v);
461 current_node_index = assignment.Value(model.NextVar(current_node_index));
462 if (model.IsEnd(current_node_index)) {
473class MoreNodesPerformedAcceptanceCriterion
476 explicit MoreNodesPerformedAcceptanceCriterion(
const RoutingModel& model)
479 bool Accept([[maybe_unused]]
const SearchState& search_state,
480 const Assignment* candidate,
481 const Assignment* reference)
override {
482 return CountPerformedNodes(model_, *candidate) >
483 CountPerformedNodes(model_, *reference);
487 const RoutingModel& model_;
492 explicit AbsencesBasedAcceptanceCriterion(
493 const RoutingModel& model,
bool remove_route_with_lowest_absences)
495 remove_route_with_lowest_absences_(remove_route_with_lowest_absences),
496 absences_(model.Size(), 0) {}
498 bool Accept([[maybe_unused]]
const SearchState& search_state,
499 const Assignment* candidate,
500 const Assignment* reference)
override {
501 int sum_candidate_absences = 0;
502 int sum_reference_absences = 0;
503 for (
int node = 0; node < model_.Size(); ++node) {
504 if (model_.IsStart(node) || model_.IsEnd(node))
continue;
505 if (candidate->Value(model_.NextVar(node)) == node) {
506 sum_candidate_absences += absences_[node];
508 if (reference->Value(model_.NextVar(node)) == node) {
509 sum_reference_absences += absences_[node];
512 return sum_candidate_absences < sum_reference_absences;
515 void OnIterationEnd(
const Assignment* reference)
override {
516 for (
int node = 0; node < model_.Size(); ++node) {
517 if (model_.IsStart(node) || model_.IsEnd(node))
continue;
518 if (reference->Value(model_.NextVar(node)) == node) {
524 void OnBestSolutionFound(Assignment* reference)
override {
525 if (!remove_route_with_lowest_absences_)
return;
527 int candidate_route = -1;
528 int min_sum_absences = std::numeric_limits<int>::max();
530 for (
int route = 0; route < model_.vehicles(); ++route) {
531 if (model_.Next(*reference, model_.Start(route)) == model_.End(route))
533 int sum_absences = 0;
534 for (int64_t node = reference->Value(model_.NextVar(model_.Start(route)));
535 node != model_.End(route);
536 node = reference->Value(model_.NextVar(node))) {
537 sum_absences += absences_[node];
540 if (sum_absences < min_sum_absences) {
541 candidate_route = route;
542 min_sum_absences = sum_absences;
547 if (candidate_route != -1) {
550 reference->Value(model_.NextVar(model_.Start(candidate_route)));
551 while (node != model_.End(candidate_route)) {
552 const int64_t next_node = reference->Value(model_.NextVar(node));
553 reference->SetValue(model_.NextVar(node), node);
554 reference->SetValue(model_.VehicleVar(node), -1);
558 reference->SetValue(model_.NextVar(model_.Start(candidate_route)),
559 model_.End(candidate_route));
564 const RoutingModel& model_;
565 bool remove_route_with_lowest_absences_;
566 std::vector<int> absences_;
572 for (
int v = 0; v < model.vehicles(); ++v) {
573 if (model.Next(assignment, model.Start(v)) != model.End(v)) {
583 for (
int vehicle = 0; vehicle < model.vehicles(); ++vehicle) {
584 count += model.Next(assignment, model.Start(vehicle)) != model.End(vehicle);
590double ComputeAverageNonEmptyRouteSize(
const RoutingModel& model,
592 const int num_used_vehicles = CountUsedVehicles(model, assignment);
593 if (num_used_vehicles == 0)
return 0;
595 const double num_visits = model.Size() - model.vehicles();
596 return num_visits / num_used_vehicles;
602int64_t PickRandomPerformedVisit(
604 std::uniform_int_distribution<int64_t>& node_dist) {
605 DCHECK_EQ(node_dist.min(), 0);
606 DCHECK_EQ(node_dist.max(), model.Size() - model.vehicles());
608 if (!HasPerformedNodes(model, assignment)) {
614 node = node_dist(rnd);
615 }
while (model.IsStart(node) ||
616 assignment.Value(model.VehicleVar(node)) == -1);
617 DCHECK(!model.IsEnd(node));
625 nexts_.resize(all_nodes, -1);
626 prevs_.resize(all_nodes, -1);
627 route_sizes_.resize(model.
vehicles(), 0);
631 assignment_ = assignment;
634 nexts_.assign(nexts_.size(), -1);
637 prevs_.assign(prevs_.size(), -1);
638 route_sizes_.assign(model_.vehicles(), -1);
642 const int64_t start = model_.Start(vehicle);
648 const int64_t
end = model_.End(vehicle);
651 int64_t curr = start;
654 route_sizes_[vehicle] = -1;
655 while (curr !=
end) {
656 const int64_t next = assignment_->Value(model_.NextVar(curr));
660 ++route_sizes_[vehicle];
672 DCHECK_EQ(nexts_[node] != -1, prevs_[node] != -1);
673 return nexts_[node] != -1;
679 : assignment_->Value(model_.NextVar(node));
689 return route_sizes_[vehicle];
693 return !model_.IsStart(node) && !model_.IsEnd(node) &&
700 DCHECK_NE(nexts_[node], node);
701 DCHECK_NE(prevs_[node], node);
703 const int64_t next = nexts_[node];
704 const int64_t prev = prevs_[node];
706 const int vehicle = assignment_->Value(model_.VehicleVar(node));
707 --route_sizes_[vehicle];
708 DCHECK_GE(route_sizes_[vehicle], 0);
718 DCHECK(!model_.IsStart(node));
719 DCHECK(!model_.IsEnd(node));
720 if (
const std::optional<int64_t> sibling_node =
721 model_.GetFirstMatchingPickupDeliverySibling(
723 [
this](int64_t candidate_node) {
724 return CanBeRemoved(candidate_node);
726 sibling_node.has_value()) {
727 const int sibling_vehicle =
728 assignment_->Value(model_.VehicleVar(sibling_node.value()));
729 DCHECK_NE(sibling_vehicle, -1);
737 int64_t visit, std::mt19937& rnd,
738 std::bernoulli_distribution& boolean_dist)
const {
740 DCHECK(!model_.IsStart(visit));
741 DCHECK(!model_.IsEnd(visit));
745 const int vehicle = assignment_->Value(model_.VehicleVar(visit));
750 const bool move_forward = boolean_dist(rnd);
753 if (model_.IsStart(next_node) || model_.IsEnd(next_node)) {
757 DCHECK(!model_.IsStart(next_node));
758 DCHECK(!model_.IsEnd(next_node));
763 int64_t seed_visit, std::mt19937& rnd,
764 std::bernoulli_distribution& boolean_dist,
int size)
const {
766 DCHECK(!model_.IsStart(seed_visit));
767 DCHECK(!model_.IsEnd(seed_visit));
779 if (model_.IsStart(left) && model_.IsEnd(right)) {
787 if (model_.IsStart(left)) {
789 }
else if (model_.IsEnd(right)) {
792 const bool move_forward = boolean_dist(rnd);
803 std::vector<int64_t> sequence;
805 while (curr != right) {
806 sequence.push_back(curr);
813 std::vector<RuinProcedure*> ruin_procedures)
818 std::vector<std::unique_ptr<RuinProcedure>> ruin_procedures,
821 ruin_procedures_(
std::move(ruin_procedures)),
822 composition_strategy_(MakeRuinCompositionStrategy(
823 ruin_procedures_, composition_strategy, rnd)),
824 ruined_assignment_(model_.solver()->MakeAssignment()),
825 next_assignment_(model_.solver()->MakeAssignment()) {}
829 const std::vector<RuinProcedure*>& ruins = composition_strategy_->Select();
831 auto next_accessors = ruins[0]->Ruin(assignment);
832 for (
int i = 1; i < ruins.size(); ++i) {
834 BuildAssignmentFromNextAccessor(next_accessors);
835 ruined_assignment_->Copy(next_assignment);
836 next_accessors = ruins[i]->Ruin(ruined_assignment_);
839 return next_accessors;
842const Assignment* CompositeRuinProcedure::BuildAssignmentFromNextAccessor(
843 const std::function<int64_t(int64_t)>& next_accessors) {
844 next_assignment_->
Clear();
847 for (
int node = 0; node < model_.
Size(); node++) {
848 const int64_t next = next_accessors(node);
857 for (
int vehicle = 0; vehicle < model_.vehicles(); ++vehicle) {
858 int64_t node = model_.Start(vehicle);
859 while (!model_.IsEnd(node)) {
860 next_assignment_->Add(model_.VehicleVar(node))->SetValue(vehicle);
861 node = next_accessors(node);
864 next_assignment_->Add(model_.VehicleVar(node))->SetValue(vehicle);
867 return next_assignment_;
871 RoutingModel* model, std::mt19937* rnd,
size_t num_routes,
872 int num_neighbors_for_route_selection)
874 neighbors_manager_(model->GetOrCreateNodeNeighborsByCostClass(
875 {num_neighbors_for_route_selection,
879 num_routes_(num_routes),
881 node_dist_(0, model->Size() - model->vehicles()),
882 removed_routes_(model->vehicles()) {}
886 if (num_routes_ == 0) {
887 return [
this, assignment](int64_t node) {
888 return assignment->
Value(model_.NextVar(node));
892 const int64_t seed_node =
893 PickRandomPerformedVisit(model_, *assignment, rnd_, node_dist_);
894 if (seed_node == -1) {
895 return [
this, assignment](int64_t node) {
896 return assignment->
Value(model_.NextVar(node));
900 removed_routes_.ResetAllToFalse();
902 const int seed_route = assignment->
Value(model_.VehicleVar(seed_node));
903 DCHECK_GE(seed_route, 0);
905 removed_routes_.Set(seed_route);
907 const RoutingCostClassIndex cost_class_index =
908 model_.GetCostClassIndexOfVehicle(seed_route);
910 const std::vector<int>& neighbors =
911 neighbors_manager_->GetOutgoingNeighborsOfNodeForCostClass(
912 cost_class_index.value(), seed_node);
914 for (
int neighbor : neighbors) {
915 if (removed_routes_.NumberOfSetCallsWithDifferentArguments() ==
919 const int64_t route = assignment->
Value(model_.VehicleVar(neighbor));
920 if (route < 0 || removed_routes_[route]) {
923 removed_routes_.Set(route);
926 return [
this, assignment](int64_t node) {
928 if (model_.IsStart(node)) {
929 const int route = assignment->
Value(model_.VehicleVar(node));
930 if (removed_routes_[route]) {
931 return model_.End(route);
934 return assignment->
Value(model_.NextVar(node));
939 RoutingModel* model, std::mt19937* rnd,
int walk_length,
940 int num_neighbors_for_route_selection)
942 routing_solution_(*model),
943 neighbors_manager_(model->GetOrCreateNodeNeighborsByCostClass(
944 {num_neighbors_for_route_selection,
949 walk_length_(walk_length),
950 node_dist_(0, model->Size() - model->vehicles()) {}
954 if (walk_length_ == 0) {
955 return [
this, assignment](int64_t node) {
956 return assignment->
Value(model_.NextVar(node));
961 PickRandomPerformedVisit(model_, *assignment, rnd_, node_dist_);
962 if (curr_node == -1) {
963 return [
this, assignment](int64_t node) {
964 return assignment->
Value(model_.NextVar(node));
968 routing_solution_.Reset(assignment);
970 int walk_length = walk_length_;
972 while (walk_length-- > 0) {
976 routing_solution_.RemovePerformedPickupDeliverySibling(curr_node);
978 const int64_t next_node = GetNextNodeToRemove(assignment, curr_node);
980 routing_solution_.RemoveNode(curr_node);
982 if (next_node == -1) {
988 curr_node = next_node;
992 [
this](int64_t node) {
return routing_solution_.GetNextNodeIndex(node); };
995int64_t RandomWalkRemovalRuinProcedure::GetNextNodeToRemove(
1000 if (boolean_dist_(rnd_)) {
1001 const int64_t next_node =
1003 if (next_node != -1) {
1010 const RoutingCostClassIndex cost_class_index =
1011 model_.GetCostClassIndexOfVehicle(curr_vehicle);
1013 const std::vector<int>& neighbors =
1014 neighbors_manager_->GetOutgoingNeighborsOfNodeForCostClass(
1015 cost_class_index.value(), node);
1017 int64_t same_route_closest_neighbor = -1;
1019 for (
int neighbor : neighbors) {
1020 const int neighbor_vehicle = assignment->
Value(model_.VehicleVar(neighbor));
1022 if (!routing_solution_.CanBeRemoved(neighbor)) {
1026 if (neighbor_vehicle == curr_vehicle) {
1027 if (same_route_closest_neighbor == -1) {
1028 same_route_closest_neighbor = neighbor;
1040 return same_route_closest_neighbor;
1044 int max_removed_sequence_size,
1045 int avg_num_removed_visits,
1046 double bypass_factor,
int num_neighbors)
1049 max_removed_sequence_size_(max_removed_sequence_size),
1050 avg_num_removed_visits_(avg_num_removed_visits),
1051 bypass_factor_(bypass_factor),
1052 neighbors_manager_(model->GetOrCreateNodeNeighborsByCostClass(
1057 node_dist_(0, model->Size() - model->vehicles()),
1058 probability_dist_(0.0, 1.0),
1059 ruined_routes_(model->vehicles()),
1060 routing_solution_(*model) {}
1064 const int64_t seed_node =
1065 PickRandomPerformedVisit(model_, *assignment, rnd_, node_dist_);
1066 if (seed_node == -1) {
1067 return [
this, assignment](int64_t node) {
1068 return assignment->
Value(model_.NextVar(node));
1072 routing_solution_.Reset(assignment);
1073 ruined_routes_.ResetAllToFalse();
1075 const double max_sequence_size =
1076 std::min<double>(max_removed_sequence_size_,
1077 ComputeAverageNonEmptyRouteSize(model_, *assignment));
1079 const double max_num_removed_sequences =
1080 (4 * avg_num_removed_visits_) / (1 + max_sequence_size) - 1;
1081 DCHECK_GE(max_num_removed_sequences, 1);
1083 const int num_sequences_to_remove =
1084 std::floor(std::uniform_real_distribution<double>(
1085 1.0, max_num_removed_sequences + 1)(rnd_));
1088 const int seed_route = RuinRoute(*assignment, seed_node, max_sequence_size);
1089 DCHECK_NE(seed_route, -1);
1091 const RoutingCostClassIndex cost_class_index =
1092 model_.GetCostClassIndexOfVehicle(seed_route);
1094 for (
const int neighbor :
1095 neighbors_manager_->GetOutgoingNeighborsOfNodeForCostClass(
1096 cost_class_index.value(), seed_node)) {
1097 if (ruined_routes_.NumberOfSetCallsWithDifferentArguments() ==
1098 num_sequences_to_remove) {
1102 if (!routing_solution_.CanBeRemoved(neighbor)) {
1106 RuinRoute(*assignment, neighbor, max_sequence_size);
1110 [
this](int64_t node) {
return routing_solution_.GetNextNodeIndex(node); };
1113int SISRRuinProcedure::RuinRoute(
const Assignment& assignment,
1115 double global_max_sequence_size) {
1117 DCHECK_GE(route, 0);
1118 if (ruined_routes_[route])
return -1;
1121 ruined_routes_.
Set(route);
1123 const double max_sequence_size = std::min<double>(
1124 routing_solution_.
GetRouteSize(route), global_max_sequence_size);
1126 int sequence_size = std::floor(
1127 std::uniform_real_distribution<double>(1.0, max_sequence_size + 1)(rnd_));
1129 if (sequence_size == 1 || sequence_size == max_sequence_size ||
1130 boolean_dist_(rnd_)) {
1131 RuinRouteWithSequenceProcedure(seed_visit, sequence_size);
1133 RuinRouteWithSplitSequenceProcedure(route, seed_visit, sequence_size);
1139void SISRRuinProcedure::RuinRouteWithSequenceProcedure(int64_t seed_visit,
1140 int sequence_size) {
1141 const std::vector<int64_t> sequence =
1142 routing_solution_.GetRandomSequenceOfVisits(seed_visit, rnd_,
1143 boolean_dist_, sequence_size);
1146 for (
const int64_t visit : sequence) {
1147 routing_solution_.RemoveNode(visit);
1151 for (
const int64_t visit : sequence) {
1152 routing_solution_.RemovePerformedPickupDeliverySibling(visit);
1156void SISRRuinProcedure::RuinRouteWithSplitSequenceProcedure(int64_t route,
1158 int sequence_size) {
1159 const int max_num_bypassed_visits =
1160 routing_solution_.GetRouteSize(route) - sequence_size;
1161 int num_bypassed_visits = 1;
1162 while (num_bypassed_visits < max_num_bypassed_visits &&
1163 probability_dist_(rnd_) >= bypass_factor_ * probability_dist_(rnd_)) {
1164 ++num_bypassed_visits;
1167 const std::vector<int64_t> sequence =
1168 routing_solution_.GetRandomSequenceOfVisits(
1169 seed_visit, rnd_, boolean_dist_, sequence_size + num_bypassed_visits);
1171 const int start_bypassed_visits = rnd_() % (sequence_size + 1);
1172 const int end_bypassed_visits = start_bypassed_visits + num_bypassed_visits;
1175 for (
int i = 0;
i < start_bypassed_visits; ++
i) {
1176 routing_solution_.RemoveNode(sequence[i]);
1178 for (
int i = end_bypassed_visits;
i < sequence.size(); ++
i) {
1179 routing_solution_.RemoveNode(sequence[i]);
1183 for (
int i = 0;
i < start_bypassed_visits; ++
i) {
1184 routing_solution_.RemovePerformedPickupDeliverySibling(sequence[i]);
1186 for (
int i = end_bypassed_visits;
i < sequence.size(); ++
i) {
1187 routing_solution_.RemovePerformedPickupDeliverySibling(sequence[i]);
1194 const Assignment* assignment, std::unique_ptr<RuinProcedure> ruin,
1195 std::unique_ptr<RoutingFilteredHeuristic> recreate)
1196 : assignment_(*assignment),
1197 ruin_(
std::move(ruin)),
1198 recreate_(
std::move(recreate)) {}
1201 Assignment*
const new_assignment = Recreate(ruin_->Ruin(&assignment_));
1202 if (new_assignment) {
1211 Assignment* Recreate(
const std::function<int64_t(int64_t)>& next_accessor) {
1212 return recreate_->BuildSolutionFromRoutes(next_accessor);
1215 const Assignment& assignment_;
1216 std::unique_ptr<RuinProcedure> ruin_;
1217 std::unique_ptr<RoutingFilteredHeuristic> recreate_;
1222 std::mt19937* rnd,
const Assignment* assignment,
1223 std::function<
bool()> stop_search,
1225 std::unique_ptr<RuinProcedure> ruin = MakeRuinProcedure(
1229 std::unique_ptr<RoutingFilteredHeuristic> recreate = MakeRecreateProcedure(
1230 parameters, model, std::move(stop_search), filter_manager);
1233 assignment, std::move(ruin), std::move(recreate)));
1238 std::mt19937* rnd,
const Assignment* assignment,
1239 std::function<
bool()> stop_search,
1245 parameters, model, rnd, assignment, std::move(stop_search),
1248 LOG(DFATAL) <<
"Unsupported perturbation strategy.";
1256 std::mt19937* rnd) {
1259 return std::make_unique<GreedyDescentAcceptanceCriterion>();
1261 return std::make_unique<SimulatedAnnealingAcceptanceCriterion>(
1263 final_search_state, rnd),
1266 return std::make_unique<AllNodesPerformedAcceptanceCriterion>(model);
1268 return std::make_unique<MoreNodesPerformedAcceptanceCriterion>(model);
1270 return std::make_unique<AbsencesBasedAcceptanceCriterion>(
1274 LOG(DFATAL) <<
"Unsupported acceptance strategy.";
1294 for (
int vehicle = 0; vehicle < model.
vehicles(); ++vehicle) {
1295 const RoutingCostClassIndex cost_class =
1297 ++num_vehicles_of_class[cost_class.value()];
1300 std::uniform_int_distribution<int64_t> node_dist(0, model.
nodes() - 1);
1302 const int sample_size = model.
nodes();
1303 DCHECK_GT(sample_size, 0);
1308 if (num_vehicles_of_class[cost_class] == 0) {
1312 for (
int i = 0; i < sample_size; ++i) {
1314 node_dist(*rnd), node_dist(*rnd), cost_class);
1316 mean_arc_cost_for_class[cost_class] /= sample_size;
1319 double reference_temperature = 0;
1323 reference_temperature += mean_arc_cost_for_class[cost_class] *
1324 num_vehicles_of_class[cost_class] /
1328 return {reference_temperature * 0.1, reference_temperature * 0.001};
bool remove_route_with_lowest_absences() const
StrategyCase strategy_case() const
const ::operations_research::AbsencesBasedAcceptanceStrategy & absences_based() const
const ::operations_research::SimulatedAnnealingAcceptanceStrategy & simulated_annealing() const
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
std::vector< RuinProcedure * > 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
static constexpr Value EXPONENTIAL
static constexpr Value LINEAR
static constexpr Value PARALLEL_SAVINGS
static constexpr Value LOCAL_CHEAPEST_INSERTION
static constexpr Value SEQUENTIAL_CHEAPEST_INSERTION
static constexpr Value LOCAL_CHEAPEST_COST_INSERTION
static constexpr Value SAVINGS
static constexpr Value PARALLEL_CHEAPEST_INSERTION
const ::operations_research::RuinRecreateParameters & ruin_recreate_parameters() const
::operations_research::PerturbationStrategy_Value perturbation_strategy() const
static constexpr Value RUIN_AND_RECREATE
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
int64_t Size() const
Returns the number of next variables in the model.
CostClassIndex GetCostClassIndexOfVehicle(int64_t vehicle) const
Get the cost class index of the given vehicle.
RoutingDisjunctionIndex DisjunctionIndex
int64_t GetArcCostForClass(int64_t from_index, int64_t to_index, int64_t cost_class_index) const
operations_research::IntVar * NextVar(int64_t index) const
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
int vehicles() const
Returns the number of vehicle routes in the model.
int64_t GetArcCostForVehicle(int64_t from_index, int64_t to_index, int64_t vehicle) const
operations_research::IntVar * VehicleVar(int64_t index) const
const ::operations_research::IteratedLocalSearchParameters & iterated_local_search_parameters() const
void RemovePerformedPickupDeliverySibling(int64_t node)
bool BelongsToInitializedRoute(int64_t node) const
void Reset(const Assignment *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)
bool CanBeRemoved(int64_t node) const
void RemoveNode(int64_t node)
int64_t GetInitializedPrevNodeIndex(int64_t node) const
int GetRouteSize(int vehicle) const
int64_t GetNextNodeIndex(int64_t node) const
Decision * Next(Solver *const solver) override
RuinAndRecreateDecisionBuilder(const Assignment *assignment, std::unique_ptr< RuinProcedure > ruin, std::unique_ptr< RoutingFilteredHeuristic > recreate)
static constexpr Value RUN_ALL_RANDOMLY
static constexpr Value RUN_ONE_RANDOMLY
RuinCompositionStrategy_Value Value
static constexpr Value RUN_ALL_SEQUENTIALLY
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
bool automatic_temperatures() const
double final_temperature() const
double initial_temperature() const
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
void Set(IntegerType index)
DecisionBuilder * MakeRuinAndRecreateDecisionBuilder(const RoutingSearchParameters ¶meters, RoutingModel *model, std::mt19937 *rnd, const Assignment *assignment, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
ClosedInterval::Iterator end(ClosedInterval interval)
std::pair< double, double > GetSimulatedAnnealingTemperatures(const RoutingModel &model, const SimulatedAnnealingAcceptanceStrategy &sa_params, std::mt19937 *rnd)
std::unique_ptr< NeighborAcceptanceCriterion > MakeNeighborAcceptanceCriterion(const RoutingModel &model, const AcceptanceStrategy &acceptance_strategy, const NeighborAcceptanceCriterion::SearchState &final_search_state, std::mt19937 *rnd)
DecisionBuilder * MakePerturbationDecisionBuilder(const RoutingSearchParameters ¶meters, RoutingModel *model, std::mt19937 *rnd, const Assignment *assignment, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...