def from_networkx(G): """ Convert a NetworkX graph into a Zen graph object. In creating the object, the NetworkX node object and node/edge data will be copied over (a shallow copy). **Returns**: The return type depends on the input type. * :py:class:`zen.Graph` if the input graph was a :py:class:`networkx.Graph`. * :py:class:`zen.DiGraph` if the input graph was a :py:class:`networkx.DiGraph`. """ Gdest = None if type(G) == networkx.DiGraph: Gdest = DiGraph() elif type(G) == networkx.Graph: Gdest = Graph() else: raise Exception('Unable to convert graph object type %s' % str(type(G))) # add nodes for n, nd in G.nodes_iter(data=True): Gdest.add_node(n, nd) # add edges for x, y, ed in G.edges_iter(data=True): Gdest.add_edge(x, y, ed) return Gdest
def barabasi_albert(n, m, **kwargs): """ Generate a random graph using the Barabasi-Albert preferential attachment model. **Args**: * ``n`` (int): the number of nodes to add to the graph * ``m`` (int): the number of edges a new node will add to the graph **KwArgs**: * ``directed [=False]`` (boolean): whether to build the graph directed. If ``True``, then the ``m`` edges created by a node upon its creation are instantiated as out-edges. All others are in-edges to that node. * ``seed [=-1]`` (int): a seed for the random number generator * ``graph [=None]`` (:py:class:`zen.Graph` or :py:class:`zen.DiGraph`): this is the actual graph instance to populate. It must be empty and its directionality must agree with the value of ``directed``. **Returns**: :py:class:`zen.Graph` or :py:class:`zen.DiGraph`. The graph generated. If ``directed = True``, then a :py:class:`DiGraph` will be returned. .. note:: Source: A. L. Barabasi and R. Albert "Emergence of scaling in random networks", Science 286, pp 509-512, 1999. """ seed = kwargs.pop('seed', None) directed = kwargs.pop('directed', False) graph = kwargs.pop('graph', None) if graph is not None: if len(graph) > 0: raise ZenException, 'the graph must be empty, if provided' if graph.is_directed() != directed: raise ZenException, 'graph and directed arguments must agree' else: if directed: graph = DiGraph() else: graph = Graph() if len(kwargs) > 0: raise ZenException, 'Unknown arguments: %s' % ', '.join(kwargs.keys()) if seed is None: seed = -1 if not directed: return __inner_barabasi_albert_udir(n, m, seed, graph) else: return __inner_barabasi_albert_dir(n, m, seed, graph)
def erdos_renyi(n, p, **kwargs): """ Generate an Erdos-Renyi graph. **Args**: * ``num_nodes`` (int): the number of nodes to populate the graph with. * ``p`` (0 <= float <= 1): the probability p given to each edge's existence. **KwArgs**: * ``directed [=False]`` (boolean): indicates whether the network generated is directed. * ``self_loops [=False]`` (boolean): indicates whether self-loops are permitted in the generated graph. * ``seed [=-1]`` (int): the seed provided to the random generator used to drive the graph construction. * ``graph [=None]`` (:py:class:`zen.Graph` or :py:class:`zen.DiGraph`): this is the actual graph instance to populate. It must be empty and its directionality must agree with the value of ``directed``. """ directed = kwargs.pop('directed', False) self_loops = kwargs.pop('self_loops', False) seed = kwargs.pop('seed', None) graph = kwargs.pop('graph', None) if graph is not None: if len(graph) > 0: raise ZenException, 'the graph must be empty, if provided' if graph.is_directed() != directed: raise ZenException, 'graph and directed arguments must agree' else: if directed: graph = DiGraph() else: graph = Graph() if len(kwargs) > 0: raise ZenException, 'Unknown arguments: %s' % ', '.join(kwargs.keys()) if seed is None: seed = -1 if directed: return __erdos_renyi_directed(n, p, self_loops, seed, graph) else: return __erdos_renyi_undirected(n, p, self_loops, seed, graph)
def build_graph(graph_tree, weight_fxn): # What kind of graph is being built? is_bipartite = bool('bipartite' in graph_tree and graph_tree['bipartite']) is_directed = bool('directed' in graph_tree and graph_tree['directed']) if is_bipartite: G = BipartiteGraph() elif is_directed: G = DiGraph() else: G = Graph() # Build the nodes if 'node' in graph_tree: # get the list of nodes nodes = graph_tree['node'] # ensure the node-list is a list (needed if there's only one node) if not isinstance(nodes, list): nodes = [nodes] # Build each node and add to the graph for node in nodes: # Does the node have an id? has_id = True has_valid_id = True if 'id' not in node: has_id = False has_valid_id = False # We can only use positive integer node ids as graph idx # If that's not the case, treat it like any other attribute elif not isinstance(node['id'], int) or node['id'] < 0: has_valid_id = False # Got a valid node id node_idx = node['id'] # For bipartite graphs determine which node set this belongs to if is_bipartite: is_in_U = node['isInU'] # collect and verify all the node properties standard_keys = set(['id', 'name', 'zenData']) node_data = {} node_obj = None zen_data = None for key, val in list(node.items()): if key == 'name': node_obj = val # give preference to 'name' as source of node_obj elif key == 'label' and node_obj is None: node_obj = val elif key == 'zenData': zen_data = val # node_data is dict of all other attributes else: node_data[key] = val # _set_ to node_data else _append_ if zen_data is not None: if len(node_data) == 0: node_data = zen_data else: node_data['zenData'] = zen_data elif len(node_data) == 0: node_data = None # make sure that the node object is hashable otherwise put it if not isinstance(node_obj, str) and node_obj is not None: if not isinstance(node_obj, Hashable): \ if not isinstance(node_obj, Iterable): node_obj = None else: node_obj = tuple(node_obj) # For bipartite graph, this insertion method does not guarantee # that indices will be unchanged after a read-write cycle if is_bipartite: G.add_node_by_class(is_in_U, node_obj, node_data) elif has_id and has_valid_id: if is_directed: G.add_node_x(node_idx, G.edge_list_capacity, G.edge_list_capacity, node_obj, node_data) else: G.add_node_x(node_idx, G.edge_list_capacity, node_obj, node_data) else: if G.is_directed: G.add_node(nobj=node_obj, data=node_data) else: G.add_node(nobj=node_obj, data=node_data) # add edges if 'edge' in graph_tree: # ensure edge list is a list (needed if there is only one edge) edges = graph_tree['edge'] if not isinstance(edges, list): edges = [edges] # iterate over the edges, add each one to the graph for edge in edges: # make sure source and target are specified source = None target = None if 'source' not in edge: raise ZenException('Edge is missing the source attribute ' '(edge = %s)' % str(edge)) if 'target' not in edge: raise ZenException('Edge is missing the target attribute ' '(edge = %s)' % str(edge)) weight = 1 edge_idx = None zen_data = None edge_data = {} for key, val in list(edge.items()): if key == 'id': edge_idx = val if type(val) != int: raise ZenException('Edge id attribute must be a ' 'positive integer (edge = %s)' % str(edge)) elif key == 'source': source = val if type(val) != int or val < 0: raise ZenException('Edge source attribute must be a ' 'positive integer (edge = %s)' % str(edge)) elif key == 'target': target = val if type(val) != int or val < 0: raise ZenException('Edge target attribute must be a ' 'positive integer (edge = %s)' % str(edge)) elif key == 'weight': weight = float(val) elif key == 'zenData': zen_data = val # edge_data is dict of all other attributes else: edge_data[key] = val # give precedence to a weight-getting function if provided if weight_fxn != None: weight = weight_fxn(edge) # if zenData is only other attribute aside from those handled above # _set_ to edge_data else _append_ if zen_data is not None: if len(edge_data) == 0: edge_data = zen_data else: edge_data['zenData'] = zen_data elif len(edge_data) == 0: edge_data = None if edge_idx != None: G.add_edge_x(edge_idx, source, target, edge_data, weight) else: G.add_edge_(source, target, edge_data, weight) return G
def duplication_divergence_iky(n, s, **kwargs): """ Generate a random graph using the duplication-divergence model proposed by Ispolatov, Krapivski, and Yuryev (2008). **Args**: * ``n`` (int): the target size of the network. * ``s`` (float): the probability that a link connected to a newly duplicated node will exist **KwArgs**: * ``directed [=False]`` (boolean): whether to build the graph directed. If ``True``, then the ``m`` edges created by a node upon its creation are instantiated as out-edges. All others are in-edges to that node. * ``seed [=-1]`` (int): a seed for the random number generator * ``graph [=None]`` (:py:class:`zen.Graph` or :py:class:`zen.DiGraph`): this is the actual graph instance to populate. It must be empty and its directionality must agree with the value of ``directed``. **Returns**: :py:class:`zen.Graph` or :py:class:`zen.DiGraph`. The graph generated. If ``directed = True``, then a :py:class:`DiGraph` will be returned. .. note:: Source: I. Ispolatov, P. L. Krapivski, and A. Yuryev "Duplication-divergence model of protein interaction network", ??, 2008. """ seed = kwargs.pop('seed', None) directed = kwargs.pop('directed', False) graph = kwargs.pop('graph', None) if graph is not None: if len(graph) > 0: raise ZenException, 'the graph must be empty, if provided' if graph.is_directed() != directed: raise ZenException, 'graph and directed arguments must agree' if len(kwargs) > 0: raise ZenException, 'Unknown arguments: %s' % ', '.join(kwargs.keys()) if seed is None: seed = -1 # initialize the random number generator if seed >= 0: random.seed(seed) if type(n) != int: raise ZenException, 'Parameter n must be an integer' if type(s) != float and type(s) != int and type(s) != double: print type(s) raise ZenException, 'Parameter s must be a float, double, or an int' G = graph if graph is None: if directed: G = DiGraph() else: G = Graph() # initialize the graph with two connected nodes G.add_edge(0, 1) # build the rest of the graph i = 2 while i < n: # pick an existing node to copy cn_seed = random.randint(0, 100000) u = choose_node(G, seed=cn_seed) ##### # create the partial copy G.add_node(i) # copy edges if not directed: for w in G.neighbors(u): if random.random() <= s: G.add_edge(i, w) else: for w in G.in_neighbors(u): if random.random() <= s: G.add_edge(w, i) for w in G.out_neighbors(u): if random.random() <= s: G.add_edge(i, w) # if the node doesn't have any connections, then ditch it if G.degree(i) == 0: G.rm_node(i) else: i += 1 # done! return G
if random.random() <= s: G.add_edge(w, i) for w in G.out_neighbors(u): if random.random() <= s: G.add_edge(i, w) # if the node doesn't have any connections, then ditch it if G.degree(i) == 0: G.rm_node(i) else: i += 1 # done! return G #### # TODO: Implement duplication_divergence_wagner ### # TODO: Implement duplication_mutation if __name__ == '__main__': from zen.drawing import UbigraphRenderer G = DiGraph() ur = UbigraphRenderer('http://localhost:20738/RPC2', event_delay=1, graph=G) G = duplication_divergence_iky(10, 0.4, directed=True, graph=G)
def build_graph(graph_data, weight_fxn): is_directed = False if 'directed' in graph_data: is_directed = graph_data['directed'] == 1 # TODO detect if graphs are bipartite and support that G = None if is_directed: G = DiGraph() else: G = Graph() # TODO: Load graph attributes # add nodes if 'node' in graph_data: nodes = graph_data['node'] if type(nodes) != list: raise ZenException, 'The node attribute of a graph must be a list' for node in nodes: # node must have an 'id' if 'id' not in node: raise ZenException, 'Node is missing the id attribute (node = %s)' % str( node) node_idx = node['id'] # collect and verify all the node properties standard_keys = set(['id', 'name', 'zenData']) node_data = {} node_obj = None zen_data = None for key, val in node.items(): if key == 'id': node_idx = val if type(val) != int or val < 0: raise ZenException, 'Node id attribute must be a positive integer (node = %s)' % str( node) elif key == 'name': node_obj = val if type(val ) != str: # enforce types on standard attributes raise ZenException, 'Node name attribute must be a string (node = %s)' % str( node) elif key == 'label': if node_obj is None: # give preference to 'name' as source of node_obj node_obj = val if type(val) != str: raise ZenException, 'Node label attribute must be a string (node = %s)' % str( node) elif key == 'zenData': zen_data = val else: node_data[ key] = val # node_data is dict of all other attributes # if zenData is only other attribute aside from those handled above _set_ to node_data else _append_ if zen_data is not None: if len(node_data) == 0: node_data = zen_data else: node_data['zenData'] = zen_data elif len(node_data) == 0: node_data = None if is_directed: G.add_node_x(node_idx, G.edge_list_capacity, G.edge_list_capacity, node_obj, node_data) else: G.add_node_x(node_idx, G.edge_list_capacity, node_obj, node_data) # add edges if 'edge' in graph_data: edges = graph_data['edge'] if type(edges) != list: raise ZenException, 'The edge attibute of a graph must be a list' for edge in edges: # make sure source and target are specified source = None target = None if 'source' not in edge: raise ZenException, 'Edge is missing the source attribute (edge = %s)' % str( edge) if 'target' not in edge: raise ZenException, 'Edge is missing the target attribute (edge = %s)' % str( edge) weight = 1 edge_idx = None zen_data = None edge_data = {} for key, val in edge.items(): if key == 'id': edge_idx = val if type(val) != int: raise ZenException, 'Edge id attribute must be a positive integer (edge = %s)' % str( edge) elif key == 'source': source = val if type(val) != int or val < 0: raise ZenException, 'Edge source attribute must be a positive integer (edge = %s)' % str( edge) elif key == 'target': target = val if type(val) != int or val < 0: raise ZenException, 'Edge target attribute must be a positive integer (edge = %s)' % str( edge) elif key == 'weight': weight = float(val) elif key == 'zenData': zen_data = val else: edge_data[ key] = val # edge_data is dict of all other attributes # give precedence to a weight-getting function if provided if weight_fxn != None: weight = weight_fxn(edge) # if zenData is only other attribute aside from those handled above _set_ to edge_data else _append_ if zen_data is not None: if len(edge_data) == 0: edge_data = zen_data else: edge_data['zenData'] = zen_data elif len(edge_data) == 0: edge_data = None if edge_idx != None: G.add_edge_x(edge_idx, source, target, edge_data, weight) else: G.add_edge_(source, target, edge_data, weight) return G
def read_str(sbuffer, **kwargs): """ Read graph data from the ascii string in the binary edge list format. **Args**: ``sbuffer`` is the string from which the network data will be read. **KwArgs**: * ``node_obj_fxn [= str]``: unlike the default definition, this function accepts integers and returns the node object * ``directed [= False]`` (boolean): indicates whether the data is read as directed * ``ignore_duplicate_edges [= False]`` (boolean): applies only when loading an undirected graph. If True, then a check will be made to ensure that no duplicate edges are attempted to be added (in case the underlying graph was originally directed). Checking incurs a small performance cost due to the check. **Returns**: :py:class:`zen.Graph` or :py:class:`zen.DiGraph`. The graph read from the input string. The ``directed`` parameter decides whether a directed or undirected graph is constructed. """ # handle the keyword arguments node_obj_fxn = kwargs.pop('node_obj_fxn', str) directed = kwargs.pop('directed', False) check_for_duplicates = kwargs.pop('ignore_duplicate_edges', False) if len(kwargs) > 0: raise ZenException, 'Unknown keyword arguments: %s' % ', '.join( kwargs.keys()) if check_for_duplicates and directed: raise ZenException, 'ignore_duplicate_edges can only be set when directed = False' # build the graph G = None if directed == True: G = DiGraph() elif directed == False: G = Graph() else: raise ZenException, 'directed must be either True or False.' ##### # convert the string into a bitvector bv = BitVector(size=len(sbuffer) * 8) offset = 0 for c in sbuffer: v = ord(c) dec2bv(v, bv, offset, 8) offset += 8 ##### # read the header offset = 0 # read the version version = bv2dec(bv, offset, VERSION_LEN) offset += VERSION_LEN if version != 1: raise Exception, 'Invalid file format or version number' # read the num of indexes last_idx = bv2dec(bv, offset, NUM_INDEX_LEN) idx_size = int(math.ceil(math.log(last_idx + 1, 2))) offset += NUM_INDEX_LEN idx2node = [None] * (last_idx + 1) # build all nodes right now if node_obj_fxn is not None: for x in xrange(last_idx + 1): n = node_obj_fxn(x) G.add_node(n) else: G.add_nodes(last_idx + 1) # read the number of edges num_edges = bv2dec(bv, offset, NUM_EDGES_LEN) offset += NUM_EDGES_LEN ##### # Read the content: every edge if directed: for ei in xrange(num_edges): idx1 = bv2dec(bv, offset, idx_size) offset += idx_size idx2 = bv2dec(bv, offset, idx_size) offset += idx_size G.add_edge_(idx1, idx2) else: for ei in xrange(num_edges): idx1 = bv2dec(bv, offset, idx_size) offset += idx_size idx2 = bv2dec(bv, offset, idx_size) offset += idx_size if check_for_duplicates and G.has_edge_(idx1, idx2): continue G.add_edge_(idx1, idx2) # done! return G
def local_attachment(n, m, r, **kwargs): """ Generate a random graph using the local attachment model. **Args**: * ``n`` (int): the number of nodes to add to the graph * ``m`` (int): the number of edges a new node will add to the graph * ``r`` (int): the number of edges (of the ``m``) that a node will add to uniformly selected random nodes. All others will be added to neighbors of the ``r`` selected nodes. **KwArgs**: * ``seed [=-1]`` (int): a seed for the random number generator * ``graph [=None]`` (:py:class:`zen.DiGraph`): the graph that will be populated. If the graph is ``None``, then a new graph will be created. **Returns**: :py:class:`zen.DiGraph`. The graph generated. .. note:: Source: M. O. Jackson and B. O. Rogers "Meeting strangers and friends of friends: How random are social networks?" The American Economic Review, 2007. """ seed = kwargs.pop('seed', None) graph = kwargs.pop('graph', None) if graph is not None and not graph.is_directed(): raise ZenException, 'The graph provided must be directed' if graph is not None and len(graph) > 0: raise ZenException, 'The graph provided is not empty' if len(kwargs) > 0: raise ZenException, 'Unknown arguments: %s' % ', '.join(kwargs.keys()) if type(r) != int: raise ZenException, 'r must be an integer' elif r < 1: raise ZenException, 'r must be 1 or larger' if seed is None: seed = -1 if seed >= 0: random.seed(seed) ##### # build the initial graph G = graph if G is None: G = DiGraph() # populate with nodes for i in range(m + 1): G.add_node(i) # according to Jackson's paper, all initial nodes have m neighbors. for i in range(m + 1): for j in range(m + 1): if j != i: G.add_edge(j, i) ###### # Build the rest of the graph node_list = list(range(m + 1)) for i in range(m + 1, n): G.add_node(i) # pick random neighbors (the parents) parents = random.sample(node_list, r) # pick neighbors from the parents' neighborhoods potentials = set() for n in parents: potentials.update(G.out_neighbors(n)) potentials.difference_update(parents) nsize = min([m - r, len(potentials)]) neighbors = random.sample(potentials, nsize) # connect for v in (parents + neighbors): G.add_edge(i, v) node_list.append(i) # done return G