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