OR-Tools  9.6
cvrptw_with_refueling.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 // Capacitated Vehicle Routing Problem with Time Windows and refueling
15 // constraints.
16 // This is an extension to the model in cvrptw.cc so refer to that file for
17 // more information on the common part of the model. The model implemented here
18 // takes into account refueling constraints using a specific dimension: vehicles
19 // must visit certain nodes (refueling nodes) before the quantity of fuel
20 // reaches zero. Fuel consumption is proportional to the distance traveled.
21 
22 #include <cstdint>
23 #include <random>
24 #include <vector>
25 
26 #include "absl/random/random.h"
27 #include "google/protobuf/text_format.h"
31 #include "ortools/base/logging.h"
35 #include "ortools/constraint_solver/routing_parameters.pb.h"
37 
46 using operations_research::RoutingNodeIndex;
47 using operations_research::RoutingSearchParameters;
49 
50 ABSL_FLAG(int, vrp_orders, 100, "Nodes in the problem.");
51 ABSL_FLAG(int, vrp_vehicles, 20,
52  "Size of Traveling Salesman Problem instance.");
53 ABSL_FLAG(bool, vrp_use_deterministic_random_seed, false,
54  "Use deterministic random seeds.");
55 ABSL_FLAG(std::string, routing_search_parameters, "",
56  "Text proto RoutingSearchParameters (possibly partial) that will "
57  "override the DefaultRoutingSearchParameters()");
58 
59 const char* kTime = "Time";
60 const char* kCapacity = "Capacity";
61 const char* kFuel = "Fuel";
62 
63 // Returns true if node is a refueling node (based on node / refuel node ratio).
64 bool IsRefuelNode(int64_t node) {
65  const int64_t kRefuelNodeRatio = 10;
66  return (node % kRefuelNodeRatio == 0);
67 }
68 
69 int main(int argc, char** argv) {
70  InitGoogle(argv[0], &argc, &argv, true);
71  CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders))
72  << "Specify an instance size greater than 0.";
73  CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles))
74  << "Specify a non-null vehicle fleet size.";
75  // VRP of size absl::GetFlag(FLAGS_vrp_size).
76  // Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and
77  // ends of the routes are at node 0.
78  const RoutingIndexManager::NodeIndex kDepot(0);
79  RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1,
80  absl::GetFlag(FLAGS_vrp_vehicles), kDepot);
81  RoutingModel routing(manager);
82 
83  // Setting up locations.
84  const int64_t kXMax = 100000;
85  const int64_t kYMax = 100000;
86  const int64_t kSpeed = 10;
87  LocationContainer locations(
88  kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
89  for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders);
90  ++location) {
91  locations.AddRandomLocation(kXMax, kYMax);
92  }
93 
94  // Setting the cost function.
95  const int vehicle_cost = routing.RegisterTransitCallback(
96  [&locations, &manager](int64_t i, int64_t j) {
97  return locations.ManhattanDistance(manager.IndexToNode(i),
98  manager.IndexToNode(j));
99  });
100  routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
101 
102  // Adding capacity dimension constraints.
103  const int64_t kVehicleCapacity = 40;
104  const int64_t kNullCapacitySlack = 0;
105  RandomDemand demand(manager.num_nodes(), kDepot,
106  absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
107  demand.Initialize();
108  routing.AddDimension(routing.RegisterTransitCallback(
109  [&demand, &manager](int64_t i, int64_t j) {
110  return demand.Demand(manager.IndexToNode(i),
111  manager.IndexToNode(j));
112  }),
113  kNullCapacitySlack, kVehicleCapacity,
114  /*fix_start_cumul_to_zero=*/true, kCapacity);
115 
116  // Adding time dimension constraints.
117  const int64_t kTimePerDemandUnit = 300;
118  const int64_t kHorizon = 24 * 3600;
120  kTimePerDemandUnit,
121  [&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
122  return demand.Demand(i, j);
123  },
124  [&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
125  return locations.ManhattanTime(i, j);
126  });
127  routing.AddDimension(
128  routing.RegisterTransitCallback([&time, &manager](int64_t i, int64_t j) {
129  return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
130  }),
131  kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime);
132  const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
133  // Adding time windows.
134  // NOTE(user): This randomized test case is quite sensible to the seed:
135  // the generated model can be much easier or harder to solve, depending on
136  // the seed. It turns out that most seeds yield pretty slow/bad solver
137  // performance: I got good performance for about 10% of the seeds.
138  std::mt19937 randomizer(
139  144 + GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
140  const int64_t kTWDuration = 5 * 3600;
141  for (int order = 1; order < manager.num_nodes(); ++order) {
142  if (!IsRefuelNode(order)) {
143  const int64_t start =
144  absl::Uniform<int32_t>(randomizer, 0, kHorizon - kTWDuration);
145  time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration);
146  }
147  }
148 
149  // Adding fuel dimension. This dimension consumes a quantity equal to the
150  // distance traveled. Only refuel nodes can make the quantity of dimension
151  // increase by letting slack variable replenish the fuel.
152  const int64_t kFuelCapacity = kXMax + kYMax;
153  routing.AddDimension(
154  routing.RegisterTransitCallback(
155  [&locations, &manager](int64_t i, int64_t j) {
156  return locations.NegManhattanDistance(manager.IndexToNode(i),
157  manager.IndexToNode(j));
158  }),
159  kFuelCapacity, kFuelCapacity, /*fix_start_cumul_to_zero=*/false, kFuel);
160  const RoutingDimension& fuel_dimension = routing.GetDimensionOrDie(kFuel);
161  for (int order = 0; order < routing.Size(); ++order) {
162  // Only let slack free for refueling nodes.
163  if (!IsRefuelNode(order) || routing.IsStart(order)) {
164  fuel_dimension.SlackVar(order)->SetValue(0);
165  }
166  // Needed to instantiate fuel quantity at each node.
167  routing.AddVariableMinimizedByFinalizer(fuel_dimension.CumulVar(order));
168  }
169 
170  // Adding penalty costs to allow skipping orders.
171  const int64_t kPenalty = 100000;
172  const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
173  for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
174  order < routing.nodes(); ++order) {
175  std::vector<int64_t> orders(1, manager.NodeToIndex(order));
176  routing.AddDisjunction(orders, kPenalty);
177  }
178 
179  // Solve, returns a solution if any (owned by RoutingModel).
180  RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
181  CHECK(google::protobuf::TextFormat::MergeFromString(
182  absl::GetFlag(FLAGS_routing_search_parameters), &parameters));
183  const Assignment* solution = routing.SolveWithParameters(parameters);
184  if (solution != nullptr) {
185  DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false,
186  /*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0,
187  routing.GetDimensionOrDie(kCapacity),
188  routing.GetDimensionOrDie(kTime));
189  } else {
190  LOG(INFO) << "No solution found.";
191  }
192  return EXIT_SUCCESS;
193 }
An Assignment is a variable -> domains mapping, used to report solutions to the user.
virtual void SetRange(int64_t l, int64_t u)
This method sets both the min and the max of the expression.
virtual void SetValue(int64_t v)
This method sets the value of the expression.
int64_t ManhattanTime(RoutingIndexManager::NodeIndex from, RoutingIndexManager::NodeIndex to) const
Definition: cvrptw_lib.cc:72
void AddRandomLocation(int64_t x_max, int64_t y_max)
Definition: cvrptw_lib.cc:49
int64_t ManhattanDistance(RoutingIndexManager::NodeIndex from, RoutingIndexManager::NodeIndex to) const
Definition: cvrptw_lib.cc:62
Dimensions represent quantities accumulated at nodes along the routes.
Definition: routing.h:2750
IntVar * SlackVar(int64_t index) const
Definition: routing.h:2774
IntVar * CumulVar(int64_t index) const
Get the cumul, transit and slack variables for the given node (given as int64_t var index).
Definition: routing.h:2769
Manager for any NodeIndex <-> variable index conversion.
NodeIndex IndexToNode(int64_t index) const
int64_t NodeToIndex(NodeIndex node) const
int nodes() const
Sizes and indices Returns the number of nodes in the model.
Definition: routing.h:1650
bool IsStart(int64_t index) const
Returns true if 'index' represents the first node of a route.
Definition: routing.h:1454
void AddVariableMinimizedByFinalizer(IntVar *var)
Adds a variable to minimize in the solution finalizer.
Definition: routing.cc:6114
int64_t Size() const
Returns the number of next variables in the model.
Definition: routing.h:1654
DisjunctionIndex AddDisjunction(const std::vector< int64_t > &indices, int64_t penalty=kNoPenalty, int64_t max_cardinality=1)
Adds a disjunction constraint on the indices: exactly 'max_cardinality' of the indices are active.
Definition: routing.cc:2179
const Assignment * SolveWithParameters(const RoutingSearchParameters &search_parameters, std::vector< const Assignment * > *solutions=nullptr)
Solves the current routing model with the given parameters.
Definition: routing.cc:3311
int RegisterTransitCallback(TransitCallback2 callback)
Definition: routing.cc:1301
void SetArcCostEvaluatorOfAllVehicles(int evaluator_index)
Sets the cost function of the model such that the cost of a segment of a route between node 'from' an...
Definition: routing.cc:1783
bool AddDimension(int evaluator_index, int64_t slack_max, int64_t capacity, bool fix_start_cumul_to_zero, const std::string &name)
Model creation.
Definition: routing.cc:1358
const RoutingDimension & GetDimensionOrDie(const std::string &dimension_name) const
Returns a dimension from its name. Dies if the dimension does not exist.
Definition: routing.cc:1685
SatParameters parameters
const char * kFuel
int main(int argc, char **argv)
const char * kCapacity
const char * kTime
ABSL_FLAG(int, vrp_orders, 100, "Nodes in the problem.")
bool IsRefuelNode(int64_t node)
void InitGoogle(const char *usage, int *argc, char ***argv, bool deprecated)
Definition: init_google.h:34
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)
Definition: cvrptw_lib.cc:160
int32_t GetSeed(bool deterministic)
Definition: cvrptw_lib.cc:35
RoutingSearchParameters DefaultRoutingSearchParameters()
int64_t demand
Definition: resource.cc:126
int64_t time
Definition: resource.cc:1694
int64_t start