Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/networkx/generators/directed.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 # Copyright (C) 2006-2019 by | |
| 3 # Aric Hagberg <hagberg@lanl.gov> | |
| 4 # Dan Schult <dschult@colgate.edu> | |
| 5 # Pieter Swart <swart@lanl.gov> | |
| 6 # Copyright (C) 2009 by Willem Ligtenberg <W.P.A.Ligtenberg@tue.nl> | |
| 7 # All rights reserved. | |
| 8 # BSD license. | |
| 9 # | |
| 10 # Authors: Aric Hagberg (hagberg@lanl.gov) | |
| 11 # Willem Ligtenberg (W.P.A.Ligtenberg@tue.nl) | |
| 12 """ | |
| 13 Generators for some directed graphs, including growing network (GN) graphs and | |
| 14 scale-free graphs. | |
| 15 | |
| 16 """ | |
| 17 | |
| 18 from collections import Counter | |
| 19 | |
| 20 import networkx as nx | |
| 21 from networkx.generators.classic import empty_graph | |
| 22 from networkx.utils import discrete_sequence | |
| 23 from networkx.utils import weighted_choice | |
| 24 from networkx.utils import py_random_state | |
| 25 | |
| 26 __all__ = ['gn_graph', 'gnc_graph', 'gnr_graph', 'random_k_out_graph', | |
| 27 'scale_free_graph'] | |
| 28 | |
| 29 | |
| 30 @py_random_state(3) | |
| 31 def gn_graph(n, kernel=None, create_using=None, seed=None): | |
| 32 """Returns the growing network (GN) digraph with `n` nodes. | |
| 33 | |
| 34 The GN graph is built by adding nodes one at a time with a link to one | |
| 35 previously added node. The target node for the link is chosen with | |
| 36 probability based on degree. The default attachment kernel is a linear | |
| 37 function of the degree of a node. | |
| 38 | |
| 39 The graph is always a (directed) tree. | |
| 40 | |
| 41 Parameters | |
| 42 ---------- | |
| 43 n : int | |
| 44 The number of nodes for the generated graph. | |
| 45 kernel : function | |
| 46 The attachment kernel. | |
| 47 create_using : NetworkX graph constructor, optional (default DiGraph) | |
| 48 Graph type to create. If graph instance, then cleared before populated. | |
| 49 seed : integer, random_state, or None (default) | |
| 50 Indicator of random number generation state. | |
| 51 See :ref:`Randomness<randomness>`. | |
| 52 | |
| 53 Examples | |
| 54 -------- | |
| 55 To create the undirected GN graph, use the :meth:`~DiGraph.to_directed` | |
| 56 method:: | |
| 57 | |
| 58 >>> D = nx.gn_graph(10) # the GN graph | |
| 59 >>> G = D.to_undirected() # the undirected version | |
| 60 | |
| 61 To specify an attachment kernel, use the `kernel` keyword argument:: | |
| 62 | |
| 63 >>> D = nx.gn_graph(10, kernel=lambda x: x ** 1.5) # A_k = k^1.5 | |
| 64 | |
| 65 References | |
| 66 ---------- | |
| 67 .. [1] P. L. Krapivsky and S. Redner, | |
| 68 Organization of Growing Random Networks, | |
| 69 Phys. Rev. E, 63, 066123, 2001. | |
| 70 """ | |
| 71 G = empty_graph(1, create_using, default=nx.DiGraph) | |
| 72 if not G.is_directed(): | |
| 73 raise nx.NetworkXError("create_using must indicate a Directed Graph") | |
| 74 | |
| 75 if kernel is None: | |
| 76 def kernel(x): return x | |
| 77 | |
| 78 if n == 1: | |
| 79 return G | |
| 80 | |
| 81 G.add_edge(1, 0) # get started | |
| 82 ds = [1, 1] # degree sequence | |
| 83 | |
| 84 for source in range(2, n): | |
| 85 # compute distribution from kernel and degree | |
| 86 dist = [kernel(d) for d in ds] | |
| 87 # choose target from discrete distribution | |
| 88 target = discrete_sequence(1, distribution=dist, seed=seed)[0] | |
| 89 G.add_edge(source, target) | |
| 90 ds.append(1) # the source has only one link (degree one) | |
| 91 ds[target] += 1 # add one to the target link degree | |
| 92 return G | |
| 93 | |
| 94 | |
| 95 @py_random_state(3) | |
| 96 def gnr_graph(n, p, create_using=None, seed=None): | |
| 97 """Returns the growing network with redirection (GNR) digraph with `n` | |
| 98 nodes and redirection probability `p`. | |
| 99 | |
| 100 The GNR graph is built by adding nodes one at a time with a link to one | |
| 101 previously added node. The previous target node is chosen uniformly at | |
| 102 random. With probabiliy `p` the link is instead "redirected" to the | |
| 103 successor node of the target. | |
| 104 | |
| 105 The graph is always a (directed) tree. | |
| 106 | |
| 107 Parameters | |
| 108 ---------- | |
| 109 n : int | |
| 110 The number of nodes for the generated graph. | |
| 111 p : float | |
| 112 The redirection probability. | |
| 113 create_using : NetworkX graph constructor, optional (default DiGraph) | |
| 114 Graph type to create. If graph instance, then cleared before populated. | |
| 115 seed : integer, random_state, or None (default) | |
| 116 Indicator of random number generation state. | |
| 117 See :ref:`Randomness<randomness>`. | |
| 118 | |
| 119 Examples | |
| 120 -------- | |
| 121 To create the undirected GNR graph, use the :meth:`~DiGraph.to_directed` | |
| 122 method:: | |
| 123 | |
| 124 >>> D = nx.gnr_graph(10, 0.5) # the GNR graph | |
| 125 >>> G = D.to_undirected() # the undirected version | |
| 126 | |
| 127 References | |
| 128 ---------- | |
| 129 .. [1] P. L. Krapivsky and S. Redner, | |
| 130 Organization of Growing Random Networks, | |
| 131 Phys. Rev. E, 63, 066123, 2001. | |
| 132 """ | |
| 133 G = empty_graph(1, create_using, default=nx.DiGraph) | |
| 134 if not G.is_directed(): | |
| 135 raise nx.NetworkXError("create_using must indicate a Directed Graph") | |
| 136 | |
| 137 if n == 1: | |
| 138 return G | |
| 139 | |
| 140 for source in range(1, n): | |
| 141 target = seed.randrange(0, source) | |
| 142 if seed.random() < p and target != 0: | |
| 143 target = next(G.successors(target)) | |
| 144 G.add_edge(source, target) | |
| 145 return G | |
| 146 | |
| 147 | |
| 148 @py_random_state(2) | |
| 149 def gnc_graph(n, create_using=None, seed=None): | |
| 150 """Returns the growing network with copying (GNC) digraph with `n` nodes. | |
| 151 | |
| 152 The GNC graph is built by adding nodes one at a time with a link to one | |
| 153 previously added node (chosen uniformly at random) and to all of that | |
| 154 node's successors. | |
| 155 | |
| 156 Parameters | |
| 157 ---------- | |
| 158 n : int | |
| 159 The number of nodes for the generated graph. | |
| 160 create_using : NetworkX graph constructor, optional (default DiGraph) | |
| 161 Graph type to create. If graph instance, then cleared before populated. | |
| 162 seed : integer, random_state, or None (default) | |
| 163 Indicator of random number generation state. | |
| 164 See :ref:`Randomness<randomness>`. | |
| 165 | |
| 166 References | |
| 167 ---------- | |
| 168 .. [1] P. L. Krapivsky and S. Redner, | |
| 169 Network Growth by Copying, | |
| 170 Phys. Rev. E, 71, 036118, 2005k.}, | |
| 171 """ | |
| 172 G = empty_graph(1, create_using, default=nx.DiGraph) | |
| 173 if not G.is_directed(): | |
| 174 raise nx.NetworkXError("create_using must indicate a Directed Graph") | |
| 175 | |
| 176 if n == 1: | |
| 177 return G | |
| 178 | |
| 179 for source in range(1, n): | |
| 180 target = seed.randrange(0, source) | |
| 181 for succ in G.successors(target): | |
| 182 G.add_edge(source, succ) | |
| 183 G.add_edge(source, target) | |
| 184 return G | |
| 185 | |
| 186 | |
| 187 @py_random_state(7) | |
| 188 def scale_free_graph(n, alpha=0.41, beta=0.54, gamma=0.05, delta_in=0.2, | |
| 189 delta_out=0, create_using=None, seed=None): | |
| 190 """Returns a scale-free directed graph. | |
| 191 | |
| 192 Parameters | |
| 193 ---------- | |
| 194 n : integer | |
| 195 Number of nodes in graph | |
| 196 alpha : float | |
| 197 Probability for adding a new node connected to an existing node | |
| 198 chosen randomly according to the in-degree distribution. | |
| 199 beta : float | |
| 200 Probability for adding an edge between two existing nodes. | |
| 201 One existing node is chosen randomly according the in-degree | |
| 202 distribution and the other chosen randomly according to the out-degree | |
| 203 distribution. | |
| 204 gamma : float | |
| 205 Probability for adding a new node connected to an existing node | |
| 206 chosen randomly according to the out-degree distribution. | |
| 207 delta_in : float | |
| 208 Bias for choosing nodes from in-degree distribution. | |
| 209 delta_out : float | |
| 210 Bias for choosing nodes from out-degree distribution. | |
| 211 create_using : NetworkX graph constructor, optional | |
| 212 The default is a MultiDiGraph 3-cycle. | |
| 213 If a graph instance, use it without clearing first. | |
| 214 If a graph constructor, call it to construct an empty graph. | |
| 215 seed : integer, random_state, or None (default) | |
| 216 Indicator of random number generation state. | |
| 217 See :ref:`Randomness<randomness>`. | |
| 218 | |
| 219 Examples | |
| 220 -------- | |
| 221 Create a scale-free graph on one hundred nodes:: | |
| 222 | |
| 223 >>> G = nx.scale_free_graph(100) | |
| 224 | |
| 225 Notes | |
| 226 ----- | |
| 227 The sum of `alpha`, `beta`, and `gamma` must be 1. | |
| 228 | |
| 229 References | |
| 230 ---------- | |
| 231 .. [1] B. Bollobás, C. Borgs, J. Chayes, and O. Riordan, | |
| 232 Directed scale-free graphs, | |
| 233 Proceedings of the fourteenth annual ACM-SIAM Symposium on | |
| 234 Discrete Algorithms, 132--139, 2003. | |
| 235 """ | |
| 236 | |
| 237 def _choose_node(G, distribution, delta, psum): | |
| 238 cumsum = 0.0 | |
| 239 # normalization | |
| 240 r = seed.random() | |
| 241 for n, d in distribution: | |
| 242 cumsum += (d + delta) / psum | |
| 243 if r < cumsum: | |
| 244 break | |
| 245 return n | |
| 246 | |
| 247 if create_using is None or not hasattr(create_using, '_adj'): | |
| 248 # start with 3-cycle | |
| 249 G = nx.empty_graph(3, create_using, default=nx.MultiDiGraph) | |
| 250 G.add_edges_from([(0, 1), (1, 2), (2, 0)]) | |
| 251 else: | |
| 252 G = create_using | |
| 253 if not (G.is_directed() and G.is_multigraph()): | |
| 254 raise nx.NetworkXError("MultiDiGraph required in create_using") | |
| 255 | |
| 256 if alpha <= 0: | |
| 257 raise ValueError('alpha must be > 0.') | |
| 258 if beta <= 0: | |
| 259 raise ValueError('beta must be > 0.') | |
| 260 if gamma <= 0: | |
| 261 raise ValueError('gamma must be > 0.') | |
| 262 | |
| 263 if abs(alpha + beta + gamma - 1.0) >= 1e-9: | |
| 264 raise ValueError('alpha+beta+gamma must equal 1.') | |
| 265 | |
| 266 number_of_edges = G.number_of_edges() | |
| 267 while len(G) < n: | |
| 268 psum_in = number_of_edges + delta_in * len(G) | |
| 269 psum_out = number_of_edges + delta_out * len(G) | |
| 270 r = seed.random() | |
| 271 # random choice in alpha,beta,gamma ranges | |
| 272 if r < alpha: | |
| 273 # alpha | |
| 274 # add new node v | |
| 275 v = len(G) | |
| 276 # choose w according to in-degree and delta_in | |
| 277 w = _choose_node(G, G.in_degree(), delta_in, psum_in) | |
| 278 elif r < alpha + beta: | |
| 279 # beta | |
| 280 # choose v according to out-degree and delta_out | |
| 281 v = _choose_node(G, G.out_degree(), delta_out, psum_out) | |
| 282 # choose w according to in-degree and delta_in | |
| 283 w = _choose_node(G, G.in_degree(), delta_in, psum_in) | |
| 284 else: | |
| 285 # gamma | |
| 286 # choose v according to out-degree and delta_out | |
| 287 v = _choose_node(G, G.out_degree(), delta_out, psum_out) | |
| 288 # add new node w | |
| 289 w = len(G) | |
| 290 G.add_edge(v, w) | |
| 291 number_of_edges += 1 | |
| 292 return G | |
| 293 | |
| 294 | |
| 295 @py_random_state(4) | |
| 296 def random_uniform_k_out_graph(n, k, self_loops=True, with_replacement=True, | |
| 297 seed=None): | |
| 298 """Returns a random `k`-out graph with uniform attachment. | |
| 299 | |
| 300 A random `k`-out graph with uniform attachment is a multidigraph | |
| 301 generated by the following algorithm. For each node *u*, choose | |
| 302 `k` nodes *v* uniformly at random (with replacement). Add a | |
| 303 directed edge joining *u* to *v*. | |
| 304 | |
| 305 Parameters | |
| 306 ---------- | |
| 307 n : int | |
| 308 The number of nodes in the returned graph. | |
| 309 | |
| 310 k : int | |
| 311 The out-degree of each node in the returned graph. | |
| 312 | |
| 313 self_loops : bool | |
| 314 If True, self-loops are allowed when generating the graph. | |
| 315 | |
| 316 with_replacement : bool | |
| 317 If True, neighbors are chosen with replacement and the | |
| 318 returned graph will be a directed multigraph. Otherwise, | |
| 319 neighbors are chosen without replacement and the returned graph | |
| 320 will be a directed graph. | |
| 321 | |
| 322 seed : integer, random_state, or None (default) | |
| 323 Indicator of random number generation state. | |
| 324 See :ref:`Randomness<randomness>`. | |
| 325 | |
| 326 Returns | |
| 327 ------- | |
| 328 NetworkX graph | |
| 329 A `k`-out-regular directed graph generated according to the | |
| 330 above algorithm. It will be a multigraph if and only if | |
| 331 `with_replacement` is True. | |
| 332 | |
| 333 Raises | |
| 334 ------ | |
| 335 ValueError | |
| 336 If `with_replacement` is False and `k` is greater than | |
| 337 `n`. | |
| 338 | |
| 339 See also | |
| 340 -------- | |
| 341 random_k_out_graph | |
| 342 | |
| 343 Notes | |
| 344 ----- | |
| 345 The return digraph or multidigraph may not be strongly connected, or | |
| 346 even weakly connected. | |
| 347 | |
| 348 If `with_replacement` is True, this function is similar to | |
| 349 :func:`random_k_out_graph`, if that function had parameter `alpha` | |
| 350 set to positive infinity. | |
| 351 | |
| 352 """ | |
| 353 if with_replacement: | |
| 354 create_using = nx.MultiDiGraph() | |
| 355 | |
| 356 def sample(v, nodes): | |
| 357 if not self_loops: | |
| 358 nodes = nodes - {v} | |
| 359 return (seed.choice(list(nodes)) for i in range(k)) | |
| 360 | |
| 361 else: | |
| 362 create_using = nx.DiGraph() | |
| 363 | |
| 364 def sample(v, nodes): | |
| 365 if not self_loops: | |
| 366 nodes = nodes - {v} | |
| 367 return seed.sample(nodes, k) | |
| 368 | |
| 369 G = nx.empty_graph(n, create_using) | |
| 370 nodes = set(G) | |
| 371 for u in G: | |
| 372 G.add_edges_from((u, v) for v in sample(u, nodes)) | |
| 373 return G | |
| 374 | |
| 375 | |
| 376 @py_random_state(4) | |
| 377 def random_k_out_graph(n, k, alpha, self_loops=True, seed=None): | |
| 378 """Returns a random `k`-out graph with preferential attachment. | |
| 379 | |
| 380 A random `k`-out graph with preferential attachment is a | |
| 381 multidigraph generated by the following algorithm. | |
| 382 | |
| 383 1. Begin with an empty digraph, and initially set each node to have | |
| 384 weight `alpha`. | |
| 385 2. Choose a node `u` with out-degree less than `k` uniformly at | |
| 386 random. | |
| 387 3. Choose a node `v` from with probability proportional to its | |
| 388 weight. | |
| 389 4. Add a directed edge from `u` to `v`, and increase the weight | |
| 390 of `v` by one. | |
| 391 5. If each node has out-degree `k`, halt, otherwise repeat from | |
| 392 step 2. | |
| 393 | |
| 394 For more information on this model of random graph, see [1]. | |
| 395 | |
| 396 Parameters | |
| 397 ---------- | |
| 398 n : int | |
| 399 The number of nodes in the returned graph. | |
| 400 | |
| 401 k : int | |
| 402 The out-degree of each node in the returned graph. | |
| 403 | |
| 404 alpha : float | |
| 405 A positive :class:`float` representing the initial weight of | |
| 406 each vertex. A higher number means that in step 3 above, nodes | |
| 407 will be chosen more like a true uniformly random sample, and a | |
| 408 lower number means that nodes are more likely to be chosen as | |
| 409 their in-degree increases. If this parameter is not positive, a | |
| 410 :exc:`ValueError` is raised. | |
| 411 | |
| 412 self_loops : bool | |
| 413 If True, self-loops are allowed when generating the graph. | |
| 414 | |
| 415 seed : integer, random_state, or None (default) | |
| 416 Indicator of random number generation state. | |
| 417 See :ref:`Randomness<randomness>`. | |
| 418 | |
| 419 Returns | |
| 420 ------- | |
| 421 :class:`~networkx.classes.MultiDiGraph` | |
| 422 A `k`-out-regular multidigraph generated according to the above | |
| 423 algorithm. | |
| 424 | |
| 425 Raises | |
| 426 ------ | |
| 427 ValueError | |
| 428 If `alpha` is not positive. | |
| 429 | |
| 430 Notes | |
| 431 ----- | |
| 432 The returned multidigraph may not be strongly connected, or even | |
| 433 weakly connected. | |
| 434 | |
| 435 References | |
| 436 ---------- | |
| 437 [1]: Peterson, Nicholas R., and Boris Pittel. | |
| 438 "Distance between two random `k`-out digraphs, with and without | |
| 439 preferential attachment." | |
| 440 arXiv preprint arXiv:1311.5961 (2013). | |
| 441 <https://arxiv.org/abs/1311.5961> | |
| 442 | |
| 443 """ | |
| 444 if alpha < 0: | |
| 445 raise ValueError('alpha must be positive') | |
| 446 G = nx.empty_graph(n, create_using=nx.MultiDiGraph) | |
| 447 weights = Counter({v: alpha for v in G}) | |
| 448 for i in range(k * n): | |
| 449 u = seed.choice([v for v, d in G.out_degree() if d < k]) | |
| 450 # If self-loops are not allowed, make the source node `u` have | |
| 451 # weight zero. | |
| 452 if not self_loops: | |
| 453 adjustment = Counter({u: weights[u]}) | |
| 454 else: | |
| 455 adjustment = Counter() | |
| 456 v = weighted_choice(weights - adjustment, seed=seed) | |
| 457 G.add_edge(u, v) | |
| 458 weights[v] += 1 | |
| 459 return G |
