tune routing search

This commit is contained in:
Laurent Perron
2021-01-20 14:53:52 +01:00
parent 93adfb62ac
commit 7003848781
8 changed files with 94 additions and 34 deletions

View File

@@ -4509,11 +4509,15 @@ void RoutingModel::CreateNeighborhoodOperators(
[this, &parameters]() {
using Heuristic = GlobalCheapestInsertionFilteredHeuristic;
Heuristic::GlobalCheapestInsertionParameters ls_gci_parameters = {
/* is_sequential */ false,
/* farthest_seeds_ratio */ 0.0,
parameters.cheapest_insertion_ls_operator_neighbors_ratio(),
/* use_neighbors_ratio_for_initialization */ true,
parameters.cheapest_insertion_add_unperformed_entries()};
.is_sequential = false,
.farthest_seeds_ratio = 0.0,
.neighbors_ratio =
parameters.cheapest_insertion_ls_operator_neighbors_ratio(),
.min_neighbors =
parameters.cheapest_insertion_ls_operator_min_neighbors(),
.use_neighbors_ratio_for_initialization = true,
.add_unperformed_entries =
parameters.cheapest_insertion_add_unperformed_entries()};
return absl::make_unique<Heuristic>(
this, absl::bind_front(&RoutingModel::GetArcCostForVehicle, this),
absl::bind_front(&RoutingModel::UnperformedPenaltyOrValue, this, 0),
@@ -5157,11 +5161,18 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders(
// Parallel/Sequential Global cheapest insertion
GlobalCheapestInsertionFilteredHeuristic::GlobalCheapestInsertionParameters
gci_parameters = {
/* is_sequential */ false,
search_parameters.cheapest_insertion_farthest_seeds_ratio(),
search_parameters.cheapest_insertion_first_solution_neighbors_ratio(),
/* use_neighbors_ratio_for_initialization */ false,
search_parameters.cheapest_insertion_add_unperformed_entries()};
.is_sequential = false,
.farthest_seeds_ratio =
search_parameters.cheapest_insertion_farthest_seeds_ratio(),
.neighbors_ratio =
search_parameters
.cheapest_insertion_first_solution_neighbors_ratio(),
.min_neighbors =
search_parameters
.cheapest_insertion_first_solution_min_neighbors(),
.use_neighbors_ratio_for_initialization = false,
.add_unperformed_entries =
search_parameters.cheapest_insertion_add_unperformed_entries()};
for (bool is_sequential : {false, true}) {
FirstSolutionStrategy::Value first_solution_strategy =
is_sequential ? FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION

View File

@@ -177,6 +177,7 @@
#include "ortools/base/hash.h"
#include "ortools/base/logging.h"
#include "ortools/base/macros.h"
#include "ortools/base/mathutil.h"
#include "ortools/constraint_solver/constraint_solver.h"
#include "ortools/constraint_solver/constraint_solveri.h"
#include "ortools/constraint_solver/routing_enums.pb.h"
@@ -3191,11 +3192,15 @@ class GlobalCheapestInsertionFilteredHeuristic
/// starting the cheapest insertion.
double farthest_seeds_ratio;
/// If neighbors_ratio < 1 then for each node only this ratio of its
/// neighbors leading to the smallest arc costs are considered.
/// neighbors leading to the smallest arc costs are considered for
/// insertions, with a minimum of 'min_neighbors':
/// num_closest_neighbors = max(min_neighbors, neighbors_ratio*N),
/// where N is the number of non-start/end nodes in the model.
double neighbors_ratio;
/// If true, only closest neighbors (see neighbors_ratio) are considered
/// as insertion positions during initialization. Otherwise, all possible
/// insertion positions are considered.
int64 min_neighbors;
/// If true, only closest neighbors (see neighbors_ratio and min_neighbors)
/// are considered as insertion positions during initialization. Otherwise,
/// all possible insertion positions are considered.
bool use_neighbors_ratio_for_initialization;
/// If true, entries are created for making the nodes/pairs unperformed, and
/// when the cost of making a node unperformed is lower than all insertions,
@@ -3450,6 +3455,12 @@ class GlobalCheapestInsertionFilteredHeuristic
return model()->Size() - model()->vehicles();
}
int64 NumNeighbors() const {
return std::max(gci_params_.min_neighbors,
MathUtil::FastInt64Round(gci_params_.neighbors_ratio *
NumNonStartEndNodes()));
}
void ResetVehicleIndices() override {
node_index_to_vehicle_.assign(node_index_to_vehicle_.size(), -1);
}

View File

@@ -173,6 +173,7 @@ void SetFirstSolutionStrategyFromFlags(RoutingSearchParameters* parameters) {
absl::GetFlag(FLAGS_cheapest_insertion_farthest_seeds_ratio));
parameters->set_cheapest_insertion_first_solution_neighbors_ratio(
absl::GetFlag(FLAGS_cheapest_insertion_first_solution_neighbors_ratio));
parameters->set_cheapest_insertion_first_solution_min_neighbors(1);
}
void SetLocalSearchMetaheuristicFromFlags(RoutingSearchParameters* parameters) {
@@ -202,6 +203,7 @@ void AddLocalSearchNeighborhoodOperatorsFromFlags(
RoutingSearchParameters* parameters) {
CHECK(parameters != nullptr);
parameters->set_cheapest_insertion_ls_operator_neighbors_ratio(1.0);
parameters->set_cheapest_insertion_ls_operator_min_neighbors(1);
RoutingSearchParameters::LocalSearchNeighborhoodOperators* const
local_search_operators = parameters->mutable_local_search_operators();

View File

@@ -52,7 +52,9 @@ RoutingSearchParameters DefaultRoutingSearchParameters() {
"savings_parallel_routes: false "
"cheapest_insertion_farthest_seeds_ratio: 0 "
"cheapest_insertion_first_solution_neighbors_ratio: 1 "
"cheapest_insertion_first_solution_min_neighbors: 1 "
"cheapest_insertion_ls_operator_neighbors_ratio: 1 "
"cheapest_insertion_ls_operator_min_neighbors: 1 "
"cheapest_insertion_add_unperformed_entries: false "
"local_search_operators {"
" use_relocate: BOOL_TRUE"
@@ -199,6 +201,14 @@ std::string FindErrorInRoutingSearchParameters(
"Invalid cheapest_insertion_first_solution_neighbors_ratio: ", ratio);
}
}
{
const int32 min_neighbors =
search_parameters.cheapest_insertion_first_solution_min_neighbors();
if (min_neighbors < 1) {
return StrCat("Invalid cheapest_insertion_first_solution_min_neighbors: ",
min_neighbors, ". Must be greater or equal to 1.");
}
}
{
const double ratio =
search_parameters.cheapest_insertion_ls_operator_neighbors_ratio();
@@ -207,6 +217,14 @@ std::string FindErrorInRoutingSearchParameters(
ratio);
}
}
{
const int32 min_neighbors =
search_parameters.cheapest_insertion_ls_operator_min_neighbors();
if (min_neighbors < 1) {
return StrCat("Invalid cheapest_insertion_ls_operator_min_neighbors: ",
min_neighbors, ". Must be greater or equal to 1.");
}
}
{
const int32 num_arcs =
search_parameters.relocate_expensive_chain_num_arcs_to_consider();

View File

@@ -34,7 +34,7 @@ package operations_research;
// then the routing library will pick its preferred value for that parameter
// automatically: this should be the case for most parameters.
// To see those "default" parameters, call GetDefaultRoutingSearchParameters().
// Next ID: 44
// Next ID: 46
message RoutingSearchParameters {
// First solution strategies, used as starting point of local search.
FirstSolutionStrategy.Value first_solution_strategy = 1;
@@ -77,13 +77,24 @@ message RoutingSearchParameters {
// cheapest insertion heuristic.
// If not overridden, its default value is 1, meaning all neighbors will be
// considered.
// The neighborhood ratio is coupled with the corresponding min_neighbors
// integer, indicating the minimum number of neighbors to consider for each
// node:
// num_closest_neighbors =
// max(min_neighbors, neighbors_ratio * NUM_NON_START_END_NODES)
// This minimum number of neighbors must be greater or equal to 1, its
// default value.
//
// Neighbors ratio for the first solution heuristic.
// Neighbors ratio and minimum number of neighbors for the first solution
// heuristic.
double cheapest_insertion_first_solution_neighbors_ratio = 21;
// Neighbors ratio for the heuristic when used in a local search operator (see
int32 cheapest_insertion_first_solution_min_neighbors = 44;
// Neighbors ratio and minimum number of neighbors for the heuristic when used
// in a local search operator (see
// local_search_operators.use_global_cheapest_insertion_path_lns and
// local_search_operators.use_global_cheapest_insertion_chain_lns below).
double cheapest_insertion_ls_operator_neighbors_ratio = 31;
int32 cheapest_insertion_ls_operator_min_neighbors = 45;
// Whether or not to consider entries making the nodes/pairs unperformed in
// the GlobalCheapestInsertion heuristic.

View File

@@ -251,7 +251,7 @@ ArcVarMap PopulateMultiRouteModelFromRoutingModel(const RoutingModel& model,
}
// The following flow constraints seem to be necessary with the Route
// constraint, greatly improving preformance due to stronger LP relaxation
// constraint, greatly improving performance due to stronger LP relaxation
// (supposedly).
// TODO(user): Remove these constraints when the Route constraint handles
// LP relaxations properly.
@@ -261,9 +261,13 @@ ArcVarMap PopulateMultiRouteModelFromRoutingModel(const RoutingModel& model,
ct->add_domain(0);
for (int node = 0; node < num_nodes; ++node) {
if (model.IsStart(node) || model.IsEnd(node)) continue;
ct->add_vars(gtl::FindOrDie(arc_vars, {depot, node}));
int* const depot_node_var = gtl::FindOrNull(arc_vars, {depot, node});
if (depot_node_var == nullptr) continue;
ct->add_vars(*depot_node_var);
ct->add_coeffs(1);
ct->add_vars(gtl::FindOrDie(arc_vars, {node, depot}));
int* const node_depot_var = gtl::FindOrNull(arc_vars, {node, depot});
if (node_depot_var == nullptr) continue;
ct->add_vars(*node_depot_var);
ct->add_coeffs(-1);
}
}
@@ -276,7 +280,9 @@ ArcVarMap PopulateMultiRouteModelFromRoutingModel(const RoutingModel& model,
std::min(model.vehicles(), model.GetMaximumNumberOfActiveVehicles()));
for (int node = 0; node < num_nodes; ++node) {
if (model.IsStart(node) || model.IsEnd(node)) continue;
ct->add_vars(gtl::FindOrDie(arc_vars, {depot, node}));
int* const var = gtl::FindOrNull(arc_vars, {depot, node});
if (var == nullptr) continue;
ct->add_vars(*var);
ct->add_coeffs(1);
}
}
@@ -297,7 +303,9 @@ ArcVarMap PopulateMultiRouteModelFromRoutingModel(const RoutingModel& model,
head = depot;
depot_added = true;
}
ct->add_vars(gtl::FindOrDie(arc_vars, {tail, head}));
int* const var = gtl::FindOrNull(arc_vars, {tail, head});
if (var == nullptr) continue;
ct->add_vars(*var);
ct->add_coeffs(1);
}
}
@@ -311,7 +319,9 @@ ArcVarMap PopulateMultiRouteModelFromRoutingModel(const RoutingModel& model,
if (model.IsEnd(head)) continue;
if (tail == head) continue;
if (model.IsStart(tail) && tail != depot) continue;
ct->add_vars(gtl::FindOrDie(arc_vars, {tail, head}));
int* const var = gtl::FindOrNull(arc_vars, {tail, head});
if (var == nullptr) continue;
ct->add_vars(*var);
ct->add_coeffs(1);
}
}

