C++ Reference

C++ Reference: Graph

io.h
Go to the documentation of this file.
1 // Copyright 2010-2018 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 // A collections of i/o utilities for the Graph classes in ./graph.h.
15 
16 #ifndef UTIL_GRAPH_IO_H_
17 #define UTIL_GRAPH_IO_H_
18 
19 #include <algorithm>
20 #include <memory>
21 #include <numeric>
22 #include <string>
23 #include <vector>
24 
25 #include "absl/status/status.h"
26 #include "absl/strings/numbers.h"
27 #include "absl/strings/str_cat.h"
28 #include "absl/strings/str_format.h"
29 #include "absl/strings/str_join.h"
30 #include "absl/strings/str_split.h"
31 #include "ortools/base/filelineiter.h"
32 #include "ortools/base/statusor.h"
33 #include "ortools/graph/graph.h"
34 
35 namespace util {
36 
37 // Returns a string representation of a graph.
39  // One arc per line, eg. "3->1".
41 
42  // One space-separated adjacency list per line, eg. "3: 5 1 3 1".
43  // Nodes with no outgoing arc get an empty list.
45 
46  // Ditto, but the adjacency lists are sorted.
48 };
49 template <class Graph>
50 std::string GraphToString(const Graph& graph, GraphToStringFormat format);
51 
52 // Read a graph file in the simple ".g" format: the file should be a text file
53 // containing only space-separated integers, whose first line is:
54 // <num nodes> <num edges> [<num_colors> <index of first node with color #1>
55 // <index of first node with color #2> ...]
56 // and whose subsequent lines represent edges if "directed" is false, or arcs if
57 // "directed" is true:
58 // <node1> <node2>.
59 //
60 // This returns a newly created graph upon success, which the user needs to take
61 // ownership of, or a failure status. See ortools/base/statusor.h.
62 //
63 // If "num_nodes_with_color_or_null" is not nullptr, it will be filled with the
64 // color information: num_nodes_with_color_or_null[i] will be the number of
65 // nodes with color #i. Furthermore, nodes are sorted by color.
66 //
67 // Examples:
68 // // Simply crash if the graph isn't successfully read from the file.
69 // typedef StaticGraph<> MyGraph; // This is just an example.
70 // std::unique_ptr<MyGraph> my_graph(
71 // ReadGraphFile<MyGraph>("graph.g", /*directed=*/ false).ValueOrDie());
72 //
73 // // More complicated error handling.
74 // absl::StatusOr<MyGraph*> error_or_graph =
75 // ReadGraphFile<MyGraph>("graph.g", /*directed=*/ false);
76 // if (!error_or_graph.ok()) {
77 // LOG(ERROR) << "Error: " << error_or_graph.status().error_message();
78 // } else {
79 // std::unique_ptr<MyGraph> my_graph(error_or_graph.ValueOrDie());
80 // ...
81 // }
82 template <class Graph>
83 absl::StatusOr<Graph*> ReadGraphFile(
84  const std::string& filename, bool directed,
85  std::vector<int>* num_nodes_with_color_or_null);
86 
87 // Writes a graph to the ".g" file format described above. If "directed" is
88 // true, all arcs are written to the file. If it is false, the graph is expected
89 // to be undirected (i.e. the number of arcs a->b is equal to the number of arcs
90 // b->a for all nodes a,b); and only the arcs a->b where a<=b are written. Note
91 // however that in this case, the symmetry of the graph is not fully checked
92 // (only the parity of the number of non-self arcs is).
93 //
94 // "num_nodes_with_color" is optional. If it is not empty, then the color
95 // information will be written to the header of the .g file. See ReadGraphFile.
96 //
97 // This method is the reverse of ReadGraphFile (with the same value for
98 // "directed").
99 template <class Graph>
100 absl::Status WriteGraphToFile(const Graph& graph, const std::string& filename,
101  bool directed,
102  const std::vector<int>& num_nodes_with_color);
103 
104 // Implementations of the templated methods.
105 
106 template <class Graph>
107 std::string GraphToString(const Graph& graph, GraphToStringFormat format) {
108  std::string out;
109  std::vector<typename Graph::NodeIndex> adj;
110  for (const typename Graph::NodeIndex node : graph.AllNodes()) {
111  if (format == PRINT_GRAPH_ARCS) {
112  for (const typename Graph::ArcIndex arc : graph.OutgoingArcs(node)) {
113  if (!out.empty()) out += '\n';
114  absl::StrAppend(&out, node, "->", graph.Head(arc));
115  }
116  } else { // PRINT_GRAPH_ADJACENCY_LISTS[_SORTED]
117  adj.clear();
118  for (const typename Graph::ArcIndex arc : graph.OutgoingArcs(node)) {
119  adj.push_back(graph.Head(arc));
120  }
121  if (format == PRINT_GRAPH_ADJACENCY_LISTS_SORTED) {
122  std::sort(adj.begin(), adj.end());
123  }
124  if (node != 0) out += '\n';
125  absl::StrAppend(&out, node, ": ", absl::StrJoin(adj, " "));
126  }
127  }
128  return out;
129 }
130 
131 template <class Graph>
132 absl::StatusOr<Graph*> ReadGraphFile(
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 num_nodes = -1;
137  int64 num_expected_lines = -1;
138  int64 num_lines_read = 0;
139  for (const std::string& line : FileLines(filename)) {
140  ++num_lines_read;
141  if (num_lines_read == 1) {
142  std::vector<int64> header_ints;
143  // if (!SplitStringAndParse(line, " ", &absl::SimpleAtoi,
144  // &header_ints) ||
145  // header_ints.size() < 2 || header_ints[0] < 0 || header_ints[1] < 0) {
146  // return absl::Status(
147  // absl::StatusCode::kInvalidArgument,
148  // absl::StrCat("First line of '", filename,
149  // "' should be at least two nonnegative integers."));
150  // }
151  num_nodes = header_ints[0];
152  num_expected_lines = header_ints[1];
153  if (num_nodes_with_color_or_null != nullptr) {
154  num_nodes_with_color_or_null->clear();
155  if (header_ints.size() == 2) {
156  // No coloring: all the nodes have the same color.
157  num_nodes_with_color_or_null->push_back(num_nodes);
158  } else {
159  const int num_colors = header_ints[2];
160  if (header_ints.size() != num_colors + 2) {
161  return absl::Status(
162  absl::StatusCode::kInvalidArgument,
163  absl::StrCat(
164  "There should be num_colors-1 color cardinalities in the"
165  " header of '",
166  filename, "' (where num_colors=", num_colors,
167  "): the last color cardinality should be", " skipped."));
168  }
169  num_nodes_with_color_or_null->reserve(num_colors);
170  int num_nodes_left = num_nodes;
171  for (int i = 3; i < header_ints.size(); ++i) {
172  num_nodes_with_color_or_null->push_back(header_ints[i]);
173  num_nodes_left -= header_ints[i];
174  if (header_ints[i] <= 0 || num_nodes_left <= 0) {
175  return absl::Status(
176  absl::StatusCode::kInvalidArgument,
177  absl::StrCat(
178  "The color cardinalities in the header of '", filename,
179  " should always be >0 and add up to less than the"
180  " total number of nodes."));
181  }
182  }
183  num_nodes_with_color_or_null->push_back(num_nodes_left);
184  }
185  }
186  const int64 num_arcs = (directed ? 1 : 2) * num_expected_lines;
187  graph.reset(new Graph(num_nodes, num_arcs));
188  continue;
189  }
190  int64_t node1 = -1;
191  int64_t node2 = -1;
192  if (sscanf(line.c_str(), "%ld %ld", &node1, &node2) != 2 || node1 < 0 ||
193  node2 < 0 || node1 >= num_nodes || node2 >= num_nodes) {
194  return absl::Status(
195  absl::StatusCode::kInvalidArgument,
196  absl::StrCat("In '", filename, "', line ", num_lines_read,
197  ": Expected two", " integers in the range [0, ",
198  num_nodes, ")."));
199  }
200  // We don't add superfluous arcs to the graph, but we still keep reading
201  // the file, to get better error messages: we want to know the actual
202  // number of lines, and also want to check the validity of the superfluous
203  // arcs (i.e. that their src/dst nodes are ok).
204  if (num_lines_read > num_expected_lines + 1) continue;
205  graph->AddArc(node1, node2);
206  if (!directed && node1 != node2) graph->AddArc(node2, node1);
207  }
208  if (num_lines_read == 0) {
209  return absl::Status(absl::StatusCode::kInvalidArgument,
210  "Unknown or empty file");
211  }
212  if (num_lines_read != num_expected_lines + 1) {
213  return absl::Status(absl::StatusCode::kInvalidArgument,
214  absl::StrCat("The number of arcs/edges in '", filename,
215  "' (", num_lines_read - 1,
216  " does not match the value announced in",
217  " the header (", num_expected_lines, ")"));
218  }
219  graph->Build();
220  return graph.release();
221 }
222 
223 template <class Graph>
224 absl::Status WriteGraphToFile(const Graph& graph, const std::string& filename,
225  bool directed,
226  const std::vector<int>& num_nodes_with_color) {
227  FILE* f = fopen(filename.c_str(), "w");
228  if (f == nullptr) {
229  return absl::Status(absl::StatusCode::kInvalidArgument,
230  "Could not open file: '" + filename + "'");
231  }
232  // In undirected mode, we must count the self-arcs separately. All other arcs
233  // should be duplicated.
234  int num_self_arcs = 0;
235  if (!directed) {
236  for (const typename Graph::NodeIndex node : graph.AllNodes()) {
237  for (const typename Graph::ArcIndex arc : graph.OutgoingArcs(node)) {
238  if (graph.Head(arc) == node) ++num_self_arcs;
239  }
240  }
241  if ((graph.num_arcs() - num_self_arcs) % 2 != 0) {
242  fclose(f);
243  return absl::Status(absl::StatusCode::kInvalidArgument,
244  "WriteGraphToFile() called with directed=false"
245  " and with a graph with an odd number of (non-self)"
246  " arcs!");
247  }
248  }
249  absl::FPrintF(
250  f, "%d %d", static_cast<int64>(graph.num_nodes()),
251  static_cast<int64>(directed ? graph.num_arcs()
252  : (graph.num_arcs() + num_self_arcs) / 2));
253  if (!num_nodes_with_color.empty()) {
254  if (std::accumulate(num_nodes_with_color.begin(),
255  num_nodes_with_color.end(), 0) != graph.num_nodes() ||
256  *std::min_element(num_nodes_with_color.begin(),
257  num_nodes_with_color.end()) <= 0) {
258  return absl::Status(absl::StatusCode::kInvalidArgument,
259  "WriteGraphToFile() called with invalid coloring.");
260  }
261  fprintf(f, " %lu", num_nodes_with_color.size());
262  for (int i = 0; i < num_nodes_with_color.size() - 1; ++i) {
263  absl::FPrintF(f, " %d", static_cast<int64>(num_nodes_with_color[i]));
264  }
265  }
266  fprintf(f, "\n");
267 
268  for (const typename Graph::NodeIndex node : graph.AllNodes()) {
269  for (const typename Graph::ArcIndex arc : graph.OutgoingArcs(node)) {
270  const typename Graph::NodeIndex head = graph.Head(arc);
271  if (directed || head >= node) {
272  absl::FPrintF(f, "%d %d\n", static_cast<int64>(node),
273  static_cast<uint64>(head));
274  }
275  }
276  }
277  if (fclose(f) != 0) {
278  return absl::Status(absl::StatusCode::kInternal,
279  "Could not close file '" + filename + "'");
280  }
281  return ::absl::OkStatus();
282 }
283 
284 } // namespace util
285 
286 #endif // UTIL_GRAPH_IO_H_
IntegerRange< NodeIndex > AllNodes() const
Definition: graph.h:929
@ PRINT_GRAPH_ADJACENCY_LISTS
Definition: io.h:44
int32 NodeIndex
Definition: graph.h:189
ListGraph Graph
Definition: graph.h:2354
std::string GraphToString(const Graph &graph, GraphToStringFormat format)
Definition: io.h:107
GraphToStringFormat
Definition: io.h:38
absl::Status WriteGraphToFile(const Graph &graph, const std::string &filename, bool directed, const std::vector< int > &num_nodes_with_color)
Definition: io.h:224
ArcIndexType num_arcs() const
Definition: graph.h:204
NodeIndexType Head(ArcIndexType arc) const
Definition: graph.h:1111
@ PRINT_GRAPH_ADJACENCY_LISTS_SORTED
Definition: io.h:47
int32 ArcIndex
Definition: graph.h:190
BeginEndWrapper< OutgoingArcIterator > OutgoingArcs(NodeIndexType node) const
@ PRINT_GRAPH_ARCS
Definition: io.h:40
absl::StatusOr< Graph * > ReadGraphFile(const std::string &filename, bool directed, std::vector< int > *num_nodes_with_color_or_null)
Definition: io.h:132
Definition: graph.h:297
NodeIndexType num_nodes() const
Definition: graph.h:201