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

1"""Bridge-finding algorithms.""" 

2from itertools import chain 

3 

4import networkx as nx 

5from networkx.utils import not_implemented_for 

6 

7__all__ = ["bridges", "has_bridges", "local_bridges"] 

8 

9 

10@not_implemented_for("directed") 

11@nx._dispatch 

12def bridges(G, root=None): 

13 """Generate all bridges in a graph. 

14 

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. 

19 

20 Parameters 

21 ---------- 

22 G : undirected graph 

23 

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. 

27 

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). 

33 

34 Raises 

35 ------ 

36 NodeNotFound 

37 If `root` is not in the graph `G`. 

38 

39 NetworkXNotImplemented 

40 If `G` is a directed graph. 

41 

42 Examples 

43 -------- 

44 The barbell graph with parameter zero has a single bridge: 

45 

46 >>> G = nx.barbell_graph(10, 0) 

47 >>> list(nx.bridges(G)) 

48 [(9, 10)] 

49 

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. 

55 

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. 

59 

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. 

64 

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 

81 

82 

83@not_implemented_for("directed") 

84@nx._dispatch 

85def has_bridges(G, root=None): 

86 """Decide whether a graph has any bridges. 

87 

88 A *bridge* in a graph is an edge whose removal causes the number of 

89 connected components of the graph to increase. 

90 

91 Parameters 

92 ---------- 

93 G : undirected graph 

94 

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. 

98 

99 Returns 

100 ------- 

101 bool 

102 Whether the graph (or the connected component containing `root`) 

103 has any bridges. 

104 

105 Raises 

106 ------ 

107 NodeNotFound 

108 If `root` is not in the graph `G`. 

109 

110 NetworkXNotImplemented 

111 If `G` is a directed graph. 

112 

113 Examples 

114 -------- 

115 The barbell graph with parameter zero has a single bridge:: 

116 

117 >>> G = nx.barbell_graph(10, 0) 

118 >>> nx.has_bridges(G) 

119 True 

120 

121 On the other hand, the cycle graph has no bridges:: 

122 

123 >>> G = nx.cycle_graph(5) 

124 >>> nx.has_bridges(G) 

125 False 

126 

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. 

133 

134 """ 

135 try: 

136 next(bridges(G, root=root)) 

137 except StopIteration: 

138 return False 

139 else: 

140 return True 

141 

142 

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 

148 

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. 

151 

152 The *span* of a *local bridge* is the shortest path length between 

153 the endpoints if the local bridge is removed. 

154 

155 Parameters 

156 ---------- 

157 G : undirected graph 

158 

159 with_span : bool 

160 If True, yield a 3-tuple `(u, v, span)` 

161 

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. 

166 

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`. 

172 

173 Raises 

174 ------ 

175 NetworkXNotImplemented 

176 If `G` is a directed graph or multigraph. 

177 

178 Examples 

179 -------- 

180 A cycle graph has every edge a local bridge with span N-1. 

181 

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} 

195 

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 

200 

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")