Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
solution_serializer.h
Go to the documentation of this file.
1// Copyright 2010-2024 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// Utilities to serialize VRP-like solutions in standardised formats: either
15// TSPLIB or CVRPLIB.
16
17#ifndef OR_TOOLS_ROUTING_PARSERS_SOLUTION_SERIALIZER_H_
18#define OR_TOOLS_ROUTING_PARSERS_SOLUTION_SERIALIZER_H_
19
20#include <optional>
21#include <string>
22#include <string_view>
23#include <utility>
24#include <vector>
25
26#include "absl/strings/str_cat.h"
27#include "absl/strings/str_format.h"
28#include "absl/strings/string_view.h"
29#include "absl/types/span.h"
30#include "ortools/base/file.h"
34
35namespace operations_research {
36
37// Indicates the format in which the output should be done. This enumeration is
38// used for solutions and solver statistics.
40 kNone,
41 kTSPLIB,
45};
46
47// Parses a user-provided description of the output format. Expected inputs look
48// like (without quotes): "tsplib", "cvrplib", "carplib". Unrecognized strings
49// are parsed as kNone.
51
52// Describes completely a solution to a routing problem in preparation of its
53// serialization as a string.
55 public:
56 // Describes a state transition performed by a vehicle: starting from/ending
57 // at a given depot, serving a given customer, etc.
58 // When need be, each event can have a specific demand ID (this is mostly
59 // useful when servicing arcs and edges). An event always stores an arc:
60 // this is simply the edge when servicing the edge (it should correspond to
61 // the direction in which the edge is traversed); when the event is about
62 // a node (either a depot or a demand), both ends of the arc should be the
63 // node the event is about.
64 struct Event {
65 // Describes the type of events that occur along a route.
66 enum class Type {
67 // The vehicle starts its route at a depot.
68 kStart,
69 // The vehicle ends its route at a depot (not necessarily the same as the
70 // starting one).
71 kEnd,
72 // The vehicle traverses the arc while servicing it.
74 // The vehicle traverses the edge while servicing it.
76 // The vehicle serves the demand of the node.
78 // The vehicle simply goes through an edge or an arc without servicing.
80 };
81
83 int64_t demand_id;
85 std::string arc_name;
86
89 Event(Type type, int64_t demand_id, Arc arc, std::string_view arc_name)
91
92 bool operator==(const Event& other) const {
93 return type == other.type && demand_id == other.demand_id &&
94 arc == other.arc && arc_name == other.arc_name;
95 }
96 bool operator!=(const Event& other) const { return !(*this == other); }
97 };
98
99 using Route = std::vector<Event>;
100
101 RoutingSolution(std::vector<Route> routes, std::vector<int64_t> total_demands,
102 std::vector<int64_t> total_distances, int64_t total_cost = -1,
103 int64_t total_distance = -1, double total_time = -1.0,
104 std::string_view name = "")
105 : routes_(std::move(routes)),
106 total_demands_(std::move(total_demands)),
107 total_distances_(std::move(total_distances)),
108 total_cost_(total_cost),
109 total_distance_(total_distance),
110 total_time_(total_time),
111 name_(name) {
112 CHECK_EQ(routes_.size(), total_demands_.size());
113 CHECK_EQ(routes_.size(), total_distances_.size());
114 }
115
116 bool operator==(const RoutingSolution& other) const {
117 return routes_ == other.routes_ && total_demands_ == other.total_demands_ &&
118 total_distances_ == other.total_distances_ &&
119 total_cost_ == other.total_cost_ && total_time_ == other.total_time_;
120 }
121 bool operator!=(const RoutingSolution& other) const {
122 return !(*this == other);
123 }
124
125 // Setters for solution metadata.
126 void SetTotalTime(double total_time) { total_time_ = total_time; }
127 void SetTotalCost(int64_t total_cost) { total_cost_ = total_cost; }
128 void SetTotalDistance(int64_t total_distance) {
129 total_distance_ = total_distance;
130 }
131 void SetName(std::string_view name) { name_ = name; }
132 void SetAuthors(std::string_view authors) { authors_ = authors; }
133
134 // Public-facing builders.
135
136 // Splits a list of nodes whose routes are separated by the given separator
137 // (TSPLIB uses -1; it is crucial that the separator cannot be a node) into
138 // a vector per route, for use in FromSplit* functions.
139 static std::vector<std::vector<int64_t>> SplitRoutes(
140 const std::vector<int64_t>& solution, int64_t separator);
141
142 // Builds a RoutingSolution object from a vector of routes, each represented
143 // as a vector of nodes being traversed. All the routes are supposed to start
144 // and end at the depot if specified.
146 absl::Span<const std::vector<int64_t>> routes,
147 std::optional<int64_t> depot = std::nullopt);
148
149 // Serializes the bare solution to a string, i.e. only the routes for the
150 // vehicles, without other metadata that is typically present in solution
151 // files.
152 std::string SerializeToString(RoutingOutputFormat format) const {
153 switch (format) {
155 return "";
157 return SerializeToTSPLIBString();
159 return SerializeToCVRPLIBString();
161 return SerializeToCARPLIBString();
163 return SerializeToNEARPLIBString();
164 }
165 }
166
167 // Serializes the full solution to the given file, including metadata like
168 // instance name or total cost, depending on the format.
169 // For TSPLIB, solution files are typically called "tours".
171 switch (format) {
173 return "";
175 return SerializeToTSPLIBSolutionFile();
177 return SerializeToCVRPLIBSolutionFile();
179 return SerializeToCARPLIBSolutionFile();
181 return SerializeToNEARPLIBSolutionFile();
182 }
183 }
184
185 // Serializes the full solution to the given file, including metadata like
186 // instance name or total cost, depending on the format.
188 const std::string& file_name) const;
189
190 private:
191 // Description of the solution. Typically, one element per route (e.g., one
192 // vector of visited nodes per route). These elements are supposed to be
193 // returned by a solver.
194 // Depots are not explicitly stored as a route-level attribute, but rather by
195 // specific transitions (starting or ending at a depot).
196 std::vector<std::vector<Event>> routes_;
197 std::vector<int64_t> total_demands_;
198 std::vector<int64_t> total_distances_;
199
200 // Solution metadata. These elements could be set either by the solver or by
201 // the caller.
202 int64_t total_cost_;
203 int64_t total_distance_;
204 double total_time_;
205 std::string name_;
206 std::string authors_;
207
208 int64_t NumberOfNonemptyRoutes() const;
209
210 // The various implementations of SerializeToString depending on the format.
211
212 // Generates a string representation of a solution in the TSPLIB format.
213 // TSPLIB explicitly outputs the depot in its tours.
214 // It has been defined in
215 // http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/ where solutions are
216 // referred to as "tours".
217 std::string SerializeToTSPLIBString() const;
218 // Generates a string representation of a solution in the CVRPLIB format.
219 // CVRPLIB doesn't explicitly output the depot in its tours.
220 // Format used in http://vrp.atd-lab.inf.puc-rio.br/
221 // Better description of the format:
222 // http://dimacs.rutgers.edu/programs/challenge/vrp/cvrp/
223 std::string SerializeToCVRPLIBString() const;
224 // Generates a string representation of a solution in the CARPLIB format.
225 // Format used in https://www.uv.es/belengue/carp.html
226 // Formal description of the format: https://www.uv.es/~belengue/carp/READ_ME
227 // Another description of the format:
228 // http://dimacs.rutgers.edu/programs/challenge/vrp/carp/
229 std::string SerializeToCARPLIBString() const;
230 // Generates a string representation of a solution in the NEARPLIB format.
231 // Format used in https://www.sintef.no/projectweb/top/nearp/
232 // Formal description of the format:
233 // https://www.sintef.no/projectweb/top/nearp/documentation/
234 // Example:
235 // https://www.sintef.no/globalassets/project/top/nearp/solutionformat.txt
236 std::string SerializeToNEARPLIBString() const;
237
238 // The various implementations of SerializeToSolutionFile depending on the
239 // format. These methods are highly similar to the previous ones.
240 std::string SerializeToTSPLIBSolutionFile() const;
241 std::string SerializeToCVRPLIBSolutionFile() const;
242 std::string SerializeToCARPLIBSolutionFile() const;
243 std::string SerializeToNEARPLIBSolutionFile() const;
244};
245
246// Formats a solution or solver statistic according to the given format.
247template <typename T>
248std::string FormatStatistic(absl::string_view name, T value,
249 RoutingOutputFormat format) {
250 // TODO(user): think about using an enum instead of names (or even a
251 // full-fledged struct/class) for the various types of fields.
252 switch (format) {
254 ABSL_FALLTHROUGH_INTENDED;
256 return absl::StrCat(name, " = ", value);
258 return absl::StrCat(name, " ", value);
260 // For CARPLIB, the statistics do not have names, it's up to the user to
261 // memorize their order.
262 return absl::StrCat(value);
264 return absl::StrCat(name, " : ", value);
265 }
266}
267
268// Specialization for doubles to show a higher precision: without this
269// specialization, 591.556557 is displayed as 591.557.
270template <>
271inline std::string FormatStatistic(absl::string_view name, double value,
272 RoutingOutputFormat format) {
273 switch (format) {
275 ABSL_FALLTHROUGH_INTENDED;
277 return absl::StrFormat("%s = %f", name, value);
279 return absl::StrFormat("%s %f", name, value);
281 return absl::StrFormat("%f", value);
283 return absl::StrFormat("%s : %f", name, value);
284 }
285}
286
287// Prints a formatted solution or solver statistic according to the given
288// format.
289template <typename T>
290void PrintStatistic(absl::string_view name, T value,
291 RoutingOutputFormat format) {
292 absl::PrintF("%s\n", FormatStatistic(name, value, format));
293}
294} // namespace operations_research
295
296#endif // OR_TOOLS_ROUTING_PARSERS_SOLUTION_SERIALIZER_H_
void SetTotalTime(double total_time)
Setters for solution metadata.
static RoutingSolution FromSplitRoutes(absl::Span< const std::vector< int64_t > > routes, std::optional< int64_t > depot=std::nullopt)
void SetTotalDistance(int64_t total_distance)
std::string SerializeToString(RoutingOutputFormat format) const
void WriteToSolutionFile(RoutingOutputFormat format, const std::string &file_name) const
bool operator==(const RoutingSolution &other) 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="")
std::string SerializeToSolutionFile(RoutingOutputFormat format) const
void SetAuthors(std::string_view authors)
static std::vector< std::vector< int64_t > > SplitRoutes(const std::vector< int64_t > &solution, int64_t separator)
Public-facing builders.
bool operator!=(const RoutingSolution &other) const
const std::string name
A name for logging purposes.
int64_t value
double solution
In SWIG mode, we don't want anything besides these top-level includes.
std::string FormatStatistic(absl::string_view name, T value, RoutingOutputFormat format)
Formats a solution or solver statistic according to the given format.
void PrintStatistic(absl::string_view name, T value, RoutingOutputFormat format)
RoutingOutputFormat RoutingOutputFormatFromString(std::string_view format)
STL namespace.
Event(Type type, int64_t demand_id, Arc arc)
Type
Describes the type of events that occur along a route.
@ 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.
Event(Type type, int64_t demand_id, Arc arc, std::string_view arc_name)