Example #1
0
def graph_extract(s: int,
                  g: Graph,
                  pmap_vrelevant=None,
                  pmap_erelevant=None,
                  callback_vertex_extract=None,
                  callback_edge_extract=None):
    """
    Extract the edges of a given Graph according to an edge-based filtering starting
    from a given source node.
    Args:
        s: The VertexDescriptor of the source node.
        g: A Graph instance.
        pmap_vrelevant: A ReadPropertyMap{VertexDescriptor : bool} which indicates
            for each vertex whether if it must be duped or not.
        pmap_erelevant: A ReadPropertyMap{EdgeDescriptor : bool} which indicates
            each edge of the Graph with a boolean equal to True iff the edge is relevant.
        callback_vertex_extract:
        callback_edge_extract:
    """
    if not pmap_vrelevant:
        pmap_vrelevant = make_func_property_map(lambda u: True)
    if not pmap_erelevant:
        pmap_erelevant = make_func_property_map(lambda e: True)

    map_vcolor = defaultdict(int)
    pmap_vcolor = make_assoc_property_map(map_vcolor)
    vis = DepthFirstSearchExtractVisitor(pmap_vrelevant, pmap_erelevant,
                                         pmap_vcolor, callback_vertex_extract,
                                         callback_edge_extract)
    depth_first_search(s,
                       g,
                       pmap_vcolor,
                       vis,
                       if_push=lambda e, g: pmap_erelevant[e] and
                       pmap_vrelevant[target(e, g)])
Example #2
0
def make_gdp2() -> GraphDp:
    g = make_g()
    vlabel = {u : "v%s" % u for u in vertices(g)}
    elabel = {e : "e%s%s" % (source(e, g), target(e, g)) for e in edges(g)}
    gdp = GraphDp(
        g,
        dpv = {
            "label" : make_assoc_property_map(vlabel),
            "color" : make_func_property_map(lambda q: "red" if q % 2 else "green"),
        },
        dpe = {
            "label" : make_assoc_property_map(elabel),
            "color" : make_func_property_map(lambda e: "red" if target(e, g) % 2 else "green"),
        }
    )
    return gdp
Example #3
0
def depth_first_search(
    s: int,
    g: Graph,
    pmap_vcolor: ReadWritePropertyMap = None,
    vis: DefaultDepthFirstSearchVisitor = None,
    # N.B: The following parameters does not exist in libboost:
    if_push=None  # if_push(e :EdgeDecriptor, g :Graph) -> bool returns True iff e is relevant
):
    if pmap_vcolor is None:
        map_vcolor = defaultdict(int)
        pmap_vcolor = make_assoc_property_map(map_vcolor)
    if vis is None:
        vis = DefaultDepthFirstSearchVisitor()
    if if_push is None:
        if_push = (lambda e, g: True)

    vis.start_vertex(s, g)
    pmap_vcolor[s] = GRAY
    vis.discover_vertex(s, g)
    u_edges = [e for e in out_edges(s, g) if if_push(e, g)]
    stack = deque([(s, 0, len(u_edges))])

    while stack:
        # Pop the current vertex u. Its (i-1)-th first out-edges have already
        # been visited. The out-degree of u is equal to n.
        (u, i, n) = stack.pop()
        u_edges = [e for e in out_edges(u, g) if if_push(e, g)]

        while i != n:
            # e is the current edge.
            e = u_edges[i]
            v = target(e, g)
            vis.examine_edge(e, g)
            color_v = pmap_vcolor[v]

            # (color[v] == WHITE) means that v has not yet been visited.
            if color_v == WHITE:
                # u must be re-examined later, its i-th out-edge has been visited.
                vis.tree_edge(e, g)
                i += 1
                stack.append((u, i, n))

                # v becomes the new current vertex
                u = v
                pmap_vcolor[u] = GRAY
                vis.discover_vertex(u, g)
                u_edges = [e for e in out_edges(u, g) if if_push(e, g)]
                i = 0
                n = len(u_edges)
            else:
                if color_v == GRAY:
                    vis.back_edge(e, g)
                else:
                    vis.forward_or_cross_edge(e, g)
                i += 1

        # u and all the vertices reachable from u have been visited.
        pmap_vcolor[u] = BLACK
        vis.finish_vertex(u, g)
Example #4
0
 def examine_edge(self, e: EdgeDescriptor, g: Graph):
     """
     Event triggered when the DFS discover a vertex not yet visited.
     Args:
         e: The EdgeDescriptor of the discovered vertex.
         g: The Graph.
     """
     if self.m_pmap_erelevant[e] and self.m_pmap_vrelevant[target(e, g)]:
         self.examine_relevant_edge(e, g)
