Explorar o código

Trying Model Transformations

rparedis %!s(int64=2) %!d(string=hai) anos
pai
achega
7c487dd1cd

BIN=BIN
core/__pycache__/__init__.cpython-38.pyc


BIN=BIN
core/__pycache__/himesis.cpython-38.pyc


BIN=BIN
core/__pycache__/match_algo.cpython-38.pyc


+ 51 - 23
core/himesis.py

@@ -52,9 +52,12 @@ class Himesis:
         self.name = name
 
         self.state = mvs
-        self.attrib = {}
+        self.attrib = {
+            Himesis.Constants.GUID: self.state.new_id()
+        }
         self.vcount = 0
-        self.edges = []
+
+        self.match_sets = []
 
         # mmTypeData: enables add_node() to properly initialize to-be nodes s.t. they reflect the default values specified by their metamodels
         # _index2guid: a fast lookup of the node's guid by its index
@@ -104,7 +107,7 @@ class Himesis:
         return len(self._index2guid)
 
     def edge_count(self):
-        return len(self.edges)
+        return len(self.edge_iter())
 
     def node_iter(self):
         """
@@ -122,39 +125,48 @@ class Himesis:
         """
         Iterates over the edges in the graph, by index
         """
-        return iter(self.edges)
+        res = []
+        for node in self.node_guid_iter():
+            res.extend(self.state.read_outgoing(node))
+        return [e for e in res if self.edge_get_label(e) != self.Constants.ATTRIBUTES]
 
     def follow_edge(self, node, label):
         return self.state.read_dict(node, label)
 
     def node_set_attribute(self, node, key, value):
-        if Himesis.Constants.ATTRIBUTES not in self.state.read_dict_keys(node):
-            attrs = self.state.create_node()
-            self.state.create_dict(node, Himesis.Constants.ATTRIBUTES, attrs)
+        attrs = [self.state.read_value(v) for v in self.state.read_dict_keys(node)]
+        if Himesis.Constants.ATTRIBUTES not in attrs:
+            attr = self.state.create_node()
+            self.state.create_dict(node, Himesis.Constants.ATTRIBUTES, attr)
         else:
-            attrs = self.follow_edge(node, Himesis.Constants.ATTRIBUTES)
-        if key in self.state.read_dict_keys(attrs):
-            target = self.follow_edge(attrs, key)
+            attr = self.follow_edge(node, Himesis.Constants.ATTRIBUTES)
+        attrs = [self.state.read_value(v) for v in self.state.read_dict_keys(attr)]
+        if key in attrs:
+            target = self.follow_edge(attr, key)
             self.state.delete_node(target)
         target = self.state.create_nodevalue(value)
-        self.state.create_dict(attrs, key, target)
+        self.state.create_dict(attr, key, target)
 
     def node_get_attribute(self, node, key):
-        if Himesis.Constants.ATTRIBUTES not in self.state.read_dict_keys(node):
+        attrs = [self.state.read_value(v) for v in self.state.read_dict_keys(node)]
+        if Himesis.Constants.ATTRIBUTES not in attrs:
             return None
-        attrs = self.follow_edge(node, Himesis.Constants.ATTRIBUTES)
-        if key in self.state.read_dict_keys(attrs):
-            target = self.follow_edge(attrs, key)
+        attr = self.follow_edge(node, Himesis.Constants.ATTRIBUTES)
+        attrs = [self.state.read_value(v) for v in self.state.read_dict_keys(attr)]
+        if key in attrs:
+            target = self.follow_edge(attr, key)
             return self.state.read_value(target)
         return None
 
     def node_get_attributes(self, node):
-        if Himesis.Constants.ATTRIBUTES not in self.state.read_dict_keys(node):
-            return []
-        attrs = self.follow_edge(node, Himesis.Constants.ATTRIBUTES)
+        attrs = [self.state.read_value(v) for v in self.state.read_dict_keys(node)]
+        if Himesis.Constants.ATTRIBUTES not in attrs:
+            return {}
+        attr = self.follow_edge(node, Himesis.Constants.ATTRIBUTES)
         res = {}
-        for key in self.state.read_dict_keys(attrs):
-            target = self.follow_edge(attrs, key)
+        attrs = [self.state.read_value(v) for v in self.state.read_dict_keys(attr)]
+        for key in attrs:
+            target = self.follow_edge(attr, key)
             res[key] = self.state.read_value(target)
         return res
 
@@ -213,6 +225,16 @@ class Himesis:
     def edge_get_target(self, edge):
         return self.state.read_edge(edge)[1]
 
+    def edge_get_label(self, edge):
+        outs = (self.state.read_edge(e) for e in self.state.read_outgoing(edge))
+        res = [self.state.read_value(t) for _, t in outs]
+        if len(res) == 0:
+            return None
+        return res[0]
+
+    def get_edge(self, edge):
+        return self.state.read_edge(edge)
+
     def link_nodes(self, A, B, label=None):
         if label is None:
             self.state.create_edge(A, B)
@@ -225,6 +247,12 @@ class Himesis:
         edge = self.state.read_dict_edge(A, label)
         self.state.delete_edge(edge)
 
+    def predecessors(self, node):
+        return [self.state.read_edge(e)[0] for e in self.state.read_incoming(node)]
+
+    def successors(self, node):
+        return [self.state.read_edge(e)[1] for e in self.state.read_outgoing(node)]
+
     # def get_node(self, guid):
     #     """
     #         Retrieves the node instance with the specified guid
