| | """ |
| | Generators for the small graph atlas. |
| | """ |
| |
|
| | import gzip |
| | import importlib.resources |
| | from itertools import islice |
| |
|
| | import networkx as nx |
| |
|
| | __all__ = ["graph_atlas", "graph_atlas_g"] |
| |
|
| | |
| | |
| | |
| | |
| | NUM_GRAPHS = 1253 |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | ATLAS_FILE = importlib.resources.files("networkx.generators") / "atlas.dat.gz" |
| |
|
| |
|
| | def _generate_graphs(): |
| | """Sequentially read the file containing the edge list data for the |
| | graphs in the atlas and generate the graphs one at a time. |
| | |
| | This function reads the file given in :data:`.ATLAS_FILE`. |
| | |
| | """ |
| | with gzip.open(ATLAS_FILE, "rb") as f: |
| | line = f.readline() |
| | while line and line.startswith(b"GRAPH"): |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | graph_index = int(line[6:].rstrip()) |
| | line = f.readline() |
| | num_nodes = int(line[6:].rstrip()) |
| | |
| | |
| | edgelist = [] |
| | line = f.readline() |
| | while line and not line.startswith(b"GRAPH"): |
| | edgelist.append(line.rstrip()) |
| | line = f.readline() |
| | G = nx.Graph() |
| | G.name = f"G{graph_index}" |
| | G.add_nodes_from(range(num_nodes)) |
| | G.add_edges_from(tuple(map(int, e.split())) for e in edgelist) |
| | yield G |
| |
|
| |
|
| | @nx._dispatchable(graphs=None, returns_graph=True) |
| | def graph_atlas(i): |
| | """Returns graph number `i` from the Graph Atlas. |
| | |
| | For more information, see :func:`.graph_atlas_g`. |
| | |
| | Parameters |
| | ---------- |
| | i : int |
| | The index of the graph from the atlas to get. The graph at index |
| | 0 is assumed to be the null graph. |
| | |
| | Returns |
| | ------- |
| | list |
| | A list of :class:`~networkx.Graph` objects, the one at index *i* |
| | corresponding to the graph *i* in the Graph Atlas. |
| | |
| | See also |
| | -------- |
| | graph_atlas_g |
| | |
| | Notes |
| | ----- |
| | The time required by this function increases linearly with the |
| | argument `i`, since it reads a large file sequentially in order to |
| | generate the graph [1]_. |
| | |
| | References |
| | ---------- |
| | .. [1] Ronald C. Read and Robin J. Wilson, *An Atlas of Graphs*. |
| | Oxford University Press, 1998. |
| | |
| | """ |
| | if not (0 <= i < NUM_GRAPHS): |
| | raise ValueError(f"index must be between 0 and {NUM_GRAPHS}") |
| | return next(islice(_generate_graphs(), i, None)) |
| |
|
| |
|
| | @nx._dispatchable(graphs=None, returns_graph=True) |
| | def graph_atlas_g(): |
| | """Returns the list of all graphs with up to seven nodes named in the |
| | Graph Atlas. |
| | |
| | The graphs are listed in increasing order by |
| | |
| | 1. number of nodes, |
| | 2. number of edges, |
| | 3. degree sequence (for example 111223 < 112222), |
| | 4. number of automorphisms, |
| | |
| | in that order, with three exceptions as described in the *Notes* |
| | section below. This causes the list to correspond with the index of |
| | the graphs in the Graph Atlas [atlas]_, with the first graph, |
| | ``G[0]``, being the null graph. |
| | |
| | Returns |
| | ------- |
| | list |
| | A list of :class:`~networkx.Graph` objects, the one at index *i* |
| | corresponding to the graph *i* in the Graph Atlas. |
| | |
| | Examples |
| | -------- |
| | >>> from pprint import pprint |
| | >>> atlas = nx.graph_atlas_g() |
| | |
| | There are 1253 graphs in the atlas |
| | |
| | >>> len(atlas) |
| | 1253 |
| | |
| | The number of graphs with *n* nodes, where *n* ranges from 0 to 7: |
| | |
| | >>> from collections import Counter |
| | >>> num_nodes_per_graph = [len(G) for G in atlas] |
| | >>> Counter(num_nodes_per_graph) |
| | Counter({7: 1044, 6: 156, 5: 34, 4: 11, 3: 4, 2: 2, 0: 1, 1: 1}) |
| | |
| | Since the atlas is ordered by the number of nodes in the graph, all graphs |
| | with *n* nodes can be obtained by slicing the atlas. For example, all |
| | graphs with 5 nodes: |
| | |
| | >>> G5_list = atlas[19:53] |
| | >>> all(len(G) == 5 for G in G5_list) |
| | True |
| | |
| | Or all graphs with at least 3 nodes but fewer than 7 nodes: |
| | |
| | >>> G3_6_list = atlas[4:209] |
| | |
| | More generally, the indices that partition the atlas by the number of nodes |
| | per graph: |
| | |
| | >>> import itertools |
| | >>> partition_indices = [0] + list( |
| | ... itertools.accumulate(Counter(num_nodes_per_graph).values()) # cumsum |
| | ... ) |
| | >>> partition_indices |
| | [0, 1, 2, 4, 8, 19, 53, 209, 1253] |
| | >>> partition_mapping = dict(enumerate(itertools.pairwise(partition_indices))) |
| | >>> pprint(partition_mapping) |
| | {0: (0, 1), |
| | 1: (1, 2), |
| | 2: (2, 4), |
| | 3: (4, 8), |
| | 4: (8, 19), |
| | 5: (19, 53), |
| | 6: (53, 209), |
| | 7: (209, 1253)} |
| | |
| | See also |
| | -------- |
| | graph_atlas |
| | |
| | Notes |
| | ----- |
| | This function may be expensive in both time and space, since it |
| | reads a large file sequentially in order to populate the list. |
| | |
| | Although the NetworkX atlas functions match the order of graphs |
| | given in the "Atlas of Graphs" book, there are (at least) three |
| | errors in the ordering described in the book. The following three |
| | pairs of nodes violate the lexicographically nondecreasing sorted |
| | degree sequence rule: |
| | |
| | - graphs 55 and 56 with degree sequences 001111 and 000112, |
| | - graphs 1007 and 1008 with degree sequences 3333444 and 3333336, |
| | - graphs 1012 and 1213 with degree sequences 1244555 and 1244456. |
| | |
| | References |
| | ---------- |
| | .. [atlas] Ronald C. Read and Robin J. Wilson, |
| | *An Atlas of Graphs*. |
| | Oxford University Press, 1998. |
| | |
| | """ |
| | return list(_generate_graphs()) |
| |
|