| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 | """Implementation of the Wright, Richmond, Odlyzko and McKay (WROM)algorithm for the enumeration of all non-isomorphic free trees of agiven order.  Rooted trees are represented by level sequences, i.e.,lists in which the i-th element specifies the distance of vertex i tothe root."""__all__ = ["nonisomorphic_trees", "number_of_nonisomorphic_trees"]import networkx as nxdef nonisomorphic_trees(order, create="graph"):    """Returns a list of nonisomporphic trees    Parameters    ----------    order : int      order of the desired tree(s)    create : graph or matrix (default="Graph)      If graph is selected a list of trees will be returned,      if matrix is selected a list of adjancency matrix will      be returned    Returns    -------    G : List of NetworkX Graphs    M : List of Adjacency matrices    References    ----------    """    if order < 2:        raise ValueError    # start at the path graph rooted at its center    layout = list(range(order // 2 + 1)) + list(range(1, (order + 1) // 2))    while layout is not None:        layout = _next_tree(layout)        if layout is not None:            if create == "graph":                yield _layout_to_graph(layout)            elif create == "matrix":                yield _layout_to_matrix(layout)            layout = _next_rooted_tree(layout)def number_of_nonisomorphic_trees(order):    """Returns the number of nonisomorphic trees    Parameters    ----------    order : int      order of the desired tree(s)    Returns    -------    length : Number of nonisomorphic graphs for the given order    References    ----------    """    return sum(1 for _ in nonisomorphic_trees(order))def _next_rooted_tree(predecessor, p=None):    """One iteration of the Beyer-Hedetniemi algorithm."""    if p is None:        p = len(predecessor) - 1        while predecessor[p] == 1:            p -= 1    if p == 0:        return None    q = p - 1    while predecessor[q] != predecessor[p] - 1:        q -= 1    result = list(predecessor)    for i in range(p, len(result)):        result[i] = result[i - p + q]    return resultdef _next_tree(candidate):    """One iteration of the Wright, Richmond, Odlyzko and McKay    algorithm."""    # valid representation of a free tree if:    # there are at least two vertices at layer 1    # (this is always the case because we start at the path graph)    left, rest = _split_tree(candidate)    # and the left subtree of the root    # is less high than the tree with the left subtree removed    left_height = max(left)    rest_height = max(rest)    valid = rest_height >= left_height    if valid and rest_height == left_height:        # and, if left and rest are of the same height,        # if left does not encompass more vertices        if len(left) > len(rest):            valid = False        # and, if they have the same number or vertices,        # if left does not come after rest lexicographically        elif len(left) == len(rest) and left > rest:            valid = False    if valid:        return candidate    else:        # jump to the next valid free tree        p = len(left)        new_candidate = _next_rooted_tree(candidate, p)        if candidate[p] > 2:            new_left, new_rest = _split_tree(new_candidate)            new_left_height = max(new_left)            suffix = range(1, new_left_height + 2)            new_candidate[-len(suffix) :] = suffix        return new_candidatedef _split_tree(layout):    """Returns a tuple of two layouts, one containing the left    subtree of the root vertex, and one containing the original tree    with the left subtree removed."""    one_found = False    m = None    for i in range(len(layout)):        if layout[i] == 1:            if one_found:                m = i                break            else:                one_found = True    if m is None:        m = len(layout)    left = [layout[i] - 1 for i in range(1, m)]    rest = [0] + [layout[i] for i in range(m, len(layout))]    return (left, rest)def _layout_to_matrix(layout):    """Create the adjacency matrix for the tree specified by the    given layout (level sequence)."""    result = [[0] * len(layout) for i in range(len(layout))]    stack = []    for i in range(len(layout)):        i_level = layout[i]        if stack:            j = stack[-1]            j_level = layout[j]            while j_level >= i_level:                stack.pop()                j = stack[-1]                j_level = layout[j]            result[i][j] = result[j][i] = 1        stack.append(i)    return resultdef _layout_to_graph(layout):    """Create a NetworkX Graph for the tree specified by the    given layout(level sequence)"""    G = nx.Graph()    stack = []    for i in range(len(layout)):        i_level = layout[i]        if stack:            j = stack[-1]            j_level = layout[j]            while j_level >= i_level:                stack.pop()                j = stack[-1]                j_level = layout[j]            G.add_edge(i, j)        stack.append(i)    return G
 |