Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/networkx/generators/classic.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 # Copyright (C) 2004-2019 by | |
| 2 # Aric Hagberg <hagberg@lanl.gov> | |
| 3 # Dan Schult <dschult@colgate.edu> | |
| 4 # Pieter Swart <swart@lanl.gov> | |
| 5 # All rights reserved. | |
| 6 # BSD license. | |
| 7 # | |
| 8 # Authors: Aric Hagberg (hagberg@lanl.gov) | |
| 9 # Pieter Swart (swart@lanl.gov) | |
| 10 """Generators for some classic graphs. | |
| 11 | |
| 12 The typical graph generator is called as follows: | |
| 13 | |
| 14 >>> G = nx.complete_graph(100) | |
| 15 | |
| 16 returning the complete graph on n nodes labeled 0, .., 99 | |
| 17 as a simple graph. Except for empty_graph, all the generators | |
| 18 in this module return a Graph class (i.e. a simple, undirected graph). | |
| 19 | |
| 20 """ | |
| 21 | |
| 22 import itertools | |
| 23 | |
| 24 import networkx as nx | |
| 25 from networkx.classes import Graph | |
| 26 from networkx.exception import NetworkXError | |
| 27 from networkx.utils import accumulate | |
| 28 from networkx.utils import nodes_or_number | |
| 29 from networkx.utils import pairwise | |
| 30 | |
| 31 __all__ = ['balanced_tree', | |
| 32 'barbell_graph', | |
| 33 'binomial_tree', | |
| 34 'complete_graph', | |
| 35 'complete_multipartite_graph', | |
| 36 'circular_ladder_graph', | |
| 37 'circulant_graph', | |
| 38 'cycle_graph', | |
| 39 'dorogovtsev_goltsev_mendes_graph', | |
| 40 'empty_graph', | |
| 41 'full_rary_tree', | |
| 42 'ladder_graph', | |
| 43 'lollipop_graph', | |
| 44 'null_graph', | |
| 45 'path_graph', | |
| 46 'star_graph', | |
| 47 'trivial_graph', | |
| 48 'turan_graph', | |
| 49 'wheel_graph'] | |
| 50 | |
| 51 | |
| 52 # ------------------------------------------------------------------- | |
| 53 # Some Classic Graphs | |
| 54 # ------------------------------------------------------------------- | |
| 55 | |
| 56 def _tree_edges(n, r): | |
| 57 if n == 0: | |
| 58 return | |
| 59 # helper function for trees | |
| 60 # yields edges in rooted tree at 0 with n nodes and branching ratio r | |
| 61 nodes = iter(range(n)) | |
| 62 parents = [next(nodes)] # stack of max length r | |
| 63 while parents: | |
| 64 source = parents.pop(0) | |
| 65 for i in range(r): | |
| 66 try: | |
| 67 target = next(nodes) | |
| 68 parents.append(target) | |
| 69 yield source, target | |
| 70 except StopIteration: | |
| 71 break | |
| 72 | |
| 73 | |
| 74 def full_rary_tree(r, n, create_using=None): | |
| 75 """Creates a full r-ary tree of n vertices. | |
| 76 | |
| 77 Sometimes called a k-ary, n-ary, or m-ary tree. | |
| 78 "... all non-leaf vertices have exactly r children and all levels | |
| 79 are full except for some rightmost position of the bottom level | |
| 80 (if a leaf at the bottom level is missing, then so are all of the | |
| 81 leaves to its right." [1]_ | |
| 82 | |
| 83 Parameters | |
| 84 ---------- | |
| 85 r : int | |
| 86 branching factor of the tree | |
| 87 n : int | |
| 88 Number of nodes in the tree | |
| 89 create_using : NetworkX graph constructor, optional (default=nx.Graph) | |
| 90 Graph type to create. If graph instance, then cleared before populated. | |
| 91 | |
| 92 Returns | |
| 93 ------- | |
| 94 G : networkx Graph | |
| 95 An r-ary tree with n nodes | |
| 96 | |
| 97 References | |
| 98 ---------- | |
| 99 .. [1] An introduction to data structures and algorithms, | |
| 100 James Andrew Storer, Birkhauser Boston 2001, (page 225). | |
| 101 """ | |
| 102 G = empty_graph(n, create_using) | |
| 103 G.add_edges_from(_tree_edges(n, r)) | |
| 104 return G | |
| 105 | |
| 106 | |
| 107 def balanced_tree(r, h, create_using=None): | |
| 108 """Returns the perfectly balanced `r`-ary tree of height `h`. | |
| 109 | |
| 110 Parameters | |
| 111 ---------- | |
| 112 r : int | |
| 113 Branching factor of the tree; each node will have `r` | |
| 114 children. | |
| 115 | |
| 116 h : int | |
| 117 Height of the tree. | |
| 118 | |
| 119 create_using : NetworkX graph constructor, optional (default=nx.Graph) | |
| 120 Graph type to create. If graph instance, then cleared before populated. | |
| 121 | |
| 122 Returns | |
| 123 ------- | |
| 124 G : NetworkX graph | |
| 125 A balanced `r`-ary tree of height `h`. | |
| 126 | |
| 127 Notes | |
| 128 ----- | |
| 129 This is the rooted tree where all leaves are at distance `h` from | |
| 130 the root. The root has degree `r` and all other internal nodes | |
| 131 have degree `r + 1`. | |
| 132 | |
| 133 Node labels are integers, starting from zero. | |
| 134 | |
| 135 A balanced tree is also known as a *complete r-ary tree*. | |
| 136 | |
| 137 """ | |
| 138 # The number of nodes in the balanced tree is `1 + r + ... + r^h`, | |
| 139 # which is computed by using the closed-form formula for a geometric | |
| 140 # sum with ratio `r`. In the special case that `r` is 1, the number | |
| 141 # of nodes is simply `h + 1` (since the tree is actually a path | |
| 142 # graph). | |
| 143 if r == 1: | |
| 144 n = h + 1 | |
| 145 else: | |
| 146 # This must be an integer if both `r` and `h` are integers. If | |
| 147 # they are not, we force integer division anyway. | |
| 148 n = (1 - r ** (h + 1)) // (1 - r) | |
| 149 return full_rary_tree(r, n, create_using=create_using) | |
| 150 | |
| 151 | |
| 152 def barbell_graph(m1, m2, create_using=None): | |
| 153 """Returns the Barbell Graph: two complete graphs connected by a path. | |
| 154 | |
| 155 For $m1 > 1$ and $m2 >= 0$. | |
| 156 | |
| 157 Two identical complete graphs $K_{m1}$ form the left and right bells, | |
| 158 and are connected by a path $P_{m2}$. | |
| 159 | |
| 160 The `2*m1+m2` nodes are numbered | |
| 161 `0, ..., m1-1` for the left barbell, | |
| 162 `m1, ..., m1+m2-1` for the path, | |
| 163 and `m1+m2, ..., 2*m1+m2-1` for the right barbell. | |
| 164 | |
| 165 The 3 subgraphs are joined via the edges `(m1-1, m1)` and | |
| 166 `(m1+m2-1, m1+m2)`. If `m2=0`, this is merely two complete | |
| 167 graphs joined together. | |
| 168 | |
| 169 This graph is an extremal example in David Aldous | |
| 170 and Jim Fill's e-text on Random Walks on Graphs. | |
| 171 | |
| 172 """ | |
| 173 if m1 < 2: | |
| 174 raise NetworkXError( | |
| 175 "Invalid graph description, m1 should be >=2") | |
| 176 if m2 < 0: | |
| 177 raise NetworkXError( | |
| 178 "Invalid graph description, m2 should be >=0") | |
| 179 | |
| 180 # left barbell | |
| 181 G = complete_graph(m1, create_using) | |
| 182 if G.is_directed(): | |
| 183 raise NetworkXError("Directed Graph not supported") | |
| 184 | |
| 185 # connecting path | |
| 186 G.add_nodes_from(range(m1, m1 + m2 - 1)) | |
| 187 if m2 > 1: | |
| 188 G.add_edges_from(pairwise(range(m1, m1 + m2))) | |
| 189 # right barbell | |
| 190 G.add_edges_from((u, v) for u in range(m1 + m2, 2 * m1 + m2) | |
| 191 for v in range(u + 1, 2 * m1 + m2)) | |
| 192 # connect it up | |
| 193 G.add_edge(m1 - 1, m1) | |
| 194 if m2 > 0: | |
| 195 G.add_edge(m1 + m2 - 1, m1 + m2) | |
| 196 return G | |
| 197 | |
| 198 def binomial_tree(n): | |
| 199 """Returns the Binomial Tree of order n. | |
| 200 | |
| 201 The binomial tree of order 0 consists of a single vertex. A binomial tree of order k | |
| 202 is defined recursively by linking two binomial trees of order k-1: the root of one is | |
| 203 the leftmost child of the root of the other. | |
| 204 | |
| 205 Parameters | |
| 206 ---------- | |
| 207 n : int | |
| 208 Order of the binomial tree. | |
| 209 | |
| 210 Returns | |
| 211 ------- | |
| 212 G : NetworkX graph | |
| 213 A binomial tree of $2^n$ vertices and $2^n - 1$ edges. | |
| 214 | |
| 215 """ | |
| 216 G = nx.empty_graph(1) | |
| 217 N = 1 | |
| 218 for i in range(n): | |
| 219 edges = [(u + N, v + N) for (u, v) in G.edges] | |
| 220 G.add_edges_from(edges) | |
| 221 G.add_edge(0,N) | |
| 222 N *= 2 | |
| 223 return G | |
| 224 | |
| 225 @nodes_or_number(0) | |
| 226 def complete_graph(n, create_using=None): | |
| 227 """ Return the complete graph `K_n` with n nodes. | |
| 228 | |
| 229 Parameters | |
| 230 ---------- | |
| 231 n : int or iterable container of nodes | |
| 232 If n is an integer, nodes are from range(n). | |
| 233 If n is a container of nodes, those nodes appear in the graph. | |
| 234 create_using : NetworkX graph constructor, optional (default=nx.Graph) | |
| 235 Graph type to create. If graph instance, then cleared before populated. | |
| 236 | |
| 237 Examples | |
| 238 -------- | |
| 239 >>> G = nx.complete_graph(9) | |
| 240 >>> len(G) | |
| 241 9 | |
| 242 >>> G.size() | |
| 243 36 | |
| 244 >>> G = nx.complete_graph(range(11, 14)) | |
| 245 >>> list(G.nodes()) | |
| 246 [11, 12, 13] | |
| 247 >>> G = nx.complete_graph(4, nx.DiGraph()) | |
| 248 >>> G.is_directed() | |
| 249 True | |
| 250 | |
| 251 """ | |
| 252 n_name, nodes = n | |
| 253 G = empty_graph(n_name, create_using) | |
| 254 if len(nodes) > 1: | |
| 255 if G.is_directed(): | |
| 256 edges = itertools.permutations(nodes, 2) | |
| 257 else: | |
| 258 edges = itertools.combinations(nodes, 2) | |
| 259 G.add_edges_from(edges) | |
| 260 return G | |
| 261 | |
| 262 | |
| 263 def circular_ladder_graph(n, create_using=None): | |
| 264 """Returns the circular ladder graph $CL_n$ of length n. | |
| 265 | |
| 266 $CL_n$ consists of two concentric n-cycles in which | |
| 267 each of the n pairs of concentric nodes are joined by an edge. | |
| 268 | |
| 269 Node labels are the integers 0 to n-1 | |
| 270 | |
| 271 """ | |
| 272 G = ladder_graph(n, create_using) | |
| 273 G.add_edge(0, n - 1) | |
| 274 G.add_edge(n, 2 * n - 1) | |
| 275 return G | |
| 276 | |
| 277 | |
| 278 def circulant_graph(n, offsets, create_using=None): | |
| 279 """Generates the circulant graph $Ci_n(x_1, x_2, ..., x_m)$ with $n$ vertices. | |
| 280 | |
| 281 Returns | |
| 282 ------- | |
| 283 The graph $Ci_n(x_1, ..., x_m)$ consisting of $n$ vertices $0, ..., n-1$ such | |
| 284 that the vertex with label $i$ is connected to the vertices labelled $(i + x)$ | |
| 285 and $(i - x)$, for all $x$ in $x_1$ up to $x_m$, with the indices taken modulo $n$. | |
| 286 | |
| 287 Parameters | |
| 288 ---------- | |
| 289 n : integer | |
| 290 The number of vertices the generated graph is to contain. | |
| 291 offsets : list of integers | |
| 292 A list of vertex offsets, $x_1$ up to $x_m$, as described above. | |
| 293 create_using : NetworkX graph constructor, optional (default=nx.Graph) | |
| 294 Graph type to create. If graph instance, then cleared before populated. | |
| 295 | |
| 296 Examples | |
| 297 -------- | |
| 298 Many well-known graph families are subfamilies of the circulant graphs; | |
| 299 for example, to generate the cycle graph on n points, we connect every | |
| 300 vertex to every other at offset plus or minus one. For n = 10, | |
| 301 | |
| 302 >>> import networkx | |
| 303 >>> G = networkx.generators.classic.circulant_graph(10, [1]) | |
| 304 >>> edges = [ | |
| 305 ... (0, 9), (0, 1), (1, 2), (2, 3), (3, 4), | |
| 306 ... (4, 5), (5, 6), (6, 7), (7, 8), (8, 9)] | |
| 307 ... | |
| 308 >>> sorted(edges) == sorted(G.edges()) | |
| 309 True | |
| 310 | |
| 311 Similarly, we can generate the complete graph on 5 points with the set of | |
| 312 offsets [1, 2]: | |
| 313 | |
| 314 >>> G = networkx.generators.classic.circulant_graph(5, [1, 2]) | |
| 315 >>> edges = [ | |
| 316 ... (0, 1), (0, 2), (0, 3), (0, 4), (1, 2), | |
| 317 ... (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)] | |
| 318 ... | |
| 319 >>> sorted(edges) == sorted(G.edges()) | |
| 320 True | |
| 321 | |
| 322 """ | |
| 323 G = empty_graph(n, create_using) | |
| 324 for i in range(n): | |
| 325 for j in offsets: | |
| 326 G.add_edge(i, (i - j) % n) | |
| 327 G.add_edge(i, (i + j) % n) | |
| 328 return G | |
| 329 | |
| 330 | |
| 331 @nodes_or_number(0) | |
| 332 def cycle_graph(n, create_using=None): | |
| 333 """Returns the cycle graph $C_n$ of cyclically connected nodes. | |
| 334 | |
| 335 $C_n$ is a path with its two end-nodes connected. | |
| 336 | |
| 337 Parameters | |
| 338 ---------- | |
| 339 n : int or iterable container of nodes | |
| 340 If n is an integer, nodes are from `range(n)`. | |
| 341 If n is a container of nodes, those nodes appear in the graph. | |
| 342 create_using : NetworkX graph constructor, optional (default=nx.Graph) | |
| 343 Graph type to create. If graph instance, then cleared before populated. | |
| 344 | |
| 345 Notes | |
| 346 ----- | |
| 347 If create_using is directed, the direction is in increasing order. | |
| 348 | |
| 349 """ | |
| 350 n_orig, nodes = n | |
| 351 G = empty_graph(nodes, create_using) | |
| 352 G.add_edges_from(pairwise(nodes)) | |
| 353 G.add_edge(nodes[-1], nodes[0]) | |
| 354 return G | |
| 355 | |
| 356 | |
| 357 def dorogovtsev_goltsev_mendes_graph(n, create_using=None): | |
| 358 """Returns the hierarchically constructed Dorogovtsev-Goltsev-Mendes graph. | |
| 359 | |
| 360 n is the generation. | |
| 361 See: arXiv:/cond-mat/0112143 by Dorogovtsev, Goltsev and Mendes. | |
| 362 | |
| 363 """ | |
| 364 G = empty_graph(0, create_using) | |
| 365 if G.is_directed(): | |
| 366 raise NetworkXError("Directed Graph not supported") | |
| 367 if G.is_multigraph(): | |
| 368 raise NetworkXError("Multigraph not supported") | |
| 369 | |
| 370 G.add_edge(0, 1) | |
| 371 if n == 0: | |
| 372 return G | |
| 373 new_node = 2 # next node to be added | |
| 374 for i in range(1, n + 1): # iterate over number of generations. | |
| 375 last_generation_edges = list(G.edges()) | |
| 376 number_of_edges_in_last_generation = len(last_generation_edges) | |
| 377 for j in range(0, number_of_edges_in_last_generation): | |
| 378 G.add_edge(new_node, last_generation_edges[j][0]) | |
| 379 G.add_edge(new_node, last_generation_edges[j][1]) | |
| 380 new_node += 1 | |
| 381 return G | |
| 382 | |
| 383 | |
| 384 @nodes_or_number(0) | |
| 385 def empty_graph(n=0, create_using=None, default=nx.Graph): | |
| 386 """Returns the empty graph with n nodes and zero edges. | |
| 387 | |
| 388 Parameters | |
| 389 ---------- | |
| 390 n : int or iterable container of nodes (default = 0) | |
| 391 If n is an integer, nodes are from `range(n)`. | |
| 392 If n is a container of nodes, those nodes appear in the graph. | |
| 393 create_using : Graph Instance, Constructor or None | |
| 394 Indicator of type of graph to return. | |
| 395 If a Graph-type instance, then clear and use it. | |
| 396 If None, use the `default` constructor. | |
| 397 If a constructor, call it to create an empty graph. | |
| 398 default : Graph constructor (optional, default = nx.Graph) | |
| 399 The constructor to use if create_using is None. | |
| 400 If None, then nx.Graph is used. | |
| 401 This is used when passing an unknown `create_using` value | |
| 402 through your home-grown function to `empty_graph` and | |
| 403 you want a default constructor other than nx.Graph. | |
| 404 | |
| 405 Examples | |
| 406 -------- | |
| 407 >>> G = nx.empty_graph(10) | |
| 408 >>> G.number_of_nodes() | |
| 409 10 | |
| 410 >>> G.number_of_edges() | |
| 411 0 | |
| 412 >>> G = nx.empty_graph("ABC") | |
| 413 >>> G.number_of_nodes() | |
| 414 3 | |
| 415 >>> sorted(G) | |
| 416 ['A', 'B', 'C'] | |
| 417 | |
| 418 Notes | |
| 419 ----- | |
| 420 The variable create_using should be a Graph Constructor or a | |
| 421 "graph"-like object. Constructors, e.g. `nx.Graph` or `nx.MultiGraph` | |
| 422 will be used to create the returned graph. "graph"-like objects | |
| 423 will be cleared (nodes and edges will be removed) and refitted as | |
| 424 an empty "graph" with nodes specified in n. This capability | |
| 425 is useful for specifying the class-nature of the resulting empty | |
| 426 "graph" (i.e. Graph, DiGraph, MyWeirdGraphClass, etc.). | |
| 427 | |
| 428 The variable create_using has three main uses: | |
| 429 Firstly, the variable create_using can be used to create an | |
| 430 empty digraph, multigraph, etc. For example, | |
| 431 | |
| 432 >>> n = 10 | |
| 433 >>> G = nx.empty_graph(n, create_using=nx.DiGraph) | |
| 434 | |
| 435 will create an empty digraph on n nodes. | |
| 436 | |
| 437 Secondly, one can pass an existing graph (digraph, multigraph, | |
| 438 etc.) via create_using. For example, if G is an existing graph | |
| 439 (resp. digraph, multigraph, etc.), then empty_graph(n, create_using=G) | |
| 440 will empty G (i.e. delete all nodes and edges using G.clear()) | |
| 441 and then add n nodes and zero edges, and return the modified graph. | |
| 442 | |
| 443 Thirdly, when constructing your home-grown graph creation function | |
| 444 you can use empty_graph to construct the graph by passing a user | |
| 445 defined create_using to empty_graph. In this case, if you want the | |
| 446 default constructor to be other than nx.Graph, specify `default`. | |
| 447 | |
| 448 >>> def mygraph(n, create_using=None): | |
| 449 ... G = nx.empty_graph(n, create_using, nx.MultiGraph) | |
| 450 ... G.add_edges_from([(0, 1), (0, 1)]) | |
| 451 ... return G | |
| 452 >>> G = mygraph(3) | |
| 453 >>> G.is_multigraph() | |
| 454 True | |
| 455 >>> G = mygraph(3, nx.Graph) | |
| 456 >>> G.is_multigraph() | |
| 457 False | |
| 458 | |
| 459 See also create_empty_copy(G). | |
| 460 | |
| 461 """ | |
| 462 if create_using is None: | |
| 463 G = default() | |
| 464 elif hasattr(create_using, '_adj'): | |
| 465 # create_using is a NetworkX style Graph | |
| 466 create_using.clear() | |
| 467 G = create_using | |
| 468 else: | |
| 469 # try create_using as constructor | |
| 470 G = create_using() | |
| 471 | |
| 472 n_name, nodes = n | |
| 473 G.add_nodes_from(nodes) | |
| 474 return G | |
| 475 | |
| 476 | |
| 477 def ladder_graph(n, create_using=None): | |
| 478 """Returns the Ladder graph of length n. | |
| 479 | |
| 480 This is two paths of n nodes, with | |
| 481 each pair connected by a single edge. | |
| 482 | |
| 483 Node labels are the integers 0 to 2*n - 1. | |
| 484 | |
| 485 """ | |
| 486 G = empty_graph(2 * n, create_using) | |
| 487 if G.is_directed(): | |
| 488 raise NetworkXError("Directed Graph not supported") | |
| 489 G.add_edges_from(pairwise(range(n))) | |
| 490 G.add_edges_from(pairwise(range(n, 2 * n))) | |
| 491 G.add_edges_from((v, v + n) for v in range(n)) | |
| 492 return G | |
| 493 | |
| 494 | |
| 495 @nodes_or_number([0, 1]) | |
| 496 def lollipop_graph(m, n, create_using=None): | |
| 497 """Returns the Lollipop Graph; `K_m` connected to `P_n`. | |
| 498 | |
| 499 This is the Barbell Graph without the right barbell. | |
| 500 | |
| 501 Parameters | |
| 502 ---------- | |
| 503 m, n : int or iterable container of nodes (default = 0) | |
| 504 If an integer, nodes are from `range(m)` and `range(m,m+n)`. | |
| 505 If a container, the entries are the coordinate of the node. | |
| 506 | |
| 507 The nodes for m appear in the complete graph $K_m$ and the nodes | |
| 508 for n appear in the path $P_n$ | |
| 509 create_using : NetworkX graph constructor, optional (default=nx.Graph) | |
| 510 Graph type to create. If graph instance, then cleared before populated. | |
| 511 | |
| 512 Notes | |
| 513 ----- | |
| 514 The 2 subgraphs are joined via an edge (m-1, m). | |
| 515 If n=0, this is merely a complete graph. | |
| 516 | |
| 517 (This graph is an extremal example in David Aldous and Jim | |
| 518 Fill's etext on Random Walks on Graphs.) | |
| 519 | |
| 520 """ | |
| 521 m, m_nodes = m | |
| 522 n, n_nodes = n | |
| 523 M = len(m_nodes) | |
| 524 N = len(n_nodes) | |
| 525 if isinstance(m, int): | |
| 526 n_nodes = [len(m_nodes) + i for i in n_nodes] | |
| 527 if M < 2: | |
| 528 raise NetworkXError( | |
| 529 "Invalid graph description, m should be >=2") | |
| 530 if N < 0: | |
| 531 raise NetworkXError( | |
| 532 "Invalid graph description, n should be >=0") | |
| 533 | |
| 534 # the ball | |
| 535 G = complete_graph(m_nodes, create_using) | |
| 536 if G.is_directed(): | |
| 537 raise NetworkXError("Directed Graph not supported") | |
| 538 # the stick | |
| 539 G.add_nodes_from(n_nodes) | |
| 540 if N > 1: | |
| 541 G.add_edges_from(pairwise(n_nodes)) | |
| 542 # connect ball to stick | |
| 543 if M > 0 and N > 0: | |
| 544 G.add_edge(m_nodes[-1], n_nodes[0]) | |
| 545 return G | |
| 546 | |
| 547 | |
| 548 def null_graph(create_using=None): | |
| 549 """Returns the Null graph with no nodes or edges. | |
| 550 | |
| 551 See empty_graph for the use of create_using. | |
| 552 | |
| 553 """ | |
| 554 G = empty_graph(0, create_using) | |
| 555 return G | |
| 556 | |
| 557 | |
| 558 @nodes_or_number(0) | |
| 559 def path_graph(n, create_using=None): | |
| 560 """Returns the Path graph `P_n` of linearly connected nodes. | |
| 561 | |
| 562 Parameters | |
| 563 ---------- | |
| 564 n : int or iterable | |
| 565 If an integer, node labels are 0 to n with center 0. | |
| 566 If an iterable of nodes, the center is the first. | |
| 567 create_using : NetworkX graph constructor, optional (default=nx.Graph) | |
| 568 Graph type to create. If graph instance, then cleared before populated. | |
| 569 | |
| 570 """ | |
| 571 n_name, nodes = n | |
| 572 G = empty_graph(nodes, create_using) | |
| 573 G.add_edges_from(pairwise(nodes)) | |
| 574 return G | |
| 575 | |
| 576 | |
| 577 @nodes_or_number(0) | |
| 578 def star_graph(n, create_using=None): | |
| 579 """ Return the star graph | |
| 580 | |
| 581 The star graph consists of one center node connected to n outer nodes. | |
| 582 | |
| 583 Parameters | |
| 584 ---------- | |
| 585 n : int or iterable | |
| 586 If an integer, node labels are 0 to n with center 0. | |
| 587 If an iterable of nodes, the center is the first. | |
| 588 create_using : NetworkX graph constructor, optional (default=nx.Graph) | |
| 589 Graph type to create. If graph instance, then cleared before populated. | |
| 590 | |
| 591 Notes | |
| 592 ----- | |
| 593 The graph has n+1 nodes for integer n. | |
| 594 So star_graph(3) is the same as star_graph(range(4)). | |
| 595 """ | |
| 596 n_name, nodes = n | |
| 597 if isinstance(n_name, int): | |
| 598 nodes = nodes + [n_name] # there should be n+1 nodes | |
| 599 first = nodes[0] | |
| 600 G = empty_graph(nodes, create_using) | |
| 601 if G.is_directed(): | |
| 602 raise NetworkXError("Directed Graph not supported") | |
| 603 G.add_edges_from((first, v) for v in nodes[1:]) | |
| 604 return G | |
| 605 | |
| 606 | |
| 607 def trivial_graph(create_using=None): | |
| 608 """ Return the Trivial graph with one node (with label 0) and no edges. | |
| 609 | |
| 610 """ | |
| 611 G = empty_graph(1, create_using) | |
| 612 return G | |
| 613 | |
| 614 | |
| 615 def turan_graph(n, r): | |
| 616 r""" Return the Turan Graph | |
| 617 | |
| 618 The Turan Graph is a complete multipartite graph on $n$ vertices | |
| 619 with $r$ disjoint subsets. It is the graph with the edges for any graph with | |
| 620 $n$ vertices and $r$ disjoint subsets. | |
| 621 | |
| 622 Given $n$ and $r$, we generate a complete multipartite graph with | |
| 623 $r-(n \mod r)$ partitions of size $n/r$, rounded down, and | |
| 624 $n \mod r$ partitions of size $n/r+1$, rounded down. | |
| 625 | |
| 626 Parameters | |
| 627 ---------- | |
| 628 n : int | |
| 629 The number of vertices. | |
| 630 r : int | |
| 631 The number of partitions. | |
| 632 Must be less than or equal to n. | |
| 633 | |
| 634 Notes | |
| 635 ----- | |
| 636 Must satisfy $1 <= r <= n$. | |
| 637 The graph has $(r-1)(n^2)/(2r)$ edges, rounded down. | |
| 638 """ | |
| 639 | |
| 640 if not 1 <= r <= n: | |
| 641 raise NetworkXError("Must satisfy 1 <= r <= n") | |
| 642 | |
| 643 partitions = [n // r] * (r - (n % r)) + [n // r + 1] * (n % r) | |
| 644 G = complete_multipartite_graph(*partitions) | |
| 645 return G | |
| 646 | |
| 647 | |
| 648 @nodes_or_number(0) | |
| 649 def wheel_graph(n, create_using=None): | |
| 650 """ Return the wheel graph | |
| 651 | |
| 652 The wheel graph consists of a hub node connected to a cycle of (n-1) nodes. | |
| 653 | |
| 654 Parameters | |
| 655 ---------- | |
| 656 n : int or iterable | |
| 657 If an integer, node labels are 0 to n with center 0. | |
| 658 If an iterable of nodes, the center is the first. | |
| 659 create_using : NetworkX graph constructor, optional (default=nx.Graph) | |
| 660 Graph type to create. If graph instance, then cleared before populated. | |
| 661 | |
| 662 Node labels are the integers 0 to n - 1. | |
| 663 """ | |
| 664 n_name, nodes = n | |
| 665 if n_name == 0: | |
| 666 G = empty_graph(0, create_using) | |
| 667 return G | |
| 668 G = star_graph(nodes, create_using) | |
| 669 if len(G) > 2: | |
| 670 G.add_edges_from(pairwise(nodes[1:])) | |
| 671 G.add_edge(nodes[-1], nodes[1]) | |
| 672 return G | |
| 673 | |
| 674 | |
| 675 def complete_multipartite_graph(*subset_sizes): | |
| 676 """Returns the complete multipartite graph with the specified subset sizes. | |
| 677 | |
| 678 Parameters | |
| 679 ---------- | |
| 680 subset_sizes : tuple of integers or tuple of node iterables | |
| 681 The arguments can either all be integer number of nodes or they | |
| 682 can all be iterables of nodes. If integers, they represent the | |
| 683 number of vertices in each subset of the multipartite graph. | |
| 684 If iterables, each is used to create the nodes for that subset. | |
| 685 The length of subset_sizes is the number of subsets. | |
| 686 | |
| 687 Returns | |
| 688 ------- | |
| 689 G : NetworkX Graph | |
| 690 Returns the complete multipartite graph with the specified subsets. | |
| 691 | |
| 692 For each node, the node attribute 'subset' is an integer | |
| 693 indicating which subset contains the node. | |
| 694 | |
| 695 Examples | |
| 696 -------- | |
| 697 Creating a complete tripartite graph, with subsets of one, two, and three | |
| 698 vertices, respectively. | |
| 699 | |
| 700 >>> import networkx as nx | |
| 701 >>> G = nx.complete_multipartite_graph(1, 2, 3) | |
| 702 >>> [G.nodes[u]['subset'] for u in G] | |
| 703 [0, 1, 1, 2, 2, 2] | |
| 704 >>> list(G.edges(0)) | |
| 705 [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)] | |
| 706 >>> list(G.edges(2)) | |
| 707 [(2, 0), (2, 3), (2, 4), (2, 5)] | |
| 708 >>> list(G.edges(4)) | |
| 709 [(4, 0), (4, 1), (4, 2)] | |
| 710 | |
| 711 >>> G = nx.complete_multipartite_graph('a', 'bc', 'def') | |
| 712 >>> [G.nodes[u]['subset'] for u in sorted(G)] | |
| 713 [0, 1, 1, 2, 2, 2] | |
| 714 | |
| 715 Notes | |
| 716 ----- | |
| 717 This function generalizes several other graph generator functions. | |
| 718 | |
| 719 - If no subset sizes are given, this returns the null graph. | |
| 720 - If a single subset size `n` is given, this returns the empty graph on | |
| 721 `n` nodes. | |
| 722 - If two subset sizes `m` and `n` are given, this returns the complete | |
| 723 bipartite graph on `m + n` nodes. | |
| 724 - If subset sizes `1` and `n` are given, this returns the star graph on | |
| 725 `n + 1` nodes. | |
| 726 | |
| 727 See also | |
| 728 -------- | |
| 729 complete_bipartite_graph | |
| 730 """ | |
| 731 # The complete multipartite graph is an undirected simple graph. | |
| 732 G = Graph() | |
| 733 | |
| 734 if len(subset_sizes) == 0: | |
| 735 return G | |
| 736 | |
| 737 # set up subsets of nodes | |
| 738 try: | |
| 739 extents = pairwise(accumulate((0,) + subset_sizes)) | |
| 740 subsets = [range(start, end) for start, end in extents] | |
| 741 except TypeError: | |
| 742 subsets = subset_sizes | |
| 743 | |
| 744 # add nodes with subset attribute | |
| 745 # while checking that ints are not mixed with iterables | |
| 746 try: | |
| 747 for (i, subset) in enumerate(subsets): | |
| 748 G.add_nodes_from(subset, subset=i) | |
| 749 except TypeError: | |
| 750 raise NetworkXError("Arguments must be all ints or all iterables") | |
| 751 | |
| 752 # Across subsets, all vertices should be adjacent. | |
| 753 # We can use itertools.combinations() because undirected. | |
| 754 for subset1, subset2 in itertools.combinations(subsets, 2): | |
| 755 G.add_edges_from(itertools.product(subset1, subset2)) | |
| 756 return G |
