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 Parameters
377 ----------
378 n : int
379 The length of the ladder. Must be at least 2.
380 create_using : NetworkX graph constructor, optional (default=nx.Graph)
381 Graph type to create. If graph instance, then cleared before populated.
382
383 Returns
384 -------
385 G : Graph
386 A circular ladder graph.
387
388 Raises
389 ------
390 ValueError
391 If `n` is less than 2.
392 NetworkXError
393 If `create_using` is a directed graph.
394
395 """
396 if n < 2:
397 raise ValueError("n must be at least 2 for circular_ladder_graph")
398 G = ladder_graph(n, create_using)
399 G.add_edge(0, n - 1)
400 G.add_edge(n, 2 * n - 1)
401 return G
402
403
404@nx._dispatchable(graphs=None, returns_graph=True)
405def circulant_graph(n, offsets, create_using=None):
406 r"""Returns the circulant graph $Ci_n(x_1, x_2, ..., x_m)$ with $n$ nodes.
407
408 The circulant graph $Ci_n(x_1, ..., x_m)$ consists of $n$ nodes $0, ..., n-1$
409 such that node $i$ is connected to nodes $(i + x) \mod n$ and $(i - x) \mod n$
410 for all $x$ in $x_1, ..., x_m$. Thus $Ci_n(1)$ is a cycle graph.
411
412 .. plot::
413
414 >>> nx.draw(nx.circulant_graph(10, [1]))
415
416 Parameters
417 ----------
418 n : integer
419 The number of nodes in the graph.
420 offsets : list of integers
421 A list of node offsets, $x_1$ up to $x_m$, as described above.
422 create_using : NetworkX graph constructor, optional (default=nx.Graph)
423 Graph type to create. If graph instance, then cleared before populated.
424
425 Returns
426 -------
427 NetworkX Graph of type create_using
428
429 Examples
430 --------
431 Many well-known graph families are subfamilies of the circulant graphs;
432 for example, to create the cycle graph on n points, we connect every
433 node to nodes on either side (with offset plus or minus one). For n = 10,
434
435 >>> G = nx.circulant_graph(10, [1])
436 >>> edges = [
437 ... (0, 9),
438 ... (0, 1),
439 ... (1, 2),
440 ... (2, 3),
441 ... (3, 4),
442 ... (4, 5),
443 ... (5, 6),
444 ... (6, 7),
445 ... (7, 8),
446 ... (8, 9),
447 ... ]
448 >>> sorted(edges) == sorted(G.edges())
449 True
450
451 Similarly, we can create the complete graph
452 on 5 points with the set of offsets [1, 2]:
453
454 >>> G = nx.circulant_graph(5, [1, 2])
455 >>> edges = [
456 ... (0, 1),
457 ... (0, 2),
458 ... (0, 3),
459 ... (0, 4),
460 ... (1, 2),
461 ... (1, 3),
462 ... (1, 4),
463 ... (2, 3),
464 ... (2, 4),
465 ... (3, 4),
466 ... ]
467 >>> sorted(edges) == sorted(G.edges())
468 True
469
470 """
471 G = empty_graph(n, create_using)
472 G.add_edges_from((i, (i - j) % n) for i in range(n) for j in offsets)
473 G.add_edges_from((i, (i + j) % n) for i in range(n) for j in offsets)
474 return G
475
476
477@nx._dispatchable(graphs=None, returns_graph=True)
478@nodes_or_number(0)
479def cycle_graph(n, create_using=None):
480 """Returns the cycle graph $C_n$ of cyclically connected nodes.
481
482 $C_n$ is a path with its two end-nodes connected.
483
484 .. plot::
485
486 >>> nx.draw(nx.cycle_graph(5))
487
488 Parameters
489 ----------
490 n : int or iterable container of nodes
491 If n is an integer, nodes are from `range(n)`.
492 If n is a container of nodes, those nodes appear in the graph.
493 Warning: n is not checked for duplicates and if present the
494 resulting graph may not be as desired. Make sure you have no duplicates.
495 create_using : NetworkX graph constructor, optional (default=nx.Graph)
496 Graph type to create. If graph instance, then cleared before populated.
497
498 Notes
499 -----
500 If create_using is directed, the direction is in increasing order.
501
502 """
503 _, nodes = n
504 G = empty_graph(nodes, create_using)
505 G.add_edges_from(pairwise(nodes, cyclic=True))
506 return G
507
508
509@nx._dispatchable(graphs=None, returns_graph=True)
510def dorogovtsev_goltsev_mendes_graph(n, create_using=None):
511 """Returns the hierarchically constructed Dorogovtsev--Goltsev--Mendes graph.
512
513 The Dorogovtsev--Goltsev--Mendes [1]_ procedure deterministically produces a
514 scale-free graph with ``3/2 * (3**(n-1) + 1)`` nodes
515 and ``3**n`` edges for a given `n`.
516
517 Note that `n` denotes the number of times the state transition is applied,
518 starting from the base graph with ``n = 0`` (no transitions), as in [2]_.
519 This is different from the parameter ``t = n - 1`` in [1]_.
520
521 .. plot::
522
523 >>> nx.draw(nx.dorogovtsev_goltsev_mendes_graph(3))
524
525 Parameters
526 ----------
527 n : integer
528 The generation number.
529
530 create_using : NetworkX graph constructor, optional (default=nx.Graph)
531 Graph type to create. Directed graphs and multigraphs are not supported.
532
533 Returns
534 -------
535 G : NetworkX `Graph`
536
537 Raises
538 ------
539 NetworkXError
540 If `n` is less than zero.
541
542 If `create_using` is a directed graph or multigraph.
543
544 Examples
545 --------
546 >>> G = nx.dorogovtsev_goltsev_mendes_graph(3)
547 >>> G.number_of_nodes()
548 15
549 >>> G.number_of_edges()
550 27
551 >>> nx.is_planar(G)
552 True
553
554 References
555 ----------
556 .. [1] S. N. Dorogovtsev, A. V. Goltsev and J. F. F. Mendes,
557 "Pseudofractal scale-free web", Physical Review E 65, 066122, 2002.
558 https://arxiv.org/pdf/cond-mat/0112143.pdf
559 .. [2] Weisstein, Eric W. "Dorogovtsev--Goltsev--Mendes Graph".
560 From MathWorld--A Wolfram Web Resource.
561 https://mathworld.wolfram.com/Dorogovtsev-Goltsev-MendesGraph.html
562 """
563 if n < 0:
564 raise NetworkXError("n must be greater than or equal to 0")
565
566 G = empty_graph(0, create_using)
567 if G.is_directed():
568 raise NetworkXError("directed graph not supported")
569 if G.is_multigraph():
570 raise NetworkXError("multigraph not supported")
571
572 G.add_edge(0, 1)
573 new_node = 2 # next node to be added
574 for _ in range(n): # iterate over number of generations.
575 new_edges = []
576 for u, v in G.edges():
577 new_edges.append((u, new_node))
578 new_edges.append((v, new_node))
579 new_node += 1
580
581 G.add_edges_from(new_edges)
582 return G
583
584
585@nx._dispatchable(graphs=None, returns_graph=True)
586@nodes_or_number(0)
587def empty_graph(n=0, create_using=None, default=Graph):
588 """Returns the empty graph with n nodes and zero edges.
589
590 .. plot::
591
592 >>> nx.draw(nx.empty_graph(5))
593
594 Parameters
595 ----------
596 n : int or iterable container of nodes (default = 0)
597 If n is an integer, nodes are from `range(n)`.
598 If n is a container of nodes, those nodes appear in the graph.
599 create_using : Graph Instance, Constructor or None
600 Indicator of type of graph to return.
601 If a Graph-type instance, then clear and use it.
602 If None, use the `default` constructor.
603 If a constructor, call it to create an empty graph.
604 default : Graph constructor (optional, default = nx.Graph)
605 The constructor to use if create_using is None.
606 If None, then nx.Graph is used.
607 This is used when passing an unknown `create_using` value
608 through your home-grown function to `empty_graph` and
609 you want a default constructor other than nx.Graph.
610
611 Examples
612 --------
613 >>> G = nx.empty_graph(10)
614 >>> G.number_of_nodes()
615 10
616 >>> G.number_of_edges()
617 0
618 >>> G = nx.empty_graph("ABC")
619 >>> G.number_of_nodes()
620 3
621 >>> sorted(G)
622 ['A', 'B', 'C']
623
624 Notes
625 -----
626 The variable create_using should be a Graph Constructor or a
627 "graph"-like object. Constructors, e.g. `nx.Graph` or `nx.MultiGraph`
628 will be used to create the returned graph. "graph"-like objects
629 will be cleared (nodes and edges will be removed) and refitted as
630 an empty "graph" with nodes specified in n. This capability
631 is useful for specifying the class-nature of the resulting empty
632 "graph" (i.e. Graph, DiGraph, MyWeirdGraphClass, etc.).
633
634 The variable create_using has three main uses:
635 Firstly, the variable create_using can be used to create an
636 empty digraph, multigraph, etc. For example,
637
638 >>> n = 10
639 >>> G = nx.empty_graph(n, create_using=nx.DiGraph)
640
641 will create an empty digraph on n nodes.
642
643 Secondly, one can pass an existing graph (digraph, multigraph,
644 etc.) via create_using. For example, if G is an existing graph
645 (resp. digraph, multigraph, etc.), then empty_graph(n, create_using=G)
646 will empty G (i.e. delete all nodes and edges using G.clear())
647 and then add n nodes and zero edges, and return the modified graph.
648
649 Thirdly, when constructing your home-grown graph creation function
650 you can use empty_graph to construct the graph by passing a user
651 defined create_using to empty_graph. In this case, if you want the
652 default constructor to be other than nx.Graph, specify `default`.
653
654 >>> def mygraph(n, create_using=None):
655 ... G = nx.empty_graph(n, create_using, nx.MultiGraph)
656 ... G.add_edges_from([(0, 1), (0, 1)])
657 ... return G
658 >>> G = mygraph(3)
659 >>> G.is_multigraph()
660 True
661 >>> G = mygraph(3, nx.Graph)
662 >>> G.is_multigraph()
663 False
664
665 See also create_empty_copy(G).
666
667 """
668 if create_using is None:
669 G = default()
670 elif isinstance(create_using, type):
671 G = create_using()
672 elif not hasattr(create_using, "adj"):
673 raise TypeError("create_using is not a valid NetworkX graph type or instance")
674 else:
675 # create_using is a NetworkX style Graph
676 create_using.clear()
677 G = create_using
678
679 _, nodes = n
680 G.add_nodes_from(nodes)
681 return G
682
683
684@nx._dispatchable(graphs=None, returns_graph=True)
685def ladder_graph(n, create_using=None):
686 """Returns the Ladder graph of length n.
687
688 This is two paths of n nodes, with
689 each pair connected by a single edge.
690
691 Node labels are the integers 0 to 2*n - 1.
692
693 .. plot::
694
695 >>> nx.draw(nx.ladder_graph(5))
696
697 """
698 G = empty_graph(2 * n, create_using)
699 if G.is_directed():
700 raise NetworkXError("Directed Graph not supported")
701 G.add_edges_from(pairwise(range(n)))
702 G.add_edges_from(pairwise(range(n, 2 * n)))
703 G.add_edges_from((v, v + n) for v in range(n))
704 return G
705
706
707@nx._dispatchable(graphs=None, returns_graph=True)
708@nodes_or_number([0, 1])
709def lollipop_graph(m, n, create_using=None):
710 """Returns the Lollipop Graph; ``K_m`` connected to ``P_n``.
711
712 This is the Barbell Graph without the right barbell.
713
714 .. plot::
715
716 >>> nx.draw(nx.lollipop_graph(3, 4))
717
718 Parameters
719 ----------
720 m, n : int or iterable container of nodes
721 If an integer, nodes are from ``range(m)`` and ``range(m, m+n)``.
722 If a container of nodes, those nodes appear in the graph.
723 Warning: `m` and `n` are not checked for duplicates and if present the
724 resulting graph may not be as desired. Make sure you have no duplicates.
725
726 The nodes for `m` appear in the complete graph $K_m$ and the nodes
727 for `n` appear in the path $P_n$
728 create_using : NetworkX graph constructor, optional (default=nx.Graph)
729 Graph type to create. If graph instance, then cleared before populated.
730
731 Returns
732 -------
733 Networkx graph
734 A complete graph with `m` nodes connected to a path of length `n`.
735
736 Notes
737 -----
738 The 2 subgraphs are joined via an edge ``(m-1, m)``.
739 If ``n=0``, this is merely a complete graph.
740
741 (This graph is an extremal example in David Aldous and Jim
742 Fill's etext on Random Walks on Graphs.)
743
744 """
745 m, m_nodes = m
746 M = len(m_nodes)
747 if M < 2:
748 raise NetworkXError("Invalid description: m should indicate at least 2 nodes")
749
750 n, n_nodes = n
751 if isinstance(m, numbers.Integral) and isinstance(n, numbers.Integral):
752 n_nodes = list(range(M, M + n))
753 N = len(n_nodes)
754
755 # the ball
756 G = complete_graph(m_nodes, create_using)
757 if G.is_directed():
758 raise NetworkXError("Directed Graph not supported")
759
760 # the stick
761 G.add_nodes_from(n_nodes)
762 if N > 1:
763 G.add_edges_from(pairwise(n_nodes))
764
765 if len(G) != M + N:
766 raise NetworkXError("Nodes must be distinct in containers m and n")
767
768 # connect ball to stick
769 if M > 0 and N > 0:
770 G.add_edge(m_nodes[-1], n_nodes[0])
771 return G
772
773
774@nx._dispatchable(graphs=None, returns_graph=True)
775def null_graph(create_using=None):
776 """Returns the Null graph with no nodes or edges.
777
778 See empty_graph for the use of create_using.
779
780 """
781 G = empty_graph(0, create_using)
782 return G
783
784
785@nx._dispatchable(graphs=None, returns_graph=True)
786@nodes_or_number(0)
787def path_graph(n, create_using=None):
788 """Returns the Path graph `P_n` of linearly connected nodes.
789
790 .. plot::
791
792 >>> nx.draw(nx.path_graph(5))
793
794 Parameters
795 ----------
796 n : int or iterable
797 If an integer, nodes are 0 to n - 1.
798 If an iterable of nodes, in the order they appear in the path.
799 Warning: n is not checked for duplicates and if present the
800 resulting graph may not be as desired. Make sure you have no duplicates.
801 create_using : NetworkX graph constructor, optional (default=nx.Graph)
802 Graph type to create. If graph instance, then cleared before populated.
803
804 """
805 _, nodes = n
806 G = empty_graph(nodes, create_using)
807 G.add_edges_from(pairwise(nodes))
808 return G
809
810
811@nx._dispatchable(graphs=None, returns_graph=True)
812@nodes_or_number(0)
813def star_graph(n, create_using=None):
814 """Return a star graph.
815
816 The star graph consists of one center node connected to `n` outer nodes.
817
818 .. plot::
819
820 >>> nx.draw(nx.star_graph(6))
821
822 Parameters
823 ----------
824 n : int or iterable
825 If an integer, node labels are ``0`` to `n`, with center ``0``.
826 If an iterable of nodes, the center is the first.
827 Warning: `n` is not checked for duplicates and if present, the
828 resulting graph may not be as desired. Make sure you have no duplicates.
829 create_using : NetworkX graph constructor, optional (default=nx.Graph)
830 Graph type to create. If graph instance, then cleared before populated.
831
832 Examples
833 --------
834 A star graph with 3 spokes can be generated with
835
836 >>> G = nx.star_graph(3)
837 >>> sorted(G.edges)
838 [(0, 1), (0, 2), (0, 3)]
839
840 For directed graphs, the convention is to have edges pointing from the hub
841 to the spokes:
842
843 >>> DG1 = nx.star_graph(3, create_using=nx.DiGraph)
844 >>> sorted(DG1.edges)
845 [(0, 1), (0, 2), (0, 3)]
846
847 Other possible definitions have edges pointing from the spokes to the hub:
848
849 >>> DG2 = nx.star_graph(3, create_using=nx.DiGraph).reverse()
850 >>> sorted(DG2.edges)
851 [(1, 0), (2, 0), (3, 0)]
852
853 or have bidirectional edges:
854
855 >>> DG3 = nx.star_graph(3).to_directed()
856 >>> sorted(DG3.edges)
857 [(0, 1), (0, 2), (0, 3), (1, 0), (2, 0), (3, 0)]
858
859 Notes
860 -----
861 The graph has ``n + 1`` nodes for integer `n`.
862 So ``star_graph(3)`` is the same as ``star_graph(range(4))``.
863 """
864 n, nodes = n
865 if isinstance(n, numbers.Integral):
866 nodes.append(int(n)) # There should be n + 1 nodes.
867 G = empty_graph(nodes, create_using)
868
869 if len(nodes) > 1:
870 hub, *spokes = nodes
871 G.add_edges_from((hub, node) for node in spokes)
872 return G
873
874
875@nx._dispatchable(graphs=None, returns_graph=True)
876@nodes_or_number([0, 1])
877def tadpole_graph(m, n, create_using=None):
878 """Returns the (m,n)-tadpole graph; ``C_m`` connected to ``P_n``.
879
880 This graph on m+n nodes connects a cycle of size `m` to a path of length `n`.
881 It looks like a tadpole. It is also called a kite graph or a dragon graph.
882
883 .. plot::
884
885 >>> nx.draw(nx.tadpole_graph(3, 5))
886
887 Parameters
888 ----------
889 m, n : int or iterable container of nodes
890 If an integer, nodes are from ``range(m)`` and ``range(m,m+n)``.
891 If a container of nodes, those nodes appear in the graph.
892 Warning: `m` and `n` are not checked for duplicates and if present the
893 resulting graph may not be as desired.
894
895 The nodes for `m` appear in the cycle graph $C_m$ and the nodes
896 for `n` appear in the path $P_n$.
897 create_using : NetworkX graph constructor, optional (default=nx.Graph)
898 Graph type to create. If graph instance, then cleared before populated.
899
900 Returns
901 -------
902 Networkx graph
903 A cycle of size `m` connected to a path of length `n`.
904
905 Raises
906 ------
907 NetworkXError
908 If ``m < 2``. The tadpole graph is undefined for ``m<2``.
909
910 Notes
911 -----
912 The 2 subgraphs are joined via an edge ``(m-1, m)``.
913 If ``n=0``, this is a cycle graph.
914 `m` and/or `n` can be a container of nodes instead of an integer.
915
916 """
917 m, m_nodes = m
918 M = len(m_nodes)
919 if M < 2:
920 raise NetworkXError("Invalid description: m should indicate at least 2 nodes")
921
922 n, n_nodes = n
923 if isinstance(m, numbers.Integral) and isinstance(n, numbers.Integral):
924 n_nodes = list(range(M, M + n))
925
926 # the circle
927 G = cycle_graph(m_nodes, create_using)
928 if G.is_directed():
929 raise NetworkXError("Directed Graph not supported")
930
931 # the stick
932 nx.add_path(G, [m_nodes[-1]] + list(n_nodes))
933
934 return G
935
936
937@nx._dispatchable(graphs=None, returns_graph=True)
938def trivial_graph(create_using=None):
939 """Return the Trivial graph with one node (with label 0) and no edges.
940
941 .. plot::
942
943 >>> nx.draw(nx.trivial_graph(), with_labels=True)
944
945 """
946 G = empty_graph(1, create_using)
947 return G
948
949
950@nx._dispatchable(graphs=None, returns_graph=True)
951def turan_graph(n, r):
952 r"""Return the Turan Graph
953
954 The Turan Graph is a complete multipartite graph on $n$ nodes
955 with $r$ disjoint subsets. That is, edges connect each node to
956 every node not in its subset.
957
958 Given $n$ and $r$, we create a complete multipartite graph with
959 $r-(n \mod r)$ partitions of size $n/r$, rounded down, and
960 $n \mod r$ partitions of size $n/r+1$, rounded down.
961
962 .. plot::
963
964 >>> nx.draw(nx.turan_graph(6, 2))
965
966 Parameters
967 ----------
968 n : int
969 The number of nodes.
970 r : int
971 The number of partitions.
972 Must be less than or equal to n.
973
974 Notes
975 -----
976 Must satisfy $1 <= r <= n$.
977 The graph has $(r-1)(n^2)/(2r)$ edges, rounded down.
978 """
979
980 if not 1 <= r <= n:
981 raise NetworkXError("Must satisfy 1 <= r <= n")
982
983 partitions = [n // r] * (r - (n % r)) + [n // r + 1] * (n % r)
984 G = complete_multipartite_graph(*partitions)
985 return G
986
987
988@nx._dispatchable(graphs=None, returns_graph=True)
989@nodes_or_number(0)
990def wheel_graph(n, create_using=None):
991 """Return the wheel graph
992
993 The wheel graph consists of a hub node connected to a cycle of (n-1) nodes.
994
995 .. plot::
996
997 >>> nx.draw(nx.wheel_graph(5))
998
999 Parameters
1000 ----------
1001 n : int or iterable
1002 If an integer, node labels are 0 to n with center 0.
1003 If an iterable of nodes, the center is the first.
1004 Warning: n is not checked for duplicates and if present the
1005 resulting graph may not be as desired. Make sure you have no duplicates.
1006 create_using : NetworkX graph constructor, optional (default=nx.Graph)
1007 Graph type to create. If graph instance, then cleared before populated.
1008
1009 Node labels are the integers 0 to n - 1.
1010 """
1011 _, nodes = n
1012 G = empty_graph(nodes, create_using)
1013 if G.is_directed():
1014 raise NetworkXError("Directed Graph not supported")
1015
1016 if len(nodes) > 1:
1017 hub, *rim = nodes
1018 G.add_edges_from((hub, node) for node in rim)
1019 if len(rim) > 1:
1020 G.add_edges_from(pairwise(rim, cyclic=True))
1021 return G
1022
1023
1024@nx._dispatchable(graphs=None, returns_graph=True)
1025def complete_multipartite_graph(*subset_sizes):
1026 """Returns the complete multipartite graph with the specified subset sizes.
1027
1028 .. plot::
1029
1030 >>> nx.draw(nx.complete_multipartite_graph(1, 2, 3))
1031
1032 Parameters
1033 ----------
1034 subset_sizes : tuple of integers or tuple of node iterables
1035 The arguments can either all be integer number of nodes or they
1036 can all be iterables of nodes. If integers, they represent the
1037 number of nodes in each subset of the multipartite graph.
1038 If iterables, each is used to create the nodes for that subset.
1039 The length of subset_sizes is the number of subsets.
1040
1041 Returns
1042 -------
1043 G : NetworkX Graph
1044 Returns the complete multipartite graph with the specified subsets.
1045
1046 For each node, the node attribute 'subset' is an integer
1047 indicating which subset contains the node.
1048
1049 Examples
1050 --------
1051 Creating a complete tripartite graph, with subsets of one, two, and three
1052 nodes, respectively.
1053
1054 >>> G = nx.complete_multipartite_graph(1, 2, 3)
1055 >>> [G.nodes[u]["subset"] for u in G]
1056 [0, 1, 1, 2, 2, 2]
1057 >>> list(G.edges(0))
1058 [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]
1059 >>> list(G.edges(2))
1060 [(2, 0), (2, 3), (2, 4), (2, 5)]
1061 >>> list(G.edges(4))
1062 [(4, 0), (4, 1), (4, 2)]
1063
1064 >>> G = nx.complete_multipartite_graph("a", "bc", "def")
1065 >>> [G.nodes[u]["subset"] for u in sorted(G)]
1066 [0, 1, 1, 2, 2, 2]
1067
1068 Notes
1069 -----
1070 This function generalizes several other graph builder functions.
1071
1072 - If no subset sizes are given, this returns the null graph.
1073 - If a single subset size `n` is given, this returns the empty graph on
1074 `n` nodes.
1075 - If two subset sizes `m` and `n` are given, this returns the complete
1076 bipartite graph on `m + n` nodes.
1077 - If subset sizes `1` and `n` are given, this returns the star graph on
1078 `n + 1` nodes.
1079
1080 See also
1081 --------
1082 complete_bipartite_graph
1083 """
1084 # The complete multipartite graph is an undirected simple graph.
1085 G = Graph()
1086
1087 if len(subset_sizes) == 0:
1088 return G
1089
1090 # set up subsets of nodes
1091 try:
1092 extents = pairwise(itertools.accumulate((0,) + subset_sizes))
1093 subsets = [range(start, end) for start, end in extents]
1094 except TypeError:
1095 subsets = subset_sizes
1096 else:
1097 if any(size < 0 for size in subset_sizes):
1098 raise NetworkXError(f"Negative number of nodes not valid: {subset_sizes}")
1099
1100 # add nodes with subset attribute
1101 # while checking that ints are not mixed with iterables
1102 try:
1103 for i, subset in enumerate(subsets):
1104 G.add_nodes_from(subset, subset=i)
1105 except TypeError as err:
1106 raise NetworkXError("Arguments must be all ints or all iterables") from err
1107
1108 # Across subsets, all nodes should be adjacent.
1109 # We can use itertools.combinations() because undirected.
1110 for subset1, subset2 in itertools.combinations(subsets, 2):
1111 G.add_edges_from(itertools.product(subset1, subset2))
1112 return G