routing: export from google3

This commit is contained in:
Corentin Le Molgat
2025-11-19 15:38:53 +01:00
parent e89fcb39a7
commit 5fc3aff39a
5 changed files with 234 additions and 167 deletions

View File

@@ -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.

View File

@@ -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()};
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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."));
}
}
}
}