Skip to content

Commit 1ccab56

Browse files
committed
routing: export from google3
1 parent c322f06 commit 1ccab56

File tree

5 files changed

+234
-167
lines changed

5 files changed

+234
-167
lines changed

ortools/routing/ils.cc

Lines changed: 101 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -368,33 +368,20 @@ class LinearCoolingSchedule : public CoolingSchedule {
368368

369369
// Returns a cooling schedule based on the given input parameters.
370370
std::unique_ptr<CoolingSchedule> MakeCoolingSchedule(
371-
const RoutingModel& model, const RoutingSearchParameters& parameters,
371+
const RoutingModel& model,
372+
const SimulatedAnnealingAcceptanceStrategy& sa_params,
373+
const NeighborAcceptanceCriterion::SearchState& final_search_state,
372374
std::mt19937* rnd) {
373-
const absl::Duration final_duration =
374-
!parameters.has_time_limit()
375-
? absl::InfiniteDuration()
376-
: util_time::DecodeGoogleApiProto(parameters.time_limit()).value();
377-
378-
const SimulatedAnnealingParameters& sa_params =
379-
parameters.iterated_local_search_parameters()
380-
.simulated_annealing_parameters();
381-
382-
NeighborAcceptanceCriterion::SearchState final_search_state{
383-
final_duration, parameters.solution_limit()};
384-
385375
const auto [initial_temperature, final_temperature] =
386376
GetSimulatedAnnealingTemperatures(model, sa_params, rnd);
387377

388378
switch (sa_params.cooling_schedule_strategy()) {
389379
case CoolingScheduleStrategy::EXPONENTIAL:
390380
return std::make_unique<ExponentialCoolingSchedule>(
391-
NeighborAcceptanceCriterion::SearchState{final_duration,
392-
parameters.solution_limit()},
393-
initial_temperature, final_temperature);
381+
final_search_state, initial_temperature, final_temperature);
394382
case CoolingScheduleStrategy::LINEAR:
395383
return std::make_unique<LinearCoolingSchedule>(
396-
std::move(final_search_state), initial_temperature,
397-
final_temperature);
384+
final_search_state, initial_temperature, final_temperature);
398385
default:
399386
LOG(DFATAL) << "Unsupported cooling schedule strategy.";
400387
return nullptr;
@@ -427,6 +414,43 @@ class SimulatedAnnealingAcceptanceCriterion
427414
std::uniform_real_distribution<double> probability_distribution_;
428415
};
429416

417+
// Acceptance criterion in which a candidate assignment is accepted when it has
418+
// all nodes performed.
419+
class AllNodesPerformedAcceptanceCriterion
420+
: public NeighborAcceptanceCriterion {
421+
public:
422+
explicit AllNodesPerformedAcceptanceCriterion(const RoutingModel& model)
423+
: model_(model) {}
424+
425+
bool Accept([[maybe_unused]] const SearchState& search_state,
426+
const Assignment* candidate,
427+
[[maybe_unused]] const Assignment* reference) override {
428+
for (RoutingModel::DisjunctionIndex d(0);
429+
d < model_.GetNumberOfDisjunctions(); ++d) {
430+
// This solution avoids counting non-fixed variables as inactive.
431+
int num_possible_actives = model_.GetDisjunctionNodeIndices(d).size();
432+
for (const int64_t node : model_.GetDisjunctionNodeIndices(d)) {
433+
if (candidate->Value(model_.NextVar(node)) == node) {
434+
--num_possible_actives;
435+
}
436+
}
437+
if (num_possible_actives < model_.GetDisjunctionMaxCardinality(d)) {
438+
return false;
439+
}
440+
}
441+
442+
for (int node = 0; node < model_.Size(); ++node) {
443+
if (model_.IsStart(node) || model_.IsEnd(node)) continue;
444+
if (!model_.GetDisjunctionIndices(node).empty()) continue;
445+
if (candidate->Value(model_.NextVar(node)) == node) return false;
446+
}
447+
return true;
448+
}
449+
450+
private:
451+
const RoutingModel& model_;
452+
};
453+
430454
// Returns whether the given assignment has at least one performed node.
431455
bool HasPerformedNodes(const RoutingModel& model,
432456
const Assignment& assignment) {
@@ -462,21 +486,21 @@ double ComputeAverageNonEmptyRouteSize(const RoutingModel& model,
462486
// performed visits.
463487
int64_t PickRandomPerformedVisit(
464488
const RoutingModel& model, const Assignment& assignment, std::mt19937& rnd,
465-
std::uniform_int_distribution<int64_t>& customer_dist) {
466-
DCHECK_EQ(customer_dist.min(), 0);
467-
DCHECK_EQ(customer_dist.max(), model.Size() - model.vehicles());
489+
std::uniform_int_distribution<int64_t>& node_dist) {
490+
DCHECK_EQ(node_dist.min(), 0);
491+
DCHECK_EQ(node_dist.max(), model.Size() - model.vehicles());
468492

469493
if (!HasPerformedNodes(model, assignment)) {
470494
return -1;
471495
}
472496

473-
int64_t customer;
497+
int64_t node;
474498
do {
475-
customer = customer_dist(rnd);
476-
} while (model.IsStart(customer) ||
477-
assignment.Value(model.VehicleVar(customer)) == -1);
478-
DCHECK(!model.IsEnd(customer));
479-
return customer;
499+
node = node_dist(rnd);
500+
} while (model.IsStart(node) ||
501+
assignment.Value(model.VehicleVar(node)) == -1);
502+
DCHECK(!model.IsEnd(node));
503+
return node;
480504
}
481505
} // namespace
482506

