1"""Functions for finding and evaluating cuts in a graph."""
2
3from itertools import chain
4
5import networkx as nx
6
7__all__ = [
8 "boundary_expansion",
9 "conductance",
10 "cut_size",
11 "edge_expansion",
12 "mixing_expansion",
13 "node_expansion",
14 "normalized_cut_size",
15 "volume",
16]
17
18
19# TODO STILL NEED TO UPDATE ALL THE DOCUMENTATION!
20
21
22@nx._dispatchable(edge_attrs="weight")
23def cut_size(G, S, T=None, weight=None):
24 """Returns the size of the cut between two sets of nodes.
25
26 A *cut* is a partition of the nodes of a graph into two sets. The
27 *cut size* is the sum of the weights of the edges "between" the two
28 sets of nodes.
29
30 Parameters
31 ----------
32 G : NetworkX graph
33
34 S : collection
35 A collection of nodes in `G`.
36
37 T : collection
38 A collection of nodes in `G`. If not specified, this is taken to
39 be the set complement of `S`.
40
41 weight : object
42 Edge attribute key to use as weight. If not specified, edges
43 have weight one.
44
45 Returns
46 -------
47 number
48 Total weight of all edges from nodes in set `S` to nodes in
49 set `T` (and, in the case of directed graphs, all edges from
50 nodes in `T` to nodes in `S`).
51
52 Examples
53 --------
54 In the graph with two cliques joined by a single edges, the natural
55 bipartition of the graph into two blocks, one for each clique,
56 yields a cut of weight one::
57
58 >>> G = nx.barbell_graph(3, 0)
59 >>> S = {0, 1, 2}
60 >>> T = {3, 4, 5}
61 >>> nx.cut_size(G, S, T)
62 1
63
64 Each parallel edge in a multigraph is counted when determining the
65 cut size::
66
67 >>> G = nx.MultiGraph(["ab", "ab"])
68 >>> S = {"a"}
69 >>> T = {"b"}
70 >>> nx.cut_size(G, S, T)
71 2
72
73 Notes
74 -----
75 In a multigraph, the cut size is the total weight of edges including
76 multiplicity.
77
78 """
79 edges = nx.edge_boundary(G, S, T, data=weight, default=1)
80 if G.is_directed():
81 edges = chain(edges, nx.edge_boundary(G, T, S, data=weight, default=1))
82 return sum(weight for u, v, weight in edges)
83
84
85@nx._dispatchable(edge_attrs="weight")
86def volume(G, S, weight=None):
87 """Returns the volume of a set of nodes.
88
89 The *volume* of a set *S* is the sum of the (out-)degrees of nodes
90 in *S* (taking into account parallel edges in multigraphs). [1]
91
92 Parameters
93 ----------
94 G : NetworkX graph
95
96 S : collection
97 A collection of nodes in `G`.
98
99 weight : object
100 Edge attribute key to use as weight. If not specified, edges
101 have weight one.
102
103 Returns
104 -------
105 number
106 The volume of the set of nodes represented by `S` in the graph
107 `G`.
108
109 See also
110 --------
111 conductance
112 cut_size
113 edge_expansion
114 edge_boundary
115 normalized_cut_size
116
117 References
118 ----------
119 .. [1] David Gleich.
120 *Hierarchical Directed Spectral Graph Partitioning*.
121 <https://www.cs.purdue.edu/homes/dgleich/publications/Gleich%202005%20-%20hierarchical%20directed%20spectral.pdf>
122
123 """
124 degree = G.out_degree if G.is_directed() else G.degree
125 return sum(d for v, d in degree(S, weight=weight))
126
127
128@nx._dispatchable(edge_attrs="weight")
129def normalized_cut_size(G, S, T=None, weight=None):
130 """Returns the normalized size of the cut between two sets of nodes.
131
132 The *normalized cut size* is the cut size times the sum of the
133 reciprocal sizes of the volumes of the two sets. [1]
134
135 Parameters
136 ----------
137 G : NetworkX graph
138
139 S : collection
140 A collection of nodes in `G`.
141
142 T : collection
143 A collection of nodes in `G`.
144
145 weight : object
146 Edge attribute key to use as weight. If not specified, edges
147 have weight one.
148
149 Returns
150 -------
151 number
152 The normalized cut size between the two sets `S` and `T`.
153
154 Notes
155 -----
156 In a multigraph, the cut size is the total weight of edges including
157 multiplicity.
158
159 See also
160 --------
161 conductance
162 cut_size
163 edge_expansion
164 volume
165
166 References
167 ----------
168 .. [1] David Gleich.
169 *Hierarchical Directed Spectral Graph Partitioning*.
170 <https://www.cs.purdue.edu/homes/dgleich/publications/Gleich%202005%20-%20hierarchical%20directed%20spectral.pdf>
171
172 """
173 if T is None:
174 T = set(G) - set(S)
175 num_cut_edges = cut_size(G, S, T=T, weight=weight)
176 volume_S = volume(G, S, weight=weight)
177 volume_T = volume(G, T, weight=weight)
178 return num_cut_edges * ((1 / volume_S) + (1 / volume_T))
179
180
181@nx._dispatchable(edge_attrs="weight")
182def conductance(G, S, T=None, weight=None):
183 """Returns the conductance of two sets of nodes.
184
185 The *conductance* is the quotient of the cut size and the smaller of
186 the volumes of the two sets. [1]
187
188 Parameters
189 ----------
190 G : NetworkX graph
191
192 S : collection
193 A collection of nodes in `G`.
194
195 T : collection
196 A collection of nodes in `G`.
197
198 weight : object
199 Edge attribute key to use as weight. If not specified, edges
200 have weight one.
201
202 Returns
203 -------
204 number
205 The conductance between the two sets `S` and `T`.
206
207 See also
208 --------
209 cut_size
210 edge_expansion
211 normalized_cut_size
212 volume
213
214 References
215 ----------
216 .. [1] David Gleich.
217 *Hierarchical Directed Spectral Graph Partitioning*.
218 <https://www.cs.purdue.edu/homes/dgleich/publications/Gleich%202005%20-%20hierarchical%20directed%20spectral.pdf>
219
220 """
221 if T is None:
222 T = set(G) - set(S)
223 num_cut_edges = cut_size(G, S, T, weight=weight)
224 volume_S = volume(G, S, weight=weight)
225 volume_T = volume(G, T, weight=weight)
226 return num_cut_edges / min(volume_S, volume_T)
227
228
229@nx._dispatchable(edge_attrs="weight")
230def edge_expansion(G, S, T=None, weight=None):
231 """Returns the edge expansion between two node sets.
232
233 The *edge expansion* is the quotient of the cut size and the smaller
234 of the cardinalities of the two sets. [1]
235
236 Parameters
237 ----------
238 G : NetworkX graph
239
240 S : collection
241 A collection of nodes in `G`.
242
243 T : collection
244 A collection of nodes in `G`.
245
246 weight : object
247 Edge attribute key to use as weight. If not specified, edges
248 have weight one.
249
250 Returns
251 -------
252 number
253 The edge expansion between the two sets `S` and `T`.
254
255 See also
256 --------
257 boundary_expansion
258 mixing_expansion
259 node_expansion
260
261 References
262 ----------
263 .. [1] Fan Chung.
264 *Spectral Graph Theory*.
265 (CBMS Regional Conference Series in Mathematics, No. 92),
266 American Mathematical Society, 1997, ISBN 0-8218-0315-8
267 <http://www.math.ucsd.edu/~fan/research/revised.html>
268
269 """
270 if T is None:
271 T = set(G) - set(S)
272 num_cut_edges = cut_size(G, S, T=T, weight=weight)
273 return num_cut_edges / min(len(S), len(T))
274
275
276@nx._dispatchable(edge_attrs="weight")
277def mixing_expansion(G, S, T=None, weight=None):
278 """Returns the mixing expansion between two node sets.
279
280 The *mixing expansion* is the quotient of the cut size and twice the
281 number of edges in the graph. [1]
282
283 Parameters
284 ----------
285 G : NetworkX graph
286
287 S : collection
288 A collection of nodes in `G`.
289
290 T : collection
291 A collection of nodes in `G`.
292
293 weight : object
294 Edge attribute key to use as weight. If not specified, edges
295 have weight one.
296
297 Returns
298 -------
299 number
300 The mixing expansion between the two sets `S` and `T`.
301
302 See also
303 --------
304 boundary_expansion
305 edge_expansion
306 node_expansion
307
308 References
309 ----------
310 .. [1] Vadhan, Salil P.
311 "Pseudorandomness."
312 *Foundations and Trends
313 in Theoretical Computer Science* 7.1–3 (2011): 1–336.
314 <https://doi.org/10.1561/0400000010>
315
316 """
317 num_cut_edges = cut_size(G, S, T=T, weight=weight)
318 num_total_edges = G.number_of_edges()
319 return num_cut_edges / (2 * num_total_edges)
320
321
322# TODO What is the generalization to two arguments, S and T? Does the
323# denominator become `min(len(S), len(T))`?
324@nx._dispatchable
325def node_expansion(G, S):
326 """Returns the node expansion of the set `S`.
327
328 The *node expansion* is the quotient of the size of the node
329 boundary of *S* and the cardinality of *S*. [1]
330
331 Parameters
332 ----------
333 G : NetworkX graph
334
335 S : collection
336 A collection of nodes in `G`.
337
338 Returns
339 -------
340 number
341 The node expansion of the set `S`.
342
343 See also
344 --------
345 boundary_expansion
346 edge_expansion
347 mixing_expansion
348
349 References
350 ----------
351 .. [1] Vadhan, Salil P.
352 "Pseudorandomness."
353 *Foundations and Trends
354 in Theoretical Computer Science* 7.1–3 (2011): 1–336.
355 <https://doi.org/10.1561/0400000010>
356
357 """
358 neighborhood = set(chain.from_iterable(G.neighbors(v) for v in S))
359 return len(neighborhood) / len(S)
360
361
362@nx._dispatchable
363def boundary_expansion(G, S):
364 """Returns the boundary expansion of the set `S`.
365
366 The *boundary expansion* of a set `S` is the ratio between the size of its
367 node boundary and the cardinality of the set itself [1]_ .
368
369 Parameters
370 ----------
371 G : NetworkX graph
372 The input graph.
373
374 S : collection
375 A collection of nodes in `G`.
376
377 Returns
378 -------
379 number
380 The boundary expansion ratio: size of node boundary / size of `S`.
381
382 Examples
383 --------
384 The node boundary is {2, 3} (size 2), divided by ``|S|=2``:
385
386 >>> G = nx.cycle_graph(4)
387 >>> S = {0, 1}
388 >>> nx.boundary_expansion(G, S)
389 1.0
390
391 For disconnected sets, e.g. here where the node boundary is ``{1, 3, 5}``:
392
393 >>> G = nx.cycle_graph(6)
394 >>> S = {0, 2, 4}
395 >>> nx.boundary_expansion(G, S)
396 1.0
397
398 See also
399 --------
400 :func:`~networkx.algorithms.boundary.node_boundary`
401 edge_expansion
402 mixing_expansion
403 node_expansion
404
405 Notes
406 -----
407 The node boundary is defined as all nodes not in `S` that are adjacent to
408 nodes in `S`.
409
410 References
411 ----------
412 .. [1] Vadhan, Salil P.
413 "Pseudorandomness." *Foundations and Trends in Theoretical Computer Science*
414 7.1–3 (2011): 1–336. <https://doi.org/10.1561/0400000010>
415 """
416 return len(nx.node_boundary(G, S)) / len(S)