예제 #1
0
def bipartite_preferential_attachment_graph(
    aseq,
    p,
    create_using=None,
):
    """
    Create a bipartite graph with a preferential attachment model
    from a given single "top" degree sequence.

    :Parameters:
       - `aseq`: degree sequence for node set A (top)
       - `p`:    probability that a new bottom node is added


    Reference::

     @article{guillaume-2004-bipartite,
       author = {Jean-Loup Guillaume and Matthieu Latapy},
       title = {Bipartite structure of all complex networks},
       journal = {Inf. Process. Lett.},
       volume = {90},
       number = {5},
       year = {2004},
       issn = {0020-0190},
       pages = {215--221},
       doi = {http://dx.doi.org/10.1016/j.ipl.2004.03.007},
       publisher = {Elsevier North-Holland, Inc.},
       address = {Amsterdam, The Netherlands, The Netherlands},
       }

    """
    if create_using == None:
        create_using = NX.MultiGraph()

    G = NX.empty_graph(0, create_using)

    if p > 1:
        raise NX.NetworkXError, "probability %s > 1" % (p)

    naseq = len(aseq)
    G.add_nodes_from(range(0, naseq))
    vv = [[v] * aseq[v] for v in range(0, naseq)]
    while vv:
        while vv[0]:
            source = vv[0][0]
            vv[0].remove(source)
            if random.random() < p or G.number_of_nodes() == naseq:
                target = G.number_of_nodes()
                G.add_edge(source, target)
            else:
                bb = [[b] * G.degree(b)
                      for b in range(naseq, G.number_of_nodes())]
                # flatten the list of lists into a list.
                bbstubs = reduce(lambda x, y: x + y, bb)
                # choose preferentially a bottom node.
                target = random.choice(bbstubs)
                G.add_edge(source, target)
        vv.remove(vv[0])
    G.name = "bipartite_preferential_attachment_model"
    return G
예제 #2
0
def bipartite_havel_hakimi_graph(
    aseq,
    bseq,
    create_using=None,
):
    """
    Return a bipartite graph from two given degree sequences
    using a Havel-Hakimi style construction.

    :Parameters:
       - `aseq`: degree sequence for node set A
       - `bseq`: degree sequence for node set B

    Nodes from the set A are connected to nodes in the set B by
    connecting the highest degree nodes in set A to
    the highest degree nodes in set B until all stubs are connected.

    The sum of the two sequences must be equal: sum(aseq)=sum(bseq)
    """
    if create_using == None:
        create_using = NX.MultiGraph()

    G = NX.empty_graph(0, create_using)

    # length of the each sequence
    naseq = len(aseq)
    nbseq = len(bseq)

    suma = sum(aseq)
    sumb = sum(bseq)

    if not suma == sumb:
        raise NX.NetworkXError, \
              'invalid degree sequences, sum(aseq)!=sum(bseq),%s,%s'\
              %(suma,sumb)

    G.add_nodes_from(range(0, naseq))  # one vertex type (a)
    G.add_nodes_from(range(naseq, naseq + nbseq))  # the other type (b)

    if max(aseq) == 0: return G  # done if no edges

    # build list of degree-repeated vertex numbers
    astubs = [[aseq[v], v] for v in range(0, naseq)]
    bstubs = [[bseq[v - naseq], v] for v in range(naseq, naseq + nbseq)]
    astubs.sort()
    while astubs:
        (degree, u) = astubs.pop()  # take of largest degree node in the a set
        if degree == 0: break  # done, all are zero
        # connect the source to largest degree nodes in the b set
        bstubs.sort()
        for target in bstubs[-degree:]:
            v = target[1]
            G.add_edge(u, v)
            target[0] -= 1  # note this updates bstubs too.
            if target[0] == 0:
                bstubs.remove(target)

    G.name = "bipartite_havel_hakimi_graph"
    return G
예제 #3
0
def bipartite_random_regular_graph(
    d,
    n,
    create_using=None,
):
    """
    UNTESTED:Generate a random bipartite graph of n nodes each with degree d.

    Restrictions on n and d:
       -  n must be even
       -  n>=2*d

    Nodes are numbered 0...n-1. 
    
    Algorithm inspired by random_regular_graph()

    """

    # helper subroutine to check for suitable edges
    def suitable(leftstubs, rightstubs):
        for s in leftstubs:
            for t in rightstubs:
                if not t in seen_edges[s]:
                    return True
        # else no suitable possible edges
        return False

    if not n * d % 2 == 0:
        print "n*d must be even"
        return False

    if not n % 2 == 0:
        print "n must be even"
        return False

    if not n >= 2 * d:
        print "n must be >= 2*d"
        return False

    if create_using == None:
        create_using = NX.MultiGraph()

    G = NX.empty_graph(0, create_using)

    nodes = range(0, n)
    G.add_nodes_from(nodes)

    seen_edges = {}
    [seen_edges.setdefault(v, {}) for v in nodes]

    vv = [[v] * d for v in nodes]  # List of degree-repeated vertex numbers
    stubs = reduce(lambda x, y: x + y,
                   vv)  # flatten the list of lists to a list

    leftstubs = stubs[:(n * d / 2)]
    rightstubs = stubs[n * d / 2:]

    while leftstubs:
        source = random.choice(leftstubs)
        target = random.choice(rightstubs)
        if source != target and not target in seen_edges[source]:
            leftstubs.remove(source)
            rightstubs.remove(target)
            seen_edges[source][target] = 1
            seen_edges[target][source] = 1
            G.add_edge(source, target)
        else:
            # further check to see if suitable
            if suitable(leftstubs, rightstubs) == False:
                return False
    return G
