@@ -368,33 +368,20 @@ class LinearCoolingSchedule : public CoolingSchedule {
368368
369369// Returns a cooling schedule based on the given input parameters.
370370std::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.
431455bool HasPerformedNodes (const RoutingModel& model,
432456 const Assignment& assignment) {
@@ -462,21 +486,21 @@ double ComputeAverageNonEmptyRouteSize(const RoutingModel& model,
462486// performed visits.
463487int64_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
548572int 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
742769std::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
810837std::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
920947std::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
11111138std::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
11271158std::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