Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
routing_parameters.cc
Go to the documentation of this file.
1// Copyright 2010-2025 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 <cmath>
17#include <cstdint>
18#include <string>
19#include <vector>
20
21#include "absl/container/flat_hash_map.h"
22#include "absl/strings/str_cat.h"
23#include "absl/strings/str_format.h"
24#include "absl/time/time.h"
25#include "google/protobuf/descriptor.h"
26#include "google/protobuf/duration.pb.h"
27#include "google/protobuf/message.h"
31#include "ortools/base/types.h"
33#include "ortools/constraint_solver/routing_enums.pb.h"
34#include "ortools/constraint_solver/routing_ils.pb.h"
35#include "ortools/constraint_solver/routing_parameters.pb.h"
36#include "ortools/constraint_solver/solver_parameters.pb.h"
38#include "ortools/sat/sat_parameters.pb.h"
39#include "ortools/util/optional_boolean.pb.h"
41
42namespace operations_research {
43
44RoutingModelParameters DefaultRoutingModelParameters() {
45 RoutingModelParameters parameters;
46 ConstraintSolverParameters* const solver_parameters =
47 parameters.mutable_solver_parameters();
48 *solver_parameters = Solver::DefaultSolverParameters();
49 solver_parameters->set_compress_trail(
50 ConstraintSolverParameters::COMPRESS_WITH_ZLIB);
51 solver_parameters->set_skip_locally_optimal_paths(true);
52 parameters.set_reduce_vehicle_cost_model(true);
53 return parameters;
54}
55
56namespace {
57IteratedLocalSearchParameters CreateDefaultIteratedLocalSearchParameters() {
58 IteratedLocalSearchParameters ils;
59 ils.set_perturbation_strategy(PerturbationStrategy::RUIN_AND_RECREATE);
60 RuinRecreateParameters* rr = ils.mutable_ruin_recreate_parameters();
61 // NOTE: As of 07/2024, we no longer add any default ruin strategies to the
62 // default RuinRecreateParameters, because since ruin_strategies is a repeated
63 // field, it will only be appended to when merging with a proto containing
64 // this field.
65 // A ruin strategy can be added as follows.
66 // rr->add_ruin_strategies()
67 // ->mutable_spatially_close_routes()
68 // ->set_num_ruined_routes(2);
69 rr->set_ruin_composition_strategy(RuinCompositionStrategy::UNSET);
70 rr->set_recreate_strategy(FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION);
71 rr->set_route_selection_neighbors_ratio(1.0);
72 rr->set_route_selection_min_neighbors(10);
73 rr->set_route_selection_max_neighbors(100);
74 ils.set_improve_perturbed_solution(true);
75 ils.set_acceptance_strategy(AcceptanceStrategy::GREEDY_DESCENT);
76 SimulatedAnnealingParameters* sa =
77 ils.mutable_simulated_annealing_parameters();
78 sa->set_cooling_schedule_strategy(CoolingScheduleStrategy::EXPONENTIAL);
79 sa->set_initial_temperature(100.0);
80 sa->set_final_temperature(0.01);
81 sa->set_automatic_temperatures(false);
82 return ils;
83}
84
85RoutingSearchParameters CreateDefaultRoutingSearchParameters() {
86 RoutingSearchParameters p;
87 p.set_first_solution_strategy(FirstSolutionStrategy::AUTOMATIC);
88 p.set_use_unfiltered_first_solution_strategy(false);
89 p.set_savings_neighbors_ratio(1);
90 p.set_savings_max_memory_usage_bytes(6e9);
91 p.set_savings_add_reverse_arcs(false);
92 p.set_savings_arc_coefficient(1);
93 p.set_cheapest_insertion_farthest_seeds_ratio(0);
94 p.set_cheapest_insertion_first_solution_neighbors_ratio(1);
95 p.set_cheapest_insertion_first_solution_min_neighbors(1);
96 p.set_cheapest_insertion_ls_operator_neighbors_ratio(1);
97 p.set_cheapest_insertion_ls_operator_min_neighbors(1);
98 p.set_cheapest_insertion_first_solution_use_neighbors_ratio_for_initialization( // NOLINT
99 false);
100 p.set_cheapest_insertion_add_unperformed_entries(false);
101 p.set_local_cheapest_insertion_pickup_delivery_strategy(
102 RoutingSearchParameters::BEST_PICKUP_THEN_BEST_DELIVERY);
103 p.set_local_cheapest_cost_insertion_pickup_delivery_strategy(
104 RoutingSearchParameters::BEST_PICKUP_DELIVERY_PAIR);
105 RoutingSearchParameters::LocalSearchNeighborhoodOperators* o =
106 p.mutable_local_search_operators();
107 o->set_use_relocate(BOOL_TRUE);
108 o->set_use_relocate_pair(BOOL_TRUE);
109 o->set_use_light_relocate_pair(BOOL_TRUE);
110 o->set_use_relocate_subtrip(BOOL_TRUE);
111 o->set_use_relocate_neighbors(BOOL_FALSE);
112 o->set_use_exchange(BOOL_TRUE);
113 o->set_use_exchange_pair(BOOL_TRUE);
114 o->set_use_exchange_subtrip(BOOL_TRUE);
115 o->set_use_cross(BOOL_TRUE);
116 o->set_use_cross_exchange(BOOL_FALSE);
117 o->set_use_relocate_expensive_chain(BOOL_TRUE);
118 o->set_use_two_opt(BOOL_TRUE);
119 o->set_use_or_opt(BOOL_TRUE);
120 o->set_use_lin_kernighan(BOOL_TRUE);
121 o->set_use_tsp_opt(BOOL_FALSE);
122 o->set_use_make_active(BOOL_TRUE);
123 o->set_use_relocate_and_make_active(BOOL_FALSE); // costly if true by default
124 o->set_use_exchange_and_make_active(BOOL_FALSE); // very costly
125 o->set_use_exchange_path_start_ends_and_make_active(BOOL_FALSE);
126 o->set_use_make_inactive(BOOL_TRUE);
127 o->set_use_make_chain_inactive(BOOL_TRUE);
128 o->set_use_swap_active(BOOL_TRUE);
129 o->set_use_swap_active_chain(BOOL_TRUE);
130 o->set_use_extended_swap_active(BOOL_FALSE);
131 o->set_use_shortest_path_swap_active(BOOL_TRUE);
132 o->set_use_shortest_path_two_opt(BOOL_TRUE);
133 o->set_use_node_pair_swap_active(BOOL_FALSE);
134 o->set_use_path_lns(BOOL_FALSE);
135 o->set_use_full_path_lns(BOOL_FALSE);
136 o->set_use_tsp_lns(BOOL_FALSE);
137 o->set_use_inactive_lns(BOOL_FALSE);
138 o->set_use_global_cheapest_insertion_path_lns(BOOL_TRUE);
139 o->set_use_local_cheapest_insertion_path_lns(BOOL_TRUE);
140 o->set_use_relocate_path_global_cheapest_insertion_insert_unperformed(
141 BOOL_TRUE);
142 o->set_use_global_cheapest_insertion_expensive_chain_lns(BOOL_FALSE);
143 o->set_use_local_cheapest_insertion_expensive_chain_lns(BOOL_FALSE);
144 o->set_use_global_cheapest_insertion_close_nodes_lns(BOOL_FALSE);
145 o->set_use_local_cheapest_insertion_close_nodes_lns(BOOL_FALSE);
146 p.set_ls_operator_neighbors_ratio(1);
147 p.set_ls_operator_min_neighbors(1);
148 p.set_use_multi_armed_bandit_concatenate_operators(false);
149 p.set_multi_armed_bandit_compound_operator_memory_coefficient(0.04);
150 p.set_multi_armed_bandit_compound_operator_exploration_coefficient(1e12);
151 p.set_max_swap_active_chain_size(10);
152 p.set_relocate_expensive_chain_num_arcs_to_consider(4);
153 p.set_heuristic_expensive_chain_lns_num_arcs_to_consider(4);
154 p.set_heuristic_close_nodes_lns_num_nodes(5);
155 p.set_local_search_metaheuristic(LocalSearchMetaheuristic::AUTOMATIC);
156 p.set_num_max_local_optima_before_metaheuristic_switch(200);
157 p.set_guided_local_search_lambda_coefficient(0.1);
158 p.set_guided_local_search_reset_penalties_on_new_best_solution(false);
159 p.set_use_depth_first_search(false);
160 p.set_use_cp(BOOL_TRUE);
161 p.set_use_cp_sat(BOOL_FALSE);
162 p.set_use_generalized_cp_sat(BOOL_FALSE);
163 p.mutable_sat_parameters()->set_linearization_level(2);
164 p.mutable_sat_parameters()->set_num_search_workers(1);
165 p.set_report_intermediate_cp_sat_solutions(false);
166 p.set_fallback_to_cp_sat_size_threshold(20);
167 p.set_continuous_scheduling_solver(RoutingSearchParameters::SCHEDULING_GLOP);
168 p.set_mixed_integer_scheduling_solver(
169 RoutingSearchParameters::SCHEDULING_CP_SAT);
170 p.set_disable_scheduling_beware_this_may_degrade_performance(false);
171 p.set_optimization_step(0.0);
172 p.set_number_of_solutions_to_collect(1);
173 // No global time_limit by default.
174 p.set_solution_limit(kint64max);
175 p.mutable_lns_time_limit()->set_nanos(100000000); // 0.1s.
176 p.set_secondary_ls_time_limit_ratio(0);
177 p.set_use_full_propagation(false);
178 p.set_log_search(false);
179 p.set_log_cost_scaling_factor(1.0);
180 p.set_log_cost_offset(0.0);
181 p.set_use_iterated_local_search(false);
182 *p.mutable_iterated_local_search_parameters() =
183 CreateDefaultIteratedLocalSearchParameters();
184
185 const std::string error = FindErrorInRoutingSearchParameters(p);
186 LOG_IF(DFATAL, !error.empty())
187 << "The default search parameters aren't valid: " << error;
188 return p;
189}
190
191RoutingSearchParameters CreateDefaultSecondaryRoutingSearchParameters() {
192 RoutingSearchParameters p = CreateDefaultRoutingSearchParameters();
193 p.set_local_search_metaheuristic(LocalSearchMetaheuristic::GREEDY_DESCENT);
194 p.set_use_iterated_local_search(false);
195 *p.mutable_iterated_local_search_parameters() =
196 CreateDefaultIteratedLocalSearchParameters();
197 RoutingSearchParameters::LocalSearchNeighborhoodOperators* o =
198 p.mutable_local_search_operators();
199 o->set_use_relocate(BOOL_TRUE);
200 o->set_use_relocate_pair(BOOL_FALSE);
201 o->set_use_light_relocate_pair(BOOL_TRUE);
202 o->set_use_relocate_subtrip(BOOL_TRUE);
203 o->set_use_relocate_neighbors(BOOL_FALSE);
204 o->set_use_exchange(BOOL_TRUE);
205 o->set_use_exchange_pair(BOOL_TRUE);
206 o->set_use_exchange_subtrip(BOOL_TRUE);
207 o->set_use_cross(BOOL_TRUE);
208 o->set_use_cross_exchange(BOOL_FALSE);
209 o->set_use_relocate_expensive_chain(BOOL_FALSE);
210 o->set_use_two_opt(BOOL_TRUE);
211 o->set_use_or_opt(BOOL_TRUE);
212 o->set_use_lin_kernighan(BOOL_TRUE);
213 o->set_use_tsp_opt(BOOL_FALSE);
214 o->set_use_make_active(BOOL_FALSE);
215 o->set_use_relocate_and_make_active(BOOL_FALSE);
216 o->set_use_exchange_and_make_active(BOOL_FALSE);
217 o->set_use_exchange_path_start_ends_and_make_active(BOOL_FALSE);
218 o->set_use_make_inactive(BOOL_FALSE);
219 o->set_use_make_chain_inactive(BOOL_FALSE);
220 o->set_use_swap_active(BOOL_FALSE);
221 o->set_use_swap_active_chain(BOOL_FALSE);
222 o->set_use_extended_swap_active(BOOL_FALSE);
223 o->set_use_shortest_path_swap_active(BOOL_FALSE);
224 o->set_use_shortest_path_two_opt(BOOL_FALSE);
225 o->set_use_node_pair_swap_active(BOOL_FALSE);
226 o->set_use_path_lns(BOOL_FALSE);
227 o->set_use_full_path_lns(BOOL_FALSE);
228 o->set_use_tsp_lns(BOOL_FALSE);
229 o->set_use_inactive_lns(BOOL_FALSE);
230 o->set_use_global_cheapest_insertion_path_lns(BOOL_FALSE);
231 o->set_use_local_cheapest_insertion_path_lns(BOOL_FALSE);
232 o->set_use_relocate_path_global_cheapest_insertion_insert_unperformed(
233 BOOL_FALSE);
234 const std::string error = FindErrorInRoutingSearchParameters(p);
235 LOG_IF(DFATAL, !error.empty())
236 << "The default secondary search parameters aren't valid: " << error;
237 return p;
238}
239} // namespace
240
241// static
242RoutingSearchParameters DefaultRoutingSearchParameters() {
243 static const auto* default_parameters =
244 new RoutingSearchParameters(CreateDefaultRoutingSearchParameters());
245 return *default_parameters;
246}
247
249 static const auto* default_parameters = new RoutingSearchParameters(
250 CreateDefaultSecondaryRoutingSearchParameters());
251 return *default_parameters;
252}
253
254namespace {
255bool IsValidNonNegativeDuration(const google::protobuf::Duration& d) {
256 const auto status_or_duration = util_time::DecodeGoogleApiProto(d);
257 return status_or_duration.ok() &&
258 status_or_duration.value() >= absl::ZeroDuration();
259}
260
261// Searches for errors in ILS parameters and appends them to the given `errors`
262// vector.
263void FindErrorsInIteratedLocalSearchParameters(
264 const RoutingSearchParameters& search_parameters,
265 std::vector<std::string>& errors) {
266 using absl::StrCat;
267 if (!search_parameters.use_iterated_local_search()) {
268 return;
269 }
270
271 if (!search_parameters.has_iterated_local_search_parameters()) {
272 errors.emplace_back(
273 "use_iterated_local_search is true but "
274 "iterated_local_search_parameters are missing.");
275 return;
276 }
277
278 const IteratedLocalSearchParameters& ils =
279 search_parameters.iterated_local_search_parameters();
280
281 if (ils.perturbation_strategy() == PerturbationStrategy::UNSET) {
282 errors.emplace_back(
283 StrCat("Invalid value for "
284 "iterated_local_search_parameters.perturbation_strategy: ",
285 ils.perturbation_strategy()));
286 }
287
288 if (ils.perturbation_strategy() == PerturbationStrategy::RUIN_AND_RECREATE) {
289 if (!ils.has_ruin_recreate_parameters()) {
290 errors.emplace_back(StrCat(
291 "iterated_local_search_parameters.perturbation_strategy is ",
292 PerturbationStrategy::RUIN_AND_RECREATE,
293 " but iterated_local_search_parameters.ruin_recreate_parameters are "
294 "missing."));
295 return;
296 }
297
298 const RuinRecreateParameters& rr = ils.ruin_recreate_parameters();
299
300 if (rr.ruin_strategies().empty()) {
301 errors.emplace_back(
302 StrCat("iterated_local_search_parameters.ruin_recreate_parameters."
303 "ruin_strategies is empty"));
304 }
305
306 if (rr.ruin_strategies().size() > 1 &&
307 rr.ruin_composition_strategy() == RuinCompositionStrategy::UNSET) {
308 errors.emplace_back(StrCat(
309 "iterated_local_search_parameters.ruin_recreate_parameters."
310 "ruin_composition_strategy cannot be unset when more than one ruin "
311 "strategy is defined"));
312 }
313
314 for (const auto& ruin : rr.ruin_strategies()) {
315 if (ruin.strategy_case() == RuinStrategy::kSpatiallyCloseRoutes &&
316 ruin.spatially_close_routes().num_ruined_routes() == 0) {
317 errors.emplace_back(StrCat(
318 "iterated_local_search_parameters.ruin_recreate_parameters."
319 "ruin_strategy is set to SpatiallyCloseRoutesRuinStrategy"
320 " but spatially_close_routes.num_ruined_routes is 0 (should be "
321 "strictly positive)"));
322 } else if (ruin.strategy_case() == RuinStrategy::kRandomWalk &&
323 ruin.random_walk().num_removed_visits() == 0) {
324 errors.emplace_back(
325 StrCat("iterated_local_search_parameters.ruin_recreate_parameters."
326 "ruin_strategy is set to RandomWalkRuinStrategy"
327 " but random_walk.num_removed_visits is 0 (should be "
328 "strictly positive)"));
329 } else if (ruin.strategy_case() == RuinStrategy::kSisr) {
330 if (ruin.sisr().avg_num_removed_visits() == 0) {
331 errors.emplace_back(
332 "iterated_local_search_parameters.ruin_recreate_parameters."
333 "ruin is set to SISRRuinStrategy"
334 " but sisr.avg_num_removed_visits is 0 (should be strictly "
335 "positive)");
336 }
337 if (ruin.sisr().max_removed_sequence_size() == 0) {
338 errors.emplace_back(
339 "iterated_local_search_parameters.ruin_recreate_parameters.ruin "
340 "is set to SISRRuinStrategy but "
341 "sisr.max_removed_sequence_size is 0 (should be strictly "
342 "positive)");
343 }
344 if (ruin.sisr().bypass_factor() < 0 ||
345 ruin.sisr().bypass_factor() > 1) {
346 errors.emplace_back(StrCat(
347 "iterated_local_search_parameters.ruin_recreate_parameters."
348 "ruin is set to SISRRuinStrategy"
349 " but sisr.bypass_factor is not in [0, 1]"));
350 }
351 }
352 }
353
354 if (const double ratio = rr.route_selection_neighbors_ratio();
355 std::isnan(ratio) || ratio <= 0 || ratio > 1) {
356 errors.emplace_back(
357 StrCat("Invalid "
358 "iterated_local_search_parameters.ruin_recreate_parameters."
359 "route_selection_neighbors_ratio: ",
360 ratio));
361 }
362 if (rr.route_selection_min_neighbors() == 0) {
363 errors.emplace_back(
364 StrCat("iterated_local_search_parameters.ruin_recreate_parameters."
365 "route_selection_min_neighbors must be positive"));
366 }
367 if (rr.route_selection_min_neighbors() >
368 rr.route_selection_max_neighbors()) {
369 errors.emplace_back(
370 StrCat("iterated_local_search_parameters.ruin_recreate_parameters."
371 "route_selection_min_neighbors cannot be greater than "
372 "iterated_local_search_parameters.ruin_recreate_parameters."
373 "route_selection_max_neighbors"));
374 }
375
376 if (rr.recreate_strategy() == FirstSolutionStrategy::UNSET) {
377 errors.emplace_back(
378 StrCat("Invalid value for "
379 "iterated_local_search_parameters.ruin_recreate_parameters."
380 "recreate_strategy: ",
381 rr.recreate_strategy()));
382 }
383 }
384
385 if (ils.acceptance_strategy() == AcceptanceStrategy::UNSET) {
386 errors.emplace_back(
387 StrCat("Invalid value for "
388 "iterated_local_search_parameters.acceptance_strategy: ",
389 ils.acceptance_strategy()));
390 }
391
392 if (ils.acceptance_strategy() == AcceptanceStrategy::SIMULATED_ANNEALING) {
393 if (!ils.has_simulated_annealing_parameters()) {
394 errors.emplace_back(
395 StrCat("iterated_local_search_parameters.acceptance_strategy is ",
396 AcceptanceStrategy::SIMULATED_ANNEALING,
397 " but "
398 "iterated_local_search_parameters.simulated_annealing_"
399 "parameters are missing."));
400 return;
401 }
402
403 const SimulatedAnnealingParameters& sa_params =
404 ils.simulated_annealing_parameters();
405
406 if (sa_params.cooling_schedule_strategy() ==
407 CoolingScheduleStrategy::UNSET) {
408 errors.emplace_back(
409 StrCat("Invalid value for "
410 "iterated_local_search_parameters.simulated_annealing_"
411 "parameters.cooling_schedule_strategy: ",
412 sa_params.cooling_schedule_strategy()));
413 }
414
415 if (!sa_params.automatic_temperatures()) {
416 if (sa_params.initial_temperature() < sa_params.final_temperature()) {
417 errors.emplace_back(
418 "iterated_local_search_parameters.simulated_annealing_parameters."
419 "initial_temperature cannot be lower than "
420 "iterated_local_search_parameters.simulated_annealing_parameters."
421 "final_temperature.");
422 }
423
424 if (sa_params.initial_temperature() < 1e-9) {
425 errors.emplace_back(
426 "iterated_local_search_parameters.simulated_annealing_parameters."
427 "initial_temperature cannot be lower than 1e-9.");
428 }
429
430 if (sa_params.final_temperature() < 1e-9) {
431 errors.emplace_back(
432 "iterated_local_search_parameters.simulated_annealing_parameters."
433 "final_temperature cannot be lower than 1e-9.");
434 }
435 }
436 }
437}
438
439} // namespace
440
442 const RoutingSearchParameters& search_parameters) {
443 const std::vector<std::string> errors =
444 FindErrorsInRoutingSearchParameters(search_parameters);
445 return (errors.empty()) ? "" : errors[0];
446}
447
448std::vector<std::string> FindErrorsInRoutingSearchParameters(
449 const RoutingSearchParameters& search_parameters) {
450 using absl::StrCat;
451 std::vector<std::string> errors;
452
453 // Check that all local search operators are set to either BOOL_TRUE or
454 // BOOL_FALSE (and not BOOL_UNSPECIFIED). Do that only in non-portable mode,
455 // since it needs proto reflection etc.
456#if !defined(__ANDROID__) && !defined(__wasm__)
457 {
458 using Reflection = google::protobuf::Reflection;
459 using Descriptor = google::protobuf::Descriptor;
460 using FieldDescriptor = google::protobuf::FieldDescriptor;
461 const RoutingSearchParameters::LocalSearchNeighborhoodOperators& operators =
462 search_parameters.local_search_operators();
463 const Reflection* ls_reflection = operators.GetReflection();
464 const Descriptor* ls_descriptor = operators.GetDescriptor();
465 for (int /*this is NOT the field's tag number*/ field_index = 0;
466 field_index < ls_descriptor->field_count(); ++field_index) {
467 const FieldDescriptor* field = ls_descriptor->field(field_index);
468 if (field->type() != FieldDescriptor::TYPE_ENUM ||
469 field->enum_type() != OptionalBoolean_descriptor()) {
470 DLOG(FATAL)
471 << "In RoutingSearchParameters::LocalSearchNeighborhoodOperators,"
472 << " field '" << field->name() << "' is not an OptionalBoolean.";
473 } else {
474 const int value = ls_reflection->GetEnum(operators, field)->number();
475 if (!OptionalBoolean_IsValid(value) || value == 0) {
476 errors.emplace_back(absl::StrFormat(
477 "local_search_neighborhood_operator.%s should be set to "
478 "BOOL_TRUE or BOOL_FALSE instead of %s (value: %d)",
479 field->name(),
480 OptionalBoolean_Name(static_cast<OptionalBoolean>(value)),
481 value));
482 }
483 }
484 }
485 }
486#endif // !__ANDROID__ && !__wasm__
487 if (const double ratio = search_parameters.savings_neighbors_ratio();
488 std::isnan(ratio) || ratio <= 0 || ratio > 1) {
489 errors.emplace_back(StrCat("Invalid savings_neighbors_ratio: ", ratio));
490 }
491 if (const double max_memory =
492 search_parameters.savings_max_memory_usage_bytes();
493 std::isnan(max_memory) || max_memory <= 0 || max_memory > 1e10) {
494 errors.emplace_back(
495 StrCat("Invalid savings_max_memory_usage_bytes: ", max_memory));
496 }
497 if (const double coefficient = search_parameters.savings_arc_coefficient();
498 std::isnan(coefficient) || coefficient <= 0 || std::isinf(coefficient)) {
499 errors.emplace_back(
500 StrCat("Invalid savings_arc_coefficient: ", coefficient));
501 }
502 if (const double ratio =
503 search_parameters.cheapest_insertion_farthest_seeds_ratio();
504 std::isnan(ratio) || ratio < 0 || ratio > 1) {
505 errors.emplace_back(
506 StrCat("Invalid cheapest_insertion_farthest_seeds_ratio: ", ratio));
507 }
508 if (const double ratio =
509 search_parameters.cheapest_insertion_first_solution_neighbors_ratio();
510 std::isnan(ratio) || ratio <= 0 || ratio > 1) {
511 errors.emplace_back(StrCat(
512 "Invalid cheapest_insertion_first_solution_neighbors_ratio: ", ratio));
513 }
514 if (const int32_t min_neighbors =
515 search_parameters.cheapest_insertion_first_solution_min_neighbors();
516 min_neighbors < 1) {
517 errors.emplace_back(
518 StrCat("Invalid cheapest_insertion_first_solution_min_neighbors: ",
519 min_neighbors, ". Must be greater or equal to 1."));
520 }
521 if (const double ratio =
522 search_parameters.cheapest_insertion_ls_operator_neighbors_ratio();
523 std::isnan(ratio) || ratio <= 0 || ratio > 1) {
524 errors.emplace_back(StrCat(
525 "Invalid cheapest_insertion_ls_operator_neighbors_ratio: ", ratio));
526 }
527 if (const int32_t min_neighbors =
528 search_parameters.cheapest_insertion_ls_operator_min_neighbors();
529 min_neighbors < 1) {
530 errors.emplace_back(StrCat(
531 "Invalid cheapest_insertion_ls_operator_min_neighbors: ", min_neighbors,
532 ". Must be greater or equal to 1."));
533 }
534 {
535 absl::flat_hash_map<RoutingSearchParameters::InsertionSortingProperty, int>
536 sorting_properties_map;
537 for (const RoutingSearchParameters::InsertionSortingProperty property :
538 REPEATED_ENUM_ADAPTER(search_parameters,
539 local_cheapest_insertion_sorting_properties)) {
540 if (property == RoutingSearchParameters::SORTING_PROPERTY_UNSPECIFIED) {
541 errors.emplace_back(
542 StrCat("Invalid local cheapest insertion sorting property: ",
543 RoutingSearchParameters::InsertionSortingProperty_Name(
544 RoutingSearchParameters::SORTING_PROPERTY_UNSPECIFIED)));
545 }
546 const int occurrences = sorting_properties_map[property]++;
547 if (occurrences == 2) {
548 errors.emplace_back(StrCat(
549 "Duplicate local cheapest insertion sorting property: ",
550 RoutingSearchParameters::InsertionSortingProperty_Name(property)));
551 }
552 }
553 }
554 if (const double ratio = search_parameters.ls_operator_neighbors_ratio();
555 std::isnan(ratio) || ratio <= 0 || ratio > 1) {
556 errors.emplace_back(StrCat("Invalid ls_operator_neighbors_ratio: ", ratio));
557 }
558 if (const int32_t min_neighbors =
559 search_parameters.ls_operator_min_neighbors();
560 min_neighbors < 1) {
561 errors.emplace_back(
562 StrCat("Invalid ls_operator_min_neighbors: ", min_neighbors,
563 ". Must be greater or equal to 1."));
564 }
565 if (const int32_t num_arcs =
566 search_parameters.relocate_expensive_chain_num_arcs_to_consider();
567 num_arcs < 2 || num_arcs > 1e6) {
568 errors.emplace_back(StrCat(
569 "Invalid relocate_expensive_chain_num_arcs_to_consider: ", num_arcs,
570 ". Must be between 2 and 10^6 (included)."));
571 }
572 if (const int32_t num_arcs =
573 search_parameters
574 .heuristic_expensive_chain_lns_num_arcs_to_consider();
575 num_arcs < 2 || num_arcs > 1e6) {
576 errors.emplace_back(
577 StrCat("Invalid heuristic_expensive_chain_lns_num_arcs_to_consider: ",
578 num_arcs, ". Must be between 2 and 10^6 (included)."));
579 }
580 if (const int32_t num_nodes =
581 search_parameters.heuristic_close_nodes_lns_num_nodes();
582 num_nodes < 0 || num_nodes > 1e4) {
583 errors.emplace_back(
584 StrCat("Invalid heuristic_close_nodes_lns_num_nodes: ", num_nodes,
585 ". Must be between 0 and 10000 (included)."));
586 }
587 if (const double gls_coefficient =
588 search_parameters.guided_local_search_lambda_coefficient();
589 std::isnan(gls_coefficient) || gls_coefficient < 0 ||
590 std::isinf(gls_coefficient)) {
591 errors.emplace_back(StrCat(
592 "Invalid guided_local_search_lambda_coefficient: ", gls_coefficient));
593 }
594 if (const double step = search_parameters.optimization_step();
595 std::isnan(step) || step < 0.0) {
596 errors.emplace_back(StrCat("Invalid optimization_step: ", step));
597 }
598 if (const int32_t num = search_parameters.number_of_solutions_to_collect();
599 num < 1) {
600 errors.emplace_back(
601 StrCat("Invalid number_of_solutions_to_collect: ", num));
602 }
603 if (const int64_t lim = search_parameters.solution_limit(); lim < 1)
604 errors.emplace_back(StrCat("Invalid solution_limit: ", lim));
605 if (!IsValidNonNegativeDuration(search_parameters.time_limit())) {
606 errors.emplace_back(
607 "Invalid time_limit: " +
608 ProtobufShortDebugString(search_parameters.time_limit()));
609 }
610 if (!IsValidNonNegativeDuration(search_parameters.lns_time_limit())) {
611 errors.emplace_back(
612 "Invalid lns_time_limit: " +
613 ProtobufShortDebugString(search_parameters.lns_time_limit()));
614 }
615 if (const double ratio = search_parameters.secondary_ls_time_limit_ratio();
616 std::isnan(ratio) || ratio < 0 || ratio >= 1) {
617 errors.emplace_back(
618 StrCat("Invalid secondary_ls_time_limit_ratio: ", ratio));
619 }
620 if (!FirstSolutionStrategy::Value_IsValid(
621 search_parameters.first_solution_strategy())) {
622 errors.emplace_back(StrCat("Invalid first_solution_strategy: ",
623 search_parameters.first_solution_strategy()));
624 }
625 const LocalSearchMetaheuristic::Value local_search_metaheuristic =
626 search_parameters.local_search_metaheuristic();
627 if (local_search_metaheuristic != LocalSearchMetaheuristic::UNSET &&
628 local_search_metaheuristic != LocalSearchMetaheuristic::AUTOMATIC &&
629 !search_parameters.local_search_metaheuristics().empty()) {
630 errors.emplace_back(
631 StrCat("local_search_metaheuristics cannot be set if "
632 "local_search_metaheuristic is different from "
633 "UNSET or AUTOMATIC: ",
634 local_search_metaheuristic));
635 }
636 if (!LocalSearchMetaheuristic::Value_IsValid(local_search_metaheuristic)) {
637 errors.emplace_back(
638 StrCat("Invalid metaheuristic: ", local_search_metaheuristic));
639 }
640 for (const int metaheuristic :
641 search_parameters.local_search_metaheuristics()) {
642 if (!LocalSearchMetaheuristic::Value_IsValid(metaheuristic) ||
643 metaheuristic == LocalSearchMetaheuristic::UNSET) {
644 errors.emplace_back(StrCat("Invalid metaheuristic: ", metaheuristic));
645 }
646 }
647 if (!search_parameters.local_search_metaheuristics().empty() &&
648 search_parameters.num_max_local_optima_before_metaheuristic_switch() <
649 1) {
650 errors.emplace_back(StrCat(
651 "Invalid num_max_local_optima_before_metaheuristic_switch: ",
652 search_parameters.num_max_local_optima_before_metaheuristic_switch()));
653 }
654
655 const double scaling_factor = search_parameters.log_cost_scaling_factor();
656 if (scaling_factor == 0 || std::isnan(scaling_factor) ||
657 std::isinf(scaling_factor)) {
658 errors.emplace_back(
659 StrCat("Invalid value for log_cost_scaling_factor: ", scaling_factor));
660 }
661 const double offset = search_parameters.log_cost_offset();
662 if (std::isnan(offset) || std::isinf(offset)) {
663 errors.emplace_back(StrCat("Invalid value for log_cost_offset: ", offset));
664 }
665 const RoutingSearchParameters::SchedulingSolver continuous_scheduling_solver =
666 search_parameters.continuous_scheduling_solver();
667 if (continuous_scheduling_solver ==
668 RoutingSearchParameters::SCHEDULING_UNSET ||
669 continuous_scheduling_solver ==
670 RoutingSearchParameters::SCHEDULING_CP_SAT) {
671 errors.emplace_back(
672 StrCat("Invalid value for continuous_scheduling_solver: ",
673 RoutingSearchParameters::SchedulingSolver_Name(
674 continuous_scheduling_solver)));
675 }
676
677 if (const RoutingSearchParameters::SchedulingSolver
678 mixed_integer_scheduling_solver =
679 search_parameters.mixed_integer_scheduling_solver();
680 mixed_integer_scheduling_solver ==
681 RoutingSearchParameters::SCHEDULING_UNSET) {
682 errors.emplace_back(
683 StrCat("Invalid value for mixed_integer_scheduling_solver: ",
684 RoutingSearchParameters::SchedulingSolver_Name(
685 mixed_integer_scheduling_solver)));
686 }
687
688 if (search_parameters.has_improvement_limit_parameters()) {
689 const double improvement_rate_coefficient =
690 search_parameters.improvement_limit_parameters()
691 .improvement_rate_coefficient();
692 if (std::isnan(improvement_rate_coefficient) ||
693 improvement_rate_coefficient <= 0) {
694 errors.emplace_back(
695 StrCat("Invalid value for "
696 "improvement_limit_parameters.improvement_rate_coefficient: ",
697 improvement_rate_coefficient));
698 }
699
700 const int32_t improvement_rate_solutions_distance =
701 search_parameters.improvement_limit_parameters()
702 .improvement_rate_solutions_distance();
703 if (improvement_rate_solutions_distance <= 0) {
704 errors.emplace_back(StrCat(
705 "Invalid value for "
706 "improvement_limit_parameters.improvement_rate_solutions_distance: ",
707 improvement_rate_solutions_distance));
708 }
709 }
710
711 if (const double memory_coefficient =
712 search_parameters
713 .multi_armed_bandit_compound_operator_memory_coefficient();
714 std::isnan(memory_coefficient) || memory_coefficient < 0 ||
715 memory_coefficient > 1) {
716 errors.emplace_back(
717 StrCat("Invalid value for "
718 "multi_armed_bandit_compound_operator_memory_coefficient: ",
719 memory_coefficient));
720 }
721 if (const double exploration_coefficient =
722 search_parameters
723 .multi_armed_bandit_compound_operator_exploration_coefficient();
724 std::isnan(exploration_coefficient) || exploration_coefficient < 0) {
725 errors.emplace_back(
726 StrCat("Invalid value for "
727 "multi_armed_bandit_compound_operator_exploration_coefficient: ",
728 exploration_coefficient));
729 }
730
731 if (const sat::SatParameters& sat_parameters =
732 search_parameters.sat_parameters();
733 sat_parameters.enumerate_all_solutions() &&
734 (sat_parameters.num_search_workers() > 1 ||
735 sat_parameters.interleave_search())) {
736 errors.emplace_back(
737 "sat_parameters.enumerate_all_solutions cannot be true in parallel"
738 " search");
739 }
740
741 if (search_parameters.max_swap_active_chain_size() < 1 &&
742 search_parameters.local_search_operators().use_swap_active_chain() ==
743 OptionalBoolean::BOOL_TRUE) {
744 errors.emplace_back(
745 "max_swap_active_chain_size must be greater than 1 if "
746 "local_search_operators.use_swap_active_chain is BOOL_TRUE");
747 }
748
749 FindErrorsInIteratedLocalSearchParameters(search_parameters, errors);
750
751 return errors;
752}
753
754} // namespace operations_research
static ConstraintSolverParameters DefaultSolverParameters()
--— ConstraintSolverParameters --—
In SWIG mode, we don't want anything besides these top-level includes.
std::string ProtobufShortDebugString(const P &message)
Definition proto_utils.h:41
RoutingModelParameters DefaultRoutingModelParameters()
RoutingSearchParameters DefaultSecondaryRoutingSearchParameters()
std::vector< std::string > FindErrorsInRoutingSearchParameters(const RoutingSearchParameters &search_parameters)
RoutingSearchParameters DefaultRoutingSearchParameters()
static
std::string FindErrorInRoutingSearchParameters(const RoutingSearchParameters &search_parameters)
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
Definition protoutil.h:42
#define REPEATED_ENUM_ADAPTER(var, field)
static const int64_t kint64max
Definition types.h:32