예제 #4
0
def bipartite_alternating_havel_hakimi_graph(
    aseq,
    bseq,
    create_using=None,
):
    """
    Return a bipartite graph from two given degree sequences
    using a alternating Havel-Hakimi style construction.

    :Parameters:
       - `aseq`: degree sequence for node set A
       - `bseq`: degree sequence for node set B

    Nodes from the set A are connected to nodes in the set B by
    connecting the highest degree nodes in set A to
    alternatively the highest and the lowest degree nodes in set
    B until all stubs are connected.

    The sum of the two sequences must be equal: sum(aseq)=sum(bseq)
    """
    if create_using == None:
        create_using = NX.MultiGraph()

    G = NX.empty_graph(0, create_using)

    # length of the each sequence
    naseq = len(aseq)
    nbseq = len(bseq)
    suma = sum(aseq)
    sumb = sum(bseq)

    if not suma == sumb:
        raise NX.NetworkXError, \
              'invalid degree sequences, sum(aseq)!=sum(bseq),%s,%s'\
              %(suma,sumb)

    G.add_nodes_from(range(0, naseq))  # one vertex type (a)
    G.add_nodes_from(range(naseq, naseq + nbseq))  # the other type (b)

    if max(aseq) == 0: return G  # done if no edges
    # build list of degree-repeated vertex numbers
    astubs = [[aseq[v], v] for v in range(0, naseq)]
    bstubs = [[bseq[v - naseq], v] for v in range(naseq, naseq + nbseq)]
    while astubs:
        astubs.sort()
        (degree, u) = astubs.pop()  # take of largest degree node in the a set
        if degree == 0: break  # done, all are zero
        bstubs.sort()
        small = bstubs[0:degree / 2]  # add these low degree targets
        large = bstubs[(-degree +
                        degree / 2):]  # and these high degree targets
        stubs = [x for z in zip(large, small) for x in z]  # combine, sorry
        if len(stubs) < len(small) + len(large):  # check for zip truncation
            stubs.append(large.pop())
        for target in stubs:
            v = target[1]
            G.add_edge(u, v)
            target[0] -= 1  # note this updates bstubs too.
            if target[0] == 0:
                bstubs.remove(target)

    G.name = "bipartite_alternating_havel_hakimi_graph"
    return G
예제 #5
0
def bipartite_configuration_model(
    aseq,
    bseq,
    create_using=None,
    seed=None,
):
    """
    Return a random bipartite graph from two given degree sequences.

    :Parameters:
       - `aseq`: degree sequence for node set A
       - `bseq`: degree sequence for node set B

    Nodes from the set A are connected to nodes in the set B by
    choosing randomly from the possible free stubs, one in A and
    one in B.

    The sum of the two sequences must be equal: sum(aseq)=sum(bseq)

    If no graph type is specified use MultiGraph with parallel edges.

    If you want a graph with no parallel edges use create_using=Graph()
    but then the resulting degree sequences might not be exact.

    """

    if create_using == None:
        create_using = NX.MultiGraph()

    G = NX.empty_graph(0, create_using)

    if not seed is None:
        random.seed(seed)

    # length and sum of each sequence
    lena = len(aseq)
    lenb = len(bseq)
    suma = sum(aseq)
    sumb = sum(bseq)

    if not suma == sumb:
        raise NX.NetworkXError, \
              'invalid degree sequences, sum(aseq)!=sum(bseq),%s,%s'\
              %(suma,sumb)

    G.add_nodes_from(range(0, lena + lenb))

    if max(aseq) == 0: return G  # done if no edges

    # build lists of degree-repeated vertex numbers
    stubs = []
    stubs.extend([[v] * aseq[v] for v in range(0, lena)])
    astubs = []
    astubs = [x for subseq in stubs for x in subseq]

    stubs = []
    stubs.extend([[v] * bseq[v - lena] for v in range(lena, lena + lenb)])
    bstubs = []
    bstubs = [x for subseq in stubs for x in subseq]

    # shuffle lists
    random.shuffle(astubs)
    random.shuffle(bstubs)

    G.add_edges_from([[astubs[i], bstubs[i]] for i in xrange(suma)])

    G.name = "bipartite_configuration_model"
    return G