From ea802abb52dcd27d25373d50b6f9f9267a8a5b17 Mon Sep 17 00:00:00 2001 From: Corentin Le Molgat Date: Mon, 5 Aug 2024 17:08:45 +0200 Subject: [PATCH] routing: Add routing sub namespace --- ortools/constraint_solver/constraint_solver.h | 53 +- .../constraint_solver/constraint_solveri.h | 7 +- ortools/constraint_solver/search.cc | 126 ++++- ortools/routing/breaks.cc | 4 +- ortools/routing/constraints.cc | 4 +- ortools/routing/constraints.h | 4 +- ortools/routing/decision_builders.cc | 4 +- ortools/routing/decision_builders.h | 4 +- ortools/routing/docs/ROUTING.md | 4 +- ortools/routing/filters.cc | 6 +- ortools/routing/filters.h | 6 +- ortools/routing/flow.cc | 4 +- ortools/routing/ils.cc | 98 ++-- ortools/routing/ils.h | 4 +- ortools/routing/index_manager.cc | 4 +- ortools/routing/index_manager.h | 4 +- ortools/routing/insertion_lns.cc | 4 +- ortools/routing/insertion_lns.h | 4 +- ortools/routing/lp_scheduling.cc | 4 +- ortools/routing/lp_scheduling.h | 4 +- ortools/routing/neighborhoods.cc | 4 +- ortools/routing/neighborhoods.h | 4 +- ortools/routing/parameters.cc | 41 +- ortools/routing/parameters.h | 4 +- ortools/routing/parameters.proto | 27 +- ortools/routing/parsers/carp_parser.cc | 4 +- ortools/routing/parsers/carp_parser.h | 4 +- ortools/routing/parsers/carp_parser_test.cc | 4 +- ortools/routing/parsers/cvrptw_lib.cc | 4 +- ortools/routing/parsers/cvrptw_lib.h | 4 +- ortools/routing/parsers/dow_parser.cc | 4 +- ortools/routing/parsers/dow_parser.h | 4 +- ortools/routing/parsers/dow_parser_test.cc | 4 +- ortools/routing/parsers/lilim_parser.cc | 4 +- ortools/routing/parsers/lilim_parser.h | 4 +- ortools/routing/parsers/lilim_parser_test.cc | 4 +- ortools/routing/parsers/nearp_parser.cc | 4 +- ortools/routing/parsers/nearp_parser.h | 4 +- ortools/routing/parsers/nearp_parser_test.cc | 4 +- ortools/routing/parsers/pdtsp_parser.cc | 4 +- ortools/routing/parsers/pdtsp_parser.h | 4 +- ortools/routing/parsers/pdtsp_parser_test.cc | 4 +- ortools/routing/parsers/simple_graph.cc | 4 +- ortools/routing/parsers/simple_graph.h | 4 +- ortools/routing/parsers/simple_graph_test.cc | 4 +- ortools/routing/parsers/solomon_parser.cc | 4 +- ortools/routing/parsers/solomon_parser.h | 4 +- .../routing/parsers/solomon_parser_test.cc | 4 +- .../routing/parsers/solution_serializer.cc | 4 +- ortools/routing/parsers/solution_serializer.h | 4 +- .../parsers/solution_serializer_test.cc | 4 +- ortools/routing/parsers/tsplib_parser.cc | 4 +- ortools/routing/parsers/tsplib_parser.h | 4 +- ortools/routing/parsers/tsplib_parser_test.cc | 4 +- ortools/routing/parsers/tsptw_parser.cc | 4 +- ortools/routing/parsers/tsptw_parser.h | 4 +- ortools/routing/parsers/tsptw_parser_test.cc | 4 +- ortools/routing/routing.cc | 470 +++++++++++------- ortools/routing/routing.h | 90 +++- .../routing/samples/simple_routing_program.cc | 4 +- ortools/routing/samples/tsp.cc | 4 +- ortools/routing/samples/tsp_circuit_board.cc | 4 +- ortools/routing/samples/tsp_cities.cc | 4 +- ortools/routing/samples/tsp_cities_routes.cc | 4 +- .../routing/samples/tsp_distance_matrix.cc | 4 +- ortools/routing/samples/vrp.cc | 4 +- ortools/routing/samples/vrp_breaks.cc | 4 +- ortools/routing/samples/vrp_capacity.cc | 4 +- ortools/routing/samples/vrp_drop_nodes.cc | 4 +- ortools/routing/samples/vrp_global_span.cc | 4 +- ortools/routing/samples/vrp_initial_routes.cc | 4 +- .../routing/samples/vrp_pickup_delivery.cc | 4 +- .../samples/vrp_pickup_delivery_fifo.cc | 4 +- .../samples/vrp_pickup_delivery_lifo.cc | 4 +- ortools/routing/samples/vrp_resources.cc | 4 +- ortools/routing/samples/vrp_routes.cc | 4 +- .../routing/samples/vrp_solution_callback.cc | 4 +- ortools/routing/samples/vrp_starts_ends.cc | 4 +- ortools/routing/samples/vrp_time_windows.cc | 4 +- .../routing/samples/vrp_with_time_limit.cc | 4 +- .../samples/vrptw_store_solution_data.cc | 4 +- ortools/routing/sat.cc | 21 +- ortools/routing/search.cc | 99 ++-- ortools/routing/search.h | 16 +- ortools/routing/types.h | 4 +- ortools/routing/utils.cc | 4 +- ortools/routing/utils.h | 4 +- 87 files changed, 891 insertions(+), 465 deletions(-) diff --git a/ortools/constraint_solver/constraint_solver.h b/ortools/constraint_solver/constraint_solver.h index ad6983c768b..8750b2242b5 100644 --- a/ortools/constraint_solver/constraint_solver.h +++ b/ortools/constraint_solver/constraint_solver.h @@ -140,6 +140,7 @@ class LocalSearchProfiler; class ModelCache; class ModelVisitor; class ObjectiveMonitor; +class BaseObjectiveMonitor; class OptimizeVar; class Pack; class ProfiledDecisionBuilder; @@ -2364,6 +2365,14 @@ class Solver { bool reset_penalties_on_new_best_solution = false); #endif + // Creates a composite objective monitor which alternates between objective + // monitors every time the search reaches a local optimum local optimium + // reached. This will stop if all monitors return false when LocalOptimium is + // called. + BaseObjectiveMonitor* MakeRoundRobinCompoundObjectiveMonitor( + std::vector monitors, + int num_max_local_optima_before_metaheuristic_switch); + /// This search monitor will restart the search periodically. /// At the iteration n, it will restart after scale_factor * Luby(n) failures /// where Luby is the Luby Strategy (i.e. 1 1 2 1 1 2 4 1 1 2 1 1 2 4 8...). @@ -4477,8 +4486,32 @@ class SolutionCollector : public SearchMonitor { #endif // SWIG }; -// Base objective monitor class. All metaheuristics derive from this. -class ObjectiveMonitor : public SearchMonitor { +// Base objective monitor class. All metaheuristics and metaheuristic combiners +// derive from this. +class BaseObjectiveMonitor : public SearchMonitor { + public: + explicit BaseObjectiveMonitor(Solver* solver) : SearchMonitor(solver) {} + ~BaseObjectiveMonitor() override {} +#ifndef SWIG + BaseObjectiveMonitor(const BaseObjectiveMonitor&) = delete; + BaseObjectiveMonitor& operator=(const BaseObjectiveMonitor&) = delete; +#endif // SWIG + virtual IntVar* ObjectiveVar(int index) const = 0; + virtual IntVar* MinimizationVar(int index) const = 0; + virtual int64_t Step(int index) const = 0; + virtual bool Maximize(int index) const = 0; + virtual int64_t BestValue(int index) const = 0; + virtual int Size() const = 0; + bool is_active() const { return is_active_; } + void set_active(bool is_active) { is_active_ = is_active; } + + private: + bool is_active_ = true; +}; + +// Base atomic objective monitor class. All non-composite metaheuristics derive +// from this. +class ObjectiveMonitor : public BaseObjectiveMonitor { public: ObjectiveMonitor(Solver* solver, const std::vector& maximize, std::vector vars, std::vector steps); @@ -4487,17 +4520,21 @@ class ObjectiveMonitor : public SearchMonitor { ObjectiveMonitor(const ObjectiveMonitor&) = delete; ObjectiveMonitor& operator=(const ObjectiveMonitor&) = delete; #endif // SWIG - IntVar* ObjectiveVar(int index) const { return objective_vars_[index]; } - IntVar* MinimizationVar(int index) const { return minimization_vars_[index]; } - int64_t Step(int index) const { return steps_[index]; } - bool Maximize(int index) const { + IntVar* ObjectiveVar(int index) const override { + return objective_vars_[index]; + } + IntVar* MinimizationVar(int index) const override { + return minimization_vars_[index]; + } + int64_t Step(int index) const override { return steps_[index]; } + bool Maximize(int index) const override { return ObjectiveVar(index) != MinimizationVar(index); } - int64_t BestValue(int index) const { + int64_t BestValue(int index) const override { return Maximize(index) ? CapOpp(BestInternalValue(index)) : BestInternalValue(index); } - int Size() const { return objective_vars_.size(); } + int Size() const override { return objective_vars_.size(); } void EnterSearch() override; bool AtSolution() override; bool AcceptDelta(Assignment* delta, Assignment* deltadelta) override; diff --git a/ortools/constraint_solver/constraint_solveri.h b/ortools/constraint_solver/constraint_solveri.h index 3e3e76f49f3..ff7911e5b73 100644 --- a/ortools/constraint_solver/constraint_solveri.h +++ b/ortools/constraint_solver/constraint_solveri.h @@ -587,10 +587,9 @@ class CallMethod2 : public Demon { } std::string DebugString() const override { - return absl::StrCat(absl::StrCat("CallMethod_", name_), - absl::StrCat("(", constraint_->DebugString()), - absl::StrCat(", ", ParameterDebugString(param1_)), - absl::StrCat(", ", ParameterDebugString(param2_), ")")); + return absl::StrCat("CallMethod_", name_, "(", constraint_->DebugString(), + ", ", ParameterDebugString(param1_), ", ", + ParameterDebugString(param2_), ")"); } private: diff --git a/ortools/constraint_solver/search.cc b/ortools/constraint_solver/search.cc index 51d8ef5c6dd..9be936d4585 100644 --- a/ortools/constraint_solver/search.cc +++ b/ortools/constraint_solver/search.cc @@ -2963,11 +2963,116 @@ SolutionCollector* Solver::MakeAllSolutionCollector() { // ---------- Objective Management ---------- +class RoundRobinCompoundObjectiveMonitor : public BaseObjectiveMonitor { + public: + RoundRobinCompoundObjectiveMonitor( + std::vector monitors, + int num_max_local_optima_before_metaheuristic_switch) + : BaseObjectiveMonitor(monitors[0]->solver()), + monitors_(std::move(monitors)), + enabled_monitors_(monitors_.size(), true), + local_optimum_limit_(num_max_local_optima_before_metaheuristic_switch) { + } + void EnterSearch() override { + active_monitor_ = 0; + num_local_optimum_ = 0; + enabled_monitors_.assign(monitors_.size(), true); + for (auto& monitor : monitors_) { + monitor->set_active(monitor == monitors_[active_monitor_]); + monitor->EnterSearch(); + } + } + void ApplyDecision(Decision* d) override { + monitors_[active_monitor_]->ApplyDecision(d); + } + void AcceptNeighbor() override { + monitors_[active_monitor_]->AcceptNeighbor(); + } + bool AtSolution() override { + bool ok = true; + for (auto& monitor : monitors_) { + ok &= monitor->AtSolution(); + } + return ok; + } + bool AcceptDelta(Assignment* delta, Assignment* deltadelta) override { + return monitors_[active_monitor_]->AcceptDelta(delta, deltadelta); + } + void BeginNextDecision(DecisionBuilder* db) override { + monitors_[active_monitor_]->BeginNextDecision(db); + } + void RefuteDecision(Decision* d) override { + monitors_[active_monitor_]->RefuteDecision(d); + } + bool AcceptSolution() override { + return monitors_[active_monitor_]->AcceptSolution(); + } + bool LocalOptimum() override { + const bool ok = monitors_[active_monitor_]->LocalOptimum(); + if (!ok) { + enabled_monitors_[active_monitor_] = false; + } + if (++num_local_optimum_ >= local_optimum_limit_ || !ok) { + monitors_[active_monitor_]->set_active(false); + int next_active_monitor = (active_monitor_ + 1) % monitors_.size(); + while (!enabled_monitors_[next_active_monitor]) { + if (next_active_monitor == active_monitor_) return false; + next_active_monitor = (active_monitor_ + 1) % monitors_.size(); + } + active_monitor_ = next_active_monitor; + monitors_[active_monitor_]->set_active(true); + num_local_optimum_ = 0; + VLOG(2) << "Switching to monitor " << active_monitor_ << " " + << monitors_[active_monitor_]->DebugString(); + } + return true; + } + IntVar* ObjectiveVar(int index) const override { + return monitors_[active_monitor_]->ObjectiveVar(index); + } + IntVar* MinimizationVar(int index) const override { + return monitors_[active_monitor_]->MinimizationVar(index); + } + int64_t Step(int index) const override { + return monitors_[active_monitor_]->Step(index); + } + bool Maximize(int index) const override { + return monitors_[active_monitor_]->Maximize(index); + } + int64_t BestValue(int index) const override { + return monitors_[active_monitor_]->BestValue(index); + } + int Size() const override { return monitors_[active_monitor_]->Size(); } + std::string DebugString() const override { + return monitors_[active_monitor_]->DebugString(); + } + void Accept(ModelVisitor* visitor) const override { + // TODO(user): properly implement this. + for (auto& monitor : monitors_) { + monitor->Accept(visitor); + } + } + + private: + const std::vector monitors_; + std::vector enabled_monitors_; + int active_monitor_ = 0; + int num_local_optimum_ = 0; + const int local_optimum_limit_; +}; + +BaseObjectiveMonitor* Solver::MakeRoundRobinCompoundObjectiveMonitor( + std::vector monitors, + int num_max_local_optima_before_metaheuristic_switch) { + return RevAlloc(new RoundRobinCompoundObjectiveMonitor( + std::move(monitors), num_max_local_optima_before_metaheuristic_switch)); +} + ObjectiveMonitor::ObjectiveMonitor(Solver* solver, const std::vector& maximize, std::vector vars, std::vector steps) - : SearchMonitor(solver), + : BaseObjectiveMonitor(solver), found_initial_solution_(false), objective_vars_(std::move(vars)), minimization_vars_(objective_vars_), @@ -3102,14 +3207,14 @@ void OptimizeVar::BeginNextDecision(DecisionBuilder*) { void OptimizeVar::ApplyBound() { if (found_initial_solution_) { MakeMinimizationVarsLessOrEqualWithSteps( - [this](int i) { return BestInternalValue(i); }); + [this](int i) { return CurrentInternalValue(i); }); } } void OptimizeVar::RefuteDecision(Decision*) { ApplyBound(); } bool OptimizeVar::AcceptSolution() { - if (!found_initial_solution_) { + if (!found_initial_solution_ || !is_active()) { return true; } else { // This code should never return false in sequential mode because @@ -3121,8 +3226,8 @@ bool OptimizeVar::AcceptSolution() { // accepted. if (!minimization_var->Bound()) return true; const int64_t value = minimization_var->Value(); - if (value == BestInternalValue(i)) continue; - return value < BestInternalValue(i); + if (value == CurrentInternalValue(i)) continue; + return value < CurrentInternalValue(i); } return false; } @@ -3304,6 +3409,7 @@ class TabuSearch : public Metaheuristic { const std::vector vars_; Assignment::IntContainer assignment_container_; + bool has_stored_assignment_ = false; std::vector last_values_; TabuList keep_tabu_list_; TabuList synced_keep_tabu_list_; @@ -3337,6 +3443,7 @@ void TabuSearch::EnterSearch() { Metaheuristic::EnterSearch(); solver()->SetUseFastLocalSearch(true); stamp_ = 0; + has_stored_assignment_ = false; } void TabuSearch::ApplyDecision(Decision* const d) { @@ -3415,7 +3522,7 @@ bool TabuSearch::AtSolution() { // New solution found: add new assignments to tabu lists; this is only // done after the first local optimum (stamp_ != 0) - if (0 != stamp_) { + if (0 != stamp_ && has_stored_assignment_) { for (int index = 0; index < vars_.size(); ++index) { IntVar* var = vars(index); const int64_t old_value = assignment_container_.Element(index).Value(); @@ -3431,6 +3538,7 @@ bool TabuSearch::AtSolution() { } } assignment_container_.Store(); + has_stored_assignment_ = true; return true; } @@ -3945,7 +4053,9 @@ void GuidedLocalSearch

