diff --git a/pattern_matching/matcher.py b/pattern_matching/matcher.py index 8817a9f..47a864d 100644 --- a/pattern_matching/matcher.py +++ b/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)