コード例 #1
0
def _delete_nonterminal_leaves(edges, terminals):
    r"""Prunes all non-terminal leaves from the tree.

    Given the edges of a tree and a set of terminal vertices, this method
    removes all non-terminal leaves and returns the new tree edges.

    Parameters
    ----------
    edges : list
        A list of the edges of the tree.

    terminals : set
        A set of the terminal vertices.

    Returns
    -------
    tree_edges : list
        A list of the edges of the pruned tree.

    Notes
    -----
    This method deletes all non-terminal leaves from the tree. This process
    is repeated until all leaves are terminals. There is room to improve
    this algorithm.
    """
    # Form a graph from the edge list.
    tree = Graph()
    for (u, v) in edges:
        tree.add_edge(u, v)

    # Now delete all leaves that are not terminals
    while True:
        leaves = tree.leaves()
        changed = False

        for leaf in leaves:
            if leaf not in terminals:
                tree.delete_vertex(leaf)
                changed = True

        if not changed:
            break

    return tree.edges()
コード例 #2
0
def gabow(graph, k=None):
    def find_exchange(father, included, excluded):
        X = NamedUnionFind(vertices)
        find = X.find
        union = X.union

        (min_weight, e, f) = (inf, None, None)

        for (x, y) in included:
            # Make it so that y = father[x]
            if y != father[x]:
                x, y = y, x
            y = find(y)
            union(x, y, y)

        for edge in excluded:
            mark(edge)

        for (x, y) in edges:
            if (x, y) in marked or (y, x) in marked:
                unmark((x, y))
                unmark((y, x))

            elif father[x] != y and father[y] != x:
                a = find(x)
                ancestors = set()
                while a not in ancestors:
                    ancestors.add(a)
                    a = find(father[a])

                a = find(y)
                while a not in ancestors:
                    a = find(father[a])

                for u in [x, y]:
                    v = find(u)
                    while v != a:
                        fv = father[v]
                        exchange_weight = weight[x, y] - weight[v, fv]
                        if exchange_weight < min_weight:
                            min_weight = exchange_weight
                            e = (v, fv)
                            f = (x, y)
                        w = find(fv)
                        union(v, w, w)
                        v = w

        return (min_weight, e, f)

    inf = float('inf')
    if k is None:
        k = inf

    weight = {(u, v): graph[u][v] for u in graph for v in graph[u]}

    marked = set()
    mark = marked.add
    unmark = marked.discard

    edges = sorted(graph.edge_set(), key=lambda (u, v): weight[(u, v)])
    vertices = graph.vertices()

    # arbitrary root vertex
    root = vertices[0]
    tree_weight, father = prim(graph, root, edge_list=False)
    father[root] = root

    (exchange_weight, e, f) = find_exchange(father, [], [])
    heap = [(tree_weight + exchange_weight, e, f, father, [], [])]

    tree_edges = sorted([(min(x, y), max(x, y)) for (x, y) in father.items()
                         if x != y])
    yield tree_weight, tree_edges

    j = 1
    while j < k:
        (tree_weight, e, f, father, included, excluded) = heappop(heap)

        if tree_weight == inf:
            return

        new_graph = Graph()
        new_graph.add_edges(father.items())
        new_graph.delete_edge(e)
        new_graph.add_edge(f)

        new_father = breadth_first_search_tree(new_graph, root)
        new_father[root] = root

        tree_edges = sorted([(min(x, y), max(x, y))
                             for (x, y) in new_father.items() if x != y])
        yield tree_weight, tree_edges

        new_tree_weight = tree_weight - weight[f] + weight[e]

        included_i = included + [e]
        excluded_j = excluded + [e]

        (exchange_weight, e, f) = find_exchange(father, included_i, excluded)
        heappush(heap, (new_tree_weight + exchange_weight, e, f, father,
                        included_i, excluded))

        (exchange_weight, e, f) = find_exchange(new_father, included,
                                                excluded_j)
        heappush(heap, (tree_weight + exchange_weight, e, f, new_father,
                        included, excluded_j))
        j += 1
