|
|
@@ -87,7 +87,7 @@ class HimesisMatcher(object):
|
|
|
|
|
|
# Set recursion limit
|
|
|
self.old_recursion_limit = sys.getrecursionlimit()
|
|
|
- expected_max_recursion_level = self.G2.vcount()
|
|
|
+ expected_max_recursion_level = self.G2.vertex_count()
|
|
|
if self.old_recursion_limit < 1.5 * expected_max_recursion_level:
|
|
|
# Give some breathing room
|
|
|
sys.setrecursionlimit(int(1.5 * expected_max_recursion_level))
|
|
|
@@ -111,8 +111,8 @@ class HimesisMatcher(object):
|
|
|
This speeds up the algorithm significantly.
|
|
|
"""
|
|
|
# Cache individual nodes
|
|
|
- self.G1_nodes = self.G1.node_iter()
|
|
|
- self.G2_nodes = self.G2.node_iter()
|
|
|
+ self.G1_nodes = self.G1.node_guid_iter()
|
|
|
+ self.G2_nodes = self.G2.node_guid_iter()
|
|
|
|
|
|
# # Memoize the predecessor & successor information:
|
|
|
# # for each node store the number of neighbours and the list
|
|
|
@@ -181,16 +181,16 @@ class HimesisMatcher(object):
|
|
|
|
|
|
def are_compatibile(self, src_node, patt_node):
|
|
|
"""
|
|
|
- Verifies if a candidate pair is compatible.
|
|
|
- More specifically, verify degree and meta-model compatibility.
|
|
|
- @param src_node: The candidate from the source graph.
|
|
|
- @param patt_node: The candidate from the pattern graph.
|
|
|
+ Verifies if a candidate pair is compatible.
|
|
|
+ More specifically, verify degree and meta-model compatibility.
|
|
|
+ @param src_node: The candidate from the source graph.
|
|
|
+ @param patt_node: The candidate from the pattern graph.
|
|
|
"""
|
|
|
- sourceNode = self.G1.vs[src_node]
|
|
|
- patternNode = self.G2.vs[patt_node]
|
|
|
+ sourceNode = src_node
|
|
|
+ patternNode = patt_node
|
|
|
|
|
|
# First check if they are of the same type
|
|
|
- if sourceNode[Himesis.Constants.FULLTYPE] == patternNode[Himesis.Constants.FULLTYPE]:
|
|
|
+ if self.G1.node_get_attribute(sourceNode, Himesis.Constants.FULLTYPE) == self.G2.node_get_attribute(patternNode, Himesis.Constants.FULLTYPE):
|
|
|
# Then check for the degree compatibility
|
|
|
return (self.pred2[patt_node][0] <= self.pred1[src_node][0]
|
|
|
and self.succ2[patt_node][0] <= self.succ1[src_node][0])
|
|
|
@@ -200,8 +200,8 @@ class HimesisMatcher(object):
|
|
|
return False
|
|
|
# Then check sub-types compatibility
|
|
|
else:
|
|
|
- return (patternNode[Himesis.Constants.MT_SUBTYPE_MATCH]
|
|
|
- and sourceNode[Himesis.Constants.FULLTYPE] in patternNode[Himesis.Constants.MT_SUBTYPES])
|
|
|
+ return (self.G2.node_get_attribute(patternNode, Himesis.Constants.MT_SUBTYPE_MATCH)
|
|
|
+ and self.G1.node_get_attribute(sourceNode, Himesis.Constants.FULLTYPE) in self.G2.node_get_attribute(patternNode, Himesis.Constants.MT_SUBTYPES))
|
|
|
|
|
|
def candidate_pairs_iter(self):
|
|
|
"""
|
|
|
@@ -399,34 +399,34 @@ class HimesisMatcher(object):
|
|
|
# It verifies that all attribute constraints are satisfied.
|
|
|
#=======================================================================
|
|
|
|
|
|
- src_node = self.G1.vs[src_node]
|
|
|
- patt_node = self.G2.vs[patt_node]
|
|
|
+ # src_node = self.G1.vs[src_node]
|
|
|
+ # patt_node = self.G2.vs[patt_node]
|
|
|
|
|
|
# check the type information
|
|
|
- exact_type_match = src_node[Himesis.Constants.FULLTYPE] == patt_node[Himesis.Constants.FULLTYPE]
|
|
|
- sub_type_match = patt_node[Himesis.Constants.MT_SUBTYPE_MATCH] and \
|
|
|
- src_node[Himesis.Constants.FULLTYPE] in patt_node[Himesis.Constants.MT_SUBTYPES]
|
|
|
+ exact_type_match = self.G1.node_get_attribute(src_node, Himesis.Constants.FULLTYPE) == self.G2.node_get_attribute(patt_node, Himesis.Constants.FULLTYPE)
|
|
|
+ sub_type_match = self.G2.node_get_attribute(patt_node, Himesis.Constants.MT_SUBTYPE_MATCH) and \
|
|
|
+ self.G1.node_get_attribute(src_node, Himesis.Constants.FULLTYPE) in self.G2.node_get_attribute(patt_node, Himesis.Constants.MT_SUBTYPES)
|
|
|
if not (exact_type_match or sub_type_match):
|
|
|
return False
|
|
|
|
|
|
# Check for attributes value/constraint
|
|
|
- for attr in patt_node.attribute_names():
|
|
|
+ for attr in self.G2.node_get_attributes(patt_node):
|
|
|
# Ignore non-RAM attributes
|
|
|
if not Himesis.is_RAM_attribute(attr) :
|
|
|
continue
|
|
|
# If the attribute does not "in theory" exist
|
|
|
- # because igraph actually stores all attribute names in all nodes.
|
|
|
- elif patt_node[attr] == None:
|
|
|
+ # because (for instance) igraph actually stores all attribute names in all nodes.
|
|
|
+ elif self.G2.node_get_attribute(patt_node, attr) is None:
|
|
|
continue
|
|
|
|
|
|
# Node patt_node has not yet been matched to src_node... however,
|
|
|
# patt_node[attr](..) is expecting a mapping of patt_node's mtLabel
|
|
|
# to src_node's index in self.G1... so we build this mapping first
|
|
|
mtLabel2graphIndexMap = {}
|
|
|
- mtLabel2graphIndexMap[ patt_node[Himesis.Constants.MT_LABEL] ] = src_node.index
|
|
|
+ mtLabel2graphIndexMap[ self.G2.node_get_attribute(patt_node, Himesis.Constants.MT_LABEL) ] = src_node
|
|
|
|
|
|
try:
|
|
|
- if not patt_node[attr](mtLabel2graphIndexMap,self.G1):
|
|
|
+ if not patt_node[attr](mtLabel2graphIndexMap, self.G1):
|
|
|
return False
|
|
|
except Exception as e:
|
|
|
#TODO: This should be a TransformationLanguageSpecificException
|
|
|
@@ -446,7 +446,7 @@ class HimesisMatcher(object):
|
|
|
#=======================================================================
|
|
|
|
|
|
# Base condition when a complete match is found
|
|
|
- if len(self.core_2) == self.G2.vcount():
|
|
|
+ if len(self.core_2) == self.G2.vertex_count():
|
|
|
# Save the final mapping, otherwise garbage collection deletes it
|
|
|
self.mapping = self.core_2.copy()
|
|
|
yield self.mapping
|
|
|
@@ -627,30 +627,31 @@ class HimesisMatcherState(object):
|
|
|
del vector[node]
|
|
|
|
|
|
|
|
|
-class VF2(HimesisMatcher):
|
|
|
- """
|
|
|
- The native VF2 algorithm for subgraph isomorphism.
|
|
|
- """
|
|
|
- def __init__(self, G1, G2):
|
|
|
- """
|
|
|
- The native VF2 algorithm for subgraph isomorphism.
|
|
|
- @param G1: The bigger graph.
|
|
|
- @param G2: The smaller graph.
|
|
|
- """
|
|
|
- HimesisMatcher.__init__(self, G1, G2)
|
|
|
-
|
|
|
- def match_iter(self):
|
|
|
- """
|
|
|
- Iterator over mappings of G2 on a subgraph of G1.
|
|
|
- @return: The mapping {pattern node uuid : source node uuid}.
|
|
|
- """
|
|
|
- for mapping in self.G1.get_subisomorphisms_vf2(self.G2):
|
|
|
- # mapping is a list for which mapping[i] is the source node index mapped to the pattern node index i
|
|
|
- # So we need to convert it into a dictionary
|
|
|
- match = {}
|
|
|
- for pattern_node, src_node in enumerate(mapping):
|
|
|
- match[pattern_node] = src_node
|
|
|
- yield match
|
|
|
+# TODO: There is no builtin implementation anymore
|
|
|
+# class VF2(HimesisMatcher):
|
|
|
+# """
|
|
|
+# The native VF2 algorithm for subgraph isomorphism.
|
|
|
+# """
|
|
|
+# def __init__(self, G1, G2):
|
|
|
+# """
|
|
|
+# The native VF2 algorithm for subgraph isomorphism.
|
|
|
+# @param G1: The bigger graph.
|
|
|
+# @param G2: The smaller graph.
|
|
|
+# """
|
|
|
+# HimesisMatcher.__init__(self, G1, G2)
|
|
|
+#
|
|
|
+# def match_iter(self):
|
|
|
+# """
|
|
|
+# Iterator over mappings of G2 on a subgraph of G1.
|
|
|
+# @return: The mapping {pattern node uuid : source node uuid}.
|
|
|
+# """
|
|
|
+# for mapping in self.G1.get_subisomorphisms_vf2(self.G2):
|
|
|
+# # mapping is a list for which mapping[i] is the source node index mapped to the pattern node index i
|
|
|
+# # So we need to convert it into a dictionary
|
|
|
+# match = {}
|
|
|
+# for pattern_node, src_node in enumerate(mapping):
|
|
|
+# match[pattern_node] = src_node
|
|
|
+# yield match
|
|
|
|
|
|
|
|
|
class SubgraphIsoMatcher(HimesisMatcher):
|