From 4c802a6a03b9059a77e85a160904ecbb7492aee2 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Thu, 12 Aug 2021 11:11:59 +0200 Subject: [PATCH 01/17] Refactored some names again --- {service => services}/__init__.py | 0 {service => services}/base.py | 0 {service => services}/bottom/V0.py | 2 +- {service => services}/bottom/__init__.py | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename {service => services}/__init__.py (100%) rename {service => services}/base.py (100%) rename {service => services}/bottom/V0.py (98%) rename {service => services}/bottom/__init__.py (100%) diff --git a/service/__init__.py b/services/__init__.py similarity index 100% rename from service/__init__.py rename to services/__init__.py diff --git a/service/base.py b/services/base.py similarity index 100% rename from service/base.py rename to services/base.py diff --git a/service/bottom/V0.py b/services/bottom/V0.py similarity index 98% rename from service/bottom/V0.py rename to services/bottom/V0.py index 36a51d8..c17b9f8 100644 --- a/service/bottom/V0.py +++ b/services/bottom/V0.py @@ -1,4 +1,4 @@ -from service.base import Service, UUID +from services.base import Service, UUID from state.base import State from typing import Any, List diff --git a/service/bottom/__init__.py b/services/bottom/__init__.py similarity index 100% rename from service/bottom/__init__.py rename to services/bottom/__init__.py From 7f7924197b6d8cbe4b6eba5b23b5d6860f23626a Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Fri, 13 Aug 2021 11:53:48 +0200 Subject: [PATCH 02/17] MCL graph (without morphisms done) --- bootstrap/__init__.py | 0 bootstrap/scd.py | 156 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 bootstrap/__init__.py create mode 100644 bootstrap/scd.py diff --git a/bootstrap/__init__.py b/bootstrap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bootstrap/scd.py b/bootstrap/scd.py new file mode 100644 index 0000000..db6e761 --- /dev/null +++ b/bootstrap/scd.py @@ -0,0 +1,156 @@ +from state.base import State, UUID +from services.bottom.V0 import Bottom + + +def create_model_root(bottom: Bottom, model_name: str) -> UUID: + model_root = bottom.create_node() + mcl_root_id = bottom.create_node(value=str(model_root)) + bottom.create_edge(bottom.model, mcl_root_id, label=model_name) + return model_root + + +def bootstrap_scd(state: State) -> UUID: + # init model roots and store their UUIDs attached to state root + state_root = state.read_root() + bottom = Bottom(state_root, state) + mcl_root = create_model_root(bottom, "SCD") + mcl_morphism_root = create_model_root(bottom, "phi(SCD,SCD)") + + # Create model roots for primitive types + integer_type_root = create_model_root(bottom, "Integer") + boolean_type_root = create_model_root(bottom, "Boolean") + string_type_root = create_model_root(bottom, "String") + float_type_root = create_model_root(bottom, "Float") + type_type_root = create_model_root(bottom, "Type") + + # create MCL, without morphism links + bottom = Bottom(mcl_root, state) + + def add_node_element(element_name, node_value=None): + """ Helper function, adds node to model with given name and value """ + _node = bottom.create_node(value=node_value) + bottom.create_edge(bottom.model, _node, element_name) + return _node + + def add_edge_element(element_name, source, target): + """ Helper function, adds edge to model with given name """ + _edge = bottom.create_edge(source, target) + bottom.create_edge(bottom.model, _edge, element_name) + return _edge + + def add_attribute_attributes(attribute_element_name, attribute_element, _name, _optional): + _name_node = add_node_element(f"{attribute_element_name}.name", _name) + _name_edge = add_edge_element(f"{attribute_element_name}01", attribute_element, _name_node) + _optional_node = add_node_element(f"{attribute_element_name}.optional", _optional) + _optional_edge = add_edge_element(f"{attribute_element_name}02", attribute_element, _optional_node) + return _name_node, _name_edge, _optional_node, _optional_edge + + # # CLASSES, i.e. elements typed by Class + # # Element + element_node = add_node_element("Element") + # # Class + class_node = add_node_element("Class") + # # Attribute + attr_node = add_node_element("Attribute") + # # ModelRef + model_ref_node = add_node_element("ModelRef") + # # Global Constraint + glob_constr_node = add_node_element("GlobalConstraint") + # # ASSOCIATIONS, i.e. elements typed by Association + # # Association + assoc_edge = add_edge_element("Association", class_node, class_node) + # # Inheritance + inh_edge = add_edge_element("Inheritance", element_node, element_node) + # # Attribute Link + attr_link_edge = add_edge_element("AttributeLink", element_node, attr_node) + # # INHERITANCES, i.e. elements typed by Inheritance + # # Class inherits from Element + class_inh_element_edge = add_edge_element("class_inh_element", class_node, element_node) + # # Attribute inherits from Element + attr_inh_element_edge = add_edge_element("attr_inh_element", attr_node, element_node) + # # Association inherits from Element + assoc_inh_element_edge = add_edge_element("assoc_inh_element", assoc_edge, element_node) + # # AttributeLink inherits from Element + attr_link_inh_element_edge = add_edge_element("attr_link_inh_element", attr_link_edge, element_node) + # # ModelRef inherits from Attribute + model_ref_inh_attr_edge = add_edge_element("model_ref_inh_attr", model_ref_node, attr_node) + # # ATTRIBUTES, i.e. elements typed by Attribute + # # Action Code # TODO: Update to ModelRef when action code is explicitly modelled + action_code_node = add_node_element("ActionCode") + # # MODELREFS, i.e. elements typed by ModelRef + # # Integer + integer_node = add_node_element("Integer", str(integer_type_root)) + # # String + string_node = add_node_element("String", str(string_type_root)) + # # Boolean + boolean_node = add_node_element("Boolean", str(boolean_type_root)) + # # ATTRIBUTE LINKS, i.e. elements typed by AttributeLink + # # name attribute of AttributeLink + attr_name_edge = add_edge_element("AttributeLink_name", attr_link_edge, string_node) + # # optional attribute of AttributeLink + attr_opt_edge = add_edge_element("AttributeLink_optional", attr_link_edge, boolean_node) + # # constraint attribute of Element + elem_constr_edge = add_edge_element("Element_constraint", element_node, action_code_node) + # # abstract attribute of Class + class_abs_edge = add_edge_element("Class_abstract", class_node, boolean_node) + # # multiplicity attributes of Class + class_l_c_edge = add_edge_element("Class_lower_cardinality", class_node, integer_node) + class_u_c_edge = add_edge_element("Class_upper_cardinality", class_node, integer_node) + # # multiplicity attributes of Association + assoc_s_l_c_edge = add_edge_element("Association_source_lower_cardinality", assoc_edge, integer_node) + assoc_s_u_c_edge = add_edge_element("Association_source_upper_cardinality", assoc_edge, integer_node) + assoc_t_l_c_edge = add_edge_element("Association_target_lower_cardinality", assoc_edge, integer_node) + assoc_t_u_c_edge = add_edge_element("Association_target_upper_cardinality", assoc_edge, integer_node) + # # ATTRIBUTE ATTRIBUTES, assign 'name' and 'optional' attributes to all AttributeLinks + # # AttributeLink_name + attr_name_name_node, attr_name_name_edge, attr_name_optional_node, attr_name_optional_edge = \ + add_attribute_attributes("AttributeLink_name", attr_name_edge, "name", False) + # # AttributeLink_opt + attr_opt_name_node, attr_opt_name_edge, attr_opt_optional_node, attr_opt_optional_edge = \ + add_attribute_attributes("AttributeLink_optional", attr_opt_edge, "optional", False) + # # Element_constraint + elem_constr_name_node, elem_constr_name_edge, elem_constr_optional_node, elem_constr_optional_edge = \ + add_attribute_attributes("Element_constraint", elem_constr_edge, "constraint", True) + # # Class_abstract + class_abs_name_node, class_abs_name_edge, class_abs_optional_node, class_abs_optional_edge = \ + add_attribute_attributes("Class_abstract", class_abs_edge, "abstract", True) + # # Class_lower_cardinality + class_l_c_name_node, class_l_c_name_edge, class_l_c_optional_node, class_l_c_optional_edge = \ + add_attribute_attributes("Class_lower_cardinality", class_l_c_edge, "lower_cardinality", True) + # # Class_upper_cardinality + class_u_c_name_node, class_u_c_name_edge, class_u_c_optional_node, class_u_c_optional_edge = \ + add_attribute_attributes("Class_upper_cardinality", class_u_c_edge, "upper_cardinality", True) + # # Association_source_lower_cardinality + assoc_s_l_c_name_node, assoc_s_l_c_name_edge, assoc_s_l_c_optional_node, assoc_s_l_c_optional_edge = \ + add_attribute_attributes("Association_source_lower_cardinality", assoc_s_l_c_edge, "source_lower_cardinality", True) + # # Association_source_upper_cardinality + assoc_s_u_c_name_node, assoc_s_u_c_name_edge, assoc_s_u_c_optional_node, assoc_s_u_c_optional_edge = \ + add_attribute_attributes("Association_source_upper_cardinality", assoc_s_u_c_edge, "source_upper_cardinality", True) + # # Association_target_lower_cardinality + assoc_t_l_c_name_node, assoc_t_l_c_name_edge, assoc_t_l_c_optional_node, assoc_t_l_c_optional_edge = \ + add_attribute_attributes("Association_target_lower_cardinality", assoc_t_l_c_edge, "target_lower_cardinality", True) + # # Association_target_upper_cardinality + assoc_t_u_c_name_node, assoc_t_u_c_name_edge, assoc_t_u_c_optional_node, assoc_t_u_c_optional_edge = \ + add_attribute_attributes("Association_target_upper_cardinality", assoc_t_u_c_edge, "target_upper_cardinality", True) + + # create phi(SCD,SCD) to type MCL with itself + + +def bootstrap_type_type(model_root: UUID, state: State) -> UUID: + pass + + +def bootstrap_boolean_type(model_root: UUID, state: State) -> UUID: + pass + + +def bootstrap_integer_type(model_root: UUID, state: State) -> UUID: + pass + + +def bootstrap_float_type(model_root: UUID, state: State) -> UUID: + pass + + +def bootstrap_string_type(model_root: UUID, state: State) -> UUID: + pass From 6723bd590b3497a28917ab1bc1b64a2e3a9b77f9 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Fri, 13 Aug 2021 20:02:28 +0200 Subject: [PATCH 03/17] added morphism to scd bootstrap --- bootstrap/scd.py | 125 +++++++++++++++++++++++++++++++++++------- services/bottom/V0.py | 5 +- 2 files changed, 109 insertions(+), 21 deletions(-) diff --git a/bootstrap/scd.py b/bootstrap/scd.py index db6e761..6ad102a 100644 --- a/bootstrap/scd.py +++ b/bootstrap/scd.py @@ -40,9 +40,9 @@ def bootstrap_scd(state: State) -> UUID: def add_attribute_attributes(attribute_element_name, attribute_element, _name, _optional): _name_node = add_node_element(f"{attribute_element_name}.name", _name) - _name_edge = add_edge_element(f"{attribute_element_name}01", attribute_element, _name_node) + _name_edge = add_edge_element(f"{attribute_element_name}.name_link", attribute_element, _name_node) _optional_node = add_node_element(f"{attribute_element_name}.optional", _optional) - _optional_edge = add_edge_element(f"{attribute_element_name}02", attribute_element, _optional_node) + _optional_edge = add_edge_element(f"{attribute_element_name}.optional_link", attribute_element, _optional_node) return _name_node, _name_edge, _optional_node, _optional_edge # # CLASSES, i.e. elements typed by Class @@ -134,23 +134,108 @@ def bootstrap_scd(state: State) -> UUID: add_attribute_attributes("Association_target_upper_cardinality", assoc_t_u_c_edge, "target_upper_cardinality", True) # create phi(SCD,SCD) to type MCL with itself + bottom.model = mcl_morphism_root + + def add_mcl_morphism(element_name, type_name): + # get elements from mcl by name + _element_edge, = bottom.read_outgoing_edges(mcl_root, element_name) + _element_node = bottom.read_edge_target(_element_edge) + _type_edge, = bottom.read_outgoing_edges(mcl_root, type_name) + _type_node = bottom.read_edge_target(_type_edge) + # add elements to morphism model + if element_name not in bottom.read_keys(bottom.model): + bottom.create_edge(bottom.model, _element_node, element_name) + if type_name not in bottom.read_keys(bottom.model): + bottom.create_edge(bottom.model, _type_node, type_name) + # create morphism link + morphism_edge = bottom.create_edge(_element_node, _type_node) + bottom.create_edge(bottom.model, morphism_edge, f"{element_name}_is_a_{type_name}") + + # Class + add_mcl_morphism("Element", "Class") + add_mcl_morphism("Class", "Class") + add_mcl_morphism("Attribute", "Class") + add_mcl_morphism("ModelRef", "Class") + add_mcl_morphism("GlobalConstraint", "Class") + # Association + add_mcl_morphism("Association", "Association") + add_mcl_morphism("Inheritance", "Association") + add_mcl_morphism("AttributeLink", "Association") + # Inheritance + add_mcl_morphism("class_inh_element", "Inheritance") + add_mcl_morphism("attr_inh_element", "Inheritance") + add_mcl_morphism("assoc_inh_element", "Inheritance") + add_mcl_morphism("attr_link_inh_element", "Inheritance") + add_mcl_morphism("model_ref_inh_attr", "Inheritance") + # Attribute + add_mcl_morphism("ActionCode", "Attribute") + # ModelRef + add_mcl_morphism("Integer", "ModelRef") + add_mcl_morphism("String", "ModelRef") + add_mcl_morphism("Boolean", "ModelRef") + # AttributeLink + add_mcl_morphism("AttributeLink_name", "AttributeLink") + add_mcl_morphism("AttributeLink_optional", "AttributeLink") + add_mcl_morphism("Element_constraint", "AttributeLink") + add_mcl_morphism("Class_abstract", "AttributeLink") + add_mcl_morphism("Class_lower_cardinality", "AttributeLink") + add_mcl_morphism("Class_upper_cardinality", "AttributeLink") + add_mcl_morphism("Association_source_lower_cardinality", "AttributeLink") + add_mcl_morphism("Association_source_upper_cardinality", "AttributeLink") + add_mcl_morphism("Association_target_lower_cardinality", "AttributeLink") + add_mcl_morphism("Association_target_upper_cardinality", "AttributeLink") + # AttributeLink_name + add_mcl_morphism("AttributeLink_name.name_link", "AttributeLink_name") + add_mcl_morphism("AttributeLink_optional.name_link", "AttributeLink_name") + add_mcl_morphism("Element_constraint.name_link", "AttributeLink_name") + add_mcl_morphism("Class_abstract.name_link", "AttributeLink_name") + add_mcl_morphism("Class_lower_cardinality.name_link", "AttributeLink_name") + add_mcl_morphism("Class_upper_cardinality.name_link", "AttributeLink_name") + add_mcl_morphism("Association_source_lower_cardinality.name_link", "AttributeLink_name") + add_mcl_morphism("Association_source_upper_cardinality.name_link", "AttributeLink_name") + add_mcl_morphism("Association_target_lower_cardinality.name_link", "AttributeLink_name") + add_mcl_morphism("Association_target_upper_cardinality.name_link", "AttributeLink_name") + # AttributeLink_optional + add_mcl_morphism("AttributeLink_name.optional_link", "AttributeLink_optional") + add_mcl_morphism("AttributeLink_optional.optional_link", "AttributeLink_optional") + add_mcl_morphism("Element_constraint.optional_link", "AttributeLink_optional") + add_mcl_morphism("Class_abstract.optional_link", "AttributeLink_optional") + add_mcl_morphism("Class_lower_cardinality.optional_link", "AttributeLink_optional") + add_mcl_morphism("Class_upper_cardinality.optional_link", "AttributeLink_optional") + add_mcl_morphism("Association_source_lower_cardinality.optional_link", "AttributeLink_optional") + add_mcl_morphism("Association_source_upper_cardinality.optional_link", "AttributeLink_optional") + add_mcl_morphism("Association_target_lower_cardinality.optional_link", "AttributeLink_optional") + add_mcl_morphism("Association_target_upper_cardinality.optional_link", "AttributeLink_optional") + # String + add_mcl_morphism("AttributeLink_name.name", "String") + add_mcl_morphism("AttributeLink_optional.name", "String") + add_mcl_morphism("Element_constraint.name", "String") + add_mcl_morphism("Class_abstract.name", "String") + add_mcl_morphism("Class_lower_cardinality.name", "String") + add_mcl_morphism("Class_upper_cardinality.name", "String") + add_mcl_morphism("Association_source_lower_cardinality.name", "String") + add_mcl_morphism("Association_source_upper_cardinality.name", "String") + add_mcl_morphism("Association_target_lower_cardinality.name", "String") + add_mcl_morphism("Association_target_upper_cardinality.name", "String") + # Boolean + add_mcl_morphism("AttributeLink_name.optional", "Boolean") + add_mcl_morphism("AttributeLink_optional.optional", "Boolean") + add_mcl_morphism("Element_constraint.optional", "Boolean") + add_mcl_morphism("Class_abstract.optional", "Boolean") + add_mcl_morphism("Class_lower_cardinality.optional", "Boolean") + add_mcl_morphism("Class_upper_cardinality.optional", "Boolean") + add_mcl_morphism("Association_source_lower_cardinality.optional", "Boolean") + add_mcl_morphism("Association_source_upper_cardinality.optional", "Boolean") + add_mcl_morphism("Association_target_lower_cardinality.optional", "Boolean") + add_mcl_morphism("Association_target_upper_cardinality.optional", "Boolean") + + return mcl_root -def bootstrap_type_type(model_root: UUID, state: State) -> UUID: - pass - - -def bootstrap_boolean_type(model_root: UUID, state: State) -> UUID: - pass - - -def bootstrap_integer_type(model_root: UUID, state: State) -> UUID: - pass - - -def bootstrap_float_type(model_root: UUID, state: State) -> UUID: - pass - - -def bootstrap_string_type(model_root: UUID, state: State) -> UUID: - pass +if __name__ == '__main__': + from state.devstate import DevState as State + s = State() + bootstrap_scd(s) + r = s.read_root() + for n in s.read_dict_keys(r): + print(s.read_value(n)) diff --git a/services/bottom/V0.py b/services/bottom/V0.py index c17b9f8..fd44962 100644 --- a/services/bottom/V0.py +++ b/services/bottom/V0.py @@ -15,7 +15,10 @@ class Bottom(Service): return self.state.create_nodevalue(value) def create_edge(self, source: UUID, target: UUID, label=None): - pass + if label is None: + return self.state.create_edge(source, target) + else: + return self.state.create_dict(source, label, target) def read_model_root(self) -> UUID: return self.model From 9b3a4c5a44b188db717ea788d88f48fafbceda6d Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sat, 14 Aug 2021 16:31:56 +0200 Subject: [PATCH 04/17] Defined SCD services interface --- bootstrap/primitive.py | 21 ++++++++++++++++++ services/scd.py | 48 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 bootstrap/primitive.py create mode 100644 services/scd.py diff --git a/bootstrap/primitive.py b/bootstrap/primitive.py new file mode 100644 index 0000000..89e4fd8 --- /dev/null +++ b/bootstrap/primitive.py @@ -0,0 +1,21 @@ +from state.base import State, UUID + + +def bootstrap_type_type(model_root: UUID, state: State) -> UUID: + pass + + +def bootstrap_boolean_type(model_root: UUID, state: State) -> UUID: + pass + + +def bootstrap_integer_type(model_root: UUID, state: State) -> UUID: + pass + + +def bootstrap_float_type(model_root: UUID, state: State) -> UUID: + pass + + +def bootstrap_string_type(model_root: UUID, state: State) -> UUID: + pass diff --git a/services/scd.py b/services/scd.py new file mode 100644 index 0000000..9cab2b9 --- /dev/null +++ b/services/scd.py @@ -0,0 +1,48 @@ +from services.base import Service, UUID +from state.base import State +from typing import Any, List + + +class SCD(Service): + def __init__(self, model: UUID, state: State): + super().__init__(model) + self.state = state + + def create_class(self, name: str, abstract: bool, min_c: int, max_c: int): + pass + + def create_association(self, name: str, source: str, target: str, src_min_c: int, src_max_c: int, tgt_min_c: int, tgt_max_c: int): + pass + + def create_global_constraint(self, name: str): + pass + + def create_attribute(self, name: str): + pass + + def create_attribute_link(self, source: str, target: str, name: str, optional: bool): + pass + + def create_model_ref(self, name: str, model: UUID): + pass + + def create_inheritance(self, child: str, parent: str): + pass + + def add_constraint(self, element: str, code: str): + pass + + def list_elements(self): + pass + + def delete_element(self, name: str): + pass + + def update_class(self, name: str, abstract: bool, min_c: int, max_c: int): + pass + + def update_association(self, name: str, src_min_c: int, src_max_c: int, tgt_min_c: int, tgt_max_c: int): + pass + + def update_attribute_link(self, name: str, attribute_name: str, optional: bool): + pass From f9aa6a8adf313c81107135186e5291b619507756 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Mon, 16 Aug 2021 07:57:17 +0200 Subject: [PATCH 05/17] Refactored some stuff --- bootstrap/scd.py | 60 +++++++++++++++---------------------------- services/base.py | 7 ----- services/bottom/V0.py | 24 ++++++++++++----- 3 files changed, 37 insertions(+), 54 deletions(-) delete mode 100644 services/base.py diff --git a/bootstrap/scd.py b/bootstrap/scd.py index 6ad102a..0698fca 100644 --- a/bootstrap/scd.py +++ b/bootstrap/scd.py @@ -5,16 +5,14 @@ from services.bottom.V0 import Bottom def create_model_root(bottom: Bottom, model_name: str) -> UUID: model_root = bottom.create_node() mcl_root_id = bottom.create_node(value=str(model_root)) - bottom.create_edge(bottom.model, mcl_root_id, label=model_name) + bottom.create_edge(bottom.state.read_root(), mcl_root_id, label=model_name) return model_root def bootstrap_scd(state: State) -> UUID: # init model roots and store their UUIDs attached to state root - state_root = state.read_root() - bottom = Bottom(state_root, state) + bottom = Bottom(state) mcl_root = create_model_root(bottom, "SCD") - mcl_morphism_root = create_model_root(bottom, "phi(SCD,SCD)") # Create model roots for primitive types integer_type_root = create_model_root(bottom, "Integer") @@ -24,18 +22,17 @@ def bootstrap_scd(state: State) -> UUID: type_type_root = create_model_root(bottom, "Type") # create MCL, without morphism links - bottom = Bottom(mcl_root, state) def add_node_element(element_name, node_value=None): """ Helper function, adds node to model with given name and value """ _node = bottom.create_node(value=node_value) - bottom.create_edge(bottom.model, _node, element_name) + bottom.create_edge(mcl_root, _node, element_name) return _node def add_edge_element(element_name, source, target): """ Helper function, adds edge to model with given name """ _edge = bottom.create_edge(source, target) - bottom.create_edge(bottom.model, _edge, element_name) + bottom.create_edge(mcl_root, _edge, element_name) return _edge def add_attribute_attributes(attribute_element_name, attribute_element, _name, _optional): @@ -65,15 +62,15 @@ def bootstrap_scd(state: State) -> UUID: attr_link_edge = add_edge_element("AttributeLink", element_node, attr_node) # # INHERITANCES, i.e. elements typed by Inheritance # # Class inherits from Element - class_inh_element_edge = add_edge_element("class_inh_element", class_node, element_node) + add_edge_element("class_inh_element", class_node, element_node) # # Attribute inherits from Element - attr_inh_element_edge = add_edge_element("attr_inh_element", attr_node, element_node) + add_edge_element("attr_inh_element", attr_node, element_node) # # Association inherits from Element - assoc_inh_element_edge = add_edge_element("assoc_inh_element", assoc_edge, element_node) + add_edge_element("assoc_inh_element", assoc_edge, element_node) # # AttributeLink inherits from Element - attr_link_inh_element_edge = add_edge_element("attr_link_inh_element", attr_link_edge, element_node) + add_edge_element("attr_link_inh_element", attr_link_edge, element_node) # # ModelRef inherits from Attribute - model_ref_inh_attr_edge = add_edge_element("model_ref_inh_attr", model_ref_node, attr_node) + add_edge_element("model_ref_inh_attr", model_ref_node, attr_node) # # ATTRIBUTES, i.e. elements typed by Attribute # # Action Code # TODO: Update to ModelRef when action code is explicitly modelled action_code_node = add_node_element("ActionCode") @@ -103,38 +100,27 @@ def bootstrap_scd(state: State) -> UUID: assoc_t_u_c_edge = add_edge_element("Association_target_upper_cardinality", assoc_edge, integer_node) # # ATTRIBUTE ATTRIBUTES, assign 'name' and 'optional' attributes to all AttributeLinks # # AttributeLink_name - attr_name_name_node, attr_name_name_edge, attr_name_optional_node, attr_name_optional_edge = \ - add_attribute_attributes("AttributeLink_name", attr_name_edge, "name", False) + add_attribute_attributes("AttributeLink_name", attr_name_edge, "name", False) # # AttributeLink_opt - attr_opt_name_node, attr_opt_name_edge, attr_opt_optional_node, attr_opt_optional_edge = \ - add_attribute_attributes("AttributeLink_optional", attr_opt_edge, "optional", False) + add_attribute_attributes("AttributeLink_optional", attr_opt_edge, "optional", False) # # Element_constraint - elem_constr_name_node, elem_constr_name_edge, elem_constr_optional_node, elem_constr_optional_edge = \ - add_attribute_attributes("Element_constraint", elem_constr_edge, "constraint", True) + add_attribute_attributes("Element_constraint", elem_constr_edge, "constraint", True) # # Class_abstract - class_abs_name_node, class_abs_name_edge, class_abs_optional_node, class_abs_optional_edge = \ - add_attribute_attributes("Class_abstract", class_abs_edge, "abstract", True) + add_attribute_attributes("Class_abstract", class_abs_edge, "abstract", True) # # Class_lower_cardinality - class_l_c_name_node, class_l_c_name_edge, class_l_c_optional_node, class_l_c_optional_edge = \ - add_attribute_attributes("Class_lower_cardinality", class_l_c_edge, "lower_cardinality", True) + add_attribute_attributes("Class_lower_cardinality", class_l_c_edge, "lower_cardinality", True) # # Class_upper_cardinality - class_u_c_name_node, class_u_c_name_edge, class_u_c_optional_node, class_u_c_optional_edge = \ - add_attribute_attributes("Class_upper_cardinality", class_u_c_edge, "upper_cardinality", True) + add_attribute_attributes("Class_upper_cardinality", class_u_c_edge, "upper_cardinality", True) # # Association_source_lower_cardinality - assoc_s_l_c_name_node, assoc_s_l_c_name_edge, assoc_s_l_c_optional_node, assoc_s_l_c_optional_edge = \ - add_attribute_attributes("Association_source_lower_cardinality", assoc_s_l_c_edge, "source_lower_cardinality", True) + add_attribute_attributes("Association_source_lower_cardinality", assoc_s_l_c_edge, "source_lower_cardinality", True) # # Association_source_upper_cardinality - assoc_s_u_c_name_node, assoc_s_u_c_name_edge, assoc_s_u_c_optional_node, assoc_s_u_c_optional_edge = \ - add_attribute_attributes("Association_source_upper_cardinality", assoc_s_u_c_edge, "source_upper_cardinality", True) + add_attribute_attributes("Association_source_upper_cardinality", assoc_s_u_c_edge, "source_upper_cardinality", True) # # Association_target_lower_cardinality - assoc_t_l_c_name_node, assoc_t_l_c_name_edge, assoc_t_l_c_optional_node, assoc_t_l_c_optional_edge = \ - add_attribute_attributes("Association_target_lower_cardinality", assoc_t_l_c_edge, "target_lower_cardinality", True) + add_attribute_attributes("Association_target_lower_cardinality", assoc_t_l_c_edge, "target_lower_cardinality", True) # # Association_target_upper_cardinality - assoc_t_u_c_name_node, assoc_t_u_c_name_edge, assoc_t_u_c_optional_node, assoc_t_u_c_optional_edge = \ - add_attribute_attributes("Association_target_upper_cardinality", assoc_t_u_c_edge, "target_upper_cardinality", True) + add_attribute_attributes("Association_target_upper_cardinality", assoc_t_u_c_edge, "target_upper_cardinality", True) # create phi(SCD,SCD) to type MCL with itself - bottom.model = mcl_morphism_root def add_mcl_morphism(element_name, type_name): # get elements from mcl by name @@ -142,14 +128,8 @@ def bootstrap_scd(state: State) -> UUID: _element_node = bottom.read_edge_target(_element_edge) _type_edge, = bottom.read_outgoing_edges(mcl_root, type_name) _type_node = bottom.read_edge_target(_type_edge) - # add elements to morphism model - if element_name not in bottom.read_keys(bottom.model): - bottom.create_edge(bottom.model, _element_node, element_name) - if type_name not in bottom.read_keys(bottom.model): - bottom.create_edge(bottom.model, _type_node, type_name) # create morphism link - morphism_edge = bottom.create_edge(_element_node, _type_node) - bottom.create_edge(bottom.model, morphism_edge, f"{element_name}_is_a_{type_name}") + bottom.create_edge(_element_node, _type_node, "Morphism") # Class add_mcl_morphism("Element", "Class") diff --git a/services/base.py b/services/base.py deleted file mode 100644 index 651607e..0000000 --- a/services/base.py +++ /dev/null @@ -1,7 +0,0 @@ -from abc import ABC, abstractmethod -from uuid import UUID - - -class Service(ABC): - def __init__(self, model: UUID): - self.model = model diff --git a/services/bottom/V0.py b/services/bottom/V0.py index fd44962..52c4ec3 100644 --- a/services/bottom/V0.py +++ b/services/bottom/V0.py @@ -1,11 +1,10 @@ -from services.base import Service, UUID +from uuid import UUID from state.base import State from typing import Any, List -class Bottom(Service): - def __init__(self, model: UUID, state: State): - super().__init__(model) +class Bottom: + def __init__(self, state: State): self.state = state def create_node(self, value=None) -> UUID: @@ -20,9 +19,6 @@ class Bottom(Service): else: return self.state.create_dict(source, label, target) - def read_model_root(self) -> UUID: - return self.model - def read_value(self, node: UUID) -> Any: return self.state.read_value(node) @@ -68,6 +64,20 @@ class Bottom(Service): edges = [e for e in edges if read_label(e) == label] return edges + def read_incoming_nodes(self, target: UUID, label=None) -> List[UUID]: + edges = self.read_incoming_edges(target, label) + if edges is None or len(edges) == 0: + return [] + else: + return [self.read_edge_source(e) for e in edges] + + def read_outgoing_nodes(self, source: UUID, label=None) -> List[UUID]: + edges = self.read_outgoing_edges(source, label) + if edges is None or len(edges) == 0: + return [] + else: + return [self.read_edge_target(e) for e in edges] + def read_keys(self, element: UUID) -> List[str]: key_nodes = self.state.read_dict_keys(element) unique_keys = {self.state.read_value(node) for node in key_nodes} From 6dcbdbe7758ef62b5aa53ec15efa97d111d77fff Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Mon, 16 Aug 2021 09:04:09 +0200 Subject: [PATCH 06/17] Primitives are now bootstrapped as well --- bootstrap/primitive.py | 57 ++++++++++++++++++++++++++++++++++-------- bootstrap/scd.py | 14 +++++++++++ 2 files changed, 61 insertions(+), 10 deletions(-) diff --git a/bootstrap/primitive.py b/bootstrap/primitive.py index 89e4fd8..bf286d4 100644 --- a/bootstrap/primitive.py +++ b/bootstrap/primitive.py @@ -1,21 +1,58 @@ from state.base import State, UUID +from services.bottom.V0 import Bottom -def bootstrap_type_type(model_root: UUID, state: State) -> UUID: - pass +def bootstrap_type(type_name: str, python_type: str, scd_root: UUID, model_root: UUID, state: State): + bottom = Bottom(state) + # create class + class_node = bottom.create_node() # create class node + bottom.create_edge(model_root, class_node, type_name) # attach to model + scd_node, = bottom.read_outgoing_nodes(scd_root, "Class") # retrieve type + bottom.create_edge(class_node, scd_node, "Morphism") # create morphism link + # set min_cardinality + min_c_node = bottom.create_node(1) + bottom.create_edge(model_root, min_c_node, f"{type_name}.lower_cardinality") + min_c_link = bottom.create_edge(class_node, min_c_node) + bottom.create_edge(model_root, min_c_link, f"{type_name}.lower_cardinality_link") + scd_node, = bottom.read_outgoing_nodes(scd_root, "Integer") + scd_link, = bottom.read_outgoing_nodes(scd_root, "Class_lower_cardinality") + bottom.create_edge(min_c_node, scd_node, "Morphism") + bottom.create_edge(min_c_link, scd_link, "Morphism") + # set max_cardinality + max_c_node = bottom.create_node(1) + bottom.create_edge(model_root, max_c_node, f"{type_name}.upper_cardinality") + max_c_link = bottom.create_edge(class_node, max_c_node) + bottom.create_edge(model_root, max_c_link, f"{type_name}.upper_cardinality_link") + scd_node, = bottom.read_outgoing_nodes(scd_root, "Integer") + scd_link, = bottom.read_outgoing_nodes(scd_root, "Class_upper_cardinality") + bottom.create_edge(max_c_node, scd_node, "Morphism") + bottom.create_edge(max_c_link, scd_link, "Morphism") + # set constraint + constraint_node = bottom.create_node(f"isinstance(read_value(x),{python_type})") + bottom.create_edge(model_root, constraint_node, f"{type_name}.constraint") + constraint_link = bottom.create_edge(class_node, constraint_node) + bottom.create_edge(model_root, constraint_link, f"{type_name}.constraint_link") + scd_node, = bottom.read_outgoing_nodes(scd_root, "ActionCode") + scd_link, = bottom.read_outgoing_nodes(scd_root, "Element_constraint") + bottom.create_edge(constraint_node, scd_node, "Morphism") + bottom.create_edge(constraint_link, scd_link, "Morphism") + + +def bootstrap_type_type(scd_root: UUID, model_root: UUID, state: State): + bootstrap_type("Type", "tuple", scd_root, model_root, state) -def bootstrap_boolean_type(model_root: UUID, state: State) -> UUID: - pass +def bootstrap_boolean_type(scd_root: UUID, model_root: UUID, state: State): + bootstrap_type("Boolean", "bool", scd_root, model_root, state) -def bootstrap_integer_type(model_root: UUID, state: State) -> UUID: - pass +def bootstrap_integer_type(scd_root: UUID, model_root: UUID, state: State): + bootstrap_type("Integer", "int", scd_root, model_root, state) -def bootstrap_float_type(model_root: UUID, state: State) -> UUID: - pass +def bootstrap_float_type(scd_root: UUID, model_root: UUID, state: State): + bootstrap_type("Float", "float", scd_root, model_root, state) -def bootstrap_string_type(model_root: UUID, state: State) -> UUID: - pass +def bootstrap_string_type(scd_root: UUID, model_root: UUID, state: State): + bootstrap_type("String", "str", scd_root, model_root, state) diff --git a/bootstrap/scd.py b/bootstrap/scd.py index 0698fca..3ebcc51 100644 --- a/bootstrap/scd.py +++ b/bootstrap/scd.py @@ -1,5 +1,12 @@ from state.base import State, UUID from services.bottom.V0 import Bottom +from bootstrap.primitive import ( + bootstrap_boolean_type, + bootstrap_float_type, + bootstrap_integer_type, + bootstrap_string_type, + bootstrap_type_type +) def create_model_root(bottom: Bottom, model_name: str) -> UUID: @@ -209,6 +216,13 @@ def bootstrap_scd(state: State) -> UUID: add_mcl_morphism("Association_target_lower_cardinality.optional", "Boolean") add_mcl_morphism("Association_target_upper_cardinality.optional", "Boolean") + # bootstrap primitive types + bootstrap_boolean_type(mcl_root, boolean_type_root, state) + bootstrap_float_type(mcl_root, float_type_root, state) + bootstrap_integer_type(mcl_root, integer_type_root, state) + bootstrap_string_type(mcl_root, string_type_root, state) + bootstrap_type_type(mcl_root, type_type_root, state) + return mcl_root From 3e2d0fb96a321fc043948f267a1ef9bee94d748e Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Mon, 16 Aug 2021 09:49:35 +0200 Subject: [PATCH 07/17] Primitive type services --- services/primitives/__init__.py | 0 services/primitives/boolean_type.py | 19 +++++++++++++++++++ services/primitives/float_type.py | 19 +++++++++++++++++++ services/primitives/integer_type.py | 19 +++++++++++++++++++ services/primitives/string_type.py | 19 +++++++++++++++++++ services/primitives/type_type.py | 19 +++++++++++++++++++ 6 files changed, 95 insertions(+) create mode 100644 services/primitives/__init__.py create mode 100644 services/primitives/boolean_type.py create mode 100644 services/primitives/float_type.py create mode 100644 services/primitives/integer_type.py create mode 100644 services/primitives/string_type.py create mode 100644 services/primitives/type_type.py diff --git a/services/primitives/__init__.py b/services/primitives/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services/primitives/boolean_type.py b/services/primitives/boolean_type.py new file mode 100644 index 0000000..52826a6 --- /dev/null +++ b/services/primitives/boolean_type.py @@ -0,0 +1,19 @@ +from uuid import UUID +from state.base import State +from services.bottom.V0 import Bottom + + +class Boolean: + def __init__(self, model: UUID, state: State): + self.model = model + self.bottom = Bottom(state) + type_model_id_node, = self.bottom.read_outgoing_nodes(state.read_root(), "Boolean") + self.type_model = UUID(self.bottom.read_value(type_model_id_node)) + + def create(self, value: bool): + if "boolean" in self.bottom.read_keys(self.model): + instance, = self.bottom.read_outgoing_nodes(self.model, "boolean") + self.bottom.delete_element(instance) + _instance = self.bottom.create_edge(self.model, self.bottom.create_node(value), "boolean") + _type, = self.bottom.read_outgoing_nodes(self.type_model, "Boolean") + self.bottom.create_edge(_instance, _type, "Morphism") diff --git a/services/primitives/float_type.py b/services/primitives/float_type.py new file mode 100644 index 0000000..28e223d --- /dev/null +++ b/services/primitives/float_type.py @@ -0,0 +1,19 @@ +from uuid import UUID +from state.base import State +from services.bottom.V0 import Bottom + + +class Float: + def __init__(self, model: UUID, state: State): + self.model = model + self.bottom = Bottom(state) + type_model_id_node, = self.bottom.read_outgoing_nodes(state.read_root(), "Float") + self.type_model = UUID(self.bottom.read_value(type_model_id_node)) + + def create(self, value: float): + if "float" in self.bottom.read_keys(self.model): + instance, = self.bottom.read_outgoing_nodes(self.model, "float") + self.bottom.delete_element(instance) + _instance = self.bottom.create_edge(self.model, self.bottom.create_node(value), "float") + _type, = self.bottom.read_outgoing_nodes(self.type_model, "Float") + self.bottom.create_edge(_instance, _type, "Morphism") diff --git a/services/primitives/integer_type.py b/services/primitives/integer_type.py new file mode 100644 index 0000000..add224a --- /dev/null +++ b/services/primitives/integer_type.py @@ -0,0 +1,19 @@ +from uuid import UUID +from state.base import State +from services.bottom.V0 import Bottom + + +class Integer: + def __init__(self, model: UUID, state: State): + self.model = model + self.bottom = Bottom(state) + type_model_id_node, = self.bottom.read_outgoing_nodes(state.read_root(), "Integer") + self.type_model = UUID(self.bottom.read_value(type_model_id_node)) + + def create(self, value: int): + if "string" in self.bottom.read_keys(self.model): + instance, = self.bottom.read_outgoing_nodes(self.model, "integer") + self.bottom.delete_element(instance) + _instance = self.bottom.create_edge(self.model, self.bottom.create_node(value), "integer") + _type, = self.bottom.read_outgoing_nodes(self.type_model, "Integer") + self.bottom.create_edge(_instance, _type, "Morphism") diff --git a/services/primitives/string_type.py b/services/primitives/string_type.py new file mode 100644 index 0000000..c95518a --- /dev/null +++ b/services/primitives/string_type.py @@ -0,0 +1,19 @@ +from uuid import UUID +from state.base import State +from services.bottom.V0 import Bottom + + +class String: + def __init__(self, model: UUID, state: State): + self.model = model + self.bottom = Bottom(state) + type_model_id_node, = self.bottom.read_outgoing_nodes(state.read_root(), "String") + self.type_model = UUID(self.bottom.read_value(type_model_id_node)) + + def create(self, value: str): + if "string" in self.bottom.read_keys(self.model): + instance, = self.bottom.read_outgoing_nodes(self.model, "string") + self.bottom.delete_element(instance) + _instance = self.bottom.create_edge(self.model, self.bottom.create_node(value), "string") + _type, = self.bottom.read_outgoing_nodes(self.type_model, "String") + self.bottom.create_edge(_instance, _type, "Morphism") diff --git a/services/primitives/type_type.py b/services/primitives/type_type.py new file mode 100644 index 0000000..bdb8a1d --- /dev/null +++ b/services/primitives/type_type.py @@ -0,0 +1,19 @@ +from uuid import UUID +from state.base import State +from services.bottom.V0 import Bottom + + +class Integer: + def __init__(self, model: UUID, state: State): + self.model = model + self.bottom = Bottom(state) + type_model_id_node, = self.bottom.read_outgoing_nodes(state.read_root(), "Type") + self.type_model = UUID(self.bottom.read_value(type_model_id_node)) + + def create(self, value: tuple): + if "string" in self.bottom.read_keys(self.model): + instance, = self.bottom.read_outgoing_nodes(self.model, "type") + self.bottom.delete_element(instance) + _instance = self.bottom.create_edge(self.model, self.bottom.create_node(value), "type") + _type, = self.bottom.read_outgoing_nodes(self.type_model, "Type") + self.bottom.create_edge(_instance, _type, "Morphism") From 938752ffe44a8e363d988ecedbdb23b3ab6d8be0 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Mon, 16 Aug 2021 09:53:45 +0200 Subject: [PATCH 08/17] REfctored name --- bootstrap/primitive.py | 14 +++++++------- services/bottom/V0.py | 4 ++-- services/primitives/boolean_type.py | 6 +++--- services/primitives/float_type.py | 6 +++--- services/primitives/integer_type.py | 6 +++--- services/primitives/string_type.py | 6 +++--- services/primitives/type_type.py | 6 +++--- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/bootstrap/primitive.py b/bootstrap/primitive.py index bf286d4..5500b6c 100644 --- a/bootstrap/primitive.py +++ b/bootstrap/primitive.py @@ -7,15 +7,15 @@ def bootstrap_type(type_name: str, python_type: str, scd_root: UUID, model_root: # create class class_node = bottom.create_node() # create class node bottom.create_edge(model_root, class_node, type_name) # attach to model - scd_node, = bottom.read_outgoing_nodes(scd_root, "Class") # retrieve type + scd_node, = bottom.read_outgoing_elements(scd_root, "Class") # retrieve type bottom.create_edge(class_node, scd_node, "Morphism") # create morphism link # set min_cardinality min_c_node = bottom.create_node(1) bottom.create_edge(model_root, min_c_node, f"{type_name}.lower_cardinality") min_c_link = bottom.create_edge(class_node, min_c_node) bottom.create_edge(model_root, min_c_link, f"{type_name}.lower_cardinality_link") - scd_node, = bottom.read_outgoing_nodes(scd_root, "Integer") - scd_link, = bottom.read_outgoing_nodes(scd_root, "Class_lower_cardinality") + scd_node, = bottom.read_outgoing_elements(scd_root, "Integer") + scd_link, = bottom.read_outgoing_elements(scd_root, "Class_lower_cardinality") bottom.create_edge(min_c_node, scd_node, "Morphism") bottom.create_edge(min_c_link, scd_link, "Morphism") # set max_cardinality @@ -23,8 +23,8 @@ def bootstrap_type(type_name: str, python_type: str, scd_root: UUID, model_root: bottom.create_edge(model_root, max_c_node, f"{type_name}.upper_cardinality") max_c_link = bottom.create_edge(class_node, max_c_node) bottom.create_edge(model_root, max_c_link, f"{type_name}.upper_cardinality_link") - scd_node, = bottom.read_outgoing_nodes(scd_root, "Integer") - scd_link, = bottom.read_outgoing_nodes(scd_root, "Class_upper_cardinality") + scd_node, = bottom.read_outgoing_elements(scd_root, "Integer") + scd_link, = bottom.read_outgoing_elements(scd_root, "Class_upper_cardinality") bottom.create_edge(max_c_node, scd_node, "Morphism") bottom.create_edge(max_c_link, scd_link, "Morphism") # set constraint @@ -32,8 +32,8 @@ def bootstrap_type(type_name: str, python_type: str, scd_root: UUID, model_root: bottom.create_edge(model_root, constraint_node, f"{type_name}.constraint") constraint_link = bottom.create_edge(class_node, constraint_node) bottom.create_edge(model_root, constraint_link, f"{type_name}.constraint_link") - scd_node, = bottom.read_outgoing_nodes(scd_root, "ActionCode") - scd_link, = bottom.read_outgoing_nodes(scd_root, "Element_constraint") + scd_node, = bottom.read_outgoing_elements(scd_root, "ActionCode") + scd_link, = bottom.read_outgoing_elements(scd_root, "Element_constraint") bottom.create_edge(constraint_node, scd_node, "Morphism") bottom.create_edge(constraint_link, scd_link, "Morphism") diff --git a/services/bottom/V0.py b/services/bottom/V0.py index 52c4ec3..cfa32eb 100644 --- a/services/bottom/V0.py +++ b/services/bottom/V0.py @@ -64,14 +64,14 @@ class Bottom: edges = [e for e in edges if read_label(e) == label] return edges - def read_incoming_nodes(self, target: UUID, label=None) -> List[UUID]: + def read_incoming_elements(self, target: UUID, label=None) -> List[UUID]: edges = self.read_incoming_edges(target, label) if edges is None or len(edges) == 0: return [] else: return [self.read_edge_source(e) for e in edges] - def read_outgoing_nodes(self, source: UUID, label=None) -> List[UUID]: + def read_outgoing_elements(self, source: UUID, label=None) -> List[UUID]: edges = self.read_outgoing_edges(source, label) if edges is None or len(edges) == 0: return [] diff --git a/services/primitives/boolean_type.py b/services/primitives/boolean_type.py index 52826a6..9ab1852 100644 --- a/services/primitives/boolean_type.py +++ b/services/primitives/boolean_type.py @@ -7,13 +7,13 @@ class Boolean: def __init__(self, model: UUID, state: State): self.model = model self.bottom = Bottom(state) - type_model_id_node, = self.bottom.read_outgoing_nodes(state.read_root(), "Boolean") + type_model_id_node, = self.bottom.read_outgoing_elements(state.read_root(), "Boolean") self.type_model = UUID(self.bottom.read_value(type_model_id_node)) def create(self, value: bool): if "boolean" in self.bottom.read_keys(self.model): - instance, = self.bottom.read_outgoing_nodes(self.model, "boolean") + instance, = self.bottom.read_outgoing_elements(self.model, "boolean") self.bottom.delete_element(instance) _instance = self.bottom.create_edge(self.model, self.bottom.create_node(value), "boolean") - _type, = self.bottom.read_outgoing_nodes(self.type_model, "Boolean") + _type, = self.bottom.read_outgoing_elements(self.type_model, "Boolean") self.bottom.create_edge(_instance, _type, "Morphism") diff --git a/services/primitives/float_type.py b/services/primitives/float_type.py index 28e223d..3d92a20 100644 --- a/services/primitives/float_type.py +++ b/services/primitives/float_type.py @@ -7,13 +7,13 @@ class Float: def __init__(self, model: UUID, state: State): self.model = model self.bottom = Bottom(state) - type_model_id_node, = self.bottom.read_outgoing_nodes(state.read_root(), "Float") + type_model_id_node, = self.bottom.read_outgoing_elements(state.read_root(), "Float") self.type_model = UUID(self.bottom.read_value(type_model_id_node)) def create(self, value: float): if "float" in self.bottom.read_keys(self.model): - instance, = self.bottom.read_outgoing_nodes(self.model, "float") + instance, = self.bottom.read_outgoing_elements(self.model, "float") self.bottom.delete_element(instance) _instance = self.bottom.create_edge(self.model, self.bottom.create_node(value), "float") - _type, = self.bottom.read_outgoing_nodes(self.type_model, "Float") + _type, = self.bottom.read_outgoing_elements(self.type_model, "Float") self.bottom.create_edge(_instance, _type, "Morphism") diff --git a/services/primitives/integer_type.py b/services/primitives/integer_type.py index add224a..87ebd7d 100644 --- a/services/primitives/integer_type.py +++ b/services/primitives/integer_type.py @@ -7,13 +7,13 @@ class Integer: def __init__(self, model: UUID, state: State): self.model = model self.bottom = Bottom(state) - type_model_id_node, = self.bottom.read_outgoing_nodes(state.read_root(), "Integer") + type_model_id_node, = self.bottom.read_outgoing_elements(state.read_root(), "Integer") self.type_model = UUID(self.bottom.read_value(type_model_id_node)) def create(self, value: int): if "string" in self.bottom.read_keys(self.model): - instance, = self.bottom.read_outgoing_nodes(self.model, "integer") + instance, = self.bottom.read_outgoing_elements(self.model, "integer") self.bottom.delete_element(instance) _instance = self.bottom.create_edge(self.model, self.bottom.create_node(value), "integer") - _type, = self.bottom.read_outgoing_nodes(self.type_model, "Integer") + _type, = self.bottom.read_outgoing_elements(self.type_model, "Integer") self.bottom.create_edge(_instance, _type, "Morphism") diff --git a/services/primitives/string_type.py b/services/primitives/string_type.py index c95518a..e1a5a87 100644 --- a/services/primitives/string_type.py +++ b/services/primitives/string_type.py @@ -7,13 +7,13 @@ class String: def __init__(self, model: UUID, state: State): self.model = model self.bottom = Bottom(state) - type_model_id_node, = self.bottom.read_outgoing_nodes(state.read_root(), "String") + type_model_id_node, = self.bottom.read_outgoing_elements(state.read_root(), "String") self.type_model = UUID(self.bottom.read_value(type_model_id_node)) def create(self, value: str): if "string" in self.bottom.read_keys(self.model): - instance, = self.bottom.read_outgoing_nodes(self.model, "string") + instance, = self.bottom.read_outgoing_elements(self.model, "string") self.bottom.delete_element(instance) _instance = self.bottom.create_edge(self.model, self.bottom.create_node(value), "string") - _type, = self.bottom.read_outgoing_nodes(self.type_model, "String") + _type, = self.bottom.read_outgoing_elements(self.type_model, "String") self.bottom.create_edge(_instance, _type, "Morphism") diff --git a/services/primitives/type_type.py b/services/primitives/type_type.py index bdb8a1d..e7031c2 100644 --- a/services/primitives/type_type.py +++ b/services/primitives/type_type.py @@ -7,13 +7,13 @@ class Integer: def __init__(self, model: UUID, state: State): self.model = model self.bottom = Bottom(state) - type_model_id_node, = self.bottom.read_outgoing_nodes(state.read_root(), "Type") + type_model_id_node, = self.bottom.read_outgoing_elements(state.read_root(), "Type") self.type_model = UUID(self.bottom.read_value(type_model_id_node)) def create(self, value: tuple): if "string" in self.bottom.read_keys(self.model): - instance, = self.bottom.read_outgoing_nodes(self.model, "type") + instance, = self.bottom.read_outgoing_elements(self.model, "type") self.bottom.delete_element(instance) _instance = self.bottom.create_edge(self.model, self.bottom.create_node(value), "type") - _type, = self.bottom.read_outgoing_nodes(self.type_model, "Type") + _type, = self.bottom.read_outgoing_elements(self.type_model, "Type") self.bottom.create_edge(_instance, _type, "Morphism") From afc3df071b80ebb4ed6390c3c55f4fece34524d0 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Mon, 16 Aug 2021 10:48:09 +0200 Subject: [PATCH 09/17] primitive models are now used corerctly --- bootstrap/scd.py | 64 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/bootstrap/scd.py b/bootstrap/scd.py index 3ebcc51..f16af18 100644 --- a/bootstrap/scd.py +++ b/bootstrap/scd.py @@ -1,5 +1,7 @@ from state.base import State, UUID from services.bottom.V0 import Bottom +from services.primitives.boolean_type import Boolean +from services.primitives.string_type import String from bootstrap.primitive import ( bootstrap_boolean_type, bootstrap_float_type, @@ -42,12 +44,14 @@ def bootstrap_scd(state: State) -> UUID: bottom.create_edge(mcl_root, _edge, element_name) return _edge - def add_attribute_attributes(attribute_element_name, attribute_element, _name, _optional): - _name_node = add_node_element(f"{attribute_element_name}.name", _name) + def add_attribute_attributes(attribute_element_name, attribute_element): + _name_model = bottom.create_node() + _name_node = add_node_element(f"{attribute_element_name}.name", str(_name_model)) _name_edge = add_edge_element(f"{attribute_element_name}.name_link", attribute_element, _name_node) - _optional_node = add_node_element(f"{attribute_element_name}.optional", _optional) + _optional_model = bottom.create_node() + _optional_node = add_node_element(f"{attribute_element_name}.optional", str(_optional_model)) _optional_edge = add_edge_element(f"{attribute_element_name}.optional_link", attribute_element, _optional_node) - return _name_node, _name_edge, _optional_node, _optional_edge + return _name_model, _optional_model # # CLASSES, i.e. elements typed by Class # # Element @@ -105,27 +109,53 @@ def bootstrap_scd(state: State) -> UUID: assoc_s_u_c_edge = add_edge_element("Association_source_upper_cardinality", assoc_edge, integer_node) assoc_t_l_c_edge = add_edge_element("Association_target_lower_cardinality", assoc_edge, integer_node) assoc_t_u_c_edge = add_edge_element("Association_target_upper_cardinality", assoc_edge, integer_node) + # # bootstrap primitive types + bootstrap_boolean_type(mcl_root, boolean_type_root, state) + bootstrap_float_type(mcl_root, float_type_root, state) + bootstrap_integer_type(mcl_root, integer_type_root, state) + bootstrap_string_type(mcl_root, string_type_root, state) + bootstrap_type_type(mcl_root, type_type_root, state) # # ATTRIBUTE ATTRIBUTES, assign 'name' and 'optional' attributes to all AttributeLinks # # AttributeLink_name - add_attribute_attributes("AttributeLink_name", attr_name_edge, "name", False) + m_name, m_opt = add_attribute_attributes("AttributeLink_name", attr_name_edge) + String(m_name, state).create("name") + Boolean(m_opt, state).create(False) # # AttributeLink_opt - add_attribute_attributes("AttributeLink_optional", attr_opt_edge, "optional", False) + m_name, m_opt = add_attribute_attributes("AttributeLink_optional", attr_opt_edge) + String(m_name, state).create("optional") + Boolean(m_opt, state).create(False) # # Element_constraint - add_attribute_attributes("Element_constraint", elem_constr_edge, "constraint", True) + m_name, m_opt = add_attribute_attributes("Element_constraint", elem_constr_edge) + String(m_name, state).create("constraint") + Boolean(m_opt, state).create(True) # # Class_abstract - add_attribute_attributes("Class_abstract", class_abs_edge, "abstract", True) + m_name, m_opt = add_attribute_attributes("Class_abstract", class_abs_edge) + String(m_name, state).create("abstract") + Boolean(m_opt, state).create(True) # # Class_lower_cardinality - add_attribute_attributes("Class_lower_cardinality", class_l_c_edge, "lower_cardinality", True) + m_name, m_opt = add_attribute_attributes("Class_lower_cardinality", class_l_c_edge) + String(m_name, state).create("lower_cardinality") + Boolean(m_opt, state).create(True) # # Class_upper_cardinality - add_attribute_attributes("Class_upper_cardinality", class_u_c_edge, "upper_cardinality", True) + m_name, m_opt = add_attribute_attributes("Class_upper_cardinality", class_u_c_edge) + String(m_name, state).create("upper_cardinality") + Boolean(m_opt, state).create(True) # # Association_source_lower_cardinality - add_attribute_attributes("Association_source_lower_cardinality", assoc_s_l_c_edge, "source_lower_cardinality", True) + m_name, m_opt = add_attribute_attributes("Association_source_lower_cardinality", assoc_s_l_c_edge) + String(m_name, state).create("source_lower_cardinality") + Boolean(m_opt, state).create(True) # # Association_source_upper_cardinality - add_attribute_attributes("Association_source_upper_cardinality", assoc_s_u_c_edge, "source_upper_cardinality", True) + m_name, m_opt = add_attribute_attributes("Association_source_upper_cardinality", assoc_s_u_c_edge) + String(m_name, state).create("source_upper_cardinality") + Boolean(m_opt, state).create(True) # # Association_target_lower_cardinality - add_attribute_attributes("Association_target_lower_cardinality", assoc_t_l_c_edge, "target_lower_cardinality", True) + m_name, m_opt = add_attribute_attributes("Association_target_lower_cardinality", assoc_t_l_c_edge) + String(m_name, state).create("target_lower_cardinality") + Boolean(m_opt, state).create(True) # # Association_target_upper_cardinality - add_attribute_attributes("Association_target_upper_cardinality", assoc_t_u_c_edge, "target_upper_cardinality", True) + m_name, m_opt = add_attribute_attributes("Association_target_upper_cardinality", assoc_t_u_c_edge) + String(m_name, state).create("target_upper_cardinality") + Boolean(m_opt, state).create(True) # create phi(SCD,SCD) to type MCL with itself @@ -216,12 +246,6 @@ def bootstrap_scd(state: State) -> UUID: add_mcl_morphism("Association_target_lower_cardinality.optional", "Boolean") add_mcl_morphism("Association_target_upper_cardinality.optional", "Boolean") - # bootstrap primitive types - bootstrap_boolean_type(mcl_root, boolean_type_root, state) - bootstrap_float_type(mcl_root, float_type_root, state) - bootstrap_integer_type(mcl_root, integer_type_root, state) - bootstrap_string_type(mcl_root, string_type_root, state) - bootstrap_type_type(mcl_root, type_type_root, state) return mcl_root From 967b17c45d0c391acc6d5936806aa6feb71c260b Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Mon, 16 Aug 2021 14:20:34 +0200 Subject: [PATCH 10/17] Implemented SCD services --- services/scd.py | 203 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 178 insertions(+), 25 deletions(-) diff --git a/services/scd.py b/services/scd.py index 9cab2b9..17ee5aa 100644 --- a/services/scd.py +++ b/services/scd.py @@ -1,48 +1,201 @@ -from services.base import Service, UUID +from uuid import UUID from state.base import State -from typing import Any, List +from services.bottom.V0 import Bottom +from services.primitives.boolean_type import Boolean +from services.primitives.integer_type import Integer +from services.primitives.string_type import String + +import re -class SCD(Service): - def __init__(self, model: UUID, state: State): - super().__init__(model) - self.state = state +class SCD: + def __init__(self, scd_model: UUID, model: UUID, state: State): + self.scd_model = scd_model + self.model = model + self.bottom = Bottom(state) - def create_class(self, name: str, abstract: bool, min_c: int, max_c: int): - pass + def create_class(self, name: str, abstract: bool = None, min_c: int = None, max_c: int = None): + + def set_cardinality(bound: str, value: int): + _c_model = self.bottom.create_node() + Integer(_c_model, self.bottom.state).create(value) + _c_node = self.bottom.create_node(str(_c_model)) + self.bottom.create_edge(self.model, _c_node, f"{name}.{bound}_cardinality") + _c_link = self.bottom.create_edge(class_node, _c_node) + self.bottom.create_edge(self.model, _c_link, f"{name}.{bound}_cardinality_link") + _scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Integer") + _scd_link, = self.bottom.read_outgoing_elements(self.scd_model, f"Class_{bound}_cardinality") + self.bottom.create_edge(_c_node, _scd_node, "Morphism") + self.bottom.create_edge(_c_link, _scd_link, "Morphism") + + # create class + attributes + morphism links + class_node = self.bottom.create_node() # create class node + self.bottom.create_edge(self.model, class_node, name) # attach to model + scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Class") # retrieve type + self.bottom.create_edge(class_node, scd_node, "Morphism") # create morphism link + if abstract is not None: + abstract_model = self.bottom.create_node() + Boolean(abstract_model, self.bottom.state).create(abstract) + abstract_node = self.bottom.create_node(str(abstract_model)) + self.bottom.create_edge(self.model, abstract_node, f"{name}.abstract") + abstract_link = self.bottom.create_edge(class_node, abstract_node) + self.bottom.create_edge(self.model, abstract_link, f"{name}.abstract_link") + scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Boolean") + scd_link, = self.bottom.read_outgoing_elements(self.scd_model, "Class_abstract") + self.bottom.create_edge(abstract_node, scd_node, "Morphism") + self.bottom.create_edge(abstract_link, scd_link, "Morphism") + if min_c is not None: + set_cardinality("lower", min_c) + if max_c is not None: + set_cardinality("upper", min_c) - def create_association(self, name: str, source: str, target: str, src_min_c: int, src_max_c: int, tgt_min_c: int, tgt_max_c: int): - pass + def create_association(self, name: str, source: str, target: str, + src_min_c: int = None, src_max_c: int = None, + tgt_min_c: int = None, tgt_max_c: int = None): + + def set_cardinality(bound: str, value: int): + _c_model = self.bottom.create_node() + Integer(_c_model, self.bottom.state).create(value) + _c_node = self.bottom.create_node(str(_c_model)) + self.bottom.create_edge(self.model, _c_node, f"{name}.{bound}_cardinality") + _c_link = self.bottom.create_edge(assoc_edge, _c_node) + self.bottom.create_edge(self.model, _c_link, f"{name}.{bound}_cardinality_link") + _scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Integer") + _scd_link, = self.bottom.read_outgoing_elements(self.scd_model, f"Class_{bound}_cardinality") + self.bottom.create_edge(_c_node, _scd_node, "Morphism") + self.bottom.create_edge(_c_link, _scd_link, "Morphism") + + # create class + attributes + morphism links + assoc_edge = self.bottom.create_edge( + *self.bottom.read_outgoing_elements(self.model, source), + *self.bottom.read_outgoing_elements(self.model, target), + ) # create assoc edge + self.bottom.create_edge(self.model, assoc_edge, name) # attach to model + scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Association") # retrieve type + self.bottom.create_edge(assoc_edge, scd_node, "Morphism") # create morphism link + if src_min_c is not None: + set_cardinality("source_lower", src_min_c) + if src_max_c is not None: + set_cardinality("source_upper", src_max_c) + if tgt_min_c is not None: + set_cardinality("target_lower", tgt_min_c) + if tgt_max_c is not None: + set_cardinality("target_upper", tgt_max_c) def create_global_constraint(self, name: str): - pass + # create element + morphism links + element_node = self.bottom.create_node() # create element node + self.bottom.create_edge(self.model, element_node, name) # attach to model + scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "GlobalConstraint") # retrieve type + self.bottom.create_edge(element_node, scd_node, "Morphism") # create morphism link def create_attribute(self, name: str): - pass + # create element + morphism links + element_node = self.bottom.create_node() # create element node + self.bottom.create_edge(self.model, element_node, name) # attach to model + scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Attribute") # retrieve type + self.bottom.create_edge(element_node, scd_node, "Morphism") # create morphism link def create_attribute_link(self, source: str, target: str, name: str, optional: bool): - pass + # create attribute link + morphism links + assoc_edge = self.bottom.create_edge( + *self.bottom.read_outgoing_elements(self.model, source), + *self.bottom.read_outgoing_elements(self.model, target), + ) # create inheritance edge + self.bottom.create_edge(self.model, assoc_edge, f"{source}_{name}") # attach to model + scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "AttributeLink") # retrieve type + self.bottom.create_edge(assoc_edge, scd_node, "Morphism") # create morphism link + # name attribute + 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"{source}_{name}.name") + name_link = self.bottom.create_edge(assoc_edge, name_node) + self.bottom.create_edge(self.model, name_link, f"{source}_{name}.name_link") + scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "String") + scd_link, = self.bottom.read_outgoing_elements(self.scd_model, "AttributeLink_name") + self.bottom.create_edge(name_node, scd_node, "Morphism") + self.bottom.create_edge(name_link, scd_link, "Morphism") + # optional attribute + optional_model = self.bottom.create_node() + Boolean(optional_model, self.bottom.state).create(optional) + optional_node = self.bottom.create_node(str(optional_model)) + self.bottom.create_edge(self.model, optional_node, f"{source}_{name}.optional") + optional_link = self.bottom.create_edge(assoc_edge, optional_node) + self.bottom.create_edge(self.model, optional_link, f"{source}_{name}.optional_link") + scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Boolean") + scd_link, = self.bottom.read_outgoing_elements(self.scd_model, "AttributeLink_optional") + self.bottom.create_edge(optional_node, scd_node, "Morphism") + self.bottom.create_edge(optional_link, scd_link, "Morphism") def create_model_ref(self, name: str, model: UUID): - pass + # create element + morphism links + element_node = self.bottom.create_node(str(model)) # create element node + self.bottom.create_edge(self.model, element_node, name) # attach to model + scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "ModelRef") # retrieve type + self.bottom.create_edge(element_node, scd_node, "Morphism") # create morphism link def create_inheritance(self, child: str, parent: str): - pass + # create inheritance + morphism links + assoc_edge = self.bottom.create_edge( + *self.bottom.read_outgoing_elements(self.model, child), + *self.bottom.read_outgoing_elements(self.model, parent), + ) # create inheritance edge + self.bottom.create_edge(self.model, assoc_edge, f"{child}_inh_{parent}") # attach to model + scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "Inheritance") # retrieve type + self.bottom.create_edge(assoc_edge, scd_node, "Morphism") # create morphism link def add_constraint(self, element: str, code: str): - pass + element_node, = self.bottom.read_outgoing_elements(self.model, element) # retrieve element + # code attribute + code_node = self.bottom.create_node(code) + self.bottom.create_edge(self.model, code_node, f"{element}.constraint") + code_link = self.bottom.create_edge(element_node, code_node) + self.bottom.create_edge(self.model, code_link, f"{element}.constraint_link") + scd_node, = self.bottom.read_outgoing_elements(self.scd_model, "ActionCode") + scd_link, = self.bottom.read_outgoing_elements(self.scd_model, "Element_constraint") + self.bottom.create_edge(code_node, scd_node, "Morphism") + self.bottom.create_edge(code_link, scd_link, "Morphism") def list_elements(self): - pass + scd_names = {} + for key in self.bottom.read_keys(self.scd_model): + element, = self.bottom.read_outgoing_elements(self.scd_model, key) + scd_names[element] = key + unsorted = [] + for key in self.bottom.read_keys(self.model): + element, = self.bottom.read_outgoing_elements(self.model, key) + element_types = self.bottom.read_outgoing_elements(element, "Morphism") + type_model_elements = self.bottom.read_outgoing_elements(self.scd_model) + element_type_node, = [e for e in element_types if e in type_model_elements] + unsorted.append((key, scd_names[element_type_node])) + for elem in sorted(unsorted, key=lambda e: e[0]): + print("{} : {}".format(*elem)) def delete_element(self, name: str): - pass + keys = self.bottom.read_keys(self.model) + r = re.compile(r"{}\..*".format(name)) + to_delete = list(filter(r.match, keys)) + for key in to_delete: + # TODO: find way to solve memory leak, primitive models are not deleted this way + node, = self.bottom.read_outgoing_elements(self.model, label=key) + self.bottom.delete_element(node) - def update_class(self, name: str, abstract: bool, min_c: int, max_c: int): - pass - def update_association(self, name: str, src_min_c: int, src_max_c: int, tgt_min_c: int, tgt_max_c: int): - pass - - def update_attribute_link(self, name: str, attribute_name: str, optional: bool): - pass +if __name__ == '__main__': + from state.devstate import DevState as State + s = State() + from bootstrap.scd import bootstrap_scd + scd = bootstrap_scd(s) + int_type_id = s.read_dict(s.read_root(), "Integer") + int_type = UUID(s.read_value(int_type_id)) + service = SCD(scd, s.create_node(), s) + service.create_class("Place") + service.create_class("Transition") + service.create_association("P2T", "Place", "Transition") + service.create_association("T2P", "Transition", "Place") + service.create_model_ref("Integer", int_type) + service.create_attribute_link("Place", "Integer", "tokens", False) + service.create_attribute_link("P2T", "Integer", "weight", False) + service.create_attribute_link("T2P", "Integer", "weight", False) + service.list_elements() From 8ddb5ac12be8f88833504c6848fa6524ac18133e Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Sat, 21 Aug 2021 11:57:20 +0200 Subject: [PATCH 11/17] PN example --- bootstrap/scd.py | 1 - services/scd.py | 35 ++++++++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/bootstrap/scd.py b/bootstrap/scd.py index f16af18..19e6c59 100644 --- a/bootstrap/scd.py +++ b/bootstrap/scd.py @@ -246,7 +246,6 @@ def bootstrap_scd(state: State) -> UUID: add_mcl_morphism("Association_target_lower_cardinality.optional", "Boolean") add_mcl_morphism("Association_target_upper_cardinality.optional", "Boolean") - return mcl_root diff --git a/services/scd.py b/services/scd.py index 17ee5aa..15a5290 100644 --- a/services/scd.py +++ b/services/scd.py @@ -187,15 +187,32 @@ if __name__ == '__main__': s = State() from bootstrap.scd import bootstrap_scd scd = bootstrap_scd(s) + # Retrieve refs to primitive type models + # # integer int_type_id = s.read_dict(s.read_root(), "Integer") int_type = UUID(s.read_value(int_type_id)) - service = SCD(scd, s.create_node(), s) - service.create_class("Place") - service.create_class("Transition") - service.create_association("P2T", "Place", "Transition") - service.create_association("T2P", "Transition", "Place") + print(f"Integer Model UUID: {int_type}") # 6 + # # string + str_type_id = s.read_dict(s.read_root(), "String") + str_type = UUID(s.read_value(str_type_id)) + print(f"String Model UUID: {str_type}") # 16 + # Create LTM_PN + model_uuid = s.create_node() + print(f"LTM_PN Model UUID: {model_uuid}") # 845 + service = SCD(scd, model_uuid, s) + # Create classes + service.create_class("P") + service.create_class("T") + # Create associations + service.create_association("P2T", "P", "T") + service.create_association("T2P", "T", "P") + # Create model refs service.create_model_ref("Integer", int_type) - service.create_attribute_link("Place", "Integer", "tokens", False) - service.create_attribute_link("P2T", "Integer", "weight", False) - service.create_attribute_link("T2P", "Integer", "weight", False) - service.list_elements() + service.create_model_ref("String", int_type) + # 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) + # Create association attributes + service.create_attribute_link("P2T", "Integer", "w", False) + service.create_attribute_link("T2P", "Integer", "w", False) From 3c1d1fa002da0a88b7731b4eb45a32f6f830eb49 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Mon, 23 Aug 2021 15:11:25 +0200 Subject: [PATCH 12/17] Working on conformance --- bootstrap/primitive.py | 11 +- bootstrap/scd.py | 11 +- framework/__init__.py | 0 framework/conformance.py | 358 ++++++++++++++++++++++++++++ services/primitives/boolean_type.py | 3 +- services/primitives/float_type.py | 3 +- services/primitives/integer_type.py | 3 +- services/primitives/string_type.py | 3 +- services/primitives/type_type.py | 3 +- 9 files changed, 386 insertions(+), 9 deletions(-) create mode 100644 framework/__init__.py create mode 100644 framework/conformance.py diff --git a/bootstrap/primitive.py b/bootstrap/primitive.py index 5500b6c..177c085 100644 --- a/bootstrap/primitive.py +++ b/bootstrap/primitive.py @@ -1,5 +1,6 @@ from state.base import State, UUID from services.bottom.V0 import Bottom +from services.primitives.integer_type import Integer def bootstrap_type(type_name: str, python_type: str, scd_root: UUID, model_root: UUID, state: State): @@ -10,7 +11,9 @@ def bootstrap_type(type_name: str, python_type: str, scd_root: UUID, model_root: scd_node, = bottom.read_outgoing_elements(scd_root, "Class") # retrieve type bottom.create_edge(class_node, scd_node, "Morphism") # create morphism link # set min_cardinality - min_c_node = bottom.create_node(1) + min_c_model = bottom.create_node() + Integer(min_c_model, state).create(1) + min_c_node = bottom.create_node(str(min_c_model)) bottom.create_edge(model_root, min_c_node, f"{type_name}.lower_cardinality") min_c_link = bottom.create_edge(class_node, min_c_node) bottom.create_edge(model_root, min_c_link, f"{type_name}.lower_cardinality_link") @@ -19,7 +22,9 @@ def bootstrap_type(type_name: str, python_type: str, scd_root: UUID, model_root: bottom.create_edge(min_c_node, scd_node, "Morphism") bottom.create_edge(min_c_link, scd_link, "Morphism") # set max_cardinality - max_c_node = bottom.create_node(1) + max_c_model = bottom.create_node() + Integer(max_c_model, state).create(1) + max_c_node = bottom.create_node(str(max_c_model)) bottom.create_edge(model_root, max_c_node, f"{type_name}.upper_cardinality") max_c_link = bottom.create_edge(class_node, max_c_node) bottom.create_edge(model_root, max_c_link, f"{type_name}.upper_cardinality_link") @@ -28,7 +33,7 @@ def bootstrap_type(type_name: str, python_type: str, scd_root: UUID, model_root: bottom.create_edge(max_c_node, scd_node, "Morphism") bottom.create_edge(max_c_link, scd_link, "Morphism") # set constraint - constraint_node = bottom.create_node(f"isinstance(read_value(x),{python_type})") + constraint_node = bottom.create_node(f"isinstance(read_value(element),{python_type})") bottom.create_edge(model_root, constraint_node, f"{type_name}.constraint") constraint_link = bottom.create_edge(class_node, constraint_node) bottom.create_edge(model_root, constraint_link, f"{type_name}.constraint_link") diff --git a/bootstrap/scd.py b/bootstrap/scd.py index 19e6c59..34fe1d3 100644 --- a/bootstrap/scd.py +++ b/bootstrap/scd.py @@ -110,9 +110,10 @@ def bootstrap_scd(state: State) -> UUID: assoc_t_l_c_edge = add_edge_element("Association_target_lower_cardinality", assoc_edge, integer_node) assoc_t_u_c_edge = add_edge_element("Association_target_upper_cardinality", assoc_edge, integer_node) # # bootstrap primitive types + # # order is important, integer must be first + bootstrap_integer_type(mcl_root, integer_type_root, state) bootstrap_boolean_type(mcl_root, boolean_type_root, state) bootstrap_float_type(mcl_root, float_type_root, state) - bootstrap_integer_type(mcl_root, integer_type_root, state) bootstrap_string_type(mcl_root, string_type_root, state) bootstrap_type_type(mcl_root, type_type_root, state) # # ATTRIBUTE ATTRIBUTES, assign 'name' and 'optional' attributes to all AttributeLinks @@ -156,6 +157,11 @@ def bootstrap_scd(state: State) -> UUID: m_name, m_opt = add_attribute_attributes("Association_target_upper_cardinality", assoc_t_u_c_edge) String(m_name, state).create("target_upper_cardinality") Boolean(m_opt, state).create(True) + # # Make Element abstract + abs_model = bottom.create_node() + abs_node = add_node_element(f"Element.abstract", str(abs_model)) + abs_edge = add_edge_element(f"Element.abstract_link", element_node, abs_node) + Boolean(abs_model, state).create(True) # create phi(SCD,SCD) to type MCL with itself @@ -245,6 +251,9 @@ def bootstrap_scd(state: State) -> UUID: add_mcl_morphism("Association_source_upper_cardinality.optional", "Boolean") add_mcl_morphism("Association_target_lower_cardinality.optional", "Boolean") add_mcl_morphism("Association_target_upper_cardinality.optional", "Boolean") + add_mcl_morphism("Element.abstract", "Boolean") + # Class_abstract + add_mcl_morphism("Element.abstract_link", "Class_abstract") return mcl_root diff --git a/framework/__init__.py b/framework/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/framework/conformance.py b/framework/conformance.py new file mode 100644 index 0000000..042aedb --- /dev/null +++ b/framework/conformance.py @@ -0,0 +1,358 @@ +from services.bottom.V0 import Bottom +from uuid import UUID +from state.base import State +from typing import Dict, Tuple, Set, Any, List +from pprint import pprint + + +class Conformance: + def __init__(self, state: State, scd_model: UUID, model: UUID, type_model: UUID): + self.state = state + self.bottom = Bottom(state) + self.scd_model = scd_model + self.model = model + self.type_model = type_model + self.type_mapping: Dict[str, str] = {} + self.model_names = { + # map model elements to their names to prevent iterating too much + self.bottom.read_outgoing_elements(self.model, e)[0]: e + for e in self.bottom.read_keys(self.model) + } + self.type_model_names = { + # map type model elements to their names to prevent iterating too much + self.bottom.read_outgoing_elements(self.type_model, e)[0]: e + for e in self.bottom.read_keys(self.type_model) + } + self.sub_types: Dict[str, Set[str]] = { + k: set() for k in self.bottom.read_keys(self.type_model) + } + self.primitive_values: Dict[UUID, Any] = {} + self.abstract_types: List[str] = [] + self.multiplicities: Dict[str, Tuple] = {} + self.source_multiplicities: Dict[str, Tuple] = {} + self.target_multiplicities: Dict[str, Tuple] = {} + + def check_nominal(self): + steps = [ + self.check_typing, + self.check_link_typing, + self.check_multiplicities, + self.check_constraints + ] + for step in steps: + conforms = step() + if not conforms: + return False + return True + + def read_attribute(self, m_element: UUID, attr_name: str): + + def has_label(_edge: UUID, _label): + elems = self.bottom.read_outgoing_elements(_edge) + for elem in elems: + value = self.primitive_values.get(elem, self.bottom.read_value(elem)) + if value is not None and value == _label: + return True + return False + + def get_outgoing_edge_by_label(_element: UUID, _label): + edges = self.bottom.read_outgoing_edges(_element) + for e in edges: + if has_label(e, _label): + return e + + outgoing = self.bottom.read_outgoing_edges(m_element) + for edge in outgoing: + try: + edge_name = self.model_names[edge] + edge_type_name = self.type_mapping[edge_name] + edge_type, = self.bottom.read_outgoing_elements(self.type_model, edge_type_name) + edge_type_src = self.bottom.read_edge_source(edge_type) + if get_outgoing_edge_by_label(edge_type_src, attr_name) == edge_type: + result = self.bottom.read_edge_target(edge) + return self.primitive_values.get(result, self.bottom.read_value(result)) + + except KeyError: + pass # non-model edge, e.g. morphism link + + def precompute_sub_types(self): + inh_element, = self.bottom.read_outgoing_elements(self.scd_model, "Inheritance") + inh_links = [] + for tm_element, tm_name in self.type_model_names.items(): + morphisms = self.bottom.read_outgoing_elements(tm_element, "Morphism") + if inh_element in morphisms: + inh_links.append(tm_element) + + for link in inh_links: + tm_source = self.bottom.read_edge_source(link) + tm_target = self.bottom.read_edge_target(link) + parent_name = self.type_model_names[tm_target] + child_name = self.type_model_names[tm_source] + self.sub_types[parent_name].add(child_name) + + stop = False + while not stop: + stop = True + for child_name, child_children in self.sub_types.items(): + for parent_name, parent_children in self.sub_types.items(): + if child_name in parent_children: + original_size = len(parent_children) + parent_children.update(child_children) + if len(parent_children) != original_size: + stop = False + + def deref_primitive_values(self): + ref_element, = self.bottom.read_outgoing_elements(self.scd_model, "ModelRef") + string_element, = self.bottom.read_outgoing_elements(self.scd_model, "String") + boolean_element, = self.bottom.read_outgoing_elements(self.scd_model, "Boolean") + integer_element, = self.bottom.read_outgoing_elements(self.scd_model, "Integer") + t_deref = [] + t_refs = [] + for tm_element, tm_name in self.type_model_names.items(): + morphisms = self.bottom.read_outgoing_elements(tm_element, "Morphism") + if ref_element in morphisms: + t_refs.append(self.type_model_names[tm_element]) + elif string_element in morphisms: + t_deref.append(tm_element) + elif boolean_element in morphisms: + t_deref.append(tm_element) + elif integer_element in morphisms: + t_deref.append(tm_element) + + for elem in t_deref: + primitive_model = UUID(self.bottom.read_value(elem)) + primitive_value_node, = self.bottom.read_outgoing_elements(primitive_model) + primitive_value = self.bottom.read_value(primitive_value_node) + self.primitive_values[elem] = primitive_value + + for m_name, tm_name in self.type_mapping.items(): + if tm_name in t_refs: + # dereference + m_element, = self.bottom.read_outgoing_elements(self.model, m_name) + primitive_model = UUID(self.bottom.read_value(m_element)) + try: + primitive_value_node, = self.bottom.read_outgoing_elements(primitive_model) + primitive_value = self.bottom.read_value(primitive_value_node) + self.primitive_values[m_element] = primitive_value + except ValueError: + pass # multiple elements in model indicate that we're not dealing with a primitive + + def precompute_multiplicities(self): + for tm_element, tm_name in self.type_model_names.items(): + # class abstract flags and multiplicities + abstract = self.read_attribute(tm_element, "abstract") + lc = self.read_attribute(tm_element, "lower_cardinality") + uc = self.read_attribute(tm_element, "upper_cardinality") + if abstract: + self.abstract_types.append(tm_name) + if lc or uc: + mult = ( + lc if lc is not None else float("-inf"), + uc if uc is not None else float("inf") + ) + self.multiplicities[tm_name] = mult + # multiplicities for associations + slc = self.read_attribute(tm_element, "source_lower_cardinality") + suc = self.read_attribute(tm_element, "source_upper_cardinality") + if slc or suc: + mult = ( + slc if slc is not None else float("-inf"), + suc if suc is not None else float("inf") + ) + self.source_multiplicities[tm_name] = mult + tlc = self.read_attribute(tm_element, "target_lower_cardinality") + tuc = self.read_attribute(tm_element, "target_upper_cardinality") + if tlc or tuc: + mult = ( + tlc if tlc is not None else float("-inf"), + tuc if tuc is not None else float("inf") + ) + self.target_multiplicities[tm_name] = mult + # optional for attribute links + opt = self.read_attribute(tm_element, "optional") + if opt is not None: + mult = (0 if opt else 1, 1) + self.source_multiplicities[tm_name] = mult + self.target_multiplicities[tm_name] = mult + + def get_type(self, element: UUID): + morphisms = self.bottom.read_outgoing_elements(element, "Morphism") + tm_element, = [m for m in morphisms if m in self.type_model_names.keys()] + return tm_element + + def check_typing(self): + """ + for each element of model check whether a morphism + link exists to some element of type_model + """ + ref_element, = self.bottom.read_outgoing_elements(self.scd_model, "ModelRef") + model_names = self.bottom.read_keys(self.model) + for m_name in model_names: + m_element, = self.bottom.read_outgoing_elements(self.model, m_name) + try: + tm_element = self.get_type(m_element) + tm_name = self.type_model_names[tm_element] + self.type_mapping[m_name] = tm_name + if ref_element in self.bottom.read_outgoing_elements(tm_element, "Morphism"): + sub_m = UUID(self.bottom.read_value(m_element)) + sub_tm = UUID(self.bottom.read_value(tm_element)) + if not Conformance(self.state, self.scd_model, sub_m, sub_tm).check_nominal(): + return False + except ValueError: + # no or too many morphism links found + print(f"Incorrectly typed element: {m_name}") + return False + return True + + def check_link_typing(self): + self.precompute_sub_types() + for m_name, tm_name in self.type_mapping.items(): + m_element, = self.bottom.read_outgoing_elements(self.model, m_name) + m_source = self.bottom.read_edge_source(m_element) + m_target = self.bottom.read_edge_target(m_element) + if m_source is None or m_target is None: + # element is not a link + continue + tm_element, = self.bottom.read_outgoing_elements(self.type_model, tm_name) + tm_source = self.bottom.read_edge_source(tm_element) + tm_target = self.bottom.read_edge_target(tm_element) + # check if source is typed correctly + source_name = self.model_names[m_source] + source_type_actual = self.type_mapping[source_name] + source_type_expected = self.type_model_names[tm_source] + if source_type_actual != source_type_expected: + if source_type_actual not in self.sub_types[source_type_expected]: + print(f"Invalid source type {source_type_actual} for element {m_name}") + return False + # check if target is typed correctly + target_name = self.model_names[m_target] + target_type_actual = self.type_mapping[target_name] + target_type_expected = self.type_model_names[tm_target] + if target_type_actual != target_type_expected: + if target_type_actual not in self.sub_types[target_type_expected]: + print(f"Invalid target type {target_type_actual} for element {m_name}") + return False + return True + + def check_multiplicities(self): + self.deref_primitive_values() + self.precompute_multiplicities() + for tm_name in self.type_model_names.values(): + # abstract classes + if tm_name in self.abstract_types: + type_count = list(self.type_mapping.values()).count(tm_name) + if type_count > 0: + print(f"Invalid instantiation of abstract class: {tm_name}") + return False + # class multiplicities + if tm_name in self.multiplicities: + lc, uc = self.multiplicities[tm_name] + type_count = list(self.type_mapping.values()).count(tm_name) + for sub_type in self.sub_types[tm_name]: + type_count += list(self.type_mapping.values()).count(sub_type) + if type_count < lc or type_count > uc: + print(f"Cardinality of type exceeds valid multiplicity range: {tm_name} ({type_count})") + return False + # association source multiplicities + if tm_name in self.source_multiplicities: + tm_element, = self.bottom.read_outgoing_elements(self.type_model, tm_name) + tm_source_element = self.bottom.read_edge_source(tm_element) + tm_source_name = self.type_model_names[tm_source_element] + lc, uc = self.source_multiplicities[tm_name] + for i, t in self.type_mapping.items(): + if t == tm_source_name or t in self.sub_types[tm_source_name]: + count = 0 + i_element, = self.bottom.read_outgoing_elements(self.model, i) + outgoing = self.bottom.read_outgoing_edges(i_element) + for o in outgoing: + try: + if self.type_mapping[self.model_names[o]] == tm_name: + count += 1 + except KeyError: + pass # for elements not part of model, e.g. morphism links + if count < lc or count > uc: + print(f"Source cardinality of type {tm_name} exceeds valid multiplicity range in {i}.") + return False + + # association target multiplicities + if tm_name in self.target_multiplicities: + tm_element, = self.bottom.read_outgoing_elements(self.type_model, tm_name) + tm_target_element = self.bottom.read_edge_source(tm_element) + tm_target_name = self.type_model_names[tm_target_element] + lc, uc = self.target_multiplicities[tm_name] + for i, t in self.type_mapping.items(): + if t == tm_target_name or t in self.sub_types[tm_target_name]: + count = 0 + i_element, = self.bottom.read_outgoing_elements(self.model, i) + outgoing = self.bottom.read_outgoing_edges(i_element) + for o in outgoing: + try: + if self.type_mapping[self.model_names[o]] == tm_name: + count += 1 + except KeyError: + pass # for elements not part of model, e.g. morphism links + if count < lc or count > uc: + print(f"Target cardinality of type {tm_name} exceeds valid multiplicity range in {i}.") + return False + return True + + def check_constraints(self): + # local constraints + for m_name, tm_name in self.type_mapping.items(): + if tm_name != "GlobalConstraint": + tm_element, = self.bottom.read_outgoing_elements(self.type_model, tm_name) + code = self.read_attribute(tm_element, "constraint") + print(code) + # global constraints + for m_name, tm_name in self.type_mapping.items(): + if tm_name == "GlobalConstraint": + tm_element, = self.bottom.read_outgoing_elements(self.type_model, tm_name) + code = self.read_attribute(tm_element, "constraint") + print(code) + return True + + +def __create_pn(state: State): + from services.scd import SCD + # Retrieve refs to primitive type models + # # integer + int_type_id = state.read_dict(state.read_root(), "Integer") + int_type = UUID(state.read_value(int_type_id)) + # # string + str_type_id = state.read_dict(state.read_root(), "String") + str_type = UUID(state.read_value(str_type_id)) + # Create LTM_PN + model_uuid = state.create_node() + service = SCD(scd, model_uuid, state) + # Create classes + service.create_class("P") + service.create_class("T") + # Create associations + service.create_association("P2T", "P", "T") + service.create_association("T2P", "T", "P") + # Create model refs + service.create_model_ref("Integer", int_type) + service.create_model_ref("String", int_type) + # 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) + # Create association attributes + service.create_attribute_link("P2T", "Integer", "w", False) + service.create_attribute_link("T2P", "Integer", "w", False) + # Create test constraint + service.add_constraint("P", "print(element)\nreturn True") + return model_uuid + + +if __name__ == '__main__': + from state.devstate import DevState as State + s = State() + from bootstrap.scd import bootstrap_scd + scd = bootstrap_scd(s) + pn = __create_pn(s) + # cf = Conformance(s, scd, scd, scd) + # cf.check_nominal() + cf = Conformance(s, scd, pn, scd) + cf.check_nominal() + diff --git a/services/primitives/boolean_type.py b/services/primitives/boolean_type.py index 9ab1852..9f157f2 100644 --- a/services/primitives/boolean_type.py +++ b/services/primitives/boolean_type.py @@ -14,6 +14,7 @@ class Boolean: if "boolean" in self.bottom.read_keys(self.model): instance, = self.bottom.read_outgoing_elements(self.model, "boolean") self.bottom.delete_element(instance) - _instance = self.bottom.create_edge(self.model, self.bottom.create_node(value), "boolean") + _instance = self.bottom.create_node(value) + self.bottom.create_edge(self.model, _instance, "boolean") _type, = self.bottom.read_outgoing_elements(self.type_model, "Boolean") self.bottom.create_edge(_instance, _type, "Morphism") diff --git a/services/primitives/float_type.py b/services/primitives/float_type.py index 3d92a20..ff18bbc 100644 --- a/services/primitives/float_type.py +++ b/services/primitives/float_type.py @@ -14,6 +14,7 @@ class Float: if "float" in self.bottom.read_keys(self.model): instance, = self.bottom.read_outgoing_elements(self.model, "float") self.bottom.delete_element(instance) - _instance = self.bottom.create_edge(self.model, self.bottom.create_node(value), "float") + _instance = self.bottom.create_node(value) + self.bottom.create_edge(self.model, _instance, "float") _type, = self.bottom.read_outgoing_elements(self.type_model, "Float") self.bottom.create_edge(_instance, _type, "Morphism") diff --git a/services/primitives/integer_type.py b/services/primitives/integer_type.py index 87ebd7d..44864bb 100644 --- a/services/primitives/integer_type.py +++ b/services/primitives/integer_type.py @@ -14,6 +14,7 @@ class Integer: if "string" in self.bottom.read_keys(self.model): instance, = self.bottom.read_outgoing_elements(self.model, "integer") self.bottom.delete_element(instance) - _instance = self.bottom.create_edge(self.model, self.bottom.create_node(value), "integer") + _instance = self.bottom.create_node(value) + self.bottom.create_edge(self.model, _instance, "integer") _type, = self.bottom.read_outgoing_elements(self.type_model, "Integer") self.bottom.create_edge(_instance, _type, "Morphism") diff --git a/services/primitives/string_type.py b/services/primitives/string_type.py index e1a5a87..926c1cd 100644 --- a/services/primitives/string_type.py +++ b/services/primitives/string_type.py @@ -14,6 +14,7 @@ class String: if "string" in self.bottom.read_keys(self.model): instance, = self.bottom.read_outgoing_elements(self.model, "string") self.bottom.delete_element(instance) - _instance = self.bottom.create_edge(self.model, self.bottom.create_node(value), "string") + _instance = self.bottom.create_node(value) + self.bottom.create_edge(self.model, _instance, "string") _type, = self.bottom.read_outgoing_elements(self.type_model, "String") self.bottom.create_edge(_instance, _type, "Morphism") diff --git a/services/primitives/type_type.py b/services/primitives/type_type.py index e7031c2..98ec38b 100644 --- a/services/primitives/type_type.py +++ b/services/primitives/type_type.py @@ -14,6 +14,7 @@ class Integer: if "string" in self.bottom.read_keys(self.model): instance, = self.bottom.read_outgoing_elements(self.model, "type") self.bottom.delete_element(instance) - _instance = self.bottom.create_edge(self.model, self.bottom.create_node(value), "type") + _instance = self.bottom.create_node(value) + self.bottom.create_edge(self.model, _instance, "type") _type, = self.bottom.read_outgoing_elements(self.type_model, "Type") self.bottom.create_edge(_instance, _type, "Morphism") From 6ce8a4ef5c2e716ec12b5ffe64637f2de687ef34 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 24 Aug 2021 17:32:21 +0200 Subject: [PATCH 13/17] Park structural conformance for now, finish constraints for nominal --- bootstrap/pn.py | 40 +++++++++++ bootstrap/scd.py | 3 + framework/conformance.py | 150 ++++++++++++++++++++++++++++++--------- services/pn.py | 127 +++++++++++++++++++++++++++++++++ 4 files changed, 285 insertions(+), 35 deletions(-) create mode 100644 bootstrap/pn.py create mode 100644 services/pn.py diff --git a/bootstrap/pn.py b/bootstrap/pn.py new file mode 100644 index 0000000..511e816 --- /dev/null +++ b/bootstrap/pn.py @@ -0,0 +1,40 @@ +from services.scd import SCD +from uuid import UUID +from state.base import State + + +def bootstrap_pn(state: State, model_name: str) -> UUID: + # Retrieve scd model + scd_id = state.read_dict(state.read_root(), "SCD") + scd = UUID(state.read_value(scd_id)) + # Retrieve refs to primitive type models + # # integer + int_type_id = state.read_dict(state.read_root(), "Integer") + int_type = UUID(state.read_value(int_type_id)) + # # string + str_type_id = state.read_dict(state.read_root(), "String") + str_type = UUID(state.read_value(str_type_id)) + # Create LTM_PN + model_uuid = state.create_node() + mcl_root_id = state.create_nodevalue(str(model_uuid)) + state.create_dict(state.read_root(), model_name, mcl_root_id) + service = SCD(scd, model_uuid, state) + # Create classes + service.create_class("P") + service.create_class("T") + # Create associations + service.create_association("P2T", "P", "T") + service.create_association("T2P", "T", "P") + # Create model refs + service.create_model_ref("Integer", int_type) + service.create_model_ref("String", str_type) + # 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) + # Create association attributes + service.create_attribute_link("P2T", "Integer", "w", False) + service.create_attribute_link("T2P", "Integer", "w", False) + # Create test constraint + service.add_constraint("P", "print(element)\nreturn True") + return model_uuid diff --git a/bootstrap/scd.py b/bootstrap/scd.py index 34fe1d3..30a42f3 100644 --- a/bootstrap/scd.py +++ b/bootstrap/scd.py @@ -74,6 +74,8 @@ def bootstrap_scd(state: State) -> UUID: # # INHERITANCES, i.e. elements typed by Inheritance # # Class inherits from Element add_edge_element("class_inh_element", class_node, element_node) + # # GlobalConstraint inherits from Element + add_edge_element("gc_inh_element", glob_constr_node, element_node) # # Attribute inherits from Element add_edge_element("attr_inh_element", attr_node, element_node) # # Association inherits from Element @@ -186,6 +188,7 @@ def bootstrap_scd(state: State) -> UUID: add_mcl_morphism("AttributeLink", "Association") # Inheritance add_mcl_morphism("class_inh_element", "Inheritance") + add_mcl_morphism("gc_inh_element", "Inheritance") add_mcl_morphism("attr_inh_element", "Inheritance") add_mcl_morphism("assoc_inh_element", "Inheritance") add_mcl_morphism("attr_link_inh_element", "Inheritance") diff --git a/framework/conformance.py b/framework/conformance.py index 042aedb..3bcb999 100644 --- a/framework/conformance.py +++ b/framework/conformance.py @@ -31,6 +31,9 @@ class Conformance: self.multiplicities: Dict[str, Tuple] = {} self.source_multiplicities: Dict[str, Tuple] = {} self.target_multiplicities: Dict[str, Tuple] = {} + self.structures = {} + self.matches = {} + self.candidates = {} def check_nominal(self): steps = [ @@ -311,38 +314,103 @@ class Conformance: print(code) return True + def precompute_structures(self): + self.precompute_sub_types() + scd_elements = self.bottom.read_outgoing_elements(self.scd_model) + # collect types + class_element, = self.bottom.read_outgoing_elements(self.scd_model, "Class") + association_element, = self.bottom.read_outgoing_elements(self.scd_model, "Association") + for tm_element, tm_name in self.type_model_names.items(): + # retrieve elements that tm_element is a morphism of + morphisms = self.bottom.read_outgoing_elements(tm_element, "Morphism") + morphism, = [m for m in morphisms if m in scd_elements] + # check if tm_element is a morphism of AttributeLink + if class_element == morphism or association_element == morphism: + self.structures[tm_name] = set() + # collect type structures + # retrieve AttributeLink to check whether element is a morphism of AttributeLink + attr_link_element, = self.bottom.read_outgoing_elements(self.scd_model, "AttributeLink") + for tm_element, tm_name in self.type_model_names.items(): + # retrieve elements that tm_element is a morphism of + morphisms = self.bottom.read_outgoing_elements(tm_element, "Morphism") + morphism, = [m for m in morphisms if m in scd_elements] + # check if tm_element is a morphism of AttributeLink + if attr_link_element == morphism: + # retrieve attributes of attribute link, i.e. 'name' and 'optional' + attrs = self.bottom.read_outgoing_elements(tm_element) + name_model_node, = filter(lambda x: self.type_model_names.get(x, "").endswith(".name"), attrs) + opt_model_node, = filter(lambda x: self.type_model_names.get(x, "").endswith(".optional"), attrs) + # get attr name value + name_model = UUID(self.bottom.read_value(name_model_node)) + name_node, = self.bottom.read_outgoing_elements(name_model) + name = self.bottom.read_value(name_node) + # get attr opt value + opt_model = UUID(self.bottom.read_value(opt_model_node)) + opt_node, = self.bottom.read_outgoing_elements(opt_model) + opt = self.bottom.read_value(opt_node) + # get attr type name + source_type_node = self.bottom.read_edge_source(tm_element) + source_type_name = self.type_model_names[source_type_node] + target_type_node = self.bottom.read_edge_target(tm_element) + target_type_name = self.type_model_names[target_type_node] + # add attribute to the structure of its source type + # attribute is stored as a (name, optional, type) triple + self.structures.setdefault(source_type_name, set()).add((name, opt, target_type_name)) + # extend structures of sub types with attrs of super types + for super_type, sub_types in self.sub_types.items(): + for sub_type in sub_types: + self.structures.setdefault(sub_type, set()).update(self.structures[super_type]) + # filter out abstract types, as they cannot be instantiated + # retrieve Class_abstract to check whether element is a morphism of Class_abstract + class_abs_element, = self.bottom.read_outgoing_elements(self.scd_model, "Class_abstract") + for tm_element, tm_name in self.type_model_names.items(): + # retrieve elements that tm_element is a morphism of + morphisms = self.bottom.read_outgoing_elements(tm_element, "Morphism") + morphism, = [m for m in morphisms if m in scd_elements] + # check if tm_element is a morphism of Class_abstract + if class_abs_element == morphism: + # retrieve 'abstract' attribute value + target_node = self.bottom.read_edge_target(tm_element) + abst_model = UUID(self.bottom.read_value(target_node)) + abst_node, = self.bottom.read_outgoing_elements(abst_model) + is_abstract = self.bottom.read_value(abst_node) + # retrieve type name + source_node = self.bottom.read_edge_source(tm_element) + type_name = self.type_model_names[source_node] + if is_abstract: + self.structures.pop(type_name) -def __create_pn(state: State): - from services.scd import SCD - # Retrieve refs to primitive type models - # # integer - int_type_id = state.read_dict(state.read_root(), "Integer") - int_type = UUID(state.read_value(int_type_id)) - # # string - str_type_id = state.read_dict(state.read_root(), "String") - str_type = UUID(state.read_value(str_type_id)) - # Create LTM_PN - model_uuid = state.create_node() - service = SCD(scd, model_uuid, state) - # Create classes - service.create_class("P") - service.create_class("T") - # Create associations - service.create_association("P2T", "P", "T") - service.create_association("T2P", "T", "P") - # Create model refs - service.create_model_ref("Integer", int_type) - service.create_model_ref("String", int_type) - # 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) - # Create association attributes - service.create_attribute_link("P2T", "Integer", "w", False) - service.create_attribute_link("T2P", "Integer", "w", False) - # Create test constraint - service.add_constraint("P", "print(element)\nreturn True") - return model_uuid + def match_structures(self): + ref_element, = self.bottom.read_outgoing_elements(self.scd_model, "ModelRef") + # match nodes + 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 = [] + for name, optional, attr_type in structure: + try: + attr, = self.bottom.read_outgoing_elements(self.model, f"{m_name}.{name}") + # 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 ref_element in morphisms: + pass + + except ValueError: + if optional: + continue + else: + break + if len(matched) == len(structure): + self.candidates[m_name].add(type_name) + + def build_morphisms(self): + pass if __name__ == '__main__': @@ -350,9 +418,21 @@ if __name__ == '__main__': s = State() from bootstrap.scd import bootstrap_scd scd = bootstrap_scd(s) - pn = __create_pn(s) - # cf = Conformance(s, scd, scd, scd) + from bootstrap.pn import bootstrap_pn + ltm_pn = bootstrap_pn(s, "PN") + from services.pn import PN + my_pn = s.create_node() + PNserv = PN(ltm_pn, my_pn, s) + PNserv.create_place("p1", 5) + PNserv.create_place("p2", 0) + PNserv.create_transition("t1") + PNserv.create_p2t("p1", "t1", 1) + PNserv.create_p2t("t1", "p2", 1) + + cf = Conformance(s, scd, my_pn, ltm_pn) + # cf = Conformance(s, scd, ltm_pn, scd) # cf.check_nominal() - cf = Conformance(s, scd, pn, scd) - cf.check_nominal() + cf.precompute_structures() + cf.match_structures() + diff --git a/services/pn.py b/services/pn.py new file mode 100644 index 0000000..285368b --- /dev/null +++ b/services/pn.py @@ -0,0 +1,127 @@ +from uuid import UUID +from state.base import State +from services.bottom.V0 import Bottom +from services.primitives.integer_type import Integer +from services.primitives.string_type import String + +import re + + +class PN: + def __init__(self, ltm_pn: UUID, model: UUID, state: State): + self.ltm_pn = ltm_pn + self.model = model + self.bottom = Bottom(state) + + def create_place(self, name: str, tokens: int): + # instantiate Place class + place_node = self.bottom.create_node() # create place node + self.bottom.create_edge(self.model, place_node, name) # attach to model + morph_node, = self.bottom.read_outgoing_elements(self.ltm_pn, "P") # retrieve type + self.bottom.create_edge(place_node, morph_node, "Morphism") # create morphism link + # instantiate name attribute + 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") + name_link = self.bottom.create_edge(place_node, name_node) + self.bottom.create_edge(self.model, name_link, f"{name}.n_link") + ltm_pn_node, = self.bottom.read_outgoing_elements(self.ltm_pn, "String") + ltm_pn_link, = self.bottom.read_outgoing_elements(self.ltm_pn, "P_n") + self.bottom.create_edge(name_node, ltm_pn_node, "Morphism") + self.bottom.create_edge(name_link, ltm_pn_link, "Morphism") + # instantiate tokens attribute + tokens_model = self.bottom.create_node() + Integer(tokens_model, self.bottom.state).create(tokens) + tokens_node = self.bottom.create_node(str(tokens_model)) + self.bottom.create_edge(self.model, tokens_node, f"{name}.t") + tokens_link = self.bottom.create_edge(place_node, tokens_node) + self.bottom.create_edge(self.model, tokens_link, f"{name}.t_link") + ltm_pn_node, = self.bottom.read_outgoing_elements(self.ltm_pn, "Integer") + ltm_pn_link, = self.bottom.read_outgoing_elements(self.ltm_pn, "P_t") + self.bottom.create_edge(tokens_node, ltm_pn_node, "Morphism") + self.bottom.create_edge(tokens_link, ltm_pn_link, "Morphism") + + def create_transition(self, name: str): + # instantiate Transition class + transition_node = self.bottom.create_node() # create transition node + self.bottom.create_edge(self.model, transition_node, name) # attach to model + morph_node, = self.bottom.read_outgoing_elements(self.ltm_pn, "T") # retrieve type + self.bottom.create_edge(transition_node, morph_node, "Morphism") # create morphism link + # instantiate name attribute + 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") + name_link = self.bottom.create_edge(transition_node, name_node) + self.bottom.create_edge(self.model, name_link, f"{name}.n_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") + self.bottom.create_edge(name_node, ltm_pn_node, "Morphism") + self.bottom.create_edge(name_link, ltm_pn_link, "Morphism") + + def create_p2t(self, place: str, transition: str, weight: int): + # create p2t link + morphism links + edge = self.bottom.create_edge( + *self.bottom.read_outgoing_elements(self.model, place), + *self.bottom.read_outgoing_elements(self.model, transition), + ) + self.bottom.create_edge(self.model, edge, f"{place}_to_{transition}") # attach to model + morph_node, = self.bottom.read_outgoing_elements(self.ltm_pn, "P2T") # retrieve type + self.bottom.create_edge(edge, morph_node, "Morphism") # create morphism link + # weight attribute + 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") + weight_link = self.bottom.create_edge(edge, weight_node) + self.bottom.create_edge(self.model, weight_link, f"{place}_to_{transition}.w_link") + scd_node, = self.bottom.read_outgoing_elements(self.ltm_pn, "Integer") + scd_link, = self.bottom.read_outgoing_elements(self.ltm_pn, "P2T_w") + self.bottom.create_edge(weight_node, scd_node, "Morphism") + self.bottom.create_edge(weight_link, scd_link, "Morphism") + + def create_t2p(self, transition: str, place: str, weight: int): + # create t2p link + morphism links + edge = self.bottom.create_edge( + *self.bottom.read_outgoing_elements(self.model, transition), + *self.bottom.read_outgoing_elements(self.model, place), + ) + self.bottom.create_edge(self.model, edge, f"{transition}_to_{place}") # attach to model + morph_node, = self.bottom.read_outgoing_elements(self.ltm_pn, "T2P") # retrieve type + self.bottom.create_edge(edge, morph_node, "Morphism") # create morphism link + # weight attribute + 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") + weight_link = self.bottom.create_edge(edge, weight_node) + self.bottom.create_edge(self.model, weight_link, f"{transition}_to_{place}.w_link") + scd_node, = self.bottom.read_outgoing_elements(self.ltm_pn, "Integer") + scd_link, = self.bottom.read_outgoing_elements(self.ltm_pn, "T2P_w") + self.bottom.create_edge(weight_node, scd_node, "Morphism") + self.bottom.create_edge(weight_link, scd_link, "Morphism") + + def list_elements(self): + pn_names = {} + for key in self.bottom.read_keys(self.ltm_pn): + element, = self.bottom.read_outgoing_elements(self.ltm_pn, key) + pn_names[element] = key + unsorted = [] + for key in self.bottom.read_keys(self.model): + element, = self.bottom.read_outgoing_elements(self.model, key) + element_types = self.bottom.read_outgoing_elements(element, "Morphism") + type_model_elements = self.bottom.read_outgoing_elements(self.ltm_pn) + element_type_node, = [e for e in element_types if e in type_model_elements] + unsorted.append((key, pn_names[element_type_node])) + for elem in sorted(unsorted, key=lambda e: e[0]): + print("{} : {}".format(*elem)) + + def delete_element(self, name: str): + keys = self.bottom.read_keys(self.model) + r = re.compile(r"{}\..*".format(name)) + to_delete = list(filter(r.match, keys)) + for key in to_delete: + # TODO: find way to solve memory leak, primitive models are not deleted this way + node, = self.bottom.read_outgoing_elements(self.model, label=key) + self.bottom.delete_element(node) From f7430cceff052850320944ffbc6bb3207f459f37 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 24 Aug 2021 20:49:02 +0200 Subject: [PATCH 14/17] Nominal constraints now working --- bootstrap/pn.py | 2 +- framework/conformance.py | 74 ++++++++++++++++++++++------------------ 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/bootstrap/pn.py b/bootstrap/pn.py index 511e816..26e8556 100644 --- a/bootstrap/pn.py +++ b/bootstrap/pn.py @@ -36,5 +36,5 @@ def bootstrap_pn(state: State, model_name: str) -> UUID: service.create_attribute_link("P2T", "Integer", "w", False) service.create_attribute_link("T2P", "Integer", "w", False) # Create test constraint - service.add_constraint("P", "print(element)\nreturn True") + service.add_constraint("P", "True") return model_uuid diff --git a/framework/conformance.py b/framework/conformance.py index 3bcb999..d0bcc9e 100644 --- a/framework/conformance.py +++ b/framework/conformance.py @@ -48,35 +48,21 @@ class Conformance: return False return True - def read_attribute(self, m_element: UUID, attr_name: str): + def read_attribute(self, element: UUID, attr_name: str): - def has_label(_edge: UUID, _label): - elems = self.bottom.read_outgoing_elements(_edge) - for elem in elems: - value = self.primitive_values.get(elem, self.bottom.read_value(elem)) - if value is not None and value == _label: - return True - return False - - def get_outgoing_edge_by_label(_element: UUID, _label): - edges = self.bottom.read_outgoing_edges(_element) - for e in edges: - if has_label(e, _label): - return e - - outgoing = self.bottom.read_outgoing_edges(m_element) - for edge in outgoing: - try: - edge_name = self.model_names[edge] - edge_type_name = self.type_mapping[edge_name] - edge_type, = self.bottom.read_outgoing_elements(self.type_model, edge_type_name) - edge_type_src = self.bottom.read_edge_source(edge_type) - if get_outgoing_edge_by_label(edge_type_src, attr_name) == edge_type: - result = self.bottom.read_edge_target(edge) - return self.primitive_values.get(result, self.bottom.read_value(result)) - - except KeyError: - pass # non-model edge, e.g. morphism link + if element in self.type_model_names: + # type model element + element_name = self.type_model_names[element] + model = self.type_model + else: + # model element + element_name = self.model_names[element] + model = self.model + try: + attr_elem, = self.bottom.read_outgoing_elements(model, f"{element_name}.{attr_name}") + return self.primitive_values.get(attr_elem, self.bottom.read_value(attr_elem)) + except ValueError: + return None def precompute_sub_types(self): inh_element, = self.bottom.read_outgoing_elements(self.scd_model, "Inheritance") @@ -299,19 +285,39 @@ class Conformance: return False return True + def evaluate_constraint(self, code, **kwargs): + funcs = { + 'read_value': self.state.read_value + } + return eval( + code, + {'__builtins__': {'isinstance': isinstance, 'print': print, + 'int': int, 'float': float, 'bool': bool, 'str': str, 'tuple': tuple} + }, # globals + {**kwargs, **funcs} # locals + ) + def check_constraints(self): # local constraints for m_name, tm_name in self.type_mapping.items(): if tm_name != "GlobalConstraint": tm_element, = self.bottom.read_outgoing_elements(self.type_model, tm_name) code = self.read_attribute(tm_element, "constraint") - print(code) + if code is not None: + morphisms = self.bottom.read_incoming_elements(tm_element, "Morphism") + morphisms = [m for m in morphisms if m in self.model_names] + for m_element in morphisms: + if not self.evaluate_constraint(code, element=m_element): + return False + # global constraints for m_name, tm_name in self.type_mapping.items(): if tm_name == "GlobalConstraint": tm_element, = self.bottom.read_outgoing_elements(self.type_model, tm_name) code = self.read_attribute(tm_element, "constraint") - print(code) + if code is not None: + if not self.evaluate_constraint(code, model=self.model): + return False return True def precompute_structures(self): @@ -427,12 +433,12 @@ if __name__ == '__main__': PNserv.create_place("p2", 0) PNserv.create_transition("t1") PNserv.create_p2t("p1", "t1", 1) - PNserv.create_p2t("t1", "p2", 1) + PNserv.create_t2p("t1", "p2", 1) cf = Conformance(s, scd, my_pn, ltm_pn) # cf = Conformance(s, scd, ltm_pn, scd) - # cf.check_nominal() - cf.precompute_structures() - cf.match_structures() + print(cf.check_nominal()) + # cf.precompute_structures() + # cf.match_structures() From 695ec6132a909eee643125adadc1f483385f8947 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Tue, 24 Aug 2021 23:56:25 +0200 Subject: [PATCH 15/17] Nominal and structural typing seem to be fully working --- bootstrap/pn.py | 6 ++-- framework/conformance.py | 78 +++++++++++++++++++++++++++++++++------- services/pn.py | 18 +++++----- 3 files changed, 77 insertions(+), 25 deletions(-) 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") From dfcc24f4874c288d9ac168e572298fb8522026d9 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Wed, 25 Aug 2021 17:08:49 +0200 Subject: [PATCH 16/17] Small bugfix --- framework/conformance.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/framework/conformance.py b/framework/conformance.py index 5ad8496..a421aaa 100644 --- a/framework/conformance.py +++ b/framework/conformance.py @@ -160,9 +160,8 @@ class Conformance: # optional for attribute links opt = self.read_attribute(tm_element, "optional") if opt is not None: - mult = (0 if opt else 1, 1) - self.source_multiplicities[tm_name] = mult - self.target_multiplicities[tm_name] = mult + self.source_multiplicities[tm_name] = (0 if opt else 1, 1) + self.target_multiplicities[tm_name] = (0, 1) def get_type(self, element: UUID): morphisms = self.bottom.read_outgoing_elements(element, "Morphism") @@ -266,14 +265,14 @@ class Conformance: # association target multiplicities if tm_name in self.target_multiplicities: tm_element, = self.bottom.read_outgoing_elements(self.type_model, tm_name) - tm_target_element = self.bottom.read_edge_source(tm_element) + tm_target_element = self.bottom.read_edge_target(tm_element) tm_target_name = self.type_model_names[tm_target_element] lc, uc = self.target_multiplicities[tm_name] for i, t in self.type_mapping.items(): if t == tm_target_name or t in self.sub_types[tm_target_name]: count = 0 i_element, = self.bottom.read_outgoing_elements(self.model, i) - outgoing = self.bottom.read_outgoing_edges(i_element) + outgoing = self.bottom.read_incoming_edges(i_element) for o in outgoing: try: if self.type_mapping[self.model_names[o]] == tm_name: @@ -404,6 +403,7 @@ class Conformance: # linguistically conforms to the specified type # if its an internally defined attribute, this will be checked by constraints morphisms = self.bottom.read_outgoing_elements(attr_tm, "Morphism") + attr_conforms = True if ref_element in morphisms: # check conformance of reference model type_model_uuid = UUID(self.bottom.read_value(attr_tm)) @@ -415,7 +415,8 @@ class Conformance: code = self.read_attribute(attr_tm, "constraint") if code is not None: attr_conforms = self.evaluate_constraint(code, element=attr) - matched += 1 + if attr_conforms: + matched += 1 except ValueError: # attr not found or failed parsing UUID if optional: From 6df566373d9af8d17d49523b4ef89cd034bf9af6 Mon Sep 17 00:00:00 2001 From: Andrei Bondarenko Date: Fri, 27 Aug 2021 09:46:44 +0200 Subject: [PATCH 17/17] Points example working --- bootstrap/pn.py | 2 +- framework/conformance.py | 76 +++++++++-------- framework/interactive_prompt.py | 99 ++++++++++++++++++++++ framework/manager.py | 140 ++++++++++++++++++++++++++++++++ framework/prompt_questions.py | 36 ++++++++ services/__init__.py | 9 ++ services/pn.py | 5 +- services/point/__init__.py | 0 services/point/cartesian.py | 83 +++++++++++++++++++ services/point/polar.py | 90 ++++++++++++++++++++ services/scd.py | 18 ++-- state.p | Bin 0 -> 72727 bytes state/devstate.py | 10 +-- 13 files changed, 518 insertions(+), 50 deletions(-) create mode 100644 framework/interactive_prompt.py create mode 100644 framework/manager.py create mode 100644 framework/prompt_questions.py create mode 100644 services/point/__init__.py create mode 100644 services/point/cartesian.py create mode 100644 services/point/polar.py create mode 100644 state.p diff --git a/bootstrap/pn.py b/bootstrap/pn.py index a319355..d80f618 100644 --- a/bootstrap/pn.py +++ b/bootstrap/pn.py @@ -18,7 +18,7 @@ def bootstrap_pn(state: State, model_name: str) -> UUID: model_uuid = state.create_node() mcl_root_id = state.create_nodevalue(str(model_uuid)) state.create_dict(state.read_root(), model_name, mcl_root_id) - service = SCD(scd, model_uuid, state) + service = SCD(model_uuid, state) # Create classes service.create_class("P") service.create_class("T") diff --git a/framework/conformance.py b/framework/conformance.py index a421aaa..9a97ca4 100644 --- a/framework/conformance.py +++ b/framework/conformance.py @@ -6,10 +6,11 @@ from pprint import pprint class Conformance: - def __init__(self, state: State, scd_model: UUID, model: UUID, type_model: UUID): + def __init__(self, state: State, model: UUID, type_model: UUID): self.state = state self.bottom = Bottom(state) - self.scd_model = scd_model + type_model_id = state.read_dict(state.read_root(), "SCD") + self.scd_model = UUID(state.read_value(type_model_id)) self.model = model self.type_model = type_model self.type_mapping: Dict[str, str] = {} @@ -35,18 +36,30 @@ class Conformance: self.matches = {} self.candidates = {} - def check_nominal(self): - steps = [ - self.check_typing, - self.check_link_typing, - self.check_multiplicities, - self.check_constraints - ] - for step in steps: - conforms = step() - if not conforms: - return False - return True + def check_nominal(self, *, log=False): + try: + self.check_typing() + self.check_link_typing() + self.check_multiplicities() + self.check_constraints() + return True + except RuntimeError as e: + if log: + print(e) + return False + + def check_structural(self, *, build_morphisms=True, log=False): + try: + self.precompute_structures() + self.match_structures() + if build_morphisms: + self.build_morphisms() + self.check_nominal(log=log) + return True + except RuntimeError as e: + if log: + print(e) + return False def read_attribute(self, element: UUID, attr_name: str): @@ -184,12 +197,11 @@ class Conformance: if ref_element in self.bottom.read_outgoing_elements(tm_element, "Morphism"): sub_m = UUID(self.bottom.read_value(m_element)) sub_tm = UUID(self.bottom.read_value(tm_element)) - if not Conformance(self.state, self.scd_model, sub_m, sub_tm).check_nominal(): - return False + if not Conformance(self.state, sub_m, sub_tm).check_nominal(): + raise RuntimeError(f"Incorrectly model reference: {m_name}") except ValueError: # no or too many morphism links found - print(f"Incorrectly typed element: {m_name}") - return False + raise RuntimeError(f"Incorrectly typed element: {m_name}") return True def check_link_typing(self): @@ -210,16 +222,14 @@ class Conformance: source_type_expected = self.type_model_names[tm_source] if source_type_actual != source_type_expected: if source_type_actual not in self.sub_types[source_type_expected]: - print(f"Invalid source type {source_type_actual} for element {m_name}") - return False + raise RuntimeError(f"Invalid source type {source_type_actual} for element {m_name}") # check if target is typed correctly target_name = self.model_names[m_target] target_type_actual = self.type_mapping[target_name] target_type_expected = self.type_model_names[tm_target] if target_type_actual != target_type_expected: if target_type_actual not in self.sub_types[target_type_expected]: - print(f"Invalid target type {target_type_actual} for element {m_name}") - return False + raise RuntimeError(f"Invalid target type {target_type_actual} for element {m_name}") return True def check_multiplicities(self): @@ -230,8 +240,7 @@ class Conformance: if tm_name in self.abstract_types: type_count = list(self.type_mapping.values()).count(tm_name) if type_count > 0: - print(f"Invalid instantiation of abstract class: {tm_name}") - return False + raise RuntimeError(f"Invalid instantiation of abstract class: {tm_name}") # class multiplicities if tm_name in self.multiplicities: lc, uc = self.multiplicities[tm_name] @@ -239,8 +248,7 @@ class Conformance: for sub_type in self.sub_types[tm_name]: type_count += list(self.type_mapping.values()).count(sub_type) if type_count < lc or type_count > uc: - print(f"Cardinality of type exceeds valid multiplicity range: {tm_name} ({type_count})") - return False + raise RuntimeError(f"Cardinality of type exceeds valid multiplicity range: {tm_name} ({type_count})") # association source multiplicities if tm_name in self.source_multiplicities: tm_element, = self.bottom.read_outgoing_elements(self.type_model, tm_name) @@ -259,8 +267,7 @@ class Conformance: except KeyError: pass # for elements not part of model, e.g. morphism links if count < lc or count > uc: - print(f"Source cardinality of type {tm_name} exceeds valid multiplicity range in {i}.") - return False + raise RuntimeError(f"Source cardinality of type {tm_name} exceeds valid multiplicity range in {i}.") # association target multiplicities if tm_name in self.target_multiplicities: @@ -307,7 +314,7 @@ class Conformance: morphisms = [m for m in morphisms if m in self.model_names] for m_element in morphisms: if not self.evaluate_constraint(code, element=m_element): - return False + raise RuntimeError(f"Local constraint of {tm_name} not satisfied in {m_name}.") # global constraints for m_name, tm_name in self.type_mapping.items(): @@ -316,7 +323,7 @@ class Conformance: code = self.read_attribute(tm_element, "constraint") if code is not None: if not self.evaluate_constraint(code, model=self.model): - return False + raise RuntimeError(f"Global constraint {tm_name} not satisfied.") return True def precompute_structures(self): @@ -408,7 +415,7 @@ class Conformance: # 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)\ + attr_conforms = Conformance(self.state, model_uuid, type_model_uuid)\ .check_nominal() else: # eval constraints @@ -446,8 +453,7 @@ class Conformance: def build_morphisms(self): if not all([len(c) == 1 for c in self.candidates.values()]): - print("Ambiguous structural matches found, unable to build unique morphism.") - return False + raise RuntimeError("Cannot build incomplete or ambiguous morphism.") mapping = {k: v.pop() for k, v in self.candidates.items()} for m_name, tm_name in mapping.items(): # morphism to class/assoc @@ -480,14 +486,14 @@ if __name__ == '__main__': ltm_pn_lola = bootstrap_pn(s, "PNlola") from services.pn import PN my_pn = s.create_node() - PNserv = PN(ltm_pn, my_pn, s) + PNserv = PN(my_pn, s) PNserv.create_place("p1", 5) PNserv.create_place("p2", 0) PNserv.create_transition("t1") PNserv.create_p2t("p1", "t1", 1) PNserv.create_t2p("t1", "p2", 1) - cf = Conformance(s, scd, my_pn, ltm_pn_lola) + cf = Conformance(s, my_pn, ltm_pn_lola) # cf = Conformance(s, scd, ltm_pn, scd) cf.precompute_structures() cf.match_structures() diff --git a/framework/interactive_prompt.py b/framework/interactive_prompt.py new file mode 100644 index 0000000..ff38c8c --- /dev/null +++ b/framework/interactive_prompt.py @@ -0,0 +1,99 @@ +from framework.manager import Manager +from state.devstate import DevState +from PyInquirer import prompt, Separator +from pprint import pprint +import prompt_questions as questions +from inspect import signature +from uuid import UUID + + +def generate_context_question(ctx_type, services): + choices = [ + s.__name__.replace('_', ' ') for s in services + ] + choices = sorted(choices) + choices.append(Separator()) + choices.append("close context") + ctx_question = [ + { + 'type': 'list', + 'name': 'op', + 'message': f'Currently in context {ctx_type.__name__}, which operation would you like to perform?', + 'choices': choices, + 'filter': lambda x: x.replace(' ', '_') + } + ] + return ctx_question + + +def main(): + state = DevState() + man = Manager(state) + + while True: + if man.current_model is not None and man.current_context is None: + answer = prompt(questions.MODEL_SELECTED) + ctx = man + elif man.current_model is not None and man.current_context is not None: + qs = generate_context_question(type(man.current_context), man.get_services()) + answer = prompt(qs) + if answer['op'] == 'close_context': + man.close_context() + continue + else: + ctx = man.current_context + else: + answer = prompt(questions.MODEL_MGMT) + ctx = man + + if answer['op'] == 'exit': + break + else: + method = getattr(ctx, answer['op']) + args_questions = [] + types = {} + for p in signature(method).parameters.values(): + types[p.name] = p.annotation # can't use filter in question dict, doesn't work for some reason... + if p.annotation == UUID: + args_questions.append({ + 'type': 'list', + 'name': p.name, + 'message': f'{p.name.replace("_", " ")}?', + 'choices': list(man.get_models()), + 'filter': lambda x: state.read_value(state.read_dict(state.read_root(), x)) + }) + else: + args_questions.append({ + 'type': 'input', + 'name': p.name, + 'message': f'{p.name.replace("_", " ")}?', + 'filter': lambda x: False if x.lower() == 'false' else x + }) + args = prompt(args_questions) + args = {k: types[k](v) for k, v in args.items()} + try: + output = method(**args) + if output is not None: + try: + if isinstance(output, str): + raise TypeError + output = list(output) + if len(output) > 0: + for o in sorted(output): + print(f"\u2022 {o}") + except TypeError: + print(f"\u2022 {output}") + except RuntimeError as e: + print(e) + + +if __name__ == '__main__': + print("""Welcome to... + __ ____ _____ + | \/ \ \ / /__ \ + | \ / |\ \ / / ) | + | |\/| | \ \/ / / / + | | | | \ / / /_ + |_| |_| \/ |____| + """) + main() diff --git a/framework/manager.py b/framework/manager.py new file mode 100644 index 0000000..289ab5e --- /dev/null +++ b/framework/manager.py @@ -0,0 +1,140 @@ +from state.base import State +from bootstrap.scd import bootstrap_scd +from services import implemented as services +from framework.conformance import Conformance +from uuid import UUID + + +class Manager: + def __init__(self, state: State): + self.current_model = None + self.current_context = None + self.state = state + bootstrap_scd(state) + scd_node = self.state.read_dict(self.state.read_root(), "SCD") + for key_node in self.state.read_dict_keys(self.state.read_root()): + model_node = self.state.read_dict_node(self.state.read_root(), key_node) + self.state.create_edge(model_node, scd_node) + + def get_models(self): + for key_node in self.state.read_dict_keys(self.state.read_root()): + yield self.state.read_value(key_node) + + def instantiate_model(self, type_model_name: str, name: str): + root = self.state.read_root() + type_model_node = self.state.read_dict(root, type_model_name) + if type_model_node is None: + raise RuntimeError(f"No type model with name {type_model_name} found.") + else: + # check if model is a linguistic type model + scd_node = self.state.read_dict(self.state.read_root(), "SCD") + incoming = self.state.read_incoming(scd_node) + incoming = [self.state.read_edge(e)[0] for e in incoming] + if type_model_node not in incoming: + raise RuntimeError(f"Model with name {type_model_name} is not a type model.") + if name in map(self.state.read_value, self.state.read_dict_keys(root)): + raise RuntimeError(f"Model with name {name} already exists.") + new_model_root = self.state.create_node() + new_model_node = self.state.create_nodevalue(str(new_model_root)) + self.state.create_dict(root, name, new_model_node) + self.state.create_edge(new_model_node, type_model_node) + self.current_model = (name, new_model_root) + if type_model_name not in services: + raise RuntimeError(f"Services for type {type_model_name} not implemented.") + self.current_context = services[type_model_name](self.current_model[1], self.state) + + def select_model(self, name: str): + root = self.state.read_root() + model_node = self.state.read_dict(root, name) + if model_node is None: + raise RuntimeError(f"No model with name {name} found.") + model_root = UUID(self.state.read_value(model_node)) + self.current_model = (name, model_root) + + def close_model(self): + self.current_model = None + self.current_context = None + + def get_types(self): + root = self.state.read_root() + if self.current_model is None: + raise RuntimeError(f"No model currently selected.") + name, model = self.current_model + model_id = self.state.read_dict(root, name) + outgoing = self.state.read_outgoing(model_id) + outgoing = [e for e in outgoing if len(self.state.read_outgoing(e)) == 0] + elements = [self.state.read_edge(e)[1] for e in outgoing] + for e in elements: + incoming = self.state.read_incoming(e) + label_edge, = [e for e in incoming if len(self.state.read_outgoing(e)) == 1] + label_edge, = self.state.read_outgoing(label_edge) + _, label_node = self.state.read_edge(label_edge) + yield self.state.read_value(label_node) + + def select_context(self, name: str): + if name not in self.get_types(): + raise RuntimeError(f"No type {name} that currently selected model conforms to.") + if name not in services: + raise RuntimeError(f"Services for type {name} not implemented.") + self.current_context = services[name](self.current_model[1], self.state) + self.current_context.from_bottom() + + def close_context(self): + self.current_context.to_bottom() + self.current_context = None + + def get_services(self): + if self.current_model is None: + raise RuntimeError(f"No model currently selected.") + if self.current_context is None: + raise RuntimeError(f"No context currently selected.") + yield from [ + getattr(self.current_context, func) + for func in dir(self.current_context) + if callable(getattr(self.current_context, func)) + and not func.startswith("__") + and not func == "from_bottom" + and not func == "to_bottom" + ] + + def check_conformance(self, type_model_name: str, model_name: str): + root = self.state.read_root() + type_model_node = self.state.read_dict(root, type_model_name) + if type_model_node is None: + raise RuntimeError(f"No type model with name {type_model_name} found.") + model_node = self.state.read_dict(root, model_name) + if model_node is None: + raise RuntimeError(f"No model with name {model_node} found.") + types = self.state.read_outgoing(model_node) + types = [self.state.read_edge(e)[1] for e in types] + if type_model_node not in types: + conf = Conformance(self.state, + UUID(self.state.read_value(model_node)), + UUID(self.state.read_value(type_model_node))).check_structural(log=True) + if conf: + self.state.create_edge(model_node, type_model_node) + return conf + else: + return Conformance(self.state, + UUID(self.state.read_value(model_node)), + UUID(self.state.read_value(type_model_node))).check_nominal(log=True) + + def dump_state(self): + import pickle + with open("state.p", "wb") as file: + pickle.dump(self.state, file) + + def load_state(self): + import pickle + with open("state.p", "rb") as file: + self.state = pickle.load(file) + + +if __name__ == '__main__': + from state.devstate import DevState + s = DevState() + m = Manager(s) + m.select_model("SCD") + m.select_context("SCD") + for f in m.get_services(): + print(f) diff --git a/framework/prompt_questions.py b/framework/prompt_questions.py new file mode 100644 index 0000000..d71e1cc --- /dev/null +++ b/framework/prompt_questions.py @@ -0,0 +1,36 @@ +from PyInquirer import Separator + +MODEL_SELECTED = [ + { + 'type': 'list', + 'name': 'op', + 'message': 'Model selected... Which operation would you like to perform?', + 'choices': [ + 'get types', + 'select context', + Separator(), + 'close model' + ], + 'filter': lambda x: x.replace(' ', '_') + } +] + +MODEL_MGMT = [ + { + 'type': 'list', + 'name': 'op', + 'message': 'Which model management operation would you like to perform?', + 'choices': [ + 'get models', + 'select model', + 'instantiate model', + 'check conformance', + Separator(), + 'load state', + 'dump state', + Separator(), + 'exit' + ], + 'filter': lambda x: x.replace(' ', '_') + } +] diff --git a/services/__init__.py b/services/__init__.py index e69de29..e36ebb5 100644 --- a/services/__init__.py +++ b/services/__init__.py @@ -0,0 +1,9 @@ +from services.scd import SCD +from services.point.cartesian import PointCartesian +from services.point.polar import PointPolar + +implemented = { + "SCD": SCD, + "PointCartesian": PointCartesian, + "PointPolar": PointPolar, +} diff --git a/services/pn.py b/services/pn.py index 3415e91..7086ff9 100644 --- a/services/pn.py +++ b/services/pn.py @@ -8,8 +8,9 @@ import re class PN: - def __init__(self, ltm_pn: UUID, model: UUID, state: State): - self.ltm_pn = ltm_pn + def __init__(self, model: UUID, state: State): + ltm_pn_id = state.read_dict(state.read_root(), "PN") + self.ltm_pn = UUID(state.read_value(ltm_pn_id)) self.model = model self.bottom = Bottom(state) diff --git a/services/point/__init__.py b/services/point/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services/point/cartesian.py b/services/point/cartesian.py new file mode 100644 index 0000000..323de54 --- /dev/null +++ b/services/point/cartesian.py @@ -0,0 +1,83 @@ +from uuid import UUID +from state.base import State +from services.bottom.V0 import Bottom +from services.primitives.float_type import Float + + +class PointCartesian: + def __init__(self, model: UUID, state: State): + type_model_id = state.read_dict(state.read_root(), "PointCartesian") + self.type_model = UUID(state.read_value(type_model_id)) + self.model = model + self.state = state + + self.point = None + + def create_point(self, x: float, y: float): + if self.point is None: + self.point = (x, y) + else: + raise RuntimeError("A PointCartesian model can contain at most 1 point.") + + def read_point(self): + if self.point is None: + raise RuntimeError("No point found in model.") + else: + return f"(X = {self.point[0]}, Y = {self.point[1]})" + + def delete_point(self): + self.point = None + + def apply_movement(self, delta_x: float, delta_y: float): + if self.point is not None: + self.point = (self.point[0] + delta_x, self.point[1] + delta_y) + else: + raise RuntimeError("No point found in model.") + + def to_bottom(self): + bottom = Bottom(self.state) + # clear residual model + for element in bottom.read_outgoing_elements(self.model): + bottom.delete_element(element) + # create primitive models + c1_model = bottom.create_node() + c2_model = bottom.create_node() + Float(c1_model, self.state).create(self.point[0]) + Float(c2_model, self.state).create(self.point[1]) + # instantiate Point class + point_node = bottom.create_node() # create point node + bottom.create_edge(self.model, point_node, "point") # attach to model + morph_node, = bottom.read_outgoing_elements(self.type_model, "PointCartesian") # retrieve type + bottom.create_edge(point_node, morph_node, "Morphism") # create morphism link + # instantiate c1 attribute + c1_node = bottom.create_node(str(c1_model)) + bottom.create_edge(self.model, c1_node, "point.c1") + c1_link = bottom.create_edge(point_node, c1_node) + bottom.create_edge(self.model, c1_link, "point.c1_link") + ltm_point_node, = bottom.read_outgoing_elements(self.type_model, "Float") + ltm_point_link, = bottom.read_outgoing_elements(self.type_model, "PointCartesian_c1") + bottom.create_edge(c1_node, ltm_point_node, "Morphism") + bottom.create_edge(c1_link, ltm_point_link, "Morphism") + # instantiate c2 attribute + c2_node = bottom.create_node(str(c2_model)) + bottom.create_edge(self.model, c2_node, "point.c2") + c2_link = bottom.create_edge(point_node, c2_node) + bottom.create_edge(self.model, c2_link, "point.c2_link") + ltm_point_node, = bottom.read_outgoing_elements(self.type_model, "Float") + ltm_point_link, = bottom.read_outgoing_elements(self.type_model, "PointCartesian_c2") + bottom.create_edge(c2_node, ltm_point_node, "Morphism") + bottom.create_edge(c2_link, ltm_point_link, "Morphism") + + def from_bottom(self): + bottom = Bottom(self.state) + keys = bottom.read_keys(self.model) + x_key, = filter(lambda k: k.endswith(".c1"), keys) + y_key, = filter(lambda k: k.endswith(".c2"), keys) + x_ref_node, = bottom.read_outgoing_elements(self.model, x_key) + y_ref_node, = bottom.read_outgoing_elements(self.model, y_key) + x_model = UUID(bottom.read_value(x_ref_node)) + y_model = UUID(bottom.read_value(y_ref_node)) + x_val_node, = bottom.read_outgoing_elements(x_model) + y_val_node, = bottom.read_outgoing_elements(y_model) + self.point = (bottom.read_value(x_val_node), bottom.read_value(y_val_node)) + diff --git a/services/point/polar.py b/services/point/polar.py new file mode 100644 index 0000000..33bfc08 --- /dev/null +++ b/services/point/polar.py @@ -0,0 +1,90 @@ +from uuid import UUID +from state.base import State +from services.bottom.V0 import Bottom +from services.primitives.float_type import Float + +import math + + +class PointPolar: + def __init__(self, model: UUID, state: State): + type_model_id = state.read_dict(state.read_root(), "PointPolar") + self.type_model = UUID(state.read_value(type_model_id)) + self.model = model + self.state = state + + self.point = None + + def create_point(self, r: float, theta: float): + if self.point is None: + self.point = (r, theta) + else: + raise RuntimeError("A PointPolar model can contain at most 1 point.") + + def read_point(self): + if self.point is None: + raise RuntimeError("No point found in model.") + else: + return f"(r = {self.point[0]}, \u03B8 = {self.point[1]})" + + def delete_point(self): + self.point = None + + def apply_movement(self, delta_r: float, delta_theta: float): + if self.point is not None: + self.point = (self.point[0] + delta_r, self.point[1] + delta_theta) + else: + raise RuntimeError("No point found in model.") + + def to_bottom(self): + x = self.point[0]*math.cos(self.point[1]) # x = r * cos(theta) + y = self.point[0]*math.sin(self.point[1]) # y = r * sin(theta) + bottom = Bottom(self.state) + # clear residual model + for element in bottom.read_outgoing_elements(self.model): + bottom.delete_element(element) + # create primitive models + c1_model = bottom.create_node() + c2_model = bottom.create_node() + Float(c1_model, self.state).create(x) + Float(c2_model, self.state).create(y) + # instantiate Point class + point_node = bottom.create_node() # create point node + bottom.create_edge(self.model, point_node, "point") # attach to model + morph_node, = bottom.read_outgoing_elements(self.type_model, "PointPolar") # retrieve type + bottom.create_edge(point_node, morph_node, "Morphism") # create morphism link + # instantiate c1 attribute + c1_node = bottom.create_node(str(c1_model)) + bottom.create_edge(self.model, c1_node, "point.c1") + c1_link = bottom.create_edge(point_node, c1_node) + bottom.create_edge(self.model, c1_link, "point.c1_link") + ltm_point_node, = bottom.read_outgoing_elements(self.type_model, "Float") + ltm_point_link, = bottom.read_outgoing_elements(self.type_model, "PointPolar_c1") + bottom.create_edge(c1_node, ltm_point_node, "Morphism") + bottom.create_edge(c1_link, ltm_point_link, "Morphism") + # instantiate c2 attribute + c2_node = bottom.create_node(str(c2_model)) + bottom.create_edge(self.model, c2_node, "point.c2") + c2_link = bottom.create_edge(point_node, c2_node) + bottom.create_edge(self.model, c2_link, "point.c2_link") + ltm_point_node, = bottom.read_outgoing_elements(self.type_model, "Float") + ltm_point_link, = bottom.read_outgoing_elements(self.type_model, "PointPolar_c2") + bottom.create_edge(c2_node, ltm_point_node, "Morphism") + bottom.create_edge(c2_link, ltm_point_link, "Morphism") + + def from_bottom(self): + bottom = Bottom(self.state) + keys = bottom.read_keys(self.model) + x_key, = filter(lambda k: k.endswith(".c1"), keys) + y_key, = filter(lambda k: k.endswith(".c2"), keys) + x_ref_node, = bottom.read_outgoing_elements(self.model, x_key) + y_ref_node, = bottom.read_outgoing_elements(self.model, y_key) + x_model = UUID(bottom.read_value(x_ref_node)) + y_model = UUID(bottom.read_value(y_ref_node)) + x_val_node, = bottom.read_outgoing_elements(x_model) + y_val_node, = bottom.read_outgoing_elements(y_model) + x = bottom.read_value(x_val_node) + y = bottom.read_value(y_val_node) + r = math.sqrt(math.pow(x, 2) + math.pow(y, 2)) + theta = math.atan2(y, x) + self.point = (r, theta) diff --git a/services/scd.py b/services/scd.py index 15a5290..d78cb88 100644 --- a/services/scd.py +++ b/services/scd.py @@ -9,8 +9,9 @@ import re class SCD: - def __init__(self, scd_model: UUID, model: UUID, state: State): - self.scd_model = scd_model + def __init__(self, model: UUID, state: State): + type_model_id = state.read_dict(state.read_root(), "SCD") + self.scd_model = UUID(state.read_value(type_model_id)) self.model = model self.bottom = Bottom(state) @@ -168,9 +169,8 @@ class SCD: element_types = self.bottom.read_outgoing_elements(element, "Morphism") type_model_elements = self.bottom.read_outgoing_elements(self.scd_model) element_type_node, = [e for e in element_types if e in type_model_elements] - unsorted.append((key, scd_names[element_type_node])) - for elem in sorted(unsorted, key=lambda e: e[0]): - print("{} : {}".format(*elem)) + unsorted.append(f"{key} : {scd_names[element_type_node]}") + return sorted(unsorted) def delete_element(self, name: str): keys = self.bottom.read_keys(self.model) @@ -181,6 +181,12 @@ class SCD: node, = self.bottom.read_outgoing_elements(self.model, label=key) self.bottom.delete_element(node) + def to_bottom(self): + pass + + def from_bottom(self): + pass + if __name__ == '__main__': from state.devstate import DevState as State @@ -199,7 +205,7 @@ if __name__ == '__main__': # Create LTM_PN model_uuid = s.create_node() print(f"LTM_PN Model UUID: {model_uuid}") # 845 - service = SCD(scd, model_uuid, s) + service = SCD(model_uuid, s) # Create classes service.create_class("P") service.create_class("T") diff --git a/state.p b/state.p new file mode 100644 index 0000000000000000000000000000000000000000..a7d43373ff49742173fb5dd8857c81f7aca3c6b8 GIT binary patch literal 72727 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 UUID: - DevState.free_id += 1 - return UUID(int=DevState.free_id - 1) + def new_id(self) -> UUID: + self.free_id += 1 + return UUID(int=self.free_id - 1) def dump(self, path: str, png_path: str = None): """Dumps the whole MV graph to a graphviz .dot-file