@@ -345,7 +373,7 @@ class HimesisPreConditionPattern(HimesisPattern):
         if pivot in self.nodes_pivot_in:
             return self.nodes_pivot_in[pivot]
 
-    def constraint(self, mtLabel2graphIndexMap, graph): # TODO??
+    def constraint(self, mtLabel2graphIndexMap, graph):
         """
             If a constraint shall be specified, the corresponding Himesis graph must override this method.
             The condition must be specified in the pattern graph and not the input graph.
@@ -406,6 +434,7 @@ class HimesisPreConditionPatternNAC(HimesisPreConditionPattern):
     def __init__(self, LHS, name, mvs):
         super(HimesisPreConditionPatternNAC, self).__init__(name, mvs)
         self.LHS = LHS
+        self.bridge = None
         self.bridge_size = 0
 
     def set_bridge_size(self):
@@ -431,7 +460,7 @@ class HimesisPreConditionPatternNAC(HimesisPreConditionPattern):
             # Swap
             G1, G2 = G2, G1
         # The bridge
-        G = HimesisPreConditionPattern('', self.state)
+        G = HimesisPreConditionPattern('', self.state) # TODO: Maybe create new state instead?
         G[Himesis.Constants.GUID] = uuid.uuid4()
 
         # We don't need to actually solve the largest common subgraph (LCS) problem
@@ -558,7 +587,6 @@ class HimesisPreConditionPatternNAC(HimesisPreConditionPattern):
         return G
 
 
-
 class HimesisPostConditionPattern(HimesisPattern):
     def __init__(self, name, mvs):
         super(HimesisPostConditionPattern, self).__init__(name, mvs)

+ 48 - 47
core/match_algo.py

@@ -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):

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1 - 0
modelversestate.drawio


BIN=BIN
rules/__pycache__/__init__.cpython-38.pyc


BIN=BIN
rules/__pycache__/arule.cpython-38.pyc


BIN=BIN
rules/__pycache__/frule.cpython-38.pyc


+ 5 - 5
rules/arule.py

@@ -2,11 +2,11 @@
 Copyright 2011 by the AToMPM team and licensed under the LGPL
 See COPYING.lesser and README.md in the root of this project for full details'''
 
-from ..tcore.composer import Composer
-from ..tcore.matcher import Matcher
-from ..tcore.iterator import Iterator
-from ..tcore.rewriter import Rewriter
-from ..tcore.resolver import Resolver
+from tcore.composer import Composer
+from tcore.matcher import Matcher
+from tcore.iterator import Iterator
+from tcore.rewriter import Rewriter
+from tcore.resolver import Resolver
 
 class ARule(Composer):
     '''

+ 2 - 2
rules/frule.py

@@ -2,9 +2,9 @@
 Copyright 2011 by the AToMPM team and licensed under the LGPL
 See COPYING.lesser and README.md in the root of this project for full details'''
 
-from ..util.infinity import INFINITY
+from util.infinity import INFINITY
 from .arule import ARule
-from ..tcore.resolver import Resolver
+from tcore.resolver import Resolver
 
 
 class FRule(ARule):

BIN=BIN
state/__pycache__/__init__.cpython-38.pyc


BIN=BIN
tcore/__pycache__/__init__.cpython-38.pyc


BIN=BIN
tcore/__pycache__/composer.cpython-38.pyc


BIN=BIN
tcore/__pycache__/composite_primitive.cpython-38.pyc


BIN=BIN
tcore/__pycache__/iterator.cpython-38.pyc


BIN=BIN
tcore/__pycache__/matcher.cpython-38.pyc


BIN=BIN
tcore/__pycache__/messages.cpython-38.pyc


BIN=BIN
tcore/__pycache__/primitive.cpython-38.pyc


BIN=BIN
tcore/__pycache__/resolver.cpython-38.pyc


BIN=BIN
tcore/__pycache__/rewriter.cpython-38.pyc


BIN=BIN
tcore/__pycache__/rule_primitive.cpython-38.pyc


+ 2 - 2
tcore/iterator.py

@@ -2,8 +2,8 @@
 Copyright 2011 by the AToMPM team and licensed under the LGPL
 See COPYING.lesser and README.md in the root of this project for full details'''
 
-from ..util.seeded_random import Random
-from ..util.infinity import INFINITY
+from util.seeded_random import Random
+from util.infinity import INFINITY
 from .rule_primitive import RulePrimitive
 #from messages import TransformationException
 

+ 3 - 3
tcore/matcher.py

@@ -4,9 +4,9 @@ See COPYING.lesser and README.md in the root of this project for full details'''
 
 import sys
 from copy import deepcopy
-from ..util.infinity import INFINITY
-from ..core.match_algo import HimesisMatcher
-from ..core.himesis import HConstants as HC
+from util.infinity import INFINITY
+from core.match_algo import HimesisMatcher
+from core.himesis import HConstants as HC
 from .rule_primitive import RulePrimitive
 from .messages import MatchSet, Match, TransformationException
 

+ 11 - 11
tcore/messages.py

@@ -3,7 +3,7 @@ Copyright 2011 by the AToMPM team and licensed under the LGPL
 See COPYING.lesser and README.md in the root of this project for full details'''
 
 import copy, traceback
-from ..core.himesis import Himesis
+from core.himesis import Himesis
 
 # Abstract class
 class Message(object): pass
@@ -260,20 +260,20 @@ class Match(dict):
         return mapping
 
     def from_mapping(self, mapping, source_graph, pattern_graph):
-        '''
-            Extracts all matches from a mapping dictionary {pattern node index: source node index}
-            and adds them to this object in the form {pattern label: source node guid}.
-            Relevant pivots are also extracted.
-        '''
+        """
+        Extracts all matches from a mapping dictionary {pattern node index: source node index}
+        and adds them to this object in the form {pattern label: source node guid}.
+        Relevant pivots are also extracted.
+        """
         for pattern_node in mapping:
             #print "Pattern Graph: ", pattern_graph
             #print "len(Pattern Graph.vs): ", pattern_graph.vcount()
             #print "Pattern Node: ", pattern_node
-            if pattern_node < pattern_graph.vcount():
-                #print "Pattern Graph.vs[pattern_node]: ", pattern_graph.vs[pattern_node]
-                label = pattern_graph.vs[pattern_node][Himesis.Constants.MT_LABEL]
-                guid = source_graph.vs[mapping[pattern_node]][Himesis.Constants.GUID]
-                self[label] = guid
+            # if pattern_node < pattern_graph.vertex_count():
+            #     print "Pattern Graph.vs[pattern_node]: ", pattern_graph.vs[pattern_node]
+            label = pattern_graph.node_get_attribute(pattern_node, Himesis.Constants.MT_LABEL)
+            guid = mapping[pattern_node]
+            self[label] = guid
 
         self.local_pivots.from_mapping(mapping, source_graph, pattern_graph)
 

+ 3 - 3
tcore/rewriter.py

@@ -4,9 +4,9 @@ See COPYING.lesser and README.md in the root of this project for full details'''
 
 from .rule_primitive import RulePrimitive
 from .messages import TransformationException
-from ..core.himesis import Himesis
-from ...tconstants import TConstants as TC
-from ...utils import Utilities as utils
+from core.himesis import Himesis
+# from ...tconstants import TConstants as TC
+# from ...utils import Utilities as utils
 
 import sys
 if sys.version_info[0] >= 3:

+ 79 - 0
transformation.py

@@ -0,0 +1,79 @@
+"""
+A test TCore transformation: an FSA to a PN, based on
+	https://homepages.ecs.vuw.ac.nz/~tk/publications/papers/explicitly-modeling-transformations.pdf
+"""
+
+from core.himesis import Himesis, HimesisPreConditionPatternNAC, HimesisPreConditionPatternLHS, HimesisPostConditionPattern
+from tcore.messages import Packet
+from rules.frule import FRule
+from state import MvSKernel, getMvSBackend
+
+def print_results(graph):
+	print("GRAPH:")
+	for node in graph.node_guid_iter():
+		print(" ", node)
+		for k, v in graph.node_get_attributes(node).items():
+			print("   ", k, "=", v)
+	for edge in graph.edge_iter():
+		label = graph.edge_get_label(edge)
+		s, t = graph.get_edge(edge)
+		print(" ", s, "-[ %s ]->" % label, t)
+
+# TODO: Read from DrawIO
+# TODO: Select graph based on Formalism name and instance name
+
+FSA = Himesis("FSA", getMvSBackend(MvSKernel.IGRAPH))
+
+# Create the nodes
+s1 = FSA.add_node("State", False)
+FSA.node_set_attribute(s1, "name", "S1")
+s2 = FSA.add_node("State", False)
+FSA.node_set_attribute(s2, "name", "S2")
+s3 = FSA.add_node("State", False)
+FSA.node_set_attribute(s3, "name", "S3")
+
+# Create the edges
+FSA.link_nodes(s1, s2, "y")
+FSA.link_nodes(s2, s2, "e")
+FSA.link_nodes(s2, s3, "s")
+
+# TODO: conformance check w.r.t. Formalism
+
+
+# Create the rules
+# TODO: Also obtain these from DrawIO
+
+# RULE 1: State2Place
+r1LHS = HimesisPreConditionPatternLHS("r1LHS", getMvSBackend(MvSKernel.IGRAPH))
+r1n1 = r1LHS.add_node("State", False)
+r1LHS.node_set_attribute(r1n1, Himesis.Constants.MT_LABEL, '1')
+
+r1NAC = HimesisPreConditionPatternNAC(None, "r1NAC", getMvSBackend(MvSKernel.IGRAPH))
+r1n2 = r1NAC.add_node("State", False)
+r1NAC.node_set_attribute(r1n2, Himesis.Constants.MT_LABEL, '1')
+r1n3 = r1NAC.add_node("Place", False)
+r1NAC.node_set_attribute(r1n3, Himesis.Constants.MT_LABEL, '2')
+r1n4 = r1NAC.add_node("Link", True)
+r1NAC.node_set_attribute(r1n4, Himesis.Constants.MT_LABEL, '3')
+r1NAC.link_nodes(r1n2, r1n4)
+r1NAC.link_nodes(r1n4, r1n3)
+r1LHS.addNAC(r1NAC)
+
+r1RHS = HimesisPostConditionPattern("r1RHS", getMvSBackend(MvSKernel.IGRAPH))
+r1n5 = r1RHS.add_node("State", False)
+r1RHS.node_set_attribute(r1n5, Himesis.Constants.MT_LABEL, '1')
+r1n6 = r1RHS.add_node("Place", False)
+r1RHS.node_set_attribute(r1n6, Himesis.Constants.MT_LABEL, '2')
+r1n7 = r1RHS.add_node("Link", True)
+r1RHS.node_set_attribute(r1n7, Himesis.Constants.MT_LABEL, '3')
+r1RHS.link_nodes(r1n5, r1n7)
+r1RHS.link_nodes(r1n7, r1n6)
+
+print("Applying FRule 1")
+frule = FRule(r1LHS, r1RHS)
+FSA2PN = frule.packet_in(Packet(FSA))
+
+print_results(FSA2PN.graph)
+
+
+

BIN=BIN
util/__pycache__/__init__.cpython-38.pyc


BIN=BIN
util/__pycache__/infinity.cpython-38.pyc


BIN=BIN
util/__pycache__/seeded_random.cpython-38.pyc