OR-Tools  9.3
routing_parameters.cc
Go to the documentation of this file.
1// Copyright 2010-2021 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 <cstdint>
17#include <string>
18
19#include "absl/strings/str_cat.h"
20#include "absl/time/time.h"
21#include "google/protobuf/descriptor.h"
22#include "google/protobuf/duration.pb.h"
23#include "google/protobuf/message.h"
24#include "google/protobuf/text_format.h"
28#include "ortools/constraint_solver/routing_enums.pb.h"
29#include "ortools/constraint_solver/solver_parameters.pb.h"
30#include "ortools/sat/sat_parameters.pb.h"
31#include "ortools/util/optional_boolean.pb.h"
32
33namespace operations_research {
34
35RoutingModelParameters DefaultRoutingModelParameters() {
36 RoutingModelParameters parameters;
37 ConstraintSolverParameters* const solver_parameters =
38 parameters.mutable_solver_parameters();
39 *solver_parameters = Solver::DefaultSolverParameters();
40 solver_parameters->set_compress_trail(
41 ConstraintSolverParameters::COMPRESS_WITH_ZLIB);
42 solver_parameters->set_skip_locally_optimal_paths(true);
43 parameters.set_reduce_vehicle_cost_model(true);
44 return parameters;
45}
46
47// static
48RoutingSearchParameters DefaultRoutingSearchParameters() {
49 static const char* const kSearchParameters =
50 "first_solution_strategy: AUTOMATIC "
51 "use_unfiltered_first_solution_strategy: false "
52 "savings_neighbors_ratio: 1 "
53 "savings_max_memory_usage_bytes: 6e9 "
54 "savings_add_reverse_arcs: false "
55 "savings_arc_coefficient: 1 "
56 "savings_parallel_routes: false "
57 "cheapest_insertion_farthest_seeds_ratio: 0 "
58 "cheapest_insertion_first_solution_neighbors_ratio: 1 "
59 "cheapest_insertion_first_solution_min_neighbors: 1 "
60 "cheapest_insertion_ls_operator_neighbors_ratio: 1 "
61 "cheapest_insertion_ls_operator_min_neighbors: 1 "
62 "cheapest_insertion_first_solution_use_neighbors_ratio_for_"
63 "initialization: false "
64 "cheapest_insertion_add_unperformed_entries: false "
65 "local_cheapest_insertion_evaluate_pickup_delivery_costs_independently: "
66 "true "
67 "local_search_operators {"
68 " use_relocate: BOOL_TRUE"
69 " use_relocate_pair: BOOL_TRUE"
70 " use_light_relocate_pair: BOOL_TRUE"
71 " use_relocate_subtrip: BOOL_TRUE"
72 " use_relocate_neighbors: BOOL_FALSE"
73 " use_exchange: BOOL_TRUE"
74 " use_exchange_pair: BOOL_TRUE"
75 " use_exchange_subtrip: BOOL_TRUE"
76 " use_cross: BOOL_TRUE"
77 " use_cross_exchange: BOOL_FALSE"
78 " use_relocate_expensive_chain: BOOL_TRUE"
79 " use_two_opt: BOOL_TRUE"
80 " use_or_opt: BOOL_TRUE"
81 " use_lin_kernighan: BOOL_TRUE"
82 " use_tsp_opt: BOOL_FALSE"
83 " use_make_active: BOOL_TRUE"
84 " use_relocate_and_make_active: BOOL_FALSE" // costly if true by default
85 " use_make_inactive: BOOL_TRUE"
86 " use_make_chain_inactive: BOOL_FALSE"
87 " use_swap_active: BOOL_TRUE"
88 " use_extended_swap_active: BOOL_FALSE"
89 " use_node_pair_swap_active: BOOL_TRUE"
90 " use_path_lns: BOOL_FALSE"
91 " use_full_path_lns: BOOL_FALSE"
92 " use_tsp_lns: BOOL_FALSE"
93 " use_inactive_lns: BOOL_FALSE"
94 " use_global_cheapest_insertion_path_lns: BOOL_TRUE"
95 " use_local_cheapest_insertion_path_lns: BOOL_TRUE"
96 " use_relocate_path_global_cheapest_insertion_insert_unperformed: "
97 "BOOL_TRUE"
98 " use_global_cheapest_insertion_expensive_chain_lns: BOOL_FALSE"
99 " use_local_cheapest_insertion_expensive_chain_lns: BOOL_FALSE"
100 " use_global_cheapest_insertion_close_nodes_lns: BOOL_FALSE"
101 " use_local_cheapest_insertion_close_nodes_lns: BOOL_FALSE"
102 "}"
103 "use_multi_armed_bandit_concatenate_operators: false "
104 "multi_armed_bandit_compound_operator_memory_coefficient: 0.04 "
105 "multi_armed_bandit_compound_operator_exploration_coefficient: 1e12 "
106 "relocate_expensive_chain_num_arcs_to_consider: 4 "
107 "heuristic_expensive_chain_lns_num_arcs_to_consider: 4 "
108 "heuristic_close_nodes_lns_num_nodes: 5 "
109 "local_search_metaheuristic: AUTOMATIC "
110 "guided_local_search_lambda_coefficient: 0.1 "
111 "use_depth_first_search: false "
112 "use_cp: BOOL_TRUE "
113 "use_cp_sat: BOOL_FALSE "
114 "use_generalized_cp_sat: BOOL_FALSE "
115 "sat_parameters { linearization_level: 2 num_search_workers: 1 } "
116 "continuous_scheduling_solver: SCHEDULING_GLOP "
117 "mixed_integer_scheduling_solver: SCHEDULING_CP_SAT "
118 "disable_scheduling_beware_this_may_degrade_performance: false "
119 "optimization_step: 0.0 "
120 "number_of_solutions_to_collect: 1 "
121 // No "time_limit" by default.
122 "solution_limit: 0x7fffffffffffffff " // kint64max
123 "lns_time_limit: { seconds:0 nanos:100000000 } " // 0.1s
124 "use_full_propagation: false "
125 "log_search: false "
126 "log_cost_scaling_factor: 1.0 "
127 "log_cost_offset: 0.0";
128 RoutingSearchParameters parameters;
129 if (!google::protobuf::TextFormat::ParseFromString(kSearchParameters,
130 &parameters)) {
131 LOG(DFATAL) << "Unsupported default search parameters: "
132 << kSearchParameters;
133 }
134 const std::string error = FindErrorInRoutingSearchParameters(parameters);
135 LOG_IF(DFATAL, !error.empty())
136 << "The default search parameters aren't valid: " << error;
137 return parameters;
138}
139
140namespace {
141bool IsValidNonNegativeDuration(const google::protobuf::Duration& d) {
142 const auto status_or_duration = util_time::DecodeGoogleApiProto(d);
143 return status_or_duration.ok() &&
144 status_or_duration.value() >= absl::ZeroDuration();
145}
146} // namespace
147
149 const RoutingSearchParameters& search_parameters) {
150 using absl::StrCat;
151 // Check that all local search operators are set to either BOOL_TRUE or
152 // BOOL_FALSE (and not BOOL_UNSPECIFIED).
153 {
154 using Reflection = google::protobuf::Reflection;
155 using Descriptor = google::protobuf::Descriptor;
156 using FieldDescriptor = google::protobuf::FieldDescriptor;
157 const RoutingSearchParameters::LocalSearchNeighborhoodOperators& operators =
158 search_parameters.local_search_operators();
159 const Reflection* ls_reflection = operators.GetReflection();
160 const Descriptor* ls_descriptor = operators.GetDescriptor();
161 for (int /*this is NOT the field's tag number*/ field_index = 0;
162 field_index < ls_descriptor->field_count(); ++field_index) {
163 const FieldDescriptor* field = ls_descriptor->field(field_index);
164 if (field->type() != FieldDescriptor::TYPE_ENUM ||
165 field->enum_type() != OptionalBoolean_descriptor()) {
166 DLOG(FATAL)
167 << "In RoutingSearchParameters::LocalSearchNeighborhoodOperators,"
168 << " field '" << field->name() << "' is not an OptionalBoolean.";
169 return "The file 'routing_search_parameters.proto' itself is invalid!";
170 }
171 const int value = ls_reflection->GetEnum(operators, field)->number();
172 if (!OptionalBoolean_IsValid(value) || value == 0) {
173 return StrCat("local_search_neighborhood_operator.", field->name(),
174 " should be set to BOOL_TRUE or BOOL_FALSE instead of ",
175 OptionalBoolean_Name(static_cast<OptionalBoolean>(value)),
176 " (value: ", value, ")");
177 }
178 }
179 }
180 {
181 const double ratio = search_parameters.savings_neighbors_ratio();
182 if (std::isnan(ratio) || ratio <= 0 || ratio > 1) {
183 return StrCat("Invalid savings_neighbors_ratio:", ratio);
184 }
185 }
186 {
187 const double max_memory =
188 search_parameters.savings_max_memory_usage_bytes();
189 if (std::isnan(max_memory) || max_memory <= 0 || max_memory > 1e10) {
190 return StrCat("Invalid savings_max_memory_usage_bytes: ", max_memory);
191 }
192 }
193 {
194 const double coefficient = search_parameters.savings_arc_coefficient();
195 if (std::isnan(coefficient) || coefficient <= 0 ||
196 std::isinf(coefficient)) {
197 return StrCat("Invalid savings_arc_coefficient:", coefficient);
198 }
199 }
200 {
201 const double ratio =
202 search_parameters.cheapest_insertion_farthest_seeds_ratio();
203 if (std::isnan(ratio) || ratio < 0 || ratio > 1) {
204 return StrCat("Invalid cheapest_insertion_farthest_seeds_ratio:", ratio);
205 }
206 }
207 {
208 const double ratio =
209 search_parameters.cheapest_insertion_first_solution_neighbors_ratio();
210 if (std::isnan(ratio) || ratio <= 0 || ratio > 1) {
211 return StrCat(
212 "Invalid cheapest_insertion_first_solution_neighbors_ratio: ", ratio);
213 }
214 }
215 {
216 const int32_t min_neighbors =
217 search_parameters.cheapest_insertion_first_solution_min_neighbors();
218 if (min_neighbors < 1) {
219 return StrCat("Invalid cheapest_insertion_first_solution_min_neighbors: ",
220 min_neighbors, ". Must be greater or equal to 1.");
221 }
222 }
223 {
224 const double ratio =
225 search_parameters.cheapest_insertion_ls_operator_neighbors_ratio();
226 if (std::isnan(ratio) || ratio <= 0 || ratio > 1) {
227 return StrCat("Invalid cheapest_insertion_ls_operator_neighbors_ratio: ",
228 ratio);
229 }
230 }
231 {
232 const int32_t min_neighbors =
233 search_parameters.cheapest_insertion_ls_operator_min_neighbors();
234 if (min_neighbors < 1) {
235 return StrCat("Invalid cheapest_insertion_ls_operator_min_neighbors: ",
236 min_neighbors, ". Must be greater or equal to 1.");
237 }
238 }
239 {
240 const int32_t num_arcs =
241 search_parameters.relocate_expensive_chain_num_arcs_to_consider();
242 if (num_arcs < 2 || num_arcs > 1e6) {
243 return StrCat("Invalid relocate_expensive_chain_num_arcs_to_consider: ",
244 num_arcs, ". Must be between 2 and 10^6 (included).");
245 }
246 }
247 {
248 const int32_t num_arcs =
249 search_parameters.heuristic_expensive_chain_lns_num_arcs_to_consider();
250 if (num_arcs < 2 || num_arcs > 1e6) {
251 return StrCat(
252 "Invalid heuristic_expensive_chain_lns_num_arcs_to_consider: ",
253 num_arcs, ". Must be between 2 and 10^6 (included).");
254 }
255 }
256 {
257 const int32_t num_nodes =
258 search_parameters.heuristic_close_nodes_lns_num_nodes();
259 if (num_nodes < 0 || num_nodes > 1e4) {
260 return StrCat("Invalid heuristic_close_nodes_lns_num_nodes: ", num_nodes,
261 ". Must be between 0 and 10000 (included).");
262 }
263 }
264 {
265 const double gls_coefficient =
266 search_parameters.guided_local_search_lambda_coefficient();
267 if (std::isnan(gls_coefficient) || gls_coefficient < 0 ||
268 std::isinf(gls_coefficient)) {
269 return StrCat("Invalid guided_local_search_lambda_coefficient: ",
270 gls_coefficient);
271 }
272 }
273 {
274 const double step = search_parameters.optimization_step();
275 if (std::isnan(step) || step < 0.0) {
276 return StrCat("Invalid optimization_step: ", step);
277 }
278 }
279 {
280 const int32_t num = search_parameters.number_of_solutions_to_collect();
281 if (num < 1) return StrCat("Invalid number_of_solutions_to_collect:", num);
282 }
283 {
284 const int64_t lim = search_parameters.solution_limit();
285 if (lim < 1) return StrCat("Invalid solution_limit:", lim);
286 }
287 if (!IsValidNonNegativeDuration(search_parameters.time_limit())) {
288 return "Invalid time_limit: " +
289 search_parameters.time_limit().ShortDebugString();
290 }
291 if (!IsValidNonNegativeDuration(search_parameters.lns_time_limit())) {
292 return "Invalid lns_time_limit: " +
293 search_parameters.lns_time_limit().ShortDebugString();
294 }
295 if (!FirstSolutionStrategy::Value_IsValid(
296 search_parameters.first_solution_strategy())) {
297 return StrCat("Invalid first_solution_strategy: ",
298 search_parameters.first_solution_strategy());
299 }
300 if (!LocalSearchMetaheuristic::Value_IsValid(
301 search_parameters.local_search_metaheuristic())) {
302 return StrCat("Invalid metaheuristic: ",
303 search_parameters.local_search_metaheuristic());
304 }
305
306 const double scaling_factor = search_parameters.log_cost_scaling_factor();
307 if (scaling_factor == 0 || std::isnan(scaling_factor) ||
308 std::isinf(scaling_factor)) {
309 return StrCat("Invalid value for log_cost_scaling_factor: ",
310 scaling_factor);
311 }
312 const double offset = search_parameters.log_cost_offset();
313 if (std::isnan(offset) || std::isinf(offset)) {
314 return StrCat("Invalid value for log_cost_offset: ", offset);
315 }
316 const RoutingSearchParameters::SchedulingSolver continuous_scheduling_solver =
317 search_parameters.continuous_scheduling_solver();
318 if (continuous_scheduling_solver ==
319 RoutingSearchParameters::SCHEDULING_UNSET ||
320 continuous_scheduling_solver ==
321 RoutingSearchParameters::SCHEDULING_CP_SAT) {
322 return StrCat("Invalid value for continuous_scheduling_solver: ",
323 RoutingSearchParameters::SchedulingSolver_Name(
324 continuous_scheduling_solver));
325 }
326 const RoutingSearchParameters::SchedulingSolver
327 mixed_integer_scheduling_solver =
328 search_parameters.mixed_integer_scheduling_solver();
329 if (mixed_integer_scheduling_solver ==
330 RoutingSearchParameters::SCHEDULING_UNSET) {
331 return StrCat("Invalid value for mixed_integer_scheduling_solver: ",
332 RoutingSearchParameters::SchedulingSolver_Name(
333 mixed_integer_scheduling_solver));
334 }
335
336 if (search_parameters.has_improvement_limit_parameters()) {
337 const double improvement_rate_coefficient =
338 search_parameters.improvement_limit_parameters()
339 .improvement_rate_coefficient();
340 if (std::isnan(improvement_rate_coefficient) ||
341 improvement_rate_coefficient <= 0) {
342 return StrCat(
343 "Invalid value for "
344 "improvement_limit_parameters.improvement_rate_coefficient: ",
345 improvement_rate_coefficient);
346 }
347
348 const int32_t improvement_rate_solutions_distance =
349 search_parameters.improvement_limit_parameters()
350 .improvement_rate_solutions_distance();
351 if (improvement_rate_solutions_distance <= 0) {
352 return StrCat(
353 "Invalid value for "
354 "improvement_limit_parameters.improvement_rate_solutions_distance: ",
355 improvement_rate_solutions_distance);
356 }
357 }
358
359 {
360 const double memory_coefficient =
361 search_parameters
362 .multi_armed_bandit_compound_operator_memory_coefficient();
363 if (std::isnan(memory_coefficient) || memory_coefficient < 0 ||
364 memory_coefficient > 1) {
365 return StrCat(
366 "Invalid value for "
367 "multi_armed_bandit_compound_operator_memory_coefficient: ",
368 memory_coefficient);
369 }
370 }
371 {
372 const double exploration_coefficient =
373 search_parameters
374 .multi_armed_bandit_compound_operator_exploration_coefficient();
375 if (std::isnan(exploration_coefficient) || exploration_coefficient < 0) {
376 return StrCat(
377 "Invalid value for "
378 "multi_armed_bandit_compound_operator_exploration_coefficient: ",
379 exploration_coefficient);
380 }
381 }
382
383 {
384 const sat::SatParameters& sat_parameters =
385 search_parameters.sat_parameters();
386 if (sat_parameters.enumerate_all_solutions() &&
387 (sat_parameters.num_search_workers() > 1 ||
388 sat_parameters.interleave_search())) {
389 return "sat_parameters.enumerate_all_solutions cannot be true in parallel"
390 " search";
391 }
392 }
393
394 return ""; // = Valid (No error).
395}
396
397} // namespace operations_research
#define LOG_IF(severity, condition)
Definition: base/logging.h:479
#define DLOG(severity)
Definition: base/logging.h:881
#define LOG(severity)
Definition: base/logging.h:420
static ConstraintSolverParameters DefaultSolverParameters()
Create a ConstraintSolverParameters proto with all the default values.
SatParameters parameters
int64_t value
const int FATAL
Definition: log_severity.h:32
Collection of objects used to extend the Constraint Solver library.
RoutingModelParameters DefaultRoutingModelParameters()
std::string FindErrorInRoutingSearchParameters(const RoutingSearchParameters &search_parameters)
Returns an empty std::string if the routing search parameters are valid, and a non-empty,...
RoutingSearchParameters DefaultRoutingSearchParameters()
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
Definition: protoutil.h:42
Fractional ratio
int64_t coefficient