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