| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843 |
- """Generators for some classic graphs.
- The typical graph builder function is called as follows:
- >>> G = nx.complete_graph(100)
- returning the complete graph on n nodes labeled 0, .., 99
- as a simple graph. Except for `empty_graph`, all the functions
- in this module return a Graph class (i.e. a simple, undirected graph).
- """
- import itertools
- import numbers
- import networkx as nx
- from networkx.classes import Graph
- from networkx.exception import NetworkXError
- from networkx.utils import nodes_or_number, pairwise
- __all__ = [
- "balanced_tree",
- "barbell_graph",
- "binomial_tree",
- "complete_graph",
- "complete_multipartite_graph",
- "circular_ladder_graph",
- "circulant_graph",
- "cycle_graph",
- "dorogovtsev_goltsev_mendes_graph",
- "empty_graph",
- "full_rary_tree",
- "ladder_graph",
- "lollipop_graph",
- "null_graph",
- "path_graph",
- "star_graph",
- "trivial_graph",
- "turan_graph",
- "wheel_graph",
- ]
- # -------------------------------------------------------------------
- # Some Classic Graphs
- # -------------------------------------------------------------------
- def _tree_edges(n, r):
- if n == 0:
- return
- # helper function for trees
- # yields edges in rooted tree at 0 with n nodes and branching ratio r
- nodes = iter(range(n))
- parents = [next(nodes)] # stack of max length r
- while parents:
- source = parents.pop(0)
- for i in range(r):
- try:
- target = next(nodes)
- parents.append(target)
- yield source, target
- except StopIteration:
- break
- def full_rary_tree(r, n, create_using=None):
- """Creates a full r-ary tree of `n` nodes.
- Sometimes called a k-ary, n-ary, or m-ary tree.
- "... all non-leaf nodes have exactly r children and all levels
- are full except for some rightmost position of the bottom level
- (if a leaf at the bottom level is missing, then so are all of the
- leaves to its right." [1]_
- Parameters
- ----------
- r : int
- branching factor of the tree
- n : int
- Number of nodes in the tree
- create_using : NetworkX graph constructor, optional (default=nx.Graph)
- Graph type to create. If graph instance, then cleared before populated.
- Returns
- -------
- G : networkx Graph
- An r-ary tree with n nodes
- References
- ----------
- .. [1] An introduction to data structures and algorithms,
- James Andrew Storer, Birkhauser Boston 2001, (page 225).
- """
- G = empty_graph(n, create_using)
- G.add_edges_from(_tree_edges(n, r))
- return G
- def balanced_tree(r, h, create_using=None):
- """Returns the perfectly balanced `r`-ary tree of height `h`.
- Parameters
- ----------
- r : int
- Branching factor of the tree; each node will have `r`
- children.
- h : int
- Height of the tree.
- create_using : NetworkX graph constructor, optional (default=nx.Graph)
- Graph type to create. If graph instance, then cleared before populated.
- Returns
- -------
- G : NetworkX graph
- A balanced `r`-ary tree of height `h`.
- Notes
- -----
- This is the rooted tree where all leaves are at distance `h` from
- the root. The root has degree `r` and all other internal nodes
- have degree `r + 1`.
- Node labels are integers, starting from zero.
- A balanced tree is also known as a *complete r-ary tree*.
- """
- # The number of nodes in the balanced tree is `1 + r + ... + r^h`,
- # which is computed by using the closed-form formula for a geometric
- # sum with ratio `r`. In the special case that `r` is 1, the number
- # of nodes is simply `h + 1` (since the tree is actually a path
- # graph).
- if r == 1:
- n = h + 1
- else:
- # This must be an integer if both `r` and `h` are integers. If
- # they are not, we force integer division anyway.
- n = (1 - r ** (h + 1)) // (1 - r)
- return full_rary_tree(r, n, create_using=create_using)
- def barbell_graph(m1, m2, create_using=None):
- """Returns the Barbell Graph: two complete graphs connected by a path.
- Parameters
- ----------
- m1 : int
- Size of the left and right barbells, must be greater than 2.
- m2 : int
- Length of the path connecting the barbells.
- create_using : NetworkX graph constructor, optional (default=nx.Graph)
- Graph type to create. If graph instance, then cleared before populated.
- Only undirected Graphs are supported.
- Returns
- -------
- G : NetworkX graph
- A barbell graph.
- Notes
- -----
- Two identical complete graphs $K_{m1}$ form the left and right bells,
- and are connected by a path $P_{m2}$.
- The `2*m1+m2` nodes are numbered
- `0, ..., m1-1` for the left barbell,
- `m1, ..., m1+m2-1` for the path,
- and `m1+m2, ..., 2*m1+m2-1` for the right barbell.
- The 3 subgraphs are joined via the edges `(m1-1, m1)` and
- `(m1+m2-1, m1+m2)`. If `m2=0`, this is merely two complete
- graphs joined together.
- This graph is an extremal example in David Aldous
- and Jim Fill's e-text on Random Walks on Graphs.
- """
- if m1 < 2:
- raise NetworkXError("Invalid graph description, m1 should be >=2")
- if m2 < 0:
- raise NetworkXError("Invalid graph description, m2 should be >=0")
- # left barbell
- G = complete_graph(m1, create_using)
- if G.is_directed():
- raise NetworkXError("Directed Graph not supported")
- # connecting path
- G.add_nodes_from(range(m1, m1 + m2 - 1))
- if m2 > 1:
- G.add_edges_from(pairwise(range(m1, m1 + m2)))
- # right barbell
- G.add_edges_from(
- (u, v) for u in range(m1 + m2, 2 * m1 + m2) for v in range(u + 1, 2 * m1 + m2)
- )
- # connect it up
- G.add_edge(m1 - 1, m1)
- if m2 > 0:
- G.add_edge(m1 + m2 - 1, m1 + m2)
- return G
- def binomial_tree(n, create_using=None):
- """Returns the Binomial Tree of order n.
- The binomial tree of order 0 consists of a single node. A binomial tree of order k
- is defined recursively by linking two binomial trees of order k-1: the root of one is
- the leftmost child of the root of the other.
- Parameters
- ----------
- n : int
- Order of the binomial tree.
- create_using : NetworkX graph constructor, optional (default=nx.Graph)
- Graph type to create. If graph instance, then cleared before populated.
- Returns
- -------
- G : NetworkX graph
- A binomial tree of $2^n$ nodes and $2^n - 1$ edges.
- """
- G = nx.empty_graph(1, create_using)
- N = 1
- for i in range(n):
- # Use G.edges() to ensure 2-tuples. G.edges is 3-tuple for MultiGraph
- edges = [(u + N, v + N) for (u, v) in G.edges()]
- G.add_edges_from(edges)
- G.add_edge(0, N)
- N *= 2
- return G
- @nodes_or_number(0)
- def complete_graph(n, create_using=None):
- """Return the complete graph `K_n` with n nodes.
- A complete graph on `n` nodes means that all pairs
- of distinct nodes have an edge connecting them.
- Parameters
- ----------
- n : int or iterable container of nodes
- If n is an integer, nodes are from range(n).
- If n is a container of nodes, those nodes appear in the graph.
- Warning: n is not checked for duplicates and if present the
- resulting graph may not be as desired. Make sure you have no duplicates.
- create_using : NetworkX graph constructor, optional (default=nx.Graph)
- Graph type to create. If graph instance, then cleared before populated.
- Examples
- --------
- >>> G = nx.complete_graph(9)
- >>> len(G)
- 9
- >>> G.size()
- 36
- >>> G = nx.complete_graph(range(11, 14))
- >>> list(G.nodes())
- [11, 12, 13]
- >>> G = nx.complete_graph(4, nx.DiGraph())
- >>> G.is_directed()
- True
- """
- _, nodes = n
- G = empty_graph(nodes, create_using)
- if len(nodes) > 1:
- if G.is_directed():
- edges = itertools.permutations(nodes, 2)
- else:
- edges = itertools.combinations(nodes, 2)
- G.add_edges_from(edges)
- return G
- def circular_ladder_graph(n, create_using=None):
- """Returns the circular ladder graph $CL_n$ of length n.
- $CL_n$ consists of two concentric n-cycles in which
- each of the n pairs of concentric nodes are joined by an edge.
- Node labels are the integers 0 to n-1
- """
- G = ladder_graph(n, create_using)
- G.add_edge(0, n - 1)
- G.add_edge(n, 2 * n - 1)
- return G
- def circulant_graph(n, offsets, create_using=None):
- r"""Returns the circulant graph $Ci_n(x_1, x_2, ..., x_m)$ with $n$ nodes.
- The circulant graph $Ci_n(x_1, ..., x_m)$ consists of $n$ nodes $0, ..., n-1$
- such that node $i$ is connected to nodes $(i + x) \mod n$ and $(i - x) \mod n$
- for all $x$ in $x_1, ..., x_m$. Thus $Ci_n(1)$ is a cycle graph.
- Parameters
- ----------
- n : integer
- The number of nodes in the graph.
- offsets : list of integers
- A list of node offsets, $x_1$ up to $x_m$, as described above.
- create_using : NetworkX graph constructor, optional (default=nx.Graph)
- Graph type to create. If graph instance, then cleared before populated.
- Returns
- -------
- NetworkX Graph of type create_using
- Examples
- --------
- Many well-known graph families are subfamilies of the circulant graphs;
- for example, to create the cycle graph on n points, we connect every
- node to nodes on either side (with offset plus or minus one). For n = 10,
- >>> G = nx.circulant_graph(10, [1])
- >>> edges = [
- ... (0, 9),
- ... (0, 1),
- ... (1, 2),
- ... (2, 3),
- ... (3, 4),
- ... (4, 5),
- ... (5, 6),
- ... (6, 7),
- ... (7, 8),
- ... (8, 9),
- ... ]
- ...
- >>> sorted(edges) == sorted(G.edges())
- True
- Similarly, we can create the complete graph
- on 5 points with the set of offsets [1, 2]:
- >>> G = nx.circulant_graph(5, [1, 2])
- >>> edges = [
- ... (0, 1),
- ... (0, 2),
- ... (0, 3),
- ... (0, 4),
- ... (1, 2),
- ... (1, 3),
- ... (1, 4),
- ... (2, 3),
- ... (2, 4),
- ... (3, 4),
- ... ]
- ...
- >>> sorted(edges) == sorted(G.edges())
- True
- """
- G = empty_graph(n, create_using)
- for i in range(n):
- for j in offsets:
- G.add_edge(i, (i - j) % n)
- G.add_edge(i, (i + j) % n)
- return G
- @nodes_or_number(0)
- def cycle_graph(n, create_using=None):
- """Returns the cycle graph $C_n$ of cyclically connected nodes.
- $C_n$ is a path with its two end-nodes connected.
- Parameters
- ----------
- n : int or iterable container of nodes
- If n is an integer, nodes are from `range(n)`.
- If n is a container of nodes, those nodes appear in the graph.
- Warning: n is not checked for duplicates and if present the
- resulting graph may not be as desired. Make sure you have no duplicates.
- create_using : NetworkX graph constructor, optional (default=nx.Graph)
- Graph type to create. If graph instance, then cleared before populated.
- Notes
- -----
- If create_using is directed, the direction is in increasing order.
- """
- _, nodes = n
- G = empty_graph(nodes, create_using)
- G.add_edges_from(pairwise(nodes, cyclic=True))
- return G
- def dorogovtsev_goltsev_mendes_graph(n, create_using=None):
- """Returns the hierarchically constructed Dorogovtsev-Goltsev-Mendes graph.
- The Dorogovtsev-Goltsev-Mendes [1]_ procedure produces a scale-free graph
- deterministically with the following properties for a given `n`:
- - Total number of nodes = ``3 * (3**n + 1) / 2``
- - Total number of edges = ``3 ** (n + 1)``
- Parameters
- ----------
- n : integer
- The generation number.
- create_using : NetworkX Graph, optional
- Graph type to be returned. Directed graphs and multi graphs are not
- supported.
- Returns
- -------
- G : NetworkX Graph
- Examples
- --------
- >>> G = nx.dorogovtsev_goltsev_mendes_graph(3)
- >>> G.number_of_nodes()
- 15
- >>> G.number_of_edges()
- 27
- >>> nx.is_planar(G)
- True
- References
- ----------
- .. [1] Dorogotsev S.N., Goltsev A.V., and Mendes J.F.F "Pseudofractal
- Scale-free Web". arXiv:cond-mat/0112143
- """
- G = empty_graph(0, create_using)
- if G.is_directed():
- raise NetworkXError("Directed Graph not supported")
- if G.is_multigraph():
- raise NetworkXError("Multigraph not supported")
- G.add_edge(0, 1)
- if n == 0:
- return G
- new_node = 2 # next node to be added
- for i in range(1, n + 1): # iterate over number of generations.
- last_generation_edges = list(G.edges())
- number_of_edges_in_last_generation = len(last_generation_edges)
- for j in range(0, number_of_edges_in_last_generation):
- G.add_edge(new_node, last_generation_edges[j][0])
- G.add_edge(new_node, last_generation_edges[j][1])
- new_node += 1
- return G
- @nodes_or_number(0)
- def empty_graph(n=0, create_using=None, default=Graph):
- """Returns the empty graph with n nodes and zero edges.
- Parameters
- ----------
- n : int or iterable container of nodes (default = 0)
- If n is an integer, nodes are from `range(n)`.
- If n is a container of nodes, those nodes appear in the graph.
- create_using : Graph Instance, Constructor or None
- Indicator of type of graph to return.
- If a Graph-type instance, then clear and use it.
- If None, use the `default` constructor.
- If a constructor, call it to create an empty graph.
- default : Graph constructor (optional, default = nx.Graph)
- The constructor to use if create_using is None.
- If None, then nx.Graph is used.
- This is used when passing an unknown `create_using` value
- through your home-grown function to `empty_graph` and
- you want a default constructor other than nx.Graph.
- Examples
- --------
- >>> G = nx.empty_graph(10)
- >>> G.number_of_nodes()
- 10
- >>> G.number_of_edges()
- 0
- >>> G = nx.empty_graph("ABC")
- >>> G.number_of_nodes()
- 3
- >>> sorted(G)
- ['A', 'B', 'C']
- Notes
- -----
- The variable create_using should be a Graph Constructor or a
- "graph"-like object. Constructors, e.g. `nx.Graph` or `nx.MultiGraph`
- will be used to create the returned graph. "graph"-like objects
- will be cleared (nodes and edges will be removed) and refitted as
- an empty "graph" with nodes specified in n. This capability
- is useful for specifying the class-nature of the resulting empty
- "graph" (i.e. Graph, DiGraph, MyWeirdGraphClass, etc.).
- The variable create_using has three main uses:
- Firstly, the variable create_using can be used to create an
- empty digraph, multigraph, etc. For example,
- >>> n = 10
- >>> G = nx.empty_graph(n, create_using=nx.DiGraph)
- will create an empty digraph on n nodes.
- Secondly, one can pass an existing graph (digraph, multigraph,
- etc.) via create_using. For example, if G is an existing graph
- (resp. digraph, multigraph, etc.), then empty_graph(n, create_using=G)
- will empty G (i.e. delete all nodes and edges using G.clear())
- and then add n nodes and zero edges, and return the modified graph.
- Thirdly, when constructing your home-grown graph creation function
- you can use empty_graph to construct the graph by passing a user
- defined create_using to empty_graph. In this case, if you want the
- default constructor to be other than nx.Graph, specify `default`.
- >>> def mygraph(n, create_using=None):
- ... G = nx.empty_graph(n, create_using, nx.MultiGraph)
- ... G.add_edges_from([(0, 1), (0, 1)])
- ... return G
- >>> G = mygraph(3)
- >>> G.is_multigraph()
- True
- >>> G = mygraph(3, nx.Graph)
- >>> G.is_multigraph()
- False
- See also create_empty_copy(G).
- """
- if create_using is None:
- G = default()
- elif isinstance(create_using, type):
- G = create_using()
- elif not hasattr(create_using, "adj"):
- raise TypeError("create_using is not a valid NetworkX graph type or instance")
- else:
- # create_using is a NetworkX style Graph
- create_using.clear()
- G = create_using
- _, nodes = n
- G.add_nodes_from(nodes)
- return G
- def ladder_graph(n, create_using=None):
- """Returns the Ladder graph of length n.
- This is two paths of n nodes, with
- each pair connected by a single edge.
- Node labels are the integers 0 to 2*n - 1.
- """
- G = empty_graph(2 * n, create_using)
- if G.is_directed():
- raise NetworkXError("Directed Graph not supported")
- G.add_edges_from(pairwise(range(n)))
- G.add_edges_from(pairwise(range(n, 2 * n)))
- G.add_edges_from((v, v + n) for v in range(n))
- return G
- @nodes_or_number([0, 1])
- def lollipop_graph(m, n, create_using=None):
- """Returns the Lollipop Graph; `K_m` connected to `P_n`.
- This is the Barbell Graph without the right barbell.
- Parameters
- ----------
- m, n : int or iterable container of nodes (default = 0)
- If an integer, nodes are from `range(m)` and `range(m,m+n)`.
- If a container of nodes, those nodes appear in the graph.
- Warning: m and n are not checked for duplicates and if present the
- resulting graph may not be as desired. Make sure you have no duplicates.
- The nodes for m appear in the complete graph $K_m$ and the nodes
- for n appear in the path $P_n$
- create_using : NetworkX graph constructor, optional (default=nx.Graph)
- Graph type to create. If graph instance, then cleared before populated.
- Notes
- -----
- The 2 subgraphs are joined via an edge (m-1, m).
- If n=0, this is merely a complete graph.
- (This graph is an extremal example in David Aldous and Jim
- Fill's etext on Random Walks on Graphs.)
- """
- m, m_nodes = m
- M = len(m_nodes)
- if M < 2:
- raise NetworkXError("Invalid description: m should indicate at least 2 nodes")
- n, n_nodes = n
- if isinstance(m, numbers.Integral) and isinstance(n, numbers.Integral):
- n_nodes = list(range(M, M + n))
- N = len(n_nodes)
- # the ball
- G = complete_graph(m_nodes, create_using)
- if G.is_directed():
- raise NetworkXError("Directed Graph not supported")
- # the stick
- G.add_nodes_from(n_nodes)
- if N > 1:
- G.add_edges_from(pairwise(n_nodes))
- if len(G) != M + N:
- raise NetworkXError("Nodes must be distinct in containers m and n")
- # connect ball to stick
- if M > 0 and N > 0:
- G.add_edge(m_nodes[-1], n_nodes[0])
- return G
- def null_graph(create_using=None):
- """Returns the Null graph with no nodes or edges.
- See empty_graph for the use of create_using.
- """
- G = empty_graph(0, create_using)
- return G
- @nodes_or_number(0)
- def path_graph(n, create_using=None):
- """Returns the Path graph `P_n` of linearly connected nodes.
- Parameters
- ----------
- n : int or iterable
- If an integer, nodes are 0 to n - 1.
- If an iterable of nodes, in the order they appear in the path.
- Warning: n is not checked for duplicates and if present the
- resulting graph may not be as desired. Make sure you have no duplicates.
- create_using : NetworkX graph constructor, optional (default=nx.Graph)
- Graph type to create. If graph instance, then cleared before populated.
- """
- _, nodes = n
- G = empty_graph(nodes, create_using)
- G.add_edges_from(pairwise(nodes))
- return G
- @nodes_or_number(0)
- def star_graph(n, create_using=None):
- """Return the star graph
- The star graph consists of one center node connected to n outer nodes.
- Parameters
- ----------
- n : int or iterable
- If an integer, node labels are 0 to n with center 0.
- If an iterable of nodes, the center is the first.
- Warning: n is not checked for duplicates and if present the
- resulting graph may not be as desired. Make sure you have no duplicates.
- create_using : NetworkX graph constructor, optional (default=nx.Graph)
- Graph type to create. If graph instance, then cleared before populated.
- Notes
- -----
- The graph has n+1 nodes for integer n.
- So star_graph(3) is the same as star_graph(range(4)).
- """
- n, nodes = n
- if isinstance(n, numbers.Integral):
- nodes.append(n) # there should be n+1 nodes
- G = empty_graph(nodes, create_using)
- if G.is_directed():
- raise NetworkXError("Directed Graph not supported")
- if len(nodes) > 1:
- hub, *spokes = nodes
- G.add_edges_from((hub, node) for node in spokes)
- return G
- def trivial_graph(create_using=None):
- """Return the Trivial graph with one node (with label 0) and no edges."""
- G = empty_graph(1, create_using)
- return G
- def turan_graph(n, r):
- r"""Return the Turan Graph
- The Turan Graph is a complete multipartite graph on $n$ nodes
- with $r$ disjoint subsets. That is, edges connect each node to
- every node not in its subset.
- Given $n$ and $r$, we create a complete multipartite graph with
- $r-(n \mod r)$ partitions of size $n/r$, rounded down, and
- $n \mod r$ partitions of size $n/r+1$, rounded down.
- Parameters
- ----------
- n : int
- The number of nodes.
- r : int
- The number of partitions.
- Must be less than or equal to n.
- Notes
- -----
- Must satisfy $1 <= r <= n$.
- The graph has $(r-1)(n^2)/(2r)$ edges, rounded down.
- """
- if not 1 <= r <= n:
- raise NetworkXError("Must satisfy 1 <= r <= n")
- partitions = [n // r] * (r - (n % r)) + [n // r + 1] * (n % r)
- G = complete_multipartite_graph(*partitions)
- return G
- @nodes_or_number(0)
- def wheel_graph(n, create_using=None):
- """Return the wheel graph
- The wheel graph consists of a hub node connected to a cycle of (n-1) nodes.
- Parameters
- ----------
- n : int or iterable
- If an integer, node labels are 0 to n with center 0.
- If an iterable of nodes, the center is the first.
- Warning: n is not checked for duplicates and if present the
- resulting graph may not be as desired. Make sure you have no duplicates.
- create_using : NetworkX graph constructor, optional (default=nx.Graph)
- Graph type to create. If graph instance, then cleared before populated.
- Node labels are the integers 0 to n - 1.
- """
- _, nodes = n
- G = empty_graph(nodes, create_using)
- if G.is_directed():
- raise NetworkXError("Directed Graph not supported")
- if len(nodes) > 1:
- hub, *rim = nodes
- G.add_edges_from((hub, node) for node in rim)
- if len(rim) > 1:
- G.add_edges_from(pairwise(rim, cyclic=True))
- return G
- def complete_multipartite_graph(*subset_sizes):
- """Returns the complete multipartite graph with the specified subset sizes.
- Parameters
- ----------
- subset_sizes : tuple of integers or tuple of node iterables
- The arguments can either all be integer number of nodes or they
- can all be iterables of nodes. If integers, they represent the
- number of nodes in each subset of the multipartite graph.
- If iterables, each is used to create the nodes for that subset.
- The length of subset_sizes is the number of subsets.
- Returns
- -------
- G : NetworkX Graph
- Returns the complete multipartite graph with the specified subsets.
- For each node, the node attribute 'subset' is an integer
- indicating which subset contains the node.
- Examples
- --------
- Creating a complete tripartite graph, with subsets of one, two, and three
- nodes, respectively.
- >>> G = nx.complete_multipartite_graph(1, 2, 3)
- >>> [G.nodes[u]["subset"] for u in G]
- [0, 1, 1, 2, 2, 2]
- >>> list(G.edges(0))
- [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]
- >>> list(G.edges(2))
- [(2, 0), (2, 3), (2, 4), (2, 5)]
- >>> list(G.edges(4))
- [(4, 0), (4, 1), (4, 2)]
- >>> G = nx.complete_multipartite_graph("a", "bc", "def")
- >>> [G.nodes[u]["subset"] for u in sorted(G)]
- [0, 1, 1, 2, 2, 2]
- Notes
- -----
- This function generalizes several other graph builder functions.
- - If no subset sizes are given, this returns the null graph.
- - If a single subset size `n` is given, this returns the empty graph on
- `n` nodes.
- - If two subset sizes `m` and `n` are given, this returns the complete
- bipartite graph on `m + n` nodes.
- - If subset sizes `1` and `n` are given, this returns the star graph on
- `n + 1` nodes.
- See also
- --------
- complete_bipartite_graph
- """
- # The complete multipartite graph is an undirected simple graph.
- G = Graph()
- if len(subset_sizes) == 0:
- return G
- # set up subsets of nodes
- try:
- extents = pairwise(itertools.accumulate((0,) + subset_sizes))
- subsets = [range(start, end) for start, end in extents]
- except TypeError:
- subsets = subset_sizes
- # add nodes with subset attribute
- # while checking that ints are not mixed with iterables
- try:
- for i, subset in enumerate(subsets):
- G.add_nodes_from(subset, subset=i)
- except TypeError as err:
- raise NetworkXError("Arguments must be all ints or all iterables") from err
- # Across subsets, all nodes should be adjacent.
- # We can use itertools.combinations() because undirected.
- for subset1, subset2 in itertools.combinations(subsets, 2):
- G.add_edges_from(itertools.product(subset1, subset2))
- return G
|