From ffd8ec0aa0fa48dead819976dbecbfef9593c0b7 Mon Sep 17 00:00:00 2001 From: Corentin Le Molgat Date: Wed, 26 Jul 2023 18:17:44 +0200 Subject: [PATCH] export constraint_solver form google3/ --- ortools/constraint_solver/routing.cc | 9 +- .../constraint_solver/routing_parameters.cc | 2 + .../routing_parameters.proto | 10 +- ortools/constraint_solver/routing_search.cc | 110 ++++++++++++------ ortools/constraint_solver/routing_search.h | 11 +- 5 files changed, 95 insertions(+), 47 deletions(-) diff --git a/ortools/constraint_solver/routing.cc b/ortools/constraint_solver/routing.cc index 03dffc13be2..e945f6822bd 100644 --- a/ortools/constraint_solver/routing.cc +++ b/ortools/constraint_solver/routing.cc @@ -4866,20 +4866,21 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders( [FirstSolutionStrategy::BEST_INSERTION])); // Local cheapest cost insertion + const RoutingSearchParameters::PairInsertionStrategy lcci_pair_strategy = + search_parameters + .local_cheapest_cost_insertion_pickup_delivery_strategy(); first_solution_filtered_decision_builders_ [FirstSolutionStrategy::LOCAL_CHEAPEST_COST_INSERTION] = CreateIntVarFilteredDecisionBuilder< LocalCheapestInsertionFilteredHeuristic>( - /*evaluator=*/nullptr, - RoutingSearchParameters::BEST_PICKUP_DELIVERY_PAIR, + /*evaluator=*/nullptr, lcci_pair_strategy, GetOrCreateLocalSearchFilterManager( search_parameters, {/*filter_objective=*/true, /*filter_with_cp_solver=*/false})); IntVarFilteredDecisionBuilder* const strong_lcci = CreateIntVarFilteredDecisionBuilder< LocalCheapestInsertionFilteredHeuristic>( - /*evaluator=*/nullptr, - RoutingSearchParameters::BEST_PICKUP_DELIVERY_PAIR, + /*evaluator=*/nullptr, lcci_pair_strategy, GetOrCreateLocalSearchFilterManager( search_parameters, {/*filter_objective=*/true, /*filter_with_cp_solver=*/true})); diff --git a/ortools/constraint_solver/routing_parameters.cc b/ortools/constraint_solver/routing_parameters.cc index 82b445bb4e2..dddca1fb211 100644 --- a/ortools/constraint_solver/routing_parameters.cc +++ b/ortools/constraint_solver/routing_parameters.cc @@ -68,6 +68,8 @@ RoutingSearchParameters CreateDefaultRoutingSearchParameters() { p.set_cheapest_insertion_add_unperformed_entries(false); p.set_local_cheapest_insertion_pickup_delivery_strategy( RoutingSearchParameters::BEST_PICKUP_THEN_BEST_DELIVERY); + p.set_local_cheapest_cost_insertion_pickup_delivery_strategy( + RoutingSearchParameters::BEST_PICKUP_DELIVERY_PAIR); RoutingSearchParameters::LocalSearchNeighborhoodOperators* o = p.mutable_local_search_operators(); o->set_use_relocate(BOOL_TRUE); diff --git a/ortools/constraint_solver/routing_parameters.proto b/ortools/constraint_solver/routing_parameters.proto index e432d6bfafd..36d378c2039 100644 --- a/ortools/constraint_solver/routing_parameters.proto +++ b/ortools/constraint_solver/routing_parameters.proto @@ -35,7 +35,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: 55 +// Next ID: 56 message RoutingSearchParameters { // First solution strategies, used as starting point of local search. FirstSolutionStrategy.Value first_solution_strategy = 1; @@ -123,9 +123,13 @@ message RoutingSearchParameters { // Order by increasing by cost(pickup) + cost(delivery). BEST_PICKUP_DELIVERY_PAIR_MULTITOUR = 3; } - // Choice of insertion strategy for pickup/delivery pairs, - // used in local cheapest insertion, both first solution heuristic and LNS. + // Choice of insertion strategy for pickup/delivery pairs, used in local + // cheapest insertion, both first solution heuristic and LNS. PairInsertionStrategy local_cheapest_insertion_pickup_delivery_strategy = 49; + // Choice of insertion strategy for pickup/delivery pairs, used in local + // cheapest cost insertion, both first solution heuristic and LNS. + PairInsertionStrategy local_cheapest_cost_insertion_pickup_delivery_strategy = + 55; // If true use minimum matching instead of minimal matching in the // Christofides algorithm. diff --git a/ortools/constraint_solver/routing_search.cc b/ortools/constraint_solver/routing_search.cc index 0134b05877e..64114355400 100644 --- a/ortools/constraint_solver/routing_search.cc +++ b/ortools/constraint_solver/routing_search.cc @@ -2225,16 +2225,13 @@ LocalCheapestInsertionFilteredHeuristic:: RoutingModel* model, std::function stop_search, std::function evaluator, RoutingSearchParameters::PairInsertionStrategy pair_insertion_strategy, - LocalSearchFilterManager* filter_manager) + LocalSearchFilterManager* filter_manager, BinCapacities* bin_capacities) : CheapestInsertionFilteredHeuristic(model, std::move(stop_search), std::move(evaluator), nullptr, filter_manager), update_start_end_distances_per_node_(true), - pair_insertion_strategy_(pair_insertion_strategy) { - DCHECK(evaluator_ != nullptr || - pair_insertion_strategy_ == - RoutingSearchParameters::BEST_PICKUP_DELIVERY_PAIR); -} + pair_insertion_strategy_(pair_insertion_strategy), + bin_capacities_(bin_capacities) {} void LocalCheapestInsertionFilteredHeuristic::Initialize() { // Avoid recomputing if used in a local search operator. @@ -2263,19 +2260,27 @@ bool LocalCheapestInsertionFilteredHeuristic::InsertPair( void LocalCheapestInsertionFilteredHeuristic::InsertBestPickupThenDelivery( const RoutingModel::IndexPair& index_pair) { - for (int64_t pickup : index_pair.first) { + for (int pickup : index_pair.first) { std::vector pickup_insertions = ComputeEvaluatorSortedPositions(pickup); - for (int64_t delivery : index_pair.second) { + for (int delivery : index_pair.second) { if (StopSearch()) return; for (const NodeInsertion& pickup_insertion : pickup_insertions) { const int vehicle = pickup_insertion.vehicle; + if (bin_capacities_ && !bin_capacities_->CheckAdditionsFeasibility( + {pickup, delivery}, vehicle)) { + continue; + } for (const NodeInsertion& delivery_insertion : ComputeEvaluatorSortedPositionsOnRouteAfter( delivery, pickup, Value(pickup_insertion.insert_after), vehicle)) { if (InsertPair(pickup, pickup_insertion.insert_after, delivery, delivery_insertion.insert_after, vehicle)) { + if (bin_capacities_) { + bin_capacities_->AddItemToBin(pickup, vehicle); + bin_capacities_->AddItemToBin(delivery, vehicle); + } return; } } @@ -2287,17 +2292,20 @@ void LocalCheapestInsertionFilteredHeuristic::InsertBestPickupThenDelivery( void LocalCheapestInsertionFilteredHeuristic::InsertBestPair( const RoutingModel::IndexPair& index_pair) { - for (int64_t pickup : index_pair.first) { - for (int64_t delivery : index_pair.second) { + for (int pickup : index_pair.first) { + for (int delivery : index_pair.second) { if (StopSearch()) return; - std::optional> - sorted_pair_positions = - ComputeEvaluatorSortedPairPositions(pickup, delivery); - if (!sorted_pair_positions.has_value()) return; + std::vector sorted_pair_positions = + ComputeEvaluatorSortedPairPositions(pickup, delivery); + if (sorted_pair_positions.empty()) continue; for (const auto [insert_pickup_after, insert_delivery_after, unused_value, - vehicle] : *sorted_pair_positions) { + vehicle] : sorted_pair_positions) { if (InsertPair(pickup, insert_pickup_after, delivery, insert_delivery_after, vehicle)) { + if (bin_capacities_) { + bin_capacities_->AddItemToBin(pickup, vehicle); + bin_capacities_->AddItemToBin(delivery, vehicle); + } return; } if (StopSearch()) return; @@ -2361,11 +2369,15 @@ void LocalCheapestInsertionFilteredHeuristic::InsertBestPairMultitour( } }; - for (int64_t pickup : index_pair.first) { + for (int pickup : index_pair.first) { if (StopSearch()) return; - for (int64_t delivery : index_pair.second) { + for (int delivery : index_pair.second) { insertion_container_.Clear(); for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) { + if (bin_capacities_ && !bin_capacities_->CheckAdditionsFeasibility( + {pickup, delivery}, vehicle)) { + continue; + } fill_path(vehicle); insertion_generator_.AppendPickupDeliveryMultitourInsertions( pickup, delivery, vehicle, path, node_is_pickup, node_is_delivery, @@ -2396,7 +2408,14 @@ void LocalCheapestInsertionFilteredHeuristic::InsertBestPairMultitour( previous_node = insertion.node; previous_succ = succ; } - if (Evaluate(/*commit=*/true).has_value()) return; + if (Evaluate(/*commit=*/true).has_value()) { + // Insertion succeeded. + if (bin_capacities_) { + bin_capacities_->AddItemToBin(pickup, vehicle); + bin_capacities_->AddItemToBin(delivery, vehicle); + } + return; + } } } } @@ -2450,6 +2469,7 @@ bool LocalCheapestInsertionFilteredHeuristic::BuildSolutionInternal() { } } std::sort(pair_domain_sizes.begin(), pair_domain_sizes.end()); + // Multitour needs to know if a node is a pickup, delivery, or single. std::vector node_is_pickup, node_is_delivery; if (pair_insertion_strategy_ == RoutingSearchParameters::BEST_PICKUP_DELIVERY_PAIR_MULTITOUR) { @@ -2465,6 +2485,16 @@ bool LocalCheapestInsertionFilteredHeuristic::BuildSolutionInternal() { } } } + // Fill vehicle bins with nodes that are already inserted. + if (bin_capacities_) { + bin_capacities_->ClearItems(); + for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) { + const int start = Value(model()->Start(vehicle)); + for (int node = start; !model()->IsEnd(node); node = Value(node)) { + bin_capacities_->AddItemToBin(node, vehicle); + } + } + } // Try to insert each pair by increasing amount of its possible vehicles. for (const PairDomainSize& pair_domain_size : pair_domain_sizes) { @@ -2506,6 +2536,9 @@ bool LocalCheapestInsertionFilteredHeuristic::BuildSolutionInternal() { InsertBetween(node, insertion.insert_after, Value(insertion.insert_after), insertion.vehicle); if (Evaluate(/*commit=*/true).has_value()) { + if (bin_capacities_) { + bin_capacities_->AddItemToBin(node, insertion.vehicle); + } break; } } @@ -2517,16 +2550,19 @@ std::vector LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPositions( int64_t node) { DCHECK(!Contains(node)); - std::vector sorted_insertions; const int size = model()->Size(); - if (node < size) { - for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) { - const int64_t start = model()->Start(vehicle); - AppendInsertionPositionsAfter(node, start, Value(start), vehicle, - /*ignore_cost=*/false, &sorted_insertions); + if (node >= size) return {}; + std::vector sorted_insertions; + for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) { + if (bin_capacities_ && + !bin_capacities_->CheckAdditionFeasibility(node, vehicle)) { + continue; } - std::sort(sorted_insertions.begin(), sorted_insertions.end()); + const int64_t start = model()->Start(vehicle); + AppendInsertionPositionsAfter(node, start, Value(start), vehicle, + /*ignore_cost=*/false, &sorted_insertions); } + std::sort(sorted_insertions.begin(), sorted_insertions.end()); return sorted_insertions; } @@ -2536,30 +2572,33 @@ LocalCheapestInsertionFilteredHeuristic:: int64_t next_after_start, int vehicle) { DCHECK(!Contains(node)); - std::vector sorted_insertions; const int size = model()->Size(); - if (node < size) { - AppendInsertionPositionsAfter(node, start, next_after_start, vehicle, - /*ignore_cost=*/false, &sorted_insertions); - std::sort(sorted_insertions.begin(), sorted_insertions.end()); - } + if (node >= size) return {}; + std::vector sorted_insertions; + AppendInsertionPositionsAfter(node, start, next_after_start, vehicle, + /*ignore_cost=*/false, &sorted_insertions); + std::sort(sorted_insertions.begin(), sorted_insertions.end()); return sorted_insertions; } -std::optional> +std::vector LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPairPositions( - int64_t pickup, int64_t delivery) { + int pickup, int delivery) { std::vector sorted_pickup_delivery_insertions; const int size = model()->Size(); DCHECK_LT(pickup, size); DCHECK_LT(delivery, size); for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) { + if (bin_capacities_ && !bin_capacities_->CheckAdditionsFeasibility( + {pickup, delivery}, vehicle)) { + continue; + } int64_t insert_pickup_after = model()->Start(vehicle); while (!model()->IsEnd(insert_pickup_after)) { const int64_t insert_pickup_before = Value(insert_pickup_after); int64_t insert_delivery_after = pickup; while (!model()->IsEnd(insert_delivery_after)) { - if (StopSearch()) return std::nullopt; + if (StopSearch()) return {}; const int64_t insert_delivery_before = insert_delivery_after == pickup ? insert_pickup_before : Value(insert_delivery_after); @@ -2592,8 +2631,7 @@ LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPairPositions( } std::sort(sorted_pickup_delivery_insertions.begin(), sorted_pickup_delivery_insertions.end()); - return std::optional>{ - sorted_pickup_delivery_insertions}; + return sorted_pickup_delivery_insertions; } // CheapestAdditionFilteredHeuristic diff --git a/ortools/constraint_solver/routing_search.h b/ortools/constraint_solver/routing_search.h index 9ce5e4eb9cb..ccbfc431500 100644 --- a/ortools/constraint_solver/routing_search.h +++ b/ortools/constraint_solver/routing_search.h @@ -44,6 +44,7 @@ #include "ortools/constraint_solver/constraint_solveri.h" #include "ortools/constraint_solver/routing.h" #include "ortools/constraint_solver/routing_index_manager.h" +#include "ortools/constraint_solver/routing_utils.h" #include "ortools/util/bitset.h" namespace operations_research { @@ -1032,7 +1033,8 @@ class LocalCheapestInsertionFilteredHeuristic RoutingModel* model, std::function stop_search, std::function evaluator, RoutingSearchParameters::PairInsertionStrategy pair_insertion_strategy, - LocalSearchFilterManager* filter_manager); + LocalSearchFilterManager* filter_manager, + BinCapacities* bin_capacities = nullptr); ~LocalCheapestInsertionFilteredHeuristic() override {} bool BuildSolutionInternal() override; std::string DebugString() const override { @@ -1056,9 +1058,9 @@ class LocalCheapestInsertionFilteredHeuristic /// Computes the possible simultaneous insertion positions of the pair /// 'pickup' and 'delivery'. Sorts them according to the current cost - /// evaluator. If a timeout is detected returns std::nullopt. - std::optional> - ComputeEvaluatorSortedPairPositions(int64_t pickup, int64_t delivery); + /// evaluator. + std::vector ComputeEvaluatorSortedPairPositions( + int pickup, int delivery); // Tries to insert any alternative of the given pair, // ordered by cost of pickup insertion, then by cost of delivery insertion. @@ -1086,6 +1088,7 @@ class LocalCheapestInsertionFilteredHeuristic // Marks whether a node has already been tried for insertion. std::vector visited_; + BinCapacities* const bin_capacities_; }; /// Filtered-base decision builder based on the addition heuristic, extending