#!/usr/bin/env python3# -*- coding: utf-8 -*-## This file classic.py is referred and derived from project NetworkX,## https://github.com/networkx/networkx/blob/master/networkx/generators/classic.py## which has the following license:## Copyright (C) 2004-2020, NetworkX Developers# Aric Hagberg <hagberg@lanl.gov># Dan Schult <dschult@colgate.edu># Pieter Swart <swart@lanl.gov># All rights reserved.## This file is part of NetworkX.## NetworkX is distributed under a BSD license; see LICENSE.txt for more# information.#importitertoolsfromitertoolsimportaccumulateimportnetworkxasnxafromnetworkx.utilsimportnodes_or_numberfromnetworkx.utilsimportpairwisefromgraphscopeimportnxfromgraphscope.nximportNetworkXErrorfromgraphscope.nx.classes.graphimportGraphfromgraphscope.nx.utils.compatimportpatch_docstring__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):ifn==0:return# helper function for trees# yields edges in rooted tree at 0 with n nodes and branching ratio rnodes=iter(range(n))parents=[next(nodes)]# stack of max length rwhileparents:source=parents.pop(0)foriinrange(r):try:target=next(nodes)parents.append(target)yieldsource,targetexceptStopIteration:break
[docs]@patch_docstring(nxa.balanced_tree)defbalanced_tree(r,h,create_using=None):# 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).ifr==1:n=h+1else:# 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)returnfull_rary_tree(r,n,create_using=create_using)
[docs]@patch_docstring(nxa.barbell_graph)defbarbell_graph(m1,m2,create_using=None):ifm1<2:raiseNetworkXError("Invalid graph description, m1 should be >=2")ifm2<0:raiseNetworkXError("Invalid graph description, m2 should be >=0")# left barbellG=complete_graph(m1,create_using)ifG.is_directed():raiseNetworkXError("Directed Graph not supported")# connecting pathG.add_nodes_from(range(m1,m1+m2-1))ifm2>1:G.add_edges_from(pairwise(range(m1,m1+m2)))# right barbellG.add_edges_from((u,v)foruinrange(m1+m2,2*m1+m2)forvinrange(u+1,2*m1+m2))# connect it upG.add_edge(m1-1,m1)ifm2>0:G.add_edge(m1+m2-1,m1+m2)returnG
[docs]@patch_docstring(nxa.binomial_tree)defbinomial_tree(n,create_using=None):G=nx.empty_graph(1,create_using)N=1foriinrange(n):# Use G.edges() to ensure 2-tuples. G.edges is 3-tuple for MultiGraphedges=[(u+N,v+N)for(u,v)inG.edges()]G.add_edges_from(edges)G.add_edge(0,N)N*=2returnG
[docs]@patch_docstring(nxa.dorogovtsev_goltsev_mendes_graph)defdorogovtsev_goltsev_mendes_graph(n,create_using=None):G=empty_graph(0,create_using)ifG.is_directed():raiseNetworkXError("Directed Graph not supported")ifG.is_multigraph():raiseNetworkXError("Multigraph not supported")G.add_edge(0,1)ifn==0:returnGnew_node=2# next node to be addedforiinrange(1,n+1):# iterate over number of generations.last_generation_edges=list(G.edges())number_of_edges_in_last_generation=len(last_generation_edges)forjinrange(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+=1returnG
[docs]@nodes_or_number(0)defempty_graph(n=0,create_using=None,default=nx.Graph,**kw):"""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'] """ifcreate_usingisNone:G=default(**kw)elifhasattr(create_using,"_adj"):# create_using is a NetworkX style Graphcreate_using.clear()G=create_usingelse:# try create_using as constructorG=create_using(**kw)n_name,nodes=nG.add_nodes_from(nodes)returnG
[docs]@patch_docstring(nxa.ladder_graph)defladder_graph(n,create_using=None):G=empty_graph(2*n,create_using)ifG.is_directed():raiseNetworkXError("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)forvinrange(n))returnG
[docs]@nodes_or_number([0,1])@patch_docstring(nxa.lollipop_graph)deflollipop_graph(m,n,create_using=None):m,m_nodes=mn,n_nodes=nM=len(m_nodes)N=len(n_nodes)ifisinstance(m,int):n_nodes=[len(m_nodes)+iforiinn_nodes]ifM<2:raiseNetworkXError("Invalid graph description, m should be >=2")ifN<0:raiseNetworkXError("Invalid graph description, n should be >=0")# the ballG=complete_graph(m_nodes,create_using)ifG.is_directed():raiseNetworkXError("Directed Graph not supported")# the stickG.add_nodes_from(n_nodes)ifN>1:G.add_edges_from(pairwise(n_nodes))# connect ball to stickifM>0andN>0:G.add_edge(m_nodes[-1],n_nodes[0])returnG
[docs]@nodes_or_number(0)@patch_docstring(nxa.star_graph)defstar_graph(n,create_using=None):n_name,nodes=nifisinstance(n_name,int):nodes=nodes+[n_name]# there should be n+1 nodesfirst=nodes[0]G=empty_graph(nodes,create_using)ifG.is_directed():raiseNetworkXError("Directed Graph not supported")G.add_edges_from((first,v)forvinnodes[1:])returnG
[docs]deftrivial_graph(create_using=None):"""Return the Trivial graph with one node (with label 0) and no edges."""G=empty_graph(1,create_using)returnG
[docs]@patch_docstring(nxa.turan_graph)defturan_graph(n,r):ifnot1<=r<=n:raiseNetworkXError("Must satisfy 1 <= r <= n")partitions=[n//r]*(r-(n%r))+[n//r+1]*(n%r)G=complete_multipartite_graph(*partitions)returnG
[docs]@patch_docstring(nxa.complete_multipartite_graph)defcomplete_multipartite_graph(*subset_sizes):# The complete multipartite graph is an undirected simple graph.G=Graph()iflen(subset_sizes)==0:returnG# set up subsets of nodestry:extents=pairwise(accumulate((0,)+subset_sizes))subsets=[range(start,end)forstart,endinextents]exceptTypeError:subsets=subset_sizes# add nodes with subset attribute# while checking that ints are not mixed with iterablestry:fori,subsetinenumerate(subsets):G.add_nodes_from(subset,subset=i)exceptTypeError:raiseNetworkXError("Arguments must be all ints or all iterables")# Across subsets, all vertices should be adjacent.# We can use itertools.combinations() because undirected.forsubset1,subset2initertools.combinations(subsets,2):G.add_edges_from(itertools.product(subset1,subset2))returnG