::ApplyDecision(Decision* const d) { assignment_penalized_value_ = CapAdd(assignment_penalized_value_, penalty); } - penalized_objective_ = solver()->MakeSum(elements)->Var(); + solver()->SaveAndSetValue( + reinterpret_cast(&penalized_objective_), + reinterpret_cast(solver()->MakeSum(elements)->Var())); } penalized_values_.Commit(); old_penalized_value_ = assignment_penalized_value_; @@ -3983,7 +4093,7 @@ bool GuidedLocalSearch

::AtSolution() { if (!ObjectiveMonitor::AtSolution()) { return false; } - if (penalized_objective_ != nullptr) { + if (penalized_objective_ != nullptr && penalized_objective_->Bound()) { // If the value of the best solution has changed (aka a new best solution // has been found), triggering a reset on the penalties to start fresh. // The immediate consequence is a greedy dive towards a local minimum, diff --git a/ortools/routing/breaks.cc b/ortools/routing/breaks.cc index a6b165e1c72..634ad259bdf 100644 --- a/ortools/routing/breaks.cc +++ b/ortools/routing/breaks.cc @@ -33,7 +33,7 @@ #include "ortools/util/saturated_arithmetic.h" #include "ortools/util/sorted_interval_list.h" -namespace operations_research { +namespace operations_research::routing { bool DisjunctivePropagator::Propagate(Tasks* tasks) { DCHECK_LE(tasks->num_chain_tasks, tasks->start_min.size()); @@ -1086,4 +1086,4 @@ IntVarLocalSearchFilter* MakeVehicleBreaksFilter( new VehicleBreaksFilter(routing_model, dimension)); } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/constraints.cc b/ortools/routing/constraints.cc index 84cd414916c..64963468050 100644 --- a/ortools/routing/constraints.cc +++ b/ortools/routing/constraints.cc @@ -32,7 +32,7 @@ #include "ortools/routing/search.h" #include "ortools/util/saturated_arithmetic.h" -namespace operations_research { +namespace operations_research::routing { namespace { // Constraint which ensures that var != values. @@ -793,4 +793,4 @@ Constraint* MakeRouteConstraint( model, std::move(route_cost_vars), std::move(route_evaluator))); } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/constraints.h b/ortools/routing/constraints.h index db3626cce0c..a3a4194a467 100644 --- a/ortools/routing/constraints.h +++ b/ortools/routing/constraints.h @@ -22,7 +22,7 @@ #include "ortools/constraint_solver/constraint_solver.h" #include "ortools/routing/routing.h" -namespace operations_research { +namespace operations_research::routing { Constraint* MakeDifferentFromValues(Solver* solver, IntVar* var, std::vector values); @@ -49,6 +49,6 @@ Constraint* MakeRouteConstraint( std::function(const std::vector&)> route_evaluator); -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_CONSTRAINTS_H_ diff --git a/ortools/routing/decision_builders.cc b/ortools/routing/decision_builders.cc index 766f2535450..e6c3b01de73 100644 --- a/ortools/routing/decision_builders.cc +++ b/ortools/routing/decision_builders.cc @@ -31,7 +31,7 @@ #include "ortools/routing/routing.h" #include "ortools/util/saturated_arithmetic.h" -namespace operations_research { +namespace operations_research::routing { namespace { @@ -897,4 +897,4 @@ DecisionBuilder* FinalizerVariables::CreateFinalizer() { std::move(targets)); } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/decision_builders.h b/ortools/routing/decision_builders.h index 5b511acb3d4..a1a1122c999 100644 --- a/ortools/routing/decision_builders.h +++ b/ortools/routing/decision_builders.h @@ -24,7 +24,7 @@ #include "ortools/routing/lp_scheduling.h" #include "ortools/routing/routing.h" -namespace operations_research { +namespace operations_research::routing { /// A decision builder which tries to assign values to variables as close as /// possible to target values first. @@ -108,5 +108,5 @@ class FinalizerVariables { #endif }; -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_DECISION_BUILDERS_H_ diff --git a/ortools/routing/docs/ROUTING.md b/ortools/routing/docs/ROUTING.md index 7657c34839c..0bc3ac6ac59 100644 --- a/ortools/routing/docs/ROUTING.md +++ b/ortools/routing/docs/ROUTING.md @@ -29,7 +29,7 @@ and .Net. Each language have different requirements for the code samples. #include "ortools/routing/parameters.h" #include "ortools/routing/routing.h" -namespace operations_research { +namespace operations_research::routing { void SimpleRoutingProgram() { // Instantiate the data problem. @@ -79,7 +79,7 @@ void SimpleRoutingProgram() { LOG(INFO) << "Distance of the route: " << route_distance << "m"; } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::SimpleRoutingProgram(); diff --git a/ortools/routing/filters.cc b/ortools/routing/filters.cc index eb70802b495..03f0fb1c564 100644 --- a/ortools/routing/filters.cc +++ b/ortools/routing/filters.cc @@ -58,7 +58,7 @@ ABSL_FLAG(bool, routing_strong_debug_checks, false, "Run stronger checks in debug; these stronger tests might change " "the complexity of the code in particular."); -namespace operations_research { +namespace operations_research::routing { namespace { // Route constraint filter @@ -4289,7 +4289,7 @@ class LightVehicleBreaksFilter : public LocalSearchFilter { LocalSearchFilter* MakeLightVehicleBreaksFilter( Solver* solver, std::unique_ptr checker, - const std::string& dimension_name) { + absl::string_view dimension_name) { LightVehicleBreaksFilter* filter = new LightVehicleBreaksFilter(std::move(checker), dimension_name); return solver->RevAlloc(filter); @@ -4751,4 +4751,4 @@ LocalSearchFilter* MakePathEnergyCostFilter( // TODO(user): Implement same-vehicle filter. Could be merged with node // precedence filter. -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/filters.h b/ortools/routing/filters.h index 88464861ec7..c88578bef58 100644 --- a/ortools/routing/filters.h +++ b/ortools/routing/filters.h @@ -34,7 +34,7 @@ #include "ortools/util/bitset.h" #include "ortools/util/range_minimum_query.h" -namespace operations_research { +namespace operations_research::routing { /// Returns a filter tracking route constraints. IntVarLocalSearchFilter* MakeRouteConstraintFilter( @@ -589,7 +589,7 @@ class LightVehicleBreaksChecker { LocalSearchFilter* MakeLightVehicleBreaksFilter( Solver* solver, std::unique_ptr checker, - const std::string& dimension_name); + absl::string_view dimension_name); // This class allows making fast range queries on sequences of elements. // * Main characteristics. @@ -978,6 +978,6 @@ class BasePathFilter : public IntVarLocalSearchFilter { bool lns_detected_; }; -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_FILTERS_H_ diff --git a/ortools/routing/flow.cc b/ortools/routing/flow.cc index 453da68b634..01efa7c71c0 100644 --- a/ortools/routing/flow.cc +++ b/ortools/routing/flow.cc @@ -34,7 +34,7 @@ #include "ortools/routing/routing.h" #include "ortools/util/saturated_arithmetic.h" -namespace operations_research { +namespace operations_research::routing { namespace { // Compute set of disjunctions involved in a pickup and delivery pair. @@ -452,4 +452,4 @@ bool RoutingModel::SolveMatchingModel( return true; } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/ils.cc b/ortools/routing/ils.cc index 9267e5511c9..1de349c77c0 100644 --- a/ortools/routing/ils.cc +++ b/ortools/routing/ils.cc @@ -37,7 +37,7 @@ #include "ortools/routing/search.h" #include "ortools/routing/types.h" -namespace operations_research { +namespace operations_research::routing { namespace { // Returns global cheapest insertion parameters based on the given search @@ -227,13 +227,15 @@ std::unique_ptr MakeRecreateProcedure( model, std::move(stop_search), absl::bind_front(&RoutingModel::GetArcCostForVehicle, model), parameters.local_cheapest_cost_insertion_pickup_delivery_strategy(), - filter_manager, model->GetBinCapacities()); + parameters.local_cheapest_insertion_sorting_mode(), filter_manager, + model->GetBinCapacities()); case FirstSolutionStrategy::LOCAL_CHEAPEST_COST_INSERTION: return std::make_unique( model, std::move(stop_search), /*evaluator=*/nullptr, parameters.local_cheapest_cost_insertion_pickup_delivery_strategy(), - filter_manager, model->GetBinCapacities()); + parameters.local_cheapest_insertion_sorting_mode(), filter_manager, + model->GetBinCapacities()); case FirstSolutionStrategy::SEQUENTIAL_CHEAPEST_INSERTION: { GlobalCheapestInsertionFilteredHeuristic:: GlobalCheapestInsertionParameters gci_parameters = @@ -432,13 +434,18 @@ bool HasPerformedNodes(const RoutingModel& model, return false; } -// Returns a random performed customer for the given assignment and customer -// distribution. The procedure assumes the assignment has at least one -// performed customer. +// Returns a random performed visit for the given assignment. The procedure +// requires a distribution including all visits. Returns -1 if there are no +// performed visits. int64_t PickRandomPerformedVisit( const RoutingModel& model, const Assignment& assignment, std::mt19937& rnd, std::uniform_int_distribution& customer_dist) { - DCHECK(HasPerformedNodes(model, assignment)); + DCHECK_EQ(customer_dist.min(), 0); + DCHECK_EQ(customer_dist.max(), model.Size() - model.vehicles()); + + if (!HasPerformedNodes(model, assignment)) { + return -1; + } int64_t customer; do { @@ -652,42 +659,53 @@ CloseRoutesRemovalRuinProcedure::CloseRoutesRemovalRuinProcedure( neighbors_manager_(model->GetOrCreateNodeNeighborsByCostClass( {num_neighbors_for_route_selection, /*add_vehicle_starts_to_neighbors=*/false, + /*add_vehicle_ends_to_neighbors=*/false, /*only_sort_neighbors_for_partial_neighborhoods=*/false})), num_routes_(num_routes), rnd_(*rnd), - customer_dist_(0, model->Size() - 1), + customer_dist_(0, model->Size() - model->vehicles()), removed_routes_(model->vehicles()) {} std::function CloseRoutesRemovalRuinProcedure::Ruin( const Assignment* assignment) { + if (num_routes_ == 0) { + return [this, assignment](int64_t node) { + return assignment->Value(model_.NextVar(node)); + }; + } + + const int64_t seed_node = + PickRandomPerformedVisit(model_, *assignment, rnd_, customer_dist_); + if (seed_node == -1) { + return [this, assignment](int64_t node) { + return assignment->Value(model_.NextVar(node)); + }; + } + removed_routes_.SparseClearAll(); - if (num_routes_ > 0 && HasPerformedNodes(model_, *assignment)) { - const int64_t seed_node = - PickRandomPerformedVisit(model_, *assignment, rnd_, customer_dist_); - const int seed_route = assignment->Value(model_.VehicleVar(seed_node)); - DCHECK_GE(seed_route, 0); + const int seed_route = assignment->Value(model_.VehicleVar(seed_node)); + DCHECK_GE(seed_route, 0); - removed_routes_.Set(seed_route); + removed_routes_.Set(seed_route); - const RoutingCostClassIndex cost_class_index = - model_.GetCostClassIndexOfVehicle(seed_route); + const RoutingCostClassIndex cost_class_index = + model_.GetCostClassIndexOfVehicle(seed_route); - const std::vector& neighbors = - neighbors_manager_->GetNeighborsOfNodeForCostClass( - cost_class_index.value(), seed_node); + const std::vector& neighbors = + neighbors_manager_->GetOutgoingNeighborsOfNodeForCostClass( + cost_class_index.value(), seed_node); - for (int neighbor : neighbors) { - if (removed_routes_.NumberOfSetCallsWithDifferentArguments() == - num_routes_) { - break; - } - const int64_t route = assignment->Value(model_.VehicleVar(neighbor)); - if (route < 0 || removed_routes_[route]) { - continue; - } - removed_routes_.Set(route); + for (int neighbor : neighbors) { + if (removed_routes_.NumberOfSetCallsWithDifferentArguments() == + num_routes_) { + break; + } + const int64_t route = assignment->Value(model_.VehicleVar(neighbor)); + if (route < 0 || removed_routes_[route]) { + continue; } + removed_routes_.Set(route); } return [this, assignment](int64_t node) { @@ -710,23 +728,29 @@ RandomWalkRemovalRuinProcedure::RandomWalkRemovalRuinProcedure( neighbors_manager_(model->GetOrCreateNodeNeighborsByCostClass( {num_neighbors_for_route_selection, /*add_vehicle_starts_to_neighbors=*/false, + /*add_vehicle_ends_to_neighbors=*/false, /*only_sort_neighbors_for_partial_neighborhoods=*/false})), rnd_(*rnd), walk_length_(walk_length), - customer_dist_(0, model->Size() - 1) {} + customer_dist_(0, model->Size() - model->vehicles()) {} std::function RandomWalkRemovalRuinProcedure::Ruin( const Assignment* assignment) { - if (!HasPerformedNodes(model_, *assignment) || walk_length_ == 0) { - return [&model = model_, assignment](int64_t node) { - return assignment->Value(model.NextVar(node)); + if (walk_length_ == 0) { + return [this, assignment](int64_t node) { + return assignment->Value(model_.NextVar(node)); }; } - routing_solution_.Reset(assignment); - int64_t curr_node = PickRandomPerformedVisit(model_, *assignment, rnd_, customer_dist_); + if (curr_node == -1) { + return [this, assignment](int64_t node) { + return assignment->Value(model_.NextVar(node)); + }; + } + + routing_solution_.Reset(assignment); int walk_length = walk_length_; @@ -772,7 +796,7 @@ int64_t RandomWalkRemovalRuinProcedure::GetNextNodeToRemove( model_.GetCostClassIndexOfVehicle(curr_vehicle); const std::vector& neighbors = - neighbors_manager_->GetNeighborsOfNodeForCostClass( + neighbors_manager_->GetOutgoingNeighborsOfNodeForCostClass( cost_class_index.value(), node); int64_t same_route_closest_neighbor = -1; @@ -931,4 +955,4 @@ std::pair GetSimulatedAnnealingTemperatures( return {reference_temperature * 0.1, reference_temperature * 0.001}; } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/ils.h b/ortools/routing/ils.h index 3703969ae7c..da637c7c562 100644 --- a/ortools/routing/ils.h +++ b/ortools/routing/ils.h @@ -29,7 +29,7 @@ #include "ortools/routing/routing.h" #include "ortools/util/bitset.h" -namespace operations_research { +namespace operations_research::routing { // Wraps a routing assignment providing extra features. class RoutingSolution { @@ -223,6 +223,6 @@ std::pair GetSimulatedAnnealingTemperatures( const RoutingModel& model, const SimulatedAnnealingParameters& sa_params, std::mt19937* rnd); -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_ILS_H_ diff --git a/ortools/routing/index_manager.cc b/ortools/routing/index_manager.cc index 5375654c128..119d145db28 100644 --- a/ortools/routing/index_manager.cc +++ b/ortools/routing/index_manager.cc @@ -23,7 +23,7 @@ #include "absl/types/span.h" #include "ortools/base/logging.h" -namespace operations_research { +namespace operations_research::routing { const int64_t RoutingIndexManager::kUnassigned = -1; @@ -146,4 +146,4 @@ std::vector RoutingIndexManager::IndicesToNodes( return nodes; } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/index_manager.h b/ortools/routing/index_manager.h index 133c908b7a8..ba401b71876 100644 --- a/ortools/routing/index_manager.h +++ b/ortools/routing/index_manager.h @@ -23,7 +23,7 @@ #include "ortools/base/strong_vector.h" #include "ortools/routing/types.h" -namespace operations_research { +namespace operations_research::routing { /// Manager for any NodeIndex <-> variable index conversion. The routing solver /// uses variable indices internally and through its API. These variable indices @@ -117,6 +117,6 @@ class RoutingIndexManager { int num_unique_depots_; }; -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_INDEX_MANAGER_H_ diff --git a/ortools/routing/insertion_lns.cc b/ortools/routing/insertion_lns.cc index f05c9ca252a..bbb39dced6e 100644 --- a/ortools/routing/insertion_lns.cc +++ b/ortools/routing/insertion_lns.cc @@ -30,7 +30,7 @@ #include "ortools/routing/utils.h" #include "ortools/util/bitset.h" -namespace operations_research { +namespace operations_research::routing { // FilteredHeuristicLocalSearchOperator @@ -524,4 +524,4 @@ bool FilteredHeuristicExpensiveChainLNSOperator:: return false; } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/insertion_lns.h b/ortools/routing/insertion_lns.h index c2e6e9215cf..8d4c51947a5 100644 --- a/ortools/routing/insertion_lns.h +++ b/ortools/routing/insertion_lns.h @@ -30,7 +30,7 @@ #include "ortools/routing/types.h" #include "ortools/util/bitset.h" -namespace operations_research { +namespace operations_research::routing { /// Class of operators using a RoutingFilteredHeuristic to insert unperformed /// nodes after changes have been made to the current solution. @@ -243,6 +243,6 @@ class FilteredHeuristicCloseNodesLNSOperator SparseBitset<> changed_prevs_; }; -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_INSERTION_LNS_H_ diff --git a/ortools/routing/lp_scheduling.cc b/ortools/routing/lp_scheduling.cc index 4129c5d81ae..6f37bfebb08 100644 --- a/ortools/routing/lp_scheduling.cc +++ b/ortools/routing/lp_scheduling.cc @@ -55,7 +55,7 @@ #include "ortools/util/saturated_arithmetic.h" #include "ortools/util/sorted_interval_list.h" -namespace operations_research { +namespace operations_research::routing { namespace { @@ -3421,4 +3421,4 @@ std::string RoutingCPSatWrapper::PrintModel() const { return s; } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/lp_scheduling.h b/ortools/routing/lp_scheduling.h index b4bb7a74149..d2706ff995b 100644 --- a/ortools/routing/lp_scheduling.h +++ b/ortools/routing/lp_scheduling.h @@ -47,7 +47,7 @@ #include "ortools/sat/sat_parameters.pb.h" #include "ortools/util/sorted_interval_list.h" -namespace operations_research { +namespace operations_research::routing { // Classes to solve dimension cumul placement (aka scheduling) problems using // linear programming. @@ -1065,6 +1065,6 @@ std::vector PiecewiseLinearFormulationToSlopeAndYIntercept( PiecewiseLinearFormulation& pwl_function, int index_start = 0, int index_end = -1); -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_LP_SCHEDULING_H_ diff --git a/ortools/routing/neighborhoods.cc b/ortools/routing/neighborhoods.cc index 5612f36df98..89e7ad8de75 100644 --- a/ortools/routing/neighborhoods.cc +++ b/ortools/routing/neighborhoods.cc @@ -28,7 +28,7 @@ #include "ortools/routing/utils.h" #include "ortools/util/saturated_arithmetic.h" -namespace operations_research { +namespace operations_research::routing { MakeRelocateNeighborsOperator::MakeRelocateNeighborsOperator( const std::vector& vars, @@ -1356,4 +1356,4 @@ bool ExchangeSubtrip::ExtractChainsFromDelivery(int64_t base_node, return true; } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/neighborhoods.h b/ortools/routing/neighborhoods.h index d62427a35d7..a4b3a8090d6 100644 --- a/ortools/routing/neighborhoods.h +++ b/ortools/routing/neighborhoods.h @@ -27,7 +27,7 @@ #include "ortools/routing/types.h" #include "ortools/util/bitset.h" -namespace operations_research { +namespace operations_research::routing { /// Relocate neighborhood which moves chains of neighbors. /// The operator starts by relocating a node n after a node m, then continues @@ -748,6 +748,6 @@ class ExchangeSubtrip : public PathOperator { std::vector path1_; }; -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_NEIGHBORHOODS_H_ diff --git a/ortools/routing/parameters.cc b/ortools/routing/parameters.cc index 2987508145a..a991cb07a9a 100644 --- a/ortools/routing/parameters.cc +++ b/ortools/routing/parameters.cc @@ -37,7 +37,7 @@ #include "ortools/util/optional_boolean.pb.h" #include "ortools/util/testing_utils.h" -namespace operations_research { +namespace operations_research::routing { RoutingModelParameters DefaultRoutingModelParameters() { RoutingModelParameters parameters; @@ -147,6 +147,7 @@ RoutingSearchParameters CreateDefaultRoutingSearchParameters() { p.set_heuristic_expensive_chain_lns_num_arcs_to_consider(4); p.set_heuristic_close_nodes_lns_num_nodes(5); p.set_local_search_metaheuristic(LocalSearchMetaheuristic::AUTOMATIC); + p.set_num_max_local_optima_before_metaheuristic_switch(200); p.set_guided_local_search_lambda_coefficient(0.1); p.set_guided_local_search_reset_penalties_on_new_best_solution(false); p.set_use_depth_first_search(false); @@ -306,14 +307,14 @@ void FindErrorsInIteratedLocalSearchParameters( ruin.spatially_close_routes().num_ruined_routes() == 0) { errors.emplace_back(StrCat( "iterated_local_search_parameters.ruin_recreate_parameters." - "ruin is set to SpatiallyCloseRoutesRuinStrategy", + "ruin_strategy is set to SpatiallyCloseRoutesRuinStrategy" " but spatially_close_routes.num_ruined_routes is 0 (should be " "strictly positive)")); } else if (ruin.strategy_case() == RuinStrategy::kRandomWalk && ruin.random_walk().num_removed_visits() == 0) { errors.emplace_back( StrCat("iterated_local_search_parameters.ruin_recreate_parameters." - "ruin is set to RandomWalkRuinStrategy", + "ruin_strategy is set to RandomWalkRuinStrategy" " but random_walk.num_removed_visits is 0 (should be " "strictly positive)")); } @@ -570,10 +571,34 @@ std::vector FindErrorsInRoutingSearchParameters( errors.emplace_back(StrCat("Invalid first_solution_strategy: ", search_parameters.first_solution_strategy())); } - if (!LocalSearchMetaheuristic::Value_IsValid( - search_parameters.local_search_metaheuristic())) { - errors.emplace_back(StrCat("Invalid metaheuristic: ", - search_parameters.local_search_metaheuristic())); + const LocalSearchMetaheuristic::Value local_search_metaheuristic = + search_parameters.local_search_metaheuristic(); + if (local_search_metaheuristic != LocalSearchMetaheuristic::UNSET && + local_search_metaheuristic != LocalSearchMetaheuristic::AUTOMATIC && + !search_parameters.local_search_metaheuristics().empty()) { + errors.emplace_back( + StrCat("local_search_metaheuristics cannot be set if " + "local_search_metaheuristic is different from " + "UNSET or AUTOMATIC: ", + local_search_metaheuristic)); + } + if (!LocalSearchMetaheuristic::Value_IsValid(local_search_metaheuristic)) { + errors.emplace_back( + StrCat("Invalid metaheuristic: ", local_search_metaheuristic)); + } + for (const int metaheuristic : + search_parameters.local_search_metaheuristics()) { + if (!LocalSearchMetaheuristic::Value_IsValid(metaheuristic) || + metaheuristic == LocalSearchMetaheuristic::UNSET) { + errors.emplace_back(StrCat("Invalid metaheuristic: ", metaheuristic)); + } + } + if (!search_parameters.local_search_metaheuristics().empty() && + search_parameters.num_max_local_optima_before_metaheuristic_switch() < + 1) { + errors.emplace_back(StrCat( + "Invalid num_max_local_optima_before_metaheuristic_switch: ", + search_parameters.num_max_local_optima_before_metaheuristic_switch())); } const double scaling_factor = search_parameters.log_cost_scaling_factor(); @@ -667,4 +692,4 @@ std::vector FindErrorsInRoutingSearchParameters( return errors; } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parameters.h b/ortools/routing/parameters.h index a678d0fcb58..0aca012be22 100644 --- a/ortools/routing/parameters.h +++ b/ortools/routing/parameters.h @@ -19,7 +19,7 @@ #include "ortools/routing/parameters.pb.h" -namespace operations_research { +namespace operations_research::routing { RoutingModelParameters DefaultRoutingModelParameters(); RoutingSearchParameters DefaultRoutingSearchParameters(); @@ -35,6 +35,6 @@ std::string FindErrorInRoutingSearchParameters( std::vector FindErrorsInRoutingSearchParameters( const RoutingSearchParameters& search_parameters); -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_PARAMETERS_H_ diff --git a/ortools/routing/parameters.proto b/ortools/routing/parameters.proto index c988cfbdea3..f4e9713f121 100644 --- a/ortools/routing/parameters.proto +++ b/ortools/routing/parameters.proto @@ -36,7 +36,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: 63 +// Next ID: 66 message RoutingSearchParameters { reserved 19; @@ -132,6 +132,25 @@ message RoutingSearchParameters { PairInsertionStrategy local_cheapest_cost_insertion_pickup_delivery_strategy = 55; + // A mode to select in which order nodes or node pairs are considered in + // insertion heuristics. + enum InsertionSortingMode { + // Default mode, equivalent to SORT_BY_ALLOWED_VEHICLES_THEN_PENALTY. + SORTING_MODE_UNSET = 0; + // Selects nodes with the least number of allowed vehicles first, then the + // ones with the highest penalty. + SORT_BY_ALLOWED_VEHICLES_THEN_PENALTY = 1; + // Selects nodes with the highest penalty first, then the ones with the + // least number of allowed vehicles. + SORT_BY_PENALTY_THEN_ALLOWED_VEHICLES = 2; + // Selects nodes with the highest penalty / number of allowed vehicles + // ratio first, then the ones with the highest penalty. + SORT_BY_PENALTY_ALLOWED_VEHICLES_RATIO_THEN_PENALTY = 3; + } + // The node insertion sorting mode used in local cheapest insertion + // heuristics. + InsertionSortingMode local_cheapest_insertion_sorting_mode = 65; + // If true use minimum matching instead of minimal matching in the // Christofides algorithm. bool christofides_use_minimum_matching = 30; @@ -459,6 +478,12 @@ message RoutingSearchParameters { // Local search metaheuristics used to guide the search. LocalSearchMetaheuristic.Value local_search_metaheuristic = 4; + // Local search metaheuristics alternatively used to guide the search. Every + // num_max_local_optima_before_metaheuristic_switch local minima found by a + // metaheurisitic, the solver will switch to the next metaheuristic. Cannot be + // defined if local_search_metaheuristic is different from UNSET or AUTOMATIC. + repeated LocalSearchMetaheuristic.Value local_search_metaheuristics = 63; + int32 num_max_local_optima_before_metaheuristic_switch = 64; // These are advanced settings which should not be modified unless you know // what you are doing. // Lambda coefficient used to penalize arc costs when GUIDED_LOCAL_SEARCH is diff --git a/ortools/routing/parsers/carp_parser.cc b/ortools/routing/parsers/carp_parser.cc index 36068056472..243b953e5aa 100644 --- a/ortools/routing/parsers/carp_parser.cc +++ b/ortools/routing/parsers/carp_parser.cc @@ -26,7 +26,7 @@ #include "ortools/base/numbers.h" #include "ortools/util/filelineiter.h" -namespace operations_research { +namespace operations_research::routing { CarpParser::CarpParser() { Initialize(); } @@ -247,4 +247,4 @@ std::optional ParseNodeIndex(std::string_view text) { return {node - 1}; } } // namespace -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/carp_parser.h b/ortools/routing/parsers/carp_parser.h index 4c7ad327df4..d5697c9e094 100644 --- a/ortools/routing/parsers/carp_parser.h +++ b/ortools/routing/parsers/carp_parser.h @@ -66,7 +66,7 @@ #include "ortools/base/logging.h" #include "ortools/routing/parsers/simple_graph.h" -namespace operations_research { +namespace operations_research::routing { class CarpParser { public: CarpParser(); @@ -182,6 +182,6 @@ class CarpParser { int64_t n_vehicles_; int64_t capacity_; }; -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_PARSERS_CARP_PARSER_H_ diff --git a/ortools/routing/parsers/carp_parser_test.cc b/ortools/routing/parsers/carp_parser_test.cc index 6810e2bb2f4..7c1bda00dcf 100644 --- a/ortools/routing/parsers/carp_parser_test.cc +++ b/ortools/routing/parsers/carp_parser_test.cc @@ -23,7 +23,7 @@ #define ROOT_DIR "com_google_ortools/" -namespace operations_research { +namespace operations_research::routing { namespace { TEST(CarpParserTest, Constructor) { CarpParser parser; @@ -271,4 +271,4 @@ TEST(CarpParserTest, LoadInstanceFileWithDifferentDepot) { EXPECT_EQ(parser.depot(), 4); } } // namespace -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/cvrptw_lib.cc b/ortools/routing/parsers/cvrptw_lib.cc index d547db37657..446cb04cb57 100644 --- a/ortools/routing/parsers/cvrptw_lib.cc +++ b/ortools/routing/parsers/cvrptw_lib.cc @@ -29,7 +29,7 @@ #include "ortools/routing/index_manager.h" #include "ortools/routing/routing.h" -namespace operations_research { +namespace operations_research::routing { using NodeIndex = RoutingIndexManager::NodeIndex; @@ -243,4 +243,4 @@ void DisplayPlan( } LOG(INFO) << plan_output; } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/cvrptw_lib.h b/ortools/routing/parsers/cvrptw_lib.h index 5f2148c7577..b855d174cab 100644 --- a/ortools/routing/parsers/cvrptw_lib.h +++ b/ortools/routing/parsers/cvrptw_lib.h @@ -24,7 +24,7 @@ #include "ortools/base/strong_vector.h" #include "ortools/routing/routing.h" -namespace operations_research { +namespace operations_research::routing { typedef std::function RoutingNodeEvaluator2; @@ -132,6 +132,6 @@ void DisplayPlan( const operations_research::RoutingDimension& capacity_dimension, const operations_research::RoutingDimension& time_dimension); -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_PARSERS_CVRPTW_LIB_H_ diff --git a/ortools/routing/parsers/dow_parser.cc b/ortools/routing/parsers/dow_parser.cc index 29dc375a446..af276bcdd74 100644 --- a/ortools/routing/parsers/dow_parser.cc +++ b/ortools/routing/parsers/dow_parser.cc @@ -29,7 +29,7 @@ #include "ortools/routing/parsers/capacity_planning.pb.h" #include "ortools/util/filelineiter.h" -namespace operations_research { +namespace operations_research::routing { ::absl::Status ReadFile(absl::string_view file_name, CapacityPlanningInstance* request) { if (!file::Exists(file_name, file::Defaults()).ok()) { @@ -103,4 +103,4 @@ ::absl::Status ReadFile(absl::string_view file_name, return absl::OkStatus(); } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/dow_parser.h b/ortools/routing/parsers/dow_parser.h index 8d78b8443ee..e3a9c5c540b 100644 --- a/ortools/routing/parsers/dow_parser.h +++ b/ortools/routing/parsers/dow_parser.h @@ -21,9 +21,9 @@ // Reader for Multicommodity fixed-charge Network Design (MCND) files using the // .dow format. -namespace operations_research { +namespace operations_research::routing { ::absl::Status ReadFile(absl::string_view file_name, CapacityPlanningInstance* request); -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_PARSERS_DOW_PARSER_H_ diff --git a/ortools/routing/parsers/dow_parser_test.cc b/ortools/routing/parsers/dow_parser_test.cc index a86338d346e..3ec5395f67b 100644 --- a/ortools/routing/parsers/dow_parser_test.cc +++ b/ortools/routing/parsers/dow_parser_test.cc @@ -21,7 +21,7 @@ #include "ortools/base/path.h" #include "ortools/routing/parsers/capacity_planning.pb.h" -namespace operations_research { +namespace operations_research::routing { namespace { TEST(CapacityPlanningReaderTest, C33PassesOK) { CapacityPlanningInstance request; @@ -57,4 +57,4 @@ TEST(CapacityPlanningReaderTest, C34DoesNotExist) { } } // namespace -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/lilim_parser.cc b/ortools/routing/parsers/lilim_parser.cc index 5fb8deb0ab7..3e0d29771c8 100644 --- a/ortools/routing/parsers/lilim_parser.cc +++ b/ortools/routing/parsers/lilim_parser.cc @@ -30,7 +30,7 @@ #include "ortools/base/zipfile.h" #include "ortools/util/filelineiter.h" -namespace operations_research { +namespace operations_research::routing { bool LiLimParser::LoadFile(absl::string_view file_name) { Initialize(); @@ -126,4 +126,4 @@ bool LiLimParser::ParseFile(absl::string_view file_name) { #undef PARSE_AND_RETURN_IF_NEGATIVE -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/lilim_parser.h b/ortools/routing/parsers/lilim_parser.h index 2c2f441667d..9cfe63f0cc2 100644 --- a/ortools/routing/parsers/lilim_parser.h +++ b/ortools/routing/parsers/lilim_parser.h @@ -51,7 +51,7 @@ #include "absl/strings/string_view.h" #include "ortools/routing/parsers/simple_graph.h" -namespace operations_research { +namespace operations_research::routing { // Li&Lim parser class class LiLimParser { @@ -122,6 +122,6 @@ class LiLimParser { std::vector> time_windows_; std::vector service_times_; }; -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_PARSERS_LILIM_PARSER_H_ diff --git a/ortools/routing/parsers/lilim_parser_test.cc b/ortools/routing/parsers/lilim_parser_test.cc index 34626dd320a..023a41efb9a 100644 --- a/ortools/routing/parsers/lilim_parser_test.cc +++ b/ortools/routing/parsers/lilim_parser_test.cc @@ -21,7 +21,7 @@ #define ROOT_DIR "com_google_ortools/" -namespace operations_research { +namespace operations_research::routing { namespace { void CheckData(const LiLimParser& parser) { @@ -78,4 +78,4 @@ TEST(LiLimParserTest, LoadNonExistingInstance) { } } // namespace -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/nearp_parser.cc b/ortools/routing/parsers/nearp_parser.cc index 8b07f8e70ce..095ce085ecb 100644 --- a/ortools/routing/parsers/nearp_parser.cc +++ b/ortools/routing/parsers/nearp_parser.cc @@ -26,7 +26,7 @@ #include "ortools/base/numbers.h" #include "ortools/util/filelineiter.h" -namespace operations_research { +namespace operations_research::routing { NearpParser::NearpParser() { Initialize(); } @@ -447,4 +447,4 @@ std::string NearpParser::GetEdgeName(Edge edge) const { return absl::StrCat("NrE", edge_position - num_edges_with_servicing_ + 1); } } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/nearp_parser.h b/ortools/routing/parsers/nearp_parser.h index e6893ecdfa5..57c32644af2 100644 --- a/ortools/routing/parsers/nearp_parser.h +++ b/ortools/routing/parsers/nearp_parser.h @@ -85,7 +85,7 @@ #include "ortools/base/logging.h" #include "ortools/routing/parsers/simple_graph.h" -namespace operations_research { +namespace operations_research::routing { class NearpParser { public: NearpParser(); @@ -253,6 +253,6 @@ class NearpParser { int num_vehicles_; int64_t capacity_; }; -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_PARSERS_NEARP_PARSER_H_ diff --git a/ortools/routing/parsers/nearp_parser_test.cc b/ortools/routing/parsers/nearp_parser_test.cc index 5dc2c1f1243..2e42930290c 100644 --- a/ortools/routing/parsers/nearp_parser_test.cc +++ b/ortools/routing/parsers/nearp_parser_test.cc @@ -21,7 +21,7 @@ #define ROOT_DIR "com_google_ortools/" -namespace operations_research { +namespace operations_research::routing { namespace { TEST(NearpParserTest, Constructor) { NearpParser parser; @@ -130,4 +130,4 @@ TEST(NearpParserTest, LoadToy) { EXPECT_EQ(parser.GetNodeName(3), "N4"); } } // namespace -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/pdtsp_parser.cc b/ortools/routing/parsers/pdtsp_parser.cc index 0f34dda26f9..e291fe644a2 100644 --- a/ortools/routing/parsers/pdtsp_parser.cc +++ b/ortools/routing/parsers/pdtsp_parser.cc @@ -26,7 +26,7 @@ #include "ortools/base/strtoint.h" #include "ortools/util/filelineiter.h" -namespace operations_research { +namespace operations_research::routing { namespace { using absl::ByAnyChar; @@ -104,4 +104,4 @@ void PdTspParser::ProcessNewLine(const std::string& line) { } } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/pdtsp_parser.h b/ortools/routing/parsers/pdtsp_parser.h index 266d55d4b1c..fcb56d1af9a 100644 --- a/ortools/routing/parsers/pdtsp_parser.h +++ b/ortools/routing/parsers/pdtsp_parser.h @@ -25,7 +25,7 @@ #include "absl/strings/string_view.h" #include "ortools/base/types.h" -namespace operations_research { +namespace operations_research::routing { class PdTspParser { public: @@ -55,6 +55,6 @@ class PdTspParser { std::vector deliveries_; }; -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_PARSERS_PDTSP_PARSER_H_ diff --git a/ortools/routing/parsers/pdtsp_parser_test.cc b/ortools/routing/parsers/pdtsp_parser_test.cc index 445d840d6c1..86476fe4c52 100644 --- a/ortools/routing/parsers/pdtsp_parser_test.cc +++ b/ortools/routing/parsers/pdtsp_parser_test.cc @@ -22,7 +22,7 @@ #define ROOT_DIR "com_google_ortools/" -namespace operations_research { +namespace operations_research::routing { namespace { TEST(PdTspParserTest, LoadDataSet) { for (const std::string& data : { @@ -45,4 +45,4 @@ TEST(PdTspParserTest, LoadDataSet) { } } } // namespace -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/simple_graph.cc b/ortools/routing/parsers/simple_graph.cc index b5d95d35a77..1ee3d99cbc7 100644 --- a/ortools/routing/parsers/simple_graph.cc +++ b/ortools/routing/parsers/simple_graph.cc @@ -13,9 +13,9 @@ #include "ortools/routing/parsers/simple_graph.h" -namespace operations_research { +namespace operations_research::routing { Edge::Edge(const Arc& arc) : tail_(arc.tail()), head_(arc.head()) {} Arc::Arc(const Edge& edge) : tail_(edge.tail()), head_(edge.head()) {} -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/simple_graph.h b/ortools/routing/parsers/simple_graph.h index 13dc5d0eed9..6ac9d6a9b11 100644 --- a/ortools/routing/parsers/simple_graph.h +++ b/ortools/routing/parsers/simple_graph.h @@ -23,7 +23,7 @@ #include "absl/hash/hash.h" -namespace operations_research { +namespace operations_research::routing { class Arc; @@ -150,6 +150,6 @@ struct SimpleTimeWindow { T end; }; -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_PARSERS_SIMPLE_GRAPH_H_ diff --git a/ortools/routing/parsers/simple_graph_test.cc b/ortools/routing/parsers/simple_graph_test.cc index 88c6846b26c..75adb235859 100644 --- a/ortools/routing/parsers/simple_graph_test.cc +++ b/ortools/routing/parsers/simple_graph_test.cc @@ -19,7 +19,7 @@ #include "absl/hash/hash_testing.h" #include "gtest/gtest.h" -namespace operations_research { +namespace operations_research::routing { namespace { TEST(SimpleGraphTest, EdgeHashing) { EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({ @@ -100,4 +100,4 @@ TEST(Coordinates3Test, Double) { Coordinates3(1.0, 2.0, 3.0)})); } } // namespace -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/solomon_parser.cc b/ortools/routing/parsers/solomon_parser.cc index fd6ce39d7b8..936a1c8a7ff 100644 --- a/ortools/routing/parsers/solomon_parser.cc +++ b/ortools/routing/parsers/solomon_parser.cc @@ -31,7 +31,7 @@ #include "ortools/util/filelineiter.h" #include "re2/re2.h" -namespace operations_research { +namespace operations_research::routing { SolomonParser::SolomonParser() : sections_({{"VEHICLE", VEHICLE}, {"CUSTOMER", CUSTOMER}}) { @@ -179,4 +179,4 @@ bool SolomonSolutionParser::ParseFile(absl::string_view file_name) { return success; } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/solomon_parser.h b/ortools/routing/parsers/solomon_parser.h index 76f29335108..e4de718f020 100644 --- a/ortools/routing/parsers/solomon_parser.h +++ b/ortools/routing/parsers/solomon_parser.h @@ -53,7 +53,7 @@ #include "absl/strings/string_view.h" #include "ortools/routing/parsers/simple_graph.h" -namespace operations_research { +namespace operations_research::routing { // Solomon parser class. class SolomonParser { @@ -172,6 +172,6 @@ class SolomonSolutionParser { absl::flat_hash_map key_values_; }; -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_PARSERS_SOLOMON_PARSER_H_ diff --git a/ortools/routing/parsers/solomon_parser_test.cc b/ortools/routing/parsers/solomon_parser_test.cc index e983735f7eb..3e7787fb2f2 100644 --- a/ortools/routing/parsers/solomon_parser_test.cc +++ b/ortools/routing/parsers/solomon_parser_test.cc @@ -30,7 +30,7 @@ ABSL_FLAG(std::string, solomon_test_archive, ABSL_FLAG(std::string, solomon_test_instance, "google2.txt", "Solomon: testing instance"); -namespace operations_research { +namespace operations_research::routing { namespace { TEST(SolomonParserTest, LoadEmptyFileName) { std::string empty_file_name; @@ -95,4 +95,4 @@ TEST(SolomonSolutionParserTest, LoadFile) { } } // namespace -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/solution_serializer.cc b/ortools/routing/parsers/solution_serializer.cc index 7ff7894161d..9da6ac0c568 100644 --- a/ortools/routing/parsers/solution_serializer.cc +++ b/ortools/routing/parsers/solution_serializer.cc @@ -25,7 +25,7 @@ #include "absl/types/span.h" #include "ortools/base/logging.h" -namespace operations_research { +namespace operations_research::routing { RoutingOutputFormat RoutingOutputFormatFromString(std::string_view format) { const std::string format_normalized = @@ -422,4 +422,4 @@ std::string SerializeRouteToCVRPLIBString(const RoutingSolution::Route& route) { return current_route; } } // namespace -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/solution_serializer.h b/ortools/routing/parsers/solution_serializer.h index 874093bab41..fcf801b3309 100644 --- a/ortools/routing/parsers/solution_serializer.h +++ b/ortools/routing/parsers/solution_serializer.h @@ -32,7 +32,7 @@ #include "ortools/base/logging.h" #include "ortools/routing/parsers/simple_graph.h" -namespace operations_research { +namespace operations_research::routing { // Indicates the format in which the output should be done. This enumeration is // used for solutions and solver statistics. @@ -291,6 +291,6 @@ void PrintStatistic(absl::string_view name, T value, RoutingOutputFormat format) { absl::PrintF("%s\n", FormatStatistic(name, value, format)); } -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_PARSERS_SOLUTION_SERIALIZER_H_ diff --git a/ortools/routing/parsers/solution_serializer_test.cc b/ortools/routing/parsers/solution_serializer_test.cc index 726f1434962..795ab304c23 100644 --- a/ortools/routing/parsers/solution_serializer_test.cc +++ b/ortools/routing/parsers/solution_serializer_test.cc @@ -28,7 +28,7 @@ #include "ortools/base/options.h" #include "ortools/routing/parsers/simple_graph.h" -namespace operations_research { +namespace operations_research::routing { namespace { using testing::MatchesRegex; @@ -629,4 +629,4 @@ TEST(RoutingSolutionSerializerTest, FormatStatisticAsNearplibLongPrecision) { "STAT : 591.556557"); } } // namespace -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/tsplib_parser.cc b/ortools/routing/parsers/tsplib_parser.cc index b1f8d8d03a9..6c67f04f026 100644 --- a/ortools/routing/parsers/tsplib_parser.cc +++ b/ortools/routing/parsers/tsplib_parser.cc @@ -36,7 +36,7 @@ #include "ortools/util/filelineiter.h" #include "re2/re2.h" -namespace operations_research { +namespace operations_research::routing { namespace { // ----- Distances ----- @@ -852,4 +852,4 @@ void CVRPToursParser::ProcessNewLine(const std::string& line) { } } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/tsplib_parser.h b/ortools/routing/parsers/tsplib_parser.h index 99d2228b24e..a2f80c48812 100644 --- a/ortools/routing/parsers/tsplib_parser.h +++ b/ortools/routing/parsers/tsplib_parser.h @@ -36,7 +36,7 @@ #include "ortools/base/types.h" #include "ortools/routing/parsers/simple_graph.h" -namespace operations_research { +namespace operations_research::routing { class TspLibParser final { public: @@ -248,6 +248,6 @@ class CVRPToursParser final { std::vector> tours_; int64_t cost_; }; -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_PARSERS_TSPLIB_PARSER_H_ diff --git a/ortools/routing/parsers/tsplib_parser_test.cc b/ortools/routing/parsers/tsplib_parser_test.cc index 77af209ff48..350f0455039 100644 --- a/ortools/routing/parsers/tsplib_parser_test.cc +++ b/ortools/routing/parsers/tsplib_parser_test.cc @@ -32,7 +32,7 @@ #define ROOT_DIR "com_google_ortools/" -namespace operations_research { +namespace operations_research::routing { namespace { TEST(TspLibParserTest, GeneratedDataSets) { @@ -342,4 +342,4 @@ TEST(CVRPToursParserTest, LoadAllDataSets) { } } } // namespace -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/tsptw_parser.cc b/ortools/routing/parsers/tsptw_parser.cc index 06a1b1caa35..5f767b7500d 100644 --- a/ortools/routing/parsers/tsptw_parser.cc +++ b/ortools/routing/parsers/tsptw_parser.cc @@ -29,7 +29,7 @@ #include "ortools/base/zipfile.h" #include "ortools/util/filelineiter.h" -namespace operations_research { +namespace operations_research::routing { namespace { @@ -211,4 +211,4 @@ bool TspTWParser::ParseDaSilvaUrrutia(absl::string_view file_name) { return true; } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/parsers/tsptw_parser.h b/ortools/routing/parsers/tsptw_parser.h index 5b8d5792680..697ef7a5ee2 100644 --- a/ortools/routing/parsers/tsptw_parser.h +++ b/ortools/routing/parsers/tsptw_parser.h @@ -29,7 +29,7 @@ #include "ortools/base/types.h" #include "ortools/routing/parsers/simple_graph.h" -namespace operations_research { +namespace operations_research::routing { class TspTWParser final { public: @@ -84,6 +84,6 @@ class TspTWParser final { std::vector distance_matrix_; }; -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_PARSERS_TSPTW_PARSER_H_ diff --git a/ortools/routing/parsers/tsptw_parser_test.cc b/ortools/routing/parsers/tsptw_parser_test.cc index d566c59416e..f74809c73bf 100644 --- a/ortools/routing/parsers/tsptw_parser_test.cc +++ b/ortools/routing/parsers/tsptw_parser_test.cc @@ -20,7 +20,7 @@ #define ROOT_DIR "com_google_ortools/" -namespace operations_research { +namespace operations_research::routing { namespace { TEST(TspTWParserTest, LoadDataSet) { @@ -69,4 +69,4 @@ TEST(TspTWParserTest, LoadDataSet) { } } // namespace -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/routing.cc b/ortools/routing/routing.cc index fa30fce9f16..fd4098192af 100644 --- a/ortools/routing/routing.cc +++ b/ortools/routing/routing.cc @@ -106,7 +106,7 @@ class TwoOpt; // Trace settings -namespace operations_research { +namespace operations_research::routing { std::string RoutingModel::RouteDimensionTravelInfo::DebugString( std::string line_prefix) const { @@ -238,165 +238,247 @@ SweepArranger* RoutingModel::sweep_arranger() const { } void RoutingModel::NodeNeighborsByCostClass::ComputeNeighbors( - const RoutingModel& routing_model, const NodeNeighborsParameters& params) { + const NodeNeighborsParameters& params) { auto [num_neighbors, add_vehicle_starts_to_neighbors, + add_vehicle_ends_to_neighbors, only_sort_neighbors_for_partial_neighborhoods] = params; DCHECK_GE(num_neighbors, 0); // TODO(user): consider checking search limits. - const int size = routing_model.Size(); - const int num_non_start_end_nodes = size - routing_model.vehicles(); - const int size_with_vehicle_nodes = size + routing_model.vehicles(); + const int size = routing_model_.Size(); + const int num_non_start_end_nodes = size - routing_model_.vehicles(); + const int size_with_vehicle_nodes = size + routing_model_.vehicles(); const int max_num_neighbors = std::max(num_non_start_end_nodes - 1, 0); num_neighbors = std::min(max_num_neighbors, num_neighbors); - node_index_to_neighbors_by_cost_class_.clear(); - node_index_to_neighbor_indicator_by_cost_class_.clear(); + node_index_to_incoming_neighbors_by_cost_class_.clear(); + node_index_to_outgoing_neighbors_by_cost_class_.clear(); + node_index_to_outgoing_neighbor_indicator_by_cost_class_.clear(); + all_incoming_nodes_.clear(); + all_outgoing_nodes_.clear(); if (num_neighbors == max_num_neighbors && only_sort_neighbors_for_partial_neighborhoods) { - all_nodes_.reserve(size); - for (int node = 0; node < size; node++) { - if (add_vehicle_starts_to_neighbors || !routing_model.IsStart(node)) { - all_nodes_.push_back(node); + all_incoming_nodes_.reserve(size); + all_outgoing_nodes_.reserve(size); + for (int node = 0; node < size_with_vehicle_nodes; node++) { + const bool not_start = !routing_model_.IsStart(node); + const bool not_end = !routing_model_.IsEnd(node); + if (not_start && (not_end || add_vehicle_ends_to_neighbors)) { + all_outgoing_nodes_.push_back(node); + } + if (not_end && (not_start || add_vehicle_starts_to_neighbors)) { + all_incoming_nodes_.push_back(node); } } return; } - const int num_cost_classes = routing_model.GetCostClassesCount(); - node_index_to_neighbors_by_cost_class_.resize(num_cost_classes); - node_index_to_neighbor_indicator_by_cost_class_.resize(num_cost_classes); + const int num_cost_classes = routing_model_.GetCostClassesCount(); + node_index_to_incoming_neighbors_by_cost_class_.resize(num_cost_classes); + node_index_to_outgoing_neighbors_by_cost_class_.resize(num_cost_classes); + node_index_to_outgoing_neighbor_indicator_by_cost_class_.resize( + num_cost_classes); std::vector>> - node_index_to_costs_by_cost_class(num_cost_classes); + node_index_to_outgoing_costs_by_cost_class(num_cost_classes); for (int cc = 0; cc < num_cost_classes; cc++) { - node_index_to_neighbors_by_cost_class_[cc].resize(size_with_vehicle_nodes); - if (!routing_model.HasVehicleWithCostClassIndex( + if (!routing_model_.HasVehicleWithCostClassIndex( RoutingCostClassIndex(cc))) { continue; } - node_index_to_neighbor_indicator_by_cost_class_[cc].resize( + node_index_to_incoming_neighbors_by_cost_class_[cc].resize( size_with_vehicle_nodes); - node_index_to_costs_by_cost_class[cc].resize(size_with_vehicle_nodes); + node_index_to_outgoing_neighbors_by_cost_class_[cc].resize(size); + node_index_to_outgoing_neighbor_indicator_by_cost_class_[cc].resize(size); + node_index_to_outgoing_costs_by_cost_class[cc].resize(size); for (int node = 0; node < size_with_vehicle_nodes; node++) { - node_index_to_neighbors_by_cost_class_[cc][node].reserve( - num_neighbors + routing_model.vehicles()); - node_index_to_neighbor_indicator_by_cost_class_[cc][node].resize(size, - false); - node_index_to_costs_by_cost_class[cc][node].resize(size, -1); + node_index_to_incoming_neighbors_by_cost_class_[cc][node].reserve( + num_neighbors + routing_model_.vehicles()); + if (node < size) { + node_index_to_outgoing_neighbors_by_cost_class_[cc][node].reserve( + num_neighbors + routing_model_.vehicles()); + node_index_to_outgoing_neighbor_indicator_by_cost_class_[cc][node] + .resize(size_with_vehicle_nodes, false); + node_index_to_outgoing_costs_by_cost_class[cc][node].resize( + size_with_vehicle_nodes, -1); + } } } - std::vector neighbors; + std::vector outgoing_neighbors; for (int cost_class = 0; cost_class < num_cost_classes; cost_class++) { - if (!routing_model.HasVehicleWithCostClassIndex( + if (!routing_model_.HasVehicleWithCostClassIndex( RoutingCostClassIndex(cost_class))) { // No vehicle with this cost class, avoid unnecessary computations. continue; } - std::vector>& node_index_to_neighbors = - node_index_to_neighbors_by_cost_class_[cost_class]; - std::vector>& node_index_to_neighbor_indicator = - node_index_to_neighbor_indicator_by_cost_class_[cost_class]; - std::vector>& node_index_to_costs = - node_index_to_costs_by_cost_class[cost_class]; + std::vector>& node_index_to_incoming_neighbors = + node_index_to_incoming_neighbors_by_cost_class_[cost_class]; + std::vector>& node_index_to_outgoing_neighbors = + node_index_to_outgoing_neighbors_by_cost_class_[cost_class]; + std::vector>& node_index_to_outgoing_neighbor_indicator = + node_index_to_outgoing_neighbor_indicator_by_cost_class_[cost_class]; + std::vector>& node_index_to_outgoing_costs = + node_index_to_outgoing_costs_by_cost_class[cost_class]; for (int node_index = 0; node_index < size; ++node_index) { - if (routing_model.IsStart(node_index)) { + if (routing_model_.IsStart(node_index)) { // For vehicle start/ends, we consider all nodes (see below). continue; } // TODO(user): Use the model's IndexNeighborFinder when available. - neighbors.clear(); - neighbors.reserve(num_non_start_end_nodes); + outgoing_neighbors.clear(); + outgoing_neighbors.reserve(num_non_start_end_nodes); if (num_neighbors > 0) { - std::vector& costs = node_index_to_costs[node_index]; + std::vector& costs = node_index_to_outgoing_costs[node_index]; for (int after_node = 0; after_node < size; ++after_node) { - if (after_node != node_index && !routing_model.IsStart(after_node)) { - costs[after_node] = routing_model.GetArcCostForClass( + if (after_node != node_index && !routing_model_.IsStart(after_node)) { + costs[after_node] = routing_model_.GetArcCostForClass( node_index, after_node, cost_class); - neighbors.push_back(after_node); + outgoing_neighbors.push_back(after_node); } } // Get the 'num_neighbors' closest neighbors. - DCHECK_GE(neighbors.size(), num_neighbors); - std::nth_element( - neighbors.begin(), neighbors.begin() + num_neighbors - 1, - neighbors.end(), [&costs](int n1, int n2) { - return std::tie(costs[n1], n1) < std::tie(costs[n2], n2); - }); - neighbors.resize(num_neighbors); + DCHECK_GE(outgoing_neighbors.size(), num_neighbors); + std::nth_element(outgoing_neighbors.begin(), + outgoing_neighbors.begin() + num_neighbors - 1, + outgoing_neighbors.end(), [&costs](int n1, int n2) { + return std::tie(costs[n1], n1) < + std::tie(costs[n2], n2); + }); + outgoing_neighbors.resize(num_neighbors); } // Add neighborhoods. - for (int neighbor : neighbors) { - DCHECK(!routing_model.IsEnd(neighbor) && - !routing_model.IsStart(neighbor)); - if (node_index_to_neighbor_indicator[node_index][neighbor]) { - DCHECK(node_index_to_neighbor_indicator[neighbor][node_index]); - continue; - } - DCHECK(!node_index_to_neighbor_indicator[node_index][neighbor]); - node_index_to_neighbor_indicator[node_index][neighbor] = true; - node_index_to_neighbors[node_index].push_back(neighbor); - DCHECK(!node_index_to_neighbor_indicator[neighbor][node_index]); - node_index_to_neighbor_indicator[neighbor][node_index] = true; - node_index_to_neighbors[neighbor].push_back(node_index); + for (int outgoing_neighbor : outgoing_neighbors) { + DCHECK(!routing_model_.IsEnd(outgoing_neighbor) && + !routing_model_.IsStart(outgoing_neighbor)); + DCHECK(!node_index_to_outgoing_neighbor_indicator[node_index] + [outgoing_neighbor]); + node_index_to_outgoing_neighbor_indicator[node_index] + [outgoing_neighbor] = true; + node_index_to_outgoing_neighbors[node_index].push_back( + outgoing_neighbor); + // node_index is an incoming neighbor of outgoing_neighbor. + node_index_to_incoming_neighbors[outgoing_neighbor].push_back( + node_index); } + } + } - // Add all vehicle starts as neighbors to this node and vice-versa. - // TODO(user): Consider keeping vehicle start/ends out of neighbors, to - // prune arcs going from node to start for instance. - // TODO(user): Investigate whether we actually need to keep track of - // neighbors for vehicle ends. - // TODO(user): Investigate if vehicle ends should be considered as - // neighbors for every node too. - // TODO(user): In the current state of the function, vehicle starts - // aren't set as neighbors for vehicle ends. Investigate if that's WAI. - for (int vehicle = 0; vehicle < routing_model.vehicles(); vehicle++) { - const int vehicle_start = routing_model.Start(vehicle); - const int64_t cost_from_start = routing_model.GetArcCostForClass( - vehicle_start, node_index, cost_class); + // Add all vehicle start/ends as incoming/outgoing neighbors for all nodes. + for (int cost_class = 0; cost_class < num_cost_classes; cost_class++) { + if (!routing_model_.HasVehicleWithCostClassIndex( + RoutingCostClassIndex(cost_class))) { + // No vehicle with this cost class, avoid unnecessary computations. + continue; + } + std::vector>& node_index_to_incoming_neighbors = + node_index_to_incoming_neighbors_by_cost_class_[cost_class]; + std::vector>& node_index_to_outgoing_neighbors = + node_index_to_outgoing_neighbors_by_cost_class_[cost_class]; + std::vector>& node_index_to_outgoing_neighbor_indicator = + node_index_to_outgoing_neighbor_indicator_by_cost_class_[cost_class]; + std::vector>& node_index_to_outgoing_costs = + node_index_to_outgoing_costs_by_cost_class[cost_class]; + for (int vehicle = 0; vehicle < routing_model_.vehicles(); vehicle++) { + const int vehicle_start = routing_model_.Start(vehicle); + const int vehicle_end = routing_model_.End(vehicle); + + // Mark vehicle_start -> vehicle_end as a neighborhood arc. + DCHECK(!node_index_to_outgoing_neighbor_indicator[vehicle_start] + [vehicle_end]); + node_index_to_outgoing_neighbor_indicator[vehicle_start][vehicle_end] = + true; + if (add_vehicle_starts_to_neighbors) { + node_index_to_incoming_neighbors[vehicle_end].push_back(vehicle_start); + } + if (add_vehicle_ends_to_neighbors) { + node_index_to_outgoing_neighbors[vehicle_start].push_back(vehicle_end); + } + node_index_to_outgoing_costs[vehicle_start][vehicle_end] = + routing_model_.GetArcCostForClass(vehicle_start, vehicle_end, + cost_class); + + for (int node_index = 0; node_index < size; ++node_index) { + if (routing_model_.IsStart(node_index)) continue; + + // Mark vehicle_start -> node_index as a neighborhood arc. + DCHECK(!node_index_to_outgoing_neighbor_indicator[node_index] + [vehicle_start]); + DCHECK(!node_index_to_outgoing_neighbor_indicator[vehicle_start] + [node_index]); + node_index_to_outgoing_neighbor_indicator[vehicle_start][node_index] = + true; if (add_vehicle_starts_to_neighbors) { - DCHECK(!node_index_to_neighbor_indicator[node_index][vehicle_start]); - node_index_to_neighbor_indicator[node_index][vehicle_start] = true; - node_index_to_neighbors[node_index].push_back(vehicle_start); - node_index_to_costs[node_index][vehicle_start] = cost_from_start; + node_index_to_incoming_neighbors[node_index].push_back(vehicle_start); + } + node_index_to_outgoing_neighbors[vehicle_start].push_back(node_index); + node_index_to_outgoing_costs[vehicle_start][node_index] = + routing_model_.GetArcCostForClass(vehicle_start, node_index, + cost_class); + + // Mark node_index -> vehicle_end as a neighborhood arc. + DCHECK(!node_index_to_outgoing_neighbor_indicator[node_index] + [vehicle_end]); + node_index_to_outgoing_neighbor_indicator[node_index][vehicle_end] = + true; + node_index_to_incoming_neighbors[vehicle_end].push_back(node_index); + if (add_vehicle_ends_to_neighbors) { + node_index_to_outgoing_neighbors[node_index].push_back(vehicle_end); } - DCHECK(!node_index_to_neighbor_indicator[vehicle_start][node_index]); - node_index_to_neighbor_indicator[vehicle_start][node_index] = true; - node_index_to_neighbors[vehicle_start].push_back(node_index); - node_index_to_costs[vehicle_start][node_index] = cost_from_start; - - const int vehicle_end = routing_model.End(vehicle); - DCHECK(!node_index_to_neighbor_indicator[vehicle_end][node_index]); - node_index_to_neighbor_indicator[vehicle_end][node_index] = true; - node_index_to_neighbors[vehicle_end].push_back(node_index); - node_index_to_costs[vehicle_end][node_index] = - routing_model.GetArcCostForClass(node_index, vehicle_end, - cost_class); + node_index_to_outgoing_costs[node_index][vehicle_end] = + routing_model_.GetArcCostForClass(node_index, vehicle_end, + cost_class); } } } - // Sort the neighbors into node_index_to_neighbors_by_cost_class_ by cost. + // Sort the neighbors into + // node_index_to_{incoming,outgoing}_neighbors_by_cost_class_ by cost. for (int cost_class = 0; cost_class < num_cost_classes; cost_class++) { - if (!routing_model.HasVehicleWithCostClassIndex( + if (!routing_model_.HasVehicleWithCostClassIndex( RoutingCostClassIndex(cost_class))) { // No vehicle with this cost class. continue; } + const std::vector>& node_index_to_outgoing_costs = + node_index_to_outgoing_costs_by_cost_class[cost_class]; for (int node_index = 0; node_index < size_with_vehicle_nodes; ++node_index) { - auto& node_neighbors = - node_index_to_neighbors_by_cost_class_[cost_class][node_index]; - const std::vector& costs = - node_index_to_costs_by_cost_class[cost_class][node_index]; - absl::c_sort(node_neighbors, [&costs](int n1, int n2) { - DCHECK_GE(costs[n1], 0); - DCHECK_GE(costs[n2], 0); - return std::tie(costs[n1], n1) < std::tie(costs[n2], n2); - }); + std::vector& incoming_node_neighbors = + node_index_to_incoming_neighbors_by_cost_class_[cost_class] + [node_index]; + absl::c_sort( + incoming_node_neighbors, + [node_index, size, &node_index_to_outgoing_costs](int n1, int n2) { + DCHECK_GE(node_index_to_outgoing_costs[n1][node_index], 0); + DCHECK_GE(node_index_to_outgoing_costs[n2][node_index], 0); + DCHECK_LT(n1, size); + DCHECK_LT(n2, size); + return std::tie(node_index_to_outgoing_costs[n1][node_index], n1) < + std::tie(node_index_to_outgoing_costs[n2][node_index], n2); + }); // Check that there are no duplicate elements. - DCHECK(absl::c_adjacent_find(node_neighbors) == node_neighbors.end()); + DCHECK(absl::c_adjacent_find(incoming_node_neighbors) == + incoming_node_neighbors.end()); + + if (node_index < size) { + std::vector& outgoing_node_neighbors = + node_index_to_outgoing_neighbors_by_cost_class_[cost_class] + [node_index]; + const std::vector& outgoing_costs = + node_index_to_outgoing_costs[node_index]; + absl::c_sort(outgoing_node_neighbors, + [&outgoing_costs](int n1, int n2) { + DCHECK_GE(outgoing_costs[n1], 0); + DCHECK_GE(outgoing_costs[n2], 0); + return std::tie(outgoing_costs[n1], n1) < + std::tie(outgoing_costs[n2], n2); + }); + + // Check that there are no duplicate elements. + DCHECK(absl::c_adjacent_find(outgoing_node_neighbors) == + outgoing_node_neighbors.end()); + } } } } @@ -404,7 +486,7 @@ void RoutingModel::NodeNeighborsByCostClass::ComputeNeighbors( const RoutingModel::NodeNeighborsByCostClass* RoutingModel::GetOrCreateNodeNeighborsByCostClass( double neighbors_ratio, int64_t min_neighbors, double& neighbors_ratio_used, - bool add_vehicle_starts_to_neighbors, + bool add_vehicle_starts_to_neighbors, bool add_vehicle_ends_to_neighbors, bool only_sort_neighbors_for_partial_neighborhoods) { const int64_t num_non_start_end_nodes = Size() - vehicles(); neighbors_ratio_used = neighbors_ratio; @@ -417,6 +499,7 @@ RoutingModel::GetOrCreateNodeNeighborsByCostClass( } return GetOrCreateNodeNeighborsByCostClass( {num_neighbors, add_vehicle_starts_to_neighbors, + add_vehicle_ends_to_neighbors, only_sort_neighbors_for_partial_neighborhoods}); } @@ -428,8 +511,9 @@ RoutingModel::GetOrCreateNodeNeighborsByCostClass( if (node_neighbors_by_cost_class != nullptr) { return node_neighbors_by_cost_class.get(); } - node_neighbors_by_cost_class = std::make_unique(); - node_neighbors_by_cost_class->ComputeNeighbors(*this, params); + node_neighbors_by_cost_class = + std::make_unique(this); + node_neighbors_by_cost_class->ComputeNeighbors(params); return node_neighbors_by_cost_class.get(); } @@ -4548,11 +4632,25 @@ void RoutingModel::CreateNeighborhoodOperators( GetOrCreateNodeNeighborsByCostClass( parameters.ls_operator_neighbors_ratio(), parameters.ls_operator_min_neighbors(), neighbors_ratio_used, - /*add_vehicle_starts_to_neighbors=*/false); + /*add_vehicle_starts_to_neighbors=*/false, + /*add_vehicle_ends_to_neighbors=*/false); + // TODO(user): Consider passing incoming/outgoing neighbors separately to + // LS operators. This would allow the Cross operator to use both incoming and + // outgoing neighbors, to exchange path starts and ends independently. const auto get_neighbors = [neighbors_by_cost_class, this]( int64_t node, int64_t start) -> const std::vector& { - return neighbors_by_cost_class->GetNeighborsOfNodeForCostClass( + if (IsEnd(node)) { + // HACK(user): As of CL/651760817, vehicle end nodes no longer have + // outgoing neighbors, which causes neighborhood operators that iterate + // on vehicle ends as seeds to fail. This is a temporary hack to consider + // the incoming neighbors for vehicle ends, before the proper fix which is + // to pass 2 separate callbacks for incoming and outgoing neighbors to + // neighborhood operators. + return neighbors_by_cost_class->GetIncomingNeighborsOfNodeForCostClass( + GetCostClassIndexOfVehicle(VehicleIndex(start)).value(), node); + } + return neighbors_by_cost_class->GetOutgoingNeighborsOfNodeForCostClass( GetCostClassIndexOfVehicle(VehicleIndex(start)).value(), node); }; @@ -4714,6 +4812,7 @@ void RoutingModel::CreateNeighborhoodOperators( this, [this]() { return CheckLimit(time_buffer_); }, GetLocalSearchArcCostCallback(parameters), parameters.local_cheapest_insertion_pickup_delivery_strategy(), + parameters.local_cheapest_insertion_sorting_mode(), GetOrCreateLocalSearchFilterManager( parameters, {/*filter_objective=*/false, /*filter_with_cp_solver=*/false}), @@ -5667,6 +5766,8 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders( } const RoutingSearchParameters::PairInsertionStrategy lci_pair_strategy = search_parameters.local_cheapest_insertion_pickup_delivery_strategy(); + const RoutingSearchParameters::InsertionSortingMode sorting_mode = + search_parameters.local_cheapest_insertion_sorting_mode(); first_solution_filtered_decision_builders_ [FirstSolutionStrategy::LOCAL_CHEAPEST_INSERTION] = CreateIntVarFilteredDecisionBuilder< @@ -5674,7 +5775,7 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders( [this](int64_t i, int64_t j, int64_t vehicle) { return GetArcCostForVehicle(i, j, vehicle); }, - lci_pair_strategy, + lci_pair_strategy, sorting_mode, GetOrCreateLocalSearchFilterManager( search_parameters, {/*filter_objective=*/false, /*filter_with_cp_solver=*/false}), @@ -5685,7 +5786,7 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders( [this](int64_t i, int64_t j, int64_t vehicle) { return GetArcCostForVehicle(i, j, vehicle); }, - lci_pair_strategy, + lci_pair_strategy, sorting_mode, GetOrCreateLocalSearchFilterManager(search_parameters, {/*filter_objective=*/false, /*filter_with_cp_solver=*/true}), @@ -5706,7 +5807,7 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders( [FirstSolutionStrategy::LOCAL_CHEAPEST_COST_INSERTION] = CreateIntVarFilteredDecisionBuilder< LocalCheapestInsertionFilteredHeuristic>( - /*evaluator=*/nullptr, lcci_pair_strategy, + /*evaluator=*/nullptr, lcci_pair_strategy, sorting_mode, GetOrCreateLocalSearchFilterManager( search_parameters, {/*filter_objective=*/true, /*filter_with_cp_solver=*/false}), @@ -5714,7 +5815,7 @@ void RoutingModel::CreateFirstSolutionDecisionBuilders( IntVarFilteredDecisionBuilder* const strong_lcci = CreateIntVarFilteredDecisionBuilder< LocalCheapestInsertionFilteredHeuristic>( - /*evaluator=*/nullptr, lcci_pair_strategy, + /*evaluator=*/nullptr, lcci_pair_strategy, sorting_mode, GetOrCreateLocalSearchFilterManager(search_parameters, {/*filter_objective=*/true, /*filter_with_cp_solver=*/true}), @@ -5973,75 +6074,94 @@ void RoutingModel::SetupDecisionBuilders( void RoutingModel::SetupMetaheuristics( const RoutingSearchParameters& search_parameters) { - SearchMonitor* optimize = nullptr; - const LocalSearchMetaheuristic::Value metaheuristic = - search_parameters.local_search_metaheuristic(); - // Some metaheuristics will effectively never terminate; warn - // user if they fail to set a time limit. - bool limit_too_long = - !search_parameters.has_time_limit() && - search_parameters.solution_limit() == std::numeric_limits::max(); - const int64_t optimization_step = std::max( - MathUtil::SafeRound(search_parameters.optimization_step()), - One()); - switch (metaheuristic) { - case LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH: { - std::function>(int64_t, int64_t)> - same_class_arc_getter; - if (search_parameters - .guided_local_search_penalize_with_vehicle_classes()) { - same_class_arc_getter = - absl::bind_front(&RoutingModel::GetSameVehicleClassArcs, this); + BaseObjectiveMonitor* optimize = nullptr; + const auto build_metaheuristic = [this, &search_parameters]( + LocalSearchMetaheuristic::Value + metaheuristic) { + BaseObjectiveMonitor* optimize = nullptr; + // Some metaheuristics will effectively never terminate; warn + // user if they fail to set a time limit. + bool limit_too_long = !search_parameters.has_time_limit() && + search_parameters.solution_limit() == + std::numeric_limits::max(); + const int64_t optimization_step = std::max( + MathUtil::SafeRound(search_parameters.optimization_step()), + One()); + switch (metaheuristic) { + case LocalSearchMetaheuristic::GUIDED_LOCAL_SEARCH: { + std::function>(int64_t, + int64_t)> + same_class_arc_getter; + if (search_parameters + .guided_local_search_penalize_with_vehicle_classes()) { + same_class_arc_getter = + absl::bind_front(&RoutingModel::GetSameVehicleClassArcs, this); + } + if (CostsAreHomogeneousAcrossVehicles()) { + optimize = solver_->MakeGuidedLocalSearch( + false, cost_, + [this](int64_t i, int64_t j) { return GetHomogeneousCost(i, j); }, + optimization_step, nexts_, + search_parameters.guided_local_search_lambda_coefficient(), + std::move(same_class_arc_getter), + search_parameters + .guided_local_search_reset_penalties_on_new_best_solution()); + } else { + optimize = solver_->MakeGuidedLocalSearch( + false, cost_, + [this](int64_t i, int64_t j, int64_t k) { + return GetArcCostForVehicle(i, j, k); + }, + optimization_step, nexts_, vehicle_vars_, + search_parameters.guided_local_search_lambda_coefficient(), + std::move(same_class_arc_getter), + search_parameters + .guided_local_search_reset_penalties_on_new_best_solution()); + } + break; } - if (CostsAreHomogeneousAcrossVehicles()) { - optimize = solver_->MakeGuidedLocalSearch( - false, cost_, - [this](int64_t i, int64_t j) { return GetHomogeneousCost(i, j); }, - optimization_step, nexts_, - search_parameters.guided_local_search_lambda_coefficient(), - std::move(same_class_arc_getter), - search_parameters - .guided_local_search_reset_penalties_on_new_best_solution()); - } else { - optimize = solver_->MakeGuidedLocalSearch( - false, cost_, - [this](int64_t i, int64_t j, int64_t k) { - return GetArcCostForVehicle(i, j, k); - }, - optimization_step, nexts_, vehicle_vars_, - search_parameters.guided_local_search_lambda_coefficient(), - std::move(same_class_arc_getter), - search_parameters - .guided_local_search_reset_penalties_on_new_best_solution()); + case LocalSearchMetaheuristic::SIMULATED_ANNEALING: + optimize = solver_->MakeSimulatedAnnealing(false, cost_, + optimization_step, 100); + break; + case LocalSearchMetaheuristic::TABU_SEARCH: + optimize = solver_->MakeTabuSearch(false, cost_, optimization_step, + nexts_, 10, 10, .8); + break; + case LocalSearchMetaheuristic::GENERIC_TABU_SEARCH: { + std::vector tabu_vars; + if (tabu_var_callback_) { + tabu_vars = tabu_var_callback_(this); + } else { + tabu_vars.push_back(cost_); + } + optimize = solver_->MakeGenericTabuSearch( + false, cost_, optimization_step, tabu_vars, 100); + break; } - break; + default: + limit_too_long = false; + optimize = solver_->MakeMinimize(cost_, optimization_step); } - case LocalSearchMetaheuristic::SIMULATED_ANNEALING: - optimize = - solver_->MakeSimulatedAnnealing(false, cost_, optimization_step, 100); - break; - case LocalSearchMetaheuristic::TABU_SEARCH: - optimize = solver_->MakeTabuSearch(false, cost_, optimization_step, - nexts_, 10, 10, .8); - break; - case LocalSearchMetaheuristic::GENERIC_TABU_SEARCH: { - std::vector tabu_vars; - if (tabu_var_callback_) { - tabu_vars = tabu_var_callback_(this); - } else { - tabu_vars.push_back(cost_); - } - optimize = solver_->MakeGenericTabuSearch(false, cost_, optimization_step, - tabu_vars, 100); - break; + if (limit_too_long) { + LOG(WARNING) << LocalSearchMetaheuristic::Value_Name(metaheuristic) + << " specified without sane timeout: solve may run forever."; } - default: - limit_too_long = false; - optimize = solver_->MakeMinimize(cost_, optimization_step); - } - if (limit_too_long) { - LOG(WARNING) << LocalSearchMetaheuristic::Value_Name(metaheuristic) - << " specified without sane timeout: solve may run forever."; + return optimize; + }; + if (!search_parameters.local_search_metaheuristics().empty()) { + std::vector metaheuristics; + for (int i = 0; i < search_parameters.local_search_metaheuristics_size(); + ++i) { + metaheuristics.push_back(build_metaheuristic( + search_parameters.local_search_metaheuristics(i))); + } + optimize = solver_->MakeRoundRobinCompoundObjectiveMonitor( + metaheuristics, + search_parameters.num_max_local_optima_before_metaheuristic_switch()); + } else { + optimize = + build_metaheuristic(search_parameters.local_search_metaheuristic()); } metaheuristic_ = optimize; monitors_.push_back(optimize); @@ -7314,4 +7434,4 @@ void RoutingDimension::SetupSlackAndDependentTransitCosts() const { } } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/routing.h b/ortools/routing/routing.h index 17f4c304542..53b6735539f 100644 --- a/ortools/routing/routing.h +++ b/ortools/routing/routing.h @@ -196,14 +196,12 @@ #include "ortools/util/saturated_arithmetic.h" #include "ortools/util/sorted_interval_list.h" -namespace operations_research { +namespace operations_research::routing { class FinalizerVariables; class GlobalDimensionCumulOptimizer; class LocalDimensionCumulOptimizer; -class LocalSearchPhaseParameters; #ifndef SWIG -class IndexNeighborFinder; class IntVarFilteredDecisionBuilder; #endif class RoutingDimension; @@ -1591,6 +1589,7 @@ class RoutingModel { struct NodeNeighborsParameters { int num_neighbors; bool add_vehicle_starts_to_neighbors = true; + bool add_vehicle_ends_to_neighbors = false; // In NodeNeighborsByCostClass, neighbors for each node are sorted by // increasing "cost" for each node. The following parameter determines if // neighbors are sorted based on distance only when the neighborhood is @@ -1602,6 +1601,8 @@ class RoutingModel { return num_neighbors == other.num_neighbors && add_vehicle_starts_to_neighbors == other.add_vehicle_starts_to_neighbors && + add_vehicle_ends_to_neighbors == + other.add_vehicle_ends_to_neighbors && only_sort_neighbors_for_partial_neighborhoods == other.only_sort_neighbors_for_partial_neighborhoods; } @@ -1609,41 +1610,81 @@ class RoutingModel { friend H AbslHashValue(H h, const NodeNeighborsParameters& params) { return H::combine(std::move(h), params.num_neighbors, params.add_vehicle_starts_to_neighbors, + params.add_vehicle_ends_to_neighbors, params.only_sort_neighbors_for_partial_neighborhoods); } }; class NodeNeighborsByCostClass { public: - NodeNeighborsByCostClass() = default; + explicit NodeNeighborsByCostClass(const RoutingModel* routing_model) + : routing_model_(*routing_model) {}; /// Computes num_neighbors neighbors of all nodes for every cost class in /// routing_model. - void ComputeNeighbors(const RoutingModel& routing_model, - const NodeNeighborsParameters& params); - /// Returns the neighbors of the given node for the given cost_class. - const std::vector& GetNeighborsOfNodeForCostClass( + void ComputeNeighbors(const NodeNeighborsParameters& params); + /// Returns the incoming neighbors of the given node for the given + /// cost_class, i.e. all 'neighbor' indices such that neighbor -> node_index + /// is a neighborhood arc for 'cost_class'. + const std::vector& GetIncomingNeighborsOfNodeForCostClass( int cost_class, int node_index) const { - return node_index_to_neighbors_by_cost_class_.empty() - ? all_nodes_ - : node_index_to_neighbors_by_cost_class_[cost_class] - [node_index]; + if (routing_model_.IsStart(node_index)) return empty_neighbors_; + + if (node_index_to_incoming_neighbors_by_cost_class_.empty()) { + return all_incoming_nodes_; + } + const std::vector>& node_index_to_incoming_neighbors = + node_index_to_incoming_neighbors_by_cost_class_[cost_class]; + if (node_index_to_incoming_neighbors.empty()) { + return empty_neighbors_; + } + return node_index_to_incoming_neighbors[node_index]; } - /// Returns whether or not node 'neighbor_index' is actually a neighbor of - /// node 'node_index' for the given cost_class. - bool IsNeighborOfNodeForCostClass(int cost_class, int node_index, - int neighbor_index) const { - return node_index_to_neighbor_indicator_by_cost_class_.empty() - ? true - : node_index_to_neighbor_indicator_by_cost_class_ - [cost_class][node_index][neighbor_index]; + + /// Returns the neighbors that are outgoing from 'node_index', i.e. + /// 'neighbor' indices such that node_index -> neighbor is a neighborhood + /// arc for 'cost_class'. + const std::vector& GetOutgoingNeighborsOfNodeForCostClass( + int cost_class, int node_index) const { + if (routing_model_.IsEnd(node_index)) return empty_neighbors_; + + if (node_index_to_outgoing_neighbors_by_cost_class_.empty()) { + return all_outgoing_nodes_; + } + const std::vector>& node_index_to_outgoing_neighbors = + node_index_to_outgoing_neighbors_by_cost_class_[cost_class]; + if (node_index_to_outgoing_neighbors.empty()) { + return empty_neighbors_; + } + return node_index_to_outgoing_neighbors[node_index]; + } + /// Returns true iff arc from_node -> to_node is a neighborhood arc for the + /// given cost_class, i.e. iff arc.to_node is an outgoing neighbor of + /// arc.from_node for 'cost_class'. + bool IsNeighborhoodArcForCostClass(int cost_class, int64_t from, + int64_t to) const { + if (node_index_to_outgoing_neighbor_indicator_by_cost_class_.empty()) { + return true; + } + if (routing_model_.IsEnd(from)) { + return false; + } + return node_index_to_outgoing_neighbor_indicator_by_cost_class_ + [cost_class][from][to]; } private: + const RoutingModel& routing_model_; + static constexpr std::vector empty_neighbors_ = {}; + + std::vector>> + node_index_to_incoming_neighbors_by_cost_class_; std::vector>> - node_index_to_neighbors_by_cost_class_; + node_index_to_outgoing_neighbors_by_cost_class_; std::vector>> - node_index_to_neighbor_indicator_by_cost_class_; - std::vector all_nodes_; + node_index_to_outgoing_neighbor_indicator_by_cost_class_; + + std::vector all_outgoing_nodes_; + std::vector all_incoming_nodes_; }; /// Returns neighbors of all nodes for every cost class. The result is cached @@ -1653,6 +1694,7 @@ class RoutingModel { const NodeNeighborsByCostClass* GetOrCreateNodeNeighborsByCostClass( double neighbors_ratio, int64_t min_neighbors, double& neighbors_ratio_used, bool add_vehicle_starts_to_neighbors = true, + bool add_vehicle_ends_to_neighbors = false, bool only_sort_neighbors_for_partial_neighborhoods = true); /// Returns parameters.num_neighbors neighbors of all nodes for every cost /// class. The result is cached and is computed once. @@ -3760,5 +3802,5 @@ IntVarLocalSearchFilter* MakeVehicleBreaksFilter( const RoutingModel& routing_model, const RoutingDimension& dimension); #endif -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_ROUTING_H_ diff --git a/ortools/routing/samples/simple_routing_program.cc b/ortools/routing/samples/simple_routing_program.cc index a760ff77911..169a54e2869 100644 --- a/ortools/routing/samples/simple_routing_program.cc +++ b/ortools/routing/samples/simple_routing_program.cc @@ -24,7 +24,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { void SimpleRoutingProgram() { // Instantiate the data problem. @@ -88,7 +88,7 @@ void SimpleRoutingProgram() { // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::SimpleRoutingProgram(); diff --git a/ortools/routing/samples/tsp.cc b/ortools/routing/samples/tsp.cc index d42be1a8528..cd0b6213ee6 100644 --- a/ortools/routing/samples/tsp.cc +++ b/ortools/routing/samples/tsp.cc @@ -26,7 +26,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> locations{ @@ -152,7 +152,7 @@ void Tsp() { PrintSolution(manager, routing, *solution); // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::Tsp(); diff --git a/ortools/routing/samples/tsp_circuit_board.cc b/ortools/routing/samples/tsp_circuit_board.cc index c12f6445176..638e8253fc8 100644 --- a/ortools/routing/samples/tsp_circuit_board.cc +++ b/ortools/routing/samples/tsp_circuit_board.cc @@ -24,7 +24,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> locations{ @@ -178,7 +178,7 @@ void Tsp() { PrintSolution(manager, routing, *solution); // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::Tsp(); diff --git a/ortools/routing/samples/tsp_cities.cc b/ortools/routing/samples/tsp_cities.cc index b73251b1042..90f1a1ddce8 100644 --- a/ortools/routing/samples/tsp_cities.cc +++ b/ortools/routing/samples/tsp_cities.cc @@ -24,7 +24,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> distance_matrix{ @@ -125,7 +125,7 @@ void Tsp() { // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::Tsp(); diff --git a/ortools/routing/samples/tsp_cities_routes.cc b/ortools/routing/samples/tsp_cities_routes.cc index 5fab12a4d6a..005e2281de9 100644 --- a/ortools/routing/samples/tsp_cities_routes.cc +++ b/ortools/routing/samples/tsp_cities_routes.cc @@ -24,7 +24,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> distance_matrix{ @@ -128,7 +128,7 @@ void Tsp() { // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::Tsp(); diff --git a/ortools/routing/samples/tsp_distance_matrix.cc b/ortools/routing/samples/tsp_distance_matrix.cc index b0b8c0e9915..ecea62c640c 100644 --- a/ortools/routing/samples/tsp_distance_matrix.cc +++ b/ortools/routing/samples/tsp_distance_matrix.cc @@ -23,7 +23,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> distance_matrix{ @@ -146,7 +146,7 @@ void Tsp() { // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::Tsp(); diff --git a/ortools/routing/samples/vrp.cc b/ortools/routing/samples/vrp.cc index 679d68b9c8f..a762ac07f65 100644 --- a/ortools/routing/samples/vrp.cc +++ b/ortools/routing/samples/vrp.cc @@ -26,7 +26,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> distance_matrix{ @@ -161,7 +161,7 @@ void Vrp() { PrintSolution(manager, routing, *solution); // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::Vrp(); diff --git a/ortools/routing/samples/vrp_breaks.cc b/ortools/routing/samples/vrp_breaks.cc index debaf81acd4..a5eabe80f6b 100644 --- a/ortools/routing/samples/vrp_breaks.cc +++ b/ortools/routing/samples/vrp_breaks.cc @@ -31,7 +31,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> time_matrix{ @@ -197,7 +197,7 @@ void VrpBreaks() { } // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::VrpBreaks(); diff --git a/ortools/routing/samples/vrp_capacity.cc b/ortools/routing/samples/vrp_capacity.cc index 65b2499b7d5..86e512dc623 100644 --- a/ortools/routing/samples/vrp_capacity.cc +++ b/ortools/routing/samples/vrp_capacity.cc @@ -24,7 +24,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> distance_matrix{ @@ -183,7 +183,7 @@ void VrpCapacity() { PrintSolution(data, manager, routing, *solution); // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::VrpCapacity(); diff --git a/ortools/routing/samples/vrp_drop_nodes.cc b/ortools/routing/samples/vrp_drop_nodes.cc index 31484d7d71e..9d91aea6ce1 100644 --- a/ortools/routing/samples/vrp_drop_nodes.cc +++ b/ortools/routing/samples/vrp_drop_nodes.cc @@ -24,7 +24,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> distance_matrix{ @@ -199,7 +199,7 @@ void VrpDropNodes() { PrintSolution(data, manager, routing, *solution); // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::VrpDropNodes(); diff --git a/ortools/routing/samples/vrp_global_span.cc b/ortools/routing/samples/vrp_global_span.cc index 5106fbadf59..502ef1ba6d5 100644 --- a/ortools/routing/samples/vrp_global_span.cc +++ b/ortools/routing/samples/vrp_global_span.cc @@ -24,7 +24,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> distance_matrix{ @@ -162,7 +162,7 @@ void VrpGlobalSpan() { } // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::VrpGlobalSpan(); diff --git a/ortools/routing/samples/vrp_initial_routes.cc b/ortools/routing/samples/vrp_initial_routes.cc index a70e260ed6b..c15583b7cba 100644 --- a/ortools/routing/samples/vrp_initial_routes.cc +++ b/ortools/routing/samples/vrp_initial_routes.cc @@ -28,7 +28,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> distance_matrix{ @@ -190,7 +190,7 @@ void VrpInitialRoutes() { PrintSolution(data, manager, routing, *solution); // [START print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::VrpInitialRoutes(); diff --git a/ortools/routing/samples/vrp_pickup_delivery.cc b/ortools/routing/samples/vrp_pickup_delivery.cc index b2cd27c0a88..403bb43aeac 100644 --- a/ortools/routing/samples/vrp_pickup_delivery.cc +++ b/ortools/routing/samples/vrp_pickup_delivery.cc @@ -23,7 +23,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> distance_matrix{ @@ -194,7 +194,7 @@ void VrpGlobalSpan() { PrintSolution(data, manager, routing, *solution); // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::VrpGlobalSpan(); diff --git a/ortools/routing/samples/vrp_pickup_delivery_fifo.cc b/ortools/routing/samples/vrp_pickup_delivery_fifo.cc index 5508d3eb23b..22c7a509bf4 100644 --- a/ortools/routing/samples/vrp_pickup_delivery_fifo.cc +++ b/ortools/routing/samples/vrp_pickup_delivery_fifo.cc @@ -23,7 +23,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> distance_matrix{ @@ -196,7 +196,7 @@ void VrpGlobalSpan() { PrintSolution(data, manager, routing, *solution); // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::VrpGlobalSpan(); diff --git a/ortools/routing/samples/vrp_pickup_delivery_lifo.cc b/ortools/routing/samples/vrp_pickup_delivery_lifo.cc index 0c5ed679194..a97c50a6249 100644 --- a/ortools/routing/samples/vrp_pickup_delivery_lifo.cc +++ b/ortools/routing/samples/vrp_pickup_delivery_lifo.cc @@ -23,7 +23,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> distance_matrix{ @@ -196,7 +196,7 @@ void VrpGlobalSpan() { PrintSolution(data, manager, routing, *solution); // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::VrpGlobalSpan(); diff --git a/ortools/routing/samples/vrp_resources.cc b/ortools/routing/samples/vrp_resources.cc index 2cb6fb5c08b..718adf7ad09 100644 --- a/ortools/routing/samples/vrp_resources.cc +++ b/ortools/routing/samples/vrp_resources.cc @@ -25,7 +25,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> time_matrix{ @@ -218,7 +218,7 @@ void VrpTimeWindows() { PrintSolution(data, manager, routing, *solution); // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::VrpTimeWindows(); diff --git a/ortools/routing/samples/vrp_routes.cc b/ortools/routing/samples/vrp_routes.cc index c5de3197aba..a96726511a2 100644 --- a/ortools/routing/samples/vrp_routes.cc +++ b/ortools/routing/samples/vrp_routes.cc @@ -23,7 +23,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> distance_matrix{ @@ -151,7 +151,7 @@ void Vrp() { PrintSolution(routes); // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::Vrp(); diff --git a/ortools/routing/samples/vrp_solution_callback.cc b/ortools/routing/samples/vrp_solution_callback.cc index c53b5d88575..2b5b949e0e8 100644 --- a/ortools/routing/samples/vrp_solution_callback.cc +++ b/ortools/routing/samples/vrp_solution_callback.cc @@ -26,7 +26,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> distance_matrix{ @@ -213,7 +213,7 @@ void VrpSolutionCallback() { } // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::VrpSolutionCallback(); diff --git a/ortools/routing/samples/vrp_starts_ends.cc b/ortools/routing/samples/vrp_starts_ends.cc index 5d6de020712..d0e4b441774 100644 --- a/ortools/routing/samples/vrp_starts_ends.cc +++ b/ortools/routing/samples/vrp_starts_ends.cc @@ -24,7 +24,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> distance_matrix{ @@ -170,7 +170,7 @@ void VrpStartsEnds() { PrintSolution(data, manager, routing, *solution); // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::VrpStartsEnds(); diff --git a/ortools/routing/samples/vrp_time_windows.cc b/ortools/routing/samples/vrp_time_windows.cc index cef85b80d5d..ea4cb53554b 100644 --- a/ortools/routing/samples/vrp_time_windows.cc +++ b/ortools/routing/samples/vrp_time_windows.cc @@ -26,7 +26,7 @@ // [END import] // [START program_part1] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> time_matrix{ @@ -192,7 +192,7 @@ void VrpTimeWindows() { PrintSolution(data, manager, routing, *solution); // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::VrpTimeWindows(); diff --git a/ortools/routing/samples/vrp_with_time_limit.cc b/ortools/routing/samples/vrp_with_time_limit.cc index 3006c30e9ca..6f73174c5ad 100644 --- a/ortools/routing/samples/vrp_with_time_limit.cc +++ b/ortools/routing/samples/vrp_with_time_limit.cc @@ -25,7 +25,7 @@ #include "ortools/routing/routing.h" // [END import] -namespace operations_research { +namespace operations_research::routing { //! @brief Print the solution. //! @param[in] manager Index manager used. //! @param[in] routing Routing solver used. @@ -122,7 +122,7 @@ void VrpGlobalSpan() { PrintSolution(manager, routing, *solution); // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::VrpGlobalSpan(); diff --git a/ortools/routing/samples/vrptw_store_solution_data.cc b/ortools/routing/samples/vrptw_store_solution_data.cc index d6e99f3dba8..de1b311cead 100644 --- a/ortools/routing/samples/vrptw_store_solution_data.cc +++ b/ortools/routing/samples/vrptw_store_solution_data.cc @@ -26,7 +26,7 @@ // [END import] // [START program_part1] -namespace operations_research { +namespace operations_research::routing { // [START data_model] struct DataModel { const std::vector> time_matrix{ @@ -232,7 +232,7 @@ void VrpTimeWindows() { GetCumulData(*solution, routing, time_dimension)); // [END print_solution] } -} // namespace operations_research +} // namespace operations_research::routing int main(int /*argc*/, char* /*argv*/[]) { operations_research::VrpTimeWindows(); diff --git a/ortools/routing/sat.cc b/ortools/routing/sat.cc index 60ebd365e84..50de8df0e43 100644 --- a/ortools/routing/sat.cc +++ b/ortools/routing/sat.cc @@ -41,10 +41,27 @@ #include "ortools/util/saturated_arithmetic.h" #include "ortools/util/time_limit.h" -namespace operations_research { +namespace operations_research::routing { namespace sat { namespace { +using operations_research::sat::BoolArgumentProto; +using operations_research::sat::CircuitConstraintProto; +using operations_research::sat::ConstraintProto; +using operations_research::sat::CpModelProto; +using operations_research::sat::CpObjectiveProto; +using operations_research::sat::CpSolverResponse; +using operations_research::sat::CpSolverStatus; +using operations_research::sat::IntegerVariableProto; +using operations_research::sat::kMaxIntegerValue; +using operations_research::sat::kMinxIntegerValue; +using operations_research::sat::LinearConstraintProto; +using operations_research::sat::Model; +using operations_research::sat::NewSatParameters; +using operations_research::sat::PartialVariableAssignment; +using operations_research::sat::RoutesConstraintProto; +using operations_research::sat::SatParameters; + // As of 07/2019, TSPs and VRPs with homogeneous fleets of vehicles are // supported. // TODO(user): Support any type of constraints. @@ -1202,4 +1219,4 @@ bool SolveModelWithSat(RoutingModel* model, objective, *model, arc_vars, solution); } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/search.cc b/ortools/routing/search.cc index ac905d09edf..76e9298caf0 100644 --- a/ortools/routing/search.cc +++ b/ortools/routing/search.cc @@ -71,7 +71,7 @@ ABSL_FLAG(int64_t, sweep_sectors, 1, "The number of sectors the space is divided into before it is sweeped" " by the ray."); -namespace operations_research { +namespace operations_research::routing { // --- VehicleTypeCurator --- @@ -618,10 +618,12 @@ void CheapestInsertionFilteredHeuristic::AddSeedNodeToQueue( if (sq->prioritize_farthest_nodes) { start_end_value.distance = CapOpp(start_end_value.distance); } - const uint64_t num_allowed_vehicles = model()->VehicleVar(node)->Size(); + const int64_t num_allowed_vehicles = model()->VehicleVar(node)->Size(); const int64_t neg_penalty = CapOpp(model()->UnperformedPenalty(node)); - sq->priority_queue.push( - {num_allowed_vehicles, neg_penalty, start_end_value, true, node}); + sq->priority_queue.push({.key = {num_allowed_vehicles, neg_penalty}, + .start_end_value = start_end_value, + .is_node_index = true, + .index = node}); start_end_distances->pop_back(); } @@ -1630,8 +1632,8 @@ void GlobalCheapestInsertionFilteredHeuristic:: existing_insertion_positions; // Explore the neighborhood of the pickup. for (const int64_t pickup_insert_after : - node_index_to_neighbors_by_cost_class_->GetNeighborsOfNodeForCostClass( - cost_class, pickup)) { + node_index_to_neighbors_by_cost_class_ + ->GetIncomingNeighborsOfNodeForCostClass(cost_class, pickup)) { if (!Contains(pickup_insert_after)) { continue; } @@ -1661,8 +1663,8 @@ void GlobalCheapestInsertionFilteredHeuristic:: // Explore the neighborhood of the delivery. for (const int64_t delivery_insert_after : - node_index_to_neighbors_by_cost_class_->GetNeighborsOfNodeForCostClass( - cost_class, delivery)) { + node_index_to_neighbors_by_cost_class_ + ->GetIncomingNeighborsOfNodeForCostClass(cost_class, delivery)) { if (!Contains(delivery_insert_after)) { continue; } @@ -1794,8 +1796,8 @@ bool GlobalCheapestInsertionFilteredHeuristic::AddPairEntriesWithPickupAfter( model()->GetPickupAndDeliveryPairs(); DCHECK(pickup_to_entries->at(insert_after).empty()); for (const int64_t pickup : - node_index_to_neighbors_by_cost_class_->GetNeighborsOfNodeForCostClass( - cost_class, insert_after)) { + node_index_to_neighbors_by_cost_class_ + ->GetOutgoingNeighborsOfNodeForCostClass(cost_class, insert_after)) { if (StopSearchAndCleanup(priority_queue)) return false; if (Contains(pickup) || !model()->VehicleVar(pickup)->Contains(vehicle)) { continue; @@ -1843,8 +1845,8 @@ bool GlobalCheapestInsertionFilteredHeuristic::AddPairEntriesWithDeliveryAfter( const std::vector& pickup_delivery_pairs = model()->GetPickupAndDeliveryPairs(); for (const int64_t delivery : - node_index_to_neighbors_by_cost_class_->GetNeighborsOfNodeForCostClass( - cost_class, insert_after)) { + node_index_to_neighbors_by_cost_class_ + ->GetOutgoingNeighborsOfNodeForCostClass(cost_class, insert_after)) { if (StopSearchAndCleanup(priority_queue)) return false; if (Contains(delivery) || !model()->VehicleVar(delivery)->Contains(vehicle)) { @@ -2041,8 +2043,8 @@ void GlobalCheapestInsertionFilteredHeuristic:: return; } - // We're only considering the closest neighbors as insertion positions for - // the node. + // We're only considering the closest incoming neighbors as insertion + // positions for the node. const auto insert_on_vehicle_for_cost_class = [this, &vehicles, all_vehicles]( int v, int cost_class) { return (model()->GetCostClassIndexOfVehicle(v).value() == cost_class) && @@ -2051,8 +2053,8 @@ void GlobalCheapestInsertionFilteredHeuristic:: for (int cost_class = 0; cost_class < model()->GetCostClassesCount(); cost_class++) { for (const int64_t insert_after : - node_index_to_neighbors_by_cost_class_->GetNeighborsOfNodeForCostClass( - cost_class, node)) { + node_index_to_neighbors_by_cost_class_ + ->GetIncomingNeighborsOfNodeForCostClass(cost_class, node)) { if (!Contains(insert_after)) { continue; } @@ -2078,6 +2080,13 @@ bool GlobalCheapestInsertionFilteredHeuristic::UpdateAfterNodeInsertion( return false; } // Add new entries after "node" which has just been inserted. + // TODO(user): Also add node entries *before* the newly inserted node + // in the case where we have non-full neighborhoods. This will leverage the + // incoming neighbors of the newly inserted node, in case they're not also + // outgoing neighbors of 'insert_after'. + // NOTE: UpdateExistingNodeEntriesOnChain() could return the set of node + // indices that already have entries between insert_after and node, to avoid + // adding entries again for them when looking at incoming neighbors of node. if (!AddNodeEntriesAfter(nodes, vehicle, node, all_vehicles, queue)) { return false; } @@ -2105,8 +2114,8 @@ bool GlobalCheapestInsertionFilteredHeuristic::AddNodeEntriesAfter( // entries or if unperformed node insertions were present. queue->ClearInsertions(insert_after); const std::vector& neighbors = - node_index_to_neighbors_by_cost_class_->GetNeighborsOfNodeForCostClass( - cost_class, insert_after); + node_index_to_neighbors_by_cost_class_ + ->GetOutgoingNeighborsOfNodeForCostClass(cost_class, insert_after); if (neighbors.size() < nodes.NumberOfSetCallsWithDifferentArguments()) { // Iterate on the neighbors. for (int node : neighbors) { @@ -2120,7 +2129,7 @@ bool GlobalCheapestInsertionFilteredHeuristic::AddNodeEntriesAfter( for (int node : nodes.PositionsSetAtLeastOnce()) { if (StopSearch()) return false; if (!Contains(node) && - node_index_to_neighbors_by_cost_class_->IsNeighborOfNodeForCostClass( + node_index_to_neighbors_by_cost_class_->IsNeighborhoodArcForCostClass( cost_class, insert_after, node)) { AddNodeEntry(node, insert_after, vehicle, all_vehicles, queue); } @@ -2268,6 +2277,7 @@ LocalCheapestInsertionFilteredHeuristic:: RoutingModel* model, std::function stop_search, std::function evaluator, RoutingSearchParameters::PairInsertionStrategy pair_insertion_strategy, + RoutingSearchParameters::InsertionSortingMode insertion_sorting_mode, LocalSearchFilterManager* filter_manager, BinCapacities* bin_capacities, std::function&, std::vector*)> @@ -2276,6 +2286,7 @@ LocalCheapestInsertionFilteredHeuristic:: std::move(evaluator), nullptr, filter_manager), pair_insertion_strategy_(pair_insertion_strategy), + insertion_sorting_mode_(insertion_sorting_mode), bin_capacities_(bin_capacities), optimize_on_insertion_(std::move(optimize_on_insertion)) {} @@ -2385,33 +2396,51 @@ void LocalCheapestInsertionFilteredHeuristic::ComputeInsertionOrder() { insertion_order_.reserve(model.Size() + model.GetPickupAndDeliveryPairs().size()); + auto get_insertion_key = + [this](int64_t penalty, + int64_t num_allowed_vehicles) -> std::tuple { + DCHECK_NE(0, num_allowed_vehicles); + switch (insertion_sorting_mode_) { + case RoutingSearchParameters::SORT_BY_PENALTY_THEN_ALLOWED_VEHICLES: + return {CapOpp(penalty), num_allowed_vehicles}; + case RoutingSearchParameters:: + SORT_BY_PENALTY_ALLOWED_VEHICLES_RATIO_THEN_PENALTY: + return {CapOpp(penalty / num_allowed_vehicles), CapOpp(penalty)}; + default: + return {num_allowed_vehicles, CapOpp(penalty)}; + } + }; + // Iterating on pickup and delivery pairs const std::vector& pairs = model.GetPickupAndDeliveryPairs(); for (int pair_index = 0; pair_index < pairs.size(); ++pair_index) { const auto& [pickups, deliveries] = pairs[pair_index]; - uint64_t num_allowed_vehicles = std::numeric_limits::max(); + int64_t num_allowed_vehicles = std::numeric_limits::max(); int64_t pickup_penalty = 0; for (int64_t pickup : pickups) { num_allowed_vehicles = - std::min(num_allowed_vehicles, model.VehicleVar(pickup)->Size()); + std::min(num_allowed_vehicles, + static_cast(model.VehicleVar(pickup)->Size())); pickup_penalty = std::max(pickup_penalty, model.UnperformedPenalty(pickup)); } int64_t delivery_penalty = 0; for (int64_t delivery : deliveries) { num_allowed_vehicles = - std::min(num_allowed_vehicles, model.VehicleVar(delivery)->Size()); + std::min(num_allowed_vehicles, + static_cast(model.VehicleVar(delivery)->Size())); delivery_penalty = std::max(delivery_penalty, model.UnperformedPenalty(delivery)); } insertion_order_.push_back( - {num_allowed_vehicles, - CapOpp(CapAdd(pickup_penalty, delivery_penalty)), - {GetNegMaxDistanceFromVehicles(model, pair_index), 0}, - false, - pair_index}); + {.key = get_insertion_key(CapAdd(pickup_penalty, delivery_penalty), + num_allowed_vehicles), + .start_end_value = {GetNegMaxDistanceFromVehicles(model, pair_index), + 0}, + .is_node_index = false, + .index = pair_index}); } Bitset64 vehicle_set(model.vehicles()); @@ -2427,14 +2456,12 @@ void LocalCheapestInsertionFilteredHeuristic::ComputeInsertionOrder() { min_distance = std::min(min_distance, dist); }, vehicle_set); - - const uint64_t num_allowed_vehicles = model.VehicleVar(node)->Size(); - const int64_t neg_penalty = CapOpp(model.UnperformedPenalty(node)); - insertion_order_.push_back({num_allowed_vehicles, - neg_penalty, - {CapOpp(min_distance), 0}, - true, - node}); + insertion_order_.push_back( + {.key = get_insertion_key(model.UnperformedPenalty(node), + model.VehicleVar(node)->Size()), + .start_end_value = {CapOpp(min_distance), 0}, + .is_node_index = true, + .index = node}); } absl::c_sort(insertion_order_, std::greater()); @@ -5104,4 +5131,4 @@ DecisionBuilder* RoutingModel::MakeSelfDependentDimensionFinalizer( solver_->MakeLocalSearchPhase(first_solution, parameters); return finalizer; } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/search.h b/ortools/routing/search.h index 7c74c684bea..957285d44d6 100644 --- a/ortools/routing/search.h +++ b/ortools/routing/search.h @@ -45,7 +45,7 @@ #include "ortools/routing/utils.h" #include "ortools/util/bitset.h" -namespace operations_research { +namespace operations_research::routing { class IntVarFilteredHeuristic; #ifndef SWIG @@ -345,8 +345,7 @@ class CheapestInsertionFilteredHeuristic : public RoutingFilteredHeuristic { } }; struct Seed { - uint64_t num_allowed_vehicles; - int64_t neg_penalty; + std::tuple key; StartEndValue start_end_value; /// Indicates whether this Seed corresponds to a pair or a single node. /// If false, the 'index' is the pair_index, otherwise it's the node index. @@ -354,10 +353,9 @@ class CheapestInsertionFilteredHeuristic : public RoutingFilteredHeuristic { int index; bool operator>(const Seed& other) const { - return std::tie(num_allowed_vehicles, neg_penalty, start_end_value, - is_node_index, index) > - std::tie(other.num_allowed_vehicles, other.neg_penalty, - other.start_end_value, other.is_node_index, other.index); + return std::tie(key, start_end_value, is_node_index, index) > + std::tie(other.key, other.start_end_value, other.is_node_index, + other.index); } }; // clang-format off @@ -1069,6 +1067,7 @@ class LocalCheapestInsertionFilteredHeuristic RoutingModel* model, std::function stop_search, std::function evaluator, RoutingSearchParameters::PairInsertionStrategy pair_insertion_strategy, + RoutingSearchParameters::InsertionSortingMode insertion_sorting_mode, LocalSearchFilterManager* filter_manager, BinCapacities* bin_capacities = nullptr, std::function&, @@ -1131,6 +1130,7 @@ class LocalCheapestInsertionFilteredHeuristic std::vector insertion_order_; const RoutingSearchParameters::PairInsertionStrategy pair_insertion_strategy_; + const RoutingSearchParameters::InsertionSortingMode insertion_sorting_mode_; InsertionSequenceContainer insertion_container_; InsertionSequenceGenerator insertion_generator_; @@ -1454,6 +1454,6 @@ DecisionBuilder* MakeSweepDecisionBuilder(RoutingModel* model, // Returns a DecisionBuilder making all nodes unperformed. DecisionBuilder* MakeAllUnperformed(RoutingModel* model); -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_SEARCH_H_ diff --git a/ortools/routing/types.h b/ortools/routing/types.h index c95063bec1b..945802aed9d 100644 --- a/ortools/routing/types.h +++ b/ortools/routing/types.h @@ -20,7 +20,7 @@ #include "ortools/base/int_type.h" -namespace operations_research { +namespace operations_research::routing { /// Defining common types used in the routing library outside the main /// RoutingModel class has several purposes: @@ -48,6 +48,6 @@ struct PickupDeliveryPair { typedef std::function RoutingTransitCallback1; typedef std::function RoutingTransitCallback2; -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_TYPES_H_ diff --git a/ortools/routing/utils.cc b/ortools/routing/utils.cc index c2d6540cf0b..14ab16a9231 100644 --- a/ortools/routing/utils.cc +++ b/ortools/routing/utils.cc @@ -24,7 +24,7 @@ #include "absl/types/span.h" #include "ortools/util/saturated_arithmetic.h" -namespace operations_research { +namespace operations_research::routing { void BinCapacities::AddDimension( std::function load_demand_of_item_for_bin, @@ -173,4 +173,4 @@ bool FindMostExpensiveArcsOnRoute( return true; } -} // namespace operations_research +} // namespace operations_research::routing diff --git a/ortools/routing/utils.h b/ortools/routing/utils.h index a81918f3bb1..040387a635a 100644 --- a/ortools/routing/utils.h +++ b/ortools/routing/utils.h @@ -21,7 +21,7 @@ #include "absl/types/span.h" -namespace operations_research { +namespace operations_research::routing { // Tracks whether bins constrained by several nonnegative dimensions can contain // items added incrementally. Also tracks soft violation costs. @@ -89,6 +89,6 @@ bool FindMostExpensiveArcsOnRoute( std::vector>* most_expensive_arc_starts_and_ranks, std::pair* first_expensive_arc_indices); -} // namespace operations_research +} // namespace operations_research::routing #endif // OR_TOOLS_ROUTING_UTILS_H_