OR-Tools  9.3
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"
32#include "ortools/graph/graph.h"
34
35namespace 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<>.
52template <class Graph>
53bool GraphHasSelfArcs(const Graph& graph);
54template <class Graph>
55bool GraphHasDuplicateArcs(const Graph& graph);
56template <class Graph>
57bool GraphIsSymmetric(const Graph& graph);
58template <class Graph>
59bool GraphIsWeaklyConnected(const Graph& graph);
60
61// Returns a fresh copy of a given graph.
62template <class Graph>
63std::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.
70template <class Graph>
71std::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.
84template <class Graph>
85std::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]) { ... }
99template <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().
133template <class Graph>
134std::vector<int> GetWeaklyConnectedComponents(const Graph& graph) {
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.
142bool IsSubsetOf0N(const std::vector<int>& v, int n);
143
144// Returns true iff the given vector is a permutation of [0..size()-1].
145inline 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.
150template <class Graph>
151std::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.
163template <class Graph>
164void RemoveCyclesFromPath(const Graph& graph, std::vector<int>* arc_path);
165
166// Returns true iff the given path contains a cycle.
167template <class Graph>
168bool 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.
185template <class Graph>
186std::vector<int> ComputeOnePossibleReverseArcMapping(const Graph& graph,
187 bool die_if_not_symmetric);
188
189// Implementations of the templated methods.
190
191template <class Graph>
192bool 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
199template <class Graph>
200bool 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
217template <class Graph>
218bool 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
246template <class Graph>
247bool 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
262template <class Graph>
263std::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
275template <class Graph>
276std::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
294template <class Graph>
295std::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
328template <class Graph>
329std::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
350template <class Graph>
351void 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
375template <class Graph>
376bool 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
386template <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_
int64_t max
Definition: alldiff_cst.cc:140
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
#define DCHECK(condition)
Definition: base/logging.h:890
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:891
bool AddEdge(int node1, int node2)
IntegerRange< ArcIndex > AllForwardArcs() const
Definition: graph.h:943
ArcIndexType num_arcs() const
Definition: graph.h:207
NodeIndexType num_nodes() const
Definition: graph.h:204
IntegerRange< NodeIndex > AllNodes() const
Definition: graph.h:937
NodeIndexType Tail(ArcIndexType arc) const
Definition: graph.h:1112
NodeIndexType Head(ArcIndexType arc) const
Definition: graph.h:1119
BeginEndWrapper< OutgoingArcIterator > OutgoingArcs(NodeIndexType node) const
void Build()
Definition: graph.h:437
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)
Definition: graph.h:1287
NodeIndexType Head(ArcIndexType arc) const
Definition: graph.h:1315
BeginEndWrapper< OutgoingArcIterator > OutgoingArcs(NodeIndexType node) const
AdjacencyListIterator(const Graph &graph, ArcIterator &&arc_it)
Definition: graph/util.h:108
UndirectedAdjacencyListsOfDirectedGraph(const Graph &graph)
Definition: graph/util.h:102
Graph::OutgoingOrOppositeIncomingArcIterator ArcIterator
Definition: graph/util.h:105
BeginEndWrapper< AdjacencyListIterator > operator[](int node) const
Definition: graph/util.h:120
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:387
ListGraph Graph
Definition: graph.h:2362
bool PathHasCycle(const Graph &graph, const std::vector< int > &arc_path)
Definition: graph/util.h:376
bool IsSubsetOf0N(const std::vector< int > &v, int n)
Definition: graph/util.cc:18
std::vector< int > GetConnectedComponents(int num_nodes, const UndirectedGraph &graph)
void RemoveCyclesFromPath(const Graph &graph, std::vector< int > *arc_path)
Definition: graph/util.h:351
bool GraphHasDuplicateArcs(const Graph &graph)
Definition: graph/util.h:200
std::vector< int > GetWeaklyConnectedComponents(const Graph &graph)
Definition: graph/util.h:134
std::unique_ptr< Graph > RemoveSelfArcsAndDuplicateArcs(const Graph &graph)
Definition: graph/util.h:329
std::unique_ptr< Graph > GetSubgraphOfNodes(const Graph &graph, const std::vector< int > &nodes)
Definition: graph/util.h:295
bool GraphHasSelfArcs(const Graph &graph)
Definition: graph/util.h:192
bool GraphIsSymmetric(const Graph &graph)
Definition: graph/util.h:218
std::unique_ptr< Graph > RemapGraph(const Graph &graph, const std::vector< int > &new_node_index)
Definition: graph/util.h:276
bool GraphIsWeaklyConnected(const Graph &graph)
Definition: graph/util.h:247
bool IsValidPermutation(const std::vector< int > &v)
Definition: graph/util.h:145
std::unique_ptr< Graph > CopyGraph(const Graph &graph)
Definition: graph/util.h:263
int64_t tail
int64_t head
int nodes