def test_simple_graph(): # Prepare graph, just a 0 -> 1 arc g = DirectedGraph() u = add_vertex(g) v = add_vertex(g) e, added = add_edge(u, v, g) assert added w = 1 map_eweight = { e: w, } # Call Dijkstra 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)) # Check assert map_vpreds == { v: {e}, } assert map_vdist == {u: 0, v: w}
def test_parallel_edges(): # Prepare graph, two parallel edges from 0 to 1 g = DirectedGraph() u = add_vertex(g) v = add_vertex(g) (e1, added) = add_edge(u, v, g) assert added (e2, added) = add_edge(u, v, g) assert added w = 1 map_eweight = { e1: w, e2: w, } # Call Dijkstra 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)) # Check assert map_vpreds == { v: {e1, e2}, } assert map_vdist == {u: 0, v: w}
def test_graph_to_html_with_html_sequences(): from collections import defaultdict from pybgl.property_map import make_assoc_property_map g = DirectedGraph(2) (e, _) = add_edge(0, 1, g) pmap_vlabel = make_assoc_property_map(defaultdict(str)) pmap_elabel = make_assoc_property_map(defaultdict(str)) gdp = GraphDp(g, dpv={"label": pmap_vlabel}, dpe={"label": pmap_elabel}) for label in [ "<b>foo</b>", "<foo>", "<", ">", "<b>foo</b><bar>", "<bar><b>foo</b>", "<font color='red'><b>foo</b></font>", # NB: foo.png must exists + graphviz imposes <img/> not <img> #"<table><tr><td><img src='foo.png'/></td></tr></table>", ]: print(f"{label} --> {graphviz_escape_html(label)}") pmap_vlabel[0] = pmap_vlabel[1] = pmap_elabel[e] = label shtml = graph_to_html(gdp) if in_ipynb(): html(shtml) ipynb_display_graph(gdp)
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)])
def strong_components(g: DirectedGraph, pmap_component: ReadWritePropertyMap) -> int: map_vcolor = defaultdict(int) map_root = defaultdict(int) map_discover_time = defaultdict(int) pmap_vcolor = make_assoc_property_map(map_vcolor) pmap_root = make_assoc_property_map(map_root) pmap_discover_time = make_assoc_property_map(map_discover_time) stack = deque() vis = TarjanVisitor(pmap_component, pmap_root, pmap_discover_time, stack) depth_first_search_graph(g, vertices(g), pmap_vcolor, vis, None) return vis.total
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)])
def __init__(self, pmap_vlabel :ReadWritePropertyMap = None, num_vertices = 0): super().__init__(num_vertices) if not pmap_vlabel: self.map_vlabel = defaultdict() self.pmap_vlabel = make_assoc_property_map(self.map_vlabel) else: self.pmap_vlabel = pmap_vlabel
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
def test_all(): for directed in [True, False]: for (i, g) in enumerate([make_g1(directed), make_g2(directed)]): print("Processing G%s (directed = %s)" % (i, directed)) vis = MyDepthFirstSearchVisitor(verbose=False) map_color = defaultdict(int) depth_first_search(0, g, make_assoc_property_map(map_color), vis) n = num_vertices(g) m = num_edges(g) n_ = vis.num_vertices m_ = vis.num_edges # Our graph are connected, so these assertion should be verified assert n_ == n, "Visited %s/%s vertices" % (n_, n) if directed: assert m_ == m, "Visited %s/%s edges" % (m_, m) else: # Undirected edges are visited forward and backward, so they are visited "twice" # Not that (u -> u) arc would be only visited once. assert m_ == 2 * m, "Visited %s/%s edges" % (m_, m) # Finally, all vertices should all be BLACK for u in vertices(g): assert map_color[u] == BLACK
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)
def topological_sort(g :DirectedGraph, stack :deque = None) -> deque: stack = stack if stack else deque() depth_first_search_graph( g, pmap_vcolor = make_assoc_property_map(defaultdict(int)), vis = TopologicalSortVisitor(stack) ) return stack
def test_isolated_vertices(): # Create 10 isolated vertices infty = sys.maxsize g = DirectedGraph(10) map_eweight = dict() for s in vertices(g): map_vpreds = defaultdict(set) map_vdist = dict() dijkstra_shortest_paths(g, s, make_assoc_property_map(map_eweight), make_assoc_property_map(map_vpreds), make_assoc_property_map(map_vdist)) # No incident arc in the shortest path DAG assert map_vpreds == dict() # Every target are at infinite distance excepted the source node. assert map_vdist == {u: infty if u != s else 0 for u in vertices(g)}
def parallel_breadth_first_search( g1: Automaton, g2: Automaton, source_pairs=None, pmap_vcolor: ReadWritePropertyMap = None, vis: ParallelBreadthFirstSearchVisitor = ParallelBreadthFirstSearchVisitor(), if_push=None, delta=delta): def get_edge(q, r, a, g): assert q is not None # It may be useful to consider (q, BOTTOM, a), see parallel_walk algorithm and tree_edge #assert r is not None return EdgeDescriptor(q, r, a) if q is not None and r == delta(q, a, g) else \ EdgeDescriptor(q, BOTTOM, a) stack = deque() if source_pairs == None: q01 = initial(g1) q02 = initial(g2) stack.appendleft((q01, q02)) vis.start_vertex(q01, g1, q02, g2) else: for (s1, s2) in source_pairs: stack.appendleft((s1, s2)) vis.start_vertex(s1, g1, s2, g2) if not pmap_vcolor: map_vcolor = defaultdict(int) pmap_vcolor = make_assoc_property_map(map_vcolor) if not if_push: if_push = (lambda e1, g1, e2, g2: True) while stack: (q1, q2) = stack.pop() vis.examine_vertex(q1, g1, q2, g2) for a in sigma(q1, g1) | sigma(q2, g2): (r1, r2) = (delta(q1, a, g1), delta(q2, a, g2)) vis.examine_symbol(q1, g1, q2, g2, a) e1 = get_edge(q1, r1, a, g1) if q1 is not BOTTOM else None e2 = get_edge(q2, r2, a, g2) if q2 is not BOTTOM else None vis.examine_edge(e1, g1, e2, g2, a) color = pmap_vcolor[(r1, r2)] if color == WHITE: vis.tree_edge(e1, g1, e2, g2, a) pmap_vcolor[(r1, r2)] = GRAY vis.discover_vertex(r1, g1, r2, g2) if if_push(e1, g1, e2, g2): stack.appendleft((r1, r2)) elif color == GRAY: vis.gray_target(e1, g1, e2, g2, a) else: vis.black_target(e1, g1, e2, g2, a) pmap_vcolor[(q1, q2)] = BLACK vis.finish_vertex(q1, g2, q2, g2)
def test_graph_extract_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) pmap_eweight = make_assoc_property_map({ e01 : 83, e02 : 3, e04 : 78, e12 : 92, e23 : 7, e24 : 18, e40 : 51, e44 : 84, }) extracted_edges = set() pmap_erelevant = make_func_property_map(lambda e: pmap_eweight[e] >= threshold) graph_extract( 0, g, pmap_erelevant = pmap_erelevant, callback_edge_extract = lambda e, g: extracted_edges.add(e) ) if in_ipynb(): pmap_extracted = make_func_property_map(lambda e: e in extracted_edges) html(dotstr_to_html(GraphDp( g, dpe = { "color" : make_func_property_map(lambda e : "darkgreen" if pmap_extracted[e] else "lightgrey"), "style" : make_func_property_map(lambda e : "solid" if pmap_extracted[e] else "dashed"), "label" : pmap_eweight, } ).to_dot()) ) expected_edges = None if threshold == 0: expected_edges = {e for e in edges(g)} elif threshold == 50: expected_edges = {e12, e40, e44, e04, e01} elif threshold > 100: expected_edges = set() if expected_edges is not None: assert (extracted_edges == expected_edges), """Invalid edges: For threshold = %s: extracted: %s expected: %s """ % (threshold, sorted(extracted_edges), sorted(expected_edges))
def __init__(self, num_vertices: int = 0, q0: int = 0, pmap_final=None, pmap_vsymbol: ReadWritePropertyMap = None): # Convention: self.adjacencies[q][a] = r super().__init__(num_vertices, q0, pmap_final) if pmap_vsymbol is None: map_vsymbol = defaultdict(lambda: None) pmap_vsymbol = make_assoc_property_map(map_vsymbol) self.pmap_vsymbol = pmap_vsymbol
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 }
def depth_first_search_graph( g: Graph, sources: set = None, # Or a generator e.g. vertices(g) pmap_vcolor: ReadWritePropertyMap = None, vis: DefaultDepthFirstSearchVisitor = None, if_push: bool = None # if_push(e :EdgeDecriptor) -> bool ): if pmap_vcolor is None: map_vcolor = defaultdict(int) pmap_vcolor = make_assoc_property_map(map_vcolor) for u in (sources if sources else vertices(g)): if pmap_vcolor[u] == WHITE: depth_first_search(u, g, pmap_vcolor, vis, if_push)
def gold_make_automaton(self): """ Builds an automaton from the observation table information. Returns: a tuple (b, g) where: b: bool, True if the operation is possible, False otherwise g: Automaton, an automaton that recognizes the language if b is True, the PTA recognizing the language otherwise """ if self.fill_holes: if not self.try_and_fill_holes(): return False, self.gold_make_pta() epsilon_idx = self.exp.index('') states = sorted(list(self.red_states.keys()), key=lambda s: (len(s), s)) transitions = [] if self.fill_holes: for q in states: for a in self.sigma: qa_val = self.red_states.get( q + a, self.blue_states.get(q + a, None)) for (r, r_val) in self.red_states.items(): if qa_val == r_val: transitions += [(q, r, a)] break else: for q in states: for a in self.sigma: if q + a in states: transitions += [(q, q + a, a)] else: qa_val = self.blue_states.get(q + a, None) r = self.choose_compatible_red_state(qa_val) transitions += [(q, r, a)] transitions = [(states.index(q), states.index(r), a) for (q, r, a) in transitions] final_states = defaultdict( bool, { states.index(state): True if self.red_states[state][epsilon_idx] == self.ONE else False for state in states }) g = make_automaton(transitions, states.index(''), make_assoc_property_map(final_states)) if not self.fill_holes: if not self.is_consistent_with_samples(g): return False, self.gold_make_pta() return True, g
def find_reachable_vertices(g: Graph, sources: set) -> set: """ Returns the set of vertices of a graph which are reachable from a set of source vertices. Args: g: Graph, an instance of `Graph` sources: set, a set of integers representing the source vertices Returns: The set of vertices that are reachable from the source vertices """ map_vcolor = defaultdict(int) pmap_vcolor = make_assoc_property_map(map_vcolor) depth_first_search_graph(g, sources, pmap_vcolor=pmap_vcolor) return set(map_vcolor.keys())
def _test_make_assoc_property_map(with_dict: bool): # Build the underlying dictionary d = dict() if with_dict else defaultdict( str) # Here we use str instead of chr, because chr() does not exists! # Initialize the property map (and its underlying dict) pmap_rot13 = make_assoc_property_map(d) for a in alphabet(): pmap_rot13[a] = rot13(a) check_rot13(pmap_rot13) # This will not trigger KeyError, because we used defaultdict instead of dict. # This is the behavior we expect (std::map<K, V> in C++ ~ defaultdict(V) in python). x = pmap_rot13['!']
def test_strong_components(): # Create the graph g = DirectedGraph(7) add_edge(0, 1, g) add_edge(1, 2, g) add_edge(2, 3, g) add_edge(3, 1, g) add_edge(3, 4, g) add_edge(4, 5, g) add_edge(5, 6, g) add_edge(6, 4, g) # Find strong connected components map_component = {u : None for u in vertices(g)} pmap_component = make_assoc_property_map(map_component) strong_components(g, pmap_component) # Rendering pmap_color = make_assoc_property_map({ 0 : "red", 1 : "blue", 2 : "green", 3 : "purple" }) assert map_component == { 0 : 2, 1 : 1, 2 : 1, 3 : 1, 4 : 0, 5 : 0, 6 : 0, } if in_ipynb(): html(strong_components_to_html(g, pmap_color, pmap_component).replace("\\n", ""))
def test_read_graphviz_custom(): from collections import defaultdict from pybgl.property_map import ReadWritePropertyMap, make_assoc_property_map from pybgl.graphviz import ReadGraphvizVisitor class MyReadGraphvizVisitor(ReadGraphvizVisitor): def __init__(self, g: Graph, pmap_vlabel: ReadWritePropertyMap, pmap_elabel: ReadWritePropertyMap): super().__init__(g) self.pmap_vlabel = pmap_vlabel self.pmap_elabel = pmap_elabel def on_install_vertex_property(self, u, g, key, value): if key == "label": self.pmap_vlabel[u] = value def on_install_edge_property(self, e, g, key, value): if key == "label": self.pmap_elabel[e] = value map_vlabel = defaultdict(str) map_elabel = defaultdict(str) g = DirectedGraph() dot = """digraph G { 0 [fontsize=8 label='red']; 1 [label='green']; 2 [label='blue' fontsize=10]; 0->1 [label='my_label']; }""" vis = MyReadGraphvizVisitor(g, make_assoc_property_map(map_vlabel), make_assoc_property_map(map_elabel)) read_graphviz(dot.splitlines(), g, vis) if in_ipynb(): ipynb_display_graph(g) assert map_vlabel == {0: "red", 1: "green", 2: "blue"}, map_vlabel e_01 = next(iter(edges(g))) assert map_elabel == {e_01: "my_label"}, map_vlabel
def test_incidence_node_automaton_pmap_vlabel(): map_vlabel = defaultdict(lambda: None) map_vlabel[v] = "a" map_vlabel[w] = "b" g = IncidenceNodeAutomaton( 3, pmap_vsymbol=make_assoc_property_map(map_vlabel)) assert num_vertices(g) == 3 print(g.adjacencies) assert symbol(u, g) is None, f"Got {symbol(u, g)}" assert symbol(v, g) == "a" assert symbol(w, g) == "b" assert num_edges(g) == 0 add_edge(u, v, g) assert num_edges(g) == 1 add_edge(u, w, g) assert num_edges(g) == 2
def test_make_incidence_node_automaton(): g = make_incidence_node_automaton( [(0, 1), (0, 2), (1, 2)], q0n=0, pmap_vlabel=make_assoc_property_map( defaultdict(lambda: None, { 1: "a", 2: "b" })), pmap_vfinal=make_func_property_map(lambda u: u in {0, 2})) assert num_vertices(g) == 3 assert num_edges(g) == 3 for u in vertices(g): assert is_initial(u, g) == (u == 0) assert is_final(u, g) == (u in {0, 2}) assert symbol(0, g) is None assert symbol(1, g) == "a" assert symbol(2, g) == "b"
def test_bfs_tree(): map_order = dict() pmap_order = make_assoc_property_map(map_order) g = make_binary_tree(10) vis = MyVisitor(pmap_order) bfs_tree(0, g, vis) assert map_order == { 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, }
def test_dfs_tree(): map_order = dict() pmap_order = make_assoc_property_map(map_order) g = make_binary_tree(10) vis = MyVisitor(pmap_order) dfs_tree(0, g, vis) assert map_order == { 0: 0, 1: 4, 2: 1, 3: 7, 4: 5, 5: 3, 6: 2, 7: 9, 8: 8, 9: 6, }
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}
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 hopcroft_minimize(g: IncidenceAutomaton) -> IncidenceAutomaton: if is_empty(g): return g aggregated_states = list(hopcroft_agglomerate_states(g)) # Find the aggregated state corresponding to the initial state q0 = None for idx, qs in enumerate(aggregated_states): if any(is_initial(q, g) for q in qs): q0 = idx break assert q0 is not None # Swap q0 and 0 so that the initial state is 0. if q0 != 0: tmp = aggregated_states[0] aggregated_states[0] = aggregated_states[q0] aggregated_states[q0] = tmp q0 = 0 # Assign an index to each state in the new automaton map_set_idx = {qs: idx for idx, qs in enumerate(list(aggregated_states))} # Build the set of final states in the new automaton final_states_new = defaultdict( bool, { map_set_idx[qs]: True if any(is_final(q, g) for q in qs) else False for qs in aggregated_states }) # Build the minimized automaton min_g = IncidenceAutomaton(len(aggregated_states), q0, make_assoc_property_map(final_states_new)) for qs in aggregated_states: for q in qs: for e in out_edges(q, g): r = target(e, g) a = label(e, g) rs = None for rs in aggregated_states: if r in rs: break add_edge(map_set_idx[qs], map_set_idx[rs], a, min_g) return min_g
def dijkstra_shortest_paths( g: DirectedGraph, s: int, pmap_eweight: ReadPropertyMap, pmap_vpreds: ReadWritePropertyMap, pmap_vdist: ReadWritePropertyMap, pmap_vcolor: ReadWritePropertyMap = None, compare: BinaryPredicate = None, # Ignored, see Heap class. combine: BinaryFunction = ClosedPlus(), zero: int = 0, infty: int = INFINITY, vis: DijkstraVisitor = None): """ Compute the shortest path in a graph from a given source node and according to the `(Distance, compare, combine)` semi-ring. 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. Each element must be initially mapped with `set()`. pmap_vdist: A `ReadWritePropertyMap{VertexDescriptor : Distance}` which will map each vertex with the weight of its shortest path(s) from `s`. Each element must be initialized to `zero`. 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) ) """ if vis is None: vis = DijkstraVisitor() if pmap_vcolor is None: color = defaultdict(int) pmap_vcolor = make_assoc_property_map(color) # Initialization dijkstra_shortest_paths_initialization(g, s, pmap_vcolor, pmap_vdist, zero, infty, vis) # Iteration if not compare: compare = Less() heap = Heap([s], to_comparable=lambda u: pmap_vdist[u]) else: heap = Heap([s], to_comparable=lambda u: Comparable(pmap_vdist[u], compare)) while heap: dijkstra_shortest_paths_iteration(heap, g, pmap_eweight, pmap_vpreds, pmap_vdist, pmap_vcolor, compare, combine, vis)