From 90e42d96072f023273494550b0d4f7a89fadaabe Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Thu, 3 Aug 2023 11:43:26 -0700 Subject: [PATCH] fix CP routing decisions --- ortools/constraint_solver/routing_search.cc | 95 +++++++++------------ ortools/constraint_solver/routing_search.h | 4 + 2 files changed, 44 insertions(+), 55 deletions(-) diff --git a/ortools/constraint_solver/routing_search.cc b/ortools/constraint_solver/routing_search.cc index 4068db0342..0c53666044 100644 --- a/ortools/constraint_solver/routing_search.cc +++ b/ortools/constraint_solver/routing_search.cc @@ -330,12 +330,7 @@ const Assignment* RoutingFilteredHeuristic::BuildSolutionFromRoutes( while (!model_->IsEnd(node)) { const int64_t next = next_accessor(node); DCHECK_NE(next, node); - SetValue(node, next); - // TODO(user): Add vehicle values to delta when this method will be - // used with cost filtering. The code should be similar to this: - // if (HasSecondaryVars()) { - // SetValue(SecondaryVarIndex(node), v); - // } + SetNext(node, next, v); SetVehicleIndex(node, v); node = next; } @@ -430,10 +425,7 @@ bool RoutingFilteredHeuristic::InitializeSolution() { int64_t node = model()->Start(vehicle); while (!model()->IsEnd(node) && Var(node)->Bound()) { const int64_t next = Var(node)->Min(); - SetValue(node, next); - if (HasSecondaryVars()) { - SetValue(SecondaryVarIndex(node), vehicle); - } + SetNext(node, next, vehicle); SetVehicleIndex(node, vehicle); node = next; } @@ -448,18 +440,12 @@ bool RoutingFilteredHeuristic::InitializeSolution() { int64_t node = start_chain_ends_[vehicle]; if (!model()->IsEnd(node)) { int64_t next = end_chain_starts_[vehicle]; - SetValue(node, next); - if (HasSecondaryVars()) { - SetValue(SecondaryVarIndex(node), vehicle); - } + SetNext(node, next, vehicle); SetVehicleIndex(node, vehicle); node = next; while (!model()->IsEnd(node)) { next = Var(node)->Min(); - SetValue(node, next); - if (HasSecondaryVars()) { - SetValue(SecondaryVarIndex(node), vehicle); - } + SetNext(node, next, vehicle); SetVehicleIndex(node, vehicle); node = next; } @@ -477,7 +463,7 @@ void RoutingFilteredHeuristic::MakeDisjunctionNodesUnperformed(int64_t node) { model()->ForEachNodeInDisjunctionWithMaxCardinalityFromIndex( node, 1, [this, node](int alternate) { if (node != alternate && !Contains(alternate)) { - SetValue(alternate, alternate); + SetNext(alternate, alternate, -1); } }); } @@ -488,10 +474,7 @@ bool RoutingFilteredHeuristic::MakeUnassignedNodesUnperformed() { for (int index = 0; index < model_->Size(); ++index) { DCHECK(!IsSecondaryVar(index)); if (!Contains(index)) { - SetValue(index, index); - if (HasSecondaryVars()) { - SetValue(SecondaryVarIndex(index), -1); - } + SetNext(index, index, -1); } } return true; @@ -527,11 +510,13 @@ void RoutingFilteredHeuristic::MakePartiallyPerformedPairsUnperformed() { } for (int index = 0; index < num_nexts; ++index) { if (to_make_unperformed[index] || !Contains(index)) continue; + const int vehicle = + HasSecondaryVars() ? Value(SecondaryVarIndex(index)) : 0; int64_t next = Value(index); while (next < num_nexts && to_make_unperformed[next]) { const int64_t next_of_next = Value(next); - SetValue(index, next_of_next); - SetValue(next, next); + SetNext(index, next_of_next, vehicle); + SetNext(next, next, -1); next = next_of_next; } } @@ -888,8 +873,8 @@ bool GlobalCheapestInsertionFilteredHeuristic::InsertPairs( const int entry_vehicle = entry->vehicle(); if (entry_vehicle == -1) { // Pair is unperformed. - SetValue(pickup, pickup); - SetValue(delivery, delivery); + SetNext(pickup, pickup, -1); + SetNext(delivery, delivery, -1); if (!Evaluate(/*commit=*/true).has_value()) { DeletePairEntry(entry, &priority_queue, &pickup_to_entries, &delivery_to_entries); @@ -1212,7 +1197,7 @@ bool GlobalCheapestInsertionFilteredHeuristic::InsertNodesOnRoutes( if (entry_vehicle == -1) { DCHECK(all_vehicles); // Make node unperformed. - SetValue(node_to_insert, node_to_insert); + SetNext(node_to_insert, node_to_insert, -1); if (!Evaluate(/*commit=*/true).has_value()) { queue.Pop(); } @@ -2770,13 +2755,13 @@ bool CheapestAdditionFilteredHeuristic::BuildSolutionInternal() { } // Insert "next" after "index", and before "end" if it is not the // end already. - SetValue(index, next); + SetNext(index, next, vehicle); if (!model()->IsEnd(next)) { - SetValue(next, end); + SetNext(next, end, vehicle); MakeDisjunctionNodesUnperformed(next); if (delivery != kUnassigned) { - SetValue(next, delivery); - SetValue(delivery, end); + SetNext(next, delivery, vehicle); + SetNext(delivery, end, vehicle); MakeDisjunctionNodesUnperformed(delivery); } } @@ -3358,11 +3343,9 @@ int SavingsFilteredHeuristic::StartNewRouteWithBestVehicleOfType( } // Try to commit the arc on this vehicle. DCHECK(VehicleIsEmpty(vehicle)); - const int64_t start = model()->Start(vehicle); - const int64_t end = model()->End(vehicle); - SetValue(start, before_node); - SetValue(before_node, after_node); - SetValue(after_node, end); + SetNext(model()->Start(vehicle), before_node, vehicle); + SetNext(before_node, after_node, vehicle); + SetNext(after_node, model()->End(vehicle), vehicle); return Evaluate(/*commit=*/true).has_value(); }; @@ -3637,8 +3620,8 @@ void SequentialSavingsFilteredHeuristic::BuildRoutesFromSavings() { ++in_index; // Extending after after_node if (!Contains(after_after_node)) { - SetValue(after_node, after_after_node); - SetValue(after_after_node, end); + SetNext(after_node, after_after_node, vehicle); + SetNext(after_after_node, end, vehicle); if (Evaluate(/*commit=*/true).has_value()) { in_index = 0; after_node = after_after_node; @@ -3649,8 +3632,8 @@ void SequentialSavingsFilteredHeuristic::BuildRoutesFromSavings() { CHECK_GE(before_before_node, 0); ++out_index; if (!Contains(before_before_node)) { - SetValue(start, before_before_node); - SetValue(before_before_node, before_node); + SetNext(start, before_before_node, vehicle); + SetNext(before_before_node, before_node, vehicle); if (Evaluate(/*commit=*/true).has_value()) { out_index = 0; before_node = before_before_node; @@ -3764,8 +3747,8 @@ void ParallelSavingsFilteredHeuristic::BuildRoutesFromSavings() { } // Try adding after_node on route of before_node. - SetValue(before_node, after_node); - SetValue(after_node, end); + SetNext(before_node, after_node, vehicle); + SetNext(after_node, end, vehicle); if (Evaluate(/*commit=*/true).has_value()) { if (first_node_on_route_[vehicle] != before_node) { // before_node is no longer the start or end of its route @@ -3797,8 +3780,8 @@ void ParallelSavingsFilteredHeuristic::BuildRoutesFromSavings() { } // Try adding before_node on route of after_node. - SetValue(before_node, after_node); - SetValue(start, before_node); + SetNext(before_node, after_node, vehicle); + SetNext(start, before_node, vehicle); if (Evaluate(/*commit=*/true).has_value()) { if (last_node_on_route_[vehicle] != after_node) { // after_node is no longer the start or end of its route @@ -3836,12 +3819,13 @@ void ParallelSavingsFilteredHeuristic::MergeRoutes(int first_vehicle, unused_vehicle = first_vehicle; } - SetValue(before_node, after_node); - SetValue(model()->Start(unused_vehicle), model()->End(unused_vehicle)); + SetNext(before_node, after_node, used_vehicle); + SetNext(model()->Start(unused_vehicle), model()->End(unused_vehicle), + unused_vehicle); if (used_vehicle == first_vehicle) { - SetValue(new_last_node, model()->End(used_vehicle)); + SetNext(new_last_node, model()->End(used_vehicle), used_vehicle); } else { - SetValue(model()->Start(used_vehicle), new_first_node); + SetNext(model()->Start(used_vehicle), new_first_node, used_vehicle); } bool committed = Evaluate(/*commit=*/true).has_value(); if (!committed && @@ -3849,12 +3833,13 @@ void ParallelSavingsFilteredHeuristic::MergeRoutes(int first_vehicle, model()->GetVehicleClassIndexOfVehicle(second_vehicle).value()) { // Try committing on other vehicle instead. std::swap(used_vehicle, unused_vehicle); - SetValue(before_node, after_node); - SetValue(model()->Start(unused_vehicle), model()->End(unused_vehicle)); + SetNext(before_node, after_node, used_vehicle); + SetNext(model()->Start(unused_vehicle), model()->End(unused_vehicle), + unused_vehicle); if (used_vehicle == first_vehicle) { - SetValue(new_last_node, model()->End(used_vehicle)); + SetNext(new_last_node, model()->End(used_vehicle), used_vehicle); } else { - SetValue(model()->Start(used_vehicle), new_first_node); + SetNext(model()->Start(used_vehicle), new_first_node, used_vehicle); } committed = Evaluate(/*commit=*/true).has_value(); } @@ -3951,8 +3936,8 @@ bool ChristofidesFilteredHeuristic::BuildSolutionInternal() { if (StopSearch()) return false; int next = indices[path[i]]; if (!Contains(next)) { - SetValue(prev, next); - SetValue(next, end); + SetNext(prev, next, vehicle); + SetNext(next, end, vehicle); if (Evaluate(/*commit=*/true).has_value()) { prev = next; } diff --git a/ortools/constraint_solver/routing_search.h b/ortools/constraint_solver/routing_search.h index 878be38b48..2b4d1c8859 100644 --- a/ortools/constraint_solver/routing_search.h +++ b/ortools/constraint_solver/routing_search.h @@ -299,6 +299,10 @@ class RoutingFilteredHeuristic : public IntVarFilteredHeuristic { bool VehicleIsEmpty(int vehicle) const { return Value(model()->Start(vehicle)) == model()->End(vehicle); } + void SetNext(int64_t node, int64_t next, int vehicle) { + SetValue(node, next); + if (HasSecondaryVars()) SetValue(SecondaryVarIndex(node), vehicle); + } private: /// Initializes the current solution with empty or partial vehicle routes.