00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include <vector>
00039
00040 #include "base/callback.h"
00041 #include "base/commandlineflags.h"
00042 #include "base/commandlineflags.h"
00043 #include "base/strtoint.h"
00044 #include "base/file.h"
00045 #include "base/split.h"
00046 #include "base/mathutil.h"
00047 #include "constraint_solver/routing.h"
00048
00049 DEFINE_string(pdp_file, "",
00050 "File containing the Pickup and Delivery Problem to solve.");
00051 DEFINE_int32(pdp_force_vehicles, 0,
00052 "Force the number of vehicles used (maximum number of routes.");
00053 DEFINE_bool(pdp_display_solution, false,
00054 "Displays the solution of the Pickup and Delivery Problem.");
00055
00056 namespace operations_research {
00057
00058
00059
00060 const int64 kScalingFactor = 1000;
00061
00062
00063
00064 typedef std::vector<std::pair<int, int> > Coordinates;
00065
00066
00067
00068 int64 Travel(const Coordinates* const coords,
00069 RoutingModel::NodeIndex from,
00070 RoutingModel::NodeIndex to) {
00071 DCHECK(coords != NULL);
00072 const int xd =
00073 coords->at(from.value()).first - coords->at(to.value()).first;
00074 const int yd =
00075 coords->at(from.value()).second - coords->at(to.value()).second;
00076 return static_cast<int64>(kScalingFactor * sqrt(1.0L * xd * xd + yd * yd));
00077 }
00078
00079
00080
00081 int64 ServiceTime(const std::vector<int64>* const service_times,
00082 RoutingModel::NodeIndex node) {
00083 return kScalingFactor * service_times->at(node.value());
00084 }
00085
00086
00087
00088
00089
00090 int64 TravelPlusServiceTime(const Coordinates* const coords,
00091 const std::vector<int64>* const service_times,
00092 RoutingModel::NodeIndex from,
00093 RoutingModel::NodeIndex to) {
00094 return ServiceTime(service_times, from) + Travel(coords, from, to);
00095 }
00096
00097
00098
00099 int64 Demand(const std::vector<int64>* const demands,
00100 RoutingModel::NodeIndex from,
00101 RoutingModel::NodeIndex to) {
00102 return demands->at(from.value());
00103 }
00104
00105
00106 string VerboseOutput(const RoutingModel& routing,
00107 const Assignment& assignment,
00108 const Coordinates& coords,
00109 const std::vector<int64>& service_times) {
00110 string output;
00111 for (int i = 0; i < routing.vehicles(); ++i) {
00112 StringAppendF(&output, "Vehicle %d: ", i);
00113 int64 index = routing.Start(i);
00114 if (routing.IsEnd(assignment.Value(routing.NextVar(index)))) {
00115 StringAppendF(&output, "empty");
00116 } else {
00117 while (!routing.IsEnd(index)) {
00118 RoutingModel::NodeIndex real_node = routing.IndexToNode(index);
00119 StringAppendF(&output, "%d ", real_node.value());
00120 const IntVar* vehicle = routing.VehicleVar(index);
00121 StringAppendF(&output, "Vehicle(%lld) ", assignment.Value(vehicle));
00122 const IntVar* arrival = routing.CumulVar(index, "time");
00123 StringAppendF(&output, "Time(%lld..%lld) ",
00124 assignment.Min(arrival),
00125 assignment.Max(arrival));
00126 const IntVar* load = routing.CumulVar(index, "demand");
00127 StringAppendF(&output, "Load(%lld..%lld) ",
00128 assignment.Min(load),
00129 assignment.Max(load));
00130 index = assignment.Value(routing.NextVar(index));
00131 StringAppendF(&output, "Transit(%lld) ",
00132 TravelPlusServiceTime(&coords,
00133 &service_times,
00134 real_node,
00135 routing.IndexToNode(index)));
00136 }
00137 StringAppendF(&output, "Route end ");
00138 const IntVar* vehicle = routing.VehicleVar(index);
00139 StringAppendF(&output, "Vehicle(%lld) ", assignment.Value(vehicle));
00140 const IntVar* arrival = routing.CumulVar(index, "time");
00141 StringAppendF(&output, "Time(%lld..%lld) ",
00142 assignment.Min(arrival),
00143 assignment.Max(arrival));
00144 const IntVar* load = routing.CumulVar(index, "demand");
00145 StringAppendF(&output, "Load(%lld..%lld) ",
00146 assignment.Min(load),
00147 assignment.Max(load));
00148 }
00149 StringAppendF(&output, "\n");
00150 }
00151 return output;
00152 }
00153
00154 namespace {
00155
00156
00157 bool SafeParseInt64Array(const string& str, std::vector<int64>* parsed_int) {
00158 std::vector<string> items;
00159 static const char kWhiteSpaces[] = " \t\n\v\f\r";
00160 SplitStringUsing(str, kWhiteSpaces, &items);
00161 parsed_int->assign(items.size(), 0);
00162 for (int i = 0; i < items.size(); ++i) {
00163 const char* item = items[i].c_str();
00164 char *endptr = NULL;
00165 (*parsed_int)[i] = strto64(item, &endptr, 10);
00166
00167 if (*endptr != '\0') return false;
00168 }
00169 return true;
00170 }
00171 }
00172
00173
00174
00175 bool LoadAndSolve(const string& pdp_file) {
00176
00177 std::vector<string> lines;
00178 {
00179 const int64 kMaxInputFileSize = 1 << 30;
00180 File* data_file = File::OpenOrDie(pdp_file.c_str(), "r");
00181 string contents;
00182 data_file->ReadToString(&contents, kMaxInputFileSize);
00183 data_file->Close();
00184 if (contents.size() == kMaxInputFileSize) {
00185 LOG(WARNING)
00186 << "Input file '" << pdp_file << "' is too large (>"
00187 << kMaxInputFileSize << " bytes).";
00188 return false;
00189 }
00190 SplitStringUsing(contents, "\n", &lines);
00191 }
00192
00193 if (lines.empty()) {
00194 LOG(WARNING) << "Empty file: " << pdp_file;
00195 return false;
00196 }
00197
00198 std::vector<int64> parsed_int;
00199 if (!SafeParseInt64Array(lines[0], &parsed_int)
00200 || parsed_int.size() != 3
00201 || parsed_int[0] < 0
00202 || parsed_int[1] < 0
00203 || parsed_int[2] < 0) {
00204 LOG(WARNING) << "Malformed header: " << lines[0];
00205 return false;
00206 }
00207 const int num_vehicles = FLAGS_pdp_force_vehicles > 0 ?
00208 FLAGS_pdp_force_vehicles : parsed_int[0];
00209 const int64 capacity = parsed_int[1];
00210
00211
00212
00213 std::vector<int> customer_ids;
00214 std::vector<std::pair<int, int> > coords;
00215 std::vector<int64> demands;
00216 std::vector<int64> open_times;
00217 std::vector<int64> close_times;
00218 std::vector<int64> service_times;
00219 std::vector<RoutingModel::NodeIndex> pickups;
00220 std::vector<RoutingModel::NodeIndex> deliveries;
00221 int64 horizon = 0;
00222 for (int line_index = 1; line_index < lines.size(); ++line_index) {
00223 if (!SafeParseInt64Array(lines[line_index], &parsed_int)
00224 || parsed_int.size() != 9
00225 || parsed_int[0] < 0
00226 || parsed_int[4] < 0
00227 || parsed_int[5] < 0
00228 || parsed_int[6] < 0
00229 || parsed_int[7] < 0
00230 || parsed_int[8] < 0) {
00231 LOG(WARNING)
00232 << "Malformed line #" << line_index << ": " << lines[line_index];
00233 return false;
00234 }
00235 const int customer_id = parsed_int[0];
00236 const int x = parsed_int[1];
00237 const int y = parsed_int[2];
00238 const int delivery = parsed_int[8];
00239 const int64 demand = delivery == 0 ? -parsed_int[3] : parsed_int[3];
00240 const int64 open_time = parsed_int[4];
00241 const int64 close_time = parsed_int[5];
00242 const int64 service_time = parsed_int[6];
00243 const int pickup = parsed_int[7];
00244 customer_ids.push_back(customer_id);
00245 coords.push_back(std::make_pair(x, y));
00246 demands.push_back(demand);
00247 open_times.push_back(open_time);
00248 close_times.push_back(close_time);
00249 service_times.push_back(service_time);
00250 pickups.push_back(RoutingModel::NodeIndex(pickup));
00251 deliveries.push_back(RoutingModel::NodeIndex(delivery));
00252 horizon = std::max(horizon, close_time);
00253 }
00254
00255
00256 const int num_nodes = customer_ids.size();
00257 RoutingModel routing(num_nodes, num_vehicles);
00258 routing.SetCost(NewPermanentCallback(
00259 Travel, const_cast<const Coordinates*>(&coords)));
00260 routing.AddDimension(
00261 NewPermanentCallback(&Demand, const_cast<const std::vector<int64>*>(&demands)),
00262 0, capacity, "demand");
00263 routing.AddDimension(
00264 NewPermanentCallback(&TravelPlusServiceTime,
00265 const_cast<const Coordinates*>(&coords),
00266 const_cast<const std::vector<int64>*>(&service_times)),
00267 kScalingFactor * horizon,
00268 kScalingFactor * horizon,
00269 "time");
00270 Solver* const solver = routing.solver();
00271 for (RoutingModel::NodeIndex i(0); i < num_nodes; ++i) {
00272 const int64 index = routing.NodeToIndex(i);
00273 if (pickups[i.value()] == 0) {
00274 if (deliveries[i.value()] == 0) {
00275 routing.SetDepot(i);
00276 } else {
00277 const int64 delivery_index = routing.NodeToIndex(deliveries[i.value()]);
00278 solver->AddConstraint(solver->MakeEquality(
00279 routing.VehicleVar(index),
00280 routing.VehicleVar(delivery_index)));
00281 solver->AddConstraint(solver->MakeLessOrEqual(
00282 routing.CumulVar(index, "time"),
00283 routing.CumulVar(delivery_index, "time")));
00284 routing.AddPickupAndDelivery(i, deliveries[i.value()]);
00285 }
00286 }
00287 IntVar* const cumul = routing.CumulVar(index, "time");
00288 cumul->SetMin(kScalingFactor * open_times[i.value()]);
00289 cumul->SetMax(kScalingFactor * close_times[i.value()]);
00290 }
00291
00292 const int64 kPenalty = 10000000;
00293 for (RoutingModel::NodeIndex order(1);
00294 order < routing.nodes(); ++order) {
00295 std::vector<RoutingModel::NodeIndex> orders(1, order);
00296 routing.AddDisjunction(orders, kPenalty);
00297 }
00298
00299
00300 routing.set_first_solution_strategy(RoutingModel::ROUTING_ALL_UNPERFORMED);
00301 routing.SetCommandLineOption("routing_no_lns", "true");
00302
00303
00304 const Assignment* assignment = routing.Solve(NULL);
00305 if (NULL != assignment) {
00306 LG << "Cost: " << assignment->ObjectiveValue();
00307 LG << VerboseOutput(routing, *assignment, coords, service_times);
00308 return true;
00309 }
00310 return false;
00311 }
00312
00313 }
00314
00315 int main(int argc, char **argv) {
00316 google::ParseCommandLineFlags(&argc, &argv, true);
00317 if (!operations_research::LoadAndSolve(FLAGS_pdp_file)) {
00318 LG << "Error solving " << FLAGS_pdp_file;
00319 }
00320 return 0;
00321 }