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