Example #5
0
def graph_copy(s: int,
               g: Graph,
               g_dup: Graph,
               pmap_vrelevant: ReadPropertyMap = None,
               pmap_erelevant: ReadPropertyMap = None,
               pmap_vertices: ReadWritePropertyMap = None,
               pmap_edges: ReadWritePropertyMap = None,
               callback_dup_vertex=None,
               callback_dup_edge=None):
    """
    Copy a sub-graph from a Graph according to an edge-based filtering
    starting from a given source node.
    Args:
        s: The VertexDescriptor of the source node.
        g: A Graph instance.
        pmap_vrelevant: A ReadPropertyMap{VertexDescriptor : bool} which indicates
            for each vertex whether if it must be duped or not.
            Only used if vis == None.
        pmap_erelevant: A ReadPropertyMap{EdgeDescriptor : bool} which indicates
            for each edge whether if it must be duped or not.
            Only used if vis == None.
        callback_dup_vertex: Callback(u, g, u_dup, g_dup).
            Pass None if irrelevant.
        callback_dup_edge: Callback(e, g, e_dup, g_dup).
            Pass None if irrelevant.
        vis: Pass a custom DepthFirstSearchExtractVisitor or None.
            This visitor must overload super()'s methods.
    """
    # Prepare the needed mappings.
    map_vcolor = defaultdict(int)
    pmap_vcolor = make_assoc_property_map(map_vcolor)

    if not pmap_vrelevant:
        pmap_vrelevant = make_func_property_map(lambda u: True)
    if not pmap_erelevant:
        pmap_erelevant = make_func_property_map(lambda e: True)

    # Prepare the DepthFirstSearchCopyVisitor.
    if not pmap_vertices:
        map_vertices = dict()
        pmap_vertices = make_assoc_property_map(map_vertices)
    if not pmap_edges:
        map_edges = dict()
        pmap_edges = make_assoc_property_map(map_edges)

    vis = DepthFirstSearchCopyVisitor(g_dup, pmap_vrelevant, pmap_erelevant,
                                      pmap_vertices, pmap_edges, pmap_vcolor,
                                      callback_dup_vertex, callback_dup_edge)

    # Copy g to g_copy according to pmap_erelevant using a DFS from s.
    depth_first_search(s,
                       g,
                       pmap_vcolor,
                       vis,
                       if_push=lambda e, g: pmap_erelevant[e] and
                       pmap_vrelevant[target(e, g)])
Example #6
0
 def examine_relevant_edge(self, e: EdgeDescriptor, g: Graph):
     u = source(e, g)
     v = target(e, g)
     u_dup = self.m_pmap_vertices[u]
     v_dup = self.m_pmap_vertices[
         v] if v in self.m_dup_vertices else self.dup_vertex(v, g)
     (e_dup, _) = add_edge(u_dup, v_dup, self.m_g_dup)
     if self.m_pmap_edges:
         self.m_pmap_edges[e] = e_dup
     if self.m_callback_dup_edge:
         self.m_callback_dup_edge(e, g, e_dup, self.m_g_dup)
Example #7
0
    def finish_vertex(self, u: int, g: DirectedGraph):
        for e in out_edges(u, g):
            v = target(e, g)
            if self.m_pmap_component[v] == INFINITY:
                # u is attached to the "lowest" root among the root of u and v
                self.m_pmap_root[u] = self.discover_min(
                    self.m_pmap_root[u], self.m_pmap_root[v])

        if self.m_pmap_root[u] == u:
            # The vertices stacked since u belong to the same component of u.
            while True:
                v = self.m_stack.popleft()
                self.m_pmap_component[v] = self.total
                self.m_pmap_root[v] = u
                if u == v: break
            self.m_total += 1
Example #8
0
def edge_color(e, g, pmap_component, pmap_color, default_color = "black"):
    """
    Returns the color assigned to an edge.
    Args:
        e: An EdgeDescriptor.
        g: A DirectedGraph.
        pmap_component: A ReadPropertyMap, mapping a vertex with its strongly
            connected component.
        pmap_color: A ReadPropertyMap, mapping a component ID with a color.
        default_color: color returned if the color of the source and the
            target of e mismatch.
    """
    u = source(e, g)
    v = target(e, g)
    color_u = pmap_color[pmap_component[u]]
    color_v = pmap_color[pmap_component[v]]
    return color_u if color_u == color_v else default_color
