tune routing search
This commit is contained in:
@@ -4509,11 +4509,15 @@ void RoutingModel::CreateNeighborhoodOperators(
|
||||
[this, ¶meters]() {
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user