OR-Tools  9.6
cvrptw_soft_capacity.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 // Soft-Capacitated Vehicle Routing Problem.
15 // A description of the problem can be found here:
16 // http://en.wikipedia.org/wiki/Vehicle_routing_problem.
17 // The variant which is tackled by this model includes a capacity dimension,
18 // implemented as a soft constraint: using more than the available capacity is
19 // penalized (i.e. "costs" more) but not forbidden. For the sake of simplicity,
20 // orders are randomly located and distances are computed using the Manhattan
21 // distance. Distances are assumed to be in meters and times in seconds.
22 
23 #include <cstdint>
24 #include <random>
25 #include <vector>
26 
27 #include "absl/random/random.h"
28 #include "google/protobuf/text_format.h"
32 #include "ortools/base/logging.h"
36 #include "ortools/constraint_solver/routing_parameters.pb.h"
38 
47 using operations_research::RoutingNodeIndex;
48 using operations_research::RoutingSearchParameters;
50 
51 ABSL_FLAG(int, vrp_orders, 100, "Number of nodes in the problem.");
52 ABSL_FLAG(int, vrp_vehicles, 20, "Number of vehicles in the problem.");
53 ABSL_FLAG(int, vrp_vehicle_hard_capacity, 80,
54  "Hard capacity for a vehicle; set to 0 to disable the hard capacity "
55  "constraint");
56 ABSL_FLAG(int, vrp_vehicle_soft_capacity, 40,
57  "Soft capacity for a vehicle; set to 0 to disable the soft capacity "
58  "constraint");
59 ABSL_FLAG(int, vrp_vehicle_soft_capacity_cost, 5000,
60  "Cost of using a vehicle beyond its soft capacity (per unit "
61  "of storage over the soft capacity)");
62 ABSL_FLAG(bool, vrp_use_deterministic_random_seed, false,
63  "Use deterministic random seeds.");
64 ABSL_FLAG(bool, vrp_use_same_vehicle_costs, false,
65  "Use same vehicle costs in the routing model");
66 ABSL_FLAG(std::string, routing_search_parameters, "",
67  "Text proto RoutingSearchParameters (possibly partial) that will "
68  "override the DefaultRoutingSearchParameters()");
69 
70 const char* kTime = "Time";
71 const char* kCapacity = "Capacity";
72 const int64_t kMaxNodesPerGroup = 10;
73 const int64_t kSameVehicleCost = 1000;
74 
75 int main(int argc, char** argv) {
76  InitGoogle(argv[0], &argc, &argv, true);
77  CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders))
78  << "Specify an instance size greater than 0.";
79  CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles))
80  << "Specify a non-null vehicle fleet size.";
81  if (absl::GetFlag(FLAGS_vrp_vehicle_hard_capacity) > 0 &&
82  absl::GetFlag(FLAGS_vrp_vehicle_soft_capacity) > 0) {
83  CHECK_LT(absl::GetFlag(FLAGS_vrp_vehicle_soft_capacity),
84  absl::GetFlag(FLAGS_vrp_vehicle_hard_capacity))
85  << "The hard capacity must be higher than the soft capacity.";
86  }
87 
88  // VRP of size absl::GetFlag(FLAGS_vrp_size).
89  // Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and
90  // ends of the routes are at node 0.
91  const RoutingIndexManager::NodeIndex kDepot(0);
92  RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1,
93  absl::GetFlag(FLAGS_vrp_vehicles), kDepot);
94  RoutingModel routing(manager);
95 
96  // Setting up locations.
97  const int64_t kXMax = 100'000;
98  const int64_t kYMax = 100'000;
99  const int64_t kSpeed = 10;
100  LocationContainer locations(
101  kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
102  for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders);
103  ++location) {
104  locations.AddRandomLocation(kXMax, kYMax);
105  }
106 
107  // Setting the cost function.
108  const int vehicle_cost = routing.RegisterTransitCallback(
109  [&locations, &manager](int64_t i, int64_t j) {
110  return locations.ManhattanDistance(manager.IndexToNode(i),
111  manager.IndexToNode(j));
112  });
113  routing.SetArcCostEvaluatorOfAllVehicles(vehicle_cost);
114 
115  // Adding capacity dimension constraints with slacks.
116  const int64_t kNullCapacitySlack = 0;
117  RandomDemand demand(manager.num_nodes(), kDepot,
118  absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
119  demand.Initialize();
120  routing.AddDimension(
121  routing.RegisterTransitCallback([&demand, &manager](int64_t i,
122  int64_t j) {
123  return demand.Demand(manager.IndexToNode(i), manager.IndexToNode(j));
124  }),
125  kNullCapacitySlack, absl::GetFlag(FLAGS_vrp_vehicle_hard_capacity),
126  /*fix_start_cumul_to_zero=*/true, kCapacity);
127  RoutingDimension* capacity_dimension = routing.GetMutableDimension(kCapacity);
128 
129  // Penalise the capacity slacks to implement the soft constraint (a hard
130  // constraint has a zero slack).
131  const int num_vehicles = absl::GetFlag(FLAGS_vrp_vehicles);
132  for (int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
133  capacity_dimension->SetCumulVarSoftUpperBound(
134  routing.End(vehicle), absl::GetFlag(FLAGS_vrp_vehicle_soft_capacity),
135  absl::GetFlag(FLAGS_vrp_vehicle_soft_capacity_cost));
136  }
137 
138  // Adding time dimension constraints.
139  const int64_t kTimePerDemandUnit = 300;
140  const int64_t kHorizon = 24 * 3600;
142  kTimePerDemandUnit,
143  [&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
144  return demand.Demand(i, j);
145  },
146  [&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
147  return locations.ManhattanTime(i, j);
148  });
149  routing.AddDimension(
150  routing.RegisterTransitCallback([&time, &manager](int64_t i, int64_t j) {
151  return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
152  }),
153  kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime);
154  const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
155 
156  // Adding time windows.
157  std::mt19937 randomizer(
158  GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
159  const int64_t kTWDuration = 5 * 3600;
160  for (int order = 1; order < manager.num_nodes(); ++order) {
161  const int64_t start =
162  absl::Uniform<int32_t>(randomizer, 0, kHorizon - kTWDuration);
163  time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration);
164  }
165 
166  // Adding penalty costs to allow skipping orders.
167  const int64_t kPenalty = 10'000'000;
168  const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
169  for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
170  order < manager.num_nodes(); ++order) {
171  std::vector<int64_t> orders(1, manager.NodeToIndex(order));
172  routing.AddDisjunction(orders, kPenalty);
173  }
174 
175  // Adding same vehicle constraint costs for consecutive nodes.
176  if (absl::GetFlag(FLAGS_vrp_use_same_vehicle_costs)) {
177  std::vector<int64_t> group;
178  for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
179  order < manager.num_nodes(); ++order) {
180  group.push_back(manager.NodeToIndex(order));
181  if (group.size() == kMaxNodesPerGroup) {
183  group.clear();
184  }
185  }
186  if (!group.empty()) {
188  }
189  }
190 
191  // Solve, returns a solution if any (owned by RoutingModel).
192  RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
193  CHECK(google::protobuf::TextFormat::MergeFromString(
194  absl::GetFlag(FLAGS_routing_search_parameters), &parameters));
195  const Assignment* solution = routing.SolveWithParameters(parameters);
196  if (solution != nullptr) {
197  DisplayPlan(manager, routing, *solution,
198  absl::GetFlag(FLAGS_vrp_use_same_vehicle_costs),
200  routing.GetDimensionOrDie(kCapacity),
201  routing.GetDimensionOrDie(kTime));
202  } else {
203  LOG(INFO) << "No solution found.";
204  }
205  return EXIT_SUCCESS;
206 }
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.
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
void SetCumulVarSoftUpperBound(int64_t index, int64_t upper_bound, int64_t coefficient)
Sets a soft upper bound to the cumul variable of a given variable index.
Definition: routing.cc:7233
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
void AddSoftSameVehicleConstraint(const std::vector< int64_t > &indices, int64_t cost)
Adds a soft constraint to force a set of variable indices to be on the same vehicle.
Definition: routing.cc:2269
RoutingDimension * GetMutableDimension(const std::string &dimension_name) const
Returns a dimension from its name.
Definition: routing.cc:1690
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
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
SatParameters parameters
int main(int argc, char **argv)
const char * kCapacity
const char * kTime
ABSL_FLAG(int, vrp_orders, 100, "Number of nodes in the problem.")
const int64_t kMaxNodesPerGroup
const int64_t kSameVehicleCost
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