21 #include "absl/strings/str_cat.h"
22 #include "absl/strings/str_format.h"
23 #include "absl/time/time.h"
24 #include "google/protobuf/descriptor.h"
25 #include "google/protobuf/duration.pb.h"
26 #include "google/protobuf/message.h"
27 #include "google/protobuf/text_format.h"
31 #include "ortools/constraint_solver/routing_enums.pb.h"
32 #include "ortools/constraint_solver/solver_parameters.pb.h"
33 #include "ortools/sat/sat_parameters.pb.h"
34 #include "ortools/util/optional_boolean.pb.h"
41 ConstraintSolverParameters*
const solver_parameters =
44 solver_parameters->set_compress_trail(
45 ConstraintSolverParameters::COMPRESS_WITH_ZLIB);
46 solver_parameters->set_skip_locally_optimal_paths(
true);
47 parameters.set_reduce_vehicle_cost_model(
true);
52 RoutingSearchParameters CreateDefaultRoutingSearchParameters() {
53 RoutingSearchParameters p;
54 p.set_first_solution_strategy(FirstSolutionStrategy::AUTOMATIC);
55 p.set_use_unfiltered_first_solution_strategy(
false);
56 p.set_savings_neighbors_ratio(1);
57 p.set_savings_max_memory_usage_bytes(6e9);
58 p.set_savings_add_reverse_arcs(
false);
59 p.set_savings_arc_coefficient(1);
60 p.set_savings_parallel_routes(
false);
61 p.set_cheapest_insertion_farthest_seeds_ratio(0);
62 p.set_cheapest_insertion_first_solution_neighbors_ratio(1);
63 p.set_cheapest_insertion_first_solution_min_neighbors(1);
64 p.set_cheapest_insertion_ls_operator_neighbors_ratio(1);
65 p.set_cheapest_insertion_ls_operator_min_neighbors(1);
66 p.set_cheapest_insertion_first_solution_use_neighbors_ratio_for_initialization(
68 p.set_cheapest_insertion_add_unperformed_entries(
false);
69 p.set_local_cheapest_insertion_pickup_delivery_strategy(
70 RoutingSearchParameters::BEST_PICKUP_THEN_BEST_DELIVERY);
71 RoutingSearchParameters::LocalSearchNeighborhoodOperators* o =
72 p.mutable_local_search_operators();
73 o->set_use_relocate(BOOL_TRUE);
74 o->set_use_relocate_pair(BOOL_TRUE);
75 o->set_use_light_relocate_pair(BOOL_TRUE);
76 o->set_use_relocate_subtrip(BOOL_TRUE);
77 o->set_use_relocate_neighbors(BOOL_FALSE);
78 o->set_use_exchange(BOOL_TRUE);
79 o->set_use_exchange_pair(BOOL_TRUE);
80 o->set_use_exchange_subtrip(BOOL_TRUE);
81 o->set_use_cross(BOOL_TRUE);
82 o->set_use_cross_exchange(BOOL_FALSE);
83 o->set_use_relocate_expensive_chain(BOOL_TRUE);
84 o->set_use_two_opt(BOOL_TRUE);
85 o->set_use_or_opt(BOOL_TRUE);
86 o->set_use_lin_kernighan(BOOL_TRUE);
87 o->set_use_tsp_opt(BOOL_FALSE);
88 o->set_use_make_active(BOOL_TRUE);
89 o->set_use_relocate_and_make_active(BOOL_FALSE);
90 o->set_use_make_inactive(BOOL_TRUE);
91 o->set_use_make_chain_inactive(BOOL_TRUE);
92 o->set_use_swap_active(BOOL_TRUE);
93 o->set_use_extended_swap_active(BOOL_FALSE);
94 o->set_use_shortest_path_swap_active(BOOL_TRUE);
95 o->set_use_node_pair_swap_active(BOOL_FALSE);
96 o->set_use_path_lns(BOOL_FALSE);
97 o->set_use_full_path_lns(BOOL_FALSE);
98 o->set_use_tsp_lns(BOOL_FALSE);
99 o->set_use_inactive_lns(BOOL_FALSE);
100 o->set_use_global_cheapest_insertion_path_lns(BOOL_TRUE);
101 o->set_use_local_cheapest_insertion_path_lns(BOOL_TRUE);
102 o->set_use_relocate_path_global_cheapest_insertion_insert_unperformed(
104 o->set_use_global_cheapest_insertion_expensive_chain_lns(BOOL_FALSE);
105 o->set_use_local_cheapest_insertion_expensive_chain_lns(BOOL_FALSE);
106 o->set_use_global_cheapest_insertion_close_nodes_lns(BOOL_FALSE);
107 o->set_use_local_cheapest_insertion_close_nodes_lns(BOOL_FALSE);
108 p.set_use_multi_armed_bandit_concatenate_operators(
false);
109 p.set_multi_armed_bandit_compound_operator_memory_coefficient(0.04);
110 p.set_multi_armed_bandit_compound_operator_exploration_coefficient(1e12);
111 p.set_relocate_expensive_chain_num_arcs_to_consider(4);
112 p.set_heuristic_expensive_chain_lns_num_arcs_to_consider(4);
113 p.set_heuristic_close_nodes_lns_num_nodes(5);
114 p.set_local_search_metaheuristic(LocalSearchMetaheuristic::AUTOMATIC);
115 p.set_guided_local_search_lambda_coefficient(0.1);
116 p.set_guided_local_search_reset_penalties_on_new_best_solution(
false);
117 p.set_use_depth_first_search(
false);
118 p.set_use_cp(BOOL_TRUE);
119 p.set_use_cp_sat(BOOL_FALSE);
120 p.set_use_generalized_cp_sat(BOOL_FALSE);
121 p.mutable_sat_parameters()->set_linearization_level(2);
122 p.mutable_sat_parameters()->set_num_search_workers(1);
123 p.set_fallback_to_cp_sat_size_threshold(20);
124 p.set_continuous_scheduling_solver(RoutingSearchParameters::SCHEDULING_GLOP);
125 p.set_mixed_integer_scheduling_solver(
126 RoutingSearchParameters::SCHEDULING_CP_SAT);
127 p.set_disable_scheduling_beware_this_may_degrade_performance(
false);
128 p.set_optimization_step(0.0);
129 p.set_number_of_solutions_to_collect(1);
132 p.mutable_lns_time_limit()->set_nanos(100000000);
133 p.set_use_full_propagation(
false);
134 p.set_log_search(
false);
135 p.set_log_cost_scaling_factor(1.0);
136 p.set_log_cost_offset(0.0);
139 LOG_IF(DFATAL, !error.empty())
140 <<
"The default search parameters aren't valid: " << error;
147 static const auto* default_parameters =
148 new RoutingSearchParameters(CreateDefaultRoutingSearchParameters());
149 return *default_parameters;
153 bool IsValidNonNegativeDuration(
const google::protobuf::Duration& d) {
155 return status_or_duration.ok() &&
156 status_or_duration.value() >= absl::ZeroDuration();
161 const RoutingSearchParameters& search_parameters) {
162 const std::vector<std::string> errors =
164 return (errors.empty()) ?
"" : errors[0];
168 const RoutingSearchParameters& search_parameters) {
170 std::vector<std::string> errors;
175 #if !defined(__ANDROID__) && !defined(__wasm__)
177 using Reflection = google::protobuf::Reflection;
178 using Descriptor = google::protobuf::Descriptor;
179 using FieldDescriptor = google::protobuf::FieldDescriptor;
180 const RoutingSearchParameters::LocalSearchNeighborhoodOperators& operators =
181 search_parameters.local_search_operators();
182 const Reflection* ls_reflection = operators.GetReflection();
183 const Descriptor* ls_descriptor = operators.GetDescriptor();
184 for (
int field_index = 0;
185 field_index < ls_descriptor->field_count(); ++field_index) {
186 const FieldDescriptor* field = ls_descriptor->field(field_index);
187 if (field->type() != FieldDescriptor::TYPE_ENUM ||
188 field->enum_type() != OptionalBoolean_descriptor()) {
190 <<
"In RoutingSearchParameters::LocalSearchNeighborhoodOperators,"
191 <<
" field '" << field->name() <<
"' is not an OptionalBoolean.";
193 const int value = ls_reflection->GetEnum(operators, field)->number();
194 if (!OptionalBoolean_IsValid(
value) ||
value == 0) {
195 errors.emplace_back(absl::StrFormat(
196 "local_search_neighborhood_operator.%s should be set to "
197 "BOOL_TRUE or BOOL_FALSE instead of %s (value: %d)",
199 OptionalBoolean_Name(
static_cast<OptionalBoolean
>(
value)),
206 if (
const double ratio = search_parameters.savings_neighbors_ratio();
207 std::isnan(
ratio) || ratio <= 0 || ratio > 1) {
208 errors.emplace_back(StrCat(
"Invalid savings_neighbors_ratio: ",
ratio));
210 if (
const double max_memory =
211 search_parameters.savings_max_memory_usage_bytes();
212 std::isnan(max_memory) || max_memory <= 0 || max_memory > 1e10) {
214 StrCat(
"Invalid savings_max_memory_usage_bytes: ", max_memory));
216 if (
const double coefficient = search_parameters.savings_arc_coefficient();
219 StrCat(
"Invalid savings_arc_coefficient: ",
coefficient));
221 if (
const double ratio =
222 search_parameters.cheapest_insertion_farthest_seeds_ratio();
223 std::isnan(
ratio) || ratio < 0 || ratio > 1) {
225 StrCat(
"Invalid cheapest_insertion_farthest_seeds_ratio: ",
ratio));
227 if (
const double ratio =
228 search_parameters.cheapest_insertion_first_solution_neighbors_ratio();
229 std::isnan(
ratio) || ratio <= 0 || ratio > 1) {
230 errors.emplace_back(StrCat(
231 "Invalid cheapest_insertion_first_solution_neighbors_ratio: ",
ratio));
233 if (
const int32_t min_neighbors =
234 search_parameters.cheapest_insertion_first_solution_min_neighbors();
237 StrCat(
"Invalid cheapest_insertion_first_solution_min_neighbors: ",
238 min_neighbors,
". Must be greater or equal to 1."));
240 if (
const double ratio =
241 search_parameters.cheapest_insertion_ls_operator_neighbors_ratio();
242 std::isnan(
ratio) || ratio <= 0 || ratio > 1) {
243 errors.emplace_back(StrCat(
244 "Invalid cheapest_insertion_ls_operator_neighbors_ratio: ",
ratio));
246 if (
const int32_t min_neighbors =
247 search_parameters.cheapest_insertion_ls_operator_min_neighbors();
249 errors.emplace_back(StrCat(
250 "Invalid cheapest_insertion_ls_operator_min_neighbors: ", min_neighbors,
251 ". Must be greater or equal to 1."));
253 if (
const int32_t num_arcs =
254 search_parameters.relocate_expensive_chain_num_arcs_to_consider();
255 num_arcs < 2 || num_arcs > 1e6) {
256 errors.emplace_back(StrCat(
257 "Invalid relocate_expensive_chain_num_arcs_to_consider: ", num_arcs,
258 ". Must be between 2 and 10^6 (included)."));
260 if (
const int32_t num_arcs =
262 .heuristic_expensive_chain_lns_num_arcs_to_consider();
263 num_arcs < 2 || num_arcs > 1e6) {
265 StrCat(
"Invalid heuristic_expensive_chain_lns_num_arcs_to_consider: ",
266 num_arcs,
". Must be between 2 and 10^6 (included)."));
268 if (
const int32_t num_nodes =
269 search_parameters.heuristic_close_nodes_lns_num_nodes();
270 num_nodes < 0 || num_nodes > 1e4) {
272 StrCat(
"Invalid heuristic_close_nodes_lns_num_nodes: ", num_nodes,
273 ". Must be between 0 and 10000 (included)."));
275 if (
const double gls_coefficient =
276 search_parameters.guided_local_search_lambda_coefficient();
277 std::isnan(gls_coefficient) || gls_coefficient < 0 ||
278 std::isinf(gls_coefficient)) {
279 errors.emplace_back(StrCat(
280 "Invalid guided_local_search_lambda_coefficient: ", gls_coefficient));
282 if (
const double step = search_parameters.optimization_step();
283 std::isnan(step) || step < 0.0) {
284 errors.emplace_back(StrCat(
"Invalid optimization_step: ", step));
286 if (
const int32_t num = search_parameters.number_of_solutions_to_collect();
289 StrCat(
"Invalid number_of_solutions_to_collect: ", num));
291 if (
const int64_t lim = search_parameters.solution_limit(); lim < 1)
292 errors.emplace_back(StrCat(
"Invalid solution_limit: ", lim));
293 if (!IsValidNonNegativeDuration(search_parameters.time_limit())) {
294 errors.emplace_back(
"Invalid time_limit: " +
295 search_parameters.time_limit().ShortDebugString());
297 if (!IsValidNonNegativeDuration(search_parameters.lns_time_limit())) {
298 errors.emplace_back(
"Invalid lns_time_limit: " +
299 search_parameters.lns_time_limit().ShortDebugString());
301 if (!FirstSolutionStrategy::Value_IsValid(
302 search_parameters.first_solution_strategy())) {
303 errors.emplace_back(StrCat(
"Invalid first_solution_strategy: ",
304 search_parameters.first_solution_strategy()));
306 if (!LocalSearchMetaheuristic::Value_IsValid(
307 search_parameters.local_search_metaheuristic())) {
308 errors.emplace_back(StrCat(
"Invalid metaheuristic: ",
309 search_parameters.local_search_metaheuristic()));
312 const double scaling_factor = search_parameters.log_cost_scaling_factor();
313 if (scaling_factor == 0 || std::isnan(scaling_factor) ||
314 std::isinf(scaling_factor)) {
316 StrCat(
"Invalid value for log_cost_scaling_factor: ", scaling_factor));
318 const double offset = search_parameters.log_cost_offset();
319 if (std::isnan(offset) || std::isinf(offset)) {
320 errors.emplace_back(StrCat(
"Invalid value for log_cost_offset: ", offset));
322 const RoutingSearchParameters::SchedulingSolver continuous_scheduling_solver =
323 search_parameters.continuous_scheduling_solver();
324 if (continuous_scheduling_solver ==
325 RoutingSearchParameters::SCHEDULING_UNSET ||
326 continuous_scheduling_solver ==
327 RoutingSearchParameters::SCHEDULING_CP_SAT) {
329 StrCat(
"Invalid value for continuous_scheduling_solver: ",
330 RoutingSearchParameters::SchedulingSolver_Name(
331 continuous_scheduling_solver)));
334 if (
const RoutingSearchParameters::SchedulingSolver
335 mixed_integer_scheduling_solver =
336 search_parameters.mixed_integer_scheduling_solver();
337 mixed_integer_scheduling_solver ==
338 RoutingSearchParameters::SCHEDULING_UNSET) {
340 StrCat(
"Invalid value for mixed_integer_scheduling_solver: ",
341 RoutingSearchParameters::SchedulingSolver_Name(
342 mixed_integer_scheduling_solver)));
345 if (search_parameters.has_improvement_limit_parameters()) {
346 const double improvement_rate_coefficient =
347 search_parameters.improvement_limit_parameters()
348 .improvement_rate_coefficient();
349 if (std::isnan(improvement_rate_coefficient) ||
350 improvement_rate_coefficient <= 0) {
352 StrCat(
"Invalid value for "
353 "improvement_limit_parameters.improvement_rate_coefficient: ",
354 improvement_rate_coefficient));
357 const int32_t improvement_rate_solutions_distance =
358 search_parameters.improvement_limit_parameters()
359 .improvement_rate_solutions_distance();
360 if (improvement_rate_solutions_distance <= 0) {
361 errors.emplace_back(StrCat(
363 "improvement_limit_parameters.improvement_rate_solutions_distance: ",
364 improvement_rate_solutions_distance));
368 if (
const double memory_coefficient =
370 .multi_armed_bandit_compound_operator_memory_coefficient();
371 std::isnan(memory_coefficient) || memory_coefficient < 0 ||
372 memory_coefficient > 1) {
374 StrCat(
"Invalid value for "
375 "multi_armed_bandit_compound_operator_memory_coefficient: ",
376 memory_coefficient));
378 if (
const double exploration_coefficient =
380 .multi_armed_bandit_compound_operator_exploration_coefficient();
381 std::isnan(exploration_coefficient) || exploration_coefficient < 0) {
383 StrCat(
"Invalid value for "
384 "multi_armed_bandit_compound_operator_exploration_coefficient: ",
385 exploration_coefficient));
388 if (
const sat::SatParameters& sat_parameters =
389 search_parameters.sat_parameters();
390 sat_parameters.enumerate_all_solutions() &&
391 (sat_parameters.num_search_workers() > 1 ||
392 sat_parameters.interleave_search())) {
394 "sat_parameters.enumerate_all_solutions cannot be true in parallel"
static ConstraintSolverParameters DefaultSolverParameters()
Create a ConstraintSolverParameters proto with all the default values.
static const int64_t kint64max
Collection of objects used to extend the Constraint Solver library.
std::vector< std::string > FindErrorsInRoutingSearchParameters(const RoutingSearchParameters &search_parameters)
Returns a list of std::string describing the errors in the routing search parameters.
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)