OR-Tools  9.6
cvrptw_with_time_dependent_costs.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 // This example is very similar to cvrptw.cc, but distances are time dependent.
15 // The function RandomStepFunction is used to add random noise to each transit.
16 
17 #include <cmath>
18 #include <cstdint>
19 #include <functional>
20 #include <memory>
21 #include <random>
22 #include <set>
23 #include <vector>
24 
25 #include "absl/functional/bind_front.h"
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"
38 #include "ortools/util/step_function.h"
39 
48 using operations_research::RoutingNodeIndex;
49 using operations_research::RoutingSearchParameters;
51 using operations_research::StepFunction;
52 
53 ABSL_FLAG(int, vrp_orders, 25, "Nodes in the problem.");
54 ABSL_FLAG(int, vrp_vehicles, 10,
55  "Size of Traveling Salesman Problem instance.");
56 ABSL_FLAG(bool, vrp_use_deterministic_random_seed, false,
57  "Use deterministic random seeds.");
58 ABSL_FLAG(std::string, routing_search_parameters, "",
59  "Text proto RoutingSearchParameters (possibly partial) that will "
60  "override the DefaultRoutingSearchParameters()");
61 
62 static const char kTime[] = "Time";
63 static const char kCapacity[] = "Capacity";
64 static const char kTimeDepedentCost[] = "TimeDependentCost";
65 
66 // This class implements the Pólya urn stochastic process, for more information:
67 // https://en.wikipedia.org/wiki/P%C3%B3lya_urn_model
68 // Basically, the polya urn is a martingale that converges almost surely to a
69 // uniform random variable over [0, 1]. It is questionable if it's realistic to
70 // model traffic deviations with this process, but traffic is hard to model in
71 // general.
72 class PolyaUrn {
73  public:
74  PolyaUrn(int red_balls, int blue_balls, int seed)
75  : red_balls_(red_balls),
76  all_balls_(red_balls + blue_balls),
77  generator_(seed) {
78  CHECK_LT(0, red_balls_);
79  CHECK_LT(red_balls_, all_balls_);
80  }
81  // Every call to Next moves the process one step forward and returns the
82  // current value.
83  double Next() {
84  CHECK_LT(0, red_balls_);
85  CHECK_LT(red_balls_, all_balls_);
86 
87  const double return_value = static_cast<double>(red_balls_) / all_balls_;
88  red_balls_ += (absl::Uniform(generator_, 0, all_balls_) < red_balls_);
89  all_balls_ += 1;
90 
91  CHECK_LT(0, return_value);
92  CHECK_LT(return_value, 1);
93  return return_value - 0.5;
94  }
95 
96  private:
97  int red_balls_;
98  int all_balls_;
99  std::mt19937 generator_;
100 };
101 
102 // Creates a random histogram over the interval [0, interval_end) using the urn.
103 StepFunction RandomStepFunction(int64_t mean, int64_t step_size,
104  int64_t interval_end, int seed) {
105  PolyaUrn random_generator(1, 1, seed);
106  StepFunction result;
107  for (int64_t step = 0; step < interval_end; step += step_size) {
108  result.AddStepToEnd(step, 2 * mean * random_generator.Next() - mean);
109  }
110  result.AddStepToEnd(interval_end, 0);
111  return result;
112 }
113 
115  public:
117  int64_t max_time)
118  : distance_evaluator_(distance_evaluator), max_time_(max_time) {}
119 
120  RoutingModel::StateDependentTransit Run(const RoutingIndexManager& manager,
121  int64_t from_index,
122  int64_t to_index) {
123  const RoutingIndexManager::NodeIndex from = manager.IndexToNode(from_index);
124  const RoutingIndexManager::NodeIndex to = manager.IndexToNode(to_index);
125  static const int magic_number = 0xfe3498aa;
126  const int64_t seed =
127  (from.value() ^ magic_number) * (to.value() ^ (~magic_number));
128  const int64_t distance = distance_evaluator_.ManhattanDistance(from, to);
129  const int64_t mean_deviation = sqrt(distance);
130  const StepFunction deviation =
131  RandomStepFunction(mean_deviation, sqrt(max_time_), max_time_, seed);
132  const std::function<int64_t(int64_t)> travel_time =
133  [distance, &deviation](int64_t time) -> int64_t {
134  return distance + deviation.GetValue(time);
135  };
136  return RoutingModel::MakeStateDependentTransit(travel_time, 0, max_time_);
137  // Now the local variables deviation and travel_time are going to be
138  // destroyed, but MakeStateDependentTransit does not store either and it
139  // uses its own caches.
140  }
141 
142  private:
143  const LocationContainer& distance_evaluator_;
144  const int64_t max_time_;
145 };
146 
147 int main(int argc, char** argv) {
148  InitGoogle(argv[0], &argc, &argv, true);
149  CHECK_LT(0, absl::GetFlag(FLAGS_vrp_orders))
150  << "Specify an instance size greater than 0.";
151  CHECK_LT(0, absl::GetFlag(FLAGS_vrp_vehicles))
152  << "Specify a non-null vehicle fleet size.";
153  // VRP of size absl::GetFlag(FLAGS_vrp_size).
154  // Nodes are indexed from 0 to absl::GetFlag(FLAGS_vrp_orders), the starts and
155  // ends of the routes are at node 0.
156  static const RoutingIndexManager::NodeIndex kDepot(0);
157  static const RoutingIndexManager::NodeIndex kFirstNodeAfterDepot(1);
158  RoutingIndexManager manager(absl::GetFlag(FLAGS_vrp_orders) + 1,
159  absl::GetFlag(FLAGS_vrp_vehicles), kDepot);
160  RoutingModel routing(manager);
161 
162  // Setting up locations.
163  const int64_t kXMax = 1000;
164  const int64_t kYMax = 1000;
165  const int64_t kSpeed = 10;
166  LocationContainer locations(
167  kSpeed, absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
168  for (int location = 0; location <= absl::GetFlag(FLAGS_vrp_orders);
169  ++location) {
170  locations.AddRandomLocation(kXMax, kYMax);
171  }
172 
173  // Adding capacity dimension constraints.
174  const int64_t kVehicleCapacity = 40;
175  const int64_t kNullCapacitySlack = 0;
176  RandomDemand demand(manager.num_nodes(), kDepot,
177  absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed));
178  demand.Initialize();
179  routing.AddDimension(routing.RegisterTransitCallback(
180  [&demand, &manager](int64_t i, int64_t j) {
181  return demand.Demand(manager.IndexToNode(i),
182  manager.IndexToNode(j));
183  }),
184  kNullCapacitySlack, kVehicleCapacity,
185  /*fix_start_cumul_to_zero=*/true, kCapacity);
186 
187  // Adding time dimension constraints.
188  const int64_t kTimePerDemandUnit = 3;
189  const int64_t kHorizon = 24 * 36;
191  kTimePerDemandUnit,
192  [&demand](RoutingNodeIndex i, RoutingNodeIndex j) {
193  return demand.Demand(i, j);
194  },
195  [&locations](RoutingNodeIndex i, RoutingNodeIndex j) {
196  return locations.ManhattanTime(i, j);
197  });
198  routing.AddDimension(
199  routing.RegisterTransitCallback([&time, &manager](int64_t i, int64_t j) {
200  return time.Compute(manager.IndexToNode(i), manager.IndexToNode(j));
201  }),
202  kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime);
203 
204  // Setting the cost function. In fact, we create a time dependent dimension.
205  const int64_t max_time = manager.num_nodes() * (kXMax + kYMax) / kSpeed;
206  TrafficTransitionEvaluator traffic_evaluator(locations, max_time);
208  routing.RegisterStateDependentTransitCallback(::absl::bind_front(
209  &TrafficTransitionEvaluator::Run, &traffic_evaluator, manager)),
210  &routing.GetDimensionOrDie(kTime), kHorizon, kHorizon,
211  /*fix_start_cumul_to_zero=*/true, kTimeDepedentCost);
214 
215  // Adding time windows.
216  std::mt19937 randomizer(
217  GetSeed(absl::GetFlag(FLAGS_vrp_use_deterministic_random_seed)));
218  const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
219  const int64_t kTWDuration = 5 * 36;
220  for (int order = 1; order < manager.num_nodes(); ++order) {
221  const int64_t start =
222  absl::Uniform<int32_t>(randomizer, 0, kHorizon - kTWDuration);
223  time_dimension.CumulVar(order)->SetRange(start, start + kTWDuration);
224  }
225 
226  // Adding penalty costs to allow skipping orders.
227  const int64_t kPenalty = 10000000;
228  for (RoutingIndexManager::NodeIndex order = kFirstNodeAfterDepot;
229  order < routing.nodes(); ++order) {
230  std::vector<int64_t> orders(1, manager.NodeToIndex(order));
231  routing.AddDisjunction(orders, kPenalty);
232  }
233 
234  // Solve, returns a solution if any (owned by RoutingModel).
235  RoutingSearchParameters parameters = DefaultRoutingSearchParameters();
236  CHECK(google::protobuf::TextFormat::MergeFromString(
237  absl::GetFlag(FLAGS_routing_search_parameters), &parameters));
238  const Assignment* solution = routing.SolveWithParameters(parameters);
239  if (solution != nullptr) {
240  DisplayPlan(manager, routing, *solution, /*use_same_vehicle_costs=*/false,
241  /*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0,
242  routing.GetDimensionOrDie(kCapacity),
243  routing.GetDimensionOrDie(kTime));
244  } else {
245  LOG(INFO) << "No solution found.";
246  }
247  return 0;
248 }
PolyaUrn(int red_balls, int blue_balls, int seed)
TrafficTransitionEvaluator(const LocationContainer &distance_evaluator, int64_t max_time)
RoutingModel::StateDependentTransit Run(const RoutingIndexManager &manager, int64_t from_index, int64_t to_index)
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 SetSpanCostCoefficientForAllVehicles(int64_t coefficient)
Definition: routing.cc:7156
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 AddDimensionDependentDimensionWithVehicleCapacity(const std::vector< int > &pure_transits, const std::vector< int > &dependent_transits, const RoutingDimension *base_dimension, int64_t slack_max, std::vector< int64_t > vehicle_capacities, bool fix_start_cumul_to_zero, const std::string &name)
Creates a dimension with transits depending on the cumuls of another dimension.
Definition: routing.h:644
int RegisterStateDependentTransitCallback(VariableIndexEvaluator2 callback)
Definition: routing.cc:1334
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
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
StepFunction RandomStepFunction(int64_t mean, int64_t step_size, int64_t interval_end, int seed)
static const char kTime[]
int main(int argc, char **argv)
ABSL_FLAG(int, vrp_orders, 25, "Nodes in the problem.")
static const char kCapacity[]
static const char kTimeDepedentCost[]
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
double distance
int64_t start