Ejemplo n.º 1
0
def Molloy_Reed(degrees: Union[np.array, Dict[int, float]], multiedge: bool = False, relax: bool=False, node_uids: Optional[list] = None) -> Optional[Network]:
    """Generate Molloy-Reed graph.

    Generates a random undirected network with given degree sequence based on
    the Molloy-Reed algorithm.

    .. note::

        The condition proposed by Erdös and Gallai (1967) is used to test
        whether the degree sequence is graphic, i.e. whether a network with the
        given degree sequence exists.

    Parameters
    ----------
    degrees : list

        List of integer node degrees. The number of nodes of the generated
        network corresponds to len(degrees).

    relax : bool

        If True, we conceptually allow self-loops and multi-edges, but do not
        add them to the network This implies that the generated network may not
        have exactly sum(degrees)/2 links, but it ensures that the algorithm
        always finishes.

    node_uids : list

        Optional list of node uids that will be used.

    Examples
    --------
    Generate random undirected network with given degree sequence

    >>> import pathpy as pp
    >>> random_network = pp.algorithms.random_graphs.Molloy_Reed([1,0])
    >>> print(random_network.summary())
    ...

    Network generation fails for non-graphic sequences

    >>> import pathpy as pp
    >>> random_network = pp.algorithms.random_graphs.Molloy_Reed([1,0])
    >>> print(random_network)
    None

    """

    # assume that we are given a graphical degree sequence
    if not is_graphic_Erdos_Gallai(degrees):
        return None

    # create empty network with n nodes
    n = len(degrees)
    network = Network(directed=False, multiedges=multiedge)

    if node_uids is None or len(node_uids) != n:
        LOG.info('No valid node uids given, generating numeric node uids')
        node_uids = []
        for i in range(n):
            node_uids.append(str(i))
    
    for i in range(n):
        network.add_node(node_uids[i])

    # generate link stubs based on degree sequence
    stubs = []
    for i in range(n):
        for _ in range(int(degrees[i])):
            stubs.append(str(node_uids[i]))

    # connect randomly chosen pairs of link stubs
    while(len(stubs) > 0):
        v, w = np.random.choice(stubs, 2, replace=False)

        if v == w or (multiedge == False and relax == False and network.nodes[w] in network.successors[v]):
            # remove random edge and add stubs
            if network.number_of_edges()>0:
                edge = random.choice(list(network.edges))
                stubs.append(edge.v.uid)
                stubs.append(edge.w.uid)
                network.remove_edge(edge)
        else:
            if not network.nodes[w] in network.successors[v]:
                network.add_edge(v, w)
            stubs.remove(v)
            stubs.remove(w)            
            

    return network
Ejemplo n.º 2
0
def Watts_Strogatz(n: int, s: int, p: float = 0.0, loops: bool = False,
                   node_uids: Optional[list] = None) -> Network:
    """Undirected Watts-Strogatz lattice network

    Generates an undirected Watts-Strogatz lattice network with lattice
    dimensionality one.

    Parameters
    ----------
    n : int

        The number of nodes in the generated network

    s : float

        The number of nearest neighbors that will be connected
        in the ring lattice

    p : float

        The rewiring probability

    node_uids : list

        Optional list of node uids that will be used.

    Examples
    --------
    Generate a Watts-Strogatz network with 100 nodes

    >>> import pathpy as pp
    >>> small_world = pp.algorithms.random_graphs.Watts_Strogatz(n=100, s=2, p=0.1)
    >>> print(small_world.summary())
    ...

    """
    network = Network(directed=False)
    if node_uids is None or len(node_uids) != n:
        LOG.info('No valid node uids given, generating numeric node uids')
        node_uids = []
        for i in range(n):
            network.add_node(Node(str(i)))
            node_uids.append(str(i))
    else:
        for i in range(n):
            network.add_node(node_uids[i])

    # construct a ring lattice (dimension 1)
    for i in range(n):
        if loops:
            x = 0
            y = s
        else:
            x = 1
            y = s+1
        for j in range(x, y):
            v = network.nodes[node_uids[i]]
            w = network.nodes[node_uids[(i+j) % n]]
            if (v.uid, w.uid) not in network.edges:
                network.add_edge(v, w)

    if p == 0:
        # nothing to do here
        return network

    # Rewire each link with probability p
    for edge in tqdm(list(network.edges.values()), 'generating WS network'):
        if np.random.rand() < p:
            # Delete original link and remember source node
            v = edge.v.uid
            network.remove_edge(edge)

            # Find new random tgt, which is not yet connected to src
            new_target = None

            # This loop repeatedly chooses a random target until we find
            # a target not yet connected to src. Note that this could potentially
            # result in an infinite loop depending on parameters.
            while new_target is None:
                x = node_uids[np.random.randint(n)]
                if (x != v or loops) and (v, x) not in network.edges:
                    new_target = x
            network.add_edge(v, new_target)
    return network