コード例 #3
0
def distance_network_heuristic(graph, terminals):
    r"""Returns an approximate minimal Steiner tree connecting `terminals`
    in `graph`.

    Given a connected, undirected graph `graph` with positive edge weights
    and a subset of the vertices `terminals`, this method finds a subgraph
    with near-minimal total edge cost among all connected subgraphs that
    contain these vertices.

    Parameters
    ----------
    graph : rosemary.graphs.graphs.Graph

    terminals : list or set or tuple
        A collection of vertices of `graph`.

    Returns
    -------
    (weight, edges) : tuple
        The number `weight` is the weight of the Steiner tree. The list
        `edges` is a list of the edges in the Steiner tree.

    Notes
    -----
    The Steiner problem in graphs asks to find the minimal tree connecting
    the terminal vertices. This problem is NP-complete and has proven to be
    extremely difficult even for small problem instances. Given this, it is
    of practical importance to have heuristics for finding low-cost Steiner
    trees.

    This method uses the heuristic algorithm given in [1], which produces a
    tree spanning the terminals with total cost <= 2*(1 - 1/t)*MST, where t
    is the number of terminal vertices and MST is the weight of a minimal
    Steiner tree. We also apply the improvement procedure given in [3].

    The implementation given runs in time O(t*|E|*log(|V|)), where |E| is
    the number of edges of `graph` and |V| is the number of vertices.

    References
    ----------
    .. [1] L. Kou, G. Markowsky, L. Berman, "A Fast Algorithm for Steiner
    Trees", Acta Informatica, Volume 15, Issue 2, June 1981, 141-145.

    .. [2] P. Winter, "Steiner Problem in Networks: A Survey", Networks,
    Volume 17, Issue 2, Summer 1987, 129-167.

    .. [3] S. Voss, "Steiner's Problem in Graphs: Heuristic Methods",
    Discrete Applied Mathematics 40, 1992, 45-72.

    .. [4] H.J. Proemel, A. Steger, "The Steiner Tree Problem - A Tour
    through Graphs, Algorithms, and Complexity", Vieweg, 2002.

    Examples
    --------
    >>> G = Graph()
    >>> G.add_edges([('u1', 'u2', 1), ('u1', 'v1', 2), ('u1', 'v2', 2),
                     ('u2', 'v3', 4), ('u2', 'v4', 3), ('u2', 'v5', 5),
                     ('u3', 'v1', 2), ('u3', 'v3', 8), ('u4', 'v2', 8),
                     ('u4', 'v5', 8), ('v1', 'v2', 8), ('v1', 'v3', 9),
                     ('v2', 'v5', 5), ('v3', 'v4', 8)])
    >>> distance_network_heuristic(G, ['v1', 'v2', 'v3', 'v4', 'v5'])
    (17, [('u1', 'u2'), ('u1', 'v1'), ('u1', 'v2'), ('u2', 'v3'),
          ('u2', 'v4'), ('v2', 'v5')])
    """
    # Create the distance network induced by the terminal set.
    distance_network = Graph()

    # shortest_prev[u] holds the predecessor dict for the shortest path
    # tree rooted at u.
    shortest_prev = {}
    shortest_dist = {}

    for u in terminals:
        u_dist, u_prev = dijkstra(graph, u)
        shortest_dist[u] = u_dist
        shortest_prev[u] = u_prev

    # For each pair (u, v) of terminal vertices, add an edge with weight
    # equal to the length of the shortest u, v path.
    distance_network.add_edges([(u, v, shortest_dist[u][v]) for (u, v) in itertools.combinations(terminals, 2)])

    # Determine the minimum spanning tree of the distance network.
    _, mst_edges = prim(distance_network, edge_list=True)
    subnetwork = Graph()

    # Construct a subnetwork of the graph by replacing each edge in the
    # minimum spanning tree by the corresponding minimum cost path.
    for (u, v) in mst_edges:
        a, b = shortest_prev[u][v], v
        while a is not None:
            subnetwork.add_edge(a, b, graph[a][b])
            a, b = shortest_prev[u][a], a

    # Determine the minimum spanning tree of the subnetwork.
    _, subnetwork_mst_edges = prim(subnetwork, edge_list=True)
    tree_weight, tree_edges = _improve(graph, subnetwork_mst_edges, terminals)

    return (tree_weight, tree_edges)
コード例 #4
0
ファイル: spanning_trees.py プロジェクト: ynasser/rosemary
def gabow(graph, k=None):
    def find_exchange(father, included, excluded):
        X = NamedUnionFind(vertices)
        find = X.find
        union = X.union

        (min_weight, e, f) = (inf, None, None)

        for (x, y) in included:
            # Make it so that y = father[x]
            if y != father[x]:
                x, y = y, x
            y = find(y)
            union(x, y, y)

        for edge in excluded:
            mark(edge)

        for (x, y) in edges:
            if (x, y) in marked or (y, x) in marked:
                unmark((x, y))
                unmark((y, x))

            elif father[x] != y and father[y] != x:
                a = find(x)
                ancestors = set()
                while a not in ancestors:
                    ancestors.add(a)
                    a = find(father[a])

                a = find(y)
                while a not in ancestors:
                    a = find(father[a])

                for u in [x, y]:
                    v = find(u)
                    while v != a:
                        fv = father[v]
                        exchange_weight = weight[x, y] - weight[v, fv]
                        if exchange_weight < min_weight:
                            min_weight = exchange_weight
                            e = (v, fv)
                            f = (x, y)
                        w = find(fv)
                        union(v, w, w)
                        v = w

        return (min_weight, e, f)

    inf = float('inf')
    if k is None:
        k = inf

    weight = {(u, v): graph[u][v] for u in graph for v in graph[u]}

    marked = set()
    mark = marked.add
    unmark = marked.discard

    edges = sorted(graph.edge_set(), key=lambda (u, v): weight[(u, v)])
    vertices = graph.vertices()

    # arbitrary root vertex
    root = vertices[0]
    tree_weight, father = prim(graph, root, edge_list=False)
    father[root] = root

    (exchange_weight, e, f) = find_exchange(father, [], [])
    heap = [(tree_weight + exchange_weight, e, f, father, [], [])]

    tree_edges = sorted([(min(x, y), max(x, y)) for (x, y) in father.items() if x != y])
    yield tree_weight, tree_edges

    j = 1
    while j < k:
        (tree_weight, e, f, father, included, excluded) = heappop(heap)

        if tree_weight == inf:
            return

        new_graph = Graph()
        new_graph.add_edges(father.items())
        new_graph.delete_edge(e)
        new_graph.add_edge(f)

        new_father = breadth_first_search_tree(new_graph, root)
        new_father[root] = root

        tree_edges = sorted([(min(x, y), max(x, y)) for (x, y) in new_father.items() if x != y])
        yield tree_weight, tree_edges

        new_tree_weight = tree_weight - weight[f] + weight[e]

        included_i = included + [e]
        excluded_j = excluded + [e]

        (exchange_weight, e, f) = find_exchange(father, included_i, excluded)
        heappush(heap, (new_tree_weight + exchange_weight, e, f, father, included_i, excluded))

        (exchange_weight, e, f) = find_exchange(new_father, included, excluded_j)
        heappush(heap, (tree_weight + exchange_weight, e, f, new_father, included, excluded_j))
        j += 1