searchGraph.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. # coding: utf-8
  2. """
  3. Author: Sten Vercamman
  4. Univeristy of Antwerp
  5. Example code for paper: Efficient model transformations for novices
  6. url: http://msdl.cs.mcgill.ca/people/hv/teaching/MSBDesign/projects/Sten.Vercammen
  7. The main goal of this code is to give an overview, and an understandable
  8. implementation, of known techniques for pattern matching and solving the
  9. sub-graph homomorphism problem. The presented techniques do not include
  10. performance adaptations/optimizations. It is not optimized to be efficient
  11. but rather for the ease of understanding the workings of the algorithms.
  12. The paper does list some possible extensions/optimizations.
  13. It is intended as a guideline, even for novices, and provides an in-depth look
  14. at the workings behind various techniques for efficient pattern matching.
  15. """
  16. from graph import *
  17. import math
  18. class SearchGraph(Graph):
  19. """
  20. A SearchGraph is an extended Graph, it keeps traks of statistics
  21. for creating the cost model when generating a search plan.
  22. It stire the amount of edges for each edge.type per vertex.type.
  23. """
  24. def __init__(self, orig=None, deepCopy=False):
  25. Graph.__init__(self)
  26. # member variables:
  27. self.nr_of_inc_edges = {} # {vertex_type, {edge_type, nr of incoming edges of edge_type for vertex_type } }
  28. self.nr_of_out_edges = {} # {vertex_type, {edge_type, nr of outgoing edges of edge_type for vertex_type } }
  29. if orig != None:
  30. if not (isinstance(orig, Graph) or isinstance(orig, SearchGraph)):
  31. raise TypeError('Can only create SearchGraph from Graph and SearchGraph types')
  32. if not deepCopy:
  33. # copy all memeber elements:
  34. self.vertices = orig.vertices # this is a reference
  35. self.edges = orig.edges # this is a reference
  36. # udpate the edge counters for each edge
  37. for _, edges in self.edges.items():
  38. for edge in edges:
  39. self.addToEdgeCounters(edge)
  40. else: # TODO: deepcopy (not really needed)
  41. pass
  42. def addCreateEdge(self, src, tgt, str_type):
  43. """
  44. Creates edge of str_type from src to tgt, and returns it,
  45. so that properties can be added to the edge.
  46. This also add the Edge to the Edge counters
  47. """
  48. # call parent fucntion, this function is an extention
  49. edge = Graph.addCreateEdge(self, src, tgt, str_type)
  50. self.updateEdgeCounters(edge)
  51. return edge
  52. def addToEdgeCounters(self, edge):
  53. """
  54. Add the Edge to the Edge counters.
  55. """
  56. # get {edge.type, counter} for tgt vertex of edge (or create it)
  57. edge_counters = self.nr_of_inc_edges.setdefault(edge.tgt.type, {})
  58. # increase counter of edge.type by 1
  59. edge_counters[edge.type] = edge_counters.get(edge.type, 0) + 1
  60. # get {edge.type, counter} for src vertex of edge (or create it)
  61. edge_counters = self.nr_of_out_edges.setdefault(edge.src.type, {})
  62. # increase counter of edge.type by 1
  63. edge_counters[edge.type] = edge_counters.get(edge.type, 0) + 1
  64. def getCostLkp(self, type, is_vertex):
  65. """
  66. Returns the cost of a lkp primitive operation (of a vertex or edge).
  67. Returns None if vertex type or edge type not present in Host Graph
  68. """
  69. if is_vertex:
  70. cost = len(self.getVerticesOfType(type))
  71. else:
  72. cost = len(self.getEdgesOfType(type))
  73. if cost == 0:
  74. return None
  75. # we use a logaritmic cost
  76. return math.log(cost)
  77. def getCostInc(self, vertex_type, edge_type):
  78. """
  79. Returns the cost of an in primitive operation.
  80. Returns None if vertex_type or edge_type not present in Host Graph
  81. """
  82. cost = float(self.nr_of_inc_edges.get(vertex_type, {}).get(edge_type))
  83. if cost != None:
  84. nr_of_vertices_with_type = len(self.getVerticesOfType(vertex_type))
  85. if nr_of_vertices_with_type != 0:
  86. cost /= len(self.getVerticesOfType(vertex_type))
  87. # we use a logaritmic cost
  88. cost = math.log(cost)
  89. return cost
  90. def getCostOut(self, vertex_type, edge_type):
  91. """
  92. Returns the cost of an out primitive operation.
  93. Returns None if vertex_type or edge_type not present in Host Graph
  94. """
  95. cost = float(self.nr_of_out_edges.get(vertex_type, {}).get(edge_type))
  96. if cost != None:
  97. nr_of_vertices_with_type = len(self.getVerticesOfType(vertex_type))
  98. if nr_of_vertices_with_type != 0:
  99. cost /= len(self.getVerticesOfType(vertex_type))
  100. # we use a logaritmic cost
  101. cost = math.log(cost)
  102. return cost