Browse Source

Nominal and structural typing seem to be fully working

Andrei Bondarenko 2 years ago
parent
commit
695ec6132a
3 changed files with 77 additions and 25 deletions
  1. 3 3
      bootstrap/pn.py
  2. 65 13
      framework/conformance.py
  3. 9 9
      services/pn.py

+ 3 - 3
bootstrap/pn.py

@@ -31,10 +31,10 @@ def bootstrap_pn(state: State, model_name: str) -> UUID:
     # Create class attributes
     service.create_attribute_link("P", "Integer", "t", False)
     service.create_attribute_link("P", "String", "n", False)
-    service.create_attribute_link("T", "String", "n", False)
+    service.create_attribute_link("T", "String", "name", False)
     # Create association attributes
-    service.create_attribute_link("P2T", "Integer", "w", False)
-    service.create_attribute_link("T2P", "Integer", "w", False)
+    service.create_attribute_link("P2T", "Integer", "weight", False)
+    service.create_attribute_link("T2P", "Integer", "weight", False)
     # Create test constraint
     service.add_constraint("P", "True")
     return model_uuid

+ 65 - 13
framework/conformance.py

@@ -388,35 +388,85 @@ class Conformance:
 
     def match_structures(self):
         ref_element, = self.bottom.read_outgoing_elements(self.scd_model, "ModelRef")
-        # match nodes
+        # matching
         for m_element, m_name in self.model_names.items():
-            self.candidates[m_name] = set()
             is_edge = self.bottom.read_edge_source(m_element) is not None
             for type_name, structure in self.structures.items():
                 tm_element, = self.bottom.read_outgoing_elements(self.type_model, type_name)
                 type_is_edge = self.bottom.read_edge_source(tm_element) is not None
                 if is_edge == type_is_edge:
-                    matched = []
+                    matched = 0
                     for name, optional, attr_type in structure:
                         try:
                             attr, = self.bottom.read_outgoing_elements(self.model, f"{m_name}.{name}")
+                            attr_tm, = self.bottom.read_outgoing_elements(self.type_model, attr_type)
                             # if attribute is a modelref, we need to check whether it
                             # linguistically conforms to the specified type
-                            # if its an internlly defined attribute, this will be checked by constraints later
-                            morphisms = self.bottom.read_outgoing_elements(tm_element, "Morphism")
+                            # if its an internally defined attribute, this will be checked by constraints
+                            morphisms = self.bottom.read_outgoing_elements(attr_tm, "Morphism")
                             if ref_element in morphisms:
-                                pass
-
+                                # check conformance of reference model
+                                type_model_uuid = UUID(self.bottom.read_value(attr_tm))
+                                model_uuid = UUID(self.bottom.read_value(attr))
+                                attr_conforms = Conformance(self.state, self.scd_model, model_uuid, type_model_uuid)\
+                                    .check_nominal()
+                            else:
+                                # eval constraints
+                                code = self.read_attribute(attr_tm, "constraint")
+                                if code is not None:
+                                    attr_conforms = self.evaluate_constraint(code, element=attr)
+                            matched += 1
                         except ValueError:
+                            # attr not found or failed parsing UUID
                             if optional:
                                 continue
                             else:
                                 break
-                    if len(matched) == len(structure):
-                        self.candidates[m_name].add(type_name)
+                    if matched == len(structure):
+                        self.candidates.setdefault(m_name, set()).add(type_name)
+        # filter out candidates for links based on source and target types
+        for m_element, m_name in self.model_names.items():
+            is_edge = self.bottom.read_edge_source(m_element) is not None
+            if is_edge and m_name in self.candidates:
+                m_source = self.bottom.read_edge_source(m_element)
+                m_target = self.bottom.read_edge_target(m_element)
+                source_candidates = self.candidates[self.model_names[m_source]]
+                target_candidates = self.candidates[self.model_names[m_target]]
+                remove = set()
+                for candidate_name in self.candidates[m_name]:
+                    candidate_element, = self.bottom.read_outgoing_elements(self.type_model, candidate_name)
+                    candidate_source = self.type_model_names[self.bottom.read_edge_source(candidate_element)]
+                    if candidate_source not in source_candidates:
+                        remove.add(candidate_name)
+                    candidate_target = self.type_model_names[self.bottom.read_edge_target(candidate_element)]
+                    if candidate_target not in target_candidates:
+                        remove.add(candidate_name)
+                self.candidates[m_name] = self.candidates[m_name].difference(remove)
 
     def build_morphisms(self):
-        pass
+        if not all([len(c) == 1 for c in self.candidates.values()]):
+            print("Ambiguous structural matches found, unable to build unique morphism.")
+            return False
+        mapping = {k: v.pop() for k, v in self.candidates.items()}
+        for m_name, tm_name in mapping.items():
+            # morphism to class/assoc
+            m_element, = self.bottom.read_outgoing_elements(self.model, m_name)
+            tm_element, = self.bottom.read_outgoing_elements(self.type_model, tm_name)
+            self.bottom.create_edge(m_element, tm_element, "Morphism")
+            # morphism for attributes and attribute links
+            structure = self.structures[tm_name]
+            for attr_name, _, attr_type in structure:
+                try:
+                    # attribute node
+                    attr_element, = self.bottom.read_outgoing_elements(self.model, f"{m_name}.{attr_name}")
+                    attr_type_element, = self.bottom.read_outgoing_elements(self.type_model, attr_type)
+                    self.bottom.create_edge(attr_element, attr_type_element, "Morphism")
+                    # attribute link
+                    attr_link_element, = self.bottom.read_outgoing_elements(self.model, f"{m_name}.{attr_name}_link")
+                    attr_link_type_element, = self.bottom.read_outgoing_elements(self.type_model, f"{tm_name}_{attr_name}")
+                    self.bottom.create_edge(attr_link_element, attr_link_type_element, "Morphism")
+                except ValueError:
+                    pass
 
 
 if __name__ == '__main__':
@@ -426,6 +476,7 @@ if __name__ == '__main__':
     scd = bootstrap_scd(s)
     from bootstrap.pn import bootstrap_pn
     ltm_pn = bootstrap_pn(s, "PN")
+    ltm_pn_lola = bootstrap_pn(s, "PNlola")
     from services.pn import PN
     my_pn = s.create_node()
     PNserv = PN(ltm_pn, my_pn, s)
@@ -435,10 +486,11 @@ if __name__ == '__main__':
     PNserv.create_p2t("p1", "t1", 1)
     PNserv.create_t2p("t1", "p2", 1)
     
-    cf = Conformance(s, scd, my_pn, ltm_pn)
+    cf = Conformance(s, scd, my_pn, ltm_pn_lola)
     # cf = Conformance(s, scd, ltm_pn, scd)
+    cf.precompute_structures()
+    cf.match_structures()
+    cf.build_morphisms()
     print(cf.check_nominal())
-    # cf.precompute_structures()
-    # cf.match_structures()
 
 

+ 9 - 9
services/pn.py

@@ -52,11 +52,11 @@ class PN:
         name_model = self.bottom.create_node()
         String(name_model, self.bottom.state).create(name)
         name_node = self.bottom.create_node(str(name_model))
-        self.bottom.create_edge(self.model, name_node, f"{name}.n")
+        self.bottom.create_edge(self.model, name_node, f"{name}.name")
         name_link = self.bottom.create_edge(transition_node, name_node)
-        self.bottom.create_edge(self.model, name_link, f"{name}.n_link")
+        self.bottom.create_edge(self.model, name_link, f"{name}.name_link")
         ltm_pn_node, = self.bottom.read_outgoing_elements(self.ltm_pn, "String")
-        ltm_pn_link, = self.bottom.read_outgoing_elements(self.ltm_pn, "T_n")
+        ltm_pn_link, = self.bottom.read_outgoing_elements(self.ltm_pn, "T_name")
         self.bottom.create_edge(name_node, ltm_pn_node, "Morphism")
         self.bottom.create_edge(name_link, ltm_pn_link, "Morphism")
 
@@ -73,11 +73,11 @@ class PN:
         weight_model = self.bottom.create_node()
         Integer(weight_model, self.bottom.state).create(weight)
         weight_node = self.bottom.create_node(str(weight_model))
-        self.bottom.create_edge(self.model, weight_node, f"{place}_to_{transition}.w")
+        self.bottom.create_edge(self.model, weight_node, f"{place}_to_{transition}.weight")
         weight_link = self.bottom.create_edge(edge, weight_node)
-        self.bottom.create_edge(self.model, weight_link, f"{place}_to_{transition}.w_link")
+        self.bottom.create_edge(self.model, weight_link, f"{place}_to_{transition}.weight_link")
         scd_node, = self.bottom.read_outgoing_elements(self.ltm_pn, "Integer")
-        scd_link, = self.bottom.read_outgoing_elements(self.ltm_pn, "P2T_w")
+        scd_link, = self.bottom.read_outgoing_elements(self.ltm_pn, "P2T_weight")
         self.bottom.create_edge(weight_node, scd_node, "Morphism")
         self.bottom.create_edge(weight_link, scd_link, "Morphism")
 
@@ -94,11 +94,11 @@ class PN:
         weight_model = self.bottom.create_node()
         Integer(weight_model, self.bottom.state).create(weight)
         weight_node = self.bottom.create_node(str(weight_model))
-        self.bottom.create_edge(self.model, weight_node, f"{transition}_to_{place}.w")
+        self.bottom.create_edge(self.model, weight_node, f"{transition}_to_{place}.weight")
         weight_link = self.bottom.create_edge(edge, weight_node)
-        self.bottom.create_edge(self.model, weight_link, f"{transition}_to_{place}.w_link")
+        self.bottom.create_edge(self.model, weight_link, f"{transition}_to_{place}.weight_link")
         scd_node, = self.bottom.read_outgoing_elements(self.ltm_pn, "Integer")
-        scd_link, = self.bottom.read_outgoing_elements(self.ltm_pn, "T2P_w")
+        scd_link, = self.bottom.read_outgoing_elements(self.ltm_pn, "T2P_weight")
         self.bottom.create_edge(weight_node, scd_node, "Morphism")
         self.bottom.create_edge(weight_link, scd_link, "Morphism")