diff --git a/examples/cpp/BUILD.bazel b/examples/cpp/BUILD.bazel index ff2e0d1da6..fb134fdfa5 100644 --- a/examples/cpp/BUILD.bazel +++ b/examples/cpp/BUILD.bazel @@ -611,79 +611,6 @@ cc_binary( ], ) -cc_library( - name = "cvrptw_lib", - hdrs = ["cvrptw_lib.h"], - deps = [ - "//ortools/base", - "//ortools/constraint_solver:routing", - "//ortools/util:random_engine", - ], -) - -cc_binary( - name = "cvrptw", - srcs = ["cvrptw.cc"], - deps = [ - ":cvrptw_lib", - "//ortools/base", - "//ortools/constraint_solver:routing", - ], -) - -cc_binary( - name = "cvrp_disjoint_tw", - srcs = ["cvrp_disjoint_tw.cc"], - deps = [ - ":cvrptw_lib", - "//ortools/base", - "//ortools/constraint_solver:routing", - ], -) - -cc_binary( - name = "cvrptw_with_breaks", - srcs = ["cvrptw_with_breaks.cc"], - deps = [ - ":cvrptw_lib", - "//ortools/base", - "//ortools/constraint_solver:routing", - "//ortools/constraint_solver:routing_enums_cc_proto", - "@com_google_absl//absl/strings", - ], -) - -cc_binary( - name = "cvrptw_with_resources", - srcs = ["cvrptw_with_resources.cc"], - deps = [ - ":cvrptw_lib", - "//ortools/base", - "//ortools/constraint_solver:routing", - ], -) - -cc_binary( - name = "cvrptw_with_stop_times_and_resources", - srcs = ["cvrptw_with_stop_times_and_resources.cc"], - deps = [ - ":cvrptw_lib", - "//ortools/base", - "//ortools/constraint_solver:routing", - "@com_google_absl//absl/strings", - ], -) - -cc_binary( - name = "cvrptw_with_refueling", - srcs = ["cvrptw_with_refueling.cc"], - deps = [ - ":cvrptw_lib", - "//ortools/base", - "//ortools/constraint_solver:routing", - ], -) - cc_binary( name = "pdptw", srcs = ["pdptw.cc"], diff --git a/examples/cpp/cvrptw_lib.h b/examples/cpp/cvrptw_lib.h deleted file mode 100644 index edb6e3a938..0000000000 --- a/examples/cpp/cvrptw_lib.h +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2010-2024 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This header provides functions to help creating random instaces of the -// vehicle routing problem; random capacities and random time windows. -#ifndef OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_ -#define OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_ - -#include -#include -#include - -#include "absl/strings/str_format.h" -#include "ortools/base/logging.h" -#include "ortools/constraint_solver/routing.h" -#include "ortools/util/random_engine.h" - -namespace operations_research { - -typedef std::function - RoutingNodeEvaluator2; - -// Random seed generator. -int32_t GetSeed(bool deterministic); - -// Location container, contains positions of orders and can be used to obtain -// Manhattan distances/times between locations. -class LocationContainer { - public: - LocationContainer(int64_t speed, bool use_deterministic_seed); - void AddLocation(int64_t x, int64_t y) { - locations_.push_back(Location(x, y)); - } - void AddRandomLocation(int64_t x_max, int64_t y_max); - void AddRandomLocation(int64_t x_max, int64_t y_max, int duplicates); - int64_t ManhattanDistance(RoutingIndexManager::NodeIndex from, - RoutingIndexManager::NodeIndex to) const; - int64_t NegManhattanDistance(RoutingIndexManager::NodeIndex from, - RoutingIndexManager::NodeIndex to) const; - int64_t ManhattanTime(RoutingIndexManager::NodeIndex from, - RoutingIndexManager::NodeIndex to) const; - - bool SameLocation(RoutingIndexManager::NodeIndex node1, - RoutingIndexManager::NodeIndex node2) const; - int64_t SameLocationFromIndex(int64_t node1, int64_t node2) const; - - private: - class Location { - public: - Location(); - Location(int64_t x, int64_t y); - int64_t DistanceTo(const Location& location) const; - bool IsAtSameLocation(const Location& location) const; - - private: - static int64_t Abs(int64_t value); - - int64_t x_; - int64_t y_; - }; - - random_engine_t randomizer_; - const int64_t speed_; - absl::StrongVector locations_; -}; - -// Random demand. -class RandomDemand { - public: - RandomDemand(int size, RoutingIndexManager::NodeIndex depot, - bool use_deterministic_seed); - void Initialize(); - int64_t Demand(RoutingIndexManager::NodeIndex from, - RoutingIndexManager::NodeIndex to) const; - - private: - std::unique_ptr demand_; - const int size_; - const RoutingIndexManager::NodeIndex depot_; - const bool use_deterministic_seed_; -}; - -// Service time (proportional to demand) + transition time callback. -class ServiceTimePlusTransition { - public: - ServiceTimePlusTransition( - int64_t time_per_demand_unit, - operations_research::RoutingNodeEvaluator2 demand, - operations_research::RoutingNodeEvaluator2 transition_time); - int64_t Compute(RoutingIndexManager::NodeIndex from, - RoutingIndexManager::NodeIndex to) const; - - private: - const int64_t time_per_demand_unit_; - operations_research::RoutingNodeEvaluator2 demand_; - operations_research::RoutingNodeEvaluator2 transition_time_; -}; - -// Stop service time + transition time callback. -class StopServiceTimePlusTransition { - public: - StopServiceTimePlusTransition( - int64_t stop_time, const LocationContainer& location_container, - operations_research::RoutingNodeEvaluator2 transition_time); - int64_t Compute(RoutingIndexManager::NodeIndex from, - RoutingIndexManager::NodeIndex to) const; - - private: - const int64_t stop_time_; - const LocationContainer& location_container_; - operations_research::RoutingNodeEvaluator2 demand_; - operations_research::RoutingNodeEvaluator2 transition_time_; -}; - -// Route plan displayer. -// TODO(user): Move the display code to the routing library. -void DisplayPlan( - const operations_research::RoutingIndexManager& manager, - const operations_research::RoutingModel& routing, - const operations_research::Assignment& plan, bool use_same_vehicle_costs, - int64_t max_nodes_per_group, int64_t same_vehicle_cost, - const operations_research::RoutingDimension& capacity_dimension, - const operations_research::RoutingDimension& time_dimension); - -using NodeIndex = RoutingIndexManager::NodeIndex; - -int32_t GetSeed(bool deterministic) { - if (deterministic) { - return 0; - } else { - return std::random_device()(); - } -} - -LocationContainer::LocationContainer(int64_t speed, bool use_deterministic_seed) - : randomizer_(GetSeed(use_deterministic_seed)), speed_(speed) { - CHECK_LT(0, speed_); -} - -void LocationContainer::AddRandomLocation(int64_t x_max, int64_t y_max) { - AddRandomLocation(x_max, y_max, 1); -} - -void LocationContainer::AddRandomLocation(int64_t x_max, int64_t y_max, - int duplicates) { - const int64_t x = absl::Uniform(randomizer_, 0, x_max + 1); - const int64_t y = absl::Uniform(randomizer_, 0, y_max + 1); - for (int i = 0; i < duplicates; ++i) { - AddLocation(x, y); - } -} - -int64_t LocationContainer::ManhattanDistance(NodeIndex from, - NodeIndex to) const { - return locations_[from].DistanceTo(locations_[to]); -} - -int64_t LocationContainer::NegManhattanDistance(NodeIndex from, - NodeIndex to) const { - return -ManhattanDistance(from, to); -} - -int64_t LocationContainer::ManhattanTime(NodeIndex from, NodeIndex to) const { - return ManhattanDistance(from, to) / speed_; -} - -bool LocationContainer::SameLocation(NodeIndex node1, NodeIndex node2) const { - if (node1 < locations_.size() && node2 < locations_.size()) { - return locations_[node1].IsAtSameLocation(locations_[node2]); - } - return false; -} -int64_t LocationContainer::SameLocationFromIndex(int64_t node1, - int64_t node2) const { - // The direct conversion from constraint model indices to routing model - // nodes is correct because the depot is node 0. - // TODO(user): Fetch proper indices from routing model. - return SameLocation(NodeIndex(node1), NodeIndex(node2)); -} - -LocationContainer::Location::Location() : x_(0), y_(0) {} - -LocationContainer::Location::Location(int64_t x, int64_t y) : x_(x), y_(y) {} - -int64_t LocationContainer::Location::DistanceTo( - const Location& location) const { - return Abs(x_ - location.x_) + Abs(y_ - location.y_); -} - -bool LocationContainer::Location::IsAtSameLocation( - const Location& location) const { - return x_ == location.x_ && y_ == location.y_; -} - -int64_t LocationContainer::Location::Abs(int64_t value) { - return std::max(value, -value); -} - -RandomDemand::RandomDemand(int size, NodeIndex depot, - bool use_deterministic_seed) - : size_(size), - depot_(depot), - use_deterministic_seed_(use_deterministic_seed) { - CHECK_LT(0, size_); -} - -void RandomDemand::Initialize() { - const int64_t kDemandMax = 5; - const int64_t kDemandMin = 1; - demand_ = absl::make_unique(size_); - random_engine_t randomizer; - for (int order = 0; order < size_; ++order) { - if (order == depot_) { - demand_[order] = 0; - } else { - demand_[order] = kDemandMin + absl::Uniform(randomizer, 0, - kDemandMax - kDemandMin + 1); - } - } -} - -int64_t RandomDemand::Demand(NodeIndex from, NodeIndex /*to*/) const { - return demand_[from.value()]; -} - -ServiceTimePlusTransition::ServiceTimePlusTransition( - int64_t time_per_demand_unit, RoutingNodeEvaluator2 demand, - RoutingNodeEvaluator2 transition_time) - : time_per_demand_unit_(time_per_demand_unit), - demand_(std::move(demand)), - transition_time_(std::move(transition_time)) {} - -int64_t ServiceTimePlusTransition::Compute(NodeIndex from, NodeIndex to) const { - return time_per_demand_unit_ * demand_(from, to) + transition_time_(from, to); -} - -StopServiceTimePlusTransition::StopServiceTimePlusTransition( - int64_t stop_time, const LocationContainer& location_container, - RoutingNodeEvaluator2 transition_time) - : stop_time_(stop_time), - location_container_(location_container), - transition_time_(std::move(transition_time)) {} - -int64_t StopServiceTimePlusTransition::Compute(NodeIndex from, - NodeIndex to) const { - return location_container_.SameLocation(from, to) - ? 0 - : stop_time_ + transition_time_(from, to); -} - -void DisplayPlan( - const RoutingIndexManager& manager, const RoutingModel& routing, - const operations_research::Assignment& plan, bool use_same_vehicle_costs, - int64_t max_nodes_per_group, int64_t same_vehicle_cost, - const operations_research::RoutingDimension& capacity_dimension, - const operations_research::RoutingDimension& time_dimension) { - // Display plan cost. - std::string plan_output = absl::StrFormat("Cost %d\n", plan.ObjectiveValue()); - - // Display dropped orders. - std::string dropped; - for (int64_t order = 0; order < routing.Size(); ++order) { - if (routing.IsStart(order) || routing.IsEnd(order)) continue; - if (plan.Value(routing.NextVar(order)) == order) { - if (dropped.empty()) { - absl::StrAppendFormat(&dropped, " %d", - manager.IndexToNode(order).value()); - } else { - absl::StrAppendFormat(&dropped, ", %d", - manager.IndexToNode(order).value()); - } - } - } - if (!dropped.empty()) { - plan_output += "Dropped orders:" + dropped + "\n"; - } - - if (use_same_vehicle_costs) { - int group_size = 0; - int64_t group_same_vehicle_cost = 0; - std::set visited; - for (int64_t order = 0; order < routing.Size(); ++order) { - if (routing.IsStart(order) || routing.IsEnd(order)) continue; - ++group_size; - visited.insert(plan.Value(routing.VehicleVar(order))); - if (group_size == max_nodes_per_group) { - if (visited.size() > 1) { - group_same_vehicle_cost += (visited.size() - 1) * same_vehicle_cost; - } - group_size = 0; - visited.clear(); - } - } - if (visited.size() > 1) { - group_same_vehicle_cost += (visited.size() - 1) * same_vehicle_cost; - } - LOG(INFO) << "Same vehicle costs: " << group_same_vehicle_cost; - } - - // Display actual output for each vehicle. - for (int route_number = 0; route_number < routing.vehicles(); - ++route_number) { - int64_t order = routing.Start(route_number); - absl::StrAppendFormat(&plan_output, "Route %d: ", route_number); - if (routing.IsEnd(plan.Value(routing.NextVar(order)))) { - plan_output += "Empty\n"; - } else { - while (true) { - operations_research::IntVar* const load_var = - capacity_dimension.CumulVar(order); - operations_research::IntVar* const time_var = - time_dimension.CumulVar(order); - operations_research::IntVar* const slack_var = - routing.IsEnd(order) ? nullptr : time_dimension.SlackVar(order); - if (slack_var != nullptr && plan.Contains(slack_var)) { - absl::StrAppendFormat( - &plan_output, "%d Load(%d) Time(%d, %d) Slack(%d, %d)", - manager.IndexToNode(order).value(), plan.Value(load_var), - plan.Min(time_var), plan.Max(time_var), plan.Min(slack_var), - plan.Max(slack_var)); - } else { - absl::StrAppendFormat(&plan_output, "%d Load(%d) Time(%d, %d)", - manager.IndexToNode(order).value(), - plan.Value(load_var), plan.Min(time_var), - plan.Max(time_var)); - } - if (routing.IsEnd(order)) break; - plan_output += " -> "; - order = plan.Value(routing.NextVar(order)); - } - plan_output += "\n"; - } - } - LOG(INFO) << plan_output; -} -} // namespace operations_research - -#endif // OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_ diff --git a/examples/cpp/cvrptw_with_breaks.cc b/examples/cpp/cvrptw_with_breaks.cc deleted file mode 100644 index 25d790fa01..0000000000 --- a/examples/cpp/cvrptw_with_breaks.cc +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2010-2024 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// -// Capacitated Vehicle Routing Problem with Time Windows and Breaks. -// A description of the Capacitated Vehicle Routing Problem with Time Windows -// can be found here: -// http://en.wikipedia.org/wiki/Vehicle_routing_problem. -// The variant which is tackled by this model includes a capacity dimension, -// time windows and optional orders, with a penalty cost if orders are not -// performed. For the sake of simplicty, orders are randomly located and -// distances are computed using the Manhattan distance. Distances are assumed -// to be in meters and times in seconds. -// This variant also includes vehicle breaks which must happen during the day -// with two alternate breaks schemes: either a long break in the middle of the -// day or two smaller ones which can be taken during a longer period of the day. - -#include -#include - -#include "absl/random/random.h" -#include "absl/strings/str_cat.h" -#include "examples/cpp/cvrptw_lib.h" -#include "google/protobuf/text_format.h" -#include "ortools/base/commandlineflags.h" -#include "ortools/base/init_google.h" -#include "ortools/base/types.h" -#include "ortools/base/logging.h" -#include "ortools/constraint_solver/routing.h" -#include "ortools/constraint_solver/routing_enums.pb.h" -#include "ortools/constraint_solver/routing_index_manager.h" -#include "ortools/constraint_solver/routing_parameters.h" -#include "ortools/constraint_solver/routing_parameters.pb.h" - -using operations_research::Assignment; -using operations_research::DefaultRoutingSearchParameters; -using operations_research::FirstSolutionStrategy; -using operations_research::GetSeed; -using operations_research::IntervalVar; -using operations_research::LocationContainer; -using operations_research::RandomDemand; -using operations_research::RoutingDimension; -using operations_research::RoutingIndexManager; -using operations_research::RoutingModel; -using operations_research::RoutingNodeIndex; -using operations_research::RoutingSearchParameters; -using operations_research::ServiceTimePlusTransition; -using operations_research::Solver; - -ABSL_FLAG(int, vrp_orders, 100, "Nodes in the problem."); -ABSL_FLAG(int, vrp_vehicles, 20, - "Size of Traveling Salesman Problem instance."); -ABSL_FLAG(bool, vrp_use_deterministic_random_seed, false, - "Use deterministic random seeds."); -ABSL_FLAG(std::string, routing_search_parameters, "", - "Text proto RoutingSearchParameters (possibly partial) that will " - "override the DefaultRoutingSearchParameters()"); - -const char* kTime = "Time"; -const char* kCapacity = "Capacity"; - -int main(int argc, char** argv) { - InitGoogle(argv[0], &argc, &argv, true); - CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders)) - << "Specify an instance size greater than 0."; - CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles)) - << "Specify a non-null vehicle fleet size."; - // VRP of size absl::GetFlag(FLAGS_vrp_size). - // Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and - // ends of the routes are at node 0. - const RoutingIndexManager::NodeIndex kDepot(0); - RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1, - absl::GetFlag(FLAGS_vrp_vehicles), kDepot); - RoutingModel routing(manager); - RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); - CHECK(google::protobuf::TextFormat::MergeFromString( - absl::GetFlag(FLAGS_routing_search_parameters), ¶meters)); - parameters.set_first_solution_strategy( - FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION); - - // Setting up locations. - const int64_t kXMax = 100000; - const int64_t kYMax = 100000; - const int64_t kSpeed = 10; - LocationContainer locations( - kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)); - for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders); - ++location) { - locations.AddRandomLocation(kXMax, kYMax); - } - - // Setting the cost function. - const int vehicle_cost = routing.RegisterTransitCallback( - [&locations, &manager](int64_t i, int64_t j) { - return locations.ManhattanDistance(manager.IndexToNode(i), - manager.IndexToNode(j)); - }); - routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); - - // Adding capacity dimension constraints. - const int64_t kVehicleCapacity = 40; - const int64_t kNullCapacitySlack = 0; - RandomDemand demand(manager.num_nodes(), kDepot, - absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)); - demand.Initialize(); - routing.AddDimension(routing.RegisterTransitCallback( - [&demand, &manager](int64_t i, int64_t j) { - return demand.Demand(manager.IndexToNode(i), - manager.IndexToNode(j)); - }), - kNullCapacitySlack, kVehicleCapacity, - /*fix_start_cumul_to_zero=*/true, kCapacity); - - // Adding time dimension constraints. - const int64_t kTimePerDemandUnit = 300; - const int64_t kHorizon = 24 * 3600; - ServiceTimePlusTransition time( - kTimePerDemandUnit, - [&demand](RoutingNodeIndex i, RoutingNodeIndex j) { - return demand.Demand(i, j); - }, - [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { - return locations.ManhattanTime(i, j); - }); - routing.AddDimension( - routing.RegisterTransitCallback([&time, &manager](int64_t i, int64_t j) { - return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j)); - }), - kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime); - RoutingDimension* const time_dimension = routing.GetMutableDimension(kTime); - - // Adding time windows. - std::mt19937 randomizer( - GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed))); - const int64_t kTWDuration = 5 * 3600; - for (int order = 1; order < manager.num_nodes(); ++order) { - const int64_t start = - absl::Uniform(randomizer, 0, kHorizon - kTWDuration); - time_dimension->CumulVar(order)->SetRange(start, start + kTWDuration); - routing.AddToAssignment(time_dimension->SlackVar(order)); - } - - // Minimize time variables. - for (int i = 0; i < routing.Size(); ++i) { - routing.AddVariableMinimizedByFinalizer(time_dimension->CumulVar(i)); - } - for (int j = 0; j < absl::GetFlag(FLAGS_vrp_vehicles); ++j) { - routing.AddVariableMinimizedByFinalizer( - time_dimension->CumulVar(routing.Start(j))); - routing.AddVariableMinimizedByFinalizer( - time_dimension->CumulVar(routing.End(j))); - } - - // Adding vehicle breaks: - // - 40min breaks between 11:00am and 1:00pm - // or - // - 2 x 30min breaks between 10:00am and 3:00pm, at least 1h apart - // First, fill service time vector. - std::vector service_times(routing.Size()); - for (int node = 0; node < routing.Size(); node++) { - if (node >= routing.nodes()) { - service_times[node] = 0; - } else { - const RoutingIndexManager::NodeIndex index(node); - service_times[node] = kTimePerDemandUnit * demand.Demand(index, index); - } - } - const std::vector> break_data = { - {/*start_min*/ 11, /*start_max*/ 13, /*duration*/ 2400}, - {/*start_min*/ 10, /*start_max*/ 15, /*duration*/ 1800}, - {/*start_min*/ 10, /*start_max*/ 15, /*duration*/ 1800}}; - Solver* const solver = routing.solver(); - for (int vehicle = 0; vehicle < absl::GetFlag(FLAGS_vrp_vehicles); - ++vehicle) { - std::vector breaks; - for (int i = 0; i < break_data.size(); ++i) { - IntervalVar* const break_interval = solver->MakeFixedDurationIntervalVar( - break_data[i][0] * 3600, break_data[i][1] * 3600, break_data[i][2], - true, absl::StrCat("Break ", i, " on vehicle ", vehicle)); - breaks.push_back(break_interval); - } - // break1 performed iff break2 performed - solver->AddConstraint(solver->MakeEquality(breaks[1]->PerformedExpr(), - breaks[2]->PerformedExpr())); - // break2 start 1h after break1. - solver->AddConstraint(solver->MakeIntervalVarRelationWithDelay( - breaks[2], Solver::STARTS_AFTER_END, breaks[1], 3600)); - // break0 performed iff break2 unperformed - solver->AddConstraint(solver->MakeNonEquality(breaks[0]->PerformedExpr(), - breaks[2]->PerformedExpr())); - - time_dimension->SetBreakIntervalsOfVehicle(std::move(breaks), vehicle, - service_times); - } - - // Adding penalty costs to allow skipping orders. - const int64_t kPenalty = 10000000; - const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1); - for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot; - order < routing.nodes(); ++order) { - std::vector orders(1, manager.NodeToIndex(order)); - routing.AddDisjunction(orders, kPenalty); - } - - // Solve, returns a solution if any (owned by RoutingModel). - const Assignment* solution = routing.SolveWithParameters(parameters); - if (solution != nullptr) { - LOG(INFO) << "Breaks: "; - for (const auto& break_interval : - solution->IntervalVarContainer().elements()) { - if (break_interval.PerformedValue() == 1) { - LOG(INFO) << break_interval.Var()->name() << " " - << break_interval.DebugString(); - } else { - LOG(INFO) << break_interval.Var()->name() << " unperformed"; - } - } - DisplayPlan(manager, routing, *solution, false, 0, 0, - routing.GetDimensionOrDie(kCapacity), - routing.GetDimensionOrDie(kTime)); - } else { - LOG(INFO) << "No solution found."; - } - return EXIT_SUCCESS; -} diff --git a/examples/cpp/cvrptw_with_refueling.cc b/examples/cpp/cvrptw_with_refueling.cc deleted file mode 100644 index 5464467771..0000000000 --- a/examples/cpp/cvrptw_with_refueling.cc +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2010-2024 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Capacitated Vehicle Routing Problem with Time Windows and refueling -// constraints. -// This is an extension to the model in cvrptw.cc so refer to that file for -// more information on the common part of the model. The model implemented here -// takes into account refueling constraints using a specific dimension: vehicles -// must visit certain nodes (refueling nodes) before the quantity of fuel -// reaches zero. Fuel consumption is proportional to the distance traveled. - -#include -#include - -#include "absl/random/random.h" -#include "examples/cpp/cvrptw_lib.h" -#include "google/protobuf/text_format.h" -#include "ortools/base/commandlineflags.h" -#include "ortools/base/init_google.h" -#include "ortools/base/types.h" -#include "ortools/base/logging.h" -#include "ortools/constraint_solver/routing.h" -#include "ortools/constraint_solver/routing_index_manager.h" -#include "ortools/constraint_solver/routing_parameters.h" -#include "ortools/constraint_solver/routing_parameters.pb.h" - -using operations_research::Assignment; -using operations_research::DefaultRoutingSearchParameters; -using operations_research::GetSeed; -using operations_research::LocationContainer; -using operations_research::RandomDemand; -using operations_research::RoutingDimension; -using operations_research::RoutingIndexManager; -using operations_research::RoutingModel; -using operations_research::RoutingNodeIndex; -using operations_research::RoutingSearchParameters; -using operations_research::ServiceTimePlusTransition; - -ABSL_FLAG(int, vrp_orders, 100, "Nodes in the problem."); -ABSL_FLAG(int, vrp_vehicles, 20, - "Size of Traveling Salesman Problem instance."); -ABSL_FLAG(bool, vrp_use_deterministic_random_seed, false, - "Use deterministic random seeds."); -ABSL_FLAG(std::string, routing_search_parameters, "", - "Text proto RoutingSearchParameters (possibly partial) that will " - "override the DefaultRoutingSearchParameters()"); - -const char* kTime = "Time"; -const char* kCapacity = "Capacity"; -const char* kFuel = "Fuel"; - -// Returns true if node is a refueling node (based on node / refuel node ratio). -bool IsRefuelNode(int64_t node) { - const int64_t kRefuelNodeRatio = 10; - return (node % kRefuelNodeRatio == 0); -} - -int main(int argc, char** argv) { - InitGoogle(argv[0], &argc, &argv, true); - CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders)) - << "Specify an instance size greater than 0."; - CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles)) - << "Specify a non-null vehicle fleet size."; - // VRP of size absl::GetFlag(FLAGS_vrp_size). - // Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and - // ends of the routes are at node 0. - const RoutingIndexManager::NodeIndex kDepot(0); - RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1, - absl::GetFlag(FLAGS_vrp_vehicles), kDepot); - RoutingModel routing(manager); - - // Setting up locations. - const int64_t kXMax = 100000; - const int64_t kYMax = 100000; - const int64_t kSpeed = 10; - LocationContainer locations( - kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)); - for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders); - ++location) { - locations.AddRandomLocation(kXMax, kYMax); - } - - // Setting the cost function. - const int vehicle_cost = routing.RegisterTransitCallback( - [&locations, &manager](int64_t i, int64_t j) { - return locations.ManhattanDistance(manager.IndexToNode(i), - manager.IndexToNode(j)); - }); - routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); - - // Adding capacity dimension constraints. - const int64_t kVehicleCapacity = 40; - const int64_t kNullCapacitySlack = 0; - RandomDemand demand(manager.num_nodes(), kDepot, - absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)); - demand.Initialize(); - routing.AddDimension(routing.RegisterTransitCallback( - [&demand, &manager](int64_t i, int64_t j) { - return demand.Demand(manager.IndexToNode(i), - manager.IndexToNode(j)); - }), - kNullCapacitySlack, kVehicleCapacity, - /*fix_start_cumul_to_zero=*/true, kCapacity); - - // Adding time dimension constraints. - const int64_t kTimePerDemandUnit = 300; - const int64_t kHorizon = 24 * 3600; - ServiceTimePlusTransition time( - kTimePerDemandUnit, - [&demand](RoutingNodeIndex i, RoutingNodeIndex j) { - return demand.Demand(i, j); - }, - [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { - return locations.ManhattanTime(i, j); - }); - routing.AddDimension( - routing.RegisterTransitCallback([&time, &manager](int64_t i, int64_t j) { - return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j)); - }), - kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime); - const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime); - // Adding time windows. - // NOTE(user): This randomized test case is quite sensible to the seed: - // the generated model can be much easier or harder to solve, depending on - // the seed. It turns out that most seeds yield pretty slow/bad solver - // performance: I got good performance for about 10% of the seeds. - std::mt19937 randomizer( - 144 + GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed))); - const int64_t kTWDuration = 5 * 3600; - for (int order = 1; order < manager.num_nodes(); ++order) { - if (!IsRefuelNode(order)) { - const int64_t start = - absl::Uniform(randomizer, 0, kHorizon - kTWDuration); - time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration); - } - } - - // Adding fuel dimension. This dimension consumes a quantity equal to the - // distance traveled. Only refuel nodes can make the quantity of dimension - // increase by letting slack variable replenish the fuel. - const int64_t kFuelCapacity = kXMax + kYMax; - routing.AddDimension( - routing.RegisterTransitCallback( - [&locations, &manager](int64_t i, int64_t j) { - return locations.NegManhattanDistance(manager.IndexToNode(i), - manager.IndexToNode(j)); - }), - kFuelCapacity, kFuelCapacity, /*fix_start_cumul_to_zero=*/false, kFuel); - const RoutingDimension& fuel_dimension = routing.GetDimensionOrDie(kFuel); - for (int order = 0; order < routing.Size(); ++order) { - // Only let slack free for refueling nodes. - if (!IsRefuelNode(order) || routing.IsStart(order)) { - fuel_dimension.SlackVar(order)->SetValue(0); - } - // Needed to instantiate fuel quantity at each node. - routing.AddVariableMinimizedByFinalizer(fuel_dimension.CumulVar(order)); - } - - // Adding penalty costs to allow skipping orders. - const int64_t kPenalty = 100000; - const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1); - for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot; - order < routing.nodes(); ++order) { - std::vector orders(1, manager.NodeToIndex(order)); - routing.AddDisjunction(orders, kPenalty); - } - - // Solve, returns a solution if any (owned by RoutingModel). - RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); - CHECK(google::protobuf::TextFormat::MergeFromString( - absl::GetFlag(FLAGS_routing_search_parameters), ¶meters)); - const Assignment* solution = routing.SolveWithParameters(parameters); - if (solution != nullptr) { - DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false, - /*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0, - routing.GetDimensionOrDie(kCapacity), - routing.GetDimensionOrDie(kTime)); - } else { - LOG(INFO) << "No solution found."; - } - return EXIT_SUCCESS; -} diff --git a/examples/cpp/cvrptw_with_resources.cc b/examples/cpp/cvrptw_with_resources.cc deleted file mode 100644 index 5171c781a1..0000000000 --- a/examples/cpp/cvrptw_with_resources.cc +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2010-2024 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Capacitated Vehicle Routing Problem with Time Windows and capacitated -// resources. -// This is an extension to the model in cvrptw.cc so refer to that file for -// more information on the common part of the model. The model implemented here -// limits the number of vehicles which can simultaneously leave or enter the -// depot due to limited resources (or capacity) available. -// TODO(user): The current model consumes resources even for vehicles with -// empty routes; fix this when we have an API on the cumulative constraints -// with variable demands. - -#include -#include - -#include "absl/random/random.h" -#include "examples/cpp/cvrptw_lib.h" -#include "google/protobuf/text_format.h" -#include "ortools/base/commandlineflags.h" -#include "ortools/base/init_google.h" -#include "ortools/base/types.h" -#include "ortools/base/logging.h" -#include "ortools/constraint_solver/routing.h" -#include "ortools/constraint_solver/routing_index_manager.h" -#include "ortools/constraint_solver/routing_parameters.h" -#include "ortools/constraint_solver/routing_parameters.pb.h" - -using operations_research::Assignment; -using operations_research::DefaultRoutingSearchParameters; -using operations_research::GetSeed; -using operations_research::IntervalVar; -using operations_research::IntVar; -using operations_research::LocationContainer; -using operations_research::RandomDemand; -using operations_research::RoutingDimension; -using operations_research::RoutingIndexManager; -using operations_research::RoutingModel; -using operations_research::RoutingNodeIndex; -using operations_research::RoutingSearchParameters; -using operations_research::ServiceTimePlusTransition; -using operations_research::Solver; - -ABSL_FLAG(int, vrp_orders, 100, "Nodes in the problem."); -ABSL_FLAG(int, vrp_vehicles, 20, - "Size of Traveling Salesman Problem instance."); -ABSL_FLAG(bool, vrp_use_deterministic_random_seed, false, - "Use deterministic random seeds."); -ABSL_FLAG(std::string, routing_search_parameters, "", - "Text proto RoutingSearchParameters (possibly partial) that will " - "override the DefaultRoutingSearchParameters()"); - -const char* kTime = "Time"; -const char* kCapacity = "Capacity"; - -int main(int argc, char** argv) { - InitGoogle(argv[0], &argc, &argv, true); - CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders)) - << "Specify an instance size greater than 0."; - CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles)) - << "Specify a non-null vehicle fleet size."; - // VRP of size absl::GetFlag(FLAGS_vrp_size). - // Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and - // ends of the routes are at node 0. - const RoutingIndexManager::NodeIndex kDepot(0); - RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1, - absl::GetFlag(FLAGS_vrp_vehicles), kDepot); - RoutingModel routing(manager); - - // Setting up locations. - const int64_t kXMax = 100000; - const int64_t kYMax = 100000; - const int64_t kSpeed = 10; - LocationContainer locations( - kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)); - for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders); - ++location) { - locations.AddRandomLocation(kXMax, kYMax); - } - - // Setting the cost function. - const int vehicle_cost = routing.RegisterTransitCallback( - [&locations, &manager](int64_t i, int64_t j) { - return locations.ManhattanDistance(manager.IndexToNode(i), - manager.IndexToNode(j)); - }); - routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); - - // Adding capacity dimension constraints. - const int64_t kVehicleCapacity = 40; - const int64_t kNullCapacitySlack = 0; - RandomDemand demand(manager.num_nodes(), kDepot, - absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)); - demand.Initialize(); - routing.AddDimension(routing.RegisterTransitCallback( - [&demand, &manager](int64_t i, int64_t j) { - return demand.Demand(manager.IndexToNode(i), - manager.IndexToNode(j)); - }), - kNullCapacitySlack, kVehicleCapacity, - /*fix_start_cumul_to_zero=*/true, kCapacity); - - // Adding time dimension constraints. - const int64_t kTimePerDemandUnit = 300; - const int64_t kHorizon = 24 * 3600; - ServiceTimePlusTransition time( - kTimePerDemandUnit, - [&demand](RoutingNodeIndex i, RoutingNodeIndex j) { - return demand.Demand(i, j); - }, - [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { - return locations.ManhattanTime(i, j); - }); - routing.AddDimension( - routing.RegisterTransitCallback([&time, &manager](int64_t i, int64_t j) { - return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j)); - }), - kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime); - const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime); - - // Adding time windows. - std::mt19937 randomizer( - GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed))); - const int64_t kTWDuration = 5 * 3600; - for (int order = 1; order < manager.num_nodes(); ++order) { - const int64_t start = - absl::Uniform(randomizer, 0, kHorizon - kTWDuration); - time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration); - } - - // Adding resource constraints at the depot (start and end location of - // routes). - std::vector start_end_times; - for (int i = 0; i < absl::GetFlag(FLAGS_vrp_vehicles); ++i) { - start_end_times.push_back(time_dimension.CumulVar(routing.End(i))); - start_end_times.push_back(time_dimension.CumulVar(routing.Start(i))); - } - // Build corresponding time intervals. - const int64_t kVehicleSetup = 180; - Solver* const solver = routing.solver(); - std::vector intervals; - solver->MakeFixedDurationIntervalVarArray(start_end_times, kVehicleSetup, - "depot_interval", &intervals); - // Constrain the number of maximum simultaneous intervals at depot. - const int64_t kDepotCapacity = 5; - std::vector depot_usage(start_end_times.size(), 1); - solver->AddConstraint( - solver->MakeCumulative(intervals, depot_usage, kDepotCapacity, "depot")); - // Instantiate route start and end times to produce feasible times. - for (int i = 0; i < start_end_times.size(); ++i) { - routing.AddVariableMinimizedByFinalizer(start_end_times[i]); - } - - // Adding penalty costs to allow skipping orders. - const int64_t kPenalty = 100000; - const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1); - for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot; - order < manager.num_nodes(); ++order) { - std::vector orders(1, manager.NodeToIndex(order)); - routing.AddDisjunction(orders, kPenalty); - } - - // Solve, returns a solution if any (owned by RoutingModel). - RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); - CHECK(google::protobuf::TextFormat::MergeFromString( - absl::GetFlag(FLAGS_routing_search_parameters), ¶meters)); - const Assignment* solution = routing.SolveWithParameters(parameters); - if (solution != nullptr) { - DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false, - /*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0, - routing.GetDimensionOrDie(kCapacity), - routing.GetDimensionOrDie(kTime)); - } else { - LOG(INFO) << "No solution found."; - } - return EXIT_SUCCESS; -} diff --git a/examples/cpp/cvrptw_with_stop_times_and_resources.cc b/examples/cpp/cvrptw_with_stop_times_and_resources.cc deleted file mode 100644 index d1c494638b..0000000000 --- a/examples/cpp/cvrptw_with_stop_times_and_resources.cc +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2010-2024 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Capacitated Vehicle Routing Problem with Time Windows, fixed stop times and -// capacitated resources. A stop is defined as consecutive nodes at the same -// location. -// This is an extension to the model in cvrptw.cc so refer to that file for -// more information on the common part of the model. The model implemented here -// limits the number of vehicles which can simultaneously leave or enter a node -// to one. - -#include -#include - -#include "absl/random/random.h" -#include "absl/strings/str_cat.h" -#include "examples/cpp/cvrptw_lib.h" -#include "google/protobuf/text_format.h" -#include "ortools/base/commandlineflags.h" -#include "ortools/base/init_google.h" -#include "ortools/base/types.h" -#include "ortools/base/logging.h" -#include "ortools/constraint_solver/routing.h" -#include "ortools/constraint_solver/routing_index_manager.h" -#include "ortools/constraint_solver/routing_parameters.h" -#include "ortools/constraint_solver/routing_parameters.pb.h" - -using operations_research::Assignment; -using operations_research::DefaultRoutingSearchParameters; -using operations_research::GetSeed; -using operations_research::IntervalVar; -using operations_research::IntVar; -using operations_research::LocationContainer; -using operations_research::RandomDemand; -using operations_research::RoutingDimension; -using operations_research::RoutingIndexManager; -using operations_research::RoutingModel; -using operations_research::RoutingNodeIndex; -using operations_research::RoutingSearchParameters; -using operations_research::Solver; -using operations_research::StopServiceTimePlusTransition; - -ABSL_FLAG(int, vrp_stops, 25, "Stop locations in the problem."); -ABSL_FLAG(int, vrp_orders_per_stop, 5, "Nodes for each stop."); -ABSL_FLAG(int, vrp_vehicles, 20, - "Size of Traveling Salesman Problem instance."); -ABSL_FLAG(bool, vrp_use_deterministic_random_seed, false, - "Use deterministic random seeds."); -ABSL_FLAG(std::string, routing_search_parameters, "", - "Text proto RoutingSearchParameters (possibly partial) that will " - "override the DefaultRoutingSearchParameters()"); - -const char* kTime = "Time"; -const char* kCapacity = "Capacity"; - -int main(int argc, char** argv) { - InitGoogle(argv[0], &argc, &argv, true); - CHECK_LT(0, absl::GetFlag(FLAGS_vrp_stops)) - << "Specify an instance size greater than 0."; - CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders_per_stop)) - << "Specify an instance size greater than 0."; - CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles)) - << "Specify a non-null vehicle fleet size."; - const int vrp_orders = - absl::GetFlag(FLAGS_vrp_stops) * absl::GetFlag(FLAGS_vrp_orders_per_stop); - // Nodes are indexed from 0 to vrp_orders, the starts and ends of the routes - // are at node 0. - const RoutingIndexManager::NodeIndex kDepot(0); - RoutingIndexManager manager(vrp_orders + 1, absl::GetFlag(FLAGS_vrp_vehicles), - kDepot); - RoutingModel routing(manager); - - // Setting up locations. - const int64_t kXMax = 100000; - const int64_t kYMax = 100000; - const int64_t kSpeed = 10; - LocationContainer locations( - kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)); - for (int stop = 0; stop <= absl::GetFlag(FLAGS_vrp_stops); ++stop) { - const int num_orders = - stop == 0 ? 1 : absl::GetFlag(FLAGS_vrp_orders_per_stop); - locations.AddRandomLocation(kXMax, kYMax, num_orders); - } - - // Setting the cost function. - const int vehicle_cost = routing.RegisterTransitCallback( - [&locations, &manager](int64_t i, int64_t j) { - return locations.ManhattanDistance(manager.IndexToNode(i), - manager.IndexToNode(j)); - }); - routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost); - - // Adding capacity dimension constraints. - const int64_t kVehicleCapacity = 40; - const int64_t kNullCapacitySlack = 0; - RandomDemand demand(manager.num_nodes(), kDepot, - absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)); - demand.Initialize(); - routing.AddDimension(routing.RegisterTransitCallback( - [&demand, &manager](int64_t i, int64_t j) { - return demand.Demand(manager.IndexToNode(i), - manager.IndexToNode(j)); - }), - kNullCapacitySlack, kVehicleCapacity, - /*fix_start_cumul_to_zero=*/true, kCapacity); - - // Adding time dimension constraints. - const int64_t kStopTime = 300; - const int64_t kHorizon = 24 * 3600; - StopServiceTimePlusTransition time( - kStopTime, locations, - [&locations](RoutingNodeIndex i, RoutingNodeIndex j) { - return locations.ManhattanTime(i, j); - }); - routing.AddDimension( - routing.RegisterTransitCallback([&time, &manager](int64_t i, int64_t j) { - return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j)); - }), - kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime); - const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime); - - // Adding time windows, for the sake of simplicty same for each stop. - std::mt19937 randomizer( - GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed))); - const int64_t kTWDuration = 5 * 3600; - for (int stop = 0; stop < absl::GetFlag(FLAGS_vrp_stops); ++stop) { - const int64_t start = - absl::Uniform(randomizer, 0, kHorizon - kTWDuration); - for (int stop_order = 0; - stop_order < absl::GetFlag(FLAGS_vrp_orders_per_stop); ++stop_order) { - const int order = - stop * absl::GetFlag(FLAGS_vrp_orders_per_stop) + stop_order + 1; - time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration); - } - } - - // Adding resource constraints at order locations. - Solver* const solver = routing.solver(); - std::vector intervals; - for (int stop = 0; stop < absl::GetFlag(FLAGS_vrp_stops); ++stop) { - std::vector stop_intervals; - for (int stop_order = 0; - stop_order < absl::GetFlag(FLAGS_vrp_orders_per_stop); ++stop_order) { - const int order = - stop * absl::GetFlag(FLAGS_vrp_orders_per_stop) + stop_order + 1; - IntervalVar* const interval = solver->MakeFixedDurationIntervalVar( - 0, kHorizon, kStopTime, true, absl::StrCat("Order", order)); - intervals.push_back(interval); - stop_intervals.push_back(interval); - // Link order and interval. - IntVar* const order_start = time_dimension.CumulVar(order); - solver->AddConstraint( - solver->MakeIsEqualCt(interval->SafeStartExpr(0), order_start, - interval->PerformedExpr()->Var())); - // Make interval performed iff corresponding order has service time. - // An order has no service time iff it is at the same location as the - // next order on the route. - IntVar* const is_null_duration = - solver - ->MakeElement( - [&locations, order](int64_t index) { - return locations.SameLocationFromIndex(order, index); - }, - routing.NextVar(order)) - ->Var(); - solver->AddConstraint( - solver->MakeNonEquality(interval->PerformedExpr(), is_null_duration)); - routing.AddIntervalToAssignment(interval); - // We are minimizing route durations by minimizing route ends; so we can - // maximize order starts to pack them together. - routing.AddVariableMaximizedByFinalizer(order_start); - } - // Only one order can happen at the same time at a given location. - std::vector location_usage(stop_intervals.size(), 1); - solver->AddConstraint(solver->MakeCumulative( - stop_intervals, location_usage, 1, absl::StrCat("Client", stop))); - } - // Minimizing route duration. - for (int vehicle = 0; vehicle < manager.num_vehicles(); ++vehicle) { - routing.AddVariableMinimizedByFinalizer( - time_dimension.CumulVar(routing.End(vehicle))); - } - - // Adding penalty costs to allow skipping orders. - const int64_t kPenalty = 100000; - const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1); - for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot; - order < routing.nodes(); ++order) { - std::vector orders(1, manager.NodeToIndex(order)); - routing.AddDisjunction(orders, kPenalty); - } - - // Solve, returns a solution if any (owned by RoutingModel). - RoutingSearchParameters parameters = DefaultRoutingSearchParameters(); - CHECK(google::protobuf::TextFormat::MergeFromString( - absl::GetFlag(FLAGS_routing_search_parameters), ¶meters)); - const Assignment* solution = routing.SolveWithParameters(parameters); - if (solution != nullptr) { - DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false, - /*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0, - routing.GetDimensionOrDie(kCapacity), - routing.GetDimensionOrDie(kTime)); - LOG(INFO) << "Stop intervals:"; - for (IntervalVar* const interval : intervals) { - if (solution->PerformedValue(interval)) { - LOG(INFO) << interval->name() << ": " << solution->StartValue(interval); - } - } - } else { - LOG(INFO) << "No solution found."; - } - return EXIT_SUCCESS; -}