16 #ifndef UTIL_GRAPH_IO_H_ 17 #define UTIL_GRAPH_IO_H_ 26 #include "absl/status/status.h" 27 #include "absl/status/statusor.h" 28 #include "absl/strings/numbers.h" 29 #include "absl/strings/str_format.h" 30 #include "absl/strings/str_join.h" 31 #include "absl/strings/str_split.h" 32 #include "ortools/base/filelineiter.h" 49 template <
class Graph>
82 template <
class Graph>
84 const std::string& filename,
bool directed,
85 std::vector<int>* num_nodes_with_color_or_null);
99 template <
class Graph>
102 const std::vector<int>& num_nodes_with_color);
106 template <
class Graph>
109 std::vector<typename Graph::NodeIndex> adj;
113 if (!out.empty()) out +=
'\n';
114 absl::StrAppend(&out, node,
"->", graph.
Head(arc));
119 adj.push_back(graph.
Head(arc));
122 std::sort(adj.begin(), adj.end());
124 if (node != 0) out +=
'\n';
125 absl::StrAppend(&out, node,
": ", absl::StrJoin(adj,
" "));
131 template <
class Graph>
133 const std::string& filename,
bool directed,
134 std::vector<int>* num_nodes_with_color_or_null) {
135 std::unique_ptr<Graph> graph;
136 int64_t num_nodes = -1;
137 int64_t num_expected_lines = -1;
138 int64_t num_lines_read = 0;
139 for (
const std::string& line : FileLines(filename)) {
141 if (num_lines_read == 1) {
142 std::vector<int64_t> header_ints;
152 num_nodes = header_ints[0];
153 num_expected_lines = header_ints[1];
154 if (num_nodes_with_color_or_null !=
nullptr) {
155 num_nodes_with_color_or_null->clear();
156 if (header_ints.size() == 2) {
158 num_nodes_with_color_or_null->push_back(num_nodes);
160 const int num_colors = header_ints[2];
161 if (header_ints.size() != num_colors + 2) {
163 absl::StatusCode::kInvalidArgument,
165 "There should be num_colors-1 color cardinalities in the" 166 " header of '%s' (where num_colors=%d): the last color" 167 " cardinality should be skipped",
168 filename, num_colors));
170 num_nodes_with_color_or_null->reserve(num_colors);
171 int num_nodes_left = num_nodes;
172 for (
int i = 3; i < header_ints.size(); ++i) {
173 num_nodes_with_color_or_null->push_back(header_ints[i]);
174 num_nodes_left -= header_ints[i];
175 if (header_ints[i] <= 0 || num_nodes_left <= 0) {
177 absl::StatusCode::kInvalidArgument,
178 absl::StrFormat(
"The color cardinalities in the header of" 179 " '%s' should always be >0 and add up to less" 180 " than the total number of nodes",
184 num_nodes_with_color_or_null->push_back(num_nodes_left);
187 const int64_t num_arcs = (directed ? 1 : 2) * num_expected_lines;
188 graph.reset(
new Graph(num_nodes, num_arcs));
191 size_t space_pos = line.find(
' ');
194 bool parse_success =
false;
195 if (space_pos != std::string::npos) {
196 if (absl::SimpleAtoi(absl::string_view(line.c_str(), space_pos),
198 absl::SimpleAtoi(absl::string_view(line.c_str() + space_pos + 1),
201 node1 >= 0 && node1 < num_nodes && node2 >= 0 && node2 < num_nodes;
204 if (!parse_success) {
206 absl::StatusCode::kInvalidArgument,
208 "In '%s', line %d: Expected two integers in the range [0, %d).",
209 filename, num_lines_read, num_nodes));
215 if (num_lines_read > num_expected_lines + 1)
continue;
216 graph->
AddArc(node1, node2);
217 if (!directed && node1 != node2) graph->
AddArc(node2, node1);
219 if (num_lines_read == 0) {
220 return absl::Status(absl::StatusCode::kInvalidArgument,
221 "Unknown or empty file");
223 if (num_lines_read != num_expected_lines + 1) {
225 absl::StatusCode::kInvalidArgument,
226 absl::StrFormat(
"The number of arcs/edges in '%s' (%d) does not match" 227 " the value announced in the header (%d)",
228 filename, num_lines_read - 1, num_expected_lines));
231 return graph.release();
234 template <
class Graph>
237 const std::vector<int>& num_nodes_with_color) {
238 FILE* f = fopen(filename.c_str(),
"w");
240 return absl::Status(absl::StatusCode::kInvalidArgument,
241 "Could not open file: '" + filename +
"'");
245 int num_self_arcs = 0;
249 if (graph.
Head(arc) == node) ++num_self_arcs;
252 if ((graph.
num_arcs() - num_self_arcs) % 2 != 0) {
254 return absl::Status(absl::StatusCode::kInvalidArgument,
255 "WriteGraphToFile() called with directed=false" 256 " and with a graph with an odd number of (non-self)" 261 f,
"%d %d", static_cast<int64_t>(graph.
num_nodes()),
262 static_cast<int64_t>(directed ? graph.
num_arcs()
263 : (graph.
num_arcs() + num_self_arcs) / 2));
264 if (!num_nodes_with_color.empty()) {
265 if (std::accumulate(num_nodes_with_color.begin(),
266 num_nodes_with_color.end(), 0) != graph.
num_nodes() ||
267 *std::min_element(num_nodes_with_color.begin(),
268 num_nodes_with_color.end()) <= 0) {
269 return absl::Status(absl::StatusCode::kInvalidArgument,
270 "WriteGraphToFile() called with invalid coloring.");
272 absl::FPrintF(f,
" %d", num_nodes_with_color.size());
273 for (
int i = 0; i < num_nodes_with_color.size() - 1; ++i) {
274 absl::FPrintF(f,
" %d", static_cast<int64_t>(num_nodes_with_color[i]));
277 absl::FPrintF(f,
"\n");
282 if (directed || head >= node) {
283 absl::FPrintF(f,
"%d %d\n", static_cast<int64_t>(node),
284 static_cast<uint64_t>(head));
288 if (fclose(f) != 0) {
289 return absl::Status(absl::StatusCode::kInternal,
290 "Could not close file '" + filename +
"'");
292 return ::absl::OkStatus();
297 #endif // UTIL_GRAPH_IO_H_ BeginEndWrapper< OutgoingArcIterator > OutgoingArcs(NodeIndexType node) const
NodeIndexType Head(ArcIndexType arc) const
ArcIndexType num_arcs() const
absl::StatusOr< Graph * > ReadGraphFile(const std::string &filename, bool directed, std::vector< int > *num_nodes_with_color_or_null)
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)
std::string GraphToString(const Graph &graph, GraphToStringFormat format)
IntegerRange< NodeIndex > AllNodes() const
absl::Status WriteGraphToFile(const Graph &graph, const std::string &filename, bool directed, const std::vector< int > &num_nodes_with_color)
NodeIndexType num_nodes() const