# -*- coding: utf-8 -*-## This file directed.py is referred and derived from project NetworkX,## https://github.com/networkx/networkx/blob/master/networkx/generators/directed.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.#"""Generators for some directed graphs, including growing network (GN) graphs andscale-free graphs."""fromcollectionsimportCounterimportnetworkxasnxafromnetworkx.utilsimportdiscrete_sequencefromnetworkx.utilsimportpy_random_statefromnetworkx.utilsimportweighted_choicefromgraphscopeimportnxfromgraphscope.nx.generators.classicimportempty_graphfromgraphscope.nx.utils.compatimportpatch_docstring__all__=["gn_graph","gnc_graph","gnr_graph","random_k_out_graph","scale_free_graph",]
[docs]@patch_docstring(nxa.gn_graph)@py_random_state(3)defgn_graph(n,kernel=None,create_using=None,seed=None):G=empty_graph(1,create_using,default=nx.DiGraph)ifnotG.is_directed():raisenx.NetworkXError("create_using must indicate a Directed Graph")ifkernelisNone:defkernel(x):returnxifn==1:returnGG.add_edge(1,0)# get startedds=[1,1]# degree sequenceforsourceinrange(2,n):# compute distribution from kernel and degreedist=[kernel(d)fordinds]# choose target from discrete distributiontarget=discrete_sequence(1,distribution=dist,seed=seed)[0]G.add_edge(source,target)ds.append(1)# the source has only one link (degree one)ds[target]+=1# add one to the target link degreereturnG
[docs]@patch_docstring(nxa.gnr_graph)@py_random_state(3)defgnr_graph(n,p,create_using=None,seed=None):G=empty_graph(1,create_using,default=nx.DiGraph)ifnotG.is_directed():raisenx.NetworkXError("create_using must indicate a Directed Graph")ifn==1:returnGforsourceinrange(1,n):target=seed.randrange(0,source)ifseed.random()<pandtarget!=0:target=next(G.successors(target))G.add_edge(source,target)returnG
[docs]@patch_docstring(nxa.gnc_graph)@py_random_state(2)defgnc_graph(n,create_using=None,seed=None):G=empty_graph(1,create_using,default=nx.DiGraph)ifnotG.is_directed():raisenx.NetworkXError("create_using must indicate a Directed Graph")ifn==1:returnGforsourceinrange(1,n):target=seed.randrange(0,source)forsuccinG.successors(target):G.add_edge(source,succ)G.add_edge(source,target)returnG
[docs]@patch_docstring(nxa.scale_free_graph)@py_random_state(7)defscale_free_graph(n,alpha=0.41,beta=0.54,gamma=0.05,delta_in=0.2,delta_out=0,create_using=None,seed=None,):def_choose_node(G,distribution,delta,psum):cumsum=0.0# normalizationr=seed.random()forn,dindistribution:cumsum+=(d+delta)/psumifr<cumsum:breakreturnnifcreate_usingisNoneornothasattr(create_using,"_adj"):# start with 3-cycleG=nx.empty_graph(3,create_using,default=nx.MultiDiGraph)G.add_edges_from([(0,1),(1,2),(2,0)])else:G=create_usingifnot(G.is_directed()andG.is_multigraph()):raisenx.NetworkXError("MultiDiGraph required in create_using")ifalpha<=0:raiseValueError("alpha must be > 0.")ifbeta<=0:raiseValueError("beta must be > 0.")ifgamma<=0:raiseValueError("gamma must be > 0.")ifabs(alpha+beta+gamma-1.0)>=1e-9:raiseValueError("alpha+beta+gamma must equal 1.")number_of_edges=G.number_of_edges()whilelen(G)<n:psum_in=number_of_edges+delta_in*len(G)psum_out=number_of_edges+delta_out*len(G)r=seed.random()# random choice in alpha,beta,gamma rangesifr<alpha:# alpha# add new node vv=len(G)# choose w according to in-degree and delta_inw=_choose_node(G,G.in_degree(),delta_in,psum_in)elifr<alpha+beta:# beta# choose v according to out-degree and delta_outv=_choose_node(G,G.out_degree(),delta_out,psum_out)# choose w according to in-degree and delta_inw=_choose_node(G,G.in_degree(),delta_in,psum_in)else:# gamma# choose v according to out-degree and delta_outv=_choose_node(G,G.out_degree(),delta_out,psum_out)# add new node ww=len(G)G.add_edge(v,w)number_of_edges+=1returnG
@py_random_state(4)defrandom_uniform_k_out_graph(n,k,self_loops=True,with_replacement=True,seed=None):"""Returns a random `k`-out graph with uniform attachment. A random `k`-out graph with uniform attachment is a multidigraph generated by the following algorithm. For each node *u*, choose `k` nodes *v* uniformly at random (with replacement). Add a directed edge joining *u* to *v*. Parameters ---------- n : int The number of nodes in the returned graph. k : int The out-degree of each node in the returned graph. self_loops : bool If True, self-loops are allowed when generating the graph. with_replacement : bool If True, neighbors are chosen with replacement and the returned graph will be a directed multigraph. Otherwise, neighbors are chosen without replacement and the returned graph will be a directed graph. seed : integer, random_state, or None (default) Indicator of random number generation state. See :ref:`Randomness<randomness>`. Returns ------- NetworkX graph A `k`-out-regular directed graph generated according to the above algorithm. It will be a multigraph if and only if `with_replacement` is True. Raises ------ ValueError If `with_replacement` is False and `k` is greater than `n`. See also -------- random_k_out_graph Notes ----- The return digraph or multidigraph may not be strongly connected, or even weakly connected. If `with_replacement` is True, this function is similar to :func:`random_k_out_graph`, if that function had parameter `alpha` set to positive infinity. """ifwith_replacement:create_using=nx.MultiDiGraph()defsample(v,nodes):ifnotself_loops:nodes=nodes-{v}return(seed.choice(list(nodes))foriinrange(k))else:create_using=nx.DiGraph()defsample(v,nodes):ifnotself_loops:nodes=nodes-{v}returnseed.sample(nodes,k)G=nx.empty_graph(n,create_using)nodes=set(G)foruinG:G.add_edges_from((u,v)forvinsample(u,nodes))returnG
[docs]@patch_docstring(nxa.random_k_out_graph)@py_random_state(4)defrandom_k_out_graph(n,k,alpha,self_loops=True,seed=None):ifalpha<0:raiseValueError("alpha must be positive")G=nx.empty_graph(n,create_using=nx.MultiDiGraph)weights=Counter({v:alphaforvinG})foriinrange(k*n):u=seed.choice([vforv,dinG.out_degree()ifd<k])# If self-loops are not allowed, make the source node `u` have# weight zero.ifnotself_loops:adjustment=Counter({u:weights[u]})else:adjustment=Counter()v=weighted_choice(weights-adjustment,seed=seed)G.add_edge(u,v)weights[v]+=1returnG