286 lines
8.4 KiB
Python
286 lines
8.4 KiB
Python
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_vtx, h_vtx: g_vtx.value == h_vtx.value) # 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)
|
|
|