routing: export from google3
This commit is contained in:
@@ -3580,12 +3580,29 @@ const Assignment* RoutingModel::SolveWithIteratedLocalSearch(
|
||||
return true;
|
||||
};
|
||||
|
||||
std::unique_ptr<NeighborAcceptanceCriterion> acceptance_criterion =
|
||||
MakeNeighborAcceptanceCriterion(*this, parameters, &rnd);
|
||||
const IteratedLocalSearchParameters& ils_parameters =
|
||||
parameters.iterated_local_search_parameters();
|
||||
|
||||
const absl::Duration final_duration =
|
||||
!parameters.has_time_limit()
|
||||
? absl::InfiniteDuration()
|
||||
: util_time::DecodeGoogleApiProto(parameters.time_limit()).value();
|
||||
|
||||
NeighborAcceptanceCriterion::SearchState final_search_state = {
|
||||
final_duration, parameters.solution_limit()};
|
||||
|
||||
std::unique_ptr<NeighborAcceptanceCriterion> reference_acceptance_criterion =
|
||||
MakeNeighborAcceptanceCriterion(
|
||||
*this, ils_parameters.reference_solution_acceptance_strategy(),
|
||||
final_search_state, &rnd);
|
||||
|
||||
std::unique_ptr<NeighborAcceptanceCriterion> best_acceptance_criterion =
|
||||
MakeNeighborAcceptanceCriterion(
|
||||
*this, ils_parameters.best_solution_acceptance_strategy(),
|
||||
final_search_state, &rnd);
|
||||
|
||||
const bool improve_perturbed_solution =
|
||||
parameters.iterated_local_search_parameters()
|
||||
.improve_perturbed_solution();
|
||||
ils_parameters.improve_perturbed_solution();
|
||||
|
||||
while (update_time_limits() &&
|
||||
explored_solutions < parameters.solution_limit()) {
|
||||
@@ -3610,15 +3627,17 @@ const Assignment* RoutingModel::SolveWithIteratedLocalSearch(
|
||||
}
|
||||
}
|
||||
|
||||
if (neighbor_solution->ObjectiveValue() < best_solution->ObjectiveValue()) {
|
||||
absl::Duration elapsed_time =
|
||||
absl::Milliseconds(solver_->wall_time() - start_time_ms);
|
||||
|
||||
if (best_acceptance_criterion->Accept({elapsed_time, explored_solutions},
|
||||
neighbor_solution, best_solution)) {
|
||||
best_solution->CopyIntersection(neighbor_solution);
|
||||
}
|
||||
|
||||
absl::Duration elapsed_time =
|
||||
absl::Milliseconds(solver_->wall_time() - start_time_ms);
|
||||
if (acceptance_criterion->Accept({elapsed_time, explored_solutions},
|
||||
neighbor_solution,
|
||||
last_accepted_solution)) {
|
||||
if (reference_acceptance_criterion->Accept(
|
||||
{elapsed_time, explored_solutions}, neighbor_solution,
|
||||
last_accepted_solution)) {
|
||||
// Note that the perturbation_db is using last_accepted_solution as
|
||||
// reference assignment. By updating last_accepted_solution here we thus
|
||||
// also keep the perturbation_db reference assignment up to date.
|
||||
|
||||
@@ -385,33 +385,20 @@ class LinearCoolingSchedule : public CoolingSchedule {
|
||||
|
||||
// Returns a cooling schedule based on the given input parameters.
|
||||
std::unique_ptr<CoolingSchedule> MakeCoolingSchedule(
|
||||
const RoutingModel& model, const RoutingSearchParameters& parameters,
|
||||
const RoutingModel& model,
|
||||
const SimulatedAnnealingAcceptanceStrategy& sa_params,
|
||||
const NeighborAcceptanceCriterion::SearchState& final_search_state,
|
||||
std::mt19937* rnd) {
|
||||
const absl::Duration final_duration =
|
||||
!parameters.has_time_limit()
|
||||
? absl::InfiniteDuration()
|
||||
: util_time::DecodeGoogleApiProto(parameters.time_limit()).value();
|
||||
|
||||
const SimulatedAnnealingParameters& sa_params =
|
||||
parameters.iterated_local_search_parameters()
|
||||
.simulated_annealing_parameters();
|
||||
|
||||
NeighborAcceptanceCriterion::SearchState final_search_state{
|
||||
final_duration, parameters.solution_limit()};
|
||||
|
||||
const auto [initial_temperature, final_temperature] =
|
||||
GetSimulatedAnnealingTemperatures(model, sa_params, rnd);
|
||||
|
||||
switch (sa_params.cooling_schedule_strategy()) {
|
||||
case CoolingScheduleStrategy::EXPONENTIAL:
|
||||
return std::make_unique<ExponentialCoolingSchedule>(
|
||||
NeighborAcceptanceCriterion::SearchState{final_duration,
|
||||
parameters.solution_limit()},
|
||||
initial_temperature, final_temperature);
|
||||
final_search_state, initial_temperature, final_temperature);
|
||||
case CoolingScheduleStrategy::LINEAR:
|
||||
return std::make_unique<LinearCoolingSchedule>(
|
||||
std::move(final_search_state), initial_temperature,
|
||||
final_temperature);
|
||||
final_search_state, initial_temperature, final_temperature);
|
||||
default:
|
||||
LOG(DFATAL) << "Unsupported cooling schedule strategy.";
|
||||
return nullptr;
|
||||
@@ -444,6 +431,43 @@ class SimulatedAnnealingAcceptanceCriterion
|
||||
std::uniform_real_distribution<double> probability_distribution_;
|
||||
};
|
||||
|
||||
// Acceptance criterion in which a candidate assignment is accepted when it has
|
||||
// all nodes performed.
|
||||
class AllNodesPerformedAcceptanceCriterion
|
||||
: public NeighborAcceptanceCriterion {
|
||||
public:
|
||||
explicit AllNodesPerformedAcceptanceCriterion(const RoutingModel& model)
|
||||
: model_(model) {}
|
||||
|
||||
bool Accept([[maybe_unused]] const SearchState& search_state,
|
||||
const Assignment* candidate,
|
||||
[[maybe_unused]] const Assignment* reference) override {
|
||||
for (RoutingModel::DisjunctionIndex d(0);
|
||||
d < model_.GetNumberOfDisjunctions(); ++d) {
|
||||
// This solution avoids counting non-fixed variables as inactive.
|
||||
int num_possible_actives = model_.GetDisjunctionNodeIndices(d).size();
|
||||
for (const int64_t node : model_.GetDisjunctionNodeIndices(d)) {
|
||||
if (candidate->Value(model_.NextVar(node)) == node) {
|
||||
--num_possible_actives;
|
||||
}
|
||||
}
|
||||
if (num_possible_actives < model_.GetDisjunctionMaxCardinality(d)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int node = 0; node < model_.Size(); ++node) {
|
||||
if (model_.IsStart(node) || model_.IsEnd(node)) continue;
|
||||
if (!model_.GetDisjunctionIndices(node).empty()) continue;
|
||||
if (candidate->Value(model_.NextVar(node)) == node) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const RoutingModel& model_;
|
||||
};
|
||||
|
||||
// Returns whether the given assignment has at least one performed node.
|
||||
bool HasPerformedNodes(const RoutingModel& model,
|
||||
const Assignment& assignment) {
|
||||
@@ -479,21 +503,21 @@ double ComputeAverageNonEmptyRouteSize(const RoutingModel& model,
|
||||
// performed visits.
|
||||
int64_t PickRandomPerformedVisit(
|
||||
const RoutingModel& model, const Assignment& assignment, std::mt19937& rnd,
|
||||
std::uniform_int_distribution<int64_t>& customer_dist) {
|
||||
DCHECK_EQ(customer_dist.min(), 0);
|
||||
DCHECK_EQ(customer_dist.max(), model.Size() - model.vehicles());
|
||||
std::uniform_int_distribution<int64_t>& node_dist) {
|
||||
DCHECK_EQ(node_dist.min(), 0);
|
||||
DCHECK_EQ(node_dist.max(), model.Size() - model.vehicles());
|
||||
|
||||
if (!HasPerformedNodes(model, assignment)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t customer;
|
||||
int64_t node;
|
||||
do {
|
||||
customer = customer_dist(rnd);
|
||||
} while (model.IsStart(customer) ||
|
||||
assignment.Value(model.VehicleVar(customer)) == -1);
|
||||
DCHECK(!model.IsEnd(customer));
|
||||
return customer;
|
||||
node = node_dist(rnd);
|
||||
} while (model.IsStart(node) ||
|
||||
assignment.Value(model.VehicleVar(node)) == -1);
|
||||
DCHECK(!model.IsEnd(node));
|
||||
return node;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -546,20 +570,20 @@ void RoutingSolution::InitializeRouteInfoIfNeeded(int vehicle) {
|
||||
prevs_[end] = prev;
|
||||
}
|
||||
|
||||
bool RoutingSolution::BelongsToInitializedRoute(int64_t node_index) const {
|
||||
DCHECK_EQ(nexts_[node_index] != -1, prevs_[node_index] != -1);
|
||||
return nexts_[node_index] != -1;
|
||||
bool RoutingSolution::BelongsToInitializedRoute(int64_t node) const {
|
||||
DCHECK_EQ(nexts_[node] != -1, prevs_[node] != -1);
|
||||
return nexts_[node] != -1;
|
||||
}
|
||||
|
||||
int64_t RoutingSolution::GetNextNodeIndex(int64_t node_index) const {
|
||||
return BelongsToInitializedRoute(node_index)
|
||||
? nexts_[node_index]
|
||||
: assignment_->Value(model_.NextVar(node_index));
|
||||
int64_t RoutingSolution::GetNextNodeIndex(int64_t node) const {
|
||||
return BelongsToInitializedRoute(node)
|
||||
? nexts_[node]
|
||||
: assignment_->Value(model_.NextVar(node));
|
||||
}
|
||||
|
||||
int64_t RoutingSolution::GetInitializedPrevNodeIndex(int64_t node_index) const {
|
||||
DCHECK(BelongsToInitializedRoute(node_index));
|
||||
return prevs_[node_index];
|
||||
int64_t RoutingSolution::GetInitializedPrevNodeIndex(int64_t node) const {
|
||||
DCHECK(BelongsToInitializedRoute(node));
|
||||
return prevs_[node];
|
||||
}
|
||||
|
||||
int RoutingSolution::GetRouteSize(int vehicle) const {
|
||||
@@ -567,37 +591,40 @@ int RoutingSolution::GetRouteSize(int vehicle) const {
|
||||
return route_sizes_[vehicle];
|
||||
}
|
||||
|
||||
bool RoutingSolution::CanBeRemoved(int64_t node_index) const {
|
||||
return !model_.IsStart(node_index) && !model_.IsEnd(node_index) &&
|
||||
GetNextNodeIndex(node_index) != node_index;
|
||||
bool RoutingSolution::CanBeRemoved(int64_t node) const {
|
||||
return !model_.IsStart(node) && !model_.IsEnd(node) &&
|
||||
GetNextNodeIndex(node) != node;
|
||||
}
|
||||
|
||||
void RoutingSolution::RemoveNode(int64_t node_index) {
|
||||
DCHECK(BelongsToInitializedRoute(node_index));
|
||||
void RoutingSolution::RemoveNode(int64_t node) {
|
||||
DCHECK(BelongsToInitializedRoute(node));
|
||||
|
||||
DCHECK_NE(nexts_[node_index], node_index);
|
||||
DCHECK_NE(prevs_[node_index], node_index);
|
||||
DCHECK_NE(nexts_[node], node);
|
||||
DCHECK_NE(prevs_[node], node);
|
||||
|
||||
const int64_t next = nexts_[node_index];
|
||||
const int64_t prev = prevs_[node_index];
|
||||
const int64_t next = nexts_[node];
|
||||
const int64_t prev = prevs_[node];
|
||||
|
||||
const int vehicle = assignment_->Value(model_.VehicleVar(node_index));
|
||||
const int vehicle = assignment_->Value(model_.VehicleVar(node));
|
||||
--route_sizes_[vehicle];
|
||||
DCHECK_GE(route_sizes_[vehicle], 0);
|
||||
|
||||
nexts_[prev] = next;
|
||||
prevs_[next] = prev;
|
||||
|
||||
nexts_[node_index] = node_index;
|
||||
prevs_[node_index] = node_index;
|
||||
nexts_[node] = node;
|
||||
prevs_[node] = node;
|
||||
}
|
||||
|
||||
void RoutingSolution::RemovePerformedPickupDeliverySibling(int64_t customer) {
|
||||
DCHECK(!model_.IsStart(customer));
|
||||
DCHECK(!model_.IsEnd(customer));
|
||||
void RoutingSolution::RemovePerformedPickupDeliverySibling(int64_t node) {
|
||||
DCHECK(!model_.IsStart(node));
|
||||
DCHECK(!model_.IsEnd(node));
|
||||
if (const std::optional<int64_t> sibling_node =
|
||||
model_.GetFirstMatchingPickupDeliverySibling(
|
||||
customer, [this](int64_t node) { return CanBeRemoved(node); });
|
||||
node,
|
||||
[this](int64_t candidate_node) {
|
||||
return CanBeRemoved(candidate_node);
|
||||
});
|
||||
sibling_node.has_value()) {
|
||||
const int sibling_vehicle =
|
||||
assignment_->Value(model_.VehicleVar(sibling_node.value()));
|
||||
@@ -753,7 +780,7 @@ CloseRoutesRemovalRuinProcedure::CloseRoutesRemovalRuinProcedure(
|
||||
/*only_sort_neighbors_for_partial_neighborhoods=*/false})),
|
||||
num_routes_(num_routes),
|
||||
rnd_(*rnd),
|
||||
customer_dist_(0, model->Size() - model->vehicles()),
|
||||
node_dist_(0, model->Size() - model->vehicles()),
|
||||
removed_routes_(model->vehicles()) {}
|
||||
|
||||
std::function<int64_t(int64_t)> CloseRoutesRemovalRuinProcedure::Ruin(
|
||||
@@ -765,7 +792,7 @@ std::function<int64_t(int64_t)> CloseRoutesRemovalRuinProcedure::Ruin(
|
||||
}
|
||||
|
||||
const int64_t seed_node =
|
||||
PickRandomPerformedVisit(model_, *assignment, rnd_, customer_dist_);
|
||||
PickRandomPerformedVisit(model_, *assignment, rnd_, node_dist_);
|
||||
if (seed_node == -1) {
|
||||
return [this, assignment](int64_t node) {
|
||||
return assignment->Value(model_.NextVar(node));
|
||||
@@ -799,7 +826,7 @@ std::function<int64_t(int64_t)> CloseRoutesRemovalRuinProcedure::Ruin(
|
||||
}
|
||||
|
||||
return [this, assignment](int64_t node) {
|
||||
// Shortcut removed routes to remove associated customers.
|
||||
// Shortcut removed routes to remove associated nodes.
|
||||
if (model_.IsStart(node)) {
|
||||
const int route = assignment->Value(model_.VehicleVar(node));
|
||||
if (removed_routes_[route]) {
|
||||
@@ -822,7 +849,7 @@ RandomWalkRemovalRuinProcedure::RandomWalkRemovalRuinProcedure(
|
||||
/*only_sort_neighbors_for_partial_neighborhoods=*/false})),
|
||||
rnd_(*rnd),
|
||||
walk_length_(walk_length),
|
||||
customer_dist_(0, model->Size() - model->vehicles()) {}
|
||||
node_dist_(0, model->Size() - model->vehicles()) {}
|
||||
|
||||
std::function<int64_t(int64_t)> RandomWalkRemovalRuinProcedure::Ruin(
|
||||
const Assignment* assignment) {
|
||||
@@ -833,7 +860,7 @@ std::function<int64_t(int64_t)> RandomWalkRemovalRuinProcedure::Ruin(
|
||||
}
|
||||
|
||||
int64_t curr_node =
|
||||
PickRandomPerformedVisit(model_, *assignment, rnd_, customer_dist_);
|
||||
PickRandomPerformedVisit(model_, *assignment, rnd_, node_dist_);
|
||||
if (curr_node == -1) {
|
||||
return [this, assignment](int64_t node) {
|
||||
return assignment->Value(model_.NextVar(node));
|
||||
@@ -908,8 +935,8 @@ int64_t RandomWalkRemovalRuinProcedure::GetNextNodeToRemove(
|
||||
return neighbor;
|
||||
}
|
||||
|
||||
// If we are not able to find a customer in another route, we are ok
|
||||
// with taking a customer from the current one.
|
||||
// If we are not able to find a node in another route, we are ok
|
||||
// with taking a node from the current one.
|
||||
// Note that it can be -1 if no removable neighbor was found for the input
|
||||
// node.
|
||||
return same_route_closest_neighbor;
|
||||
@@ -929,7 +956,7 @@ SISRRuinProcedure::SISRRuinProcedure(RoutingModel* model, std::mt19937* rnd,
|
||||
/*add_vehicle_starts_to_neighbors=*/false,
|
||||
/*add_vehicle_ends_to_neighbors=*/false,
|
||||
/*only_sort_neighbors_for_partial_neighborhoods=*/false})),
|
||||
customer_dist_(0, model->Size() - model->vehicles()),
|
||||
node_dist_(0, model->Size() - model->vehicles()),
|
||||
probability_dist_(0.0, 1.0),
|
||||
ruined_routes_(model->vehicles()),
|
||||
routing_solution_(*model) {}
|
||||
@@ -937,7 +964,7 @@ SISRRuinProcedure::SISRRuinProcedure(RoutingModel* model, std::mt19937* rnd,
|
||||
std::function<int64_t(int64_t)> SISRRuinProcedure::Ruin(
|
||||
const Assignment* assignment) {
|
||||
const int64_t seed_node =
|
||||
PickRandomPerformedVisit(model_, *assignment, rnd_, customer_dist_);
|
||||
PickRandomPerformedVisit(model_, *assignment, rnd_, node_dist_);
|
||||
if (seed_node == -1) {
|
||||
return [this, assignment](int64_t node) {
|
||||
return assignment->Value(model_.NextVar(node));
|
||||
@@ -1126,15 +1153,19 @@ DecisionBuilder* MakePerturbationDecisionBuilder(
|
||||
}
|
||||
|
||||
std::unique_ptr<NeighborAcceptanceCriterion> MakeNeighborAcceptanceCriterion(
|
||||
const RoutingModel& model, const RoutingSearchParameters& parameters,
|
||||
const RoutingModel& model, const AcceptanceStrategy& acceptance_strategy,
|
||||
const NeighborAcceptanceCriterion::SearchState& final_search_state,
|
||||
std::mt19937* rnd) {
|
||||
CHECK(parameters.has_iterated_local_search_parameters());
|
||||
switch (parameters.iterated_local_search_parameters().acceptance_strategy()) {
|
||||
case AcceptanceStrategy::GREEDY_DESCENT:
|
||||
switch (acceptance_strategy.strategy_case()) {
|
||||
case AcceptanceStrategy::kGreedyDescent:
|
||||
return std::make_unique<GreedyDescentAcceptanceCriterion>();
|
||||
case AcceptanceStrategy::SIMULATED_ANNEALING:
|
||||
case AcceptanceStrategy::kSimulatedAnnealing:
|
||||
return std::make_unique<SimulatedAnnealingAcceptanceCriterion>(
|
||||
MakeCoolingSchedule(model, parameters, rnd), rnd);
|
||||
MakeCoolingSchedule(model, acceptance_strategy.simulated_annealing(),
|
||||
final_search_state, rnd),
|
||||
rnd);
|
||||
case AcceptanceStrategy::kAllNodesPerformed:
|
||||
return std::make_unique<AllNodesPerformedAcceptanceCriterion>(model);
|
||||
default:
|
||||
LOG(DFATAL) << "Unsupported acceptance strategy.";
|
||||
return nullptr;
|
||||
@@ -1142,8 +1173,8 @@ std::unique_ptr<NeighborAcceptanceCriterion> MakeNeighborAcceptanceCriterion(
|
||||
}
|
||||
|
||||
std::pair<double, double> GetSimulatedAnnealingTemperatures(
|
||||
const RoutingModel& model, const SimulatedAnnealingParameters& sa_params,
|
||||
std::mt19937* rnd) {
|
||||
const RoutingModel& model,
|
||||
const SimulatedAnnealingAcceptanceStrategy& sa_params, std::mt19937* rnd) {
|
||||
if (!sa_params.automatic_temperatures()) {
|
||||
return {sa_params.initial_temperature(), sa_params.final_temperature()};
|
||||
}
|
||||
|
||||
@@ -43,30 +43,30 @@ class RoutingSolution {
|
||||
// vehicle, if not already done.
|
||||
void InitializeRouteInfoIfNeeded(int vehicle);
|
||||
|
||||
// Returns whether node_index belongs to a route that has been initialized.
|
||||
bool BelongsToInitializedRoute(int64_t node_index) const;
|
||||
// Returns whether node belongs to a route that has been initialized.
|
||||
bool BelongsToInitializedRoute(int64_t node) const;
|
||||
|
||||
// Returns the next node index of the given node_index.
|
||||
int64_t GetNextNodeIndex(int64_t node_index) const;
|
||||
// Returns the next node index of the given node.
|
||||
int64_t GetNextNodeIndex(int64_t node) const;
|
||||
|
||||
// Returns the previous node index of the given node_index.
|
||||
// This must be called for node_index belonging to initialized routes.
|
||||
int64_t GetInitializedPrevNodeIndex(int64_t node_index) const;
|
||||
// Returns the previous node index of the given node.
|
||||
// This must be called for node belonging to initialized routes.
|
||||
int64_t GetInitializedPrevNodeIndex(int64_t node) const;
|
||||
|
||||
// Returns the number of visits performed by the given vehicle.
|
||||
// This must be called for a vehicle associated with an initialized route.
|
||||
int GetRouteSize(int vehicle) const;
|
||||
|
||||
// Returns whether node_index can be removed from the solution.
|
||||
// This must be called for node_index belonging to initialized routes.
|
||||
bool CanBeRemoved(int64_t node_index) const;
|
||||
// Returns whether node can be removed from the solution.
|
||||
// This must be called for node belonging to initialized routes.
|
||||
bool CanBeRemoved(int64_t node) const;
|
||||
|
||||
// Removes the node with the given node_index.
|
||||
// This must be called for node_index belonging to initialized routes.
|
||||
void RemoveNode(int64_t node_index);
|
||||
// Removes the node with the given node.
|
||||
// This must be called for node belonging to initialized routes.
|
||||
void RemoveNode(int64_t node);
|
||||
|
||||
// Removes the performed sibling pickup or delivery of customer, if any.
|
||||
void RemovePerformedPickupDeliverySibling(int64_t customer);
|
||||
// Removes the performed sibling pickup or delivery of node, if any.
|
||||
void RemovePerformedPickupDeliverySibling(int64_t node);
|
||||
|
||||
// Randomly returns the next or previous visit of the given performed
|
||||
// visit. Returns -1 if there are no other available visits. When the
|
||||
@@ -115,7 +115,7 @@ class CloseRoutesRemovalRuinProcedure : public RuinProcedure {
|
||||
int num_neighbors_for_route_selection);
|
||||
// Returns next accessors where at most num_routes routes have been shortcut,
|
||||
// i.e., next(shortcut route begin) = shortcut route end.
|
||||
// Next accessors for customers belonging to shortcut routes are still set to
|
||||
// Next accessors for nodes belonging to shortcut routes are still set to
|
||||
// their original value and should not be used.
|
||||
std::function<int64_t(int64_t)> Ruin(const Assignment* assignment) override;
|
||||
|
||||
@@ -124,7 +124,7 @@ class CloseRoutesRemovalRuinProcedure : public RuinProcedure {
|
||||
const RoutingModel::NodeNeighborsByCostClass* const neighbors_manager_;
|
||||
const size_t num_routes_;
|
||||
std::mt19937& rnd_;
|
||||
std::uniform_int_distribution<int64_t> customer_dist_;
|
||||
std::uniform_int_distribution<int64_t> node_dist_;
|
||||
SparseBitset<int64_t> removed_routes_;
|
||||
};
|
||||
|
||||
@@ -149,7 +149,7 @@ class RandomWalkRemovalRuinProcedure : public RuinProcedure {
|
||||
const RoutingModel::NodeNeighborsByCostClass* const neighbors_manager_;
|
||||
std::mt19937& rnd_;
|
||||
const int walk_length_;
|
||||
std::uniform_int_distribution<int64_t> customer_dist_;
|
||||
std::uniform_int_distribution<int64_t> node_dist_;
|
||||
std::bernoulli_distribution boolean_dist_;
|
||||
};
|
||||
|
||||
@@ -233,7 +233,7 @@ class SISRRuinProcedure : public RuinProcedure {
|
||||
int avg_num_removed_visits_;
|
||||
double bypass_factor_;
|
||||
const RoutingModel::NodeNeighborsByCostClass* const neighbors_manager_;
|
||||
std::uniform_int_distribution<int64_t> customer_dist_;
|
||||
std::uniform_int_distribution<int64_t> node_dist_;
|
||||
std::bernoulli_distribution boolean_dist_;
|
||||
std::uniform_real_distribution<double> probability_dist_;
|
||||
SparseBitset<int64_t> ruined_routes_;
|
||||
@@ -269,14 +269,15 @@ class NeighborAcceptanceCriterion {
|
||||
|
||||
// Returns a neighbor acceptance criterion based on the given parameters.
|
||||
std::unique_ptr<NeighborAcceptanceCriterion> MakeNeighborAcceptanceCriterion(
|
||||
const RoutingModel& model, const RoutingSearchParameters& parameters,
|
||||
const RoutingModel& model, const AcceptanceStrategy& acceptance_strategy,
|
||||
const NeighborAcceptanceCriterion::SearchState& final_search_state,
|
||||
std::mt19937* rnd);
|
||||
|
||||
// Returns initial and final simulated annealing temperatures according to the
|
||||
// given simulated annealing input parameters.
|
||||
std::pair<double, double> GetSimulatedAnnealingTemperatures(
|
||||
const RoutingModel& model, const SimulatedAnnealingParameters& sa_params,
|
||||
std::mt19937* rnd);
|
||||
const RoutingModel& model,
|
||||
const SimulatedAnnealingAcceptanceStrategy& sa_params, std::mt19937* rnd);
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ message SpatiallyCloseRoutesRuinStrategy {
|
||||
optional uint32 num_ruined_routes = 3;
|
||||
}
|
||||
|
||||
// Ruin strategy that removes a number of customers by performing a random walk
|
||||
// Ruin strategy that removes a number of nodes by performing a random walk
|
||||
// on the underlying routing solution. More precisely, starting from a randomly
|
||||
// selected seed visit, the walk is extended by either moving within the
|
||||
// same route or by jumping to a visit served by a different neighboring
|
||||
@@ -132,7 +132,7 @@ message SISRRuinStrategy {
|
||||
// paper is \bar{c} and the suggested value is 10.
|
||||
optional uint32 avg_num_removed_visits = 2;
|
||||
|
||||
// Value in [0, 1] ruling the number of preserved customers in the split
|
||||
// Value in [0, 1] ruling the number of preserved nodes in the split
|
||||
// sequence removal. The parameter name in the paper is \alpha and the
|
||||
// suggested value is 0.01.
|
||||
optional double bypass_factor = 3;
|
||||
@@ -262,8 +262,12 @@ message CoolingScheduleStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
// Specifies the behavior of a simulated annealing acceptance strategy.
|
||||
message SimulatedAnnealingParameters {
|
||||
// Acceptance strategy in which only improving solutions are accepted.
|
||||
message GreedyDescentAcceptanceStrategy {}
|
||||
|
||||
// Acceptance strategy in which solutions are accepted with a probability that
|
||||
// depends on its quality and on the current state of the search.
|
||||
message SimulatedAnnealingAcceptanceStrategy {
|
||||
// Determines the speed at which the temperature changes from initial to
|
||||
// final.
|
||||
CoolingScheduleStrategy.Value cooling_schedule_strategy = 1;
|
||||
@@ -287,21 +291,17 @@ message SimulatedAnnealingParameters {
|
||||
optional bool automatic_temperatures = 4;
|
||||
}
|
||||
|
||||
// Determines when a neighbor solution, obtained by the application of a
|
||||
// perturbation and improvement step to a reference solution, is used to
|
||||
// replace the reference solution.
|
||||
// Acceptance strategy in which a solution is accepted only if all nodes
|
||||
// are performed. Disjunctions are respected when several nodes can be
|
||||
// performed.
|
||||
message AllNodesPerformedAcceptanceStrategy {}
|
||||
|
||||
// Determines when a candidate solution replaces another one.
|
||||
message AcceptanceStrategy {
|
||||
enum Value {
|
||||
// Unspecified value.
|
||||
UNSET = 0;
|
||||
|
||||
// Accepts only solutions that are improving with respect to the reference
|
||||
// one.
|
||||
GREEDY_DESCENT = 1;
|
||||
|
||||
// Accepts a candidate solution with a probability that depends on its
|
||||
// quality and on the current state of the search.
|
||||
SIMULATED_ANNEALING = 2;
|
||||
oneof strategy {
|
||||
GreedyDescentAcceptanceStrategy greedy_descent = 1;
|
||||
SimulatedAnnealingAcceptanceStrategy simulated_annealing = 2;
|
||||
AllNodesPerformedAcceptanceStrategy all_nodes_performed = 3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,9 +320,9 @@ message IteratedLocalSearchParameters {
|
||||
|
||||
// Determines when the neighbor solution S', possibly improved if
|
||||
// `improve_perturbed_solution` is true, replaces the reference solution S.
|
||||
AcceptanceStrategy.Value acceptance_strategy = 4;
|
||||
AcceptanceStrategy reference_solution_acceptance_strategy = 4;
|
||||
|
||||
// Parameters to customize a simulated annealing acceptance strategy. These
|
||||
// parameters are required iff the acceptance_strategy is SIMULATED_ANNEALING.
|
||||
SimulatedAnnealingParameters simulated_annealing_parameters = 5;
|
||||
// Determines when the neighbor solution S' replaces the best solution found
|
||||
// so far.
|
||||
AcceptanceStrategy best_solution_acceptance_strategy = 5;
|
||||
}
|
||||
|
||||
@@ -78,9 +78,10 @@ IteratedLocalSearchParameters CreateDefaultIteratedLocalSearchParameters() {
|
||||
rr->set_route_selection_min_neighbors(10);
|
||||
rr->set_route_selection_max_neighbors(100);
|
||||
ils.set_improve_perturbed_solution(true);
|
||||
ils.set_acceptance_strategy(AcceptanceStrategy::GREEDY_DESCENT);
|
||||
SimulatedAnnealingParameters* sa =
|
||||
ils.mutable_simulated_annealing_parameters();
|
||||
ils.mutable_best_solution_acceptance_strategy()->mutable_greedy_descent();
|
||||
SimulatedAnnealingAcceptanceStrategy* sa =
|
||||
ils.mutable_reference_solution_acceptance_strategy()
|
||||
->mutable_simulated_annealing();
|
||||
sa->set_cooling_schedule_strategy(CoolingScheduleStrategy::EXPONENTIAL);
|
||||
sa->set_initial_temperature(100.0);
|
||||
sa->set_final_temperature(0.01);
|
||||
@@ -506,55 +507,70 @@ void FindErrorsInIteratedLocalSearchParameters(
|
||||
}
|
||||
}
|
||||
|
||||
if (ils.acceptance_strategy() == AcceptanceStrategy::UNSET) {
|
||||
struct NamedAcceptanceStrategy {
|
||||
std::string name;
|
||||
AcceptanceStrategy acceptance_strategy;
|
||||
};
|
||||
std::vector<NamedAcceptanceStrategy> named_acceptance_strategies;
|
||||
|
||||
if (!ils.has_reference_solution_acceptance_strategy()) {
|
||||
errors.emplace_back(
|
||||
StrCat("Invalid value for "
|
||||
"iterated_local_search_parameters.acceptance_strategy: ",
|
||||
ils.acceptance_strategy()));
|
||||
StrCat("Unset value for "
|
||||
"iterated_local_search_parameters.reference_solution_acceptance_"
|
||||
"strategy."));
|
||||
} else {
|
||||
named_acceptance_strategies.push_back(
|
||||
{"reference_solution", ils.reference_solution_acceptance_strategy()});
|
||||
}
|
||||
|
||||
if (ils.acceptance_strategy() == AcceptanceStrategy::SIMULATED_ANNEALING) {
|
||||
if (!ils.has_simulated_annealing_parameters()) {
|
||||
errors.emplace_back(
|
||||
StrCat("iterated_local_search_parameters.acceptance_strategy is ",
|
||||
AcceptanceStrategy::SIMULATED_ANNEALING,
|
||||
" but "
|
||||
"iterated_local_search_parameters.simulated_annealing_"
|
||||
"parameters are missing."));
|
||||
return;
|
||||
}
|
||||
if (!ils.has_best_solution_acceptance_strategy()) {
|
||||
errors.emplace_back(StrCat(
|
||||
"Unset value for "
|
||||
"iterated_local_search_parameters.best_solution_acceptance_strategy."));
|
||||
} else {
|
||||
named_acceptance_strategies.push_back(
|
||||
{"best_solution", ils.best_solution_acceptance_strategy()});
|
||||
}
|
||||
|
||||
const SimulatedAnnealingParameters& sa_params =
|
||||
ils.simulated_annealing_parameters();
|
||||
for (const auto& [name, acceptance_strategy] : named_acceptance_strategies) {
|
||||
if (acceptance_strategy.has_simulated_annealing()) {
|
||||
const SimulatedAnnealingAcceptanceStrategy& sa_params =
|
||||
acceptance_strategy.simulated_annealing();
|
||||
|
||||
if (sa_params.cooling_schedule_strategy() ==
|
||||
CoolingScheduleStrategy::UNSET) {
|
||||
errors.emplace_back(
|
||||
StrCat("Invalid value for "
|
||||
"iterated_local_search_parameters.simulated_annealing_"
|
||||
"parameters.cooling_schedule_strategy: ",
|
||||
sa_params.cooling_schedule_strategy()));
|
||||
}
|
||||
|
||||
if (!sa_params.automatic_temperatures()) {
|
||||
if (sa_params.initial_temperature() < sa_params.final_temperature()) {
|
||||
if (sa_params.cooling_schedule_strategy() ==
|
||||
CoolingScheduleStrategy::UNSET) {
|
||||
errors.emplace_back(
|
||||
"iterated_local_search_parameters.simulated_annealing_parameters."
|
||||
"initial_temperature cannot be lower than "
|
||||
"iterated_local_search_parameters.simulated_annealing_parameters."
|
||||
"final_temperature.");
|
||||
StrCat("Invalid value for "
|
||||
"iterated_local_search_parameters.",
|
||||
name,
|
||||
"_acceptance_strategy.simulated_annealing.cooling_schedule_"
|
||||
"strategy: ",
|
||||
sa_params.cooling_schedule_strategy()));
|
||||
}
|
||||
|
||||
if (sa_params.initial_temperature() < 1e-9) {
|
||||
errors.emplace_back(
|
||||
"iterated_local_search_parameters.simulated_annealing_parameters."
|
||||
"initial_temperature cannot be lower than 1e-9.");
|
||||
}
|
||||
if (!sa_params.automatic_temperatures()) {
|
||||
if (sa_params.initial_temperature() < sa_params.final_temperature()) {
|
||||
errors.emplace_back(StrCat(
|
||||
"iterated_local_search_parameters.", name,
|
||||
"_acceptance_strategy.simulated_annealing."
|
||||
"initial_temperature cannot be lower than "
|
||||
"iterated_local_search_parameters.simulated_annealing_parameters."
|
||||
"final_temperature."));
|
||||
}
|
||||
|
||||
if (sa_params.final_temperature() < 1e-9) {
|
||||
errors.emplace_back(
|
||||
"iterated_local_search_parameters.simulated_annealing_parameters."
|
||||
"final_temperature cannot be lower than 1e-9.");
|
||||
if (sa_params.initial_temperature() < 1e-9) {
|
||||
errors.emplace_back(
|
||||
StrCat("iterated_local_search_parameters.", name,
|
||||
"_acceptance_strategy.simulated_annealing."
|
||||
"initial_temperature cannot be lower than 1e-9."));
|
||||
}
|
||||
|
||||
if (sa_params.final_temperature() < 1e-9) {
|
||||
errors.emplace_back(
|
||||
StrCat("iterated_local_search_parameters.", name,
|
||||
"_acceptance_strategy.simulated_annealing."
|
||||
"final_temperature cannot be lower than 1e-9."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user