Example #9
0
def dijkstra_shortest_paths_iteration(
    heap: Heap,
    g: DirectedGraph,
    pmap_eweight: ReadPropertyMap,
    pmap_vpreds: ReadWritePropertyMap,
    pmap_vdist: ReadWritePropertyMap,
    pmap_vcolor: ReadWritePropertyMap,
    compare: BinaryPredicate = Less(),  # Ignored, see Heap class.
    combine: BinaryFunction = ClosedPlus(),
    vis: DijkstraVisitor = DijkstraVisitor()):
    if vis is None:
        vis = DijkstraVisitor()

    u = heap.pop()
    w_su = pmap_vdist[u]
    vis.examine_vertex(u, g)

    # Update weight and predecessors of each successor of u
    for e in out_edges(u, g):
        vis.examine_edge(e, g)
        v = target(e, g)
        w_sv = pmap_vdist[v]
        w_uv = pmap_eweight[e]
        w = combine(w_su, w_uv)
        if compare(w, w_sv):  # Traversing u is worth!
            pmap_vdist[v] = w
            pmap_vpreds[v] = {e}
            if pmap_vcolor[v] == WHITE:
                heap.push(v)  # As v is WHITE, v cannot be in the heap.
                pmap_vcolor[v] = GRAY
                vis.discover_vertex(v, g)
            elif pmap_vcolor[v] == GRAY:
                heap.decrease_key(v)
            vis.edge_relaxed(e, g)
        elif w == w_sv:  # Hence we discover equally-cost shortest paths
            preds_v = pmap_vpreds[v]
            preds_v.add(e)
            pmap_vpreds[v] = preds_v
            vis.edge_relaxed(e, g)
        else:
            vis.edge_not_relaxed(e, g)
    pmap_vcolor[u] = BLACK
    vis.finish_vertex(u, g)
    return w_su
Example #10
0
File: cut.py Project: nokia/PyBGL
def cut(s: int, g: Graph, in_cut) -> set:
    """
    Find a vertex cut given an edge cut.
    Args:
        g: A `Graph` instance corresponding to an acyclic graph.
        s: The `VertexDescriptor` corresponding to the source vertex.
        in_cut: `Callback(EdgeDescriptor, Graph) -> bool` indicating whether an
            edge belong to the considered cut.
    """
    class LeavesVisitor(DefaultDepthFirstSearchVisitor):
        def __init__(self, leaves: set):
            self.leaves = leaves

        def examine_edge(self, e: EdgeDescriptor, g: Graph):
            u = source(e, g)
            self.leaves.discard(u)

        def discover_vertex(self, u: int, g: Graph):
            self.leaves.add(u)

    class IfPush:
        def __init__(self, in_cut, cutting_edges: set):
            self.in_cut = in_cut
            self.cutting_edges = cutting_edges

        def __call__(self, e: EdgeDescriptor, g: Graph) -> bool:
            is_cutting_edge = self.in_cut(e, g)
            if is_cutting_edge:
                self.cutting_edges.add(e)
            return not is_cutting_edge

    leaves = set()
    cutting_edges = set()
    map_vcolor = defaultdict(int)
    depth_first_search(s,
                       g,
                       pmap_vcolor=make_assoc_property_map(map_vcolor),
                       vis=LeavesVisitor(leaves),
                       if_push=IfPush(in_cut, cutting_edges))
    return {target(e, g)
            for e in cutting_edges
            } | {u
                 for u in leaves} - {source(e, g)
                                     for e in cutting_edges}
Example #11
0
def breadth_first_search_graph(
    g: Graph,
    sources: set = None,  # Or a generator e.g. vertices(g)
    pmap_vcolor: ReadWritePropertyMap = None,
    vis=None,
    # N.B: The following parameter does not exist in libboost:
    if_push=None  # if_push(e :EdgeDecriptor, g :Graph) -> bool returns True iff e is relevant
):
    if pmap_vcolor is None:
        map_vcolor = defaultdict(int)
        pmap_vcolor = make_assoc_property_map(map_vcolor)
    if vis is None:
        vis = DefaultBreadthFirstSearchVisitor()
    if not if_push:
        if_push = (lambda e, g: True)

    stack = deque()
    for s in sources:
        pmap_vcolor[s] = GRAY
        vis.discover_vertex(s, g)
        stack.append(s)

    while stack:
        u = stack.pop()
        vis.examine_vertex(u, g)
        for e in out_edges(u, g):
            if not if_push(e, g):
                continue
            v = target(e, g)
            vis.examine_edge(e, g)
            color_v = pmap_vcolor[v]
            if color_v == WHITE:
                vis.tree_edge(e, g)
                pmap_vcolor[v] = GRAY
                vis.discover_vertex(v, g)
                stack.appendleft(v)
            elif color_v == GRAY:
                vis.gray_target(e, g)
            else:
                vis.black_target(e, g)
        pmap_vcolor[u] = BLACK
        vis.finish_vertex(u, g)
