OR-Tools  9.6
routing_parameters.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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/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"
28 #include "ortools/base/logging.h"
29 #include "ortools/base/protoutil.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"
36 
37 namespace operations_research {
38 
39 RoutingModelParameters DefaultRoutingModelParameters() {
40  RoutingModelParameters parameters;
41  ConstraintSolverParameters* const solver_parameters =
42  parameters.mutable_solver_parameters();
43  *solver_parameters = Solver::DefaultSolverParameters();
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);
48  return parameters;
49 }
50 
51 namespace {
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( // NOLINT
67  false);
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); // costly if true by default
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(
103  BOOL_TRUE);
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);
130  // No global time_limit by default.
131  p.set_solution_limit(kint64max);
132  p.mutable_lns_time_limit()->set_nanos(100000000); // 0.1s.
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);
137 
138  const std::string error = FindErrorInRoutingSearchParameters(p);
139  LOG_IF(DFATAL, !error.empty())
140  << "The default search parameters aren't valid: " << error;
141  return p;
142 }
143 } // namespace
144 
145 // static
146 RoutingSearchParameters DefaultRoutingSearchParameters() {
147  static const auto* default_parameters =
148  new RoutingSearchParameters(CreateDefaultRoutingSearchParameters());
149  return *default_parameters;
150 }
151 
152 namespace {
153 bool IsValidNonNegativeDuration(const google::protobuf::Duration& d) {
154  const auto status_or_duration = util_time::DecodeGoogleApiProto(d);
155  return status_or_duration.ok() &&
156  status_or_duration.value() >= absl::ZeroDuration();
157 }
158 } // namespace
159 
161  const RoutingSearchParameters& search_parameters) {
162  const std::vector<std::string> errors =
163  FindErrorsInRoutingSearchParameters(search_parameters);
164  return (errors.empty()) ? "" : errors[0];
165 }
166 
167 std::vector<std::string> FindErrorsInRoutingSearchParameters(
168  const RoutingSearchParameters& search_parameters) {
169  using absl::StrCat;
170  std::vector<std::string> errors;
171 
172  // Check that all local search operators are set to either BOOL_TRUE or
173  // BOOL_FALSE (and not BOOL_UNSPECIFIED). Do that only in non-portable mode,
174  // since it needs proto reflection etc.
175 #if !defined(__ANDROID__) && !defined(__wasm__)
176  {
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 /*this is NOT the field's tag number*/ 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()) {
189  DLOG(FATAL)
190  << "In RoutingSearchParameters::LocalSearchNeighborhoodOperators,"
191  << " field '" << field->name() << "' is not an OptionalBoolean.";
192  } else {
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)",
198  field->name(),
199  OptionalBoolean_Name(static_cast<OptionalBoolean>(value)),
200  value));
201  }
202  }
203  }
204  }
205 #endif // !__ANDROID__ && !__wasm__
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));
209  }
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) {
213  errors.emplace_back(
214  StrCat("Invalid savings_max_memory_usage_bytes: ", max_memory));
215  }
216  if (const double coefficient = search_parameters.savings_arc_coefficient();
217  std::isnan(coefficient) || coefficient <= 0 || std::isinf(coefficient)) {
218  errors.emplace_back(
219  StrCat("Invalid savings_arc_coefficient: ", coefficient));
220  }
221  if (const double ratio =
222  search_parameters.cheapest_insertion_farthest_seeds_ratio();
223  std::isnan(ratio) || ratio < 0 || ratio > 1) {
224  errors.emplace_back(
225  StrCat("Invalid cheapest_insertion_farthest_seeds_ratio: ", ratio));
226  }
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));
232  }
233  if (const int32_t min_neighbors =
234  search_parameters.cheapest_insertion_first_solution_min_neighbors();
235  min_neighbors < 1) {
236  errors.emplace_back(
237  StrCat("Invalid cheapest_insertion_first_solution_min_neighbors: ",
238  min_neighbors, ". Must be greater or equal to 1."));
239  }
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));
245  }
246  if (const int32_t min_neighbors =
247  search_parameters.cheapest_insertion_ls_operator_min_neighbors();
248  min_neighbors < 1) {
249  errors.emplace_back(StrCat(
250  "Invalid cheapest_insertion_ls_operator_min_neighbors: ", min_neighbors,
251  ". Must be greater or equal to 1."));
252  }
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)."));
259  }
260  if (const int32_t num_arcs =
261  search_parameters
262  .heuristic_expensive_chain_lns_num_arcs_to_consider();
263  num_arcs < 2 || num_arcs > 1e6) {
264  errors.emplace_back(
265  StrCat("Invalid heuristic_expensive_chain_lns_num_arcs_to_consider: ",
266  num_arcs, ". Must be between 2 and 10^6 (included)."));
267  }
268  if (const int32_t num_nodes =
269  search_parameters.heuristic_close_nodes_lns_num_nodes();
270  num_nodes < 0 || num_nodes > 1e4) {
271  errors.emplace_back(
272  StrCat("Invalid heuristic_close_nodes_lns_num_nodes: ", num_nodes,
273  ". Must be between 0 and 10000 (included)."));
274  }
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));
281  }
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));
285  }
286  if (const int32_t num = search_parameters.number_of_solutions_to_collect();
287  num < 1) {
288  errors.emplace_back(
289  StrCat("Invalid number_of_solutions_to_collect: ", num));
290  }
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());
296  }
297  if (!IsValidNonNegativeDuration(search_parameters.lns_time_limit())) {
298  errors.emplace_back("Invalid lns_time_limit: " +
299  search_parameters.lns_time_limit().ShortDebugString());
300  }
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()));
305  }
306  if (!LocalSearchMetaheuristic::Value_IsValid(
307  search_parameters.local_search_metaheuristic())) {
308  errors.emplace_back(StrCat("Invalid metaheuristic: ",
309  search_parameters.local_search_metaheuristic()));
310  }
311 
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)) {
315  errors.emplace_back(
316  StrCat("Invalid value for log_cost_scaling_factor: ", scaling_factor));
317  }
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));
321  }
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) {
328  errors.emplace_back(
329  StrCat("Invalid value for continuous_scheduling_solver: ",
330  RoutingSearchParameters::SchedulingSolver_Name(
331  continuous_scheduling_solver)));
332  }
333 
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) {
339  errors.emplace_back(
340  StrCat("Invalid value for mixed_integer_scheduling_solver: ",
341  RoutingSearchParameters::SchedulingSolver_Name(
342  mixed_integer_scheduling_solver)));
343  }
344 
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) {
351  errors.emplace_back(
352  StrCat("Invalid value for "
353  "improvement_limit_parameters.improvement_rate_coefficient: ",
354  improvement_rate_coefficient));
355  }
356 
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(
362  "Invalid value for "
363  "improvement_limit_parameters.improvement_rate_solutions_distance: ",
364  improvement_rate_solutions_distance));
365  }
366  }
367 
368  if (const double memory_coefficient =
369  search_parameters
370  .multi_armed_bandit_compound_operator_memory_coefficient();
371  std::isnan(memory_coefficient) || memory_coefficient < 0 ||
372  memory_coefficient > 1) {
373  errors.emplace_back(
374  StrCat("Invalid value for "
375  "multi_armed_bandit_compound_operator_memory_coefficient: ",
376  memory_coefficient));
377  }
378  if (const double exploration_coefficient =
379  search_parameters
380  .multi_armed_bandit_compound_operator_exploration_coefficient();
381  std::isnan(exploration_coefficient) || exploration_coefficient < 0) {
382  errors.emplace_back(
383  StrCat("Invalid value for "
384  "multi_armed_bandit_compound_operator_exploration_coefficient: ",
385  exploration_coefficient));
386  }
387 
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())) {
393  errors.emplace_back(
394  "sat_parameters.enumerate_all_solutions cannot be true in parallel"
395  " search");
396  }
397 
398  return errors;
399 }
400 
401 } // namespace operations_research
static ConstraintSolverParameters DefaultSolverParameters()
Create a ConstraintSolverParameters proto with all the default values.
SatParameters parameters
int64_t value
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)
Definition: protoutil.h:42
Fractional ratio
int64_t coefficient