C++ Reference

C++ Reference: Graph

util.h
Go to the documentation of this file.
1 // Copyright 2010-2021 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 utilities for the Graph classes in ./graph.h.
15 
16 #ifndef UTIL_GRAPH_UTIL_H_
17 #define UTIL_GRAPH_UTIL_H_
18 
19 #include <algorithm>
20 #include <cstdint>
21 #include <map>
22 #include <memory>
23 #include <set>
24 #include <string>
25 #include <vector>
26 
27 #include "absl/container/flat_hash_map.h"
28 #include "absl/container/inlined_vector.h"
29 #include "ortools/base/hash.h"
30 #include "ortools/base/map_util.h"
32 #include "ortools/graph/graph.h"
34 
35 namespace util {
36 
37 // Here's a set of simple diagnosis tools. Notes:
38 // - A self-arc is an arc from a node to itself.
39 // - We say that an arc A->B is duplicate when there is another arc A->B in the
40 // same graph.
41 // - A graph is said "weakly connected" if it is connected when considering all
42 // arcs as undirected edges.
43 // - A graph is said "symmetric" iff for all (a, b), the number of arcs a->b
44 // is equal to the number of arcs b->a.
45 //
46 // All these diagnosis work in O(graph size), since the inverse Ackerman
47 // function is <= 5 for all practical instances, and are very fast.
48 //
49 // If the graph is a "static" kind, they must be finalized, except for
50 // GraphHasSelfArcs() and GraphIsWeaklyConnected() which also support
51 // non-finalized StaticGraph<>.
52 template <class Graph>
53 bool GraphHasSelfArcs(const Graph& graph);
54 template <class Graph>
55 bool GraphHasDuplicateArcs(const Graph& graph);
56 template <class Graph>
57 bool GraphIsSymmetric(const Graph& graph);
58 template <class Graph>
59 bool GraphIsWeaklyConnected(const Graph& graph);
60 
61 // Returns a fresh copy of a given graph.
62 template <class Graph>
63 std::unique_ptr<Graph> CopyGraph(const Graph& graph);
64 
65 // Creates a remapped copy of graph "graph", where node i becomes node
66 // new_node_index[i].
67 // "new_node_index" must be a valid permutation of [0..num_nodes-1] or the
68 // behavior is undefined (it may die).
69 // Note that you can call IsValidPermutation() to check it yourself.
70 template <class Graph>
71 std::unique_ptr<Graph> RemapGraph(const Graph& graph,
72  const std::vector<int>& new_node_index);
73 
74 // Gets the induced subgraph of "graph" restricted to the nodes in "nodes":
75 // the resulting graph will have exactly nodes.size() nodes, and its
76 // node #0 will be the former graph's node #nodes[0], etc.
77 // See https://en.wikipedia.org/wiki/Induced_subgraph .
78 // The "nodes" must be a valid subset (no repetitions) of
79 // [0..graph.num_nodes()-1], or the behavior is undefined (it may die).
80 // Note that you can call IsSubsetOf0N() to check it yourself.
81 //
82 // Current complexity: O(num old nodes + num new arcs). It could easily
83 // be done in O(num new nodes + num new arcs) but with a higher constant.
84 template <class Graph>
85 std::unique_ptr<Graph> GetSubgraphOfNodes(const Graph& graph,
86  const std::vector<int>& nodes);
87 
88 // This can be used to view a directed graph (that supports reverse arcs)
89 // from graph.h as un undirected graph: operator[](node) returns a
90 // pseudo-container that iterates over all nodes adjacent to "node" (from
91 // outgoing or incoming arcs).
92 // CAVEAT: Self-arcs (aka loops) will appear twice.
93 //
94 // Example:
95 // ReverseArcsStaticGraph<> dgraph;
96 // ...
97 // UndirectedAdjacencyListsOfDirectedGraph<decltype(dgraph)> ugraph(dgraph);
98 // for (int neighbor_of_node_42 : ugraph[42]) { ... }
99 template <class Graph>
101  public:
103  : graph_(graph) {}
104 
105  typedef typename Graph::OutgoingOrOppositeIncomingArcIterator ArcIterator;
107  public:
108  explicit AdjacencyListIterator(const Graph& graph, ArcIterator&& arc_it)
109  : ArcIterator(arc_it), graph_(graph) {}
110  // Overwrite operator* to return the heads of the arcs.
111  typename Graph::NodeIndex operator*() const {
112  return graph_.Head(ArcIterator::operator*());
113  }
114 
115  private:
116  const Graph& graph_;
117  };
118 
119  // Returns a pseudo-container of all the nodes adjacent to "node".
121  const auto& arc_range = graph_.OutgoingOrOppositeIncomingArcs(node);
122  return {AdjacencyListIterator(graph_, arc_range.begin()),
123  AdjacencyListIterator(graph_, arc_range.end())};
124  }
125 
126  private:
127  const Graph& graph_;
128 };
129 
130 // Computes the weakly connected components of a directed graph that
131 // provides the OutgoingOrOppositeIncomingArcs() API, and returns them
132 // as a mapping from node to component index. See GetConnectedComponens().
133 template <class Graph>
134 std::vector<int> GetWeaklyConnectedComponents(const Graph& graph) {
135  return GetConnectedComponents(
137 }
138 
139 // Returns true iff the given vector is a subset of [0..n-1], i.e.
140 // all elements i are such that 0 <= i < n and no two elements are equal.
141 // "n" must be >= 0 or the result is undefined.
142 bool IsSubsetOf0N(const std::vector<int>& v, int n);
143 
144 // Returns true iff the given vector is a permutation of [0..size()-1].
145 inline bool IsValidPermutation(const std::vector<int>& v) {
146  return IsSubsetOf0N(v, v.size());
147 }
148 
149 // Returns a copy of "graph", without self-arcs and duplicate arcs.
150 template <class Graph>
151 std::unique_ptr<Graph> RemoveSelfArcsAndDuplicateArcs(const Graph& graph);
152 
153 // Given an arc path, changes it to a sub-path with the same source and
154 // destination but without any cycle. Nothing happen if the path was already
155 // without cycle.
156 //
157 // The graph class should support Tail(arc) and Head(arc). They should both
158 // return an integer representing the corresponding tail/head of the passed arc.
159 //
160 // TODO(user): In some cases, there is more than one possible solution. We could
161 // take some arc costs and return the cheapest path instead. Or return the
162 // shortest path in term of number of arcs.
163 template <class Graph>
164 void RemoveCyclesFromPath(const Graph& graph, std::vector<int>* arc_path);
165 
166 // Returns true iff the given path contains a cycle.
167 template <class Graph>
168 bool PathHasCycle(const Graph& graph, const std::vector<int>& arc_path);
169 
170 // Returns a vector representing a mapping from arcs to arcs such that each arc
171 // is mapped to another arc with its (tail, head) flipped, if such an arc
172 // exists (otherwise it is mapped to -1).
173 // If the graph is symmetric, the returned mapping is bijective and reflexive,
174 // i.e. out[out[arc]] = arc for all "arc", where "out" is the returned vector.
175 // If "die_if_not_symmetric" is true, this function CHECKs() that the graph
176 // is symmetric.
177 //
178 // Self-arcs are always mapped to themselves.
179 //
180 // Note that since graphs may have multi-arcs, the mapping isn't necessarily
181 // unique, hence the function name.
182 //
183 // PERFORMANCE: If you see this function taking too much memory and/or too much
184 // time, reach out to viger@: one could halve the memory usage and speed it up.
185 template <class Graph>
186 std::vector<int> ComputeOnePossibleReverseArcMapping(const Graph& graph,
187  bool die_if_not_symmetric);
188 
189 // Implementations of the templated methods.
190 
191 template <class Graph>
192 bool GraphHasSelfArcs(const Graph& graph) {
193  for (const auto arc : graph.AllForwardArcs()) {
194  if (graph.Tail(arc) == graph.Head(arc)) return true;
195  }
196  return false;
197 }
198 
199 template <class Graph>
200 bool GraphHasDuplicateArcs(const Graph& graph) {
201  typedef typename Graph::ArcIndex ArcIndex;
202  typedef typename Graph::NodeIndex NodeIndex;
203  std::vector<bool> tmp_node_mask(graph.num_nodes(), false);
204  for (const NodeIndex tail : graph.AllNodes()) {
205  for (const ArcIndex arc : graph.OutgoingArcs(tail)) {
206  const NodeIndex head = graph.Head(arc);
207  if (tmp_node_mask[head]) return true;
208  tmp_node_mask[head] = true;
209  }
210  for (const ArcIndex arc : graph.OutgoingArcs(tail)) {
211  tmp_node_mask[graph.Head(arc)] = false;
212  }
213  }
214  return false;
215 }
216 
217 template <class Graph>
218 bool GraphIsSymmetric(const Graph& graph) {
219  typedef typename Graph::NodeIndex NodeIndex;
220  typedef typename Graph::ArcIndex ArcIndex;
221  // Create a reverse copy of the graph.
222  StaticGraph<NodeIndex, ArcIndex> reverse_graph(graph.num_nodes(),
223  graph.num_arcs());
224  for (const NodeIndex node : graph.AllNodes()) {
225  for (const ArcIndex arc : graph.OutgoingArcs(node)) {
226  reverse_graph.AddArc(graph.Head(arc), node);
227  }
228  }
229  reverse_graph.Build();
230  // Compare the graph to its reverse, one adjacency list at a time.
231  std::vector<ArcIndex> count(graph.num_nodes(), 0);
232  for (const NodeIndex node : graph.AllNodes()) {
233  for (const ArcIndex arc : graph.OutgoingArcs(node)) {
234  ++count[graph.Head(arc)];
235  }
236  for (const ArcIndex arc : reverse_graph.OutgoingArcs(node)) {
237  if (--count[reverse_graph.Head(arc)] < 0) return false;
238  }
239  for (const ArcIndex arc : graph.OutgoingArcs(node)) {
240  if (count[graph.Head(arc)] != 0) return false;
241  }
242  }
243  return true;
244 }
245 
246 template <class Graph>
247 bool GraphIsWeaklyConnected(const Graph& graph) {
248  typedef typename Graph::NodeIndex NodeIndex;
249  static_assert(std::numeric_limits<NodeIndex>::max() <= INT_MAX,
250  "GraphIsWeaklyConnected() isn't yet implemented for graphs"
251  " that support more than INT_MAX nodes. Reach out to"
252  " or-core-team@ if you need this.");
253  if (graph.num_nodes() == 0) return true;
255  union_find.SetNumberOfNodes(graph.num_nodes());
256  for (typename Graph::ArcIndex arc = 0; arc < graph.num_arcs(); ++arc) {
257  union_find.AddEdge(graph.Tail(arc), graph.Head(arc));
258  }
259  return union_find.GetNumberOfComponents() == 1;
260 }
261 
262 template <class Graph>
263 std::unique_ptr<Graph> CopyGraph(const Graph& graph) {
264  std::unique_ptr<Graph> new_graph(
265  new Graph(graph.num_nodes(), graph.num_arcs()));
266  for (const auto node : graph.AllNodes()) {
267  for (const auto arc : graph.OutgoingArcs(node)) {
268  new_graph->AddArc(node, graph.Head(arc));
269  }
270  }
271  new_graph->Build();
272  return new_graph;
273 }
274 
275 template <class Graph>
276 std::unique_ptr<Graph> RemapGraph(const Graph& old_graph,
277  const std::vector<int>& new_node_index) {
278  DCHECK(IsValidPermutation(new_node_index)) << "Invalid permutation";
279  const int num_nodes = old_graph.num_nodes();
280  CHECK_EQ(new_node_index.size(), num_nodes);
281  std::unique_ptr<Graph> new_graph(new Graph(num_nodes, old_graph.num_arcs()));
282  typedef typename Graph::NodeIndex NodeIndex;
283  typedef typename Graph::ArcIndex ArcIndex;
284  for (const NodeIndex node : old_graph.AllNodes()) {
285  for (const ArcIndex arc : old_graph.OutgoingArcs(node)) {
286  new_graph->AddArc(new_node_index[node],
287  new_node_index[old_graph.Head(arc)]);
288  }
289  }
290  new_graph->Build();
291  return new_graph;
292 }
293 
294 template <class Graph>
295 std::unique_ptr<Graph> GetSubgraphOfNodes(const Graph& old_graph,
296  const std::vector<int>& nodes) {
297  typedef typename Graph::NodeIndex NodeIndex;
298  typedef typename Graph::ArcIndex ArcIndex;
299  DCHECK(IsSubsetOf0N(nodes, old_graph.num_nodes())) << "Invalid subset";
300  std::vector<NodeIndex> new_node_index(old_graph.num_nodes(), -1);
301  for (NodeIndex new_index = 0; new_index < nodes.size(); ++new_index) {
302  new_node_index[nodes[new_index]] = new_index;
303  }
304  // Do a first pass to count the arcs, so that we don't allocate more memory
305  // than needed.
306  ArcIndex num_arcs = 0;
307  for (const NodeIndex node : nodes) {
308  for (const ArcIndex arc : old_graph.OutgoingArcs(node)) {
309  if (new_node_index[old_graph.Head(arc)] != -1) ++num_arcs;
310  }
311  }
312  // A second pass where we actually copy the subgraph.
313  // NOTE(user): there might seem to be a bit of duplication with RemapGraph(),
314  // but there is a key difference: the loop below only iterates on "nodes",
315  // which could be much smaller than all the graph's nodes.
316  std::unique_ptr<Graph> new_graph(new Graph(nodes.size(), num_arcs));
317  for (NodeIndex new_tail = 0; new_tail < nodes.size(); ++new_tail) {
318  const NodeIndex old_tail = nodes[new_tail];
319  for (const ArcIndex arc : old_graph.OutgoingArcs(old_tail)) {
320  const NodeIndex new_head = new_node_index[old_graph.Head(arc)];
321  if (new_head != -1) new_graph->AddArc(new_tail, new_head);
322  }
323  }
324  new_graph->Build();
325  return new_graph;
326 }
327 
328 template <class Graph>
329 std::unique_ptr<Graph> RemoveSelfArcsAndDuplicateArcs(const Graph& graph) {
330  std::unique_ptr<Graph> g(new Graph(graph.num_nodes(), graph.num_arcs()));
331  typedef typename Graph::ArcIndex ArcIndex;
332  typedef typename Graph::NodeIndex NodeIndex;
333  std::vector<bool> tmp_node_mask(graph.num_nodes(), false);
334  for (const NodeIndex tail : graph.AllNodes()) {
335  for (const ArcIndex arc : graph.OutgoingArcs(tail)) {
336  const NodeIndex head = graph.Head(arc);
337  if (head != tail && !tmp_node_mask[head]) {
338  tmp_node_mask[head] = true;
339  g->AddArc(tail, head);
340  }
341  }
342  for (const ArcIndex arc : graph.OutgoingArcs(tail)) {
343  tmp_node_mask[graph.Head(arc)] = false;
344  }
345  }
346  g->Build();
347  return g;
348 }
349 
350 template <class Graph>
351 void RemoveCyclesFromPath(const Graph& graph, std::vector<int>* arc_path) {
352  if (arc_path->empty()) return;
353 
354  // This maps each node to the latest arc in the given path that leaves it.
355  std::map<int, int> last_arc_leaving_node;
356  for (const int arc : *arc_path) last_arc_leaving_node[graph.Tail(arc)] = arc;
357 
358  // Special case for the destination.
359  // Note that this requires that -1 is not a valid arc of Graph.
360  last_arc_leaving_node[graph.Head(arc_path->back())] = -1;
361 
362  // Reconstruct the path by starting at the source and then following the
363  // "next" arcs. We override the given arc_path at the same time.
364  int node = graph.Tail(arc_path->front());
365  int new_size = 0;
366  while (new_size < arc_path->size()) { // To prevent cycle on bad input.
367  const int arc = gtl::FindOrDie(last_arc_leaving_node, node);
368  if (arc == -1) break;
369  (*arc_path)[new_size++] = arc;
370  node = graph.Head(arc);
371  }
372  arc_path->resize(new_size);
373 }
374 
375 template <class Graph>
376 bool PathHasCycle(const Graph& graph, const std::vector<int>& arc_path) {
377  if (arc_path.empty()) return false;
378  std::set<int> seen;
379  seen.insert(graph.Tail(arc_path.front()));
380  for (const int arc : arc_path) {
381  if (!gtl::InsertIfNotPresent(&seen, graph.Head(arc))) return true;
382  }
383  return false;
384 }
385 
386 template <class Graph>
388  const Graph& graph, bool die_if_not_symmetric) {
389  std::vector<int> reverse_arc(graph.num_arcs(), -1);
390  // We need a multi-map since a given (tail,head) may appear several times.
391  // NOTE(user): It's free, in terms of space, to use InlinedVector<int, 4>
392  // rather than std::vector<int>. See go/inlined-vector-size.
393  absl::flat_hash_map<std::pair</*tail*/ int, /*head*/ int>,
394  absl::InlinedVector<int, 4>>
395  arc_map;
396 
397  for (int arc = 0; arc < graph.num_arcs(); ++arc) {
398  const int tail = graph.Tail(arc);
399  const int head = graph.Head(arc);
400  if (tail == head) {
401  // Special case: directly map any self-arc to itself.
402  reverse_arc[arc] = arc;
403  continue;
404  }
405  // Lookup for the reverse arc of the current one...
406  auto it = arc_map.find({head, tail});
407  if (it != arc_map.end()) {
408  // Found a reverse arc! Store the mapping and remove the
409  // reverse arc from the map.
410  reverse_arc[arc] = it->second.back();
411  reverse_arc[it->second.back()] = arc;
412  if (it->second.size() > 1) {
413  it->second.pop_back();
414  } else {
415  arc_map.erase(it);
416  }
417  } else {
418  // Reverse arc not in the map. Add the current arc to the map.
419  arc_map[{tail, head}].push_back(arc);
420  }
421  }
422  // Algorithm check, for debugging.
423  if (DEBUG_MODE) {
424  int64_t num_unmapped_arcs = 0;
425  for (const auto& p : arc_map) {
426  num_unmapped_arcs += p.second.size();
427  }
428  DCHECK_EQ(std::count(reverse_arc.begin(), reverse_arc.end(), -1),
429  num_unmapped_arcs);
430  }
431  if (die_if_not_symmetric) {
432  CHECK_EQ(arc_map.size(), 0)
433  << "The graph is not symmetric: " << arc_map.size() << " of "
434  << graph.num_arcs() << " arcs did not have a reverse.";
435  }
436  return reverse_arc;
437 }
438 
439 } // namespace util
440 
441 #endif // UTIL_GRAPH_UTIL_H_
BeginEndWrapper< OutgoingArcIterator > OutgoingArcs(NodeIndexType node) const
bool IsValidPermutation(const std::vector< int > &v)
Definition: util.h:145
NodeIndexType Head(ArcIndexType arc) const
Definition: graph.h:1119
std::unique_ptr< Graph > RemoveSelfArcsAndDuplicateArcs(const Graph &graph)
Definition: util.h:329
bool PathHasCycle(const Graph &graph, const std::vector< int > &arc_path)
Definition: util.h:376
AdjacencyListIterator(const Graph &graph, ArcIterator &&arc_it)
Definition: util.h:108
IntegerRange< ArcIndex > AllForwardArcs() const
Definition: graph.h:943
ArcIndexType num_arcs() const
Definition: graph.h:207
BeginEndWrapper< AdjacencyListIterator > operator[](int node) const
Definition: util.h:120
NodeIndexType Tail(ArcIndexType arc) const
Definition: graph.h:1112
std::vector< int > ComputeOnePossibleReverseArcMapping(const Graph &graph, bool die_if_not_symmetric)
Definition: util.h:387
void RemoveCyclesFromPath(const Graph &graph, std::vector< int > *arc_path)
Definition: util.h:351
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)
Definition: graph.h:1287
bool GraphHasSelfArcs(const Graph &graph)
Definition: util.h:192
std::vector< int > GetConnectedComponents(int num_nodes, const UndirectedGraph &graph)
bool GraphIsSymmetric(const Graph &graph)
Definition: util.h:218
bool IsSubsetOf0N(const std::vector< int > &v, int n)
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)
Definition: graph.h:1142
bool GraphHasDuplicateArcs(const Graph &graph)
Definition: util.h:200
std::unique_ptr< Graph > CopyGraph(const Graph &graph)
Definition: util.h:263
void Build()
Definition: graph.h:342
void SetNumberOfNodes(int num_nodes)
ListGraph Graph
Definition: graph.h:2362
Graph::OutgoingOrOppositeIncomingArcIterator ArcIterator
Definition: util.h:105
IntegerRange< NodeIndex > AllNodes() const
Definition: graph.h:937
std::vector< int > GetWeaklyConnectedComponents(const Graph &graph)
Definition: util.h:134
NodeIndexType num_nodes() const
Definition: graph.h:204
std::unique_ptr< Graph > RemapGraph(const Graph &graph, const std::vector< int > &new_node_index)
Definition: util.h:276
bool AddEdge(int node1, int node2)
UndirectedAdjacencyListsOfDirectedGraph(const Graph &graph)
Definition: util.h:102
bool GraphIsWeaklyConnected(const Graph &graph)
Definition: util.h:247
std::unique_ptr< Graph > GetSubgraphOfNodes(const Graph &graph, const std::vector< int > &nodes)
Definition: util.h:295