Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
routing_ils.cc
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <cstddef>
17#include <cstdint>
18#include <functional>
19#include <memory>
20#include <utility>
21#include <vector>
22
23#include "absl/functional/bind_front.h"
24#include "absl/log/check.h"
28#include "ortools/constraint_solver/routing_ils.pb.h"
29#include "ortools/constraint_solver/routing_parameters.pb.h"
32
33namespace operations_research {
34namespace {
35
36GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
37MakeGlobalCheapestInsertionParameters(
38 const RoutingSearchParameters& search_parameters, bool is_sequential) {
39 GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
40 gci_parameters;
41 gci_parameters.is_sequential = is_sequential;
42 gci_parameters.farthest_seeds_ratio =
43 search_parameters.cheapest_insertion_farthest_seeds_ratio();
44 gci_parameters.neighbors_ratio =
45 search_parameters.cheapest_insertion_first_solution_neighbors_ratio();
46 gci_parameters.min_neighbors =
47 search_parameters.cheapest_insertion_first_solution_min_neighbors();
48 gci_parameters.use_neighbors_ratio_for_initialization =
49 search_parameters
50 .cheapest_insertion_first_solution_use_neighbors_ratio_for_initialization(); // NOLINT
51 gci_parameters.add_unperformed_entries =
52 search_parameters.cheapest_insertion_add_unperformed_entries();
53 return gci_parameters;
54}
55
56// Returns a ruin procedure based to the given parameters.
57std::unique_ptr<RuinProcedure> MakeRuinProcedure(
58 const RuinRecreateParameters& parameters, RoutingModel* model) {
59 switch (parameters.ruin_strategy()) {
60 case RuinStrategy::UNSET:
61 LOG(ERROR) << "Unset ruin procedure, using "
62 "RuinStrategy::SPATIALLY_CLOSE_ROUTES_REMOVAL.";
63 [[fallthrough]];
64 case RuinStrategy::SPATIALLY_CLOSE_ROUTES_REMOVAL:
65 return std::make_unique<CloseRoutesRemovalRuinProcedure>(
66 model, parameters.num_ruined_routes());
67 break;
68 default:
69 LOG(ERROR) << "Unsupported ruin procedure.";
70 return nullptr;
71 }
72}
73
74// Returns a recreate procedure based on the given parameters.
75std::unique_ptr<RoutingFilteredHeuristic> MakeRecreateProcedure(
76 const RoutingSearchParameters& parameters, RoutingModel* model,
77 std::function<bool()> stop_search,
78 LocalSearchFilterManager* filter_manager) {
79 switch (parameters.iterated_local_search_parameters()
80 .ruin_recreate_parameters()
81 .recreate_strategy()) {
82 case FirstSolutionStrategy::UNSET:
83 LOG(ERROR) << "Unset recreate procedure, using "
84 "FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION";
85 [[fallthrough]];
86 case FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION:
87 return std::make_unique<LocalCheapestInsertionFilteredHeuristic>(
88 model, std::move(stop_search),
89 absl::bind_front(&RoutingModel::GetArcCostForVehicle, model),
90 parameters.local_cheapest_cost_insertion_pickup_delivery_strategy(),
91 filter_manager, model->GetBinCapacities());
92 case FirstSolutionStrategy::LOCAL_CHEAPEST_COST_INSERTION:
93 return std::make_unique<LocalCheapestInsertionFilteredHeuristic>(
94 model, std::move(stop_search),
95 /*evaluator=*/nullptr,
96 parameters.local_cheapest_cost_insertion_pickup_delivery_strategy(),
97 filter_manager, model->GetBinCapacities());
98 case FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION: {
99 GlobalCheapestInsertionFilteredHeuristic::
100 GlobalCheapestInsertionParameters gci_parameters =
101 MakeGlobalCheapestInsertionParameters(parameters,
102 /*is_sequential=*/true);
103 return std::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
104 model, std::move(stop_search),
105 absl::bind_front(&RoutingModel::GetArcCostForVehicle, model),
106 [model](int64_t i) { return model->UnperformedPenaltyOrValue(0, i); },
107 filter_manager, gci_parameters);
108 }
109 case FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION: {
110 GlobalCheapestInsertionFilteredHeuristic::
111 GlobalCheapestInsertionParameters gci_parameters =
112 MakeGlobalCheapestInsertionParameters(parameters,
113 /*is_sequential=*/false);
114 return std::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
115 model, std::move(stop_search),
116 absl::bind_front(&RoutingModel::GetArcCostForVehicle, model),
117 [model](int64_t i) { return model->UnperformedPenaltyOrValue(0, i); },
118 filter_manager, gci_parameters);
119 }
120 default:
121 LOG(ERROR) << "Unsupported recreate procedure.";
122 return nullptr;
123 }
124
125 return nullptr;
126}
127
128// Greedy criterion in which the reference assignment is only replaced by an
129// improving candidate assignment.
130class GreedyDescentAcceptanceCriterion : public NeighborAcceptanceCriterion {
131 public:
132 bool Accept(const Assignment* candidate,
133 const Assignment* reference) const override {
134 return candidate->ObjectiveValue() < reference->ObjectiveValue();
135 }
136};
137
138} // namespace
139
141 RoutingModel* model, size_t num_routes)
142 : model_(*model),
143 neighbors_manager_(model->GetOrCreateNodeNeighborsByCostClass(
144 /*TODO(user): use a parameter*/ 100,
145 /*add_vehicle_starts_to_neighbors=*/false)),
146 num_routes_(num_routes),
147 rnd_(/*fixed seed=*/0),
148 customer_dist_(0, model->Size() - 1),
149 removed_routes_(model->vehicles()) {}
150
151std::function<int64_t(int64_t)> CloseRoutesRemovalRuinProcedure::Ruin(
152 const Assignment* assignment) {
153 removed_routes_.SparseClearAll();
154
155 if (num_routes_ > 0 && HasPerformedNodes(assignment)) {
156 int64_t seed_node;
157 int seed_route = -1;
158 do {
159 seed_node = customer_dist_(rnd_);
160 seed_route = assignment->Value(model_.VehicleVar(seed_node));
161 } while (model_.IsStart(seed_node) || seed_route == -1);
162 DCHECK(!model_.IsEnd(seed_node));
163
164 const RoutingCostClassIndex cost_class_index =
165 model_.GetCostClassIndexOfVehicle(seed_route);
166
167 const std::vector<int>& neighbors =
168 neighbors_manager_->GetNeighborsOfNodeForCostClass(
169 cost_class_index.value(), seed_node);
170
171 for (int neighbor : neighbors) {
172 const int64_t route = assignment->Value(model_.VehicleVar(neighbor));
173 if (route < 0 || removed_routes_[route]) {
174 continue;
175 }
176 removed_routes_.Set(route);
177 if (removed_routes_.NumberOfSetCallsWithDifferentArguments() ==
178 num_routes_) {
179 break;
180 }
181 }
182 }
183
184 return [this, assignment](int64_t node) {
185 // Shortcut removed routes to remove associated customers.
186 if (model_.IsStart(node)) {
187 const int route = assignment->Value(model_.VehicleVar(node));
188 if (removed_routes_[route]) {
189 return model_.End(route);
190 }
191 }
192 return assignment->Value(model_.NextVar(node));
193 };
194}
195
196bool CloseRoutesRemovalRuinProcedure::HasPerformedNodes(
197 const Assignment* assignment) {
198 for (int v = 0; v < model_.vehicles(); ++v) {
199 if (model_.Next(*assignment, model_.Start(v)) != model_.End(v)) {
200 return true;
201 }
202 }
203 return false;
204}
205
207 public:
209 const Assignment* assignment, std::unique_ptr<RuinProcedure> ruin,
210 std::unique_ptr<RoutingFilteredHeuristic> recreate)
211 : assignment_(*assignment),
212 ruin_(std::move(ruin)),
213 recreate_(std::move(recreate)) {}
214
215 Decision* Next(Solver* const solver) override {
216 Assignment* const new_assignment = Recreate(ruin_->Ruin(&assignment_));
217 if (new_assignment) {
218 new_assignment->Restore();
219 } else {
220 solver->Fail();
221 }
222 return nullptr;
223 }
224
225 private:
226 Assignment* Recreate(const std::function<int64_t(int64_t)>& next_accessor) {
227 return recreate_->BuildSolutionFromRoutes(next_accessor);
228 }
229
230 const Assignment& assignment_;
231 std::unique_ptr<RuinProcedure> ruin_;
232 std::unique_ptr<RoutingFilteredHeuristic> recreate_;
233};
234
236 const RoutingSearchParameters& parameters, RoutingModel* model,
237 const Assignment* assignment, std::function<bool()> stop_search,
238 LocalSearchFilterManager* filter_manager) {
239 std::unique_ptr<RuinProcedure> ruin = MakeRuinProcedure(
240 parameters.iterated_local_search_parameters().ruin_recreate_parameters(),
241 model);
242
243 std::unique_ptr<RoutingFilteredHeuristic> recreate = MakeRecreateProcedure(
244 parameters, model, std::move(stop_search), filter_manager);
245
246 return model->solver()->RevAlloc(new RuinAndRecreateDecisionBuilder(
247 assignment, std::move(ruin), std::move(recreate)));
248}
249
251 const RoutingSearchParameters& parameters, RoutingModel* model,
252 const Assignment* assignment, std::function<bool()> stop_search,
253 LocalSearchFilterManager* filter_manager) {
254 switch (
255 parameters.iterated_local_search_parameters().perturbation_strategy()) {
256 case PerturbationStrategy::UNSET:
257 LOG(ERROR) << "Unset perturbation strategy, using "
258 "PerturbationStrategy::RUIN_AND_RECREATE.";
259 [[fallthrough]];
260 case PerturbationStrategy::RUIN_AND_RECREATE:
262 std::move(stop_search),
263 filter_manager);
264 default:
265 LOG(ERROR) << "Unsupported perturbation strategy.";
266 return nullptr;
267 }
268}
269
270std::unique_ptr<NeighborAcceptanceCriterion> MakeNeighborAcceptanceCriterion(
271 const RoutingSearchParameters& parameters) {
272 CHECK(parameters.has_iterated_local_search_parameters());
273 switch (parameters.iterated_local_search_parameters().acceptance_strategy()) {
274 case AcceptanceStrategy::UNSET:
275 LOG(ERROR) << "Unset acceptance strategy, using "
276 "AcceptanceStrategy::GREEDY_DESCENT.";
277 [[fallthrough]];
278 case AcceptanceStrategy::GREEDY_DESCENT:
279 return std::make_unique<GreedyDescentAcceptanceCriterion>();
280 default:
281 LOG(ERROR) << "Unsupported acceptance strategy.";
282 return nullptr;
283 }
284}
285
286} // namespace operations_research
int64_t Value(const IntVar *var) const
CloseRoutesRemovalRuinProcedure(RoutingModel *model, size_t num_routes)
std::function< int64_t(int64_t)> Ruin(const Assignment *assignment) override
Decision * Next(Solver *const solver) override
RuinAndRecreateDecisionBuilder(const Assignment *assignment, std::unique_ptr< RuinProcedure > ruin, std::unique_ptr< RoutingFilteredHeuristic > recreate)
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)
Definition bitset.h:864
int NumberOfSetCallsWithDifferentArguments() const
Definition bitset.h:875
SatParameters parameters
GRBmodel * model
In SWIG mode, we don't want anything besides these top-level includes.
std::unique_ptr< NeighborAcceptanceCriterion > MakeNeighborAcceptanceCriterion(const RoutingSearchParameters &parameters)
Returns a neighbor acceptance criterion based on the given parameters.
DecisionBuilder * MakeRuinAndRecreateDecisionBuilder(const RoutingSearchParameters &parameters, RoutingModel *model, const Assignment *assignment, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
DecisionBuilder * MakePerturbationDecisionBuilder(const RoutingSearchParameters &parameters, RoutingModel *model, const Assignment *assignment, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
STL namespace.
RoutingModel::CostClassIndex cost_class_index
The cost class of the vehicle.
Definition routing.cc:1336