@@ -529,58 +553,61 @@ void RoutingSolution::InitializeRouteInfoIfNeeded(int vehicle) {
529553
prevs_[end] = prev;
530554
}
531555

532-
bool RoutingSolution::BelongsToInitializedRoute(int64_t node_index) const {
533-
DCHECK_EQ(nexts_[node_index] != -1, prevs_[node_index] != -1);
534-
return nexts_[node_index] != -1;
556+
bool RoutingSolution::BelongsToInitializedRoute(int64_t node) const {
557+
DCHECK_EQ(nexts_[node] != -1, prevs_[node] != -1);
558+
return nexts_[node] != -1;
535559
}
536560

537-
int64_t RoutingSolution::GetNextNodeIndex(int64_t node_index) const {
538-
return BelongsToInitializedRoute(node_index)
539-
? nexts_[node_index]
540-
: assignment_->Value(model_.NextVar(node_index));
561+
int64_t RoutingSolution::GetNextNodeIndex(int64_t node) const {
562+
return BelongsToInitializedRoute(node)
563+
? nexts_[node]
564+
: assignment_->Value(model_.NextVar(node));
541565
}
542566

543-
int64_t RoutingSolution::GetInitializedPrevNodeIndex(int64_t node_index) const {
544-
DCHECK(BelongsToInitializedRoute(node_index));
545-
return prevs_[node_index];
567+
int64_t RoutingSolution::GetInitializedPrevNodeIndex(int64_t node) const {
568+
DCHECK(BelongsToInitializedRoute(node));
569+
return prevs_[node];
546570
}
547571

548572
int RoutingSolution::GetRouteSize(int vehicle) const {
549573
DCHECK(BelongsToInitializedRoute(model_.Start(vehicle)));
550574
return route_sizes_[vehicle];
551575
}
552576

553-
bool RoutingSolution::CanBeRemoved(int64_t node_index) const {
554-
return !model_.IsStart(node_index) && !model_.IsEnd(node_index) &&
555-
GetNextNodeIndex(node_index) != node_index;
577+
bool RoutingSolution::CanBeRemoved(int64_t node) const {
578+
return !model_.IsStart(node) && !model_.IsEnd(node) &&
579+
GetNextNodeIndex(node) != node;
556580
}
557581

558-
void RoutingSolution::RemoveNode(int64_t node_index) {
559-
DCHECK(BelongsToInitializedRoute(node_index));
582+
void RoutingSolution::RemoveNode(int64_t node) {
583+
DCHECK(BelongsToInitializedRoute(node));
560584

561-
DCHECK_NE(nexts_[node_index], node_index);
562-
DCHECK_NE(prevs_[node_index], node_index);
585+
DCHECK_NE(nexts_[node], node);
586+
DCHECK_NE(prevs_[node], node);
563587

564-
const int64_t next = nexts_[node_index];
565-
const int64_t prev = prevs_[node_index];
588+
const int64_t next = nexts_[node];
589+
const int64_t prev = prevs_[node];
566590

567-
const int vehicle = assignment_->Value(model_.VehicleVar(node_index));
591+
const int vehicle = assignment_->Value(model_.VehicleVar(node));
568592
--route_sizes_[vehicle];
569593
DCHECK_GE(route_sizes_[vehicle], 0);
570594

571595
nexts_[prev] = next;
572596
prevs_[next] = prev;
573597

574-
nexts_[node_index] = node_index;
575-
prevs_[node_index] = node_index;
598+
nexts_[node] = node;
599+
prevs_[node] = node;
576600
}
577601

578-
void RoutingSolution::RemovePerformedPickupDeliverySibling(int64_t customer) {
579-
DCHECK(!model_.IsStart(customer));
580-
DCHECK(!model_.IsEnd(customer));
602+
void RoutingSolution::RemovePerformedPickupDeliverySibling(int64_t node) {
603+
DCHECK(!model_.IsStart(node));
604+
DCHECK(!model_.IsEnd(node));
581605
if (const std::optional<int64_t> sibling_node =
582606
model_.GetFirstMatchingPickupDeliverySibling(
583-
customer, [this](int64_t node) { return CanBeRemoved(node); });
607+
node,
608+
[this](int64_t candidate_node) {
609+
return CanBeRemoved(candidate_node);
610+
});
584611
sibling_node.has_value()) {
585612
const int sibling_vehicle =
586613
assignment_->Value(model_.VehicleVar(sibling_node.value()));
@@ -736,7 +763,7 @@ CloseRoutesRemovalRuinProcedure::CloseRoutesRemovalRuinProcedure(
736763
/*only_sort_neighbors_for_partial_neighborhoods=*/false})),
737764
num_routes_(num_routes),
738765
rnd_(*rnd),
739-
customer_dist_(0, model->Size() - model->vehicles()),
766+
node_dist_(0, model->Size() - model->vehicles()),
740767
removed_routes_(model->vehicles()) {}
741768

742769
std::function<int64_t(int64_t)> CloseRoutesRemovalRuinProcedure::Ruin(
@@ -748,7 +775,7 @@ std::function<int64_t(int64_t)> CloseRoutesRemovalRuinProcedure::Ruin(
748775
}
749776

750777
const int64_t seed_node =
751-
PickRandomPerformedVisit(model_, *assignment, rnd_, customer_dist_);
778+
PickRandomPerformedVisit(model_, *assignment, rnd_, node_dist_);
752779
if (seed_node == -1) {
753780
return [this, assignment](int64_t node) {
754781
return assignment->Value(model_.NextVar(node));
@@ -782,7 +809,7 @@ std::function<int64_t(int64_t)> CloseRoutesRemovalRuinProcedure::Ruin(
782809
}
783810

784811
return [this, assignment](int64_t node) {
785-
// Shortcut removed routes to remove associated customers.
812+
// Shortcut removed routes to remove associated nodes.
786813
if (model_.IsStart(node)) {
787814
const int route = assignment->Value(model_.VehicleVar(node));
788815
if (removed_routes_[route]) {
@@ -805,7 +832,7 @@ RandomWalkRemovalRuinProcedure::RandomWalkRemovalRuinProcedure(
805832
/*only_sort_neighbors_for_partial_neighborhoods=*/false})),
806833
rnd_(*rnd),
807834
walk_length_(walk_length),
808-
customer_dist_(0, model->Size() - model->vehicles()) {}
835+
node_dist_(0, model->Size() - model->vehicles()) {}
809836

810837
std::function<int64_t(int64_t)> RandomWalkRemovalRuinProcedure::Ruin(
811838
const Assignment* assignment) {
@@ -816,7 +843,7 @@ std::function<int64_t(int64_t)> RandomWalkRemovalRuinProcedure::Ruin(
816843
}
817844

818845
int64_t curr_node =
819-
PickRandomPerformedVisit(model_, *assignment, rnd_, customer_dist_);
846+
PickRandomPerformedVisit(model_, *assignment, rnd_, node_dist_);
820847
if (curr_node == -1) {
821848
return [this, assignment](int64_t node) {
822849
return assignment->Value(model_.NextVar(node));
@@ -891,8 +918,8 @@ int64_t RandomWalkRemovalRuinProcedure::GetNextNodeToRemove(
891918
return neighbor;
892919
}
893920

894-
// If we are not able to find a customer in another route, we are ok
895-
// with taking a customer from the current one.
921+
// If we are not able to find a node in another route, we are ok
922+
// with taking a node from the current one.
896923
// Note that it can be -1 if no removable neighbor was found for the input
897924
// node.
898925
return same_route_closest_neighbor;
@@ -912,15 +939,15 @@ SISRRuinProcedure::SISRRuinProcedure(RoutingModel* model, std::mt19937* rnd,
912939
/*add_vehicle_starts_to_neighbors=*/false,
913940
/*add_vehicle_ends_to_neighbors=*/false,
914941
/*only_sort_neighbors_for_partial_neighborhoods=*/false})),
915-
customer_dist_(0, model->Size() - model->vehicles()),
942+
node_dist_(0, model->Size() - model->vehicles()),
916943
probability_dist_(0.0, 1.0),
917944
ruined_routes_(model->vehicles()),
918945
routing_solution_(*model) {}
919946

920947
std::function<int64_t(int64_t)> SISRRuinProcedure::Ruin(
921948
const Assignment* assignment) {
922949
const int64_t seed_node =
923-
PickRandomPerformedVisit(model_, *assignment, rnd_, customer_dist_);
950+
PickRandomPerformedVisit(model_, *assignment, rnd_, node_dist_);
924951
if (seed_node == -1) {
925952
return [this, assignment](int64_t node) {
926953
return assignment->Value(model_.NextVar(node));
@@ -1109,24 +1136,28 @@ DecisionBuilder* MakePerturbationDecisionBuilder(
11091136
}
11101137

11111138
std::unique_ptr<NeighborAcceptanceCriterion> MakeNeighborAcceptanceCriterion(
1112-
const RoutingModel& model, const RoutingSearchParameters& parameters,
1139+
const RoutingModel& model, const AcceptanceStrategy& acceptance_strategy,
1140+
const NeighborAcceptanceCriterion::SearchState& final_search_state,
11131141
std::mt19937* rnd) {
1114-
CHECK(parameters.has_iterated_local_search_parameters());
1115-
switch (parameters.iterated_local_search_parameters().acceptance_strategy()) {
1116-
case AcceptanceStrategy::GREEDY_DESCENT:
1142+
switch (acceptance_strategy.strategy_case()) {
1143+
case AcceptanceStrategy::kGreedyDescent:
11171144
return std::make_unique<GreedyDescentAcceptanceCriterion>();
1118-
case AcceptanceStrategy::SIMULATED_ANNEALING:
1145+
case AcceptanceStrategy::kSimulatedAnnealing:
11191146
return std::make_unique<SimulatedAnnealingAcceptanceCriterion>(
1120-
MakeCoolingSchedule(model, parameters, rnd), rnd);
1147+
MakeCoolingSchedule(model, acceptance_strategy.simulated_annealing(),
1148+
final_search_state, rnd),
1149+
rnd);
1150+
case AcceptanceStrategy::kAllNodesPerformed:
1151+
return std::make_unique<AllNodesPerformedAcceptanceCriterion>(model);
11211152
default:
11221153
LOG(DFATAL) << "Unsupported acceptance strategy.";
11231154
return nullptr;
11241155
}
11251156
}
11261157

11271158
std::pair<double, double> GetSimulatedAnnealingTemperatures(
1128-
const RoutingModel& model, const SimulatedAnnealingParameters& sa_params,
1129-
std::mt19937* rnd) {
1159+
const RoutingModel& model,
1160+
const SimulatedAnnealingAcceptanceStrategy& sa_params, std::mt19937* rnd) {
11301161
if (!sa_params.automatic_temperatures()) {
11311162
return {sa_params.initial_temperature(), sa_params.final_temperature()};
11321163
}

0 commit comments

Comments
 (0)