matcher.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. '''*****************************************************************************
  2. AToMPM - A Tool for Multi-Paradigm Modelling
  3. Copyright (c) 2011 Eugene Syriani
  4. This file is part of AToMPM.
  5. AToMPM is free software: you can redistribute it and/or modify it under the
  6. terms of the GNU Lesser General Public License as published by the Free Software
  7. Foundation, either version 3 of the License, or (at your option) any later
  8. version.
  9. AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  11. PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with AToMPM. If not, see <http://www.gnu.org/licenses/>.
  14. *****************************************************************************'''
  15. from copy import deepcopy
  16. from ..util.infinity import INFINITY
  17. from ..core.match_algo import HimesisMatcher
  18. from ..core.himesis import HConstants as HC
  19. from rule_primitive import RulePrimitive
  20. from messages import MatchSet, Match, TransformationException
  21. class Matcher(RulePrimitive):
  22. '''
  23. Binds the source graph according to the pre-condition pattern.
  24. '''
  25. def __init__(self, condition, max=INFINITY):
  26. '''
  27. Binds the source graph according to the pre-condition pattern.
  28. @param condition: The pre-condition pattern.
  29. @param max: The maximum number of matches.
  30. '''
  31. super(Matcher, self).__init__()
  32. self.max = max
  33. self.condition = condition
  34. def __str__(self):
  35. s = super(Matcher, self).__str__()
  36. s = s.split(' ')
  37. s.insert(1, '[%s]' % self.condition.name)
  38. return reduce(lambda x, y: '%s %s' % (x,y), s)
  39. def packet_in(self, packet):
  40. self.exception = None
  41. self.is_success = False
  42. if self.condition[HC.GUID] in packet.match_sets:
  43. matchSet = packet.match_sets[self.condition[HC.GUID]]
  44. else:
  45. matchSet = MatchSet()
  46. # Find the matches
  47. try:
  48. i = 1
  49. if i <= self.max:
  50. for mapping in self._match(packet.graph, packet.global_pivots):
  51. # Convert the mapping to a Match object
  52. match = Match()
  53. match.from_mapping(mapping, packet.graph, self.condition)
  54. matchSet.matches.append(match)
  55. i += 1
  56. if i > self.max:
  57. # We don't need any more matches
  58. break
  59. except Exception, e:
  60. self.is_success = False
  61. self.exception = TransformationException(e)
  62. self.exception.packet = packet
  63. self.exception.transformation_unit = self
  64. return packet
  65. # Don't forget to add the match set to the packet, even if no matches were found
  66. if len(matchSet.matches) > 0:
  67. packet.match_sets[self.condition[HC.GUID]] = matchSet
  68. # Identify that this is the condition we are currently processing
  69. packet.current = self.condition[HC.GUID]
  70. # Success only if matches were found
  71. self.is_success = len(matchSet.matches) > 0
  72. return packet
  73. def _match(self, graph, pivots) :
  74. '''
  75. Matcher with pivots and (possibly) multiple NACs
  76. 1. Verify that no unbound NAC has a match
  77. 2. Let the "bridge" denote the biggest graph that is the intersection of the LHS and a NAC, among all NACs
  78. 3. Match the common part between the LHS & the NAC, i.e., the "bridge"
  79. 3.1 Continue the matching ensuring no occurrence of the NAC
  80. 3.2. If a NAC is found, ignore the current bridge mapping
  81. 3.3. Continue to find complete matches of the LHS,
  82. given each partial match found in 3.1.
  83. 3.4. For each valid match, verify that no occurrence of any remaining bound NAC is found,
  84. given the mapping found in 3.3.
  85. '''
  86. pred1 = {} # To optimize the matcher, since otherwise matcher will compute the predecessors of the source graph many times
  87. succ1 = {} # To optimize the matcher, since otherwise matcher will compute the successors of the source graph many times
  88. # Cache the pivot nodes of the source graph
  89. pivots = deepcopy(pivots)
  90. pivots.to_source_node_indices(graph)
  91. #===================================================================
  92. # First process the NACs that are not bound to the LHS
  93. #===================================================================
  94. for NAC in self.condition.getUnboundNACs():
  95. # Look for a NAC match
  96. nacMatcher = HimesisMatcher(source_graph=graph, pattern_graph=NAC)
  97. # Convert the pivots
  98. nac_pivots = pivots.to_mapping(graph, NAC)
  99. try:
  100. for mapping in nacMatcher.match_iter(context=nac_pivots):
  101. # Make the mapping into {...,NAClabel:graphIndex,...}
  102. match = Match()
  103. match.from_mapping(mapping, graph, self.condition)
  104. if NAC[HC.MT_CONSTRAINT](match.to_label_mapping(graph), graph):
  105. # An unbound NAC has been found: this pattern can never match
  106. return
  107. except: raise
  108. finally: nacMatcher.reset_recursion_limit()
  109. # For further matching optimizations
  110. pred1 = nacMatcher.pred1
  111. succ1 = nacMatcher.succ1
  112. # Either there are no NACs, or there were only unbound NACs that do not match, so match the LHS now
  113. if not self.condition.hasBoundNACs():
  114. lhsMatcher = HimesisMatcher(source_graph=graph, pattern_graph=self.condition, pred1=pred1, succ1=succ1)
  115. # Convert the pivots
  116. lhs_pivots = pivots.to_mapping(graph, self.condition)
  117. try:
  118. for mapping in lhsMatcher.match_iter(context=lhs_pivots):
  119. # Make the mapping into {...,LHSlabel:graphIndex,...}
  120. match = Match()
  121. match.from_mapping(mapping, graph, self.condition)
  122. if self.condition[HC.MT_CONSTRAINT](match.to_label_mapping(graph), graph):
  123. yield mapping
  124. except: raise
  125. finally: lhsMatcher.reset_recursion_limit()
  126. # The matching is complete
  127. return
  128. #===================================================================
  129. # Now process the NACs that have some nodes bound to the LHS
  130. #===================================================================
  131. # Continue the matching looking for the LHS now
  132. lhsMatcher = HimesisMatcher(source_graph=graph, pattern_graph=self.condition, pred1=pred1, succ1=succ1)
  133. # Augment the bridge mapping with the pivot mappings
  134. lhs_pivots = pivots.to_mapping(graph, self.condition)
  135. try:
  136. for mapping in lhsMatcher.match_iter(context=lhs_pivots):
  137. # Make the mapping into {...,LHSlabel:graphIndex,...}
  138. match = Match()
  139. match.from_mapping(mapping, graph, self.condition)
  140. if self.condition[HC.MT_CONSTRAINT](match.to_label_mapping(graph), graph):
  141. # A match of the LHS is found: ensure that no remaining NAC do match
  142. invalid = False
  143. for NAC in self.condition.getBoundNACs():
  144. # This mapping represents the mapping of the bridge of this NAC with the LHS
  145. bridgeMapping = match.to_mapping(graph, NAC)
  146. # Now continue the matching looking for a match of the corresponding NAC
  147. nacMatcher = HimesisMatcher(source_graph=graph, pattern_graph=NAC, pred1=pred1, succ1=succ1)
  148. for nac_mapping in nacMatcher.match_iter(context=bridgeMapping):
  149. # Make the mapping into {...,NAClabel:graphIndex,...}
  150. match = Match()
  151. match.from_mapping(nac_mapping, graph, NAC)
  152. if NAC[HC.MT_CONSTRAINT](match.to_label_mapping(graph), graph):
  153. # An occurrence of the NAC is found: current mapping is not valid
  154. invalid = True
  155. break
  156. if invalid:
  157. # An occurrence of the NAC was found: current mapping is not valid
  158. break
  159. else:
  160. # Either there are no bound NACs or no occurrence of any bound NAC was found: current mapping is valid
  161. yield mapping
  162. except: raise
  163. finally: lhsMatcher.reset_recursion_limit()