benchmark.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. import time
  2. import matcher as j # joeri's matcher
  3. import graph as sgraph # sten's graph
  4. import patternMatching as s # sten's matcher
  5. import generator
  6. def j_to_s(j):
  7. s = sgraph.Graph()
  8. m = {}
  9. for jv in j.vtxs:
  10. sv = s.addCreateVertex(jv.value) # value becomes type
  11. m[jv] = sv
  12. for je in j.edges:
  13. s.addCreateEdge(m[je.src], m[je.tgt], "e") # only one type
  14. return s
  15. def s_to_j(s):
  16. jg = j.Graph()
  17. jg.vtxs = [ j.Vertex(typ) for (typ,svs) in s.vertices.items() for sv in svs ]
  18. m = { sv : jg.vtxs[i] for svs in s.vertices.values() for i,sv in enumerate(svs) }
  19. jg.edges = [j.Edge(m[se.src], m[se.tgt]) for ses in s.edges.values() for se in ses ]
  20. return j
  21. def run_benchmark(jhost, jguest, shost, sguest, expected=None):
  22. j_durations = 0
  23. s_durations = 0
  24. # benchmark Joeri
  25. m = j.MatcherVF2(host, guest,
  26. lambda g_vtx, h_vtx: g_vtx.value == h_vtx.value) # all vertices can be matched
  27. iterations = 50
  28. print(" Patience (joeri)...")
  29. for n in range(iterations):
  30. time_start = time.perf_counter_ns()
  31. matches = [mm for mm in m.match()]
  32. time_end = time.perf_counter_ns()
  33. duration = time_end - time_start
  34. j_durations += duration
  35. print(f' {iterations} iterations, took {j_durations/1000000:.3f} ms, {j_durations/iterations/1000000:.3f} ms per iteration')
  36. if expected == None:
  37. print(f" {len(matches)} matches")
  38. else:
  39. if len(matches) == expected:
  40. print(" correct (probably)")
  41. else:
  42. print(f" WRONG! expected: {expected}, got: {len(matches)}")
  43. # print([m.mapping_vtxs for m in matches])
  44. # print([m.mapping_edges for m in matches])
  45. # benchmark Sten
  46. m = s.PatternMatching()
  47. print(" Patience (sten)...")
  48. for n in range(iterations):
  49. time_start = time.perf_counter_ns()
  50. matches = [mm for mm in m.matchVF2(sguest, shost)]
  51. time_end = time.perf_counter_ns()
  52. duration = time_end - time_start
  53. s_durations += duration
  54. print(f' {iterations} iterations, took {s_durations/1000000:.3f} ms, {s_durations/iterations/1000000:.3f} ms per iteration')
  55. if expected == None:
  56. print(f" {len(matches)} matches")
  57. else:
  58. if len(matches) == expected:
  59. print(" correct (probably)")
  60. else:
  61. print(f" WRONG! expected: {expected}, got: {len(matches)}")
  62. # print(matches)
  63. print(f" joeri is {s_durations/j_durations:.2f} times faster")
  64. if __name__ == "__main__":
  65. print("\nBENCHMARK: small graph, simple pattern")
  66. host = j.Graph()
  67. host.vtxs = [j.Vertex(0), j.Vertex(0), j.Vertex(0), j.Vertex(0)]
  68. host.edges = [
  69. j.Edge(host.vtxs[0], host.vtxs[1]),
  70. j.Edge(host.vtxs[1], host.vtxs[2]),
  71. j.Edge(host.vtxs[2], host.vtxs[0]),
  72. j.Edge(host.vtxs[2], host.vtxs[3]),
  73. j.Edge(host.vtxs[3], host.vtxs[2]),
  74. ]
  75. guest = j.Graph()
  76. guest.vtxs = [
  77. j.Vertex(0),
  78. j.Vertex(0)]
  79. guest.edges = [
  80. # Look for a simple loop:
  81. j.Edge(guest.vtxs[0], guest.vtxs[1]),
  82. j.Edge(guest.vtxs[1], guest.vtxs[0]),
  83. ]
  84. # because of the symmetry in our pattern, there will be 2 matches
  85. run_benchmark(host, guest, j_to_s(host), j_to_s(guest), expected=2)
  86. #######################################################################
  87. print("\nBENCHMARK: larger graph, simple pattern")
  88. host = j.Graph()
  89. host.vtxs = [
  90. j.Vertex('triangle'), # 0
  91. j.Vertex('square'), # 1
  92. j.Vertex('square'), # 2
  93. j.Vertex('circle'), # 3
  94. j.Vertex('circle'), # 4
  95. j.Vertex('circle'), # 5
  96. ]
  97. host.edges = [
  98. # not a match:
  99. j.Edge(host.vtxs[0], host.vtxs[5]),
  100. j.Edge(host.vtxs[5], host.vtxs[0]),
  101. # will be a match:
  102. j.Edge(host.vtxs[1], host.vtxs[5]),
  103. j.Edge(host.vtxs[5], host.vtxs[1]),
  104. # noise:
  105. j.Edge(host.vtxs[1], host.vtxs[2]),
  106. # will be a match:
  107. j.Edge(host.vtxs[2], host.vtxs[4]),
  108. j.Edge(host.vtxs[4], host.vtxs[2]),
  109. # noise:
  110. j.Edge(host.vtxs[0], host.vtxs[1]),
  111. j.Edge(host.vtxs[0], host.vtxs[3]),
  112. j.Edge(host.vtxs[0], host.vtxs[0]),
  113. j.Edge(host.vtxs[1], host.vtxs[1]),
  114. # will be a match:
  115. j.Edge(host.vtxs[3], host.vtxs[2]),
  116. j.Edge(host.vtxs[2], host.vtxs[3]),
  117. ]
  118. guest = j.Graph()
  119. guest.vtxs = [
  120. j.Vertex('square'), # 0
  121. j.Vertex('circle')] # 1
  122. guest.edges = [
  123. j.Edge(guest.vtxs[0], guest.vtxs[1]),
  124. j.Edge(guest.vtxs[1], guest.vtxs[0]),
  125. ]
  126. # should give 3 matches
  127. run_benchmark(host, guest, j_to_s(host), j_to_s(guest), expected=3)
  128. #######################################################################
  129. print("\nBENCHMARK: same as before, but with larger pattern")
  130. host = j.Graph()
  131. host.vtxs = [
  132. j.Vertex('triangle'), # 0
  133. j.Vertex('square'), # 1
  134. j.Vertex('square'), # 2
  135. j.Vertex('circle'), # 3
  136. j.Vertex('circle'), # 4
  137. j.Vertex('circle'), # 5
  138. ]
  139. host.edges = [
  140. # not a match:
  141. j.Edge(host.vtxs[0], host.vtxs[5]),
  142. j.Edge(host.vtxs[5], host.vtxs[0]),
  143. # will be a match:
  144. j.Edge(host.vtxs[1], host.vtxs[5]),
  145. j.Edge(host.vtxs[5], host.vtxs[1]),
  146. # noise:
  147. j.Edge(host.vtxs[1], host.vtxs[2]),
  148. # will be a match:
  149. j.Edge(host.vtxs[2], host.vtxs[4]),
  150. j.Edge(host.vtxs[4], host.vtxs[2]),
  151. # noise:
  152. j.Edge(host.vtxs[0], host.vtxs[1]),
  153. j.Edge(host.vtxs[0], host.vtxs[3]),
  154. j.Edge(host.vtxs[0], host.vtxs[0]),
  155. j.Edge(host.vtxs[1], host.vtxs[1]),
  156. # will be a match:
  157. j.Edge(host.vtxs[3], host.vtxs[2]),
  158. j.Edge(host.vtxs[2], host.vtxs[3]),
  159. ]
  160. guest = j.Graph()
  161. guest.vtxs = [
  162. j.Vertex('square'), # 0
  163. j.Vertex('circle'), # 1
  164. j.Vertex('square')] # 2
  165. guest.edges = [
  166. j.Edge(guest.vtxs[0], guest.vtxs[1]),
  167. j.Edge(guest.vtxs[1], guest.vtxs[0]),
  168. j.Edge(guest.vtxs[2], guest.vtxs[0]),
  169. ]
  170. # this time, only 2 matches
  171. run_benchmark(host, guest, j_to_s(host), j_to_s(guest), expected=2)
  172. #######################################################################
  173. print("\nBENCHMARK: disconnected pattern")
  174. host = j.Graph()
  175. host.vtxs = [
  176. j.Vertex('triangle'), # 0
  177. j.Vertex('square'), # 1
  178. j.Vertex('square'), # 2
  179. j.Vertex('circle'), # 3
  180. j.Vertex('circle'), # 4
  181. j.Vertex('circle'), # 5
  182. j.Vertex('bear'),
  183. j.Vertex('bear'),
  184. ]
  185. host.edges = [
  186. # not a match:
  187. j.Edge(host.vtxs[0], host.vtxs[5]),
  188. j.Edge(host.vtxs[5], host.vtxs[0]),
  189. # will be a match:
  190. j.Edge(host.vtxs[1], host.vtxs[5]),
  191. j.Edge(host.vtxs[5], host.vtxs[1]),
  192. # noise:
  193. j.Edge(host.vtxs[1], host.vtxs[2]),
  194. # will be a match:
  195. j.Edge(host.vtxs[2], host.vtxs[4]),
  196. j.Edge(host.vtxs[4], host.vtxs[2]),
  197. # noise:
  198. j.Edge(host.vtxs[0], host.vtxs[1]),
  199. j.Edge(host.vtxs[0], host.vtxs[3]),
  200. j.Edge(host.vtxs[0], host.vtxs[0]),
  201. j.Edge(host.vtxs[1], host.vtxs[1]),
  202. # will be a match:
  203. j.Edge(host.vtxs[3], host.vtxs[2]),
  204. j.Edge(host.vtxs[2], host.vtxs[3]),
  205. ]
  206. guest = j.Graph()
  207. guest.vtxs = [
  208. j.Vertex('square'), # 0
  209. j.Vertex('circle'), # 1
  210. j.Vertex('bear')]
  211. guest.edges = [
  212. j.Edge(guest.vtxs[0], guest.vtxs[1]),
  213. j.Edge(guest.vtxs[1], guest.vtxs[0]),
  214. ]
  215. # the 'bear' in our pattern can be matched with any of the two bears in the graph, effectively doubling the number of matches
  216. run_benchmark(host, guest, j_to_s(host), j_to_s(guest), expected=6)
  217. #######################################################################
  218. print("\nBENCHMARK: larger graph")
  219. shost, sguest = generator.get_large_host_and_guest()
  220. run_benchmark(s_to_j(shost), s_to_j(sguest), shost, sguest)
  221. #######################################################################
  222. print("\nBENCHMARK: large random graph")
  223. import random
  224. random.seed(0)
  225. shost, sguest = generator.get_random_host_and_guest(
  226. nr_vtxs = 10,
  227. nr_vtx_types = 0,
  228. nr_edges = 20,
  229. nr_edge_types = 0,
  230. )
  231. run_benchmark(s_to_j(shost), s_to_j(sguest), shost, sguest)