def test_directed_graph(links: list = LINKS):
    map_eweight = dict()
    pmap_eweight = make_assoc_property_map(map_eweight)
    g = make_graph(LINKS,
                   pmap_eweight,
                   directed=True,
                   build_reverse_edge=False)

    map_vpreds = defaultdict(set)
    map_vdist = dict()
    dijkstra_shortest_paths(g, 0, pmap_eweight,
                            make_assoc_property_map(map_vpreds),
                            make_assoc_property_map(map_vdist))

    infty = sys.maxsize
    E = {(source(e, g), target(e, g)): e for e in edges(g)}

    assert map_vpreds == {
        1: {E[0, 1]},
        2: {E[1, 2]},
        3: {E[1, 3]},
        4: {E[3, 4]},
        5: {E[4, 5]},
        6: {E[4, 6]},
        7: {E[6, 7]},
        8: {E[7, 8]},
        9: {E[7, 9]}
    }
    assert map_vdist == {
        0: 0,
        1: 1,
        2: 2,
        3: 4,
        4: 5,
        5: 6,
        6: 6,
        7: 14,
        8: 15,
        9: 15,
        10: infty
    }
Example #13
0
def make_path(g: DirectedGraph, s: int, t: int,
              pmap_vpreds: ReadPropertyMap) -> list:
    """
    Extract the path from `s` to `t` in a graph `g` gien
    to `t` in a graph `g` given the predecessors map computed
    using `dijkstra_shortest_paths` from `s`.
    Args:
        g: The graph.
        s: The target's vertex descriptor.
        t: The target's vertex descriptor.
        pmap_vpreds: A `ReadPropertyMap{int : set(int)}` mapping each
            vertex with its set of (direct) predecessors.
    Returns:
        A list of consecutive arcs forming a shortest path from `s` of `t`.
        If several shortest paths exist from `s` to `t`, this function returns
        an arbitrary path.
    """
    arcs = make_dag(g, s, t, pmap_vpreds, single_path=True)
    path = list(arcs)
    path.sort(key=lambda e: (source(e, g), target(e, g)))
    return path
Example #14
0
 def edge_to_pair(e: EdgeDescriptor, g: DirectedGraph) -> tuple:
     return (source(e, g), target(e, g))
Example #15
0
def test_graph_copy_small(threshold :int = 50):
    g = DirectedGraph(5)
    (e01, _) = add_edge(0, 1, g)
    (e02, _) = add_edge(0, 2, g)
    (e04, _) = add_edge(0, 4, g)
    (e12, _) = add_edge(1, 2, g)
    (e23, _) = add_edge(2, 3, g)
    (e24, _) = add_edge(2, 4, g)
    (e40, _) = add_edge(4, 0, g)
    (e44, _) = add_edge(4, 4, g)
    map_eweight = {
        e01 : 83,
        e02 : 3,
        e04 : 78,
        e12 : 92,
        e23 : 7,
        e24 : 18,
        e40 : 51,
        e44 : 84,
    }
    pmap_eweight = make_assoc_property_map(map_eweight)
    pmap_erelevant = make_func_property_map(lambda e: pmap_eweight[e] >= threshold)

    g_dup = DirectedGraph(0)

    # Edge duplicate
    map_eweight_dup = dict()
    pmap_eweight_dup = make_assoc_property_map(map_eweight_dup)
    def callback_dup_edge(e, g, e_dup, g_dup):
        pmap_eweight_dup[e_dup] = pmap_eweight[e]

    # Vertex mapping
    map_vertices = dict()
    pmap_vertices = make_assoc_property_map(map_vertices)
    map_edges = dict()
    pmap_edges = make_assoc_property_map(map_edges)

    graph_copy(
        0, g, g_dup,
        pmap_erelevant    = pmap_erelevant,
        pmap_vertices     = pmap_vertices,
        pmap_edges        = pmap_edges,
        callback_dup_edge = callback_dup_edge
    )

    if in_ipynb():
        ori_html = dotstr_to_html(
            GraphDp(
                g,
                dpv = {
                    "label" : make_func_property_map(lambda u: "%s<br/>(becomes %s)" % (u, pmap_vertices[u]))
                },
                dpe = {
                    "color" : make_func_property_map(lambda e : "darkgreen" if pmap_erelevant[e] else "lightgrey"),
                    "style" : make_func_property_map(lambda e : "solid"     if pmap_erelevant[e] else "dashed"),
                    "label" : pmap_eweight,
                }
            ).to_dot()
        )
        dup_html = dotstr_to_html(
            GraphDp(
                g_dup,
                dpe = {
                    "label" : pmap_eweight_dup,
                }
            ).to_dot()
        )
        html(
            """
            <table>
                <tr>
                    <th>Original</th>
                    <th>Extracted</th>
                </tr><tr>
                    <td>%s</td>
                    <td>%s</td>
                </tr>
            </table>
            """ % (ori_html, dup_html)
        )

    if threshold == 50:
        expected_num_edges = 5
        assert map_vertices == {
            0 : 0,
            1 : 1,
            2 : 2,
            4 : 3
        }
        for e, e_dup in map_edges.items():
            u = source(e, g)
            v = target(e, g)
            u_dup = source(e_dup, g_dup)
            v_dup = target(e_dup, g_dup)
            assert u_dup == pmap_vertices[u], "u_dup = %s ; pmap_vertices[%s] = %s" % (u_dup, u, pmap_vertices[u])
            assert v_dup == pmap_vertices[v], "v_dup = %s ; pmap_vertices[%s] = %s" % (v_dup, v, pmap_vertices[v])
            assert pmap_eweight[e] == pmap_eweight_dup[e_dup]
    elif threshold < min([w for w in map_eweight.values()]):
        expected_num_edges = num_edges(g)
    elif threshold > max([w for w in map_eweight.values()]):
        expected_num_edges = 0

    assert expected_num_edges == num_edges(g_dup), \
        """
        Invalid edge number:
          Expected: %s
          Obtained: %s
        """ % (expected_num_edges, num_edges(g_dup))
