19 #include <string_view>
22 #include "absl/strings/ascii.h"
23 #include "absl/time/clock.h"
24 #include "absl/time/time.h"
30 const std::string format_normalized =
31 absl::AsciiStrToLower(absl::StripAsciiWhitespace(format));
41 std::vector<RoutingSolution::Route> RoutesFromVector(
42 const std::vector<std::vector<int64_t>>& routes,
43 std::optional<int64_t> depot = std::nullopt);
47 const std::vector<int64_t>& solution, int64_t separator) {
51 std::vector<std::vector<int64_t>> routes;
52 int64_t current_route = 0;
53 for (int64_t node : solution) {
54 if (routes.size() == current_route) {
55 routes.emplace_back(std::vector<int64_t>());
58 if (node == separator) {
61 routes[current_route].emplace_back(node);
68 const std::vector<std::vector<int64_t>>& routes,
69 std::optional<int64_t> depot) {
70 std::vector<int64_t> total_demands(routes.size(), -1);
71 std::vector<int64_t> total_distances(routes.size(), -1);
73 return {RoutesFromVector(routes, depot), total_demands, total_distances};
76 int64_t RoutingSolution::NumberOfNonemptyRoutes()
const {
77 int64_t num_nonempty_routes = 0;
78 for (
const Route& route : routes_) {
79 if (!route.empty()) num_nonempty_routes++;
81 return num_nonempty_routes;
85 const std::string& file_name)
const {
88 <<
"Could not open the solution file '" << file_name <<
"'";
91 <<
"Could not write the solution file '" << file_name <<
"'";
95 std::string RoutingSolution::SerializeToTSPLIBString()
const {
97 for (
const Route& route : routes_) {
98 if (route.empty())
continue;
100 for (
const Event& event : route) {
102 absl::StrAppendFormat(&tour_out,
"%d\n", event.arc.head());
105 absl::StrAppendFormat(&tour_out,
"-1\n");
110 std::string RoutingSolution::SerializeToTSPLIBSolutionFile()
const {
113 int64_t number_of_nodes = 0;
114 for (
const Route& route : routes_) {
115 for (
const Event& event : route) {
116 if (event.arc.tail() > number_of_nodes) {
117 number_of_nodes =
event.arc.tail();
119 if (event.arc.head() > number_of_nodes) {
120 number_of_nodes =
event.arc.head();
124 number_of_nodes += 1;
126 std::string tour_out;
127 absl::StrAppendFormat(&tour_out,
"NAME : %s\n", name_);
128 absl::StrAppendFormat(&tour_out,
"COMMENT : Length = %d; Total time = %f s\n",
129 total_distance_, total_time_);
130 absl::StrAppendFormat(&tour_out,
"TYPE : TOUR\n");
131 absl::StrAppendFormat(&tour_out,
"DIMENSION : %d\n", number_of_nodes);
132 absl::StrAppendFormat(&tour_out,
"TOUR_SECTION\n");
133 absl::StrAppendFormat(&tour_out,
"%s", SerializeToTSPLIBString());
134 absl::StrAppendFormat(&tour_out,
"EOF");
142 std::string RoutingSolution::SerializeToCVRPLIBString()
const {
143 std::string tour_out;
146 for (
const Route& route : routes_) {
147 if (route.empty())
continue;
148 std::string current_route = SerializeRouteToCVRPLIBString(route);
151 if (!current_route.empty()) {
152 absl::StrAppendFormat(&tour_out,
"Route #%d: %s\n", route_index++,
153 absl::StripAsciiWhitespace(current_route));
159 std::string RoutingSolution::SerializeToCVRPLIBSolutionFile()
const {
160 std::string tour_out = SerializeToCVRPLIBString();
161 absl::StrAppendFormat(&tour_out,
"Cost %d", total_cost_);
165 std::string RoutingSolution::SerializeToCARPLIBString()
const {
166 std::string tour_out;
167 int64_t num_out_route = 1;
168 int64_t num_iteration_route = 0;
171 for (
const Route& route : routes_) {
172 std::string current_route;
174 for (
const RoutingSolution::Event& event : route) {
176 switch (event.type) {
178 ABSL_FALLTHROUGH_INTENDED;
180 CHECK_EQ(event.arc.tail(), event.arc.head());
181 depot =
event.arc.tail();
186 ABSL_FALLTHROUGH_INTENDED;
198 absl::StrAppendFormat(¤t_route,
"(%s %d,%d,%d) ", type,
199 event.demand_id, event.arc.tail() + 1,
200 event.arc.head() + 1);
205 if (!route.empty()) {
206 const int64_t day = 1;
207 const int64_t num_events = std::count_if(
208 route.begin(), route.end(), [](
const RoutingSolution::Event& event) {
210 return event.type != RoutingSolution::Event::Type::kTransit;
213 absl::StrAppendFormat(
214 &tour_out,
"%d %d %d %d %d %d %s\n",
216 day, num_out_route, total_demands_[num_iteration_route],
217 total_distances_[num_iteration_route], num_events,
218 absl::StripAsciiWhitespace(current_route));
223 num_iteration_route += 1;
225 absl::StripTrailingAsciiWhitespace(&tour_out);
229 std::string RoutingSolution::SerializeToCARPLIBSolutionFile()
const {
230 std::string solution;
231 absl::StrAppendFormat(&solution,
"%d\n", total_cost_);
232 absl::StrAppendFormat(&solution,
"%d\n", NumberOfNonemptyRoutes());
233 absl::StrAppendFormat(&solution,
"%f\n", total_time_);
234 absl::StrAppend(&solution, SerializeToCARPLIBString());
238 std::string RoutingSolution::SerializeToNEARPLIBString()
const {
239 std::string tour_out;
240 int64_t route_index = 1;
242 for (
const Route& route : routes_) {
243 std::string current_route;
244 int64_t current_node = -2;
249 if (route.size() <= 1)
continue;
250 if (route.size() == 2 &&
262 for (
const RoutingSolution::Event& event : route) {
263 switch (event.type) {
266 CHECK_EQ(event.arc.tail(), event.arc.head());
267 current_node =
event.arc.tail();
268 absl::StrAppendFormat(¤t_route,
"%d", event.arc.tail() + 1);
272 CHECK_EQ(event.arc.tail(), event.arc.head());
273 if (current_node != event.arc.tail()) {
274 absl::StrAppendFormat(¤t_route,
" %d", event.arc.tail() + 1);
278 ABSL_FALLTHROUGH_INTENDED;
280 CHECK(!event.arc_name.empty())
281 <<
"Arc " <<
event.arc.tail() <<
"-" <<
event.arc.head()
282 <<
" does not have a name in the solution object.";
287 if (current_node == event.arc.tail()) {
289 absl::StrAppendFormat(¤t_route,
"-%s-%d", event.arc_name,
290 event.arc.head() + 1);
294 absl::StrAppendFormat(¤t_route,
" %d-%s-%d",
295 event.arc.tail() + 1, event.arc_name,
296 event.arc.head() + 1);
298 current_node =
event.arc.head();
301 CHECK_EQ(event.arc.tail(), event.arc.head());
302 absl::StrAppendFormat(¤t_route,
" N%d", event.arc.head() + 1);
303 current_node =
event.arc.head();
312 if (!current_route.empty()) {
313 absl::StrAppendFormat(&tour_out,
"Route #%d : %s\n", route_index++,
314 absl::StripAsciiWhitespace(current_route));
317 absl::StripTrailingAsciiWhitespace(&tour_out);
321 std::string RoutingSolution::SerializeToNEARPLIBSolutionFile()
const {
322 const std::string date =
323 absl::FormatTime(
"%B %d, %E4Y", absl::Now(), absl::LocalTimeZone());
325 std::string solution;
326 absl::StrAppendFormat(&solution,
"Instance name: %s\n", name_);
327 absl::StrAppendFormat(&solution,
"Authors: %s\n", authors_);
328 absl::StrAppendFormat(&solution,
"Date: %s\n", date);
329 absl::StrAppendFormat(&solution,
"Reference: OR-Tools\n");
330 absl::StrAppendFormat(&solution,
"Solution\n");
331 absl::StrAppendFormat(&solution,
"%s\n", SerializeToNEARPLIBString());
332 absl::StrAppendFormat(&solution,
"Total cost: %d", total_cost_);
342 const std::vector<int64_t>& route_int,
343 std::optional<int64_t> depot = std::nullopt);
345 std::vector<RoutingSolution::Route> RoutesFromVector(
346 const std::vector<std::vector<int64_t>>& routes,
347 std::optional<int64_t> depot) {
348 std::vector<RoutingSolution::Route> solution_routes;
349 solution_routes.reserve(routes.size());
350 for (
const std::vector<int64_t>& route : routes) {
352 solution_routes.emplace_back(RouteFromVector(route, depot));
354 return solution_routes;
358 std::optional<int64_t> forced_depot) {
365 (forced_depot.has_value()) ? forced_depot.value() : route_int[0];
369 -1, Arc{depot, depot}});
370 for (int64_t i = 0; i < route_int.size() - 1; ++i) {
371 int64_t
tail = route_int[i];
372 int64_t
head = route_int[i + 1];
373 route.emplace_back(RoutingSolution::Event{
377 -1, Arc{depot, depot}});
386 RoutingSolution::Event first_event = route[0];
388 <<
"The route does not begin with a Start event to indicate "
390 const int64_t depot = first_event.arc.tail();
392 CHECK_GE(depot, 0) <<
"The given depot is negative: " << depot;
393 CHECK_LE(depot, 1) <<
"The given depot is greater than 1: " << depot;
396 std::string current_route;
398 for (int64_t i = 1; i < route.size() - 1; ++i) {
399 RoutingSolution::Event
event = route[i];
403 int64_t node =
event.arc.head();
405 absl::StrAppendFormat(¤t_route,
"%d ", node - depot);
412 RoutingSolution::Event last_event = route.back();
414 CHECK_EQ(depot, last_event.arc.tail());
415 CHECK_EQ(last_event.arc.tail(), last_event.arc.head());
417 LOG(FATAL) <<
"The route does not finish with an End event to "
418 "indicate the depot.";
421 return current_route;
static RoutingSolution FromSplitRoutes(const std::vector< std::vector< int64_t >> &routes, std::optional< int64_t > depot=std::nullopt)
static std::vector< std::vector< int64_t > > SplitRoutes(const std::vector< int64_t > &solution, int64_t separator)
void WriteToSolutionFile(RoutingOutputFormat format, const std::string &file_name) const
std::vector< Event > Route
std::string SerializeToSolutionFile(RoutingOutputFormat format) const
absl::Status WriteString(File *file, const absl::string_view &contents, int flags)
absl::Status Open(const absl::string_view &filename, const absl::string_view &mode, File **f, int flags)
Collection of objects used to extend the Constraint Solver library.
RoutingOutputFormat RoutingOutputFormatFromString(std::string_view format)