Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/networkx/algorithms/graphical.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:32:28 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:d30785e31577 | 1:56ad4e20f292 |
---|---|
1 # -*- coding: utf-8 -*- | |
2 """Test sequences for graphiness. | |
3 """ | |
4 # Copyright (C) 2004-2019 by | |
5 # Aric Hagberg <hagberg@lanl.gov> | |
6 # Dan Schult <dschult@colgate.edu> | |
7 # Pieter Swart <swart@lanl.gov> | |
8 # All rights reserved. | |
9 # BSD license. | |
10 import heapq | |
11 import networkx as nx | |
12 __author__ = "\n".join(['Aric Hagberg (hagberg@lanl.gov)', | |
13 'Pieter Swart (swart@lanl.gov)', | |
14 'Dan Schult (dschult@colgate.edu)' | |
15 'Joel Miller (joel.c.miller.research@gmail.com)' | |
16 'Ben Edwards' | |
17 'Brian Cloteaux <brian.cloteaux@nist.gov>']) | |
18 | |
19 __all__ = ['is_graphical', | |
20 'is_multigraphical', | |
21 'is_pseudographical', | |
22 'is_digraphical', | |
23 'is_valid_degree_sequence_erdos_gallai', | |
24 'is_valid_degree_sequence_havel_hakimi', | |
25 ] | |
26 | |
27 | |
28 def is_graphical(sequence, method='eg'): | |
29 """Returns True if sequence is a valid degree sequence. | |
30 | |
31 A degree sequence is valid if some graph can realize it. | |
32 | |
33 Parameters | |
34 ---------- | |
35 sequence : list or iterable container | |
36 A sequence of integer node degrees | |
37 | |
38 method : "eg" | "hh" (default: 'eg') | |
39 The method used to validate the degree sequence. | |
40 "eg" corresponds to the Erdős-Gallai algorithm, and | |
41 "hh" to the Havel-Hakimi algorithm. | |
42 | |
43 Returns | |
44 ------- | |
45 valid : bool | |
46 True if the sequence is a valid degree sequence and False if not. | |
47 | |
48 Examples | |
49 -------- | |
50 >>> G = nx.path_graph(4) | |
51 >>> sequence = (d for n, d in G.degree()) | |
52 >>> nx.is_graphical(sequence) | |
53 True | |
54 | |
55 References | |
56 ---------- | |
57 Erdős-Gallai | |
58 [EG1960]_, [choudum1986]_ | |
59 | |
60 Havel-Hakimi | |
61 [havel1955]_, [hakimi1962]_, [CL1996]_ | |
62 """ | |
63 if method == 'eg': | |
64 valid = is_valid_degree_sequence_erdos_gallai(list(sequence)) | |
65 elif method == 'hh': | |
66 valid = is_valid_degree_sequence_havel_hakimi(list(sequence)) | |
67 else: | |
68 msg = "`method` must be 'eg' or 'hh'" | |
69 raise nx.NetworkXException(msg) | |
70 return valid | |
71 | |
72 | |
73 def _basic_graphical_tests(deg_sequence): | |
74 # Sort and perform some simple tests on the sequence | |
75 deg_sequence = nx.utils.make_list_of_ints(deg_sequence) | |
76 p = len(deg_sequence) | |
77 num_degs = [0] * p | |
78 dmax, dmin, dsum, n = 0, p, 0, 0 | |
79 for d in deg_sequence: | |
80 # Reject if degree is negative or larger than the sequence length | |
81 if d < 0 or d >= p: | |
82 raise nx.NetworkXUnfeasible | |
83 # Process only the non-zero integers | |
84 elif d > 0: | |
85 dmax, dmin, dsum, n = max(dmax, d), min(dmin, d), dsum + d, n + 1 | |
86 num_degs[d] += 1 | |
87 # Reject sequence if it has odd sum or is oversaturated | |
88 if dsum % 2 or dsum > n * (n - 1): | |
89 raise nx.NetworkXUnfeasible | |
90 return dmax, dmin, dsum, n, num_degs | |
91 | |
92 | |
93 def is_valid_degree_sequence_havel_hakimi(deg_sequence): | |
94 r"""Returns True if deg_sequence can be realized by a simple graph. | |
95 | |
96 The validation proceeds using the Havel-Hakimi theorem. | |
97 Worst-case run time is $O(s)$ where $s$ is the sum of the sequence. | |
98 | |
99 Parameters | |
100 ---------- | |
101 deg_sequence : list | |
102 A list of integers where each element specifies the degree of a node | |
103 in a graph. | |
104 | |
105 Returns | |
106 ------- | |
107 valid : bool | |
108 True if deg_sequence is graphical and False if not. | |
109 | |
110 Notes | |
111 ----- | |
112 The ZZ condition says that for the sequence d if | |
113 | |
114 .. math:: | |
115 |d| >= \frac{(\max(d) + \min(d) + 1)^2}{4*\min(d)} | |
116 | |
117 then d is graphical. This was shown in Theorem 6 in [1]_. | |
118 | |
119 References | |
120 ---------- | |
121 .. [1] I.E. Zverovich and V.E. Zverovich. "Contributions to the theory | |
122 of graphic sequences", Discrete Mathematics, 105, pp. 292-303 (1992). | |
123 | |
124 [havel1955]_, [hakimi1962]_, [CL1996]_ | |
125 | |
126 """ | |
127 try: | |
128 dmax, dmin, dsum, n, num_degs = _basic_graphical_tests(deg_sequence) | |
129 except nx.NetworkXUnfeasible: | |
130 return False | |
131 # Accept if sequence has no non-zero degrees or passes the ZZ condition | |
132 if n == 0 or 4 * dmin * n >= (dmax + dmin + 1) * (dmax + dmin + 1): | |
133 return True | |
134 | |
135 modstubs = [0] * (dmax + 1) | |
136 # Successively reduce degree sequence by removing the maximum degree | |
137 while n > 0: | |
138 # Retrieve the maximum degree in the sequence | |
139 while num_degs[dmax] == 0: | |
140 dmax -= 1 | |
141 # If there are not enough stubs to connect to, then the sequence is | |
142 # not graphical | |
143 if dmax > n - 1: | |
144 return False | |
145 | |
146 # Remove largest stub in list | |
147 num_degs[dmax], n = num_degs[dmax] - 1, n - 1 | |
148 # Reduce the next dmax largest stubs | |
149 mslen = 0 | |
150 k = dmax | |
151 for i in range(dmax): | |
152 while num_degs[k] == 0: | |
153 k -= 1 | |
154 num_degs[k], n = num_degs[k] - 1, n - 1 | |
155 if k > 1: | |
156 modstubs[mslen] = k - 1 | |
157 mslen += 1 | |
158 # Add back to the list any non-zero stubs that were removed | |
159 for i in range(mslen): | |
160 stub = modstubs[i] | |
161 num_degs[stub], n = num_degs[stub] + 1, n + 1 | |
162 return True | |
163 | |
164 | |
165 def is_valid_degree_sequence_erdos_gallai(deg_sequence): | |
166 r"""Returns True if deg_sequence can be realized by a simple graph. | |
167 | |
168 The validation is done using the Erdős-Gallai theorem [EG1960]_. | |
169 | |
170 Parameters | |
171 ---------- | |
172 deg_sequence : list | |
173 A list of integers | |
174 | |
175 Returns | |
176 ------- | |
177 valid : bool | |
178 True if deg_sequence is graphical and False if not. | |
179 | |
180 Notes | |
181 ----- | |
182 | |
183 This implementation uses an equivalent form of the Erdős-Gallai criterion. | |
184 Worst-case run time is $O(n)$ where $n$ is the length of the sequence. | |
185 | |
186 Specifically, a sequence d is graphical if and only if the | |
187 sum of the sequence is even and for all strong indices k in the sequence, | |
188 | |
189 .. math:: | |
190 | |
191 \sum_{i=1}^{k} d_i \leq k(k-1) + \sum_{j=k+1}^{n} \min(d_i,k) | |
192 = k(n-1) - ( k \sum_{j=0}^{k-1} n_j - \sum_{j=0}^{k-1} j n_j ) | |
193 | |
194 A strong index k is any index where d_k >= k and the value n_j is the | |
195 number of occurrences of j in d. The maximal strong index is called the | |
196 Durfee index. | |
197 | |
198 This particular rearrangement comes from the proof of Theorem 3 in [2]_. | |
199 | |
200 The ZZ condition says that for the sequence d if | |
201 | |
202 .. math:: | |
203 |d| >= \frac{(\max(d) + \min(d) + 1)^2}{4*\min(d)} | |
204 | |
205 then d is graphical. This was shown in Theorem 6 in [2]_. | |
206 | |
207 References | |
208 ---------- | |
209 .. [1] A. Tripathi and S. Vijay. "A note on a theorem of Erdős & Gallai", | |
210 Discrete Mathematics, 265, pp. 417-420 (2003). | |
211 .. [2] I.E. Zverovich and V.E. Zverovich. "Contributions to the theory | |
212 of graphic sequences", Discrete Mathematics, 105, pp. 292-303 (1992). | |
213 | |
214 [EG1960]_, [choudum1986]_ | |
215 """ | |
216 try: | |
217 dmax, dmin, dsum, n, num_degs = _basic_graphical_tests(deg_sequence) | |
218 except nx.NetworkXUnfeasible: | |
219 return False | |
220 # Accept if sequence has no non-zero degrees or passes the ZZ condition | |
221 if n == 0 or 4 * dmin * n >= (dmax + dmin + 1) * (dmax + dmin + 1): | |
222 return True | |
223 | |
224 # Perform the EG checks using the reformulation of Zverovich and Zverovich | |
225 k, sum_deg, sum_nj, sum_jnj = 0, 0, 0, 0 | |
226 for dk in range(dmax, dmin - 1, -1): | |
227 if dk < k + 1: # Check if already past Durfee index | |
228 return True | |
229 if num_degs[dk] > 0: | |
230 run_size = num_degs[dk] # Process a run of identical-valued degrees | |
231 if dk < k + run_size: # Check if end of run is past Durfee index | |
232 run_size = dk - k # Adjust back to Durfee index | |
233 sum_deg += run_size * dk | |
234 for v in range(run_size): | |
235 sum_nj += num_degs[k + v] | |
236 sum_jnj += (k + v) * num_degs[k + v] | |
237 k += run_size | |
238 if sum_deg > k * (n - 1) - k * sum_nj + sum_jnj: | |
239 return False | |
240 return True | |
241 | |
242 | |
243 def is_multigraphical(sequence): | |
244 """Returns True if some multigraph can realize the sequence. | |
245 | |
246 Parameters | |
247 ---------- | |
248 sequence : list | |
249 A list of integers | |
250 | |
251 Returns | |
252 ------- | |
253 valid : bool | |
254 True if deg_sequence is a multigraphic degree sequence and False if not. | |
255 | |
256 Notes | |
257 ----- | |
258 The worst-case run time is $O(n)$ where $n$ is the length of the sequence. | |
259 | |
260 References | |
261 ---------- | |
262 .. [1] S. L. Hakimi. "On the realizability of a set of integers as | |
263 degrees of the vertices of a linear graph", J. SIAM, 10, pp. 496-506 | |
264 (1962). | |
265 """ | |
266 try: | |
267 deg_sequence = nx.utils.make_list_of_ints(sequence) | |
268 except nx.NetworkXError: | |
269 return False | |
270 dsum, dmax = 0, 0 | |
271 for d in deg_sequence: | |
272 if d < 0: | |
273 return False | |
274 dsum, dmax = dsum + d, max(dmax, d) | |
275 if dsum % 2 or dsum < 2 * dmax: | |
276 return False | |
277 return True | |
278 | |
279 | |
280 def is_pseudographical(sequence): | |
281 """Returns True if some pseudograph can realize the sequence. | |
282 | |
283 Every nonnegative integer sequence with an even sum is pseudographical | |
284 (see [1]_). | |
285 | |
286 Parameters | |
287 ---------- | |
288 sequence : list or iterable container | |
289 A sequence of integer node degrees | |
290 | |
291 Returns | |
292 ------- | |
293 valid : bool | |
294 True if the sequence is a pseudographic degree sequence and False if not. | |
295 | |
296 Notes | |
297 ----- | |
298 The worst-case run time is $O(n)$ where n is the length of the sequence. | |
299 | |
300 References | |
301 ---------- | |
302 .. [1] F. Boesch and F. Harary. "Line removal algorithms for graphs | |
303 and their degree lists", IEEE Trans. Circuits and Systems, CAS-23(12), | |
304 pp. 778-782 (1976). | |
305 """ | |
306 try: | |
307 deg_sequence = nx.utils.make_list_of_ints(sequence) | |
308 except nx.NetworkXError: | |
309 return False | |
310 return sum(deg_sequence) % 2 == 0 and min(deg_sequence) >= 0 | |
311 | |
312 | |
313 def is_digraphical(in_sequence, out_sequence): | |
314 r"""Returns True if some directed graph can realize the in- and out-degree | |
315 sequences. | |
316 | |
317 Parameters | |
318 ---------- | |
319 in_sequence : list or iterable container | |
320 A sequence of integer node in-degrees | |
321 | |
322 out_sequence : list or iterable container | |
323 A sequence of integer node out-degrees | |
324 | |
325 Returns | |
326 ------- | |
327 valid : bool | |
328 True if in and out-sequences are digraphic False if not. | |
329 | |
330 Notes | |
331 ----- | |
332 This algorithm is from Kleitman and Wang [1]_. | |
333 The worst case runtime is $O(s \times \log n)$ where $s$ and $n$ are the | |
334 sum and length of the sequences respectively. | |
335 | |
336 References | |
337 ---------- | |
338 .. [1] D.J. Kleitman and D.L. Wang | |
339 Algorithms for Constructing Graphs and Digraphs with Given Valences | |
340 and Factors, Discrete Mathematics, 6(1), pp. 79-88 (1973) | |
341 """ | |
342 try: | |
343 in_deg_sequence = nx.utils.make_list_of_ints(in_sequence) | |
344 out_deg_sequence = nx.utils.make_list_of_ints(out_sequence) | |
345 except nx.NetworkXError: | |
346 return False | |
347 # Process the sequences and form two heaps to store degree pairs with | |
348 # either zero or non-zero out degrees | |
349 sumin, sumout, nin, nout = 0, 0, len(in_deg_sequence), len(out_deg_sequence) | |
350 maxn = max(nin, nout) | |
351 maxin = 0 | |
352 if maxn == 0: | |
353 return True | |
354 stubheap, zeroheap = [], [] | |
355 for n in range(maxn): | |
356 in_deg, out_deg = 0, 0 | |
357 if n < nout: | |
358 out_deg = out_deg_sequence[n] | |
359 if n < nin: | |
360 in_deg = in_deg_sequence[n] | |
361 if in_deg < 0 or out_deg < 0: | |
362 return False | |
363 sumin, sumout, maxin = sumin + in_deg, sumout + out_deg, max(maxin, in_deg) | |
364 if in_deg > 0: | |
365 stubheap.append((-1 * out_deg, -1 * in_deg)) | |
366 elif out_deg > 0: | |
367 zeroheap.append(-1 * out_deg) | |
368 if sumin != sumout: | |
369 return False | |
370 heapq.heapify(stubheap) | |
371 heapq.heapify(zeroheap) | |
372 | |
373 modstubs = [(0, 0)] * (maxin + 1) | |
374 # Successively reduce degree sequence by removing the maximum out degree | |
375 while stubheap: | |
376 # Take the first value in the sequence with non-zero in degree | |
377 (freeout, freein) = heapq.heappop(stubheap) | |
378 freein *= -1 | |
379 if freein > len(stubheap) + len(zeroheap): | |
380 return False | |
381 | |
382 # Attach out stubs to the nodes with the most in stubs | |
383 mslen = 0 | |
384 for i in range(freein): | |
385 if zeroheap and (not stubheap or stubheap[0][0] > zeroheap[0]): | |
386 stubout = heapq.heappop(zeroheap) | |
387 stubin = 0 | |
388 else: | |
389 (stubout, stubin) = heapq.heappop(stubheap) | |
390 if stubout == 0: | |
391 return False | |
392 # Check if target is now totally connected | |
393 if stubout + 1 < 0 or stubin < 0: | |
394 modstubs[mslen] = (stubout + 1, stubin) | |
395 mslen += 1 | |
396 | |
397 # Add back the nodes to the heap that still have available stubs | |
398 for i in range(mslen): | |
399 stub = modstubs[i] | |
400 if stub[1] < 0: | |
401 heapq.heappush(stubheap, stub) | |
402 else: | |
403 heapq.heappush(zeroheap, stub[0]) | |
404 if freeout < 0: | |
405 heapq.heappush(zeroheap, freeout) | |
406 return True |