OR-Tools  9.6
cvrptw_with_stop_times_and_resources.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, fixed stop times and
15 // capacitated resources. A stop is defined as consecutive nodes at the same
16 // location.
17 // This is an extension to the model in cvrptw.cc so refer to that file for
18 // more information on the common part of the model. The model implemented here
19 // limits the number of vehicles which can simultaneously leave or enter a node
20 // to one.
21 
22 #include <cstdint>
23 #include <random>
24 #include <vector>
25 
26 #include "absl/random/random.h"
27 #include "absl/strings/str_cat.h"
28 #include "google/protobuf/text_format.h"
32 #include "ortools/base/logging.h"
36 #include "ortools/constraint_solver/routing_parameters.pb.h"
38 
49 using operations_research::RoutingNodeIndex;
50 using operations_research::RoutingSearchParameters;
53 
54 ABSL_FLAG(int, vrp_stops, 25, "Stop locations in the problem.");
55 ABSL_FLAG(int, vrp_orders_per_stop, 5, "Nodes for each stop.");
56 ABSL_FLAG(int, vrp_vehicles, 20,
57  "Size of Traveling Salesman Problem instance.");
58 ABSL_FLAG(bool, vrp_use_deterministic_random_seed, false,
59  "Use deterministic random seeds.");
60 ABSL_FLAG(std::string, routing_search_parameters, "",
61  "Text proto RoutingSearchParameters (possibly partial) that will "
62  "override the DefaultRoutingSearchParameters()");
63 
64 const char* kTime = "Time";
65 const char* kCapacity = "Capacity";
66 
67 int main(int argc, char** argv) {
68  InitGoogle(argv[0], &argc, &argv, true);
69  CHECK_LT(0, absl::GetFlag(FLAGS_vrp_stops))
70  << "Specify an instance size greater than 0.";
71  CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders_per_stop))
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  const int vrp_orders =
76  absl::GetFlag(FLAGS_vrp_stops) * absl::GetFlag(FLAGS_vrp_orders_per_stop);
77  // Nodes are indexed from 0 to vrp_orders, the starts and ends of the routes
78  // are at node 0.
79  const RoutingIndexManager::NodeIndex kDepot(0);
80  RoutingIndexManager manager(vrp_orders + 1, absl::GetFlag(FLAGS_vrp_vehicles),
81  kDepot);
82  RoutingModel routing(manager);
83 
84  // Setting up locations.
85  const int64_t kXMax = 100000;
86  const int64_t kYMax = 100000;
87  const int64_t kSpeed = 10;
88  LocationContainer locations(
89  kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
90  for (int stop = 0; stop <= absl::GetFlag(FLAGS_vrp_stops); ++stop) {
91  const int num_orders =
92  stop == 0 ? 1 : absl::GetFlag(FLAGS_vrp_orders_per_stop);
93  locations.AddRandomLocation(kXMax, kYMax, num_orders);
94  }
95 
96  // Setting the cost function.
97  const int vehicle_cost = routing.RegisterTransitCallback(
98  [&locations, &manager](int64_t i, int64_t j) {
99  return locations.ManhattanDistance(manager.IndexToNode(i),
100  manager.IndexToNode(j));
101  });
102  routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
103 
104  // Adding capacity dimension constraints.
105  const int64_t kVehicleCapacity = 40;
106  const int64_t kNullCapacitySlack = 0;
107  RandomDemand demand(manager.num_nodes(), kDepot,
108  absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
109  demand.Initialize();
110  routing.AddDimension(routing.RegisterTransitCallback(
111  [&demand, &manager](int64_t i, int64_t j) {
112  return demand.Demand(manager.IndexToNode(i),
113  manager.IndexToNode(j));
114  }),
115  kNullCapacitySlack, kVehicleCapacity,
116  /*fix_start_cumul_to_zero=*/true, kCapacity);
117 
118  // Adding time dimension constraints.
119  const int64_t kStopTime = 300;
120  const int64_t kHorizon = 24 * 3600;
122  kStopTime, locations,
123  [&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
124  return locations.ManhattanTime(i, j);
125  });
126  routing.AddDimension(
127  routing.RegisterTransitCallback([&time, &manager](int64_t i, int64_t j) {
128  return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
129  }),
130  kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime);
131  const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
132 
133  // Adding time windows, for the sake of simplicty same for each stop.
134  std::mt19937 randomizer(
135  GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
136  const int64_t kTWDuration = 5 * 3600;
137  for (int stop = 0; stop < absl::GetFlag(FLAGS_vrp_stops); ++stop) {
138  const int64_t start =
139  absl::Uniform<int32_t>(randomizer, 0, kHorizon - kTWDuration);
140  for (int stop_order = 0;
141  stop_order < absl::GetFlag(FLAGS_vrp_orders_per_stop); ++stop_order) {
142  const int order =
143  stop * absl::GetFlag(FLAGS_vrp_orders_per_stop) + stop_order + 1;
144  time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration);
145  }
146  }
147 
148  // Adding resource constraints at order locations.
149  Solver* const solver = routing.solver();
150  std::vector<IntervalVar*> intervals;
151  for (int stop = 0; stop < absl::GetFlag(FLAGS_vrp_stops); ++stop) {
152  std::vector<IntervalVar*> stop_intervals;
153  for (int stop_order = 0;
154  stop_order < absl::GetFlag(FLAGS_vrp_orders_per_stop); ++stop_order) {
155  const int order =
156  stop * absl::GetFlag(FLAGS_vrp_orders_per_stop) + stop_order + 1;
158  0, kHorizon, kStopTime, true, absl::StrCat("Order", order));
159  intervals.push_back(interval);
160  stop_intervals.push_back(interval);
161  // Link order and interval.
162  IntVar* const order_start = time_dimension.CumulVar(order);
163  solver->AddConstraint(
164  solver->MakeIsEqualCt(interval->SafeStartExpr(0), order_start,
165  interval->PerformedExpr()->Var()));
166  // Make interval performed iff corresponding order has service time.
167  // An order has no service time iff it is at the same location as the
168  // next order on the route.
169  IntVar* const is_null_duration =
170  solver
171  ->MakeElement(
172  [&locations, order](int64_t index) {
173  return locations.SameLocationFromIndex(order, index);
174  },
175  routing.NextVar(order))
176  ->Var();
177  solver->AddConstraint(
178  solver->MakeNonEquality(interval->PerformedExpr(), is_null_duration));
180  // We are minimizing route durations by minimizing route ends; so we can
181  // maximize order starts to pack them together.
182  routing.AddVariableMaximizedByFinalizer(order_start);
183  }
184  // Only one order can happen at the same time at a given location.
185  std::vector<int64_t> location_usage(stop_intervals.size(), 1);
186  solver->AddConstraint(solver->MakeCumulative(
187  stop_intervals, location_usage, 1, absl::StrCat("Client", stop)));
188  }
189  // Minimizing route duration.
190  for (int vehicle = 0; vehicle < manager.num_vehicles(); ++vehicle) {
192  time_dimension.CumulVar(routing.End(vehicle)));
193  }
194 
195  // Adding penalty costs to allow skipping orders.
196  const int64_t kPenalty = 100000;
197  const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
198  for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
199  order < routing.nodes(); ++order) {
200  std::vector<int64_t> orders(1, manager.NodeToIndex(order));
201  routing.AddDisjunction(orders, kPenalty);
202  }
203 
204  // Solve, returns a solution if any (owned by RoutingModel).
205  RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
206  CHECK(google::protobuf::TextFormat::MergeFromString(
207  absl::GetFlag(FLAGS_routing_search_parameters), &parameters));
208  const Assignment* solution = routing.SolveWithParameters(parameters);
209  if (solution != nullptr) {
210  DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false,
211  /*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0,
212  routing.GetDimensionOrDie(kCapacity),
213  routing.GetDimensionOrDie(kTime));
214  LOG(INFO) << "Stop intervals:";
215  for (IntervalVar* const interval : intervals) {
216  if (solution->PerformedValue(interval)) {
217  LOG(INFO) << interval->name() << ": " << solution->StartValue(interval);
218  }
219  }
220  } else {
221  LOG(INFO) << "No solution found.";
222  }
223  return EXIT_SUCCESS;
224 }
An Assignment is a variable -> domains mapping, used to report solutions to the user.
int64_t StartValue(const IntervalVar *const var) const
int64_t PerformedValue(const IntervalVar *const var) const
virtual IntVar * Var()=0
Creates a variable from the expression.
virtual void SetRange(int64_t l, int64_t u)
This method sets both the min and the max of the expression.
The class IntVar is a subset of IntExpr.
Interval variables are often used in scheduling.
virtual IntExpr * SafeStartExpr(int64_t unperformed_value)=0
These methods create expressions encapsulating the start, end and duration of the interval var.
virtual IntExpr * PerformedExpr()=0
int64_t ManhattanTime(RoutingIndexManager::NodeIndex from, RoutingIndexManager::NodeIndex to) const
Definition: cvrptw_lib.cc:72
int64_t SameLocationFromIndex(int64_t node1, int64_t node2) const
Definition: cvrptw_lib.cc:82
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
virtual std::string name() const
Object naming.
Dimensions represent quantities accumulated at nodes along the routes.
Definition: routing.h:2750
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
IntVar * NextVar(int64_t index) const
!defined(SWIGPYTHON)
Definition: routing.h:1485
void AddVariableMinimizedByFinalizer(IntVar *var)
Adds a variable to minimize in the solution finalizer.
Definition: routing.cc:6114
Solver * solver() const
Returns the underlying constraint solver.
Definition: routing.h:1630
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 AddVariableMaximizedByFinalizer(IntVar *var)
Adds a variable to maximize in the solution finalizer (see above for information on the solution fina...
Definition: routing.cc:6110
void AddIntervalToAssignment(IntervalVar *const interval)
Definition: routing.cc:6128
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
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
Definition: routing.h:1452
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
IntervalVar * MakeFixedDurationIntervalVar(int64_t start_min, int64_t start_max, int64_t duration, bool optional, const std::string &name)
Creates an interval var with a fixed duration.
Definition: interval.cc:2272
void AddConstraint(Constraint *const c)
Adds the constraint 'c' to the model.
Constraint * MakeIsEqualCt(IntExpr *const v1, IntExpr *v2, IntVar *const b)
b == (v1 == v2)
Definition: range_cst.cc:624
Constraint * MakeCumulative(const std::vector< IntervalVar * > &intervals, const std::vector< int64_t > &demands, int64_t capacity, const std::string &name)
This constraint forces that, for any integer t, the sum of the demands corresponding to an interval c...
Definition: resource.cc:2598
IntExpr * MakeElement(const std::vector< int64_t > &values, IntVar *const index)
values[index]
Definition: element.cc:658
Constraint * MakeNonEquality(IntExpr *const left, IntExpr *const right)
left != right
Definition: range_cst.cc:566
SatParameters parameters
int main(int argc, char **argv)
const char * kCapacity
ABSL_FLAG(int, vrp_stops, 25, "Stop locations in the problem.")
void InitGoogle(const char *usage, int *argc, char ***argv, bool deprecated)
Definition: init_google.h:34
int index
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
IntervalVar * interval
Definition: resource.cc:101
int64_t start