Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/networkx/algorithms/flow/gomory_hu.py: 24%
38 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"""
2Gomory-Hu tree of undirected Graphs.
3"""
4import networkx as nx
5from networkx.utils import not_implemented_for
7from .edmondskarp import edmonds_karp
8from .utils import build_residual_network
10default_flow_func = edmonds_karp
12__all__ = ["gomory_hu_tree"]
15@not_implemented_for("directed")
16@nx._dispatch(edge_attrs={"capacity": float("inf")})
17def gomory_hu_tree(G, capacity="capacity", flow_func=None):
18 r"""Returns the Gomory-Hu tree of an undirected graph G.
20 A Gomory-Hu tree of an undirected graph with capacities is a
21 weighted tree that represents the minimum s-t cuts for all s-t
22 pairs in the graph.
24 It only requires `n-1` minimum cut computations instead of the
25 obvious `n(n-1)/2`. The tree represents all s-t cuts as the
26 minimum cut value among any pair of nodes is the minimum edge
27 weight in the shortest path between the two nodes in the
28 Gomory-Hu tree.
30 The Gomory-Hu tree also has the property that removing the
31 edge with the minimum weight in the shortest path between
32 any two nodes leaves two connected components that form
33 a partition of the nodes in G that defines the minimum s-t
34 cut.
36 See Examples section below for details.
38 Parameters
39 ----------
40 G : NetworkX graph
41 Undirected graph
43 capacity : string
44 Edges of the graph G are expected to have an attribute capacity
45 that indicates how much flow the edge can support. If this
46 attribute is not present, the edge is considered to have
47 infinite capacity. Default value: 'capacity'.
49 flow_func : function
50 Function to perform the underlying flow computations. Default value
51 :func:`edmonds_karp`. This function performs better in sparse graphs
52 with right tailed degree distributions.
53 :func:`shortest_augmenting_path` will perform better in denser
54 graphs.
56 Returns
57 -------
58 Tree : NetworkX graph
59 A NetworkX graph representing the Gomory-Hu tree of the input graph.
61 Raises
62 ------
63 NetworkXNotImplemented
64 Raised if the input graph is directed.
66 NetworkXError
67 Raised if the input graph is an empty Graph.
69 Examples
70 --------
71 >>> G = nx.karate_club_graph()
72 >>> nx.set_edge_attributes(G, 1, "capacity")
73 >>> T = nx.gomory_hu_tree(G)
74 >>> # The value of the minimum cut between any pair
75 ... # of nodes in G is the minimum edge weight in the
76 ... # shortest path between the two nodes in the
77 ... # Gomory-Hu tree.
78 ... def minimum_edge_weight_in_shortest_path(T, u, v):
79 ... path = nx.shortest_path(T, u, v, weight="weight")
80 ... return min((T[u][v]["weight"], (u, v)) for (u, v) in zip(path, path[1:]))
81 >>> u, v = 0, 33
82 >>> cut_value, edge = minimum_edge_weight_in_shortest_path(T, u, v)
83 >>> cut_value
84 10
85 >>> nx.minimum_cut_value(G, u, v)
86 10
87 >>> # The Gomory-Hu tree also has the property that removing the
88 ... # edge with the minimum weight in the shortest path between
89 ... # any two nodes leaves two connected components that form
90 ... # a partition of the nodes in G that defines the minimum s-t
91 ... # cut.
92 ... cut_value, edge = minimum_edge_weight_in_shortest_path(T, u, v)
93 >>> T.remove_edge(*edge)
94 >>> U, V = list(nx.connected_components(T))
95 >>> # Thus U and V form a partition that defines a minimum cut
96 ... # between u and v in G. You can compute the edge cut set,
97 ... # that is, the set of edges that if removed from G will
98 ... # disconnect u from v in G, with this information:
99 ... cutset = set()
100 >>> for x, nbrs in ((n, G[n]) for n in U):
101 ... cutset.update((x, y) for y in nbrs if y in V)
102 >>> # Because we have set the capacities of all edges to 1
103 ... # the cutset contains ten edges
104 ... len(cutset)
105 10
106 >>> # You can use any maximum flow algorithm for the underlying
107 ... # flow computations using the argument flow_func
108 ... from networkx.algorithms import flow
109 >>> T = nx.gomory_hu_tree(G, flow_func=flow.boykov_kolmogorov)
110 >>> cut_value, edge = minimum_edge_weight_in_shortest_path(T, u, v)
111 >>> cut_value
112 10
113 >>> nx.minimum_cut_value(G, u, v, flow_func=flow.boykov_kolmogorov)
114 10
116 Notes
117 -----
118 This implementation is based on Gusfield approach [1]_ to compute
119 Gomory-Hu trees, which does not require node contractions and has
120 the same computational complexity than the original method.
122 See also
123 --------
124 :func:`minimum_cut`
125 :func:`maximum_flow`
127 References
128 ----------
129 .. [1] Gusfield D: Very simple methods for all pairs network flow analysis.
130 SIAM J Comput 19(1):143-155, 1990.
132 """
133 if flow_func is None:
134 flow_func = default_flow_func
136 if len(G) == 0: # empty graph
137 msg = "Empty Graph does not have a Gomory-Hu tree representation"
138 raise nx.NetworkXError(msg)
140 # Start the tree as a star graph with an arbitrary node at the center
141 tree = {}
142 labels = {}
143 iter_nodes = iter(G)
144 root = next(iter_nodes)
145 for n in iter_nodes:
146 tree[n] = root
148 # Reuse residual network
149 R = build_residual_network(G, capacity)
151 # For all the leaves in the star graph tree (that is n-1 nodes).
152 for source in tree:
153 # Find neighbor in the tree
154 target = tree[source]
155 # compute minimum cut
156 cut_value, partition = nx.minimum_cut(
157 G, source, target, capacity=capacity, flow_func=flow_func, residual=R
158 )
159 labels[(source, target)] = cut_value
160 # Update the tree
161 # Source will always be in partition[0] and target in partition[1]
162 for node in partition[0]:
163 if node != source and node in tree and tree[node] == target:
164 tree[node] = source
165 labels[node, source] = labels.get((node, target), cut_value)
166 #
167 if target != root and tree[target] in partition[0]:
168 labels[source, tree[target]] = labels[target, tree[target]]
169 labels[target, source] = cut_value
170 tree[source] = tree[target]
171 tree[target] = source
173 # Build the tree
174 T = nx.Graph()
175 T.add_nodes_from(G)
176 T.add_weighted_edges_from(((u, v, labels[u, v]) for u, v in tree.items()))
177 return T