Explorar el Código

fix bug in matcher

Joeri Exelmans hace 1 año
padre
commit
f9049be167
Se han modificado 1 ficheros con 56 adiciones y 37 borrados
  1. 56 37
      pattern_matching/matcher.py

+ 56 - 37
pattern_matching/matcher.py

@@ -105,12 +105,14 @@ class MatcherVF2:
     def _match(self, state, already_visited, indent=0):
         # print("  "*indent, "match")
 
-        def visit_for_first_time(state):
-            hashable_state = state.make_hashable()
-            if hashable_state in already_visited:
-                return False
-            already_visited.add(hashable_state)
-            return True
+        hashable_state = state.make_hashable()
+        if hashable_state in already_visited:
+            # print("  "*indent, "    SKIP - ALREADY VISITED")
+            # print("  "*indent, "   ", hashable_state)
+            return
+        # print("  "*indent, "    ADD STATE")
+        # print("  "*indent, "   ", hashable_state)
+        already_visited.add(hashable_state)
 
 
         if len(state.mapping_edges) == len(self.guest.edges):
@@ -120,49 +122,63 @@ class MatcherVF2:
             yield state
             return
 
+        def read_edge(edge, direction):
+            if direction == "outgoing":
+                return edge.tgt
+            elif direction == "incoming":
+                return edge.src
+            else:
+                raise Exception("wtf!")
+
         def attempt_grow(direction, indent):
             # print("  "*indent, 'attempt_grow', direction)
             if state.boundary != None:
                 g_vtx, h_vtx = state.boundary
                 for g_candidate_edge in getattr(g_vtx, direction):
-                    # print("  "*indent, g_candidate_edge)
+                    # print("  "*indent, 'g_candidate_edge:', g_candidate_edge)
                     if g_candidate_edge in state.mapping_edges:
+                        # print("  "*indent, "  skip, guest edge already matched")
                         continue # skip already matched guest edge
-                    g_candidate_vtx = g_candidate_edge.tgt
+                    g_candidate_vtx = read_edge(g_candidate_edge, direction)
                     for h_candidate_edge in getattr(h_vtx, direction):
+                        # print("  "*indent, 'h_candidate_edge:', h_candidate_edge)
                         if h_candidate_edge in state.r_mapping_edges:
+                            # print("  "*indent, "  skip, host edge already matched")
                             continue # skip already matched host edge
-                        h_candidate_vtx = h_candidate_edge.tgt
+                        h_candidate_vtx = read_edge(h_candidate_edge, direction)
+                        # print("  "*indent, 'grow edge', g_candidate_edge, ':', h_candidate_edge)
                         new_state = state.grow_edge(h_candidate_edge, g_candidate_edge)
-                        if visit_for_first_time(new_state):
-                            # print("  "*indent, 'grow edge', g_candidate_edge, ':', h_candidate_edge)
-                            yield from attempt_match_vtxs(
-                                new_state,
-                                g_candidate_vtx,
-                                h_candidate_vtx,
-                                indent+1)
+                        yield from attempt_match_vtxs(
+                            new_state,
+                            g_candidate_vtx,
+                            h_candidate_vtx,
+                            indent+1)
 
         def attempt_match_vtxs(state, g_candidate_vtx, h_candidate_vtx, indent):
+            # print("  "*indent, 'attempt_match_vtxs')
+            if g_candidate_vtx in state.mapping_vtxs:
+                if state.mapping_vtxs[g_candidate_vtx] != h_candidate_vtx:
+                    # print("  "*indent, "  nope, guest already mapped (mismatch)")
+                    return # guest vtx is already mapped but doesn't match host vtx
+            if h_candidate_vtx in state.r_mapping_vtxs:
+                if state.r_mapping_vtxs[h_candidate_vtx] != g_candidate_vtx:
+                    # print("  "*indent, "  nope, host already mapped (mismatch)")
+                    return # host vtx is already mapped but doesn't match guest vtx
+            g_outdegree = len(g_candidate_vtx.outgoing)
+            h_outdegree = len(h_candidate_vtx.outgoing)
+            if g_outdegree > h_outdegree:
+                return
+            g_indegree = len(g_candidate_vtx.incoming)
+            h_indegree = len(h_candidate_vtx.incoming)
+            if g_indegree > h_indegree:
+                return
+            if not self.compare_fn(h_candidate_vtx.value, g_candidate_vtx.value):
+                return
             new_state = state.grow_vtx(
                 h_candidate_vtx,
                 g_candidate_vtx)
-            if visit_for_first_time(new_state):
-                # print("  "*indent, 'attempt_match_vtxs')
-                if h_candidate_vtx in state.r_mapping_vtxs:
-                    if state.r_mapping_vtxs[h_candidate_vtx] != g_candidate_vtx:
-                        return # host vtx is already mapped but doesn't match guest vtx
-                g_outdegree = len(g_candidate_vtx.outgoing)
-                h_outdegree = len(h_candidate_vtx.outgoing)
-                if g_outdegree > h_outdegree:
-                    return
-                g_indegree = len(g_candidate_vtx.incoming)
-                h_indegree = len(h_candidate_vtx.incoming)
-                if g_indegree > h_indegree:
-                    return
-                if not self.compare_fn(h_candidate_vtx.value, g_candidate_vtx.value):
-                    return
-                # print("  "*indent, 'grow vtx', g_candidate_vtx, ':', h_candidate_vtx)
-                yield from self._match(new_state, already_visited, indent+1)
+            # print("  "*indent, 'grow vtx', g_candidate_vtx, ':', h_candidate_vtx)
+            yield from self._match(new_state, already_visited, indent+1)
 
         # print("  "*indent, 'preferred...')
         yield from attempt_grow('outgoing', indent+1)
@@ -176,36 +192,39 @@ class MatcherVF2:
         # if indent == 0:
         #     print('visited', len(already_visited), 'states total')
 
-
 # demo time...
 if __name__ == "__main__":
     host = Graph()
-    host.vtxs = [Vertex(0), Vertex(1), Vertex(2)]
+    host.vtxs = [Vertex(0), Vertex(1), Vertex(2), Vertex(3)]
     host.edges = [
         Edge(host.vtxs[0], host.vtxs[1]),
         Edge(host.vtxs[1], host.vtxs[2]),
         Edge(host.vtxs[2], host.vtxs[0]),
+        Edge(host.vtxs[2], host.vtxs[3]),
+        Edge(host.vtxs[3], host.vtxs[2]),
     ]
 
     guest = Graph()
     guest.vtxs = [Vertex('src'), Vertex('tgt')]
     guest.edges = [
         Edge(guest.vtxs[0], guest.vtxs[1]),
+        Edge(guest.vtxs[1], guest.vtxs[0]),
     ]
 
     m = MatcherVF2(host, guest, lambda hv, gv: True)
     import time
     durations = 0
-    iterations = 2000
+    iterations = 100
+    print("Patience...")
     for n in range(iterations):
         time_start = time.perf_counter_ns()
         matches = [mm for mm in m.match()]
-        # print("found", len(matches), "matches")
         time_end = time.perf_counter_ns()
         time_duration = time_end - time_start
         durations += time_duration
 
     print(f'{iterations} iterations, took {durations/1000000:.3f} ms, {durations/iterations/1000000:.3f} ms per iteration')
+    print("found", len(matches), "matches")
     for mm in matches:
         print("match:")
         print(" ", mm.mapping_vtxs)