Google OR-Tools v9.15
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/log/log.h"
23#include "absl/strings/str_cat.h"
24#include "absl/strings/str_format.h"
25#include "absl/strings/string_view.h"
26#include "absl/time/time.h"
27#include "google/protobuf/descriptor.h"
28#include "google/protobuf/duration.pb.h"
29#include "google/protobuf/extension_set.h"
30#include "google/protobuf/message.h"
34#include "ortools/base/types.h"
46
47namespace operations_research {
48
50 RoutingModelParameters parameters;
51 ConstraintSolverParameters* const solver_parameters =
52 parameters.mutable_solver_parameters();
53 *solver_parameters = Solver::DefaultSolverParameters();
54 solver_parameters->set_compress_trail(
56 solver_parameters->set_skip_locally_optimal_paths(true);
57 parameters.set_reduce_vehicle_cost_model(true);
58 return parameters;
59}
60
61namespace {
62IteratedLocalSearchParameters CreateDefaultIteratedLocalSearchParameters() {
63 IteratedLocalSearchParameters ils;
64 ils.set_perturbation_strategy(PerturbationStrategy::RUIN_AND_RECREATE);
65 RuinRecreateParameters* rr = ils.mutable_ruin_recreate_parameters();
66 // NOTE: As of 07/2024, we no longer add any default ruin strategies to the
67 // default RuinRecreateParameters, because since ruin_strategies is a repeated
68 // field, it will only be appended to when merging with a proto containing
69 // this field.
70 // A ruin strategy can be added as follows.
71 // rr->add_ruin_strategies()
72 // ->mutable_spatially_close_routes()
73 // ->set_num_ruined_routes(2);
74 rr->set_ruin_composition_strategy(RuinCompositionStrategy::UNSET);
75 rr->mutable_recreate_strategy()->set_heuristic(
77 rr->set_route_selection_neighbors_ratio(1.0);
78 rr->set_route_selection_min_neighbors(10);
79 rr->set_route_selection_max_neighbors(100);
80 ils.set_improve_perturbed_solution(true);
81 ils.mutable_best_solution_acceptance_strategy()->mutable_greedy_descent();
82 SimulatedAnnealingAcceptanceStrategy* sa =
83 ils.mutable_reference_solution_acceptance_strategy()
84 ->mutable_simulated_annealing();
85 sa->set_cooling_schedule_strategy(CoolingScheduleStrategy::EXPONENTIAL);
86 sa->set_initial_temperature(100.0);
87 sa->set_final_temperature(0.01);
88 sa->set_automatic_temperatures(false);
89 return ils;
90}
91
93CreateDefaultGlobalCheapestInsertionParameters(
94 bool use_neighbors_ratio_for_initialization) {
97 gci.set_neighbors_ratio(1.0);
98 gci.set_min_neighbors(1);
99 gci.set_use_neighbors_ratio_for_initialization(
100 use_neighbors_ratio_for_initialization);
101 gci.set_add_unperformed_entries(false);
102 return gci;
103}
104
105RoutingSearchParameters CreateDefaultRoutingSearchParameters() {
108 p.set_use_unfiltered_first_solution_strategy(false);
109 p.mutable_savings_parameters()->set_neighbors_ratio(1);
110 p.mutable_savings_parameters()->set_max_memory_usage_bytes(6e9);
111 p.mutable_savings_parameters()->set_add_reverse_arcs(false);
112 p.mutable_savings_parameters()->set_arc_coefficient(1);
113 *p.mutable_global_cheapest_insertion_first_solution_parameters() =
114 CreateDefaultGlobalCheapestInsertionParameters(
115 /*use_neighbors_ratio_for_initialization=*/false);
116 *p.mutable_global_cheapest_insertion_ls_operator_parameters() =
117 CreateDefaultGlobalCheapestInsertionParameters(
118 /*use_neighbors_ratio_for_initialization=*/true);
119 p.mutable_local_cheapest_insertion_parameters()->set_pickup_delivery_strategy(
121 p.mutable_local_cheapest_cost_insertion_parameters()
122 ->set_pickup_delivery_strategy(
125 p.mutable_local_search_operators();
126 o->set_use_relocate(BOOL_TRUE);
127 o->set_use_relocate_pair(BOOL_TRUE);
128 o->set_use_light_relocate_pair(BOOL_TRUE);
129 o->set_use_relocate_subtrip(BOOL_TRUE);
130 o->set_use_relocate_neighbors(BOOL_FALSE);
131 o->set_use_exchange(BOOL_TRUE);
132 o->set_use_exchange_pair(BOOL_TRUE);
133 o->set_use_exchange_subtrip(BOOL_TRUE);
134 o->set_use_cross(BOOL_TRUE);
135 o->set_use_cross_exchange(BOOL_FALSE);
136 o->set_use_relocate_expensive_chain(BOOL_TRUE);
137 o->set_use_two_opt(BOOL_TRUE);
138 o->set_use_or_opt(BOOL_TRUE);
139 o->set_use_lin_kernighan(BOOL_TRUE);
140 o->set_use_tsp_opt(BOOL_FALSE);
141 o->set_use_make_active(BOOL_TRUE);
142 o->set_use_relocate_and_make_active(BOOL_FALSE); // costly if true by default
143 o->set_use_exchange_and_make_active(BOOL_FALSE); // very costly
144 o->set_use_exchange_path_start_ends_and_make_active(BOOL_FALSE);
145 o->set_use_make_inactive(BOOL_TRUE);
146 o->set_use_make_chain_inactive(BOOL_TRUE);
147 o->set_use_swap_active(BOOL_TRUE);
148 o->set_use_swap_active_chain(BOOL_TRUE);
149 o->set_use_extended_swap_active(BOOL_FALSE);
150 o->set_use_shortest_path_swap_active(BOOL_TRUE);
151 o->set_use_shortest_path_two_opt(BOOL_TRUE);
152 o->set_use_node_pair_swap_active(BOOL_FALSE);
153 o->set_use_path_lns(BOOL_FALSE);
154 o->set_use_full_path_lns(BOOL_FALSE);
155 o->set_use_tsp_lns(BOOL_FALSE);
156 o->set_use_inactive_lns(BOOL_FALSE);
157 o->set_use_global_cheapest_insertion_path_lns(BOOL_TRUE);
158 o->set_use_local_cheapest_insertion_path_lns(BOOL_TRUE);
159 o->set_use_relocate_path_global_cheapest_insertion_insert_unperformed(
160 BOOL_TRUE);
161 o->set_use_global_cheapest_insertion_expensive_chain_lns(BOOL_FALSE);
162 o->set_use_local_cheapest_insertion_expensive_chain_lns(BOOL_FALSE);
163 o->set_use_global_cheapest_insertion_close_nodes_lns(BOOL_FALSE);
164 o->set_use_local_cheapest_insertion_close_nodes_lns(BOOL_FALSE);
165 o->set_use_global_cheapest_insertion_visit_types_lns(BOOL_TRUE);
166 o->set_use_local_cheapest_insertion_visit_types_lns(BOOL_TRUE);
167 p.set_ls_operator_neighbors_ratio(1);
168 p.set_ls_operator_min_neighbors(1);
169 p.set_use_multi_armed_bandit_concatenate_operators(false);
170 p.set_multi_armed_bandit_compound_operator_memory_coefficient(0.04);
171 p.set_multi_armed_bandit_compound_operator_exploration_coefficient(1e12);
172 p.set_max_swap_active_chain_size(10);
173 p.set_relocate_expensive_chain_num_arcs_to_consider(4);
174 p.set_heuristic_expensive_chain_lns_num_arcs_to_consider(4);
175 p.set_heuristic_close_nodes_lns_num_nodes(5);
176 p.set_local_search_metaheuristic(LocalSearchMetaheuristic::AUTOMATIC);
177 p.set_num_max_local_optima_before_metaheuristic_switch(200);
178 p.set_guided_local_search_lambda_coefficient(0.1);
179 p.set_guided_local_search_reset_penalties_on_new_best_solution(false);
180 p.set_use_depth_first_search(false);
181 p.set_use_cp(BOOL_TRUE);
182 p.set_use_cp_sat(BOOL_FALSE);
183 p.set_use_generalized_cp_sat(BOOL_FALSE);
184 p.mutable_sat_parameters()->set_linearization_level(2);
185 p.mutable_sat_parameters()->set_num_workers(1);
186 p.set_report_intermediate_cp_sat_solutions(false);
187 p.set_fallback_to_cp_sat_size_threshold(20);
188 p.set_continuous_scheduling_solver(RoutingSearchParameters::SCHEDULING_GLOP);
189 p.set_mixed_integer_scheduling_solver(
191 p.set_disable_scheduling_beware_this_may_degrade_performance(false);
192 p.set_optimization_step(0.0);
193 p.set_number_of_solutions_to_collect(1);
194 // No global time_limit by default.
195 p.set_solution_limit(kint64max);
196 p.mutable_lns_time_limit()->set_nanos(100000000); // 0.1s.
197 p.set_secondary_ls_time_limit_ratio(0);
198 p.set_use_full_propagation(false);
199 p.set_log_search(false);
200 p.set_log_cost_scaling_factor(1.0);
201 p.set_log_cost_offset(0.0);
202 p.set_use_iterated_local_search(false);
203 *p.mutable_iterated_local_search_parameters() =
204 CreateDefaultIteratedLocalSearchParameters();
205
206 const std::string error = FindErrorInRoutingSearchParameters(p);
207 LOG_IF(DFATAL, !error.empty())
208 << "The default search parameters aren't valid: " << error;
209 return p;
210}
211
212RoutingSearchParameters CreateDefaultSecondaryRoutingSearchParameters() {
213 RoutingSearchParameters p = CreateDefaultRoutingSearchParameters();
214 p.set_local_search_metaheuristic(LocalSearchMetaheuristic::GREEDY_DESCENT);
215 p.set_use_iterated_local_search(false);
216 *p.mutable_iterated_local_search_parameters() =
217 CreateDefaultIteratedLocalSearchParameters();
219 p.mutable_local_search_operators();
220 o->set_use_relocate(BOOL_TRUE);
221 o->set_use_relocate_pair(BOOL_FALSE);
222 o->set_use_light_relocate_pair(BOOL_TRUE);
223 o->set_use_relocate_subtrip(BOOL_TRUE);
224 o->set_use_relocate_neighbors(BOOL_FALSE);
225 o->set_use_exchange(BOOL_TRUE);
226 o->set_use_exchange_pair(BOOL_TRUE);
227 o->set_use_exchange_subtrip(BOOL_TRUE);
228 o->set_use_cross(BOOL_TRUE);
229 o->set_use_cross_exchange(BOOL_FALSE);
230 o->set_use_relocate_expensive_chain(BOOL_FALSE);
231 o->set_use_two_opt(BOOL_TRUE);
232 o->set_use_or_opt(BOOL_TRUE);
233 o->set_use_lin_kernighan(BOOL_TRUE);
234 o->set_use_tsp_opt(BOOL_FALSE);
235 o->set_use_make_active(BOOL_FALSE);
236 o->set_use_relocate_and_make_active(BOOL_FALSE);
237 o->set_use_exchange_and_make_active(BOOL_FALSE);
238 o->set_use_exchange_path_start_ends_and_make_active(BOOL_FALSE);
239 o->set_use_make_inactive(BOOL_FALSE);
240 o->set_use_make_chain_inactive(BOOL_FALSE);
241 o->set_use_swap_active(BOOL_FALSE);
242 o->set_use_swap_active_chain(BOOL_FALSE);
243 o->set_use_extended_swap_active(BOOL_FALSE);
244 o->set_use_shortest_path_swap_active(BOOL_FALSE);
245 o->set_use_shortest_path_two_opt(BOOL_FALSE);
246 o->set_use_node_pair_swap_active(BOOL_FALSE);
247 o->set_use_path_lns(BOOL_FALSE);
248 o->set_use_full_path_lns(BOOL_FALSE);
249 o->set_use_tsp_lns(BOOL_FALSE);
250 o->set_use_inactive_lns(BOOL_FALSE);
251 o->set_use_global_cheapest_insertion_path_lns(BOOL_FALSE);
252 o->set_use_local_cheapest_insertion_path_lns(BOOL_FALSE);
253 o->set_use_relocate_path_global_cheapest_insertion_insert_unperformed(
254 BOOL_FALSE);
255 const std::string error = FindErrorInRoutingSearchParameters(p);
256 LOG_IF(DFATAL, !error.empty())
257 << "The default secondary search parameters aren't valid: " << error;
258 return p;
259}
260} // namespace
261
262// static
264 static const auto* default_parameters =
265 new RoutingSearchParameters(CreateDefaultRoutingSearchParameters());
266 return *default_parameters;
267}
268
270 static const auto* default_parameters = new RoutingSearchParameters(
271 CreateDefaultSecondaryRoutingSearchParameters());
272 return *default_parameters;
273}
274
275namespace {
276bool IsValidNonNegativeDuration(const google::protobuf::Duration& d) {
277 const auto status_or_duration = util_time::DecodeGoogleApiProto(d);
278 return status_or_duration.ok() &&
279 status_or_duration.value() >= absl::ZeroDuration();
280}
281
282// Searches for errors in LocalCheapestInsertionParameters and appends them to
283// the given `errors` vector.
284void FindErrorsInLocalCheapestInsertionParameters(
285 absl::string_view prefix,
286 const LocalCheapestInsertionParameters& parameters,
287 std::vector<std::string>& errors) {
288 using absl::StrCat;
289
290 absl::flat_hash_map<
292 sorting_properties_map;
294 property :
295 REPEATED_ENUM_ADAPTER(parameters, insertion_sorting_properties)) {
296 if (property ==
298 errors.emplace_back(StrCat(
299 prefix, " - Invalid insertion sorting property: ",
302 }
303 const int occurrences = sorting_properties_map[property]++;
304 if (occurrences == 2) {
305 errors.emplace_back(StrCat(
306 prefix, " - Duplicate insertion sorting property: ",
308 property)));
309 }
311 parameters.insertion_sorting_properties().size() > 1) {
312 errors.emplace_back(
313 StrCat(prefix,
314 " - SORTING_PROPERTY_RANDOM cannot be used in conjunction "
315 "with other properties."));
316 }
317 }
318}
319
320// Searches for errors in SavingsParameters and appends them to the given
321// `errors` vector.
322void FindErrorsInSavingsParameters(const absl::string_view prefix,
323 const SavingsParameters& savings_parameters,
324 std::vector<std::string>& errors) {
325 using absl::StrCat;
326
327 if (const double ratio = savings_parameters.neighbors_ratio();
328 std::isnan(ratio) || ratio <= 0 || ratio > 1) {
329 errors.emplace_back(StrCat(
330 prefix, " - Invalid savings_parameters.neighbors_ratio: ", ratio));
331 }
332 if (const double max_memory = savings_parameters.max_memory_usage_bytes();
333 std::isnan(max_memory) || max_memory <= 0 || max_memory > 1e10) {
334 errors.emplace_back(StrCat(
335 prefix,
336 " - Invalid savings_parameters.max_memory_usage_bytes: ", max_memory));
337 }
338 if (const double coefficient = savings_parameters.arc_coefficient();
339 std::isnan(coefficient) || coefficient <= 0 || std::isinf(coefficient)) {
340 errors.emplace_back(
341 StrCat(prefix,
342 " - Invalid savings_parameters.arc_coefficient: ", coefficient));
343 }
344}
345
346void FindErrorsInGlobalCheapestInsertionParameters(
347 absl::string_view prefix,
348 const GlobalCheapestInsertionParameters& gci_parameters,
349 std::vector<std::string>& errors) {
350 using absl::StrCat;
351
352 if (const double ratio = gci_parameters.farthest_seeds_ratio();
353 std::isnan(ratio) || ratio < 0 || ratio > 1) {
354 errors.emplace_back(
355 StrCat(prefix,
356 " - Invalid "
357 "global_cheapest_insertion_parameters.farthest_seeds_ratio: ",
358 ratio));
359 }
360 if (const double ratio = gci_parameters.neighbors_ratio();
361 std::isnan(ratio) || ratio <= 0 || ratio > 1) {
362 errors.emplace_back(StrCat(
363 prefix,
364 " - Invalid global_cheapest_insertion_parameters.neighbors_ratio: ",
365 ratio));
366 }
367 if (const int32_t min_neighbors = gci_parameters.min_neighbors();
368 min_neighbors < 1) {
369 errors.emplace_back(StrCat(
370 prefix,
371 " - Invalid global_cheapest_insertion_parameters.min_neighbors: ",
372 min_neighbors, ". Must be greater or equal to 1."));
373 }
374}
375
376void FindErrorsInRecreateParameters(
377 const FirstSolutionStrategy::Value heuristic,
378 const RecreateParameters& parameters, std::vector<std::string>& errors) {
379 switch (parameters.parameters_case()) {
381 const std::string prefix =
383 ? "Local cheapest insertion (recreate heuristic)"
384 : "Local cheapest cost insertion (recreate heuristic)";
385 FindErrorsInLocalCheapestInsertionParameters(
386 prefix, parameters.local_cheapest_insertion(), errors);
387 break;
388 }
390 FindErrorsInSavingsParameters("Savings (recreate heuristic)",
391 parameters.savings(), errors);
392 break;
394 FindErrorsInGlobalCheapestInsertionParameters(
395 "Global cheapest insertion (recreate heuristic)",
396 parameters.global_cheapest_insertion(), errors);
397 break;
398 default:
399 LOG(DFATAL) << "Unsupported unset recreate parameters.";
400 break;
401 }
402}
403
404// Searches for errors in ILS parameters and appends them to the given `errors`
405// vector.
406void FindErrorsInIteratedLocalSearchParameters(
407 const RoutingSearchParameters& search_parameters,
408 std::vector<std::string>& errors) {
409 using absl::StrCat;
410 if (!search_parameters.use_iterated_local_search()) {
411 return;
412 }
413
414 if (!search_parameters.has_iterated_local_search_parameters()) {
415 errors.emplace_back(
416 "use_iterated_local_search is true but "
417 "iterated_local_search_parameters are missing.");
418 return;
419 }
420
422 search_parameters.iterated_local_search_parameters();
423
424 if (ils.perturbation_strategy() == PerturbationStrategy::UNSET) {
425 errors.emplace_back(
426 StrCat("Invalid value for "
427 "iterated_local_search_parameters.perturbation_strategy: ",
428 ils.perturbation_strategy()));
429 }
430
431 if (ils.perturbation_strategy() == PerturbationStrategy::RUIN_AND_RECREATE) {
432 if (!ils.has_ruin_recreate_parameters()) {
433 errors.emplace_back(StrCat(
434 "iterated_local_search_parameters.perturbation_strategy is ",
436 " but iterated_local_search_parameters.ruin_recreate_parameters are "
437 "missing."));
438 return;
439 }
440
441 const RuinRecreateParameters& rr = ils.ruin_recreate_parameters();
442
443 if (rr.ruin_strategies().empty()) {
444 errors.emplace_back(
445 StrCat("iterated_local_search_parameters.ruin_recreate_parameters."
446 "ruin_strategies is empty"));
447 }
448
449 if (rr.ruin_strategies().size() > 1 &&
450 rr.ruin_composition_strategy() == RuinCompositionStrategy::UNSET) {
451 errors.emplace_back(StrCat(
452 "iterated_local_search_parameters.ruin_recreate_parameters."
453 "ruin_composition_strategy cannot be unset when more than one ruin "
454 "strategy is defined"));
455 }
456
457 for (const auto& ruin : rr.ruin_strategies()) {
458 if (ruin.strategy_case() == RuinStrategy::kSpatiallyCloseRoutes &&
459 ruin.spatially_close_routes().num_ruined_routes() == 0) {
460 errors.emplace_back(StrCat(
461 "iterated_local_search_parameters.ruin_recreate_parameters."
462 "ruin_strategy is set to SpatiallyCloseRoutesRuinStrategy"
463 " but spatially_close_routes.num_ruined_routes is 0 (should be "
464 "strictly positive)"));
465 } else if (ruin.strategy_case() == RuinStrategy::kRandomWalk &&
466 ruin.random_walk().num_removed_visits() == 0) {
467 errors.emplace_back(
468 StrCat("iterated_local_search_parameters.ruin_recreate_parameters."
469 "ruin_strategy is set to RandomWalkRuinStrategy"
470 " but random_walk.num_removed_visits is 0 (should be "
471 "strictly positive)"));
472 } else if (ruin.strategy_case() == RuinStrategy::kSisr) {
473 if (ruin.sisr().avg_num_removed_visits() == 0) {
474 errors.emplace_back(
475 "iterated_local_search_parameters.ruin_recreate_parameters."
476 "ruin is set to SISRRuinStrategy"
477 " but sisr.avg_num_removed_visits is 0 (should be strictly "
478 "positive)");
479 }
480 if (ruin.sisr().max_removed_sequence_size() == 0) {
481 errors.emplace_back(
482 "iterated_local_search_parameters.ruin_recreate_parameters.ruin "
483 "is set to SISRRuinStrategy but "
484 "sisr.max_removed_sequence_size is 0 (should be strictly "
485 "positive)");
486 }
487 if (ruin.sisr().bypass_factor() < 0 ||
488 ruin.sisr().bypass_factor() > 1) {
489 errors.emplace_back(StrCat(
490 "iterated_local_search_parameters.ruin_recreate_parameters."
491 "ruin is set to SISRRuinStrategy"
492 " but sisr.bypass_factor is not in [0, 1]"));
493 }
494 }
495 }
496
497 if (const double ratio = rr.route_selection_neighbors_ratio();
498 std::isnan(ratio) || ratio <= 0 || ratio > 1) {
499 errors.emplace_back(
500 StrCat("Invalid "
501 "iterated_local_search_parameters.ruin_recreate_parameters."
502 "route_selection_neighbors_ratio: ",
503 ratio));
504 }
505 if (rr.route_selection_min_neighbors() == 0) {
506 errors.emplace_back(
507 StrCat("iterated_local_search_parameters.ruin_recreate_parameters."
508 "route_selection_min_neighbors must be positive"));
509 }
510 if (rr.route_selection_min_neighbors() >
511 rr.route_selection_max_neighbors()) {
512 errors.emplace_back(
513 StrCat("iterated_local_search_parameters.ruin_recreate_parameters."
514 "route_selection_min_neighbors cannot be greater than "
515 "iterated_local_search_parameters.ruin_recreate_parameters."
516 "route_selection_max_neighbors"));
517 }
518
519 const FirstSolutionStrategy::Value recreate_heuristic =
520 rr.recreate_strategy().heuristic();
521 if (recreate_heuristic == FirstSolutionStrategy::UNSET) {
522 errors.emplace_back(
523 StrCat("Invalid value for "
524 "iterated_local_search_parameters.ruin_recreate_parameters."
525 "recreate_strategy.heuristic: ",
526 FirstSolutionStrategy::Value_Name(recreate_heuristic)));
527 }
528
529 if (rr.recreate_strategy().has_parameters()) {
530 const RecreateParameters& recreate_params =
531 rr.recreate_strategy().parameters();
532 if (recreate_params.parameters_case() ==
534 errors.emplace_back(StrCat(
535 "Invalid value for "
536 "iterated_local_search_parameters.ruin_recreate_parameters."
537 "recreate_strategy.parameters: ",
538 GetRecreateParametersName(recreate_params.parameters_case())));
539 } else {
540 if (const RecreateParameters::ParametersCase params =
541 GetParameterCaseForRecreateHeuristic(recreate_heuristic);
542 recreate_params.parameters_case() != params) {
543 errors.emplace_back(StrCat(
544 "recreate_strategy.heuristic is set to ",
545 FirstSolutionStrategy::Value_Name(recreate_heuristic),
546 " but recreate_strategy.parameters define ",
547 GetRecreateParametersName(recreate_params.parameters_case())));
548 } else {
549 FindErrorsInRecreateParameters(recreate_heuristic, recreate_params,
550 errors);
551 }
552 }
553 }
554 }
555
556 struct NamedAcceptanceStrategy {
557 std::string name;
558 AcceptanceStrategy acceptance_strategy;
559 };
560 std::vector<NamedAcceptanceStrategy> named_acceptance_strategies;
561
562 if (!ils.has_reference_solution_acceptance_strategy()) {
563 errors.emplace_back(
564 StrCat("Unset value for "
565 "iterated_local_search_parameters.reference_solution_acceptance_"
566 "strategy."));
567 } else {
568 named_acceptance_strategies.push_back(
569 {"reference_solution", ils.reference_solution_acceptance_strategy()});
570 }
571
572 if (!ils.has_best_solution_acceptance_strategy()) {
573 errors.emplace_back(StrCat(
574 "Unset value for "
575 "iterated_local_search_parameters.best_solution_acceptance_strategy."));
576 } else {
577 named_acceptance_strategies.push_back(
578 {"best_solution", ils.best_solution_acceptance_strategy()});
579 }
580
581 for (const auto& [name, acceptance_strategy] : named_acceptance_strategies) {
582 if (acceptance_strategy.has_simulated_annealing()) {
583 const SimulatedAnnealingAcceptanceStrategy& sa_params =
584 acceptance_strategy.simulated_annealing();
585
586 if (sa_params.cooling_schedule_strategy() ==
588 errors.emplace_back(
589 StrCat("Invalid value for "
590 "iterated_local_search_parameters.",
591 name,
592 "_acceptance_strategy.simulated_annealing.cooling_schedule_"
593 "strategy: ",
594 sa_params.cooling_schedule_strategy()));
595 }
596
597 if (!sa_params.automatic_temperatures()) {
598 if (sa_params.initial_temperature() < sa_params.final_temperature()) {
599 errors.emplace_back(StrCat(
600 "iterated_local_search_parameters.", name,
601 "_acceptance_strategy.simulated_annealing."
602 "initial_temperature cannot be lower than "
603 "iterated_local_search_parameters.simulated_annealing_parameters."
604 "final_temperature."));
605 }
606
607 if (sa_params.initial_temperature() < 1e-9) {
608 errors.emplace_back(
609 StrCat("iterated_local_search_parameters.", name,
610 "_acceptance_strategy.simulated_annealing."
611 "initial_temperature cannot be lower than 1e-9."));
612 }
613
614 if (sa_params.final_temperature() < 1e-9) {
615 errors.emplace_back(
616 StrCat("iterated_local_search_parameters.", name,
617 "_acceptance_strategy.simulated_annealing."
618 "final_temperature cannot be lower than 1e-9."));
619 }
620 }
621 }
622 }
623}
624
625} // namespace
626
628 const RoutingSearchParameters& search_parameters) {
629 const std::vector<std::string> errors =
630 FindErrorsInRoutingSearchParameters(search_parameters);
631 return (errors.empty()) ? "" : errors[0];
632}
633
634std::vector<std::string> FindErrorsInRoutingSearchParameters(
635 const RoutingSearchParameters& search_parameters) {
636 using absl::StrCat;
637 std::vector<std::string> errors;
638
639 // Check that all local search operators are set to either BOOL_TRUE or
640 // BOOL_FALSE (and not BOOL_UNSPECIFIED). Do that only in non-portable mode,
641 // since it needs proto reflection etc.
642#if !defined(__ANDROID__) && !defined(__wasm__)
643 {
644 using Reflection = google::protobuf::Reflection;
645 using Descriptor = google::protobuf::Descriptor;
646 using FieldDescriptor = google::protobuf::FieldDescriptor;
648 search_parameters.local_search_operators();
649 const Reflection* ls_reflection = operators.GetReflection();
650 const Descriptor* ls_descriptor = operators.GetDescriptor();
651 for (int /*this is NOT the field's tag number*/ field_index = 0;
652 field_index < ls_descriptor->field_count(); ++field_index) {
653 const FieldDescriptor* field = ls_descriptor->field(field_index);
654 if (field->type() != FieldDescriptor::TYPE_ENUM ||
655 field->enum_type() != OptionalBoolean_descriptor()) {
656 DLOG(FATAL)
657 << "In RoutingSearchParameters::LocalSearchNeighborhoodOperators,"
658 << " field '" << field->name() << "' is not an OptionalBoolean.";
659 } else {
660 const int value = ls_reflection->GetEnum(operators, field)->number();
661 if (!OptionalBoolean_IsValid(value) || value == 0) {
662 errors.emplace_back(absl::StrFormat(
663 "local_search_neighborhood_operator.%s should be set to "
664 "BOOL_TRUE or BOOL_FALSE instead of %s (value: %d)",
665 field->name(),
666 OptionalBoolean_Name(static_cast<OptionalBoolean>(value)),
667 value));
668 }
669 }
670 }
671 }
672#endif // !__ANDROID__ && !__wasm__
673 FindErrorsInSavingsParameters("Savings (first solution heuristic)",
674 search_parameters.savings_parameters(), errors);
675 FindErrorsInGlobalCheapestInsertionParameters(
676 "Global cheapest insertion (first solution heuristic)",
678 errors);
679 FindErrorsInGlobalCheapestInsertionParameters(
680 "Global cheapest insertion (ls operator)",
682 errors);
683 FindErrorsInLocalCheapestInsertionParameters(
684 "Local cheapest insertion (first solution heuristic)",
685 search_parameters.local_cheapest_insertion_parameters(), errors);
686 FindErrorsInLocalCheapestInsertionParameters(
687 "Local cheapest cost insertion (first solution heuristic)",
688 search_parameters.local_cheapest_cost_insertion_parameters(), errors);
689
690 if (const double ratio = search_parameters.ls_operator_neighbors_ratio();
691 std::isnan(ratio) || ratio <= 0 || ratio > 1) {
692 errors.emplace_back(StrCat("Invalid ls_operator_neighbors_ratio: ", ratio));
693 }
694 if (const int32_t min_neighbors =
695 search_parameters.ls_operator_min_neighbors();
696 min_neighbors < 1) {
697 errors.emplace_back(
698 StrCat("Invalid ls_operator_min_neighbors: ", min_neighbors,
699 ". Must be greater or equal to 1."));
700 }
701 if (const int32_t num_arcs =
703 num_arcs < 2 || num_arcs > 1e6) {
704 errors.emplace_back(StrCat(
705 "Invalid relocate_expensive_chain_num_arcs_to_consider: ", num_arcs,
706 ". Must be between 2 and 10^6 (included)."));
707 }
708 if (const int32_t num_arcs =
709 search_parameters
710 .heuristic_expensive_chain_lns_num_arcs_to_consider();
711 num_arcs < 2 || num_arcs > 1e6) {
712 errors.emplace_back(
713 StrCat("Invalid heuristic_expensive_chain_lns_num_arcs_to_consider: ",
714 num_arcs, ". Must be between 2 and 10^6 (included)."));
715 }
716 if (const int32_t num_nodes =
717 search_parameters.heuristic_close_nodes_lns_num_nodes();
718 num_nodes < 0 || num_nodes > 1e4) {
719 errors.emplace_back(
720 StrCat("Invalid heuristic_close_nodes_lns_num_nodes: ", num_nodes,
721 ". Must be between 0 and 10000 (included)."));
722 }
723 if (const double gls_coefficient =
724 search_parameters.guided_local_search_lambda_coefficient();
725 std::isnan(gls_coefficient) || gls_coefficient < 0 ||
726 std::isinf(gls_coefficient)) {
727 errors.emplace_back(StrCat(
728 "Invalid guided_local_search_lambda_coefficient: ", gls_coefficient));
729 }
730 if (const double step = search_parameters.optimization_step();
731 std::isnan(step) || step < 0.0) {
732 errors.emplace_back(StrCat("Invalid optimization_step: ", step));
733 }
734 if (const int32_t num = search_parameters.number_of_solutions_to_collect();
735 num < 1) {
736 errors.emplace_back(
737 StrCat("Invalid number_of_solutions_to_collect: ", num));
738 }
739 if (const int64_t lim = search_parameters.solution_limit(); lim < 1)
740 errors.emplace_back(StrCat("Invalid solution_limit: ", lim));
741 if (!IsValidNonNegativeDuration(search_parameters.time_limit())) {
742 errors.emplace_back(
743 "Invalid time_limit: " +
744 ProtobufShortDebugString(search_parameters.time_limit()));
745 }
746 if (!IsValidNonNegativeDuration(search_parameters.lns_time_limit())) {
747 errors.emplace_back(
748 "Invalid lns_time_limit: " +
749 ProtobufShortDebugString(search_parameters.lns_time_limit()));
750 }
751 if (const double ratio = search_parameters.secondary_ls_time_limit_ratio();
752 std::isnan(ratio) || ratio < 0 || ratio >= 1) {
753 errors.emplace_back(
754 StrCat("Invalid secondary_ls_time_limit_ratio: ", ratio));
755 }
757 search_parameters.first_solution_strategy())) {
758 errors.emplace_back(StrCat("Invalid first_solution_strategy: ",
759 search_parameters.first_solution_strategy()));
760 }
761 const LocalSearchMetaheuristic::Value local_search_metaheuristic =
762 search_parameters.local_search_metaheuristic();
763 if (local_search_metaheuristic != LocalSearchMetaheuristic::UNSET &&
764 local_search_metaheuristic != LocalSearchMetaheuristic::AUTOMATIC &&
765 !search_parameters.local_search_metaheuristics().empty()) {
766 errors.emplace_back(
767 StrCat("local_search_metaheuristics cannot be set if "
768 "local_search_metaheuristic is different from "
769 "UNSET or AUTOMATIC: ",
770 local_search_metaheuristic));
771 }
772 if (!LocalSearchMetaheuristic::Value_IsValid(local_search_metaheuristic)) {
773 errors.emplace_back(
774 StrCat("Invalid metaheuristic: ", local_search_metaheuristic));
775 }
776 for (const int metaheuristic :
777 search_parameters.local_search_metaheuristics()) {
778 if (!LocalSearchMetaheuristic::Value_IsValid(metaheuristic) ||
779 metaheuristic == LocalSearchMetaheuristic::UNSET) {
780 errors.emplace_back(StrCat("Invalid metaheuristic: ", metaheuristic));
781 }
782 }
783 if (!search_parameters.local_search_metaheuristics().empty() &&
785 1) {
786 errors.emplace_back(StrCat(
787 "Invalid num_max_local_optima_before_metaheuristic_switch: ",
789 }
790
791 const double scaling_factor = search_parameters.log_cost_scaling_factor();
792 if (scaling_factor == 0 || std::isnan(scaling_factor) ||
793 std::isinf(scaling_factor)) {
794 errors.emplace_back(
795 StrCat("Invalid value for log_cost_scaling_factor: ", scaling_factor));
796 }
797 const double offset = search_parameters.log_cost_offset();
798 if (std::isnan(offset) || std::isinf(offset)) {
799 errors.emplace_back(StrCat("Invalid value for log_cost_offset: ", offset));
800 }
801 const RoutingSearchParameters::SchedulingSolver continuous_scheduling_solver =
802 search_parameters.continuous_scheduling_solver();
803 if (continuous_scheduling_solver ==
805 continuous_scheduling_solver ==
807 errors.emplace_back(
808 StrCat("Invalid value for continuous_scheduling_solver: ",
810 continuous_scheduling_solver)));
811 }
812
814 mixed_integer_scheduling_solver =
815 search_parameters.mixed_integer_scheduling_solver();
816 mixed_integer_scheduling_solver ==
818 errors.emplace_back(
819 StrCat("Invalid value for mixed_integer_scheduling_solver: ",
821 mixed_integer_scheduling_solver)));
822 }
823
824 if (search_parameters.has_improvement_limit_parameters()) {
825 const double improvement_rate_coefficient =
826 search_parameters.improvement_limit_parameters()
828 if (std::isnan(improvement_rate_coefficient) ||
829 improvement_rate_coefficient <= 0) {
830 errors.emplace_back(
831 StrCat("Invalid value for "
832 "improvement_limit_parameters.improvement_rate_coefficient: ",
833 improvement_rate_coefficient));
834 }
835
836 const int32_t improvement_rate_solutions_distance =
837 search_parameters.improvement_limit_parameters()
839 if (improvement_rate_solutions_distance <= 0) {
840 errors.emplace_back(StrCat(
841 "Invalid value for "
842 "improvement_limit_parameters.improvement_rate_solutions_distance: ",
843 improvement_rate_solutions_distance));
844 }
845 }
846
847 if (const double memory_coefficient =
848 search_parameters
849 .multi_armed_bandit_compound_operator_memory_coefficient();
850 std::isnan(memory_coefficient) || memory_coefficient < 0 ||
851 memory_coefficient > 1) {
852 errors.emplace_back(
853 StrCat("Invalid value for "
854 "multi_armed_bandit_compound_operator_memory_coefficient: ",
855 memory_coefficient));
856 }
857 if (const double exploration_coefficient =
858 search_parameters
859 .multi_armed_bandit_compound_operator_exploration_coefficient();
860 std::isnan(exploration_coefficient) || exploration_coefficient < 0) {
861 errors.emplace_back(
862 StrCat("Invalid value for "
863 "multi_armed_bandit_compound_operator_exploration_coefficient: ",
864 exploration_coefficient));
865 }
866
867 if (const sat::SatParameters& sat_parameters =
868 search_parameters.sat_parameters();
869 sat_parameters.enumerate_all_solutions() &&
870 (sat_parameters.num_workers() > 1 ||
871 sat_parameters.interleave_search())) {
872 errors.emplace_back(
873 "sat_parameters.enumerate_all_solutions cannot be true in parallel"
874 " search");
875 }
876
877 if (search_parameters.max_swap_active_chain_size() < 1 &&
878 search_parameters.local_search_operators().use_swap_active_chain() ==
880 errors.emplace_back(
881 "max_swap_active_chain_size must be greater than 1 if "
882 "local_search_operators.use_swap_active_chain is BOOL_TRUE");
883 }
884
885 FindErrorsInIteratedLocalSearchParameters(search_parameters, errors);
886
887 return errors;
888}
889
890} // namespace operations_research
static constexpr TrailCompression COMPRESS_WITH_ZLIB
void set_compress_trail(::operations_research::ConstraintSolverParameters_TrailCompression value)
static const ::std::string & Value_Name(T value)
LocalCheapestInsertionParameters_InsertionSortingProperty InsertionSortingProperty
static constexpr InsertionSortingProperty SORTING_PROPERTY_UNSPECIFIED
::operations_research::ConstraintSolverParameters *PROTOBUF_NONNULL mutable_solver_parameters()
static const ::google::protobuf::Descriptor *PROTOBUF_NONNULL GetDescriptor()
static const ::google::protobuf::Reflection *PROTOBUF_NONNULL GetReflection()
const ::operations_research::RoutingSearchParameters_LocalSearchNeighborhoodOperators & local_search_operators() const
static constexpr SchedulingSolver SCHEDULING_GLOP
const ::operations_research::GlobalCheapestInsertionParameters & global_cheapest_insertion_ls_operator_parameters() const
void set_first_solution_strategy(::operations_research::FirstSolutionStrategy_Value value)
const ::operations_research::LocalCheapestInsertionParameters & local_cheapest_insertion_parameters() const
RoutingSearchParameters_LocalSearchNeighborhoodOperators LocalSearchNeighborhoodOperators
const ::operations_research::sat::SatParameters & sat_parameters() const
const ::operations_research::LocalCheapestInsertionParameters & local_cheapest_cost_insertion_parameters() const
const ::operations_research::GlobalCheapestInsertionParameters & global_cheapest_insertion_first_solution_parameters() const
::operations_research::LocalSearchMetaheuristic_Value local_search_metaheuristic() const
RoutingSearchParameters_SchedulingSolver SchedulingSolver
const ::google::protobuf::Duration & time_limit() const
::operations_research::LocalSearchMetaheuristic_Value local_search_metaheuristics(int index) const
::operations_research::RoutingSearchParameters_SchedulingSolver mixed_integer_scheduling_solver() const
const ::operations_research::RoutingSearchParameters_ImprovementSearchLimitParameters & improvement_limit_parameters() const
const ::operations_research::SavingsParameters & savings_parameters() const
static constexpr SchedulingSolver SCHEDULING_UNSET
::operations_research::RoutingSearchParameters_SchedulingSolver continuous_scheduling_solver() const
static constexpr SchedulingSolver SCHEDULING_CP_SAT
::operations_research::FirstSolutionStrategy_Value first_solution_strategy() const
const ::google::protobuf::Duration & lns_time_limit() const
static const ::std::string & SchedulingSolver_Name(T value)
static ConstraintSolverParameters DefaultSolverParameters()
Create a ConstraintSolverParameters proto with all the default values.
OR-Tools root namespace.
bool OptionalBoolean_IsValid(int value)
std::string GetRecreateParametersName(RecreateParameters::ParametersCase parameters_case)
std::string ProtobufShortDebugString(const P &message)
Definition proto_utils.h:46
const ::google::protobuf::EnumDescriptor *PROTOBUF_NONNULL OptionalBoolean_descriptor()
RoutingModelParameters DefaultRoutingModelParameters()
RoutingSearchParameters DefaultSecondaryRoutingSearchParameters()
const ::std::string & OptionalBoolean_Name(T value)
RecreateParameters::ParametersCase GetParameterCaseForRecreateHeuristic(FirstSolutionStrategy::Value recreate_heuristic)
std::vector< std::string > FindErrorsInRoutingSearchParameters(const RoutingSearchParameters &search_parameters)
RoutingSearchParameters DefaultRoutingSearchParameters()
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