Example #16
0
def dijkstra_shortest_paths(g: DirectedGraph,
                            s: int,
                            pmap_eweight: ReadPropertyMap,
                            pmap_vpreds: ReadWritePropertyMap,
                            pmap_vdist: ReadWritePropertyMap,
                            compare: BinaryPredicate = Less(),
                            combine: BinaryFunction = ClosedPlus(),
                            zero: int = 0,
                            infty: int = sys.maxsize,
                            vis: DijkstraVisitor = DijkstraVisitor()):
    """
    Compute the shortest path in a graph from a given source node.
    Args:
        g: A DirectedGraph instance.
        s: The vertex descriptor of the source node.
        pmap_eweight: A ReadPropertyMap{EdgeDescriptor : Distance}
            which map each edge with its weight.
        pmap_vpreds: A ReadWritePropertyMap{VertexDescriptor : EdgeDescriptor}
            which will map each vertex with its incident arcs in the shortest
            path Directed Acyclic Graph.
        pmap_vdist: A ReadWritePropertyMap{VertexDescriptor : Distance}
            which will map each vertex with the weight of its shortest path(s)
            from s.
        zero: The null Distance (e.g. 0).
        infty: The infinite Distance (e.g. sys.maxsize).
        vis: A DijkstraVisitor instance.

    Example:
        g = DirectedGraph(2)
        e, _ = add_edge(0, 1, g)
        map_eweight[e] = 10
        map_vpreds = defaultdict(set)
        map_vdist = dict()
        dijkstra_shortest_paths(
            g, u,
            make_assoc_property_map(map_eweight),
            make_assoc_property_map(map_vpreds),
            make_assoc_property_map(map_vdist)
        )
    """
    # WHITE: not yet processed, GRAY: under process, BLACK: processed.
    color = defaultdict(int)
    pmap_vcolor = make_assoc_property_map(color)
    pmap_vcolor[s] = WHITE

    for u in vertices(g):
        pmap_vdist[u] = zero if u == s else infty
        #pmap_vpreds[u] = set()
        #pmap_vcolor[u]  = GRAY if u == s else WHITE

    stack = {s}
    while stack:
        u = min(stack, key=lambda u: pmap_vdist[u])  # TODO use compare
        stack.remove(u)
        w_su = pmap_vdist[u]

        # Update weight and predecessors of each successor of u
        for e in out_edges(u, g):
            v = target(e, g)
            w_sv = pmap_vdist[v]
            w_uv = pmap_eweight[e]
            w = combine(w_su, w_uv)
            if compare(w, w_sv):  # Traversing u is worth!
                pmap_vdist[v] = w
                pmap_vpreds[v] = {e}
                if pmap_vcolor[v] == WHITE:
                    pmap_vcolor[v] = GRAY
                    stack.add(v)
            elif w == w_sv:  # Hence we discover equally-cost shortest paths
                preds_v = pmap_vpreds[v]
                preds_v.add(e)
                pmap_vpreds[v] = preds_v
        pmap_vcolor[u] = BLACK