1"""Generators for some classic graphs.
2
3The typical graph builder function is called as follows:
4
5>>> G = nx.complete_graph(100)
6
7returning the complete graph on n nodes labeled 0, .., 99
8as a simple graph. Except for `empty_graph`, all the functions
9in this module return a Graph class (i.e. a simple, undirected graph).
10
11"""
12
13import itertools
14import numbers
15
16import networkx as nx
17from networkx.classes import Graph
18from networkx.exception import NetworkXError
19from networkx.utils import nodes_or_number, pairwise
20
21__all__ = [
22 "balanced_tree",
23 "barbell_graph",
24 "binomial_tree",
25 "complete_graph",
26 "complete_multipartite_graph",
27 "circular_ladder_graph",
28 "circulant_graph",
29 "cycle_graph",
30 "dorogovtsev_goltsev_mendes_graph",
31 "empty_graph",
32 "full_rary_tree",
33 "kneser_graph",
34 "ladder_graph",
35 "lollipop_graph",
36 "null_graph",
37 "path_graph",
38 "star_graph",
39 "tadpole_graph",
40 "trivial_graph",
41 "turan_graph",
42 "wheel_graph",
43]
44
45
46# -------------------------------------------------------------------
47# Some Classic Graphs
48# -------------------------------------------------------------------
49
50
51def _tree_edges(n, r):
52 if n == 0:
53 return
54 # helper function for trees
55 # yields edges in rooted tree at 0 with n nodes and branching ratio r
56 nodes = iter(range(n))
57 parents = [next(nodes)] # stack of max length r
58 while parents:
59 source = parents.pop(0)
60 for i in range(r):
61 try:
62 target = next(nodes)
63 parents.append(target)
64 yield source, target
65 except StopIteration:
66 break
67
68
69@nx._dispatchable(graphs=None, returns_graph=True)
70def full_rary_tree(r, n, create_using=None):
71 """Creates a full r-ary tree of `n` nodes.
72
73 Sometimes called a k-ary, n-ary, or m-ary tree.
74 "... all non-leaf nodes have exactly r children and all levels
75 are full except for some rightmost position of the bottom level
76 (if a leaf at the bottom level is missing, then so are all of the
77 leaves to its right." [1]_
78
79 .. plot::
80
81 >>> nx.draw(nx.full_rary_tree(2, 10))
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@nx._dispatchable(graphs=None, returns_graph=True)
108def kneser_graph(n, k):
109 """Returns the Kneser Graph with parameters `n` and `k`.
110
111 The Kneser Graph has nodes that are k-tuples (subsets) of the integers
112 between 0 and ``n-1``. Nodes are adjacent if their corresponding sets are disjoint.
113
114 Parameters
115 ----------
116 n: int
117 Number of integers from which to make node subsets.
118 Subsets are drawn from ``set(range(n))``.
119 k: int
120 Size of the subsets.
121
122 Returns
123 -------
124 G : NetworkX Graph
125
126 Examples
127 --------
128 >>> G = nx.kneser_graph(5, 2)
129 >>> G.number_of_nodes()
130 10
131 >>> G.number_of_edges()
132 15
133 >>> nx.is_isomorphic(G, nx.petersen_graph())
134 True
135 """
136 if n <= 0:
137 raise NetworkXError("n should be greater than zero")
138 if k <= 0 or k > n:
139 raise NetworkXError("k should be greater than zero and smaller than n")
140
141 G = nx.Graph()
142 # Create all k-subsets of [0, 1, ..., n-1]
143 subsets = list(itertools.combinations(range(n), k))
144
145 if 2 * k > n:
146 G.add_nodes_from(subsets)
147
148 universe = set(range(n))
149 comb = itertools.combinations # only to make it all fit on one line
150 G.add_edges_from((s, t) for s in subsets for t in comb(universe - set(s), k))
151 return G
152
153
154@nx._dispatchable(graphs=None, returns_graph=True)
155def balanced_tree(r, h, create_using=None):
156 """Returns the perfectly balanced `r`-ary tree of height `h`.
157
158 .. plot::
159
160 >>> nx.draw(nx.balanced_tree(2, 3))
161
162 Parameters
163 ----------
164 r : int
165 Branching factor of the tree; each node will have `r`
166 children.
167
168 h : int
169 Height of the tree.
170
171 create_using : NetworkX graph constructor, optional (default=nx.Graph)
172 Graph type to create. If graph instance, then cleared before populated.
173
174 Returns
175 -------
176 G : NetworkX graph
177 A balanced `r`-ary tree of height `h`.
178
179 Notes
180 -----
181 This is the rooted tree where all leaves are at distance `h` from
182 the root. The root has degree `r` and all other internal nodes
183 have degree `r + 1`.
184
185 Node labels are integers, starting from zero.
186
187 A balanced tree is also known as a *complete r-ary tree*.
188
189 """
190 # The number of nodes in the balanced tree is `1 + r + ... + r^h`,
191 # which is computed by using the closed-form formula for a geometric
192 # sum with ratio `r`. In the special case that `r` is 1, the number
193 # of nodes is simply `h + 1` (since the tree is actually a path
194 # graph).
195 if r == 1:
196 n = h + 1
197 else:
198 # This must be an integer if both `r` and `h` are integers. If
199 # they are not, we force integer division anyway.
200 n = (1 - r ** (h + 1)) // (1 - r)
201 return full_rary_tree(r, n, create_using=create_using)
202
203
204@nx._dispatchable(graphs=None, returns_graph=True)
205def barbell_graph(m1, m2, create_using=None):
206 """Returns the Barbell Graph: two complete graphs connected by a path.
207
208 .. plot::
209
210 >>> nx.draw(nx.barbell_graph(4, 2))
211
212 Parameters
213 ----------
214 m1 : int
215 Size of the left and right barbells, must be greater than 2.
216
217 m2 : int
218 Length of the path connecting the barbells.
219
220 create_using : NetworkX graph constructor, optional (default=nx.Graph)
221 Graph type to create. If graph instance, then cleared before populated.
222 Only undirected Graphs are supported.
223
224 Returns
225 -------
226 G : NetworkX graph
227 A barbell graph.
228
229 Notes
230 -----
231
232
233 Two identical complete graphs $K_{m1}$ form the left and right bells,
234 and are connected by a path $P_{m2}$.
235
236 The `2*m1+m2` nodes are numbered
237 `0, ..., m1-1` for the left barbell,
238 `m1, ..., m1+m2-1` for the path,
239 and `m1+m2, ..., 2*m1+m2-1` for the right barbell.
240
241 The 3 subgraphs are joined via the edges `(m1-1, m1)` and
242 `(m1+m2-1, m1+m2)`. If `m2=0`, this is merely two complete
243 graphs joined together.
244
245 This graph is an extremal example in David Aldous
246 and Jim Fill's e-text on Random Walks on Graphs.
247
248 """
249 if m1 < 2:
250 raise NetworkXError("Invalid graph description, m1 should be >=2")
251 if m2 < 0:
252 raise NetworkXError("Invalid graph description, m2 should be >=0")
253
254 # left barbell
255 G = complete_graph(m1, create_using)
256 if G.is_directed():
257 raise NetworkXError("Directed Graph not supported")
258
259 # connecting path
260 G.add_nodes_from(range(m1, m1 + m2 - 1))
261 if m2 > 1:
262 G.add_edges_from(pairwise(range(m1, m1 + m2)))
263
264 # right barbell
265 G.add_edges_from(
266 (u, v) for u in range(m1 + m2, 2 * m1 + m2) for v in range(u + 1, 2 * m1 + m2)
267 )
268
269 # connect it up
270 G.add_edge(m1 - 1, m1)
271 if m2 > 0:
272 G.add_edge(m1 + m2 - 1, m1 + m2)
273
274 return G
275
276
277@nx._dispatchable(graphs=None, returns_graph=True)
278def binomial_tree(n, create_using=None):
279 """Returns the Binomial Tree of order n.
280
281 The binomial tree of order 0 consists of a single node. A binomial tree of order k
282 is defined recursively by linking two binomial trees of order k-1: the root of one is
283 the leftmost child of the root of the other.
284
285 .. plot::
286
287 >>> nx.draw(nx.binomial_tree(3))
288
289 Parameters
290 ----------
291 n : int
292 Order of the binomial tree.
293
294 create_using : NetworkX graph constructor, optional (default=nx.Graph)
295 Graph type to create. If graph instance, then cleared before populated.
296
297 Returns
298 -------
299 G : NetworkX graph
300 A binomial tree of $2^n$ nodes and $2^n - 1$ edges.
301
302 """
303 G = nx.empty_graph(1, create_using)
304
305 N = 1
306 for i in range(n):
307 # Use G.edges() to ensure 2-tuples. G.edges is 3-tuple for MultiGraph
308 edges = [(u + N, v + N) for (u, v) in G.edges()]
309 G.add_edges_from(edges)
310 G.add_edge(0, N)
311 N *= 2
312 return G
313
314
315@nx._dispatchable(graphs=None, returns_graph=True)
316@nodes_or_number(0)
317def complete_graph(n, create_using=None):
318 """Return the complete graph `K_n` with n nodes.
319
320 A complete graph on `n` nodes means that all pairs
321 of distinct nodes have an edge connecting them.
322
323 .. plot::
324
325 >>> nx.draw(nx.complete_graph(5))
326
327 Parameters
328 ----------
329 n : int or iterable container of nodes
330 If n is an integer, nodes are from range(n).
331 If n is a container of nodes, those nodes appear in the graph.
332 Warning: n is not checked for duplicates and if present the
333 resulting graph may not be as desired. Make sure you have no duplicates.
334 create_using : NetworkX graph constructor, optional (default=nx.Graph)
335 Graph type to create. If graph instance, then cleared before populated.
336
337 Examples
338 --------
339 >>> G = nx.complete_graph(9)
340 >>> len(G)
341 9
342 >>> G.size()
343 36
344 >>> G = nx.complete_graph(range(11, 14))
345 >>> list(G.nodes())
346 [11, 12, 13]
347 >>> G = nx.complete_graph(4, nx.DiGraph())
348 >>> G.is_directed()
349 True
350
351 """
352 _, nodes = n
353 G = empty_graph(nodes, create_using)
354 if len(nodes) > 1:
355 if G.is_directed():
356 edges = itertools.permutations(nodes, 2)
357 else:
358 edges = itertools.combinations(nodes, 2)
359 G.add_edges_from(edges)
360 return G
361
362
363@nx._dispatchable(graphs=None, returns_graph=True)
364def circular_ladder_graph(n, create_using=None):
365 """Returns the circular ladder graph $CL_n$ of length n.
366
367 $CL_n$ consists of two concentric n-cycles in which
368 each of the n pairs of concentric nodes are joined by an edge.
369
370 Node labels are the integers 0 to n-1
371
372 .. plot::
373
374 >>> nx.draw(nx.circular_ladder_graph(5))
375
376 """
377 G = ladder_graph(n, create_using)
378 G.add_edge(0, n - 1)
379 G.add_edge(n, 2 * n - 1)
380 return G
381
382
383@nx._dispatchable(graphs=None, returns_graph=True)
384def circulant_graph(n, offsets, create_using=None):
385 r"""Returns the circulant graph $Ci_n(x_1, x_2, ..., x_m)$ with $n$ nodes.
386
387 The circulant graph $Ci_n(x_1, ..., x_m)$ consists of $n$ nodes $0, ..., n-1$
388 such that node $i$ is connected to nodes $(i + x) \mod n$ and $(i - x) \mod n$
389 for all $x$ in $x_1, ..., x_m$. Thus $Ci_n(1)$ is a cycle graph.
390
391 .. plot::
392
393 >>> nx.draw(nx.circulant_graph(10, [1]))
394
395 Parameters
396 ----------
397 n : integer
398 The number of nodes in the graph.
399 offsets : list of integers
400 A list of node offsets, $x_1$ up to $x_m$, as described above.
401 create_using : NetworkX graph constructor, optional (default=nx.Graph)
402 Graph type to create. If graph instance, then cleared before populated.
403
404 Returns
405 -------
406 NetworkX Graph of type create_using
407
408 Examples
409 --------
410 Many well-known graph families are subfamilies of the circulant graphs;
411 for example, to create the cycle graph on n points, we connect every
412 node to nodes on either side (with offset plus or minus one). For n = 10,
413
414 >>> G = nx.circulant_graph(10, [1])
415 >>> edges = [
416 ... (0, 9),
417 ... (0, 1),
418 ... (1, 2),
419 ... (2, 3),
420 ... (3, 4),
421 ... (4, 5),
422 ... (5, 6),
423 ... (6, 7),
424 ... (7, 8),
425 ... (8, 9),
426 ... ]
427 >>> sorted(edges) == sorted(G.edges())
428 True
429
430 Similarly, we can create the complete graph
431 on 5 points with the set of offsets [1, 2]:
432
433 >>> G = nx.circulant_graph(5, [1, 2])
434 >>> edges = [
435 ... (0, 1),
436 ... (0, 2),
437 ... (0, 3),
438 ... (0, 4),
439 ... (1, 2),
440 ... (1, 3),
441 ... (1, 4),
442 ... (2, 3),
443 ... (2, 4),
444 ... (3, 4),
445 ... ]
446 >>> sorted(edges) == sorted(G.edges())
447 True
448
449 """
450 G = empty_graph(n, create_using)
451 G.add_edges_from((i, (i - j) % n) for i in range(n) for j in offsets)
452 G.add_edges_from((i, (i + j) % n) for i in range(n) for j in offsets)
453 return G
454
455
456@nx._dispatchable(graphs=None, returns_graph=True)
457@nodes_or_number(0)
458def cycle_graph(n, create_using=None):
459 """Returns the cycle graph $C_n$ of cyclically connected nodes.
460
461 $C_n$ is a path with its two end-nodes connected.
462
463 .. plot::
464
465 >>> nx.draw(nx.cycle_graph(5))
466
467 Parameters
468 ----------
469 n : int or iterable container of nodes
470 If n is an integer, nodes are from `range(n)`.
471 If n is a container of nodes, those nodes appear in the graph.
472 Warning: n is not checked for duplicates and if present the
473 resulting graph may not be as desired. Make sure you have no duplicates.
474 create_using : NetworkX graph constructor, optional (default=nx.Graph)
475 Graph type to create. If graph instance, then cleared before populated.
476
477 Notes
478 -----
479 If create_using is directed, the direction is in increasing order.
480
481 """
482 _, nodes = n
483 G = empty_graph(nodes, create_using)
484 G.add_edges_from(pairwise(nodes, cyclic=True))
485 return G
486
487
488@nx._dispatchable(graphs=None, returns_graph=True)
489def dorogovtsev_goltsev_mendes_graph(n, create_using=None):
490 """Returns the hierarchically constructed Dorogovtsev--Goltsev--Mendes graph.
491
492 The Dorogovtsev--Goltsev--Mendes [1]_ procedure deterministically produces a
493 scale-free graph with ``3/2 * (3**(n-1) + 1)`` nodes
494 and ``3**n`` edges for a given `n`.
495
496 Note that `n` denotes the number of times the state transition is applied,
497 starting from the base graph with ``n = 0`` (no transitions), as in [2]_.
498 This is different from the parameter ``t = n - 1`` in [1]_.
499
500 .. plot::
501
502 >>> nx.draw(nx.dorogovtsev_goltsev_mendes_graph(3))
503
504 Parameters
505 ----------
506 n : integer
507 The generation number.
508
509 create_using : NetworkX graph constructor, optional (default=nx.Graph)
510 Graph type to create. Directed graphs and multigraphs are not supported.
511
512 Returns
513 -------
514 G : NetworkX `Graph`
515
516 Raises
517 ------
518 NetworkXError
519 If `n` is less than zero.
520
521 If `create_using` is a directed graph or multigraph.
522
523 Examples
524 --------
525 >>> G = nx.dorogovtsev_goltsev_mendes_graph(3)
526 >>> G.number_of_nodes()
527 15
528 >>> G.number_of_edges()
529 27
530 >>> nx.is_planar(G)
531 True
532
533 References
534 ----------
535 .. [1] S. N. Dorogovtsev, A. V. Goltsev and J. F. F. Mendes,
536 "Pseudofractal scale-free web", Physical Review E 65, 066122, 2002.
537 https://arxiv.org/pdf/cond-mat/0112143.pdf
538 .. [2] Weisstein, Eric W. "Dorogovtsev--Goltsev--Mendes Graph".
539 From MathWorld--A Wolfram Web Resource.
540 https://mathworld.wolfram.com/Dorogovtsev-Goltsev-MendesGraph.html
541 """
542 if n < 0:
543 raise NetworkXError("n must be greater than or equal to 0")
544
545 G = empty_graph(0, create_using)
546 if G.is_directed():
547 raise NetworkXError("directed graph not supported")
548 if G.is_multigraph():
549 raise NetworkXError("multigraph not supported")
550
551 G.add_edge(0, 1)
552 new_node = 2 # next node to be added
553 for _ in range(n): # iterate over number of generations.
554 new_edges = []
555 for u, v in G.edges():
556 new_edges.append((u, new_node))
557 new_edges.append((v, new_node))
558 new_node += 1
559
560 G.add_edges_from(new_edges)
561 return G
562
563
564@nx._dispatchable(graphs=None, returns_graph=True)
565@nodes_or_number(0)
566def empty_graph(n=0, create_using=None, default=Graph):
567 """Returns the empty graph with n nodes and zero edges.
568
569 .. plot::
570
571 >>> nx.draw(nx.empty_graph(5))
572
573 Parameters
574 ----------
575 n : int or iterable container of nodes (default = 0)
576 If n is an integer, nodes are from `range(n)`.
577 If n is a container of nodes, those nodes appear in the graph.
578 create_using : Graph Instance, Constructor or None
579 Indicator of type of graph to return.
580 If a Graph-type instance, then clear and use it.
581 If None, use the `default` constructor.
582 If a constructor, call it to create an empty graph.
583 default : Graph constructor (optional, default = nx.Graph)
584 The constructor to use if create_using is None.
585 If None, then nx.Graph is used.
586 This is used when passing an unknown `create_using` value
587 through your home-grown function to `empty_graph` and
588 you want a default constructor other than nx.Graph.
589
590 Examples
591 --------
592 >>> G = nx.empty_graph(10)
593 >>> G.number_of_nodes()
594 10
595 >>> G.number_of_edges()
596 0
597 >>> G = nx.empty_graph("ABC")
598 >>> G.number_of_nodes()
599 3
600 >>> sorted(G)
601 ['A', 'B', 'C']
602
603 Notes
604 -----
605 The variable create_using should be a Graph Constructor or a
606 "graph"-like object. Constructors, e.g. `nx.Graph` or `nx.MultiGraph`
607 will be used to create the returned graph. "graph"-like objects
608 will be cleared (nodes and edges will be removed) and refitted as
609 an empty "graph" with nodes specified in n. This capability
610 is useful for specifying the class-nature of the resulting empty
611 "graph" (i.e. Graph, DiGraph, MyWeirdGraphClass, etc.).
612
613 The variable create_using has three main uses:
614 Firstly, the variable create_using can be used to create an
615 empty digraph, multigraph, etc. For example,
616
617 >>> n = 10
618 >>> G = nx.empty_graph(n, create_using=nx.DiGraph)
619
620 will create an empty digraph on n nodes.
621
622 Secondly, one can pass an existing graph (digraph, multigraph,
623 etc.) via create_using. For example, if G is an existing graph
624 (resp. digraph, multigraph, etc.), then empty_graph(n, create_using=G)
625 will empty G (i.e. delete all nodes and edges using G.clear())
626 and then add n nodes and zero edges, and return the modified graph.
627
628 Thirdly, when constructing your home-grown graph creation function
629 you can use empty_graph to construct the graph by passing a user
630 defined create_using to empty_graph. In this case, if you want the
631 default constructor to be other than nx.Graph, specify `default`.
632
633 >>> def mygraph(n, create_using=None):
634 ... G = nx.empty_graph(n, create_using, nx.MultiGraph)
635 ... G.add_edges_from([(0, 1), (0, 1)])
636 ... return G
637 >>> G = mygraph(3)
638 >>> G.is_multigraph()
639 True
640 >>> G = mygraph(3, nx.Graph)
641 >>> G.is_multigraph()
642 False
643
644 See also create_empty_copy(G).
645
646 """
647 if create_using is None:
648 G = default()
649 elif isinstance(create_using, type):
650 G = create_using()
651 elif not hasattr(create_using, "adj"):
652 raise TypeError("create_using is not a valid NetworkX graph type or instance")
653 else:
654 # create_using is a NetworkX style Graph
655 create_using.clear()
656 G = create_using
657
658 _, nodes = n
659 G.add_nodes_from(nodes)
660 return G
661
662
663@nx._dispatchable(graphs=None, returns_graph=True)
664def ladder_graph(n, create_using=None):
665 """Returns the Ladder graph of length n.
666
667 This is two paths of n nodes, with
668 each pair connected by a single edge.
669
670 Node labels are the integers 0 to 2*n - 1.
671
672 .. plot::
673
674 >>> nx.draw(nx.ladder_graph(5))
675
676 """
677 G = empty_graph(2 * n, create_using)
678 if G.is_directed():
679 raise NetworkXError("Directed Graph not supported")
680 G.add_edges_from(pairwise(range(n)))
681 G.add_edges_from(pairwise(range(n, 2 * n)))
682 G.add_edges_from((v, v + n) for v in range(n))
683 return G
684
685
686@nx._dispatchable(graphs=None, returns_graph=True)
687@nodes_or_number([0, 1])
688def lollipop_graph(m, n, create_using=None):
689 """Returns the Lollipop Graph; ``K_m`` connected to ``P_n``.
690
691 This is the Barbell Graph without the right barbell.
692
693 .. plot::
694
695 >>> nx.draw(nx.lollipop_graph(3, 4))
696
697 Parameters
698 ----------
699 m, n : int or iterable container of nodes
700 If an integer, nodes are from ``range(m)`` and ``range(m, m+n)``.
701 If a container of nodes, those nodes appear in the graph.
702 Warning: `m` and `n` are not checked for duplicates and if present the
703 resulting graph may not be as desired. Make sure you have no duplicates.
704
705 The nodes for `m` appear in the complete graph $K_m$ and the nodes
706 for `n` appear in the path $P_n$
707 create_using : NetworkX graph constructor, optional (default=nx.Graph)
708 Graph type to create. If graph instance, then cleared before populated.
709
710 Returns
711 -------
712 Networkx graph
713 A complete graph with `m` nodes connected to a path of length `n`.
714
715 Notes
716 -----
717 The 2 subgraphs are joined via an edge ``(m-1, m)``.
718 If ``n=0``, this is merely a complete graph.
719
720 (This graph is an extremal example in David Aldous and Jim
721 Fill's etext on Random Walks on Graphs.)
722
723 """
724 m, m_nodes = m
725 M = len(m_nodes)
726 if M < 2:
727 raise NetworkXError("Invalid description: m should indicate at least 2 nodes")
728
729 n, n_nodes = n
730 if isinstance(m, numbers.Integral) and isinstance(n, numbers.Integral):
731 n_nodes = list(range(M, M + n))
732 N = len(n_nodes)
733
734 # the ball
735 G = complete_graph(m_nodes, create_using)
736 if G.is_directed():
737 raise NetworkXError("Directed Graph not supported")
738
739 # the stick
740 G.add_nodes_from(n_nodes)
741 if N > 1:
742 G.add_edges_from(pairwise(n_nodes))
743
744 if len(G) != M + N:
745 raise NetworkXError("Nodes must be distinct in containers m and n")
746
747 # connect ball to stick
748 if M > 0 and N > 0:
749 G.add_edge(m_nodes[-1], n_nodes[0])
750 return G
751
752
753@nx._dispatchable(graphs=None, returns_graph=True)
754def null_graph(create_using=None):
755 """Returns the Null graph with no nodes or edges.
756
757 See empty_graph for the use of create_using.
758
759 """
760 G = empty_graph(0, create_using)
761 return G
762
763
764@nx._dispatchable(graphs=None, returns_graph=True)
765@nodes_or_number(0)
766def path_graph(n, create_using=None):
767 """Returns the Path graph `P_n` of linearly connected nodes.
768
769 .. plot::
770
771 >>> nx.draw(nx.path_graph(5))
772
773 Parameters
774 ----------
775 n : int or iterable
776 If an integer, nodes are 0 to n - 1.
777 If an iterable of nodes, in the order they appear in the path.
778 Warning: n is not checked for duplicates and if present the
779 resulting graph may not be as desired. Make sure you have no duplicates.
780 create_using : NetworkX graph constructor, optional (default=nx.Graph)
781 Graph type to create. If graph instance, then cleared before populated.
782
783 """
784 _, nodes = n
785 G = empty_graph(nodes, create_using)
786 G.add_edges_from(pairwise(nodes))
787 return G
788
789
790@nx._dispatchable(graphs=None, returns_graph=True)
791@nodes_or_number(0)
792def star_graph(n, create_using=None):
793 """Return a star graph.
794
795 The star graph consists of one center node connected to `n` outer nodes.
796
797 .. plot::
798
799 >>> nx.draw(nx.star_graph(6))
800
801 Parameters
802 ----------
803 n : int or iterable
804 If an integer, node labels are ``0`` to `n`, with center ``0``.
805 If an iterable of nodes, the center is the first.
806 Warning: `n` is not checked for duplicates and if present, the
807 resulting graph may not be as desired. Make sure you have no duplicates.
808 create_using : NetworkX graph constructor, optional (default=nx.Graph)
809 Graph type to create. If graph instance, then cleared before populated.
810
811 Examples
812 --------
813 A star graph with 3 spokes can be generated with
814
815 >>> G = nx.star_graph(3)
816 >>> sorted(G.edges)
817 [(0, 1), (0, 2), (0, 3)]
818
819 For directed graphs, the convention is to have edges pointing from the hub
820 to the spokes:
821
822 >>> DG1 = nx.star_graph(3, create_using=nx.DiGraph)
823 >>> sorted(DG1.edges)
824 [(0, 1), (0, 2), (0, 3)]
825
826 Other possible definitions have edges pointing from the spokes to the hub:
827
828 >>> DG2 = nx.star_graph(3, create_using=nx.DiGraph).reverse()
829 >>> sorted(DG2.edges)
830 [(1, 0), (2, 0), (3, 0)]
831
832 or have bidirectional edges:
833
834 >>> DG3 = nx.star_graph(3).to_directed()
835 >>> sorted(DG3.edges)
836 [(0, 1), (0, 2), (0, 3), (1, 0), (2, 0), (3, 0)]
837
838 Notes
839 -----
840 The graph has ``n + 1`` nodes for integer `n`.
841 So ``star_graph(3)`` is the same as ``star_graph(range(4))``.
842 """
843 n, nodes = n
844 if isinstance(n, numbers.Integral):
845 nodes.append(int(n)) # There should be n + 1 nodes.
846 G = empty_graph(nodes, create_using)
847
848 if len(nodes) > 1:
849 hub, *spokes = nodes
850 G.add_edges_from((hub, node) for node in spokes)
851 return G
852
853
854@nx._dispatchable(graphs=None, returns_graph=True)
855@nodes_or_number([0, 1])
856def tadpole_graph(m, n, create_using=None):
857 """Returns the (m,n)-tadpole graph; ``C_m`` connected to ``P_n``.
858
859 This graph on m+n nodes connects a cycle of size `m` to a path of length `n`.
860 It looks like a tadpole. It is also called a kite graph or a dragon graph.
861
862 .. plot::
863
864 >>> nx.draw(nx.tadpole_graph(3, 5))
865
866 Parameters
867 ----------
868 m, n : int or iterable container of nodes
869 If an integer, nodes are from ``range(m)`` and ``range(m,m+n)``.
870 If a container of nodes, those nodes appear in the graph.
871 Warning: `m` and `n` are not checked for duplicates and if present the
872 resulting graph may not be as desired.
873
874 The nodes for `m` appear in the cycle graph $C_m$ and the nodes
875 for `n` appear in the path $P_n$.
876 create_using : NetworkX graph constructor, optional (default=nx.Graph)
877 Graph type to create. If graph instance, then cleared before populated.
878
879 Returns
880 -------
881 Networkx graph
882 A cycle of size `m` connected to a path of length `n`.
883
884 Raises
885 ------
886 NetworkXError
887 If ``m < 2``. The tadpole graph is undefined for ``m<2``.
888
889 Notes
890 -----
891 The 2 subgraphs are joined via an edge ``(m-1, m)``.
892 If ``n=0``, this is a cycle graph.
893 `m` and/or `n` can be a container of nodes instead of an integer.
894
895 """
896 m, m_nodes = m
897 M = len(m_nodes)
898 if M < 2:
899 raise NetworkXError("Invalid description: m should indicate at least 2 nodes")
900
901 n, n_nodes = n
902 if isinstance(m, numbers.Integral) and isinstance(n, numbers.Integral):
903 n_nodes = list(range(M, M + n))
904
905 # the circle
906 G = cycle_graph(m_nodes, create_using)
907 if G.is_directed():
908 raise NetworkXError("Directed Graph not supported")
909
910 # the stick
911 nx.add_path(G, [m_nodes[-1]] + list(n_nodes))
912
913 return G
914
915
916@nx._dispatchable(graphs=None, returns_graph=True)
917def trivial_graph(create_using=None):
918 """Return the Trivial graph with one node (with label 0) and no edges.
919
920 .. plot::
921
922 >>> nx.draw(nx.trivial_graph(), with_labels=True)
923
924 """
925 G = empty_graph(1, create_using)
926 return G
927
928
929@nx._dispatchable(graphs=None, returns_graph=True)
930def turan_graph(n, r):
931 r"""Return the Turan Graph
932
933 The Turan Graph is a complete multipartite graph on $n$ nodes
934 with $r$ disjoint subsets. That is, edges connect each node to
935 every node not in its subset.
936
937 Given $n$ and $r$, we create a complete multipartite graph with
938 $r-(n \mod r)$ partitions of size $n/r$, rounded down, and
939 $n \mod r$ partitions of size $n/r+1$, rounded down.
940
941 .. plot::
942
943 >>> nx.draw(nx.turan_graph(6, 2))
944
945 Parameters
946 ----------
947 n : int
948 The number of nodes.
949 r : int
950 The number of partitions.
951 Must be less than or equal to n.
952
953 Notes
954 -----
955 Must satisfy $1 <= r <= n$.
956 The graph has $(r-1)(n^2)/(2r)$ edges, rounded down.
957 """
958
959 if not 1 <= r <= n:
960 raise NetworkXError("Must satisfy 1 <= r <= n")
961
962 partitions = [n // r] * (r - (n % r)) + [n // r + 1] * (n % r)
963 G = complete_multipartite_graph(*partitions)
964 return G
965
966
967@nx._dispatchable(graphs=None, returns_graph=True)
968@nodes_or_number(0)
969def wheel_graph(n, create_using=None):
970 """Return the wheel graph
971
972 The wheel graph consists of a hub node connected to a cycle of (n-1) nodes.
973
974 .. plot::
975
976 >>> nx.draw(nx.wheel_graph(5))
977
978 Parameters
979 ----------
980 n : int or iterable
981 If an integer, node labels are 0 to n with center 0.
982 If an iterable of nodes, the center is the first.
983 Warning: n is not checked for duplicates and if present the
984 resulting graph may not be as desired. Make sure you have no duplicates.
985 create_using : NetworkX graph constructor, optional (default=nx.Graph)
986 Graph type to create. If graph instance, then cleared before populated.
987
988 Node labels are the integers 0 to n - 1.
989 """
990 _, nodes = n
991 G = empty_graph(nodes, create_using)
992 if G.is_directed():
993 raise NetworkXError("Directed Graph not supported")
994
995 if len(nodes) > 1:
996 hub, *rim = nodes
997 G.add_edges_from((hub, node) for node in rim)
998 if len(rim) > 1:
999 G.add_edges_from(pairwise(rim, cyclic=True))
1000 return G
1001
1002
1003@nx._dispatchable(graphs=None, returns_graph=True)
1004def complete_multipartite_graph(*subset_sizes):
1005 """Returns the complete multipartite graph with the specified subset sizes.
1006
1007 .. plot::
1008
1009 >>> nx.draw(nx.complete_multipartite_graph(1, 2, 3))
1010
1011 Parameters
1012 ----------
1013 subset_sizes : tuple of integers or tuple of node iterables
1014 The arguments can either all be integer number of nodes or they
1015 can all be iterables of nodes. If integers, they represent the
1016 number of nodes in each subset of the multipartite graph.
1017 If iterables, each is used to create the nodes for that subset.
1018 The length of subset_sizes is the number of subsets.
1019
1020 Returns
1021 -------
1022 G : NetworkX Graph
1023 Returns the complete multipartite graph with the specified subsets.
1024
1025 For each node, the node attribute 'subset' is an integer
1026 indicating which subset contains the node.
1027
1028 Examples
1029 --------
1030 Creating a complete tripartite graph, with subsets of one, two, and three
1031 nodes, respectively.
1032
1033 >>> G = nx.complete_multipartite_graph(1, 2, 3)
1034 >>> [G.nodes[u]["subset"] for u in G]
1035 [0, 1, 1, 2, 2, 2]
1036 >>> list(G.edges(0))
1037 [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]
1038 >>> list(G.edges(2))
1039 [(2, 0), (2, 3), (2, 4), (2, 5)]
1040 >>> list(G.edges(4))
1041 [(4, 0), (4, 1), (4, 2)]
1042
1043 >>> G = nx.complete_multipartite_graph("a", "bc", "def")
1044 >>> [G.nodes[u]["subset"] for u in sorted(G)]
1045 [0, 1, 1, 2, 2, 2]
1046
1047 Notes
1048 -----
1049 This function generalizes several other graph builder functions.
1050
1051 - If no subset sizes are given, this returns the null graph.
1052 - If a single subset size `n` is given, this returns the empty graph on
1053 `n` nodes.
1054 - If two subset sizes `m` and `n` are given, this returns the complete
1055 bipartite graph on `m + n` nodes.
1056 - If subset sizes `1` and `n` are given, this returns the star graph on
1057 `n + 1` nodes.
1058
1059 See also
1060 --------
1061 complete_bipartite_graph
1062 """
1063 # The complete multipartite graph is an undirected simple graph.
1064 G = Graph()
1065
1066 if len(subset_sizes) == 0:
1067 return G
1068
1069 # set up subsets of nodes
1070 try:
1071 extents = pairwise(itertools.accumulate((0,) + subset_sizes))
1072 subsets = [range(start, end) for start, end in extents]
1073 except TypeError:
1074 subsets = subset_sizes
1075 else:
1076 if any(size < 0 for size in subset_sizes):
1077 raise NetworkXError(f"Negative number of nodes not valid: {subset_sizes}")
1078
1079 # add nodes with subset attribute
1080 # while checking that ints are not mixed with iterables
1081 try:
1082 for i, subset in enumerate(subsets):
1083 G.add_nodes_from(subset, subset=i)
1084 except TypeError as err:
1085 raise NetworkXError("Arguments must be all ints or all iterables") from err
1086
1087 # Across subsets, all nodes should be adjacent.
1088 # We can use itertools.combinations() because undirected.
1089 for subset1, subset2 in itertools.combinations(subsets, 2):
1090 G.add_edges_from(itertools.product(subset1, subset2))
1091 return G