View File

@@ -3352,12 +3352,9 @@ GlobalCheapestInsertionFilteredHeuristic::
empty_vehicle_type_curator_(nullptr) {
CHECK_GT(gci_params_.neighbors_ratio, 0);
CHECK_LE(gci_params_.neighbors_ratio, 1);
CHECK_GE(gci_params_.min_neighbors, 1);
const int64 num_non_start_end_nodes = NumNonStartEndNodes();
const int64 num_neighbors =
std::max(1.0, gci_params_.neighbors_ratio * num_non_start_end_nodes);
if (num_neighbors >= num_non_start_end_nodes - 1) {
if (NumNeighbors() >= NumNonStartEndNodes() - 1) {
// All nodes are neighbors, so we set the neighbors_ratio to 1 to avoid
// unnecessary computations in the code.
gci_params_.neighbors_ratio = 1;
@@ -3378,12 +3375,10 @@ void GlobalCheapestInsertionFilteredHeuristic::ComputeNeighborhoods() {
}
// TODO(user): Refactor the neighborhood computations in RoutingModel.
const int64 num_non_start_end_nodes = NumNonStartEndNodes();
const int64 num_neighbors =
std::max(1.0, gci_params_.neighbors_ratio * num_non_start_end_nodes);
const int64 num_neighbors = NumNeighbors();
// If num_neighbors was greater or equal num_non_start_end_nodes - 1,
// gci_params_.neighbors_ratio should have been set to 1.
DCHECK_LT(num_neighbors, num_non_start_end_nodes - 1);
DCHECK_LT(num_neighbors, NumNonStartEndNodes() - 1);
const RoutingModel& routing_model = *model();
const int64 size = routing_model.Size();

View File

@@ -26,8 +26,8 @@ import com.google.protobuf.Duration;
import java.util.logging.Logger;
// [END import]
/** Minimal VRP.*/
public class VrpCapacity {
/** Minimal VRP. */
public final class VrpCapacity {
private static final Logger logger = Logger.getLogger(VrpCapacity.class.getName());
// [START data_model]
@@ -160,5 +160,7 @@ public class VrpCapacity {
printSolution(data, routing, manager, solution);
// [END print_solution]
}
private VrpCapacity() {}
}
// [END program]