Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/networkx/algorithms/bridges.py: 29%
48 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-20 07:00 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-10-20 07:00 +0000
1"""Bridge-finding algorithms."""
2from itertools import chain
4import networkx as nx
5from networkx.utils import not_implemented_for
7__all__ = ["bridges", "has_bridges", "local_bridges"]
10@not_implemented_for("directed")
11@nx._dispatch
12def bridges(G, root=None):
13 """Generate all bridges in a graph.
15 A *bridge* in a graph is an edge whose removal causes the number of
16 connected components of the graph to increase. Equivalently, a bridge is an
17 edge that does not belong to any cycle. Bridges are also known as cut-edges,
18 isthmuses, or cut arcs.
20 Parameters
21 ----------
22 G : undirected graph
24 root : node (optional)
25 A node in the graph `G`. If specified, only the bridges in the
26 connected component containing this node will be returned.
28 Yields
29 ------
30 e : edge
31 An edge in the graph whose removal disconnects the graph (or
32 causes the number of connected components to increase).
34 Raises
35 ------
36 NodeNotFound
37 If `root` is not in the graph `G`.
39 NetworkXNotImplemented
40 If `G` is a directed graph.
42 Examples
43 --------
44 The barbell graph with parameter zero has a single bridge:
46 >>> G = nx.barbell_graph(10, 0)
47 >>> list(nx.bridges(G))
48 [(9, 10)]
50 Notes
51 -----
52 This is an implementation of the algorithm described in [1]_. An edge is a
53 bridge if and only if it is not contained in any chain. Chains are found
54 using the :func:`networkx.chain_decomposition` function.
56 The algorithm described in [1]_ requires a simple graph. If the provided
57 graph is a multigraph, we convert it to a simple graph and verify that any
58 bridges discovered by the chain decomposition algorithm are not multi-edges.
60 Ignoring polylogarithmic factors, the worst-case time complexity is the
61 same as the :func:`networkx.chain_decomposition` function,
62 $O(m + n)$, where $n$ is the number of nodes in the graph and $m$ is
63 the number of edges.
65 References
66 ----------
67 .. [1] https://en.wikipedia.org/wiki/Bridge_%28graph_theory%29#Bridge-Finding_with_Chain_Decompositions
68 """
69 multigraph = G.is_multigraph()
70 H = nx.Graph(G) if multigraph else G
71 chains = nx.chain_decomposition(H, root=root)
72 chain_edges = set(chain.from_iterable(chains))
73 H_copy = H.copy()
74 if root is not None:
75 H = H.subgraph(nx.node_connected_component(H, root)).copy()
76 for u, v in H.edges():
77 if (u, v) not in chain_edges and (v, u) not in chain_edges:
78 if multigraph and len(G[u][v]) > 1:
79 continue
80 yield u, v
83@not_implemented_for("directed")
84@nx._dispatch
85def has_bridges(G, root=None):
86 """Decide whether a graph has any bridges.
88 A *bridge* in a graph is an edge whose removal causes the number of
89 connected components of the graph to increase.
91 Parameters
92 ----------
93 G : undirected graph
95 root : node (optional)
96 A node in the graph `G`. If specified, only the bridges in the
97 connected component containing this node will be considered.
99 Returns
100 -------
101 bool
102 Whether the graph (or the connected component containing `root`)
103 has any bridges.
105 Raises
106 ------
107 NodeNotFound
108 If `root` is not in the graph `G`.
110 NetworkXNotImplemented
111 If `G` is a directed graph.
113 Examples
114 --------
115 The barbell graph with parameter zero has a single bridge::
117 >>> G = nx.barbell_graph(10, 0)
118 >>> nx.has_bridges(G)
119 True
121 On the other hand, the cycle graph has no bridges::
123 >>> G = nx.cycle_graph(5)
124 >>> nx.has_bridges(G)
125 False
127 Notes
128 -----
129 This implementation uses the :func:`networkx.bridges` function, so
130 it shares its worst-case time complexity, $O(m + n)$, ignoring
131 polylogarithmic factors, where $n$ is the number of nodes in the
132 graph and $m$ is the number of edges.
134 """
135 try:
136 next(bridges(G, root=root))
137 except StopIteration:
138 return False
139 else:
140 return True
143@not_implemented_for("multigraph")
144@not_implemented_for("directed")
145@nx._dispatch(edge_attrs="weight")
146def local_bridges(G, with_span=True, weight=None):
147 """Iterate over local bridges of `G` optionally computing the span
149 A *local bridge* is an edge whose endpoints have no common neighbors.
150 That is, the edge is not part of a triangle in the graph.
152 The *span* of a *local bridge* is the shortest path length between
153 the endpoints if the local bridge is removed.
155 Parameters
156 ----------
157 G : undirected graph
159 with_span : bool
160 If True, yield a 3-tuple `(u, v, span)`
162 weight : function, string or None (default: None)
163 If function, used to compute edge weights for the span.
164 If string, the edge data attribute used in calculating span.
165 If None, all edges have weight 1.
167 Yields
168 ------
169 e : edge
170 The local bridges as an edge 2-tuple of nodes `(u, v)` or
171 as a 3-tuple `(u, v, span)` when `with_span is True`.
173 Raises
174 ------
175 NetworkXNotImplemented
176 If `G` is a directed graph or multigraph.
178 Examples
179 --------
180 A cycle graph has every edge a local bridge with span N-1.
182 >>> G = nx.cycle_graph(9)
183 >>> (0, 8, 8) in set(nx.local_bridges(G))
184 True
185 """
186 if with_span is not True:
187 for u, v in G.edges:
188 if not (set(G[u]) & set(G[v])):
189 yield u, v
190 else:
191 wt = nx.weighted._weight_function(G, weight)
192 for u, v in G.edges:
193 if not (set(G[u]) & set(G[v])):
194 enodes = {u, v}
196 def hide_edge(n, nbr, d):
197 if n not in enodes or nbr not in enodes:
198 return wt(n, nbr, d)
199 return None
201 try:
202 span = nx.shortest_path_length(G, u, v, weight=hide_edge)
203 yield u, v, span
204 except nx.NetworkXNoPath:
205 yield u, v, float("inf")