Exemple #1
0
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
Exemple #2
0
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)
Exemple #3
0
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)
Exemple #4
0
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
Exemple #5
0
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
Exemple #6
0
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
Exemple #7
0
                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)
Exemple #8
0
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
Exemple #9
0
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
Exemple #10
0
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
Exemple #11
0
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
Exemple #12
0
def read_str(sbuffer, **kwargs): #node_fxn=lambda x: x, edge_fxn=None, G = UNDIRECTED, build_all_nodes = False):
	"""
	Read graph data from the ascii string in the binary edge list format.
	"""
	
	# handle the keyword arguments
	node_obj_fxn = kwargs.pop('node_obj_fxn',str)
	directed = kwargs.pop('directed',False)
	check_for_duplicates = kwargs.pop('check_for_duplicates',False)
	
	if len(kwargs) > 0:
		raise ZenException, 'Unknown keyword arguments: %s' % ', '.join(kwargs.keys())
	
	if check_for_duplicates and directed:
		raise ZenException, 'check_for_duplicates 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
Exemple #13
0
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
Exemple #14
0
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