22#include "absl/strings/ascii.h"
23#include "absl/time/clock.h"
24#include "absl/time/time.h"
25#include "absl/types/span.h"
31 const std::string format_normalized =
32 absl::AsciiStrToLower(absl::StripAsciiWhitespace(format));
42std::vector<RoutingSolution::Route> RoutesFromVector(
43 absl::Span<
const std::vector<int64_t>> routes,
44 std::optional<int64_t> depot = std::nullopt);
48 const std::vector<int64_t>&
solution, int64_t separator) {
52 std::vector<std::vector<int64_t>> routes;
53 int64_t current_route = 0;
55 if (routes.size() == current_route) {
56 routes.emplace_back(std::vector<int64_t>());
59 if (node == separator) {
62 routes[current_route].emplace_back(node);
69 absl::Span<
const std::vector<int64_t>> routes,
70 std::optional<int64_t> depot) {
71 std::vector<int64_t> total_demands(routes.size(), -1);
72 std::vector<int64_t> total_distances(routes.size(), -1);
74 return {RoutesFromVector(routes, depot), total_demands, total_distances};
77int64_t RoutingSolution::NumberOfNonemptyRoutes()
const {
78 int64_t num_nonempty_routes = 0;
79 for (
const Route& route : routes_) {
80 if (!route.empty()) num_nonempty_routes++;
82 return num_nonempty_routes;
86 const std::string& file_name)
const {
89 <<
"Could not open the solution file '" << file_name <<
"'";
92 <<
"Could not write the solution file '" << file_name <<
"'";
96std::string RoutingSolution::SerializeToTSPLIBString()
const {
98 for (
const Route& route : routes_) {
99 if (route.empty())
continue;
101 for (
const Event& event : route) {
103 absl::StrAppendFormat(&tour_out,
"%d\n", event.arc.head());
106 absl::StrAppendFormat(&tour_out,
"-1\n");
111std::string RoutingSolution::SerializeToTSPLIBSolutionFile()
const {
114 int64_t number_of_nodes = 0;
115 for (
const Route& route : routes_) {
116 for (
const Event& event : route) {
117 if (event.arc.tail() > number_of_nodes) {
118 number_of_nodes =
event.arc.tail();
120 if (event.arc.head() > number_of_nodes) {
121 number_of_nodes =
event.arc.head();
125 number_of_nodes += 1;
127 std::string tour_out;
128 absl::StrAppendFormat(&tour_out,
"NAME : %s\n", name_);
129 absl::StrAppendFormat(&tour_out,
"COMMENT : Length = %d; Total time = %f s\n",
130 total_distance_, total_time_);
131 absl::StrAppendFormat(&tour_out,
"TYPE : TOUR\n");
132 absl::StrAppendFormat(&tour_out,
"DIMENSION : %d\n", number_of_nodes);
133 absl::StrAppendFormat(&tour_out,
"TOUR_SECTION\n");
134 absl::StrAppendFormat(&tour_out,
"%s", SerializeToTSPLIBString());
135 absl::StrAppendFormat(&tour_out,
"EOF");
143std::string RoutingSolution::SerializeToCVRPLIBString()
const {
144 std::string tour_out;
147 for (
const Route& route : routes_) {
148 if (route.empty())
continue;
149 std::string current_route = SerializeRouteToCVRPLIBString(route);
152 if (!current_route.empty()) {
153 absl::StrAppendFormat(&tour_out,
"Route #%d: %s\n", route_index++,
154 absl::StripAsciiWhitespace(current_route));
160std::string RoutingSolution::SerializeToCVRPLIBSolutionFile()
const {
161 std::string tour_out = SerializeToCVRPLIBString();
162 absl::StrAppendFormat(&tour_out,
"Cost %d", total_cost_);
166std::string RoutingSolution::SerializeToCARPLIBString()
const {
167 std::string tour_out;
168 int64_t num_out_route = 1;
169 int64_t num_iteration_route = 0;
172 for (
const Route& route : routes_) {
173 std::string current_route;
175 for (
const RoutingSolution::Event& event : route) {
177 switch (event.type) {
179 ABSL_FALLTHROUGH_INTENDED;
181 CHECK_EQ(event.arc.tail(), event.arc.head());
182 depot =
event.arc.tail();
187 ABSL_FALLTHROUGH_INTENDED;
199 absl::StrAppendFormat(¤t_route,
"(%s %d,%d,%d) ", type,
200 event.demand_id, event.arc.tail() + 1,
201 event.arc.head() + 1);
206 if (!route.empty()) {
207 const int64_t day = 1;
208 const int64_t num_events = std::count_if(
209 route.begin(), route.end(), [](
const RoutingSolution::Event& event) {
211 return event.type != RoutingSolution::Event::Type::kTransit;
214 absl::StrAppendFormat(
215 &tour_out,
"%d %d %d %d %d %d %s\n",
217 day, num_out_route, total_demands_[num_iteration_route],
218 total_distances_[num_iteration_route], num_events,
219 absl::StripAsciiWhitespace(current_route));
224 num_iteration_route += 1;
226 absl::StripTrailingAsciiWhitespace(&tour_out);
230std::string RoutingSolution::SerializeToCARPLIBSolutionFile()
const {
232 absl::StrAppendFormat(&
solution,
"%d\n", total_cost_);
233 absl::StrAppendFormat(&
solution,
"%d\n", NumberOfNonemptyRoutes());
234 absl::StrAppendFormat(&
solution,
"%f\n", total_time_);
235 absl::StrAppend(&
solution, SerializeToCARPLIBString());
239std::string RoutingSolution::SerializeToNEARPLIBString()
const {
240 std::string tour_out;
241 int64_t route_index = 1;
243 for (
const Route& route : routes_) {
244 std::string current_route;
245 int64_t current_node = -2;
250 if (route.size() <= 1)
continue;
251 if (route.size() == 2 &&
263 for (
const RoutingSolution::Event& event : route) {
264 switch (event.type) {
267 CHECK_EQ(event.arc.tail(), event.arc.head());
268 current_node =
event.arc.tail();
269 absl::StrAppendFormat(¤t_route,
"%d", event.arc.tail() + 1);
273 CHECK_EQ(event.arc.tail(), event.arc.head());
274 if (current_node != event.arc.tail()) {
275 absl::StrAppendFormat(¤t_route,
" %d", event.arc.tail() + 1);
279 ABSL_FALLTHROUGH_INTENDED;
281 CHECK(!event.arc_name.empty())
282 <<
"Arc " <<
event.arc.tail() <<
"-" <<
event.arc.head()
283 <<
" does not have a name in the solution object.";
288 if (current_node == event.arc.tail()) {
290 absl::StrAppendFormat(¤t_route,
"-%s-%d", event.arc_name,
291 event.arc.head() + 1);
295 absl::StrAppendFormat(¤t_route,
" %d-%s-%d",
296 event.arc.tail() + 1, event.arc_name,
297 event.arc.head() + 1);
299 current_node =
event.arc.head();
302 CHECK_EQ(event.arc.tail(), event.arc.head());
303 absl::StrAppendFormat(¤t_route,
" N%d", event.arc.head() + 1);
304 current_node =
event.arc.head();
313 if (!current_route.empty()) {
314 absl::StrAppendFormat(&tour_out,
"Route #%d : %s\n", route_index++,
315 absl::StripAsciiWhitespace(current_route));
318 absl::StripTrailingAsciiWhitespace(&tour_out);
322std::string RoutingSolution::SerializeToNEARPLIBSolutionFile()
const {
323 const std::string date =
324 absl::FormatTime(
"%B %d, %E4Y", absl::Now(), absl::LocalTimeZone());
327 absl::StrAppendFormat(&
solution,
"Instance name: %s\n", name_);
328 absl::StrAppendFormat(&
solution,
"Authors: %s\n", authors_);
329 absl::StrAppendFormat(&
solution,
"Date: %s\n", date);
330 absl::StrAppendFormat(&
solution,
"Reference: OR-Tools\n");
331 absl::StrAppendFormat(&
solution,
"Solution\n");
332 absl::StrAppendFormat(&
solution,
"%s\n", SerializeToNEARPLIBString());
333 absl::StrAppendFormat(&
solution,
"Total cost: %d", total_cost_);
343 const std::vector<int64_t>& route_int,
344 std::optional<int64_t> depot = std::nullopt);
346std::vector<RoutingSolution::Route> RoutesFromVector(
347 absl::Span<
const std::vector<int64_t>> routes,
348 std::optional<int64_t> depot) {
349 std::vector<RoutingSolution::Route> solution_routes;
350 solution_routes.reserve(routes.size());
351 for (
const std::vector<int64_t>& route : routes) {
353 solution_routes.emplace_back(RouteFromVector(route, depot));
355 return solution_routes;
359 std::optional<int64_t> forced_depot) {
366 (forced_depot.has_value()) ? forced_depot.value() : route_int[0];
370 -1, Arc{depot, depot}});
371 for (int64_t i = 0;
i < route_int.size() - 1; ++
i) {
372 int64_t
tail = route_int[
i];
373 int64_t
head = route_int[
i + 1];
374 route.emplace_back(RoutingSolution::Event{
378 -1, Arc{depot, depot}});
387 RoutingSolution::Event first_event = route[0];
389 <<
"The route does not begin with a Start event to indicate "
391 const int64_t depot = first_event.arc.tail();
393 CHECK_GE(depot, 0) <<
"The given depot is negative: " << depot;
394 CHECK_LE(depot, 1) <<
"The given depot is greater than 1: " << depot;
397 std::string current_route;
399 for (int64_t i = 1;
i < route.size() - 1; ++
i) {
400 RoutingSolution::Event
event = route[
i];
404 int64_t node =
event.arc.head();
406 absl::StrAppendFormat(¤t_route,
"%d ", node - depot);
413 RoutingSolution::Event last_event = route.back();
415 CHECK_EQ(depot, last_event.arc.tail());
416 CHECK_EQ(last_event.arc.tail(), last_event.arc.head());
418 LOG(FATAL) <<
"The route does not finish with an End event to "
419 "indicate the depot.";
422 return current_route;
static RoutingSolution FromSplitRoutes(absl::Span< const std::vector< int64_t > > routes, std::optional< int64_t > depot=std::nullopt)
void WriteToSolutionFile(RoutingOutputFormat format, const std::string &file_name) const
std::string SerializeToSolutionFile(RoutingOutputFormat format) const
static std::vector< std::vector< int64_t > > SplitRoutes(const std::vector< int64_t > &solution, int64_t separator)
Public-facing builders.
std::vector< Event > Route
absl::Status Open(absl::string_view filename, absl::string_view mode, File **f, Options options)
As of 2016-01, these methods can only be used with flags = file::Defaults().
absl::Status WriteString(File *file, absl::string_view contents, Options options)
In SWIG mode, we don't want anything besides these top-level includes.
RoutingOutputFormat RoutingOutputFormatFromString(std::string_view format)
@ kServeNode
The vehicle serves the demand of the node.
@ kStart
The vehicle starts its route at a depot.
@ kServeEdge
The vehicle traverses the edge while servicing it.
@ kTransit
The vehicle simply goes through an edge or an arc without servicing.
@ kServeArc
The vehicle traverses the arc while servicing it.