Пример #1
0
    def test_dijkstra(self):
        distance, previous = dijkstra(self.graph, 'a')
        self.assertEqual(distance, self.distance)
        self.assertEqual(previous, self.previous)

        self.assertRaisesRegexp(ValueError,
                                "dijkstra: 0 is not a vertex of graph.",
                                dijkstra, self.graph, 0)

        distance, previous = dijkstra(self.graph2, 'd')
        self.assertEqual(distance, self.distance2)
        self.assertEqual(previous, self.previous2)
Пример #2
0
    def test_dijkstra_iterator(self):
        f = lambda g, u: list(dijkstra_iterator(g, u))
        self.assertRaisesRegexp(ValueError,
                                "dijkstra_iterator: 0 is not a vertex of graph.",
                                f, self.graph, 0)

        for graph in (self.graph, self.graph2):
            paths = list(dijkstra_iterator(graph, 'a'))
            ddist, dprev = dijkstra(graph, 'a')
            for (node, dist, path) in paths:
                self.assertEqual(dist, ddist[node])
Пример #3
0
    def test_dijkstra_bidirectional(self):
        self.assertRaisesRegexp(ValueError,
                                "dijkstra_bidirectional: 0 is not a vertex of graph.",
                                dijkstra_bidirectional, self.graph, 0, 'a')

        self.assertRaisesRegexp(ValueError,
                                "dijkstra_bidirectional: 2 is not a vertex of graph.",
                                dijkstra_bidirectional, self.graph, 'a', 2)

        dist, path = dijkstra_bidirectional(self.graph, 'a', 'e')
        ddist, dprev = dijkstra(self.graph, 'a')
        self.assertEqual(dist, ddist['e'])

        self.graph.add_vertex('f')
        self.assertEqual(dijkstra_bidirectional(self.graph, 'a', 'f'), float('inf'))
Пример #4
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)
Пример #5
0
def minimum_cost_paths_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 use the modification given in [2], using each terminal
    as a root, and keeping the best tree. We also apply the improvement
    procedure given in [2].

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

    References
    ----------
    .. [1] H. Takahashi, A. Matsuyama, "An Approximate Solution for the
    Steiner Problem in Graphs", Math. Japonica 24, No. 6, 1980, 573-577.

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

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

    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')])
    """
    # 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

    best_weight = inf

    for root in terminals:
        tree_edges = []
        tree_vertices = set()
        terminal_set = set(terminals)
        terminal_set.discard(root)
        tree_vertices.add(root)

        # Repeat until the tree contains all terminals.
        while terminal_set:
            min_dist = inf
            # Find vertices u and v with minimal distance with v in the
            # tree and u a terminal not in the tree.
            for u in terminal_set - tree_vertices:
                for v in tree_vertices:
                    if shortest_dist[u][v] < min_dist:
                        min_dist = shortest_dist[u][v]
                        min_vertices = (u, v)

            u, v = min_vertices
            a, b = shortest_prev[u][v], v

            tree_vertices.add(u)
            terminal_set.discard(u)

            # Add the edges (a, b) in the shortest u, v path to the tree.
            while a is not None:
                if a < b:
                    tree_edges.append((a, b))
                else:
                    tree_edges.append((b, a))

                tree_vertices.add(a)
                a, b = shortest_prev[u][a], a

        # Apply the improvement procedure to the tree.
        tree_weight, tree_edges = _improve(graph, tree_edges, terminals)

        if tree_weight < best_weight:
            best_weight = tree_weight
            best_edges = tree_edges

    return best_weight, best_edges
Пример #6
0
 def test_dijkstra(self):
     distance, previous = dijkstra(self.graph, 'a')
     self.assertEqual(distance, self.distance)
     self.assertEqual(previous, self.previous)