23#include "absl/base/attributes.h"
24#include "absl/log/check.h"
25#include "absl/log/log.h"
26#include "absl/strings/ascii.h"
27#include "absl/strings/str_cat.h"
28#include "absl/strings/str_format.h"
29#include "absl/time/clock.h"
30#include "absl/time/time.h"
31#include "absl/types/span.h"
40 const std::string format_normalized =
41 absl::AsciiStrToLower(absl::StripAsciiWhitespace(format));
51std::vector<RoutingSolution::Route> RoutesFromVector(
52 absl::Span<
const std::vector<int64_t>> routes,
53 std::optional<int64_t> depot = std::nullopt);
57 absl::Span<const int64_t>
solution, int64_t separator) {
61 std::vector<std::vector<int64_t>> routes;
62 int64_t current_route = 0;
64 if (routes.size() == current_route) {
65 routes.emplace_back(std::vector<int64_t>());
68 if (node == separator) {
71 routes[current_route].emplace_back(node);
78 absl::Span<
const std::vector<int64_t>> routes,
79 std::optional<int64_t> depot) {
80 std::vector<int64_t> total_demands(routes.size(), -1);
81 std::vector<int64_t> total_distances(routes.size(), -1);
83 return {RoutesFromVector(routes, depot), total_demands, total_distances};
86int64_t RoutingSolution::NumberOfNonemptyRoutes()
const {
87 int64_t num_nonempty_routes = 0;
88 for (
const Route& route : routes_) {
89 if (!route.empty()) num_nonempty_routes++;
91 return num_nonempty_routes;
95 const std::string& file_name)
const {
98 <<
"Could not open the solution file '" << file_name <<
"'";
101 <<
"Could not write the solution file '" << file_name <<
"'";
105std::string RoutingSolution::SerializeToTSPLIBString()
const {
106 std::string tour_out;
107 for (
const Route& route : routes_) {
108 if (route.empty())
continue;
110 for (
const Event& event : route) {
112 absl::StrAppendFormat(&tour_out,
"%d\n", event.arc.head());
115 absl::StrAppendFormat(&tour_out,
"-1\n");
120std::string RoutingSolution::SerializeToTSPLIBSolutionFile()
const {
123 int64_t number_of_nodes = 0;
124 for (
const Route& route : routes_) {
125 for (
const Event& event : route) {
126 if (event.arc.tail() > number_of_nodes) {
127 number_of_nodes =
event.arc.tail();
129 if (event.arc.head() > number_of_nodes) {
130 number_of_nodes =
event.arc.head();
134 number_of_nodes += 1;
136 std::string tour_out;
137 absl::StrAppendFormat(&tour_out,
"NAME : %s\n", name_);
138 absl::StrAppendFormat(&tour_out,
"COMMENT : Length = %d; Total time = %f s\n",
139 total_distance_, total_time_);
140 absl::StrAppendFormat(&tour_out,
"TYPE : TOUR\n");
141 absl::StrAppendFormat(&tour_out,
"DIMENSION : %d\n", number_of_nodes);
142 absl::StrAppendFormat(&tour_out,
"TOUR_SECTION\n");
143 absl::StrAppendFormat(&tour_out,
"%s", SerializeToTSPLIBString());
144 absl::StrAppendFormat(&tour_out,
"EOF");
152std::string RoutingSolution::SerializeToCVRPLIBString()
const {
153 std::string tour_out;
156 for (
const Route& route : routes_) {
157 if (route.empty())
continue;
158 std::string current_route = SerializeRouteToCVRPLIBString(route);
161 if (!current_route.empty()) {
162 absl::StrAppendFormat(&tour_out,
"Route #%d: %s\n", route_index++,
163 absl::StripAsciiWhitespace(current_route));
169std::string RoutingSolution::SerializeToCVRPLIBSolutionFile()
const {
170 std::string tour_out = SerializeToCVRPLIBString();
171 absl::StrAppendFormat(&tour_out,
"Cost %d", total_cost_);
175std::string RoutingSolution::SerializeToCARPLIBString()
const {
176 std::string tour_out;
177 int64_t num_out_route = 1;
178 int64_t num_iteration_route = 0;
181 for (
const Route& route : routes_) {
182 std::string current_route;
184 for (
const RoutingSolution::Event& event : route) {
186 switch (event.type) {
188 ABSL_FALLTHROUGH_INTENDED;
190 CHECK_EQ(event.arc.tail(), event.arc.head());
191 depot =
event.arc.tail();
196 ABSL_FALLTHROUGH_INTENDED;
208 absl::StrAppendFormat(¤t_route,
"(%s %d,%d,%d) ", type,
209 event.demand_id, event.arc.tail() + 1,
210 event.arc.head() + 1);
215 if (!route.empty()) {
216 const int64_t day = 1;
217 const int64_t num_events = std::count_if(
218 route.begin(), route.end(), [](
const RoutingSolution::Event& event) {
220 return event.type != RoutingSolution::Event::Type::kTransit;
223 absl::StrAppendFormat(
224 &tour_out,
"%d %d %d %d %d %d %s\n",
226 day, num_out_route, total_demands_[num_iteration_route],
227 total_distances_[num_iteration_route], num_events,
228 absl::StripAsciiWhitespace(current_route));
233 num_iteration_route += 1;
235 absl::StripTrailingAsciiWhitespace(&tour_out);
239std::string RoutingSolution::SerializeToCARPLIBSolutionFile()
const {
241 absl::StrAppendFormat(&
solution,
"%d\n", total_cost_);
242 absl::StrAppendFormat(&
solution,
"%d\n", NumberOfNonemptyRoutes());
243 absl::StrAppendFormat(&
solution,
"%f\n", total_time_);
244 absl::StrAppend(&
solution, SerializeToCARPLIBString());
248std::string RoutingSolution::SerializeToNEARPLIBString()
const {
249 std::string tour_out;
250 int64_t route_index = 1;
252 for (
const Route& route : routes_) {
253 std::string current_route;
254 int64_t current_node = -2;
259 if (route.size() <= 1)
continue;
260 if (route.size() == 2 &&
272 for (
const RoutingSolution::Event& event : route) {
273 switch (event.type) {
276 CHECK_EQ(event.arc.tail(), event.arc.head());
277 current_node =
event.arc.tail();
278 absl::StrAppendFormat(¤t_route,
"%d", event.arc.tail() + 1);
282 CHECK_EQ(event.arc.tail(), event.arc.head());
283 if (current_node != event.arc.tail()) {
284 absl::StrAppendFormat(¤t_route,
" %d", event.arc.tail() + 1);
288 ABSL_FALLTHROUGH_INTENDED;
290 CHECK(!event.arc_name.empty())
291 <<
"Arc " <<
event.arc.tail() <<
"-" <<
event.arc.head()
292 <<
" does not have a name in the solution object.";
297 if (current_node == event.arc.tail()) {
299 absl::StrAppendFormat(¤t_route,
"-%s-%d", event.arc_name,
300 event.arc.head() + 1);
304 absl::StrAppendFormat(¤t_route,
" %d-%s-%d",
305 event.arc.tail() + 1, event.arc_name,
306 event.arc.head() + 1);
308 current_node =
event.arc.head();
311 CHECK_EQ(event.arc.tail(), event.arc.head());
312 absl::StrAppendFormat(¤t_route,
" N%d", event.arc.head() + 1);
313 current_node =
event.arc.head();
322 if (!current_route.empty()) {
323 absl::StrAppendFormat(&tour_out,
"Route #%d : %s\n", route_index++,
324 absl::StripAsciiWhitespace(current_route));
327 absl::StripTrailingAsciiWhitespace(&tour_out);
331std::string RoutingSolution::SerializeToNEARPLIBSolutionFile()
const {
332 const std::string date =
333 absl::FormatTime(
"%B %d, %E4Y", absl::Now(), absl::LocalTimeZone());
336 absl::StrAppendFormat(&
solution,
"Instance name: %s\n", name_);
337 absl::StrAppendFormat(&
solution,
"Authors: %s\n", authors_);
338 absl::StrAppendFormat(&
solution,
"Date: %s\n", date);
339 absl::StrAppendFormat(&
solution,
"Reference: OR-Tools\n");
340 absl::StrAppendFormat(&
solution,
"Solution\n");
341 absl::StrAppendFormat(&
solution,
"%s\n", SerializeToNEARPLIBString());
342 absl::StrAppendFormat(&
solution,
"Total cost: %d", total_cost_);
352 absl::Span<const int64_t> route_int,
353 std::optional<int64_t> depot = std::nullopt);
355std::vector<RoutingSolution::Route> RoutesFromVector(
356 absl::Span<
const std::vector<int64_t>> routes,
357 std::optional<int64_t> depot) {
358 std::vector<RoutingSolution::Route> solution_routes;
359 solution_routes.reserve(routes.size());
360 for (
const std::vector<int64_t>& route : routes) {
362 solution_routes.emplace_back(RouteFromVector(route, depot));
364 return solution_routes;
368 std::optional<int64_t> forced_depot) {
375 (forced_depot.has_value()) ? forced_depot.value() : route_int[0];
379 -1,
Arc{depot, depot}});
380 for (int64_t i = 0;
i < route_int.size() - 1; ++
i) {
381 int64_t tail = route_int[
i];
382 int64_t head = route_int[
i + 1];
387 -1,
Arc{depot, depot}});
398 <<
"The route does not begin with a Start event to indicate "
400 const int64_t depot = first_event.
arc.
tail();
402 CHECK_GE(depot, 0) <<
"The given depot is negative: " << depot;
403 CHECK_LE(depot, 1) <<
"The given depot is greater than 1: " << depot;
406 std::string current_route;
408 for (int64_t i = 1;
i < route.size() - 1; ++
i) {
413 int64_t node =
event.
arc.
head();
415 absl::StrAppendFormat(¤t_route,
"%d ", node - depot);
424 CHECK_EQ(depot, last_event.arc.tail());
425 CHECK_EQ(last_event.arc.tail(), last_event.arc.head());
427 LOG(FATAL) <<
"The route does not finish with an End event to "
428 "indicate the depot.";
431 return current_route;
static std::vector< std::vector< int64_t > > SplitRoutes(absl::Span< const int64_t > solution, int64_t separator)
std::string SerializeToSolutionFile(RoutingOutputFormat format) const
RoutingSolution(std::vector< Route > routes, std::vector< int64_t > total_demands, std::vector< int64_t > total_distances, int64_t total_cost=-1, int64_t total_distance=-1, double total_time=-1.0, std::string_view name="")
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::vector< Event > Route
absl::Status WriteString(File *file, absl::string_view contents, Options options)
absl::Status Open(absl::string_view file_name, absl::string_view mode, File **f, Options options)
RoutingOutputFormat RoutingOutputFormatFromString(std::string_view format)
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid