Add performance comparison between Sten's and Joeri's matcher. Sten's seems to be broken (giving a different match set every time), however.
This commit is contained in:
parent
b4f41cc090
commit
bed3529676
2 changed files with 354 additions and 58 deletions
286
pattern_matching/benchmark.py
Normal file
286
pattern_matching/benchmark.py
Normal file
|
|
@ -0,0 +1,286 @@
|
||||||
|
import time
|
||||||
|
|
||||||
|
import matcher as j # joeri's matcher
|
||||||
|
import graph as sgraph # sten's graph
|
||||||
|
import patternMatching as s # sten's matcher
|
||||||
|
import generator
|
||||||
|
|
||||||
|
def j_to_s(j):
|
||||||
|
s = sgraph.Graph()
|
||||||
|
m = {}
|
||||||
|
for jv in j.vtxs:
|
||||||
|
sv = s.addCreateVertex(jv.value) # value becomes type
|
||||||
|
m[jv] = sv
|
||||||
|
for je in j.edges:
|
||||||
|
s.addCreateEdge(m[je.src], m[je.tgt], "e") # only one type
|
||||||
|
return s
|
||||||
|
|
||||||
|
def s_to_j(s):
|
||||||
|
jg = j.Graph()
|
||||||
|
jg.vtxs = [ j.Vertex(typ) for (typ,svs) in s.vertices.items() for sv in svs ]
|
||||||
|
m = { sv : jg.vtxs[i] for svs in s.vertices.values() for i,sv in enumerate(svs) }
|
||||||
|
jg.edges = [j.Edge(m[se.src], m[se.tgt]) for ses in s.edges.values() for se in ses ]
|
||||||
|
return j
|
||||||
|
|
||||||
|
|
||||||
|
def run_benchmark(jhost, jguest, shost, sguest, expected=None):
|
||||||
|
j_durations = 0
|
||||||
|
s_durations = 0
|
||||||
|
|
||||||
|
# benchmark Joeri
|
||||||
|
m = j.MatcherVF2(host, guest,
|
||||||
|
lambda g_val, h_val: g_val == h_val) # all vertices can be matched
|
||||||
|
iterations = 50
|
||||||
|
print(" Patience (joeri)...")
|
||||||
|
for n in range(iterations):
|
||||||
|
time_start = time.perf_counter_ns()
|
||||||
|
matches = [mm for mm in m.match()]
|
||||||
|
time_end = time.perf_counter_ns()
|
||||||
|
duration = time_end - time_start
|
||||||
|
j_durations += duration
|
||||||
|
print(f' {iterations} iterations, took {j_durations/1000000:.3f} ms, {j_durations/iterations/1000000:.3f} ms per iteration')
|
||||||
|
if expected == None:
|
||||||
|
print(f" {len(matches)} matches")
|
||||||
|
else:
|
||||||
|
if len(matches) == expected:
|
||||||
|
print(" correct (probably)")
|
||||||
|
else:
|
||||||
|
print(f" WRONG! expected: {expected}, got: {len(matches)}")
|
||||||
|
# print([m.mapping_vtxs for m in matches])
|
||||||
|
# print([m.mapping_edges for m in matches])
|
||||||
|
|
||||||
|
# benchmark Sten
|
||||||
|
m = s.PatternMatching()
|
||||||
|
print(" Patience (sten)...")
|
||||||
|
for n in range(iterations):
|
||||||
|
time_start = time.perf_counter_ns()
|
||||||
|
matches = [mm for mm in m.matchVF2(sguest, shost)]
|
||||||
|
time_end = time.perf_counter_ns()
|
||||||
|
duration = time_end - time_start
|
||||||
|
s_durations += duration
|
||||||
|
print(f' {iterations} iterations, took {s_durations/1000000:.3f} ms, {s_durations/iterations/1000000:.3f} ms per iteration')
|
||||||
|
if expected == None:
|
||||||
|
print(f" {len(matches)} matches")
|
||||||
|
else:
|
||||||
|
if len(matches) == expected:
|
||||||
|
print(" correct (probably)")
|
||||||
|
else:
|
||||||
|
print(f" WRONG! expected: {expected}, got: {len(matches)}")
|
||||||
|
# print(matches)
|
||||||
|
|
||||||
|
print(f" joeri is {s_durations/j_durations:.2f} times faster")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
print("\nBENCHMARK: small graph, simple pattern")
|
||||||
|
|
||||||
|
host = j.Graph()
|
||||||
|
host.vtxs = [j.Vertex(0), j.Vertex(0), j.Vertex(0), j.Vertex(0)]
|
||||||
|
host.edges = [
|
||||||
|
j.Edge(host.vtxs[0], host.vtxs[1]),
|
||||||
|
j.Edge(host.vtxs[1], host.vtxs[2]),
|
||||||
|
j.Edge(host.vtxs[2], host.vtxs[0]),
|
||||||
|
j.Edge(host.vtxs[2], host.vtxs[3]),
|
||||||
|
j.Edge(host.vtxs[3], host.vtxs[2]),
|
||||||
|
]
|
||||||
|
|
||||||
|
guest = j.Graph()
|
||||||
|
guest.vtxs = [
|
||||||
|
j.Vertex(0),
|
||||||
|
j.Vertex(0)]
|
||||||
|
guest.edges = [
|
||||||
|
# Look for a simple loop:
|
||||||
|
j.Edge(guest.vtxs[0], guest.vtxs[1]),
|
||||||
|
j.Edge(guest.vtxs[1], guest.vtxs[0]),
|
||||||
|
]
|
||||||
|
|
||||||
|
# because of the symmetry in our pattern, there will be 2 matches
|
||||||
|
|
||||||
|
run_benchmark(host, guest, j_to_s(host), j_to_s(guest), expected=2)
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
|
||||||
|
print("\nBENCHMARK: larger graph, simple pattern")
|
||||||
|
|
||||||
|
host = j.Graph()
|
||||||
|
host.vtxs = [
|
||||||
|
j.Vertex('triangle'), # 0
|
||||||
|
j.Vertex('square'), # 1
|
||||||
|
j.Vertex('square'), # 2
|
||||||
|
j.Vertex('circle'), # 3
|
||||||
|
j.Vertex('circle'), # 4
|
||||||
|
j.Vertex('circle'), # 5
|
||||||
|
]
|
||||||
|
host.edges = [
|
||||||
|
# not a match:
|
||||||
|
j.Edge(host.vtxs[0], host.vtxs[5]),
|
||||||
|
j.Edge(host.vtxs[5], host.vtxs[0]),
|
||||||
|
|
||||||
|
# will be a match:
|
||||||
|
j.Edge(host.vtxs[1], host.vtxs[5]),
|
||||||
|
j.Edge(host.vtxs[5], host.vtxs[1]),
|
||||||
|
|
||||||
|
# noise:
|
||||||
|
j.Edge(host.vtxs[1], host.vtxs[2]),
|
||||||
|
|
||||||
|
# will be a match:
|
||||||
|
j.Edge(host.vtxs[2], host.vtxs[4]),
|
||||||
|
j.Edge(host.vtxs[4], host.vtxs[2]),
|
||||||
|
|
||||||
|
# noise:
|
||||||
|
j.Edge(host.vtxs[0], host.vtxs[1]),
|
||||||
|
j.Edge(host.vtxs[0], host.vtxs[3]),
|
||||||
|
j.Edge(host.vtxs[0], host.vtxs[0]),
|
||||||
|
j.Edge(host.vtxs[1], host.vtxs[1]),
|
||||||
|
|
||||||
|
# will be a match:
|
||||||
|
j.Edge(host.vtxs[3], host.vtxs[2]),
|
||||||
|
j.Edge(host.vtxs[2], host.vtxs[3]),
|
||||||
|
]
|
||||||
|
|
||||||
|
guest = j.Graph()
|
||||||
|
guest.vtxs = [
|
||||||
|
j.Vertex('square'), # 0
|
||||||
|
j.Vertex('circle')] # 1
|
||||||
|
guest.edges = [
|
||||||
|
j.Edge(guest.vtxs[0], guest.vtxs[1]),
|
||||||
|
j.Edge(guest.vtxs[1], guest.vtxs[0]),
|
||||||
|
]
|
||||||
|
|
||||||
|
# should give 3 matches
|
||||||
|
|
||||||
|
run_benchmark(host, guest, j_to_s(host), j_to_s(guest), expected=3)
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
|
||||||
|
print("\nBENCHMARK: same as before, but with larger pattern")
|
||||||
|
|
||||||
|
host = j.Graph()
|
||||||
|
host.vtxs = [
|
||||||
|
j.Vertex('triangle'), # 0
|
||||||
|
j.Vertex('square'), # 1
|
||||||
|
j.Vertex('square'), # 2
|
||||||
|
j.Vertex('circle'), # 3
|
||||||
|
j.Vertex('circle'), # 4
|
||||||
|
j.Vertex('circle'), # 5
|
||||||
|
]
|
||||||
|
host.edges = [
|
||||||
|
# not a match:
|
||||||
|
j.Edge(host.vtxs[0], host.vtxs[5]),
|
||||||
|
j.Edge(host.vtxs[5], host.vtxs[0]),
|
||||||
|
|
||||||
|
# will be a match:
|
||||||
|
j.Edge(host.vtxs[1], host.vtxs[5]),
|
||||||
|
j.Edge(host.vtxs[5], host.vtxs[1]),
|
||||||
|
|
||||||
|
# noise:
|
||||||
|
j.Edge(host.vtxs[1], host.vtxs[2]),
|
||||||
|
|
||||||
|
# will be a match:
|
||||||
|
j.Edge(host.vtxs[2], host.vtxs[4]),
|
||||||
|
j.Edge(host.vtxs[4], host.vtxs[2]),
|
||||||
|
|
||||||
|
# noise:
|
||||||
|
j.Edge(host.vtxs[0], host.vtxs[1]),
|
||||||
|
j.Edge(host.vtxs[0], host.vtxs[3]),
|
||||||
|
j.Edge(host.vtxs[0], host.vtxs[0]),
|
||||||
|
j.Edge(host.vtxs[1], host.vtxs[1]),
|
||||||
|
|
||||||
|
# will be a match:
|
||||||
|
j.Edge(host.vtxs[3], host.vtxs[2]),
|
||||||
|
j.Edge(host.vtxs[2], host.vtxs[3]),
|
||||||
|
]
|
||||||
|
|
||||||
|
guest = j.Graph()
|
||||||
|
guest.vtxs = [
|
||||||
|
j.Vertex('square'), # 0
|
||||||
|
j.Vertex('circle'), # 1
|
||||||
|
j.Vertex('square')] # 2
|
||||||
|
guest.edges = [
|
||||||
|
j.Edge(guest.vtxs[0], guest.vtxs[1]),
|
||||||
|
j.Edge(guest.vtxs[1], guest.vtxs[0]),
|
||||||
|
j.Edge(guest.vtxs[2], guest.vtxs[0]),
|
||||||
|
]
|
||||||
|
|
||||||
|
# this time, only 2 matches
|
||||||
|
|
||||||
|
run_benchmark(host, guest, j_to_s(host), j_to_s(guest), expected=2)
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
|
||||||
|
print("\nBENCHMARK: disconnected pattern")
|
||||||
|
|
||||||
|
host = j.Graph()
|
||||||
|
host.vtxs = [
|
||||||
|
j.Vertex('triangle'), # 0
|
||||||
|
j.Vertex('square'), # 1
|
||||||
|
j.Vertex('square'), # 2
|
||||||
|
j.Vertex('circle'), # 3
|
||||||
|
j.Vertex('circle'), # 4
|
||||||
|
j.Vertex('circle'), # 5
|
||||||
|
j.Vertex('bear'),
|
||||||
|
j.Vertex('bear'),
|
||||||
|
]
|
||||||
|
host.edges = [
|
||||||
|
# not a match:
|
||||||
|
j.Edge(host.vtxs[0], host.vtxs[5]),
|
||||||
|
j.Edge(host.vtxs[5], host.vtxs[0]),
|
||||||
|
|
||||||
|
# will be a match:
|
||||||
|
j.Edge(host.vtxs[1], host.vtxs[5]),
|
||||||
|
j.Edge(host.vtxs[5], host.vtxs[1]),
|
||||||
|
|
||||||
|
# noise:
|
||||||
|
j.Edge(host.vtxs[1], host.vtxs[2]),
|
||||||
|
|
||||||
|
# will be a match:
|
||||||
|
j.Edge(host.vtxs[2], host.vtxs[4]),
|
||||||
|
j.Edge(host.vtxs[4], host.vtxs[2]),
|
||||||
|
|
||||||
|
# noise:
|
||||||
|
j.Edge(host.vtxs[0], host.vtxs[1]),
|
||||||
|
j.Edge(host.vtxs[0], host.vtxs[3]),
|
||||||
|
j.Edge(host.vtxs[0], host.vtxs[0]),
|
||||||
|
j.Edge(host.vtxs[1], host.vtxs[1]),
|
||||||
|
|
||||||
|
# will be a match:
|
||||||
|
j.Edge(host.vtxs[3], host.vtxs[2]),
|
||||||
|
j.Edge(host.vtxs[2], host.vtxs[3]),
|
||||||
|
]
|
||||||
|
|
||||||
|
guest = j.Graph()
|
||||||
|
guest.vtxs = [
|
||||||
|
j.Vertex('square'), # 0
|
||||||
|
j.Vertex('circle'), # 1
|
||||||
|
j.Vertex('bear')]
|
||||||
|
guest.edges = [
|
||||||
|
j.Edge(guest.vtxs[0], guest.vtxs[1]),
|
||||||
|
j.Edge(guest.vtxs[1], guest.vtxs[0]),
|
||||||
|
]
|
||||||
|
|
||||||
|
# the 'bear' in our pattern can be matched with any of the two bears in the graph, effectively doubling the number of matches
|
||||||
|
|
||||||
|
run_benchmark(host, guest, j_to_s(host), j_to_s(guest), expected=6)
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
|
||||||
|
print("\nBENCHMARK: larger graph")
|
||||||
|
|
||||||
|
shost, sguest = generator.get_large_host_and_guest()
|
||||||
|
run_benchmark(s_to_j(shost), s_to_j(sguest), shost, sguest)
|
||||||
|
|
||||||
|
#######################################################################
|
||||||
|
|
||||||
|
print("\nBENCHMARK: large random graph")
|
||||||
|
|
||||||
|
import random
|
||||||
|
random.seed(0)
|
||||||
|
|
||||||
|
shost, sguest = generator.get_random_host_and_guest(
|
||||||
|
nr_vtxs = 10,
|
||||||
|
nr_vtx_types = 0,
|
||||||
|
nr_edges = 20,
|
||||||
|
nr_edge_types = 0,
|
||||||
|
)
|
||||||
|
run_benchmark(s_to_j(shost), s_to_j(sguest), shost, sguest)
|
||||||
|
|
||||||
|
|
@ -26,7 +26,6 @@ class PatternMatching(object):
|
||||||
Returns an occurrence of a given pattern from the given Graph
|
Returns an occurrence of a given pattern from the given Graph
|
||||||
"""
|
"""
|
||||||
def __init__(self, optimize=True):
|
def __init__(self, optimize=True):
|
||||||
# store the type of matching we want to use
|
|
||||||
self.optimize = optimize
|
self.optimize = optimize
|
||||||
|
|
||||||
def matchNaive(self, pattern, vertices, edges, pattern_vertices=None):
|
def matchNaive(self, pattern, vertices, edges, pattern_vertices=None):
|
||||||
|
|
@ -34,17 +33,19 @@ class PatternMatching(object):
|
||||||
Try to find an occurrence of the pattern in the Graph naively.
|
Try to find an occurrence of the pattern in the Graph naively.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
print('matchNaive...')
|
# print('matchNaive...')
|
||||||
print('pattern:', pattern)
|
# print('pattern.vertices:', pattern.vertices)
|
||||||
print('vertices:', vertices)
|
# print('pattern.edges:', pattern.edges)
|
||||||
print('edges:', edges)
|
# print('vertices:', vertices)
|
||||||
print('pattern_vertices:', pattern_vertices)
|
# print('edges:', edges)
|
||||||
|
# print('pattern_vertices:', pattern_vertices)
|
||||||
|
|
||||||
# allow call with specific arguments
|
# allow call with specific arguments
|
||||||
if pattern_vertices == None:
|
if pattern_vertices == None:
|
||||||
pattern_vertices = pattern.vertices
|
pattern_vertices = pattern.vertices
|
||||||
|
|
||||||
def visitEdge(pattern_vertices, p_edge, inc, g_edges, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
|
def visitEdge(pattern_vertices, p_edge, inc, g_edges, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
|
||||||
|
# print('visitEdge')
|
||||||
"""
|
"""
|
||||||
Visit a pattern edge, and try to bind it to a graph edge.
|
Visit a pattern edge, and try to bind it to a graph edge.
|
||||||
(If the first fails, try the second, and so on...)
|
(If the first fails, try the second, and so on...)
|
||||||
|
|
@ -70,6 +71,7 @@ class PatternMatching(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def visitEdges(pattern_vertices, p_edges, inc, g_edges, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
|
def visitEdges(pattern_vertices, p_edges, inc, g_edges, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
|
||||||
|
# print('visitEdges')
|
||||||
"""
|
"""
|
||||||
Visit all edges of the pattern vertex (edges given as argument).
|
Visit all edges of the pattern vertex (edges given as argument).
|
||||||
We need to try visiting them for all its permutations, as matching
|
We need to try visiting them for all its permutations, as matching
|
||||||
|
|
@ -130,6 +132,7 @@ class PatternMatching(object):
|
||||||
return foundallEdges
|
return foundallEdges
|
||||||
|
|
||||||
def visitVertex(pattern_vertices, p_vertex, g_vertex, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
|
def visitVertex(pattern_vertices, p_vertex, g_vertex, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
|
||||||
|
# print('visitVertex')
|
||||||
"""
|
"""
|
||||||
Visit a pattern vertex, and try to bind it to the graph vertex
|
Visit a pattern vertex, and try to bind it to the graph vertex
|
||||||
(both are given as argument). A binding is successful if all the
|
(both are given as argument). A binding is successful if all the
|
||||||
|
|
@ -153,6 +156,7 @@ class PatternMatching(object):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def visitVertices(pattern_vertices, p_vertex, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
|
def visitVertices(pattern_vertices, p_vertex, visited_p_vertices, visited_p_edges, visited_g_vertices, visited_g_edges, vertices, edges):
|
||||||
|
# print('visitVertices')
|
||||||
"""
|
"""
|
||||||
Visit a pattern vertex and try to bind a graph vertex to it.
|
Visit a pattern vertex and try to bind a graph vertex to it.
|
||||||
"""
|
"""
|
||||||
|
|
@ -201,9 +205,9 @@ class PatternMatching(object):
|
||||||
"""
|
"""
|
||||||
Return adjacency matrix and the order of the vertices.
|
Return adjacency matrix and the order of the vertices.
|
||||||
"""
|
"""
|
||||||
print('createAdjacencyMatrixMap...')
|
# print('createAdjacencyMatrixMap...')
|
||||||
print('graph:', graph)
|
# print('graph:', graph)
|
||||||
print('pattern:', pattern)
|
# print('pattern:', pattern)
|
||||||
|
|
||||||
matrix = collections.OrderedDict() # { vertex, (index, [has edge from index to pos?]) }
|
matrix = collections.OrderedDict() # { vertex, (index, [has edge from index to pos?]) }
|
||||||
|
|
||||||
|
|
@ -253,9 +257,9 @@ class PatternMatching(object):
|
||||||
return AM, vertices_order
|
return AM, vertices_order
|
||||||
|
|
||||||
def matchVF2(self, pattern, graph):
|
def matchVF2(self, pattern, graph):
|
||||||
print('matchVF2...')
|
# print('matchVF2...')
|
||||||
print('pattern:', pattern)
|
# print('pattern:', pattern)
|
||||||
print('graph:', graph)
|
# print('graph:', graph)
|
||||||
|
|
||||||
class VF2_Obj(object):
|
class VF2_Obj(object):
|
||||||
"""
|
"""
|
||||||
|
|
@ -263,23 +267,24 @@ class PatternMatching(object):
|
||||||
"""
|
"""
|
||||||
def __init__(self, len_graph_vertices, len_pattern_vertices):
|
def __init__(self, len_graph_vertices, len_pattern_vertices):
|
||||||
# represents if n-the element (h[n] or p[n]) matched
|
# represents if n-the element (h[n] or p[n]) matched
|
||||||
self.core_graph = [False]*len_graph_vertices
|
self.host_vtx_is_matched = [False]*len_graph_vertices
|
||||||
self.core_pattern = [False]*len_pattern_vertices
|
self.pattern_vtx_is_matched = [False]*len_pattern_vertices
|
||||||
|
|
||||||
# save mapping from pattern to graph
|
# save mapping from pattern to graph
|
||||||
self.mapping = {}
|
self.mapping = {}
|
||||||
|
self.edge_mapping = {}
|
||||||
|
|
||||||
# preference lvl 1
|
# preference lvl 1
|
||||||
# ordered set of vertices adjecent to M_graph connected via an outgoing edge
|
# ordered set of vertices adjecent to M_graph connected via an outgoing edge
|
||||||
self.N_out_graph = [-1]*len_graph_vertices
|
self.N_out_graph = [-1]*len_graph_vertices
|
||||||
# ordered set of vertices adjecent to M_pattern connected via an outgoing edge
|
# ordered set of vertices adjecent to M_pattern connected via an outgoing edge
|
||||||
self.N_out_pattern = [-1]*len_pattern_vertices
|
self.N_out_pattern = [-1]*len_pattern_vertices
|
||||||
|
|
||||||
# preference lvl 2
|
# preference lvl 2
|
||||||
# ordered set of vertices adjecent to M_graph connected via an incoming edge
|
# ordered set of vertices adjecent to M_graph connected via an incoming edge
|
||||||
self.N_inc_graph = [-1]*len_graph_vertices
|
self.N_inc_graph = [-1]*len_graph_vertices
|
||||||
# ordered set of vertices adjecent to M_pattern connected via an incoming edge
|
# ordered set of vertices adjecent to M_pattern connected via an incoming edge
|
||||||
self.N_inc_pattern = [-1]*len_pattern_vertices
|
self.N_inc_pattern = [-1]*len_pattern_vertices
|
||||||
|
|
||||||
# preference lvl 3
|
# preference lvl 3
|
||||||
# not in the above
|
# not in the above
|
||||||
|
|
@ -361,51 +366,53 @@ class PatternMatching(object):
|
||||||
|
|
||||||
# add all neihgbours of pattern vertex m
|
# add all neihgbours of pattern vertex m
|
||||||
for i in range(0, len(P)): # P is a nxn-matrix
|
for i in range(0, len(P)): # P is a nxn-matrix
|
||||||
if (P[m][i] or P[i][m]) and VF2_obj.core_pattern[i]:
|
if (P[m][i] or P[i][m]) and VF2_obj.pattern_vtx_is_matched[i]:
|
||||||
neighbours_pattern.setdefault(p[i].type, set()).add(p[i])
|
neighbours_pattern.setdefault(p[i].type, set()).add(p[i])
|
||||||
|
|
||||||
# add all neihgbours of graph vertex n
|
# add all neihgbours of graph vertex n
|
||||||
for i in range(0, len(H)): # P is a nxn-matrix
|
for i in range(0, len(H)): # P is a nxn-matrix
|
||||||
if (H[n][i] or H[i][n]) and VF2_obj.core_graph[i]:
|
if (H[n][i] or H[i][n]) and VF2_obj.host_vtx_is_matched[i]:
|
||||||
neighbours_graph.setdefault(h[i].type, set()).add(h[i])
|
neighbours_graph.setdefault(h[i].type, set()).add(h[i])
|
||||||
|
|
||||||
# take a coding shortcut,
|
# take a coding shortcut,
|
||||||
# use self.matchNaive function to see if it is feasable.
|
# use self.matchNaive function to see if it is feasable.
|
||||||
# this way, we immidiatly test the semantic attributes
|
# this way, we immidiatly test the semantic attributes
|
||||||
if not self.matchNaive(pattern, pattern_vertices=neighbours_pattern, vertices=neighbours_graph, edges=graph.edges):
|
# print('pattern.vertices', pattern.vertices)
|
||||||
|
matched = self.matchNaive(pattern, pattern_vertices=neighbours_pattern, vertices=neighbours_graph, edges=graph.edges)
|
||||||
|
if matched == None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# count ext_edges from core_graph to a adjecent vertices and
|
# count ext_edges from host_vtx_is_matched to a adjecent vertices and
|
||||||
# cuotn ext_edges for adjecent vertices and not matched vertices
|
# cuotn ext_edges for adjecent vertices and not matched vertices
|
||||||
# connected via the ext_edges
|
# connected via the ext_edges
|
||||||
ext_edges_graph_ca = 0
|
ext_edges_graph_ca = 0
|
||||||
ext_edges_graph_an = 0
|
ext_edges_graph_an = 0
|
||||||
# for all core vertices
|
# for all core vertices
|
||||||
for x in range(0, len(VF2_obj.core_graph)):
|
for x in range(0, len(VF2_obj.host_vtx_is_matched)):
|
||||||
# for all its neighbours
|
# for all its neighbours
|
||||||
for y in range(0, len(H)):
|
for y in range(0, len(H)):
|
||||||
if H[x][y]:
|
if H[x][y]:
|
||||||
# if it is a neighbor and not yet matched
|
# if it is a neighbor and not yet matched
|
||||||
if (VF2_obj.N_out_graph[y] != -1 or VF2_obj.N_inc_graph[y] != -1) and VF2_obj.core_graph[y]:
|
if (VF2_obj.N_out_graph[y] != -1 or VF2_obj.N_inc_graph[y] != -1) and VF2_obj.host_vtx_is_matched[y]:
|
||||||
# if we matched it
|
# if we matched it
|
||||||
if VF2_obj.core_graph[x] != -1:
|
if VF2_obj.host_vtx_is_matched[x] != -1:
|
||||||
ext_edges_graph_ca += 1
|
ext_edges_graph_ca += 1
|
||||||
else:
|
else:
|
||||||
ext_edges_graph_an += 1
|
ext_edges_graph_an += 1
|
||||||
|
|
||||||
# count ext_edges from core_pattern to a adjecent vertices
|
# count ext_edges from pattern_vtx_is_matched to a adjecent vertices
|
||||||
# connected via the ext_edges
|
# connected via the ext_edges
|
||||||
ext_edges_pattern_ca = 0
|
ext_edges_pattern_ca = 0
|
||||||
ext_edges_pattern_an = 0
|
ext_edges_pattern_an = 0
|
||||||
# for all core vertices
|
# for all core vertices
|
||||||
for x in range(0, len(VF2_obj.core_pattern)):
|
for x in range(0, len(VF2_obj.pattern_vtx_is_matched)):
|
||||||
# for all its neighbours
|
# for all its neighbours
|
||||||
for y in range(0, len(P)):
|
for y in range(0, len(P)):
|
||||||
if P[x][y]:
|
if P[x][y]:
|
||||||
# if it is a neighbor and not yet matched
|
# if it is a neighbor and not yet matched
|
||||||
if (VF2_obj.N_out_pattern[y] != -1 or VF2_obj.N_inc_pattern[y] != -1) and VF2_obj.core_pattern[y]:
|
if (VF2_obj.N_out_pattern[y] != -1 or VF2_obj.N_inc_pattern[y] != -1) and VF2_obj.pattern_vtx_is_matched[y]:
|
||||||
# if we matched it
|
# if we matched it
|
||||||
if VF2_obj.core_pattern[x] != -1:
|
if VF2_obj.pattern_vtx_is_matched[x] != -1:
|
||||||
ext_edges_pattern_ca += 1
|
ext_edges_pattern_ca += 1
|
||||||
else:
|
else:
|
||||||
ext_edges_pattern_an += 1
|
ext_edges_pattern_an += 1
|
||||||
|
|
@ -425,9 +432,9 @@ class PatternMatching(object):
|
||||||
if ext_edges_pattern_an > ext_edges_graph_an:
|
if ext_edges_pattern_an > ext_edges_graph_an:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return matched
|
||||||
|
|
||||||
def matchPhase(H, P, h, p, index_M, VF2_obj, n, m):
|
def matchPhase(index_M, VF2_obj, n, m):
|
||||||
"""
|
"""
|
||||||
The matching fase of the VF2 algorithm. If the chosen n, m pair
|
The matching fase of the VF2 algorithm. If the chosen n, m pair
|
||||||
passes the feasibilityTest, the pair gets added and we start
|
passes the feasibilityTest, the pair gets added and we start
|
||||||
|
|
@ -448,12 +455,15 @@ class PatternMatching(object):
|
||||||
return False # already visited this (partial) match -> skip
|
return False # already visited this (partial) match -> skip
|
||||||
|
|
||||||
|
|
||||||
if feasibilityTest(H, P, h, p, VF2_obj, n, m):
|
matched = feasibilityTest(H, P, h, p, VF2_obj, n, m)
|
||||||
print(self.indent*" ","adding to match:", n, "->", m)
|
|
||||||
|
if matched != False:
|
||||||
|
# print(self.indent*" ","adding to match:", n, "->", m)
|
||||||
# adapt VF2_obj
|
# adapt VF2_obj
|
||||||
VF2_obj.core_graph[n] = True
|
VF2_obj.host_vtx_is_matched[n] = True
|
||||||
VF2_obj.core_pattern[m] = True
|
VF2_obj.pattern_vtx_is_matched[m] = True
|
||||||
VF2_obj.mapping[h[n]] = p[m]
|
VF2_obj.mapping[h[n]] = p[m]
|
||||||
|
# VF2_obj.edge_mapping
|
||||||
addOutNeighbours(H[n], VF2_obj.N_out_graph, index_M)
|
addOutNeighbours(H[n], VF2_obj.N_out_graph, index_M)
|
||||||
addIncNeighbours(H, n, VF2_obj.N_inc_graph, index_M)
|
addIncNeighbours(H, n, VF2_obj.N_inc_graph, index_M)
|
||||||
addOutNeighbours(P[m], VF2_obj.N_out_pattern, index_M)
|
addOutNeighbours(P[m], VF2_obj.N_out_pattern, index_M)
|
||||||
|
|
@ -475,11 +485,11 @@ class PatternMatching(object):
|
||||||
|
|
||||||
if True:
|
if True:
|
||||||
# else:
|
# else:
|
||||||
print(self.indent*" ","backtracking... remove", n, "->", m)
|
# print(self.indent*" ","backtracking... remove", n, "->", m)
|
||||||
|
|
||||||
# else, backtrack, adapt VF2_obj
|
# else, backtrack, adapt VF2_obj
|
||||||
VF2_obj.core_graph[n] = False
|
VF2_obj.host_vtx_is_matched[n] = False
|
||||||
VF2_obj.core_pattern[m] = False
|
VF2_obj.pattern_vtx_is_matched[m] = False
|
||||||
del VF2_obj.mapping[h[n]]
|
del VF2_obj.mapping[h[n]]
|
||||||
delNeighbours(VF2_obj.N_out_graph, index_M)
|
delNeighbours(VF2_obj.N_out_graph, index_M)
|
||||||
delNeighbours(VF2_obj.N_inc_graph, index_M)
|
delNeighbours(VF2_obj.N_inc_graph, index_M)
|
||||||
|
|
@ -488,7 +498,7 @@ class PatternMatching(object):
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def preferred(H, P, h, p, index_M, VF2_obj, N_graph, N_pattern):
|
def preferred(index_M, VF2_obj, N_graph, N_pattern):
|
||||||
"""
|
"""
|
||||||
Try to match the adjacency vertices connected via outgoing
|
Try to match the adjacency vertices connected via outgoing
|
||||||
or incoming edges. (Depending on what is given for N_graph and
|
or incoming edges. (Depending on what is given for N_graph and
|
||||||
|
|
@ -497,23 +507,23 @@ class PatternMatching(object):
|
||||||
for n in range(0, len(N_graph)):
|
for n in range(0, len(N_graph)):
|
||||||
# skip graph vertices that are not in VF2_obj.N_out_graph
|
# skip graph vertices that are not in VF2_obj.N_out_graph
|
||||||
# (or already matched)
|
# (or already matched)
|
||||||
if N_graph[n] == -1 or VF2_obj.core_graph[n]:
|
if N_graph[n] == -1 or VF2_obj.host_vtx_is_matched[n]:
|
||||||
# print(self.indent*" "," skipping")
|
# print(self.indent*" "," skipping")
|
||||||
continue
|
continue
|
||||||
print(self.indent*" "," n:", n)
|
# print(self.indent*" "," n:", n)
|
||||||
for m in range(0, len(N_pattern)):
|
for m in range(0, len(N_pattern)):
|
||||||
# skip graph vertices that are not in VF2_obj.N_out_pattern
|
# skip graph vertices that are not in VF2_obj.N_out_pattern
|
||||||
# (or already matched)
|
# (or already matched)
|
||||||
if N_pattern[m] == -1 or VF2_obj.core_pattern[m]:
|
if N_pattern[m] == -1 or VF2_obj.pattern_vtx_is_matched[m]:
|
||||||
continue
|
continue
|
||||||
print(self.indent*" "," m:", m)
|
# print(self.indent*" "," m:", m)
|
||||||
matched = yield from matchPhase(H, P, h, p, index_M, VF2_obj, n, m)
|
matched = yield from matchPhase(index_M, VF2_obj, n, m)
|
||||||
if matched:
|
if matched:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def leastPreferred(H, P, h, p, index_M, VF2_obj):
|
def leastPreferred(index_M, VF2_obj):
|
||||||
"""
|
"""
|
||||||
Try to match the vertices that are not connected to the curretly
|
Try to match the vertices that are not connected to the curretly
|
||||||
matched vertices.
|
matched vertices.
|
||||||
|
|
@ -521,28 +531,28 @@ class PatternMatching(object):
|
||||||
for n in range(0, len(VF2_obj.N_out_graph)):
|
for n in range(0, len(VF2_obj.N_out_graph)):
|
||||||
# skip vertices that are connected to the graph
|
# skip vertices that are connected to the graph
|
||||||
# (or already matched)
|
# (or already matched)
|
||||||
if not (VF2_obj.N_out_graph[n] == -1 and VF2_obj.N_inc_graph[n] == -1) or VF2_obj.core_graph[n]:
|
if not (VF2_obj.N_out_graph[n] == -1 and VF2_obj.N_inc_graph[n] == -1) or VF2_obj.host_vtx_is_matched[n]:
|
||||||
# print(self.indent*" "," skipping")
|
# print(self.indent*" "," skipping")
|
||||||
continue
|
continue
|
||||||
print(" n:", n)
|
# print(" n:", n)
|
||||||
for m in range(0, len(VF2_obj.N_out_pattern)):
|
for m in range(0, len(VF2_obj.N_out_pattern)):
|
||||||
# skip vertices that are connected to the graph
|
# skip vertices that are connected to the graph
|
||||||
# (or already matched)
|
# (or already matched)
|
||||||
if not (VF2_obj.N_out_pattern[m] == -1 and VF2_obj.N_inc_pattern[m] == -1) or VF2_obj.core_pattern[m]:
|
if not (VF2_obj.N_out_pattern[m] == -1 and VF2_obj.N_inc_pattern[m] == -1) or VF2_obj.pattern_vtx_is_matched[m]:
|
||||||
# print(self.indent*" "," skipping")
|
# print(self.indent*" "," skipping")
|
||||||
continue
|
continue
|
||||||
print(self.indent*" "," m:", m)
|
# print(self.indent*" "," m:", m)
|
||||||
matched = yield from matchPhase(H, P, h, p, index_M, VF2_obj, n, m)
|
matched = yield from matchPhase(index_M, VF2_obj, n, m)
|
||||||
if matched:
|
if matched:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
print(self.indent*" ","index_M:", index_M)
|
# print(self.indent*" ","index_M:", index_M)
|
||||||
|
|
||||||
# We are at the end, we found an candidate.
|
# We are at the end, we found an candidate.
|
||||||
if index_M == len(p):
|
if index_M == len(p):
|
||||||
print(self.indent*" ","end...")
|
# print(self.indent*" ","end...")
|
||||||
bound_graph_vertices = {}
|
bound_graph_vertices = {}
|
||||||
for vertex_bound, _ in VF2_obj.mapping.items():
|
for vertex_bound, _ in VF2_obj.mapping.items():
|
||||||
bound_graph_vertices.setdefault(vertex_bound.type, set()).add(vertex_bound)
|
bound_graph_vertices.setdefault(vertex_bound.type, set()).add(vertex_bound)
|
||||||
|
|
@ -555,28 +565,28 @@ class PatternMatching(object):
|
||||||
if index_M > 0:
|
if index_M > 0:
|
||||||
# try the candidates is the preffered order
|
# try the candidates is the preffered order
|
||||||
# first try the adjacent vertices connected via the outgoing edges.
|
# first try the adjacent vertices connected via the outgoing edges.
|
||||||
print(self.indent*" ","preferred L1")
|
# print(self.indent*" ","preferred L1")
|
||||||
matched = yield from preferred(H, P, h, p, index_M, VF2_obj, VF2_obj.N_out_graph, VF2_obj.N_out_pattern)
|
matched = yield from preferred(index_M, VF2_obj, VF2_obj.N_out_graph, VF2_obj.N_out_pattern)
|
||||||
if matched:
|
if matched:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
print(self.indent*" ","preferred L2")
|
# print(self.indent*" ","preferred L2")
|
||||||
# then try the adjacent vertices connected via the incoming edges.
|
# then try the adjacent vertices connected via the incoming edges.
|
||||||
matched = yield from preferred(H, P, h, p, index_M, VF2_obj, VF2_obj.N_inc_graph, VF2_obj.N_inc_pattern)
|
matched = yield from preferred(index_M, VF2_obj, VF2_obj.N_inc_graph, VF2_obj.N_inc_pattern)
|
||||||
if matched:
|
if matched:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
print(self.indent*" ","leastPreferred")
|
# print(self.indent*" ","leastPreferred")
|
||||||
# and lastly, try the vertices not connected to the currently matched vertices
|
# and lastly, try the vertices not connected to the currently matched vertices
|
||||||
matched = yield from leastPreferred(H, P, h, p, index_M, VF2_obj)
|
matched = yield from leastPreferred(index_M, VF2_obj)
|
||||||
if matched:
|
if matched:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# create adjecency matrix of the graph
|
# create adjacency matrix of the graph
|
||||||
H, h = self.createAdjacencyMatrixMap(graph, pattern)
|
H, h = self.createAdjacencyMatrixMap(graph, pattern)
|
||||||
# create adjecency matrix of the pattern
|
# create adjacency matrix of the pattern
|
||||||
P, p = self.createAdjacencyMatrixMap(pattern, pattern)
|
P, p = self.createAdjacencyMatrixMap(pattern, pattern)
|
||||||
|
|
||||||
VF2_obj = VF2_Obj(len(h), len(p))
|
VF2_obj = VF2_Obj(len(h), len(p))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue