diff --git a/bootstrap/pn.py b/bootstrap/pn.py index 26e8556..a319355 100644 --- a/bootstrap/pn.py +++ b/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 diff --git a/framework/conformance.py b/framework/conformance.py index d0bcc9e..5ad8496 100644 --- a/framework/conformance.py +++ b/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() diff --git a/services/pn.py b/services/pn.py index 285368b..3415e91 100644 --- a/services/pn.py +++ b/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")